Enhance Unreferenced Assemblies (Proposal 2)

Dec 22, 2011 at 7:21 AM

Please see Proposal 1, for introduction.

Issue: Although Proposal 1 (above) allows a NuSpec to specify dependency exclusions when building from a project, in the example given (of an instrumentation library) the NuGet itself knows that it does not create a dependency and this scenario is not supported.

The ModuleInitializer NuGet instruments the library it is added to by performing build time assembly modifications.  The ModuleInitializer code is itself dependent on the Mono library.  However in the scenario where the library it is added creates a NuGet itself (for example a logging NuGet) we don't want that NuGet to have a dependency on ModuleInitializer.

If you add the logging NuGet to another project, it will load the logging NuGet and the Module Initializer NuGet which will instrument (undesirably) the new project.

Obviously, using Proposal 1 we could exclude the dependency explicitly in the logging NuGet to prevent this occurring.  However, this requires users of the ModuleInitializer NuGet to understand the issue and update their NuSpecs accordingly.  This is likely to cause a lot of downstream problems for the NuGet users.

Proposal: Add a <DoesNotCreateDependency /> tag to the NuSpec specification.  The presence of this tag is respected by the spec/pack commands when packing from a project, and the dependency is not added automatically.  To add a dependency in a downstream NuGet it would have to be added explicitly to the NuSpec dependencies.

Dec 22, 2011 at 7:30 AM

Please note that I am happy to submit a spec, implement the changes and request a pull if there's a good chance they'll be approved.

Coordinator
Dec 23, 2011 at 12:10 AM

Thanks for the proposal.

Just so I understand the issue, you have a package installed into a project that performs build time modifications to the project, but isn't used at runtime. Is that correct? Rather than add a <DoesNotCreateDependency /> tag, I'd prefer if we could just detect this situation and do the right thing.

For example, the package that you installed that does the build time assembly modification doesn't have any thing in the lib folder, right? Or if it does have something in the lib folder, the assembly is excluded by using the <references /> node in the nuspec. In that case, the end result is there is no assembly being referenced in the package.

In that case, we should be able to determine that nuget pack should not include that package in the resulting package automatically.

Make sense?

Dec 23, 2011 at 1:11 PM

You are indeed right that I don't put anything in the lib folder, and instead add stuff into the tools folder.  In my example auto-detecting this scenario would work.

Of course the problem with this approach is that I have to manually stipulate what goes into the tools folder when building from a project (which I need to do because it is itself dependent on frequently changing NuGets).  Ideally I'd want the ability to either allow the NuSpec to override the project output destination (I may raise another suggestion for this as a follow on from Proposal 1), or to allow the files to be placed automatically in the lib folder and the references to be removed via a NuSpec (as in Proposal 1).

What about the situation where I create an instrumentation NuGet, that once included in a NuGet should be included in any NuGets that include that NuGet (take PostSharp for example - although this does have PostSharp.dll in a lib, if you created an instrumentation NuGet that didn't require this, but had the same kind of need to alter the build of downstream NuGets then your automatic idea would break the build process.

Further to comments I made in Proposal 1 there is a danger in assuming we can anticipate every use case.  If an automatic step is added then there should be a way to override it.  I think this is sufficiently edge that it won't occur frequently enough that people will remember the behaviour and as such if it were automatic it is likely to result in confusion and unspotted bugs.  If someone needs to prevent dependencies being added automatically in downstream NuGets they would search for a solution and find the <DoesNotCreateDependency /> (or perhaps better '<DoesNotAutomaticallyCreateDependency />'.

Secondly if the dependency is erroneously removed automatically, you would have to add it back with a <Dependency> node in the NuSpec.  The <Dependency> node requires a version number, and so you would have to maintain the NuSpec version number which would be a real pain in automated processes.

In short I'm not sure there's enough justification for a Convention to exist here instead of just simple Configuration.

Finally, if we also implement a property tab, it would be easy to see the option to switch on 'DoesNotAutomaticallyCreateDependency'.

Coordinator
Dec 23, 2011 at 7:16 PM

Hmm, I'm having trouble parsing this sentence:

What about the situation where I create an instrumentation NuGet, that once included in a NuGet should be included in any NuGets that include that NuGet

NuGet is the name of the project, so I'm assuming you're talking about NuGet packages (or nupkgs for short). But if that's what you meant, why are you including packages within packages.

We're vary wary of adding configuration switches that only support a minor set of use cases. We're trying to keep the set of tools simple and let people compose solutions with those simple sets of tools.

Perhaps if we heard from others who had similar use cases that required this flag, that'd help us better understand the need for this feature. As it stands, I think the solution I mentioned is the simplest thing that would work and would solve a lot of cases. I'd prefer to do that first, and then look at the cases that are still problematic.

Also, the dependency node's version attribute is a version range. So you don't technically need to change it with each release unless the min version required changes.

Dec 26, 2011 at 9:08 AM

It should therefore read:

What about the situation where I create an instrumentation package that once included in a project that itself creates a package should be included in any projects that include that package.

I gave the example of PostSharp, the AOP framework that allows code manipulation at compile time.  If I create a package containing aspects (if your not familiar with AOP and PostSharp then there's an explanation here), then for those aspects to be applied to any project that uses my package, that project not only must include my package but also include the PostSharp package so that PostSharp can modify it's build process by inserting a target.

Currently PostSharp also includes lib elements that generate required references, but that would not necessarily be true for other AOP technologies.  For example my ModuleInitializer NuGet does not require any references (although it doesn't need to instrument downstream packages).

I think this is a very good example of a real world scenario that would be very difficult to anticipate by the NuGet designers unless they were familiar with the technology, my original point is that there will many such cases.

 

As to the dependency versions, having now re-scoured the nuspec reference, it makes no explicit reference to the version being a range.  There is a single nuspec example (not in the section about dependencies though) that does include 

<dependency id="yet-another-package"/>

which at least implies that the version is optional, so thanks for the bit of additional info.

Coordinator
Dec 26, 2011 at 6:19 PM

Ah, I think our docs need to be improved there. Here's the specification for version ranges. http://docs.nuget.org/docs/reference/versioning#Specifying_Version_Ranges_in_.nuspec_Files

If you ever see any other issues in the docs, do let us know. Or even better, if you feel so inclined, our docs can be edited via the browser here. https://github.com/NuGet/NuGetDocs :)

Dec 27, 2011 at 7:53 AM

I'm waiting to see what happens with my fork.  I've learnt a lot about the way it currently works (the logic for nuspec parsing is spread all over the place), so if all goes well I'll update the docs.

Jan 6, 2012 at 10:13 AM

This is now pretty urgent for me - as without it NuGet is not a viable solution for instrumentation/aop/assembly modifying packages.

So your suggestion is that packing only automatically includes dependencies on nugets from the package.config that result in references being added.

Ironically, this is probably harder to implement without making the included manifest within the package explicitly specify references.  The current implementation does not include references in the manifest that are added automatically and the code that decides what references to add is hardly encapsulated.  To check 'does this package add references' is therefore non-trivial.  Whereas introducing a <DoesNotCreateDependency /> requires less code changes as the pack command can check for this tag in the manifest of packages found in the packages.config.

Hopefully that makes sense.