...

Text file src/go.starlark.net/syntax/testdata/scan.star

Documentation: go.starlark.net/syntax/testdata

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

View as plain text