Search code examples
visual-studio.net-corenugetnuget-packagenuspec

NuGet to copy files into the project directory


I've created a .nuspec file which packages a bunch of .proto files for sharing between projects. This is great. Unfortunately, for the .proto files to be built, they need to be actually copied over to the project directory, not just referenced. Note that this is a .NET Core project.

Right now my definition creates a package that, when used, references the files in the project, but these files still reside in the original .Nuget folder, and that's not really what I need.

Here's the .nuspec definition that I've got right now:

<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
    <metadata>
        <id>GRPCProtoFiles</id>
        <version>1.0.0</version>
        <authors>Author</authors>
        <requireLicenseAcceptance>false</requireLicenseAcceptance>
        <description>.proto files NuGet package.</description>
        <dependencies>
          <dependency id="Google.Protobuf" version="3.14.0"/>
          <dependency id="Grpc" version="2.35.0"/>
        </dependencies>
        <contentFiles>
          <files include="any/any/Protos/**/*.proto" buildAction="None" copyToOutput="false" />
        </contentFiles>
    </metadata>
    <files>
        <file src="**\*.proto" target="contentFiles\any\any\Protos" exclude="google\**" />
    </files>
</package>

I know that "build action" is currently set to "none", but I can get that fixed once the files land in the correct folder upon package installation. As is, if I try to set the build action for the proto files to the correct one (which is Protobuf Compiler) then I get an error, because the files aren't where they're supposed to be.

What am I doing wrong?

EDIT:

OK, I've read up a bit on what might be happening and why. Specifically this answer gave me a lot of insight. However I'm left wondering how I can actually do what I need to do. Right now it seems that what I want to do is impossible with NuGet and I'm using the wrong tool for the job. But besides this one limitation NuGet seems ideal - it's able to pick just the .proto files from the source project and just publish that rather then pre-built libraries for various target systems.

I could, possibly, add a pre-build action to copy the files to the project dir, but I've no idea how to reference the source NuGet package folder (especially if the version changes). Any idea if this problem even has a solution?


Solution

  • I really know your ideas. First, make these proto files be packed into nupkg with Build Action None. And then use pre-build event under the main project to handle the files from the nupkg. The question is that how to use msbuild property to get files from nupkg since Net Core projects use Link properties to references the files under the main project rather than copy the real nuget files into the main project folder.

    And under the Link properties, you could not change the Build Action of that file because it actually does not exist under the main project folder and the main project has no duty to handle the file.

    There are two functions to solve it:

    =============================================

    Function one

    You could did this directly under nupkg itself and after that, the proto files will be copied automatically during build process with the nuget package. Try the following steps and you should not add any copy task from another main project which install the nuget package.

    You have to use <packages_id>.props/targets file into nupkg to get what you want. Refer to this official document.

    1) create a file called <packages_id>.props file. In your side, it should be named as GRPCProtoFiles.props so that it will work.

    enter image description here

    2) add these on the GRPCProtoFiles.props file:

    <Project>
    
        <Target Name="CopyFiles" BeforeTargets="Build">
            <ItemGroup>
                <File Include="$(MSBuildThisFileDirectory)..\contentFiles\**\*.*"></File>
            </ItemGroup>
            <Copy SourceFiles="@(File)" DestinationFolder="$(ProjectDir)\Protos"></Copy>
        </Target> 
    
    
    </Project>
    

    3) add these on the GRPCProtoFiles.nuspec file to include the props file into nupkg:

    <file src="build\*.props" target="build" />
    

    The whole GRPCProtoFiles.nuspec file should be like this:

    <?xml version="1.0"?>
    <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
        <metadata>
            <id>GRPCProtoFiles</id>
            <version>1.0.0</version>
            <authors>Author</authors>
            <requireLicenseAcceptance>false</requireLicenseAcceptance>
            <description>.proto files NuGet package.</description>
            <dependencies>
              <dependency id="Google.Protobuf" version="3.14.0"/>
              <dependency id="Grpc" version="2.35.0"/>
            </dependencies>
            <contentFiles>
              <files include="any/any/Protos/**/*.proto" buildAction="None" copyToOutput="false" />
            </contentFiles>
        </metadata>
        <files>
            <file src="xxx.proto" target="contentFiles\any\any\Protos" exclude="google\**" />
            <file src="build\*.props" target="build" />
        </files>
    </package>
    

    4) then re-pack the new version of nuget project. Before you install the new release version, please clean nuget caches first or just delete all files under C:\Users\xxx\.nuget\packages

    After the installation of the nuget package, please click Build button to execute the target.

    More similar to this issue.

    ====================================================

    Function Two

    You could not do any internal steps for nupkg itself. You could directly copy the file from the external main project.

    1) do not do any steps under Function One for your nuget package. Use your old nuget package.

    Modify the Project A's csproj file which has installed GRPCProtoFiles nuget package.

    Add <GeneratePathProperty>true</GeneratePathProperty> for PackageReference GRPCProtoFiles to generate an exclusive property as $(PkgGRPCProtoFiles) to get the path of the nupkg's content.

    The whole is like this:

    <ItemGroup>
        <PackageReference Include="GRPCProtoFiles" Version="1.0.0">
          <GeneratePathProperty>true</GeneratePathProperty>
        </PackageReference>
     </ItemGroup>
    

    Then, right-click on the Project A-->Properties-->Build Events-->add these under Pre-build event Command Line:

    xcopy  /s /e /y /i  $(PkgGRPCProtoFiles)\contentFiles\any\any\Protos  $(ProjectDir)\Protos
    

    enter image description here

    Or, add this target into ProjectA.csproj file:

    <Target Name="CopyFiles" BeforeTargets="PrepareForBuild">
            <ItemGroup>
                <File Include="$(PkgGRPCProtoFiles)\contentFiles\**\*.*"></File>
            </ItemGroup>
            <Copy SourceFiles="@(File)" DestinationFolder="$(ProjectDir)\Protos"></Copy>
    </Target>
    

    enter image description here