common 8.6 KB

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