Default or specify msbuild properties in an external file - msbuild

Ok, so I have a few dozen solutions all built using the exact same command line.
msbuild SolutionName.sln /p:property1=value1;property2=value2;etc etc etc.
Except the number of properties just grows and grows.
Is there a way to specify an external file some how so I don't end up with a 10 line msbuild command? (Think property 100, property 101, etc).
I'm aware of .wpp.target files. However, having to copy them into each project folder really... is my last resort.
And no, I'm not modifying any default MSBuild targets/files whatsoever.

To answer the original question, yes you can specify properties in an external file. They are called MSBuild response files.
msbuild somesolution.sln #PathToResponseFile.rsp
Inside the response file you can put your properties, one per line.
/verbosity:detailed
/target:build
/platform:AnyCPU
/configuration=Release
Some links to better understand:
http://dailytechlearnings.wordpress.com/2011/08/24/msbuild-response-file/
http://msdn.microsoft.com/en-us/library/vstudio/ms404301.aspx
However, using an msbuild file to build your solutions and projects is a better solution. You can create global targets that will do exactly as you want. You can create your own custom Clean and Build targets that will then build/clean your solutions.

First of all - I would recommend you to use msbuild scripts to build your solutions, instead of direct building sln file using command line. E.g. use something like this:
msbuild SolutionName.Build.proj
and inside this Solution1.Build.proj you can put anything as simple as
<Project ToolsVersion="4.0" DefaultTargets="BuildMe" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="BuildMe">
<MSBuild Projects="SolutionName.sln" Properties="property1=value1;property2=value2;"/>
</Target>
</Project>
After this step, which adds flexibility to your build process, you can start leverage AdditionalProperties metadata for MSBuild task.
Then you can use <Import construction to store your list of shared properties in a separate file and item metadata for passing property values:
<Project ToolsVersion="4.0" DefaultTargets="BuildMe" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="MySharedProperies.props" />
<ItemGroup>
<ProjectToBuild Include="SolutionName.sln">
<AdditionalProperties>SomeProjectSpecificProperty</AdditionalProperties>
</ProjectToBuild>
</ItemGroup>
<Target Name="BuildMe">
<MSBuild Projects="#(ProjectToBuild)" Properties="#(MySharedProperies)"/>
</Target>
</Project>
You can check this post for more details about properties and additional properties metadata or this original MSDN reference (scroll to Properties Metadata section)
This is the base idea how to do it, if you have any questions - feel free to ask.

I use an Import file for things that are common across various projects.
<Import Project="CommonBuildProperties.proj"/>
That file contains a PropertyGroup that has the things I want to have the same value across build projects. There's also a conditional statement there that sets certain folder names depending on the name of the computer it's running on. At runtime, if I need to override anything on the command line I do that.
I also have some project-specific Import files (one of our builds is a Powerbuilder application with its own toolset and pecadilloes); order of Import ensures if they require different values for the same element name I get what I want.
My command lines aren't horrible unless I'm doing something odd that needs most everything overridden. About the only things I have to pass in are version number and build type (release or debug).

Related

Embed a conditional XML file in a dll

I want to create a config class for a dll that reads from an embedded resource. I've created multiple xml files with the configuration for all our different environments. I have a separate configuration for each environment DEV, QA, PROD with matches the prefix on the xml file.
.Configuration/DEV.config.xml
.Configuration/QA.config.xml
.Configuration/PROD.config.xml
etc
We've never used MSBuild before and are confused because there seems to be about 3-4 different ways of using it (pre/post build events, xml scripting, custom tasks etc). All i'm trying to do is rename the respective file to config.xml and embed it in the dll, so that the config class can read it out when it's run.
I've seen similar questions that simply embed a file but none that embed and rename at the same time. I assume that any sort of embedding would need to be done as a pre-build event.
Normally I would have tried a few bits and bobs but I have no idea where to start with this, if anyone could point me in the right direction I would be extremely grateful.
Update:
so with some help from jlew I should be able to do something like this
<ItemGroup>
<EmbeddedResource Include="Configuration\$(Configuration).config.xml" >
<LogicalName>config.xml</LogicalName>
</EmbeddedResource>
</ItemGroup>
What you probably want to do (without having seen your code) is to not rename the input file, but direct MSBuild to use a "logical name" for the resource which is different than the file.
<ItemGroup>
<EmbeddedResource Include="Dev.config.xml">
<LogicalName>MyRenamedConfig.config.xml</LogicalName>
</EmbeddedResource>
</ItemGroup>
If you are using csc.exe directly, you can do something similar with:
csc ... /resource:Dev.config.xml,MyRenamedConfig.config.xml

MSBuild: Generate XML documentation for main project but not dependency projects

