blob: bcbf451b583c8a77c0500f774705b24e27cef6fe [file] [log] [blame]
Tim Windelschmidtbed76d92025-02-18 03:04:14 +01001FSSpecInfo = 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
9def 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
Jan Schär0fd36f42025-04-29 10:26:03 +000012 input from the `files`, `symlinks` and `fsspecs` attributes
Tim Windelschmidtbed76d92025-02-18 03:04:14 +010013 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 = []
Jan Schär0fd36f42025-04-29 10:26:03 +000021 for p, label in ctx.attr.files.items() + extra_files:
Tim Windelschmidtbed76d92025-02-18 03:04:14 +010022 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 Windelschmidtad4d9542025-03-24 20:20:13 +010049 for p, target in ctx.attr.symlinks.items():
Tim Windelschmidtbed76d92025-02-18 03:04:14 +010050 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