MsBuild not generating PDB files in Release configuration - msbuild

<MSBuild Projects="$(ProjectFile)" Targets="_WPPCopyWebApplication;"
Properties="OutDir=..\publish;Configuration=Release;Platform=AnyCPU" />
I am using above script to publish Asp.Net project. In the project settings, I have absolutely made sure debug symbols are generated in release mode. Still MsBuild is not generating the pdb files in the output.
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>Full</DebugType>
<DefineDebug>false</DefineDebug>
<DefineTrace>true</DefineTrace>
<Optimize>true</Optimize>
<OutputPath>bin\</OutputPath>
<DocumentationFile>WebProject.xml</DocumentationFile>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>

After looking at the Microsoft.Web.Publishing.targets source, I have found a variable (ExcludeGeneratedDebugSymbol) being set to True in Release mode. From the comments, it looks like they wanted to exclude symbols from WebSite project, but the condition is not properly set for WebApplication project.
So, I have decided to override my build scrip from the caller arguments and it worked like a charm. I have not yet ascertained any side affects it may cause or using the undocumented property for future stability, but it works for now.
From the Microsoft.Web.Publishing.target file
<!--For website we will always exclude debug symbols from publishing unless it is set explicitly by user in website publish profile-->
<ExcludeGeneratedDebugSymbol Condition="'$(ExcludeGeneratedDebugSymbol)'=='' And '$(_WebProjectType)' == 'WebSite'">True</ExcludeGeneratedDebugSymbol>
<ExcludeGeneratedDebugSymbol Condition="'$(ExcludeGeneratedDebugSymbol)'=='' And '$(Configuration)' == 'Release'">True</ExcludeGeneratedDebugSymbol>
<ExcludeGeneratedDebugSymbol Condition="'$(ExcludeGeneratedDebugSymbol)'==''">False</ExcludeGeneratedDebugSymbol>
I have updated my script as follows.
<MSBuild Projects="$(ProjectFile)" Targets="_WPPCopyWebApplication;"
Properties="OutDir=..\publish;Configuration=Release;Platform=AnyCPU"; ExcludeGeneratedDebugSymbol=false />

You could also updated your publish profile (.pubxml) file to include that property value. I had to do this today with the new build bits in TFS Build 2015 to have the web publishing include the .pdb files. See example contents of file with property added to bottom.
<?xml version="1.0" encoding="utf-8"?>
<!--
This file is used by the publish/package process of your Web project. You can customize the behavior of this process
by editing this MSBuild file. In order to learn more about this please visit http://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<WebPublishMethod>FileSystem</WebPublishMethod>
<SiteUrlToLaunchAfterPublish />
<publishUrl>C:\Publish</publishUrl>
<DeleteExistingFiles>True</DeleteExistingFiles>
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
<LastUsedPlatform>Any CPU</LastUsedPlatform>
<ExcludeApp_Data>False</ExcludeApp_Data>
<LaunchSiteAfterPublish>False</LaunchSiteAfterPublish>
<ExcludeGeneratedDebugSymbol>false</ExcludeGeneratedDebugSymbol>
</PropertyGroup>
</Project>

You can put this directly in your *.csproj file, as the last property group section (right before the Import elements):
<PropertyGroup>
<ExcludeGeneratedDebugSymbol Condition="$(DebugSymbols) == true">false</ExcludeGeneratedDebugSymbol>
</PropertyGroup>

Related

Visual Studio 2017 Always Deploying in Development Mode

