Skip to content

Scoping dependencies

carllewis edited this page Nov 30, 2011 · 3 revisions

Scoping dependencies

Note that this feature is not complete, the documentation serves as a high-level design specification.

Very often, some dependencies are only needed at certain times. For example, adding nunit makes sense for your test projects but not necessarily for your core projects. Sometimes, you'll want your project to use sqlite for it's database for development, but SQL server for production.

Scoping lets you provides multiple "scopes" in which your package can be resolved.

How does scoping work?

At the root of an OpenWrap project, there's the Package-descriptor file that defines your dependencies. For example, a project using OpenRasta will have a descriptor looking like the following:

# MyProject.wrapdesc
name: MyProject
depends: openrasta-core = 2.1
depends: openrasta-sharpview = 2.1

To define that your unit tests use nunit, you could add a dependency on nunit in that file, and every project would automatically get references to the NUnit dlls added. Because compilers are smart, if you don't use test code, even though the reference is imported, the compiled assembly will not reference nunit, and everything would be great, until someone references your pacakge: they'll end-up pulling your selected version of nunit.

To prevent this from happening, you can add a second descriptor alongside your default descriptor, and add your dependency there.

# MyProject.tests.wrapdesc
depends: nunit = 2.5.1

Or you can do the same thing from the command-line by adding the -scope parameter to the add-wrap command.

$ o add-wrap nunit 2.5.1 -scope tests

How does OpenWrap know what Visual Studio project belongs to what scope?

By convention. By default, any project that matches one of the following conventions will automatically use the tests scope when resolving dependencies.

  • *.unittests.*proj
  • *.tests.unit.*proj

Customizing conventions

If you want to change that convention, you need to specify a custom source file location.

For example, if all your core projects live in /src and all your test projects live in /testing, you would customize your default descriptor by adding the following line.

directory-structure: {source: src}
directory-structure: {scope: testing=tests}

You can of course create as many of those instructions as necessary, but they need to be in your default descriptor.

Per-project customization

You can override the scope for a specific MSBuild-based project by adding an MSBuild property to your file.

<PropertyGroup>
  <OpenWrap-Scope>customscope</OpenWrap-Scope>
</PropertyGroup>

Order of precedence

OpenWrap merges all your descriptor files before compiling or executing a project, starting from your default descriptor.

If a dependency is present in both the default scope and a more specific scope, the more specific overrides the definition of the default scope.

For example, for a custom usetrunk scope, the two following files will be merged to declare openrasta-core = 3.0.

# MyProject.wrapdesc
depends: openrasta-core = 2.1

# MyProject.usetrunk.wrapdesc
depends: openrasta-core = 3.0

Pre-defined scopes

OpenWrap uses certain pre-defined scopes that you can use to solve common scenarios.

  • default: The default scope is always included in all scenarios.
  • tests: Any run of unit-tests, be it as part of a package installation or through the o test-wrap command, will be run using the tests scope. It's extremely important for your unit-test framework to be included in this scope, as without it the test-runner will not be detected and tests will not be ran.

For OpenWrap 1.1, scoping inheritance is limited to one level, a.k.a. any custom scope depends from the default package. In later releases we may allow more complex scenarios.