Skip to content

Commit 7cb2f92

Browse files
authored
Merge pull request #14265 from ziglang/init-package-manager
Package Manager MVP
2 parents cbbf8c8 + f4d6b37 commit 7cb2f92

16 files changed

+1124
-43
lines changed

build.zig

+4-3
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ pub fn build(b: *Builder) !void {
185185
exe_options.addOption(bool, "llvm_has_arc", llvm_has_arc);
186186
exe_options.addOption(bool, "force_gpa", force_gpa);
187187
exe_options.addOption(bool, "only_c", only_c);
188+
exe_options.addOption(bool, "omit_pkg_fetching_code", false);
188189

189190
if (link_libc) {
190191
exe.linkLibC();
@@ -567,14 +568,14 @@ fn addCmakeCfgOptionsToExe(
567568
// back to -lc++ and cross our fingers.
568569
addCxxKnownPath(b, cfg, exe, b.fmt("libstdc++.{s}", .{lib_suffix}), "", need_cpp_includes) catch |err| switch (err) {
569570
error.RequiredLibraryNotFound => {
570-
exe.linkSystemLibrary("c++");
571+
exe.linkLibCpp();
571572
},
572573
else => |e| return e,
573574
};
574575
exe.linkSystemLibrary("unwind");
575576
},
576-
.ios, .macos, .watchos, .tvos => {
577-
exe.linkSystemLibrary("c++");
577+
.ios, .macos, .watchos, .tvos, .windows => {
578+
exe.linkLibCpp();
578579
},
579580
.freebsd => {
580581
if (static) {

lib/build_runner.zig

+4-10
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ const process = std.process;
99
const ArrayList = std.ArrayList;
1010
const File = std.fs.File;
1111

12+
pub const dependencies = @import("@dependencies");
13+
1214
pub fn main() !void {
1315
// Here we use an ArenaAllocator backed by a DirectAllocator because a build is a short-lived,
1416
// one shot program. We don't need to waste time freeing memory and finding places to squish
@@ -207,7 +209,7 @@ pub fn main() !void {
207209

208210
builder.debug_log_scopes = debug_log_scopes.items;
209211
builder.resolveInstallPrefix(install_prefix, dir_list);
210-
try runBuild(builder);
212+
try builder.runBuild(root);
211213

212214
if (builder.validateUserInputDidItFail())
213215
return usageAndErr(builder, true, stderr_stream);
@@ -223,19 +225,11 @@ pub fn main() !void {
223225
};
224226
}
225227

226-
fn runBuild(builder: *Builder) anyerror!void {
227-
switch (@typeInfo(@typeInfo(@TypeOf(root.build)).Fn.return_type.?)) {
228-
.Void => root.build(builder),
229-
.ErrorUnion => try root.build(builder),
230-
else => @compileError("expected return type of build to be 'void' or '!void'"),
231-
}
232-
}
233-
234228
fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void {
235229
// run the build script to collect the options
236230
if (!already_ran_build) {
237231
builder.resolveInstallPrefix(null, .{});
238-
try runBuild(builder);
232+
try builder.runBuild(root);
239233
}
240234

241235
try out_stream.print(

lib/std/Ini.zig

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
bytes: []const u8,
2+
3+
pub const SectionIterator = struct {
4+
ini: Ini,
5+
next_index: ?usize,
6+
header: []const u8,
7+
8+
pub fn next(it: *SectionIterator) ?[]const u8 {
9+
const bytes = it.ini.bytes;
10+
const start = it.next_index orelse return null;
11+
const end = mem.indexOfPos(u8, bytes, start, "\n[") orelse bytes.len;
12+
const result = bytes[start..end];
13+
if (mem.indexOfPos(u8, bytes, start, it.header)) |next_index| {
14+
it.next_index = next_index + it.header.len;
15+
} else {
16+
it.next_index = null;
17+
}
18+
return result;
19+
}
20+
};
21+
22+
/// Asserts that `header` includes "\n[" at the beginning and "]\n" at the end.
23+
/// `header` must remain valid for the lifetime of the iterator.
24+
pub fn iterateSection(ini: Ini, header: []const u8) SectionIterator {
25+
assert(mem.startsWith(u8, header, "\n["));
26+
assert(mem.endsWith(u8, header, "]\n"));
27+
const first_header = header[1..];
28+
const next_index = if (mem.indexOf(u8, ini.bytes, first_header)) |i|
29+
i + first_header.len
30+
else
31+
null;
32+
return .{
33+
.ini = ini,
34+
.next_index = next_index,
35+
.header = header,
36+
};
37+
}
38+
39+
const std = @import("std.zig");
40+
const mem = std.mem;
41+
const assert = std.debug.assert;
42+
const Ini = @This();
43+
const testing = std.testing;
44+
45+
test iterateSection {
46+
const example =
47+
\\[package]
48+
\\name=libffmpeg
49+
\\version=5.1.2
50+
\\
51+
\\[dependency]
52+
\\id=libz
53+
\\url=url1
54+
\\
55+
\\[dependency]
56+
\\id=libmp3lame
57+
\\url=url2
58+
;
59+
var ini: Ini = .{ .bytes = example };
60+
var it = ini.iterateSection("\n[dependency]\n");
61+
const section1 = it.next() orelse return error.TestFailed;
62+
try testing.expectEqualStrings("id=libz\nurl=url1\n", section1);
63+
const section2 = it.next() orelse return error.TestFailed;
64+
try testing.expectEqualStrings("id=libmp3lame\nurl=url2", section2);
65+
try testing.expect(it.next() == null);
66+
}

lib/std/build.zig

+159-2
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,15 @@ pub const Builder = struct {
6969
search_prefixes: ArrayList([]const u8),
7070
libc_file: ?[]const u8 = null,
7171
installed_files: ArrayList(InstalledFile),
72+
/// Path to the directory containing build.zig.
7273
build_root: []const u8,
7374
cache_root: []const u8,
7475
global_cache_root: []const u8,
7576
release_mode: ?std.builtin.Mode,
7677
is_release: bool,
78+
/// zig lib dir
7779
override_lib_dir: ?[]const u8,
78-
vcpkg_root: VcpkgRoot,
80+
vcpkg_root: VcpkgRoot = .unattempted,
7981
pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null,
8082
args: ?[][]const u8 = null,
8183
debug_log_scopes: []const []const u8 = &.{},
@@ -100,6 +102,8 @@ pub const Builder = struct {
100102
/// Information about the native target. Computed before build() is invoked.
101103
host: NativeTargetInfo,
102104

105+
dep_prefix: []const u8 = "",
106+
103107
pub const ExecError = error{
104108
ReadFailure,
105109
ExitCodeFailure,
@@ -223,7 +227,6 @@ pub const Builder = struct {
223227
.is_release = false,
224228
.override_lib_dir = null,
225229
.install_path = undefined,
226-
.vcpkg_root = VcpkgRoot{ .unattempted = {} },
227230
.args = null,
228231
.host = host,
229232
};
@@ -233,6 +236,92 @@ pub const Builder = struct {
233236
return self;
234237
}
235238

239+
fn createChild(
240+
parent: *Builder,
241+
dep_name: []const u8,
242+
build_root: []const u8,
243+
args: anytype,
244+
) !*Builder {
245+
const child = try createChildOnly(parent, dep_name, build_root);
246+
try applyArgs(child, args);
247+
return child;
248+
}
249+
250+
fn createChildOnly(parent: *Builder, dep_name: []const u8, build_root: []const u8) !*Builder {
251+
const allocator = parent.allocator;
252+
const child = try allocator.create(Builder);
253+
child.* = .{
254+
.allocator = allocator,
255+
.install_tls = .{
256+
.step = Step.initNoOp(.top_level, "install", allocator),
257+
.description = "Copy build artifacts to prefix path",
258+
},
259+
.uninstall_tls = .{
260+
.step = Step.init(.top_level, "uninstall", allocator, makeUninstall),
261+
.description = "Remove build artifacts from prefix path",
262+
},
263+
.user_input_options = UserInputOptionsMap.init(allocator),
264+
.available_options_map = AvailableOptionsMap.init(allocator),
265+
.available_options_list = ArrayList(AvailableOption).init(allocator),
266+
.verbose = parent.verbose,
267+
.verbose_link = parent.verbose_link,
268+
.verbose_cc = parent.verbose_cc,
269+
.verbose_air = parent.verbose_air,
270+
.verbose_llvm_ir = parent.verbose_llvm_ir,
271+
.verbose_cimport = parent.verbose_cimport,
272+
.verbose_llvm_cpu_features = parent.verbose_llvm_cpu_features,
273+
.prominent_compile_errors = parent.prominent_compile_errors,
274+
.color = parent.color,
275+
.reference_trace = parent.reference_trace,
276+
.invalid_user_input = false,
277+
.zig_exe = parent.zig_exe,
278+
.default_step = undefined,
279+
.env_map = parent.env_map,
280+
.top_level_steps = ArrayList(*TopLevelStep).init(allocator),
281+
.install_prefix = undefined,
282+
.dest_dir = parent.dest_dir,
283+
.lib_dir = parent.lib_dir,
284+
.exe_dir = parent.exe_dir,
285+
.h_dir = parent.h_dir,
286+
.install_path = parent.install_path,
287+
.sysroot = parent.sysroot,
288+
.search_prefixes = ArrayList([]const u8).init(allocator),
289+
.libc_file = parent.libc_file,
290+
.installed_files = ArrayList(InstalledFile).init(allocator),
291+
.build_root = build_root,
292+
.cache_root = parent.cache_root,
293+
.global_cache_root = parent.global_cache_root,
294+
.release_mode = parent.release_mode,
295+
.is_release = parent.is_release,
296+
.override_lib_dir = parent.override_lib_dir,
297+
.debug_log_scopes = parent.debug_log_scopes,
298+
.debug_compile_errors = parent.debug_compile_errors,
299+
.enable_darling = parent.enable_darling,
300+
.enable_qemu = parent.enable_qemu,
301+
.enable_rosetta = parent.enable_rosetta,
302+
.enable_wasmtime = parent.enable_wasmtime,
303+
.enable_wine = parent.enable_wine,
304+
.glibc_runtimes_dir = parent.glibc_runtimes_dir,
305+
.host = parent.host,
306+
.dep_prefix = parent.fmt("{s}{s}.", .{ parent.dep_prefix, dep_name }),
307+
};
308+
try child.top_level_steps.append(&child.install_tls);
309+
try child.top_level_steps.append(&child.uninstall_tls);
310+
child.default_step = &child.install_tls.step;
311+
return child;
312+
}
313+
314+
fn applyArgs(b: *Builder, args: anytype) !void {
315+
// TODO this function is the way that a build.zig file communicates
316+
// options to its dependencies. It is the programmatic way to give
317+
// command line arguments to a build.zig script.
318+
_ = args;
319+
// TODO create a hash based on the args and the package hash, use this
320+
// to compute the install prefix.
321+
const install_prefix = b.pathJoin(&.{ b.cache_root, "pkg" });
322+
b.resolveInstallPrefix(install_prefix, .{});
323+
}
324+
236325
pub fn destroy(self: *Builder) void {
237326
self.env_map.deinit();
238327
self.top_level_steps.deinit();
@@ -1068,6 +1157,10 @@ pub const Builder = struct {
10681157
return self.addInstallFileWithDir(source.dupe(self), .lib, dest_rel_path);
10691158
}
10701159

1160+
pub fn addInstallHeaderFile(b: *Builder, src_path: []const u8, dest_rel_path: []const u8) *InstallFileStep {
1161+
return b.addInstallFileWithDir(.{ .path = src_path }, .header, dest_rel_path);
1162+
}
1163+
10711164
pub fn addInstallRaw(self: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
10721165
return InstallRawStep.create(self, artifact, dest_filename, options);
10731166
}
@@ -1300,6 +1393,70 @@ pub const Builder = struct {
13001393
&[_][]const u8{ base_dir, dest_rel_path },
13011394
) catch unreachable;
13021395
}
1396+
1397+
pub const Dependency = struct {
1398+
builder: *Builder,
1399+
1400+
pub fn artifact(d: *Dependency, name: []const u8) *LibExeObjStep {
1401+
var found: ?*LibExeObjStep = null;
1402+
for (d.builder.install_tls.step.dependencies.items) |dep_step| {
1403+
const inst = dep_step.cast(InstallArtifactStep) orelse continue;
1404+
if (mem.eql(u8, inst.artifact.name, name)) {
1405+
if (found != null) panic("artifact name '{s}' is ambiguous", .{name});
1406+
found = inst.artifact;
1407+
}
1408+
}
1409+
return found orelse {
1410+
for (d.builder.install_tls.step.dependencies.items) |dep_step| {
1411+
const inst = dep_step.cast(InstallArtifactStep) orelse continue;
1412+
log.info("available artifact: '{s}'", .{inst.artifact.name});
1413+
}
1414+
panic("unable to find artifact '{s}'", .{name});
1415+
};
1416+
}
1417+
};
1418+
1419+
pub fn dependency(b: *Builder, name: []const u8, args: anytype) *Dependency {
1420+
const build_runner = @import("root");
1421+
const deps = build_runner.dependencies;
1422+
1423+
inline for (@typeInfo(deps.imports).Struct.decls) |decl| {
1424+
if (mem.startsWith(u8, decl.name, b.dep_prefix) and
1425+
mem.endsWith(u8, decl.name, name) and
1426+
decl.name.len == b.dep_prefix.len + name.len)
1427+
{
1428+
const build_zig = @field(deps.imports, decl.name);
1429+
const build_root = @field(deps.build_root, decl.name);
1430+
return dependencyInner(b, name, build_root, build_zig, args);
1431+
}
1432+
}
1433+
1434+
const full_path = b.pathFromRoot("build.zig.ini");
1435+
std.debug.print("no dependency named '{s}' in '{s}'\n", .{ name, full_path });
1436+
std.process.exit(1);
1437+
}
1438+
1439+
fn dependencyInner(
1440+
b: *Builder,
1441+
name: []const u8,
1442+
build_root: []const u8,
1443+
comptime build_zig: type,
1444+
args: anytype,
1445+
) *Dependency {
1446+
const sub_builder = b.createChild(name, build_root, args) catch unreachable;
1447+
sub_builder.runBuild(build_zig) catch unreachable;
1448+
const dep = b.allocator.create(Dependency) catch unreachable;
1449+
dep.* = .{ .builder = sub_builder };
1450+
return dep;
1451+
}
1452+
1453+
pub fn runBuild(b: *Builder, build_zig: anytype) anyerror!void {
1454+
switch (@typeInfo(@typeInfo(@TypeOf(build_zig.build)).Fn.return_type.?)) {
1455+
.Void => build_zig.build(b),
1456+
.ErrorUnion => try build_zig.build(b),
1457+
else => @compileError("expected return type of build to be 'void' or '!void'"),
1458+
}
1459+
}
13031460
};
13041461

13051462
test "builder.findProgram compiles" {

0 commit comments

Comments
 (0)