I have an ASP.NET Core application that I'm attempting to perform web deploy to a server and no matter what build configuration I select in the profile wizard, it always deploys multiple appsettings files as well as the PDB files for the DLLs. Anyone know what could be causing this?
If you want to exclude .pdb files for release publishing, then you may disable their generation during release build by adding next property to .csproj file (found this here)
<PropertyGroup>
<DebugType Condition=" '$(Configuration)' == 'Release' ">None</DebugType>
</PropertyGroup>
Regarding any configuration (or not) file, it will be published based on CopyToPublishDirectory attribute value. So again, you may use Condition attribute and have something like this (just an idea):
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<Content Update="appsettings.debug.json" CopyToPublishDirectory="Always"/>
<Content Update="appsettings.release.json" CopyToPublishDirectory="Never"/>
</ItemGroup>
<ItemGroup Condition=" '$(Configuration)' == 'Release' ">
<Content Update="appsettings.debug.json" CopyToPublishDirectory="Never"/>
<Content Update="appsettings.release.json" CopyToPublishDirectory="Always"/>
</ItemGroup>
But in general, it's better to depend on environment setting (Dev, Prod) when we are talking about configs.

MSBuild, OutputPath to a lib directory is not honoured

I spent hours now but I simply don't get it:
Why is a lib sub directory not honoured by the VS "fast up-to-date check"?
If a lib output dir for libraries is set, the solution is always rebuild - if changes have been made or not does not matter. If \lib sub dir is removed it works. Why?
Here is what I tested so far:
Refer to the next code snippet. That one works perfectly. If several dependent project are asked to build multiple times they actually build only once if no changes have been made. The Visual Studio FastUpToDateCheck kicks in.
But if you change the line
<OutputPath>$(SolutionDir)bin\$(Configuration)\$(Platform)</OutputPath>
to
<OutputPath>$(SolutionDir)bin\$(Configuration)\$(Platform)\lib\</OutputPath>
it constantly rebuilds. Any ideas why?
ComponentBuild.props located next to .sln file
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<IntermediateOutputPath>$(SolutionDir)obj\$(Configuration)\$(MSBuildProjectName)\</IntermediateOutputPath>
<UseCommonOutputDirectory>False</UseCommonOutputDirectory>
<DisableFastUpToDateCheck>false</DisableFastUpToDateCheck>
</PropertyGroup>
<PropertyGroup Condition=" '$(OutputType)' == 'Library' ">
<!-- To distinguish by \lib\ does not work, a rebuild is triggered since the up-to-date check fails -->
<!-- <OutputPath>$(SolutionDir)bin\$(Configuration)\$(Platform)\lib\</OutputPath> -->
<OutputPath>$(SolutionDir)bin\$(Configuration)\$(Platform)</OutputPath>
<OutDir>$(OutputPath)</OutDir>
</PropertyGroup>
<PropertyGroup Condition=" '$(OutputType)' == 'Exe' ">
<OutputPath>$(SolutionDir)bin\$(Configuration)\$(Platform)\</OutputPath>
<OutDir>$(OutputPath)</OutDir>
</PropertyGroup>
</Project>
The file is included in csproj files just before Import Microsoft.CSharp.targets:
.csproj file:
<!-- position of include is important, OutputType of project must be defined already -->
<Import Project="$(SolutionDir)ComponentBuild.props" Condition="Exists('$(SolutionDir)ComponentBuild.props')" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
The behaviour becomes more weird, the more I test.
I created two simple library projects A and B. B depends on A. I added above mentioned import and the FastUpToDateCheck works.
After adding lib path to the library outputtype, it works when nothing else is changed. But when lib B project is cleaned, every subsequent builds do rebuild project B.
When adding lib path to the exe outputtype as well. The FastUpToDateCheck works again.
Then I removed the lib path again from output type exe, but the FastUpToDateCheck surprisingly still works - always. Even when cleaning the build, changing a class or deleting all obj and bin folders.
BUT as soon as I removed the lib path from the lib outputtype as well, i.e. I set back all to the original state, it FAILS. It rebuilds every time. The first line of the diagnostic output is
Project 'ClassLibrary1' is not up to date. Missing output file
'c:\Users\hg348\Documents\Visual Studio
2015\Projects\BuildTest\bin\Debug\AnyCPU\lib\ClassLibrary1.dll'
It still looks into lib path even though it isn't set any more.
I think there is some nasty caching involved.
Can someone please verify this?
Well, my tests as described above lead to the answer:
It is caching in Visual Studio (VS) which triggers the builds after changing the output path. After making changes to the outputpath and probably outdir as well, Visual Studio still looks in the old directory for its FastUpToDateCheck.
Closing and reopening the Solution helps already to clear the VS cache. In some cases it is necessary to delete the hidden file .suo in hidden folder .vs
This solves all problems stated in the sample file given in the question above.
My final import file looks like this:
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Note that VS caches settings, to be sure the FastUpToDateCheck works
* reopen the solution after
- changing properties
- after adding a platform config
- after adding references to projects
* close VS and remove the hidden file
<solution folder>\.vs\<solution name>\v14\.suo after changing IntermediateOutputPath,
(You have to enable "how hidden files" in windows file explorer)
* After updating App.config do a random change in any .cs source file too,
otherwise FastUpToDateCheck fails
-->
<PropertyGroup>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<IntermediateOutputPath>$(SolutionDir)obj\$(Configuration)\$(Platform)\$(MSBuildProjectName)\</IntermediateOutputPath>
<!-- if true, don't copy output files of referenced assemblies, since everything builds to the same folder. -->
<UseCommonOutputDirectory>true</UseCommonOutputDirectory>
<DisableFastUpToDateCheck>false</DisableFastUpToDateCheck>
</PropertyGroup>
<PropertyGroup Condition=" '$(OutputType)' == 'Library' ">
<OutputPath>$(SolutionDir)bin\$(Configuration)\$(Platform)\lib\</OutputPath>
<OutDir>$(OutputPath)</OutDir>
</PropertyGroup>
<PropertyGroup Condition=" '$(OutputType)' == 'Exe' ">
<OutputPath>$(SolutionDir)bin\$(Configuration)\$(Platform)\</OutputPath>
<OutDir>$(OutputPath)</OutDir>
</PropertyGroup>
<!-- sets "Copy Local" property of references to false on reopen of solution
don't copy output files of referenced assemblies, since everything builds to the same folder -->
<ItemDefinitionGroup>
<Reference>
<Private>False</Private>
</Reference>
<ProjectReference>
<Private>False</Private>
</ProjectReference>
</ItemDefinitionGroup>
</Project>

