0
votes

I have my own nuget package, that contains some native dll files.

When I build project, that references the nuget package, I want all native files from the nuget package to be copied into output directory.

How to create such nuget pacakge that copies the files into output dir?

I've tried to use Baseclass.Contrib.Nuget.Output, but I can't get it work on my Build Server (TFS 2013):

C:\Builds\1\MyProject\BuildName\src\MyProject\Packages\Baseclass.Contrib.Nuget.Output.2.1.0\build\net40\Baseclass.Contrib.Nuget.Output.targets (73): The source file for this compilation can be found at: "C:\Users\MHABLDSvc\AppData\Local\Temp\fd4f7f9d-59e3-4f6a-af8c-6a48deb7af3e.txt"

C:\Builds\1\MyProject\BuildName\src\MyProject\Packages\Baseclass.Contrib.Nuget.Output.2.1.0\build\net40\Baseclass.Contrib.Nuget.Output.targets (73): An error has occurred during compilation. error CS1705: Assembly 'Microsoft.Build.Utilities.Core, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' uses 'Microsoft.Build.Framework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' which has a higher version than referenced assembly 'Microsoft.Build.Framework, Version=12.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'

C:\Builds\1\MyProject\BuildName\src\MyProject\Packages\Baseclass.Contrib.Nuget.Output.2.1.0\build\net40\Baseclass.Contrib.Nuget.Output.targets (73): The "PackageFilter" task was not found. Check the following: 1.) The name of the task in the project file is the same as the name of the task class. 2.) The task class is "public" and implements the Microsoft.Build.Framework.ITask interface. 3.) The task is correctly declared with in the project file, or in the *.tasks files located in the "C:\Program Files (x86)\MSBuild\14.0\bin\amd64" directory.

C:\Builds\1\MyProject\BuildName\src\MyProject\Packages\Baseclass.Contrib.Nuget.Output.2.1.0\build\net40\Baseclass.Contrib.Nuget.Output.targets (73): An error has occurred during compilation. error CS1705: Assembly 'Microsoft.Build.Utilities.Core, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' uses 'Microsoft.Build.Framework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' which has a higher version than referenced assembly 'Microsoft.Build.Framework, Version=12.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'

C:\Builds\1\MyProject\BuildName\src\MyProject\Packages\Baseclass.Contrib.Nuget.Output.2.1.0\build\net40\Baseclass.Contrib.Nuget.Output.targets (73): The "PackageFilter" task was not found. Check the following: 1.) The name of the task in the project file is the same as the name of the task class. 2.) The task class is "public" and implements the Microsoft.Build.Framework.ITask interface. 3.) The task is correctly declared with in the project file, or in the *.tasks files located in the "C:\Program Files (x86)\MSBuild\14.0\bin\amd64" directory.

1
Why not setting CopyLocal of your referenced assemblies in your project file?user1064248
because they are not assemblies and they are not referenced.Liero
You can do that with any file added to your project. But then the property is named Copy to Output Directory. This would also work then for local builds.user1064248
Imagine nuget package for EmguCV for example. It contains a lof of native opencv files (hundreds of megabytes). If you add such package, would you like it to add all the files into your project? And if you had an application, that uses several such packages, it would be total mess. The other thing is, that some people, including be does not check it nuget packages into source control, since we use automatic nuget restore during build. I want to avoid to check it tons of large files into source control, especially when they are not sourcesLiero
If you're creating a package, then see docs.nuget.org/create/creating-and-publishing-a-package. You can either use <file src="..." /> items in your .nuspec file, or add a PowerShell script if you need better copy logic (e.g. detecting if .dlls are native or not).makhdumi

1 Answers

0
votes

You could write some MSBuild logic to do this. I haven't tested this code but you could use something similar to the following (with tweaking):

 <Target Name="AfterBuild">
   <ItemGroup>
     <NugetDlls Include="$(SolutionDir)\packages\mypackage\**\*.dll" />
   </ItemGroup>
   <Copy SourceFiles="@(NugetDlls)" DestinationFolder="$(OutputPath)" />
 </Target>

You can even automate this change via Nuget using a start.ps1 PowerShell script in the Tools directory. In this case I would create a separate .targets file with the MSBuild Target above, add it to your Nuget package and then import this targets file in the project file via the install.ps1 script. The install.ps1 would be similar to (although I haven't tested this, it may need some tweaks):

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

# Add targets file to .csproj
$TargetsFile = 'AddToOutput.targets'
$path = [System.IO.Path]
$ProjectDirectory = $path::GetDirectoryName($project.FileName)
$TargetsPath = [System.IO.Path]::Combine($toolsPath, $TargetsFile)

Add-Type -AssemblyName 'Microsoft.Build, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'

$MSBProject = [Microsoft.Build.Evaluation.ProjectCollection]::GlobalProjectCollection.GetLoadedProjects($project.FullName) |
    Select-Object -First 1

$ProjectUri = New-Object -TypeName Uri -ArgumentList "file://$($project.FullName)"
$TargetUri = New-Object -TypeName Uri -ArgumentList "file://$TargetsPath"

$RelativePath = $ProjectUri.MakeRelativeUri($TargetUri) -replace '/','\'

$ExistingImports = $MSBProject.Xml.Imports |
    Where-Object { $_.Project -like "*\$TargetsFile" }
if ($ExistingImports) {
    $ExistingImports | 
        ForEach-Object {
            $MSBProject.Xml.RemoveChild($_) | Out-Null
        }
}
$MSBProject.Xml.AddImport($RelativePath) | Out-Null
$project.Save()