Skip to content

Commit 3aff56e

Browse files
committed
feat!: move to Run Step-based architecture
this change should hopefully be more future-proof: in light of ziglang/zig#14498, collecting the EmbedFile.zig logic as a Run Step is likely to be the preferred (indeed, only) way to extend the Build system in the future. also, by breaking out the work originally in `make` into a collection of other steps, EmbedFile.zig should hopefully correctly work with the Zig build syste's caching system. (Previously, I found that updating file contents were not reflected in the `@embedFile` calls.) adds `writeSources`, which creates and returns a named `WriteFile` step which collects everything the `EmbedFile` step touches and outputs it into one directory. also, one can now use the `embed-file` executable standalone if desired. BREAKING CHANGE: the signatures of `addFile` and `addDirectory` have changed. taking `sub_path` as an argument was incorrect. BREAKING CHANGE: `createModule` is removed; instead use `embed_file.module` directly.
1 parent b148d61 commit 3aff56e

File tree

8 files changed

+486
-432
lines changed

8 files changed

+486
-432
lines changed

README.org

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,6 @@ This is the public API of an =EmbedFile= Step:
3838
embed_file: *EmbedFile,
3939
// this is a path to the parent directory of the file to be added
4040
source: std.Build.LazyPath,
41-
// this is the name of the file as it exists as input
42-
sub_path: []const u8,
4341
// this is the name which will be available when using the resulting Zig module
4442
name: []const u8,
4543
alignment: ?u29,
@@ -51,8 +49,6 @@ This is the public API of an =EmbedFile= Step:
5149
embed_file: *EmbedFile,
5250
// this is a path to the parent directory of the directory to be added
5351
source: std.Build.LazyPath,
54-
// this is the name of the directory as it exists as input
55-
sub_path: []const u8,
5652
// this allows the user to include or exclude files based on their extensions
5753
options: std.Build.Step.WriteFile.Directory.Options,
5854
// this is the namespace which will be available when using the resulting Zig module
@@ -68,14 +64,20 @@ This is the public API of an =EmbedFile= Step:
6864
// ...
6965
}
7066

