| author | |
| committer | |
| log | 7ee6dab39fac7aa12fa9fd952bb2bdc28d5eabe8 |
| tree | b97957e62077e78f6bf49836babcdb84b4ff772f |
| parent | 9a158c1dae531f2a4e5667569bed38c27cbd4d57 |
This reverts commit b461d07a5464aec86c533434dab0b58edfffb331.
After some discussion in the team, we've decided that this is too disruptive,
especially because the linker errors are less than helpful. That's a fixable
problem, so we might reconsider this in the future, but revert it for now.10 files changed, 46 insertions(+), 78 deletions(-)
build.zig-20| ... | ... | @@ -452,7 +452,6 @@ pub fn build(b: *std.Build) !void { |
| 452 | 452 | .desc = "Run the behavior tests", |
| 453 | 453 | .optimize_modes = optimization_modes, |
| 454 | 454 | .include_paths = &.{}, |
| 455 | .windows_libs = &.{}, | |
| 456 | 455 | .skip_single_threaded = skip_single_threaded, |
| 457 | 456 | .skip_non_native = skip_non_native, |
| 458 | 457 | .skip_freebsd = skip_freebsd, |
| ... | ... | @@ -475,7 +474,6 @@ pub fn build(b: *std.Build) !void { |
| 475 | 474 | .desc = "Run the @cImport tests", |
| 476 | 475 | .optimize_modes = optimization_modes, |
| 477 | 476 | .include_paths = &.{"test/c_import"}, |
| 478 | .windows_libs = &.{}, | |
| 479 | 477 | .skip_single_threaded = true, |
| 480 | 478 | .skip_non_native = skip_non_native, |
| 481 | 479 | .skip_freebsd = skip_freebsd, |
| ... | ... | @@ -496,7 +494,6 @@ pub fn build(b: *std.Build) !void { |
| 496 | 494 | .desc = "Run the compiler_rt tests", |
| 497 | 495 | .optimize_modes = optimization_modes, |
| 498 | 496 | .include_paths = &.{}, |
| 499 | .windows_libs = &.{}, | |
| 500 | 497 | .skip_single_threaded = true, |
| 501 | 498 | .skip_non_native = skip_non_native, |
| 502 | 499 | .skip_freebsd = skip_freebsd, |
| ... | ... | @@ -518,7 +515,6 @@ pub fn build(b: *std.Build) !void { |
| 518 | 515 | .desc = "Run the zigc tests", |
| 519 | 516 | .optimize_modes = optimization_modes, |
| 520 | 517 | .include_paths = &.{}, |
| 521 | .windows_libs = &.{}, | |
| 522 | 518 | .skip_single_threaded = true, |
| 523 | 519 | .skip_non_native = skip_non_native, |
| 524 | 520 | .skip_freebsd = skip_freebsd, |
| ... | ... | @@ -540,12 +536,6 @@ pub fn build(b: *std.Build) !void { |
| 540 | 536 | .desc = "Run the standard library tests", |
| 541 | 537 | .optimize_modes = optimization_modes, |
| 542 | 538 | .include_paths = &.{}, |
| 543 | .windows_libs = &.{ | |
| 544 | "advapi32", | |
| 545 | "crypt32", | |
| 546 | "iphlpapi", | |
| 547 | "ws2_32", | |
| 548 | }, | |
| 549 | 539 | .skip_single_threaded = skip_single_threaded, |
| 550 | 540 | .skip_non_native = skip_non_native, |
| 551 | 541 | .skip_freebsd = skip_freebsd, |
| ... | ... | @@ -743,12 +733,6 @@ fn addCompilerMod(b: *std.Build, options: AddCompilerModOptions) *std.Build.Modu |
| 743 | 733 | compiler_mod.addImport("aro", aro_mod); |
| 744 | 734 | compiler_mod.addImport("aro_translate_c", aro_translate_c_mod); |
| 745 | 735 | |
| 746 | if (options.target.result.os.tag == .windows) { | |
| 747 | compiler_mod.linkSystemLibrary("advapi32", .{}); | |
| 748 | compiler_mod.linkSystemLibrary("crypt32", .{}); | |
| 749 | compiler_mod.linkSystemLibrary("ws2_32", .{}); | |
| 750 | } | |
| 751 | ||
| 752 | 736 | return compiler_mod; |
| 753 | 737 | } |
| 754 | 738 | |
| ... | ... | @@ -1446,10 +1430,6 @@ fn generateLangRef(b: *std.Build) std.Build.LazyPath { |
| 1446 | 1430 | }), |
| 1447 | 1431 | }); |
| 1448 | 1432 | |
| 1449 | if (b.graph.host.result.os.tag == .windows) { | |
| 1450 | doctest_exe.root_module.linkSystemLibrary("advapi32", .{}); | |
| 1451 | } | |
| 1452 | ||
| 1453 | 1433 | var dir = b.build_root.handle.openDir("doc/langref", .{ .iterate = true }) catch |err| { |
| 1454 | 1434 | std.debug.panic("unable to open '{f}doc/langref' directory: {s}", .{ |
| 1455 | 1435 | b.build_root, @errorName(err), |
src/Compilation.zig+29-10| ... | ... | @@ -2185,8 +2185,12 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 2185 | 2185 | .emit_docs = try options.emit_docs.resolve(arena, &options, .docs), |
| 2186 | 2186 | }; |
| 2187 | 2187 | |
| 2188 | comp.windows_libs = try std.StringArrayHashMapUnmanaged(void).init(gpa, options.windows_lib_names, &.{}); | |
| 2189 | errdefer comp.windows_libs.deinit(gpa); | |
| 2188 | errdefer { | |
| 2189 | for (comp.windows_libs.keys()) |windows_lib| gpa.free(windows_lib); | |
| 2190 | comp.windows_libs.deinit(gpa); | |
| 2191 | } | |
| 2192 | try comp.windows_libs.ensureUnusedCapacity(gpa, options.windows_lib_names.len); | |
| 2193 | for (options.windows_lib_names) |windows_lib| comp.windows_libs.putAssumeCapacity(try gpa.dupe(u8, windows_lib), {}); | |
| 2190 | 2194 | |
| 2191 | 2195 | // Prevent some footguns by making the "any" fields of config reflect |
| 2192 | 2196 | // the default Module settings. |
| ... | ... | @@ -2417,13 +2421,6 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 2417 | 2421 | |
| 2418 | 2422 | if (comp.emit_bin != null and target.ofmt != .c) { |
| 2419 | 2423 | if (!comp.skip_linker_dependencies) { |
| 2420 | // These DLLs are always loaded into every Windows process. | |
| 2421 | if (target.os.tag == .windows and is_exe_or_dyn_lib) { | |
| 2422 | try comp.windows_libs.ensureUnusedCapacity(gpa, 2); | |
| 2423 | comp.windows_libs.putAssumeCapacity("kernel32", {}); | |
| 2424 | comp.windows_libs.putAssumeCapacity("ntdll", {}); | |
| 2425 | } | |
| 2426 | ||
| 2427 | 2424 | // If we need to build libc for the target, add work items for it. |
| 2428 | 2425 | // We go through the work queue so that building can be done in parallel. |
| 2429 | 2426 | // If linking against host libc installation, instead queue up jobs |
| ... | ... | @@ -2512,7 +2509,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 2512 | 2509 | |
| 2513 | 2510 | // When linking mingw-w64 there are some import libs we always need. |
| 2514 | 2511 | try comp.windows_libs.ensureUnusedCapacity(gpa, mingw.always_link_libs.len); |
| 2515 | for (mingw.always_link_libs) |name| comp.windows_libs.putAssumeCapacity(name, {}); | |
| 2512 | for (mingw.always_link_libs) |name| comp.windows_libs.putAssumeCapacity(try gpa.dupe(u8, name), {}); | |
| 2516 | 2513 | } else { |
| 2517 | 2514 | return error.LibCUnavailable; |
| 2518 | 2515 | } |
| ... | ... | @@ -2610,6 +2607,7 @@ pub fn destroy(comp: *Compilation) void { |
| 2610 | 2607 | comp.c_object_work_queue.deinit(); |
| 2611 | 2608 | comp.win32_resource_work_queue.deinit(); |
| 2612 | 2609 | |
| 2610 | for (comp.windows_libs.keys()) |windows_lib| gpa.free(windows_lib); | |
| 2613 | 2611 | comp.windows_libs.deinit(gpa); |
| 2614 | 2612 | |
| 2615 | 2613 | { |
| ... | ... | @@ -7795,6 +7793,27 @@ fn getCrtPathsInner( |
| 7795 | 7793 | }; |
| 7796 | 7794 | } |
| 7797 | 7795 | |
| 7796 | pub fn addLinkLib(comp: *Compilation, lib_name: []const u8) !void { | |
| 7797 | // Avoid deadlocking on building import libs such as kernel32.lib | |
| 7798 | // This can happen when the user uses `build-exe foo.obj -lkernel32` and | |
| 7799 | // then when we create a sub-Compilation for zig libc, it also tries to | |
| 7800 | // build kernel32.lib. | |
| 7801 | if (comp.skip_linker_dependencies) return; | |
| 7802 | const target = &comp.root_mod.resolved_target.result; | |
| 7803 | if (target.os.tag != .windows or target.ofmt == .c) return; | |
| 7804 | ||
| 7805 | // This happens when an `extern "foo"` function is referenced. | |
| 7806 | // If we haven't seen this library yet and we're targeting Windows, we need | |
| 7807 | // to queue up a work item to produce the DLL import library for this. | |
| 7808 | const gop = try comp.windows_libs.getOrPut(comp.gpa, lib_name); | |
| 7809 | if (gop.found_existing) return; | |
| 7810 | { | |
| 7811 | errdefer _ = comp.windows_libs.pop(); | |
| 7812 | gop.key_ptr.* = try comp.gpa.dupe(u8, lib_name); | |
| 7813 | } | |
| 7814 | try comp.queueJob(.{ .windows_import_lib = gop.index }); | |
| 7815 | } | |
| 7816 | ||
| 7798 | 7817 | /// This decides the optimization mode for all zig-provided libraries, including |
| 7799 | 7818 | /// compiler-rt, libcxx, libc, libunwind, etc. |
| 7800 | 7819 | pub fn compilerRtOptMode(comp: Compilation) std.builtin.OptimizeMode { |
src/Sema.zig+13| ... | ... | @@ -8906,6 +8906,14 @@ fn resolveGenericBody( |
| 8906 | 8906 | return sema.resolveConstDefinedValue(block, src, result, reason); |
| 8907 | 8907 | } |
| 8908 | 8908 | |
| 8909 | /// Given a library name, examines if the library name should end up in | |
| 8910 | /// `link.File.Options.windows_libs` table (for example, libc is always | |
| 8911 | /// specified via dedicated flag `link_libc` instead), | |
| 8912 | /// and puts it there if it doesn't exist. | |
| 8913 | /// It also dupes the library name which can then be saved as part of the | |
| 8914 | /// respective `Decl` (either `ExternFn` or `Var`). | |
| 8915 | /// The liveness of the duped library name is tied to liveness of `Zcu`. | |
| 8916 | /// To deallocate, call `deinit` on the respective `Decl` (`ExternFn` or `Var`). | |
| 8909 | 8917 | pub fn handleExternLibName( |
| 8910 | 8918 | sema: *Sema, |
| 8911 | 8919 | block: *Block, |
| ... | ... | @@ -8955,6 +8963,11 @@ pub fn handleExternLibName( |
| 8955 | 8963 | .{ lib_name, lib_name }, |
| 8956 | 8964 | ); |
| 8957 | 8965 | } |
| 8966 | comp.addLinkLib(lib_name) catch |err| { | |
| 8967 | return sema.fail(block, src_loc, "unable to add link lib '{s}': {s}", .{ | |
| 8968 | lib_name, @errorName(err), | |
| 8969 | }); | |
| 8970 | }; | |
| 8958 | 8971 | } |
| 8959 | 8972 | } |
| 8960 | 8973 |
src/libs/mingw.zig+2-1| ... | ... | @@ -1012,7 +1012,6 @@ const mingw32_winpthreads_src = [_][]const u8{ |
| 1012 | 1012 | "winpthreads" ++ path.sep_str ++ "thread.c", |
| 1013 | 1013 | }; |
| 1014 | 1014 | |
| 1015 | // Note: kernel32 and ntdll are always linked even without targeting MinGW-w64. | |
| 1016 | 1015 | pub const always_link_libs = [_][]const u8{ |
| 1017 | 1016 | "api-ms-win-crt-conio-l1-1-0", |
| 1018 | 1017 | "api-ms-win-crt-convert-l1-1-0", |
| ... | ... | @@ -1030,6 +1029,8 @@ pub const always_link_libs = [_][]const u8{ |
| 1030 | 1029 | "api-ms-win-crt-time-l1-1-0", |
| 1031 | 1030 | "api-ms-win-crt-utility-l1-1-0", |
| 1032 | 1031 | "advapi32", |
| 1032 | "kernel32", | |
| 1033 | "ntdll", | |
| 1033 | 1034 | "shell32", |
| 1034 | 1035 | "user32", |
| 1035 | 1036 | }; |
src/main.zig+2-22| ... | ... | @@ -312,7 +312,6 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 312 | 312 | return jitCmd(gpa, arena, cmd_args, .{ |
| 313 | 313 | .cmd_name = "resinator", |
| 314 | 314 | .root_src_path = "resinator/main.zig", |
| 315 | .windows_libs = &.{"advapi32"}, | |
| 316 | 315 | .depend_on_aro = true, |
| 317 | 316 | .prepend_zig_lib_dir_path = true, |
| 318 | 317 | .server = use_server, |
| ... | ... | @@ -337,7 +336,6 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 337 | 336 | return jitCmd(gpa, arena, cmd_args, .{ |
| 338 | 337 | .cmd_name = "std", |
| 339 | 338 | .root_src_path = "std-docs.zig", |
| 340 | .windows_libs = &.{"ws2_32"}, | |
| 341 | 339 | .prepend_zig_lib_dir_path = true, |
| 342 | 340 | .prepend_zig_exe_path = true, |
| 343 | 341 | .prepend_global_cache_path = true, |
| ... | ... | @@ -3659,6 +3657,7 @@ fn buildOutputType( |
| 3659 | 3657 | } else if (target.os.tag == .windows) { |
| 3660 | 3658 | try test_exec_args.appendSlice(arena, &.{ |
| 3661 | 3659 | "--subsystem", "console", |
| 3660 | "-lkernel32", "-lntdll", | |
| 3662 | 3661 | }); |
| 3663 | 3662 | } |
| 3664 | 3663 | |
| ... | ... | @@ -3862,8 +3861,7 @@ fn createModule( |
| 3862 | 3861 | .only_compiler_rt => continue, |
| 3863 | 3862 | } |
| 3864 | 3863 | |
| 3865 | // We currently prefer import libraries provided by MinGW-w64 even for MSVC. | |
| 3866 | if (target.os.tag == .windows) { | |
| 3864 | if (target.isMinGW()) { | |
| 3867 | 3865 | const exists = mingw.libExists(arena, target, create_module.dirs.zig_lib, lib_name) catch |err| { |
| 3868 | 3866 | fatal("failed to check zig installation for DLL import libs: {s}", .{ |
| 3869 | 3867 | @errorName(err), |
| ... | ... | @@ -5375,14 +5373,6 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 5375 | 5373 | |
| 5376 | 5374 | try root_mod.deps.put(arena, "@build", build_mod); |
| 5377 | 5375 | |
| 5378 | var windows_libs: std.StringArrayHashMapUnmanaged(void) = .empty; | |
| 5379 | ||
| 5380 | if (resolved_target.result.os.tag == .windows) { | |
| 5381 | try windows_libs.ensureUnusedCapacity(arena, 2); | |
| 5382 | windows_libs.putAssumeCapacity("advapi32", {}); | |
| 5383 | windows_libs.putAssumeCapacity("ws2_32", {}); // for `--listen` (web interface) | |
| 5384 | } | |
| 5385 | ||
| 5386 | 5376 | const comp = Compilation.create(gpa, arena, .{ |
| 5387 | 5377 | .libc_installation = libc_installation, |
| 5388 | 5378 | .dirs = dirs, |
| ... | ... | @@ -5405,7 +5395,6 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 5405 | 5395 | .cache_mode = .whole, |
| 5406 | 5396 | .reference_trace = reference_trace, |
| 5407 | 5397 | .debug_compile_errors = debug_compile_errors, |
| 5408 | .windows_lib_names = windows_libs.keys(), | |
| 5409 | 5398 | }) catch |err| { |
| 5410 | 5399 | fatal("unable to create compilation: {s}", .{@errorName(err)}); |
| 5411 | 5400 | }; |
| ... | ... | @@ -5509,7 +5498,6 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 5509 | 5498 | const JitCmdOptions = struct { |
| 5510 | 5499 | cmd_name: []const u8, |
| 5511 | 5500 | root_src_path: []const u8, |
| 5512 | windows_libs: []const []const u8 = &.{}, | |
| 5513 | 5501 | prepend_zig_lib_dir_path: bool = false, |
| 5514 | 5502 | prepend_global_cache_path: bool = false, |
| 5515 | 5503 | prepend_zig_exe_path: bool = false, |
| ... | ... | @@ -5626,13 +5614,6 @@ fn jitCmd( |
| 5626 | 5614 | try root_mod.deps.put(arena, "aro", aro_mod); |
| 5627 | 5615 | } |
| 5628 | 5616 | |
| 5629 | var windows_libs: std.StringArrayHashMapUnmanaged(void) = .empty; | |
| 5630 | ||
| 5631 | if (resolved_target.result.os.tag == .windows) { | |
| 5632 | try windows_libs.ensureUnusedCapacity(arena, options.windows_libs.len); | |
| 5633 | for (options.windows_libs) |lib| windows_libs.putAssumeCapacity(lib, {}); | |
| 5634 | } | |
| 5635 | ||
| 5636 | 5617 | const comp = Compilation.create(gpa, arena, .{ |
| 5637 | 5618 | .dirs = dirs, |
| 5638 | 5619 | .root_name = options.cmd_name, |
| ... | ... | @@ -5643,7 +5624,6 @@ fn jitCmd( |
| 5643 | 5624 | .self_exe_path = self_exe_path, |
| 5644 | 5625 | .thread_pool = &thread_pool, |
| 5645 | 5626 | .cache_mode = .whole, |
| 5646 | .windows_lib_names = windows_libs.keys(), | |
| 5647 | 5627 | }) catch |err| { |
| 5648 | 5628 | fatal("unable to create compilation: {s}", .{@errorName(err)}); |
| 5649 | 5629 | }; |
test/standalone/simple/build.zig-8| ... | ... | @@ -50,10 +50,6 @@ pub fn build(b: *std.Build) void { |
| 50 | 50 | }); |
| 51 | 51 | if (case.link_libc) exe.root_module.link_libc = true; |
| 52 | 52 | |
| 53 | if (resolved_target.result.os.tag == .windows) { | |
| 54 | exe.root_module.linkSystemLibrary("advapi32", .{}); | |
| 55 | } | |
| 56 | ||
| 57 | 53 | _ = exe.getEmittedBin(); |
| 58 | 54 | |
| 59 | 55 | step.dependOn(&exe.step); |
| ... | ... | @@ -70,10 +66,6 @@ pub fn build(b: *std.Build) void { |
| 70 | 66 | }); |
| 71 | 67 | if (case.link_libc) exe.root_module.link_libc = true; |
| 72 | 68 | |
| 73 | if (resolved_target.result.os.tag == .windows) { | |
| 74 | exe.root_module.linkSystemLibrary("advapi32", .{}); | |
| 75 | } | |
| 76 | ||
| 77 | 69 | const run = b.addRunArtifact(exe); |
| 78 | 70 | step.dependOn(&run.step); |
| 79 | 71 | } |
test/standalone/windows_argv/build.zig-2| ... | ... | @@ -47,8 +47,6 @@ pub fn build(b: *std.Build) !void { |
| 47 | 47 | }), |
| 48 | 48 | }); |
| 49 | 49 | |
| 50 | fuzz.root_module.linkSystemLibrary("advapi32", .{}); | |
| 51 | ||
| 52 | 50 | const fuzz_max_iterations = b.option(u64, "iterations", "The max fuzz iterations (default: 100)") orelse 100; |
| 53 | 51 | const fuzz_iterations_arg = std.fmt.allocPrint(b.allocator, "{}", .{fuzz_max_iterations}) catch @panic("oom"); |
| 54 | 52 |
test/standalone/windows_bat_args/build.zig-4| ... | ... | @@ -28,8 +28,6 @@ pub fn build(b: *std.Build) !void { |
| 28 | 28 | }), |
| 29 | 29 | }); |
| 30 | 30 | |
| 31 | test_exe.root_module.linkSystemLibrary("advapi32", .{}); | |
| 32 | ||
| 33 | 31 | const run = b.addRunArtifact(test_exe); |
| 34 | 32 | run.addArtifactArg(echo_args); |
| 35 | 33 | run.expectExitCode(0); |
| ... | ... | @@ -46,8 +44,6 @@ pub fn build(b: *std.Build) !void { |
| 46 | 44 | }), |
| 47 | 45 | }); |
| 48 | 46 | |
| 49 | fuzz.root_module.linkSystemLibrary("advapi32", .{}); | |
| 50 | ||
| 51 | 47 | const fuzz_max_iterations = b.option(u64, "iterations", "The max fuzz iterations (default: 100)") orelse 100; |
| 52 | 48 | const fuzz_iterations_arg = std.fmt.allocPrint(b.allocator, "{}", .{fuzz_max_iterations}) catch @panic("oom"); |
| 53 | 49 |
test/standalone/windows_spawn/build.zig-2| ... | ... | @@ -28,8 +28,6 @@ pub fn build(b: *std.Build) void { |
| 28 | 28 | }), |
| 29 | 29 | }); |
| 30 | 30 | |
| 31 | main.root_module.linkSystemLibrary("advapi32", .{}); | |
| 32 | ||
| 33 | 31 | const run = b.addRunArtifact(main); |
| 34 | 32 | run.addArtifactArg(hello); |
| 35 | 33 | run.expectExitCode(0); |
test/tests.zig-9| ... | ... | @@ -2238,7 +2238,6 @@ const ModuleTestOptions = struct { |
| 2238 | 2238 | desc: []const u8, |
| 2239 | 2239 | optimize_modes: []const OptimizeMode, |
| 2240 | 2240 | include_paths: []const []const u8, |
| 2241 | windows_libs: []const []const u8, | |
| 2242 | 2241 | skip_single_threaded: bool, |
| 2243 | 2242 | skip_non_native: bool, |
| 2244 | 2243 | skip_freebsd: bool, |
| ... | ... | @@ -2373,10 +2372,6 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { |
| 2373 | 2372 | |
| 2374 | 2373 | for (options.include_paths) |include_path| these_tests.root_module.addIncludePath(b.path(include_path)); |
| 2375 | 2374 | |
| 2376 | if (target.os.tag == .windows) { | |
| 2377 | for (options.windows_libs) |lib| these_tests.root_module.linkSystemLibrary(lib, .{}); | |
| 2378 | } | |
| 2379 | ||
| 2380 | 2375 | const qualified_name = b.fmt("{s}-{s}-{s}-{s}{s}{s}{s}{s}{s}{s}", .{ |
| 2381 | 2376 | options.name, |
| 2382 | 2377 | triple_txt, |
| ... | ... | @@ -2672,10 +2667,6 @@ pub fn addIncrementalTests(b: *std.Build, test_step: *Step) !void { |
| 2672 | 2667 | }), |
| 2673 | 2668 | }); |
| 2674 | 2669 | |
| 2675 | if (b.graph.host.result.os.tag == .windows) { | |
| 2676 | incr_check.root_module.linkSystemLibrary("advapi32", .{}); | |
| 2677 | } | |
| 2678 | ||
| 2679 | 2670 | var dir = try b.build_root.handle.openDir("test/incremental", .{ .iterate = true }); |
| 2680 | 2671 | defer dir.close(); |
| 2681 | 2672 |