Copy files from one project to another after build - msbuild

I have a project for all my javascripts in side (that I made common for three projects).
During a build I want to move these scripts to the three projects, how can I do this by editing .csproj and how?
Update:
I have in the same solution webapplication1 and webapplication2 and i'm editing .csproj for webapplication1, and i want to copy all files in test folder
<Target Name="AfterBuild">
<Copy SourceFiles="$(WDTargetDir)test\"
DestinationFolder="$(SolutionRoot)WebApplication2">
</Copy>
</Target>
However nothing is happening.

There is two ways to do that:
_ Edit the .csproject, at the end of the file you should find a Target named AfterBuild (uncomment the target).
<ItemGroup>
<JavascriptFiles Include="*.js"/>
</ItemGroup>
<Target Name="AfterBuild">
<Copy
SourceFiles="#(JavascriptFiles)"
DestinationFolder="PathWhereYouWantToCopyYourJavascriptFiles"
/>
</Target>
_ Post build events in visual studio (in project properties)

I found my solution:
<Target Name="AfterBuild">
<Copy SourceFiles="test\CodeFile1.cs"
DestinationFolder="..\WebApplication2\scripts">
</Copy>
</Target>

If you can use Visual Studio to build then use post build events in visual studio. Failing that if you have access to the command line just issue a copy command

Related

.NET Core msbuild ProjectReference

I have a solution that contains a console application with a .csproj file like the this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp1.1</TargetFramework>
</PropertyGroup>
</Project>
I also have a library project that uses the console application to generate a heap of C# code that get compiled into the library, the library .csproj file looks like this.
<Project Sdk="Microsoft.NET.Sdk" InitialTargets="RunGenerator">
<PropertyGroup>
<TargetFramework>netstandard1.4</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../generator/generator.csproj">
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference>
</ItemGroup>
<Target Name="RunGenerator">
<Exec Command="dotnet run -p "../generator/generator.csproj" input output" />
</Target>
</Project>
This fails because the dependency analysis says that a netstandard1.4 assembly cannot reference a netcoreapp1.1 assembly. That is correct except that I am not referencing the assembly.
I can work around that issue by building the generator project like this:
<Project Sdk="Microsoft.NET.Sdk" InitialTargets="RunGenerator">
<PropertyGroup>
<TargetFramework>netstandard1.4</TargetFramework>
</PropertyGroup>
<Target Name="RunGenerator">
<Exec Command="dotnet build "../generator/generator.csproj"" />
<Exec Command="dotnet run -p "../generator/generator.csproj" input output" />
</Target>
</Project>
The problem is that the generator project no longer takes part in the dependency analysis when these projects are built using the containing solution file and the explicit build of the generator project sometimes runs concurrently with another build of the same project initiated by the solution build and this results in errors because files are locked etc.
Is it possible to have a project dependency without checking the target framework?
Can anyone suggest a workaround?
Thanks.
Here are some MSBuild tips. You might need to combine a few of these ideas.
You can use your solution file to add an explicit project dependency. See https://learn.microsoft.com/en-us/visualstudio/ide/how-to-create-and-remove-project-dependencies (This question was originally asked here: Visual Studio 2010: How to enforce build order of projects in a solution?). Unfortunately, this is really hard to do if you don't have VS. The format is .sln files is kinda a nightmare.
To avoid the concurrent build issue, use the MSBuild task instead of the Exec task. See https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-task
<Target Name="CompileAnotherProject">
<MSBuild Projects="../generator/generator.csproj" Targets="Build" />
</Target>
dotnet-run invokes "dotnet build" automatically. This is actually problematic in concurrent builds. You can instead add a target to your generator.csproj that runs the app after it has been built. "dotnet filepath.dll" runs the compiled app without building it.
<Target Name="RunCodeGen" AfterTargets="Build">
<Exec Command="dotnet $(AssemblyName).dll input output"
WorkingDirectory="$(OutDir)" />
</Target>

How to order the harvesting of file before compilation?

