Build target reference from .nuspec file - msbuild

I am creating a build target in .csproj file in VS 2017
<Target Name="CopyPackage" AfterTargets="Pack">
<Copy
SourceFiles="$(OutputPath)..\$(PackageId).$(PackageVersion).nupkg"
DestinationFolder="\\myshare\packageshare\"
/>
</Target>
The "PackageId" and "PackageVersion" need to be mentioned in the .csproj file to accomplish the goal.
<PackageId>My Package</PackageId>
<PackageVersion>1.0.0</PackageVersion>
But I have variables defined in .nuspec file.
Is it possible to access any variable from .nuspec file inside the .csproj?

If you already have a .nuspec file and want to query its content, you can use MSBuild's XmlPeek task:
<Target Name="PrintVersions" AfterTargets="Pack">
<XmlPeek Namespaces="<Namespace Prefix='nu' Uri='http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd'/>"
XmlInputPath="$(NuspecFile)"
Query="/nu:package/nu:metadata/nu:id/text()">
<Output TaskParameter="Result" PropertyName="MyPackageId" />
</XmlPeek>
<XmlPeek Namespaces="<Namespace Prefix='nu' Uri='http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd'/>"
XmlInputPath="$(NuspecFile)"
Query="/nu:package/nu:metadata/nu:version/text()">
<Output TaskParameter="Result" PropertyName="PackageVersion" />
</XmlPeek>
<Message Importance="high" Text="PackageId: $(PackageId)" />
<Message Importance="high" Text="PackageVersion: $(MyPackageVersion)" />
</Target>

Related

Msbuild task with variables

I want the $(fooAssemblyInfo.Version) to work in my BuildRepositoryZip task but it aint. Why? Do I have to move out the ItemName or something out of Target assemblyversion and in that case how is that done?
<Target Name="AssemblyVersion">
<GetAssemblyIdentity AssemblyFiles="..\Tools\Utility.dll">
<Output TaskParameter="Assemblies" ItemName="fooAssemblyInfo"/>
</GetAssemblyIdentity>
<Message Text="%(fooAssemblyInfo.Version)" />
</Target>
<!-- Zipping all *.zip to a package for repository server-->
<Target Name="BuildRepositoryZip" DependsOnTargets="AssemblyVersion">
<!-- Create a zip file based on a Path -->
<MSBuild.ExtensionPack.Compression.Zip TaskAction="Create" CompressPath="..\..\Deploy\*.zip" ZipFileName="..\..\$(fooAssemblyInfo.Version).zip"/>
</Target>

MsBuild Copy output and remove part of path

