blob: 5e957137aca0c9c34a72172ecb3f3bc9f7587f4c [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:context.bzl",
"go_context",
)
load("@io_bazel_rules_go//go/private:common.bzl",
"split_srcs",
"join_srcs",
"pkg_dir",
"sets",
"to_set",
)
load("@io_bazel_rules_go//go/private:providers.bzl",
"GoLibrary",
)
_CgoCodegen = provider()
def _mangle(src):
src_stem, _, src_ext = src.path.rpartition('.')
mangled_stem = src_stem.replace('/', '_')
return mangled_stem, src_ext
def _c_filter_options(options, blacklist):
return [opt for opt in options
if not any([opt.startswith(prefix) for prefix in blacklist])]
def _select_archive(files):
"""Selects a single archive from a list of files produced by a
static cc_library.
In some configurations, cc_library can produce multiple files, and the
order isn't guaranteed, so we can't simply pick the first one.
"""
# list of file extensions in descending order or preference.
exts = [".pic.lo", ".lo", ".a"]
for ext in exts:
for f in files:
if f.basename.endswith(ext):
return f
fail("cc_library did not produce any files")
def _cgo_codegen_impl(ctx):
go = go_context(ctx)
if not go.stdlib.cgo_tools:
fail("Go toolchain does not support cgo")
linkopts = ctx.attr.linkopts[:]
copts = go.stdlib.cgo_tools.c_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
cc = go.stdlib.cgo_tools.compiler_executable
args = go.args(go)
args.add(["-cc", str(cc), "-objdir", out_dir])
c_outs = [cgo_export_h, cgo_export_c]
go_outs = [cgo_types]
source = split_srcs(ctx.files.srcs)
for src in source.headers:
copts.extend(['-iquote', src.dirname])
for src in source.go:
mangled_stem, src_ext = _mangle(src)
gen_file = go.declare_file(go, path=mangled_stem + ".cgo1."+src_ext)
gen_c_file = go.declare_file(go, path=mangled_stem + ".cgo2.c")
go_outs.append(gen_file)
c_outs.append(gen_c_file)
args.add(["-src", gen_file.path + "=" + src.path])
for src in source.asm:
mangled_stem, src_ext = _mangle(src)
gen_file = go.declare_file(go, path=mangled_stem + ".cgo1."+src_ext)
go_outs.append(gen_file)
args.add(["-src", gen_file.path + "=" + src.path])
for src in source.c:
mangled_stem, src_ext = _mangle(src)
gen_file = go.declare_file(go, path=mangled_stem + ".cgo1."+src_ext)
c_outs.append(gen_file)
args.add(["-src", gen_file.path + "=" + src.path])
inputs = sets.union(ctx.files.srcs, go.crosstool, go.stdlib.files,
*[d.cc.transitive_headers for d in ctx.attr.deps])
deps = sets.union(deps, *[d.cc.libs for d in ctx.attr.deps])
runfiles = ctx.runfiles(collect_data = True)
for d in ctx.attr.deps:
runfiles = runfiles.merge(d.data_runfiles)
copts.extend(['-D' + define for define in d.cc.defines])
for inc in d.cc.include_directories:
copts.extend(['-I', inc])
for inc in d.cc.quote_include_directories:
copts.extend(['-iquote', inc])
for inc in d.cc.system_include_directories:
copts.extend(['-isystem', inc])
for lib in d.cc.libs:
if lib.basename.startswith('lib') and lib.basename.endswith('.so'):
linkopts.extend(['-L', lib.dirname, '-l', lib.basename[3:-3]])
else:
linkopts.append(lib.path)
linkopts.extend(d.cc.link_flags)
# The first -- below is to stop the cgo from processing args, the
# second is an actual arg to forward to the underlying go tool
args.add(["--", "--"])
args.add(copts)
ctx.actions.run(
inputs = inputs,
outputs = c_outs + go_outs + [cgo_main],
mnemonic = "CGoCodeGen",
progress_message = "CGoCodeGen %s" % ctx.label,
executable = go.toolchain.tools.cgo,
arguments = [args],
env = {
"CGO_LDFLAGS": " ".join(linkopts),
},
)
return [
_CgoCodegen(
go_files = to_set(go_outs),
main_c = to_set([cgo_main]),
deps = deps.to_list(),
exports = [cgo_export_h],
),
DefaultInfo(
files = depset(),
runfiles = runfiles,
),
OutputGroupInfo(
go_files = to_set(go_outs),
input_go_files = to_set(source.go + source.asm),
c_files = sets.union(c_outs, source.headers),
main_c = to_set([cgo_main]),
),
]
_cgo_codegen = rule(
_cgo_codegen_impl,
attrs = {
"srcs": attr.label_list(allow_files = True),
"deps": attr.label_list(
allow_files = False,
providers = ["cc"],
),
"copts": attr.string_list(),
"linkopts": attr.string_list(),
"_go_context_data": attr.label(default=Label("@io_bazel_rules_go//:go_context_data")),
},
toolchains = ["@io_bazel_rules_go//go:toolchain"],
)
def _cgo_import_impl(ctx):
go = go_context(ctx)
out = go.declare_file(go, ext=".go")
args = go.args(go)
args.add([
"-dynout", out,
"-dynimport", ctx.file.cgo_o,
"-src", ctx.files.sample_go_srcs[0],
])
ctx.actions.run(
inputs = [
ctx.file.cgo_o,
ctx.files.sample_go_srcs[0],
] + go.stdlib.files,
outputs = [out],
executable = go.toolchain.tools.cgo,
arguments = [args],
mnemonic = "CGoImportGen",
)
return struct(
files = depset([out]),
)
_cgo_import = rule(
_cgo_import_impl,
attrs = {
"cgo_o": attr.label(
allow_files = True,
single_file = True,
),
"sample_go_srcs": attr.label_list(allow_files = True),
"_go_context_data": attr.label(default=Label("@io_bazel_rules_go//:go_context_data")),
},
toolchains = ["@io_bazel_rules_go//go:toolchain"],
)
"""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 _pure(ctx, mode):
return mode.pure
def _not_pure(ctx, mode):
return not mode.pure
def _cgo_library_to_source(go, attr, source, merge):
library = source["library"]
if source["mode"].pure:
source["srcs"] = library.input_go_srcs + source["srcs"]
return
source["srcs"] = library.gen_go_srcs + source["srcs"]
source["cgo_deps"] = source["cgo_deps"] + library.cgo_deps
source["cgo_exports"] = source["cgo_exports"] + library.cgo_exports
source["cgo_archive"] = library.cgo_archive
source["runfiles"] = source["runfiles"].merge(attr.codegen.data_runfiles)
def _cgo_collect_info_impl(ctx):
go = go_context(ctx)
codegen = ctx.attr.codegen[_CgoCodegen]
runfiles = ctx.runfiles(collect_data = True)
runfiles = runfiles.merge(ctx.attr.codegen.data_runfiles)
library = go.new_library(go,
resolver=_cgo_library_to_source,
input_go_srcs = ctx.files.input_go_srcs,
gen_go_srcs = ctx.files.gen_go_srcs,
cgo_deps = ctx.attr.codegen[_CgoCodegen].deps,
cgo_exports = ctx.attr.codegen[_CgoCodegen].exports,
cgo_archive = _select_archive(ctx.files.lib),
)
source = go.library_to_source(go, ctx.attr, library, ctx.coverage_instrumented())
return [
source, library,
DefaultInfo(files = depset(), runfiles = runfiles),
]
_cgo_collect_info = rule(
_cgo_collect_info_impl,
attrs = {
"codegen": attr.label(mandatory = True, providers = [_CgoCodegen]),
"input_go_srcs": attr.label_list(mandatory = True, allow_files = [".go"]),
"gen_go_srcs": attr.label_list(mandatory = True, allow_files = [".go"]),
"lib": attr.label(mandatory = True, providers = ["cc"]),
"_go_context_data": attr.label(default=Label("@io_bazel_rules_go//:go_context_data")),
},
toolchains = ["@io_bazel_rules_go//go:toolchain"],
)
"""No-op rule that collects information from _cgo_codegen and cc_library
info into a GoSourceList provider for easy consumption."""
def setup_cgo_library(name, srcs, cdeps, copts, clinkopts):
# Apply build constraints to source files (both Go and C) but not to header
# files. Separate filtered Go and C sources.
# Run cgo on the filtered Go files. This will split them into pure Go files
# and pure C files, plus a few other glue files.
base_dir = pkg_dir(
"external/" + REPOSITORY_NAME[1:] if len(REPOSITORY_NAME) > 1 else "",
PACKAGE_NAME)
copts = copts + ["-I", base_dir]
cgo_codegen_name = name + ".cgo_codegen"
_cgo_codegen(
name = cgo_codegen_name,
srcs = srcs,
deps = cdeps,
copts = copts,
linkopts = clinkopts,
visibility = ["//visibility:private"],
)
select_go_files = name + ".select_go_files"
native.filegroup(
name = select_go_files,
srcs = [cgo_codegen_name],
output_group = "go_files",
visibility = ["//visibility:private"],
)
select_input_go_files = name + ".select_input_go_files"
native.filegroup(
name = select_input_go_files,
srcs = [cgo_codegen_name],
output_group = "input_go_files",
visibility = ["//visibility:private"],
)
select_c_files = name + ".select_c_files"
native.filegroup(
name = select_c_files,
srcs = [cgo_codegen_name],
output_group = "c_files",
visibility = ["//visibility:private"],
)
select_main_c = name + ".select_main_c"
native.filegroup(
name = select_main_c,
srcs = [cgo_codegen_name],
output_group = "main_c",
visibility = ["//visibility:private"],
)
# 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.
platform_copts = select({
"@io_bazel_rules_go//go/platform:darwin_amd64": [],
"@io_bazel_rules_go//go/platform:windows_amd64": ["-mthreads"],
"//conditions:default": ["-pthread"],
})
platform_linkopts = platform_copts
cgo_lib_name = name + ".cgo_c_lib"
native.cc_library(
name = cgo_lib_name,
srcs = [select_c_files],
deps = cdeps,
copts = copts + platform_copts + [
# The generated thunks often contain unused variables.
"-Wno-unused-variable",
],
linkopts = clinkopts + platform_linkopts,
linkstatic = 1,
# _cgo_.o needs all symbols because _cgo_import needs to see them.
alwayslink = 1,
visibility = ["//visibility:private"],
)
# Create a loadable object with no undefined references. cgo reads this
# when it generates _cgo_import.go.
cgo_o_name = name + "._cgo_.o"
native.cc_binary(
name = cgo_o_name,
srcs = [select_main_c],
deps = cdeps + [cgo_lib_name],
copts = copts,
linkopts = clinkopts,
visibility = ["//visibility:private"],
)
# Create a Go file which imports symbols from the C library.
cgo_import_name = name + ".cgo_import"
_cgo_import(
name = cgo_import_name,
cgo_o = cgo_o_name,
sample_go_srcs = [select_go_files],
visibility = ["//visibility:private"],
)
cgo_embed_name = name + ".cgo_embed"
_cgo_collect_info(
name = cgo_embed_name,
codegen = cgo_codegen_name,
input_go_srcs = [
select_input_go_files,
],
gen_go_srcs = [
select_go_files,
cgo_import_name,
],
lib = cgo_lib_name,
visibility = ["//visibility:private"],
)
return cgo_embed_name