blob: 7631f310920955200c2f89de111e5a67c6fdae10 [file] [log] [blame]
# Copyright 2014 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
load(
"@io_bazel_rules_go//go/private:skylib/lib/sets.bzl",
"sets",
)
load(
"@io_bazel_rules_go//go/private:context.bzl",
"go_context",
)
load(
"@io_bazel_rules_go//go/private:common.bzl",
"as_iterable",
"as_list",
"as_set",
"has_simple_shared_lib_extension",
"has_versioned_shared_lib_extension",
"join_srcs",
"pkg_dir",
"split_srcs",
)
load(
"@io_bazel_rules_go//go/private:providers.bzl",
"GoLibrary",
)
load(
"@io_bazel_rules_go//go/private:rules/rule.bzl",
"go_rule",
)
load(
"@io_bazel_rules_go//go/private:mode.bzl",
"LINKMODE_C_ARCHIVE",
"LINKMODE_C_SHARED",
"LINKMODE_NORMAL",
"LINKMODE_PIE",
"extldflags_from_cc_toolchain",
"mode_string",
"new_mode",
)
load(
"@io_bazel_rules_go//go/platform:list.bzl",
"GOARCH",
"GOOS",
"GOOS_GOARCH",
"MSAN_GOOS_GOARCH",
"RACE_GOOS_GOARCH",
)
load(
"@io_bazel_rules_go_compat//:compat.bzl",
"CC_PROVIDER_NAME",
"cc_compile_flags",
"cc_defines",
"cc_includes",
"cc_libs",
"cc_link_flags",
"cc_quote_includes",
"cc_system_includes",
"cc_transitive_headers",
"has_cc",
)
def cgo_configure(go, srcs, cdeps, cppopts, copts, cxxopts, clinkopts):
"""cgo_configure returns the inputs and compile / link options
that are required to build a cgo archive.
Args:
go: a GoContext.
srcs: list of source files being compiled. Include options are added
for the headers.
cdeps: list of Targets for C++ dependencies. Include and link options
may be added.
cppopts: list of C preprocessor options for the library.
copts: list of C compiler options for the library.
cxxopts: list of C++ compiler options for the library.
clinkopts: list of linker options for the library.
Returns: a struct containing:
inputs: depset of files that must be available for the build.
deps: depset of files for dynamic libraries.
runfiles: runfiles object for the C/C++ dependencies.
cppopts: complete list of preprocessor options
copts: complete list of C compiler options.
cxxopts: complete list of C++ compiler options.
clinkopts: complete list of linker options.
"""
if not go.cgo_tools:
fail("Go toolchain does not support cgo")
cppopts = list(cppopts)
base_dir, _, _ = go._ctx.build_file_path.rpartition("/")
if base_dir:
cppopts.extend(["-I", base_dir])
copts = go.cgo_tools.c_compile_options + copts
cxxopts = go.cgo_tools.cxx_compile_options + cxxopts
clinkopts = extldflags_from_cc_toolchain(go) + clinkopts
if go.mode != LINKMODE_NORMAL:
if "-fPIC" not in copts:
copts.append("-fPIC")
if "-fPIC" not in cxxopts:
cxxopts.append("-fPIC")
seen_includes = {}
seen_quote_includes = {}
seen_system_includes = {}
for f in srcs:
if f.basename.endswith(".h"):
_include_unique(cppopts, "-iquote", f.dirname, seen_quote_includes)
inputs_direct = []
inputs_transitive = []
deps_direct = []
lib_opts = []
runfiles = go._ctx.runfiles(collect_data = True)
for d in cdeps:
runfiles = runfiles.merge(d.data_runfiles)
if not has_cc(d):
fail("library has no cc provider: %s" % d.label)
inputs_transitive.append(cc_transitive_headers(d))
inputs_direct.extend(cc_libs(d))
deps_direct.extend(cc_libs(d))
cppopts.extend(["-D" + define for define in cc_defines(d)])
for inc in cc_includes(d):
_include_unique(cppopts, "-I", inc, seen_includes)
for inc in cc_quote_includes(d):
_include_unique(cppopts, "-iquote", inc, seen_quote_includes)
for inc in cc_system_includes(d):
_include_unique(cppopts, "-isystem", inc, seen_system_includes)
for lib in cc_libs(d):
# If both static and dynamic variants are available, Bazel will only give
# us the static variant. We'll get one file for each transitive dependency,
# so the same file may appear more than once.
if (lib.basename.startswith("lib") and
has_simple_shared_lib_extension(lib.basename)):
# If the loader would be able to find the library using rpaths,
# use -L and -l instead of hard coding the path to the library in
# the binary. This gives users more flexibility. The linker will add
# rpaths later. We can't add them here because they are relative to
# the binary location, and we don't know where that is.
libname = lib.basename[len("lib"):lib.basename.rindex(".")]
clinkopts.extend(["-L", lib.dirname, "-l", libname])
inputs_direct.append(lib)
elif (lib.basename.startswith("lib") and
has_versioned_shared_lib_extension(lib.basename)):
# With a versioned shared library, we must use the full filename,
# otherwise the library will not be found by the linker.
libname = ":%s" % lib.basename
clinkopts.extend(["-L", lib.dirname, "-l", libname])
inputs_direct.append(lib)
else:
lib_opts.append(lib.path)
clinkopts.extend(cc_link_flags(d))
inputs = depset(direct = inputs_direct, transitive = inputs_transitive)
deps = depset(direct = deps_direct)
# HACK: some C/C++ toolchains will ignore libraries (including dynamic libs
# specified with -l flags) unless they appear after .o or .a files with
# undefined symbols they provide. Put all the .a files from cdeps first,
# so that we actually link with -lstdc++ and others.
clinkopts = lib_opts + clinkopts
return struct(
inputs = inputs,
deps = deps,
runfiles = runfiles,
cppopts = cppopts,
copts = copts,
cxxopts = cxxopts,
clinkopts = clinkopts,
)
_CgoCodegen = provider()
_CgoInfo = provider()
# Maximum number of characters in stem of base name for mangled cgo files.
# Some file systems have fairly short limits (eCryptFS has a limit of 143),
# and this should be kept below those to accomodate number suffixes and
# extensions.
MAX_STEM_LENGTH = 130
def _mangle(src, stems):
"""_mangle returns a file stem and extension for a source file that will
be passed to cgo. The stem will be unique among other sources in the same
library. It will not contain any separators, so cgo's name mangling algorithm
will be a no-op."""
stem, _, ext = src.basename.rpartition(".")
if len(stem) > MAX_STEM_LENGTH:
stem = stem[:MAX_STEM_LENGTH]
if stem in stems:
for i in range(100):
next_stem = "{}_{}".format(stem, i)
if next_stem not in stems:
break
if next_stem in stems:
fail("could not find unique mangled name for {}".format(src.path))
stem = next_stem
stems[stem] = True
return stem, ext
_DEFAULT_PLATFORM_COPTS = select({
"@io_bazel_rules_go//go/platform:darwin": [],
"@io_bazel_rules_go//go/platform:windows_amd64": ["-mthreads"],
"//conditions:default": ["-pthread"],
})
_DEFAULT_PLATFORM_LINKOPTS = select({
"@io_bazel_rules_go//go/platform:android": ["-llog", "-ldl"],
"@io_bazel_rules_go//go/platform:darwin": [],
"@io_bazel_rules_go//go/platform:windows_amd64": ["-mthreads"],
"//conditions:default": ["-pthread"],
})
def _c_filter_options(options, blacklist):
return [
opt
for opt in options
if not any([opt.startswith(prefix) for prefix in blacklist])
]
def _select_archives(libs):
"""Selects static archives to pack from a list of cc_library targets.
Returns at most one file per library.
Each cc_library may produce several files which are logically the same
static library. We prefer files with the extensions .pic.lo, .lo, or .a
in that order. If a cc_library is empty, it may not produce any files,
so _select_archives may return fewer archives than libs.
"""
# list of file extensions in descending order or preference.
exts = [".pic.lo", ".lo", ".a"]
outs = []
for lib in libs:
out = None
for ext in exts:
for f in as_iterable(lib.files):
if f.basename.endswith(ext):
out = f
break
if out:
break
if out:
outs.append(out)
return outs
def _include_unique(opts, flag, include, seen):
if include in seen:
return
seen[include] = True
opts.extend([flag, include])
def _encode_cgo_mode(goos, goarch, race, msan):
return "_".join((goos, goarch, str(race), str(msan)))
def _cgo_codegen_impl(ctx):
go = go_context(ctx)
if not go.cgo_tools:
fail("Go toolchain does not support cgo")
linkopts = extldflags_from_cc_toolchain(go) + ctx.attr.linkopts
cppopts = list(ctx.attr.cppopts)
copts = go.cgo_tools.c_compile_options + ctx.attr.copts
deps = depset([], order = "topological")
cgo_export_h = go.declare_file(go, path = "_cgo_export.h")
cgo_export_c = go.declare_file(go, path = "_cgo_export.c")
cgo_main = go.declare_file(go, path = "_cgo_main.c")
cgo_types = go.declare_file(go, path = "_cgo_gotypes.go")
out_dir = cgo_main.dirname
builder_args = go.builder_args(go, "cgo") # interpreted by builder
tool_args = go.tool_args(go) # interpreted by cgo
cc_args = go.tool_args(go) # interpreted by C compiler
c_outs = [cgo_export_h, cgo_export_c]
cxx_outs = [cgo_export_h]
objc_outs = [cgo_export_h]
transformed_go_outs = []
transformed_go_map = {}
gen_go_outs = [cgo_types]
seen_includes = {}
seen_quote_includes = {}
seen_system_includes = {}
source = split_srcs(ctx.files.srcs)
for hdr in source.headers:
_include_unique(cppopts, "-iquote", hdr.dirname, seen_quote_includes)
stems = {}
for src in source.go:
mangled_stem, src_ext = _mangle(src, stems)
gen_go_file = go.declare_file(go, path = mangled_stem + ".cgo1." + src_ext)
gen_c_file = go.declare_file(go, path = mangled_stem + ".cgo2.c")
transformed_go_outs.append(gen_go_file)
transformed_go_map[gen_go_file] = src
c_outs.append(gen_c_file)
builder_args.add("-src", gen_go_file.path + "=" + src.path)
for src in source.asm:
mangled_stem, src_ext = _mangle(src, stems)
gen_file = go.declare_file(go, path = mangled_stem + ".cgo1." + src_ext)
transformed_go_outs.append(gen_file)
builder_args.add("-src", gen_file.path + "=" + src.path)
for src in source.c:
mangled_stem, src_ext = _mangle(src, stems)
gen_file = go.declare_file(go, path = mangled_stem + ".cgo1." + src_ext)
c_outs.append(gen_file)
builder_args.add("-src", gen_file.path + "=" + src.path)
for src in source.cxx:
mangled_stem, src_ext = _mangle(src, stems)
gen_file = go.declare_file(go, path = mangled_stem + ".cgo1." + src_ext)
cxx_outs.append(gen_file)
builder_args.add("-src", gen_file.path + "=" + src.path)
for src in source.objc:
mangled_stem, src_ext = _mangle(src, stems)
gen_file = go.declare_file(go, path = mangled_stem + ".cgo1." + src_ext)
objc_outs.append(gen_file)
builder_args.add("-src", gen_file.path + "=" + src.path)
# Filter out -lstdc++ in CGO_LDFLAGS if we don't have any C++ code. This
# also gets filtered out in link.bzl.
have_cc = len(source.cxx) + len(source.objc) + len(ctx.attr.deps) > 0
if not have_cc:
linkopts = [o for o in linkopts if o not in ("-lstdc++", "-lc++")]
tool_args.add("-objdir", out_dir)
inputs = sets.union(ctx.files.srcs, go.crosstool, go.sdk.tools)
deps = depset()
runfiles = ctx.runfiles(collect_data = True)
for d in ctx.attr.deps:
runfiles = runfiles.merge(d.data_runfiles)
if has_cc(d):
inputs = sets.union(inputs, cc_transitive_headers(d))
deps = sets.union(deps, cc_libs(d))
cppopts.extend(["-D" + define for define in cc_defines(d)])
for inc in cc_includes(d):
_include_unique(cppopts, "-I", inc, seen_includes)
for inc in cc_quote_includes(d):
_include_unique(cppopts, "-iquote", inc, seen_quote_includes)
for inc in cc_system_includes(d):
_include_unique(cppopts, "-isystem", inc, seen_system_includes)
for lib in cc_libs(d):
# If both static and dynamic variants are available, Bazel will only give
# us the static variant. We'll get one file for each transitive dependency,
# so the same file may appear more than once.
if (lib.basename.startswith("lib") and
has_simple_shared_lib_extension(lib.basename)):
# If the loader would be able to find the library using rpaths,
# use -L and -l instead of hard coding the path to the library in
# the binary. This gives users more flexibility. The linker will add
# rpaths later. We can't add them here because they are relative to
# the binary location, and we don't know where that is.
libname = lib.basename[len("lib"):lib.basename.rindex(".")]
linkopts.extend(["-L", lib.dirname, "-l", libname])
elif (lib.basename.startswith("lib") and
has_versioned_shared_lib_extension(lib.basename)):
# With a versioned shared library, we must use the full filename,
# otherwise the library will not be found by the linker.
libname = ":%s" % lib.basename
linkopts.extend(["-L", lib.dirname, "-l", libname])
else:
linkopts.append(lib.path)
linkopts.extend(cc_link_flags(d))
elif hasattr(d, "objc"):
cppopts.extend(["-D" + define for define in d.objc.define.to_list()])
for inc in d.objc.include.to_list():
_include_unique(cppopts, "-I", inc, seen_includes)
for inc in d.objc.iquote.to_list():
_include_unique(cppopts, "-iquote", inc, seen_quote_includes)
for inc in d.objc.include_system.to_list():
_include_unique(cppopts, "-isystem", inc, seen_system_includes)
# TODO(jayconrod): do we need to link against dynamic libraries or
# frameworks? We link against *_fully_linked.a, so maybe not?
else:
fail("unknown library has neither cc nor objc providers: %s" % d.label)
# cgo writes CGO_LDFLAGS to _cgo_import.go in the form of pragmas which get
# compiled into .a files. The linker finds these and passes them to the
# external linker.
# TODO(jayconrod): do we need to set this here, or only in _cgo_import?
# go build does it here.
env = go.env
env["CC"] = go.cgo_tools.c_compiler_path
env["CGO_LDFLAGS"] = " ".join(linkopts)
cc_args.add_all(cppopts)
cc_args.add_all(copts)
ctx.actions.run(
inputs = inputs,
outputs = c_outs + cxx_outs + objc_outs + gen_go_outs + transformed_go_outs + [cgo_main],
mnemonic = "CGoCodeGen",
progress_message = "CGoCodeGen %s" % ctx.label,
executable = go.toolchain._builder,
arguments = [builder_args, "--", tool_args, "--", cc_args],
env = env,
)
return [
_CgoCodegen(
transformed_go = transformed_go_outs,
transformed_go_map = transformed_go_map,
gen_go = gen_go_outs,
deps = as_list(deps),
exports = [cgo_export_h],
),
DefaultInfo(
files = depset(),
runfiles = runfiles,
),
OutputGroupInfo(
c_files = sets.union(c_outs, source.headers),
cxx_files = sets.union(cxx_outs, source.headers),
objc_files = sets.union(objc_outs, source.headers),
go_files = sets.union(transformed_go_outs, gen_go_outs),
main_c = as_set([cgo_main]),
),
]
_cgo_codegen = go_rule(
_cgo_codegen_impl,
attrs = {
"srcs": attr.label_list(allow_files = True),
"deps": attr.label_list(allow_files = False),
"copts": attr.string_list(),
"cxxopts": attr.string_list(),
"cppopts": attr.string_list(),
"linkopts": attr.string_list(),
# Attributes below are read into go.mode. They determine build tags
# which are used to filter sources. We need to set these explicitly,
# since the aspect won't reach this rule.
"goos": attr.string(
mandatory = True,
values = GOOS.keys(),
),
"goarch": attr.string(
mandatory = True,
values = GOARCH.keys(),
),
"race": attr.string(
values = ["on", "off"],
default = "off",
),
"msan": attr.string(
values = ["on", "off"],
default = "off",
),
"pure": attr.string(default = "off"), # never explicitly set
},
)
def _cgo_import_impl(ctx):
go = go_context(ctx)
out = go.declare_file(go, path = "_cgo_import.go")
args = go.builder_args(go, "cgo")
args.add("-import")
args.add("-src", ctx.files.sample_go_srcs[0])
args.add("--") # stop builder from processing args
args.add("-dynout", out)
args.add("-dynimport", ctx.file.cgo_o)
ctx.actions.run(
inputs = [
ctx.file.cgo_o,
ctx.files.sample_go_srcs[0],
] + go.sdk.tools,
outputs = [out],
executable = go.toolchain._builder,
arguments = [args],
mnemonic = "CGoImportGen",
)
return [DefaultInfo(files = depset([out]))]
_cgo_import = go_rule(
_cgo_import_impl,
attrs = {
"cgo_o": attr.label(
allow_single_file = True,
),
"sample_go_srcs": attr.label_list(allow_files = True),
},
)
"""Generates symbol-import directives for cgo
Args:
cgo_o: The loadable object to extract dynamic symbols from.
sample_go_src: A go source which is compiled together with the generated file.
The generated file will have the same Go package name as this file.
out: Destination of the generated codes.
"""
def _cgo_resolve_source(go, attr, source, merge):
library = source["library"]
cgo_mode = _encode_cgo_mode(go.mode.goos, go.mode.goarch, go.mode.race, go.mode.msan)
cgo_info = None
for target, target_cgo_mode in library.cgo_mode_info.items():
if cgo_mode == target_cgo_mode:
cgo_info = target[_CgoInfo]
break
if not cgo_info:
fail("{}: no matching cgo rules for mode {}".format(library.label, mode_string(go.mode)))
source["orig_srcs"] = cgo_info.orig_srcs
source["orig_src_map"] = cgo_info.transformed_go_map
source["runfiles"] = cgo_info.runfiles
source["cover"] = []
if source["mode"].pure:
split = split_srcs(cgo_info.orig_srcs)
source["srcs"] = split.go + split.asm
if go.coverage_instrumented:
source["cover"] = source["srcs"]
else:
source["srcs"] = cgo_info.transformed_go_srcs + cgo_info.gen_go_srcs
if go.coverage_instrumented:
source["cover"] = cgo_info.transformed_go_srcs
source["cgo_deps"] = cgo_info.cgo_deps
source["cgo_exports"] = cgo_info.cgo_exports
source["cgo_archives"] = cgo_info.cgo_archives
def _cgo_collect_info_impl(ctx):
go = go_context(ctx)
codegen = ctx.attr.codegen[_CgoCodegen]
import_files = as_list(ctx.files.cgo_import)
runfiles = ctx.runfiles(collect_data = True)
runfiles = runfiles.merge(ctx.attr.codegen.data_runfiles)
return [
_CgoInfo(
orig_srcs = ctx.files.srcs,
transformed_go_srcs = codegen.transformed_go,
transformed_go_map = codegen.transformed_go_map,
gen_go_srcs = codegen.gen_go + import_files,
cgo_deps = codegen.deps,
cgo_exports = codegen.exports,
cgo_archives = _select_archives(ctx.attr.libs),
runfiles = runfiles,
),
DefaultInfo(files = depset(), runfiles = runfiles),
]
_cgo_collect_info = go_rule(
_cgo_collect_info_impl,
attrs = {
"srcs": attr.label_list(
mandatory = True,
allow_files = True,
),
"codegen": attr.label(
mandatory = True,
providers = [_CgoCodegen],
),
"libs": attr.label_list(
mandatory = True,
allow_files = True,
providers = [CC_PROVIDER_NAME],
),
"cgo_import": attr.label(mandatory = True),
},
)
"""No-op rule that collects mode-specific information from _cgo_codegen and
cc_library info into a _CgoInfo provider for easy consumption."""
def _cgo_select_embed_impl(ctx):
go = go_context(ctx)
library = go.new_library(
go,
resolver = _cgo_resolve_source,
cgo_mode_info = ctx.attr.info,
)
source = go.library_to_source(go, ctx.attr, library, ctx.coverage_instrumented())
return [
library,
source,
DefaultInfo(files = depset(), runfiles = source.runfiles),
]
_cgo_select_embed = go_rule(
_cgo_select_embed_impl,
attrs = {
"info": attr.label_keyed_string_dict(
mandatory = True,
providers = [_CgoInfo],
),
},
)
"""No-op rule that collects information about cgo rules in all supported
modes, then builds GoLibrary and GoSource providers for the current mode."""
def setup_cgo_library(name, srcs, cdeps, copts, cxxopts, cppopts, clinkopts, objc, objcopts, linkmode, **common_attrs):
"""Declares a graph of rules needed to build the cgo part of a go_library.
The graph is collected into a single rule which may be embedded in a
regular go_library.
We build C/C++/ObjC code using cc_library / objc_library rules. We'd prefer
to create our own compile / link actions within a single rule, but we'd
almost certainly break something.
"""
# Add some implicit flags for the current rule.
repo_name = native.repository_name()
base_dir = pkg_dir(
"external/" + repo_name[1:] if len(repo_name) > 1 else "",
native.package_name(),
)
copts = copts
cxxopts = cxxopts
cppopts = cppopts + ["-I", base_dir]
if objc:
clinkopts = clinkopts + [
"-fobjc-link-runtime",
]
for framework in objcopts.get("sdk_frameworks", []):
clinkopts.append("-framework %s" % framework)
if linkmode == LINKMODE_PIE and "-pie" not in clinkopts:
clinkopts = clinkopts + ["-pie"]
# Declare cgo rules for each platform and race / msan configuration. We
# normally propagate mode attributes through an aspect, but we can't create
# alternate cc_library rules with an aspect.
cgo_mode_info = {}
for goos, goarch in GOOS_GOARCH:
cgo_info_name = setup_cgo_library_for_mode(name, srcs, cdeps, copts, cxxopts, cppopts, clinkopts, objc, objcopts, goos, goarch, race = False, msan = False, **common_attrs)
cgo_mode_info[cgo_info_name] = _encode_cgo_mode(goos, goarch, race = False, msan = False)
for goos, goarch in RACE_GOOS_GOARCH:
cgo_info_name = setup_cgo_library_for_mode(name, srcs, cdeps, copts, cxxopts, cppopts, clinkopts, objc, objcopts, goos, goarch, race = True, msan = False, **common_attrs)
cgo_mode_info[cgo_info_name] = _encode_cgo_mode(goos, goarch, race = True, msan = False)
for goos, goarch in MSAN_GOOS_GOARCH:
cgo_info_name = setup_cgo_library_for_mode(name, srcs, cdeps, copts, cxxopts, cppopts, clinkopts, objc, objcopts, goos, goarch, race = False, msan = True, **common_attrs)
cgo_mode_info[cgo_info_name] = _encode_cgo_mode(goos, goarch, race = False, msan = True)
# Collect everything in a single embedable, aspect-friendly library.
cgo_embed_name = name + "__cgo_embed"
_cgo_select_embed(
name = cgo_embed_name,
info = cgo_mode_info,
visibility = ["//visibility:private"],
**common_attrs
)
return cgo_embed_name
def setup_cgo_library_for_mode(name, srcs, cdeps, copts, cxxopts, cppopts, clinkopts, objc, objcopts, goos, goarch, race, msan, **common_attrs):
mode = new_mode(
goos = goos,
goarch = goarch,
race = race,
msan = msan,
)
prefix = "{}%{}%".format(name, mode_string(mode))
cgo_codegen_name = prefix + "cgo_codegen"
_cgo_codegen(
name = cgo_codegen_name,
srcs = srcs,
deps = cdeps,
copts = copts,
cxxopts = cxxopts,
cppopts = cppopts,
linkopts = clinkopts,
goos = goos,
goarch = goarch,
race = "on" if race else "off",
msan = "on" if msan else "off",
visibility = ["//visibility:private"],
**common_attrs
)
select_go_files = prefix + "select_go_files"
native.filegroup(
name = select_go_files,
srcs = [cgo_codegen_name],
output_group = "go_files",
visibility = ["//visibility:private"],
**common_attrs
)
select_c_files = prefix + "select_c_files"
native.filegroup(
name = select_c_files,
srcs = [cgo_codegen_name],
output_group = "c_files",
visibility = ["//visibility:private"],
**common_attrs
)
select_cxx_files = prefix + "select_cxx_files"
native.filegroup(
name = select_cxx_files,
srcs = [cgo_codegen_name],
output_group = "cxx_files",
visibility = ["//visibility:private"],
**common_attrs
)
select_objc_files = prefix + "select_objc_files"
native.filegroup(
name = select_objc_files,
srcs = [cgo_codegen_name],
output_group = "objc_files",
visibility = ["//visibility:private"],
**common_attrs
)
select_main_c = prefix + "select_main_c"
native.filegroup(
name = select_main_c,
srcs = [cgo_codegen_name],
output_group = "main_c",
visibility = ["//visibility:private"],
**common_attrs
)
# Compile C sources and generated files into a library. This will be linked
# into binaries that depend on this cgo_library. It will also be used
# in _cgo_.o.
cgo_c_lib_name = prefix + "cgo_c_lib"
native.cc_library(
name = cgo_c_lib_name,
srcs = [select_c_files],
deps = cdeps,
copts = copts + cppopts + _DEFAULT_PLATFORM_COPTS + [
# The generated thunks often contain unused variables.
"-Wno-unused-variable",
],
linkopts = clinkopts + _DEFAULT_PLATFORM_LINKOPTS,
linkstatic = 1,
# _cgo_.o needs all symbols because _cgo_import needs to see them.
alwayslink = 1,
visibility = ["//visibility:private"],
**common_attrs
)
cgo_cxx_lib_name = prefix + "cgo_cxx_lib"
native.cc_library(
name = cgo_cxx_lib_name,
srcs = [select_cxx_files],
deps = cdeps,
copts = cxxopts + cppopts + _DEFAULT_PLATFORM_COPTS + [
# The generated thunks often contain unused variables.
"-Wno-unused-variable",
],
linkopts = clinkopts + _DEFAULT_PLATFORM_LINKOPTS,
linkstatic = 1,
# _cgo_.o needs all symbols because _cgo_import needs to see them.
alwayslink = 1,
visibility = ["//visibility:private"],
**common_attrs
)
cgo_o_deps = [
cgo_c_lib_name,
cgo_cxx_lib_name,
]
cgo_collect_info_libs = cgo_o_deps[:]
if objc:
cgo_objc_lib_name = prefix + "cgo_objc_lib"
objcopts.update(common_attrs)
native.objc_library(
name = cgo_objc_lib_name,
srcs = [select_objc_files],
deps = cdeps,
copts = copts + cppopts + _DEFAULT_PLATFORM_COPTS + [
# The generated thunks often contain unused variables.
"-Wno-unused-variable",
],
# _cgo_.o needs all symbols because _cgo_import needs to see them.
alwayslink = 1,
visibility = ["//visibility:private"],
**objcopts
)
cgo_o_deps.append(cgo_objc_lib_name)
# cgo needs all the symbols it can find when generating _cgo_import.go. For
# cc_library we use linkstatic = 1. This option does not exist on
# objc_library. To work around that, we used the implicit and documented
# target that objc_library provides, which includes the fully transitive
# dependencies.
# See https://docs.bazel.build/versions/master/be/objective-c.html#objc_library
# for more information.
cgo_collect_info_libs.append(cgo_objc_lib_name + "_fully_linked.a")
# Create a loadable object with no undefined references. cgo reads this
# when it generates _cgo_import.go.
cgo_o_name = prefix + "_cgo_.o"
native.cc_binary(
name = cgo_o_name,
srcs = [select_main_c],
deps = cdeps + cgo_o_deps,
copts = copts + cppopts,
linkopts = clinkopts,
visibility = ["//visibility:private"],
**common_attrs
)
# Create a Go file which imports symbols from the C library.
cgo_import_name = prefix + "cgo_import"
_cgo_import(
name = cgo_import_name,
cgo_o = cgo_o_name,
sample_go_srcs = [select_go_files],
visibility = ["//visibility:private"],
**common_attrs
)
cgo_info_name = prefix + "cgo_info"
_cgo_collect_info(
name = cgo_info_name,
srcs = srcs,
cgo_import = cgo_import_name,
codegen = cgo_codegen_name,
libs = cgo_collect_info_libs,
visibility = ["//visibility:private"],
**common_attrs
)
return cgo_info_name
# Sets up the cc_ targets when a go_binary is built in either c-archive or
# c-shared mode.
def go_binary_c_archive_shared(name, kwargs):
linkmode = kwargs.get("linkmode")
if linkmode not in [LINKMODE_C_SHARED, LINKMODE_C_ARCHIVE]:
return
cgo_exports = name + ".cgo_exports"
c_hdrs = name + ".c_hdrs"
cc_import_name = name + ".cc_import"
cc_library_name = name + ".cc"
tags = kwargs.get("tags", ["manual"])
if "manual" not in tags:
# These archives can't be built on all platforms, so use "manual" tags.
tags.append("manual")
native.filegroup(
name = cgo_exports,
srcs = [name],
output_group = "cgo_exports",
visibility = ["//visibility:private"],
tags = tags,
)
native.genrule(
name = c_hdrs,
srcs = [cgo_exports],
outs = ["%s.h" % name],
cmd = "cat $(SRCS) > $(@)",
visibility = ["//visibility:private"],
tags = tags,
)
cc_import_kwargs = {}
if linkmode == LINKMODE_C_SHARED:
cc_import_kwargs["shared_library"] = name
elif linkmode == LINKMODE_C_ARCHIVE:
cc_import_kwargs["static_library"] = name
cc_import_kwargs["alwayslink"] = 1
native.cc_import(
name = cc_import_name,
visibility = ["//visibility:private"],
tags = tags,
**cc_import_kwargs
)
native.cc_library(
name = cc_library_name,
hdrs = [c_hdrs],
deps = [cc_import_name],
alwayslink = 1,
linkstatic = (linkmode == LINKMODE_C_ARCHIVE and 1 or 0),
copts = _DEFAULT_PLATFORM_COPTS,
linkopts = _DEFAULT_PLATFORM_COPTS,
visibility = ["//visibility:public"],
tags = tags,
)