in Sharpmake/Solution.cs [246:438]
internal void Link(Builder builder)
{
if (_dependenciesResolved)
return;
bool hasFastBuildProjectConf = false;
var unlinkedConfigurations = new Dictionary<Solution.Configuration, List<Project.Configuration>>(); // This will hold MSBuild -> Fastbuild refs
using (builder.CreateProfilingScope("Solution.Link:confs", Configurations.Count))
{
foreach (Solution.Configuration solutionConfiguration in Configurations)
{
// Build SolutionFilesMapping
string configurationFile = Path.Combine(solutionConfiguration.SolutionPath, solutionConfiguration.SolutionFileName);
var fileConfigurationList = SolutionFilesMapping.GetValueOrAdd(configurationFile, new List<Solution.Configuration>());
fileConfigurationList.Add(solutionConfiguration);
var unlinkedList = unlinkedConfigurations.GetValueOrAdd(solutionConfiguration, new List<Project.Configuration>());
// solutionConfiguration.IncludedProjectInfos will be appended
// while iterating, but with projects that we already have resolved,
// so no need to parse them again
int origCount = solutionConfiguration.IncludedProjectInfos.Count;
for (int i = 0; i < origCount; ++i)
{
Configuration.IncludedProjectInfo configurationProject = solutionConfiguration.IncludedProjectInfos[i];
bool projectIsInactive = configurationProject.InactiveProject;
Project project = builder.GetProject(configurationProject.Type);
Project.Configuration projectConfiguration = project.GetConfiguration(configurationProject.Target);
if (projectConfiguration == null)
{
var messageBuilder = new System.Text.StringBuilder();
messageBuilder.AppendFormat("Resolving dependencies for solution {0}, target '{1}': cannot find target '{3}' in project {2}",
GetType().FullName, solutionConfiguration.Target, project.GetType().FullName, configurationProject.Target);
messageBuilder.AppendLine();
if (project.Configurations.Any())
{
messageBuilder.AppendLine("Project configurations are:");
int confNum = 0;
foreach (var conf in project.Configurations)
messageBuilder.AppendLine(++confNum + "/" + project.Configurations.Count + " " + conf.ToString());
}
else
{
messageBuilder.AppendLine("The project does not contain any configurations!");
}
Trace.WriteLine(messageBuilder.ToString());
Debugger.Break();
throw new Error(messageBuilder.ToString());
}
if (configurationProject.Project == null)
configurationProject.Project = project;
else if (configurationProject.Project != project)
throw new Error("Tried to match more than one project to Project type.");
if (configurationProject.Configuration == null)
configurationProject.Configuration = projectConfiguration;
else if (configurationProject.Configuration != projectConfiguration)
throw new Error("Tried to match more than one Project Configuration to a solution configuration.");
hasFastBuildProjectConf |= projectConfiguration.IsFastBuild;
if (projectConfiguration.IsFastBuild)
projectConfiguration.AddMasterBff(solutionConfiguration.MasterBffFilePath);
bool build = !projectConfiguration.IsExcludedFromBuild && !configurationProject.InactiveProject;
if (build && solutionConfiguration.IncludeOnlyFilterProject && (configurationProject.Project.SourceFilesFiltersCount == 0 || configurationProject.Project.SkipProjectWhenFiltersActive))
build = false;
if (configurationProject.ToBuild != Configuration.IncludedProjectInfo.Build.YesThroughDependency)
{
if (build)
configurationProject.ToBuild = Configuration.IncludedProjectInfo.Build.Yes;
else if (configurationProject.ToBuild != Configuration.IncludedProjectInfo.Build.Yes)
configurationProject.ToBuild = Configuration.IncludedProjectInfo.Build.No;
}
var dependenciesConfiguration = configurationProject.Configuration.GetRecursiveDependencies();
// TODO: Slow LINQ? May be better to create this list as part of GetRecursiveDependencies
if (!configurationProject.Configuration.IsFastBuild && configurationProject.Configuration.ResolvedDependencies.Any(d => d.IsFastBuild))
unlinkedList.Add(configurationProject.Configuration);
unlinkedList.AddRange(dependenciesConfiguration.Where(c => !c.IsFastBuild && c.ResolvedDependencies.Any(d => d.IsFastBuild)));
foreach (Project.Configuration dependencyConfiguration in dependenciesConfiguration)
{
// Skip configuration that only have swapped-to-dll dependencies
var projectsSwappedToDll = configurationProject.Configuration.ConfigurationsSwappedToDll;
if (projectsSwappedToDll is not null && projectsSwappedToDll.Contains(dependencyConfiguration))
continue;
Project dependencyProject = dependencyConfiguration.Project;
if (dependencyProject.SharpmakeProjectType == Project.ProjectTypeAttribute.Export)
continue;
Type dependencyProjectType = dependencyProject.GetType();
ITarget dependencyProjectTarget = dependencyConfiguration.Target;
hasFastBuildProjectConf |= dependencyConfiguration.IsFastBuild;
if (dependencyConfiguration.IsFastBuild)
dependencyConfiguration.AddMasterBff(solutionConfiguration.MasterBffFilePath);
Configuration.IncludedProjectInfo configurationProjectDependency = solutionConfiguration.GetProject(dependencyProjectType);
// if that project was not explicitly added to the solution configuration, add it ourselves, as it is needed
if (configurationProjectDependency == null)
{
configurationProjectDependency = new Configuration.IncludedProjectInfo
{
Type = dependencyProjectType,
Project = dependencyProject,
Configuration = dependencyConfiguration,
Target = dependencyProjectTarget,
InactiveProject = projectIsInactive // inherit from the parent: no reason to mark dependencies for build if parent is inactive
};
solutionConfiguration.IncludedProjectInfos.Add(configurationProjectDependency);
}
else if (!projectIsInactive && configurationProjectDependency.InactiveProject)
{
// if the project we found in the solutionConfiguration is inactive, and the current is not, replace its settings
configurationProjectDependency.Type = dependencyProjectType;
configurationProjectDependency.Project = dependencyProject;
configurationProjectDependency.Configuration = dependencyConfiguration;
configurationProjectDependency.Target = dependencyProjectTarget;
configurationProjectDependency.InactiveProject = false;
}
else if (projectIsInactive)
{
// if the current project is inactive, ignore
}
else
{
if (!configurationProjectDependency.Target.IsEqualTo(dependencyProjectTarget))
{
throw new Error("In solution configuration (solution: {3}, config: {4}) the parent project {5} generates multiple dependency targets for the same child project {0}: {1} and {2}. Look for all AddPublicDependency() and AddPrivateDependency() calls for the child project and follow the dependency chain.",
configurationProjectDependency.Project?.GetType().ToString(),
configurationProjectDependency.Target,
dependencyProjectTarget,
solutionConfiguration.SolutionFileName,
solutionConfiguration.Target,
project.Name
);
}
if (configurationProjectDependency.Project == null)
configurationProjectDependency.Project = dependencyProject;
else if (configurationProjectDependency.Project != dependencyProject)
throw new Error("Tried to match more than one project to Project type.");
if (configurationProjectDependency.Configuration == null)
configurationProjectDependency.Configuration = dependencyConfiguration;
else if (configurationProjectDependency.Configuration != dependencyConfiguration)
throw new Error("Tried to match more than one Project Configuration to a solution configuration.");
}
if (configurationProjectDependency.ToBuild != Configuration.IncludedProjectInfo.Build.YesThroughDependency)
{
// If we're finding a Fastbuild dependency of an MSBuild project, we know that it'll need re-linking if the All project is generated.
var needsFastbuildRelink = (dependencyConfiguration.IsFastBuild && !configurationProject.Configuration.IsFastBuild && GenerateFastBuildAllProject);
var isExcludedSinceNoFilter = solutionConfiguration.IncludeOnlyFilterProject
&& (configurationProjectDependency.Project.SourceFilesFiltersCount == 0 || configurationProjectDependency.Project.SkipProjectWhenFiltersActive);
var skipBuild = dependencyConfiguration.IsExcludedFromBuild
|| projectIsInactive
|| configurationProjectDependency.InactiveProject
|| needsFastbuildRelink
|| isExcludedSinceNoFilter;
if (!skipBuild)
{
if (projectConfiguration.Output == Project.Configuration.OutputType.Dll || projectConfiguration.Output == Project.Configuration.OutputType.Exe)
configurationProjectDependency.ToBuild = Configuration.IncludedProjectInfo.Build.YesThroughDependency;
else
configurationProjectDependency.ToBuild = Configuration.IncludedProjectInfo.Build.Yes;
}
else if (configurationProjectDependency.ToBuild != Configuration.IncludedProjectInfo.Build.Yes)
configurationProjectDependency.ToBuild = Configuration.IncludedProjectInfo.Build.No;
}
}
}
}
if (hasFastBuildProjectConf)
MakeFastBuildAllProjectIfNeeded(builder, unlinkedConfigurations);
_dependenciesResolved = true;
}
}