Using MSBuild to buld a solution (.sln) with many projects in how can I make each project build into its own folder? - msbuild

I am trying to create a simple build process for a quite complex (many projects) vs2010 solution.
I wish for a folder structure such as this
-Build
-Proj1
-proj1.exe
-proj1.dll
-Proj2
-proj2.exe
-proj2.dll
......
-Projn
-projn.exe
-projn.dll
What I am getting from my attempts below is
-Build
-proj1.exe
-proj1.dll
-proj2.exe
-proj2.dll
-projn.exe
-projn.dll
I currently have this as a .proj file. (see below)
This builds things fine, however it puts everything in the "build" folder that I specify. I want each project to be in its own seperate folder within that 'build' folder. How can I achive this?
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<BuildOutputDir>C:\Projects\BuildScripts\Build</BuildOutputDir>
<SolutionToCompile>PathToSolution.sln</SolutionToCompile>
</PropertyGroup>
<Target Name="Clean">
<RemoveDir Directories="$(BuildOutputDir)" />
</Target>
<Target Name="Compile">
<MakeDir Directories="$(BuildOutputDir)" />
<MSBuild Projects="$(SolutionToCompile)"
properties = "OutputPath=$(BuildOutputDir)" Targets="Rebuild" />
</Target>
<Target Name="Build" DependsOnTargets="Clean;Compile">
<Message Text="Clean, Compile"/>
</Target>
</Project>
I call the .proj with a simple bat
"%windir%\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe" /nologo externalBuild.proj /m:2 %*
pause
I have also tried a more complex version (copy and paste!) that looks more like it should work, but still puts things in a single folder.
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="BuildAll" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ProjectsToBuild Include="path to solution folder\**\*proj" Exclude="$(MSBuildProjectFile)"/>
</ItemGroup>
<PropertyGroup>
<Configuration>CI</Configuration>
</PropertyGroup>
<Target Name="CoreBuild">
<MSBuild Projects ="#(ProjectsToBuild)"
ContinueOnError ="false"
Properties="Configuration=$(Configuration)">
<Output ItemName="OutputFiles" TaskParameter="TargetOutputs"/>
</MSBuild>
</Target>
<PropertyGroup>
<DestFolder>Build\</DestFolder>
</PropertyGroup>
<Target Name="CopyFiles">
<Copy SourceFiles="#(OutputFiles)"
DestinationFiles="#(OutputFiles->'$(DestFolder)%(RecursiveDir)%(Filename)%(Extension)')" />
</Target>
<Target Name="CleanAll">
<!-- Delete any files this process may have created from a previous execution -->
<CreateItem Include="$(DestFolder)\**\*exe;$(DestFolder)\**\*dll">
<Output ItemName="GeneratedFiles" TaskParameter="Include"/>
</CreateItem>
<Delete Files="#(GeneratedFiles)"/>
<MSBuild Projects="#(ProjectsToBuild)" Targets="Clean" Properties="Configuration=$(Configuration);"/>
</Target>
<PropertyGroup>
<BuildAllDependsOn>CleanAll;CoreBuild;CopyFiles</BuildAllDependsOn>
</PropertyGroup>
<Target Name="BuildAll" DependsOnTargets="$(BuildAllDependsOn)"/>
</Project>

Using devenv.com to build from the command line will do what you want. It will use the output directories specified in the project files. This is what we're using, because at the moment we don't need more control over the build mechanism.

Related

Execute a XCopy operation after all the projects build in MSBuild

