.netnugetnuget-spec

How to package a single EXE with NuGet


I have a large EXE that my project needs, but I don't want to commit large EXEs into Git. So, seems to me a viable option is to pull it in through NuGet. Easy enough, I created a .nuspec file with my one EXE in it:

<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
    <metadata>
        <id>Traefik</id>
        <version>1.5.1</version>
        <authors>Containous</authors>
        <owners>MikeC</owners>
        <projectUrl>https://github.com/containous/traefik</projectUrl>
        <license type="expression">MIT</license>
        <requireLicenseAcceptance>false</requireLicenseAcceptance>
        <description>Traefik binaries</description>
        <copyright>Copyright ©2020 Containous</copyright>
    </metadata>

    <files>
        <file src="traefik.exe" target="lib" />
    </files>
</package>

I then run nuget pack and create a .nupkg file. Everything is correct:

enter image description here

However, when I try to reference my NuGet package in my project, I get the following error right when clicking the "Install" button:

Severity    Code    Description Project File    Line    Suppression State
Error       Could not install package 'Traefik 1.5.1'. You are trying to install this package into a project that targets 'Unsupported,Version=v0.0', but the package does not contain any assembly references or content files that are compatible with that framework. For more information, contact the package author.              

This is a NuGet package that has no assemblies and nothing to reference. It's simply content I want to pull in and copy somewhere using a post build action. Is it possible to create a NuGet package that has no assemblies to reference and is purely content (in this case, a single EXE file)? If so, what's the correct way to lay this out in the file structure? I've tried putting the EXE in a few places, such as "bin", "tools", "content", etc. Same error no matter what. Thanks!


Solution

  • The lib folder is not suitable for this, because it is used for assemblies that your project references when installing the package. Apart from that, the folder structure must contain a suitable target framework folder like net472. You cannot place assemblies directly unter lib.

    <file src="traefik.exe" target="lib/net472"/>
    

    If you want to treat your executable as a pure content file, you can use contentFiles.

    <?xml version="1.0"?>
    <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
        <metadata>
            <id>Traefik</id>
            <version>1.5.1</version>
            <authors>Containous</authors>
            <owners>MikeC</owners>
            <projectUrl>https://github.com/containous/traefik</projectUrl>
            <license type="expression">MIT</license>
            <requireLicenseAcceptance>false</requireLicenseAcceptance>
            <description>Traefik binaries</description>
            <copyright>Copyright ©2020 Containous</copyright>
            <contentFiles>
                <files include="any/any/traefik.exe" buildAction="None" copyToOutput="true"/>
            </contentFiles>
        </metadata>
        <files>
            <file src="traefik.exe" target="contentFiles/any/any/traefik.exe"/>
            <file src="traefik.exe" target="content/traefik.exe"/>
        </files>
    </package>
    

    This way, the traefik.exe will be copied directly to your output folder. Inside of your package, the folder structure will be like this. This folder structure is predefined for content files.

    content
        - traefik.exe
    contentFiles
        - any
            - any
                - traefik.exe
    

    You can even target a distinct or multiple frameworks with this approach. Say you have a special executable for .NET Framework 4.7.2, then adapt the second any to your target framework moniker, here net472. The executable will then only be copied to your output folder if the framework matches your project framework.

    <files include="any/net472/traefik.exe" buildAction="None" copyToOutput="true" flatten="true"/>
    <file src="traefik.exe" target="contentFiles/any/net472/traefik.exe"/>
    

    Of course, you can define multiple executables for different frameworks like this. A few remarks about the NuSpec file. The executable is also copied to the content folder of your package for backwards compatibility, because contentFiles is supported only since NuGet 4.0 using PackageReference.