| # 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 |