MSbuild Update all assembly file in a solution with svn build revision - visual-studio

Is there a way using msbuild community task, to update all the assemblyInfo.cs in a solution with the SVN build number. There is a a lot of solution out there using FileUpdate but the source is one file and not all the files.
<FileUpdate Files="version.txt"
Regex="(\d+)\.(\d+)\.(\d+)\.(\d+)"
ReplacementText="$1.$2.$3.123" />
I want dynamically to go through all the projects and change it version without knowing the projects file name in the solution

It could look for all assemblyinfo.cs files by using wildcards. For example:
<ItemGroup>
<AssemblyInfoFiles Include="..\**\AssemblyInfo.cs"/>
</ItemGroup>
<Target Name="AfterBuild">
<FileUpdate Files="#(AssemblyInfoFiles)"
Regex="(\d+)\.(\d+)\.(\d+)\.(\d+)"
ReplacementText="$1.$2.$3.123" />
</Target>
Then as stijn said that, you can add a project which all others depend on if you want update all assemblyinfo.cs files before build.
If you want to update all asseblyinfo.cs files after build, you need to add other projects' reference to this project.

Related

Include File in NuGet package on Build, only if it does not exist

A while ago I asked this question:
Including a Folder in NuGet Package and have it install into project as file .netcore/Razor
The answer to which has got me most of the way where I want to go, but the trouble is rebuilding or updating the NuGet package in my projects is now overwriting files that are user-editable.
I feel the answer is somewhere in the .props file but I am not sure how to achieve it:
<Project>
<Target Name="CopyFiles" BeforeTargets="Build" >
<ItemGroup>
<File Include="$(MSBuildThisFileDirectory)..\Pagesettings\*.*"></File>
</ItemGroup>
<Copy SourceFiles="#(File)" DestinationFolder="$(ProjectDir)Pagesettings"></Copy>
</Target>
</Project>
The Target tag has an optional attribute Condition. I am guessing that somehow this can be used...?
So, to clarify my question:
From my original question linked above I have a NuGet package which when downloaded creates a folder called PageSettings, into which is copied a bunch of default JSON files containing PageSettings from my NuGet package. NOTE; these files initially appear in Visual Studio as linked files. They are not actually copied to the directory until the project is built.
However, the purpose of these PageSettings files is to be a default , or if you like an example, of PageSettings for users of my NuGet package. Once installed, the user can and should change these .JSON files, so now when their project is rebuilt, or if there is an update to the NuGet package available which they install, these JSON files should NOT update/install.
Sure. My answer quite has some flaws. To prevent overwriting existing files and files that have changed, I suggest you use xcopy with /d. It will not overwrite the existing files.
Instead, use this in test.props file:
<Project>
<Target Name="CopyFiles" BeforeTargets="Build">
<ItemGroup>
<File Include="$(MSBuildThisFileDirectory)..\Pagesettings\*.*"></File>
</ItemGroup>
<Exec Command="xcopy /s/d/y #(File) $(ProjectDir)Pagesettings\"></Exec>
<!--<Copy SourceFiles="#(File)" DestinationFolder="$(PorojectDir)Pagesettings" Condition="!Exists('$(PorojectDir)Pagesettings\*.*')"></Copy>-->
</Target>
</Project>
Then, repack your nuget project. Before using this new one, please clean nuget caches first.

Make visual studio build when output won't change?

In my project I have a json file I use for configuration that I have git set to ignore. When the repository is first cloned, the configuration file that is part of the project and that is copied to the output directory doesn't exist. I've gotten this to work using tasks in the 'BeforeBuild' target in the project that will copy the sample file to the actual config file if it doesn't exist.
<Target Name="BeforeBuild">
<ItemGroup>
<MySourceFiles Include="Configuration.sample.json" />
</ItemGroup>
<ItemGroup>
<MyDestinationFiles Include="Configuration.json" />
</ItemGroup>
<Message Importance="high" Condition="!Exists('#(MyDestinationFiles)')"
Text="Copying #(MySourceFiles) to #(MyDestinationFiles)" />
<Copy Condition="!Exists('#(MyDestinationFiles)')"
SourceFiles="#(MySourceFiles)"
DestinationFiles="#(MyDestinationFiles)" />
</Target>
So if I build the project, then delete the configuration file and do a build, nothing happens because no changes have been made that would change the outputs I think. Is there a way to change the project file so that a build will be flagged as necessary? It shouldn't come up very often and I can always do a 'Clean' or 'Rebuild' manually, but it's nagging at me since I'm just starting to learn MSBuild files.
From the documentation on a Target's Outputs attribute:
The files that form outputs into this target. Multiple files are
separated by semicolons. The timestamps of the files will be compared
with the timestamps of files in Inputs to determine whether the Target
is up to date
So if you add the paths to the outputfiles created by your Beforebuild target to it's Outputs attribute, at the start of every build msbuild will check if those files exist and if not it will start a build because now the project is considered to not be up-to-date anymore. In practice use:
<Target Name="BeforeBuild" Outputs="#(MyDestinationFiles)">

