| # Copyright 2026 The IREE Authors | |
| # | |
| # Licensed under the Apache License v2.0 with LLVM Exceptions. | |
| # See https://llvm.org/LICENSE.txt for license information. | |
| # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| """Platform-adaptive test rule. | |
| iree_cc_test wraps a cc_binary as a test target. On native platforms it | |
| symlinks to the binary. On wasm32 platforms it bundles the .wasm with a | |
| WASI test entry point and runs via Node.js. | |
| On wasm32, the entry point is determined by: | |
| 1. If an iree_wasm_entry target is found in cc_deps, its main JS file | |
| is used as the entry point (with its srcs as bundler sandbox inputs). | |
| 2. Otherwise, the default wasm_test_main.mjs generic WASI harness is used. | |
| """ | |
| load( | |
| "//build_tools/bazel:iree_wasm_library.bzl", | |
| "IreeWasmEntryCollectionInfo", | |
| "collect_and_bundle", | |
| "collect_wasm_js", | |
| ) | |
| def _iree_cc_test_impl(ctx): | |
| is_wasm = ctx.target_platform_has_constraint( | |
| ctx.attr._wasm32_constraint[platform_common.ConstraintValueInfo], | |
| ) | |
| binary_info = ctx.attr.binary[DefaultInfo] | |
| binary_executable = binary_info.files_to_run.executable | |
| # Collect data files into runfiles. | |
| data_runfiles = ctx.runfiles(files = ctx.files.data) | |
| # Build environment dict, expanding $(rootpath) for data labels. | |
| env = {} | |
| for key, value in ctx.attr.env.items(): | |
| env[key] = ctx.expand_location(value, ctx.attr.data) | |
| if not is_wasm: | |
| # Native: symlink to the cc_binary executable. | |
| executable = ctx.actions.declare_file(ctx.label.name) | |
| ctx.actions.symlink( | |
| output = executable, | |
| target_file = binary_executable, | |
| ) | |
| runfiles = ctx.runfiles().merge(binary_info.default_runfiles).merge(data_runfiles) | |
| return [ | |
| DefaultInfo( | |
| executable = executable, | |
| runfiles = runfiles, | |
| ), | |
| testing.TestEnvironment(env), | |
| ] | |
| # Wasm: discover entry point from deps, falling back to the generic | |
| # WASI test harness if no iree_wasm_entry target is present. | |
| main_js = None | |
| main_srcs = [] | |
| for dep in ctx.attr.cc_deps: | |
| if IreeWasmEntryCollectionInfo in dep: | |
| for entry in dep[IreeWasmEntryCollectionInfo].entries.to_list(): | |
| if main_js != None: | |
| fail("Multiple iree_wasm_entry targets found in deps " + | |
| "of %s; expected at most one" % ctx.label) | |
| main_js = entry.main | |
| main_srcs = list(entry.srcs) | |
| if main_js == None: | |
| main_js = ctx.file._test_main | |
| output_mjs = collect_and_bundle( | |
| ctx = ctx, | |
| wasm_binary = binary_executable, | |
| main_js = main_js, | |
| cc_deps = ctx.attr.cc_deps, | |
| bundler = ctx.executable._bundler, | |
| main_srcs = main_srcs, | |
| ) | |
| # Shell wrapper: locates the bundled .mjs in the runfiles tree and | |
| # executes it via node. Test arguments are forwarded. | |
| executable = ctx.actions.declare_file(ctx.label.name) | |
| wrapper_content = ( | |
| "#!/bin/bash\n" + | |
| 'RUNFILES="${{RUNFILES_DIR:-$0.runfiles}}"\n' + | |
| 'exec node "$RUNFILES/{workspace}/{mjs_path}" "$@"\n' | |
| ).format( | |
| workspace = ctx.workspace_name, | |
| mjs_path = output_mjs.short_path, | |
| ) | |
| ctx.actions.write( | |
| output = executable, | |
| content = wrapper_content, | |
| is_executable = True, | |
| ) | |
| runfiles = ctx.runfiles( | |
| files = [binary_executable, output_mjs], | |
| ).merge(binary_info.default_runfiles) | |
| return [DefaultInfo( | |
| executable = executable, | |
| runfiles = runfiles, | |
| )] | |
| iree_cc_test = rule( | |
| implementation = _iree_cc_test_impl, | |
| test = True, | |
| attrs = { | |
| "binary": attr.label( | |
| mandatory = True, | |
| providers = [DefaultInfo], | |
| doc = "The cc_binary target to wrap as a test.", | |
| ), | |
| "cc_deps": attr.label_list( | |
| aspects = [collect_wasm_js], | |
| doc = "Same deps as the cc_binary (for wasm JS companion collection).", | |
| ), | |
| "data": attr.label_list( | |
| allow_files = True, | |
| doc = "Data files available to the test at runtime.", | |
| ), | |
| "env": attr.string_dict( | |
| doc = "Environment variables set for the test.", | |
| ), | |
| "_wasm32_constraint": attr.label( | |
| default = "@platforms//cpu:wasm32", | |
| ), | |
| "_test_main": attr.label( | |
| default = "//build_tools/wasm:wasm_test_main.mjs", | |
| allow_single_file = True, | |
| ), | |
| "_bundler": attr.label( | |
| default = "//build_tools/wasm:wasm_binary_bundler", | |
| executable = True, | |
| cfg = "exec", | |
| ), | |
| }, | |
| doc = "Platform-adaptive test rule. On native platforms, runs the " + | |
| "cc_binary directly. On wasm32 platforms, bundles with a WASI " + | |
| "test entry point and runs via Node.js. If an iree_wasm_entry " + | |
| "target is found in deps, it overrides the default entry point.", | |
| ) |