In class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine:
[mcs.git] / class / Microsoft.Build.Engine / Microsoft.Build.BuildEngine / Engine.cs
blobb6fe05eb2c630e8b14b8098e9403e2f726e860eb
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 string targets;
388 if (target_names == null || target_names.Length == 0)
389 targets = String.Empty;
390 else
391 targets = String.Join (";", target_names);
393 ProjectStartedEventArgs psea = new ProjectStartedEventArgs ("Project started.", null, project.FullFileName, targets,
394 project.EvaluatedPropertiesAsDictionaryEntries, project.EvaluatedItemsByNameAsDictionaryEntries);
396 eventSource.FireProjectStarted (this, psea);
399 void LogProjectFinished (Project project, bool succeeded)
401 ProjectFinishedEventArgs pfea;
402 pfea = new ProjectFinishedEventArgs ("Project started.", null, project.FullFileName, succeeded);
403 eventSource.FireProjectFinished (this, pfea);
406 void LogBuildStarted ()
408 BuildStartedEventArgs bsea;
409 bsea = new BuildStartedEventArgs ("Build started.", null);
410 eventSource.FireBuildStarted (this, bsea);
413 void LogBuildFinished (bool succeeded)
415 BuildFinishedEventArgs bfea;
416 bfea = new BuildFinishedEventArgs ("Build finished.", null, succeeded);
417 eventSource.FireBuildFinished (this, bfea);
420 internal TaskDatabase GetDefaultTasks (string toolsVersion)
422 TaskDatabase db;
423 if (defaultTasksTableByToolsVersion.TryGetValue (toolsVersion, out db))
424 return db;
426 var toolset = Toolsets [toolsVersion];
427 if (toolset == null)
428 throw new Exception ("Unknown toolsversion: " + toolsVersion);
430 string toolsPath = toolset.ToolsPath;
431 string tasksFile = Path.Combine (toolsPath, defaultTasksProjectName);
432 this.LogMessage (MessageImportance.Low, "Loading default tasks for ToolsVersion: {0} from {1}", toolsVersion, tasksFile);
434 // set a empty taskdb here, because the project loading the tasks
435 // file will try to get the default task db
436 defaultTasksTableByToolsVersion [toolsVersion] = new TaskDatabase ();
438 db = defaultTasksTableByToolsVersion [toolsVersion] = RegisterDefaultTasks (tasksFile);
440 return db;
443 TaskDatabase RegisterDefaultTasks (string tasksFile)
445 Project defaultTasksProject = CreateNewProject ();
446 TaskDatabase db;
448 if (File.Exists (tasksFile)) {
449 defaultTasksProject.Load (tasksFile);
450 db = defaultTasksProject.TaskDatabase;
451 } else {
452 this.LogWarning ("Default tasks file {0} not found, ignoring.", tasksFile);
453 db = new TaskDatabase ();
456 return db;
459 public string BinPath {
460 get { return binPath; }
461 set { binPath = value; }
464 public bool BuildEnabled {
465 get { return buildEnabled; }
466 set { buildEnabled = value; }
469 public static Version Version {
470 get { return version; }
473 public static Engine GlobalEngine {
474 get {
475 if (globalEngine == null)
476 globalEngine = new Engine ();
477 return globalEngine;
481 public BuildPropertyGroup GlobalProperties {
482 get { return global_properties; }
483 set { global_properties = value; }
486 public ToolsetCollection Toolsets {
487 get; private set;
490 public string DefaultToolsVersion {
491 get {
492 if (String.IsNullOrEmpty (defaultToolsVersion))
493 #if NET_4_0
494 return "4.0";
495 #elif NET_3_5
496 return "3.5";
497 #else
498 return "2.0";
499 #endif
501 return defaultToolsVersion;
503 set { defaultToolsVersion = value; }
506 public bool IsBuilding {
507 get { return buildStarted; }
510 public bool OnlyLogCriticalEvents {
511 get { return eventSource.OnlyLogCriticalEvents; }
512 set { eventSource.OnlyLogCriticalEvents = value; }
515 internal EventSource EventSource {
516 get { return eventSource; }
519 internal Dictionary<string, ITaskItem[]> BuiltTargetsOutputByName {
520 get { return builtTargetsOutputByName; }
525 #endif