diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000..bfeeb4c87c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,11 @@ +FROM ubuntu +RUN apt-get update && apt-get install -y ca-certificates curl git python3 binutils zstd file clang lld +RUN apt-get install -y build-essential +RUN curl -L https://github.com/bazelbuild/bazelisk/releases/download/v1.18.0/bazelisk-linux-amd64 -o /usr/bin/bazel && chmod +x /usr/bin/bazel +RUN adduser fakeuser +USER fakeuser + +WORKDIR /rules_rust +RUN echo "8.4.2" > .bazelversion +RUN touch WORKSPACE && bazel help +RUN echo "bazel test //..." > ~/.bash_history diff --git a/build_docker.sh b/build_docker.sh new file mode 100755 index 0000000000..b5659453e0 --- /dev/null +++ b/build_docker.sh @@ -0,0 +1,10 @@ +set -eux + +docker stop bazel-builder || true +docker rm bazel-builder || true +docker build --platform=linux/amd64 -t bazel-base-image . +docker run --privileged --shm-size=4G \ + --mount type=bind,src="$(realpath $(dirname $0))",dst=/rules_rust \ + --mount type=bind,src="$HOME/.bazelrc",dst=/home/fakeuser/.bazelrc \ + --mount type=volume,src=bazel-cache,dst=/home/fakeuser/.cache/bazel \ + --name bazel-builder -it bazel-base-image bash diff --git a/rust/private/clippy.bzl b/rust/private/clippy.bzl index f5a7d8d178..bea062e6de 100644 --- a/rust/private/clippy.bzl +++ b/rust/private/clippy.bzl @@ -132,7 +132,7 @@ def _clippy_aspect_impl(target, ctx): ctx.rule.attr.lint_config[LintsInfo].clippy_lint_files + \ ctx.rule.attr.lint_config[LintsInfo].rustc_lint_files - compile_inputs, out_dir, build_env_files, build_flags_files, linkstamp_outs, ambiguous_libs = collect_inputs( + compile_inputs, out_dir, build_env_files, build_flags_files, linkstamp_outs = collect_inputs( ctx, ctx.rule.file, ctx.rule.files, @@ -171,7 +171,6 @@ def _clippy_aspect_impl(target, ctx): crate_info = crate_info, dep_info = dep_info, linkstamp_outs = linkstamp_outs, - ambiguous_libs = ambiguous_libs, output_hash = determine_output_hash(crate_info.root, ctx.label), rust_flags = [], out_dir = out_dir, diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl index 105352a8b8..498b9e2467 100644 --- a/rust/private/rustc.bzl +++ b/rust/private/rustc.bzl @@ -25,7 +25,6 @@ load( load("@rules_cc//cc/common:cc_common.bzl", "cc_common") load("@rules_cc//cc/common:cc_info.bzl", "CcInfo") load(":common.bzl", "rust_common") -load(":compat.bzl", "abs") load(":lto.bzl", "construct_lto_arguments") load( ":providers.bzl", @@ -479,128 +478,6 @@ def get_linker_and_args(ctx, crate_type, cc_toolchain, feature_configuration, rp return ld, link_args, link_env -def _symlink_for_ambiguous_lib(actions, toolchain, crate_info, lib): - """Constructs a disambiguating symlink for a library dependency. - - Args: - actions (Actions): The rule's context actions object. - toolchain: The Rust toolchain object. - crate_info (CrateInfo): The target crate's info. - lib (File): The library to symlink to. - - Returns: - (File): The disambiguating symlink for the library. - """ - # FIXME: Once the relative order part of the native-link-modifiers rustc - # feature is stable, we should be able to eliminate the need to construct - # symlinks by passing the full paths to the libraries. - # https://github.com/rust-lang/rust/issues/81490. - - # Take the absolute value of hash() since it could be negative. - path_hash = abs(hash(lib.path)) - lib_name = get_lib_name_for_windows(lib) if toolchain.target_os.startswith("windows") else get_lib_name_default(lib) - - if toolchain.target_os.startswith("windows"): - prefix = "" - extension = ".lib" - elif lib_name.endswith(".pic"): - # Strip the .pic suffix - lib_name = lib_name[:-4] - prefix = "lib" - extension = ".pic.a" - else: - prefix = "lib" - extension = ".a" - - # Ensure the symlink follows the lib.a pattern on Unix-like platforms - # or .lib on Windows. - # Add a hash of the original library path to disambiguate libraries with the same basename. - symlink_name = "{}{}-{}{}".format(prefix, lib_name, path_hash, extension) - - # Add the symlink to a target crate-specific _ambiguous_libs/ subfolder, - # to avoid possible collisions with sibling crates that may depend on the - # same ambiguous libraries. - symlink = actions.declare_file("_ambiguous_libs/" + crate_info.output.basename + "/" + symlink_name) - actions.symlink( - output = symlink, - target_file = lib, - progress_message = "Creating symlink to ambiguous lib: {}".format(lib.path), - ) - return symlink - -def _disambiguate_libs(actions, toolchain, crate_info, dep_info, use_pic): - """Constructs disambiguating symlinks for ambiguous library dependencies. - - The symlinks are all created in a _ambiguous_libs/ subfolder specific to - the target crate to avoid possible collisions with sibling crates that may - depend on the same ambiguous libraries. - - Args: - actions (Actions): The rule's context actions object. - toolchain: The Rust toolchain object. - crate_info (CrateInfo): The target crate's info. - dep_info: (DepInfo): The target crate's dependency info. - use_pic: (boolean): Whether the build should use PIC. - - Returns: - dict[String, File]: A mapping from ambiguous library paths to their - disambiguating symlink. - """ - # FIXME: Once the relative order part of the native-link-modifiers rustc - # feature is stable, we should be able to eliminate the need to construct - # symlinks by passing the full paths to the libraries. - # https://github.com/rust-lang/rust/issues/81490. - - # A dictionary from file paths of ambiguous libraries to the corresponding - # symlink. - ambiguous_libs = {} - - # A dictionary maintaining a mapping from preferred library name to the - # last visited artifact with that name. - visited_libs = {} - for link_input in dep_info.transitive_noncrates.to_list(): - for lib in link_input.libraries: - # FIXME: Dynamic libs are not disambiguated right now, there are - # cases where those have a non-standard name with version (e.g., - # //test/unit/versioned_libs). We hope that the link modifiers - # stabilization will come before we need to make this work. - if _is_dylib(lib): - continue - artifact = get_preferred_artifact(lib, use_pic) - name = get_lib_name_for_windows(artifact) if toolchain.target_os.startswith("windows") else get_lib_name_default(artifact) - - # On Linux-like platforms, normally library base names start with - # `lib`, following the pattern `lib[name].(a|lo)` and we pass - # -lstatic=name. - # On Windows, the base name looks like `name.lib` and we pass - # -lstatic=name. - # FIXME: Under the native-link-modifiers unstable rustc feature, - # we could use -lstatic:+verbatim instead. - needs_symlink_to_standardize_name = ( - toolchain.target_os.startswith(("linux", "mac", "darwin")) and - artifact.basename.endswith(".a") and not artifact.basename.startswith("lib") - ) or ( - toolchain.target_os.startswith("windows") and not artifact.basename.endswith(".lib") - ) - - # Detect cases where we need to disambiguate library dependencies - # by constructing symlinks. - if ( - needs_symlink_to_standardize_name or - # We have multiple libraries with the same name. - (name in visited_libs and visited_libs[name].path != artifact.path) - ): - # Disambiguate the previously visited library (if we just detected - # that it is ambiguous) and the current library. - if name in visited_libs: - old_path = visited_libs[name].path - if old_path not in ambiguous_libs: - ambiguous_libs[old_path] = _symlink_for_ambiguous_lib(actions, toolchain, crate_info, visited_libs[name]) - ambiguous_libs[artifact.path] = _symlink_for_ambiguous_lib(actions, toolchain, crate_info, artifact) - - visited_libs[name] = artifact - return ambiguous_libs - def _depend_on_metadata(crate_info, force_depend_on_objects): """Determines if we can depend on metadata for this crate. @@ -668,7 +545,6 @@ def collect_inputs( - (File): An optional path to a generated environment file from a `cargo_build_script` target - (depset[File]): All direct and transitive build flag files from the current build info - (list[File]): Linkstamp outputs - - (dict[String, File]): Ambiguous libs, see `_disambiguate_libs`. """ linker_script = getattr(file, "linker_script", None) @@ -689,15 +565,13 @@ def collect_inputs( # the output is rlib. This avoids quadratic behavior where transitive noncrates are # flattened on each transitive rust_library dependency. additional_transitive_inputs = [] - ambiguous_libs = {} if crate_info.type not in ("lib", "rlib"): linker_inputs = dep_info.transitive_noncrates.to_list() - ambiguous_libs = _disambiguate_libs(ctx.actions, toolchain, crate_info, dep_info, use_pic) additional_transitive_inputs = _collect_libs_from_linker_inputs(linker_inputs, use_pic) + [ additional_input for linker_input in linker_inputs for additional_input in linker_input.additional_inputs - ] + ambiguous_libs.values() + ] # Compute linkstamps. Use the inputs of the binary as inputs to the # linkstamp action to ensure linkstamps are rebuilt whenever binary inputs @@ -795,7 +669,7 @@ def collect_inputs( build_env_files = list(build_env_files) build_env_files.append(build_env_file) compile_inputs = depset(build_env_files + lint_files, transitive = [build_script_compile_inputs, compile_inputs]) - return compile_inputs, out_dir, build_env_files, build_flags_files, linkstamp_outs, ambiguous_libs + return compile_inputs, out_dir, build_env_files, build_flags_files, linkstamp_outs def _will_emit_object_file(emit): for e in emit: @@ -818,7 +692,6 @@ def construct_arguments( crate_info, dep_info, linkstamp_outs, - ambiguous_libs, output_hash, rust_flags, out_dir, @@ -848,7 +721,6 @@ def construct_arguments( crate_info (CrateInfo): The CrateInfo provider of the target crate dep_info (DepInfo): The DepInfo provider of the target crate linkstamp_outs (list): Linkstamp outputs of native dependencies - ambiguous_libs (dict): Ambiguous libs, see `_disambiguate_libs` output_hash (str): The hashed path of the crate root rust_flags (list): Additional flags to pass to rustc out_dir (str): The path to the output directory for the target Crate. @@ -1046,7 +918,7 @@ def construct_arguments( # Additional context: https://github.com/rust-lang/rust/pull/36574 rustc_flags.add_all(link_args, format_each = "--codegen=link-arg=%s") - _add_native_link_flags(rustc_flags, dep_info, linkstamp_outs, ambiguous_libs, crate_info.type, toolchain, cc_toolchain, feature_configuration, compilation_mode, include_link_flags = include_link_flags) + _add_native_link_flags(rustc_flags, dep_info, linkstamp_outs, crate_info.type, toolchain, cc_toolchain, feature_configuration, compilation_mode, include_link_flags = include_link_flags) use_metadata = _depend_on_metadata(crate_info, force_depend_on_objects) @@ -1255,7 +1127,7 @@ def rustc_compile_action( rust_flags = rust_flags + ctx.attr.lint_config[LintsInfo].rustc_lint_flags lint_files = lint_files + ctx.attr.lint_config[LintsInfo].rustc_lint_files - compile_inputs, out_dir, build_env_files, build_flags_files, linkstamp_outs, ambiguous_libs = collect_inputs( + compile_inputs, out_dir, build_env_files, build_flags_files, linkstamp_outs = collect_inputs( ctx = ctx, file = ctx.file, files = ctx.files, @@ -1297,7 +1169,6 @@ def rustc_compile_action( crate_info = crate_info, dep_info = dep_info, linkstamp_outs = linkstamp_outs, - ambiguous_libs = ambiguous_libs, output_hash = output_hash, rust_flags = rust_flags, out_dir = out_dir, @@ -1323,7 +1194,6 @@ def rustc_compile_action( crate_info = crate_info, dep_info = dep_info, linkstamp_outs = linkstamp_outs, - ambiguous_libs = ambiguous_libs, output_hash = output_hash, rust_flags = rust_flags, out_dir = out_dir, @@ -2076,10 +1946,8 @@ def _get_crate_dirname(crate): """ return crate.output.dirname -def _portable_link_flags(lib, use_pic, ambiguous_libs, get_lib_name, for_windows = False, for_darwin = False, flavor_msvc = False): +def _portable_link_flags(lib, use_pic, for_darwin = False): artifact = get_preferred_artifact(lib, use_pic) - if ambiguous_libs and artifact.path in ambiguous_libs: - artifact = ambiguous_libs[artifact.path] if lib.static_library or lib.pic_static_library: # To ensure appropriate linker library argument order, in the presence # of both native libraries that depend on rlibs and rlibs that depend @@ -2114,52 +1982,35 @@ def _portable_link_flags(lib, use_pic, ambiguous_libs, get_lib_name, for_windows artifact.basename.startswith("libtest-") or artifact.basename.startswith("libstd-") or artifact.basename.startswith("test-") or artifact.basename.startswith("std-") ): - return [] if for_darwin else ["-lstatic=%s" % get_lib_name(artifact)] + return [] if for_darwin else ["-Clink-arg=%s" % artifact.path] + + return ["-Clink-arg=%s" % artifact.path] - if for_windows: - if flavor_msvc: - return [ - "-lstatic=%s" % get_lib_name(artifact), - "-Clink-arg={}".format(artifact.basename), - ] - else: - return [ - "-lstatic=%s" % get_lib_name(artifact), - "-Clink-arg=-l{}".format(artifact.basename), - ] - else: - return [ - "-lstatic=%s" % get_lib_name(artifact), - "-Clink-arg=-l{}".format(get_lib_name(artifact)), - ] elif _is_dylib(lib): return [ - "-ldylib=%s" % get_lib_name(artifact), + "-Clink-arg=%s" % artifact.path, ] return [] def _add_user_link_flags(ret, linker_input): - ret.extend(["--codegen=link-arg={}".format(flag) for flag in linker_input.user_link_flags]) + ret.extend(["--codegen=link-arg=%s" % flag for flag in linker_input.user_link_flags]) def _make_link_flags_windows(make_link_flags_args, flavor_msvc): - linker_input, use_pic, ambiguous_libs, include_link_flags = make_link_flags_args + linker_input, use_pic, include_link_flags = make_link_flags_args ret = [] for lib in linker_input.libraries: if lib.alwayslink: if flavor_msvc: - ret.extend(["-C", "link-arg=/WHOLEARCHIVE:%s" % get_preferred_artifact(lib, use_pic).path]) + ret.append("-Clink-arg=/WHOLEARCHIVE:%s" % get_preferred_artifact(lib, use_pic).path) else: ret.extend([ - "-C", - "link-arg=-Wl,--whole-archive", - "-C", - ("link-arg=%s" % get_preferred_artifact(lib, use_pic).path), - "-C", - "link-arg=-Wl,--no-whole-archive", + "-Clink-arg=-Wl,--whole-archive", + "-Clink-arg=%s" % get_preferred_artifact(lib, use_pic).path, + "-Clink-arg=-Wl,--no-whole-archive", ]) elif include_link_flags: - ret.extend(_portable_link_flags(lib, use_pic, ambiguous_libs, get_lib_name_for_windows, for_windows = True, flavor_msvc = flavor_msvc)) + ret.extend(_portable_link_flags(lib, use_pic)) _add_user_link_flags(ret, linker_input) return ret @@ -2170,51 +2021,38 @@ def _make_link_flags_windows_gnu(make_link_flags_args): return _make_link_flags_windows(make_link_flags_args, flavor_msvc = False) def _make_link_flags_darwin(make_link_flags_args): - linker_input, use_pic, ambiguous_libs, include_link_flags = make_link_flags_args + linker_input, use_pic, include_link_flags = make_link_flags_args ret = [] for lib in linker_input.libraries: if lib.alwayslink: - ret.extend([ - "-C", - ("link-arg=-Wl,-force_load,%s" % get_preferred_artifact(lib, use_pic).path), - ]) + ret.append("-Clink-arg=-Wl,-force_load,%s" % get_preferred_artifact(lib, use_pic).path) elif include_link_flags: - ret.extend(_portable_link_flags(lib, use_pic, ambiguous_libs, get_lib_name_default, for_darwin = True)) + ret.extend(_portable_link_flags(lib, use_pic, for_darwin = True)) _add_user_link_flags(ret, linker_input) return ret def _make_link_flags_default(make_link_flags_args): - linker_input, use_pic, ambiguous_libs, include_link_flags = make_link_flags_args + linker_input, use_pic, include_link_flags = make_link_flags_args ret = [] for lib in linker_input.libraries: if lib.alwayslink: ret.extend([ - "-C", - "link-arg=-Wl,--whole-archive", - "-C", - ("link-arg=%s" % get_preferred_artifact(lib, use_pic).path), - "-C", - "link-arg=-Wl,--no-whole-archive", + "-Clink-arg=-Wl,--whole-archive", + "-Clink-arg=%s" % get_preferred_artifact(lib, use_pic).path, + "-Clink-arg=-Wl,--no-whole-archive", ]) elif include_link_flags: - ret.extend(_portable_link_flags(lib, use_pic, ambiguous_libs, get_lib_name_default)) + ret.extend(_portable_link_flags(lib, use_pic)) _add_user_link_flags(ret, linker_input) return ret -def _libraries_dirnames(make_link_flags_args): - link_input, use_pic, _, _ = make_link_flags_args - - # De-duplicate names. - return depset([get_preferred_artifact(lib, use_pic).dirname for lib in link_input.libraries]).to_list() - -def _add_native_link_flags(args, dep_info, linkstamp_outs, ambiguous_libs, crate_type, toolchain, cc_toolchain, feature_configuration, compilation_mode, include_link_flags = True): +def _add_native_link_flags(args, dep_info, linkstamp_outs, crate_type, toolchain, cc_toolchain, feature_configuration, compilation_mode, include_link_flags = True): """Adds linker flags for all dependencies of the current target. Args: args (Args): The Args struct for a ctx.action dep_info (DepInfo): Dependency Info provider linkstamp_outs (list): Linkstamp outputs of native dependencies - ambiguous_libs (dict): Ambiguous libs, see `_disambiguate_libs` crate_type: Crate type of the current target toolchain (rust_toolchain): The current `rust_toolchain` cc_toolchain (CcToolchainInfo): The current `cc_toolchain` @@ -2238,17 +2076,11 @@ def _add_native_link_flags(args, dep_info, linkstamp_outs, ambiguous_libs, crate get_lib_name = get_lib_name_default # TODO(hlopko): Remove depset flattening by using lambdas once we are on >=Bazel 5.0 - make_link_flags_args = [(arg, use_pic, ambiguous_libs, include_link_flags) for arg in dep_info.transitive_noncrates.to_list()] - args.add_all(make_link_flags_args, map_each = _libraries_dirnames, uniquify = True, format_each = "-Lnative=%s") - if ambiguous_libs: - # If there are ambiguous libs, the disambiguation symlinks to them are - # all created in the same directory. Add it to the library search path. - ambiguous_libs_dirname = ambiguous_libs.values()[0].dirname - args.add(ambiguous_libs_dirname, format = "-Lnative=%s") + make_link_flags_args = [(arg, use_pic, include_link_flags) for arg in dep_info.transitive_noncrates.to_list()] args.add_all(make_link_flags_args, map_each = make_link_flags) - args.add_all(linkstamp_outs, before_each = "-C", format_each = "link-args=%s") + args.add_all(linkstamp_outs, format_each = "-Clink-args=%s") if crate_type in ["dylib", "cdylib"]: # For shared libraries we want to link C++ runtime library dynamically diff --git a/rust/private/rustdoc.bzl b/rust/private/rustdoc.bzl index 84ee415321..f15a2abf29 100644 --- a/rust/private/rustdoc.bzl +++ b/rust/private/rustdoc.bzl @@ -103,7 +103,7 @@ def rustdoc_compile_action( aliases = crate_info.aliases, ) - compile_inputs, out_dir, build_env_files, build_flags_files, linkstamp_outs, ambiguous_libs = collect_inputs( + compile_inputs, out_dir, build_env_files, build_flags_files, linkstamp_outs = collect_inputs( ctx = ctx, file = ctx.file, files = ctx.files, @@ -137,7 +137,6 @@ def rustdoc_compile_action( crate_info = rustdoc_crate_info, dep_info = dep_info, linkstamp_outs = linkstamp_outs, - ambiguous_libs = ambiguous_libs, output_hash = None, rust_flags = rustdoc_flags, out_dir = out_dir, diff --git a/rust/private/unpretty.bzl b/rust/private/unpretty.bzl index b7257b6821..f6037f7b59 100644 --- a/rust/private/unpretty.bzl +++ b/rust/private/unpretty.bzl @@ -147,7 +147,7 @@ def _rust_unpretty_aspect_impl(target, ctx): ) lint_files = [] - compile_inputs, out_dir, build_env_files, build_flags_files, linkstamp_outs, ambiguous_libs = collect_inputs( + compile_inputs, out_dir, build_env_files, build_flags_files, linkstamp_outs = collect_inputs( ctx, ctx.rule.file, ctx.rule.files, @@ -196,7 +196,6 @@ def _rust_unpretty_aspect_impl(target, ctx): crate_info = crate_info, dep_info = dep_info, linkstamp_outs = linkstamp_outs, - ambiguous_libs = ambiguous_libs, output_hash = determine_output_hash(crate_info.root, ctx.label), rust_flags = rust_flags, out_dir = out_dir, diff --git a/test/unit/ambiguous_libs/ambiguous_libs_test.bzl b/test/unit/ambiguous_libs/ambiguous_libs_test.bzl index 52a01be39f..3567dcb05f 100644 --- a/test/unit/ambiguous_libs/ambiguous_libs_test.bzl +++ b/test/unit/ambiguous_libs/ambiguous_libs_test.bzl @@ -19,18 +19,22 @@ def _ambiguous_deps_test_impl(ctx): tut = analysistest.target_under_test(env) rustc_action = [action for action in tut.actions if action.mnemonic == "Rustc"][0] + for_shared_library = _get_crate_info(tut).type in ("dylib", "cdylib", "proc-macro") + pic_suffix = _get_pic_suffix(ctx, for_shared_library) + # We depend on two C++ libraries named "native_dep", which we need to pass to the command line - # in the form of "-lstatic=native-dep-{hash} "-lstatic=native-dep-{hash}.pic. - link_args = [arg for arg in rustc_action.argv if arg.startswith("-lstatic=native_dep-")] + # in the form of "-Clink-arg=${pwd}/bazel-out/darwin_arm64-fastbuild/bin/test/unit/ambiguous_libs/first_dep/libnative_dep.a" + if ctx.target_platform_has_constraint( + ctx.attr._windows_constraint[platform_common.ConstraintValueInfo], + ): + lib_name = "native_dep{}.lib".format(pic_suffix) + else: + lib_name = "libnative_dep{}.a".format(pic_suffix) + + link_args = [arg for arg in rustc_action.argv if arg.endswith(lib_name)] asserts.equals(env, 2, len(link_args)) asserts.false(env, link_args[0] == link_args[1]) - for_shared_library = _get_crate_info(tut).type in ("dylib", "cdylib", "proc-macro") - extension = _get_pic_suffix(ctx, for_shared_library) - - asserts.true(env, link_args[0].endswith(extension)) - asserts.true(env, link_args[1].endswith(extension)) - return analysistest.end(env) def _get_pic_suffix(ctx, for_shared_library): diff --git a/test/unit/common.bzl b/test/unit/common.bzl index 4099e8dd13..5d57a2abbc 100644 --- a/test/unit/common.bzl +++ b/test/unit/common.bzl @@ -29,6 +29,18 @@ def assert_argv_contains_prefix_suffix(env, action, prefix, suffix): ), ) +def assert_argv_contains_prefix_suffix_not(env, action, prefix, suffix): + for found_flag in action.argv: + if found_flag.startswith(prefix) and found_flag.endswith(suffix): + unittest.fail( + env, + "Expected an arg with prefix '{prefix}' and suffix '{suffix}' to not appear in {args}".format( + prefix = prefix, + suffix = suffix, + args = action.argv, + ), + ) + def assert_argv_contains_prefix(env, action, prefix): for found_flag in action.argv: if found_flag.startswith(prefix): diff --git a/test/unit/linker_inputs_propagation/linker_inputs_propagation_test.bzl b/test/unit/linker_inputs_propagation/linker_inputs_propagation_test.bzl index ba8f4166ff..62715177a7 100644 --- a/test/unit/linker_inputs_propagation/linker_inputs_propagation_test.bzl +++ b/test/unit/linker_inputs_propagation/linker_inputs_propagation_test.bzl @@ -30,7 +30,19 @@ def _dependency_linkopts_are_propagated_test_impl(ctx): # Expect a library's own linkopts to come after the flags we create to link them. # This is required, because linkopts are ordered and the linker will only apply later ones when resolving symbols required for earlier ones. # This means that if one of our transitive deps has a linkopt like `-lfoo`, the dep will see the symbols of foo at link time. - _assert_contains_in_order(env, link_action.argv, ["-lstatic=foo_with_linkopts", "-Clink-arg=-lfoo_with_linkopts", "--codegen=link-arg=-L/doesnotexist"]) + link_flag_index = -1 + for i, arg in enumerate(link_action.argv): + if arg.startswith("-Clink-arg=bazel-out") and arg.endswith("test/linker_inputs_propagation/libfoo_with_linkopts.a"): + link_flag_index = i + break + if link_flag_index == -1: + unittest.fail(env, "Link flag not found") + + linkopt_index = link_action.argv.index("--codegen=link-arg=-L/doesnotexist") + + if linkopt_index < link_flag_index: + unittest.fail(env, "Flags out of order") + return analysistest.end(env) def _assert_contains_input(env, inputs, name): @@ -41,12 +53,6 @@ def _assert_contains_input(env, inputs, name): return unittest.fail(env, "Expected {} to contain a library starting with {}".format(inputs.to_list(), name)) -def _assert_contains_in_order(env, haystack, needle): - for i in range(len(haystack)): - if haystack[i:i + len(needle)] == needle: - return - unittest.fail(env, "Expected {} to contain {}".format(haystack, needle)) - def _get_lib_name(ctx, name): if ctx.target_platform_has_constraint(ctx.attr._windows_constraint[platform_common.ConstraintValueInfo]): return name diff --git a/test/unit/native_deps/native_deps_test.bzl b/test/unit/native_deps/native_deps_test.bzl index ab97be6598..cb99b1701f 100644 --- a/test/unit/native_deps/native_deps_test.bzl +++ b/test/unit/native_deps/native_deps_test.bzl @@ -6,10 +6,10 @@ load("//rust:defs.bzl", "rust_binary", "rust_library", "rust_proc_macro", "rust_ load( "//test/unit:common.bzl", "assert_argv_contains", - "assert_argv_contains_not", "assert_argv_contains_prefix", "assert_argv_contains_prefix_not", "assert_argv_contains_prefix_suffix", + "assert_argv_contains_prefix_suffix_not", "assert_list_contains_adjacent_elements", ) @@ -28,8 +28,7 @@ def _rlib_has_no_native_libs_test_impl(ctx): tut = analysistest.target_under_test(env) action = tut.actions[0] assert_argv_contains(env, action, "--crate-type=rlib") - assert_argv_contains_not(env, action, "-lstatic=native_dep") - assert_argv_contains_not(env, action, "-ldylib=native_dep") + assert_argv_contains_prefix_suffix_not(env, action, "-Clink-arg=bazel-out/", "test/unit/native_deps/libnative_dep.a") assert_argv_contains_prefix_not(env, action, "--codegen=linker=") return analysistest.end(env) @@ -40,17 +39,15 @@ def _cdylib_has_native_libs_test_impl(ctx): toolchain = _get_toolchain(ctx) compilation_mode = ctx.var["COMPILATION_MODE"] pic_suffix = _get_pic_suffix(ctx, compilation_mode) - assert_argv_contains_prefix_suffix(env, action, "-Lnative=", "/native_deps") assert_argv_contains(env, action, "--crate-type=cdylib") - assert_argv_contains(env, action, "-lstatic=native_dep{}".format(pic_suffix)) - if toolchain.target_os == "windows": - if toolchain.target_triple.abi == "msvc": - native_link_arg = "-Clink-arg=native_dep.lib" - else: - native_link_arg = "-Clink-arg=-lnative_dep.lib" + + if toolchain.target_os == "windows" and toolchain.target_triple.abi == "msvc": + assert_argv_contains_prefix_suffix(env, action, "-Lnative=", "/native_deps") + assert_argv_contains(env, action, "-lstatic=native_dep{}".format(pic_suffix)) + assert_argv_contains(env, action, "-Clink-arg=native_dep.lib") else: - native_link_arg = "-Clink-arg=-lnative_dep{}".format(pic_suffix) - assert_argv_contains(env, action, native_link_arg) + assert_argv_contains_prefix_suffix(env, action, "-Clink-arg=bazel-out/", "test/unit/native_deps/libnative_dep{}.a".format(pic_suffix)) + assert_argv_contains_prefix(env, action, "--codegen=linker=") return analysistest.end(env) @@ -59,17 +56,14 @@ def _staticlib_has_native_libs_test_impl(ctx): tut = analysistest.target_under_test(env) action = tut.actions[0] toolchain = _get_toolchain(ctx) - assert_argv_contains_prefix_suffix(env, action, "-Lnative=", "/native_deps") - assert_argv_contains(env, action, "--crate-type=staticlib") - assert_argv_contains(env, action, "-lstatic=native_dep") - if toolchain.target_os == "windows": - if toolchain.target_triple.abi == "msvc": - native_link_arg = "-Clink-arg=native_dep.lib" - else: - native_link_arg = "-Clink-arg=-lnative_dep.lib" + + if toolchain.target_os == "windows" and toolchain.target_triple.abi == "msvc": + assert_argv_contains_prefix_suffix(env, action, "-Lnative=", "/native_deps") + assert_argv_contains(env, action, "-lstatic=native_dep") + assert_argv_contains(env, action, "-Clink-arg=native_dep.lib") else: - native_link_arg = "-Clink-arg=-lnative_dep" - assert_argv_contains(env, action, native_link_arg) + assert_argv_contains_prefix_suffix(env, action, "-Clink-arg=bazel-out/", "test/unit/native_deps/libnative_dep.a") + assert_argv_contains_prefix(env, action, "--codegen=linker=") return analysistest.end(env) @@ -80,17 +74,15 @@ def _proc_macro_has_native_libs_test_impl(ctx): toolchain = _get_toolchain(ctx) compilation_mode = ctx.var["COMPILATION_MODE"] pic_suffix = _get_pic_suffix(ctx, compilation_mode) - assert_argv_contains_prefix_suffix(env, action, "-Lnative=", "/native_deps") assert_argv_contains(env, action, "--crate-type=proc-macro") - assert_argv_contains(env, action, "-lstatic=native_dep{}".format(pic_suffix)) - if toolchain.target_os == "windows": - if toolchain.target_triple.abi == "msvc": - native_link_arg = "-Clink-arg=native_dep.lib" - else: - native_link_arg = "-Clink-arg=-lnative_dep.lib" + + if toolchain.target_os == "windows" and toolchain.target_triple.abi == "msvc": + assert_argv_contains_prefix_suffix(env, action, "-Lnative=", "/native_deps") + assert_argv_contains(env, action, "-lstatic=native_dep{}".format(pic_suffix)) + assert_argv_contains(env, action, "-Clink-arg=native_dep.lib") else: - native_link_arg = "-Clink-arg=-lnative_dep{}".format(pic_suffix) - assert_argv_contains(env, action, native_link_arg) + assert_argv_contains_prefix_suffix(env, action, "-Clink-arg=bazel-out/", "test/unit/native_deps/libnative_dep{}.a".format(pic_suffix)) + assert_argv_contains_prefix(env, action, "--codegen=linker=") return analysistest.end(env) @@ -99,16 +91,11 @@ def _bin_has_native_libs_test_impl(ctx): tut = analysistest.target_under_test(env) action = tut.actions[0] toolchain = _get_toolchain(ctx) - assert_argv_contains_prefix_suffix(env, action, "-Lnative=", "/native_deps") - assert_argv_contains(env, action, "-lstatic=native_dep") - if toolchain.target_os == "windows": - if toolchain.target_triple.abi == "msvc": - native_link_arg = "-Clink-arg=native_dep.lib" - else: - native_link_arg = "-Clink-arg=-lnative_dep.lib" + if toolchain.target_os == "windows" and toolchain.target_triple.abi == "msvc": + assert_argv_contains(env, action, "-lstatic=native_dep") + assert_argv_contains(env, action, "-Clink-arg=native_dep.lib") else: - native_link_arg = "-Clink-arg=-lnative_dep" - assert_argv_contains(env, action, native_link_arg) + assert_argv_contains_prefix_suffix(env, action, "-Clink-arg=bazel-out/", "test/unit/native_deps/libnative_dep.a") assert_argv_contains_prefix(env, action, "--codegen=linker=") return analysistest.end(env) @@ -144,8 +131,7 @@ def _bin_has_native_dep_and_alwayslink_test_impl(ctx): if toolchain.target_os in ["macos", "darwin"]: darwin_component = _get_darwin_component(link_args[-1]) want = [ - "-lstatic=native_dep", - "-lnative_dep", + "bazel-out/{}-{}/bin/{}test/unit/native_deps/libnative_dep.a".format(darwin_component, compilation_mode, workspace_prefix), "-Wl,-force_load,bazel-out/{}-{}/bin/{}test/unit/native_deps/libalwayslink.lo".format(darwin_component, compilation_mode, workspace_prefix), ] assert_list_contains_adjacent_elements(env, link_args, want) @@ -158,23 +144,21 @@ def _bin_has_native_dep_and_alwayslink_test_impl(ctx): ] else: want = [ - "-lstatic=native_dep", - "native_dep.lib", + "bazel-out/x64_windows-{}/bin/{}test/unit/native_deps/libnative_dep.a".format(compilation_mode, workspace_prefix), "-Wl,--whole-archive", "bazel-out/x64_windows-{}/bin/{}test/unit/native_deps/alwayslink.lo.lib".format(compilation_mode, workspace_prefix), "-Wl,--no-whole-archive", ] elif toolchain.target_arch == "s390x": want = [ - "-lstatic=native_dep", + "bazel-out/s390x-{}/bin/{}test/unit/native_deps/libnative_dep.a".format(compilation_mode, workspace_prefix), "link-arg=-Wl,--whole-archive", "link-arg=bazel-out/s390x-{}/bin/{}test/unit/native_deps/libalwayslink.lo".format(compilation_mode, workspace_prefix), "link-arg=-Wl,--no-whole-archive", ] else: want = [ - "-lstatic=native_dep", - "-lnative_dep", + "bazel-out/k8-{}/bin/{}test/unit/native_deps/libnative_dep.a".format(compilation_mode, workspace_prefix), "-Wl,--whole-archive", "bazel-out/k8-{}/bin/{}test/unit/native_deps/libalwayslink.lo".format(compilation_mode, workspace_prefix), "-Wl,--no-whole-archive", @@ -198,8 +182,7 @@ def _cdylib_has_native_dep_and_alwayslink_test_impl(ctx): if toolchain.target_os in ["macos", "darwin"]: darwin_component = _get_darwin_component(linker_args[-1]) want = [ - "-lstatic=native_dep{}".format(pic_suffix), - "-lnative_dep{}".format(pic_suffix), + "bazel-out/{}-{}/bin/{}test/unit/native_deps/libnative_dep{}.a".format(darwin_component, compilation_mode, workspace_prefix, pic_suffix), "-Wl,-force_load,bazel-out/{}-{}/bin/{}test/unit/native_deps/libalwayslink{}.lo".format(darwin_component, compilation_mode, workspace_prefix, pic_suffix), ] elif toolchain.target_os == "windows": @@ -211,23 +194,21 @@ def _cdylib_has_native_dep_and_alwayslink_test_impl(ctx): ] else: want = [ - "-lstatic=native_dep", - "native_dep.lib", + "bazel-out/x64_windows-{}/bin/{}test/unit/native_deps/libnative_dep{}.a".format(compilation_mode, workspace_prefix, pic_suffix), "-Wl,--whole-archive", "bazel-out/x64_windows-{}/bin/{}test/unit/native_deps/alwayslink.lo.lib".format(compilation_mode, workspace_prefix), "-Wl,--no-whole-archive", ] elif toolchain.target_arch == "s390x": want = [ - "-lstatic=native_dep{}".format(pic_suffix), + "bazel-out/s390x-{}/bin/{}test/unit/native_deps/libnative_dep{}.a".format(compilation_mode, workspace_prefix, pic_suffix), "link-arg=-Wl,--whole-archive", "link-arg=bazel-out/s390x-{}/bin/{}test/unit/native_deps/libalwayslink{}.lo".format(compilation_mode, workspace_prefix, pic_suffix), "link-arg=-Wl,--no-whole-archive", ] else: want = [ - "-lstatic=native_dep{}".format(pic_suffix), - "-lnative_dep{}".format(pic_suffix), + "bazel-out/k8-{}/bin/{}test/unit/native_deps/libnative_dep{}.a".format(compilation_mode, workspace_prefix, pic_suffix), "-Wl,--whole-archive", "bazel-out/k8-{}/bin/{}test/unit/native_deps/libalwayslink{}.lo".format(compilation_mode, workspace_prefix, pic_suffix), "-Wl,--no-whole-archive", diff --git a/test/unit/proc_macro/leaks_deps/proc_macro_does_not_leak_deps.bzl b/test/unit/proc_macro/leaks_deps/proc_macro_does_not_leak_deps.bzl index f246e1ddc0..c2d5687714 100644 --- a/test/unit/proc_macro/leaks_deps/proc_macro_does_not_leak_deps.bzl +++ b/test/unit/proc_macro/leaks_deps/proc_macro_does_not_leak_deps.bzl @@ -3,9 +3,6 @@ load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") load("//rust:defs.bzl", "rust_library", "rust_proc_macro", "rust_test") -def _get_toolchain(ctx): - return ctx.attr._toolchain[platform_common.ToolchainInfo] - def _proc_macro_does_not_leak_deps_impl(ctx): env = analysistest.begin(ctx) actions = analysistest.target_under_test(env).actions @@ -14,7 +11,6 @@ def _proc_macro_does_not_leak_deps_impl(ctx): if action.mnemonic == "Rustc": rustc_action = action break - toolchain = _get_toolchain(ctx) asserts.false(env, rustc_action == None) @@ -29,13 +25,7 @@ def _proc_macro_does_not_leak_deps_impl(ctx): # Our test target depends on proc_macro_dep:native directly, as well as transitively through the # proc_macro. The proc_macro should not leak its dependency, so we should only get the "native" # library once on the command line. - if toolchain.target_os == "windows": - if toolchain.target_triple.abi == "msvc": - native_deps = [arg for arg in rustc_action.argv if arg == "-Clink-arg=native.lib"] - else: - native_deps = [arg for arg in rustc_action.argv if arg == "-Clink-arg=-lnative.lib"] - else: - native_deps = [arg for arg in rustc_action.argv if arg == "-Clink-arg=-lnative"] + native_deps = [arg for arg in rustc_action.argv if arg.endswith("test/unit/proc_macro/leaks_deps/native/libnative.a")] asserts.equals(env, 1, len(native_deps)) return analysistest.end(env) @@ -76,9 +66,7 @@ def _proc_macro_does_not_leak_deps_test(): target_under_test = ":deps_not_leaked", ) -proc_macro_does_not_leak_deps_test = analysistest.make(_proc_macro_does_not_leak_deps_impl, attrs = { - "_toolchain": attr.label(default = Label("//rust/toolchain:current_rust_toolchain")), -}) +proc_macro_does_not_leak_deps_test = analysistest.make(_proc_macro_does_not_leak_deps_impl) # Tests that a lib_a -> proc_macro -> lib_b does not propagate lib_b to the inputs of lib_a def _proc_macro_does_not_leak_lib_deps_impl(ctx): diff --git a/test/unit/rustdoc/rustdoc_unit_test.bzl b/test/unit/rustdoc/rustdoc_unit_test.bzl index aa1f9b2f02..c0fe3a182e 100644 --- a/test/unit/rustdoc/rustdoc_unit_test.bzl +++ b/test/unit/rustdoc/rustdoc_unit_test.bzl @@ -294,9 +294,6 @@ def _define_targets(): hdrs = ["rustdoc.h"], srcs = ["rustdoc.cc"], deps = [":cc_lib"], - # This is not needed for :cc_lib, but it is needed in other - # circumstances to link in system libraries. - linkopts = ["-lcc_lib"], linkstatic = True, ) diff --git a/test/unit/versioned_libs/versioned_libs_analysis_test.bzl b/test/unit/versioned_libs/versioned_libs_analysis_test.bzl index 4545b14e13..7621933ef5 100644 --- a/test/unit/versioned_libs/versioned_libs_analysis_test.bzl +++ b/test/unit/versioned_libs/versioned_libs_analysis_test.bzl @@ -7,15 +7,28 @@ load("//rust:defs.bzl", "rust_shared_library") LIBNAMES = ["sterling", "cheryl", "lana", "pam", "malory", "cyril"] -def _is_in_argv(argv, version = None): - return any(["-ldylib={}{}".format(name, version or "") in argv for name in LIBNAMES]) +def _is_in_argv(ctx, argv, version = ""): + toolchain = ctx.attr._toolchain[platform_common.ToolchainInfo] + if toolchain.target_os in ["macos", "darwin"]: + prefix = "lib" + suffix = "dylib" + elif toolchain.target_os == "windows": + prefix = "" + suffix = "lib" + else: + prefix = "lib" + suffix = "so" + + print(argv) + pattern = prefix + "{}" + version + "." + suffix + return any([arg.endswith(pattern.format(name)) for arg in argv for name in LIBNAMES]) def _no_version_test_impl(ctx): env = analysistest.begin(ctx) tut = analysistest.target_under_test(env) argv = tut.actions[0].argv - asserts.true(env, _is_in_argv(argv)) + asserts.true(env, _is_in_argv(ctx, argv)) return analysistest.end(env) @@ -24,7 +37,7 @@ def _prefix_version_test_impl(ctx): tut = analysistest.target_under_test(env) argv = tut.actions[0].argv - asserts.true(env, _is_in_argv(argv, "3.8")) + asserts.true(env, _is_in_argv(ctx, argv, "3.8")) return analysistest.end(env) @@ -33,13 +46,19 @@ def _suffix_version_test_impl(ctx): tut = analysistest.target_under_test(env) argv = tut.actions[0].argv - asserts.true(env, _is_in_argv(argv)) + asserts.true(env, _is_in_argv(ctx, argv)) return analysistest.end(env) -no_version_test = analysistest.make(_no_version_test_impl) -prefix_version_test = analysistest.make(_prefix_version_test_impl) -suffix_version_test = analysistest.make(_suffix_version_test_impl) +no_version_test = analysistest.make(_no_version_test_impl, attrs = { + "_toolchain": attr.label(default = Label("//rust/toolchain:current_rust_toolchain")), +}) +prefix_version_test = analysistest.make(_prefix_version_test_impl, attrs = { + "_toolchain": attr.label(default = Label("//rust/toolchain:current_rust_toolchain")), +}) +suffix_version_test = analysistest.make(_suffix_version_test_impl, attrs = { + "_toolchain": attr.label(default = Label("//rust/toolchain:current_rust_toolchain")), +}) def _test_linux(): rust_shared_library(