From 7e78d738548f7535b959992a615e7f352627d06f Mon Sep 17 00:00:00 2001 From: Erica Marigold Date: Sat, 23 Nov 2024 12:51:22 +0000 Subject: [PATCH] feat: use lune linker scripts for binaries --- .gitignore | 2 +- core/src/init.luau | 141 +++++++++++++++++++++++++++++++++------------ lune/init.luau | 2 +- lune/pesde.lock | 26 ++++----- lune/pesde.toml | 7 ++- 5 files changed, 124 insertions(+), 54 deletions(-) diff --git a/.gitignore b/.gitignore index 738b417..67b9151 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ lune_packages/ luau_packages/ -# core/pesde.lock +core/pesde.lock diff --git a/core/src/init.luau b/core/src/init.luau index 16ce960..6228f95 100644 --- a/core/src/init.luau +++ b/core/src/init.luau @@ -63,49 +63,41 @@ local function downloadAndDecompress(asset: { return decompressedPath end -local function runTool(path: pathfs.Path) - -- TODO: Fixup our wrapper and use that instead in the future - process.spawn(path:toString(), process.args, { - stdio = "forward", - shell = true, - }) +local function chmod(path: pathfs.Path, mode: number) + if process.os ~= "windows" then + local child = process.spawn("chmod", { string.format("%o", mode), path:toString() }) + if not child.ok then + error(`chmod failed: {path} - {child.stderr}`) + end + end end -local TOOL_INSTALL_DIR = (dirs.homeDir() or error("Couldn't get home dir :(")) - :join(".pesde") - :join("bin") - :join("tool_storage") +local function toolAliasOrDefault(tool: ToolId): string + return tool.alias:unwrapOr(string.split((tool :: ToolId).repo, "/")[2]) +end --- if not pathfs.isDir(TOOL_INSTALL_DIR) then --- pathfs.writeDir(TOOL_INSTALL_DIR) --- end +local LINK_INSTALL_DIR = (dirs.homeDir() or error("Couldn't get home dir :(")):join(".pesde"):join("bin") +local TOOL_STORAGE_DIR = LINK_INSTALL_DIR:join("tool_storage") + +function runTool(tool: ToolId | pathfs.Path): number + -- FIXME: `process.spawn` has a bug where interactive features don't + -- forward properly + local toolId = tool :: ToolId + local path = if toolId.alias ~= nil then LINK_INSTALL_DIR:join(toolAliasOrDefault(toolId)) else tool :: pathfs.Path + + return process.spawn(path:toString(), process.args, { + cwd = process.cwd, + env = process.env, + stdio = "forward", + }).code +end function installTool(tool: ToolId) - local toolAlias = tool.alias:unwrapOr(string.split(tool.repo, "/")[2]) + local toolAlias = toolAliasOrDefault(tool) local toolId = string.gsub(tool.repo, "/", "+") - local toolInstallPath = TOOL_INSTALL_DIR:join(toolId) + local toolInstallPath = TOOL_STORAGE_DIR:join(toolId) :join(`{toolAlias}-` .. tostring(tool.version:map(tostring):unwrapOr("latest"))) - -- TODO: In order to eliminate fs read overhead on startup and to disallow - -- the use of the tool binary when outside a package where it is installed, - -- we can improve this by following what rokit does, where we symlink - -- the tool's path to this script, and check the file that we are being - -- invoked as, in order to figure out what tool to execute - - -- We can create "linker" scripts for each tool at ~/.pesde/bins with - -- contents like so: - --[[ - #!/bin/env -S lune run - - -- First off, we check whether the tool is installed in pesde.toml - -- if we're being run as a symlink, and not a `pesde x` bin - - local pathInfo = debug.info(1, "s") - local path = string.sub(pathInfo, 10, #pathInfo - 2)) - - -- Now we can use `path` to figure out the real tool to execute - -- ... - ]] if pathfs.isFile(toolInstallPath) then runTool(toolInstallPath) return @@ -187,10 +179,85 @@ function installTool(tool: ToolId) end pathfs.move(binaryPath, toolInstallPath) - runTool(toolInstallPath) + + -- In order to eliminate fs read overhead on startup and to disallow + -- the use of the tool binary when outside a package where it is installed, + -- we can improve this by following what rokit does, where we symlink + -- the tool's path to this script, and check the file that we are being + -- invoked as, in order to figure out what tool to execute + + -- We can create "linker" scripts for each tool at ~/.pesde/bins with + -- contents like so: + --[[ + #!/bin/env -S lune run + + -- First off, we check whether the tool is installed in pesde.toml + -- if we're being run as a symlink, and not a `pesde x` bin + + local pathInfo = debug.info(1, "s") + local path = string.sub(pathInfo, 10, #pathInfo - 2)) + + -- Now we can use `path` to figure out the real tool to execute + -- ... + ]] + + local linkPath = LINK_INSTALL_DIR:join(toolAlias) + pathfs.writeFile( + linkPath, + string.format( + [[#!/bin/env -S lune run + + local serde = require("@lune/serde") + local process = require("@lune/process") + local fs = require("@lune/fs") + + local TOOL_NAME = "%s" + --TOOL_PATH_BEGIN-- + local TOOL_PATH = "%s" + --TOOL_PATH_END-- + + local IS_DEV = true + local PLATFORM_SEP = if process.platform == "windows" then "\\" else "/" + + -- TODO: Check whether we are being run as `pesde x` and skip this check + -- That would involve getting the file path like this, probably: + local selfPathInfo = debug.info(1, "s") + local selfPath = string.sub(selfPathInfo, 10, #selfPathInfo - 2) + + -- FIXME: Does the CWD have a trailing slash on all platforms? + local manifestContents = fs.readFile(`{process.cwd}{PLATFORM_SEP}pesde.toml`) + local ok, manifest = pcall(serde.decode, "toml", manifestContents) + if not ok then + error(`Failed to parse pesde.toml: {tostring(manifest)}`) + end + + local isInstalled = IS_DEV + if manifest.dev_dependencies ~= nil then + for package, _ in manifest.dev_dependencies do + if package == TOOL_NAME then + isInstalled = true + break + end + end + end + + if not isInstalled then + error(`Tool {TOOL_NAME} in any pesde manifest file!`) + end + + process.exit(process.spawn(TOOL_PATH, process.args, { + stdio = "forward", + cwd = process.cwd, + env = process.env, + }).code)]], + string.split(toolId, "+")[2], + toolInstallPath:toString() + ) + ) + chmod(linkPath, 0b111101101) end return { - installTool = installTool, runTool = runTool, + installTool = installTool, } diff --git a/lune/init.luau b/lune/init.luau index 5fe7ded..bbccf46 100644 --- a/lune/init.luau +++ b/lune/init.luau @@ -3,7 +3,7 @@ local Semver = require("./luau_packages/semver") local Option = require("./lune_packages/option") type Option = Option.Option --- TODO: Use _G._PESDE_ROOT to get the install directory, then decode the +-- TODO: Use _G.PESDE_ROOT to get the install directory, then decode the -- pesde manifest to get the version of the tool dynamically core.installTool({ alias = Option.Some("lune"), diff --git a/lune/pesde.lock b/lune/pesde.lock index de18204..4fc19ea 100644 --- a/lune/pesde.lock +++ b/lune/pesde.lock @@ -1,10 +1,10 @@ name = "compeydev/lune" -version = "0.1.0" +version = "0.8.9" target = "lune" [graph."0x5eal/semver"."0.1.1 luau"] -direct = ["semver", { name = "0x5eal/semver", version = "^0.1.1", target = "luau" }] -ty = "standard" +direct = ["semver", { name = "0x5eal/semver", version = "^0.1.1", target = "luau" }, "standard"] +resolved_ty = "standard" [graph."0x5eal/semver"."0.1.1 luau".target] environment = "luau" @@ -30,8 +30,8 @@ environment = "luau" lib = "lib/init.luau" [graph."compeydev/binlib"."0.1.0 lune"] -direct = ["core", { workspace = "compeydev/binlib", version = "^" }] -ty = "standard" +direct = ["core", { workspace = "compeydev/binlib", version = "^" }, "standard"] +resolved_ty = "standard" [graph."compeydev/binlib"."0.1.0 lune".target] environment = "lune" @@ -60,7 +60,7 @@ environment = "lune" lib = "src/init.luau" [graph."jiwonz/dirs"."0.1.2 lune"] -ty = "standard" +resolved_ty = "standard" [graph."jiwonz/dirs"."0.1.2 lune".target] environment = "lune" @@ -83,7 +83,7 @@ environment = "lune" lib = "src/init.luau" [graph."jiwonz/pathfs"."0.1.0 lune"] -ty = "standard" +resolved_ty = "standard" [graph."jiwonz/pathfs"."0.1.0 lune".target] environment = "lune" @@ -100,8 +100,8 @@ environment = "lune" lib = "init.luau" [graph."lukadev_0/option"."1.2.0 lune"] -direct = ["option", { name = "lukadev_0/option", version = "^1.2.0" }] -ty = "standard" +direct = ["option", { name = "lukadev_0/option", version = "^1.2.0" }, "standard"] +resolved_ty = "standard" [graph."lukadev_0/option"."1.2.0 lune".target] environment = "lune" @@ -118,7 +118,7 @@ environment = "lune" lib = "lib/init.luau" [graph."lukadev_0/option"."1.2.0 luau"] -ty = "peer" +resolved_ty = "peer" [graph."lukadev_0/option"."1.2.0 luau".target] environment = "luau" @@ -135,8 +135,8 @@ environment = "luau" lib = "lib/init.luau" [graph."lukadev_0/result"."1.2.0 lune"] -direct = ["result", { name = "lukadev_0/result", version = "^1.2.0" }] -ty = "standard" +direct = ["result", { name = "lukadev_0/result", version = "^1.2.0" }, "standard"] +resolved_ty = "standard" [graph."lukadev_0/result"."1.2.0 lune".target] environment = "lune" @@ -153,7 +153,7 @@ environment = "lune" lib = "lib/init.luau" [graph."lukadev_0/result"."1.2.0 luau"] -ty = "peer" +resolved_ty = "peer" [graph."lukadev_0/result"."1.2.0 luau".target] environment = "luau" diff --git a/lune/pesde.toml b/lune/pesde.toml index 3702323..6b0f5a7 100644 --- a/lune/pesde.toml +++ b/lune/pesde.toml @@ -1,7 +1,10 @@ name = "compeydev/lune" -version = "0.1.0" +version = "0.8.9" description = "A standalone Luau runtime" -authors = ["CompeyDev ", "Filip Tibell "] +authors = [ + "CompeyDev ", + "Filip Tibell ", +] repository = "https://github.com/CompeyDev/pesde-tooling/blob/main/lune" license = "MIT"