authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-02-21 11:43:31-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-02-21 11:43:31-05:00
log7f691b3fe26f623d108a6b2b2018bbc3aa999224
tree81814816cfde6610a4b965d9d3799de3cb3eaccd
parent05da5b32a820c031001098034840940964f41a81
parentf94cbab3acc3b31464f45872c1f700874eecb23e
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #14664 from mlugg/feat/new-module-cli

New module CLI

34 files changed, 784 insertions(+), 248 deletions(-)

CMakeLists.txt+4-2
......@@ -748,7 +748,8 @@ set(BUILD_ZIG2_ARGS
748748 build-exe src/main.zig -ofmt=c -lc
749749 -OReleaseSmall
750750 --name zig2 -femit-bin="${ZIG2_C_SOURCE}"
751 --pkg-begin build_options "${ZIG_CONFIG_ZIG_OUT}" --pkg-end
751 --mod "build_options::${ZIG_CONFIG_ZIG_OUT}"
752 --deps build_options
752753 -target "${HOST_TARGET_TRIPLE}"
753754)
754755
......@@ -765,7 +766,8 @@ set(BUILD_COMPILER_RT_ARGS
765766 build-obj lib/compiler_rt.zig -ofmt=c
766767 -OReleaseSmall
767768 --name compiler_rt -femit-bin="${ZIG_COMPILER_RT_C_SOURCE}"
768 --pkg-begin build_options "${ZIG_CONFIG_ZIG_OUT}" --pkg-end
769 --mod "build_options::${ZIG_CONFIG_ZIG_OUT}"
770 --deps build_options
769771 -target "${HOST_TARGET_TRIPLE}"
770772)
771773
ci/x86_64-windows-debug.ps1+2-1
......@@ -87,7 +87,8 @@ CheckLastExitCode
8787 -OReleaseSmall `
8888 --name compiler_rt `
8989 -femit-bin="compiler_rt-x86_64-windows-msvc.c" `
90 --pkg-begin build_options config.zig --pkg-end `
90 --mod build_options::config.zig `
91 --deps build_options `
9192 -target x86_64-windows-msvc
9293CheckLastExitCode
9394
ci/x86_64-windows-release.ps1+2-1
......@@ -87,7 +87,8 @@ CheckLastExitCode
8787 -OReleaseSmall `
8888 --name compiler_rt `
8989 -femit-bin="compiler_rt-x86_64-windows-msvc.c" `
90 --pkg-begin build_options config.zig --pkg-end `
90 --mod build_options::config.zig `
91 --deps build_options `
9192 -target x86_64-windows-msvc
9293CheckLastExitCode
9394
lib/std/Build/CompileStep.zig+107-20
......@@ -955,7 +955,10 @@ pub fn addFrameworkPath(self: *CompileStep, dir_path: []const u8) void {
955955/// package's module table using `name`.
956956pub fn addModule(cs: *CompileStep, name: []const u8, module: *Module) void {
957957 cs.modules.put(cs.builder.dupe(name), module) catch @panic("OOM");
958 cs.addRecursiveBuildDeps(module);
958
959 var done = std.AutoHashMap(*Module, void).init(cs.builder.allocator);
960 defer done.deinit();
961 cs.addRecursiveBuildDeps(module, &done) catch @panic("OOM");
959962}
960963
961964/// Adds a module to be used with `@import` without exposing it in the current
......@@ -969,10 +972,12 @@ pub fn addOptions(cs: *CompileStep, module_name: []const u8, options: *OptionsSt
969972 addModule(cs, module_name, options.createModule());
970973}
971974
972fn addRecursiveBuildDeps(cs: *CompileStep, module: *Module) void {
975fn addRecursiveBuildDeps(cs: *CompileStep, module: *Module, done: *std.AutoHashMap(*Module, void)) !void {
976 if (done.contains(module)) return;
977 try done.put(module, {});
973978 module.source_file.addStepDependencies(&cs.step);
974979 for (module.dependencies.values()) |dep| {
975 cs.addRecursiveBuildDeps(dep);
980 try cs.addRecursiveBuildDeps(dep, done);
976981 }
977982}
978983
......@@ -1031,22 +1036,110 @@ fn linkLibraryOrObject(self: *CompileStep, other: *CompileStep) void {
10311036fn appendModuleArgs(
10321037 cs: *CompileStep,
10331038 zig_args: *ArrayList([]const u8),
1034 name: []const u8,
1035 module: *Module,
10361039) error{OutOfMemory}!void {
1037 try zig_args.append("--pkg-begin");
1038 try zig_args.append(name);
1039 try zig_args.append(module.builder.pathFromRoot(module.source_file.getPath(module.builder)));
1040 // First, traverse the whole dependency graph and give every module a unique name, ideally one
1041 // named after what it's called somewhere in the graph. It will help here to have both a mapping
1042 // from module to name and a set of all the currently-used names.
1043 var mod_names = std.AutoHashMap(*Module, []const u8).init(cs.builder.allocator);
1044 var names = std.StringHashMap(void).init(cs.builder.allocator);
1045
1046 var to_name = std.ArrayList(struct {
1047 name: []const u8,
1048 mod: *Module,
1049 }).init(cs.builder.allocator);
1050 {
1051 var it = cs.modules.iterator();
1052 while (it.next()) |kv| {
1053 // While we're traversing the root dependencies, let's make sure that no module names
1054 // have colons in them, since the CLI forbids it. We handle this for transitive
1055 // dependencies further down.
1056 if (std.mem.indexOfScalar(u8, kv.key_ptr.*, ':') != null) {
1057 @panic("Module names cannot contain colons");
1058 }
1059 try to_name.append(.{
1060 .name = kv.key_ptr.*,
1061 .mod = kv.value_ptr.*,
1062 });
1063 }
1064 }
1065
1066 while (to_name.popOrNull()) |dep| {
1067 if (mod_names.contains(dep.mod)) continue;
1068
1069 // We'll use this buffer to store the name we decide on
1070 var buf = try cs.builder.allocator.alloc(u8, dep.name.len + 32);
1071 // First, try just the exposed dependency name
1072 std.mem.copy(u8, buf, dep.name);
1073 var name = buf[0..dep.name.len];
1074 var n: usize = 0;
1075 while (names.contains(name)) {
1076 // If that failed, append an incrementing number to the end
1077 name = std.fmt.bufPrint(buf, "{s}{}", .{ dep.name, n }) catch unreachable;
1078 n += 1;
1079 }
1080
1081 try mod_names.put(dep.mod, name);
1082 try names.put(name, {});
1083
1084 var it = dep.mod.dependencies.iterator();
1085 while (it.next()) |kv| {
1086 // Same colon-in-name check as above, but for transitive dependencies.
1087 if (std.mem.indexOfScalar(u8, kv.key_ptr.*, ':') != null) {
1088 @panic("Module names cannot contain colons");
1089 }
1090 try to_name.append(.{
1091 .name = kv.key_ptr.*,
1092 .mod = kv.value_ptr.*,
1093 });
1094 }
1095 }
10401096
1097 // Since the module names given to the CLI are based off of the exposed names, we already know
1098 // that none of the CLI names have colons in them, so there's no need to check that explicitly.
1099
1100 // Every module in the graph is now named; output their definitions
10411101 {
1042 const keys = module.dependencies.keys();
1043 for (module.dependencies.values(), 0..) |sub_module, i| {
1044 const sub_name = keys[i];
1045 try cs.appendModuleArgs(zig_args, sub_name, sub_module);
1102 var it = mod_names.iterator();
1103 while (it.next()) |kv| {
1104 const mod = kv.key_ptr.*;
1105 const name = kv.value_ptr.*;
1106
1107 const deps_str = try constructDepString(cs.builder.allocator, mod_names, mod.dependencies);
1108 const src = mod.builder.pathFromRoot(mod.source_file.getPath(mod.builder));
1109 try zig_args.append("--mod");
1110 try zig_args.append(try std.fmt.allocPrint(cs.builder.allocator, "{s}:{s}:{s}", .{ name, deps_str, src }));
10461111 }
10471112 }
10481113
1049 try zig_args.append("--pkg-end");
1114 // Lastly, output the root dependencies
1115 const deps_str = try constructDepString(cs.builder.allocator, mod_names, cs.modules);
1116 if (deps_str.len > 0) {
1117 try zig_args.append("--deps");
1118 try zig_args.append(deps_str);
1119 }
1120}
1121
1122fn constructDepString(
1123 allocator: std.mem.Allocator,
1124 mod_names: std.AutoHashMap(*Module, []const u8),
1125 deps: std.StringArrayHashMap(*Module),
1126) ![]const u8 {
1127 var deps_str = std.ArrayList(u8).init(allocator);
1128 var it = deps.iterator();
1129 while (it.next()) |kv| {
1130 const expose = kv.key_ptr.*;
1131 const name = mod_names.get(kv.value_ptr.*).?;
1132 if (std.mem.eql(u8, expose, name)) {
1133 try deps_str.writer().print("{s},", .{name});
1134 } else {
1135 try deps_str.writer().print("{s}={s},", .{ expose, name });
1136 }
1137 }
1138 if (deps_str.items.len > 0) {
1139 return deps_str.items[0 .. deps_str.items.len - 1]; // omit trailing comma
1140 } else {
1141 return "";
1142 }
10501143}
10511144
10521145fn make(step: *Step) !void {
......@@ -1573,13 +1666,7 @@ fn make(step: *Step) !void {
15731666 try zig_args.append("--test-no-exec");
15741667 }
15751668
1576 {
1577 const keys = self.modules.keys();
1578 for (self.modules.values(), 0..) |module, i| {
1579 const name = keys[i];
1580 try self.appendModuleArgs(&zig_args, name, module);
1581 }
1582 }
1669 try self.appendModuleArgs(&zig_args);
15831670
15841671 for (self.include_dirs.items) |include_dir| {
15851672 switch (include_dir) {
src/Autodoc.zig+1-9
......@@ -860,17 +860,9 @@ fn walkInstruction(
860860 const str_tok = data[inst_index].str_tok;
861861 var path = str_tok.get(file.zir);
862862
863 const maybe_other_package: ?*Package = blk: {
864 if (self.module.main_pkg_is_std and std.mem.eql(u8, path, "std")) {
865 path = "std";
866 break :blk self.module.main_pkg;
867 } else {
868 break :blk file.pkg.table.get(path);
869 }
870 };
871863 // importFile cannot error out since all files
872864 // are already loaded at this point
873 if (maybe_other_package) |other_package| {
865 if (file.pkg.table.get(path)) |other_package| {
874866 const result = try self.packages.getOrPut(self.arena, other_package);
875867
876868 // Immediately add this package to the import table of our
src/Compilation.zig+140-82
......@@ -1596,36 +1596,53 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
15961596
15971597 const builtin_pkg = try Package.createWithDir(
15981598 gpa,
1599 "builtin",
16001599 zig_cache_artifact_directory,
16011600 null,
16021601 "builtin.zig",
16031602 );
16041603 errdefer builtin_pkg.destroy(gpa);
16051604
1606 const std_pkg = try Package.createWithDir(
1607 gpa,
1608 "std",
1609 options.zig_lib_directory,
1610 "std",
1611 "std.zig",
1612 );
1613 errdefer std_pkg.destroy(gpa);
1605 // When you're testing std, the main module is std. In that case, we'll just set the std
1606 // module to the main one, since avoiding the errors caused by duplicating it is more
1607 // effort than it's worth.
1608 const main_pkg_is_std = m: {
1609 const std_path = try std.fs.path.resolve(arena, &[_][]const u8{
1610 options.zig_lib_directory.path orelse ".",
1611 "std",
1612 "std.zig",
1613 });
1614 defer arena.free(std_path);
1615 const main_path = try std.fs.path.resolve(arena, &[_][]const u8{
1616 main_pkg.root_src_directory.path orelse ".",
1617 main_pkg.root_src_path,
1618 });
1619 defer arena.free(main_path);
1620 break :m mem.eql(u8, main_path, std_path);
1621 };
1622
1623 const std_pkg = if (main_pkg_is_std)
1624 main_pkg
1625 else
1626 try Package.createWithDir(
1627 gpa,
1628 options.zig_lib_directory,
1629 "std",
1630 "std.zig",
1631 );
1632
1633 errdefer if (!main_pkg_is_std) std_pkg.destroy(gpa);
16141634
16151635 const root_pkg = if (options.is_test) root_pkg: {
1616 // TODO: we currently have two packages named 'root' here, which is weird. This
1617 // should be changed as part of the resolution of #12201
16181636 const test_pkg = if (options.test_runner_path) |test_runner| test_pkg: {
16191637 const test_dir = std.fs.path.dirname(test_runner);
16201638 const basename = std.fs.path.basename(test_runner);
1621 const pkg = try Package.create(gpa, "root", test_dir, basename);
1639 const pkg = try Package.create(gpa, test_dir, basename);
16221640
16231641 // copy package table from main_pkg to root_pkg
16241642 pkg.table = try main_pkg.table.clone(gpa);
16251643 break :test_pkg pkg;
16261644 } else try Package.createWithDir(
16271645 gpa,
1628 "root",
16291646 options.zig_lib_directory,
16301647 null,
16311648 "test_runner.zig",
......@@ -1639,7 +1656,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
16391656 const compiler_rt_pkg = if (include_compiler_rt and options.output_mode == .Obj) compiler_rt_pkg: {
16401657 break :compiler_rt_pkg try Package.createWithDir(
16411658 gpa,
1642 "compiler_rt",
16431659 options.zig_lib_directory,
16441660 null,
16451661 "compiler_rt.zig",
......@@ -1647,28 +1663,14 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
16471663 } else null;
16481664 errdefer if (compiler_rt_pkg) |p| p.destroy(gpa);
16491665
1650 try main_pkg.addAndAdopt(gpa, builtin_pkg);
1651 try main_pkg.add(gpa, root_pkg);
1652 try main_pkg.addAndAdopt(gpa, std_pkg);
1666 try main_pkg.add(gpa, "builtin", builtin_pkg);
1667 try main_pkg.add(gpa, "root", root_pkg);
1668 try main_pkg.add(gpa, "std", std_pkg);
16531669
16541670 if (compiler_rt_pkg) |p| {
1655 try main_pkg.addAndAdopt(gpa, p);
1671 try main_pkg.add(gpa, "compiler_rt", p);
16561672 }
16571673
1658 const main_pkg_is_std = m: {
1659 const std_path = try std.fs.path.resolve(arena, &[_][]const u8{
1660 std_pkg.root_src_directory.path orelse ".",
1661 std_pkg.root_src_path,
1662 });
1663 defer arena.free(std_path);
1664 const main_path = try std.fs.path.resolve(arena, &[_][]const u8{
1665 main_pkg.root_src_directory.path orelse ".",
1666 main_pkg.root_src_path,
1667 });
1668 defer arena.free(main_path);
1669 break :m mem.eql(u8, main_path, std_path);
1670 };
1671
16721674 // Pre-open the directory handles for cached ZIR code so that it does not need
16731675 // to redundantly happen for each AstGen operation.
16741676 const zir_sub_dir = "z";
......@@ -1705,7 +1707,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
17051707 .gpa = gpa,
17061708 .comp = comp,
17071709 .main_pkg = main_pkg,
1708 .main_pkg_is_std = main_pkg_is_std,
17091710 .root_pkg = root_pkg,
17101711 .zig_cache_artifact_directory = zig_cache_artifact_directory,
17111712 .global_zir_cache = global_zir_cache,
......@@ -2772,6 +2773,111 @@ fn emitOthers(comp: *Compilation) void {
27722773 }
27732774}
27742775
2776fn reportMultiModuleErrors(mod: *Module) !void {
2777 // Some cases can give you a whole bunch of multi-module errors, which it's not helpful to
2778 // print all of, so we'll cap the number of these to emit.
2779 var num_errors: u32 = 0;
2780 const max_errors = 5;
2781 // Attach the "some omitted" note to the final error message
2782 var last_err: ?*Module.ErrorMsg = null;
2783
2784 for (mod.import_table.values()) |file| {
2785 if (!file.multi_pkg) continue;
2786
2787 num_errors += 1;
2788 if (num_errors > max_errors) continue;
2789
2790 const err = err_blk: {
2791 // Like with errors, let's cap the number of notes to prevent a huge error spew.
2792 const max_notes = 5;
2793 const omitted = file.references.items.len -| max_notes;
2794 const num_notes = file.references.items.len - omitted;
2795
2796 const notes = try mod.gpa.alloc(Module.ErrorMsg, if (omitted > 0) num_notes + 1 else num_notes);
2797 errdefer mod.gpa.free(notes);
2798
2799 for (notes[0..num_notes], file.references.items[0..num_notes], 0..) |*note, ref, i| {
2800 errdefer for (notes[0..i]) |*n| n.deinit(mod.gpa);
2801 note.* = switch (ref) {
2802 .import => |loc| blk: {
2803 const name = try loc.file_scope.pkg.getName(mod.gpa, mod.*);
2804 defer mod.gpa.free(name);
2805 break :blk try Module.ErrorMsg.init(
2806 mod.gpa,
2807 loc,
2808 "imported from module {s}",
2809 .{name},
2810 );
2811 },
2812 .root => |pkg| blk: {
2813 const name = try pkg.getName(mod.gpa, mod.*);
2814 defer mod.gpa.free(name);
2815 break :blk try Module.ErrorMsg.init(
2816 mod.gpa,
2817 .{ .file_scope = file, .parent_decl_node = 0, .lazy = .entire_file },
2818 "root of module {s}",
2819 .{name},
2820 );
2821 },
2822 };
2823 }
2824 errdefer for (notes[0..num_notes]) |*n| n.deinit(mod.gpa);
2825
2826 if (omitted > 0) {
2827 notes[num_notes] = try Module.ErrorMsg.init(
2828 mod.gpa,
2829 .{ .file_scope = file, .parent_decl_node = 0, .lazy = .entire_file },
2830 "{} more references omitted",
2831 .{omitted},
2832 );
2833 }
2834 errdefer if (omitted > 0) notes[num_notes].deinit(mod.gpa);
2835
2836 const err = try Module.ErrorMsg.create(
2837 mod.gpa,
2838 .{ .file_scope = file, .parent_decl_node = 0, .lazy = .entire_file },
2839 "file exists in multiple modules",
2840 .{},
2841 );
2842 err.notes = notes;
2843 break :err_blk err;
2844 };
2845 errdefer err.destroy(mod.gpa);
2846 try mod.failed_files.putNoClobber(mod.gpa, file, err);
2847 last_err = err;
2848 }
2849
2850 // If we omitted any errors, add a note saying that
2851 if (num_errors > max_errors) {
2852 const err = last_err.?;
2853
2854 // There isn't really any meaningful place to put this note, so just attach it to the
2855 // last failed file
2856 var note = try Module.ErrorMsg.init(
2857 mod.gpa,
2858 err.src_loc,
2859 "{} more errors omitted",
2860 .{num_errors - max_errors},
2861 );
2862 errdefer note.deinit(mod.gpa);
2863
2864 const i = err.notes.len;
2865 err.notes = try mod.gpa.realloc(err.notes, i + 1);
2866 err.notes[i] = note;
2867 }
2868
2869 // Now that we've reported the errors, we need to deal with
2870 // dependencies. Any file referenced by a multi_pkg file should also be
2871 // marked multi_pkg and have its status set to astgen_failure, as it's
2872 // ambiguous which package they should be analyzed as a part of. We need
2873 // to add this flag after reporting the errors however, as otherwise
2874 // we'd get an error for every single downstream file, which wouldn't be
2875 // very useful.
2876 for (mod.import_table.values()) |file| {
2877 if (file.multi_pkg) file.recursiveMarkMultiPkg(mod);
2878 }
2879}
2880
27752881/// Having the file open for writing is problematic as far as executing the
27762882/// binary is concerned. This will remove the write flag, or close the file,
27772883/// or whatever is needed so that it can be executed.
......@@ -3098,54 +3204,7 @@ pub fn performAllTheWork(
30983204 }
30993205
31003206 if (comp.bin_file.options.module) |mod| {
3101 for (mod.import_table.values()) |file| {
3102 if (!file.multi_pkg) continue;
3103 const err = err_blk: {
3104 const notes = try mod.gpa.alloc(Module.ErrorMsg, file.references.items.len);
3105 errdefer mod.gpa.free(notes);
3106
3107 for (notes, 0..) |*note, i| {
3108 errdefer for (notes[0..i]) |*n| n.deinit(mod.gpa);
3109 note.* = switch (file.references.items[i]) {
3110 .import => |loc| try Module.ErrorMsg.init(
3111 mod.gpa,
3112 loc,
3113 "imported from package {s}",
3114 .{loc.file_scope.pkg.name},
3115 ),
3116 .root => |pkg| try Module.ErrorMsg.init(
3117 mod.gpa,
3118 .{ .file_scope = file, .parent_decl_node = 0, .lazy = .entire_file },
3119 "root of package {s}",
3120 .{pkg.name},
3121 ),
3122 };
3123 }
3124 errdefer for (notes) |*n| n.deinit(mod.gpa);
3125
3126 const err = try Module.ErrorMsg.create(
3127 mod.gpa,
3128 .{ .file_scope = file, .parent_decl_node = 0, .lazy = .entire_file },
3129 "file exists in multiple packages",
3130 .{},
3131 );
3132 err.notes = notes;
3133 break :err_blk err;
3134 };
3135 errdefer err.destroy(mod.gpa);
3136 try mod.failed_files.putNoClobber(mod.gpa, file, err);
3137 }
3138
3139 // Now that we've reported the errors, we need to deal with
3140 // dependencies. Any file referenced by a multi_pkg file should also be
3141 // marked multi_pkg and have its status set to astgen_failure, as it's
3142 // ambiguous which package they should be analyzed as a part of. We need
3143 // to add this flag after reporting the errors however, as otherwise
3144 // we'd get an error for every single downstream file, which wouldn't be
3145 // very useful.
3146 for (mod.import_table.values()) |file| {
3147 if (file.multi_pkg) file.recursiveMarkMultiPkg(mod);
3148 }
3207 try reportMultiModuleErrors(mod);
31493208 }
31503209
31513210 {
......@@ -5408,7 +5467,6 @@ fn buildOutputFromZig(
54085467 var main_pkg: Package = .{
54095468 .root_src_directory = comp.zig_lib_directory,
54105469 .root_src_path = src_basename,
5411 .name = "root",
54125470 };
54135471 defer main_pkg.deinitTable(comp.gpa);
54145472 const root_name = src_basename[0 .. src_basename.len - std.fs.path.extension(src_basename).len];
src/Module.zig+46-23
......@@ -144,10 +144,6 @@ stage1_flags: packed struct {
144144} = .{},
145145
146146job_queued_update_builtin_zig: bool = true,
147/// This makes it so that we can run `zig test` on the standard library.
148/// Otherwise, the logic for scanning test decls skips all of them because
149/// `main_pkg != std_pkg`.
150main_pkg_is_std: bool,
151147
152148compile_log_text: ArrayListUnmanaged(u8) = .{},
153149
......@@ -1950,7 +1946,7 @@ pub const File = struct {
19501946 prev_zir: ?*Zir = null,
19511947
19521948 /// A single reference to a file.
1953 const Reference = union(enum) {
1949 pub const Reference = union(enum) {
19541950 /// The file is imported directly (i.e. not as a package) with @import.
19551951 import: SrcLoc,
19561952 /// The file is the root of a package.
......@@ -2113,7 +2109,27 @@ pub const File = struct {
21132109
21142110 /// Add a reference to this file during AstGen.
21152111 pub fn addReference(file: *File, mod: Module, ref: Reference) !void {
2116 try file.references.append(mod.gpa, ref);
2112 // Don't add the same module root twice. Note that since we always add module roots at the
2113 // front of the references array (see below), this loop is actually O(1) on valid code.
2114 if (ref == .root) {
2115 for (file.references.items) |other| {
2116 switch (other) {
2117 .root => |r| if (ref.root == r) return,
2118 else => break, // reached the end of the "is-root" references
2119 }
2120 }
2121 }
2122
2123 switch (ref) {
2124 // We put root references at the front of the list both to make the above loop fast and
2125 // to make multi-module errors more helpful (since "root-of" notes are generally more
2126 // informative than "imported-from" notes). This path is hit very rarely, so the speed
2127 // of the insert operation doesn't matter too much.
2128 .root => try file.references.insert(mod.gpa, 0, ref),
2129
2130 // Other references we'll just put at the end.
2131 else => try file.references.append(mod.gpa, ref),
2132 }
21172133
21182134 const pkg = switch (ref) {
21192135 .import => |loc| loc.file_scope.pkg,
......@@ -2128,7 +2144,10 @@ pub const File = struct {
21282144 file.multi_pkg = true;
21292145 file.status = .astgen_failure;
21302146
2131 std.debug.assert(file.zir_loaded);
2147 // We can only mark children as failed if the ZIR is loaded, which may not
2148 // be the case if there were other astgen failures in this file
2149 if (!file.zir_loaded) return;
2150
21322151 const imports_index = file.zir.extra[@enumToInt(Zir.ExtraIndex.imports)];
21332152 if (imports_index == 0) return;
21342153 const extra = file.zir.extraData(Zir.Inst.Imports, imports_index);
......@@ -3323,10 +3342,19 @@ pub fn deinit(mod: *Module) void {
33233342 // The callsite of `Compilation.create` owns the `main_pkg`, however
33243343 // Module owns the builtin and std packages that it adds.
33253344 if (mod.main_pkg.table.fetchRemove("builtin")) |kv| {
3345 gpa.free(kv.key);
33263346 kv.value.destroy(gpa);
33273347 }
33283348 if (mod.main_pkg.table.fetchRemove("std")) |kv| {
3329 kv.value.destroy(gpa);
3349 gpa.free(kv.key);
3350 // It's possible for main_pkg to be std when running 'zig test'! In this case, we must not
3351 // destroy it, since it would lead to a double-free.
3352 if (kv.value != mod.main_pkg) {
3353 kv.value.destroy(gpa);
3354 }
3355 }
3356 if (mod.main_pkg.table.fetchRemove("root")) |kv| {
3357 gpa.free(kv.key);
33303358 }
33313359 if (mod.root_pkg != mod.main_pkg) {
33323360 mod.root_pkg.destroy(gpa);
......@@ -4808,11 +4836,14 @@ pub fn importPkg(mod: *Module, pkg: *Package) !ImportFileResult {
48084836
48094837 const gop = try mod.import_table.getOrPut(gpa, resolved_path);
48104838 errdefer _ = mod.import_table.pop();
4811 if (gop.found_existing) return ImportFileResult{
4812 .file = gop.value_ptr.*,
4813 .is_new = false,
4814 .is_pkg = true,
4815 };
4839 if (gop.found_existing) {
4840 try gop.value_ptr.*.addReference(mod.*, .{ .root = pkg });
4841 return ImportFileResult{
4842 .file = gop.value_ptr.*,
4843 .is_new = false,
4844 .is_pkg = true,
4845 };
4846 }
48164847
48174848 const sub_file_path = try gpa.dupe(u8, pkg.root_src_path);
48184849 errdefer gpa.free(sub_file_path);
......@@ -5208,22 +5239,14 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err
52085239 // test decl with no name. Skip the part where we check against
52095240 // the test name filter.
52105241 if (!comp.bin_file.options.is_test) break :blk false;
5211 if (decl_pkg != mod.main_pkg) {
5212 if (!mod.main_pkg_is_std) break :blk false;
5213 const std_pkg = mod.main_pkg.table.get("std").?;
5214 if (std_pkg != decl_pkg) break :blk false;
5215 }
5242 if (decl_pkg != mod.main_pkg) break :blk false;
52165243 try mod.test_functions.put(gpa, new_decl_index, {});
52175244 break :blk true;
52185245 },
52195246 else => blk: {
52205247 if (!is_named_test) break :blk false;
52215248 if (!comp.bin_file.options.is_test) break :blk false;
5222 if (decl_pkg != mod.main_pkg) {
5223 if (!mod.main_pkg_is_std) break :blk false;
5224 const std_pkg = mod.main_pkg.table.get("std").?;
5225 if (std_pkg != decl_pkg) break :blk false;
5226 }
5249 if (decl_pkg != mod.main_pkg) break :blk false;
52275250 if (comp.test_filter) |test_filter| {
52285251 if (mem.indexOf(u8, decl_name, test_filter) == null) {
52295252 break :blk false;
src/Package.zig+94-29
......@@ -22,17 +22,16 @@ pub const Table = std.StringHashMapUnmanaged(*Package);
2222root_src_directory: Compilation.Directory,
2323/// Relative to `root_src_directory`. May contain path separators.
2424root_src_path: []const u8,
25/// The dependency table of this module. Shared dependencies such as 'std', 'builtin', and 'root'
26/// are not specified in every dependency table, but instead only in the table of `main_pkg`.
27/// `Module.importFile` is responsible for detecting these names and using the correct package.
2528table: Table = .{},
26parent: ?*Package = null,
2729/// Whether to free `root_src_directory` on `destroy`.
2830root_src_directory_owned: bool = false,
29/// This information can be recovered from 'table', but it's more convenient to store on the package.
30name: []const u8,
3131
3232/// Allocate a Package. No references to the slices passed are kept.
3333pub fn create(
3434 gpa: Allocator,
35 name: []const u8,
3635 /// Null indicates the current working directory
3736 root_src_dir_path: ?[]const u8,
3837 /// Relative to root_src_dir_path
......@@ -47,9 +46,6 @@ pub fn create(
4746 const owned_src_path = try gpa.dupe(u8, root_src_path);
4847 errdefer gpa.free(owned_src_path);
4948
50 const owned_name = try gpa.dupe(u8, name);
51 errdefer gpa.free(owned_name);
52
5349 ptr.* = .{
5450 .root_src_directory = .{
5551 .path = owned_dir_path,
......@@ -57,7 +53,6 @@ pub fn create(
5753 },
5854 .root_src_path = owned_src_path,
5955 .root_src_directory_owned = true,
60 .name = owned_name,
6156 };
6257
6358 return ptr;
......@@ -65,7 +60,6 @@ pub fn create(
6560
6661pub fn createWithDir(
6762 gpa: Allocator,
68 name: []const u8,
6963 directory: Compilation.Directory,
7064 /// Relative to `directory`. If null, means `directory` is the root src dir
7165 /// and is owned externally.
......@@ -79,9 +73,6 @@ pub fn createWithDir(
7973 const owned_src_path = try gpa.dupe(u8, root_src_path);
8074 errdefer gpa.free(owned_src_path);
8175
82 const owned_name = try gpa.dupe(u8, name);
83 errdefer gpa.free(owned_name);
84
8576 if (root_src_dir_path) |p| {
8677 const owned_dir_path = try directory.join(gpa, &[1][]const u8{p});
8778 errdefer gpa.free(owned_dir_path);
......@@ -93,14 +84,12 @@ pub fn createWithDir(
9384 },
9485 .root_src_directory_owned = true,
9586 .root_src_path = owned_src_path,
96 .name = owned_name,
9787 };
9888 } else {
9989 ptr.* = .{
10090 .root_src_directory = directory,
10191 .root_src_directory_owned = false,
10292 .root_src_path = owned_src_path,
103 .name = owned_name,
10493 };
10594 }
10695 return ptr;
......@@ -110,7 +99,6 @@ pub fn createWithDir(
11099/// inside its table; the caller is responsible for calling destroy() on them.
111100pub fn destroy(pkg: *Package, gpa: Allocator) void {
112101 gpa.free(pkg.root_src_path);
113 gpa.free(pkg.name);
114102
115103 if (pkg.root_src_directory_owned) {
116104 // If root_src_directory.path is null then the handle is the cwd()
......@@ -130,15 +118,97 @@ pub fn deinitTable(pkg: *Package, gpa: Allocator) void {
130118 pkg.table.deinit(gpa);
131119}
132120
133pub fn add(pkg: *Package, gpa: Allocator, package: *Package) !void {
121pub fn add(pkg: *Package, gpa: Allocator, name: []const u8, package: *Package) !void {
134122 try pkg.table.ensureUnusedCapacity(gpa, 1);
135 pkg.table.putAssumeCapacityNoClobber(package.name, package);
123 const name_dupe = try gpa.dupe(u8, name);
124 pkg.table.putAssumeCapacityNoClobber(name_dupe, package);
136125}
137126
138pub fn addAndAdopt(parent: *Package, gpa: Allocator, child: *Package) !void {
139 assert(child.parent == null); // make up your mind, who is the parent??
140 child.parent = parent;
141 return parent.add(gpa, child);
127/// Compute a readable name for the package. The returned name should be freed from gpa. This
128/// function is very slow, as it traverses the whole package hierarchy to find a path to this
129/// package. It should only be used for error output.
130pub fn getName(target: *const Package, gpa: Allocator, mod: Module) ![]const u8 {
131 // we'll do a breadth-first search from the root module to try and find a short name for this
132 // module, using a TailQueue of module/parent pairs. note that the "parent" there is just the
133 // first-found shortest path - a module may be children of arbitrarily many other modules.
134 // also, this path may vary between executions due to hashmap iteration order, but that doesn't
135 // matter too much.
136 var node_arena = std.heap.ArenaAllocator.init(gpa);
137 defer node_arena.deinit();
138 const Parented = struct {
139 parent: ?*const @This(),
140 mod: *const Package,
141 };
142 const Queue = std.TailQueue(Parented);
143 var to_check: Queue = .{};
144
145 {
146 const new = try node_arena.allocator().create(Queue.Node);
147 new.* = .{ .data = .{ .parent = null, .mod = mod.root_pkg } };
148 to_check.prepend(new);
149 }
150
151 if (mod.main_pkg != mod.root_pkg) {
152 const new = try node_arena.allocator().create(Queue.Node);
153 // TODO: once #12201 is resolved, we may want a way of indicating a different name for this
154 new.* = .{ .data = .{ .parent = null, .mod = mod.main_pkg } };
155 to_check.prepend(new);
156 }
157
158 // set of modules we've already checked to prevent loops
159 var checked = std.AutoHashMap(*const Package, void).init(gpa);
160 defer checked.deinit();
161
162 const linked = while (to_check.pop()) |node| {
163 const check = &node.data;
164
165 if (checked.contains(check.mod)) continue;
166 try checked.put(check.mod, {});
167
168 if (check.mod == target) break check;
169
170 var it = check.mod.table.iterator();
171 while (it.next()) |kv| {
172 var new = try node_arena.allocator().create(Queue.Node);
173 new.* = .{ .data = .{
174 .parent = check,
175 .mod = kv.value_ptr.*,
176 } };
177 to_check.prepend(new);
178 }
179 } else {
180 // this can happen for e.g. @cImport packages
181 return gpa.dupe(u8, "<unnamed>");
182 };
183
184 // we found a path to the module! unfortunately, we can only traverse *up* it, so we have to put
185 // all the names into a buffer so we can then print them in order.
186 var names = std.ArrayList([]const u8).init(gpa);
187 defer names.deinit();
188
189 var cur: *const Parented = linked;
190 while (cur.parent) |parent| : (cur = parent) {
191 // find cur's name in parent
192 var it = parent.mod.table.iterator();
193 const name = while (it.next()) |kv| {
194 if (kv.value_ptr.* == cur.mod) {
195 break kv.key_ptr.*;
196 }
197 } else unreachable;
198 try names.append(name);
199 }
200
201 // finally, print the names into a buffer!
202 var buf = std.ArrayList(u8).init(gpa);
203 defer buf.deinit();
204 try buf.writer().writeAll("root");
205 var i: usize = names.items.len;
206 while (i > 0) {
207 i -= 1;
208 try buf.writer().print(".{s}", .{names.items[i]});
209 }
210
211 return buf.toOwnedSlice();
142212}
143213
144214pub const build_zig_basename = "build.zig";
......@@ -236,7 +306,7 @@ pub fn fetchAndAddDependencies(
236306 color,
237307 );
238308
239 try addAndAdopt(pkg, gpa, sub_pkg);
309 try add(pkg, gpa, fqn, sub_pkg);
240310
241311 try dependencies_source.writer().print(" pub const {s} = @import(\"{}\");\n", .{
242312 std.zig.fmtId(fqn), std.zig.fmtEscapes(fqn),
......@@ -248,7 +318,6 @@ pub fn fetchAndAddDependencies(
248318
249319pub fn createFilePkg(
250320 gpa: Allocator,
251 name: []const u8,
252321 cache_directory: Compilation.Directory,
253322 basename: []const u8,
254323 contents: []const u8,
......@@ -269,7 +338,7 @@ pub fn createFilePkg(
269338 const o_dir_sub_path = "o" ++ fs.path.sep_str ++ hex_digest;
270339 try renameTmpIntoCache(cache_directory.handle, tmp_dir_sub_path, o_dir_sub_path);
271340
272 return createWithDir(gpa, name, cache_directory, o_dir_sub_path, basename);
341 return createWithDir(gpa, cache_directory, o_dir_sub_path, basename);
273342}
274343
275344const Report = struct {
......@@ -363,9 +432,6 @@ fn fetchAndUnpack(
363432 const owned_src_path = try gpa.dupe(u8, build_zig_basename);
364433 errdefer gpa.free(owned_src_path);
365434
366 const owned_name = try gpa.dupe(u8, fqn);
367 errdefer gpa.free(owned_name);
368
369435 const build_root = try global_cache_directory.join(gpa, &.{pkg_dir_sub_path});
370436 errdefer gpa.free(build_root);
371437
......@@ -380,7 +446,6 @@ fn fetchAndUnpack(
380446 },
381447 .root_src_directory_owned = true,
382448 .root_src_path = owned_src_path,
383 .name = owned_name,
384449 };
385450
386451 return ptr;
......@@ -455,7 +520,7 @@ fn fetchAndUnpack(
455520 std.zig.fmtId(fqn), std.zig.fmtEscapes(build_root),
456521 });
457522
458 return createWithDir(gpa, fqn, global_cache_directory, pkg_dir_sub_path, build_zig_basename);
523 return createWithDir(gpa, global_cache_directory, pkg_dir_sub_path, build_zig_basename);
459524}
460525
461526fn unpackTarball(
src/Sema.zig+3-3
......@@ -5311,7 +5311,6 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr
53115311 }
53125312 const c_import_pkg = Package.create(
53135313 sema.gpa,
5314 "c_import", // TODO: should we make this unique?
53155314 null,
53165315 c_import_res.out_zig_path,
53175316 ) catch |err| switch (err) {
......@@ -11793,8 +11792,9 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
1179311792 return sema.fail(block, operand_src, "import of file outside package path: '{s}'", .{operand});
1179411793 },
1179511794 error.PackageNotFound => {
11796 const cur_pkg = block.getFileScope().pkg;
11797 return sema.fail(block, operand_src, "no package named '{s}' available within package '{s}'", .{ operand, cur_pkg.name });
11795 const name = try block.getFileScope().pkg.getName(sema.gpa, mod.*);
11796 defer sema.gpa.free(name);
11797 return sema.fail(block, operand_src, "no package named '{s}' available within package '{s}'", .{ operand, name });
1179811798 },
1179911799 else => {
1180011800 // TODO: these errors are file system errors; make sure an update() will
src/main.zig+132-76
......@@ -403,8 +403,11 @@ const usage_build_generic =
403403 \\ ReleaseFast Optimizations on, safety off
404404 \\ ReleaseSafe Optimizations on, safety on
405405 \\ ReleaseSmall Optimize for small binary, safety off
406 \\ --pkg-begin [name] [path] Make pkg available to import and push current pkg
407 \\ --pkg-end Pop current pkg
406 \\ --mod [name]:[deps]:[src] Make a module available for dependency under the given name
407 \\ deps: [dep],[dep],...
408 \\ dep: [[import=]name]
409 \\ --deps [dep],[dep],... Set dependency names for the root package
410 \\ dep: [[import=]name]
408411 \\ --main-pkg-path Set the directory of the root package
409412 \\ -fPIC Force-enable Position Independent Code
410413 \\ -fno-PIC Force-disable Position Independent Code
......@@ -858,15 +861,21 @@ fn buildOutputType(
858861 var linker_export_symbol_names = std.ArrayList([]const u8).init(gpa);
859862 defer linker_export_symbol_names.deinit();
860863
861 // This package only exists to clean up the code parsing --pkg-begin and
862 // --pkg-end flags. Use dummy values that are safe for the destroy call.
863 var pkg_tree_root: Package = .{
864 .root_src_directory = .{ .path = null, .handle = fs.cwd() },
865 .root_src_path = &[0]u8{},
866 .name = &[0]u8{},
867 };
868 defer freePkgTree(gpa, &pkg_tree_root, false);
869 var cur_pkg: *Package = &pkg_tree_root;
864 // Contains every module specified via --mod. The dependencies are added
865 // after argument parsing is completed. We use a StringArrayHashMap to make
866 // error output consistent.
867 var modules = std.StringArrayHashMap(struct {
868 mod: *Package,
869 deps_str: []const u8, // still in CLI arg format
870 }).init(gpa);
871 defer {
872 var it = modules.iterator();
873 while (it.next()) |kv| kv.value_ptr.mod.destroy(gpa);
874 modules.deinit();
875 }
876
877 // The dependency string for the root package
878 var root_deps_str: ?[]const u8 = null;
870879
871880 // before arg parsing, check for the NO_COLOR environment variable
872881 // if it exists, default the color setting to .off
......@@ -943,34 +952,44 @@ fn buildOutputType(
943952 } else {
944953 fatal("unexpected end-of-parameter mark: --", .{});
945954 }
946 } else if (mem.eql(u8, arg, "--pkg-begin")) {
947 const opt_pkg_name = args_iter.next();
948 const opt_pkg_path = args_iter.next();
949 if (opt_pkg_name == null or opt_pkg_path == null)
950 fatal("Expected 2 arguments after {s}", .{arg});
951
952 const pkg_name = opt_pkg_name.?;
953 const pkg_path = try introspect.resolvePath(arena, opt_pkg_path.?);
954
955 const new_cur_pkg = Package.create(
956 gpa,
957 pkg_name,
958 fs.path.dirname(pkg_path),
959 fs.path.basename(pkg_path),
960 ) catch |err| {
961 fatal("Failed to add package at path {s}: {s}", .{ pkg_path, @errorName(err) });
962 };
955 } else if (mem.eql(u8, arg, "--mod")) {
956 const info = args_iter.nextOrFatal();
957 var info_it = mem.split(u8, info, ":");
958 const mod_name = info_it.next() orelse fatal("expected non-empty argument after {s}", .{arg});
959 const deps_str = info_it.next() orelse fatal("expected 'name:deps:path' after {s}", .{arg});
960 const root_src_orig = info_it.rest();
961 if (root_src_orig.len == 0) fatal("expected 'name:deps:path' after {s}", .{arg});
962 if (mod_name.len == 0) fatal("empty name for module at '{s}'", .{root_src_orig});
963
964 const root_src = try introspect.resolvePath(arena, root_src_orig);
965
966 for ([_][]const u8{ "std", "root", "builtin" }) |name| {
967 if (mem.eql(u8, mod_name, name)) {
968 fatal("unable to add module '{s}' -> '{s}': conflicts with builtin module", .{ mod_name, root_src });
969 }
970 }
963971
964 if (mem.eql(u8, pkg_name, "std") or mem.eql(u8, pkg_name, "root") or mem.eql(u8, pkg_name, "builtin")) {
965 fatal("unable to add package '{s}' -> '{s}': conflicts with builtin package", .{ pkg_name, pkg_path });
966 } else if (cur_pkg.table.get(pkg_name)) |prev| {
967 fatal("unable to add package '{s}' -> '{s}': already exists as '{s}", .{ pkg_name, pkg_path, prev.root_src_path });
972 var mod_it = modules.iterator();
973 while (mod_it.next()) |kv| {
974 if (std.mem.eql(u8, mod_name, kv.key_ptr.*)) {
975 fatal("unable to add module '{s}' -> '{s}': already exists as '{s}'", .{ mod_name, root_src, kv.value_ptr.mod.root_src_path });
976 }
977 }
978
979 try modules.ensureUnusedCapacity(1);
980 modules.put(mod_name, .{
981 .mod = try Package.create(
982 gpa,
983 fs.path.dirname(root_src),
984 fs.path.basename(root_src),
985 ),
986 .deps_str = deps_str,
987 }) catch unreachable;
988 } else if (mem.eql(u8, arg, "--deps")) {
989 if (root_deps_str != null) {
990 fatal("only one --deps argument is allowed", .{});
968991 }
969 try cur_pkg.addAndAdopt(gpa, new_cur_pkg);
970 cur_pkg = new_cur_pkg;
971 } else if (mem.eql(u8, arg, "--pkg-end")) {
972 cur_pkg = cur_pkg.parent orelse
973 fatal("encountered --pkg-end with no matching --pkg-begin", .{});
992 root_deps_str = args_iter.nextOrFatal();
974993 } else if (mem.eql(u8, arg, "--main-pkg-path")) {
975994 main_pkg_path = args_iter.nextOrFatal();
976995 } else if (mem.eql(u8, arg, "-cflags")) {
......@@ -2307,6 +2326,31 @@ fn buildOutputType(
23072326 },
23082327 }
23092328
2329 {
2330 // Resolve module dependencies
2331 var it = modules.iterator();
2332 while (it.next()) |kv| {
2333 const deps_str = kv.value_ptr.deps_str;
2334 var deps_it = ModuleDepIterator.init(deps_str);
2335 while (deps_it.next()) |dep| {
2336 if (dep.expose.len == 0) {
2337 fatal("module '{s}' depends on '{s}' with a blank name", .{ kv.key_ptr.*, dep.name });
2338 }
2339
2340 for ([_][]const u8{ "std", "root", "builtin" }) |name| {
2341 if (mem.eql(u8, dep.expose, name)) {
2342 fatal("unable to add module '{s}' under name '{s}': conflicts with builtin module", .{ dep.name, dep.expose });
2343 }
2344 }
2345
2346 const dep_mod = modules.get(dep.name) orelse
2347 fatal("module '{s}' depends on module '{s}' which does not exist", .{ kv.key_ptr.*, dep.name });
2348
2349 try kv.value_ptr.mod.add(gpa, dep.expose, dep_mod.mod);
2350 }
2351 }
2352 }
2353
23102354 if (arg_mode == .build and optimize_mode == .ReleaseSmall and strip == null)
23112355 strip = true;
23122356
......@@ -2886,14 +2930,14 @@ fn buildOutputType(
28862930 if (main_pkg_path) |unresolved_main_pkg_path| {
28872931 const p = try introspect.resolvePath(arena, unresolved_main_pkg_path);
28882932 if (p.len == 0) {
2889 break :blk try Package.create(gpa, "root", null, src_path);
2933 break :blk try Package.create(gpa, null, src_path);
28902934 } else {
28912935 const rel_src_path = try fs.path.relative(arena, p, src_path);
2892 break :blk try Package.create(gpa, "root", p, rel_src_path);
2936 break :blk try Package.create(gpa, p, rel_src_path);
28932937 }
28942938 } else {
28952939 const root_src_dir_path = fs.path.dirname(src_path);
2896 break :blk Package.create(gpa, "root", root_src_dir_path, fs.path.basename(src_path)) catch |err| {
2940 break :blk Package.create(gpa, root_src_dir_path, fs.path.basename(src_path)) catch |err| {
28972941 if (root_src_dir_path) |p| {
28982942 fatal("unable to open '{s}': {s}", .{ p, @errorName(err) });
28992943 } else {
......@@ -2904,23 +2948,24 @@ fn buildOutputType(
29042948 } else null;
29052949 defer if (main_pkg) |p| p.destroy(gpa);
29062950
2907 // Transfer packages added with --pkg-begin/--pkg-end to the root package
2908 if (main_pkg) |pkg| {
2909 var it = pkg_tree_root.table.valueIterator();
2910 while (it.next()) |p| {
2911 if (p.*.parent == &pkg_tree_root) {
2912 p.*.parent = pkg;
2951 // Transfer packages added with --deps to the root package
2952 if (main_pkg) |mod| {
2953 var it = ModuleDepIterator.init(root_deps_str orelse "");
2954 while (it.next()) |dep| {
2955 if (dep.expose.len == 0) {
2956 fatal("root module depends on '{s}' with a blank name", .{dep.name});
29132957 }
2914 }
2915 pkg.table = pkg_tree_root.table;
2916 pkg_tree_root.table = .{};
2917 } else {
2918 // Remove any dangling pointers just in case.
2919 var it = pkg_tree_root.table.valueIterator();
2920 while (it.next()) |p| {
2921 if (p.*.parent == &pkg_tree_root) {
2922 p.*.parent = null;
2958
2959 for ([_][]const u8{ "std", "root", "builtin" }) |name| {
2960 if (mem.eql(u8, dep.expose, name)) {
2961 fatal("unable to add module '{s}' under name '{s}': conflicts with builtin module", .{ dep.name, dep.expose });
2962 }
29232963 }
2964
2965 const dep_mod = modules.get(dep.name) orelse
2966 fatal("root module depends on module '{s}' which does not exist", .{dep.name});
2967
2968 try mod.add(gpa, dep.expose, dep_mod.mod);
29242969 }
29252970 }
29262971
......@@ -3400,6 +3445,32 @@ fn buildOutputType(
34003445 return cleanExit();
34013446}
34023447
3448const ModuleDepIterator = struct {
3449 split: mem.SplitIterator(u8),
3450
3451 fn init(deps_str: []const u8) ModuleDepIterator {
3452 return .{ .split = mem.split(u8, deps_str, ",") };
3453 }
3454
3455 const Dependency = struct {
3456 expose: []const u8,
3457 name: []const u8,
3458 };
3459
3460 fn next(it: *ModuleDepIterator) ?Dependency {
3461 if (it.split.buffer.len == 0) return null; // don't return "" for the first iteration on ""
3462 const str = it.split.next() orelse return null;
3463 if (mem.indexOfScalar(u8, str, '=')) |i| {
3464 return .{
3465 .expose = str[0..i],
3466 .name = str[i + 1 ..],
3467 };
3468 } else {
3469 return .{ .expose = str, .name = str };
3470 }
3471 }
3472};
3473
34033474fn parseCrossTargetOrReportFatalError(
34043475 allocator: Allocator,
34053476 opts: std.zig.CrossTarget.ParseOptions,
......@@ -3626,18 +3697,6 @@ fn updateModule(gpa: Allocator, comp: *Compilation, hook: AfterUpdateHook) !void
36263697 }
36273698}
36283699
3629fn freePkgTree(gpa: Allocator, pkg: *Package, free_parent: bool) void {
3630 {
3631 var it = pkg.table.valueIterator();
3632 while (it.next()) |value| {
3633 freePkgTree(gpa, value.*, true);
3634 }
3635 }
3636 if (free_parent) {
3637 pkg.destroy(gpa);
3638 }
3639}
3640
36413700fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void {
36423701 if (!build_options.have_llvm)
36433702 fatal("cannot translate-c: compiler built without LLVM extensions", .{});
......@@ -4141,7 +4200,6 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
41414200 var main_pkg: Package = .{
41424201 .root_src_directory = zig_lib_directory,
41434202 .root_src_path = "build_runner.zig",
4144 .name = "root",
41454203 };
41464204
41474205 if (!build_options.omit_pkg_fetching_code) {
......@@ -4184,22 +4242,20 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
41844242
41854243 const deps_pkg = try Package.createFilePkg(
41864244 gpa,
4187 "@dependencies",
41884245 local_cache_directory,
41894246 "dependencies.zig",
41904247 dependencies_source.items,
41914248 );
41924249
41934250 mem.swap(Package.Table, &main_pkg.table, &deps_pkg.table);
4194 try main_pkg.addAndAdopt(gpa, deps_pkg);
4251 try main_pkg.add(gpa, "@dependencies", deps_pkg);
41954252 }
41964253
41974254 var build_pkg: Package = .{
41984255 .root_src_directory = build_directory,
41994256 .root_src_path = build_zig_basename,
4200 .name = "@build",
42014257 };
4202 try main_pkg.addAndAdopt(gpa, &build_pkg);
4258 try main_pkg.add(gpa, "@build", &build_pkg);
42034259
42044260 const comp = Compilation.create(gpa, .{
42054261 .zig_lib_directory = zig_lib_directory,
......@@ -4434,7 +4490,7 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void
44344490 .root_decl = .none,
44354491 };
44364492
4437 file.pkg = try Package.create(gpa, "root", null, file.sub_file_path);
4493 file.pkg = try Package.create(gpa, null, file.sub_file_path);
44384494 defer file.pkg.destroy(gpa);
44394495
44404496 file.zir = try AstGen.generate(gpa, file.tree);
......@@ -4645,7 +4701,7 @@ fn fmtPathFile(
46454701 .root_decl = .none,
46464702 };
46474703
4648 file.pkg = try Package.create(fmt.gpa, "root", null, file.sub_file_path);
4704 file.pkg = try Package.create(fmt.gpa, null, file.sub_file_path);
46494705 defer file.pkg.destroy(fmt.gpa);
46504706
46514707 if (stat.size > max_src_size)
......@@ -5357,7 +5413,7 @@ pub fn cmdAstCheck(
53575413 file.stat.size = source.len;
53585414 }
53595415
5360 file.pkg = try Package.create(gpa, "root", null, file.sub_file_path);
5416 file.pkg = try Package.create(gpa, null, file.sub_file_path);
53615417 defer file.pkg.destroy(gpa);
53625418
53635419 file.tree = try Ast.parse(gpa, file.source, .zig);
......@@ -5476,7 +5532,7 @@ pub fn cmdChangelist(
54765532 .root_decl = .none,
54775533 };
54785534
5479 file.pkg = try Package.create(gpa, "root", null, file.sub_file_path);
5535 file.pkg = try Package.create(gpa, null, file.sub_file_path);
54805536 defer file.pkg.destroy(gpa);
54815537
54825538 const source = try arena.allocSentinel(u8, @intCast(usize, stat.size), 0);
src/test.zig+38-2
......@@ -583,6 +583,11 @@ pub const TestContext = struct {
583583 path: []const u8,
584584 };
585585
586 pub const DepModule = struct {
587 name: []const u8,
588 path: []const u8,
589 };
590
586591 pub const Backend = enum {
587592 stage1,
588593 stage2,
......@@ -611,6 +616,7 @@ pub const TestContext = struct {
611616 link_libc: bool = false,
612617
613618 files: std.ArrayList(File),
619 deps: std.ArrayList(DepModule),
614620
615621 result: anyerror!void = {},
616622
......@@ -618,6 +624,13 @@ pub const TestContext = struct {
618624 case.files.append(.{ .path = name, .src = src }) catch @panic("out of memory");
619625 }
620626
627 pub fn addDepModule(case: *Case, name: []const u8, path: []const u8) void {
628 case.deps.append(.{
629 .name = name,
630 .path = path,
631 }) catch @panic("out of memory");
632 }
633
621634 /// Adds a subcase in which the module is updated with `src`, and a C
622635 /// header is generated.
623636 pub fn addHeader(self: *Case, src: [:0]const u8, result: [:0]const u8) void {
......@@ -767,6 +780,7 @@ pub const TestContext = struct {
767780 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
768781 .output_mode = .Exe,
769782 .files = std.ArrayList(File).init(ctx.arena),
783 .deps = std.ArrayList(DepModule).init(ctx.arena),
770784 }) catch @panic("out of memory");
771785 return &ctx.cases.items[ctx.cases.items.len - 1];
772786 }
......@@ -787,6 +801,7 @@ pub const TestContext = struct {
787801 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
788802 .output_mode = .Exe,
789803 .files = std.ArrayList(File).init(ctx.arena),
804 .deps = std.ArrayList(DepModule).init(ctx.arena),
790805 .link_libc = true,
791806 }) catch @panic("out of memory");
792807 return &ctx.cases.items[ctx.cases.items.len - 1];
......@@ -801,6 +816,7 @@ pub const TestContext = struct {
801816 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
802817 .output_mode = .Exe,
803818 .files = std.ArrayList(File).init(ctx.arena),
819 .deps = std.ArrayList(DepModule).init(ctx.arena),
804820 .backend = .llvm,
805821 .link_libc = true,
806822 }) catch @panic("out of memory");
......@@ -818,6 +834,7 @@ pub const TestContext = struct {
818834 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
819835 .output_mode = .Obj,
820836 .files = std.ArrayList(File).init(ctx.arena),
837 .deps = std.ArrayList(DepModule).init(ctx.arena),
821838 }) catch @panic("out of memory");
822839 return &ctx.cases.items[ctx.cases.items.len - 1];
823840 }
......@@ -834,6 +851,7 @@ pub const TestContext = struct {
834851 .output_mode = .Exe,
835852 .is_test = true,
836853 .files = std.ArrayList(File).init(ctx.arena),
854 .deps = std.ArrayList(DepModule).init(ctx.arena),
837855 }) catch @panic("out of memory");
838856 return &ctx.cases.items[ctx.cases.items.len - 1];
839857 }
......@@ -858,6 +876,7 @@ pub const TestContext = struct {
858876 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
859877 .output_mode = .Obj,
860878 .files = std.ArrayList(File).init(ctx.arena),
879 .deps = std.ArrayList(DepModule).init(ctx.arena),
861880 }) catch @panic("out of memory");
862881 return &ctx.cases.items[ctx.cases.items.len - 1];
863882 }
......@@ -1145,6 +1164,7 @@ pub const TestContext = struct {
11451164 .output_mode = output_mode,
11461165 .link_libc = backend == .llvm,
11471166 .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator),
1167 .deps = std.ArrayList(DepModule).init(ctx.cases.allocator),
11481168 });
11491169 try cases.append(next);
11501170 }
......@@ -1497,9 +1517,25 @@ pub const TestContext = struct {
14971517 var main_pkg: Package = .{
14981518 .root_src_directory = .{ .path = tmp_dir_path, .handle = tmp.dir },
14991519 .root_src_path = tmp_src_path,
1500 .name = "root",
15011520 };
1502 defer main_pkg.table.deinit(allocator);
1521 defer {
1522 var it = main_pkg.table.iterator();
1523 while (it.next()) |kv| {
1524 allocator.free(kv.key_ptr.*);
1525 kv.value_ptr.*.destroy(allocator);
1526 }
1527 main_pkg.table.deinit(allocator);
1528 }
1529
1530 for (case.deps.items) |dep| {
1531 var pkg = try Package.create(
1532 allocator,
1533 tmp_dir_path,
1534 dep.path,
1535 );
1536 errdefer pkg.destroy(allocator);
1537 try main_pkg.add(allocator, dep.name, pkg);
1538 }
15031539
15041540 const bin_name = try std.zig.binNameAlloc(arena, .{
15051541 .root_name = "test_case",
stage1/zig1.wasm
Binary files a/stage1/zig1.wasm and b/stage1/zig1.wasm differ
test/compile_errors.zig+22
......@@ -288,4 +288,26 @@ pub fn addCases(ctx: *TestContext) !void {
288288 //, &[_][]const u8{
289289 // "tmp.zig:4:1: error: unable to inline function",
290290 //});
291
292 {
293 const case = ctx.obj("file in multiple modules", .{});
294 case.backend = .stage2;
295
296 case.addSourceFile("foo.zig",
297 \\const dummy = 0;
298 );
299
300 case.addDepModule("foo", "foo.zig");
301
302 case.addError(
303 \\comptime {
304 \\ _ = @import("foo");
305 \\ _ = @import("foo.zig");
306 \\}
307 , &[_][]const u8{
308 ":1:1: error: file exists in multiple modules",
309 ":1:1: note: root of module root.foo",
310 ":3:17: note: imported from module root",
311 });
312 }
291313}
test/stage2/nvptx.zig+1
......@@ -97,6 +97,7 @@ pub fn addPtx(
9797 .updates = std.ArrayList(TestContext.Update).init(ctx.cases.allocator),
9898 .output_mode = .Obj,
9999 .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator),
100 .deps = std.ArrayList(TestContext.DepModule).init(ctx.cases.allocator),
100101 .link_libc = false,
101102 .backend = .llvm,
102103 // Bug in Debug mode
test/standalone.zig+6
......@@ -107,4 +107,10 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
107107 cases.addBuildFile("test/standalone/emit_asm_and_bin/build.zig", .{});
108108 cases.addBuildFile("test/standalone/issue_12588/build.zig", .{});
109109 cases.addBuildFile("test/standalone/embed_generated_file/build.zig", .{});
110
111 cases.addBuildFile("test/standalone/dep_diamond/build.zig", .{});
112 cases.addBuildFile("test/standalone/dep_triangle/build.zig", .{});
113 cases.addBuildFile("test/standalone/dep_recursive/build.zig", .{});
114 cases.addBuildFile("test/standalone/dep_mutually_recursive/build.zig", .{});
115 cases.addBuildFile("test/standalone/dep_shared_builtin/build.zig", .{});
110116}
test/standalone/dep_diamond/bar.zig created+1
......@@ -0,0 +1 @@
1pub const shared = @import("shared");
test/standalone/dep_diamond/build.zig created+28
......@@ -0,0 +1,28 @@
1const std = @import("std");
2
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
5
6 const shared = b.createModule(.{
7 .source_file = .{ .path = "shared.zig" },
8 });
9
10 const exe = b.addExecutable(.{
11 .name = "test",
12 .root_source_file = .{ .path = "test.zig" },
13 .optimize = optimize,
14 });
15 exe.addAnonymousModule("foo", .{
16 .source_file = .{ .path = "foo.zig" },
17 .dependencies = &.{.{ .name = "shared", .module = shared }},
18 });
19 exe.addAnonymousModule("bar", .{
20 .source_file = .{ .path = "bar.zig" },
21 .dependencies = &.{.{ .name = "shared", .module = shared }},
22 });
23
24 const run = exe.run();
25
26 const test_step = b.step("test", "Test it");
27 test_step.dependOn(&run.step);
28}
test/standalone/dep_diamond/foo.zig created+1
......@@ -0,0 +1 @@
1pub const shared = @import("shared");
test/standalone/dep_diamond/shared.zig created+1
......@@ -0,0 +1 @@
1// (empty)
test/standalone/dep_diamond/test.zig created+7
......@@ -0,0 +1,7 @@
1const foo = @import("foo");
2const bar = @import("bar");
3const assert = @import("std").debug.assert;
4
5pub fn main() void {
6 assert(foo.shared == bar.shared);
7}
test/standalone/dep_mutually_recursive/bar.zig created+6
......@@ -0,0 +1,6 @@
1const assert = @import("std").debug.assert;
2pub const foo = @import("foo");
3
4comptime {
5 assert(foo.bar == @This());
6}
test/standalone/dep_mutually_recursive/build.zig created+26
......@@ -0,0 +1,26 @@
1const std = @import("std");
2
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
5
6 const foo = b.createModule(.{
7 .source_file = .{ .path = "foo.zig" },
8 });
9 const bar = b.createModule(.{
10 .source_file = .{ .path = "bar.zig" },
11 });
12 foo.dependencies.put("bar", bar) catch @panic("OOM");
13 bar.dependencies.put("foo", foo) catch @panic("OOM");
14
15 const exe = b.addExecutable(.{
16 .name = "test",
17 .root_source_file = .{ .path = "test.zig" },
18 .optimize = optimize,
19 });
20 exe.addModule("foo", foo);
21
22 const run = exe.run();
23
24 const test_step = b.step("test", "Test it");
25 test_step.dependOn(&run.step);
26}
test/standalone/dep_mutually_recursive/foo.zig created+6
......@@ -0,0 +1,6 @@
1const assert = @import("std").debug.assert;
2pub const bar = @import("bar");
3
4comptime {
5 assert(bar.foo == @This());
6}
test/standalone/dep_mutually_recursive/test.zig created+7
......@@ -0,0 +1,7 @@
1const foo = @import("foo");
2const assert = @import("std").debug.assert;
3
4pub fn main() void {
5 assert(foo == foo.bar.foo);
6 assert(foo == foo.bar.foo.bar.foo);
7}
test/standalone/dep_recursive/build.zig created+22
......@@ -0,0 +1,22 @@
1const std = @import("std");
2
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
5
6 const foo = b.createModule(.{
7 .source_file = .{ .path = "foo.zig" },
8 });
9 foo.dependencies.put("foo", foo) catch @panic("OOM");
10
11 const exe = b.addExecutable(.{
12 .name = "test",
13 .root_source_file = .{ .path = "test.zig" },
14 .optimize = optimize,
15 });
16 exe.addModule("foo", foo);
17
18 const run = exe.run();
19
20 const test_step = b.step("test", "Test it");
21 test_step.dependOn(&run.step);
22}
test/standalone/dep_recursive/foo.zig created+6
......@@ -0,0 +1,6 @@
1const assert = @import("std").debug.assert;
2pub const foo = @import("foo");
3
4comptime {
5 assert(foo == @This());
6}
test/standalone/dep_recursive/test.zig created+8
......@@ -0,0 +1,8 @@
1const foo = @import("foo");
2const shared = @import("shared");
3const assert = @import("std").debug.assert;
4
5pub fn main() void {
6 assert(foo == foo.foo);
7 assert(foo == foo.foo.foo);
8}
test/standalone/dep_shared_builtin/build.zig created+19
......@@ -0,0 +1,19 @@
1const std = @import("std");
2
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
5
6 const exe = b.addExecutable(.{
7 .name = "test",
8 .root_source_file = .{ .path = "test.zig" },
9 .optimize = optimize,
10 });
11 exe.addAnonymousModule("foo", .{
12 .source_file = .{ .path = "foo.zig" },
13 });
14
15 const run = exe.run();
16
17 const test_step = b.step("test", "Test it");
18 test_step.dependOn(&run.step);
19}
test/standalone/dep_shared_builtin/foo.zig created+3
......@@ -0,0 +1,3 @@
1pub const std = @import("std");
2pub const builtin = @import("builtin");
3pub const root = @import("root");
test/standalone/dep_shared_builtin/test.zig created+11
......@@ -0,0 +1,11 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const root = @import("root");
4const foo = @import("foo");
5
6pub fn main() void {
7 std.debug.assert(root == @This());
8 std.debug.assert(std == foo.std);
9 std.debug.assert(builtin == foo.builtin);
10 std.debug.assert(root == foo.root);
11}
test/standalone/dep_triangle/build.zig created+25
......@@ -0,0 +1,25 @@
1const std = @import("std");
2
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
5
6 const shared = b.createModule(.{
7 .source_file = .{ .path = "shared.zig" },
8 });
9
10 const exe = b.addExecutable(.{
11 .name = "test",
12 .root_source_file = .{ .path = "test.zig" },
13 .optimize = optimize,
14 });
15 exe.addAnonymousModule("foo", .{
16 .source_file = .{ .path = "foo.zig" },
17 .dependencies = &.{.{ .name = "shared", .module = shared }},
18 });
19 exe.addModule("shared", shared);
20
21 const run = exe.run();
22
23 const test_step = b.step("test", "Test it");
24 test_step.dependOn(&run.step);
25}
test/standalone/dep_triangle/foo.zig created+1
......@@ -0,0 +1 @@
1pub const shared = @import("shared");
test/standalone/dep_triangle/shared.zig created+1
......@@ -0,0 +1 @@
1// (empty)
test/standalone/dep_triangle/test.zig created+7
......@@ -0,0 +1,7 @@
1const foo = @import("foo");
2const shared = @import("shared");
3const assert = @import("std").debug.assert;
4
5pub fn main() void {
6 assert(foo.shared == shared);
7}