in src/Build/BackEnd/Components/RequestBuilder/IntrinsicTasks/MSBuild.cs [428:587]
public async Task<bool> ExecuteInternal()
{
// If no projects were passed in, just return success.
if ((Projects == null) || (Projects.Length == 0))
{
return true;
}
// We have been asked to unescape all escaped characters before processing
if (this.TargetAndPropertyListSeparators != null && this.TargetAndPropertyListSeparators.Length > 0)
{
ExpandAllTargetsAndProperties();
}
// Parse the global properties into a hashtable.
Hashtable propertiesTable;
if (!PropertyParser.GetTableWithEscaping(Log, ResourceUtilities.FormatResourceString("General.GlobalProperties"), "Properties", this.Properties, out propertiesTable))
{
return false;
}
// Parse out the properties to undefine, if any.
string[] undefinePropertiesArray = null;
if (!String.IsNullOrEmpty(_undefineProperties))
{
Log.LogMessageFromResources(MessageImportance.Low, "General.UndefineProperties");
undefinePropertiesArray = _undefineProperties.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
foreach (string property in undefinePropertiesArray)
{
Log.LogMessageFromText(String.Format(CultureInfo.InvariantCulture, " {0}", property), MessageImportance.Low);
}
}
bool isRunningMultipleNodes = BuildEngine2.IsRunningMultipleNodes;
// If we are in single proc mode and stopOnFirstFailure is true, we cannot build in parallel because
// building in parallel sends all of the projects to the engine at once preventing us from not sending
// any more projects after the first failure. Therefore, to preserve compatibility with whidbey if we are in this situation disable buildInParallel.
if (!isRunningMultipleNodes && _stopOnFirstFailure && _buildInParallel)
{
_buildInParallel = false;
Log.LogMessageFromResources(MessageImportance.Low, "MSBuild.NotBuildingInParallel");
}
// When the condition below is met, provide an information message indicating stopOnFirstFailure
// will have no effect. The reason there will be no effect is, when buildInParallel is true
// All project files will be submitted to the engine all at once, this mean there is no stopping for failures between projects.
// When RunEachTargetSeparately is false, all targets will be submitted to the engine at once, this means there is no way to stop between target failures.
// therefore the first failure seen will be the only failure seen.
if (isRunningMultipleNodes && _buildInParallel && _stopOnFirstFailure && !_runEachTargetSeparately)
{
Log.LogMessageFromResources(MessageImportance.Low, "MSBuild.NoStopOnFirstFailure");
}
// This is a list of string[]. That is, each element in the list is a string[]. Each
// string[] represents a set of target names to build. Depending on the value
// of the RunEachTargetSeparately parameter, we each just call the engine to run all
// the targets together, or we call the engine separately for each target.
ArrayList targetLists = CreateTargetLists(this.Targets, this.RunEachTargetSeparately);
bool success = true;
ITaskItem[] singleProject = null;
bool[] skipProjects = null;
if (_buildInParallel)
{
skipProjects = new bool[Projects.Length];
for (int i = 0; i < skipProjects.Length; i++)
{
skipProjects[i] = true;
}
}
else
{
singleProject = new ITaskItem[1];
}
// Read in each project file. If there are any errors opening the file or parsing the XML,
// raise an event and return False. If any one of the projects fails to build, return False,
// otherwise return True. If parallel build is requested we first check for file existence so
// that we don't pass a non-existent file to IBuildEngine causing an exception
for (int i = 0; i < Projects.Length; i++)
{
ITaskItem project = Projects[i];
string projectPath = FileUtilities.AttemptToShortenPath(project.ItemSpec);
if (_stopOnFirstFailure && !success)
{
// Inform the user that we skipped the remaining projects because StopOnFirstFailure=true.
Log.LogMessageFromResources(MessageImportance.Low, "MSBuild.SkippingRemainingProjects");
// We have encountered a failure. Caller has requested that we not
// continue with remaining projects.
break;
}
if (File.Exists(projectPath) || (_skipNonexistentProjects == SkipNonexistentProjectsBehavior.Build))
{
if (FileUtilities.IsVCProjFilename(projectPath))
{
Log.LogErrorWithCodeFromResources("MSBuild.ProjectUpgradeNeededToVcxProj", project.ItemSpec);
success = false;
continue;
}
// If we are building in parallel we want to only make one call to
// ExecuteTargets once we verified that all projects exist
if (!_buildInParallel)
{
singleProject[0] = project;
bool executeResult = await ExecuteTargets(
singleProject,
propertiesTable,
undefinePropertiesArray,
targetLists,
StopOnFirstFailure,
RebaseOutputs,
BuildEngine3,
Log,
_targetOutputs,
_useResultsCache,
_unloadProjectsOnCompletion,
ToolsVersion
);
if (!executeResult)
{
success = false;
}
}
else
{
skipProjects[i] = false;
}
}
else
{
if (_skipNonexistentProjects == SkipNonexistentProjectsBehavior.Skip)
{
Log.LogMessageFromResources(MessageImportance.High, "MSBuild.ProjectFileNotFoundMessage", project.ItemSpec);
}
else
{
ErrorUtilities.VerifyThrow(_skipNonexistentProjects == SkipNonexistentProjectsBehavior.Error, "skipNonexistentProjects has unexpected value {0}", _skipNonexistentProjects);
Log.LogErrorWithCodeFromResources("MSBuild.ProjectFileNotFound", project.ItemSpec);
success = false;
}
}
}
// We need to build all the projects that were not skipped
if (_buildInParallel)
{
success = await BuildProjectsInParallel(propertiesTable, undefinePropertiesArray, targetLists, success, skipProjects);
}
return success;
}