1"""Unittests for rust rules.""" 2 3load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") 4load("//rust:defs.bzl", "rust_binary", "rust_library", "rust_proc_macro") 5load("//test/unit:common.bzl", "assert_argv_contains", "assert_list_contains_adjacent_elements", "assert_list_contains_adjacent_elements_not") 6load(":wrap.bzl", "wrap") 7 8NOT_WINDOWS = select({ 9 "@platforms//os:linux": [], 10 "@platforms//os:macos": [], 11 "//conditions:default": ["@platforms//:incompatible"], 12}) 13 14ENABLE_PIPELINING = { 15 str(Label("//rust/settings:pipelined_compilation")): True, 16} 17 18def _second_lib_test_impl(ctx): 19 env = analysistest.begin(ctx) 20 tut = analysistest.target_under_test(env) 21 rlib_action = [act for act in tut.actions if act.mnemonic == "Rustc"][0] 22 metadata_action = [act for act in tut.actions if act.mnemonic == "RustcMetadata"][0] 23 24 # Both actions should use the same --emit= 25 assert_argv_contains(env, rlib_action, "--emit=dep-info,link,metadata") 26 assert_argv_contains(env, metadata_action, "--emit=dep-info,link,metadata") 27 28 # The metadata action should have a .rmeta as output and the rlib action a .rlib 29 path = rlib_action.outputs.to_list()[0].path 30 asserts.true( 31 env, 32 path.endswith(".rlib"), 33 "expected Rustc to output .rlib, got " + path, 34 ) 35 path = metadata_action.outputs.to_list()[0].path 36 asserts.true( 37 env, 38 path.endswith(".rmeta"), 39 "expected RustcMetadata to output .rmeta, got " + path, 40 ) 41 42 # Only the action building metadata should contain --rustc-quit-on-rmeta 43 assert_list_contains_adjacent_elements_not(env, rlib_action.argv, ["--rustc-quit-on-rmeta", "true"]) 44 assert_list_contains_adjacent_elements(env, metadata_action.argv, ["--rustc-quit-on-rmeta", "true"]) 45 46 # Check that both actions refer to the metadata of :first, not the rlib 47 extern_metadata = [arg for arg in metadata_action.argv if arg.startswith("--extern=first=") and "libfirst" in arg and arg.endswith(".rmeta")] 48 asserts.true( 49 env, 50 len(extern_metadata) == 1, 51 "did not find a --extern=first=*.rmeta but expected one", 52 ) 53 extern_rlib = [arg for arg in rlib_action.argv if arg.startswith("--extern=first=") and "libfirst" in arg and arg.endswith(".rmeta")] 54 asserts.true( 55 env, 56 len(extern_rlib) == 1, 57 "did not find a --extern=first=*.rlib but expected one", 58 ) 59 60 # Check that the input to both actions is the metadata of :first 61 input_metadata = [i for i in metadata_action.inputs.to_list() if i.basename.startswith("libfirst")] 62 asserts.true(env, len(input_metadata) == 1, "expected only one libfirst input, found " + str([i.path for i in input_metadata])) 63 asserts.true(env, input_metadata[0].extension == "rmeta", "expected libfirst dependency to be rmeta, found " + input_metadata[0].path) 64 input_rlib = [i for i in rlib_action.inputs.to_list() if i.basename.startswith("libfirst")] 65 asserts.true(env, len(input_rlib) == 1, "expected only one libfirst input, found " + str([i.path for i in input_rlib])) 66 asserts.true(env, input_rlib[0].extension == "rmeta", "expected libfirst dependency to be rmeta, found " + input_rlib[0].path) 67 68 return analysistest.end(env) 69 70def _bin_test_impl(ctx): 71 env = analysistest.begin(ctx) 72 tut = analysistest.target_under_test(env) 73 bin_action = [act for act in tut.actions if act.mnemonic == "Rustc"][0] 74 75 # Check that no inputs to this binary are .rmeta files. 76 metadata_inputs = [i.path for i in bin_action.inputs.to_list() if i.path.endswith(".rmeta")] 77 asserts.false(env, metadata_inputs, "expected no metadata inputs, found " + str(metadata_inputs)) 78 79 return analysistest.end(env) 80 81bin_test = analysistest.make(_bin_test_impl, config_settings = ENABLE_PIPELINING) 82second_lib_test = analysistest.make(_second_lib_test_impl, config_settings = ENABLE_PIPELINING) 83 84def _pipelined_compilation_test(): 85 rust_proc_macro( 86 name = "my_macro", 87 edition = "2021", 88 srcs = ["my_macro.rs"], 89 ) 90 91 rust_library( 92 name = "first", 93 edition = "2021", 94 srcs = ["first.rs"], 95 ) 96 97 rust_library( 98 name = "second", 99 edition = "2021", 100 srcs = ["second.rs"], 101 deps = [":first"], 102 proc_macro_deps = [":my_macro"], 103 ) 104 105 rust_binary( 106 name = "bin", 107 edition = "2021", 108 srcs = ["bin.rs"], 109 deps = [":second"], 110 ) 111 112 second_lib_test(name = "second_lib_test", target_under_test = ":second", target_compatible_with = NOT_WINDOWS) 113 bin_test(name = "bin_test", target_under_test = ":bin", target_compatible_with = NOT_WINDOWS) 114 115def _rmeta_is_propagated_through_custom_rule_test_impl(ctx): 116 env = analysistest.begin(ctx) 117 tut = analysistest.target_under_test(env) 118 119 # This is the metadata-generating action. It should depend on metadata for the library and, if generate_metadata is set 120 # also depend on metadata for 'wrapper'. 121 rust_action = [act for act in tut.actions if act.mnemonic == "RustcMetadata"][0] 122 123 metadata_inputs = [i for i in rust_action.inputs.to_list() if i.path.endswith(".rmeta")] 124 rlib_inputs = [i for i in rust_action.inputs.to_list() if i.path.endswith(".rlib")] 125 126 seen_wrapper_metadata = False 127 seen_to_wrap_metadata = False 128 for mi in metadata_inputs: 129 if "libwrapper" in mi.path: 130 seen_wrapper_metadata = True 131 if "libto_wrap" in mi.path: 132 seen_to_wrap_metadata = True 133 134 seen_wrapper_rlib = False 135 seen_to_wrap_rlib = False 136 for ri in rlib_inputs: 137 if "libwrapper" in ri.path: 138 seen_wrapper_rlib = True 139 if "libto_wrap" in ri.path: 140 seen_to_wrap_rlib = True 141 142 if ctx.attr.generate_metadata: 143 asserts.true(env, seen_wrapper_metadata, "expected dependency on metadata for 'wrapper' but not found") 144 asserts.false(env, seen_wrapper_rlib, "expected no dependency on object for 'wrapper' but it was found") 145 else: 146 asserts.true(env, seen_wrapper_rlib, "expected dependency on object for 'wrapper' but not found") 147 asserts.false(env, seen_wrapper_metadata, "expected no dependency on metadata for 'wrapper' but it was found") 148 149 asserts.true(env, seen_to_wrap_metadata, "expected dependency on metadata for 'to_wrap' but not found") 150 asserts.false(env, seen_to_wrap_rlib, "expected no dependency on object for 'to_wrap' but it was found") 151 152 return analysistest.end(env) 153 154def _rmeta_is_used_when_building_custom_rule_test_impl(ctx): 155 env = analysistest.begin(ctx) 156 tut = analysistest.target_under_test(env) 157 158 # This is the custom rule invocation of rustc. 159 rust_action = [act for act in tut.actions if act.mnemonic == "Rustc"][0] 160 161 # We want to check that the action depends on metadata, regardless of ctx.attr.generate_metadata 162 seen_to_wrap_rlib = False 163 seen_to_wrap_rmeta = False 164 for act in rust_action.inputs.to_list(): 165 if "libto_wrap" in act.path and act.path.endswith(".rlib"): 166 seen_to_wrap_rlib = True 167 elif "libto_wrap" in act.path and act.path.endswith(".rmeta"): 168 seen_to_wrap_rmeta = True 169 170 asserts.true(env, seen_to_wrap_rmeta, "expected dependency on metadata for 'to_wrap' but not found") 171 asserts.false(env, seen_to_wrap_rlib, "expected no dependency on object for 'to_wrap' but it was found") 172 173 return analysistest.end(env) 174 175rmeta_is_propagated_through_custom_rule_test = analysistest.make(_rmeta_is_propagated_through_custom_rule_test_impl, attrs = {"generate_metadata": attr.bool()}, config_settings = ENABLE_PIPELINING) 176rmeta_is_used_when_building_custom_rule_test = analysistest.make(_rmeta_is_used_when_building_custom_rule_test_impl, config_settings = ENABLE_PIPELINING) 177 178def _rmeta_not_produced_if_pipelining_disabled_test_impl(ctx): 179 env = analysistest.begin(ctx) 180 tut = analysistest.target_under_test(env) 181 182 rust_action = [act for act in tut.actions if act.mnemonic == "RustcMetadata"] 183 asserts.true(env, len(rust_action) == 0, "expected no metadata to be produced, but found a metadata action") 184 185 return analysistest.end(env) 186 187rmeta_not_produced_if_pipelining_disabled_test = analysistest.make(_rmeta_not_produced_if_pipelining_disabled_test_impl, config_settings = ENABLE_PIPELINING) 188 189def _disable_pipelining_test(): 190 rust_library( 191 name = "lib", 192 srcs = ["custom_rule_test/to_wrap.rs"], 193 edition = "2021", 194 disable_pipelining = True, 195 ) 196 rmeta_not_produced_if_pipelining_disabled_test( 197 name = "rmeta_not_produced_if_pipelining_disabled_test", 198 target_compatible_with = NOT_WINDOWS, 199 target_under_test = ":lib", 200 ) 201 202def _custom_rule_test(generate_metadata, suffix): 203 rust_library( 204 name = "to_wrap" + suffix, 205 crate_name = "to_wrap", 206 srcs = ["custom_rule_test/to_wrap.rs"], 207 edition = "2021", 208 ) 209 wrap( 210 name = "wrapper" + suffix, 211 crate_name = "wrapper", 212 target = ":to_wrap" + suffix, 213 generate_metadata = generate_metadata, 214 ) 215 rust_library( 216 name = "uses_wrapper" + suffix, 217 srcs = ["custom_rule_test/uses_wrapper.rs"], 218 deps = [":wrapper" + suffix], 219 edition = "2021", 220 ) 221 222 rmeta_is_propagated_through_custom_rule_test( 223 name = "rmeta_is_propagated_through_custom_rule_test" + suffix, 224 generate_metadata = generate_metadata, 225 target_compatible_with = NOT_WINDOWS, 226 target_under_test = ":uses_wrapper" + suffix, 227 ) 228 229 rmeta_is_used_when_building_custom_rule_test( 230 name = "rmeta_is_used_when_building_custom_rule_test" + suffix, 231 target_compatible_with = NOT_WINDOWS, 232 target_under_test = ":wrapper" + suffix, 233 ) 234 235def pipelined_compilation_test_suite(name): 236 """Entry-point macro called from the BUILD file. 237 238 Args: 239 name: Name of the macro. 240 """ 241 _pipelined_compilation_test() 242 _disable_pipelining_test() 243 _custom_rule_test(generate_metadata = True, suffix = "_with_metadata") 244 _custom_rule_test(generate_metadata = False, suffix = "_without_metadata") 245 246 native.test_suite( 247 name = name, 248 tests = [ 249 ":bin_test", 250 ":second_lib_test", 251 ":rmeta_is_propagated_through_custom_rule_test_with_metadata", 252 ":rmeta_is_propagated_through_custom_rule_test_without_metadata", 253 ":rmeta_is_used_when_building_custom_rule_test_with_metadata", 254 ":rmeta_is_used_when_building_custom_rule_test_without_metadata", 255 ":rmeta_not_produced_if_pipelining_disabled_test", 256 ], 257 ) 258