aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVinh Tran <vinhdaitran@google.com>2024-02-15 23:04:34 -0500
committerGitHub <noreply@github.com>2024-02-15 23:04:34 -0500
commit8fd0904a58d1256a531a89ecdb774ba970049185 (patch)
treedb985bd9ccc382cab5a7291f9f839ec2dc11950d
parent5c715ec50602e2ba6ca2ebfdd870662a6e6d1eda (diff)
downloadbazelbuild-rules_rust-8fd0904a58d1256a531a89ecdb774ba970049185.tar.gz
Fix linkopts set in cc deps of bindgen (#2422)
### Problem As of now (before this PR), we seem to mix `link_flags` file into using for two purposes. For ``` cc_library( name = "simple", srcs = ["simple.cc"], hdrs = ["simple.h"], linkopts = ["-framework"], ) rust_bindgen_library( name = "simple_bindgen", cc_lib = ":simple", header = "simple.h", ) rust_binary( name = "simple_example", srcs = ["main.rs"], deps = [":simple_bindgen"], ) ``` `rust_bindgen_library` produces a `link_flags` file with ``` -lstatic=simple -C link-args=-framework ``` `-lstatic=simple` is consumed by `rustc` whereas `-framework` is expected to be consumed by an actual linker (either invoked by `rustc` or `cc_common.link`) The flags are then passed to `rustc` command to compile `libsimple_bindgen.rlib`. It does not yield any error because `-lstatic=simple` is correctly used whereas `-framework` is no-op (there is no linker being invoked for producing `rlib`). However, the rustc ***doesn't*** pass `-framework` to the linker that link the `rust_binary` (which is where the cc linkopts matters). Another problem is that this approach is not compatible with `experimental_use_cc_common_link` option which let `cc_common.link` instead of `rustc` invoke the linker. See #2413 ### Solution We're referring "link" as at least two things (which I think what causes problem here): 1. https://doc.rust-lang.org/rustc/command-line-arguments.html#-l-link-the-generated-crate-to-a-native-library 2. https://doc.rust-lang.org/rustc/codegen-options/index.html#linker https://doc.rust-lang.org/rustc/codegen-options/index.html#link-args As proposed in https://github.com/bazelbuild/rules_rust/pull/2415#issuecomment-1890109092, this PR splits `<rust_library_bindgen>__bindgen.link_flags` produced by `rust_bindgen` rule into two files: 1. `rustc_flags` ``` -lstatic=simple ``` 2. `linker_flags` ``` -framework ``` We "sort-of" (but not perfectly) had it correct before https://github.com/bazelbuild/rules_rust/pull/2361 when we link the binary directly with the cc_library (aka both kinds of flags). But since we want to support the Cargo style of linking ``` cc_lib -> bindgen -> lib_a -> bin ``` instead of ``` cc_lib -> bindgen -> lib_a -> bin \___________________________^ ``` we can pass `-lstatic=simple` to the `rustc` command that builds `simple_bindgen` (rust_library) and propagate `linkopts` to `simple_example` (rust_binary) so that the linker can use it. ``` cc_library -> rust_bindgen -> rust_library -> rust_binary -lstatic=simple -Clink-args="-framework" ``` This is long and sounds like a proposal. I'm open for suggestion @UebelAndre @illicitonion @krasimirgg Fixes #2413
-rw-r--r--bindgen/private/bindgen.bzl49
-rw-r--r--cargo/private/cargo_build_script.bzl6
-rw-r--r--rust/private/providers.bzl4
-rw-r--r--rust/private/rustc.bzl35
4 files changed, 59 insertions, 35 deletions
diff --git a/bindgen/private/bindgen.bzl b/bindgen/private/bindgen.bzl
index f32a4fc8..445036ba 100644
--- a/bindgen/private/bindgen.bzl
+++ b/bindgen/private/bindgen.bzl
@@ -99,6 +99,14 @@ def rust_bindgen_library(
**kwargs
)
+def _get_user_link_flags(cc_lib):
+ linker_flags = []
+
+ for linker_input in cc_lib[CcInfo].linking_context.linker_inputs.to_list():
+ linker_flags.extend(linker_input.user_link_flags)
+
+ return linker_flags
+
def _generate_cc_link_build_info(ctx, cc_lib):
"""Produce the eqivilant cargo_build_script providers for use in linking the library.
@@ -110,33 +118,30 @@ def _generate_cc_link_build_info(ctx, cc_lib):
The `BuildInfo` provider.
"""
compile_data = []
- linker_flags = []
+
+ rustc_flags = []
linker_search_paths = []
for linker_input in cc_lib[CcInfo].linking_context.linker_inputs.to_list():
for lib in linker_input.libraries:
if lib.static_library:
- linker_flags.append("-lstatic={}".format(get_lib_name_default(lib.static_library)))
+ rustc_flags.append("-lstatic={}".format(get_lib_name_default(lib.static_library)))
linker_search_paths.append(lib.static_library.dirname)
compile_data.append(lib.static_library)
elif lib.pic_static_library:
- linker_flags.append("-lstatic={}".format(get_lib_name_default(lib.pic_static_library)))
+ rustc_flags.append("-lstatic={}".format(get_lib_name_default(lib.pic_static_library)))
linker_search_paths.append(lib.pic_static_library.dirname)
compile_data.append(lib.pic_static_library)
- if linker_input.user_link_flags:
- linker_flags.append("-C")
- linker_flags.append("link-args={}".format(" ".join(linker_input.user_link_flags)))
-
if not compile_data:
fail("No static libraries found in {}".format(
cc_lib.label,
))
- link_flags = ctx.actions.declare_file("{}.link_flags".format(ctx.label.name))
+ rustc_flags_file = ctx.actions.declare_file("{}.rustc_flags".format(ctx.label.name))
ctx.actions.write(
- output = link_flags,
- content = "\n".join(linker_flags),
+ output = rustc_flags_file,
+ content = "\n".join(rustc_flags),
)
link_search_paths = ctx.actions.declare_file("{}.link_search_paths".format(ctx.label.name))
@@ -151,8 +156,9 @@ def _generate_cc_link_build_info(ctx, cc_lib):
return BuildInfo(
compile_data = depset(compile_data),
dep_env = None,
- flags = None,
- link_flags = link_flags,
+ flags = rustc_flags_file,
+ # linker_flags is provided via CcInfo
+ linker_flags = None,
link_search_paths = link_search_paths,
out_dir = None,
rustc_env = None,
@@ -282,7 +288,24 @@ def _rust_bindgen_impl(ctx):
direct_cc_infos = [cc_lib[CcInfo]],
)]
else:
- providers = [_generate_cc_link_build_info(ctx, cc_lib)]
+ providers = [
+ _generate_cc_link_build_info(ctx, cc_lib),
+ # As in https://github.com/bazelbuild/rules_rust/pull/2361, we want
+ # to link cc_lib to the direct parent (rlib) using `-lstatic=<cc_lib>` rustc flag
+ # Hence, we do not need to provide the whole CcInfo of cc_lib because
+ # it will cause the downstream binary to link the cc_lib again
+ # (same effect as setting `leak_symbols` attribute above)
+ # The CcInfo here only contains the custom link flags (i.e. linkopts attribute)
+ # specified by users in cc_lib
+ CcInfo(
+ linking_context = cc_common.create_linking_context(
+ linker_inputs = depset([cc_common.create_linker_input(
+ owner = ctx.label,
+ user_link_flags = _get_user_link_flags(cc_lib),
+ )]),
+ ),
+ ),
+ ]
return providers + [
OutputGroupInfo(
diff --git a/cargo/private/cargo_build_script.bzl b/cargo/private/cargo_build_script.bzl
index ad93a5c2..e88158c1 100644
--- a/cargo/private/cargo_build_script.bzl
+++ b/cargo/private/cargo_build_script.bzl
@@ -299,7 +299,7 @@ def _cargo_build_script_impl(ctx):
rustc_env = env_out,
dep_env = dep_env_out,
flags = flags_out,
- link_flags = link_flags,
+ linker_flags = link_flags,
link_search_paths = link_search_paths,
compile_data = depset([]),
),
@@ -445,7 +445,7 @@ def _cargo_dep_env_implementation(ctx):
build_infos.append(BuildInfo(
dep_env = empty_file,
flags = empty_file,
- link_flags = empty_file,
+ linker_flags = empty_file,
link_search_paths = empty_file,
out_dir = out_dir,
rustc_env = empty_file,
@@ -464,7 +464,7 @@ def _cargo_dep_env_implementation(ctx):
BuildInfo(
dep_env = empty_file,
flags = empty_file,
- link_flags = empty_file,
+ linker_flags = empty_file,
link_search_paths = empty_file,
out_dir = None,
rustc_env = empty_file,
diff --git a/rust/private/providers.bzl b/rust/private/providers.bzl
index 56f91e0e..a1ab2fe6 100644
--- a/rust/private/providers.bzl
+++ b/rust/private/providers.bzl
@@ -75,8 +75,8 @@ BuildInfo = provider(
"compile_data": "Depset[File]: Compile data provided by the build script that was not copied into `out_dir`.",
"dep_env": "Optinal[File]: extra build script environment varibles to be set to direct dependencies.",
"flags": "Optional[File]: file containing additional flags to pass to rustc",
- "link_flags": "Optional[File]: file containing flags to pass to the linker",
- "link_search_paths": "Optional[File]: file containing search paths to pass to the linker",
+ "link_search_paths": "Optional[File]: file containing search paths to pass to rustc and linker",
+ "linker_flags": "Optional[File]: file containing flags to pass to the linker invoked by rustc or cc_common.link",
"out_dir": "Optional[File]: directory containing the result of a build script",
"rustc_env": "Optional[File]: file containing additional environment variables to set for rustc.",
},
diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl
index 5c11f762..81dab3d7 100644
--- a/rust/private/rustc.bzl
+++ b/rust/private/rustc.bzl
@@ -310,18 +310,19 @@ def collect_deps(
transitive_link_search_paths.append(dep_info.link_search_path_files)
transitive_build_infos.append(dep_info.transitive_build_infos)
-
- elif cc_info:
- # This dependency is a cc_library
- transitive_noncrates.append(cc_info.linking_context.linker_inputs)
- elif dep_build_info:
- if build_info:
- fail("Several deps are providing build information, " +
- "only one is allowed in the dependencies")
- build_info = dep_build_info
- transitive_build_infos.append(depset([build_info]))
- if build_info.link_search_paths:
- transitive_link_search_paths.append(depset([build_info.link_search_paths]))
+ elif cc_info or dep_build_info:
+ if cc_info:
+ # This dependency is a cc_library
+ transitive_noncrates.append(cc_info.linking_context.linker_inputs)
+
+ if dep_build_info:
+ if build_info:
+ fail("Several deps are providing build information, " +
+ "only one is allowed in the dependencies")
+ build_info = dep_build_info
+ transitive_build_infos.append(depset([build_info]))
+ if build_info.link_search_paths:
+ transitive_link_search_paths.append(depset([build_info.link_search_paths]))
else:
fail("rust targets can only depend on rust_library, rust_*_library or cc_library " +
"targets.")
@@ -776,7 +777,6 @@ def collect_inputs(
if build_env_file:
build_env_files = [f for f in build_env_files] + [build_env_file]
compile_inputs = depset(build_env_files, transitive = [compile_inputs])
-
return compile_inputs, out_dir, build_env_files, build_flags_files, linkstamp_outs, ambiguous_libs
def construct_arguments(
@@ -1672,7 +1672,7 @@ def _create_extra_input_args(build_info, dep_info):
- (depset[File]): A list of all build info `OUT_DIR` File objects
- (str): The `OUT_DIR` of the current build info
- (File): An optional generated environment file from a `cargo_build_script` target
- - (depset[File]): All direct and transitive build flag files from the current build info.
+ - (depset[File]): All direct and transitive build flag files from the current build info to be passed to rustc.
"""
input_files = []
input_depsets = []
@@ -1690,9 +1690,10 @@ def _create_extra_input_args(build_info, dep_info):
build_env_file = build_info.rustc_env
if build_info.flags:
build_flags_files.append(build_info.flags)
- if build_info.link_flags:
- build_flags_files.append(build_info.link_flags)
- input_files.append(build_info.link_flags)
+ if build_info.linker_flags:
+ build_flags_files.append(build_info.linker_flags)
+ input_files.append(build_info.linker_flags)
+
input_depsets.append(build_info.compile_data)
return (