Expose MSBuild property in Cake script after build step - msbuild

Can you get property set by MSBuild inside your cake script?
I currently have a target that runs after compilation to indicate whether it has run, or whether it was an incremental build.
I want to detect in the remainder of my cake build whether incremental build took place.
The target that I currently use on my MSBuild is as follows:
<!-- Defines Targets that should be run after Compile, but skipped if Compile doesn't take place -->
<PropertyGroup>
<TargetsTriggeredByCompilation>
$(TargetsTriggeredByCompilation);
EnablePostBuild
</TargetsTriggeredByCompilation>
</PropertyGroup>
<Target Name="EnablePostBuild">
<!-- Disable post build actions -->
<PropertyGroup>
<SkipPostBuildActions>false</SkipPostBuildActions>
</PropertyGroup>
</Target>
If I trigger the build in Cake as follows:
var buildSettings = new MSBuildSettings()
.WithProperty("SkipPostBuildActions", "true")
MSBuild("./src/Application.sln",buildSettings );
var SkipPostBuildActionsVal = buildSettings??
Can I get the value of SkipPostBuildActions after the MSBuild step?

This is actually not much of a Cake problem: Cake "only" runs msbuild using the given parameters.
So, if you find a way how to access a Property from outside msbuild you can transfer that solution to Cake.
AFAIK msbuild does not even support easy sharing of property-modifications between tasks, let alone outside the msbuild-process.
I see two possible solutions:
Output the value of SkipPostBuildActions into the log, using the Message-Task then set a FileLogger on your msbuild-call and parse the log-file afterwards.
Output the value of SkipPostBuildActions to a dedicated file using the WriteLinesToFile-Task then parse that file after msbuild has run.
Personally I'd chose the latter option.

Related

Disable predefined targets (like CoreBuild and CorePublish) if property is set

I have a project structure with several dirs.proj and cs/ccproj files.
When developers and certain builds run, I don't want to generate cloud service .cspkg files (takes too long time when we don't need them). What our build infrastructure team have set up is an extra property "BuildCloudPackages"=='true', which I can use to only build the cloud packages when needed.
I want to stop CorePublish and Build unless BuildCloudPackages is set to true for all ccproj's in a dirs.proj.
Is there a way to retrofit conditions to existing targets? Or Conditionally including a ProjectFile in a dirs.proj. Or conditionally redefine targets inside a Choose/When? Or stop build/publish by disabling some of their preconditions? Or remove Build and CorePublish from default targets on the condition?
I worked around it using conditional import, where one disabled the tasks I didn't want to run, and one adds what I need only when I ask for it.
<Import Project="..\..\..\targets\DisableBuild.targets"
Condition="'$(BuildCloudPackages)'!='true'" />
<Import Project="..\..\..\targets\CustomTasks.targets"
Condition="'$(BuildCloudPackages)'=='true'" />
Where the disable file is something like this:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="CoreBuild" />
<Target Name="CorePublish" />
</Project>

How can I know which properties are applicable for MSBuild?

