diff --git a/scripts/build-all.ts b/scripts/build-all.ts index 1c7404ef..d7ad09e2 100755 --- a/scripts/build-all.ts +++ b/scripts/build-all.ts @@ -13,7 +13,6 @@ args: ---*/ import usePantry from "hooks/usePantry.ts" -import hydrate from "prefab/hydrate.ts" import build from "prefab/build.ts" import doInstall from "prefab/install.ts" import { lvl1 as link } from "prefab/link.ts" @@ -95,16 +94,15 @@ async function buildIfNeeded({ project, install }: BuildOptions) { console.verbose({ building: project }) } - const deps = await pantry.getDeps({ pkg, wbuild: true }) - const graph = await hydrate(deps) + const deps = await pantry.getDeps(pkg) - for (const dep of graph) { + for (const dep of [...deps.build, ...deps.runtime]) { await buildIfNeeded({ project: dep, install: true }) } await prepare(pkg) try { - const path = await build({ pkg, deps: graph }) + const path = await build({ pkg, deps }) await link({ path, pkg }) results.built.push(`${pkg.project}-${pkg.version.version}`) } catch (error) { diff --git a/scripts/build.ts b/scripts/build.ts index 660e694a..bda7138e 100755 --- a/scripts/build.ts +++ b/scripts/build.ts @@ -12,7 +12,6 @@ args: - --import-map={{ srcroot }}/import-map.json ---*/ -import hydrate from "prefab/hydrate.ts" import build from "prefab/build.ts" import { lvl1 as link } from "prefab/link.ts" import usePantry from "hooks/usePantry.ts" @@ -39,7 +38,7 @@ for (const req of args[0].map(parsePackageRequirement)) { if (!version) throw "no-version-found" const pkg = { project: req.project, version } console.debug(pkg) - const deps = await pantry.getDeps({ pkg, wbuild: true }) + const deps = await pantry.getDeps(pkg) console.debug({ deps }) diff --git a/src/hooks/usePantry.ts b/src/hooks/usePantry.ts index b8775eea..390b6429 100644 --- a/src/hooks/usePantry.ts +++ b/src/hooks/usePantry.ts @@ -5,16 +5,11 @@ import useCellar from "hooks/useCellar.ts" import usePlatform from "hooks/usePlatform.ts" -interface GetDepsOptions { - pkg: Package | PackageRequirement - wbuild?: boolean -} - interface Response { getDistributable(rq: Package): Promise<{ url: string, stripComponents?: number }> /// returns sorted versions getVersions(rq: PackageRequirement | Package): Promise - getDeps(opts: GetDepsOptions): Promise + getDeps(pkg: Package | PackageRequirement): Promise<{ runtime: PackageRequirement[], build: PackageRequirement[] }> getBuildScript(pkg: Package): Promise update(): Promise getProvides(rq: PackageRequirement | Package): Promise @@ -74,9 +69,12 @@ export default function usePantry(): Response { } } - const getDeps = async ({pkg, wbuild}: GetDepsOptions) => { + const getDeps = async (pkg: Package | PackageRequirement) => { const yml = await entry(pkg).yml() - return go(yml.dependencies).concat(go(wbuild && yml.build?.dependencies)) + return { + runtime: go(yml.dependencies), + build: go(yml.build?.dependencies) + } // deno-lint-ignore no-explicit-any function go(node: any) { if (!node) return [] diff --git a/src/prefab/build.ts b/src/prefab/build.ts index ce3e1636..c338571e 100644 --- a/src/prefab/build.ts +++ b/src/prefab/build.ts @@ -1,4 +1,4 @@ -import { Path, Package, PackageRequirement } from "types" +import { Path, Package, PackageRequirement, semver } from "types" import usePantry from "hooks/usePantry.ts" import useCellar from "hooks/useCellar.ts" import useShellEnv from "hooks/useShellEnv.ts" @@ -7,14 +7,17 @@ import usePlatform from "hooks/usePlatform.ts"; interface Options { pkg: Package - deps: PackageRequirement[] + deps: { + build: PackageRequirement[] + runtime: PackageRequirement[] + } } export default async function build({ pkg, deps }: Options): Promise { const pantry = usePantry() const dst = useCellar().mkpath(pkg) const src = dst.join("src") - const env = await useShellEnv(deps) + const env = await useShellEnv([...deps.build, ...deps.runtime]) const sh = await pantry.getBuildScript(pkg) const { platform } = usePlatform() @@ -40,6 +43,10 @@ export default async function build({ pkg, deps }: Options): Promise { }).chmod(0o500) await run({ cmd }) + await fix(dst, [ + ...deps.runtime, + {project: pkg.project, constraint: new semver.Range(`=${pkg.version}`)} + ] ) return dst } @@ -52,3 +59,42 @@ function expand(env: Record) { } return rv } + +async function* exefiles(prefix: Path) { + for (const basename of ["bin", "lib"]) { //TODO the rest + const d = prefix.join(basename).isDirectory() + if (!d) continue + for await (const [exename] of d.ls()) { + if (exename.isExecutableFile()) yield exename + } + } +} + +/// fix rpaths or install names for executables and dynamic libraries +async function fix(prefix: Path, pkgs: PackageRequirement[]) { + if (usePlatform().platform != 'linux') return + // ^^ TODO we need to do this on mac too + + for await (const exename of exefiles(prefix)) { + await setRpath(exename, pkgs) + } +} + +//TODO this is not resilient to upgrades (obv) +async function setRpath(exename: Path, pkgs: PackageRequirement[]) { + const cellar = useCellar() + const rpath = (await Promise.all(pkgs.map(pkg => prefix(pkg)))).join(":") + + try { + await run({ + cmd: ["patchelf", "--set-rpath", rpath, exename] + }) + } catch (e) { + console.warn(e) + //FIXME we skip all errors as we are not checking if files are executables rather than eg. scripts + } + + async function prefix(pkg: PackageRequirement) { + return (await cellar.resolve(pkg)).path.join("lib").string + } +} diff --git a/src/prefab/hydrate.ts b/src/prefab/hydrate.ts index 0ebdadcb..0d0722a8 100644 --- a/src/prefab/hydrate.ts +++ b/src/prefab/hydrate.ts @@ -12,7 +12,7 @@ export default async function hydrate(reqs: PackageRequirement[]): Promise 0) { const curr = stack.shift()! - for (const dep of await pantry.getDeps({ pkg: curr })) { + for (const dep of (await pantry.getDeps(curr)).runtime) { if (dep.project in set) { const intersection = semver_intersection(set[dep.project]!, dep.constraint) // ^^ throws if no intersection possible