aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/CommandLineArgsApi.java
blob: 1a95b346122d373bfdc5e255d57e28bcd0c2df6c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
// Copyright 2018 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.devtools.build.lib.skylarkbuildapi;

import com.google.devtools.build.lib.events.Location;
import com.google.devtools.build.lib.skylarkinterface.Param;
import com.google.devtools.build.lib.skylarkinterface.ParamType;
import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable;
import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory;
import com.google.devtools.build.lib.skylarkinterface.SkylarkValue;
import com.google.devtools.build.lib.syntax.BaseFunction;
import com.google.devtools.build.lib.syntax.EvalException;
import com.google.devtools.build.lib.syntax.Runtime;
import com.google.devtools.build.lib.syntax.SkylarkList;
import com.google.devtools.build.lib.syntax.SkylarkNestedSet;

/** Command line args module. */
@SkylarkModule(
  name = "Args",
  category = SkylarkModuleCategory.BUILTIN,
  doc =
      "An object that encapsulates, in a memory-efficient way, the data needed to build part or "
          + "all of a command line."
          + ""
          + "<p>It often happens that an action requires a large command line containing values "
          + "accumulated from transitive dependencies. For example, a linker command line might "
          + "list every object file needed by all of the libraries being linked. It is best "
          + "practice to store such transitive data in <a href='depset.html'><code>depset"
          + "</code></a>s, so that they can be shared by multiple targets. However, if the rule "
          + "author had to convert these depsets into lists of strings in order to construct an "
          + "action command line, it would defeat this memory-sharing optimization."
          + ""
          + "<p>For this reason, the action-constructing functions accept <code>Args</code> "
          + "objects in addition to strings. Each <code>Args</code> object represents a "
          + "concatenation of strings and depsets, with optional transformations for "
          + "manipulating the data. <code>Args</code> objects do not process the depsets they "
          + "encapsulate until the execution phase, when it comes time to calculate the command "
          + "line. This helps defer any expensive copying until after the analysis phase is "
          + "complete. See the <a href='../performance.$DOC_EXT'>Optimizing Performance</a> page "
          + "for more information."
          + ""
          + "<p><code>Args</code> are constructed by calling <a href='actions.html#args'><code>"
          + "ctx.actions.args()</code></a>. They can be passed as the <code>arguments</code> "
          + "parameter of <a href='actions.html#run'><code>ctx.actions.run()</code></a> or "
          + "<a href='actions.html#run_shell'><code>ctx.actions.run_shell()</code></a>. Each "
          + "mutation of an <code>Args</code> object appends values to the eventual command "
          + "line."
          + ""
          + "<p>The <code>map_each</code> feature allows you to customize how items are "
          + "transformed into strings. If you do not provide a <code>map_each</code> function, "
          + "the standard conversion is as follows: "
          + "<ul>"
          + "<li>Values that are already strings are left as-is."
          + "<li><a href='File.html'><code>File</code></a> objects are turned into their "
          + "    <code>File.path</code> values."
          + "<li>All other types are turned into strings in an <i>unspecified</i> manner. For "
          + "    this reason, you should avoid passing values that are not of string or "
          + "    <code>File</code> type to <code>add()</code>, and if you pass them to "
          + "    <code>add_all()</code> or <code>add_joined()</code> then you should provide a "
          + "    <code>map_each</code> function."
          + "</ul>"
          + ""
          + "<p>When using string formatting (<code>format</code>, <code>format_each</code>, and "
          + "<code>format_joined</code> params of the <code>add*()</code> methods), the format "
          + "template is interpreted in the same way as <code>%</code>-substitution on strings, "
          + "except that the template must have exactly one substitution placeholder and it must "
          + "be <code>%s</code>. Literal percents may be escaped as <code>%%</code>. Formatting "
          + "is applied after the value is converted to a string as per the above."
          + ""
          + "<p>Each of the <code>add*()</code> methods have an alternate form that accepts an "
          + "extra positional parameter, an \"arg name\" string to insert before the rest of the "
          + "arguments. For <code>add_all</code> and <code>add_joined</code> the extra string "
          + "will not be added if the sequence turns out to be empty. "
          + "For instance, the same usage can add either <code>--foo val1 val2 val3 --bar"
          + "</code> or just <code>--bar</code> to the command line, depending on whether the "
          + "given sequence contains <code>val1..val3</code> or is empty."
          + ""
          + "<p>If the size of the command line can grow longer than the maximum size allowed by "
          + "the system, the arguments can be spilled over into parameter files. See "
          + "<a href='#use_param_file'><code>use_param_file()</code></a> and "
          + "<a href='#set_param_file_format'><code>set_param_file_format()</code></a>."
          + ""
          + "<p>Example: Suppose we wanted to generate the command line: "
          + "<pre>\n"
          + "--foo foo1.txt foo2.txt ... fooN.txt --bar bar1.txt,bar2.txt,...,barM.txt --baz\n"
          + "</pre>"
          + "We could use the following <code>Args</code> object: "
          + "<pre class=language-python>\n"
          + "# foo_deps and bar_deps are depsets containing\n"
          + "# File objects for the foo and bar .txt files.\n"
          + "args = ctx.actions.args()\n"
          + "args.add_all(\"--foo\", foo_deps)\n"
          + "args.add_joined(\"--bar\", bar_deps, join_with=\",\")\n"
          + "args.add(\"--baz\")\n"
          + "ctx.actions.run(\n"
          + "  ...\n"
          + "  arguments = [args],\n"
          + "  ...\n"
          + ")\n"
          + "</pre>"
)
public interface CommandLineArgsApi extends SkylarkValue {
  @SkylarkCallable(
    name = "add",
    doc =
        "Appends an argument to this command line."
            + ""
            + "<p><b>Deprecation note:</b> The <code>before_each</code>, <code>join_with</code> "
            + "and <code>map_fn</code> params are replaced by the <a href='#add_all'><code>"
            + "add_all()</code></a> and <a href='#add_joined'><code>add_joined()</code></a> "
            + "methods. These parameters will be removed, and are currently disallowed if the "
            + "<a href='../backward-compatibility.$DOC_EXT#new-args-api'><code>"
            + "--incompatible_disallow_old_style_args_add</code></a> flag is set. Likewise, "
            + "<code>value</code> should now be a scalar value, not a list, tuple, or depset of "
            + "items.",
    parameters = {
      @Param(
          name = "arg_name_or_value",
          doc =
              "If two positional parameters are passed this is interpreted as the arg name. "
                  + "The arg name is added before the value without any processing. "
                  + "If only one positional parameter is passed, it is interpreted as "
                  + "<code>value</code> (see below)."),
      @Param(
          name = "value",
          defaultValue = "unbound",
          doc =
              "The object to append. It will be converted to a string using the standard "
                  + "conversion mentioned above. Since there is no <code>map_each</code> parameter "
                  + "for this function, <code>value</code> should be either a string or a <code>"
                  + "File</code>."
                  + ""
                  + "<p><i>Deprecated behavior:</i> <code>value</code> may also be a list, tuple, "
                  + "or depset of multiple items to append."),
      @Param(
          name = "format",
          type = String.class,
          named = true,
          positional = false,
          defaultValue = "None",
          noneable = true,
          doc =
              "A format string pattern, to be applied to the stringified version of <code>value"
                  + "</code>."
                  + ""
                  + "<p><i>Deprecated behavior:</i> If <code>value</code> is a list or depset, "
                  + "formatting is applied to each item."),
      @Param(
          name = "before_each",
          type = String.class,
          named = true,
          positional = false,
          defaultValue = "None",
          noneable = true,
          doc =
              "<i>Deprecated:</i> Only supported when <code>value</code> is a list, tuple, or "
                  + "depset. This string will be appended prior to appending each item."),
      @Param(
          name = "join_with",
          type = String.class,
          named = true,
          positional = false,
          defaultValue = "None",
          noneable = true,
          doc =
              "<i>Deprecated:</i> Only supported when <code>value</code> is a list, tuple, or "
                  + "depset. All items will be joined together using this string to form a single "
                  + "arg to append."),
      @Param(
          name = "map_fn",
          type = BaseFunction.class,
          named = true,
          positional = false,
          defaultValue = "None",
          noneable = true,
          doc =
              "<i>Deprecated:</i> Only supported when <code>value</code> is a list, tuple, or "
                  + "depset. This is a function that transforms the sequence of items into a list "
                  + "of strings. The sequence of items is given as a positional argument -- the "
                  + "function must not take any other parameters -- and the returned list's length "
                  + "must equal the number of items. Use <code>map_each</code> of <code>add_all"
                  + "</code> or <code>add_joined</code> instead.")
    },
    useLocation = true
  )
  public Runtime.NoneType addArgument(
      Object argNameOrValue,
      Object value,
      Object format,
      Object beforeEach,
      Object joinWith,
      Object mapFn,
      Location loc)
      throws EvalException;

