diff --git a/Source/cmDocumentGeneratorExpressions.h b/Source/cmDocumentGeneratorExpressions.h index 7358a36506..841061cd47 100644 --- a/Source/cmDocumentGeneratorExpressions.h +++ b/Source/cmDocumentGeneratorExpressions.h @@ -54,6 +54,13 @@ "else '0'.\n" \ " $ = '1' if v1 is the same version as v2, " \ "else '0'.\n" \ + " $ = The version of the C compiler used.\n" \ + " $ = '1' if the version of the C " \ + "compiler matches ver, otherwise '0'.\n" \ + " $ = The version of the CXX compiler " \ + "used.\n" \ + " $ = '1' if the version of the CXX " \ + "compiler matches ver, otherwise '0'.\n" \ " $ = main file (.exe, .so.1.2, .a)\n" \ " $ = file used to link (.a, .lib, .so)\n" \ " $ = file with soname (.so.3)\n" \ diff --git a/Source/cmGeneratorExpressionEvaluator.cxx b/Source/cmGeneratorExpressionEvaluator.cxx index 037ef31d75..05bbc1ce81 100644 --- a/Source/cmGeneratorExpressionEvaluator.cxx +++ b/Source/cmGeneratorExpressionEvaluator.cxx @@ -341,6 +341,102 @@ static const struct CXXCompilerIdNode : public CompilerIdNode } } cxxCompilerIdNode; +//---------------------------------------------------------------------------- +struct CompilerVersionNode : public cmGeneratorExpressionNode +{ + CompilerVersionNode() {} + + virtual int NumExpectedParameters() const { return ZeroOrMoreParameters; } + + std::string EvaluateWithLanguage(const std::vector ¶meters, + cmGeneratorExpressionContext *context, + const GeneratorExpressionContent *content, + cmGeneratorExpressionDAGChecker *, + const std::string &lang) const + { + const char *compilerVersion = context->Makefile ? + context->Makefile->GetSafeDefinition(( + "CMAKE_" + lang + "_COMPILER_VERSION").c_str()) : ""; + if (parameters.size() == 0) + { + return compilerVersion ? compilerVersion : ""; + } + + cmsys::RegularExpression compilerIdValidator; + compilerIdValidator.compile("^[0-9\\.]*$"); + if (!compilerIdValidator.find(parameters.begin()->c_str())) + { + reportError(context, content->GetOriginalExpression(), + "Expression syntax not recognized."); + return std::string(); + } + if (!compilerVersion) + { + return parameters.front().empty() ? "1" : "0"; + } + + return cmSystemTools::VersionCompare(cmSystemTools::OP_EQUAL, + parameters.begin()->c_str(), + compilerVersion) ? "1" : "0"; + } +}; + +//---------------------------------------------------------------------------- +static const struct CCompilerVersionNode : public CompilerVersionNode +{ + CCompilerVersionNode() {} + + std::string Evaluate(const std::vector ¶meters, + cmGeneratorExpressionContext *context, + const GeneratorExpressionContent *content, + cmGeneratorExpressionDAGChecker *dagChecker) const + { + if (parameters.size() != 0 && parameters.size() != 1) + { + reportError(context, content->GetOriginalExpression(), + "$ expression requires one or two parameters"); + return std::string(); + } + if (!context->HeadTarget) + { + reportError(context, content->GetOriginalExpression(), + "$ may only be used with targets. It may not " + "be used with add_custom_command."); + } + return this->EvaluateWithLanguage(parameters, context, content, + dagChecker, "C"); + } +} cCompilerVersionNode; + +//---------------------------------------------------------------------------- +static const struct CxxCompilerVersionNode : public CompilerVersionNode +{ + CxxCompilerVersionNode() {} + + std::string Evaluate(const std::vector ¶meters, + cmGeneratorExpressionContext *context, + const GeneratorExpressionContent *content, + cmGeneratorExpressionDAGChecker *dagChecker) const + { + if (parameters.size() != 0 && parameters.size() != 1) + { + reportError(context, content->GetOriginalExpression(), + "$ expression requires one or two " + "parameters"); + return std::string(); + } + if (!context->HeadTarget) + { + reportError(context, content->GetOriginalExpression(), + "$ may only be used with targets. It may " + "not be used with add_custom_command."); + } + return this->EvaluateWithLanguage(parameters, context, content, + dagChecker, "CXX"); + } +} cxxCompilerVersionNode; + + //---------------------------------------------------------------------------- static const struct VersionGreaterNode : public cmGeneratorExpressionNode { @@ -1247,6 +1343,10 @@ cmGeneratorExpressionNode* GetNode(const std::string &identifier) return &versionLessNode; else if (identifier == "VERSION_EQUAL") return &versionEqualNode; + else if (identifier == "C_COMPILER_VERSION") + return &cCompilerVersionNode; + else if (identifier == "CXX_COMPILER_VERSION") + return &cxxCompilerVersionNode; else if (identifier == "CONFIGURATION") return &configurationNode; else if (identifier == "CONFIG") diff --git a/Tests/CompileOptions/CMakeLists.txt b/Tests/CompileOptions/CMakeLists.txt index 52c3759f6a..9b6c9c2a0e 100644 --- a/Tests/CompileOptions/CMakeLists.txt +++ b/Tests/CompileOptions/CMakeLists.txt @@ -5,11 +5,23 @@ project(CompileOptions) add_library(testlib other.cpp) add_executable(CompileOptions main.cpp) + +macro(get_compiler_test_genex lst lang) + list(APPEND ${lst} -DTEST_${lang}_COMPILER_VERSION="$<${lang}_COMPILER_VERSION>") + list(APPEND ${lst} -DTEST_${lang}_COMPILER_VERSION_EQUALITY=$<${lang}_COMPILER_VERSION:${CMAKE_${lang}_COMPILER_VERSION}>) +endmacro() + +get_compiler_test_genex(c_tests C) +get_compiler_test_genex(cxx_tests CXX) + set_property(TARGET CompileOptions PROPERTY COMPILE_OPTIONS "-DTEST_DEFINE" "-DNEEDS_ESCAPE=\"E$CAPE\"" "$<$:-DTEST_DEFINE_GNU>" + ${c_tests} + ${cxx_tests} ) + target_link_libraries(CompileOptions testlib) if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") @@ -18,3 +30,9 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") "DO_GNU_TESTS" ) endif() + +target_compile_definitions(CompileOptions + PRIVATE + "EXPECTED_C_COMPILER_VERSION=\"${CMAKE_C_COMPILER_VERSION}\"" + "EXPECTED_CXX_COMPILER_VERSION=\"${CMAKE_CXX_COMPILER_VERSION}\"" +) diff --git a/Tests/CompileOptions/main.cpp b/Tests/CompileOptions/main.cpp index 90740f1431..42f4cca119 100644 --- a/Tests/CompileOptions/main.cpp +++ b/Tests/CompileOptions/main.cpp @@ -16,5 +16,9 @@ int main() { - return strcmp(NEEDS_ESCAPE, "E$CAPE") == 0 ? 0 : 1; + return (strcmp(NEEDS_ESCAPE, "E$CAPE") == 0 + && strcmp(EXPECTED_C_COMPILER_VERSION, TEST_C_COMPILER_VERSION) == 0 + && strcmp(EXPECTED_CXX_COMPILER_VERSION, TEST_CXX_COMPILER_VERSION) == 0 + && TEST_C_COMPILER_VERSION_EQUALITY == 1 + && TEST_CXX_COMPILER_VERSION_EQUALITY == 1) ? 0 : 1; }