I use the _PublishedApplications to generate the structure in the TFS Build Server. After this, I use the <HeatDirectory> in the WiX project to correctly harvest the content of _PublishedApplications folder. But my problem is the order during build.
If I use the <HeatDirectory> inside <Target Name="BeforeBuild"> it doesn't include the binaries copied to the _PublishedApplications, as the harvesting is executed before the publish (file copy).
If I change the target to BeforeCompile the compilation doesn't succeed because there is no file in first place. Here is the code for the WiX project (the relevant part of it):
<ItemGroup>
<Compile Include="Product.wxs" />
<Compile Include="Autogenerated.wxs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SimpleProject\SimpleProject.csproj">
<Name>SimpleProject</Name>
<Project>{GUID}</Project>
<Private>True</Private>
<DoNotHarvest>True</DoNotHarvest>
<RefProjectOutputGroups>Binaries</RefProjectOutputGroups>
<RefTargetDir>INSTALLFOLDER</RefTargetDir>
</ProjectReference>
</ItemGroup>
<Import Project="$(WixTargetsPath)" />
<Target Name="BeforeCompile">
<HeatDirectory OutputFile="Autogenerated.wxs" Directory="$(Sources)"
PreprocessorVariable="var.SimpleProject.TargetDir"
AutogenerateGuids="true" SuppressRegistry="true"
ToolPath="$(WixToolPath)" DirectoryRefId="INSTALLFOLDER"
ComponentGroupName="ComponentGroup_Core"
SuppressRootDirectory="true" />
</Target>
Question
How can I execute the harvesting after the binaries are copied to _PublishedApplications?
I build my solution using the following command:
msbuild SimpleInstaller.sln /p:OutDir=C:\Temp\Output\ /v:diag > C:\Temp\Log.txt
This will output all log to a text file. You can use the MSBuild logger instead.
Then I found when you build a C# project, the target Compile in file Microsoft.Common.targets is invoked. This target has an attribute DependsOnTarget which contains a reference to target BeforeCompile.
I can override this target BeforeCompile in my own C# project, just by adding the following code at the end of it (file .csproj):
...
<Target Name="BeforeCompile">
<!-- custom action. -->
</Target>
</Project>
But the problem is my WiX project cannot override the BeforeCompile target because this target isn't defined for WiX projects. You can check this in the wix2010.targets file. The target Compile only has dependence upon targets PrepareForBuild, ResolveWixExtensionReferences and GenerateCompileWithObjectPath.
My solution was to identify an alternative to BeforeCompile which is the Harvest target. My WiX project (.wixproj) has the following target now:
<Target Name="Harvest">
<HeatDirectory OutputFile="Autogenerated.wxs" Directory="$(Sources)"
PreprocessorVariable="var.SimpleProject.TargetDir"
AutogenerateGuids="true" SuppressRegistry="true"
ToolPath="$(WixToolPath)" DirectoryRefId="INSTALLFOLDER"
ComponentGroupName="ComponentGroup_Core"
SuppressRootDirectory="true" />
</Target>
All this problem occurred because my first project in the solution was the WiX project and only then I added the C# projects. For this reason the BeforeBuild was being executed before everything else.
Another solution to solve this issue is to edit the solution file (.sln) and move the WiX project declaration in the beginning of the solution file to the end of all project declarations (not the end of the solution file). Then the BeforeBuild of the WiX project will be executed after the _PublishedApplications folder is created by the C# project.
This manual edit is required because if you change the Project Build Order you are actually changing the project references (at least in the solution file), but the target BeforeBuild is called anyway before the ResolveProjectReferences which is the responsible for invoking the build of any references.
This is the project declaration that should be after all others:
Project("GUID") = "SimpleInstaller", "SimpleInstaller\SimpleInstaller.wixproj", "GUID"
EndProject
My recommendation is still to use Harvest target as it is independent of any changes in the solution file.
The first thing you need to do is add a project reference from your installer project to your application project. This will force the application to build and be available for your installer project.
You can verify the build sequence of the projects if you right click your solution under VisualStudio and click the "Project Build Order...", if you need to change the order you need to configure the Project dependencies.
Then do something like this in your installer project:
<PropertyGroup>
<RootDir>{PATH TO _PublishedApplications FOLDER}</RootDir>
<HarvestDirectoryNoLogo>true</HarvestDirectoryNoLogo>
<HarvestDirectorySuppressAllWarnings>false</HarvestDirectorySuppressAllWarnings>
<HarvestDirectoryTreatWarningsAsErrors>false</HarvestDirectoryTreatWarningsAsErrors>
<HarvestDirectoryTreatSpecificWarningsAsErrors>
</HarvestDirectoryTreatSpecificWarningsAsErrors>
<HarvestDirectoryVerboseOutput>false</HarvestDirectoryVerboseOutput>
<HarvestDirectoryAutogenerateGuids>false</HarvestDirectoryAutogenerateGuids>
<HarvestDirectoryGenerateGuidsNow>true</HarvestDirectoryGenerateGuidsNow>
<HarvestDirectorySuppressFragments>true</HarvestDirectorySuppressFragments>
<HarvestDirectorySuppressUniqueIds>false</HarvestDirectorySuppressUniqueIds>
</PropertyGroup>
<Import Project="$(WixTargetsPath)" />
<Target Name="BeforeBuild">
<ItemGroup>
<HarvestDirectory Include="$(RootDir)">
<Transforms>
</Transforms>
<ComponentGroupName>MyComponent</ComponentGroupName>
<DirectoryRefId>WebSiteRoot</DirectoryRefId>
<PreprocessorVariable>var.RootDir</PreprocessorVariable>
<SuppressCom>false</SuppressCom>
<SuppressRegistry>false</SuppressRegistry>
<SuppressRootDirectory>true</SuppressRootDirectory>
<KeepEmptyDirectories>true</KeepEmptyDirectories>
</HarvestDirectory>
</ItemGroup>
<HeatDirectory
NoLogo="$(HarvestDirectoryNoLogo)"
SuppressAllWarnings="$(HarvestDirectorySuppressAllWarnings)"
SuppressSpecificWarnings="$(HarvestDirectorySuppressSpecificWarnings)"
ToolPath="$(WixToolPath)"
TreatWarningsAsErrors="$(HarvestDirectoryTreatWarningsAsErrors)"
TreatSpecificWarningsAsErrors="$(HarvestDirectoryTreatSpecificWarningsAsErrors)"
VerboseOutput="$(HarvestDirectoryVerboseOutput)"
AutogenerateGuids="$(HarvestDirectoryAutogenerateGuids)"
GenerateGuidsNow="$(HarvestDirectoryGenerateGuidsNow)"
OutputFile="$(IntermediateOutputPath)_%(HarvestDirectory.Filename)_dir.wxs"
SuppressFragments="$(HarvestDirectorySuppressFragments)"
SuppressUniqueIds="$(HarvestDirectorySuppressUniqueIds)"
Transforms="%(HarvestDirectory.Transforms)"
Directory="#(HarvestDirectory)"
ComponentGroupName="%(HarvestDirectory.ComponentGroupName)"
DirectoryRefId="%(HarvestDirectory.DirectoryRefId)"
KeepEmptyDirectories="%(HarvestDirectory.KeepEmptyDirectories)"
PreprocessorVariable="%(HarvestDirectory.PreprocessorVariable)"
SuppressCom="%(HarvestDirectory.SuppressCom)"
SuppressRootDirectory="%(HarvestDirectory.SuppressRootDirectory)"
SuppressRegistry="%(HarvestDirectory.SuppressRegistry)" />
</Target>
</Project>
I generated this code based on the documentation of the HeatDirectory Task and I use it in real projects.

