How to extract directory from property? - msbuild

I have a property GroupProj storing a full path name. How can I extract the directory of the property?
I have the following code, but it doesn't work as expected:
<PropertyGroup>
<GroupProj>C:\development\project\default.groupproj</GroupProj>
</PropertyGroup>
<Target Name="Default">
<Message Text="Echo: $(GroupProj->'%(RootDir)')" />
</Target>
I will describe my actual intention of doing so. Perhaps there is a way to do the job that I am not aware of.
I have a Delphi groupproj (MSBuild project) file, C:\development\project\default.groupproj:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Projects Include="project1.dproj">
<Dependencies/>
</Projects>
<Projects Include="project2.dproj">
<Dependencies/>
</Projects>
<Projects Include="project3.dproj">
<Dependencies/>
</Projects>
</ItemGroup>
...
</Project>
There are other 3 MSBuild files (project1.dproj, project2.dproj and project3.dproj) stored in same folder as default.groupproj.
I create a MSBuild project file (c:\test.targets):
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build" ToolsVersion="3.5">
<Import Project="$(GroupProj)" />
<Target Name="Build">
<MSBuild BuildInParallel="True" Projects="project1.dproj;project2.dproj;project3.dproj"/>
</Target>
</Project>
And execute as:
c:\> msbuild /p:GroupProj="C:\development\project\default.groupproj" test.targets
The execution shall fail as MSBuild can't find projectN.dproj file. The issue shall be the working directory isn't set to default.groupproj.
One straight solution come into my mind is to extract directory of $(GroupProj) and concat to there projectN.dproj file.
That's the whole story of my question.

Try something like this:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<GroupProj>C:\development\project\default.groupproj</GroupProj>
</PropertyGroup>
<Target Name="Build">
<CreateItem Include="$(GroupProj)">
<Output TaskParameter="Include" ItemName="ItemFromProp"/>
</CreateItem>
<Message Text="1. #(ItemFromProp -> '%(RootDir)%(Directory)')"/>
<Message Text="2. %(ItemFromProp.RootDir)%(ItemFromProp.Directory)"/>
<Message Text="3. %(ItemFromProp.Identity)"/>
<Message Text="4. %(ItemFromProp.FullPath)"/>
<Message Text="5. %(ItemFromProp.FileName)"/>
<Message Text="6. %(ItemFromProp.Extension)"/>
</Target>
</Project>
EDIT:
To build the projects in parallel try this:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="GetGroupProjPath">
<ItemGroup>
<GroupProj Include="$(GroupProj)" />
<GroupProjPath Include="#(GroupProj->'%(Directory)')" />
</ItemGroup>
<PropertyGroup>
<GroupProjPath>#(GroupProjPath->'%(RootDir)%(Identity)')</GroupProjPath>
</PropertyGroup>
</Target>
<Import Project="$(GroupProj)" />
<Target Name="GetDProjs" DependsOnTargets="GetGroupProjPath">
<ItemGroup>
<DProjs Include="#(Projects->'$(GroupProjPath)%(FileName)%(Extension)')" />
</ItemGroup>
</Target>
<Target Name="Build" DependsOnTargets="GetDProjs">
<Message Text="#(DProjs)" />
</Target>
</Project>

Related

Execute a XCopy operation after all the projects build in MSBuild

