diff options
Diffstat (limited to 'mcs/class/Microsoft.Build/Microsoft.Build.Internal/BuildEngine4.cs')
-rw-r--r-- | mcs/class/Microsoft.Build/Microsoft.Build.Internal/BuildEngine4.cs | 634 |
1 files changed, 634 insertions, 0 deletions
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Internal/BuildEngine4.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Internal/BuildEngine4.cs new file mode 100644 index 0000000000..22a8c615d4 --- /dev/null +++ b/mcs/class/Microsoft.Build/Microsoft.Build.Internal/BuildEngine4.cs @@ -0,0 +1,634 @@ +// +// BuildEngine4.cs +// +// Author: +// Atsushi Enomoto (atsushi@xamarin.com) +// +// Copyright (C) 2013 Xamarin Inc. (http://www.xamarin.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +using System; +using System.Collections; +using System.Collections.Generic; +using Microsoft.Build.Execution; +using Microsoft.Build.Framework; +using Microsoft.Build.Evaluation; +using System.Linq; +using System.IO; +using Microsoft.Build.Exceptions; +using System.Globalization; + +namespace Microsoft.Build.Internal +{ + class BuildEngine4 +#if NET_4_5 + : IBuildEngine4 +#else + : IBuildEngine3 +#endif + { + public BuildEngine4 (BuildSubmission submission) + { + this.submission = submission; + event_source = new Microsoft.Build.BuildEngine.EventSource (); + if (submission.BuildManager.OngoingBuildParameters.Loggers != null) + foreach (var l in submission.BuildManager.OngoingBuildParameters.Loggers) + l.Initialize (event_source); + } + + BuildSubmission submission; + ProjectInstance project; + ProjectTaskInstance current_task; + Microsoft.Build.BuildEngine.EventSource event_source; + + public ProjectCollection Projects { + get { return submission.BuildManager.OngoingBuildParameters.ProjectCollection; } + } + + // FIXME: + // While we are not faced to implement those features, there are some modern task execution requirements. + // + // This will have to be available for "out of process" nodes (see NodeAffinity). + // NodeAffinity is set per project file at BuildManager.HostServices. + // When NodeAffinity is set to OutOfProc, it should probably launch different build host + // that runs separate build tasks. (.NET has MSBuildTaskHost.exe which I guess is about that.) + // + // Also note that the complete implementation has to support LoadInSeparateAppDomainAttribute + // (which is most likely derived from AppDomainIsolatedBuildTask) that marks a task to run + // in separate AppDomain. + // + public void BuildProject (Func<bool> checkCancel, BuildResult result, ProjectInstance project, IEnumerable<string> targetNames, IDictionary<string,string> globalProperties, IDictionary<string,string> targetOutputs, string toolsVersion) + { + if (toolsVersion == null) + throw new ArgumentNullException ("toolsVersion"); + + var parameters = submission.BuildManager.OngoingBuildParameters; + var toolset = parameters.GetToolset (toolsVersion); + if (toolset == null) + throw new InvalidOperationException (string.Format ("Toolset version '{0}' was not resolved to valid toolset", toolsVersion)); + LogMessageEvent (new BuildMessageEventArgs (string.Format ("Using Toolset version {0}.", toolsVersion), null, null, MessageImportance.Low)); + var buildTaskFactory = new BuildTaskFactory (BuildTaskDatabase.GetDefaultTaskDatabase (toolset), submission.BuildRequest.ProjectInstance.TaskDatabase); + BuildProject (new InternalBuildArguments () { CheckCancel = checkCancel, Result = result, Project = project, TargetNames = targetNames, GlobalProperties = globalProperties, TargetOutputs = targetOutputs, ToolsVersion = toolsVersion, BuildTaskFactory = buildTaskFactory }); + } + + class InternalBuildArguments + { + public Func<bool> CheckCancel; + public BuildResult Result; + public ProjectInstance Project; + public IEnumerable<string> TargetNames; + public IDictionary<string,string> GlobalProperties; + public IDictionary<string,string> TargetOutputs; + public string ToolsVersion; + public BuildTaskFactory BuildTaskFactory; + + public void AddTargetResult (string targetName, TargetResult targetResult) + { + if (!Result.HasResultsForTarget (targetName)) + Result.AddResultsForTarget (targetName, targetResult); + } + } + + void BuildProject (InternalBuildArguments args) + { + var request = submission.BuildRequest; + var parameters = submission.BuildManager.OngoingBuildParameters; + this.project = args.Project; + + event_source.FireBuildStarted (this, new BuildStartedEventArgs ("Build Started", null, DateTime.Now)); + + try { + + var initialPropertiesFormatted = "Initial Properties:\n" + string.Join (Environment.NewLine, project.Properties.OrderBy (p => p.Name).Select (p => string.Format ("{0} = {1}", p.Name, p.EvaluatedValue)).ToArray ()); + LogMessageEvent (new BuildMessageEventArgs (initialPropertiesFormatted, null, null, MessageImportance.Low)); + var initialItemsFormatted = "Initial Items:\n" + string.Join (Environment.NewLine, project.Items.OrderBy (i => i.ItemType).Select (i => string.Format ("{0} : {1}", i.ItemType, i.EvaluatedInclude)).ToArray ()); + LogMessageEvent (new BuildMessageEventArgs (initialItemsFormatted, null, null, MessageImportance.Low)); + + // null targets -> success. empty targets -> success(!) + if (request.TargetNames == null) + args.Result.OverallResult = BuildResultCode.Success; + else { + foreach (var targetName in (args.TargetNames ?? request.TargetNames).Where (t => t != null)) + BuildTargetByName (targetName, args); + + // FIXME: check .NET behavior, whether cancellation always results in failure. + args.Result.OverallResult = args.CheckCancel () ? BuildResultCode.Failure : args.Result.ResultsByTarget.Any (p => p.Value.ResultCode == TargetResultCode.Failure) ? BuildResultCode.Failure : BuildResultCode.Success; + } + } catch (Exception ex) { + args.Result.OverallResult = BuildResultCode.Failure; + LogErrorEvent (new BuildErrorEventArgs (null, null, project.FullPath, 0, 0, 0, 0, "Unhandled exception occured during a build", null, null)); + LogMessageEvent (new BuildMessageEventArgs ("Exception details: " + ex, null, null, MessageImportance.Low)); + throw; // BuildSubmission re-catches this. + } finally { + event_source.FireBuildFinished (this, new BuildFinishedEventArgs ("Build Finished.", null, args.Result.OverallResult == BuildResultCode.Success, DateTime.Now)); + } + } + + bool BuildTargetByName (string targetName, InternalBuildArguments args) + { + var request = submission.BuildRequest; + var parameters = submission.BuildManager.OngoingBuildParameters; + ProjectTargetInstance target; + TargetResult dummyResult; + + if (args.Result.ResultsByTarget.TryGetValue (targetName, out dummyResult) && dummyResult.ResultCode == TargetResultCode.Success) { + LogMessageEvent (new BuildMessageEventArgs (string.Format ("Target '{0}' was skipped because it was already built successfully.", targetName), null, null, MessageImportance.Low)); + return true; // do not add result. + } + + var targetResult = new TargetResult (); + + // null key is allowed and regarded as blind success(!) (as long as it could retrieve target) + if (!request.ProjectInstance.Targets.TryGetValue (targetName, out target)) + throw new InvalidOperationException (string.Format ("target '{0}' was not found in project '{1}'", targetName, project.FullPath)); + else if (!args.Project.EvaluateCondition (target.Condition)) { + LogMessageEvent (new BuildMessageEventArgs (string.Format ("Target '{0}' was skipped because condition '{1}' was not met.", target.Name, target.Condition), null, null, MessageImportance.Low)); + targetResult.Skip (); + } else { + // process DependsOnTargets first. + foreach (var dep in project.ExpandString (target.DependsOnTargets).Split (';').Select (s => s.Trim ()).Where (s => !string.IsNullOrEmpty (s))) { + if (!BuildTargetByName (dep, args)) { + return false; + } + } + + Func<string,ITaskItem> creator = s => new TargetOutputTaskItem () { ItemSpec = s }; + + event_source.FireTargetStarted (this, new TargetStartedEventArgs ("Target Started", null, target.Name, project.FullPath, target.FullPath)); + try { + if (!string.IsNullOrEmpty (target.Inputs) != !string.IsNullOrEmpty (target.Outputs)) { + targetResult.Failure (new InvalidProjectFileException (target.Location, null, string.Format ("Target {0} has mismatching Inputs and Outputs specification. When one is specified, another one has to be specified too.", targetName), null, null, null)); + } else { + bool skip = false; + if (!string.IsNullOrEmpty (target.Inputs)) { + var inputs = args.Project.GetAllItems (target.Inputs, string.Empty, creator, creator, s => true, (t, s) => { + }); + if (!inputs.Any ()) { + LogMessageEvent (new BuildMessageEventArgs (string.Format ("Target '{0}' was skipped because there is no input.", target.Name), null, null, MessageImportance.Low)); + skip = true; + } else { + var outputs = args.Project.GetAllItems (target.Outputs, string.Empty, creator, creator, s => true, (t, s) => { + }); + var needsUpdates = GetOlderOutputsThanInputs (inputs, outputs).FirstOrDefault (); + if (needsUpdates != null) + LogMessageEvent (new BuildMessageEventArgs (string.Format ("Target '{0}' needs to be built because new output {1} is needed.", target.Name, needsUpdates.ItemSpec), null, null, MessageImportance.Low)); + else { + LogMessageEvent (new BuildMessageEventArgs (string.Format ("Target '{0}' was skipped because all the outputs are newer than all the inputs.", target.Name), null, null, MessageImportance.Low)); + skip = true; + } + } + } + if (skip) { + targetResult.Skip (); + } else { + if (DoBuildTarget (target, targetResult, args)) { + var items = args.Project.GetAllItems (target.Outputs, string.Empty, creator, creator, s => true, (t, s) => { + }); + targetResult.Success (items); + } + } + } + } finally { + event_source.FireTargetFinished (this, new TargetFinishedEventArgs ("Target Finished", null, targetName, project.FullPath, target.FullPath, targetResult.ResultCode != TargetResultCode.Failure)); + } + } + args.AddTargetResult (targetName, targetResult); + + return targetResult.ResultCode != TargetResultCode.Failure; + } + + IEnumerable<ITaskItem> GetOlderOutputsThanInputs (IEnumerable<ITaskItem> inputs, IEnumerable<ITaskItem> outputs) + { + return outputs.Where (o => !File.Exists (o.GetMetadata ("FullPath")) || inputs.Any (i => string.CompareOrdinal (i.GetMetadata ("LastModifiedTime"), o.GetMetadata ("LastModifiedTime")) > 0)); + } + + bool DoBuildTarget (ProjectTargetInstance target, TargetResult targetResult, InternalBuildArguments args) + { + var request = submission.BuildRequest; + + // Here we check cancellation (only after TargetStarted event). + if (args.CheckCancel ()) { + targetResult.Failure (new BuildAbortedException ("Build has canceled")); + return false; + } + + var propsToRestore = new Dictionary<string,string> (); + var itemsToRemove = new List<ProjectItemInstance> (); + try { + // Evaluate additional target properties + foreach (var c in target.Children.OfType<ProjectPropertyGroupTaskInstance> ()) { + if (!args.Project.EvaluateCondition (c.Condition)) + continue; + foreach (var p in c.Properties) { + if (!args.Project.EvaluateCondition (p.Condition)) + continue; + var value = args.Project.ExpandString (p.Value); + propsToRestore.Add (p.Name, project.GetPropertyValue (value)); + project.SetProperty (p.Name, value); + } + } + + // Evaluate additional target items + foreach (var c in target.Children.OfType<ProjectItemGroupTaskInstance> ()) { + if (!args.Project.EvaluateCondition (c.Condition)) + continue; + foreach (var item in c.Items) { + if (!args.Project.EvaluateCondition (item.Condition)) + continue; + Func<string,ProjectItemInstance> creator = i => new ProjectItemInstance (project, item.ItemType, item.Metadata.Select (m => new KeyValuePair<string,string> (m.Name, m.Value)), i); + foreach (var ti in project.GetAllItems (item.Include, item.Exclude, creator, creator, s => s == item.ItemType, (ti, s) => ti.SetMetadata ("RecurseDir", s))) + itemsToRemove.Add (ti); + } + } + + foreach (var c in target.Children.OfType<ProjectOnErrorInstance> ()) { + if (!args.Project.EvaluateCondition (c.Condition)) + continue; + throw new NotImplementedException (); + } + + // run tasks + foreach (var ti in target.Children.OfType<ProjectTaskInstance> ()) { + current_task = ti; + if (!args.Project.EvaluateCondition (ti.Condition)) { + LogMessageEvent (new BuildMessageEventArgs (string.Format ("Task '{0}' was skipped because condition '{1}' wasn't met.", ti.Name, ti.Condition), null, null, MessageImportance.Low)); + continue; + } + if (!RunBuildTask (target, ti, targetResult, args)) + return false; + } + } finally { + // restore temporary property state to the original state. + foreach (var p in propsToRestore) { + if (p.Value == string.Empty) + project.RemoveProperty (p.Key); + else + project.SetProperty (p.Key, p.Value); + } + foreach (var item in itemsToRemove) + project.RemoveItem (item); + } + return true; + } + + bool RunBuildTask (ProjectTargetInstance target, ProjectTaskInstance taskInstance, TargetResult targetResult, InternalBuildArguments args) + { + var request = submission.BuildRequest; + + var host = request.HostServices == null ? null : request.HostServices.GetHostObject (request.ProjectFullPath, target.Name, taskInstance.Name); + + // Create Task instance. + var factoryIdentityParameters = new Dictionary<string,string> (); + #if NET_4_5 + factoryIdentityParameters ["MSBuildRuntime"] = taskInstance.MSBuildRuntime; + factoryIdentityParameters ["MSBuildArchitecture"] = taskInstance.MSBuildArchitecture; + #endif + var task = args.BuildTaskFactory.CreateTask (taskInstance.Name, factoryIdentityParameters, this); + LogMessageEvent (new BuildMessageEventArgs (string.Format ("Using task {0} from {1}", taskInstance.Name, task.GetType ().AssemblyQualifiedName), null, null, MessageImportance.Low)); + task.HostObject = host; + task.BuildEngine = this; + + // Prepare task parameters. + var evaluatedTaskParams = taskInstance.Parameters.Select (p => new KeyValuePair<string,string> (p.Key, project.ExpandString (p.Value))); + + var requiredProps = task.GetType ().GetProperties () + .Where (p => p.CanWrite && p.GetCustomAttributes (typeof (RequiredAttribute), true).Any ()); + var missings = requiredProps.Where (p => !evaluatedTaskParams.Any (tp => tp.Key.Equals (p.Name, StringComparison.OrdinalIgnoreCase))); + if (missings.Any ()) + throw new InvalidOperationException (string.Format ("Task {0} of type {1} is used without specifying mandatory property: {2}", + taskInstance.Name, task.GetType (), string.Join (", ", missings.Select (p => p.Name).ToArray ()))); + + foreach (var p in evaluatedTaskParams) { + var prop = task.GetType ().GetProperty (p.Key); + if (prop == null) + throw new InvalidOperationException (string.Format ("Task {0} does not have property {1}", taskInstance.Name, p.Key)); + if (!prop.CanWrite) + throw new InvalidOperationException (string.Format ("Task {0} has property {1} but it is read-only.", taskInstance.Name, p.Key)); + if (string.IsNullOrEmpty (p.Value) && !requiredProps.Contains (prop)) + continue; + try { + var valueInstance = ConvertTo (p.Value, prop.PropertyType); + prop.SetValue (task, valueInstance, null); + } catch (Exception ex) { + throw new InvalidOperationException (string.Format ("Failed to convert '{0}' for property '{1}' of type {2}", p.Value, prop.Name, prop.PropertyType), ex); + } + } + + // Do execute task. + bool taskSuccess = false; + event_source.FireTaskStarted (this, new TaskStartedEventArgs ("Task Started", null, project.FullPath, taskInstance.FullPath, taskInstance.Name)); + try { + taskSuccess = task.Execute (); + + if (!taskSuccess) { + targetResult.Failure (null); + if (!ContinueOnError) { + return false; + } + } else { + // Evaluate task output properties and items. + foreach (var to in taskInstance.Outputs) { + if (!project.EvaluateCondition (to.Condition)) + continue; + var toItem = to as ProjectTaskOutputItemInstance; + var toProp = to as ProjectTaskOutputPropertyInstance; + string taskParameter = toItem != null ? toItem.TaskParameter : toProp.TaskParameter; + var pi = task.GetType ().GetProperty (taskParameter); + if (pi == null) + throw new InvalidOperationException (string.Format ("Task {0} does not have property {1} specified as TaskParameter", taskInstance.Name, toItem.TaskParameter)); + if (!pi.CanRead) + throw new InvalidOperationException (string.Format ("Task {0} has property {1} specified as TaskParameter, but it is write-only", taskInstance.Name, toItem.TaskParameter)); + var value = ConvertFrom (pi.GetValue (task, null)); + if (toItem != null) { + LogMessageEvent (new BuildMessageEventArgs (string.Format ("Output Item {0} from TaskParameter {1}: {2}", toItem.ItemType, toItem.TaskParameter, value), null, null, MessageImportance.Low)); + foreach (var item in value.Split (';')) + args.Project.AddItem (toItem.ItemType, item); + } else { + LogMessageEvent (new BuildMessageEventArgs (string.Format ("Output Property {0} from TaskParameter {1}: {2}", toProp.PropertyName, toProp.TaskParameter, value), null, null, MessageImportance.Low)); + args.Project.SetProperty (toProp.PropertyName, value); + } + } + } + } finally { + event_source.FireTaskFinished (this, new TaskFinishedEventArgs ("Task Finished", null, project.FullPath, taskInstance.FullPath, taskInstance.Name, taskSuccess)); + } + return true; + } + + object ConvertTo (string source, Type targetType) + { + if (targetType == typeof(ITaskItem) || targetType.IsSubclassOf (typeof(ITaskItem))) + return new TargetOutputTaskItem () { ItemSpec = WindowsCompatibilityExtensions.NormalizeFilePath (source.Trim ()) }; + if (targetType.IsArray) + return new ArrayList (source.Split (';').Select (s => s.Trim ()).Where (s => !string.IsNullOrEmpty (s)).Select (s => ConvertTo (s, targetType.GetElementType ())).ToArray ()) + .ToArray (targetType.GetElementType ()); + if (targetType == typeof(bool)) { + switch (source != null ? source.ToLower (CultureInfo.InvariantCulture) : string.Empty) { + case "true": + case "yes": + case "on": + return true; + case "false": + case "no": + case "off": + case "": + return false; + } + } + return Convert.ChangeType (source == "" ? null : source, targetType); + } + + string ConvertFrom (object source) + { + if (source == null) + return string.Empty; + if (source is ITaskItem) + return ((ITaskItem) source).ItemSpec; + if (source.GetType ().IsArray) + return string.Join (";", ((Array) source).Cast<object> ().Select (o => ConvertFrom (o)).ToArray ()); + else + return (string) Convert.ChangeType (source, typeof (string)); + } + + class TargetOutputTaskItem : ITaskItem2 + { + Hashtable metadata = new Hashtable (); + + #region ITaskItem2 implementation + public string GetMetadataValueEscaped (string metadataName) + { + return ProjectCollection.Escape ((string) metadata [metadataName]); + } + public void SetMetadataValueLiteral (string metadataName, string metadataValue) + { + metadata [metadataName] = ProjectCollection.Unescape (metadataValue); + } + public IDictionary CloneCustomMetadataEscaped () + { + var ret = new Hashtable (); + foreach (DictionaryEntry e in metadata) + ret [e.Key] = ProjectCollection.Escape ((string) e.Value); + return ret; + } + public string EvaluatedIncludeEscaped { + get { return ProjectCollection.Escape (ItemSpec); } + set { ItemSpec = ProjectCollection.Unescape (value); } + } + #endregion + #region ITaskItem implementation + public IDictionary CloneCustomMetadata () + { + return new Hashtable (metadata); + } + public void CopyMetadataTo (ITaskItem destinationItem) + { + foreach (DictionaryEntry e in metadata) + destinationItem.SetMetadata ((string) e.Key, (string) e.Value); + } + public string GetMetadata (string metadataName) + { + var wk = ProjectCollection.GetWellKnownMetadata (metadataName, ItemSpec, Path.GetFullPath, null); + if (wk != null) + return wk; + return (string) metadata [metadataName]; + } + public void RemoveMetadata (string metadataName) + { + metadata.Remove (metadataName); + } + public void SetMetadata (string metadataName, string metadataValue) + { + metadata [metadataName] = metadataValue; + } + public string ItemSpec { get; set; } + public int MetadataCount { + get { return metadata.Count; } + } + public ICollection MetadataNames { + get { return metadata.Keys; } + } + #endregion + } + +#if NET_4_5 + #region IBuildEngine4 implementation + + // task objects are not in use anyways though... + + class TaskObjectRegistration + { + public TaskObjectRegistration (object key, object obj, RegisteredTaskObjectLifetime lifetime, bool allowEarlyCollection) + { + Key = key; + Object = obj; + Lifetime = lifetime; + AllowEarlyCollection = allowEarlyCollection; + } + public object Key { get; private set; } + public object Object { get; private set; } + public RegisteredTaskObjectLifetime Lifetime { get; private set; } + public bool AllowEarlyCollection { get; private set; } + } + + List<TaskObjectRegistration> task_objects = new List<TaskObjectRegistration> (); + + public object GetRegisteredTaskObject (object key, RegisteredTaskObjectLifetime lifetime) + { + var reg = task_objects.FirstOrDefault (t => t.Key == key && t.Lifetime == lifetime); + return reg != null ? reg.Object : null; + } + + public void RegisterTaskObject (object key, object obj, RegisteredTaskObjectLifetime lifetime, bool allowEarlyCollection) + { + task_objects.Add (new TaskObjectRegistration (key, obj, lifetime, allowEarlyCollection)); + } + + public object UnregisterTaskObject (object key, RegisteredTaskObjectLifetime lifetime) + { + var reg = task_objects.FirstOrDefault (t => t.Key == key && t.Lifetime == lifetime); + if (reg != null) + task_objects.Remove (reg); + return reg.Object; + } + #endregion +#endif + + #region IBuildEngine3 implementation + + public BuildEngineResult BuildProjectFilesInParallel (string[] projectFileNames, string[] targetNames, IDictionary[] globalProperties, IList<string>[] removeGlobalProperties, string[] toolsVersion, bool returnTargetOutputs) + { + throw new NotImplementedException (); + } + + public void Reacquire () + { + throw new NotImplementedException (); + } + + public void Yield () + { + throw new NotImplementedException (); + } + + #endregion + + #region IBuildEngine2 implementation + + public bool BuildProjectFile (string projectFileName, string[] targetNames, IDictionary globalProperties, IDictionary targetOutputs, string toolsVersion) + { + var proj = GetProjectInstance (projectFileName, toolsVersion); + var globalPropertiesThatMakeSense = new Dictionary<string,string> (); + foreach (DictionaryEntry p in globalProperties) + globalPropertiesThatMakeSense [(string) p.Key] = (string) p.Value; + var result = new BuildResult (); + var outputs = new Dictionary<string, string> (); + BuildProject (() => false, result, proj, targetNames, globalPropertiesThatMakeSense, outputs, toolsVersion); + foreach (var p in outputs) + targetOutputs [p.Key] = p.Value; + return result.OverallResult == BuildResultCode.Success; + } + + public bool BuildProjectFilesInParallel (string[] projectFileNames, string[] targetNames, IDictionary[] globalProperties, IDictionary[] targetOutputsPerProject, string[] toolsVersion, bool useResultsCache, bool unloadProjectsOnCompletion) + { + throw new NotImplementedException (); + } + + public bool IsRunningMultipleNodes { + get { + throw new NotImplementedException (); + } + } + + ProjectInstance GetProjectInstance (string projectFileName, string toolsVersion) + { + string fullPath = Path.GetFullPath (projectFileName); + if (submission.BuildRequest.ProjectFullPath == fullPath) + return submission.BuildRequest.ProjectInstance; + // FIXME: could also be filtered by global properties + // http://msdn.microsoft.com/en-us/library/microsoft.build.evaluation.projectcollection.getloadedprojects.aspx + var project = Projects.GetLoadedProjects (projectFileName).FirstOrDefault (p => p.ToolsVersion == toolsVersion); + if (project == null) + throw new InvalidOperationException (string.Format ("Project '{0}' is not loaded", projectFileName)); + return submission.BuildManager.GetProjectInstanceForBuild (project); + } + + #endregion + + #region IBuildEngine implementation + + public bool BuildProjectFile (string projectFileName, string[] targetNames, IDictionary globalProperties, IDictionary targetOutputs) + { + return BuildProjectFile (projectFileName, targetNames, globalProperties, targetOutputs, Projects.DefaultToolsVersion); + } + + public void LogCustomEvent (CustomBuildEventArgs e) + { + event_source.FireCustomEventRaised (this, e); + } + + public void LogErrorEvent (BuildErrorEventArgs e) + { + event_source.FireErrorRaised (this, e); + } + + public void LogMessageEvent (BuildMessageEventArgs e) + { + event_source.FireMessageRaised (this, e); + } + + public void LogWarningEvent (BuildWarningEventArgs e) + { + event_source.FireWarningRaised (this, e); + } + + public int ColumnNumberOfTaskNode { + get { return current_task.Location != null ? current_task.Location.Column : 0; } + } + + public bool ContinueOnError { + get { return current_task != null && project.EvaluateCondition (current_task.Condition) && EvaluateContinueOnError (current_task.ContinueOnError); } + } + + bool EvaluateContinueOnError (string value) + { + switch (value) { + case "WarnAndContinue": + case "ErrorAndContinue": + return true; + case "ErrorAndStop": + return false; + } + // empty means "stop on error", so don't pass empty string to EvaluateCondition(). + return !string.IsNullOrEmpty (value) && project.EvaluateCondition (value); + } + + public int LineNumberOfTaskNode { + get { return current_task.Location != null ? current_task.Location.Line : 0; } + } + + public string ProjectFileOfTaskNode { + get { return current_task.FullPath; } + } + + #endregion + } +} + |