Msbuild - build with output in diffrent folders

I have right now this in a script and want to have it in a msbuild instead.
msbuild /t:Build;PipelinePreDeployCopyAllFilesToOneFolder XXXXX\XXXX.XXX.xxx\XXXXX.XXXX.XXXXX1.csproj /p:Configuration="Release";_PackageTempDir=....\Deploy\XXXX1
msbuild /t:Build;PipelinePreDeployCopyAllFilesToOneFolder XXXXX\XXXX.XXX.xxx\XXXXX.XXXX.XXXXX2.csproj /p:Configuration="Release";_PackageTempDir=....\Deploy\XXXX2
How would this be written in a msbuild script? I only have managed to do it with one build but to create diffrent folders in the Deploy folder I havent been able to do.
Could someone teach me?
You can specify OutDir (new projects) or OutputPath (some old project types) as Properties when you call msbuild task to build your project. Or whatever property you want, like your "_PackageTempDir"
Something like this:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="BatchMyProjects" ToolsVersion="4.0">
<ItemGroup>
<BuildMyProjects Include="XXXX-Project-1" />
<BuildMyProjects Include="XXXX-Project-2" />
</ItemGroup>
<Target Name="BatchMyProjects" >
<ItemGroup>
<ProjectsToBuild Condition="Exists('SomeSourcePath\%(BuildMyProjects.Identity)/%(BuildMyProjects.Identity).csproj')">
<ProjectName>SomeSourcePath/%(BuildMyProjects.Identity)/%(BuildMyProjects.Identity).csproj</ProjectName>
<PublishSubFolder>%(BuildMyProjects.Identity)</PublishSubFolder>
</ProjectsToBuild>
</ItemGroup>
<MSBuild Projects="%(ProjectsToBuild.ProjectName)" Targets="Build;PipelinePreDeployCopyAllFilesToOneFolder"
Properties="Configuration=Release;
OutDir=SomePathToDeploy/Deploy/%(ProjectsToBuild.PublishSubFolder)/;
OutputPath= SomePathToDeploy/Deploy/%(ProjectsToBuild.PublishSubFolder)/;
_PackageTempDir=SomePathToDeploy/Deploy/%(ProjectsToBuild.PublishSubFolder)/
" />
</Target>
</Project>
Also I created gist for this same example
This do work but that seems abit strange. I rateher have it to work as you mentioned
<Target Name="testar" >
<MSBuild Projects="..\xxxxxx\xxxxxx1.csproj" Targets="Build;PipelinePreDeployCopyAllFilesToOneFolder" Properties="Configuration=Release;_PackageTempDir=../../Deploy/xxx1/"/>
<MSBuild Projects="..\xxxx\xxxxxx2.csproj" Targets="Build;PipelinePreDeployCopyAllFilesToOneFolder" Properties="Configuration=Release;_PackageTempDir=../../Deploy/xxx2/"/>
</Target>

