zig/test/cli.zig

142 lines
5.0 KiB
Zig
Raw Normal View History

const std = @import("std");
const builtin = @import("builtin");
const testing = std.testing;
2019-05-26 10:17:34 -07:00
const process = std.process;
const fs = std.fs;
const ChildProcess = std.ChildProcess;
var a: *std.mem.Allocator = undefined;
pub fn main() !void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
2019-05-26 10:17:34 -07:00
var arg_it = process.args();
// skip my own exe name
_ = arg_it.skip();
a = &arena.allocator;
const zig_exe_rel = try (arg_it.next(a) orelse {
std.debug.warn("Expected first argument to be path to zig compiler\n");
return error.InvalidArgs;
});
const cache_root = try (arg_it.next(a) orelse {
std.debug.warn("Expected second argument to be cache root directory path\n");
return error.InvalidArgs;
});
2019-11-29 20:04:19 -08:00
const zig_exe = try fs.path.resolve(a, &[_][]const u8{zig_exe_rel});
2019-11-29 20:04:19 -08:00
const dir_path = try fs.path.join(a, &[_][]const u8{ cache_root, "clitest" });
const TestFn = fn ([]const u8, []const u8) anyerror!void;
const test_fns = [_]TestFn{
testZigInitLib,
testZigInitExe,
testGodboltApi,
testMissingOutputPath,
};
for (test_fns) |testFn| {
try fs.deleteTree(dir_path);
2019-05-26 10:17:34 -07:00
try fs.makeDir(dir_path);
try testFn(zig_exe, dir_path);
}
}
fn unwrapArg(arg: UnwrapArgError![]u8) UnwrapArgError![]u8 {
return arg catch |err| {
warn("Unable to parse command line: {}\n", err);
return err;
};
}
fn printCmd(cwd: []const u8, argv: []const []const u8) void {
std.debug.warn("cd {} && ", cwd);
for (argv) |arg| {
std.debug.warn("{} ", arg);
}
std.debug.warn("\n");
}
2019-05-26 10:17:34 -07:00
fn exec(cwd: []const u8, argv: []const []const u8) !ChildProcess.ExecResult {
const max_output_size = 100 * 1024;
2019-05-26 10:17:34 -07:00
const result = ChildProcess.exec(a, argv, cwd, null, max_output_size) catch |err| {
std.debug.warn("The following command failed:\n");
printCmd(cwd, argv);
return err;
};
switch (result.term) {
2019-05-26 10:17:34 -07:00
.Exited => |code| {
if (code != 0) {
std.debug.warn("The following command exited with error code {}:\n", code);
printCmd(cwd, argv);
std.debug.warn("stderr:\n{}\n", result.stderr);
return error.CommandFailed;
}
},
else => {
std.debug.warn("The following command terminated unexpectedly:\n");
printCmd(cwd, argv);
std.debug.warn("stderr:\n{}\n", result.stderr);
return error.CommandFailed;
},
}
return result;
}
fn testZigInitLib(zig_exe: []const u8, dir_path: []const u8) !void {
2019-11-29 20:04:19 -08:00
_ = try exec(dir_path, &[_][]const u8{ zig_exe, "init-lib" });
const test_result = try exec(dir_path, &[_][]const u8{ zig_exe, "build", "test" });
2019-11-24 16:24:52 -08:00
testing.expect(std.mem.endsWith(u8, test_result.stderr, "All 1 tests passed.\n"));
}
fn testZigInitExe(zig_exe: []const u8, dir_path: []const u8) !void {
2019-11-29 20:04:19 -08:00
_ = try exec(dir_path, &[_][]const u8{ zig_exe, "init-exe" });
const run_result = try exec(dir_path, &[_][]const u8{ zig_exe, "build", "run" });
testing.expect(std.mem.eql(u8, run_result.stderr, "All your base are belong to us.\n"));
}
fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void {
2019-05-26 10:17:34 -07:00
if (builtin.os != .linux or builtin.arch != .x86_64) return;
2019-11-29 20:04:19 -08:00
const example_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "example.zig" });
const example_s_path = try fs.path.join(a, &[_][]const u8{ dir_path, "example.s" });
try std.io.writeFile(example_zig_path,
\\// Type your code here, or load an example.
\\export fn square(num: i32) i32 {
\\ return num * num;
\\}
\\extern fn zig_panic() noreturn;
\\pub inline fn panic(msg: []const u8, error_return_trace: ?*@import("builtin").StackTrace) noreturn {
\\ zig_panic();
\\}
);
const args = [_][]const u8{
2019-05-26 10:17:34 -07:00
zig_exe, "build-obj",
"--cache-dir", dir_path,
"--name", "example",
"--output-dir", dir_path,
"--emit", "asm",
"-mllvm", "--x86-asm-syntax=intel",
"--strip", "--release-fast",
breaking changes to zig build API and improved caching * in Zig build scripts, getOutputPath() is no longer a valid function to call, unless setOutputDir() was used, or within a custom make() function. Instead there is more convenient API to use which takes advantage of the caching system. Search this commit diff for `exe.run()` for an example. * Zig build by default enables caching. All build artifacts will go into zig-cache. If you want to access build artifacts in a convenient location, it is recommended to add an `install` step. Otherwise you can use the `run()` API mentioned above to execute programs directly from their location in the cache. Closes #330. `addSystemCommand` is available for programs not built with Zig build. * Please note that Zig does no cache evicting yet. You may have to manually delete zig-cache directories periodically to keep disk usage down. It's planned for this to be a simple Least Recently Used eviction system eventually. * `--output`, `--output-lib`, and `--output-h` are removed. Instead, use `--output-dir` which defaults to the current working directory. Or take advantage of `--cache on`, which will print the main output path to stdout, and the other artifacts will be in the same directory with predictable file names. `--disable-gen-h` is available when one wants to prevent .h file generation. * `@cImport` is always independently cached now. Closes #2015. It always writes the generated Zig code to disk which makes debug info and compile errors better. No more "TODO: remember C source location to display here" * Fix .d file parsing. (Fixes the MacOS CI failure) * Zig no longer creates "temporary files" other than inside a zig-cache directory. This breaks the CLI API that Godbolt uses. The suggested new invocation can be found in this commit diff, in the changes to `test/cli.zig`.
2019-03-08 19:53:35 -08:00
example_zig_path, "--disable-gen-h",
};
2019-11-29 20:04:19 -08:00
_ = try exec(dir_path, &args);
const out_asm = try std.io.readFileAlloc(a, example_s_path);
testing.expect(std.mem.indexOf(u8, out_asm, "square:") != null);
testing.expect(std.mem.indexOf(u8, out_asm, "mov\teax, edi") != null);
testing.expect(std.mem.indexOf(u8, out_asm, "imul\teax, edi") != null);
}
fn testMissingOutputPath(zig_exe: []const u8, dir_path: []const u8) !void {
2019-11-29 20:04:19 -08:00
_ = try exec(dir_path, &[_][]const u8{ zig_exe, "init-exe" });
const output_path = try fs.path.join(a, &[_][]const u8{ "does", "not", "exist" });
const source_path = try fs.path.join(a, &[_][]const u8{ "src", "main.zig" });
_ = try exec(dir_path, &[_][]const u8{
zig_exe, "build-exe", source_path, "--output-dir", output_path,
});
}