From 2ee617540c0bf0f9e240eb319c04b0480c3fb505 Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Sat, 4 May 2024 11:54:42 -0600 Subject: [PATCH] rework ci --- .github/workflows/artifact.yml | 33 ++--- build.zig | 254 ++++++++++++++++++++------------- test.zig | 98 ++++++++----- 3 files changed, 235 insertions(+), 150 deletions(-) diff --git a/.github/workflows/artifact.yml b/.github/workflows/artifact.yml index 0d29928..cca1a16 100644 --- a/.github/workflows/artifact.yml +++ b/.github/workflows/artifact.yml @@ -9,28 +9,19 @@ jobs: fail-fast: false runs-on: ${{matrix.os}} steps: - - uses: actions/checkout@v2 - - uses: goto-bus-stop/setup-zig@v1 + - uses: actions/checkout@v4 + - uses: goto-bus-stop/setup-zig@v2 with: version: 0.12.0 - run: | - zig build test -Dci_target=${{matrix.os}}-${{matrix.arch}} - - run: | - zig build -Dci_target=ubuntu-latest-x86_64 -p zig-out-ubuntu-latest-x86_64 - - run: | - zig build -Dci_target=ubuntu-latest-aarch64 -p zig-out-ubuntu-latest-aarch64 - - run: | - zig build -Dci_target=macos-latest-x86_64 -p zig-out-macos-latest-x86_64 - - run: | - zig build -Dci_target=macos-latest-aarch64 -p zig-out-macos-latest-aarch64 - - run: | - zig build -Dci_target=windows-latest-x86_64 -p zig-out-windows-latest-x86_64 - - uses: actions/upload-artifact@v2 - with: - name: zigup ${{ matrix.os }}-${{ matrix.arch }} - path: zig-out/bin/* - - if: ${{ matrix.os == 'ubuntu-latest' || matrix.os == 'macos-latest' }} - uses: actions/upload-artifact@v2 + zig build ci + - if: ${{ matrix.os == 'ubuntu-latest' }} + uses: actions/upload-artifact@v4 with: - name: zigup ${{ matrix.os }}-aarch64 - path: zig-out-${{matrix.os}}-aarch64/bin/* + name: zigup-archives + path: | + zig-out/zigup-x86_64-linux.tar.gz + zig-out/zigup-x86_64-macos.tar.gz + zig-out/zigup-x86_64-windows.zip + zig-out/zigup-aarch64-linux.tar.gz + zig-out/zigup-aarch64-macos.tar.gz diff --git a/build.zig b/build.zig index 5ede94e..70c4dd0 100644 --- a/build.zig +++ b/build.zig @@ -1,63 +1,42 @@ const std = @import("std"); const builtin = @import("builtin"); -const Pkg = std.build.Pkg; - -fn unwrapOptionalBool(optionalBool: ?bool) bool { - if (optionalBool) |b| return b; - return false; -} pub fn build(b: *std.Build) !void { - //var github_release_step = b.step("github-release", "Build the github-release binaries"); - //try addGithubReleaseExe(b, github_release_step, ziget_repo, "x86_64-linux", .std); - - const maybe_ci_target = b.option([]const u8, "ci_target", "the CI target being built"); - const target = if (maybe_ci_target) |ci_target| - b.resolveTargetQuery(try std.zig.CrossTarget.parse(.{ .arch_os_abi = ci_target_map.get(ci_target) orelse { - std.log.err("unknown ci_target '{s}'", .{ci_target}); - std.process.exit(1); - } })) - else - b.standardTargetOptions(.{}); - - // Compile in ReleaseSafe on Windows for faster extraction - const optimize: std.builtin.OptimizeMode = if ( - (maybe_ci_target != null) and (target.result.os.tag == .windows) - ) .ReleaseSafe else b.standardOptimizeOption(.{}); - const win32exelink_mod: ?*std.Build.Module = blk: { - if (target.result.os.tag == .windows) { - const exe = b.addExecutable(.{ - .name = "win32exelink", - .root_source_file = .{ .path = "win32exelink.zig" }, - .target = target, - .optimize = optimize, - }); - break :blk b.createModule(.{ - .root_source_file = exe.getEmittedBin(), - }); + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + const zigup_exe_native = blk: { + const exe = addZigupExe(b, target, optimize); + b.installArtifact(exe); + const run_cmd = b.addRunArtifact(exe); + run_cmd.step.dependOn(b.getInstallStep()); + const run_step = b.step("run", "Run the app"); + run_step.dependOn(&run_cmd.step); + if (b.args) |args| { + run_cmd.addArgs(args); } - break :blk null; + break :blk exe; }; - const exe = try addZigupExe( - b, - target, - optimize, - win32exelink_mod, - ); - b.installArtifact(exe); - - const run_cmd = b.addRunArtifact(exe); - run_cmd.step.dependOn(b.getInstallStep()); - - const run_step = b.step("run", "Run the app"); - run_step.dependOn(&run_cmd.step); - if (b.args) |args| { - run_cmd.addArgs(args); + const test_step = b.step("test", "test the executable"); + { + const exe = b.addExecutable(.{ + .name = "test", + .root_source_file = .{ .path = "test.zig" }, + .target = target, + .optimize = optimize, + }); + const run_cmd = b.addRunArtifact(exe); + run_cmd.addArtifactArg(zigup_exe_native); + run_cmd.addDirectoryArg(b.path("scratch/native")); + test_step.dependOn(&run_cmd.step); } - addTest(b, exe, target, optimize); + const unzip_step = b.step( + "unzip", + "Build/install the unzip cmdline tool", + ); { const unzip = b.addExecutable(.{ @@ -67,39 +46,36 @@ pub fn build(b: *std.Build) !void { .optimize = optimize, }); const install = b.addInstallArtifact(unzip, .{}); - b.step("unzip", "Build/install the unzip cmdline tool").dependOn(&install.step); + unzip_step.dependOn(&install.step); } -} -fn addTest( - b: *std.Build, - exe: *std.Build.Step.Compile, - target: std.Build.ResolvedTarget, - optimize: std.builtin.Mode, -) void { - const test_exe = b.addExecutable(.{ - .name = "test", - .root_source_file = .{ .path = "test.zig" }, - .target = target, - .optimize = optimize, - }); - const run_cmd = b.addRunArtifact(test_exe); - - // TODO: make this work, add exe install path as argument to test - //run_cmd.addArg(exe.getInstallPath()); - _ = exe; - run_cmd.step.dependOn(b.getInstallStep()); - - const test_step = b.step("test", "test the executable"); - test_step.dependOn(&run_cmd.step); + const ci_step = b.step("ci", "The build/test step to run on the CI"); + ci_step.dependOn(b.getInstallStep()); + ci_step.dependOn(test_step); + ci_step.dependOn(unzip_step); + try ci(b, ci_step, test_step); } fn addZigupExe( b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.Mode, - win32exelink_mod: ?*std.Build.Module, -) !*std.Build.Step.Compile { +) *std.Build.Step.Compile { + const win32exelink_mod: ?*std.Build.Module = blk: { + if (target.result.os.tag == .windows) { + const exe = b.addExecutable(.{ + .name = "win32exelink", + .root_source_file = .{ .path = "win32exelink.zig" }, + .target = target, + .optimize = optimize, + }); + break :blk b.createModule(.{ + .root_source_file = exe.getEmittedBin(), + }); + } + break :blk null; + }; + const exe = b.addExecutable(.{ .name = "zigup", .root_source_file = .{ .path = "zigup.zig" }, @@ -110,31 +86,115 @@ fn addZigupExe( if (target.result.os.tag == .windows) { exe.root_module.addImport("win32exelink", win32exelink_mod.?); } - return exe; } -fn addGithubReleaseExe( - b: *std.Build, - github_release_step: *std.build.Step, - comptime target_triple: []const u8, -) !void { - const small_release = true; - - const target = try std.zig.CrossTarget.parse(.{ .arch_os_abi = target_triple }); - const mode = if (small_release) .ReleaseSafe else .Debug; - const exe = try addZigupExe(b, target, mode); - if (small_release) { - exe.strip = true; +fn ci(b: *std.Build, ci_step: *std.Build.Step, test_step: *std.Build.Step) !void { + const ci_targets = [_][]const u8 { + "x86_64-linux", + "x86_64-macos", + "x86_64-windows", + "aarch64-linux", + "aarch64-macos", + }; + + const make_archive_step = b.step("archive", "Create CI archives"); + ci_step.dependOn(make_archive_step); + + var previous_test_step = test_step; + + for (ci_targets) |ci_target_str| { + const target = b.resolveTargetQuery(try std.zig.CrossTarget.parse( + .{ .arch_os_abi = ci_target_str }, + )); + const optimize: std.builtin.OptimizeMode = + // Compile in ReleaseSafe on Windows for faster extraction + if (target.result.os.tag == .windows) .ReleaseSafe + else .Debug; + const zigup_exe = addZigupExe(b, target, optimize); + const zigup_exe_install = b.addInstallArtifact(zigup_exe, .{ + .dest_dir = .{ .override = .{ .custom = ci_target_str } }, + }); + ci_step.dependOn(&zigup_exe_install.step); + + const test_exe = b.addExecutable(.{ + .name = b.fmt("test-{s}", .{ci_target_str}), + .root_source_file = .{ .path = "test.zig" }, + .target = target, + .optimize = optimize, + }); + const run_cmd = b.addRunArtifact(test_exe); + run_cmd.addArtifactArg(zigup_exe); + run_cmd.addDirectoryArg(b.path(b.fmt("scratch/{s}", .{ci_target_str}))); + + // This doesn't seem to be working, so I've added a pre-check below + run_cmd.failing_to_execute_foreign_is_an_error = false; + const os_compatible = (builtin.os.tag == target.result.os.tag); + const arch_compatible = (builtin.cpu.arch == target.result.cpu.arch); + if (os_compatible and arch_compatible) { + ci_step.dependOn(&run_cmd.step); + + // prevent tests from running at the same time so their output + // doesn't mangle each other. + run_cmd.step.dependOn(previous_test_step); + previous_test_step = &run_cmd.step; + } + + if (builtin.os.tag == .linux) { + make_archive_step.dependOn(makeCiArchiveStep( + b, ci_target_str, target.result, zigup_exe_install, + )); + } } - exe.setOutputDir("github-release" ++ std.fs.path.sep_str ++ target_triple); - github_release_step.dependOn(&exe.step); } -const ci_target_map = std.ComptimeStringMap([]const u8, .{ - .{ "ubuntu-latest-x86_64", "x86_64-linux" }, - .{ "macos-latest-x86_64", "x86_64-macos" }, - .{ "windows-latest-x86_64", "x86_64-windows" }, - .{ "ubuntu-latest-aarch64", "aarch64-linux" }, - .{ "macos-latest-aarch64", "aarch64-macos" }, -}); +fn makeCiArchiveStep( + b: *std.Build, + ci_target_str: []const u8, + target: std.Target, + exe_install: *std.Build.Step.InstallArtifact, +) *std.Build.Step { + const install_path = b.getInstallPath(.prefix, "."); + + switch (target.os.tag) { + .windows => { + const zip = b.pathJoin(&.{ + install_path, + b.fmt("zigup-{s}.zip", .{ci_target_str}), + }); + const tar = b.addSystemCommand(&.{ + "tar", + "-a", + "-c", + "-f", zip, + "zigup.exe", + "zigup.pdb", + }); + tar.cwd = .{ .path = b.getInstallPath( + exe_install.dest_dir.?, + ".", + )}; + tar.step.dependOn(&exe_install.step); + return &tar.step; + }, + .linux, .macos => { + const targz = b.pathJoin(&.{ + install_path, + b.fmt("zigup-{s}.tar.gz", .{ci_target_str}), + }); + const tar = b.addSystemCommand(&.{ + "tar", + "-czf", + targz, + "zigup", + }); + tar.cwd = .{ .path = b.getInstallPath( + exe_install.dest_dir.?, + ".", + )}; + tar.step.dependOn(&exe_install.step); + return &tar.step; + }, + else => @panic("todo"), + } +} diff --git a/test.zig b/test.zig index e342dc2..e2e3baa 100644 --- a/test.zig +++ b/test.zig @@ -18,39 +18,67 @@ fn setPathEnv(new_path: []const u8) void { const expected_zig_version_0_7_0 = if (builtin.os.tag == .macos) "0.7.0+9af53f8e0" else "0.7.0"; pub fn main() !u8 { - std.log.info("running test!", .{}); - try fixdeletetree.deleteTree(std.fs.cwd(), "scratch"); - try std.fs.cwd().makeDir("scratch"); - const bin_dir = "scratch" ++ sep ++ "bin"; + var allocator_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator); + //defer allocator_instance.deinit(); + const allocator = allocator_instance.allocator(); + + const all_cmdline_args = try std.process.argsAlloc(allocator); + if (all_cmdline_args.len <= 1) { + try std.io.getStdErr().writer().print("Usage: test ZIGUP_EXE TEST_DIR\n", .{}); + return 0xff; + } + const cmdline_args = all_cmdline_args[1..]; + if (cmdline_args.len != 2) { + std.log.err("expected 1 cmdline arg but got {}", .{cmdline_args.len}); + return 0xff; + } + + const zigup_src_exe = cmdline_args[0]; + const test_dir = cmdline_args[1]; + std.log.info("run zigup tests", .{}); + std.log.info("zigup exe '{s}'", .{zigup_src_exe}); + std.log.info("test directory '{s}'", .{test_dir}); + + if (!std.fs.path.isAbsolute(test_dir)) { + std.log.err("currently the test requires an absolute test directory path", .{}); + return 0xff; + } + + try fixdeletetree.deleteTree(std.fs.cwd(), test_dir); + try std.fs.cwd().makePath(test_dir); + const bin_dir = try std.fs.path.join(allocator, &.{ test_dir, "bin" }); try std.fs.cwd().makeDir(bin_dir); - const install_dir = if (builtin.os.tag == .windows) (bin_dir ++ "\\zig") else ("scratch/install"); + const install_sub_path = if (builtin.os.tag == .windows) "bin\\zig" else "install"; + const install_dir = try std.fs.path.join(allocator, &.{test_dir, install_sub_path }); try std.fs.cwd().makeDir(install_dir); - // NOTE: for now we are incorrectly assuming the install dir is CWD/zig-out - const zigup = comptime "." ++ sep ++ bin_dir ++ sep ++ "zigup" ++ builtin.target.exeFileExt(); + const zigup = try std.fs.path.join(allocator, &.{ + test_dir, + "bin", + "zigup" ++ comptime builtin.target.exeFileExt() + }); try std.fs.cwd().copyFile( - comptime "zig-out" ++ sep ++ "bin" ++ sep ++ "zigup" ++ builtin.target.exeFileExt(), + zigup_src_exe, std.fs.cwd(), zigup, .{}, ); if (builtin.os.tag == .windows) { - const zigup_pdb = comptime "." ++ sep ++ bin_dir ++ sep ++ "zigup.pdb"; - try std.fs.cwd().copyFile( - comptime "zig-out" ++ sep ++ "bin" ++ sep ++ "zigup.pdb", - std.fs.cwd(), - zigup_pdb, - .{}, + const zigup_src_pdb = try std.mem.concat( + allocator, u8, &.{ zigup_src_exe[0 .. zigup_src_exe.len-4], ".pdb" } ); + defer allocator.free(zigup_src_pdb); + const zigup_pdb = try std.fs.path.join(allocator, &.{ test_dir, "bin\\zigup.pdb" }); + defer allocator.free(zigup_pdb); + try std.fs.cwd().copyFile(zigup_src_pdb, std.fs.cwd(), zigup_pdb, .{}); } - const install_args = if (builtin.os.tag == .windows) [_][]const u8{} else [_][]const u8{ "--install-dir", install_dir }; + const install_args = if (builtin.os.tag == .windows) [_][]const u8{ + } else [_][]const u8{ + "--install-dir", install_dir, + }; const zigup_args = &[_][]const u8{zigup} ++ install_args; - var allocator_store = std.heap.ArenaAllocator.init(std.heap.page_allocator); - defer allocator_store.deinit(); - const allocator = allocator_store.allocator(); - const path_link = try std.fs.path.join(allocator, &.{ bin_dir, comptime "zig" ++ builtin.target.exeFileExt() }); defer allocator.free(path_link); @@ -60,14 +88,9 @@ pub fn main() !u8 { std.log.err("the PATH environment variable does not exist?", .{}); return 1; }; - const cwd = try std.process.getCwdAlloc(allocator); const original_path_env = path_env_ptr.*; - { - const scratch_bin_path = try std.fs.path.join(allocator, &.{ cwd, bin_dir }); - defer allocator.free(scratch_bin_path); - setPathEnv(try std.mem.concat(allocator, u8, &.{ scratch_bin_path, path_env_sep, original_path_env })); - } + setPathEnv(try std.mem.concat(allocator, u8, &.{ bin_dir, path_env_sep, original_path_env })); { const result = try runCaptureOuts(allocator, zigup_args ++ &[_][]const u8{ "default", "master" }); @@ -139,7 +162,8 @@ pub fn main() !u8 { // verify we print a nice error message if we can't update the symlink // because it's a directory { - const zig_exe_link = comptime "scratch" ++ sep ++ "bin" ++ sep ++ "zig" ++ builtin.target.exeFileExt(); + const zig_exe_link = try std.fs.path.join(allocator, &.{ bin_dir, "zig" ++ comptime builtin.target.exeFileExt() }); + defer allocator.free(zig_exe_link); if (std.fs.cwd().access(zig_exe_link, .{})) { try std.fs.cwd().deleteFile(zig_exe_link); @@ -237,7 +261,7 @@ pub fn main() !u8 { try testing.expectEqual(@as(u32, 3), try getCompilerCount(install_dir)); // Just make a directory to trick zigup into thinking there is another compiler so we don't have to wait for it to download/install - try std.fs.cwd().makeDir(install_dir ++ sep ++ "0.9.0"); + try makeDir(test_dir, install_sub_path ++ sep ++ "0.9.0"); try testing.expectEqual(@as(u32, 4), try getCompilerCount(install_dir)); try runNoCapture(zigup_args ++ &[_][]const u8{"clean"}); try testing.expectEqual(@as(u32, 3), try getCompilerCount(install_dir)); @@ -281,20 +305,24 @@ pub fn main() !u8 { // verify that we get an error if there is another compiler in the path { - const bin2_dir = "scratch" ++ sep ++ "bin2"; + const bin2_dir = try std.fs.path.join(allocator, &.{ test_dir, "bin2" }); + defer allocator.free(bin2_dir); try std.fs.cwd().makeDir(bin2_dir); const previous_path = path_env_ptr.*; - const scratch_bin2_path = try std.fs.path.join(allocator, &.{ cwd, bin2_dir }); - defer allocator.free(scratch_bin2_path); { - var file = try std.fs.cwd().createFile(comptime bin2_dir ++ sep ++ "zig" ++ builtin.target.exeFileExt(), .{}); + const fake_zig = try std.fs.path.join(allocator, &.{ + bin2_dir, + "zig" ++ comptime builtin.target.exeFileExt() + }); + defer allocator.free(fake_zig); + var file = try std.fs.cwd().createFile(fake_zig, .{}); defer file.close(); try file.writer().writeAll("a fake executable"); } - setPathEnv(try std.mem.concat(allocator, u8, &.{ scratch_bin2_path, path_env_sep, previous_path })); + setPathEnv(try std.mem.concat(allocator, u8, &.{ bin2_dir, path_env_sep, previous_path })); defer setPathEnv(previous_path); // verify zig isn't currently on 0.7.0 before we set it as the default @@ -326,6 +354,12 @@ pub fn main() !u8 { return 0; } +fn makeDir(dir_path: []const u8, sub_path: []const u8) !void { + var dir = try std.fs.cwd().openDir(dir_path, .{}); + defer dir.close(); + try dir.makeDir(sub_path); +} + fn checkZigVersion(allocator: std.mem.Allocator, zig: []const u8, compare: []const u8, want_equal: enum { not_equal, equal }) !void { const result = try runCaptureOuts(allocator, &[_][]const u8{ zig, "version" }); defer {