1
0
Эх сурвалжийг харах

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
Brad King 9 жил өмнө
parent
commit
45aa03b97a

+ 17 - 1
Help/command/try_compile.rst

@@ -35,7 +35,11 @@ Try Compiling Source Files
               [COMPILE_DEFINITIONS <defs>...]
               [LINK_LIBRARIES <libs>...]
               [OUTPUT_VARIABLE <var>]
-              [COPY_FILE <fileName> [COPY_FILE_ERROR <var>]])
+              [COPY_FILE <fileName> [COPY_FILE_ERROR <var>]]
+              [<LANG>_STANDARD <std>]
+              [<LANG>_STANDARD_REQUIRED <bool>]
+              [<LANG>_EXTENSIONS <bool>]
+              )
 
 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 <var>``
   Store the output from the build process the given variable.
 
+``<LANG>_STANDARD <std>``
+  Specify the :prop_tgt:`C_STANDARD` or :prop_tgt:`CXX_STANDARD`
+  target property of the generated project.
+
+``<LANG>_STANDARD_REQUIRED <bool>``
+  Specify the :prop_tgt:`C_STANDARD_REQUIRED` or
+  :prop_tgt:`CXX_STANDARD_REQUIRED` target property of the generated project.
+
+``<LANG>_EXTENSIONS <bool>``
+  Specify the :prop_tgt:`C_EXTENSIONS` or :prop_tgt:`CXX_EXTENSIONS`
+  target property of the generated project.
+
 In this version all files in ``<bindir>/CMakeFiles/CMakeTmp`` will be
 cleaned automatically.  For debugging, ``--debug-trycompile`` can be
 passed to ``cmake`` to avoid this clean.  However, multiple sequential

+ 5 - 0
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.

+ 128 - 0
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<std::string> const& argv,
                                      bool isTryRun)
 {
@@ -87,6 +95,12 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> 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<std::string> targets;
   std::string libsToLink = " ";
   bool useOldLinkLibs = true;
@@ -94,6 +108,12 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> 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<std::string> sources;
 
@@ -106,6 +126,12 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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());

+ 12 - 0
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)

+ 1 - 0
Tests/RunCMake/try_compile/CStandard-result.txt

@@ -0,0 +1 @@
+1

+ 7 - 0
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\)$

+ 7 - 0
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}")

+ 10 - 0
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;
+}

+ 11 - 0
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()

+ 9 - 0
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()

+ 1 - 0
Tests/RunCMake/try_compile/CxxStandard-result.txt

@@ -0,0 +1 @@
+1

+ 7 - 0
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\)$

+ 7 - 0
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}")

+ 11 - 0
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()

+ 11 - 0
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;
+}

+ 9 - 0
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()

+ 17 - 0
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)
 

+ 4 - 0
Tests/RunCMake/try_compile/src.cxx

@@ -0,0 +1,4 @@
+int main()
+{
+  return 0;
+}