How can I exclude a project from a build in MSBuild?

I need to build a solution, but exclude one project. How should I do it?
I searched a lot about this issue, but nothing could help.
An ItemGroup section rises the following exception:
Invalid element . Unknown task or datatype.
PropertyGroup also rises the exception.
Below is my code sample:
<project name="TI 8.1.6 build script">
<ItemGroup>
<Solution Include="${ROOT}\Core\TI Core.sln" Exclude="${ROOT}\Utilities\DTS Indexing Service\Tdi.Origami.IndexUpdaterServiceSetup\Tdi.Origami.IndexUpdaterServiceSetup.wixproj"/>
</ItemGroup>
...
</project>
How can I do this?
You can exclude projects at the solution level for a specific build configuration by using the Configuration Manager Dialog in Visual Studio:
Then you can simply invoke msbuild on the solution file specifying the build configuration to use:
msbuild /property:Configuration=Release MySolution.sln
The solution suggested by Enrico is the most versatile solution that would work always. An alternative solution might be to use a <MSBuild> task directly. This will work for you if you have all your project files under a particular directory, or be able to easily enumerate all projects you want to build (i.e. number of projects in your solution is not very big).
For example, this MSBuild file will build every project under your current directory except for a specific project:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<MyProjectReferences Include="**\*.*proj" />
<MyProjectReferences Exclude="Utilities\DTS Indexing Service\Tdi.Origami.IndexUpdaterServiceSetup\Tdi.Origami.IndexUpdaterServiceSetup.wixproj" />
</ItemGroup>
<Target Name="BuildAllExceptWixProject">
<MSBuild Projects="#(MyProjectReferences)" Targets="Build" />
</Target>
</Project>
Then you can build that using command line msbuild <myproject> /t:BuildAllExceptWixProject
In your solution file (.sln), remove the Build.0 entries. For example:
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MyProject", "MyProject.vcxproj", "{2281D9E7-5261-433D-BB04-176A61500CA3}"
EndProject
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{2281D9E7-5261-433D-BB04-176A61500CA3}.Debug|x86.Build.0 = Debug|x64
If you delete this "Build.0" entry, it will load in the solution fine, but will not be built, either through the GUI or via external MSBuild.
Since VS 2019 and MSBuild 16.7, the right way is to use Solution filters. Ref
create a master.proj file:
in another ItemGroup add DefaultExclude properties for programs - put it in front of the solution
-- BA was Canadian
Configuration=Release
Release
drop the master.proj into the directory with the programs and msbuild the master.proj
compiles everything except... that HelloWorld

MSBuild: changing project references to file binary references

