Use NuGet contentFiles to copy a file to build output directory - visual-studio

I need my NuGet package to add a file to a project and set its "Copy to Output Directory" flag.
So far, I'm using install.ps1 script, as seen in the answer by #workabyte to Set content files to “copy local : always” in a nuget package.
But I've understood that NuGet 3.3 and newer has a native support using its contentFiles model.
My understanding was that I create a directory structure like:
contentFiles\
any\
any\
image.png
And then in my .nuspec:
<?xml version="1.0"?>
<package>
<metadata minClientVersion="3.3.0">
...
<contentFiles>
<files include="**/*.png" buildAction="None" copyToOutput="true" />
</contentFiles>
</metadata>
</package>
When the package is compiled, the image.png with its directory structure is added to the .nupkg.
But, when I install the package in Visual Studio (2017), the image.png is not even added to the project.
What am I doing wrong?

The contentFiles feature is supported only when using the PackageReference "style" of referencing NuGet packages. It does not work for projects using packages.config (not that there is no migration path at the time of writing other than uninstalling all packages, changing the type and installing all of them again).
See NuGet's blog post "NuGet is now fully integrated into MSBuild", especially the section "What about other project types that are not .NET Core?" for instructions on how to set the preferred way of referencing NuGet packages.

Related

UnPack NuGet package that gets created on build

I am trying to unpack the nuget package that gets created during the build.
My Directory.Build.props file looks like this:
<?xml version="1.0" encoding="utf-8"?>
<Project>
<PropertyGroup>
<RestorePackagesPath>C:\packages</RestorePackagesPath>
</PropertyGroup>
<PropertyGroup>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageOutputPath>C:\LocalNuGetPackages</PackageOutputPath>
</PropertyGroup>
<PropertyGroup>
<Version>1.0.0.1</Version>
</PropertyGroup>
<Target Name="UnPack" AfterTargets="Pack">
<Exec Command="nuget install $(PackageId) -Version $(PackageVersion) -Source C:\LocalNuGetPackages -OutputDirectory C:\packages" />
</Target>
</Project>
But this gives a different directory structure as Visual Studio is doing it.
Visual Studio produces the following directories
C:\packages\$(PackageId)\$(PackageVersion)\
But the used command (nuget install) produces
C:\packages\$(PackageId).$(PackageVersion)\
Is there a way to call the Visual Studio "internal" nuget to get the same directory structure, or am I missing an argument that enables this structure?
Currently I am using the nuget.exe from here https://www.nuget.org/downloads which I have added to PATH in my system variables.
Side note, I am not trying to install the NuGet package to any project, I am just looking to unpack it like Visual Studio does.
That is designed by that. global nuget caches always make the nuget path like package_id/version/xxx. That's the way VS IDE unpacks NuGet packages into the global cache mechanism.
And when you use nuget install or packages folder under the solution folder by packages.config, it actually likes package_id.version/xxx.
This difference is not what we can handle and can belong to the design itself. So I have reported the issue to the Team.
You could vote it and add any comments if I did not describe it in detail. Hope it could solve your confusion as soon as possible.
As was pointed out in the github issue created by #Perry the command I was actually looking for was
nuget add "C:\LocalNuGetPackages\castle.core.4.4.0.nupkg" -Source C:\packages -expand
Instead of
nuget install castle.core -Version 4.4.0 -Source C:\LocalNuGetPackages -OutputDirectory C:\packages
The add command lacks the ability to fetch the nuget package from a "source" (eg. NuGet.org or C:\LocalNuGetPackages) and you already need the *.nupkg downloaded and ready to unpack.
But this lack of ability is irrelevant in my situation, since upon build I am creating the *.nupkg and they are ther for me to unpack.
Don't get confused with the argument being called -Source in the add it is actually the equivalent of -OutputDirectory for the install command.
So for other users/cases it would be desirable to have the install command do the different directory structure, but for me add was what I wanted.

Copy JSON file to bin from class library nuget package

I apologize for the vague title, but I am not sure how to phrase it.
I am working with a "custom" appsettings.json folder. The json file lives in the class library that is using it. I want to create a nuget package to install this class library but also make sure that appsettings.json is copied into the correct directory (if I am installing it in a console app, the build output directory).
I have seen one "answer"
How can I set the 'copy to output directory' property in my nuspec file?
but I am using VS 2019 and .NET Standard 2.0. I am pretty frustrated so any help (even if told not possible) is appreciated! Thanks in advance.
If you just want to install this nuget package only on net core projects, you could just add these node under the net standard project's csproj file:
<ItemGroup>
<None Update="xxx\appsettings.json" Pack="true" PackagePath="contentFiles\any\any;content">
<PackageCopyToOutput>true</PackageCopyToOutput>
</None>
</ItemGroup>
Then, repack the net standard project into a nuget package, before install it under a new project, you should clean nuget caches first.
If you want to install this nuget package into a net framework project, you should try to use <package_id>.props file on the net standard2.0 lib project.
Please try the function under this link.

