live-bootstrap/steps/helpers.sh
fosslinux a67db8fcbd Make patches relative to where tarballs are extracted
Ever since an old patch version, it has (for reasonable security
reasons) not supported patched with ../ in the filename.
Many of our patches have been relying on this behaviour being OK,
because we start off with an ancient patch version that didn't perform
such checks. As soon as we need this behaviour after we build a newer
patch though, we will have problems.

So, let's change the policy.
Patches are relative to where tarballs are extracted, rather than the
"working directory" - e.g. have patches for `coreutils-9.4/src/cp.c`
instead of `src/cp.c`.
Keeping this consistent has a few implications;
- patches are applied from the build/ directory in bash era now, with
  `-p0`
- when patches are manually applied in the bash era, use `-p` as
  required, usually `-p1`
- in kaem era where patches are always manually applied, `-p1` is used
2024-12-23 15:20:42 +11:00

538 lines
16 KiB
Bash
Executable file

#!/bin/bash -e
# SPDX-FileCopyrightText: 2021 Andrius Štikonas <andrius@stikonas.eu>
# SPDX-FileCopyrightText: 2021-22 fosslinux <fosslinux@aussies.space>
# SPDX-FileCopyrightText: 2021 Paul Dersey <pdersey@gmail.com>
# SPDX-FileCopyrightText: 2021 Melg Eight <public.melg8@gmail.com>
#
# SPDX-License-Identifier: GPL-3.0-or-later
# Set constant umask
umask 022
# Get a list of files
get_files() {
echo "."
_get_files "${1}"
}
_get_files() {
local prefix
prefix="${1}"
fs=
if [ -n "$(ls 2>/dev/null)" ]; then
# This can be removed once Debian 12 is stable
# shellcheck disable=SC2035
fs=$(echo *)
fi
if [ -n "$(ls .[0-z]* 2>/dev/null)" ]; then
fs="${fs} $(echo .[0-z]*)"
fi
for f in ${fs}; do
# Archive symlinks to directories as symlinks
echo "${prefix}/${f}"
if [ -d "${f}" ] && ! [ -h "${f}" ]; then
cd "${f}"
_get_files "${prefix}/${f}"
cd ..
fi
done
}
# Reset all timestamps to unix time 0
reset_timestamp() {
if command -v find >/dev/null 2>&1; then
# find does not error out on exec error
find . -print0 | xargs -0 touch -h -t 197001010000.00
else
# A rudimentary find implementation that does the trick
fs=
if [ -n "$(ls 2>/dev/null)" ]; then
fs=$(echo ./*)
fi
if [ -n "$(ls .[0-z]* 2>/dev/null)" ]; then
fs="${fs} $(echo .[0-z]*)"
fi
for f in ${fs}; do
touch -h -t 197001010000.00 "${f}"
if [ -d "${f}" ]; then
cd "${f}"
reset_timestamp
cd ..
fi
done
fi
}
# Fake grep
_grep() {
local text="${1}"
local fname="${2}"
if command -v grep >/dev/null 2>&1; then
grep "${text}" "${fname}"
else
# shellcheck disable=SC2162
while read line; do
case "${line}" in *"${text}"*)
echo "${line}" ;;
esac
done < "${fname}"
fi
}
get_revision() {
local pkg=$1
local oldpwd="${PWD}"
cd "/external/repo"
# Get revision (n time this package has been built)
revision=$( (ls -1 "${pkg}"* 2>/dev/null || true) | wc -l | sed 's/ *//g')
cd "${oldpwd}"
}
# Installs binary packages from an earlier run
# This is useful to speed up development cycle
bin_preseed() {
if [ -d "/external/repo-preseeded" ]; then
get_revision "${pkg}"
cd "/external/repo-preseeded"
test -e "${pkg}_${revision}.tar.bz2" || return 1
if [ "${UPDATE_CHECKSUMS}" = "True" ] || src_checksum "${pkg}" $((revision)); then
echo "${pkg}: installing prebuilt package."
mv "${pkg}_${revision}"* /external/repo || return 1
cd "/external/repo"
rm -f /tmp/filelist.txt
src_apply "${pkg}" $((revision))
cd "${SRCDIR}"
return 0
fi
fi
return 1
}
# Removes either an existing package or file
uninstall() {
local in_fs in_pkg symlinks
while [ $# -gt 0 ]; do
removing="$1"
case "${removing}" in
/*)
# Removing a file
echo "removing file: ${removing}."
rm -f "${removing}"
;;
*)
echo "${removing}: uninstalling."
local oldpwd="${PWD}"
mkdir -p "/tmp/removing"
cd "/tmp/removing"
get_revision "${removing}"
local filename="/external/repo/${removing}_$((revision-1)).tar.bz2"
# Initial bzip2 built against meslibc has broken pipes
bzip2 -dc "${filename}" | tar -xf -
# reverse to have files before directories
if command -v find >/dev/null 2>&1; then
find . | sort -r > ../filelist
else
get_files . | tac > ../filelist
fi
# shellcheck disable=SC2162
while read file; do
if [ -d "${file}" ]; then
if [ -z "$(ls -A "/${file}")" ]; then
rmdir "/${file}"
fi
else
# in some cases we might be uninstalling a file that has already been overwritten
# in this case we don't want to remove it
in_fs="$(sha256sum "${file}" 2>/dev/null | cut -d' ' -f1)"
in_pkg="$(sha256sum "/${file}" 2>/dev/null | cut -d' ' -f1)"
if [ "${in_fs}" = "${in_pkg}" ]; then
rm -f "/${file}"
fi
if [ -h "${file}" ]; then
symlinks="${symlinks} ${file}"
fi
fi
done < ../filelist
rm -f ../filelist
for link in ${symlinks}; do
if [ ! -e "/${link}" ]; then
rm -f "/${link}"
fi
done
cd "${oldpwd}"
rm -rf "/tmp/removing"
;;
esac
shift
done
}
# Common build steps
# Build function provides a few common stages with default implementation
# that can be overridden on per package basis in the build script.
# build takes two arguments:
# 1) name-version of the package
# 2) optionally specify build script. Default is pass$((revision+1)).sh
# 3) optionally specify directory to cd into
build() {
pkg=$1
get_revision "${pkg}"
script_name=${2:-pass$((revision+1)).sh}
dirname=${3:-${pkg}}
# shellcheck disable=SC2015
bin_preseed && return || true # Normal build if preseed fails
cd "${SRCDIR}/${pkg}" || (echo "Cannot cd into ${pkg}!"; kill $$)
echo "${pkg}: beginning build using script ${script_name}"
base_dir="${PWD}"
if [ -e "${base_dir}/patches-$(basename "${script_name}" .sh)" ]; then
patch_dir="${base_dir}/patches-$(basename "${script_name}" .sh)"
else
patch_dir="${base_dir}/patches"
fi
mk_dir="${base_dir}/mk"
files_dir="${base_dir}/files"
rm -rf "build"
mkdir "build"
cd "build"
build_script="${base_dir}/${script_name}"
if test -e "${build_script}"; then
# shellcheck source=/dev/null
. "${build_script}"
fi
echo "${pkg}: getting sources."
build_stage=src_get
call $build_stage
echo "${pkg}: unpacking source."
build_stage=src_unpack
call $build_stage
unset EXTRA_DISTFILES
cd "${dirname}" || (echo "Cannot cd into build/${dirname}!"; kill $$)
echo "${pkg}: preparing source."
build_stage=src_prepare
call $build_stage
echo "${pkg}: configuring source."
build_stage=src_configure
call $build_stage
echo "${pkg}: compiling source."
build_stage=src_compile
call $build_stage
echo "${pkg}: install to fakeroot."
mkdir -p "${DESTDIR}"
build_stage=src_install
call $build_stage
echo "${pkg}: postprocess binaries."
build_stage=src_postprocess
call $build_stage
echo "${pkg}: creating package."
cd "${DESTDIR}"
src_pkg
src_checksum "${pkg}" "${revision}"
echo "${pkg}: cleaning up."
rm -rf "${SRCDIR}/${pkg}/build"
rm -rf "${DESTDIR}"
echo "${pkg}: installing package."
src_apply "${pkg}" "${revision}"
echo "${pkg}: build successful"
cd "${SRCDIR}"
unset -f src_get src_unpack src_prepare src_configure src_compile src_install src_postprocess
unset extract
}
download_source_line() {
url="${1}"
checksum="${2}"
fname="${3}"
# Default to basename of url if not given
fname="${fname:-$(basename "${url}")}"
if ! [ -e "${fname}" ]; then
curl --fail --retry 5 --location "${url}" --output "${fname}" || true
fi
}
check_source_line() {
url="${1}"
checksum="${2}"
fname="${3}"
# Default to basename of url if not given
fname="${fname:-$(basename "${url}")}"
if ! [ -e "${fname}" ]; then
false
fi
echo "${checksum} ${fname}" > "${fname}.sum"
sha256sum -c "${fname}.sum"
rm "${fname}.sum"
}
# Default get function that downloads source tarballs.
default_src_get() {
# shellcheck disable=SC2153
cd "${DISTFILES}"
# shellcheck disable=SC2162
while read line; do
# This is intentional - we want to split out ${line} into separate arguments.
# shellcheck disable=SC2086
download_source_line ${line}
done < "${base_dir}/sources"
# shellcheck disable=SC2162
while read line; do
# This is intentional - we want to split out ${line} into separate arguments.
# shellcheck disable=SC2086
check_source_line ${line}
done < "${base_dir}/sources"
cd -
}
# Intelligently extracts a file based upon its filetype.
extract_file() {
f="${3:-$(basename "${1}")}"
# shellcheck disable=SC2154
case "${noextract}" in
*${f}*)
cp "${DISTFILES}/${f}" .
;;
*)
case "${f}" in
*.tar* | *.tgz)
# shellcheck disable=SC2153
if test -e "${PREFIX}/libexec/rmt"; then
# Again, we want to split out into words.
# shellcheck disable=SC2086
tar --no-same-owner -xf "${DISTFILES}/${f}" ${extract}
else
# shellcheck disable=SC2086
case "${f}" in
*.tar.gz) tar -xzf "${DISTFILES}/${f}" ${extract} ;;
*.tar.bz2)
# Initial bzip2 built against meslibc has broken pipes
bzip2 -dc "${DISTFILES}/${f}" | tar -xf - ${extract} ;;
*.tar.xz | *.tar.lzma)
if test -e "${PREFIX}/bin/xz"; then
tar -xf "${DISTFILES}/${f}" --use-compress-program=xz ${extract}
else
unxz --file "${DISTFILES}/${f}" | tar -xf - ${extract}
fi
;;
esac
fi
;;
*)
cp "${DISTFILES}/${f}" .
;;
esac
;;
esac
}
# Default unpacking function that unpacks all sources.
default_src_unpack() {
# Handle the first one differently
first_line=$(head -n 1 ../sources)
# Again, we want to split out into words.
# shellcheck disable=SC2086
extract_file ${first_line}
# This assumes there is only one directory in the tarball
# Get the dirname "smartly"
if ! [ -e "${dirname}" ]; then
for i in *; do
if [ -d "${i}" ]; then
dirname="${i}"
break
fi
done
fi
# shellcheck disable=SC2162
tail -n +2 ../sources | while read line; do
# shellcheck disable=SC2086
extract_file ${line}
done
}
# Default function to prepare source code.
# It applies all patches from patch_dir (at the moment only -p0 patches are supported).
# Patches are applied from the parent directory.
# Then it copies our custom makefile and any other custom files from files directory.
default_src_prepare() {
if test -d "${patch_dir}"; then
if ls "${patch_dir}"/*.patch >/dev/null 2>&1; then
for p in "${patch_dir}"/*.patch; do
echo "Applying patch: ${p}"
patch -d.. -Np0 < "${p}"
done
fi
fi
makefile="${mk_dir}/main.mk"
if test -e "${makefile}"; then
cp "${makefile}" Makefile
fi
if test -d "${files_dir}"; then
cp --no-preserve=mode "${files_dir}"/* "${PWD}/"
fi
}
# Default function for configuring source.
default_src_configure() {
:
}
# Default function for compiling source. It simply runs make without any parameters.
default_src_compile() {
make "${MAKEJOBS}" -f Makefile PREFIX="${PREFIX}"
}
# Default installing function. PREFIX should be set by run.sh script.
# Note that upstream makefiles might ignore PREFIX and have to be configured in configure stage.
default_src_install() {
make -f Makefile install PREFIX="${PREFIX}" DESTDIR="${DESTDIR}"
}
# Default function for postprocessing binaries.
default_src_postprocess() {
if (command -v find && command -v file && command -v strip) >/dev/null 2>&1; then
# Logic largely taken from void linux 06-strip-and-debug-pkgs.sh
# shellcheck disable=SC2162
find "${DESTDIR}" -type f | while read f; do
case "$(file -bi "${f}")" in
application/x-executable*) strip "${f}" ;;
application/x-sharedlib*|application/x-pie-executable*)
machine_set="$(file -b "${f}")"
case "${machine_set}" in
*no\ machine*) ;; # don't strip ELF container-only
*) strip --strip-unneeded "${f}" ;;
esac
;;
application/x-archive*) strip --strip-debug "${f}" ;;
esac
done
fi
}
src_pkg() {
touch -t 197001010000.00 .
reset_timestamp
local tar_basename="${pkg}_${revision}.tar"
local dest_tar="/external/repo/${tar_basename}"
local filelist=/tmp/filelist.txt
cd /external/repo
# If grep is unavailable, then tar --sort is unavailable.
# So this does not need a command -v grep.
if tar --help | grep ' \-\-sort' >/dev/null 2>&1; then
tar -C "${DESTDIR}" --sort=name --hard-dereference \
--numeric-owner --owner=0 --group=0 --mode=go=rX,u+rw -cf "${dest_tar}" .
else
local olddir
olddir=$PWD
cd "${DESTDIR}"
local null
if command -v find >/dev/null 2>&1 && command -v sort >/dev/null 2>&1; then
find . -print0 | LC_ALL=C sort -z > "${filelist}"
null="--null"
elif command -v sort >/dev/null 2>&1; then
get_files . | LC_ALL=C sort > "${filelist}"
else
get_files . > ${filelist}
fi
tar --no-recursion ${null} --files-from "${filelist}" \
--numeric-owner --owner=0 --group=0 --mode=go=rX,u+rw -cf "${dest_tar}"
rm -f "$filelist"
cd "$olddir"
fi
touch -t 197001010000.00 "${tar_basename}"
bzip2 --best "${tar_basename}"
}
src_checksum() {
local pkg=$1 revision=$2
local rval=0
if ! [ "$UPDATE_CHECKSUMS" = True ] ; then
# We avoid using pipes as that is not supported by initial sha256sum from mescc-tools-extra
local checksum_file=/tmp/checksum
_grep "${pkg}_${revision}" "${SRCDIR}/SHA256SUMS.pkgs" > "${checksum_file}" || true
# Check there is something in checksum_file
if ! [ -s "${checksum_file}" ]; then
echo "${pkg}: no checksum stored!"
false
fi
echo "${pkg}: checksumming created package."
sha256sum -c "${checksum_file}" || rval=$?
rm "${checksum_file}"
fi
return "${rval}"
}
src_apply() {
local pkg="${1}" revision="${2}"
local TAR_PREFIX BZIP2_PREFIX
# Make sure we have at least one copy of tar
if [[ "${pkg}" == tar-* ]]; then
mkdir -p /tmp
cp "${PREFIX}/bin/tar" "/tmp/tar"
TAR_PREFIX="/tmp/"
fi
# Bash does not like to be overwritten
if [[ "${pkg}" == bash-* ]]; then
rm "${PREFIX}/bin/bash"
fi
# Overwriting files is mega busted, so do it manually
# shellcheck disable=SC2162
if [ -e /tmp/filelist.txt ]; then
while IFS= read -d $'\0' file; do
rm -f "/${file}" >/dev/null 2>&1 || true
done < /tmp/filelist.txt
fi
# Bzip2 does not like to be overwritten
if [[ "${pkg}" == bzip2-* ]]; then
mkdir -p /tmp
mv "${PREFIX}/bin/bzip2" "/tmp/bzip2"
BZIP2_PREFIX="/tmp/"
fi
"${BZIP2_PREFIX}bzip2" -dc "/external/repo/${pkg}_${revision}.tar.bz2" | \
"${TAR_PREFIX}tar" -C / -xpf -
rm -f "/tmp/bzip2" "/tmp/tar"
}
# Check if bash function exists
fn_exists() {
test "$(type -t "$1")" == 'function'
}
# Call package specific function or default implementation.
call() {
if fn_exists "$1"; then
$1
else
default_"${1}"
fi
}
# Call default build stage function
default() {
"default_${build_stage}"
}