2010-03-12 Jb Evain <jbevain@novell.com>
[mono.git] / mcs / class / Microsoft.Build.Engine / Microsoft.Build.BuildEngine / Engine.cs
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:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
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.
27
28 #if NET_2_0
29
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;
37
38 namespace Microsoft.Build.BuildEngine {
39         public class Engine {
40                 
41                 string                  binPath;
42                 bool                    buildEnabled;
43                 TaskDatabase            defaultTasks;
44                 bool                    defaultTasksRegistered;
45                 const string            defaultTasksProjectName = "Microsoft.Common.tasks";
46                 EventSource             eventSource;
47                 bool                    buildStarted;
48                 BuildPropertyGroup      global_properties;
49                 //IDictionary           importedProjects;
50                 List <ILogger>          loggers;
51                 //bool                  onlyLogCriticalEvents;
52                 Dictionary <string, Project>    projects;
53
54                 // the key here represents the project+target+global_properties set
55                 Dictionary <string, ITaskItem[]> builtTargetsOutputByName;
56                 Stack<Project> currentlyBuildingProjectsStack;
57
58                 static Engine           globalEngine;
59                 static Version          version;
60
61                 static Engine ()
62                 {
63                         version = new Version ("0.1");
64                 }
65                 
66                 public Engine ()
67                         : this (ToolLocationHelper.GetPathToDotNetFramework (TargetDotNetFrameworkVersion.Version20))
68                 {
69                 }
70
71                 // engine should be invoked with path where binary files are
72                 // to find microsoft.build.tasks
73                 public Engine (string binPath)
74                 {
75                         this.binPath = binPath;
76                         this.buildEnabled = true;
77                         this.projects = new Dictionary <string, Project> ();
78                         this.eventSource = new EventSource ();
79                         this.loggers = new List <ILogger> ();
80                         this.buildStarted = false;
81                         this.global_properties = new BuildPropertyGroup ();
82                         this.builtTargetsOutputByName = new Dictionary<string, ITaskItem[]> ();
83                         this.currentlyBuildingProjectsStack = new Stack<Project> ();
84                         
85                         RegisterDefaultTasks ();
86                 }
87                 
88                 [MonoTODO]
89                 public bool BuildProject (Project project)
90                 {
91                         if (project == null)
92                                 throw new ArgumentException ("project");
93                         return project.Build ();
94                 }
95                 
96                 [MonoTODO]
97                 public bool BuildProject (Project project, string targetName)
98                 {
99                         if (project == null)
100                                 throw new ArgumentException ("project");
101                         if (targetName == null)
102                                 return false;
103
104                         return BuildProject (project, new string[] { targetName}, null, BuildSettings.None);
105                 }
106                 
107                 [MonoTODO]
108                 public bool BuildProject (Project project, string[] targetNames)
109                 {
110                         return BuildProject (project, targetNames, null, BuildSettings.None);
111                 }
112
113                 [MonoTODO]
114                 public bool BuildProject (Project project,
115                                           string[] targetNames,
116                                           IDictionary targetOutputs)
117                 {
118                         return BuildProject (project, targetNames, targetOutputs, BuildSettings.None);
119                 }
120                 
121                 public bool BuildProject (Project project,
122                                           string[] targetNames,
123                                           IDictionary targetOutputs,
124                                           BuildSettings buildFlags)
125                 {
126                         if (project == null)
127                                 throw new ArgumentException ("project");
128                         if (targetNames == null)
129                                 return false;
130
131                         return project.Build (targetNames, targetOutputs, buildFlags);
132                 }
133
134                 [MonoTODO]
135                 public bool BuildProjectFile (string projectFile)
136                 {
137                         throw new NotImplementedException ();
138                 }
139                 
140                 [MonoTODO]
141                 public bool BuildProjectFile (string projectFile,
142                                               string targetName)
143                 {
144                         throw new NotImplementedException ();
145                 }
146                 
147                 [MonoTODO]
148                 public bool BuildProjectFile (string projectFile,
149                                               string[] targetNames)
150                 {
151                         throw new NotImplementedException ();
152                 }
153                 
154                 [MonoTODO]
155                 public bool BuildProjectFile (string projectFile,
156                                               string[] targetNames,
157                                               BuildPropertyGroup globalProperties)
158                 {
159                         return BuildProjectFile (projectFile, targetNames, globalProperties, null, BuildSettings.None);
160                 }
161                 
162                 [MonoTODO]
163                 public bool BuildProjectFile (string projectFile,
164                                               string[] targetNames,
165                                               BuildPropertyGroup globalProperties,
166                                               IDictionary targetOutputs)
167                 {
168                         return BuildProjectFile (projectFile, targetNames, globalProperties, targetOutputs, BuildSettings.None);
169                 }
170                 
171                 public bool BuildProjectFile (string projectFile,
172                                               string[] targetNames,
173                                               BuildPropertyGroup globalProperties,
174                                               IDictionary targetOutputs,
175                                               BuildSettings buildFlags)
176                 {
177                         Project project;
178
179                         if (projects.ContainsKey (projectFile)) {
180                                 project = (Project) projects [projectFile];
181                         } else {
182                                 project = CreateNewProject ();
183                                 project.Load (projectFile);
184                         }
185
186                         BuildPropertyGroup engine_old_grp = null;
187                         BuildPropertyGroup project_old_grp = null;
188                         if (globalProperties != null) {
189                                 engine_old_grp = GlobalProperties.Clone (true);
190                                 project_old_grp = project.GlobalProperties.Clone (true);
191
192                                 // Override project's global properties with the
193                                 // ones explicitlcur_y specified here
194                                 foreach (BuildProperty bp in globalProperties)
195                                         project.GlobalProperties.AddProperty (bp);
196                                 project.NeedToReevaluate ();
197                         }
198
199                         try {
200                                 return project.Build (targetNames, targetOutputs, buildFlags);
201                         } finally {
202                                 if (globalProperties != null) {
203                                         GlobalProperties = engine_old_grp;
204                                         project.GlobalProperties = project_old_grp;
205                                 }
206                         }
207                 }
208
209                 void CheckBinPath ()
210                 {
211                         if (BinPath == null) {
212                                 throw new InvalidOperationException ("Before a project can be instantiated, " +
213                                         "Engine.BinPath must be set to the location on disk where MSBuild " + 
214                                         "is installed. This is used to evaluate $(MSBuildBinPath).");
215                         }
216                 }
217
218                 public Project CreateNewProject ()
219                 {
220                         if (defaultTasksRegistered)
221                                 CheckBinPath ();
222                         return new Project (this);
223                 }
224
225                 public Project GetLoadedProject (string projectFullFileName)
226                 {
227                         if (projectFullFileName == null)
228                                 throw new ArgumentNullException ("projectFullFileName");
229                         
230                         // FIXME: test it
231                         return projects [projectFullFileName];
232                 }
233
234                 internal void RemoveLoadedProject (Project p)
235                 {
236                         if (p.FullFileName != String.Empty)
237                                 projects.Remove (p.FullFileName);
238                 }
239
240                 internal void AddLoadedProject (Project p)
241                 {
242                         if (p.FullFileName != String.Empty)
243                                 projects.Add (p.FullFileName, p);
244                 }
245         
246                 public void UnloadProject (Project project)
247                 {
248                         if (project == null)
249                                 throw new ArgumentNullException ("project");
250
251                         if (project.ParentEngine != this)
252                                 throw new InvalidOperationException ("The \"Project\" object specified does not belong to the correct \"Engine\" object.");
253                         
254                         project.CheckUnloaded ();
255                         
256                         if (project.FullFileName != String.Empty)
257                                 projects.Remove (project.FullFileName);
258                         
259                         project.Unload ();
260                 }
261
262                 public void UnloadAllProjects ()
263                 {
264                         IList<Project> values = new List<Project> (projects.Values);
265                         foreach (Project p in values)
266                                 UnloadProject (p);
267                 }
268
269                 [MonoTODO]
270                 public void RegisterLogger (ILogger logger)
271                 {
272                         if (logger == null)
273                                 throw new ArgumentNullException ("logger");
274                         
275                         logger.Initialize (eventSource);
276                         loggers.Add (logger);
277                 }
278                 
279                 [MonoTODO]
280                 public void UnregisterAllLoggers ()
281                 {
282                         // FIXME: check if build succeeded
283                         // FIXME: it shouldn't be here
284                         if (buildStarted)
285                                 LogBuildFinished (true);
286                         foreach (ILogger i in loggers) {
287                                 i.Shutdown ();
288                         }
289                         loggers.Clear ();
290                 }
291
292                 internal void StartProjectBuild (Project project, string [] target_names)
293                 {
294                         if (!buildStarted) {
295                                 LogBuildStarted ();
296                                 buildStarted = true;
297                         }
298
299                         if (currentlyBuildingProjectsStack.Count == 0 ||
300                                 String.Compare (currentlyBuildingProjectsStack.Peek ().FullFileName, project.FullFileName) != 0)
301                                         LogProjectStarted (project, target_names);
302
303                         currentlyBuildingProjectsStack.Push (project);
304                 }
305
306                 internal void EndProjectBuild (Project project, bool succeeded)
307                 {
308                         if (!buildStarted)
309                                 throw new Exception ("build isnt started currently");
310
311                         Project top_project = currentlyBuildingProjectsStack.Pop ();
312
313                         if (String.Compare (project.FullFileName, top_project.FullFileName) != 0)
314                                 throw new Exception (String.Format (
315                                                         "INTERNAL ERROR: Project finishing is not the same as the one on top " +
316                                                         "of the stack. Project: {0} Top of stack: {1}",
317                                                         project.FullFileName, top_project.FullFileName));
318
319                         if (currentlyBuildingProjectsStack.Count == 0 ||
320                                 String.Compare (top_project.FullFileName, currentlyBuildingProjectsStack.Peek ().FullFileName) != 0)
321                                 LogProjectFinished (top_project, succeeded);
322
323                         if (currentlyBuildingProjectsStack.Count == 0) {
324                                 LogBuildFinished (succeeded);
325                                 buildStarted = false;
326                         }
327                 }
328
329                 void LogProjectStarted (Project project, string [] target_names)
330                 {
331                         ProjectStartedEventArgs psea;
332                         if (target_names == null || target_names.Length == 0)
333                                 psea = new ProjectStartedEventArgs ("Project started.", null, project.FullFileName,
334                                                 String.Empty, null, null);
335                         else
336                                 psea = new ProjectStartedEventArgs ("Project started.", null, project.FullFileName,
337                                                 String.Join (";", target_names), null, null);
338                         eventSource.FireProjectStarted (this, psea);
339                 }
340
341                 void LogProjectFinished (Project project, bool succeeded)
342                 {
343                         ProjectFinishedEventArgs pfea;
344                         pfea = new ProjectFinishedEventArgs ("Project started.", null, project.FullFileName, succeeded);
345                         eventSource.FireProjectFinished (this, pfea);
346                 }
347
348                 void LogBuildStarted ()
349                 {
350                         BuildStartedEventArgs bsea;
351                         bsea = new BuildStartedEventArgs ("Build started.", null);
352                         eventSource.FireBuildStarted (this, bsea);
353                 }
354                 
355                 void LogBuildFinished (bool succeeded)
356                 {
357                         BuildFinishedEventArgs bfea;
358                         bfea = new BuildFinishedEventArgs ("Build finished.", null, succeeded);
359                         eventSource.FireBuildFinished (this, bfea);
360                 }
361                 
362                 void RegisterDefaultTasks ()
363                 {
364                         this.defaultTasksRegistered = false;
365                         
366                         Project defaultTasksProject = CreateNewProject ();
367                         
368                         if (binPath != null) {
369                                 if (File.Exists (Path.Combine (binPath, defaultTasksProjectName))) {
370                                         defaultTasksProject.Load (Path.Combine (binPath, defaultTasksProjectName));
371                                         defaultTasks = defaultTasksProject.TaskDatabase;
372                                 } else
373                                         defaultTasks = new TaskDatabase ();
374                         } else
375                                 defaultTasks = new TaskDatabase ();
376                         
377                         this.defaultTasksRegistered = true;
378                 }
379
380                 public string BinPath {
381                         get { return binPath; }
382                         set { binPath = value; }
383                 }
384
385                 public bool BuildEnabled {
386                         get { return buildEnabled; }
387                         set { buildEnabled = value; }
388                 }
389
390                 public static Version Version {
391                         get { return version; }
392                 }
393
394                 public static Engine GlobalEngine {
395                         get {
396                                 if (globalEngine == null)
397                                         globalEngine = new Engine ();
398                                 return globalEngine;
399                         }
400                 }
401
402                 public BuildPropertyGroup GlobalProperties {
403                         get { return global_properties; }
404                         set { global_properties = value; }
405                 }
406
407                 public bool OnlyLogCriticalEvents {
408                         get { return eventSource.OnlyLogCriticalEvents; }
409                         set { eventSource.OnlyLogCriticalEvents = value; }
410                 }
411                 
412                 internal EventSource EventSource {
413                         get { return eventSource; }
414                 }
415                 
416                 internal bool DefaultTasksRegistered {
417                         get { return defaultTasksRegistered; }
418                 }
419                 
420                 internal TaskDatabase DefaultTasks {
421                         get { return defaultTasks; }
422                 }
423
424                 internal Dictionary<string, ITaskItem[]> BuiltTargetsOutputByName {
425                         get { return builtTargetsOutputByName; }
426                 }
427         }
428 }
429
430 #endif