I have an MsBuild project which builds various solutions and then copies the output of Web Deployment Projects into a destination folder with two sub folder as follows:
The WDP output folders are copied over from the BuildFolder "Release".
DestFolder/PresentationTier/MyProject.xxx0Services_deploy/**Release**/Files...
DestFolder/MidTier/MyProject.xx1UI_deploy/**Release**/Files...
This works but I want to remove the $(Configuration) value from the output.
So the desired output folder layout is to be:
DestFolder/PresentationTier/MyProject.xxx0Services_deploy/Files...
DestFolder/MidTier/MyProject.xx1UI_deploy/Files...
Note the removal of "Release" folder
My code is below.
How can I change this to give the desired out please:
Code extract is as follows
<Target Name="CopyMidTierBuildOutput" DependsOnTargets="CopyPresentationTierBuildOutput" >
<Message Text="Copying midTier Build Output=================" />
<CreateItem Include="$(DeploymentRoot)**/MyProject.xxx0Services_deploy/$(Configuration)/**/*.*;
$(DeploymentRoot)**/MyProject.xxx1Services.Host_deploy/$(Configuration)/**/*.*;
$(DeploymentRoot)**/MyProject.xxx2.Host.IIS.csproj_deploy/$(Configuration)/**/*.*;
$(DeploymentRoot)**/MyProject.xxx3Services_deploy/$(Configuration)/**/*.*;
$(DeploymentRoot)**/Nad.xxx4_deploy/$(Configuration)/**/*.*;
$(DeploymentRoot)**/Nad.xxx5Services.Host_deploy/$(Configuration)/**/*.*;
$(DeploymentRoot)**/Nad.xxx6Services.Host_deploy/$(Configuration)/**/*.*;
$(DeploymentRoot)**/Nad.xxx7Service.Host.IIS_deploy/$(Configuration)/**/*.*;
$(DeploymentRoot)**/Nad.xxx8Services.Host_deploy/$(Configuration)/**/*.*;
$(DeploymentRoot)**/Nad.xxx9Service.Host.IIS.csproj_deploy/$(Configuration)/**/*.*;
$(DeploymentRoot)**/Nad.xxx10Services.Host_deploy/$(Configuration)/**/*.*">
<Output TaskParameter="Include" ItemName="MidTierDeploys"/>
</CreateItem>
<Copy
SourceFiles="#(MidTierDeploys)"
DestinationFolder="$(DestFolder)/MidTier/%(RecursiveDir)" ContinueOnError="false" />
You can implement expected behaviour with biltin features of MSBuild 4:
<ItemGroup>
<DeploymentProjects Include="1_deploy" />
<DeploymentProjects Include="2_deploy" />
</ItemGroup>
<Target Name="CopyMidTierBuildOutput" >
<Message Text="Copying midTier Build Output" Importance="High"/>
<ItemGroup>
<MidTierDeploys Include="$(DeploymentRoot)**\%(DeploymentProjects.Identity)\$(Configuration)\**\*.*">
<DeploymentProject>%(DeploymentProjects.Identity)</DeploymentProject>
</MidTierDeploys>
</ItemGroup>
<Msbuild Targets="CopyDeploymentItem"
Projects="$(MSBuildProjectFile)"
Properties="ItemFullPath=%(MidTierDeploys.FullPath);ItemRecursiveDir=%(MidTierDeploys.RecursiveDir);ItemDeploymentProject=%(MidTierDeploys.DeploymentProject);Configuration=$(Configuration);DestFolder=$(DestFolder)" />
</Target>
<Target Name="CopyDeploymentItem" >
<PropertyGroup>
<ItemExcludePath>$(ItemDeploymentProject)\$(Configuration)</ItemExcludePath>
<ItemDestRecursiveDirIndex>$(ItemRecursiveDir.IndexOf($(ItemExcludePath))) </ItemDestRecursiveDirIndex>
<ItemExcludePathLength>$(ItemExcludePath.Length)</ItemExcludePathLength>
<ItemSkippingCount>$([MSBuild]::Add($(ItemDestRecursiveDirIndex), $(ItemExcludePathLength)))</ItemSkippingCount>
<ItemDestRecursiveDir>$(ItemRecursiveDir.Substring($(ItemSkippingCount)))</ItemDestRecursiveDir>
</PropertyGroup>
<Copy
SourceFiles="$(ItemFullPath)"
DestinationFolder="$(DestFolder)/MidTier/$(ItemDeploymentProject)/$(ItemDestRecursiveDir)" ContinueOnError="false" />
</Target>
See Property functions for more info.

MSBuild find value in file

So I run my task with ccnet and my task creates files. What is the best way to read the file and identify if there is a certain value in it from msbuild??
It's depend on your file.
Plain text with multiple lines
If the file is like that :
Building XXX
...
BUILD SUCCESSFUL
Total time: 38 seconds
Buildfile: file.
You could use ReadLinesFromFile to read the file and CreateProperty with a Condition to check the value.
<PropertyGroup>
<ValueToCheck>BUILD SUCCESSFUL</ValueToCheck>
</PropertyGroup>
<Target Name="CheckValue">
<ReadLinesFromFile File="#(MyTextFile)" >
<Output TaskParameter="Lines" ItemName="Value"/>
</ReadLinesFromFile>
<CreateProperty Value="true"
Condition="'%(Value.Identity)' == '$(ValueToCheck)'">
<Output TaskParameter="Value" PropertyName="ValueIsPresent" />
</CreateProperty>
</Target>
Xml file
If the file is in Xml, you could use XmlPeek (MSBuild 4) or XmlRead from MSBuild Community Task.
How to use XmlPeek?
How to use XmlRead?
Here's what I did in MSBuild 4. It's a crude but native grep for MSBuild, with no pattern matching. This MSBuild project will look for files (FILES_TO_FIND) in a folder (SOURCE_FOLDER) that contain a string (STRING_TO_FIND).
After parsing the files, it prints a list of files that do not contain the string (FILES_THAT_DONT_MATCH), and a list of files that did (FILES_THAT_MATCH).
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
ToolsVersion="4.0" DefaultTargets="Main">
<!-- Works as-is in MSBuild 4.0.30319.1 -->
<PropertyGroup>
<SOURCE_FOLDER>C:\MyCode</SOURCE_FOLDER>
<FILES_TO_SEARCH>*.sln</FILES_TO_SEARCH>
<STRING_TO_FIND>vcxproj</STRING_TO_FIND>
</PropertyGroup>
<ItemGroup>
<FILES_TO_SEARCH Include="$(SOURCE_FOLDER)\**\$(FILES_TO_SEARCH)"/>
</ItemGroup>
<Target Name="Main" DependsOnTargets="CheckForValue">
<Message Text="$(FILES_TO_SEARCH) files without '$(STRING_TO_FIND)':"
Importance="high"/>
<Message Text=" - %(FILES_THAT_DONT_MATCH.Identity)"/>
<Message Text=" "/>
<Message Text="$(FILES_TO_SEARCH) files with '$(STRING_TO_FIND)':"
Importance="high"/>
<Message Text=" - %(FILES_THAT_MATCH.Identity)"/>
</Target>
<Target Name="CheckForValue" Outputs="%(FILES_TO_SEARCH.Identity)">
<ReadLinesFromFile File="%(FILES_TO_SEARCH.Identity)" >
<Output TaskParameter="Lines" ItemName="LinesFromReadFile"/>
</ReadLinesFromFile>
<PropertyGroup>
<FileContent>#(LinesFromReadFile)</FileContent>
</PropertyGroup>
<ItemGroup>
<FILES_THAT_MATCH Include="%(FILES_TO_SEARCH.Identity)"
Condition="$(FileContent.Contains ('$(STRING_TO_FIND)'))"/>
<FILES_THAT_DONT_MATCH Include="%(FILES_TO_SEARCH.Identity)"
Condition="!$(FileContent.Contains ('$(STRING_TO_FIND)'))"/>
</ItemGroup>
</Target>
</Project>