Xamarin.Android: Package XX is not compatible with monoandroid81 (MonoAndroid,Version=v8.1)

I encounter a stranger behavior with a Xamarin.Android project in Visual Studio on Windows.
I've created a new Android blank project, and I try ton install the NuGet package "AForge" (a mathematics library), but I get the following error message:
Package AForge 2.2.5 is not compatible with monoandroid81
(MonoAndroid,Version=v8.1).
I've already tried to re create several projects, after having relaunched Visual Studio and the computer, but the problem is still the same.
However, if I do the same thing on Mac, through Visual Studio for Mac, I don't encounter the same problem: the package is well installed.
In add, I can see in the package.config file, that the package il well related to monodroid81:
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="AForge" version="2.2.5" targetFramework="monoandroid81" />
</packages>
Would you have any explanation about this issue?
Update: new issue
Hi #Leo Liu-MSFT, I come back to you as I encounter an new issue. The dll copy works fine locally, but I get an error on AppCenter:
CoreCompile:
/Library/Frameworks/Mono.framework/Versions/5.10.1/lib/mono/4.5/csc.exe
/noconfig /nowarn:1701,1702 /nostdlib+ /errorreport:prompt /warn:4
/define:DEBUG;TRACE;XAMARIN_ANDROID_v1_0;MOBILE;ANDROID;ANDROID_1;ANDROID_2;ANDROID_3;ANDROID_4;ANDROID_5;ANDROID_6;ANDROID_7;ANDROID_8;ANDROID_9;ANDROID_10;ANDROID_11;ANDROID_12;ANDROID_13;ANDROID_14;ANDROID_15;ANDROID_16;ANDROID_17;ANDROID_18;ANDROID_19;ANDROID_20;ANDROID_21;ANDROID_22;ANDROID_23;ANDROID_24;ANDROID_25;ANDROID_26;ANDROID_27
/reference:/Library/Frameworks/Mono.framework/External/xbuild-frameworks/MonoAndroid/v1.0/Java.Interop.dll
/reference:/Users/vsts/.nuget/packages/microsoft.appcenter.analytics/1.7.0/lib/MonoAndroid403/Microsoft.AppCenter.Analytics.Android.Bindings.dll
/reference:/Users/vsts/.nuget/packages/microsoft.appcenter.analytics/1.7.0/lib/MonoAndroid403/Microsoft.AppCenter.Analytics.dll
/reference:/Users/vsts/.nuget/packages/...
Services/AudioService.cs(8,7): error CS0246: The type or namespace
name 'AForge' could not be found (are you missing a using directive or
an assembly reference?)
[/Users/vsts/agent/2.136.1/work/1/s/XxxApp/XxxApp.csproj] Done
Building Project
"/Users/vsts/agent/2.136.1/work/1/s/XxxApp/XxxApp.csproj"
(PackageForAndroid target(s)) -- FAILED.
So I tried your suggestion, but I get an error when I try to save the package:
a local file header is corrupted'
Would you have another suggestion?
Would you have any explanation about this issue?
You can get the official document here:
Description: With PackageReference, assemblies present at the root of
lib folder without a target framework specific sub-folder are ignored.
NuGet looks for a sub-folder matching the target framework moniker
(TFM) corresponding to the project’s target framework and installs the
matching assemblies into the project.
In this case, NuGet will consider installing this package to the .NET Framework, which is not compatible with monoandroid81. Then you will that error "Package AForge 2.2.5 is not compatible with monoandroid81".
To resolve this issue, the best way is contact the author of the AForge NuGet package to update this package, or you can also add reference dll file directly to your project.
Besides, if you still want to use nuget to manager this package, I would like provide you a workaround to resolve this issue:
Download this nuget package from nuget.org.
Copy the download package, rename it with .zip, like aforge.2.2.5 - Copy.nupkg.zip, then unzip it.
Open the package aforge.2.2.5.nupkg with NuGet Package Explorer(Get it from Microsoft store), add a new folder MonoAndroid81 under the lib node, add exists file AForge.dll and AForge.xml from the copy folder aforge.2.2.5 - Copy.nupkg.zip, save this nuget package.
Add this new create nuget package to the nuget local feed, then add this nuget package to the project. Do not forget delete the nuget package cache in the C:\Users\<UserName>\.nuget\packages\aforge before you add the package.
Hope this helps.

