github.com/k14s/starlark-go@v0.0.0-20200720175618-3a5c849cc368/syntax/testdata/scan.star (about)

     1  # Copyright 2014 The Bazel Authors. All rights reserved.
     2  #
     3  # Licensed under the Apache License, Version 2.0 (the "License");
     4  # you may not use this file except in compliance with the License.
     5  # You may obtain a copy of the License at
     6  #
     7  #    http://www.apache.org/licenses/LICENSE-2.0
     8  #
     9  # Unless required by applicable law or agreed to in writing, software
    10  # distributed under the License is distributed on an "AS IS" BASIS,
    11  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  # See the License for the specific language governing permissions and
    13  # limitations under the License.
    14  
    15  # (From https://github.com/bazelbuild/rules_go/blob/master/go/def.bzl@a6f9d0c)
    16  
    17  load("//go/private:repositories.bzl", "go_repositories")
    18  load("//go/private:go_repository.bzl", "go_repository", "new_go_repository")
    19  load("//go/private:go_prefix.bzl", "go_prefix")
    20  load("//go/private:json.bzl", "json_marshal")
    21  
    22  """These are bare-bones Go rules.
    23  
    24  In order of priority:
    25  
    26  - BUILD file must be written by hand.
    27  
    28  - No support for SWIG
    29  
    30  - No test sharding or test XML.
    31  
    32  """
    33  
    34  _DEFAULT_LIB = "go_default_library"
    35  
    36  _VENDOR_PREFIX = "/vendor/"
    37  
    38  go_filetype = FileType([
    39      ".go",
    40      ".s",
    41      ".S",
    42      ".h",  # may be included by .s
    43  ])
    44  
    45  # be consistent to cc_library.
    46  hdr_exts = [
    47      ".h",
    48      ".hh",
    49      ".hpp",
    50      ".hxx",
    51      ".inc",
    52  ]
    53  
    54  cc_hdr_filetype = FileType(hdr_exts)
    55  
    56  # Extensions of files we can build with the Go compiler or with cc_library.
    57  # This is a subset of the extensions recognized by go/build.
    58  cgo_filetype = FileType([
    59      ".go",
    60      ".c",
    61      ".cc",
    62      ".cxx",
    63      ".cpp",
    64      ".s",
    65      ".S",
    66      ".h",
    67      ".hh",
    68      ".hpp",
    69      ".hxx",
    70  ])
    71  
    72  ################
    73  
    74  def go_environment_vars(ctx):
    75    """Return a map of environment variables for use with actions, based on
    76    the arguments. Uses the ctx.fragments.cpp.cpu attribute, if present,
    77    and picks a default of target_os="linux" and target_arch="amd64"
    78    otherwise.
    79  
    80    Args:
    81      The starlark Context.
    82  
    83    Returns:
    84      A dict of environment variables for running Go tool commands that build for
    85      the target OS and architecture.
    86    """
    87    default_toolchain = {"GOOS": "linux", "GOARCH": "amd64"}
    88    bazel_to_go_toolchain = {
    89      "k8": {"GOOS": "linux", "GOARCH": "amd64"},
    90      "piii": {"GOOS": "linux", "GOARCH": "386"},
    91      "darwin": {"GOOS": "darwin", "GOARCH": "amd64"},
    92      "darwin_x86_64": {"GOOS": "darwin", "GOARCH": "amd64"},
    93      "freebsd": {"GOOS": "freebsd", "GOARCH": "amd64"},
    94      "armeabi-v7a": {"GOOS": "linux", "GOARCH": "arm"},
    95      "arm": {"GOOS": "linux", "GOARCH": "arm"}
    96    }
    97    env = {}
    98    if hasattr(ctx.file, "go_tool"):
    99      env["GOROOT"] = ctx.file.go_tool.dirname + "/.."
   100    env.update(bazel_to_go_toolchain.get(ctx.fragments.cpp.cpu, default_toolchain))
   101    return env
   102  
   103  def _is_darwin_cpu(ctx):
   104    cpu = ctx.fragments.cpp.cpu
   105    return cpu == "darwin" or cpu == "darwin_x86_64"
   106  
   107  def _emit_generate_params_action(cmds, ctx, fn):
   108    cmds_all = [
   109        # Use bash explicitly. /bin/sh is default, and it may be linked to a
   110        # different shell, e.g., /bin/dash on Ubuntu.
   111        "#!/bin/bash",
   112        "set -e",
   113    ]
   114    cmds_all += cmds
   115    cmds_all_str = "\n".join(cmds_all) + "\n"
   116    f = ctx.new_file(ctx.configuration.bin_dir, fn)
   117    ctx.file_action(
   118        output = f,
   119        content = cmds_all_str,
   120        executable = True)
   121    return f
   122  
   123  def _emit_go_asm_action(ctx, source, hdrs, out_obj):
   124    """Construct the command line for compiling Go Assembly code.
   125    Constructs a symlink tree to accomodate for workspace name.
   126    Args:
   127      ctx: The starlark Context.
   128      source: a source code artifact
   129      hdrs: list of .h files that may be included
   130      out_obj: the artifact (configured target?) that should be produced
   131    """
   132    params = {
   133        "go_tool": ctx.file.go_tool.path,
   134        "includes": [f.dirname for f in hdrs] + [ctx.file.go_include.path],
   135        "source": source.path,
   136        "out": out_obj.path,
   137    }
   138  
   139    inputs = hdrs + ctx.files.toolchain + [source]
   140    ctx.action(
   141        inputs = inputs,
   142        outputs = [out_obj],
   143        mnemonic = "GoAsmCompile",
   144        executable = ctx.executable._asm,
   145        arguments = [json_marshal(params)],
   146    )
   147  
   148  def _go_importpath(ctx):
   149    """Returns the expected importpath of the go_library being built.
   150  
   151    Args:
   152      ctx: The starlark Context
   153  
   154    Returns:
   155      Go importpath of the library
   156    """
   157    path = ctx.attr.importpath
   158    if path != "":
   159      return path
   160    path = ctx.attr.go_prefix.go_prefix
   161    if path.endswith("/"):
   162      path = path[:-1]
   163    if ctx.label.package:
   164      path += "/" + ctx.label.package
   165    if ctx.label.name != _DEFAULT_LIB:
   166      path += "/" + ctx.label.name
   167    if path.rfind(_VENDOR_PREFIX) != -1:
   168      path = path[len(_VENDOR_PREFIX) + path.rfind(_VENDOR_PREFIX):]
   169    if path[0] == "/":
   170      path = path[1:]
   171    return path
   172  
   173  def _emit_go_compile_action(ctx, sources, deps, libpaths, out_object, gc_goopts):
   174    """Construct the command line for compiling Go code.
   175  
   176    Args:
   177      ctx: The starlark Context.
   178      sources: an iterable of source code artifacts (or CTs? or labels?)
   179      deps: an iterable of dependencies. Each dependency d should have an
   180        artifact in d.transitive_go_libraries representing all imported libraries.
   181      libpaths: the set of paths to search for imported libraries.
   182      out_object: the object file that should be produced
   183      gc_goopts: additional flags to pass to the compiler.
   184    """
   185    if ctx.coverage_instrumented():
   186      sources = _emit_go_cover_action(ctx, sources)
   187  
   188    # Compile filtered files.
   189    args = [
   190        "-cgo",
   191        ctx.file.go_tool.path,
   192        "tool", "compile",
   193        "-o", out_object.path,
   194        "-trimpath", "-abs-.",
   195        "-I", "-abs-.",
   196    ]
   197    inputs = depset(sources + ctx.files.toolchain)
   198    for dep in deps:
   199      inputs += dep.transitive_go_libraries
   200    for path in libpaths:
   201      args += ["-I", path]
   202    args += gc_goopts + [("" if i.basename.startswith("_cgo") else "-filter-") + i.path for i in sources]
   203    ctx.action(
   204        inputs = list(inputs),
   205        outputs = [out_object],
   206        mnemonic = "GoCompile",
   207        executable = ctx.executable._filter_exec,
   208        arguments = args,
   209        env = go_environment_vars(ctx),
   210    )
   211  
   212    return sources
   213  
   214  def _emit_go_pack_action(ctx, out_lib, objects):
   215    """Construct the command line for packing objects together.
   216  
   217    Args:
   218      ctx: The starlark Context.
   219      out_lib: the archive that should be produced
   220      objects: an iterable of object files to be added to the output archive file.
   221    """
   222    ctx.action(
   223        inputs = objects + ctx.files.toolchain,
   224        outputs = [out_lib],
   225        mnemonic = "GoPack",
   226        executable = ctx.file.go_tool,
   227        arguments = ["tool", "pack", "c", out_lib.path] + [a.path for a in objects],
   228        env = go_environment_vars(ctx),
   229    )
   230  
   231  def _emit_go_cover_action(ctx, sources):
   232    """Construct the command line for test coverage instrument.
   233  
   234    Args:
   235      ctx: The starlark Context.
   236      sources: an iterable of Go source files.
   237  
   238    Returns:
   239      A list of Go source code files which might be coverage instrumented.
   240    """
   241    outputs = []
   242    # TODO(linuxerwang): make the mode configurable.
   243    count = 0
   244  
   245    for src in sources:
   246      if not src.path.endswith(".go") or src.path.endswith("_test.go"):
   247        outputs += [src]
   248        continue
   249  
   250      cover_var = "GoCover_%d" % count
   251      out = ctx.new_file(src, src.basename[:-3] + '_' + cover_var + '.cover.go')
   252      outputs += [out]
   253      ctx.action(
   254          inputs = [src] + ctx.files.toolchain,
   255          outputs = [out],
   256          mnemonic = "GoCover",
   257          executable = ctx.file.go_tool,
   258          arguments = ["tool", "cover", "--mode=set", "-var=%s" % cover_var, "-o", out.path, src.path],
   259          env = go_environment_vars(ctx),
   260      )
   261      count += 1
   262  
   263    return outputs
   264  
   265  def go_library_impl(ctx):
   266    """Implements the go_library() rule."""
   267  
   268    sources = depset(ctx.files.srcs)
   269    go_srcs = depset([s for s in sources if s.basename.endswith('.go')])
   270    asm_srcs = [s for s in sources if s.basename.endswith('.s') or s.basename.endswith('.S')]
   271    asm_hdrs = [s for s in sources if s.basename.endswith('.h')]
   272    deps = ctx.attr.deps
   273    dep_runfiles = [d.data_runfiles for d in deps]
   274  
   275    cgo_object = None
   276    if hasattr(ctx.attr, "cgo_object"):
   277      cgo_object = ctx.attr.cgo_object
   278  
   279    if ctx.attr.library:
   280      go_srcs += ctx.attr.library.go_sources
   281      asm_srcs += ctx.attr.library.asm_sources
   282      asm_hdrs += ctx.attr.library.asm_headers
   283      deps += ctx.attr.library.direct_deps
   284      dep_runfiles += [ctx.attr.library.data_runfiles]
   285      if ctx.attr.library.cgo_object:
   286        if cgo_object:
   287          fail("go_library %s cannot have cgo_object because the package " +
   288               "already has cgo_object in %s" % (ctx.label.name,
   289                                                 ctx.attr.library.name))
   290        cgo_object = ctx.attr.library.cgo_object
   291    if not go_srcs:
   292      fail("may not be empty", "srcs")
   293  
   294    transitive_cgo_deps = depset([], order="topological")
   295    if cgo_object:
   296      dep_runfiles += [cgo_object.data_runfiles]
   297      transitive_cgo_deps += cgo_object.cgo_deps
   298  
   299    extra_objects = [cgo_object.cgo_obj] if cgo_object else []
   300    for src in asm_srcs:
   301      obj = ctx.new_file(src, "%s.dir/%s.o" % (ctx.label.name, src.basename[:-2]))
   302      _emit_go_asm_action(ctx, src, asm_hdrs, obj)
   303      extra_objects += [obj]
   304  
   305    lib_name = _go_importpath(ctx) + ".a"
   306    out_lib = ctx.new_file(lib_name)
   307    out_object = ctx.new_file(ctx.label.name + ".o")
   308    search_path = out_lib.path[:-len(lib_name)]
   309    gc_goopts = _gc_goopts(ctx)
   310    transitive_go_libraries = depset([out_lib])
   311    transitive_go_library_paths = depset([search_path])
   312    for dep in deps:
   313      transitive_go_libraries += dep.transitive_go_libraries
   314      transitive_cgo_deps += dep.transitive_cgo_deps
   315      transitive_go_library_paths += dep.transitive_go_library_paths
   316  
   317    go_srcs = _emit_go_compile_action(ctx,
   318        sources = go_srcs,
   319        deps = deps,
   320        libpaths = transitive_go_library_paths,
   321        out_object = out_object,
   322        gc_goopts = gc_goopts,
   323    )
   324    _emit_go_pack_action(ctx, out_lib, [out_object] + extra_objects)
   325  
   326    dylibs = []
   327    if cgo_object:
   328      dylibs += [d for d in cgo_object.cgo_deps if d.path.endswith(".so")]
   329  
   330    runfiles = ctx.runfiles(files = dylibs, collect_data = True)
   331    for d in dep_runfiles:
   332      runfiles = runfiles.merge(d)
   333  
   334    return struct(
   335      label = ctx.label,
   336      files = depset([out_lib]),
   337      runfiles = runfiles,
   338      go_sources = go_srcs,
   339      asm_sources = asm_srcs,
   340      asm_headers = asm_hdrs,
   341      cgo_object = cgo_object,
   342      direct_deps = ctx.attr.deps,
   343      transitive_cgo_deps = transitive_cgo_deps,
   344      transitive_go_libraries = transitive_go_libraries,
   345      transitive_go_library_paths = transitive_go_library_paths,
   346      gc_goopts = gc_goopts,
   347    )
   348  
   349  def _c_linker_options(ctx, blacklist=[]):
   350    """Extracts flags to pass to $(CC) on link from the current context
   351  
   352    Args:
   353      ctx: the current context
   354      blacklist: Any flags starts with any of these prefixes are filtered out from
   355        the return value.
   356  
   357    Returns:
   358      A list of command line flags
   359    """
   360    cpp = ctx.fragments.cpp
   361    features = ctx.features
   362    options = cpp.compiler_options(features)
   363    options += cpp.unfiltered_compiler_options(features)
   364    options += cpp.link_options
   365    options += cpp.mostly_static_link_options(ctx.features, False)
   366    filtered = []
   367    for opt in options:
   368      if any([opt.startswith(prefix) for prefix in blacklist]):
   369        continue
   370      filtered.append(opt)
   371    return filtered
   372  
   373  def _gc_goopts(ctx):
   374    gc_goopts = [ctx.expand_make_variables("gc_goopts", f, {})
   375                 for f in ctx.attr.gc_goopts]
   376    if ctx.attr.library:
   377      gc_goopts += ctx.attr.library.gc_goopts
   378    return gc_goopts
   379  
   380  def _gc_linkopts(ctx):
   381    gc_linkopts = [ctx.expand_make_variables("gc_linkopts", f, {})
   382                   for f in ctx.attr.gc_linkopts]
   383    for k, v in ctx.attr.x_defs.items():
   384      gc_linkopts += ["-X", "%s='%s'" % (k, v)]
   385    return gc_linkopts
   386  
   387  def _extract_extldflags(gc_linkopts, extldflags):
   388    """Extracts -extldflags from gc_linkopts and combines them into a single list.
   389  
   390    Args:
   391      gc_linkopts: a list of flags passed in through the gc_linkopts attributes.
   392        ctx.expand_make_variables should have already been applied.
   393      extldflags: a list of flags to be passed to the external linker.
   394  
   395    Return:
   396      A tuple containing the filtered gc_linkopts with external flags removed,
   397      and a combined list of external flags.
   398    """
   399    filtered_gc_linkopts = []
   400    is_extldflags = False
   401    for opt in gc_linkopts:
   402      if is_extldflags:
   403        is_extldflags = False
   404        extldflags += [opt]
   405      elif opt == "-extldflags":
   406        is_extldflags = True
   407      else:
   408        filtered_gc_linkopts += [opt]
   409    return filtered_gc_linkopts, extldflags
   410  
   411  def _emit_go_link_action(ctx, transitive_go_library_paths, transitive_go_libraries, cgo_deps, libs,
   412                           executable, gc_linkopts):
   413    """Sets up a symlink tree to libraries to link together."""
   414    config_strip = len(ctx.configuration.bin_dir.path) + 1
   415    pkg_depth = executable.dirname[config_strip:].count('/') + 1
   416  
   417    ld = "%s" % ctx.fragments.cpp.compiler_executable
   418    extldflags = _c_linker_options(ctx) + [
   419        "-Wl,-rpath,$ORIGIN/" + ("../" * pkg_depth),
   420    ]
   421    for d in cgo_deps:
   422      if d.basename.endswith('.so'):
   423        short_dir = d.dirname[len(d.root.path):]
   424        extldflags += ["-Wl,-rpath,$ORIGIN/" + ("../" * pkg_depth) + short_dir]
   425    gc_linkopts, extldflags = _extract_extldflags(gc_linkopts, extldflags)
   426  
   427    link_cmd = [
   428        ctx.file.go_tool.path,
   429        "tool", "link",
   430        "-L", "."
   431    ]
   432    for path in transitive_go_library_paths:
   433      link_cmd += ["-L", path]
   434    link_cmd += [
   435        "-o", executable.path,
   436    ] + gc_linkopts + ['"${STAMP_XDEFS[@]}"']
   437  
   438    # workaround for a bug in ld(1) on Mac OS X.
   439    # http://lists.apple.com/archives/Darwin-dev/2006/Sep/msg00084.html
   440    # TODO(yugui) Remove this workaround once rules_go stops supporting XCode 7.2
   441    # or earlier.
   442    if not _is_darwin_cpu(ctx):
   443      link_cmd += ["-s"]
   444  
   445    link_cmd += [
   446        "-extld", ld,
   447        "-extldflags", "'%s'" % " ".join(extldflags),
   448    ] + [lib.path for lib in libs]
   449  
   450    # Avoided -s on OSX but but it requires dsymutil to be on $PATH.
   451    # TODO(yugui) Remove this workaround once rules_go stops supporting XCode 7.2
   452    # or earlier.
   453    cmds = ["export PATH=$PATH:/usr/bin"]
   454  
   455    cmds += [
   456        "STAMP_XDEFS=()",
   457    ]
   458  
   459    stamp_inputs = []
   460    if ctx.attr.linkstamp:
   461      # read workspace status files, converting "KEY value" lines
   462      # to "-X $linkstamp.KEY=value" arguments to the go linker.
   463      stamp_inputs = [ctx.info_file, ctx.version_file]
   464      for f in stamp_inputs:
   465        cmds += [
   466            "while read -r key value || [[ -n $key ]]; do",
   467            "  STAMP_XDEFS+=(-X \"%s.$key=$value\")" % ctx.attr.linkstamp,
   468            "done < " + f.path,
   469        ]
   470  
   471    cmds += [' '.join(link_cmd)]
   472  
   473    f = _emit_generate_params_action(cmds, ctx, lib.basename + ".GoLinkFile.params")
   474  
   475    ctx.action(
   476        inputs = [f] + (list(transitive_go_libraries) + [lib] + list(cgo_deps) +
   477                  ctx.files.toolchain + ctx.files._crosstool) + stamp_inputs,
   478        outputs = [executable],
   479        command = f.path,
   480        mnemonic = "GoLink",
   481        env = go_environment_vars(ctx),
   482    )
   483  
   484  def go_binary_impl(ctx):
   485    """go_binary_impl emits actions for compiling and linking a go executable."""
   486    lib_result = go_library_impl(ctx)
   487    _emit_go_link_action(
   488      ctx,
   489      transitive_go_libraries=lib_result.transitive_go_libraries,
   490      transitive_go_library_paths=lib_result.transitive_go_library_paths,
   491      cgo_deps=lib_result.transitive_cgo_deps,
   492      libs=lib_result.files,
   493      executable=ctx.outputs.executable,
   494      gc_linkopts=_gc_linkopts(ctx))
   495  
   496    return struct(
   497        files = depset([ctx.outputs.executable]),
   498        runfiles = lib_result.runfiles,
   499        cgo_object = lib_result.cgo_object,
   500    )
   501  
   502  def go_test_impl(ctx):
   503    """go_test_impl implements go testing.
   504  
   505    It emits an action to run the test generator, and then compiles the
   506    test into a binary."""
   507  
   508    lib_result = go_library_impl(ctx)
   509    main_go = ctx.new_file(ctx.label.name + "_main_test.go")
   510    main_object = ctx.new_file(ctx.label.name + "_main_test.o")
   511    main_lib = ctx.new_file(ctx.label.name + "_main_test.a")
   512    go_import = _go_importpath(ctx)
   513  
   514    cmds = [
   515        'UNFILTERED_TEST_FILES=(%s)' %
   516            ' '.join(["'%s'" % f.path for f in lib_result.go_sources]),
   517        'FILTERED_TEST_FILES=()',
   518        'while read -r line; do',
   519        '  if [ -n "$line" ]; then',
   520        '    FILTERED_TEST_FILES+=("$line")',
   521        '  fi',
   522        'done < <(\'%s\' -cgo "${UNFILTERED_TEST_FILES[@]}")' %
   523            ctx.executable._filter_tags.path,
   524        ' '.join([
   525            "'%s'" % ctx.executable.test_generator.path,
   526            '--package',
   527            go_import,
   528            '--output',
   529            "'%s'" % main_go.path,
   530            '"${FILTERED_TEST_FILES[@]}"',
   531        ]),
   532    ]
   533    f = _emit_generate_params_action(
   534        cmds, ctx, ctx.label.name + ".GoTestGenTest.params")
   535    inputs = (list(lib_result.go_sources) + list(ctx.files.toolchain) +
   536              [f, ctx.executable._filter_tags, ctx.executable.test_generator])
   537    ctx.action(
   538        inputs = inputs,
   539        outputs = [main_go],
   540        command = f.path,
   541        mnemonic = "GoTestGenTest",
   542        env = dict(go_environment_vars(ctx), RUNDIR=ctx.label.package))
   543  
   544    _emit_go_compile_action(
   545      ctx,
   546      sources=depset([main_go]),
   547      deps=ctx.attr.deps + [lib_result],
   548      libpaths=lib_result.transitive_go_library_paths,
   549      out_object=main_object,
   550      gc_goopts=_gc_goopts(ctx),
   551    )
   552    _emit_go_pack_action(ctx, main_lib, [main_object])
   553    _emit_go_link_action(
   554      ctx,
   555      transitive_go_library_paths=lib_result.transitive_go_library_paths,
   556      transitive_go_libraries=lib_result.transitive_go_libraries,
   557      cgo_deps=lib_result.transitive_cgo_deps,
   558      libs=[main_lib],
   559      executable=ctx.outputs.executable,
   560      gc_linkopts=_gc_linkopts(ctx))
   561  
   562    # TODO(bazel-team): the Go tests should do a chdir to the directory
   563    # holding the data files, so open-source go tests continue to work
   564    # without code changes.
   565    runfiles = ctx.runfiles(files = [ctx.outputs.executable])
   566    runfiles = runfiles.merge(lib_result.runfiles)
   567    return struct(
   568        files = depset([ctx.outputs.executable]),
   569        runfiles = runfiles,
   570    )
   571  
   572  go_env_attrs = {
   573      "toolchain": attr.label(
   574          default = Label("//go/toolchain:toolchain"),
   575          allow_files = True,
   576          cfg = "host",
   577      ),
   578      "go_tool": attr.label(
   579          default = Label("//go/toolchain:go_tool"),
   580          single_file = True,
   581          allow_files = True,
   582          cfg = "host",
   583      ),
   584      "go_prefix": attr.label(
   585          providers = ["go_prefix"],
   586          default = Label(
   587              "//:go_prefix",
   588              relative_to_caller_repository = True,
   589          ),
   590          allow_files = False,
   591          cfg = "host",
   592      ),
   593      "go_src": attr.label(
   594          default = Label("//go/toolchain:go_src"),
   595          allow_files = True,
   596          cfg = "host",
   597      ),
   598      "go_include": attr.label(
   599          default = Label("//go/toolchain:go_include"),
   600          single_file = True,
   601          allow_files = True,
   602          cfg = "host",
   603      ),
   604      "go_root": attr.label(
   605          providers = ["go_root"],
   606          default = Label(
   607              "//go/toolchain:go_root",
   608          ),
   609          allow_files = False,
   610          cfg = "host",
   611      ),
   612      "_filter_tags": attr.label(
   613          default = Label("//go/tools/filter_tags"),
   614          cfg = "host",
   615          executable = True,
   616          single_file = True,
   617      ),
   618      "_filter_exec": attr.label(
   619          default = Label("//go/tools/filter_exec"),
   620          cfg = "host",
   621          executable = True,
   622          single_file = True,
   623      ),
   624      "_asm": attr.label(
   625          default = Label("//go/tools/builders:asm"),
   626          cfg = "host",
   627          executable = True,
   628          single_file = True,
   629      ),
   630  }
   631  
   632  go_library_attrs = go_env_attrs + {
   633      "data": attr.label_list(
   634          allow_files = True,
   635          cfg = "data",
   636      ),
   637      "srcs": attr.label_list(allow_files = go_filetype),
   638      "deps": attr.label_list(
   639          providers = [
   640              "transitive_go_library_paths",
   641              "transitive_go_libraries",
   642              "transitive_cgo_deps",
   643          ],
   644      ),
   645      "importpath": attr.string(),
   646      "library": attr.label(
   647          providers = [
   648              "direct_deps",
   649              "go_sources",
   650              "asm_sources",
   651              "cgo_object",
   652              "gc_goopts",
   653          ],
   654      ),
   655      "gc_goopts": attr.string_list(),
   656  }
   657  
   658  _crosstool_attrs = {
   659      "_crosstool": attr.label(
   660          default = Label("//tools/defaults:crosstool"),
   661      ),
   662  }
   663  
   664  go_link_attrs = go_library_attrs + _crosstool_attrs + {
   665      "gc_linkopts": attr.string_list(),
   666      "linkstamp": attr.string(),
   667      "x_defs": attr.string_dict(),
   668  }
   669  
   670  go_library = rule(
   671      go_library_impl,
   672      attrs = go_library_attrs + {
   673          "cgo_object": attr.label(
   674              providers = [
   675                  "cgo_obj",
   676                  "cgo_deps",
   677              ],
   678          ),
   679      },
   680      fragments = ["cpp"],
   681  )
   682  
   683  go_binary = rule(
   684      go_binary_impl,
   685      attrs = go_library_attrs + _crosstool_attrs + go_link_attrs,
   686      executable = True,
   687      fragments = ["cpp"],
   688  )
   689  
   690  go_test = rule(
   691      go_test_impl,
   692      attrs = go_library_attrs + _crosstool_attrs + go_link_attrs + {
   693          "test_generator": attr.label(
   694              executable = True,
   695              default = Label(
   696                  "//go/tools:generate_test_main",
   697              ),
   698              cfg = "host",
   699          ),
   700      },
   701      executable = True,
   702      fragments = ["cpp"],
   703      test = True,
   704  )
   705  
   706  def _pkg_dir(workspace_root, package_name):
   707    if workspace_root and package_name:
   708      return workspace_root + "/" + package_name
   709    if workspace_root:
   710      return workspace_root
   711    if package_name:
   712      return package_name
   713    return "."
   714  
   715  def _exec_path(path):
   716    if path.startswith('/'):
   717      return path
   718    return '${execroot}/' + path
   719  
   720  def _cgo_filter_srcs_impl(ctx):
   721    srcs = ctx.files.srcs
   722    dsts = []
   723    cmds = []
   724    for src in srcs:
   725      stem, _, ext = src.path.rpartition('.')
   726      dst_basename = "%s.filtered.%s" % (stem, ext)
   727      dst = ctx.new_file(src, dst_basename)
   728      cmds += [
   729          "if '%s' -cgo -quiet '%s'; then" %
   730              (ctx.executable._filter_tags.path, src.path),
   731          "  cp '%s' '%s'" % (src.path, dst.path),
   732          "else",
   733          "  echo -n >'%s'" % dst.path,
   734          "fi",
   735      ]
   736      dsts.append(dst)
   737  
   738    if ctx.label.package == "":
   739      script_name = ctx.label.name + ".CGoFilterSrcs.params"
   740    else:
   741      script_name = ctx.label.package + "/" + ctx.label.name + ".CGoFilterSrcs.params"
   742    f = _emit_generate_params_action(cmds, ctx, script_name)
   743    ctx.action(
   744        inputs = [f, ctx.executable._filter_tags] + srcs,
   745        outputs = dsts,
   746        command = f.path,
   747        mnemonic = "CgoFilterSrcs",
   748    )
   749    return struct(
   750        files = depset(dsts),
   751    )
   752  
   753  _cgo_filter_srcs = rule(
   754      implementation = _cgo_filter_srcs_impl,
   755      attrs = {
   756          "srcs": attr.label_list(
   757              allow_files = cgo_filetype,
   758          ),
   759          "_filter_tags": attr.label(
   760              default = Label("//go/tools/filter_tags"),
   761              cfg = "host",
   762              executable = True,
   763              single_file = True,
   764          ),
   765      },
   766      fragments = ["cpp"],
   767  )
   768  
   769  def _cgo_codegen_impl(ctx):
   770    go_srcs = ctx.files.srcs
   771    srcs = go_srcs + ctx.files.c_hdrs
   772    linkopts = ctx.attr.linkopts
   773    copts = ctx.fragments.cpp.c_options + ctx.attr.copts
   774    deps = depset([], order="topological")
   775    for d in ctx.attr.deps:
   776      srcs += list(d.cc.transitive_headers)
   777      deps += d.cc.libs
   778      copts += ['-D' + define for define in d.cc.defines]
   779      for inc in d.cc.include_directories:
   780        copts += ['-I', _exec_path(inc)]
   781      for hdr in ctx.files.c_hdrs:
   782          copts += ['-iquote', hdr.dirname]
   783      for inc in d.cc.quote_include_directories:
   784        copts += ['-iquote', _exec_path(inc)]
   785      for inc in d.cc.system_include_directories:
   786        copts += ['-isystem',  _exec_path(inc)]
   787      for lib in d.cc.libs:
   788        if lib.basename.startswith('lib') and lib.basename.endswith('.so'):
   789          linkopts += ['-L', lib.dirname, '-l', lib.basename[3:-3]]
   790        else:
   791          linkopts += [lib.path]
   792      linkopts += d.cc.link_flags
   793  
   794    p = _pkg_dir(ctx.label.workspace_root, ctx.label.package) + "/"
   795    if p == "./":
   796      p = "" # workaround when cgo_library in repository root
   797    out_dir = (ctx.configuration.genfiles_dir.path + '/' +
   798               p + ctx.attr.outdir)
   799    cc = ctx.fragments.cpp.compiler_executable
   800    cmds = [
   801        # We cannot use env for CC because $(CC) on OSX is relative
   802        # and '../' does not work fine due to symlinks.
   803        'export CC=$(cd $(dirname {cc}); pwd)/$(basename {cc})'.format(cc=cc),
   804        'export CXX=$CC',
   805        'objdir="%s/gen"' % out_dir,
   806        'execroot=$(pwd)',
   807        'mkdir -p "$objdir"',
   808        'unfiltered_go_files=(%s)' % ' '.join(["'%s'" % f.path for f in go_srcs]),
   809        'filtered_go_files=()',
   810        'for file in "${unfiltered_go_files[@]}"; do',
   811        '  stem=$(basename "$file" .go)',
   812        '  if %s -cgo -quiet "$file"; then' % ctx.executable._filter_tags.path,
   813        '    filtered_go_files+=("$file")',
   814        '  else',
   815        '    grep --max-count 1 "^package " "$file" >"$objdir/$stem.go"',
   816        '    echo -n >"$objdir/$stem.c"',
   817        '  fi',
   818        'done',
   819        'if [ ${#filtered_go_files[@]} -eq 0 ]; then',
   820        '  echo no buildable Go source files in %s >&1' % str(ctx.label),
   821        '  exit 1',
   822        'fi',
   823        '"$GOROOT/bin/go" tool cgo -objdir "$objdir" -- %s "${filtered_go_files[@]}"' %
   824            ' '.join(['"%s"' % copt for copt in copts]),
   825        # Rename the outputs using glob so we don't have to understand cgo's mangling
   826        # TODO(#350): might be fixed by this?.
   827        'for file in "${filtered_go_files[@]}"; do',
   828        '  stem=$(basename "$file" .go)',
   829        '  mv "$objdir/"*"$stem.cgo1.go" "$objdir/$stem.go"',
   830        '  mv "$objdir/"*"$stem.cgo2.c" "$objdir/$stem.c"',
   831        'done',
   832        'rm -f $objdir/_cgo_.o $objdir/_cgo_flags',
   833      ]
   834  
   835    f = _emit_generate_params_action(cmds, ctx, out_dir + ".CGoCodeGenFile.params")
   836  
   837    inputs = (srcs + ctx.files.toolchain + ctx.files._crosstool +
   838              [f, ctx.executable._filter_tags])
   839    ctx.action(
   840        inputs = inputs,
   841        outputs = ctx.outputs.outs,
   842        mnemonic = "CGoCodeGen",
   843        progress_message = "CGoCodeGen %s" % ctx.label,
   844        command = f.path,
   845        env = go_environment_vars(ctx) + {
   846            "CGO_LDFLAGS": " ".join(linkopts),
   847        },
   848    )
   849    return struct(
   850        label = ctx.label,
   851        files = depset(ctx.outputs.outs),
   852        cgo_deps = deps,
   853    )
   854  
   855  _cgo_codegen_rule = rule(
   856      _cgo_codegen_impl,
   857      attrs = go_env_attrs + _crosstool_attrs + {
   858          "srcs": attr.label_list(
   859              allow_files = go_filetype,
   860              non_empty = True,
   861          ),
   862          "c_hdrs": attr.label_list(
   863              allow_files = cc_hdr_filetype,
   864          ),
   865          "deps": attr.label_list(
   866              allow_files = False,
   867              providers = ["cc"],
   868          ),
   869          "copts": attr.string_list(),
   870          "linkopts": attr.string_list(),
   871          "outdir": attr.string(mandatory = True),
   872          "outs": attr.output_list(
   873              mandatory = True,
   874              non_empty = True,
   875          ),
   876      },
   877      fragments = ["cpp"],
   878      output_to_genfiles = True,
   879  )
   880  
   881  def _cgo_codegen(name, srcs, c_hdrs=[], deps=[], copts=[], linkopts=[],
   882                   go_tool=None, toolchain=None):
   883    """Generates glue codes for interop between C and Go
   884  
   885    Args:
   886      name: A unique name of the rule
   887      srcs: list of Go source files.
   888        Each of them must contain `import "C"`.
   889      c_hdrs: C/C++ header files necessary to determine kinds of
   890        C/C++ identifiers in srcs.
   891      deps: A list of cc_library rules.
   892        The generated codes are expected to be linked with these deps.
   893      linkopts: A list of linker options,
   894        These flags are passed to the linker when the generated codes
   895        are linked into the target binary.
   896    """
   897    outdir = name + ".dir"
   898    outgen = outdir + "/gen"
   899  
   900    go_thunks = []
   901    c_thunks = []
   902    for s in srcs:
   903      if not s.endswith('.go'):
   904        fail("not a .go file: %s" % s)
   905      basename = s[:-3]
   906      if basename.rfind("/") >= 0:
   907        basename = basename[basename.rfind("/")+1:]
   908      go_thunks.append(outgen + "/" + basename + ".go")
   909      c_thunks.append(outgen + "/" + basename + ".c")
   910  
   911    outs = struct(
   912        name = name,
   913  
   914        outdir = outgen,
   915        go_thunks = go_thunks,
   916        c_thunks = c_thunks,
   917        c_exports = [
   918            outgen + "/_cgo_export.c",
   919            outgen + "/_cgo_export.h",
   920        ],
   921        c_dummy = outgen + "/_cgo_main.c",
   922        gotypes = outgen + "/_cgo_gotypes.go",
   923    )
   924  
   925    _cgo_codegen_rule(
   926        name = name,
   927        srcs = srcs,
   928        c_hdrs = c_hdrs,
   929        deps = deps,
   930        copts = copts,
   931        linkopts = linkopts,
   932  
   933        go_tool = go_tool,
   934        toolchain = toolchain,
   935  
   936        outdir = outdir,
   937        outs = outs.go_thunks + outs.c_thunks + outs.c_exports + [
   938            outs.c_dummy, outs.gotypes,
   939        ],
   940  
   941        visibility = ["//visibility:private"],
   942    )
   943    return outs
   944  
   945  def _cgo_import_impl(ctx):
   946    cmds = [
   947        (ctx.file.go_tool.path + " tool cgo" +
   948         " -dynout " + ctx.outputs.out.path +
   949         " -dynimport " + ctx.file.cgo_o.path +
   950         " -dynpackage $(%s %s)"  % (ctx.executable._extract_package.path,
   951                                     ctx.file.sample_go_src.path)),
   952    ]
   953    f = _emit_generate_params_action(cmds, ctx, ctx.outputs.out.path + ".CGoImportGenFile.params")
   954    ctx.action(
   955        inputs = (ctx.files.toolchain +
   956                  [f, ctx.file.go_tool, ctx.executable._extract_package,
   957                   ctx.file.cgo_o, ctx.file.sample_go_src]),
   958        outputs = [ctx.outputs.out],
   959        command = f.path,
   960        mnemonic = "CGoImportGen",
   961        env = go_environment_vars(ctx),
   962    )
   963    return struct(
   964        files = depset([ctx.outputs.out]),
   965    )
   966  
   967  _cgo_import = rule(
   968      _cgo_import_impl,
   969      attrs = go_env_attrs + {
   970          "cgo_o": attr.label(
   971              allow_files = True,
   972              single_file = True,
   973          ),
   974          "sample_go_src": attr.label(
   975              allow_files = True,
   976              single_file = True,
   977          ),
   978          "out": attr.output(
   979              mandatory = True,
   980          ),
   981          "_extract_package": attr.label(
   982              default = Label("//go/tools/extract_package"),
   983              executable = True,
   984              cfg = "host",
   985          ),
   986      },
   987      fragments = ["cpp"],
   988  )
   989  
   990  def _cgo_genrule_impl(ctx):
   991    return struct(
   992      label = ctx.label,
   993      go_sources = ctx.files.srcs,
   994      asm_sources = [],
   995      asm_headers = [],
   996      cgo_object = ctx.attr.cgo_object,
   997      direct_deps = ctx.attr.deps,
   998      gc_goopts = [],
   999    )
  1000  
  1001  _cgo_genrule = rule(
  1002      _cgo_genrule_impl,
  1003      attrs = {
  1004          "srcs": attr.label_list(allow_files = FileType([".go"])),
  1005          "cgo_object": attr.label(
  1006              providers = [
  1007                  "cgo_obj",
  1008                  "cgo_deps",
  1009              ],
  1010          ),
  1011          "deps": attr.label_list(
  1012              providers = [
  1013                  "direct_deps",
  1014                  "transitive_go_library_paths",
  1015                  "transitive_go_libraries",
  1016                  "transitive_cgo_deps",
  1017              ],
  1018          ),
  1019      },
  1020      fragments = ["cpp"],
  1021  )
  1022  
  1023  """Generates symbol-import directives for cgo
  1024  
  1025  Args:
  1026    cgo_o: The loadable object to extract dynamic symbols from.
  1027    sample_go_src: A go source which is compiled together with the generated file.
  1028      The generated file will have the same Go package name as this file.
  1029    out: Destination of the generated codes.
  1030  """
  1031  
  1032  def _cgo_object_impl(ctx):
  1033    arguments = _c_linker_options(ctx, blacklist=[
  1034        # never link any dependency libraries
  1035        "-l", "-L",
  1036        # manage flags to ld(1) by ourselves
  1037        "-Wl,"])
  1038    arguments += [
  1039        "-o", ctx.outputs.out.path,
  1040        "-nostdlib",
  1041        "-Wl,-r",
  1042    ]
  1043    if _is_darwin_cpu(ctx):
  1044      arguments += ["-shared", "-Wl,-all_load"]
  1045    else:
  1046      arguments += ["-Wl,-whole-archive"]
  1047  
  1048    lo = ctx.files.src[-1]
  1049    arguments += [lo.path]
  1050  
  1051    ctx.action(
  1052        inputs = [lo] + ctx.files._crosstool,
  1053        outputs = [ctx.outputs.out],
  1054        mnemonic = "CGoObject",
  1055        progress_message = "Linking %s" % ctx.outputs.out.short_path,
  1056        executable = ctx.fragments.cpp.compiler_executable,
  1057        arguments = arguments,
  1058    )
  1059    runfiles = ctx.runfiles(collect_data = True)
  1060    runfiles = runfiles.merge(ctx.attr.src.data_runfiles)
  1061    return struct(
  1062        files = depset([ctx.outputs.out]),
  1063        cgo_obj = ctx.outputs.out,
  1064        cgo_deps = ctx.attr.cgogen.cgo_deps,
  1065        runfiles = runfiles,
  1066    )
  1067  
  1068  _cgo_object = rule(
  1069      _cgo_object_impl,
  1070      attrs = _crosstool_attrs + {
  1071          "src": attr.label(
  1072              mandatory = True,
  1073              providers = ["cc"],
  1074          ),
  1075          "cgogen": attr.label(
  1076              mandatory = True,
  1077              providers = ["cgo_deps"],
  1078          ),
  1079          "out": attr.output(
  1080              mandatory = True,
  1081          ),
  1082      },
  1083      fragments = ["cpp"],
  1084  )
  1085  
  1086  """Generates _all.o to be archived together with Go objects.
  1087  
  1088  Args:
  1089    src: source static library which contains objects
  1090    cgogen: _cgo_codegen rule which knows the dependency cc_library() rules
  1091      to be linked together with src when we generate the final go binary.
  1092  """
  1093  
  1094  def _setup_cgo_library(name, srcs, cdeps, copts, clinkopts, go_tool, toolchain):
  1095    go_srcs = [s for s in srcs if s.endswith('.go')]
  1096    c_hdrs = [s for s in srcs if any([s.endswith(ext) for ext in hdr_exts])]
  1097    c_srcs = [s for s in srcs if not s in (go_srcs + c_hdrs)]
  1098  
  1099    # Split cgo files into .go parts and .c parts (plus some other files).
  1100    cgogen = _cgo_codegen(
  1101        name = name + ".cgo",
  1102        srcs = go_srcs,
  1103        c_hdrs = c_hdrs,
  1104        deps = cdeps,
  1105        copts = copts,
  1106        linkopts = clinkopts,
  1107        go_tool = go_tool,
  1108        toolchain = toolchain,
  1109    )
  1110  
  1111    # Filter c_srcs with build constraints.
  1112    c_filtered_srcs = []
  1113    if len(c_srcs) > 0:
  1114      c_filtered_srcs_name = name + "_filter_cgo_srcs"
  1115      _cgo_filter_srcs(
  1116          name = c_filtered_srcs_name,
  1117          srcs = c_srcs,
  1118      )
  1119      c_filtered_srcs.append(":" + c_filtered_srcs_name)
  1120  
  1121    pkg_dir = _pkg_dir(
  1122        "external/" + REPOSITORY_NAME[1:] if len(REPOSITORY_NAME) > 1 else "",
  1123        PACKAGE_NAME)
  1124  
  1125    # Platform-specific settings
  1126    native.config_setting(
  1127        name = name + "_windows_setting",
  1128        values = {
  1129            "cpu": "x64_windows_msvc",
  1130        },
  1131    )
  1132    platform_copts = select({
  1133        ":" + name + "_windows_setting": ["-mthreads"],
  1134        "//conditions:default": ["-pthread"],
  1135    })
  1136    platform_linkopts = select({
  1137        ":" + name + "_windows_setting": ["-mthreads"],
  1138        "//conditions:default": ["-pthread"],
  1139    })
  1140  
  1141    # Bundles objects into an archive so that _cgo_.o and _all.o can share them.
  1142    native.cc_library(
  1143        name = cgogen.outdir + "/_cgo_lib",
  1144        srcs = cgogen.c_thunks + cgogen.c_exports + c_filtered_srcs + c_hdrs,
  1145        deps = cdeps,
  1146        copts = copts + platform_copts + [
  1147            "-I", pkg_dir,
  1148            "-I", "$(GENDIR)/" + pkg_dir + "/" + cgogen.outdir,
  1149            # The generated thunks often contain unused variables.
  1150            "-Wno-unused-variable",
  1151        ],
  1152        linkopts = clinkopts + platform_linkopts,
  1153        linkstatic = 1,
  1154        # _cgo_.o and _all.o keep all objects in this archive.
  1155        # But it should not be very annoying in the final binary target
  1156        # because _cgo_object rule does not propagate alwayslink=1
  1157        alwayslink = 1,
  1158        visibility = ["//visibility:private"],
  1159    )
  1160  
  1161    # Loadable object which cgo reads when it generates _cgo_import.go
  1162    native.cc_binary(
  1163        name = cgogen.outdir + "/_cgo_.o",
  1164        srcs = [cgogen.c_dummy],
  1165        deps = cdeps + [cgogen.outdir + "/_cgo_lib"],
  1166        copts = copts,
  1167        linkopts = clinkopts,
  1168        visibility = ["//visibility:private"],
  1169    )
  1170    _cgo_import(
  1171        name = "%s.cgo.importgen" % name,
  1172        cgo_o = cgogen.outdir + "/_cgo_.o",
  1173        out = cgogen.outdir + "/_cgo_import.go",
  1174        sample_go_src = go_srcs[0],
  1175        go_tool = go_tool,
  1176        toolchain = toolchain,
  1177        visibility = ["//visibility:private"],
  1178    )
  1179  
  1180    _cgo_object(
  1181        name = cgogen.outdir + "/_cgo_object",
  1182        src = cgogen.outdir + "/_cgo_lib",
  1183        out = cgogen.outdir + "/_all.o",
  1184        cgogen = cgogen.name,
  1185        visibility = ["//visibility:private"],
  1186    )
  1187    return cgogen
  1188  
  1189  def cgo_genrule(name, srcs,
  1190                  copts=[],
  1191                  clinkopts=[],
  1192                  cdeps=[],
  1193                  **kwargs):
  1194    cgogen = _setup_cgo_library(
  1195        name = name,
  1196        srcs = srcs,
  1197        cdeps = cdeps,
  1198        copts = copts,
  1199        clinkopts = clinkopts,
  1200        toolchain = None,
  1201        go_tool = None,
  1202    )
  1203    _cgo_genrule(
  1204        name = name,
  1205        srcs = cgogen.go_thunks + [
  1206            cgogen.gotypes,
  1207            cgogen.outdir + "/_cgo_import.go",
  1208        ],
  1209        cgo_object = cgogen.outdir + "/_cgo_object",
  1210        **kwargs
  1211    )
  1212  
  1213  def cgo_library(name, srcs,
  1214                  toolchain=None,
  1215                  go_tool=None,
  1216                  copts=[],
  1217                  clinkopts=[],
  1218                  cdeps=[],
  1219                  **kwargs):
  1220    """Builds a cgo-enabled go library.
  1221  
  1222    Args:
  1223      name: A unique name for this rule.
  1224      srcs: List of Go, C and C++ files that are processed to build a Go library.
  1225        Those Go files must contain `import "C"`.
  1226        C and C++ files can be anything allowed in `srcs` attribute of
  1227        `cc_library`.
  1228      copts: Add these flags to the C++ compiler.
  1229      clinkopts: Add these flags to the C++ linker.
  1230      cdeps: List of C/C++ libraries to be linked into the binary target.
  1231        They must be `cc_library` rules.
  1232      deps: List of other libraries to be linked to this library target.
  1233      data: List of files needed by this rule at runtime.
  1234  
  1235    NOTE:
  1236      `srcs` cannot contain pure-Go files, which do not have `import "C"`.
  1237      So you need to define another `go_library` when you build a go package with
  1238      both cgo-enabled and pure-Go sources.
  1239  
  1240      ```
  1241      cgo_library(
  1242          name = "cgo_enabled",
  1243          srcs = ["cgo-enabled.go", "foo.cc", "bar.S", "baz.a"],
  1244      )
  1245  
  1246      go_library(
  1247          name = "go_default_library",
  1248          srcs = ["pure-go.go"],
  1249          library = ":cgo_enabled",
  1250      )
  1251      ```
  1252    """
  1253    cgogen = _setup_cgo_library(
  1254        name = name,
  1255        srcs = srcs,
  1256        cdeps = cdeps,
  1257        copts = copts,
  1258        clinkopts = clinkopts,
  1259        go_tool = go_tool,
  1260        toolchain = toolchain,
  1261    )
  1262  
  1263    go_library(
  1264        name = name,
  1265        srcs = cgogen.go_thunks + [
  1266            cgogen.gotypes,
  1267            cgogen.outdir + "/_cgo_import.go",
  1268        ],
  1269        cgo_object = cgogen.outdir + "/_cgo_object",
  1270        go_tool = go_tool,
  1271        toolchain = toolchain,
  1272        **kwargs
  1273    )