| author | |
| committer | |
| log | 7f691b3fe26f623d108a6b2b2018bbc3aa999224 |
| tree | 81814816cfde6610a4b965d9d3799de3cb3eaccd |
| parent | 05da5b32a820c031001098034840940964f41a81 |
| parent | f94cbab3acc3b31464f45872c1f700874eecb23e |
| signature |
New module CLI34 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 -lc | 748 | build-exe src/main.zig -ofmt=c -lc |
| 749 | -OReleaseSmall | 749 | -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-end | 751 | --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=c | 766 | build-obj lib/compiler_rt.zig -ofmt=c |
| 766 | -OReleaseSmall | 767 | -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-end | 769 | --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-msvc | 92 | -target x86_64-windows-msvc |
| 92 | CheckLastExitCode | 93 | CheckLastExitCode |
| 93 | 94 |
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-msvc | 92 | -target x86_64-windows-msvc |
| 92 | CheckLastExitCode | 93 | CheckLastExitCode |
| 93 | 94 |
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`. |
| 956 | pub fn addModule(cs: *CompileStep, name: []const u8, module: *Module) void { | 956 | pub 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 | } |
| 960 | 963 | ||
| 961 | /// Adds a module to be used with `@import` without exposing it in the current | 964 | /// 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 | } |
| 971 | 974 | ||
| 972 | fn addRecursiveBuildDeps(cs: *CompileStep, module: *Module) void { | 975 | fn 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 | } |
| 978 | 983 | ||
| ... | @@ -1031,22 +1036,110 @@ fn linkLibraryOrObject(self: *CompileStep, other: *CompileStep) void { | ... | @@ -1031,22 +1036,110 @@ fn linkLibraryOrObject(self: *CompileStep, other: *CompileStep) void { |
| 1031 | fn appendModuleArgs( | 1036 | fn 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 | } | ||
| 1040 | 1096 | ||
| 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 | } |
| 1048 | 1113 | ||
| 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 | |||
| 1122 | fn 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 | } |
| 1051 | 1144 | ||
| 1052 | fn make(step: *Step) !void { | 1145 | fn 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 | } |
| 1575 | 1668 | ||
| 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 | } | ||
| 1583 | 1670 | ||
| 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); |
| 862 | 862 | ||
| 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 files | 863 | // importFile cannot error out since all files |
| 872 | // are already loaded at this point | 864 | // 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); |
| 875 | 867 | ||
| 876 | // Immediately add this package to the import table of our | 868 | // 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 { |
| 1596 | 1596 | ||
| 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); |
| 1605 | 1604 | ||
| 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); | ||
| 1614 | 1634 | ||
| 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); |
| 1622 | 1640 | ||
| 1623 | // copy package table from main_pkg to root_pkg | 1641 | // 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); |
| 1649 | 1665 | ||
| 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); |
| 1653 | 1669 | ||
| 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 | } |
| 1657 | 1673 | ||
| 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 need | 1674 | // 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 | } |
| 2774 | 2775 | ||
| 2776 | fn 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 the | 2881 | /// 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 | } |
| 3099 | 3205 | ||
| 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 | } |
| 3150 | 3209 | ||
| 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 | } = .{}, |
| 145 | 145 | ||
| 146 | job_queued_update_builtin_zig: bool = true, | 146 | job_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`. | ||
| 150 | main_pkg_is_std: bool, | ||
| 151 | 147 | ||
| 152 | compile_log_text: ArrayListUnmanaged(u8) = .{}, | 148 | compile_log_text: ArrayListUnmanaged(u8) = .{}, |
| 153 | 149 | ||
| ... | @@ -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, |
| 1951 | 1947 | ||
| 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 { |
| 2113 | 2109 | ||
| 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 | } | ||
| 2117 | 2133 | ||
| 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; |
| 2130 | 2146 | ||
| 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`, however | 3342 | // 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 { |
| 4808 | 4836 | ||
| 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 | } | ||
| 4816 | 4847 | ||
| 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 against | 5239 | // 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); |
| 22 | root_src_directory: Compilation.Directory, | 22 | root_src_directory: Compilation.Directory, |
| 23 | /// Relative to `root_src_directory`. May contain path separators. | 23 | /// Relative to `root_src_directory`. May contain path separators. |
| 24 | root_src_path: []const u8, | 24 | root_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. | ||
| 25 | table: Table = .{}, | 28 | table: Table = .{}, |
| 26 | parent: ?*Package = null, | ||
| 27 | /// Whether to free `root_src_directory` on `destroy`. | 29 | /// Whether to free `root_src_directory` on `destroy`. |
| 28 | root_src_directory_owned: bool = false, | 30 | root_src_directory_owned: bool = false, |
| 29 | /// This information can be recovered from 'table', but it's more convenient to store on the package. | ||
| 30 | name: []const u8, | ||
| 31 | 31 | ||
| 32 | /// Allocate a Package. No references to the slices passed are kept. | 32 | /// Allocate a Package. No references to the slices passed are kept. |
| 33 | pub fn create( | 33 | pub fn create( |
| 34 | gpa: Allocator, | 34 | gpa: Allocator, |
| 35 | name: []const u8, | ||
| 36 | /// Null indicates the current working directory | 35 | /// 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_path | 37 | /// 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); |
| 49 | 48 | ||
| 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 | }; |
| 62 | 57 | ||
| 63 | return ptr; | 58 | return ptr; |
| ... | @@ -65,7 +60,6 @@ pub fn create( | ... | @@ -65,7 +60,6 @@ pub fn create( |
| 65 | 60 | ||
| 66 | pub fn createWithDir( | 61 | pub 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 dir | 64 | /// 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); |
| 81 | 75 | ||
| 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. |
| 111 | pub fn destroy(pkg: *Package, gpa: Allocator) void { | 100 | pub 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); | ||
| 114 | 102 | ||
| 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 | } |
| 132 | 120 | ||
| 133 | pub fn add(pkg: *Package, gpa: Allocator, package: *Package) !void { | 121 | pub 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 | } |
| 137 | 126 | ||
| 138 | pub 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); | 130 | pub 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 | } |
| 143 | 213 | ||
| 144 | pub const build_zig_basename = "build.zig"; | 214 | pub 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 | ); |
| 238 | 308 | ||
| 239 | try addAndAdopt(pkg, gpa, sub_pkg); | 309 | try add(pkg, gpa, fqn, sub_pkg); |
| 240 | 310 | ||
| 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( |
| 248 | 318 | ||
| 249 | pub fn createFilePkg( | 319 | pub 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); |
| 271 | 340 | ||
| 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 | } |
| 274 | 343 | ||
| 275 | const Report = struct { | 344 | const 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); |
| 365 | 434 | ||
| 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); |
| 371 | 437 | ||
| ... | @@ -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 | }; |
| 385 | 450 | ||
| 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 | }); |
| 457 | 522 | ||
| 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 | } |
| 460 | 525 | ||
| 461 | fn unpackTarball( | 526 | fn 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() will | 11800 | // 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 off | 403 | \\ ReleaseFast Optimizations on, safety off |
| 404 | \\ ReleaseSafe Optimizations on, safety on | 404 | \\ ReleaseSafe Optimizations on, safety on |
| 405 | \\ ReleaseSmall Optimize for small binary, safety off | 405 | \\ ReleaseSmall Optimize for small binary, safety off |
| 406 | \\ --pkg-begin [name] [path] Make pkg available to import and push current pkg | 406 | \\ --mod [name]:[deps]:[src] Make a module available for dependency under the given name |
| 407 | \\ --pkg-end Pop current pkg | 407 | \\ 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 package | 411 | \\ --main-pkg-path Set the directory of the root package |
| 409 | \\ -fPIC Force-enable Position Independent Code | 412 | \\ -fPIC Force-enable Position Independent Code |
| 410 | \\ -fno-PIC Force-disable Position Independent Code | 413 | \\ -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(); |
| 860 | 863 | ||
| 861 | // This package only exists to clean up the code parsing --pkg-begin and | 864 | // 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; | ||
| 870 | 879 | ||
| 871 | // before arg parsing, check for the NO_COLOR environment variable | 880 | // before arg parsing, check for the NO_COLOR environment variable |
| 872 | // if it exists, default the color setting to .off | 881 | // 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}); |
| 951 | 960 | 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}); |
| 954 | 963 | ||
| 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 | }; | ||
| 963 | 971 | ||
| 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 | } |
| 2309 | 2328 | ||
| 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; |
| 2312 | 2356 | ||
| ... | @@ -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); |
| 2906 | 2950 | ||
| 2907 | // Transfer packages added with --pkg-begin/--pkg-end to the root package | 2951 | // 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 | } |
| 2926 | 2971 | ||
| ... | @@ -3400,6 +3445,32 @@ fn buildOutputType( | ... | @@ -3400,6 +3445,32 @@ fn buildOutputType( |
| 3400 | return cleanExit(); | 3445 | return cleanExit(); |
| 3401 | } | 3446 | } |
| 3402 | 3447 | ||
| 3448 | const 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 | |||
| 3403 | fn parseCrossTargetOrReportFatalError( | 3474 | fn 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 | } |
| 3628 | 3699 | ||
| 3629 | fn 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 | |||
| 3641 | fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void { | 3700 | fn 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 | }; |
| 4146 | 4204 | ||
| 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 |
| 4184 | 4242 | ||
| 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 | ); |
| 4192 | 4249 | ||
| 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 | } |
| 4196 | 4253 | ||
| 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); |
| 4203 | 4259 | ||
| 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 | }; |
| 4436 | 4492 | ||
| 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); |
| 4439 | 4495 | ||
| 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 | }; |
| 4647 | 4703 | ||
| 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); |
| 4650 | 4706 | ||
| 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 | } |
| 5359 | 5415 | ||
| 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); |
| 5362 | 5418 | ||
| 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 | }; |
| 5478 | 5534 | ||
| 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); |
| 5481 | 5537 | ||
| 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 | }; |
| 585 | 585 | ||
| 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, |
| 612 | 617 | ||
| 613 | files: std.ArrayList(File), | 618 | files: std.ArrayList(File), |
| 619 | deps: std.ArrayList(DepModule), | ||
| 614 | 620 | ||
| 615 | result: anyerror!void = {}, | 621 | result: anyerror!void = {}, |
| 616 | 622 | ||
| ... | @@ -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 | } |
| 620 | 626 | ||
| 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 C | 634 | /// 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 | } | ||
| 1503 | 1539 | ||
| 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 mode | 103 | // 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 @@ | ||
| 1 | pub const shared = @import("shared"); | ||
test/standalone/dep_diamond/build.zig created+28| ... | @@ -0,0 +1,28 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | pub 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 @@ | ||
| 1 | pub 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 @@ | ||
| 1 | const foo = @import("foo"); | ||
| 2 | const bar = @import("bar"); | ||
| 3 | const assert = @import("std").debug.assert; | ||
| 4 | |||
| 5 | pub fn main() void { | ||
| 6 | assert(foo.shared == bar.shared); | ||
| 7 | } | ||
test/standalone/dep_mutually_recursive/bar.zig created+6| ... | @@ -0,0 +1,6 @@ | ||
| 1 | const assert = @import("std").debug.assert; | ||
| 2 | pub const foo = @import("foo"); | ||
| 3 | |||
| 4 | comptime { | ||
| 5 | assert(foo.bar == @This()); | ||
| 6 | } | ||
test/standalone/dep_mutually_recursive/build.zig created+26| ... | @@ -0,0 +1,26 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | pub 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 @@ | ||
| 1 | const assert = @import("std").debug.assert; | ||
| 2 | pub const bar = @import("bar"); | ||
| 3 | |||
| 4 | comptime { | ||
| 5 | assert(bar.foo == @This()); | ||
| 6 | } | ||
test/standalone/dep_mutually_recursive/test.zig created+7| ... | @@ -0,0 +1,7 @@ | ||
| 1 | const foo = @import("foo"); | ||
| 2 | const assert = @import("std").debug.assert; | ||
| 3 | |||
| 4 | pub 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 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | pub 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 @@ | ||
| 1 | const assert = @import("std").debug.assert; | ||
| 2 | pub const foo = @import("foo"); | ||
| 3 | |||
| 4 | comptime { | ||
| 5 | assert(foo == @This()); | ||
| 6 | } | ||
test/standalone/dep_recursive/test.zig created+8| ... | @@ -0,0 +1,8 @@ | ||
| 1 | const foo = @import("foo"); | ||
| 2 | const shared = @import("shared"); | ||
| 3 | const assert = @import("std").debug.assert; | ||
| 4 | |||
| 5 | pub 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 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | pub 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 @@ | ||
| 1 | pub const std = @import("std"); | ||
| 2 | pub const builtin = @import("builtin"); | ||
| 3 | pub const root = @import("root"); | ||
test/standalone/dep_shared_builtin/test.zig created+11| ... | @@ -0,0 +1,11 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const root = @import("root"); | ||
| 4 | const foo = @import("foo"); | ||
| 5 | |||
| 6 | pub 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 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | pub 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 @@ | ||
| 1 | pub 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 @@ | ||
| 1 | const foo = @import("foo"); | ||
| 2 | const shared = @import("shared"); | ||
| 3 | const assert = @import("std").debug.assert; | ||
| 4 | |||
| 5 | pub fn main() void { | ||
| 6 | assert(foo.shared == shared); | ||
| 7 | } | ||