From here, I get to know how to create a Web Deployment Package with MSBuild. The command line looks like this:
MSBuild "MyProjectName.csproj" /T:Package /P:Configuration=Staging;PackageLocation="D:\Vishal\Package.zip"
The Configuration, PackageLocation are both properties.
I just wonder how can I know which properties are applicable? And their formal definitions?
I searched the MSBuild Reserved and Well-Known Properties, but they are not there.
And I searched the MSBuild Task, still no luck.
ADD
It seems different project types have their specific properties. For example, the PackageLocation property should be specific to a Web Application project. What I am looking for is the specific definition of these properties.
ADD 2
I have a MSBuild task as below.
> <MSBuild Targets="Clean; Package"
> Projects="$(XXXSolutionDirectory)\Web\Web.csproj"
> Properties="Configuration=$(Configuration); Platform=$(Platform);
> OutputPath=$(BinDirectory)\Web_Deployment_Package;
> PackageLocation=$(BinDirectory)\Web_Deployment_Package;
> PublishDir=$(BinDirectory); OutDir=$(BinDirectory);
> IntDir=$(IntDirectory); TfsBuild=$(TfsBuild);
> CscToolPath=$(CscToolPath); CscToolExe=$(CscToolExe);
> VbcToolPath=$(VbcToolPath); VbcToolExe=$(VbcToolExe);
> TargetProfile=$(XXXConfiguration)"></MSBuild>
The properties such as PackageLocation are placed within the Properties attribute of MSBuild task. Rather than in a PropertyGroup definition. And this is the only place it shows up in the build proj file. So where can I find its definition to understand its intended usage?
Ok, let's start first with the bit of fundamental processing pipeline of MSBuild.
When msbuild engine parses your proj file - it takes all
c:\Windows\Microsoft.NET\Framework64\v4.0.30319\Microsoft.CSharp.targets
around line 379 you can see
<Import Project="Microsoft.Common.targets" />
this means - in the final big script - instead of line 379 you'll see the content of
c:\Windows\Microsoft.NET\Framework64\v4.0.30319\Microsoft.Common.targets
then in the next phase - msbuild will calculate all Global Properties and Items values.
It has some rules on which value (because you may have multiple property declarations with different values which will end up in a property with single value - usually last one or global one wins) will be assigned to property, but I'll omit these details here.
And the properties you specified in msbuild command line also participating in this process.
Particular property declaration can be in more than one .targets file but that really doesn't matter. What's matter is - what value this property will have at the end of global property processing phase.
But if you really want to know where is particular property is defined - you need to manually search through all imported .targets files and find property declaration tag.
Targets files can be in your .NET Fw folder or in installed SDKs (if you have specific project types like Azure .ccproj ones)
For example - let's take most popular property "Configuration".
I sought in all .targets files text <Configuration and Microsoft.Common.targets line 132 has this entry:
<Configuration Condition=" '$(Configuration)'=='' ">Debug</Configuration>
As you can see - there is a condition which is saying - set configuration to Debug if it's not yet defined.
Because you specified this property's value as a command line parameter - your property will have higher priority and will cause this condition to be false, thus your value will be the final value of this particular property.
Same for PublishDir - Microsoft.Common.targets line 425:
<!-- Output location for publish target. -->
<PropertyGroup>
<PublishDir Condition="'$(PublishDir)' != '' and !HasTrailingSlash('$(PublishDir)')">$(PublishDir)\</PublishDir>
<PublishDir Condition="'$(PublishDir)'==''">$(OutputPath)app.publish\</PublishDir>
</PropertyGroup>
and so on.
Some properties (especially for custom project types ) can be defined in it's own SDK .targets files, but if you'll open that custom .zzProj file and may find project properties there OR you can manually follow all <import directives - and eventually will find where every specific property is defined. And usually along with definition - you can trace how this property is being used by targets (search for $(MyPropertyName) ) and tasks, thus - alter it for your own needs or spot bugs or weird usages.
Hope this helps you.
First pre-process the MsBuild script to flatten and consolidate all imported scripts into a single MsBuild file.
MsBuild.exe MyProject.proj /pp >Output.xml
Now, open Output.xml in Notepad and search for instances of $(Configuration) and $(PackageLocation).
$(Configuration) is one of the base default properties you'd find in most MsBuild projects and you'd see it used in Microsoft.Common.CurrentVersion.Targets in targets that are designed to fail the build if an invalid or unexpected Platform or Configuration property is used.
PackageLocation is specific-to ASP.NET projects and the import taxonomy would include Microsoft.Web.Publishing.targets which contains several targets dedicated to parsing and validating that property as part of the web publication workflow.

TFS 2010: Perform different builds and command line task in sequence?

