...

Text file src/github.com/bazelbuild/rules_go/go/private/rules/cgo.bzl

Documentation: github.com/bazelbuild/rules_go/go/private/rules

     1# Copyright 2014 The Bazel Authors. All rights reserved.
     2#
     3# Licensed under the Apache License, Version 2.0 (the "License");
     4# you may not use this file except in compliance with the License.
     5# You may obtain a copy of the License at
     6#
     7#    http://www.apache.org/licenses/LICENSE-2.0
     8#
     9# Unless required by applicable law or agreed to in writing, software
    10# distributed under the License is distributed on an "AS IS" BASIS,
    11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12# See the License for the specific language governing permissions and
    13# limitations under the License.
    14
    15load(
    16    "//go/private:common.bzl",
    17    "get_versioned_shared_lib_extension",
    18    "has_simple_shared_lib_extension",
    19    "hdr_exts",
    20)
    21load(
    22    "//go/private:mode.bzl",
    23    "LINKMODE_NORMAL",
    24    "extldflags_from_cc_toolchain",
    25)
    26
    27def cgo_configure(go, srcs, cdeps, cppopts, copts, cxxopts, clinkopts):
    28    """cgo_configure returns the inputs and compile / link options
    29    that are required to build a cgo archive.
    30
    31    Args:
    32        go: a GoContext.
    33        srcs: list of source files being compiled. Include options are added
    34            for the headers.
    35        cdeps: list of Targets for C++ dependencies. Include and link options
    36            may be added.
    37        cppopts: list of C preprocessor options for the library.
    38        copts: list of C compiler options for the library.
    39        cxxopts: list of C++ compiler options for the library.
    40        clinkopts: list of linker options for the library.
    41
    42    Returns: a struct containing:
    43        inputs: depset of files that must be available for the build.
    44        deps: depset of files for dynamic libraries.
    45        runfiles: runfiles object for the C/C++ dependencies.
    46        cppopts: complete list of preprocessor options
    47        copts: complete list of C compiler options.
    48        cxxopts: complete list of C++ compiler options.
    49        objcopts: complete list of Objective-C compiler options.
    50        objcxxopts: complete list of Objective-C++ compiler options.
    51        clinkopts: complete list of linker options.
    52    """
    53    if not go.cgo_tools:
    54        fail("Go toolchain does not support cgo")
    55
    56    cppopts = list(cppopts)
    57    copts = go.cgo_tools.c_compile_options + copts
    58    cxxopts = go.cgo_tools.cxx_compile_options + cxxopts
    59    objcopts = go.cgo_tools.objc_compile_options + copts
    60    objcxxopts = go.cgo_tools.objcxx_compile_options + cxxopts
    61    clinkopts = extldflags_from_cc_toolchain(go) + clinkopts
    62
    63    # NOTE(#2545): avoid unnecessary dynamic link
    64    if "-static-libstdc++" in clinkopts:
    65        clinkopts = [
    66            option
    67            for option in clinkopts
    68            if option not in ("-lstdc++", "-lc++")
    69        ]
    70
    71    if go.mode != LINKMODE_NORMAL:
    72        for opt_list in (copts, cxxopts, objcopts, objcxxopts):
    73            if "-fPIC" not in opt_list:
    74                opt_list.append("-fPIC")
    75
    76    seen_includes = {}
    77    seen_quote_includes = {}
    78    seen_system_includes = {}
    79    have_hdrs = any([f.basename.endswith(ext) for f in srcs for ext in hdr_exts])
    80    if have_hdrs:
    81        # Add include paths for all sources so we can use include paths relative
    82        # to any source file or any header file. The go command requires all
    83        # sources to be in the same directory, but that's not necessarily the
    84        # case here.
    85        #
    86        # Use -I so either <> or "" includes may be used (same as go command).
    87        for f in srcs:
    88            _include_unique(cppopts, "-I", f.dirname, seen_includes)
    89
    90    inputs_direct = []
    91    inputs_transitive = []
    92    deps_direct = []
    93    lib_opts = []
    94    runfiles = go._ctx.runfiles(collect_data = True)
    95
    96    # Always include the sandbox as part of the build. Bazel does this, but it
    97    # doesn't appear in the CompilationContext.
    98    _include_unique(cppopts, "-iquote", ".", seen_quote_includes)
    99    for d in cdeps:
   100        runfiles = runfiles.merge(d.data_runfiles)
   101        if CcInfo in d:
   102            cc_transitive_headers = d[CcInfo].compilation_context.headers
   103            inputs_transitive.append(cc_transitive_headers)
   104            cc_libs, cc_link_flags = _cc_libs_and_flags(d)
   105            inputs_direct.extend(cc_libs)
   106            deps_direct.extend(cc_libs)
   107            cc_defines = d[CcInfo].compilation_context.defines.to_list()
   108            cppopts.extend(["-D" + define for define in cc_defines])
   109            cc_includes = d[CcInfo].compilation_context.includes.to_list()
   110            for inc in cc_includes:
   111                _include_unique(cppopts, "-I", inc, seen_includes)
   112            cc_quote_includes = d[CcInfo].compilation_context.quote_includes.to_list()
   113            for inc in cc_quote_includes:
   114                _include_unique(cppopts, "-iquote", inc, seen_quote_includes)
   115            cc_system_includes = d[CcInfo].compilation_context.system_includes.to_list()
   116            for inc in cc_system_includes:
   117                _include_unique(cppopts, "-isystem", inc, seen_system_includes)
   118            for lib in cc_libs:
   119                # If both static and dynamic variants are available, Bazel will only give
   120                # us the static variant. We'll get one file for each transitive dependency,
   121                # so the same file may appear more than once.
   122                if lib.basename.startswith("lib"):
   123                    if has_simple_shared_lib_extension(lib.basename):
   124                        # If the loader would be able to find the library using rpaths,
   125                        # use -L and -l instead of hard coding the path to the library in
   126                        # the binary. This gives users more flexibility. The linker will add
   127                        # rpaths later. We can't add them here because they are relative to
   128                        # the binary location, and we don't know where that is.
   129                        libname = lib.basename[len("lib"):lib.basename.rindex(".")]
   130                        clinkopts.extend(["-L", lib.dirname, "-l", libname])
   131                        inputs_direct.append(lib)
   132                        continue
   133                    extension = get_versioned_shared_lib_extension(lib.basename)
   134                    if extension.startswith("so"):
   135                        # With a versioned .so file, we must use the full filename,
   136                        # otherwise the library will not be found by the linker.
   137                        libname = ":%s" % lib.basename
   138                        clinkopts.extend(["-L", lib.dirname, "-l", libname])
   139                        inputs_direct.append(lib)
   140                        continue
   141                    elif extension.startswith("dylib"):
   142                        # A standard versioned dylib is named as libMagick.2.dylib, which is
   143                        # treated as a simple shared library. Non-standard versioned dylibs such as
   144                        # libclntsh.dylib.12.1, users have to create a unversioned symbolic link,
   145                        # so it can be treated as a simple shared library too.
   146                        continue
   147                lib_opts.append(lib.path)
   148            clinkopts.extend(cc_link_flags)
   149
   150        elif hasattr(d, "objc"):
   151            cppopts.extend(["-D" + define for define in d.objc.define.to_list()])
   152            for inc in d.objc.include.to_list():
   153                _include_unique(cppopts, "-I", inc, seen_includes)
   154            for inc in d.objc.iquote.to_list():
   155                _include_unique(cppopts, "-iquote", inc, seen_quote_includes)
   156            for inc in d.objc.include_system.to_list():
   157                _include_unique(cppopts, "-isystem", inc, seen_system_includes)
   158
   159            # TODO(jayconrod): do we need to link against dynamic libraries or
   160            # frameworks? We link against *_fully_linked.a, so maybe not?
   161
   162        else:
   163            fail("unknown library has neither cc nor objc providers: %s" % d.label)
   164
   165    inputs = depset(direct = inputs_direct, transitive = inputs_transitive)
   166    deps = depset(direct = deps_direct)
   167
   168    # HACK: some C/C++ toolchains will ignore libraries (including dynamic libs
   169    # specified with -l flags) unless they appear after .o or .a files with
   170    # undefined symbols they provide. Put all the .a files from cdeps first,
   171    # so that we actually link with -lstdc++ and others.
   172    clinkopts = lib_opts + clinkopts
   173
   174    return struct(
   175        inputs = inputs,
   176        deps = deps,
   177        runfiles = runfiles,
   178        cppopts = cppopts,
   179        copts = copts,
   180        cxxopts = cxxopts,
   181        objcopts = objcopts,
   182        objcxxopts = objcxxopts,
   183        clinkopts = clinkopts,
   184    )
   185
   186def _cc_libs_and_flags(target):
   187    # Copied from get_libs_for_static_executable in migration instructions
   188    # from bazelbuild/bazel#7036.
   189    libs = []
   190    flags = []
   191    for li in target[CcInfo].linking_context.linker_inputs.to_list():
   192        flags.extend(li.user_link_flags)
   193        for library_to_link in li.libraries:
   194            if library_to_link.static_library != None:
   195                libs.append(library_to_link.static_library)
   196            elif library_to_link.pic_static_library != None:
   197                libs.append(library_to_link.pic_static_library)
   198            elif library_to_link.interface_library != None:
   199                libs.append(library_to_link.interface_library)
   200            elif library_to_link.dynamic_library != None:
   201                libs.append(library_to_link.dynamic_library)
   202    return libs, flags
   203
   204def _include_unique(opts, flag, include, seen):
   205    if include in seen:
   206        return
   207    seen[include] = True
   208    opts.extend([flag, include])

View as plain text