How to use MSBuild to build multiple platforms - msbuild

I am using Visual Studio online build with an MSBuild task. I currently have the following MSBuild Arguments fed to my task:
/p:Configuration=Release;AppxBundle=Always;AppxBundlePlatforms="x86|x64|ARM";AppxPackageDir="$(Build.BinariesDirectory)\AppxPackages\\";UapAppxPackageBuildMode=StoreUpload
This creates my application in x86, x64 and ARM. It creates Release version of the libraries in x86 BUT creates Debug version of my libraries in x64 and ARM.
When my .appxupload package is creates it fails Windows Certification tests because my libraries are built in debug.
How can I make MSBuild build for all 3 configurations. My guess is because I haven't provided a /platform configuration. How do I provide this configuration for 3 platforms?
I have tried platform="x86|x64|ARM" but it returned an error

For a standard project file there's no way to do this in a single command. Either use multiple commands to build the project for each platform/configuration combination needed, or use a 'master' build file which does the same for you, something like:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="FullRebuild">
<Target>
<ItemGroup>
<Configurations Include="Debug;Release"/>
<Platforms Include="x86;x64;ARM"/>
<ConfigAndPlatform Include="#(Configurations)">
<Platform>%(Platforms.Identity)</Platform>
</ConfigAndPlatform>
</ItemGroup>
<MSBuild Projects="myproject.sln" Targets="Build"
Properties="Configuration=%(ConfigAndPlatform.Identity);Platform=%(ConfigAndPlatform.Platform)"/>
</Target>
</Project>

Related

Inexplicably cleared msbuild properties in TeamCity build

I'm trying to create a "tools NuGet package" that provides a tool and setting that is unpacked during build and used by a later TeamCity build step.
The NuGet package contains the following content in its build\MyPackageId.props file:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<MyTool1>$(MSBuildThisFileDirectory)..\tools\MyTool.exe</MyTool1>
</PropertyGroup>
<Target Name="ReportMyToolToTeamCity" BeforeTargets="PrepareToRun">
<PropertyGroup>
<MyTool2>$(MSBuildThisFileDirectory)..\tools\MyTool.exe</MyTool2>
</PropertyGroup>
<Message Text="MyTool1 = $(MyTool1)" />
<Message Text="MyTool2 = $(MyTool2)" />
</Target>
</Project>
(The messages will eventually set a TeamCity property, but this is sufficient to demonstrate the issue.)
Because it's a props file, after installing the NuGet package into a C# project it has added an import as the very first thing, above the import of Microsoft.Common.props. I want a props file rather than a targets file so that the property values are also available to other project settings and targets files.
When I compile this inside Visual Studio 2015, I see both MyTool1 and MyTool2 paths set to the same (correct) path as expected.
When I compile this from TeamCity (2017.2.2, using the Visual Studio (sln) runner), according to the output the MyTool1 property is empty and only MyTool2 shows the correct value.
Why?

Referencing macros on msbuild (12.0) command line property assignment

