| # Copyright 2021 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. | |
| """Implementation of cc_shared_library""" | |
| load("@com_google_protobuf//bazel/common:proto_info.bzl", "ProtoInfo") | |
| load("//cc:use_cc_toolchain.bzl", "use_cc_toolchain") | |
| load("//cc/common:cc_info.bzl", "CcInfo") | |
| load("//cc/common:cc_shared_library_hint_info.bzl", "CcSharedLibraryHintInfo") | |
| load("//cc/common:cc_shared_library_info.bzl", "CcSharedLibraryInfo") | |
| load("//cc/common:semantics.bzl", "semantics") | |
| load("//cc/private:graph_node_info.bzl", "GraphNodeInfo") | |
| load(":function_providing_rule.bzl", "proxy") | |
| # TODO(#5200): Add export_define to library_to_link and cc_library | |
| # Add this as a tag to any target that can be linked by more than one | |
| # cc_shared_library because it doesn't have static initializers or anything | |
| # else that may cause issues when being linked more than once. This should be | |
| # used sparingly after making sure it's safe to use. | |
| LINKABLE_MORE_THAN_ONCE = "LINKABLE_MORE_THAN_ONCE" | |
| def _graph_structure_aspect_impl(target, ctx): | |
| children = [] | |
| attributes = dir(ctx.rule.attr) | |
| owners = [ctx.label] | |
| if CcSharedLibraryHintInfo in target: | |
| attributes = getattr(target[CcSharedLibraryHintInfo], "attributes", dir(ctx.rule.attr)) | |
| owners = getattr(target[CcSharedLibraryHintInfo], "owners", [ctx.label]) | |
| # Collect graph structure info from any possible deplike attribute. The aspect | |
| # itself applies across every deplike attribute (attr_aspects is *), so enumerate | |
| # over all attributes and consume GraphNodeInfo if available. | |
| for fieldname in attributes: | |
| deps = getattr(ctx.rule.attr, fieldname, None) | |
| if type(deps) == "list": | |
| for dep in deps: | |
| if type(dep) == "Target" and GraphNodeInfo in dep: | |
| children.append(dep[GraphNodeInfo]) | |
| elif type(deps) == "Target" and GraphNodeInfo in deps: | |
| children.append(deps[GraphNodeInfo]) | |
| if hasattr(ctx.rule, "toolchains") and hasattr(ctx.rule.toolchains, "toolchain_types"): | |
| for toolchain_type in ctx.rule.toolchains.toolchain_types(): | |
| dep = ctx.rule.toolchains[toolchain_type] | |
| if dep and GraphNodeInfo in dep: | |
| children.append(dep[GraphNodeInfo]) | |
| # TODO(bazel-team): Add flag to Bazel that can toggle the initialization of | |
| # linkable_more_than_once. | |
| linkable_more_than_once = False | |
| if hasattr(ctx.rule.attr, "tags"): | |
| for tag in ctx.rule.attr.tags: | |
| if tag == LINKABLE_MORE_THAN_ONCE: | |
| linkable_more_than_once = True | |
| return [GraphNodeInfo( | |
| owners = owners, | |
| children = children, | |
| linkable_more_than_once = linkable_more_than_once, | |
| )] | |
| graph_structure_aspect = aspect( | |
| attr_aspects = ["*"], | |
| toolchains_aspects = ["*"], | |
| required_providers = [[CcInfo], [CcSharedLibraryHintInfo], [ProtoInfo], [platform_common.ToolchainInfo]], | |
| required_aspect_providers = [[CcInfo], [CcSharedLibraryHintInfo]], | |
| implementation = _graph_structure_aspect_impl, | |
| ) | |
| def _cc_shared_library_initializer(**kwargs): | |
| """Converts labels in exports_filter into canonical form relative to the current repository. | |
| This conversion can only be done in a macro as it requires access to the repository mapping of | |
| the repository containing the cc_shared_library target. This mapping is automatically | |
| applied to label attributes, but exports_filter is a list of strings attribute. | |
| """ | |
| if "exports_filter" not in kwargs: | |
| return kwargs | |
| raw_exports_filter = kwargs["exports_filter"] | |
| if type(raw_exports_filter) != type([]): | |
| # TODO: Also canonicalize labels in selects once macros can operate on them. | |
| # https://github.com/bazelbuild/bazel/issues/14157 | |
| return kwargs | |
| canonical_exports_filter = [ | |
| str(native.package_relative_label(s)) | |
| for s in raw_exports_filter | |
| ] | |
| return kwargs | {"exports_filter": canonical_exports_filter} | |
| cc_shared_library = rule( | |
| implementation = proxy, | |
| initializer = _cc_shared_library_initializer, | |
| doc = """ | |
| <p>It produces a shared library.</p> | |
| <h4 id="cc_shard_library_examples">Example</h4> | |
| <pre class="code"> | |
| cc_shared_library( | |
| name = "foo_shared", | |
| deps = [ | |
| ":foo", | |
| ], | |
| dynamic_deps = [ | |
| ":bar_shared", | |
| ], | |
| additional_linker_inputs = [ | |
| ":foo.lds", | |
| ], | |
| user_link_flags = [ | |
| "-Wl,--version-script=$(location :foo.lds)", | |
| ], | |
| ) | |
| cc_library( | |
| name = "foo", | |
| srcs = ["foo.cc"], | |
| hdrs = ["foo.h"], | |
| deps = [ | |
| ":bar", | |
| ":baz", | |
| ], | |
| ) | |
| cc_shared_library( | |
| name = "bar_shared", | |
| shared_lib_name = "bar.so", | |
| deps = [":bar"], | |
| ) | |
| cc_library( | |
| name = "bar", | |
| srcs = ["bar.cc"], | |
| hdrs = ["bar.h"], | |
| ) | |
| cc_library( | |
| name = "baz", | |
| srcs = ["baz.cc"], | |
| hdrs = ["baz.h"], | |
| ) | |
| </pre> | |
| <p>In the example <code>foo_shared</code> statically links <code>foo</code> | |
| and <code>baz</code>, the latter being a transitive dependency. It doesn't | |
| link <code>bar</code> because it is already provided dynamically by the | |
| <code>dynamic_dep</code> <code>bar_shared</code>.</p> | |
| <p><code>foo_shared</code> uses a linker script *.lds file to control which | |
| symbols should be exported. The <code>cc_shared_library</code> rule logic does | |
| not control which symbols get exported, it only uses what is assumed to be | |
| exported to give errors during analysis phase if two shared libraries export the | |
| same targets.</p> | |
| <p>Every direct dependency of <code>cc_shared_library</code> is assumed to be | |
| exported. Therefore, Bazel assumes during analysis that <code>foo</code> is being | |
| exported by <code>foo_shared</code>. <code>baz</code> is not assumed to be exported | |
| by <code>foo_shared</code>. Every target matched by the <code>exports_filter</code> | |
| is also assumed to be exported.</p> | |
| <p>Every single <code>cc_library</code> in the example should appear at most in one | |
| <code>cc_shared_library</code>. If we wanted to link <code>baz</code> also into | |
| <code>bar_shared</code> we would need to add | |
| <code>tags = ["LINKABLE_MORE_THAN_ONCE"]</code> to <code>baz</code>.</p> | |
| <p>Due to the <code>shared_lib_name</code> attribute, the file produced by | |
| <code>bar_shared</code> will have the name <code>bar.so</code> as opposed | |
| to the name <code>libbar.so</code> that it would have by default on Linux.</p> | |
| <h4 id="cc_shard_library_examples">Errors</h4> | |
| <h5><code>Two shared libraries in dependencies export the same symbols.</code></h5> | |
| <p>This will happen whenever you are creating a target with two different | |
| <code>cc_shared_library</code> dependencies that export the same target. To fix this | |
| you need to stop the libraries from being exported in one of the | |
| <code>cc_shared_library</code> dependencies.</p> | |
| <h5><code>Two shared libraries in dependencies link the same library statically</code></h5> | |
| <p>This will happen whenever you are creating a new <code>cc_shared_library</code> with two | |
| different <code>cc_shared_library</code> dependencies that link the same target statically. | |
| Similar to the error with exports.</p> | |
| <p>One way to fix this is to stop linking the library into one of the | |
| <code>cc_shared_library</code> dependencies. At the same time, the one that still links it | |
| needs to export the library so that the one not linking it keeps visibility to | |
| the symbols. Another way is to pull out a third library that exports the target. | |
| A third way is to tag the culprit <code>cc_library</code> with <code>LINKABLE_MORE_THAN_ONCE</code> | |
| but this fix should be rare and you should absolutely make sure that the | |
| <code>cc_library</code> is indeed safe to link more than once.</p> | |
| <h5><code>'//foo:foo' is already linked statically in '//bar:bar' but not exported`</code></h5> | |
| <p>This means that a library in the transitive closure of your <code>deps</code> is reachable | |
| without going through one of the <code>cc_shared_library</code> dependencies but is already | |
| linked into a different <code>cc_shared_library</code> in <code>dynamic_deps</code> and is not | |
| exported.</p> | |
| <p>The solution is to export it from the <code>cc_shared_library</code> dependency or pull out | |
| a third <code>cc_shared_library</code> that exports it.</p> | |
| <h5><code>Do not place libraries which only contain a precompiled dynamic library in deps. | |
| </code></h5> | |
| <p>If you have a precompiled dynamic library, this doesn't need to and cannot be | |
| linked statically into the current <code>cc_shared_library</code> target that you are | |
| currently creating. Therefore, it doesn't belong in <code>deps</code> of the | |
| <code>cc_shared_library</code>. If this precompiled dynamic library is a dependency of one | |
| of your <code>cc_libraries</code>, then the <code>cc_library</code> needs to depend on it | |
| directly.</p> | |
| <h5><code>Trying to export a library already exported by a different shared library</code></h5> | |
| <p>You will see this error if on the current rule you are claiming to export a | |
| target that is already being exported by one of your dynamic dependencies.</p> | |
| <p>To fix this, remove the target from <code>deps</code> and just rely on it from the dynamic | |
| dependency or make sure that the <code>exports_filter</code> doesn't catch this target.</p>""", | |
| attrs = { | |
| "additional_linker_inputs": attr.label_list(allow_files = True, doc = """ | |
| Any additional files that you may want to pass to the linker, for example, linker scripts. | |
| You have to separately pass any linker flags that the linker needs in order to be aware | |
| of this file. You can do so via the <code>user_link_flags</code> attribute."""), | |
| "shared_lib_name": attr.string(doc = """ | |
| By default cc_shared_library will use a name for the shared library output file based on | |
| the target's name and the platform. This includes an extension and sometimes a prefix. | |
| Sometimes you may not want the default name, for example, when loading C++ shared libraries | |
| for Python the default lib* prefix is often not desired, in which case you can use this | |
| attribute to choose a custom name."""), | |
| "dynamic_deps": attr.label_list(providers = [CcSharedLibraryInfo], doc = """ | |
| These are other <code>cc_shared_library</code> dependencies the current target depends on. | |
| <p> | |
| The <code>cc_shared_library</code> implementation will use the list of | |
| <code>dynamic_deps</code> (transitively, i.e. also the <code>dynamic_deps</code> of the | |
| current target's <code>dynamic_deps</code>) to decide which <code>cc_libraries</code> in | |
| the transitive <code>deps</code> should not be linked in because they are already provided | |
| by a different <code>cc_shared_library</code>. | |
| </p>"""), | |
| "exports_filter": attr.string_list(doc = """ | |
| This attribute contains a list of targets that are claimed to be exported by the current | |
| shared library. | |
| <p> | |
| Any target <code>deps</code> is already understood to be exported by the shared library. | |
| This attribute should be used to list any targets that are exported by the shared library | |
| but are transitive dependencies of <code>deps</code>. | |
| </p> | |
| <p> | |
| Note that this attribute is not actually adding a dependency edge to those targets, the | |
| dependency edge should instead be created by <code>deps</code>.The entries in this | |
| attribute are just strings. Keep in mind that when placing a target in this attribute, | |
| this is considered a claim that the shared library exports the symbols from that target. | |
| The <code>cc_shared_library</code> logic doesn't actually handle telling the linker which | |
| symbols should be exported. | |
| </p> | |
| <p>The following syntax is allowed:</p> | |
| <p><code>//foo:__pkg__</code> to account for any target in foo/BUILD</p> | |
| <p><code>//foo:__subpackages__</code> to account for any target in foo/BUILD or any other | |
| package below foo/ like foo/bar/BUILD</p>"""), | |
| "win_def_file": attr.label(allow_single_file = [".def"], doc = """ | |
| The Windows DEF file to be passed to linker. | |
| <p>This attribute should only be used when Windows is the target platform. | |
| It can be used to <a href="https://msdn.microsoft.com/en-us/library/d91k01sh.aspx"> | |
| export symbols</a> during linking a shared library.</p>"""), | |
| "roots": attr.label_list(providers = [CcInfo], aspects = [graph_structure_aspect]), | |
| "deps": attr.label_list(providers = [CcInfo], aspects = [graph_structure_aspect], doc = """ | |
| Top level libraries that will unconditionally be statically linked into the shared library | |
| after being whole-archived. | |
| <p> | |
| Any transitive library dependency of these direct deps will be linked into this shared | |
| library as long as they have not already been linked by a <code>cc_shared_library</code> | |
| in <code>dynamic_deps</code>.</p> | |
| <p> | |
| During analysis, the rule implementation will consider any target listed in | |
| <code>deps</code> as being exported by the shared library in order to give errors when | |
| multiple <code>cc_shared_libraries</code> export the same targets. The rule implementation | |
| does not take care of informing the linker about which symbols should be exported by the | |
| shared object. The user should take care of this via linker scripts or visibility | |
| declarations in the source code.</p> | |
| <p> | |
| The implementation will also trigger errors whenever the same library is linked statically | |
| into more than one <code>cc_shared_library</code>. This can be avoided by adding | |
| <code>"LINKABLE_MORE_THAN_ONCE"</code> to the <code>cc_library.tags</code> or by listing | |
| the `cc_library` as an export of one of the shared libraries so that one can be made a | |
| <code>dynamic_dep</code> of the other. | |
| </p>"""), | |
| "static_deps": attr.string_list(), | |
| "user_link_flags": attr.string_list(doc = """ | |
| Any additional flags that you may want to pass to the linker. For example, to make the | |
| linker aware of a linker script passed via additional_linker_inputs you can use the | |
| following: | |
| <pre><code class="lang-starlark"> | |
| cc_shared_library( | |
| name = "foo_shared", | |
| additional_linker_inputs = select({ | |
| "//src/conditions:linux": [ | |
| ":foo.lds", | |
| ":additional_script.txt", | |
| ], | |
| "//conditions:default": []}), | |
| user_link_flags = select({ | |
| "//src/conditions:linux": [ | |
| "-Wl,-rpath,kittens", | |
| "-Wl,--version-script=$(location :foo.lds)", | |
| "-Wl,--script=$(location :additional_script.txt)", | |
| ], | |
| "//conditions:default": []}), | |
| ... | |
| ) | |
| </code></pre>"""), | |
| "_def_parser": semantics.get_def_parser(), | |
| "_impl_delegate": attr.label( | |
| default = Label("//cc/private/rules_impl/wrappers:cc_shared_library_impl_wrapper"), | |
| cfg = "exec", | |
| ), | |
| }, # buildifier: disable=unsorted-dict-items | |
| toolchains = use_cc_toolchain() + semantics.get_runtimes_toolchain(), | |
| fragments = ["cpp"] + semantics.additional_fragments(), | |
| ) | |
| def _is_non_empty_list_or_select(value, attr): | |
| if type(value) == "list": | |
| return len(value) > 0 | |
| elif type(value) == "select": | |
| return True | |
| else: | |
| fail("Only select or list is valid for {} attr".format(attr)) | |
| def dynamic_deps_initializer(**kwargs): | |
| """Initializes dynamic_deps_attrs | |
| Args: | |
| **kwargs: (dict) | |
| Returns: | |
| (dict) | |
| """ | |
| if "dynamic_deps" in kwargs and _is_non_empty_list_or_select(kwargs["dynamic_deps"], "dynamic_deps"): | |
| # Propagate an aspect if dynamic_deps attribute is specified. | |
| # Use += for lists rather than extend or append to allow for the case where deps | |
| # is a select. | |
| all_deps = [] | |
| if "deps" in kwargs: | |
| all_deps += kwargs["deps"] | |
| if "linkshared" not in kwargs or not kwargs["linkshared"]: | |
| # The += [...] pattern below doesn't work if malloc or link_extra_lib are | |
| # themselves selects, but as of March 2024, there is no way to combine mixed | |
| # selects and these attributes usually point to label flags anyway. | |
| if "link_extra_lib" in kwargs: | |
| all_deps += [kwargs["link_extra_lib"]] # buildifier: disable=list-append | |
| if "malloc" in kwargs: | |
| all_deps += [kwargs["malloc"]] # buildifier: disable=list-append | |
| return kwargs | {"_deps_analyzed_by_graph_structure_aspect": all_deps} | |
| return kwargs | |
| dynamic_deps_attrs = { | |
| "dynamic_deps": attr.label_list( | |
| allow_files = False, | |
| providers = [CcSharedLibraryInfo], | |
| doc = """ | |
| These are other <code>cc_shared_library</code> dependencies the current target depends on. | |
| <p> | |
| The <code>cc_shared_library</code> implementation will use the list of | |
| <code>dynamic_deps</code> (transitively, i.e. also the <code>dynamic_deps</code> of the | |
| current target's <code>dynamic_deps</code>) to decide which <code>cc_libraries</code> in | |
| the transitive <code>deps</code> should not be linked in because they are already provided | |
| by a different <code>cc_shared_library</code>. | |
| """ + semantics.dynamic_deps_extra_docs, | |
| ), | |
| "_deps_analyzed_by_graph_structure_aspect": attr.label_list( | |
| providers = [CcInfo], | |
| aspects = [graph_structure_aspect], | |
| ), | |
| } |