I have a .proj file which is configured to execute a solution file which in turn build all the projects in the solution.
I want to add an XCopy operation which should copy the .dll files of all projects to another location only after all the projects build is completed.
I have tried with below, but it is not copying the dlls.
I am newbie in writing MSBuild tags, so it could be that I may be wrong in choosing this approach to write the task in this way.
Please provide a solution, if anyone knows.
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition="'$(Configuration)' == 'Release|Debug'"/>
<Platform Condition="'$(Platform)' == ''">x64</Platform>
</PropertyGroup>
<ItemDefinitionGroup>
<SolutionToBuild>
<Properties>Configuration=$(Configuration);Platform=$(Platform)</Properties>
<Targets>Clean;Build</Targets>
</SolutionToBuild>
</ItemDefinitionGroup>
<ItemGroup>
<SolutionToBuild Include="..\Seg\Algorithms.sln" />
</ItemGroup>
<Target Name="Build" >
<MSBuild Projects="#(SolutionToBuild)" Targets="%(SolutionToBuild.Targets)" Properties="%(SolutionToBuild.Properties)" BuildInParallel="false" ContinueOnError="false" />
</Target>
<Target Name="Clean">
<MSBuild Projects="#(SolutionToBuild)" Targets="Clean" Properties="%(SolutionToBuild.Properties)" BuildInParallel="false" ContinueOnError="false" />
</Target>
<PropertyGroup>
<CopyDestination>..\Extern\Algo\bin\$(Configuration)\</CopyDestination>
<CopySource>..\Seg\Algorithms\$(Configuration)\DoBin\</CopySource>
</PropertyGroup>
<ItemGroup>
<FilesToCopy Include="$(CopySource)*.dll"/>
</ItemGroup>
<ItemGroup>
<CustomBuildStep Include ="#(FilesToCopy)">
<Message>Copying..</Message>
<Command> XCOPY %(Identity) $(CopyDestination) /f /y </Command>
</CustomBuildStep>
</ItemGroup>
<PropertyGroup>
<CustomBuildAfterTargets>Build</CustomBuildAfterTargets>
</PropertyGroup>
</Project>
Think of Targets as methods that are called. They run in sequence, so you just need to put your copy after the solution build:
<Target Name="Build">
<MSBuild Projects="#(SolutionToBuild)" Targets="%(SolutionToBuild.Targets)" Properties="%(SolutionToBuild.Properties)" BuildInParallel="false" ContinueOnError="false" />
<ItemGroup>
<FilesToCopy Include="..\Seg\Algorithms\$(Configuration)\DoBin\*.dll" />
</ItemGroup>
<Copy SourceFiles="#(FilesToCopy)" DestinationFolder="..\Extern\Algo\bin\$(Configuration)\" SkipUnchangedFiles="true" />
</Target>

MSBuild: Output properties from imported projects

Let's say I have a build.proj like this:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0"
DefaultTargets="AfterBuild"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<CustomAfterMicrosoftCSharpTargets>$(MSBuildThisFileDirectory)Common.Build.targets</CustomAfterMicrosoftCSharpTargets>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<ProjectProperties>
Configuration=$(Configuration);
Platform=$(Platform);
CustomAfterMicrosoftCSharpTargets=$(CustomAfterMicrosoftCSharpTargets);
</ProjectProperties>
</PropertyGroup>
<ItemGroup>
<ProjectToBuild Include="$(MSBuildThisFileDirectory)src\Proj\MyApp.csproj" />
</ItemGroup>
<Target Name="Build">
<MSBuild Targets="Build"
Projects="#(ProjectToBuild)"
Properties="$(ProjectProperties)" />
</Target>
<Target Name="AfterBuild" DependsOn="Build">
<Message Text="ChildProperty: $(ChildProperty)" />
</Target>
</Project>
In Common.Build.targets, I have a Target that creates a property:
<Target Name="DoSomethingUseful">
<!-- Do something useful -->
<CreateProperty Value="SomeComputedThingy">
<Output TaskParameter="Value" PropertyName="ChildProperty"/>
</CreateProperty>
</Target>
Now if I build build.proj, I do not see the value of ChildProperty in the message. The output is blank: ChildProperty:.
I was under the impression that any output for a target is merged back to global context after its execution. But it seems that it only applies to anything within that target file.
How do I make ChildProperty bubble up to the parent build.proj?
When you are calling <MSBuild> task on dependent projects, read TargetOutputs output parameter of the task. See example from MSDN:
<Target Name="BuildOtherProjects">
<MSBuild
Projects="#(ProjectReferences)"
Targets="Build">
<Output
TaskParameter="TargetOutputs"
ItemName="AssembliesBuiltByChildProjects" />
</MSBuild>
</Target>
You will also need to ensure the target you are calling in dependent projects correctly populates Returns or Output parameter (Returns takes precedence if used). E.g.:
<Target Name="MyTarget" Inputs="..." Outputs="..." Returns="$(MyOutputValue)">
<PropertyGroup>
<MyOutputValue>set it here</MyOutputValue>
</PropertyGroup>
</Target>

