using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; namespace Microsoft.DotNet.Cli.Build.Framework { public class BuildContext { private IDictionary _completedTargets = new Dictionary(StringComparer.OrdinalIgnoreCase); public static readonly string DefaultTarget = "Default"; private int _maxTargetLen; private Stack _targetStack = new Stack(); public IDictionary Targets { get; } public IDictionary Properties = new Dictionary(); public string BuildDirectory { get; } public object this[string name] { get { if (Properties.ContainsKey(name)) { return Properties[name]; } else { throw new KeyNotFoundException("No property with key " + name + " was found."); } } set { Properties[name] = value; } } public BuildContext(IDictionary targets, string buildDirectory) { Targets = targets; BuildDirectory = buildDirectory; _maxTargetLen = targets.Values.Select(t => t.Name.Length).Max(); } public T Get(string name) => (T)this[name]; public BuildTargetResult RunTarget(string name) => RunTarget(name, force: false); public BuildTargetResult RunTarget(string name, bool force) { BuildTarget target; if (!Targets.TryGetValue(name, out target)) { throw new UndefinedTargetException($"Undefined target: {name}"); } if (!EvaluateTargetConditions(target)) { Reporter.Verbose.WriteLine($"Skipping, Target Conditions not met: {target.Name}"); return new BuildTargetResult(target, success: true); } // Check if it's been completed BuildTargetResult result; if (!force && _completedTargets.TryGetValue(name, out result)) { Reporter.Verbose.WriteLine($"Skipping completed target: {target.Name}"); return result; } // It hasn't, or we're forcing, so run it result = ExecTarget(target); _completedTargets[target.Name] = result; return result; } public void Verbose(string message) { Reporter.Output.WriteLine("trace".White() + $": {message}"); } public void Info(string message) { Reporter.Output.WriteLine("info ".Green() + $": {message}"); } public void Warn(string message) { Reporter.Output.WriteLine("warn ".Yellow() + $": {message}"); } public void Error(string message) { Reporter.Error.WriteLine("error".Red().Bold() + $": {message}"); } private bool EvaluateTargetConditions(BuildTarget target) { if (target == null) { throw new ArgumentNullException(nameof(target)); } if (target.Conditions == null) { return true; } foreach (var condition in target.Conditions) { if (!condition()) { return false; } } return true; } private BuildTargetResult ExecTarget(BuildTarget target) { if (target == null) { throw new ArgumentNullException(nameof(target)); } var sectionName = $"{target.Name.PadRight(_maxTargetLen + 2).Yellow()} ({target.Source.White()})"; BuildReporter.BeginSection("TARGET", sectionName); BuildTargetResult result; // Run the dependencies var dependencyResults = new Dictionary(); var failedDependencyResult = RunDependencies(target, dependencyResults); if (failedDependencyResult != null) { result = failedDependencyResult; } else if (target.Body != null) { try { result = target.Body(new BuildTargetContext(this, target, dependencyResults)); } catch (Exception ex) { result = new BuildTargetResult(target, success: false, exception: ex); } } else { result = new BuildTargetResult(target, success: true); } BuildReporter.EndSection("TARGET", sectionName, result.Success); return result; } private BuildTargetResult RunDependencies(BuildTarget target, Dictionary dependencyResults) { BuildTargetResult result = null; foreach (var dependency in target.Dependencies) { result = RunTarget(dependency); dependencyResults[dependency] = result; if (!result.Success) { return result; } } return null; } } }