zig build: organize build artifacts

closes #328
This commit is contained in:
Andrew Kelley 2017-04-30 18:56:24 -04:00
parent 38a04a267c
commit 363d9038c9
16 changed files with 357 additions and 146 deletions

2
.gitignore vendored
View File

@ -1,6 +1,6 @@
zig-cache/
build/
build-release/
/.cproject
/.project
/.settings/
/test_artifacts/

View File

@ -5,19 +5,16 @@ pub fn build(b: &Builder) {
const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter");
const test_step = b.step("test", "Run all the tests");
const cleanup = b.addRemoveDirTree("test_artifacts");
test_step.dependOn(&cleanup.step);
cleanup.step.dependOn(tests.addPkgTests(b, test_filter,
test_step.dependOn(tests.addPkgTests(b, test_filter,
"test/behavior.zig", "behavior", "Run the behavior tests"));
cleanup.step.dependOn(tests.addPkgTests(b, test_filter,
test_step.dependOn(tests.addPkgTests(b, test_filter,
"std/index.zig", "std", "Run the standard library tests"));
cleanup.step.dependOn(tests.addCompareOutputTests(b, test_filter));
cleanup.step.dependOn(tests.addBuildExampleTests(b, test_filter));
cleanup.step.dependOn(tests.addCompileErrorTests(b, test_filter));
cleanup.step.dependOn(tests.addAssembleAndLinkTests(b, test_filter));
cleanup.step.dependOn(tests.addDebugSafetyTests(b, test_filter));
cleanup.step.dependOn(tests.addParseHTests(b, test_filter));
test_step.dependOn(tests.addCompareOutputTests(b, test_filter));
test_step.dependOn(tests.addBuildExampleTests(b, test_filter));
test_step.dependOn(tests.addCompileErrorTests(b, test_filter));
test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter));
test_step.dependOn(tests.addDebugSafetyTests(b, test_filter));
test_step.dependOn(tests.addParseHTests(b, test_filter));
}

View File

@ -12,7 +12,7 @@ pub fn build(b: &Builder) {
b.default_step.dependOn(&exe.step);
const run_cmd = b.addCommand(b.out_dir, b.env_map, "./test", [][]const u8{});
const run_cmd = b.addCommand(b.cache_root, b.env_map, "./test", [][]const u8{});
run_cmd.step.dependOn(&exe.step);
const test_step = b.step("test", "Test the program");

View File

@ -12,7 +12,7 @@ pub fn build(b: &Builder) {
b.default_step.dependOn(&exe.step);
const run_cmd = b.addCommand(b.out_dir, b.env_map, "./test", [][]const u8{});
const run_cmd = b.addCommand(b.cache_root, b.env_map, "./test", [][]const u8{});
run_cmd.step.dependOn(&exe.step);
const test_step = b.step("test", "Test the program");

View File

@ -1488,6 +1488,7 @@ struct CodeGen {
ZigList<TimeEvent> timing_events;
Buf *cache_dir;
Buf *out_h_path;
};
enum VarLinkage {

View File

@ -55,11 +55,12 @@ PackageTableEntry *new_package(const char *root_src_dir, const char *root_src_pa
return entry;
}
CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target) {
CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out_type) {
CodeGen *g = allocate<CodeGen>(1);
codegen_add_time_event(g, "Initialize");
g->out_type = out_type;
g->import_table.init(32);
g->builtin_fn_table.init(32);
g->primitive_type_table.init(32);
@ -74,7 +75,7 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target) {
g->external_prototypes.init(8);
g->is_release_build = false;
g->is_test_build = false;
g->want_h_file = true;
g->want_h_file = (out_type == OutTypeObj || out_type == OutTypeLib);
buf_resize(&g->global_asm, 0);
@ -145,6 +146,10 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target) {
return g;
}
void codegen_set_output_h_path(CodeGen *g, Buf *h_path) {
g->out_h_path = h_path;
}
void codegen_set_clang_argv(CodeGen *g, const char **args, size_t len) {
g->clang_argv = args;
g->clang_argv_len = len;
@ -196,10 +201,6 @@ void codegen_set_strip(CodeGen *g, bool strip) {
g->strip_debug_symbols = strip;
}
void codegen_set_out_type(CodeGen *g, OutType out_type) {
g->out_type = out_type;
}
void codegen_set_out_name(CodeGen *g, Buf *out_name) {
g->root_out_name = out_name;
}
@ -4808,15 +4809,6 @@ static void gen_global_asm(CodeGen *g) {
}
}
void codegen_build(CodeGen *g) {
assert(g->out_type != OutTypeUnknown);
init(g);
gen_global_asm(g);
gen_root_source(g);
do_code_gen(g);
}
void codegen_add_object(CodeGen *g, Buf *object_path) {
g->link_objects.append(object_path);
}
@ -4946,13 +4938,21 @@ static void get_c_type(CodeGen *g, TypeTableEntry *type_entry, Buf *out_buf) {
}
}
void codegen_generate_h_file(CodeGen *g) {
static void gen_h_file(CodeGen *g) {
if (!g->want_h_file)
return;
codegen_add_time_event(g, "Generate .h");
assert(!g->is_test_build);
Buf *h_file_out_path = buf_sprintf("%s.h", buf_ptr(g->root_out_name));
FILE *out_h = fopen(buf_ptr(h_file_out_path), "wb");
if (!g->out_h_path) {
g->out_h_path = buf_sprintf("%s.h", buf_ptr(g->root_out_name));
}
FILE *out_h = fopen(buf_ptr(g->out_h_path), "wb");
if (!out_h)
zig_panic("unable to open %s: %s", buf_ptr(h_file_out_path), strerror(errno));
zig_panic("unable to open %s: %s", buf_ptr(g->out_h_path), strerror(errno));
Buf *export_macro = buf_sprintf("%s_EXPORT", buf_ptr(g->root_out_name));
buf_upcase(export_macro);
@ -5056,3 +5056,13 @@ void codegen_print_timing_report(CodeGen *g, FILE *f) {
void codegen_add_time_event(CodeGen *g, const char *name) {
g->timing_events.append({os_get_time(), name});
}
void codegen_build(CodeGen *g) {
assert(g->out_type != OutTypeUnknown);
init(g);
gen_global_asm(g);
gen_root_source(g);
do_code_gen(g);
gen_h_file(g);
}

View File

@ -14,7 +14,7 @@
#include <stdio.h>
CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target);
CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out_type);
void codegen_set_clang_argv(CodeGen *codegen, const char **args, size_t len);
void codegen_set_is_release(CodeGen *codegen, bool is_release);
@ -25,7 +25,6 @@ void codegen_set_is_static(CodeGen *codegen, bool is_static);
void codegen_set_strip(CodeGen *codegen, bool strip);
void codegen_set_verbose(CodeGen *codegen, bool verbose);
void codegen_set_errmsg_color(CodeGen *codegen, ErrColor err_color);
void codegen_set_out_type(CodeGen *codegen, OutType out_type);
void codegen_set_out_name(CodeGen *codegen, Buf *out_name);
void codegen_set_libc_lib_dir(CodeGen *codegen, Buf *libc_lib_dir);
void codegen_set_libc_static_lib_dir(CodeGen *g, Buf *libc_static_lib_dir);
@ -48,6 +47,7 @@ void codegen_set_test_filter(CodeGen *g, Buf *filter);
void codegen_set_test_name_prefix(CodeGen *g, Buf *prefix);
void codegen_set_lib_version(CodeGen *g, size_t major, size_t minor, size_t patch);
void codegen_set_cache_dir(CodeGen *g, Buf *cache_dir);
void codegen_set_output_h_path(CodeGen *g, Buf *h_path);
void codegen_add_time_event(CodeGen *g, const char *name);
void codegen_print_timing_report(CodeGen *g, FILE *f);
void codegen_build(CodeGen *g);
@ -59,6 +59,5 @@ void codegen_add_object(CodeGen *g, Buf *object_path);
void codegen_parseh(CodeGen *g, Buf *path);
void codegen_render_ast(CodeGen *g, FILE *f, int indent_size);
void codegen_generate_h_file(CodeGen *g);
#endif

View File

@ -37,7 +37,7 @@ static Buf *build_o(CodeGen *parent_gen, const char *oname) {
os_path_join(parent_gen->zig_std_special_dir, source_basename, full_path);
ZigTarget *child_target = parent_gen->is_native_target ? nullptr : &parent_gen->zig_target;
CodeGen *child_gen = codegen_create(full_path, child_target);
CodeGen *child_gen = codegen_create(full_path, child_target, OutTypeObj);
child_gen->link_libc = parent_gen->link_libc;
child_gen->link_libs.resize(parent_gen->link_libs.length);
@ -55,7 +55,6 @@ static Buf *build_o(CodeGen *parent_gen, const char *oname) {
codegen_set_strip(child_gen, parent_gen->strip_debug_symbols);
codegen_set_is_static(child_gen, parent_gen->is_static);
codegen_set_out_type(child_gen, OutTypeObj);
codegen_set_out_name(child_gen, buf_create_from_str(oname));
codegen_set_verbose(child_gen, parent_gen->verbose);
@ -186,9 +185,10 @@ static void construct_linker_job_elf(LinkJob *lj) {
} else if (shared) {
lj->args.append("-shared");
buf_resize(&lj->out_file, 0);
buf_appendf(&lj->out_file, "lib%s.so.%zu.%zu.%zu",
buf_ptr(g->root_out_name), g->version_major, g->version_minor, g->version_patch);
if (buf_len(&lj->out_file) == 0) {
buf_appendf(&lj->out_file, "lib%s.so.%zu.%zu.%zu",
buf_ptr(g->root_out_name), g->version_major, g->version_minor, g->version_patch);
}
soname = buf_sprintf("lib%s.so.%zu", buf_ptr(g->root_out_name), g->version_major);
}
@ -752,9 +752,6 @@ void codegen_link(CodeGen *g, const char *out_file) {
}
if (g->out_type == OutTypeObj) {
if (g->want_h_file) {
codegen_generate_h_file(g);
}
if (override_out_file) {
assert(g->link_objects.length == 1);
Buf *o_file_path = g->link_objects.at(0);
@ -798,13 +795,6 @@ void codegen_link(CodeGen *g, const char *out_file) {
fprintf(stderr, "%s\n", buf_ptr(&diag));
exit(1);
}
codegen_add_time_event(g, "Generate .h");
if (g->out_type == OutTypeLib ||
g->out_type == OutTypeObj)
{
codegen_generate_h_file(g);
}
codegen_add_time_event(g, "Done");

View File

@ -35,6 +35,7 @@ static int usage(const char *arg0) {
" --libc-include-dir [path] directory where libc stdlib.h resides\n"
" --name [name] override output name\n"
" --output [file] override destination path\n"
" --output-h [file] override generated header file path\n"
" --release build with optimizations on and debug protection off\n"
" --static output will be statically linked\n"
" --strip exclude debug symbols\n"
@ -118,6 +119,8 @@ enum Cmd {
CmdTargets,
};
static const char *default_zig_cache_name = "zig-cache";
int main(int argc, char **argv) {
os_init();
@ -125,6 +128,7 @@ int main(int argc, char **argv) {
Cmd cmd = CmdInvalid;
const char *in_file = nullptr;
const char *out_file = nullptr;
const char *out_file_h = nullptr;
bool is_release_build = false;
bool strip = false;
bool is_static = false;
@ -163,7 +167,7 @@ int main(int argc, char **argv) {
size_t ver_minor = 0;
size_t ver_patch = 0;
bool timing_info = false;
const char *cache_dir = "zig-cache";
const char *cache_dir = nullptr;
if (argc >= 2 && strcmp(argv[1], "build") == 0) {
const char *zig_exe_path = arg0;
@ -200,9 +204,8 @@ int main(int argc, char **argv) {
}
}
CodeGen *g = codegen_create(build_runner_path, nullptr);
CodeGen *g = codegen_create(build_runner_path, nullptr, OutTypeExe);
codegen_set_out_name(g, buf_create_from_str("build"));
codegen_set_out_type(g, OutTypeExe);
codegen_set_verbose(g, verbose);
Buf build_file_abs = BUF_INIT;
@ -212,7 +215,12 @@ int main(int argc, char **argv) {
os_path_split(&build_file_abs, &build_file_dirname, &build_file_basename);
Buf *full_cache_dir = buf_alloc();
os_path_resolve(buf_create_from_str("."), buf_create_from_str(cache_dir), full_cache_dir);
if (cache_dir == nullptr) {
os_path_join(&build_file_dirname, buf_create_from_str(default_zig_cache_name), full_cache_dir);
} else {
os_path_resolve(buf_create_from_str("."), buf_create_from_str(cache_dir), full_cache_dir);
}
Buf *path_to_build_exe = buf_alloc();
os_path_join(full_cache_dir, buf_create_from_str("build"), path_to_build_exe);
codegen_set_cache_dir(g, full_cache_dir);
@ -309,6 +317,8 @@ int main(int argc, char **argv) {
return usage(arg0);
} else if (strcmp(arg, "--output") == 0) {
out_file = argv[i];
} else if (strcmp(arg, "--output-h") == 0) {
out_file_h = argv[i];
} else if (strcmp(arg, "--color") == 0) {
if (strcmp(argv[i], "auto") == 0) {
color = ErrColorAuto;
@ -396,6 +406,7 @@ int main(int argc, char **argv) {
cmd = CmdParseH;
} else if (strcmp(arg, "test") == 0) {
cmd = CmdTest;
out_type = OutTypeExe;
} else if (strcmp(arg, "targets") == 0) {
cmd = CmdTargets;
} else {
@ -495,9 +506,11 @@ int main(int argc, char **argv) {
Buf *zig_root_source_file = (cmd == CmdParseH) ? nullptr : in_file_buf;
Buf *full_cache_dir = buf_alloc();
os_path_resolve(buf_create_from_str("."), buf_create_from_str(cache_dir), full_cache_dir);
os_path_resolve(buf_create_from_str("."),
buf_create_from_str((cache_dir == nullptr) ? default_zig_cache_name : cache_dir),
full_cache_dir);
CodeGen *g = codegen_create(zig_root_source_file, target);
CodeGen *g = codegen_create(zig_root_source_file, target, out_type);
codegen_set_out_name(g, buf_out_name);
codegen_set_lib_version(g, ver_major, ver_minor, ver_patch);
codegen_set_is_release(g, is_release_build);
@ -510,11 +523,6 @@ int main(int argc, char **argv) {
codegen_set_clang_argv(g, clang_argv.items, clang_argv.length);
codegen_set_strip(g, strip);
codegen_set_is_static(g, is_static);
if (out_type != OutTypeUnknown) {
codegen_set_out_type(g, out_type);
} else if (cmd == CmdTest) {
codegen_set_out_type(g, OutTypeExe);
}
if (libc_lib_dir)
codegen_set_libc_lib_dir(g, buf_create_from_str(libc_lib_dir));
if (libc_static_lib_dir)
@ -568,6 +576,9 @@ int main(int argc, char **argv) {
codegen_set_test_name_prefix(g, buf_create_from_str(test_name_prefix));
}
if (out_file_h)
codegen_set_output_h_path(g, buf_create_from_str(out_file_h));
if (cmd == CmdBuild) {
for (size_t i = 0; i < objects.length; i += 1) {
codegen_add_object(g, buf_create_from_str(objects.at(i)));

View File

@ -37,7 +37,6 @@ pub const Builder = struct {
top_level_steps: List(&TopLevelStep),
prefix: []const u8,
lib_dir: []const u8,
out_dir: []u8, // TODO get rid of this
installed_files: List([]const u8),
build_root: []const u8,
cache_root: []const u8,
@ -97,7 +96,6 @@ pub const Builder = struct {
.env_map = %%os.getEnvMap(allocator),
.prefix = undefined,
.lib_dir = undefined,
.out_dir = %%os.getCwd(allocator),
.installed_files = List([]const u8).init(allocator),
.uninstall_tls = TopLevelStep {
.step = Step.init("uninstall", allocator, makeUninstall),
@ -111,7 +109,6 @@ pub const Builder = struct {
}
pub fn deinit(self: &Builder) {
self.allocator.free(self.out_dir);
self.lib_paths.deinit();
self.include_paths.deinit();
self.rpaths.deinit();
@ -172,12 +169,10 @@ pub const Builder = struct {
return exe;
}
pub fn addCommand(self: &Builder, cwd: []const u8, env_map: &const BufMap,
pub fn addCommand(self: &Builder, cwd: ?[]const u8, env_map: &const BufMap,
path: []const u8, args: []const []const u8) -> &CommandStep
{
const cmd = %%self.allocator.create(CommandStep);
*cmd = CommandStep.init(self, cwd, env_map, path, args);
return cmd;
return CommandStep.create(self, cwd, env_map, path, args);
}
pub fn addWriteFile(self: &Builder, file_path: []const u8, data: []const u8) -> &WriteFileStep {
@ -489,21 +484,22 @@ pub const Builder = struct {
}
fn spawnChild(self: &Builder, exe_path: []const u8, args: []const []const u8) -> %void {
return self.spawnChildEnvMap(&self.env_map, exe_path, args);
return self.spawnChildEnvMap(null, &self.env_map, exe_path, args);
}
fn spawnChildEnvMap(self: &Builder, env_map: &const BufMap, exe_path: []const u8,
args: []const []const u8) -> %void
fn spawnChildEnvMap(self: &Builder, cwd: ?[]const u8, env_map: &const BufMap,
exe_path: []const u8, args: []const []const u8) -> %void
{
if (self.verbose) {
%%io.stderr.printf("{}", exe_path);
test (cwd) |yes_cwd| %%io.stderr.print("cd {}; ", yes_cwd);
%%io.stderr.print("{}", exe_path);
for (args) |arg| {
%%io.stderr.printf(" {}", arg);
%%io.stderr.print(" {}", arg);
}
%%io.stderr.printf("\n");
}
var child = os.ChildProcess.spawn(exe_path, args, env_map,
var child = os.ChildProcess.spawn(exe_path, args, cwd, env_map,
StdIo.Ignore, StdIo.Inherit, StdIo.Inherit, self.allocator) %% |err|
{
%%io.stderr.printf("Unable to spawn {}: {}\n", exe_path, @errorName(err));
@ -511,6 +507,7 @@ pub const Builder = struct {
};
const term = child.wait() %% |err| {
test (cwd) |yes_cwd| %%io.stderr.printf("cwd: {}\n", yes_cwd);
%%io.stderr.printf("Unable to spawn {}: {}\n", exe_path, @errorName(err));
return err;
};
@ -560,11 +557,12 @@ pub const Builder = struct {
fn copyFile(self: &Builder, source_path: []const u8, dest_path: []const u8) {
const dirname = os.path.dirname(dest_path);
const abs_source_path = self.pathFromRoot(source_path);
os.makePath(self.allocator, dirname) %% |err| {
debug.panic("Unable to create path {}: {}", dirname, @errorName(err));
};
os.copyFile(self.allocator, source_path, dest_path) %% |err| {
debug.panic("Unable to copy {} to {}: {}", source_path, dest_path, @errorName(err));
os.copyFile(self.allocator, abs_source_path, dest_path) %% |err| {
debug.panic("Unable to copy {} to {}: {}", abs_source_path, dest_path, @errorName(err));
};
}
@ -628,8 +626,10 @@ pub const LibExeObjStep = struct {
release: bool,
static: bool,
output_path: ?[]const u8,
output_h_path: ?[]const u8,
kind: Kind,
version: Version,
out_h_filename: []const u8,
out_filename: []const u8,
out_filename_major_only: []const u8,
out_filename_name_only: []const u8,
@ -684,8 +684,10 @@ pub const LibExeObjStep = struct {
.link_libs = BufSet.init(builder.allocator),
.step = Step.init(name, builder.allocator, make),
.output_path = null,
.output_h_path = null,
.version = *ver,
.out_filename = undefined,
.out_h_filename = builder.fmt("{}.h", name),
.out_filename_major_only = undefined,
.out_filename_name_only = undefined,
.object_files = List([]const u8).init(builder.allocator),
@ -747,6 +749,27 @@ pub const LibExeObjStep = struct {
self.output_path = value;
}
pub fn getOutputPath(self: &LibExeObjStep) -> []const u8 {
test (self.output_path) |output_path| {
output_path
} else {
const wanted_path = %%os.path.join(self.builder.allocator, self.builder.cache_root, self.out_filename);
%%os.path.relative(self.builder.allocator, self.builder.build_root, wanted_path)
}
}
pub fn setOutputHPath(self: &LibExeObjStep, value: []const u8) {
self.output_h_path = value;
}
pub fn getOutputHPath(self: &LibExeObjStep) -> []const u8 {
test (self.output_h_path) |output_h_path| {
output_h_path
} else {
%%os.path.join(self.builder.allocator, self.builder.cache_root, self.out_h_filename)
}
}
pub fn addAssemblyFile(self: &LibExeObjStep, path: []const u8) {
%%self.assembly_files.append(path);
}
@ -763,14 +786,7 @@ pub const LibExeObjStep = struct {
self.step.dependOn(&obj.step);
const path_to_obj = test (obj.output_path) |explicit_out_path| {
explicit_out_path
} else {
// TODO make it so we always know where this will be
%%os.path.join(self.builder.allocator, self.builder.cache_root,
self.builder.fmt("{}{}", obj.name, obj.target.oFileExt()))
};
%%self.object_files.append(path_to_obj);
%%self.object_files.append(obj.getOutputPath());
}
fn make(step: &Step) -> %void {
@ -814,10 +830,16 @@ pub const LibExeObjStep = struct {
%%zig_args.append("--release");
}
test (self.output_path) |output_path| {
%%zig_args.append("--output");
%%zig_args.append(builder.pathFromRoot(output_path));
}
%%zig_args.append("--cache-dir");
%%zig_args.append(builder.cache_root);
const output_path = builder.pathFromRoot(self.getOutputPath());
%%zig_args.append("--output");
%%zig_args.append(output_path);
const output_h_path = self.getOutputHPath();
%%zig_args.append("--output-h");
%%zig_args.append(builder.pathFromRoot(output_h_path));
%%zig_args.append("--name");
%%zig_args.append(self.name);
@ -879,10 +901,8 @@ pub const LibExeObjStep = struct {
%return builder.spawnChild(builder.zig_exe, zig_args.toSliceConst());
if (self.kind == Kind.Lib and !self.static) {
// sym link for libfoo.so.1 to libfoo.so.1.2.3
%%os.atomicSymLink(builder.allocator, self.out_filename, self.out_filename_major_only);
// sym link for libfoo.so to libfoo.so.1
%%os.atomicSymLink(builder.allocator, self.out_filename_major_only, self.out_filename_name_only);
%return doAtomicSymLinks(builder.allocator, output_path, self.out_filename_major_only,
self.out_filename_name_only);
}
}
};
@ -987,10 +1007,12 @@ pub const TestStep = struct {
}
};
// TODO merge with CExecutable
pub const CLibrary = struct {
step: Step,
name: []const u8,
out_filename: []const u8,
output_path: ?[]const u8,
static: bool,
version: Version,
cflags: List([]const u8),
@ -1024,6 +1046,7 @@ pub const CLibrary = struct {
.step = Step.init(name, builder.allocator, make),
.link_libs = BufSet.init(builder.allocator),
.include_dirs = List([]const u8).init(builder.allocator),
.output_path = null,
.out_filename = undefined,
.major_only_filename = undefined,
.name_only_filename = undefined,
@ -1043,6 +1066,19 @@ pub const CLibrary = struct {
}
}
pub fn setOutputPath(self: &CLibrary, value: []const u8) {
self.output_path = value;
}
pub fn getOutputPath(self: &CLibrary) -> []const u8 {
test (self.output_path) |output_path| {
output_path
} else {
const wanted_path = %%os.path.join(self.builder.allocator, self.builder.cache_root, self.out_filename);
%%os.path.relative(self.builder.allocator, self.builder.build_root, wanted_path)
}
}
pub fn linkSystemLibrary(self: &CLibrary, name: []const u8) {
%%self.link_libs.put(name);
}
@ -1131,8 +1167,9 @@ pub const CLibrary = struct {
defer builder.allocator.free(soname_arg);
%%cc_args.append(soname_arg);
const output_path = builder.pathFromRoot(self.getOutputPath());
%%cc_args.append("-o");
%%cc_args.append(self.out_filename);
%%cc_args.append(output_path);
for (self.object_files.toSliceConst()) |object_file| {
%%cc_args.append(builder.pathFromRoot(object_file));
@ -1140,10 +1177,8 @@ pub const CLibrary = struct {
%return builder.spawnChild(cc, cc_args.toSliceConst());
// sym link for libfoo.so.1 to libfoo.so.1.2.3
%%os.atomicSymLink(builder.allocator, self.out_filename, self.major_only_filename);
// sym link for libfoo.so to libfoo.so.1
%%os.atomicSymLink(builder.allocator, self.major_only_filename, self.name_only_filename);
%return doAtomicSymLinks(builder.allocator, output_path, self.major_only_filename,
self.name_only_filename);
}
}
@ -1169,9 +1204,11 @@ pub const CExecutable = struct {
link_libs: BufSet,
target: Target,
include_dirs: List([]const u8),
output_path: ?[]const u8,
out_filename: []const u8,
pub fn init(builder: &Builder, name: []const u8) -> CExecutable {
CExecutable {
var self = CExecutable {
.builder = builder,
.name = name,
.target = Target.Native,
@ -1182,6 +1219,27 @@ pub const CExecutable = struct {
.step = Step.init(name, builder.allocator, make),
.link_libs = BufSet.init(builder.allocator),
.include_dirs = List([]const u8).init(builder.allocator),
.output_path = null,
.out_filename = undefined,
};
self.computeOutFileName();
return self;
}
fn computeOutFileName(self: &CExecutable) {
self.out_filename = self.builder.fmt("{}{}", self.name, self.target.exeFileExt());
}
pub fn setOutputPath(self: &CExecutable, value: []const u8) {
self.output_path = value;
}
pub fn getOutputPath(self: &CExecutable) -> []const u8 {
test (self.output_path) |output_path| {
self.builder.pathFromRoot(output_path)
} else {
const wanted_path = %%os.path.join(self.builder.allocator, self.builder.cache_root, self.out_filename);
%%os.path.relative(self.builder.allocator, self.builder.build_root, wanted_path)
}
}
@ -1191,15 +1249,15 @@ pub const CExecutable = struct {
pub fn linkCLibrary(self: &CExecutable, clib: &CLibrary) {
self.step.dependOn(&clib.step);
%%self.full_path_libs.append(clib.out_filename);
%%self.full_path_libs.append(clib.getOutputPath());
}
pub fn linkLibrary(self: &CExecutable, lib: &LibExeObjStep) {
assert(lib.kind == LibExeObjStep.Kind.Lib);
self.step.dependOn(&lib.step);
%%self.full_path_libs.append(lib.out_filename);
%%self.full_path_libs.append(lib.getOutputPath());
// TODO should be some kind of isolated directory that only has this header in it
%%self.include_dirs.append(self.builder.out_dir);
%%self.include_dirs.append(self.builder.cache_root);
}
pub fn addObject(self: &CExecutable, obj: &LibExeObjStep) {
@ -1207,12 +1265,10 @@ pub const CExecutable = struct {
self.step.dependOn(&obj.step);
// TODO make it so we always know where this will be
%%self.object_files.append(%%os.path.join(self.builder.allocator, self.builder.cache_root,
self.builder.fmt("{}{}", obj.name, obj.target.oFileExt())));
%%self.object_files.append(obj.getOutputPath());
// TODO should be some kind of isolated directory that only has this header in it
%%self.include_dirs.append(self.builder.out_dir);
%%self.include_dirs.append(self.builder.cache_root);
}
pub fn addSourceFile(self: &CExecutable, file: []const u8) {
@ -1256,7 +1312,6 @@ pub const CExecutable = struct {
%%cc_args.append("-c");
%%cc_args.append(builder.pathFromRoot(source_file));
// TODO don't dump the .o file in the same place as the source file
const rel_src_path = %%os.path.relative(builder.allocator, builder.build_root, source_file);
const cache_o_src = %%os.path.join(builder.allocator, builder.cache_root, rel_src_path);
const cache_o_dir = os.path.dirname(cache_o_src);
@ -1276,26 +1331,28 @@ pub const CExecutable = struct {
%return builder.spawnChild(cc, cc_args.toSliceConst());
%%self.object_files.append(cache_o_file);
%%self.object_files.append(%%os.path.relative(builder.allocator, builder.build_root, cache_o_file));
}
%%cc_args.resize(0);
for (self.object_files.toSliceConst()) |object_file| {
%%cc_args.append(object_file);
%%cc_args.append(builder.pathFromRoot(object_file));
}
const output_path = builder.pathFromRoot(self.getOutputPath());
%%cc_args.append("-o");
%%cc_args.append(self.name);
%%cc_args.append(output_path);
const rpath_arg = builder.fmt("-Wl,-rpath,{}", builder.out_dir);
const rpath_arg = builder.fmt("-Wl,-rpath,{}",
%%os.path.real(builder.allocator, builder.cache_root));
defer builder.allocator.free(rpath_arg);
%%cc_args.append(rpath_arg);
%%cc_args.append("-rdynamic");
for (self.full_path_libs.toSliceConst()) |full_path_lib| {
%%cc_args.append(full_path_lib);
%%cc_args.append(builder.pathFromRoot(full_path_lib));
}
%return builder.spawnChild(cc, cc_args.toSliceConst());
@ -1317,27 +1374,29 @@ pub const CommandStep = struct {
builder: &Builder,
exe_path: []const u8,
args: []const []const u8,
cwd: []const u8,
cwd: ?[]const u8,
env_map: &const BufMap,
pub fn init(builder: &Builder, cwd: []const u8, env_map: &const BufMap,
exe_path: []const u8, args: []const []const u8) -> CommandStep
pub fn create(builder: &Builder, cwd: ?[]const u8, env_map: &const BufMap,
exe_path: []const u8, args: []const []const u8) -> &CommandStep
{
CommandStep {
const self = %%builder.allocator.create(CommandStep);
*self = CommandStep {
.builder = builder,
.step = Step.init(exe_path, builder.allocator, make),
.exe_path = exe_path,
.args = args,
.cwd = cwd,
.env_map = env_map,
}
};
return self;
}
fn make(step: &Step) -> %void {
const self = @fieldParentPtr(CommandStep, "step", step);
// TODO set cwd
return self.builder.spawnChildEnvMap(self.env_map, self.exe_path, self.args);
const cwd = test (self.cwd) |cwd| self.builder.pathFromRoot(cwd) else null;
return self.builder.spawnChildEnvMap(cwd, self.env_map, self.exe_path, self.args);
}
};
@ -1367,12 +1426,10 @@ pub const InstallCLibraryStep = struct {
const self = @fieldParentPtr(InstallCLibraryStep, "step", step);
const builder = self.builder;
self.builder.copyFile(self.lib.out_filename, self.dest_file);
self.builder.copyFile(self.lib.getOutputPath(), self.dest_file);
if (!self.lib.static) {
const dest_major_only = %%os.path.join(builder.allocator, builder.lib_dir, self.lib.major_only_filename);
const dest_name_only = %%os.path.join(builder.allocator, builder.lib_dir, self.lib.name_only_filename);
%%os.atomicSymLink(self.builder.allocator, self.lib.out_filename, dest_major_only);
%%os.atomicSymLink(self.builder.allocator, self.lib.major_only_filename, dest_name_only);
%return doAtomicSymLinks(self.builder.allocator, self.dest_file, self.lib.major_only_filename,
self.lib.name_only_filename);
}
}
};
@ -1506,3 +1563,22 @@ pub const Step = struct {
fn makeNoOp(self: &Step) -> %void {}
};
fn doAtomicSymLinks(allocator: &Allocator, output_path: []const u8, filename_major_only: []const u8,
filename_name_only: []const u8) -> %void
{
const out_dir = os.path.dirname(output_path);
const out_basename = os.path.basename(output_path);
// sym link for libfoo.so.1 to libfoo.so.1.2.3
const major_only_path = %%os.path.join(allocator, out_dir, filename_major_only);
os.atomicSymLink(allocator, out_basename, major_only_path) %% |err| {
%%io.stderr.printf("Unable to symlink {} -> {}\n", major_only_path, out_basename);
return err;
};
// sym link for libfoo.so to libfoo.so.1
const name_only_path = %%os.path.join(allocator, out_dir, filename_name_only);
os.atomicSymLink(allocator, filename_major_only, name_only_path) %% |err| {
%%io.stderr.printf("Unable to symlink {} -> {}\n", name_only_path, filename_major_only);
return err;
};
}

View File

@ -345,17 +345,17 @@ fn bufPrintWrite(context: &BufPrintContext, bytes: []const u8) -> bool {
return true;
}
pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) {
pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) -> []u8 {
var context = BufPrintContext { .remaining = buf, };
_ = format(&context, bufPrintWrite, fmt, args);
return buf[0...buf.len - context.remaining.len];
}
pub fn allocPrint(allocator: &mem.Allocator, comptime fmt: []const u8, args: ...) -> %[]u8 {
var size: usize = 0;
_ = format(&size, countSize, fmt, args);
const buf = %return allocator.alloc(u8, size);
bufPrint(buf, fmt, args);
return buf;
return bufPrint(buf, fmt, args);
}
fn countSize(size: &usize, bytes: []const u8) -> bool {

View File

@ -30,12 +30,13 @@ pub const ChildProcess = struct {
Close,
};
pub fn spawn(exe_path: []const u8, args: []const []const u8, env_map: &const BufMap,
pub fn spawn(exe_path: []const u8, args: []const []const u8,
cwd: ?[]const u8, env_map: &const BufMap,
stdin: StdIo, stdout: StdIo, stderr: StdIo, allocator: &Allocator) -> %ChildProcess
{
switch (@compileVar("os")) {
Os.linux, Os.macosx, Os.ios, Os.darwin => {
return spawnPosix(exe_path, args, env_map, stdin, stdout, stderr, allocator);
return spawnPosix(exe_path, args, cwd, env_map, stdin, stdout, stderr, allocator);
},
else => @compileError("Unsupported OS"),
}
@ -98,7 +99,8 @@ pub const ChildProcess = struct {
};
}
fn spawnPosix(exe_path: []const u8, args: []const []const u8, env_map: &const BufMap,
fn spawnPosix(exe_path: []const u8, args: []const []const u8,
maybe_cwd: ?[]const u8, env_map: &const BufMap,
stdin: StdIo, stdout: StdIo, stderr: StdIo, allocator: &Allocator) -> %ChildProcess
{
// TODO issue #295
@ -155,6 +157,11 @@ pub const ChildProcess = struct {
setUpChildIo(stderr, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) %%
|err| forkChildErrReport(err_pipe[1], err);
test (maybe_cwd) |cwd| {
os.changeCurDir(allocator, cwd) %%
|err| forkChildErrReport(err_pipe[1], err);
}
os.posixExecve(exe_path, args, env_map, allocator) %%
|err| forkChildErrReport(err_pipe[1], err);
}

View File

@ -780,3 +780,60 @@ pub const Dir = struct {
};
}
};
pub fn changeCurDir(allocator: &Allocator, dir_path: []const u8) -> %void {
const path_buf = %return allocator.alloc(u8, dir_path.len + 1);
defer allocator.free(path_buf);
mem.copy(u8, path_buf, dir_path);
path_buf[dir_path.len] = 0;
const err = posix.getErrno(posix.chdir(path_buf.ptr));
if (err > 0) {
return switch (err) {
errno.EACCES => error.AccessDenied,
errno.EFAULT => unreachable,
errno.EIO => error.FileSystem,
errno.ELOOP => error.SymLinkLoop,
errno.ENAMETOOLONG => error.NameTooLong,
errno.ENOENT => error.FileNotFound,
errno.ENOMEM => error.SystemResources,
errno.ENOTDIR => error.NotDir,
else => error.Unexpected,
};
}
}
/// Read value of a symbolic link.
pub fn readLink(allocator: &Allocator, pathname: []const u8) -> %[]u8 {
const path_buf = %return allocator.alloc(u8, pathname.len + 1);
defer allocator.free(path_buf);
mem.copy(u8, path_buf, pathname);
path_buf[pathname.len] = 0;
var result_buf = %return allocator.alloc(u8, 1024);
%defer allocator.free(result_buf);
while (true) {
const ret_val = posix.readlink(path_buf.ptr, result_buf.ptr, result_buf.len);
const err = posix.getErrno(ret_val);
if (err > 0) {
return switch (err) {
errno.EACCES => error.AccessDenied,
errno.EFAULT, errno.EINVAL => unreachable,
errno.EIO => error.FileSystem,
errno.ELOOP => error.SymLinkLoop,
errno.ENAMETOOLONG => error.NameTooLong,
errno.ENOENT => error.FileNotFound,
errno.ENOMEM => error.SystemResources,
errno.ENOTDIR => error.NotDir,
else => error.Unexpected,
};
}
if (ret_val == result_buf.len) {
result_buf = %return allocator.realloc(u8, result_buf, result_buf.len * 2);
continue;
}
return result_buf[0...ret_val];
}
}

View File

@ -335,6 +335,10 @@ pub fn dup2(old: i32, new: i32) -> usize {
arch.syscall2(arch.SYS_dup2, usize(old), usize(new))
}
pub fn chdir(path: &const u8) -> usize {
arch.syscall1(arch.SYS_chdir, usize(path))
}
pub fn execve(path: &const u8, argv: &const ?&const u8, envp: &const ?&const u8) -> usize {
arch.syscall3(arch.SYS_execve, usize(path), usize(argv), usize(envp))
}
@ -356,6 +360,10 @@ pub fn isatty(fd: i32) -> bool {
return arch.syscall3(arch.SYS_ioctl, usize(fd), TIOCGWINSZ, usize(&wsz)) == 0;
}
pub fn readlink(noalias path: &const u8, noalias buf_ptr: &u8, buf_len: usize) -> usize {
arch.syscall3(arch.SYS_readlink, usize(path), usize(buf_ptr), buf_len)
}
pub fn mkdir(path: &const u8, mode: usize) -> usize {
arch.syscall2(arch.SYS_mkdir, usize(path), mode)
}

View File

@ -1,9 +1,11 @@
const debug = @import("../debug.zig");
const assert = debug.assert;
const mem = @import("../mem.zig");
const fmt = @import("../fmt.zig");
const Allocator = mem.Allocator;
const os = @import("index.zig");
const math = @import("../math.zig");
const posix = os.posix;
pub const sep = switch (@compileVar("os")) {
Os.windows => '\\',
@ -185,6 +187,45 @@ fn testDirname(input: []const u8, expected_output: []const u8) {
assert(mem.eql(u8, dirname(input), expected_output));
}
pub fn basename(path: []const u8) -> []const u8 {
if (path.len == 0)
return []u8{};
var end_index: usize = path.len - 1;
while (path[end_index] == '/') {
if (end_index == 0)
return []u8{};
end_index -= 1;
}
var start_index: usize = end_index;
end_index += 1;
while (path[start_index] != '/') {
if (start_index == 0)
return path[0...end_index];
start_index -= 1;
}
return path[start_index + 1...end_index];
}
test "os.path.basename" {
testBasename("", "");
testBasename("/", "");
testBasename("/dir/basename.ext", "basename.ext");
testBasename("/basename.ext", "basename.ext");
testBasename("basename.ext", "basename.ext");
testBasename("basename.ext/", "basename.ext");
testBasename("basename.ext//", "basename.ext");
testBasename("/aaa/bbb", "bbb");
testBasename("/aaa/", "aaa");
testBasename("/aaa/b", "b");
testBasename("/a/b", "b");
testBasename("//a", "a");
}
fn testBasename(input: []const u8, expected_output: []const u8) {
assert(mem.eql(u8, basename(input), expected_output));
}
/// Returns the relative path from ::from to ::to. If ::from and ::to each
/// resolve to the same path (after calling ::resolve on each), a zero-length
/// string is returned.
@ -252,3 +293,17 @@ fn testRelative(from: []const u8, to: []const u8, expected_output: []const u8) {
const result = %%relative(&debug.global_allocator, from, to);
assert(mem.eql(u8, result, expected_output));
}
/// Return the canonicalized absolute pathname.
/// Expands all symbolic links and resolves references to `.`, `..`, and
/// extra `/` characters in ::pathname.
/// Caller must deallocate result.
pub fn real(allocator: &Allocator, pathname: []const u8) -> %[]u8 {
const fd = %return os.posixOpen(pathname, posix.O_PATH|posix.O_NONBLOCK|posix.O_CLOEXEC, 0, allocator);
defer os.posixClose(fd);
var buf: ["/proc/self/fd/-2147483648".len]u8 = undefined;
const proc_path = fmt.bufPrint(buf[0...], "/proc/self/fd/{}", fd);
return os.readLink(allocator, proc_path);
}

View File

@ -189,7 +189,7 @@ pub const CompareOutputContext = struct {
%%io.stderr.printf("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
var child = os.ChildProcess.spawn(full_exe_path, [][]u8{}, &b.env_map,
var child = os.ChildProcess.spawn(full_exe_path, [][]u8{}, null, &b.env_map,
StdIo.Ignore, StdIo.Pipe, StdIo.Pipe, b.allocator) %% |err|
{
debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err));
@ -264,7 +264,7 @@ pub const CompareOutputContext = struct {
%%io.stderr.printf("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
var child = os.ChildProcess.spawn(full_exe_path, [][]u8{}, &b.env_map,
var child = os.ChildProcess.spawn(full_exe_path, [][]u8{}, null, &b.env_map,
StdIo.Ignore, StdIo.Pipe, StdIo.Pipe, b.allocator) %% |err|
{
debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err));
@ -344,8 +344,8 @@ pub const CompareOutputContext = struct {
pub fn addCase(self: &CompareOutputContext, case: &const TestCase) {
const b = self.b;
const root_src = %%os.path.join(b.allocator, "test_artifacts", case.sources.items[0].filename);
const exe_path = %%os.path.join(b.allocator, "test_artifacts", "test");
const root_src = %%os.path.join(b.allocator, b.cache_root, case.sources.items[0].filename);
const exe_path = %%os.path.join(b.allocator, b.cache_root, "test");
switch (case.special) {
Special.Asm => {
@ -360,7 +360,7 @@ pub const CompareOutputContext = struct {
exe.setOutputPath(exe_path);
for (case.sources.toSliceConst()) |src_file| {
const expanded_src_path = %%os.path.join(b.allocator, "test_artifacts", src_file.filename);
const expanded_src_path = %%os.path.join(b.allocator, b.cache_root, src_file.filename);
const write_src = b.addWriteFile(expanded_src_path, src_file.source);
exe.step.dependOn(&write_src.step);
}
@ -388,7 +388,7 @@ pub const CompareOutputContext = struct {
}
for (case.sources.toSliceConst()) |src_file| {
const expanded_src_path = %%os.path.join(b.allocator, "test_artifacts", src_file.filename);
const expanded_src_path = %%os.path.join(b.allocator, b.cache_root, src_file.filename);
const write_src = b.addWriteFile(expanded_src_path, src_file.source);
exe.step.dependOn(&write_src.step);
}
@ -401,7 +401,7 @@ pub const CompareOutputContext = struct {
}
},
Special.DebugSafety => {
const obj_path = %%os.path.join(b.allocator, "test_artifacts", "test.o");
const obj_path = %%os.path.join(b.allocator, b.cache_root, "test.o");
const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "debug-safety {}", case.name);
test (self.test_filter) |filter| {
if (mem.indexOf(u8, annotated_case_name, filter) == null)
@ -415,7 +415,7 @@ pub const CompareOutputContext = struct {
}
for (case.sources.toSliceConst()) |src_file| {
const expanded_src_path = %%os.path.join(b.allocator, "test_artifacts", src_file.filename);
const expanded_src_path = %%os.path.join(b.allocator, b.cache_root, src_file.filename);
const write_src = b.addWriteFile(expanded_src_path, src_file.source);
exe.step.dependOn(&write_src.step);
}
@ -488,8 +488,8 @@ pub const CompileErrorContext = struct {
const self = @fieldParentPtr(CompileCmpOutputStep, "step", step);
const b = self.context.b;
const root_src = %%os.path.join(b.allocator, "test_artifacts", self.case.sources.items[0].filename);
const obj_path = %%os.path.join(b.allocator, "test_artifacts", "test.o");
const root_src = %%os.path.join(b.allocator, b.cache_root, self.case.sources.items[0].filename);
const obj_path = %%os.path.join(b.allocator, b.cache_root, "test.o");
var zig_args = List([]const u8).init(b.allocator);
%%zig_args.append(if (self.case.is_exe) "build_exe" else "build_obj");
@ -511,7 +511,7 @@ pub const CompileErrorContext = struct {
printInvocation(b.zig_exe, zig_args.toSliceConst());
}
var child = os.ChildProcess.spawn(b.zig_exe, zig_args.toSliceConst(), &b.env_map,
var child = os.ChildProcess.spawn(b.zig_exe, zig_args.toSliceConst(), null, &b.env_map,
StdIo.Ignore, StdIo.Pipe, StdIo.Pipe, b.allocator) %% |err|
{
debug.panic("Unable to spawn {}: {}\n", b.zig_exe, @errorName(err));
@ -632,7 +632,7 @@ pub const CompileErrorContext = struct {
self.step.dependOn(&compile_and_cmp_errors.step);
for (case.sources.toSliceConst()) |src_file| {
const expanded_src_path = %%os.path.join(b.allocator, "test_artifacts", src_file.filename);
const expanded_src_path = %%os.path.join(b.allocator, b.cache_root, src_file.filename);
const write_src = b.addWriteFile(expanded_src_path, src_file.source);
compile_and_cmp_errors.step.dependOn(&write_src.step);
}
@ -675,7 +675,7 @@ pub const BuildExamplesContext = struct {
%%zig_args.append("--verbose");
}
const run_cmd = b.addCommand(b.cache_root, b.env_map, b.zig_exe, zig_args.toSliceConst());
const run_cmd = b.addCommand(null, b.env_map, b.zig_exe, zig_args.toSliceConst());
const log_step = b.addLog("PASS {}\n", annotated_case_name);
log_step.step.dependOn(&run_cmd.step);
@ -762,7 +762,7 @@ pub const ParseHContext = struct {
const self = @fieldParentPtr(ParseHCmpOutputStep, "step", step);
const b = self.context.b;
const root_src = %%os.path.join(b.allocator, "test_artifacts", self.case.sources.items[0].filename);
const root_src = %%os.path.join(b.allocator, b.cache_root, self.case.sources.items[0].filename);
var zig_args = List([]const u8).init(b.allocator);
%%zig_args.append("parseh");
@ -774,7 +774,7 @@ pub const ParseHContext = struct {
printInvocation(b.zig_exe, zig_args.toSliceConst());
}
var child = os.ChildProcess.spawn(b.zig_exe, zig_args.toSliceConst(), &b.env_map,
var child = os.ChildProcess.spawn(b.zig_exe, zig_args.toSliceConst(), null, &b.env_map,
StdIo.Ignore, StdIo.Pipe, StdIo.Pipe, b.allocator) %% |err|
{
debug.panic("Unable to spawn {}: {}\n", b.zig_exe, @errorName(err));
@ -886,7 +886,7 @@ pub const ParseHContext = struct {
self.step.dependOn(&parseh_and_cmp.step);
for (case.sources.toSliceConst()) |src_file| {
const expanded_src_path = %%os.path.join(b.allocator, "test_artifacts", src_file.filename);
const expanded_src_path = %%os.path.join(b.allocator, b.cache_root, src_file.filename);
const write_src = b.addWriteFile(expanded_src_path, src_file.source);
parseh_and_cmp.step.dependOn(&write_src.step);
}