I have the following task in an MSBuild script:
<XmlUpdate
Namespace="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"
XmlFileName="$(PackageDir)\temp\OddEnds.Testing\OddEnds.Testing.nuspec"
XPath="/package/metadata/version"
Value="%(OddEndsTestingAsmInfo.Version)" />
which is supposed to update an empty version node in a NuGet specification file with the assembly version. My .nuspec file looks like this:
<?xml version="1.0" encoding="utf-8"?>
<package xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http:www.w3.org/2001/XMLSchema-instance">
<metadata xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<id>OddEnds</id>
<authors>Tomas Lycken</authors>
<!-- Here's the node I want to update -->
<version></version>
<owners>Tomas Lycken</owners>
<description>Odd ends and bits that I might need in any project.</description>
</metadata>
</package>
I believe the XPath pointer /package/metadata/version points to the right node (since if I change it to something else, it complains about not finding the node) yet the output says 0 node(s) selected for update.
What am I missing?
You may need to include the namespace in your xpath string.
Check out this blog post: http://www.lesnikowski.com/blog/index.php/update-nuspec-version-from-msbuild/
You can also try //*:version. This will select all version elements regardless of namespace.
I had exactly the same problem with NuGet, XmlUpdate, MSBuild and XPath.
In the end I switched to the NuGetPack task of the MSBuild Community Tasks project.
(Note that the NuGet tasks are (at least for right now) only available in the Nightly Build)
Adding the version number to your NuGet package via MSBuild using this task would then look somehow like the following snippet:
<Target Name="NuGet">
<GetAssemblyIdentity AssemblyFiles="$(BuildCompileDirectory)\$(AssemblyName).dll">
<Output TaskParameter="Assemblies" ItemName="AssemblyIdentities"/>
</GetAssemblyIdentity>
<NuGetPack
ToolPath="$(ToolsDirectory)"
WorkingDirectory="$(BuildCompileDirectory)"
File="$(SrcDirectory)\$(SolutionName).nuspec"
Version="%(AssemblyIdentities.Version)"/>
</Target>
Hope that helps!
Your task would need to look like this:
<XmlUpdate
Prefix="xmlsucks"
Namespace="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"
XmlFileName="$(PackageDir)\temp\OddEnds.Testing\OddEnds.Testing.nuspec"
XPath="/xmlsucks:package/xmlsucks:metadata/xmlsucks:version"
Value="%(OddEndsTestingAsmInfo.Version)" />
Feel free to change the prefix to whatever derogatory term you would like to use :-)
Related
in my assambly Version I want to set the first 3 digits and the fourth should be set thorugh Teamcity.
Example:
I set 2.0.1.0 in my Assambly.
Then my Teamcity should do:
2.0.1.90, 2.0.1.91...
At the moment I have:
2.0.1.%build.counter%
But what I want to have is, that 2.0.1 is set through my assambly version not through a hard number
Which Teamcity version are you using? Teamcity 9.1 has a new build feature File Content Replacer which allows you to replace contents of a file using regex. However earlier versions of teamcity do not have this in built, so for them you'll have a write it on your own, May be a powershell script or MSBuild target.
Sample code for MSBuild target SetRevisionNumber
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<UsingTask AssemblyFile="MSBuild.Community.Tasks.dll" TaskName="MSBuild.Community.Tasks.FileUpdate" />
<Target Name="SetRevisionNumber" Condition="$(RevisionNumber) != ''">
<Message Text="Setting Revision Number to $(RevisionNumber) in $(AssemblyVersionFile)" Importance="High" />
<FileUpdate Files="$(AssemblyVersionFile)"
Regex="\[assembly: AssemblyVersion\(.(\d+)\.(\d+)\.(\d+)\.(\d+)"
ReplacementText="[assembly: AssemblyVersion("$1.$2.$3.$(RevisionNumber)" />
</Target>
</Project>
I'm trying to configure the YUICompressor.NET in my Visual Studio project.
As I've understood, I have to create a .proj file and add it to my solution. After that, I need to create a post-build event that will build this .proj file and I'll get my desired output (minified js/css files).
So, I have:
This .proj contains:
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/MsBuild/2003">
<UsingTask TaskName="CssCompressorTask" AssemblyFile="\..\packages\YUICompressor.NET.MSBuild.2.7.0.0\lib\NET20\Yahoo.Yui.Compressor.Build.MsBuild.dll" />
<UsingTask TaskName="JavaScriptCompressorTask" AssemblyFile="\..\packages\YUICompressor.NET.MSBuild.2.7.0.0\lib\NET20\Yahoo.Yui.Compressor.Build.MsBuild.dll" />
<Target Name="Minify">
<ItemGroup>
<!-- Single files, listed in order of dependency -->
<CssFiles Include="Content\*.css"/>
<JavaScriptFiles Include="Scripts\*.js"/>
</ItemGroup>
<CssCompressorTask
SourceFiles="#(CssFiles)"
OutputFile="Content\min.css"
/>
<JavaScriptCompressorTask
SourceFiles="#(JavaScriptFiles)"
OutputFile="Scripts\min.js"
/>
</Target>
</Project>
I'm trying to build it as the following:
I'm getting the following error:
The command "msbuild C:\Users\Me\Desktop\MvcApplicationExample\MvcApplicationExample\YuiCompressorMsBuild.proj" exited with code 9009.
This error suggests that "msbuild" is not a valid command. So, how should I build this type of project? (I've followed this tutorial: youtube)
Thanks for any help.
As you said maybe the visual studio cannot find the MSBuild command, try with the following command instead
"$(MSBuildBinPath)\msbuild.exe"
That uses the complete path to msbuild.
Update (For futures references)
As the comment by Steve Medley, you should not forget the encapsulating quotes.
vfabre is correct to use $(MSBuildBinPath)\msbuild.exe but missing one thing. you should encapsulate that in quotes as there will 99.99% of the time be a space in the file path to msbuild
Well, one thing that might be a problem is that you want it as a Post-build event but it's set for the Pre-build command line.
I've created a custom task to get me a three-part version number of my assembly that was built in MSBuild.
I've created a custom <Target Name="GetVersion"> for this, and it works nicely - the three-part version number(1.5.2) is stored into a ThreePartBuildNumber property in MSBuild.
But how do I tell MSBuild inside Visual Studio 2010 to call this target once it's compiled my assembly, and before creating my WiX Setup project (where I'd like to set the WiX install script's Product/#Version to this three-part version number automatically)?
How can I "plug" this new target into the usual VS 2010 build process?
Update:
OK, I've managed to get this into the *.wixproj file which is also a MSBuild file, really. In the <Target Name="BeforeBuild">, I can successfully determine the three-part version number, and it's stored inside a MSBuild property called ThreePartVersionNumber.
But how on earth can I now access this properly filled MSBuild property in my WiX setup? I tried setting <Product Version="$(var.ThreePartVersionNumber) ...>, but that doesn't work - it doesn't seem to find the variable.... neither works with the sys. or env. prefixes, either....
So how do I make this MSBuild property that has the information I need "visible" to the WiX installer script/XML ?!?!?!? I can't seem to see the forest for all those angle brackets .....
Use the /verbosity:d switch to get a full view of all the targets that were performed and their rough reason for being called (dependent-on). Identify the exact thing you want to be before or after or dependent upon. Besides using the depends attributes on your Target, there are also various properties that are used to collect dependencies for various purposes. You can identify these by using /preprocess and then looking up the Targets that catch your eye from the previous step.
I've found that specific answers often don't work, as the build situation is different for my language or the exact inclusion order matters or other minor things; so this is how I've found the real triggers in my case.
What I've done in the end:
inside my WiX setup project, in the myproject.wixproj MSBuild file, I've added a new custom task like this:
<UsingTask TaskName="GetThreePartVersion" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
<ParameterGroup>
<AssemblyPath ParameterType="System.String" Required="true" />
<ThreePartVersion ParameterType="System.String" Output="true" />
</ParameterGroup>
<Task>
<Using Namespace="System.Diagnostics" />
<Code Type="Fragment" Language="cs">
<![CDATA[
Log.LogMessage("Getting version details of assembly at: " + this.AssemblyPath, MessageImportance.High);
Version v = Version.Parse(FileVersionInfo.GetVersionInfo(this.AssemblyPath).FileVersion);
this.ThreePartVersion = v.ToString(3);
]]>
</Code>
</Task>
</UsingTask>
and then in the BeforeBuild target, I added these lines to call this task and define a WiX constant with the results:
<Target Name="BeforeBuild">
<GetThreePartVersion AssemblyPath="$(SolutionDir)Plugin\$(OutputPath)Swisscom.Vidia.Plugin.dll">
<Output TaskParameter="ThreePartVersion" PropertyName="ThreePartVersionNumber" />
</GetThreePartVersion>
<PropertyGroup>
<DefineConstants>ThreePartBuildVersion=$(ThreePartVersionNumber)</DefineConstants>
</PropertyGroup>
<Message Text="Three-part version: $(ThreePartVersionNumber)" />
</Target>
and now in my WiX project.wxs file, I can reference that constant that's been defined, and use it for the <Product Version="..." ... /> attribute:
<Product Id="*" Name="MyProject" Language="1033"
Version="$(var.ThreePartBuildVersion)" ......>
It took a bit of twiddling and a lot of trial & mostly error until I finally got it right - but this is the way it works for me now. Hope this might help some other soul some day....
Consider these two MSBuild files. First build-script.xml:
<?xml version="1.0" encoding="utf-8"?>
<!-- build-script.xml -->
<Project
ToolsVersion="4.0"
DefaultTargets="Build"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<OutputName>OutputName</OutputName>
</PropertyGroup>
<Import Project="build-script-to-import.xml"/>
</Project>
and then build-script-to-import.xml:
<?xml version="1.0" encoding="utf-8"?>
<!-- build-script-to-import.xml -->
<Project
ToolsVersion="4.0"
DefaultTargets="Build"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<OutputPath>Path\$(OutputName)</OutputPath>
</PropertyGroup>
<Target Name="Build">
<Message Text="$(OutputPath)" />
</Target>
</Project>
These two files represents a very simplified C# project file (build-script.xml) referencing a common targets file (build-script-to-import.xml). That means that it is not an option to change the build-script-to-import.xml file. The problem is that the OutputName is calculated, and I need to set it to this calculated value before I import the build-script-to-import.xml file. Do you know how I can accomplish this?
NOTE 1: The calculated value is in fact an assembly version which I am able to fetch from an assembly file with the GetAssemblyIdentity MSBuild task. The problem as I see it is that I may not call a target before the import happens. I would like to set the value for property OutputName with the help of the GetAssemblyIdentity MSBuild task.
NOTE 2: The build will be triggered by Visual Studio, so I may not use a batch file and send a property to MSBuild as a command line argument.
Move the declaration of $(OutputPath) inside your "Build" target. The only way to use "calculated" properties is to have them evaluated inside a target. Globally declared "static" properties will always be evaluated before any target runs, and presumably you need to run a target to calculate the value of $(OutputName). Just be sure your name calculating target runs before the "Build" target.
If Corresponding targets from imported project allows to modify DependsOnTargets
<Target Name="Build" DependsOnTargets="#(BuildDependsOn)" />
you can modify Item and add your target wich will prepare needed properties.
If it is not possible you can write wrapper to call underlying target/targets (Build/Rebuild/Clean) by yourself (without importing build-script-to-import.xml):
build-script.xml
<Target Name="Build" DependsOn="PrepareProperties">
<MSBuild Projects="build-script-to-import.xml"
Targets="Build"
Properties="OutputName=$(OutputName);$(OtherProperties)"/>
</Target>
Suggestion
Depends on your requirement to dynamically change OutputName there may be mach simpler solution. Just not to do it. It is rather simpler to write your own custom build step to copy from fixed output directory to your destination wich is calculated using your rules.
For example, I prefer practice when I have directory called latest with fixed path and a directory for each version of product. Every one can take latest version from that folder.
In Visual Studio 2010, you know how you can change your configuration (debug, release, etc), right-click a project in the solution explorer, click publish, and have all the important web app project files for the selected configuration copied to a target folder along with an xdt-transformed web.config? Well, I am looking for the MSBUILD equivalent of exactly that.
My challenge to you: Provide the ONE LINE that I need to execute at my command prompt in order to accomplish this. No third party programs. No tutorial videos. Just a single, straight-up command that I can literally copy from your response, paste into a command window, modify as necessary to support my directory structure, and then hit enter.
If not, then perhaps someone could provide a link to a complete MSBUILD reference showing every command, switch, and value I can use at the command line.
Put the below to ProjectPublish.MSBuild.xml file (change PropertyGroup as needed):
<?xml version="1.0" encoding="utf-8" ?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Publish">
<PropertyGroup>
<ProjectFile>Path\To\Web.csproj</ProjectFile>
<PublishDir>Path\For\Publish\Output</PublishDir>
<TempDir>Path\To\Temp\Folder</TempDir>
<BuildConfig>Release|Debug</BuildConfig>
</PropertyGroup>
<Target Name="Publish">
<MSBuild Projects="$(ProjectFile)"
Properties="Configuration=$(BuildConfig);WebProjectOutputDir=$(PublishDir);OutDir=$(TempDir)\;BuildingProject=true"
Targets="ResolveReferences;_CopyWebApplication" />
</Target>
</Project>
Calling this from command line (or .bat file) should do the trick:
%windir%\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe ProjectPublish.MSBuild.xml
I found the solution I was looking for after all these months here
In case the above link goes bad, here's the skinny of what it says:
Unload then edit your project file. Look for the line where it's importing Microsoft.WebApplication.targets. Will look like:
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" />
Beneath that line, paste in this XML:
<Target Name="PublishToFileSystem" DependsOnTargets="PipelinePreDeployCopyAllFilesToOneFolder">
<Error Condition="'$(PublishDestination)'==''" Text="The PublishDestination property must be set to the intended publishing destination." />
<MakeDir Condition="!Exists($(PublishDestination))" Directories="$(PublishDestination)" />
<ItemGroup>
<PublishFiles Include="$(_PackageTempDir)\**\*.*" />
</ItemGroup>
<Copy SourceFiles="#(PublishFiles)" DestinationFiles="#(PublishFiles->'$(PublishDestination)\%(RecursiveDir)%(Filename)%(Extension)')" SkipUnchangedFiles="True" />
</Target>
Now, run this in a command prompt within the same folder as your project file:
msbuild TestWebApp.csproj "/p:Platform=AnyCPU;Configuration=Debug;PublishDestination=C:\pub" /t:PublishToFileSystem
Remember to specify the path to MSBUILD in the command or add the path to your global path environmental variable (which is what I did). On my machine, it was here:
C:\Windows\Microsoft.NET\Framework\v4.0.30319
To test this, I put a config transform in my Web.Release.config to add an AppSetting key (if you do this, make sure the AppSettings node is present in your base config file or you will get an error). When I used the above command to build the debug configuration, the key was not present in the published config file as expected. However, when I used the release config, the key was successfully added to the file.
I really wish Microsoft hadn't obfuscated the heck out of this. At any rate, this is the simplest solution I have found anywhere on the internet. I hope it helps the rest of you.