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