mirror of
https://github.com/ivabus/pantry
synced 2024-11-14 12:35:10 +03:00
140 lines
4.3 KiB
TypeScript
140 lines
4.3 KiB
TypeScript
import { useSourceUnarchiver, useCellar, usePantry, useCache, usePrefix } from "hooks"
|
||
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 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()
|
||
const { platform } = host()
|
||
|
||
export default async function _build(pkg: Package) {
|
||
try {
|
||
return await __build(pkg)
|
||
} catch (e) {
|
||
cellar.keg(pkg).isDirectory()?.isEmpty()?.rm() // don’t leave empty kegs around
|
||
throw e
|
||
}
|
||
}
|
||
|
||
async function __build(pkg: Package) {
|
||
const [deps, wet, resolved] = await calc_deps()
|
||
await clean()
|
||
const env = await mkenv()
|
||
const dst = cellar.keg(pkg).mkpath()
|
||
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)
|
||
const resolved = await Promise.all(wet.pkgs.map(pkg => cellar.resolve(pkg)))
|
||
return tuplize(deps, wet, resolved)
|
||
}
|
||
|
||
async function clean() {
|
||
const installation = await should_clean()
|
||
if (installation) {
|
||
console.log({ cleaning: installation.path })
|
||
for await (const [path] of installation.path.ls()) {
|
||
// we delete contents rather than the directory itself to prevent broken vx.y symlinks
|
||
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)
|
||
const wet_dep = () => wet.pkgs.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() && !wet_dep()) {
|
||
return await cellar.has(pkg)
|
||
}
|
||
}
|
||
}
|
||
|
||
function mkenv() {
|
||
const env = useShellEnv(resolved)
|
||
|
||
if (platform == 'darwin') {
|
||
env.vars['MACOSX_DEPLOYMENT_TARGET'] = ['11.0']
|
||
}
|
||
|
||
return env
|
||
}
|
||
|
||
async function build() {
|
||
const sh = await pantry.getScript(pkg, 'build', resolved)
|
||
|
||
const cmd = src.parent().join("build.sh").write({ force: true, text: undent`
|
||
#!/bin/bash
|
||
|
||
set -e
|
||
set -o pipefail
|
||
set -x
|
||
cd "${src}"
|
||
|
||
export SRCROOT="${src}"
|
||
${expand(env.vars)}
|
||
|
||
${/*FIXME hardcoded paths*/ ''}
|
||
export PATH=/opt/tea.xyz/var/pantry/scripts/brewkit:"$PATH"
|
||
|
||
${sh}
|
||
`
|
||
}).chmod(0o500)
|
||
|
||
// copy in auxillary files from pantry directory
|
||
for await (const [path, {isFile}] of pantry.getYAML(pkg).path.parent().ls()) {
|
||
if (isFile) {
|
||
path.cp({ into: src.join("props").mkdir() })
|
||
}
|
||
}
|
||
|
||
await run({ cmd }) // THE BUILD
|
||
|
||
return { path: dst, pkg }
|
||
}
|
||
|
||
async function fix_binaries(installation: Installation) {
|
||
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])
|
||
}}
|
||
}
|
||
}
|
||
|
||
async function fetch_src(pkg: Package): Promise<Path> {
|
||
const dstdir = usePrefix().join(pkg.project, "src", `v${pkg.version}`)
|
||
const { url, stripComponents } = await pantry.getDistributable(pkg)
|
||
const zipfile = await useCache().download({ pkg, url, type: 'src' })
|
||
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,
|
||
}
|
||
})
|
||
}
|