I have a pretty large complicated application that has a smart client front end in one solution and a web services layer in another solution. We use 2 team build definitions in TFS 2010 to build the solutions. We are still using MS Build scripts not the new workflow based templates.
How can we use a single build definition to build both solutions. We are open to either tricks inside MS Build scripts or moving to the new workflow templates.
You can simply set two Solutions to build by creating them in an item group if I am understanding what you are trying to do
<SolutionToBuild Include="$(BuildProjectFolderPath)/../../$(SuiteSourceBranchRoot)/MyCompany.Suite.sln">
<Targets></Targets>
<Properties></Properties>
</SolutionToBuild>
<SolutionToBuild Include="$(BuildProjectFolderPath)/../../$(SuiteSourceBranchRoot)/MyCompany.Another.sln">
<Targets></Targets>
<Properties></Properties>
</SolutionToBuild>
</ItemGroup>
You can also do it inside any build step with:
<!-- Build the deployment solution. -->
<MSBuild Projects="$(SolutionRoot)\$(SuiteSourceBranchRoot)/Company.Deployment.sln" Properties="Configuration=Release;" />
You can build multiple solutions from a single team build project definition by definitng multiple SolutionToBuild items in your TFSBuild.proj MSBuild project file.
<ItemGroup>
<SolutionToBuild Include="$(SolutionRoot)\A\A.sln" />
<SolutionToBuild Include="$(SolutionRoot)\B\B.sln" />
</ItemGroup>
You may also have to modify the build definition's workspace mapping to include sources for both solutions.
Related
In MSBuild there is a variable $(BuildingInsideVisualStudio) which can be used to detect whether build is running inside Visual Studio, so I can do conditions like this:
<PropertyGroup Condition="'$(BuildingInsideVisualStudio)' != 'true'">
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>
Is there anything similar for NuGet? I want different conditions to run if the project is being used inside package manager.
Your comment to the question makes it sound like your goal is to keep a packages versions consistent across different conditions in a single project, but it's also a common case that you want to keep it consistent across projects in a solution or repo.
I'm going to suggest a different solution. Create a Directory.Build.props in your repo root that looks something like this:
<Project>
<PropertyGroup>
<NewtonsoftJsonVersion>12.0.1</NewtonsoftJsonVersion>
<xunitVersion>2.4.1</xunitVersion>
</PropertyGroup>
</Project>
Now in your projects that need Newtonsoft.json, you change the PackageReference to <PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonVersion)" />.
If you put your production code in src\ and test code in test\, then you can create a test\Directory.Build.props with the contents:
<Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
<PropertyGroup>
<PackageReference Include="xunit" Version="$(xunitVersion)" />
</PropertyGroup>
</Project>
Now all of your projects under test\ will get xunit automatically, and it's guaranteed to be the same version.
When you want to upgrade a package version, you can use the Package Manager UI to check for versions, but unfortunately not to upgrade the version. For that, you'll need to manually edit the repo root Directory.Build.props (so add it to your solution for quick access), but you can be confident that every reference to that package will use the same version. It is limited to projects using PackageReference, there's no solution currently for packages.config, but MSBuild conditions only for for PackageReference too.
You can see this pattern often in Microsoft repositories. Certainly NuGet (my team, yay!), and various .NET repos like cli and sdk do it, although in manually imported props files, rather than Directory.Build.props, though the concept is the same.
There is no direct solution for the case. NuGet is just download manager, it loads sources. MSBuild is a build system, it builds sources. They don't exchange any information between.
I would suggest you to move an another way. You can add a props file into your nuget packaging project with
<?xml version="1.0" encoding="utf-8" ?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<ItemGroup>
<PackageUsedFromNuget>true</PackageUsedFromNuget>
</ItemGroup>
</Project>
Using Visual Studio 2013
I have a set of 8 SSDT projects that can all be deployed to several distinct environments. The advanced publish settings for each project, however, are meant to be identical. At present I have created a distinct publish profile for each environment, meaning I have ~20 publish profiles all using the exact same settings but different connection strings.
Tweaking the publish settings (which happens with some regularity as I am still a bit new to SSDT) for my databases is most annoying, but I have yet to find a way around this as I am unable to apply transforms to publish profiles like I can to web.config files in an ASP.NET project. I even tried installing the Visual Studio SlowCheetah plugin, but it doesn't appear to work with SSDT projects as the option to apply transform does not appear when right-clicking on a publish profile.
I don't want my team to have to think about entering connection details manually when deploying a DB to dev or QA environments. Is there any way to set a master publish profile or otherwise specify a group of shared settings so that I don't have to manage 20 nearly identical publish profiles??
EDIT:
Using SAS' answer I was able to cobble together the following XML for my .sqlproj file:
<PropertyGroup>
<PublishProfileDir>$(ProjectDir)Publish Profiles\</PublishProfileDir>
<TemplatePublishProfile>$(PublishProfileDir)Baseline\publish.xml</TemplatePublishProfile>
</PropertyGroup>
<Target Name="CopyXml" AfterTargets="Build">
<Copy SourceFiles="$(TemplatePublishProfile)" DestinationFolder="$(PublishProfileDir)Dev"/>
<Copy SourceFiles="$(TemplatePublishProfile)" DestinationFolder="$(PublishProfileDir)Qa"/>
</Target>
<ItemGroup>
<DevPublishUpdates Include="ConfigUpdates">
<XPath>/msb:Project/msb:PropertyGroup/msb:TargetDatabaseName</XPath>
<NewValue>CountyRecordsDev</NewValue>
</DevPublishUpdates>
<DevPublishUpdates Include="ConfigUpdates">
<XPath>/msb:Project/msb:PropertyGroup/msb:DeployScriptFileName</XPath>
<NewValue>CountyRecords.Dev.Sql</NewValue>
</DevPublishUpdates>
</ItemGroup>
<Target Name="UpdateXml" AfterTargets="CopyXml">
<Message Text="Editing Derived Xml Publish Profiles" Importance="high" />
<XmlPoke Namespaces="<NamespacePrefix='msb'Uri='http://schemas.microsoft.com/developer/msbuild/2003'/>"
XmlInputath="$(PublishProfileDir)Dev\publish.xml"
Query="%(DevPublishUpdates.XPath)"
Value="%(DevPublishUpdates.NewValue)" />
</Target>
The only downside is that I seem to need a separate folder for all my publish profiles in order to prevent one transform from overwriting another, I could not seem to find a way to simply overwrite an existing file. For XmlPoke, the namespaces attribute is critical to operation. I learned more about this process from this blog post by Sayed Ibrahim Hashimi.
We are using a template xml file that is copied automagically as a pre-step in the publish, for all our targets, so any changes need only be mande in the template. The target server name is replaced dynamically as the publish xml files are created. We also had to modify the xaml for this. We use Copy and XMLPoke tags in common proj-file thar is included in our proj-files. It takes some work, but works fine.
Edit: I have pasted in some code below to try to explain, it is only part of the original but I hope it is enough to get everyone started:
This part of what is in our common file (SQLCommonInclude.proj):
<Target Name="CreatePublishXMLFile">
<PropertyGroup>
<VersionNumber Condition="'$(VersionNumber)'==''">Local Build</VersionNumber>
<CurrentDate>$([System.DateTime]::Now.ToString(yyyy-MM-dd HH:mm:ss))</CurrentDate>
<SqlPublishProfilePath Condition="'$(SqlPublishProfilePath)'==''">Publish\$(TargetServerParam).publish.xml</SqlPublishProfilePath>
<TargetXMLFile>$(ProjectDir)Publish\$(TargetServerParam).publish.xml</TargetXMLFile>
<ChangeSets Condition="'$(ChangeSets)'==''">Unknown</ChangeSets>
</PropertyGroup>
<XmlPoke XmlInputPath="$(TargetXMLFile)" Query="/*[local-name()='Project']/*[local-name()='PropertyGroup']/*[local-name()='TargetConnectionString']" Value="Data Source=$(TargetServerParam)%3BIntegrated Security=True%3BPooling=False" />
<XmlPoke XmlInputPath="$(TargetXMLFile)" Query="/*[local-name()='Project']/*[local-name()='PropertyGroup']/*[local-name()='TargetDatabaseName']" Value="$(ProjectName)" />
<XmlPoke XmlInputPath="$(TargetXMLFile)" Query="/*[local-name()='Project']/*[local-name()='ItemGroup']/*[local-name()='SqlCmdVariable'][#Include='ChangeSets']/*[local-name()='Value']" Value="$(ChangeSets)" />
</Target>
Then call this repeatedly, for each target server:
<Target Name="CreateAllPublishXMLFiles">
<MSBuild Projects="$(MSBuildProjectFile)" Targets="CreatePublishXMLFile" Properties="TargetServerParam=OURSERVER1" />
<MSBuild Projects="$(MSBuildProjectFile)" Targets="CreatePublishXMLFile" Properties="TargetServerParam=OURSERVER2" />
</Target>
In each Project file we include and call the common code:
<Import Project="$(SolutionDir)SQLCommonInclude.proj" />
<Target Name="BeforeBuild" DependsOnTargets="CreateAllPublishXMLFiles">
Then, In a Post-deployment Script we set the Extended Properties like this:
IF NOT EXISTS (SELECT NULL FROM SYS.EXTENDED_PROPERTIES WHERE class_desc = 'DATABASE' AND name = 'SSDT ChangeSets')
EXEC sp_addextendedproperty #name = N'SSDT ChangeSets', #value = '';
EXEC sp_updateextendedproperty #name = N'SSDT ChangeSets', #value = '$(ChangeSets)';
Assuming the latest version of MSBuild, let's say I have 3 projects ProjA, ProjB and ProjC. I have a custom target in A and B that copy the individual outputs (bin items) into a custom path $(CustomOutputPath) - this all works fine individually. ProjC also has a custom target but in addition to copying its files to $(CustomOutputPath), it also cleans up the output path first, then chains ProjA and ProjB so that essentially all 3 projects have their files in the custom output path.
Let's assume I cannot change this requirement.
My ProjC target looks something like this:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Contains groups and properties only like
CustomOutputPath and BuildProjects -->
<Import Project="SharedGroups.msbuild"/>
<Target Name="AfterBuild">
<!-- Removing old output -->
<RemoveDir Directories="$(CustomOutputPath)" />
<ItemGroup>
<!-- Arbitrary contents of this project -->
<FilesToCopy Include="**\*.*" />
</ItemGroup>
<!-- this works fine -->
<Copy SourceFiles="#(FilesToCopy)"
DestinationFolder="$(CustomOutputPath)"
OverwriteReadOnlyFiles="true" />
<!-- Once cleanup and copy is completed, I want to run all the other
projects builds which contain similar but specific copy tasks as
above, with no clean up. BuildProjects is an ItemGroup of all
the projects I want to build -->
<MSBuild Projects="#(BuildProjects)"
Properties="Configuration=$(Configuration); BuildProjectReferences=true"/>
</Target>
</Project>
The problem I'm having is that one of the projects I am trying to build in the last step is failing because it references another project in the solution, which is not being built as part of the BuildProjectReferences=true directive, so it can't find the DLL. If I build this dependency individually then the MSBuild task will work, but I don't want to have to build this project independently.
Why is my referenced project not being built and is there a better way to do this with MSBuild?
Note: I am open to other solutions - I have tried to make ProjA and ProjB references of ProjC (hence not needing the MSBuild task at the bottom of ProjC target) but then the cleanup step in C happens AFTER A and B copy their output out so that doesn't work.
Use /verbosity:detailed and redirect output to a file. Look through the verbiage to see what's happening with the ResolveProjectReferences target. You can also use /verbosity:diag and see details of why is skipped things etc. That might be useful since the Condition on the various tasks used there are pretty hairy.
I've managed to get builds working on my build server, but now the issue I have is that the output in the output folder contains all output from all projects, rather than just the output from the wix project(s).
Any idea how to change this?
TIA
If you are only interested in the msi as a build output then you could create a step in your team build to copy your installer files to another location, the following build target, added to your build project should help.
This overrides the target AfterDropBuild
<Target Name="AfterDropBuild">
<PropertyGroup>
<InstallerDir>$(DropLocation)\$(BuildNumber)\Installers</InstallerDir>
</PropertyGroup>
<Message Importance="low" Text="InstallerDir=$(InstallerDir)" />
<MakeDir Directories="$(InstallerDir)" Condition="!Exists('$(InstallerDir)')" />
<CreateItem Include="$(BinariesRoot)\**\*.msi">
<Output TaskParameter="Include" ItemName="InstallationFiles"/>
</CreateItem>
<Copy SourceFiles="#(InstallationFiles)"
DestinationFolder="$(InstallerDir)"/>
</Target>
I create different solution platforms { Application, Setup } and set my .NET projects to build with application and my wix to build with setup. Then I tell the build definition to build those two platforms in that order. The result is that TFS archives the .NET code in an application folder that looks like the deployed machine and the MSI in the setup folder.
Only downside is when you add new projects you have to select the platform to build in configuration manager. My developers don't seem annoyed by it though.
Here's what I'm trying to do:
A single build script
That script builds two executables from the same Visual Studio project.
The first compiled .exe has a small amount of code disabled.
The other compiled .exe has everything enabled.
I've been reading up on conditional compilation and that takes care of my needs as far as enabling/disabling blocks of code.
I just can't figure out how to control conditional compilation from a build script using msbuild.
Is there a way to manipulate conditional compilation variables from a build script or some other way to accomplish what I'm trying to do?
Use build configurations in your project file. Set the parameters in a PropertyGroup that is optionally included based on the configuration. The configuration can then also define the output path for the two different versions of the assembly.
For the version that needs to remove some code use a configuration that includes the PropertyGroup.
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'CompiledOutDebug|AnyCPU' ">
<DefineConstants>$(DefineConstants);MY_CONDITIONAL_COMPILATION_CONSTANT</DefineConstants>
</PropertyGroup>
Then use an MSBuild script that calls the project MSBuild script twice and uses the Properties attribute of the MSBuild task to specify the configuration to build:
<Target Name="Build">
<MSBuild Projects="MyProject.csproj;"
Targets="Build"
Properties="Configuration=Release" />
<MSBuild Projects="MyProject.csproj"
Targets="Build"
Properties="Configuration=CompiledOutDebug" />
</Target>
Hamish beat me to it.
Here's an alternate solution using the same concepts:
At the command line:
msbuild -t:Clean
msbuild
CopyOutputDirForWithoutDefine.cmd
msbuild -t:Clean
msbuild -property:DefineConstants=MY_CONDITIONAL_COMPILE_CONSTANT
CopyOutputDirForWithDefine.cmd
The 1st and 3rd 'msbuild -t:Clean' ensures that you don't have left over turds from previous builds. The 2nd 'msbuild' builds without the conditional define, while the 4rth builds with the conditional define.
If the above are just a couple on shot items, then a batch file maybe enough. I recommend learning a bit of MSBuild and actually scripting everything in a MSBuild file as Hamish has done.
If you don't want to create a separate target for the two compilations, you can do it by specifying the conditional define in the DefineConstants property when you call the build the second time:
<Target Name="Build">
<MSBuild Projects="MyProject.csproj;"
Targets="Build"
Properties="Configuration=Debug" />
<MSBuild Projects="MyProject.csproj"
Targets="Build"
Properties="Configuration=Debug;
AssemblyName=$(AssemblyName)_Conditional;
DefineConstants=$(DefineConstants);CONDITIONAL_DEFINE" />
</Target>
Note that if you do it this way, you need to also overwrite the AssemblyName, otherwise your second build might pick intermediate files from the first build.
You should also look at the MSBuild task docs on MSDN, there are some interesting tidbits there.