| ... | ... | @@ -9,7 +9,6 @@ const mem = std.mem; |
| 9 | 9 | const panic = std.debug.panic; |
| 10 | 10 | const assert = std.debug.assert; |
| 11 | 11 | const log = std.log; |
| 12 | | const StringHashMap = std.StringHashMap; |
| 13 | 12 | const Allocator = std.mem.Allocator; |
| 14 | 13 | const Target = std.Target; |
| 15 | 14 | const process = std.process; |
| ... | ... | @@ -32,9 +31,6 @@ graph: *Graph, |
| 32 | 31 | install_tls: Step.TopLevel, |
| 33 | 32 | uninstall_tls: Step.TopLevel, |
| 34 | 33 | allocator: Allocator, |
| 35 | | user_input_options: UserInputOptionsMap, |
| 36 | | available_options_map: std.array_hash_map.String(AvailableOption) = .empty, |
| 37 | | invalid_user_input: bool, |
| 38 | 34 | default_step: *Step, |
| 39 | 35 | top_level_steps: std.array_hash_map.String(*Step.TopLevel), |
| 40 | 36 | /// Path to the directory containing build.zig. |
| ... | ... | @@ -45,6 +41,10 @@ debug_log_scopes: []const []const u8 = &.{}, |
| 45 | 41 | /// Set to 0 to disable stack collection. |
| 46 | 42 | debug_stack_frames_count: u8 = 8, |
| 47 | 43 | |
| 44 | user_input_options: PackageOptions.Map, |
| 45 | available_options_map: std.array_hash_map.String(AvailableOption) = .empty, |
| 46 | invalid_user_input: bool, |
| 47 | |
| 48 | 48 | dep_prefix: []const u8 = "", |
| 49 | 49 | |
| 50 | 50 | modules: std.array_hash_map.String(*Module), |
| ... | ... | @@ -82,7 +82,7 @@ pub const Graph = struct { |
| 82 | 82 | needed_lazy_dependencies: std.array_hash_map.String(void) = .empty, |
| 83 | 83 | /// Information about the native target. Computed before build() is invoked. |
| 84 | 84 | host: ResolvedTarget, |
| 85 | | dependency_cache: InitializedDepMap = .empty, |
| 85 | dependency_cache: PackageInstanceMap = .empty, |
| 86 | 86 | allow_so_scripts: ?bool = null, |
| 87 | 87 | time_report: bool = false, |
| 88 | 88 | verbose: bool = false, |
| ... | ... | @@ -238,65 +238,123 @@ pub const SystemLibraryMode = enum { |
| 238 | 238 | declared_enabled, |
| 239 | 239 | }; |
| 240 | 240 | |
| 241 | | const InitializedDepMap = std.HashMapUnmanaged(InitializedDepKey, *Dependency, InitializedDepContext, std.hash_map.default_max_load_percentage); |
| 242 | | const InitializedDepKey = struct { |
| 243 | | build_root_string: []const u8, |
| 244 | | user_input_options: UserInputOptionsMap, |
| 245 | | }; |
| 246 | | |
| 247 | | const InitializedDepContext = struct { |
| 248 | | allocator: Allocator, |
| 249 | | |
| 250 | | pub fn hash(ctx: @This(), k: InitializedDepKey) u64 { |
| 241 | const PackageInstanceMap = std.array_hash_map.Custom(PackageInstanceKey, *Dependency, struct { |
| 242 | pub fn hash(_: @This(), k: PackageInstanceKey) u32 { |
| 251 | 243 | var hasher = std.hash.Wyhash.init(0); |
| 252 | | hasher.update(k.build_root_string); |
| 253 | | hashUserInputOptionsMap(ctx.allocator, k.user_input_options, &hasher); |
| 254 | | return hasher.final(); |
| 244 | hasher.update(k.pkg_hash); |
| 245 | for (k.options.keys(), k.options.values()) |option_key, option_value| { |
| 246 | hasher.update(option_key); |
| 247 | option_value.hash(&hasher); |
| 248 | } |
| 249 | return @truncate(hasher.final()); |
| 250 | } |
| 251 | |
| 252 | pub fn eql(_: @This(), a: PackageInstanceKey, b: PackageInstanceKey, _: usize) bool { |
| 253 | if (!mem.eql(u8, a.pkg_hash, b.pkg_hash)) return false; |
| 254 | if (a.options.count() != b.options.count()) return false; |
| 255 | for ( |
| 256 | a.options.keys(), |
| 257 | b.options.keys(), |
| 258 | a.options.values(), |
| 259 | b.options.values(), |
| 260 | ) |a_key, b_key, a_val, b_val| { |
| 261 | if (!mem.eql(u8, a_key, b_key)) return false; |
| 262 | if (!a_val.eql(b_val)) return false; |
| 263 | } |
| 264 | return true; |
| 255 | 265 | } |
| 266 | }, true); |
| 256 | 267 | |
| 257 | | pub fn eql(_: @This(), lhs: InitializedDepKey, rhs: InitializedDepKey) bool { |
| 258 | | if (!std.mem.eql(u8, lhs.build_root_string, rhs.build_root_string)) |
| 259 | | return false; |
| 268 | const PackageInstanceKey = struct { |
| 269 | pkg_hash: []const u8, |
| 270 | options: *const PackageOptions.Map, |
| 271 | }; |
| 260 | 272 | |
| 261 | | if (lhs.user_input_options.count() != rhs.user_input_options.count()) |
| 262 | | return false; |
| 273 | /// Build system implementation details. |
| 274 | pub const PackageOptions = struct { |
| 275 | pub const Map = std.array_hash_map.String(UserProvided); |
| 276 | |
| 277 | pub const UserProvided = union(enum) { |
| 278 | flag: void, |
| 279 | scalar: []const u8, |
| 280 | list: std.ArrayList([]const u8), |
| 281 | map: std.array_hash_map.String(*const UserProvided), |
| 282 | lazy_path: LazyPath, |
| 283 | lazy_path_list: std.ArrayList(LazyPath), |
| 284 | |
| 285 | fn eql(a: UserProvided, b: UserProvided) bool { |
| 286 | if (std.meta.activeTag(a) != b) return false; |
| 287 | return switch (a) { |
| 288 | .flag => true, |
| 289 | .scalar => |a_scalar| return mem.eql(u8, a_scalar, b.scalar), |
| 290 | .list => |a_list| { |
| 291 | if (a_list.items.len != b.list.items.len) return false; |
| 292 | for (a_list.items, b.list.items) |a_elem, b_elem| { |
| 293 | if (!mem.eql(u8, a_elem, b_elem)) |
| 294 | return false; |
| 295 | } |
| 296 | return true; |
| 297 | }, |
| 298 | .map => |a_map| { |
| 299 | if (a_map.count() != b.map.count()) return false; |
| 300 | for (a_map.keys(), a_map.values(), b.map.keys(), b.map.values()) |a_key, a_val, b_key, b_val| { |
| 301 | if (!mem.eql(u8, a_key, b_key)) return false; |
| 302 | if (!a_val.eql(b_val.*)) return false; |
| 303 | } |
| 304 | return true; |
| 305 | }, |
| 306 | .lazy_path => |a_lazy_path| return a_lazy_path.eql(b.lazy_path), |
| 307 | .lazy_path_list => |a_lazy_path_list| { |
| 308 | if (a_lazy_path_list.items.len != b.lazy_path_list.items.len) return false; |
| 309 | for (a_lazy_path_list.items, b.lazy_path_list.items) |a_lp, b_lp| { |
| 310 | if (!a_lp.eql(b_lp)) return false; |
| 311 | } |
| 312 | return true; |
| 313 | }, |
| 314 | }; |
| 315 | } |
| 316 | |
| 317 | fn hash(a: UserProvided, hasher: *std.hash.Wyhash) void { |
| 318 | hasher.update(&mem.toBytes(std.meta.activeTag(a))); |
| 319 | switch (a) { |
| 320 | .flag => {}, |
| 321 | .scalar => |scalar| hasher.update(scalar), |
| 322 | .list => |*list| for (list.items) |elem| hasher.update(elem), |
| 323 | .map => |*map| for (map.keys(), map.values()) |key, val| { |
| 324 | hasher.update(key); |
| 325 | val.hash(hasher); |
| 326 | }, |
| 327 | .lazy_path => |lp| lp.hash(hasher), |
| 328 | .lazy_path_list => |*list| for (list.items) |lp| lp.hash(hasher), |
| 329 | } |
| 330 | } |
| 331 | }; |
| 263 | 332 | |
| 264 | | var it = lhs.user_input_options.iterator(); |
| 265 | | while (it.next()) |lhs_entry| { |
| 266 | | const rhs_value = rhs.user_input_options.get(lhs_entry.key_ptr.*) orelse return false; |
| 267 | | if (!userValuesAreSame(lhs_entry.value_ptr.*.value, rhs_value.value)) |
| 268 | | return false; |
| 333 | fn fromArgs(arena: Allocator, map: *PackageOptions.Map, args: anytype) void { |
| 334 | const args_info = @typeInfo(@TypeOf(args)).@"struct"; |
| 335 | inline for (args_info.field_names, args_info.field_types) |field_name, field_type| { |
| 336 | if (field_type == @TypeOf(null)) continue; |
| 337 | addPackageOptionFromArg(arena, map, field_name, field_type, @field(args, field_name)); |
| 269 | 338 | } |
| 339 | } |
| 270 | 340 | |
| 271 | | return true; |
| 341 | pub fn sort(map: *Map) void { |
| 342 | map.sortUnstable(@as(struct { |
| 343 | keys: []const []const u8, |
| 344 | pub fn lessThan(this: @This(), a_index: usize, b_index: usize) bool { |
| 345 | return mem.lessThan(u8, this.keys[a_index], this.keys[b_index]); |
| 346 | } |
| 347 | }, .{ .keys = map.keys() })); |
| 272 | 348 | } |
| 273 | 349 | }; |
| 274 | 350 | |
| 275 | | pub const UserInputOptionsMap = StringHashMap(UserInputOption); |
| 276 | | |
| 277 | 351 | const AvailableOption = struct { |
| 278 | | name: []const u8, |
| 279 | 352 | type_id: Configuration.AvailableOption.Type, |
| 280 | 353 | description: []const u8, |
| 281 | 354 | /// If the `type_id` is `enum` or `enum_list` this provides the list of enum options |
| 282 | 355 | enum_options: ?[]const []const u8, |
| 283 | 356 | }; |
| 284 | 357 | |
| 285 | | pub const UserInputOption = struct { |
| 286 | | name: []const u8, |
| 287 | | value: UserValue, |
| 288 | | used: bool, |
| 289 | | }; |
| 290 | | |
| 291 | | pub const UserValue = union(enum) { |
| 292 | | flag: void, |
| 293 | | scalar: []const u8, |
| 294 | | list: std.array_list.Managed([]const u8), |
| 295 | | map: StringHashMap(*const UserValue), |
| 296 | | lazy_path: LazyPath, |
| 297 | | lazy_path_list: std.array_list.Managed(LazyPath), |
| 298 | | }; |
| 299 | | |
| 300 | 358 | /// Build system implementation detail. |
| 301 | 359 | pub fn create( |
| 302 | 360 | graph: *Graph, |
| ... | ... | @@ -311,7 +369,7 @@ pub fn create( |
| 311 | 369 | .root = root, |
| 312 | 370 | .invalid_user_input = false, |
| 313 | 371 | .allocator = arena, |
| 314 | | .user_input_options = UserInputOptionsMap.init(arena), |
| 372 | .user_input_options = .empty, |
| 315 | 373 | .top_level_steps = .{}, |
| 316 | 374 | .default_step = undefined, |
| 317 | 375 | .install_tls = .{ |
| ... | ... | @@ -348,7 +406,7 @@ fn createChild( |
| 348 | 406 | root: Cache.Path, |
| 349 | 407 | pkg_hash: []const u8, |
| 350 | 408 | pkg_deps: AvailableDeps, |
| 351 | | user_input_options: UserInputOptionsMap, |
| 409 | user_input_options: PackageOptions.Map, |
| 352 | 410 | ) error{OutOfMemory}!*Build { |
| 353 | 411 | const arena = parent.graph.arena; |
| 354 | 412 | const child = try arena.create(Build); |
| ... | ... | @@ -390,167 +448,96 @@ fn createChild( |
| 390 | 448 | return child; |
| 391 | 449 | } |
| 392 | 450 | |
| 393 | | fn userInputOptionsFromArgs(arena: Allocator, args: anytype) UserInputOptionsMap { |
| 394 | | var map = UserInputOptionsMap.init(arena); |
| 395 | | const args_info = @typeInfo(@TypeOf(args)).@"struct"; |
| 396 | | inline for (args_info.field_names, args_info.field_types) |field_name, field_type| { |
| 397 | | if (field_type == @TypeOf(null)) continue; |
| 398 | | addUserInputOptionFromArg(arena, &map, field_name, field_type, @field(args, field_name)); |
| 399 | | } |
| 400 | | return map; |
| 401 | | } |
| 402 | | |
| 403 | | fn addUserInputOptionFromArg( |
| 451 | fn addPackageOptionFromArg( |
| 404 | 452 | arena: Allocator, |
| 405 | | map: *UserInputOptionsMap, |
| 453 | map: *PackageOptions.Map, |
| 406 | 454 | field_name: [:0]const u8, |
| 407 | 455 | comptime T: type, |
| 408 | 456 | /// If null, the value won't be added, but `T` will still be type-checked. |
| 409 | 457 | maybe_value: ?T, |
| 410 | 458 | ) void { |
| 459 | map.ensureUnusedCapacity(arena, 2) catch @panic("OOM"); |
| 411 | 460 | switch (T) { |
| 412 | 461 | Target.Query => return if (maybe_value) |v| { |
| 413 | | map.put(field_name, .{ |
| 414 | | .name = field_name, |
| 415 | | .value = .{ .scalar = v.zigTriple(arena) catch @panic("OOM") }, |
| 416 | | .used = false, |
| 417 | | }) catch @panic("OOM"); |
| 418 | | map.put("cpu", .{ |
| 419 | | .name = "cpu", |
| 420 | | .value = .{ .scalar = v.serializeCpuAlloc(arena) catch @panic("OOM") }, |
| 421 | | .used = false, |
| 422 | | }) catch @panic("OOM"); |
| 462 | map.putAssumeCapacity(field_name, .{ .scalar = v.zigTriple(arena) catch @panic("OOM") }); |
| 463 | map.putAssumeCapacity("cpu", .{ .scalar = v.serializeCpuAlloc(arena) catch @panic("OOM") }); |
| 423 | 464 | }, |
| 424 | 465 | ResolvedTarget => return if (maybe_value) |v| { |
| 425 | | map.put(field_name, .{ |
| 426 | | .name = field_name, |
| 427 | | .value = .{ .scalar = v.query.zigTriple(arena) catch @panic("OOM") }, |
| 428 | | .used = false, |
| 429 | | }) catch @panic("OOM"); |
| 430 | | map.put("cpu", .{ |
| 431 | | .name = "cpu", |
| 432 | | .value = .{ .scalar = v.query.serializeCpuAlloc(arena) catch @panic("OOM") }, |
| 433 | | .used = false, |
| 434 | | }) catch @panic("OOM"); |
| 466 | map.putAssumeCapacity(field_name, .{ .scalar = v.query.zigTriple(arena) catch @panic("OOM") }); |
| 467 | map.putAssumeCapacity("cpu", .{ .scalar = v.query.serializeCpuAlloc(arena) catch @panic("OOM") }); |
| 435 | 468 | }, |
| 436 | 469 | std.zig.BuildId => return if (maybe_value) |v| { |
| 437 | | map.put(field_name, .{ |
| 438 | | .name = field_name, |
| 439 | | .value = .{ .scalar = std.fmt.allocPrint(arena, "{f}", .{v}) catch @panic("OOM") }, |
| 440 | | .used = false, |
| 441 | | }) catch @panic("OOM"); |
| 470 | map.putAssumeCapacity(field_name, .{ |
| 471 | .scalar = std.fmt.allocPrint(arena, "{f}", .{v}) catch @panic("OOM"), |
| 472 | }); |
| 442 | 473 | }, |
| 443 | 474 | LazyPath => return if (maybe_value) |v| { |
| 444 | | map.put(field_name, .{ |
| 445 | | .name = field_name, |
| 446 | | .value = .{ .lazy_path = v.dupeInner(arena) }, |
| 447 | | .used = false, |
| 448 | | }) catch @panic("OOM"); |
| 475 | map.putAssumeCapacity(field_name, .{ .lazy_path = v.dupeInner(arena) }); |
| 449 | 476 | }, |
| 450 | 477 | []const LazyPath => return if (maybe_value) |v| { |
| 451 | | var list = std.array_list.Managed(LazyPath).initCapacity(arena, v.len) catch @panic("OOM"); |
| 452 | | for (v) |lp| list.appendAssumeCapacity(lp.dupeInner(arena)); |
| 453 | | map.put(field_name, .{ |
| 454 | | .name = field_name, |
| 455 | | .value = .{ .lazy_path_list = list }, |
| 456 | | .used = false, |
| 457 | | }) catch @panic("OOM"); |
| 478 | var list: std.ArrayList(LazyPath) = .empty; |
| 479 | const elems = list.addManyAsSlice(arena, v.len) catch @panic("OOM"); |
| 480 | for (v, elems) |lp, *elem| elem.* = lp.dupeInner(arena); |
| 481 | map.putAssumeCapacity(field_name, .{ .lazy_path_list = list }); |
| 458 | 482 | }, |
| 459 | 483 | []const u8 => return if (maybe_value) |v| { |
| 460 | | map.put(field_name, .{ |
| 461 | | .name = field_name, |
| 462 | | .value = .{ .scalar = arena.dupe(u8, v) catch @panic("OOM") }, |
| 463 | | .used = false, |
| 464 | | }) catch @panic("OOM"); |
| 484 | map.putAssumeCapacity(field_name, .{ .scalar = arena.dupe(u8, v) catch @panic("OOM") }); |
| 465 | 485 | }, |
| 466 | 486 | []const []const u8 => return if (maybe_value) |v| { |
| 467 | | var list = std.array_list.Managed([]const u8).initCapacity(arena, v.len) catch @panic("OOM"); |
| 468 | | for (v) |s| list.appendAssumeCapacity(arena.dupe(u8, s) catch @panic("OOM")); |
| 469 | | map.put(field_name, .{ |
| 470 | | .name = field_name, |
| 471 | | .value = .{ .list = list }, |
| 472 | | .used = false, |
| 473 | | }) catch @panic("OOM"); |
| 487 | var list: std.ArrayList([]const u8) = .empty; |
| 488 | const elems = list.addManyAsSlice(arena, v.len) catch @panic("OOM"); |
| 489 | for (v, elems) |s, *elem| elem.* = arena.dupe(u8, s) catch @panic("OOM"); |
| 490 | map.putAssumeCapacity(field_name, .{ .list = list }); |
| 474 | 491 | }, |
| 475 | 492 | else => switch (@typeInfo(T)) { |
| 476 | 493 | .bool => return if (maybe_value) |v| { |
| 477 | | map.put(field_name, .{ |
| 478 | | .name = field_name, |
| 479 | | .value = .{ .scalar = if (v) "true" else "false" }, |
| 480 | | .used = false, |
| 481 | | }) catch @panic("OOM"); |
| 494 | map.putAssumeCapacity(field_name, .{ .scalar = if (v) "true" else "false" }); |
| 482 | 495 | }, |
| 483 | 496 | .@"enum", .enum_literal => return if (maybe_value) |v| { |
| 484 | | map.put(field_name, .{ |
| 485 | | .name = field_name, |
| 486 | | .value = .{ .scalar = @tagName(v) }, |
| 487 | | .used = false, |
| 488 | | }) catch @panic("OOM"); |
| 497 | map.putAssumeCapacity(field_name, .{ .scalar = @tagName(v) }); |
| 489 | 498 | }, |
| 490 | 499 | .comptime_int, .int => return if (maybe_value) |v| { |
| 491 | | map.put(field_name, .{ |
| 492 | | .name = field_name, |
| 493 | | .value = .{ .scalar = std.fmt.allocPrint(arena, "{d}", .{v}) catch @panic("OOM") }, |
| 494 | | .used = false, |
| 495 | | }) catch @panic("OOM"); |
| 500 | map.putAssumeCapacity(field_name, .{ |
| 501 | .scalar = std.fmt.allocPrint(arena, "{d}", .{v}) catch @panic("OOM"), |
| 502 | }); |
| 496 | 503 | }, |
| 497 | 504 | .comptime_float, .float => return if (maybe_value) |v| { |
| 498 | | map.put(field_name, .{ |
| 499 | | .name = field_name, |
| 500 | | .value = .{ .scalar = std.fmt.allocPrint(arena, "{x}", .{v}) catch @panic("OOM") }, |
| 501 | | .used = false, |
| 502 | | }) catch @panic("OOM"); |
| 505 | map.putAssumeCapacity(field_name, .{ |
| 506 | .scalar = std.fmt.allocPrint(arena, "{x}", .{v}) catch @panic("OOM"), |
| 507 | }); |
| 503 | 508 | }, |
| 504 | 509 | .pointer => |ptr_info| switch (ptr_info.size) { |
| 505 | 510 | .one => switch (@typeInfo(ptr_info.child)) { |
| 506 | | .array => |array_info| { |
| 507 | | addUserInputOptionFromArg( |
| 508 | | arena, |
| 509 | | map, |
| 510 | | field_name, |
| 511 | | @Pointer(.slice, .{ .@"const" = true }, array_info.child, null), |
| 512 | | maybe_value orelse null, |
| 513 | | ); |
| 514 | | return; |
| 515 | | }, |
| 511 | .array => |array_info| return addPackageOptionFromArg( |
| 512 | arena, |
| 513 | map, |
| 514 | field_name, |
| 515 | @Pointer(.slice, .{ .@"const" = true }, array_info.child, null), |
| 516 | maybe_value orelse null, |
| 517 | ), |
| 516 | 518 | else => {}, |
| 517 | 519 | }, |
| 518 | 520 | .slice => switch (@typeInfo(ptr_info.child)) { |
| 519 | 521 | .@"enum" => return if (maybe_value) |v| { |
| 520 | | var list = std.array_list.Managed([]const u8).initCapacity(arena, v.len) catch @panic("OOM"); |
| 521 | | for (v) |tag| list.appendAssumeCapacity(@tagName(tag)); |
| 522 | | map.put(field_name, .{ |
| 523 | | .name = field_name, |
| 524 | | .value = .{ .list = list }, |
| 525 | | .used = false, |
| 526 | | }) catch @panic("OOM"); |
| 527 | | }, |
| 528 | | else => { |
| 529 | | addUserInputOptionFromArg( |
| 530 | | arena, |
| 531 | | map, |
| 532 | | field_name, |
| 533 | | @Pointer(ptr_info.size, .{ .@"const" = true }, ptr_info.child, null), |
| 534 | | maybe_value orelse null, |
| 535 | | ); |
| 536 | | return; |
| 522 | var list: std.ArrayList([]const u8) = .empty; |
| 523 | const elems = list.addManyAsSlice(arena, v.len) catch @panic("OOM"); |
| 524 | for (elems, v) |*elem, tag| elem.* = @tagName(tag); |
| 525 | map.putAssumeCapacity(field_name, .{ .list = list }); |
| 537 | 526 | }, |
| 527 | else => return addPackageOptionFromArg( |
| 528 | arena, |
| 529 | map, |
| 530 | field_name, |
| 531 | @Pointer(ptr_info.size, .{ .@"const" = true }, ptr_info.child, null), |
| 532 | maybe_value orelse null, |
| 533 | ), |
| 538 | 534 | }, |
| 539 | 535 | else => {}, |
| 540 | 536 | }, |
| 541 | 537 | .null => unreachable, |
| 542 | 538 | .optional => |info| switch (@typeInfo(info.child)) { |
| 543 | 539 | .optional => {}, |
| 544 | | else => { |
| 545 | | addUserInputOptionFromArg( |
| 546 | | arena, |
| 547 | | map, |
| 548 | | field_name, |
| 549 | | info.child, |
| 550 | | maybe_value orelse null, |
| 551 | | ); |
| 552 | | return; |
| 553 | | }, |
| 540 | else => return addPackageOptionFromArg(arena, map, field_name, info.child, maybe_value orelse null), |
| 554 | 541 | }, |
| 555 | 542 | else => {}, |
| 556 | 543 | }, |
| ... | ... | @@ -558,130 +545,6 @@ fn addUserInputOptionFromArg( |
| 558 | 545 | @compileError("option '" ++ field_name ++ "' has unsupported type: " ++ @typeName(T)); |
| 559 | 546 | } |
| 560 | 547 | |
| 561 | | const OrderedUserValue = union(enum) { |
| 562 | | flag: void, |
| 563 | | scalar: []const u8, |
| 564 | | list: std.array_list.Managed([]const u8), |
| 565 | | map: std.array_list.Managed(Pair), |
| 566 | | lazy_path: LazyPath, |
| 567 | | lazy_path_list: std.array_list.Managed(LazyPath), |
| 568 | | |
| 569 | | const Pair = struct { |
| 570 | | name: []const u8, |
| 571 | | value: OrderedUserValue, |
| 572 | | fn lessThan(_: void, lhs: Pair, rhs: Pair) bool { |
| 573 | | return std.ascii.lessThanIgnoreCase(lhs.name, rhs.name); |
| 574 | | } |
| 575 | | }; |
| 576 | | |
| 577 | | fn hash(val: OrderedUserValue, hasher: *std.hash.Wyhash) void { |
| 578 | | hasher.update(&std.mem.toBytes(std.meta.activeTag(val))); |
| 579 | | switch (val) { |
| 580 | | .flag => {}, |
| 581 | | .scalar => |scalar| hasher.update(scalar), |
| 582 | | // lists are already ordered |
| 583 | | .list => |list| for (list.items) |list_entry| |
| 584 | | hasher.update(list_entry), |
| 585 | | .map => |map| for (map.items) |map_entry| { |
| 586 | | hasher.update(map_entry.name); |
| 587 | | map_entry.value.hash(hasher); |
| 588 | | }, |
| 589 | | .lazy_path => |lp| hashLazyPath(lp, hasher), |
| 590 | | .lazy_path_list => |lp_list| for (lp_list.items) |lp| { |
| 591 | | hashLazyPath(lp, hasher); |
| 592 | | }, |
| 593 | | } |
| 594 | | } |
| 595 | | |
| 596 | | fn hashLazyPath(lp: LazyPath, hasher: *std.hash.Wyhash) void { |
| 597 | | switch (lp) { |
| 598 | | .src_path => |sp| { |
| 599 | | hasher.update(sp.owner.pkg_hash); |
| 600 | | hasher.update(sp.sub_path); |
| 601 | | }, |
| 602 | | .generated => |gen| { |
| 603 | | hasher.update(@ptrCast(&gen.index)); |
| 604 | | hasher.update(@ptrCast(&gen.up)); |
| 605 | | hasher.update(gen.sub_path); |
| 606 | | }, |
| 607 | | .cwd_relative => |rel_path| { |
| 608 | | hasher.update(rel_path); |
| 609 | | }, |
| 610 | | .relative => |r| { |
| 611 | | hasher.update(@ptrCast(&r.base)); |
| 612 | | hasher.update(@ptrCast(&r.sub_path)); |
| 613 | | }, |
| 614 | | .dependency => |dep| { |
| 615 | | hasher.update(dep.dependency.builder.pkg_hash); |
| 616 | | hasher.update(dep.sub_path); |
| 617 | | }, |
| 618 | | } |
| 619 | | } |
| 620 | | |
| 621 | | fn mapFromUnordered(allocator: Allocator, unordered: std.StringHashMap(*const UserValue)) std.array_list.Managed(Pair) { |
| 622 | | var ordered = std.array_list.Managed(Pair).init(allocator); |
| 623 | | var it = unordered.iterator(); |
| 624 | | while (it.next()) |entry| { |
| 625 | | ordered.append(.{ |
| 626 | | .name = entry.key_ptr.*, |
| 627 | | .value = OrderedUserValue.fromUnordered(allocator, entry.value_ptr.*.*), |
| 628 | | }) catch @panic("OOM"); |
| 629 | | } |
| 630 | | |
| 631 | | std.mem.sortUnstable(Pair, ordered.items, {}, Pair.lessThan); |
| 632 | | return ordered; |
| 633 | | } |
| 634 | | |
| 635 | | fn fromUnordered(allocator: Allocator, unordered: UserValue) OrderedUserValue { |
| 636 | | return switch (unordered) { |
| 637 | | .flag => .{ .flag = {} }, |
| 638 | | .scalar => |scalar| .{ .scalar = scalar }, |
| 639 | | .list => |list| .{ .list = list }, |
| 640 | | .map => |map| .{ .map = OrderedUserValue.mapFromUnordered(allocator, map) }, |
| 641 | | .lazy_path => |lp| .{ .lazy_path = lp }, |
| 642 | | .lazy_path_list => |list| .{ .lazy_path_list = list }, |
| 643 | | }; |
| 644 | | } |
| 645 | | }; |
| 646 | | |
| 647 | | const OrderedUserInputOption = struct { |
| 648 | | name: []const u8, |
| 649 | | value: OrderedUserValue, |
| 650 | | used: bool, |
| 651 | | |
| 652 | | fn hash(opt: OrderedUserInputOption, hasher: *std.hash.Wyhash) void { |
| 653 | | hasher.update(opt.name); |
| 654 | | opt.value.hash(hasher); |
| 655 | | } |
| 656 | | |
| 657 | | fn fromUnordered(allocator: Allocator, user_input_option: UserInputOption) OrderedUserInputOption { |
| 658 | | return OrderedUserInputOption{ |
| 659 | | .name = user_input_option.name, |
| 660 | | .used = user_input_option.used, |
| 661 | | .value = OrderedUserValue.fromUnordered(allocator, user_input_option.value), |
| 662 | | }; |
| 663 | | } |
| 664 | | |
| 665 | | fn lessThan(_: void, lhs: OrderedUserInputOption, rhs: OrderedUserInputOption) bool { |
| 666 | | return std.ascii.lessThanIgnoreCase(lhs.name, rhs.name); |
| 667 | | } |
| 668 | | }; |
| 669 | | |
| 670 | | // The hash should be consistent with the same values given a different order. |
| 671 | | // This function takes a user input map, orders it, then hashes the contents. |
| 672 | | fn hashUserInputOptionsMap(allocator: Allocator, user_input_options: UserInputOptionsMap, hasher: *std.hash.Wyhash) void { |
| 673 | | var ordered = std.array_list.Managed(OrderedUserInputOption).init(allocator); |
| 674 | | var it = user_input_options.iterator(); |
| 675 | | while (it.next()) |entry| |
| 676 | | ordered.append(OrderedUserInputOption.fromUnordered(allocator, entry.value_ptr.*)) catch @panic("OOM"); |
| 677 | | |
| 678 | | std.mem.sortUnstable(OrderedUserInputOption, ordered.items, {}, OrderedUserInputOption.lessThan); |
| 679 | | |
| 680 | | // juice it |
| 681 | | for (ordered.items) |user_option| |
| 682 | | user_option.hash(hasher); |
| 683 | | } |
| 684 | | |
| 685 | 548 | /// Create a set of key-value pairs that can be converted into a Zig source |
| 686 | 549 | /// file and then inserted into a Zig compilation's module table for importing. |
| 687 | 550 | /// |
| ... | ... | @@ -1107,31 +970,20 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw |
| 1107 | 970 | const name = graph.dupeString(name_raw); |
| 1108 | 971 | const description = graph.dupeString(description_raw); |
| 1109 | 972 | const type_id = comptime typeToEnum(T); |
| 1110 | | const enum_options = if (type_id == .@"enum" or type_id == .enum_list) blk: { |
| 1111 | | const EnumType = if (type_id == .enum_list) @typeInfo(T).pointer.child else T; |
| 1112 | | const field_names = @typeInfo(EnumType).@"enum".field_names; |
| 1113 | | var options = std.array_list.Managed([]const u8).initCapacity(b.allocator, field_names.len) catch @panic("OOM"); |
| 1114 | | |
| 1115 | | inline for (field_names) |field_name| { |
| 1116 | | options.appendAssumeCapacity(field_name); |
| 1117 | | } |
| 1118 | | |
| 1119 | | break :blk options.toOwnedSlice() catch @panic("OOM"); |
| 1120 | | } else null; |
| 1121 | | const available_option = AvailableOption{ |
| 1122 | | .name = name, |
| 973 | const available_option: AvailableOption = .{ |
| 1123 | 974 | .type_id = type_id, |
| 1124 | 975 | .description = description, |
| 1125 | | .enum_options = enum_options, |
| 976 | .enum_options = if (type_id == .@"enum" or type_id == .enum_list) blk: { |
| 977 | const E = if (type_id == .enum_list) @typeInfo(T).pointer.child else T; |
| 978 | break :blk @typeInfo(E).@"enum".field_names; |
| 979 | } else null, |
| 1126 | 980 | }; |
| 1127 | 981 | if ((b.available_options_map.fetchPut(arena, name, available_option) catch @panic("OOM")) != null) { |
| 1128 | 982 | panic("option {q} declared twice", .{name}); |
| 1129 | 983 | } |
| 1130 | | |
| 1131 | | const option_ptr = b.user_input_options.getPtr(name) orelse return null; |
| 1132 | | option_ptr.used = true; |
| 984 | const user_provided = b.user_input_options.get(name) orelse return null; |
| 1133 | 985 | switch (type_id) { |
| 1134 | | .bool => switch (option_ptr.value) { |
| 986 | .bool => switch (user_provided) { |
| 1135 | 987 | .flag => return true, |
| 1136 | 988 | .scalar => |s| { |
| 1137 | 989 | if (mem.eql(u8, s, "true")) { |
| ... | ... | @@ -1145,14 +997,14 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw |
| 1145 | 997 | } |
| 1146 | 998 | }, |
| 1147 | 999 | .list, .map, .lazy_path, .lazy_path_list => { |
| 1148 | | log.err("expected -D{s} to be a boolean; received: {t}", .{ name, option_ptr.value }); |
| 1000 | log.err("expected -D{s} to be a boolean; received: {t}", .{ name, user_provided }); |
| 1149 | 1001 | b.markInvalidUserInput(); |
| 1150 | 1002 | return null; |
| 1151 | 1003 | }, |
| 1152 | 1004 | }, |
| 1153 | | .int => switch (option_ptr.value) { |
| 1005 | .int => switch (user_provided) { |
| 1154 | 1006 | .flag, .list, .map, .lazy_path, .lazy_path_list => { |
| 1155 | | log.err("expected -D{s} to be an integer; received: {t}", .{ name, option_ptr.value }); |
| 1007 | log.err("expected -D{s} to be an integer; received: {t}", .{ name, user_provided }); |
| 1156 | 1008 | b.markInvalidUserInput(); |
| 1157 | 1009 | return null; |
| 1158 | 1010 | }, |
| ... | ... | @@ -1172,9 +1024,9 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw |
| 1172 | 1024 | return n; |
| 1173 | 1025 | }, |
| 1174 | 1026 | }, |
| 1175 | | .float => switch (option_ptr.value) { |
| 1027 | .float => switch (user_provided) { |
| 1176 | 1028 | .flag, .map, .list, .lazy_path, .lazy_path_list => { |
| 1177 | | log.err("expected -D{s} to be a float; received: {t}", .{ name, option_ptr.value }); |
| 1029 | log.err("expected -D{s} to be a float; received: {t}", .{ name, user_provided }); |
| 1178 | 1030 | b.markInvalidUserInput(); |
| 1179 | 1031 | return null; |
| 1180 | 1032 | }, |
| ... | ... | @@ -1187,9 +1039,9 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw |
| 1187 | 1039 | return n; |
| 1188 | 1040 | }, |
| 1189 | 1041 | }, |
| 1190 | | .@"enum" => switch (option_ptr.value) { |
| 1042 | .@"enum" => switch (user_provided) { |
| 1191 | 1043 | .flag, .map, .list, .lazy_path, .lazy_path_list => { |
| 1192 | | log.err("expected -D{s} to be an enum; received: {t}.", .{ name, option_ptr.value }); |
| 1044 | log.err("expected -D{s} to be an enum; received: {t}.", .{ name, user_provided }); |
| 1193 | 1045 | b.markInvalidUserInput(); |
| 1194 | 1046 | return null; |
| 1195 | 1047 | }, |
| ... | ... | @@ -1206,17 +1058,17 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw |
| 1206 | 1058 | return null; |
| 1207 | 1059 | }, |
| 1208 | 1060 | }, |
| 1209 | | .string => switch (option_ptr.value) { |
| 1061 | .string => switch (user_provided) { |
| 1210 | 1062 | .flag, .list, .map, .lazy_path, .lazy_path_list => { |
| 1211 | | log.err("expected -D{s} to be a string; received: {t}", .{ name, option_ptr.value }); |
| 1063 | log.err("expected -D{s} to be a string; received: {t}", .{ name, user_provided }); |
| 1212 | 1064 | b.markInvalidUserInput(); |
| 1213 | 1065 | return null; |
| 1214 | 1066 | }, |
| 1215 | 1067 | .scalar => |s| return s, |
| 1216 | 1068 | }, |
| 1217 | | .build_id => switch (option_ptr.value) { |
| 1069 | .build_id => switch (user_provided) { |
| 1218 | 1070 | .flag, .map, .list, .lazy_path, .lazy_path_list => { |
| 1219 | | log.err("expected -D{s} to be an enum; received: {t}.", .{ name, option_ptr.value }); |
| 1071 | log.err("expected -D{s} to be an enum; received: {t}.", .{ name, user_provided }); |
| 1220 | 1072 | b.markInvalidUserInput(); |
| 1221 | 1073 | return null; |
| 1222 | 1074 | }, |
| ... | ... | @@ -1230,9 +1082,9 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw |
| 1230 | 1082 | } |
| 1231 | 1083 | }, |
| 1232 | 1084 | }, |
| 1233 | | .list => switch (option_ptr.value) { |
| 1085 | .list => switch (user_provided) { |
| 1234 | 1086 | .flag, .map, .lazy_path, .lazy_path_list => { |
| 1235 | | log.err("expected -D{s} to be a list; received: {t}", .{ name, option_ptr.value }); |
| 1087 | log.err("expected -D{s} to be a list; received: {t}", .{ name, user_provided }); |
| 1236 | 1088 | b.markInvalidUserInput(); |
| 1237 | 1089 | return null; |
| 1238 | 1090 | }, |
| ... | ... | @@ -1241,9 +1093,9 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw |
| 1241 | 1093 | }, |
| 1242 | 1094 | .list => |lst| return lst.items, |
| 1243 | 1095 | }, |
| 1244 | | .enum_list => switch (option_ptr.value) { |
| 1096 | .enum_list => switch (user_provided) { |
| 1245 | 1097 | .flag, .map, .lazy_path, .lazy_path_list => { |
| 1246 | | log.err("expected -D{s} to be a list; received: {t}", .{ name, option_ptr.value }); |
| 1098 | log.err("expected -D{s} to be a list; received: {t}", .{ name, user_provided }); |
| 1247 | 1099 | b.markInvalidUserInput(); |
| 1248 | 1100 | return null; |
| 1249 | 1101 | }, |
| ... | ... | @@ -1283,16 +1135,16 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw |
| 1283 | 1135 | return new_list; |
| 1284 | 1136 | }, |
| 1285 | 1137 | }, |
| 1286 | | .lazy_path => switch (option_ptr.value) { |
| 1138 | .lazy_path => switch (user_provided) { |
| 1287 | 1139 | .scalar => |s| return .{ .cwd_relative = s }, |
| 1288 | 1140 | .lazy_path => |lp| return lp, |
| 1289 | 1141 | .flag, .map, .list, .lazy_path_list => { |
| 1290 | | log.err("expected -D{s} to be a path; received: {t}", .{ name, option_ptr.value }); |
| 1142 | log.err("expected -D{s} to be a path; received: {t}", .{ name, user_provided }); |
| 1291 | 1143 | b.markInvalidUserInput(); |
| 1292 | 1144 | return null; |
| 1293 | 1145 | }, |
| 1294 | 1146 | }, |
| 1295 | | .lazy_path_list => switch (option_ptr.value) { |
| 1147 | .lazy_path_list => switch (user_provided) { |
| 1296 | 1148 | .scalar => |s| return arena.dupe(LazyPath, &[_]LazyPath{.{ .cwd_relative = s }}) catch @panic("OOM"), |
| 1297 | 1149 | .lazy_path => |lp| return arena.dupe(LazyPath, &[_]LazyPath{lp}) catch @panic("OOM"), |
| 1298 | 1150 | .list => |lst| { |
| ... | ... | @@ -1304,7 +1156,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw |
| 1304 | 1156 | }, |
| 1305 | 1157 | .lazy_path_list => |lp_list| return lp_list.items, |
| 1306 | 1158 | .flag, .map => { |
| 1307 | | log.err("expected -D{s} to be a path; received: {t}", .{ name, option_ptr.value }); |
| 1159 | log.err("expected -D{s} to be a path; received: {t}", .{ name, user_provided }); |
| 1308 | 1160 | b.markInvalidUserInput(); |
| 1309 | 1161 | return null; |
| 1310 | 1162 | }, |
| ... | ... | @@ -1497,88 +1349,63 @@ pub fn standardTargetOptionsQueryOnly(b: *Build, args: StandardTargetOptionsArgs |
| 1497 | 1349 | } |
| 1498 | 1350 | |
| 1499 | 1351 | /// Build system implementation detail. |
| 1500 | | pub fn addUserInputOption(b: *Build, name_raw: []const u8, value_raw: []const u8) error{OutOfMemory}!bool { |
| 1352 | pub fn addUserInputOption(b: *Build, name: []const u8, value_raw: []const u8) error{OutOfMemory}!bool { |
| 1501 | 1353 | const graph = b.graph; |
| 1502 | 1354 | const arena = graph.arena; |
| 1503 | | const name = graph.dupeString(name_raw); |
| 1504 | 1355 | const value = graph.dupeString(value_raw); |
| 1505 | | const gop = try b.user_input_options.getOrPut(name); |
| 1356 | const gop = try b.user_input_options.getOrPut(arena, name); |
| 1357 | |
| 1506 | 1358 | if (!gop.found_existing) { |
| 1507 | | gop.value_ptr.* = UserInputOption{ |
| 1508 | | .name = name, |
| 1509 | | .value = .{ .scalar = value }, |
| 1510 | | .used = false, |
| 1511 | | }; |
| 1359 | gop.key_ptr.* = graph.dupeString(name); |
| 1360 | gop.value_ptr.* = .{ .scalar = value }; |
| 1512 | 1361 | return false; |
| 1513 | 1362 | } |
| 1514 | 1363 | |
| 1515 | | // option already exists |
| 1516 | | switch (gop.value_ptr.value) { |
| 1364 | // Option already exists. |
| 1365 | switch (gop.value_ptr.*) { |
| 1517 | 1366 | .scalar => |s| { |
| 1518 | | // turn it into a list |
| 1519 | | var list = std.array_list.Managed([]const u8).init(arena); |
| 1520 | | try list.append(s); |
| 1521 | | try list.append(value); |
| 1522 | | try b.user_input_options.put(name, .{ |
| 1523 | | .name = name, |
| 1524 | | .value = .{ .list = list }, |
| 1525 | | .used = false, |
| 1526 | | }); |
| 1527 | | }, |
| 1528 | | .list => |*list| { |
| 1529 | | // append to the list |
| 1530 | | try list.append(value); |
| 1531 | | try b.user_input_options.put(name, .{ |
| 1532 | | .name = name, |
| 1533 | | .value = .{ .list = list.* }, |
| 1534 | | .used = false, |
| 1535 | | }); |
| 1367 | // Turn it into a list. |
| 1368 | var list: std.ArrayList([]const u8) = .empty; |
| 1369 | (try list.addManyAsArray(arena, 2)).* = .{ s, value }; |
| 1370 | gop.value_ptr.* = .{ .list = list }; |
| 1536 | 1371 | }, |
| 1372 | .list => |*list| try list.append(arena, value), |
| 1537 | 1373 | .flag => { |
| 1538 | | log.warn("option '-D{s}={s}' conflicts with flag '-D{s}'.", .{ name, value, name }); |
| 1374 | log.err("option -D{s}={s} conflicts with flag -D{s}", .{ name, value, name }); |
| 1539 | 1375 | return true; |
| 1540 | 1376 | }, |
| 1541 | 1377 | .map => |*map| { |
| 1542 | 1378 | _ = map; |
| 1543 | | log.warn("TODO maps as command line arguments is not implemented yet.", .{}); |
| 1544 | | return true; |
| 1545 | | }, |
| 1546 | | .lazy_path, .lazy_path_list => { |
| 1547 | | log.warn("the lazy path value type isn't added from the CLI, but somehow {q} is a .{f}", .{ |
| 1548 | | name, std.zig.fmtId(@tagName(gop.value_ptr.value)), |
| 1549 | | }); |
| 1550 | | return true; |
| 1379 | unreachable; // TODO implement maps as command line arguments |
| 1551 | 1380 | }, |
| 1381 | .lazy_path => unreachable, |
| 1382 | .lazy_path_list => unreachable, |
| 1552 | 1383 | } |
| 1553 | 1384 | return false; |
| 1554 | 1385 | } |
| 1555 | 1386 | |
| 1556 | 1387 | /// Build system implementation detail. |
| 1557 | | pub fn addUserInputFlag(b: *Build, name_raw: []const u8) error{OutOfMemory}!bool { |
| 1388 | pub fn addUserInputFlag(b: *Build, name: []const u8) error{OutOfMemory}!bool { |
| 1558 | 1389 | const graph = b.graph; |
| 1559 | | const name = graph.dupeString(name_raw); |
| 1560 | | const gop = try b.user_input_options.getOrPut(name); |
| 1390 | const arena = graph.arena; |
| 1391 | const gop = try b.user_input_options.getOrPut(arena, name); |
| 1561 | 1392 | if (!gop.found_existing) { |
| 1562 | | gop.value_ptr.* = .{ |
| 1563 | | .name = name, |
| 1564 | | .value = .{ .flag = {} }, |
| 1565 | | .used = false, |
| 1566 | | }; |
| 1393 | gop.key_ptr.* = graph.dupeString(name); |
| 1394 | gop.value_ptr.* = .{ .flag = {} }; |
| 1567 | 1395 | return false; |
| 1568 | 1396 | } |
| 1569 | | |
| 1570 | | // option already exists |
| 1571 | | switch (gop.value_ptr.value) { |
| 1397 | // Option already exists. |
| 1398 | switch (gop.value_ptr.*) { |
| 1572 | 1399 | .scalar => |s| { |
| 1573 | | log.err("Flag '-D{s}' conflicts with option '-D{s}={s}'.", .{ name, name, s }); |
| 1400 | log.err("flag -D{s} conflicts with option -D{s}={s}", .{ name, name, s }); |
| 1574 | 1401 | return true; |
| 1575 | 1402 | }, |
| 1576 | 1403 | .list, .map, .lazy_path_list => { |
| 1577 | | log.err("Flag '-D{s}' conflicts with multiple options of the same name.", .{name}); |
| 1404 | log.err("flag -D{s} conflicts with multiple options of the same name", .{name}); |
| 1578 | 1405 | return true; |
| 1579 | 1406 | }, |
| 1580 | 1407 | .lazy_path => |lp| { |
| 1581 | | log.err("Flag '-D{s}' conflicts with option '-D{s}={f}'.", .{ name, name, lp }); |
| 1408 | log.err("flag -D{s} conflicts with option -D{s}={f}", .{ name, name, lp }); |
| 1582 | 1409 | return true; |
| 1583 | 1410 | }, |
| 1584 | 1411 | |
| ... | ... | @@ -1614,17 +1441,16 @@ fn markInvalidUserInput(b: *Build) void { |
| 1614 | 1441 | b.invalid_user_input = true; |
| 1615 | 1442 | } |
| 1616 | 1443 | |
| 1617 | | /// Build system implementation detail. |
| 1618 | | pub fn validateUserInputDidItFail(b: *Build) bool { |
| 1619 | | // Make sure all args are used. |
| 1620 | | var it = b.user_input_options.iterator(); |
| 1621 | | while (it.next()) |entry| { |
| 1622 | | if (!entry.value_ptr.used) { |
| 1623 | | log.err("invalid option: -D{s}", .{entry.key_ptr.*}); |
| 1444 | fn validateUserInputDidItFail(b: *Build) bool { |
| 1445 | for (b.user_input_options.keys()) |name| { |
| 1446 | if (!b.available_options_map.contains(name)) { |
| 1447 | for (b.available_options_map.keys(), b.available_options_map.values()) |available_name, *available| { |
| 1448 | log.info("available option: {q}: {s}", .{ available_name, available.description }); |
| 1449 | } |
| 1450 | log.err("invalid option: {q}", .{name}); |
| 1624 | 1451 | b.markInvalidUserInput(); |
| 1625 | 1452 | } |
| 1626 | 1453 | } |
| 1627 | | |
| 1628 | 1454 | return b.invalid_user_input; |
| 1629 | 1455 | } |
| 1630 | 1456 | |
| ... | ... | @@ -2092,14 +1918,14 @@ inline fn findImportPkgHashOrFatal(b: *Build, comptime asking_build_zig: type, c |
| 2092 | 1918 | const pkg = @field(deps.packages, pkg_hash); |
| 2093 | 1919 | if (@hasDecl(pkg, "build_zig") and pkg.build_zig == asking_build_zig) break .{ pkg_hash, pkg.deps }; |
| 2094 | 1920 | } else .{ "", deps.root_deps }; |
| 2095 | | if (!std.mem.eql(u8, b_pkg_hash, b.pkg_hash)) { |
| 1921 | if (!mem.eql(u8, b_pkg_hash, b.pkg_hash)) { |
| 2096 | 1922 | const build_zig_path = b.root.join(arena, "build.zig") catch @panic("OOM"); |
| 2097 | 1923 | panic("{} is not the struct that corresponds to {f}", .{ |
| 2098 | 1924 | asking_build_zig, build_zig_path, |
| 2099 | 1925 | }); |
| 2100 | 1926 | } |
| 2101 | 1927 | comptime for (b_pkg_deps) |dep| { |
| 2102 | | if (std.mem.eql(u8, dep[0], dep_name)) return dep[1]; |
| 1928 | if (mem.eql(u8, dep[0], dep_name)) return dep[1]; |
| 2103 | 1929 | }; |
| 2104 | 1930 | |
| 2105 | 1931 | const full_path = b.root.join(arena, "build.zig.zon") catch @panic("OOM"); |
| ... | ... | @@ -2141,7 +1967,9 @@ pub fn dependencyLazy(b: *Build, name: []const u8, args: anytype) error{LazyDepe |
| 2141 | 1967 | markNeededLazyDep(b, pkg_hash); |
| 2142 | 1968 | return error.LazyDependencyNeeded; |
| 2143 | 1969 | } |
| 2144 | | return dependencyResolved(b, name, entry, userInputOptionsFromArgs(b.graph.arena, args)); |
| 1970 | var map: PackageOptions.Map = .empty; |
| 1971 | PackageOptions.fromArgs(b.graph.arena, &map, args); |
| 1972 | return dependencyResolved(b, name, entry, &map); |
| 2145 | 1973 | } |
| 2146 | 1974 | |
| 2147 | 1975 | pub const PackageEntry = struct { |
| ... | ... | @@ -2232,12 +2060,14 @@ pub inline fn lazyImport( |
| 2232 | 2060 | comptime unreachable; // Bad @dependencies source |
| 2233 | 2061 | } |
| 2234 | 2062 | |
| 2235 | | fn pkgHashFromBuildZig(comptime build_zig: type) ?[]const u8 { |
| 2236 | | const deps = @import("root").dependencies; |
| 2237 | | return comptime for (@typeInfo(deps.packages).@"struct".decl_names) |pkg_hash| { |
| 2238 | | const pkg = @field(deps.packages, pkg_hash); |
| 2239 | | if (@hasDecl(pkg, "build_zig") and pkg.build_zig == build_zig) break pkg_hash; |
| 2240 | | } else null; |
| 2063 | inline fn pkgHashFromBuildZig(comptime build_zig: type) ?[]const u8 { |
| 2064 | comptime { |
| 2065 | const deps = @import("root").dependencies; |
| 2066 | return for (@typeInfo(deps.packages).@"struct".decl_names) |pkg_hash| { |
| 2067 | const pkg = @field(deps.packages, pkg_hash); |
| 2068 | if (@hasDecl(pkg, "build_zig") and pkg.build_zig == build_zig) break pkg_hash; |
| 2069 | } else null; |
| 2070 | } |
| 2241 | 2071 | } |
| 2242 | 2072 | |
| 2243 | 2073 | /// Build system implementation detail. |
| ... | ... | @@ -2251,114 +2081,37 @@ pub fn dependencyFromBuildZig( |
| 2251 | 2081 | const arena = b.graph.arena; |
| 2252 | 2082 | |
| 2253 | 2083 | find_dep: { |
| 2254 | | const pkg_hash = comptime pkgHashFromBuildZig(build_zig) orelse break :find_dep; |
| 2084 | const pkg_hash = pkgHashFromBuildZig(build_zig) orelse break :find_dep; |
| 2255 | 2085 | const dep_name = for (b.available_deps) |dep| { |
| 2256 | 2086 | if (mem.eql(u8, dep[1], pkg_hash)) break dep[1]; |
| 2257 | 2087 | } else break :find_dep; |
| 2258 | 2088 | const entry = package_map.get(pkg_hash) orelse break :find_dep; |
| 2259 | | return dependencyResolved(b, dep_name, entry, userInputOptionsFromArgs(arena, args)); |
| 2089 | var map: PackageOptions.Map = .empty; |
| 2090 | PackageOptions.fromArgs(arena, &map, args); |
| 2091 | return dependencyResolved(b, dep_name, entry, &map); |
| 2260 | 2092 | } |
| 2261 | 2093 | |
| 2262 | 2094 | const full_path = b.root.join(arena, "build.zig.zon") catch @panic("OOM"); |
| 2263 | 2095 | panic("{} is not a build.zig struct of a dependency in {f}", .{ build_zig, full_path }); |
| 2264 | 2096 | } |
| 2265 | 2097 | |
| 2266 | | fn userValuesAreSame(lhs: UserValue, rhs: UserValue) bool { |
| 2267 | | if (std.meta.activeTag(lhs) != rhs) return false; |
| 2268 | | switch (lhs) { |
| 2269 | | .flag => {}, |
| 2270 | | .scalar => |lhs_scalar| { |
| 2271 | | const rhs_scalar = rhs.scalar; |
| 2272 | | |
| 2273 | | if (!std.mem.eql(u8, lhs_scalar, rhs_scalar)) |
| 2274 | | return false; |
| 2275 | | }, |
| 2276 | | .list => |lhs_list| { |
| 2277 | | const rhs_list = rhs.list; |
| 2278 | | |
| 2279 | | if (lhs_list.items.len != rhs_list.items.len) |
| 2280 | | return false; |
| 2281 | | |
| 2282 | | for (lhs_list.items, rhs_list.items) |lhs_list_entry, rhs_list_entry| { |
| 2283 | | if (!std.mem.eql(u8, lhs_list_entry, rhs_list_entry)) |
| 2284 | | return false; |
| 2285 | | } |
| 2286 | | }, |
| 2287 | | .map => |lhs_map| { |
| 2288 | | const rhs_map = rhs.map; |
| 2289 | | |
| 2290 | | if (lhs_map.count() != rhs_map.count()) |
| 2291 | | return false; |
| 2292 | | |
| 2293 | | var lhs_it = lhs_map.iterator(); |
| 2294 | | while (lhs_it.next()) |lhs_entry| { |
| 2295 | | const rhs_value = rhs_map.get(lhs_entry.key_ptr.*) orelse return false; |
| 2296 | | if (!userValuesAreSame(lhs_entry.value_ptr.*.*, rhs_value.*)) |
| 2297 | | return false; |
| 2298 | | } |
| 2299 | | }, |
| 2300 | | .lazy_path => |lhs_lp| { |
| 2301 | | const rhs_lp = rhs.lazy_path; |
| 2302 | | return userLazyPathsAreTheSame(lhs_lp, rhs_lp); |
| 2303 | | }, |
| 2304 | | .lazy_path_list => |lhs_lp_list| { |
| 2305 | | const rhs_lp_list = rhs.lazy_path_list; |
| 2306 | | if (lhs_lp_list.items.len != rhs_lp_list.items.len) return false; |
| 2307 | | for (lhs_lp_list.items, rhs_lp_list.items) |lhs_lp, rhs_lp| { |
| 2308 | | if (!userLazyPathsAreTheSame(lhs_lp, rhs_lp)) return false; |
| 2309 | | } |
| 2310 | | return true; |
| 2311 | | }, |
| 2312 | | } |
| 2313 | | |
| 2314 | | return true; |
| 2315 | | } |
| 2316 | | |
| 2317 | | fn userLazyPathsAreTheSame(lhs_lp: LazyPath, rhs_lp: LazyPath) bool { |
| 2318 | | if (std.meta.activeTag(lhs_lp) != rhs_lp) return false; |
| 2319 | | switch (lhs_lp) { |
| 2320 | | .src_path => |lhs_sp| { |
| 2321 | | const rhs_sp = rhs_lp.src_path; |
| 2322 | | |
| 2323 | | if (lhs_sp.owner != rhs_sp.owner) return false; |
| 2324 | | if (std.mem.eql(u8, lhs_sp.sub_path, rhs_sp.sub_path)) return false; |
| 2325 | | }, |
| 2326 | | .generated => |*lhs_gen| { |
| 2327 | | const rhs_gen = &rhs_lp.generated; |
| 2328 | | |
| 2329 | | if (lhs_gen.index != rhs_gen.index) return false; |
| 2330 | | if (lhs_gen.up != rhs_gen.up) return false; |
| 2331 | | if (std.mem.eql(u8, lhs_gen.sub_path, rhs_gen.sub_path)) return false; |
| 2332 | | }, |
| 2333 | | .cwd_relative => |lhs_rel_path| { |
| 2334 | | const rhs_rel_path = rhs_lp.cwd_relative; |
| 2335 | | |
| 2336 | | if (!std.mem.eql(u8, lhs_rel_path, rhs_rel_path)) return false; |
| 2337 | | }, |
| 2338 | | .relative => |lhs| return lhs.eql(rhs_lp.relative), |
| 2339 | | .dependency => |lhs_dep| { |
| 2340 | | const rhs_dep = rhs_lp.dependency; |
| 2341 | | |
| 2342 | | if (lhs_dep.dependency != rhs_dep.dependency) return false; |
| 2343 | | if (!std.mem.eql(u8, lhs_dep.sub_path, rhs_dep.sub_path)) return false; |
| 2344 | | }, |
| 2345 | | } |
| 2346 | | return true; |
| 2347 | | } |
| 2348 | | |
| 2098 | /// Takes ownership of `package_options`, which may be unsorted. |
| 2349 | 2099 | fn dependencyResolved( |
| 2350 | 2100 | b: *Build, |
| 2351 | 2101 | name: []const u8, |
| 2352 | 2102 | entry: PackageEntry, |
| 2353 | | user_input_options: UserInputOptionsMap, |
| 2103 | package_options: *PackageOptions.Map, |
| 2354 | 2104 | ) *Dependency { |
| 2355 | 2105 | const graph = b.graph; |
| 2356 | 2106 | const io = graph.io; |
| 2357 | 2107 | const arena = graph.arena; |
| 2108 | |
| 2109 | PackageOptions.sort(package_options); |
| 2110 | |
| 2358 | 2111 | if (graph.dependency_cache.getContext(.{ |
| 2359 | | .build_root_string = entry.build_root, |
| 2360 | | .user_input_options = user_input_options, |
| 2361 | | }, .{ .allocator = arena })) |dep| return dep; |
| 2112 | .pkg_hash = entry.hash, |
| 2113 | .options = package_options, |
| 2114 | }, .{})) |dep| return dep; |
| 2362 | 2115 | |
| 2363 | 2116 | const dep_root: Cache.Path = .{ |
| 2364 | 2117 | .root_dir = .{ |
| ... | ... | @@ -2368,7 +2121,7 @@ fn dependencyResolved( |
| 2368 | 2121 | }, |
| 2369 | 2122 | }; |
| 2370 | 2123 | |
| 2371 | | const sub_builder = b.createChild(name, dep_root, entry.hash, entry.deps, user_input_options) catch @panic("OOM"); |
| 2124 | const sub_builder = b.createChild(name, dep_root, entry.hash, entry.deps, package_options.*) catch @panic("OOM"); |
| 2372 | 2125 | if (entry.run_build) |run_build| { |
| 2373 | 2126 | run_build(sub_builder); |
| 2374 | 2127 | |
| ... | ... | @@ -2381,9 +2134,9 @@ fn dependencyResolved( |
| 2381 | 2134 | dep.* = .{ .builder = sub_builder }; |
| 2382 | 2135 | |
| 2383 | 2136 | graph.dependency_cache.putContext(arena, .{ |
| 2384 | | .build_root_string = entry.build_root, |
| 2385 | | .user_input_options = user_input_options, |
| 2386 | | }, dep, .{ .allocator = arena }) catch @panic("OOM"); |
| 2137 | .pkg_hash = entry.hash, |
| 2138 | .options = &sub_builder.user_input_options, |
| 2139 | }, dep, .{}) catch @panic("OOM"); |
| 2387 | 2140 | return dep; |
| 2388 | 2141 | } |
| 2389 | 2142 | |
| ... | ... | @@ -2650,6 +2403,59 @@ pub const LazyPath = union(enum) { |
| 2650 | 2403 | } }, |
| 2651 | 2404 | }; |
| 2652 | 2405 | } |
| 2406 | |
| 2407 | fn eql(a: LazyPath, b: LazyPath) bool { |
| 2408 | if (std.meta.activeTag(a) != b) return false; |
| 2409 | switch (a) { |
| 2410 | .src_path => |a_sp| { |
| 2411 | const b_sp = b.src_path; |
| 2412 | if (a_sp.owner != b_sp.owner) return false; |
| 2413 | if (mem.eql(u8, a_sp.sub_path, b_sp.sub_path)) return false; |
| 2414 | }, |
| 2415 | .generated => |*a_gen| { |
| 2416 | const b_gen = &b.generated; |
| 2417 | if (a_gen.index != b_gen.index) return false; |
| 2418 | if (a_gen.up != b_gen.up) return false; |
| 2419 | if (mem.eql(u8, a_gen.sub_path, b_gen.sub_path)) return false; |
| 2420 | }, |
| 2421 | .cwd_relative => |a_rel_path| { |
| 2422 | const b_rel_path = b.cwd_relative; |
| 2423 | if (!mem.eql(u8, a_rel_path, b_rel_path)) return false; |
| 2424 | }, |
| 2425 | .relative => |a_relative| return a_relative.eql(b.relative), |
| 2426 | .dependency => |a_dep| { |
| 2427 | const b_dep = b.dependency; |
| 2428 | if (a_dep.dependency != b_dep.dependency) return false; |
| 2429 | if (!mem.eql(u8, a_dep.sub_path, b_dep.sub_path)) return false; |
| 2430 | }, |
| 2431 | } |
| 2432 | return true; |
| 2433 | } |
| 2434 | |
| 2435 | fn hash(lp: LazyPath, hasher: *std.hash.Wyhash) void { |
| 2436 | switch (lp) { |
| 2437 | .src_path => |sp| { |
| 2438 | hasher.update(sp.owner.pkg_hash); |
| 2439 | hasher.update(sp.sub_path); |
| 2440 | }, |
| 2441 | .generated => |gen| { |
| 2442 | hasher.update(@ptrCast(&gen.index)); |
| 2443 | hasher.update(@ptrCast(&gen.up)); |
| 2444 | hasher.update(gen.sub_path); |
| 2445 | }, |
| 2446 | .cwd_relative => |rel_path| { |
| 2447 | hasher.update(rel_path); |
| 2448 | }, |
| 2449 | .relative => |r| { |
| 2450 | hasher.update(@ptrCast(&r.base)); |
| 2451 | hasher.update(@ptrCast(&r.sub_path)); |
| 2452 | }, |
| 2453 | .dependency => |dep| { |
| 2454 | hasher.update(dep.dependency.builder.pkg_hash); |
| 2455 | hasher.update(dep.sub_path); |
| 2456 | }, |
| 2457 | } |
| 2458 | } |
| 2653 | 2459 | }; |
| 2654 | 2460 | |
| 2655 | 2461 | fn dumpBadDirnameHelp( |