Code sign assemblies before Setup gets created - msbuild

I try to update our build-process so that it code sign all the assemblies and installers:
1) Build all the projects
2) Sign their outputs
3) Build installers (msi, with VS2010 Setup Projects)
4) Sign installers
The problem I have is, that in Step 3, the Installer-Project takes the primary output out of the obj-folder. But in step 2 I take the outputs of the MSBuild-Task, and they are in the bin folder. So I have an unsigned primary output in my msi.
The MSBuild-call:
<MSBuild Projects="%(SolutionFiles.FullPath)" ContinueOnError ="false" Properties="Configuration=$(BuildConfiguration)" Targets="Build">
<Output TaskParameter="TargetOutputs" ItemName="AssembliesBuildByChildProjects"/>
</MSBuild>
The signing works with a cmd-File:
<Exec
Command=""$(SignPath)signcode.cmd" "%(AssembliesBuildByChildProjects.Fullpath)" /t"
WorkingDirectory="$(SignPath)" />
Thanks

I found a solution for this, not nice - but could be worst :-)
I just sign all the output-files located in the obj-Folder also:
<ItemGroup>
<PrimaryOut
Include="#(AssembliesBuildByChildProjects->'..\**\obj\**\%(FileName).%(Extension)')" />
</ItemGroup>
<Exec
Command=""$(SignPath)signcode.cmd" "%(PrimaryOut.Fullpath)" /t"
WorkingDirectory="$(SignPath)" />
<Exec
Command=""$(SignPath)signcode.cmd" "%(AssembliesBuildByChildProjects.Fullpath)" /t"
WorkingDirectory="$(SignPath)" />

Related

MSBuild - Projects with *name to output folder

I'm trying to automate the process of some projects output folder, at the moment I'm writing those lines for each project that my website uses:
<Target Name="BuildPlugin" BeforeTargets="Build">
<MSBuild Projects="..\Module.Products\Module.Products.csproj" Targets="Publish" Properties="Configuration=$(Configuration);PublishDir=$(OutputPath)/plugins/Module.Products" />
<MSBuild Projects="..\Module.Sales\Module.Sales.csproj" Targets="Publish" Properties="Configuration=$(Configuration);PublishDir=$(OutputPath)/plugins/Module.Sales" />
</Target>
Is there any way I can automate that to search for Module.* and use in all those places where the name repeats? This is a standalone project.
Yes! Just make Items and then iterate over them while leveraging their metadata (https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-well-known-item-metadata). Something like this (apologies for any mistakes – typing this on a phone).
<Target Name="BuildPlugin" BeforeTargets="Build">
<ItemGroup>
<ProjectFiles Include="..\*\*.csproj" />
</ItemGroup>
<MSBuild Projects="%(ProjectFiles.Identity)" Targets="Publish" Properties="Configuration=$(Configuration);PublishDir=$(OutputPath)/plugins/%(ProjectFiles.Filename)" />
</Target>

What happened to BeforeBuild and other targets in VS2012?

