Skip to content

Commit 32795cf

Browse files
committed
std.Build: support exposing and depending on zig modules
New API introduced: std.Build.addModule This function exposes a zig module with the given name, which can be used by packages that depend on this one via std.Build.Dependency.module. std.Build.Pkg and related functionality is deleted. Every use case has a straightforward upgrade path using the new Module struct. std.Build.OptionsStep.getPackage is replaced by std.Build.OptionsStep.createModule. std.Build.CompileStep.addPackagePath is replaced by std.Build.CompileStep.addAnonymousModule. This partially addresses #14307 by renaming some of the instances of "package" to "module". Closes #14278
1 parent 60935de commit 32795cf

File tree

4 files changed

+106
-144
lines changed

4 files changed

+106
-144
lines changed

lib/std/Build.zig

Lines changed: 60 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ host: NativeTargetInfo,
109109

110110
dep_prefix: []const u8 = "",
111111

112+
modules: std.StringArrayHashMap(*Module),
113+
112114
pub const ExecError = error{
113115
ReadFailure,
114116
ExitCodeFailure,
@@ -232,6 +234,7 @@ pub fn create(
232234
.install_path = undefined,
233235
.args = null,
234236
.host = host,
237+
.modules = std.StringArrayHashMap(*Module).init(allocator),
235238
};
236239
try self.top_level_steps.append(&self.install_tls);
237240
try self.top_level_steps.append(&self.uninstall_tls);
@@ -305,6 +308,7 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: []const u8)
305308
.glibc_runtimes_dir = parent.glibc_runtimes_dir,
306309
.host = parent.host,
307310
.dep_prefix = parent.fmt("{s}{s}.", .{ parent.dep_prefix, dep_name }),
311+
.modules = std.StringArrayHashMap(*Module).init(allocator),
308312
};
309313
try child.top_level_steps.append(&child.install_tls);
310314
try child.top_level_steps.append(&child.uninstall_tls);
@@ -539,6 +543,49 @@ pub fn addAssembly(b: *Build, options: AssemblyOptions) *CompileStep {
539543
return obj_step;
540544
}
541545

546+
pub const AddModuleOptions = struct {
547+
name: []const u8,
548+
source_file: FileSource,
549+
dependencies: []const ModuleDependency = &.{},
550+
};
551+
552+
pub fn addModule(b: *Build, options: AddModuleOptions) void {
553+
b.modules.put(b.dupe(options.name), b.createModule(.{
554+
.source_file = options.source_file,
555+
.dependencies = options.dependencies,
556+
})) catch @panic("OOM");
557+
}
558+
559+
pub const ModuleDependency = struct {
560+
name: []const u8,
561+
module: *Module,
562+
};
563+
564+
pub const CreateModuleOptions = struct {
565+
source_file: FileSource,
566+
dependencies: []const ModuleDependency = &.{},
567+
};
568+
569+
/// Prefer to use `addModule` which will make the module available to other
570+
/// packages which depend on this package.
571+
pub fn createModule(b: *Build, options: CreateModuleOptions) *Module {
572+
const module = b.allocator.create(Module) catch @panic("OOM");
573+
module.* = .{
574+
.builder = b,
575+
.source_file = options.source_file,
576+
.dependencies = moduleDependenciesToArrayHashMap(b.allocator, options.dependencies),
577+
};
578+
return module;
579+
}
580+
581+
fn moduleDependenciesToArrayHashMap(arena: Allocator, deps: []const ModuleDependency) std.StringArrayHashMap(*Module) {
582+
var result = std.StringArrayHashMap(*Module).init(arena);
583+
for (deps) |dep| {
584+
result.put(dep.name, dep.module) catch @panic("OOM");
585+
}
586+
return result;
587+
}
588+
542589
/// Initializes a RunStep with argv, which must at least have the path to the
543590
/// executable. More command line arguments can be added with `addArg`,
544591
/// `addArgs`, and `addArtifactArg`.
@@ -588,24 +635,6 @@ pub fn dupePath(self: *Build, bytes: []const u8) []u8 {
588635
return the_copy;
589636
}
590637

