logo

live-bootstrap

Mirror of <https://github.com/fosslinux/live-bootstrap>

helpers.sh (16681B)


  1. #!/bin/bash -e
  2. # SPDX-FileCopyrightText: 2021 Andrius Štikonas <andrius@stikonas.eu>
  3. # SPDX-FileCopyrightText: 2021-22 fosslinux <fosslinux@aussies.space>
  4. # SPDX-FileCopyrightText: 2021 Paul Dersey <pdersey@gmail.com>
  5. # SPDX-FileCopyrightText: 2021 Melg Eight <public.melg8@gmail.com>
  6. #
  7. # SPDX-License-Identifier: GPL-3.0-or-later
  8. # Set constant umask
  9. umask 022
  10. # Get a list of files
  11. get_files() {
  12. echo "."
  13. _get_files "${1}"
  14. }
  15. _get_files() {
  16. local prefix
  17. prefix="${1}"
  18. fs=
  19. if [ -n "$(ls 2>/dev/null)" ]; then
  20. # This can be removed once Debian 12 is stable
  21. # shellcheck disable=SC2035
  22. fs=$(echo *)
  23. fi
  24. if [ -n "$(ls .[0-z]* 2>/dev/null)" ]; then
  25. fs="${fs} $(echo .[0-z]*)"
  26. fi
  27. for f in ${fs}; do
  28. # Archive symlinks to directories as symlinks
  29. echo "${prefix}/${f}"
  30. if [ -d "./${f}" ] && ! [ -h "./${f}" ]; then
  31. cd "./${f}"
  32. _get_files "${prefix}/${f}"
  33. cd ..
  34. fi
  35. done
  36. }
  37. # Reset all timestamps to unix time 0
  38. reset_timestamp() {
  39. if command -v find >/dev/null 2>&1; then
  40. # find does not error out on exec error
  41. find . -print0 | xargs -0 touch -h -t 197001010000.00
  42. else
  43. # A rudimentary find implementation that does the trick
  44. fs=
  45. if [ -n "$(ls 2>/dev/null)" ]; then
  46. fs=$(echo ./*)
  47. fi
  48. if [ -n "$(ls .[0-z]* 2>/dev/null)" ]; then
  49. fs="${fs} $(echo .[0-z]*)"
  50. fi
  51. for f in ${fs}; do
  52. touch -h -t 197001010000.00 "./${f}"
  53. if [ -d "./${f}" ]; then
  54. cd "./${f}"
  55. reset_timestamp
  56. cd ..
  57. fi
  58. done
  59. fi
  60. }
  61. # Fake grep
  62. _grep() {
  63. local text="${1}"
  64. local fname="${2}"
  65. if command -v grep >/dev/null 2>&1; then
  66. grep "${text}" "${fname}"
  67. else
  68. # shellcheck disable=SC2162
  69. while read line; do
  70. case "${line}" in *"${text}"*)
  71. echo "${line}" ;;
  72. esac
  73. done < "${fname}"
  74. fi
  75. }
  76. get_revision() {
  77. local pkg=$1
  78. local oldpwd="${PWD}"
  79. cd "/external/repo"
  80. # Get revision (n time this package has been built)
  81. revision=$( (ls -1 "${pkg}"* 2>/dev/null || true) | wc -l | sed 's/ *//g')
  82. cd "${oldpwd}"
  83. }
  84. # Installs binary packages from an earlier run
  85. # This is useful to speed up development cycle
  86. bin_preseed() {
  87. if [ -d "/external/repo-preseeded" ]; then
  88. get_revision "${pkg}"
  89. cd "/external/repo-preseeded"
  90. test -e "${pkg}_${revision}.tar.bz2" || return 1
  91. if [ "${UPDATE_CHECKSUMS}" = "True" ] || src_checksum "${pkg}" $((revision)); then
  92. echo "${pkg}: installing prebuilt package."
  93. mv "${pkg}_${revision}"* /external/repo || return 1
  94. cd "/external/repo"
  95. rm -f /tmp/filelist.txt
  96. src_apply "${pkg}" $((revision))
  97. cd "${SRCDIR}"
  98. return 0
  99. fi
  100. fi
  101. return 1
  102. }
  103. # Removes either an existing package or file
  104. uninstall() {
  105. local in_fs in_pkg symlinks
  106. while [ $# -gt 0 ]; do
  107. removing="$1"
  108. case "${removing}" in
  109. /*)
  110. # Removing a file
  111. echo "removing file: ${removing}."
  112. rm -f "${removing}"
  113. ;;
  114. *)
  115. echo "${removing}: uninstalling."
  116. local oldpwd="${PWD}"
  117. mkdir -p "/tmp/removing"
  118. cd "/tmp/removing"
  119. get_revision "${removing}"
  120. local filename="/external/repo/${removing}_$((revision-1)).tar.bz2"
  121. # Initial bzip2 built against meslibc has broken pipes
  122. bzip2 -dc "${filename}" | tar -xf -
  123. # reverse to have files before directories
  124. if command -v find >/dev/null 2>&1; then
  125. find . | sort -r > ../filelist
  126. else
  127. get_files . | tac > ../filelist
  128. fi
  129. # shellcheck disable=SC2162
  130. while read file; do
  131. if [ -d "${file}" ]; then
  132. if [ -z "$(ls -A "/${file}")" ]; then
  133. rmdir "/${file}"
  134. fi
  135. else
  136. # in some cases we might be uninstalling a file that has already been overwritten
  137. # in this case we don't want to remove it
  138. in_fs="$(sha256sum "${file}" 2>/dev/null | cut -d' ' -f1)"
  139. in_pkg="$(sha256sum "/${file}" 2>/dev/null | cut -d' ' -f1)"
  140. if [ "${in_fs}" = "${in_pkg}" ]; then
  141. rm -f "/${file}"
  142. fi
  143. if [ -h "${file}" ]; then
  144. symlinks="${symlinks} ${file}"
  145. fi
  146. fi
  147. done < ../filelist
  148. rm -f ../filelist
  149. for link in ${symlinks}; do
  150. if [ ! -e "/${link}" ]; then
  151. rm -f "/${link}"
  152. fi
  153. done
  154. cd "${oldpwd}"
  155. rm -rf "/tmp/removing"
  156. ;;
  157. esac
  158. shift
  159. done
  160. }
  161. # Common build steps
  162. # Build function provides a few common stages with default implementation
  163. # that can be overridden on per package basis in the build script.
  164. # build takes two arguments:
  165. # 1) name-version of the package
  166. # 2) optionally specify build script. Default is pass$((revision+1)).sh
  167. # 3) optionally specify directory to cd into
  168. build() {
  169. pkg=$1
  170. get_revision "${pkg}"
  171. script_name=${2:-pass$((revision+1)).sh}
  172. dirname=${3:-${pkg}}
  173. # shellcheck disable=SC2015
  174. bin_preseed && return || true # Normal build if preseed fails
  175. cd "${SRCDIR}/${pkg}" || (echo "Cannot cd into ${pkg}!"; kill $$)
  176. echo "${pkg}: beginning build using script ${script_name}"
  177. base_dir="${PWD}"
  178. if [ -e "${base_dir}/patches-$(basename "${script_name}" .sh)" ]; then
  179. patch_dir="${base_dir}/patches-$(basename "${script_name}" .sh)"
  180. else
  181. patch_dir="${base_dir}/patches"
  182. fi
  183. mk_dir="${base_dir}/mk"
  184. files_dir="${base_dir}/files"
  185. rm -rf "build"
  186. mkdir "build"
  187. cd "build"
  188. build_script="${base_dir}/${script_name}"
  189. if test -e "${build_script}"; then
  190. # shellcheck source=/dev/null
  191. . "${build_script}"
  192. fi
  193. echo "${pkg}: getting sources."
  194. build_stage=src_get
  195. call $build_stage
  196. echo "${pkg}: unpacking source."
  197. build_stage=src_unpack
  198. call $build_stage
  199. unset EXTRA_DISTFILES
  200. cd "${dirname}" || (echo "Cannot cd into build/${dirname}!"; kill $$)
  201. echo "${pkg}: preparing source."
  202. build_stage=src_prepare
  203. call $build_stage
  204. echo "${pkg}: configuring source."
  205. build_stage=src_configure
  206. call $build_stage
  207. echo "${pkg}: compiling source."
  208. build_stage=src_compile
  209. call $build_stage
  210. echo "${pkg}: install to fakeroot."
  211. mkdir -p "${DESTDIR}"
  212. build_stage=src_install
  213. call $build_stage
  214. echo "${pkg}: postprocess binaries."
  215. build_stage=src_postprocess
  216. call $build_stage
  217. echo "${pkg}: creating package."
  218. cd "${DESTDIR}"
  219. src_pkg
  220. src_checksum "${pkg}" "${revision}"
  221. echo "${pkg}: cleaning up."
  222. rm -rf "${SRCDIR}/${pkg}/build"
  223. rm -rf "${DESTDIR}"
  224. echo "${pkg}: installing package."
  225. src_apply "${pkg}" "${revision}"
  226. echo "${pkg}: build successful"
  227. cd "${SRCDIR}"
  228. unset -f src_get src_unpack src_prepare src_configure src_compile src_install src_postprocess
  229. unset extract
  230. }
  231. download_source_line() {
  232. url="${1}"
  233. checksum="${2}"
  234. fname="${3}"
  235. # Default to basename of url if not given
  236. fname="${fname:-$(basename "${url}")}"
  237. if ! [ -e "${fname}" ]; then
  238. curl --fail --retry 5 --location "${url}" --output "${fname}" || true
  239. fi
  240. }
  241. check_source_line() {
  242. url="${1}"
  243. checksum="${2}"
  244. fname="${3}"
  245. # Default to basename of url if not given
  246. fname="${fname:-$(basename "${url}")}"
  247. if ! [ -e "${fname}" ]; then
  248. false
  249. fi
  250. echo "${checksum} ${fname}" > "${fname}.sum"
  251. sha256sum -c "${fname}.sum"
  252. rm "${fname}.sum"
  253. }
  254. # Default get function that downloads source tarballs.
  255. default_src_get() {
  256. # shellcheck disable=SC2153
  257. cd "${DISTFILES}"
  258. # shellcheck disable=SC2162
  259. while read line; do
  260. # This is intentional - we want to split out ${line} into separate arguments.
  261. # shellcheck disable=SC2086
  262. download_source_line ${line}
  263. done < "${base_dir}/sources"
  264. # shellcheck disable=SC2162
  265. while read line; do
  266. # This is intentional - we want to split out ${line} into separate arguments.
  267. # shellcheck disable=SC2086
  268. check_source_line ${line}
  269. done < "${base_dir}/sources"
  270. cd -
  271. }
  272. # Intelligently extracts a file based upon its filetype.
  273. extract_file() {
  274. f="${3:-$(basename "${1}")}"
  275. # shellcheck disable=SC2154
  276. case "${noextract}" in
  277. *${f}*)
  278. cp "${DISTFILES}/${f}" .
  279. ;;
  280. *)
  281. case "${f}" in
  282. *.tar* | *.tgz)
  283. # shellcheck disable=SC2153
  284. if test -e "${PREFIX}/libexec/rmt"; then
  285. # Again, we want to split out into words.
  286. # shellcheck disable=SC2086
  287. tar --no-same-owner -xf "${DISTFILES}/${f}" ${extract}
  288. else
  289. # shellcheck disable=SC2086
  290. case "${f}" in
  291. *.tar.gz) tar -xzf "${DISTFILES}/${f}" ${extract} ;;
  292. *.tar.bz2)
  293. # Initial bzip2 built against meslibc has broken pipes
  294. bzip2 -dc "${DISTFILES}/${f}" | tar -xf - ${extract} ;;
  295. *.tar.xz | *.tar.lzma)
  296. if test -e "${PREFIX}/bin/xz"; then
  297. tar -xf "${DISTFILES}/${f}" --use-compress-program=xz ${extract}
  298. else
  299. unxz --file "${DISTFILES}/${f}" | tar -xf - ${extract}
  300. fi
  301. ;;
  302. esac
  303. fi
  304. ;;
  305. *)
  306. cp "${DISTFILES}/${f}" .
  307. ;;
  308. esac
  309. ;;
  310. esac
  311. }
  312. # Default unpacking function that unpacks all sources.
  313. default_src_unpack() {
  314. # Handle the first one differently
  315. first_line=$(head -n 1 ../sources)
  316. # Again, we want to split out into words.
  317. # shellcheck disable=SC2086
  318. extract_file ${first_line}
  319. # This assumes there is only one directory in the tarball
  320. # Get the dirname "smartly"
  321. if ! [ -e "${dirname}" ]; then
  322. for i in *; do
  323. if [ -d "${i}" ]; then
  324. dirname="${i}"
  325. break
  326. fi
  327. done
  328. fi
  329. # shellcheck disable=SC2162
  330. tail -n +2 ../sources | while read line; do
  331. # shellcheck disable=SC2086
  332. extract_file ${line}
  333. done
  334. }
  335. # Default function to prepare source code.
  336. # It applies all patches from patch_dir (at the moment only -p0 patches are supported).
  337. # Patches are applied from the parent directory.
  338. # Then it copies our custom makefile and any other custom files from files directory.
  339. default_src_prepare() {
  340. if test -d "${patch_dir}"; then
  341. if ls "${patch_dir}"/*.patch >/dev/null 2>&1; then
  342. for p in "${patch_dir}"/*.patch; do
  343. echo "Applying patch: ${p}"
  344. patch -d.. -Np0 < "${p}"
  345. done
  346. fi
  347. fi
  348. makefile="${mk_dir}/main.mk"
  349. if test -e "${makefile}"; then
  350. cp "${makefile}" Makefile
  351. fi
  352. if test -d "${files_dir}"; then
  353. cp --no-preserve=mode "${files_dir}"/* "${PWD}/"
  354. fi
  355. }
  356. # Default function for configuring source.
  357. default_src_configure() {
  358. :
  359. }
  360. # Default function for compiling source. It simply runs make without any parameters.
  361. default_src_compile() {
  362. make "${MAKEJOBS}" -f Makefile PREFIX="${PREFIX}"
  363. }
  364. # Default installing function. PREFIX should be set by run.sh script.
  365. # Note that upstream makefiles might ignore PREFIX and have to be configured in configure stage.
  366. default_src_install() {
  367. make -f Makefile install PREFIX="${PREFIX}" DESTDIR="${DESTDIR}"
  368. }
  369. # Default function for postprocessing binaries.
  370. default_src_postprocess() {
  371. if (command -v find && command -v file && command -v strip) >/dev/null 2>&1; then
  372. # Logic largely taken from void linux 06-strip-and-debug-pkgs.sh
  373. # shellcheck disable=SC2162
  374. find "${DESTDIR}" -type f | while read f; do
  375. case "$(file -bi "${f}")" in
  376. application/x-executable*) strip "${f}" ;;
  377. application/x-sharedlib*|application/x-pie-executable*)
  378. machine_set="$(file -b "${f}")"
  379. case "${machine_set}" in
  380. *no\ machine*) ;; # don't strip ELF container-only
  381. *) strip --strip-unneeded "${f}" ;;
  382. esac
  383. ;;
  384. application/x-archive*) strip --strip-debug "${f}" ;;
  385. esac
  386. done
  387. fi
  388. }
  389. src_pkg() {
  390. touch -t 197001010000.00 .
  391. reset_timestamp
  392. local tar_basename="${pkg}_${revision}.tar"
  393. local dest_tar="/external/repo/${tar_basename}"
  394. local filelist=/tmp/filelist.txt
  395. cd /external/repo
  396. # If grep is unavailable, then tar --sort is unavailable.
  397. # So this does not need a command -v grep.
  398. if tar --help | grep ' \-\-sort' >/dev/null 2>&1; then
  399. tar -C "${DESTDIR}" --sort=name --hard-dereference \
  400. --numeric-owner --owner=0 --group=0 --mode=go=rX,u+rw -cf "${dest_tar}" .
  401. else
  402. local olddir
  403. olddir=$PWD
  404. cd "${DESTDIR}"
  405. local null
  406. if command -v find >/dev/null 2>&1 && command -v sort >/dev/null 2>&1; then
  407. find . -print0 | LC_ALL=C sort -z > "${filelist}"
  408. null="--null"
  409. elif command -v sort >/dev/null 2>&1; then
  410. get_files . | LC_ALL=C sort > "${filelist}"
  411. else
  412. get_files . > ${filelist}
  413. fi
  414. tar --no-recursion ${null} --files-from "${filelist}" \
  415. --numeric-owner --owner=0 --group=0 --mode=go=rX,u+rw -cf "${dest_tar}"
  416. rm -f "$filelist"
  417. cd "$olddir"
  418. fi
  419. touch -t 197001010000.00 "${tar_basename}"
  420. bzip2 --best "${tar_basename}"
  421. }
  422. src_checksum() {
  423. local pkg=$1 revision=$2
  424. local rval=0
  425. if ! [ "$UPDATE_CHECKSUMS" = True ] ; then
  426. # We avoid using pipes as that is not supported by initial sha256sum from mescc-tools-extra
  427. local checksum_file=/tmp/checksum
  428. _grep "${pkg}_${revision}" "${SRCDIR}/SHA256SUMS.pkgs" > "${checksum_file}" || true
  429. # Check there is something in checksum_file
  430. if ! [ -s "${checksum_file}" ]; then
  431. echo "${pkg}: no checksum stored!"
  432. false
  433. fi
  434. echo "${pkg}: checksumming created package."
  435. sha256sum -c "${checksum_file}" || rval=$?
  436. rm "${checksum_file}"
  437. fi
  438. return "${rval}"
  439. }
  440. src_apply() {
  441. local pkg="${1}" revision="${2}"
  442. local TAR_PREFIX BZIP2_PREFIX
  443. # Make sure we have at least one copy of tar
  444. if [[ "${pkg}" == tar-* ]]; then
  445. mkdir -p /tmp
  446. cp "${PREFIX}/bin/tar" "/tmp/tar"
  447. TAR_PREFIX="/tmp/"
  448. fi
  449. # Bash does not like to be overwritten
  450. if [[ "${pkg}" == bash-* ]]; then
  451. rm "${PREFIX}/bin/bash"
  452. fi
  453. # Overwriting files is mega busted, so do it manually
  454. # shellcheck disable=SC2162
  455. if [ -e /tmp/filelist.txt ]; then
  456. while IFS= read -d $'\0' file; do
  457. rm -f "/${file}" >/dev/null 2>&1 || true
  458. done < /tmp/filelist.txt
  459. fi
  460. # Bzip2 does not like to be overwritten
  461. if [[ "${pkg}" == bzip2-* ]]; then
  462. mkdir -p /tmp
  463. mv "${PREFIX}/bin/bzip2" "/tmp/bzip2"
  464. BZIP2_PREFIX="/tmp/"
  465. fi
  466. "${BZIP2_PREFIX}bzip2" -dc "/external/repo/${pkg}_${revision}.tar.bz2" | \
  467. "${TAR_PREFIX}tar" -C / -xpf -
  468. rm -f "/tmp/bzip2" "/tmp/tar"
  469. }
  470. # Check if bash function exists
  471. fn_exists() {
  472. test "$(type -t "$1")" == 'function'
  473. }
  474. # Call package specific function or default implementation.
  475. call() {
  476. if fn_exists "$1"; then
  477. $1
  478. else
  479. default_"${1}"
  480. fi
  481. }
  482. # Call default build stage function
  483. default() {
  484. "default_${build_stage}"
  485. }