2018-09-17 21:08:56 +00:00
|
|
|
const std = @import("std");
|
2019-02-08 23:18:47 +00:00
|
|
|
const testing = std.testing;
|
2019-05-26 17:17:34 +00:00
|
|
|
const process = std.process;
|
|
|
|
const fs = std.fs;
|
|
|
|
const ChildProcess = std.ChildProcess;
|
2018-09-17 21:08:56 +00:00
|
|
|
|
|
|
|
var a: *std.mem.Allocator = undefined;
|
|
|
|
|
|
|
|
pub fn main() !void {
|
2020-01-30 06:26:54 +00:00
|
|
|
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
2018-09-17 21:08:56 +00:00
|
|
|
defer arena.deinit();
|
|
|
|
|
2019-05-26 17:17:34 +00:00
|
|
|
var arg_it = process.args();
|
2018-09-17 21:08:56 +00:00
|
|
|
|
|
|
|
// skip my own exe name
|
|
|
|
_ = arg_it.skip();
|
|
|
|
|
|
|
|
a = &arena.allocator;
|
|
|
|
|
|
|
|
const zig_exe_rel = try (arg_it.next(a) orelse {
|
2019-12-09 03:53:51 +00:00
|
|
|
std.debug.warn("Expected first argument to be path to zig compiler\n", .{});
|
2018-09-17 21:08:56 +00:00
|
|
|
return error.InvalidArgs;
|
|
|
|
});
|
|
|
|
const cache_root = try (arg_it.next(a) orelse {
|
2019-12-09 03:53:51 +00:00
|
|
|
std.debug.warn("Expected second argument to be cache root directory path\n", .{});
|
2018-09-17 21:08:56 +00:00
|
|
|
return error.InvalidArgs;
|
|
|
|
});
|
2019-11-30 04:04:19 +00:00
|
|
|
const zig_exe = try fs.path.resolve(a, &[_][]const u8{zig_exe_rel});
|
2018-09-17 21:08:56 +00:00
|
|
|
|
2019-11-30 04:04:19 +00:00
|
|
|
const dir_path = try fs.path.join(a, &[_][]const u8{ cache_root, "clitest" });
|
2018-11-13 13:08:37 +00:00
|
|
|
const TestFn = fn ([]const u8, []const u8) anyerror!void;
|
2019-06-09 23:24:24 +00:00
|
|
|
const test_fns = [_]TestFn{
|
2018-09-24 15:12:21 +00:00
|
|
|
testZigInitLib,
|
|
|
|
testZigInitExe,
|
|
|
|
testGodboltApi,
|
2019-09-11 04:25:10 +00:00
|
|
|
testMissingOutputPath,
|
2020-06-21 03:49:45 +00:00
|
|
|
testZigFmt,
|
2018-09-24 15:12:21 +00:00
|
|
|
};
|
|
|
|
for (test_fns) |testFn| {
|
2020-03-18 20:09:06 +00:00
|
|
|
try fs.cwd().deleteTree(dir_path);
|
2020-03-03 20:58:14 +00:00
|
|
|
try fs.cwd().makeDir(dir_path);
|
2018-09-24 15:12:21 +00:00
|
|
|
try testFn(zig_exe, dir_path);
|
|
|
|
}
|
2018-09-17 21:08:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn unwrapArg(arg: UnwrapArgError![]u8) UnwrapArgError![]u8 {
|
|
|
|
return arg catch |err| {
|
2019-12-09 03:53:51 +00:00
|
|
|
warn("Unable to parse command line: {}\n", .{err});
|
2018-09-17 21:08:56 +00:00
|
|
|
return err;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
fn printCmd(cwd: []const u8, argv: []const []const u8) void {
|
2019-12-09 03:53:51 +00:00
|
|
|
std.debug.warn("cd {} && ", .{cwd});
|
2018-09-17 21:08:56 +00:00
|
|
|
for (argv) |arg| {
|
2019-12-09 03:53:51 +00:00
|
|
|
std.debug.warn("{} ", .{arg});
|
2018-09-17 21:08:56 +00:00
|
|
|
}
|
2019-12-09 03:53:51 +00:00
|
|
|
std.debug.warn("\n", .{});
|
2018-09-17 21:08:56 +00:00
|
|
|
}
|
|
|
|
|
2019-05-26 17:17:34 +00:00
|
|
|
fn exec(cwd: []const u8, argv: []const []const u8) !ChildProcess.ExecResult {
|
2018-09-17 21:08:56 +00:00
|
|
|
const max_output_size = 100 * 1024;
|
2020-03-30 18:23:22 +00:00
|
|
|
const result = ChildProcess.exec(.{
|
|
|
|
.allocator = a,
|
|
|
|
.argv = argv,
|
|
|
|
.cwd = cwd,
|
|
|
|
.max_output_bytes = max_output_size,
|
|
|
|
}) catch |err| {
|
2019-12-09 03:53:51 +00:00
|
|
|
std.debug.warn("The following command failed:\n", .{});
|
2018-09-17 21:08:56 +00:00
|
|
|
printCmd(cwd, argv);
|
|
|
|
return err;
|
|
|
|
};
|
|
|
|
switch (result.term) {
|
2019-05-26 17:17:34 +00:00
|
|
|
.Exited => |code| {
|
2018-09-17 21:08:56 +00:00
|
|
|
if (code != 0) {
|
2019-12-09 03:53:51 +00:00
|
|
|
std.debug.warn("The following command exited with error code {}:\n", .{code});
|
2018-09-17 21:08:56 +00:00
|
|
|
printCmd(cwd, argv);
|
2019-12-09 03:53:51 +00:00
|
|
|
std.debug.warn("stderr:\n{}\n", .{result.stderr});
|
2018-09-17 21:08:56 +00:00
|
|
|
return error.CommandFailed;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
else => {
|
2019-12-09 03:53:51 +00:00
|
|
|
std.debug.warn("The following command terminated unexpectedly:\n", .{});
|
2018-09-17 21:08:56 +00:00
|
|
|
printCmd(cwd, argv);
|
2019-12-09 03:53:51 +00:00
|
|
|
std.debug.warn("stderr:\n{}\n", .{result.stderr});
|
2018-09-17 21:08:56 +00:00
|
|
|
return error.CommandFailed;
|
|
|
|
},
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2018-09-24 15:12:21 +00:00
|
|
|
fn testZigInitLib(zig_exe: []const u8, dir_path: []const u8) !void {
|
2019-11-30 04:04:19 +00: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-25 00:24:52 +00:00
|
|
|
testing.expect(std.mem.endsWith(u8, test_result.stderr, "All 1 tests passed.\n"));
|
2018-09-17 21:08:56 +00:00
|
|
|
}
|
|
|
|
|
2018-09-24 15:12:21 +00:00
|
|
|
fn testZigInitExe(zig_exe: []const u8, dir_path: []const u8) !void {
|
2019-11-30 04:04:19 +00:00
|
|
|
_ = try exec(dir_path, &[_][]const u8{ zig_exe, "init-exe" });
|
|
|
|
const run_result = try exec(dir_path, &[_][]const u8{ zig_exe, "build", "run" });
|
2020-09-26 01:21:21 +00:00
|
|
|
testing.expect(std.mem.eql(u8, run_result.stderr, "info: All your codebase are belong to us.\n"));
|
2018-09-17 21:08:56 +00:00
|
|
|
}
|
2018-09-24 15:12:21 +00:00
|
|
|
|
2018-11-13 13:08:37 +00:00
|
|
|
fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void {
|
2020-02-27 18:32:35 +00:00
|
|
|
if (std.Target.current.os.tag != .linux or std.Target.current.cpu.arch != .x86_64) return;
|
2018-09-24 15:12:21 +00:00
|
|
|
|
2019-11-30 04:04:19 +00: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" });
|
2018-09-24 15:12:21 +00:00
|
|
|
|
2020-03-30 18:23:22 +00:00
|
|
|
try fs.cwd().writeFile(example_zig_path,
|
2018-09-24 15:12:21 +00:00
|
|
|
\\// Type your code here, or load an example.
|
|
|
|
\\export fn square(num: i32) i32 {
|
|
|
|
\\ return num * num;
|
|
|
|
\\}
|
2018-09-24 15:14:39 +00:00
|
|
|
\\extern fn zig_panic() noreturn;
|
|
|
|
\\pub inline fn panic(msg: []const u8, error_return_trace: ?*@import("builtin").StackTrace) noreturn {
|
|
|
|
\\ zig_panic();
|
|
|
|
\\}
|
2018-09-24 15:12:21 +00:00
|
|
|
);
|
|
|
|
|
2019-06-09 23:24:24 +00:00
|
|
|
const args = [_][]const u8{
|
2019-05-26 17:17:34 +00: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-09 03:53:35 +00:00
|
|
|
example_zig_path, "--disable-gen-h",
|
2018-09-24 15:12:21 +00:00
|
|
|
};
|
2019-11-30 04:04:19 +00:00
|
|
|
_ = try exec(dir_path, &args);
|
2018-09-24 15:12:21 +00:00
|
|
|
|
2020-03-30 18:23:22 +00:00
|
|
|
const out_asm = try std.fs.cwd().readFileAlloc(a, example_s_path, std.math.maxInt(usize));
|
2019-02-08 23:18:47 +00:00
|
|
|
testing.expect(std.mem.indexOf(u8, out_asm, "square:") != null);
|
2019-02-09 23:57:39 +00:00
|
|
|
testing.expect(std.mem.indexOf(u8, out_asm, "mov\teax, edi") != null);
|
|
|
|
testing.expect(std.mem.indexOf(u8, out_asm, "imul\teax, edi") != null);
|
2018-09-24 15:12:21 +00:00
|
|
|
}
|
2019-09-11 04:25:10 +00:00
|
|
|
|
|
|
|
fn testMissingOutputPath(zig_exe: []const u8, dir_path: []const u8) !void {
|
2019-11-30 04:04:19 +00: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{
|
2019-10-18 01:46:41 +00:00
|
|
|
zig_exe, "build-exe", source_path, "--output-dir", output_path,
|
2019-09-11 04:25:10 +00:00
|
|
|
});
|
|
|
|
}
|
2020-06-21 03:49:45 +00:00
|
|
|
|
|
|
|
fn testZigFmt(zig_exe: []const u8, dir_path: []const u8) !void {
|
|
|
|
_ = try exec(dir_path, &[_][]const u8{ zig_exe, "init-exe" });
|
|
|
|
|
2020-06-21 05:20:23 +00:00
|
|
|
const unformatted_code = " // no reason for indent";
|
2020-06-21 03:49:45 +00:00
|
|
|
|
|
|
|
const fmt1_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "fmt1.zig" });
|
|
|
|
try fs.cwd().writeFile(fmt1_zig_path, unformatted_code);
|
|
|
|
|
|
|
|
const run_result1 = try exec(dir_path, &[_][]const u8{ zig_exe, "fmt", fmt1_zig_path });
|
|
|
|
// stderr should be file path + \n
|
|
|
|
testing.expect(std.mem.startsWith(u8, run_result1.stderr, fmt1_zig_path));
|
|
|
|
testing.expect(run_result1.stderr.len == fmt1_zig_path.len + 1 and run_result1.stderr[run_result1.stderr.len - 1] == '\n');
|
|
|
|
|
|
|
|
const fmt2_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "fmt2.zig" });
|
|
|
|
try fs.cwd().writeFile(fmt2_zig_path, unformatted_code);
|
|
|
|
|
|
|
|
const run_result2 = try exec(dir_path, &[_][]const u8{ zig_exe, "fmt", dir_path });
|
|
|
|
// running it on the dir, only the new file should be changed
|
|
|
|
testing.expect(std.mem.startsWith(u8, run_result2.stderr, fmt2_zig_path));
|
|
|
|
testing.expect(run_result2.stderr.len == fmt2_zig_path.len + 1 and run_result2.stderr[run_result2.stderr.len - 1] == '\n');
|
2020-06-21 05:21:23 +00:00
|
|
|
|
|
|
|
const run_result3 = try exec(dir_path, &[_][]const u8{ zig_exe, "fmt", dir_path });
|
|
|
|
// both files have been formatted, nothing should change now
|
|
|
|
testing.expect(run_result3.stderr.len == 0);
|
2020-06-21 03:49:45 +00:00
|
|
|
}
|