diff options
author | Brad King <brad.king@kitware.com> | 2022-08-01 17:53:49 (GMT) |
---|---|---|
committer | Brad King <brad.king@kitware.com> | 2022-08-02 16:54:56 (GMT) |
commit | 6b427d8da9b5f5db3753c499765b4b62f8e9020d (patch) | |
tree | 8c474dee4b3f9fafed9ecd813f3c69aa2603e2d5 /Source | |
parent | 067ba3a2bd1ce168b2867de74d2ea6b944a936fc (diff) | |
download | CMake-6b427d8da9b5f5db3753c499765b4b62f8e9020d.zip CMake-6b427d8da9b5f5db3753c499765b4b62f8e9020d.tar.gz CMake-6b427d8da9b5f5db3753c499765b4b62f8e9020d.tar.bz2 |
cmCoreTryCompile: Port to cmArgumentParser
Diffstat (limited to 'Source')
-rw-r--r-- | Source/cmCoreTryCompile.cxx | 603 | ||||
-rw-r--r-- | Source/cmCoreTryCompile.h | 43 | ||||
-rw-r--r-- | Source/cmTryCompileCommand.cxx | 8 | ||||
-rw-r--r-- | Source/cmTryRunCommand.cxx | 192 |
4 files changed, 356 insertions, 490 deletions
diff --git a/Source/cmCoreTryCompile.cxx b/Source/cmCoreTryCompile.cxx index 3c749ed..c2c3118 100644 --- a/Source/cmCoreTryCompile.cxx +++ b/Source/cmCoreTryCompile.cxx @@ -2,6 +2,7 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmCoreTryCompile.h" +#include <array> #include <cstdio> #include <cstring> #include <set> @@ -13,12 +14,14 @@ #include "cmsys/Directory.hxx" +#include "cmArgumentParser.h" #include "cmExportTryCompileFileGenerator.h" #include "cmGlobalGenerator.h" #include "cmMakefile.h" #include "cmMessageType.h" #include "cmOutputConverter.h" #include "cmPolicies.h" +#include "cmRange.h" #include "cmState.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -28,142 +31,6 @@ #include "cmake.h" namespace { -class LanguageStandardState -{ -public: - LanguageStandardState(std::string&& lang) - : StandardFlag(lang + "_STANDARD") - , RequiredFlag(lang + "_STANDARD_REQUIRED") - , ExtensionFlag(lang + "_EXTENSIONS") - { - } - - void Enabled(bool isEnabled) { this->IsEnabled = isEnabled; } - - bool UpdateIfMatches(std::vector<std::string> const& argv, size_t& index) - { - bool updated = false; - if (argv[index] == this->StandardFlag) { - this->DidStandard = true; - this->StandardValue = argv[++index]; - updated = true; - } else if (argv[index] == this->RequiredFlag) { - this->DidStandardRequired = true; - this->RequiredValue = argv[++index]; - updated = true; - } else if (argv[index] == this->ExtensionFlag) { - this->DidExtensions = true; - this->ExtensionValue = argv[++index]; - updated = true; - } - return updated; - } - - bool Validate(cmMakefile* const makefile) const - { - if (this->DidStandard) { - makefile->IssueMessage( - MessageType::FATAL_ERROR, - cmStrCat(this->StandardFlag, - " allowed only in source file signature.")); - return false; - } - if (this->DidStandardRequired) { - makefile->IssueMessage( - MessageType::FATAL_ERROR, - cmStrCat(this->RequiredFlag, - " allowed only in source file signature.")); - return false; - } - if (this->DidExtensions) { - makefile->IssueMessage( - MessageType::FATAL_ERROR, - cmStrCat(this->ExtensionFlag, - " allowed only in source file signature.")); - return false; - } - - return true; - } - - bool DidNone() const - { - return !this->DidStandard && !this->DidStandardRequired && - !this->DidExtensions; - } - - void LoadUnsetPropertyValues(cmMakefile* const makefile, bool honorStandard, - bool warnCMP0067, - std::vector<std::string>& warnCMP0067Variables) - { - if (!this->IsEnabled) { - return; - } - - auto lookupStdVar = [&](std::string const& var) -> std::string { - std::string value = makefile->GetSafeDefinition(var); - if (warnCMP0067 && !value.empty()) { - value.clear(); - warnCMP0067Variables.emplace_back(var); - } - return value; - }; - - if (honorStandard || warnCMP0067) { - if (!this->DidStandard) { - this->StandardValue = - lookupStdVar(cmStrCat("CMAKE_", this->StandardFlag)); - } - if (!this->DidStandardRequired) { - this->RequiredValue = - lookupStdVar(cmStrCat("CMAKE_", this->RequiredFlag)); - } - if (!this->DidExtensions) { - this->ExtensionValue = - lookupStdVar(cmStrCat("CMAKE_", this->ExtensionFlag)); - } - } - } - - void WriteProperties(FILE* fout, std::string const& targetName) const - { - if (!this->IsEnabled) { - return; - } - - auto writeProp = [&](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()); - }; - - if (!this->StandardValue.empty()) { - writeProp(this->StandardFlag, this->StandardValue); - } - if (!this->RequiredValue.empty()) { - writeProp(this->RequiredFlag, this->RequiredValue); - } - if (!this->ExtensionValue.empty()) { - writeProp(this->ExtensionFlag, this->ExtensionValue); - } - } - -private: - bool IsEnabled = false; - bool DidStandard = false; - bool DidStandardRequired = false; - bool DidExtensions = false; - - std::string StandardFlag; - std::string RequiredFlag; - std::string ExtensionFlag; - - std::string StandardValue; - std::string RequiredValue; - std::string ExtensionValue; -}; - constexpr size_t lang_property_start = 0; constexpr size_t lang_property_size = 4; constexpr size_t pie_property_start = 4; @@ -233,12 +100,132 @@ std::set<std::string> const ghs_platform_vars{ "GHS_OS_ROOT", "GHS_OS_DIR", "GHS_BSP_NAME", "GHS_OS_DIR_OPTION" }; +using Arguments = cmCoreTryCompile::Arguments; + +ArgumentParser::Continue TryCompileLangProp(Arguments& args, + cm::string_view key, + cm::string_view val) +{ + args.LangProps[std::string(key)] = std::string(val); + return ArgumentParser::Continue::No; +} + +ArgumentParser::Continue TryCompileCompileDefs(Arguments& args, + cm::string_view val) +{ + cmExpandList(val, args.CompileDefs); + return ArgumentParser::Continue::Yes; } -bool cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, +#define BIND_LANG_PROPS(lang) \ + Bind(#lang "_STANDARD"_s, TryCompileLangProp) \ + .Bind(#lang "_STANDARD_REQUIRED"_s, TryCompileLangProp) \ + .Bind(#lang "_EXTENSIONS"_s, TryCompileLangProp) + +auto const TryCompileArgParser = + cmArgumentParser<Arguments>{} + .Bind(0, &Arguments::CompileResultVariable) + .Bind(1, &Arguments::BinaryDirectory) + .Bind(2, &Arguments::SourceDirectoryOrFile) + .Bind(3, &Arguments::ProjectName) + .Bind(4, &Arguments::TargetName) + .Bind("SOURCES"_s, &Arguments::Sources) + .Bind("CMAKE_FLAGS"_s, &Arguments::CMakeFlags) + .Bind("COMPILE_DEFINITIONS"_s, TryCompileCompileDefs, + ArgumentParser::ExpectAtLeast{ 0 }) + .Bind("LINK_LIBRARIES"_s, &Arguments::LinkLibraries) + .Bind("LINK_OPTIONS"_s, &Arguments::LinkOptions) + .Bind("__CMAKE_INTERNAL"_s, &Arguments::CMakeInternal) + .Bind("OUTPUT_VARIABLE"_s, &Arguments::OutputVariable) + .Bind("COPY_FILE"_s, &Arguments::CopyFileTo) + .Bind("COPY_FILE_ERROR"_s, &Arguments::CopyFileError) + .BIND_LANG_PROPS(C) + .BIND_LANG_PROPS(CUDA) + .BIND_LANG_PROPS(CXX) + .BIND_LANG_PROPS(HIP) + .BIND_LANG_PROPS(OBJC) + .BIND_LANG_PROPS(OBJCXX) + .Bind("COMPILE_OUTPUT_VARIABLE"_s, &Arguments::CompileOutputVariable) + .Bind("RUN_OUTPUT_VARIABLE"_s, &Arguments::RunOutputVariable) + .Bind("RUN_OUTPUT_STDOUT_VARIABLE"_s, &Arguments::RunOutputStdOutVariable) + .Bind("RUN_OUTPUT_STDERR_VARIABLE"_s, &Arguments::RunOutputStdErrVariable) + .Bind("WORKING_DIRECTORY"_s, &Arguments::RunWorkingDirectory) + .Bind("ARGS"_s, &Arguments::RunArgs) + /* keep semicolon on own line */; + +#undef BIND_LANG_PROPS +} + +Arguments cmCoreTryCompile::ParseArgs( + cmRange<std::vector<std::string>::const_iterator> args, bool isTryRun) +{ + std::vector<std::string> unparsedArguments; + auto arguments = TryCompileArgParser.Parse(args, &unparsedArguments, 0); + if (!arguments.MaybeReportError(*(this->Makefile)) && + !unparsedArguments.empty()) { + std::string m = "Unknown arguments:"; + for (const auto& i : unparsedArguments) { + m = cmStrCat(m, "\n ", i, "\""); + } + this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, m); + } + // For historical reasons, treat some empty-valued keyword + // arguments as if they were not specified at all. + if (arguments.OutputVariable && arguments.OutputVariable->empty()) { + arguments.OutputVariable = cm::nullopt; + } + if (isTryRun) { + if (arguments.CompileOutputVariable && + arguments.CompileOutputVariable->empty()) { + arguments.CompileOutputVariable = cm::nullopt; + } + if (arguments.RunOutputVariable && arguments.RunOutputVariable->empty()) { + arguments.RunOutputVariable = cm::nullopt; + } + if (arguments.RunOutputStdOutVariable && + arguments.RunOutputStdOutVariable->empty()) { + arguments.RunOutputStdOutVariable = cm::nullopt; + } + if (arguments.RunOutputStdErrVariable && + arguments.RunOutputStdErrVariable->empty()) { + arguments.RunOutputStdErrVariable = cm::nullopt; + } + if (arguments.RunWorkingDirectory && + arguments.RunWorkingDirectory->empty()) { + arguments.RunWorkingDirectory = cm::nullopt; + } + } else { + std::string tryRunArgs; + if (arguments.CompileOutputVariable) { + tryRunArgs = cmStrCat(tryRunArgs, " COMPILE_OUTPUT_VARIABLE\n"); + } + if (arguments.RunOutputVariable) { + tryRunArgs = cmStrCat(tryRunArgs, " RUN_OUTPUT_VARIABLE\n"); + } + if (arguments.RunOutputStdOutVariable) { + tryRunArgs = cmStrCat(tryRunArgs, " RUN_OUTPUT_STDOUT_VARIABLE\n"); + } + if (arguments.RunOutputStdErrVariable) { + tryRunArgs = cmStrCat(tryRunArgs, " RUN_OUTPUT_STDERR_VARIABLE\n"); + } + if (arguments.RunWorkingDirectory) { + tryRunArgs = cmStrCat(tryRunArgs, " WORKING_DIRECTORY\n"); + } + if (arguments.RunArgs) { + tryRunArgs = cmStrCat(tryRunArgs, " ARGS\n"); + } + if (!tryRunArgs.empty()) { + this->Makefile->IssueMessage( + MessageType::AUTHOR_WARNING, + cmStrCat("Ignoring try_run arguments for try_compile:\n", tryRunArgs)); + } + } + return arguments; +} + +bool cmCoreTryCompile::TryCompileCode(Arguments& arguments, cmStateEnums::TargetType targetType) { - std::string const& resultVar = argv[0]; this->OutputFile.clear(); // which signature were we called with ? this->SrcFileSignature = true; @@ -246,82 +233,47 @@ bool cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, std::string sourceDirectory; std::string projectName; std::string targetName; - std::vector<std::string> cmakeFlags(1, "CMAKE_FLAGS"); // fake argv[0] - std::vector<std::string> compileDefs; - std::string cmakeInternal; - std::string outputVariable; - std::string copyFile; - std::string copyFileError; - LanguageStandardState cState("C"); - LanguageStandardState cudaState("CUDA"); - LanguageStandardState cxxState("CXX"); - LanguageStandardState hipState("HIP"); - LanguageStandardState objcState("OBJC"); - LanguageStandardState objcxxState("OBJCXX"); + if (arguments.SourceDirectoryOrFile && arguments.ProjectName) { + this->SrcFileSignature = false; + sourceDirectory = *arguments.SourceDirectoryOrFile; + projectName = *arguments.ProjectName; + if (arguments.TargetName) { + targetName = *arguments.TargetName; + } + } else { + projectName = "CMAKE_TRY_COMPILE"; + /* Use a random file name to avoid rapid creation and deletion + of the same executable name (some filesystems fail on that). */ + char targetNameBuf[64]; + snprintf(targetNameBuf, sizeof(targetNameBuf), "cmTC_%05x", + cmSystemTools::RandomSeed() & 0xFFFFF); + targetName = targetNameBuf; + } + + if (arguments.BinaryDirectory && !arguments.BinaryDirectory->empty()) { + if (!cmSystemTools::FileIsFullPath(*arguments.BinaryDirectory)) { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("<bindir> is not an absolute path:\n '", + *arguments.BinaryDirectory, "'")); + return false; + } + this->BinaryDirectory = *arguments.BinaryDirectory; + // compute the binary dir when TRY_COMPILE is called with a src file + // signature + if (this->SrcFileSignature) { + this->BinaryDirectory += "/CMakeFiles/CMakeTmp"; + } + } else { + this->Makefile->IssueMessage(MessageType::FATAL_ERROR, + "No <bindir> specified."); + return false; + } + std::vector<std::string> targets; - std::vector<std::string> linkOptions; - std::string libsToLink = " "; - bool useOldLinkLibs = true; - bool didOutputVariable = false; - bool didCopyFile = false; - bool didCopyFileError = false; - bool useSources = false; - std::vector<std::string> sources; - - enum Doing - { - DoingNone, - DoingCMakeFlags, - DoingCompileDefinitions, - DoingLinkOptions, - DoingLinkLibraries, - DoingOutputVariable, - DoingCopyFile, - DoingCopyFileError, - DoingSources, - DoingCMakeInternal - }; - Doing doing = DoingNone; - for (size_t i = 1; i < argv.size(); ++i) { - if (argv[i] == "SOURCES") { - useSources = true; - doing = DoingSources; - } else if (argv[i] == "CMAKE_FLAGS") { - doing = DoingCMakeFlags; - } else if (argv[i] == "COMPILE_DEFINITIONS") { - doing = DoingCompileDefinitions; - } else if (argv[i] == "LINK_OPTIONS") { - doing = DoingLinkOptions; - } else if (argv[i] == "LINK_LIBRARIES") { - doing = DoingLinkLibraries; - useOldLinkLibs = false; - } else if (argv[i] == "OUTPUT_VARIABLE") { - doing = DoingOutputVariable; - didOutputVariable = true; - } else if (argv[i] == "COPY_FILE") { - doing = DoingCopyFile; - didCopyFile = true; - } else if (argv[i] == "COPY_FILE_ERROR") { - doing = DoingCopyFileError; - didCopyFileError = true; - } else if (cState.UpdateIfMatches(argv, i) || - cxxState.UpdateIfMatches(argv, i) || - cudaState.UpdateIfMatches(argv, i) || - hipState.UpdateIfMatches(argv, i) || - objcState.UpdateIfMatches(argv, i) || - objcxxState.UpdateIfMatches(argv, i)) { - continue; - } else if (argv[i] == "__CMAKE_INTERNAL") { - doing = DoingCMakeInternal; - } else if (doing == DoingCMakeFlags) { - cmakeFlags.emplace_back(argv[i]); - } else if (doing == DoingCompileDefinitions) { - cmExpandList(argv[i], compileDefs); - } else if (doing == DoingLinkOptions) { - linkOptions.emplace_back(argv[i]); - } else if (doing == DoingLinkLibraries) { - libsToLink += "\"" + cmTrimWhitespace(argv[i]) + "\" "; - if (cmTarget* tgt = this->Makefile->FindTargetToUse(argv[i])) { + if (arguments.LinkLibraries) { + for (std::string const& i : *arguments.LinkLibraries) { + if (cmTarget* tgt = this->Makefile->FindTargetToUse(i)) { switch (tgt->GetType()) { case cmStateEnums::SHARED_LIBRARY: case cmStateEnums::STATIC_LIBRARY: @@ -343,98 +295,33 @@ bool cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, return false; } if (tgt->IsImported()) { - targets.emplace_back(argv[i]); + targets.emplace_back(i); } } - } else if (doing == DoingOutputVariable) { - outputVariable = argv[i]; - doing = DoingNone; - } else if (doing == DoingCopyFile) { - copyFile = argv[i]; - doing = DoingNone; - } else if (doing == DoingCopyFileError) { - copyFileError = argv[i]; - doing = DoingNone; - } else if (doing == DoingSources) { - sources.emplace_back(argv[i]); - } else if (doing == DoingCMakeInternal) { - cmakeInternal = argv[i]; - doing = DoingNone; - } else if (i == 1) { - this->BinaryDirectory = argv[i]; - } else if (i == 2) { - sourceDirectory = argv[i]; - } else if (i == 3) { - this->SrcFileSignature = false; - projectName = argv[i]; - } else if (i == 4 && !this->SrcFileSignature) { - targetName = argv[i]; - } else { - std::ostringstream m; - m << "try_compile given unknown argument \"" << argv[i] << "\"."; - this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, m.str()); - } - } - - if (!this->BinaryDirectory.empty()) { - if (!cmSystemTools::FileIsFullPath(this->BinaryDirectory)) { - this->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - cmStrCat("<bindir> is not an absolute path:\n '", - this->BinaryDirectory, "'")); - // Do not try to clean up the ill-specified directory. - this->BinaryDirectory.clear(); - return false; - } - // compute the binary dir when TRY_COMPILE is called with a src file - // signature - if (this->SrcFileSignature) { - this->BinaryDirectory += "/CMakeFiles/CMakeTmp"; } - } else { - this->Makefile->IssueMessage(MessageType::FATAL_ERROR, - "No <bindir> specified."); - return false; - } - - if (this->SrcFileSignature) { - projectName = "CMAKE_TRY_COMPILE"; - /* Use a random file name to avoid rapid creation and deletion - of the same executable name (some filesystems fail on that). */ - char targetNameBuf[64]; - snprintf(targetNameBuf, sizeof(targetNameBuf), "cmTC_%05x", - cmSystemTools::RandomSeed() & 0xFFFFF); - targetName = targetNameBuf; } - if (didCopyFile && copyFile.empty()) { + if (arguments.CopyFileTo && arguments.CopyFileTo->empty()) { this->Makefile->IssueMessage(MessageType::FATAL_ERROR, "COPY_FILE must be followed by a file path"); return false; } - if (didCopyFileError && copyFileError.empty()) { + if (arguments.CopyFileError && arguments.CopyFileError->empty()) { this->Makefile->IssueMessage( MessageType::FATAL_ERROR, "COPY_FILE_ERROR must be followed by a variable name"); return false; } - if (didCopyFileError && !didCopyFile) { + if (arguments.CopyFileError && !arguments.CopyFileTo) { this->Makefile->IssueMessage( MessageType::FATAL_ERROR, "COPY_FILE_ERROR may be used only with COPY_FILE"); return false; } - if (didOutputVariable && outputVariable.empty()) { - this->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - "OUTPUT_VARIABLE must be followed by a variable name"); - return false; - } - - if (useSources && sources.empty()) { + if (arguments.Sources && arguments.Sources->empty()) { this->Makefile->IssueMessage( MessageType::FATAL_ERROR, "SOURCES must be followed by at least one source file"); @@ -443,32 +330,20 @@ bool cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, // only valid for srcfile signatures if (!this->SrcFileSignature) { - if (!cState.Validate(this->Makefile)) { - return false; - } - if (!cudaState.Validate(this->Makefile)) { - return false; - } - if (!hipState.Validate(this->Makefile)) { - return false; - } - if (!cxxState.Validate(this->Makefile)) { - return false; - } - if (!objcState.Validate(this->Makefile)) { - return false; - } - if (!objcxxState.Validate(this->Makefile)) { + if (!arguments.LangProps.empty()) { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat(arguments.LangProps.begin()->first, + " allowed only in source file signature.")); return false; } - - if (!compileDefs.empty()) { + if (!arguments.CompileDefs.empty()) { this->Makefile->IssueMessage( MessageType::FATAL_ERROR, "COMPILE_DEFINITIONS specified on a srcdir type TRY_COMPILE"); return false; } - if (!copyFile.empty()) { + if (arguments.CopyFileTo) { this->Makefile->IssueMessage( MessageType::FATAL_ERROR, "COPY_FILE specified on a srcdir type TRY_COMPILE"); @@ -495,8 +370,12 @@ bool cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, cmSystemTools::RemoveFile(ccFile); // Choose sources. - if (!useSources) { - sources.emplace_back(argv[2]); + std::vector<std::string> sources; + if (arguments.Sources) { + sources = std::move(*arguments.Sources); + } else { + // TODO: ensure SourceDirectoryOrFile has a value + sources.emplace_back(*arguments.SourceDirectoryOrFile); } // Detect languages to enable. @@ -608,7 +487,7 @@ bool cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, } } fprintf(fout, "project(CMAKE_TRY_COMPILE%s)\n", projectLangs.c_str()); - if (cmakeInternal == "ABI") { + if (arguments.CMakeInternal == "ABI") { // This is the ABI detection step, also used for implicit includes. // Erase any include_directories() calls from the toolchain file so // that we do not see them as implicit. Our ABI detection source @@ -715,10 +594,10 @@ bool cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, fprintf(fout, "set(CMAKE_SUPPRESS_REGENERATION 1)\n"); fprintf(fout, "link_directories(${LINK_DIRECTORIES})\n"); // handle any compile flags we need to pass on - if (!compileDefs.empty()) { + if (!arguments.CompileDefs.empty()) { // Pass using bracket arguments to preserve content. fprintf(fout, "add_definitions([==[%s]==])\n", - cmJoin(compileDefs, "]==] [==[").c_str()); + cmJoin(arguments.CompileDefs, "]==] [==[").c_str()); } if (!targets.empty()) { @@ -782,18 +661,10 @@ bool cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, } fprintf(fout, ")\n"); - cState.Enabled(testLangs.find("C") != testLangs.end()); - cxxState.Enabled(testLangs.find("CXX") != testLangs.end()); - cudaState.Enabled(testLangs.find("CUDA") != testLangs.end()); - hipState.Enabled(testLangs.find("HIP") != testLangs.end()); - objcState.Enabled(testLangs.find("OBJC") != testLangs.end()); - objcxxState.Enabled(testLangs.find("OBJCXX") != testLangs.end()); - bool warnCMP0067 = false; bool honorStandard = true; - if (cState.DidNone() && cxxState.DidNone() && objcState.DidNone() && - objcxxState.DidNone() && cudaState.DidNone() && hipState.DidNone()) { + if (arguments.LangProps.empty()) { switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0067)) { case cmPolicies::WARN: warnCMP0067 = this->Makefile->PolicyOptionalWarningEnabled( @@ -818,18 +689,33 @@ bool cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, std::vector<std::string> warnCMP0067Variables; - cState.LoadUnsetPropertyValues(this->Makefile, honorStandard, warnCMP0067, - warnCMP0067Variables); - cxxState.LoadUnsetPropertyValues(this->Makefile, honorStandard, - warnCMP0067, warnCMP0067Variables); - cudaState.LoadUnsetPropertyValues(this->Makefile, honorStandard, - warnCMP0067, warnCMP0067Variables); - hipState.LoadUnsetPropertyValues(this->Makefile, honorStandard, - warnCMP0067, warnCMP0067Variables); - objcState.LoadUnsetPropertyValues(this->Makefile, honorStandard, - warnCMP0067, warnCMP0067Variables); - objcxxState.LoadUnsetPropertyValues(this->Makefile, honorStandard, - warnCMP0067, warnCMP0067Variables); + if (honorStandard || warnCMP0067) { + static std::array<std::string, 6> const possibleLangs{ + { "C", "CXX", "CUDA", "HIP", "OBJC", "OBJCXX" } + }; + static std::array<cm::string_view, 3> const langPropSuffixes{ + { "_STANDARD"_s, "_STANDARD_REQUIRED"_s, "_EXTENSIONS"_s } + }; + for (std::string const& lang : possibleLangs) { + if (testLangs.find(lang) == testLangs.end()) { + continue; + } + for (cm::string_view propSuffix : langPropSuffixes) { + std::string langProp = cmStrCat(lang, propSuffix); + if (!arguments.LangProps.count(langProp)) { + std::string langPropVar = cmStrCat("CMAKE_"_s, langProp); + std::string value = this->Makefile->GetSafeDefinition(langPropVar); + if (warnCMP0067 && !value.empty()) { + value.clear(); + warnCMP0067Variables.emplace_back(langPropVar); + } + if (!value.empty()) { + arguments.LangProps[langProp] = value; + } + } + } + } + } if (!warnCMP0067Variables.empty()) { std::ostringstream w; @@ -845,17 +731,20 @@ bool cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, w.str()); } - cState.WriteProperties(fout, targetName); - cxxState.WriteProperties(fout, targetName); - cudaState.WriteProperties(fout, targetName); - hipState.WriteProperties(fout, targetName); - objcState.WriteProperties(fout, targetName); - objcxxState.WriteProperties(fout, targetName); + for (auto const& p : arguments.LangProps) { + if (p.second.empty()) { + continue; + } + fprintf(fout, "set_property(TARGET %s PROPERTY %s %s)\n", + targetName.c_str(), + cmOutputConverter::EscapeForCMake(p.first).c_str(), + cmOutputConverter::EscapeForCMake(p.second).c_str()); + } - if (!linkOptions.empty()) { + if (!arguments.LinkOptions.empty()) { std::vector<std::string> options; - options.reserve(linkOptions.size()); - for (const auto& option : linkOptions) { + options.reserve(arguments.LinkOptions.size()); + for (const auto& option : arguments.LinkOptions) { options.emplace_back(cmOutputConverter::EscapeForCMake(option)); } @@ -869,12 +758,16 @@ bool cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, } } - if (useOldLinkLibs) { - fprintf(fout, "target_link_libraries(%s ${LINK_LIBRARIES})\n", - targetName.c_str()); - } else { + if (arguments.LinkLibraries) { + std::string libsToLink = " "; + for (std::string const& i : *arguments.LinkLibraries) { + libsToLink += "\"" + cmTrimWhitespace(i) + "\" "; + } fprintf(fout, "target_link_libraries(%s %s)\n", targetName.c_str(), libsToLink.c_str()); + } else { + fprintf(fout, "target_link_libraries(%s ${LINK_LIBRARIES})\n", + targetName.c_str()); } fclose(fout); } @@ -965,13 +858,13 @@ bool cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, kCMAKE_TRY_COMPILE_OSX_ARCHITECTURES)) { vars.erase(kCMAKE_OSX_ARCHITECTURES); std::string flag = "-DCMAKE_OSX_ARCHITECTURES=" + *tcArchs; - cmakeFlags.emplace_back(std::move(flag)); + arguments.CMakeFlags.emplace_back(std::move(flag)); } for (std::string const& var : vars) { if (cmValue val = this->Makefile->GetDefinition(var)) { std::string flag = "-D" + var + "=" + *val; - cmakeFlags.emplace_back(std::move(flag)); + arguments.CMakeFlags.emplace_back(std::move(flag)); } } } @@ -981,7 +874,7 @@ bool cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, for (std::string const& var : ghs_platform_vars) { if (cmValue val = this->Makefile->GetDefinition(var)) { std::string flag = "-D" + var + "=" + "'" + *val + "'"; - cmakeFlags.emplace_back(std::move(flag)); + arguments.CMakeFlags.emplace_back(std::move(flag)); } } } @@ -992,26 +885,27 @@ bool cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, // actually do the try compile now that everything is setup int res = this->Makefile->TryCompile( sourceDirectory, this->BinaryDirectory, projectName, targetName, - this->SrcFileSignature, cmake::NO_BUILD_PARALLEL_LEVEL, &cmakeFlags, - output); + this->SrcFileSignature, cmake::NO_BUILD_PARALLEL_LEVEL, + &arguments.CMakeFlags, output); if (erroroc) { cmSystemTools::SetErrorOccurred(); } // set the result var to the return value to indicate success or failure - this->Makefile->AddCacheDefinition(resultVar, (res == 0 ? "TRUE" : "FALSE"), - "Result of TRY_COMPILE", - cmStateEnums::INTERNAL); + this->Makefile->AddCacheDefinition( + *arguments.CompileResultVariable, (res == 0 ? "TRUE" : "FALSE"), + "Result of TRY_COMPILE", cmStateEnums::INTERNAL); - if (!outputVariable.empty()) { - this->Makefile->AddDefinition(outputVariable, output); + if (arguments.OutputVariable) { + this->Makefile->AddDefinition(*arguments.OutputVariable, output); } if (this->SrcFileSignature) { std::string copyFileErrorMessage; this->FindOutputFile(targetName, targetType); - if ((res == 0) && !copyFile.empty()) { + if ((res == 0) && arguments.CopyFileTo) { + std::string const& copyFile = *arguments.CopyFileTo; if (this->OutputFile.empty() || !cmSystemTools::CopyFileAlways(this->OutputFile, copyFile)) { std::ostringstream emsg; @@ -1024,7 +918,7 @@ bool cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, if (!this->FindErrorMessage.empty()) { emsg << this->FindErrorMessage; } - if (copyFileError.empty()) { + if (!arguments.CopyFileError) { this->Makefile->IssueMessage(MessageType::FATAL_ERROR, emsg.str()); return false; } @@ -1032,7 +926,8 @@ bool cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, } } - if (!copyFileError.empty()) { + if (arguments.CopyFileError) { + std::string const& copyFileError = *arguments.CopyFileError; this->Makefile->AddDefinition(copyFileError, copyFileErrorMessage); } } diff --git a/Source/cmCoreTryCompile.h b/Source/cmCoreTryCompile.h index 6ca08e8..deefe57 100644 --- a/Source/cmCoreTryCompile.h +++ b/Source/cmCoreTryCompile.h @@ -4,12 +4,19 @@ #include "cmConfigure.h" // IWYU pragma: keep +#include <map> #include <string> #include <vector> +#include <cm/optional> + +#include "cmArgumentParser.h" +#include "cmArgumentParserTypes.h" #include "cmStateTypes.h" class cmMakefile; +template <typename Iter> +class cmRange; /** \class cmCoreTryCompile * \brief Base class for cmTryCompileCommand and cmTryRunCommand @@ -25,12 +32,46 @@ public: { } + struct Arguments : public ArgumentParser::ParseResult + { + cm::optional<std::string> CompileResultVariable; + cm::optional<std::string> BinaryDirectory; + cm::optional<std::string> SourceDirectoryOrFile; + cm::optional<std::string> ProjectName; + cm::optional<std::string> TargetName; + cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> Sources; + ArgumentParser::MaybeEmpty<std::vector<std::string>> CMakeFlags{ + 1, "CMAKE_FLAGS" + }; // fake argv[0] + std::vector<std::string> CompileDefs; + cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> + LinkLibraries; + ArgumentParser::MaybeEmpty<std::vector<std::string>> LinkOptions; + std::map<std::string, std::string> LangProps; + std::string CMakeInternal; + cm::optional<std::string> OutputVariable; + cm::optional<std::string> CopyFileTo; + cm::optional<std::string> CopyFileError; + + // Argument for try_run only. + // Keep in sync with warnings in cmCoreTryCompile::ParseArgs. + cm::optional<std::string> CompileOutputVariable; + cm::optional<std::string> RunOutputVariable; + cm::optional<std::string> RunOutputStdOutVariable; + cm::optional<std::string> RunOutputStdErrVariable; + cm::optional<std::string> RunWorkingDirectory; + cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> RunArgs; + }; + + Arguments ParseArgs(cmRange<std::vector<std::string>::const_iterator> args, + bool isTryRun); + /** * This is the core code for try compile. It is here so that other * commands, such as TryRun can access the same logic without * duplication. */ - bool TryCompileCode(std::vector<std::string> const& argv, + bool TryCompileCode(Arguments& arguments, cmStateEnums::TargetType targetType); /** diff --git a/Source/cmTryCompileCommand.cxx b/Source/cmTryCompileCommand.cxx index 971dccb..b59c225 100644 --- a/Source/cmTryCompileCommand.cxx +++ b/Source/cmTryCompileCommand.cxx @@ -6,6 +6,7 @@ #include "cmExecutionStatus.h" #include "cmMakefile.h" #include "cmMessageType.h" +#include "cmRange.h" #include "cmState.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" @@ -50,7 +51,12 @@ bool cmTryCompileCommand(std::vector<std::string> const& args, } cmCoreTryCompile tc(&mf); - tc.TryCompileCode(args, targetType); + cmCoreTryCompile::Arguments arguments = + tc.ParseArgs(cmMakeRange(args), false); + if (!arguments) { + return true; + } + tc.TryCompileCode(arguments, targetType); // if They specified clean then we clean up what we can if (tc.SrcFileSignature) { diff --git a/Source/cmTryRunCommand.cxx b/Source/cmTryRunCommand.cxx index 2cf75e2..7a29521 100644 --- a/Source/cmTryRunCommand.cxx +++ b/Source/cmTryRunCommand.cxx @@ -4,8 +4,11 @@ #include <cstdio> +#include <cm/optional> + #include "cmsys/FStream.hxx" +#include "cmArgumentParserTypes.h" #include "cmCoreTryCompile.h" #include "cmDuration.h" #include "cmExecutionStatus.h" @@ -32,115 +35,35 @@ public: bool TryRunCode(std::vector<std::string> const& args); void RunExecutable(const std::string& runArgs, + cm::optional<std::string> const& workDir, std::string* runOutputContents, std::string* runOutputStdOutContents, std::string* runOutputStdErrContents); void DoNotRunExecutable(const std::string& runArgs, const std::string& srcFile, + std::string const& compileResultVariable, std::string* runOutputContents, std::string* runOutputStdOutContents, std::string* runOutputStdErrContents); - std::string CompileResultVariable; std::string RunResultVariable; - std::string OutputVariable; - std::string RunOutputVariable; - std::string RunOutputStdOutVariable; - std::string RunOutputStdErrVariable; - std::string CompileOutputVariable; - std::string WorkingDirectory; }; bool TryRunCommandImpl::TryRunCode(std::vector<std::string> const& argv) { - // build an arg list for TryCompile and extract the runArgs, - std::vector<std::string> tryCompile; - - this->CompileResultVariable.clear(); - this->RunResultVariable.clear(); - this->OutputVariable.clear(); - this->RunOutputVariable.clear(); - this->RunOutputStdOutVariable.clear(); - this->RunOutputStdErrVariable.clear(); - this->CompileOutputVariable.clear(); - - std::string runArgs; - unsigned int i; - for (i = 1; i < argv.size(); ++i) { - if (argv[i] == "ARGS") { - ++i; - while (i < argv.size() && argv[i] != "COMPILE_DEFINITIONS" && - argv[i] != "CMAKE_FLAGS" && argv[i] != "LINK_OPTIONS" && - argv[i] != "LINK_LIBRARIES") { - runArgs += " "; - runArgs += argv[i]; - ++i; - } - if (i < argv.size()) { - tryCompile.push_back(argv[i]); - } - } else { - if (argv[i] == "OUTPUT_VARIABLE") { - if (argv.size() <= (i + 1)) { - cmSystemTools::Error( - "OUTPUT_VARIABLE specified but there is no variable"); - return false; - } - i++; - this->OutputVariable = argv[i]; - } else if (argv[i] == "RUN_OUTPUT_VARIABLE") { - if (argv.size() <= (i + 1)) { - cmSystemTools::Error( - "RUN_OUTPUT_VARIABLE specified but there is no variable"); - return false; - } - i++; - this->RunOutputVariable = argv[i]; - } else if (argv[i] == "RUN_OUTPUT_STDOUT_VARIABLE") { - if (argv.size() <= (i + 1)) { - cmSystemTools::Error( - "RUN_OUTPUT_STDOUT_VARIABLE specified but there is no variable"); - return false; - } - i++; - this->RunOutputStdOutVariable = argv[i]; - } else if (argv[i] == "RUN_OUTPUT_STDERR_VARIABLE") { - if (argv.size() <= (i + 1)) { - cmSystemTools::Error( - "RUN_OUTPUT_STDERR_VARIABLE specified but there is no variable"); - return false; - } - i++; - this->RunOutputStdErrVariable = argv[i]; - } else if (argv[i] == "COMPILE_OUTPUT_VARIABLE") { - if (argv.size() <= (i + 1)) { - cmSystemTools::Error( - "COMPILE_OUTPUT_VARIABLE specified but there is no variable"); - return false; - } - i++; - this->CompileOutputVariable = argv[i]; - } else if (argv[i] == "WORKING_DIRECTORY") { - if (argv.size() <= (i + 1)) { - cmSystemTools::Error( - "WORKING_DIRECTORY specified but there is no variable"); - return false; - } - i++; - this->WorkingDirectory = argv[i]; - } else { - tryCompile.push_back(argv[i]); - } - } + this->RunResultVariable = argv[0]; + cmCoreTryCompile::Arguments arguments = + this->ParseArgs(cmMakeRange(argv).advance(1), true); + if (!arguments) { + return true; } // although they could be used together, don't allow it, because // using OUTPUT_VARIABLE makes crosscompiling harder - if (!this->OutputVariable.empty() && - (!this->RunOutputVariable.empty() || - !this->CompileOutputVariable.empty() || - !this->RunOutputStdOutVariable.empty() || - !this->RunOutputStdErrVariable.empty())) { + if (arguments.OutputVariable && + (arguments.CompileOutputVariable || arguments.RunOutputVariable || + arguments.RunOutputStdOutVariable || + arguments.RunOutputStdErrVariable)) { cmSystemTools::Error( "You cannot use OUTPUT_VARIABLE together with COMPILE_OUTPUT_VARIABLE " ", RUN_OUTPUT_VARIABLE, RUN_OUTPUT_STDOUT_VARIABLE or " @@ -151,9 +74,9 @@ bool TryRunCommandImpl::TryRunCode(std::vector<std::string> const& argv) return false; } - if ((!this->RunOutputStdOutVariable.empty() || - !RunOutputStdErrVariable.empty()) && - !this->RunOutputVariable.empty()) { + if ((arguments.RunOutputStdOutVariable || + arguments.RunOutputStdErrVariable) && + arguments.RunOutputVariable) { cmSystemTools::Error( "You cannot use RUN_OUTPUT_STDOUT_VARIABLE or " "RUN_OUTPUT_STDERR_VARIABLE together " @@ -162,43 +85,40 @@ bool TryRunCommandImpl::TryRunCode(std::vector<std::string> const& argv) return false; } - if (!this->WorkingDirectory.empty()) { - if (!cmSystemTools::MakeDirectory(this->WorkingDirectory)) { + if (arguments.RunWorkingDirectory) { + if (!cmSystemTools::MakeDirectory(*arguments.RunWorkingDirectory)) { cmSystemTools::Error(cmStrCat("Error creating working directory \"", - this->WorkingDirectory, "\".")); + *arguments.RunWorkingDirectory, "\".")); return false; } } bool captureRunOutput = false; bool captureRunOutputStdOutErr = false; - if (!this->OutputVariable.empty()) { + if (arguments.OutputVariable) { captureRunOutput = true; - tryCompile.emplace_back("OUTPUT_VARIABLE"); - tryCompile.push_back(this->OutputVariable); - } - if (!this->CompileOutputVariable.empty()) { - tryCompile.emplace_back("OUTPUT_VARIABLE"); - tryCompile.push_back(this->CompileOutputVariable); + } else if (arguments.CompileOutputVariable) { + arguments.OutputVariable = arguments.CompileOutputVariable; } - if (!this->RunOutputStdOutVariable.empty() || - !RunOutputStdErrVariable.empty()) { + if (arguments.RunOutputStdOutVariable || arguments.RunOutputStdErrVariable) { captureRunOutputStdOutErr = true; - } else if (!this->RunOutputVariable.empty()) { + } else if (arguments.RunOutputVariable) { captureRunOutput = true; } - this->RunResultVariable = argv[0]; - this->CompileResultVariable = argv[1]; - // do the try compile - bool compiled = this->TryCompileCode(tryCompile, cmStateEnums::EXECUTABLE); + bool compiled = this->TryCompileCode(arguments, cmStateEnums::EXECUTABLE); // now try running the command if it compiled if (compiled) { if (this->OutputFile.empty()) { cmSystemTools::Error(this->FindErrorMessage); } else { + std::string runArgs; + if (arguments.RunArgs) { + runArgs = cmStrCat(" ", cmJoin(*arguments.RunArgs, " ")); + } + // "run" it and capture the output std::string runOutputContents; std::string runOutputStdOutContents; @@ -206,47 +126,51 @@ bool TryRunCommandImpl::TryRunCode(std::vector<std::string> const& argv) if (this->Makefile->IsOn("CMAKE_CROSSCOMPILING") && !this->Makefile->IsDefinitionSet("CMAKE_CROSSCOMPILING_EMULATOR")) { this->DoNotRunExecutable( - runArgs, argv[3], captureRunOutput ? &runOutputContents : nullptr, - captureRunOutputStdOutErr && !RunOutputStdOutVariable.empty() + runArgs, *arguments.SourceDirectoryOrFile, + *arguments.CompileResultVariable, + captureRunOutput ? &runOutputContents : nullptr, + captureRunOutputStdOutErr && arguments.RunOutputStdOutVariable ? &runOutputStdOutContents : nullptr, - captureRunOutputStdOutErr && !RunOutputStdErrVariable.empty() + captureRunOutputStdOutErr && arguments.RunOutputStdErrVariable ? &runOutputStdErrContents : nullptr); } else { this->RunExecutable( - runArgs, captureRunOutput ? &runOutputContents : nullptr, - captureRunOutputStdOutErr && !RunOutputStdOutVariable.empty() + runArgs, arguments.RunWorkingDirectory, + captureRunOutput ? &runOutputContents : nullptr, + captureRunOutputStdOutErr && arguments.RunOutputStdOutVariable ? &runOutputStdOutContents : nullptr, - captureRunOutputStdOutErr && !RunOutputStdErrVariable.empty() + captureRunOutputStdOutErr && arguments.RunOutputStdErrVariable ? &runOutputStdErrContents : nullptr); } // now put the output into the variables - if (!this->RunOutputVariable.empty()) { - this->Makefile->AddDefinition(this->RunOutputVariable, + if (arguments.RunOutputVariable) { + this->Makefile->AddDefinition(*arguments.RunOutputVariable, runOutputContents); } - if (!this->RunOutputStdOutVariable.empty()) { - this->Makefile->AddDefinition(this->RunOutputStdOutVariable, + if (arguments.RunOutputStdOutVariable) { + this->Makefile->AddDefinition(*arguments.RunOutputStdOutVariable, runOutputStdOutContents); } - if (!this->RunOutputStdErrVariable.empty()) { - this->Makefile->AddDefinition(this->RunOutputStdErrVariable, + if (arguments.RunOutputStdErrVariable) { + this->Makefile->AddDefinition(*arguments.RunOutputStdErrVariable, runOutputStdErrContents); } - if (!this->OutputVariable.empty()) { + if (arguments.OutputVariable && !arguments.CompileOutputVariable) { // if the TryCompileCore saved output in this outputVariable then // prepend that output to this output cmValue compileOutput = - this->Makefile->GetDefinition(this->OutputVariable); + this->Makefile->GetDefinition(*arguments.OutputVariable); if (compileOutput) { runOutputContents = *compileOutput + runOutputContents; } - this->Makefile->AddDefinition(this->OutputVariable, runOutputContents); + this->Makefile->AddDefinition(*arguments.OutputVariable, + runOutputContents); } } } @@ -259,6 +183,7 @@ bool TryRunCommandImpl::TryRunCode(std::vector<std::string> const& argv) } void TryRunCommandImpl::RunExecutable(const std::string& runArgs, + cm::optional<std::string> const& workDir, std::string* out, std::string* stdOut, std::string* stdErr) { @@ -286,8 +211,8 @@ void TryRunCommandImpl::RunExecutable(const std::string& runArgs, bool worked = cmSystemTools::RunSingleCommand( finalCommand, stdOut || stdErr ? stdOut : out, stdOut || stdErr ? stdErr : out, &retVal, - this->WorkingDirectory.empty() ? nullptr : this->WorkingDirectory.c_str(), - cmSystemTools::OUTPUT_NONE, cmDuration::zero()); + workDir ? workDir->c_str() : nullptr, cmSystemTools::OUTPUT_NONE, + cmDuration::zero()); // set the run var char retChar[16]; const char* retStr; @@ -306,11 +231,10 @@ void TryRunCommandImpl::RunExecutable(const std::string& runArgs, executable, two cache variables are created which will hold the results the executable would have produced. */ -void TryRunCommandImpl::DoNotRunExecutable(const std::string& runArgs, - const std::string& srcFile, - std::string* out, - std::string* stdOut, - std::string* stdErr) +void TryRunCommandImpl::DoNotRunExecutable( + const std::string& runArgs, const std::string& srcFile, + std::string const& compileResultVariable, std::string* out, + std::string* stdOut, std::string* stdErr) { // copy the executable out of the CMakeFiles/ directory, so it is not // removed at the end of try_run() and the user can run it manually @@ -490,7 +414,7 @@ void TryRunCommandImpl::DoNotRunExecutable(const std::string& runArgs, } comment += "The "; - comment += this->CompileResultVariable; + comment += compileResultVariable; comment += " variable holds the build result for this try_run().\n\n" "Source file : "; comment += srcFile + "\n"; |