Merge topic 'vs-sln'

6a77c80834 cmGlobalVisualStudioGenerator: Consolidate solution generation methods
6b1c101410 cmGlobalVisualStudioGenerator: Adopt more solution generation methods
772cf917c1 cmGlobalVisualStudio71Generator: Remove this now-unnecessary class
3882718872 VS: Decouple solution generation from `.sln` file format
d67e7d2726 VS: Factor out Visual Studio `Version` enumeration
76266f9df6 cmGlobalVisualStudioGenerator: Adopt some solution generation methods
d52eb1d083 cmGlobalVisualStudioGenerator: Remove unused folder support rules
50f87b4bf2 cmGlobalGenerator: Simplify collection of targets under a project()
...

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !11196
This commit is contained in:
Brad King
2025-09-18 13:06:16 +00:00
committed by Kitware Robot
35 changed files with 926 additions and 950 deletions

View File

@@ -935,8 +935,6 @@ if(WIN32)
cmGlobalNMakeMakefileGenerator.h
cmGlobalJOMMakefileGenerator.cxx
cmGlobalJOMMakefileGenerator.h
cmGlobalVisualStudio71Generator.cxx
cmGlobalVisualStudio71Generator.h
cmGlobalVisualStudio7Generator.cxx
cmGlobalVisualStudio7Generator.h
cmGlobalVisualStudio8Generator.cxx
@@ -975,6 +973,9 @@ if(WIN32)
cmVisualStudioWCEPlatformParser.cxx
cmVSSetupHelper.cxx
cmVSSetupHelper.h
cmVSSolution.cxx
cmVSSolution.h
cmVSVersion.h
)
# Add a manifest file to executables on Windows to allow for

View File

@@ -3497,10 +3497,11 @@ void cmGlobalGenerator::GetFilesReplacedDuringGenerate(
std::back_inserter(filenames));
}
void cmGlobalGenerator::GetTargetSets(
TargetDependSet& projectTargets, TargetDependSet& originalTargets,
cmLocalGenerator* root, std::vector<cmLocalGenerator*>& generators)
cmGlobalGenerator::TargetDependSet cmGlobalGenerator::GetTargetsForProject(
cmLocalGenerator const* root,
std::vector<cmLocalGenerator*> const& generators) const
{
TargetDependSet projectTargets;
// loop over all local generators
for (auto* generator : generators) {
// check to make sure generator is not excluded
@@ -3513,12 +3514,11 @@ void cmGlobalGenerator::GetTargetSets(
target->GetLocalGenerator() != root) {
continue;
}
// put the target in the set of original targets
originalTargets.insert(target.get());
// Get the set of targets that depend on target
this->AddTargetDepends(target.get(), projectTargets);
}
}
return projectTargets;
}
bool cmGlobalGenerator::IsRootOnlyTarget(cmGeneratorTarget* target) const
@@ -3528,7 +3528,7 @@ bool cmGlobalGenerator::IsRootOnlyTarget(cmGeneratorTarget* target) const
}
void cmGlobalGenerator::AddTargetDepends(cmGeneratorTarget const* target,
TargetDependSet& projectTargets)
TargetDependSet& projectTargets) const
{
// add the target itself
if (projectTargets.insert(target).second) {

View File

@@ -704,14 +704,15 @@ public:
cmXcFrameworkPlist const& content);
protected:
// for a project collect all its targets by following depend
// information, and also collect all the targets
void GetTargetSets(TargetDependSet& projectTargets,
TargetDependSet& originalTargets, cmLocalGenerator* root,
std::vector<cmLocalGenerator*>& generators);
/** Get all targets produced under the given root, plus the transitive
closure of targets on which they depend, possibly from other dirs. */
TargetDependSet GetTargetsForProject(
cmLocalGenerator const* root,
std::vector<cmLocalGenerator*> const& generators) const;
bool IsRootOnlyTarget(cmGeneratorTarget* target) const;
void AddTargetDepends(cmGeneratorTarget const* target,
TargetDependSet& projectTargets);
TargetDependSet& projectTargets) const;
void SetLanguageEnabledFlag(std::string const& l, cmMakefile* mf);
void SetLanguageEnabledMaps(std::string const& l, cmMakefile* mf);
void FillExtensionToLanguageMap(std::string const& l, cmMakefile* mf);

View File

