Creating Packages

Although consuming packages is very easy with NuGet, there wouldn't be any packages to consume if people didn't also create them. This is why the NuGet team is focused on making sure that creating packages is as simple as possible.

Before you create a package, make sure to download the NuGet.exe command-line utility from the NuGet CodePlex website at http://nuget.codeplex.com/. Copy NuGet.exe to a more central location on your hard drive and add that location to your PATH environment variable.

NuGet.exe is self-updatable via the Update command. For example, you can run:

NuGet.exe update

or use the short form:

Nuget u

to back up the current version of NuGet.exe by appending the .old extension to it and replace it with the latest version of NuGet.exe.

Once you have NuGet.exe installed, creating a package requires three main steps:

1. Organize the package contents into a convention-based folder structure.

2. Specify the metadata for the package in a .nuspec file.

3. Run the NuGet.exe Pack command against the .nuspec file.

Install-Package NuGet.CommandLine

Folder Structure

By default, the NuGet Pack command recursively includes all the files in the folder where the specified .nuspec file is located. It is possible to override this default by specifying the set of files to include within the .nuspec file.

A package consists of three types of files as outlined in Table 10.1.

Table 10.1: Package File Types

Folder Description
lib Each assembly (.dll file) in this folder gets referenced as an assembly reference in the target project.
content Files within the content folder are copied to the application root when the package is installed. If the file ends with the .pp or .transform extension, a transformation is applied before copying it.
tools Contains PowerShell scripts that may be run during installation or initialization of the solution as well as any programs that should be accessible from the Package Manager Console.

Typically, when creating a package, you'll set up one or more of these default folders with the files needed for your package.

Most packages add an assembly into a project, so it's worth going into more detail about the structure of the lib folder.

NuSpec File

When you create a package, you'll want to specify information about the package such as the package ID, a description, the authors, and so on. All this metadata is specified in an XML format in a .nuspec file. This file is also used to drive package creation and is included within the package after creation.

To get started quickly with writing a NuSpec file, you can use the NuGet Spec command to generate a boilerplate file. Use the AssemblyPath flag to generate a NuSpec file using the metadata stored in an assembly.

nuget spec –AssemblyPath MusicCategorizer.dll

This command generates the following NuSpec file:

<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
  <metadata>
    <id>MusicCategorizer</id>
    <version>1.0.0.0</version>
    <title>MusicCategorizer</title>
    <authors>Haackbeat Enterprises</authors>
    <owners>Owner here</owners>
    <licenseUrl>http://LICENSE_URL_HERE_OR_DELETE_THIS_LINE</licenseUrl>
    <projectUrl>http://PROJECT_URL_HERE_OR_DELETE_THIS_LINE</projectUrl>
    <iconUrl>http://ICON_URL_HERE_OR_DELETE_THIS_LINE</iconUrl>
    <requireLicenseAcceptance>false</requireLicenseAcceptance>
    <description>
      Categorizes music into genres and determines beats per minute (BPM) of a song.
    </description>
    <tags>Tag1 Tag2</tags>
    <dependencies>
      <dependency id="SampleDependency" version="1.0" />
    </dependencies>
  </metadata>
</package>

All NuSpec files start with the outer <packages> element. This element must contain a child <metadata> element and optionally may contain a <files> element, which I cover later. If you follow the folder structure convention mentioned earlier, the <files> element is not needed.

Metadata

Table 10.2 outlines the elements contained within the <metadata> section of a NuSpec file.

Table 10.2: Metadata Elements

