Update Post-Build with custom nuget package - visual-studio

I need help to be able to update the post-build event of a project thanks to a custom nuget package.
I've created a package thanks to a nuspec file that include a .targets file :
<file src="*.targets" target="build"/>
Here is the .targets file :
<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="AfterBuild" AfterTargets="Build" >
<Message Text="Hello World" Importance="high" />
</Target>
</Project>
Actually, the file is read when i install the package (if i delete some '>', i've an error).
But the .csproj isn't updated (so, nothing in the post-build event textbox).
Did i miss something ?

I agree with Matt Ward. From NuGet 2.5, NuGet recognizes a new top-level folder: \build.
Within the \build folder, you can provide a “.props” file and/or a
“.targets” file that will be automatically imported into the project.
For this convention, the file name must match your package id with
either a “.props” or “.targets” extension.
Please refer to the MSBuild Integration part in following document:
http://blog.nuget.org/20130426/native-support.html
And after install the package, you will see a import node in .csproj which import the package.targets file. Then when build your project, you will see "Hello World" text in output window.

Related

Create nuget containing shared project properties - automatic references

We would like to create a nuget that contains an msbuild properties (.props) file. We do this by creating a nuspec which as the following MIL (most important line) :
<files>
<file src="SharedProperties.props" target="build\SharedProperties.props" />
</files>
How can we change our .nuspec definition so that a project (.csproj) that references this nuget will automatically include the property file ("like" line 3 in this snippet):
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\Shared\SharedProperties.props" />
(Is this even possible ?)
That is a feature of the nuget package design. And nuget has the automatic import targets mechanism. See this document.
The tip is that you should name the targets or props file to <package_id>.props or targets and then pack the file into build folder. That is all.
For an example, I created a lib project and then use this nuspec file to pack:
<?xml version="1.0" encoding="utf-8"?>
<package >
<metadata>
<id>test_A</id>
<version>1.0.0</version>
<title>me</title>
<authors>me</authors>
......
</metadata>
<files>
<file src="test_A.props" target="build" />
</files>
</package>
If my package is called test_A.1.0.0.nupkg, the file should be named as test_A.props file.
Then, when you install the nuget package into a new project, you can check the <new project>.csproj file, the props file is added automatically during the nuget installation.
If you use PackageReference nuget management format to install the nuget package, the file is added under obj\xxx.csproj.nuget.g.props or obj\xxx.csproj.nuget.g.targets file:
For new-sdk project, that also work. If you create a new-sdk class library project, you could use this into csproj file to pack it:
<None Include="<packages_id>.props" Pack="true" PackagePath="build">
When you finish it, install the new package into new-sdk main project, you will find that the props file has imported automatically under obj\xxx.csproj.nuget.g.props file.
According to the documentation the ".props" file should be automatically added to the beginning of a .csproj as an import (a .targets file should go to the end). However, for the new sdk-style projects this doesn't seem to work?

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?
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.
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
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>

how use properties from Directory.Build.props in Import from project file of Visual Studio

I have this situation:
I have a .proj file in project directory:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Compile Include="PQExtensionTest.pq">
<SubType>Code</SubType>
</Compile>
<Content Include="PQExtensionTest.query.pq">
<SubType>Code</SubType>
</Content>
</ItemGroup>
<!-- <Import Project="..\Directory.Build.props" /> -->
<Import Project="$(aProperty)add.targets" />
</Project>
In the solution directory (..\ from project directory) I have file Directory.Build.props:
<Project DefaultTargets="BuildExtension" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<aProperty>$(MSBuildProjectDirectory)/Subdir/</aProperty>
</PropertyGroup>
</Project>
In the project directory I have subdirectory "Subdir", where there is file add.targets, which contains all the targets I need (do not show it's contains here because it is not relevant to the problem).
So all above has this folder structure:
Solution directory
Directory.Build.props
Project Directory
Project.mproj
Subdir
add.targets
Preparing all the above, I expected that aProperty will be initiated before the import and the import of add.targets will happen without problem. But I get error that imported project is not found, and I see in error message that MSBuild tries to import from project directory, and not from subdirectory Subdir.
If I uncomment this row:
<Import Project="..\Directory.Build.props" />
all works fine.
The only reasonable explanation for me of such behavior is that aProperty is empty at the moment of importing, because explicit import happens before implicit one.
Is there any way to force MSBuild to inexplicitly import Directory.Build.props before any other imports, while work in Visual Studio?
"While in Visual Studio"
For C# and VB language project, we don't need to import
Directory.Build.props manually or force it before other imports.
When creating a new project(C# or VB) in VS, open its proj file we can find the format is like this:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
...
</PropertyGroup>
<ItemGroup>
...
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
Every time when creating new C# or VB project, the top line within the <Project>node is Import project="Microsoft.Common.props", and we can find the sentence from this document:
When MSBuild runs, Microsoft.Common.props searches your directory structure for the Directory.Build.props file (and Microsoft.Common.targets looks for Directory.Build.targets). If it finds one, it imports the property.
So in visual studio, we don't need to force it before other imports.Its always called after import Microsoft.Common.props, and since the import Microsoft.Common.props is always first line of project node by default, the Directory.Build.Targets is always implicitly imported right after the Microsoft.Common.props and before others.
Note: This feature only supports C# and VB, cause only these two kinds of projects will import the Microsoft.Common.Props in proj file.
And for other kinds of projects, just like your .mproj or
.vcxproj(C++), this feature(Directory.Build.props) is not supported
yet.
So the Directory.Build.Targets or .props is the same as any custom .props. It doesn't make difference between Directory.Build.Targets and anyName.props.
In this way,to read the value in it we have to use import project to call it manually. And that's why the build can't succeed until you uncomment the row:<Import Project="..\Directory.Build.props" />
The way to import properties from 'Directory.Build.props' file from nested folder structure is given below:
Refer: https://learn.microsoft.com/en-us/visualstudio/msbuild/customize-your-build?view=vs-2019
Note that:
1. These property file definition works from MSBuild tools version 15.0
2. You need to be aware of where to place this import: At the beginning of the file or at the end of the file. Generally it is good to place at the end as nested properties will be visible to parent properties.

How to deal with relative NuGet packages paths to facilitate easy pckages dir relocation with NuGet.Config?

We moved NuGet's packages folder to some other place and created a NuGet.Config file at the slns level:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<config>
<add key="repositoryPath" value="../.nugetPackages" />
</config>
</configuration>
We also have this in few of our csproj files:
<Import Project="..\..\..\Proj\packages\Microsoft.Azure.DocumentDB.1.7.1\build\Microsoft.Azure.DocumentDB.targets" Condition="Exists('..\..\..\Proj\packages\Microsoft.Azure.DocumentDB.1.7.1\build\Microsoft.Azure.DocumentDB.targets')" />
<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('..\..\..\Proj\packages\Microsoft.Azure.DocumentDB.1.7.1\build\Microsoft.Azure.DocumentDB.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\Proj\packages\Microsoft.Azure.DocumentDB.1.7.1\build\Microsoft.Azure.DocumentDB.targets'))" />
</Target>
And this too:
<Import Project="..\..\packages\PostSharp.4.2.17\tools\PostSharp.targets" Condition="Exists('..\..\packages\PostSharp.4.2.17\tools\PostSharp.targets')" />
<Target Name="EnsurePostSharpImported" BeforeTargets="BeforeBuild" Condition="'$(PostSharp30Imported)' == ''">
<Error Condition="!Exists('..\..\packages\PostSharp.4.2.17\tools\PostSharp.targets')" Text="This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://www.postsharp.net/links/nuget-restore." />
<Error Condition="Exists('..\..\packages\PostSharp.4.2.17\tools\PostSharp.targets')" Text="The build restored NuGet packages. Build the project again to include these packages in the build. For more information, see http://www.postsharp.net/links/nuget-restore." />
</Target>
My question is how to deal with such relative package paths in the csproj files to keep things generic (so we could easily move the package folder anytime anywhere)?
Is there maybe some variable we can use like $NuGetPackagesDir or something like that?
(We use Vs2015, NuGet 5.3 and TeamCity build server [I don't remember current version..])
You cannot really have a generic path and still use the NuGet tooling within Visual Studio since when you update a package the hint path will be updated by NuGet back to a relative path if you are using a packages.config file.
Whilst you could specify a global packages folder the hint paths will be relative to that path depending on where you put the solution.
The hint path problem goes away when you use a project.json file or have a new .NET Core style project that uses PackageReference elements in the .csproj file. If you are using a packages.config file then your .csproj file will have hint paths pointing to assemblies within the packages folder.

Is there a way to add source files to visual studio project from command-line?

I want to use sublime to edit a visual studio project.
I have a custom build:
{
"cmd": ["c:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\MSBuild.exe"],
"working_dir": "${project_path:${folder:${file_path}}}/../Project"
}
But if I add new files I also need to include them in the project.
Is there a way to do this from the command line, maybe at compile-time?
I am working with opengl using c++;
I basically set up a project using one of the examples provided on the opengl website.
Then I opened the project folder in sublime text and successfully compiled it using the custom build system.
However, when I add NEW source files to the project (*.h and *.cpp) I get a linking error.
I get the same error when I build in visual studio.
The error disappeared after I had included the files by manually browsing and adding them to the project.
What I wanted was a way to automatically add all the source files in a folder to the project(via command line, or wildcard or smth else).
This way I can easily work on a vs2010 project in sublime, add new source files and build the project.
Or maybe there already is a better workflow for this?
You could try to modify your .vcxproj file to include any .h and .cpp file in your project folder or folders below.
In case of a c++ VS project you can try to alter your .vcxproj file like this:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- rest of project file untouched -->
<!-- start of modified part -->
<ItemGroup>
<ClInclude Include="**\*.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="**\*.cpp" />
</ItemGroup>
<!-- end of modified part -->
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
Be aware that adding files to your project from inside VS at later point will replace the modification described above!
As an alternative you could also create an external project file holding the same <ItemGroup /> elements described above and include this project file into your .vcxproj.
I'll add an example of this alternative if you're interested.

Resources