2 // Engine.cs: Main engine of XBuild.
5 // Marek Sieradzki (marek.sieradzki@gmail.com)
7 // (C) 2005 Marek Sieradzki
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.
31 using System
.Collections
;
32 using System
.Collections
.Generic
;
34 using Microsoft
.Build
.Framework
;
35 using Microsoft
.Build
.Utilities
;
36 using Mono
.XBuild
.Utilities
;
38 namespace Microsoft
.Build
.BuildEngine
{
43 TaskDatabase defaultTasks
;
44 bool defaultTasksRegistered
;
45 const string defaultTasksProjectName
= "Microsoft.Common.tasks";
46 EventSource eventSource
;
48 BuildPropertyGroup global_properties
;
49 //IDictionary importedProjects;
50 List
<ILogger
> loggers
;
51 //bool onlyLogCriticalEvents;
52 Dictionary
<string, Project
> projects
;
54 // the key here represents the project+target+global_properties set
55 Dictionary
<string, ITaskItem
[]> builtTargetsOutputByName
;
56 Stack
<Project
> currentlyBuildingProjectsStack
;
58 static Engine globalEngine
;
59 static Version version
;
63 version
= new Version ("0.1");
67 : this (ToolLocationHelper
.GetPathToDotNetFramework (TargetDotNetFrameworkVersion
.Version20
))
71 // engine should be invoked with path where binary files are
72 // to find microsoft.build.tasks
73 public Engine (string binPath
)
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
> ();
85 RegisterDefaultTasks ();
89 public bool BuildProject (Project project
)
92 throw new ArgumentException ("project");
93 return project
.Build ();
97 public bool BuildProject (Project project
, string targetName
)
100 throw new ArgumentException ("project");
101 if (targetName
== null)
104 return BuildProject (project
, new string[] { targetName}
, null, BuildSettings
.None
);
108 public bool BuildProject (Project project
, string[] targetNames
)
110 return BuildProject (project
, targetNames
, null, BuildSettings
.None
);
114 public bool BuildProject (Project project
,
115 string[] targetNames
,
116 IDictionary targetOutputs
)
118 return BuildProject (project
, targetNames
, targetOutputs
, BuildSettings
.None
);
121 public bool BuildProject (Project project
,
122 string[] targetNames
,
123 IDictionary targetOutputs
,
124 BuildSettings buildFlags
)
127 throw new ArgumentException ("project");
128 if (targetNames
== null)
131 return project
.Build (targetNames
, targetOutputs
, buildFlags
);
135 public bool BuildProjectFile (string projectFile
)
137 throw new NotImplementedException ();
141 public bool BuildProjectFile (string projectFile
,
144 throw new NotImplementedException ();
148 public bool BuildProjectFile (string projectFile
,
149 string[] targetNames
)
151 throw new NotImplementedException ();
155 public bool BuildProjectFile (string projectFile
,
156 string[] targetNames
,
157 BuildPropertyGroup globalProperties
)
159 return BuildProjectFile (projectFile
, targetNames
, globalProperties
, null, BuildSettings
.None
);
163 public bool BuildProjectFile (string projectFile
,
164 string[] targetNames
,
165 BuildPropertyGroup globalProperties
,
166 IDictionary targetOutputs
)
168 return BuildProjectFile (projectFile
, targetNames
, globalProperties
, targetOutputs
, BuildSettings
.None
);
171 public bool BuildProjectFile (string projectFile
,
172 string[] targetNames
,
173 BuildPropertyGroup globalProperties
,
174 IDictionary targetOutputs
,
175 BuildSettings buildFlags
)
179 if (projects
.ContainsKey (projectFile
)) {
180 project
= (Project
) projects
[projectFile
];
182 project
= CreateNewProject ();
183 project
.Load (projectFile
);
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);
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 ();
200 return project
.Build (targetNames
, targetOutputs
, buildFlags
);
202 if (globalProperties
!= null) {
203 GlobalProperties
= engine_old_grp
;
204 project
.GlobalProperties
= project_old_grp
;
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).");
218 public Project
CreateNewProject ()
220 if (defaultTasksRegistered
)
222 return new Project (this);
225 public Project
GetLoadedProject (string projectFullFileName
)
227 if (projectFullFileName
== null)
228 throw new ArgumentNullException ("projectFullFileName");
231 return projects
[projectFullFileName
];
234 internal void RemoveLoadedProject (Project p
)
236 if (p
.FullFileName
!= String
.Empty
)
237 projects
.Remove (p
.FullFileName
);
240 internal void AddLoadedProject (Project p
)
242 if (p
.FullFileName
!= String
.Empty
)
243 projects
.Add (p
.FullFileName
, p
);
246 public void UnloadProject (Project project
)
249 throw new ArgumentNullException ("project");
251 if (project
.ParentEngine
!= this)
252 throw new InvalidOperationException ("The \"Project\" object specified does not belong to the correct \"Engine\" object.");
254 project
.CheckUnloaded ();
256 if (project
.FullFileName
!= String
.Empty
)
257 projects
.Remove (project
.FullFileName
);
262 public void UnloadAllProjects ()
264 IList
<Project
> values
= new List
<Project
> (projects
.Values
);
265 foreach (Project p
in values
)
270 public void RegisterLogger (ILogger logger
)
273 throw new ArgumentNullException ("logger");
275 logger
.Initialize (eventSource
);
276 loggers
.Add (logger
);
280 public void UnregisterAllLoggers ()
282 // FIXME: check if build succeeded
283 // FIXME: it shouldn't be here
285 LogBuildFinished (true);
286 foreach (ILogger i
in loggers
) {
292 internal void StartProjectBuild (Project project
, string [] target_names
)
299 if (currentlyBuildingProjectsStack
.Count
== 0 ||
300 String
.Compare (currentlyBuildingProjectsStack
.Peek ().FullFileName
, project
.FullFileName
) != 0)
301 LogProjectStarted (project
, target_names
);
303 currentlyBuildingProjectsStack
.Push (project
);
306 internal void EndProjectBuild (Project project
, bool succeeded
)
309 throw new Exception ("build isnt started currently");
311 Project top_project
= currentlyBuildingProjectsStack
.Pop ();
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
));
319 if (currentlyBuildingProjectsStack
.Count
== 0 ||
320 String
.Compare (top_project
.FullFileName
, currentlyBuildingProjectsStack
.Peek ().FullFileName
) != 0)
321 LogProjectFinished (top_project
, succeeded
);
323 if (currentlyBuildingProjectsStack
.Count
== 0) {
324 LogBuildFinished (succeeded
);
325 buildStarted
= false;
329 void LogProjectStarted (Project project
, string [] target_names
)
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);
336 psea
= new ProjectStartedEventArgs ("Project started.", null, project
.FullFileName
,
337 String
.Join (";", target_names
), null, null);
338 eventSource
.FireProjectStarted (this, psea
);
341 void LogProjectFinished (Project project
, bool succeeded
)
343 ProjectFinishedEventArgs pfea
;
344 pfea
= new ProjectFinishedEventArgs ("Project started.", null, project
.FullFileName
, succeeded
);
345 eventSource
.FireProjectFinished (this, pfea
);
348 void LogBuildStarted ()
350 BuildStartedEventArgs bsea
;
351 bsea
= new BuildStartedEventArgs ("Build started.", null);
352 eventSource
.FireBuildStarted (this, bsea
);
355 void LogBuildFinished (bool succeeded
)
357 BuildFinishedEventArgs bfea
;
358 bfea
= new BuildFinishedEventArgs ("Build finished.", null, succeeded
);
359 eventSource
.FireBuildFinished (this, bfea
);
362 void RegisterDefaultTasks ()
364 this.defaultTasksRegistered
= false;
366 Project defaultTasksProject
= CreateNewProject ();
368 if (binPath
!= null) {
369 if (File
.Exists (Path
.Combine (binPath
, defaultTasksProjectName
))) {
370 defaultTasksProject
.Load (Path
.Combine (binPath
, defaultTasksProjectName
));
371 defaultTasks
= defaultTasksProject
.TaskDatabase
;
373 defaultTasks
= new TaskDatabase ();
375 defaultTasks
= new TaskDatabase ();
377 this.defaultTasksRegistered
= true;
380 public string BinPath
{
381 get { return binPath; }
382 set { binPath = value; }
385 public bool BuildEnabled
{
386 get { return buildEnabled; }
387 set { buildEnabled = value; }
390 public static Version Version
{
391 get { return version; }
394 public static Engine GlobalEngine
{
396 if (globalEngine
== null)
397 globalEngine
= new Engine ();
402 public BuildPropertyGroup GlobalProperties
{
403 get { return global_properties; }
404 set { global_properties = value; }
407 public bool OnlyLogCriticalEvents
{
408 get { return eventSource.OnlyLogCriticalEvents; }
409 set { eventSource.OnlyLogCriticalEvents = value; }
412 internal EventSource EventSource
{
413 get { return eventSource; }
416 internal bool DefaultTasksRegistered
{
417 get { return defaultTasksRegistered; }
420 internal TaskDatabase DefaultTasks
{
421 get { return defaultTasks; }
424 internal Dictionary
<string, ITaskItem
[]> BuiltTargetsOutputByName
{
425 get { return builtTargetsOutputByName; }