How to associate new project and already downloaded NuGet packages?

How to easily associate a new project and already downloaded NuGet packages?
An example scenario:
I created a Visual Studio solution and project, named mylib. And I installed Nuget packages, like C++ boost library. I can use the boost library right away without setting header/linker directories manually. This is very convenient.
Now I create a new project (or add an existing project) under the same solution, named executable. I also want to use the boost library in this project.
Unfortunately, there is no graphical or IDE interface to link the dependency for the new project.
The above picture shows NuGet packages are installed, but newly added project executable still don't have links.
To correct this, I have to manually modify the project (e.g., .vcxproj) XML file. I copied from mylib and pasted it to executable.
<ImportGroup Label="ExtensionTargets">
<Import Project="packages\boost.1.65.1.0\build\native\boost.targets" Condition="Exists('packages\boost.1.65.1.0\build\native\boost.targets')" />
<Import Project="packages\boost_regex-vc141.1.65.1.0\build\native\boost_regex-vc141.targets" Condition="Exists('packages\boost_regex-vc141.1.65.1.0\build\native\boost_regex-vc141.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('packages\boost.1.65.1.0\build\native\boost.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\boost.1.65.1.0\build\native\boost.targets'))" />
<Error Condition="!Exists('packages\boost_regex-vc141.1.65.1.0\build\native\boost_regex-vc141.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\boost_regex-vc141.1.65.1.0\build\native\boost_regex-vc141.targets'))" />
</Target>
It works then.
Or, uninstalling already downloaded NuGet packages and reinstalling them also works. But that's obviously not a good solution.
I'm wondering if there is a nice way to re-associate dependency between already downloaded NuGet packages and projects. I was unable to find such feature in the project property pages in Visual Studio 2017.
To correct this, I have to manually modify the project (e.g., .vcxproj) XML file. I copied from mylib and pasted it to executable
According to your description, that seems the package boost has not been installed properly to the project executable. So you can use the NuGet command line in the Package Manager Console:
Update-Package -reinstall
to force reinstall the package to the executable project.

Configuring NuGet dependencies and VS2013

I wish to set up my NuGet dependencies such that:
Anyone checking out the project (i.e. other developers) will get the correct dependencies.
Anyone using my package will get the correct dependencies.
In VS2013 my NuGet dependencies are specified in packages.config
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="log4net" version="2.0.3" targetFramework="net45" />
</packages>
I noticed that you can also specify dependencies in the .nuspec file (much like a maven pom file) but this doesn't seem to be used by the package manager plugin.
<dependencies>
<dependency id="log4net" version="2.0.3"/>
</dependencies>
What is the correct way to configure dependencies and why?
The packages.config file lists all the packages that are currently installed for a given project. This includes any dependencies. NuGet will automatically update this file any time you add, update, or remove packages from your project, either through the NuGet UI or Package Manager Console (e.g. Install-Package log4net).
NuGet will automatically install any dependencies for a given package. NuGet will also follow the dependency chain for each of these other packages, until all dependent packages are installed. It will add these packages to the packages.config file to show that they have been installed. So you may initially install one package, but 10 packages may end up being installed based on all the dependencies. Again, this will be reflected in the packages.config file, which you shouldn't touch.
You do NOT need to edit this file.
The only way you should update packages.config is by installing or updating packages via the NuGet UI or Package Manager Console. Do not edit this file by hand.
EDIT: Add section about package restore
The packages.config file is also used by NuGet to restore packages (Automatic Package Restore) that do not exist on the user's hard drive. This is useful since you do not have to commit the packages folder to source control. So when another developer checks out your project, NuGet will automatically download any packages listed in packages.config before building. NOTE: This is not the same as installing packages. It is assumed that you've already used NuGet to install the package, which will update project references, add files, modify .config files, etc. Those changes should already be committed. All package restore does is download the binaries as if they were also committed, without bloating your repository. Package restore is mainly used for other developers building your project from source code. It is not applicable for installing a package you create, which is what the .nuspec file is for.
The .nuspec file is used when you are creating your OWN packages for others to use. The <dependencies/> section lists the packages YOUR package needs. When a developer installs YOUR package, NuGet will automatically install any dependencies listed in your .nuspec. Just like above, NuGet will follow the dependency chain by looking at each packages .nuspec file to see what dependencies it requires.
So unless you're CREATING a package, you do not have to worry about .nuspec files.
As you can see, NuGet uses the .nuspec file in each package to determine if there are any dependencies. Installing a package will update the packages.config files.
TL;DR: packages.config and .nuspec files are different things although somewhat related.

Resources