How can I preserve certain files when compiling as a single .exe? - c#

I am trying to compile my .NET Core application where all the assemblies are as one .exe and certain .csv and .txt files remain in the output.
I am using this command to compile as one .exe: dotnet publish -r win-x64 -c Release /p:PublishSingleFile=true
However, when I use this, my csv and txt files are not included in the output and my application does not work. I have tried setting their 'Copy to Output Directory' value as true, but this does not fix it.
How can I do this?

Answer found:
You must add the <ExcludeFromSingleFile>true</ExcludeFromSingleFile> tag to your project.csproj file items.
eg.
<None Update="additionaldata.csv">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</None>

Related

MSBuild copy dlls to bin folder, not following the path

I am using csproj file to bundle Chrome windows edition into our ASP.NET Core app. I place all the needed files into $(ProjectDir)\chrome-win and use below XML to copy the files in csproj
<None Update="chrome-win\**" CopyToPublishDirectory="Always" CopyToOutputDirectory="PreserveNewest" LinkBase="chrome-win\" />
What is strange is that when I publish the project using the built in folder publish profile, all the *.dll files gets copied to bin\chrome-win\ and other files are in chrome-win\. This is so frustrating, how can I tell stupid MSBuild / Visual Studio to not to do this? When I build it, the behavior is even stranger, the *.dll files gets copied twice, once to chrome-win folder, also gets copied to bin folder.
I am using the latest VS 2019 and MSBuild
I believe you would want to use the $(BaseOutputPath) property for your copy so that everything writes into the same folder. So it would look like this.
<None Update="chrome-win\**" CopyToPublishDirectory="Always" CopyToOutputDirectory="PreserveNewest" LinkBase="$(BaseOutputPath)\chrome-win\" />
EDIT1:
If you want all the contents of the bin folder moved to \bin\chrome-win\ you could use the CopyTask
<Copy SourceFiles="$(BaseOutputPath)\*" DestinationFiles="$(BaseOutputPath)\" AfterTargets="AfterBuild" />
You might need to tweak this a bit but it should do what you need.

How to have a Project Reference without referencing the actual binary

Following works fine but it does not copy the .pdb file for Asp.net Core 2.0 project.
<ProjectReference Include="..\ProjectA\ProjectA.csproj">
<Project>{b402782f-de0a-41fa-b364-60612a786fb2}</Project>
<Name>ProjectA</Name>
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
<OutputItemType>Content</OutputItemType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Targets>Build;DebugSymbolsProjectOutputGroup</Targets>
</ProjectReference>
https://blogs.msdn.microsoft.com/kirillosenkov/2015/04/04/how-to-have-a-project-reference-without-referencing-the-actual-binary
So, what should we do for copying the pdb file?
So, what should we do for copying the pdb file?
Just as Marc and that bolg pointed out this method not work in the Visual Studio to copy the .pdb file. Because we set <ReferenceOutputAssembly>false</ReferenceOutputAssembly>, the project reference without referencing the actual binary, VS/MSBuild will not copy the .pdb file.
many people have asked how to also copy the .pdb file in addition to
the .exe/.dll. Turns out there is a trick, but it doesn’t work when
building in VS. But it’s OK since you don’t really need the .pdb in VS
anyway, since the debugger will find the .pdb at its original path
anyway.
If you still want to copy this .pdb file, you can use MSBuild copy task or a build event for your project:
MSBuild copy task:
To accomplish this, unload your project. Then at the very end of the project, just before the end-tag , place below scripts:
<Target Name="CopyPDBfile" AfterTargets="Build">
<Copy SourceFiles="$(SolutionDir)ProjectA\bin\Debug\ProjectA.pdb" DestinationFolder="$(TargetDir)" />
</Target>
Build event:
Add following build command line in the Pre-build/Post-build event:
xcopy /y "$(SolutionDir)ProjectA\bin\Debug\ProjectA.pdb" "$(TargetDir)"
Note: Do not ignore double quotes and spaces in the build command line.
Hope this helps.

C# Class library Copy content not copied to output [duplicate]