I am curious, is it possible to reference a macro on a command line property assignment for MSBuild?
E.g:
msbuild.exe MySolution.sln /p:CustomBeforeMicrosoftCSharpTargets="$(SolutionDir)\custom.targets"
Would this also work when specified as "MSBuildArguments" from an "Edit Build Definition"/"Queue New Build" from Visual Studio connected to TFS?
E.g:
/p:CustomBeforeMicrosoftCSharpTargets="$(SolutionDir)\custom.targets"
Because it doesn't appear to be importing these targets for me. But the targets file is definitely there, alongside the solution, in the build workspace.
I don't want to have to specify an absolute path. Not sure how working with relative paths is meant to work here, can't find any advice on the internet, and debugging it is quite difficult, as it is called on a build agent using a workflow. The workflow logging is definitely reporting it is calling MSBuild with these arguments, but nowhere in the verbose logging output can I see it is making reference to the CustomBeforeMicrosoftCSharpTargets target, or calling it.
EDIT
I wrote a little test build project buildme.proj to further my understanding.
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<SetMe>NotInTheSandbox</SetMe>
</PropertyGroup>
<PropertyGroup>
<SomeMacroValue>c:\Sandbox\BuildTest</SomeMacroValue>
</PropertyGroup>
<PropertyGroup>
<AlreadySet>$(SomeMacroValue)\InTheSandbox</AlreadySet>
</PropertyGroup>
<Target Name="Build">
<Message Text="I am building!" />
<Message Text="Some macro value: $(SomeMacroValue)" />
<Message Text="$(SetMe)" />
<Message Text="$(AlreadySet)" />
</Target>
</Project>
When I execute with the command:
msbuild buildme.proj /p:SetMe="$(SomeMacroValue)\StillNotInSandbox"
I get the following output:
Microsoft (R) Build Engine version 12.0.31101.0
[Microsoft .NET Framework, version 4.0.30319.42000]
Copyright (C) Microsoft Corporation. All rights reserved.
Build started 10/12/2015 22:12:08.
Project "C:\Sandbox\BuildTest\buildme.proj" on node 1 (default targets).
Build:
I am building!
Some macro value: c:\Sandbox\BuildTest
$(SomeMacroValue)\StillNotInSandbox
c:\Sandbox\BuildTest\InTheSandbox
Done Building Project "C:\Sandbox\BuildTest\buildme.proj" (default targets).
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:00:00.02
So clearly, it is not behaving how I expected: The macro identifier appears in the output message text.
Is there a solution to this?
A "macro" like $(SolutionDir) exists only in VisualStudio and VS passes the value to MSBuild.
Instead MSBuild makes Environment variables available as properties, so a batch file like this
set SomeMacroValue=foo
msbuild buildme.proj /p:SetMe="$(SomeMacroValue)\StillNotInSandbox"
is probably what you are looking for.
And you can set environment variables per-user or per-machine (Control Panel\All Control Panel Items\System Advanced System Settings, Environment variables).

Why is MSBuild trying to run the projects it's building?

I'm currently writing an msbuild script to build a solution I've been working on, as well as run its tests. On my development machine, this works as expected. However, when I try to run the same build script on our build server, I get several failures. I've tracked the source of the problem down to the fact that my build script appears to be trying to run the .exe file associated with my application. This line during the script execution tipped me off, since it doesn't run that command on my dev box:
MSIAuthoring:
Building MSI
"C:\Program Files (x86)\Jenkins\workspace\Test Build\BuildArtifacts\MsiBuildTool.exe" "/MBSBUILD:MsiBuildTool"
I'm fairly new to build scripting, but my understanding is that the build script shouldn't be trying to run my program unless I explicitly tell it to do so. Does anyone know what might be causing this?
For reference, here is my build script:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="RunTests"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<BuildArtifactsDir Include="BuildArtifacts\"/>
<SolutionFile Include="MsiBuildTool.sln"/>
<NUnitConsole Include="C:\Program Files (x86)\NUnit 2.6.4\bin\nunit-console.exe"/>
<UnitTestsDll Include="BuildArtifacts\MsiBuildToolUnitTests.dll"/>
<TestResultsPath Include="BuildArtifacts\TestResults.xml"/>
</ItemGroup>
<PropertyGroup>
<Configuration Condition="'$(Configuration)' == ''">Release</Configuration>
<Platform Condition="'$(Platform)' == ''">Any CPU</Platform>
</PropertyGroup>
<Target Name="Init" DependsOnTargets="Clean">
<MakeDir Directories="#(BuildArtifactsDir)"/>
</Target>
<Target Name="Clean">
<RemoveDir Directories="#(BuildArtifactsDir)"/>
</Target>
<Target Name ="Compile" DependsOnTargets="Init">
<MSBuild Projects="#(SolutionFile)"
Targets ="Build"
Properties ="OutDir=%(BuildArtifactsDir.FullPath);Configuration=$(Configuration);Platform=$(Platform)"/>
</Target>
<Target Name="RunTests" DependsOnTargets="Compile">
<Exec Command='"#(NUnitConsole)" #(UnitTestsDll) /xml=#(TestResultsPath)'/>
</Target>
</Project>
Update:
After some digging through the output, I found that "MSIAuthoring" step was the result of the Wix# library that I'm using. As described by this thread: https://wixsharp.codeplex.com/discussions/644609#
I disabled the MSIAuthoring step by removing this line in my .csproj files:
<Import Project="..\packages\WixSharp.1.0.22.3\build\WixSharp.targets" Condition="Exists('..\packages\WixSharp.1.0.22.3\build\WixSharp.targets')" />
You're building solution file, thus MSBuild will generate msbuild-xml script first and then will build it. To find why it's being called on build machine but not on your dev machine - follow this advice and obtain generated MSBuild scripts from your dev environment and your build server. Then compare it.
Also enable diagnostic logging (/verbosity:diag in the command line) as Lex Li advised, and you'll see detailed decisions why each target being run or not - grep logs for something like "Conditions A, B, C on target BuildMSI evaluated to False" and this will show you the difference between environments.
It might be some type of post-build script on one of the projects which builds MSI only if it's being run not on dev environment - check actual build script to find where it comes from. Also check that it's really related to your build script, and it's not an extra build step in your Jenkins build configuration.

