| Tim Windelschmidt | bed76d9 | 2025-02-18 03:04:14 +0100 | [diff] [blame] | 1 | FSSpecInfo = provider( |
| 2 | "Provides parts of an FSSpec used to assemble filesystem images", |
| 3 | fields = { |
| 4 | "spec": "File containing the partial FSSpec as prototext", |
| 5 | "referenced": "Files (potentially) referenced by the spec", |
| 6 | }, |
| 7 | ) |
| 8 | |
| 9 | def fsspec_core_impl(ctx, tool, output_file, extra_files = [], extra_fsspecs = []): |
| 10 | """ |
| 11 | fsspec_core_impl implements the core of an fsspec-based rule. It takes |
| 12 | input from the `files`,`files_cc`, `symlinks` and `fsspecs` attributes |
| 13 | and calls `tool` with the `-out` parameter pointing to `output_file` |
| 14 | and paths to all fsspecs as positional arguments. |
| 15 | """ |
| 16 | fs_spec_name = ctx.label.name + ".prototxt" |
| 17 | fs_spec = ctx.actions.declare_file(fs_spec_name) |
| 18 | |
| 19 | fs_files = [] |
| 20 | inputs = [] |
| 21 | for p, label in ctx.attr.files.items() + ctx.attr.files_cc.items() + extra_files: |
| 22 | if not p.startswith("/"): |
| 23 | fail("file {} invalid: must begin with /".format(p)) |
| 24 | |
| 25 | # Figure out if this is an executable. |
| 26 | is_executable = True |
| 27 | |
| 28 | di = label[DefaultInfo] |
| 29 | if di.files_to_run.executable == None: |
| 30 | # Generated non-executable files will have DefaultInfo.files_to_run.executable == None |
| 31 | is_executable = False |
| 32 | elif di.files_to_run.executable.is_source: |
| 33 | # Source files will have executable.is_source == True |
| 34 | is_executable = False |
| 35 | |
| 36 | # Ensure only single output is declared. |
| 37 | # If you hit this error, figure out a better logic to find what file you need, maybe looking at providers other |
| 38 | # than DefaultInfo. |
| 39 | files = di.files.to_list() |
| 40 | if len(files) > 1: |
| 41 | fail("file {} has more than one output: {}", p, files) |
| 42 | src = files[0] |
| 43 | inputs.append(src) |
| 44 | |
| 45 | mode = 0o555 if is_executable else 0o444 |
| 46 | fs_files.append(struct(path = p, source_path = src.path, mode = mode, uid = 0, gid = 0)) |
| 47 | |
| 48 | fs_symlinks = [] |
| Tim Windelschmidt | ad4d954 | 2025-03-24 20:20:13 +0100 | [diff] [blame^] | 49 | for p, target in ctx.attr.symlinks.items(): |
| Tim Windelschmidt | bed76d9 | 2025-02-18 03:04:14 +0100 | [diff] [blame] | 50 | fs_symlinks.append(struct(path = p, target_path = target)) |
| 51 | |
| 52 | fs_spec_content = struct(file = fs_files, directory = [], symbolic_link = fs_symlinks) |
| 53 | ctx.actions.write(fs_spec, proto.encode_text(fs_spec_content)) |
| 54 | |
| 55 | extra_specs = [] |
| 56 | |
| 57 | for fsspec in ctx.attr.fsspecs + extra_fsspecs: |
| 58 | if FSSpecInfo in fsspec: |
| 59 | fsspec_info = fsspec[FSSpecInfo] |
| 60 | extra_specs.append(fsspec_info.spec) |
| 61 | for f in fsspec_info.referenced: |
| 62 | inputs.append(f) |
| 63 | else: |
| 64 | # Raw .fsspec prototext. No referenced data allowed. |
| 65 | di = fsspec[DefaultInfo] |
| 66 | extra_specs += di.files.to_list() |
| 67 | |
| 68 | ctx.actions.run( |
| 69 | mnemonic = "GenFSSpecImage", |
| 70 | progress_message = "Generating a fsspec based image: {}".format(output_file.short_path), |
| 71 | outputs = [output_file], |
| 72 | inputs = [fs_spec] + inputs + extra_specs, |
| 73 | tools = [tool], |
| 74 | executable = tool, |
| 75 | arguments = ["-out", output_file.path, fs_spec.path] + [s.path for s in extra_specs], |
| 76 | ) |
| 77 | return |