Wednesday, October 8, 2014

Command 'nuget spec' Sucks for Dlls

Not So Nice

The NuGet documentation says that you can create a nuspec file from an existing DLL if you run the following command:

nuget spec [path to dll]

Unfortunately, the output is less than satisfying:

<?xml version="1.0"?>
<package >
    <metadata>
        <id>SomeAwesomeDll.dll</id>
        <version>1.0.0</version>
        <authors>swoogan</authors>
        <owners>swoogan</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>Package description</description>
        <releaseNotes>Summary of changes made in this release of the package.</releaseNotes>
        <copyright>Copyright 2014</copyright>
        <tags>Tag1 Tag2</tags>
        <dependencies>
        <dependency id="SampleDependency" version="1.0" />
        </dependencies>
    </metadata>
</package>

Note: the version is always 1.0.0, because it does not bother to extract the version number from the assembly.

All and all not what I was hoping for!

The three glaring problems are:

  1. It does not add the version, as mentioned above
  2. It does not add <files> section with the name of the DLL you pointed too!!!
  3. As a result of the second point, it does not produce a valid nuspec file that can be packaged.

A Better Way

Here is a preferable default template:

<?xml version="1.0" encoding="utf-8"?>
<package mlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
    <metadata>
        <id>[[DllNameWithoutExtension]]</id>
        <version>[[Version]]</version>
        <authors>swoogan</authors>
        <requireLicenseAcceptance>false</requireLicenseAcceptance>
        <description>[[DllNameWithoutExtension]]</description>        
    </metadata>
    <files>
      <file src="[[DllName]]" target="lib\[[DllName]]" />
    </files>
</package>

Using the above template and a powershell template engine that I adapted from
http://bricelam.net/2012/09/simple-template-engine-for-powershell.html

   function Merge-Tokens
   {
       <#
       .SYNOPSIS
       Replaces tokens in a template
       .DESCRIPTION
       Replaces tokens found in a template file with values from the supplied hashtable
       .EXAMPLE
       Merge-Tokens -Path mytemplate.xml -Tokens @{ Variable = Value }
       .EXAMPLE
       Merge-Tokens -Path mytemplate.xml -Tokens @{ Variable = Value } -OutputPath newfile.xml
       .PARAMETER Path
       Path to the template file to replace the tokens in
       .PARAMETER Tokens
       Hashtable with the names of the tokens in the file and their replacement values
       .PARAMETER OutputPath
       Path to write the output to. If not supplied, the replacement is returned as a string
       #>
       [CmdletBinding()]
       param 
       (
           [Parameter(
               Mandatory=$True,
               Position=0)]
           [string] $Path, 

           [Parameter(
               Mandatory=$True,
               Position=1)]
           [hashtable] $Tokens,

           [Parameter(
               Mandatory=$False,
               Position=2)]
           [string] $OutputPath
       )

       $template = Get-Content $Path -Raw

       $output = [regex]::Replace(
           $template,
           '\[\[(?\w+)\]\]',
           {
               param($match)
               $tokenName = $match.Groups['tokenName'].Value
               return $Tokens[$tokenName]
           })

       if ($OutputPath -ne "") {
           Set-Content -Path $OutputPath -Value $output
       } else {
           Write-Output $output
       }
   }

I was able to write the following script:

   param (
       [string] $Path
   )

   Import-Module .\Merge-Tokens.ps1

   $nuspecTemplate = "$PSScriptRoot\nuspec.tpl"

   $fileInfo = gci $Path

   $tokens =  @{ 
                DllName = $fileInfo.Name
                Version = $fileInfo.VersionInfo.ProductVersion
                DllNameWithoutExtension = $fileInfo.BaseName
               }

   $outputPath = ($fileInfo.Directory.FullName + "\" + $fileInfo.BaseName + ".nuspec" )

   Merge-Tokens -Path $nuspecTemplate -Tokens $tokens -OutputPath $outputPath
  

In this case, I stored the above sane template in nuspec.tpl. Notice that I passed the -Raw parameter to Get-Content this causes it to get the entire file as a single string rather than an array of strings. This helps [regex]::Replace return a string that preserves the newlines found in the original file.

Putting it all together, I was able to generate nuget packages from dozens of existing DLLs stored on a network share.

No comments:

Post a Comment