I have a big problem with MSBuild on Mac. So I can't use conditions because they are sadly not resolved. (WTF)
So I am trying to workaround this problem, but I am stuck. Basically what I don't understand but want to understand is the following thing:
<PropertyGroup>
<PropertyA>#(SomeItemGroup)</PropertyA>
<PropertyB>$(PropertyA)</PropertyB>
</PropertyGroup>
<Message Importance="high" Text="$(PropertyA)"/>
<Message Importance="high" Text="$(PropertyB)"/>
This example is fine. Both will result in the content of the ItemGroup and will print the message.
Now I have the following example where I am working with Transform:
<PropertyGroup>
<PropertyA>#(SomeItemGroup->'%(SomeItem)', '')</PropertyA> => One String for List
<PropertyB>$(PropertyA.Substring(0, 5))</PropertyB> => PropertyA does not resolve
</PropertyGroup>
<Message Importance="high" Text="$(PropertyA)"/>
<Message Importance="high" Text="$(PropertyB)"/>
The first message will give me the content of the ItemGroup. But the second message will only return "#(Som", because it doesnt resolve the PropertyA. Is there any chance to force a property to resolve within another property?
Related
I have been trying to update the ApplicationVersion property in my csproj file.witch works fine; i have added a Target that runs an custom task to extract the AssemblyFileVersion from my assemblyinfo.cs; this works there is no doubt about that.
But then when i want to use my updated ApplicationVersion to determan where to put my newly build files, i get the default value set in the property.
<PropertyGroup>
...
<ApplicationVersion>1.0.0.0</ApplicationVersion>
...
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\media-converter-BUILD\debug\$(ApplicationVersion)\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>..\media-converter-BUILD\debug\$(ApplicationVersion)\MediaConverter.XML</DocumentationFile>
</PropertyGroup>
My Targets
<UsingTask AssemblyFile="GetAssemblyFileVersion.dll" TaskName="GetAssemblyFileVersion.GetAssemblyFileVersion" />
<Target Name="MainAfterCompile">
<CallTarget Targets="AfterCompile" />
<CallTarget Targets="VerifyParam" />
</Target>
<Target Name="AfterCompile">
<GetAssemblyFileVersion strFilePathAssemblyInfo="Properties\AssemblyInfo.cs">
<Output TaskParameter="strAssemblyFileVersion" PropertyName="ApplicationVersionModded" />
</GetAssemblyFileVersion>
<PropertyGroup>
<ApplicationVersion>$(ApplicationVersionModded)</ApplicationVersion>
</PropertyGroup>
</Target>
<Target Name="VerifyParam">
<Message Text="New $(ApplicationVersionModded)" Importance="high"/>
<Message Text="Old Updated $(ApplicationVersion)" Importance="high"/>
</Target>
the GetAssemblyFileVersion.dll i more or less stole from some post i found on the internet, just can't find it again, so i can't add a link, sorry.
My theory on why it does not work is that the transforms and parameters in PropertyGroups are rendered before both InitailTagets and DefaultTargets is run. And there for will my plan never work
but if anyone knows of a way to make it work, i will be grateful to here it
My theory on why it does not work is that the transforms and parameters in PropertyGroups are rendered before both InitailTagets and DefaultTargets is run indeed, that's how the evaluation order works: msbuild evaluates global properties in the first pass of the file, you define OutputPath, that is used by the Microsoft.Common.CurrentVersion.targets file to derive OutDir/BaseIntermediateOutputPath/.... Then in another pass your targets run and update the version number, but there isn't another pass which evaluates the global OutputPath property again.
You can however override the value of OutputPath and derived paths in a Target, and it will take effect, you just have to take care of running it early in the build so that other targets use the updated version. This does the trick:
<Target Name="GetApplicationVersion">
<GetAssemblyFileVersion strFilePathAssemblyInfo="Properties\AssemblyInfo.cs">
<Output TaskParameter="strAssemblyFileVersion" PropertyName="ApplicationVersion" />
</GetAssemblyFileVersion>
</Target>
<Target Name="SetOutputPaths" DependsOnTargets="GetApplicationVersion"
BeforeTargets="PrepareForBuild">
<PropertyGroup>
<OutputPath>bin\$(Configuration)\$(ApplicationVersion)\</OutputPath>
<OutDir>$(OutputPath)</OutDir>
</PropertyGroup>
<Message Text="Set OutDir to $(OutDir)" Importance="high" />
</Target>
Another way to deal with this is doing things the other way around: define the application version as a global msbuild property, then use it to define OutputPath and to update the number in AssemblyVersion.cs before it is compiled.
I have a property group which includes a property for the build_number which is being passed in from TeamCity as solely the Build Counter. The build number format being set in TeamCity as simply {0} for the counter.
<PropertyGroup>
<Major>10</Major>
<Minor>1</Minor>
<Build>$(BUILD_NUMBER)</Build>
<Release>0</Release>
...
</PropertyGroup>
The Major, Minor and Release properties are then updated from values in a file in source control.
So that TeamCity logs the build as the full 4 part build reference (not just the counter), I set it thus:
<TeamCitySetBuildNumber BuildNumber="$(Major).$(Minor).$(Build).$(Release)" />
However, now when I reference my $(Build) property, it's now set to the 4 part build reference, and any property I have made which makes reference to $(BUILD_NUMBER) prior to setting using TeamCitySetBuildNumber also gets overwritten with the 4 part reference.
NB I've also changed it with a system message:
<Message Text="##teamcity[buildNumber '$(Major).$(Minor).$(Build).$(Release)']" />
but the overall effect is the same.
How Can I refer to the build counter (only) AFTER I have set the BuildNumber above?
If you're using a project file, you could try calling the TeamCitySetBuildNumber command in the AfterBuild section of the *.vbproj or *.csproj file:
<Target Name="AfterBuild">
<TeamCitySetBuildNumber BuildNumber="$(Major).$(Minor).$(Build).$(Release)" />
</Target>
If you're using a solution file, I'd create a *.proj file that calls your solution file and then after that call the TeamCitySetBuildNumber command (not sure if you can call the TeamCitySetBuildNumber command within the target like this though...):
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="SetBuildNumber">
<PropertyGroup>
<Major>10</Major>
<Minor>1</Minor>
<Build>$(BUILD_NUMBER)</Build>
<Release>0</Release>
</PropertyGroup>
<Target Name="Build">
<Message Text="Build task called... " Importance="high"/>
<MSBuild Projects="$(teamcity_build_checkoutDir)\your_solution.sln" Properties="Configuration=Release"/>
</Target>
<Target Name="SetBuildNumber" DependsOnTargets="Build">
<Message Text="Setting build number back to TeamCity... " Importance="high"/>
<TeamCitySetBuildNumber BuildNumber="$(Major).$(Minor).$(Build).$(Release)" />
</Target>
</Project>
I am in the process of upgrading a project to .NET 4.0 and I get this error when I try and build the solution: Invalid argument. Parameter "ItemSpec" cannot be null. c:\Windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets 1558 9
Is there a way to find out what is being passed as null to the ResolveComReference task?
Try this; temporarily add the following to the project file that is failing:
<Target Name="DiagnoseNullItems"
BeforeTargets="ResolveComReferences"
AfterTargets="ResolveAssemblyReferences">
<Message Importance="High" Text="COMReference is '#(COMReference)'" />
<Message Importance="High" Text="COMFileReference is '#(COMFileReference)'" />
<Message Importance="High" Text="ReferencePath is '#(ReferencePath)'" />
<Message Importance="High" Text="_ResolveComReferenceCache is '#(_ResolveComReferenceCache)'" />
</Target>
I think that is all of them, unless some of the other $() arguments to that task are also of type ITaskItem. If that doesn't reveal an empty item, do the same for the properties passed to the ResolveComReference task.
With MSBuild, as soon as an error occurs, the execution of the project is stopped unless ContinueOnError=true.
Is there a way to stop the execution of the project without raising an error?
I'd like to have this possibility because I have an existing set of msbuild project files and in some circumstances, I would need to stop processing the projects without raising an error because it is a normal exit point for the process and I don't want the person using the script to think something is wrong.
I know I could just set some property and put all remaining tasks conditional on this but I would like to avoid that.
As you explain it, you want to stop your build under special circumstance without raising an error because it is a normal exit point. Why not create a target doing nothing that will serve as your exit point. Under your special conditions you will call this target.
<target Name="BuildProcess">
<Message Text="Build starts"/>
...
<CallTarget Targets="Exit"
Condition="Special Condition"/>
<CallTarget Targets="Continue"
Condition="!(Special Condition)"/>
...
</target>
<target Name="Continue">
<Message Text="Build continue"/>
</target>
<target Name="Exit">
<!-- This target could be removed -->
<!-- Only used for logging here -->
<Message Text="Build ended because special condition occured"/>
</target>
The way to do this is the create another target to wrap the target you're interested in conditioning.
So if you have a scenario with a target like this:
<Target Name="MainTarget">
command - run under a certain condition
command - run under a certain condition
command - run under a certain condition
command - run under a certain condition
command - run under a certain condition
</Target>
The point is that you want to save having to use the condition statement a whole bunch of times, right?
To address this, you can do this:
<Target Name="MainWrapper" DependsOnTargets="EstablishCondition;MainTarget" />
<Target Name="EstablishCondition">
<SomeCustomTask Input="blah">
<Output PropertyName="TestProperty" TaskParameter="value" />
</SomeCustomTask>
</Target>
<Target Name="MainTarget" Condition="$(TestProperty)='true'">
command
command
command
command
command
</Target>
Eventually found an elegant solution for a similar issue. I just needed to rephrase my concern from "Break/interrupt MSBuild execution" to "Skip the next targets".
<PropertyGroup>
<LastInfoFileName>LastInfo.xml</LastInfoFileName>
<NewInfoFileName>NewInfo.xml</NewInfoFileName>
</PropertyGroup>
<Target Name="CheckSomethingFirst" BeforeTargets="DoSomething">
<Message Condition="ConditionForContinue"
Text="Let's carry on with next target" />
<WriteLinesToFile Condition="ConditionForContinue"
File="$(NewInfoFileName)"
Lines="#(SomeText)"
Overwrite="true" />
<Message Condition="!ConditionForContinue"
Text="Let's discard next target" />
<Copy Condition="!ConditionForContinue"
SourceFiles="$(LastInfoFileName)"
DestinationFiles="$(NewInfoFileName)" />
</Target>
<Target Name="DoSomething" Inputs="$(NewInfoFileName)"
Outputs="$(LastInfoFileName)">
<Message Text="DoSomethingMore" />
<Copy SourceFiles="$(NewInfoFileName)"
DestinationFiles="$(LastInfoFileName)" />
</Target>
This works ok with a command like:
msbuild.exe Do.targets /t:DoSomething
where target DoSomething Inputs/Outputs are correctly checked after the CheckSomethingFirst target was executed.
Is there a way to get the Name of the currently running Target in MSBuild?
MSBuild does not provide a property (or other mechanism) that provides the name of the current executing target.
If you are thinking in terms of languages and frameworks that expose a stack trace at runtime, MSBuild has nothing equivalent to that.
If you are debugging and trying to visualize the order of target invocation, one trick is to define your own property and successively add values to it as a breadcrumb trail.
e.g.
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="A" DependsOnTargets="B">
<PropertyGroup>
<Trace>$(Trace);A</Trace>
</PropertyGroup>
<Message Text="$(Trace)" />
</Target>
<Target Name="B" DependsOnTargets="C">
<PropertyGroup>
<Trace>$(Trace);B</Trace>
</PropertyGroup>
</Target>
<Target Name="C">
<PropertyGroup>
<Trace>$(Trace);C</Trace>
</PropertyGroup>
</Target>
</Project>
In the example, each PropertyGroup in each target is hard-coding a value that is added to the $(Trace) property.
Running this MSBuild file with verbosity set to normal will produce the output:
;C;B;A
It might be nice to have a generic snippet of code that could be pasted unchanged into each target but currently that's not possible.
You are probably looking for the $(MSBuildProjectFile) but "Target" is kind of vague.