Element Description
id Required. The unique identifier for the package.
version Required. The version of the package using the standard version format of up to four version segments (ex. 1.1 or 1.1.2 or 1.1.2.5).
title The human-friendly title of the package. If omitted, the ID is displayed instead.
authors Required. A comma-separated list of authors of the package code.
owners A comma-separated list of the package creators. This is often, though not necessarily, the same list as in authors. Note that when you upload your package to the gallery, the account on the gallery supersedes this field.
licenseUrl A link to the package's license.
projectUrl A URL for the homepage of the package where people can find more information about the package.
iconUrl A URL for the image to use as the icon for the package in the dialog. This should be a 32x32-pixel .png file that has a transparent background.
requireLicenseAcceptance A Boolean value that specifies whether the client needs to ensure that the package license (described by licenseUrl) is accepted before the package is installed.
Description Required. A long description of the package. This shows up in the right pane of the package manager dialog.
Tags A space-delimited list of tags and keywords that describe the package.
dependencies The list of dependencies for the package specified via child <dependency> elements.
language The Microsoft Locale ID string (or LCID string) for the package, such as en-us.
summary A short description of the package. This shows up in the middle pane of the package manager dialog.

It's very important to choose an ID for a package carefully because it must be unique. This is the value used to identify a package when running commands to install and update packages.

The format for a package ID follows the same basic rules as you'd follow when naming a .NET namespace. So MusicCategorizer and MusicCategorizer.Mvc are valid package IDs, but MusicCategorizer!!!Web is not.

Dependencies

Many packages are not developed in isolation, but themselves depend on other libraries. Rather than including those libraries in your package, if they are available as a package, you can specify those packages as dependencies in your package.

If those libraries don't exist as packages, consider contacting the owner of the library and offering to help them to package it up!

Each <dependency> contains two key pieces of information as shown in Table 10.3.

Table 10.3: Dependency Element

Attribute Description
Id The package ID that this package depends on.
Version The range of versions of the dependency package that this package may depend on.

As mentioned in Table 10.3, the version attribute specifies a range of versions. By default, if you just enter a version number, for example <dependency id=“MusicCategorizer” version=“1.0” />, that indicates a minimum version for the dependency. This example shows a dependency that allows your package to take a dependency on version 1.0 and above of the MusicCategorizer package.

If more control over the dependencies is required, you can use interval notation (remember that from sixth grade?) to specify a range. Table 10.4 shows the various ways to specify a version range.

Table 10.4: Version Ranges

Range Meaning
1.0 Version is greater than or equal to 1.0. This is the most common and recommended usage.
[1.0, 2.0) Version is between 1.0 and 2.0 including 1.0, but excluding 2.0.
(,1.0] Version is less than or equal to 1.0
(,1.0) Version is strictly less than 1.0
[1.0] Version is exactly 1.0
(1.0,) Version is strictly greater than 1.0
(1.0,2.0) Version is between 1.0 and 2.0, excluding those versions.
[1.0,2.0] Version is between 1.0 and 2.0 including those versions.
(1.0, 2.0] Version is between 1.0 and 2.0 excluding 1.0, but including 2.0.
(1.0) Invalid.
Empty All versions.

In general, the recommended approach is to specify only a lower bound. In many cases, this gives the person installing a package a chance to make it work, rather than blocking them prematurely. In the case of strongly named assemblies, NuGet automatically adds the appropriate assembly binding redirects to your configuration file.

For an in-depth discussion of the versioning strategy employed by NuGet, read the blog series by David Ebbo at http://blog.davidebbo.com/2011/01/nuget-versioning-part-1-taking-on-dll.html.

Specifying Files to Include

If you follow the folder structure conventions described earlier, you do not have to specify a list of files in the .nuspec file. But in some cases you may choose to be explicit about which files to include. For example, you might have a build process where you'd rather choose the files to include rather than copy them into the convention-based structure first. You can use the <files> element to choose which files to include.

Note that if you specify any files, the conventions are ignored and only the files listed in the NuSpec file are included in the package.

The <files> element is an optional child element of the <package> element and contains a set of <file> elements. Each <file> element specifies the source and destination of a file to include in the package. Table 10.5 describes these attributes.

Table 10.5: Version Ranges

