How to use Visual Studio 2010 config transform when running/debugging locally? - visual-studio-2010

In the team I'm working in we have a big product with many WCF web services and some web sites which use the services. We are just about to upgrade to VS 2010 and I'm looking at if we should start using the new config transform functions in VS 2010.
We have several different environments which need different web.configs (database connection strings, WCF addresses and so on). Often when debugging something high up such as the web frontend it is useful to configure it to directly connect with the TEST or QA backend / databases. On each developer's local machine the IIS is configured directly to the source folder of each WCF/web project, and when running locally it is a simple matter of Ctrl-Shift-B or F5 to debug something.
One would think that it would be possible to build/F5 with TEST or QA as configuration mode and get the TEST/QA config, but I don't see how. Is it not supported, or maybe we need to change how we work with things?
Our other option is to instead use a simple replace-script as a prebuild event that creates the web.config from a template and a key-file depending on configuration mode. With this method you would get TEST config if you compile in TEST and so on but it feels a bit bad to roll our own solution when there is a function built into Visual Studio.

You can achieve the effect you're looking for by using the BeforeBuild and AfterBuild targets available in the .csproj file. The VS.NET IDE will execute these targets when doing a Build or a Rebuild, so you can use them to execute the web.config transforms. Since you'll need to do a web.config transform and then overwrite the actual web.config file, you'll need to rely on a new file called web.default.config to store the base web.config data.
I tried this out in a test project, here were the changes I made to the .csproj file:
<Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" />
<ProjectExtensions>
...
</ProjectExtensions>
<Target Name="BeforeBuild">
<Copy SourceFiles="$(ProjectDir)web.default.config" DestinationFiles="$(ProjectDir)web.config" />
</Target>
<Target Name="AfterBuild" Condition="$(FirstRun) != 'false'">
<MSBuild Projects="$(MSBuildProjectFile)" Targets="TransformWebConfig" Properties="FirstRun=false;" />
<Sleep Milliseconds="2000" />
<Copy SourceFiles="$(ProjectDir)obj\$(ConfigurationName)\TransformWebConfig\transformed\web.config"
DestinationFiles="$(ProjectDir)web.config" />
</Target>
I had to manually add these to the .csproj file (I used Notepad++). As far as I can tell there is no way to add these instructions through the VS.NET IDE. You need to supply the conditional on the AfterBuild to keep from having a circular reference, as the call to MSBuild will rerun the build to generate the web.config transform.
Basically what we're doing is copying the web.default.config file (our base template) over the existing web.config before we start to build, and then we use MSBuild to generate a web.config for whatever configuration we're building. After the transform is complete, we use a Copy task to take the transformed file and copy it over to the web.config file in the web root. One issue I occasionally ran into was a file in use error when trying to overwrite the web.config after the transform was complete. Adding a Sleep task (from MSBuildCommunityTasks) after the MSBuild task took care of that issue.
I only tested this approach using the built in ASP.NET server, not IIS, so YMMV but I feel like this is a workable solution.
The FirstRun idea came from this post.

Related