TFS 2010 build Server Wix. Output only MSI

I've managed to get builds working on my build server, but now the issue I have is that the output in the output folder contains all output from all projects, rather than just the output from the wix project(s).
Any idea how to change this?
TIA
If you are only interested in the msi as a build output then you could create a step in your team build to copy your installer files to another location, the following build target, added to your build project should help.
This overrides the target AfterDropBuild
<Target Name="AfterDropBuild">
<PropertyGroup>
<InstallerDir>$(DropLocation)\$(BuildNumber)\Installers</InstallerDir>
</PropertyGroup>
<Message Importance="low" Text="InstallerDir=$(InstallerDir)" />
<MakeDir Directories="$(InstallerDir)" Condition="!Exists('$(InstallerDir)')" />
<CreateItem Include="$(BinariesRoot)\**\*.msi">
<Output TaskParameter="Include" ItemName="InstallationFiles"/>
</CreateItem>
<Copy SourceFiles="#(InstallationFiles)"
DestinationFolder="$(InstallerDir)"/>
</Target>
I create different solution platforms { Application, Setup } and set my .NET projects to build with application and my wix to build with setup. Then I tell the build definition to build those two platforms in that order. The result is that TFS archives the .NET code in an application folder that looks like the deployed machine and the MSI in the setup folder.
Only downside is when you add new projects you have to select the platform to build in configuration manager. My developers don't seem annoyed by it though.

Specifying assembly version of all projects within a web deployment wdproj script

I have a .wdproj Web Deployment Project created with VS2010 that contains references to other class libraries, like this:
<Project ToolsVersion="4.0" DefaultTargets="Build"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ProjectReference Include="..\Path\Proj1.csproj">
<Project>{GUID-HERE}</Project>
<Name>Proj1</Name>
</ProjectReference>
<ProjectReference Include="..\Path\Proj2.csproj">
<Project>{GUID-HERE}</Project>
<Name>Proj2</Name>
</ProjectReference>
There are lots of these. I want to be able to run msbuild /t:Rebuild /p:Configuration=Release and have all the assemblies of all the included projects compiled at a specified version. Nothing fancy just static like 2.5.6.0 and specified once in the wdproj file. I dont want to open 30 files manually.
I have looked at MSBuild Community Task and MSBuildExtension Pack and can not get anything to work. The build runs ok without errors.
Anyone have an example of how this can be done?
This is an attempt with MSBuild Extensions (adapted from the sample included) that doesn't work:
<Import Project="$(MSBuildExtensionsPath)\ExtensionPack\4.0\MSBuild.ExtensionPack.VersionNumber.targets"/>
<Target Name="Build">
<MSBuild.ExtensionPack.Framework.AssemblyInfo
ComVisible="true"
AssemblyInfoFiles="VersionInfo.cs"
AssemblyFileMajorVersion="2"
AssemblyFileMinorVersion="5"
AssemblyFileBuildNumber="6"
AssemblyFileRevision="0"
/>
</Target>
MSBuild is definately looking at the MSBuild.ExtensionPack.Framework.AssemblyInfo element because if the attribute names are incorrect the build will fail. This builds ok but none of the versions on the referenced assemblies are changed. The version numbers on the ASP.NET page assemblies from the website are all 0.0.0.0.
Are you maybe missing to specify the CodeLanguage and OutputFile attributes?
I think the AssemblyInfo task is intended to generate (replace) a source file prior to compiling.