소스 검색

cmCoreTryCompile: Port to cmArgumentParser

Brad King 3 년 전
부모
커밋
6b427d8da9

+ 249 - 354
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);
     }
   }

+ 42 - 1
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);
 
   /**

+ 7 - 1
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) {

+ 58 - 134
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";

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

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

+ 7 - 0
Tests/RunCMake/try_compile/NoCStandard-stderr.txt

@@ -0,0 +1,7 @@
+CMake Error at NoCStandard.cmake:1 \(try_compile\):
+  Error after keyword "C_STANDARD":
+
+    missing required value
+
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)

+ 2 - 0
Tests/RunCMake/try_compile/NoCStandard.cmake

@@ -0,0 +1,2 @@
+try_compile(result ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src.c
+  C_STANDARD)

+ 4 - 1
Tests/RunCMake/try_compile/NoCopyFile-stderr.txt

@@ -1,4 +1,7 @@
 CMake Error at NoCopyFile.cmake:1 \(try_compile\):
-  COPY_FILE must be followed by a file path
+  Error after keyword "COPY_FILE":
+
+    missing required value
+
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)

+ 4 - 1
Tests/RunCMake/try_compile/NoCopyFile2-stderr.txt

@@ -1,4 +1,7 @@
 CMake Error at NoCopyFile2.cmake:1 \(try_compile\):
-  COPY_FILE must be followed by a file path
+  Error after keyword "COPY_FILE":
+
+    missing required value
+
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)

+ 4 - 1
Tests/RunCMake/try_compile/NoCopyFileError-stderr.txt

@@ -1,4 +1,7 @@
 CMake Error at NoCopyFileError.cmake:1 \(try_compile\):
-  COPY_FILE_ERROR must be followed by a variable name
+  Error after keyword "COPY_FILE_ERROR":
+
+    missing required value
+
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)

+ 4 - 1
Tests/RunCMake/try_compile/NoOutputVariable-stderr.txt

@@ -1,4 +1,7 @@
 CMake Error at NoOutputVariable.cmake:1 \(try_compile\):
-  OUTPUT_VARIABLE must be followed by a variable name
+  Error after keyword "OUTPUT_VARIABLE":
+
+    missing required value
+
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)

+ 4 - 1
Tests/RunCMake/try_compile/NoOutputVariable2-stderr.txt

@@ -1,4 +1,7 @@
 CMake Error at NoOutputVariable2.cmake:1 \(try_compile\):
-  OUTPUT_VARIABLE must be followed by a variable name
+  Error after keyword "OUTPUT_VARIABLE":
+
+    missing required value
+
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)

+ 4 - 1
Tests/RunCMake/try_compile/NoSources-stderr.txt

@@ -1,4 +1,7 @@
 CMake Error at NoSources.cmake:1 \(try_compile\):
-  SOURCES must be followed by at least one source file
+  Error after keyword "SOURCES":
+
+    missing required value
+
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)

+ 1 - 0
Tests/RunCMake/try_compile/RunCMakeTest.cmake

@@ -7,6 +7,7 @@ run_cmake(TwoArgs)
 run_cmake(NoCopyFile)
 run_cmake(NoCopyFile2)
 run_cmake(NoCopyFileError)
+run_cmake(NoCStandard)
 run_cmake(NoOutputVariable)
 run_cmake(NoOutputVariable2)
 run_cmake(NoSources)

+ 9 - 67
Tests/RunCMake/try_compile/TryRunArgs-stderr.txt

@@ -1,71 +1,13 @@
 ^CMake Warning \(dev\) at TryRunArgs.cmake:[0-9]+ \(try_compile\):
-  try_compile given unknown argument "COMPILE_OUTPUT_VARIABLE".
-Call Stack \(most recent call first\):
-  CMakeLists.txt:[0-9]+ \(include\)
-This warning is for project developers.  Use -Wno-dev to suppress it.
-+
-CMake Warning \(dev\) at TryRunArgs.cmake:[0-9]+ \(try_compile\):
-  try_compile given unknown argument "compOutputVar".
-Call Stack \(most recent call first\):
-  CMakeLists.txt:[0-9]+ \(include\)
-This warning is for project developers.  Use -Wno-dev to suppress it.
-+
-CMake Warning \(dev\) at TryRunArgs.cmake:[0-9]+ \(try_compile\):
-  try_compile given unknown argument "RUN_OUTPUT_VARIABLE".
-Call Stack \(most recent call first\):
-  CMakeLists.txt:[0-9]+ \(include\)
-This warning is for project developers.  Use -Wno-dev to suppress it.
-+
-CMake Warning \(dev\) at TryRunArgs.cmake:[0-9]+ \(try_compile\):
-  try_compile given unknown argument "runOutputVar".
-Call Stack \(most recent call first\):
-  CMakeLists.txt:[0-9]+ \(include\)
-This warning is for project developers.  Use -Wno-dev to suppress it.
-+
-CMake Warning \(dev\) at TryRunArgs.cmake:[0-9]+ \(try_compile\):
-  try_compile given unknown argument "RUN_OUTPUT_STDOUT_VARIABLE".
-Call Stack \(most recent call first\):
-  CMakeLists.txt:[0-9]+ \(include\)
-This warning is for project developers.  Use -Wno-dev to suppress it.
-+
-CMake Warning \(dev\) at TryRunArgs.cmake:[0-9]+ \(try_compile\):
-  try_compile given unknown argument "runOutputStdOutVar".
-Call Stack \(most recent call first\):
-  CMakeLists.txt:[0-9]+ \(include\)
-This warning is for project developers.  Use -Wno-dev to suppress it.
-+
-CMake Warning \(dev\) at TryRunArgs.cmake:[0-9]+ \(try_compile\):
-  try_compile given unknown argument "RUN_OUTPUT_STDERR_VARIABLE".
-Call Stack \(most recent call first\):
-  CMakeLists.txt:[0-9]+ \(include\)
-This warning is for project developers.  Use -Wno-dev to suppress it.
-+
-CMake Warning \(dev\) at TryRunArgs.cmake:[0-9]+ \(try_compile\):
-  try_compile given unknown argument "runOutputStdErrVar".
-Call Stack \(most recent call first\):
-  CMakeLists.txt:[0-9]+ \(include\)
-This warning is for project developers.  Use -Wno-dev to suppress it.
-+
-CMake Warning \(dev\) at TryRunArgs.cmake:[0-9]+ \(try_compile\):
-  try_compile given unknown argument "WORKING_DIRECTORY".
-Call Stack \(most recent call first\):
-  CMakeLists.txt:[0-9]+ \(include\)
-This warning is for project developers.  Use -Wno-dev to suppress it.
-+
-CMake Warning \(dev\) at TryRunArgs.cmake:[0-9]+ \(try_compile\):
-  try_compile given unknown argument "runWorkDir".
-Call Stack \(most recent call first\):
-  CMakeLists.txt:[0-9]+ \(include\)
-This warning is for project developers.  Use -Wno-dev to suppress it.
-+
-CMake Warning \(dev\) at TryRunArgs.cmake:[0-9]+ \(try_compile\):
-  try_compile given unknown argument "ARGS".
-Call Stack \(most recent call first\):
-  CMakeLists.txt:[0-9]+ \(include\)
-This warning is for project developers.  Use -Wno-dev to suppress it.
-+
-CMake Warning \(dev\) at TryRunArgs.cmake:[0-9]+ \(try_compile\):
-  try_compile given unknown argument "runArgs".
+  Ignoring try_run arguments for try_compile:
+
+    COMPILE_OUTPUT_VARIABLE
+    RUN_OUTPUT_VARIABLE
+    RUN_OUTPUT_STDOUT_VARIABLE
+    RUN_OUTPUT_STDERR_VARIABLE
+    WORKING_DIRECTORY
+    ARGS
+
 Call Stack \(most recent call first\):
   CMakeLists.txt:[0-9]+ \(include\)
 This warning is for project developers.  Use -Wno-dev to suppress it.$

+ 5 - 3
Tests/RunCMake/try_run/NoCompileOutputVariable-stderr.txt

@@ -1,5 +1,7 @@
-^CMake Error: COMPILE_OUTPUT_VARIABLE specified but there is no variable
-CMake Error at NoCompileOutputVariable.cmake:[0-9]+ \(try_run\):
-  try_run unknown error.
+^CMake Error at NoCompileOutputVariable.cmake:[0-9]+ \(try_run\):
+  Error after keyword "COMPILE_OUTPUT_VARIABLE":
+
+    missing required value
+
 Call Stack \(most recent call first\):
   CMakeLists.txt:[0-9]+ \(include\)$

+ 5 - 3
Tests/RunCMake/try_run/NoOutputVariable-stderr.txt

@@ -1,5 +1,7 @@
-^CMake Error: OUTPUT_VARIABLE specified but there is no variable
-CMake Error at NoOutputVariable.cmake:[0-9]+ \(try_run\):
-  try_run unknown error.
+^CMake Error at NoOutputVariable.cmake:[0-9]+ \(try_run\):
+  Error after keyword "OUTPUT_VARIABLE":
+
+    missing required value
+
 Call Stack \(most recent call first\):
   CMakeLists.txt:[0-9]+ \(include\)$

+ 5 - 3
Tests/RunCMake/try_run/NoRunOutputVariable-stderr.txt

@@ -1,5 +1,7 @@
-^CMake Error: RUN_OUTPUT_VARIABLE specified but there is no variable
-CMake Error at NoRunOutputVariable.cmake:[0-9]+ \(try_run\):
-  try_run unknown error.
+^CMake Error at NoRunOutputVariable.cmake:[0-9]+ \(try_run\):
+  Error after keyword "RUN_OUTPUT_VARIABLE":
+
+    missing required value
+
 Call Stack \(most recent call first\):
   CMakeLists.txt:[0-9]+ \(include\)$

+ 5 - 3
Tests/RunCMake/try_run/NoRunStdErrVariable-stderr.txt

@@ -1,5 +1,7 @@
-^CMake Error: RUN_OUTPUT_STDERR_VARIABLE specified but there is no variable
-CMake Error at NoRunStdErrVariable.cmake:1 \(try_run\):
-  try_run unknown error.
+^CMake Error at NoRunStdErrVariable.cmake:1 \(try_run\):
+  Error after keyword "RUN_OUTPUT_STDERR_VARIABLE":
+
+    missing required value
+
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)$

+ 5 - 3
Tests/RunCMake/try_run/NoRunStdOutVariable-stderr.txt

@@ -1,5 +1,7 @@
-^CMake Error: RUN_OUTPUT_STDOUT_VARIABLE specified but there is no variable
-CMake Error at NoRunStdOutVariable.cmake:1 \(try_run\):
-  try_run unknown error.
+^CMake Error at NoRunStdOutVariable.cmake:1 \(try_run\):
+  Error after keyword "RUN_OUTPUT_STDOUT_VARIABLE":
+
+    missing required value
+
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)$

+ 5 - 3
Tests/RunCMake/try_run/NoWorkingDirectory-stderr.txt

@@ -1,5 +1,7 @@
-^CMake Error: WORKING_DIRECTORY specified but there is no variable
-CMake Error at NoWorkingDirectory.cmake:[0-9]+ \(try_run\):
-  try_run unknown error.
+^CMake Error at NoWorkingDirectory.cmake:[0-9]+ \(try_run\):
+  Error after keyword "WORKING_DIRECTORY":
+
+    missing required value
+
 Call Stack \(most recent call first\):
   CMakeLists.txt:[0-9]+ \(include\)$