From 2db623f554d5522350214a7c5bacd5ec2dec1b34 Mon Sep 17 00:00:00 2001 From: Brad King Date: Mon, 14 Sep 2020 11:52:31 -0400 Subject: Xcode: Add option to specify build system variant Extend the `-T ` option to support a `buildsystem=` field with the Xcode generator. Add a `CMAKE_XCODE_BUILD_SYSTEM` variable to inform project code about the selected build system variant. --- Help/generator/Xcode.rst | 23 ++++- Help/manual/cmake-variables.7.rst | 1 + Help/variable/CMAKE_XCODE_BUILD_SYSTEM.rst | 17 +++ Source/cmGlobalXCodeGenerator.cxx | 115 ++++++++++++++++++--- Source/cmGlobalXCodeGenerator.h | 11 ++ Tests/RunCMake/CMakeLists.txt | 3 + .../BadToolsetHostArchXcode-stderr.txt | 6 +- .../BadToolsetXcodeBuildSystem-result.txt | 1 + .../BadToolsetXcodeBuildSystem-stderr.txt | 10 ++ .../BadToolsetXcodeBuildSystem.cmake | 1 + Tests/RunCMake/GeneratorToolset/RunCMakeTest.cmake | 4 + .../TestToolsetXcodeBuildSystemDefault1-result.txt | 1 + .../TestToolsetXcodeBuildSystemDefault1-stderr.txt | 4 + .../TestToolsetXcodeBuildSystemDefault1-stdout.txt | 1 + .../TestToolsetXcodeBuildSystemDefault1.cmake | 8 ++ 15 files changed, 187 insertions(+), 19 deletions(-) create mode 100644 Help/variable/CMAKE_XCODE_BUILD_SYSTEM.rst create mode 100644 Tests/RunCMake/GeneratorToolset/BadToolsetXcodeBuildSystem-result.txt create mode 100644 Tests/RunCMake/GeneratorToolset/BadToolsetXcodeBuildSystem-stderr.txt create mode 100644 Tests/RunCMake/GeneratorToolset/BadToolsetXcodeBuildSystem.cmake create mode 100644 Tests/RunCMake/GeneratorToolset/TestToolsetXcodeBuildSystemDefault1-result.txt create mode 100644 Tests/RunCMake/GeneratorToolset/TestToolsetXcodeBuildSystemDefault1-stderr.txt create mode 100644 Tests/RunCMake/GeneratorToolset/TestToolsetXcodeBuildSystemDefault1-stdout.txt create mode 100644 Tests/RunCMake/GeneratorToolset/TestToolsetXcodeBuildSystemDefault1.cmake diff --git a/Help/generator/Xcode.rst b/Help/generator/Xcode.rst index d893ac5..c0745dc 100644 --- a/Help/generator/Xcode.rst +++ b/Help/generator/Xcode.rst @@ -5,9 +5,28 @@ Generate Xcode project files. This supports Xcode 5.0 and above. -Toolset Selection -^^^^^^^^^^^^^^^^^ +.. _`Xcode Build System Selection`: + +Toolset and Build System Selection +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ By default Xcode is allowed to select its own default toolchain. The :variable:`CMAKE_GENERATOR_TOOLSET` option may be set, perhaps via the :manual:`cmake(1)` ``-T`` option, to specify another toolset. + +This generator supports toolset specification using one of these forms: + +* ``toolset`` +* ``toolset[,key=value]*`` +* ``key=value[,key=value]*`` + +The ``toolset`` specifies the toolset name. The selected toolset name +is provided in the :variable:`CMAKE_XCODE_PLATFORM_TOOLSET` variable. + +The ``key=value`` pairs form a comma-separated list of options to +specify generator-specific details of the toolset selection. +Supported pairs are: + +``buildsystem=`` + Specify the buildsystem variant to use. + See the :variable:`CMAKE_XCODE_BUILD_SYSTEM` variable for allowed values. diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst index b5bc7b1..17d0882 100644 --- a/Help/manual/cmake-variables.7.rst +++ b/Help/manual/cmake-variables.7.rst @@ -125,6 +125,7 @@ Variables that Provide Information /variable/CMAKE_VS_PLATFORM_TOOLSET_VERSION /variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION /variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM + /variable/CMAKE_XCODE_BUILD_SYSTEM /variable/CMAKE_XCODE_PLATFORM_TOOLSET /variable/PROJECT-NAME_BINARY_DIR /variable/PROJECT-NAME_DESCRIPTION diff --git a/Help/variable/CMAKE_XCODE_BUILD_SYSTEM.rst b/Help/variable/CMAKE_XCODE_BUILD_SYSTEM.rst new file mode 100644 index 0000000..8696bbf --- /dev/null +++ b/Help/variable/CMAKE_XCODE_BUILD_SYSTEM.rst @@ -0,0 +1,17 @@ +CMAKE_XCODE_BUILD_SYSTEM +------------------------ + +Xcode build system selection. + +The :generator:`Xcode` generator defines this variable to indicate which +variant of the Xcode build system will be used. The value is the +version of Xcode in which the corresponding build system first became +mature enough for use by CMake. The possible values are: + +``1`` + The original Xcode build system. + This is the default. + +The ``CMAKE_XCODE_BUILD_SYSTEM`` variable is informational and should not +be modified by project code. See the :ref:`Xcode Build System Selection` +documentation section to select the Xcode build system. diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index be4f9ad..cceaea0 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/Source/cmGlobalXCodeGenerator.cxx @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -280,32 +281,122 @@ bool cmGlobalXCodeGenerator::SetSystemName(std::string const& s, return this->cmGlobalGenerator::SetSystemName(s, mf); } +namespace { +cm::string_view cmXcodeBuildSystemString(cmGlobalXCodeGenerator::BuildSystem b) +{ + switch (b) { + case cmGlobalXCodeGenerator::BuildSystem::One: + return "1"_s; + } + return {}; +} +} + bool cmGlobalXCodeGenerator::SetGeneratorToolset(std::string const& ts, bool build, cmMakefile* mf) { - if (ts.find_first_of(",=") != std::string::npos) { - std::ostringstream e; - /* clang-format off */ - e << - "Generator\n" - " " << this->GetName() << "\n" - "does not recognize the toolset\n" - " " << ts << "\n" - "that was specified."; - /* clang-format on */ - mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); + if (!this->ParseGeneratorToolset(ts, mf)) { return false; } - this->GeneratorToolset = ts; if (build) { return true; } if (!this->GeneratorToolset.empty()) { mf->AddDefinition("CMAKE_XCODE_PLATFORM_TOOLSET", this->GeneratorToolset); } + mf->AddDefinition("CMAKE_XCODE_BUILD_SYSTEM", + cmXcodeBuildSystemString(this->XcodeBuildSystem)); return true; } +bool cmGlobalXCodeGenerator::ParseGeneratorToolset(std::string const& ts, + cmMakefile* mf) +{ + std::vector const fields = cmTokenize(ts, ","); + auto fi = fields.cbegin(); + if (fi == fields.cend()) { + return true; + } + + // The first field may be the Xcode GCC_VERSION. + if (fi->find('=') == fi->npos) { + this->GeneratorToolset = *fi; + ++fi; + } + + std::unordered_set handled; + + // The rest of the fields must be key=value pairs. + for (; fi != fields.cend(); ++fi) { + std::string::size_type pos = fi->find('='); + if (pos == fi->npos) { + /* clang-format off */ + std::string const& e = cmStrCat( + "Generator\n" + " ", this->GetName(), "\n" + "given toolset specification\n" + " ", ts, "\n" + "that contains a field after the first ',' with no '='." + ); + /* clang-format on */ + mf->IssueMessage(MessageType::FATAL_ERROR, e); + return false; + } + std::string const key = fi->substr(0, pos); + std::string const value = fi->substr(pos + 1); + if (!handled.insert(key).second) { + /* clang-format off */ + std::string const& e = cmStrCat( + "Generator\n" + " ", this->GetName(), "\n" + "given toolset specification\n" + " ", ts, "\n" + "that contains duplicate field key '", key, "'." + ); + /* clang-format on */ + mf->IssueMessage(MessageType::FATAL_ERROR, e); + return false; + } + if (!this->ProcessGeneratorToolsetField(key, value, mf)) { + return false; + } + } + + return true; +} + +bool cmGlobalXCodeGenerator::ProcessGeneratorToolsetField( + std::string const& key, std::string const& value, cmMakefile* mf) +{ + if (key == "buildsystem") { + if (value == "1"_s) { + this->XcodeBuildSystem = BuildSystem::One; + } else { + /* clang-format off */ + std::string const& e = cmStrCat( + "Generator\n" + " ", this->GetName(), "\n" + "toolset specification field\n" + " buildsystem=", value, "\n" + "value is unkonwn. It must be '1'." + ); + /* clang-format on */ + mf->IssueMessage(MessageType::FATAL_ERROR, e); + return false; + } + return true; + } + /* clang-format off */ + std::string const& e = cmStrCat( + "Generator\n" + " ", this->GetName(), "\n" + "given toolset specification that contains invalid field '", key, "'." + ); + /* clang-format on */ + mf->IssueMessage(MessageType::FATAL_ERROR, e); + return false; +} + void cmGlobalXCodeGenerator::EnableLanguage( std::vector const& lang, cmMakefile* mf, bool optional) { diff --git a/Source/cmGlobalXCodeGenerator.h b/Source/cmGlobalXCodeGenerator.h index 4e68682..bbf3ee9 100644 --- a/Source/cmGlobalXCodeGenerator.h +++ b/Source/cmGlobalXCodeGenerator.h @@ -113,11 +113,20 @@ public: cmMakefile* mf) override; void AppendFlag(std::string& flags, std::string const& flag) const; + enum class BuildSystem + { + One = 1, + }; + protected: void AddExtraIDETargets() override; void Generate() override; private: + bool ParseGeneratorToolset(std::string const& ts, cmMakefile* mf); + bool ProcessGeneratorToolsetField(std::string const& key, + std::string const& value, cmMakefile* mf); + cmXCodeObject* CreateOrGetPBXGroup(cmGeneratorTarget* gtgt, cmSourceGroup* sg); cmXCodeObject* CreatePBXGroup(cmXCodeObject* parent, @@ -254,6 +263,8 @@ protected: std::vector> XCodeObjects; cmXCodeObject* RootObject; + BuildSystem XcodeBuildSystem = BuildSystem::One; + private: std::string const& GetXcodeBuildCommand(); std::string FindXcodeBuildCommand(); diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index e586961..39455b7 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -238,6 +238,9 @@ add_RunCMake_test(GenEx-GENEX_EVAL) add_RunCMake_test(GeneratorExpression) add_RunCMake_test(GeneratorInstance) add_RunCMake_test(GeneratorPlatform) +if(XCODE_VERSION) + set(GeneratorToolset_ARGS -DXCODE_VERSION=${XCODE_VERSION}) +endif() add_RunCMake_test(GeneratorToolset) add_RunCMake_test(GetPrerequisites) add_RunCMake_test(GNUInstallDirs -DSYSTEM_NAME=${CMAKE_SYSTEM_NAME}) diff --git a/Tests/RunCMake/GeneratorToolset/BadToolsetHostArchXcode-stderr.txt b/Tests/RunCMake/GeneratorToolset/BadToolsetHostArchXcode-stderr.txt index 5737e95..a86bd02 100644 --- a/Tests/RunCMake/GeneratorToolset/BadToolsetHostArchXcode-stderr.txt +++ b/Tests/RunCMake/GeneratorToolset/BadToolsetHostArchXcode-stderr.txt @@ -3,8 +3,4 @@ CMake Error at CMakeLists.txt:[0-9]+ \(project\): .* - does not recognize the toolset - - Test Toolset,host=x6[45] - - that was specified\.$ + given toolset specification that contains invalid field 'host'\.$ diff --git a/Tests/RunCMake/GeneratorToolset/BadToolsetXcodeBuildSystem-result.txt b/Tests/RunCMake/GeneratorToolset/BadToolsetXcodeBuildSystem-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/GeneratorToolset/BadToolsetXcodeBuildSystem-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/GeneratorToolset/BadToolsetXcodeBuildSystem-stderr.txt b/Tests/RunCMake/GeneratorToolset/BadToolsetXcodeBuildSystem-stderr.txt new file mode 100644 index 0000000..9ef6b20 --- /dev/null +++ b/Tests/RunCMake/GeneratorToolset/BadToolsetXcodeBuildSystem-stderr.txt @@ -0,0 +1,10 @@ +CMake Error at CMakeLists.txt:[0-9]+ \(project\): + Generator + + Xcode + + toolset specification field + + buildsystem=bad + + value is unkonwn. It must be '1'\.$ diff --git a/Tests/RunCMake/GeneratorToolset/BadToolsetXcodeBuildSystem.cmake b/Tests/RunCMake/GeneratorToolset/BadToolsetXcodeBuildSystem.cmake new file mode 100644 index 0000000..2fc38e5 --- /dev/null +++ b/Tests/RunCMake/GeneratorToolset/BadToolsetXcodeBuildSystem.cmake @@ -0,0 +1 @@ +message(FATAL_ERROR "This should not be reached!") diff --git a/Tests/RunCMake/GeneratorToolset/RunCMakeTest.cmake b/Tests/RunCMake/GeneratorToolset/RunCMakeTest.cmake index bb22841..af59019 100644 --- a/Tests/RunCMake/GeneratorToolset/RunCMakeTest.cmake +++ b/Tests/RunCMake/GeneratorToolset/RunCMakeTest.cmake @@ -54,6 +54,10 @@ elseif("${RunCMake_GENERATOR}" STREQUAL "Xcode") run_cmake(TestToolset) set(RunCMake_GENERATOR_TOOLSET "Test Toolset,host=x64") run_cmake(BadToolsetHostArchXcode) + set(RunCMake_GENERATOR_TOOLSET "buildsystem=bad") + run_cmake(BadToolsetXcodeBuildSystem) + set(RunCMake_GENERATOR_TOOLSET "Test Toolset") + run_cmake(TestToolsetXcodeBuildSystemDefault1) else() set(RunCMake_GENERATOR_TOOLSET "Bad Toolset") run_cmake(BadToolset) diff --git a/Tests/RunCMake/GeneratorToolset/TestToolsetXcodeBuildSystemDefault1-result.txt b/Tests/RunCMake/GeneratorToolset/TestToolsetXcodeBuildSystemDefault1-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/GeneratorToolset/TestToolsetXcodeBuildSystemDefault1-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/GeneratorToolset/TestToolsetXcodeBuildSystemDefault1-stderr.txt b/Tests/RunCMake/GeneratorToolset/TestToolsetXcodeBuildSystemDefault1-stderr.txt new file mode 100644 index 0000000..0adbd0c --- /dev/null +++ b/Tests/RunCMake/GeneratorToolset/TestToolsetXcodeBuildSystemDefault1-stderr.txt @@ -0,0 +1,4 @@ +^CMake Error at TestToolsetXcodeBuildSystemDefault1.cmake:[0-9]+ \(message\): + CMAKE_GENERATOR_TOOLSET is "Test Toolset" as expected. +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/GeneratorToolset/TestToolsetXcodeBuildSystemDefault1-stdout.txt b/Tests/RunCMake/GeneratorToolset/TestToolsetXcodeBuildSystemDefault1-stdout.txt new file mode 100644 index 0000000..cba95ce --- /dev/null +++ b/Tests/RunCMake/GeneratorToolset/TestToolsetXcodeBuildSystemDefault1-stdout.txt @@ -0,0 +1 @@ +CMAKE_XCODE_BUILD_SYSTEM='1' diff --git a/Tests/RunCMake/GeneratorToolset/TestToolsetXcodeBuildSystemDefault1.cmake b/Tests/RunCMake/GeneratorToolset/TestToolsetXcodeBuildSystemDefault1.cmake new file mode 100644 index 0000000..645bb19 --- /dev/null +++ b/Tests/RunCMake/GeneratorToolset/TestToolsetXcodeBuildSystemDefault1.cmake @@ -0,0 +1,8 @@ +message(STATUS "CMAKE_XCODE_BUILD_SYSTEM='${CMAKE_XCODE_BUILD_SYSTEM}'") +if(CMAKE_GENERATOR_TOOLSET STREQUAL "Test Toolset") + message(FATAL_ERROR "CMAKE_GENERATOR_TOOLSET is \"Test Toolset\" as expected.") +else() + message(FATAL_ERROR + "CMAKE_GENERATOR_TOOLSET is \"${CMAKE_GENERATOR_TOOLSET}\" " + "but should be \"Test Toolset\"!") +endif() -- cgit v0.12