I have a .proj file which is configured to execute a solution file which in turn build all the projects in the solution.
I want to add an XCopy operation which should copy the .dll files of all projects to another location only after all the projects build is completed.
I have tried with below, but it is not copying the dlls.
I am newbie in writing MSBuild tags, so it could be that I may be wrong in choosing this approach to write the task in this way.
Please provide a solution, if anyone knows.
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition="'$(Configuration)' == 'Release|Debug'"/>
<Platform Condition="'$(Platform)' == ''">x64</Platform>
</PropertyGroup>
<ItemDefinitionGroup>
<SolutionToBuild>
<Properties>Configuration=$(Configuration);Platform=$(Platform)</Properties>
<Targets>Clean;Build</Targets>
</SolutionToBuild>
</ItemDefinitionGroup>
<ItemGroup>
<SolutionToBuild Include="..\Seg\Algorithms.sln" />
</ItemGroup>
<Target Name="Build" >
<MSBuild Projects="#(SolutionToBuild)" Targets="%(SolutionToBuild.Targets)" Properties="%(SolutionToBuild.Properties)" BuildInParallel="false" ContinueOnError="false" />
</Target>
<Target Name="Clean">
<MSBuild Projects="#(SolutionToBuild)" Targets="Clean" Properties="%(SolutionToBuild.Properties)" BuildInParallel="false" ContinueOnError="false" />
</Target>
<PropertyGroup>
<CopyDestination>..\Extern\Algo\bin\$(Configuration)\</CopyDestination>
<CopySource>..\Seg\Algorithms\$(Configuration)\DoBin\</CopySource>
</PropertyGroup>
<ItemGroup>
<FilesToCopy Include="$(CopySource)*.dll"/>
</ItemGroup>
<ItemGroup>
<CustomBuildStep Include ="#(FilesToCopy)">
<Message>Copying..</Message>
<Command> XCOPY %(Identity) $(CopyDestination) /f /y </Command>
</CustomBuildStep>
</ItemGroup>
<PropertyGroup>
<CustomBuildAfterTargets>Build</CustomBuildAfterTargets>
</PropertyGroup>
</Project>
Think of Targets as methods that are called. They run in sequence, so you just need to put your copy after the solution build:
<Target Name="Build">
<MSBuild Projects="#(SolutionToBuild)" Targets="%(SolutionToBuild.Targets)" Properties="%(SolutionToBuild.Properties)" BuildInParallel="false" ContinueOnError="false" />
<ItemGroup>
<FilesToCopy Include="..\Seg\Algorithms\$(Configuration)\DoBin\*.dll" />
</ItemGroup>
<Copy SourceFiles="#(FilesToCopy)" DestinationFolder="..\Extern\Algo\bin\$(Configuration)\" SkipUnchangedFiles="true" />
</Target>

MSBuild: Output properties from imported projects

Let's say I have a build.proj like this:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0"
DefaultTargets="AfterBuild"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<CustomAfterMicrosoftCSharpTargets>$(MSBuildThisFileDirectory)Common.Build.targets</CustomAfterMicrosoftCSharpTargets>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<ProjectProperties>
Configuration=$(Configuration);
Platform=$(Platform);
CustomAfterMicrosoftCSharpTargets=$(CustomAfterMicrosoftCSharpTargets);
</ProjectProperties>
</PropertyGroup>
<ItemGroup>
<ProjectToBuild Include="$(MSBuildThisFileDirectory)src\Proj\MyApp.csproj" />
</ItemGroup>
<Target Name="Build">
<MSBuild Targets="Build"
Projects="#(ProjectToBuild)"
Properties="$(ProjectProperties)" />
</Target>
<Target Name="AfterBuild" DependsOn="Build">
<Message Text="ChildProperty: $(ChildProperty)" />
</Target>
</Project>
In Common.Build.targets, I have a Target that creates a property:
<Target Name="DoSomethingUseful">
<!-- Do something useful -->
<CreateProperty Value="SomeComputedThingy">
<Output TaskParameter="Value" PropertyName="ChildProperty"/>
</CreateProperty>
</Target>
Now if I build build.proj, I do not see the value of ChildProperty in the message. The output is blank: ChildProperty:.
I was under the impression that any output for a target is merged back to global context after its execution. But it seems that it only applies to anything within that target file.
How do I make ChildProperty bubble up to the parent build.proj?
When you are calling <MSBuild> task on dependent projects, read TargetOutputs output parameter of the task. See example from MSDN:
<Target Name="BuildOtherProjects">
<MSBuild
Projects="#(ProjectReferences)"
Targets="Build">
<Output
TaskParameter="TargetOutputs"
ItemName="AssembliesBuiltByChildProjects" />
</MSBuild>
</Target>
You will also need to ensure the target you are calling in dependent projects correctly populates Returns or Output parameter (Returns takes precedence if used). E.g.:
<Target Name="MyTarget" Inputs="..." Outputs="..." Returns="$(MyOutputValue)">
<PropertyGroup>
<MyOutputValue>set it here</MyOutputValue>
</PropertyGroup>
</Target>