Visual Studio add pre-build event that always runs (C# project)

In my project, I am running an external tool to update some binary files. These files are included in the project as "content".
At the moment the tool is set to run during "pre-build event" in C# project properties. Unfortunately, this event is only executed if the project is out of date, which is not what I need.
I am working around this by always using "rebuild" instead of "build" on my project, but this is tedious and slow.
I need to execute this tool always, irrespective of whether a project is or is not up to date. Actually, even before MSBuild even determines whether the project is up-to-date, because the tool modifies some of the files included in the project, therefore affecting the up-to-date check result.
Is there a proper way to do it?
Here's the solution. Define this property in your project file:
<PropertyGroup>
<DisableFastUpToDateCheck>true</DisableFastUpToDateCheck>
</PropertyGroup>
PreBuildStep will then execute every time, regardless of whether the project is or isn't up to date.
It seems that Visual Studio is bypassing normal up-to-date checks of MSBuild and using some sort of custom check that is faster, but has a side effect of breaking customized build targets.
In project level, you have three options:
1) Pre-build action
<PropertyGroup>
<PreBuildEvent>notepad.exe Foo.txt</PreBuildEvent>
</PropertyGroup>
2) normal BeforeBuild target
<Target Name="BeforeBuild">
<Exec Command="notepad.exe Foo.txt" />
</Target>
3) "attached" to "Build" target (like stijn suggested)
<Target Name="BeforeBuild2" BeforeTargets="Build">
<Exec Command="notepad.exe Foo.txt" />
</Target>
Actually this solution (in case of Build) will not work, because DependsOnTargets is executed BEFORE BeforeTargets. And exactly in DependsOnTargets the real (CoreBuild) sits :)
This is why they invented the 'BeforeBuild' target ;)
In both cases VS check if something is changes (files are up-to-date). Why do you even want to run external program if nothing was changed? If this program work on file (eg. "content") msbuild and VS should detect files as out-of-date and process building.
Unfortunately IDE (Visual Studio) has it's own method to deal with msbuild projects. The main mechanism is the same, but when it's came to determine what project build or not, or in which order... VS act totalny different.
You can use external tool and run "msbuild" against your solution or project. This will also compile "the proper way" and binaries will be not different, but you will have full capabilities and potentials of MsBuild
There is also one additional pre build event that was not discussed here. Usually code analyzers are using that to check if code analyzer was downloaded by NuGet.
So if you want to execute something before code analyzers you need to use that target.
You just need to add <Target/> node under <Project/> node in your .csproj file:
<Target Name="DownloadNugetPackages" BeforeTargets="PrepareForBuild">
<Exec Command="notepad.exe Foo.txt"/>
</Target>
PrepareForBuild event will run before pre build events.
None of these solutions worked for me using Visual Studio for Mac.
I want to run a bash script before building the project but since I'm using Xamarin, some things are getting out of whack. I tried all different types of targets and even tried the CustomCommands in the project options but still I would get issues around MSbuild just running automatically or not truly running before the build
You can run the PreBuild events in a separate project that compiles before your main project.
Create a new project and name it something like "PreBuildEvent"
Add your MSbuild targets/commands as shown above to this new PreBuildEvent.csproj file for each property group/build configuration
In the project where you originally to do the PreBuild work, add a reference to this new project.
This new project will build first, executing any PreBuild events, and once this project is built, it will kick off the build for your original project. This is because when you add a reference to a project, visual studio makes sure to build them in the correct order
The solution that works for me is to have another project (configuration Makefile) and set that project as a BuildDependancy.
This avoid modification of how the prebuild step runs, and could allow you to regenerate your binary files in isolation to the rest of your build process if required.

Does Visual Studio support adding MSBuild tasks to projects?

