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...@@ -748,7 +748,8 @@ set(BUILD_ZIG2_ARGS
748 build-exe src/main.zig -ofmt=c -lc748 build-exe src/main.zig -ofmt=c -lc
749 -OReleaseSmall749 -OReleaseSmall
750 --name zig2 -femit-bin="${ZIG2_C_SOURCE}"750 --name zig2 -femit-bin="${ZIG2_C_SOURCE}"
751 --pkg-begin build_options "${ZIG_CONFIG_ZIG_OUT}" --pkg-end751 --mod "build_options::${ZIG_CONFIG_ZIG_OUT}"
752 --deps build_options
752 -target "${HOST_TARGET_TRIPLE}"753 -target "${HOST_TARGET_TRIPLE}"
753)754)
754 755
...@@ -765,7 +766,8 @@ set(BUILD_COMPILER_RT_ARGS...@@ -765,7 +766,8 @@ set(BUILD_COMPILER_RT_ARGS
765 build-obj lib/compiler_rt.zig -ofmt=c766 build-obj lib/compiler_rt.zig -ofmt=c
766 -OReleaseSmall767 -OReleaseSmall
767 --name compiler_rt -femit-bin="${ZIG_COMPILER_RT_C_SOURCE}"768 --name compiler_rt -femit-bin="${ZIG_COMPILER_RT_C_SOURCE}"
768 --pkg-begin build_options "${ZIG_CONFIG_ZIG_OUT}" --pkg-end769 --mod "build_options::${ZIG_CONFIG_ZIG_OUT}"
770 --deps build_options
769 -target "${HOST_TARGET_TRIPLE}"771 -target "${HOST_TARGET_TRIPLE}"
770)772)
771 773
ci/x86_64-windows-debug.ps1+2-1
...@@ -87,7 +87,8 @@ CheckLastExitCode...@@ -87,7 +87,8 @@ CheckLastExitCode
87 -OReleaseSmall `87 -OReleaseSmall `
88 --name compiler_rt `88 --name compiler_rt `
89 -femit-bin="compiler_rt-x86_64-windows-msvc.c" `89 -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 `
91 -target x86_64-windows-msvc92 -target x86_64-windows-msvc
92CheckLastExitCode93CheckLastExitCode
9394
ci/x86_64-windows-release.ps1+2-1
...@@ -87,7 +87,8 @@ CheckLastExitCode...@@ -87,7 +87,8 @@ CheckLastExitCode
87 -OReleaseSmall `87 -OReleaseSmall `
88 --name compiler_rt `88 --name compiler_rt `
89 -femit-bin="compiler_rt-x86_64-windows-msvc.c" `89 -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 `
91 -target x86_64-windows-msvc92 -target x86_64-windows-msvc
92CheckLastExitCode93CheckLastExitCode
9394
lib/std/Build/CompileStep.zig+107-20
...@@ -955,7 +955,10 @@ pub fn addFrameworkPath(self: *CompileStep, dir_path: []const u8) void {...@@ -955,7 +955,10 @@ pub fn addFrameworkPath(self: *CompileStep, dir_path: []const u8) void {
955/// package's module table using `name`.955/// package's module table using `name`.
956pub fn addModule(cs: *CompileStep, name: []const u8, module: *Module) void {956pub fn addModule(cs: *CompileStep, name: []const u8, module: *Module) void {
957 cs.modules.put(cs.builder.dupe(name), module) catch @panic("OOM");957 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");
959}962}
960963
961/// Adds a module to be used with `@import` without exposing it in the current964/// 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...@@ -969,10 +972,12 @@ pub fn addOptions(cs: *CompileStep, module_name: []const u8, options: *OptionsSt
969 addModule(cs, module_name, options.createModule());972 addModule(cs, module_name, options.createModule());
970}973}
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, {});
973 module.source_file.addStepDependencies(&cs.step);978 module.source_file.addStepDependencies(&cs.step);
974 for (module.dependencies.values()) |dep| {979 for (module.dependencies.values()) |dep| {
975 cs.addRecursiveBuildDeps(dep);980 try cs.addRecursiveBuildDeps(dep, done);
976 }981 }
977}982}
978983
...@@ -1031,22 +1036,110 @@ fn linkLibraryOrObject(self: *CompileStep, other: *CompileStep) void {...@@ -1031,22 +1036,110 @@ fn linkLibraryOrObject(self: *CompileStep, other: *CompileStep) void {
1031fn appendModuleArgs(1036fn appendModuleArgs(
1032 cs: *CompileStep,1037 cs: *CompileStep,
1033 zig_args: *ArrayList([]const u8),1038 zig_args: *ArrayList([]const u8),
1034 name: []const u8,
1035 module: *Module,
1036) error{OutOfMemory}!void {1039) error{OutOfMemory}!void {
1037 try zig_args.append("--pkg-begin");1040 // First, traverse the whole dependency graph and give every module a unique name, ideally one
1038 try zig_args.append(name);1041 // named after what it's called somewhere in the graph. It will help here to have both a mapping
1039 try zig_args.append(module.builder.pathFromRoot(module.source_file.getPath(module.builder)));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
1041 {1101 {
1042 const keys = module.dependencies.keys();1102 var it = mod_names.iterator();
1043 for (module.dependencies.values(), 0..) |sub_module, i| {1103 while (it.next()) |kv| {
1044 const sub_name = keys[i];1104 const mod = kv.key_ptr.*;
1045 try cs.appendModuleArgs(zig_args, sub_name, sub_module);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 }));
1046 }1111 }
1047 }1112 }
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 }
1050}1143}
10511144
1052fn make(step: *Step) !void {1145fn make(step: *Step) !void {
...@@ -1573,13 +1666,7 @@ fn make(step: *Step) !void {...@@ -1573,13 +1666,7 @@ fn make(step: *Step) !void {
1573 try zig_args.append("--test-no-exec");1666 try zig_args.append("--test-no-exec");
1574 }1667 }
15751668
1576 {1669 try self.appendModuleArgs(&zig_args);
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 }
15831670
1584 for (self.include_dirs.items) |include_dir| {1671 for (self.include_dirs.items) |include_dir| {
1585 switch (include_dir) {1672 switch (include_dir) {
src/Autodoc.zig+1-9
...@@ -860,17 +860,9 @@ fn walkInstruction(...@@ -860,17 +860,9 @@ fn walkInstruction(
860 const str_tok = data[inst_index].str_tok;860 const str_tok = data[inst_index].str_tok;
861 var path = str_tok.get(file.zir);861 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 };
871 // importFile cannot error out since all files863 // importFile cannot error out since all files
872 // are already loaded at this point864 // are already loaded at this point
873 if (maybe_other_package) |other_package| {865 if (file.pkg.table.get(path)) |other_package| {
874 const result = try self.packages.getOrPut(self.arena, other_package);866 const result = try self.packages.getOrPut(self.arena, other_package);
875867
876 // Immediately add this package to the import table of our868 // 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 {...@@ -1596,36 +1596,53 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
15961596
1597 const builtin_pkg = try Package.createWithDir(1597 const builtin_pkg = try Package.createWithDir(
1598 gpa,1598 gpa,
1599 "builtin",
1600 zig_cache_artifact_directory,1599 zig_cache_artifact_directory,
1601 null,1600 null,
1602 "builtin.zig",1601 "builtin.zig",
1603 );1602 );
1604 errdefer builtin_pkg.destroy(gpa);1603 errdefer builtin_pkg.destroy(gpa);
16051604
1606 const std_pkg = try Package.createWithDir(1605 // When you're testing std, the main module is std. In that case, we'll just set the std
1607 gpa,1606 // module to the main one, since avoiding the errors caused by duplicating it is more
1608 "std",1607 // effort than it's worth.
1609 options.zig_lib_directory,1608 const main_pkg_is_std = m: {
1610 "std",1609 const std_path = try std.fs.path.resolve(arena, &[_][]const u8{
1611 "std.zig",1610 options.zig_lib_directory.path orelse ".",
1612 );1611 "std",
1613 errdefer std_pkg.destroy(gpa);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
1615 const root_pkg = if (options.is_test) root_pkg: {1635 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
1618 const test_pkg = if (options.test_runner_path) |test_runner| test_pkg: {1636 const test_pkg = if (options.test_runner_path) |test_runner| test_pkg: {
1619 const test_dir = std.fs.path.dirname(test_runner);1637 const test_dir = std.fs.path.dirname(test_runner);
1620 const basename = std.fs.path.basename(test_runner);1638 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
1623 // copy package table from main_pkg to root_pkg1641 // copy package table from main_pkg to root_pkg
1624 pkg.table = try main_pkg.table.clone(gpa);1642 pkg.table = try main_pkg.table.clone(gpa);
1625 break :test_pkg pkg;1643 break :test_pkg pkg;
1626 } else try Package.createWithDir(1644 } else try Package.createWithDir(
1627 gpa,1645 gpa,
1628 "root",
1629 options.zig_lib_directory,1646 options.zig_lib_directory,
1630 null,1647 null,
1631 "test_runner.zig",1648 "test_runner.zig",
...@@ -1639,7 +1656,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1639,7 +1656,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1639 const compiler_rt_pkg = if (include_compiler_rt and options.output_mode == .Obj) compiler_rt_pkg: {1656 const compiler_rt_pkg = if (include_compiler_rt and options.output_mode == .Obj) compiler_rt_pkg: {
1640 break :compiler_rt_pkg try Package.createWithDir(1657 break :compiler_rt_pkg try Package.createWithDir(
1641 gpa,1658 gpa,
1642 "compiler_rt",
1643 options.zig_lib_directory,1659 options.zig_lib_directory,
1644 null,1660 null,
1645 "compiler_rt.zig",1661 "compiler_rt.zig",
...@@ -1647,28 +1663,14 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1647,28 +1663,14 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1647 } else null;1663 } else null;
1648 errdefer if (compiler_rt_pkg) |p| p.destroy(gpa);1664 errdefer if (compiler_rt_pkg) |p| p.destroy(gpa);
16491665
1650 try main_pkg.addAndAdopt(gpa, builtin_pkg);1666 try main_pkg.add(gpa, "builtin", builtin_pkg);
1651 try main_pkg.add(gpa, root_pkg);1667 try main_pkg.add(gpa, "root", root_pkg);
1652 try main_pkg.addAndAdopt(gpa, std_pkg);1668 try main_pkg.add(gpa, "std", std_pkg);
16531669
1654 if (compiler_rt_pkg) |p| {1670 if (compiler_rt_pkg) |p| {
1655 try main_pkg.addAndAdopt(gpa, p);1671 try main_pkg.add(gpa, "compiler_rt", p);
1656 }1672 }
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
1672 // Pre-open the directory handles for cached ZIR code so that it does not need1674 // Pre-open the directory handles for cached ZIR code so that it does not need
1673 // to redundantly happen for each AstGen operation.1675 // to redundantly happen for each AstGen operation.
1674 const zir_sub_dir = "z";1676 const zir_sub_dir = "z";
...@@ -1705,7 +1707,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1705,7 +1707,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1705 .gpa = gpa,1707 .gpa = gpa,
1706 .comp = comp,1708 .comp = comp,
1707 .main_pkg = main_pkg,1709 .main_pkg = main_pkg,
1708 .main_pkg_is_std = main_pkg_is_std,
1709 .root_pkg = root_pkg,1710 .root_pkg = root_pkg,
1710 .zig_cache_artifact_directory = zig_cache_artifact_directory,1711 .zig_cache_artifact_directory = zig_cache_artifact_directory,
1711 .global_zir_cache = global_zir_cache,1712 .global_zir_cache = global_zir_cache,
...@@ -2772,6 +2773,111 @@ fn emitOthers(comp: *Compilation) void {...@@ -2772,6 +2773,111 @@ fn emitOthers(comp: *Compilation) void {
2772 }2773 }
2773}2774}
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
2775/// Having the file open for writing is problematic as far as executing the2881/// Having the file open for writing is problematic as far as executing the
2776/// binary is concerned. This will remove the write flag, or close the file,2882/// binary is concerned. This will remove the write flag, or close the file,
2777/// or whatever is needed so that it can be executed.2883/// or whatever is needed so that it can be executed.
...@@ -3098,54 +3204,7 @@ pub fn performAllTheWork(...@@ -3098,54 +3204,7 @@ pub fn performAllTheWork(
3098 }3204 }
30993205
3100 if (comp.bin_file.options.module) |mod| {3206 if (comp.bin_file.options.module) |mod| {
3101 for (mod.import_table.values()) |file| {3207 try reportMultiModuleErrors(mod);
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 }
3149 }3208 }
31503209
3151 {3210 {
...@@ -5408,7 +5467,6 @@ fn buildOutputFromZig(...@@ -5408,7 +5467,6 @@ fn buildOutputFromZig(
5408 var main_pkg: Package = .{5467 var main_pkg: Package = .{
5409 .root_src_directory = comp.zig_lib_directory,5468 .root_src_directory = comp.zig_lib_directory,
5410 .root_src_path = src_basename,5469 .root_src_path = src_basename,
5411 .name = "root",
5412 };5470 };
5413 defer main_pkg.deinitTable(comp.gpa);5471 defer main_pkg.deinitTable(comp.gpa);
5414 const root_name = src_basename[0 .. src_basename.len - std.fs.path.extension(src_basename).len];5472 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 {...@@ -144,10 +144,6 @@ stage1_flags: packed struct {
144} = .{},144} = .{},
145145
146job_queued_update_builtin_zig: bool = true,146job_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
152compile_log_text: ArrayListUnmanaged(u8) = .{},148compile_log_text: ArrayListUnmanaged(u8) = .{},
153149
...@@ -1950,7 +1946,7 @@ pub const File = struct {...@@ -1950,7 +1946,7 @@ pub const File = struct {
1950 prev_zir: ?*Zir = null,1946 prev_zir: ?*Zir = null,
19511947
1952 /// A single reference to a file.1948 /// A single reference to a file.
1953 const Reference = union(enum) {1949 pub const Reference = union(enum) {
1954 /// The file is imported directly (i.e. not as a package) with @import.1950 /// The file is imported directly (i.e. not as a package) with @import.
1955 import: SrcLoc,1951 import: SrcLoc,
1956 /// The file is the root of a package.1952 /// The file is the root of a package.
...@@ -2113,7 +2109,27 @@ pub const File = struct {...@@ -2113,7 +2109,27 @@ pub const File = struct {
21132109
2114 /// Add a reference to this file during AstGen.2110 /// Add a reference to this file during AstGen.
2115 pub fn addReference(file: *File, mod: Module, ref: Reference) !void {2111 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
2118 const pkg = switch (ref) {2134 const pkg = switch (ref) {
2119 .import => |loc| loc.file_scope.pkg,2135 .import => |loc| loc.file_scope.pkg,
...@@ -2128,7 +2144,10 @@ pub const File = struct {...@@ -2128,7 +2144,10 @@ pub const File = struct {
2128 file.multi_pkg = true;2144 file.multi_pkg = true;
2129 file.status = .astgen_failure;2145 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
2132 const imports_index = file.zir.extra[@enumToInt(Zir.ExtraIndex.imports)];2151 const imports_index = file.zir.extra[@enumToInt(Zir.ExtraIndex.imports)];
2133 if (imports_index == 0) return;2152 if (imports_index == 0) return;
2134 const extra = file.zir.extraData(Zir.Inst.Imports, imports_index);2153 const extra = file.zir.extraData(Zir.Inst.Imports, imports_index);
...@@ -3323,10 +3342,19 @@ pub fn deinit(mod: *Module) void {...@@ -3323,10 +3342,19 @@ pub fn deinit(mod: *Module) void {
3323 // The callsite of `Compilation.create` owns the `main_pkg`, however3342 // The callsite of `Compilation.create` owns the `main_pkg`, however
3324 // Module owns the builtin and std packages that it adds.3343 // Module owns the builtin and std packages that it adds.
3325 if (mod.main_pkg.table.fetchRemove("builtin")) |kv| {3344 if (mod.main_pkg.table.fetchRemove("builtin")) |kv| {
3345 gpa.free(kv.key);
3326 kv.value.destroy(gpa);3346 kv.value.destroy(gpa);
3327 }3347 }
3328 if (mod.main_pkg.table.fetchRemove("std")) |kv| {3348 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);
3330 }3358 }
3331 if (mod.root_pkg != mod.main_pkg) {3359 if (mod.root_pkg != mod.main_pkg) {
3332 mod.root_pkg.destroy(gpa);3360 mod.root_pkg.destroy(gpa);
...@@ -4808,11 +4836,14 @@ pub fn importPkg(mod: *Module, pkg: *Package) !ImportFileResult {...@@ -4808,11 +4836,14 @@ pub fn importPkg(mod: *Module, pkg: *Package) !ImportFileResult {
48084836
4809 const gop = try mod.import_table.getOrPut(gpa, resolved_path);4837 const gop = try mod.import_table.getOrPut(gpa, resolved_path);
4810 errdefer _ = mod.import_table.pop();4838 errdefer _ = mod.import_table.pop();
4811 if (gop.found_existing) return ImportFileResult{4839 if (gop.found_existing) {
4812 .file = gop.value_ptr.*,4840 try gop.value_ptr.*.addReference(mod.*, .{ .root = pkg });
4813 .is_new = false,4841 return ImportFileResult{
4814 .is_pkg = true,4842 .file = gop.value_ptr.*,
4815 };4843 .is_new = false,
4844 .is_pkg = true,
4845 };
4846 }
48164847
4817 const sub_file_path = try gpa.dupe(u8, pkg.root_src_path);4848 const sub_file_path = try gpa.dupe(u8, pkg.root_src_path);
4818 errdefer gpa.free(sub_file_path);4849 errdefer gpa.free(sub_file_path);
...@@ -5208,22 +5239,14 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err...@@ -5208,22 +5239,14 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err
5208 // test decl with no name. Skip the part where we check against5239 // test decl with no name. Skip the part where we check against
5209 // the test name filter.5240 // the test name filter.
5210 if (!comp.bin_file.options.is_test) break :blk false;5241 if (!comp.bin_file.options.is_test) break :blk false;
5211 if (decl_pkg != mod.main_pkg) {5242 if (decl_pkg != mod.main_pkg) break :blk false;
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 }
5216 try mod.test_functions.put(gpa, new_decl_index, {});5243 try mod.test_functions.put(gpa, new_decl_index, {});
5217 break :blk true;5244 break :blk true;
5218 },5245 },
5219 else => blk: {5246 else => blk: {
5220 if (!is_named_test) break :blk false;5247 if (!is_named_test) break :blk false;
5221 if (!comp.bin_file.options.is_test) break :blk false;5248 if (!comp.bin_file.options.is_test) break :blk false;
5222 if (decl_pkg != mod.main_pkg) {5249 if (decl_pkg != mod.main_pkg) break :blk false;
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 }
5227 if (comp.test_filter) |test_filter| {5250 if (comp.test_filter) |test_filter| {
5228 if (mem.indexOf(u8, decl_name, test_filter) == null) {5251 if (mem.indexOf(u8, decl_name, test_filter) == null) {
5229 break :blk false;5252 break :blk false;
src/Package.zig+94-29
...@@ -22,17 +22,16 @@ pub const Table = std.StringHashMapUnmanaged(*Package);...@@ -22,17 +22,16 @@ pub const Table = std.StringHashMapUnmanaged(*Package);
22root_src_directory: Compilation.Directory,22root_src_directory: Compilation.Directory,
23/// Relative to `root_src_directory`. May contain path separators.23/// Relative to `root_src_directory`. May contain path separators.
24root_src_path: []const u8,24root_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.
25table: Table = .{},28table: Table = .{},
26parent: ?*Package = null,
27/// Whether to free `root_src_directory` on `destroy`.29/// Whether to free `root_src_directory` on `destroy`.
28root_src_directory_owned: bool = false,30root_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
32/// Allocate a Package. No references to the slices passed are kept.32/// Allocate a Package. No references to the slices passed are kept.
33pub fn create(33pub fn create(
34 gpa: Allocator,34 gpa: Allocator,
35 name: []const u8,
36 /// Null indicates the current working directory35 /// Null indicates the current working directory
37 root_src_dir_path: ?[]const u8,36 root_src_dir_path: ?[]const u8,
38 /// Relative to root_src_dir_path37 /// Relative to root_src_dir_path
...@@ -47,9 +46,6 @@ pub fn create(...@@ -47,9 +46,6 @@ pub fn create(
47 const owned_src_path = try gpa.dupe(u8, root_src_path);46 const owned_src_path = try gpa.dupe(u8, root_src_path);
48 errdefer gpa.free(owned_src_path);47 errdefer gpa.free(owned_src_path);
4948
50 const owned_name = try gpa.dupe(u8, name);
51 errdefer gpa.free(owned_name);
52
53 ptr.* = .{49 ptr.* = .{
54 .root_src_directory = .{50 .root_src_directory = .{
55 .path = owned_dir_path,51 .path = owned_dir_path,
...@@ -57,7 +53,6 @@ pub fn create(...@@ -57,7 +53,6 @@ pub fn create(
57 },53 },
58 .root_src_path = owned_src_path,54 .root_src_path = owned_src_path,
59 .root_src_directory_owned = true,55 .root_src_directory_owned = true,
60 .name = owned_name,
61 };56 };
6257
63 return ptr;58 return ptr;
...@@ -65,7 +60,6 @@ pub fn create(...@@ -65,7 +60,6 @@ pub fn create(
6560
66pub fn createWithDir(61pub fn createWithDir(
67 gpa: Allocator,62 gpa: Allocator,
68 name: []const u8,
69 directory: Compilation.Directory,63 directory: Compilation.Directory,
70 /// Relative to `directory`. If null, means `directory` is the root src dir64 /// Relative to `directory`. If null, means `directory` is the root src dir
71 /// and is owned externally.65 /// and is owned externally.
...@@ -79,9 +73,6 @@ pub fn createWithDir(...@@ -79,9 +73,6 @@ pub fn createWithDir(
79 const owned_src_path = try gpa.dupe(u8, root_src_path);73 const owned_src_path = try gpa.dupe(u8, root_src_path);
80 errdefer gpa.free(owned_src_path);74 errdefer gpa.free(owned_src_path);
8175
82 const owned_name = try gpa.dupe(u8, name);
83 errdefer gpa.free(owned_name);
84
85 if (root_src_dir_path) |p| {76 if (root_src_dir_path) |p| {
86 const owned_dir_path = try directory.join(gpa, &[1][]const u8{p});77 const owned_dir_path = try directory.join(gpa, &[1][]const u8{p});
87 errdefer gpa.free(owned_dir_path);78 errdefer gpa.free(owned_dir_path);
...@@ -93,14 +84,12 @@ pub fn createWithDir(...@@ -93,14 +84,12 @@ pub fn createWithDir(
93 },84 },
94 .root_src_directory_owned = true,85 .root_src_directory_owned = true,
95 .root_src_path = owned_src_path,86 .root_src_path = owned_src_path,
96 .name = owned_name,
97 };87 };
98 } else {88 } else {
99 ptr.* = .{89 ptr.* = .{
100 .root_src_directory = directory,90 .root_src_directory = directory,
101 .root_src_directory_owned = false,91 .root_src_directory_owned = false,
102 .root_src_path = owned_src_path,92 .root_src_path = owned_src_path,
103 .name = owned_name,
104 };93 };
105 }94 }
106 return ptr;95 return ptr;
...@@ -110,7 +99,6 @@ pub fn createWithDir(...@@ -110,7 +99,6 @@ pub fn createWithDir(
110/// inside its table; the caller is responsible for calling destroy() on them.99/// inside its table; the caller is responsible for calling destroy() on them.
111pub fn destroy(pkg: *Package, gpa: Allocator) void {100pub fn destroy(pkg: *Package, gpa: Allocator) void {
112 gpa.free(pkg.root_src_path);101 gpa.free(pkg.root_src_path);
113 gpa.free(pkg.name);
114102
115 if (pkg.root_src_directory_owned) {103 if (pkg.root_src_directory_owned) {
116 // If root_src_directory.path is null then the handle is the cwd()104 // 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 {...@@ -130,15 +118,97 @@ pub fn deinitTable(pkg: *Package, gpa: Allocator) void {
130 pkg.table.deinit(gpa);118 pkg.table.deinit(gpa);
131}119}
132120
133pub fn add(pkg: *Package, gpa: Allocator, package: *Package) !void {121pub fn add(pkg: *Package, gpa: Allocator, name: []const u8, package: *Package) !void {
134 try pkg.table.ensureUnusedCapacity(gpa, 1);122 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);
136}125}
137126
138pub fn addAndAdopt(parent: *Package, gpa: Allocator, child: *Package) !void {127/// Compute a readable name for the package. The returned name should be freed from gpa. This
139 assert(child.parent == null); // make up your mind, who is the parent??128/// function is very slow, as it traverses the whole package hierarchy to find a path to this
140 child.parent = parent;129/// package. It should only be used for error output.
141 return parent.add(gpa, child);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();
142}212}
143213
144pub const build_zig_basename = "build.zig";214pub const build_zig_basename = "build.zig";
...@@ -236,7 +306,7 @@ pub fn fetchAndAddDependencies(...@@ -236,7 +306,7 @@ pub fn fetchAndAddDependencies(
236 color,306 color,
237 );307 );
238308
239 try addAndAdopt(pkg, gpa, sub_pkg);309 try add(pkg, gpa, fqn, sub_pkg);
240310
241 try dependencies_source.writer().print(" pub const {s} = @import(\"{}\");\n", .{311 try dependencies_source.writer().print(" pub const {s} = @import(\"{}\");\n", .{
242 std.zig.fmtId(fqn), std.zig.fmtEscapes(fqn),312 std.zig.fmtId(fqn), std.zig.fmtEscapes(fqn),
...@@ -248,7 +318,6 @@ pub fn fetchAndAddDependencies(...@@ -248,7 +318,6 @@ pub fn fetchAndAddDependencies(
248318
249pub fn createFilePkg(319pub fn createFilePkg(
250 gpa: Allocator,320 gpa: Allocator,
251 name: []const u8,
252 cache_directory: Compilation.Directory,321 cache_directory: Compilation.Directory,
253 basename: []const u8,322 basename: []const u8,
254 contents: []const u8,323 contents: []const u8,
...@@ -269,7 +338,7 @@ pub fn createFilePkg(...@@ -269,7 +338,7 @@ pub fn createFilePkg(
269 const o_dir_sub_path = "o" ++ fs.path.sep_str ++ hex_digest;338 const o_dir_sub_path = "o" ++ fs.path.sep_str ++ hex_digest;
270 try renameTmpIntoCache(cache_directory.handle, tmp_dir_sub_path, o_dir_sub_path);339 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);
273}342}
274343
275const Report = struct {344const Report = struct {
...@@ -363,9 +432,6 @@ fn fetchAndUnpack(...@@ -363,9 +432,6 @@ fn fetchAndUnpack(
363 const owned_src_path = try gpa.dupe(u8, build_zig_basename);432 const owned_src_path = try gpa.dupe(u8, build_zig_basename);
364 errdefer gpa.free(owned_src_path);433 errdefer gpa.free(owned_src_path);
365434
366 const owned_name = try gpa.dupe(u8, fqn);
367 errdefer gpa.free(owned_name);
368
369 const build_root = try global_cache_directory.join(gpa, &.{pkg_dir_sub_path});435 const build_root = try global_cache_directory.join(gpa, &.{pkg_dir_sub_path});
370 errdefer gpa.free(build_root);436 errdefer gpa.free(build_root);
371437
...@@ -380,7 +446,6 @@ fn fetchAndUnpack(...@@ -380,7 +446,6 @@ fn fetchAndUnpack(
380 },446 },
381 .root_src_directory_owned = true,447 .root_src_directory_owned = true,
382 .root_src_path = owned_src_path,448 .root_src_path = owned_src_path,
383 .name = owned_name,
384 };449 };
385450
386 return ptr;451 return ptr;
...@@ -455,7 +520,7 @@ fn fetchAndUnpack(...@@ -455,7 +520,7 @@ fn fetchAndUnpack(
455 std.zig.fmtId(fqn), std.zig.fmtEscapes(build_root),520 std.zig.fmtId(fqn), std.zig.fmtEscapes(build_root),
456 });521 });
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);
459}524}
460525
461fn unpackTarball(526fn unpackTarball(
src/Sema.zig+3-3
...@@ -5311,7 +5311,6 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr...@@ -5311,7 +5311,6 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr
5311 }5311 }
5312 const c_import_pkg = Package.create(5312 const c_import_pkg = Package.create(
5313 sema.gpa,5313 sema.gpa,
5314 "c_import", // TODO: should we make this unique?
5315 null,5314 null,
5316 c_import_res.out_zig_path,5315 c_import_res.out_zig_path,
5317 ) catch |err| switch (err) {5316 ) catch |err| switch (err) {
...@@ -11793,8 +11792,9 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air....@@ -11793,8 +11792,9 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
11793 return sema.fail(block, operand_src, "import of file outside package path: '{s}'", .{operand});11792 return sema.fail(block, operand_src, "import of file outside package path: '{s}'", .{operand});
11794 },11793 },
11795 error.PackageNotFound => {11794 error.PackageNotFound => {
11796 const cur_pkg = block.getFileScope().pkg;11795 const name = try block.getFileScope().pkg.getName(sema.gpa, mod.*);
11797 return sema.fail(block, operand_src, "no package named '{s}' available within package '{s}'", .{ operand, cur_pkg.name });11796 defer sema.gpa.free(name);
11797 return sema.fail(block, operand_src, "no package named '{s}' available within package '{s}'", .{ operand, name });
11798 },11798 },
11799 else => {11799 else => {
11800 // TODO: these errors are file system errors; make sure an update() will11800 // 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 =...@@ -403,8 +403,11 @@ const usage_build_generic =
403 \\ ReleaseFast Optimizations on, safety off403 \\ ReleaseFast Optimizations on, safety off
404 \\ ReleaseSafe Optimizations on, safety on404 \\ ReleaseSafe Optimizations on, safety on
405 \\ ReleaseSmall Optimize for small binary, safety off405 \\ ReleaseSmall Optimize for small binary, safety off
406 \\ --pkg-begin [name] [path] Make pkg available to import and push current pkg406 \\ --mod [name]:[deps]:[src] Make a module available for dependency under the given name
407 \\ --pkg-end Pop current pkg407 \\ deps: [dep],[dep],...
408 \\ dep: [[import=]name]
409 \\ --deps [dep],[dep],... Set dependency names for the root package
410 \\ dep: [[import=]name]
408 \\ --main-pkg-path Set the directory of the root package411 \\ --main-pkg-path Set the directory of the root package
409 \\ -fPIC Force-enable Position Independent Code412 \\ -fPIC Force-enable Position Independent Code
410 \\ -fno-PIC Force-disable Position Independent Code413 \\ -fno-PIC Force-disable Position Independent Code
...@@ -858,15 +861,21 @@ fn buildOutputType(...@@ -858,15 +861,21 @@ fn buildOutputType(
858 var linker_export_symbol_names = std.ArrayList([]const u8).init(gpa);861 var linker_export_symbol_names = std.ArrayList([]const u8).init(gpa);
859 defer linker_export_symbol_names.deinit();862 defer linker_export_symbol_names.deinit();
860863
861 // This package only exists to clean up the code parsing --pkg-begin and864 // Contains every module specified via --mod. The dependencies are added
862 // --pkg-end flags. Use dummy values that are safe for the destroy call.865 // after argument parsing is completed. We use a StringArrayHashMap to make
863 var pkg_tree_root: Package = .{866 // error output consistent.
864 .root_src_directory = .{ .path = null, .handle = fs.cwd() },867 var modules = std.StringArrayHashMap(struct {
865 .root_src_path = &[0]u8{},868 mod: *Package,
866 .name = &[0]u8{},869 deps_str: []const u8, // still in CLI arg format
867 };870 }).init(gpa);
868 defer freePkgTree(gpa, &pkg_tree_root, false);871 defer {
869 var cur_pkg: *Package = &pkg_tree_root;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
871 // before arg parsing, check for the NO_COLOR environment variable880 // before arg parsing, check for the NO_COLOR environment variable
872 // if it exists, default the color setting to .off881 // if it exists, default the color setting to .off
...@@ -943,34 +952,44 @@ fn buildOutputType(...@@ -943,34 +952,44 @@ fn buildOutputType(
943 } else {952 } else {
944 fatal("unexpected end-of-parameter mark: --", .{});953 fatal("unexpected end-of-parameter mark: --", .{});
945 }954 }
946 } else if (mem.eql(u8, arg, "--pkg-begin")) {955 } else if (mem.eql(u8, arg, "--mod")) {
947 const opt_pkg_name = args_iter.next();956 const info = args_iter.nextOrFatal();
948 const opt_pkg_path = args_iter.next();957 var info_it = mem.split(u8, info, ":");
949 if (opt_pkg_name == null or opt_pkg_path == null)958 const mod_name = info_it.next() orelse fatal("expected non-empty argument after {s}", .{arg});
950 fatal("Expected 2 arguments after {s}", .{arg});959 const deps_str = info_it.next() orelse fatal("expected 'name:deps:path' after {s}", .{arg});
951960 const root_src_orig = info_it.rest();
952 const pkg_name = opt_pkg_name.?;961 if (root_src_orig.len == 0) fatal("expected 'name:deps:path' after {s}", .{arg});
953 const pkg_path = try introspect.resolvePath(arena, opt_pkg_path.?);962 if (mod_name.len == 0) fatal("empty name for module at '{s}'", .{root_src_orig});
954963
955 const new_cur_pkg = Package.create(964 const root_src = try introspect.resolvePath(arena, root_src_orig);
956 gpa,965
957 pkg_name,966 for ([_][]const u8{ "std", "root", "builtin" }) |name| {
958 fs.path.dirname(pkg_path),967 if (mem.eql(u8, mod_name, name)) {
959 fs.path.basename(pkg_path),968 fatal("unable to add module '{s}' -> '{s}': conflicts with builtin module", .{ mod_name, root_src });
960 ) catch |err| {969 }
961 fatal("Failed to add package at path {s}: {s}", .{ pkg_path, @errorName(err) });970 }
962 };
963971
964 if (mem.eql(u8, pkg_name, "std") or mem.eql(u8, pkg_name, "root") or mem.eql(u8, pkg_name, "builtin")) {972 var mod_it = modules.iterator();
965 fatal("unable to add package '{s}' -> '{s}': conflicts with builtin package", .{ pkg_name, pkg_path });973 while (mod_it.next()) |kv| {
966 } else if (cur_pkg.table.get(pkg_name)) |prev| {974 if (std.mem.eql(u8, mod_name, kv.key_ptr.*)) {
967 fatal("unable to add package '{s}' -> '{s}': already exists as '{s}", .{ pkg_name, pkg_path, prev.root_src_path });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", .{});
968 }991 }
969 try cur_pkg.addAndAdopt(gpa, new_cur_pkg);992 root_deps_str = args_iter.nextOrFatal();
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", .{});
974 } else if (mem.eql(u8, arg, "--main-pkg-path")) {993 } else if (mem.eql(u8, arg, "--main-pkg-path")) {
975 main_pkg_path = args_iter.nextOrFatal();994 main_pkg_path = args_iter.nextOrFatal();
976 } else if (mem.eql(u8, arg, "-cflags")) {995 } else if (mem.eql(u8, arg, "-cflags")) {
...@@ -2307,6 +2326,31 @@ fn buildOutputType(...@@ -2307,6 +2326,31 @@ fn buildOutputType(
2307 },2326 },
2308 }2327 }
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
2310 if (arg_mode == .build and optimize_mode == .ReleaseSmall and strip == null)2354 if (arg_mode == .build and optimize_mode == .ReleaseSmall and strip == null)
2311 strip = true;2355 strip = true;
23122356
...@@ -2886,14 +2930,14 @@ fn buildOutputType(...@@ -2886,14 +2930,14 @@ fn buildOutputType(
2886 if (main_pkg_path) |unresolved_main_pkg_path| {2930 if (main_pkg_path) |unresolved_main_pkg_path| {
2887 const p = try introspect.resolvePath(arena, unresolved_main_pkg_path);2931 const p = try introspect.resolvePath(arena, unresolved_main_pkg_path);
2888 if (p.len == 0) {2932 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);
2890 } else {2934 } else {
2891 const rel_src_path = try fs.path.relative(arena, p, src_path);2935 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);
2893 }2937 }
2894 } else {2938 } else {
2895 const root_src_dir_path = fs.path.dirname(src_path);2939 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| {
2897 if (root_src_dir_path) |p| {2941 if (root_src_dir_path) |p| {
2898 fatal("unable to open '{s}': {s}", .{ p, @errorName(err) });2942 fatal("unable to open '{s}': {s}", .{ p, @errorName(err) });
2899 } else {2943 } else {
...@@ -2904,23 +2948,24 @@ fn buildOutputType(...@@ -2904,23 +2948,24 @@ fn buildOutputType(
2904 } else null;2948 } else null;
2905 defer if (main_pkg) |p| p.destroy(gpa);2949 defer if (main_pkg) |p| p.destroy(gpa);
29062950
2907 // Transfer packages added with --pkg-begin/--pkg-end to the root package2951 // Transfer packages added with --deps to the root package
2908 if (main_pkg) |pkg| {2952 if (main_pkg) |mod| {
2909 var it = pkg_tree_root.table.valueIterator();2953 var it = ModuleDepIterator.init(root_deps_str orelse "");
2910 while (it.next()) |p| {2954 while (it.next()) |dep| {
2911 if (p.*.parent == &pkg_tree_root) {2955 if (dep.expose.len == 0) {
2912 p.*.parent = pkg;2956 fatal("root module depends on '{s}' with a blank name", .{dep.name});
2913 }2957 }
2914 }2958
2915 pkg.table = pkg_tree_root.table;2959 for ([_][]const u8{ "std", "root", "builtin" }) |name| {
2916 pkg_tree_root.table = .{};2960 if (mem.eql(u8, dep.expose, name)) {
2917 } else {2961 fatal("unable to add module '{s}' under name '{s}': conflicts with builtin module", .{ dep.name, dep.expose });
2918 // Remove any dangling pointers just in case.2962 }
2919 var it = pkg_tree_root.table.valueIterator();
2920 while (it.next()) |p| {
2921 if (p.*.parent == &pkg_tree_root) {
2922 p.*.parent = null;
2923 }2963 }
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);
2924 }2969 }
2925 }2970 }
29262971
...@@ -3400,6 +3445,32 @@ fn buildOutputType(...@@ -3400,6 +3445,32 @@ fn buildOutputType(
3400 return cleanExit();3445 return cleanExit();
3401}3446}
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
3403fn parseCrossTargetOrReportFatalError(3474fn parseCrossTargetOrReportFatalError(
3404 allocator: Allocator,3475 allocator: Allocator,
3405 opts: std.zig.CrossTarget.ParseOptions,3476 opts: std.zig.CrossTarget.ParseOptions,
...@@ -3626,18 +3697,6 @@ fn updateModule(gpa: Allocator, comp: *Compilation, hook: AfterUpdateHook) !void...@@ -3626,18 +3697,6 @@ fn updateModule(gpa: Allocator, comp: *Compilation, hook: AfterUpdateHook) !void
3626 }3697 }
3627}3698}
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
3641fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void {3700fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void {
3642 if (!build_options.have_llvm)3701 if (!build_options.have_llvm)
3643 fatal("cannot translate-c: compiler built without LLVM extensions", .{});3702 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...@@ -4141,7 +4200,6 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
4141 var main_pkg: Package = .{4200 var main_pkg: Package = .{
4142 .root_src_directory = zig_lib_directory,4201 .root_src_directory = zig_lib_directory,
4143 .root_src_path = "build_runner.zig",4202 .root_src_path = "build_runner.zig",
4144 .name = "root",
4145 };4203 };
41464204
4147 if (!build_options.omit_pkg_fetching_code) {4205 if (!build_options.omit_pkg_fetching_code) {
...@@ -4184,22 +4242,20 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi...@@ -4184,22 +4242,20 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
41844242
4185 const deps_pkg = try Package.createFilePkg(4243 const deps_pkg = try Package.createFilePkg(
4186 gpa,4244 gpa,
4187 "@dependencies",
4188 local_cache_directory,4245 local_cache_directory,
4189 "dependencies.zig",4246 "dependencies.zig",
4190 dependencies_source.items,4247 dependencies_source.items,
4191 );4248 );
41924249
4193 mem.swap(Package.Table, &main_pkg.table, &deps_pkg.table);4250 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);
4195 }4252 }
41964253
4197 var build_pkg: Package = .{4254 var build_pkg: Package = .{
4198 .root_src_directory = build_directory,4255 .root_src_directory = build_directory,
4199 .root_src_path = build_zig_basename,4256 .root_src_path = build_zig_basename,
4200 .name = "@build",
4201 };4257 };
4202 try main_pkg.addAndAdopt(gpa, &build_pkg);4258 try main_pkg.add(gpa, "@build", &build_pkg);
42034259
4204 const comp = Compilation.create(gpa, .{4260 const comp = Compilation.create(gpa, .{
4205 .zig_lib_directory = zig_lib_directory,4261 .zig_lib_directory = zig_lib_directory,
...@@ -4434,7 +4490,7 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void...@@ -4434,7 +4490,7 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void
4434 .root_decl = .none,4490 .root_decl = .none,
4435 };4491 };
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);
4438 defer file.pkg.destroy(gpa);4494 defer file.pkg.destroy(gpa);
44394495
4440 file.zir = try AstGen.generate(gpa, file.tree);4496 file.zir = try AstGen.generate(gpa, file.tree);
...@@ -4645,7 +4701,7 @@ fn fmtPathFile(...@@ -4645,7 +4701,7 @@ fn fmtPathFile(
4645 .root_decl = .none,4701 .root_decl = .none,
4646 };4702 };
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);
4649 defer file.pkg.destroy(fmt.gpa);4705 defer file.pkg.destroy(fmt.gpa);
46504706
4651 if (stat.size > max_src_size)4707 if (stat.size > max_src_size)
...@@ -5357,7 +5413,7 @@ pub fn cmdAstCheck(...@@ -5357,7 +5413,7 @@ pub fn cmdAstCheck(
5357 file.stat.size = source.len;5413 file.stat.size = source.len;
5358 }5414 }
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);
5361 defer file.pkg.destroy(gpa);5417 defer file.pkg.destroy(gpa);
53625418
5363 file.tree = try Ast.parse(gpa, file.source, .zig);5419 file.tree = try Ast.parse(gpa, file.source, .zig);
...@@ -5476,7 +5532,7 @@ pub fn cmdChangelist(...@@ -5476,7 +5532,7 @@ pub fn cmdChangelist(
5476 .root_decl = .none,5532 .root_decl = .none,
5477 };5533 };
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);
5480 defer file.pkg.destroy(gpa);5536 defer file.pkg.destroy(gpa);
54815537
5482 const source = try arena.allocSentinel(u8, @intCast(usize, stat.size), 0);5538 const source = try arena.allocSentinel(u8, @intCast(usize, stat.size), 0);
src/test.zig+38-2
...@@ -583,6 +583,11 @@ pub const TestContext = struct {...@@ -583,6 +583,11 @@ pub const TestContext = struct {
583 path: []const u8,583 path: []const u8,
584 };584 };
585585
586 pub const DepModule = struct {
587 name: []const u8,
588 path: []const u8,
589 };
590
586 pub const Backend = enum {591 pub const Backend = enum {
587 stage1,592 stage1,
588 stage2,593 stage2,
...@@ -611,6 +616,7 @@ pub const TestContext = struct {...@@ -611,6 +616,7 @@ pub const TestContext = struct {
611 link_libc: bool = false,616 link_libc: bool = false,
612617
613 files: std.ArrayList(File),618 files: std.ArrayList(File),
619 deps: std.ArrayList(DepModule),
614620
615 result: anyerror!void = {},621 result: anyerror!void = {},
616622
...@@ -618,6 +624,13 @@ pub const TestContext = struct {...@@ -618,6 +624,13 @@ pub const TestContext = struct {
618 case.files.append(.{ .path = name, .src = src }) catch @panic("out of memory");624 case.files.append(.{ .path = name, .src = src }) catch @panic("out of memory");
619 }625 }
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
621 /// Adds a subcase in which the module is updated with `src`, and a C634 /// Adds a subcase in which the module is updated with `src`, and a C
622 /// header is generated.635 /// header is generated.
623 pub fn addHeader(self: *Case, src: [:0]const u8, result: [:0]const u8) void {636 pub fn addHeader(self: *Case, src: [:0]const u8, result: [:0]const u8) void {
...@@ -767,6 +780,7 @@ pub const TestContext = struct {...@@ -767,6 +780,7 @@ pub const TestContext = struct {
767 .updates = std.ArrayList(Update).init(ctx.cases.allocator),780 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
768 .output_mode = .Exe,781 .output_mode = .Exe,
769 .files = std.ArrayList(File).init(ctx.arena),782 .files = std.ArrayList(File).init(ctx.arena),
783 .deps = std.ArrayList(DepModule).init(ctx.arena),
770 }) catch @panic("out of memory");784 }) catch @panic("out of memory");
771 return &ctx.cases.items[ctx.cases.items.len - 1];785 return &ctx.cases.items[ctx.cases.items.len - 1];
772 }786 }
...@@ -787,6 +801,7 @@ pub const TestContext = struct {...@@ -787,6 +801,7 @@ pub const TestContext = struct {
787 .updates = std.ArrayList(Update).init(ctx.cases.allocator),801 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
788 .output_mode = .Exe,802 .output_mode = .Exe,
789 .files = std.ArrayList(File).init(ctx.arena),803 .files = std.ArrayList(File).init(ctx.arena),
804 .deps = std.ArrayList(DepModule).init(ctx.arena),
790 .link_libc = true,805 .link_libc = true,
791 }) catch @panic("out of memory");806 }) catch @panic("out of memory");
792 return &ctx.cases.items[ctx.cases.items.len - 1];807 return &ctx.cases.items[ctx.cases.items.len - 1];
...@@ -801,6 +816,7 @@ pub const TestContext = struct {...@@ -801,6 +816,7 @@ pub const TestContext = struct {
801 .updates = std.ArrayList(Update).init(ctx.cases.allocator),816 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
802 .output_mode = .Exe,817 .output_mode = .Exe,
803 .files = std.ArrayList(File).init(ctx.arena),818 .files = std.ArrayList(File).init(ctx.arena),
819 .deps = std.ArrayList(DepModule).init(ctx.arena),
804 .backend = .llvm,820 .backend = .llvm,
805 .link_libc = true,821 .link_libc = true,
806 }) catch @panic("out of memory");822 }) catch @panic("out of memory");
...@@ -818,6 +834,7 @@ pub const TestContext = struct {...@@ -818,6 +834,7 @@ pub const TestContext = struct {
818 .updates = std.ArrayList(Update).init(ctx.cases.allocator),834 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
819 .output_mode = .Obj,835 .output_mode = .Obj,
820 .files = std.ArrayList(File).init(ctx.arena),836 .files = std.ArrayList(File).init(ctx.arena),
837 .deps = std.ArrayList(DepModule).init(ctx.arena),
821 }) catch @panic("out of memory");838 }) catch @panic("out of memory");
822 return &ctx.cases.items[ctx.cases.items.len - 1];839 return &ctx.cases.items[ctx.cases.items.len - 1];
823 }840 }
...@@ -834,6 +851,7 @@ pub const TestContext = struct {...@@ -834,6 +851,7 @@ pub const TestContext = struct {
834 .output_mode = .Exe,851 .output_mode = .Exe,
835 .is_test = true,852 .is_test = true,
836 .files = std.ArrayList(File).init(ctx.arena),853 .files = std.ArrayList(File).init(ctx.arena),
854 .deps = std.ArrayList(DepModule).init(ctx.arena),
837 }) catch @panic("out of memory");855 }) catch @panic("out of memory");
838 return &ctx.cases.items[ctx.cases.items.len - 1];856 return &ctx.cases.items[ctx.cases.items.len - 1];
839 }857 }
...@@ -858,6 +876,7 @@ pub const TestContext = struct {...@@ -858,6 +876,7 @@ pub const TestContext = struct {
858 .updates = std.ArrayList(Update).init(ctx.cases.allocator),876 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
859 .output_mode = .Obj,877 .output_mode = .Obj,
860 .files = std.ArrayList(File).init(ctx.arena),878 .files = std.ArrayList(File).init(ctx.arena),
879 .deps = std.ArrayList(DepModule).init(ctx.arena),
861 }) catch @panic("out of memory");880 }) catch @panic("out of memory");
862 return &ctx.cases.items[ctx.cases.items.len - 1];881 return &ctx.cases.items[ctx.cases.items.len - 1];
863 }882 }
...@@ -1145,6 +1164,7 @@ pub const TestContext = struct {...@@ -1145,6 +1164,7 @@ pub const TestContext = struct {
1145 .output_mode = output_mode,1164 .output_mode = output_mode,
1146 .link_libc = backend == .llvm,1165 .link_libc = backend == .llvm,
1147 .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator),1166 .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator),
1167 .deps = std.ArrayList(DepModule).init(ctx.cases.allocator),
1148 });1168 });
1149 try cases.append(next);1169 try cases.append(next);
1150 }1170 }
...@@ -1497,9 +1517,25 @@ pub const TestContext = struct {...@@ -1497,9 +1517,25 @@ pub const TestContext = struct {
1497 var main_pkg: Package = .{1517 var main_pkg: Package = .{
1498 .root_src_directory = .{ .path = tmp_dir_path, .handle = tmp.dir },1518 .root_src_directory = .{ .path = tmp_dir_path, .handle = tmp.dir },
1499 .root_src_path = tmp_src_path,1519 .root_src_path = tmp_src_path,
1500 .name = "root",
1501 };1520 };
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
1504 const bin_name = try std.zig.binNameAlloc(arena, .{1540 const bin_name = try std.zig.binNameAlloc(arena, .{
1505 .root_name = "test_case",1541 .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 {...@@ -288,4 +288,26 @@ pub fn addCases(ctx: *TestContext) !void {
288 //, &[_][]const u8{288 //, &[_][]const u8{
289 // "tmp.zig:4:1: error: unable to inline function",289 // "tmp.zig:4:1: error: unable to inline function",
290 //});290 //});
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 }
291}313}
test/stage2/nvptx.zig+1
...@@ -97,6 +97,7 @@ pub fn addPtx(...@@ -97,6 +97,7 @@ pub fn addPtx(
97 .updates = std.ArrayList(TestContext.Update).init(ctx.cases.allocator),97 .updates = std.ArrayList(TestContext.Update).init(ctx.cases.allocator),
98 .output_mode = .Obj,98 .output_mode = .Obj,
99 .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator),99 .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator),
100 .deps = std.ArrayList(TestContext.DepModule).init(ctx.cases.allocator),
100 .link_libc = false,101 .link_libc = false,
101 .backend = .llvm,102 .backend = .llvm,
102 // Bug in Debug mode103 // Bug in Debug mode
test/standalone.zig+6
...@@ -107,4 +107,10 @@ pub fn addCases(cases: *tests.StandaloneContext) void {...@@ -107,4 +107,10 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
107 cases.addBuildFile("test/standalone/emit_asm_and_bin/build.zig", .{});107 cases.addBuildFile("test/standalone/emit_asm_and_bin/build.zig", .{});
108 cases.addBuildFile("test/standalone/issue_12588/build.zig", .{});108 cases.addBuildFile("test/standalone/issue_12588/build.zig", .{});
109 cases.addBuildFile("test/standalone/embed_generated_file/build.zig", .{});109 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", .{});
110}116}
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}