123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686 |
- const std = @import("std");
- const builtin = @import("builtin");
- pub fn build(b: *std.Build) !void {
- 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 exe;
- };
- const test_step = b.step("test", "test the executable");
- addTests(b, target, zigup_exe_native, test_step, .{
- .make_build_steps = true,
- });
- const unzip_step = b.step(
- "unzip",
- "Build/install the unzip cmdline tool",
- );
- {
- const unzip = b.addExecutable(.{
- .name = "unzip",
- .root_source_file = b.path("unzip.zig"),
- .target = target,
- .optimize = optimize,
- });
- const install = b.addInstallArtifact(unzip, .{});
- unzip_step.dependOn(&install.step);
- }
- const zip_step = b.step(
- "zip",
- "Build/install the zip cmdline tool",
- );
- {
- const zip = b.addExecutable(.{
- .name = "zip",
- .root_source_file = b.path("zip.zig"),
- .target = target,
- .optimize = optimize,
- });
- const install = b.addInstallArtifact(zip, .{});
- zip_step.dependOn(&install.step);
- }
- const host_zip_exe = b.addExecutable(.{
- .name = "zip",
- .root_source_file = b.path("zip.zig"),
- .target = b.host,
- });
- 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);
- ci_step.dependOn(zip_step);
- try ci(b, ci_step, host_zip_exe);
- }
- fn addZigupExe(
- b: *std.Build,
- target: std.Build.ResolvedTarget,
- optimize: std.builtin.Mode,
- ) *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 = b.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 = b.path("zigup.zig"),
- .target = target,
- .optimize = optimize,
- });
- if (target.result.os.tag == .windows) {
- exe.root_module.addImport("win32exelink", win32exelink_mod.?);
- }
- return exe;
- }
- fn ci(
- b: *std.Build,
- ci_step: *std.Build.Step,
- host_zip_exe: *std.Build.Step.Compile,
- ) !void {
- const ci_targets = [_][]const u8{
- "x86_64-linux",
- "x86_64-macos",
- "x86_64-windows",
- "aarch64-linux",
- "aarch64-macos",
- "aarch64-windows",
- "arm-linux",
- "riscv64-linux",
- "powerpc-linux",
- "powerpc64le-linux",
- };
- const make_archive_step = b.step("archive", "Create CI archives");
- ci_step.dependOn(make_archive_step);
- for (ci_targets) |ci_target_str| {
- const target = b.resolveTargetQuery(try std.Target.Query.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 target_test_step = b.step(b.fmt("test-{s}", .{ci_target_str}), "");
- addTests(b, target, zigup_exe, target_test_step, .{
- .make_build_steps = false,
- // This doesn't seem to be working, so we're only adding these tests
- // as a dependency if we see the arch is compatible beforehand
- .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(target_test_step);
- }
- if (builtin.os.tag == .linux) {
- make_archive_step.dependOn(makeCiArchiveStep(b, ci_target_str, target.result, zigup_exe_install, host_zip_exe));
- }
- }
- }
- fn makeCiArchiveStep(
- b: *std.Build,
- ci_target_str: []const u8,
- target: std.Target,
- exe_install: *std.Build.Step.InstallArtifact,
- host_zip_exe: *std.Build.Step.Compile,
- ) *std.Build.Step {
- const install_path = b.getInstallPath(.prefix, ".");
- if (target.os.tag == .windows) {
- const out_zip_file = b.pathJoin(&.{
- install_path,
- b.fmt("zigup-{s}.zip", .{ci_target_str}),
- });
- const zip = b.addRunArtifact(host_zip_exe);
- zip.addArg(out_zip_file);
- zip.addArg("zigup.exe");
- zip.addArg("zigup.pdb");
- zip.cwd = .{ .cwd_relative = b.getInstallPath(
- exe_install.dest_dir.?,
- ".",
- ) };
- zip.step.dependOn(&exe_install.step);
- return &zip.step;
- }
- 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 = .{ .cwd_relative = b.getInstallPath(
- exe_install.dest_dir.?,
- ".",
- ) };
- tar.step.dependOn(&exe_install.step);
- return &tar.step;
- }
- const SharedTestOptions = struct {
- make_build_steps: bool,
- failing_to_execute_foreign_is_an_error: bool = true,
- };
- fn addTests(
- b: *std.Build,
- target: std.Build.ResolvedTarget,
- zigup_exe: *std.Build.Step.Compile,
- test_step: *std.Build.Step,
- shared_options: SharedTestOptions,
- ) void {
- const runtest_exe = b.addExecutable(.{
- .name = "runtest",
- .root_source_file = b.path("runtest.zig"),
- .target = target,
- });
- const tests: Tests = .{
- .b = b,
- .test_step = test_step,
- .zigup_exe = zigup_exe,
- .runtest_exe = runtest_exe,
- .shared_options = shared_options,
- };
- tests.addWithClean(.{
- .name = "test-usage-h",
- .argv = &.{"-h"},
- .check = .{ .expect_stderr_match = "Usage" },
- });
- tests.addWithClean(.{
- .name = "test-usage-help",
- .argv = &.{"--help"},
- .check = .{ .expect_stderr_match = "Usage" },
- });
- tests.addWithClean(.{
- .name = "test-fetch-index",
- .argv = &.{"fetch-index"},
- .checks = &.{
- .{ .expect_stdout_match = "master" },
- .{ .expect_stdout_match = "version" },
- .{ .expect_stdout_match = "0.13.0" },
- },
- });
- tests.addWithClean(.{
- .name = "test-no-default",
- .argv = &.{"default"},
- .check = .{ .expect_stdout_exact = "<no-default>\n" },
- });
- tests.addWithClean(.{
- .name = "test-default-master-not-fetched",
- .argv = &.{ "default", "master" },
- .check = .{ .expect_stderr_match = "master has not been fetched" },
- });
- tests.addWithClean(.{
- .name = "test-default-0.7.0-not-fetched",
- .argv = &.{ "default", "0.7.0" },
- .check = .{ .expect_stderr_match = "error: compiler '0.7.0' is not installed\n" },
- });
- tests.addWithClean(.{
- .name = "test-bad-version",
- .argv = &.{ "THIS_ZIG_VERSION_DOES_NOT_EXIT" },
- .checks = &.{
- .{ .expect_stderr_match = "error: download '" },
- .{ .expect_stderr_match = "' failed: " },
- },
- });
- // NOTE: this test will eventually break when these builds are cleaned up,
- // we should support downloading from bazel and use that instead since
- // it should be more permanent
- tests.addWithClean(.{
- .name = "test-dev-version",
- .argv = &.{ "0.14.0-dev.2465+70de2f3a7" },
- .check = .{ .expect_stdout_exact = "" },
- });
- const _7 = tests.add(.{
- .name = "test-7",
- .argv = &.{"0.7.0"},
- .check = .{ .expect_stdout_match = "" },
- });
- tests.addWithClean(.{
- .name = "test-already-fetched-7",
- .env = _7,
- .argv = &.{ "fetch", "0.7.0" },
- .check = .{ .expect_stderr_match = "already installed" },
- });
- tests.addWithClean(.{
- .name = "test-get-default-7",
- .env = _7,
- .argv = &.{"default"},
- .check = .{ .expect_stdout_exact = "0.7.0\n" },
- });
- tests.addWithClean(.{
- .name = "test-get-default-7-no-path",
- .env = _7,
- .add_path = false,
- .argv = &.{ "default", "0.7.0" },
- .check = .{ .expect_stderr_match = " is not in PATH" },
- });
- // verify we print a nice error message if we can't update the symlink
- // because it's a directory
- tests.addWithClean(.{
- .name = "test-get-default-7-path-link-is-directory",
- .env = _7,
- .setup_option = "path-link-is-directory",
- .argv = &.{ "default", "0.7.0" },
- .checks = switch (builtin.os.tag) {
- .windows => &.{
- .{ .expect_stderr_match = "unable to create the exe link, the path '" },
- .{ .expect_stderr_match = "' is a directory" },
- },
- else => &.{
- .{ .expect_stderr_match = "unable to update/overwrite the 'zig' PATH symlink, the file '" },
- .{ .expect_stderr_match = "' already exists and is not a symlink" },
- },
- },
- });
- const _7_and_8 = tests.add(.{
- .name = "test-fetch-8",
- .env = _7,
- .argv = &.{ "fetch", "0.8.0" },
- });
- tests.addWithClean(.{
- .name = "test-get-default-7-after-fetch-8",
- .env = _7_and_8,
- .argv = &.{"default"},
- .check = .{ .expect_stdout_exact = "0.7.0\n" },
- });
- tests.addWithClean(.{
- .name = "test-already-fetched-8",
- .env = _7_and_8,
- .argv = &.{ "fetch", "0.8.0" },
- .check = .{ .expect_stderr_match = "already installed" },
- });
- const _7_and_default_8 = tests.add(.{
- .name = "test-set-default-8",
- .env = _7_and_8,
- .argv = &.{ "default", "0.8.0" },
- .check = .{ .expect_stdout_exact = "" },
- });
- tests.addWithClean(.{
- .name = "test-7-after-default-8",
- .env = _7_and_default_8,
- .argv = &.{"0.7.0"},
- .check = .{ .expect_stdout_exact = "" },
- });
- const master_7_and_8 = tests.add(.{
- .name = "test-master",
- .env = _7_and_8,
- .argv = &.{"master"},
- .check = .{ .expect_stdout_exact = "" },
- });
- tests.addWithClean(.{
- .name = "test-already-fetched-master",
- .env = master_7_and_8,
- .argv = &.{ "fetch", "master" },
- .check = .{ .expect_stderr_match = "already installed" },
- });
- tests.addWithClean(.{
- .name = "test-default-after-master",
- .env = master_7_and_8,
- .argv = &.{"default"},
- // master version could be anything so we won't check
- });
- tests.addWithClean(.{
- .name = "test-default-master",
- .env = master_7_and_8,
- .argv = &.{ "default", "master" },
- });
- tests.addWithClean(.{
- .name = "test-default-not-in-path",
- .add_path = false,
- .env = master_7_and_8,
- .argv = &.{ "default", "master" },
- .check = .{ .expect_stderr_match = " is not in PATH" },
- });
- // verify that we get an error if there is another compiler in the path
- tests.addWithClean(.{
- .name = "test-default-master-with-another-zig",
- .setup_option = "another-zig",
- .env = master_7_and_8,
- .argv = &.{ "default", "master" },
- .checks = &.{
- .{ .expect_stderr_match = "error: zig compiler '" },
- .{ .expect_stderr_match = "' is higher priority in PATH than the path-link '" },
- },
- });
- {
- const default8 = tests.add(.{
- .name = "test-default8-with-another-zig",
- .setup_option = "another-zig",
- .env = master_7_and_8,
- .argv = &.{ "default", "0.8.0" },
- .checks = &.{
- .{ .expect_stderr_match = "error: zig compiler '" },
- .{ .expect_stderr_match = "' is higher priority in PATH than the path-link '" },
- },
- });
- // default compiler should still be set
- tests.addWithClean(.{
- .name = "test-default8-even-with-another-zig",
- .env = default8,
- .argv = &.{ "default" },
- .check = .{ .expect_stdout_exact = "0.8.0\n" },
- });
- }
- tests.addWithClean(.{
- .name = "test-list",
- .env = master_7_and_8,
- .argv = &.{"list"},
- .checks = &.{
- .{ .expect_stdout_match = "0.7.0\n" },
- .{ .expect_stdout_match = "0.8.0\n" },
- },
- });
- {
- const default_8 = tests.add(.{
- .name = "test-8-with-master",
- .env = master_7_and_8,
- .argv = &.{"0.8.0"},
- .check = .{ .expect_stdout_exact = "" },
- });
- tests.addWithClean(.{
- .name = "test-default-8",
- .env = default_8,
- .argv = &.{"default"},
- .check = .{ .expect_stdout_exact = "0.8.0\n" },
- });
- }
- tests.addWithClean(.{
- .name = "test-run-8",
- .env = master_7_and_8,
- .argv = &.{ "run", "0.8.0", "version" },
- .check = .{ .expect_stdout_exact = "0.8.0\n" },
- });
- tests.addWithClean(.{
- .name = "test-run-doesnotexist",
- .env = master_7_and_8,
- .argv = &.{ "run", "doesnotexist", "version" },
- .check = .{ .expect_stderr_exact = "error: compiler 'doesnotexist' does not exist, fetch it first with: zigup fetch doesnotexist\n" },
- });
- tests.addWithClean(.{
- .name = "test-clean-default-master",
- .env = master_7_and_8,
- .argv = &.{"clean"},
- .checks = &.{
- .{ .expect_stderr_match = "keeping '" },
- .{ .expect_stderr_match = "' (is default compiler)\n" },
- .{ .expect_stderr_match = "deleting '" },
- .{ .expect_stderr_match = "0.7.0'\n" },
- .{ .expect_stderr_match = "0.8.0'\n" },
- .{ .expect_stdout_exact = "" },
- },
- });
- {
- const default7 = tests.add(.{
- .name = "test-set-default-7",
- .env = master_7_and_8,
- .argv = &.{ "default", "0.7.0" },
- .checks = &.{
- .{ .expect_stdout_exact = "" },
- },
- });
- tests.addWithClean(.{
- .name = "test-clean-default-7",
- .env = default7,
- .argv = &.{"clean"},
- .checks = &.{
- .{ .expect_stderr_match = "keeping '" },
- .{ .expect_stderr_match = "' (it is master)\n" },
- .{ .expect_stderr_match = "keeping '0.7.0' (is default compiler)\n" },
- .{ .expect_stderr_match = "deleting '" },
- .{ .expect_stderr_match = "0.8.0'\n" },
- .{ .expect_stdout_exact = "" },
- },
- });
- }
- {
- const keep8 = tests.add(.{
- .name = "test-keep8",
- .env = master_7_and_8,
- .argv = &.{ "keep", "0.8.0" },
- .check = .{ .expect_stdout_exact = "" },
- });
- {
- const keep8_default_7 = tests.add(.{
- .name = "test-set-default-7-keep8",
- .env = keep8,
- .argv = &.{ "default", "0.7.0" },
- .checks = &.{
- .{ .expect_stdout_exact = "" },
- },
- });
- tests.addWithClean(.{
- .name = "test-clean-default-7-keep8",
- .env = keep8_default_7,
- .argv = &.{"clean"},
- .checks = &.{
- .{ .expect_stderr_match = "keeping '" },
- .{ .expect_stderr_match = "' (it is master)\n" },
- .{ .expect_stderr_match = "keeping '0.7.0' (is default compiler)\n" },
- .{ .expect_stderr_match = "keeping '0.8.0' (has keep file)\n" },
- .{ .expect_stdout_exact = "" },
- },
- });
- tests.addWithClean(.{
- .name = "test-clean-master",
- .env = keep8_default_7,
- .argv = &.{"clean", "master"},
- .checks = &.{
- .{ .expect_stderr_match = "deleting '" },
- .{ .expect_stderr_match = "master'\n" },
- .{ .expect_stdout_exact = "" },
- },
- });
- }
- const after_clean = tests.add(.{
- .name = "test-clean-keep8",
- .env = keep8,
- .argv = &.{"clean"},
- .checks = &.{
- .{ .expect_stderr_match = "keeping '" },
- .{ .expect_stderr_match = "' (is default compiler)\n" },
- .{ .expect_stderr_match = "keeping '0.8.0' (has keep file)\n" },
- .{ .expect_stderr_match = "deleting '" },
- .{ .expect_stderr_match = "0.7.0'\n" },
- },
- });
- tests.addWithClean(.{
- .name = "test-set-default-7-after-clean",
- .env = after_clean,
- .argv = &.{ "default", "0.7.0" },
- .checks = &.{
- .{ .expect_stderr_match = "error: compiler '0.7.0' is not installed\n" },
- },
- });
- const default8 = tests.add(.{
- .name = "test-set-default-8-after-clean",
- .env = after_clean,
- .argv = &.{ "default", "0.8.0" },
- .checks = &.{
- .{ .expect_stdout_exact = "" },
- },
- });
- tests.addWithClean(.{
- .name = "test-clean8-as-default",
- .env = default8,
- .argv = &.{ "clean", "0.8.0" },
- .checks = &.{
- .{ .expect_stderr_match = "error: cannot clean '0.8.0' (is default compiler)\n" },
- },
- });
- const after_clean8 = tests.add(.{
- .name = "test-clean8",
- .env = after_clean,
- .argv = &.{ "clean", "0.8.0" },
- .checks = &.{
- .{ .expect_stderr_match = "deleting '" },
- .{ .expect_stderr_match = "0.8.0'\n" },
- .{ .expect_stdout_exact = "" },
- },
- });
- tests.addWithClean(.{
- .name = "test-clean-after-clean8",
- .env = after_clean8,
- .argv = &.{"clean"},
- .checks = &.{
- .{ .expect_stderr_match = "keeping '" },
- .{ .expect_stderr_match = "' (is default compiler)\n" },
- .{ .expect_stdout_exact = "" },
- },
- });
- }
- }
- const native_exe_ext = builtin.os.tag.exeFileExt(builtin.cpu.arch);
- const TestOptions = struct {
- name: []const u8,
- add_path: bool = true,
- env: ?std.Build.LazyPath = null,
- setup_option: []const u8 = "no-extra-setup",
- argv: []const []const u8,
- check: ?std.Build.Step.Run.StdIo.Check = null,
- checks: []const std.Build.Step.Run.StdIo.Check = &.{},
- };
- const Tests = struct {
- b: *std.Build,
- test_step: *std.Build.Step,
- zigup_exe: *std.Build.Step.Compile,
- runtest_exe: *std.Build.Step.Compile,
- shared_options: SharedTestOptions,
- fn addWithClean(tests: Tests, opt: TestOptions) void {
- _ = tests.addCommon(opt, .yes_clean);
- }
- fn add(tests: Tests, opt: TestOptions) std.Build.LazyPath {
- return tests.addCommon(opt, .no_clean);
- }
- fn addCommon(tests: Tests, opt: TestOptions, clean_opt: enum { no_clean, yes_clean }) std.Build.LazyPath {
- const b = tests.b;
- const run = std.Build.Step.Run.create(b, b.fmt("run {s}", .{opt.name}));
- run.failing_to_execute_foreign_is_an_error = tests.shared_options.failing_to_execute_foreign_is_an_error;
- run.addArtifactArg(tests.runtest_exe);
- run.addArg(opt.name);
- run.addArg(if (opt.add_path) "--with-path" else "--no-path");
- if (opt.env) |env| {
- run.addDirectoryArg(env);
- } else {
- run.addArg("--no-input-environment");
- }
- const out_env = run.addOutputDirectoryArg(opt.name);
- run.addArg(opt.setup_option);
- run.addFileArg(tests.zigup_exe.getEmittedBin());
- run.addArgs(opt.argv);
- if (opt.check) |check| {
- run.addCheck(check);
- }
- for (opt.checks) |check| {
- run.addCheck(check);
- }
- const test_step: *std.Build.Step = switch (clean_opt) {
- .no_clean => &run.step,
- .yes_clean => &CleanDir.create(tests.b, out_env).step,
- };
- if (tests.shared_options.make_build_steps) {
- b.step(opt.name, "").dependOn(test_step);
- }
- tests.test_step.dependOn(test_step);
- return out_env;
- }
- };
- const CleanDir = struct {
- step: std.Build.Step,
- dir_path: std.Build.LazyPath,
- pub fn create(owner: *std.Build, path: std.Build.LazyPath) *CleanDir {
- const clean_dir = owner.allocator.create(CleanDir) catch @panic("OOM");
- clean_dir.* = .{
- .step = std.Build.Step.init(.{
- .id = .custom,
- .name = owner.fmt("CleanDir {s}", .{path.getDisplayName()}),
- .owner = owner,
- .makeFn = make,
- }),
- .dir_path = path.dupe(owner),
- };
- path.addStepDependencies(&clean_dir.step);
- return clean_dir;
- }
- fn make(step: *std.Build.Step, prog_node: std.Progress.Node) !void {
- _ = prog_node;
- const b = step.owner;
- const clean_dir: *CleanDir = @fieldParentPtr("step", step);
- try b.build_root.handle.deleteTree(clean_dir.dir_path.getPath(b));
- }
- };
|