diff --git a/.github/workflows/gen-docs.yml b/.github/workflows/gen-docs.yml index ba352062c..f869b8b17 100644 --- a/.github/workflows/gen-docs.yml +++ b/.github/workflows/gen-docs.yml @@ -12,7 +12,7 @@ jobs: deploy: runs-on: ubuntu-22.04 env: - ZIG_VERSION: 0.11.0 + ZIG_VERSION: 0.12.0 steps: - name: Clone repo. uses: actions/checkout@v3 diff --git a/.github/workflows/latest-build.yml b/.github/workflows/latest-build.yml index 60c994083..12ce299ed 100644 --- a/.github/workflows/latest-build.yml +++ b/.github/workflows/latest-build.yml @@ -200,14 +200,14 @@ jobs: restore-keys: build-${{ env.BUILD_TARGET }}-${{ env.BUILD_MODE }}-1 - - name: Run tests. (cli, debug) - if: env.BUILD_TARGET != 'wasm32-wasi' && env.BUILD_CMD == 'cli' + - name: Run tests. (debug) + if: env.BUILD_TARGET != 'wasm32-wasi' && (env.BUILD_CMD == 'cli' || env.BUILD_CMD == 'lib') run: | zig build test ${{ env.ZIG_TARGET_FLAG }} # Optimize with ReleaseSafe since there are issues with Zig 0.11.0 and building tests for ReleaseFast. - - name: Run tests. (cli, release) - if: env.BUILD_TARGET != 'wasm32-wasi' && env.BUILD_CMD == 'cli' + - name: Run tests. (release) + if: env.BUILD_TARGET != 'wasm32-wasi' && (env.BUILD_CMD == 'cli' || env.BUILD_CMD == 'lib') run: | zig build test ${{ env.ZIG_TARGET_FLAG }} -Doptimize=ReleaseSafe diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml index 43f7f482a..7d1c59ce1 100644 --- a/.github/workflows/pr-build.yml +++ b/.github/workflows/pr-build.yml @@ -152,14 +152,14 @@ jobs: restore-keys: pr-build-${{ env.BUILD_TARGET }}-${{ env.BUILD_MODE }}-1 - - name: Run tests. (cli, debug) - if: env.BUILD_TARGET != 'wasm32-wasi' && env.BUILD_CMD == 'cli' + - name: Run tests. (debug) + if: env.BUILD_TARGET != 'wasm32-wasi' && (env.BUILD_CMD == 'cli' || env.BUILD_CMD == 'lib') run: | zig build test ${{ env.ZIG_TARGET_FLAG }} # Optimize with ReleaseSafe since there are issues with Zig 0.11.0 and building tests for ReleaseFast. - - name: Run tests. (cli, release) - if: env.BUILD_TARGET != 'wasm32-wasi' && env.BUILD_CMD == 'cli' + - name: Run tests. (release) + if: env.BUILD_TARGET != 'wasm32-wasi' && (env.BUILD_CMD == 'cli' || env.BUILD_CMD == 'lib') run: | zig build test ${{ env.ZIG_TARGET_FLAG }} -Doptimize=ReleaseSafe diff --git a/build.zig b/build.zig index 332c74935..903117c37 100644 --- a/build.zig +++ b/build.zig @@ -19,10 +19,6 @@ var optStatic: ?bool = undefined; var optJIT: ?bool = undefined; var optRT: ?config.Runtime = undefined; -var tcc: *std.Build.Module = undefined; -var linenoise: *std.Build.Module = undefined; -var mimalloc: *std.Build.Module = undefined; - var rtarget: std.Build.ResolvedTarget = undefined; var target: std.Target = undefined; var optimize: std.builtin.OptimizeMode = undefined; @@ -44,10 +40,6 @@ pub fn build(b: *std.Build) !void { optRT = b.option(config.Runtime, "rt", "Runtime."); link_test = b.option(bool, "link-test", "Build test by linking lib. Disable for better stack traces.") orelse true; - tcc = tcc_lib.createModule(b); - mimalloc = mimalloc_lib.createModule(b); - linenoise = createLinenoiseModule(b); - { const step = b.step("cli", "Build main cli."); @@ -228,6 +220,7 @@ pub fn build(b: *std.Build) !void { fn createAllModule(b: *std.Build, build_options: *std.Build.Module, stdx: *std.Build.Module, opts: Options) !*std.Build.Module { const mod = b.createModule(.{ .root_source_file = .{ .path = thisDir() ++ "/src/all.zig" }, + .target = rtarget, }); mod.addIncludePath(.{ .path = thisDir() ++ "/src" }); try buildAndLinkDeps(mod, build_options, stdx, opts); @@ -244,6 +237,7 @@ pub fn buildAndLinkDeps(mod: *std.Build.Module, build_options: *std.Build.Module mod.link_libc = true; if (opts.malloc == .mimalloc) { + const mimalloc = mimalloc_lib.createModule(b); mod.addImport("mimalloc", mimalloc); mimalloc_lib.buildAndLink(b, mod, .{ .target = rtarget, @@ -260,8 +254,8 @@ pub fn buildAndLinkDeps(mod: *std.Build.Module, build_options: *std.Build.Module } if (opts.ffi) { - const tcc_ = tcc_lib.createModule(b); - tcc_lib.addImport(mod, "tcc", tcc_); + const tcc = tcc_lib.createModule(b); + tcc_lib.addImport(mod, "tcc", tcc); tcc_lib.buildAndLink(b, mod, .{ .selinux = selinux, .target = rtarget, @@ -276,8 +270,8 @@ pub fn buildAndLinkDeps(mod: *std.Build.Module, build_options: *std.Build.Module } if (opts.cli and target.os.tag != .windows and target.os.tag != .wasi) { - const linenoise_ = createLinenoiseModule(b); - mod.addImport("linenoise", linenoise_); + const linenoise = createLinenoiseModule(b); + mod.addImport("linenoise", linenoise); buildAndLinkLinenoise(b, mod); } else { mod.addAnonymousImport("linenoise", .{ @@ -447,6 +441,9 @@ fn addTraceTest(b: *std.Build, opts: Options) !*std.Build.Step.Compile { }); step.addIncludePath(.{ .path = thisDir() ++ "/src" }); + // For linux FFI test. + step.rdynamic = true; + var opts_ = opts; opts_.trace = true; const lib = try buildLib(b, opts_); @@ -526,11 +523,6 @@ pub fn buildCVM(b: *std.Build, opts: Options) !*std.Build.Step.Compile { } else { try cflags.append("-DDEBUG=0"); } - if (optimize == .Debug) { - try cflags.append("-DDEBUG=1"); - } else { - try cflags.append("-DDEBUG=0"); - } try cflags.append("-DCGOTO=1"); if (opts.trackGlobalRc) { try cflags.append("-DTRACK_GLOBAL_RC=1"); diff --git a/src/platform.zig b/src/platform.zig index edafc0111..d6fd8b00b 100644 --- a/src/platform.zig +++ b/src/platform.zig @@ -1,36 +1,88 @@ +/// Extracted from https://github.com/ziglibs/known-folders/blob/master/known-folders.zig + const std = @import("std"); +const root = @import("root"); const builtin = @import("builtin"); -/// Extracted from https://github.com/ziglibs/known-folders/blob/master/known-folders.zig - pub const KnownFolder = enum { home, + documents, + pictures, + music, + videos, + desktop, + downloads, + public, + fonts, + app_menu, + cache, + roaming_configuration, local_configuration, + global_configuration, + data, + runtime, + executable_dir, }; +// Explicitly define possible errors to make it clearer what callers need to handle pub const Error = error{ ParseError, OutOfMemory }; +pub const KnownFolderConfig = struct { + xdg_force_default: bool = false, + xdg_on_mac: bool = false, +}; + +/// Returns a directory handle, or, if the folder does not exist, `null`. +pub fn open(allocator: std.mem.Allocator, folder: KnownFolder, args: std.fs.Dir.OpenDirOptions) (std.fs.Dir.OpenError || Error)!?std.fs.Dir { + const path_or_null = try getPath(allocator, folder); + if (path_or_null) |path| { + defer allocator.free(path); + + return try std.fs.cwd().openDir(path, args); + } else { + return null; + } +} + /// Returns the path to the folder or, if the folder does not exist, `null`. pub fn getPath(allocator: std.mem.Allocator, folder: KnownFolder) Error!?[]const u8 { + if (folder == .executable_dir) { + if (builtin.os.tag == .wasi) return null; + return std.fs.selfExeDirPathAlloc(allocator) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + else => null, + }; + } + // used for temporary allocations var arena = std.heap.ArenaAllocator.init(allocator); defer arena.deinit(); switch (builtin.os.tag) { .windows => { + const funcs = struct { + extern "shell32" fn SHGetKnownFolderPath( + rfid: *const std.os.windows.GUID, + dwFlags: std.os.windows.DWORD, + hToken: ?std.os.windows.HANDLE, + ppszPathL: *std.os.windows.PWSTR, + ) callconv(std.os.windows.WINAPI) std.os.windows.HRESULT; + extern "ole32" fn CoTaskMemFree(pv: std.os.windows.LPVOID) callconv(std.os.windows.WINAPI) void; + }; + const folder_spec = windows_folder_spec.get(folder); switch (folder_spec) { .by_guid => |guid| { var dir_path_ptr: [*:0]u16 = undefined; - switch (std.os.windows.shell32.SHGetKnownFolderPath( + switch (funcs.SHGetKnownFolderPath( &guid, std.os.windows.KF_FLAG_CREATE, // TODO: Chose sane option here? null, &dir_path_ptr, )) { std.os.windows.S_OK => { - defer std.os.windows.ole32.CoTaskMemFree(@ptrCast(dir_path_ptr)); + defer funcs.CoTaskMemFree(@ptrCast(dir_path_ptr)); const global_dir = std.unicode.utf16leToUtf8Alloc(allocator, std.mem.span(dir_path_ptr)) catch |err| switch (err) { error.UnexpectedSecondSurrogateHalf => return null, error.ExpectedSecondSurrogateHalf => return null, @@ -47,14 +99,14 @@ pub fn getPath(allocator: std.mem.Allocator, folder: KnownFolder) Error!?[]const if (env_path.subdir) |sub_dir| { const root_path = std.process.getEnvVarOwned(arena.allocator(), env_path.env_var) catch |err| switch (err) { error.EnvironmentVariableNotFound => return null, - error.InvalidUtf8 => return null, + error.InvalidWtf8 => return null, error.OutOfMemory => |e| return e, }; return try std.fs.path.join(allocator, &[_][]const u8{ root_path, sub_dir }); } else { return std.process.getEnvVarOwned(allocator, env_path.env_var) catch |err| switch (err) { error.EnvironmentVariableNotFound => return null, - error.InvalidUtf8 => return null, + error.InvalidWtf8 => return null, error.OutOfMemory => |e| return e, }; } @@ -62,6 +114,10 @@ pub fn getPath(allocator: std.mem.Allocator, folder: KnownFolder) Error!?[]const } }, .macos => { + if (@hasDecl(root, "known_folders_config") and root.known_folders_config.xdg_on_mac) { + return getPathXdg(allocator, &arena, folder); + } + switch (mac_folder_spec.get(folder)) { .absolute => |abs| { return try allocator.dupe(u8, abs); @@ -92,24 +148,30 @@ pub fn getPath(allocator: std.mem.Allocator, folder: KnownFolder) Error!?[]const fn getPathXdg(allocator: std.mem.Allocator, arena: *std.heap.ArenaAllocator, folder: KnownFolder) Error!?[]const u8 { const folder_spec = xdg_folder_spec.get(folder); - var env_opt = std.os.getenv(folder_spec.env.name); + if (@hasDecl(root, "known_folders_config") and root.known_folders_config.xdg_force_default) { + if (folder_spec.default) |default| { + if (default[0] == '~') { + const home = std.process.getEnvVarOwned(arena.allocator(), "HOME") catch null orelse return null; + return try std.mem.concat(allocator, u8, &[_][]const u8{ home, default[1..] }); + } else { + return try allocator.dupe(u8, default); + } + } + } + + const env_opt = env_opt: { + if (std.process.getEnvVarOwned(arena.allocator(), folder_spec.env.name) catch null) |env_opt| break :env_opt env_opt; - // TODO: add caching so we only need to read once in a run - if (env_opt == null and folder_spec.env.user_dir) block: { - const config_dir_path = if (std.io.is_async) blk: { - var frame = arena.allocator().create(@Frame(getPathXdg)) catch break :block; - _ = @asyncCall(frame, {}, getPathXdg, .{ arena.allocator(), arena, .local_configuration }); - break :blk (await frame) catch null orelse break :block; - } else blk: { - break :blk getPathXdg(arena.allocator(), arena, .local_configuration) catch null orelse break :block; - }; + if (!folder_spec.env.user_dir) break :env_opt null; - const config_dir = std.fs.cwd().openDir(config_dir_path, .{}) catch break :block; - const home = std.os.getenv("HOME") orelse break :block; - const user_dirs = config_dir.openFile("user-dirs.dirs", .{}) catch null orelse break :block; + // TODO: add caching so we only need to read once in a run + const config_dir_path = getPathXdg(arena.allocator(), arena, .local_configuration) catch null orelse break :env_opt null; + const config_dir = std.fs.cwd().openDir(config_dir_path, .{}) catch break :env_opt null; + const home = std.process.getEnvVarOwned(arena.allocator(), "HOME") catch null orelse break :env_opt null; + const user_dirs = config_dir.openFile("user-dirs.dirs", .{}) catch null orelse break :env_opt null; var read: [1024 * 8]u8 = undefined; - _ = user_dirs.readAll(&read) catch null orelse break :block; + _ = user_dirs.readAll(&read) catch null orelse break :env_opt null; const start = folder_spec.env.name.len + "=\"$HOME".len; var line_it = std.mem.split(u8, &read, "\n"); @@ -122,11 +184,11 @@ fn getPathXdg(allocator: std.mem.Allocator, arena: *std.heap.ArenaAllocator, fol const subdir = line[start..end]; - env_opt = try std.mem.concatWithSentinel(arena.allocator(), u8, &[_][]const u8{ home, subdir }, 0); - break; + break :env_opt try std.mem.concat(arena.allocator(), u8, &[_][]const u8{ home, subdir }); } } - } + break :env_opt null; + }; if (env_opt) |env| { if (folder_spec.env.suffix) |suffix| { @@ -142,7 +204,7 @@ fn getPathXdg(allocator: std.mem.Allocator, arena: *std.heap.ArenaAllocator, fol } else { const default = folder_spec.default orelse return null; if (default[0] == '~') { - const home = std.os.getenv("HOME") orelse return null; + const home = std.process.getEnvVarOwned(arena.allocator(), "HOME") catch null orelse return null; return try std.mem.concat(allocator, u8, &[_][]const u8{ home, default[1..] }); } else { return try allocator.dupe(u8, default); @@ -150,25 +212,29 @@ fn getPathXdg(allocator: std.mem.Allocator, arena: *std.heap.ArenaAllocator, fol } } -/// Stores how to find each known folder on windows. -const windows_folder_spec = blk: { - // workaround for zig eval branch quota when parsing the GUIDs -// @setEvalBranchQuota(10_000); - break :blk KnownFolderSpec(WindowsFolderSpec){ - .home = WindowsFolderSpec{ .by_guid = std.os.windows.GUID.parse("{5E6C858F-0E22-4760-9AFE-EA3317B67173}") }, // FOLDERID_Profile - .local_configuration = WindowsFolderSpec{ .by_guid = std.os.windows.GUID.parse("{F1B32785-6FBA-4FCF-9D55-7B8E7F157091}") }, // FOLDERID_LocalAppData - }; +/// Contains the GUIDs for each available known-folder on windows +const WindowsFolderSpec = union(enum) { + by_guid: std.os.windows.GUID, + by_env: struct { + env_var: []const u8, + subdir: ?[]const u8, + }, }; -const mac_folder_spec = KnownFolderSpec(MacFolderSpec){ - .home = MacFolderSpec{ .suffix = null }, - .local_configuration = MacFolderSpec{ .suffix = "Library/Application Support" }, +/// Contains the xdg environment variable amd the default value for each available known-folder on windows +const XdgFolderSpec = struct { + env: struct { + name: []const u8, + user_dir: bool, + suffix: ?[]const u8, + }, + default: ?[]const u8, }; -/// Stores how to find each known folder in xdg. -const xdg_folder_spec = KnownFolderSpec(XdgFolderSpec){ - .home = XdgFolderSpec{ .env = .{ .name = "HOME", .user_dir = false, .suffix = null }, .default = null }, - .local_configuration = XdgFolderSpec{ .env = .{ .name = "XDG_CONFIG_HOME", .user_dir = false, .suffix = null }, .default = "~/.config" }, +/// Contains the folder items for macOS +const MacFolderSpec = union(enum) { + suffix: ?[]const u8, + absolute: []const u8, }; /// This returns a struct type with one field per KnownFolder of type `T`. @@ -178,7 +244,21 @@ fn KnownFolderSpec(comptime T: type) type { const Self = @This(); home: T, + documents: T, + pictures: T, + music: T, + videos: T, + desktop: T, + downloads: T, + public: T, + fonts: T, + app_menu: T, + cache: T, + roaming_configuration: T, local_configuration: T, + global_configuration: T, + data: T, + runtime: T, fn get(self: Self, folder: KnownFolder) T { inline for (std.meta.fields(Self)) |fld| { @@ -190,31 +270,77 @@ fn KnownFolderSpec(comptime T: type) type { }; } -/// Contains the GUIDs for each available known-folder on windows -const WindowsFolderSpec = union(enum) { - by_guid: std.os.windows.GUID, - by_env: struct { - env_var: []const u8, - subdir: ?[]const u8, - }, +/// Stores how to find each known folder on windows. +const windows_folder_spec = blk: { + // workaround for zig eval branch quota when parsing the GUIDs + @setEvalBranchQuota(10_000); + break :blk KnownFolderSpec(WindowsFolderSpec){ + .home = WindowsFolderSpec{ .by_guid = std.os.windows.GUID.parse("{5E6C858F-0E22-4760-9AFE-EA3317B67173}") }, // FOLDERID_Profile + .documents = WindowsFolderSpec{ .by_guid = std.os.windows.GUID.parse("{FDD39AD0-238F-46AF-ADB4-6C85480369C7}") }, // FOLDERID_Documents + .pictures = WindowsFolderSpec{ .by_guid = std.os.windows.GUID.parse("{33E28130-4E1E-4676-835A-98395C3BC3BB}") }, // FOLDERID_Pictures + .music = WindowsFolderSpec{ .by_guid = std.os.windows.GUID.parse("{4BD8D571-6D19-48D3-BE97-422220080E43}") }, // FOLDERID_Music + .videos = WindowsFolderSpec{ .by_guid = std.os.windows.GUID.parse("{18989B1D-99B5-455B-841C-AB7C74E4DDFC}") }, // FOLDERID_Videos + .desktop = WindowsFolderSpec{ .by_guid = std.os.windows.GUID.parse("{B4BFCC3A-DB2C-424C-B029-7FE99A87C641}") }, // FOLDERID_Desktop + .downloads = WindowsFolderSpec{ .by_guid = std.os.windows.GUID.parse("{374DE290-123F-4565-9164-39C4925E467B}") }, // FOLDERID_Downloads + .public = WindowsFolderSpec{ .by_guid = std.os.windows.GUID.parse("{DFDF76A2-C82A-4D63-906A-5644AC457385}") }, // FOLDERID_Public + .fonts = WindowsFolderSpec{ .by_guid = std.os.windows.GUID.parse("{FD228CB7-AE11-4AE3-864C-16F3910AB8FE}") }, // FOLDERID_Fonts + .app_menu = WindowsFolderSpec{ .by_guid = std.os.windows.GUID.parse("{625B53C3-AB48-4EC1-BA1F-A1EF4146FC19}") }, // FOLDERID_StartMenu + .cache = WindowsFolderSpec{ .by_env = .{ .env_var = "LOCALAPPDATA", .subdir = "Temp" } }, // %LOCALAPPDATA%\Temp + .roaming_configuration = WindowsFolderSpec{ .by_guid = std.os.windows.GUID.parse("{3EB685DB-65F9-4CF6-A03A-E3EF65729F3D}") }, // FOLDERID_RoamingAppData + .local_configuration = WindowsFolderSpec{ .by_guid = std.os.windows.GUID.parse("{F1B32785-6FBA-4FCF-9D55-7B8E7F157091}") }, // FOLDERID_LocalAppData + .global_configuration = WindowsFolderSpec{ .by_guid = std.os.windows.GUID.parse("{62AB5D82-FDC1-4DC3-A9DD-070D1D495D97}") }, // FOLDERID_ProgramData + .data = WindowsFolderSpec{ .by_env = .{ .env_var = "APPDATA", .subdir = null } }, // %LOCALAPPDATA%\Temp + .runtime = WindowsFolderSpec{ .by_env = .{ .env_var = "LOCALAPPDATA", .subdir = "Temp" } }, + }; }; -/// Contains the folder items for macOS -const MacFolderSpec = union(enum) { - suffix: ?[]const u8, - absolute: []const u8, +/// Stores how to find each known folder in xdg. +const xdg_folder_spec = KnownFolderSpec(XdgFolderSpec){ + .home = XdgFolderSpec{ .env = .{ .name = "HOME", .user_dir = false, .suffix = null }, .default = null }, + .documents = XdgFolderSpec{ .env = .{ .name = "XDG_DOCUMENTS_DIR", .user_dir = true, .suffix = null }, .default = "~/Documents" }, + .pictures = XdgFolderSpec{ .env = .{ .name = "XDG_PICTURES_DIR", .user_dir = true, .suffix = null }, .default = "~/Pictures" }, + .music = XdgFolderSpec{ .env = .{ .name = "XDG_MUSIC_DIR", .user_dir = true, .suffix = null }, .default = "~/Music" }, + .videos = XdgFolderSpec{ .env = .{ .name = "XDG_VIDEOS_DIR", .user_dir = true, .suffix = null }, .default = "~/Videos" }, + .desktop = XdgFolderSpec{ .env = .{ .name = "XDG_DESKTOP_DIR", .user_dir = true, .suffix = null }, .default = "~/Desktop" }, + .downloads = XdgFolderSpec{ .env = .{ .name = "XDG_DOWNLOAD_DIR", .user_dir = true, .suffix = null }, .default = "~/Downloads" }, + .public = XdgFolderSpec{ .env = .{ .name = "XDG_PUBLICSHARE_DIR", .user_dir = true, .suffix = null }, .default = "~/Public" }, + .fonts = XdgFolderSpec{ .env = .{ .name = "XDG_DATA_HOME", .user_dir = false, .suffix = "/fonts" }, .default = "~/.local/share/fonts" }, + .app_menu = XdgFolderSpec{ .env = .{ .name = "XDG_DATA_HOME", .user_dir = false, .suffix = "/applications" }, .default = "~/.local/share/applications" }, + .cache = XdgFolderSpec{ .env = .{ .name = "XDG_CACHE_HOME", .user_dir = false, .suffix = null }, .default = "~/.cache" }, + .roaming_configuration = XdgFolderSpec{ .env = .{ .name = "XDG_CONFIG_HOME", .user_dir = false, .suffix = null }, .default = "~/.config" }, + .local_configuration = XdgFolderSpec{ .env = .{ .name = "XDG_CONFIG_HOME", .user_dir = false, .suffix = null }, .default = "~/.config" }, + .global_configuration = XdgFolderSpec{ .env = .{ .name = "XDG_CONFIG_DIRS", .user_dir = false, .suffix = null }, .default = "/etc" }, + .data = XdgFolderSpec{ .env = .{ .name = "XDG_DATA_HOME", .user_dir = false, .suffix = null }, .default = "~/.local/share" }, + .runtime = XdgFolderSpec{ .env = .{ .name = "XDG_RUNTIME_DIR", .user_dir = false, .suffix = null }, .default = null }, }; -/// Contains the xdg environment variable amd the default value for each available known-folder on windows -const XdgFolderSpec = struct { - env: struct { - name: []const u8, - user_dir: bool, - suffix: ?[]const u8, - }, - default: ?[]const u8, +const mac_folder_spec = KnownFolderSpec(MacFolderSpec){ + .home = MacFolderSpec{ .suffix = null }, + .documents = MacFolderSpec{ .suffix = "Documents" }, + .pictures = MacFolderSpec{ .suffix = "Pictures" }, + .music = MacFolderSpec{ .suffix = "Music" }, + .videos = MacFolderSpec{ .suffix = "Movies" }, + .desktop = MacFolderSpec{ .suffix = "Desktop" }, + .downloads = MacFolderSpec{ .suffix = "Downloads" }, + .public = MacFolderSpec{ .suffix = "Public" }, + .fonts = MacFolderSpec{ .suffix = "Library/Fonts" }, + .app_menu = MacFolderSpec{ .suffix = "Applications" }, + .cache = MacFolderSpec{ .suffix = "Library/Caches" }, + .roaming_configuration = MacFolderSpec{ .suffix = "Library/Preferences" }, + .local_configuration = MacFolderSpec{ .suffix = "Library/Application Support" }, + .global_configuration = MacFolderSpec{ .absolute = "/Library/Preferences" }, + .data = MacFolderSpec{ .suffix = "Library/Application Support" }, + .runtime = MacFolderSpec{ .suffix = "Library/Application Support" }, }; +// Ref decls +comptime { + _ = KnownFolder; + _ = Error; + _ = open; + _ = getPath; +} + test "query each known folders" { inline for (std.meta.fields(KnownFolder)) |fld| { const path_or_null = try getPath(std.testing.allocator, @field(KnownFolder, fld.name)); @@ -224,4 +350,16 @@ test "query each known folders" { std.testing.allocator.free(path); } } -} \ No newline at end of file +} + +test "open each known folders" { + inline for (std.meta.fields(KnownFolder)) |fld| { + var dir_or_null = open(std.testing.allocator, @field(KnownFolder, fld.name), .{ .access_sub_paths = true }) catch |e| switch (e) { + error.FileNotFound => return, + else => return e, + }; + if (dir_or_null) |*dir| { + dir.close(); + } + } +} diff --git a/src/sema.zig b/src/sema.zig index 7f5faa299..b11789a3d 100644 --- a/src/sema.zig +++ b/src/sema.zig @@ -5965,7 +5965,7 @@ pub fn unescapeSeq(seq: []const u8) !CharAdvance { test "sema internals." { if (builtin.mode == .ReleaseFast) { if (cy.is32Bit) { - try t.eq(@sizeOf(LocalVar), 36); + try t.eq(@sizeOf(LocalVar), 32); } else { try t.eq(@sizeOf(LocalVar), 40); } diff --git a/src/std/os_ffi.zig b/src/std/os_ffi.zig index 7842d496c..d28efe5f5 100644 --- a/src/std/os_ffi.zig +++ b/src/std/os_ffi.zig @@ -1024,13 +1024,15 @@ const CFuncData = struct { fn dlopen(path: []const u8) !std.DynLib { if (builtin.os.tag == .linux and builtin.link_libc) { - const path_c = try std.os.toPosixPath(path); + const path_c = try std.posix.toPosixPath(path); // Place the lookup scope of the symbols in this library ahead of the global scope. const RTLD_DEEPBIND = 0x00008; return std.DynLib{ - .handle = std.os.system.dlopen(&path_c, std.os.system.RTLD.LAZY | RTLD_DEEPBIND) orelse { - return error.FileNotFound; - } + .inner = .{ + .handle = std.c.dlopen(&path_c, std.c.RTLD.LAZY | RTLD_DEEPBIND) orelse { + return error.FileNotFound; + }, + }, }; } else { return std.DynLib.open(path); diff --git a/src/vm.h b/src/vm.h index 704cbd344..b60327e47 100644 --- a/src/vm.h +++ b/src/vm.h @@ -800,13 +800,11 @@ typedef struct VMC { void* typesPtr; size_t typesLen; -#if TRACK_GLOBAL_RC - size_t refCounts; -#endif - -#if TRACE TraceInfo* trace; u32 debugPc; + +#if TRACK_GLOBAL_RC + size_t refCounts; #endif } VMC; diff --git a/src/vm.zig b/src/vm.zig index 03306020c..6008e80bb 100644 --- a/src/vm.zig +++ b/src/vm.zig @@ -68,13 +68,13 @@ const VMC = extern struct { types: [*]const types.Type, types_len: usize, - refCounts: if (cy.TrackGlobalRC) usize else void, - - trace: if (cy.Trace) *vmc.TraceInfo else void, + trace: *vmc.TraceInfo, /// In debug mode, always save the current pc so tracing can be obtained. /// debugPc == NullId indicates execution has not started. - debugPc: if (cy.Trace) u32 else void, + debugPc: u32, + + refCounts: if (cy.TrackGlobalRC) usize else void, pub fn getTryStack(self: *VMC) *cy.List(vmc.TryFrame) { return @ptrCast(&self.tryStack); @@ -105,8 +105,8 @@ pub const VM = struct { /// Object heap pages. heapPages: cy.List(*cy.heap.HeapPage), heapFreeHead: ?*HeapObject, - /// Tail is only used in trace mode. - heapFreeTail: if (cy.Trace) ?*HeapObject else void, + /// Trace mode. + heapFreeTail: ?*HeapObject, /// GC: Contains the head to the first cyclable object. /// Always contains one dummy node to avoid null checking. @@ -173,8 +173,8 @@ pub const VM = struct { print: cc.PrintFn, print_err: cc.PrintErrorFn, - /// Object to pc of instruction that allocated it. - objectTraceMap: if (cy.Trace) std.AutoHashMapUnmanaged(*HeapObject, debug.ObjectTrace) else void, + /// Object to pc of instruction that allocated it. Trace mode. + objectTraceMap: std.AutoHashMapUnmanaged(*HeapObject, debug.ObjectTrace), /// Interface used for imports and fetch. httpClient: http.HttpClient, @@ -191,8 +191,9 @@ pub const VM = struct { config: cc.EvalConfig, - countFrees: if (cy.Trace) bool else void, - numFreed: if (cy.Trace) u32 else void, + // Trace mode. + countFrees: bool, + numFreed: u32, /// Whether this VM is already deinited. Used to skip the next deinit to avoid using undefined memory. deinited: bool, @@ -250,7 +251,7 @@ pub const VM = struct { .refCounts = if (cy.TrackGlobalRC) 0 else undefined, .mainFiber = undefined, .curFiber = undefined, - .debugPc = if (cy.Trace) cy.NullId else undefined, + .debugPc = cy.NullId, }, .methods = .{}, .method_map = .{}, @@ -273,7 +274,7 @@ pub const VM = struct { .unwindTempPrevIndexes = undefined, .compactTrace = .{}, .endLocal = undefined, - .objectTraceMap = if (cy.Trace) .{} else undefined, + .objectTraceMap = .{}, // Initialize to NullId to indicate vm is still in initing. .deinited = false, .deinitedRtObjects = false, @@ -284,8 +285,8 @@ pub const VM = struct { .varSymExtras = .{}, .print = defaultPrint, .print_err = defaultPrintError, - .countFrees = if (cy.Trace) false else {}, - .numFreed = if (cy.Trace) 0 else {}, + .countFrees = false, + .numFreed = 0, .tempBuf = undefined, .lastExeError = "", .last_res = cc.Success, @@ -1806,18 +1807,16 @@ test "vm internals." { try t.eq(@offsetOf(VMC, "ops"), @offsetOf(vmc.VMC, "instPtr")); try t.eq(@offsetOf(VMC, "consts"), @offsetOf(vmc.VMC, "constPtr")); try t.eq(@offsetOf(VMC, "tryStack"), @offsetOf(vmc.VMC, "tryStack")); - if (cy.TrackGlobalRC) { - try t.eq(@offsetOf(VMC, "refCounts"), @offsetOf(vmc.VMC, "refCounts")); - } try t.eq(@offsetOf(VMC, "varSyms"), @offsetOf(vmc.VMC, "varSyms")); try t.eq(@offsetOf(VMC, "fieldSyms"), @offsetOf(vmc.VMC, "fieldSyms")); try t.eq(@offsetOf(VMC, "curFiber"), @offsetOf(vmc.VMC, "curFiber")); try t.eq(@offsetOf(VMC, "mainFiber"), @offsetOf(vmc.VMC, "mainFiber")); - if (cy.Trace) { - try t.eq(@offsetOf(VMC, "trace"), @offsetOf(vmc.VMC, "trace")); - try t.eq(@offsetOf(VMC, "debugPc"), @offsetOf(vmc.VMC, "debugPc")); + try t.eq(@offsetOf(VMC, "types"), @offsetOf(vmc.VMC, "typesPtr")); + if (cy.TrackGlobalRC) { + try t.eq(@offsetOf(VMC, "refCounts"), @offsetOf(vmc.VMC, "refCounts")); } - + try t.eq(@offsetOf(VMC, "trace"), @offsetOf(vmc.VMC, "trace")); + try t.eq(@offsetOf(VMC, "debugPc"), @offsetOf(vmc.VMC, "debugPc")); try t.eq(@offsetOf(VM, "c"), @offsetOf(vmc.VM, "c")); }