I have a few .net / C# projects that I'm building with MSBuild.
In the project I'm building, there are some project references to other C# projects.
I'm only building individual projects, so I'd like to repoint the project references to actual binaries in a folder during the build.
I'm already setting "BuildProjectReferences" to False for my MSBuild step.
So essentially I want to have MSBuild ignore the project refs and look for those binaries in a new directory.
Is this possible to do? Is this possible without having to dynamically modify the project file prior to build?
** Update 1 **
More info might help...I'm actually building each binary via Ant/AntDotNet/MSBuild and uploading to Artifactory. I'm basically using Ivy's dependency management with .Net binaries.
Right now I have the uploads and downloads of dependencies working fine.
The only part I'm missing is getting MSBuild to look for the binaries as file dependencies instead of the project references that it has in the project file.
** Update 2 **
It looks like MSBuild supports editing the csproj file using XMLPoke and XMLPeek.
So in my case I'd need to change the following in my project file:
<ProjectReference Include="..\MyReferencedProject\MyReferencedProject.csproj">
<Project>{MyReferencedProjectGUID}</Project>
<Name>MyReferencedProject</Name>
<Private>False</Private>
</ProjectReference>
to this
<Reference Include="MyReferencedProject">
</Reference>
Can anyone give me any pointers on that?
Answer on Update2:
Your question is related/duplicate to this question. There is no easy way to convert it. You should write MSBuild custom task for it to update MSBuild project files before build.
I ended up using Ant combined with the xmltask addon.
<target name="convert" description="convert project references to file references">
<echo>Converting the following Project references :: ${Project.ItemGroup.ProjectReference.Name}</echo>
<xmltask source="MyParentProject.csproj" dest="MyParentProject.csproj">
<rename path="/:Project/:ItemGroup/:ProjectReference/:Name" to="BinaryName"/>
</xmltask>
<replace file="MyParentProject.csproj" token="<BinaryName>" value="<Reference Include=""/>
<replace file="MyParentProject.csproj" token="</BinaryName>" value=""></Reference>"/>
<xmltask source="MyParentProject.csproj" dest="MyParentProject.csproj">
<copy path="/:Project/:ItemGroup/:ProjectReference/:Reference" buffer="refbuffer" append="true"/>
<paste path="/:Project/:ItemGroup[1]" buffer="refbuffer"/>
<remove path="/:Project/:ItemGroup/:ProjectReference" />
</xmltask>
<echo>Conversion complete.</echo>
</target>
In the first xmltask we are setting the "Name" element to "BinaryName" so we can find it.
Next we have two Ant Replace tasks that change something like
"<BinaryName>MyReferencedProject</BinaryName>"
to
"<Reference Include="MyReferencedProject"></Reference>"
And in the last xmltask moves our project references we converted into file references into the upper ItemGroup that normally has the file references.
Then finally we delete the ProjectReferences section.

How to Integrate ILMerge into Visual Studio Build Process to Merge Assemblies?

