Coverage Report

Created: 2026-10-28 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmAddCustomTargetCommand.cxx
Line
Count
Source
1
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2
   file LICENSE.rst or https://cmake.org/licensing for details.  */
3
#include "cmAddCustomTargetCommand.h"
4
5
#include <utility>
6
7
#include <cm/memory>
8
#include <cm/optional>
9
10
#include "cmCustomCommand.h"
11
#include "cmCustomCommandLines.h"
12
#include "cmExecutionStatus.h"
13
#include "cmGeneratorExpression.h"
14
#include "cmGlobalGenerator.h"
15
#include "cmMakefile.h"
16
#include "cmMessageType.h"
17
#include "cmStringAlgorithms.h"
18
#include "cmSystemTools.h"
19
#include "cmTarget.h"
20
#include "cmValue.h"
21
22
bool cmAddCustomTargetCommand(std::vector<std::string> const& args,
23
                              cmExecutionStatus& status)
24
5.92k
{
25
5.92k
  if (args.empty()) {
26
0
    status.SetError("called with incorrect number of arguments");
27
0
    return false;
28
0
  }
29
30
5.92k
  cmMakefile& mf = status.GetMakefile();
31
5.92k
  std::string const& targetName = args[0];
32
33
  // Check the target name.
34
5.92k
  if (targetName.find_first_of("/\\") != std::string::npos) {
35
0
    status.SetError(cmStrCat("called with invalid target name \"", targetName,
36
0
                             "\".  Target names may not contain a slash.  "
37
0
                             "Use ADD_CUSTOM_COMMAND to generate files."));
38
0
    return false;
39
0
  }
40
41
  // Accumulate one command line at a time.
42
5.92k
  cmCustomCommandLine currentLine;
43
44
  // Save all command lines.
45
5.92k
  cmCustomCommandLines commandLines;
46
47
  // Accumulate dependencies.
48
5.92k
  std::vector<std::string> depends;
49
5.92k
  std::vector<std::string> byproducts;
50
5.92k
  std::string working_directory;
51
5.92k
  bool verbatim = false;
52
5.92k
  bool uses_terminal = false;
53
5.92k
  bool command_expand_lists = false;
54
5.92k
  cm::optional<std::string> comment;
55
5.92k
  std::vector<std::string> sources;
56
5.92k
  std::string job_pool;
57
5.92k
  std::string job_server_aware;
58
59
  // Keep track of parser state.
60
5.92k
  enum tdoing
61
5.92k
  {
62
5.92k
    doing_command,
63
5.92k
    doing_depends,
64
5.92k
    doing_byproducts,
65
5.92k
    doing_working_directory,
66
5.92k
    doing_comment,
67
5.92k
    doing_source,
68
5.92k
    doing_job_pool,
69
5.92k
    doing_job_server_aware,
70
5.92k
    doing_nothing
71
5.92k
  };
72
5.92k
  tdoing doing = doing_command;
73
74
  // Look for the ALL option.
75
5.92k
  bool excludeFromAll = true;
76
5.92k
  unsigned int start = 1;
77
5.92k
  if (args.size() > 1) {
78
5.92k
    if (args[1] == "ALL") {
79
0
      excludeFromAll = false;
80
0
      start = 2;
81
0
    }
82
5.92k
  }
83
84
  // Parse the rest of the arguments.
85
23.7k
  for (unsigned int j = start; j < args.size(); ++j) {
86
17.7k
    std::string const& copy = args[j];
87
88
17.7k
    if (copy == "DEPENDS") {
89
0
      doing = doing_depends;
90
17.7k
    } else if (copy == "BYPRODUCTS") {
91
0
      doing = doing_byproducts;
92
17.7k
    } else if (copy == "WORKING_DIRECTORY") {
93
0
      doing = doing_working_directory;
94
17.7k
    } else if (copy == "VERBATIM") {
95
0
      doing = doing_nothing;
96
0
      verbatim = true;
97
17.7k
    } else if (copy == "USES_TERMINAL") {
98
0
      doing = doing_nothing;
99
0
      uses_terminal = true;
100
17.7k
    } else if (copy == "COMMAND_EXPAND_LISTS") {
101
0
      doing = doing_nothing;
102
0
      command_expand_lists = true;
103
17.7k
    } else if (copy == "COMMENT") {
104
0
      doing = doing_comment;
105
17.7k
    } else if (copy == "JOB_POOL") {
106
0
      doing = doing_job_pool;
107
17.7k
    } else if (copy == "JOB_SERVER_AWARE") {
108
0
      doing = doing_job_server_aware;
109
17.7k
    } else if (copy == "COMMAND") {
110
5.92k
      doing = doing_command;
111
112
      // Save the current command before starting the next command.
113
5.92k
      if (!currentLine.empty()) {
114
0
        commandLines.push_back(currentLine);
115
0
        currentLine.clear();
116
0
      }
117
11.8k
    } else if (copy == "SOURCES") {
118
0
      doing = doing_source;
119
11.8k
    } else {
120
11.8k
      switch (doing) {
121
0
        case doing_working_directory:
122
0
          working_directory = copy;
123
0
          break;
124
11.8k
        case doing_command:
125
11.8k
          currentLine.push_back(copy);
126
11.8k
          break;
127
0
        case doing_byproducts: {
128
0
          std::string filename;
129
0
          if (!cmSystemTools::FileIsFullPath(copy) &&
130
0
              cmGeneratorExpression::Find(copy) != 0) {
131
0
            filename = cmStrCat(mf.GetCurrentBinaryDirectory(), '/');
132
0
          }
133
0
          filename += copy;
134
0
          cmSystemTools::ConvertToUnixSlashes(filename);
135
0
          if (cmSystemTools::FileIsFullPath(filename)) {
136
0
            filename = cmSystemTools::CollapseFullPath(filename);
137
0
          }
138
0
          byproducts.push_back(filename);
139
0
        } break;
140
0
        case doing_depends: {
141
0
          std::string dep = copy;
142
0
          cmSystemTools::ConvertToUnixSlashes(dep);
143
0
          depends.push_back(std::move(dep));
144
0
        } break;
145
0
        case doing_comment:
146
0
          comment = copy;
147
0
          break;
148
0
        case doing_source:
149
0
          sources.push_back(copy);
150
0
          break;
151
0
        case doing_job_pool:
152
0
          job_pool = copy;
153
0
          break;
154
0
        case doing_job_server_aware:
155
0
          job_server_aware = copy;
156
0
          break;
157
0
        default:
158
0
          status.SetError("Wrong syntax. Unknown type of argument.");
159
0
          return false;
160
11.8k
      }
161
11.8k
    }
162
17.7k
  }
163
164
5.92k
  bool nameOk = cmGeneratorExpression::IsValidTargetName(targetName) &&
165
5.92k
    !cmGlobalGenerator::IsReservedTarget(targetName) &&
166
5.92k
    targetName.find(':') == std::string::npos;
167
5.92k
  if (!nameOk) {
168
0
    mf.IssueInvalidTargetNameError(targetName);
169
0
    return false;
170
0
  }
171
172
  // Store the last command line finished.
173
5.92k
  if (!currentLine.empty()) {
174
5.92k
    commandLines.push_back(currentLine);
175
5.92k
    currentLine.clear();
176
5.92k
  }
177
178
  // Enforce name uniqueness.
179
5.92k
  {
180
5.92k
    std::string msg;
181
5.92k
    if (!mf.EnforceUniqueName(targetName, msg, true)) {
182
0
      status.SetError(msg);
183
0
      return false;
184
0
    }
185
5.92k
  }
186
187
5.92k
  if (commandLines.empty() && !byproducts.empty()) {
188
0
    mf.IssueMessage(MessageType::FATAL_ERROR,
189
0
                    "BYPRODUCTS may not be specified without any COMMAND");
190
0
    return true;
191
0
  }
192
5.92k
  if (commandLines.empty() && uses_terminal) {
193
0
    mf.IssueMessage(MessageType::FATAL_ERROR,
194
0
                    "USES_TERMINAL may not be specified without any COMMAND");
195
0
    return true;
196
0
  }
197
5.92k
  if (commandLines.empty() && command_expand_lists) {
198
0
    mf.IssueMessage(
199
0
      MessageType::FATAL_ERROR,
200
0
      "COMMAND_EXPAND_LISTS may not be specified without any COMMAND");
201
0
    return true;
202
0
  }
203
204
5.92k
  if (uses_terminal && !job_pool.empty()) {
205
0
    status.SetError("JOB_POOL is shadowed by USES_TERMINAL.");
206
0
    return false;
207
0
  }
208
209
  // Add the utility target to the makefile.
210
5.92k
  auto cc = cm::make_unique<cmCustomCommand>();
211
5.92k
  cc->SetWorkingDirectory(working_directory);
212
5.92k
  cc->SetByproducts(byproducts);
213
5.92k
  cc->SetDepends(depends);
214
5.92k
  cc->SetCommandLines(commandLines);
215
5.92k
  cc->SetEscapeOldStyle(!verbatim);
216
5.92k
  cc->SetComment(comment);
217
5.92k
  cc->SetUsesTerminal(uses_terminal);
218
5.92k
  cc->SetCommandExpandLists(command_expand_lists);
219
5.92k
  cc->SetJobPool(job_pool);
220
5.92k
  cc->SetJobserverAware(cmIsOn(job_server_aware));
221
5.92k
  cmTarget* target =
222
5.92k
    mf.AddUtilityCommand(targetName, excludeFromAll, std::move(cc));
223
224
  // Add additional user-specified source files to the target.
225
5.92k
  target->AddSources(sources);
226
227
5.92k
  return true;
228
5.92k
}