@@ -955,7 +955,10 @@ pub fn addFrameworkPath(self: *CompileStep, dir_path: []const u8) void {
955
955
/// package's module table using `name`.
956
956
pub fn addModule (cs : * CompileStep , name : []const u8 , module : * Module ) void {
957
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
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
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
978
module .source_file .addStepDependencies (& cs .step );
974
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
1036
fn appendModuleArgs (
1032
1037
cs : * CompileStep ,
1033
1038
zig_args : * ArrayList ([]const u8 ),
1034
- name : []const u8 ,
1035
- module : * Module ,
1036
1039
) error {OutOfMemory }! void {
1037
- try zig_args .append ("--pkg-begin" );
1038
- try zig_args .append (name );
1039
- try zig_args .append (module .builder .pathFromRoot (module .source_file .getPath (module .builder )));
1040
+ // First, traverse the whole dependency graph and give every module a unique name, ideally one
1041
+ // named after what it's called somewhere in the graph. It will help here to have both a mapping
1042
+ // from module to name and a set of all the currently-used names.
1043
+ var mod_names = std .AutoHashMap (* Module , []const u8 ).init (cs .builder .allocator );
1044
+ var names = std .StringHashMap (void ).init (cs .builder .allocator );
1045
+
1046
+ var to_name = std .ArrayList (struct {
1047
+ name : []const u8 ,
1048
+ mod : * Module ,
1049
+ }).init (cs .builder .allocator );
1050
+ {
1051
+ var it = cs .modules .iterator ();
1052
+ while (it .next ()) | kv | {
1053
+ // While we're traversing the root dependencies, let's make sure that no module names
1054
+ // have colons in them, since the CLI forbids it. We handle this for transitive
1055
+ // dependencies further down.
1056
+ if (std .mem .indexOfScalar (u8 , kv .key_ptr .* , ':' ) != null ) {
1057
+ @panic ("Module names cannot contain colons" );
1058
+ }
1059
+ try to_name .append (.{
1060
+ .name = kv .key_ptr .* ,
1061
+ .mod = kv .value_ptr .* ,
1062
+ });
1063
+ }
1064
+ }
1065
+
1066
+ while (to_name .popOrNull ()) | dep | {
1067
+ if (mod_names .contains (dep .mod )) continue ;
1068
+
1069
+ // We'll use this buffer to store the name we decide on
1070
+ var buf = try cs .builder .allocator .alloc (u8 , dep .name .len + 32 );
1071
+ // First, try just the exposed dependency name
1072
+ std .mem .copy (u8 , buf , dep .name );
1073
+ var name = buf [0.. dep .name .len ];
1074
+ var n : usize = 0 ;
1075
+ while (names .contains (name )) {
1076
+ // If that failed, append an incrementing number to the end
1077
+ name = std .fmt .bufPrint (buf , "{s}{}" , .{ dep .name , n }) catch unreachable ;
1078
+ n += 1 ;
1079
+ }
1080
+
1081
+ try mod_names .put (dep .mod , name );
1082
+ try names .put (name , {});
1083
+
1084
+ var it = dep .mod .dependencies .iterator ();
1085
+ while (it .next ()) | kv | {
1086
+ // Same colon-in-name check as above, but for transitive dependencies.
1087
+ if (std .mem .indexOfScalar (u8 , kv .key_ptr .* , ':' ) != null ) {
1088
+ @panic ("Module names cannot contain colons" );
1089
+ }
1090
+ try to_name .append (.{
1091
+ .name = kv .key_ptr .* ,
1092
+ .mod = kv .value_ptr .* ,
1093
+ });
1094
+ }
1095
+ }
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 ();
1043
- for (module .dependencies .values (), 0.. ) | sub_module , i | {
1044
- const sub_name = keys [i ];
1045
- try cs .appendModuleArgs (zig_args , sub_name , sub_module );
1102
+ var it = mod_names .iterator ();
1103
+ while (it .next ()) | kv | {
1104
+ const mod = kv .key_ptr .* ;
1105
+ const name = kv .value_ptr .* ;
1106
+
1107
+ const deps_str = try constructDepString (cs .builder .allocator , mod_names , mod .dependencies );
1108
+ const src = mod .builder .pathFromRoot (mod .source_file .getPath (mod .builder ));
1109
+ try zig_args .append ("--mod" );
1110
+ try zig_args .append (try std .fmt .allocPrint (cs .builder .allocator , "{s}:{s}:{s}" , .{ name , deps_str , src }));
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
1145
fn make (step : * Step ) ! void {
@@ -1573,13 +1666,7 @@ fn make(step: *Step) !void {
1573
1666
try zig_args .append ("--test-no-exec" );
1574
1667
}
1575
1668
1576
- {
1577
- const keys = self .modules .keys ();
1578
- for (self .modules .values (), 0.. ) | module , i | {
1579
- const name = keys [i ];
1580
- try self .appendModuleArgs (& zig_args , name , module );
1581
- }
1582
- }
1669
+ try self .appendModuleArgs (& zig_args );
1583
1670
1584
1671
for (self .include_dirs .items ) | include_dir | {
1585
1672
switch (include_dir ) {
0 commit comments