From 286947912db137d28a1893c394458f41093b30a6 Mon Sep 17 00:00:00 2001 From: Max Howell Date: Mon, 3 Oct 2022 11:30:12 -0400 Subject: [PATCH] attempt to install as little as possible with apt (#173) --- .github/workflows/build.yml | 6 +- projects/darwinsys.com/file/package.yml | 25 +++++++++ projects/darwinsys.com/file/relocatable.diff | 55 +++++++++++++++++++ .../fix-elf.ts} | 54 +++++++++--------- scripts/{build => brewkit}/fix-machos.rb | 0 scripts/brewkit/fix-shebangs.ts | 1 - scripts/build/build.ts | 42 +++++++------- scripts/build/fix-pkg-config-files.ts | 1 + 8 files changed, 134 insertions(+), 50 deletions(-) create mode 100644 projects/darwinsys.com/file/package.yml create mode 100644 projects/darwinsys.com/file/relocatable.diff rename scripts/{build/fix-linux-rpaths.ts => brewkit/fix-elf.ts} (85%) mode change 100644 => 100755 rename scripts/{build => brewkit}/fix-machos.rb (100%) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ec862386..dbf12944 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -61,8 +61,8 @@ jobs: run: | case ${{ matrix.os }} in ubuntu-latest) - apt update - apt --yes install libc-dev libstdc++-8-dev libgcc-8-dev bzip2 xz-utils patchelf file + apt-get update + apt-get --yes install libc-dev libstdc++-8-dev libgcc-8-dev ;; macos-11) # screws up a lot of build scripts @@ -136,7 +136,7 @@ jobs: - run: | apt-get update - apt --yes install libc-dev libstdc++-8-dev libgcc-8-dev bzip2 xz-utils patchelf file + apt-get --yes install libc-dev libstdc++-8-dev libgcc-8-dev if: ${{ matrix.container != '' }} - uses: teaxyz/setup@v0 diff --git a/projects/darwinsys.com/file/package.yml b/projects/darwinsys.com/file/package.yml new file mode 100644 index 00000000..a8642d4c --- /dev/null +++ b/projects/darwinsys.com/file/package.yml @@ -0,0 +1,25 @@ +distributable: + url: https://astron.com/pub/file/file-{{version.raw}}.tar.gz + #TODO url: ftp://ftp.astron.com/pub/file/file-{{version.raw}}.tar.gz + strip-components: 1 + +versions: + - 5.43 + #TODO github: file/file/tags (has an awful format :/) + +dependencies: + zlib.net: 1 #FIXME this is actually an optional dep + +build: + dependencies: + tea.xyz/gx/cc: c99 + tea.xyz/gx/make: '*' + git-scm.org: 2 + script: | + git apply props/relocatable.diff + ./configure --prefix={{prefix}} + make --jobs {{hw.concurrency}} install + +test: | + file {{prefix}}/bin/file +#TODO check output, we don’t since it varies by platform annoyingly diff --git a/projects/darwinsys.com/file/relocatable.diff b/projects/darwinsys.com/file/relocatable.diff new file mode 100644 index 00000000..649c9d8c --- /dev/null +++ b/projects/darwinsys.com/file/relocatable.diff @@ -0,0 +1,55 @@ +diff --git a/src/file.h b/src/file.h +index 85675df..8d0f058 100644 +--- a/src/file.h ++++ b/src/file.h +@@ -95,9 +95,10 @@ + + #define ENABLE_CONDITIONALS + +-#ifndef MAGIC +-#define MAGIC "/etc/magic" +-#endif ++const char *tea_get_lib_dir(void); ++ ++#undef MAGIC ++#define MAGIC tea_get_lib_dir() + + #if defined(__EMX__) || defined (WIN32) + #define PATHSEP ';' +diff --git a/src/magic.c b/src/magic.c +index 7768497..84e8ba7 100644 +--- a/src/magic.c ++++ b/src/magic.c +@@ -257,7 +257,7 @@ magic_getpath(const char *magicfile, int action) + return magicfile; + + magicfile = getenv("MAGIC"); +- if (magicfile != NULL) ++ if (magicfile != NULL && magicfile[0] == '/') + return magicfile; + + return action == FILE_LOAD ? get_default_magic() : MAGIC; +@@ -678,3 +678,23 @@ magic_getparam(struct magic_set *ms, int param, void *val) + return -1; + } + } ++ ++ ++#include ++ ++/// returns the library dir of our relocatable installation ++protected const char *tea_get_lib_dir(void) { ++ char path[MAXPATHLEN + 1] = {0}; ++ uint32_t len = sizeof(path) - 1; ++#if __APPLE__ ++ int rv = _NSGetExecutablePath(path, &len); ++#else ++ int rv = readlink("/proc/self/exe", path, len - 1); ++#endif ++ if (rv != -1) { ++ sprintf(path, "%s/%s", dirname(dirname(path)), "share/misc/magic"); ++ return strdup(path) ?: "/etc/magic"; ++ } else { ++ return "/etc/magic"; ++ } ++} diff --git a/scripts/build/fix-linux-rpaths.ts b/scripts/brewkit/fix-elf.ts old mode 100644 new mode 100755 similarity index 85% rename from scripts/build/fix-linux-rpaths.ts rename to scripts/brewkit/fix-elf.ts index 6829e9b0..f7b48d4b --- a/scripts/build/fix-linux-rpaths.ts +++ b/scripts/brewkit/fix-elf.ts @@ -1,11 +1,32 @@ +#!/usr/bin/env -S tea -E + +/*--- +args: + - deno + - run + - --allow-run + - --allow-env + - --allow-read + - --allow-write={{tea.prefix}} + - --import-map={{ srcroot }}/import-map.json +dependencies: + nixos.org/patchelf: '*' + darwinsys.com/file: 5 +---*/ + import { useCellar } from "hooks" -import { PackageRequirement, Installation } from "types" -import { backticks, run, host } from "utils" +import { PackageRequirement, Installation, Package } from "types" +import { backticks, run, host, pkg as pkgutils } from "utils" import Path from "path" if (import.meta.main) { - console.log(await get_rpaths(new Path(Deno.args[0]))) + const cellar = useCellar() + const [installation, ...pkgs] = Deno.args + await fix_rpaths( + await cellar.resolve(new Path(installation)), + pkgs.map(pkgutils.parse) + ) } @@ -13,7 +34,7 @@ if (import.meta.main) { //NOTE solution is to have the rpath reference major version (or more specific if poss) /// fix rpaths or install names for executables and dynamic libraries -export default async function fix_rpaths(installation: Installation, pkgs: PackageRequirement[]) { +export default async function fix_rpaths(installation: Installation, pkgs: (Package | PackageRequirement)[]) { const skip_rpaths = [ "go.dev", // skipping because for some reason patchelf breaks the go binary resulting in the only output being: `Segmentation Fault` "tea.xyz", // this causes tea to pass -E/--version (and everything else?) directly to deno, making it _too_ much of a wrapper. @@ -33,7 +54,7 @@ export default async function fix_rpaths(installation: Installation, pkgs: Packa //NOTE we should have a `safety-inspector` step before bottling to check for this sort of thing // and then have virtual env manager be more specific via (DY)?LD_LIBRARY_PATH //FIXME somewhat inefficient for eg. git since git is mostly hardlinks to the same file -async function set_rpaths(exename: Path, pkgs: PackageRequirement[], installation: Installation) { +async function set_rpaths(exename: Path, pkgs: (Package | PackageRequirement)[], installation: Installation) { if (host().platform != 'linux') throw new Error() const cellar = useCellar() @@ -80,32 +101,11 @@ async function set_rpaths(exename: Path, pkgs: PackageRequirement[], installatio } } - async function prefix(pkg: PackageRequirement) { + async function prefix(pkg: Package | PackageRequirement) { return (await cellar.resolve(pkg)).path.join("lib").string } } -async function get_rpaths(exename: Path): Promise { - //GOOD_1ST_ISSUE better tokenizer for the output - - const lines = (await backticks({ - cmd: ["otool", "-l", exename] - })) - .trim() - .split("\n") - const it = lines.values() - const rv: string[] = [] - for (const line of it) { - if (line.trim().match(/^cmd\s+LC_RPATH$/)) { - it.next() - rv.push(it.next().value.trim().match(/^path\s+(.+)$/)[1]) - - console.debug(rv.at(-1)) - } - } - return rv -} - //FIXME pretty slow since we execute `file` for every file // eg. perl has hundreds of `.pm` files in its `lib` async function* exefiles(prefix: Path): AsyncGenerator<[Path, 'exe' | 'lib']> { diff --git a/scripts/build/fix-machos.rb b/scripts/brewkit/fix-machos.rb similarity index 100% rename from scripts/build/fix-machos.rb rename to scripts/brewkit/fix-machos.rb diff --git a/scripts/brewkit/fix-shebangs.ts b/scripts/brewkit/fix-shebangs.ts index c79d0f3e..725d532d 100755 --- a/scripts/brewkit/fix-shebangs.ts +++ b/scripts/brewkit/fix-shebangs.ts @@ -4,7 +4,6 @@ args: - deno - run - - --allow-net - --allow-run - --allow-env - --allow-read diff --git a/scripts/build/build.ts b/scripts/build/build.ts index 22ea9771..cc65255d 100644 --- a/scripts/build/build.ts +++ b/scripts/build/build.ts @@ -3,10 +3,9 @@ import { link, hydrate } from "prefab" import { Installation, Package } from "types" import useShellEnv, { expand } from "hooks/useShellEnv.ts" import { run, undent, host, tuplize } from "utils" +import { str as pkgstr } from "utils/pkg.ts" import fix_pkg_config_files from "./fix-pkg-config-files.ts" -import fix_linux_rpaths from "./fix-linux-rpaths.ts" import Path from "path" -import * as semver from "semver" const cellar = useCellar() const pantry = usePantry() @@ -109,14 +108,30 @@ async function __build(pkg: Package) { } async function fix_binaries(installation: Installation) { + const prefix = usePrefix().join("tea.xyz/var/pantry/scripts/brewkit") + const env = { + TEA_PREFIX: usePrefix().string, + } switch (host().platform) { case 'darwin': - await fix_macho(installation) - break - case 'linux': { - const self = {project: pkg.project, constraint: new semver.Range(pkg.version.toString())} - await fix_linux_rpaths(installation, [...deps.runtime, self]) - }} + return await run({ + cmd: [ + prefix.join('fix-machos.rb'), + installation.path, + ...['bin', 'lib', 'libexec'].map(x => installation.path.join(x)).filter(x => x.isDirectory()) + ], + env + }) + case 'linux': + return await run({ + cmd: [ + prefix.join('fix-elf.ts'), + installation.path, + ...[...deps.runtime, pkg].map(pkgstr) + ], + env + }) + } } } @@ -127,14 +142,3 @@ async function fetch_src(pkg: Package): Promise { await useSourceUnarchiver().unarchive({ dstdir, zipfile, stripComponents }) return dstdir } - -async function fix_macho(installation: Installation) { - const d = new Path(new URL(import.meta.url).pathname).parent() - const walk = ['bin', 'lib', 'libexec'].map(x => installation.path.join(x)).filter(x => x.isDirectory()) - await run({ - cmd: [d.join('fix-machos.rb'), installation.path, ...walk], - env: { - TEA_PREFIX: usePrefix().string, - } - }) -} diff --git a/scripts/build/fix-pkg-config-files.ts b/scripts/build/fix-pkg-config-files.ts index 4be45c1e..cea260db 100644 --- a/scripts/build/fix-pkg-config-files.ts +++ b/scripts/build/fix-pkg-config-files.ts @@ -1,5 +1,6 @@ import { Installation } from "types" import Path from "path" +import "utils" export default async function fix_pkg_config_files(installation: Installation) { for await (const pcfile of find_pkg_config_files(installation)) {