diff options
44 files changed, 1008 insertions, 815 deletions
diff --git a/Help/command/add_library.rst b/Help/command/add_library.rst index b42fe42..7274e44 100644 --- a/Help/command/add_library.rst +++ b/Help/command/add_library.rst @@ -94,6 +94,12 @@ The most important properties are: See documentation of the ``IMPORTED_*`` and ``INTERFACE_*`` properties for more information. +An ``UNKNOWN`` library type is typically only used in the implementation of +:ref:`Find Modules`. It allows the path to an imported library (often found +using the :command:`find_library` command) to be used without having to know +what type of library it is. This is especially useful on Windows where a +static library and a DLL's import library both have the same file extension. + Object Libraries ^^^^^^^^^^^^^^^^ diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst index f2e6597..c0449fb 100644 --- a/Help/manual/cmake-generator-expressions.7.rst +++ b/Help/manual/cmake-generator-expressions.7.rst @@ -110,24 +110,30 @@ Variable Queries The mapping in :prop_tgt:`MAP_IMPORTED_CONFIG_<CONFIG>` is also considered by this expression when it is evaluated on a property on an :prop_tgt:`IMPORTED` target. -``$<PLATFORM_ID:platform_id>`` - ``1`` if the CMake's platform id matches ``platform_id`` - otherwise ``0``. +``$<PLATFORM_ID:platform_ids>`` + where ``platform_ids`` is a comma-separated list. + ``1`` if the CMake's platform id matches any one of the entries in + ``platform_ids``, otherwise ``0``. See also the :variable:`CMAKE_SYSTEM_NAME` variable. -``$<C_COMPILER_ID:compiler_id>`` - ``1`` if the CMake's compiler id of the C compiler matches ``compiler_id``, - otherwise ``0``. +``$<C_COMPILER_ID:compiler_ids>`` + where ``compiler_ids`` is a comma-separated list. + ``1`` if the CMake's compiler id of the C compiler matches any one + of the entries in ``compiler_ids``, otherwise ``0``. See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable. -``$<CXX_COMPILER_ID:compiler_id>`` - ``1`` if the CMake's compiler id of the CXX compiler matches ``compiler_id``, - otherwise ``0``. -``$<CUDA_COMPILER_ID:compiler_id>`` - ``1`` if the CMake's compiler id of the CUDA compiler matches ``compiler_id``, - otherwise ``0``. +``$<CXX_COMPILER_ID:compiler_ids>`` + where ``compiler_ids`` is a comma-separated list. + ``1`` if the CMake's compiler id of the CXX compiler matches any one + of the entries in ``compiler_ids``, otherwise ``0``. See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable. -``$<Fortran_COMPILER_ID:compiler_id>`` - ``1`` if the CMake's compiler id of the Fortran compiler matches ``compiler_id``, - otherwise ``0``. +``$<CUDA_COMPILER_ID:compiler_ids>`` + where ``compiler_ids`` is a comma-separated list. + ``1`` if the CMake's compiler id of the CUDA compiler matches any one + of the entries in ``compiler_ids``, otherwise ``0``. + See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable. +``$<Fortran_COMPILER_ID:compiler_ids>`` + where ``compiler_ids`` is a comma-separated list. + ``1`` if the CMake's compiler id of the Fortran compiler matches any one + of the entries in ``compiler_ids``, otherwise ``0``. See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable. ``$<C_COMPILER_VERSION:version>`` ``1`` if the version of the C compiler matches ``version``, otherwise ``0``. @@ -158,20 +164,20 @@ Variable Queries .. _`Boolean COMPILE_LANGUAGE Generator Expression`: -``$<COMPILE_LANG_AND_ID:language,compiler_id>`` +``$<COMPILE_LANG_AND_ID:language,compiler_ids>`` ``1`` when the language used for compilation unit matches ``language`` and - the CMake's compiler id of the language compiler matches ``compiler_id``, - otherwise ``0``. This expression is a short form for the combination of - ``$<COMPILE_LANGUAGE:language>`` and ``$<LANG_COMPILER_ID:compiler_id>``. - This expression may be used to specify compile options, - compile definitions, and include directories for source files of a + the CMake's compiler id of the language compiler matches any one of the + entries in ``compiler_ids``, otherwise ``0``. This expression is a short form + for the combination of ``$<COMPILE_LANGUAGE:language>`` and + ``$<LANG_COMPILER_ID:compiler_ids>``. This expression may be used to specify + compile options, compile definitions, and include directories for source files of a particular language and compiler combination in a target. For example: .. code-block:: cmake add_executable(myapp main.cpp foo.c bar.cpp zot.cu) target_compile_definitions(myapp - PRIVATE $<$<COMPILE_LANG_AND_ID:CXX,Clang>:COMPILING_CXX_WITH_CLANG> + PRIVATE $<$<COMPILE_LANG_AND_ID:CXX,AppleClang,Clang>:COMPILING_CXX_WITH_CLANG> $<$<COMPILE_LANG_AND_ID:CXX,Intel>:COMPILING_CXX_WITH_INTEL> $<$<COMPILE_LANG_AND_ID:C,Clang>:COMPILING_C_WITH_CLANG> ) @@ -194,10 +200,10 @@ Variable Queries $<$<AND:$<COMPILE_LANGUAGE:C>,$<C_COMPILER_ID:Clang>>:COMPILING_C_WITH_CLANG> ) -``$<COMPILE_LANGUAGE:language>`` - ``1`` when the language used for compilation unit matches ``language``, - otherwise ``0``. This expression may be used to specify compile options, - compile definitions, and include directories for source files of a +``$<COMPILE_LANGUAGE:languages>`` + ``1`` when the language used for compilation unit matches any of the entries + in ``languages``, otherwise ``0``. This expression may be used to specify + compile options, compile definitions, and include directories for source files of a particular language in a target. For example: .. code-block:: cmake @@ -211,7 +217,7 @@ Variable Queries $<$<COMPILE_LANGUAGE:CUDA>:COMPILING_CUDA> ) target_include_directories(myapp - PRIVATE $<$<COMPILE_LANGUAGE:CXX>:/opt/foo/cxx_headers> + PRIVATE $<$<COMPILE_LANGUAGE:CXX,CUDA>:/opt/foo/headers> ) This specifies the use of the ``-fno-exceptions`` compile option, diff --git a/Help/prop_test/COST.rst b/Help/prop_test/COST.rst index 0c0fca7..9300d7b 100644 --- a/Help/prop_test/COST.rst +++ b/Help/prop_test/COST.rst @@ -1,8 +1,14 @@ COST ---- -Set this to a floating point value. Tests in a test set will be run -in descending order of cost. +This property describes the cost of a test. When parallel testing is +enabled, tests in the test set will be run in descending order of cost. +Projects can explicitly define the cost of a test by setting this property +to a floating point value. -This property describes the cost of a test. You can explicitly set -this value; tests with higher ``COST`` values will run first. +When the cost of a test is not defined by the project, +:manual:`ctest <ctest(1)>` will initially use a default cost of ``0``. +It computes a weighted average of the cost each time a test is run and +uses that as an improved estimate of the cost for the next run. The more +a test is re-run in the same build directory, the more representative the +cost should become. diff --git a/Help/release/dev/genex-comma-separated.rst b/Help/release/dev/genex-comma-separated.rst new file mode 100644 index 0000000..f27f781 --- /dev/null +++ b/Help/release/dev/genex-comma-separated.rst @@ -0,0 +1,8 @@ +genex-comma-separated +--------------------- + +* The :manual:`generator expressions <cmake-generator-expressions(7)>` + ``C_COMPILER_ID``, ``CXX_COMPILER_ID``, ``CUDA_COMPILER_ID``, + ``Fortran_COMPILER_ID``, ``COMPILE_LANGUAGE``, ``COMPILE_LANG_AND_ID``, and + ``PLATFORM_ID`` learned to support matching one value from a comma-separated + list. diff --git a/Modules/CheckLanguage.cmake b/Modules/CheckLanguage.cmake index d096849..a1a3a7a 100644 --- a/Modules/CheckLanguage.cmake +++ b/Modules/CheckLanguage.cmake @@ -18,7 +18,9 @@ such as ``Fortran``. If :variable:`CMAKE_<LANG>_COMPILER` is already defined the check does nothing. Otherwise it tries enabling the language in a test project. The result is cached in :variable:`CMAKE_<LANG>_COMPILER` as the compiler that was found, or ``NOTFOUND`` if the language cannot be -enabled. +enabled. For CUDA which can have an explicit host compiler, the cache +:variable:`CMAKE_CUDA_HOST_COMPILER` variable will be set if it was required +for compilation. Example: @@ -39,13 +41,23 @@ macro(check_language lang) set(_desc "Looking for a ${lang} compiler") message(STATUS ${_desc}) file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/Check${lang}) - file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/Check${lang}/CMakeLists.txt" + + set(extra_compiler_variables) + if(lang STREQUAL CUDA) + set(extra_compiler_variables "set(CMAKE_CUDA_HOST_COMPILER \\\"\${CMAKE_CUDA_HOST_COMPILER}\\\")") + endif() + + set(content "cmake_minimum_required(VERSION ${CMAKE_VERSION}) project(Check${lang} ${lang}) file(WRITE \"\${CMAKE_CURRENT_BINARY_DIR}/result.cmake\" \"set(CMAKE_${lang}_COMPILER \\\"\${CMAKE_${lang}_COMPILER}\\\")\\n\" - ) -") + \"${extra_compiler_variables}\\n\" + )" + ) + + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/Check${lang}/CMakeLists.txt" + "${content}") if(CMAKE_GENERATOR_INSTANCE) set(_D_CMAKE_GENERATOR_INSTANCE "-DCMAKE_GENERATOR_INSTANCE:INTERNAL=${CMAKE_GENERATOR_INSTANCE}") else() @@ -75,5 +87,12 @@ file(WRITE \"\${CMAKE_CURRENT_BINARY_DIR}/result.cmake\" message(STATUS "${_desc} - ${CMAKE_${lang}_COMPILER}") set(CMAKE_${lang}_COMPILER "${CMAKE_${lang}_COMPILER}" CACHE FILEPATH "${lang} compiler") mark_as_advanced(CMAKE_${lang}_COMPILER) + + if(CMAKE_${lang}_HOST_COMPILER) + message(STATUS "Looking for a ${lang} host compiler - ${CMAKE_${lang}_HOST_COMPILER}") + set(CMAKE_${lang}_HOST_COMPILER "${CMAKE_${lang}_HOST_COMPILER}" CACHE FILEPATH "${lang} host compiler") + mark_as_advanced(CMAKE_${lang}_HOST_COMPILER) + endif() + endif() endmacro() diff --git a/Modules/Compiler/NVIDIA-CUDA.cmake b/Modules/Compiler/NVIDIA-CUDA.cmake index de9dd99..c0ccb71 100644 --- a/Modules/Compiler/NVIDIA-CUDA.cmake +++ b/Modules/Compiler/NVIDIA-CUDA.cmake @@ -40,3 +40,8 @@ endif() set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_INCLUDES 0) set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_LIBRARIES 0) set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_OBJECTS 0) + +if (CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL "9.0") + set(CMAKE_CUDA_RESPONSE_FILE_LINK_FLAG "--options-file ") + set(CMAKE_CUDA_RESPONSE_FILE_FLAG "--options-file ") +endif() diff --git a/Modules/FindPython/Support.cmake b/Modules/FindPython/Support.cmake index 5d39240..49d8e26 100644 --- a/Modules/FindPython/Support.cmake +++ b/Modules/FindPython/Support.cmake @@ -1228,6 +1228,7 @@ if ("Development" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS find_library (${_PYTHON_PREFIX}_LIBRARY_RELEASE NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_CMAKE_PATH @@ -1338,6 +1339,7 @@ if ("Development" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS find_library (${_PYTHON_PREFIX}_LIBRARY_RELEASE NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_CMAKE_PATH diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index a1a4c22..aeccf8e 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake @@ -1,5 +1,5 @@ # CMake version number components. set(CMake_VERSION_MAJOR 3) set(CMake_VERSION_MINOR 14) -set(CMake_VERSION_PATCH 20190602) +set(CMake_VERSION_PATCH 20190604) #set(CMake_VERSION_RC 1) diff --git a/Source/cmGeneratorExpressionEvaluator.cxx b/Source/cmGeneratorExpressionEvaluator.cxx index 268de6f..7442018 100644 --- a/Source/cmGeneratorExpressionEvaluator.cxx +++ b/Source/cmGeneratorExpressionEvaluator.cxx @@ -166,9 +166,13 @@ std::string GeneratorExpressionContent::EvaluateParameters( reportError(context, this->GetOriginalExpression(), "$<" + identifier + "> expression requires at least one parameter."); - } - if (numExpected == cmGeneratorExpressionNode::OneOrZeroParameters && - parameters.size() > 1) { + } else if (numExpected == cmGeneratorExpressionNode::TwoOrMoreParameters && + parameters.size() < 2) { + reportError(context, this->GetOriginalExpression(), + "$<" + identifier + + "> expression requires at least two parameters."); + } else if (numExpected == cmGeneratorExpressionNode::OneOrZeroParameters && + parameters.size() > 1) { reportError(context, this->GetOriginalExpression(), "$<" + identifier + "> expression requires one or zero parameters."); diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx index 68ef170..a60c75c 100644 --- a/Source/cmGeneratorExpressionNode.cxx +++ b/Source/cmGeneratorExpressionNode.cxx @@ -632,7 +632,7 @@ struct CompilerIdNode : public cmGeneratorExpressionNode { } - int NumExpectedParameters() const override { return OneOrZeroParameters; } + int NumExpectedParameters() const override { return ZeroOrMoreParameters; } std::string Evaluate( const std::vector<std::string>& parameters, @@ -664,36 +664,39 @@ struct CompilerIdNode : public cmGeneratorExpressionNode if (parameters.empty()) { return compilerId; } - static cmsys::RegularExpression compilerIdValidator("^[A-Za-z0-9_]*$"); - if (!compilerIdValidator.find(parameters.front())) { - reportError(context, content->GetOriginalExpression(), - "Expression syntax not recognized."); - return std::string(); - } if (compilerId.empty()) { return parameters.front().empty() ? "1" : "0"; } + static cmsys::RegularExpression compilerIdValidator("^[A-Za-z0-9_]*$"); - if (strcmp(parameters.front().c_str(), compilerId.c_str()) == 0) { - return "1"; - } + for (auto& param : parameters) { - if (cmsysString_strcasecmp(parameters.front().c_str(), - compilerId.c_str()) == 0) { - switch (context->LG->GetPolicyStatus(cmPolicies::CMP0044)) { - case cmPolicies::WARN: { - std::ostringstream e; - e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0044); - context->LG->GetCMakeInstance()->IssueMessage( - MessageType::AUTHOR_WARNING, e.str(), context->Backtrace); - CM_FALLTHROUGH; + if (!compilerIdValidator.find(param)) { + reportError(context, content->GetOriginalExpression(), + "Expression syntax not recognized."); + return std::string(); + } + + if (strcmp(param.c_str(), compilerId.c_str()) == 0) { + return "1"; + } + + if (cmsysString_strcasecmp(param.c_str(), compilerId.c_str()) == 0) { + switch (context->LG->GetPolicyStatus(cmPolicies::CMP0044)) { + case cmPolicies::WARN: { + std::ostringstream e; + e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0044); + context->LG->GetCMakeInstance()->IssueMessage( + MessageType::AUTHOR_WARNING, e.str(), context->Backtrace); + CM_FALLTHROUGH; + } + case cmPolicies::OLD: + return "1"; + case cmPolicies::NEW: + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::REQUIRED_IF_USED: + break; } - case cmPolicies::OLD: - return "1"; - case cmPolicies::NEW: - case cmPolicies::REQUIRED_ALWAYS: - case cmPolicies::REQUIRED_IF_USED: - break; } } return "0"; @@ -773,7 +776,7 @@ struct PlatformIdNode : public cmGeneratorExpressionNode { PlatformIdNode() {} // NOLINT(modernize-use-equals-default) - int NumExpectedParameters() const override { return OneOrZeroParameters; } + int NumExpectedParameters() const override { return ZeroOrMoreParameters; } std::string Evaluate( const std::vector<std::string>& parameters, @@ -791,8 +794,10 @@ struct PlatformIdNode : public cmGeneratorExpressionNode return parameters.front().empty() ? "1" : "0"; } - if (parameters.front() == platformId) { - return "1"; + for (auto& param : parameters) { + if (param == platformId) { + return "1"; + } } return "0"; } @@ -946,7 +951,7 @@ static const struct CompileLanguageNode : public cmGeneratorExpressionNode { CompileLanguageNode() {} // NOLINT(modernize-use-equals-default) - int NumExpectedParameters() const override { return OneOrZeroParameters; } + int NumExpectedParameters() const override { return ZeroOrMoreParameters; } std::string Evaluate( const std::vector<std::string>& parameters, @@ -977,7 +982,13 @@ static const struct CompileLanguageNode : public cmGeneratorExpressionNode if (parameters.empty()) { return context->Language; } - return context->Language == parameters.front() ? "1" : "0"; + + for (auto& param : parameters) { + if (context->Language == param) { + return "1"; + } + } + return "0"; } } languageNode; @@ -985,7 +996,7 @@ static const struct CompileLanguageAndIdNode : public cmGeneratorExpressionNode { CompileLanguageAndIdNode() {} // NOLINT(modernize-use-equals-default) - int NumExpectedParameters() const override { return 2; } + int NumExpectedParameters() const override { return TwoOrMoreParameters; } std::string Evaluate( const std::vector<std::string>& parameters, @@ -1018,7 +1029,8 @@ static const struct CompileLanguageAndIdNode : public cmGeneratorExpressionNode const std::string& lang = context->Language; if (lang == parameters.front()) { - std::vector<std::string> idParameter = { parameters[1] }; + std::vector<std::string> idParameter((parameters.cbegin() + 1), + parameters.cend()); return CompilerIdNode{ lang.c_str() }.EvaluateWithLanguage( idParameter, context, content, dagChecker, lang); } diff --git a/Source/cmGeneratorExpressionNode.h b/Source/cmGeneratorExpressionNode.h index 3dbfc6e..7a36924 100644 --- a/Source/cmGeneratorExpressionNode.h +++ b/Source/cmGeneratorExpressionNode.h @@ -20,7 +20,9 @@ struct cmGeneratorExpressionNode { DynamicParameters = 0, OneOrMoreParameters = -1, - OneOrZeroParameters = -2 + TwoOrMoreParameters = -2, + ZeroOrMoreParameters = -3, + OneOrZeroParameters = -4 }; virtual ~cmGeneratorExpressionNode() = default; diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index 26886f5..df0f33f 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -331,6 +331,37 @@ bool cmGlobalGenerator::CheckTargetsForMissingSources() const return failed; } +bool cmGlobalGenerator::CheckTargetsForType() const +{ + if (!this->GetLanguageEnabled("Swift")) { + return false; + } + bool failed = false; + for (cmLocalGenerator* generator : this->LocalGenerators) { + for (cmGeneratorTarget* target : generator->GetGeneratorTargets()) { + std::vector<std::string> configs; + target->Makefile->GetConfigurations(configs); + if (configs.empty()) { + configs.emplace_back(); + } + + for (std::string const& config : configs) { + if (target->GetLinkerLanguage(config) == "Swift") { + if (target->GetPropertyAsBool("WIN32_EXECUTABLE")) { + this->GetCMakeInstance()->IssueMessage( + MessageType::FATAL_ERROR, + "WIN32_EXECUTABLE property is not supported on Swift " + "executables", + target->GetBacktrace()); + failed = true; + } + } + } + } + } + return failed; +} + bool cmGlobalGenerator::IsExportedTargetsFile( const std::string& filename) const { @@ -1414,6 +1445,10 @@ bool cmGlobalGenerator::Compute() return false; } + if (this->CheckTargetsForType()) { + return false; + } + for (cmLocalGenerator* localGen : this->LocalGenerators) { localGen->ComputeHomeRelativeOutputPath(); } diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index dcd8c5f..db96489 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -609,6 +609,7 @@ private: virtual void ForceLinkerLanguages(); bool CheckTargetsForMissingSources() const; + bool CheckTargetsForType() const; void CreateLocalGenerators(); diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index 3fce29e..2d52356 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -127,113 +127,107 @@ std::string cmGlobalNinjaGenerator::EncodePath(const std::string& path) return result; } -void cmGlobalNinjaGenerator::WriteBuild( - std::ostream& os, const std::string& comment, const std::string& rule, - const cmNinjaDeps& outputs, const cmNinjaDeps& implicitOuts, - const cmNinjaDeps& explicitDeps, const cmNinjaDeps& implicitDeps, - const cmNinjaDeps& orderOnlyDeps, const cmNinjaVars& variables, - const std::string& rspfile, int cmdLineLimit, bool* usedResponseFile) +void cmGlobalNinjaGenerator::WriteBuild(std::ostream& os, + cmNinjaBuild const& build, + int cmdLineLimit, + bool* usedResponseFile) { // Make sure there is a rule. - if (rule.empty()) { + if (build.Rule.empty()) { cmSystemTools::Error("No rule for WriteBuild! called with comment: " + - comment); + build.Comment); return; } // Make sure there is at least one output file. - if (outputs.empty()) { + if (build.Outputs.empty()) { cmSystemTools::Error( - "No output files for WriteBuild! called with comment: " + comment); + "No output files for WriteBuild! called with comment: " + build.Comment); return; } - cmGlobalNinjaGenerator::WriteComment(os, comment); - - std::string arguments; - - // TODO: Better formatting for when there are multiple input/output files. + cmGlobalNinjaGenerator::WriteComment(os, build.Comment); - // Write explicit dependencies. - for (std::string const& explicitDep : explicitDeps) { - arguments += " " + EncodePath(explicitDep); - } - - // Write implicit dependencies. - if (!implicitDeps.empty()) { - arguments += " |"; - for (std::string const& implicitDep : implicitDeps) { - arguments += " " + EncodePath(implicitDep); + // Write output files. + std::string buildStr("build"); + { + // Write explicit outputs + for (std::string const& output : build.Outputs) { + buildStr += " " + EncodePath(output); + if (this->ComputingUnknownDependencies) { + this->CombinedBuildOutputs.insert(output); + } } - } - - // Write order-only dependencies. - if (!orderOnlyDeps.empty()) { - arguments += " ||"; - for (std::string const& orderOnlyDep : orderOnlyDeps) { - arguments += " " + EncodePath(orderOnlyDep); + // Write implicit outputs + if (!build.ImplicitOuts.empty()) { + buildStr += " |"; + for (std::string const& implicitOut : build.ImplicitOuts) { + buildStr += " " + EncodePath(implicitOut); + } } + buildStr += ":"; + + // Write the rule. + buildStr += " "; + buildStr += build.Rule; } - arguments += "\n"; + std::string arguments; + { + // TODO: Better formatting for when there are multiple input/output files. - std::string build; + // Write explicit dependencies. + for (std::string const& explicitDep : build.ExplicitDeps) { + arguments += " " + EncodePath(explicitDep); + } - // Write outputs files. - build += "build"; - for (std::string const& output : outputs) { - build += " " + EncodePath(output); - if (this->ComputingUnknownDependencies) { - this->CombinedBuildOutputs.insert(output); + // Write implicit dependencies. + if (!build.ImplicitDeps.empty()) { + arguments += " |"; + for (std::string const& implicitDep : build.ImplicitDeps) { + arguments += " " + EncodePath(implicitDep); + } } - } - if (!implicitOuts.empty()) { - build += " |"; - for (std::string const& implicitOut : implicitOuts) { - build += " " + EncodePath(implicitOut); + + // Write order-only dependencies. + if (!build.OrderOnlyDeps.empty()) { + arguments += " ||"; + for (std::string const& orderOnlyDep : build.OrderOnlyDeps) { + arguments += " " + EncodePath(orderOnlyDep); + } } - } - build += ":"; - // Write the rule. - build += " " + rule; + arguments += "\n"; + } // Write the variables bound to this build statement. - std::ostringstream variable_assignments; - for (auto const& variable : variables) { - cmGlobalNinjaGenerator::WriteVariable(variable_assignments, variable.first, - variable.second, "", 1); - } + std::string assignments; + { + std::ostringstream variable_assignments; + for (auto const& variable : build.Variables) { + cmGlobalNinjaGenerator::WriteVariable( + variable_assignments, variable.first, variable.second, "", 1); + } - // check if a response file rule should be used - std::string buildstr = build; - std::string assignments = variable_assignments.str(); - bool useResponseFile = false; - if (cmdLineLimit < 0 || - (cmdLineLimit > 0 && - (arguments.size() + buildstr.size() + assignments.size() + 1000) > - static_cast<size_t>(cmdLineLimit))) { - variable_assignments.str(std::string()); - cmGlobalNinjaGenerator::WriteVariable(variable_assignments, "RSP_FILE", - rspfile, "", 1); - assignments += variable_assignments.str(); - useResponseFile = true; - } - if (usedResponseFile) { - *usedResponseFile = useResponseFile; + // check if a response file rule should be used + assignments = variable_assignments.str(); + bool useResponseFile = false; + if (cmdLineLimit < 0 || + (cmdLineLimit > 0 && + (arguments.size() + buildStr.size() + assignments.size() + 1000) > + static_cast<size_t>(cmdLineLimit))) { + variable_assignments.str(std::string()); + cmGlobalNinjaGenerator::WriteVariable(variable_assignments, "RSP_FILE", + build.RspFile, "", 1); + assignments += variable_assignments.str(); + useResponseFile = true; + } + if (usedResponseFile) { + *usedResponseFile = useResponseFile; + } } - os << buildstr << arguments << assignments; -} - -void cmGlobalNinjaGenerator::WritePhonyBuild( - std::ostream& os, const std::string& comment, const cmNinjaDeps& outputs, - const cmNinjaDeps& explicitDeps, const cmNinjaDeps& implicitDeps, - const cmNinjaDeps& orderOnlyDeps, const cmNinjaVars& variables) -{ - this->WriteBuild(os, comment, "phony", outputs, - /*implicitOuts=*/cmNinjaDeps(), explicitDeps, implicitDeps, - orderOnlyDeps, variables); + os << buildStr << arguments << assignments << "\n"; } void cmGlobalNinjaGenerator::AddCustomCommandRule() @@ -249,40 +243,47 @@ void cmGlobalNinjaGenerator::WriteCustomCommandBuild( const std::string& command, const std::string& description, const std::string& comment, const std::string& depfile, const std::string& job_pool, bool uses_terminal, bool restat, - const cmNinjaDeps& outputs, const cmNinjaDeps& deps, - const cmNinjaDeps& orderOnly) + const cmNinjaDeps& outputs, const cmNinjaDeps& explicitDeps, + const cmNinjaDeps& orderOnlyDeps) { - std::string cmd = command; // NOLINT(*) -#ifdef _WIN32 - if (cmd.empty()) - // TODO Shouldn't an empty command be handled by ninja? - cmd = "cmd.exe /c"; -#endif - this->AddCustomCommandRule(); - cmNinjaVars vars; - vars["COMMAND"] = cmd; - vars["DESC"] = EncodeLiteral(description); - if (restat) { - vars["restat"] = "1"; - } - if (uses_terminal && SupportsConsolePool()) { - vars["pool"] = "console"; - } else if (!job_pool.empty()) { - vars["pool"] = job_pool; - } - if (!depfile.empty()) { - vars["depfile"] = depfile; + { + cmNinjaBuild build("CUSTOM_COMMAND"); + build.Comment = comment; + build.Outputs = outputs; + build.ExplicitDeps = explicitDeps; + build.OrderOnlyDeps = orderOnlyDeps; + + cmNinjaVars& vars = build.Variables; + { + std::string cmd = command; // NOLINT(*) +#ifdef _WIN32 + if (cmd.empty()) + // TODO Shouldn't an empty command be handled by ninja? + cmd = "cmd.exe /c"; +#endif + vars["COMMAND"] = std::move(cmd); + } + vars["DESC"] = EncodeLiteral(description); + if (restat) { + vars["restat"] = "1"; + } + if (uses_terminal && SupportsConsolePool()) { + vars["pool"] = "console"; + } else if (!job_pool.empty()) { + vars["pool"] = job_pool; + } + if (!depfile.empty()) { + vars["depfile"] = depfile; + } + this->WriteBuild(*this->BuildFileStream, build); } - this->WriteBuild(*this->BuildFileStream, comment, "CUSTOM_COMMAND", outputs, - /*implicitOuts=*/cmNinjaDeps(), deps, cmNinjaDeps(), - orderOnly, vars); if (this->ComputingUnknownDependencies) { // we need to track every dependency that comes in, since we are trying // to find dependencies that are side effects of build commands - for (std::string const& dep : deps) { + for (std::string const& dep : explicitDeps) { this->CombinedCustomCommandExplicitDependencies.insert(dep); } } @@ -297,20 +298,16 @@ void cmGlobalNinjaGenerator::AddMacOSXContentRule() this->AddRule(rule); } -void cmGlobalNinjaGenerator::WriteMacOSXContentBuild(const std::string& input, - const std::string& output) +void cmGlobalNinjaGenerator::WriteMacOSXContentBuild(std::string input, + std::string output) { this->AddMacOSXContentRule(); - - cmNinjaDeps outputs; - outputs.push_back(output); - cmNinjaDeps deps; - deps.push_back(input); - cmNinjaVars vars; - - this->WriteBuild(*this->BuildFileStream, "", "COPY_OSX_CONTENT", outputs, - /*implicitOuts=*/cmNinjaDeps(), deps, cmNinjaDeps(), - cmNinjaDeps(), cmNinjaVars()); + { + cmNinjaBuild build("COPY_OSX_CONTENT"); + build.Outputs.push_back(std::move(output)); + build.ExplicitDeps.push_back(std::move(input)); + this->WriteBuild(*this->BuildFileStream, build); + } } void cmGlobalNinjaGenerator::WriteRule(std::ostream& os, @@ -1083,6 +1080,8 @@ void cmGlobalNinjaGenerator::WriteTargetAliases(std::ostream& os) cmGlobalNinjaGenerator::WriteDivider(os); os << "# Target aliases.\n\n"; + cmNinjaBuild build("phony"); + build.Outputs.emplace_back(""); for (auto const& ta : TargetAliases) { // Don't write ambiguous aliases. if (!ta.second) { @@ -1095,10 +1094,13 @@ void cmGlobalNinjaGenerator::WriteTargetAliases(std::ostream& os) continue; } - cmNinjaDeps deps; - this->AppendTargetOutputs(ta.second, deps); - - this->WritePhonyBuild(os, "", cmNinjaDeps(1, ta.first), deps); + // Outputs + build.Outputs[0] = ta.first; + // Explicit depdendencies + build.ExplicitDeps.clear(); + this->AppendTargetOutputs(ta.second, build.ExplicitDeps); + // Write + this->WriteBuild(os, build); } } @@ -1107,13 +1109,22 @@ void cmGlobalNinjaGenerator::WriteFolderTargets(std::ostream& os) cmGlobalNinjaGenerator::WriteDivider(os); os << "# Folder targets.\n\n"; + std::string const& rootBinaryDir = + this->LocalGenerators[0]->GetBinaryDirectory(); + std::map<std::string, cmNinjaDeps> targetsPerFolder; for (cmLocalGenerator const* lg : this->LocalGenerators) { - const std::string currentBinaryFolder( + std::string const& currentBinaryFolder( lg->GetStateSnapshot().GetDirectory().GetCurrentBinary()); + + // Do not generate a rule for the root binary dir. + if (currentBinaryFolder == rootBinaryDir) { + continue; + } + // The directory-level rule should depend on the target-level rules // for all targets in the directory. - targetsPerFolder[currentBinaryFolder] = cmNinjaDeps(); + cmNinjaDeps& folderTargets = targetsPerFolder[currentBinaryFolder]; for (auto gt : lg->GetGeneratorTargets()) { cmStateEnums::TargetType const type = gt->GetType(); if ((type == cmStateEnums::EXECUTABLE || @@ -1123,37 +1134,34 @@ void cmGlobalNinjaGenerator::WriteFolderTargets(std::ostream& os) type == cmStateEnums::OBJECT_LIBRARY || type == cmStateEnums::UTILITY) && !gt->GetPropertyAsBool("EXCLUDE_FROM_ALL")) { - targetsPerFolder[currentBinaryFolder].push_back(gt->GetName()); + folderTargets.push_back(gt->GetName()); } } // The directory-level rule should depend on the directory-level // rules of the subdirectories. for (cmStateSnapshot const& state : lg->GetStateSnapshot().GetChildren()) { - std::string const currentBinaryDir = + std::string const& currentBinaryDir = state.GetDirectory().GetCurrentBinary(); - - targetsPerFolder[currentBinaryFolder].push_back( + folderTargets.push_back( this->ConvertToNinjaPath(currentBinaryDir + "/all")); } } - std::string const rootBinaryDir = - this->LocalGenerators[0]->GetBinaryDirectory(); - for (auto const& it : targetsPerFolder) { - cmGlobalNinjaGenerator::WriteDivider(os); - std::string const& currentBinaryDir = it.first; + if (!targetsPerFolder.empty()) { + cmNinjaBuild build("phony"); + build.Outputs.emplace_back(""); + for (auto& it : targetsPerFolder) { + cmGlobalNinjaGenerator::WriteDivider(os); + std::string const& currentBinaryDir = it.first; - // Do not generate a rule for the root binary dir. - if (rootBinaryDir.length() >= currentBinaryDir.length()) { - continue; + // Setup target + build.Comment = "Folder: " + currentBinaryDir; + build.Outputs[0] = this->ConvertToNinjaPath(currentBinaryDir + "/all"); + build.ExplicitDeps = std::move(it.second); + // Write target + this->WriteBuild(os, build); } - - std::string const comment = "Folder: " + currentBinaryDir; - cmNinjaDeps output(1); - output.push_back(this->ConvertToNinjaPath(currentBinaryDir + "/all")); - - this->WritePhonyBuild(os, comment, output, it.second); } } @@ -1234,23 +1242,26 @@ void cmGlobalNinjaGenerator::WriteUnknownExplicitDependencies(std::ostream& os) knownDependencies.begin(), knownDependencies.end(), std::back_inserter(unknownExplicitDepends)); - std::string const rootBuildDirectory = - this->GetCMakeInstance()->GetHomeOutputDirectory(); - bool const inSourceBuild = - (rootBuildDirectory == this->GetCMakeInstance()->GetHomeDirectory()); std::vector<std::string> warnExplicitDepends; - for (std::string const& i : unknownExplicitDepends) { - // verify the file is in the build directory - std::string const absDepPath = - cmSystemTools::CollapseFullPath(i, rootBuildDirectory); - bool const inBuildDir = - cmSystemTools::IsSubDirectory(absDepPath, rootBuildDirectory); - if (inBuildDir) { - cmNinjaDeps deps(1, i); - this->WritePhonyBuild(os, "", deps, cmNinjaDeps()); - if (this->PolicyCMP0058 == cmPolicies::WARN && !inSourceBuild && - warnExplicitDepends.size() < 10) { - warnExplicitDepends.push_back(i); + if (!unknownExplicitDepends.empty()) { + cmake* cmk = this->GetCMakeInstance(); + std::string const& buildRoot = cmk->GetHomeOutputDirectory(); + bool const inSource = (buildRoot == cmk->GetHomeDirectory()); + bool const warn = (!inSource && (this->PolicyCMP0058 == cmPolicies::WARN)); + cmNinjaBuild build("phony"); + build.Outputs.emplace_back(""); + for (std::string const& ued : unknownExplicitDepends) { + // verify the file is in the build directory + std::string const absDepPath = + cmSystemTools::CollapseFullPath(ued, buildRoot); + if (cmSystemTools::IsSubDirectory(absDepPath, buildRoot)) { + // Generate phony build statement + build.Outputs[0] = ued; + this->WriteBuild(os, build); + // Add to warning on demand + if (warn && warnExplicitDepends.size() < 10) { + warnExplicitDepends.push_back(ued); + } } } } @@ -1291,14 +1302,14 @@ void cmGlobalNinjaGenerator::WriteBuiltinTargets(std::ostream& os) void cmGlobalNinjaGenerator::WriteTargetAll(std::ostream& os) { - cmNinjaDeps outputs; - outputs.push_back(this->TargetAll); - - this->WritePhonyBuild(os, "The main all target.", outputs, - this->AllDependencies); + cmNinjaBuild build("phony"); + build.Comment = "The main all target."; + build.Outputs.push_back(this->TargetAll); + build.ExplicitDeps = this->AllDependencies; + this->WriteBuild(os, build); if (!this->HasOutputPathPrefix()) { - cmGlobalNinjaGenerator::WriteDefault(os, outputs, + cmGlobalNinjaGenerator::WriteDefault(os, build.Outputs, "Make the all target the default."); } } @@ -1325,20 +1336,21 @@ void cmGlobalNinjaGenerator::WriteTargetRebuildManifest(std::ostream& os) WriteRule(*this->RulesFileStream, rule); } - cmNinjaDeps implicitDeps; - cmNinjaDeps explicitDeps; + cmNinjaBuild reBuild("RERUN_CMAKE"); + reBuild.Comment = "Re-run CMake if any of its inputs changed."; + reBuild.Outputs.push_back(this->NinjaOutputPath(NINJA_BUILD_FILE)); + for (cmLocalGenerator* localGen : this->LocalGenerators) { for (std::string const& fi : localGen->GetMakefile()->GetListFiles()) { - implicitDeps.push_back(this->ConvertToNinjaPath(fi)); + reBuild.ImplicitDeps.push_back(this->ConvertToNinjaPath(fi)); } } - implicitDeps.push_back(this->CMakeCacheFile); + reBuild.ImplicitDeps.push_back(this->CMakeCacheFile); - cmNinjaVars variables; // Use 'console' pool to get non buffered output of the CMake re-run call // Available since Ninja 1.5 if (SupportsConsolePool()) { - variables["pool"] = "console"; + reBuild.Variables["pool"] = "console"; } cmake* cm = this->GetCMakeInstance(); @@ -1355,29 +1367,28 @@ void cmGlobalNinjaGenerator::WriteTargetRebuildManifest(std::ostream& os) this->WriteRule(*this->RulesFileStream, rule); } - std::string verifyForce = cm->GetGlobVerifyScript() + "_force"; - cmNinjaDeps verifyForceDeps(1, this->NinjaOutputPath(verifyForce)); - - this->WritePhonyBuild(os, "Phony target to force glob verification run.", - verifyForceDeps, cmNinjaDeps()); + cmNinjaBuild phonyBuild("phony"); + phonyBuild.Comment = "Phony target to force glob verification run."; + phonyBuild.Outputs.push_back(cm->GetGlobVerifyScript() + "_force"); + this->WriteBuild(os, phonyBuild); - variables["restat"] = "1"; + reBuild.Variables["restat"] = "1"; std::string const verifyScriptFile = this->NinjaOutputPath(cm->GetGlobVerifyScript()); std::string const verifyStampFile = this->NinjaOutputPath(cm->GetGlobVerifyStamp()); - this->WriteBuild(os, - "Re-run CMake to check if globbed directories changed.", - "VERIFY_GLOBS", - /*outputs=*/cmNinjaDeps(1, verifyStampFile), - /*implicitOuts=*/cmNinjaDeps(), - /*explicitDeps=*/cmNinjaDeps(), - /*implicitDeps=*/verifyForceDeps, - /*orderOnlyDeps=*/cmNinjaDeps(), variables); - - variables.erase("restat"); - implicitDeps.push_back(verifyScriptFile); - explicitDeps.push_back(verifyStampFile); + { + cmNinjaBuild vgBuild("VERIFY_GLOBS"); + vgBuild.Comment = + "Re-run CMake to check if globbed directories changed."; + vgBuild.Outputs.push_back(verifyStampFile); + vgBuild.ImplicitDeps = phonyBuild.Outputs; + vgBuild.Variables = reBuild.Variables; + this->WriteBuild(os, vgBuild); + } + reBuild.Variables.erase("restat"); + reBuild.ImplicitDeps.push_back(verifyScriptFile); + reBuild.ExplicitDeps.push_back(verifyStampFile); } else if (!this->SupportsManifestRestat() && cm->DoWriteGlobVerifyTarget()) { std::ostringstream msg; @@ -1395,25 +1406,23 @@ void cmGlobalNinjaGenerator::WriteTargetRebuildManifest(std::ostream& os) msg.str()); } - std::sort(implicitDeps.begin(), implicitDeps.end()); - implicitDeps.erase(std::unique(implicitDeps.begin(), implicitDeps.end()), - implicitDeps.end()); - - 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, ninjaBuildFile), - /*implicitOuts=*/cmNinjaDeps(), explicitDeps, implicitDeps, - /*orderOnlyDeps=*/cmNinjaDeps(), variables); + std::sort(reBuild.ImplicitDeps.begin(), reBuild.ImplicitDeps.end()); + reBuild.ImplicitDeps.erase( + std::unique(reBuild.ImplicitDeps.begin(), reBuild.ImplicitDeps.end()), + reBuild.ImplicitDeps.end()); - cmNinjaDeps missingInputs; - std::set_difference(std::make_move_iterator(implicitDeps.begin()), - std::make_move_iterator(implicitDeps.end()), - CustomCommandOutputs.begin(), CustomCommandOutputs.end(), - std::back_inserter(missingInputs)); + this->WriteBuild(os, reBuild); - this->WritePhonyBuild(os, "A missing CMake input file is not an error.", - missingInputs, cmNinjaDeps()); + { + cmNinjaBuild build("phony"); + build.Comment = "A missing CMake input file is not an error."; + std::set_difference(std::make_move_iterator(reBuild.ImplicitDeps.begin()), + std::make_move_iterator(reBuild.ImplicitDeps.end()), + CustomCommandOutputs.begin(), + CustomCommandOutputs.end(), + std::back_inserter(build.Outputs)); + this->WriteBuild(os, build); + } } std::string cmGlobalNinjaGenerator::CMakeCmd() const @@ -1500,16 +1509,11 @@ bool cmGlobalNinjaGenerator::WriteTargetCleanAdditional(std::ostream& os) // Write build { - cmNinjaDeps outputs; - outputs.emplace_back( + cmNinjaBuild build("CLEAN_ADDITIONAL"); + build.Comment = "Clean additional files."; + build.Outputs.push_back( this->NinjaOutputPath(this->GetAdditionalCleanTargetName())); - WriteBuild(os, "Clean additional files.", "CLEAN_ADDITIONAL", - /*outputs=*/outputs, - /*implicitOuts=*/cmNinjaDeps(), - /*explicitDeps=*/cmNinjaDeps(), - /*implicitDeps=*/cmNinjaDeps(), - /*orderOnlyDeps=*/cmNinjaDeps(), - /*variables=*/cmNinjaVars()); + WriteBuild(os, build); } // Return success return true; @@ -1532,20 +1536,14 @@ void cmGlobalNinjaGenerator::WriteTargetClean(std::ostream& os) // Write build { - cmNinjaDeps explicitDeps; + cmNinjaBuild build("CLEAN"); + build.Comment = "Clean all the built files."; + build.Outputs.push_back(this->NinjaOutputPath(this->GetCleanTargetName())); if (additionalFiles) { - explicitDeps.emplace_back( + build.ExplicitDeps.push_back( this->NinjaOutputPath(this->GetAdditionalCleanTargetName())); } - cmNinjaDeps outputs; - outputs.emplace_back(this->NinjaOutputPath(this->GetCleanTargetName())); - WriteBuild(os, "Clean all the built files.", "CLEAN", - /*outputs=*/outputs, - /*implicitOuts=*/cmNinjaDeps(), - /*explicitDeps=*/explicitDeps, - /*implicitDeps=*/cmNinjaDeps(), - /*orderOnlyDeps=*/cmNinjaDeps(), - /*variables=*/cmNinjaVars()); + WriteBuild(os, build); } } @@ -1558,13 +1556,12 @@ void cmGlobalNinjaGenerator::WriteTargetHelp(std::ostream& os) rule.Comment = "Rule for printing all primary targets available."; WriteRule(*this->RulesFileStream, rule); } - WriteBuild(os, "Print all primary targets available.", "HELP", - /*outputs=*/cmNinjaDeps(1, this->NinjaOutputPath("help")), - /*implicitOuts=*/cmNinjaDeps(), - /*explicitDeps=*/cmNinjaDeps(), - /*implicitDeps=*/cmNinjaDeps(), - /*orderOnlyDeps=*/cmNinjaDeps(), - /*variables=*/cmNinjaVars()); + { + cmNinjaBuild build("HELP"); + build.Comment = "Print all primary targets available."; + build.Outputs.push_back(this->NinjaOutputPath("help")); + WriteBuild(os, build); + } } void cmGlobalNinjaGenerator::InitOutputPathPrefix() @@ -1937,32 +1934,29 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( cmGeneratedFileStream ddf(arg_dd); ddf << "ninja_dyndep_version = 1.0\n"; - for (cmDyndepObjectInfo const& object : objects) { - std::string const ddComment; - std::string const ddRule = "dyndep"; - cmNinjaDeps ddOutputs; - cmNinjaDeps ddImplicitOuts; - cmNinjaDeps ddExplicitDeps; - cmNinjaDeps ddImplicitDeps; - cmNinjaDeps ddOrderOnlyDeps; - cmNinjaVars ddVars; - - ddOutputs.push_back(object.Object); - for (std::string const& p : object.Provides) { - ddImplicitOuts.push_back(this->ConvertToNinjaPath(mod_files[p])); - } - for (std::string const& r : object.Requires) { - std::map<std::string, std::string>::iterator m = mod_files.find(r); - if (m != mod_files.end()) { - ddImplicitDeps.push_back(this->ConvertToNinjaPath(m->second)); + { + cmNinjaBuild build("dyndep"); + build.Outputs.emplace_back(""); + for (cmDyndepObjectInfo const& object : objects) { + build.Outputs[0] = object.Object; + build.ImplicitOuts.clear(); + for (std::string const& p : object.Provides) { + build.ImplicitOuts.push_back(this->ConvertToNinjaPath(mod_files[p])); + } + build.ImplicitDeps.clear(); + for (std::string const& r : object.Requires) { + auto mit = mod_files.find(r); + if (mit != mod_files.end()) { + build.ImplicitDeps.push_back(this->ConvertToNinjaPath(mit->second)); + } + } + build.Variables.clear(); + if (!object.Provides.empty()) { + build.Variables.emplace("restat", "1"); } - } - if (!object.Provides.empty()) { - ddVars["restat"] = "1"; - } - this->WriteBuild(ddf, ddComment, ddRule, ddOutputs, ddImplicitOuts, - ddExplicitDeps, ddImplicitDeps, ddOrderOnlyDeps, ddVars); + this->WriteBuild(ddf, build); + } } // Store the map of modules provided by this target in a file for diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h index dcc358b..15dd404 100644 --- a/Source/cmGlobalNinjaGenerator.h +++ b/Source/cmGlobalNinjaGenerator.h @@ -101,41 +101,21 @@ public: bool IsIPOSupported() const override { return true; } /** - * Write a build statement to @a os with the @a comment using - * the @a rule the list of @a outputs files and inputs. - * It also writes the variables bound to this build statement. + * Write a build statement @a build to @a os. * @warning no escaping of any kind is done here. */ - void WriteBuild(std::ostream& os, const std::string& comment, - const std::string& rule, const cmNinjaDeps& outputs, - const cmNinjaDeps& implicitOuts, - const cmNinjaDeps& explicitDeps, - const cmNinjaDeps& implicitDeps, - const cmNinjaDeps& orderOnlyDeps, - const cmNinjaVars& variables, - const std::string& rspfile = std::string(), + void WriteBuild(std::ostream& os, cmNinjaBuild const& build, int cmdLineLimit = 0, bool* usedResponseFile = nullptr); - /** - * Helper to write a build statement with the special 'phony' rule. - */ - void WritePhonyBuild(std::ostream& os, const std::string& comment, - const cmNinjaDeps& outputs, - const cmNinjaDeps& explicitDeps, - const cmNinjaDeps& implicitDeps = cmNinjaDeps(), - const cmNinjaDeps& orderOnlyDeps = cmNinjaDeps(), - const cmNinjaVars& variables = cmNinjaVars()); - - void WriteCustomCommandBuild(const std::string& command, - const std::string& description, - const std::string& comment, - const std::string& depfile, - const std::string& pool, bool uses_terminal, - bool restat, const cmNinjaDeps& outputs, - const cmNinjaDeps& deps = cmNinjaDeps(), - const cmNinjaDeps& orderOnly = cmNinjaDeps()); - void WriteMacOSXContentBuild(const std::string& input, - const std::string& output); + void WriteCustomCommandBuild( + const std::string& command, const std::string& description, + const std::string& comment, const std::string& depfile, + const std::string& pool, bool uses_terminal, bool restat, + const cmNinjaDeps& outputs, + const cmNinjaDeps& explicitDeps = cmNinjaDeps(), + const cmNinjaDeps& orderOnlyDeps = cmNinjaDeps()); + + void WriteMacOSXContentBuild(std::string input, std::string output); /** * Write a rule statement to @a os. diff --git a/Source/cmListFileCache.cxx b/Source/cmListFileCache.cxx index f99caed..df0d00c 100644 --- a/Source/cmListFileCache.cxx +++ b/Source/cmListFileCache.cxx @@ -192,12 +192,9 @@ bool cmListFileParser::ParseFunction(const char* name, long line) } // Arguments. - unsigned long lastLine; unsigned long parenDepth = 0; this->Separation = SeparationOkay; - while ( - (static_cast<void>(lastLine = cmListFileLexer_GetCurrentLine(this->Lexer)), - token = cmListFileLexer_Scan(this->Lexer))) { + while ((token = cmListFileLexer_Scan(this->Lexer))) { if (token->type == cmListFileLexer_Token_Space || token->type == cmListFileLexer_Token_Newline) { this->Separation = SeparationOkay; @@ -252,7 +249,7 @@ bool cmListFileParser::ParseFunction(const char* name, long line) std::ostringstream error; cmListFileContext lfc; lfc.FilePath = this->FileName; - lfc.Line = lastLine; + lfc.Line = line; cmListFileBacktrace lfbt = this->Backtrace; lfbt = lfbt.Push(lfc); error << "Parse error. Function missing ending \")\". " diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index 87d2232..fe5c8af 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -1169,30 +1169,34 @@ void cmLocalGenerator::GetTargetFlags( libraryLinkVariable = "CMAKE_MODULE_LINKER_FLAGS"; CM_FALLTHROUGH; case cmStateEnums::SHARED_LIBRARY: { - linkFlags = this->Makefile->GetSafeDefinition(libraryLinkVariable); - linkFlags += " "; - if (!buildType.empty()) { - std::string build = libraryLinkVariable; - build += "_"; - build += buildType; - linkFlags += this->Makefile->GetSafeDefinition(build); + if (linkLanguage != "Swift") { + linkFlags = this->Makefile->GetSafeDefinition(libraryLinkVariable); linkFlags += " "; - } - if (this->Makefile->IsOn("WIN32") && - !(this->Makefile->IsOn("CYGWIN") || this->Makefile->IsOn("MINGW"))) { - std::vector<cmSourceFile*> sources; - target->GetSourceFiles(sources, buildType); - std::string defFlag = - this->Makefile->GetSafeDefinition("CMAKE_LINK_DEF_FILE_FLAG"); - for (cmSourceFile* sf : sources) { - if (sf->GetExtension() == "def") { - linkFlags += defFlag; - linkFlags += this->ConvertToOutputFormat( - cmSystemTools::CollapseFullPath(sf->GetFullPath()), SHELL); - linkFlags += " "; + if (!buildType.empty()) { + std::string build = libraryLinkVariable; + build += "_"; + build += buildType; + linkFlags += this->Makefile->GetSafeDefinition(build); + linkFlags += " "; + } + if (this->Makefile->IsOn("WIN32") && + !(this->Makefile->IsOn("CYGWIN") || + this->Makefile->IsOn("MINGW"))) { + std::vector<cmSourceFile*> sources; + target->GetSourceFiles(sources, buildType); + std::string defFlag = + this->Makefile->GetSafeDefinition("CMAKE_LINK_DEF_FILE_FLAG"); + for (cmSourceFile* sf : sources) { + if (sf->GetExtension() == "def") { + linkFlags += defFlag; + linkFlags += this->ConvertToOutputFormat( + cmSystemTools::CollapseFullPath(sf->GetFullPath()), SHELL); + linkFlags += " "; + } } } } + const char* targetLinkFlags = target->GetProperty("LINK_FLAGS"); if (targetLinkFlags) { linkFlags += targetLinkFlags; @@ -1207,6 +1211,7 @@ void cmLocalGenerator::GetTargetFlags( linkFlags += " "; } } + std::vector<std::string> opts; target->GetLinkOptions(opts, config, linkLanguage); // LINK_OPTIONS are escaped. @@ -1217,25 +1222,49 @@ void cmLocalGenerator::GetTargetFlags( } } break; case cmStateEnums::EXECUTABLE: { - linkFlags += this->Makefile->GetSafeDefinition("CMAKE_EXE_LINKER_FLAGS"); - linkFlags += " "; - if (!buildType.empty()) { - std::string build = "CMAKE_EXE_LINKER_FLAGS_"; - build += buildType; - linkFlags += this->Makefile->GetSafeDefinition(build); + if (linkLanguage != "Swift") { + linkFlags += + this->Makefile->GetSafeDefinition("CMAKE_EXE_LINKER_FLAGS"); linkFlags += " "; + if (!buildType.empty()) { + std::string build = "CMAKE_EXE_LINKER_FLAGS_"; + build += buildType; + linkFlags += this->Makefile->GetSafeDefinition(build); + linkFlags += " "; + } + if (linkLanguage.empty()) { + cmSystemTools::Error( + "CMake can not determine linker language for target: " + + target->GetName()); + return; + } + + if (target->GetPropertyAsBool("WIN32_EXECUTABLE")) { + linkFlags += + this->Makefile->GetSafeDefinition("CMAKE_CREATE_WIN32_EXE"); + linkFlags += " "; + } else { + linkFlags += + this->Makefile->GetSafeDefinition("CMAKE_CREATE_CONSOLE_EXE"); + linkFlags += " "; + } + + if (target->IsExecutableWithExports()) { + std::string exportFlagVar = "CMAKE_EXE_EXPORTS_"; + exportFlagVar += linkLanguage; + exportFlagVar += "_FLAG"; + + linkFlags += this->Makefile->GetSafeDefinition(exportFlagVar); + linkFlags += " "; + } } - if (linkLanguage.empty()) { - cmSystemTools::Error( - "CMake can not determine linker language for target: " + - target->GetName()); - return; - } + this->AddLanguageFlagsForLinking(flags, target, linkLanguage, buildType); if (pcli) { this->OutputLinkLibraries(pcli, linkLineComputer, linkLibs, frameworkPath, linkPath); } + if (cmSystemTools::IsOn( this->Makefile->GetDefinition("BUILD_SHARED_LIBS"))) { std::string sFlagVar = std::string("CMAKE_SHARED_BUILD_") + @@ -1243,23 +1272,6 @@ void cmLocalGenerator::GetTargetFlags( linkFlags += this->Makefile->GetSafeDefinition(sFlagVar); linkFlags += " "; } - if (target->GetPropertyAsBool("WIN32_EXECUTABLE")) { - linkFlags += - this->Makefile->GetSafeDefinition("CMAKE_CREATE_WIN32_EXE"); - linkFlags += " "; - } else { - linkFlags += - this->Makefile->GetSafeDefinition("CMAKE_CREATE_CONSOLE_EXE"); - linkFlags += " "; - } - if (target->IsExecutableWithExports()) { - std::string exportFlagVar = "CMAKE_EXE_EXPORTS_"; - exportFlagVar += linkLanguage; - exportFlagVar += "_FLAG"; - - linkFlags += this->Makefile->GetSafeDefinition(exportFlagVar); - linkFlags += " "; - } std::string cmp0065Flags = this->GetLinkLibsCMP0065(linkLanguage, *target); @@ -1282,6 +1294,7 @@ void cmLocalGenerator::GetTargetFlags( linkFlags += " "; } } + std::vector<std::string> opts; target->GetLinkOptions(opts, config, linkLanguage); // LINK_OPTIONS are escaped. @@ -1914,7 +1927,9 @@ static void AddVisibilityCompileOption(std::string& flags, strcmp(prop, "protected") != 0 && strcmp(prop, "internal") != 0) { std::ostringstream e; e << "Target " << target->GetName() << " uses unsupported value \"" << prop - << "\" for " << flagDefine << "."; + << "\" for " << flagDefine << "." + << " The supported values are: default, hidden, protected, and " + "internal."; cmSystemTools::Error(e.str()); return; } diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx index 9b651a4..81cafa3 100644 --- a/Source/cmLocalNinjaGenerator.cxx +++ b/Source/cmLocalNinjaGenerator.cxx @@ -454,7 +454,8 @@ void cmLocalNinjaGenerator::AppendCustomCommandLines( void cmLocalNinjaGenerator::WriteCustomCommandBuildStatement( cmCustomCommand const* cc, const cmNinjaDeps& orderOnlyDeps) { - if (this->GetGlobalNinjaGenerator()->SeenCustomCommand(cc)) { + cmGlobalNinjaGenerator* gg = this->GetGlobalNinjaGenerator(); + if (gg->SeenCustomCommand(cc)) { return; } @@ -462,13 +463,12 @@ void cmLocalNinjaGenerator::WriteCustomCommandBuildStatement( const std::vector<std::string>& outputs = ccg.GetOutputs(); const std::vector<std::string>& byproducts = ccg.GetByproducts(); - cmNinjaDeps ninjaOutputs(outputs.size() + byproducts.size()), ninjaDeps; bool symbolic = false; for (std::string const& output : outputs) { if (cmSourceFile* sf = this->Makefile->GetSource(output)) { - symbolic = sf->GetPropertyAsBool("SYMBOLIC"); - if (symbolic) { + if (sf->GetPropertyAsBool("SYMBOLIC")) { + symbolic = true; break; } } @@ -479,25 +479,29 @@ void cmLocalNinjaGenerator::WriteCustomCommandBuildStatement( file of each imported target that has an add_dependencies pointing \ at us. How to know which ExternalProject step actually provides it? #endif + cmNinjaDeps ninjaOutputs(outputs.size() + byproducts.size()); std::transform(outputs.begin(), outputs.end(), ninjaOutputs.begin(), - this->GetGlobalNinjaGenerator()->MapToNinjaPath()); + gg->MapToNinjaPath()); std::transform(byproducts.begin(), byproducts.end(), - ninjaOutputs.begin() + outputs.size(), - this->GetGlobalNinjaGenerator()->MapToNinjaPath()); - this->AppendCustomCommandDeps(ccg, ninjaDeps); + ninjaOutputs.begin() + outputs.size(), gg->MapToNinjaPath()); for (std::string const& ninjaOutput : ninjaOutputs) { - this->GetGlobalNinjaGenerator()->SeenCustomCommandOutput(ninjaOutput); + gg->SeenCustomCommandOutput(ninjaOutput); } + cmNinjaDeps ninjaDeps; + this->AppendCustomCommandDeps(ccg, ninjaDeps); + std::vector<std::string> cmdLines; this->AppendCustomCommandLines(ccg, cmdLines); if (cmdLines.empty()) { - this->GetGlobalNinjaGenerator()->WritePhonyBuild( - this->GetBuildFileStream(), - "Phony custom command for " + ninjaOutputs[0], ninjaOutputs, ninjaDeps, - cmNinjaDeps(), orderOnlyDeps, cmNinjaVars()); + cmNinjaBuild build("phony"); + build.Comment = "Phony custom command for " + ninjaOutputs[0]; + build.Outputs = std::move(ninjaOutputs); + build.ExplicitDeps = std::move(ninjaDeps); + build.OrderOnlyDeps = orderOnlyDeps; + gg->WriteBuild(this->GetBuildFileStream(), build); } else { std::string customStep = cmSystemTools::GetFilenameName(ninjaOutputs[0]); // Hash full path to make unique. @@ -505,7 +509,7 @@ void cmLocalNinjaGenerator::WriteCustomCommandBuildStatement( cmCryptoHash hash(cmCryptoHash::AlgoSHA256); customStep += hash.HashString(ninjaOutputs[0]).substr(0, 7); - this->GetGlobalNinjaGenerator()->WriteCustomCommandBuild( + gg->WriteCustomCommandBuild( this->BuildCommandLine(cmdLines, customStep), this->ConstructComment(ccg), "Custom command for " + ninjaOutputs[0], cc->GetDepfile(), cc->GetJobPool(), cc->GetUsesTerminal(), diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index 6e9e112..7ad8ab3 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -172,21 +172,24 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkRule(bool useResponseFile) vars.Language = "CUDA"; std::string responseFlag; - if (!useResponseFile) { + + std::string cmakeVarLang = "CMAKE_"; + cmakeVarLang += this->TargetLinkLanguage; + + // build response file name + std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_LINK_FLAG"; + const char* flag = GetMakefile()->GetDefinition(cmakeLinkVar); + + if (flag) { + responseFlag = flag; + } else if (this->TargetLinkLanguage != "CUDA") { + responseFlag = "@"; + } + + if (!useResponseFile || responseFlag.empty()) { vars.Objects = "$in"; vars.LinkLibraries = "$LINK_PATH $LINK_LIBRARIES"; } else { - std::string cmakeVarLang = "CMAKE_"; - cmakeVarLang += this->TargetLinkLanguage; - - // build response file name - std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_LINK_FLAG"; - const char* flag = GetMakefile()->GetDefinition(cmakeLinkVar); - if (flag) { - responseFlag = flag; - } else { - responseFlag = "@"; - } rule.RspFile = "$RSP_FILE"; responseFlag += rule.RspFile; @@ -287,21 +290,24 @@ void cmNinjaNormalTargetGenerator::WriteLinkRule(bool useResponseFile) } std::string responseFlag; - if (!useResponseFile) { + + std::string cmakeVarLang = "CMAKE_"; + cmakeVarLang += this->TargetLinkLanguage; + + // build response file name + std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_LINK_FLAG"; + const char* flag = GetMakefile()->GetDefinition(cmakeLinkVar); + + if (flag) { + responseFlag = flag; + } else if (this->TargetLinkLanguage != "CUDA") { + responseFlag = "@"; + } + + if (!useResponseFile || responseFlag.empty()) { vars.Objects = "$in"; vars.LinkLibraries = "$LINK_PATH $LINK_LIBRARIES"; } else { - std::string cmakeVarLang = "CMAKE_"; - cmakeVarLang += this->TargetLinkLanguage; - - // build response file name - std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_LINK_FLAG"; - const char* flag = GetMakefile()->GetDefinition(cmakeLinkVar); - if (flag) { - responseFlag = flag; - } else { - responseFlag = "@"; - } rule.RspFile = "$RSP_FILE"; responseFlag += rule.RspFile; @@ -554,11 +560,12 @@ std::vector<std::string> cmNinjaNormalTargetGenerator::ComputeLinkCmd() void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatement() { - if (!this->GetGlobalGenerator()->GetLanguageEnabled("CUDA")) { + cmGlobalNinjaGenerator* globalGen = this->GetGlobalGenerator(); + if (!globalGen->GetLanguageEnabled("CUDA")) { return; } - cmGeneratorTarget& genTarget = *this->GetGeneratorTarget(); + cmGeneratorTarget* genTarget = this->GetGeneratorTarget(); bool requiresDeviceLinking = requireDeviceLinking( *this->GeneratorTarget, *this->GetLocalGenerator(), this->ConfigName); @@ -576,39 +583,39 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatement() std::string const cfgName = this->GetConfigName(); std::string const targetOutputReal = ConvertToNinjaPath( - genTarget.ObjectDirectory + "cmake_device_link" + objExt); + genTarget->ObjectDirectory + "cmake_device_link" + objExt); std::string const targetOutputImplib = ConvertToNinjaPath( - genTarget.GetFullPath(cfgName, cmStateEnums::ImportLibraryArtifact)); + genTarget->GetFullPath(cfgName, cmStateEnums::ImportLibraryArtifact)); this->DeviceLinkObject = targetOutputReal; // Write comments. cmGlobalNinjaGenerator::WriteDivider(this->GetBuildFileStream()); - const cmStateEnums::TargetType targetType = genTarget.GetType(); + const cmStateEnums::TargetType targetType = genTarget->GetType(); this->GetBuildFileStream() << "# Device Link build statements for " << cmState::GetTargetTypeName(targetType) << " target " << this->GetTargetName() << "\n\n"; // Compute the comment. - std::ostringstream comment; - comment << "Link the " << this->GetVisibleTypeName() << " " - << targetOutputReal; + cmNinjaBuild build(this->LanguageLinkerDeviceRule()); + build.Comment = "Link the "; + build.Comment += this->GetVisibleTypeName(); + build.Comment += " "; + build.Comment += targetOutputReal; - cmNinjaDeps emptyDeps; - cmNinjaVars vars; + cmNinjaVars& vars = build.Variables; // Compute outputs. - cmNinjaDeps outputs; - outputs.push_back(targetOutputReal); + build.Outputs.push_back(targetOutputReal); // Compute specific libraries to link with. - cmNinjaDeps explicitDeps = this->GetObjects(); - cmNinjaDeps implicitDeps = this->ComputeLinkDeps(this->TargetLinkLanguage); + build.ExplicitDeps = this->GetObjects(); + build.ImplicitDeps = this->ComputeLinkDeps(this->TargetLinkLanguage); std::string frameworkPath; std::string linkPath; - std::string createRule = genTarget.GetCreateRuleVariable( + std::string createRule = genTarget->GetCreateRuleVariable( this->TargetLinkLanguage, this->GetConfigName()); const bool useWatcomQuote = this->GetMakefile()->IsOn(createRule + "_USE_WATCOM_QUOTE"); @@ -621,14 +628,14 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatement() new cmNinjaLinkLineDeviceComputer( this->GetLocalGenerator(), this->GetLocalGenerator()->GetStateSnapshot().GetDirectory(), - this->GetGlobalGenerator())); + globalGen)); linkLineComputer->SetUseWatcomQuote(useWatcomQuote); localGen.GetTargetFlags( linkLineComputer.get(), this->GetConfigName(), vars["LINK_LIBRARIES"], - vars["FLAGS"], vars["LINK_FLAGS"], frameworkPath, linkPath, &genTarget); + vars["FLAGS"], vars["LINK_FLAGS"], frameworkPath, linkPath, genTarget); - this->addPoolNinjaVariable("JOB_POOL_LINK", &genTarget, vars); + this->addPoolNinjaVariable("JOB_POOL_LINK", genTarget, vars); vars["LINK_FLAGS"] = cmGlobalNinjaGenerator::EncodeLiteral(vars["LINK_FLAGS"]); @@ -642,18 +649,18 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatement() // code between the Makefile executable and library generators. if (targetType == cmStateEnums::EXECUTABLE) { std::string t = vars["FLAGS"]; - localGen.AddArchitectureFlags(t, &genTarget, cudaLinkLanguage, cfgName); + localGen.AddArchitectureFlags(t, genTarget, cudaLinkLanguage, cfgName); vars["FLAGS"] = t; } else { std::string t = vars["ARCH_FLAGS"]; - localGen.AddArchitectureFlags(t, &genTarget, cudaLinkLanguage, cfgName); + localGen.AddArchitectureFlags(t, genTarget, cudaLinkLanguage, cfgName); vars["ARCH_FLAGS"] = t; t.clear(); - localGen.AddLanguageFlagsForLinking(t, &genTarget, cudaLinkLanguage, + localGen.AddLanguageFlagsForLinking(t, genTarget, cudaLinkLanguage, cfgName); vars["LANGUAGE_COMPILE_FLAGS"] = t; } - if (this->GetGeneratorTarget()->HasSOName(cfgName)) { + if (genTarget->HasSOName(cfgName)) { vars["SONAME_FLAG"] = this->GetMakefile()->GetSONameFlag(this->TargetLinkLanguage); vars["SONAME"] = this->TargetNames.SharedObject; @@ -681,7 +688,7 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatement() this->SetMsvcTargetPdbVariable(vars); - if (this->GetGlobalGenerator()->IsGCCOnWindows()) { + if (globalGen->IsGCCOnWindows()) { // ar.exe can't handle backslashes in rsp files (implicitly used by gcc) std::string& linkLibraries = vars["LINK_LIBRARIES"]; std::replace(linkLibraries.begin(), linkLibraries.end(), '\\', '/'); @@ -689,46 +696,43 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatement() std::replace(link_path.begin(), link_path.end(), '\\', '/'); } - cmGlobalNinjaGenerator& globalGen = *this->GetGlobalGenerator(); - // Device linking currently doesn't support response files so // do not check if the user has explicitly forced a response file. int const commandLineLengthLimit = static_cast<int>(cmSystemTools::CalculateCommandLineLengthLimit()) - - globalGen.GetRuleCmdLength(this->LanguageLinkerDeviceRule()); + globalGen->GetRuleCmdLength(this->LanguageLinkerDeviceRule()); - const std::string rspfile = this->ConvertToNinjaPath( - std::string("CMakeFiles/") + genTarget.GetName() + ".rsp"); + build.RspFile = this->ConvertToNinjaPath(std::string("CMakeFiles/") + + genTarget->GetName() + ".rsp"); // Gather order-only dependencies. - cmNinjaDeps orderOnlyDeps; this->GetLocalGenerator()->AppendTargetDepends(this->GetGeneratorTarget(), - orderOnlyDeps); + build.OrderOnlyDeps); // Write the build statement for this target. bool usedResponseFile = false; - globalGen.WriteBuild(this->GetBuildFileStream(), comment.str(), - this->LanguageLinkerDeviceRule(), outputs, - /*implicitOuts=*/cmNinjaDeps(), explicitDeps, - implicitDeps, orderOnlyDeps, vars, rspfile, - commandLineLengthLimit, &usedResponseFile); - this->WriteDeviceLinkRule(false); + globalGen->WriteBuild(this->GetBuildFileStream(), build, + commandLineLengthLimit, &usedResponseFile); + this->WriteDeviceLinkRule(usedResponseFile); } void cmNinjaNormalTargetGenerator::WriteLinkStatement() { - cmGeneratorTarget& gt = *this->GetGeneratorTarget(); + cmMakefile* mf = this->GetMakefile(); + cmGlobalNinjaGenerator* globalGen = this->GetGlobalGenerator(); + cmGeneratorTarget* gt = this->GetGeneratorTarget(); + const std::string cfgName = this->GetConfigName(); - std::string targetOutput = ConvertToNinjaPath(gt.GetFullPath(cfgName)); + std::string targetOutput = ConvertToNinjaPath(gt->GetFullPath(cfgName)); std::string targetOutputReal = ConvertToNinjaPath( - gt.GetFullPath(cfgName, cmStateEnums::RuntimeBinaryArtifact, - /*realname=*/true)); + gt->GetFullPath(cfgName, cmStateEnums::RuntimeBinaryArtifact, + /*realname=*/true)); std::string targetOutputImplib = ConvertToNinjaPath( - gt.GetFullPath(cfgName, cmStateEnums::ImportLibraryArtifact)); + gt->GetFullPath(cfgName, cmStateEnums::ImportLibraryArtifact)); - if (gt.IsAppBundleOnApple()) { + if (gt->IsAppBundleOnApple()) { // Create the app bundle - std::string outpath = gt.GetDirectory(cfgName); + std::string outpath = gt->GetDirectory(cfgName); this->OSXBundleGenerator->CreateAppBundle(this->TargetNames.Output, outpath); @@ -741,34 +745,34 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() targetOutputReal += "/"; targetOutputReal += this->TargetNames.Real; targetOutputReal = this->ConvertToNinjaPath(targetOutputReal); - } else if (gt.IsFrameworkOnApple()) { + } else if (gt->IsFrameworkOnApple()) { // Create the library framework. this->OSXBundleGenerator->CreateFramework(this->TargetNames.Output, - gt.GetDirectory(cfgName)); - } else if (gt.IsCFBundleOnApple()) { + gt->GetDirectory(cfgName)); + } else if (gt->IsCFBundleOnApple()) { // Create the core foundation bundle. this->OSXBundleGenerator->CreateCFBundle(this->TargetNames.Output, - gt.GetDirectory(cfgName)); + gt->GetDirectory(cfgName)); } // Write comments. cmGlobalNinjaGenerator::WriteDivider(this->GetBuildFileStream()); - const cmStateEnums::TargetType targetType = gt.GetType(); + const cmStateEnums::TargetType targetType = gt->GetType(); this->GetBuildFileStream() << "# Link build statements for " << cmState::GetTargetTypeName(targetType) << " target " << this->GetTargetName() << "\n\n"; - cmNinjaDeps emptyDeps; - cmNinjaVars vars; + cmNinjaBuild linkBuild(this->LanguageLinkerRule()); + cmNinjaVars& vars = linkBuild.Variables; // Compute the comment. - std::ostringstream comment; - comment << "Link the " << this->GetVisibleTypeName() << " " - << targetOutputReal; + linkBuild.Comment = "Link the "; + linkBuild.Comment += this->GetVisibleTypeName(); + linkBuild.Comment += " "; + linkBuild.Comment += targetOutputReal; // Compute outputs. - cmNinjaDeps outputs; - outputs.push_back(targetOutputReal); + linkBuild.Outputs.push_back(targetOutputReal); if (this->TargetLinkLanguage == "Swift") { vars["SWIFT_LIBRARY_NAME"] = [this]() -> std::string { @@ -777,12 +781,11 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() return targetNames.Base; }(); - vars["SWIFT_MODULE_NAME"] = [this]() -> std::string { - if (const char* name = - this->GetGeneratorTarget()->GetProperty("Swift_MODULE_NAME")) { + vars["SWIFT_MODULE_NAME"] = [gt]() -> std::string { + if (const char* name = gt->GetProperty("Swift_MODULE_NAME")) { return name; } - return this->GetGeneratorTarget()->GetName(); + return gt->GetName(); }(); vars["SWIFT_MODULE"] = [this](const std::string& module) -> std::string { @@ -806,7 +809,7 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() vars["SWIFT_OUTPUT_FILE_MAP"] = this->GetLocalGenerator()->ConvertToOutputFormat( - this->ConvertToNinjaPath(gt.GetSupportDirectory() + + this->ConvertToNinjaPath(gt->GetSupportDirectory() + "/output-file-map.json"), cmOutputConverter::SHELL); @@ -835,35 +838,31 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() } // Compute specific libraries to link with. - cmNinjaDeps explicitDeps; if (this->TargetLinkLanguage == "Swift") { std::vector<cmSourceFile const*> sources; - this->GetGeneratorTarget()->GetObjectSources(sources, - this->GetConfigName()); + gt->GetObjectSources(sources, this->GetConfigName()); for (const auto& source : sources) { - outputs.push_back( + linkBuild.Outputs.push_back( this->ConvertToNinjaPath(this->GetObjectFilePath(source))); - explicitDeps.push_back( + linkBuild.ExplicitDeps.push_back( this->ConvertToNinjaPath(this->GetSourceFilePath(source))); } - outputs.push_back(vars["SWIFT_MODULE"]); + linkBuild.Outputs.push_back(vars["SWIFT_MODULE"]); } else { - explicitDeps = this->GetObjects(); + linkBuild.ExplicitDeps = this->GetObjects(); } - cmNinjaDeps implicitDeps = this->ComputeLinkDeps(this->TargetLinkLanguage); + linkBuild.ImplicitDeps = this->ComputeLinkDeps(this->TargetLinkLanguage); if (!this->DeviceLinkObject.empty()) { - explicitDeps.push_back(this->DeviceLinkObject); + linkBuild.ExplicitDeps.push_back(this->DeviceLinkObject); } - cmMakefile* mf = this->GetMakefile(); - std::string frameworkPath; std::string linkPath; std::string createRule = - gt.GetCreateRuleVariable(this->TargetLinkLanguage, this->GetConfigName()); + gt->GetCreateRuleVariable(this->TargetLinkLanguage, this->GetConfigName()); bool useWatcomQuote = mf->IsOn(createRule + "_USE_WATCOM_QUOTE"); cmLocalNinjaGenerator& localGen = *this->GetLocalGenerator(); @@ -871,14 +870,14 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() localGen.ConvertToOutputFormat(targetOutputReal, cmOutputConverter::SHELL); std::unique_ptr<cmLinkLineComputer> linkLineComputer( - this->GetGlobalGenerator()->CreateLinkLineComputer( + globalGen->CreateLinkLineComputer( this->GetLocalGenerator(), this->GetLocalGenerator()->GetStateSnapshot().GetDirectory())); linkLineComputer->SetUseWatcomQuote(useWatcomQuote); localGen.GetTargetFlags(linkLineComputer.get(), this->GetConfigName(), vars["LINK_LIBRARIES"], vars["FLAGS"], - vars["LINK_FLAGS"], frameworkPath, linkPath, >); + vars["LINK_FLAGS"], frameworkPath, linkPath, gt); // Add OS X version flags, if any. if (this->GeneratorTarget->GetType() == cmStateEnums::SHARED_LIBRARY || @@ -889,7 +888,7 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() "CURRENT", false); } - this->addPoolNinjaVariable("JOB_POOL_LINK", >, vars); + this->addPoolNinjaVariable("JOB_POOL_LINK", gt, vars); this->AddModuleDefinitionFlag(linkLineComputer.get(), vars["LINK_FLAGS"]); vars["LINK_FLAGS"] = @@ -899,7 +898,7 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() vars["LINK_PATH"] = frameworkPath + linkPath; std::string lwyuFlags; - if (gt.GetPropertyAsBool("LINK_WHAT_YOU_USE")) { + if (gt->GetPropertyAsBool("LINK_WHAT_YOU_USE")) { lwyuFlags = " -Wl,--no-as-needed"; } @@ -908,24 +907,23 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() // code between the Makefile executable and library generators. if (targetType == cmStateEnums::EXECUTABLE) { std::string t = vars["FLAGS"]; - localGen.AddArchitectureFlags(t, >, TargetLinkLanguage, cfgName); + localGen.AddArchitectureFlags(t, gt, TargetLinkLanguage, cfgName); t += lwyuFlags; vars["FLAGS"] = t; } else { std::string t = vars["ARCH_FLAGS"]; - localGen.AddArchitectureFlags(t, >, TargetLinkLanguage, cfgName); + localGen.AddArchitectureFlags(t, gt, TargetLinkLanguage, cfgName); vars["ARCH_FLAGS"] = t; t.clear(); t += lwyuFlags; - localGen.AddLanguageFlagsForLinking(t, >, TargetLinkLanguage, cfgName); + localGen.AddLanguageFlagsForLinking(t, gt, TargetLinkLanguage, cfgName); vars["LANGUAGE_COMPILE_FLAGS"] = t; } - if (this->GetGeneratorTarget()->HasSOName(cfgName)) { + if (gt->HasSOName(cfgName)) { vars["SONAME_FLAG"] = mf->GetSONameFlag(this->TargetLinkLanguage); vars["SONAME"] = this->TargetNames.SharedObject; if (targetType == cmStateEnums::SHARED_LIBRARY) { - std::string install_dir = - this->GetGeneratorTarget()->GetInstallNameDirForBuildTree(cfgName); + std::string install_dir = gt->GetInstallNameDirForBuildTree(cfgName); if (!install_dir.empty()) { vars["INSTALLNAME_DIR"] = localGen.ConvertToOutputFormat( install_dir, cmOutputConverter::SHELL); @@ -940,7 +938,7 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() targetOutputImplib, cmOutputConverter::SHELL); vars["TARGET_IMPLIB"] = impLibPath; EnsureParentDirectoryExists(impLibPath); - if (gt.HasImportLibrary(cfgName)) { + if (gt->HasImportLibrary(cfgName)) { byproducts.push_back(targetOutputImplib); } } @@ -951,7 +949,7 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() std::string prefix; std::string base; std::string suffix; - this->GetGeneratorTarget()->GetFullNameComponents(prefix, base, suffix); + gt->GetFullNameComponents(prefix, base, suffix); std::string dbg_suffix = ".dbg"; // TODO: Where to document? if (mf->GetDefinition("CMAKE_DEBUG_SYMBOL_SUFFIX")) { @@ -960,12 +958,12 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() vars["TARGET_PDB"] = base + suffix + dbg_suffix; } - const std::string objPath = GetGeneratorTarget()->GetSupportDirectory(); + const std::string objPath = gt->GetSupportDirectory(); vars["OBJECT_DIR"] = this->GetLocalGenerator()->ConvertToOutputFormat( this->ConvertToNinjaPath(objPath), cmOutputConverter::SHELL); EnsureDirectoryExists(objPath); - if (this->GetGlobalGenerator()->IsGCCOnWindows()) { + if (globalGen->IsGCCOnWindows()) { // ar.exe can't handle backslashes in rsp files (implicitly used by gcc) std::string& linkLibraries = vars["LINK_LIBRARIES"]; std::replace(linkLibraries.begin(), linkLibraries.end(), '\\', '/'); @@ -974,8 +972,8 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() } const std::vector<cmCustomCommand>* cmdLists[3] = { - >.GetPreBuildCommands(), >.GetPreLinkCommands(), - >.GetPostBuildCommands() + >->GetPreBuildCommands(), >->GetPreLinkCommands(), + >->GetPostBuildCommands() }; std::vector<std::string> preLinkCmdLines, postBuildCmdLines; @@ -995,7 +993,7 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() // maybe create .def file from list of objects cmGeneratorTarget::ModuleDefinitionInfo const* mdi = - gt.GetModuleDefinitionInfo(this->GetConfigName()); + gt->GetModuleDefinitionInfo(this->GetConfigName()); if (mdi && mdi->DefFileGenerated) { std::string cmakeCommand = this->GetLocalGenerator()->ConvertToOutputFormat( @@ -1027,8 +1025,7 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() } } // If we have any PRE_LINK commands, we need to go back to CMAKE_BINARY_DIR - // for - // the link commands. + // for the link commands. if (!preLinkCmdLines.empty()) { const std::string homeOutDir = localGen.ConvertToOutputFormat( localGen.GetBinaryDirectory(), cmOutputConverter::SHELL); @@ -1042,59 +1039,64 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() cmNinjaVars symlinkVars; bool const symlinkNeeded = - (targetOutput != targetOutputReal && !gt.IsFrameworkOnApple()); + (targetOutput != targetOutputReal && !gt->IsFrameworkOnApple()); if (!symlinkNeeded) { vars["POST_BUILD"] = postBuildCmdLine; } else { vars["POST_BUILD"] = cmGlobalNinjaGenerator::SHELL_NOOP; symlinkVars["POST_BUILD"] = postBuildCmdLine; } - cmGlobalNinjaGenerator& globalGen = *this->GetGlobalGenerator(); + + std::string cmakeVarLang = "CMAKE_"; + cmakeVarLang += this->TargetLinkLanguage; + + // build response file name + std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_LINK_FLAG"; + + const char* flag = GetMakefile()->GetDefinition(cmakeLinkVar); bool const lang_supports_response = - !(this->TargetLinkLanguage == "RC" || this->TargetLinkLanguage == "CUDA"); + !(this->TargetLinkLanguage == "RC" || + (this->TargetLinkLanguage == "CUDA" && !flag)); int commandLineLengthLimit = -1; if (!lang_supports_response || !this->ForceResponseFile()) { commandLineLengthLimit = static_cast<int>(cmSystemTools::CalculateCommandLineLengthLimit()) - - globalGen.GetRuleCmdLength(this->LanguageLinkerRule()); + globalGen->GetRuleCmdLength(linkBuild.Rule); } - const std::string rspfile = this->ConvertToNinjaPath( - std::string("CMakeFiles/") + gt.GetName() + ".rsp"); + linkBuild.RspFile = this->ConvertToNinjaPath(std::string("CMakeFiles/") + + gt->GetName() + ".rsp"); // Gather order-only dependencies. - cmNinjaDeps orderOnlyDeps; - this->GetLocalGenerator()->AppendTargetDepends(this->GetGeneratorTarget(), - orderOnlyDeps); + this->GetLocalGenerator()->AppendTargetDepends(gt, linkBuild.OrderOnlyDeps); // Ninja should restat after linking if and only if there are byproducts. vars["RESTAT"] = byproducts.empty() ? "" : "1"; for (std::string const& o : byproducts) { - this->GetGlobalGenerator()->SeenCustomCommandOutput(o); - outputs.push_back(o); + globalGen->SeenCustomCommandOutput(o); + linkBuild.Outputs.push_back(o); } // Write the build statement for this target. bool usedResponseFile = false; - globalGen.WriteBuild(this->GetBuildFileStream(), comment.str(), - this->LanguageLinkerRule(), outputs, - /*implicitOuts=*/cmNinjaDeps(), explicitDeps, - implicitDeps, orderOnlyDeps, vars, rspfile, - commandLineLengthLimit, &usedResponseFile); + globalGen->WriteBuild(this->GetBuildFileStream(), linkBuild, + commandLineLengthLimit, &usedResponseFile); this->WriteLinkRule(usedResponseFile); if (symlinkNeeded) { if (targetType == cmStateEnums::EXECUTABLE) { - globalGen.WriteBuild( - this->GetBuildFileStream(), - "Create executable symlink " + targetOutput, - "CMAKE_SYMLINK_EXECUTABLE", cmNinjaDeps(1, targetOutput), - /*implicitOuts=*/cmNinjaDeps(), cmNinjaDeps(1, targetOutputReal), - emptyDeps, emptyDeps, symlinkVars); + cmNinjaBuild build("CMAKE_SYMLINK_EXECUTABLE"); + build.Comment = "Create executable symlink " + targetOutput; + build.Outputs.push_back(targetOutput); + build.ExplicitDeps.push_back(targetOutputReal); + build.Variables = std::move(symlinkVars); + globalGen->WriteBuild(this->GetBuildFileStream(), build); } else { - cmNinjaDeps symlinks; + cmNinjaBuild build("CMAKE_SYMLINK_LIBRARY"); + build.Comment = "Create library symlink " + targetOutput; + std::string const soName = this->ConvertToNinjaPath( this->GetTargetFilePath(this->TargetNames.SharedObject)); // If one link has to be created. @@ -1104,32 +1106,32 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() soName, cmOutputConverter::SHELL); } else { symlinkVars["SONAME"].clear(); - symlinks.push_back(soName); + build.Outputs.push_back(soName); } - symlinks.push_back(targetOutput); - globalGen.WriteBuild( - this->GetBuildFileStream(), "Create library symlink " + targetOutput, - "CMAKE_SYMLINK_LIBRARY", symlinks, - /*implicitOuts=*/cmNinjaDeps(), cmNinjaDeps(1, targetOutputReal), - emptyDeps, emptyDeps, symlinkVars); + build.Outputs.push_back(targetOutput); + build.ExplicitDeps.push_back(targetOutputReal); + build.Variables = std::move(symlinkVars); + + globalGen->WriteBuild(this->GetBuildFileStream(), build); } } // Add aliases for the file name and the target name. - globalGen.AddTargetAlias(this->TargetNames.Output, >); - globalGen.AddTargetAlias(this->GetTargetName(), >); + globalGen->AddTargetAlias(this->TargetNames.Output, gt); + globalGen->AddTargetAlias(this->GetTargetName(), gt); } void cmNinjaNormalTargetGenerator::WriteObjectLibStatement() { // Write a phony output that depends on all object files. - cmNinjaDeps outputs; - this->GetLocalGenerator()->AppendTargetOutputs(this->GetGeneratorTarget(), - outputs); - cmNinjaDeps depends = this->GetObjects(); - this->GetGlobalGenerator()->WritePhonyBuild( - this->GetBuildFileStream(), "Object library " + this->GetTargetName(), - outputs, depends); + { + cmNinjaBuild build("phony"); + build.Comment = "Object library " + this->GetTargetName(); + this->GetLocalGenerator()->AppendTargetOutputs(this->GetGeneratorTarget(), + build.Outputs); + build.ExplicitDeps = this->GetObjects(); + this->GetGlobalGenerator()->WriteBuild(this->GetBuildFileStream(), build); + } // Add aliases for the target name. this->GetGlobalGenerator()->AddTargetAlias(this->GetTargetName(), diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index 5b8ed90..2139a45 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -465,12 +465,12 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang) std::string flags = "$FLAGS"; std::string responseFlag; - bool const lang_supports_response = !(lang == "RC" || lang == "CUDA"); + bool const lang_supports_response = lang != "RC"; if (lang_supports_response && this->ForceResponseFile()) { std::string const responseFlagVar = "CMAKE_" + lang + "_RESPONSE_FILE_FLAG"; responseFlag = this->Makefile->GetSafeDefinition(responseFlagVar); - if (responseFlag.empty()) { + if (responseFlag.empty() && lang != "CUDA") { responseFlag = "@"; } } @@ -796,97 +796,102 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatements() const std::string& config = this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"); - std::vector<cmSourceFile const*> customCommands; - this->GeneratorTarget->GetCustomCommands(customCommands, config); - for (cmSourceFile const* sf : customCommands) { - cmCustomCommand const* cc = sf->GetCustomCommand(); - this->GetLocalGenerator()->AddCustomCommandTarget( - cc, this->GetGeneratorTarget()); - // Record the custom commands for this target. The container is used - // in WriteObjectBuildStatement when called in a loop below. - this->CustomCommands.push_back(cc); + { + std::vector<cmSourceFile const*> customCommands; + this->GeneratorTarget->GetCustomCommands(customCommands, config); + for (cmSourceFile const* sf : customCommands) { + cmCustomCommand const* cc = sf->GetCustomCommand(); + this->GetLocalGenerator()->AddCustomCommandTarget( + cc, this->GetGeneratorTarget()); + // Record the custom commands for this target. The container is used + // in WriteObjectBuildStatement when called in a loop below. + this->CustomCommands.push_back(cc); + } } - std::vector<cmSourceFile const*> headerSources; - this->GeneratorTarget->GetHeaderSources(headerSources, config); - this->OSXBundleGenerator->GenerateMacOSXContentStatements( - headerSources, this->MacOSXContentGenerator.get()); - std::vector<cmSourceFile const*> extraSources; - this->GeneratorTarget->GetExtraSources(extraSources, config); - this->OSXBundleGenerator->GenerateMacOSXContentStatements( - extraSources, this->MacOSXContentGenerator.get()); - std::vector<cmSourceFile const*> externalObjects; - this->GeneratorTarget->GetExternalObjects(externalObjects, config); - for (cmSourceFile const* sf : externalObjects) { - this->Objects.push_back(this->GetSourceFilePath(sf)); + { + std::vector<cmSourceFile const*> headerSources; + this->GeneratorTarget->GetHeaderSources(headerSources, config); + this->OSXBundleGenerator->GenerateMacOSXContentStatements( + headerSources, this->MacOSXContentGenerator.get()); } - - cmNinjaDeps orderOnlyDeps; - this->GetLocalGenerator()->AppendTargetDepends( - this->GeneratorTarget, orderOnlyDeps, DependOnTargetOrdering); - - // Add order-only dependencies on other files associated with the target. - cmAppend(orderOnlyDeps, this->ExtraFiles); - - // Add order-only dependencies on custom command outputs. - for (cmCustomCommand const* cc : this->CustomCommands) { - cmCustomCommandGenerator ccg(*cc, this->GetConfigName(), - this->GetLocalGenerator()); - const std::vector<std::string>& ccoutputs = ccg.GetOutputs(); - const std::vector<std::string>& ccbyproducts = ccg.GetByproducts(); - std::transform(ccoutputs.begin(), ccoutputs.end(), - std::back_inserter(orderOnlyDeps), MapToNinjaPath()); - std::transform(ccbyproducts.begin(), ccbyproducts.end(), - std::back_inserter(orderOnlyDeps), MapToNinjaPath()); + { + std::vector<cmSourceFile const*> extraSources; + this->GeneratorTarget->GetExtraSources(extraSources, config); + this->OSXBundleGenerator->GenerateMacOSXContentStatements( + extraSources, this->MacOSXContentGenerator.get()); } - - std::sort(orderOnlyDeps.begin(), orderOnlyDeps.end()); - orderOnlyDeps.erase(std::unique(orderOnlyDeps.begin(), orderOnlyDeps.end()), - orderOnlyDeps.end()); - - // The phony target must depend on at least one input or ninja will explain - // that "output ... of phony edge with no inputs doesn't exist" and consider - // the phony output "dirty". - if (orderOnlyDeps.empty()) { - // Any path that always exists will work here. It would be nice to - // use just "." but that is not supported by Ninja < 1.7. - std::string tgtDir; - tgtDir += this->LocalGenerator->GetCurrentBinaryDirectory(); - tgtDir += "/"; - tgtDir += this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget); - orderOnlyDeps.push_back(this->ConvertToNinjaPath(tgtDir)); + { + std::vector<cmSourceFile const*> externalObjects; + this->GeneratorTarget->GetExternalObjects(externalObjects, config); + for (cmSourceFile const* sf : externalObjects) { + this->Objects.push_back(this->GetSourceFilePath(sf)); + } } { - cmNinjaDeps orderOnlyTarget; - orderOnlyTarget.push_back(this->OrderDependsTargetForTarget()); - this->GetGlobalGenerator()->WritePhonyBuild( - this->GetBuildFileStream(), - "Order-only phony target for " + this->GetTargetName(), orderOnlyTarget, - cmNinjaDeps(), cmNinjaDeps(), orderOnlyDeps); + cmNinjaBuild build("phony"); + build.Comment = "Order-only phony target for " + this->GetTargetName(); + build.Outputs.push_back(this->OrderDependsTargetForTarget()); + + cmNinjaDeps& orderOnlyDeps = build.OrderOnlyDeps; + this->GetLocalGenerator()->AppendTargetDepends( + this->GeneratorTarget, orderOnlyDeps, DependOnTargetOrdering); + + // Add order-only dependencies on other files associated with the target. + cmAppend(orderOnlyDeps, this->ExtraFiles); + + // Add order-only dependencies on custom command outputs. + for (cmCustomCommand const* cc : this->CustomCommands) { + cmCustomCommandGenerator ccg(*cc, this->GetConfigName(), + this->GetLocalGenerator()); + const std::vector<std::string>& ccoutputs = ccg.GetOutputs(); + const std::vector<std::string>& ccbyproducts = ccg.GetByproducts(); + std::transform(ccoutputs.begin(), ccoutputs.end(), + std::back_inserter(orderOnlyDeps), MapToNinjaPath()); + std::transform(ccbyproducts.begin(), ccbyproducts.end(), + std::back_inserter(orderOnlyDeps), MapToNinjaPath()); + } + + std::sort(orderOnlyDeps.begin(), orderOnlyDeps.end()); + orderOnlyDeps.erase( + std::unique(orderOnlyDeps.begin(), orderOnlyDeps.end()), + orderOnlyDeps.end()); + + // The phony target must depend on at least one input or ninja will explain + // that "output ... of phony edge with no inputs doesn't exist" and + // consider the phony output "dirty". + if (orderOnlyDeps.empty()) { + // Any path that always exists will work here. It would be nice to + // use just "." but that is not supported by Ninja < 1.7. + std::string tgtDir; + tgtDir += this->LocalGenerator->GetCurrentBinaryDirectory(); + tgtDir += "/"; + tgtDir += + this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget); + orderOnlyDeps.push_back(this->ConvertToNinjaPath(tgtDir)); + } + + this->GetGlobalGenerator()->WriteBuild(this->GetBuildFileStream(), build); } - std::vector<cmSourceFile const*> objectSources; - this->GeneratorTarget->GetObjectSources(objectSources, config); - for (cmSourceFile const* sf : objectSources) { - this->WriteObjectBuildStatement(sf); + + { + std::vector<cmSourceFile const*> objectSources; + this->GeneratorTarget->GetObjectSources(objectSources, config); + for (cmSourceFile const* sf : objectSources) { + this->WriteObjectBuildStatement(sf); + } } for (auto const& langDDIFiles : this->DDIFiles) { std::string const& language = langDDIFiles.first; cmNinjaDeps const& ddiFiles = langDDIFiles.second; - std::string const ddComment; - std::string const ddRule = this->LanguageDyndepRule(language); - cmNinjaDeps ddOutputs; - cmNinjaDeps ddImplicitOuts; - cmNinjaDeps const& ddExplicitDeps = ddiFiles; - cmNinjaDeps ddImplicitDeps; - cmNinjaDeps ddOrderOnlyDeps; - cmNinjaVars ddVars; + cmNinjaBuild build(this->LanguageDyndepRule(language)); + build.Outputs.push_back(this->GetDyndepFilePath(language)); + build.ExplicitDeps = ddiFiles; this->WriteTargetDependInfo(language); - ddOutputs.push_back(this->GetDyndepFilePath(language)); - // Make sure dyndep files for all our dependencies have already // been generated so that the '<LANG>Modules.json' files they // produced as side-effects are available for us to read. @@ -896,11 +901,9 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatements() // refactoring the Ninja generator to generate targets in // dependency order so that we can collect the needed information. this->GetLocalGenerator()->AppendTargetDepends( - this->GeneratorTarget, ddOrderOnlyDeps, DependOnTargetArtifact); + this->GeneratorTarget, build.OrderOnlyDeps, DependOnTargetArtifact); - this->GetGlobalGenerator()->WriteBuild( - this->GetBuildFileStream(), ddComment, ddRule, ddOutputs, ddImplicitOuts, - ddExplicitDeps, ddImplicitDeps, ddOrderOnlyDeps, ddVars); + this->GetGlobalGenerator()->WriteBuild(this->GetBuildFileStream(), build); } this->GetBuildFileStream() << "\n"; @@ -941,12 +944,21 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( std::string const objectFileDir = cmSystemTools::GetFilenamePath(objectFileName); + std::string cmakeVarLang = "CMAKE_"; + cmakeVarLang += language; + + // build response file name + std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_FLAG"; + + const char* flag = GetMakefile()->GetDefinition(cmakeLinkVar); + bool const lang_supports_response = - !(language == "RC" || language == "CUDA"); + !(language == "RC" || (language == "CUDA" && !flag)); int const commandLineLengthLimit = ((lang_supports_response && this->ForceResponseFile())) ? -1 : 0; - cmNinjaVars vars; + cmNinjaBuild objBuild(this->LanguageCompilerRule(language)); + cmNinjaVars& vars = objBuild.Variables; vars["FLAGS"] = this->ComputeFlagsForObject(source, language); vars["DEFINES"] = this->ComputeDefines(source, language); vars["INCLUDES"] = this->ComputeIncludes(source, language); @@ -977,32 +989,26 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( language, sourceFileName, objectDir, objectFileName, objectFileDir, vars["FLAGS"], vars["DEFINES"], vars["INCLUDES"]); - std::string comment; - std::string rule = this->LanguageCompilerRule(language); - - cmNinjaDeps outputs; - outputs.push_back(objectFileName); + objBuild.Outputs.push_back(objectFileName); // Add this object to the list of object files. this->Objects.push_back(objectFileName); - cmNinjaDeps explicitDeps; - explicitDeps.push_back(sourceFileName); + objBuild.ExplicitDeps.push_back(sourceFileName); - cmNinjaDeps implicitDeps; if (const char* objectDeps = source->GetProperty("OBJECT_DEPENDS")) { - std::vector<std::string> depList; - cmSystemTools::ExpandListArgument(objectDeps, depList); + std::vector<std::string> depList = + cmSystemTools::ExpandedListArgument(objectDeps); for (std::string& odi : depList) { if (cmSystemTools::FileIsFullPath(odi)) { odi = cmSystemTools::CollapseFullPath(odi); } } std::transform(depList.begin(), depList.end(), - std::back_inserter(implicitDeps), MapToNinjaPath()); + std::back_inserter(objBuild.ImplicitDeps), + MapToNinjaPath()); } - cmNinjaDeps orderOnlyDeps; - orderOnlyDeps.push_back(this->OrderDependsTargetForTarget()); + objBuild.OrderOnlyDeps.push_back(this->OrderDependsTargetForTarget()); // If the source file is GENERATED and does not have a custom command // (either attached to this source file or another one), assume that one of @@ -1012,8 +1018,8 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( !source->GetPropertyAsBool("__CMAKE_GENERATED_BY_CMAKE") && !source->GetCustomCommand() && !this->GetGlobalGenerator()->HasCustomCommandOutput(sourceFileName)) { - this->GetGlobalGenerator()->AddAssumedSourceDependencies(sourceFileName, - orderOnlyDeps); + this->GetGlobalGenerator()->AddAssumedSourceDependencies( + sourceFileName, objBuild.OrderOnlyDeps); } // For some cases we need to generate a ninja dyndep file. @@ -1022,39 +1028,34 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( // For some cases we do an explicit preprocessor invocation. bool const explicitPP = this->NeedExplicitPreprocessing(language); if (explicitPP) { - bool const compilePP = this->UsePreprocessedSource(language); - std::string const ppComment; - std::string const ppRule = this->LanguagePreprocessRule(language); - cmNinjaDeps ppOutputs; - cmNinjaDeps ppImplicitOuts; - cmNinjaDeps ppExplicitDeps; - cmNinjaDeps ppImplicitDeps; - cmNinjaDeps ppOrderOnlyDeps; - cmNinjaVars ppVars; + cmNinjaBuild ppBuild(this->LanguagePreprocessRule(language)); std::string const ppFileName = this->ConvertToNinjaPath(this->GetPreprocessedFilePath(source)); - ppOutputs.push_back(ppFileName); + ppBuild.Outputs.push_back(ppFileName); + ppBuild.RspFile = ppFileName + ".rsp"; + + bool const compilePP = this->UsePreprocessedSource(language); if (compilePP) { // Move compilation dependencies to the preprocessing build statement. - std::swap(ppExplicitDeps, explicitDeps); - std::swap(ppImplicitDeps, implicitDeps); - std::swap(ppOrderOnlyDeps, orderOnlyDeps); - std::swap(ppVars["IN_ABS"], vars["IN_ABS"]); + std::swap(ppBuild.ExplicitDeps, objBuild.ExplicitDeps); + std::swap(ppBuild.ImplicitDeps, objBuild.ImplicitDeps); + std::swap(ppBuild.OrderOnlyDeps, objBuild.OrderOnlyDeps); + std::swap(ppBuild.Variables["IN_ABS"], vars["IN_ABS"]); // The actual compilation will now use the preprocessed source. - explicitDeps.push_back(ppFileName); + objBuild.ExplicitDeps.push_back(ppFileName); } else { // Copy compilation dependencies to the preprocessing build statement. - ppExplicitDeps = explicitDeps; - ppImplicitDeps = implicitDeps; - ppOrderOnlyDeps = orderOnlyDeps; - ppVars["IN_ABS"] = vars["IN_ABS"]; + ppBuild.ExplicitDeps = objBuild.ExplicitDeps; + ppBuild.ImplicitDeps = objBuild.ImplicitDeps; + ppBuild.OrderOnlyDeps = objBuild.OrderOnlyDeps; + ppBuild.Variables["IN_ABS"] = vars["IN_ABS"]; } // Preprocessing and compilation generally use the same flags. - ppVars["FLAGS"] = vars["FLAGS"]; + ppBuild.Variables["FLAGS"] = vars["FLAGS"]; if (compilePP) { // In case compilation requires flags that are incompatible with @@ -1066,16 +1067,16 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( if (compilePP) { // Move preprocessor definitions to the preprocessor build statement. - std::swap(ppVars["DEFINES"], vars["DEFINES"]); + std::swap(ppBuild.Variables["DEFINES"], vars["DEFINES"]); } else { // Copy preprocessor definitions to the preprocessor build statement. - ppVars["DEFINES"] = vars["DEFINES"]; + ppBuild.Variables["DEFINES"] = vars["DEFINES"]; } // Copy include directories to the preprocessor build statement. The // Fortran compilation build statement still needs them for the INCLUDE // directive. - ppVars["INCLUDES"] = vars["INCLUDES"]; + ppBuild.Variables["INCLUDES"] = vars["INCLUDES"]; if (compilePP) { // Prepend source file's original directory as an include directory @@ -1092,8 +1093,9 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( } // Explicit preprocessing always uses a depfile. - ppVars["DEP_FILE"] = this->GetLocalGenerator()->ConvertToOutputFormat( - objectFileName + ".pp.d", cmOutputConverter::SHELL); + ppBuild.Variables["DEP_FILE"] = + this->GetLocalGenerator()->ConvertToOutputFormat( + objectFileName + ".pp.d", cmOutputConverter::SHELL); if (compilePP) { // The actual compilation does not need a depfile because it // depends on the already-preprocessed source. @@ -1103,28 +1105,24 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( if (needDyndep) { // Tell dependency scanner the object file that will result from // compiling the source. - ppVars["OBJ_FILE"] = objectFileName; + ppBuild.Variables["OBJ_FILE"] = objectFileName; // Tell dependency scanner where to store dyndep intermediate results. std::string const ddiFile = objectFileName + ".ddi"; - ppVars["DYNDEP_INTERMEDIATE_FILE"] = ddiFile; - ppImplicitOuts.push_back(ddiFile); + ppBuild.Variables["DYNDEP_INTERMEDIATE_FILE"] = ddiFile; + ppBuild.ImplicitOuts.push_back(ddiFile); this->DDIFiles[language].push_back(ddiFile); } this->addPoolNinjaVariable("JOB_POOL_COMPILE", this->GetGeneratorTarget(), - ppVars); - - std::string const ppRspFile = ppFileName + ".rsp"; + ppBuild.Variables); - this->GetGlobalGenerator()->WriteBuild( - this->GetBuildFileStream(), ppComment, ppRule, ppOutputs, ppImplicitOuts, - ppExplicitDeps, ppImplicitDeps, ppOrderOnlyDeps, ppVars, ppRspFile, - commandLineLengthLimit); + this->GetGlobalGenerator()->WriteBuild(this->GetBuildFileStream(), ppBuild, + commandLineLengthLimit); } if (needDyndep) { std::string const dyndep = this->GetDyndepFilePath(language); - orderOnlyDeps.push_back(dyndep); + objBuild.OrderOnlyDeps.push_back(dyndep); vars["dyndep"] = dyndep; } @@ -1140,25 +1138,23 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( this->SetMsvcTargetPdbVariable(vars); - std::string const rspfile = objectFileName + ".rsp"; + objBuild.RspFile = objectFileName + ".rsp"; if (language == "Swift") { this->EmitSwiftDependencyInfo(source); } else { - this->GetGlobalGenerator()->WriteBuild( - this->GetBuildFileStream(), comment, rule, outputs, - /*implicitOuts=*/cmNinjaDeps(), explicitDeps, implicitDeps, - orderOnlyDeps, vars, rspfile, commandLineLengthLimit); + this->GetGlobalGenerator()->WriteBuild(this->GetBuildFileStream(), + objBuild, commandLineLengthLimit); } if (const char* objectOutputs = source->GetProperty("OBJECT_OUTPUTS")) { - std::vector<std::string> outputList; - cmSystemTools::ExpandListArgument(objectOutputs, outputList); - std::transform(outputList.begin(), outputList.end(), outputList.begin(), - MapToNinjaPath()); - this->GetGlobalGenerator()->WritePhonyBuild(this->GetBuildFileStream(), - "Additional output files.", - outputList, outputs); + cmNinjaBuild build("phony"); + build.Comment = "Additional output files."; + build.Outputs = cmSystemTools::ExpandedListArgument(objectOutputs); + std::transform(build.Outputs.begin(), build.Outputs.end(), + build.Outputs.begin(), MapToNinjaPath()); + build.ExplicitDeps = objBuild.Outputs; + this->GetGlobalGenerator()->WriteBuild(this->GetBuildFileStream(), build); } } diff --git a/Source/cmNinjaTypes.h b/Source/cmNinjaTypes.h index 78f3917..52c05b6 100644 --- a/Source/cmNinjaTypes.h +++ b/Source/cmNinjaTypes.h @@ -41,4 +41,24 @@ public: bool Generator = false; }; +class cmNinjaBuild +{ +public: + cmNinjaBuild() = default; + cmNinjaBuild(std::string rule) + : Rule(std::move(rule)) + { + } + + std::string Comment; + std::string Rule; + cmNinjaDeps Outputs; + cmNinjaDeps ImplicitOuts; + cmNinjaDeps ExplicitDeps; + cmNinjaDeps ImplicitDeps; + cmNinjaDeps OrderOnlyDeps; + cmNinjaVars Variables; + std::string RspFile; +}; + #endif // ! cmNinjaTypes_h diff --git a/Source/cmNinjaUtilityTargetGenerator.cxx b/Source/cmNinjaUtilityTargetGenerator.cxx index ab777c8..1225cbd 100644 --- a/Source/cmNinjaUtilityTargetGenerator.cxx +++ b/Source/cmNinjaUtilityTargetGenerator.cxx @@ -16,8 +16,10 @@ #include "cmSystemTools.h" #include <algorithm> +#include <array> #include <iterator> #include <string> +#include <utility> #include <vector> cmNinjaUtilityTargetGenerator::cmNinjaUtilityTargetGenerator( @@ -30,74 +32,74 @@ cmNinjaUtilityTargetGenerator::~cmNinjaUtilityTargetGenerator() = default; void cmNinjaUtilityTargetGenerator::Generate() { - std::string utilCommandName = - this->GetLocalGenerator()->GetCurrentBinaryDirectory(); + cmGlobalNinjaGenerator* gg = this->GetGlobalGenerator(); + cmLocalNinjaGenerator* lg = this->GetLocalGenerator(); + cmGeneratorTarget* genTarget = this->GetGeneratorTarget(); + + std::string utilCommandName = lg->GetCurrentBinaryDirectory(); utilCommandName += "/CMakeFiles"; utilCommandName += "/"; utilCommandName += this->GetTargetName() + ".util"; utilCommandName = this->ConvertToNinjaPath(utilCommandName); + cmNinjaBuild phonyBuild("phony"); std::vector<std::string> commands; - cmNinjaDeps deps, outputs, util_outputs(1, utilCommandName); - - const std::vector<cmCustomCommand>* cmdLists[2] = { - &this->GetGeneratorTarget()->GetPreBuildCommands(), - &this->GetGeneratorTarget()->GetPostBuildCommands() - }; + cmNinjaDeps deps, util_outputs(1, utilCommandName); bool uses_terminal = false; - - for (unsigned i = 0; i != 2; ++i) { - for (cmCustomCommand const& ci : *cmdLists[i]) { - cmCustomCommandGenerator ccg(ci, this->GetConfigName(), - this->GetLocalGenerator()); - this->GetLocalGenerator()->AppendCustomCommandDeps(ccg, deps); - this->GetLocalGenerator()->AppendCustomCommandLines(ccg, commands); - std::vector<std::string> const& ccByproducts = ccg.GetByproducts(); - std::transform(ccByproducts.begin(), ccByproducts.end(), - std::back_inserter(util_outputs), MapToNinjaPath()); - if (ci.GetUsesTerminal()) { - uses_terminal = true; + { + std::array<std::vector<cmCustomCommand> const*, 2> const cmdLists = { + { &genTarget->GetPreBuildCommands(), &genTarget->GetPostBuildCommands() } + }; + + for (std::vector<cmCustomCommand> const* cmdList : cmdLists) { + for (cmCustomCommand const& ci : *cmdList) { + cmCustomCommandGenerator ccg(ci, this->GetConfigName(), lg); + lg->AppendCustomCommandDeps(ccg, deps); + lg->AppendCustomCommandLines(ccg, commands); + std::vector<std::string> const& ccByproducts = ccg.GetByproducts(); + std::transform(ccByproducts.begin(), ccByproducts.end(), + std::back_inserter(util_outputs), MapToNinjaPath()); + if (ci.GetUsesTerminal()) { + uses_terminal = true; + } } } } - std::vector<cmSourceFile*> sources; - std::string config = - this->GetMakefile()->GetSafeDefinition("CMAKE_BUILD_TYPE"); - this->GetGeneratorTarget()->GetSourceFiles(sources, config); - for (cmSourceFile const* source : sources) { - if (cmCustomCommand const* cc = source->GetCustomCommand()) { - cmCustomCommandGenerator ccg(*cc, this->GetConfigName(), - this->GetLocalGenerator()); - this->GetLocalGenerator()->AddCustomCommandTarget( - cc, this->GetGeneratorTarget()); - - // Depend on all custom command outputs. - const std::vector<std::string>& ccOutputs = ccg.GetOutputs(); - const std::vector<std::string>& ccByproducts = ccg.GetByproducts(); - std::transform(ccOutputs.begin(), ccOutputs.end(), - std::back_inserter(deps), MapToNinjaPath()); - std::transform(ccByproducts.begin(), ccByproducts.end(), - std::back_inserter(deps), MapToNinjaPath()); + { + std::string const& config = + this->GetMakefile()->GetSafeDefinition("CMAKE_BUILD_TYPE"); + std::vector<cmSourceFile*> sources; + genTarget->GetSourceFiles(sources, config); + for (cmSourceFile const* source : sources) { + if (cmCustomCommand const* cc = source->GetCustomCommand()) { + cmCustomCommandGenerator ccg(*cc, this->GetConfigName(), lg); + lg->AddCustomCommandTarget(cc, genTarget); + + // Depend on all custom command outputs. + const std::vector<std::string>& ccOutputs = ccg.GetOutputs(); + const std::vector<std::string>& ccByproducts = ccg.GetByproducts(); + std::transform(ccOutputs.begin(), ccOutputs.end(), + std::back_inserter(deps), MapToNinjaPath()); + std::transform(ccByproducts.begin(), ccByproducts.end(), + std::back_inserter(deps), MapToNinjaPath()); + } } } - this->GetLocalGenerator()->AppendTargetOutputs(this->GetGeneratorTarget(), - outputs); - this->GetLocalGenerator()->AppendTargetDepends(this->GetGeneratorTarget(), - deps); + lg->AppendTargetOutputs(genTarget, phonyBuild.Outputs); + lg->AppendTargetDepends(genTarget, deps); if (commands.empty()) { - this->GetGlobalGenerator()->WritePhonyBuild( - this->GetBuildFileStream(), - "Utility command for " + this->GetTargetName(), outputs, deps); + phonyBuild.Comment = "Utility command for " + this->GetTargetName(); + phonyBuild.ExplicitDeps = std::move(deps); + gg->WriteBuild(this->GetBuildFileStream(), phonyBuild); } else { - std::string command = this->GetLocalGenerator()->BuildCommandLine( - commands, "utility", this->GeneratorTarget); - const char* echoStr = - this->GetGeneratorTarget()->GetProperty("EchoString"); + std::string command = + lg->BuildCommandLine(commands, "utility", this->GeneratorTarget); std::string desc; + const char* echoStr = genTarget->GetProperty("EchoString"); if (echoStr) { desc = echoStr; } else { @@ -108,18 +110,12 @@ void cmNinjaUtilityTargetGenerator::Generate() // makefile vars. cmSystemTools::ReplaceString( command, "$(CMAKE_SOURCE_DIR)", - this->GetLocalGenerator() - ->ConvertToOutputFormat( - this->GetLocalGenerator()->GetSourceDirectory(), - cmOutputConverter::SHELL) - .c_str()); + lg->ConvertToOutputFormat(lg->GetSourceDirectory(), + cmOutputConverter::SHELL)); cmSystemTools::ReplaceString( command, "$(CMAKE_BINARY_DIR)", - this->GetLocalGenerator() - ->ConvertToOutputFormat( - this->GetLocalGenerator()->GetBinaryDirectory(), - cmOutputConverter::SHELL) - .c_str()); + lg->ConvertToOutputFormat(lg->GetBinaryDirectory(), + cmOutputConverter::SHELL)); cmSystemTools::ReplaceString(command, "$(ARGS)", ""); if (command.find('$') != std::string::npos) { @@ -127,24 +123,22 @@ void cmNinjaUtilityTargetGenerator::Generate() } for (std::string const& util_output : util_outputs) { - this->GetGlobalGenerator()->SeenCustomCommandOutput(util_output); + gg->SeenCustomCommandOutput(util_output); } - this->GetGlobalGenerator()->WriteCustomCommandBuild( - command, desc, "Utility command for " + this->GetTargetName(), - /*depfile*/ "", /*job_pool*/ "", uses_terminal, - /*restat*/ true, util_outputs, deps); + gg->WriteCustomCommandBuild(command, desc, + "Utility command for " + this->GetTargetName(), + /*depfile*/ "", /*job_pool*/ "", uses_terminal, + /*restat*/ true, util_outputs, deps); - this->GetGlobalGenerator()->WritePhonyBuild( - this->GetBuildFileStream(), "", outputs, - cmNinjaDeps(1, utilCommandName)); + phonyBuild.ExplicitDeps.push_back(utilCommandName); + gg->WriteBuild(this->GetBuildFileStream(), phonyBuild); } // Add an alias for the logical target name regardless of what directory // contains it. Skip this for GLOBAL_TARGET because they are meant to // be per-directory and have one at the top-level anyway. - if (this->GetGeneratorTarget()->GetType() != cmStateEnums::GLOBAL_TARGET) { - this->GetGlobalGenerator()->AddTargetAlias(this->GetTargetName(), - this->GetGeneratorTarget()); + if (genTarget->GetType() != cmStateEnums::GLOBAL_TARGET) { + gg->AddTargetAlias(this->GetTargetName(), genTarget); } } diff --git a/Tests/CMakeCommands/target_compile_definitions/CMakeLists.txt b/Tests/CMakeCommands/target_compile_definitions/CMakeLists.txt index 7dc7995..a5bc1e1 100644 --- a/Tests/CMakeCommands/target_compile_definitions/CMakeLists.txt +++ b/Tests/CMakeCommands/target_compile_definitions/CMakeLists.txt @@ -34,6 +34,7 @@ target_compile_definitions(consumer CONSUMER_LANG_$<COMPILE_LANGUAGE> LANG_IS_CXX=$<COMPILE_LANGUAGE:CXX> LANG_IS_C=$<COMPILE_LANGUAGE:C> + LANG_IS_C_OR_CXX=$<COMPILE_LANGUAGE:C,CXX> ) if(CMAKE_GENERATOR MATCHES "Visual Studio|Xcode") target_compile_definitions(consumer diff --git a/Tests/CMakeCommands/target_compile_definitions/consumer.c b/Tests/CMakeCommands/target_compile_definitions/consumer.c index bacd4c4..bb65e01 100644 --- a/Tests/CMakeCommands/target_compile_definitions/consumer.c +++ b/Tests/CMakeCommands/target_compile_definitions/consumer.c @@ -35,6 +35,10 @@ # endif #endif +#if !LANG_IS_C_OR_CXX +# error Expected LANG_IS_C_OR_CXX +#endif + void consumer_c() { } diff --git a/Tests/CMakeCommands/target_compile_options/CMakeLists.txt b/Tests/CMakeCommands/target_compile_options/CMakeLists.txt index a24cd53..a7055b1 100644 --- a/Tests/CMakeCommands/target_compile_options/CMakeLists.txt +++ b/Tests/CMakeCommands/target_compile_options/CMakeLists.txt @@ -7,9 +7,11 @@ add_executable(target_compile_options "${CMAKE_CURRENT_SOURCE_DIR}/main.cpp" ) target_compile_options(target_compile_options - PRIVATE $<$<CXX_COMPILER_ID:GNU>:-DMY_PRIVATE_DEFINE> + PRIVATE $<$<CXX_COMPILER_ID:AppleClang,Clang,GNU>:-DMY_PRIVATE_DEFINE> PUBLIC $<$<COMPILE_LANG_AND_ID:CXX,GNU>:-DMY_PUBLIC_DEFINE> + PUBLIC $<$<COMPILE_LANG_AND_ID:CXX,GNU,Clang,AppleClang>:-DMY_MUTLI_COMP_PUBLIC_DEFINE> INTERFACE $<$<CXX_COMPILER_ID:GNU>:-DMY_INTERFACE_DEFINE> + INTERFACE $<$<CXX_COMPILER_ID:GNU,Clang,AppleClang>:-DMY_MULTI_COMP_INTERFACE_DEFINE> ) if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") @@ -17,6 +19,11 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") PRIVATE "DO_GNU_TESTS" ) +elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + target_compile_definitions(target_compile_options + PRIVATE + "DO_CLANG_TESTS" + ) endif() add_executable(consumer @@ -40,7 +47,7 @@ if(CMAKE_GENERATOR MATCHES "Visual Studio") endif() target_compile_options(consumer - PRIVATE $<$<CXX_COMPILER_ID:GNU>:$<TARGET_PROPERTY:target_compile_options,INTERFACE_COMPILE_OPTIONS>> + PRIVATE $<$<CXX_COMPILER_ID:GNU,Clang,AppleClang>:$<TARGET_PROPERTY:target_compile_options,INTERFACE_COMPILE_OPTIONS>> ) if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") @@ -48,6 +55,11 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") PRIVATE "DO_GNU_TESTS" ) +elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + target_compile_definitions(consumer + PRIVATE + "DO_CLANG_TESTS" + ) endif() # Test no items diff --git a/Tests/CMakeCommands/target_compile_options/consumer.cpp b/Tests/CMakeCommands/target_compile_options/consumer.cpp index fe79eb5..78928b4 100644 --- a/Tests/CMakeCommands/target_compile_options/consumer.cpp +++ b/Tests/CMakeCommands/target_compile_options/consumer.cpp @@ -13,6 +13,30 @@ # error Expected MY_INTERFACE_DEFINE # endif +# ifndef MY_MULTI_COMP_INTERFACE_DEFINE +# error Expected MY_MULTI_COMP_INTERFACE_DEFINE +# endif + +# ifndef MY_MUTLI_COMP_PUBLIC_DEFINE +# error Expected MY_MUTLI_COMP_PUBLIC_DEFINE +# endif + +#endif + +#ifdef DO_CLANG_TESTS + +# ifdef MY_PRIVATE_DEFINE +# error Unexpected MY_PRIVATE_DEFINE +# endif + +# ifndef MY_MULTI_COMP_INTERFACE_DEFINE +# error Expected MY_MULTI_COMP_INTERFACE_DEFINE +# endif + +# ifndef MY_MUTLI_COMP_PUBLIC_DEFINE +# error Expected MY_MUTLI_COMP_PUBLIC_DEFINE +# endif + #endif #ifndef CONSUMER_LANG_CXX diff --git a/Tests/CMakeCommands/target_compile_options/main.cpp b/Tests/CMakeCommands/target_compile_options/main.cpp index 829a25e..7608400 100644 --- a/Tests/CMakeCommands/target_compile_options/main.cpp +++ b/Tests/CMakeCommands/target_compile_options/main.cpp @@ -9,12 +9,32 @@ # error Expected MY_PUBLIC_DEFINE # endif +# ifndef MY_MUTLI_COMP_PUBLIC_DEFINE +# error Expected MY_MUTLI_COMP_PUBLIC_DEFINE +# endif + # ifdef MY_INTERFACE_DEFINE # error Unexpected MY_INTERFACE_DEFINE # endif #endif +#ifdef DO_CLANG_TESTS + +# ifndef MY_PRIVATE_DEFINE +# error Expected MY_PRIVATE_DEFINE +# endif + +# ifdef MY_PUBLIC_DEFINE +# error Unexpected MY_PUBLIC_DEFINE +# endif + +# ifndef MY_MUTLI_COMP_PUBLIC_DEFINE +# error Expected MY_MUTLI_COMP_PUBLIC_DEFINE +# endif + +#endif + int main() { return 0; diff --git a/Tests/CMakeOnly/CheckLanguage/CMakeLists.txt b/Tests/CMakeOnly/CheckLanguage/CMakeLists.txt index f5336dc..ca4becb 100644 --- a/Tests/CMakeOnly/CheckLanguage/CMakeLists.txt +++ b/Tests/CMakeOnly/CheckLanguage/CMakeLists.txt @@ -7,7 +7,8 @@ set(expect_C 1) set(expect_CXX 1) unset(expect_Fortran) set(expect_NoSuchLanguage 0) -foreach(lang C CXX Fortran NoSuchLanguage) + +foreach(lang C CXX Fortran CUDA NoSuchLanguage) check_language(${lang}) if(NOT DEFINED CMAKE_${lang}_COMPILER) message(FATAL_ERROR "check_language(${lang}) did not set result") diff --git a/Tests/GeneratorExpression/CMakeLists.txt b/Tests/GeneratorExpression/CMakeLists.txt index 5ba0dc0..3ff2b85 100644 --- a/Tests/GeneratorExpression/CMakeLists.txt +++ b/Tests/GeneratorExpression/CMakeLists.txt @@ -220,6 +220,7 @@ add_custom_target(check-part3 ALL -Dtest_early_termination_2=$<$<1:>:, -Dsystem_name=${CMAKE_HOST_SYSTEM_NAME} -Dtest_platform_id=$<PLATFORM_ID> + -Dtest_platform_id_supported=$<PLATFORM_ID:Linux,Windows,Darwin> -Dtest_platform_id_Linux=$<PLATFORM_ID:Linux> -Dtest_platform_id_Windows=$<PLATFORM_ID:Windows> -Dtest_platform_id_Darwin=$<PLATFORM_ID:Darwin> diff --git a/Tests/GeneratorExpression/check-part3.cmake b/Tests/GeneratorExpression/check-part3.cmake index 9014406..4fb7308 100644 --- a/Tests/GeneratorExpression/check-part3.cmake +++ b/Tests/GeneratorExpression/check-part3.cmake @@ -28,11 +28,16 @@ check(test_early_termination_2 "$<:,") check(test_platform_id "${system_name}") foreach(system Linux Windows Darwin) if(system_name STREQUAL system) + check(test_platform_id_supported 1) check(test_platform_id_${system} 1) + set(platform_supported 1) else() check(test_platform_id_${system} 0) endif() endforeach() +if(NOT platform_supported) + check(test_platform_id_supported 0) +endif() check(lower_case "mi,xed") check(upper_case "MIX,ED") check(make_c_identifier "_4f_oo__bar__") diff --git a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake index 6c17a6a..c9d3a4d 100644 --- a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake +++ b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake @@ -68,6 +68,9 @@ run_cmake_command(cache-empty-entry ${CMAKE_COMMAND} --build ${RunCMake_SOURCE_DIR}/cache-empty-entry/) function(run_ExplicitDirs) + set(RunCMake_TEST_NO_CLEAN 1) + set(RunCMake_TEST_NO_SOURCE_DIR 1) + set(source_dir ${RunCMake_BINARY_DIR}/ExplicitDirsMissing) file(REMOVE_RECURSE "${source_dir}") @@ -76,16 +79,15 @@ function(run_ExplicitDirs) cmake_minimum_required(VERSION 3.13) project(ExplicitDirsMissing LANGUAGES NONE) ]=]) - run_cmake_command(no-S-B ${CMAKE_COMMAND} -E chdir ${source_dir} - ${CMAKE_COMMAND} -DFOO=BAR) + set(RunCMake_TEST_SOURCE_DIR "${source_dir}") + set(RunCMake_TEST_BINARY_DIR "${source_dir}") + run_cmake_with_options(no-S-B -DFOO=BAR) set(source_dir ${RunCMake_SOURCE_DIR}/ExplicitDirs) set(binary_dir ${RunCMake_BINARY_DIR}/ExplicitDirs-build) set(RunCMake_TEST_SOURCE_DIR "${source_dir}") set(RunCMake_TEST_BINARY_DIR "${binary_dir}") - set(RunCMake_TEST_NO_CLEAN 1) - set(RunCMake_TEST_NO_SOURCE_DIR 1) file(REMOVE_RECURSE "${binary_dir}") run_cmake_with_options(S-arg -S ${source_dir} ${binary_dir}) diff --git a/Tests/RunCMake/GeneratorExpression/COMPILE_LANG_AND_ID-add_custom_command-stderr.txt b/Tests/RunCMake/GeneratorExpression/COMPILE_LANG_AND_ID-add_custom_command-stderr.txt index fc3c3de..2ee96ed 100644 --- a/Tests/RunCMake/GeneratorExpression/COMPILE_LANG_AND_ID-add_custom_command-stderr.txt +++ b/Tests/RunCMake/GeneratorExpression/COMPILE_LANG_AND_ID-add_custom_command-stderr.txt @@ -3,7 +3,6 @@ CMake Error at COMPILE_LANG_AND_ID-add_custom_command.cmake:2 \(add_custom_comma \$<COMPILE_LANG_AND_ID> - \$<COMPILE_LANG_AND_ID> expression requires 2 comma separated parameters, - but got 0 instead. + \$<COMPILE_LANG_AND_ID> expression requires at least two parameters. Call Stack \(most recent call first\): CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/GeneratorExpression/COMPILE_LANG_AND_ID-target_sources-stderr.txt b/Tests/RunCMake/GeneratorExpression/COMPILE_LANG_AND_ID-target_sources-stderr.txt index 44d8684..3ecbdc3 100644 --- a/Tests/RunCMake/GeneratorExpression/COMPILE_LANG_AND_ID-target_sources-stderr.txt +++ b/Tests/RunCMake/GeneratorExpression/COMPILE_LANG_AND_ID-target_sources-stderr.txt @@ -3,7 +3,6 @@ CMake Error at COMPILE_LANG_AND_ID-target_sources.cmake:2 \(target_sources\): \$<COMPILE_LANG_AND_ID> - \$<COMPILE_LANG_AND_ID> expression requires 2 comma separated parameters, - but got 0 instead. + \$<COMPILE_LANG_AND_ID> expression requires at least two parameters. Call Stack \(most recent call first\): CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/PolicyScope/dir1/CMakeLists.txt b/Tests/RunCMake/PolicyScope/dir1/CMakeLists.txt index 16bcb36..66ff016 100644 --- a/Tests/RunCMake/PolicyScope/dir1/CMakeLists.txt +++ b/Tests/RunCMake/PolicyScope/dir1/CMakeLists.txt @@ -2,4 +2,4 @@ add_library(foo STATIC foo.cpp) string(TOLOWER ${CMAKE_CXX_COMPILER_ID} compiler_id) -target_compile_definitions(foo PRIVATE Foo=$<CXX_COMPILER_ID:${compiler_id}>) +target_compile_definitions(foo PRIVATE Foo=$<CXX_COMPILER_ID:invalid,${compiler_id}>) diff --git a/Tests/RunCMake/Swift/E.swift b/Tests/RunCMake/Swift/E.swift new file mode 100644 index 0000000..a415467 --- /dev/null +++ b/Tests/RunCMake/Swift/E.swift @@ -0,0 +1,2 @@ +func f() { +} diff --git a/Tests/RunCMake/Swift/RunCMakeTest.cmake b/Tests/RunCMake/Swift/RunCMakeTest.cmake index de99042..4817045 100644 --- a/Tests/RunCMake/Swift/RunCMakeTest.cmake +++ b/Tests/RunCMake/Swift/RunCMakeTest.cmake @@ -6,7 +6,7 @@ if(RunCMake_GENERATOR STREQUAL Xcode) endif() elseif(RunCMake_GENERATOR STREQUAL Ninja) if(CMAKE_Swift_COMPILER) - # Add Ninja-specific Swift tests here. + run_cmake(Win32ExecutableDisallowed) endif() else() run_cmake(NotSupported) diff --git a/Tests/RunCMake/Swift/Win32ExecutableDisallowed-result.txt b/Tests/RunCMake/Swift/Win32ExecutableDisallowed-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/Swift/Win32ExecutableDisallowed-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/Swift/Win32ExecutableDisallowed-stderr.txt b/Tests/RunCMake/Swift/Win32ExecutableDisallowed-stderr.txt new file mode 100644 index 0000000..d78101a --- /dev/null +++ b/Tests/RunCMake/Swift/Win32ExecutableDisallowed-stderr.txt @@ -0,0 +1,4 @@ +^CMake Error at Win32ExecutableDisallowed.cmake:[0-9]+ \(add_executable\): + WIN32_EXECUTABLE property is not supported on Swift executables +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/Swift/Win32ExecutableDisallowed.cmake b/Tests/RunCMake/Swift/Win32ExecutableDisallowed.cmake new file mode 100644 index 0000000..02d5447 --- /dev/null +++ b/Tests/RunCMake/Swift/Win32ExecutableDisallowed.cmake @@ -0,0 +1,4 @@ +enable_language(Swift) +add_executable(E E.swift) +set_target_properties(E PROPERTIES + WIN32_EXECUTABLE TRUE) diff --git a/Tests/RunCMake/Syntax/UnterminatedCall1-stderr.txt b/Tests/RunCMake/Syntax/UnterminatedCall1-stderr.txt index 3f52244..f3ee895 100644 --- a/Tests/RunCMake/Syntax/UnterminatedCall1-stderr.txt +++ b/Tests/RunCMake/Syntax/UnterminatedCall1-stderr.txt @@ -1,4 +1,4 @@ -CMake Error at UnterminatedCall1.cmake:2: +CMake Error at UnterminatedCall1.cmake:1: Parse error. Function missing ending "\)". End of file reached. Call Stack \(most recent call first\): CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/Syntax/UnterminatedCall1.cmake b/Tests/RunCMake/Syntax/UnterminatedCall1.cmake index 1166109..e1d2118 100644 --- a/Tests/RunCMake/Syntax/UnterminatedCall1.cmake +++ b/Tests/RunCMake/Syntax/UnterminatedCall1.cmake @@ -1 +1,4 @@ message( + + +message("Additional message") diff --git a/Tests/RunCMake/Syntax/UnterminatedCall2-stderr.txt b/Tests/RunCMake/Syntax/UnterminatedCall2-stderr.txt index 18656f7..04216c3 100644 --- a/Tests/RunCMake/Syntax/UnterminatedCall2-stderr.txt +++ b/Tests/RunCMake/Syntax/UnterminatedCall2-stderr.txt @@ -1,4 +1,4 @@ -CMake Error at UnterminatedCall2.cmake:4: +CMake Error at UnterminatedCall2.cmake:3: Parse error. Function missing ending "\)". End of file reached. Call Stack \(most recent call first\): CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/Syntax/UnterminatedCall2.cmake b/Tests/RunCMake/Syntax/UnterminatedCall2.cmake index 26e9e62..8d4088d 100644 --- a/Tests/RunCMake/Syntax/UnterminatedCall2.cmake +++ b/Tests/RunCMake/Syntax/UnterminatedCall2.cmake @@ -1,3 +1,6 @@ set(var "\ ") message( + + +message("Additional message") diff --git a/Tests/RunCMake/VisibilityPreset/PropertyTypo-stderr.txt b/Tests/RunCMake/VisibilityPreset/PropertyTypo-stderr.txt index ca8c33f..a63591f 100644 --- a/Tests/RunCMake/VisibilityPreset/PropertyTypo-stderr.txt +++ b/Tests/RunCMake/VisibilityPreset/PropertyTypo-stderr.txt @@ -1 +1 @@ -CMake Error: Target visibility_preset uses unsupported value \"hiden\" for CXX_VISIBILITY_PRESET +CMake Error: Target visibility_preset uses unsupported value \"hiden\" for CXX_VISIBILITY_PRESET. The supported values are: default, hidden, protected, and internal. |