MSBuild project file: Copy item to specific location in output directory

In the process of cleaning up the folder/file structure on a project I inherited, I'm running into a problem with organizing the required external libraries. I want to keep them in their own .\dll\ folder, but they're not being copied to the build directory properly. They should be in the root build directory, but they're being moved to a subfolder instead.
My .csproj file contains the following xml:
<Project>
<ItemGroup>
<None Include="dlls\libraryA.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
Then, on build, the libraryA.dll file is copied to the bin\Debug\dll\ folder, but I want it in the bin\Debug\ folder.
I tried this and msbuild always wants to copy the files using their directory path, but there is a workaround...
Edit the csproj file and after this line:
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
Add these lines:
<PropertyGroup>
<PrepareForRunDependsOn>$(PrepareForRunDependsOn);MyCopyFilesToOutputDirectory</PrepareForRunDependsOn>
</PropertyGroup>
<Target Name="MyCopyFilesToOutputDirectory">
<Copy SourceFiles="#(None)" DestinationFolder="$(OutDir)" />
</Target>
The copy of the output files happens in the PrepareForRun target. This adds your own target to the list of targets that are executed as part of PrepareForRun.
This example copies all items in the None item group. You could create your own item group (e.g. MyFiles) and do the copy on that item group if you have other "None" files you don't want copied. When I tried this I had to change the item group name by editing the csproj file directly. Visual Studio did not allow me to set the item group of a file from the UI, but after I edited the csproj and changed it, Visual Studio displayed my custom item group name correctly.
If you only want to change it for one file, it may be easier to use the property:
<None Include="dlls\libraryA.dll">
<Link>%(Filename)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
Including content files in .csproj that are outside the project cone
This approach works
If you need to force copy of a specific file/nuget package into an asp.net core project (2.2), add at the end of your csproj :
<!-- Force copy MathNet because we need it in compilation -->
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="Build">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\MathNet.Numerics.4.8.1\lib\netstandard2.0\MathNet.Numerics.dll')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MathNet.Numerics.4.8.1\lib\netstandard2.0\MathNet.Numerics.dll'))" />
</Target>
<ItemGroup>
<ContentWithTargetPath Include="..\packages\MathNet.Numerics.4.8.1\lib\netstandard2.0\MathNet.Numerics.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<TargetPath>MathNet.Numerics.dll</TargetPath>
</ContentWithTargetPath>
</ItemGroup>
In SDK-style csproj you can write something like:
<Target Name="CopyFilesTargetName" AfterTargets="Build">
<Copy SourceFiles="$(OutDir)\dlls\Some.dll;$(OutDir)\dlls\SomeOther.dll" DestinationFolder="$(OutDir)" />
</Target>
You can also use <Move instead of <Copy to move files

How can I change AssemblyProduct, AssemblyTitle using MSBuild?

