logo

live-bootstrap

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

helpers.sh (17639B)


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