mirror of
https://github.com/Kitware/CMake.git
synced 2026-05-05 13:50:10 -05:00
Merge topic 'vs-solution-items'
0bb13ba0e6VS: Add support for Visual Studio solution itemsf1bcb7276aVS: Refactor solution folder creation Acked-by: Kitware Robot <kwrobot@kitware.com> Merge-request: !10003
This commit is contained in:
@@ -97,6 +97,7 @@ Properties on Directories
|
||||
/prop_dir/VARIABLES
|
||||
/prop_dir/VS_GLOBAL_SECTION_POST_section
|
||||
/prop_dir/VS_GLOBAL_SECTION_PRE_section
|
||||
/prop_dir/VS_SOLUTION_ITEMS
|
||||
/prop_dir/VS_STARTUP_PROJECT
|
||||
|
||||
.. _`Target Properties`:
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
VS_SOLUTION_ITEMS
|
||||
-----------------
|
||||
|
||||
.. versionadded:: 3.32
|
||||
|
||||
Specify solution level items included in the generated Visual Studio solution.
|
||||
|
||||
The :ref:`Visual Studio Generators` create a ``.sln`` file for each directory
|
||||
whose ``CMakeLists.txt`` file calls the :command:`project` command. Append paths
|
||||
to this property in the same directory as the top-level :command:`project`
|
||||
command call (e.g. in the top-level ``CMakeLists.txt`` file) to specify files
|
||||
included in the corresponding solution file.
|
||||
|
||||
If a file specified in ``VS_SOLUTION_ITEMS`` matches a :command:`source_group`
|
||||
command call, the affected solution level items are placed in a hierarchy of
|
||||
solution level folders according to the name specified in that command.
|
||||
Otherwise the items are placed in a default solution level directory named
|
||||
``Solution Items``. This name matches the default directory name used by Visual
|
||||
Studio when attempting to add solution level items at the root of the solution.
|
||||
@@ -0,0 +1,6 @@
|
||||
vs-solution-items
|
||||
-----------------
|
||||
|
||||
* The :prop_dir:`VS_SOLUTION_ITEMS` directory property was added
|
||||
to tell :ref:`Visual Studio Generators` to attach files directly
|
||||
to the Solution (``.sln``).
|
||||
@@ -541,3 +541,56 @@ std::string cmGlobalVisualStudio14Generator::GetWindows10SDKVersion(
|
||||
// Return an empty string
|
||||
return std::string();
|
||||
}
|
||||
|
||||
void cmGlobalVisualStudio14Generator::AddSolutionItems(cmLocalGenerator* root)
|
||||
{
|
||||
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 (const std::string& relativePath : cmList(n)) {
|
||||
pathComponents[2] = relativePath;
|
||||
std::string fullPath = 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 = this->CreateSolutionFolders(folderPath);
|
||||
} else {
|
||||
// Lazily initialize the default solution items folder.
|
||||
if (defaultFolder == nullptr) {
|
||||
defaultFolder = this->CreateSolutionFolders("Solution Items");
|
||||
}
|
||||
folder = defaultFolder;
|
||||
}
|
||||
|
||||
folder->SolutionItems.insert(fullPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cmGlobalVisualStudio14Generator::WriteFolderSolutionItems(
|
||||
std::ostream& fout, const cmVisualStudioFolder& folder)
|
||||
{
|
||||
fout << "\tProjectSection(SolutionItems) = preProject\n";
|
||||
|
||||
for (const std::string& item : folder.SolutionItems) {
|
||||
fout << "\t\t" << item << " = " << item << "\n";
|
||||
}
|
||||
|
||||
fout << "\tEndProjectSection\n";
|
||||
}
|
||||
|
||||
@@ -67,6 +67,11 @@ protected:
|
||||
|
||||
std::string GetWindows10SDKVersion(cmMakefile* mf);
|
||||
|
||||
void AddSolutionItems(cmLocalGenerator* root) override;
|
||||
|
||||
void WriteFolderSolutionItems(std::ostream& fout,
|
||||
const cmVisualStudioFolder& folder) override;
|
||||
|
||||
private:
|
||||
class Factory;
|
||||
friend class Factory;
|
||||
|
||||
@@ -48,9 +48,10 @@ void cmGlobalVisualStudio71Generator::WriteSLNFile(
|
||||
std::ostringstream targetsSlnString;
|
||||
this->WriteTargetsToSolution(targetsSlnString, root, orderedProjectTargets);
|
||||
|
||||
this->AddSolutionItems(root);
|
||||
|
||||
// Generate folder specification.
|
||||
bool useFolderProperty = this->UseFolderProperty();
|
||||
if (useFolderProperty) {
|
||||
if (!this->VisualStudioFolders.empty()) {
|
||||
this->WriteFolders(fout);
|
||||
}
|
||||
|
||||
@@ -67,7 +68,7 @@ void cmGlobalVisualStudio71Generator::WriteSLNFile(
|
||||
this->WriteTargetConfigurations(fout, configs, orderedProjectTargets);
|
||||
fout << "\tEndGlobalSection\n";
|
||||
|
||||
if (useFolderProperty) {
|
||||
if (!this->VisualStudioFolders.empty()) {
|
||||
// Write out project folders
|
||||
fout << "\tGlobalSection(NestedProjects) = preSolution\n";
|
||||
this->WriteFoldersContent(fout);
|
||||
|
||||
@@ -375,6 +375,40 @@ void cmGlobalVisualStudio7Generator::WriteTargetConfigurations(
|
||||
}
|
||||
}
|
||||
|
||||
cmVisualStudioFolder* cmGlobalVisualStudio7Generator::CreateSolutionFolders(
|
||||
const std::string& 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->VisualStudioFolders[cumulativePath].Projects.insert(
|
||||
cmStrCat(cumulativePath, '/', iter));
|
||||
|
||||
cumulativePath = cmStrCat(cumulativePath, '/', iter);
|
||||
}
|
||||
}
|
||||
|
||||
if (cumulativePath.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &this->VisualStudioFolders[cumulativePath];
|
||||
}
|
||||
|
||||
void cmGlobalVisualStudio7Generator::WriteTargetsToSolution(
|
||||
std::ostream& fout, cmLocalGenerator* root,
|
||||
OrderedTargetDependSet const& projectTargets)
|
||||
@@ -421,31 +455,11 @@ void cmGlobalVisualStudio7Generator::WriteTargetsToSolution(
|
||||
// Create "solution folder" information from FOLDER target property
|
||||
//
|
||||
if (written && this->UseFolderProperty()) {
|
||||
const std::string targetFolder = target->GetEffectiveFolderName();
|
||||
if (!targetFolder.empty()) {
|
||||
std::vector<std::string> tokens =
|
||||
cmSystemTools::SplitString(targetFolder, '/', false);
|
||||
cmVisualStudioFolder* folder =
|
||||
this->CreateSolutionFolders(target->GetEffectiveFolderName());
|
||||
|
||||
std::string cumulativePath;
|
||||
|
||||
for (std::string const& iter : tokens) {
|
||||
if (iter.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cumulativePath.empty()) {
|
||||
cumulativePath = cmStrCat("CMAKE_FOLDER_GUID_", iter);
|
||||
} else {
|
||||
VisualStudioFolders[cumulativePath].insert(
|
||||
cmStrCat(cumulativePath, '/', iter));
|
||||
|
||||
cumulativePath = cmStrCat(cumulativePath, '/', iter);
|
||||
}
|
||||
}
|
||||
|
||||
if (!cumulativePath.empty()) {
|
||||
VisualStudioFolders[cumulativePath].insert(target->GetName());
|
||||
}
|
||||
if (folder != nullptr) {
|
||||
folder->Projects.insert(target->GetName());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -467,7 +481,13 @@ void cmGlobalVisualStudio7Generator::WriteFolders(std::ostream& fout)
|
||||
std::string nameOnly = cmSystemTools::GetFilenameName(fullName);
|
||||
|
||||
fout << "Project(\"{" << guidProjectTypeFolder << "}\") = \"" << nameOnly
|
||||
<< "\", \"" << fullName << "\", \"{" << guid << "}\"\nEndProject\n";
|
||||
<< "\", \"" << fullName << "\", \"{" << guid << "}\"\n";
|
||||
|
||||
if (!iter.second.SolutionItems.empty()) {
|
||||
this->WriteFolderSolutionItems(fout, iter.second);
|
||||
}
|
||||
|
||||
fout << "EndProject\n";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -477,7 +497,7 @@ void cmGlobalVisualStudio7Generator::WriteFoldersContent(std::ostream& fout)
|
||||
std::string key(iter.first);
|
||||
std::string guidParent(this->GetGUID(key));
|
||||
|
||||
for (std::string const& it : iter.second) {
|
||||
for (std::string const& it : iter.second.Projects) {
|
||||
std::string const& value(it);
|
||||
std::string guid(this->GetGUID(value));
|
||||
|
||||
|
||||
@@ -25,6 +25,12 @@ 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.
|
||||
*
|
||||
@@ -154,6 +160,8 @@ protected:
|
||||
virtual void WriteSLNFooter(std::ostream& fout);
|
||||
std::string WriteUtilityDepend(const cmGeneratorTarget* target) override;
|
||||
|
||||
cmVisualStudioFolder* CreateSolutionFolders(const std::string& path);
|
||||
|
||||
virtual void WriteTargetsToSolution(
|
||||
std::ostream& fout, cmLocalGenerator* root,
|
||||
OrderedTargetDependSet const& projectTargets);
|
||||
@@ -178,7 +186,12 @@ protected:
|
||||
|
||||
virtual void WriteFolders(std::ostream& fout);
|
||||
virtual void WriteFoldersContent(std::ostream& fout);
|
||||
std::map<std::string, std::set<std::string>> VisualStudioFolders;
|
||||
|
||||
virtual void AddSolutionItems(cmLocalGenerator* root) = 0;
|
||||
virtual void WriteFolderSolutionItems(
|
||||
std::ostream& fout, const cmVisualStudioFolder& folder) = 0;
|
||||
|
||||
std::map<std::string, cmVisualStudioFolder> VisualStudioFolders;
|
||||
|
||||
// Set during OutputSLNFile with the name of the current project.
|
||||
// There is one SLN file per project.
|
||||
|
||||
@@ -0,0 +1,351 @@
|
||||
function(MapAppend map key value)
|
||||
list(APPEND "${map}_k" "${key}")
|
||||
list(APPEND "${map}_v" "${value}")
|
||||
|
||||
set("${map}_k" "${${map}_k}" PARENT_SCOPE)
|
||||
set("${map}_v" "${${map}_v}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(MapLength map out_variable)
|
||||
list(LENGTH "${map}_k" length)
|
||||
set("${out_variable}" "${length}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(MapFind map key out_variable)
|
||||
list(FIND "${map}_k" "${key}" index)
|
||||
if("${index}" LESS 0)
|
||||
unset("${out_variable}" PARENT_SCOPE)
|
||||
else()
|
||||
list(GET "${map}_v" "${index}" value)
|
||||
set("${out_variable}" "${value}" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
macro(MapPropagateToParentScope map)
|
||||
set("${map}_k" "${${map}_k}" PARENT_SCOPE)
|
||||
set("${map}_v" "${${map}_v}" PARENT_SCOPE)
|
||||
endmacro()
|
||||
|
||||
|
||||
function(ParseSln vcSlnFile)
|
||||
if(NOT EXISTS "${vcSlnFile}")
|
||||
set(RunCMake_TEST_FAILED "Solution file ${vcSlnFile} does not exist." PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(SCOPE "")
|
||||
|
||||
set(IN_SOLUTION_ITEMS FALSE)
|
||||
set(IN_NESTED_PROJECTS FALSE)
|
||||
|
||||
set(REGUID "\\{[0-9A-F-]+\\}")
|
||||
|
||||
file(STRINGS "${vcSlnFile}" lines)
|
||||
foreach(line IN LISTS lines)
|
||||
string(STRIP "${line}" line)
|
||||
|
||||
# Project(...)
|
||||
if(line MATCHES "Project\\(\"(${REGUID})\"\\) = \"([^\"]+)\", \"([^\"]+)\", \"(${REGUID})\"")
|
||||
if(NOT "${SCOPE}" STREQUAL "")
|
||||
set(RunCMake_TEST_FAILED "Improper nesting of Project" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
set(SCOPE "Project")
|
||||
|
||||
if("${CMAKE_MATCH_1}" STREQUAL "{2150E333-8FDC-42A3-9474-1A3956D46DE8}")
|
||||
set(GROUP_NAME "${CMAKE_MATCH_2}")
|
||||
|
||||
MapFind(GROUP_PATHS "${GROUP_NAME}" existing_path)
|
||||
if(DEFINED existing_path)
|
||||
set(RunCMake_TEST_FAILED "Duplicate solution items project '${GROUP_NAME}'" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
MapAppend(GROUP_PATHS "${GROUP_NAME}" "${CMAKE_MATCH_3}")
|
||||
MapAppend(GROUP_GUIDS "${GROUP_NAME}" "${CMAKE_MATCH_4}")
|
||||
endif()
|
||||
|
||||
# EndProject
|
||||
elseif(line STREQUAL "EndProject")
|
||||
if(NOT "${SCOPE}" STREQUAL "Project")
|
||||
set(RunCMake_TEST_FAILED "Improper nesting of EndProject" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
set(SCOPE "")
|
||||
|
||||
unset(GROUP_NAME)
|
||||
|
||||
# ProjectSection
|
||||
elseif(line MATCHES "ProjectSection\\(([a-zA-Z]+)\\) = ([a-zA-Z]+)")
|
||||
if(NOT "${SCOPE}" STREQUAL "Project")
|
||||
set(RunCMake_TEST_FAILED "Improper nesting of ProjectSection" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
set(SCOPE "ProjectSection")
|
||||
|
||||
if("${CMAKE_MATCH_1}" STREQUAL "SolutionItems")
|
||||
if(NOT "${CMAKE_MATCH_2}" STREQUAL "preProject")
|
||||
set(RunCMake_TEST_FAILED "SolutionItems must be preProject" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(IN_SOLUTION_ITEMS TRUE)
|
||||
endif()
|
||||
|
||||
# EndProjectSection
|
||||
elseif(line STREQUAL "EndProjectSection")
|
||||
if(NOT "${SCOPE}" STREQUAL "ProjectSection")
|
||||
set(RunCMake_TEST_FAILED "Improper nesting of EndProjectSection" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
set(SCOPE "Project")
|
||||
|
||||
set(IN_SOLUTION_ITEMS FALSE)
|
||||
|
||||
# Global
|
||||
elseif(line STREQUAL "Global")
|
||||
if(NOT "${SCOPE}" STREQUAL "")
|
||||
set(RunCMake_TEST_FAILED "Improper nesting of Global" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
set(SCOPE "Global")
|
||||
|
||||
# EndGlobal
|
||||
elseif(line STREQUAL "EndGlobal")
|
||||
if(NOT "${SCOPE}" STREQUAL "Global")
|
||||
set(RunCMake_TEST_FAILED "Improper nesting of EndGlobal" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
set(SCOPE "")
|
||||
|
||||
# GlobalSection
|
||||
elseif(line MATCHES "GlobalSection\\(([a-zA-Z]+)\\) = ([a-zA-Z]+)")
|
||||
if(NOT "${SCOPE}" STREQUAL "Global")
|
||||
set(RunCMake_TEST_FAILED "Improper nesting of GlobalSection" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
set(SCOPE "GlobalSection")
|
||||
|
||||
if("${CMAKE_MATCH_1}" STREQUAL "NestedProjects")
|
||||
if(NOT "${CMAKE_MATCH_2}" STREQUAL "preSolution")
|
||||
set(RunCMake_TEST_FAILED "NestedProjects must be preSolution" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(IN_NESTED_PROJECTS TRUE)
|
||||
endif()
|
||||
|
||||
# EndGlobalSection
|
||||
elseif(line STREQUAL "EndGlobalSection")
|
||||
if(NOT "${SCOPE}" STREQUAL "GlobalSection")
|
||||
set(RunCMake_TEST_FAILED "Improper nesting of EndGlobalSection" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
set(SCOPE "Global")
|
||||
|
||||
set(IN_NESTED_PROJECTS FALSE)
|
||||
|
||||
# .../solution-item-0-1.txt = .../solution-item-0-1.txt
|
||||
elseif(${IN_SOLUTION_ITEMS})
|
||||
if(NOT line MATCHES "([^=]+)=([^=]+)")
|
||||
set(RunCMake_TEST_FAILED "Invalid solution item paths 1" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
string(STRIP "${CMAKE_MATCH_1}" CMAKE_MATCH_1)
|
||||
string(STRIP "${CMAKE_MATCH_2}" CMAKE_MATCH_2)
|
||||
|
||||
if(NOT "${CMAKE_MATCH_1}" STREQUAL "${CMAKE_MATCH_2}")
|
||||
set(RunCMake_TEST_FAILED "Invalid solution item paths 2" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
cmake_path(GET CMAKE_MATCH_1 FILENAME filename)
|
||||
MapAppend(SOLUTION_ITEMS "${filename}" "${GROUP_NAME}")
|
||||
|
||||
# {1EB55F5E...} = {A11E84C6...}
|
||||
elseif(${IN_NESTED_PROJECTS})
|
||||
if(NOT line MATCHES "(${REGUID}) = (${REGUID})")
|
||||
set(RunCMake_TEST_FAILED "Invalid nested project guids" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
MapFind(PROJECT_PARENTS "${CMAKE_MATCH_1}" existing_parent)
|
||||
if(DEFINED existing_parent)
|
||||
set(RunCMake_TEST_FAILED "Duplicate nested project: '${CMAKE_MATCH_1}'" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
MapAppend(PROJECT_PARENTS "${CMAKE_MATCH_1}" "${CMAKE_MATCH_2}")
|
||||
|
||||
endif()
|
||||
|
||||
MapPropagateToParentScope(GROUP_PATHS)
|
||||
MapPropagateToParentScope(GROUP_GUIDS)
|
||||
MapPropagateToParentScope(PROJECT_PARENTS)
|
||||
MapPropagateToParentScope(SOLUTION_ITEMS)
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
|
||||
# Check the root solution:
|
||||
block()
|
||||
ParseSln("${RunCMake_TEST_BINARY_DIR}/SolutionItems.sln")
|
||||
|
||||
if(DEFINED RunCMake_TEST_FAILED)
|
||||
set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
|
||||
# Check group guids and nesting:
|
||||
|
||||
MapFind(GROUP_GUIDS "Solution Items" root_group_guid)
|
||||
if(NOT DEFINED root_group_guid)
|
||||
set(RunCMake_TEST_FAILED "Solution Items not found" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
MapFind(GROUP_PATHS "Solution Items" root_group_path)
|
||||
if(NOT "${root_group_path}" STREQUAL "Solution Items")
|
||||
set(RunCMake_TEST_FAILED "Invalid Solution Items path: '${root_group_path}'" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
MapFind(PROJECT_PARENTS "${root_group_guid}" root_group_parent_guid)
|
||||
if(DEFINED root_group_parent_guid)
|
||||
set(RunCMake_TEST_FAILED "Solution Items is nested" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
MapFind(GROUP_GUIDS "Outer Group" outer_group_guid)
|
||||
if(NOT DEFINED outer_group_guid)
|
||||
set(RunCMake_TEST_FAILED "Outer Group not found" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
MapFind(GROUP_PATHS "Outer Group" outer_group_path)
|
||||
if(NOT "${outer_group_path}" STREQUAL "Outer Group")
|
||||
set(RunCMake_TEST_FAILED "Invalid Outer Group path: '${outer_group_path}'" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
MapFind(PROJECT_PARENTS "${outer_group_guid}" outer_group_parent_guid)
|
||||
if(DEFINED outer_group_parent_guid)
|
||||
set(RunCMake_TEST_FAILED "Outer Group is nested" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
MapFind(GROUP_GUIDS "Inner Group" inner_group_guid)
|
||||
if(NOT DEFINED inner_group_guid)
|
||||
set(RunCMake_TEST_FAILED "Inner Group not found" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
MapFind(GROUP_PATHS "Inner Group" inner_group_path)
|
||||
if(NOT "${inner_group_path}" STREQUAL "Outer Group\\Inner Group")
|
||||
set(RunCMake_TEST_FAILED "Invalid Inner Group path: '${inner_group_path}'" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
MapFind(PROJECT_PARENTS "${inner_group_guid}" inner_group_parent_guid)
|
||||
if(NOT DEFINED inner_group_parent_guid)
|
||||
set(RunCMake_TEST_FAILED "Inner Group is not nested" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
if(NOT "${inner_group_parent_guid}" STREQUAL "${outer_group_guid}")
|
||||
set(RunCMake_TEST_FAILED "Inner Group is not nested within Outer Group" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
|
||||
# Check solution items and nesting:
|
||||
MapLength(SOLUTION_ITEMS solution_item_count)
|
||||
if(NOT "${solution_item_count}" EQUAL 4)
|
||||
set(RunCMake_TEST_FAILED "Unexpected number of solution items: ${solution_item_count}")
|
||||
return()
|
||||
endif()
|
||||
|
||||
MapFind(SOLUTION_ITEMS "solution-item-0-1.txt" group_name)
|
||||
if(NOT DEFINED group_name)
|
||||
set(RunCMake_TEST_FAILED "Solution item not found: solution-item-0-1.txt")
|
||||
return()
|
||||
endif()
|
||||
if(NOT "${group_name}" STREQUAL "Solution Items")
|
||||
set(RunCMake_TEST_FAILED "Invalid group for solution-item-0-1.txt: '${group_name}'")
|
||||
return()
|
||||
endif()
|
||||
|
||||
MapFind(SOLUTION_ITEMS "solution-item-1-1.txt" group_name)
|
||||
if(NOT DEFINED group_name)
|
||||
set(RunCMake_TEST_FAILED "Solution item not found: solution-item-1-1.txt")
|
||||
return()
|
||||
endif()
|
||||
if(NOT "${group_name}" STREQUAL "Outer Group")
|
||||
set(RunCMake_TEST_FAILED "Invalid group for solution-item-1-1.txt: '${group_name}'")
|
||||
return()
|
||||
endif()
|
||||
|
||||
MapFind(SOLUTION_ITEMS "solution-item-2-1.txt" group_name)
|
||||
if(NOT DEFINED group_name)
|
||||
set(RunCMake_TEST_FAILED "Solution item not found: solution-item-2-1.txt")
|
||||
return()
|
||||
endif()
|
||||
if(NOT "${group_name}" STREQUAL "Inner Group")
|
||||
set(RunCMake_TEST_FAILED "Invalid group for solution-item-2-1.txt: '${group_name}'")
|
||||
return()
|
||||
endif()
|
||||
|
||||
MapFind(SOLUTION_ITEMS "solution-item-2-2.txt" group_name)
|
||||
if(NOT DEFINED group_name)
|
||||
set(RunCMake_TEST_FAILED "Solution item not found: solution-item-2-2.txt")
|
||||
return()
|
||||
endif()
|
||||
if(NOT "${group_name}" STREQUAL "Inner Group")
|
||||
set(RunCMake_TEST_FAILED "Invalid group for solution-item-2-2.txt: '${group_name}'")
|
||||
return()
|
||||
endif()
|
||||
endblock()
|
||||
|
||||
|
||||
# Check the nested solution:
|
||||
block()
|
||||
ParseSln("${RunCMake_TEST_BINARY_DIR}/SolutionItems/SolutionItemsSubproject.sln")
|
||||
|
||||
if(DEFINED RunCMake_TEST_FAILED)
|
||||
set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
|
||||
# Check group guids and nesting:
|
||||
|
||||
MapFind(GROUP_GUIDS "Extraneous" root_group_guid)
|
||||
if(NOT DEFINED root_group_guid)
|
||||
set(RunCMake_TEST_FAILED "Extraneous not found" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
MapFind(GROUP_PATHS "Extraneous" root_group_path)
|
||||
if(NOT "${root_group_path}" STREQUAL "Extraneous")
|
||||
set(RunCMake_TEST_FAILED "Invalid Extraneous path: '${root_group_path}'" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
MapFind(PROJECT_PARENTS "${root_group_guid}" root_group_parent_guid)
|
||||
if(DEFINED root_group_parent_guid)
|
||||
set(RunCMake_TEST_FAILED "Extraneous is nested" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
|
||||
# Check solution items and nesting:
|
||||
|
||||
MapLength(SOLUTION_ITEMS solution_item_count)
|
||||
if(NOT "${solution_item_count}" EQUAL 1)
|
||||
set(RunCMake_TEST_FAILED "Unexpected number of solution items: ${solution_item_count}" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
MapFind(SOLUTION_ITEMS "extraneous.txt" group_name)
|
||||
if(NOT DEFINED group_name)
|
||||
set(RunCMake_TEST_FAILED "Solution item not found: extraneous.txt" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
if(NOT "${group_name}" STREQUAL "Extraneous")
|
||||
set(RunCMake_TEST_FAILED "Invalid group for extraneous.txt: '${group_name}'" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
endblock()
|
||||
@@ -0,0 +1,4 @@
|
||||
set_property(DIRECTORY APPEND PROPERTY VS_SOLUTION_ITEMS solution-item-0-1.txt solution-item-1-1.txt solution-item-2-1.txt solution-item-2-2.txt)
|
||||
source_group("Outer Group" FILES solution-item-1-1.txt)
|
||||
source_group("Outer Group/Inner Group" REGULAR_EXPRESSION "solution-item-2-[0-9]+\\.txt")
|
||||
add_subdirectory(SolutionItems)
|
||||
@@ -0,0 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
project(SolutionItemsSubproject)
|
||||
set_property(DIRECTORY APPEND PROPERTY VS_SOLUTION_ITEMS extraneous.txt)
|
||||
source_group("Extraneous" REGULAR_EXPRESSION "[^.]+\\.txt")
|
||||
Reference in New Issue
Block a user