X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fclass%2FMicrosoft.Build.Engine%2FMicrosoft.Build.BuildEngine%2FEngine.cs;h=47b5ffddffd0e5213883adcdeb7ff3040db9727c;hb=e9e6f95822efe00783a98a9863b57e745b824289;hp=87c7d185531a528360925d2e7483e6dc51168680;hpb=b6b13e72e91d5b529a6306ce53bda685932c77db;p=mono.git diff --git a/mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/Engine.cs b/mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/Engine.cs index 87c7d185531..47b5ffddffd 100644 --- a/mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/Engine.cs +++ b/mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/Engine.cs @@ -31,7 +31,9 @@ using System; using System.Collections; using System.Collections.Generic; using System.IO; +using System.Linq; using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; using Mono.XBuild.Utilities; namespace Microsoft.Build.BuildEngine { @@ -39,16 +41,21 @@ namespace Microsoft.Build.BuildEngine { string binPath; bool buildEnabled; - TaskDatabase defaultTasks; - bool defaultTasksRegistered; + Dictionary defaultTasksTableByToolsVersion; const string defaultTasksProjectName = "Microsoft.Common.tasks"; EventSource eventSource; bool buildStarted; - BuildPropertyGroup globalProperties; + //ToolsetDefinitionLocations toolsetLocations; + BuildPropertyGroup global_properties; //IDictionary importedProjects; List loggers; - bool onlyLogCriticalEvents; + //bool onlyLogCriticalEvents; Dictionary projects; + string defaultToolsVersion; + + // the key here represents the project+target+global_properties set + Dictionary builtTargetsOutputByName; + Stack currentlyBuildingProjectsStack; static Engine globalEngine; static Version version; @@ -59,8 +66,27 @@ namespace Microsoft.Build.BuildEngine { } public Engine () - : this (null) + : this (ToolLocationHelper.GetPathToDotNetFramework (TargetDotNetFrameworkVersion.Version20)) + { + } + + public Engine (ToolsetDefinitionLocations locations) + : this (ToolLocationHelper.GetPathToDotNetFramework (TargetDotNetFrameworkVersion.Version20)) { + //toolsetLocations = locations; + } + + public Engine (BuildPropertyGroup globalProperties) + : this (ToolLocationHelper.GetPathToDotNetFramework (TargetDotNetFrameworkVersion.Version20)) + { + this.global_properties = globalProperties; + } + + public Engine (BuildPropertyGroup globalProperties, ToolsetDefinitionLocations locations) + : this (ToolLocationHelper.GetPathToDotNetFramework (TargetDotNetFrameworkVersion.Version20)) + { + this.global_properties = globalProperties; + //toolsetLocations = locations; } // engine should be invoked with path where binary files are @@ -73,27 +99,53 @@ namespace Microsoft.Build.BuildEngine { this.eventSource = new EventSource (); this.loggers = new List (); this.buildStarted = false; - this.globalProperties = new BuildPropertyGroup (); - - RegisterDefaultTasks (); + this.global_properties = new BuildPropertyGroup (); + this.builtTargetsOutputByName = new Dictionary (); + this.currentlyBuildingProjectsStack = new Stack (); + this.Toolsets = new ToolsetCollection (); + LoadDefaultToolsets (); + defaultTasksTableByToolsVersion = new Dictionary (); + } + + //FIXME: should be loaded from config file + void LoadDefaultToolsets () + { + Toolsets.Add (new Toolset ("2.0", + ToolLocationHelper.GetPathToDotNetFramework (TargetDotNetFrameworkVersion.Version20))); + Toolsets.Add (new Toolset ("3.0", + ToolLocationHelper.GetPathToDotNetFramework (TargetDotNetFrameworkVersion.Version30))); + Toolsets.Add (new Toolset ("3.5", + ToolLocationHelper.GetPathToDotNetFramework (TargetDotNetFrameworkVersion.Version35))); +#if NET_4_0 + Toolsets.Add (new Toolset ("4.0", + ToolLocationHelper.GetPathToDotNetFramework (TargetDotNetFrameworkVersion.Version40))); +#endif } [MonoTODO] public bool BuildProject (Project project) { + if (project == null) + throw new ArgumentException ("project"); + builtTargetsOutputByName.Clear (); return project.Build (); } [MonoTODO] public bool BuildProject (Project project, string targetName) { - return BuildProject (project, new string[] { targetName}, new Hashtable (), BuildSettings.None); + if (project == null) + throw new ArgumentException ("project"); + if (targetName == null) + return false; + + return BuildProject (project, new string[] { targetName}, null, BuildSettings.None); } [MonoTODO] public bool BuildProject (Project project, string[] targetNames) { - return BuildProject (project, targetNames, new Hashtable (), BuildSettings.None); + return BuildProject (project, targetNames, null, BuildSettings.None); } [MonoTODO] @@ -104,41 +156,44 @@ namespace Microsoft.Build.BuildEngine { return BuildProject (project, targetNames, targetOutputs, BuildSettings.None); } - [MonoTODO ("use buildFlags")] public bool BuildProject (Project project, string[] targetNames, IDictionary targetOutputs, BuildSettings buildFlags) { - bool result; - - LogProjectStarted (project, targetNames); - - result = project.Build (targetNames, targetOutputs); - - LogProjectFinished (project, result); - - return result; + if (project == null) + throw new ArgumentException ("project"); + if (targetNames == null) + return false; + + if ((buildFlags & BuildSettings.DoNotResetPreviouslyBuiltTargets) != BuildSettings.DoNotResetPreviouslyBuiltTargets) + builtTargetsOutputByName.Clear (); + + if (defaultToolsVersion != null) + // it has been explicitly set, xbuild does this.. + project.ToolsVersion = defaultToolsVersion; + return project.Build (targetNames, targetOutputs, buildFlags); } [MonoTODO] public bool BuildProjectFile (string projectFile) { - throw new NotImplementedException (); + return BuildProjectFile (projectFile, new string [0]); } [MonoTODO] public bool BuildProjectFile (string projectFile, string targetName) { - throw new NotImplementedException (); + return BuildProjectFile (projectFile, + targetName == null ? new string [0] : new string [] {targetName}); } [MonoTODO] public bool BuildProjectFile (string projectFile, string[] targetNames) { - throw new NotImplementedException (); + return BuildProjectFile (projectFile, targetNames, null); } [MonoTODO] @@ -146,7 +201,7 @@ namespace Microsoft.Build.BuildEngine { string[] targetNames, BuildPropertyGroup globalProperties) { - return BuildProjectFile (projectFile, targetNames, globalProperties, new Hashtable (), BuildSettings.None); + return BuildProjectFile (projectFile, targetNames, globalProperties, null, BuildSettings.None); } [MonoTODO] @@ -158,30 +213,101 @@ namespace Microsoft.Build.BuildEngine { return BuildProjectFile (projectFile, targetNames, globalProperties, targetOutputs, BuildSettings.None); } - [MonoTODO ("use buildFlags")] public bool BuildProjectFile (string projectFile, string[] targetNames, BuildPropertyGroup globalProperties, IDictionary targetOutputs, BuildSettings buildFlags) { - bool result; - Project project; + return BuildProjectFile (projectFile, targetNames, globalProperties, targetOutputs, buildFlags, null); + } - if (projects.ContainsKey (projectFile)) { - project = (Project) projects [projectFile]; - LogProjectStarted (project, targetNames); - result = project.Build (targetNames, targetOutputs); - } - else + //FIXME: add a test for null @toolsVersion + public bool BuildProjectFile (string projectFile, + string[] targetNames, + BuildPropertyGroup globalProperties, + IDictionary targetOutputs, + BuildSettings buildFlags, string toolsVersion) + { + bool result = false; + try { + StartEngineBuild (); + result = BuildProjectFileInternal (projectFile, targetNames, globalProperties, targetOutputs, buildFlags, toolsVersion); + return result; + } catch (InvalidProjectFileException ie) { + this.LogErrorWithFilename (projectFile, ie.Message); + this.LogMessage (MessageImportance.Low, String.Format ("{0}: {1}", projectFile, ie.ToString ())); return false; - - LogProjectFinished (project, result); - - return result; + } catch (Exception e) { + this.LogErrorWithFilename (projectFile, e.Message); + this.LogMessage (MessageImportance.Low, String.Format ("{0}: {1}", projectFile, e.ToString ())); + return false; + } finally { + EndEngineBuild (result); + } + } + + bool BuildProjectFileInternal (string projectFile, + string[] targetNames, + BuildPropertyGroup globalProperties, + IDictionary targetOutputs, + BuildSettings buildFlags, string toolsVersion) + { + + if ((buildFlags & BuildSettings.DoNotResetPreviouslyBuiltTargets) != BuildSettings.DoNotResetPreviouslyBuiltTargets) + builtTargetsOutputByName.Clear (); + + Project project; + + bool newProject = false; + if (!projects.TryGetValue (projectFile, out project)) { + project = CreateNewProject (); + newProject = true; + } + + BuildPropertyGroup engine_old_grp = null; + BuildPropertyGroup project_old_grp = null; + if (globalProperties != null) { + engine_old_grp = GlobalProperties.Clone (true); + project_old_grp = project.GlobalProperties.Clone (true); + + // Override project's global properties with the + // ones explicitlcur_y specified here + foreach (BuildProperty bp in globalProperties) + project.GlobalProperties.AddProperty (bp); + + if (!newProject) + project.NeedToReevaluate (); + } + + if (newProject) + project.Load (projectFile); + + try { + string oldProjectToolsVersion = project.ToolsVersion; + if (String.IsNullOrEmpty (toolsVersion) && defaultToolsVersion != null) + // no tv specified, let the project inherit it from the + // engine. 'defaultToolsVersion' will be effective only + // it has been overridden. Otherwise, the project's own + // tv will be used. + project.ToolsVersion = defaultToolsVersion; + else + project.ToolsVersion = toolsVersion; + + try { + return project.Build (targetNames, targetOutputs, buildFlags); + } finally { + project.ToolsVersion = oldProjectToolsVersion; + } + } finally { + if (globalProperties != null) { + GlobalProperties = engine_old_grp; + project.GlobalProperties = project_old_grp; + } + } } - private void CheckBinPath () + void CheckBinPath () { if (BinPath == null) { throw new InvalidOperationException ("Before a project can be instantiated, " + @@ -192,10 +318,6 @@ namespace Microsoft.Build.BuildEngine { public Project CreateNewProject () { - if (defaultTasksRegistered == true) - CheckBinPath (); - // FIXME: I don't really know if it should be here - LogBuildStarted (); return new Project (this); } @@ -204,13 +326,18 @@ namespace Microsoft.Build.BuildEngine { if (projectFullFileName == null) throw new ArgumentNullException ("projectFullFileName"); - return (Project) projects [projectFullFileName]; + Project project; + projects.TryGetValue (projectFullFileName, out project); + + return project; } internal void RemoveLoadedProject (Project p) { - if (p.FullFileName != String.Empty) + if (!String.IsNullOrEmpty (p.FullFileName)) { + ClearBuiltTargetsForProject (p); projects.Remove (p.FullFileName); + } } internal void AddLoadedProject (Project p) @@ -221,21 +348,24 @@ namespace Microsoft.Build.BuildEngine { public void UnloadProject (Project project) { + if (project == null) + throw new ArgumentNullException ("project"); + if (project.ParentEngine != this) - throw new InvalidOperationException ("This project is not loaded in this engine"); + throw new InvalidOperationException ("The \"Project\" object specified does not belong to the correct \"Engine\" object."); project.CheckUnloaded (); - if (project.FullFileName != String.Empty) - projects.Remove (project.FullFileName); + RemoveLoadedProject (project); project.Unload (); } public void UnloadAllProjects () { - foreach (KeyValuePair e in projects) - UnloadProject ((Project) e.Value); + IList values = new List (projects.Values); + foreach (Project p in values) + UnloadProject (p); } [MonoTODO] @@ -252,67 +382,142 @@ namespace Microsoft.Build.BuildEngine { public void UnregisterAllLoggers () { // FIXME: check if build succeeded - LogBuildFinished (true); + // FIXME: it shouldn't be here + if (buildStarted) + LogBuildFinished (true); foreach (ILogger i in loggers) { i.Shutdown (); } loggers.Clear (); } - - private void LogProjectStarted (Project project, string[] targetNames) + + void StartEngineBuild () { - ProjectStartedEventArgs psea; - if (targetNames.Length == 0) { - if (project.DefaultTargets != String.Empty) - psea = new ProjectStartedEventArgs ("Project started.", null, project.FullFileName, - project.DefaultTargets, null, null); - else - psea = new ProjectStartedEventArgs ("Project started.", null, project.FullFileName, "default", null, null); - } else - psea = new ProjectStartedEventArgs ("Project started.", null, project.FullFileName, String.Join (";", - targetNames), null, null); + if (!buildStarted) { + LogBuildStarted (); + buildStarted = true; + } + } + + void EndEngineBuild (bool succeeded) + { + if (buildStarted && currentlyBuildingProjectsStack.Count == 0) { + LogBuildFinished (succeeded); + buildStarted = false; + } + } + + internal void StartProjectBuild (Project project, string [] target_names) + { + StartEngineBuild (); + + if (currentlyBuildingProjectsStack.Count == 0 || + String.Compare (currentlyBuildingProjectsStack.Peek ().FullFileName, project.FullFileName) != 0) + LogProjectStarted (project, target_names); + + currentlyBuildingProjectsStack.Push (project); + } + + internal void EndProjectBuild (Project project, bool succeeded) + { + if (!buildStarted) + throw new Exception ("build isnt started currently"); + + Project top_project = currentlyBuildingProjectsStack.Pop (); + + if (String.Compare (project.FullFileName, top_project.FullFileName) != 0) + throw new Exception (String.Format ( + "INTERNAL ERROR: Project finishing is not the same as the one on top " + + "of the stack. Project: {0} Top of stack: {1}", + project.FullFileName, top_project.FullFileName)); + + if (currentlyBuildingProjectsStack.Count == 0 || + String.Compare (top_project.FullFileName, currentlyBuildingProjectsStack.Peek ().FullFileName) != 0) + LogProjectFinished (top_project, succeeded); + + EndEngineBuild (succeeded); + } + + internal void ClearBuiltTargetsForProject (Project project) + { + string project_key = project.GetKeyForTarget (String.Empty, false); + var to_remove_keys = BuiltTargetsOutputByName.Keys.Where (key => key.StartsWith (project_key)).ToList (); + foreach (string to_remove_key in to_remove_keys) + BuiltTargetsOutputByName.Remove (to_remove_key); + } + + void LogProjectStarted (Project project, string [] target_names) + { + string targets; + if (target_names == null || target_names.Length == 0) + targets = String.Empty; + else + targets = String.Join (";", target_names); + + ProjectStartedEventArgs psea = new ProjectStartedEventArgs ("Project started.", null, project.FullFileName, targets, + project.EvaluatedPropertiesAsDictionaryEntries, project.EvaluatedItemsByNameAsDictionaryEntries); + eventSource.FireProjectStarted (this, psea); } - - private void LogProjectFinished (Project project, bool succeeded) + + void LogProjectFinished (Project project, bool succeeded) { ProjectFinishedEventArgs pfea; pfea = new ProjectFinishedEventArgs ("Project started.", null, project.FullFileName, succeeded); eventSource.FireProjectFinished (this, pfea); } - - private void LogBuildStarted () + + void LogBuildStarted () { BuildStartedEventArgs bsea; bsea = new BuildStartedEventArgs ("Build started.", null); eventSource.FireBuildStarted (this, bsea); } - private void LogBuildFinished (bool succeeded) + void LogBuildFinished (bool succeeded) { BuildFinishedEventArgs bfea; bfea = new BuildFinishedEventArgs ("Build finished.", null, succeeded); eventSource.FireBuildFinished (this, bfea); } + + internal TaskDatabase GetDefaultTasks (string toolsVersion) + { + TaskDatabase db; + if (defaultTasksTableByToolsVersion.TryGetValue (toolsVersion, out db)) + return db; + + var toolset = Toolsets [toolsVersion]; + if (toolset == null) + throw new UnknownToolsVersionException (toolsVersion); + + string toolsPath = toolset.ToolsPath; + string tasksFile = Path.Combine (toolsPath, defaultTasksProjectName); + this.LogMessage (MessageImportance.Low, "Loading default tasks for ToolsVersion: {0} from {1}", toolsVersion, tasksFile); + + // set a empty taskdb here, because the project loading the tasks + // file will try to get the default task db + defaultTasksTableByToolsVersion [toolsVersion] = new TaskDatabase (); + + db = defaultTasksTableByToolsVersion [toolsVersion] = RegisterDefaultTasks (tasksFile); + + return db; + } - private void RegisterDefaultTasks () + TaskDatabase RegisterDefaultTasks (string tasksFile) { - this.defaultTasksRegistered = false; - Project defaultTasksProject = CreateNewProject (); + TaskDatabase db; - if (binPath != null) { - if (File.Exists (Path.Combine (binPath, defaultTasksProjectName)) == true) { - defaultTasksProject.Load (Path.Combine (binPath, defaultTasksProjectName)); - defaultTasks = defaultTasksProject.TaskDatabase; - } else { - defaultTasks = new TaskDatabase (); - } + if (File.Exists (tasksFile)) { + defaultTasksProject.Load (tasksFile); + db = defaultTasksProject.TaskDatabase; } else { - defaultTasks = new TaskDatabase (); + this.LogWarning ("Default tasks file {0} not found, ignoring.", tasksFile); + db = new TaskDatabase (); } - - this.defaultTasksRegistered = true; + + return db; } public string BinPath { @@ -338,10 +543,36 @@ namespace Microsoft.Build.BuildEngine { } public BuildPropertyGroup GlobalProperties { - get { return globalProperties; } - set { globalProperties = value; } + get { return global_properties; } + set { global_properties = value; } + } + + public ToolsetCollection Toolsets { + get; private set; } + public string DefaultToolsVersion { + get { + // This is used as the fall back version if the + // project can't find a version to use + // Hard-coded to 2.0, so it allows even vs2005 projects + // to build correctly, as they won't have a ToolsVersion + // set! + return String.IsNullOrEmpty (defaultToolsVersion) + ? "2.0" + : defaultToolsVersion; + } + set { + if (Toolsets [value] == null) + throw new UnknownToolsVersionException (value); + defaultToolsVersion = value; + } + } + + public bool IsBuilding { + get { return buildStarted; } + } + public bool OnlyLogCriticalEvents { get { return eventSource.OnlyLogCriticalEvents; } set { eventSource.OnlyLogCriticalEvents = value; } @@ -351,12 +582,8 @@ namespace Microsoft.Build.BuildEngine { get { return eventSource; } } - internal bool DefaultTasksRegistered { - get { return defaultTasksRegistered; } - } - - internal TaskDatabase DefaultTasks { - get { return defaultTasks; } + internal Dictionary BuiltTargetsOutputByName { + get { return builtTargetsOutputByName; } } } }