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