|
| 1 | +"""Implementation of the `custom_toolchain` rule.""" |
| 2 | + |
| 3 | +def _get_xcode_product_version(*, xcode_config): |
| 4 | + raw_version = str(xcode_config.xcode_version()) |
| 5 | + if not raw_version: |
| 6 | + fail("""\ |
| 7 | +`xcode_config.xcode_version` was not set. This is a bazel bug. Try again. |
| 8 | +""") |
| 9 | + |
| 10 | + version_components = raw_version.split(".") |
| 11 | + if len(version_components) < 4: |
| 12 | + # This will result in analysis cache misses, but it's better than |
| 13 | + # failing |
| 14 | + return raw_version |
| 15 | + |
| 16 | + return version_components[3] |
| 17 | + |
| 18 | +def _custom_toolchain_impl(ctx): |
| 19 | + xcode_version = _get_xcode_product_version( |
| 20 | + xcode_config = ctx.attr._xcode_config[apple_common.XcodeVersionConfig], |
| 21 | + ) |
| 22 | + |
| 23 | + toolchain_name_base = ctx.attr.toolchain_name |
| 24 | + toolchain_dir = ctx.actions.declare_directory( |
| 25 | + toolchain_name_base + "{}".format(xcode_version) + ".xctoolchain", |
| 26 | + ) |
| 27 | + |
| 28 | + resolved_overrides = {} |
| 29 | + override_files = [] |
| 30 | + |
| 31 | + for tool_target, tool_name in ctx.attr.overrides.items(): |
| 32 | + files = tool_target.files.to_list() |
| 33 | + if not files: |
| 34 | + fail("ERROR: Override for '{}' does not produce any files!".format(tool_name)) |
| 35 | + |
| 36 | + if len(files) > 1: |
| 37 | + fail("ERROR: Override for '{}' produces multiple files ({}). Each override must have exactly one file.".format( |
| 38 | + tool_name, |
| 39 | + len(files), |
| 40 | + )) |
| 41 | + |
| 42 | + override_file = files[0] |
| 43 | + override_files.append(override_file) |
| 44 | + resolved_overrides[tool_name] = override_file.path |
| 45 | + |
| 46 | + overrides_list = " ".join(["{}={}".format(k, v) for k, v in resolved_overrides.items()]) |
| 47 | + |
| 48 | + script_file = ctx.actions.declare_file(toolchain_name_base + "_setup.sh") |
| 49 | + |
| 50 | + ctx.actions.expand_template( |
| 51 | + template = ctx.file._symlink_template, |
| 52 | + output = script_file, |
| 53 | + is_executable = True, |
| 54 | + substitutions = { |
| 55 | + "%overrides_list%": overrides_list, |
| 56 | + "%toolchain_dir%": toolchain_dir.path, |
| 57 | + "%toolchain_name_base%": toolchain_name_base, |
| 58 | + "%xcode_version%": xcode_version, |
| 59 | + }, |
| 60 | + ) |
| 61 | + |
| 62 | + ctx.actions.run_shell( |
| 63 | + outputs = [toolchain_dir], |
| 64 | + inputs = override_files, |
| 65 | + tools = [script_file], |
| 66 | + mnemonic = "CreateCustomToolchain", |
| 67 | + command = script_file.path, |
| 68 | + execution_requirements = { |
| 69 | + "local": "1", |
| 70 | + "no-cache": "1", |
| 71 | + "no-sandbox": "1", |
| 72 | + "requires-darwin": "1", |
| 73 | + }, |
| 74 | + use_default_shell_env = True, |
| 75 | + ) |
| 76 | + |
| 77 | + # Create runfiles with the override files and script file |
| 78 | + runfiles = ctx.runfiles(files = override_files + [script_file]) |
| 79 | + |
| 80 | + return [DefaultInfo( |
| 81 | + files = depset([toolchain_dir]), |
| 82 | + runfiles = runfiles, |
| 83 | + )] |
| 84 | + |
| 85 | +custom_toolchain = rule( |
| 86 | + implementation = _custom_toolchain_impl, |
| 87 | + attrs = { |
| 88 | + "overrides": attr.label_keyed_string_dict( |
| 89 | + allow_files = True, |
| 90 | + mandatory = False, |
| 91 | + default = {}, |
| 92 | + ), |
| 93 | + "toolchain_name": attr.string(mandatory = True), |
| 94 | + "_symlink_template": attr.label( |
| 95 | + allow_single_file = True, |
| 96 | + default = Label("//xcodeproj/internal/templates:custom_toolchain_symlink.sh"), |
| 97 | + ), |
| 98 | + "_xcode_config": attr.label( |
| 99 | + default = configuration_field( |
| 100 | + name = "xcode_config_label", |
| 101 | + fragment = "apple", |
| 102 | + ), |
| 103 | + ), |
| 104 | + }, |
| 105 | +) |
0 commit comments