I wish to start using MS-Build. I have lots of projects which I build manually (from Visual Studio) as of now. I want to automate build process and preferably from a machine onto which I don't wish to install Visual Studio. I started reading about MS-Build on MSDN. But I am yet to get a step by step guidance where to start and how to do. My questions are like:
How can I start MS-Build? Is there any download-able?
What is the first step?
How to create an MS-Build script?
And a lot similar questions. Can somebody guide me?
MS Build comes with the .NET Framework itself and the executable (msbuild.exe) is located in the .NET-framework directory, something like (depending on version):
C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319
C:\WINDOWS\Microsoft.NET\Framework\v3.5
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727
(The right version is also in %path% when using the "Visual Studio command prompt" from the start menu.)
MsBuild files are xml-files. You can start by making a new text file, lets say "c:\myscript.msbuild", and copy-paste this to the file:
<Project DefaultTargets="MyTarget" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="MyTarget">
<Message Text="Hello world!" Importance="high"/>
</Target>
</Project>
Then go to command prompt and type:
C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\msbuild.exe c:\myscript.msbuild
That is a good start. :)
Then you can customize the targets and properties.
Second example:
<Project DefaultTargets="All" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition="'$(MyCondition)' == 'x'" >
<MyProperty>World2</MyProperty>
</PropertyGroup>
<Target Name="MyTarget">
<Message Text="Hello" Importance="high"/>
<Message Text="$(MyProperty)" Importance="high"/>
</Target>
<Target Name="MyTarget2">
</Target>
<Target Name="All">
<CallTarget Targets="MyTarget" />
<CallTarget Targets="MyTarget2" />
</Target>
</Project>
C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\msbuild.exe c:\myscript.msbuild /target:mytarget /property:MyCondition=x
You can have also build files inside build-files.
<Project DefaultTargets="MyTarget" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="MyExternalProperties.msbuild"/>
<Target Name="MyTarget">
<Exec Command="echo Hello world 3"/>
</Target>
</Project>
MSBuild is similar to other build products like NAnt (just in case you've used one of those), but it is still different in a few respects.
Here is a good start page on MSDN. There are a truckload of different MSBuild task libraries released under various licences, most that i have seen are completely free to use and come with source code. Probably the two biggest are:
the open source MSBuild Community Tasks Project
the SDC Tasks Library on codeplex
Other good places to get info:
the MSBuild team blog
MSBuild Book
the blog of Sayed Ibrahim Hashimi (who is incredibly knowledgeable about the product but didn't work for MS until just recently). He also hangs out here on SO and may stumble across this question.
That should be enough to get started. If you can't find a task to do what you want, just write it yourseld - it is very easy.
Related
I have a solution that contains a console application with a .csproj file like the this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp1.1</TargetFramework>
</PropertyGroup>
</Project>
I also have a library project that uses the console application to generate a heap of C# code that get compiled into the library, the library .csproj file looks like this.
<Project Sdk="Microsoft.NET.Sdk" InitialTargets="RunGenerator">
<PropertyGroup>
<TargetFramework>netstandard1.4</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../generator/generator.csproj">
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference>
</ItemGroup>
<Target Name="RunGenerator">
<Exec Command="dotnet run -p "../generator/generator.csproj" input output" />
</Target>
</Project>
This fails because the dependency analysis says that a netstandard1.4 assembly cannot reference a netcoreapp1.1 assembly. That is correct except that I am not referencing the assembly.
I can work around that issue by building the generator project like this:
<Project Sdk="Microsoft.NET.Sdk" InitialTargets="RunGenerator">
<PropertyGroup>
<TargetFramework>netstandard1.4</TargetFramework>
</PropertyGroup>
<Target Name="RunGenerator">
<Exec Command="dotnet build "../generator/generator.csproj"" />
<Exec Command="dotnet run -p "../generator/generator.csproj" input output" />
</Target>
</Project>
The problem is that the generator project no longer takes part in the dependency analysis when these projects are built using the containing solution file and the explicit build of the generator project sometimes runs concurrently with another build of the same project initiated by the solution build and this results in errors because files are locked etc.
Is it possible to have a project dependency without checking the target framework?
Can anyone suggest a workaround?
Thanks.
Here are some MSBuild tips. You might need to combine a few of these ideas.
You can use your solution file to add an explicit project dependency. See https://learn.microsoft.com/en-us/visualstudio/ide/how-to-create-and-remove-project-dependencies (This question was originally asked here: Visual Studio 2010: How to enforce build order of projects in a solution?). Unfortunately, this is really hard to do if you don't have VS. The format is .sln files is kinda a nightmare.
To avoid the concurrent build issue, use the MSBuild task instead of the Exec task. See https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-task
<Target Name="CompileAnotherProject">
<MSBuild Projects="../generator/generator.csproj" Targets="Build" />
</Target>
dotnet-run invokes "dotnet build" automatically. This is actually problematic in concurrent builds. You can instead add a target to your generator.csproj that runs the app after it has been built. "dotnet filepath.dll" runs the compiled app without building it.
<Target Name="RunCodeGen" AfterTargets="Build">
<Exec Command="dotnet $(AssemblyName).dll input output"
WorkingDirectory="$(OutDir)" />
</Target>
We are just getting started with TFS 2010 and migration from SVN and CruiseControl.NET to TFS.
With cruisecontrol.NET we have a powershell script that does everything: copying, modifying, compressing files.
Now my question is how we can integrate that script into the TFS build server? Modifying the solution or creating a custom msbuild file?
Also I would like to combine this with Web Packaging. Any idea how this can be accomplished?
My recommendation is to create a custom msbuild file. In this file call build of your solution and then call your powershell script. Like:
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="Build">
<!-- Compile whole solution in release mode -->
<MSBuild
Projects="MySolutionFile.sln"
Targets="ReBuild"
Properties="Configuration=Release" />
<Exec
Command=“command_for_run_cutom_script“
ContinueOnError="false"
WorkingDirectory="." />
</Target>
</Project>
However consider rewriting your powershell script fully to msbuild script. You will get better maintenance. Copying, modifying, compressing files… are no problem for msbuild.
http://tfsccnetplugin.codeplex.com/ has all the documentation you need in terms of Configuring CCNet with TFS, as for the web packaging...unfortunately someone else will have to help with that.
I'm trying to get Team City to build my .NET solution and run my nUnit tests.
I know I can modify the individual projects and tell them always run the unit tests. I don't want the unit tests to run when I click "build" in visual studio, but I do want the unit tests to run when Team City kicks off a msbuild task.
I tried "msbuild solutionname.sln" and gave team city the targets of "BUILD" and my custom build tag of "TEST". However, msbuild can't find any specified target when invoked against a sln solution. So, I ran msbuild to convert my solution into a project which has a target like this:
<Target Name="Build">
<MSBuild Projects="#(BuildLevel0)" >
</Target>
I naively thought I could write a new task like this:
<Target Name="BuildAndTest">
<CallTarget Targets="Build"/> <!-- This builds everything in solution -->
<CallTarget Targets="Test"/> <!-- DOES NOT WORK. This target exists in project that gets built by this solution -->
</Target>
The nunit target looks like this:
<Target Name="Test" DependsOnTargets="Build" Condition=" '$(Configuration)' == 'Release'">
<NUnit Assemblies="$(OutputPath)\Tsa.BaseTest.dll" ContinueOnError="false" ToolPath="C:\Program Files\NUnit 2.5.2\bin\net-2.0\" DisableShadowCopy="true" OutputXmlFile="$(OutputPath)\nunit-results.xml" />
</Target>
As you can see, it references OutputPath, which only the project knows--the solution doesn't have reference to $OutputPath, else I'd just put all the test targets into the "solution project".
Any suggestions on how I can get this to work?
I think you're making this a lot harder than it needs to be. TeamCity has built-in support for running NUnit unit tests after the build - you don't need to modify the MSBuild file at all. Just set up your Build Configuration (I think it's under Runner) to specify the version of NUnit and which assemblies are test assemblies.
NOTE: I checked and we have this under Runner: sln2008 (section NUnit Test Settings) in TeamCity Enterprise Version 4.5.4, but I don't see anything on the JetBrains site that states that it's specific to Enterprise. It may require a version upgrade, though. See TeamCity Testing Frameworks.
This is what finally worked. It is ignored by visual studio, msbuild will run this section correctly, and team city will as well, although it replaces the Target with its own an runtime (according to the build log).
TeamCity will "automatically" run nunit tests and display the results, only in the sense that it will do so after manually editing the msbuild file, doing numerous manual teaks and telling TeamCity where each assembly is and where each output file is.
<Project (snip) DefaultTargets="BuildAndTest" (snip)>
<Target Name="BuildAndTest">
<CallTarget Targets="Build" />
<CallTarget Targets="TestBase" />
</Target>
<Target Name="TestBase" DependsOnTargets="Build">
<NUnit Assemblies="Tsa.BaseTest\bin\RELEASE\Tsa.BaseTest.dll" ContinueOnError="false" ToolPath="C:\Program Files\NUnit 2.5.2\bin\net-2.0\" DisableShadowCopy="true" OutputXmlFile="$(SolutionDir)\Tsa.BaseTest\bin\RELEASE\nunit-results.xml" />
</Target>
</Target>
</Project>
I'm trying to make a batch file to publish the few ClickOnce application we have in one click. I'm using msbuild for that, and as an example the below command line shows how I'm doing it:
msbuild
MyApp.sln
/t:Publish
/p:Configuration=Release
/p:PublishUrl="C:\Apps\"
/v:normal > Log.txt
(wrapped for easier reading)
when I run the above command it builds and publish the application in the release directory, i.e. bin\release! Any idea why msbuild doesn't respect PublishUrl property in my example above?
PS: I tried also different combinations including remove 'Configuration', use 'Rebuild' and 'PublishOnly' as targets, and remove the the quotation marks but without any success.
You are setting the wrong property. Try PublishDir instead.
You can pass it into MSBuild as you are or you can set it in the project file (or maybe the sln file too, not sure I always use the project file.) like this
<PropertyGroup>
<PublishDir>C:\Dev\Release\$(BuildEnvironment)\</PublishDir>
</PropertyGroup>
I've just done a few blog posts on MsBuild and ClickOnce stuff, check it out you 'should' find them useful...
Some features are done by Visual-Studio and not by the MSBuild-script. So the click-once-deployment behaves differently when it's executed from the command-line.
The ApplicationRevision isn't increased with every build. This works only when is exectued from Visual Studio
In in somecases, the PublishUrl isn't used. Quote from MSDN:
For example, you could set the PublishURL to an FTP path and set the InstallURL to a Web URL. In this case, the PublishURL is only used in the IDE to transfer the files, but not used in the command-line builds. Finally, you can use UpdateUrl if you want to publish a ClickOnce application that updates itself from a separate location from which it is installed.
I've created a special MSBuild-file which does this things. It runs the publish-target and copies then the files to the right location.
An example of the build-file, as requested by alhambraeidos. It basically runs the regular VisualStudio-build and then copies the click-once data to the real release folder. Note that removed some project-specific stuff, so it's maybe broken. Furthermore it doesn't increase the build-number. Thats done by our Continues-Build-Server:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Publish" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<!-- the folder of the project to build -->
<ProjLocation>..\YourProjectFolder</ProjLocation>
<ProjLocationReleaseDir>$(ProjLocation)\bin\Release</ProjLocationReleaseDir>
<ProjPublishLocation>$(ProjLocationReleaseDir)\app.publish</ProjPublishLocation>
<!-- This is the web-folder, which provides the artefacts for click-once. After this
build the project is actually deployed on the server -->
<DeploymentFolder>D:\server\releases\</DeploymentFolder>
</PropertyGroup>
<Target Name="Publish" DependsOnTargets="Clean">
<Message Text="Publish-Build started for build no $(ApplicationRevision)" />
<MSBuild Projects="$(ProjLocation)/YourProject.csproj" Properties="Configuration=Release" Targets="Publish"/>
<ItemGroup>
<SchoolPlannerSetupFiles Include="$(ProjPublishLocation)\*.*"/>
<SchoolPlannerUpdateFiles Include="$(ProjPublishLocation)\Application Files\**\*.*"/>
</ItemGroup>
<Copy
SourceFiles="#(SchoolPlannerSetupFiles)"
DestinationFolder="$(DeploymentFolder)\"
/>
<Copy
SourceFiles="#(SchoolPlannerUpdateFiles)"
DestinationFolder="$(DeploymentFolder)\Application Files\%(RecursiveDir)"
/>
<CallTarget Targets="RestoreLog"/>
</Target>
<Target Name="Clean">
<Message Text="Clean project:" />
<MSBuild Projects="$(ProjLocation)/YourProject.csproj" Properties="Configuration=Release" Targets="Clean"/>
</Target>
</Project>
I'll put in my 2 cents, this syntax seems to work (right or wrong):
/p:publishUrl="C:\\_\\Projects\\Samples\\artifacts\\Web\\"
For me, the soultion was to escape the path.
Instead of:
/p:PublishUrl="C:\Apps\"
Put:
/p:PublishUrl="C:\\Apps\\"
I got a directory I want to copy to a number of locations.
Say I have
home.aspx
I want to copy it to
abc/home.aspx
def/home.aspx
ghi/home.aspx
so two questions for me:
How do I define the list abc, def, ghi?
How do I execute my Copy task with each element of this list?
Here is an actual example that I put together that shows what you were looking for:
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Test" ToolsVersion="3.5">
<!--Declare an ItemGroup that points to your file you want to copy.-->
<ItemGroup>
<ItemToCopy Include=".\Home.aspx" />
</ItemGroup>
<!--Declare an ItemGroup that points to your destination Locations-->
<ItemGroup>
<DestLocations Include=".\abc\home.aspx" />
<DestLocations Include=".\def\home.aspx" />
<DestLocations Include=".\ghi\home.aspx" />
</ItemGroup>
<Target Name="CopyFiles">
<!--Run the copy command to copy the item to your dest locations-->
<!--This is where the magic happens. The % sign before the DestLocations reference says to use
Batching. So Copy will be run for each unique FullPath MetaData in the DestLocations ItemGroup.-->
<Copy SourceFiles="#(ItemToCopy)" DestinationFolder="%(DestLocations.FullPath)" />
</Target>
</Project>
The concept that you should be interested in is known as Batching.
I've covered this exact scenario on my blog at http://www.sedodream.com/PermaLink,guid,5f1e0445-ce3d-4052-ba80-42fd19512d42.aspx
Here is the text of that blog entry, you can download the mentioned files at the link above.
Today someone was telling me about a co-worker who was having issues with MSBuild. He told me that he was trying to copy a set of files to a set of different servers. But the issue was that he didn’t know how to achieve this without performing multiple Copy task invocations. I told him that he could achieve this using MSBuild Batching. Batching is a process of performing a task (or target) on a set of items (batches) at a time. A batch can also include a single item. So in this scenario we need to perform the copy one time for each server that he wanted to deploy to. I’ve created a simple msbuild file which demonstrates this in two different ways. The first way uses task batching, which can bee seen in the Test target. And the other uses Target batching which can be seen in the DoItCore target. I've also created a clean target, which has nothing to do with batching.
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Test">
<ItemGroup>
<SourceFiles Include="*.txt"/>
<Dest Include="One;Two;Three;Four;Five"/>
</ItemGroup>
<Target Name="Test">
<Copy SourceFiles ="#(SourceFiles)" DestinationFolder="%(Dest.FullPath)"/>
<Message Text="Fullpath: %(Dest.FullPath)"/>
</Target>
<!-- These targets demonstrate target batching -->
<Target Name="DoIt" DependsOnTargets="DoItCore"/>
<Target Name="DoItCore" Inputs="#(SourceFiles)" Outputs="%(Dest.FullPath)">
<Copy SourceFiles="#(SourceFiles)" DestinationFolder="%(Dest.FullPath)"/>
</Target>
<!-- This will clean up the files -->
<Target Name="Clean">
<CreateItem Include="%(Dest.FullPath)\**\*">
<Output ItemName="FilesToDelete" TaskParameter="Include"/>
</CreateItem>
<Delete Files="#(FilesToDelete)"/>
</Target>
</Project>
Batching is an advanced topic of MSBuild, and is defintely neglected. I have to admit I’m guilty of not writing about it enough myself. There are some good batching resources, they are listed below.
Here are some other batching related blog entries that I've posted.
MSBuild Batching Part 1
MSBuild Batching Part 2
MSBuild Batching Part 3
MSBuild RE: Enforcing the Build Agent in a Team Build
Thanks,
Sayed Ibrahim Hashimi
My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build
You really are best off doing this yourself as a learning exercise, rather than treating MSBUILD as a magic box. This article from Patrick Smacchia gives you most of the techniques involved.
Have an itemgroup where you build up this list of destinations ("<Destination>abc</Destionation>..., etc). Then invoke the copy task with this list (#Destination).
I'm sure you'll find plenty of examples if you search for it. http://keithhill.spaces.live.com/?_c11_BlogPart_BlogPart=blogview&_c=BlogPart&partqs=cat%3dMSBuild