zig build: support install for zig artifacts

also make os.copyFile atomic

closes #332
This commit is contained in:
Andrew Kelley 2017-04-30 22:09:44 -04:00
parent 943dbe5b50
commit c5dd536845
3 changed files with 102 additions and 59 deletions

View File

@ -552,20 +552,20 @@ pub const Builder = struct {
}; };
} }
pub fn installCLibrary(self: &Builder, lib: &CLibExeObjStep) { pub fn installArtifact(self: &Builder, artifact: &LibExeObjStep) {
self.getInstallStep().dependOn(&self.addInstallCLibrary(lib).step); self.getInstallStep().dependOn(&self.addInstallArtifact(artifact).step);
} }
pub fn addInstallCLibrary(self: &Builder, lib: &CLibExeObjStep) -> &InstallCArtifactStep { pub fn addInstallArtifact(self: &Builder, artifact: &LibExeObjStep) -> &InstallArtifactStep(LibExeObjStep) {
return InstallCArtifactStep.create(self, lib); return InstallArtifactStep(LibExeObjStep).create(self, artifact);
} }
pub fn installCExecutable(self: &Builder, exe: &CLibExeObjStep) { pub fn installCArtifact(self: &Builder, artifact: &CLibExeObjStep) {
self.getInstallStep().dependOn(&self.addInstallCExecutable(exe).step); self.getInstallStep().dependOn(&self.addInstallCArtifact(artifact).step);
} }
pub fn addInstallCExecutable(self: &Builder, exe: &CLibExeObjStep) -> &InstallCArtifactStep { pub fn addInstallCArtifact(self: &Builder, artifact: &CLibExeObjStep) -> &InstallArtifactStep(CLibExeObjStep) {
return InstallCArtifactStep.create(self, exe); return InstallArtifactStep(CLibExeObjStep).create(self, artifact);
} }
///::dest_rel_path is relative to prefix path or it can be an absolute path ///::dest_rel_path is relative to prefix path or it can be an absolute path
@ -588,14 +588,24 @@ pub const Builder = struct {
%%self.installed_files.append(full_path); %%self.installed_files.append(full_path);
} }
fn copyFile(self: &Builder, source_path: []const u8, dest_path: []const u8) { fn copyFile(self: &Builder, source_path: []const u8, dest_path: []const u8) -> %void {
return self.copyFileMode(source_path, dest_path, 0o666);
}
fn copyFileMode(self: &Builder, source_path: []const u8, dest_path: []const u8, mode: usize) -> %void {
if (self.verbose) {
%%io.stderr.printf("cp {} {}\n", source_path, dest_path);
}
const dirname = os.path.dirname(dest_path); const dirname = os.path.dirname(dest_path);
const abs_source_path = self.pathFromRoot(source_path); const abs_source_path = self.pathFromRoot(source_path);
os.makePath(self.allocator, dirname) %% |err| { os.makePath(self.allocator, dirname) %% |err| {
debug.panic("Unable to create path {}: {}", dirname, @errorName(err)); %%io.stderr.printf("Unable to create path {}: {}\n", dirname, @errorName(err));
return err;
}; };
os.copyFile(self.allocator, abs_source_path, dest_path) %% |err| { os.copyFileMode(self.allocator, abs_source_path, dest_path, mode) %% |err| {
debug.panic("Unable to copy {} to {}: {}", abs_source_path, dest_path, @errorName(err)); %%io.stderr.printf("Unable to copy {} to {}: {}\n", abs_source_path, dest_path, @errorName(err));
return err;
}; };
} }
@ -664,8 +674,8 @@ pub const LibExeObjStep = struct {
version: Version, version: Version,
out_h_filename: []const u8, out_h_filename: []const u8,
out_filename: []const u8, out_filename: []const u8,
out_filename_major_only: []const u8, major_only_filename: []const u8,
out_filename_name_only: []const u8, name_only_filename: []const u8,
object_files: List([]const u8), object_files: List([]const u8),
assembly_files: List([]const u8), assembly_files: List([]const u8),
@ -721,8 +731,8 @@ pub const LibExeObjStep = struct {
.version = *ver, .version = *ver,
.out_filename = undefined, .out_filename = undefined,
.out_h_filename = builder.fmt("{}.h", name), .out_h_filename = builder.fmt("{}.h", name),
.out_filename_major_only = undefined, .major_only_filename = undefined,
.out_filename_name_only = undefined, .name_only_filename = undefined,
.object_files = List([]const u8).init(builder.allocator), .object_files = List([]const u8).init(builder.allocator),
.assembly_files = List([]const u8).init(builder.allocator), .assembly_files = List([]const u8).init(builder.allocator),
}; };
@ -744,8 +754,8 @@ pub const LibExeObjStep = struct {
} else { } else {
self.out_filename = self.builder.fmt("lib{}.so.{d}.{d}.{d}", self.out_filename = self.builder.fmt("lib{}.so.{d}.{d}.{d}",
self.name, self.version.major, self.version.minor, self.version.patch); self.name, self.version.major, self.version.minor, self.version.patch);
self.out_filename_major_only = self.builder.fmt("lib{}.so.{d}", self.name, self.version.major); self.major_only_filename = self.builder.fmt("lib{}.so.{d}", self.name, self.version.major);
self.out_filename_name_only = self.builder.fmt("lib{}.so", self.name); self.name_only_filename = self.builder.fmt("lib{}.so", self.name);
} }
}, },
} }
@ -934,8 +944,8 @@ pub const LibExeObjStep = struct {
%return builder.spawnChild(builder.zig_exe, zig_args.toSliceConst()); %return builder.spawnChild(builder.zig_exe, zig_args.toSliceConst());
if (self.kind == Kind.Lib and !self.static) { if (self.kind == Kind.Lib and !self.static) {
%return doAtomicSymLinks(builder.allocator, output_path, self.out_filename_major_only, %return doAtomicSymLinks(builder.allocator, output_path, self.major_only_filename,
self.out_filename_name_only); self.name_only_filename);
} }
} }
}; };
@ -1424,45 +1434,56 @@ pub const CommandStep = struct {
} }
}; };
pub const InstallCArtifactStep = struct { fn InstallArtifactStep(comptime Artifact: type) -> type {
step: Step, struct {
builder: &Builder, step: Step,
artifact: &CLibExeObjStep, builder: &Builder,
dest_file: []const u8, artifact: &Artifact,
dest_file: []const u8,
pub fn create(builder: &Builder, artifact: &CLibExeObjStep) -> &InstallCArtifactStep { const Self = this;
const self = %%builder.allocator.create(InstallCArtifactStep);
const dest_dir = switch (artifact.kind) { pub fn create(builder: &Builder, artifact: &Artifact) -> &Self {
CLibExeObjStep.Kind.Obj => unreachable, const self = %%builder.allocator.create(Self);
CLibExeObjStep.Kind.Exe => builder.exe_dir, const dest_dir = switch (artifact.kind) {
CLibExeObjStep.Kind.Lib => builder.lib_dir, Artifact.Kind.Obj => unreachable,
}; Artifact.Kind.Exe => builder.exe_dir,
*self = InstallCArtifactStep { Artifact.Kind.Lib => builder.lib_dir,
.builder = builder, };
.step = Step.init(builder.fmt("install {}", artifact.step.name), builder.allocator, make), *self = Self {
.artifact = artifact, .builder = builder,
.dest_file = %%os.path.join(builder.allocator, builder.lib_dir, artifact.out_filename), .step = Step.init(builder.fmt("install {}", artifact.step.name), builder.allocator, make),
}; .artifact = artifact,
self.step.dependOn(&artifact.step); .dest_file = %%os.path.join(builder.allocator, dest_dir, artifact.out_filename),
builder.pushInstalledFile(self.dest_file); };
if (self.artifact.kind == CLibExeObjStep.Kind.Lib and !self.artifact.static) { self.step.dependOn(&artifact.step);
builder.pushInstalledFile(%%os.path.join(builder.allocator, builder.lib_dir, artifact.major_only_filename)); builder.pushInstalledFile(self.dest_file);
builder.pushInstalledFile(%%os.path.join(builder.allocator, builder.lib_dir, artifact.name_only_filename)); if (self.artifact.kind == Artifact.Kind.Lib and !self.artifact.static) {
builder.pushInstalledFile(%%os.path.join(builder.allocator, builder.lib_dir,
artifact.major_only_filename));
builder.pushInstalledFile(%%os.path.join(builder.allocator, builder.lib_dir,
artifact.name_only_filename));
}
return self;
} }
return self;
}
fn make(step: &Step) -> %void { fn make(step: &Step) -> %void {
const self = @fieldParentPtr(InstallCArtifactStep, "step", step); const self = @fieldParentPtr(Self, "step", step);
const builder = self.builder; const builder = self.builder;
builder.copyFile(self.artifact.getOutputPath(), self.dest_file); const mode = switch (self.artifact.kind) {
if (self.artifact.kind == CLibExeObjStep.Kind.Lib and !self.artifact.static) { Artifact.Kind.Obj => unreachable,
%return doAtomicSymLinks(builder.allocator, self.dest_file, Artifact.Kind.Exe => usize(0o755),
self.artifact.major_only_filename, self.artifact.name_only_filename); Artifact.Kind.Lib => if (self.artifact.static) usize(0o666) else usize(0o755),
};
%return builder.copyFileMode(self.artifact.getOutputPath(), self.dest_file, mode);
if (self.artifact.kind == Artifact.Kind.Lib and !self.artifact.static) {
%return doAtomicSymLinks(builder.allocator, self.dest_file,
self.artifact.major_only_filename, self.artifact.name_only_filename);
}
} }
} }
}; }
pub const InstallFileStep = struct { pub const InstallFileStep = struct {
step: Step, step: Step,
@ -1481,7 +1502,7 @@ pub const InstallFileStep = struct {
fn make(step: &Step) -> %void { fn make(step: &Step) -> %void {
const self = @fieldParentPtr(InstallFileStep, "step", step); const self = @fieldParentPtr(InstallFileStep, "step", step);
self.builder.copyFile(self.src_path, self.dest_path); %return self.builder.copyFile(self.src_path, self.dest_path);
} }
}; };

View File

@ -67,16 +67,22 @@ pub const OutStream = struct {
buffer: [os.page_size]u8, buffer: [os.page_size]u8,
index: usize, index: usize,
/// Calls ::openMode with 0o666 for the mode.
pub fn open(path: []const u8, allocator: ?&mem.Allocator) -> %OutStream {
return openMode(path, 0o666, allocator);
}
/// `path` may need to be copied in memory to add a null terminating byte. In this case /// `path` may need to be copied in memory to add a null terminating byte. In this case
/// a fixed size buffer of size std.os.max_noalloc_path_len is an attempted solution. If the fixed /// a fixed size buffer of size std.os.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. /// 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. /// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory.
/// Call close to clean up. /// Call close to clean up.
pub fn open(path: []const u8, allocator: ?&mem.Allocator) -> %OutStream { pub fn openMode(path: []const u8, mode: usize, allocator: ?&mem.Allocator) -> %OutStream {
switch (@compileVar("os")) { switch (@compileVar("os")) {
Os.linux, Os.darwin, Os.macosx, Os.ios => { Os.linux, Os.darwin, Os.macosx, Os.ios => {
const flags = system.O_LARGEFILE|system.O_WRONLY|system.O_CREAT|system.O_CLOEXEC|system.O_TRUNC; const flags = system.O_LARGEFILE|system.O_WRONLY|system.O_CREAT|system.O_CLOEXEC|system.O_TRUNC;
const fd = %return os.posixOpen(path, flags, 0o666, allocator); const fd = %return os.posixOpen(path, flags, mode, allocator);
return OutStream { return OutStream {
.fd = fd, .fd = fd,
.index = 0, .index = 0,

View File

@ -491,11 +491,27 @@ pub fn deleteFile(allocator: &Allocator, file_path: []const u8) -> %void {
} }
} }
/// Calls ::copyFileMode with 0o666 for the mode.
pub fn copyFile(allocator: &Allocator, source_path: []const u8, dest_path: []const u8) -> %void { pub fn copyFile(allocator: &Allocator, source_path: []const u8, dest_path: []const u8) -> %void {
return copyFileMode(allocator, source_path, dest_path, 0o666);
}
// TODO instead of accepting a mode argument, use the mode from fstat'ing the source path once open
/// Guaranteed to be atomic.
pub fn copyFileMode(allocator: &Allocator, source_path: []const u8, dest_path: []const u8, mode: usize) -> %void {
var rand_buf: [12]u8 = undefined;
const tmp_path = %return allocator.alloc(u8, dest_path.len + base64.calcEncodedSize(rand_buf.len));
defer allocator.free(tmp_path);
mem.copy(u8, tmp_path[0...], dest_path);
%return getRandomBytes(rand_buf[0...]);
_ = base64.encodeWithAlphabet(tmp_path[dest_path.len...], rand_buf, b64_fs_alphabet);
var out_stream = %return io.OutStream.openMode(tmp_path, mode, allocator);
defer out_stream.close();
%defer _ = deleteFile(allocator, tmp_path);
var in_stream = %return io.InStream.open(source_path, allocator); var in_stream = %return io.InStream.open(source_path, allocator);
defer in_stream.close(); defer in_stream.close();
var out_stream = %return io.OutStream.open(dest_path, allocator);
defer out_stream.close();
const buf = out_stream.buffer[0...]; const buf = out_stream.buffer[0...];
while (true) { while (true) {
@ -503,7 +519,7 @@ pub fn copyFile(allocator: &Allocator, source_path: []const u8, dest_path: []con
out_stream.index = amt; out_stream.index = amt;
%return out_stream.flush(); %return out_stream.flush();
if (amt != out_stream.buffer.len) if (amt != out_stream.buffer.len)
return; return rename(allocator, tmp_path, dest_path);
} }
} }