I'm trying to add some simple MSBuild tasks to a Visual Studio project (VS 2012 Express) - specifically, to create a subdirectory then copy some files to a subdirectory of the output directory ready for packaging.
I see that VS supports custom build steps, which are command-line invocations. However, since VS is based on MSBuild it should be possible to add these directly as MSBuild tasks like the Copy Task in the AfterBuild pre-defined target.
What I can't find is any way to actually add such tasks within the framework of Visual Studio. The documentation only talks about it from an MSBuild perspective, not how it works within Visual Studio's UI. It also doesn't seem to discuss the properties that refer to build output etc there; presumably they're just those used by msbuild its self.
Is there support for MSBuild task management in Visual Studio's UI and it's just crippled out of my Express edition? Or do I have to go hack the project file XML to add MSBuild tasks? Is that supported and the way it's supposed to be done?
I'm used to working with Eclipse and Ant or Maven, where all this is supported within the IDE, though of course you can hack the XML directly. Finding no UI at all for MSBuild task management in Visual Studio is quite confusing. Am I missing the obvious or crippled by using the freebie edition?
For C++ projects, you can use the property
<CppCleanDependsOn>DeleteOutputs;$(CppCleanDependsOn)</CppCleanDependsOn>
instead of defining the BeforeClean target like you did.
From what I read, CallTarget is to be avoided. In your example, you should use DependsOnTargets to do that, as you see in many dummy targets in the MS supplied files. The analogous mechanism of a function where a target just "calls" other targets is done with DependsOnTargets. The flow is not really the same as procedural programming.
Intellisense: I never use it. Is that true for conditional AdditionalIncludeDirectories in the props file only? Go ahead and edit the entry in the proj file where the IDE put it, if you edit the property in the IDE with just one configuration chosen.
(After a bunch more reading I found out how this works):
Visual Studio doesn't seem to expose advanced MSBuild project editing, even though modern vcxproj files are just MSBuild project files with a bunch of extra labeled properties and other entries for Visual Studio IDE specifics. So you have to hack the project XML.
To make it cleaner, only add one line to your actual vcxproj file - an include of a .targets file that contains the rest of your build customisations. e.g, just before the end of the project file, insert:
<Import Project="pg_sysdatetime.targets" />
</Build>
Now create your .targets file with the same structure as any other MSBuild project. Here's mine from the project I've been working on:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- MSBuild extension targets for Visual Studio build -->
<PropertyGroup>
<DistDir>pg_sysdatetime_pg$(PGMAJORVERSION)-$(Configuration)-$(Platform)</DistDir>
</PropertyGroup>
<ItemGroup>
<DocFiles Include="README.md;LICENSE"/>
<ExtensionSourceFiles Include="pg_sysdatetime--1.0.sql;pg_sysdatetime.control"/>
<ExtensionDll Include="$(TargetDir)\pg_sysdatetime.dll"/>
</ItemGroup>
<Target Name="CopyOutputs">
<Message Text="Copying build product to $(DistDir)" Importance="high" />
<Copy
SourceFiles="#(DocFiles)"
DestinationFolder="$(DistDir)"
/>
<Copy
SourceFiles="#(ExtensionDll)"
DestinationFolder="$(DistDir)\lib"
/>
<Copy
SourceFiles="#(ExtensionSourceFiles)"
DestinationFolder="$(DistDir)\share\extension"
/>
</Target>
<Target Name="DeleteOutputs">
<Message Text="Deleting $(DistDir)" Importance="normal" />
<Delete Files="$(DistDir)"/>
</Target>
<!-- Attach to Visual Studio build hooks -->
<Target Name="BeforeClean">
<CallTarget Targets="DeleteOutputs"/>
</Target>
<Target Name="AfterBuild">
<CallTarget Targets="CopyOutputs"/>
</Target>
</Project>
This can contain whatver MSBuild tasks you want, grouped into targets. It can also have property groups, item groups, and whatever else MSBuild supports.
To integrate into Visual Studio you add specially named targets that invoke what you want. Here you can see I've defined the BeforeClean and AfterBuild targets. You can get the supported targets from the VS integration docs.
Now, when I build or rebuild, a new directory containing the product DLL and a bunch of static files is automatically created, ready to zip up. If I wanted I could add the Nuget package for MSBuild Community Extensions and use the Zip task to bundle the whole thing into a zip file at the end too.
BTW, while you can define properties in your .targets files it's better to define them in property sheets instead. That way they're visible in the UI.
I'm using VS2010 Pro, and it doesn't expose the AfterBuild target, at least in C++ projects which is what I'm doing. As you see, it does have the "Events", which according to what I've read are for backward compatibility with converted projects from VSBuild. I agree, a MSBuild task rather than a command script is the way to go.
Forget the UI. It's made to support free editing of the XML files, and continue using the UI too as it respects what you had in there and uses labels for its own stuff so it can find it to update it.
But to keep it neat, you could use a property page; a stand-alone XML file with *.props name, and put what you want in it. Then add that props file to the projects using the UI. You won't hand-edit the project file that the UI is maintaining, and it won't touch the props file unless you go through the property manager view and open it explicitly.
Oh, I also recall seeing additional standard targets something like Package and Publish. Maybe those are not used on your project type, but you could use those entry points anyway.

How to collect NUnit reports with MSBuild?

