authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-04-17 06:45:44-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-04-17 06:47:20-04:00
log05b3082121e225ec2dfc9a062c765b4b60befaef
tree166aedf2cbfd92e847e16d27a5c847757b66e704
parente4ec2d10c67542589e10e1eaf569157284171159

zig build system: progress toward install and uninstall

also: * add std.os.path.join * add std.os.deleteFile

5 files changed, 196 insertions(+), 47 deletions(-)

CMakeLists.txt+1
...@@ -233,6 +233,7 @@ install(FILES "${CMAKE_SOURCE_DIR}/std/os/index.zig" DESTINATION "${ZIG_STD_DEST...@@ -233,6 +233,7 @@ install(FILES "${CMAKE_SOURCE_DIR}/std/os/index.zig" DESTINATION "${ZIG_STD_DEST
233install(FILES "${CMAKE_SOURCE_DIR}/std/os/linux.zig" DESTINATION "${ZIG_STD_DEST}/os")233install(FILES "${CMAKE_SOURCE_DIR}/std/os/linux.zig" DESTINATION "${ZIG_STD_DEST}/os")
234install(FILES "${CMAKE_SOURCE_DIR}/std/os/linux_i386.zig" DESTINATION "${ZIG_STD_DEST}/os")234install(FILES "${CMAKE_SOURCE_DIR}/std/os/linux_i386.zig" DESTINATION "${ZIG_STD_DEST}/os")
235install(FILES "${CMAKE_SOURCE_DIR}/std/os/linux_x86_64.zig" DESTINATION "${ZIG_STD_DEST}/os")235install(FILES "${CMAKE_SOURCE_DIR}/std/os/linux_x86_64.zig" DESTINATION "${ZIG_STD_DEST}/os")
236install(FILES "${CMAKE_SOURCE_DIR}/std/os/path.zig" DESTINATION "${ZIG_STD_DEST}/os")
236install(FILES "${CMAKE_SOURCE_DIR}/std/os/windows.zig" DESTINATION "${ZIG_STD_DEST}/os")237install(FILES "${CMAKE_SOURCE_DIR}/std/os/windows.zig" DESTINATION "${ZIG_STD_DEST}/os")
237install(FILES "${CMAKE_SOURCE_DIR}/std/rand.zig" DESTINATION "${ZIG_STD_DEST}")238install(FILES "${CMAKE_SOURCE_DIR}/std/rand.zig" DESTINATION "${ZIG_STD_DEST}")
238install(FILES "${CMAKE_SOURCE_DIR}/std/rand_test.zig" DESTINATION "${ZIG_STD_DEST}")239install(FILES "${CMAKE_SOURCE_DIR}/std/rand_test.zig" DESTINATION "${ZIG_STD_DEST}")
std/build.zig+139-36
...@@ -19,6 +19,8 @@ error DependencyLoopDetected;...@@ -19,6 +19,8 @@ error DependencyLoopDetected;
19error NoCompilerFound;19error NoCompilerFound;
2020
21pub const Builder = struct {21pub const Builder = struct {
22 uninstall_tls: TopLevelStep,
23 have_uninstall_step: bool,
22 allocator: &Allocator,24 allocator: &Allocator,
23 lib_paths: List([]const u8),25 lib_paths: List([]const u8),
24 include_paths: List([]const u8),26 include_paths: List([]const u8),
...@@ -33,7 +35,9 @@ pub const Builder = struct {...@@ -33,7 +35,9 @@ pub const Builder = struct {
33 env_map: BufMap,35 env_map: BufMap,
34 top_level_steps: List(&TopLevelStep),36 top_level_steps: List(&TopLevelStep),
35 prefix: []const u8,37 prefix: []const u8,
38 lib_dir: []const u8,
36 out_dir: []u8,39 out_dir: []u8,
40 installed_files: List([]const u8),
3741
38 const UserInputOptionsMap = HashMap([]const u8, UserInputOption, mem.hash_slice_u8, mem.eql_slice_u8);42 const UserInputOptionsMap = HashMap([]const u8, UserInputOption, mem.hash_slice_u8, mem.eql_slice_u8);
39 const AvailableOptionsMap = HashMap([]const u8, AvailableOption, mem.hash_slice_u8, mem.eql_slice_u8);43 const AvailableOptionsMap = HashMap([]const u8, AvailableOption, mem.hash_slice_u8, mem.eql_slice_u8);
...@@ -85,7 +89,14 @@ pub const Builder = struct {...@@ -85,7 +89,14 @@ pub const Builder = struct {
85 .default_step = undefined,89 .default_step = undefined,
86 .env_map = %%os.getEnvMap(allocator),90 .env_map = %%os.getEnvMap(allocator),
87 .prefix = undefined,91 .prefix = undefined,
92 .lib_dir = undefined,
88 .out_dir = %%os.getCwd(allocator),93 .out_dir = %%os.getCwd(allocator),
94 .installed_files = List([]const u8).init(allocator),
95 .uninstall_tls = TopLevelStep {
96 .step = Step.init("uninstall", allocator, makeUninstall),
97 .description = "Remove build artifacts from prefix path",
98 },
99 .have_uninstall_step = false,
89 };100 };
90 self.processNixOSEnvVars();101 self.processNixOSEnvVars();
91 self.default_step = self.step("default", "Build the project");102 self.default_step = self.step("default", "Build the project");
...@@ -102,12 +113,8 @@ pub const Builder = struct {...@@ -102,12 +113,8 @@ pub const Builder = struct {
102 }113 }
103114
104 pub fn setInstallPrefix(self: &Builder, maybe_prefix: ?[]const u8) {115 pub fn setInstallPrefix(self: &Builder, maybe_prefix: ?[]const u8) {
105 if (const prefix ?= maybe_prefix) {116 self.prefix = maybe_prefix ?? "/usr/local"; // TODO better default
106 self.prefix = prefix;117 self.lib_dir = %%os.path.join(self.allocator, self.prefix, "lib");
107 return;
108 }
109 // TODO better default
110 self.prefix = "/usr/local";
111 }118 }
112119
113 pub fn addExecutable(self: &Builder, name: []const u8, root_src: []const u8) -> &Exe {120 pub fn addExecutable(self: &Builder, name: []const u8, root_src: []const u8) -> &Exe {
...@@ -180,6 +187,27 @@ pub const Builder = struct {...@@ -180,6 +187,27 @@ pub const Builder = struct {
180 }187 }
181 }188 }
182189
190 pub fn getUninstallStep(self: &Builder) -> &Step {
191 if (self.have_uninstall_step)
192 return &self.uninstall_tls.step;
193
194 %%self.top_level_steps.append(&self.uninstall_tls);
195 self.have_uninstall_step = true;
196 return &self.uninstall_tls.step;
197 }
198
199 fn makeUninstall(uninstall_step: &Step) -> %void {
200 // TODO
201 // const self = @fieldParentPtr(Exe, "step", step);
202 const self = @ptrcast(&Builder, uninstall_step);
203
204 for (self.installed_files.toSliceConst()) |installed_file| {
205 _ = os.deleteFile(self.allocator, installed_file);
206 }
207
208 // TODO remove empty directories
209 }
210
183 fn makeOneStep(self: &Builder, s: &Step) -> %void {211 fn makeOneStep(self: &Builder, s: &Step) -> %void {
184 if (s.loop_flag) {212 if (s.loop_flag) {
185 %%io.stderr.printf("Dependency loop detected:\n {}\n", s.name);213 %%io.stderr.printf("Dependency loop detected:\n {}\n", s.name);
...@@ -426,6 +454,34 @@ pub const Builder = struct {...@@ -426,6 +454,34 @@ pub const Builder = struct {
426 };454 };
427455
428 }456 }
457
458 pub fn installCLibrary(self: &Builder, lib: &CLibrary) -> &InstallCLibraryStep {
459 const install_step = %%self.allocator.create(InstallCLibraryStep);
460 *install_step = InstallCLibraryStep.init(self, lib);
461 install_step.step.dependOn(&lib.step);
462 return install_step;
463 }
464
465 ///::dest_rel_path is relative to prefix path
466 pub fn installFile(self: &Builder, src_path: []const u8, dest_rel_path: []const u8) -> &InstallFileStep {
467 const full_dest_path = %%os.path.join(self.allocator, self.prefix, dest_rel_path);
468 self.addInstalledFile(full_dest_path);
469
470 const install_step = %%self.allocator.create(InstallFileStep);
471 *install_step = InstallFileStep.init(self, src_path, full_dest_path);
472 return install_step;
473 }
474
475 pub fn addInstalledFile(self: &Builder, full_path: []const u8) {
476 _ = self.getUninstallStep();
477 %%self.installed_files.append(full_path);
478 }
479
480 fn copyFile(self: &Builder, source_path: []const u8, dest_path: []const u8) {
481 os.copyFile(self.allocator, source_path, dest_path) %% |err| {
482 debug.panic("Unable to copy {} to {}: {}", source_path, dest_path, @errorName(err));
483 };
484 }
429};485};
430486
431const Version = struct {487const Version = struct {
...@@ -616,6 +672,8 @@ const CLibrary = struct {...@@ -616,6 +672,8 @@ const CLibrary = struct {
616 target: Target,672 target: Target,
617 builder: &Builder,673 builder: &Builder,
618 include_dirs: List([]const u8),674 include_dirs: List([]const u8),
675 major_only_filename: []const u8,
676 name_only_filename: []const u8,
619677
620 pub fn initShared(builder: &Builder, name: []const u8, version: &const Version) -> CLibrary {678 pub fn initShared(builder: &Builder, name: []const u8, version: &const Version) -> CLibrary {
621 return init(builder, name, version, false);679 return init(builder, name, version, false);
...@@ -639,6 +697,8 @@ const CLibrary = struct {...@@ -639,6 +697,8 @@ const CLibrary = struct {
639 .link_libs = BufSet.init(builder.allocator),697 .link_libs = BufSet.init(builder.allocator),
640 .include_dirs = List([]const u8).init(builder.allocator),698 .include_dirs = List([]const u8).init(builder.allocator),
641 .out_filename = undefined,699 .out_filename = undefined,
700 .major_only_filename = undefined,
701 .name_only_filename = undefined,
642 };702 };
643 clib.computeOutFileName();703 clib.computeOutFileName();
644 return clib;704 return clib;
...@@ -650,6 +710,10 @@ const CLibrary = struct {...@@ -650,6 +710,10 @@ const CLibrary = struct {
650 } else {710 } else {
651 self.out_filename = %%fmt.allocPrint(self.builder.allocator, "lib{}.so.{d}.{d}.{d}",711 self.out_filename = %%fmt.allocPrint(self.builder.allocator, "lib{}.so.{d}.{d}.{d}",
652 self.name, self.version.major, self.version.minor, self.version.patch);712 self.name, self.version.major, self.version.minor, self.version.patch);
713 self.major_only_filename = %%fmt.allocPrint(self.builder.allocator,
714 "lib{}.so.{d}", self.name, self.version.major);
715 self.name_only_filename = %%fmt.allocPrint(self.builder.allocator,
716 "lib{}.so", self.name);
653 }717 }
654 }718 }
655719
...@@ -752,15 +816,11 @@ const CLibrary = struct {...@@ -752,15 +816,11 @@ const CLibrary = struct {
752 builder.spawnChild(cc, cc_args.toSliceConst());816 builder.spawnChild(cc, cc_args.toSliceConst());
753817
754 // sym link for libfoo.so.1 to libfoo.so.1.2.3818 // sym link for libfoo.so.1 to libfoo.so.1.2.3
755 const major_only = %%fmt.allocPrint(builder.allocator, "lib{}.so.{d}", self.name, self.version.major);819 _ = os.deleteFile(builder.allocator, self.major_only_filename);
756 defer builder.allocator.free(major_only);820 %%os.symLink(builder.allocator, self.out_filename, self.major_only_filename);
757 _ = os.deleteFile(builder.allocator, major_only);
758 %%os.symLink(builder.allocator, self.out_filename, major_only);
759 // sym link for libfoo.so to libfoo.so.1821 // sym link for libfoo.so to libfoo.so.1
760 const name_only = %%fmt.allocPrint(builder.allocator, "lib{}.so", self.name);822 _ = os.deleteFile(builder.allocator, self.name_only_filename);
761 defer builder.allocator.free(name_only);823 %%os.symLink(builder.allocator, self.major_only_filename, self.name_only_filename);
762 _ = os.deleteFile(builder.allocator, name_only);
763 %%os.symLink(builder.allocator, major_only, name_only);
764 }824 }
765 }825 }
766826
...@@ -938,6 +998,71 @@ const CommandStep = struct {...@@ -938,6 +998,71 @@ const CommandStep = struct {
938 }998 }
939};999};
9401000
1001const InstallCLibraryStep = struct {
1002 step: Step,
1003 builder: &Builder,
1004 lib: &CLibrary,
1005 dest_file: []const u8,
1006
1007 pub fn init(builder: &Builder, lib: &CLibrary) -> InstallCLibraryStep {
1008 var self = InstallCLibraryStep {
1009 .builder = builder,
1010 .step = Step.init(
1011 %%fmt.allocPrint(builder.allocator, "install {}", lib.step.name),
1012 builder.allocator, make),
1013 .lib = lib,
1014 .dest_file = undefined,
1015 };
1016 self.dest_file = %%os.path.join(builder.allocator, builder.lib_dir, lib.out_filename);
1017 builder.addInstalledFile(self.dest_file);
1018 if (!self.lib.static) {
1019 builder.addInstalledFile(%%os.path.join(builder.allocator, builder.lib_dir, lib.major_only_filename));
1020 builder.addInstalledFile(%%os.path.join(builder.allocator, builder.lib_dir, lib.name_only_filename));
1021 }
1022 return self;
1023 }
1024
1025 fn make(step: &Step) -> %void {
1026 // TODO issue #320
1027 //const self = @fieldParentPtr(InstallCLibraryStep, "step", step);
1028 const self = @ptrcast(&InstallCLibraryStep, step);
1029
1030 self.builder.copyFile(self.lib.out_filename, self.dest_file);
1031 if (!self.lib.static) {
1032 _ = os.deleteFile(self.builder.allocator, self.lib.major_only_filename);
1033 %%os.symLink(self.builder.allocator, self.lib.out_filename, self.lib.major_only_filename);
1034 _ = os.deleteFile(self.builder.allocator, self.lib.name_only_filename);
1035 %%os.symLink(self.builder.allocator, self.lib.major_only_filename, self.lib.name_only_filename);
1036 }
1037 }
1038};
1039
1040const InstallFileStep = struct {
1041 step: Step,
1042 builder: &Builder,
1043 src_path: []const u8,
1044 dest_path: []const u8,
1045
1046 pub fn init(builder: &Builder, src_path: []const u8, dest_path: []const u8) -> InstallFileStep {
1047 return InstallFileStep {
1048 .builder = builder,
1049 .step = Step.init(
1050 %%fmt.allocPrint(builder.allocator, "install {}", src_path),
1051 builder.allocator, make),
1052 .src_path = src_path,
1053 .dest_path = dest_path,
1054 };
1055 }
1056
1057 fn make(step: &Step) -> %void {
1058 // TODO issue #320
1059 //const self = @fieldParentPtr(InstallFileStep, "step", step);
1060 const self = @ptrcast(&InstallFileStep, step);
1061
1062 debug.panic("TODO install file");
1063 }
1064};
1065
941const Step = struct {1066const Step = struct {
942 name: []const u8,1067 name: []const u8,
943 makeFn: fn(self: &Step) -> %void,1068 makeFn: fn(self: &Step) -> %void,
...@@ -972,25 +1097,3 @@ const Step = struct {...@@ -972,25 +1097,3 @@ const Step = struct {
9721097
973 fn makeNoOp(self: &Step) -> %void {}1098 fn makeNoOp(self: &Step) -> %void {}
974};1099};
975
976fn printInvocation(exe_name: []const u8, args: &const List([]const u8)) {
977 %%io.stderr.printf("{}", exe_name);
978 for (args.toSliceConst()) |arg| {
979 %%io.stderr.printf(" {}", arg);
980 }
981 %%io.stderr.printf("\n");
982}
983
984fn waitForCleanExit(child: &os.ChildProcess) -> %void {
985 const term = %%child.wait();
986 switch (term) {
987 Term.Clean => |code| {
988 if (code != 0) {
989 return error.UncleanExit;
990 }
991 },
992 else => {
993 return error.UncleanExit;
994 },
995 };
996}
std/os/index.zig+30-11
...@@ -10,6 +10,7 @@ pub const posix = switch(@compileVar("os")) {...@@ -10,6 +10,7 @@ pub const posix = switch(@compileVar("os")) {
1010
11pub const max_noalloc_path_len = 1024;11pub const max_noalloc_path_len = 1024;
12pub const ChildProcess = @import("child_process.zig").ChildProcess;12pub const ChildProcess = @import("child_process.zig").ChildProcess;
13pub const path = @import("path.zig");
1314
14const debug = @import("../debug.zig");15const debug = @import("../debug.zig");
15const assert = debug.assert;16const assert = debug.assert;
...@@ -24,6 +25,8 @@ const Allocator = mem.Allocator;...@@ -24,6 +25,8 @@ const Allocator = mem.Allocator;
24const BufMap = @import("../buf_map.zig").BufMap;25const BufMap = @import("../buf_map.zig").BufMap;
25const cstr = @import("../cstr.zig");26const cstr = @import("../cstr.zig");
2627
28const io = @import("../io.zig");
29
27error Unexpected;30error Unexpected;
28error SystemResources;31error SystemResources;
29error AccessDenied;32error AccessDenied;
...@@ -134,21 +137,21 @@ pub fn posixWrite(fd: i32, bytes: []const u8) -> %void {...@@ -134,21 +137,21 @@ pub fn posixWrite(fd: i32, bytes: []const u8) -> %void {
134}137}
135138
136139
137/// ::path may need to be copied in memory to add a null terminating byte. In this case140/// ::file_path may need to be copied in memory to add a null terminating byte. In this case
138/// a fixed size buffer of size ::max_noalloc_path_len is an attempted solution. If the fixed141/// a fixed size buffer of size ::max_noalloc_path_len is an attempted solution. If the fixed
139/// size buffer is too small, and the provided allocator is null, ::error.NameTooLong is returned.142/// size buffer is too small, and the provided allocator is null, ::error.NameTooLong is returned.
140/// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory.143/// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory.
141/// Calls POSIX open, keeps trying if it gets interrupted, and translates144/// Calls POSIX open, keeps trying if it gets interrupted, and translates
142/// the return value into zig errors.145/// the return value into zig errors.
143pub fn posixOpen(path: []const u8, flags: usize, perm: usize, allocator: ?&Allocator) -> %i32 {146pub fn posixOpen(file_path: []const u8, flags: usize, perm: usize, allocator: ?&Allocator) -> %i32 {
144 var stack_buf: [max_noalloc_path_len]u8 = undefined;147 var stack_buf: [max_noalloc_path_len]u8 = undefined;
145 var path0: []u8 = undefined;148 var path0: []u8 = undefined;
146 var need_free = false;149 var need_free = false;
147150
148 if (path.len < stack_buf.len) {151 if (file_path.len < stack_buf.len) {
149 path0 = stack_buf[0...path.len + 1];152 path0 = stack_buf[0...file_path.len + 1];
150 } else if (const a ?= allocator) {153 } else if (const a ?= allocator) {
151 path0 = %return a.alloc(u8, path.len + 1);154 path0 = %return a.alloc(u8, file_path.len + 1);
152 need_free = true;155 need_free = true;
153 } else {156 } else {
154 return error.NameTooLong;157 return error.NameTooLong;
...@@ -156,8 +159,8 @@ pub fn posixOpen(path: []const u8, flags: usize, perm: usize, allocator: ?&Alloc...@@ -156,8 +159,8 @@ pub fn posixOpen(path: []const u8, flags: usize, perm: usize, allocator: ?&Alloc
156 defer if (need_free) {159 defer if (need_free) {
157 (??allocator).free(path0);160 (??allocator).free(path0);
158 };161 };
159 mem.copy(u8, path0, path);162 mem.copy(u8, path0, file_path);
160 path0[path.len] = 0;163 path0[file_path.len] = 0;
161164
162 while (true) {165 while (true) {
163 const result = posix.open(path0.ptr, flags, perm);166 const result = posix.open(path0.ptr, flags, perm);
...@@ -416,12 +419,12 @@ pub fn symLink(allocator: &Allocator, existing_path: []const u8, new_path: []con...@@ -416,12 +419,12 @@ pub fn symLink(allocator: &Allocator, existing_path: []const u8, new_path: []con
416 }419 }
417}420}
418421
419pub fn deleteFile(allocator: &Allocator, path: []const u8) -> %void {422pub fn deleteFile(allocator: &Allocator, file_path: []const u8) -> %void {
420 const buf = %return allocator.alloc(u8, path.len + 1);423 const buf = %return allocator.alloc(u8, file_path.len + 1);
421 defer allocator.free(buf);424 defer allocator.free(buf);
422425
423 mem.copy(u8, buf, path);426 mem.copy(u8, buf, file_path);
424 buf[path.len] = 0;427 buf[file_path.len] = 0;
425428
426 const err = posix.getErrno(posix.unlink(buf.ptr));429 const err = posix.getErrno(posix.unlink(buf.ptr));
427 if (err > 0) {430 if (err > 0) {
...@@ -440,3 +443,19 @@ pub fn deleteFile(allocator: &Allocator, path: []const u8) -> %void {...@@ -440,3 +443,19 @@ pub fn deleteFile(allocator: &Allocator, path: []const u8) -> %void {
440 };443 };
441 }444 }
442}445}
446
447pub fn copyFile(allocator: &Allocator, source_path: []const u8, dest_path: []const u8) -> %void {
448 var in_stream = %return io.InStream.open(source_path, allocator);
449 defer in_stream.close();
450 var out_stream = %return io.OutStream.open(dest_path, allocator);
451 defer out_stream.close();
452
453 const buf = out_stream.buffer[0...];
454 while (true) {
455 const amt = %return in_stream.read(buf);
456 out_stream.index = amt;
457 %return out_stream.flush();
458 if (amt != out_stream.buffer.len)
459 return;
460 }
461}
std/os/path.zig created+25
...@@ -0,0 +1,25 @@
1const debug = @import("../debug.zig");
2const assert = debug.assert;
3const mem = @import("../mem.zig");
4const Allocator = mem.Allocator;
5
6/// Allocates memory for the result, which must be freed by the caller.
7pub fn join(allocator: &Allocator, dirname: []const u8, basename: []const u8) -> %[]const u8 {
8 const buf = %return allocator.alloc(u8, dirname.len + basename.len + 1);
9 %defer allocator.free(buf);
10
11 mem.copy(u8, buf, dirname);
12 if (dirname[dirname.len - 1] == '/') {
13 mem.copy(u8, buf[dirname.len...], basename);
14 return buf[0...buf.len - 1];
15 } else {
16 buf[dirname.len] = '/';
17 mem.copy(u8, buf[dirname.len + 1 ...], basename);
18 return buf;
19 }
20}
21
22test "os.path.join" {
23 assert(mem.eql(u8, %%join(&debug.global_allocator, "/a/b", "c"), "/a/b/c"));
24 assert(mem.eql(u8, %%join(&debug.global_allocator, "/a/b/", "c"), "/a/b/c"));
25}
std/special/build_runner.zig+1
...@@ -80,6 +80,7 @@ fn usage(builder: &Builder, maybe_zig_exe: ?[]const u8, already_ran_build: bool,...@@ -80,6 +80,7 @@ fn usage(builder: &Builder, maybe_zig_exe: ?[]const u8, already_ran_build: bool,
8080
81 // run the build script to collect the options81 // run the build script to collect the options
82 if (!already_ran_build) {82 if (!already_ran_build) {
83 builder.setInstallPrefix(null);
83 root.build(builder);84 root.build(builder);
84 }85 }
8586