diff options
Diffstat (limited to 'go/private/rules/cgo.bzl')
-rw-r--r-- | go/private/rules/cgo.bzl | 208 |
1 files changed, 208 insertions, 0 deletions
diff --git a/go/private/rules/cgo.bzl b/go/private/rules/cgo.bzl new file mode 100644 index 00000000..b8fc93a6 --- /dev/null +++ b/go/private/rules/cgo.bzl @@ -0,0 +1,208 @@ +# 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( + "//go/private:common.bzl", + "get_versioned_shared_lib_extension", + "has_simple_shared_lib_extension", + "hdr_exts", +) +load( + "//go/private:mode.bzl", + "LINKMODE_NORMAL", + "extldflags_from_cc_toolchain", +) + +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. + objcopts: complete list of Objective-C compiler options. + objcxxopts: complete list of Objective-C++ compiler options. + clinkopts: complete list of linker options. + """ + if not go.cgo_tools: + fail("Go toolchain does not support cgo") + + cppopts = list(cppopts) + copts = go.cgo_tools.c_compile_options + copts + cxxopts = go.cgo_tools.cxx_compile_options + cxxopts + objcopts = go.cgo_tools.objc_compile_options + copts + objcxxopts = go.cgo_tools.objcxx_compile_options + cxxopts + clinkopts = extldflags_from_cc_toolchain(go) + clinkopts + + # NOTE(#2545): avoid unnecessary dynamic link + if "-static-libstdc++" in clinkopts: + clinkopts = [ + option + for option in clinkopts + if option not in ("-lstdc++", "-lc++") + ] + + if go.mode != LINKMODE_NORMAL: + for opt_list in (copts, cxxopts, objcopts, objcxxopts): + if "-fPIC" not in opt_list: + opt_list.append("-fPIC") + + seen_includes = {} + seen_quote_includes = {} + seen_system_includes = {} + have_hdrs = any([f.basename.endswith(ext) for f in srcs for ext in hdr_exts]) + if have_hdrs: + # Add include paths for all sources so we can use include paths relative + # to any source file or any header file. The go command requires all + # sources to be in the same directory, but that's not necessarily the + # case here. + # + # Use -I so either <> or "" includes may be used (same as go command). + for f in srcs: + _include_unique(cppopts, "-I", f.dirname, seen_includes) + + inputs_direct = [] + inputs_transitive = [] + deps_direct = [] + lib_opts = [] + runfiles = go._ctx.runfiles(collect_data = True) + + # Always include the sandbox as part of the build. Bazel does this, but it + # doesn't appear in the CompilationContext. + _include_unique(cppopts, "-iquote", ".", seen_quote_includes) + for d in cdeps: + runfiles = runfiles.merge(d.data_runfiles) + if CcInfo in d: + cc_transitive_headers = d[CcInfo].compilation_context.headers + inputs_transitive.append(cc_transitive_headers) + cc_libs, cc_link_flags = _cc_libs_and_flags(d) + inputs_direct.extend(cc_libs) + deps_direct.extend(cc_libs) + cc_defines = d[CcInfo].compilation_context.defines.to_list() + cppopts.extend(["-D" + define for define in cc_defines]) + cc_includes = d[CcInfo].compilation_context.includes.to_list() + for inc in cc_includes: + _include_unique(cppopts, "-I", inc, seen_includes) + cc_quote_includes = d[CcInfo].compilation_context.quote_includes.to_list() + for inc in cc_quote_includes: + _include_unique(cppopts, "-iquote", inc, seen_quote_includes) + cc_system_includes = d[CcInfo].compilation_context.system_includes.to_list() + for inc in cc_system_includes: + _include_unique(cppopts, "-isystem", inc, seen_system_includes) + for lib in cc_libs: + # 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"): + if 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) + continue + extension = get_versioned_shared_lib_extension(lib.basename) + if extension.startswith("so"): + # With a versioned .so file, 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) + continue + elif extension.startswith("dylib"): + # A standard versioned dylib is named as libMagick.2.dylib, which is + # treated as a simple shared library. Non-standard versioned dylibs such as + # libclntsh.dylib.12.1, users have to create a unversioned symbolic link, + # so it can be treated as a simple shared library too. + continue + lib_opts.append(lib.path) + clinkopts.extend(cc_link_flags) + + 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) + + 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, + objcopts = objcopts, + objcxxopts = objcxxopts, + clinkopts = clinkopts, + ) + +def _cc_libs_and_flags(target): + # Copied from get_libs_for_static_executable in migration instructions + # from bazelbuild/bazel#7036. + libs = [] + flags = [] + for li in target[CcInfo].linking_context.linker_inputs.to_list(): + flags.extend(li.user_link_flags) + for library_to_link in li.libraries: + if library_to_link.static_library != None: + libs.append(library_to_link.static_library) + elif library_to_link.pic_static_library != None: + libs.append(library_to_link.pic_static_library) + elif library_to_link.interface_library != None: + libs.append(library_to_link.interface_library) + elif library_to_link.dynamic_library != None: + libs.append(library_to_link.dynamic_library) + return libs, flags + +def _include_unique(opts, flag, include, seen): + if include in seen: + return + seen[include] = True + opts.extend([flag, include]) |