MSBuild MSBuildCommunityTasks Task Time - msbuild

I have a MSBuild project and I want the current date to be added to a zip file that I am creating.
I am using the MSBuildCommunityTasks.
<!-- Import the CommunityTasks Helpper -->
<Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" />
On the website http://msbuildtasks.tigris.org/ I can see a task called time. I have not been able to find doc on how to use Time.

In msbuild 4 you can now
$([Namespace.Type]::Method(..parameters…))
$([Namespace.Type]::Property)
$([Namespace.Type]::set_Property(value))
so I am using
$([System.DateTime]::Now.ToString(`yyyy.MMdd`))
those ticks around the format are backticks not '

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Deploy" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets"/>
<!-- Include MSBuild tasks here -->
<ItemGroup>
<DefaultExclude Include="****" />
</ItemGroup>
<Target Name="Deploy" >
<Time Format="yyyy-MM-dd">
<Output TaskParameter="FormattedTime" PropertyName="buildDate" />
</Time>
<Message Text="Deploying ...."></Message>
<Copy SourceFiles="#(DeploymentFiles)" DestinationFolder="C:\CCNET\$(buildDate)\bin\" />
</Target>
</Project>

Maslow's answer is correct (I can't comment on it or I would); I would only add to it that you have to be careful when implicitly calling System.DateTime.Parse.
A parsed string value like $([System.DateTime]::Parse("1970-01-01T00:00:00.0000000Z") doesn't seem to end up with a Kind of DateTimeKind.Utc.
But you can use nested property functions to make it work; like this (to get the Unix timestamp):
$([System.DateTime]::UtcNow.Subtract($([System.DateTime]::Parse("1970-01-01T00:00:00.0000000Z").ToUniversalTime())).TotalSeconds.ToString("F0"))

Related

Where is IntermediateAssembly set initially?

I want you to ask where IntermediateAssembly is set intially.
I ask for the reason that I want to optimize my assignments to only apply after IntermediateAssembly has been initialized.
Consider the following comparisons:
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<!-- NOT WORKING: list is empty -->
<ModuleInitializerAssemblyLoaderInjectionTargetAssemblies Include="#(IntermediateAssembly->'%(Identity)')" />
</ItemGroup>
</Project>
<Project InitialTargets="init" Sdk="Microsoft.NET.Sdk">
<Target Name="init">
<ItemGroup>
<!-- WORKING: list is initialized -->
<ModuleInitializerAssemblyLoaderInjectionTargetAssemblies Include="#(IntermediateAssembly->'%(Identity)')" />
</ItemGroup>
</Target>
</Project>
In dotnet sdk I cannot find the reference where IntermediateAssembly is set intially.
After searching, I have found it here https://github.com/dotnet/msbuild/blob/fdddb27ebec8f6a51513b6e869f039a32d3a8c39/src/Tasks/Microsoft.Common.CurrentVersion.targets#L359.
It is set in Microsoft.Common.CurrentVersion.targets in an ItemGroup under the Project node.

Compiler Additional Options computed in a custom Target

I have a msbuild custom Target and a Task computing a Value.
The Task will output the Value as Property.
This Property I would like to uses as Additional Option to the Compiler call.
But the Property is empty when used as Additional Option.
My *.targets File looks like this:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<UsingTask TaskName="GetBranchName_TASK" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" >
<ParameterGroup>
<sPath ParameterType="System.String" Required="true" />
<sBranchName ParameterType="System.String" Output="true" />
</ParameterGroup>
<Task>
<Code Type="Fragment" Language="cs">
<![CDATA[
... some Code ...
]]>
</Code>
</Task>
</UsingTask>
<Target Name="GetBranchName_TARGET">
<GetBranchName_TASK sPath="$(MSBuildThisFileDirectory)">
<Output PropertyName="BranchName" TaskParameter="sBranchName" />
</GetBranchName_TASK>
<Message Importance="High" Text="BranchName = $(BranchName)" />
</Target>
<PropertyGroup>
<BuildDependsOn>
GetBranchName_TARGET;
$(BuildDependsOn);
</BuildDependsOn>
</PropertyGroup>
</Project>
My *.props File is like this:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Configuration">
... some Properties here ...
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="IRSGetBranchName.targets" />
<ItemDefinitionGroup>
<ClCompile>
<AdditionalOptions>/DBRANCHNAME=$(BranchName) /DMORE=BAR</AdditionalOptions>
<ClCompile>
<ItemDefinitionGroup>
</Project>
This .props File then is imported into several .vcxproj
The Value printed as Message in my GetBranchName_TARGET is correct as expected (showing the correct TFS-Branch Name).
But when looking at Detailed Build Output, the Value seems empty:
1>ClCompile
1> ..\FOO.cpp
1> AdditionalOptions = /DBRANCHNAME= /DMORE=BAR
I tried for hours but found no solution and I really hope someone help whats wrong here ...
a) Is the Property BranchName not available globally? I tried to print the Property from other custom Targets and it worked well!
b) Or is the ClCompile.AdditionalOptions evaluated/build before my Target is excuted? In this case how can I re-evaluate?
c) ...
I'am very thankful for any Input.
You should be familiar with the msbuild evaluation process, as described here:
When the MSBuild engine begins to process a build file, it is evaluated in a top-down fashion in a multi-pass manner. These passes are described in order in the following list:
Load all environment and global properties, and toolset properties. In Microsoft Visual Studio 2010, for example, C++ defines several properties in the MSBuild 4.0 toolset.
Evaluate properties and process imports as encountered
Evaluate item definitions
Evaluate items
Evaluate using tasks
Start build and reading targets
So, in your case, the ItemDefinitionGroup for ClCompile has been evaluated before the GetBranchName_TARGET has been executed. So, it is empty by design.
In order to achieve the desired behavior, you should Add the following:
<Target Name="GetBranchName_TARGET">
<GetBranchName_TASK sPath="$(MSBuildThisFileDirectory)">
<Output PropertyName="BranchName" TaskParameter="sBranchName" />
</GetBranchName_TASK>
<Message Importance="High" Text="BranchName = $(BranchName)" />
<ItemGroup>
<ClCompile>
<AdditionalOptions>/DBRANCHNAME=$(BranchName) /DMORE=BAR</AdditionalOptions>
</ClCompile>
</ItemGroup>
</Target>
You can use a Condition attribute in the ClCompile in order to include only your sources, for example. Actually, what you are looking for is the feature to modify item metadata after it was declared.

