From cb14ae2b870e9568d18990992882e0facacf22d4 Mon Sep 17 00:00:00 2001 From: Matthew Woehlke Date: Wed, 21 Sep 2022 13:34:02 -0400 Subject: try_compile: Add SOURCE_FROM_{ARG,VAR} Add ability to "feed" try_compile (and try_run) sources more directly, either from literal content, or from a CMake variable which contains literal content. This saves the user from needing a separate step to write the content to a file, and allows for the sources to only exist in the scratch directory. --- Help/command/try_compile.rst | 36 +++++++-- Help/command/try_run.rst | 8 +- Source/cmCoreTryCompile.cxx | 92 ++++++++++++++++++++-- Source/cmCoreTryCompile.h | 7 ++ Tests/RunCMake/try_compile/RunCMakeTest.cmake | 4 + .../try_compile/SourceFromBadName-result.txt | 1 + .../try_compile/SourceFromBadName-stderr.txt | 4 + Tests/RunCMake/try_compile/SourceFromBadName.cmake | 1 + .../try_compile/SourceFromOneArg-result.txt | 1 + .../try_compile/SourceFromOneArg-stderr.txt | 4 + Tests/RunCMake/try_compile/SourceFromOneArg.cmake | 1 + .../try_compile/SourceFromThreeArgs-result.txt | 1 + .../try_compile/SourceFromThreeArgs-stderr.txt | 4 + .../RunCMake/try_compile/SourceFromThreeArgs.cmake | 1 + Tests/TryCompile/CMakeLists.txt | 42 ++++++++++ 15 files changed, 193 insertions(+), 14 deletions(-) create mode 100644 Tests/RunCMake/try_compile/SourceFromBadName-result.txt create mode 100644 Tests/RunCMake/try_compile/SourceFromBadName-stderr.txt create mode 100644 Tests/RunCMake/try_compile/SourceFromBadName.cmake create mode 100644 Tests/RunCMake/try_compile/SourceFromOneArg-result.txt create mode 100644 Tests/RunCMake/try_compile/SourceFromOneArg-stderr.txt create mode 100644 Tests/RunCMake/try_compile/SourceFromOneArg.cmake create mode 100644 Tests/RunCMake/try_compile/SourceFromThreeArgs-result.txt create mode 100644 Tests/RunCMake/try_compile/SourceFromThreeArgs-stderr.txt create mode 100644 Tests/RunCMake/try_compile/SourceFromThreeArgs.cmake diff --git a/Help/command/try_compile.rst b/Help/command/try_compile.rst index 97ad481..ed61a2f 100644 --- a/Help/command/try_compile.rst +++ b/Help/command/try_compile.rst @@ -55,7 +55,10 @@ Try Compiling Source Files .. code-block:: cmake - try_compile( SOURCES + try_compile( + ] | + SOURCE_FROM_ARG ] | + SOURCE_FROM_VAR ] >... [CMAKE_FLAGS ...] [COMPILE_DEFINITIONS ...] [LINK_OPTIONS ...] @@ -74,10 +77,12 @@ Try building an executable or static library from one or more source files variable). The success or failure of the ``try_compile``, i.e. ``TRUE`` or ``FALSE`` respectively, is returned in ````. -In this form, one or more source files must be provided. If -:variable:`CMAKE_TRY_COMPILE_TARGET_TYPE` is unset or is set to ``EXECUTABLE``, -the sources must include a definition for ``main`` and CMake will create a -``CMakeLists.txt`` file to build the source(s) as an executable. +In this form, one or more source files must be provided. Additionally, one of +``SOURCES`` and/or ``SOURCE_FROM_*`` must precede other keywords. + +If :variable:`CMAKE_TRY_COMPILE_TARGET_TYPE` is unset or is set to +``EXECUTABLE``, the sources must include a definition for ``main`` and CMake +will create a ``CMakeLists.txt`` file to build the source(s) as an executable. If :variable:`CMAKE_TRY_COMPILE_TARGET_TYPE` is set to ``STATIC_LIBRARY``, a static library will be built instead and no definition for ``main`` is required. For an executable, the generated ``CMakeLists.txt`` file would @@ -163,6 +168,27 @@ The options are: ``OUTPUT_VARIABLE `` Store the output from the build process in the given variable. +``SOURCE_FROM_ARG `` + .. versionadded:: 3.25 + + Write ```` to a file named ```` in the operation directory. + This can be used to bypass the need to separately write a source file when + the contents of the file are dynamically specified. The specified ```` + is not allowed to contain path components. + + ``SOURCE_FROM_ARG`` may be specified multiple times. + +``SOURCE_FROM_VAR `` + .. versionadded:: 3.25 + + Write the contents of ```` to a file named ```` in the operation + directory. This is the same as ``SOURCE_FROM_ARG``, but takes the contents + from the specified CMake variable, rather than directly, which may be useful + when passing arguments through a function which wraps ``try_compile``. The + specified ```` is not allowed to contain path components. + + ``SOURCE_FROM_VAR`` may be specified multiple times. + ``_STANDARD `` .. versionadded:: 3.8 diff --git a/Help/command/try_run.rst b/Help/command/try_run.rst index 1dc2355..5498344 100644 --- a/Help/command/try_run.rst +++ b/Help/command/try_run.rst @@ -12,7 +12,10 @@ Try Compiling and Running Source Files .. code-block:: cmake - try_run( SOURCES + try_run( + ] | + SOURCE_FROM_ARG ] | + SOURCE_FROM_VAR ] >... [CMAKE_FLAGS ...] [COMPILE_DEFINITIONS ...] [LINK_OPTIONS ...] @@ -40,6 +43,9 @@ set to ``FAILED_TO_RUN``. See the :command:`try_compile` command for documentation of options common to both commands, and for information on how the test project is constructed to build the source file. +One or more source files must be provided. Additionally, one of ``SOURCES`` +and/or ``SOURCE_FROM_*`` must precede other keywords. + This command also supports an alternate signature which was present in older versions of CMake: diff --git a/Source/cmCoreTryCompile.cxx b/Source/cmCoreTryCompile.cxx index 2f63b3c..0ef32c0 100644 --- a/Source/cmCoreTryCompile.cxx +++ b/Source/cmCoreTryCompile.cxx @@ -153,7 +153,7 @@ auto const TryCompileBaseArgParser = .Bind("__CMAKE_INTERNAL"_s, &Arguments::CMakeInternal) /* keep semicolon on own line */; -auto const TryCompileBaseNonProjectArgParser = +auto const TryCompileBaseSourcesArgParser = cmArgumentParser{ TryCompileBaseArgParser } .Bind("SOURCES"_s, &Arguments::Sources) .Bind("COMPILE_DEFINITIONS"_s, TryCompileCompileDefs, @@ -170,6 +170,12 @@ auto const TryCompileBaseNonProjectArgParser = .BIND_LANG_PROPS(OBJCXX) /* keep semicolon on own line */; +auto const TryCompileBaseNewSourcesArgParser = + cmArgumentParser{ TryCompileBaseSourcesArgParser } + .Bind("SOURCE_FROM_ARG"_s, &Arguments::SourceFromArg) + .Bind("SOURCE_FROM_VAR"_s, &Arguments::SourceFromVar) + /* keep semicolon on own line */; + auto const TryCompileBaseProjectArgParser = cmArgumentParser{ TryCompileBaseArgParser } .Bind("PROJECT"_s, &Arguments::ProjectName) @@ -182,10 +188,10 @@ auto const TryCompileProjectArgParser = makeTryCompileParser(TryCompileBaseProjectArgParser); auto const TryCompileSourcesArgParser = - makeTryCompileParser(TryCompileBaseNonProjectArgParser); + makeTryCompileParser(TryCompileBaseNewSourcesArgParser); auto const TryCompileOldArgParser = - makeTryCompileParser(TryCompileBaseNonProjectArgParser) + makeTryCompileParser(TryCompileBaseSourcesArgParser) .Bind(1, &Arguments::BinaryDirectory) .Bind(2, &Arguments::SourceDirectoryOrFile) .Bind(3, &Arguments::ProjectName) @@ -196,7 +202,7 @@ auto const TryRunProjectArgParser = makeTryRunParser(TryCompileBaseProjectArgParser); auto const TryRunSourcesArgParser = - makeTryRunParser(TryCompileBaseNonProjectArgParser); + makeTryRunParser(TryCompileBaseNewSourcesArgParser); auto const TryRunOldArgParser = makeTryRunParser(TryCompileOldArgParser); @@ -397,8 +403,21 @@ bool cmCoreTryCompile::TryCompileCode(Arguments& arguments, return false; } - // only valid for srcfile signatures - if (!this->SrcFileSignature) { + if (this->SrcFileSignature) { + if (arguments.SourceFromArg && arguments.SourceFromArg->size() % 2) { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + "SOURCE_FROM_ARG requires exactly two arguments"); + return false; + } + if (arguments.SourceFromVar && arguments.SourceFromVar->size() % 2) { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + "SOURCE_FROM_VAR requires exactly two arguments"); + return false; + } + } else { + // only valid for srcfile signatures if (!arguments.LangProps.empty()) { this->Makefile->IssueMessage( MessageType::FATAL_ERROR, @@ -419,6 +438,7 @@ bool cmCoreTryCompile::TryCompileCode(Arguments& arguments, return false; } } + // make sure the binary directory exists if (useUniqueBinaryDirectory) { this->BinaryDirectory = @@ -449,10 +469,35 @@ bool cmCoreTryCompile::TryCompileCode(Arguments& arguments, std::vector sources; if (arguments.Sources) { sources = std::move(*arguments.Sources); - } else { - // TODO: ensure SourceDirectoryOrFile has a value + } else if (arguments.SourceDirectoryOrFile) { sources.emplace_back(*arguments.SourceDirectoryOrFile); } + if (arguments.SourceFromArg) { + auto const k = arguments.SourceFromArg->size(); + for (auto i = decltype(k){ 0 }; i < k; i += 2) { + const auto& name = (*arguments.SourceFromArg)[i + 0]; + const auto& content = (*arguments.SourceFromArg)[i + 1]; + auto out = this->WriteSource(name, content, "SOURCES_FROM_ARG"); + if (out.empty()) { + return false; + } + sources.emplace_back(std::move(out)); + } + } + if (arguments.SourceFromVar) { + auto const k = arguments.SourceFromVar->size(); + for (auto i = decltype(k){ 0 }; i < k; i += 2) { + const auto& name = (*arguments.SourceFromVar)[i + 0]; + const auto& var = (*arguments.SourceFromVar)[i + 1]; + const auto& content = this->Makefile->GetDefinition(var); + auto out = this->WriteSource(name, content, "SOURCES_FROM_VAR"); + if (out.empty()) { + return false; + } + sources.emplace_back(std::move(out)); + } + } + // TODO: ensure sources is not empty // Detect languages to enable. cmGlobalGenerator* gg = this->Makefile->GetGlobalGenerator(); @@ -1132,3 +1177,34 @@ void cmCoreTryCompile::FindOutputFile(const std::string& targetName) this->OutputFile = cmSystemTools::CollapseFullPath(outputFileLocation); } + +std::string cmCoreTryCompile::WriteSource(std::string const& filename, + std::string const& content, + char const* command) const +{ + if (!cmSystemTools::GetFilenamePath(filename).empty()) { + const auto& msg = + cmStrCat(command, " given invalid filename \"", filename, "\""); + this->Makefile->IssueMessage(MessageType::FATAL_ERROR, msg); + return {}; + } + + auto filepath = cmStrCat(this->BinaryDirectory, "/", filename); + cmsys::ofstream file{ filepath.c_str(), std::ios::out }; + if (!file) { + const auto& msg = + cmStrCat(command, " failed to open \"", filename, "\" for writing"); + this->Makefile->IssueMessage(MessageType::FATAL_ERROR, msg); + return {}; + } + + file << content; + if (!file) { + const auto& msg = cmStrCat(command, " failed to write \"", filename, "\""); + this->Makefile->IssueMessage(MessageType::FATAL_ERROR, msg); + return {}; + } + + file.close(); + return filepath; +} diff --git a/Source/cmCoreTryCompile.h b/Source/cmCoreTryCompile.h index 729aa9f..75dcf6e 100644 --- a/Source/cmCoreTryCompile.h +++ b/Source/cmCoreTryCompile.h @@ -40,6 +40,10 @@ public: cm::optional ProjectName; cm::optional TargetName; cm::optional>> Sources; + cm::optional>> + SourceFromArg; + cm::optional>> + SourceFromVar; ArgumentParser::MaybeEmpty> CMakeFlags{ 1, "CMAKE_FLAGS" }; // fake argv[0] @@ -103,6 +107,9 @@ public: cmMakefile* Makefile; private: + std::string WriteSource(std::string const& name, std::string const& content, + char const* command) const; + Arguments ParseArgs( const cmRange::const_iterator>& args, const cmArgumentParser& parser, diff --git a/Tests/RunCMake/try_compile/RunCMakeTest.cmake b/Tests/RunCMake/try_compile/RunCMakeTest.cmake index bb11a57..0aa9039 100644 --- a/Tests/RunCMake/try_compile/RunCMakeTest.cmake +++ b/Tests/RunCMake/try_compile/RunCMakeTest.cmake @@ -20,6 +20,10 @@ set(RunCMake_TEST_OPTIONS -Dtry_compile_DEFS=new_signature.cmake) include(${RunCMake_SOURCE_DIR}/old_and_new_signature_tests.cmake) unset(RunCMake_TEST_OPTIONS) +run_cmake(SourceFromOneArg) +run_cmake(SourceFromThreeArgs) +run_cmake(SourceFromBadName) + run_cmake(ProjectCopyFile) run_cmake(NonSourceCopyFile) run_cmake(NonSourceCompileDefinitions) diff --git a/Tests/RunCMake/try_compile/SourceFromBadName-result.txt b/Tests/RunCMake/try_compile/SourceFromBadName-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/try_compile/SourceFromBadName-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/try_compile/SourceFromBadName-stderr.txt b/Tests/RunCMake/try_compile/SourceFromBadName-stderr.txt new file mode 100644 index 0000000..ef6847c --- /dev/null +++ b/Tests/RunCMake/try_compile/SourceFromBadName-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at SourceFromBadName.cmake:[0-9]+ \(try_compile\): + SOURCES_FROM_ARG given invalid filename "bad/name.c" +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/try_compile/SourceFromBadName.cmake b/Tests/RunCMake/try_compile/SourceFromBadName.cmake new file mode 100644 index 0000000..e53a73f --- /dev/null +++ b/Tests/RunCMake/try_compile/SourceFromBadName.cmake @@ -0,0 +1 @@ +try_compile(RESULT SOURCE_FROM_ARG bad/name.c "int main();") diff --git a/Tests/RunCMake/try_compile/SourceFromOneArg-result.txt b/Tests/RunCMake/try_compile/SourceFromOneArg-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/try_compile/SourceFromOneArg-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/try_compile/SourceFromOneArg-stderr.txt b/Tests/RunCMake/try_compile/SourceFromOneArg-stderr.txt new file mode 100644 index 0000000..bebe8bb --- /dev/null +++ b/Tests/RunCMake/try_compile/SourceFromOneArg-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at SourceFromOneArg.cmake:[0-9]+ \(try_compile\): + SOURCE_FROM_ARG requires exactly two arguments +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/try_compile/SourceFromOneArg.cmake b/Tests/RunCMake/try_compile/SourceFromOneArg.cmake new file mode 100644 index 0000000..39ca11e --- /dev/null +++ b/Tests/RunCMake/try_compile/SourceFromOneArg.cmake @@ -0,0 +1 @@ +try_compile(RESULT SOURCE_FROM_ARG test.c) diff --git a/Tests/RunCMake/try_compile/SourceFromThreeArgs-result.txt b/Tests/RunCMake/try_compile/SourceFromThreeArgs-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/try_compile/SourceFromThreeArgs-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/try_compile/SourceFromThreeArgs-stderr.txt b/Tests/RunCMake/try_compile/SourceFromThreeArgs-stderr.txt new file mode 100644 index 0000000..2cf201d --- /dev/null +++ b/Tests/RunCMake/try_compile/SourceFromThreeArgs-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at SourceFromThreeArgs.cmake:[0-9]+ \(try_compile\): + SOURCE_FROM_ARG requires exactly two arguments +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/try_compile/SourceFromThreeArgs.cmake b/Tests/RunCMake/try_compile/SourceFromThreeArgs.cmake new file mode 100644 index 0000000..af6340d --- /dev/null +++ b/Tests/RunCMake/try_compile/SourceFromThreeArgs.cmake @@ -0,0 +1 @@ +try_compile(RESULT SOURCE_FROM_ARG test.c "int" "main();") diff --git a/Tests/TryCompile/CMakeLists.txt b/Tests/TryCompile/CMakeLists.txt index 8ebb00a..9560190 100644 --- a/Tests/TryCompile/CMakeLists.txt +++ b/Tests/TryCompile/CMakeLists.txt @@ -64,6 +64,48 @@ set(try_compile_compile_output_var COMPILE_OUT) set(try_compile_run_output_var RUN_OUTPUT) include(old_and_new_signature_tests.cmake) +# try to compile an empty source specified directly +try_compile(SHOULD_FAIL_DUE_TO_EMPTY_SOURCE + SOURCE_FROM_ARG empty.c "") +if(SHOULD_FAIL_DUE_TO_EMPTY_SOURCE) + message(SEND_ERROR "Trying to compile an empty source succeeded?") +endif() + +try_compile(SHOULD_FAIL_DUE_TO_EMPTY_SOURCE + SOURCE_FROM_VAR empty.c NAME_OF_A_VAR_THAT_IS_NOT_SET) +if(SHOULD_FAIL_DUE_TO_EMPTY_SOURCE) + message(SEND_ERROR "Trying to compile an empty source succeeded?") +endif() + +# try to run a source specified directly +set(TRY_RUN_MAIN_CODE + "extern int answer(); \n" + "int main() { return answer(); }\n") +set(TRY_RUN_EXT_CODE + "int answer() { return 42; }\n") + +try_run(SHOULD_EXIT_WITH_ERROR SHOULD_COMPILE + SOURCE_FROM_ARG main.c "${TRY_RUN_MAIN_CODE}" + SOURCE_FROM_ARG answer.c "${TRY_RUN_EXT_CODE}" + COMPILE_OUTPUT_VARIABLE COMPILE_OUTPUT) +if(NOT SHOULD_COMPILE) + message(SEND_ERROR " SOURCE_FROM_ARG failed compiling: ${COMPILE_OUTPUT}") +endif() +if(NOT SHOULD_EXIT_WITH_ERROR EQUAL 42) + message(SEND_ERROR " SOURCE_FROM_ARG gave unexpected run result: ${SHOULD_EXIT_WITH_ERROR}") +endif() + +try_run(SHOULD_EXIT_WITH_ERROR SHOULD_COMPILE + SOURCE_FROM_VAR main.c TRY_RUN_MAIN_CODE + SOURCE_FROM_VAR answer.c TRY_RUN_EXT_CODE + COMPILE_OUTPUT_VARIABLE COMPILE_OUTPUT) +if(NOT SHOULD_COMPILE) + message(SEND_ERROR " SOURCE_FROM_VAR failed compiling: ${COMPILE_OUTPUT}") +endif() +if(NOT SHOULD_EXIT_WITH_ERROR EQUAL 42) + message(SEND_ERROR " SOURCE_FROM_VAR gave unexpected run result: ${SHOULD_EXIT_WITH_ERROR}") +endif() + # try to compile a project (old signature) message("Testing try_compile project mode (old signature)") try_compile(TEST_INNER -- cgit v0.12 From a04eaf6742d98dd415ee939eae8c955c8afd1ce6 Mon Sep 17 00:00:00 2001 From: Matthew Woehlke Date: Thu, 22 Sep 2022 13:26:01 -0400 Subject: Tests: Clean up and simplify TryCompile tests Add and use some additional helper macros to simplify repetitive checks. Use existing macros in more places. Tweak macros to improve output in case of failure. --- Tests/TryCompile/CMakeLists.txt | 32 +++++++++-------- Tests/TryCompile/exit_with_error.c | 2 +- Tests/TryCompile/old_and_new_signature_tests.cmake | 41 ++++++---------------- 3 files changed, 29 insertions(+), 46 deletions(-) diff --git a/Tests/TryCompile/CMakeLists.txt b/Tests/TryCompile/CMakeLists.txt index 9560190..cca19bc 100644 --- a/Tests/TryCompile/CMakeLists.txt +++ b/Tests/TryCompile/CMakeLists.txt @@ -6,13 +6,25 @@ project(TryCompile) macro(EXPECT_PASS var out) if(NOT ${var}) - message(SEND_ERROR "Should pass failed ${out}") + message(SEND_ERROR "Should pass failed:\n${out}") endif() endmacro() macro(EXPECT_FAIL var out) if(${var}) - message(SEND_ERROR "Should fail passed ${out}") + message(SEND_ERROR "Should fail passed:\n${out}") + endif() +endmacro() + +macro(EXPECT_COMPILED name var out) + if(NOT ${var}) + message(SEND_ERROR "${name} failed compiling:\n${out}") + endif() +endmacro() + +macro(EXPECT_RUN_RESULT name var expected) + if(NOT ${var} EQUAL ${expected}) + message(SEND_ERROR " ${name} gave unexpected run result: ${${var}} expected: ${expected}") endif() endmacro() @@ -88,23 +100,15 @@ try_run(SHOULD_EXIT_WITH_ERROR SHOULD_COMPILE SOURCE_FROM_ARG main.c "${TRY_RUN_MAIN_CODE}" SOURCE_FROM_ARG answer.c "${TRY_RUN_EXT_CODE}" COMPILE_OUTPUT_VARIABLE COMPILE_OUTPUT) -if(NOT SHOULD_COMPILE) - message(SEND_ERROR " SOURCE_FROM_ARG failed compiling: ${COMPILE_OUTPUT}") -endif() -if(NOT SHOULD_EXIT_WITH_ERROR EQUAL 42) - message(SEND_ERROR " SOURCE_FROM_ARG gave unexpected run result: ${SHOULD_EXIT_WITH_ERROR}") -endif() +EXPECT_COMPILED("SOURCE_FROM_ARG" SHOULD_COMPILE "${COMPILE_OUTPUT}") +EXPECT_RUN_RESULT("SOURCE_FROM_ARG" SHOULD_EXIT_WITH_ERROR 42) try_run(SHOULD_EXIT_WITH_ERROR SHOULD_COMPILE SOURCE_FROM_VAR main.c TRY_RUN_MAIN_CODE SOURCE_FROM_VAR answer.c TRY_RUN_EXT_CODE COMPILE_OUTPUT_VARIABLE COMPILE_OUTPUT) -if(NOT SHOULD_COMPILE) - message(SEND_ERROR " SOURCE_FROM_VAR failed compiling: ${COMPILE_OUTPUT}") -endif() -if(NOT SHOULD_EXIT_WITH_ERROR EQUAL 42) - message(SEND_ERROR " SOURCE_FROM_VAR gave unexpected run result: ${SHOULD_EXIT_WITH_ERROR}") -endif() +EXPECT_COMPILED("SOURCE_FROM_VAR" SHOULD_COMPILE "${COMPILE_OUTPUT}") +EXPECT_RUN_RESULT("SOURCE_FROM_VAR" SHOULD_EXIT_WITH_ERROR 42) # try to compile a project (old signature) message("Testing try_compile project mode (old signature)") diff --git a/Tests/TryCompile/exit_with_error.c b/Tests/TryCompile/exit_with_error.c index f3c523d..dbddcf5 100644 --- a/Tests/TryCompile/exit_with_error.c +++ b/Tests/TryCompile/exit_with_error.c @@ -3,5 +3,5 @@ int main() { printf("hello world\n"); - return -1; + return 1; } diff --git a/Tests/TryCompile/old_and_new_signature_tests.cmake b/Tests/TryCompile/old_and_new_signature_tests.cmake index c2dd233..997a5a3 100644 --- a/Tests/TryCompile/old_and_new_signature_tests.cmake +++ b/Tests/TryCompile/old_and_new_signature_tests.cmake @@ -130,18 +130,14 @@ if(APPLE) ${try_compile_bindir_or_SOURCES} ${TryCompile_SOURCE_DIR}/pass.m OUTPUT_VARIABLE TRY_OUT) - if(NOT SHOULD_PASS) - message(SEND_ERROR "should pass failed ${TRY_OUT}") - endif() + EXPECT_PASS(SHOULD_PASS "${TRY_OUT}") # try to compile a file that should not compile try_compile(SHOULD_FAIL ${try_compile_bindir_or_SOURCES} ${TryCompile_SOURCE_DIR}/fail.m OUTPUT_VARIABLE TRY_OUT) - if(SHOULD_FAIL) - message(SEND_ERROR "Should fail passed ${TRY_OUT}") - endif() + EXPECT_FAIL(SHOULD_FAIL "${TRY_OUT}") endif() ###################################### @@ -155,14 +151,9 @@ try_run(SHOULD_RUN SHOULD_COMPILE ${try_compile_bindir_or_SOURCES} ${TryCompile_SOURCE_DIR}/exit_success.c ${try_compile_output_vars}) -if(NOT SHOULD_COMPILE) - message(SEND_ERROR - "exit_success failed compiling: ${${try_compile_compile_output_var}}") -endif() -if(NOT "${SHOULD_RUN}" STREQUAL "0") - message(SEND_ERROR - "exit_success failed running with exit code ${SHOULD_RUN}") -endif() +EXPECT_COMPILED("exit_success" SHOULD_COMPILE "${${try_compile_compile_output_var}}") +EXPECT_RUN_RESULT("exit_success" SHOULD_RUN 0) + # check the compile output for the filename if(NOT "${${try_compile_compile_output_var}}" MATCHES "exit_success") message(SEND_ERROR @@ -181,12 +172,8 @@ try_run(ARG_TEST_RUN ARG_TEST_COMPILE ${TryCompile_SOURCE_DIR}/expect_arg.c COMPILE_OUTPUT_VARIABLE TRY_OUT ARGS arg1 arg2) -if(NOT ARG_TEST_COMPILE) - message(SEND_ERROR "expect_arg failed compiling: ${TRY_OUT}") -endif() -if(NOT "${ARG_TEST_RUN}" STREQUAL "0") - message(SEND_ERROR "expect_arg failed running with exit code ${ARG_TEST_RUN} ${TRY_OUT}") -endif() +EXPECT_COMPILED("expect_arg" ARG_TEST_COMPILE "${TRY_OUT}") +EXPECT_RUN_RESULT("expect_arg" ARG_TEST_RUN 0) # try to run a file that should compile and run, but return an error try_run(SHOULD_EXIT_WITH_ERROR SHOULD_COMPILE @@ -194,13 +181,8 @@ try_run(SHOULD_EXIT_WITH_ERROR SHOULD_COMPILE ${TryCompile_SOURCE_DIR}/exit_with_error.c COMPILE_OUTPUT_VARIABLE COMPILE_OUTPUT RUN_OUTPUT_VARIABLE RUN_OUTPUT) - -if(NOT SHOULD_COMPILE) - message(STATUS " exit_with_error failed compiling: ${COMPILE_OUTPUT}") -endif() -if("${SHOULD_EXIT_WITH_ERROR}" STREQUAL "0") - message(SEND_ERROR " exit_with_error passed with exit code ${SHOULD_EXIT_WITH_ERROR}") -endif() +EXPECT_COMPILED("exit_with_error" SHOULD_COMPILE "${COMPILE_OUTPUT}") +EXPECT_RUN_RESULT("exit_with_error" SHOULD_EXIT_WITH_ERROR 1) # check the compile output, it should contain the filename if(NOT "${COMPILE_OUTPUT}" MATCHES "exit_with_error") @@ -224,10 +206,7 @@ try_run(SHOULD_EXIT_WITH_ERROR SHOULD_COMPILE COMPILE_OUTPUT_VARIABLE COMPILE_OUTPUT RUN_OUTPUT_STDOUT_VARIABLE RUN_OUTPUT_STDOUT RUN_OUTPUT_STDERR_VARIABLE RUN_OUTPUT_STDERR) - -if(NOT SHOULD_COMPILE) - message(STATUS " exit_with_error failed compiling: ${COMPILE_OUTPUT}") -endif() +EXPECT_PASS(SHOULD_COMPILE "${COMPILE_OUTPUT}") if(NOT EXISTS "${TryCompile_BINARY_DIR}/CopyOfRun") message(SEND_ERROR "COPY_FILE to \"${TryCompile_BINARY_DIR}/CopyOfRun\" failed") -- cgit v0.12 From 611d80179006ba6670273fcfcad46b0bb64a36c2 Mon Sep 17 00:00:00 2001 From: Matthew Woehlke Date: Thu, 22 Sep 2022 13:43:14 -0400 Subject: try_compile: Add SOURCE_FROM_FILE Add ability to copy try_compile (and try_run) source files from arbitrary locations into the operation directory. This is included for the sake of completion and consolidation, although use cases which actually require this may be rare. --- Help/command/try_compile.rst | 13 ++++++++- Help/command/try_run.rst | 3 +- Source/cmCoreTryCompile.cxx | 32 ++++++++++++++++++++++ Source/cmCoreTryCompile.h | 2 ++ Tests/RunCMake/try_compile/RunCMakeTest.cmake | 1 + .../try_compile/SourceFromBadFile-result.txt | 1 + .../try_compile/SourceFromBadFile-stderr.txt | 4 +++ Tests/RunCMake/try_compile/SourceFromBadFile.cmake | 1 + Tests/TryCompile/CMakeLists.txt | 6 ++++ 9 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 Tests/RunCMake/try_compile/SourceFromBadFile-result.txt create mode 100644 Tests/RunCMake/try_compile/SourceFromBadFile-stderr.txt create mode 100644 Tests/RunCMake/try_compile/SourceFromBadFile.cmake diff --git a/Help/command/try_compile.rst b/Help/command/try_compile.rst index ed61a2f..4632cd9 100644 --- a/Help/command/try_compile.rst +++ b/Help/command/try_compile.rst @@ -58,7 +58,8 @@ Try Compiling Source Files try_compile( ] | SOURCE_FROM_ARG ] | - SOURCE_FROM_VAR ] >... + SOURCE_FROM_VAR ] | + SOURCE_FROM_FILE >... [CMAKE_FLAGS ...] [COMPILE_DEFINITIONS ...] [LINK_OPTIONS ...] @@ -178,6 +179,16 @@ The options are: ``SOURCE_FROM_ARG`` may be specified multiple times. +``SOURCE_FROM_FILE `` + .. versionadded:: 3.25 + + Copy ```` to a file named ```` in the operation directory. This + can be used to consolidate files into the operation directory, which may be + useful if a source which already exists (i.e. as a stand-alone file in a + project's source repository) needs to refer to other file(s) created by + ``SOURCE_FROM_*``. (Otherwise, ``SOURCES`` is usually more convenient.) The + specified ```` is not allowed to contain path components. + ``SOURCE_FROM_VAR `` .. versionadded:: 3.25 diff --git a/Help/command/try_run.rst b/Help/command/try_run.rst index 5498344..efc10ec 100644 --- a/Help/command/try_run.rst +++ b/Help/command/try_run.rst @@ -15,7 +15,8 @@ Try Compiling and Running Source Files try_run( ] | SOURCE_FROM_ARG ] | - SOURCE_FROM_VAR ] >... + SOURCE_FROM_VAR ] | + SOURCE_FROM_FILE >... [CMAKE_FLAGS ...] [COMPILE_DEFINITIONS ...] [LINK_OPTIONS ...] diff --git a/Source/cmCoreTryCompile.cxx b/Source/cmCoreTryCompile.cxx index 0ef32c0..839cb7b 100644 --- a/Source/cmCoreTryCompile.cxx +++ b/Source/cmCoreTryCompile.cxx @@ -174,6 +174,7 @@ auto const TryCompileBaseNewSourcesArgParser = cmArgumentParser{ TryCompileBaseSourcesArgParser } .Bind("SOURCE_FROM_ARG"_s, &Arguments::SourceFromArg) .Bind("SOURCE_FROM_VAR"_s, &Arguments::SourceFromVar) + .Bind("SOURCE_FROM_FILE"_s, &Arguments::SourceFromFile) /* keep semicolon on own line */; auto const TryCompileBaseProjectArgParser = @@ -416,6 +417,12 @@ bool cmCoreTryCompile::TryCompileCode(Arguments& arguments, "SOURCE_FROM_VAR requires exactly two arguments"); return false; } + if (arguments.SourceFromFile && arguments.SourceFromFile->size() % 2) { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + "SOURCE_FROM_FILE requires exactly two arguments"); + return false; + } } else { // only valid for srcfile signatures if (!arguments.LangProps.empty()) { @@ -497,6 +504,31 @@ bool cmCoreTryCompile::TryCompileCode(Arguments& arguments, sources.emplace_back(std::move(out)); } } + if (arguments.SourceFromFile) { + auto const k = arguments.SourceFromFile->size(); + for (auto i = decltype(k){ 0 }; i < k; i += 2) { + const auto& dst = (*arguments.SourceFromFile)[i + 0]; + const auto& src = (*arguments.SourceFromFile)[i + 1]; + + if (!cmSystemTools::GetFilenamePath(dst).empty()) { + const auto& msg = + cmStrCat("SOURCE_FROM_FILE given invalid filename \"", dst, "\""); + this->Makefile->IssueMessage(MessageType::FATAL_ERROR, msg); + return false; + } + + auto dstPath = cmStrCat(this->BinaryDirectory, "/", dst); + auto const result = cmSystemTools::CopyFileAlways(src, dstPath); + if (!result.IsSuccess()) { + const auto& msg = cmStrCat("SOURCE_FROM_FILE failed to copy \"", src, + "\": ", result.GetString()); + this->Makefile->IssueMessage(MessageType::FATAL_ERROR, msg); + return false; + } + + sources.emplace_back(std::move(dstPath)); + } + } // TODO: ensure sources is not empty // Detect languages to enable. diff --git a/Source/cmCoreTryCompile.h b/Source/cmCoreTryCompile.h index 75dcf6e..3f4a4dc 100644 --- a/Source/cmCoreTryCompile.h +++ b/Source/cmCoreTryCompile.h @@ -44,6 +44,8 @@ public: SourceFromArg; cm::optional>> SourceFromVar; + cm::optional>> + SourceFromFile; ArgumentParser::MaybeEmpty> CMakeFlags{ 1, "CMAKE_FLAGS" }; // fake argv[0] diff --git a/Tests/RunCMake/try_compile/RunCMakeTest.cmake b/Tests/RunCMake/try_compile/RunCMakeTest.cmake index 0aa9039..d63624c 100644 --- a/Tests/RunCMake/try_compile/RunCMakeTest.cmake +++ b/Tests/RunCMake/try_compile/RunCMakeTest.cmake @@ -23,6 +23,7 @@ unset(RunCMake_TEST_OPTIONS) run_cmake(SourceFromOneArg) run_cmake(SourceFromThreeArgs) run_cmake(SourceFromBadName) +run_cmake(SourceFromBadFile) run_cmake(ProjectCopyFile) run_cmake(NonSourceCopyFile) diff --git a/Tests/RunCMake/try_compile/SourceFromBadFile-result.txt b/Tests/RunCMake/try_compile/SourceFromBadFile-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/try_compile/SourceFromBadFile-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/try_compile/SourceFromBadFile-stderr.txt b/Tests/RunCMake/try_compile/SourceFromBadFile-stderr.txt new file mode 100644 index 0000000..53a6d8d --- /dev/null +++ b/Tests/RunCMake/try_compile/SourceFromBadFile-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at SourceFromBadFile.cmake:[0-9]+ \(try_compile\): + SOURCE_FROM_FILE failed to copy "bad#source.c": No such file or directory +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/try_compile/SourceFromBadFile.cmake b/Tests/RunCMake/try_compile/SourceFromBadFile.cmake new file mode 100644 index 0000000..0a37f11 --- /dev/null +++ b/Tests/RunCMake/try_compile/SourceFromBadFile.cmake @@ -0,0 +1 @@ +try_compile(RESULT SOURCE_FROM_FILE bad.c "bad#source.c") diff --git a/Tests/TryCompile/CMakeLists.txt b/Tests/TryCompile/CMakeLists.txt index cca19bc..9396cfa 100644 --- a/Tests/TryCompile/CMakeLists.txt +++ b/Tests/TryCompile/CMakeLists.txt @@ -89,6 +89,12 @@ if(SHOULD_FAIL_DUE_TO_EMPTY_SOURCE) message(SEND_ERROR "Trying to compile an empty source succeeded?") endif() +# try to compile a copied source +try_compile(SHOULD_PASS + SOURCE_FROM_FILE pass.c ${TryCompile_SOURCE_DIR}/pass.c + OUTPUT_VARIABLE TRY_OUT) +EXPECT_COMPILED("SOURCE_FROM_FILE" SHOULD_PASS "${TRY_OUT}") + # try to run a source specified directly set(TRY_RUN_MAIN_CODE "extern int answer(); \n" -- cgit v0.12