blob: b8fc93a677cb5b9d1ecc68663c7ad365fc027b45 [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(
"//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])