Calling MSbuild Publish

Is it possible to call MSbuild publish during build or in pre-buildevent or in post-buildevent? I'm trying to publish two of the web projects from a solution. I'm using file system publishing.Requirement here is , building solution should take care of publish those two web projects. Can any one please suggest ?
I wouldn't put too much deploy logic in a post-build event. It becomes "fragile".
Create a separate .msbuild file, and do the "extra" logic in it, instead of messing too much with the .csproj file.
Below is a basic example.
Place the xml below in an file call "MyBuildAndDeploy.msbuild", put it in the same folder as your .sln (or .csproj) file, and then use
msbuild.exe "MyBuildAndDeploy.msbuild" from the command line.
So below is a basic example of building the primary solution and then copying the files somewhere.
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="AllTargetsWrapper">
<PropertyGroup>
<!-- Always declare some kind of "base directory" and then work off of that in the majority of cases -->
<WorkingCheckout>.</WorkingCheckout>
<BuildResultsRootFolder>$(WorkingCheckout)\..\BuildResults</BuildResultsRootFolder>
</PropertyGroup>
<Target Name="AllTargetsWrapper">
<CallTarget Targets="BuildSolution" />
<CallTarget Targets="CopyBuildOutputFiles" />
</Target>
<Target Name="BuildSolution">
<MSBuild Projects="$(WorkingCheckout)\MySuperCoolSolution.sln" Targets="Build" Properties="Configuration=$(Configuration)">
<Output TaskParameter="TargetOutputs" ItemName="TargetOutputsItemName"/>
</MSBuild>
<Message Text="BuildSolution completed" />
</Target>
<Target Name="CopyBuildOutputFiles">
<MakeDir Directories="$(BuildResultsRootFolder)\$(Configuration)" Condition="!Exists('$(BuildResultsRootFolder)\$(Configuration)\')"/>
<ItemGroup>
<BuildOutputFilesExcludeFiles Include="$(WorkingCheckout)\**\*.doesnotexist" />
<BuildOutputFilesIncludeFiles Include="$(WorkingCheckout)\**\*.dll" Exclude="#(BuildOutputFilesExcludeFiles)" />
<BuildOutputFilesIncludeFiles Include="$(WorkingCheckout)\**\*.exe" Exclude="#(BuildOutputFilesExcludeFiles)" />
<BuildOutputFilesIncludeFiles Include="$(WorkingCheckout)\**\*.config" Exclude="#(BuildOutputFilesExcludeFiles)" />
<BuildOutputFilesIncludeFiles Include="$(WorkingCheckout)\**\*.pdb" Exclude="#(BuildOutputFilesExcludeFiles)" />
</ItemGroup>
<Copy SourceFiles="#(BuildOutputFilesIncludeFiles)"
DestinationFolder="$(BuildResultsRootFolder)\$(Configuration)\"/>
</Target>
</Project>