Attribute Description
Src The location of the file or files to include. The path is relative to the NuSpec file unless an absolute path is specified. The wildcard character, *, is allowed. Using a double wildcard, **, implies a recursive directory search.
target Optional. The destination path for the file or set of files. This is a relative path within the package, such as target=“lib” or target=“lib et40”. Other typical values include target=“content” or target=“tools”.

The following example shows a typical files element.

<files>
  <file src="binDebug*.dll" target="lib" /> 
  <file src="binDebug*.pdb" target="lib" /> 
  <file src="tools***.*" target="tools" />
</files>

All paths are resolved relative to the .nuspec file unless an absolute path is specified. For more details on how this element works, check out the specifications on the NuGet Documentation website: http://docs.nuget.org/docs/reference/nuspec-reference.

Tools

A package can include PowerShell scripts that automatically run when the package is installed or removed. Some scripts can add new commands to the console such as the MvcScaffolding package.

Let's walk through building a very simple package that adds a new command to the Package Manager Console. In this particular case, the package won't be particularly useful, but it will illustrate some useful concepts.

I've always been a fan of the novelty toy called the Magic 8-Ball. If you're not familiar with this toy, it's very simple. It's an oversized plastic 8-ball (the kind you use when playing pool or pocket billiards). First, you ask the 8-ball any yes or no question that pops in your head. You then shake it and then peer into a small clear window that allows you to see one face of an icosahedral (20-sided) die with the answer to the question.

You'll build your own version of the Magic 8-Ball as a package that adds a new PowerShell command to the console. We'll start by writing a script named init.ps1. By convention, scripts with this name placed in the tools folder of the package are executed every time the solution is opened allowing the script to add this command to the console.

Table 10.6, shows a list of all of the special PowerShell scripts that can be included in the tools folder of a package and when NuGet executes them.

Table 10.6: Special PowerShell Scripts

Name Description
Init.ps1 Runs the first time a package is installed into any project within a solution. If the same package is installed into additional projects in the solution, the script is not run during those installations. The script also runs every time the solution is opened in Visual Studio. This is useful for adding new commands into the Package Manager Console.
Install.ps1 Runs when a package is installed into a project. If the same package is installed in multiple projects in a solution, the script runs each time the package is installed into the project. This is useful for taking additional installation steps beyond what NuGet normally can do.
Uninstall.ps1 Runs every time a package is uninstalled from a project. This is useful for any cleanup your package may need to do beyond what NuGet does normally.

When calling these scripts, NuGet will pass in a set of parameters as shown in Table 10.7.

Table 10.7: NuGet PowerShell Script Parameters

Name Description
$installPath Path to the installed package.
$toolsPath Path to the tools directory within the installed package directory.
$package An instance of the package.
$project The project you are installing the package into. This is null in the case of init.ps1 because init.ps1 runs at the solution level.

Your init.ps1 script will be very simple. It will simply import a PowerShell module that contains your real logic:

param($installPath, $toolsPath, $package, $project)

Import-Module (Join-Path $toolsPath MagicEightBall.psm1)

The first line declares the parameters to the script that NuGet will pass into the script when calling it (described in Table 10.7).

The second line imports a module named MagicEightBall.psm1. This is the PowerShell module script that contains the logic for this new command you plan to write. This module is located in the same directory as the init.ps1 script, which as described earlier, must go in the tools directory. That's why you need to join the $toolsPath (path to the tools directory) with the name of your module to get the full path to your module script file.

The following is the source for MagicEightBall.psm1:

$answers =   "As I see it, yes", 
                    "Reply hazy, try again", 
                    "Outlook not so good"

function Get-Answer($question) {
       $rand = New-Object System.Random
       return $answers[$rand.Next(0, 3)]
}

Register-TabExpansion ‘Get-Answer’ @{
       ‘question’ = { 
              "Is this my lucky day?",
              "Will it rain tonight?",
              "Do I watch too much TV?"
       }
}

Export-ModuleMember Get-Answer

