| ... | ... | @@ -217,6 +217,8 @@ const UserValue = union(enum) { |
| 217 | 217 | scalar: []const u8, |
| 218 | 218 | list: ArrayList([]const u8), |
| 219 | 219 | map: StringHashMap(*const UserValue), |
| 220 | lazy_path: LazyPath, |
| 221 | lazy_path_list: ArrayList(LazyPath), |
| 220 | 222 | }; |
| 221 | 223 | |
| 222 | 224 | const TypeId = enum { |
| ... | ... | @@ -228,6 +230,8 @@ const TypeId = enum { |
| 228 | 230 | string, |
| 229 | 231 | list, |
| 230 | 232 | build_id, |
| 233 | lazy_path, |
| 234 | lazy_path_list, |
| 231 | 235 | }; |
| 232 | 236 | |
| 233 | 237 | const TopLevelStep = struct { |
| ... | ... | @@ -433,6 +437,22 @@ fn userInputOptionsFromArgs(allocator: Allocator, args: anytype) UserInputOption |
| 433 | 437 | .used = false, |
| 434 | 438 | }) catch @panic("OOM"); |
| 435 | 439 | }, |
| 440 | LazyPath => { |
| 441 | user_input_options.put(field.name, .{ |
| 442 | .name = field.name, |
| 443 | .value = .{ .lazy_path = v.dupeInner(allocator) }, |
| 444 | .used = false, |
| 445 | }) catch @panic("OOM"); |
| 446 | }, |
| 447 | []const LazyPath => { |
| 448 | var list = ArrayList(LazyPath).initCapacity(allocator, v.len) catch @panic("OOM"); |
| 449 | for (v) |lp| list.appendAssumeCapacity(lp.dupeInner(allocator)); |
| 450 | user_input_options.put(field.name, .{ |
| 451 | .name = field.name, |
| 452 | .value = .{ .lazy_path_list = list }, |
| 453 | .used = false, |
| 454 | }) catch @panic("OOM"); |
| 455 | }, |
| 436 | 456 | []const u8 => { |
| 437 | 457 | user_input_options.put(field.name, .{ |
| 438 | 458 | .name = field.name, |
| ... | ... | @@ -492,6 +512,8 @@ const OrderedUserValue = union(enum) { |
| 492 | 512 | scalar: []const u8, |
| 493 | 513 | list: ArrayList([]const u8), |
| 494 | 514 | map: ArrayList(Pair), |
| 515 | lazy_path: LazyPath, |
| 516 | lazy_path_list: ArrayList(LazyPath), |
| 495 | 517 | |
| 496 | 518 | const Pair = struct { |
| 497 | 519 | name: []const u8, |
| ... | ... | @@ -502,6 +524,7 @@ const OrderedUserValue = union(enum) { |
| 502 | 524 | }; |
| 503 | 525 | |
| 504 | 526 | fn hash(val: OrderedUserValue, hasher: *std.hash.Wyhash) void { |
| 527 | hasher.update(&std.mem.toBytes(std.meta.activeTag(val))); |
| 505 | 528 | switch (val) { |
| 506 | 529 | .flag => {}, |
| 507 | 530 | .scalar => |scalar| hasher.update(scalar), |
| ... | ... | @@ -512,6 +535,31 @@ const OrderedUserValue = union(enum) { |
| 512 | 535 | hasher.update(map_entry.name); |
| 513 | 536 | map_entry.value.hash(hasher); |
| 514 | 537 | }, |
| 538 | .lazy_path => |lp| hashLazyPath(lp, hasher), |
| 539 | .lazy_path_list => |lp_list| for (lp_list.items) |lp| { |
| 540 | hashLazyPath(lp, hasher); |
| 541 | }, |
| 542 | } |
| 543 | } |
| 544 | |
| 545 | fn hashLazyPath(lp: LazyPath, hasher: *std.hash.Wyhash) void { |
| 546 | switch (lp) { |
| 547 | .src_path => |sp| { |
| 548 | hasher.update(sp.owner.pkg_hash); |
| 549 | hasher.update(sp.sub_path); |
| 550 | }, |
| 551 | .generated => |gen| { |
| 552 | hasher.update(gen.file.step.owner.pkg_hash); |
| 553 | hasher.update(std.mem.asBytes(&gen.up)); |
| 554 | hasher.update(gen.sub_path); |
| 555 | }, |
| 556 | .cwd_relative => |rel_path| { |
| 557 | hasher.update(rel_path); |
| 558 | }, |
| 559 | .dependency => |dep| { |
| 560 | hasher.update(dep.dependency.builder.pkg_hash); |
| 561 | hasher.update(dep.sub_path); |
| 562 | }, |
| 515 | 563 | } |
| 516 | 564 | } |
| 517 | 565 | |
| ... | ... | @@ -535,6 +583,8 @@ const OrderedUserValue = union(enum) { |
| 535 | 583 | .scalar => |scalar| .{ .scalar = scalar }, |
| 536 | 584 | .list => |list| .{ .list = list }, |
| 537 | 585 | .map => |map| .{ .map = OrderedUserValue.mapFromUnordered(allocator, map) }, |
| 586 | .lazy_path => |lp| .{ .lazy_path = lp }, |
| 587 | .lazy_path_list => |list| .{ .lazy_path_list = list }, |
| 538 | 588 | }; |
| 539 | 589 | } |
| 540 | 590 | }; |
| ... | ... | @@ -1023,7 +1073,11 @@ pub fn addConfigHeader( |
| 1023 | 1073 | |
| 1024 | 1074 | /// Allocator.dupe without the need to handle out of memory. |
| 1025 | 1075 | pub fn dupe(b: *Build, bytes: []const u8) []u8 { |
| 1026 | | return b.allocator.dupe(u8, bytes) catch @panic("OOM"); |
| 1076 | return dupeInner(b.allocator, bytes); |
| 1077 | } |
| 1078 | |
| 1079 | pub fn dupeInner(allocator: std.mem.Allocator, bytes: []const u8) []u8 { |
| 1080 | return allocator.dupe(u8, bytes) catch @panic("OOM"); |
| 1027 | 1081 | } |
| 1028 | 1082 | |
| 1029 | 1083 | /// Duplicates an array of strings without the need to handle out of memory. |
| ... | ... | @@ -1035,7 +1089,11 @@ pub fn dupeStrings(b: *Build, strings: []const []const u8) [][]u8 { |
| 1035 | 1089 | |
| 1036 | 1090 | /// Duplicates a path and converts all slashes to the OS's canonical path separator. |
| 1037 | 1091 | pub fn dupePath(b: *Build, bytes: []const u8) []u8 { |
| 1038 | | const the_copy = b.dupe(bytes); |
| 1092 | return dupePathInner(b.allocator, bytes); |
| 1093 | } |
| 1094 | |
| 1095 | fn dupePathInner(allocator: std.mem.Allocator, bytes: []const u8) []u8 { |
| 1096 | const the_copy = dupeInner(allocator, bytes); |
| 1039 | 1097 | for (the_copy) |*byte| { |
| 1040 | 1098 | switch (byte.*) { |
| 1041 | 1099 | '/', '\\' => byte.* = fs.path.sep, |
| ... | ... | @@ -1149,7 +1207,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw |
| 1149 | 1207 | return null; |
| 1150 | 1208 | } |
| 1151 | 1209 | }, |
| 1152 | | .list, .map => { |
| 1210 | .list, .map, .lazy_path, .lazy_path_list => { |
| 1153 | 1211 | log.err("Expected -D{s} to be a boolean, but received a {s}.", .{ |
| 1154 | 1212 | name, @tagName(option_ptr.value), |
| 1155 | 1213 | }); |
| ... | ... | @@ -1158,7 +1216,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw |
| 1158 | 1216 | }, |
| 1159 | 1217 | }, |
| 1160 | 1218 | .int => switch (option_ptr.value) { |
| 1161 | | .flag, .list, .map => { |
| 1219 | .flag, .list, .map, .lazy_path, .lazy_path_list => { |
| 1162 | 1220 | log.err("Expected -D{s} to be an integer, but received a {s}.", .{ |
| 1163 | 1221 | name, @tagName(option_ptr.value), |
| 1164 | 1222 | }); |
| ... | ... | @@ -1182,7 +1240,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw |
| 1182 | 1240 | }, |
| 1183 | 1241 | }, |
| 1184 | 1242 | .float => switch (option_ptr.value) { |
| 1185 | | .flag, .map, .list => { |
| 1243 | .flag, .map, .list, .lazy_path, .lazy_path_list => { |
| 1186 | 1244 | log.err("Expected -D{s} to be a float, but received a {s}.", .{ |
| 1187 | 1245 | name, @tagName(option_ptr.value), |
| 1188 | 1246 | }); |
| ... | ... | @@ -1199,7 +1257,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw |
| 1199 | 1257 | }, |
| 1200 | 1258 | }, |
| 1201 | 1259 | .@"enum" => switch (option_ptr.value) { |
| 1202 | | .flag, .map, .list => { |
| 1260 | .flag, .map, .list, .lazy_path, .lazy_path_list => { |
| 1203 | 1261 | log.err("Expected -D{s} to be an enum, but received a {s}.", .{ |
| 1204 | 1262 | name, @tagName(option_ptr.value), |
| 1205 | 1263 | }); |
| ... | ... | @@ -1217,7 +1275,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw |
| 1217 | 1275 | }, |
| 1218 | 1276 | }, |
| 1219 | 1277 | .string => switch (option_ptr.value) { |
| 1220 | | .flag, .list, .map => { |
| 1278 | .flag, .list, .map, .lazy_path, .lazy_path_list => { |
| 1221 | 1279 | log.err("Expected -D{s} to be a string, but received a {s}.", .{ |
| 1222 | 1280 | name, @tagName(option_ptr.value), |
| 1223 | 1281 | }); |
| ... | ... | @@ -1227,7 +1285,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw |
| 1227 | 1285 | .scalar => |s| return s, |
| 1228 | 1286 | }, |
| 1229 | 1287 | .build_id => switch (option_ptr.value) { |
| 1230 | | .flag, .map, .list => { |
| 1288 | .flag, .map, .list, .lazy_path, .lazy_path_list => { |
| 1231 | 1289 | log.err("Expected -D{s} to be an enum, but received a {s}.", .{ |
| 1232 | 1290 | name, @tagName(option_ptr.value), |
| 1233 | 1291 | }); |
| ... | ... | @@ -1245,7 +1303,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw |
| 1245 | 1303 | }, |
| 1246 | 1304 | }, |
| 1247 | 1305 | .list => switch (option_ptr.value) { |
| 1248 | | .flag, .map => { |
| 1306 | .flag, .map, .lazy_path, .lazy_path_list => { |
| 1249 | 1307 | log.err("Expected -D{s} to be a list, but received a {s}.", .{ |
| 1250 | 1308 | name, @tagName(option_ptr.value), |
| 1251 | 1309 | }); |
| ... | ... | @@ -1258,7 +1316,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw |
| 1258 | 1316 | .list => |lst| return lst.items, |
| 1259 | 1317 | }, |
| 1260 | 1318 | .enum_list => switch (option_ptr.value) { |
| 1261 | | .flag, .map => { |
| 1319 | .flag, .map, .lazy_path, .lazy_path_list => { |
| 1262 | 1320 | log.err("Expected -D{s} to be a list, but received a {s}.", .{ |
| 1263 | 1321 | name, @tagName(option_ptr.value), |
| 1264 | 1322 | }); |
| ... | ... | @@ -1276,19 +1334,48 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw |
| 1276 | 1334 | }, |
| 1277 | 1335 | .list => |lst| { |
| 1278 | 1336 | const Child = @typeInfo(T).pointer.child; |
| 1279 | | var new_list = b.allocator.alloc(Child, lst.items.len) catch @panic("OOM"); |
| 1280 | | for (lst.items, 0..) |str, i| { |
| 1281 | | const value = std.meta.stringToEnum(Child, str) orelse { |
| 1337 | const new_list = b.allocator.alloc(Child, lst.items.len) catch @panic("OOM"); |
| 1338 | for (new_list, lst.items) |*new_item, str| { |
| 1339 | new_item.* = std.meta.stringToEnum(Child, str) orelse { |
| 1282 | 1340 | log.err("Expected -D{s} to be of type {s}.", .{ name, @typeName(Child) }); |
| 1283 | 1341 | b.markInvalidUserInput(); |
| 1284 | 1342 | b.allocator.free(new_list); |
| 1285 | 1343 | return null; |
| 1286 | 1344 | }; |
| 1287 | | new_list[i] = value; |
| 1288 | 1345 | } |
| 1289 | 1346 | return new_list; |
| 1290 | 1347 | }, |
| 1291 | 1348 | }, |
| 1349 | .lazy_path => switch (option_ptr.value) { |
| 1350 | .scalar => |s| return .{ .cwd_relative = s }, |
| 1351 | .lazy_path => |lp| return lp, |
| 1352 | .flag, .map, .list, .lazy_path_list => { |
| 1353 | log.err("Expected -D{s} to be a path, but received a {s}.", .{ |
| 1354 | name, @tagName(option_ptr.value), |
| 1355 | }); |
| 1356 | b.markInvalidUserInput(); |
| 1357 | return null; |
| 1358 | }, |
| 1359 | }, |
| 1360 | .lazy_path_list => switch (option_ptr.value) { |
| 1361 | .scalar => |s| return b.allocator.dupe(LazyPath, &[_]LazyPath{.{ .cwd_relative = s }}) catch @panic("OOM"), |
| 1362 | .lazy_path => |lp| return b.allocator.dupe(LazyPath, &[_]LazyPath{lp}) catch @panic("OOM"), |
| 1363 | .list => |lst| { |
| 1364 | const new_list = b.allocator.alloc(LazyPath, lst.items.len) catch @panic("OOM"); |
| 1365 | for (new_list, lst.items) |*new_item, str| { |
| 1366 | new_item.* = .{ .cwd_relative = str }; |
| 1367 | } |
| 1368 | return new_list; |
| 1369 | }, |
| 1370 | .lazy_path_list => |lp_list| return lp_list.items, |
| 1371 | .flag, .map => { |
| 1372 | log.err("Expected -D{s} to be a path, but received a {s}.", .{ |
| 1373 | name, @tagName(option_ptr.value), |
| 1374 | }); |
| 1375 | b.markInvalidUserInput(); |
| 1376 | return null; |
| 1377 | }, |
| 1378 | }, |
| 1292 | 1379 | } |
| 1293 | 1380 | } |
| 1294 | 1381 | |
| ... | ... | @@ -1508,6 +1595,10 @@ pub fn addUserInputOption(b: *Build, name_raw: []const u8, value_raw: []const u8 |
| 1508 | 1595 | log.warn("TODO maps as command line arguments is not implemented yet.", .{}); |
| 1509 | 1596 | return true; |
| 1510 | 1597 | }, |
| 1598 | .lazy_path, .lazy_path_list => { |
| 1599 | log.warn("the lazy path value type isn't added from the CLI, but somehow '{s}' is a .{}", .{ name, std.zig.fmtId(@tagName(gop.value_ptr.value)) }); |
| 1600 | return true; |
| 1601 | }, |
| 1511 | 1602 | } |
| 1512 | 1603 | return false; |
| 1513 | 1604 | } |
| ... | ... | @@ -1530,10 +1621,15 @@ pub fn addUserInputFlag(b: *Build, name_raw: []const u8) !bool { |
| 1530 | 1621 | log.err("Flag '-D{s}' conflicts with option '-D{s}={s}'.", .{ name, name, s }); |
| 1531 | 1622 | return true; |
| 1532 | 1623 | }, |
| 1533 | | .list, .map => { |
| 1624 | .list, .map, .lazy_path_list => { |
| 1534 | 1625 | log.err("Flag '-D{s}' conflicts with multiple options of the same name.", .{name}); |
| 1535 | 1626 | return true; |
| 1536 | 1627 | }, |
| 1628 | .lazy_path => |lp| { |
| 1629 | log.err("Flag '-D{s}' conflicts with option '-D{s}={s}'.", .{ name, name, lp.getDisplayName() }); |
| 1630 | return true; |
| 1631 | }, |
| 1632 | |
| 1537 | 1633 | .flag => {}, |
| 1538 | 1634 | } |
| 1539 | 1635 | return false; |
| ... | ... | @@ -1542,6 +1638,7 @@ pub fn addUserInputFlag(b: *Build, name_raw: []const u8) !bool { |
| 1542 | 1638 | fn typeToEnum(comptime T: type) TypeId { |
| 1543 | 1639 | return switch (T) { |
| 1544 | 1640 | std.zig.BuildId => .build_id, |
| 1641 | LazyPath => .lazy_path, |
| 1545 | 1642 | else => return switch (@typeInfo(T)) { |
| 1546 | 1643 | .int => .int, |
| 1547 | 1644 | .float => .float, |
| ... | ... | @@ -1550,6 +1647,7 @@ fn typeToEnum(comptime T: type) TypeId { |
| 1550 | 1647 | .pointer => |pointer| switch (pointer.child) { |
| 1551 | 1648 | u8 => .string, |
| 1552 | 1649 | []const u8 => .list, |
| 1650 | LazyPath => .lazy_path_list, |
| 1553 | 1651 | else => switch (@typeInfo(pointer.child)) { |
| 1554 | 1652 | .@"enum" => .enum_list, |
| 1555 | 1653 | else => @compileError("Unsupported type: " ++ @typeName(T)), |
| ... | ... | @@ -2065,22 +2163,17 @@ pub fn dependencyFromBuildZig( |
| 2065 | 2163 | } |
| 2066 | 2164 | |
| 2067 | 2165 | fn userValuesAreSame(lhs: UserValue, rhs: UserValue) bool { |
| 2166 | if (std.meta.activeTag(lhs) != rhs) return false; |
| 2068 | 2167 | switch (lhs) { |
| 2069 | 2168 | .flag => {}, |
| 2070 | 2169 | .scalar => |lhs_scalar| { |
| 2071 | | const rhs_scalar = switch (rhs) { |
| 2072 | | .scalar => |scalar| scalar, |
| 2073 | | else => return false, |
| 2074 | | }; |
| 2170 | const rhs_scalar = rhs.scalar; |
| 2075 | 2171 | |
| 2076 | 2172 | if (!std.mem.eql(u8, lhs_scalar, rhs_scalar)) |
| 2077 | 2173 | return false; |
| 2078 | 2174 | }, |
| 2079 | 2175 | .list => |lhs_list| { |
| 2080 | | const rhs_list = switch (rhs) { |
| 2081 | | .list => |list| list, |
| 2082 | | else => return false, |
| 2083 | | }; |
| 2176 | const rhs_list = rhs.list; |
| 2084 | 2177 | |
| 2085 | 2178 | if (lhs_list.items.len != rhs_list.items.len) |
| 2086 | 2179 | return false; |
| ... | ... | @@ -2091,10 +2184,7 @@ fn userValuesAreSame(lhs: UserValue, rhs: UserValue) bool { |
| 2091 | 2184 | } |
| 2092 | 2185 | }, |
| 2093 | 2186 | .map => |lhs_map| { |
| 2094 | | const rhs_map = switch (rhs) { |
| 2095 | | .map => |map| map, |
| 2096 | | else => return false, |
| 2097 | | }; |
| 2187 | const rhs_map = rhs.map; |
| 2098 | 2188 | |
| 2099 | 2189 | if (lhs_map.count() != rhs_map.count()) |
| 2100 | 2190 | return false; |
| ... | ... | @@ -2106,11 +2196,54 @@ fn userValuesAreSame(lhs: UserValue, rhs: UserValue) bool { |
| 2106 | 2196 | return false; |
| 2107 | 2197 | } |
| 2108 | 2198 | }, |
| 2199 | .lazy_path => |lhs_lp| { |
| 2200 | const rhs_lp = rhs.lazy_path; |
| 2201 | return userLazyPathsAreTheSame(lhs_lp, rhs_lp); |
| 2202 | }, |
| 2203 | .lazy_path_list => |lhs_lp_list| { |
| 2204 | const rhs_lp_list = rhs.lazy_path_list; |
| 2205 | if (lhs_lp_list.items.len != rhs_lp_list.items.len) return false; |
| 2206 | for (lhs_lp_list.items, rhs_lp_list.items) |lhs_lp, rhs_lp| { |
| 2207 | if (!userLazyPathsAreTheSame(lhs_lp, rhs_lp)) return false; |
| 2208 | } |
| 2209 | return true; |
| 2210 | }, |
| 2109 | 2211 | } |
| 2110 | 2212 | |
| 2111 | 2213 | return true; |
| 2112 | 2214 | } |
| 2113 | 2215 | |
| 2216 | fn userLazyPathsAreTheSame(lhs_lp: LazyPath, rhs_lp: LazyPath) bool { |
| 2217 | if (std.meta.activeTag(lhs_lp) != rhs_lp) return false; |
| 2218 | switch (lhs_lp) { |
| 2219 | .src_path => |lhs_sp| { |
| 2220 | const rhs_sp = rhs_lp.src_path; |
| 2221 | |
| 2222 | if (lhs_sp.owner != rhs_sp.owner) return false; |
| 2223 | if (std.mem.eql(u8, lhs_sp.sub_path, rhs_sp.sub_path)) return false; |
| 2224 | }, |
| 2225 | .generated => |lhs_gen| { |
| 2226 | const rhs_gen = rhs_lp.generated; |
| 2227 | |
| 2228 | if (lhs_gen.file != rhs_gen.file) return false; |
| 2229 | if (lhs_gen.up != rhs_gen.up) return false; |
| 2230 | if (std.mem.eql(u8, lhs_gen.sub_path, rhs_gen.sub_path)) return false; |
| 2231 | }, |
| 2232 | .cwd_relative => |lhs_rel_path| { |
| 2233 | const rhs_rel_path = rhs_lp.cwd_relative; |
| 2234 | |
| 2235 | if (!std.mem.eql(u8, lhs_rel_path, rhs_rel_path)) return false; |
| 2236 | }, |
| 2237 | .dependency => |lhs_dep| { |
| 2238 | const rhs_dep = rhs_lp.dependency; |
| 2239 | |
| 2240 | if (lhs_dep.dependency != rhs_dep.dependency) return false; |
| 2241 | if (!std.mem.eql(u8, lhs_dep.sub_path, rhs_dep.sub_path)) return false; |
| 2242 | }, |
| 2243 | } |
| 2244 | return true; |
| 2245 | } |
| 2246 | |
| 2114 | 2247 | fn dependencyInner( |
| 2115 | 2248 | b: *Build, |
| 2116 | 2249 | name: []const u8, |
| ... | ... | @@ -2435,16 +2568,20 @@ pub const LazyPath = union(enum) { |
| 2435 | 2568 | /// The `b` parameter is only used for its allocator. All *Build instances |
| 2436 | 2569 | /// share the same allocator. |
| 2437 | 2570 | pub fn dupe(lazy_path: LazyPath, b: *Build) LazyPath { |
| 2571 | return lazy_path.dupeInner(b.allocator); |
| 2572 | } |
| 2573 | |
| 2574 | fn dupeInner(lazy_path: LazyPath, allocator: std.mem.Allocator) LazyPath { |
| 2438 | 2575 | return switch (lazy_path) { |
| 2439 | 2576 | .src_path => |sp| .{ .src_path = .{ |
| 2440 | 2577 | .owner = sp.owner, |
| 2441 | 2578 | .sub_path = sp.owner.dupePath(sp.sub_path), |
| 2442 | 2579 | } }, |
| 2443 | | .cwd_relative => |p| .{ .cwd_relative = b.dupePath(p) }, |
| 2580 | .cwd_relative => |p| .{ .cwd_relative = dupePathInner(allocator, p) }, |
| 2444 | 2581 | .generated => |gen| .{ .generated = .{ |
| 2445 | 2582 | .file = gen.file, |
| 2446 | 2583 | .up = gen.up, |
| 2447 | | .sub_path = b.dupePath(gen.sub_path), |
| 2584 | .sub_path = dupePathInner(allocator, gen.sub_path), |
| 2448 | 2585 | } }, |
| 2449 | 2586 | .dependency => |dep| .{ .dependency = dep }, |
| 2450 | 2587 | }; |