Configure NuGet package to add a build event when installed - nuget-package

Does anyone know if it's possible to add something to a nuspec file so that when a package is installed via NuGet a pre or post build event to a project?

I think editing the user's PostBuildEvent property is the wrong way to go about adding a post-build action to a project. I believe the recommended way is to put your custom action into an MSBuild Target that is imported into the project file. As of NuGet 2.5, if you include a 'build' folder in your package (at the same level as content and tools) and it contains a {packageid}.targets file or {packageid}.props file, NuGet will automatically add an Import to the project file when you install the package.
For example you have a package called MyNuGet. You create a file build\MyNuGet.targets containing:
<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="MyNuGetCustomTarget" AfterTargets="Build">
<Message Importance="high" Text="Message from MyNuGetCustomTarget. Configuration: $(Configuration)" />
</Target>
</Project>
This creates a custom target that is configured to run after the standard Build target. NuGet will handle inserting the Import on install and removing it on uninstall.

This is a slightly cleaner way to set a build event from an install.ps1 powershell script in the NuGet Package:
$project.Properties.Item("PostBuildEvent").Value = "your build event here"

Not to the .nuspec file itself, but you can to install.ps1 which you can add to your package using nuspec. Here is what I'm doing (I don't know if it's the best way to do it but it does work):
param($installPath, $toolsPath, $package, $project)
$project.Properties | where { $_.Name -eq "PreBuildEvent" } | foreach { $_.Value = "copy `"`n`$(ProjectDir)Web.`$(ConfigurationName).config`" `"`$(ProjectDir)Web.config`"" }

Related

Call executable inside Nuget package cache from launchSettings.json

I created a small executable that is used to run and debug specific libraries from other solutions. The executable is given the path to a library as a command line argument which the executable loads at runtime.
I want to put my executable in a Nuget package, and have consuming libraries just add the Nuget package to be able to run and debug, using a launchSettings.json.
Because the executable is in the tools/ directory of the Nuget package, its path is put into the PATH environment variable by the Package Manger Console. Therefore the launchSettings can simply just call the executable.
I create my Nuget package with nuget.exe pack my.nuspec -Version 1.0.0, with my.nuspec containing:
<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>DebugHost</id>
<version>$version$</version>
<title>DebugHost</title>
<authors>Some company</authors>
<owners>Some company</owners>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>DebugHost</description>
<summary>DebugHost</summary>
<copyright>2020, Some company</copyright>
</metadata>
<files>
<file src="bin\Debug\netcoreapp3.1\*" target="tools" />
</files>
</package>
From consuming library projects I put in Properties\launchSettings.json containting:
{
"profiles": {
"DebugHost": {
"commandName": "Executable",
"executablePath": "my.exe",
"commandLineArgs": "$(ProjectDir)$(OutDir)$(TargetName)$(TargetExt)"
}
}
}
I have three different questions that are somewhat related, as any might answer another:
This setup only works as long as I keep the Package Manager Console opened. If I try to Debug / Launch the library without first opening the Package Manager Console, the launch fails because it cannot find the executable. Unable to start program 'my.exe'. The system cannot fint the file specified.
Can I for example add a targets file in the Nuget package that will hint its path during build/launch, which in turn I can use as a variable in the launchSettings.json? (If so, how?)
This setup requires that a Properties\launchSettings.json is used in the consuming project. Can I make my Nuget package create that file?
Can the file be an asset in the Nuget that gets copied to the correct place?
Or can I even have an install.ps1 script that creates the json, with the correct absolute path to the executable in it? This would make question 1 answered as well.
The above situation only works with nuget.exe pack and a .nuspec.
Can I instead move stuff into my (new style) .csproj and get this to work with just dotnet.exe pack and the .csproj? (or msbuild.exe -t:pack and the .csproj?) So ultimately I can also just create the package by simply right clicking the project in Visual Studio and selecting Pack from the context menu?

TypeScript's Linked Files are unseen by current project's TypeScript files

