2010-04-06 Jb Evain <jbevain@novell.com>
[mcs.git] / class / Microsoft.Build.Engine / Microsoft.Build.BuildEngine / Engine.cs
blobcaefd7f9db56e19838c37cc2bd73ad1ffd3c2167
1 //
2 // Engine.cs: Main engine of XBuild.
3 //
4 // Author:
5 // Marek Sieradzki (marek.sieradzki@gmail.com)
6 //
7 // (C) 2005 Marek Sieradzki
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 #if NET_2_0
30 using System;
31 using System.Collections;
32 using System.Collections.Generic;
33 using System.IO;
34 using Microsoft.Build.Framework;
35 using Microsoft.Build.Utilities;
36 using Mono.XBuild.Utilities;
38 namespace Microsoft.Build.BuildEngine {
39 public class Engine {
41 string binPath;
42 bool buildEnabled;
43 Dictionary<string, TaskDatabase> defaultTasksTableByToolsVersion;
44 const string defaultTasksProjectName = "Microsoft.Common.tasks";
45 EventSource eventSource;
46 bool buildStarted;
47 ToolsetDefinitionLocations toolsetLocations;
48 BuildPropertyGroup global_properties;
49 //IDictionary importedProjects;
50 List <ILogger> loggers;
51 //bool onlyLogCriticalEvents;
52 Dictionary <string, Project> projects;
53 string defaultToolsVersion;
55 // the key here represents the project+target+global_properties set
56 Dictionary <string, ITaskItem[]> builtTargetsOutputByName;
57 Stack<Project> currentlyBuildingProjectsStack;
59 static Engine globalEngine;
60 static Version version;
62 static Engine ()
64 version = new Version ("0.1");
67 public Engine ()
68 : this (ToolLocationHelper.GetPathToDotNetFramework (TargetDotNetFrameworkVersion.Version20))
72 public Engine (ToolsetDefinitionLocations locations)
73 : this (ToolLocationHelper.GetPathToDotNetFramework (TargetDotNetFrameworkVersion.Version20))
75 toolsetLocations = locations;
78 public Engine (BuildPropertyGroup globalProperties)
79 : this (ToolLocationHelper.GetPathToDotNetFramework (TargetDotNetFrameworkVersion.Version20))
81 this.global_properties = globalProperties;
84 public Engine (BuildPropertyGroup globalProperties, ToolsetDefinitionLocations locations)
85 : this (ToolLocationHelper.GetPathToDotNetFramework (TargetDotNetFrameworkVersion.Version20))
87 this.global_properties = globalProperties;
88 toolsetLocations = locations;
91 // engine should be invoked with path where binary files are
92 // to find microsoft.build.tasks
93 public Engine (string binPath)
95 this.binPath = binPath;
96 this.buildEnabled = true;
97 this.projects = new Dictionary <string, Project> ();
98 this.eventSource = new EventSource ();
99 this.loggers = new List <ILogger> ();
100 this.buildStarted = false;
101 this.global_properties = new BuildPropertyGroup ();
102 this.builtTargetsOutputByName = new Dictionary<string, ITaskItem[]> ();
103 this.currentlyBuildingProjectsStack = new Stack<Project> ();
104 this.Toolsets = new ToolsetCollection ();
105 LoadDefaultToolsets ();
106 defaultTasksTableByToolsVersion = new Dictionary<string, TaskDatabase> ();
107 GetDefaultTasks (DefaultToolsVersion);
110 //FIXME: should be loaded from config file
111 void LoadDefaultToolsets ()
113 Toolsets.Add (new Toolset ("2.0",
114 ToolLocationHelper.GetPathToDotNetFramework (TargetDotNetFrameworkVersion.Version20)));
115 Toolsets.Add (new Toolset ("3.0",
116 ToolLocationHelper.GetPathToDotNetFramework (TargetDotNetFrameworkVersion.Version30)));
117 Toolsets.Add (new Toolset ("3.5",
118 ToolLocationHelper.GetPathToDotNetFramework (TargetDotNetFrameworkVersion.Version35)));
119 #if NET_4_0
120 Toolsets.Add (new Toolset ("4.0",
121 ToolLocationHelper.GetPathToDotNetFramework (TargetDotNetFrameworkVersion.Version40)));
122 #endif
125 [MonoTODO]
126 public bool BuildProject (Project project)
128 if (project == null)
129 throw new ArgumentException ("project");
130 return project.Build ();
133 [MonoTODO]
134 public bool BuildProject (Project project, string targetName)
136 if (project == null)
137 throw new ArgumentException ("project");
138 if (targetName == null)
139 return false;
141 return BuildProject (project, new string[] { targetName}, null, BuildSettings.None);
144 [MonoTODO]
145 public bool BuildProject (Project project, string[] targetNames)
147 return BuildProject (project, targetNames, null, BuildSettings.None);
150 [MonoTODO]
151 public bool BuildProject (Project project,
152 string[] targetNames,
153 IDictionary targetOutputs)
155 return BuildProject (project, targetNames, targetOutputs, BuildSettings.None);
158 public bool BuildProject (Project project,
159 string[] targetNames,
160 IDictionary targetOutputs,
161 BuildSettings buildFlags)
163 if (project == null)
164 throw new ArgumentException ("project");
165 if (targetNames == null)
166 return false;
168 if (defaultToolsVersion != null)
169 // it has been explicitly set, xbuild does this..
170 project.ToolsVersion = defaultToolsVersion;
171 return project.Build (targetNames, targetOutputs, buildFlags);
174 [MonoTODO]
175 public bool BuildProjectFile (string projectFile)
177 return BuildProjectFile (projectFile, new string [0]);
180 [MonoTODO]
181 public bool BuildProjectFile (string projectFile,
182 string targetName)
184 return BuildProjectFile (projectFile,
185 targetName == null ? new string [0] : new string [] {targetName});
188 [MonoTODO]
189 public bool BuildProjectFile (string projectFile,
190 string[] targetNames)
192 return BuildProjectFile (projectFile, targetNames, null);
195 [MonoTODO]
196 public bool BuildProjectFile (string projectFile,
197 string[] targetNames,
198 BuildPropertyGroup globalProperties)
200 return BuildProjectFile (projectFile, targetNames, globalProperties, null, BuildSettings.None);
203 [MonoTODO]
204 public bool BuildProjectFile (string projectFile,
205 string[] targetNames,
206 BuildPropertyGroup globalProperties,
207 IDictionary targetOutputs)
209 return BuildProjectFile (projectFile, targetNames, globalProperties, targetOutputs, BuildSettings.None);
212 public bool BuildProjectFile (string projectFile,
213 string[] targetNames,
214 BuildPropertyGroup globalProperties,
215 IDictionary targetOutputs,
216 BuildSettings buildFlags)
218 return BuildProjectFile (projectFile, targetNames, globalProperties, targetOutputs, buildFlags, null);
221 //FIXME: add a test for null @toolsVersion
222 public bool BuildProjectFile (string projectFile,
223 string[] targetNames,
224 BuildPropertyGroup globalProperties,
225 IDictionary targetOutputs,
226 BuildSettings buildFlags, string toolsVersion)
228 Project project;
230 if (projects.ContainsKey (projectFile)) {
231 project = (Project) projects [projectFile];
232 } else {
233 project = CreateNewProject ();
234 project.Load (projectFile);
237 BuildPropertyGroup engine_old_grp = null;
238 BuildPropertyGroup project_old_grp = null;
239 if (globalProperties != null) {
240 engine_old_grp = GlobalProperties.Clone (true);
241 project_old_grp = project.GlobalProperties.Clone (true);
243 // Override project's global properties with the
244 // ones explicitlcur_y specified here
245 foreach (BuildProperty bp in globalProperties)
246 project.GlobalProperties.AddProperty (bp);
247 project.NeedToReevaluate ();
250 try {
251 if (String.IsNullOrEmpty (toolsVersion) && defaultToolsVersion != null)
252 // it has been explicitly set, xbuild does this..
253 //FIXME: should this be cleared after building?
254 project.ToolsVersion = defaultToolsVersion;
255 else
256 project.ToolsVersion = toolsVersion;
258 return project.Build (targetNames, targetOutputs, buildFlags);
259 } finally {
260 if (globalProperties != null) {
261 GlobalProperties = engine_old_grp;
262 project.GlobalProperties = project_old_grp;
267 void CheckBinPath ()
269 if (BinPath == null) {
270 throw new InvalidOperationException ("Before a project can be instantiated, " +
271 "Engine.BinPath must be set to the location on disk where MSBuild " +
272 "is installed. This is used to evaluate $(MSBuildBinPath).");
276 public Project CreateNewProject ()
278 return new Project (this);
281 public Project GetLoadedProject (string projectFullFileName)
283 if (projectFullFileName == null)
284 throw new ArgumentNullException ("projectFullFileName");
286 // FIXME: test it
287 return projects [projectFullFileName];
290 internal void RemoveLoadedProject (Project p)
292 if (p.FullFileName != String.Empty)
293 projects.Remove (p.FullFileName);
296 internal void AddLoadedProject (Project p)
298 if (p.FullFileName != String.Empty)
299 projects.Add (p.FullFileName, p);
302 public void UnloadProject (Project project)
304 if (project == null)
305 throw new ArgumentNullException ("project");
307 if (project.ParentEngine != this)
308 throw new InvalidOperationException ("The \"Project\" object specified does not belong to the correct \"Engine\" object.");
310 project.CheckUnloaded ();
312 if (project.FullFileName != String.Empty)
313 projects.Remove (project.FullFileName);
315 project.Unload ();
318 public void UnloadAllProjects ()
320 IList<Project> values = new List<Project> (projects.Values);
321 foreach (Project p in values)
322 UnloadProject (p);
325 [MonoTODO]
326 public void RegisterLogger (ILogger logger)
328 if (logger == null)
329 throw new ArgumentNullException ("logger");
331 logger.Initialize (eventSource);
332 loggers.Add (logger);
335 [MonoTODO]
336 public void UnregisterAllLoggers ()
338 // FIXME: check if build succeeded
339 // FIXME: it shouldn't be here
340 if (buildStarted)
341 LogBuildFinished (true);
342 foreach (ILogger i in loggers) {
343 i.Shutdown ();
345 loggers.Clear ();
348 internal void StartProjectBuild (Project project, string [] target_names)
350 if (!buildStarted) {
351 LogBuildStarted ();
352 buildStarted = true;
355 if (currentlyBuildingProjectsStack.Count == 0 ||
356 String.Compare (currentlyBuildingProjectsStack.Peek ().FullFileName, project.FullFileName) != 0)
357 LogProjectStarted (project, target_names);
359 currentlyBuildingProjectsStack.Push (project);
362 internal void EndProjectBuild (Project project, bool succeeded)
364 if (!buildStarted)
365 throw new Exception ("build isnt started currently");
367 Project top_project = currentlyBuildingProjectsStack.Pop ();
369 if (String.Compare (project.FullFileName, top_project.FullFileName) != 0)
370 throw new Exception (String.Format (
371 "INTERNAL ERROR: Project finishing is not the same as the one on top " +
372 "of the stack. Project: {0} Top of stack: {1}",
373 project.FullFileName, top_project.FullFileName));
375 if (currentlyBuildingProjectsStack.Count == 0 ||
376 String.Compare (top_project.FullFileName, currentlyBuildingProjectsStack.Peek ().FullFileName) != 0)
377 LogProjectFinished (top_project, succeeded);
379 if (currentlyBuildingProjectsStack.Count == 0) {
380 LogBuildFinished (succeeded);
381 buildStarted = false;
385 void LogProjectStarted (Project project, string [] target_names)
387 ProjectStartedEventArgs psea;
388 if (target_names == null || target_names.Length == 0)
389 psea = new ProjectStartedEventArgs ("Project started.", null, project.FullFileName,
390 String.Empty, null, null);
391 else
392 psea = new ProjectStartedEventArgs ("Project started.", null, project.FullFileName,
393 String.Join (";", target_names), null, null);
394 eventSource.FireProjectStarted (this, psea);
397 void LogProjectFinished (Project project, bool succeeded)
399 ProjectFinishedEventArgs pfea;
400 pfea = new ProjectFinishedEventArgs ("Project started.", null, project.FullFileName, succeeded);
401 eventSource.FireProjectFinished (this, pfea);
404 void LogBuildStarted ()
406 BuildStartedEventArgs bsea;
407 bsea = new BuildStartedEventArgs ("Build started.", null);
408 eventSource.FireBuildStarted (this, bsea);
411 void LogBuildFinished (bool succeeded)
413 BuildFinishedEventArgs bfea;
414 bfea = new BuildFinishedEventArgs ("Build finished.", null, succeeded);
415 eventSource.FireBuildFinished (this, bfea);
418 internal TaskDatabase GetDefaultTasks (string toolsVersion)
420 TaskDatabase db;
421 if (defaultTasksTableByToolsVersion.TryGetValue (toolsVersion, out db))
422 return db;
424 var toolset = Toolsets [toolsVersion];
425 if (toolset == null)
426 throw new Exception ("Unknown toolsversion: " + toolsVersion);
428 string toolsPath = toolset.ToolsPath;
429 string tasksFile = Path.Combine (toolsPath, defaultTasksProjectName);
430 this.LogMessage (MessageImportance.Low, "Loading default tasks for ToolsVersion: {0} from {1}", toolsVersion, tasksFile);
432 // set a empty taskdb here, because the project loading the tasks
433 // file will try to get the default task db
434 defaultTasksTableByToolsVersion [toolsVersion] = new TaskDatabase ();
436 db = defaultTasksTableByToolsVersion [toolsVersion] = RegisterDefaultTasks (tasksFile);
438 return db;
441 TaskDatabase RegisterDefaultTasks (string tasksFile)
443 Project defaultTasksProject = CreateNewProject ();
444 TaskDatabase db;
446 if (File.Exists (tasksFile)) {
447 defaultTasksProject.Load (tasksFile);
448 db = defaultTasksProject.TaskDatabase;
449 } else {
450 this.LogWarning ("Default tasks file {0} not found, ignoring.", tasksFile);
451 db = new TaskDatabase ();
454 return db;
457 public string BinPath {
458 get { return binPath; }
459 set { binPath = value; }
462 public bool BuildEnabled {
463 get { return buildEnabled; }
464 set { buildEnabled = value; }
467 public static Version Version {
468 get { return version; }
471 public static Engine GlobalEngine {
472 get {
473 if (globalEngine == null)
474 globalEngine = new Engine ();
475 return globalEngine;
479 public BuildPropertyGroup GlobalProperties {
480 get { return global_properties; }
481 set { global_properties = value; }
484 public ToolsetCollection Toolsets {
485 get; private set;
488 public string DefaultToolsVersion {
489 get {
490 if (String.IsNullOrEmpty (defaultToolsVersion))
491 #if NET_4_0
492 return "4.0";
493 #elif NET_3_5
494 return "3.5";
495 #else
496 return "2.0";
497 #endif
499 return defaultToolsVersion;
501 set { defaultToolsVersion = value; }
504 public bool IsBuilding {
505 get { return buildStarted; }
508 public bool OnlyLogCriticalEvents {
509 get { return eventSource.OnlyLogCriticalEvents; }
510 set { eventSource.OnlyLogCriticalEvents = value; }
513 internal EventSource EventSource {
514 get { return eventSource; }
517 internal Dictionary<string, ITaskItem[]> BuiltTargetsOutputByName {
518 get { return builtTargetsOutputByName; }
523 #endif