How wait copy process on msbuild

I create example to copy file from one folder to another (use msbuild). I try check after copy, if file was moved? But message still show, that files didn't moved. But when I see on folder, files was moved successful. So how it's fix?
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<PathPackage>C:\Users\test\Desktop\test\1\*.txt</PathPackage>
<Files>C:\Users\test\Desktop\test\2\*.*</Files>
</PropertyGroup>
<ItemGroup>
<Packages Include="$(PathPackage)"/>
<FilesOnFolder Include="$(Files)"/>
</ItemGroup>
<Target Name="B">
<Message Importance="normal" Text="Package before copy:#(Packages)"/>
<CreateItem Include="#(Packages)">
<Output TaskParameter="Include" ItemName="FilesToMove" />
</CreateItem>
<Copy
SourceFiles="#(Packages)"
DestinationFolder="C:\Users\test\Desktop\test\2"
/>
<Delete Files="#(Packages)" />
<Message Importance="normal" Text="Package after package:#(Packages)"/><!--It's full! -->
<Message Importance="normal" Text="Destination Folder:#(FilesOnFolder)"/> <!--It's empty! -->
</Target>
</Project>
Problem was in ItemGroup. It need write inside target.

Daily builds with MsBuild

What i want to do is to copy all files and subfolders from the OutputPath to the daily folder. For example i have project called Clock, i have msbuild script for it:
<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectPath>C:\Clock</ProjectSolutionName>
<ProjectSolutionName>Clock</ProjectSolutionName>
</PropertyGroup>
<Target Name="ReleaseBuild">
<Message Text="Building $(ProjectSolutionName) Release Build" />
<MSBuild Projects="$(ProjectPath)\$(ProjectSolutionName).sln" Targets="Clean" Properties="Configuration=Release" />
<MSBuild Projects="$(ProjectPath)\$(ProjectSolutionName).sln" Targets="Build" Properties="Configuration=Release" />
<Message Text="$(ProjectSolutionName) Release Build Complete!" />
</Target>
</Project>
Now when i run the script it compiles the solution and the files will be stored to a Release folder. How could i copy all the files and subfolders from the Release folder to folder named as the date, as for today for example: C:\Clock_Builds\20110803
This should do most of what you ask (its msbuild 4):
<Project DefaultTargets="DateCopy" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<Target Name="DateCopy">
<ItemGroup>
<Release Include="d:\Build\**\**"/>
</ItemGroup>
<PropertyGroup>
<StringDate>$([System.DateTime]::Now.ToString('yyyyMMdd'))</StringDate>
</PropertyGroup>
<MakeDir Directories="D:\Release\$(StringDate)"/>
<Message Text="$(StringDate)" Importance="High"/>
<Copy SourceFiles="#(Release)"
DestinationFolder="D:\Release\$(StringDate)\%(RecursiveDir)"/>
</Target>
</Project>
Hope that helps

Why won't the copy task item work in my msbuild script?

I have the following code in my msbuild script:
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0" InitialTargets="Build">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<SolutionName>CommTrac.Web\CommTrac.Web</SolutionName>
<SolutionFileName>$(SolutionName).csproj</SolutionFileName>
</PropertyGroup>
<Target Name="Build">
<Message Text="Building the solution"/>
<MSBuild Projects="$(SolutionFileName)" ContinueOnError="false" Properties="Configuration=$(Configuration)" />
</Target>
<Target Name="CopyOutput" DependsOnTargets="Build">
<ItemGroup>
<BinFolder Exclude="*.pdb" Include="$(ProjectDir)bin\**\*.*"/>
<BuildOutputFolder Include="C:\BuildOutput" />
</ItemGroup>
<Message Text="Copying from directory: $(BinFolder)"/>
<Copy SourceFiles="$(BinFolder)" DestinationFolder="$(BuildOutputFolder)"/>
</Target>
</Project>
For some reason, it will not copy the files to my output directory. I have tried all the similar
solutions with other questions that I have seen similar to this issue. Anyone have any ideas?
BindFolder and BuildOutputFolder are items, not properties. So you need to reference them using #(BindFolder) and #(BuildOutputFolder) instead of using '$'.