How do I get an msbuild task to do config transforms on a collection of files?

I am trying to transform all of the web.config files in a project I have, here's a my tree structure:
Transform.bat
Transforms
ConfigTransform.proj
Web.Transform.config
Website
web.config
Views
web.config
There's more web.config files, but the idea is that this will find all of them and apply the same config transform on them.
I've taken a few hints from a blog post I found but I get stuck in the last step, the actual transformation. Also there's a bit of a rough part in the middle that I don't really like (I don't quite understand what I'm doing and I'm obviously doing it wrong). Here's where I am so far:
<Project ToolsVersion="4.0" DefaultTargets="Transform" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<UsingTask TaskName="TransformXml" AssemblyFile="Tools\Microsoft.Web.Publishing.Tasks.dll"/>
<PropertyGroup>
<SitePath>..\..\Website</SitePath>
<WebConfigTransformInputFile>$(SitePath)\Web.config</WebConfigTransformInputFile>
<WebConfigTransformFile>Web.Transform.config</WebConfigTransformFile>
<OutDir>..\N\N\</OutDir>
</PropertyGroup>
<ItemGroup>
<_FilesToTransform Include="$(SitePath)\**\web.config"/>
</ItemGroup>
<Target Name="Transform">
<MakeDir Directories="#(_FilesToTransform->'$(OutDir)%(RelativeDir)')" />
<TransformXml Source="#(_FilesToTransform->'$(OutDir)%(RelativeDir)%(Filename)%(Extension)')"
Transform="$(WebConfigTransformFile)"
Destination="#(_FilesToTransform->'$(OutDir)%(RelativeDir)%(Filename)%(Extension)')" />
</Target>
</Project>
My Transform.bat looks like this:
%systemroot%\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe %CD%\Transforms\ConfigTransform.proj
So when I call the batch, the appropriate directories get created. However, as you can see I've had to be a little creative with the OutDir, making it ..\N\N. For some reason, if I don't do this the OutDir path will be exactly the same as the input directory. So I obviously need to change something in MakeDir but I'm not sure what.
The real problem comes when it starts to do the transforms. I've tried to keep the TransformXml Source parameter like this or like so:
#(_FilesToTransformNotAppConfig->'%(FullPath)')
The latter gives me an error "Could not open Source file: The given path's format is not supported." and the former gives me this output:
Build started 30-4-2012 14:02:48.
Project "D:\Dev\transform\DoTransforms\Transforms\ConfigTransform.proj" on node 1 (default targets).
Transform:
Creating directory "..\N\N\..\..\Website\Views\".
Transforming Source File: ..\N\N\..\..\Website\Views\Web.config;..\N\N\..\..\Website\Web.config
D:\Dev\transform\DoTransforms\Transforms\ConfigTransform.proj(32,2): error : Could not open Source file: Could not find a part of the path 'D:\Dev\transform\DoTransforms\Website\Views\Web.config;\Website\Web.config'.
Transformation failed
Done Building Project "D:\Dev\transform\DoTransforms\Transforms\ConfigTransform.proj" (default targets) -- FAILED.
Build FAILED.
To summarize my questions:
How do I avoid the path issue for the OutDir? I've fiddled with multiple paths but I can't get it right.
How do I get the TransformXml task to accept multiple files in the Source attribute?
I think you were pretty close. I have pasted a sample below which shows how to do this.
In my sample I discover the transform sitting next to the web.config file itself. For your scenario you can just use an MSBuild property pointing to a specific file.
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="TransformAll" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll"/>
<PropertyGroup>
<Configuration Condition=" '$(Configuration)'=='' ">Release</Configuration>
<OutputFolder Condition=" '$(OutputFolder)'=='' ">C:\temp\transformed-files\</OutputFolder>
</PropertyGroup>
<!--
This target shows how to transform web.config with a specific transform file associated to that specific web.config file.
-->
<Target Name="TransformAll">
<!-- discover the files to transform -->
<ItemGroup>
<FilesToTransofm Include="$(MSBuildProjectDirectory)\**\web.config"/>
</ItemGroup>
<!-- Ensure all target directories exist -->
<MakeDir Directories="#(FilesToTransofm->'$(OutputFolder)%(RecursiveDir)')"/>
<!-- TransformXml only supports single values for source/transform/destination so use %(FilesToTransofm.Identity)
to sned only 1 value to it -->
<TransformXml Source="%(FilesToTransofm.Identity)"
Transform="#(FilesToTransofm->'%(RecursiveDir)web.$(Configuration).config')"
Destination="#(FilesToTransofm->'$(OutputFolder)%(RecursiveDir)web.config')" />
</Target>
</Project>
FYI you can download a full sample at https://github.com/sayedihashimi/sayed-samples/tree/master/TransformMultipleWebConfigs.

