zig build system: progress toward install and uninstall

also:
 * add std.os.path.join
 * add std.os.deleteFile
master
Andrew Kelley 2017-04-17 06:45:44 -04:00
parent e4ec2d10c6
commit 05b3082121
5 changed files with 196 additions and 47 deletions

View File

@ -233,6 +233,7 @@ install(FILES "${CMAKE_SOURCE_DIR}/std/os/index.zig" DESTINATION "${ZIG_STD_DEST
install(FILES "${CMAKE_SOURCE_DIR}/std/os/linux.zig" DESTINATION "${ZIG_STD_DEST}/os")
install(FILES "${CMAKE_SOURCE_DIR}/std/os/linux_i386.zig" DESTINATION "${ZIG_STD_DEST}/os")
install(FILES "${CMAKE_SOURCE_DIR}/std/os/linux_x86_64.zig" DESTINATION "${ZIG_STD_DEST}/os")
install(FILES "${CMAKE_SOURCE_DIR}/std/os/path.zig" DESTINATION "${ZIG_STD_DEST}/os")
install(FILES "${CMAKE_SOURCE_DIR}/std/os/windows.zig" DESTINATION "${ZIG_STD_DEST}/os")
install(FILES "${CMAKE_SOURCE_DIR}/std/rand.zig" DESTINATION "${ZIG_STD_DEST}")
install(FILES "${CMAKE_SOURCE_DIR}/std/rand_test.zig" DESTINATION "${ZIG_STD_DEST}")

View File

@ -19,6 +19,8 @@ error DependencyLoopDetected;
error NoCompilerFound;
pub const Builder = struct {
uninstall_tls: TopLevelStep,
have_uninstall_step: bool,
allocator: &Allocator,
lib_paths: List([]const u8),
include_paths: List([]const u8),
@ -33,7 +35,9 @@ pub const Builder = struct {
env_map: BufMap,
top_level_steps: List(&TopLevelStep),
prefix: []const u8,
lib_dir: []const u8,
out_dir: []u8,
installed_files: List([]const u8),
const UserInputOptionsMap = HashMap([]const u8, UserInputOption, mem.hash_slice_u8, mem.eql_slice_u8);
const AvailableOptionsMap = HashMap([]const u8, AvailableOption, mem.hash_slice_u8, mem.eql_slice_u8);
@ -85,7 +89,14 @@ pub const Builder = struct {
.default_step = undefined,
.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),
.description = "Remove build artifacts from prefix path",
},
.have_uninstall_step = false,
};
self.processNixOSEnvVars();
self.default_step = self.step("default", "Build the project");
@ -102,12 +113,8 @@ pub const Builder = struct {
}
pub fn setInstallPrefix(self: &Builder, maybe_prefix: ?[]const u8) {
if (const prefix ?= maybe_prefix) {
self.prefix = prefix;
return;
}
// TODO better default
self.prefix = "/usr/local";
self.prefix = maybe_prefix ?? "/usr/local"; // TODO better default
self.lib_dir = %%os.path.join(self.allocator, self.prefix, "lib");
}
pub fn addExecutable(self: &Builder, name: []const u8, root_src: []const u8) -> &Exe {
@ -180,6 +187,27 @@ pub const Builder = struct {
}
}
pub fn getUninstallStep(self: &Builder) -> &Step {
if (self.have_uninstall_step)
return &self.uninstall_tls.step;
%%self.top_level_steps.append(&self.uninstall_tls);
self.have_uninstall_step = true;
return &self.uninstall_tls.step;
}
fn makeUninstall(uninstall_step: &Step) -> %void {
// TODO
// const self = @fieldParentPtr(Exe, "step", step);
const self = @ptrcast(&Builder, uninstall_step);
for (self.installed_files.toSliceConst()) |installed_file| {
_ = os.deleteFile(self.allocator, installed_file);
}
// TODO remove empty directories
}
fn makeOneStep(self: &Builder, s: &Step) -> %void {
if (s.loop_flag) {
%%io.stderr.printf("Dependency loop detected:\n {}\n", s.name);
@ -426,6 +454,34 @@ pub const Builder = struct {
};
}
pub fn installCLibrary(self: &Builder, lib: &CLibrary) -> &InstallCLibraryStep {
const install_step = %%self.allocator.create(InstallCLibraryStep);
*install_step = InstallCLibraryStep.init(self, lib);
install_step.step.dependOn(&lib.step);
return install_step;
}
///::dest_rel_path is relative to prefix path
pub fn installFile(self: &Builder, src_path: []const u8, dest_rel_path: []const u8) -> &InstallFileStep {
const full_dest_path = %%os.path.join(self.allocator, self.prefix, dest_rel_path);
self.addInstalledFile(full_dest_path);
const install_step = %%self.allocator.create(InstallFileStep);
*install_step = InstallFileStep.init(self, src_path, full_dest_path);
return install_step;
}
pub fn addInstalledFile(self: &Builder, full_path: []const u8) {
_ = self.getUninstallStep();
%%self.installed_files.append(full_path);
}
fn copyFile(self: &Builder, source_path: []const u8, dest_path: []const u8) {
os.copyFile(self.allocator, source_path, dest_path) %% |err| {
debug.panic("Unable to copy {} to {}: {}", source_path, dest_path, @errorName(err));
};
}
};
const Version = struct {
@ -616,6 +672,8 @@ const CLibrary = struct {
target: Target,
builder: &Builder,
include_dirs: List([]const u8),
major_only_filename: []const u8,
name_only_filename: []const u8,
pub fn initShared(builder: &Builder, name: []const u8, version: &const Version) -> CLibrary {
return init(builder, name, version, false);
@ -639,6 +697,8 @@ const CLibrary = struct {
.link_libs = BufSet.init(builder.allocator),
.include_dirs = List([]const u8).init(builder.allocator),
.out_filename = undefined,
.major_only_filename = undefined,
.name_only_filename = undefined,
};
clib.computeOutFileName();
return clib;
@ -650,6 +710,10 @@ const CLibrary = struct {
} else {
self.out_filename = %%fmt.allocPrint(self.builder.allocator, "lib{}.so.{d}.{d}.{d}",
self.name, self.version.major, self.version.minor, self.version.patch);
self.major_only_filename = %%fmt.allocPrint(self.builder.allocator,
"lib{}.so.{d}", self.name, self.version.major);
self.name_only_filename = %%fmt.allocPrint(self.builder.allocator,
"lib{}.so", self.name);
}
}
@ -752,15 +816,11 @@ const CLibrary = struct {
builder.spawnChild(cc, cc_args.toSliceConst());
// sym link for libfoo.so.1 to libfoo.so.1.2.3
const major_only = %%fmt.allocPrint(builder.allocator, "lib{}.so.{d}", self.name, self.version.major);
defer builder.allocator.free(major_only);
_ = os.deleteFile(builder.allocator, major_only);
%%os.symLink(builder.allocator, self.out_filename, major_only);
_ = os.deleteFile(builder.allocator, self.major_only_filename);
%%os.symLink(builder.allocator, self.out_filename, self.major_only_filename);
// sym link for libfoo.so to libfoo.so.1
const name_only = %%fmt.allocPrint(builder.allocator, "lib{}.so", self.name);
defer builder.allocator.free(name_only);
_ = os.deleteFile(builder.allocator, name_only);
%%os.symLink(builder.allocator, major_only, name_only);
_ = os.deleteFile(builder.allocator, self.name_only_filename);
%%os.symLink(builder.allocator, self.major_only_filename, self.name_only_filename);
}
}
@ -938,6 +998,71 @@ const CommandStep = struct {
}
};
const InstallCLibraryStep = struct {
step: Step,
builder: &Builder,
lib: &CLibrary,
dest_file: []const u8,
pub fn init(builder: &Builder, lib: &CLibrary) -> InstallCLibraryStep {
var self = InstallCLibraryStep {
.builder = builder,
.step = Step.init(
%%fmt.allocPrint(builder.allocator, "install {}", lib.step.name),
builder.allocator, make),
.lib = lib,
.dest_file = undefined,
};
self.dest_file = %%os.path.join(builder.allocator, builder.lib_dir, lib.out_filename);
builder.addInstalledFile(self.dest_file);
if (!self.lib.static) {
builder.addInstalledFile(%%os.path.join(builder.allocator, builder.lib_dir, lib.major_only_filename));
builder.addInstalledFile(%%os.path.join(builder.allocator, builder.lib_dir, lib.name_only_filename));
}
return self;
}
fn make(step: &Step) -> %void {
// TODO issue #320
//const self = @fieldParentPtr(InstallCLibraryStep, "step", step);
const self = @ptrcast(&InstallCLibraryStep, step);
self.builder.copyFile(self.lib.out_filename, self.dest_file);
if (!self.lib.static) {
_ = os.deleteFile(self.builder.allocator, self.lib.major_only_filename);
%%os.symLink(self.builder.allocator, self.lib.out_filename, self.lib.major_only_filename);
_ = os.deleteFile(self.builder.allocator, self.lib.name_only_filename);
%%os.symLink(self.builder.allocator, self.lib.major_only_filename, self.lib.name_only_filename);
}
}
};
const InstallFileStep = struct {
step: Step,
builder: &Builder,
src_path: []const u8,
dest_path: []const u8,
pub fn init(builder: &Builder, src_path: []const u8, dest_path: []const u8) -> InstallFileStep {
return InstallFileStep {
.builder = builder,
.step = Step.init(
%%fmt.allocPrint(builder.allocator, "install {}", src_path),
builder.allocator, make),
.src_path = src_path,
.dest_path = dest_path,
};
}
fn make(step: &Step) -> %void {
// TODO issue #320
//const self = @fieldParentPtr(InstallFileStep, "step", step);
const self = @ptrcast(&InstallFileStep, step);
debug.panic("TODO install file");
}
};
const Step = struct {
name: []const u8,
makeFn: fn(self: &Step) -> %void,
@ -972,25 +1097,3 @@ const Step = struct {
fn makeNoOp(self: &Step) -> %void {}
};
fn printInvocation(exe_name: []const u8, args: &const List([]const u8)) {
%%io.stderr.printf("{}", exe_name);
for (args.toSliceConst()) |arg| {
%%io.stderr.printf(" {}", arg);
}
%%io.stderr.printf("\n");
}
fn waitForCleanExit(child: &os.ChildProcess) -> %void {
const term = %%child.wait();
switch (term) {
Term.Clean => |code| {
if (code != 0) {
return error.UncleanExit;
}
},
else => {
return error.UncleanExit;
},
};
}

View File

@ -10,6 +10,7 @@ pub const posix = switch(@compileVar("os")) {
pub const max_noalloc_path_len = 1024;
pub const ChildProcess = @import("child_process.zig").ChildProcess;
pub const path = @import("path.zig");
const debug = @import("../debug.zig");
const assert = debug.assert;
@ -24,6 +25,8 @@ const Allocator = mem.Allocator;
const BufMap = @import("../buf_map.zig").BufMap;
const cstr = @import("../cstr.zig");
const io = @import("../io.zig");
error Unexpected;
error SystemResources;
error AccessDenied;
@ -134,21 +137,21 @@ pub fn posixWrite(fd: i32, bytes: []const u8) -> %void {
}
/// ::path may need to be copied in memory to add a null terminating byte. In this case
/// ::file_path may need to be copied in memory to add a null terminating byte. In this case
/// a fixed size buffer of size ::max_noalloc_path_len is an attempted solution. If the fixed
/// size buffer is too small, and the provided allocator is null, ::error.NameTooLong is returned.
/// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory.
/// Calls POSIX open, keeps trying if it gets interrupted, and translates
/// the return value into zig errors.
pub fn posixOpen(path: []const u8, flags: usize, perm: usize, allocator: ?&Allocator) -> %i32 {
pub fn posixOpen(file_path: []const u8, flags: usize, perm: usize, allocator: ?&Allocator) -> %i32 {
var stack_buf: [max_noalloc_path_len]u8 = undefined;
var path0: []u8 = undefined;
var need_free = false;
if (path.len < stack_buf.len) {
path0 = stack_buf[0...path.len + 1];
if (file_path.len < stack_buf.len) {
path0 = stack_buf[0...file_path.len + 1];
} else if (const a ?= allocator) {
path0 = %return a.alloc(u8, path.len + 1);
path0 = %return a.alloc(u8, file_path.len + 1);
need_free = true;
} else {
return error.NameTooLong;
@ -156,8 +159,8 @@ pub fn posixOpen(path: []const u8, flags: usize, perm: usize, allocator: ?&Alloc
defer if (need_free) {
(??allocator).free(path0);
};
mem.copy(u8, path0, path);
path0[path.len] = 0;
mem.copy(u8, path0, file_path);
path0[file_path.len] = 0;
while (true) {
const result = posix.open(path0.ptr, flags, perm);
@ -416,12 +419,12 @@ pub fn symLink(allocator: &Allocator, existing_path: []const u8, new_path: []con
}
}
pub fn deleteFile(allocator: &Allocator, path: []const u8) -> %void {
const buf = %return allocator.alloc(u8, path.len + 1);
pub fn deleteFile(allocator: &Allocator, file_path: []const u8) -> %void {
const buf = %return allocator.alloc(u8, file_path.len + 1);
defer allocator.free(buf);
mem.copy(u8, buf, path);
buf[path.len] = 0;
mem.copy(u8, buf, file_path);
buf[file_path.len] = 0;
const err = posix.getErrno(posix.unlink(buf.ptr));
if (err > 0) {
@ -440,3 +443,19 @@ pub fn deleteFile(allocator: &Allocator, path: []const u8) -> %void {
};
}
}
pub fn copyFile(allocator: &Allocator, source_path: []const u8, dest_path: []const u8) -> %void {
var in_stream = %return io.InStream.open(source_path, allocator);
defer in_stream.close();
var out_stream = %return io.OutStream.open(dest_path, allocator);
defer out_stream.close();
const buf = out_stream.buffer[0...];
while (true) {
const amt = %return in_stream.read(buf);
out_stream.index = amt;
%return out_stream.flush();
if (amt != out_stream.buffer.len)
return;
}
}

25
std/os/path.zig Normal file
View File

@ -0,0 +1,25 @@
const debug = @import("../debug.zig");
const assert = debug.assert;
const mem = @import("../mem.zig");
const Allocator = mem.Allocator;
/// Allocates memory for the result, which must be freed by the caller.
pub fn join(allocator: &Allocator, dirname: []const u8, basename: []const u8) -> %[]const u8 {
const buf = %return allocator.alloc(u8, dirname.len + basename.len + 1);
%defer allocator.free(buf);
mem.copy(u8, buf, dirname);
if (dirname[dirname.len - 1] == '/') {
mem.copy(u8, buf[dirname.len...], basename);
return buf[0...buf.len - 1];
} else {
buf[dirname.len] = '/';
mem.copy(u8, buf[dirname.len + 1 ...], basename);
return buf;
}
}
test "os.path.join" {
assert(mem.eql(u8, %%join(&debug.global_allocator, "/a/b", "c"), "/a/b/c"));
assert(mem.eql(u8, %%join(&debug.global_allocator, "/a/b/", "c"), "/a/b/c"));
}

View File

@ -80,6 +80,7 @@ fn usage(builder: &Builder, maybe_zig_exe: ?[]const u8, already_ran_build: bool,
// run the build script to collect the options
if (!already_ran_build) {
builder.setInstallPrefix(null);
root.build(builder);
}