I've completed rewritting my javascript framework project in TypeScript. Now I'm trying to use these files in many other web projects.
I've tried linking (Linked File) them into the web project. First thing I noticed, I can't change the Build Action to "TypeScriptCompile". .ts files are compiled in their source folder, not where they are linked. The problem is that when creating a new file .ts file in web project, it doesn't see the linked files and I get a TypeScript error.
Also, everytime I try to build a project with Linked TypeScript Files, it crashes Visual Studio.
I'm using AMD and RequireJS. The structure needs to be respected. I'm setting the baseURL to /Scripts/ and my framework and TS files need to be inside that structure.
Does anyone have any idea ?
Here's a sample of what I'm trying
Content from Linked File from a Framework project:
export class Log {
static error (msg: string) { console.log(msg); }
}
Content from File in web project using Linked File:
import fw = module('linkedFile');
fw.Log.error('this file can\'t find the linked file, so this code won\t work');
Thanks !
UPDATE:
The only way I found so far is to copy Frameworks files from the source project to my web project on Post Build:
xcopy /y /e /s /d "$(ProjectDir)Scripts\." "$(ProjectDir)..\..\OtherProject\Scripts\."
The problem with this is we have to edit framework files in the first project, otherwise, our changes will be overwritten.
UPDATE 2:
I'm currently using this script to automatically copy all linked files where they are in the project. You need to edit your CSPROJ file and it has to be a WEB Project. Check the link for a complete description:
http://consultingblogs.emc.com/jamesdawson/archive/2008/06/03/using-linked-files-with-web-application-projects.aspx?CommentPosted=true#commentmessage
<!-- ======================== -->
<!-- Copy linked files -->
<Target Name="_CopyLinkedContentFiles">
<!-- Remove any old copies of the files -->
<Delete Condition=" '%(Content.Link)' != '' AND Exists('$(WebProjectOutputDir)\%(Content.Link)') " Files="$(WebProjectOutputDir)\%(Content.Link)" />
<!-- Copy linked content files recursively to the project folder -->
<Copy Condition=" '%(Content.Link)' != '' " SourceFiles="%(Content.Identity)" DestinationFiles="$(WebProjectOutputDir)\%(Content.Link)" />
</Target>
<!-- Override the default target dependencies to -->
<!-- include the new _CopyLinkedContentFiles target. -->
<PropertyGroup>
<PrepareForRunDependsOn>
$(PrepareForRunDependsOn);
_CopyWebApplication;
_CopyLinkedContentFiles;
_BuiltWebOutputGroupOutput
</PrepareForRunDependsOn>
</PropertyGroup>
<PropertyGroup>
<!-- <PostBuildEvent>$(MSBuildBinPath)\msbuild.exe "$(ProjectDir)_build\site.xml"</PostBuildEvent> -->
</PropertyGroup>
<!-- ======================== -->
When you link a file the path is still relative to its actual location, so you'd have to use a whole bunch of ../../../ to get there.
One option is to set the file to copy on build so it will be copied to your bin folder. I do this for tests but not for releasable code.
Another option is to package your modules to make it easier to use particular versions. You could use a private NuGet repo to do this.

How do I include an XSD file for a configuration section with a NuGet package?

