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