I want to merge one .NET DLL assembly and one C# Class Library project referenced by a VB.NET Console Application project into one command-line console executable.
I can do this with ILMerge from the command-line, but I want to integrate this merging of reference assemblies and projects into the Visual Studio project. From my reading, I understand that I can do this through a MSBuild Task or a Target and just add it to a C#/VB.NET Project file, but I can find no specific example since MSBuild is large topic. Moreover, I find some references that add the ILMerge command to the Post-build event.
How do I integrate ILMerge into a Visual Studio (C#/VB.NET) project, which are just MSBuild projects, to merge all referenced assemblies (copy-local=true) into one assembly?
How does this tie into a possible ILMerge.Targets file?
Is it better to use the Post-build event?
The "MSBuild ILMerge task" (or MSBuild.ILMerge.Task) NuGet package makes this process quite simple. It defaults to merging any "copy local" references into your main assembly.
Note: Although the packages have similar names, this one is different from ILMerge.MSBuild.Tasks that Davide Icardi mentioned in his answer. The one I'm suggesting here was first published in August 2014.
Here an alternative solution:
1) Install ILMerge.MSBuild.Tasks package from nuget
PM> Install-Package ILMerge.MSBuild.Tasks
2) Edit the *.csproj file of the project that you want to merge by adding the code below:
<!-- Code to merge the assemblies into one:setup.exe -->
<UsingTask TaskName="ILMerge.MSBuild.Tasks.ILMerge" AssemblyFile="$(SolutionDir)\packages\ILMerge.MSBuild.Tasks.1.0.0.3\tools\ILMerge.MSBuild.Tasks.dll" />
<Target Name="AfterBuild">
<ItemGroup>
<MergeAsm Include="$(OutputPath)$(TargetFileName)" />
<MergeAsm Include="$(OutputPath)LIB1_To_MERGE.dll" />
<MergeAsm Include="$(OutputPath)LIB2_To_MERGE.dll" />
</ItemGroup>
<PropertyGroup>
<MergedAssembly>$(ProjectDir)$(OutDir)MERGED_ASSEMBLY_NAME.exe</MergedAssembly>
</PropertyGroup>
<Message Text="ILMerge #(MergeAsm) -> $(MergedAssembly)" Importance="high" />
<ILMerge InputAssemblies="#(MergeAsm)" OutputFile="$(MergedAssembly)" TargetKind="SameAsPrimaryAssembly" />
</Target>
3) Build your project as usual.
Some more information that might be useful to some people implementing Scott Hanselman's solution.
When I first set this up it would complain about not being able to resolve references to System.Core, etc.
It is something to do with .NET 4 support. Including a /lib argument pointing to the .NET 4 Framework directory fixes it (in fact just include the $(MSBuildBinPath)).
/lib:$(MSBuildBinPath)
I then found that IlMerge would hang while merging. It was using a bit of CPU and a lot of RAM but wasn't outputting anything. I found the fix on stackoverflow of course.
/targetplatform:v4
I also found that some of the MSBuild properties used in Scott's blog article relied on executing MsBuild from the project's directory, so I tweaked them a bit.
I then moved the targets & ilmerge.exe to the tools folder of our source tree which required another small tweak to the paths...
I finally ended up with the following Exec element to replace the one in Scott's original article:
<Exec Command=""$(MSBuildThisFileDirectory)Ilmerge.exe" /lib:$(MSBuildBinPath) /targetplatform:v4 /out:#(MainAssembly) "$(MSBuildProjectDirectory)\#(IntermediateAssembly)" #(IlmergeAssemblies->'"%(FullPath)"', ' ')" />
UPDATE
I also found Logic Labs answer about keeping the CopyLocal behaviour and just excluding ilMerged assemblies from CopyLocal essential if you are using Nuget packages. Otherwise you need to specify a /lib argument for each package directory of referenced assemblies that aren't being merged.
The article Mixing Languages in a Single Assembly in Visual Studio seamlessly with ILMerge and MSBuild at http://www.hanselman.com/blog/MixingLanguagesInASingleAssemblyInVisualStudioSeamlesslyWithILMergeAndMSBuild.aspx demonstrates how to use ILMerge and MSBuild within a Visual Studio Project.
One issue I found with the article at: http://www.hanselman.com/blog/MixingLanguagesInASingleAssemblyInVisualStudioSeamlesslyWithILMergeAndMSBuild.aspx.
If you have any references that you do not wish to ILMerge then the code in the article fails because it overrides the default CopyLocal behaviour to do nothing.
To fix this - Instead of:
<Target Name="_CopyFilesMarkedCopyLocal"/>
Add this entry to the targets file instead (.NET 3.5 only) (to filter out the non-ilmerge copylocal files, and treat them as normal)
<Target Name="AfterResolveReferences">
<Message Text="Filtering out ilmerge assemblies from ReferenceCopyLocalPaths" Importance="High" />
<ItemGroup>
<ReferenceCopyLocalPaths Remove="#(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.IlMerge)'=='true'" />
</ItemGroup>
</Target>
This is a great article that will show you how to merge your referenced assemblies into the output assembly. It shows exactly how to merge assemblies using msbuild.
My 2 cents - I picked up #Jason's response and made it work for my solution where I wanted to generate the *.exe in the bin/Debug folder with all *.dlls inside the same folder.
<Exec Command=""$(SolutionDir)packages\ILMerge.2.13.0307\Ilmerge.exe" /wildcards /out:"$(SolutionDir)..\$(TargetFileName)" "$(TargetPath)" $(OutDir)*.dll" />
Note: This solution is obviously hardcoded into the ILMerge nuget package version. Please let me know if you have some suggestions to improve.
Edit the *.csproj file of the project that you want to merge by adding the code below:
<Target Name="AfterBuild" Condition=" '$(ConfigurationName)' == 'Release' " BeforeTargets="PostBuildEvent">
<CreateItem Include="#(ReferenceCopyLocalPaths)" Condition="'%(Extension)'=='.dll'">
<Output ItemName="AssembliesToMerge" TaskParameter="Include" />
</CreateItem>
<Exec Command=""$(SolutionDir)packages\ILMerge.3.0.29\tools\net452\ILMerge.exe" /internalize:"$(MSBuildProjectPath)ilmerge.exclude" /ndebug /out:#(MainAssembly) "#(IntermediateAssembly)" #(AssembliesToMerge->'"%(FullPath)"', ' ')" />
<Delete Files="#(ReferenceCopyLocalPaths->'$(OutDir)%(DestinationSubDirectory)%(Filename)%(Extension)')" />
</Target>
Notes:
Replace $(SolutionDir)packages\ILMerge.3.0.29\tools\net452\ILMerge.exe with whatever path you have the ILMerge.exe in.
You can remove the Condition in the target to also merge on Debug but then the Debugger might not work
If you are not excluding anything you can remove: /internalize:"$(MSBuildProjectPath)ilmerge.exclude"
Check out this article by Jomo. He has a quick process to hack ILMerge into the msbuild system
http://blogs.msdn.com/jomo_fisher/archive/2006/03/05/544144.aspx

Resources