Mulitargeting C# project files with Mono and MonoDevelop

I have a collection of csproj files that all refer to the same set of source files, but have slightly different target data making it so I need to keep the project files separate. E.g. there are WinPhone, XBox, Desktop, MonoTouch variants of the same project.
For things that really are duplicated, like the list of .cs files to compile, I'd like to consolidate the list into a single file so I don't keep having to make sure that all variations are kept in sync. I originally tried doing this by removing the sources from the .csprojs and putting them into a .targets file that got imported by all the csprojs, but that made the source files disappear from both VS and MonoDevelop.
My second attempt was by making the Desktop csproj file the primary one, and letting all the variations import that csproj with some conditional logic. This keeps the source files editable from the main csproj and they build into all flavors. Now Visual Studio understands what I was trying to do but MonoDevelop can't build it. In the MonoDevelop solution the iOS version of the core DLL is grayed out and says "(not built in active configuration)"
I've also tried xbuilding the csproj and solution, which seems to get past the problems that MonoDevelop has but hiccups on other things related to resolving monotouch assemblies. I had thought MonoDevelop used xbuild, but maybe not?
Since this works in the Windows msbuild it seems like it's either a bug or a not supported feature in Mono. Or maybe there's a better way to tackle the whole scenario... Thought I'd ask here.
For specifics,
My Core.iOS.csproj file looks like this:
<?xml version="1.0" encoding="utf-8"?>
<Project
xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
DefaultTargets="Build"
ToolsVersion="4.0" >
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>10.0.0</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{AE37B15F-F4BE-48DE-9F20-F00A601EC89E}</ProjectGuid>
<ProjectTypeGuids>{6BC8ED88-2882-458C-8E55-DFD12B67127B};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<AssemblyName>Core.iOS</AssemblyName>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="monotouch" />
</ItemGroup>
<Import Project=".\Core.csproj" />
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>
And my Core.csproj file looks like this:
<?xml version="1.0" encoding="utf-8"?>
<Project
xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
DefaultTargets="Build"
ToolsVersion="4.0">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Core</RootNamespace>
</PropertyGroup>
<!-- Using AssemblyName's presence to check for whether this is imported. -->
<PropertyGroup Condition=" '$(AssemblyName)' == '' ">
<ProductVersion>8.0.50727</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{FC495BD8-11B1-46B0-A9DE-F245A0CBEE94}</ProjectGuid>
<AssemblyName>Core</AssemblyName>
<SignManifests>false</SignManifests>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<!-- properties similar to Debug -->
</PropertyGroup>
<ItemGroup Condition=" '$(Platform)' == 'AnyCPU' ">
<Reference Include="System" />
<Reference Include="System.Core" />
</ItemGroup>
<Import Condition=" '$(AssemblyName)' == 'Core' " Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<Compile Include="[...]" />
<Compile Include="[...]" />
</Project>
And, like I said, a variation of this seems to be working correctly when using VS Express for WinPhone and XBox360 projects. Is this something that should work? Is there a better way to do this?
Thanks,
Short answer:
This won't work because although MonoDevelop uses the MSBuild file format, it doesn't use the real MSBuild/xbuild engine for all project types yet. I'd suggest using links instead of an include.
Full background:
MonoDevelop has an old internal build engine that's derived from the SharpDevelop 1.0 build engine, i.e it predates the existence of MSBuild. We're in the process of migrating to MSBuild, but this has taken several stages, and is not yet complete.
A few years ago, MonoDevelop switched its project file format to a Visual Studio compatible subset of MSBuild. This was done by serializing/deserializing known MSBuild properties and items into MD's internal project model, but doing the build using the old build engine. This meant that any MSBuild projects that only used features accessible from the Visual Studio UI worked fine. However, it did not support the more advanced MSBuild features that are only accessible by hand-editing the MSBuild XML.
Later MD gained experimental support for using the xbuild/MSBuild build engine, but at the time xbuild was not mature, and it did not have MSBuild targets for all project types. It remained experimental, and build code for new project types (MonoTouch, etc) was written using the MD internal build engine.
Mono for Android needed to be supported in Visual Studio, so had to have MSBuild targets. Instead of writing and maintaining build code for two build engines, we finished up xbuild and MonoDevelop's MSBuild engine integration so it could be used for Mono for Android projects. However, we could not enable the xbuild build engine by default in MD, since many other project types did not yet have xbuild targets. Instead, we allowed project addins to force the use of the xbuild engine on a per-project-type basis.
This is essentially the current state - the xbuild engine is used for Mono for Android projects, and newer project types such as iPhone Binding projects and PLP projects, and is recommended for new project types. But older project types such as MonoTouch, MonoMac, ASP.NET etc have not yet been migrated at the time of writing.