@@ -439,9 +439,8 @@ void cmGlobalGhsMultiGenerator::OutputTopLevelProject(
// Collect all targets under this root generator and the transitive
// closure of their dependencies.
TargetDependSet projectTargets;
TargetDependSet originalTargets;
this->GetTargetSets(projectTargets, originalTargets, root, generators);
TargetDependSet const projectTargets =
this->GetTargetsForProject(root, generators);
OrderedTargetDependSet sortedProjectTargets(projectTargets, "");
this->ProjectTargets.clear();
for (cmGeneratorTarget const* t : sortedProjectTargets) {

View File

@@ -23,7 +23,6 @@
#include "cmExperimental.h"
#include "cmGeneratorTarget.h"
#include "cmGlobalGenerator.h"
#include "cmGlobalVisualStudio71Generator.h"
#include "cmGlobalVisualStudio7Generator.h"
#include "cmGlobalVisualStudioGenerator.h"
#include "cmIDEFlagTable.h"
@@ -870,7 +869,7 @@ std::string cmGlobalVisualStudio10Generator::FindDevEnvCommand()
// Skip over the cmGlobalVisualStudio8Generator implementation because
// we expect a real devenv and do not want to look for VCExpress.
// NOLINTNEXTLINE(bugprone-parent-virtual-call)
return this->cmGlobalVisualStudio71Generator::FindDevEnvCommand();
return this->cmGlobalVisualStudio7Generator::FindDevEnvCommand();
}
bool cmGlobalVisualStudio10Generator::FindVCTargetsPath(cmMakefile* mf)

View File

@@ -103,15 +103,6 @@ bool cmGlobalVisualStudio11Generator::SelectWindowsStoreToolset(
toolset);
}
bool cmGlobalVisualStudio11Generator::UseFolderProperty() const
{
// Intentionally skip up to the top-level class implementation.
// Folders are not supported by the Express editions in VS10 and earlier,
// but they are in VS11 Express and above.
// NOLINTNEXTLINE(bugprone-parent-virtual-call)
return cmGlobalGenerator::UseFolderProperty();
}
std::set<std::string>
cmGlobalVisualStudio11Generator::GetInstalledWindowsCESDKs()
{

View File

@@ -47,7 +47,6 @@ protected:
bool IsWindowsPhoneToolsetInstalled() const;
bool IsWindowsStoreToolsetInstalled() const;
bool UseFolderProperty() const override;
static std::set<std::string> GetInstalledWindowsCESDKs();
/** Return true if target system supports debugging deployment. */

View File

@@ -521,60 +521,3 @@ std::string cmGlobalVisualStudio14Generator::GetWindows10SDKVersion(
// Return an empty string
return std::string();
}
void cmGlobalVisualStudio14Generator::AddSolutionItems(cmLocalGenerator* root,
VSFolders& vsFolders)
{
cmValue n = root->GetMakefile()->GetProperty("VS_SOLUTION_ITEMS");
if (cmNonempty(n)) {
cmMakefile* makefile = root->GetMakefile();
std::vector<cmSourceGroup> sourceGroups = makefile->GetSourceGroups();
cmVisualStudioFolder* defaultFolder = nullptr;
std::vector<std::string> pathComponents = {
makefile->GetCurrentSourceDirectory(),
"",
"",
};
for (std::string const& relativePath : cmList(n)) {
pathComponents[2] = relativePath;
std::string fullPath = cmSystemTools::FileIsFullPath(relativePath)
? relativePath
: cmSystemTools::JoinPath(pathComponents);
cmSourceGroup* sg = makefile->FindSourceGroup(fullPath, sourceGroups);
cmVisualStudioFolder* folder = nullptr;
if (!sg->GetFullName().empty()) {
std::string folderPath = sg->GetFullName();
// Source groups use '\' while solution folders use '/'.
cmSystemTools::ReplaceString(folderPath, "\\", "/");
folder = vsFolders.Create(folderPath);
} else {
// Lazily initialize the default solution items folder.
if (defaultFolder == nullptr) {
defaultFolder = vsFolders.Create("Solution Items");
}
folder = defaultFolder;
}
folder->SolutionItems.insert(fullPath);
}
}
}
void cmGlobalVisualStudio14Generator::WriteFolderSolutionItems(
std::ostream& fout, cmVisualStudioFolder const& folder) const
{
fout << "\tProjectSection(SolutionItems) = preProject\n";
for (std::string const& item : folder.SolutionItems) {
fout << "\t\t" << item << " = " << item << "\n";
}
fout << "\tEndProjectSection\n";
}

View File

@@ -66,11 +66,6 @@ protected:
std::string GetWindows10SDKVersion(cmMakefile* mf);
void AddSolutionItems(cmLocalGenerator* root, VSFolders& vsFolders) override;
void WriteFolderSolutionItems(
std::ostream& fout, cmVisualStudioFolder const& folder) const override;
private:
class Factory;
friend class Factory;

View File

@@ -1,168 +0,0 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file LICENSE.rst or https://cmake.org/licensing for details. */
#include "cmGlobalVisualStudio71Generator.h"
#include <map>
#include <sstream>
#include "cmGeneratorTarget.h"
#include "cmGlobalGenerator.h"
#include "cmGlobalVisualStudioGenerator.h"
#include "cmList.h"
#include "cmListFileCache.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
class cmake;
cmGlobalVisualStudio71Generator::cmGlobalVisualStudio71Generator(cmake* cm)
: cmGlobalVisualStudio7Generator(cm)
{
this->ProjectConfigurationSectionName = "ProjectConfiguration";
}
void cmGlobalVisualStudio71Generator::WriteSLNFile(
std::ostream& fout, cmLocalGenerator* root,
OrderedTargetDependSet const& orderedProjectTargets,
VSFolders const& vsFolders) const
{
std::vector<std::string> configs =
root->GetMakefile()->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig);
// Write out the header for a SLN file
this->WriteSLNHeader(fout);
// Generate folder specification.
if (!vsFolders.Folders.empty()) {
this->WriteFolders(fout, vsFolders);
}
// Now write the actual target specification content.
this->WriteTargetsToSolution(fout, root, orderedProjectTargets);
// Write out the configurations information for the solution
fout << "Global\n";
// Write out the configurations for the solution
this->WriteSolutionConfigurations(fout, configs);
fout << "\tGlobalSection(" << this->ProjectConfigurationSectionName
<< ") = postSolution\n";
// Write out the configurations for all the targets in the project
this->WriteTargetConfigurations(fout, configs, orderedProjectTargets);
fout << "\tEndGlobalSection\n";
if (!vsFolders.Folders.empty()) {
// Write out project folders
fout << "\tGlobalSection(NestedProjects) = preSolution\n";
this->WriteFoldersContent(fout, vsFolders);
fout << "\tEndGlobalSection\n";
}
// Write out global sections
this->WriteSLNGlobalSections(fout, root);
// Write the footer for the SLN file
this->WriteSLNFooter(fout);
}
// Write a dsp file into the SLN file,
// Note, that dependencies from executables to
// the libraries it uses are also done here
void cmGlobalVisualStudio71Generator::WriteProject(
std::ostream& fout, std::string const& dspname, std::string const& dir,
cmGeneratorTarget const* t) const
{
// check to see if this is a fortran build
std::string ext = ".vcproj";
char const* project =
R"(Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = ")";
if (this->TargetIsFortranOnly(t)) {
ext = ".vfproj";
project = R"(Project("{6989167D-11E4-40FE-8C1A-2192A86A7E90}") = ")";
}
if (t->IsCSharpOnly()) {
ext = ".csproj";
project = R"(Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = ")";
}
cmValue targetExt = t->GetProperty("GENERATOR_FILE_NAME_EXT");
if (targetExt) {
ext = *targetExt;
}
std::string guid = this->GetGUID(dspname);
fout << project << dspname << "\", \"" << this->ConvertToSolutionPath(dir)
<< (!dir.empty() ? "\\" : "") << dspname << ext << "\", \"{" << guid
<< "}\"\n";
fout << "\tProjectSection(ProjectDependencies) = postProject\n";
this->WriteProjectDepends(fout, dspname, dir, t);
fout << "\tEndProjectSection\n";
fout << "EndProject\n";
}
// Write a dsp file into the SLN file, Note, that dependencies from
// executables to the libraries it uses are also done here
void cmGlobalVisualStudio71Generator::WriteExternalProject(
std::ostream& fout, std::string const& name, std::string const& location,
cmValue typeGuid,
std::set<BT<std::pair<std::string, bool>>> const& depends) const
{
fout << "Project(\"{"
<< (typeGuid ? *typeGuid
: std::string(
cmGlobalVisualStudio71Generator::ExternalProjectType(
location)))
<< "}\") = \"" << name << "\", \""
<< this->ConvertToSolutionPath(location) << "\", \"{"
<< this->GetGUID(name) << "}\"\n";
// write out the dependencies here VS 7.1 includes dependencies with the
// project instead of in the global section
if (!depends.empty()) {
fout << "\tProjectSection(ProjectDependencies) = postProject\n";
for (BT<std::pair<std::string, bool>> const& it : depends) {
std::string const& dep = it.Value.first;
if (this->IsDepInSolution(dep)) {
fout << "\t\t{" << this->GetGUID(dep) << "} = {" << this->GetGUID(dep)
<< "}\n";
}
}
fout << "\tEndProjectSection\n";
}
fout << "EndProject\n";
}
// Write a dsp file into the SLN file, Note, that dependencies from
// executables to the libraries it uses are also done here
void cmGlobalVisualStudio71Generator::WriteProjectConfigurations(
std::ostream& fout, std::string const& name, cmGeneratorTarget const& target,
std::vector<std::string> const& configs,
std::set<std::string> const& configsPartOfDefaultBuild,
std::string const& platformMapping) const
{
std::string const& platformName =
!platformMapping.empty() ? platformMapping : this->GetPlatformName();
std::string guid = this->GetGUID(name);
for (std::string const& i : configs) {
cmList mapConfig;
char const* dstConfig = i.c_str();
if (target.GetProperty("EXTERNAL_MSPROJECT")) {
if (cmValue m = target.GetProperty(
cmStrCat("MAP_IMPORTED_CONFIG_", cmSystemTools::UpperCase(i)))) {
mapConfig.assign(*m);
if (!mapConfig.empty()) {
dstConfig = mapConfig[0].c_str();
}
}
}
fout << "\t\t{" << guid << "}." << i << ".ActiveCfg = " << dstConfig << '|'
<< platformName << std::endl;
auto ci = configsPartOfDefaultBuild.find(i);
if (!(ci == configsPartOfDefaultBuild.end())) {
fout << "\t\t{" << guid << "}." << i << ".Build.0 = " << dstConfig << '|'
<< platformName << std::endl;
}
}
}

View File

@@ -1,53 +0,0 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file LICENSE.rst or https://cmake.org/licensing for details. */
#pragma once
#include <iosfwd>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include "cmGlobalVisualStudio7Generator.h"
#include "cmValue.h"
class cmGeneratorTarget;
class cmLocalGenerator;
class cmake;
template <typename T>
class BT;
/** \class cmGlobalVisualStudio71Generator
* \brief Write a Unix makefiles.
*
* cmGlobalVisualStudio71Generator manages UNIX build process for a tree
*/
class cmGlobalVisualStudio71Generator : public cmGlobalVisualStudio7Generator
{
public:
cmGlobalVisualStudio71Generator(cmake* cm);
protected:
void WriteSLNFile(std::ostream& fout, cmLocalGenerator* root,
OrderedTargetDependSet const& orderedProjectTargets,
VSFolders const& vsFolders) const override;
virtual void WriteSolutionConfigurations(
std::ostream& fout, std::vector<std::string> const& configs) const = 0;
void WriteProject(std::ostream& fout, std::string const& name,
std::string const& path,
cmGeneratorTarget const* t) const override;
void WriteProjectConfigurations(
std::ostream& fout, std::string const& name,
cmGeneratorTarget const& target, std::vector<std::string> const& configs,
std::set<std::string> const& configsPartOfDefaultBuild,
std::string const& platformMapping = "") const override;
void WriteExternalProject(
std::ostream& fout, std::string const& name, std::string const& path,
cmValue typeGuid,
std::set<BT<std::pair<std::string, bool>>> const& depends) const override;
// Folders are not supported by VS 7.1.
bool UseFolderProperty() const override { return false; }
std::string ProjectConfigurationSectionName;
};

