github.phpd.cn/thought-machine/please@v12.2.0+incompatible/src/parse/rules/cc_rules.build_defs (about)

     1  """Rules to build C and C++ targets.
     2  
     3  Note that the C / C++ build process is complex with many options; we attempt to keep things
     4  as high-level as possible here but expose flags to tune things as needed.
     5  
     6  As a general note, most of the rules work by attaching labels indicating various flags etc which
     7  later rules need to know about. These get picked up by later rules to adjust commands; this way
     8  you can write a cc_library rule specifying e.g. linker_flags = ['-lz'] and not have to re-specify
     9  that on every single cc_binary / cc_test that transitively depends on that library.
    10  """
    11  
    12  _COVERAGE_FLAGS = ' -ftest-coverage -fprofile-arcs -fprofile-dir=.'
    13  # OSX's ld uses --all_load / --noall_load instead of --whole-archive.
    14  _WHOLE_ARCHIVE = '-all_load' if CONFIG.OS == 'darwin' else '--whole-archive'
    15  _NO_WHOLE_ARCHIVE = '-noall_load' if CONFIG.OS == 'darwin' else '--no-whole-archive'
    16  
    17  
    18  def cc_library(name:str, srcs:list=[], hdrs:list=[], private_hdrs:list=[], deps:list=[],
    19                 visibility:list=None, test_only:bool&testonly=False, compiler_flags:list&cflags&copts=[],
    20                 linker_flags:list&ldflags&linkopts=[], pkg_config_libs:list=[], includes:list=[],
    21                 defines:list|dict=[], alwayslink:bool=False, linkstatic:bool=False, _c=False, textual_hdrs:list=[]):
    22      """Generate a C++ library target.
    23  
    24      Args:
    25        name (str): Name of the rule
    26        srcs (list): C++ source files to compile.
    27        hdrs (list): Header files. These will be made available to dependent rules, so the distinction
    28                     between srcs and hdrs is important.
    29        private_hdrs (list): Header files that are available only to this rule and not exported to
    30                             dependent rules.
    31        deps (list): Dependent rules.
    32        visibility (list): Visibility declaration for this rule.
    33        test_only (bool): If True, is only available to other test rules.
    34        compiler_flags (list): Flags to pass to the compiler.
    35        linker_flags (list): Flags to pass to the linker; these will not be used here but will be
    36                             picked up by a cc_binary or cc_test rule.
    37        pkg_config_libs (list): Libraries to declare a dependency on using pkg-config. Again, the ldflags
    38                                will be picked up by cc_binary or cc_test rules.
    39        includes (list): List of include directories to be added to the compiler's path.
    40        defines (list | dict): List of tokens to define in the preprocessor.
    41                               Alternatively can be a dict of name -> value to define, in which case
    42                               values are surrounded by quotes.
    43        alwayslink (bool): If True, any binaries / tests using this library will link in all symbols,
    44                           even if they don't directly reference them. This is useful for e.g. having
    45                           static members that register themselves at construction time.
    46        linkstatic (bool): Only provided for Bazel compatibility. Has no actual effect.
    47        textual_hdrs (list): Also provided for Bazel compatibility. Effectively works the same as hdrs for now.
    48      """
    49      # Bazel suggests passing nonexported header files in 'srcs'. We however treat
    50      # srcs as things to actually compile and must mark a distinction.
    51      if CONFIG.BAZEL_COMPATIBILITY:
    52          src_hdrs = [src for src in srcs if src.endswith('.h') or src.endswith('.inc')]
    53          srcs = [src for src in srcs if src not in src_hdrs]
    54          # This is rather nasty; people seem to be relying on being able to reuse
    55          # headers that they've put in srcs. We hence need to re-export them here, but really
    56          # they should be added to private_hdrs instead.
    57          hdrs += src_hdrs
    58          hdrs += textual_hdrs
    59          # Found this in a few cases... can't pass -pthread to the linker.
    60          linker_flags = ['-lpthread' if l == '-pthread' else l for l in linker_flags]
    61  
    62      # Handle defines being passed as a dict, as a nicety for the user.
    63      if isinstance(defines, dict):
    64          defines = [k if v is None else r'%s=\"%s\"' % (k, v) for k, v in sorted(defines.items())]
    65  
    66      pkg_name = package_name()
    67      labels = (['cc:ld:' + flag for flag in linker_flags] +
    68                ['cc:pc:' + lib for lib in pkg_config_libs] +
    69                ['cc:inc:' + join_path(pkg_name, include) for include in includes] +
    70                ['cc:def:' + define for define in defines])
    71  
    72  
    73      if not srcs:
    74          # Header-only library, no compilation needed.
    75          return filegroup(
    76              name=name,
    77              srcs=hdrs,
    78              exported_deps=deps,
    79              labels=labels,
    80              test_only=test_only,
    81              visibility=visibility,
    82              output_is_complete=False,
    83          )
    84  
    85      # Collect the headers for other rules
    86      hdrs_rule = filegroup(
    87          name = name,
    88          tag = 'hdrs',
    89          srcs=hdrs,
    90          requires=['cc_hdrs'],
    91          deps=deps,
    92          test_only=test_only,
    93          labels=labels,
    94          output_is_complete=False,
    95      )
    96  
    97      cmds, tools = _library_cmds(_c, compiler_flags, pkg_config_libs)
    98      # TODO(pebers): handle includes and defines in _library_cmds as well.
    99      pre_build = _library_transitive_labels(_c, compiler_flags, pkg_config_libs) if (deps or includes or defines) else None
   100      if len(srcs) > 1:
   101          # Compile all the sources separately, this is much faster for large numbers of files
   102          # than giving them all to gcc in one invocation.
   103          a_rules = []
   104          for src in srcs:
   105              a_name = '_%s#%s' % (name, src.replace('/', '_').replace('.', '_').replace(':', '_').replace('|', '_'))
   106              a_rule = build_rule(
   107                  name=a_name,
   108                  srcs={'srcs': [src], 'hdrs': hdrs, 'priv': private_hdrs},
   109                  outs=[a_name + '.a'],
   110                  optional_outs=['*.gcno'],  # For coverage
   111                  deps=deps,
   112                  cmd=cmds,
   113                  building_description='Compiling...',
   114                  requires=['cc_hdrs'],
   115                  test_only=test_only,
   116                  labels=labels,
   117                  tools=tools,
   118                  pre_build=pre_build,
   119                  needs_transitive_deps=True,
   120              )
   121              a_rules.append(a_rule)
   122  
   123          # Combine the archives into one.
   124          a_rule = build_rule(
   125              name = name,
   126              tag = 'a',
   127              srcs = a_rules,
   128              outs = [name + '.a'],
   129              cmd = 'echo $SRCS | xargs -n 1 $TOOL xo && $TOOL rcs $OUT *.o',
   130              building_description = 'Archiving...',
   131              test_only = test_only,
   132              labels = labels,
   133              output_is_complete = True,
   134              deps = deps + [hdrs_rule],  # This is a little suboptimal but makes sure they get built when needed.
   135              tools = [CONFIG.AR_TOOL],
   136          )
   137          if alwayslink:
   138              labels.append('cc:al:%s/%s.a' % (get_base_path(), name))
   139  
   140          # Filegroup to pick that up with extra deps. This is a little annoying but means that
   141          # things depending on this get the combined rule and not the individual ones, but do get
   142          # all the other dependencies which are probably important.
   143          lib_rule = filegroup(
   144              name = name,
   145              tag = 'lib',
   146              srcs = [a_rule],
   147              deps = deps,
   148              test_only = test_only,
   149              labels = labels,
   150              output_is_complete=False,
   151          )
   152  
   153      else:
   154          # Single source file, optimise slightly by not extracting & remerging the archive.
   155          cc_rule = build_rule(
   156              name=name,
   157              tag='cc',
   158              srcs={'srcs': srcs, 'hdrs': hdrs, 'priv': private_hdrs},
   159              outs=[name + '.a'],
   160              optional_outs=['*.gcno'],  # For coverage
   161              deps=deps,
   162              cmd=cmds,
   163              building_description='Compiling...',
   164              requires=['cc_hdrs'],
   165              test_only=test_only,
   166              labels=labels,
   167              tools=tools,
   168              pre_build=pre_build,
   169              needs_transitive_deps=True,
   170          )
   171          if alwayslink:
   172              labels.append('cc:al:%s/%s.a' % (get_base_path(), name))
   173          # Need another rule to cover require / provide stuff. This is getting a bit complicated...
   174          lib_rule = filegroup(
   175              name = name,
   176              tag = 'lib',
   177              srcs = [cc_rule],
   178              deps = deps,
   179              test_only = test_only,
   180              labels = labels,
   181              output_is_complete=False,
   182          )
   183  
   184      return filegroup(
   185          name=name,
   186          srcs=[lib_rule],
   187          deps=[hdrs_rule],
   188          provides={
   189              'cc_hdrs': hdrs_rule,
   190              'cc': lib_rule,
   191          },
   192          test_only=test_only,
   193          visibility=visibility,
   194          output_is_complete=False,
   195      )
   196  
   197  
   198  def cc_object(name:str, src:str, hdrs:list=[], private_hdrs:list=[], out:str=None, test_only:bool&testonly=False,
   199                compiler_flags:list&cflags&copts=[], linker_flags:list&ldflags&linkopts=[], pkg_config_libs:list=[], includes:list=[],
   200                defines:list|dict=[], alwayslink:bool=False, _c=False, visibility:list=None, deps:list=[]):
   201      """Generate a C or C++ object file from a single source.
   202  
   203      N.B. This is fairly low-level; for most use cases cc_library should be preferred.
   204  
   205      Args:
   206        name (str): Name of the rule
   207        src (str): C or C++ source file to compile. This can be another rule, but if so it must
   208                   have exactly one output.
   209        hdrs (list): Header files. These will be made available to dependent rules, so the distinction
   210                     between srcs and hdrs is important.
   211        private_hdrs (list): Header files that are available only to this rule and not exported to
   212                             dependent rules.
   213        out (str): Name of the output file. Defaults to name + .o.
   214        deps (list): Dependent rules.
   215        visibility (list): Visibility declaration for this rule.
   216        test_only (bool): If True, is only available to other test rules.
   217        compiler_flags (list): Flags to pass to the compiler.
   218        linker_flags (list): Flags to pass to the linker; these will not be used here but will be
   219                             picked up by a cc_binary or cc_test rule.
   220        pkg_config_libs (list): Libraries to declare a dependency on using pkg-config. Again, the ldflags
   221                                will be picked up by cc_binary or cc_test rules.
   222        includes (list): List of include directories to be added to the compiler's path.
   223        defines (list | dict): List of tokens to define in the preprocessor.
   224                               Alternatively can be a dict of name -> value to define, in which case
   225                               values are surrounded by quotes.
   226        alwayslink (bool): If True, any binaries / tests using this library will link in all symbols,
   227                           even if they don't directly reference them. This is useful for e.g. having
   228                           static members that register themselves at construction time.
   229      """
   230      # Handle defines being passed as a dict, as a nicety for the user.
   231      if isinstance(defines, dict):
   232          defines = [k if v is None else r'%s=\"%s\"' % (k, v) for k, v in sorted(defines.items())]
   233  
   234      labels = (['cc:ld:' + flag for flag in linker_flags] +
   235                ['cc:pc:' + lib for lib in pkg_config_libs] +
   236                ['cc:inc:%s/%s' % (get_base_path(), include) for include in includes] +
   237                ['cc:def:' + define for define in defines])
   238      if alwayslink:
   239          labels.append('cc:al:%s/%s.a' % (get_base_path(), name))
   240      cmds, tools = _library_cmds(_c, compiler_flags, pkg_config_libs, archive=False)
   241  
   242      return build_rule(
   243          name=name,
   244          srcs={'srcs': [src], 'hdrs': hdrs, 'priv': private_hdrs},
   245          outs=[out or name + '.o'],
   246          optional_outs=['*.gcno'],  # For coverage
   247          deps=deps,
   248          cmd=cmds,
   249          building_description='Compiling...',
   250          requires=['cc_hdrs'],
   251          test_only=test_only,
   252          labels=labels,
   253          tools=tools,
   254          pre_build=_library_transitive_labels(_c, compiler_flags, pkg_config_libs, archive=False)
   255                    if (deps or includes or defines) else None,
   256          needs_transitive_deps=True,
   257      )
   258  
   259  
   260  def cc_static_library(name:str, srcs:list=[], hdrs:list=[], compiler_flags:list&cflags&copts=[], linker_flags:list&ldflags&linkopts=[],
   261                        deps:list=[], visibility:list=None, test_only:bool&testonly=False, pkg_config_libs:list=[], _c=False):
   262      """Generates a C++ static library (.a).
   263  
   264      This is essentially just a collection of other cc_library rules into a single archive.
   265      Optionally this rule can have sources of its own, but it's quite reasonable just to use
   266      it as a collection of other rules.
   267  
   268      Args:
   269        name (str): Name of the rule
   270        srcs (list): C or C++ source files to compile.
   271        hdrs (list): Header files.
   272        compiler_flags (list): Flags to pass to the compiler.
   273        linker_flags (list): Flags to pass to the linker.
   274        deps (list): Dependent rules.
   275        visibility (list): Visibility declaration for this rule.
   276        test_only (bool): If True, is only available to other test rules.
   277        pkg_config_libs (list): Libraries to declare a dependency on using pkg-config.
   278      """
   279      provides = None
   280      if srcs:
   281          lib_rule = cc_library(
   282              name = '_%s#lib' % name,
   283              srcs = srcs,
   284              hdrs = hdrs,
   285              compiler_flags = compiler_flags,
   286              linker_flags = linker_flags,
   287              deps = deps,
   288              test_only = test_only,
   289              pkg_config_libs = pkg_config_libs,
   290              _c=_c,
   291          )
   292          deps += [lib_rule, ':_%s#lib_hdrs' % name]
   293          provides = {
   294              'cc_hdrs': ':_%s#lib_hdrs' % name,
   295              'cc': ':' + name,
   296          }
   297      return build_rule(
   298          name = name,
   299          deps = deps,
   300          outs = ['lib%s.a' % name],
   301          cmd = (
   302              'for a in `find . -name "*.a"`; do (cd $(dirname $a) && $TOOL x $(basename $a)); done;' +
   303              '$TOOL rcs $OUT `find . -name "*.o" | sort`'
   304          ),
   305          needs_transitive_deps = True,
   306          output_is_complete = True,
   307          visibility = visibility,
   308          test_only = test_only,
   309          building_description = 'Archiving...',
   310          provides = provides,
   311          requires = ['cc'],
   312          tools = [CONFIG.AR_TOOL],
   313      )
   314  
   315  
   316  def cc_shared_object(name:str, srcs:list=[], hdrs:list=[], out:str='', compiler_flags:list&cflags&copts=[],
   317                       linker_flags:list&ldflags&linkopts=[], deps:list=[], visibility:list=None, test_only:bool&testonly=False,
   318                       pkg_config_libs:list=[], includes:list=[], _c=False):
   319      """Generates a C++ shared object (.so) with its dependencies linked in.
   320  
   321      Args:
   322        name (str): Name of the rule
   323        srcs (list): C or C++ source files to compile.
   324        hdrs (list): Header files. These will be made available to dependent rules, so the distinction
   325                     between srcs and hdrs is important.
   326        out (str): Name of the output .so. Defaults to name + .so.
   327        compiler_flags (list): Flags to pass to the compiler.
   328        linker_flags (list): Flags to pass to the linker.
   329        deps (list): Dependent rules.
   330        visibility (list): Visibility declaration for this rule.
   331        test_only (bool): If True, is only available to other test rules.
   332        pkg_config_libs (list): Libraries to declare a dependency on using pkg-config.
   333        includes (list): Include directories to be added to the compiler's lookup path.
   334      """
   335      if CONFIG.DEFAULT_LDFLAGS:
   336          linker_flags.append(CONFIG.DEFAULT_LDFLAGS)
   337      provides = None
   338      if srcs:
   339          lib_rule = cc_library(
   340              name = '_%s#lib' % name,
   341              srcs = srcs,
   342              hdrs = hdrs,
   343              compiler_flags = compiler_flags,
   344              linker_flags = linker_flags,
   345              deps = deps,
   346              test_only = test_only,
   347              pkg_config_libs = pkg_config_libs,
   348              includes = includes,
   349              _c=_c,
   350          )
   351          deps += [lib_rule, ':_%s#lib_hdrs' % name]
   352          provides = {
   353              'cc_hdrs': ':_%s#lib_hdrs' % name,
   354              'cc': ':' + name,
   355          }
   356      cmds, tools = _binary_cmds(_c, linker_flags, pkg_config_libs, shared=True)
   357      return build_rule(
   358          name=name,
   359          srcs={'srcs': srcs, 'hdrs': hdrs},
   360          outs=[out or name + '.so'],
   361          deps=deps,
   362          visibility=visibility,
   363          cmd=cmds,
   364          building_description='Linking...',
   365          binary=True,
   366          needs_transitive_deps=True,
   367          output_is_complete=True,
   368          provides=provides,
   369          tools=tools,
   370          test_only=test_only,
   371          requires=['cc', 'cc_hdrs'],
   372          pre_build=_binary_transitive_labels(_c, linker_flags, pkg_config_libs, shared=True) if deps else None,
   373      )
   374  
   375  
   376  def cc_binary(name:str, srcs:list=[], hdrs:list=[], private_hdrs:list=[], compiler_flags:list&cflags&copts=[],
   377                linker_flags:list&ldflags&linkopts=[], deps:list=[], visibility:list=None, pkg_config_libs:list=[],
   378                test_only:bool&testonly=False, static:bool=False, _c=False, linkstatic:bool=False):
   379      """Builds a binary from a collection of C++ rules.
   380  
   381      Args:
   382        name (str): Name of the rule
   383        srcs (list): C or C++ source files to compile.
   384        hdrs (list): Header files.
   385        private_hdrs (list): Header files that are available only to this rule and not exported to
   386                             dependent rules.
   387        compiler_flags (list): Flags to pass to the compiler.
   388        linker_flags (list): Flags to pass to the linker.
   389        deps (list): Dependent rules.
   390        visibility (list): Visibility declaration for this rule.
   391        pkg_config_libs (list): Libraries to declare a dependency on using pkg-config.
   392        test_only (bool): If True, this rule can only be used by tests.
   393        static (bool): If True, the binary will be linked statically.
   394        linkstatic (bool): Only provided for Bazel compatibility. Has no actual effect since we always
   395                           link roughly equivalently to their "mostly-static" mode.
   396      """
   397      if CONFIG.BAZEL_COMPATIBILITY:
   398          linker_flags = ['-lpthread' if l == '-pthread' else l for l in linker_flags]
   399      if CONFIG.DEFAULT_LDFLAGS:
   400          linker_flags.append(CONFIG.DEFAULT_LDFLAGS)
   401      if static:
   402          linker_flags.append('-static')
   403      cmds, tools = _binary_cmds(_c, linker_flags, pkg_config_libs)
   404      if srcs:
   405          if static:
   406              compiler_flags.append('-static -static-libgcc')
   407          lib_rule = cc_library(
   408              name='_%s#lib' % name,
   409              srcs=srcs,
   410              hdrs=hdrs,
   411              private_hdrs=private_hdrs,
   412              deps=deps,
   413              pkg_config_libs=pkg_config_libs,
   414              compiler_flags=compiler_flags,
   415              test_only=test_only,
   416              _c=_c,
   417          )
   418          deps.append(lib_rule)
   419      return build_rule(
   420          name=name,
   421          outs=[name],
   422          deps=deps,
   423          visibility=visibility,
   424          cmd=cmds,
   425          building_description='Linking...',
   426          binary=True,
   427          needs_transitive_deps=True,
   428          output_is_complete=True,
   429          requires=['cc'],
   430          tools=tools,
   431          pre_build=_binary_transitive_labels(_c, linker_flags, pkg_config_libs),
   432          test_only=test_only,
   433      )
   434  
   435  
   436  def cc_test(name:str, srcs:list=[], hdrs:list=[], compiler_flags:list&cflags&copts=[], linker_flags:list&ldflags&linkopts=[],
   437              pkg_config_libs:list=[], deps:list=[], data:list=[], visibility:list=[], flags:str='',
   438              labels:list&features&tags=[], flaky:bool|int=0, test_outputs:list=[], size:str=None, timeout:int=0,
   439              container:bool|dict=False, sandbox:bool=None, write_main:bool=not CONFIG.BAZEL_COMPATIBILITY, _c=False):
   440      """Defines a C++ test using UnitTest++.
   441  
   442      We template in a main file so you don't have to supply your own.
   443      (Later we might allow that to be configured to help support other unit test frameworks).
   444  
   445      Args:
   446        name (str): Name of the rule
   447        srcs (list): C or C++ source files to compile.
   448        hdrs (list): Header files.
   449        compiler_flags (list): Flags to pass to the compiler.
   450        linker_flags (list): Flags to pass to the linker.
   451        pkg_config_libs (list): Libraries to declare a dependency on using pkg-config.
   452        deps (list): Dependent rules.
   453        data (list): Runtime data files for this test.
   454        visibility (list): Visibility declaration for this rule.
   455        flags (str): Flags to apply to the test invocation.
   456        labels (list): Labels to attach to this test.
   457        flaky (bool | int): If true the test will be marked as flaky and automatically retried.
   458        test_outputs (list): Extra test output files to generate from this test.
   459        size (str): Test size (enormous, large, medium or small).
   460        timeout (int): Length of time in seconds to allow the test to run for before killing it.
   461        container (bool | dict): If true the test is run in a container (eg. Docker).
   462        sandbox (bool): Sandbox the test on Linux to restrict access to namespaces such as network.
   463        write_main (bool): Whether or not to write a main() for these tests.
   464      """
   465      if CONFIG.BAZEL_COMPATIBILITY:
   466          linker_flags = ['-lpthread' if l == '-pthread' else l for l in linker_flags]
   467      timeout, labels = _test_size_and_timeout(size, timeout, labels)
   468      if CONFIG.DEFAULT_LDFLAGS:
   469          linker_flags.append(CONFIG.DEFAULT_LDFLAGS)
   470      if write_main:
   471          linker_flags.append('-lUnitTest++')
   472          main_rule = genrule(
   473              name='_%s#main' % name,
   474              outs=['_%s_main.cc' % name],
   475              cmd='echo \'%s\' > $OUT' % _CC_TEST_MAIN_CONTENTS,
   476              test_only=True,
   477          )
   478          srcs.append(main_rule)
   479      cmds, tools = _binary_cmds(_c, linker_flags, pkg_config_libs)
   480      test_cmd = None if not CONFIG.CPP_COVERAGE else {
   481          'opt': '$TEST %s' % flags,
   482          'dbg': '$TEST %s' % flags,
   483          'cover': '$TEST %s; R=$?; cp $GCNO_DIR/*.gcno . && gcov *.gcda && cat *.gcov > test.coverage; exit $R' % flags,
   484      }
   485      if srcs:
   486          lib_rule = cc_library(
   487              name='_%s#lib' % name,
   488              srcs=srcs,
   489              hdrs=hdrs,
   490              deps=deps,
   491              pkg_config_libs=pkg_config_libs,
   492              compiler_flags=compiler_flags,
   493              test_only=True,
   494              alwayslink=True,
   495              _c=_c,
   496          )
   497          deps.append(lib_rule)
   498      return build_rule(
   499          name=name,
   500          outs=[name],
   501          deps=deps,
   502          data=data,
   503          visibility=visibility,
   504          cmd=cmds,
   505          test_cmd=test_cmd,
   506          building_description='Linking...',
   507          binary=True,
   508          test=True,
   509          needs_transitive_deps=True,
   510          output_is_complete=True,
   511          requires=['cc', 'cc_hdrs'],
   512          labels=labels,
   513          tools=tools,
   514          pre_build=_binary_transitive_labels(_c, linker_flags, pkg_config_libs),
   515          flaky=flaky,
   516          test_outputs=test_outputs,
   517          test_timeout=timeout,
   518          container=container,
   519          test_sandbox=sandbox,
   520      )
   521  
   522  
   523  def cc_embed_binary(name:str, src:str, deps:list=[], visibility:list=None, test_only:bool&testonly=False, namespace:str=None):
   524      """Build rule to embed an arbitrary binary file into a C library.
   525  
   526      You can depend on the output of this as though it were a cc_library rule.
   527      There are five functions available to access the data once compiled, all of which are
   528      prefixed with the file's basename:
   529        filename_start(): returns a const char* pointing to the beginning of the data.
   530        filename_end(): returns a const char* pointing to the end of the data.
   531        filename_size(): returns the length of the data in bytes.
   532        filename_start_nc(): returns a char* pointing to the beginning of the data.
   533                             This is a convenience wrapper using const_cast, you should not
   534                             mutate the contents of the returned pointer.
   535        filename_end_nc(): returns a char* pointing to the end of the data.
   536                           Again, don't mutate the contents of the pointer.
   537      You don't own the contents of any of these pointers so don't try to delete them :)
   538  
   539      Args:
   540        name (str): Name of the rule.
   541        src (str): Source file to embed.
   542        deps (list): Dependencies.
   543        visibility (list): Rule visibility.
   544        test_only (bool): If True, is only available to test rules.
   545        namespace (str): Allows specifying the namespace the symbols will be available in.
   546      """
   547      if src.startswith(':') or src.startswith('/'):
   548          deps += [src]
   549      namespace = namespace or CONFIG.DEFAULT_NAMESPACE
   550      if not namespace:
   551          raise ValueError('You must either pass namespace= to cc_library or set the default namespace in .plzconfig')
   552      darwin = CONFIG.OS == 'darwin'
   553  
   554      hdr_rule = build_rule(
   555          name=name,
   556          tag='hdr',
   557          outs=[name + '.h'],
   558          srcs=[src],
   559          deps=deps,
   560          cmd='; '.join([
   561              # This replacement roughly mimics what ld will do to munge it into a symbol name.
   562              'export ENCODED_FILENAME="${SRCS//[\\/\\.]/_}"',
   563              'export BINARY_NAME=' + name,
   564              'export NAMESPACE=' + namespace,
   565              'echo "%s" > $OUT' % _CC_HEADER_CONTENTS,
   566          ]),
   567          visibility=visibility,
   568          building_description='Writing header...',
   569          requires=['cc'],
   570          test_only=test_only,
   571      )
   572  
   573      tools = {'ar': [CONFIG.AR_TOOL]}
   574      if darwin:
   575          # OSX's ld doesn't support '--format binary', and this is the least fiddly
   576          # alternative. Requiring an additional tool is a bit suboptimal but probably
   577          # in the end easier than the alternatives.
   578          cmd = ' && '.join([
   579              'export ENCODED_FILENAME=${SRCS//[\\/\\.]/_}',
   580              'echo "%s" > embedded.asm' % _CC_DARWIN_ASM_CONTENTS,
   581              '$TOOLS_ASM -fmacho64 embedded.asm -o ${OUTS/.a/.o}',
   582              '$TOOLS_AR rcs $OUT ${OUTS/.a/.o}',
   583          ])
   584          tools['asm'] = [CONFIG.ASM_TOOL]
   585      else:
   586          cmd = '$TOOLS_LD -r --format binary %s -o ${OUTS/.a/.o} $SRC && $TOOLS_AR rcs $OUT ${OUTS/.a/.o}' % CONFIG.DEFAULT_LDFLAGS
   587          tools['ld'] = [CONFIG.LD_TOOL]
   588  
   589      lib_rule = build_rule(
   590          name = name,
   591          tag = 'lib',
   592          srcs=[src],
   593          outs=['lib%s.a' % name],
   594          deps=deps,
   595          cmd=cmd,
   596          visibility=visibility,
   597          building_description='Embedding...',
   598          requires=['cc'],
   599          tools=tools,
   600          test_only=test_only,
   601      )
   602      return filegroup(
   603          name=name,
   604          srcs=[lib_rule, hdr_rule],
   605          visibility=visibility,
   606          test_only=test_only,
   607          provides={
   608              'cc_hdrs': hdr_rule,
   609              'cc': lib_rule,
   610          },
   611      )
   612  
   613  
   614  _CC_HEADER_CONTENTS = """\
   615  #ifdef __cplusplus
   616  namespace ${NAMESPACE} {
   617  extern \\"C\\" {
   618  #endif  // __cplusplus
   619  extern const char _binary_${ENCODED_FILENAME}_start[];
   620  extern const char _binary_${ENCODED_FILENAME}_end[];
   621  #ifdef __cplusplus
   622  }
   623  #endif  // __cplusplus
   624  
   625  // Nicer aliases.
   626  inline const char* ${BINARY_NAME}_start() {
   627    return _binary_${ENCODED_FILENAME}_start;
   628  }
   629  inline const char* ${BINARY_NAME}_end() {
   630    return _binary_${ENCODED_FILENAME}_end;
   631  }
   632  inline unsigned long ${BINARY_NAME}_size() {
   633    return _binary_${ENCODED_FILENAME}_end - _binary_${ENCODED_FILENAME}_start;
   634  }
   635  inline char* ${BINARY_NAME}_start_nc() {
   636    return (char*)(_binary_${ENCODED_FILENAME}_start);
   637  }
   638  inline char* ${BINARY_NAME}_end_nc() {
   639    return (char*)(_binary_${ENCODED_FILENAME}_end);
   640  }
   641  #ifdef __cplusplus
   642  }  // namespace ${NAMESPACE}
   643  #endif  // __cplusplus
   644  """
   645  
   646  # We duplicate the symbols with _ and __ preceding, the compiler fails if _ is not
   647  # present and the linker fails if __ isn't.
   648  _CC_DARWIN_ASM_CONTENTS = r"""
   649  bits 64
   650  
   651  section .rodata
   652  
   653  global _binary_${ENCODED_FILENAME}_start
   654  global __binary_${ENCODED_FILENAME}_start
   655  global _binary_${ENCODED_FILENAME}_end
   656  global __binary_${ENCODED_FILENAME}_end
   657  global _binary_${ENCODED_FILENAME}_size
   658  global __binary_${ENCODED_FILENAME}_size
   659  
   660  _binary_${ENCODED_FILENAME}_start:    incbin \"${SRCS}\"
   661  __binary_${ENCODED_FILENAME}_start:   incbin \"${SRCS}\"
   662  _binary_${ENCODED_FILENAME}_end:
   663  __binary_${ENCODED_FILENAME}_end:
   664  _binary_${ENCODED_FILENAME}_size:     dd \\$-_binary_${ENCODED_FILENAME}_start
   665  __binary_${ENCODED_FILENAME}_size:    dd \\$-_binary_${ENCODED_FILENAME}_start
   666  """
   667  
   668  
   669  # This is a lightweight way of building the test main, but it's awkward not
   670  # having command line output as well as XML output.
   671  _CC_TEST_MAIN_CONTENTS = """
   672  #include <algorithm>
   673  #include <fstream>
   674  #include <stdlib.h>
   675  #include <string.h>
   676  #include <unistd.h>
   677  #include <vector>
   678  #include "UnitTest++/UnitTest++.h"
   679  #include "UnitTest++/XmlTestReporter.h"
   680  int main(int argc, const char** argv) {
   681      if (getenv("DEBUG")) {
   682          unsetenv("DEBUG");
   683          std::vector<char*> v({strdup("gdb"), strdup("-ex"), strdup("run"), strdup("--args")});
   684          v.insert(v.end(), const_cast<char**>(argv), const_cast<char**>(argv) + argc);
   685          execvp("gdb", const_cast<char**>(&v[0]));
   686      }
   687      auto run_named = [argc, argv](UnitTest::Test* test) {
   688          return argc <= 1 || std::any_of(argv + 1, argv + argc, [test](const char* name) {
   689              return strcmp(test->m_details.testName, name) == 0;
   690          });
   691      };
   692  
   693      std::ofstream f("test.results");
   694      if (!f.good()) {
   695        fprintf(stderr, "Failed to open results file\\n");
   696        return -1;
   697      }
   698      if (getenv("COVERAGE")) {
   699        std::ofstream c("test.coverage");
   700        c << "gcov";
   701      }
   702      UnitTest::XmlTestReporter reporter(f);
   703      UnitTest::TestRunner runner(reporter);
   704      return runner.RunTestsIf(UnitTest::Test::GetTestList(),
   705                               NULL,
   706                               run_named,
   707                               0);
   708  }
   709  """
   710  
   711  
   712  def _default_cflags(c, dbg):
   713      """Returns the default cflags / cppflags for opt/dbg as appropriate."""
   714      if c:
   715          return CONFIG.DEFAULT_DBG_CFLAGS if dbg else CONFIG.DEFAULT_OPT_CFLAGS
   716      else:
   717          return CONFIG.DEFAULT_DBG_CPPFLAGS if dbg else CONFIG.DEFAULT_OPT_CPPFLAGS
   718  
   719  
   720  def _build_flags(compiler_flags:list, pkg_config_libs:list, defines=None, c=False, dbg=False):
   721      """Builds flags that we'll pass to the compiler invocation."""
   722      compiler_flags = [_default_cflags(c, dbg), '-fPIC'] + compiler_flags  # N.B. order is important!
   723      if defines:
   724          compiler_flags += ['-D' + define for define in defines]
   725      pkg_config_cmd = ' '.join(['`pkg-config --cflags %s`' % x for x in pkg_config_libs])
   726      return ' '.join(compiler_flags) + ' ' + pkg_config_cmd
   727  
   728  
   729  def _binary_build_flags(linker_flags:list, pkg_config_libs:list, shared=False, alwayslink='', c=False, dbg=False):
   730      """Builds flags that we'll pass to the linker invocation."""
   731      pkg_config_cmd = ' '.join(['`pkg-config --libs %s`' % x for x in pkg_config_libs])
   732      objs = '`find . -name "*.o" -or -name "*.a" | sort`'
   733      linker_prefix = '' if CONFIG.LINK_WITH_LD_TOOL else '-Wl,'
   734      if (not shared) and alwayslink:
   735          objs = '%s%s %s %s%s %s' % (linker_prefix, _WHOLE_ARCHIVE, alwayslink, linker_prefix, _NO_WHOLE_ARCHIVE, objs)
   736      build_id_flag = ''
   737      if CONFIG.OS != 'darwin':
   738          # We don't order libraries in a way that is especially useful for the linker, which is
   739          # nicely solved by --start-group / --end-group. Unfortunately the OSX linker doesn't
   740          # support those flags; in many cases it will work without, so try that.
   741          # Ordering them would be ideal but we lack a convenient way of working that out from here.
   742          objs = '%s--start-group %s %s--end-group' % (linker_prefix, objs, linker_prefix)
   743          # OSX's ld doesn't have --build-id
   744          # TODO(pebers): Not sure about other OS's / linkers? This might be GNU specific?
   745          build_id_flag = linker_prefix + '--build-id=none'
   746      if shared:
   747          objs = '-shared %s%s %s %s%s' % (linker_prefix, _WHOLE_ARCHIVE, objs, linker_prefix, _NO_WHOLE_ARCHIVE)
   748      linker_flags = ' '.join([linker_prefix + f for f in linker_flags])
   749      if not CONFIG.LINK_WITH_LD_TOOL:
   750          linker_flags += ' ' + _default_cflags(c, dbg)
   751      return ' '.join([objs, build_id_flag, linker_flags, pkg_config_cmd])
   752  
   753  
   754  def _library_cmds(c, compiler_flags, pkg_config_libs, extra_flags='', archive=True):
   755      """Returns the commands needed for a cc_library rule."""
   756      dbg_flags = _build_flags(compiler_flags, pkg_config_libs, c=c, dbg=True)
   757      opt_flags = _build_flags(compiler_flags, pkg_config_libs, c=c)
   758      cmd_template = '$TOOLS_CC -c -I . ${SRCS_SRCS} %s %s'
   759      if archive:
   760          cmd_template += ' && $TOOLS_AR rcs $OUT *.o'
   761      cmds = {
   762          'dbg': cmd_template % (dbg_flags, extra_flags),
   763          'opt': cmd_template % (opt_flags, extra_flags),
   764      }
   765      if CONFIG.CPP_COVERAGE:
   766          cmds['cover'] = cmd_template % (dbg_flags + _COVERAGE_FLAGS, extra_flags)
   767      return cmds, {
   768          'cc': [CONFIG.CC_TOOL if c else CONFIG.CPP_TOOL],
   769          'ar': [CONFIG.AR_TOOL if archive else None],
   770      }
   771  
   772  
   773  def _binary_cmds(c, linker_flags, pkg_config_libs, extra_flags='', shared=False, alwayslink=''):
   774      """Returns the commands needed for a cc_binary, cc_test or cc_shared_object rule."""
   775      dbg_flags = _binary_build_flags(linker_flags, pkg_config_libs, shared, alwayslink, c=c, dbg=True)
   776      opt_flags = _binary_build_flags(linker_flags, pkg_config_libs, shared, alwayslink, c=c, dbg=False)
   777      cmds = {
   778          'dbg': '$TOOL -o ${OUT} %s %s' % (dbg_flags, extra_flags),
   779          'opt': '$TOOL -o ${OUT} %s %s' % (opt_flags, extra_flags),
   780      }
   781      if CONFIG.CPP_COVERAGE:
   782          cmds['cover'] = '$TOOL -o ${OUT} %s %s %s' % (dbg_flags, extra_flags, _COVERAGE_FLAGS)
   783      return cmds, [CONFIG.LD_TOOL if CONFIG.LINK_WITH_LD_TOOL else
   784                    CONFIG.CC_TOOL if c else CONFIG.CPP_TOOL]
   785  
   786  
   787  def _library_transitive_labels(c, compiler_flags, pkg_config_libs, archive=True):
   788      """Applies commands from transitive labels to a cc_library rule."""
   789      def apply_transitive_labels(name):
   790          labels = get_labels(name, 'cc:')
   791          flags = ['-isystem %s' % l[4:] for l in labels if l.startswith('inc:')]
   792          flags.extend(['-D' + l[4:] for l in labels if l.startswith('def:')])
   793          pkg_config_libs.extend([l[3:] for l in labels if l.startswith('pc:') and l[3:] not in pkg_config_libs])
   794          if flags:  # Don't update if there aren't any relevant labels
   795              cmds, _ = _library_cmds(c, compiler_flags, pkg_config_libs, ' '.join(flags), archive=archive)
   796              for k, v in cmds.items():
   797                  set_command(name, k, v)
   798      return apply_transitive_labels
   799  
   800  
   801  def _binary_transitive_labels(c, linker_flags, pkg_config_libs, shared=False):
   802      """Applies commands from transitive labels to a cc_binary, cc_test or cc_shared_object rule."""
   803      def apply_transitive_labels(name):
   804          labels = get_labels(name, 'cc:')
   805          linker_prefix = '' if CONFIG.LINK_WITH_LD_TOOL else '-Wl,'
   806          flags = [linker_prefix + l[3:] for l in labels if l.startswith('ld:')]
   807          flags.extend(['`pkg-config --libs %s`' % l[3:] for l in labels if l.startswith('pc:')])
   808          # ./ here because some weak linkers don't realise ./lib.a is the same file as lib.a
   809          # and report duplicate symbol errors as a result.
   810          alwayslink = ' '.join(['./' + l[3:] for l in labels if l.startswith('al:')])
   811          # Probably a little optimistic to check this (most binaries are likely to have *some*
   812          # kind of linker flags to apply), but we might as well.
   813          if flags or alwayslink:
   814              cmds, _ = _binary_cmds(c, linker_flags, pkg_config_libs, ' '.join(flags), shared, alwayslink)
   815              for k, v in cmds.items():
   816                  set_command(name, k, v)
   817      return apply_transitive_labels
   818  
   819  
   820  if CONFIG.BAZEL_COMPATIBILITY:
   821      # For nominal Buck compatibility. The cc_ forms are preferred.
   822      cxx_binary = cc_binary
   823      cxx_library = cc_library
   824      cxx_test = cc_test