mirror of
https://github.com/Kitware/CMake.git
synced 2025-10-14 02:08:27 +08:00
VS: Decouple solution generation from .sln
file format
Rewrite `.sln` generation using a structured intermediate representation. Issue: #25887
This commit is contained in:
@@ -975,6 +975,8 @@ if(WIN32)
|
||||
cmVisualStudioWCEPlatformParser.cxx
|
||||
cmVSSetupHelper.cxx
|
||||
cmVSSetupHelper.h
|
||||
cmVSSolution.cxx
|
||||
cmVSSolution.h
|
||||
cmVSVersion.h
|
||||
)
|
||||
|
||||
|
@@ -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";
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -2,167 +2,9 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -2,49 +2,12 @@
|
||||
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;
|
||||
|
||||
std::string ProjectConfigurationSectionName;
|
||||
};
|
||||
|
@@ -28,7 +28,6 @@
|
||||
#include "cmSystemTools.h"
|
||||
#include "cmTarget.h"
|
||||
#include "cmTargetDepend.h"
|
||||
#include "cmUuid.h"
|
||||
#include "cmVisualStudioGeneratorOptions.h"
|
||||
#include "cmake.h"
|
||||
|
||||
@@ -344,11 +343,6 @@ void cmGlobalVisualStudio7Generator::OutputSLNFile(
|
||||
// closure of their dependencies.
|
||||
TargetDependSet const projectTargets =
|
||||
this->GetTargetsForProject(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);
|
||||
@@ -356,255 +350,12 @@ void cmGlobalVisualStudio7Generator::OutputSLNFile(
|
||||
if (!fout) {
|
||||
return;
|
||||
}
|
||||
this->WriteSLNFile(fout, root, orderedProjectTargets, vsFolders);
|
||||
this->WriteSLNFile(fout, root, projectTargets);
|
||||
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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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";
|
||||
}
|
||||
|
||||
void cmGlobalVisualStudio7Generator::AppendDirectoryForConfig(
|
||||
std::string const& prefix, std::string const& config,
|
||||
std::string const& suffix, std::string& dir)
|
||||
|
@@ -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.
|
||||
*
|
||||
@@ -126,12 +120,6 @@ protected:
|
||||
|
||||
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();
|
||||
|
||||
@@ -139,49 +127,6 @@ protected:
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
@@ -42,7 +42,6 @@ cmGlobalVisualStudio8Generator::cmGlobalVisualStudio8Generator(
|
||||
cmake* cm, std::string const& name)
|
||||
: cmGlobalVisualStudio71Generator(cm)
|
||||
{
|
||||
this->ProjectConfigurationSectionName = "ProjectConfigurationPlatforms";
|
||||
this->Name = name;
|
||||
this->ExtraFlagTable =
|
||||
cmGlobalVisualStudio8Generator::GetExtraFlagTableVS8();
|
||||
@@ -356,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
|
||||
{
|
||||
@@ -444,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)
|
||||
{
|
||||
|
@@ -70,17 +70,6 @@ protected:
|
||||
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;
|
||||
|
||||
std::string Name;
|
||||
std::string WindowsCEVersion;
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "cmGlobalGenerator.h"
|
||||
#include "cmTargetDepend.h"
|
||||
#include "cmVSSolution.h"
|
||||
#include "cmVSVersion.h"
|
||||
#include "cmValue.h"
|
||||
|
||||
@@ -169,16 +170,12 @@ protected:
|
||||
|
||||
char const* GetIDEVersion() const;
|
||||
|
||||
void WriteSLNHeader(std::ostream& fout) const;
|
||||
|
||||
VSVersion Version;
|
||||
bool ExpressEdition;
|
||||
|
||||
std::string GeneratorPlatform;
|
||||
std::string DefaultPlatformName;
|
||||
|
||||
std::string ConvertToSolutionPath(std::string const& path) const;
|
||||
|
||||
/** Return true if the configuration needs to be deployed */
|
||||
virtual bool NeedsDeploy(cmGeneratorTarget const& target,
|
||||
char const* config) const = 0;
|
||||
@@ -188,12 +185,20 @@ protected:
|
||||
|
||||
std::set<std::string> IsPartOfDefaultBuild(
|
||||
std::vector<std::string> const& configs,
|
||||
OrderedTargetDependSet const& projectTargets,
|
||||
TargetDependSet const& projectTargets,
|
||||
cmGeneratorTarget const* target) const;
|
||||
bool IsDependedOn(OrderedTargetDependSet const& projectTargets,
|
||||
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 WriteSLNFile(std::ostream& fout, cmLocalGenerator* root,
|
||||
TargetDependSet const& projectTargets) const;
|
||||
|
||||
private:
|
||||
virtual std::string GetVSMakeProgram() = 0;
|
||||
void PrintCompilerAdvice(std::ostream&, std::string const&,
|
||||
|
@@ -8,6 +8,7 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <cm/optional>
|
||||
#include <cmext/string_view>
|
||||
|
||||
#include "cmsys/FStream.hxx"
|
||||
|
@@ -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
283
Source/cmVSSolution.cxx
Normal 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
169
Source/cmVSSolution.h
Normal 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);
|
||||
|
||||
}
|
||||
}
|
@@ -15,5 +15,11 @@ enum class Version : std::uint16_t
|
||||
VS17 = 170,
|
||||
VS18 = 180,
|
||||
};
|
||||
|
||||
enum class VersionExpress
|
||||
{
|
||||
No,
|
||||
Yes,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user