zig/lib/std/special/build_runner.zig

220 lines
8.7 KiB
Zig
Raw Normal View History

const root = @import("@build");
const std = @import("std");
2018-02-07 23:08:45 -08:00
const builtin = @import("builtin");
const io = std.io;
const fmt = std.fmt;
const Builder = std.build.Builder;
const mem = std.mem;
2019-05-26 10:17:34 -07:00
const process = std.process;
const ArrayList = std.ArrayList;
const warn = std.debug.warn;
2019-05-24 19:52:07 -07:00
const File = std.fs.File;
2018-01-31 19:48:40 -08:00
pub fn main() !void {
// Here we use an ArenaAllocator backed by a DirectAllocator because a build is a short-lived,
// one shot program. We don't need to waste time freeing memory and finding places to squish
// bytes into. So we free everything all at once at the very end.
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const allocator = &arena.allocator;
var args = try process.argsAlloc(allocator);
defer process.argsFree(allocator, args);
// skip my own exe name
var arg_idx: usize = 1;
const zig_exe = nextArg(args, &arg_idx) orelse {
warn("Expected first argument to be path to zig compiler\n", .{});
return error.InvalidArgs;
};
const build_root = nextArg(args, &arg_idx) orelse {
warn("Expected second argument to be build root directory path\n", .{});
return error.InvalidArgs;
};
const cache_root = nextArg(args, &arg_idx) orelse {
warn("Expected third argument to be cache root directory path\n", .{});
return error.InvalidArgs;
};
const builder = try Builder.create(allocator, zig_exe, build_root, cache_root);
defer builder.destroy();
var targets = ArrayList([]const u8).init(allocator);
2020-03-10 17:22:30 -07:00
const stderr_stream = io.getStdErr().outStream();
const stdout_stream = io.getStdOut().outStream();
while (nextArg(args, &arg_idx)) |arg| {
if (mem.startsWith(u8, arg, "-D")) {
const option_contents = arg[2..];
if (option_contents.len == 0) {
warn("Expected option name after '-D'\n\n", .{});
return usageAndErr(builder, false, stderr_stream);
}
if (mem.indexOfScalar(u8, option_contents, '=')) |name_end| {
const option_name = option_contents[0..name_end];
2018-05-30 13:09:11 -07:00
const option_value = option_contents[name_end + 1 ..];
if (try builder.addUserInputOption(option_name, option_value))
return usageAndErr(builder, false, stderr_stream);
} else {
if (try builder.addUserInputFlag(option_contents))
return usageAndErr(builder, false, stderr_stream);
}
} else if (mem.startsWith(u8, arg, "-")) {
if (mem.eql(u8, arg, "--verbose")) {
builder.verbose = true;
} else if (mem.eql(u8, arg, "--help")) {
return usage(builder, false, stdout_stream);
} else if (mem.eql(u8, arg, "--prefix")) {
builder.install_prefix = nextArg(args, &arg_idx) orelse {
warn("Expected argument after --prefix\n\n", .{});
return usageAndErr(builder, false, stderr_stream);
};
2017-12-23 17:21:57 -08:00
} else if (mem.eql(u8, arg, "--search-prefix")) {
const search_prefix = nextArg(args, &arg_idx) orelse {
warn("Expected argument after --search-prefix\n\n", .{});
return usageAndErr(builder, false, stderr_stream);
};
2017-12-23 17:21:57 -08:00
builder.addSearchPrefix(search_prefix);
stage1 is now a hybrid of C++ and Zig This modifies the build process of Zig to put all of the source files into libcompiler.a, except main.cpp and userland.cpp. Next, the build process links main.cpp, userland.cpp, and libcompiler.a into zig1. userland.cpp is a shim for functions that will later be replaced with self-hosted implementations. Next, the build process uses zig1 to build src-self-hosted/stage1.zig into libuserland.a, which does not depend on any of the things that are shimmed in userland.cpp, such as translate-c. Finally, the build process re-links main.cpp and libcompiler.a, except with libuserland.a instead of userland.cpp. Now the shims are replaced with .zig code. This provides all of the Zig standard library to the stage1 C++ compiler, and enables us to move certain things to userland, such as translate-c. As a proof of concept I have made the `zig zen` command use text defined in userland. I added `zig translate-c-2` which is a work-in-progress reimplementation of translate-c in userland, which currently calls `std.debug.panic("unimplemented")` and you can see the stack trace makes it all the way back into the C++ main() function (Thanks LemonBoy for improving that!). This could potentially let us move other things into userland, such as hashing algorithms, the entire cache system, .d file parsing, pretty much anything that libuserland.a itself doesn't need to depend on. This can also let us have `zig fmt` in stage1 without the overhead of child process execution, and without the initial compilation delay before it gets cached. See #1964
2019-04-16 13:47:47 -07:00
} else if (mem.eql(u8, arg, "--override-lib-dir")) {
builder.override_lib_dir = nextArg(args, &arg_idx) orelse {
warn("Expected argument after --override-lib-dir\n\n", .{});
return usageAndErr(builder, false, stderr_stream);
};
} else if (mem.eql(u8, arg, "--verbose-tokenize")) {
builder.verbose_tokenize = true;
} else if (mem.eql(u8, arg, "--verbose-ast")) {
builder.verbose_ast = true;
} else if (mem.eql(u8, arg, "--verbose-link")) {
builder.verbose_link = true;
} else if (mem.eql(u8, arg, "--verbose-ir")) {
builder.verbose_ir = true;
} else if (mem.eql(u8, arg, "--verbose-llvm-ir")) {
builder.verbose_llvm_ir = true;
} else if (mem.eql(u8, arg, "--verbose-cimport")) {
builder.verbose_cimport = true;
} else if (mem.eql(u8, arg, "--verbose-cc")) {
builder.verbose_cc = true;
} else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) {
builder.verbose_llvm_cpu_features = true;
} else if (mem.eql(u8, arg, "--")) {
builder.args = argsRest(args, arg_idx);
break;
} else {
warn("Unrecognized argument: {}\n\n", .{arg});
return usageAndErr(builder, false, stderr_stream);
}
} else {
try targets.append(arg);
}
}
builder.resolveInstallPrefix();
try runBuild(builder);
if (builder.validateUserInputDidItFail())
return usageAndErr(builder, true, stderr_stream);
builder.make(targets.toSliceConst()) catch |err| {
switch (err) {
error.InvalidStepName => {
return usageAndErr(builder, true, stderr_stream);
},
2019-05-26 10:17:34 -07:00
error.UncleanExit => process.exit(1),
else => return err,
}
};
}
fn runBuild(builder: *Builder) anyerror!void {
switch (@typeInfo(@TypeOf(root.build).ReturnType)) {
.Void => root.build(builder),
.ErrorUnion => try root.build(builder),
2018-02-07 23:08:45 -08:00
else => @compileError("expected return type of build to be 'void' or '!void'"),
}
}
fn usage(builder: *Builder, already_ran_build: bool, out_stream: var) !void {
// run the build script to collect the options
if (!already_ran_build) {
builder.setInstallPrefix(null);
builder.resolveInstallPrefix();
2018-02-07 23:08:45 -08:00
try runBuild(builder);
}
try out_stream.print(
\\Usage: {} build [steps] [options]
\\
\\Steps:
\\
, .{builder.zig_exe});
const allocator = builder.allocator;
for (builder.top_level_steps.toSliceConst()) |top_level_step| {
const name = if (&top_level_step.step == builder.default_step)
try fmt.allocPrint(allocator, "{} (default)", .{top_level_step.step.name})
else
top_level_step.step.name;
try out_stream.print(" {s:22} {}\n", .{ name, top_level_step.description });
}
2020-03-10 17:22:30 -07:00
try out_stream.writeAll(
\\
\\General Options:
\\ --help Print this help and exit
\\ --verbose Print commands before executing them
\\ --prefix [path] Override default install prefix
2017-12-23 17:21:57 -08:00
\\ --search-prefix [path] Add a path to look for binaries, libraries, headers
\\
\\Project-Specific Options:
\\
);
if (builder.available_options_list.len == 0) {
try out_stream.print(" (none)\n", .{});
} else {
for (builder.available_options_list.toSliceConst()) |option| {
const name = try fmt.allocPrint(allocator, " -D{}=[{}]", .{
option.name,
Builder.typeIdName(option.type_id),
});
defer allocator.free(name);
try out_stream.print("{s:24} {}\n", .{ name, option.description });
}
}
2020-03-10 17:22:30 -07:00
try out_stream.writeAll(
\\
\\Advanced Options:
\\ --build-file [file] Override path to build.zig
\\ --cache-dir [path] Override path to zig cache directory
\\ --override-lib-dir [arg] Override path to Zig lib directory
\\ --verbose-tokenize Enable compiler debug output for tokenization
\\ --verbose-ast Enable compiler debug output for parsing into an AST
\\ --verbose-link Enable compiler debug output for linking
\\ --verbose-ir Enable compiler debug output for Zig IR
\\ --verbose-llvm-ir Enable compiler debug output for LLVM IR
\\ --verbose-cimport Enable compiler debug output for C imports
\\ --verbose-cc Enable compiler debug output for C compilation
\\ --verbose-llvm-cpu-features Enable compiler debug output for LLVM CPU features
\\
);
}
fn usageAndErr(builder: *Builder, already_ran_build: bool, out_stream: var) void {
usage(builder, already_ran_build, out_stream) catch {};
2019-05-26 10:17:34 -07:00
process.exit(1);
}
fn nextArg(args: [][]const u8, idx: *usize) ?[]const u8 {
if (idx.* >= args.len) return null;
defer idx.* += 1;
return args[idx.*];
}
2018-02-07 23:08:45 -08:00
fn argsRest(args: [][]const u8, idx: usize) ?[][]const u8 {
if (idx >= args.len) return null;
return args[idx..];
}