private void GenerateFilesSection()

in Sharpmake.Generators/VisualStudio/Vcxproj.cs [1339:2034]


        private void GenerateFilesSection(
            GenerationContext context,
            IFileGenerator fileGenerator,
            IList<string> generatedFiles,
            IList<string> skipFiles
        )
        {
            string filtersFileName = context.ProjectPath + ProjectExtension + ProjectFilterExtension;
            string copyDependenciesFileName = context.ProjectPath + CopyDependenciesExtension;
            string relativeCopyDependenciesFileName = Util.PathGetRelative(context.ProjectDirectory, copyDependenciesFileName, false);

            Strings projectFiles = context.Project.GetSourceFilesForConfigurations(context.ProjectConfigurations);

            // Add source files
            var allFiles = new List<ProjectFile>();
            var includeFiles = new List<ProjectFile>();
            var sourceFiles = new List<ProjectFile>();
            var NatvisFiles = new List<ProjectFile>();
            var PRIFiles = new List<ProjectFile>();
            var NoneFiles = new List<ProjectFile>();
            var XResourcesReswFiles = new List<ProjectFile>();
            var XResourcesImgFiles = new List<ProjectFile>();
            var customBuildFiles = new List<ProjectFile>();

            foreach (string file in context.Project.NatvisFiles)
            {
                var natvisFile = new ProjectFile(context, file);
                NatvisFiles.Add(natvisFile);
            }

            foreach (string file in context.Project.NoneFiles)
            {
                var priFile = new ProjectFile(context, file);
                NoneFiles.Add(priFile);
            }

            foreach (string file in projectFiles)
            {
                var projectFile = new ProjectFile(context, file);
                allFiles.Add(projectFile);
            }

            allFiles.Sort((l, r) => { return string.Compare(l.FileNameProjectRelative, r.FileNameProjectRelative, StringComparison.InvariantCultureIgnoreCase); });

            // Gather files with custom build steps.
            var configurationCustomFileBuildSteps = new Dictionary<Project.Configuration, Dictionary<string, CombinedCustomFileBuildStep>>();
            Strings configurationCustomBuildFiles = new Strings();
            using (Builder.Instance.CreateProfilingScope("GenerateFilesSection:confs1", context.ProjectConfigurations.Count))
            {
                foreach (Project.Configuration config in context.ProjectConfigurations)
                {
                    using (fileGenerator.Resolver.NewScopedParameter("project", context.Project))
                    using (fileGenerator.Resolver.NewScopedParameter("config", config))
                    using (fileGenerator.Resolver.NewScopedParameter("target", config.Target))
                    {
                        var customFileBuildSteps = CombineCustomFileBuildSteps(context.ProjectDirectory, fileGenerator.Resolver, config.CustomFileBuildSteps.Where(step => step.Filter != Project.Configuration.CustomFileBuildStep.ProjectFilter.BFFOnly));
                        configurationCustomFileBuildSteps.Add(config, customFileBuildSteps);
                        foreach (var customBuildSetup in customFileBuildSteps)
                        {
                            configurationCustomBuildFiles.Add(customBuildSetup.Key);
                        }
                    }
                }
            }

            // type -> files
            var customSourceFiles = new Dictionary<string, List<ProjectFile>>();
            using (Builder.Instance.CreateProfilingScope("GenerateFilesSection:allFiles", allFiles.Count))
            {
                foreach (var projectFile in allFiles)
                {
                    string type = null;
                    if (context.Project.ExtensionBuildTools.TryGetValue(projectFile.FileExtension, out type))
                    {
                        List<ProjectFile> files = null;
                        if (!customSourceFiles.TryGetValue(type, out files))
                        {
                            files = new List<ProjectFile>();
                            customSourceFiles[type] = files;
                        }
                        files.Add(projectFile);
                    }
                    else if (configurationCustomBuildFiles.Contains(projectFile.FileNameProjectRelative))
                    {
                        customBuildFiles.Add(projectFile);
                    }
                    else if (context.Project.SourceFilesCompileExtensions.Contains(projectFile.FileExtension) ||
                             (string.Compare(projectFile.FileExtension, ".rc", StringComparison.OrdinalIgnoreCase) == 0))
                    {
                        sourceFiles.Add(projectFile);
                    }
                    else // if (projectFile.FileExtension == "h")
                    {
                        includeFiles.Add(projectFile);
                    }
                }
            }

            // Write header files
            fileGenerator.Write(Template.Project.ProjectFilesBegin);

            bool hasCustomBuildForAllIncludes = context.ProjectConfigurations.First().CustomBuildForAllIncludes != null;

            if (hasCustomBuildForAllIncludes)
            {
                foreach (var file in includeFiles)
                {
                    using (fileGenerator.Declare("file", file.FileNameProjectRelative))
                    using (fileGenerator.Declare("filetype", FileGeneratorUtilities.RemoveLineTag))
                    {
                        fileGenerator.Write(Template.Project.ProjectFilesCustomBuildBegin);

                        foreach (Project.Configuration conf in context.ProjectConfigurations)
                        {
                            if (conf.CustomBuildForAllIncludes == null)
                                continue;

                            using (fileGenerator.Declare("conf", conf))
                            using (fileGenerator.Declare("platformName", Util.GetToolchainPlatformString(conf.Platform, conf.Project, conf.Target)))
                            using (fileGenerator.Declare("description", conf.CustomBuildForAllIncludes.Description))
                            using (fileGenerator.Declare("command", conf.CustomBuildForAllIncludes.CommandLines.JoinStrings(Environment.NewLine, escapeXml: true)))
                            using (fileGenerator.Declare("inputs", FileGeneratorUtilities.RemoveLineTag))
                            using (fileGenerator.Declare("outputs", conf.CustomBuildForAllIncludes.Outputs.JoinStrings(";")))
                            {
                                fileGenerator.Write(Template.Project.ProjectFilesCustomBuildDescription);
                                fileGenerator.Write(Template.Project.ProjectFilesCustomBuildCommand);
                                fileGenerator.Write(Template.Project.ProjectFilesCustomBuildInputs);
                                fileGenerator.Write(Template.Project.ProjectFilesCustomBuildOutputs);
                            }
                        }

                        fileGenerator.Write(Template.Project.ProjectFilesCustomBuildEnd);
                    }
                }
            }
            else
            {
                foreach (var file in includeFiles)
                {
                    using (fileGenerator.Declare("file", file))
                        fileGenerator.Write(Template.Project.ProjectFilesHeader);
                }
            }
            fileGenerator.Write(Template.Project.ProjectFilesEnd);

            if (customBuildFiles.Count > 0)
            {
                // Write custom build steps
                fileGenerator.Write(Template.Project.ProjectFilesBegin);

                foreach (var file in customBuildFiles)
                {
                    using (fileGenerator.Declare("file", file.FileNameProjectRelative))
                    using (fileGenerator.Declare("filetype", FileGeneratorUtilities.RemoveLineTag))
                    {
                        fileGenerator.Write(Template.Project.ProjectFilesCustomBuildBegin);

                        foreach (Project.Configuration conf in context.ProjectConfigurations)
                        {
                            CombinedCustomFileBuildStep buildStep;
                            if (configurationCustomFileBuildSteps[conf].TryGetValue(file.FileNameProjectRelative, out buildStep))
                            {
                                using (fileGenerator.Declare("conf", conf))
                                using (fileGenerator.Declare("platformName", Util.GetToolchainPlatformString(conf.Platform, conf.Project, conf.Target)))
                                using (fileGenerator.Declare("description", buildStep.Description))
                                using (fileGenerator.Declare("command", buildStep.Commands))
                                using (fileGenerator.Declare("inputs", buildStep.AdditionalInputs))
                                using (fileGenerator.Declare("outputs", buildStep.Outputs))
                                using (fileGenerator.Declare("outputItemType", string.IsNullOrEmpty(buildStep.OutputItemType) ? FileGeneratorUtilities.RemoveLineTag : buildStep.OutputItemType))
                                {
                                    fileGenerator.Write(Template.Project.ProjectFilesCustomBuildDescription);
                                    fileGenerator.Write(Template.Project.ProjectFilesCustomBuildCommand);
                                    fileGenerator.Write(Template.Project.ProjectFilesCustomBuildInputs);
                                    fileGenerator.Write(Template.Project.ProjectFilesCustomBuildOutputs);
                                    fileGenerator.Write(Template.Project.ProjectFilesCustomBuildOutputItemType);
                                }
                            }
                        }

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

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

            // Write natvis files
            if (context.Project.NatvisFiles.Count > 0 && context.ProjectConfigurations.Any(conf => conf.Target.HaveFragment<DevEnv>() && conf.Target.GetFragment<DevEnv>() >= DevEnv.vs2015))
            {
                fileGenerator.Write(Template.Project.ProjectFilesBegin);
                foreach (var file in NatvisFiles)
                {
                    using (fileGenerator.Declare("file", file))
                        fileGenerator.Write(Template.Project.ProjectFilesNatvis);
                }
                fileGenerator.Write(Template.Project.ProjectFilesEnd);
            }

            // Write PRI files
            var writtenPRIFiles = new Strings();
            if (context.Project.PRIFiles.Count > 0)
            {
                fileGenerator.Write(Template.Project.ProjectFilesBegin);
                foreach (string file in context.Project.PRIFiles.SortedValues)
                {
                    var priFile = new ProjectFile(context, file);
                    PRIFiles.Add(priFile);
                    writtenPRIFiles.Add(priFile.FileNameProjectRelative);
                    using (fileGenerator.Declare("file", priFile))
                        fileGenerator.Write(Template.Project.ProjectFilesPRIResources);
                }
                fileGenerator.Write(Template.Project.ProjectFilesEnd);
            }

            // Write None files
            if (context.Project.NoneFiles.Count > 0)
            {
                fileGenerator.Write(Template.Project.ProjectFilesBegin);
                foreach (string file in context.Project.NoneFiles)
                {
                    var projectFile = new ProjectFile(context, file);
                    using (fileGenerator.Declare("file", projectFile))
                        fileGenerator.Write(Template.Project.ProjectFilesNone);
                }
                fileGenerator.Write(Template.Project.ProjectFilesEnd);
            }

            foreach (var platform in context.PresentPlatforms.Values)
            {
                platform.GeneratePlatformResourceFileList(context, fileGenerator, writtenPRIFiles, XResourcesReswFiles, XResourcesImgFiles);

                var customPlatformFiles = platform.GetPlatformFileLists(context);
                foreach (var tuple in customPlatformFiles)
                {
                    string type = tuple.Item1;
                    var files = tuple.Item2;
                    customSourceFiles.GetValueOrAdd(type, new List<ProjectFile>()).AddRange(files);
                }
            }

            fileGenerator.Write(Template.Project.ProjectFilesBegin);

            // Validation map
            var configurationCompiledFiles = new List<List<ProjectFile>>();
            foreach (Project.Configuration conf in context.ProjectConfigurations)
                configurationCompiledFiles.Add(new List<ProjectFile>());

            bool hasCustomBuildForAllSources = context.ProjectConfigurations.First().CustomBuildForAllSources != null;
            if (hasCustomBuildForAllSources)
            {
                foreach (var file in sourceFiles)
                {
                    using (fileGenerator.Declare("file", file.FileNameProjectRelative))
                    using (fileGenerator.Declare("filetype", FileGeneratorUtilities.RemoveLineTag))
                    {
                        fileGenerator.Write(Template.Project.ProjectFilesCustomBuildBegin);

                        foreach (Project.Configuration conf in context.ProjectConfigurations)
                        {
                            if (conf.CustomBuildForAllSources == null)
                                continue;

                            using (fileGenerator.Declare("conf", conf))
                            using (fileGenerator.Declare("platformName", Util.GetToolchainPlatformString(conf.Platform, conf.Project, conf.Target)))
                            using (fileGenerator.Declare("description", conf.CustomBuildForAllSources.Description))
                            using (fileGenerator.Declare("command", conf.CustomBuildForAllSources.CommandLines.JoinStrings(Environment.NewLine, escapeXml: true)))
                            using (fileGenerator.Declare("inputs", FileGeneratorUtilities.RemoveLineTag))
                            using (fileGenerator.Declare("outputs", conf.CustomBuildForAllSources.Outputs.JoinStrings(";")))
                            {
                                fileGenerator.Write(Template.Project.ProjectFilesCustomBuildDescription);
                                fileGenerator.Write(Template.Project.ProjectFilesCustomBuildCommand);
                                fileGenerator.Write(Template.Project.ProjectFilesCustomBuildInputs);
                                fileGenerator.Write(Template.Project.ProjectFilesCustomBuildOutputs);
                            }
                        }

                        fileGenerator.Write(Template.Project.ProjectFilesCustomBuildEnd);
                    }
                }
            }
            else
            {
                // Write source files
                foreach (var file in sourceFiles)
                {
                    using (fileGenerator.Declare("file", file))
                    using (fileGenerator.Declare("filetype", FileGeneratorUtilities.RemoveLineTag))
                    {
                        bool isResource = string.Compare(file.FileExtension, ".rc", StringComparison.OrdinalIgnoreCase) == 0;

                        if (isResource)
                            fileGenerator.Write(Template.Project.ProjectFilesResourceBegin);
                        else
                            fileGenerator.Write(Template.Project.ProjectFilesSourceBegin);

                        bool haveFileOptions = false;
                        bool closeFileSource = true;

                        for (int i = 0; i < context.ProjectConfigurations.Count; ++i)
                        {
                            Project.Configuration conf = context.ProjectConfigurations[i];
                            context.Configuration = conf;
                            var platformVcxproj = context.PresentPlatforms[conf.Platform];

                            var compiledFiles = configurationCompiledFiles[i];

                            bool hasPrecomp = platformVcxproj.HasPrecomp(context);
                            bool isPrecompSource = !string.IsNullOrEmpty(conf.PrecompSource) && file.FileName.EndsWith(conf.PrecompSource, StringComparison.OrdinalIgnoreCase);
                            bool isDontUsePrecomp = conf.PrecompSourceExclude.Contains(file.FileName) ||
                                                    conf.PrecompSourceExcludeFolders.Any(folder => file.FileName.StartsWith(folder, StringComparison.OrdinalIgnoreCase)) ||
                                                    conf.PrecompSourceExcludeExtension.Contains(file.FileExtension);
                            bool hasForcedIncludes = conf.ForcedIncludesFilters.Any(filter => filter.IsValid(file.FileName));

                            bool isExcludeFromBuild = conf.ResolvedSourceFilesBuildExclude.Contains(file.FileName);
                            bool consumeWinRTExtensions = conf.ConsumeWinRTExtensions.Contains(file.FileName) || conf.ResolvedSourceFilesWithCompileAsWinRTOption.Contains(file.FileName);
                            bool excludeWinRTExtensions = conf.ExcludeWinRTExtensions.Contains(file.FileName) || conf.ResolvedSourceFilesWithExcludeAsWinRTOption.Contains(file.FileName);

                            bool isBlobFileDefine = conf.BlobFileDefine != string.Empty && file.FileName.EndsWith(Project.BlobExtension, StringComparison.OrdinalIgnoreCase);
                            bool isResourceFileDefine = conf.ResourceFileDefine != string.Empty && file.FileName.EndsWith(".rc", StringComparison.OrdinalIgnoreCase);
                            bool isCompileAsCFile = conf.ResolvedSourceFilesWithCompileAsCOption.Contains(file.FileName);
                            bool isCompileAsCPPFile = conf.ResolvedSourceFilesWithCompileAsCPPOption.Contains(file.FileName);
                            bool isCompileAsCLRFile = conf.ResolvedSourceFilesWithCompileAsCLROption.Contains(file.FileName);
                            bool isCompileAsNonCLRFile = conf.ResolvedSourceFilesWithCompileAsNonCLROption.Contains(file.FileName);
                            bool objsInSubdirectories = conf.ObjectFileName != null && !isResource;
                            bool isExcludeFromGenerateXmlDocumentation = conf.ResolvedSourceFilesGenerateXmlDocumentationExclude.Contains(file.FileName);

                            if (isPrecompSource && platformVcxproj.ExcludesPrecompiledHeadersFromBuild)
                                isExcludeFromBuild = true;
                            if (!isExcludeFromBuild && !isResource)
                                compiledFiles.Add(file);

                            if (isCompileAsCLRFile || consumeWinRTExtensions || excludeWinRTExtensions)
                                isDontUsePrecomp = true;
                            if (string.Compare(file.FileExtension, ".c", StringComparison.OrdinalIgnoreCase) == 0)
                                isDontUsePrecomp = true;

                            string exceptionSetting = null;
                            switch (conf.GetExceptionSettingForFile(file.FileName))
                            {
                                case Sharpmake.Options.Vc.Compiler.Exceptions.Enable:
                                    exceptionSetting = "Sync";
                                    break;
                                case Sharpmake.Options.Vc.Compiler.Exceptions.EnableWithExternC:
                                    exceptionSetting = "SyncCThrow";
                                    break;
                                case Sharpmake.Options.Vc.Compiler.Exceptions.EnableWithSEH:
                                    exceptionSetting = "Async";
                                    break;
                            }

                            bool hasExceptionSetting = !string.IsNullOrEmpty(exceptionSetting);

                            haveFileOptions = haveFileOptions ||
                                              isExcludeFromBuild ||
                                              isPrecompSource ||
                                              (isDontUsePrecomp && hasPrecomp) ||
                                              hasForcedIncludes ||
                                              isBlobFileDefine ||
                                              isResourceFileDefine ||
                                              isCompileAsCFile ||
                                              isCompileAsCPPFile ||
                                              isCompileAsNonCLRFile ||
                                              hasExceptionSetting ||
                                              consumeWinRTExtensions ||
                                              excludeWinRTExtensions ||
                                              objsInSubdirectories;

                            if (haveFileOptions)
                            {
                                using (fileGenerator.Declare("conf", conf))
                                using (fileGenerator.Declare("platformName", Util.GetToolchainPlatformString(conf.Platform, conf.Project, conf.Target)))
                                {
                                    if (closeFileSource)
                                    {
                                        fileGenerator.Write(Template.Project.ProjectFilesSourceBeginOptions);
                                        closeFileSource = false;
                                    }

                                    if (isBlobFileDefine)
                                    {
                                        using (fileGenerator.Declare("ProjectFilesSourceDefine", conf.BlobFileDefine))
                                            fileGenerator.Write(Template.Project.ProjectFilesSourceDefine);
                                    }

                                    if (isResourceFileDefine)
                                    {
                                        using (fileGenerator.Declare("ProjectFilesSourceDefine", conf.ResourceFileDefine))
                                            fileGenerator.Write(Template.Project.ProjectFilesSourceDefine);
                                    }

                                    if (isExcludeFromBuild)
                                    {
                                        fileGenerator.Write(Template.Project.ProjectFilesSourceExcludeFromBuild);
                                    }
                                    else
                                    {
                                        if (isCompileAsCFile)
                                        {
                                            fileGenerator.Write(Template.Project.ProjectFilesSourceCompileAsC);
                                        }
                                        else if (isCompileAsCPPFile)
                                        {
                                            fileGenerator.Write(Template.Project.ProjectFilesSourceCompileAsCPP);
                                        }
                                        else if (isCompileAsCLRFile)
                                        {
                                            fileGenerator.Write(Template.Project.ProjectFilesSourceCompileAsCLR);
                                        }
                                        if (isCompileAsNonCLRFile)
                                        {
                                            fileGenerator.Write(Template.Project.ProjectFilesSourceDoNotCompileAsCLR);
                                        }

                                        bool writeVanillaForcedInclude = false;
                                        if (isPrecompSource)
                                        {
                                            fileGenerator.Write(Template.Project.ProjectFilesSourcePrecompCreate);
                                        }
                                        else if (isDontUsePrecomp && hasPrecomp)
                                        {
                                            fileGenerator.Write(Template.Project.ProjectFilesSourcePrecompNotUsing);

                                            // in case we are using the LLVM toolchain, the PCH was added
                                            // as force include globally for the conf, so we need
                                            // to use the forced include vanilla list that we prepared
                                            var optionsForConf = context.ProjectConfigurationOptions[conf];
                                            if (optionsForConf.ContainsKey("ForcedIncludeFilesVanilla"))
                                            {
                                                // Note: faster to test that the options array has the
                                                // vanilla list, as we only add it in case we use LLVM,
                                                // but we could also have tested
                                                // Options.GetObject<Options.Vc.General.PlatformToolset>(conf).IsLLVMToolchain()
                                                writeVanillaForcedInclude = true;
                                            }
                                        }

                                        if (hasForcedIncludes)
                                        {
                                            Strings forcedIncludes = new Strings(conf.ForcedIncludesFilters
                                                                                     .Where(filter => filter.IsValid(file.FileName))
                                                                                     .SelectMany(filter => filter.ForcedIncludes));
                                            var optionsForConf = context.ProjectConfigurationOptions[conf];
                                            using (fileGenerator.Declare("ForcedIncludeFiles", forcedIncludes.JoinStrings(";")))
                                            using (fileGenerator.Declare("options", optionsForConf))
                                            {
                                                if (writeVanillaForcedInclude)
                                                {
                                                    fileGenerator.Write(Template.Project.ProjectFilesAdditionalForcedIncludeVanilla);
                                                }
                                                else
                                                {
                                                    fileGenerator.Write(Template.Project.ProjectFilesAdditionalForcedInclude);
                                                }
                                            }
                                        }
                                        else if (writeVanillaForcedInclude)
                                        {
                                            var optionsForConf = context.ProjectConfigurationOptions[conf];
                                            using (fileGenerator.Declare("options", optionsForConf))
                                                fileGenerator.Write(Template.Project.ProjectFilesForcedIncludeVanilla);
                                        }

                                        if (consumeWinRTExtensions)
                                        {
                                            fileGenerator.Write(Template.Project.ProjectFilesSourceConsumeWinRTExtensions);
                                        }

                                        if (hasExceptionSetting)
                                        {
                                            using (fileGenerator.Declare("exceptionSetting", exceptionSetting))
                                            {
                                                fileGenerator.Write(Template.Project.ProjectFilesSourceEnableExceptions);
                                            }
                                        }

                                        if (excludeWinRTExtensions)
                                        {
                                            fileGenerator.Write(Template.Project.ProjectFilesSourceExcludeWinRTExtensions);
                                        }

                                        if (objsInSubdirectories)
                                        {
                                            string objectFileName = conf.ObjectFileName(file.FileNameSourceRelative);
                                            if (!string.IsNullOrEmpty(objectFileName))
                                            {
                                                using (fileGenerator.Declare("ObjectFileName", objectFileName))
                                                {
                                                    fileGenerator.Write(Template.Project.ProjectFilesSourceObjectFileName);
                                                }
                                            }
                                        }

                                        if (isExcludeFromGenerateXmlDocumentation)
                                        {
                                            fileGenerator.Write(Template.Project.ProjectFilesSourceExcludeGenerateXmlDocumentation);
                                        }
                                    }
                                }
                            }
                        }

                        if (haveFileOptions)
                        {
                            if (isResource)
                                fileGenerator.Write(Template.Project.ProjectFilesResourceEnd);
                            else
                                fileGenerator.Write(Template.Project.ProjectFilesSourceEndOptions);
                        }
                        else
                            fileGenerator.Write(Template.Project.ProjectFilesSourceEnd);
                    }
                }
            }

            // Write files built with custom tools
            var typeNames = new List<string>(customSourceFiles.Keys);
            typeNames.Sort();
            foreach (string typeName in typeNames)
            {
                fileGenerator.Write(Template.Project.ProjectFilesEnd);
                fileGenerator.Write(Template.Project.ProjectFilesBegin);
                using (fileGenerator.Declare("type", typeName))
                {
                    var files = customSourceFiles[typeName];
                    foreach (var file in files)
                    {
                        using (fileGenerator.Declare("file", file))
                        {
                            fileGenerator.Write(Template.Project.ProjectFilesCustomSourceBegin);

                            bool haveFileOptions = false;
                            for (int i = 0; i < context.ProjectConfigurations.Count; ++i)
                            {
                                Project.Configuration conf = context.ProjectConfigurations[i];
                                using (fileGenerator.Declare("conf", conf))
                                using (fileGenerator.Declare("platformName", Util.GetToolchainPlatformString(conf.Platform, conf.Project, conf.Target)))
                                {
                                    var compiledFiles = configurationCompiledFiles[i];
                                    bool isExcludeFromBuild = conf.ResolvedSourceFilesBuildExclude.Contains(file.FileName);
                                    if (isExcludeFromBuild)
                                    {
                                        if (!haveFileOptions)
                                        {
                                            fileGenerator.Write(Template.Project.ProjectFilesCustomSourceBeginOptions);
                                            haveFileOptions = true;
                                        }
                                        fileGenerator.Write(Template.Project.ProjectFilesSourceExcludeFromBuild);
                                    }
                                }
                            }
                            if (haveFileOptions)
                                fileGenerator.Write(Template.Project.ProjectFilesCustomSourceEndOptions);
                            else
                                fileGenerator.Write(Template.Project.ProjectFilesCustomSourceEnd);
                        }
                    }
                }
            }

            var copyDependenciesBuildStepDictionary = new Dictionary<Project.Configuration, Project.Configuration.FileCustomBuild>();
            foreach (var conf in context.ProjectConfigurations)
            {
                if (conf.IsFastBuild) // copies handled in bff
                    continue;

                if (conf.Output != Project.Configuration.OutputType.Exe && !conf.ExecuteTargetCopy)
                    continue;

                var copies = ProjectOptionsGenerator.ConvertPostBuildCopiesToRelative(conf, context.ProjectDirectory);
                if (!copies.Any())
                    continue;

                var copyDependenciesBuildStep = copyDependenciesBuildStepDictionary.GetValueOrAdd(conf, new Project.Configuration.FileCustomBuild("Copy files to output paths..."));
                if (conf.CopyDependenciesBuildStep != null)
                    copyDependenciesBuildStep = conf.CopyDependenciesBuildStep;

                foreach (var copy in copies)
                {
                    var sourceFile = copy.Key;
                    var destinationFolder = copy.Value;

                    copyDependenciesBuildStep.CommandLines.Add(conf.CreateTargetCopyCommand(sourceFile, destinationFolder, context.ProjectDirectory));
                    copyDependenciesBuildStep.Inputs.Add(sourceFile);
                    copyDependenciesBuildStep.Outputs.Add(Path.Combine(destinationFolder, Path.GetFileName(sourceFile)));
                }
            }

            // Write the "copy dependencies" build step (as a custom build tool on a dummy file, to make sure the copy is always done when needed)
            bool hasDependenciesToCopy = copyDependenciesBuildStepDictionary.Any();
            var dependenciesFileGenerator = new XmlFileGenerator(fileGenerator.Resolver); // borrowing resolver
            if (hasDependenciesToCopy)
            {
                fileGenerator.Write(Template.Project.ProjectFilesEnd);
                fileGenerator.Write(Template.Project.ProjectFilesBegin);

                using (fileGenerator.Declare("file", relativeCopyDependenciesFileName))
                using (fileGenerator.Declare("filetype", "Document"))
                {
                    fileGenerator.Write(Template.Project.ProjectFilesCustomBuildBegin);

                    foreach (var pair in copyDependenciesBuildStepDictionary)
                    {
                        var conf = pair.Key;
                        Project.Configuration.FileCustomBuild copyDependencies = pair.Value;

                        using (fileGenerator.Declare("conf", conf))
                        using (fileGenerator.Declare("platformName", Util.GetToolchainPlatformString(conf.Platform, conf.Project, conf.Target)))
                        using (fileGenerator.Declare("description", copyDependencies.Description))
                        using (fileGenerator.Declare("command", copyDependencies.CommandLines.JoinStrings(Environment.NewLine, escapeXml: true)))
                        using (fileGenerator.Declare("inputs", copyDependencies.Inputs.JoinStrings(";")))
                        using (fileGenerator.Declare("outputs", copyDependencies.Outputs.JoinStrings(";")))
                        using (fileGenerator.Declare("linkobjects", copyDependencies.LinkObjects))
                        {
                            fileGenerator.Write(Template.Project.ProjectFilesCustomBuildDescription);
                            fileGenerator.Write(Template.Project.ProjectFilesCustomBuildCommand);
                            fileGenerator.Write(Template.Project.ProjectFilesCustomBuildInputs);
                            fileGenerator.Write(Template.Project.ProjectFilesCustomBuildOutputs);
                            fileGenerator.Write(Template.Project.ProjectFilesCustomBuildLinkObject);

                            // Also write the dependencies in the generated "runtimedependencies" file, for convenience
                            dependenciesFileGenerator.Write(string.Format("{0}[conf.Name]|[platformName]{0}", Environment.NewLine));
                            dependenciesFileGenerator.Write(string.Format("  {0}" + Environment.NewLine, copyDependencies.Inputs.JoinStrings(Environment.NewLine + "  ")));
                        }
                    }
                    fileGenerator.Write(Template.Project.ProjectFilesCustomBuildEnd);
                }
            }

            // Validation
            for (int i = 0; i < context.ProjectConfigurations.Count; ++i)
            {
                Project.Configuration conf = context.ProjectConfigurations[i];
                var compiledFiles = configurationCompiledFiles[i];

                compiledFiles.Sort((l, r) => { return string.Compare(l.FileNameWithoutExtension, r.FileNameWithoutExtension, StringComparison.OrdinalIgnoreCase); });

                for (int j = 0; j < compiledFiles.Count - 1; ++j)
                {
                    ProjectFile l = compiledFiles[j];
                    ProjectFile r = compiledFiles[j + 1];

                    if (string.Compare(l.FileNameWithoutExtension, r.FileNameSourceRelative, StringComparison.OrdinalIgnoreCase) == 0)
                    {
                        string plausibleCause = "";

                        string message =
                            string.Format(
                                "error: {0} project configuration contains 2 files with the same file name '{1}', project compilation will fail due to same obj names"
                                + Environment.NewLine + "{2}" + Environment.NewLine + "{3}.{4}",
                                conf, l.FileNameWithoutExtension, l.FileNameProjectRelative, r.FileNameProjectRelative, plausibleCause);
                        throw new Error(message);
                    }
                }
            }

            // done!
            fileGenerator.Write(Template.Project.ProjectFilesEnd);

            // for the configuration that are fastbuild but external and requires to add the bff files
            if (context.ProjectConfigurations.Any(x => x.IsFastBuild))
                GenerateBffFilesSection(context, fileGenerator);

            var allFileLists = new List<Tuple<string, List<ProjectFile>>>();
            allFileLists.Add(Tuple.Create(hasCustomBuildForAllSources ? "CustomBuild" : "ClCompile", sourceFiles));
            allFileLists.Add(Tuple.Create("PRIResource", XResourcesReswFiles));
            allFileLists.Add(Tuple.Create("Image", XResourcesImgFiles));
            allFileLists.Add(Tuple.Create(hasCustomBuildForAllIncludes ? "CustomBuild" : "ClInclude", includeFiles));
            allFileLists.Add(Tuple.Create("CustomBuild", customBuildFiles));
            if (NatvisFiles.Count > 0)
                allFileLists.Add(Tuple.Create("Natvis", NatvisFiles));
            if (PRIFiles.Count > 0)
                allFileLists.Add(Tuple.Create("PRIResource", PRIFiles));
            if (NoneFiles.Count > 0)
                allFileLists.Add(Tuple.Create("None", NoneFiles));
            foreach (var entry in customSourceFiles)
            {
                allFileLists.Add(Tuple.Create(entry.Key, entry.Value));
            }

            bool skipFilterGeneration = context.ProjectConfigurations.Any(conf => conf.SkipFilterGeneration);
            if (!skipFilterGeneration || !File.Exists(filtersFileName))
            {
                using (fileGenerator.Declare("project", context.Project))
                    GenerateFiltersFile(context, filtersFileName, allFileLists, hasDependenciesToCopy ? relativeCopyDependenciesFileName : string.Empty, fileGenerator.Resolver, generatedFiles, skipFiles);
            }

            if (hasDependenciesToCopy)
            {
                FileInfo copyDependenciesFileInfo = new FileInfo(copyDependenciesFileName);

                if (context.Builder.Context.WriteGeneratedFile(context.Project.GetType(), copyDependenciesFileInfo, dependenciesFileGenerator))
                    generatedFiles.Add(copyDependenciesFileInfo.FullName);
                else
                    skipFiles.Add(copyDependenciesFileInfo.FullName);
            }
        }