| ... | @@ -170,8 +170,10 @@ pub fn main(init: process.Init.Minimal) !void { | ... | @@ -170,8 +170,10 @@ pub fn main(init: process.Init.Minimal) !void { |
| 170 | .random_seed = parseRandomSeed(seed_arg), | 170 | .random_seed = parseRandomSeed(seed_arg), |
| 171 | }; | 171 | }; |
| 172 | | 172 | |
| 173 | const cmd = stringToEnum(enum { fetch, build }, cmd_name) orelse fatal("bad command name: {q}", .{ cmd_name }); | 173 | const cmd = stringToEnum(enum { init, fetch, build }, cmd_name) orelse |
| | 174 | fatal("bad command name: {q}", .{ cmd_name }); |
| 174 | switch (cmd) { | 175 | switch (cmd) { |
| | 176 | .init => return cmdInit( gpa, &graph, args[arg_i..]), |
| 175 | .fetch => return cmdFetch( gpa, &graph, args[arg_i..]), | 177 | .fetch => return cmdFetch( gpa, &graph, args[arg_i..]), |
| 176 | .build => {}, | 178 | .build => {}, |
| 177 | } | 179 | } |
| ... | @@ -978,7 +980,7 @@ pub fn main(init: process.Init.Minimal) !void { | ... | @@ -978,7 +980,7 @@ pub fn main(init: process.Init.Minimal) !void { |
| 978 | } | 980 | } |
| 979 | | 981 | |
| 980 | const rand_int = randInt(io, u64); | 982 | const rand_int = randInt(io, u64); |
| 981 | const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int); | 983 | const tmp_dir_sub_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int); |
| 982 | const config_tmp_path: Path = .{ | 984 | const config_tmp_path: Path = .{ |
| 983 | .root_dir = dirs.local_cache, | 985 | .root_dir = dirs.local_cache, |
| 984 | .sub_path = tmp_dir_sub_path, | 986 | .sub_path = tmp_dir_sub_path, |
| ... | @@ -1033,7 +1035,7 @@ pub fn main(init: process.Init.Minimal) !void { | ... | @@ -1033,7 +1035,7 @@ pub fn main(init: process.Init.Minimal) !void { |
| 1033 | if (system_pkg_dir_path) |p| { | 1035 | if (system_pkg_dir_path) |p| { |
| 1034 | // In this mode, the system needs to provide these packages; they | 1036 | // In this mode, the system needs to provide these packages; they |
| 1035 | // cannot be fetched by Zig. | 1037 | // cannot be fetched by Zig. |
| 1036 | const s = fs.path.sep_str; | 1038 | const s = Dir.path.sep_str; |
| 1037 | for (unlazy_set.keys()) |*hash| { | 1039 | for (unlazy_set.keys()) |*hash| { |
| 1038 | std.log.err("lazy dependency package not found: {s}" ++ s ++ "{s}", .{ p, hash.toSlice() }); | 1040 | std.log.err("lazy dependency package not found: {s}" ++ s ++ "{s}", .{ p, hash.toSlice() }); |
| 1039 | } | 1041 | } |
| ... | @@ -1100,7 +1102,7 @@ pub fn main(init: process.Init.Minimal) !void { | ... | @@ -1100,7 +1102,7 @@ pub fn main(init: process.Init.Minimal) !void { |
| 1100 | config_tmp_path, final_path, e, | 1102 | config_tmp_path, final_path, e, |
| 1101 | }); | 1103 | }); |
| 1102 | }; | 1104 | }; |
| 1103 | config_man.writeManifest() catch |err| warn("failed to write cache manifest: {t}", .{err}); | 1105 | config_man.writeManifest() catch |err| log.warn("failed to write cache manifest: {t}", .{err}); |
| 1104 | break :cp .{ final_path, false }; | 1106 | break :cp .{ final_path, false }; |
| 1105 | } | 1107 | } |
| 1106 | }; | 1108 | }; |
| ... | @@ -1549,7 +1551,7 @@ fn cmdFetch( | ... | @@ -1549,7 +1551,7 @@ fn cmdFetch( |
| 1549 | ast.deinit(gpa); | 1551 | ast.deinit(gpa); |
| 1550 | } | 1552 | } |
| 1551 | | 1553 | |
| 1552 | var fixups: Ast.Render.Fixups = .{}; | 1554 | var fixups: std.zig.Ast.Render.Fixups = .{}; |
| 1553 | defer fixups.deinit(gpa); | 1555 | defer fixups.deinit(gpa); |
| 1554 | | 1556 | |
| 1555 | var saved_path_or_url = path_or_url; | 1557 | var saved_path_or_url = path_or_url; |
| ... | @@ -1630,7 +1632,7 @@ fn cmdFetch( | ... | @@ -1630,7 +1632,7 @@ fn cmdFetch( |
| 1630 | .{std.zig.fmtString(package_hash_slice)}, | 1632 | .{std.zig.fmtString(package_hash_slice)}, |
| 1631 | ); | 1633 | ); |
| 1632 | | 1634 | |
| 1633 | warn("overwriting existing dependency named {q}", .{name}); | 1635 | log.warn("overwriting existing dependency named {q}", .{name}); |
| 1634 | try fixups.replace_nodes_with_string.put(gpa, dep.location_node, location_replace); | 1636 | try fixups.replace_nodes_with_string.put(gpa, dep.location_node, location_replace); |
| 1635 | if (dep.hash_node.unwrap()) |hash_node| { | 1637 | if (dep.hash_node.unwrap()) |hash_node| { |
| 1636 | try fixups.replace_nodes_with_string.put(gpa, hash_node, hash_replace); | 1638 | try fixups.replace_nodes_with_string.put(gpa, hash_node, hash_replace); |
| ... | @@ -1692,10 +1694,129 @@ const usage_fetch = | ... | @@ -1692,10 +1694,129 @@ const usage_fetch = |
| 1692 | \\ | 1694 | \\ |
| 1693 | ; | 1695 | ; |
| 1694 | | 1696 | |
| 1695 | fn cmdBuild() !void { | 1697 | const usage_init = |
| | 1698 | \\Usage: zig init |
| | 1699 | \\ |
| | 1700 | \\ Initializes a `zig build` project in the current working |
| | 1701 | \\ directory. |
| | 1702 | \\ |
| | 1703 | \\Options: |
| | 1704 | \\ -m, --minimal Use minimal init template |
| | 1705 | \\ -h, --help Print this help and exit |
| | 1706 | \\ |
| | 1707 | \\ |
| | 1708 | ; |
| | 1709 | |
| | 1710 | fn cmdInit( |
| | 1711 | gpa: Allocator, |
| | 1712 | graph: *Graph, |
| | 1713 | args: []const []const u8 |
| | 1714 | ) !void { |
| | 1715 | const arena = graph.arena; |
| | 1716 | const io = graph.io; |
| | 1717 | |
| | 1718 | var template: enum { example, minimal } = .example; |
| | 1719 | { |
| | 1720 | var i: usize = 0; |
| | 1721 | while (i < args.len) : (i += 1) { |
| | 1722 | const arg = args[i]; |
| | 1723 | if (mem.startsWith(u8, arg, "-")) { |
| | 1724 | if (mem.eql(u8, arg, "-m") or mem.eql(u8, arg, "--minimal")) { |
| | 1725 | template = .minimal; |
| | 1726 | } else if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { |
| | 1727 | try Io.File.stdout().writeStreamingAll(io, usage_init); |
| | 1728 | return cleanExit(io); |
| | 1729 | } else { |
| | 1730 | fatal("unrecognized parameter: {q}", .{arg}); |
| | 1731 | } |
| | 1732 | } else { |
| | 1733 | fatal("unexpected extra parameter: {q}", .{arg}); |
| | 1734 | } |
| | 1735 | } |
| | 1736 | } |
| | 1737 | |
| | 1738 | const cwd_path = try std.zig.getResolvedCwd(io, arena); |
| | 1739 | const cwd_basename = Dir.path.basename(cwd_path); |
| | 1740 | const sanitized_root_name = try sanitizeExampleName(arena, cwd_basename); |
| | 1741 | |
| | 1742 | const rng: std.Random.IoSource = .{ .io = io }; |
| | 1743 | const fingerprint: Package.Fingerprint = .generate(rng.interface(), sanitized_root_name); |
| | 1744 | |
| | 1745 | switch (template) { |
| | 1746 | .example => { |
| | 1747 | var templates = findTemplates(gpa, arena, io); |
| | 1748 | defer templates.deinit(io); |
| | 1749 | |
| | 1750 | const s = Dir.path.sep_str; |
| | 1751 | const template_paths = [_][]const u8{ |
| | 1752 | Package.build_zig_basename, |
| | 1753 | Package.Manifest.basename, |
| | 1754 | "src" ++ s ++ "main.zig", |
| | 1755 | "src" ++ s ++ "root.zig", |
| | 1756 | }; |
| | 1757 | var ok_count: usize = 0; |
| | 1758 | |
| | 1759 | for (template_paths) |template_path| { |
| | 1760 | if (templates.write(arena, io, Io.Dir.cwd(), sanitized_root_name, template_path, fingerprint)) |_| { |
| | 1761 | std.log.info("created {s}", .{template_path}); |
| | 1762 | ok_count += 1; |
| | 1763 | } else |err| switch (err) { |
| | 1764 | error.PathAlreadyExists => std.log.info("preserving already existing file: {s}", .{ |
| | 1765 | template_path, |
| | 1766 | }), |
| | 1767 | else => std.log.err("unable to write {s}: {s}\n", .{ template_path, @errorName(err) }), |
| | 1768 | } |
| | 1769 | } |
| 1696 | | 1770 | |
| | 1771 | if (ok_count == template_paths.len) { |
| | 1772 | std.log.info("see `zig build --help` for a menu of options", .{}); |
| | 1773 | } |
| | 1774 | return cleanExit(io); |
| | 1775 | }, |
| | 1776 | .minimal => { |
| | 1777 | writeSimpleTemplateFile(io, Package.Manifest.basename, |
| | 1778 | \\.{{ |
| | 1779 | \\ .name = .{s}, |
| | 1780 | \\ .version = "0.0.1", |
| | 1781 | \\ .minimum_zig_version = "{s}", |
| | 1782 | \\ .paths = .{{""}}, |
| | 1783 | \\ .fingerprint = 0x{x}, |
| | 1784 | \\}} |
| | 1785 | \\ |
| | 1786 | , .{ |
| | 1787 | sanitized_root_name, |
| | 1788 | builtin.zig_version_string, |
| | 1789 | fingerprint.int(), |
| | 1790 | }) catch |err| switch (err) { |
| | 1791 | else => fatal("failed to create {q}: {t}", .{ Package.Manifest.basename, err }), |
| | 1792 | error.PathAlreadyExists => fatal("refusing to overwrite {q}", .{Package.Manifest.basename}), |
| | 1793 | }; |
| | 1794 | writeSimpleTemplateFile(io, Package.build_zig_basename, |
| | 1795 | \\const std = @import("std"); |
| | 1796 | \\ |
| | 1797 | \\pub fn build(b: *std.Build) void {{ |
| | 1798 | \\ _ = b; // stub |
| | 1799 | \\}} |
| | 1800 | \\ |
| | 1801 | , .{}) catch |err| switch (err) { |
| | 1802 | else => fatal("failed to create {q}: {t}", .{ Package.build_zig_basename, err }), |
| | 1803 | // `build.zig` already existing is okay: the user has just used `zig init` to set up |
| | 1804 | // their `build.zig.zon` *after* writing their `build.zig`. So this one isn't fatal. |
| | 1805 | error.PathAlreadyExists => { |
| | 1806 | std.log.info("successfully populated {q}, preserving existing {q}", .{ |
| | 1807 | Package.Manifest.basename, Package.build_zig_basename, |
| | 1808 | }); |
| | 1809 | return cleanExit(io); |
| | 1810 | }, |
| | 1811 | }; |
| | 1812 | std.log.info("successfully populated {q} and {q}", .{ Package.Manifest.basename, Package.build_zig_basename }); |
| | 1813 | return cleanExit(io); |
| | 1814 | }, |
| | 1815 | } |
| 1697 | } | 1816 | } |
| 1698 | | 1817 | |
| | 1818 | |
| | 1819 | |
| 1699 | fn markFailedStepsDirty(maker: *Maker) void { | 1820 | fn markFailedStepsDirty(maker: *Maker) void { |
| 1700 | const all_steps = maker.step_stack.keys(); | 1821 | const all_steps = maker.step_stack.keys(); |
| 1701 | | 1822 | |
| ... | @@ -3234,7 +3355,7 @@ fn loadManifest( | ... | @@ -3234,7 +3355,7 @@ fn loadManifest( |
| 3234 | \\ | 3355 | \\ |
| 3235 | , .{ | 3356 | , .{ |
| 3236 | options.root_name, | 3357 | options.root_name, |
| 3237 | build_options.version, | 3358 | builtin.zig_version_string, |
| 3238 | Package.Fingerprint.generate(rng.interface(), options.root_name).int(), | 3359 | Package.Fingerprint.generate(rng.interface(), options.root_name).int(), |
| 3239 | }) catch |e| { | 3360 | }) catch |e| { |
| 3240 | fatal("unable to write {s}: {t}", .{ Package.Manifest.basename, e }); | 3361 | fatal("unable to write {s}: {t}", .{ Package.Manifest.basename, e }); |
| ... | @@ -3244,7 +3365,7 @@ fn loadManifest( | ... | @@ -3244,7 +3365,7 @@ fn loadManifest( |
| 3244 | else => |e| fatal("unable to load {s}: {t}", .{ Package.Manifest.basename, e }), | 3365 | else => |e| fatal("unable to load {s}: {t}", .{ Package.Manifest.basename, e }), |
| 3245 | }; | 3366 | }; |
| 3246 | }; | 3367 | }; |
| 3247 | var ast = try Ast.parse(gpa, manifest_bytes, .zon); | 3368 | var ast = try std.zig.Ast.parse(gpa, manifest_bytes, .zon); |
| 3248 | errdefer ast.deinit(gpa); | 3369 | errdefer ast.deinit(gpa); |
| 3249 | | 3370 | |
| 3250 | if (ast.errors.len > 0) { | 3371 | if (ast.errors.len > 0) { |
| ... | @@ -3272,3 +3393,138 @@ fn loadManifest( | ... | @@ -3272,3 +3393,138 @@ fn loadManifest( |
| 3272 | return .{ manifest, ast }; | 3393 | return .{ manifest, ast }; |
| 3273 | } | 3394 | } |
| 3274 | | 3395 | |
| | 3396 | fn sanitizeExampleName(arena: Allocator, bytes: []const u8) error{OutOfMemory}![]const u8 { |
| | 3397 | var result: std.ArrayList(u8) = .empty; |
| | 3398 | for (bytes, 0..) |byte, i| switch (byte) { |
| | 3399 | '0'...'9' => { |
| | 3400 | if (i == 0) try result.append(arena, '_'); |
| | 3401 | try result.append(arena, byte); |
| | 3402 | }, |
| | 3403 | '_', 'a'...'z', 'A'...'Z' => try result.append(arena, byte), |
| | 3404 | '-', '.', ' ' => try result.append(arena, '_'), |
| | 3405 | else => continue, |
| | 3406 | }; |
| | 3407 | if (!std.zig.isValidId(result.items)) return "foo"; |
| | 3408 | if (result.items.len > Package.Manifest.max_name_len) |
| | 3409 | result.shrinkRetainingCapacity(Package.Manifest.max_name_len); |
| | 3410 | |
| | 3411 | return result.toOwnedSlice(arena); |
| | 3412 | } |
| | 3413 | |
| | 3414 | test sanitizeExampleName { |
| | 3415 | var arena_instance = std.heap.ArenaAllocator.init(std.testing.allocator); |
| | 3416 | defer arena_instance.deinit(); |
| | 3417 | const arena = arena_instance.allocator(); |
| | 3418 | |
| | 3419 | try std.testing.expectEqualStrings("foo_bar", try sanitizeExampleName(arena, "foo bar+")); |
| | 3420 | try std.testing.expectEqualStrings("foo", try sanitizeExampleName(arena, "")); |
| | 3421 | try std.testing.expectEqualStrings("foo", try sanitizeExampleName(arena, "!")); |
| | 3422 | try std.testing.expectEqualStrings("a", try sanitizeExampleName(arena, "!a")); |
| | 3423 | try std.testing.expectEqualStrings("a_b", try sanitizeExampleName(arena, "a.b!")); |
| | 3424 | try std.testing.expectEqualStrings("_01234", try sanitizeExampleName(arena, "01234")); |
| | 3425 | try std.testing.expectEqualStrings("foo", try sanitizeExampleName(arena, "error")); |
| | 3426 | try std.testing.expectEqualStrings("foo", try sanitizeExampleName(arena, "test")); |
| | 3427 | try std.testing.expectEqualStrings("tests", try sanitizeExampleName(arena, "tests")); |
| | 3428 | try std.testing.expectEqualStrings("test_project", try sanitizeExampleName(arena, "test project")); |
| | 3429 | } |
| | 3430 | |
| | 3431 | const Templates = struct { |
| | 3432 | zig_lib_directory: Cache.Directory, |
| | 3433 | dir: Io.Dir, |
| | 3434 | buffer: std.array_list.Managed(u8), |
| | 3435 | |
| | 3436 | fn deinit(templates: *Templates, io: Io) void { |
| | 3437 | templates.zig_lib_directory.handle.close(io); |
| | 3438 | templates.dir.close(io); |
| | 3439 | templates.buffer.deinit(); |
| | 3440 | templates.* = undefined; |
| | 3441 | } |
| | 3442 | |
| | 3443 | fn write( |
| | 3444 | templates: *Templates, |
| | 3445 | arena: Allocator, |
| | 3446 | io: Io, |
| | 3447 | out_dir: Io.Dir, |
| | 3448 | root_name: []const u8, |
| | 3449 | template_path: []const u8, |
| | 3450 | fingerprint: Package.Fingerprint, |
| | 3451 | ) !void { |
| | 3452 | if (Dir.path.dirname(template_path)) |dirname| { |
| | 3453 | out_dir.createDirPath(io, dirname) catch |err| { |
| | 3454 | fatal("unable to make path {q}: {t}", .{ dirname, err }); |
| | 3455 | }; |
| | 3456 | } |
| | 3457 | |
| | 3458 | const max_bytes = 10 * 1024 * 1024; |
| | 3459 | const contents = templates.dir.readFileAlloc(io, template_path, arena, .limited(max_bytes)) catch |err| { |
| | 3460 | fatal("unable to read template file {q}: {t}", .{ template_path, err }); |
| | 3461 | }; |
| | 3462 | templates.buffer.clearRetainingCapacity(); |
| | 3463 | try templates.buffer.ensureUnusedCapacity(contents.len); |
| | 3464 | var i: usize = 0; |
| | 3465 | while (i < contents.len) { |
| | 3466 | if (contents[i] == '_' or contents[i] == '.') { |
| | 3467 | // Both '_' and '.' are allowed because depending on the context |
| | 3468 | // one prefix will be valid, while the other might not. |
| | 3469 | if (std.mem.startsWith(u8, contents[i + 1 ..], "NAME")) { |
| | 3470 | try templates.buffer.appendSlice(root_name); |
| | 3471 | i += "_NAME".len; |
| | 3472 | continue; |
| | 3473 | } else if (std.mem.startsWith(u8, contents[i + 1 ..], "FINGERPRINT")) { |
| | 3474 | try templates.buffer.print("0x{x}", .{fingerprint.int()}); |
| | 3475 | i += "_FINGERPRINT".len; |
| | 3476 | continue; |
| | 3477 | } else if (std.mem.startsWith(u8, contents[i + 1 ..], "ZIGVER")) { |
| | 3478 | try templates.buffer.appendSlice(builtin.zig_version_string); |
| | 3479 | i += "_ZIGVER".len; |
| | 3480 | continue; |
| | 3481 | } |
| | 3482 | } |
| | 3483 | |
| | 3484 | try templates.buffer.append(contents[i]); |
| | 3485 | i += 1; |
| | 3486 | } |
| | 3487 | |
| | 3488 | return out_dir.writeFile(io, .{ |
| | 3489 | .sub_path = template_path, |
| | 3490 | .data = templates.buffer.items, |
| | 3491 | .flags = .{ .exclusive = true }, |
| | 3492 | }); |
| | 3493 | } |
| | 3494 | }; |
| | 3495 | fn writeSimpleTemplateFile(io: Io, file_name: []const u8, comptime format: []const u8, args: anytype) !void { |
| | 3496 | const f = try Io.Dir.cwd().createFile(io, file_name, .{ .exclusive = true }); |
| | 3497 | defer f.close(io); |
| | 3498 | var buf: [4096]u8 = undefined; |
| | 3499 | var fw = f.writer(io, &buf); |
| | 3500 | try fw.interface.print(format, args); |
| | 3501 | try fw.interface.flush(); |
| | 3502 | } |
| | 3503 | |
| | 3504 | fn findTemplates(gpa: Allocator, arena: Allocator, io: Io) Templates { |
| | 3505 | const cwd_path = std.zig.getResolvedCwd(io, arena) catch |err| { |
| | 3506 | fatal("unable to get cwd: {t}", .{err}); |
| | 3507 | }; |
| | 3508 | const self_exe_path = process.executablePathAlloc(io, arena) catch |err| { |
| | 3509 | fatal("unable to find self exe path: {t}", .{err}); |
| | 3510 | }; |
| | 3511 | var zig_lib_directory = std.zig.findZigLibDirFromSelfExe(arena, io, cwd_path, self_exe_path) catch |err| { |
| | 3512 | fatal("unable to find zig installation directory {q}: {t}", .{ self_exe_path, err }); |
| | 3513 | }; |
| | 3514 | |
| | 3515 | const s = Dir.path.sep_str; |
| | 3516 | const template_sub_path = "init"; |
| | 3517 | const template_dir = zig_lib_directory.handle.openDir(io, template_sub_path, .{}) catch |err| { |
| | 3518 | const path = zig_lib_directory.path orelse "."; |
| | 3519 | fatal("unable to open zig project template directory '{s}{s}{s}': {t}", .{ |
| | 3520 | path, s, template_sub_path, err, |
| | 3521 | }); |
| | 3522 | }; |
| | 3523 | |
| | 3524 | return .{ |
| | 3525 | .zig_lib_directory = zig_lib_directory, |
| | 3526 | .dir = template_dir, |
| | 3527 | .buffer = std.array_list.Managed(u8).init(gpa), |
| | 3528 | }; |
| | 3529 | } |
| | 3530 | |