I want to run a task for al files and exclude those who did not change

I have two buildtargets to check my code quality.
I run the following buildtargets every time i compile. This takes up too much time and i would like them to only check the files that did change.
In other words i want to filter files that did not change from the ItemGroup CppCheckFiles / LinterFiles.
<Target Name="CppCheck">
<ItemGroup>
<CppCheckFiles Include="*main.c" />
<CppCheckFiles Include="Source/*/*.c" />
</ItemGroup>
<Message Text="$(Configuration) starting." Importance="High" />
<Exec Command="C:\Cppcheck\cppcheck.exe %(CppCheckFiles.FullPath) --enable=style --template="{file}({line}): error:{severity}-{id}: {message}"" />
</Target>
<Target Name="SPLint">
<ItemGroup>
<LinterFiles Include="*main.c" />
<LinterFiles Include="Source/*/*.c" />
<LinterFiles Include="Source/*/*.h" />
</ItemGroup>
<Message Text="$(Configuration) starting." Importance="High" />
<Exec Command="splintCaller %(LinterFiles.FullPath)" />
</Target>
I know that the regular build process does this and i wonder if i have to go so fas as to write my own task.
hmm.. this sounds interesting. I can't help you. But it would be nice if the cppcheck wiki or manual had some small example project that did this.
Some people use cppcheck in commit hooks. I've tried it with GIT myself (I added a linux shell script). And there is a TortoiseSVN plugin you can try (http://sourceforge.net/apps/phpbb/cppcheck/viewtopic.php?f=3&t=443).
The solution is incremental Build. Where MSBuild compares Timestamps to exclude complete Buildtargets if nothing changed.
The following target creates a timesstamp for each file and skippes those files that did not change.
cppcheck.exe returns -1 if an error was detected and the timestamp is not written.
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="CppCheck" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<CppCheckFiles Include="*main.c" />
<CppCheckFiles Include="Source/*/*.c" />
</ItemGroup>
<Target Name="CppCheck"
Inputs="#(CppCheckFiles)"
Outputs="CCPCheck\%(CppCheckFiles.Filename)%(CppCheckFiles.Extension).stamp">
<Exec Command="C:\Cppcheck\cppcheck.exe %(CppCheckFiles.FullPath) --enable=style --template="{file}({line}): error:{severity}-{id}: {message}"" />
<MakeDir Directories="CCPCheck"/>
<Touch Files="CCPCheck\%(CppCheckFiles.Filename)%(CppCheckFiles.Extension).stamp" AlwaysCreate = "true" />
</Target>
</Project>

In MSBuild, can I use the String.Replace function on a MetaData item?

