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