Let's break it down:

  • The first line declares an array of possible answers. While the real Magic 8-Ball has 20 possible answers, you'll start off simple with only three.
  • The next block of code declares your function named Get-Answer. This is the new command that this package adds to the Package Manager Console. It generates a random integer number between 0 (inclusive) and 3 (exclusive). You then use this random number as an index into your array to return a random answer.
  • The next block of code registers a tab expansion for your new command via the Register-TabExpansion method. This is a very neat way to provide IntelliSense-like tab completion to any function. The first parameter is the name of the function you will provide tab expansion for. The second parameter is a dictionary used to supply the possible tab expansion values for each parameter to the function. Each entry in the dictionary has a key corresponding to the parameter name. In this example, you only have one parameter, question. The value of each entry is an array of possible values. This code sample provides three possible questions you can ask the 8-ball, but of course the user of the function is free to ask any question.
  • The last line of code exports the Get-Answer function. This makes it available to the console as a publicly callable command.

Now all you need to do is package these files up and install your package. In order for these scripts to run, they must be added to the tools folder of a package. If you drag these files into the Contents pane of Package Explorer, a useful tool we cover later in this chapter in the section “Using the Package Explorer,” it'll automatically prompt you to place them in the tools folder. If you're using NuGet.exe to create the package, place these files in a folder named tools.

Once you're done creating the package, you can test it out by installing it locally. Simply place the package in a folder and add that folder as a package source. This is covered in more depth later in the chapter in the section “Hosting a Private NuGet Feed.” After installing the package, a new command becomes available in the package manager complete with tab expansion, as shown in Figures 10.16 and 10.17.

Building packages that can add powerful new commands to the Package Manager Console is relatively quick and easy, once you get the hang of PowerShell. We've only begun to scratch the surface of the types of things you can do with it.

Framework and Profile Targeting

Many assemblies target a specific version of the .NET Framework. For example, you might have one version of your library that's specific to .NET 2.0 and another version of the same library that takes advantage of .NET 4 features. You do not need to create separate packages for each of these versions. NuGet supports putting multiple versions of the same library in a single package, keeping them in separate folders within the package.

When NuGet installs an assembly from a package, it checks the target .NET Framework version of the project you are adding the package to. NuGet then selects the correct version of the assembly in the package by selecting the correct subfolder within the lib folder.

Figure 10.18 shows an example of the layout for a package that targets both .NET 2.0 and .NET 4.

To enable NuGet to do this, you use the following naming convention to indicate which assemblies go with which framework versions:

lib{framework name}{version} 

There are only two choices for the framework name: .NET Framework and Silverlight. It's customary to use the abbreviations for these frameworks in this case, net and sl, respectively.

The version is the version of the framework. For brevity, you can omit the dot character. Thus:

  • net11 targets .NET 1.1
  • net40 targets .NET 4
  • sl4 targets Silverlight 4.0

Assemblies that have no associated framework name or version are stored directly in the lib folder.

When NuGet installs a package that has multiple assembly versions, it tries to match the framework name and version of the assembly with the target framework of the project.

If a match is not found, NuGet looks at each of the folders within the lib folder of the package and finds the folder with a matching framework version and the highest version number that's less than or equal to the project's target framework.

For example, if you install a package that has the lib folder structure, previously shown in Figure 10.19, into a project that targets the .NET Framework 3.5, the assembly in the net20 folder (for .NET Framework 2.0) is selected because that's the highest version that's still less than 3.5.

NuGet also supports targeting a specific framework profile by appending a dash and the profile name to the end of the folder:

lib{framework name}{version}

For example, to target the Windows Phone profile, place your assembly in a folder named sl4-wp.

Profiles supported by NuGet include:

  • Client: Client Profile
  • Full: Full Profile
  • WP: Windows Phone

At the time of this writing, to target the Windows Phone profile, the Silverlight 4 framework must be specified. It is anticipated that in the future, later versions of Silverlight will be supported on the phone.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.142.171.253