From 5ca72750c8de3c1c925c433293eb3bc6b3ea234c Mon Sep 17 00:00:00 2001 From: Brad King Date: Mon, 16 May 2016 15:30:53 -0400 Subject: [PATCH 1/9] Ninja: Simplify generation of custom target logical path In `AppendTargetOutputs` we generate a logical build target name for each UTILITY command. Simplify the logic to avoid testing the result of `ConvertToNinjaPath`. --- Source/cmGlobalNinjaGenerator.cxx | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index 012ef9010..a8e02e188 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -836,15 +836,10 @@ void cmGlobalNinjaGenerator::AppendTargetOutputs( } case cmState::OBJECT_LIBRARY: case cmState::UTILITY: { - std::string path = this->ConvertToNinjaPath( - target->GetLocalGenerator()->GetCurrentBinaryDirectory()); - if (path.empty() || path == ".") - outputs.push_back(target->GetName()); - else { - path += "/"; - path += target->GetName(); - outputs.push_back(path); - } + std::string path = + target->GetLocalGenerator()->GetCurrentBinaryDirectory() + + std::string("/") + target->GetName(); + outputs.push_back(this->ConvertToNinjaPath(path)); break; } From 3b3ecdfa4872a86dcb8eb7d5f5478259890d4707 Mon Sep 17 00:00:00 2001 From: Nicolas Despres Date: Fri, 13 May 2016 23:42:02 +0200 Subject: [PATCH 2/9] Ninja: Pre-compute "all" build target name --- Source/cmGlobalNinjaGenerator.cxx | 6 ++++-- Source/cmGlobalNinjaGenerator.h | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index a8e02e188..864cd2b8d 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -488,6 +488,8 @@ void cmGlobalNinjaGenerator::Generate() this->OpenBuildFileStream(); this->OpenRulesFileStream(); + this->TargetAll = "all"; + this->PolicyCMP0058 = this->LocalGenerators[0]->GetMakefile()->GetPolicyStatus( cmPolicies::CMP0058); @@ -1059,7 +1061,7 @@ void cmGlobalNinjaGenerator::WriteUnknownExplicitDependencies(std::ostream& os) // should all match now. std::vector unknownExplicitDepends; - this->CombinedCustomCommandExplicitDependencies.erase("all"); + this->CombinedCustomCommandExplicitDependencies.erase(this->TargetAll); std::set_difference(this->CombinedCustomCommandExplicitDependencies.begin(), this->CombinedCustomCommandExplicitDependencies.end(), @@ -1125,7 +1127,7 @@ void cmGlobalNinjaGenerator::WriteBuiltinTargets(std::ostream& os) void cmGlobalNinjaGenerator::WriteTargetAll(std::ostream& os) { cmNinjaDeps outputs; - outputs.push_back("all"); + outputs.push_back(this->TargetAll); this->WritePhonyBuild(os, "The main all target.", outputs, this->AllDependencies); diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h index 881104fa9..151950b2c 100644 --- a/Source/cmGlobalNinjaGenerator.h +++ b/Source/cmGlobalNinjaGenerator.h @@ -397,6 +397,9 @@ private: std::string NinjaCommand; std::string NinjaVersion; + +private: + std::string TargetAll; }; #endif // ! cmGlobalNinjaGenerator_h From 0397c92a15e505021d75eb0a156a312b024eb85e Mon Sep 17 00:00:00 2001 From: Nicolas Despres Date: Fri, 13 May 2016 23:44:25 +0200 Subject: [PATCH 3/9] Ninja: Pre-compute "CMakeCache.txt" build target name --- Source/cmGlobalNinjaGenerator.cxx | 5 +++-- Source/cmGlobalNinjaGenerator.h | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index 864cd2b8d..df5a00159 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -489,6 +489,7 @@ void cmGlobalNinjaGenerator::Generate() this->OpenRulesFileStream(); this->TargetAll = "all"; + this->CMakeCacheFile = "CMakeCache.txt"; this->PolicyCMP0058 = this->LocalGenerators[0]->GetMakefile()->GetPolicyStatus( @@ -1040,7 +1041,7 @@ void cmGlobalNinjaGenerator::WriteUnknownExplicitDependencies(std::ostream& os) } } } - knownDependencies.insert("CMakeCache.txt"); + knownDependencies.insert(this->CMakeCacheFile); for (TargetAliasMap::const_iterator i = this->TargetAliases.begin(); i != this->TargetAliases.end(); ++i) { @@ -1168,7 +1169,7 @@ void cmGlobalNinjaGenerator::WriteTargetRebuildManifest(std::ostream& os) implicitDeps.push_back(this->ConvertToNinjaPath(*fi)); } } - implicitDeps.push_back("CMakeCache.txt"); + implicitDeps.push_back(this->CMakeCacheFile); std::sort(implicitDeps.begin(), implicitDeps.end()); implicitDeps.erase(std::unique(implicitDeps.begin(), implicitDeps.end()), diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h index 151950b2c..28034f666 100644 --- a/Source/cmGlobalNinjaGenerator.h +++ b/Source/cmGlobalNinjaGenerator.h @@ -400,6 +400,7 @@ private: private: std::string TargetAll; + std::string CMakeCacheFile; }; #endif // ! cmGlobalNinjaGenerator_h From d4381cb15df3e81b78b6d8d9e7d9b727a616985d Mon Sep 17 00:00:00 2001 From: Nicolas Despres Date: Sat, 14 May 2016 01:18:20 +0200 Subject: [PATCH 4/9] Ninja: Convert link library file names like all other output paths All paths generated on Ninja-invoked command lines should be passed through ConvertToNinjaPath. Fix ConvertToLinkReference to call this instead of partially duplicating its implementation. --- Source/cmLocalNinjaGenerator.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx index 671d8a089..69f8fe518 100644 --- a/Source/cmLocalNinjaGenerator.cxx +++ b/Source/cmLocalNinjaGenerator.cxx @@ -123,7 +123,8 @@ cmGlobalNinjaGenerator* cmLocalNinjaGenerator::GetGlobalNinjaGenerator() std::string cmLocalNinjaGenerator::ConvertToLinkReference( std::string const& lib, OutputFormat format) { - return this->Convert(lib, HOME_OUTPUT, format); + std::string path = this->GetGlobalNinjaGenerator()->ConvertToNinjaPath(lib); + return this->ConvertToOutputFormat(path, format); } std::string cmLocalNinjaGenerator::ConvertToIncludeReference( From ac3cdd9af287e653d53b18c432f21a919b102158 Mon Sep 17 00:00:00 2001 From: Nicolas Despres Date: Sat, 14 May 2016 01:18:20 +0200 Subject: [PATCH 5/9] Ninja: Convert object file names to ninja paths earlier In WriteObjectBuildStatement we pass object file names and directories to several places that expect paths as Ninja sees them. Convert them to Ninja paths before all such uses. --- Source/cmNinjaTargetGenerator.cxx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index c214c8a4a..994daeae2 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -521,8 +521,10 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( std::string const language = source->GetLanguage(); std::string const sourceFileName = language == "RC" ? source->GetFullPath() : this->GetSourceFilePath(source); - std::string const objectDir = this->GeneratorTarget->GetSupportDirectory(); - std::string const objectFileName = this->GetObjectFilePath(source); + std::string const objectDir = + this->ConvertToNinjaPath(this->GeneratorTarget->GetSupportDirectory()); + std::string const objectFileName = + this->ConvertToNinjaPath(this->GetObjectFilePath(source)); std::string const objectFileDir = cmSystemTools::GetFilenamePath(objectFileName); @@ -582,9 +584,9 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( EnsureParentDirectoryExists(objectFileName); vars["OBJECT_DIR"] = this->GetLocalGenerator()->ConvertToOutputFormat( - ConvertToNinjaPath(objectDir), cmLocalGenerator::SHELL); + objectDir, cmLocalGenerator::SHELL); vars["OBJECT_FILE_DIR"] = this->GetLocalGenerator()->ConvertToOutputFormat( - ConvertToNinjaPath(objectFileDir), cmLocalGenerator::SHELL); + objectFileDir, cmLocalGenerator::SHELL); this->addPoolNinjaVariable("JOB_POOL_COMPILE", this->GetGeneratorTarget(), vars); From 7c26a6a269b0c12ecdf7605183d9fa26ad2b91eb Mon Sep 17 00:00:00 2001 From: Nicolas Despres Date: Sat, 14 May 2016 01:18:20 +0200 Subject: [PATCH 6/9] Ninja: Fix path to soname-d target file In WriteLinkStatement we convert all target output paths to ninja-style paths. Add a missing conversion for the `soname` file name. --- Source/cmNinjaNormalTargetGenerator.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index 831d44b9b..8d7a89272 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -652,7 +652,8 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() cmNinjaDeps(1, targetOutputReal), emptyDeps, emptyDeps, symlinkVars); } else { cmNinjaDeps symlinks; - const std::string soName = this->GetTargetFilePath(this->TargetNameSO); + std::string const soName = + this->ConvertToNinjaPath(this->GetTargetFilePath(this->TargetNameSO)); // If one link has to be created. if (targetOutputReal == soName || targetOutput == soName) { symlinkVars["SONAME"] = soName; From 038e7716e58e4cf79bda6ba72b92814a14978a8f Mon Sep 17 00:00:00 2001 From: Nicolas Despres Date: Sat, 14 May 2016 01:18:20 +0200 Subject: [PATCH 7/9] Ninja: Pass all build paths through a central method This gives us a central location to revise paths. --- Source/cmGlobalNinjaGenerator.cxx | 23 ++++++++++++++++------- Source/cmGlobalNinjaGenerator.h | 2 ++ Source/cmLocalNinjaGenerator.cxx | 9 +++++++-- Source/cmNinjaUtilityTargetGenerator.cxx | 2 ++ 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index df5a00159..3c5a8e3bb 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -488,8 +488,8 @@ void cmGlobalNinjaGenerator::Generate() this->OpenBuildFileStream(); this->OpenRulesFileStream(); - this->TargetAll = "all"; - this->CMakeCacheFile = "CMakeCache.txt"; + this->TargetAll = this->NinjaOutputPath("all"); + this->CMakeCacheFile = this->NinjaOutputPath("CMakeCache.txt"); this->PolicyCMP0058 = this->LocalGenerators[0]->GetMakefile()->GetPolicyStatus( @@ -722,6 +722,7 @@ std::string cmGlobalNinjaGenerator::ConvertToNinjaPath(const std::string& path) cmLocalNinjaGenerator* ng = static_cast(this->LocalGenerators[0]); std::string convPath = ng->Convert(path, cmOutputConverter::HOME_OUTPUT); + convPath = this->NinjaOutputPath(convPath); #ifdef _WIN32 cmSystemTools::ReplaceString(convPath, "/", "\\"); #endif @@ -734,6 +735,7 @@ std::string cmGlobalNinjaGenerator::ConvertToNinjaFolderRule( cmLocalNinjaGenerator* ng = static_cast(this->LocalGenerators[0]); std::string convPath = ng->Convert(path + "/all", cmOutputConverter::HOME); + convPath = this->NinjaOutputPath(convPath); #ifdef _WIN32 cmSystemTools::ReplaceString(convPath, "/", "\\"); #endif @@ -849,7 +851,7 @@ void cmGlobalNinjaGenerator::AppendTargetOutputs( case cmState::GLOBAL_TARGET: // Always use the target in HOME instead of an unused duplicate in a // subdirectory. - outputs.push_back(target->GetName()); + outputs.push_back(this->NinjaOutputPath(target->GetName())); break; default: @@ -883,6 +885,7 @@ void cmGlobalNinjaGenerator::AppendTargetDepends( void cmGlobalNinjaGenerator::AddTargetAlias(const std::string& alias, cmGeneratorTarget* target) { + std::string buildAlias = this->NinjaOutputPath(alias); cmNinjaDeps outputs; this->AppendTargetOutputs(target, outputs); // Mark the target's outputs as ambiguous to ensure that no other target uses @@ -893,7 +896,7 @@ void cmGlobalNinjaGenerator::AddTargetAlias(const std::string& alias, // Insert the alias into the map. If the alias was already present in the // map and referred to another target, mark it as ambiguous. std::pair newAlias = - TargetAliases.insert(std::make_pair(alias, target)); + TargetAliases.insert(std::make_pair(buildAlias, target)); if (newAlias.second && newAlias.first->second != target) newAlias.first->second = 0; } @@ -1182,9 +1185,10 @@ void cmGlobalNinjaGenerator::WriteTargetRebuildManifest(std::ostream& os) variables["pool"] = "console"; } + std::string const ninjaBuildFile = this->NinjaOutputPath(NINJA_BUILD_FILE); this->WriteBuild(os, "Re-run CMake if any of its inputs changed.", "RERUN_CMAKE", - /*outputs=*/cmNinjaDeps(1, NINJA_BUILD_FILE), + /*outputs=*/cmNinjaDeps(1, ninjaBuildFile), /*explicitDeps=*/cmNinjaDeps(), implicitDeps, /*orderOnlyDeps=*/cmNinjaDeps(), variables); @@ -1221,7 +1225,7 @@ void cmGlobalNinjaGenerator::WriteTargetClean(std::ostream& os) /*restat=*/"", /*generator=*/false); WriteBuild(os, "Clean all the built files.", "CLEAN", - /*outputs=*/cmNinjaDeps(1, "clean"), + /*outputs=*/cmNinjaDeps(1, this->NinjaOutputPath("clean")), /*explicitDeps=*/cmNinjaDeps(), /*implicitDeps=*/cmNinjaDeps(), /*orderOnlyDeps=*/cmNinjaDeps(), @@ -1240,9 +1244,14 @@ void cmGlobalNinjaGenerator::WriteTargetHelp(std::ostream& os) /*restat=*/"", /*generator=*/false); WriteBuild(os, "Print all primary targets available.", "HELP", - /*outputs=*/cmNinjaDeps(1, "help"), + /*outputs=*/cmNinjaDeps(1, this->NinjaOutputPath("help")), /*explicitDeps=*/cmNinjaDeps(), /*implicitDeps=*/cmNinjaDeps(), /*orderOnlyDeps=*/cmNinjaDeps(), /*variables=*/cmNinjaVars()); } + +std::string cmGlobalNinjaGenerator::NinjaOutputPath(std::string const& path) +{ + return path; +} diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h index 28034f666..15c0f3452 100644 --- a/Source/cmGlobalNinjaGenerator.h +++ b/Source/cmGlobalNinjaGenerator.h @@ -314,6 +314,8 @@ public: static std::string RequiredNinjaVersionForConsolePool() { return "1.5"; } bool SupportsConsolePool() const; + std::string NinjaOutputPath(std::string const& path); + protected: virtual void Generate(); diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx index 69f8fe518..c7e1a90f8 100644 --- a/Source/cmLocalNinjaGenerator.cxx +++ b/Source/cmLocalNinjaGenerator.cxx @@ -225,8 +225,13 @@ void cmLocalNinjaGenerator::WriteNinjaFilesInclusion(std::ostream& os) cmGlobalNinjaGenerator::WriteDivider(os); os << "# Include auxiliary files.\n" << "\n"; - cmGlobalNinjaGenerator::WriteInclude( - os, cmGlobalNinjaGenerator::NINJA_RULES_FILE, "Include rules file."); + cmGlobalNinjaGenerator* ng = this->GetGlobalNinjaGenerator(); + std::string const ninjaRulesFile = + ng->NinjaOutputPath(cmGlobalNinjaGenerator::NINJA_RULES_FILE); + std::string const rulesFilePath = + ng->EncodeIdent(ng->EncodePath(ninjaRulesFile), os); + cmGlobalNinjaGenerator::WriteInclude(os, rulesFilePath, + "Include rules file."); os << "\n"; } diff --git a/Source/cmNinjaUtilityTargetGenerator.cxx b/Source/cmNinjaUtilityTargetGenerator.cxx index b0b11476e..d07341c80 100644 --- a/Source/cmNinjaUtilityTargetGenerator.cxx +++ b/Source/cmNinjaUtilityTargetGenerator.cxx @@ -33,6 +33,8 @@ void cmNinjaUtilityTargetGenerator::Generate() { std::string utilCommandName = cmake::GetCMakeFilesDirectoryPostSlash(); utilCommandName += this->GetTargetName() + ".util"; + utilCommandName = + this->GetGlobalGenerator()->NinjaOutputPath(utilCommandName); std::vector commands; cmNinjaDeps deps, outputs, util_outputs(1, utilCommandName); From 8a862a4d4b852c9f61ae4ed7fc46042b00a83123 Mon Sep 17 00:00:00 2001 From: Nicolas Despres Date: Sat, 14 May 2016 01:18:20 +0200 Subject: [PATCH 8/9] Ninja: Support embedding of CMake as subninja project Add a `CMAKE_NINJA_OUTPUT_PATH_PREFIX` variable. When it is set, CMake generates a `build.ninja` file suitable for embedding into another ninja project potentially generated by an alien generator. --- Help/manual/cmake-variables.7.rst | 1 + Help/release/dev/ninja-output-path-prefix.rst | 6 + .../CMAKE_NINJA_OUTPUT_PATH_PREFIX.rst | 27 ++++ Source/cmAlgorithms.h | 15 ++ Source/cmGlobalNinjaGenerator.cxx | 47 +++++- Source/cmGlobalNinjaGenerator.h | 5 + Source/cmNinjaTargetGenerator.cxx | 10 +- .../RunCMake/Ninja/CheckNoPrefixSubDir.cmake | 7 + .../Ninja/CheckNoPrefixSubDirScript.cmake | 8 + Tests/RunCMake/Ninja/CheckOutput.cmake | 23 +++ .../Ninja/CustomCommandWorkingDirectory.cmake | 13 ++ Tests/RunCMake/Ninja/Executable.cmake | 5 + Tests/RunCMake/Ninja/RunCMakeTest.cmake | 145 ++++++++++++++++++ Tests/RunCMake/Ninja/SharedLib.cmake | 8 + Tests/RunCMake/Ninja/StaticLib.cmake | 9 ++ Tests/RunCMake/Ninja/SubDirPrefix.cmake | 8 + .../Ninja/SubDirPrefix/CMakeLists.txt | 2 + Tests/RunCMake/Ninja/SubDirPrefix/greeting.c | 9 ++ Tests/RunCMake/Ninja/SubDirPrefix/greeting.h | 4 + Tests/RunCMake/Ninja/TwoLibs.cmake | 17 ++ Tests/RunCMake/Ninja/greeting.c | 9 ++ Tests/RunCMake/Ninja/greeting.h | 4 + Tests/RunCMake/Ninja/greeting2.c | 6 + Tests/RunCMake/Ninja/greeting2.h | 1 + Tests/RunCMake/Ninja/hello.c | 7 + Tests/RunCMake/Ninja/hello_sub_greeting.c | 7 + Tests/RunCMake/Ninja/hello_with_greeting.c | 7 + .../RunCMake/Ninja/hello_with_two_greetings.c | 9 ++ 28 files changed, 412 insertions(+), 7 deletions(-) create mode 100644 Help/release/dev/ninja-output-path-prefix.rst create mode 100644 Help/variable/CMAKE_NINJA_OUTPUT_PATH_PREFIX.rst create mode 100644 Tests/RunCMake/Ninja/CheckNoPrefixSubDir.cmake create mode 100644 Tests/RunCMake/Ninja/CheckNoPrefixSubDirScript.cmake create mode 100644 Tests/RunCMake/Ninja/CheckOutput.cmake create mode 100644 Tests/RunCMake/Ninja/CustomCommandWorkingDirectory.cmake create mode 100644 Tests/RunCMake/Ninja/Executable.cmake create mode 100644 Tests/RunCMake/Ninja/SharedLib.cmake create mode 100644 Tests/RunCMake/Ninja/StaticLib.cmake create mode 100644 Tests/RunCMake/Ninja/SubDirPrefix.cmake create mode 100644 Tests/RunCMake/Ninja/SubDirPrefix/CMakeLists.txt create mode 100644 Tests/RunCMake/Ninja/SubDirPrefix/greeting.c create mode 100644 Tests/RunCMake/Ninja/SubDirPrefix/greeting.h create mode 100644 Tests/RunCMake/Ninja/TwoLibs.cmake create mode 100644 Tests/RunCMake/Ninja/greeting.c create mode 100644 Tests/RunCMake/Ninja/greeting.h create mode 100644 Tests/RunCMake/Ninja/greeting2.c create mode 100644 Tests/RunCMake/Ninja/greeting2.h create mode 100644 Tests/RunCMake/Ninja/hello.c create mode 100644 Tests/RunCMake/Ninja/hello_sub_greeting.c create mode 100644 Tests/RunCMake/Ninja/hello_with_greeting.c create mode 100644 Tests/RunCMake/Ninja/hello_with_two_greetings.c diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst index 181fbbcd1..ec741ae12 100644 --- a/Help/manual/cmake-variables.7.rst +++ b/Help/manual/cmake-variables.7.rst @@ -281,6 +281,7 @@ Variables that Control the Build /variable/CMAKE_MAP_IMPORTED_CONFIG_CONFIG /variable/CMAKE_MODULE_LINKER_FLAGS_CONFIG /variable/CMAKE_MODULE_LINKER_FLAGS + /variable/CMAKE_NINJA_OUTPUT_PATH_PREFIX /variable/CMAKE_NO_BUILTIN_CHRPATH /variable/CMAKE_NO_SYSTEM_FROM_IMPORTED /variable/CMAKE_OSX_ARCHITECTURES diff --git a/Help/release/dev/ninja-output-path-prefix.rst b/Help/release/dev/ninja-output-path-prefix.rst new file mode 100644 index 000000000..47a96603a --- /dev/null +++ b/Help/release/dev/ninja-output-path-prefix.rst @@ -0,0 +1,6 @@ +ninja-output-path-prefix +------------------------ + +* The :generator:`Ninja` generator learned to read a new + :variable:`CMAKE_NINJA_OUTPUT_PATH_PREFIX` variable to configure + the generated ``build.ninja`` file for use as a ``subninja``. diff --git a/Help/variable/CMAKE_NINJA_OUTPUT_PATH_PREFIX.rst b/Help/variable/CMAKE_NINJA_OUTPUT_PATH_PREFIX.rst new file mode 100644 index 000000000..64091aaaf --- /dev/null +++ b/Help/variable/CMAKE_NINJA_OUTPUT_PATH_PREFIX.rst @@ -0,0 +1,27 @@ +CMAKE_NINJA_OUTPUT_PATH_PREFIX +------------------------------ + +Set output files path prefix for the :generator:`Ninja` generator. + +Every output files listed in the generated ``build.ninja`` will be +prefixed by the contents of this variable (a trailing slash is +appended if missing). This is useful when the generated ninja file is +meant to be embedded as a ``subninja`` file into a *super* ninja +project. For example, a ninja build file generated with a command +like:: + + cd top-build-dir/sub && + cmake -G Ninja -DCMAKE_NINJA_OUTPUT_PATH_PREFIX=sub/ path/to/source + +can be embedded in ``top-build-dir/build.ninja`` with a directive like +this:: + + subninja sub/build.ninja + +The ``auto-regeneration`` rule in ``top-build-dir/build.ninja`` must have an +order-only dependency on ``sub/build.ninja``. + +.. note:: + When ``CMAKE_NINJA_OUTPUT_PATH_PREFIX`` is set, the project generated + by CMake cannot be used as a standalone project. No default targets + are specified. diff --git a/Source/cmAlgorithms.h b/Source/cmAlgorithms.h index 76acaca8f..ee803c8e8 100644 --- a/Source/cmAlgorithms.h +++ b/Source/cmAlgorithms.h @@ -379,4 +379,19 @@ std::reverse_iterator cmMakeReverseIterator(Iter it) return std::reverse_iterator(it); } +inline bool cmHasSuffix(const std::string& str, const std::string& suffix) +{ + if (str.size() < suffix.size()) { + return false; + } + return str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0; +} + +inline void cmStripSuffixIfExists(std::string& str, const std::string& suffix) +{ + if (cmHasSuffix(str, suffix)) { + str.resize(str.size() - suffix.size()); + } +} + #endif diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index 3c5a8e3bb..d65b463de 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -488,6 +488,7 @@ void cmGlobalNinjaGenerator::Generate() this->OpenBuildFileStream(); this->OpenRulesFileStream(); + this->InitOutputPathPrefix(); this->TargetAll = this->NinjaOutputPath("all"); this->CMakeCacheFile = this->NinjaOutputPath("CMakeCache.txt"); @@ -717,6 +718,23 @@ void cmGlobalNinjaGenerator::CloseRulesFileStream() } } +static void EnsureTrailingSlash(std::string& path) +{ + if (path.empty()) { + return; + } + std::string::value_type last = path[path.size() - 1]; +#ifdef _WIN32 + if (last != '\\') { + path += '\\'; + } +#else + if (last != '/') { + path += '/'; + } +#endif +} + std::string cmGlobalNinjaGenerator::ConvertToNinjaPath(const std::string& path) { cmLocalNinjaGenerator* ng = @@ -1136,8 +1154,10 @@ void cmGlobalNinjaGenerator::WriteTargetAll(std::ostream& os) this->WritePhonyBuild(os, "The main all target.", outputs, this->AllDependencies); - cmGlobalNinjaGenerator::WriteDefault(os, outputs, - "Make the all target the default."); + if (!this->HasOutputPathPrefix()) { + cmGlobalNinjaGenerator::WriteDefault(os, outputs, + "Make the all target the default."); + } } void cmGlobalNinjaGenerator::WriteTargetRebuildManifest(std::ostream& os) @@ -1251,7 +1271,28 @@ void cmGlobalNinjaGenerator::WriteTargetHelp(std::ostream& os) /*variables=*/cmNinjaVars()); } +void cmGlobalNinjaGenerator::InitOutputPathPrefix() +{ + this->OutputPathPrefix = + this->LocalGenerators[0]->GetMakefile()->GetSafeDefinition( + "CMAKE_NINJA_OUTPUT_PATH_PREFIX"); + EnsureTrailingSlash(this->OutputPathPrefix); +} + std::string cmGlobalNinjaGenerator::NinjaOutputPath(std::string const& path) { - return path; + if (!this->HasOutputPathPrefix() || cmSystemTools::FileIsFullPath(path)) { + return path; + } + return this->OutputPathPrefix + path; +} + +void cmGlobalNinjaGenerator::StripNinjaOutputPathPrefixAsSuffix( + std::string& path) +{ + if (path.empty()) { + return; + } + EnsureTrailingSlash(path); + cmStripSuffixIfExists(path, this->OutputPathPrefix); } diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h index 15c0f3452..6d9bfe835 100644 --- a/Source/cmGlobalNinjaGenerator.h +++ b/Source/cmGlobalNinjaGenerator.h @@ -315,6 +315,8 @@ public: bool SupportsConsolePool() const; std::string NinjaOutputPath(std::string const& path); + bool HasOutputPathPrefix() const { return !this->OutputPathPrefix.empty(); } + void StripNinjaOutputPathPrefixAsSuffix(std::string& path); protected: virtual void Generate(); @@ -401,6 +403,9 @@ private: std::string NinjaVersion; private: + void InitOutputPathPrefix(); + + std::string OutputPathPrefix; std::string TargetAll; std::string CMakeCacheFile; }; diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index 994daeae2..4d58242a5 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -669,10 +669,12 @@ void cmNinjaTargetGenerator::EnsureDirectoryExists( if (cmSystemTools::FileIsFullPath(path.c_str())) { cmSystemTools::MakeDirectory(path.c_str()); } else { - const std::string fullPath = std::string(this->GetGlobalGenerator() - ->GetCMakeInstance() - ->GetHomeOutputDirectory()) + - "/" + path; + cmGlobalNinjaGenerator* gg = this->GetGlobalGenerator(); + std::string fullPath = + std::string(gg->GetCMakeInstance()->GetHomeOutputDirectory()); + // Also ensures their is a trailing slash. + gg->StripNinjaOutputPathPrefixAsSuffix(fullPath); + fullPath += path; cmSystemTools::MakeDirectory(fullPath.c_str()); } } diff --git a/Tests/RunCMake/Ninja/CheckNoPrefixSubDir.cmake b/Tests/RunCMake/Ninja/CheckNoPrefixSubDir.cmake new file mode 100644 index 000000000..b845f2f4a --- /dev/null +++ b/Tests/RunCMake/Ninja/CheckNoPrefixSubDir.cmake @@ -0,0 +1,7 @@ +add_custom_target(check_no_prefix_sub_dir ALL + COMMAND "${CMAKE_COMMAND}" + "-DNINJA_OUTPUT_PATH_PREFIX=${CMAKE_NINJA_OUTPUT_PATH_PREFIX}" + "-DCUR_BIN_DIR=${CMAKE_CURRENT_BINARY_DIR}" + -P "${CMAKE_CURRENT_SOURCE_DIR}/CheckNoPrefixSubDirScript.cmake" + VERBATIM + ) diff --git a/Tests/RunCMake/Ninja/CheckNoPrefixSubDirScript.cmake b/Tests/RunCMake/Ninja/CheckNoPrefixSubDirScript.cmake new file mode 100644 index 000000000..5a03fcb7e --- /dev/null +++ b/Tests/RunCMake/Ninja/CheckNoPrefixSubDirScript.cmake @@ -0,0 +1,8 @@ +# Check that the prefix sub-directory is not repeated. + +if(EXISTS "${CUR_BIN_DIR}/${NINJA_OUTPUT_PATH_PREFIX}") + message(FATAL_ERROR + "no sub directory named after the CMAKE_NINJA_OUTPUT_PATH_PREFIX " + "should be in the binary directory." + ) +endif() diff --git a/Tests/RunCMake/Ninja/CheckOutput.cmake b/Tests/RunCMake/Ninja/CheckOutput.cmake new file mode 100644 index 000000000..ddb35a75e --- /dev/null +++ b/Tests/RunCMake/Ninja/CheckOutput.cmake @@ -0,0 +1,23 @@ +# Add rules to check the generated executable works. + +set(hello_output "${CMAKE_CURRENT_BINARY_DIR}/hello.output") +add_custom_command( + OUTPUT "${hello_output}" + COMMAND "$" > "${hello_output}" + DEPENDS hello + VERBATIM + ) + +if(NOT DEFINED HELLO_OUTPUT_STRING) + set(HELLO_OUTPUT_STRING "Hello world!\n") +endif() + +set(hello_output_ref "${CMAKE_CURRENT_BINARY_DIR}/hello.output.ref") +file(WRITE "${hello_output_ref}" "${HELLO_OUTPUT_STRING}") + +add_custom_target(check_output ALL + COMMAND "${CMAKE_COMMAND}" -E compare_files + "${hello_output}" "${hello_output_ref}" + DEPENDS "${hello_output}" "${hello_output_ref}" + VERBATIM + ) diff --git a/Tests/RunCMake/Ninja/CustomCommandWorkingDirectory.cmake b/Tests/RunCMake/Ninja/CustomCommandWorkingDirectory.cmake new file mode 100644 index 000000000..8e01c8c65 --- /dev/null +++ b/Tests/RunCMake/Ninja/CustomCommandWorkingDirectory.cmake @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.5) +project(hello NONE) + +add_custom_command( + OUTPUT hello.copy.c + COMMAND "${CMAKE_COMMAND}" -E copy + "${CMAKE_CURRENT_SOURCE_DIR}/hello.c" + hello.copy.c + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + ) +add_custom_target(copy ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/hello.copy.c") + +include(CheckNoPrefixSubDir.cmake) diff --git a/Tests/RunCMake/Ninja/Executable.cmake b/Tests/RunCMake/Ninja/Executable.cmake new file mode 100644 index 000000000..4e17d6889 --- /dev/null +++ b/Tests/RunCMake/Ninja/Executable.cmake @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.5) +project(hello C) +add_executable(hello hello.c) +include(CheckOutput.cmake) +include(CheckNoPrefixSubDir.cmake) diff --git a/Tests/RunCMake/Ninja/RunCMakeTest.cmake b/Tests/RunCMake/Ninja/RunCMakeTest.cmake index 4e068882d..8160c2dd8 100644 --- a/Tests/RunCMake/Ninja/RunCMakeTest.cmake +++ b/Tests/RunCMake/Ninja/RunCMakeTest.cmake @@ -32,3 +32,148 @@ function(run_SubDir) run_cmake_command(SubDir-build ${CMAKE_COMMAND} --build . --target ${SubDir_all}) endfunction() run_SubDir() + +function(run_ninja dir) + execute_process( + COMMAND "${RunCMake_MAKE_PROGRAM}" + WORKING_DIRECTORY "${dir}" + OUTPUT_VARIABLE ninja_stdout + ERROR_VARIABLE ninja_stderr + RESULT_VARIABLE ninja_result + ) + if(NOT ninja_result EQUAL 0) + message(STATUS " +============ beginning of ninja's stdout ============ +${ninja_stdout} +=============== end of ninja's stdout =============== +") + message(STATUS " +============ beginning of ninja's stderr ============ +${ninja_stderr} +=============== end of ninja's stderr =============== +") + message(FATAL_ERROR + "top ninja build failed exited with status ${ninja_result}") + endif() +endfunction(run_ninja) + +function(sleep delay) + execute_process( + COMMAND ${CMAKE_COMMAND} -E sleep ${delay} + RESULT_VARIABLE result + ) + if(NOT result EQUAL 0) + message(FATAL_ERROR "failed to sleep for ${delay} second.") + endif() +endfunction(sleep) + +function(touch path) + execute_process( + COMMAND ${CMAKE_COMMAND} -E touch ${path} + RESULT_VARIABLE result + ) + if(NOT result EQUAL 0) + message(FATAL_ERROR "failed to touch main ${path} file.") + endif() +endfunction(touch) + +macro(ninja_escape_path path out) + string(REPLACE "\$ " "\$\$" "${out}" "${path}") + string(REPLACE " " "\$ " "${out}" "${${out}}") + string(REPLACE ":" "\$:" "${out}" "${${out}}") +endmacro(ninja_escape_path) + +macro(shell_escape string out) + string(REPLACE "\"" "\\\"" "${out}" "${string}") +endmacro(shell_escape) + +function(run_sub_cmake test ninja_output_path_prefix) + set(top_build_dir "${RunCMake_BINARY_DIR}/${test}-build/") + file(REMOVE_RECURSE "${top_build_dir}") + file(MAKE_DIRECTORY "${top_build_dir}") + + ninja_escape_path("${ninja_output_path_prefix}" + escaped_ninja_output_path_prefix) + + # Generate top build ninja file. + set(top_build_ninja "${top_build_dir}/build.ninja") + shell_escape("${top_build_ninja}" escaped_top_build_ninja) + set(build_ninja_dep "${top_build_dir}/build_ninja_dep") + ninja_escape_path("${build_ninja_dep}" escaped_build_ninja_dep) + shell_escape("${CMAKE_COMMAND}" escaped_CMAKE_COMMAND) + file(WRITE "${build_ninja_dep}" "fake dependency of top build.ninja file\n") + if(WIN32) + set(cmd_prefix "cmd.exe /C \"") + set(cmd_suffix "\"") + else() + set(cmd_prefix "") + set(cmd_suffix "") + endif() + file(WRITE "${top_build_ninja}" "\ +subninja ${escaped_ninja_output_path_prefix}/build.ninja +default ${escaped_ninja_output_path_prefix}/all + +# Sleep for 1 second before to regenerate to make sure the timestamp of +# the top build.ninja will be strictly greater than the timestamp of the +# sub/build.ninja file. We assume the system as 1 sec timestamp resolution. +rule RERUN + command = ${cmd_prefix}\"${escaped_CMAKE_COMMAND}\" -E sleep 1 && \"${escaped_CMAKE_COMMAND}\" -E touch \"${escaped_top_build_ninja}\"${cmd_suffix} + description = Testing regeneration + generator = 1 + +build build.ninja: RERUN ${escaped_build_ninja_dep} || ${escaped_ninja_output_path_prefix}/build.ninja + pool = console +") + + # Run sub cmake project. + set(RunCMake_TEST_OPTIONS "-DCMAKE_NINJA_OUTPUT_PATH_PREFIX=${ninja_output_path_prefix}") + set(RunCMake_TEST_BINARY_DIR "${top_build_dir}/${ninja_output_path_prefix}") + run_cmake(${test}) + + # Check there is no 'default' statement in Ninja file generated by CMake. + set(sub_build_ninja "${RunCMake_TEST_BINARY_DIR}/build.ninja") + file(READ "${sub_build_ninja}" sub_build_ninja_file) + if(sub_build_ninja_file MATCHES "\ndefault [^\n][^\n]*all\n") + message(FATAL_ERROR + "unexpected 'default' statement found in '${sub_build_ninja}'") + endif() + + # Run ninja from the top build directory. + run_ninja("${top_build_dir}") + + # Test regeneration rules run in order. + set(main_cmakelists "${RunCMake_SOURCE_DIR}/CMakeLists.txt") + sleep(1) # Assume the system as 1 sec timestamp resolution. + touch("${main_cmakelists}") + touch("${build_ninja_dep}") + run_ninja("${top_build_dir}") + file(TIMESTAMP "${main_cmakelists}" mtime_main_cmakelists UTC) + file(TIMESTAMP "${sub_build_ninja}" mtime_sub_build_ninja UTC) + file(TIMESTAMP "${top_build_ninja}" mtime_top_build_ninja UTC) + + # Check sub build.ninja is regenerated. + if(mtime_main_cmakelists STRGREATER mtime_sub_build_ninja) + message(FATAL_ERROR + "sub build.ninja not regenerated: + CMakeLists.txt = ${mtime_main_cmakelists} + sub/build.ninja = ${mtime_sub_build_ninja}") + endif() + + # Check top build.ninja is regenerated after sub build.ninja. + if(NOT mtime_top_build_ninja STRGREATER mtime_sub_build_ninja) + message(FATAL_ERROR + "top build.ninja not regenerated strictly after sub build.ninja: + sub/build.ninja = ${mtime_sub_build_ninja} + build.ninja = ${mtime_top_build_ninja}") + endif() + +endfunction(run_sub_cmake) + +foreach(ninja_output_path_prefix "sub space" "sub") + run_sub_cmake(Executable "${ninja_output_path_prefix}") + run_sub_cmake(StaticLib "${ninja_output_path_prefix}") + run_sub_cmake(SharedLib "${ninja_output_path_prefix}") + run_sub_cmake(TwoLibs "${ninja_output_path_prefix}") + run_sub_cmake(SubDirPrefix "${ninja_output_path_prefix}") + run_sub_cmake(CustomCommandWorkingDirectory "${ninja_output_path_prefix}") +endforeach(ninja_output_path_prefix) diff --git a/Tests/RunCMake/Ninja/SharedLib.cmake b/Tests/RunCMake/Ninja/SharedLib.cmake new file mode 100644 index 000000000..1a78390e3 --- /dev/null +++ b/Tests/RunCMake/Ninja/SharedLib.cmake @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.5) +project(hello C) +add_library(greeting SHARED greeting.c) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) +add_executable(hello hello_with_greeting.c) +target_link_libraries(hello greeting) +include(CheckOutput.cmake) +include(CheckNoPrefixSubDir.cmake) diff --git a/Tests/RunCMake/Ninja/StaticLib.cmake b/Tests/RunCMake/Ninja/StaticLib.cmake new file mode 100644 index 000000000..0f815ae5e --- /dev/null +++ b/Tests/RunCMake/Ninja/StaticLib.cmake @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.5) +project(hello C) +add_definitions(-DGREETING_STATIC) +add_library(greeting STATIC greeting.c) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) +add_executable(hello hello_with_greeting.c) +target_link_libraries(hello greeting) +include(CheckOutput.cmake) +include(CheckNoPrefixSubDir.cmake) diff --git a/Tests/RunCMake/Ninja/SubDirPrefix.cmake b/Tests/RunCMake/Ninja/SubDirPrefix.cmake new file mode 100644 index 000000000..30ad1e607 --- /dev/null +++ b/Tests/RunCMake/Ninja/SubDirPrefix.cmake @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.5) +project(hello C) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin") +add_subdirectory(SubDirPrefix) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) +add_executable(hello hello_sub_greeting.c) +target_link_libraries(hello greeting) +include(CheckOutput.cmake) diff --git a/Tests/RunCMake/Ninja/SubDirPrefix/CMakeLists.txt b/Tests/RunCMake/Ninja/SubDirPrefix/CMakeLists.txt new file mode 100644 index 000000000..575597d48 --- /dev/null +++ b/Tests/RunCMake/Ninja/SubDirPrefix/CMakeLists.txt @@ -0,0 +1,2 @@ +add_library(greeting SHARED greeting.c) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/Tests/RunCMake/Ninja/SubDirPrefix/greeting.c b/Tests/RunCMake/Ninja/SubDirPrefix/greeting.c new file mode 100644 index 000000000..ba3e55beb --- /dev/null +++ b/Tests/RunCMake/Ninja/SubDirPrefix/greeting.c @@ -0,0 +1,9 @@ +#include + +#if defined(_WIN32) && !defined(GREETING_STATIC) +__declspec(dllexport) +#endif + void greeting(void) +{ + printf("Hello world!\n"); +} diff --git a/Tests/RunCMake/Ninja/SubDirPrefix/greeting.h b/Tests/RunCMake/Ninja/SubDirPrefix/greeting.h new file mode 100644 index 000000000..ee4053ef7 --- /dev/null +++ b/Tests/RunCMake/Ninja/SubDirPrefix/greeting.h @@ -0,0 +1,4 @@ +#if defined(_WIN32) && !defined(GREETING_STATIC) +__declspec(dllimport) +#endif + void greeting(void); diff --git a/Tests/RunCMake/Ninja/TwoLibs.cmake b/Tests/RunCMake/Ninja/TwoLibs.cmake new file mode 100644 index 000000000..666452f34 --- /dev/null +++ b/Tests/RunCMake/Ninja/TwoLibs.cmake @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.5) +project(hello C) + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib-static") +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") + +add_library(greeting SHARED greeting.c) +add_library(greeting2 STATIC greeting2.c) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) +add_executable(hello hello_with_two_greetings.c) +target_link_libraries(hello greeting greeting2) + +set(HELLO_OUTPUT_STRING "Hello world!\nHello world 2!\n") +include(CheckOutput.cmake) + +include(CheckNoPrefixSubDir.cmake) diff --git a/Tests/RunCMake/Ninja/greeting.c b/Tests/RunCMake/Ninja/greeting.c new file mode 100644 index 000000000..ba3e55beb --- /dev/null +++ b/Tests/RunCMake/Ninja/greeting.c @@ -0,0 +1,9 @@ +#include + +#if defined(_WIN32) && !defined(GREETING_STATIC) +__declspec(dllexport) +#endif + void greeting(void) +{ + printf("Hello world!\n"); +} diff --git a/Tests/RunCMake/Ninja/greeting.h b/Tests/RunCMake/Ninja/greeting.h new file mode 100644 index 000000000..ee4053ef7 --- /dev/null +++ b/Tests/RunCMake/Ninja/greeting.h @@ -0,0 +1,4 @@ +#if defined(_WIN32) && !defined(GREETING_STATIC) +__declspec(dllimport) +#endif + void greeting(void); diff --git a/Tests/RunCMake/Ninja/greeting2.c b/Tests/RunCMake/Ninja/greeting2.c new file mode 100644 index 000000000..c6ed87daa --- /dev/null +++ b/Tests/RunCMake/Ninja/greeting2.c @@ -0,0 +1,6 @@ +#include + +void greeting2(void) +{ + printf("Hello world 2!\n"); +} diff --git a/Tests/RunCMake/Ninja/greeting2.h b/Tests/RunCMake/Ninja/greeting2.h new file mode 100644 index 000000000..c4d8f9b29 --- /dev/null +++ b/Tests/RunCMake/Ninja/greeting2.h @@ -0,0 +1 @@ +void greeting2(void); diff --git a/Tests/RunCMake/Ninja/hello.c b/Tests/RunCMake/Ninja/hello.c new file mode 100644 index 000000000..aac8b4e0f --- /dev/null +++ b/Tests/RunCMake/Ninja/hello.c @@ -0,0 +1,7 @@ +#include + +int main(void) +{ + printf("Hello world!\n"); + return 0; +} diff --git a/Tests/RunCMake/Ninja/hello_sub_greeting.c b/Tests/RunCMake/Ninja/hello_sub_greeting.c new file mode 100644 index 000000000..fc4bd60f1 --- /dev/null +++ b/Tests/RunCMake/Ninja/hello_sub_greeting.c @@ -0,0 +1,7 @@ +#include + +int main(void) +{ + greeting(); + return 0; +} diff --git a/Tests/RunCMake/Ninja/hello_with_greeting.c b/Tests/RunCMake/Ninja/hello_with_greeting.c new file mode 100644 index 000000000..dbf90e314 --- /dev/null +++ b/Tests/RunCMake/Ninja/hello_with_greeting.c @@ -0,0 +1,7 @@ +#include + +int main(void) +{ + greeting(); + return 0; +} diff --git a/Tests/RunCMake/Ninja/hello_with_two_greetings.c b/Tests/RunCMake/Ninja/hello_with_two_greetings.c new file mode 100644 index 000000000..cbbdaadc0 --- /dev/null +++ b/Tests/RunCMake/Ninja/hello_with_two_greetings.c @@ -0,0 +1,9 @@ +#include +#include + +int main(void) +{ + greeting(); + greeting2(); + return 0; +} From eb076692459a8d4cc214156af3553c36309bdce3 Mon Sep 17 00:00:00 2001 From: Brad King Date: Wed, 18 May 2016 09:24:54 -0400 Subject: [PATCH 9/9] Tests: Select RunCMake.Ninja test cases based on ninja version Some test cases need features not available in Ninja < 1.6, so check the version before running them. --- Tests/RunCMake/Ninja/RunCMakeTest.cmake | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Tests/RunCMake/Ninja/RunCMakeTest.cmake b/Tests/RunCMake/Ninja/RunCMakeTest.cmake index 8160c2dd8..c73f852ae 100644 --- a/Tests/RunCMake/Ninja/RunCMakeTest.cmake +++ b/Tests/RunCMake/Ninja/RunCMakeTest.cmake @@ -1,5 +1,20 @@ include(RunCMake) +# Detect ninja version so we know what tests can be supported. +execute_process( + COMMAND "${RunCMake_MAKE_PROGRAM}" --version + OUTPUT_VARIABLE ninja_out + ERROR_VARIABLE ninja_out + RESULT_VARIABLE ninja_res + OUTPUT_STRIP_TRAILING_WHITESPACE + ) +if(ninja_res EQUAL 0 AND "x${ninja_out}" MATCHES "^x[0-9]+\\.[0-9]+") + set(ninja_version "${ninja_out}") + message(STATUS "ninja version: ${ninja_version}") +else() + message(FATAL_ERROR "'ninja --version' reported:\n${ninja_out}") +endif() + function(run_CMP0058 case) # Use a single build tree for a few tests without cleaning. set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CMP0058-${case}-build) @@ -169,6 +184,11 @@ build build.ninja: RERUN ${escaped_build_ninja_dep} || ${escaped_ninja_output_pa endfunction(run_sub_cmake) +if("${ninja_version}" VERSION_LESS 1.6) + message(WARNING "Ninja is too old; skipping rest of test.") + return() +endif() + foreach(ninja_output_path_prefix "sub space" "sub") run_sub_cmake(Executable "${ninja_output_path_prefix}") run_sub_cmake(StaticLib "${ninja_output_path_prefix}")