I've recently introduced NUnit to a Visual Studio C# project. The project folder structure looks like
- project root
-- applications (rich client interface, web interface, small tools)
-- components (business logic)
-- vendor (3rd party components)
-- tests (Nunit tests)
For each Visual Studio project "MyProject" under applications or components there is a corresponding project under tests named "MyProject.Test". When I introduced the NUnit test, I put the following in each .Test.csproj file:
<Target Name="AfterBuild">
<CreateItem Include="$(TargetPath)">
<Output TaskParameter="Include" ItemName="MyProjectTests" />
</CreateItem>
<!-- Create folder for test results -->
<MakeDir Directories="$(OutDir)\TestResults" />
<!-- Run tests-->
<NUnit Assemblies="#(MyProjectTests)" ToolPath="..\vendor\NUnit\bin" OutputXmlFile=".\TestResults\MyProject.Test.Results.xml" WorkingDirectory="$(OutDir)" />
<!-- Create HTML report -->
<Xslt Inputs="$(OutDir)\TestResults\MyProject.Test.Results.xml" Xsl="$(MSBuildCommunityTasksPath)\NUnitReport.xsl" RootTag="Root" Output="$(OutDir)\TestResults\MyProject.Test.Results.html" />
</Target>
This works fine, both when building solutions from within Visual Studio as well as on a build server with the MSBuild CLI.
The remaining inconvenience of that approach is that it leaves me with the test reports in a TestResults folder in each test projects output folder, but with nothing in my solution's main output folder. So, my question is:
What is the preferred way of collecting the resulting NUnit html reports in the solution's/startup project's output folder? What MSBuild instructions should I place in which .csproj file? I'm just getting started with MSBuild and I can't figure out the best practice...
It has to work both in Visual Studio and with the MSBuild CLI, but that shouldn't be a problem, I guess.
Thanks
If you have a build server, one good approach is to setup a site with a virtual path configured to your test folder. Then any person on your company can browse something like http://build.companydomain.local/yourapp/nunitreports/, hosted on the build server's IIS (in case your build server have one).
I'm doing this with my coverage reports. Any person can browse it at any moment. I hope it helps!

Visual Studio Build Tasks - TFS Operations

I'm looking to extend some post build tasks to include the checking out and then checking in of a DLL. We are using TFS and I know there are command line tools to do this. What I don't know how to do is to integrate these into my existing post build tasks. Right now my post build tasks are simple and are managed in Visual Studio through the project properties. Eventually I want to break out my custom build tasks into external files and call them in, but that is the subject of another question ;)
Without resorting to custom Build tasks you could try to use the Team Foundation Source Control Command-Line tool (tf.exe).
The example below shows how to use tf.exe to check out a file from TFS.
<PropertyGroup>
<TfCommand>
"C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\tf.exe"
</TfCommand>
</PropertyGroup>
<Target Name="AfterCompile">
<Exec Command="$(TfCommand) get /force /noprompt "$(SolutionRoot)\sources\example.cs""
ContinueOnError="true" />
<Exec Command="$(TfCommand) checkout "$(SolutionRoot)\sources\example.cs""
ContinueOnError="true"/>
</Target>
Include this in your own MSBuild project file.
This example doesn't do anything useful and you need to change it to match your environment, but maybe it gives you a start.
I got this example from tfsbuild.com.
You could use the Team Foundation Server client API. TeamFoundationServer is the base class that should allow you to connect to a server, list and manipulate TFS projects.
Msbuildtasks has some extensions for msbuild with sourcecode (its opensource). You could use this to create your own checkin/checkout functionality. (in combination with what Darin suggests)
http://msbuildtasks.tigris.org/
Take a look at the SDC Tasks Library on CodePlex. It's a set of custom MSBuild tasks that includes Checkin and Checkout tasks (see the Microsoft.Sdc.Tasks.SourceTfs namespace in the accompanying documentation). You can incorporate these tasks in the "AfterBuild" target in your project file.
<SourceTfs.Checkout Path="Path" TfsVersion="tfsVersion"
WorkingDirectory="workingDirectory"/>
<SourceTfs.Checkin Path="Path" Comments="Comments" TfsVersion="tfsVersion"
WorkingDirectory="workingDirectory" Override="overrideText"/>
You would set TfsVersion to "2005" or "2008" as appropriate.
Our team has several small projects which output DLL's used by several other projects. Part of our release is to publish these DLL's. I use the AfterDropBuild target for this. Hopefully the comments in my build script snippet are clear enough to show what I am doing.
<!-- Get a reference to the new release address finalizer DLL and the existing published address finalizer DLL -->
<PropertyGroup>
<ReleaseDLL>$(DropLocation)\$(BuildNumber)\Release\Address_Finalizer.dll</ReleaseDLL>
<PublishedFolder>$(SolutionRoot)\3rd Party\bin\PG File Import</PublishedFolder>
<PublishedDLL>$(PublishedFolder)\Address_Finalizer.dll</PublishedDLL>
</PropertyGroup>
<!-- Check out the published DLL -->
<Exec WorkingDirectory="$(SolutionRoot)" Command='$(TfCommand) checkout /lock:checkout "$(PublishedDLL)"'/>
<!-- Copy release to published -->
<Copy SourceFiles="$(ReleaseDLL)" DestinationFolder="$(PublishedFolder)"/>
<!-- Check in the published DLL -->
<Exec WorkingDirectory="$(SolutionRoot)" Command='$(TfCommand) checkin /override:Automated /noprompt /comment:"$(VersionComment)" "$(PublishedDLL)"'/>

