github.com/sercand/please@v13.4.0+incompatible/src/parse/rules/sh_rules.build_defs (about)

     1  """ Rules to 'build' shell scripts.
     2  
     3  Note that these do pretty much nothing beyond collecting the files. In future we might
     4  implement something more advanced (ala .sar files or whatever).
     5  """
     6  
     7  _SH_BINARY_TMPL_PREFIX = """
     8  unzip -qo $0 -d $(dirname $0)
     9  """.replace('$', '\\\$').replace('"', '\\"')
    10  
    11  _SH_BINARY_TMPL_SUFFIX = """
    12  exit 0\n\
    13  """
    14  
    15  
    16  def sh_library(name:str, src:str, deps:list=None, visibility:list=None, labels:list&features&tags=None):
    17      """Generates a shell script binary, essentially just the given source.
    18  
    19      Note that these are individually executable so can only have one source file each.
    20      This is a bit tedious and would be nice to improve sometime.
    21  
    22      Args:
    23        name (str): Name of the rule.
    24        src (str): Source file for the rule.
    25        deps (list): Dependencies of this rule.
    26        visibility (list): Visibility declaration of the rule.
    27        labels (list): List of labels.
    28      """
    29      return filegroup(
    30          name=name,
    31          srcs=[src],
    32          deps=deps,
    33          visibility=visibility,
    34          binary=True,
    35          labels=labels,
    36      )
    37  
    38  
    39  def sh_binary(name:str, main:str, deps:list=None, visibility:list=None, labels:list&features&tags=None):
    40      """Generates a shell script binary.
    41  
    42      It assumes that unzip is in your path.
    43  
    44      The resulting script will contain three things:
    45      1) Code necessary to unzip dependent files.
    46      2) The user defined shell script.
    47      3) The zipfile containing all dependent files.
    48  
    49      Args:
    50        name (str): Name of the rule
    51        main (str): The script to execute after all files have been uncompressed
    52        deps (list): Dependencies of this rule
    53        visibility (list): Visibility declaration of the rule.
    54        labels (list): List of labels.
    55      """
    56      # No need to go through zip/unzip and injecting code if there are no dependencies
    57      if deps:
    58          cmds = ' && '.join([
    59              # Use the same shebang as the original script
    60              'head -1 $SRCS > _tmp.txt',
    61              # Inject bash code to untar the compressed files.
    62              f'echo "{_SH_BINARY_TMPL_PREFIX}" >> _tmp.txt',
    63              # Inject the user defined script.
    64              'cat $SRCS >> _tmp.txt',
    65              # Inject a final exit so it doesn't try to execute the zipfile contents.
    66              f'echo "{_SH_BINARY_TMPL_SUFFIX}" >> _tmp.txt',
    67              # Compress the dependent files and dump out into the bash script.
    68              'find . -type f | grep -v $SRCS | sort | grep -v "_tmp.txt" | $TOOL z -d -i - -o $OUT --preamble_file _tmp.txt --strip_prefix ./',
    69          ])
    70      else:
    71          cmds = 'cp $SRCS $OUT'
    72  
    73      return build_rule(
    74          name = name,
    75          srcs = [main],
    76          outs = ['%s.sh' % name],
    77          tools = [CONFIG.JARCAT_TOOL],
    78          cmd = cmds,
    79          deps = deps,
    80          binary = True,
    81          needs_transitive_deps = True,
    82          labels = labels,
    83          visibility = visibility,
    84      )
    85  
    86  
    87  def sh_test(name:str, src:str=None, labels:list&features&tags=None, data:list=None, deps:list=None, worker:str='',
    88              size:str=None, visibility:list=None, flags:str='', flaky:bool|int=0, test_outputs:list=None, timeout:int=0,
    89              container:bool|dict=False, sandbox:bool=None):
    90      """Generates a shell test. Note that these aren't packaged in a useful way.
    91  
    92      Args:
    93        name (str): Name of the rule
    94        src (str): Test script file.
    95        labels (list): Labels to apply to this test.
    96        data (list): Runtime data for the test.
    97        deps (list): Dependencies of this rule
    98        worker (str): Reference to worker script, A persistent worker process that is used to set up the test.
    99        size (str): Test size (enormous, large, medium or small).
   100        visibility (list): Visibility declaration of the rule.
   101        flags (str): Flags to apply to the test invocation.
   102        timeout (int): Maximum length of time, in seconds, to allow this test to run for.
   103        flaky (int | bool): True to mark this as flaky and automatically rerun.
   104        test_outputs (list): Extra test output files to generate from this test.
   105        container (bool | dict): True to run this test within a container (eg. Docker).
   106        sandbox (bool): Sandbox the test on Linux to restrict access to namespaces such as network.
   107      """
   108      timeout, labels = _test_size_and_timeout(size, timeout, labels)
   109  
   110      test_cmd = '$TEST %s' % flags
   111      if worker:
   112          test_cmd = f'$(worker {worker}) && {test_cmd} '
   113          deps += [worker]
   114  
   115      return build_rule(
   116          name=name,
   117          srcs=[src or test],
   118          data=data,
   119          deps=deps,
   120          outs=[name + '.sh'],
   121          cmd='mv ${SRC} ${OUT}',
   122          test_cmd=test_cmd,
   123          visibility=visibility,
   124          labels=labels,
   125          binary=True,
   126          test=True,
   127          no_test_output=True,
   128          flaky=flaky,
   129          test_outputs=test_outputs,
   130          test_timeout=timeout,
   131          container=container,
   132          test_sandbox=sandbox,
   133      )
   134  
   135  
   136  def sh_cmd(name:str, cmd:str|dict, srcs:list|dict=None, out:list=None, shell:str='/bin/sh',
   137             labels:list&features&tags=None, deps:list=None, visibility:list=None,
   138             test_only:bool=False):
   139      """Generates a runnable shell script from a command.
   140  
   141      This is doable with a genrule with a little effort but it's awkward enough to be nice
   142      to have a builtin.
   143      The command is subject to Please's usual variable expansion at build time. Note that if
   144      you want `plz run` to transparently work and refer to other files, you may need to use
   145      $(out_location ...) instead of $(location ...).
   146  
   147      Args:
   148        name (str): Name of the rule.
   149        cmd (str | dict): Command to write into the output script file.
   150        srcs (list | dict): Source files. Can be consumed by the generated command (but are not
   151                            written into the output in any other way).
   152        out (str): Name of the output file to create. Defaults to name + .sh.
   153        shell (str): Shell to invoke in, by default /bin/sh.
   154        labels (list): Labels to apply to this rule.
   155        deps (list): Any dependencies for this rule.
   156        visibility (list): Visibility declaration of the rule.
   157        test_only (bool): If True, this rule can only be depended on by tests.
   158      """
   159      if isinstance(cmd, str):
   160          cmds = f'cat > $OUT << EOF\n#!{shell}\n{cmd}\nEOF'
   161      else:
   162          cmds = {k: f'cat > $OUT << EOF\n#!{shell}\n{v}\nEOF' for k, v in cmd.items()}
   163      return build_rule(
   164          name = name,
   165          outs = [out or name + '.sh'],
   166          srcs = srcs,
   167          cmd = cmds,
   168          labels = labels,
   169          deps = deps,
   170          visibility = visibility,
   171          binary = True,
   172          test_only = test_only,
   173      )