591-
/// Duplicates a package recursively.
592-
pub fn dupePkg(self: *Build, package: Pkg) Pkg {
593-
var the_copy = Pkg{
594-
.name = self.dupe(package.name),
595-
.source = package.source.dupe(self),
596-
};
597-
598-
if (package.dependencies) |dependencies| {
599-
const new_dependencies = self.allocator.alloc(Pkg, dependencies.len) catch @panic("OOM");
600-
the_copy.dependencies = new_dependencies;
601-
602-
for (dependencies) |dep_package, i| {
603-
new_dependencies[i] = self.dupePkg(dep_package);
604-
}
605-
}
606-
return the_copy;
607-
}
608-
609638
pub fn addWriteFile(self: *Build, file_path: []const u8, data: []const u8) *WriteFileStep {
610639
const write_file_step = self.addWriteFiles();
611640
write_file_step.add(file_path, data);
@@ -1479,6 +1508,12 @@ pub const Dependency = struct {
14791508
panic("unable to find artifact '{s}'", .{name});
14801509
};
14811510
}
1511+
1512+
pub fn module(d: *Dependency, name: []const u8) *Module {
1513+
return d.builder.modules.get(name) orelse {
1514+
panic("unable to find module '{s}'", .{name});
1515+
};
1516+
}
14821517
};
14831518