I have a library with a pretty verbose configuration section. I've created an XSD and would like to distribute that with my package so that when a user installs the package, Visual Studio knows about the XSD without the user needing to do anything extra. How do I do this?
You can include any files you want in a nuget package by placing them in the content directory. These will then be installed into the root of your target project when the package is installed. If you're using a nuspec file to build your package you would add the following element under the element.
<files>
<file src="Configuration\MyXsd.xsd" target="content\TargetFolderName" />
</files>
This will create the following file in the target project
\TargetFolderName\MyXsd.xsd
Once the xsd is in the target project visual studio should pick it up automatically for validating your config section.
This has recently become more complicated with SDK style projects and the different ways of referencing nuget packages.
Note the end of this section.
Basically, if a nuget project is referenced by package.config file, the files from the content folder of the nuget package will be copied to the referencing project. If the nuget package is referenced by PackageReference in the project file, the files from the contentFiles folder within the package will be used. It is recommended to include both.
Now if you are using a .nuspec file to configure your nuget package, you can use SynXsiS answer to include the file to both directories:
<files>
<file src="Configuration\MyXsd.xsd" target="content\TargetFolderName" />
<file src="Configuration\MyXsd.xsd" target="contentFiles\any\any\TargetFolderName" />
</files>
However, if you want to configure it in the .csproj file of SDK projects, you have to add the file you want to include in the nuget package into the project file with the following properties:
<ItemGroup>
<None Include="MyXsd.xsd">
<Pack>true</Pack>
<PackagePath>contentFiles\any\any\TargetFolderName;content\TargetFolderName</PackagePath>
</None>
</ItemGroup>
The any\any\ part of the path for the contentFiles specifies for which language (cs, vb, ...) and target framework the file is meant.
Note, that the item not necessarily has to be of type "None" it could also be "Content" and others as described here.
You can find all information for this structure here, general information of the folder structure can be found here and help for the SDK style projects here.

Excluding all files of a type in NuGet

Problem: I have .sql files in my Visual Studio project which I want to exclude from my NuGet package. These .sql files are nested into various directories, and I have been unable to successfully exclude all .sql files from my nuget package.
1: I've created a .nuspec file by running the following command on my target project:
nuget spec
2: I've edited the resulting nuspec file so that it looks like this:
<?xml version="1.0"?>
<package >
<metadata>
<id>$id$</id>
<version>$version$</version>
<title>$title$</title>
<authors>$author$</authors>
<owners>$author$</owners>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Security Core Impl</description>
<releaseNotes>Security Core Impl</releaseNotes>
<copyright>Copyright 2013</copyright>
<tags>Security Core</tags>
</metadata>
<files>
<file src="**\*.dll" target="lib\net40\" exclude="**\*.sql;" />
</files>
</package>
3: I run the following command to build my .nupkg:
nuget pack -Prop Configuration=Release
Whenever I install this package into another project, however, the .sql files get included. I've tried a few different variations of the exclude above, and have tried the -Exclude switch. Nothing seems to work.
All .sql files are marked:
Build Action: None
Copy to Output Directory: Do not copy
I have also tried clearing my NuGet cache via Visual Studio options.
Note: The NuGet package is hosted on a private server.
I've just been having the exact same issue, but with .txt files. The solution as pointed out by a colleague is to change the Build Action from "Content" to "None".
I'm assuming your *.sql files are under the content folder of the package?
<file src="**\*.dll" target="lib\net40\" exclude="**\*.sql;" /> tells NuGet to add all .DLL files under lib\net40. SQL files are ignored as your include didn't include them anyway.
Can you try adding <file target="content\" exclude="**\*.sql;" /> to the nuspec?

Missing Second-Level Binary References with MSBuild Web Deployment