  @SkylarkCallable(
    name = "add_all",
    doc =
        "Appends multiple arguments to this command line. For depsets, the items are "
            + "evaluated lazily during the execution phase."
            + ""
            + "<p>Most of the processing occurs over a list of arguments to be appended, as per "
            + "the following steps:"
            + "<ol>"
            + "<li>If <code>map_each</code> is given, it is applied to each input item, and the "
            + "    resulting lists of strings are concatenated to form the initial argument "
            + "    list. Otherwise, the initial argument list is the result of applying the "
            + "    standard conversion to each item."
            + "<li>Each argument in the list is formatted with <code>format_each</code>, if "
            + "    present."
            + "<li>If <code>uniquify</code> is true, duplicate arguments are removed. The first "
            + "    occurrence is the one that remains."
            + "<li>If a <code>before_each</code> string is given, it is inserted as a new "
            + "    argument before each existing argument in the list. This effectively doubles "
            + "    the number of arguments to be appended by this point."
            + "<li>Except in the case that the list is empty and <code>omit_if_empty</code> is "
            + "    true (the default), the arg name and <code>terminate_with</code> are "
            + "    inserted as the first and last arguments, respectively, if they are given."
            + "</ol>"
            + "Note that empty strings are valid arguments that are subject to all these "
            + "processing steps.",
    parameters = {
      @Param(
        name = "arg_name_or_values",
        doc =
            "If two positional parameters are passed this is interpreted as the arg name. "
                + "The arg name is added before the <code>values</code> without any processing. "
                + "This arg name will not be added if <code>omit_if_empty</code> is true "
                + "(the default) and no other items are appended (as happens if "
                + "<code>values</code> is empty or all of its items are filtered). "
                + "If only one positional parameter is passed, it is interpreted as "
                + "<code>values</code> (see below)."
      ),
      @Param(
        name = "values",
        allowedTypes = {
          @ParamType(type = SkylarkList.class),
          @ParamType(type = SkylarkNestedSet.class),
        },
        defaultValue = "unbound",
        doc = "The list, tuple, or depset whose items will be appended."
      ),
      @Param(
        name = "map_each",
        type = BaseFunction.class,
        named = true,
        positional = false,
        defaultValue = "None",
        noneable = true,
        doc =
            "A function that converts each item to zero or more strings, which may be further "
                + "processed before appending. If this param is not provided, the standard "
                + "conversion is used."
                + ""
                + "<p>The function takes in the item as a positional parameter and must have no "
                + "other parameters. The return value's type depends on how many arguments "
                + "are to be produced for the item:"
                + "<ul>"
                + "<li>In the common case when each item turns into one string, the function "
                + "    should return that string."
                + "<li>If the item is to be filtered out entirely, the function should return "
                + "    <code>None</code>."
                + "<li>If the item turns into multiple strings, the function returns a list of "
                + "    those strings."
                + "</ul>"
                + "Returning a single string or <code>None</code> has the same effect as "
                + "returning a list of length 1 or length 0 respectively. However, it is more "
                + "efficient and readable to avoid creating a list where it is not needed."
                + ""
                + "<p><i>Warning:</i> <a href='globals.html#print'><code>print()</code></a> "
                + "statements that are executed during the call to <code>map_each</code> will "
                + "not produce any visible output."
      ),
      @Param(
        name = "format_each",
        type = String.class,
        named = true,
        positional = false,
        defaultValue = "None",
        noneable = true,
        doc =
            "An optional format string pattern, applied to each string returned by the "
                + "<code>map_each</code> function. "
                + "The format string must have exactly one '%s' placeholder."
      ),
      @Param(
        name = "before_each",
        type = String.class,
        named = true,
        positional = false,
        defaultValue = "None",
        noneable = true,
        doc =
            "An optional string to append before each argument derived from <code>values</code> "
                + "is appended."
      ),
      @Param(
        name = "omit_if_empty",
        type = Boolean.class,
        named = true,
        positional = false,
        defaultValue = "True",
        doc =
            "If true, if there are no arguments derived from <code>values</code> to be "
                + "appended, then all further processing is suppressed and the command line will "
                + "be unchanged. If false, the arg name and <code>terminate_with</code>, "
                + "if provided, will still be appended regardless of whether or not there are "
                + "other arguments."
      ),
      @Param(
        name = "uniquify",
        type = Boolean.class,
        named = true,
        positional = false,
        defaultValue = "False",
        doc =
            "If true, duplicate arguments that are derived from <code>values</code> will be "
                + "omitted. Only the first occurrence of each argument will remain. Usually this "
                + "feature is not needed because depsets already omit duplicates, but it can be "
                + "useful if <code>map_each</code> emits the same string for multiple items."
      ),
      @Param(
        name = "terminate_with",
        type = String.class,
        named = true,
        positional = false,
        defaultValue = "None",
        noneable = true,
        doc =
            "An optional string to append after all other arguments. This string will not be "
                + "added if <code>omit_if_empty</code> is true (the default) and no other items "
                + "are appended (as happens if <code>values</code> is empty or all of its items "
                + "are filtered)."
      ),
    },
    useLocation = true
  )
  public Runtime.NoneType addAll(
      Object argNameOrValue,
      Object values,
      Object mapEach,
      Object formatEach,
      Object beforeEach,
      Boolean omitIfEmpty,
      Boolean uniquify,
      Object terminateWith,
      Location loc)
      throws EvalException;