I have a .sln file with several projects in it. To keep this simple, let's call them...
ProjectA
ProjectB
ProjectC
...where A is the main project which references B and C. My goal is to update my build script to generate an XML "Intellisense" documentation file for ProjectA, without giving build warnings about missing documentation from B and C.
Current Build Script
I have an MSBuild script which includes the following in the build step:
<PropertyGroup>
<CustomOutputPath>C:\build\output\</CustomOutputPath>
</PropertyGroup>
<ItemGroup>
<Projects Include="ProjectA\ProjectA.csproj">
<Properties>OutputPath=$(CustomOutputPath)</Properties>
</Projects>
</ItemGroup>
<MSBuild Projects="#(Projects)" />
(There are actually multiple Projects listed in the ItemGroup, but again, let's keep this simple.)
When I run the build script, it's smart enough to compile B, C, and A for me, even though I've only specified A. All output appears in the "CustomOutputPath" location.
The closest I've gotten...
If I add a 'DocumentationFile' property to my Project entry...
<ItemGroup>
<Projects Include="ProjectA\ProjectA.csproj">
<Properties>OutputPath=$(CustomOutputPath);DocumentationFile=ProjectA.xml</Properties>
</Projects>
</ItemGroup>
...then 'ProjectA.xml' appears in "CustomOutputPath". However, I also get files named 'ProjectA.xml' in the project folder for all three projects:
ProjectA/ProjectA.xml
ProjectB/ProjectA.xml
ProjectC/ProjectA.xml
These files contain the "Intellisense" documentation for their respective projects, even though they're all named "ProjectA.xml".
This creates undesired and misleadingly-named files in the project folders, and (more importantly) generates build warnings for the missing documentation comments in B and C. I don't want to add documentation comments to those projects, so I'd prefer to find a way to have MSBuild generate the documentation only for ProjectA.
Can anyone provide any insight, or an alternative solution?
Based on what I've found - DocumentationFile is a global-level property (and will be used in creation of DocFileItem - global level items list). From my understanding you won't be able to alter it in any easy way in a single logical script.
What you can do is to define special target in separate file that will be imported to every proj file (directly editing proj files or using properties like $CustomBeforeMicrosoftCommonTargets) that will overwrite DocumentationFile with project-dependent value.
As a result - you probably can generate different documentation file names for different projects.
Another solution - just clean-up all unnecessary doc files right after all projs were built.

In MSBuild can you set per-file properties for all files once?

I'm trying to setup a simple rule for VS2010/MSBuild builds to reduce project management. It's related to the 'ExcludedFromBuild' property.
The rule is, if the filename doesn't have the platform name in it, ExcludedFromBuild = true.
ie-
I have Win32Math.cpp & Win64Math.cpp. I only want Win32Math to be compiled when I'm buliding the Win32 Platform. Similar for Win64.
Setting this up per file is easy, but a bit tedious. We have 4 platforms we're targeting, and each time we add a file we have to update properties for each target. I want the rule to be global, so each time I add a platform file I don't have to go through the setup each time.
Is this possible?
You can use ítem definition groups for this kind of thing http://msdn.microsoft.com/en-us/library/bb629392.aspx, but I don't quite understand your specific situation. You'll probably need to set the metadata based on the item's filename matching the platform.
This shows how to use property functions with item metadata. Using Item functions on metadata values
It is possible, but you can't test intrinsic item metadata in <ItemDefinitionGroup>s. The only known way is to use a target.
<Target Name="RemoveNonPlatformItems" BeforeTargets="ClCompile">
<ItemGroup>
<ClCompile>
<ExcludedFromBuild Condition="!$([System.String]::Copy(%(FileName)).Contains($(Platform)))">true</ExcludedFromBuild>
</ClCompile>
</ItemGroup>
</Target>
Or even better:
<Target Name="RemoveNonPlatformItems" BeforeTargets="ClCompile">
<ItemGroup>
<ClCompile Remove="%(Identity)" Condition="!$([System.String]::Copy(%(FileName)).Contains($(Platform)))" />
</ItemGroup>
</Target>

Tfs2010 Build Number and Assembly File Versions & MSBuild targets

I read John Robbins' article TFS 2010 Build Number and Assembly File Versions: Completely In Sync with Only MSBuild 4.0, and I'm wondering about the best way to go about integrating this.
The download for the article has two files, one is a targets file and one is a proj file.
The targets file has a number of tasks to scrape out a build number based on the Tfs build number (the same one used for the builds) and write that number out to some location (call it BuildNumberFile) for consumption by other proj files.
The proj file is very simple. It just imports the aforementioned targets file, and then declares a target with name "All" while also declaring DefaultTargets on the Project element to be All as well.
<Project ToolsVersion="4.0" DefaultTargets="All" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<!-- The two required properties so the Wintellect.TFSBuildNumber tasks knows your major and minor values.-->
<TFSMajorBuildNumber>3</TFSMajorBuildNumber>
<TFSMinorBuildNumber>1</TFSMinorBuildNumber>
</PropertyGroup>
<Import Project="Wintellect.TFSBuildNumber.targets"/>
<!-- Just ask for the version information files you need. These are here to show all the diffent ones in
Wintellect.TFSBuildNumber.Targets. You can change the names -->
<Target Name="All"
DependsOnTargets="WriteSharedCSharpAssemblyVersionFile;
WriteSharedVBAssemblyVersionFile;
WriteSharedCPPCLIAssemblyVersionFile;
WriteSharedCPPAssemblyVersionFile;
WriteSharedWiXAssemblyVersionFile;
WriteSharedTextAssemblyVersionFile;"/>
</Project>
I have two questions about this:
I'm still learning MSBuild. If the name of the target isn't specified elsewhere in the targets, is the target executed? How do I ensure that this target is run?
Are the csproj files supposed to declare an Include item for the location where BuildNumberFile is, even though it doesn't exist until compiletime?
Do ItemGroups and Include have a DependsOnTargets or something that allows them make sure the file exists before they build?
Are the entire contents of the csproj file using this supposed to be wrapped in a target that expresses DependsOnTargets for BuildNumberFile?
Thanks!
I think I've got this figured out, but two people promoted my question so I'll answer it here:
You can ensure that a target is run by expressing a dependency on it from another target. Microsoft.Common.targets exposes two targets--BeforeBuild and AfterBuild--expressly for the purpose of being overridden for customizability. I found the easiest way to do this was <Target Name="BeforeBuild" DependsOnTargets="WriteSharedCSharpAssemblyVersionFile" /> where WriteSharedCSharpAssemblyVersionFile is the target declared in the download from the link in the original post. Also, if you're new to MSBuild, this BeforeBuild target must be declared after the Microsoft.CSharp.targets is imported, but the default csproj template guides you in doing this.
The WriteSharedCSharpAssemblyVersionFile target should indeed write the file to some central location, since when building a solution, all targets are executed only once. All projects should reference the file from that location even if it doesn't exist, since by the time compilation happens (or more importantly, by the time references are resolved), the BeforeBuild target will have run and the file will be in place.
In my structure, I have these versioning files in a folder directly underneath the branch root folder. Furthermore, since the file being built is generated, I have it build to the output directory. It seems a little strange to be referencing things from the output, but it preserves the invariant of having all build products in one place so that the output directory can be blown away as a means of performing a clean.
In MSBuild items constitute inputs into the system (usually files) so it's weird to think of them depending on targets. After some learning this question doesn't make a lot of sense. In any case, the answer is no.
The entire contents of the file should indeed not be all in one target--all that is required is to import the Wintellect.TFSBuildNumber.targets file at the beginning of your csproj file, and declare BeforeBuild's dependency on WriteSharedCSharpAssemblyVersionFile at the end.
Hope this helps!

CreateItem vs ItemGroup

What is the difference between creating an item inside a target like this:
<Target Name="DoStuff">
<CreateItem Include="#(IntermediateAssembly)" >
<Output TaskParameter="Include" ItemName="FileWrites"/>
</CreateItem>
</Target>
and like this:
<Target Name="DoStuff">
<ItemGroup>
<FileWrites Include="#(IntermediateAssembly)" />
</ItemGroup>
</Target>
When would you use one or the other and why?
In versions of MSBuild prior to 3.5 you could not define properties or items inside of targets (like in your second example). So a task was used instead (CreateItem and CreateProperty)
If you are using ToolsVersion 3.5 then you don't need to use CreateItem anymore (though you still can if you prefer).
In the end they both create the item the same, with the same scope. Using the second syntax is more readable and setting up custom meta data is much easier (in my opinion).
NOTE: The 3.5 version of MSBuild is installed with .NET 3.5. Though you need to define ToolsVersion="3.5" in the Project tag of your MSBuild file to use 3.5 features.
In case you are wondering, I got most of this info from the book Inside the Microsoft® Build Engine: Using MSBuild and Team Foundation Build which I really liked (but am not affiliated with in any way).
CreateItem and CreateProperty are obsoleted in MSBuild 3.5 (though will always continue to work, of course). It was pretty obvious we needed the same familiar syntax for ItemGroup and PropertyGroup to work inside targets.
But ItemGroup inside a target has some special extra powers. It can modify items: for example, this will add true to all items in Resources list that have a metadata named Primary with value of true; only if there isn't already Copy metadata:
<ItemGroup>
<Resources Condition=" '%(Primary)' == 'true' ">
<Copy Condition=" '%(Copy)' == '' ">true</Copy>
</Resources>
</ItemGroup>
One other magic power: you can now remove items from a list. This example will remove all items from the Resources list that have metadata Type with value Bitmap:
<ItemGroup>
<Resources Condition=" '%(Type)'=='Bitmap' " Remove="#(Resources)"/>
</ItemGroup>
These magic powers only work inside at present, not outside.
For full details of this stuff, I highly recommend Sayed Hashimi's book on MSBuild. It's easily found on Amazon.
Dan -- msbuild team.
I dont think the answer accepted has defined the difference.
The difference is:
ItemGroup is evaluated when the MSBuild script is loaded.
CreateItem is evaluated when the Target is executed
This can lead to different values of the Item within the script.
Take the example of a Task that does something with a all the files that match "*.txt" in a directory. If your MSBuild script is loaded in visual studio, only the files that existed when VS started will be in the Item if you use ItemGroup.
If you use CreateItem - it will do a search for all *.txt files when the target is executed.
As an additional info for others passing here: The Build-Engine that contains an API to construct MSBuild projects does not support adding ItemGroups the new way to a Target. Here you WILL have to use the old-fashioned way.