In MSBuild v4 one can use functions (like string.replace) on Properties. But how can I use functions on Metadata?
I'd like to use the string.replace function as below:
<Target Name="Build">
<Message Text="#(Files->'%(Filename).Replace(".config","")')" />
</Target>
Unfortunately this outputs as (not quite what I was going for):
log4net.Replace(".config","");ajaxPro.Replace(".config","");appSettings.Replace(".config","");cachingConfiguration20.Replace(".config","");cmsSiteConfiguration.Replace(".config","");dataProductsGraphConfiguration.Replace(".config","");ajaxPro.Replace(".config","");appSettings.Replace(".config","");cachingConfiguration20.Replace(".config","");cmsSiteConfiguration
Any thoughts?
You can do this with a little bit of trickery:
$([System.String]::Copy('%(Filename)').Replace('config',''))
Basically, we call the static method 'Copy' to create a new string (for some reason it doesn't like it if you just try $('%(Filename)'.Replace('.config',''))), then call the replace function on the string.
The full text should look like this:
<Target Name="Build">
<Message Text="#(Files->'$([System.String]::Copy("%(Filename)").Replace(".config",""))')" />
</Target>
Edit: MSBuild 12.0 seems to have broken the above method. As an alternative, we can add a new metadata entry to all existing Files items. We perform the replace while defining the metadata item, then we can access the modified value like any other metadata item.
e.g.
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Files Include="Alice.jpg"/>
<Files Include="Bob.not-config.gif"/>
<Files Include="Charlie.config.txt"/>
</ItemGroup>
<Target Name="Build">
<ItemGroup>
<!--
Modify all existing 'Files' items so that they contain an entry where we have done our replace.
Note: This needs to be done WITHIN the '<Target>' (it's a requirment for modifying existing items like this
-->
<Files>
<FilenameWithoutConfig>$([System.String]::Copy('%(Filename)').Replace('.config', ''))</FilenameWithoutConfig>
</Files>
</ItemGroup>
<Message Text="#(Files->'%(FilenameWithoutConfig)')" Importance="high" />
</Target>
</Project>
Result:
D:\temp>"c:\Program Files (x86)\MSBuild\12.0\Bin\MSBuild.exe" /nologo test.xml
Build started 2015/02/11 11:19:10 AM.
Project "D:\temp\test.xml" on node 1 (default targets).
Build:
Alice;Bob.not-config;Charlie
Done Building Project "D:\temp\test.xml" (default targets).
I needed to do something similar, the following worked for me.
<Target Name="Build">
<Message Text="#(Files->'%(Filename)'->Replace('.config', ''))" />
</Target>
Those functions works in properties only (as I know). So create target which will perform operation throw batching:
<Target Name="Build"
DependsOnTargets="ProcessFile" />
<Target Name="ProcessFile"
Outputs="%(Files.Identity)">
<PropertyGroup>
<OriginalFileName>%(Files.Filename)</OriginalFileName>
<ModifiedFileName>$(OriginalFileName.Replace(".config",""))</ModifiedFileName>
</PropertyGroup>
<Message Text="$(ModifiedFileName)" Importance="High"/>
</Target>
Do you really need in your example such kind of task? I mean there exists MSBuild Well-known Item Metadata
EDIT: I should specify that this task processes all items in #(Files).
i dont think you can use functions directly with itemgroups and metadata (that would be easy)
However you can use batching:
Taking the ideas from this post:
array-iteration
I was trying to trim an itemgroup to send to a commandline tool (i needed to lose .server off the filename)
<Target Name="ProcessFile" DependsOnTargets="FullPaths">
<ItemGroup>
<Environments Include="$(TemplateFolder)\$(Branch)\*.server.xml"/>
</ItemGroup>
<MSBuild Projects=".\Configure.msbuild"
Properties="CurrentXmlFile=%(Environments.Filename)"
Targets="Configure"/>
</Target>
<Target Name="Configure" DependsOnTargets="FullPaths">
<PropertyGroup>
<Trimmed>$(CurrentXmlFile.Replace('.server',''))</Trimmed>
</PropertyGroup>
<Message Text="Trimmed: $(Trimmed)"/>
<Exec Command="ConfigCmd $(Trimmed)"/>
</Target>
For MSBuild 12.0, here's an alternative.
<Target Name="Build">
<Message Text="$([System.String]::Copy("%(Files.Filename)").Replace(".config",""))" />
</Target>
Got the same problem (except with MakeRelative), so I passed with another solution : Using good old CreateItem that take a string and transform to Item :)
<ItemGroup>
<_ToUploadFTP Include="$(PublishDir)**\*.*"></_ToUploadFTP>
</ItemGroup>
<CreateItem Include="$([MSBuild]::MakeRelative('c:\$(PublishDir)','c:\%(relativedir)%(filename)%(_ToUploadFTP.extension)'))">
<Output ItemName="_ToUploadFTPRelative" TaskParameter="Include"/>
</CreateItem>
<FtpUpload Username="$(UserName)"
Password="$(UserPassword)"
RemoteUri="$(FtpHost)"
LocalFiles="#(_ToUploadFTP)"
RemoteFiles="#(_ToUploadFTPRelative->'$(FtpSitePath)/%(relativedir)%(filename)%(extension)')"
UsePassive="$(FtpPassiveMode)" ></FtpUpload>