|
|
@@ -123,45 +123,6 @@ char const* cmGlobalVisualStudioGenerator::GetIDEVersion() const
|
|
|
return "";
|
|
|
}
|
|
|
|
|
|
-void cmGlobalVisualStudioGenerator::WriteSLNHeader(std::ostream& fout) const
|
|
|
-{
|
|
|
- char utf8bom[] = { char(0xEF), char(0xBB), char(0xBF) };
|
|
|
- fout.write(utf8bom, 3);
|
|
|
- fout << '\n';
|
|
|
-
|
|
|
- switch (this->Version) {
|
|
|
- case cmGlobalVisualStudioGenerator::VSVersion::VS14:
|
|
|
- // Visual Studio 14 writes .sln format 12.00
|
|
|
- fout << "Microsoft Visual Studio Solution File, Format Version 12.00\n";
|
|
|
- if (this->ExpressEdition) {
|
|
|
- fout << "# Visual Studio Express 14 for Windows Desktop\n";
|
|
|
- } else {
|
|
|
- fout << "# Visual Studio 14\n";
|
|
|
- }
|
|
|
- break;
|
|
|
- case cmGlobalVisualStudioGenerator::VSVersion::VS15:
|
|
|
- // Visual Studio 15 writes .sln format 12.00
|
|
|
- fout << "Microsoft Visual Studio Solution File, Format Version 12.00\n";
|
|
|
- fout << "# Visual Studio 15\n";
|
|
|
- break;
|
|
|
- case cmGlobalVisualStudioGenerator::VSVersion::VS16:
|
|
|
- // Visual Studio 16 writes .sln format 12.00
|
|
|
- fout << "Microsoft Visual Studio Solution File, Format Version 12.00\n";
|
|
|
- fout << "# Visual Studio Version 16\n";
|
|
|
- break;
|
|
|
- case cmGlobalVisualStudioGenerator::VSVersion::VS17:
|
|
|
- // Visual Studio 17 writes .sln format 12.00
|
|
|
- fout << "Microsoft Visual Studio Solution File, Format Version 12.00\n";
|
|
|
- fout << "# Visual Studio Version 17\n";
|
|
|
- break;
|
|
|
- case cmGlobalVisualStudioGenerator::VSVersion::VS18:
|
|
|
- // Visual Studio 18 writes .sln format 12.00
|
|
|
- fout << "Microsoft Visual Studio Solution File, Format Version 12.00\n";
|
|
|
- fout << "# Visual Studio Version 18\n";
|
|
|
- break;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
std::string cmGlobalVisualStudioGenerator::GetRegistryBase()
|
|
|
{
|
|
|
return cmGlobalVisualStudioGenerator::GetRegistryBase(this->GetIDEVersion());
|
|
|
@@ -813,23 +774,8 @@ bool cmGlobalVisualStudioGenerator::Open(std::string const& bindir,
|
|
|
return std::async(std::launch::async, OpenSolution, sln).get();
|
|
|
}
|
|
|
|
|
|
-std::string cmGlobalVisualStudioGenerator::ConvertToSolutionPath(
|
|
|
- std::string const& path) const
|
|
|
-{
|
|
|
- // Convert to backslashes. Do not use ConvertToOutputPath because
|
|
|
- // we will add quoting ourselves, and we know these projects always
|
|
|
- // use windows slashes.
|
|
|
- std::string d = path;
|
|
|
- std::string::size_type pos = 0;
|
|
|
- while ((pos = d.find('/', pos)) != std::string::npos) {
|
|
|
- d[pos++] = '\\';
|
|
|
- }
|
|
|
- return d;
|
|
|
-}
|
|
|
-
|
|
|
bool cmGlobalVisualStudioGenerator::IsDependedOn(
|
|
|
- OrderedTargetDependSet const& projectTargets,
|
|
|
- cmGeneratorTarget const* gtIn) const
|
|
|
+ TargetDependSet const& projectTargets, cmGeneratorTarget const* gtIn) const
|
|
|
{
|
|
|
return std::any_of(projectTargets.begin(), projectTargets.end(),
|
|
|
[this, gtIn](cmTargetDepend const& l) {
|
|
|
@@ -841,8 +787,7 @@ bool cmGlobalVisualStudioGenerator::IsDependedOn(
|
|
|
|
|
|
std::set<std::string> cmGlobalVisualStudioGenerator::IsPartOfDefaultBuild(
|
|
|
std::vector<std::string> const& configs,
|
|
|
- OrderedTargetDependSet const& projectTargets,
|
|
|
- cmGeneratorTarget const* target) const
|
|
|
+ TargetDependSet const& projectTargets, cmGeneratorTarget const* target) const
|
|
|
{
|
|
|
std::set<std::string> activeConfigs;
|
|
|
// if it is a utility target then only make it part of the
|
|
|
@@ -906,3 +851,243 @@ std::string cmGlobalVisualStudioGenerator::GetGUID(
|
|
|
|
|
|
return cmSystemTools::UpperCase(guid);
|
|
|
}
|
|
|
+
|
|
|
+cm::VS::Solution::Folder* cmGlobalVisualStudioGenerator::CreateSolutionFolder(
|
|
|
+ cm::VS::Solution& solution, cm::string_view rawName) const
|
|
|
+{
|
|
|
+ cm::VS::Solution::Folder* folder = nullptr;
|
|
|
+ std::string canonicalName;
|
|
|
+ for (std::string::size_type cur = 0;;) {
|
|
|
+ static std::string delims = "/\\";
|
|
|
+ cur = rawName.find_first_not_of(delims, cur);
|
|
|
+ if (cur == std::string::npos) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ std::string::size_type end = rawName.find_first_of(delims, cur);
|
|
|
+ cm::string_view f = end == std::string::npos
|
|
|
+ ? rawName.substr(cur)
|
|
|
+ : rawName.substr(cur, end - cur);
|
|
|
+ canonicalName =
|
|
|
+ canonicalName.empty() ? std::string(f) : cmStrCat(canonicalName, '/', f);
|
|
|
+ cm::VS::Solution::Folder* nextFolder = solution.GetFolder(canonicalName);
|
|
|
+ if (nextFolder->Id.empty()) {
|
|
|
+ nextFolder->Id =
|
|
|
+ this->GetGUID(cmStrCat("CMAKE_FOLDER_GUID_"_s, canonicalName));
|
|
|
+ if (folder) {
|
|
|
+ folder->Folders.emplace_back(nextFolder);
|
|
|
+ }
|
|
|
+ solution.Folders.emplace_back(nextFolder);
|
|
|
+ }
|
|
|
+ folder = nextFolder;
|
|
|
+ cur = end;
|
|
|
+ }
|
|
|
+ return folder;
|
|
|
+}
|
|
|
+
|
|
|
+cm::VS::Solution cmGlobalVisualStudioGenerator::CreateSolution(
|
|
|
+ cmLocalGenerator const* root, TargetDependSet const& projectTargets) const
|
|
|
+{
|
|
|
+ using namespace cm::VS;
|
|
|
+ Solution solution;
|
|
|
+ solution.VSVersion = this->Version;
|
|
|
+ solution.VSExpress =
|
|
|
+ this->ExpressEdition ? VersionExpress::Yes : VersionExpress::No;
|
|
|
+ solution.Platform = this->GetPlatformName();
|
|
|
+ solution.Configs =
|
|
|
+ root->GetMakefile()->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig);
|
|
|
+ solution.StartupProject = this->GetStartupProjectName(root);
|
|
|
+
|
|
|
+ auto addProject = [this, useFolders = this->UseFolderProperty(),
|
|
|
+ &solution](cmGeneratorTarget const* gt,
|
|
|
+ Solution::Project const* p) {
|
|
|
+ if (Solution::Folder* const folder = useFolders
|
|
|
+ ? this->CreateSolutionFolder(solution, gt->GetEffectiveFolderName())
|
|
|
+ : nullptr) {
|
|
|
+ folder->Projects.emplace_back(p);
|
|
|
+ } else {
|
|
|
+ solution.Projects.emplace_back(p);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ for (cmTargetDepend const& projectTarget : projectTargets) {
|
|
|
+ cmGeneratorTarget const* gt = projectTarget;
|
|
|
+ if (!this->IsInSolution(gt)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ Solution::Project* project = solution.GetProject(gt->GetName());
|
|
|
+ project->Id = this->GetGUID(gt->GetName());
|
|
|
+
|
|
|
+ std::set<std::string> const& includeConfigs =
|
|
|
+ this->IsPartOfDefaultBuild(solution.Configs, projectTargets, gt);
|
|
|
+ auto addProjectConfig =
|
|
|
+ [this, project, gt, &includeConfigs](std::string const& solutionConfig,
|
|
|
+ std::string const& projectConfig) {
|
|
|
+ bool const build =
|
|
|
+ includeConfigs.find(solutionConfig) != includeConfigs.end();
|
|
|
+ bool const deploy = this->NeedsDeploy(*gt, solutionConfig.c_str());
|
|
|
+ project->Configs.emplace_back(
|
|
|
+ Solution::ProjectConfig{ projectConfig, build, deploy });
|
|
|
+ };
|
|
|
+
|
|
|
+ if (cmValue expath = gt->GetProperty("EXTERNAL_MSPROJECT")) {
|
|
|
+ project->Path = *expath;
|
|
|
+ cmValue const projectType = gt->GetProperty("VS_PROJECT_TYPE");
|
|
|
+ if (!projectType.IsEmpty()) {
|
|
|
+ project->TypeId = *projectType;
|
|
|
+ } else {
|
|
|
+ project->TypeId = Solution::Project::TypeIdDefault;
|
|
|
+ }
|
|
|
+ for (std::string const& config : solution.Configs) {
|
|
|
+ cmList mapConfig{ gt->GetProperty(cmStrCat(
|
|
|
+ "MAP_IMPORTED_CONFIG_", cmSystemTools::UpperCase(config))) };
|
|
|
+ addProjectConfig(config, !mapConfig.empty() ? mapConfig[0] : config);
|
|
|
+ }
|
|
|
+ cmValue platformMapping = gt->GetProperty("VS_PLATFORM_MAPPING");
|
|
|
+ project->Platform =
|
|
|
+ !platformMapping.IsEmpty() ? *platformMapping : solution.Platform;
|
|
|
+ for (BT<std::pair<std::string, bool>> const& i : gt->GetUtilities()) {
|
|
|
+ std::string const& dep = i.Value.first;
|
|
|
+ if (this->IsDepInSolution(dep)) {
|
|
|
+ project->BuildDependencies.emplace_back(solution.GetProject(dep));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ addProject(gt, project);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ cmValue vcprojName = gt->GetProperty("GENERATOR_FILE_NAME");
|
|
|
+ cmValue vcprojType = gt->GetProperty("GENERATOR_FILE_NAME_EXT");
|
|
|
+ if (vcprojName && vcprojType) {
|
|
|
+ cmLocalGenerator* lg = gt->GetLocalGenerator();
|
|
|
+ std::string dir =
|
|
|
+ root->MaybeRelativeToCurBinDir(lg->GetCurrentBinaryDirectory());
|
|
|
+ if (dir == "."_s) {
|
|
|
+ dir.clear();
|
|
|
+ } else if (!cmHasLiteralSuffix(dir, "/")) {
|
|
|
+ dir += "/";
|
|
|
+ }
|
|
|
+
|
|
|
+ project->Path = cmStrCat(dir, *vcprojName, *vcprojType);
|
|
|
+ if (this->TargetIsFortranOnly(gt)) {
|
|
|
+ project->TypeId = Solution::Project::TypeIdFortran;
|
|
|
+ } else if (gt->IsCSharpOnly()) {
|
|
|
+ project->TypeId = Solution::Project::TypeIdCSharp;
|
|
|
+ } else {
|
|
|
+ project->TypeId = Solution::Project::TypeIdDefault;
|
|
|
+ }
|
|
|
+
|
|
|
+ project->Platform =
|
|
|
+ // On VS 19 and above, always map .NET SDK projects to "Any CPU".
|
|
|
+ (gt->IsDotNetSdkTarget() && this->Version >= VSVersion::VS16 &&
|
|
|
+ !cmGlobalVisualStudioGenerator::IsReservedTarget(gt->GetName()))
|
|
|
+ ? "Any CPU"
|
|
|
+ : solution.Platform;
|
|
|
+
|
|
|
+ // Add solution-level dependencies.
|
|
|
+ TargetDependSet const& depends = this->GetTargetDirectDepends(gt);
|
|
|
+ for (cmTargetDepend const& dep : depends) {
|
|
|
+ if (this->IsInSolution(dep)) {
|
|
|
+ project->BuildDependencies.emplace_back(
|
|
|
+ solution.GetProject(dep->GetName()));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ for (std::string const& config : solution.Configs) {
|
|
|
+ addProjectConfig(config, config);
|
|
|
+ }
|
|
|
+
|
|
|
+ addProject(gt, project);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ cmMakefile* mf = root->GetMakefile();
|
|
|
+ // Unfortunately we have to copy the source groups because
|
|
|
+ // FindSourceGroup uses a regex which is modifying the group.
|
|
|
+ std::vector<cmSourceGroup> sourceGroups = mf->GetSourceGroups();
|
|
|
+ std::vector<std::string> items =
|
|
|
+ cmList{ root->GetMakefile()->GetProperty("VS_SOLUTION_ITEMS") };
|
|
|
+ for (std::string item : items) {
|
|
|
+ if (!cmSystemTools::FileIsFullPath(item)) {
|
|
|
+ item =
|
|
|
+ cmSystemTools::CollapseFullPath(item, mf->GetCurrentSourceDirectory());
|
|
|
+ }
|
|
|
+ cmSourceGroup* sg = mf->FindSourceGroup(item, sourceGroups);
|
|
|
+ std::string folderName = sg->GetFullName();
|
|
|
+ if (folderName.empty()) {
|
|
|
+ folderName = "Solution Items"_s;
|
|
|
+ }
|
|
|
+ Solution::Folder* folder =
|
|
|
+ this->CreateSolutionFolder(solution, folderName);
|
|
|
+ folder->Files.emplace(std::move(item));
|
|
|
+ }
|
|
|
+
|
|
|
+ Solution::PropertyGroup* pgExtensibilityGlobals = nullptr;
|
|
|
+ Solution::PropertyGroup* pgExtensibilityAddIns = nullptr;
|
|
|
+ std::vector<std::string> const propKeys =
|
|
|
+ root->GetMakefile()->GetPropertyKeys();
|
|
|
+ for (std::string const& it : propKeys) {
|
|
|
+ if (!cmHasLiteralPrefix(it, "VS_GLOBAL_SECTION_")) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ std::string name = it.substr(18);
|
|
|
+ Solution::PropertyGroup::Load scope;
|
|
|
+ if (cmHasLiteralPrefix(name, "PRE_")) {
|
|
|
+ name = name.substr(4);
|
|
|
+ scope = Solution::PropertyGroup::Load::Pre;
|
|
|
+ } else if (cmHasLiteralPrefix(name, "POST_")) {
|
|
|
+ name = name.substr(5);
|
|
|
+ scope = Solution::PropertyGroup::Load::Post;
|
|
|
+ } else {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (name.empty()) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ Solution::PropertyGroup* pg = solution.GetPropertyGroup(name);
|
|
|
+ solution.PropertyGroups.emplace_back(pg);
|
|
|
+ pg->Scope = scope;
|
|
|
+ cmList keyValuePairs{ root->GetMakefile()->GetProperty(it) };
|
|
|
+ for (std::string const& itPair : keyValuePairs) {
|
|
|
+ std::string::size_type const posEqual = itPair.find('=');
|
|
|
+ if (posEqual != std::string::npos) {
|
|
|
+ std::string key = cmTrimWhitespace(itPair.substr(0, posEqual));
|
|
|
+ std::string value = cmTrimWhitespace(itPair.substr(posEqual + 1));
|
|
|
+ pg->Map.emplace(std::move(key), std::move(value));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (name == "ExtensibilityGlobals"_s) {
|
|
|
+ pgExtensibilityGlobals = pg;
|
|
|
+ } else if (name == "ExtensibilityAddIns"_s) {
|
|
|
+ pgExtensibilityAddIns = pg;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!pgExtensibilityGlobals) {
|
|
|
+ pgExtensibilityGlobals =
|
|
|
+ solution.GetPropertyGroup("ExtensibilityGlobals"_s);
|
|
|
+ solution.PropertyGroups.emplace_back(pgExtensibilityGlobals);
|
|
|
+ }
|
|
|
+ std::string const solutionGuid =
|
|
|
+ this->GetGUID(cmStrCat(root->GetProjectName(), ".sln"));
|
|
|
+ pgExtensibilityGlobals->Map.emplace("SolutionGuid",
|
|
|
+ cmStrCat('{', solutionGuid, '}'));
|
|
|
+
|
|
|
+ if (!pgExtensibilityAddIns) {
|
|
|
+ pgExtensibilityAddIns = solution.GetPropertyGroup("ExtensibilityAddIns"_s);
|
|
|
+ solution.PropertyGroups.emplace_back(pgExtensibilityAddIns);
|
|
|
+ }
|
|
|
+
|
|
|
+ solution.CanonicalizeOrder();
|
|
|
+
|
|
|
+ return solution;
|
|
|
+}
|
|
|
+
|
|
|
+void cmGlobalVisualStudioGenerator::WriteSLNFile(
|
|
|
+ std::ostream& fout, cmLocalGenerator* root,
|
|
|
+ TargetDependSet const& projectTargets) const
|
|
|
+{
|
|
|
+ cm::VS::Solution const solution = this->CreateSolution(root, projectTargets);
|
|
|
+ WriteSln(fout, solution);
|
|
|
+}
|