I have a process where I need to automate the process of generating the satellite assemblies. Specifically this is for WPF and combining Resx and BAML resources.
I've got a build script that works, but it requires manual adding of the .resources files I want to combine with the BAML resources. IOW, I have to add to the build script each time I add a .Resx resource. Not cool!
Currently I'm running the assembly linker manually and the script looks like this:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Adds the build action 'LocBamlCsv' -->
<ItemGroup>
<AvailableItemName Include="LocBamlCsv" />
</ItemGroup>
<Target Name="CreateSatelliteAssemblies"
DependsOnTargets="$(CreateSatelliteAssembliesDependsOn)">
<!-- Locbaml needs the runtime assemblies in the intermediate dir -->
<Copy SourceFiles="$(ProjectDir)..\Tools\LocBaml.exe"
DestinationFolder="$(OutputPath)" />
<!-- generate a .resources file for .csv merged output -->
<Exec Command="LocBaml /generate ..\..\$(IntermediateOutputPath)$(TargetName).g.$(UICulture).resources /trans:%(LocBamlCsv.FullPath) /out:../../$(IntermediateOutputPath) /cul:%(LocBamlCsv.Culture)"
WorkingDirectory="$(OutputPath)"
Outputs="$(OutputPath)%(LocBamlCsv.Culture)\$(TargetName).$(UICulture).dll" />
<!-- Generate the resource assembly by merging all .resources files -->
<!-- NOTE: Explicitly add any resource files here -->
<Exec Command="al /template:$(TargetName).exe /culture:%(LocBamlCsv.Culture) /out:%(LocBamlCsv.Culture)\$(TargetName).resources.dll /embed:$(TargetName).g.%(LocBamlCsv.Culture).resources /embed:$(TargetName).Properties.Resources.%(LocBamlCsv.Culture).resources"
WorkingDirectory="$(InterMediateOutputPath)"
/>
</Target>
</Project>
As mentioned it works. but the last command that calls al would be much easier to work with if there was some way to use wild cards (ie. $(TargetName).*s.%(LocBamlCsv.Culture).resources.
I've tried a number of things. Using the build process apparently fires at the wrong time and it ends up failing to find files.
I'm not sure exactly what your problem is
but you did say something that makes me wonder. "Using the build process apparently fires at the wrong time and it ends up failing to find files." From this I get the impression that you are trying to create an item which contains files that are generated during the build process. If this is the case then you should declare those as dynamic items, which are items declared inside of a target. Items declared outside of targets (static items) are evaluated before any target begins to execute. See my blog post MSBuild: Property and Item Evaluation.
Sayed Ibrahim Hashimi
My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build
Related
I am facing an issue with MSBuild I can't overcome it by myself. As a result I rely on community's wisdom.
The real situation I'm having troubles with
I have a soluiton file containing several projects with dependencies to other projects in same solution. I'd like to append a custom target to one of the project's csproj file and build it from the command line. It will allow me to make all the necessary output binaries for this project for further processing during the building of the custom target. But the main thing is that I can't figure out how to do it, googling doesn't help either.
Simplification
To make thing simplier I decided to make a new C# console project, add a simple custom target to the project's file and try to make it build. Still no success! Here what I've done so far:
Created a solution app with a default console project coreapp. This gaves me at least two files:
app.sln
coreapp\coreapp.csproj
Modified coreapp.csproj with addition of my custom target inside of the Project tag
<Target Name="SampleTarget">
<Message Text="This is a SampleTarget" />
</Target>
Run on the command line the following command
%windir%\Microsoft.NET\framework\v3.5\msbuild.exe app.sln /t:coreapp:SampleTarget
or even
%windir%\Microsoft.NET\framework\v3.5\msbuild.exe app.sln /t:coreapp.csproj:SampleTarget
Results
No luck, facing the error
MSB4057: The target "coreapp.csproj:SampleTarget" does not exist in the project.
I suspect that MSBuild thinks somehting fundamentally different from what I want it to think...
BEsides that, I also tried to set on the same command line the environment variable MSBuildEmitSolution=1 to force msbuild dump a temporary solution file it creates while processing the solution. In this file, indeed, no such target. However I guess it isn't the reason because I asked msbuild to build coreapp.proj where target SampleTarget really resides.
The question is how to build SampleTarget in this simplified scenario using solution file since potencially it can contain dependencies for the project containing this SampleTarget target?
I'd be greatful for any sort of help or firection for further investigation!
Instead of inserting a custom target in your project file, you could try creating a new standalone msbuild file, which would:
build the solution file (which builds projects)
defines your extra target
Call it app-custom-Debug.msbuild , for example.
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<WorkingFolder>$(MSBuildProjectDirectory)</WorkingFolder>
<Configuration>Debug</Configuration>
<SolutionFile>app.sln</SolutionFile>
</PropertyGroup>
<Target Name="Build" DependsOnTargets="Compile" />
<Target Name="Compile">
<Message Text="=== COMPILING $(Configuration) configuration ===" />
<MSBuild Projects="$(SolutionFile)"
Properties="Configuration=$(Configuration)" />
</Target>
<Target Name="SampleTarget">
<Message Text="This is a SampleTarget" />
</Target>
</Project>
Then you execute:
msbuild.exe app-custom-Debug.msbuild /t:SampleTarget
One option is to tie your SampleTarget to the standard Build targets via overriding the appropriate DependsOn property. In this case you could tell BeforeBuild that it DependsOn SampleTarget or you do the same thing with AfterBuild. This will ensure that MSBuild processes your target prior to the standard target indicated.
I want to inhibit the building of certain projects within a solution from building (within a TeamCity Build Configuration in order to optimize the speed of my Commit Build feedback if you must know).
I'm aware of the Solution Configurations mechanism but don't want to have to force lots of .sln files to end up with every permutation of things I want to be able to switch off. I have Convention based rule where I want to say "If I'm doing the Commit Build, I dont want to do the final installer packaging". (And I don't want to break it out into a separate solution).
I'd prefer not to use a solution involving find and replace in the .sln file or in a .proj file created via [MsBuildEmitSolution][1]. I'm aware of questions here which cover the out of the box solution and this slightly related question.
I see MSBuild /v:diag is saying:
2>Target "Build" in file "Z.sln.metaproj" from project "Z.sln" (entry point):
Using "MSBuild" task from assembly "Microsoft.Build.Tasks.v4.0, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a".
Task "MSBuild"
Global Properties:
BuildingSolutionFile=true
CurrentSolutionConfigurationContents=<SolutionConfiguration>
<ProjectConfiguration Project="{C83D035D-169B-4023-9BEE-1790C9FE22AB}" AbsolutePath="X.csproj" BuildProjectInSolution="True">Debug|AnyCPU</ProjectConfiguration>
<ProjectConfiguration Project="{15E7887D-F1DB-4D85-8454-E4EF5CBDE6D5}" AbsolutePath="Y.csproj" BuildProjectInSolution="True">Debug|AnyCPU</ProjectConfiguration>
</SolutionConfiguration>
So the question is:
Is there a neat way of me getting to do an XPath replace or similar to have the effect of changing BuildProjectInSolution="True" to BuildProjectInSolution="False" for Project Y above
Failing that, is there a relatively simple edit I can do within a .ccproj (An Azure 1.4 Package) or a .csproj (a general project) file to cause the effects (including triggering of dependent projects) of the project being enabled within a commandline msbuild Z.sln solution build to be nullified?
Not sure it qualifies as neat, but you can set CustomAfterMicrosoftCommonTargets to import an msbuild file to over-ride the BuildDependsOn property, pointing it to your own custom build task. Basically, by setting CustomAfterMicrosoftCommonTargets you get msbuild to import an msbuild file containing the following:
<PropertyGroup>
<OldBuildDependsOn>$(BuildDependsOn)</OldBuildDependsOn>
<BuildDependsOn>MyBuild</BuildDependsOn>
</PropertyGroup>
<Target Name="OldBuild" DependsOnTargets="$(OldBuildDependsOn)" />
<Target Name="MyBuild">
<CallTarget Targets="OldBuild" Condition="<IfIWantThis>" />
</Target>
Edit
You can use the following MyBuild target to Include/Exclude projects based on regular expressions passed in as IncludeInBuild and ExcludeFromBuild properties. (If you want complex regexes, you may fall foul of MSBuild special character escaping, but this works well enough for simple matching)
> msbuild /p:ExcludeFromBuild="Tests|Install|Azure"
<Target Name="MyBuild">
<CallTarget Targets="OldBuild" Condition="('$(IncludeInBuild)'=='' OR
'$([System.Text.RegularExpressions.Regex]::IsMatch($(MSBuildProjectFullPath),
$(IncludeInBuild),
System.Text.RegularExpressions.RegexOptions.IgnoreCase))'=='True') AND
('$(ExcludeFromBuild)'=='' OR
'$([System.Text.RegularExpressions.Regex]::IsMatch($(MSBuildProjectFullPath),
$(ExcludeFromBuild),
System.Text.RegularExpressions.RegexOptions.IgnoreCase))'=='False')" />
</Target>
You could always pass the particular projects you want to build as parameters to the MSBuild.
The MSBuild command line would look like this:
MSBuild /t:<Project Name>:Rebuild;<Another Project Name>:Rebuild
In TeamCity, you would put <Project Name>:<Target Action> in the target field in the MSBuild runner.
I add a system parameter under Parameters
Name: system.ExcludeFromBuild
Kind: System property (system.)
Value: path to your csproj
While chasing incremental build time improvements, I found that .btproj files and thus all other projects that depend on these are rebuilt (partly) on each incremental build. Tracking this all the way to BizTalkCommon.targets, I found that it does a 2 pass compilation of the assembly - but only the first pass respects already built artifacts, thus breaking the incremental part of the dependency chain. The offending target can be seen in BizTalkCommon.targets (line 228):
<!-- Delete the assembly and rerun the build process -->
<Target Name="SecondPass"
Condition="$(SecondBuild)!=true and $(TempAssemblyOnly)!=true">
<Delete Files="#(IntermediateAssembly)" />
<MSBuild Projects="$(MSBuildProjectFile)" Properties="SecondBuild=true"/>
</Target>
I realize that there's a reason for the 2 pass build, but simply cannot believe it wouldn't be possible to specify appropriate in- and outputs for the target to handle incremental builds correctly.
Does anyone know if there's a patch for the .targets file, or if there's another good reason that incremental builds aren't supported?
You can enable incremental compilation of MSBuild BizTalk project with a couple of very simple changes. Basically, you need to override two targets that are defined in the BizTalkCommon.targets file.
Those targets can be overriden in your own .btproj files and do not require modifying the original .targets file that ships with BizTalk.
How To
First Create you own .targets file to host your customizations, for instance BizTalkCustom.targets :
<Import Project="$(MSBuildExtensionsPath)\Microsoft\BizTalk\BizTalkC.targets" />
<!-- Rerun the build process (second pass) -->
<Target Name="SecondPass" Condition="$(SecondBuild)!=true and $(TempAssemblyOnly)!=true and #(XLang)!=''">
<MSBuild Projects="$(MSBuildProjectFile)" Properties="SecondBuild=true" />
</Target>
<!-- Compile XLang/s orchestration -->
<Target
Name="CompileODX"
Condition="$(SecondBuild)==true"
Inputs="#(XLang);$(MSBuildAllProjects);$(ClrTypesAssembly)"
Outputs="$(BuildDone)">
<!-- Delete previously generated C# files from XLang compilation -->
<Delete Files="#(IntermediateAssembly)" />
<Delete Files="#(CSharpOutputFromXLang)" />
<XLangTask XLangItems="#(XLang)"
ProjectReferences="#(ReferencePath)"
WarningLevel="$(WarningLevel)"
BpelCompliance="$(BpelCompliance)"
DefineConstants="$(DefineConstants)"
TreatWarningsAsErrors="$(TreatWarningsAsErrors)"
TempAssembly="$(ClrTypesAssembly)"
OutputDirectory="$(XLangOutputPath)">
</XLangTask>
</Target>
Then, replace the last Import statement in your .btproj file:
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(MyCustomExtensions)\BizTalkCustom.targets" />
How doe it work
BizTalk Server projects need somehow to be compiled in two passes. The first pass compiles schemas, maps and pipelines, whereas the second pass compiles orchestrations.
You'll notice that the overriden targets are very very similar than the original ones, defined inside the BizTalkCommon.targets file. In fact, I made two simple changes:
The first change involves modifying the SecondPass Target and adding an extra test in the Conditionattribute. This test is usefull to prevent the second pass from occurring if your project does not even have Orchestrations.
Unfortunately, if your project contains Orchestrations, the original SecondPass Target deletes the intermediate assemblies and then proceed to compile the Orchestrations. However, the CompileODX Target does not need to run if all files are already up to date. Therefore, the second change involves moving the Delete Task from the SecondPass Target to the CompiledODX Target.
That's all there is to it.
This is something my team ran into a while back and simply backed off customizing the build files and went with the BizTalk deployment framework instead, located here. BizTalk does lots of "funny" things from a VS level, since 2009 was the first version BizTalk didn't use an external build process. But I'm not sure why the second pass is needed, except maybe from a designer perspective.
I have an MSBuild file that manipulates the AssemblyInfo file before the application is compiled. At the end of the build, it restores the AssemblyInfo file. It does this by backing up the file, manipulating it, and then after build time, restoring the file.
This works fairly well except when an error occurs during the build. It then does not restore the original file. Is there a way I can tell MSBuild to execute a target at the end of a build no matter if it succeeded or failed?
Based on your last comment to the original question I would take another approach, and forget the approach you are currently taking. You should know that your version info doesn't have to be in the AssemblyInfo.cs file. It can be in any code file, just as long as you only have attributes AssemblyVersion and AssemblyFileVersion defined once each. With that being said what I would do is follow these steps:
Remove AssemblyVersion & AssemblyFileVersion from AssemblyInfo.cs
Create a new file, name it whatever you want want in my case I put it at Properties\VersionInfo.cs. Do not add this file to the project.
Edit the project file to include that file into the list of file to be compiled only when you want it
Let's expand a bit on #3. When you build a .NET project, the project itself is an MSBuild file. Inside that file you will find an item declared Compile. This is the list of files that will be sent to the compiler to be compiled. You can dynamically include/exclude files from that list. In you case you want to include the VersionInfo.cs file only if you are building on the build server (or whatever other condition you define). For this example I defined that condition to be if the project was building in Release mode. So for Release mode VersionInfo.cs would be sent to the compiler, and for other builds not. Here are the contents of VersionInfo.cs
VersionInfo.cs
[assembly: System.Reflection.AssemblyVersion("1.2.3.4")]
[assembly: System.Reflection.AssemblyFileVersion("1.2.3.4")]
In order to hook this into the build process you have to edit the project file. In that file you will find an element (maybe more than 1 depending on project type). You should add a target similar to the following there.
<Target Name="BeforeCompile">
<ItemGroup Condition=" '$(Configuration)'=='Release' ">
<Compile Include="Properties\VersionInfo.cs" />
</ItemGroup>
</Target>
Here what I've done here is to define a target, BeforeCompile, which is a well-known target that you can override. See this MSDN article about other similar targets. Basically this is a target which will always be called before the compiler is invoked. In this target I add the VersionInfo.cs to the Compile item only if the Configuration property is set to release. You could define that property to be whatever you wanted. For instance if you have TFS as your build server then it could be,
<Target Name="BeforeCompile">
<ItemGroup Condition=" '$(TeamFoundationServerUrl)'!='' ">
<Compile Include="Properties\VersionInfo.cs" />
</ItemGroup>
</Target>
Because we know that TeamFoundationServerUrl is only defined when building through TFS.
If you are building form the command line then something like this
<Target Name="BeforeCompile">
<ItemGroup Condition=" '$(IncludeVersionInfo)'=='true' ">
<Compile Include="Properties\VersionInfo.cs" />
</ItemGroup>
</Target>
And when you build the project just do msbuild.exe YourProject.proj /p:IncludeVersion=true. Note: this will not work when building a solution.
What about changing the problem:
Add a "template" AssemblyInfo.cs.template to version control that represents your "ideal" AssemblyInfo.cs with regex hooks in there
Before build, copy the template to the real and apply your regexes
Add some kind of subversion ignore for AssemblyInfo.cs (I'm no svn expert, but I'm pretty sure there is a way you can tell it to ignore certain files)
In the event that your devs need to add some kind of customization that would normally appear in an AssemblyInfo.cs (eg InternalsVisibleTo), then get them to add it to a different .cs file that IS checked in.
As a further refinement, combine Sayed's solution with mine and remove version info stuff from the actual AssemblyInfo.cs and have a VersionInfo.cs.template that is checked in, that creates a VersionInfo.cs in BeforeBuild.
I never used it, but from the documentation it seems that the OnError Element is useful to what you're trying to achieve.
Causes one or more targets to execute,
if the ContinueOnError attribute is
false for a failed task.
I need a set of tasks that need to be executed exactly once for the entire solution. This will run tasks that will modify each project to run a separate set of tasks for each project. We had done this earlier using a separate project to the solution which had the solution level tasks, but we want to move away from that. Has anyone done this or does anyone have any suggestions on how to implement this?
Since Solution files are not in MSBuild format they are not easily extended or customized. If you want more control over the build process you would have to create a "driver" msbuild file which would replace your solution file. Inside this driver file you would build all the projects that you needed and perform some additional tasks. You would do this using the MSBuild task. Here is a sample showing how to build more than 1 project.
<Project ...>
<ItemGroup>
<Projects Include="proj01.csproj"/>
<Projects Include="proj02.csproj"/>
<Projects Include="proj03.csproj"/>
</ItemGroup>
<Target Name="BuildAll">
<MSBuild Projects="#(Projects)" BuildInParallel="true" />
</Target>
</Project>
So in your case you would just execute the tasks before you build the projects. Also note that I specified the value true for the BuildInParallel indicating that MSBuild can try and build more than one project at once.
An alternative solution is to have a single target that dispatches to an MSBuild invoked target with as many Global properties removed as possible. My team have a target in the InitialTargets of a Directory.Build.props Import'ed props file - something like:
<Target Name="Prebuild">
<MSBuild Projects="$(MSBuildThisFileFullPath)"
Targets="PrebuildWorker"
RemoveProperties="Configuration;Platform;TargetFramework;BuildProjectReferences" />
</Target>
Since MSBuild appears to synchronize parallel builds on the {project file, global properties, target} set, then by removing all of the properties you can synchronize the build and run it once for all projects being built. The downside: you have to maintain the RemoveProperties attribute - MSBuild doesn't have a way to remove all global properties. If something in the build issues a new MSBuild task with a custom property specified, then you'll get a second instance of the Target invoked.
And - of course - your build will be synchronized on this target. You could try hooking the target up by setting, say, CompileDependsOn to depend on the Prebuild target, to allow independent progress in the build. But to have the target run early and ubiquitously using InitialTargets seems like the better option.