my build process with TFS 2010 should perform different task one after the other like:
Build 1st project in solution
Execute MSBuild via command line (to publish the project)
Execute a 3rd party tool via command line (to obfuscate the binaries)
Build a 2nd project in the solution (an InstallShield project)
How can I achieve this? I can define several project in the Build Definition but how can I invoke several command line task between these build steps? And the MSBuildArguments in the Build Definition: Are these arguments for every msbuild call for each project/solution?
Thanks
Konrad
At first, you need to add in your build definition the distinct *.*proj instead of one big *.sln - or (even better) construct more than one *.sln & order them to get build in the build definition. So you could organize a Project1.sln, Project2.sln etc that are only used from the Build.
In addition to that, you would have to make changes in the build process template to get this.By default you get something like that, that executes each set project/solution within a bigger foreach:
A good way would be to enhance this as a sequence, where all your custom action are set as InvokeProcess activities:
Obviously, you would have to insert here a flow control, so that Publish & Dotfuscator execute the first time (where Project1.sln gets build), while ISDEV executes the second time (where Project2.sln gets build). In the sample below I used a switch & packed Publish & Dotfuscator in a new Sequence.
Finally, you would have to have a counter of some sort. The most immediate option is to set a new Int32 Variable with default == 1 and increase it by hand during execution. In the sample below this is done in the lower Assign:
This final override of Complie the Project, along with a changed Build Definition should get what you 're after.
The team build definition takes a list of sln's and msbuild project files. You can put simply split your InstallShield project out into it's own solution ( most developers won't have a copy of InstallShield anyways likely ) and write an msbuild targets file for steps 2 and 3. Then just tell your build definition to build solution 1, the targets file and solution 2.
You could also choose to put the stuff in the targets file in a postbuild event for one of the projects in solution 1.
I wouldn't do this in workflow.

MSBuild extensionpack Copy all the contents of a Directory to another

I am using MSBuild extensionpack. I'd like to copy the entire contents of the build directory to another directory on the file system. I do not want to rename the destination directory, just replace the contents. It could be my unfamiliarity with msbuild extensionpack but it seems like this should be easy and I have been unable to find readily available documentation on the web.
I am trying to set up a service that is automatically deployed in the Continuous Integration environment after a successful build.
As far as I remember, you'll need to clear and copy in separate steps. So do the delete/purge first, then copy over. I wasn't able (at the time I last did) to find a way to "overwrite". This actually worked better for us b/c one build may remove files that a previous one contained, so we wouldn't want them to "linger".
To delete, try (assuming DeploymentDesintationPath is a property with the path):
<MSBuild.ExtensionPack.FileSystem.Folder
TaskAction="RemoveContent"
path="$(DeploymentDestinationPath)" />
And then copy (notice you need to populate an itemgroup for both the source and the destination)
<ItemGroup>
<DeploymentSourceFiles
Include="$(BuildFolder)\**\*"
/>
<DeploymentDestinationFiles
Include="#(DeploymentSourceFiles->
'$(DeploymentDestinationPath)\%(RecursiveDir)%(Filename)%(Extension)')"
/>
</ItemGroup>
<Copy SourceFiles="#(DeploymentSourceFiles)"
DestinationFiles="#(DeploymentDestinationFiles)" />
I haven't done this in a few months, so pardon if any of these examples require a bit of tweaking.

MsBuild - Is it possible to isolate batch tasks, so one failing task doesn't skip the remaining tasks?

Let me start by example... I have an all.proj that looks similar to this:
<ItemGroup>
<ProjectsToBuild Include="..\Sites\*\*.csproj" />
</ItemGroup>
<Target Name="DeployWebsites" DependsOnTargets="BuildMergedSolutions">
<AspNetCompiler
PhysicalPath="%(ProjectsToBuild.RootDir)%(ProjectsToBuild.Directory)"
TargetPath="%(ProjectsToBuild.RootDir)%(ProjectsToBuild.Directory)..\..\..\deploy\%(ProjectsToBuild.Filename)"
VirtualPath="/%(ProjectsToBuild.Filename)%(ProjectsToBuild.Extension)"
Debug="true"
Updateable="true"
Force="true" />
</Target>
If one of the tasks fails it will exit the target. Is there any way to just print the error and continue exceuting the remaining tasks?
ContinueOnError is not an option since it will just convert the errors to warnings. I want the build to fail in the end but I also want to get as much error information as I can get so I still need to compile all the sites even though some of them fail.
The only way you can do this is if you can detect when an error has occurred. Basically the task will have to write out some artifact, or present you an output parameter where you can tell if it failed or not. You would use that along with setting ContinueOnError to true for the task itself. The idea is, set ContinueOnError to true, allow all the task invocations to complete then after that look to see if there was an error and act accordingly.
I did something similar for executing unit tests from MSBuild. I wanted all the unit tests to execute in all test assemblies, but also wanted to fail the build after they were done. So what I did was set ContinueOnError to true, then searched the XML file that the results were written to for any failed test cases, also I aggregated the messages from that file.
In your case the AspNetCompiler task doesn't write out any such file. The AspNetCompiler is wrapping up the aspnet_compiler.exe utility by extending ToolTask (via ToolTaskExtension) so you could keep track of the ExitCode. It is kind of tricky without writing you own task extending that task. If you use Target Batching you could invoke the AspNetCompiler task and then write each ExitCode into a file. Then after that look through that file for and non-zero exit code. You may want to consider writing your own custom task which extends the AspNetCompiler task, should be pretty simple to write.
For more info on batching see the resources at http://sedotech.com/Resources#Batching.