Replace .sln with MSBuild and wrap contained projects into targets

I'd like to create a MSBuild project that reflects the project dependencies in a solution and wraps the VS projects inside reusable targets.
The problem I like solve doing this is to svn-export, build and deploy a specific assembly (and its dependencies) in an BizTalk application.
My question is: How can I make the targets for svn-exporting, building and deploying reusable and also reuse the wrapped projects when they are built for different dependencies?
I know it would be simpler to just build the solution and deploy only the assemblies needed but I'd like to reuse the targets as much as possible.
The parts
The project I like to deploy
<Project DefaultTargets="Deploy" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ExportRoot Condition="'$(Export)'==''">Export</ExportRoot>
</PropertyGroup>
<Target Name="Clean_Export">
<RemoveDir Directories="$(ExportRoot)\My.Project.Dir" />
</Target>
<Target Name="Export_MyProject">
<Exec Command="svn export svn://xxx/trunk/Biztalk2009/MyProject.btproj --force" WorkingDirectory="$(ExportRoot)" />
</Target>
<Target Name="Build_MyProject" DependsOnTargets="Export_MyProject">
<MSBuild Projects="$(ExportRoot)\My.Project.Dir\MyProject.btproj" Targets="Build" Properties="Configuration=Release"></MSBuild>
</Target>
<Target Name="Deploy_MyProject" DependsOnTargets="Build_MyProject">
<Exec Command="BTSTask AddResource -ApplicationName:CORE -Source:MyProject.dll" />
</Target>
</Project>
The projects it depends upon look almost exactly like this (other .btproj and .csproj).
Wow, this is a loaded question for a forum post. I wrote about 20 pages on creating reusable .targets files in my book, but I'll get you started here with the basics here. I believe that the key to creating reusable build scripts (i.e. .targets files) is three elements:
Place behavior (i.e. targets) into separate files
Place data (i.e. properties and items, these are called .proj files) into their own files
Extensibility
.targets files should validate assumptions
The idea is that you want to place all of your targets into separate files and then these files will be imported by the files which will be driving the build process. These are the files which contain the data. Since you import the .targets files you get all the targets as if they had been defined inline. There will be a silent contract between the .proj and .targets files. This contract is defined in properties and items which both use. This is what needs to be validated.
The idea here is not new. This pattern is followed by .csproj (and other projects generated by Visual Studio). If you take a look your .csproj file you will not find a single target, just properties and items. Then towards the bottom of the file it imports Microsoft.csharp.targets (may differ depending on project type). This project file (along with others that it imports) contains all the targets which actually perform the build.
So it's layed out like this:
SharedBuild.targets
MyProduct.proj
Where MyProdcut.proj might look like:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- This uses a .targets file to off load performing the build -->
<PropertyGroup>
<Configuration Condition=" '$(Configuration)'=='' ">Release</Configuration>
<OutputPath Condition=" '$(OutputPath)'=='' ">$(MSBuildProjectDirectory)\BuildArtifacts\bin\</OutputPath>
</PropertyGroup>
<ItemGroup>
<Projects Include="$(MSBuildProjectDirectory)\..\ClassLibrary1\ClassLibrary1.csproj"/>
<Projects Include="$(MSBuildProjectDirectory)\..\ClassLibrary2\ClassLibrary2.csproj"/>
<Projects Include="$(MSBuildProjectDirectory)\..\ClassLibrary3\ClassLibrary3.csproj"/>
<Projects Include="$(MSBuildProjectDirectory)\..\WindowsFormsApplication1\WindowsFormsApplication1.csproj"/>
</ItemGroup>
<Import Project="SharedBuild.targets"/>
</Project>
And SharedBuild.targets might look like:
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- This represents a re-usable build file -->
<Target Name="SharedBuild_Validate">
<!-- See http://sedodream.com/2009/06/30/ElementsOfReusableMSBuildScriptsValidation.aspx for more info
about this validation pattern
-->
<ItemGroup>
<_RequiredProperties Include ="Configuration">
<Value>$(Configuration)</Value>
</_RequiredProperties>
<_RequiredProperties Include ="OutputPath">
<Value>$(OutputPath)</Value>
</_RequiredProperties>
<_RequiredItems Include="Projects">
<RequiredValue>%(Projects.Identity)</RequiredValue>
<RequiredFilePath>%(Projects.Identity)</RequiredFilePath>
</_RequiredItems>
</ItemGroup>
<!-- Raise an error if any value in _RequiredProperties is missing -->
<Error Condition="'%(_RequiredProperties.Value)'==''"
Text="Missing required property [%(_RequiredProperties.Identity)]"/>
<!-- Raise an error if any value in _RequiredItems is empty -->
<Error Condition="'%(_RequiredItems.RequiredValue)'==''"
Text="Missing required item value [%(_RequiredItems.Identity)]" />
<!-- Validate any file/directory that should exist -->
<Error Condition="'%(_RequiredItems.RequiredFilePath)' != '' and !Exists('%(_RequiredItems.RequiredFilePath)')"
Text="Unable to find expeceted path [%(_RequiredItems.RequiredFilePath)] on item [%(_RequiredItems.Identity)]" />
</Target>
<PropertyGroup>
<BuildDependsOn>
SharedBuild_Validate;
BeforeBuild;
CoreBuild;
AfterBuild;
</BuildDependsOn>
</PropertyGroup>
<Target Name="Build" DependsOnTargets="$(BuildDependsOn)"/>
<Target Name="BeforeBuild"/>
<Target Name="AfterBuild"/>
<Target Name="CoreBuild">
<!-- Make sure output folder exists -->
<PropertyGroup>
<_FullOutputPath>$(OutputPath)$(Configuration)\</_FullOutputPath>
</PropertyGroup>
<MakeDir Directories="$(_FullOutputPath)"/>
<MSBuild Projects="#(Projects)"
BuildInParallel="true"
Properties="OutputPath=$(_FullOutputPath)"/>
</Target>
</Project>
Don't look too much at the SharedBuild_Validate target yet. I put that there for completeness but don't focus on it. You can find more info on that at my blog at http://sedodream.com/2009/06/30/ElementsOfReusableMSBuildScriptsValidation.aspx.
The important parts to notice are the extensibility points. Even though this is a very basic file, it has all the components of a reusable .targets file. You can customize it's behavior by passing in different properties and items to build. You can extend it's behavior by overriding a target (BeforeBuild, AfterBuild or even CoreBuild) and you can inject your own targets into the build with:
<Project ...>
...
<Import Project="SharedBuild.targets"/>
<PropertyGroup>
<BuildDependsOn>
$(BuildDependsOn);
CustomAfterBuild
</BuildDependsOn>
</PropertyGroup>
<Target Name="CustomAfterBuild">
<!-- Insert stuff here -->
</Target>
</Project>
In your case I would create an SvnExport.targets file which uses the required properties:
SvnExportRoot
SvnUrl
SvnWorkingDirectory
You will use these properties to do the Export.
Then create another one for Biztalk build and deploy. You could split this up into 2 if necessary.
Then inside of your .proj file you just import both and setup the targets to build in the right order, and your off.
This is only really the beginning of creating reusable build elements, but this should get the wheels turning in your head. I am going to post all of this to my blog as well as download links for all files.
UPDATE:
Posted to blog at http://sedodream.com/2010/03/19/ReplacingSolutionFilesWithMSBuildFiles.aspx