Does Msbuild recognise any build configurations other than DEBUG|RELEASE

I created a configuration named Test via Visual Studio which currently just takes all of DEBUG settings, however I employ compiler conditions to determine some specific actions if the build happens to be TEST|DEBUG|RELEASE.
However how can I get my MSBUILD script to detect the TEST configuration??
Currently I build
<MSBuild Projects="#(SolutionsToBuild)" Properties="Configuration=$(Configuration);OutDir=$(BuildDir)\Builds\" />
Where #(SolutionsToBuild) is a my solution. In the Common MsBuild Project Properties it states that $(Configuration) is a common property but it always appears blank?
Does this mean that it never gets set but is simply reserved for my use or that it can ONLY detect DEBUG|RELEASE. If so what is the point in allowing the creation of different build configurations?
I haven't done much with defining an MSBUILD configuration file but I have done builds of different configurations using a batch file like this
msbuild /v:n /p:Configuration=Release "Capture.sln"
msbuild /v:n /p:Configuration=ReleaseNoUploads "Capture.sln"
I defined the ReleaseNoUploads configuration inside Visual Studio.
Here's what I had to do for that (this is Visual Studio 2005):
Open the Tools:Options menu, go to the Projects and Solutions:General option, and check Show advanced build configurations.
From there, go to the Build:Configuration Manager menu
In the dialog that pops up, click on the Active solution configuration pulldown and click <New...> to create a new build configuration.
Sure, you can have as many custom build configurations as you want to define. See this related question for how the setup might look.
How to conditionally deploy an app.config based on build configuration?
Note that when 'inside visual studio', the $(Configuration) and $(Platform) are always set by VS using the Configuration Manager stuff in the dropdowns at the top. Whereas if you want to set these values using msbuild from the command line, you must pass in the values explicitly (as in #MarkBiek's answer).
(Most VS project templates will 'default in' a value for Configuration/Platform, so that you can use the command-line MSBuild without specifying these values explicitly. This is good, but makes these two useful/common properties appear a little more magical/weird than they actually are.)
Normally what I do to have Release and Debug both build from a single MSBuild script is:
<PropertyGroup Condition="'$(Configuration)'==''">
<Configuration>Debug;Release</Configuration>
</PropertyGroup>
Then add this but of MSBuild secret sauce:
<Target Name="configurations">
<CreateItem Include="$(Configuration)">
<Output TaskParameter="Include" ItemName="Configuration" />
</CreateItem>
</Target>
And then for each target do something like this:
<Target Name="Compile" DependsOnTargets="configurations" Inputs="#(Configuration)" Outputs="target\%(Configuration.FileName)">
<MSBuild Projects="#(MyProjects)" Targets="Build" Properties="Configuration=%(Configuration.Identity);WarningLevel=1" />
</Target>

Resources