From 05b3082121e225ec2dfc9a062c765b4b60befaef Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 17 Apr 2017 06:45:44 -0400 Subject: [PATCH] zig build system: progress toward install and uninstall also: * add std.os.path.join * add std.os.deleteFile --- CMakeLists.txt | 1 + std/build.zig | 175 ++++++++++++++++++++++++++++------- std/os/index.zig | 41 +++++--- std/os/path.zig | 25 +++++ std/special/build_runner.zig | 1 + 5 files changed, 196 insertions(+), 47 deletions(-) create mode 100644 std/os/path.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index b4a5494ad7..8aea076502 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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}") diff --git a/std/build.zig b/std/build.zig index f4bf93c558..cdb3c7707c 100644 --- a/std/build.zig +++ b/std/build.zig @@ -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; - }, - }; -} diff --git a/std/os/index.zig b/std/os/index.zig index 2b40f5e8ed..1f15d441ff 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -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; + } +} diff --git a/std/os/path.zig b/std/os/path.zig new file mode 100644 index 0000000000..e8b650567a --- /dev/null +++ b/std/os/path.zig @@ -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")); +} diff --git a/std/special/build_runner.zig b/std/special/build_runner.zig index d56bf1ed18..21ab1baaf8 100644 --- a/std/special/build_runner.zig +++ b/std/special/build_runner.zig @@ -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); }