MSBuild: Add additional files to compile without altering the project file

After looking around I can't find a simple answer to this problem.
I am trying to create an MSBuild file to allow me to easily use SpecFlow and NUnit within Visual Studio 2010 express.
The file below is not complete this is just a proof of concept and it needs to be made more generic.
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<BuildDependsOn>
BuildSolution;
SpecFlow;
BuildProject;
NUnit;
</BuildDependsOn>
</PropertyGroup>
<PropertyGroup>
<Solution>C:\Users\Craig\Documents\My Dropbox\Cells\Cells.sln</Solution>
<CSProject>C:\Users\Craig\Documents\My Dropbox\Cells\Configuration\Configuration.csproj</CSProject>
<DLL>C:\Users\Craig\Documents\My Dropbox\Cells\Configuration\bin\Debug\Configuration.dll</DLL>
<CSFile>C:\Users\Craig\Documents\My Dropbox\Cells\Configuration\SpecFlowFeature1.feature.cs</CSFile>
</PropertyGroup>
<Target Name="Build" DependsOnTargets="$(BuildDependsOn)">
<Message Text="Build Started" Importance="high" />
<Message Text="Build Ended" Importance="high" />
</Target>
<Target Name="BuildSolution">
<Message Text="BuildSolution Started" Importance="high" />
<MSBuild Projects="$(Solution)" Properties="Configuration=Debug" />
<Message Text="BuildSolution Ended" Importance="high" />
</Target>
<Target Name="SpecFlow">
<Message Text="SpecFlow Started" Importance="high" />
<Exec Command='SpecFlow generateall "$(CSProject)"' />
<Message Text="SpecFlow Ended" Importance="high" />
</Target>
<Target Name="BuildProject">
<Message Text="BuildProject Started" Importance="high" />
<MSBuild Projects="$(CSProject)" Properties="Configuration=Debug" />
<Message Text="BuildProject Ended" Importance="high" />
</Target>
<Target Name="NUnit">
<Message Text="NUnit Started" Importance="high" />
<Exec Command='NUnit /run "$(DLL)"' />
<Message Text="NUnit Ended" Importance="high" />
</Target>
</Project>
The SpecFlow Task looks in the .csproj file and creates a SpecFlowFeature1.feature.cs.
I need to include this file when building the .csproj so that NUnit can use it.
I know I could modify (either directly or on a copy) the .csproj file to include the generated file but I'd prefer to avoid this.
My question is: Is there a way to use the MSBuild Task to build the project file and tell it to include an additional file to include in the build?
Thank you.
I found no way of doing it without editing the project file.
So I made an MSBuild file to:
Copy the project files
Run the copies through SpecFlow
Add the new .cs files to the copied projects
Compile the projects
Debug Run each of the compiled DLLs through NUnit
Clean up - Delete the copied projects
I've blogged about how to use it here:
http://learntdd.wordpress.com/2010/06/10/using-specflow-and-nunit-on-visual-studio-2010-express/
(It's version 1, I'd like to improve the script)
I couldn't think of any way to achieve without any modification to the .csproj file.
The approach I'd suggest would look like this.
In your .csproj you Import a container target file
...
<Import Project="SpecFlow.target" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
...
just above the CSharp.targets.
Specflow.targets would look like this
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Compile Include="#(Compile)" />
</ItemGroup>
</Project>
so it doesn't harm while building the project from VS.
You could then use the Output of your SpecFlow Exec and add it to the SpecFlow.targets file
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Compile Include="#(Compile)" />
<Compile Include="SpecFlowFeature1.feature.cs" />
</ItemGroup>
</Project>
...
and clean SpecFlow.targets after building your .csproj.

