blob: 17285e723adc2b6001571394ff75a00418b9904a [file] [log] [blame]
Brad King86578ec2016-09-27 15:01:08 -04001/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
Bill Hoffman2d37e6d2002-04-30 14:00:35 -04003#include "cmSeparateArgumentsCommand.h"
4
Daniel Pfeifere81c3232016-10-25 20:35:04 +02005#include <algorithm>
Daniel Pfeifere81c3232016-10-25 20:35:04 +02006
Marc Chevrier79362cf2021-09-13 16:55:00 +02007#include <cm/string_view>
Marc Chevrierf4c21d42020-09-22 15:04:03 +02008#include <cmext/string_view>
9
10#include "cmArgumentParser.h"
Gabor Bencze2a929972019-08-04 19:05:36 +020011#include "cmExecutionStatus.h"
Daniel Pfeifere81c3232016-10-25 20:35:04 +020012#include "cmMakefile.h"
Marc Chevrierf4c21d42020-09-22 15:04:03 +020013#include "cmRange.h"
Asit Dhal9dba84c2019-09-15 19:11:02 +020014#include "cmStringAlgorithms.h"
Daniel Pfeifer608afd42016-10-19 22:30:58 +020015#include "cmSystemTools.h"
Marc Chevriercc56dc72021-09-21 17:12:35 +020016#include "cmValue.h"
Daniel Pfeifer608afd42016-10-19 22:30:58 +020017
Bill Hoffman2d37e6d2002-04-30 14:00:35 -040018// cmSeparateArgumentsCommand
Gabor Bencze2a929972019-08-04 19:05:36 +020019bool cmSeparateArgumentsCommand(std::vector<std::string> const& args,
20 cmExecutionStatus& status)
Bill Hoffman2d37e6d2002-04-30 14:00:35 -040021{
Kitware Robotd9fd2f52016-05-16 10:34:04 -040022 if (args.empty()) {
Gabor Bencze2a929972019-08-04 19:05:36 +020023 status.SetError("must be given at least one argument.");
Bill Hoffman2d37e6d2002-04-30 14:00:35 -040024 return false;
Kitware Robotd9fd2f52016-05-16 10:34:04 -040025 }
Brad Kingb23b1802009-07-14 10:15:47 -040026
Marc Chevrierf4c21d42020-09-22 15:04:03 +020027 std::string const& var = args.front();
Brad Kingb23b1802009-07-14 10:15:47 -040028
Marc Chevrierf4c21d42020-09-22 15:04:03 +020029 if (args.size() == 1) {
Brad Kingb23b1802009-07-14 10:15:47 -040030 // Original space-replacement version of command.
Marc Chevriercc56dc72021-09-21 17:12:35 +020031 if (cmValue def = status.GetMakefile().GetDefinition(var)) {
Vitaly Stakhovsky11425042020-08-01 15:00:00 -040032 std::string value = *def;
Daniel Pfeifer5cec9532016-05-24 22:58:11 +020033 std::replace(value.begin(), value.end(), ' ', ';');
Gabor Bencze2a929972019-08-04 19:05:36 +020034 status.GetMakefile().AddDefinition(var, value);
Brad Kingb23b1802009-07-14 10:15:47 -040035 }
Brad Kingb23b1802009-07-14 10:15:47 -040036
Marc Chevrierf4c21d42020-09-22 15:04:03 +020037 return true;
Kitware Robotd9fd2f52016-05-16 10:34:04 -040038 }
Brad Kingb23b1802009-07-14 10:15:47 -040039
Marc Chevrierf4c21d42020-09-22 15:04:03 +020040 struct Arguments
41 {
42 bool UnixCommand = false;
43 bool WindowsCommand = false;
44 bool NativeCommand = false;
Marc Chevrierd832c1c2020-09-22 16:31:29 +020045 bool Program = false;
46 bool SeparateArgs = false;
Marc Chevrierf4c21d42020-09-22 15:04:03 +020047 };
48
49 static auto const parser =
50 cmArgumentParser<Arguments>{}
51 .Bind("UNIX_COMMAND"_s, &Arguments::UnixCommand)
52 .Bind("WINDOWS_COMMAND"_s, &Arguments::WindowsCommand)
Marc Chevrierd832c1c2020-09-22 16:31:29 +020053 .Bind("NATIVE_COMMAND"_s, &Arguments::NativeCommand)
54 .Bind("PROGRAM"_s, &Arguments::Program)
55 .Bind("SEPARATE_ARGS"_s, &Arguments::SeparateArgs);
Marc Chevrierf4c21d42020-09-22 15:04:03 +020056
57 std::vector<std::string> unparsedArguments;
58 Arguments arguments =
59 parser.Parse(cmMakeRange(args).advance(1), &unparsedArguments);
60
61 if (!arguments.UnixCommand && !arguments.WindowsCommand &&
62 !arguments.NativeCommand) {
63 status.SetError("missing required option: 'UNIX_COMMAND' or "
64 "'WINDOWS_COMMAND' or 'NATIVE_COMMAND'");
65 return false;
66 }
67 if ((arguments.UnixCommand && arguments.WindowsCommand) ||
68 (arguments.UnixCommand && arguments.NativeCommand) ||
69 (arguments.WindowsCommand && arguments.NativeCommand)) {
70 status.SetError("'UNIX_COMMAND', 'WINDOWS_COMMAND' and 'NATIVE_COMMAND' "
71 "are mutually exclusive");
72 return false;
73 }
Marc Chevrierd832c1c2020-09-22 16:31:29 +020074 if (arguments.SeparateArgs && !arguments.Program) {
75 status.SetError("`SEPARATE_ARGS` option requires `PROGRAM' option");
76 return false;
77 }
Marc Chevrierf4c21d42020-09-22 15:04:03 +020078
79 if (unparsedArguments.size() > 1) {
80 status.SetError("given unexpected argument(s)");
81 return false;
82 }
83
Robert Bozzetto747f80f2020-10-16 12:00:47 +110084 if (unparsedArguments.empty()) {
Marc Chevrier79362cf2021-09-13 16:55:00 +020085 status.GetMakefile().AddDefinition(var, cm::string_view{});
Robert Bozzetto747f80f2020-10-16 12:00:47 +110086 return true;
87 }
88
Marc Chevrierf4c21d42020-09-22 15:04:03 +020089 std::string& command = unparsedArguments.front();
90
91 if (command.empty()) {
92 status.GetMakefile().AddDefinition(var, command);
93 return true;
94 }
95
Marc Chevrierd832c1c2020-09-22 16:31:29 +020096 if (arguments.Program && !arguments.SeparateArgs) {
97 std::string program;
98 std::string programArgs;
99
100 // First assume the path to the program was specified with no
101 // arguments and with no quoting or escaping for spaces.
102 // Only bother doing this if there is non-whitespace.
103 if (!cmTrimWhitespace(command).empty()) {
104 program = cmSystemTools::FindProgram(command);
105 }
106
107 // If that failed then assume a command-line string was given
108 // and split the program part from the rest of the arguments.
109 if (program.empty()) {
110 if (cmSystemTools::SplitProgramFromArgs(command, program, programArgs)) {
111 if (!cmSystemTools::FileExists(program)) {
112 program = cmSystemTools::FindProgram(program);
113 }
114 }
115 }
116
117 if (!program.empty()) {
118 program += cmStrCat(';', programArgs);
119 }
120
121 status.GetMakefile().AddDefinition(var, program);
122 return true;
123 }
124
Marc Chevrierf4c21d42020-09-22 15:04:03 +0200125 // split command given
126 std::vector<std::string> values;
127
128 if (arguments.NativeCommand) {
129#if defined(_WIN32)
130 arguments.WindowsCommand = true;
131#else
132 arguments.UnixCommand = true;
133#endif
134 }
135
136 if (arguments.UnixCommand) {
137 cmSystemTools::ParseUnixCommandLine(command.c_str(), values);
138 } else {
139 cmSystemTools::ParseWindowsCommandLine(command.c_str(), values);
140 }
141
Marc Chevrierd832c1c2020-09-22 16:31:29 +0200142 if (arguments.Program) {
143 // check program exist
144 if (!cmSystemTools::FileExists(values.front())) {
145 auto result = cmSystemTools::FindProgram(values.front());
146 if (result.empty()) {
147 values.clear();
148 } else {
149 values.front() = result;
150 }
151 }
152 }
153
Marc Chevrierf4c21d42020-09-22 15:04:03 +0200154 // preserve semicolons in arguments
155 std::for_each(values.begin(), values.end(), [](std::string& value) {
156 std::string::size_type pos = 0;
157 while ((pos = value.find_first_of(';', pos)) != std::string::npos) {
158 value.insert(pos, 1, '\\');
159 pos += 2;
160 }
161 });
162 auto value = cmJoin(values, ";");
163 status.GetMakefile().AddDefinition(var, value);
164
Bill Hoffman2d37e6d2002-04-30 14:00:35 -0400165 return true;
166}