View File

@@ -28,7 +28,6 @@
#include "cmSystemTools.h"
#include "cmTarget.h"
#include "cmTargetDepend.h"
#include "cmUuid.h"
#include "cmVisualStudioGeneratorOptions.h"
#include "cmake.h"
@@ -58,14 +57,6 @@ static cmVS7FlagTable cmVS7ExtraFlagTable[] = {
{ "", "", "", "", 0 }
};
namespace {
std::string GetSLNFile(cmLocalGenerator* root)
{
return cmStrCat(root->GetCurrentBinaryDirectory(), '/',
root->GetProjectName(), ".sln");
}
}
cmGlobalVisualStudio7Generator::cmGlobalVisualStudio7Generator(cmake* cm)
: cmGlobalVisualStudioGenerator(cm)
{
@@ -294,355 +285,6 @@ bool cmGlobalVisualStudio7Generator::SetSystemName(std::string const& s,
return this->cmGlobalVisualStudioGenerator::SetSystemName(s, mf);
}
void cmGlobalVisualStudio7Generator::Generate()
{
// first do the superclass method
this->cmGlobalVisualStudioGenerator::Generate();
// Now write out the VS Solution files.
for (auto& it : this->ProjectMap) {
this->OutputSLNFile(it.second[0], it.second);
}
// If any solution or project files changed during the generation,
// tell Visual Studio to reload them...
if (!cmSystemTools::GetErrorOccurredFlag() &&
!this->LocalGenerators.empty()) {
this->CallVisualStudioMacro(MacroReload,
GetSLNFile(this->LocalGenerators[0].get()));
}
if (this->Version == VSVersion::VS14 &&
!this->CMakeInstance->GetIsInTryCompile()) {
std::string cmakeWarnVS14;
if (cmValue cached = this->CMakeInstance->GetState()->GetCacheEntryValue(
"CMAKE_WARN_VS14")) {
this->CMakeInstance->MarkCliAsUsed("CMAKE_WARN_VS14");
cmakeWarnVS14 = *cached;
} else {
cmSystemTools::GetEnv("CMAKE_WARN_VS14", cmakeWarnVS14);
}
if (cmakeWarnVS14.empty() || !cmIsOff(cmakeWarnVS14)) {
this->CMakeInstance->IssueMessage(
MessageType::WARNING,
"The \"Visual Studio 14 2015\" generator is deprecated "
"and will be removed in a future version of CMake."
"\n"
"Add CMAKE_WARN_VS14=OFF to the cache to disable this warning.");
}
}
}
void cmGlobalVisualStudio7Generator::OutputSLNFile(
cmLocalGenerator* root, std::vector<cmLocalGenerator*>& generators)
{
if (generators.empty()) {
return;
}
// Collect all targets under this root generator and the transitive
// closure of their dependencies.
TargetDependSet projectTargets;
TargetDependSet originalTargets;
this->GetTargetSets(projectTargets, originalTargets, root, generators);
OrderedTargetDependSet orderedProjectTargets(
projectTargets, this->GetStartupProjectName(root));
VSFolders vsFolders = this->CreateSolutionFolders(orderedProjectTargets);
this->AddSolutionItems(root, vsFolders);
std::string fname = GetSLNFile(root);
cmGeneratedFileStream fout(fname);
fout.SetCopyIfDifferent(true);
if (!fout) {
return;
}
this->WriteSLNFile(fout, root, orderedProjectTargets, vsFolders);
if (fout.Close()) {
this->FileReplacedDuringGenerate(fname);
}
}
void cmGlobalVisualStudio7Generator::WriteTargetConfigurations(
std::ostream& fout, std::vector<std::string> const& configs,
OrderedTargetDependSet const& projectTargets) const
{
// loop over again and write out configurations for each target
// in the solution
for (cmGeneratorTarget const* target : projectTargets) {
if (!this->IsInSolution(target)) {
continue;
}
cmValue expath = target->GetProperty("EXTERNAL_MSPROJECT");
if (expath) {
std::set<std::string> allConfigurations(configs.begin(), configs.end());
cmValue mapping = target->GetProperty("VS_PLATFORM_MAPPING");
this->WriteProjectConfigurations(fout, target->GetName(), *target,
configs, allConfigurations,
mapping ? *mapping : "");
} else {
std::set<std::string> const& configsPartOfDefaultBuild =
this->IsPartOfDefaultBuild(configs, projectTargets, target);
cmValue vcprojName = target->GetProperty("GENERATOR_FILE_NAME");
if (vcprojName) {
std::string mapping;
// On VS 19 and above, always map .NET SDK projects to "Any CPU".
if (target->IsDotNetSdkTarget() && this->Version >= VSVersion::VS16 &&
!cmGlobalVisualStudio7Generator::IsReservedTarget(
target->GetName())) {
mapping = "Any CPU";
}
this->WriteProjectConfigurations(fout, *vcprojName, *target, configs,
configsPartOfDefaultBuild, mapping);
}
}
}
}
cmGlobalVisualStudio7Generator::VSFolders
cmGlobalVisualStudio7Generator::CreateSolutionFolders(
OrderedTargetDependSet const& orderedProjectTargets)
{
VSFolders vsFolders;
if (!this->UseFolderProperty()) {
return vsFolders;
}
for (cmGeneratorTarget const* target : orderedProjectTargets) {
if (this->IsInSolution(target) &&
(target->GetProperty("EXTERNAL_MSPROJECT") ||
target->GetProperty("GENERATOR_FILE_NAME"))) {
// Create "solution folder" information from FOLDER target property
if (cmVisualStudioFolder* folder =
vsFolders.Create(target->GetEffectiveFolderName())) {
folder->Projects.insert(target->GetName());
}
}
}
return vsFolders;
}
cmVisualStudioFolder* cmGlobalVisualStudio7Generator::VSFolders::Create(
std::string const& path)
{
if (path.empty()) {
return nullptr;
}
std::vector<std::string> tokens =
cmSystemTools::SplitString(path, '/', false);
std::string cumulativePath;
for (std::string const& iter : tokens) {
if (iter.empty()) {
continue;
}
if (cumulativePath.empty()) {
cumulativePath = cmStrCat("CMAKE_FOLDER_GUID_", iter);
} else {
this->Folders[cumulativePath].Projects.insert(
cmStrCat(cumulativePath, '/', iter));
cumulativePath = cmStrCat(cumulativePath, '/', iter);
}
}
if (cumulativePath.empty()) {
return nullptr;
}
return &this->Folders[cumulativePath];
}
void cmGlobalVisualStudio7Generator::WriteTargetsToSolution(
std::ostream& fout, cmLocalGenerator* root,
OrderedTargetDependSet const& projectTargets) const
{
std::vector<std::string> configs =
root->GetMakefile()->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig);
for (cmGeneratorTarget const* target : projectTargets) {
if (!this->IsInSolution(target)) {
continue;
}
// handle external vc project files
cmValue expath = target->GetProperty("EXTERNAL_MSPROJECT");
if (expath) {
std::string project = target->GetName();
std::string const& location = *expath;
this->WriteExternalProject(fout, project, location,
target->GetProperty("VS_PROJECT_TYPE"),
target->GetUtilities());
} else {
cmValue vcprojName = target->GetProperty("GENERATOR_FILE_NAME");
if (vcprojName) {
cmLocalGenerator* lg = target->GetLocalGenerator();
std::string dir = lg->GetCurrentBinaryDirectory();
dir = root->MaybeRelativeToCurBinDir(dir);
if (dir == "."_s) {
dir.clear(); // msbuild cannot handle ".\" prefix
}
this->WriteProject(fout, *vcprojName, dir, target);
}
}
}
}
void cmGlobalVisualStudio7Generator::WriteFolders(
std::ostream& fout, VSFolders const& vsFolders) const
{
cm::string_view const prefix = "CMAKE_FOLDER_GUID_";
std::string guidProjectTypeFolder = "2150E333-8FDC-42A3-9474-1A3956D46DE8";
for (auto const& iter : vsFolders.Folders) {
std::string fullName = iter.first;
std::string guid = this->GetGUID(fullName);
std::replace(fullName.begin(), fullName.end(), '/', '\\');
if (cmHasPrefix(fullName, prefix)) {
fullName = fullName.substr(prefix.size());
}
std::string nameOnly = cmSystemTools::GetFilenameName(fullName);
fout << "Project(\"{" << guidProjectTypeFolder << "}\") = \"" << nameOnly
<< "\", \"" << fullName << "\", \"{" << guid << "}\"\n";
if (!iter.second.SolutionItems.empty()) {
this->WriteFolderSolutionItems(fout, iter.second);
}
fout << "EndProject\n";
}
}
void cmGlobalVisualStudio7Generator::WriteFoldersContent(
std::ostream& fout, VSFolders const& vsFolders) const
{
for (auto const& iter : vsFolders.Folders) {
std::string key(iter.first);
std::string guidParent(this->GetGUID(key));
for (std::string const& it : iter.second.Projects) {
std::string const& value(it);
std::string guid(this->GetGUID(value));
fout << "\t\t{" << guid << "} = {" << guidParent << "}\n";
}
}
}
std::string cmGlobalVisualStudio7Generator::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;
}
void cmGlobalVisualStudio7Generator::WriteSLNGlobalSections(
std::ostream& fout, cmLocalGenerator* root) const
{
std::string const guid =
this->GetGUID(cmStrCat(root->GetProjectName(), ".sln"));
bool extensibilityGlobalsOverridden = false;
bool extensibilityAddInsOverridden = false;
std::vector<std::string> const propKeys =
root->GetMakefile()->GetPropertyKeys();
for (std::string const& it : propKeys) {
if (cmHasLiteralPrefix(it, "VS_GLOBAL_SECTION_")) {
std::string sectionType;
std::string name = it.substr(18);
if (cmHasLiteralPrefix(name, "PRE_")) {
name = name.substr(4);
sectionType = "preSolution";
} else if (cmHasLiteralPrefix(name, "POST_")) {
name = name.substr(5);
sectionType = "postSolution";
} else {
continue;
}
if (!name.empty()) {
bool addGuid = false;
if (name == "ExtensibilityGlobals"_s &&
sectionType == "postSolution"_s) {
addGuid = true;
extensibilityGlobalsOverridden = true;
} else if (name == "ExtensibilityAddIns"_s &&
sectionType == "postSolution"_s) {
extensibilityAddInsOverridden = true;
}
fout << "\tGlobalSection(" << name << ") = " << sectionType << '\n';
cmValue p = root->GetMakefile()->GetProperty(it);
cmList keyValuePairs{ *p };
for (std::string const& itPair : keyValuePairs) {
std::string::size_type const posEqual = itPair.find('=');
if (posEqual != std::string::npos) {
std::string const key =
cmTrimWhitespace(itPair.substr(0, posEqual));
std::string const value =
cmTrimWhitespace(itPair.substr(posEqual + 1));
fout << "\t\t" << key << " = " << value << '\n';
if (key == "SolutionGuid"_s) {
addGuid = false;
}
}
}
if (addGuid) {
fout << "\t\tSolutionGuid = {" << guid << "}\n";
}
fout << "\tEndGlobalSection\n";
}
}
}
if (!extensibilityGlobalsOverridden) {
fout << "\tGlobalSection(ExtensibilityGlobals) = postSolution\n"
<< "\t\tSolutionGuid = {" << guid << "}\n"
<< "\tEndGlobalSection\n";
}
if (!extensibilityAddInsOverridden) {
fout << "\tGlobalSection(ExtensibilityAddIns) = postSolution\n"
<< "\tEndGlobalSection\n";
}
}
// Standard end of dsw file
void cmGlobalVisualStudio7Generator::WriteSLNFooter(std::ostream& fout) const
{
fout << "EndGlobal\n";
}
std::string cmGlobalVisualStudio7Generator::GetGUID(
std::string const& name) const
{
std::string const& guidStoreName = cmStrCat(name, "_GUID_CMAKE");
if (cmValue storedGUID =
this->CMakeInstance->GetCacheDefinition(guidStoreName)) {
return *storedGUID;
}
// Compute a GUID that is deterministic but unique to the build tree.
std::string input =
cmStrCat(this->CMakeInstance->GetState()->GetBinaryDirectory(), '|', name);
cmUuid uuidGenerator;
std::vector<unsigned char> uuidNamespace;
uuidGenerator.StringToBinary("ee30c4be-5192-4fb0-b335-722a2dffe760",
uuidNamespace);
std::string guid = uuidGenerator.FromMd5(uuidNamespace, input);
return cmSystemTools::UpperCase(guid);
}
void cmGlobalVisualStudio7Generator::AppendDirectoryForConfig(
std::string const& prefix, std::string const& config,
std::string const& suffix, std::string& dir)
@@ -652,63 +294,6 @@ void cmGlobalVisualStudio7Generator::AppendDirectoryForConfig(
}
}
std::set<std::string> cmGlobalVisualStudio7Generator::IsPartOfDefaultBuild(
std::vector<std::string> const& configs,
OrderedTargetDependSet const& projectTargets,
cmGeneratorTarget const* target) const
{
std::set<std::string> activeConfigs;
// if it is a utility target then only make it part of the
// default build if another target depends on it
int type = target->GetType();
if (type == cmStateEnums::GLOBAL_TARGET) {
std::vector<std::string> targetNames;
targetNames.push_back("INSTALL");
targetNames.push_back("PACKAGE");
for (std::string const& t : targetNames) {
// check if target <t> is part of default build
if (target->GetName() == t) {
std::string const propertyName =
cmStrCat("CMAKE_VS_INCLUDE_", t, "_TO_DEFAULT_BUILD");
// inspect CMAKE_VS_INCLUDE_<t>_TO_DEFAULT_BUILD properties
for (std::string const& i : configs) {
cmValue propertyValue =
target->Target->GetMakefile()->GetDefinition(propertyName);
if (propertyValue &&
cmIsOn(cmGeneratorExpression::Evaluate(
*propertyValue, target->GetLocalGenerator(), i))) {
activeConfigs.insert(i);
}
}
}
}
return activeConfigs;
}
if (type == cmStateEnums::UTILITY &&
!this->IsDependedOn(projectTargets, target)) {
return activeConfigs;
}
// inspect EXCLUDE_FROM_DEFAULT_BUILD[_<CONFIG>] properties
for (std::string const& i : configs) {
if (target->GetFeature("EXCLUDE_FROM_DEFAULT_BUILD", i).IsOff()) {
activeConfigs.insert(i);
}
}
return activeConfigs;
}
bool cmGlobalVisualStudio7Generator::IsDependedOn(
OrderedTargetDependSet const& projectTargets,
cmGeneratorTarget const* gtIn) const
{
return std::any_of(projectTargets.begin(), projectTargets.end(),
[this, gtIn](cmTargetDepend const& l) {
TargetDependSet const& tgtdeps =
this->GetTargetDirectDepends(l);
return tgtdeps.count(gtIn);
});
}
std::string cmGlobalVisualStudio7Generator::Encoding()
{
return "UTF-8";

View File

@@ -25,12 +25,6 @@ class cmake;
template <typename T>
class BT;
struct cmVisualStudioFolder
{
std::set<std::string> Projects;
std::set<std::string> SolutionItems;
};
/** \class cmGlobalVisualStudio7Generator
* \brief Write a Unix makefiles.
*
@@ -82,9 +76,6 @@ public:
std::vector<std::string> const& makeOptions =
std::vector<std::string>()) override;
//! Lookup a stored GUID or compute one deterministically.
std::string GetGUID(std::string const& name) const;
/** Append the subdirectory for the given configuration. */
void AppendDirectoryForConfig(std::string const& prefix,
std::string const& config,
@@ -127,75 +118,11 @@ public:
protected:
cmGlobalVisualStudio7Generator(cmake* cm);
void Generate() override;
struct VSFolders
{
std::map<std::string, cmVisualStudioFolder> Folders;
cmVisualStudioFolder* Create(std::string const& path);
};
std::string const& GetDevEnvCommand();
virtual std::string FindDevEnvCommand();
static char const* ExternalProjectType(std::string const& location);
virtual void OutputSLNFile(cmLocalGenerator* root,
std::vector<cmLocalGenerator*>& generators);
virtual void WriteSLNFile(
std::ostream& fout, cmLocalGenerator* root,
OrderedTargetDependSet const& orderedProjectTargets,
VSFolders const& vsFolders) const = 0;
virtual void WriteProject(std::ostream& fout, std::string const& name,
std::string const& path,
cmGeneratorTarget const* t) const = 0;
virtual void WriteProjectDepends(std::ostream& fout, std::string const& name,
std::string const& path,
cmGeneratorTarget const* t) const = 0;
virtual void WriteProjectConfigurations(
std::ostream& fout, std::string const& name,
cmGeneratorTarget const& target, std::vector<std::string> const& configs,
std::set<std::string> const& configsPartOfDefaultBuild,
std::string const& platformMapping = "") const = 0;
virtual void WriteSLNGlobalSections(std::ostream& fout,
cmLocalGenerator* root) const;
virtual void WriteSLNFooter(std::ostream& fout) const;
VSFolders CreateSolutionFolders(
OrderedTargetDependSet const& orderedProjectTargets);
virtual void WriteTargetsToSolution(
std::ostream& fout, cmLocalGenerator* root,
OrderedTargetDependSet const& projectTargets) const;
virtual void WriteTargetConfigurations(
std::ostream& fout, std::vector<std::string> const& configs,
OrderedTargetDependSet const& projectTargets) const;
virtual void WriteExternalProject(
std::ostream& fout, std::string const& name, std::string const& path,
cmValue typeGuid,
std::set<BT<std::pair<std::string, bool>>> const& dependencies) const = 0;
std::string ConvertToSolutionPath(std::string const& path) const;
std::set<std::string> IsPartOfDefaultBuild(
std::vector<std::string> const& configs,
OrderedTargetDependSet const& projectTargets,
cmGeneratorTarget const* target) const;
bool IsDependedOn(OrderedTargetDependSet const& projectTargets,
cmGeneratorTarget const* target) const;
std::map<std::string, std::string> GUIDMap;
virtual void WriteFolders(std::ostream& fout,
VSFolders const& vsFolders) const;
virtual void WriteFoldersContent(std::ostream& fout,
VSFolders const& vsFolders) const;
virtual void AddSolutionItems(cmLocalGenerator* root,
VSFolders& vsFolders) = 0;
virtual void WriteFolderSolutionItems(
std::ostream& fout, cmVisualStudioFolder const& folder) const = 0;
bool MarmasmEnabled;
bool MasmEnabled;
bool NasmEnabled;

View File

@@ -40,9 +40,8 @@ struct cmIDEFlagTable;
cmGlobalVisualStudio8Generator::cmGlobalVisualStudio8Generator(
cmake* cm, std::string const& name)
: cmGlobalVisualStudio71Generator(cm)
: cmGlobalVisualStudio7Generator(cm)
{
this->ProjectConfigurationSectionName = "ProjectConfigurationPlatforms";
this->Name = name;
this->ExtraFlagTable =
cmGlobalVisualStudio8Generator::GetExtraFlagTableVS8();
@@ -62,7 +61,7 @@ std::string cmGlobalVisualStudio8Generator::FindDevEnvCommand()
return vsxcmd;
}
// Now look for devenv.
return this->cmGlobalVisualStudio71Generator::FindDevEnvCommand();
return this->cmGlobalVisualStudio7Generator::FindDevEnvCommand();
}
void cmGlobalVisualStudio8Generator::EnableLanguage(
@@ -218,12 +217,6 @@ std::string cmGlobalVisualStudio8Generator::GetGenerateStampList()
return "generate.stamp.list";
}
bool cmGlobalVisualStudio8Generator::UseFolderProperty() const
{
// NOLINTNEXTLINE(bugprone-parent-virtual-call)
return IsExpressEdition() ? false : cmGlobalGenerator::UseFolderProperty();
}
bool cmGlobalVisualStudio8Generator::AddCheckTarget()
{
// Add a special target on which all other targets depend that
@@ -362,59 +355,6 @@ void cmGlobalVisualStudio8Generator::AddExtraIDETargets()
}
}
void cmGlobalVisualStudio8Generator::WriteSolutionConfigurations(
std::ostream& fout, std::vector<std::string> const& configs) const
{
fout << "\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n";
for (std::string const& i : configs) {
fout << "\t\t" << i << '|' << this->GetPlatformName() << " = " << i << '|'
<< this->GetPlatformName() << '\n';
}
fout << "\tEndGlobalSection\n";
}
void cmGlobalVisualStudio8Generator::WriteProjectConfigurations(
std::ostream& fout, std::string const& name, cmGeneratorTarget const& target,
std::vector<std::string> const& configs,
std::set<std::string> const& configsPartOfDefaultBuild,
std::string const& platformMapping) const
{
std::string guid = this->GetGUID(name);
for (std::string const& i : configs) {
cmList mapConfig;
char const* dstConfig = i.c_str();
if (target.GetProperty("EXTERNAL_MSPROJECT")) {
if (cmValue m = target.GetProperty(
cmStrCat("MAP_IMPORTED_CONFIG_", cmSystemTools::UpperCase(i)))) {
mapConfig.assign(*m);
if (!mapConfig.empty()) {
dstConfig = mapConfig[0].c_str();
}
}
}
fout << "\t\t{" << guid << "}." << i << '|' << this->GetPlatformName()
<< ".ActiveCfg = " << dstConfig << '|'
<< (!platformMapping.empty() ? platformMapping
: this->GetPlatformName())
<< '\n';
auto ci = configsPartOfDefaultBuild.find(i);
if (!(ci == configsPartOfDefaultBuild.end())) {
fout << "\t\t{" << guid << "}." << i << '|' << this->GetPlatformName()
<< ".Build.0 = " << dstConfig << '|'
<< (!platformMapping.empty() ? platformMapping
: this->GetPlatformName())
<< '\n';
}
if (this->NeedsDeploy(target, dstConfig)) {
fout << "\t\t{" << guid << "}." << i << '|' << this->GetPlatformName()
<< ".Deploy.0 = " << dstConfig << '|'
<< (!platformMapping.empty() ? platformMapping
: this->GetPlatformName())
<< '\n';
}
}
}
bool cmGlobalVisualStudio8Generator::NeedsDeploy(
cmGeneratorTarget const& target, char const* config) const
{
@@ -450,21 +390,6 @@ bool cmGlobalVisualStudio8Generator::TargetSystemSupportsDeployment() const
return this->TargetsWindowsCE();
}
void cmGlobalVisualStudio8Generator::WriteProjectDepends(
std::ostream& fout, std::string const&, std::string const&,
cmGeneratorTarget const* gt) const
{
TargetDependSet const& unordered = this->GetTargetDirectDepends(gt);
OrderedTargetDependSet depends(unordered, std::string());
for (cmTargetDepend const& i : depends) {
if (!this->IsInSolution(i)) {
continue;
}
std::string guid = this->GetGUID(i->GetName());
fout << "\t\t{" << guid << "} = {" << guid << "}\n";
}
}
bool cmGlobalVisualStudio8Generator::NeedLinkLibraryDependencies(
cmGeneratorTarget* target)
{

View File

@@ -9,7 +9,7 @@
#include <cm/optional>
#include "cmGlobalVisualStudio71Generator.h"
#include "cmGlobalVisualStudio7Generator.h"
class cmGeneratorTarget;
class cmMakefile;
@@ -21,7 +21,7 @@ struct cmIDEFlagTable;
*
* cmGlobalVisualStudio8Generator manages UNIX build process for a tree
*/
class cmGlobalVisualStudio8Generator : public cmGlobalVisualStudio71Generator
class cmGlobalVisualStudio8Generator : public cmGlobalVisualStudio7Generator
{
public:
//! Get the name for the generator.
@@ -64,27 +64,12 @@ protected:
bool AddCheckTarget();
/** Return true if the configuration needs to be deployed */
virtual bool NeedsDeploy(cmGeneratorTarget const& target,
char const* config) const;
bool NeedsDeploy(cmGeneratorTarget const& target,
char const* config) const override;
/** Returns true if the target system support debugging deployment. */
virtual bool TargetSystemSupportsDeployment() const;
bool TargetSystemSupportsDeployment() const override;
static cmIDEFlagTable const* GetExtraFlagTableVS8();
void WriteSolutionConfigurations(
std::ostream& fout,
std::vector<std::string> const& configs) const override;
void WriteProjectConfigurations(
std::ostream& fout, std::string const& name,
cmGeneratorTarget const& target, std::vector<std::string> const& configs,
std::set<std::string> const& configsPartOfDefaultBuild,
std::string const& platformMapping = "") const override;
void WriteProjectDepends(std::ostream& fout, std::string const& name,
std::string const& path,
cmGeneratorTarget const* t) const override;
bool UseFolderProperty() const override;
std::string Name;
std::string WindowsCEVersion;

View File

@@ -3,6 +3,7 @@
file LICENSE.rst or https://cmake.org/licensing for details. */
#include "cmGlobalVisualStudioGenerator.h"
#include <algorithm>
#include <cassert>
#include <future>
#include <iostream>
@@ -34,8 +35,17 @@
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmTarget.h"
#include "cmUuid.h"
#include "cmake.h"
namespace {
std::string GetSLNFile(cmLocalGenerator const* root)
{
return cmStrCat(root->GetCurrentBinaryDirectory(), '/',
root->GetProjectName(), ".sln");
}
}
cmGlobalVisualStudioGenerator::cmGlobalVisualStudioGenerator(cmake* cm)
: cmGlobalGenerator(cm)
{
@@ -121,45 +131,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());
@@ -810,3 +781,380 @@ bool cmGlobalVisualStudioGenerator::Open(std::string const& bindir,
return std::async(std::launch::async, OpenSolution, sln).get();
}
bool cmGlobalVisualStudioGenerator::IsDependedOn(
TargetDependSet const& projectTargets, cmGeneratorTarget const* gtIn) const
{
return std::any_of(projectTargets.begin(), projectTargets.end(),
[this, gtIn](cmTargetDepend const& l) {
TargetDependSet const& tgtdeps =
this->GetTargetDirectDepends(l);
return tgtdeps.count(gtIn);
});
}
std::set<std::string> cmGlobalVisualStudioGenerator::IsPartOfDefaultBuild(
std::vector<std::string> const& configs,
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
// default build if another target depends on it
int type = target->GetType();
if (type == cmStateEnums::GLOBAL_TARGET) {
std::vector<std::string> targetNames;
targetNames.push_back("INSTALL");
targetNames.push_back("PACKAGE");
for (std::string const& t : targetNames) {
// check if target <t> is part of default build
if (target->GetName() == t) {
std::string const propertyName =
cmStrCat("CMAKE_VS_INCLUDE_", t, "_TO_DEFAULT_BUILD");
// inspect CMAKE_VS_INCLUDE_<t>_TO_DEFAULT_BUILD properties
for (std::string const& i : configs) {
cmValue propertyValue =
target->Target->GetMakefile()->GetDefinition(propertyName);
if (propertyValue &&
cmIsOn(cmGeneratorExpression::Evaluate(
*propertyValue, target->GetLocalGenerator(), i))) {
activeConfigs.insert(i);
}
}
}
}
return activeConfigs;
}
if (type == cmStateEnums::UTILITY &&
!this->IsDependedOn(projectTargets, target)) {
return activeConfigs;
}
// inspect EXCLUDE_FROM_DEFAULT_BUILD[_<CONFIG>] properties
for (std::string const& i : configs) {
if (target->GetFeature("EXCLUDE_FROM_DEFAULT_BUILD", i).IsOff()) {
activeConfigs.insert(i);
}
}
return activeConfigs;
}
std::string cmGlobalVisualStudioGenerator::GetGUID(
std::string const& name) const
{
std::string const& guidStoreName = cmStrCat(name, "_GUID_CMAKE");
if (cmValue storedGUID =
this->CMakeInstance->GetCacheDefinition(guidStoreName)) {
return *storedGUID;
}
// Compute a GUID that is deterministic but unique to the build tree.
std::string input =
cmStrCat(this->CMakeInstance->GetState()->GetBinaryDirectory(), '|', name);
cmUuid uuidGenerator;
std::vector<unsigned char> uuidNamespace;
uuidGenerator.StringToBinary("ee30c4be-5192-4fb0-b335-722a2dffe760",
uuidNamespace);
std::string guid = uuidGenerator.FromMd5(uuidNamespace, input);
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::Generate()
{
// first do the superclass method
this->cmGlobalGenerator::Generate();
// Now write out the VS Solution files.
for (auto& it : this->ProjectMap) {
this->GenerateSolution(it.second[0], it.second);
}
// If any solution or project files changed during the generation,
// tell Visual Studio to reload them...
if (!cmSystemTools::GetErrorOccurredFlag() &&
!this->LocalGenerators.empty()) {
this->CallVisualStudioMacro(MacroReload,
GetSLNFile(this->LocalGenerators[0].get()));
}
if (this->Version == VSVersion::VS14 &&
!this->CMakeInstance->GetIsInTryCompile()) {
std::string cmakeWarnVS14;
if (cmValue cached = this->CMakeInstance->GetState()->GetCacheEntryValue(
"CMAKE_WARN_VS14")) {
this->CMakeInstance->MarkCliAsUsed("CMAKE_WARN_VS14");
cmakeWarnVS14 = *cached;
} else {
cmSystemTools::GetEnv("CMAKE_WARN_VS14", cmakeWarnVS14);
}
if (cmakeWarnVS14.empty() || !cmIsOff(cmakeWarnVS14)) {
this->CMakeInstance->IssueMessage(
MessageType::WARNING,
"The \"Visual Studio 14 2015\" generator is deprecated "
"and will be removed in a future version of CMake."
"\n"
"Add CMAKE_WARN_VS14=OFF to the cache to disable this warning.");
}
}
}
void cmGlobalVisualStudioGenerator::GenerateSolution(
cmLocalGenerator const* root,
std::vector<cmLocalGenerator*> const& generators)
{
if (generators.empty()) {
return;
}
// Collect all targets under this root generator and the transitive
// closure of their dependencies.
TargetDependSet const projectTargets =
this->GetTargetsForProject(root, generators);
std::string fname = GetSLNFile(root);
cmGeneratedFileStream fout(fname);
fout.SetCopyIfDifferent(true);
if (!fout) {
return;
}
cm::VS::Solution const solution = this->CreateSolution(root, projectTargets);
WriteSln(fout, solution);
if (fout.Close()) {
this->FileReplacedDuringGenerate(fname);
}
}

View File

@@ -14,6 +14,8 @@
#include "cmGlobalGenerator.h"
#include "cmTargetDepend.h"
#include "cmVSSolution.h"
#include "cmVSVersion.h"
#include "cmValue.h"
class cmCustomCommand;
@@ -31,15 +33,7 @@ class cmake;
class cmGlobalVisualStudioGenerator : public cmGlobalGenerator
{
public:
/** Known versions of Visual Studio. */
enum class VSVersion : uint16_t
{
VS14 = 140,
VS15 = 150,
VS16 = 160,
VS17 = 170,
VS18 = 180,
};
using VSVersion = cm::VS::Version;
~cmGlobalVisualStudioGenerator() override;
@@ -159,6 +153,9 @@ public:
bool IsVisualStudio() const override { return true; }
//! Lookup a stored GUID or compute one deterministically.
std::string GetGUID(std::string const& name) const;
protected:
cmGlobalVisualStudioGenerator(cmake* cm);
@@ -173,14 +170,37 @@ protected:
char const* GetIDEVersion() const;
void WriteSLNHeader(std::ostream& fout) const;
VSVersion Version;
bool ExpressEdition;
std::string GeneratorPlatform;
std::string DefaultPlatformName;
/** Return true if the configuration needs to be deployed */
virtual bool NeedsDeploy(cmGeneratorTarget const& target,
char const* config) const = 0;
/** Returns true if the target system support debugging deployment. */
virtual bool TargetSystemSupportsDeployment() const = 0;
std::set<std::string> IsPartOfDefaultBuild(
std::vector<std::string> const& configs,
TargetDependSet const& projectTargets,
cmGeneratorTarget const* target) const;
bool IsDependedOn(TargetDependSet const& projectTargets,
cmGeneratorTarget const* target) const;
std::map<std::string, std::string> GUIDMap;
cm::VS::Solution CreateSolution(cmLocalGenerator const* root,
TargetDependSet const& projectTargets) const;
cm::VS::Solution::Folder* CreateSolutionFolder(
cm::VS::Solution& solution, cm::string_view rawName) const;
void Generate() override;
void GenerateSolution(cmLocalGenerator const* root,
std::vector<cmLocalGenerator*> const& generators);
private:
virtual std::string GetVSMakeProgram() = 0;
void PrintCompilerAdvice(std::ostream&, std::string const&,

View File

@@ -8,6 +8,7 @@
#include <utility>
#include <vector>
#include <cm/optional>
#include <cmext/string_view>
#include "cmsys/FStream.hxx"

View File

@@ -8,6 +8,7 @@
#include <string>
#include <cm/optional>
#include <cm/string_view>
#include "cmGlobalVisualStudio10Generator.h"
#include "cmGlobalVisualStudio14Generator.h"

283
Source/cmVSSolution.cxx Normal file
View File

@@ -0,0 +1,283 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file LICENSE.rst or https://cmake.org/licensing for details. */
#include "cmVSSolution.h"
#include <algorithm>
#include <cassert>
#include <iostream>
#include <map>
#include <cm/string_view>
#include <cmext/string_view>
#include "cmSystemTools.h"
namespace cm {
namespace VS {
cm::string_view const Solution::Project::TypeIdDefault =
"8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942"_s;
cm::string_view const Solution::Project::TypeIdCSharp =
"FAE04EC0-301F-11D3-BF4B-00C04F79EFBC"_s;
cm::string_view const Solution::Project::TypeIdFortran =
"6989167D-11E4-40FE-8C1A-2192A86A7E90"_s;
cm::string_view const Solution::Folder::TypeId =
"2150E333-8FDC-42A3-9474-1A3956D46DE8"_s;
std::vector<Solution::Project const*> Solution::GetAllProjects() const
{
std::vector<Project const*> projects;
projects.reserve(this->ProjectMap.size());
for (Project const* project : this->Projects) {
projects.emplace_back(project);
}
for (Folder const* folder : this->Folders) {
for (Project const* project : folder->Projects) {
projects.emplace_back(project);
}
}
return projects;
}
namespace {
template <typename T>
T* GetEntry(std::map<cm::string_view, std::unique_ptr<T>>& entryMap,
cm::string_view name)
{
auto i = entryMap.find(name);
if (i == entryMap.end()) {
auto p = cm::make_unique<T>();
p->Name = name;
i = entryMap.emplace(p->Name, std::move(p)).first;
}
return i->second.get();
}
}
Solution::Folder* Solution::GetFolder(cm::string_view name)
{
return GetEntry(this->FolderMap, name);
}
Solution::Project* Solution::GetProject(cm::string_view name)
{
return GetEntry(this->ProjectMap, name);
}
Solution::PropertyGroup* Solution::GetPropertyGroup(cm::string_view name)
{
return GetEntry(this->PropertyGroupMap, name);
}
namespace {
struct OrderByName
{
template <typename T>
bool operator()(T const* l, T const* r) const
{
return l->Name < r->Name;
}
};
}
void Solution::CanonicalizeOrder()
{
std::sort(this->Folders.begin(), this->Folders.end(), OrderByName());
for (auto& fi : this->FolderMap) {
Folder* folder = fi.second.get();
std::sort(folder->Folders.begin(), folder->Folders.end(), OrderByName());
std::sort(folder->Projects.begin(), folder->Projects.end(), OrderByName());
}
std::sort(this->Projects.begin(), this->Projects.end(), OrderByName());
for (auto& pi : this->ProjectMap) {
Project* project = pi.second.get();
std::sort(project->BuildDependencies.begin(),
project->BuildDependencies.end(), OrderByName());
}
}
namespace {
void WriteSlnHeader(std::ostream& sln, Version version, VersionExpress express)
{
char utf8bom[] = { char(0xEF), char(0xBB), char(0xBF) };
sln.write(utf8bom, 3);
sln << '\n';
switch (version) {
case Version::VS14:
// Visual Studio 14 writes .sln format 12.00
sln << "Microsoft Visual Studio Solution File, Format Version 12.00\n";
if (express == VersionExpress::Yes) {
sln << "# Visual Studio Express 14 for Windows Desktop\n";
} else {
sln << "# Visual Studio 14\n";
}
break;
case Version::VS15:
// Visual Studio 15 writes .sln format 12.00
sln << "Microsoft Visual Studio Solution File, Format Version 12.00\n";
sln << "# Visual Studio 15\n";
break;
case Version::VS16:
// Visual Studio 16 writes .sln format 12.00
sln << "Microsoft Visual Studio Solution File, Format Version 12.00\n";
sln << "# Visual Studio Version 16\n";
break;
case Version::VS17:
// Visual Studio 17 writes .sln format 12.00
sln << "Microsoft Visual Studio Solution File, Format Version 12.00\n";
sln << "# Visual Studio Version 17\n";
break;
case Version::VS18:
// Visual Studio 18 writes .sln format 12.00
sln << "Microsoft Visual Studio Solution File, Format Version 12.00\n";
sln << "# Visual Studio Version 18\n";
break;
}
}
void WriteSlnProject(std::ostream& sln, Solution::Project const& project)
{
std::string projectPath = project.Path;
std::replace(projectPath.begin(), projectPath.end(), '/', '\\');
sln << "Project(\"{" << project.TypeId << "}\") = \"" << project.Name
<< "\", \"" << projectPath << "\", \"{" << project.Id << "}\"\n";
sln << "\tProjectSection(ProjectDependencies) = postProject\n";
for (Solution::Project const* d : project.BuildDependencies) {
sln << "\t\t{" << d->Id << "} = {" << d->Id << "}\n";
}
sln << "\tEndProjectSection\n";
sln << "EndProject\n";
}
void WriteSlnFolder(std::ostream& sln, Solution::Folder const& folder)
{
std::string folderName = folder.Name;
std::replace(folderName.begin(), folderName.end(), '/', '\\');
std::string const fileName = cmSystemTools::GetFilenameName(folder.Name);
sln << "Project(\"{" << Solution::Folder::TypeId << "}\") = \"" << fileName
<< "\", \"" << folderName << "\", \"{" << folder.Id << "}\"\n";
if (!folder.Files.empty()) {
sln << "\tProjectSection(SolutionItems) = preProject\n";
for (std::string const& item : folder.Files) {
sln << "\t\t" << item << " = " << item << "\n";
}
sln << "\tEndProjectSection\n";
}
sln << "EndProject\n";
}
void WriteSlnSolutionConfigurationPlatforms(std::ostream& sln,
Solution const& solution)
{
sln << "\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n";
for (std::string const& config : solution.Configs) {
sln << "\t\t" << config << '|' << solution.Platform << " = " << config
<< '|' << solution.Platform << '\n';
}
sln << "\tEndGlobalSection\n";
}
void WriteSlnProjectConfigurationPlatforms(std::ostream& sln,
Solution const& solution,
Solution::Project const& project)
{
auto const writeStep = [&sln, &solution, &project](std::size_t i,
cm::string_view step) {
sln << "\t\t{" << project.Id << "}." << solution.Configs[i] << '|'
<< solution.Platform << "." << step << " = "
<< project.Configs[i].Config << '|' << project.Platform << '\n';
};
assert(project.Configs.size() == solution.Configs.size());
for (std::size_t i = 0; i < solution.Configs.size(); ++i) {
writeStep(i, "ActiveCfg"_s);
if (project.Configs[i].Build) {
writeStep(i, "Build.0"_s);
}
if (project.Configs[i].Deploy) {
writeStep(i, "Deploy.0"_s);
}
}
}
void WriteSlnProjectConfigurationPlatforms(
std::ostream& sln, Solution const& solution,
std::vector<Solution::Project const*> const& projects)
{
sln << "\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n";
for (Solution::Project const* project : projects) {
WriteSlnProjectConfigurationPlatforms(sln, solution, *project);
}
sln << "\tEndGlobalSection\n";
}
void WriteSlnNestedProjects(
std::ostream& sln, std::vector<Solution::Folder const*> const& folders)
{
sln << "\tGlobalSection(NestedProjects) = preSolution\n";
for (Solution::Folder const* folder : folders) {
for (Solution::Folder const* nestedFolder : folder->Folders) {
sln << "\t\t{" << nestedFolder->Id << "} = {" << folder->Id << "}\n";
}
for (Solution::Project const* project : folder->Projects) {
sln << "\t\t{" << project->Id << "} = {" << folder->Id << "}\n";
}
}
sln << "\tEndGlobalSection\n";
}
void WriteSlnPropertyGroup(std::ostream& sln,
Solution::PropertyGroup const& pg)
{
cm::string_view const order = pg.Scope == Solution::PropertyGroup::Load::Pre
? "preSolution"_s
: "postSolution"_s;
sln << "\tGlobalSection(" << pg.Name << ") = " << order << '\n';
for (auto const& i : pg.Map) {
sln << "\t\t" << i.first << " = " << i.second << '\n';
}
sln << "\tEndGlobalSection\n";
}
}
void WriteSln(std::ostream& sln, Solution const& solution)
{
assert(solution.VSVersion);
assert(solution.VSExpress);
std::vector<Solution::Project const*> projects = solution.GetAllProjects();
std::sort(projects.begin(), projects.end(),
[&solution](Solution::Project const* l,
Solution::Project const* r) -> bool {
if (r->Name == solution.StartupProject) {
return false;
}
if (l->Name == solution.StartupProject) {
return true;
}
return l->Name < r->Name;
});
WriteSlnHeader(sln, *solution.VSVersion, *solution.VSExpress);
for (Solution::Folder const* folder : solution.Folders) {
WriteSlnFolder(sln, *folder);
}
for (Solution::Project const* project : projects) {
WriteSlnProject(sln, *project);
}
sln << "Global\n";
WriteSlnSolutionConfigurationPlatforms(sln, solution);
WriteSlnProjectConfigurationPlatforms(sln, solution, projects);
if (!solution.Folders.empty()) {
WriteSlnNestedProjects(sln, solution.Folders);
}
for (Solution::PropertyGroup const* pg : solution.PropertyGroups) {
WriteSlnPropertyGroup(sln, *pg);
}
sln << "EndGlobal\n";
}
}
}

169
Source/cmVSSolution.h Normal file
View File

@@ -0,0 +1,169 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file LICENSE.rst or https://cmake.org/licensing for details. */
#pragma once
#include "cmConfigure.h" // IWYU pragma: keep
#include <map>
#include <set>
#include <string>
#include <vector>
#include <cm/memory>
#include <cm/optional>
#include <cm/string_view>
#include "cmVSVersion.h"
namespace cm {
namespace VS {
/** Represent a Visual Studio Solution.
In VS terminology, a "project" corresponds to a CMake "target". */
struct Solution final
{
Solution() = default;
Solution(Solution&&) = default;
Solution& operator=(Solution&&) = default;
/** Represent how a project behaves under one solution config. */
struct ProjectConfig final
{
/** Project-specific config corresponding to this solution config.
This is usually the same as the solution config, but it can map
to another config in some cases. */
std::string Config;
/** Does the project build under this solution config? */
bool Build = false;
/** Does the project deploy under this solution config? */
bool Deploy = false;
};
/** Represent one project in a Solution.
This corresponds to one CMake "target". */
struct Project final
{
/** Project name. This corresponds to the CMake "target" name. */
std::string Name;
/** Project GUID. */
std::string Id;
/** Project type GUID. */
std::string TypeId;
/** Path to the project file on disk.
This is either absolute or relative to the Solution file. */
std::string Path;
/** Project-specific platform. This is usually the same as the
Solution::Platform, but it can be different in some cases. */
std::string Platform;
/** Project-specific configuration corresponding to each solution config.
This vector has the same length as the Solution::Configs vector. */
std::vector<ProjectConfig> Configs;
/** Solution-level dependencies of the project on other projects. */
std::vector<Project const*> BuildDependencies;
// Project type GUIDs used during creation.
static cm::string_view const TypeIdDefault;
static cm::string_view const TypeIdCSharp;
static cm::string_view const TypeIdFortran;
};
/** Represent one folder in a Solution. */
struct Folder final
{
/** Canonical folder name. This includes parent folders separated by
forward slashes. */
std::string Name;
/** Folder GUID. */
std::string Id;
/** List of folders contained inside this folder. */
std::vector<Folder const*> Folders;
/** List of projects contained inside this folder. */
std::vector<Project const*> Projects;
/** Solution-level files contained inside this folder. */
std::set<std::string> Files;
// Folder type GUID.
static cm::string_view const TypeId;
};
/** Represent a group of solution-level Properties. */
struct PropertyGroup final
{
enum class Load
{
Pre,
Post,
};
/** Properties group name. */
std::string Name;
/** Properties group load behavior. */
Load Scope = Load::Post;
/** Property key-value pairs in the group. */
std::map<std::string, std::string> Map;
};
/** Visual Studio major version number, if known. */
cm::optional<Version> VSVersion;
/** Whether this is a VS Express edition, if known. */
cm::optional<VersionExpress> VSExpress;
/** Solution-wide target platform. This is a Windows architecture. */
std::string Platform;
/** Solution-wide build configurations.
This corresponds to CMAKE_CONFIGURATION_TYPES. */
std::vector<std::string> Configs;
/** List of all folders in the solution. */
std::vector<Folder const*> Folders;
/** List of projects in the solution that are not in folders. */
std::vector<Project const*> Projects;
/** List of solution-level property groups. */
std::vector<PropertyGroup const*> PropertyGroups;
/** Name of the default startup project. */
std::string StartupProject;
/** Get all projects in the solution, including all folders. */
std::vector<Project const*> GetAllProjects() const;
// Non-const methods used during creation.
Folder* GetFolder(cm::string_view name);
Project* GetProject(cm::string_view name);
PropertyGroup* GetPropertyGroup(cm::string_view name);
void CanonicalizeOrder();
private:
Solution(Solution const&) = delete;
Solution& operator=(Solution const&) = delete;
// Own and index named entities.
// The string_view keys point at the Name members.
std::map<cm::string_view, std::unique_ptr<Folder>> FolderMap;
std::map<cm::string_view, std::unique_ptr<Project>> ProjectMap;
std::map<cm::string_view, std::unique_ptr<PropertyGroup>> PropertyGroupMap;
};
/** Write the .sln-format representation. */
void WriteSln(std::ostream& sln, Solution const& solution);
}
}

25
Source/cmVSVersion.h Normal file
View File

@@ -0,0 +1,25 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file LICENSE.rst or https://cmake.org/licensing for details. */
#pragma once
#include <cstdint>
namespace cm {
namespace VS {
/** Known versions of Visual Studio. */
enum class Version : std::uint16_t
{
VS14 = 140,
VS15 = 150,
VS16 = 160,
VS17 = 170,
VS18 = 180,
};
enum class VersionExpress
{
No,
Yes,
};
}
}

View File

@@ -18,7 +18,6 @@ run_cmake(ExplicitCMakeLists)
run_cmake(InterfaceLibSources)
run_cmake(NoImpLib)
run_cmake(RuntimeLibrary)
run_cmake(SolutionItems)
run_cmake(SourceGroupCMakeLists)
run_cmake(SourceGroupTreeCMakeLists)
run_cmake(SourceGroupFileSet)
@@ -45,7 +44,6 @@ run_cmake(VsDpiAwareBadParam)
run_cmake(VsForceInclude)
run_cmake(VsPrecompileHeaders)
run_cmake(VsPrecompileHeadersShort)
run_cmake(VsDeployEnabled)
run_cmake(VsSettings)
run_cmake(VsSourceSettingsTool)
run_cmake(VsPlatformToolset)

View File

@@ -7,7 +7,7 @@ endif()
# Test solution file for deployment.
#
set(vcSlnFile "${RunCMake_TEST_BINARY_DIR}/VsDeployEnabled.sln")
set(vcSlnFile "${RunCMake_TEST_BINARY_DIR}/DeployEnabled.sln")
if(NOT EXISTS "${vcSlnFile}")
set(RunCMake_TEST_FAILED "Solution file ${vcSlnFile} does not exist.")
return()

View File

@@ -1,6 +1,7 @@
include(RunCMake)
include(${CMAKE_CURRENT_LIST_DIR}/solution_parsing.cmake)
run_cmake(DeployEnabled)
run_cmake(OnePre)
run_cmake(OnePost)
run_cmake(MorePre)
@@ -9,6 +10,7 @@ run_cmake(PrePost)
run_cmake(Override1)
run_cmake(Override2)
run_cmake(Override3)
run_cmake(SolutionItems)
run_cmake(StartupProject)
run_cmake(StartupProjectMissing)
run_cmake(AddPackageToDefault)