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
This commit is contained in:
Brad King
2016-12-01 14:24:02 -05:00
parent 3bb2051eef
commit 45aa03b97a
18 changed files with 274 additions and 1 deletions

View File

@@ -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

View File

@@ -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.

View File

@@ -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());

View File

@@ -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)

View File

@@ -0,0 +1 @@
1

View File

@@ -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\)$

View File

@@ -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}")

View File

@@ -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;
}

View File

@@ -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()

View File

@@ -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()

View File

@@ -0,0 +1 @@
1

View File

@@ -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\)$

View File

@@ -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}")

View File

@@ -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()

View File

@@ -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;
}

View File

@@ -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()

View File

@@ -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)

View File

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