  @SkylarkCallable(
    name = "add_joined",
    doc =
        "Appends an argument to this command line by concatenating together multiple values "
            + "using a separator. For depsets, the items are evaluated lazily during the "
            + "execution phase."
            + ""
            + "<p>Processing is similar to <a href='#add_all'><code>add_all()</code></a>, but "
            + "the list of arguments derived from <code>values</code> is combined into a single "
            + "argument as if by <code>join_with.join(...)</code>, and then formatted using the "
            + "given <code>format_joined</code> string template. Unlike <code>add_all()</code>, "
            + "there is no <code>before_each</code> or <code>terminate_with</code> parameter "
            + "since these are not generally useful when the items are combined into a single "
            + "argument."
            + ""
            + "<p>If after filtering there are no strings to join into an argument, and if "
            + "<code>omit_if_empty</code> is true (the default), no processing is done. "
            + "Otherwise if there are no strings to join but <code>omit_if_empty</code> is "
            + "false, the joined string will be an empty string.",
    parameters = {
      @Param(
          name = "arg_name_or_values",
          doc =
              "If two positional parameters are passed this is interpreted as the arg name. "
                  + "The arg name is added before <code>values</code> without any processing. "
                  + "This arg will not be added if <code>omit_if_empty</code> is true "
                  + "(the default) and there are no strings derived from <code>values</code> "
                  + "to join together (which can happen if <code>values</code> is empty "
                  + "or all of its items are filtered)."
                  + "If only one positional parameter is passed, it is interpreted as "
                  + "<code>values</code> (see below)."),
      @Param(
          name = "values",
          allowedTypes = {
            @ParamType(type = SkylarkList.class),
            @ParamType(type = SkylarkNestedSet.class),
          },
          defaultValue = "unbound",
          doc = "The list, tuple, or depset whose items will be joined."),
      @Param(
          name = "join_with",
          type = String.class,
          named = true,
          positional = false,
          doc =
              "A delimiter string used to join together the strings obtained from applying "
                  + "<code>map_each</code> and <code>format_each</code>, in the same manner as "
                  + "<a href='string.html#join'><code>string.join()</code></a>."),
      @Param(
          name = "map_each",
          type = BaseFunction.class,
          named = true,
          positional = false,
          defaultValue = "None",
          noneable = true,
          doc = "Same as for <a href='#add_all.map_each'><code>add_all</code></a>."),
      @Param(
        name = "format_each",
        type = String.class,
        named = true,
        positional = false,
        defaultValue = "None",
        noneable = true,
        doc = "Same as for <a href='#add_all.format_each'><code>add_all</code></a>."
      ),
      @Param(
        name = "format_joined",
        type = String.class,
        named = true,
        positional = false,
        defaultValue = "None",
        noneable = true,
        doc =
            "An optional format string pattern applied to the joined string. "
                + "The format string must have exactly one '%s' placeholder."
      ),
      @Param(
        name = "omit_if_empty",
        type = Boolean.class,
        named = true,
        positional = false,
        defaultValue = "True",
        doc =
            "If true, if there are no strings to join together (either because <code>values"
                + "</code> is empty or all its items are filtered), then all further processing "
                + "is suppressed and the command line will be unchanged. If false, then even if "
                + "there are no strings to join together, two arguments will be appended: "
                + "the arg name followed by an empty string (which is the logical join "
                + "of zero strings)."
      ),
      @Param(
        name = "uniquify",
        type = Boolean.class,
        named = true,
        positional = false,
        defaultValue = "False",
        doc = "Same as for <a href='#add_all.uniquify'><code>add_all</code></a>."
      )
    },
    useLocation = true
  )
  public Runtime.NoneType addJoined(
      Object argNameOrValue,
      Object values,
      String joinWith,
      Object mapEach,
      Object formatEach,
      Object formatJoined,
      Boolean omitIfEmpty,
      Boolean uniquify,
      Location loc)
      throws EvalException;