MSBuild project to test a C++ program

I have a .vcxproj file that compiles a C++ program. I would like to create a second MSBuild project file that tests the program by running it, but only if the program has been rebuilt since the last successful test. How can I access the "TargetPath" of the program from the second project file?
If I could access TargetPath as an "item" from the .vcxproj file, then the the tester project file will look like this:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="Build" Inputs="#(TargetPath)" Outputs="#(TargetPath->'%(filename).test-passed)'">
<Exec Command="#(TargetPath)" />
<Touch Files="#(TargetPath->'%(filename).test-passed)'" />
</Target>
</Project>
I would like to execute the test using a separate project file from the compilation of the program, to make it easier to choose between build-and-test or build-and-debug within Visual Studio, without multiplying the build configurations.
It is possible to run a native program compiled by a separate .vcxproj using the MSBuild task. Use the <Output> element to create an Item with the "TargetOutputs" from the C++ application build. However, if you are building a "native" program, "TargetOutputs" is normally blank. In this case, use the "GetNativeTargetPath" target to get the output path. The following project .vcxproj file works with Visual Studio. It builds test_build.vcxproj. The test_build.exe file is run, if it has changed since the last successful run.
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{80DB0D71-72E0-4FB1-B53F-EFB858A1D5A8}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>nordic_test_run</RootNamespace>
</PropertyGroup>
<PropertyGroup>
<ConfigurationType>Application</ConfigurationType>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ItemGroup>
<ProjectReference Include="test_build.vcxproj" />
</ItemGroup>
<Target Name="BuildExecutable">
<MSBuild Projects="#(ProjectReference)" Targets="Build" BuildInParallel="True" />
<MSBuild Projects="#(ProjectReference)" Targets="GetNativeTargetPath" BuildInParallel="True">
<Output TaskParameter="TargetOutputs" ItemName="NativeTests" />
</MSBuild>
</Target>
<Target Name="Build" DependsOnTargets="BuildExecutable" Inputs="#(NativeTests)" Outputs="#(NativeTests->'%(filename).test-passed')">
<Exec Command="#(NativeTests)" />
<Touch Files="#(TestTargets->'%(filename).test-passed')" />
</Target>
</Project>

Daily builds with MsBuild

What i want to do is to copy all files and subfolders from the OutputPath to the daily folder. For example i have project called Clock, i have msbuild script for it:
<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectPath>C:\Clock</ProjectSolutionName>
<ProjectSolutionName>Clock</ProjectSolutionName>
</PropertyGroup>
<Target Name="ReleaseBuild">
<Message Text="Building $(ProjectSolutionName) Release Build" />
<MSBuild Projects="$(ProjectPath)\$(ProjectSolutionName).sln" Targets="Clean" Properties="Configuration=Release" />
<MSBuild Projects="$(ProjectPath)\$(ProjectSolutionName).sln" Targets="Build" Properties="Configuration=Release" />
<Message Text="$(ProjectSolutionName) Release Build Complete!" />
</Target>
</Project>
Now when i run the script it compiles the solution and the files will be stored to a Release folder. How could i copy all the files and subfolders from the Release folder to folder named as the date, as for today for example: C:\Clock_Builds\20110803
This should do most of what you ask (its msbuild 4):
<Project DefaultTargets="DateCopy" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<Target Name="DateCopy">
<ItemGroup>
<Release Include="d:\Build\**\**"/>
</ItemGroup>
<PropertyGroup>
<StringDate>$([System.DateTime]::Now.ToString('yyyyMMdd'))</StringDate>
</PropertyGroup>
<MakeDir Directories="D:\Release\$(StringDate)"/>
<Message Text="$(StringDate)" Importance="High"/>
<Copy SourceFiles="#(Release)"
DestinationFolder="D:\Release\$(StringDate)\%(RecursiveDir)"/>
</Target>
</Project>
Hope that helps

