private void GenerateImpl()

in Sharpmake.Generators/VisualStudio/Vcxproj.cs [359:761]


        private void GenerateImpl(GenerationContext context, IList<string> generatedFiles, IList<string> skipFiles)
        {
            FileName = context.ProjectPath;

            GenerateConfOptions(context);

            var fileGenerator = new FileGenerator();

            // xml begin header
            using (fileGenerator.Declare("toolsVersion", context.DevelopmentEnvironmentsRange.MinDevEnv.GetVisualProjectToolsVersionString()))
            {
                fileGenerator.Write(Template.Project.ProjectBegin);
            }

            var firstConf = context.ProjectConfigurations.First();

            NuGet nuGet = new NuGet(context.Project.NuGetReferenceType);

            VsProjCommon.WriteCustomProperties(context.Project.CustomProperties, fileGenerator);

            foreach (var platformVcxproj in context.PresentPlatforms.Values)
                platformVcxproj.GenerateSdkVcxproj(context, fileGenerator);

            VsProjCommon.WriteProjectConfigurationsDescription(context.ProjectConfigurations, fileGenerator);

            bool hasFastBuildConfig = false;
            bool hasNonFastBuildConfig = false;
            foreach (var conf in context.ProjectConfigurations)
            {
                if (conf.IsFastBuild)
                    hasFastBuildConfig = true;
                else
                    hasNonFastBuildConfig = true;
            }

       

            //checking only the first one, having one with CLR support and others without would be an error
            bool clrSupport = Util.IsDotNet(firstConf);

            string projectKeyword = FileGeneratorUtilities.RemoveLineTag;
            string targetFrameworkString = FileGeneratorUtilities.RemoveLineTag;
            string targetFrameworkVersionString = FileGeneratorUtilities.RemoveLineTag;

            if (clrSupport)
            {
                projectKeyword = "ManagedCProj";
                var dotnetFrameWork = firstConf.Target.GetFragment<DotNetFramework>();

                if (dotnetFrameWork.IsDotNetCore()) // .Net Core and .Net 5.0 have different element than old .Netfx, see: https://docs.microsoft.com/en-us/dotnet/core/porting/cpp-cli
                {
                    targetFrameworkString = dotnetFrameWork.ToVersionString();
                }
                else
                {
                    targetFrameworkVersionString = Util.GetDotNetTargetString(dotnetFrameWork);
                }
            }

            using (fileGenerator.Declare("projectName", firstConf.ProjectName))
            using (fileGenerator.Declare("guid", firstConf.ProjectGuid))
            using (fileGenerator.Declare("targetFramework", targetFrameworkString))
            using (fileGenerator.Declare("targetFrameworkVersion", targetFrameworkVersionString))
            using (fileGenerator.Declare("projectKeyword", projectKeyword))
            {
                fileGenerator.Write(Template.Project.ProjectDescription);
            }

            string vcTargetsPath = "$(VCTargetsPath)";
            if (WriteVcOverrides(context, fileGenerator))
            {
                // Disabling this, since it prevents opening old projects in recent visual studio versions
                // TODO: find a way to make it work
                //string vcRootPathKey;
                //string vcTargetsPathKey;
                //// we use the targets path from the most recent devenv supported in this vcxproj,
                //// since it will know how to redirect to older toolsets
                //context.DevelopmentEnvironmentsRange.MaxDevEnv.GetVcPathKeysFromDevEnv(out vcTargetsPathKey, out vcRootPathKey);
                //vcTargetsPath = $"$({vcTargetsPathKey})";
            }

            var vcTargetsPathScopeVar = fileGenerator.Declare("vcTargetsPath", vcTargetsPath);

            fileGenerator.Write(Template.Project.PropertyGroupEnd);
            // xml end header

            if (clrSupport && firstConf.FrameworkReferences.Count > 0)
            {
                fileGenerator.Write(Template.Project.ItemGroupBegin);

                foreach (var frameworkReference in firstConf.FrameworkReferences)
                    using (fileGenerator.Declare("include", frameworkReference))
                        fileGenerator.Write(CSproj.Template.ItemGroups.FrameworkReference);

                fileGenerator.Write(Template.Project.ItemGroupEnd);
            }

            foreach (var platform in context.PresentPlatforms.Values)
                platform.GeneratePlatformSpecificProjectDescription(context, fileGenerator);

            foreach (var platform in context.PresentPlatforms.Values)
                platform.GenerateProjectPlatformSdkDirectoryDescription(context, fileGenerator);

            fileGenerator.Write(Template.Project.ImportCppDefaultProps);

            foreach (var platform in context.PresentPlatforms.Values)
                platform.GeneratePostDefaultPropsImport(context, fileGenerator);

            // user file
            string projectFilePath = FileName + ProjectExtension;
            UserFile uf = new UserFile(projectFilePath);
            uf.GenerateUserFile(context.Builder, context.Project, context.ProjectConfigurations, generatedFiles, skipFiles);

            // configuration general
            using (Builder.Instance.CreateProfilingScope("GenerateImpl:confs2", context.ProjectConfigurations.Count))
            {
                foreach (Project.Configuration conf in context.ProjectConfigurations)
                {
                    context.Configuration = conf;

                    using (fileGenerator.Declare("platformName", Util.GetToolchainPlatformString(conf.Platform, conf.Project, conf.Target)))
                    using (fileGenerator.Declare("conf", conf))
                    using (fileGenerator.Declare("options", context.ProjectConfigurationOptions[conf]))
                    {
                        var platformVcxproj = context.PresentPlatforms[conf.Platform];
                        platformVcxproj.GenerateProjectConfigurationGeneral(context, fileGenerator);
                    }
                }
            }

            // .props files
            fileGenerator.Write(Template.Project.ProjectAfterConfigurationsGeneral);
            if (context.Project.ContainsASM)
            {
                fileGenerator.Write(Template.Project.ProjectImportedMasmProps);
            }

            if (context.Project.ContainsNASM)
            {
                if (context.Project.NasmExePath.Length == 0)
                {
                    throw new ArgumentNullException("NasmExePath not set and needed for NASM assembly files.");
                }
                using (fileGenerator.Declare("importedNasmPropsFile", context.Project.NasmPropsFile))
                {
                    fileGenerator.Write(Template.Project.ProjectImportedNasmProps);
                }
            }

            VsProjCommon.WriteProjectCustomPropsFiles(context.Project.CustomPropsFiles, context.ProjectDirectoryCapitalized, fileGenerator);
            VsProjCommon.WriteConfigurationsCustomPropsFiles(context.ProjectConfigurations, context.ProjectDirectoryCapitalized, fileGenerator);

            fileGenerator.Write(Template.Project.ProjectImportedPropsEnd);
            fileGenerator.Write(Template.Project.ProjectAfterConfigurationsGeneralImportPropertySheets);
            foreach (var platform in context.PresentPlatforms.Values)
                platform.GenerateProjectPlatformImportSheet(context, fileGenerator);
            fileGenerator.Write(Template.Project.ProjectAfterImportedProps);

            // configuration general2
            using (Builder.Instance.CreateProfilingScope("GenerateImpl:confs3", context.ProjectConfigurations.Count))
            {
                foreach (Project.Configuration conf in context.ProjectConfigurations)
                {
                    context.Configuration = conf;

                    using (fileGenerator.Declare("project", context.Project))
                    using (fileGenerator.Declare("platformName", Util.GetToolchainPlatformString(conf.Platform, conf.Project, conf.Target)))
                    using (fileGenerator.Declare("conf", conf))
                    using (fileGenerator.Declare("options", context.ProjectConfigurationOptions[conf]))
                    using (fileGenerator.Declare("target", conf.Target))
                    {
                        var platformVcxproj = context.PresentPlatforms[conf.Platform];

                        if (conf.IsFastBuild)
                        {
                            string commandLine = conf.GetFastBuildCommandLineArguments();

                            // Make the commandline written in the bff available, except the master bff -config
                            Bff.SetCommandLineArguments(conf, commandLine);

                            commandLine += " -config $(SolutionName)" + FastBuildSettings.FastBuildConfigFileExtension;

                            string makeExecutable = context.FastBuildMakeCommandGenerator.GetExecutablePath(conf);
                            using (fileGenerator.Declare("fastBuildWorkingDirectory", context.FastBuildMakeCommandGenerator.GetWorkingDirectory(conf)))
                            using (fileGenerator.Declare("fastBuildMakeCommandBuild", $"{makeExecutable} {context.FastBuildMakeCommandGenerator.GetArguments(FastBuildMakeCommandGenerator.BuildType.Build, conf, commandLine)}"))
                            using (fileGenerator.Declare("fastBuildMakeCommandRebuild", $"{makeExecutable} {context.FastBuildMakeCommandGenerator.GetArguments(FastBuildMakeCommandGenerator.BuildType.Rebuild, conf, commandLine)}"))
                            using (fileGenerator.Declare("fastBuildMakeCommandCompileFile", $"{makeExecutable} {context.FastBuildMakeCommandGenerator.GetArguments(FastBuildMakeCommandGenerator.BuildType.CompileFile, conf, commandLine)}"))
                            {
                                platformVcxproj.GenerateProjectConfigurationFastBuildMakeFile(context, fileGenerator);
                            }
                        }
                        else if (conf.CustomBuildSettings != null)
                        {
                            platformVcxproj.GenerateProjectConfigurationCustomMakeFile(context, fileGenerator);
                        }
                        else
                        {
                            platformVcxproj.GenerateProjectConfigurationGeneral2(context, fileGenerator);
                        }

                        VsProjCommon.WriteConfigurationsCustomProperties(conf, fileGenerator);
                    }
                }
            }

            // configuration ItemDefinitionGroup
            using (Builder.Instance.CreateProfilingScope("GenerateImpl:confs4", context.ProjectConfigurations.Count))
            {
                foreach (Project.Configuration conf in context.ProjectConfigurations)
                {
                    context.Configuration = conf;

                    if (!conf.IsFastBuild)
                    {
                        var compileAsManagedString = FileGeneratorUtilities.RemoveLineTag;

                        if (clrSupport)
                        {
                            var dotNetFramework = conf.Target.GetFragment<DotNetFramework>();

                            if (!dotNetFramework.IsDotNetCore())
                            {
                                // This needs to be omitted when targeting .Net Core otherwise compilation fails due to internal compiler errors. Only info found is from here: https://stackoverflow.com/a/62773057
                                compileAsManagedString = "true";
                            }
                        }

                        using (fileGenerator.Declare("platformName", Util.GetToolchainPlatformString(conf.Platform, conf.Project, conf.Target)))
                        using (fileGenerator.Declare("conf", conf))
                        using (fileGenerator.Declare("project", conf.Project))
                        using (fileGenerator.Declare("target", conf.Target))
                        using (fileGenerator.Declare("options", context.ProjectConfigurationOptions[conf]))
                        using (fileGenerator.Declare("compileAsManaged", compileAsManagedString))
                        {
                            fileGenerator.Write(Template.Project.ProjectConfigurationBeginItemDefinition);

                            var platformVcxproj = context.PresentPlatforms[conf.Platform];
                            platformVcxproj.GenerateProjectCompileVcxproj(context, fileGenerator);
                            platformVcxproj.GenerateProjectLinkVcxproj(context, fileGenerator);

                            if (conf.Project.ContainsASM)
                            {
                                platformVcxproj.GenerateProjectMasmVcxproj(context, fileGenerator);
                            }
                            if (conf.Project.ContainsNASM)
                            {
                                platformVcxproj.GenerateProjectNasmVcxproj(context, fileGenerator);
                            }

                            if (conf.EventPreBuild.Count != 0)
                                fileGenerator.Write(Template.Project.ProjectConfigurationsPreBuildEvent);

                            if (conf.EventPreLink.Count != 0)
                                fileGenerator.Write(Template.Project.ProjectConfigurationsPreLinkEvent);

                            if (conf.EventPrePostLink.Count != 0)
                                fileGenerator.Write(Template.Project.ProjectConfigurationsPrePostLinkEvent);

                            if (conf.EventPostBuild.Count != 0)
                                fileGenerator.Write(Template.Project.ProjectConfigurationsPostBuildEvent);

                            if (conf.CustomBuildStep.Count != 0)
                                fileGenerator.Write(Template.Project.ProjectConfigurationsCustomBuildStep);

                            if (conf.EventCustomBuild.Count != 0)
                                fileGenerator.Write(Template.Project.ProjectConfigurationsCustomBuildEvent);

                            if (conf.Platform.IsPC())
                                fileGenerator.Write(Template.Project.ProjectConfigurationsResourceCompile);

                            if (conf.AdditionalManifestFiles.Count != 0 || (Options.GetObjects<Options.Vc.ManifestTool.EnableDpiAwareness>(conf).Any()) && (conf.Platform.IsPC() && conf.Platform.IsMicrosoft()))
                                fileGenerator.Write(Template.Project.ProjectConfigurationsManifestTool);

                            fileGenerator.Write(Template.Project.ProjectConfigurationEndItemDefinition);
                        }
                    }
                }
            }

            // For all projects configurations that are fastbuild only, do not add the cpp
            // source file requires to be remove from the projects, so that not 2 same cpp file be in 2 different project.
            // TODO: make a better check
            if (hasNonFastBuildConfig || !context.Project.StripFastBuildSourceFiles || context.ProjectConfigurations.Any(conf => !conf.StripFastBuildSourceFiles))
            {
                using (Builder.Instance.CreateProfilingScope("GenerateFilesSection"))
                    GenerateFilesSection(context, fileGenerator, generatedFiles, skipFiles);
            }
            else if (hasFastBuildConfig)
                GenerateBffFilesSection(context, fileGenerator);

            // Generate and add reference to packages.config file for project (if using packages.config mode)
            if (firstConf.ReferencesByNuGetPackage.Count > 0)
            {
                if (hasFastBuildConfig)
                {
                    throw new NotImplementedException("Nuget packages in c++ is not currently supported by FastBuild");
                }

                nuGet.TryGeneratePackagesConfig(firstConf, context, fileGenerator, generatedFiles, skipFiles);
            }

            // Import platform makefiles.
            foreach (var platform in context.PresentPlatforms.Values)
                platform.GenerateMakefileConfigurationVcxproj(context, fileGenerator);

            // .targets files
            {
                fileGenerator.Write(Template.Project.ProjectTargetsBegin);
                if (context.Project.ContainsASM)
                {
                    fileGenerator.Write(Template.Project.ProjectMasmTargetsItem);
                }
                if (context.Project.ContainsNASM)
                {
                    if (context.Project.NasmExePath.Length == 0)
                    {
                        throw new ArgumentNullException("NasmExePath not set and needed for NASM assembly files.");
                    }
                    using (fileGenerator.Declare("importedNasmTargetsFile", context.Project.NasmTargetsFile))
                    {
                        fileGenerator.Write(Template.Project.ProjectNasmTargetsItem);
                    }
                }

                foreach (string targetsFiles in context.Project.CustomTargetsFiles)
                {
                    string capitalizedFile = Project.GetCapitalizedFile(targetsFiles) ?? targetsFiles;

                    string relativeFile = Util.PathGetRelative(context.ProjectDirectoryCapitalized, capitalizedFile);
                    using (fileGenerator.Declare("importedTargetsFile", relativeFile))
                    {
                        fileGenerator.Write(Template.Project.ProjectTargetsItem);
                    }
                }

                // configuration .targets files
                foreach (Project.Configuration conf in context.ProjectConfigurations)
                {
                    using (fileGenerator.Declare("platformName", Util.GetToolchainPlatformString(conf.Platform, conf.Project, conf.Target)))
                    using (fileGenerator.Declare("conf", conf))
                    {
                        foreach (string targetsFile in conf.CustomTargetsFiles)
                        {
                            string capitalizedFile = Project.GetCapitalizedFile(targetsFile) ?? targetsFile;

                            string relativeFile = Util.PathGetRelative(context.ProjectDirectoryCapitalized, capitalizedFile);
                            using (fileGenerator.Declare("importedTargetsFile", relativeFile))
                            {
                                fileGenerator.Write(Template.Project.ProjectConfigurationImportedTargets);
                            }
                        }
                    }
                }

                // add .targets files imported from nuget packages (if using packages.config mode)
                nuGet.TryGenerateImport(NuGet.ImportFileExtension.Targets, firstConf, fileGenerator);

                fileGenerator.Write(Template.Project.ProjectTargetsEnd);
            } // .targets files done

            // add error checks for nuget package targets files (if using packages.config mode)
            if (firstConf.ReferencesByNuGetPackage.Count > 0)
            {
                nuGet.TryGenerateImportErrorCheck(NuGet.ImportFileExtension.Targets, firstConf, fileGenerator);
            }


            // Instead trying add nuget package reference in modern way (if using PackageReference mode)
            if (firstConf.ReferencesByNuGetPackage.Count > 0)
            {
                nuGet.TryGeneratePackageReferences(firstConf, fileGenerator);
            }


            // in case we are using fast build we do not want to write most dependencies
            // in the vcxproj because they are handled internally in the bff.
            // Nevertheless, non-fastbuild dependencies (such as C# projects) must be written.
            GenerateProjectReferences(context, fileGenerator, hasFastBuildConfig);

            // Environment variables
            var environmentVariables = context.ProjectConfigurations.Select(conf => conf.Platform).Distinct().SelectMany(platform => context.PresentPlatforms[platform].GetEnvironmentVariables(context));
            VsProjCommon.WriteEnvironmentVariables(environmentVariables, fileGenerator);

            // Generate vcxproj configuration to run after a deployment from the PC
            if (context.Project.UseRunFromPcDeployment)
            {
                foreach (var platform in context.PresentPlatforms.Values)
                    platform.GenerateRunFromPcDeployment(context, fileGenerator);
            }

            fileGenerator.Write(Template.Project.ProjectEnd);

            // remove all line that contain RemoveLineTag
            fileGenerator.RemoveTaggedLines();

            vcTargetsPathScopeVar.Dispose();

            FileInfo projectFileInfo = new FileInfo(context.ProjectPath + ProjectExtension);
            if (context.Builder.Context.WriteGeneratedFile(context.Project.GetType(), projectFileInfo, fileGenerator))
                generatedFiles.Add(projectFileInfo.FullName);
            else
                skipFiles.Add(projectFileInfo.FullName);
        }