14841519
pub fn dependency(b: *Build, name: []const u8, args: anytype) *Dependency {
@@ -1548,10 +1583,13 @@ test "builder.findProgram compiles" {
15481583
_ = builder.findProgram(&[_][]const u8{}, &[_][]const u8{}) catch null;
15491584
}
15501585

1551-
pub const Pkg = struct {
1552-
name: []const u8,
1553-
source: FileSource,
1554-
dependencies: ?[]const Pkg = null,
1586+
pub const Module = struct {
1587+
builder: *Build,
1588+
/// This could either be a generated file, in which case the module
1589+
/// contains exactly one file, or it could be a path to the root source
1590+
/// file of directory of files which constitute the module.
1591+
source_file: FileSource,
1592+
dependencies: std.StringArrayHashMap(*Module),
15551593
};
15561594

15571595
/// A file that is generated by a build step.
@@ -1713,54 +1751,6 @@ pub fn serializeCpu(allocator: Allocator, cpu: std.Target.Cpu) ![]const u8 {
17131751
}
17141752
}
17151753

1716-
test "dupePkg()" {
1717-
if (builtin.os.tag == .wasi) return error.SkipZigTest;
1718-
1719-
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
1720-
defer arena.deinit();
1721-
1722-
const host = try NativeTargetInfo.detect(.{});
1723-
1724-
var builder = try Build.create(
1725-
arena.allocator(),
1726-
"test",
1727-
"test",
1728-
"test",
1729-
"test",
1730-
host,
1731-
);
1732-
defer builder.destroy();
1733-
1734-
var pkg_dep = Pkg{
1735-
.name = "pkg_dep",
1736-
.source = .{ .path = "/not/a/pkg_dep.zig" },
1737-
};
1738-
var pkg_top = Pkg{
1739-
.name = "pkg_top",
1740-
.source = .{ .path = "/not/a/pkg_top.zig" },
1741-
.dependencies = &[_]Pkg{pkg_dep},
1742-
};
1743-
const duped = builder.dupePkg(pkg_top);
1744-
1745-
const original_deps = pkg_top.dependencies.?;
1746-
const dupe_deps = duped.dependencies.?;
1747-
1748-
// probably the same top level package details
1749-
try std.testing.expectEqualStrings(pkg_top.name, duped.name);
1750-
1751-
// probably the same dependencies
1752-
try std.testing.expectEqual(original_deps.len, dupe_deps.len);
1753-
try std.testing.expectEqual(original_deps[0].name, pkg_dep.name);
1754-
1755-
// could segfault otherwise if pointers in duplicated package's fields are
1756-
// the same as those in stack allocated package's fields
1757-
try std.testing.expect(dupe_deps.ptr != original_deps.ptr);
1758-
try std.testing.expect(duped.name.ptr != pkg_top.name.ptr);
1759-
try std.testing.expect(duped.source.path.ptr != pkg_top.source.path.ptr);
1760-
try std.testing.expect(dupe_deps[0].name.ptr != pkg_dep.name.ptr);
1761-
try std.testing.expect(dupe_deps[0].source.path.ptr != pkg_dep.source.path.ptr);
1762-
}
1763-
17641754
test {
17651755
_ = CheckFileStep;
17661756
_ = CheckObjectStep;

lib/std/Build/CompileStep.zig

Lines changed: 40 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const FileSource = std.Build.FileSource;
1616
const PkgConfigPkg = std.Build.PkgConfigPkg;
1717
const PkgConfigError = std.Build.PkgConfigError;
1818
const ExecError = std.Build.ExecError;
19-
const Pkg = std.Build.Pkg;
19+
const Module = std.Build.Module;
2020
const VcpkgRoot = std.Build.VcpkgRoot;
2121
const InstallDir = std.Build.InstallDir;
2222
const InstallArtifactStep = std.Build.InstallArtifactStep;
@@ -99,7 +99,7 @@ root_src: ?FileSource,
9999
out_h_filename: []const u8,
100100
out_lib_filename: []const u8,
101101
out_pdb_filename: []const u8,
102-
packages: ArrayList(Pkg),
102+
modules: std.StringArrayHashMap(*Module),
103103

104104
object_src: []const u8,
105105

@@ -334,7 +334,7 @@ pub fn create(builder: *std.Build, options: Options) *CompileStep {
334334
.out_pdb_filename = builder.fmt("{s}.pdb", .{name}),
335335
.major_only_filename = null,
336336
.name_only_filename = null,
337-
.packages = ArrayList(Pkg).init(builder.allocator),
337+
.modules = std.StringArrayHashMap(*Module).init(builder.allocator),
338338
.include_dirs = ArrayList(IncludeDir).init(builder.allocator),
339339
.link_objects = ArrayList(LinkObject).init(builder.allocator),
340340
.c_macros = ArrayList([]const u8).init(builder.allocator),
@@ -946,29 +946,29 @@ pub fn addFrameworkPath(self: *CompileStep, dir_path: []const u8) void {
946946
self.framework_dirs.append(self.builder.dupe(dir_path)) catch @panic("OOM");
947947
}
948948

949-
pub fn addPackage(self: *CompileStep, package: Pkg) void {
950-
self.packages.append(self.builder.dupePkg(package)) catch @panic("OOM");
951-
self.addRecursiveBuildDeps(package);
949+
/// Adds a module to be used with `@import` and exposing it in the current
950+
/// package's module table using `name`.
951+
pub fn addModule(cs: *CompileStep, name: []const u8, module: *Module) void {
952+
cs.modules.put(cs.builder.dupe(name), module) catch @panic("OOM");
953+
cs.addRecursiveBuildDeps(module);
952954
}
953955

954-
pub fn addOptions(self: *CompileStep, package_name: []const u8, options: *OptionsStep) void {
955-
self.addPackage(options.getPackage(package_name));
956+
/// Adds a module to be used with `@import` without exposing it in the current
957+
/// package's module table.
958+
pub fn addAnonymousModule(cs: *CompileStep, name: []const u8, options: std.Build.CreateModuleOptions) void {
959+
const module = cs.builder.createModule(options);
960+
return addModule(cs, name, module);
956961
}
957962

958-
fn addRecursiveBuildDeps(self: *CompileStep, package: Pkg) void {
959-
package.source.addStepDependencies(&self.step);
960-
if (package.dependencies) |deps| {
961-
for (deps) |dep| {
962-
self.addRecursiveBuildDeps(dep);
963-
}
964-
}
963+
pub fn addOptions(cs: *CompileStep, module_name: []const u8, options: *OptionsStep) void {
964+
addModule(cs, module_name, options.createModule());
965965
}
966966

967-
pub fn addPackagePath(self: *CompileStep, name: []const u8, pkg_index_path: []const u8) void {
968-
self.addPackage(Pkg{
969-
.name = self.builder.dupe(name),
970-
.source = .{ .path = self.builder.dupe(pkg_index_path) },
971-
});
967+
fn addRecursiveBuildDeps(cs: *CompileStep, module: *Module) void {
968+
module.source_file.addStepDependencies(&cs.step);
969+
for (module.dependencies.values()) |dep| {
970+
cs.addRecursiveBuildDeps(dep);
971+
}
972972
}
973973

974974
/// If Vcpkg was found on the system, it will be added to include and lib
@@ -1023,16 +1023,21 @@ fn linkLibraryOrObject(self: *CompileStep, other: *CompileStep) void {
10231023
self.include_dirs.append(.{ .other_step = other }) catch @panic("OOM");
10241024
}
10251025

1026-
fn makePackageCmd(self: *CompileStep, pkg: Pkg, zig_args: *ArrayList([]const u8)) error{OutOfMemory}!void {
1027-
const builder = self.builder;
1028-
1026+
fn appendModuleArgs(
1027+
cs: *CompileStep,
1028+
zig_args: *ArrayList([]const u8),
1029+
name: []const u8,
1030+
module: *Module,
1031+
) error{OutOfMemory}!void {
10291032
try zig_args.append("--pkg-begin");
1030-
try zig_args.append(pkg.name);
1031-
try zig_args.append(builder.pathFromRoot(pkg.source.getPath(self.builder)));
1032-
1033-
if (pkg.dependencies) |dependencies| {
1034-
for (dependencies) |sub_pkg| {
1035-
try self.makePackageCmd(sub_pkg, zig_args);
1033+
try zig_args.append(name);
1034+
try zig_args.append(module.builder.pathFromRoot(module.source_file.getPath(module.builder)));
1035+
1036+
{
1037+
const keys = module.dependencies.keys();
1038+
for (module.dependencies.values()) |sub_module, i| {
1039+
const sub_name = keys[i];
1040+
try cs.appendModuleArgs(zig_args, sub_name, sub_module);
10361041
}
10371042
}
10381043

@@ -1563,8 +1568,12 @@ fn make(step: *Step) !void {
15631568
try zig_args.append("--test-no-exec");
15641569
}
15651570

1566-
for (self.packages.items) |pkg| {
1567-
try self.makePackageCmd(pkg, &zig_args);
1571+
{
1572+
const keys = self.modules.keys();
1573+
for (self.modules.values()) |module, i| {
1574+
const name = keys[i];
1575+
try self.appendModuleArgs(&zig_args, name, module);
1576+
}
15681577
}
15691578

15701579
for (self.include_dirs.items) |include_dir| {
@@ -1942,46 +1951,6 @@ fn getPkgConfigList(self: *std.Build) ![]const PkgConfigPkg {
19421951
}
19431952
}
19441953

1945-
test "addPackage" {
1946-
if (builtin.os.tag == .wasi) return error.SkipZigTest;
1947-
1948-
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
1949-
defer arena.deinit();
1950-
1951-
const host = try NativeTargetInfo.detect(.{});
1952-
1953-
var builder = try std.Build.create(
1954-
arena.allocator(),
1955-
"test",
1956-
"test",
1957-
"test",
1958-
"test",
1959-
host,
1960-
);
1961-
defer builder.destroy();
1962-
1963-
const pkg_dep = Pkg{
1964-
.name = "pkg_dep",
1965-
.source = .{ .path = "/not/a/pkg_dep.zig" },
1966-
};
1967-
const pkg_top = Pkg{
1968-
.name = "pkg_dep",
1969-
.source = .{ .path = "/not/a/pkg_top.zig" },
1970-
.dependencies = &[_]Pkg{pkg_dep},
1971-
};
1972-
1973-
var exe = builder.addExecutable(.{
1974-
.name = "not_an_executable",
1975-
.root_source_file = .{ .path = "/not/an/executable.zig" },
1976-
});
1977-
exe.addPackage(pkg_top);
1978-
1979-
try std.testing.expectEqual(@as(usize, 1), exe.packages.items.len);
1980-
1981-
const dupe = exe.packages.items[0];
1982-
try std.testing.expectEqualStrings(pkg_top.name, dupe.name);
1983-
}
1984-
19851954
fn addFlag(args: *ArrayList([]const u8), comptime name: []const u8, opt: ?bool) !void {
19861955
const cond = opt orelse return;
19871956
try args.ensureUnusedCapacity(1);

lib/std/Build/OptionsStep.zig

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,8 +204,11 @@ pub fn addOptionArtifact(self: *OptionsStep, name: []const u8, artifact: *Compil
204204
self.step.dependOn(&artifact.step);
205205
}
206206

207-
pub fn getPackage(self: *OptionsStep, package_name: []const u8) std.Build.Pkg {
208-
return .{ .name = package_name, .source = self.getSource() };
207+
pub fn createModule(self: *OptionsStep) *std.Build.Module {
208+
return self.builder.createModule(.{
209+
.source_file = self.getSource(),
210+
.dependencies = &.{},
211+
});
209212
}
210213

211214
pub fn getSource(self: *OptionsStep) FileSource {

test/standalone/pkg_import/build.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ pub fn build(b: *std.Build) void {
88
.root_source_file = .{ .path = "test.zig" },
99
.optimize = optimize,
1010
});
11-
exe.addPackagePath("my_pkg", "pkg.zig");
11+
exe.addAnonymousModule("my_pkg", .{ .source_file = .{ .path = "pkg.zig" } });
1212

1313
const run = exe.run();
1414

0 commit comments

Comments
 (0)