/// <summary> /// Initializes a new instance of the <see cref="FileExpressionBase"/> class. /// </summary> /// <param name="rawExpression">An unprocessed string for a single expression.</param> /// <param name="project">The project where the expression exists.</param> /// <param name="task">The task where the expression exists.</param> protected FileExpressionBase(string rawExpression, ProjectInstance project, ProjectTaskInstance task = null) { string trimmedExpression = rawExpression.Trim(); if (task != null) { string copyTaskFilePath = task.Location.File; // Process MsBuildThis* macros // ignore copy tasks within the proj - evaluation will just work. if (!copyTaskFilePath.Equals(project.FullPath, StringComparison.OrdinalIgnoreCase)) { // We leave off the trailing ')' to allow for macro operations. This could allow us to misdetect macros // (e.g. $(MsBuildThisFileButNotReally), but should be rare and should still function correctly even if we // do. if (trimmedExpression.IndexOf("$(MSBuildThisFile", StringComparison.OrdinalIgnoreCase) >= 0) { #pragma warning disable CA1308 // Normalize strings to uppercase trimmedExpression = trimmedExpression.ToLowerInvariant(); #pragma warning restore CA1308 // Normalize strings to uppercase trimmedExpression = trimmedExpression.Replace("$(msbuildthisfiledirectory)", Path.GetDirectoryName(copyTaskFilePath) + "\\"); trimmedExpression = trimmedExpression.Replace("$(msbuildthisfile)", Path.GetFileName(copyTaskFilePath)); trimmedExpression = trimmedExpression.Replace("$(msbuildthisfileextension)", Path.GetExtension(copyTaskFilePath)); trimmedExpression = trimmedExpression.Replace("$(msbuildthisfilefullpath)", copyTaskFilePath); trimmedExpression = trimmedExpression.Replace("$(msbuildthisfilename)", Path.GetFileNameWithoutExtension(copyTaskFilePath)); } } } ProcessedExpression = trimmedExpression; }
/// <summary> /// Initializes a new instance of the <see cref="FileExpressionList"/> class. /// </summary> /// <param name="rawFileListString">The unprocessed list of file expressions.</param> /// <param name="project">The project where the expression list exists.</param> /// <param name="task">The task where the expression list exists.</param> public FileExpressionList(string rawFileListString, ProjectInstance project, ProjectTaskInstance task) { IList <string> expressions = rawFileListString.SplitStringList(); var seenFiles = new HashSet <string>(PathComparer.Instance); foreach (string expression in expressions) { FileExpressionBase parsedExpression = FileExpressionFactory.ParseExpression(expression, project, task); Expressions.Add(parsedExpression); foreach (string file in parsedExpression.EvaluatedFiles) { if (string.IsNullOrWhiteSpace(file)) { continue; } if (seenFiles.Add(file)) { DedupedFiles.Add(file); } AllFiles.Add(file); } } }
/// <summary> /// Initializes a new instance of the <see cref="FileExpressionLiteral"/> class. /// </summary> /// <param name="rawExpression">An unprocessed string for a single expression.</param> /// <param name="project">The project where the expression exists.</param> /// <param name="task">The task where the expression exists.</param> public FileExpressionLiteral(string rawExpression, ProjectInstance project, ProjectTaskInstance task = null) : base(rawExpression, project, task) { string expandedFileListString = project.ExpandString(ProcessedExpression); EvaluatedFiles = expandedFileListString.SplitStringList(); }
/// <summary> /// Initializes a new instance of the <see cref="FileExpressionList"/> class. /// </summary> /// <param name="rawFileListString">The unprocessed list of file expressions.</param> /// <param name="project">The project where the expression list exists.</param> /// <param name="task">The task where the expression list exists.</param> public FileExpressionList(string rawFileListString, ProjectInstance project, ProjectTaskInstance task) { List <string> expressions = rawFileListString.SplitStringList(); NumExpressions = expressions.Count; var seenFiles = new HashSet <string>(PathComparer.Instance); foreach (string expression in expressions) { List <string> evaluatedFiles = FileExpression.EvaluateExpression(expression, project, task, out bool isBatched); if (isBatched) { NumBatchExpressions++; } foreach (string file in evaluatedFiles) { if (string.IsNullOrWhiteSpace(file)) { continue; } if (seenFiles.Add(file)) { DedupedFiles.Add(file); } AllFiles.Add(file); } } }
/// <summary> /// Evaluates file expressions, which are MSBuild expressions that /// evaluate to a list of files (e.g. @(Compile -> '%(filename)') or %(None.filename)). /// <param name="expression">An unprocessed string for a single expression.</param> /// <param name="project">The project where the expression exists.</param> /// <param name="task">The task where the expression exists.</param> /// <returns>the set of all files in the evaluated expression.</returns> public static List <string> EvaluateExpression( string expression, ProjectInstance project, ProjectTaskInstance task, out bool isBatched) { expression = expression.Trim(); isBatched = !expression.StartsWith("@(", StringComparison.Ordinal) && // This would make it a transform expression instead of a batched expression expression.IndexOf("%(", StringComparison.Ordinal) >= 0; return(isBatched ? EvaluateBatchedExpression(expression, project, task) : EvaluateLiteralExpression(expression, project, task)); }
/// <summary> /// Executes the task. /// </summary> public Task <WorkUnitResult> ExecuteTask(TargetLoggingContext targetLoggingContext, BuildRequestEntry requestEntry, ITargetBuilderCallback targetBuilderCallback, ProjectTargetInstanceChild task, TaskExecutionMode mode, Lookup lookupForInference, Lookup lookupForExecution, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { return(Task <WorkUnitResult> .FromResult(new WorkUnitResult(WorkUnitResultCode.Canceled, WorkUnitActionCode.Stop, null))); } ProjectOnErrorInstance errorTask = task as ProjectOnErrorInstance; if (null != errorTask) { ErrorTasks.Add(errorTask); } else { ProjectTaskInstance taskInstance = task as ProjectTaskInstance; ExecutedTasks.Add(taskInstance); if ((mode & TaskExecutionMode.InferOutputsOnly) == TaskExecutionMode.InferOutputsOnly) { lookupForInference.AddNewItem(new ProjectItemInstance(requestEntry.RequestConfiguration.Project, taskInstance.Name + "_Item", "Item", task.Location.File)); } else if ((mode & TaskExecutionMode.ExecuteTaskAndGatherOutputs) == TaskExecutionMode.ExecuteTaskAndGatherOutputs) { lookupForExecution.AddNewItem(new ProjectItemInstance(requestEntry.RequestConfiguration.Project, taskInstance.Name + "_Item", "Item", task.Location.File)); } if (String.Equals(taskInstance.Name, "CallTarget", StringComparison.OrdinalIgnoreCase)) { taskInstance.GetParameter("Targets"); char[] splitter = new char[] { ';' }; targetBuilderCallback.LegacyCallTarget(taskInstance.GetParameter("Targets").Split(splitter), false, taskInstance.Location); } _taskNumber++; if (FailTaskNumber == _taskNumber) { if (taskInstance.ContinueOnError == "True") { return(Task <WorkUnitResult> .FromResult(new WorkUnitResult(WorkUnitResultCode.Failed, WorkUnitActionCode.Continue, null))); } return(Task <WorkUnitResult> .FromResult(new WorkUnitResult(WorkUnitResultCode.Failed, WorkUnitActionCode.Stop, null))); } } return(Task <WorkUnitResult> .FromResult(new WorkUnitResult(WorkUnitResultCode.Success, WorkUnitActionCode.Continue, null))); }
private static ProjectTaskInstance GetTaskInstance(string taskXmlString) { string content = @" <Project> <Target Name='t'> " + taskXmlString + @" </Target> </Project> "; ProjectRootElement xml = ProjectRootElement.Create(XmlReader.Create(new StringReader(content))); Project project = new Project(xml); ProjectInstance instance = project.CreateProjectInstance(); ProjectTaskInstance task = (ProjectTaskInstance)(instance.Targets["t"].Children[0]); return(task); }
/// <summary> /// Return a task instance representing the task XML string passed in /// </summary> private static ProjectTaskInstance GetTaskInstance(string taskXmlString) { string content = @" <Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003' > <Target Name='t'> " + taskXmlString + @" </Target> </Project> "; ProjectRootElement xml = ProjectRootElement.Create(XmlReader.Create(new StringReader(content))); Project project = new Project(xml); ProjectInstance instance = project.CreateProjectInstance(); ProjectTaskInstance task = (ProjectTaskInstance)(instance.Targets["t"].Children[0]); return(task); }
/// <summary> /// Constructs a task logging context from a parent target context and a task node. /// </summary> internal TaskLoggingContext(TargetLoggingContext targetLoggingContext, string projectFullPath, ProjectTargetInstanceChild task) : base(targetLoggingContext) { _targetLoggingContext = targetLoggingContext; _task = task; ProjectTaskInstance taskInstance = task as ProjectTaskInstance; if (taskInstance != null) { _taskName = taskInstance.Name; } else { ProjectPropertyGroupTaskInstance propertyGroupInstance = task as ProjectPropertyGroupTaskInstance; if (propertyGroupInstance != null) { _taskName = "PropertyGroup"; } else { ProjectItemGroupTaskInstance itemGroupInstance = task as ProjectItemGroupTaskInstance; if (itemGroupInstance != null) { _taskName = "ItemGroup"; } else { _taskName = "Unknown"; } } } this.BuildEventContext = LoggingService.LogTaskStarted2 ( targetLoggingContext.BuildEventContext, _taskName, projectFullPath, task.Location.File, task.Location.Line, task.Location.Column ); this.IsValid = true; }
public void ProjectTaskInstanceCanSerializeViaTranslator( IDictionary <string, Tuple <string, MockElementLocation> > parameters, List <ProjectTaskInstanceChild> outputs) { parameters = parameters ?? new Dictionary <string, Tuple <string, MockElementLocation> >(); var parametersCopy = new Dictionary <string, Tuple <string, ElementLocation> >(parameters.Count); foreach (var param in parameters) { parametersCopy[param.Key] = Tuple.Create(param.Value.Item1, (ElementLocation)param.Value.Item2); } var original = CreateTargetTask(null, parametersCopy, outputs); ((INodePacketTranslatable)original).Translate(TranslationHelpers.GetWriteTranslator()); var copy = ProjectTaskInstance.FactoryForDeserialization(TranslationHelpers.GetReadTranslator()); Assert.Equal(original, copy, new TargetTaskComparer()); }
private static ProjectTaskOutputPropertyInstance GetSampleTaskOutputInstance() { string content = @" <Project> <Target Name='t'> <t1> <Output TaskParameter='p' Condition='c' PropertyName='p1'/> </t1> </Target> </Project> "; ProjectRootElement xml = ProjectRootElement.Create(XmlReader.Create(new StringReader(content))); Project project = new Project(xml); ProjectInstance instance = project.CreateProjectInstance(); ProjectTaskInstance task = (ProjectTaskInstance)instance.Targets["t"].Children[0]; ProjectTaskOutputPropertyInstance output = (ProjectTaskOutputPropertyInstance)task.Outputs[0]; return(output); }
/// <summary> /// Create a TaskInstance with some parameters /// </summary> private static ProjectTaskOutputItemInstance GetSampleTaskOutputInstance() { string content = @" <Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003' > <Target Name='t'> <t1> <Output TaskParameter='p' Condition='c' ItemName='i'/> </t1> </Target> </Project> "; ProjectRootElement xml = ProjectRootElement.Create(XmlReader.Create(new StringReader(content))); Project project = new Project(xml); ProjectInstance instance = project.CreateProjectInstance(); ProjectTaskInstance task = (ProjectTaskInstance)instance.Targets["t"].Children[0]; ProjectTaskOutputItemInstance output = (ProjectTaskOutputItemInstance)task.Outputs[0]; return(output); }
/// <summary> /// Simulates executing a task. Execution time simulation is rounded to the closes numeric value. If this value is less than 1000 than nothing happens. /// </summary> public Task <WorkUnitResult> ExecuteTask(TargetLoggingContext targetLoggingContext, BuildRequestEntry requestEntry, ITargetBuilderCallback targetBuilderCallback, ProjectTargetInstanceChild task, TaskExecutionMode mode, Lookup lookupForInference, Lookup lookupForExecution, CancellationToken cancellationToken) { bool cancelled = false; RequestDefinition testDefinition = _testDataProvider[requestEntry.Request.ConfigurationId]; TargetDefinition targetDefinition = testDefinition.ProjectDefinition.TargetsCollection[targetLoggingContext.Target.Name]; ProjectTaskInstance taskInstance = (ProjectTaskInstance)task as ProjectTaskInstance; TaskDefinition taskDefinition = targetDefinition.TasksCollection[taskInstance.Name]; taskDefinition.SignalTaskStarted(); if (testDefinition.ExecutionTime > 1000) { DateTime startTime = DateTime.Now; long executionTimeInSeconds = testDefinition.ExecutionTime / 1000; while (executionTimeInSeconds > 0) { if (cancellationToken.WaitHandle.WaitOne(1, false) == true) { cancelled = true; break; } Thread.Sleep(1000); executionTimeInSeconds -= 1; } } if (!cancelled) { _result = taskDefinition.ExpectedResult; } else { _result = new WorkUnitResult(WorkUnitResultCode.Canceled, WorkUnitActionCode.Stop, new MockTaskBuilderException()); } taskDefinition.SignalTaskCompleted(); _taskDone.Set(); return(Task <BuildResult> .FromResult(_result)); }
/// <summary> /// Initializes a new instance of the <see cref="FileExpressionBatched"/> class. /// </summary> /// <param name="rawExpression">An unprocessed string for a single expression.</param> /// <param name="project">The project where the expression exists.</param> /// <param name="task">The task where the expression exists.</param> public FileExpressionBatched(string rawExpression, ProjectInstance project, ProjectTaskInstance task = null) : base(rawExpression, project, task) { // Copy task has batching in it. Get the batched items if possible, then parse inputs. Match regexMatch = BatchedItemRegex.Match(ProcessedExpression); if (regexMatch.Success) { // If the user didn't specify a metadata item, then we default to Identity string transformItem = string.IsNullOrEmpty(regexMatch.Groups[2].Value) ? BatchedItemRegex.Replace(ProcessedExpression, @"%(Identity)") : BatchedItemRegex.Replace(ProcessedExpression, @"%($2)"); // Convert the batch into a transform. If this is an item -> metadata based transition then it will do the replacements for you. string expandedString = project.ExpandString( $"@({regexMatch.Groups["ItemType"].Value}-> '{transformItem}')"); EvaluatedFiles = expandedString.SplitStringList(); } else { throw new InvalidOperationException(); } }
/// <summary> /// Initializes a new instance of the <see cref="FileExpressionTransform"/> class. /// </summary> /// <param name="rawExpression">An unprocessed string for a single expression.</param> /// <param name="project">The project where the expression exists.</param> /// <param name="task">The task where the expression exists.</param> public FileExpressionTransform(string rawExpression, ProjectInstance project, ProjectTaskInstance task = null) : base(rawExpression, project, task) { }
/// <summary> /// Evaluates a literal expression, e.g. '$(Outdir)\foo.dll'. /// </summary> /// <param name="expression">An unprocessed string for a single expression.</param> /// <param name="project">The project where the expression exists.</param> /// <param name="task">The task where the expression exists.</param> /// <returns>The set of all files in the evaluated expression.</returns> private static List <string> EvaluateLiteralExpression(string expression, ProjectInstance project, ProjectTaskInstance task) { expression = ProcessExpression(expression, project, task); expression = project.ExpandString(expression); return(expression.SplitStringList()); }
/// <summary> /// Factory method that parses an unprocessed Expression string and returns an Expression /// of the appropriate type. /// </summary> /// <param name="rawExpression">An unprocessed string for a single expression.</param> /// <param name="project">The project where the expression exists.</param> /// <param name="task">The task where the expression exists.</param> /// <returns>A file expression class.</returns> public static FileExpressionBase ParseExpression(string rawExpression, ProjectInstance project, ProjectTaskInstance task) { string trimmedExpression = rawExpression.Trim(); if (FileExpressionBatched.IsExpression(trimmedExpression)) { return(new FileExpressionBatched(rawExpression, project, task)); } if (FileExpressionTransform.IsExpression(rawExpression)) { return(new FileExpressionTransform(trimmedExpression, project, task)); } return(new FileExpressionLiteral(rawExpression, project, task)); }
// FIXME: Exception should be caught at caller site. 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); } try { foreach (var child in target.Children) { // Evaluate additional target properties var tp = child as ProjectPropertyGroupTaskInstance; if (tp != null) { if (!args.Project.EvaluateCondition(tp.Condition)) { continue; } foreach (var p in tp.Properties) { if (!args.Project.EvaluateCondition(p.Condition)) { continue; } var value = args.Project.ExpandString(p.Value); project.SetProperty(p.Name, value); } continue; } var ii = child as ProjectItemGroupTaskInstance; if (ii != null) { if (!args.Project.EvaluateCondition(ii.Condition)) { continue; } foreach (var item in ii.Items) { if (!args.Project.EvaluateCondition(item.Condition)) { continue; } project.AddItem(item.ItemType, project.ExpandString(item.Include)); } continue; } var task = child as ProjectTaskInstance; if (task != null) { current_task = task; if (!args.Project.EvaluateCondition(task.Condition)) { LogMessageEvent(new BuildMessageEventArgs(string.Format("Task '{0}' was skipped because condition '{1}' wasn't met.", task.Name, task.Condition), null, null, MessageImportance.Low)); continue; } if (!RunBuildTask(target, task, targetResult, args)) { return(false); } continue; } var onError = child as ProjectOnErrorInstance; if (onError != null) { continue; // evaluated under catch clause. } throw new NotSupportedException(string.Format("Unexpected Target element children \"{0}\"", child.GetType())); } } catch (Exception ex) { // fallback task specified by OnError element foreach (var c in target.Children.OfType <ProjectOnErrorInstance> ()) { if (!args.Project.EvaluateCondition(c.Condition)) { continue; } foreach (var fallbackTarget in project.ExpandString(c.ExecuteTargets).Split(';')) { BuildTargetByName(fallbackTarget, args); } } int line = target.Location != null ? target.Location.Line : 0; int col = target.Location != null ? target.Location.Column : 0; LogErrorEvent(new BuildErrorEventArgs(null, null, target.FullPath, line, col, 0, 0, ex.Message, null, null)); targetResult.Failure(ex); return(false); } 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> (); factoryIdentityParameters ["MSBuildRuntime"] = taskInstance.MSBuildRuntime; factoryIdentityParameters ["MSBuildArchitecture"] = taskInstance.MSBuildArchitecture; var task = args.BuildTaskFactory.CreateTask(taskInstance.Name, factoryIdentityParameters, this); if (task == null) { throw new InvalidOperationException(string.Format("TaskFactory {0} returned null Task", args.BuildTaskFactory)); } LogMessageEvent(new BuildMessageEventArgs(string.Format("Using task {0} from {1}", taskInstance.Name, task.GetType()), null, null, MessageImportance.Low)); task.HostObject = host; task.BuildEngine = this; // Prepare task parameters. var evaluator = new ExpressionEvaluator(project); var evaluatedTaskParams = taskInstance.Parameters.Select(p => new KeyValuePair <string, string> (p.Key, project.ExpandString(evaluator, 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) { switch (p.Key.ToLower()) { case "condition": case "continueonerror": continue; } 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 { prop.SetValue(task, ConvertTo(p.Value, prop.PropertyType, evaluator), 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 = pi.GetValue(task, null); var valueString = ConvertFrom(value); if (toItem != null) { LogMessageEvent(new BuildMessageEventArgs(string.Format("Output Item {0} from TaskParameter {1}: {2}", toItem.ItemType, toItem.TaskParameter, valueString), null, null, MessageImportance.Low)); Action <ITaskItem> addItem = i => { var metadata = new ArrayList(i.MetadataNames).ToArray().Cast <string> ().Select(n => new KeyValuePair <string, string> (n, i.GetMetadata(n))); args.Project.AddItem(toItem.ItemType, i.ItemSpec, metadata); }; var taskItemArray = value as ITaskItem []; if (taskItemArray != null) { foreach (var ti in taskItemArray) { addItem(ti); } } else { var taskItem = value as ITaskItem; if (taskItem != null) { addItem(taskItem); } else { foreach (var item in valueString.Split(';')) { args.Project.AddItem(toItem.ItemType, item); } } } } else { LogMessageEvent(new BuildMessageEventArgs(string.Format("Output Property {0} from TaskParameter {1}: {2}", toProp.PropertyName, toProp.TaskParameter, valueString), null, null, MessageImportance.Low)); args.Project.SetProperty(toProp.PropertyName, valueString); } } } } finally { event_source.FireTaskFinished(this, new TaskFinishedEventArgs("Task Finished", null, project.FullPath, taskInstance.FullPath, taskInstance.Name, taskSuccess)); } return(true); }
/// <summary> /// Create a TaskInstance with some parameters /// </summary> private static ProjectTaskInstance GetSampleTaskInstance() { ProjectTaskInstance task = GetTaskInstance(@"<t1 a='a1' b='b1' ContinueOnError='coe' Condition='c'/>"); return(task); }