Hi,
I haven't been able to find a way to hook tasks in at the solution level. I was hoping for something as easy as with projects (where you just hook into the project file xml).
What's the best way to accomplish this To be a bit more specific, there are things I want to happen before anything gets built and things I want to happen after everything gets built. All the examples I've hunted down seem to make modifications at the project level.
I assume I could create a Proj that uses the MSBuild task to build the solution and add whatever tasks I want before and after, but I'm hoping to find a way to do this while remaining inside visual studio (so that they are triggered when I hit build within visual studio).
thanks,
Orlando

solution level tasks
Antony Perkov
I took the idea you gave and used it for inspiration to write a powershell utility for doing all this work:
Here is a link to my page describing it.
Sunny Boy
You build each solution twice, is'nt it
first:
second:
It semms to me that call of ValidateSolutionConfiguration is better to create .sln.proj files.
Alex
csrking
Using it you can hook into the solution level build process and execute tasks at the start and then execute tasks at the end (of the solution build). You can also set and get state for the build that is accessible from all projects.
My option works from within the IDE as well.
Check it out and give me feedback!
Marko Simic
Hey,
Has anyone thought of including an arbratary msbuild project in the solution, then making all other projects dependant on it for the pre build, and making another where it is dependant on all projects as a post
Mark McCasland
It's a MUCH needed freature to allow building on the command line and vs.net off of a single msbuild file.
Kunal105
Alexey ,
I try to build your code but I get 3 build errors:
TornadoBaseConfiguration is undefinded
QueueItem is undefinde,
another thing can you tell me what this code exactly do and how to run it
Thanks,
Nabeel.
IGeorgeI
There is a short answer and a long answer. The short answer to your exact question is that unfortunately, it's not possible.
However, you may be interested in a small trick for your bag of tricks. It is entirely possible to do from the command line, if you generate the msbuild project that represents your solution. On the command line, set an environment variable called MSBuildEmitSolution and set it to 1. Then run msbuild on the solution file. This will generate a project file that represents the solution, that you can then build from the command line.
The reason why I said it's not possible is because this file will not behave in the IDE exactly as a solution would.
I am sorry I don't have a better answer for you. Authoring a separate project file that invokes msbuild on the solution (like you pointed out) also only works on the command line.
Faisal Mohamood
MSBuild Team
Chandra.Sekhar
Hi!
This is my way to solve problem. I'm using the bat file like this
set msbuildemitsolution=1
rem shortest task only to generate proj file from solution
msbuild solution.sln /t:ValidateSolutionConfiguration
set msbuildemitsolution=
rem xslt transformation of project
msxsl solution.sln.proj style.xslt -o solution.proj
msbuild solution.proj /t:Rebuild
The result of xslt transformation looks like this:
<Project DefaultTargets="Build" InitialTargets="ValidateSolutionConfiguration" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
...
<!-- inserted by xslt -->
<Target Name="MyTargeBeforeRebuildSolution">
...
</Target>
<!-- inserted by xslt -->
<Target Name="MyTargeAfterRebuildSolution">
...
</Target>
<Target Name="Rebuild" Condition="'$(CurrentSolutionConfigurationContents)' != ''">
<!-- inserted by xslt -->
<CallTarget Targets="MyTargetBeforeRebuildSolution" />
<!-- original line -->
<CallTarget Targets="Project1:Rebuild;Project2:Rebuild;" RunEachTargetSeparately="true" />
<!-- inserted by xslt -->
<CallTarget Targets="MyTargeAfterRebuildSolution" />
</Target>
</Project>
Paul Hendley
Also another enhancement I'll add is the ability to call other targets besides build.
Thanks,
Sayed Ibrahim Hashimi
www.sedodream.com
Glamiac
I have tried the msbuildemit trick and looked at the resulting proj file -- this isn't quite what I want. Perhaps someone can point me in the right direction I am new to msbuild, but I have done a lot of reading and I am not finding what I need.
I have a solution file with 30 projects. The projects are not organized in some systematic and convinient way -- they are all over the place so no assumption can be made about the project tree structure. What I want is the ability to get to the list of project references (as specified in the solution file) so that I can recursively get them from VSS I don't want to explicitly hardcode each referenced project in my msbuild file. If the projects are already referenced in the solution, I shouldn't have to re-enter them again in the .proj file, correct Unfortunately, the proj file generated from the msbuildemit=1 approach has all the VSS info stripped out of it. Once the referenced projects are retrieved from VSS, I'd like to be able to build them -- again, without having to explicitly list them one by one. Is that possible
THank you
Tomas Kiss
As the whole msbuild system is extensible it would have been nice to have extension points (start, end, startproj, endproj etc) in the generated sln msbuild file for the time being (until the sln itself becomes a msbuild.proj file).
But can't you mimic the behaviour by doing an import in each of your project files to a solution.targets file. Then override BeforeClean( ) in each of your projectfile, call a target (ie SolutionStart) in solution.targets and have the SolutionStart target produce an Output.
As far as I know MSBuild will not rebuild a target if it has already been build (if the Output is present), so during a build the SolutionStart target will only be run once.
Disclaimer: I didn't try this, it is just a thought...
Scott Stanfield
Reza
Unfortunately msbuildemitsolution is working not so well: it’s loosing some projects from my solution file. So I’ve wrote simple C# code to generate project, preprocess and build it
It's part of special service, that builds solutions immediately after svn commit.
using
System;using
System.Collections.Generic;using
System.Collections.Specialized;using
System.IO;using
System.Security.AccessControl;using
System.Text;using
System.Text.RegularExpressions;using
System.Xml;using
Microsoft.Build.BuildEngine;using
Microsoft.Build.Utilities;using
Microsoft.Win32;namespace
Parus.Build.Tasks{
public abstract class TornadoBase : ITask{
private static readonly string s_MakeHtml = "MakeHtmlLog.xslt"; protected abstract TornadoBaseConfiguration Config { get;} protected abstract string TaskLabel { get;} protected string MailList{
get { return Config.MailList; }}
protected abstract string MsBuildMainTask { get; } protected string Solution{
get{
return Path.Combine(SourceDir, Config.Solution);}
}
protected string SourceDir{
get { return Path.Combine(Config.SrcDir, m_Branch.Replace(@"/", @"\")); }}
protected string LogDir{
get { return Config.LogDir; }}
protected abstract string ReleaseDir { get; } protected string KeyFile{
get { return Config.KeyFile; }}
protected abstract void MainBuildParams(); protected abstract string Configuration { get; } protected abstract string Platform { get; } private StringDictionary m_Properties; protected StringDictionary Properties{
get{
if (null == m_Properties){
m_Properties =
new StringDictionary(); foreach (string key in Config.ProjectProperties.AllKeys)m_Properties.Add(key, Config.ProjectProperties[key].Value);
}
return m_Properties;}
}
protected QueueItem m_Item; protected string m_Branch;#region
IBuildTask Members protected Target m_InitialTarget; protected bool DoBuild(){
try{
string newFile = Path.ChangeExtension(Solution, ".proj"); string parusTargets = ModuleProvider.GetModule("Parus.targets"); string taskConditionPattern = string.Format( @"^\s*\(\s*'\$\(Configuration\)'\s*==\s*'{0}'\s*\)\s*and\s*\(\s*'\$\(Platform\)'\s*==\s*'{1}'\s*\)\s*$",Configuration, Platform);
Regex taskConditionTest = new Regex(taskConditionPattern, RegexOptions.Singleline | RegexOptions.Compiled); string targetNamePattern = @"^(.+\log.LoadXml(m_SvnLog);
solution.Load(Solution);
solution.DefaultTargets = MsBuildMainTask;
solution.AddNewImport(parusTargets,
null);AppendHostSpecificProps(solution);
foreach (Target target in solution.Targets){
targets.Add(target);
}
for (int i = targets.Count - 1; i >= 0; i--){
if (!targetNameTest.IsMatch(targets{
solution.Targets.RemoveTarget(targets
);
targets.RemoveAt(i);
}
else{
List<BuildTask> tasks = new List<BuildTask>(); foreach (BuildTask task in targets{
tasks.Add(task);
}
for (int j = tasks.Count - 1; j >= 0; j--){
if (!string.IsNullOrEmpty(tasks[j].Condition)){
if (!taskConditionTest.IsMatch(tasks[j].Condition)){
targets
.RemoveTask(tasks[j]);
tasks.RemoveAt(j);
}
else{
if (tasks[j].Name == "MSBuild"){
string csproj = tasks[j].GetParameterValue("Projects"); if (Path.GetExtension(csproj) == ".csproj"){
string oldProp = tasks[j].GetParameterValue("Properties"); string newProp = ""; if (Properties.ContainsKey(""))newProp = Properties[
""]; if (Properties.ContainsKey(csproj))newProp = Properties[csproj];
tasks[j].SetParameterValue(
"Properties", string.Format("{0}; {1}", oldProp, newProp));csProjects.Add(csproj);
}
}
}
}
}
}
}
BuildItemGroup ig = solution.AddNewItemGroup(); foreach (string project in csProjects){
ig.AddNewItem(
"SolutionProjectsList", project, false);ProcessCSProject(engine,
Path.Combine(Path.GetDirectoryName(Solution), project));}
m_InitialTarget = solution.Targets.AddNewTarget(
"SvnInfoTarget");InitialWarning(
"SVN_REVISION", log.SelectSingleNode("/log/logentry/@revision").InnerText);InitialWarning(
"SVN_AUTHOR", log.SelectSingleNode("/log/logentry/author").InnerText);InitialWarning(
"SVN_DATE", log.SelectSingleNode("/log/logentry/date").InnerText);InitialWarning(
"SVN_MSG", log.SelectSingleNode("/log/logentry/msg").InnerText);
foreach (XmlNode node in log.SelectNodes("/log/logentry/paths/path")){
InitialWarning(
"SVN_PATH", node.InnerText);}
solution.InitialTargets = m_InitialTarget.Name;
MainBuildParams();
solution.Save(newFile);
//build XmlLogger xmlLogger = new XmlLogger();xmlLogger.Parameters = XmlLog;
engine.RegisterLogger(xmlLogger);
solution.SetProperty(
"Configuration", Configuration, "1==1");solution.SetProperty(
"Platform", Platform, "1==1"); bool res = solution.Build(MsBuildMainTask);engine.UnregisterAllLoggers();
return res;}
catch (Exception e){
Logger.WriteLine(VerbosityLevel.Quiet, e.ToString()); return false;}
}
private static void AppendHostSpecificProps(Project solution){
BuildPropertyGroup props = solution.AddNewPropertyGroup(false); string installDir = GetDevEnvironmentPath(); if (installDir != null){
props.AddNewProperty(
"DevEnvDir", installDir);}
}
private static string GetDevEnvironmentPath(){
try{
RegistryKey installKey = Registry.LocalMachine.OpenSubKey(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\8.0", RegistryKeyPermissionCheck.Default, RegistryRights.ReadKey | RegistryRights.QueryValues); string installDir = installKey.GetValue("InstallDir") as string; if (installDir != null && Directory.Exists(installDir)){
if (!installDir.EndsWith(new string(Path.DirectorySeparatorChar, 1)))installDir +=
Path.DirectorySeparatorChar; return installDir;}
}
catch //{
}
return null;}
protected void InitialProperty(string name, string value){
BuildTask prop;prop = m_InitialTarget.AddNewTask(
"CreateProperty");prop.SetParameterValue(
"Value", value);prop.AddOutputProperty(
"Value", name);}
protected void InitialWarning(string key, string value){
BuildTask warn;warn = m_InitialTarget.AddNewTask(
"Warning");warn.SetParameterValue(
"Code", key);warn.SetParameterValue(
"Text",value);}
protected virtual void ProcessCSProject(Engine engine, string project){
}
protected string m_SvnLog;
public string XmlLog{
get { return Path.Combine(LogDir, m_Item.Revision + ".xml"); }}
public string HtmLog{
get { return Path.Combine(LogDir, m_Item.Revision + ".htm"); }}
public bool Execute(QueueItem item){
bool res; string subj;m_Item = item;
try{
if (!Directory.Exists(LogDir)) Directory.CreateDirectory(LogDir); // delete log files File.Delete(XmlLog); File.Delete(HtmLog); // find branchm_Branch =
BuildHelper.GetBranch(m_Item.Repository, m_Item.Revision); // clear working directory BuildHelper.CleanWorkDir(SourceDir, m_Item.Revision); // get revision infom_SvnLog =
BuildHelper.StartSvn(string.Format( "log --xml -v -r {0} {1}",m_Item.Revision,
SourceDir));
// main buildres = DoBuild();
string svnLogFile = Path.GetTempFileName(); using (StreamWriter logWriter = new StreamWriter(svnLogFile)){
logWriter.WriteLine(m_SvnLog);
logWriter.Flush();
logWriter.Close();
}
// make HTML log StringDictionary makeHtmlParams = new StringDictionary();makeHtmlParams.Add(
"svnlog", svnLogFile); BuildHelper.Xslt(XmlLog,
ModuleProvider.GetModule(s_MakeHtml),HtmLog,
null); File.Delete(svnLogFile); // renew revision commentsubj = res
"OK" : "Fails"; BuildHelper.UpdateSvnLog(m_Item.Repository, m_Item.Revision, subj);}
catch (Exception e){
res =
false;subj =
"Fails"; using ( StreamWriter htmlWriter = new StreamWriter(HtmLog, false, Encoding.UTF8)){
htmlWriter.WriteLine(
"<html><h1>Build {0}</h1><h2>{1}</h2>",m_Item.Revision, e.Message);
foreach ( string s ine.ToString().Split(
new string[] {"\n"}, StringSplitOptions.None)){
htmlWriter.WriteLine(
"<p>{0}</p>", s);}
htmlWriter.WriteLine(
"</html>");htmlWriter.Flush();
htmlWriter.Close();
}
}
try{
File.Copy(HtmLog, Path.Combine(LogDir, "Default.htm"), true); BuildHelper.Alert(HtmLog, MailList, string.Format("{0} {1} ({2}): {3} {4}",TaskLabel, m_Item.Revision, m_Branch, subj, m_Item.Comment)); if (res)OnSuccessBuild();
elseOnUnsuccessBuild();
}
catch{
res =
false;}
return res;}
protected StringDictionary m_MainBuildParams; protected virtual void OnUnsuccessBuild(){
}
protected virtual void OnSuccessBuild(){
}
#endregion
}
}
Roger_Wagner
Here is an all MSBuild solution:
By using Keith Hills SetEnvVar task we can write an MSBuild file to drive the whole process, and we can place customizations within this file.
Place this project file in the same folder that contains the solution file(s) that you want to build.
<!--
=======================================================================================MSBuild file which can be used to inject steps into the building of solution files
===========================================================================================
--><
Project InitialTargets="SetMSBuildEmit" DefaultTargets="BuildSolution" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"><
PropertyGroup><!--
Location that holds all needed assemblies for the shared tasks --><
SharedTasksDir>..\tasks\</SharedTasksDir><!--
Assembly that contains the custom tasks --><
EnvAssemblyFilename>Sedodream.MSBuild.dll</EnvAssemblyFilename><!--
Where is the solution file(s) located --><
BuildSolutionDir>.\</BuildSolutionDir></
PropertyGroup><
ItemGroup><!--
Item for solutions file(s) --><
SlnFiles Include="$(BuildSolutionDir)*.sln"/></
ItemGroup><!--
Let MSBuild know where to find our tasks--><
UsingTask AssemblyFile="$(SharedTasksDir)$(EnvAssemblyFilename)" TaskName="SetEnvVar"/><!--
Set the envrionment variable msbuildemit solution to 1 to create the msbuild
project file that represents the solution.
--><
Target Name="SetMSBuildEmit"><
SetEnvVar Variable="msbuildemitsolution" Value="1"/></
Target><
Target Name="BuildSolution" DependsOnTargets="SetMSBuildEmit"><!--
Have MSBuild emit the solution --><
MSBuild Projects="@(SlnFiles)" Targets="Build"/><!--
Create a new item to pick up the newly generated file --><
CreateItem Include="*.sln.proj"><
Output TaskParameter="Include" ItemName="SolutionMSBuildFiles"/></
CreateItem><!--
Call MSBuild for each solution file. Pass the property:
theSolution=SOLUTION_FILE_TO_BUILD.
In the DoBuildSolution we use this to determine which file to build.
--><
MSBuild Projects="$(MSBuildProjectFile)" Targets="DoBuildSolution" Properties="@(SolutionMSBuildFiles->'theSolution=%(Filename)%(Extension)')"/></
Target><!--
============================================================================================Content used when this file is invoked above
================================================================================================
--><!--
Define dependencies for the solution build. This can be extended like other XXXDependsOn Properties
--><
PropertyGroup><
DoBuildSolutionDependsOn>BeforeDoBuildSolution;
CoreBuildSolution;
AfterDoBuildSolution
</
DoBuildSolutionDependsOn></
PropertyGroup><
Target Name="DoBuildSolution" DependsOnTargets="$(DoBuildSolutionDependsOn)" /><
Target Name="BeforeDoBuildSolution"><
Message Text="**************Before building your solution*****************" Importance="high"/></
Target><!--
Here is where we actually build the solution passed to us.
--><
Target Name="CoreBuildSolution"><
MSBuild Projects="$(theSolution)" Targets="Build"/></
Target><
Target Name="AfterDoBuildSolution"><
Message Text="##############After building your solution##################" Importance="high"/></
Target></
Project>Then invoke msbuild.exe on this file with:
>msbuild.exe SolutionBuild.proj /t:BuildSolution
You can place customizations in BeforeDoBuildSolution and AfterDoBuildSolution. These targets should be called for every solution in that folder, and you can change the SlnFiles to include more if you want. I've only used this to build 1 solution file, but I assume you can build multiple with this.
More detailed info at my blog: www.sedodream.com for those who are interested. I'm very interested in hearing feedback regarding this. I know that several people have asked about this in the past.
Sayed Ibrahim Hashimi
www.sedodream.com