I'm trying to get some pre-build steps to work in a C++ project in Visual Studio 2012 but they do not get invoked (while I'm pretty sure the same techniques were OK in Visual Studio 2010). Command line builds behave exactly the same.
This is the end of the project file; the file was generated using Visual Studio and then I just added the last couple of lines:
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Target Name="BeforeBuild">
<Message Text="### BeforeBuild ###" />
</Target>
<Target Name="BeforeCompile">
<Message Text="### BeforeCompile ###" />
</Target>
<Target Name="AfterBuild">
<Message Text="### AfterBuild ###" />
</Target>
and here's the output:
Project "d:\temp\temp.vcxproj" on node 1 (default targets).
InitializeBuildStatus:
Creating "Debug\temp.unsuccessfulbuild" because "AlwaysCreate" was specified.
AfterBuild:
AfterBuild
FinalizeBuildStatus:
Deleting file "Debug\temp.unsuccessfulbuild".
Touching "Debug\temp.lastbuildstate".
So only AfterBuild is considered and the others are ignored.
Looking into this I found this PropertyGroup in Microsoft.BuildSteps.targets:
<BuildDependsOn>
_PrepareForBuild;
$(BuildSteps);
AfterBuild;
FinalizeBuildStatus;
</BuildDependsOn>
Shouldn't this also have BeforeBuild and the BuildEvent targets? Or is something wrong with my MSBuild install causing it to use this BuildSteps.targets file instead of something else?
Solution
As Alexey points out, using Before/AfterTarget provides a usable workaround. You just have to take care of which targets to use, but this is easy by looking at the BuildSteps file. This seems to work fine for now:
<Target Name="BeforeBuild" BeforeTargets="PrepareForBuild">
<Message Text="### BeforeBuild ###" />
</Target>
<Target Name="BeforeCompile" BeforeTargets="BuildCompile">
<Message Text="### BeforeCompile ###" />
</Target>
<Target Name="AfterBuild" AfterTargets="Build">
<Message Text="### AfterBuild ###" />
</Target>
I have same msbuild targets as you described, so I think your msbuild installation is fine.
Looks like they decide to make some cleanup to targets and dependencies ( not sure if this issue related to VS version, VS just using same targets, provided by msbuild). BeforeBuild and other targets still exists in Microsoft.common.targets. I suppose it just reserved for .NET projects (I never played with C++ ones, so I don't know, how to build a pipeline there).
Anyway whether it works or not on previous versions, your problem can be solved much easier - just use new attributes BeforeTargets\AfterTargets for MSBuild 4.0 and hook your targets directly on whatever you want:
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Target Name="BeforeBuild" BeforeTargets="Build">
<Message Text="### BeforeBuild ###" Importance="high" />
</Target>
<Target Name="BeforeCompile" BeforeTargets="Compile">
<Message Text="### BeforeCompile ###" Importance="high" />
</Target>
<Target Name="AfterBuild" AfterTargets="Build">
<Message Text="### AfterBuild ###" Importance="high" />
</Target>

Get NuGet package folder in MSBuild

I want to call executable tools like NUnit which I manage via NuGet in MSBuild:
<Target Name="Test">
<CreateItem Include="$(BuildCompileDirectory)\*.Tests.*dll">
<Output TaskParameter="Include" ItemName="TestAssemblies" />
</CreateItem>
<NUnit
Assemblies="#(TestAssemblies)"
ToolPath="$(PackagesDirectory)\NUnit.2.5.10.11092\tools"
WorkingDirectory="$(BuildCompileDirectory)"
OutputXmlFile="$(BuildDirectory)\$(SolutionName).Tests.xml" />
</Target>
The problem is that the folder of a NuGet packages is containing the version number of the package. For instance nunit-console.exe is in the folder packages\NUnit.2.5.10.11092\tools. If I update the NUnit package this path will change and I have to update my MSBuild script. That isn't acceptable.
MSBuild doesn't allow Wildcards in directories, so this isn't working:
ToolPath="$(PackagesDirectory)\NUnit.*\tools"
How can I call tools in MSBuild without having to update my build script whenever I update a NuGet package?
You can use MSBuild Transforms to get the relative directory of a specific tool:
<ItemGroup>
<NunitPackage Include="$(PackagesDirectory)\NUnit.*\tools\nunit-console.exe"/>
</ItemGroup>
<Target Name="Test">
<CreateItem Include="$(BuildCompileDirectory)\*.Tests.*dll">
<Output TaskParameter="Include" ItemName="TestAssemblies" />
</CreateItem>
<NUnit
Assemblies="#(TestAssemblies)"
ToolPath="#(NunitPackage->'%(relativedir)')"
WorkingDirectory="$(BuildCompileDirectory)"
OutputXmlFile="$(BuildDirectory)\$(SolutionName).Tests.xml" />
</Target>
The comment of Mike Rosoft links to Patrik Svensson his blog post and it helped me as follows:
Add GeneratePathProperty="true" to PackageReference of the NuGet package you want to have the location of.
Use it as $(PkgPackage_Name) whereby the dots are replaced by an underscore. Note the Pkg prefix.
This example forces Nswag to use the 32 bit dotnet.exe by overwriting the existing NSwagExe_Net60 property. This was necessary on an x86 project.
<PropertyGroup>
<NSwagExe_Net60>"$(MSBuildProgramFiles32)\dotnet\dotnet.exe" "$(PkgNSwag_MSBuild)\tools\Net60\dotnet-nswag.dll"</NSwagExe_Net60>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NSwag.MSBuild" Version="13.16.1" GeneratePathProperty="true">
</ItemGroup>

msbuild output remove assemblies

I have some msbuild code that looks something like this:
<Target Name="Build">
<MSBuild
Projects="#(UnitTestProject)"
Properties="$(BuildProperties)">
<Output TaskParameter="TargetOutputs" ItemName="TestAssembly" />
</MSBuild>
</Target>
<Target Name="Test" DependsOnTargets="Build">
<ItemGroup>
<TestAssembly Remove="*.Example.dll" />
</ItemGroup>
<xunit Assemblies="#(TestAssembly)" />
</Target>
So I am building all of my unit test projects and caputuring the built dll's using the Output task on the TargetOutputs parameter. The problem is that one of the projects is calling a task that outputs some dll's that I don't want to actually run xunit against.
What's weird though is that the Remove="*.Example.dll" appears to not have any affect at all and xunit is trying to test the assembly anyway.
Why is Remove not working?
Actually I think I figured it out. It appears that the problem resides in the way the relative path is resolved in ItemGroups in the Target vs. outside of a Target. I need to be a little more explicit with my path and then it works. Basically I did this to get it to work:
<Target Name="Build">
<MSBuild
Projects="#(UnitTestProject)"
Properties="$(BuildProperties)">
<Output TaskParameter="TargetOutputs" ItemName="UnitTestOutput" />
</MSBuild>
<ItemGroup>
<TestAssembly Include="#(UnitTestOutput)" Exclude="$(RootTestPath)\**\*.Example.dll" />
</Target>
<Target Name="Test" DependsOnTargets="Build">
<xunit Assemblies="#(TestAssembly)" />
</Target>

Teamcity build loops on successful build

I have set up a build with Teamcity. See my build file below.
When the build is succesful and the tests pass, the build process just runs again and again indefinitely in a loop.
When the build fails, this does not happen.
I have tried to first set 60 second pause on buildtriggering, and finally disabled build triggering altogether. No difference.
What else could be the cause of this?
My MSBuild file looks like this:
<Project DefaultTargets="Build;Test" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
<PropertyGroup>
<DeployDirectory>$(MSBuildProjectDirectory)\..\bin</DeployDirectory>
<DependencyDirectory>$(MSBuildProjectDirectory)\Dependencies</DependencyDirectory>
<LinqToSqlMapFolder>$(DeployDirectory)\LinqToSql</LinqToSqlMapFolder>
<NCoverVersionForMSI>$(BUILD_NUMBER)</NCoverVersionForMSI>
<NCoverVersionPeriod>$(BUILD_NUMBER)</NCoverVersionPeriod>
</PropertyGroup>
<ItemGroup>
<ProjectFiles Include="**\*.vbproj"/>
<ConfigFiles Include="**\*.config"/>
<MapFiles Include="**\*.linqtosql.config"/>
<TestAssemblies Include="$(DeployDirectory)\*.Test.dll"/>
<Dependencies Include="$(DependencyDirectory)\**\*" />
</ItemGroup>
<Target Name="Clean">
<MSBuild Projects="#(ProjectFiles)" Targets="Clean"/>
</Target>
<Target Name="Build">
<MSBuild Projects="#(ProjectFiles)" Targets="Rebuild">
<Output TaskParameter="TargetOutputs" ItemName="BuildOutput"/>
</MSBuild>
<Copy SourceFiles="#(BuildOutput)" DestinationFolder="$(DeployDirectory)" />
<Copy SourceFiles="#(Dependencies)" DestinationFolder="$(DeployDirectory)" SkipUnchangedFiles="true" />
<Copy SourceFiles="#(ConfigFiles)" DestinationFolder="$(DeployDirectory)" SkipUnchangedFiles="true" />
<Copy SourceFiles="#(MapFiles)" DestinationFolder="$(LinqToSqlMapFolder)" SkipUnchangedFiles="true" />
</Target>
<UsingTask AssemblyFile="$(DependencyDirectory)\Gallio\Gallio.MsBuildTasks.dll" TaskName="Gallio" />
<Target Name="Test">
<Gallio IgnoreFailures="true" Files="#(TestAssemblies)">
<Output TaskParameter="ExitCode" PropertyName="ExitCode"/>
</Gallio>
</Target>
</Project>
While it appears that this wasn't your issue, I ran into a similar looping problem of my own. I had enabled labeling in the project configuration. I was also using a check for modifications every 60 seconds rule to trigger the build. As a result, upon successful build, TeamCity would tag the build in the VCS and then 60 seconds later, it would see (it's own) modification and trigger another build.
To fix our problem, we just disabled labeling because we didn't want it anyways, but you can also configure a rule to ignore particular authors such that it won't trigger on modifications it made.
It appears there were some problems with the install of teamcity, and a backup of configuration following a reinstall solved the problem with exactly same configuration and buildscript.