Updating Assembly information with MSBuild failing

All
i am trying to automatically update the assembly information of a project using AssemblyInfo task, before build however the target appears to do nothing (no failure/error) just no update/creation
Below is the build.proj file I am using (obviously some contents altered)
Can anyone help?
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets"/>
<Import Project="$(MSBuildExtensionsPath)\Microsoft\AssemblyInfoTask\Microsoft.VersionNumber.targets"/>
<PropertyGroup>
<Major>1</Major>
<Minor>0</Minor>
<Build>0</Build>
<Revision>0</Revision>
</PropertyGroup>
<PropertyGroup>
<BuildDir>C:\svn\Infrastructure</BuildDir>
</PropertyGroup>
<ItemGroup>
<SolutionsToBuild Include="Infrastructure.sln"/>
</ItemGroup>
<Target Name="Build" DependsOnTargets="ChangeDataAccessAssemblyInfo">
<RemoveDir Directories="$(BuildDir)\Builds" Condition="Exists('$(BuildDir)\Builds')" />
<MSBuild Projects="#(SolutionsToBuild)" Properties="Configuration=Debug" Targets="Rebuild" />
</Target>
<ItemGroup>
<TestAssemblies Include="Build\Logging\Logging.UnitTests.dll" />
</ItemGroup>
<!--<UsingTask TaskName="NUnit" AssemblyFile="$(teamcity_dotnet_nunitlauncher_msbuild_task)" />
<Target Name="Test" DependsOnTargets="Build">
<NUnit NUnitVersion="NUnit-2.4.6" Assemblies="#(TestAssemblies)" />
</Target>-->
<Target Name="ChangeDataAccessAssemblyInfo" >
<Message Text="Writing ChangeDataAccessAssemblyInfo file for 1"/>
<Message Text="Will update $(BuildDir)\DataAccess\My Project\AssemblyInfo.vb" />
<AssemblyInfo CodeLanguage="VB"
OutputFile="$(BuildDir)\DataAccess\My Project\AssemblyInfo_new.vb"
AssemblyTitle="Data Access Layer"
AssemblyDescription="Message1"
AssemblyCompany="http://somewebiste"
AssemblyProduct="the project"
AssemblyCopyright="Copyright notice"
ComVisible="true"
CLSCompliant="true"
Guid="hjhjhkoi-9898989"
AssemblyVersion="$(Major).$(Minor).1.1"
AssemblyFileVersion="$(Major).$(Minor).5.7"
Condition="$(Revision) != '0' "
ContinueOnError="false" />
<Message Text="Updated Assembly File Info"
ContinueOnError="false"/>
</Target>
</Project>
I think you are missing the specification of the AssemblyInfoFiles attribute on your AssemblyInfo task. Here's how it looks on a project I'm working on...
<Target Name="AfterGet">
<Message Text="In After Get"/>
<CreateItem Include="$(SolutionRoot)\Source\SomeProject\My Project\AssemblyInfo.vb">
<Output ItemName="AssemblyInfoFiles" TaskParameter="Include"/>
</CreateItem>
<Attrib Files="#(AssemblyInfoFiles)"
ReadOnly="false"/>
<AssemblyInfo AssemblyInfoFiles="#(AssemblyInfoFiles)"
AssemblyDescription="$(LabelName)">
</AssemblyInfo>
</Target>
What we're doing is first using to create a property that contains the name of the file we'll be updating. We have to do this via createItem because when we start the build the file doesn't exist (and that is when MSBuild evaluates the and definitions in your build file.
We then take the readonly bit off the file.
Finally we invoke the AssemblyInfo task passing it the file(s) to update and a custom assembly name that we want to give it (in this case we put the TFS build label into the Assembly Description field so that we can easily tell which team build the assembly came from.