71-
/// returns a `Module` containing the Zig source file generated from this `EmbedFile`
72-
pub fn createModule(embed_file: *EmbedFile) *std.Build.Module {
67+
/// adds a named WriteFile step that collects all of this EmbedFile's dependencies to write out
68+
pub fn writeSources(embed_file: *EmbedFile, name: []const u8) *std.Build.Step.WriteFile {
7369
// ...
7470
}
71+
72+
/// a `*Module` containing the Zig source file generated from this `EmbedFile`
73+
/// add it to your compilation by passing it `addImport`,
74+
/// e.g. `exe.root_module.addImport("assets", embed_file.module);`
75+
module: *std.Build.Module,
7576
#+end_src
7677

7778
To see an example of correct usage,
78-
you can clone this repository and run =zig build test=.
79+
you can clone this repository and run =zig build test= in the =tests= directory.
7980
The resulting Zig file will be placed in =test-output/module.zig= relative to the build prefix.
80-
(NB: the resulting =module.zig= will not compile,
81-
since its declarations use paths relative to a folder in the cache.)
81+
(NB: the resulting =module.zig= will not compile without =build.zig= logic,
82+
since its declarations use module imports that =EmbedFile= constructs itself.
83+
Of course, you could rewrite or reproduce these imports yourself.)

build.zig

Lines changed: 149 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,34 @@
11
const std = @import("std");
2+
const EmbedFile = @This();
23

3-
pub const EmbedFile = @import("src/EmbedFile.zig");
4+
wf: *std.Build.Step.WriteFile,
5+
contents: std.ArrayListUnmanaged(u8),
6+
module: *std.Build.Module,
7+
named_wfs: std.ArrayListUnmanaged(*std.Build.Step.WriteFile),
8+
9+
/// creates a new EmbedFile step
10+
pub fn create(owner: *std.Build) *EmbedFile {
11+
const embed_file = owner.allocator.create(EmbedFile) catch @panic("OOM");
12+
embed_file.* = .{
13+
.wf = std.Build.Step.WriteFile.create(owner),
14+
.module = owner.createModule(.{}),
15+
.contents = .{},
16+
.named_wfs = .{},
17+
};
18+
const canonicalize = owner.addSystemCommand(&.{ owner.graph.zig_exe, "fmt", "--stdin" });
19+
canonicalize.setStdIn(.{ .lazy_path = embed_file.wf.getDirectory().path(owner, "module.zig") });
20+
const module_file = canonicalize.captureStdOut();
21+
embed_file.module.root_source_file = module_file;
22+
embed_file.contents.appendSlice(owner.allocator,
23+
\\//! This file is automatically generated by EmbedFile.zig!
24+
\\//! EmbedFile.zig is NOT an official part of the Zig build system;
25+
\\//! please report any issues at https://github.com/robbielyman/EmbedFile.zig
26+
\\
27+
\\
28+
) catch @panic("OOM");
29+
embed_file.updateContents();
30+
return embed_file;
31+
}
432

533
pub fn addEmbedFiles(b: *std.Build) *EmbedFile {
634
return EmbedFile.create(b);
@@ -9,47 +37,138 @@ pub fn addEmbedFiles(b: *std.Build) *EmbedFile {
937
pub fn addEmbedFile(b: *std.Build, name: []const u8, bytes: []const u8, alignment: ?u29) *EmbedFile {
1038
const ret = EmbedFile.create(b);
1139
ret.add(name, bytes, alignment);
12-
ret.step.name = b.fmt("EmbedFile {s}", .{name});
1340
return ret;
1441
}
1542

16-
pub fn build(b: *std.Build) void {
17-
const target = b.standardTargetOptions(.{});
18-
const optimize = b.standardOptimizeOption(.{});
19-
_ = b.addModule("EmbedFile", .{
20-
.root_source_file = b.path("src/EmbedFile.zig"),
21-
.target = target,
22-
.optimize = optimize,
23-
});
24-
25-
const test_step = b.step("test", "test EmbedFile");
26-
test_step.dependOn(tests(b));
43+
fn updateContents(embed_file: *EmbedFile) void {
44+
if (embed_file.wf.files.items.len == 0) {
45+
_ = embed_file.wf.add("module.zig", embed_file.contents.items);
46+
return;
47+
}
48+
embed_file.wf.files.items[0].contents = .{ .bytes = embed_file.contents.items };
2749
}
2850

29-
fn tests(b: *std.Build) *std.Build.Step {
30-
const test_module = b.addTest(.{
31-
.root_source_file = b.path("src/test.zig"),
51+
const Kind = enum { file, directory };
52+
53+
fn addImport(
54+
embed_file: *EmbedFile,
55+
b: *std.Build,
56+
write_file: *std.Build.Step.WriteFile,
57+
name: []const u8,
58+
kind: Kind,
59+
alignment: ?u29,
60+
) void {
61+
const this_dep = b.dependencyFromBuildZig(@This(), .{
62+
.target = b.graph.host,
63+
.optimize = .Debug,
3264
});
65+
const exe = this_dep.artifact("embed-file");
66+
const run = b.addRunArtifact(exe);
67+
switch (kind) {
68+
.file => run.addFileArg(write_file.getDirectory().path(b, name)),
69+
.directory => run.addDirectoryArg(write_file.getDirectory().path(b, name)),
70+
}
71+
if (alignment) |a| run.addArg(b.fmt("{d}", .{a}));
72+
run.step.dependOn(&write_file.step);
73+
const input = run.captureStdOut();
74+
const fmt = b.addSystemCommand(&.{ b.graph.zig_exe, "fmt", "--stdin" });
75+
fmt.setStdIn(.{ .lazy_path = input });
76+
const output = fmt.captureStdOut();
77+
const copy_file = b.addWriteFiles();
78+
const module_file = copy_file.addCopyFile(output, "module.zig");
79+
_ = copy_file.addCopyDirectory(write_file.getDirectory(), "", .{});
80+
for (embed_file.named_wfs.items) |wf| {
81+
_ = wf.addCopyDirectory(copy_file.getDirectory(), name, .{});
82+
}
83+
const import_module = b.createModule(.{
84+
.root_source_file = module_file,
85+
});
86+
embed_file.module.addImport(name, import_module);
87+
switch (kind) {
88+
.file => embed_file.contents.writer(b.allocator).print(
89+
\\pub const @"{s}" = @import("{s}").@"{s}";
90+
\\
91+
, .{ name, name, name }) catch @panic("OOM"),
92+
.directory => embed_file.contents.writer(b.allocator).print(
93+
\\pub const @"{s}" = @import("{s}");
94+
\\
95+
, .{ name, name }) catch @panic("OOM"),
96+
}
97+
embed_file.updateContents();
98+
}
3399

100+
/// adds a declaration by name, contents and alignment directly
101+
pub fn add(embed_file: *EmbedFile, name: []const u8, bytes: []const u8, alignment: ?u29) void {
102+
const b = embed_file.module.owner;
103+
const write_file = b.addWriteFile(name, bytes);
104+
embed_file.addImport(b, write_file, name, .file, alignment);
105+
}
106+
107+
/// name is the eventual declaration name to be used,
108+
/// while source is the path as it exists on the file system
109+
pub fn addFile(
110+
embed_file: *EmbedFile,
111+
source: std.Build.LazyPath,
112+
name: []const u8,
113+
alignment: ?u29,
114+
) void {
115+
const b = embed_file.module.owner;
34116
const write_file = b.addWriteFiles();
35-
_ = write_file.add("a.include", "a.a");
36-
_ = write_file.add("b.include", "a.b");
37-
_ = write_file.add("c" ++ std.fs.path.sep_str ++ "a.include", "a.c.a");
38-
_ = write_file.add("d.exclude", "a.d");
117+
_ = write_file.addCopyFile(source, name);
118+
embed_file.addImport(b, write_file, name, .file, alignment);
119+
}
39120

40-
const second_write_file = b.addWriteFile("path.txt", "this is to test addFile");
121+
/// name is the eventual declaration name to be used as a namespace,
122+
/// while source is the path as it exists on the file system
123+
/// files matching the Directory.Options specification
124+
/// are made available as declarations namespaced under "name";
125+
/// the filename (minus any extension) is used as the declaration name
126+
/// alignment, if non-null, is used as the alignment for all sub-declarations
127+
pub fn addDirectory(
128+
embed_file: *EmbedFile,
129+
source: std.Build.LazyPath,
130+
options: std.Build.Step.WriteFile.Directory.Options,
131+
name: []const u8,
132+
alignment: ?u29,
133+
) void {
134+
const b = embed_file.module.owner;
135+
const write_file = b.addWriteFiles();
136+
_ = write_file.addCopyDirectory(source, name, options);
137+
embed_file.addImport(b, write_file, name, .directory, alignment);
138+
}
41139

42-
const embed_file = addEmbedFile(b, "name with spaces", "names can have spaces", null);
43-
embed_file.addDirectory(write_file.getDirectory(), "", .{ .exclude_extensions = &.{".exclude"}, .include_extensions = &.{".include"} }, "a", 16);
44-
embed_file.addFile(second_write_file.getDirectory(), "path.txt", "other", null);
140+
/// returns a `LazyPath` representing the Zig source file generated from this `EmbedFile`
141+
pub fn getSource(embed_file: *EmbedFile) std.Build.LazyPath {
142+
return embed_file.module.root_source_file.?;
143+
}
45144

46-
embed_file.step.dependOn(&write_file.step);
145+
/// adds a named WriteFile step that collects all of this EmbedFile's dependencies to write out
146+
pub fn writeSources(embed_file: *EmbedFile, name: []const u8) *std.Build.Step.WriteFile {
147+
const owner = embed_file.module.owner;
148+
const gpa = owner.allocator;
149+
const new_wf = owner.addNamedWriteFiles(name);
150+
// collect all currently existing dependencies
151+
// addImport will add the rest
152+
var it = embed_file.module.iterateDependencies(null, false);
153+
while (it.next()) |item| if (std.mem.eql(u8, item.name, "root")) {
154+
_ = new_wf.addCopyFile(item.module.root_source_file.?, "module.zig");
155+
} else {
156+
_ = new_wf.addCopyDirectory(item.module.root_source_file.?.dirname(), item.name, .{});
157+
};
47158

48-
test_module.root_module.addImport("assets", embed_file.createModule());
159+
embed_file.named_wfs.append(gpa, new_wf) catch @panic("OOM");
160+
return new_wf;
161+
}
49162

50-
const install_output_file = b.addInstallFileWithDir(embed_file.getSource(), .{ .custom = "test-output" }, "module.zig");
163+
pub fn build(b: *std.Build) void {
164+
const target = b.standardTargetOptions(.{});
165+
const optimize = b.standardOptimizeOption(.{});
51166

52-
const run_tests = b.addRunArtifact(test_module);
53-
run_tests.step.dependOn(&install_output_file.step);
54-
return &run_tests.step;
167+
const exe = b.addExecutable(.{
168+
.name = "embed-file",
169+
.target = target,
170+
.optimize = optimize,
171+
.root_source_file = b.path("src/main.zig"),
172+
});
173+
b.installArtifact(exe);
55174
}

build.zig.zon

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
.{
22
.name = "embed-file",
3-
.version = "0.1.0",
4-
.paths = .{ "build.zig.zon", "build.zig", "LICENSE", "README.org", "src" },
3+
.version = "1.0.0",
4+
.paths = .{ "build.zig.zon", "build.zig", "LICENSE", "README.org", "src/main.zig", "tests" },
55
.dependencies = .{},
66
}

0 commit comments

Comments
 (0)