mirror of
https://github.com/ivabus/pantry
synced 2025-06-08 08:20:32 +03:00
fixes not incl runtime deps of build deps
Refactor build a bit. This was causing build failure for GHC on linux as the system perl didn't have something autoconf needed. TODO: infuser shouldn't have perl installed!
This commit is contained in:
parent
3607ae5a7a
commit
55eb7dc5e4
11 changed files with 161 additions and 184 deletions
|
@ -3,3 +3,12 @@
|
|||
```sh
|
||||
scripts/ls.ts | xargs scripts/sort.ts | xargs scripts/build.ts
|
||||
```
|
||||
|
||||
# test all
|
||||
|
||||
`each.ts` reduces output for each input to a concise ✅ or ❌ based on exit
|
||||
status.
|
||||
|
||||
```sh
|
||||
scripts/ls.ts | xargs scripts/each.ts scripts/test.ts
|
||||
```
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
#!/usr/bin/env -S tea -E
|
||||
|
||||
/*---
|
||||
args:
|
||||
- deno
|
||||
- run
|
||||
- --allow-net
|
||||
- --allow-run
|
||||
- --allow-read
|
||||
- --allow-write={{ tea.prefix }}
|
||||
- --allow-env=GITHUB_TOKEN,TEA_PREFIX
|
||||
- --import-map={{ srcroot }}/import-map.json
|
||||
---*/
|
||||
|
||||
import { bottle } from "./bottle.ts"
|
||||
import { ls } from "./ls.ts"
|
||||
import useCellar from "hooks/useCellar.ts"
|
||||
|
||||
const cellar = useCellar()
|
||||
|
||||
for await (const {project} of ls()) {
|
||||
for (const { path, pkg } of await cellar.ls(project)) {
|
||||
try {
|
||||
await bottle({ path, pkg })
|
||||
} catch (error) {
|
||||
console.error({ 'bottling-failure': pkg, error })
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,95 +0,0 @@
|
|||
import { Path, Package, PackageRequirement, semver } from "types"
|
||||
import usePantry from "hooks/usePantry.ts"
|
||||
import useCellar from "hooks/useCellar.ts"
|
||||
import useShellEnv, { expand } from "hooks/useShellEnv.ts"
|
||||
import { run, undent } from "utils"
|
||||
import fix_pkg_config_files from "./fix-pkg-config-files.ts"
|
||||
import fix_linux_rpaths from "./fix-linux-rpaths.ts"
|
||||
import usePlatform from "hooks/usePlatform.ts"
|
||||
|
||||
interface Options {
|
||||
pkg: Package
|
||||
prebuild: () => Promise<void>
|
||||
deps: {
|
||||
// only include direct build-time dependencies
|
||||
build: PackageRequirement[]
|
||||
|
||||
// include transitive dependencies
|
||||
runtime: PackageRequirement[]
|
||||
}
|
||||
/// additional env to set, will override (REPLACE) any calculated env
|
||||
env?: Record<string, string[]>
|
||||
}
|
||||
|
||||
export default async function build({ pkg, deps, prebuild, env: add_env }: Options): Promise<Path> {
|
||||
const pantry = usePantry()
|
||||
const cellar = useCellar()
|
||||
const dst = cellar.mkpath(pkg)
|
||||
const src = dst.join("src")
|
||||
const env = await useShellEnv([...deps.runtime, ...deps.build])
|
||||
const sh = await pantry.getScript(pkg, 'build')
|
||||
|
||||
if (cellar.prefix.string != "/opt") {
|
||||
console.error({ TEA_PREFIX: cellar.prefix.string })
|
||||
throw new Error("builds go to /opt (try TEA_PREFIX=/opt)")
|
||||
}
|
||||
|
||||
if (env.pending.length) {
|
||||
console.error({uninstalled: env.pending})
|
||||
throw new Error("uninstalled")
|
||||
}
|
||||
|
||||
await prebuild()
|
||||
|
||||
if (add_env) {
|
||||
for (const [key, value] of Object.entries(add_env)) {
|
||||
env.vars[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
const cmd = dst.join("build.sh").write({ force: true, text: undent`
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
set -o pipefail
|
||||
set -x
|
||||
cd "${src}"
|
||||
|
||||
${expand(env.vars)}
|
||||
|
||||
${/*FIXME hardcoded paths*/ ''}
|
||||
export PATH=/opt/tea.xyz/var/pantry/scripts/brewkit:"$PATH"
|
||||
|
||||
${sh}
|
||||
`
|
||||
}).chmod(0o500)
|
||||
|
||||
await run({ cmd })
|
||||
|
||||
const installation = { pkg, path: dst }
|
||||
const self = {project: pkg.project, constraint: new semver.Range(`=${pkg.version}`)}
|
||||
|
||||
//// fix rpaths, etc.
|
||||
switch (usePlatform().platform) {
|
||||
case 'darwin': {
|
||||
const d = new Path(new URL(import.meta.url).pathname).parent()
|
||||
const walk = ['bin', 'lib', 'libexec'].map(x=>dst.join(x)).filter(x => x.isDirectory())
|
||||
await run({
|
||||
cmd: [d.join('fix-machos.rb'), dst, ...walk],
|
||||
env: {
|
||||
TEA_PREFIX: cellar.prefix.string,
|
||||
}
|
||||
})
|
||||
} break
|
||||
case 'linux':
|
||||
await fix_linux_rpaths(installation, [...deps.runtime, self])
|
||||
break
|
||||
default:
|
||||
throw new Error("unsupported platform")
|
||||
}
|
||||
|
||||
//// fix rest of stuff
|
||||
await fix_pkg_config_files({ path: dst, pkg })
|
||||
|
||||
return dst
|
||||
}
|
|
@ -12,31 +12,30 @@ args:
|
|||
- --import-map={{ srcroot }}/import-map.json
|
||||
---*/
|
||||
|
||||
import useSourceUnarchiver from "hooks/useSourceUnarchiver.ts"
|
||||
import useCellar from "hooks/useCellar.ts"
|
||||
import usePantry from "hooks/usePantry.ts"
|
||||
import useCache from "hooks/useCache.ts"
|
||||
import { lvl1 as link } from "prefab/link.ts"
|
||||
import build from "./build.prefab.ts"
|
||||
import build from "./build/build.ts"
|
||||
import { Package, parsePackageRequirement, semver } from "types"
|
||||
import useFlags from "hooks/useFlags.ts"
|
||||
import usePlatform from "hooks/usePlatform.ts"
|
||||
import hydrate from "prefab/hydrate.ts"
|
||||
import useCellar from "hooks/useCellar.ts"
|
||||
|
||||
useFlags()
|
||||
|
||||
const pantry = usePantry()
|
||||
const cellar = useCellar()
|
||||
|
||||
const dry = Deno.args.map(parsePackageRequirement)
|
||||
const gha = !!Deno.env.get("GITHUB_ACTIONS")
|
||||
const group_it = gha && dry.length > 1
|
||||
|
||||
const rv: Package[] = []
|
||||
|
||||
if (cellar.prefix.string != "/opt") {
|
||||
console.error({ TEA_PREFIX: cellar.prefix.string })
|
||||
throw new Error("builds must be performed in /opt (try TEA_PREFIX=/opt)")
|
||||
}
|
||||
|
||||
for (const pkgrq of dry) {
|
||||
const versions = await pantry.getVersions(pkgrq)
|
||||
const version = semver.maxSatisfying(versions, pkgrq.constraint)
|
||||
if (!version) throw "no-version-found"
|
||||
if (!version) throw new Error(`no-version-found: ${pkgrq.project}`)
|
||||
const pkg = { project: pkgrq.project, version }
|
||||
|
||||
if (group_it) {
|
||||
|
@ -45,48 +44,7 @@ for (const pkgrq of dry) {
|
|||
console.log({ building: pkgrq.project })
|
||||
}
|
||||
|
||||
// Get the source
|
||||
const prebuild = async () => {
|
||||
const dstdir = cellar.mkpath(pkg).join("src")
|
||||
const { url, stripComponents } = await pantry.getDistributable(pkg)
|
||||
const { download } = useCache()
|
||||
const zip = await download({ pkg, url, type: 'src' })
|
||||
await useSourceUnarchiver().unarchive({
|
||||
dstdir,
|
||||
zipfile: zip,
|
||||
stripComponents
|
||||
})
|
||||
}
|
||||
|
||||
//TODO this was already calculated in `sort` and should not be recalculated
|
||||
const deps = await pantry.getDeps(pkg)
|
||||
const wet = await hydrate(deps.runtime, pkg => pantry.getDeps(pkg).then(x => x.runtime))
|
||||
deps.runtime.push(...wet.pkgs)
|
||||
|
||||
// only required as we aren't passing everything into hydrate
|
||||
const depends_on_self = () => deps.build.some(x => x.project === pkg.project)
|
||||
|
||||
// provided this package doesn't transitively depend on itself (yes this happens)
|
||||
// clean out the destination prefix first
|
||||
if (!wet.bootstrap_required.has(pkg.project) && !depends_on_self()) {
|
||||
const installation = await cellar.isInstalled(pkg)
|
||||
if (installation) {
|
||||
console.log({ cleaning: installation.path })
|
||||
for await (const [path, {name}] of installation.path.ls()) {
|
||||
if (name == 'src') continue
|
||||
path.rm({ recursive: true })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const env = usePlatform().platform == 'darwin'
|
||||
? {MACOSX_DEPLOYMENT_TARGET: ['11.0']}
|
||||
: undefined
|
||||
|
||||
// Build and link
|
||||
const path = await build({ pkg, deps, prebuild, env })
|
||||
await link({ path, pkg })
|
||||
|
||||
await build(pkg)
|
||||
rv.push(pkg)
|
||||
|
||||
if (group_it) {
|
||||
|
|
135
scripts/build/build.ts
Normal file
135
scripts/build/build.ts
Normal file
|
@ -0,0 +1,135 @@
|
|||
import useSourceUnarchiver from "hooks/useSourceUnarchiver.ts"
|
||||
import useCellar from "hooks/useCellar.ts"
|
||||
import usePantry from "hooks/usePantry.ts"
|
||||
import useCache from "hooks/useCache.ts"
|
||||
import { lvl1 as link } from "prefab/link.ts"
|
||||
import { Installation, Package, Path, semver } from "types"
|
||||
import usePlatform from "hooks/usePlatform.ts"
|
||||
import hydrate from "prefab/hydrate.ts"
|
||||
import useShellEnv, { expand } from "hooks/useShellEnv.ts"
|
||||
import { run, undent } from "utils"
|
||||
import fix_pkg_config_files from "./fix-pkg-config-files.ts"
|
||||
import fix_linux_rpaths from "./fix-linux-rpaths.ts"
|
||||
|
||||
const cellar = useCellar()
|
||||
const pantry = usePantry()
|
||||
const { platform } = usePlatform()
|
||||
|
||||
export default async function _build(pkg: Package) {
|
||||
const [deps, wet] = await calc_deps()
|
||||
await clean()
|
||||
const env = await mkenv()
|
||||
const dst = cellar.mkpath(pkg)
|
||||
const src = await fetch_src(pkg)
|
||||
const installation = await build()
|
||||
await link(installation)
|
||||
await fix_binaries(installation)
|
||||
await fix_pkg_config_files(installation)
|
||||
|
||||
return installation
|
||||
|
||||
//////// utils
|
||||
async function calc_deps() {
|
||||
const deps = await pantry.getDeps(pkg)
|
||||
const wet = await hydrate([...deps.runtime, ...deps.build], pkg => pantry.getDeps(pkg).then(x => x.runtime))
|
||||
deps.runtime.push(...wet.pkgs)
|
||||
function tuplize<T extends any[]>(...elements: T) { return elements }
|
||||
return tuplize(deps, wet)
|
||||
}
|
||||
|
||||
async function clean() {
|
||||
const installation = await should_clean()
|
||||
if (!installation) return
|
||||
console.log({ cleaning: installation.path })
|
||||
for await (const [path, {name}] of installation.path.ls()) {
|
||||
if (name == 'src') continue
|
||||
path.rm({ recursive: true })
|
||||
}
|
||||
|
||||
async function should_clean() {
|
||||
// only required as we aren't passing everything into hydrate
|
||||
const depends_on_self = () => deps.build.some(x => x.project === pkg.project)
|
||||
|
||||
// provided this package doesn't transitively depend on itself (yes this happens)
|
||||
// clean out the destination prefix first
|
||||
if (!wet.bootstrap_required.has(pkg.project) && !depends_on_self()) {
|
||||
return await cellar.isInstalled(pkg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function mkenv() {
|
||||
const env = await useShellEnv([...deps.runtime, ...deps.build])
|
||||
|
||||
if (env.pending.length) {
|
||||
console.error({uninstalled: env.pending})
|
||||
throw new Error("uninstalled")
|
||||
}
|
||||
|
||||
if (platform == 'darwin') {
|
||||
env.vars['MACOSX_DEPLOYMENT_TARGET'] = ['11.0']
|
||||
}
|
||||
|
||||
return env
|
||||
}
|
||||
|
||||
async function build() {
|
||||
const sh = await pantry.getScript(pkg, 'build')
|
||||
|
||||
const cmd = dst.join("build.sh").write({ force: true, text: undent`
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
set -o pipefail
|
||||
set -x
|
||||
cd "${src}"
|
||||
|
||||
${expand(env.vars)}
|
||||
|
||||
${/*FIXME hardcoded paths*/ ''}
|
||||
export PATH=/opt/tea.xyz/var/pantry/scripts/brewkit:"$PATH"
|
||||
|
||||
${sh}
|
||||
`
|
||||
}).chmod(0o500)
|
||||
|
||||
await run({ cmd }) // THE BUILD
|
||||
|
||||
return { path: dst, pkg }
|
||||
}
|
||||
|
||||
async function fix_binaries(installation: Installation) {
|
||||
switch (usePlatform().platform) {
|
||||
case 'darwin':
|
||||
await fix_macho(installation)
|
||||
break
|
||||
case 'linux': {
|
||||
const self = {project: pkg.project, constraint: new semver.Range(`=${pkg.version}`)}
|
||||
await fix_linux_rpaths(installation, [...deps.runtime, self])
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
async function fetch_src(pkg: Package): Promise<Path> {
|
||||
const dstdir = cellar.mkpath(pkg).join("src")
|
||||
const { url, stripComponents } = await pantry.getDistributable(pkg)
|
||||
const { download } = useCache()
|
||||
const zip = await download({ pkg, url, type: 'src' })
|
||||
await useSourceUnarchiver().unarchive({
|
||||
dstdir,
|
||||
zipfile: zip,
|
||||
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: cellar.prefix.string,
|
||||
}
|
||||
})
|
||||
}
|
0
scripts/clean-all.sh
Executable file → Normal file
0
scripts/clean-all.sh
Executable file → Normal file
|
@ -10,19 +10,18 @@ args:
|
|||
- --import-map={{ srcroot }}/import-map.json
|
||||
---*/
|
||||
|
||||
import { Path } from "types"
|
||||
import { ls } from "./ls.ts"
|
||||
const args = [...Deno.args]
|
||||
const via = args.shift()
|
||||
|
||||
const cwd = new Path(new URL(import.meta.url).pathname).parent().string
|
||||
|
||||
for await (const { project } of ls()) {
|
||||
for (const arg of args) {
|
||||
const proc = Deno.run({
|
||||
stdout: "null", stderr: "null",
|
||||
cmd: ["./test.ts", project],
|
||||
cwd
|
||||
cmd: [via!, arg]
|
||||
})
|
||||
const status = await proc.status()
|
||||
if (status.code !== 0) {
|
||||
console.error(`test failed: ${project}`)
|
||||
console.error(`${arg} ❌`)
|
||||
} else {
|
||||
console.info(`${arg} ✅`)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue