internal void Link()

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;
            }
        }