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