I am trying to do a Jenkins-based automated build/deployment of a web application (.NET 4.0). The web application project has several project references, which in turn have binary references third party DLLs.
The problem:
The second-level references (references of project references) are not pulled into the bin folder in the obj\<CONFIGURATION>\Package\PackageTmp\bin folder, used for building deployment packages.
When I build in the visual studio, the second level references are pulled into the regular build output directory.
When building with MSBuild, second level dependencies are not pulled into the regular output directory, nor into the PackageTmp\bin directory.
This is confirmed by MS as a Won't-Fix issue here.
Related questions here, here and here either do not match my problem, or offer solutions that don't work. I've reviewed all answers, not just the accepted ones.
My build command looks like this (using MSBuild 4.0):
MSBuild MySolution.sln /p:Configuration=Integration /p:platform="Any
CPU" /t:Clean,Build /p:DeployOnBuild=true /p:DeployTarget=Package
/p:AutoParameterizationWebConfigConnectionStrings=false
I've tried to manually edit Reference elements in project files, adding <Private>True</Private>, with no success.
I am trying to work around this known issue, so that my second-level dependencies are automatically and correctly pulled into the web publishing temp directory.
My current attempt combines the general approach here (customizing the web publishing pipeline by adding a MyProject.wpp.targets file next to the web project file), combined with some MSBuild code for finding DLLs here. So far this has either produced no results or broken the project file. I am new to custom MSBuild code and find it pretty arcane.
My Question: I am looking for a more complete example that works in my specific case. I think the goal is to intervene in the web publishing pipeline that gathers files for copying to the package temp directory, and adding the second-level dependencies to it.
My custom MyWebProj.wpp.targets looks like this:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<BRPathFiles Include="$(SolutionDir)..\Common\**\*.dll;$(SolutionDir)**\*.dll" />
<ConfigPathFiles Include="$(SolutionDir)..\Common\**\*.config;$(SolutionDir)**\*.config" />
</ItemGroup>
<Target Name="CopySecondLevelDependencies" BeforeTargets="CopyAllFilesToSingleFolderForPackage">
<RemoveDuplicates Inputs="#(BRPathFiles->'%(RootDir)%(Directory)')">
<Output TaskParameter="Filtered" ItemName="BRPaths" />
</RemoveDuplicates>
<RemoveDuplicates Inputs="#(ConfigPathFiles->'%(RootDir)%(Directory)')">
<Output TaskParameter="Filtered" ItemName="ConfigPaths" />
</RemoveDuplicates>
<CreateItem Include="%(BRPaths.Identity);%(ConfigPaths.Identity);">
<Output ItemName="FileList" TaskParameter="Include"/>
</CreateItem>
<CreateItem Value="#(BRSearchPath);$(ConfigSearchPath)">
<Output TaskParameter="Value" PropertyName="SecondLevelFiles" />
</CreateItem>
</Target>
<ItemGroup>
<FilesForPackagingFromProject
Include="%(SecondLevelFiles->'$(OutDir)%(FileName)%(Extension)')">
<DestinationRelativePath>$(_PackageTempDir)\bin\%(FileName)%(Extension) </DestinationRelativePath>
<FromTarget>CopySecondLevelDependencies</FromTarget>
<Category>Run</Category>
</FilesForPackagingFromProject>
</ItemGroup>
</Project>
Assuming you have collected all libraries needed at runtime in a folder outside your solution/project, have you tried just using post-build events to copy all these libraries to your main project target directory (bin) and then include that directory in your deployment package using Sayeds method: http://sedodream.com/2010/05/01/WebDeploymentToolMSDeployBuildPackageIncludingExtraFilesOrExcludingSpecificFiles.aspx (also available in this post: How do you include additional files using VS2010 web deployment packages?)?
I have (among others) the following line in my main project's post-build events:
xcopy "$(ProjectDir)..\..\Libraries\*.dll" "$(TargetDir)" /Y /S
In addition to this, I have added the following lines to my .csproj file:
<PropertyGroup>
<CopyAllFilesToSingleFolderForPackageDependsOn>
PostBuildLibraries;
$(CopyAllFilesToSingleFolderForPackageDependsOn);
</CopyAllFilesToSingleFolderForPackageDependsOn>
</PropertyGroup>
<Target Name="PostBuildLibraries">
<ItemGroup>
<_PostBuildLibraries Include="$(TargetDir)**\*" />
<FilesForPackagingFromProject Include="%(_PostBuildLibraries.Identity)">
<DestinationRelativePath>$(OutDir)%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
</FilesForPackagingFromProject>
</ItemGroup>
</Target>
Be sure to add these lines after the import of the "Microsoft.WebApplication.targets". Check out the links above for more details.
This makes all the desired libraries available after each build (copied to the project's target directory) and each time I create a deployment package (copied to the obj\<CONFIGURATION>\Package\PackageTmp\bin).
Also, since I'm building my main project, not my solution, I'm using the $(ProjectDir) macro instead of the $(SolutionDir).

Resources