From 45aa03b97aeeb512264ac2bfbb2028330be254d1 Mon Sep 17 00:00:00 2001 From: Brad King Date: Thu, 1 Dec 2016 14:24:02 -0500 Subject: try_compile: Add options to specify language standards Give `try_compile` callers a way to control the `CXX_STANDARD`, `CXX_STANDARD_REQUIRED`, and `CXX_EXTENSIONS` properties of the generated test target (or the `C` equivalents) in order to compile a test source for a particular language standard. Issue: #16456 --- Help/command/try_compile.rst | 18 ++- Help/release/dev/try_compile-lang-std.rst | 5 + Source/cmCoreTryCompile.cxx | 128 +++++++++++++++++++++ Tests/RunCMake/CMakeLists.txt | 12 ++ Tests/RunCMake/try_compile/CStandard-result.txt | 1 + Tests/RunCMake/try_compile/CStandard-stderr.txt | 7 ++ Tests/RunCMake/try_compile/CStandard.cmake | 7 ++ Tests/RunCMake/try_compile/CStandardGNU.c | 10 ++ Tests/RunCMake/try_compile/CStandardGNU.cmake | 11 ++ .../RunCMake/try_compile/CStandardNoDefault.cmake | 9 ++ Tests/RunCMake/try_compile/CxxStandard-result.txt | 1 + Tests/RunCMake/try_compile/CxxStandard-stderr.txt | 7 ++ Tests/RunCMake/try_compile/CxxStandard.cmake | 7 ++ Tests/RunCMake/try_compile/CxxStandardGNU.cmake | 11 ++ Tests/RunCMake/try_compile/CxxStandardGNU.cxx | 11 ++ .../try_compile/CxxStandardNoDefault.cmake | 9 ++ Tests/RunCMake/try_compile/RunCMakeTest.cmake | 17 +++ Tests/RunCMake/try_compile/src.cxx | 4 + 18 files changed, 274 insertions(+), 1 deletion(-) create mode 100644 Help/release/dev/try_compile-lang-std.rst create mode 100644 Tests/RunCMake/try_compile/CStandard-result.txt create mode 100644 Tests/RunCMake/try_compile/CStandard-stderr.txt create mode 100644 Tests/RunCMake/try_compile/CStandard.cmake create mode 100644 Tests/RunCMake/try_compile/CStandardGNU.c create mode 100644 Tests/RunCMake/try_compile/CStandardGNU.cmake create mode 100644 Tests/RunCMake/try_compile/CStandardNoDefault.cmake create mode 100644 Tests/RunCMake/try_compile/CxxStandard-result.txt create mode 100644 Tests/RunCMake/try_compile/CxxStandard-stderr.txt create mode 100644 Tests/RunCMake/try_compile/CxxStandard.cmake create mode 100644 Tests/RunCMake/try_compile/CxxStandardGNU.cmake create mode 100644 Tests/RunCMake/try_compile/CxxStandardGNU.cxx create mode 100644 Tests/RunCMake/try_compile/CxxStandardNoDefault.cmake create mode 100644 Tests/RunCMake/try_compile/src.cxx diff --git a/Help/command/try_compile.rst b/Help/command/try_compile.rst index 7830deb..3f16b63 100644 --- a/Help/command/try_compile.rst +++ b/Help/command/try_compile.rst @@ -35,7 +35,11 @@ Try Compiling Source Files [COMPILE_DEFINITIONS ...] [LINK_LIBRARIES ...] [OUTPUT_VARIABLE ] - [COPY_FILE [COPY_FILE_ERROR ]]) + [COPY_FILE [COPY_FILE_ERROR ]] + [_STANDARD ] + [_STANDARD_REQUIRED ] + [_EXTENSIONS ] + ) Try building an executable from one or more source files. The success or failure of the ``try_compile``, i.e. ``TRUE`` or ``FALSE`` respectively, is @@ -82,6 +86,18 @@ The options are: ``OUTPUT_VARIABLE `` Store the output from the build process the given variable. +``_STANDARD `` + Specify the :prop_tgt:`C_STANDARD` or :prop_tgt:`CXX_STANDARD` + target property of the generated project. + +``_STANDARD_REQUIRED `` + Specify the :prop_tgt:`C_STANDARD_REQUIRED` or + :prop_tgt:`CXX_STANDARD_REQUIRED` target property of the generated project. + +``_EXTENSIONS `` + Specify the :prop_tgt:`C_EXTENSIONS` or :prop_tgt:`CXX_EXTENSIONS` + target property of the generated project. + In this version all files in ``/CMakeFiles/CMakeTmp`` will be cleaned automatically. For debugging, ``--debug-trycompile`` can be passed to ``cmake`` to avoid this clean. However, multiple sequential diff --git a/Help/release/dev/try_compile-lang-std.rst b/Help/release/dev/try_compile-lang-std.rst new file mode 100644 index 0000000..64d082b --- /dev/null +++ b/Help/release/dev/try_compile-lang-std.rst @@ -0,0 +1,5 @@ +try_compile-lang-std +-------------------- + +* The :command:`try_compile` command source file signature gained new options + to specify the language standard to use in the generated test project. diff --git a/Source/cmCoreTryCompile.cxx b/Source/cmCoreTryCompile.cxx index fbad778..3b72440 100644 --- a/Source/cmCoreTryCompile.cxx +++ b/Source/cmCoreTryCompile.cxx @@ -46,6 +46,14 @@ static std::string const kCMAKE_TRY_COMPILE_PLATFORM_VARIABLES = "CMAKE_TRY_COMPILE_PLATFORM_VARIABLES"; static std::string const kCMAKE_WARN_DEPRECATED = "CMAKE_WARN_DEPRECATED"; +static void writeProperty(FILE* fout, std::string const& targetName, + std::string const& prop, std::string const& value) +{ + fprintf(fout, "set_property(TARGET %s PROPERTY %s %s)\n", targetName.c_str(), + cmOutputConverter::EscapeForCMake(prop).c_str(), + cmOutputConverter::EscapeForCMake(value).c_str()); +} + int cmCoreTryCompile::TryCompileCode(std::vector const& argv, bool isTryRun) { @@ -87,6 +95,12 @@ int cmCoreTryCompile::TryCompileCode(std::vector const& argv, std::string outputVariable; std::string copyFile; std::string copyFileError; + std::string cStandard; + std::string cxxStandard; + std::string cStandardRequired; + std::string cxxStandardRequired; + std::string cExtensions; + std::string cxxExtensions; std::vector targets; std::string libsToLink = " "; bool useOldLinkLibs = true; @@ -94,6 +108,12 @@ int cmCoreTryCompile::TryCompileCode(std::vector const& argv, bool didOutputVariable = false; bool didCopyFile = false; bool didCopyFileError = false; + bool didCStandard = false; + bool didCxxStandard = false; + bool didCStandardRequired = false; + bool didCxxStandardRequired = false; + bool didCExtensions = false; + bool didCxxExtensions = false; bool useSources = argv[2] == "SOURCES"; std::vector sources; @@ -106,6 +126,12 @@ int cmCoreTryCompile::TryCompileCode(std::vector const& argv, DoingOutputVariable, DoingCopyFile, DoingCopyFileError, + DoingCStandard, + DoingCxxStandard, + DoingCStandardRequired, + DoingCxxStandardRequired, + DoingCExtensions, + DoingCxxExtensions, DoingSources }; Doing doing = useSources ? DoingSources : DoingNone; @@ -126,6 +152,24 @@ int cmCoreTryCompile::TryCompileCode(std::vector const& argv, } else if (argv[i] == "COPY_FILE_ERROR") { doing = DoingCopyFileError; didCopyFileError = true; + } else if (argv[i] == "C_STANDARD") { + doing = DoingCStandard; + didCStandard = true; + } else if (argv[i] == "CXX_STANDARD") { + doing = DoingCxxStandard; + didCxxStandard = true; + } else if (argv[i] == "C_STANDARD_REQUIRED") { + doing = DoingCStandardRequired; + didCStandardRequired = true; + } else if (argv[i] == "CXX_STANDARD_REQUIRED") { + doing = DoingCxxStandardRequired; + didCxxStandardRequired = true; + } else if (argv[i] == "C_EXTENSIONS") { + doing = DoingCExtensions; + didCExtensions = true; + } else if (argv[i] == "CXX_EXTENSIONS") { + doing = DoingCxxExtensions; + didCxxExtensions = true; } else if (doing == DoingCMakeFlags) { cmakeFlags.push_back(argv[i]); } else if (doing == DoingCompileDefinitions) { @@ -166,6 +210,24 @@ int cmCoreTryCompile::TryCompileCode(std::vector const& argv, } else if (doing == DoingCopyFileError) { copyFileError = argv[i]; doing = DoingNone; + } else if (doing == DoingCStandard) { + cStandard = argv[i]; + doing = DoingNone; + } else if (doing == DoingCxxStandard) { + cxxStandard = argv[i]; + doing = DoingNone; + } else if (doing == DoingCStandardRequired) { + cStandardRequired = argv[i]; + doing = DoingNone; + } else if (doing == DoingCxxStandardRequired) { + cxxStandardRequired = argv[i]; + doing = DoingNone; + } else if (doing == DoingCExtensions) { + cExtensions = argv[i]; + doing = DoingNone; + } else if (doing == DoingCxxExtensions) { + cxxExtensions = argv[i]; + doing = DoingNone; } else if (doing == DoingSources) { sources.push_back(argv[i]); } else if (i == 3) { @@ -213,6 +275,42 @@ int cmCoreTryCompile::TryCompileCode(std::vector const& argv, return -1; } + if (didCStandard && !this->SrcFileSignature) { + this->Makefile->IssueMessage( + cmake::FATAL_ERROR, "C_STANDARD allowed only in source file signature."); + return -1; + } + if (didCxxStandard && !this->SrcFileSignature) { + this->Makefile->IssueMessage( + cmake::FATAL_ERROR, + "CXX_STANDARD allowed only in source file signature."); + return -1; + } + if (didCStandardRequired && !this->SrcFileSignature) { + this->Makefile->IssueMessage( + cmake::FATAL_ERROR, + "C_STANDARD_REQUIRED allowed only in source file signature."); + return -1; + } + if (didCxxStandardRequired && !this->SrcFileSignature) { + this->Makefile->IssueMessage( + cmake::FATAL_ERROR, + "CXX_STANDARD_REQUIRED allowed only in source file signature."); + return -1; + } + if (didCExtensions && !this->SrcFileSignature) { + this->Makefile->IssueMessage( + cmake::FATAL_ERROR, + "C_EXTENSIONS allowed only in source file signature."); + return -1; + } + if (didCxxExtensions && !this->SrcFileSignature) { + this->Makefile->IssueMessage( + cmake::FATAL_ERROR, + "CXX_EXTENSIONS allowed only in source file signature."); + return -1; + } + // compute the binary dir when TRY_COMPILE is called with a src file // signature if (this->SrcFileSignature) { @@ -518,6 +616,36 @@ int cmCoreTryCompile::TryCompileCode(std::vector const& argv, } } fprintf(fout, ")\n"); + + bool const testC = testLangs.find("C") != testLangs.end(); + bool const testCxx = testLangs.find("CXX") != testLangs.end(); + + if (testC) { + if (!cStandard.empty()) { + writeProperty(fout, targetName, "C_STANDARD", cStandard); + } + if (!cStandardRequired.empty()) { + writeProperty(fout, targetName, "C_STANDARD_REQUIRED", + cStandardRequired); + } + if (!cExtensions.empty()) { + writeProperty(fout, targetName, "C_EXTENSIONS", cExtensions); + } + } + + if (testCxx) { + if (!cxxStandard.empty()) { + writeProperty(fout, targetName, "CXX_STANDARD", cxxStandard); + } + if (!cxxStandardRequired.empty()) { + writeProperty(fout, targetName, "CXX_STANDARD_REQUIRED", + cxxStandardRequired); + } + if (!cxxExtensions.empty()) { + writeProperty(fout, targetName, "CXX_EXTENSIONS", cxxExtensions); + } + } + if (useOldLinkLibs) { fprintf(fout, "target_link_libraries(%s ${LINK_LIBRARIES})\n", targetName.c_str()); diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index c02b917..1b88d46 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -215,6 +215,18 @@ add_RunCMake_test(project -DCMake_TEST_RESOURCES=${CMake_TEST_RESOURCES}) add_RunCMake_test(return) add_RunCMake_test(set_property) add_RunCMake_test(string) +foreach(var + CMAKE_C_COMPILER_ID + CMAKE_C_COMPILER_VERSION + CMAKE_C_STANDARD_DEFAULT + CMAKE_CXX_COMPILER_ID + CMAKE_CXX_COMPILER_VERSION + CMAKE_CXX_STANDARD_DEFAULT + ) + if(DEFINED ${var}) + list(APPEND try_compile_ARGS -D${var}=${${var}}) + endif() +endforeach() add_RunCMake_test(try_compile) add_RunCMake_test(try_run) add_RunCMake_test(set) diff --git a/Tests/RunCMake/try_compile/CStandard-result.txt b/Tests/RunCMake/try_compile/CStandard-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/try_compile/CStandard-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/try_compile/CStandard-stderr.txt b/Tests/RunCMake/try_compile/CStandard-stderr.txt new file mode 100644 index 0000000..209afcc --- /dev/null +++ b/Tests/RunCMake/try_compile/CStandard-stderr.txt @@ -0,0 +1,7 @@ +^CMake Error at .*/Tests/RunCMake/try_compile/CStandard-build/CMakeFiles/CMakeTmp/CMakeLists.txt:[0-9]+ \(add_executable\): + C_STANDARD is set to invalid value '3' ++ +CMake Error at CStandard.cmake:[0-9]+ \(try_compile\): + Failed to generate test project build system. +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/try_compile/CStandard.cmake b/Tests/RunCMake/try_compile/CStandard.cmake new file mode 100644 index 0000000..2849ed4 --- /dev/null +++ b/Tests/RunCMake/try_compile/CStandard.cmake @@ -0,0 +1,7 @@ +enable_language(C) +try_compile(result ${CMAKE_CURRENT_BINARY_DIR} + SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src.c + C_STANDARD 3 + OUTPUT_VARIABLE out + ) +message("try_compile output:\n${out}") diff --git a/Tests/RunCMake/try_compile/CStandardGNU.c b/Tests/RunCMake/try_compile/CStandardGNU.c new file mode 100644 index 0000000..ac26c15 --- /dev/null +++ b/Tests/RunCMake/try_compile/CStandardGNU.c @@ -0,0 +1,10 @@ +#if __STDC_VERSION__ != 199901L +#error "Not GNU C 99 mode!" +#endif +#ifndef __STRICT_ANSI__ +#error "Not GNU C strict ANSI!" +#endif +int main(void) +{ + return 0; +} diff --git a/Tests/RunCMake/try_compile/CStandardGNU.cmake b/Tests/RunCMake/try_compile/CStandardGNU.cmake new file mode 100644 index 0000000..29ce315 --- /dev/null +++ b/Tests/RunCMake/try_compile/CStandardGNU.cmake @@ -0,0 +1,11 @@ +enable_language(C) +try_compile(result ${CMAKE_CURRENT_BINARY_DIR} + SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/CStandardGNU.c + C_STANDARD 99 + C_STANDARD_REQUIRED 1 + C_EXTENSIONS 0 + OUTPUT_VARIABLE out + ) +if(NOT result) + message(FATAL_ERROR "try_compile failed:\n${out}") +endif() diff --git a/Tests/RunCMake/try_compile/CStandardNoDefault.cmake b/Tests/RunCMake/try_compile/CStandardNoDefault.cmake new file mode 100644 index 0000000..97e72ea --- /dev/null +++ b/Tests/RunCMake/try_compile/CStandardNoDefault.cmake @@ -0,0 +1,9 @@ +enable_language(C) +try_compile(result ${CMAKE_CURRENT_BINARY_DIR} + SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src.c + C_STANDARD 3 # bogus, but not used + OUTPUT_VARIABLE out + ) +if(NOT result) + message(FATAL_ERROR "try_compile failed:\n${out}") +endif() diff --git a/Tests/RunCMake/try_compile/CxxStandard-result.txt b/Tests/RunCMake/try_compile/CxxStandard-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/try_compile/CxxStandard-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/try_compile/CxxStandard-stderr.txt b/Tests/RunCMake/try_compile/CxxStandard-stderr.txt new file mode 100644 index 0000000..ec7245f --- /dev/null +++ b/Tests/RunCMake/try_compile/CxxStandard-stderr.txt @@ -0,0 +1,7 @@ +^CMake Error at .*/Tests/RunCMake/try_compile/CxxStandard-build/CMakeFiles/CMakeTmp/CMakeLists.txt:[0-9]+ \(add_executable\): + CXX_STANDARD is set to invalid value '3' ++ +CMake Error at CxxStandard.cmake:[0-9]+ \(try_compile\): + Failed to generate test project build system. +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/try_compile/CxxStandard.cmake b/Tests/RunCMake/try_compile/CxxStandard.cmake new file mode 100644 index 0000000..bcb49b9 --- /dev/null +++ b/Tests/RunCMake/try_compile/CxxStandard.cmake @@ -0,0 +1,7 @@ +enable_language(CXX) +try_compile(result ${CMAKE_CURRENT_BINARY_DIR} + SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src.cxx + CXX_STANDARD 3 + OUTPUT_VARIABLE out + ) +message("try_compile output:\n${out}") diff --git a/Tests/RunCMake/try_compile/CxxStandardGNU.cmake b/Tests/RunCMake/try_compile/CxxStandardGNU.cmake new file mode 100644 index 0000000..cc16bea --- /dev/null +++ b/Tests/RunCMake/try_compile/CxxStandardGNU.cmake @@ -0,0 +1,11 @@ +enable_language(CXX) +try_compile(result ${CMAKE_CURRENT_BINARY_DIR} + SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/CxxStandardGNU.cxx + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED 1 + CXX_EXTENSIONS 0 + OUTPUT_VARIABLE out + ) +if(NOT result) + message(FATAL_ERROR "try_compile failed:\n${out}") +endif() diff --git a/Tests/RunCMake/try_compile/CxxStandardGNU.cxx b/Tests/RunCMake/try_compile/CxxStandardGNU.cxx new file mode 100644 index 0000000..7990a78 --- /dev/null +++ b/Tests/RunCMake/try_compile/CxxStandardGNU.cxx @@ -0,0 +1,11 @@ +#if __cplusplus != 201103L && \ + !(__cplusplus < 201103L && defined(__GXX_EXPERIMENTAL_CXX0X__)) +#error "Not GNU C++ 11 mode!" +#endif +#ifndef __STRICT_ANSI__ +#error "Not GNU C++ strict ANSI!" +#endif +int main() +{ + return 0; +} diff --git a/Tests/RunCMake/try_compile/CxxStandardNoDefault.cmake b/Tests/RunCMake/try_compile/CxxStandardNoDefault.cmake new file mode 100644 index 0000000..35caa9d --- /dev/null +++ b/Tests/RunCMake/try_compile/CxxStandardNoDefault.cmake @@ -0,0 +1,9 @@ +enable_language(CXX) +try_compile(result ${CMAKE_CURRENT_BINARY_DIR} + SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src.cxx + CXX_STANDARD 3 # bogus, but not used + OUTPUT_VARIABLE out + ) +if(NOT result) + message(FATAL_ERROR "try_compile failed:\n${out}") +endif() diff --git a/Tests/RunCMake/try_compile/RunCMakeTest.cmake b/Tests/RunCMake/try_compile/RunCMakeTest.cmake index 4934bcd..dadcf35 100644 --- a/Tests/RunCMake/try_compile/RunCMakeTest.cmake +++ b/Tests/RunCMake/try_compile/RunCMakeTest.cmake @@ -25,6 +25,23 @@ run_cmake(TargetTypeExe) run_cmake(TargetTypeInvalid) run_cmake(TargetTypeStatic) +if(CMAKE_C_STANDARD_DEFAULT) + run_cmake(CStandard) +elseif(DEFINED CMAKE_C_STANDARD_DEFAULT) + run_cmake(CStandardNoDefault) +endif() +if(CMAKE_CXX_STANDARD_DEFAULT) + run_cmake(CxxStandard) +elseif(DEFINED CMAKE_CXX_STANDARD_DEFAULT) + run_cmake(CxxStandardNoDefault) +endif() +if(CMAKE_C_COMPILER_ID STREQUAL "GNU" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.4) + run_cmake(CStandardGNU) +endif() +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.4) + run_cmake(CxxStandardGNU) +endif() + run_cmake(CMP0056) run_cmake(CMP0066) diff --git a/Tests/RunCMake/try_compile/src.cxx b/Tests/RunCMake/try_compile/src.cxx new file mode 100644 index 0000000..f8b643a --- /dev/null +++ b/Tests/RunCMake/try_compile/src.cxx @@ -0,0 +1,4 @@ +int main() +{ + return 0; +} -- cgit v0.12