github.com/bazelbuild/rules_go@v0.47.2-0.20240515105122-e7ddb9ea474e/go/private/rules/cgo.bzl (about) 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 15 load( 16 "//go/private:common.bzl", 17 "get_versioned_shared_lib_extension", 18 "has_simple_shared_lib_extension", 19 "hdr_exts", 20 ) 21 load( 22 "//go/private:mode.bzl", 23 "LINKMODE_NORMAL", 24 "extldflags_from_cc_toolchain", 25 ) 26 27 def 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 186 def _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 204 def _include_unique(opts, flag, include, seen): 205 if include in seen: 206 return 207 seen[include] = True 208 opts.extend([flag, include])