common 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  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. DOTEPOCH=".epoch"
  34. DOTRNDSEED=".rndseed"
  35. DOTVERSION=".version"
  36. DOTREVISION=".revision"
  37. DOTTARFILES=".tarfiles"
  38. ARCHIVE="tar.xz"
  39. CHECKSUM="sha256sum"
  40. DSIG="asc"
  41. CONFIG_SHELL="${CONFIG_SHELL:-$(which bash)}"
  42. EDITOR="${EDITOR:-$(which vi || true)}"
  43. TASKS="${TASKS:-1}"
  44. function_check() {
  45. local function=$1
  46. declare -f -F "$function" > /dev/null
  47. }
  48. variable_check() {
  49. local variable=$1
  50. test ! -z "${!variable}"
  51. }
  52. arguments_list() {
  53. local argument
  54. for argument in "$@"
  55. do
  56. printf '%s\n' "$argument"
  57. done
  58. }
  59. download_wrapper() {
  60. local download_dir="$1"
  61. shift
  62. local uris=($@)
  63. local wget_options=(
  64. '--config=/dev/null'
  65. '--secure-protocol=PFS'
  66. "--directory-prefix=$download_dir"
  67. '--continue'
  68. '--'
  69. )
  70. local curl_options=(
  71. '-q'
  72. '--continue-at -'
  73. '--remote-name'
  74. '--retry 20'
  75. '--ssl'
  76. '--tlsv1.2'
  77. '--'
  78. )
  79. if hash wget > /dev/null 2>&1; then
  80. wget "${wget_options[@]}" "${uris[@]}"
  81. elif hash curl > /dev/null 2>&1; then
  82. (
  83. cd "$download_dir"
  84. curl "${curl_options[@]}" "${uris[@]}"
  85. )
  86. else
  87. printf '\n%s\n\n' 'Error: Neither wget nor curl were found' 1>&2
  88. return 1
  89. fi
  90. }
  91. diff_patch_file() {
  92. local repository_path="$1"
  93. local patch_file_path="$2"
  94. # TODO: Improve handling of filenames to avoid gotchas w/ \n, \t, etc.
  95. local filename_in_diff="$(sed -rne 's/^-{3}\s+([^ \r\n]*).*/\1/p' "$patch_file_path")"
  96. local source_file_path
  97. if ! ( grep -E '^-{3}.*/' "$patch_file_path" >/dev/null 2>&1 ); then
  98. source_file_path="$repository_path/$filename_in_diff"
  99. else
  100. source_file_path="$repository_path/${filename_in_diff##*/}"
  101. fi
  102. patch "$source_file_path" "$patch_file_path"
  103. }
  104. path_wildcard_expand() {
  105. local path=$@
  106. # Evaluation fails with unescaped whitespaces.
  107. path=$(printf '%s\n' "$path" | sed "s/ /\\\ /g")
  108. eval "arguments_list "$path""
  109. }
  110. file_checksum_create() {
  111. local path=$1
  112. local checksum_path="$path.$CHECKSUM"
  113. local name=$(basename "$path")
  114. local directory_path=$(dirname "$path")
  115. (
  116. cd "$directory_path"
  117. sha256sum "$name" > "$checksum_path"
  118. )
  119. }
  120. file_checksum_check() {
  121. local path=$1
  122. local checksum_path="$path.$CHECKSUM"
  123. local name=$(basename "$path")
  124. local directory_path=$(dirname "$path")
  125. if ! [[ -f "$checksum_path" ]]
  126. then
  127. printf 1>&2 '%s\n' 'Could not verify file checksum!'
  128. return 1
  129. fi
  130. (
  131. cd "$directory_path"
  132. sha256sum -c "$checksum_path"
  133. )
  134. }
  135. file_signature_create() {
  136. local path=$1
  137. local signature_path="$path.$DSIG"
  138. if [[ -z "$RELEASE_KEY" ]]
  139. then
  140. return 0
  141. fi
  142. gpg --default-key "$RELEASE_KEY" --armor --output "$signature_path" --detach-sign --yes "$path"
  143. }
  144. file_signature_check() {
  145. local path=$1
  146. local signature_path="$path.$DSIG"
  147. if ! [[ -f "$signature_path" ]]
  148. then
  149. printf 1>&2 '%s\n' 'Could not verify file signature!'
  150. return 1
  151. fi
  152. gpg --armor --verify "$signature_path" "$path"
  153. }
  154. file_verification_create() {
  155. local path=$1
  156. file_checksum_create "$path"
  157. file_signature_create "$path"
  158. }
  159. file_verification_check() {
  160. local path=$1
  161. file_checksum_check "$path"
  162. file_signature_check "$path"
  163. }
  164. file_exists_check() {
  165. local path=$1
  166. test -f "$path"
  167. }
  168. directory_filled_check() {
  169. local path=$1
  170. if [[ -z "$(ls -A "$path" 2> /dev/null)" ]]
  171. then
  172. return 1
  173. else
  174. return 0
  175. fi
  176. }
  177. archive_files_create() {
  178. local source_path="$1"
  179. local directory="$(basename "$source_path")"
  180. local tarfiles_path="$source_path/$DOTTARFILES"
  181. local revision_path="$source_path/$DOTREVISION"
  182. local version_path="$source_path/$DOTVERSION"
  183. local epoch_path="$source_path/$DOTEPOCH"
  184. local rnd_seed_path="$source_path/$DOTRNDSEED"
  185. # Files in "$tarfiles_path" are NUL terminated.
  186. # `tr '\0' '\n'` for human-readable output.
  187. if git_check "$source_path"; then
  188. git_files "$source_path" > "$tarfiles_path"
  189. printf '%s\0' "$DOTTARFILES" >> "$tarfiles_path"
  190. else
  191. find "$source_path" -print0 | env LC_ALL='C.UTF-8' sort -z | sed -z "1d;s,^$source_path/\\?,,;/^$DOTTARFILES\$/d" > "$tarfiles_path"
  192. fi
  193. for dotfile in "$revision_path" \
  194. "$version_path" \
  195. "$epoch_path" \
  196. "$rnd_seed_path"
  197. do
  198. if [[ -f "$dotfile" ]]; then
  199. printf '%s\0' ".${dotfile##*.}" >> "$tarfiles_path"
  200. fi
  201. done
  202. }
  203. archive_files_date() {
  204. local source_path="$1"
  205. local epoch_path="$source_path/$DOTEPOCH"
  206. if [[ -n "$SOURCE_DATE_EPOCH" ]]; then
  207. find "$source_path" -execdir touch --no-dereference --date="@$SOURCE_DATE_EPOCH" {} +
  208. fi
  209. }
  210. archive_create() {
  211. local archive_path="$1"
  212. local source_path="$2"
  213. local directory="$3"
  214. local tarfiles_path="$source_path/$DOTTARFILES"
  215. local directory_path="$(dirname "$archive_path")"
  216. mkdir -p "$directory_path"
  217. if [[ -z "$directory" ]]; then
  218. directory="$(basename "$source_path")"
  219. fi
  220. archive_files_create "$source_path"
  221. archive_files_date "$source_path"
  222. local tar_options=(
  223. --create
  224. --xz
  225. --file="$archive_path"
  226. --files-from="$tarfiles_path"
  227. --transform="s,^,$directory/,S"
  228. --no-recursion
  229. --warning=no-filename-with-nuls
  230. --null
  231. --owner=0
  232. --group=0
  233. --numeric-owner
  234. )
  235. (
  236. cd "$source_path"
  237. tar "${tar_options[@]}"
  238. )
  239. }
  240. archive_extract() {
  241. local archive_path="$1"
  242. local destination_path="$2"
  243. if [[ -z "$destination_path" ]]; then
  244. destination_path="$(dirname "$archive_path")"
  245. fi
  246. tar -xf "$archive_path" -ps -C "$destination_path"
  247. }
  248. rootfs_files_create() {
  249. local source_path="$1"
  250. local directory="$(basename "$source_path")"
  251. local tarfiles_path="$source_path/$DOTTARFILES"
  252. # Files in "$tarfiles_path" are NUL terminated.
  253. # `tr '\0' '\n'` for human-readable output.
  254. execute_root find "$source_path" -print0 | env LC_ALL='C.UTF-8' sort -z | sed -z "1d;s,^$source_path/\\?,,;/^$DOTTARFILES\$/d" > "$tarfiles_path"
  255. }
  256. rootfs_files_date() {
  257. local source_path="$1"
  258. local epoch_path="$source_path/$DOTEPOCH"
  259. if [[ -n "$SOURCE_DATE_EPOCH" ]]; then
  260. execute_root find "$source_path" -execdir touch --no-dereference --date="@$SOURCE_DATE_EPOCH" {} +
  261. fi
  262. }
  263. rootfs_create() {
  264. local rootfs_path="$1"
  265. local source_path="$2"
  266. local directory="$3"
  267. local tarfiles_path="$source_path/$DOTTARFILES"
  268. local directory_path="$(dirname "$rootfs_path")"
  269. mkdir -p "$directory_path"
  270. if [[ -z "$directory" ]]; then
  271. directory="$(basename "$source_path")"
  272. fi
  273. rootfs_files_create "$source_path"
  274. rootfs_files_date "$source_path"
  275. local tar_options=(
  276. --create
  277. --xz
  278. --file="$rootfs_path"
  279. --files-from="$tarfiles_path"
  280. --no-recursion
  281. --warning=no-filename-with-nuls
  282. --null
  283. --owner=0
  284. --group=0
  285. --numeric-owner
  286. )
  287. (
  288. cd "$source_path"
  289. execute_root tar "${tar_options[@]}"
  290. )
  291. execute_root chmod 644 "$rootfs_path"
  292. execute_root chown "$USER:$USER" "$rootfs_path"
  293. }
  294. requirements() {
  295. local requirement
  296. local requirement_path
  297. for requirement in "$@"
  298. do
  299. requirement_path=$(which "$requirement" || true)
  300. if [[ -z "$requirement_path" ]]
  301. then
  302. printf 1>&2 '%s\n' "Missing requirement: $requirement"
  303. exit 1
  304. fi
  305. done
  306. }
  307. requirements_root() {
  308. local requirement
  309. local requirement_path
  310. for requirement in "$@"
  311. do
  312. # We need to keep stdout output to show the command.
  313. requirement_path=$(execute_root which "$requirement" || true)
  314. if [[ -z "$requirement_path" ]]
  315. then
  316. printf 1>&2 '%s\n' "Missing requirement: $requirement"
  317. exit 1
  318. fi
  319. done
  320. }
  321. arguments_concat() {
  322. local delimiter=$1
  323. shift
  324. local concat
  325. for argument in "$@"
  326. do
  327. if [[ -n "$concat" ]]
  328. then
  329. concat="$concat""$delimiter""$argument"
  330. else
  331. concat="$argument"
  332. fi
  333. done
  334. printf '%s\n' "$concat"
  335. }
  336. execute_root() {
  337. local sudo=$(which sudo 2> /dev/null || true)
  338. local arguments
  339. printf 1>&2 '%s' 'Running command as root: '
  340. printf 1>&2 '%b\n' "$*"
  341. if [[ -n "$sudo" ]]
  342. then
  343. sudo "$@"
  344. else
  345. # Quote arguments for eval through su.
  346. arguments=$(printf '%q ' "$@")
  347. su -c "$arguments"
  348. fi
  349. }