/// <summary> /// Builds multiple project files in parallel. This is the method the old MSBuild task invokes. /// Thread safe. /// </summary> /// <param name="projectFileNames">The list of projects to build</param> /// <param name="targetNames">The set of targets to build</param> /// <param name="globalProperties">The global properties to use for each project</param> /// <param name="targetOutputsPerProject">The outputs for each target on each project</param> /// <param name="toolsVersion">The tools versions to use</param> /// <param name="useResultsCache">Whether to use the results cache</param> /// <param name="unloadProjectsOnCompletion">Whether to unload projects when we are done.</param> /// <returns>True on success, false otherwise.</returns> public bool BuildProjectFilesInParallel(string[] projectFileNames, string[] targetNames, System.Collections.IDictionary[] globalProperties, System.Collections.IDictionary[] targetOutputsPerProject, string[] toolsVersion, bool useResultsCache, bool unloadProjectsOnCompletion) { bool includeTargetOutputs = (targetOutputsPerProject != null); // If the caller supplies an array to put the target outputs in, it must have the same length as the array of project file names they provided, too. // "MSB3094: "{2}" refers to {0} item(s), and "{3}" refers to {1} item(s). They must have the same number of items." ErrorUtilities.VerifyThrowArgument((targetOutputsPerProject == null) || (projectFileNames.Length == targetOutputsPerProject.Length), "General.TwoVectorsMustHaveSameLength", projectFileNames.Length, targetOutputsPerProject?.Length ?? 0, "projectFileNames", "targetOutputsPerProject"); BuildEngineResult result = BuildProjectFilesInParallel(projectFileNames, targetNames, globalProperties, new List <String> [projectFileNames.Length], toolsVersion, includeTargetOutputs); if (includeTargetOutputs) { // Copy results from result.TargetOutputsPerProject to targetOutputsPerProject // We should always have the same number of entries - although an entry might be empty if a project failed. ErrorUtilities.VerifyThrow(targetOutputsPerProject.Length == result.TargetOutputsPerProject.Count, "{0} != {1}", targetOutputsPerProject.Length, result.TargetOutputsPerProject.Count); for (int i = 0; i < targetOutputsPerProject.Length; i++) { if (targetOutputsPerProject[i] != null) { foreach (KeyValuePair <string, ITaskItem[]> output in result.TargetOutputsPerProject[i]) { targetOutputsPerProject[i].Add(output.Key, output.Value); } } } } BuildRequestsSucceeded = result.Result; return(result.Result); }
public bool BuildProjectFilesInParallel ( string[] projectFileNames, string[] targetNames, IDictionary[] globalProperties, IDictionary[] targetOutputsPerProject, string[] toolsVersion, bool useResultsCache, bool unloadProjectsOnCompletion ) { bool includeTargetOutputs = targetOutputsPerProject != null; BuildEngineResult result = BuildProjectFilesInParallel(projectFileNames, targetNames, globalProperties, new List <String> [projectFileNames.Length], toolsVersion, includeTargetOutputs); if (includeTargetOutputs) { for (int i = 0; i < targetOutputsPerProject.Length; i++) { if (targetOutputsPerProject[i] != null) { foreach (KeyValuePair <string, ITaskItem[]> output in result.TargetOutputsPerProject[i]) { targetOutputsPerProject[i].Add(output.Key, output.Value); } } } } return(result.Result); }
private bool ExecuteCore() { var assemblyNameToProjectFile = JsonConvert.DeserializeObject<Dictionary<string, LiveReferenceData>>(File.ReadAllText(LiveReferenceCacheFile)); var referencePaths = new List<ITaskItem>(); var referencedProjects = new List<LiveReferenceData>(); foreach (var reference in References) { var assemblyName = reference.GetMetadata("FileName"); LiveReferenceData referenceData; if (assemblyNameToProjectFile.TryGetValue(assemblyName, out referenceData)) { referencedProjects.Add(referenceData); } else { referencePaths.Add(reference); } } var additionalAssemblyReferences = AdditionalAssemblyReferences.Split(';').Where(s => !string.IsNullOrEmpty(s)).ToList(); foreach (var assemblyName in additionalAssemblyReferences) { referencedProjects.Add(assemblyNameToProjectFile[assemblyName]); } var projectsToBuild = new List<string>(); foreach (var liveReference in referencedProjects) { if (!File.Exists(liveReference.TargetPath)) { Log.LogMessage(MessageImportance.Normal, "Reference '{0}' doesn't exist. It will be built.", liveReference.TargetPath); projectsToBuild.Add(liveReference.Project); } else { Log.LogMessage(MessageImportance.Normal, "Reference '{0}' exists", liveReference.TargetPath); } } BuildEngineResult result = BuildEngine3.BuildProjectFilesInParallel( projectsToBuild.ToArray(), "Build", new Dictionary<string, string>(), new[] {"CustomAfterBuildCommonTargets"}, null, false ); if (!result.Result) { Log.LogError("Failed to resolve references."); return false; } referencePaths.AddRange(referencedProjects.Select(p => new TaskItem(p.TargetPath))); ReferencePaths = referencePaths.ToArray(); return true; }
private bool ExecuteCore() { var properties = new Dictionary <string, string>(); foreach (var prop in SetProperties.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) { var key = prop.Substring(0, prop.IndexOf('=')).Trim(); var value = prop.Substring(prop.IndexOf('=') + 1).Trim(); if (!string.IsNullOrEmpty(key)) { properties[key] = value; } } var projectFiles = CandidateReferenceProjects .Select(p => p.GetMetadata("FullPath")) .ToArray(); var propertyArray = new IDictionary[projectFiles.Length]; for (int i = 0; i < projectFiles.Length; i++) { propertyArray[i] = properties; } var removePropertiesArray = Enumerable.Repeat((IList <string>)UndefineProperties.Split(new[] { ';' }).Select(p => p.Trim()).Where(s => !string.IsNullOrEmpty(s)).ToArray(), propertyArray.Length).ToArray(); BuildEngineResult result = BuildEngine3.BuildProjectFilesInParallel( projectFiles, Enumerable.Repeat("GetTargetPath", projectFiles.Length).ToArray(), propertyArray, removePropertiesArray, new string[projectFiles.Length], true ); if (!result.Result) { Log.LogError("Building 'GetTargetPath' Failed."); return(false); } var assemblyNameToProject = new Dictionary <string, LiveReferenceData>(); for (int i = 0; i < projectFiles.Length; i++) { string projectFile = projectFiles[i]; IDictionary <string, ITaskItem[]> targetOutputs = result.TargetOutputsPerProject[i]; ITaskItem targetPath = targetOutputs["GetTargetPath"].First(); string assemblyName = targetPath.GetMetadata("FileName"); assemblyNameToProject[assemblyName] = new LiveReferenceData { Project = projectFile, TargetPath = targetPath.GetMetadata("FullPath") }; } File.WriteAllText(LiveReferenceCacheFile, JsonConvert.SerializeObject(assemblyNameToProject, Formatting.Indented)); return(true); }
/// <summary> /// Called by the internal MSBuild task. /// Does not take the lock because it is called by another request builder thread. /// </summary> public async Task <BuildEngineResult> InternalBuildProjects(string[] projectFileNames, string[] targetNames, IDictionary[] globalProperties, IList <String>[] undefineProperties, string[] toolsVersion, bool returnTargetOutputs, bool skipNonexistentTargets = false) { ErrorUtilities.VerifyThrowArgumentNull(projectFileNames, "projectFileNames"); ErrorUtilities.VerifyThrowArgumentNull(globalProperties, "globalProperties"); VerifyActiveProxy(); BuildEngineResult result; if (projectFileNames.Length == 1 && projectFileNames[0] == null && globalProperties[0] == null && (undefineProperties == null || undefineProperties[0] == null) && toolsVersion[0] == null) { bool overallSuccess = true; List <IDictionary <string, ITaskItem[]> > targetOutputsPerProject = null; // This is really a legacy CallTarget invocation ITargetResult[] results = await _targetBuilderCallback.LegacyCallTarget(targetNames, ContinueOnError, _taskLocation); if (returnTargetOutputs) { targetOutputsPerProject = new List <IDictionary <string, ITaskItem[]> >(1); targetOutputsPerProject.Add(new Dictionary <string, ITaskItem[]>(StringComparer.OrdinalIgnoreCase)); } for (int i = 0; i < targetNames.Length; i++) { targetOutputsPerProject[0][targetNames[i]] = results[i].Items; if (results[i].ResultCode == TargetResultCode.Failure) { overallSuccess = false; } } result = new BuildEngineResult(overallSuccess, targetOutputsPerProject); BuildRequestsSucceeded = overallSuccess; } else { // Post the request, then yield up the thread. result = await BuildProjectFilesInParallelAsync(projectFileNames, targetNames, globalProperties, undefineProperties, toolsVersion, true /* ask that target outputs are returned in the buildengineresult */, skipNonexistentTargets); } return(result); }
internal static async Task <bool> ExecuteTargets( ITaskItem[] projects, Dictionary <string, string> propertiesTable, string[] undefineProperties, List <string[]> targetLists, bool stopOnFirstFailure, bool rebaseOutputs, IBuildEngine3 buildEngine, TaskLoggingHelper log, List <ITaskItem> targetOutputs, bool unloadProjectsOnCompletion, string toolsVersion, bool skipNonexistentTargets) { bool success = true; // We don't log a message about the project and targets we're going to // build, because it'll all be in the immediately subsequent ProjectStarted event. var projectDirectory = new string[projects.Length]; var projectNames = new string[projects.Length]; var toolsVersions = new string[projects.Length]; var projectProperties = new Dictionary <string, string> [projects.Length]; var undefinePropertiesPerProject = new List <string> [projects.Length]; for (int i = 0; i < projectNames.Length; i++) { projectNames[i] = null; projectProperties[i] = propertiesTable; if (projects[i] != null) { // Retrieve projectDirectory only the first time. It never changes anyway. string projectPath = FileUtilities.AttemptToShortenPath(projects[i].ItemSpec); projectDirectory[i] = Path.GetDirectoryName(projectPath); projectNames[i] = projects[i].ItemSpec; toolsVersions[i] = toolsVersion; // If the user specified a different set of global properties for this project, then // parse the string containing the properties if (!String.IsNullOrEmpty(projects[i].GetMetadata("Properties"))) { if (!PropertyParser.GetTableWithEscaping (log, ResourceUtilities.FormatResourceStringIgnoreCodeAndKeyword("General.OverridingProperties", projectNames[i]), "Properties", projects[i].GetMetadata("Properties").Split(MSBuildConstants.SemicolonChar, StringSplitOptions.RemoveEmptyEntries), out Dictionary <string, string> preProjectPropertiesTable) ) { return(false); } projectProperties[i] = preProjectPropertiesTable; } if (undefineProperties != null) { undefinePropertiesPerProject[i] = new List <string>(undefineProperties); } // If the user wanted to undefine specific global properties for this project, parse // that string and remove them now. string projectUndefineProperties = projects[i].GetMetadata("UndefineProperties"); if (!String.IsNullOrEmpty(projectUndefineProperties)) { string[] propertiesToUndefine = projectUndefineProperties.Split(MSBuildConstants.SemicolonChar, StringSplitOptions.RemoveEmptyEntries); if (undefinePropertiesPerProject[i] == null) { undefinePropertiesPerProject[i] = new List <string>(propertiesToUndefine.Length); } if (log != null && propertiesToUndefine.Length > 0) { log.LogMessageFromResources(MessageImportance.Low, "General.ProjectUndefineProperties", projectNames[i]); foreach (string property in propertiesToUndefine) { undefinePropertiesPerProject[i].Add(property); log.LogMessageFromText(String.Format(CultureInfo.InvariantCulture, " {0}", property), MessageImportance.Low); } } } // If the user specified a different set of global properties for this project, then // parse the string containing the properties if (!String.IsNullOrEmpty(projects[i].GetMetadata("AdditionalProperties"))) { if (!PropertyParser.GetTableWithEscaping (log, ResourceUtilities.FormatResourceStringIgnoreCodeAndKeyword("General.AdditionalProperties", projectNames[i]), "AdditionalProperties", projects[i].GetMetadata("AdditionalProperties").Split(MSBuildConstants.SemicolonChar, StringSplitOptions.RemoveEmptyEntries), out Dictionary <string, string> additionalProjectPropertiesTable) ) { return(false); } var combinedTable = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase); // First copy in the properties from the global table that not in the additional properties table if (projectProperties[i] != null) { foreach (KeyValuePair <string, string> entry in projectProperties[i]) { if (!additionalProjectPropertiesTable.ContainsKey(entry.Key)) { combinedTable.Add(entry.Key, entry.Value); } } } // Add all the additional properties foreach (KeyValuePair <string, string> entry in additionalProjectPropertiesTable) { combinedTable.Add(entry.Key, entry.Value); } projectProperties[i] = combinedTable; } // If the user specified a different toolsVersion for this project - then override the setting if (!String.IsNullOrEmpty(projects[i].GetMetadata("ToolsVersion"))) { toolsVersions[i] = projects[i].GetMetadata("ToolsVersion"); } } } foreach (string[] targetList in targetLists) { if (stopOnFirstFailure && !success) { // Inform the user that we skipped the remaining targets StopOnFirstFailure=true. log.LogMessageFromResources(MessageImportance.Low, "MSBuild.SkippingRemainingTargets"); // We have encountered a failure. Caller has requested that we not // continue with remaining targets. break; } // Send the project off to the build engine. By passing in null to the // first param, we are indicating that the project to build is the same // as the *calling* project file. var taskHost = (TaskHost)buildEngine; BuildEngineResult result = await taskHost.InternalBuildProjects(projectNames, targetList, projectProperties, undefinePropertiesPerProject, toolsVersions, true /* ask that target outputs are returned in the buildengineresult */, skipNonexistentTargets); bool currentTargetResult = result.Result; IList <IDictionary <string, ITaskItem[]> > targetOutputsPerProject = result.TargetOutputsPerProject; success = success && currentTargetResult; // If the engine was able to satisfy the build request if (currentTargetResult) { for (int i = 0; i < projects.Length; i++) { IEnumerable <string> nonNullTargetList = targetList ?? targetOutputsPerProject[i].Keys; foreach (string targetName in nonNullTargetList) { if (targetOutputsPerProject[i].ContainsKey(targetName)) { ITaskItem[] outputItemsFromTarget = targetOutputsPerProject[i][targetName]; foreach (ITaskItem outputItemFromTarget in outputItemsFromTarget) { // No need to rebase if the calling project is the same as the callee project // (project == null). Also no point in trying to copy item metadata either, // because no items were passed into the Projects parameter! if (projects[i] != null) { // Rebase the output item paths if necessary. No need to rebase if the calling // project is the same as the callee project (project == null). if (rebaseOutputs) { try { outputItemFromTarget.ItemSpec = Path.Combine(projectDirectory[i], outputItemFromTarget.ItemSpec); } catch (ArgumentException e) { log.LogWarningWithCodeFromResources(null, projects[i].ItemSpec, 0, 0, 0, 0, "MSBuild.CannotRebaseOutputItemPath", outputItemFromTarget.ItemSpec, e.Message); } } // Copy the custom item metadata from the "Projects" items to these // output items. projects[i].CopyMetadataTo(outputItemFromTarget); // Set a metadata on the output items called "MSBuildProjectFile" which tells you which project file produced this item. if (String.IsNullOrEmpty(outputItemFromTarget.GetMetadata(ItemMetadataNames.msbuildSourceProjectFile))) { outputItemFromTarget.SetMetadata(ItemMetadataNames.msbuildSourceProjectFile, projects[i].GetMetadata(FileUtilities.ItemSpecModifiers.FullPath)); } } // Set a metadata on the output items called "MSBuildTargetName" which tells you which target produced this item. if (String.IsNullOrEmpty(outputItemFromTarget.GetMetadata(ItemMetadataNames.msbuildSourceTargetName))) { outputItemFromTarget.SetMetadata(ItemMetadataNames.msbuildSourceTargetName, targetName); } } targetOutputs.AddRange(outputItemsFromTarget); } } } } } return(success); }
/// <returns>True if the operation was successful</returns> internal static bool ExecuteTargets ( List <ITaskItem> projects, Dictionary <string, string> propertiesTable, string[] undefineProperties, List <string[]> targetLists, bool stopOnFirstFailure, bool rebaseOutputs, IBuildEngine3 buildEngine, TaskLoggingHelper log, List <ITaskItem> targetOutputs, bool unloadProjectsOnCompletion, string toolsVersion ) { bool success = true; // We don't log a message about the project and targets we're going to // build, because it'll all be in the immediately subsequent ProjectStarted event. var projectDirectory = new string[projects.Count]; var projectNames = new string[projects.Count]; var toolsVersions = new string[projects.Count]; var projectProperties = new Dictionary <string, string> [projects.Count]; var undefinePropertiesPerProject = new IList <string> [projects.Count]; for (int i = 0; i < projectNames.Length; i++) { projectNames[i] = null; projectProperties[i] = propertiesTable; if (projects[i] != null) { // Retrieve projectDirectory only the first time. It never changes anyway. string projectPath = FileUtilities.AttemptToShortenPath(projects[i].ItemSpec); projectDirectory[i] = Path.GetDirectoryName(projectPath); projectNames[i] = projects[i].ItemSpec; toolsVersions[i] = toolsVersion; // If the user specified a different set of global properties for this project, then // parse the string containing the properties if (!String.IsNullOrEmpty(projects[i].GetMetadata("Properties"))) { if (!PropertyParser.GetTableWithEscaping (log, ResourceUtilities.FormatResourceStringIgnoreCodeAndKeyword("General.OverridingProperties", projectNames[i]), "Properties", projects[i].GetMetadata("Properties").Split(MSBuildConstants.SemicolonChar), out Dictionary <string, string> preProjectPropertiesTable) ) { return(false); } projectProperties[i] = preProjectPropertiesTable; } if (undefineProperties != null) { undefinePropertiesPerProject[i] = new List <string>(undefineProperties); } // If the user wanted to undefine specific global properties for this project, parse // that string and remove them now. string projectUndefineProperties = projects[i].GetMetadata("UndefineProperties"); if (!String.IsNullOrEmpty(projectUndefineProperties)) { string[] propertiesToUndefine = projectUndefineProperties.Split(MSBuildConstants.SemicolonChar); if (undefinePropertiesPerProject[i] == null) { undefinePropertiesPerProject[i] = new List <string>(propertiesToUndefine.Length); } if (log != null && propertiesToUndefine.Length > 0) { log.LogMessageFromResources(MessageImportance.Low, "General.ProjectUndefineProperties", projectNames[i]); foreach (string property in propertiesToUndefine) { undefinePropertiesPerProject[i].Add(property); log.LogMessageFromText($" {property}", MessageImportance.Low); } } } // If the user specified a different set of global properties for this project, then // parse the string containing the properties if (!String.IsNullOrEmpty(projects[i].GetMetadata("AdditionalProperties"))) { if (!PropertyParser.GetTableWithEscaping (log, ResourceUtilities.FormatResourceStringIgnoreCodeAndKeyword("General.AdditionalProperties", projectNames[i]), "AdditionalProperties", projects[i].GetMetadata("AdditionalProperties").Split(MSBuildConstants.SemicolonChar), out Dictionary <string, string> additionalProjectPropertiesTable) ) { return(false); } var combinedTable = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase); // First copy in the properties from the global table that not in the additional properties table if (projectProperties[i] != null) { foreach (KeyValuePair <string, string> entry in projectProperties[i]) { if (!additionalProjectPropertiesTable.ContainsKey(entry.Key)) { combinedTable.Add(entry.Key, entry.Value); } } } // Add all the additional properties foreach (KeyValuePair <string, string> entry in additionalProjectPropertiesTable) { combinedTable.Add(entry.Key, entry.Value); } projectProperties[i] = combinedTable; } // If the user specified a different toolsVersion for this project - then override the setting if (!String.IsNullOrEmpty(projects[i].GetMetadata("ToolsVersion"))) { toolsVersions[i] = projects[i].GetMetadata("ToolsVersion"); } } } foreach (string[] targetList in targetLists) { if (stopOnFirstFailure && !success) { // Inform the user that we skipped the remaining targets StopOnFirstFailure=true. log.LogMessageFromResources(MessageImportance.Low, "MSBuild.SkippingRemainingTargets"); // We have encountered a failure. Caller has requested that we not // continue with remaining targets. break; } // Send the project off to the build engine. By passing in null to the // first param, we are indicating that the project to build is the same // as the *calling* project file. BuildEngineResult result = buildEngine.BuildProjectFilesInParallel(projectNames, targetList, projectProperties, undefinePropertiesPerProject, toolsVersions, true /* ask that target outputs are returned in the buildengineresult */); bool currentTargetResult = result.Result; IList <IDictionary <string, ITaskItem[]> > targetOutputsPerProject = result.TargetOutputsPerProject; success = success && currentTargetResult; // If the engine was able to satisfy the build request if (currentTargetResult) { for (int i = 0; i < projects.Count; i++) { IEnumerable <string> nonNullTargetList = targetList ?? targetOutputsPerProject[i].Keys; foreach (string targetName in nonNullTargetList) { if (targetOutputsPerProject[i].TryGetValue(targetName, out ITaskItem[] outputItemsFromTarget))
internal static bool ExecuteTargets(ITaskItem[] projects, Hashtable propertiesTable, string[] undefineProperties, ArrayList targetLists, bool stopOnFirstFailure, bool rebaseOutputs, IBuildEngine3 buildEngine, TaskLoggingHelper log, ArrayList targetOutputs, bool useResultsCache, bool unloadProjectsOnCompletion, string toolsVersion) { bool flag = true; string[] strArray = new string[projects.Length]; string[] projectFileNames = new string[projects.Length]; string[] strArray3 = new string[projects.Length]; IList <IDictionary <string, ITaskItem[]> > targetOutputsPerProject = null; IDictionary[] globalProperties = new IDictionary[projects.Length]; List <string>[] removeGlobalProperties = new List <string> [projects.Length]; for (int i = 0; i < projectFileNames.Length; i++) { projectFileNames[i] = null; globalProperties[i] = propertiesTable; if (projects[i] != null) { string path = Microsoft.Build.Shared.FileUtilities.AttemptToShortenPath(projects[i].ItemSpec); strArray[i] = Path.GetDirectoryName(path); projectFileNames[i] = projects[i].ItemSpec; strArray3[i] = toolsVersion; if (!string.IsNullOrEmpty(projects[i].GetMetadata("Properties"))) { Hashtable hashtable; if (!PropertyParser.GetTableWithEscaping(log, Microsoft.Build.Shared.ResourceUtilities.FormatResourceString("General.OverridingProperties", new object[] { projectFileNames[i] }), "Properties", projects[i].GetMetadata("Properties").Split(new char[] { ';' }), out hashtable)) { return(false); } globalProperties[i] = hashtable; } if (undefineProperties != null) { removeGlobalProperties[i] = new List <string>(undefineProperties); } string metadata = projects[i].GetMetadata("UndefineProperties"); if (!string.IsNullOrEmpty(metadata)) { string[] strArray4 = metadata.Split(new char[] { ';' }); if (removeGlobalProperties[i] == null) { removeGlobalProperties[i] = new List <string>(strArray4.Length); } if ((log != null) && (strArray4.Length > 0)) { log.LogMessageFromResources(MessageImportance.Low, "General.ProjectUndefineProperties", new object[] { projectFileNames[i] }); foreach (string str3 in strArray4) { removeGlobalProperties[i].Add(str3); log.LogMessageFromText(string.Format(CultureInfo.InvariantCulture, " {0}", new object[] { str3 }), MessageImportance.Low); } } } if (!string.IsNullOrEmpty(projects[i].GetMetadata("AdditionalProperties"))) { Hashtable hashtable2; if (!PropertyParser.GetTableWithEscaping(log, Microsoft.Build.Shared.ResourceUtilities.FormatResourceString("General.AdditionalProperties", new object[] { projectFileNames[i] }), "AdditionalProperties", projects[i].GetMetadata("AdditionalProperties").Split(new char[] { ';' }), out hashtable2)) { return(false); } Hashtable hashtable3 = new Hashtable(StringComparer.OrdinalIgnoreCase); if (globalProperties[i] != null) { foreach (DictionaryEntry entry in globalProperties[i]) { if (!hashtable2.Contains(entry.Key)) { hashtable3.Add(entry.Key, entry.Value); } } } foreach (DictionaryEntry entry2 in hashtable2) { hashtable3.Add(entry2.Key, entry2.Value); } globalProperties[i] = hashtable3; } if (!string.IsNullOrEmpty(projects[i].GetMetadata("ToolsVersion"))) { strArray3[i] = projects[i].GetMetadata("ToolsVersion"); } } } foreach (string[] strArray5 in targetLists) { if (stopOnFirstFailure && !flag) { log.LogMessageFromResources(MessageImportance.Low, "MSBuild.SkippingRemainingTargets", new object[0]); return(flag); } bool flag2 = true; BuildEngineResult result = buildEngine.BuildProjectFilesInParallel(projectFileNames, strArray5, globalProperties, removeGlobalProperties, strArray3, true); flag2 = result.Result; targetOutputsPerProject = result.TargetOutputsPerProject; flag = flag && flag2; if (flag2) { for (int j = 0; j < projects.Length; j++) { IEnumerable enumerable = (strArray5 != null) ? ((IEnumerable)strArray5) : ((IEnumerable)targetOutputsPerProject[j].Keys); foreach (string str4 in enumerable) { if (targetOutputsPerProject[j].ContainsKey(str4)) { ITaskItem[] c = targetOutputsPerProject[j][str4]; foreach (ITaskItem item in c) { if (projects[j] != null) { if (rebaseOutputs) { try { item.ItemSpec = Path.Combine(strArray[j], item.ItemSpec); } catch (ArgumentException exception) { log.LogWarningWithCodeFromResources(null, projects[j].ItemSpec, 0, 0, 0, 0, "MSBuild.CannotRebaseOutputItemPath", new object[] { item.ItemSpec, exception.Message }); } } projects[j].CopyMetadataTo(item); if (string.IsNullOrEmpty(item.GetMetadata("MSBuildSourceProjectFile"))) { item.SetMetadata("MSBuildSourceProjectFile", projects[j].GetMetadata("FullPath")); } } if (string.IsNullOrEmpty(item.GetMetadata("MSBuildSourceTargetName"))) { item.SetMetadata("MSBuildSourceTargetName", str4); } } targetOutputs.AddRange(c); } } } } } return(flag); }