Making the core package building api more flexible

Dec 14, 2010 at 6:06 PM

While working on building a package from a Project file (http://nuget.codeplex.com/Thread/View.aspx?ThreadId=237902) I went down a rabbit hole a bit.

Here are some of my thoughts and suggestions on how we can make the core building / consuming api a bit more obvious and flexible.  The changes aren't really that radical, it's mainly moving some code around and promoting a few assumptions to first class concepts and letting them be overridden.

I decided to do this writeup instead of including it with the "package from a project file" work, since I wanted to keep that lean to spur adoption of it :-)

So here you go:

The problem right now is PackageBuilder the API is very closely tied to the XML layout,
and decisions like "add files if we're not explicilty told" are hard coded in the setup of PackageBuilder.

Instead make PackageBuilder a thin layer that sits on top of some different machinery.

That machinery would be IPackageMetadataParser, IPackageWriter, and IPackageFileStrategy.

IPackageMetadata would stay the same.


interface IPackageMetadataParser {
    IPackageMetadata Parse();
}

interface IPackageWriter {
    void Save(Stream stream);
}

interface IPackageFileStrategryParser {
    IPackageFileStrategy GetFileStrategy();
}

interface IPackageFileStrategy {
    IEnumerable GetFiles();
}

IPackageBuilder becomes

interface IPackageBuilder {
    IPackageWriter GetWriter();
}

IPackageFileStrategy requires some more explanation.  The idea here is that we would have different "Strategies" for what files
get added to a package.  I really only envison a few:

* AllFilesInPathStrategy which would yield return a PhysicalPackageFile for each file in the specified directory tree
* SpecifiedFileStrategy which would take a params of string filePaths and / or a params of IPackageFile
* FilterMatchStrategy which would only give back files in the current tree that matched some filter (indeed AllFilesInPath
  could really just be a special case of this).

--

Also one additional proposal is to add a Merge feature to IPackageMetadata:

public static class PackageMetadataMerging {
    public static IPackageMetadata Merge(this IPackageMetadata left, IPackageMetadata right) {
        // Return new Metadata where information in "right" takes precedence over "left"
        return new ManifestMetadata { ... };
    }
}


-- 

Given the above:

* We have NuSpec and .csproj file implementations that implement both IPackageMetadataParse and IPackageFileStrategyParser

* The implementation of IPackageWriter takes a IPackageMetadata and IPackageFileStrategy, and does WriteManifest, WriteFiles just like
  PackageBuilder does now.

* PackageBuilder can become a Fluent like api for defining what goes in a package;  all you do is set
metadata info and file stuff like now, and then ask it for an IPackageWriter that can create the appropriate Package.

* You create the Metadata, and Strategy objects yourself and give them to an IPackageWriter to save out.

* With Metadata Merging the implementation of building a package from a .csproj file and a .nuspec becomes a matter of:

** Parse metadata for .csproj and .nuspec files
** Merge the metadata giving preference to the .csproj metadata
** Create a PackageWriter giving it the merged metadata and the .csproj FileStrategy

* Also with Metadata Merging we can do "inherited" spec files.