/// <summary> /// Returns true if the specified project can be built directly, without using a metaproject. /// </summary> private bool CanBuildDirectly(ProjectInstance traversalProject, ProjectInSolution projectToAdd, ProjectConfigurationInSolution projectConfiguration) { // Can we build this project directly, without a metaproject? We can if it's MSBuild-able and has no references building in this configuration. bool canBuildDirectly = false; string unknownProjectTypeErrorMessage; if ((projectToAdd.ProjectType == SolutionProjectType.KnownToBeMSBuildFormat) || (projectToAdd.CanBeMSBuildProjectFile(out unknownProjectTypeErrorMessage))) { canBuildDirectly = true; foreach (string dependencyProjectGuid in projectToAdd.Dependencies) { ProjectInSolution dependencyProject; if (!_solutionFile.ProjectsByGuid.TryGetValue(dependencyProjectGuid, out dependencyProject)) { ProjectFileErrorUtilities.VerifyThrowInvalidProjectFile ( false, "SubCategoryForSolutionParsingErrors", new BuildEventFileInfo(traversalProject.FullPath), "SolutionParseProjectDepNotFoundError", projectToAdd.ProjectGuid, dependencyProjectGuid ); } if (WouldProjectBuild(_solutionFile, _selectedSolutionConfiguration, dependencyProject, projectConfiguration)) { // This is a reference we would have to build, so we can't build the project directly. canBuildDirectly = false; break; } } } return canBuildDirectly; }
/// <summary> /// Produces a set of targets which allows the MSBuild scheduler to schedule projects in the order automatically by /// following their dependencies without enforcing build levels. /// </summary> /// <remarks> /// We want MSBuild to be able to parallelize the builds of these projects where possible and still honor references. /// Since the project files referenced by the solution do not (necessarily) themselves contain actual project references /// to the projects they depend on, we need to synthesize this relationship ourselves. This is done by creating a target /// which first invokes the project's dependencies, then invokes the actual project itself. However, invoking the /// dependencies must also invoke their dependencies and so on down the line. /// /// Additionally, we do not wish to create a separate MSBuild project to contain this target yet we want to parallelize /// calls to these targets. The way to do this is to pass in different global properties to the same project in the same /// MSBuild call. MSBuild easily allows this using the AdditionalProperties metadata which can be specified on an Item. /// /// Assuming the solution project we are generating is called "foo.proj", we can accomplish this parallelism as follows: /// <ItemGroup> /// <ProjectReference Include="Project0"/> /// <ProjectReference Include="Project1"/> /// <ProjectReference Include="Project2"/> /// </ItemGroup> /// /// We now have expressed the top level reference to all projects as @(SolutionReference) and each project's /// set of references as @(PROJECTNAMEReference). We construct our target as: /// /// <Target Name="Build"> /// <MSBuild Projects="@(ProjectReference)" Targets="Build" /> /// <MSBuild Projects="actualProjectName" Targets="Build" /> /// </Target> /// /// The first MSBuild call re-invokes the solution project instructing it to build the reference projects for the /// current project. The second MSBuild call invokes the actual project itself. Because all reference projects have /// the same additional properties, MSBuild will only build the first one it comes across and the rest will be /// satisfied from the cache. /// </remarks> private ProjectInstance CreateMetaproject(ProjectInstance traversalProject, ProjectInSolution project, ProjectConfigurationInSolution projectConfiguration) { // Create a new project instance with global properties and tools version from the existing project ProjectInstance metaprojectInstance = new ProjectInstance(EscapingUtilities.UnescapeAll(GetMetaprojectName(project)), traversalProject, GetMetaprojectGlobalProperties(traversalProject)); // Add the project references which must build before this one. AddMetaprojectReferenceItems(traversalProject, metaprojectInstance, project); // This string holds the error message generated when we try to determine if a project is an MSBuild format // project but it is not. string unknownProjectTypeErrorMessage; if (project.ProjectType == SolutionProjectType.WebProject) { AddMetaprojectTargetForWebProject(traversalProject, metaprojectInstance, project, null); AddMetaprojectTargetForWebProject(traversalProject, metaprojectInstance, project, "Clean"); AddMetaprojectTargetForWebProject(traversalProject, metaprojectInstance, project, "Rebuild"); AddMetaprojectTargetForWebProject(traversalProject, metaprojectInstance, project, "Publish"); } else if ((project.ProjectType == SolutionProjectType.KnownToBeMSBuildFormat) || (project.CanBeMSBuildProjectFile(out unknownProjectTypeErrorMessage))) { string safeItemNameFromProjectName = MakeIntoSafeItemName(project.ProjectName); string targetOutputItemName = string.Format(CultureInfo.InvariantCulture, "{0}BuildOutput", safeItemNameFromProjectName); AddMetaprojectTargetForManagedProject(traversalProject, metaprojectInstance, project, projectConfiguration, "Clean", null); AddMetaprojectTargetForManagedProject(traversalProject, metaprojectInstance, project, projectConfiguration, null, targetOutputItemName); AddMetaprojectTargetForManagedProject(traversalProject, metaprojectInstance, project, projectConfiguration, "Rebuild", targetOutputItemName); AddMetaprojectTargetForManagedProject(traversalProject, metaprojectInstance, project, projectConfiguration, "Publish", null); } else { AddMetaprojectTargetForUnknownProjectType(traversalProject, metaprojectInstance, project, null, unknownProjectTypeErrorMessage); AddMetaprojectTargetForUnknownProjectType(traversalProject, metaprojectInstance, project, "Clean", unknownProjectTypeErrorMessage); AddMetaprojectTargetForUnknownProjectType(traversalProject, metaprojectInstance, project, "Rebuild", unknownProjectTypeErrorMessage); AddMetaprojectTargetForUnknownProjectType(traversalProject, metaprojectInstance, project, "Publish", unknownProjectTypeErrorMessage); } return metaprojectInstance; }