common 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. #!/usr/bin/env bash
  2. # Copyright (C) 2016 Paul Kocialkowski <contact@paulk.fr>
  3. #
  4. # This program is free software: you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation, either version 3 of the License, or
  7. # (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. BUILD_SYSTEM="libreboot"
  17. PROJECTS="projects"
  18. SOURCES="sources"
  19. BUILD="build"
  20. INSTALL="install"
  21. RELEASE="release"
  22. SYSTEMS="systems"
  23. IMAGES="images"
  24. TOOLS="tools"
  25. CONFIGS="configs"
  26. PATCHES="patches"
  27. TARGETS="targets"
  28. REVISION="revision"
  29. VARIANTS="variants"
  30. BLOBS="blobs"
  31. BLOBS_IGNORE="blobs-ignore"
  32. BLOBS_DISCOVER="blobs-discover"
  33. DEPENDENCIES="dependencies"
  34. DOTEPOCH=".epoch"
  35. DOTRNDSEED=".rndseed"
  36. DOTVERSION=".version"
  37. DOTREVISION=".revision"
  38. DOTTARFILES=".tarfiles"
  39. ARCHIVE="tar.xz"
  40. CHECKSUM="sha256sum"
  41. DSIG="asc"
  42. CONFIG_SHELL="${CONFIG_SHELL:-$(which bash)}"
  43. EDITOR="${EDITOR:-$(which vi || true)}"
  44. TASKS="${TASKS:-1}"
  45. function_check() {
  46. local function=$1
  47. declare -f -F "$function" > /dev/null
  48. }
  49. variable_check() {
  50. local variable=$1
  51. test ! -z "${!variable}"
  52. }
  53. arguments_list() {
  54. local argument
  55. for argument in "$@"
  56. do
  57. printf '%s\n' "$argument"
  58. done
  59. }
  60. download_wrapper() {
  61. local download_dir="$1"
  62. shift
  63. local uris=($@)
  64. local wget_options=(
  65. '--config=/dev/null'
  66. '--secure-protocol=PFS'
  67. "--directory-prefix=$download_dir"
  68. '--continue'
  69. '--'
  70. )
  71. local curl_options=(
  72. '-q'
  73. '--continue-at -'
  74. '--remote-name'
  75. '--retry 20'
  76. '--ssl'
  77. '--tlsv1.2'
  78. '--'
  79. )
  80. if hash wget > /dev/null 2>&1; then
  81. wget "${wget_options[@]}" "${uris[@]}"
  82. elif hash curl > /dev/null 2>&1; then
  83. (
  84. cd "$download_dir"
  85. curl "${curl_options[@]}" "${uris[@]}"
  86. )
  87. else
  88. printf '\n%s\n\n' 'Error: Neither wget nor curl were found' 1>&2
  89. return 1
  90. fi
  91. }
  92. diff_patch() {
  93. local sources_path=$1
  94. local patch_path=$2
  95. patch -fd "$sources_path" -r - < "$patch_path"
  96. }
  97. path_wildcard_expand() {
  98. local path=$@
  99. # Evaluation fails with unescaped whitespaces.
  100. path=$(printf '%s\n' "$path" | sed "s/ /\\\ /g")
  101. eval "arguments_list "$path""
  102. }
  103. file_checksum_create() {
  104. local path=$1
  105. local checksum_path="$path.$CHECKSUM"
  106. local name=$(basename "$path")
  107. local directory_path=$(dirname "$path")
  108. (
  109. cd "$directory_path"
  110. sha256sum "$name" > "$checksum_path"
  111. )
  112. }
  113. file_checksum_check() {
  114. local path=$1
  115. local checksum_path="$path.$CHECKSUM"
  116. local name=$(basename "$path")
  117. local directory_path=$(dirname "$path")
  118. if ! [[ -f "$checksum_path" ]]
  119. then
  120. printf 1>&2 '%s\n' 'Could not verify file checksum!'
  121. return 1
  122. fi
  123. (
  124. cd "$directory_path"
  125. sha256sum -c "$checksum_path"
  126. )
  127. }
  128. file_signature_create() {
  129. local path=$1
  130. local signature_path="$path.$DSIG"
  131. if [[ -z "$RELEASE_KEY" ]]
  132. then
  133. return 0
  134. fi
  135. gpg --default-key "$RELEASE_KEY" --armor --output "$signature_path" --detach-sign --yes "$path"
  136. }
  137. file_signature_check() {
  138. local path=$1
  139. local signature_path="$path.$DSIG"
  140. if ! [[ -f "$signature_path" ]]
  141. then
  142. printf 1>&2 '%s\n' 'Could not verify file signature!'
  143. return 1
  144. fi
  145. gpg --armor --verify "$signature_path" "$path"
  146. }
  147. file_verification_create() {
  148. local path=$1
  149. file_checksum_create "$path"
  150. file_signature_create "$path"
  151. }
  152. file_verification_check() {
  153. local path=$1
  154. file_checksum_check "$path"
  155. file_signature_check "$path"
  156. }
  157. file_exists_check() {
  158. local path=$1
  159. test -f "$path"
  160. }
  161. directory_filled_check() {
  162. local path=$1
  163. if [[ -z "$(ls -A "$path" 2> /dev/null)" ]]
  164. then
  165. return 1
  166. else
  167. return 0
  168. fi
  169. }
  170. archive_files_create() {
  171. local source_path="$1"
  172. local directory="$(basename "$source_path")"
  173. local tarfiles_path="$source_path/$DOTTARFILES"
  174. local revision_path="$source_path/$DOTREVISION"
  175. local version_path="$source_path/$DOTVERSION"
  176. local epoch_path="$source_path/$DOTEPOCH"
  177. local rnd_seed_path="$source_path/$DOTRNDSEED"
  178. # Files in "$tarfiles_path" are NUL terminated.
  179. # `tr '\0' '\n'` for human-readable output.
  180. if git_check "$source_path"; then
  181. git_files "$source_path" > "$tarfiles_path"
  182. printf '%s\0' "$DOTTARFILES" >> "$tarfiles_path"
  183. else
  184. find "$source_path" -print0 | env LC_ALL='C.UTF-8' sort -z | sed -z "1d;s,^$source_path/\\?,,;/^$DOTTARFILES\$/d" > "$tarfiles_path"
  185. fi
  186. for dotfile in "$revision_path" \
  187. "$version_path" \
  188. "$epoch_path" \
  189. "$rnd_seed_path"
  190. do
  191. if [[ -f "$dotfile" ]]; then
  192. printf '%s\0' ".${dotfile##*.}" >> "$tarfiles_path"
  193. fi
  194. done
  195. }
  196. archive_files_date() {
  197. local source_path="$1"
  198. local epoch_path="$source_path/$DOTEPOCH"
  199. if [[ -n "$SOURCE_DATE_EPOCH" ]]; then
  200. find "$source_path" -execdir touch --no-dereference --date="@$SOURCE_DATE_EPOCH" {} +
  201. fi
  202. }
  203. archive_create() {
  204. local archive_path="$1"
  205. local source_path="$2"
  206. local directory="$3"
  207. local tarfiles_path="$source_path/$DOTTARFILES"
  208. local directory_path="$(dirname "$archive_path")"
  209. mkdir -p "$directory_path"
  210. if [[ -z "$directory" ]]; then
  211. directory="$(basename "$source_path")"
  212. fi
  213. archive_files_create "$source_path"
  214. archive_files_date "$source_path"
  215. local tar_options=(
  216. --create
  217. --xz
  218. --file="$archive_path"
  219. --files-from="$tarfiles_path"
  220. --transform="s,^,$directory/,S"
  221. --no-recursion
  222. --warning=no-filename-with-nuls
  223. --null
  224. --owner=0
  225. --group=0
  226. --numeric-owner
  227. )
  228. (
  229. cd "$source_path"
  230. tar "${tar_options[@]}"
  231. )
  232. }
  233. archive_extract() {
  234. local archive_path="$1"
  235. local destination_path="$2"
  236. if [[ -z "$destination_path" ]]; then
  237. destination_path="$(dirname "$archive_path")"
  238. fi
  239. tar -xf "$archive_path" -ps -C "$destination_path"
  240. }
  241. rootfs_files_create() {
  242. local source_path="$1"
  243. local directory="$(basename "$source_path")"
  244. local tarfiles_path="$source_path/$DOTTARFILES"
  245. # Files in "$tarfiles_path" are NUL terminated.
  246. # `tr '\0' '\n'` for human-readable output.
  247. execute_root find "$source_path" -print0 | env LC_ALL='C.UTF-8' sort -z | sed -z "1d;s,^$source_path/\\?,,;/^$DOTTARFILES\$/d" > "$tarfiles_path"
  248. }
  249. rootfs_files_date() {
  250. local source_path="$1"
  251. local epoch_path="$source_path/$DOTEPOCH"
  252. if [[ -n "$SOURCE_DATE_EPOCH" ]]; then
  253. execute_root find "$source_path" -execdir touch --no-dereference --date="@$SOURCE_DATE_EPOCH" {} +
  254. fi
  255. }
  256. rootfs_create() {
  257. local rootfs_path="$1"
  258. local source_path="$2"
  259. local directory="$3"
  260. local tarfiles_path="$source_path/$DOTTARFILES"
  261. local directory_path="$(dirname "$rootfs_path")"
  262. mkdir -p "$directory_path"
  263. if [[ -z "$directory" ]]; then
  264. directory="$(basename "$source_path")"
  265. fi
  266. rootfs_files_create "$source_path"
  267. rootfs_files_date "$source_path"
  268. local tar_options=(
  269. --create
  270. --xz
  271. --file="$rootfs_path"
  272. --files-from="$tarfiles_path"
  273. --no-recursion
  274. --warning=no-filename-with-nuls
  275. --null
  276. --owner=0
  277. --group=0
  278. --numeric-owner
  279. )
  280. (
  281. cd "$source_path"
  282. execute_root tar "${tar_options[@]}"
  283. )
  284. execute_root chmod 644 "$rootfs_path"
  285. execute_root chown "$USER:$USER" "$rootfs_path"
  286. }
  287. requirements() {
  288. local requirement
  289. local requirement_path
  290. for requirement in "$@"
  291. do
  292. requirement_path=$(which "$requirement" || true)
  293. if [[ -z "$requirement_path" ]]
  294. then
  295. printf 1>&2 '%s\n' "Missing requirement: $requirement"
  296. exit 1
  297. fi
  298. done
  299. }
  300. requirements_root() {
  301. local requirement
  302. local requirement_path
  303. for requirement in "$@"
  304. do
  305. # We need to keep stdout output to show the command.
  306. requirement_path=$(execute_root which "$requirement" || true)
  307. if [[ -z "$requirement_path" ]]
  308. then
  309. printf 1>&2 '%s\n' "Missing requirement: $requirement"
  310. exit 1
  311. fi
  312. done
  313. }
  314. arguments_concat() {
  315. local delimiter=$1
  316. shift
  317. local concat
  318. for argument in "$@"
  319. do
  320. if [[ -n "$concat" ]]
  321. then
  322. concat="$concat""$delimiter""$argument"
  323. else
  324. concat="$argument"
  325. fi
  326. done
  327. printf '%s\n' "$concat"
  328. }
  329. execute_root() {
  330. local sudo=$(which sudo 2> /dev/null || true)
  331. local arguments
  332. printf 1>&2 '%s' 'Running command as root: '
  333. printf 1>&2 '%b\n' "$*"
  334. if [[ -n "$sudo" ]]
  335. then
  336. sudo "$@"
  337. else
  338. # Quote arguments for eval through su.
  339. arguments=$(printf '%q ' "$@")
  340. su -c "$arguments"
  341. fi
  342. }