I generate a nuget package from a project with this command in the post-build event. the variable %conf% is set to the right configuration (debug or release) and %1 is the project name (e.g. "MyCompany.MyProject").
nuget pack -Prop Configuration=%conf% "%1.csproj" -exclude *.sql -IncludeReferencedProjects
This package is for our own usage only, it will never be published on nuget. It ends in our private repository.
In the project, there is a file that is set to generate action : content and copy local : always. (My Visual Studio is in French, so I'm not 100% sure of the translation). Let's name it importantfile.xml.
In the generated package, I end up with this structure :
- content
- importantfile.xml
- lib
-net45 (.NetFramework,Version=v4.5)
-MyCompany.MyProject.dll
Which is fine, I want importantfile.xml to be deployed in the package, because, well, this file is important!
When I install the package in another project, importantfile.xml is deployed at the root of the project. That's OK. But it is not set to copy local : always.
I need importantfile.xml to be copy local : always in this project where I install my package.
How can I achieve that?
Notes :
I can set copy local : always on the file just after installing the package, that's no big deal. I would live with it if later updates of the package would let this property as-is, which is not the case. When updating the package, copy local is reset to never (as stated here).
There's a nuspec file in the project's folder, here it is :
<?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>$description$</description>
<copyright>Copyright 2014</copyright>
<tags>some random tags</tags>
</metadata>
</package>
Instead of using a PowerShell script another approach is to use an MSBuild targets or props file with the same name as the package id:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<None Include="$(MSBuildThisFileDirectory)importantfile.xml">
<Link>importantfile.xml</Link>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
In the nuspec file then, instead of adding the required files to the Content directory, add them to the Build directory along with the targets file.
Build
importantfile.xml
MyPackage.targets
lib
net45
MyAssembly.dll
If you require different content for different architectures then you can add architecture folders under Build also each with their own targets file.
Benefits to using a targets file over the PowerShell script with NuGet Content directory:
required content files aren't shown in the project in Visual Studio
content files are linked to rather than copied into the directory of each project which references the NuGet package (preventing there being multiple copies and keeping behaviour the same as for assemblies / libraries from NuGet packages)
PowerShell scripts only work in Visual Studio and aren't run when NuGet is run from the commandline (build servers, other IDEs and other OS), this approach will work everywhere
PowerShell install scripts are not supported in NuGet 3.x project.json system.
I know you guys got a working solution to this but it didn't work for me so I'm going to share what I pulled out of the NLog.config NuGet package install.ps1 (github source here).
NOTE: this is not my code, this is the content of the install.ps1 from
the NLog.config nuget package just sharing the knowledge.
It seems a little more straight forward to me and just hoping to help others that will likely stumble upon this.
You can find the accepted int values for BuildAction here and the accepted values for CopyToOutputDirectory here.
if the link breaks again
Fields
prjBuildActionCompile 1
The file is compiled.
prjBuildActionContent 2
The file is included in the Content project output group (see Deploying Applications, Services, and Components)
prjBuildActionEmbeddedResource 3
The file is included in the main generated assembly or in a satellite assembly as a resource.
prjBuildActionNone 0
No action is taken.
param($installPath, $toolsPath, $package, $project)
$configItem = $project.ProjectItems.Item("NLog.config")
# set 'Copy To Output Directory' to 'Copy if newer'
$copyToOutput = $configItem.Properties.Item("CopyToOutputDirectory")
# Copy Always Always copyToOutput.Value = 1
# Copy if Newer copyToOutput.Value = 2
$copyToOutput.Value = 2
# set 'Build Action' to 'Content'
$buildAction = $configItem.Properties.Item("BuildAction")
$buildAction.Value = 2
I have made this which copies files from my build folder to the output folder (bin/debug or bin/release). Works like a charm for me.
Nuspec file:
<package>
<files>
<file src="\bin\Release\*.dll" target="lib" />
<file src="\bin\Release\x64\*.dll" target="build\x64" />
<file src="\bin\Release\x86\*.dll" target="build\x86" />
<file src="MyProject.targets" target="build\" />
</files>
</package>
MyProject.targets
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<NativeLibs Include="$(MSBuildThisFileDirectory)**\*.dll" />
<None Include="#(NativeLibs)">
<Link>%(RecursiveDir)%(FileName)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
You can use PowerShell and the Install.ps1 hook provided by NuGet.
See the documentation.
Via PowerShell you have to 'search' for the content element which includes your importantfile.xml in an attribute. When the script found it, it has to add <CopyToOutputDirectory>Always</CopyToOutputDirectory> as a child element.
<Content Include="importantfile.xml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
You can find some PowerShell snippets here. Just take a look at the .ps1 files.
You could try the following (not tested). The file has to be named Install.ps1 and copied into the tools folder:
param($installPath, $toolsPath, $package, $project)
# Load project XML.
$doc = New-Object System.Xml.XmlDocument
$doc.Load($project.FullName)
$namespace = 'http://schemas.microsoft.com/developer/msbuild/2003'
# Find the node containing the file. The tag "Content" may be replace by "None" depending of the case, check your .csproj file.
$xmlNode = Select-Xml "//msb:Project/msb:ItemGroup/msb:Content[#Include='importantfile.xml']" $doc -Namespace #{msb = $namespace}
#check if the node exists.
if($xmlNode -ne $null)
{
$nodeName = "CopyToOutputDirectory"
#Check if the property already exists, just in case.
$property = $xmlNode.Node.SelectSingleNode($nodeName)
if($property -eq $null)
{
$property = $doc.CreateElement($nodeName, $namespace)
$property.AppendChild($doc.CreateTextNode("Always"))
$xmlNode.Node.AppendChild($property)
# Save changes.
$doc.Save($project.FullName)
}
}
You should also check if everything is removed completely when uninstalling the package.
Note by Jonhhy5
When updating the package via update-package, Visual Studio warns that the project is modified "outside the environnment". That's caused by $doc.Save($project.FullName). If I click reload before the command is fully terminated, it sometimes causes errors. The trick is to leave the dialog there until the process finishes, and then reload the projects.
I have written a little tool called NuGetLib to automatically add files to the nuget package after build.
create a tools folder with your Install.ps1 script
build your nugetPackage
add the tools folder to the built nugetPackage
https://stackoverflow.com/a/47134733/6229375

How to have an optional file that gets copied to the output directory in VS 2017?

On a large (team and code) project, we have set up the various App.configs and Web.configs to reference an (optional) local.config file so that developers can override some things when running locally. local.config is listed in .getignore so it's ignored and not included in commits. This works very well to support local configuration overrides.
However, for console apps, local.config isn't copied to the output directory by default. I can set its properties in VS so that it's copied to the output - it gets listed in the .csproj file like this:
<None Include="local.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
This works fine in VS, even when a particular developer does not have a local.config file at all. However, it fails in the VSTS build jobs because apparently msbuild doesn't like it when a listed file is absent.
Is there any way to configure the project or msbuild so that it will tolerate/ignore the missing (optional) file instead of failing the builds?
This can be done by using an msbuild condition:
<None Include="local.config" Condition="Exists('local.config')">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>

How to prevent nuget update from changing content files properties in C# project? [duplicate]

I generate a nuget package from a project with this command in the post-build event. the variable %conf% is set to the right configuration (debug or release) and %1 is the project name (e.g. "MyCompany.MyProject").
nuget pack -Prop Configuration=%conf% "%1.csproj" -exclude *.sql -IncludeReferencedProjects
This package is for our own usage only, it will never be published on nuget. It ends in our private repository.
In the project, there is a file that is set to generate action : content and copy local : always. (My Visual Studio is in French, so I'm not 100% sure of the translation). Let's name it importantfile.xml.
In the generated package, I end up with this structure :
- content
- importantfile.xml
- lib
-net45 (.NetFramework,Version=v4.5)
-MyCompany.MyProject.dll
Which is fine, I want importantfile.xml to be deployed in the package, because, well, this file is important!
When I install the package in another project, importantfile.xml is deployed at the root of the project. That's OK. But it is not set to copy local : always.
I need importantfile.xml to be copy local : always in this project where I install my package.
How can I achieve that?
Notes :
I can set copy local : always on the file just after installing the package, that's no big deal. I would live with it if later updates of the package would let this property as-is, which is not the case. When updating the package, copy local is reset to never (as stated here).
There's a nuspec file in the project's folder, here it is :
<?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>$description$</description>
<copyright>Copyright 2014</copyright>
<tags>some random tags</tags>
</metadata>
</package>
Instead of using a PowerShell script another approach is to use an MSBuild targets or props file with the same name as the package id:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<None Include="$(MSBuildThisFileDirectory)importantfile.xml">
<Link>importantfile.xml</Link>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
In the nuspec file then, instead of adding the required files to the Content directory, add them to the Build directory along with the targets file.
Build
importantfile.xml
MyPackage.targets
lib
net45
MyAssembly.dll
If you require different content for different architectures then you can add architecture folders under Build also each with their own targets file.
Benefits to using a targets file over the PowerShell script with NuGet Content directory:
required content files aren't shown in the project in Visual Studio
content files are linked to rather than copied into the directory of each project which references the NuGet package (preventing there being multiple copies and keeping behaviour the same as for assemblies / libraries from NuGet packages)
PowerShell scripts only work in Visual Studio and aren't run when NuGet is run from the commandline (build servers, other IDEs and other OS), this approach will work everywhere
PowerShell install scripts are not supported in NuGet 3.x project.json system.
I know you guys got a working solution to this but it didn't work for me so I'm going to share what I pulled out of the NLog.config NuGet package install.ps1 (github source here).
NOTE: this is not my code, this is the content of the install.ps1 from
the NLog.config nuget package just sharing the knowledge.
It seems a little more straight forward to me and just hoping to help others that will likely stumble upon this.
You can find the accepted int values for BuildAction here and the accepted values for CopyToOutputDirectory here.
if the link breaks again
Fields
prjBuildActionCompile 1
The file is compiled.
prjBuildActionContent 2
The file is included in the Content project output group (see Deploying Applications, Services, and Components)
prjBuildActionEmbeddedResource 3
The file is included in the main generated assembly or in a satellite assembly as a resource.
prjBuildActionNone 0
No action is taken.
param($installPath, $toolsPath, $package, $project)
$configItem = $project.ProjectItems.Item("NLog.config")
# set 'Copy To Output Directory' to 'Copy if newer'
$copyToOutput = $configItem.Properties.Item("CopyToOutputDirectory")
# Copy Always Always copyToOutput.Value = 1
# Copy if Newer copyToOutput.Value = 2
$copyToOutput.Value = 2
# set 'Build Action' to 'Content'
$buildAction = $configItem.Properties.Item("BuildAction")
$buildAction.Value = 2
I have made this which copies files from my build folder to the output folder (bin/debug or bin/release). Works like a charm for me.
Nuspec file:
<package>
<files>
<file src="\bin\Release\*.dll" target="lib" />
<file src="\bin\Release\x64\*.dll" target="build\x64" />
<file src="\bin\Release\x86\*.dll" target="build\x86" />
<file src="MyProject.targets" target="build\" />
</files>
</package>
MyProject.targets
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<NativeLibs Include="$(MSBuildThisFileDirectory)**\*.dll" />
<None Include="#(NativeLibs)">
<Link>%(RecursiveDir)%(FileName)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
You can use PowerShell and the Install.ps1 hook provided by NuGet.
See the documentation.
Via PowerShell you have to 'search' for the content element which includes your importantfile.xml in an attribute. When the script found it, it has to add <CopyToOutputDirectory>Always</CopyToOutputDirectory> as a child element.
<Content Include="importantfile.xml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
You can find some PowerShell snippets here. Just take a look at the .ps1 files.
You could try the following (not tested). The file has to be named Install.ps1 and copied into the tools folder:
param($installPath, $toolsPath, $package, $project)
# Load project XML.
$doc = New-Object System.Xml.XmlDocument
$doc.Load($project.FullName)
$namespace = 'http://schemas.microsoft.com/developer/msbuild/2003'
# Find the node containing the file. The tag "Content" may be replace by "None" depending of the case, check your .csproj file.
$xmlNode = Select-Xml "//msb:Project/msb:ItemGroup/msb:Content[#Include='importantfile.xml']" $doc -Namespace #{msb = $namespace}
#check if the node exists.
if($xmlNode -ne $null)
{
$nodeName = "CopyToOutputDirectory"
#Check if the property already exists, just in case.
$property = $xmlNode.Node.SelectSingleNode($nodeName)
if($property -eq $null)
{
$property = $doc.CreateElement($nodeName, $namespace)
$property.AppendChild($doc.CreateTextNode("Always"))
$xmlNode.Node.AppendChild($property)
# Save changes.
$doc.Save($project.FullName)
}
}
You should also check if everything is removed completely when uninstalling the package.
Note by Jonhhy5
When updating the package via update-package, Visual Studio warns that the project is modified "outside the environnment". That's caused by $doc.Save($project.FullName). If I click reload before the command is fully terminated, it sometimes causes errors. The trick is to leave the dialog there until the process finishes, and then reload the projects.
I have written a little tool called NuGetLib to automatically add files to the nuget package after build.
create a tools folder with your Install.ps1 script
build your nugetPackage
add the tools folder to the built nugetPackage
https://stackoverflow.com/a/47134733/6229375

Categories