From 2b78a90424ee47ee1a9ef590dcf517026d0f13d1 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Fri, 30 Nov 2018 00:37:01 +0900 Subject: std.os.path: remove dependance on std.mem.join; std/os/child_process.zig: windows test/cli.zig: godbolt; doc/docgen.zig --- std/debug/index.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'std/debug') diff --git a/std/debug/index.zig b/std/debug/index.zig index c317432654..7596d80d9b 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -1290,7 +1290,7 @@ const LineNumberProgram = struct { return error.InvalidDebugInfo; } else self.include_dirs[file_entry.dir_index]; - const file_name = try os.path.join(self.file_entries.allocator, dir_name, file_entry.file_name); + const file_name = try os.path.join(self.file_entries.allocator, [][]const u8{dir_name, file_entry.file_name}); errdefer self.file_entries.allocator.free(file_name); return LineInfo{ .line = if (self.prev_line >= 0) @intCast(usize, self.prev_line) else 0, -- cgit v1.2.3 From 36bade5c562bf0b2479b6dfdd465a1a312890835 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 7 Feb 2019 00:42:41 -0500 Subject: fixups, and modify std.mem.join and std.os.path.resolve API * zig fmt * std.mem.join takes a slice of slices instead of var args * std.mem.join takes a separator slice rather than byte, and always inserts it. Previously it would not insert the separator if there already was one, violating the documented behavior. * std.mem.join calculates exactly the correct amount to allocate and has no call to allocator.shrink() * bring back joinWindows and joinPosix and the corresponding tests. it is intended to be able to call these functions from any OS. * rename std.os.path.resolveSlice to resolve (now resolve takes a slice of slices instead of var args) --- build.zig | 35 ++++++-- doc/docgen.zig | 25 ++++-- src-self-hosted/libc_installation.zig | 15 +++- std/build.zig | 79 ++++++++++++----- std/debug/index.zig | 4 +- std/event/fs.zig | 4 +- std/mem.zig | 49 +++++------ std/os/child_process.zig | 2 +- std/os/get_app_data_dir.zig | 7 +- std/os/index.zig | 7 +- std/os/path.zig | 154 +++++++++++++++++++++------------- test/cli.zig | 2 +- test/tests.zig | 55 +++++++++--- 13 files changed, 291 insertions(+), 147 deletions(-) (limited to 'std/debug') diff --git a/build.zig b/build.zig index b90f58ff51..5c7c5b8a18 100644 --- a/build.zig +++ b/build.zig @@ -16,7 +16,10 @@ pub fn build(b: *Builder) !void { var docgen_exe = b.addExecutable("docgen", "doc/docgen.zig"); const rel_zig_exe = try os.path.relative(b.allocator, b.build_root, b.zig_exe); - const langref_out_path = os.path.join(b.allocator, [][]const u8{ b.cache_root, "langref.html" }) catch unreachable; + const langref_out_path = os.path.join( + b.allocator, + [][]const u8{ b.cache_root, "langref.html" }, + ) catch unreachable; var docgen_cmd = b.addCommand(null, b.env_map, [][]const u8{ docgen_exe.getOutputPath(), rel_zig_exe, @@ -125,13 +128,19 @@ fn dependOnLib(b: *Builder, lib_exe_obj: var, dep: LibraryDep) void { for (dep.libdirs.toSliceConst()) |lib_dir| { lib_exe_obj.addLibPath(lib_dir); } - const lib_dir = os.path.join(b.allocator, [][]const u8{dep.prefix, "lib"}) catch unreachable; + const lib_dir = os.path.join( + b.allocator, + [][]const u8{ dep.prefix, "lib" }, + ) catch unreachable; for (dep.system_libs.toSliceConst()) |lib| { const static_bare_name = if (mem.eql(u8, lib, "curses")) ([]const u8)("libncurses.a") else b.fmt("lib{}.a", lib); - const static_lib_name = os.path.join(b.allocator, [][]const u8{lib_dir, static_bare_name}) catch unreachable; + const static_lib_name = os.path.join( + b.allocator, + [][]const u8{ lib_dir, static_bare_name }, + ) catch unreachable; const have_static = fileExists(static_lib_name) catch unreachable; if (have_static) { lib_exe_obj.addObjectFile(static_lib_name); @@ -159,7 +168,11 @@ fn fileExists(filename: []const u8) !bool { fn addCppLib(b: *Builder, lib_exe_obj: var, cmake_binary_dir: []const u8, lib_name: []const u8) void { const lib_prefix = if (lib_exe_obj.target.isWindows()) "" else "lib"; - lib_exe_obj.addObjectFile(os.path.join(b.allocator, [][]const u8{ cmake_binary_dir, "zig_cpp", b.fmt("{}{}{}", lib_prefix, lib_name, lib_exe_obj.target.libFileExt()) }) catch unreachable); + lib_exe_obj.addObjectFile(os.path.join(b.allocator, [][]const u8{ + cmake_binary_dir, + "zig_cpp", + b.fmt("{}{}{}", lib_prefix, lib_name, lib_exe_obj.target.libFileExt()), + }) catch unreachable); } const LibraryDep = struct { @@ -235,8 +248,11 @@ fn findLLVM(b: *Builder, llvm_config_exe: []const u8) !LibraryDep { pub fn installStdLib(b: *Builder, stdlib_files: []const u8) void { var it = mem.tokenize(stdlib_files, ";"); while (it.next()) |stdlib_file| { - const src_path = os.path.join(b.allocator, [][]const u8{"std", stdlib_file}) catch unreachable; - const dest_path = os.path.join(b.allocator, [][]const u8{"lib", "zig", "std", stdlib_file}) catch unreachable; + const src_path = os.path.join(b.allocator, [][]const u8{ "std", stdlib_file }) catch unreachable; + const dest_path = os.path.join( + b.allocator, + [][]const u8{ "lib", "zig", "std", stdlib_file }, + ) catch unreachable; b.installFile(src_path, dest_path); } } @@ -244,8 +260,11 @@ pub fn installStdLib(b: *Builder, stdlib_files: []const u8) void { pub fn installCHeaders(b: *Builder, c_header_files: []const u8) void { var it = mem.tokenize(c_header_files, ";"); while (it.next()) |c_header_file| { - const src_path = os.path.join(b.allocator, [][]const u8{"c_headers", c_header_file}) catch unreachable; - const dest_path = os.path.join(b.allocator, [][]const u8{"lib", "zig", "include", c_header_file}) catch unreachable; + const src_path = os.path.join(b.allocator, [][]const u8{ "c_headers", c_header_file }) catch unreachable; + const dest_path = os.path.join( + b.allocator, + [][]const u8{ "lib", "zig", "include", c_header_file }, + ) catch unreachable; b.installFile(src_path, dest_path); } } diff --git a/doc/docgen.zig b/doc/docgen.zig index 8b2a7e4c10..14e4700553 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -990,13 +990,19 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var try tokenizeAndPrint(tokenizer, out, code.source_token); try out.write(""); const name_plus_ext = try std.fmt.allocPrint(allocator, "{}.zig", code.name); - const tmp_source_file_name = try os.path.join(allocator, [][]const u8{ tmp_dir_name, name_plus_ext }); + const tmp_source_file_name = try os.path.join( + allocator, + [][]const u8{ tmp_dir_name, name_plus_ext }, + ); try io.writeFile(tmp_source_file_name, trimmed_raw_source); switch (code.id) { Code.Id.Exe => |expected_outcome| { const name_plus_bin_ext = try std.fmt.allocPrint(allocator, "{}{}", code.name, exe_ext); - const tmp_bin_file_name = try os.path.join(allocator, [][]const u8{ tmp_dir_name, name_plus_bin_ext }); + const tmp_bin_file_name = try os.path.join( + allocator, + [][]const u8{ tmp_dir_name, name_plus_bin_ext }, + ); var build_args = std.ArrayList([]const u8).init(allocator); defer build_args.deinit(); try build_args.appendSlice([][]const u8{ @@ -1024,7 +1030,10 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var } for (code.link_objects) |link_object| { const name_with_ext = try std.fmt.allocPrint(allocator, "{}{}", link_object, obj_ext); - const full_path_object = try os.path.join(allocator, [][]const u8{ tmp_dir_name, name_with_ext }); + const full_path_object = try os.path.join( + allocator, + [][]const u8{ tmp_dir_name, name_with_ext }, + ); try build_args.append("--object"); try build_args.append(full_path_object); try out.print(" --object {}", name_with_ext); @@ -1216,12 +1225,18 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var }, Code.Id.Obj => |maybe_error_match| { const name_plus_obj_ext = try std.fmt.allocPrint(allocator, "{}{}", code.name, obj_ext); - const tmp_obj_file_name = try os.path.join(allocator, [][]const u8{ tmp_dir_name, name_plus_obj_ext }); + const tmp_obj_file_name = try os.path.join( + allocator, + [][]const u8{ tmp_dir_name, name_plus_obj_ext }, + ); var build_args = std.ArrayList([]const u8).init(allocator); defer build_args.deinit(); const name_plus_h_ext = try std.fmt.allocPrint(allocator, "{}.h", code.name); - const output_h_file_name = try os.path.join(allocator, [][]const u8{ tmp_dir_name, name_plus_h_ext }); + const output_h_file_name = try os.path.join( + allocator, + [][]const u8{ tmp_dir_name, name_plus_h_ext }, + ); try build_args.appendSlice([][]const u8{ zig_exe, diff --git a/src-self-hosted/libc_installation.zig b/src-self-hosted/libc_installation.zig index 682e10c361..edcb9dc579 100644 --- a/src-self-hosted/libc_installation.zig +++ b/src-self-hosted/libc_installation.zig @@ -254,7 +254,10 @@ pub const LibCInstallation = struct { const stream = &std.io.BufferOutStream.init(&result_buf).stream; try stream.print("{}\\Include\\{}\\ucrt", search.path, search.version); - const stdlib_path = try std.os.path.join(loop.allocator, [][]const u8{ result_buf.toSliceConst(), "stdlib.h" }); + const stdlib_path = try std.os.path.join( + loop.allocator, + [][]const u8{ result_buf.toSliceConst(), "stdlib.h" }, + ); defer loop.allocator.free(stdlib_path); if (try fileExists(stdlib_path)) { @@ -283,7 +286,10 @@ pub const LibCInstallation = struct { builtin.Arch.aarch64v8 => try stream.write("arm"), else => return error.UnsupportedArchitecture, } - const ucrt_lib_path = try std.os.path.join(loop.allocator, [][]const u8{ result_buf.toSliceConst(), "ucrt.lib" }); + const ucrt_lib_path = try std.os.path.join( + loop.allocator, + [][]const u8{ result_buf.toSliceConst(), "ucrt.lib" }, + ); defer loop.allocator.free(ucrt_lib_path); if (try fileExists(ucrt_lib_path)) { self.lib_dir = result_buf.toOwnedSlice(); @@ -358,7 +364,10 @@ pub const LibCInstallation = struct { builtin.Arch.aarch64v8 => try stream.write("arm\\"), else => return error.UnsupportedArchitecture, } - const kernel32_path = try std.os.path.join(loop.allocator, [][]const u8{ result_buf.toSliceConst(), "kernel32.lib" }); + const kernel32_path = try std.os.path.join( + loop.allocator, + [][]const u8{ result_buf.toSliceConst(), "kernel32.lib" }, + ); defer loop.allocator.free(kernel32_path); if (try fileExists(kernel32_path)) { self.kernel32_lib_dir = result_buf.toOwnedSlice(); diff --git a/std/build.zig b/std/build.zig index 1348396f59..0dbbded802 100644 --- a/std/build.zig +++ b/std/build.zig @@ -145,8 +145,8 @@ pub const Builder = struct { pub fn setInstallPrefix(self: *Builder, maybe_prefix: ?[]const u8) void { self.prefix = maybe_prefix orelse "/usr/local"; // TODO better default - self.lib_dir = os.path.join(self.allocator, [][]const u8{self.prefix, "lib"}) catch unreachable; - self.exe_dir = os.path.join(self.allocator, [][]const u8{self.prefix, "bin"}) catch unreachable; + self.lib_dir = os.path.join(self.allocator, [][]const u8{ self.prefix, "lib" }) catch unreachable; + self.exe_dir = os.path.join(self.allocator, [][]const u8{ self.prefix, "bin" }) catch unreachable; } pub fn addExecutable(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { @@ -618,7 +618,10 @@ pub const Builder = struct { ///::dest_rel_path is relative to prefix path or it can be an absolute path pub fn addInstallFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) *InstallFileStep { - const full_dest_path = os.path.resolve(self.allocator, self.prefix, dest_rel_path) catch unreachable; + const full_dest_path = os.path.resolve( + self.allocator, + [][]const u8{ self.prefix, dest_rel_path }, + ) catch unreachable; self.pushInstalledFile(full_dest_path); const install_step = self.allocator.create(InstallFileStep) catch unreachable; @@ -653,7 +656,7 @@ pub const Builder = struct { } fn pathFromRoot(self: *Builder, rel_path: []const u8) []u8 { - return os.path.resolve(self.allocator, self.build_root, rel_path) catch unreachable; + return os.path.resolve(self.allocator, [][]const u8{ self.build_root, rel_path }) catch unreachable; } pub fn fmt(self: *Builder, comptime format: []const u8, args: ...) []u8 { @@ -676,7 +679,7 @@ pub const Builder = struct { if (os.path.isAbsolute(name)) { return name; } - const full_path = try os.path.join(self.allocator, [][]const u8{search_prefix, "bin", self.fmt("{}{}", name, exe_extension)}); + const full_path = try os.path.join(self.allocator, [][]const u8{ search_prefix, "bin", self.fmt("{}{}", name, exe_extension) }); if (os.path.real(self.allocator, full_path)) |real_path| { return real_path; } else |_| { @@ -691,7 +694,7 @@ pub const Builder = struct { } var it = mem.tokenize(PATH, []u8{os.path.delimiter}); while (it.next()) |path| { - const full_path = try os.path.join(self.allocator, [][]const u8{path, self.fmt("{}{}", name, exe_extension)}); + const full_path = try os.path.join(self.allocator, [][]const u8{ path, self.fmt("{}{}", name, exe_extension) }); if (os.path.real(self.allocator, full_path)) |real_path| { return real_path; } else |_| { @@ -705,7 +708,7 @@ pub const Builder = struct { return name; } for (paths) |path| { - const full_path = try os.path.join(self.allocator, [][]const u8{path, self.fmt("{}{}", name, exe_extension)}); + const full_path = try os.path.join(self.allocator, [][]const u8{ path, self.fmt("{}{}", name, exe_extension) }); if (os.path.real(self.allocator, full_path)) |real_path| { return real_path; } else |_| { @@ -1113,7 +1116,10 @@ pub const LibExeObjStep = struct { } pub fn getOutputPath(self: *LibExeObjStep) []const u8 { - return if (self.output_path) |output_path| output_path else os.path.join(self.builder.allocator, [][]const u8{self.builder.cache_root, self.out_filename}) catch unreachable; + return if (self.output_path) |output_path| output_path else os.path.join( + self.builder.allocator, + [][]const u8{ self.builder.cache_root, self.out_filename }, + ) catch unreachable; } pub fn setOutputHPath(self: *LibExeObjStep, file_path: []const u8) void { @@ -1126,7 +1132,10 @@ pub const LibExeObjStep = struct { } pub fn getOutputHPath(self: *LibExeObjStep) []const u8 { - return if (self.output_h_path) |output_h_path| output_h_path else os.path.join(self.builder.allocator, [][]const u8{self.builder.cache_root, self.out_h_filename}) catch unreachable; + return if (self.output_h_path) |output_h_path| output_h_path else os.path.join( + self.builder.allocator, + [][]const u8{ self.builder.cache_root, self.out_h_filename }, + ) catch unreachable; } pub fn addAssemblyFile(self: *LibExeObjStep, path: []const u8) void { @@ -1226,7 +1235,10 @@ pub const LibExeObjStep = struct { } if (self.build_options_contents.len() > 0) { - const build_options_file = try os.path.join(builder.allocator, [][]const u8{builder.cache_root, builder.fmt("{}_build_options.zig", self.name)}); + const build_options_file = try os.path.join( + builder.allocator, + [][]const u8{ builder.cache_root, builder.fmt("{}_build_options.zig", self.name) }, + ); try std.io.writeFile(build_options_file, self.build_options_contents.toSliceConst()); try zig_args.append("--pkg-begin"); try zig_args.append("build_options"); @@ -1476,7 +1488,10 @@ pub const LibExeObjStep = struct { cc_args.append("-c") catch unreachable; cc_args.append(abs_source_file) catch unreachable; - const cache_o_src = os.path.join(builder.allocator, [][]const u8{builder.cache_root, source_file}) catch unreachable; + const cache_o_src = os.path.join( + builder.allocator, + [][]const u8{ builder.cache_root, source_file }, + ) catch unreachable; if (os.path.dirname(cache_o_src)) |cache_o_dir| { try builder.makePath(cache_o_dir); } @@ -1528,7 +1543,10 @@ pub const LibExeObjStep = struct { cc_args.append("-current_version") catch unreachable; cc_args.append(builder.fmt("{}.{}.{}", self.version.major, self.version.minor, self.version.patch)) catch unreachable; - const install_name = builder.pathFromRoot(os.path.join(builder.allocator, [][]const u8{builder.cache_root, self.major_only_filename}) catch unreachable); + const install_name = builder.pathFromRoot(os.path.join( + builder.allocator, + [][]const u8{ builder.cache_root, self.major_only_filename }, + ) catch unreachable); cc_args.append("-install_name") catch unreachable; cc_args.append(install_name) catch unreachable; } else { @@ -1594,7 +1612,10 @@ pub const LibExeObjStep = struct { cc_args.append("-c") catch unreachable; cc_args.append(abs_source_file) catch unreachable; - const cache_o_src = os.path.join(builder.allocator, [][]const u8{builder.cache_root, source_file}) catch unreachable; + const cache_o_src = os.path.join( + builder.allocator, + [][]const u8{ builder.cache_root, source_file }, + ) catch unreachable; if (os.path.dirname(cache_o_src)) |cache_o_dir| { try builder.makePath(cache_o_dir); } @@ -1757,7 +1778,10 @@ pub const TestStep = struct { return output_path; } else { const basename = self.builder.fmt("test{}", self.target.exeFileExt()); - return os.path.join(self.builder.allocator, [][]const u8{self.builder.cache_root, basename}) catch unreachable; + return os.path.join( + self.builder.allocator, + [][]const u8{ self.builder.cache_root, basename }, + ) catch unreachable; } } @@ -1979,13 +2003,22 @@ const InstallArtifactStep = struct { .builder = builder, .step = Step.init(builder.fmt("install {}", artifact.step.name), builder.allocator, make), .artifact = artifact, - .dest_file = os.path.join(builder.allocator, dest_dir, artifact.out_filename) catch unreachable, - }) catch unreachable; + .dest_file = os.path.join( + builder.allocator, + [][]const u8{ dest_dir, artifact.out_filename }, + ) catch unreachable, + }; self.step.dependOn(&artifact.step); builder.pushInstalledFile(self.dest_file); if (self.artifact.kind == LibExeObjStep.Kind.Lib and !self.artifact.static) { - builder.pushInstalledFile(os.path.join(builder.allocator, [][]const u8{builder.lib_dir, artifact.major_only_filename}) catch unreachable); - builder.pushInstalledFile(os.path.join(builder.allocator, [][]const u8{builder.lib_dir, artifact.name_only_filename}) catch unreachable); + builder.pushInstalledFile(os.path.join( + builder.allocator, + [][]const u8{ builder.lib_dir, artifact.major_only_filename }, + ) catch unreachable); + builder.pushInstalledFile(os.path.join( + builder.allocator, + [][]const u8{ builder.lib_dir, artifact.name_only_filename }, + ) catch unreachable); } return self; } @@ -2141,13 +2174,19 @@ fn doAtomicSymLinks(allocator: *Allocator, output_path: []const u8, filename_maj const out_dir = os.path.dirname(output_path) orelse "."; 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, [][]const u8{out_dir, filename_major_only}) catch unreachable; + const major_only_path = os.path.join( + allocator, + [][]const u8{ out_dir, filename_major_only }, + ) catch unreachable; os.atomicSymLink(allocator, out_basename, major_only_path) catch |err| { warn("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, [][]const u8{out_dir, filename_name_only}) catch unreachable; + const name_only_path = os.path.join( + allocator, + [][]const u8{ out_dir, filename_name_only }, + ) catch unreachable; os.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| { warn("Unable to symlink {} -> {}\n", name_only_path, filename_major_only); return err; diff --git a/std/debug/index.zig b/std/debug/index.zig index 0253912d37..a1e2747df5 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -774,7 +774,7 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo { const len = try di.coff.getPdbPath(path_buf[0..]); const raw_path = path_buf[0..len]; - const path = try os.path.resolve(allocator, raw_path); + const path = try os.path.resolve(allocator, [][]const u8{raw_path}); try di.pdb.openFile(di.coff, path); @@ -1352,7 +1352,7 @@ const LineNumberProgram = struct { return error.InvalidDebugInfo; } else self.include_dirs[file_entry.dir_index]; - const file_name = try os.path.join(self.file_entries.allocator, [][]const u8{dir_name, file_entry.file_name}); + const file_name = try os.path.join(self.file_entries.allocator, [][]const u8{ dir_name, file_entry.file_name }); errdefer self.file_entries.allocator.free(file_name); return LineInfo{ .line = if (self.prev_line >= 0) @intCast(usize, self.prev_line) else 0, diff --git a/std/event/fs.zig b/std/event/fs.zig index 97a79bed39..fd0fe434cb 100644 --- a/std/event/fs.zig +++ b/std/event/fs.zig @@ -871,7 +871,7 @@ pub fn Watch(comptime V: type) type { } async fn addFileKEvent(self: *Self, file_path: []const u8, value: V) !?V { - const resolved_path = try os.path.resolve(self.channel.loop.allocator, file_path); + const resolved_path = try os.path.resolve(self.channel.loop.allocator, [][]const u8{file_path}); var resolved_path_consumed = false; defer if (!resolved_path_consumed) self.channel.loop.allocator.free(resolved_path); @@ -1336,7 +1336,7 @@ async fn testFsWatchCantFail(loop: *Loop, result: *(anyerror!void)) void { } async fn testFsWatch(loop: *Loop) !void { - const file_path = try os.path.join(loop.allocator, [][]const u8{test_tmp_dir, "file.txt"}); + const file_path = try os.path.join(loop.allocator, [][]const u8{ test_tmp_dir, "file.txt" }); defer loop.allocator.free(file_path); const contents = diff --git a/std/mem.zig b/std/mem.zig index 178a5f6c6f..48d1cb930c 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -882,42 +882,37 @@ pub const SplitIterator = struct { } }; -/// Naively combines a series of strings with a separator. +/// Naively combines a series of slices with a separator. /// Allocates memory for the result, which must be freed by the caller. -pub fn join(allocator: *Allocator, sep: u8, strings: ...) ![]u8 { - comptime assert(strings.len >= 1); - var total_strings_len: usize = strings.len; // 1 sep per string - { - comptime var string_i = 0; - inline while (string_i < strings.len) : (string_i += 1) { - const arg = ([]const u8)(strings[string_i]); - total_strings_len += arg.len; - } - } +pub fn join(allocator: *Allocator, separator: []const u8, slices: []const []const u8) ![]u8 { + if (slices.len == 0) return (([*]u8)(undefined))[0..0]; + + const total_len = blk: { + var sum: usize = separator.len * (slices.len - 1); + for (slices) |slice| + sum += slice.len; + break :blk sum; + }; - const buf = try allocator.alloc(u8, total_strings_len); + const buf = try allocator.alloc(u8, total_len); errdefer allocator.free(buf); - var buf_index: usize = 0; - comptime var string_i = 0; - inline while (true) { - const arg = ([]const u8)(strings[string_i]); - string_i += 1; - copy(u8, buf[buf_index..], arg); - buf_index += arg.len; - if (string_i >= strings.len) break; - if (buf[buf_index - 1] != sep) { - buf[buf_index] = sep; - buf_index += 1; - } + copy(u8, buf, slices[0]); + var buf_index: usize = slices[0].len; + for (slices[1..]) |slice| { + copy(u8, buf[buf_index..], separator); + buf_index += separator.len; + copy(u8, buf[buf_index..], slice); + buf_index += slice.len; } - return allocator.shrink(u8, buf, buf_index); + // No need for shrink since buf is exactly the correct size. + return buf; } test "mem.join" { - assert(eql(u8, try join(debug.global_allocator, ',', "a", "b", "c"), "a,b,c")); - assert(eql(u8, try join(debug.global_allocator, ',', "a"), "a")); + assert(eql(u8, try join(debug.global_allocator, ",", [][]const u8{ "a", "b", "c" }), "a,b,c")); + assert(eql(u8, try join(debug.global_allocator, ",", [][]const u8{"a"}), "a")); } test "testStringEquality" { diff --git a/std/os/child_process.zig b/std/os/child_process.zig index 2651310c98..6635b76976 100644 --- a/std/os/child_process.zig +++ b/std/os/child_process.zig @@ -574,7 +574,7 @@ pub const ChildProcess = struct { // to match posix semantics const app_name = x: { if (self.cwd) |cwd| { - const resolved = try os.path.resolve(self.allocator, cwd, self.argv[0]); + const resolved = try os.path.resolve(self.allocator, [][]const u8{ cwd, self.argv[0] }); defer self.allocator.free(resolved); break :x try cstr.addNullByte(self.allocator, resolved); } else { diff --git a/std/os/get_app_data_dir.zig b/std/os/get_app_data_dir.zig index a0315b4511..f5e0b78eec 100644 --- a/std/os/get_app_data_dir.zig +++ b/std/os/get_app_data_dir.zig @@ -30,7 +30,7 @@ pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataD error.OutOfMemory => return error.OutOfMemory, }; defer allocator.free(global_dir); - return os.path.join(allocator, [][]const u8{global_dir, appname}); + return os.path.join(allocator, [][]const u8{ global_dir, appname }); }, os.windows.E_OUTOFMEMORY => return error.OutOfMemory, else => return error.AppDataDirUnavailable, @@ -41,14 +41,14 @@ pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataD // TODO look in /etc/passwd return error.AppDataDirUnavailable; }; - return os.path.join(allocator, [][]const u8{home_dir, "Library", "Application Support", appname}); + return os.path.join(allocator, [][]const u8{ home_dir, "Library", "Application Support", appname }); }, builtin.Os.linux, builtin.Os.freebsd => { const home_dir = os.getEnvPosix("HOME") orelse { // TODO look in /etc/passwd return error.AppDataDirUnavailable; }; - return os.path.join(allocator, [][]const u8{home_dir, ".local", "share", appname}); + return os.path.join(allocator, [][]const u8{ home_dir, ".local", "share", appname }); }, else => @compileError("Unsupported OS"), } @@ -67,4 +67,3 @@ test "std.os.getAppDataDir" { // We can't actually validate the result _ = getAppDataDir(allocator, "zig") catch return; } - diff --git a/std/os/index.zig b/std/os/index.zig index d17b6f4f40..8e9876c36b 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -1284,7 +1284,7 @@ pub fn makeDirPosix(dir_path: []const u8) !void { /// already exists and is a directory. /// TODO determine if we can remove the allocator requirement from this function pub fn makePath(allocator: *Allocator, full_path: []const u8) !void { - const resolved_path = try path.resolve(allocator, full_path); + const resolved_path = try path.resolve(allocator, [][]const u8{full_path}); defer allocator.free(resolved_path); var end_index: usize = resolved_path.len; @@ -2304,18 +2304,17 @@ pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) ![]u8 { switch (builtin.os) { Os.linux => return readLink(out_buffer, "/proc/self/exe"), Os.freebsd => { - var mib = [4]c_int{ posix.CTL_KERN, posix.KERN_PROC, posix.KERN_PROC_PATHNAME, -1}; + var mib = [4]c_int{ posix.CTL_KERN, posix.KERN_PROC, posix.KERN_PROC_PATHNAME, -1 }; var out_len: usize = out_buffer.len; const err = posix.getErrno(posix.sysctl(&mib, 4, out_buffer, &out_len, null, 0)); - if (err == 0 ) return mem.toSlice(u8, out_buffer); + if (err == 0) return mem.toSlice(u8, out_buffer); return switch (err) { posix.EFAULT => error.BadAdress, posix.EPERM => error.PermissionDenied, else => unexpectedErrorPosix(err), }; - }, Os.windows => { var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined; diff --git a/std/os/path.zig b/std/os/path.zig index 5a5b1b7772..266a77b97c 100644 --- a/std/os/path.zig +++ b/std/os/path.zig @@ -33,63 +33,103 @@ pub fn isSep(byte: u8) bool { } } -/// Naively combines a series of paths with the native path seperator. -/// Allocates memory for the result, which must be freed by the caller. - -pub fn join(allocator: *Allocator, paths: []const []const u8) ![]u8 { - assert(paths.len >= 1); - var total_paths_len: usize = paths.len; // 1 sep per path - { - var path_i: usize = 0; - while (path_i < paths.len) : (path_i += 1) { - const arg = ([]const u8)(paths[path_i]); - total_paths_len += arg.len; +/// This is different from mem.join in that the separator will not be repeated if +/// it is found at the end or beginning of a pair of consecutive paths. +fn joinSep(allocator: *Allocator, separator: u8, paths: []const []const u8) ![]u8 { + if (paths.len == 0) return (([*]u8)(undefined))[0..0]; + + const total_len = blk: { + var sum: usize = paths[0].len; + var i: usize = 1; + while (i < paths.len) : (i += 1) { + const prev_path = paths[i - 1]; + const this_path = paths[i]; + const prev_sep = (prev_path.len != 0 and prev_path[prev_path.len - 1] == separator); + const this_sep = (this_path.len != 0 and this_path[0] == separator); + sum += @boolToInt(!prev_sep and !this_sep); + sum += if (prev_sep and this_sep) this_path.len - 1 else this_path.len; } - } + break :blk sum; + }; - const buf = try allocator.alloc(u8, total_paths_len); + const buf = try allocator.alloc(u8, total_len); errdefer allocator.free(buf); - var buf_index: usize = 0; - var path_i: usize = 0; - while (true) { - const arg = ([]const u8)(paths[path_i]); - path_i += 1; - mem.copy(u8, buf[buf_index..], arg); - buf_index += arg.len; - if (path_i >= paths.len) break; - if (buf_index > 0 and buf[buf_index - 1] != sep) { - buf[buf_index] = sep; + mem.copy(u8, buf, paths[0]); + var buf_index: usize = paths[0].len; + var i: usize = 1; + while (i < paths.len) : (i += 1) { + const prev_path = paths[i - 1]; + const this_path = paths[i]; + const prev_sep = (prev_path.len != 0 and prev_path[prev_path.len - 1] == separator); + const this_sep = (this_path.len != 0 and this_path[0] == separator); + if (!prev_sep and !this_sep) { + buf[buf_index] = separator; buf_index += 1; } + const adjusted_path = if (prev_sep and this_sep) this_path[1..] else this_path; + mem.copy(u8, buf[buf_index..], adjusted_path); + buf_index += adjusted_path.len; } - return allocator.shrink(u8, buf, buf_index); + // No need for shrink since buf is exactly the correct size. + return buf; +} + +pub const join = if (is_windows) joinWindows else joinPosix; + +/// Naively combines a series of paths with the native path seperator. +/// Allocates memory for the result, which must be freed by the caller. +pub fn joinWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 { + return joinSep(allocator, sep_windows, paths); +} + +/// Naively combines a series of paths with the native path seperator. +/// Allocates memory for the result, which must be freed by the caller. +pub fn joinPosix(allocator: *Allocator, paths: []const []const u8) ![]u8 { + return joinSep(allocator, sep_posix, paths); +} + +fn testJoinWindows(paths: []const []const u8, expected: []const u8) void { + var buf: [1024]u8 = undefined; + const a = &std.heap.FixedBufferAllocator.init(&buf).allocator; + const actual = joinWindows(a, paths) catch @panic("fail"); + debug.assertOrPanic(mem.eql(u8, actual, expected)); +} + +fn testJoinPosix(paths: []const []const u8, expected: []const u8) void { + var buf: [1024]u8 = undefined; + const a = &std.heap.FixedBufferAllocator.init(&buf).allocator; + const actual = joinPosix(a, paths) catch @panic("fail"); + debug.assertOrPanic(mem.eql(u8, actual, expected)); } test "os.path.join" { - switch (builtin.os) { - Os.windows => { - assert(mem.eql(u8, try join(debug.global_allocator, [][]const u8{"c:\\a\\b", "c"}), "c:\\a\\b\\c")); - assert(mem.eql(u8, try join(debug.global_allocator, [][]const u8{"c:\\a\\b\\", "c"}), "c:\\a\\b\\c")); - assert(mem.eql(u8, try join(debug.global_allocator, [][]const u8{"c:\\", "a", "b\\", "c"}), "c:\\a\\b\\c")); - assert(mem.eql(u8, try join(debug.global_allocator, [][]const u8{"c:\\a\\", "b\\", "c"}), "c:\\a\\b\\c")); - assert(mem.eql(u8, try join( debug.global_allocator - , [][]const u8{ "c:\\home\\andy\\dev\\zig\\build\\lib\\zig\\std" - , "io.zig"}) - , "c:\\home\\andy\\dev\\zig\\build\\lib\\zig\\std\\io.zig")); - }, - else => { - assert(mem.eql(u8, try join(debug.global_allocator, [][]const u8{"/a/b", "c"}), "/a/b/c")); - assert(mem.eql(u8, try join(debug.global_allocator, [][]const u8{"/a/b/", "c"}), "/a/b/c")); - assert(mem.eql(u8, try join(debug.global_allocator, [][]const u8{"/", "a", "b/", "c"}), "/a/b/c")); - assert(mem.eql(u8, try join(debug.global_allocator, [][]const u8{"/a/", "b/", "c"}), "/a/b/c")); - assert(mem.eql(u8, try join( debug.global_allocator - , [][]const u8{ "/home/andy/dev/zig/build/lib/zig/std" - , "io.zig"}) - , "/home/andy/dev/zig/build/lib/zig/std/io.zig")); - } - } + testJoinWindows([][]const u8{ "c:\\a\\b", "c" }, "c:\\a\\b\\c"); + testJoinWindows([][]const u8{ "c:\\a\\b", "c" }, "c:\\a\\b\\c"); + testJoinWindows([][]const u8{ "c:\\a\\b\\", "c" }, "c:\\a\\b\\c"); + + testJoinWindows([][]const u8{ "c:\\", "a", "b\\", "c" }, "c:\\a\\b\\c"); + testJoinWindows([][]const u8{ "c:\\a\\", "b\\", "c" }, "c:\\a\\b\\c"); + + testJoinWindows( + [][]const u8{ "c:\\home\\andy\\dev\\zig\\build\\lib\\zig\\std", "io.zig" }, + "c:\\home\\andy\\dev\\zig\\build\\lib\\zig\\std\\io.zig", + ); + + testJoinPosix([][]const u8{ "/a/b", "c" }, "/a/b/c"); + testJoinPosix([][]const u8{ "/a/b/", "c" }, "/a/b/c"); + + testJoinPosix([][]const u8{ "/", "a", "b/", "c" }, "/a/b/c"); + testJoinPosix([][]const u8{ "/a/", "b/", "c" }, "/a/b/c"); + + testJoinPosix( + [][]const u8{ "/home/andy/dev/zig/build/lib/zig/std", "io.zig" }, + "/home/andy/dev/zig/build/lib/zig/std/io.zig", + ); + + testJoinPosix([][]const u8{ "a", "/c" }, "a/c"); + testJoinPosix([][]const u8{ "a/", "/c" }, "a/c"); } pub fn isAbsolute(path: []const u8) bool { @@ -335,18 +375,8 @@ fn asciiEqlIgnoreCase(s1: []const u8, s2: []const u8) bool { return true; } -/// Converts the command line arguments into a slice and calls `resolveSlice`. -pub fn resolve(allocator: *Allocator, args: ...) ![]u8 { - var paths: [args.len][]const u8 = undefined; - comptime var arg_i = 0; - inline while (arg_i < args.len) : (arg_i += 1) { - paths[arg_i] = args[arg_i]; - } - return resolveSlice(allocator, paths); -} - /// On Windows, this calls `resolveWindows` and on POSIX it calls `resolvePosix`. -pub fn resolveSlice(allocator: *Allocator, paths: []const []const u8) ![]u8 { +pub fn resolve(allocator: *Allocator, paths: []const []const u8) ![]u8 { if (is_windows) { return resolveWindows(allocator, paths); } else { @@ -625,7 +655,10 @@ test "os.path.resolveWindows" { const parsed_cwd = windowsParsePath(cwd); { const result = testResolveWindows([][]const u8{ "/usr/local", "lib\\zig\\std\\array_list.zig" }); - const expected = try join(debug.global_allocator, [][]const u8{ parsed_cwd.disk_designator, "usr\\local\\lib\\zig\\std\\array_list.zig"}); + const expected = try join(debug.global_allocator, [][]const u8{ + parsed_cwd.disk_designator, + "usr\\local\\lib\\zig\\std\\array_list.zig", + }); if (parsed_cwd.kind == WindowsPath.Kind.Drive) { expected[0] = asciiUpper(parsed_cwd.disk_designator[0]); } @@ -633,7 +666,10 @@ test "os.path.resolveWindows" { } { const result = testResolveWindows([][]const u8{ "usr/local", "lib\\zig" }); - const expected = try join(debug.global_allocator, [][]const u8{ cwd, "usr\\local\\lib\\zig" }); + const expected = try join(debug.global_allocator, [][]const u8{ + cwd, + "usr\\local\\lib\\zig", + }); if (parsed_cwd.kind == WindowsPath.Kind.Drive) { expected[0] = asciiUpper(parsed_cwd.disk_designator[0]); } diff --git a/test/cli.zig b/test/cli.zig index 0980e8c2d8..745da4dd80 100644 --- a/test/cli.zig +++ b/test/cli.zig @@ -27,7 +27,7 @@ pub fn main() !void { std.debug.warn("Expected second argument to be cache root directory path\n"); return error.InvalidArgs; }); - const zig_exe = try os.path.resolve(a, zig_exe_rel); + const zig_exe = try os.path.resolve(a, [][]const u8{zig_exe_rel}); const dir_path = try os.path.join(a, [][]const u8{ cache_root, "clitest" }); const TestFn = fn ([]const u8, []const u8) anyerror!void; diff --git a/test/tests.zig b/test/tests.zig index fac941cbde..670c410509 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -439,7 +439,10 @@ pub const CompareOutputContext = struct { pub fn addCase(self: *CompareOutputContext, case: TestCase) void { const b = self.b; - const root_src = os.path.join(b.allocator, [][]const u8{b.cache_root, case.sources.items[0].filename}) catch unreachable; + const root_src = os.path.join( + b.allocator, + [][]const u8{ b.cache_root, case.sources.items[0].filename }, + ) catch unreachable; switch (case.special) { Special.Asm => { @@ -452,7 +455,10 @@ pub const CompareOutputContext = struct { exe.addAssemblyFile(root_src); for (case.sources.toSliceConst()) |src_file| { - const expanded_src_path = os.path.join(b.allocator, [][]const u8{b.cache_root, src_file.filename}) catch unreachable; + const expanded_src_path = os.path.join( + b.allocator, + [][]const u8{ b.cache_root, src_file.filename }, + ) catch unreachable; const write_src = b.addWriteFile(expanded_src_path, src_file.source); exe.step.dependOn(&write_src.step); } @@ -476,7 +482,10 @@ pub const CompareOutputContext = struct { } for (case.sources.toSliceConst()) |src_file| { - const expanded_src_path = os.path.join(b.allocator, [][]const u8{b.cache_root, src_file.filename}) catch unreachable; + const expanded_src_path = os.path.join( + b.allocator, + [][]const u8{ b.cache_root, src_file.filename }, + ) catch unreachable; const write_src = b.addWriteFile(expanded_src_path, src_file.source); exe.step.dependOn(&write_src.step); } @@ -499,7 +508,10 @@ pub const CompareOutputContext = struct { } for (case.sources.toSliceConst()) |src_file| { - const expanded_src_path = os.path.join(b.allocator, [][]const u8{b.cache_root, src_file.filename}) catch unreachable; + const expanded_src_path = os.path.join( + b.allocator, + [][]const u8{ b.cache_root, src_file.filename }, + ) catch unreachable; const write_src = b.addWriteFile(expanded_src_path, src_file.source); exe.step.dependOn(&write_src.step); } @@ -572,8 +584,14 @@ pub const CompileErrorContext = struct { const self = @fieldParentPtr(CompileCmpOutputStep, "step", step); const b = self.context.b; - const root_src = os.path.join(b.allocator, [][]const u8{b.cache_root, self.case.sources.items[0].filename}) catch unreachable; - const obj_path = os.path.join(b.allocator, [][]const u8{b.cache_root, "test.o"}) catch unreachable; + const root_src = os.path.join( + b.allocator, + [][]const u8{ b.cache_root, self.case.sources.items[0].filename }, + ) catch unreachable; + const obj_path = os.path.join( + b.allocator, + [][]const u8{ b.cache_root, "test.o" }, + ) catch unreachable; var zig_args = ArrayList([]const u8).init(b.allocator); zig_args.append(b.zig_exe) catch unreachable; @@ -721,7 +739,10 @@ 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, [][]const u8{b.cache_root, src_file.filename}) catch unreachable; + const expanded_src_path = os.path.join( + b.allocator, + [][]const u8{ b.cache_root, src_file.filename }, + ) catch unreachable; const write_src = b.addWriteFile(expanded_src_path, src_file.source); compile_and_cmp_errors.step.dependOn(&write_src.step); } @@ -852,7 +873,10 @@ pub const TranslateCContext = struct { const self = @fieldParentPtr(TranslateCCmpOutputStep, "step", step); const b = self.context.b; - const root_src = os.path.join(b.allocator, [][]const u8{b.cache_root, self.case.sources.items[0].filename}) catch unreachable; + const root_src = os.path.join( + b.allocator, + [][]const u8{ b.cache_root, self.case.sources.items[0].filename }, + ) catch unreachable; var zig_args = ArrayList([]const u8).init(b.allocator); zig_args.append(b.zig_exe) catch unreachable; @@ -986,7 +1010,10 @@ pub const TranslateCContext = struct { self.step.dependOn(&translate_c_and_cmp.step); for (case.sources.toSliceConst()) |src_file| { - const expanded_src_path = os.path.join(b.allocator, [][]const u8{b.cache_root, src_file.filename}) catch unreachable; + const expanded_src_path = os.path.join( + b.allocator, + [][]const u8{ b.cache_root, src_file.filename }, + ) catch unreachable; const write_src = b.addWriteFile(expanded_src_path, src_file.source); translate_c_and_cmp.step.dependOn(&write_src.step); } @@ -1101,7 +1128,10 @@ pub const GenHContext = struct { pub fn addCase(self: *GenHContext, case: *const TestCase) void { const b = self.b; - const root_src = os.path.join(b.allocator, [][]const u8{b.cache_root, case.sources.items[0].filename}) catch unreachable; + const root_src = os.path.join( + b.allocator, + [][]const u8{ b.cache_root, case.sources.items[0].filename }, + ) catch unreachable; const mode = builtin.Mode.Debug; const annotated_case_name = fmt.allocPrint(self.b.allocator, "gen-h {} ({})", case.name, @tagName(mode)) catch unreachable; @@ -1113,7 +1143,10 @@ pub const GenHContext = struct { obj.setBuildMode(mode); for (case.sources.toSliceConst()) |src_file| { - const expanded_src_path = os.path.join(b.allocator, [][]const u8{b.cache_root, src_file.filename}) catch unreachable; + const expanded_src_path = os.path.join( + b.allocator, + [][]const u8{ b.cache_root, src_file.filename }, + ) catch unreachable; const write_src = b.addWriteFile(expanded_src_path, src_file.source); obj.step.dependOn(&write_src.step); } -- cgit v1.2.3