  @SkylarkCallable(
      name = "use_param_file",
      doc =
          "Spills the args to a params file, replacing them with a pointer to the param file. "
              + "Use when your args may be too large for the system's command length limits ",
      parameters = {
        @Param(
            name = "param_file_arg",
            type = String.class,
            named = true,
            doc =
                "A format string with a single \"%s\". "
                    + "If the args are spilled to a params file then they are replaced "
                    + "with an argument consisting of this string formatted with "
                    + "the path of the params file."),
        @Param(
            name = "use_always",
            type = Boolean.class,
            named = true,
            positional = false,
            defaultValue = "False",
            doc =
                "Whether to always spill the args to a params file. If false, "
                    + "bazel will decide whether the arguments need to be spilled "
                    + "based on your system and arg length.")
      })
  public void useParamsFile(String paramFileArg, Boolean useAlways) throws EvalException;

  @SkylarkCallable(
      name = "set_param_file_format",
      doc = "Sets the format of the param file when written to disk",
      parameters = {
        @Param(
            name = "format",
            type = String.class,
            named = true,
            doc =
                "The format of the param file. Must be one of:<ul><li>"
                    + "\"shell\": All arguments are shell quoted and separated by "
                    + "whitespace (space, tab, newline)</li><li>"
                    + "\"multiline\": All arguments are unquoted and separated by newline "
                    + "characters</li></ul>"
                    + "<p>The format defaults to \"shell\" if not called.")
      })
  public void setParamFileFormat(String format) throws EvalException;
}