From fcc976dcfb838f60526d2f38a6d3e9a1cfa76803 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phan=20Kochen?= Date: Sat, 29 Jan 2022 19:59:10 +0100 Subject: [PATCH] Add option to install binaries of dependencies --- README.md | 5 ++ src/InstallBinCommand.ts | 93 +++++++++++++++---------- src/index.ts | 6 ++ src/tmpl/bin-wrapper-node-modules.sh.in | 2 +- src/tmpl/bin-wrapper-pnp.sh.in | 2 +- 5 files changed, 70 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 80e8723..7f281b0 100644 --- a/README.md +++ b/README.md @@ -133,6 +133,11 @@ Some additional settings are available in `.yarnrc.yml`: - `isolatedNixBuilds`, see [ISOLATED_BUILDS.md](./ISOLATED_BUILDS.md). +- `installNixBinariesForDependencies` can be set to also install executables + for binaries defined by dependencies. This can be useful if these need to be + in `$PATH` for other tools, or if you're creating a workspace just to collect + command-line tools. + [niv]: https://github.com/nmattia/niv ## Hacking diff --git a/src/InstallBinCommand.ts b/src/InstallBinCommand.ts index 2762418..42f0b74 100644 --- a/src/InstallBinCommand.ts +++ b/src/InstallBinCommand.ts @@ -1,6 +1,7 @@ import { Command, Option } from "clipanion"; -import { Filename, npath, ppath, xfs } from "@yarnpkg/fslib"; +import { Filename, PortablePath, npath, ppath, xfs } from "@yarnpkg/fslib"; import { getPnpPath } from "@yarnpkg/plugin-pnp"; +import { scriptUtils } from "@yarnpkg/core"; import { CommandContext, @@ -34,53 +35,73 @@ export default class InstallBinCommand extends Command { const report = await StreamReport.start( { configuration, stdout: this.context.stdout }, async (report) => { - if (!workspace || workspace.manifest.bin.size === 0) { + if (!workspace) { return; } - let nodeLinker = configuration.get(`nodeLinker`); - if (!supportedLinkers.includes(nodeLinker)) { - nodeLinker = `node-modules`; - report.reportWarning( - 0, - `The nodeLinker ${nodeLinker} is not supported - executables may have trouble finding dependencies` + const binDir = npath.toPortablePath(this.binDir); + + for (const [name, binaryFile] of workspace.manifest.bin) { + const wrapperPath = ppath.join(binDir, name as Filename); + const binaryPath = ppath.join( + project.cwd, + npath.toPortablePath(binaryFile) ); + await this.writeWrapper(wrapperPath, binaryPath, { + configuration, + project, + }); } - const binDir = npath.toPortablePath(this.binDir); - const pnpPath = getPnpPath(project).cjs; - for (const [name, scriptInput] of workspace.manifest.bin) { - const binPath = ppath.join(binDir, name as Filename); - const scriptPath = ppath.join( - project.cwd, - npath.toPortablePath(scriptInput) + if (configuration.get(`installNixBinariesForDependencies`)) { + await project.resolveEverything({ report, lockfileOnly: true }); + const binaries = await scriptUtils.getPackageAccessibleBinaries( + project.topLevelWorkspace.anchoredLocator, + { project } ); - - let script; - switch (nodeLinker) { - case `pnp`: - script = renderTmpl(binWrapperPnpTmpl, { - NODE_PATH: process.execPath, - PNP_PATH: pnpPath, - SCRIPT_PATH: scriptPath, - }); - break; - case `node-modules`: - script = renderTmpl(binWrapperNodeModulesTmpl, { - NODE_PATH: process.execPath, - SCRIPT_PATH: scriptPath, - }); - break; - default: - throw Error(`Assertion failed: Invalid nodeLinker ${nodeLinker}`); + for (const [name, [_, binaryPath]] of binaries.entries()) { + const wrapperPath = ppath.join(binDir, name as Filename); + await this.writeWrapper( + wrapperPath, + npath.toPortablePath(binaryPath), + { configuration, project } + ); } - - await xfs.writeFilePromise(binPath, script); - await xfs.chmodPromise(binPath, 0o755); } } ); return report.exitCode(); } + + private async writeWrapper( + wrapperPath: PortablePath, + binaryPath: PortablePath, + { + configuration, + project, + }: { configuration: Configuration; project: Project } + ) { + let wrapper; + switch (configuration.get(`nodeLinker`)) { + case `pnp`: + wrapper = renderTmpl(binWrapperPnpTmpl, { + NODE_PATH: process.execPath, + PNP_PATH: getPnpPath(project).cjs, + BINARY_PATH: binaryPath, + }); + break; + case `node-modules`: + wrapper = renderTmpl(binWrapperNodeModulesTmpl, { + NODE_PATH: process.execPath, + BINARY_PATH: binaryPath, + }); + break; + default: + throw Error(`Assertion failed: Invalid nodeLinker`); + } + + await xfs.writeFilePromise(wrapperPath, wrapper); + await xfs.chmodPromise(wrapperPath, 0o755); + } } diff --git a/src/index.ts b/src/index.ts index 3761591..7d62ab1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -13,6 +13,7 @@ declare module "@yarnpkg/core" { generateDefaultNix: boolean; enableNixPreload: boolean; isolatedNixBuilds: string[]; + installNixBinariesForDependencies: boolean; } } @@ -55,6 +56,11 @@ const plugin: Plugin = { default: [], isArray: true, }, + installNixBinariesForDependencies: { + description: `If true, the Nix output 'bin' directory will also contain executables for binaries defined by dependencies`, + type: SettingsType.BOOLEAN, + default: false, + }, }, }; diff --git a/src/tmpl/bin-wrapper-node-modules.sh.in b/src/tmpl/bin-wrapper-node-modules.sh.in index 43fa91b..e68e43d 100644 --- a/src/tmpl/bin-wrapper-node-modules.sh.in +++ b/src/tmpl/bin-wrapper-node-modules.sh.in @@ -1,2 +1,2 @@ #!/bin/sh -exec '@@NODE_PATH@@' '@@SCRIPT_PATH@@' "$@" +exec '@@NODE_PATH@@' '@@BINARY_PATH@@' "$@" diff --git a/src/tmpl/bin-wrapper-pnp.sh.in b/src/tmpl/bin-wrapper-pnp.sh.in index a929f04..011be39 100644 --- a/src/tmpl/bin-wrapper-pnp.sh.in +++ b/src/tmpl/bin-wrapper-pnp.sh.in @@ -1,3 +1,3 @@ #!/bin/sh export NODE_OPTIONS="--require @@PNP_PATH@@" -exec '@@NODE_PATH@@' '@@SCRIPT_PATH@@' "$@" +exec '@@NODE_PATH@@' '@@BINARY_PATH@@' "$@"