Browse Source

Merge topic 'issue-20915'

6d0712c225 file(configure): Order of named options should be specified in any order

Acked-by: Kitware Robot <[email protected]>
Merge-request: !5608
Brad King 5 years ago
parent
commit
807c903c55

+ 55 - 30
Source/cmFileCommand.cxx

@@ -2946,17 +2946,60 @@ bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args,
 bool HandleConfigureCommand(std::vector<std::string> const& args,
                             cmExecutionStatus& status)
 {
-  if (args.size() < 5) {
-    status.SetError("Incorrect arguments to CONFIGURE subcommand.");
+  struct Arguments
+  {
+    std::string Output;
+    std::string Content;
+    bool EscapeQuotes = false;
+    bool AtOnly = false;
+    std::string NewlineStyle;
+  };
+
+  static auto const parser =
+    cmArgumentParser<Arguments>{}
+      .Bind("OUTPUT"_s, &Arguments::Output)
+      .Bind("CONTENT"_s, &Arguments::Content)
+      .Bind("ESCAPE_QUOTES"_s, &Arguments::EscapeQuotes)
+      .Bind("@ONLY"_s, &Arguments::AtOnly)
+      .Bind("NEWLINE_STYLE"_s, &Arguments::NewlineStyle);
+
+  std::vector<std::string> unrecognizedArguments;
+  std::vector<std::string> keywordsMissingArguments;
+  std::vector<std::string> parsedKeywords;
+  auto parsedArgs =
+    parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments,
+                 &keywordsMissingArguments, &parsedKeywords);
+
+  auto argIt = unrecognizedArguments.begin();
+  if (argIt != unrecognizedArguments.end()) {
+    status.SetError(
+      cmStrCat("CONFIGURE Unrecognized argument: \"", *argIt, "\""));
+    cmSystemTools::SetFatalErrorOccured();
     return false;
   }
-  if (args[1] != "OUTPUT") {
-    status.SetError("Incorrect arguments to CONFIGURE subcommand.");
-    return false;
+
+  std::vector<std::string> mandatoryOptions{ "OUTPUT", "CONTENT" };
+  for (auto const& e : mandatoryOptions) {
+    const bool optionHasNoValue =
+      std::find(keywordsMissingArguments.begin(),
+                keywordsMissingArguments.end(),
+                e) != keywordsMissingArguments.end();
+    if (optionHasNoValue) {
+      status.SetError(cmStrCat("CONFIGURE ", e, " option needs a value."));
+      cmSystemTools::SetFatalErrorOccured();
+      return false;
+    }
   }
-  if (args[3] != "CONTENT") {
-    status.SetError("Incorrect arguments to CONFIGURE subcommand.");
-    return false;
+
+  for (auto const& e : mandatoryOptions) {
+    const bool optionGiven =
+      std::find(parsedKeywords.begin(), parsedKeywords.end(), e) !=
+      parsedKeywords.end();
+    if (!optionGiven) {
+      status.SetError(cmStrCat("CONFIGURE ", e, " option is mandatory."));
+      cmSystemTools::SetFatalErrorOccured();
+      return false;
+    }
   }
 
   std::string errorMessage;
@@ -2966,28 +3009,9 @@ bool HandleConfigureCommand(std::vector<std::string> const& args,
     return false;
   }
 
-  bool escapeQuotes = false;
-  bool atOnly = false;
-  for (unsigned int i = 5; i < args.size(); ++i) {
-    if (args[i] == "@ONLY") {
-      atOnly = true;
-    } else if (args[i] == "ESCAPE_QUOTES") {
-      escapeQuotes = true;
-    } else if (args[i] == "NEWLINE_STYLE" || args[i] == "LF" ||
-               args[i] == "UNIX" || args[i] == "CRLF" || args[i] == "WIN32" ||
-               args[i] == "DOS") {
-      /* Options handled by NewLineStyle member above.  */
-    } else {
-      status.SetError(
-        cmStrCat("CONFIGURE Unrecognized argument \"", args[i], "\""));
-      return false;
-    }
-  }
-
   // Check for generator expressions
-  const std::string input = args[4];
   std::string outputFile = cmSystemTools::CollapseFullPath(
-    args[2], status.GetMakefile().GetCurrentBinaryDirectory());
+    parsedArgs.Output, status.GetMakefile().GetCurrentBinaryDirectory());
 
   std::string::size_type pos = outputFile.find_first_of("<>");
   if (pos != std::string::npos) {
@@ -3036,12 +3060,13 @@ bool HandleConfigureCommand(std::vector<std::string> const& args,
   fout.SetCopyIfDifferent(true);
 
   // copy input to output and expand variables from input at the same time
-  std::stringstream sin(input, std::ios::in);
+  std::stringstream sin(parsedArgs.Content, std::ios::in);
   std::string inLine;
   std::string outLine;
   while (cmSystemTools::GetLineFromStream(sin, inLine)) {
     outLine.clear();
-    makeFile.ConfigureString(inLine, outLine, atOnly, escapeQuotes);
+    makeFile.ConfigureString(inLine, outLine, parsedArgs.AtOnly,
+                             parsedArgs.EscapeQuotes);
     fout << outLine << newLineCharacters;
   }
 

+ 12 - 0
Tests/RunCMake/File_Configure/AtOnly.cmake

@@ -0,0 +1,12 @@
+set(file_name  ${CMAKE_CURRENT_BINARY_DIR}/atonly.txt)
+set(var_a "a")
+set(var_b "b")
+file(CONFIGURE
+    OUTPUT ${file_name}
+    CONTENT "-->@var_a@<-- -->${var_b}<--"
+    @ONLY
+)
+file(READ ${file_name} file_content)
+if(NOT file_content STREQUAL "-->a<-- -->${var_b}<--")
+    message(FATAL_ERROR "@ONLY doesn't work")
+endif()

+ 0 - 4
Tests/RunCMake/File_Configure/BadArg-stderr.txt

@@ -1,4 +0,0 @@
-CMake Error at BadArg.cmake:[0-9]+ \(file\):
-  file Incorrect arguments to CONFIGURE subcommand.
-Call Stack \(most recent call first\):
-  CMakeLists.txt:3 \(include\)

+ 0 - 0
Tests/RunCMake/File_Configure/BadArgContent-result.txt


+ 4 - 0
Tests/RunCMake/File_Configure/BadArgContent-stderr.txt

@@ -0,0 +1,4 @@
+CMake Error at BadArgContent.cmake:[0-9]+ \(file\):
+  file CONFIGURE CONTENT option needs a value.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)

+ 1 - 0
Tests/RunCMake/File_Configure/BadArgContent.cmake

@@ -0,0 +1 @@
+file(CONFIGURE CONTENT)

+ 0 - 0
Tests/RunCMake/File_Configure/BadArg-result.txt → Tests/RunCMake/File_Configure/BadArgOutput-result.txt


+ 4 - 0
Tests/RunCMake/File_Configure/BadArgOutput-stderr.txt

@@ -0,0 +1,4 @@
+CMake Error at BadArgOutput.cmake:[0-9]+ \(file\):
+  file CONFIGURE OUTPUT option needs a value.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)

+ 0 - 0
Tests/RunCMake/File_Configure/BadArg.cmake → Tests/RunCMake/File_Configure/BadArgOutput.cmake


+ 12 - 0
Tests/RunCMake/File_Configure/EscapeQuotes.cmake

@@ -0,0 +1,12 @@
+set(file_name  ${CMAKE_CURRENT_BINARY_DIR}/escape_quotes.txt)
+set(var "\t")
+set(ref "${var}")
+file(CONFIGURE
+    CONTENT "-->@ref@<--"
+    OUTPUT ${file_name}
+    ESCAPE_QUOTES
+)
+file(READ ${file_name} file_content)
+if(NOT file_content MATCHES "^-->\t<--$")
+    message(FATAL_ERROR "ESCAPE_QUOTES doesn't work")
+endif()

+ 5 - 1
Tests/RunCMake/File_Configure/RunCMakeTest.cmake

@@ -1,10 +1,14 @@
 include(RunCMake)
 
 run_cmake(AngleBracketsContent)
-run_cmake(BadArg)
+run_cmake(BadArgOutput)
+run_cmake(BadArgContent)
 run_cmake(BadArgGeneratorExpressionOutput)
+run_cmake(UnrecognizedArgs)
 run_cmake(DirOutput)
 run_cmake(NewLineStyle-NoArg)
 run_cmake(NewLineStyle-ValidArg)
 run_cmake(NewLineStyle-WrongArg)
 run_cmake(SubDir)
+run_cmake(AtOnly)
+run_cmake(EscapeQuotes)

+ 1 - 0
Tests/RunCMake/File_Configure/UnrecognizedArgs-result.txt

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

+ 4 - 0
Tests/RunCMake/File_Configure/UnrecognizedArgs-stderr.txt

@@ -0,0 +1,4 @@
+CMake Error at UnrecognizedArgs.cmake:[0-9]+ \(file\):
+  file CONFIGURE Unrecognized argument: "INPUT"
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 1 - 0
Tests/RunCMake/File_Configure/UnrecognizedArgs.cmake

@@ -0,0 +1 @@
+file(CONFIGURE INPUT)