Updating Assembly information with MSBuild failing

All
i am trying to automatically update the assembly information of a project using AssemblyInfo task, before build however the target appears to do nothing (no failure/error) just no update/creation
Below is the build.proj file I am using (obviously some contents altered)
Can anyone help?
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets"/>
<Import Project="$(MSBuildExtensionsPath)\Microsoft\AssemblyInfoTask\Microsoft.VersionNumber.targets"/>
<PropertyGroup>
<Major>1</Major>
<Minor>0</Minor>
<Build>0</Build>
<Revision>0</Revision>
</PropertyGroup>
<PropertyGroup>
<BuildDir>C:\svn\Infrastructure</BuildDir>
</PropertyGroup>
<ItemGroup>
<SolutionsToBuild Include="Infrastructure.sln"/>
</ItemGroup>
<Target Name="Build" DependsOnTargets="ChangeDataAccessAssemblyInfo">
<RemoveDir Directories="$(BuildDir)\Builds" Condition="Exists('$(BuildDir)\Builds')" />
<MSBuild Projects="#(SolutionsToBuild)" Properties="Configuration=Debug" Targets="Rebuild" />
</Target>
<ItemGroup>
<TestAssemblies Include="Build\Logging\Logging.UnitTests.dll" />
</ItemGroup>
<!--<UsingTask TaskName="NUnit" AssemblyFile="$(teamcity_dotnet_nunitlauncher_msbuild_task)" />
<Target Name="Test" DependsOnTargets="Build">
<NUnit NUnitVersion="NUnit-2.4.6" Assemblies="#(TestAssemblies)" />
</Target>-->
<Target Name="ChangeDataAccessAssemblyInfo" >
<Message Text="Writing ChangeDataAccessAssemblyInfo file for 1"/>
<Message Text="Will update $(BuildDir)\DataAccess\My Project\AssemblyInfo.vb" />
<AssemblyInfo CodeLanguage="VB"
OutputFile="$(BuildDir)\DataAccess\My Project\AssemblyInfo_new.vb"
AssemblyTitle="Data Access Layer"
AssemblyDescription="Message1"
AssemblyCompany="http://somewebiste"
AssemblyProduct="the project"
AssemblyCopyright="Copyright notice"
ComVisible="true"
CLSCompliant="true"
Guid="hjhjhkoi-9898989"
AssemblyVersion="$(Major).$(Minor).1.1"
AssemblyFileVersion="$(Major).$(Minor).5.7"
Condition="$(Revision) != '0' "
ContinueOnError="false" />
<Message Text="Updated Assembly File Info"
ContinueOnError="false"/>
</Target>
</Project>
I think you are missing the specification of the AssemblyInfoFiles attribute on your AssemblyInfo task. Here's how it looks on a project I'm working on...
<Target Name="AfterGet">
<Message Text="In After Get"/>
<CreateItem Include="$(SolutionRoot)\Source\SomeProject\My Project\AssemblyInfo.vb">
<Output ItemName="AssemblyInfoFiles" TaskParameter="Include"/>
</CreateItem>
<Attrib Files="#(AssemblyInfoFiles)"
ReadOnly="false"/>
<AssemblyInfo AssemblyInfoFiles="#(AssemblyInfoFiles)"
AssemblyDescription="$(LabelName)">
</AssemblyInfo>
</Target>
What we're doing is first using to create a property that contains the name of the file we'll be updating. We have to do this via createItem because when we start the build the file doesn't exist (and that is when MSBuild evaluates the and definitions in your build file.
We then take the readonly bit off the file.
Finally we invoke the AssemblyInfo task passing it the file(s) to update and a custom assembly name that we want to give it (in this case we put the TFS build label into the Assembly Description field so that we can easily tell which team build the assembly came from.