I have an MSBuild script which compiles my existing solution but I'd like to change some properties of one of the projects within the solution at compile-time, including but not limited to AssemblyProduct and AssemblyTitle.
Here's a snippet of my build script:
<Target Name="Compile" >
<MSBuild Projects="..\MySolution.sln"
Properties="Configuration=MyReleaseConfig;Platform=x86" />
</Target>
I've got one main executable and several DLLs that are compiled. I am aware of the MSBuild Extension Pack and I suspect it might help me to get to where I need to be, although I'm not sure how to proceed.
Can I selectively change AssemblyInfo properties at build time?
You're on the right track with the MSBuild Extension Pack.
I find the easiest way to conditionally generate the assembly details at build time is to add an "AssemblyVersion" target directly to my .csproj file(s) that require an updated AssemblyInfo file. You can add the target directly to each csproj file that requires an updated AssemblyInfo file, or as I prefer to do it, create a custom targets file with the AssemblyVersion target and have each csproj file include your custom targets file.
Either way you likely want to use the MSBuild Extension Pack or the MSBuild Community Tasks to use their respective AssemblyInfo task.
Here's some code from our build scripts:
<!-- Import the AssemblyInfo task -->
<Import Project="$(MSBuildCommunityTasksPath)\MSBuild.Community.Tasks.Targets"/>
<!-- Overriding the Microsoft.CSharp.targets target dependency chain -->
<!-- Call our custom AssemblyVersion target before build, even from VS -->
<PropertyGroup>
<BuildDependsOn>
AssemblyVersion;
$(BuildDependsOn)
</BuildDependsOn>
</PropertyGroup>
<ItemGroup>
<AssemblyVersionFiles Include="$(MSBuildProjectDirectory)\Properties\AssemblyInfo.cs"/>
</ItemGroup>
<Target Name="AssemblyVersion"
Inputs="#(AssemblyVersionFiles)"
Outputs="UpdatedAssemblyVersionFiles">
<Attrib Files="%(AssemblyVersionFiles.FullPath)"
Normal="true"/>
<AssemblyInfo
CodeLanguage="CS"
OutputFile="%(AssemblyVersionFiles.FullPath)"
AssemblyCompany="$(CompanyName)"
AssemblyCopyright="Copyright $(CompanyName), All rights reserved."
AssemblyVersion="$(Version)"
AssemblyFileVersion="$(Version)">
<Output TaskParameter="OutputFile"
ItemName="UpdatedAssemblyVersionFiles"/>
</AssemblyInfo>
</Target>
Sneal's answer was very helpful, but I'd like to show what I actually ended up doing. Instead of editing csproj files (there are several) I instead added tasks to my build script. Here's a snippet:
<PropertyGroup>
<ProductName>MyApp</ProductName>
<CompanyName>MyCompany</CompanyName>
<Major>1</Major>
<Minor>0</Minor>
<Build>0</Build>
<Revision>0</Revision>
</PropertyGroup>
<ItemGroup>
<AssemblyVersionFiles Include="..\MyMainProject\Properties\AssemblyInfo.cs"/>
</ItemGroup>
<Target Name="AssemblyVersionMAIN" Inputs="#(AssemblyVersionFiles)" Outputs="UpdatedAssemblyVersionFiles">
<Attrib Files="%(AssemblyVersionFiles.FullPath)" Normal="true"/>
<AssemblyInfo
CodeLanguage="CS"
OutputFile="%(AssemblyVersionFiles.FullPath)"
AssemblyProduct="$(ProductName)"
AssemblyTitle="$(ProductName)"
AssemblyCompany="$(CompanyName)"
AssemblyCopyright="© $(CompanyName) 2010"
AssemblyVersion="$(Major).$(Minor).$(Build).$(Revision)"
AssemblyFileVersion="$(Major).$(Minor).$(Build).$(Revision)"
AssemblyInformationalVersion="$(Major).$(Minor).$(Build).$(Revision)">
<Output TaskParameter="OutputFile" ItemName="UpdatedAssemblyVersionFiles"/>
</AssemblyInfo>
</Target>
<Target Name="Compile" DependsOnTargets="AssemblyVersionMAIN">
<MSBuild Projects="..\MySolution.sln"
Properties="Configuration=Release;Platform=x86;Optimize=true" />
</Target>
Then, I can override my variables from the command line, or a batch script, like so:
set MAJ=1
set MIN=2
set BLD=3
set REV=4
msbuild buildScript.xml /t:Compile /p:Major=%MAJ% /p:Minor=%MIN% /p:Build=%BLD% /p:Revision=%REV%
<Target Name="SetVersion">
<ItemGroup>
<AssemblyInfoFiles Include="$(TargetDir)\**\AssemblyInfo.cs"/>
</ItemGroup>
<Message Text="change the Version number for:"/>
<Message Text="%(AssemblyInfoFiles.FullPath)"/>
<MSbuild.ExtensionPack.Framework.AssemblyInfo
AssemblyInfoFiles="#(AssemblyInfoFiles)"
AssemblyTitle="newTitle"
AssemblyMajorVersion="2"
AssemblyMinorVersion="0"/>
</Target>