chroot-buildpackages.sh 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. #!/bin/bash
  2. #
  3. # Copyright (c) 2013-2021 Igor Pecovnik, igor.pecovnik@gma**.com
  4. #
  5. # This file is licensed under the terms of the GNU General Public
  6. # License version 2. This program is licensed "as is" without any
  7. # warranty of any kind, whether express or implied.
  8. # Functions:
  9. # create_chroot
  10. # chroot_prepare_distccd
  11. # chroot_build_packages
  12. # chroot_installpackages_local
  13. # chroot_installpackages
  14. # create_chroot <target_dir> <release> <arch>
  15. #
  16. create_chroot()
  17. {
  18. local target_dir="$1"
  19. local release=$2
  20. local arch=$3
  21. declare -A qemu_binary apt_mirror components
  22. qemu_binary['armhf']='qemu-arm-static'
  23. qemu_binary['arm64']='qemu-aarch64-static'
  24. apt_mirror['stretch']="$DEBIAN_MIRROR"
  25. apt_mirror['buster']="$DEBIAN_MIRROR"
  26. apt_mirror['bullseye']="$DEBIAN_MIRROR"
  27. apt_mirror['bookworm']="$DEBIAN_MIRROR"
  28. apt_mirror['xenial']="$UBUNTU_MIRROR"
  29. apt_mirror['bionic']="$UBUNTU_MIRROR"
  30. apt_mirror['focal']="$UBUNTU_MIRROR"
  31. apt_mirror['hirsute']="$UBUNTU_MIRROR"
  32. apt_mirror['impish']="$UBUNTU_MIRROR"
  33. components['stretch']='main,contrib'
  34. apt_mirror['jammy']="$UBUNTU_MIRROR"
  35. apt_mirror['noble']="$UBUNTU_MIRROR"
  36. components['buster']='main,contrib'
  37. components['bullseye']='main,contrib'
  38. components['bookworm']='main,contrib'
  39. components['sid']='main'
  40. components['xenial']='main,universe,multiverse'
  41. components['bionic']='main,universe,multiverse'
  42. components['focal']='main,universe,multiverse'
  43. components['hirsute']='main,universe,multiverse'
  44. components['impish']='main,universe,multiverse'
  45. components['jammy']='main,universe,multiverse'
  46. components['noble']='main,universe,multiverse'
  47. display_alert "Creating build chroot" "$release/$arch" "info"
  48. local includes="ccache,locales,git,ca-certificates,devscripts,libfile-fcntllock-perl,debhelper,rsync,python3,distcc,apt-utils"
  49. # perhaps a temporally workaround
  50. case $release in
  51. buster|bullseye|focal|hirsute|sid|bookworm)
  52. includes=${includes}",perl-openssl-defaults,libnet-ssleay-perl"
  53. ;;
  54. esac
  55. if [[ $NO_APT_CACHER != yes ]]; then
  56. local mirror_addr="http://localhost:3142/${apt_mirror[${release}]}"
  57. else
  58. local mirror_addr="http://${apt_mirror[${release}]}"
  59. fi
  60. mkdir -p "${target_dir}"
  61. cd "${target_dir}"
  62. debootstrap --variant=buildd \
  63. --components="${components[${release}]}" \
  64. --arch="${arch}" $DEBOOTSTRAP_OPTION \
  65. --foreign \
  66. --include="${includes}" "${release}" "${target_dir}" "${mirror_addr}"
  67. [[ $? -ne 0 || ! -f "${target_dir}"/debootstrap/debootstrap ]] && \
  68. exit_with_error "Create chroot first stage failed"
  69. cp /usr/bin/${qemu_binary[$arch]} "${target_dir}"/usr/bin/
  70. [[ ! -f "${target_dir}"/usr/share/keyrings/debian-archive-keyring.gpg ]] && \
  71. mkdir -p "${target_dir}"/usr/share/keyrings/ && \
  72. cp /usr/share/keyrings/debian-archive-keyring.gpg "${target_dir}"/usr/share/keyrings/
  73. chroot "${target_dir}" /bin/bash -c "/debootstrap/debootstrap --second-stage"
  74. [[ $? -ne 0 || ! -f "${target_dir}"/bin/bash ]] && exit_with_error "Create chroot second stage failed"
  75. create_sources_list "$release" "${target_dir}"
  76. [[ $NO_APT_CACHER != yes ]] && \
  77. echo 'Acquire::http { Proxy "http://localhost:3142"; };' > "${target_dir}"/etc/apt/apt.conf.d/02proxy
  78. cat <<-EOF > "${target_dir}"/etc/apt/apt.conf.d/71-no-recommends
  79. APT::Install-Recommends "0";
  80. APT::Install-Suggests "0";
  81. EOF
  82. [[ -f "${target_dir}"/etc/locale.gen ]] && \
  83. sed -i "s/^# en_US.UTF-8/en_US.UTF-8/" "${target_dir}"/etc/locale.gen
  84. chroot "${target_dir}" /bin/bash -c "locale-gen; update-locale LANG=en_US:en LC_ALL=en_US.UTF-8"
  85. printf '#!/bin/sh\nexit 101' > "${target_dir}"/usr/sbin/policy-rc.d
  86. chmod 755 "${target_dir}"/usr/sbin/policy-rc.d
  87. rm "${target_dir}"/etc/resolv.conf 2>/dev/null
  88. echo "nameserver $NAMESERVER" > "${target_dir}"/etc/resolv.conf
  89. rm "${target_dir}"/etc/hosts 2>/dev/null
  90. echo "127.0.0.1 localhost" > "${target_dir}"/etc/hosts
  91. mkdir -p "${target_dir}"/root/{build,overlay,sources} "${target_dir}"/selinux
  92. if [[ -L "${target_dir}"/var/lock ]]; then
  93. rm -rf "${target_dir}"/var/lock 2>/dev/null
  94. mkdir -p "${target_dir}"/var/lock
  95. fi
  96. chroot "${target_dir}" /bin/bash -c "/usr/sbin/update-ccache-symlinks"
  97. display_alert "Upgrading packages in" "${target_dir}" "info"
  98. chroot "${target_dir}" /bin/bash -c "apt-get -q update; apt-get -q -y upgrade; apt-get clean"
  99. date +%s >"$target_dir/root/.update-timestamp"
  100. case $release in
  101. bullseye|focal|hirsute|sid|bookworm)
  102. chroot "${target_dir}" /bin/bash -c "apt-get install python-is-python3"
  103. ;;
  104. esac
  105. touch "${target_dir}"/root/.debootstrap-complete
  106. display_alert "Debootstrap complete" "${release}/${arch}" "info"
  107. } #############################################################################
  108. # chroot_prepare_distccd <release> <arch>
  109. #
  110. chroot_prepare_distccd()
  111. {
  112. local release=$1
  113. local arch=$2
  114. local dest=/tmp/distcc/${release}-${arch}
  115. declare -A gcc_version gcc_type
  116. gcc_version['stretch']='6.3'
  117. gcc_version['buster']='8.3'
  118. gcc_version['bullseye']='9.2'
  119. gcc_version['bookworm']='10.2'
  120. gcc_version['xenial']='5.4'
  121. gcc_version['bionic']='5.4'
  122. gcc_version['focal']='9.2'
  123. gcc_version['jammy']='10.2'
  124. gcc_version['noble']='13.2'
  125. gcc_version['hirsute']='10.2'
  126. gcc_version['sid']='10.2'
  127. gcc_type['armhf']='arm-linux-gnueabihf-'
  128. gcc_type['arm64']='aarch64-linux-gnu-'
  129. rm -f "${dest}"/cmdlist
  130. mkdir -p "${dest}"
  131. local toolchain_path
  132. #local toolchain_path=$(find_toolchain "${gcc_type[${arch}]}" "== ${gcc_version[${release}]}")
  133. toolchain_path=${toolchain}
  134. ln -sf "${toolchain_path}/${gcc_type[${arch}]}gcc" "${dest}"/cc
  135. echo "${dest}/cc" >> "${dest}"/cmdlist
  136. for compiler in gcc cpp g++ c++; do
  137. echo "${dest}/$compiler" >> "${dest}"/cmdlist
  138. echo "${dest}/${gcc_type[$arch]}${compiler}" >> "${dest}"/cmdlist
  139. ln -sf "${toolchain_path}/${gcc_type[${arch}]}${compiler}" "${dest}/${compiler}"
  140. ln -sf "${toolchain_path}/${gcc_type[${arch}]}${compiler}" "${dest}/${gcc_type[${arch}]}${compiler}"
  141. done
  142. mkdir -p /var/run/distcc/
  143. touch /var/run/distcc/"${release}-${arch}".pid
  144. chown -R distccd /var/run/distcc/
  145. chown -R distccd /tmp/distcc
  146. }
  147. # chroot_build_packages
  148. #
  149. chroot_build_packages()
  150. {
  151. local built_ok=()
  152. local failed=()
  153. if [[ $IMAGE_TYPE == user-built ]]; then
  154. # if user-built image compile only for selected arch/release
  155. target_release="${RELEASE}"
  156. target_arch="${ARCH}"
  157. else
  158. # only make packages for recent releases. There are no changes on older
  159. target_release="stretch bionic buster bullseye bookworm focal hirsute jammy noble sid"
  160. target_arch="armhf arm64"
  161. fi
  162. for release in $target_release; do
  163. for arch in $target_arch; do
  164. display_alert "Starting package building process" "$release/$arch" "info"
  165. local target_dir
  166. target_dir="${EXTER}/cache/buildpkg/${release}-${arch}-v${CHROOT_CACHE_VERSION}"
  167. local distcc_bindaddr="127.0.0.2"
  168. [[ ! -f "${target_dir}"/root/.debootstrap-complete ]] && create_chroot "${target_dir}" "${release}" "${arch}"
  169. [[ ! -f "${target_dir}"/root/.debootstrap-complete ]] && exit_with_error "Creating chroot failed" "${release}/${arch}"
  170. [[ -f /var/run/distcc/"${release}-${arch}".pid ]] && kill "$(<"/var/run/distcc/${release}-${arch}.pid")" > /dev/null 2>&1
  171. chroot_prepare_distccd "${release}" "${arch}"
  172. # DISTCC_TCP_DEFER_ACCEPT=0
  173. DISTCC_CMDLIST=/tmp/distcc/${release}-${arch}/cmdlist TMPDIR=/tmp/distcc distccd --daemon \
  174. --pid-file "/var/run/distcc/${release}-${arch}.pid" --listen $distcc_bindaddr --allow 127.0.0.0/24 \
  175. --log-file "/tmp/distcc/${release}-${arch}.log" --user distccd
  176. local t=$target_dir/root/.update-timestamp
  177. if [[ ! -f ${t} || $(( ($(date +%s) - $(<"${t}")) / 86400 )) -gt 7 ]]; then
  178. display_alert "Upgrading packages" "$release/$arch" "info"
  179. systemd-nspawn -a -q -D "${target_dir}" /bin/bash -c "apt-get -q update; apt-get -q -y upgrade; apt-get clean"
  180. date +%s > "${t}"
  181. fi
  182. for plugin in "${EXTER}"/packages/extras-buildpkgs/*.conf; do
  183. unset package_name package_repo package_ref package_builddeps package_install_chroot package_install_target \
  184. package_upstream_version needs_building plugin_target_dir package_component "package_builddeps_${release}"
  185. source "${plugin}"
  186. # check build condition
  187. if [[ $(type -t package_checkbuild) == function ]] && ! package_checkbuild; then
  188. display_alert "Skipping building $package_name for" "$release/$arch"
  189. continue
  190. fi
  191. local plugin_target_dir=${DEB_STORAGE}/extra/$package_component/
  192. mkdir -p "${plugin_target_dir}"
  193. # check if needs building
  194. local needs_building=no
  195. if [[ -n $package_install_target ]]; then
  196. for f in $package_install_target; do
  197. if [[ -z $(find "${plugin_target_dir}" -name "${f}_*$REVISION*_$arch.deb") ]]; then
  198. needs_building=yes
  199. break
  200. fi
  201. done
  202. else
  203. needs_building=yes
  204. fi
  205. if [[ $needs_building == no ]]; then
  206. display_alert "Packages are up to date" "$package_name $release/$arch" "info"
  207. continue
  208. fi
  209. display_alert "Building packages" "$package_name $release/$arch" "ext"
  210. local dist_builddeps_name="package_builddeps_${release}"
  211. [[ -v $dist_builddeps_name ]] && package_builddeps="${package_builddeps} ${!dist_builddeps_name}"
  212. # create build script
  213. create_build_script
  214. fetch_from_repo "$package_repo" "${EXTER}/cache/sources/extra/$package_name" "$package_ref"
  215. eval systemd-nspawn -a -q \
  216. --capability=CAP_MKNOD -D "${target_dir}" \
  217. --tmpfs=/root/build \
  218. --tmpfs=/tmp:mode=777 \
  219. --bind-ro "${EXTER}"/packages/extras-buildpkgs/:/root/overlay \
  220. --bind-ro "${EXTER}"/cache/sources/extra/:/root/sources /bin/bash -c "/root/build.sh" 2>&1 \
  221. ${PROGRESS_LOG_TO_FILE:+' | tee -a $DEST/${LOG_SUBPATH}/buildpkg.log'}
  222. if [[ ${PIPESTATUS[0]} -eq 2 ]]; then
  223. failed+=("$package_name:$release/$arch")
  224. else
  225. built_ok+=("$package_name:$release/$arch")
  226. fi
  227. mv "${target_dir}"/root/*.deb "${plugin_target_dir}" 2>/dev/null
  228. done
  229. # cleanup for distcc
  230. kill "$(<"/var/run/distcc/${release}-${arch}.pid")"
  231. done
  232. done
  233. if [[ ${#built_ok[@]} -gt 0 ]]; then
  234. display_alert "Following packages were built without errors" "" "info"
  235. for p in ${built_ok[@]}; do
  236. display_alert "$p"
  237. done
  238. fi
  239. if [[ ${#failed[@]} -gt 0 ]]; then
  240. display_alert "Following packages failed to build" "" "wrn"
  241. for p in ${failed[@]}; do
  242. display_alert "$p"
  243. done
  244. fi
  245. } #############################################################################
  246. # create build script
  247. create_build_script ()
  248. {
  249. cat <<-EOF > "${target_dir}"/root/build.sh
  250. #!/bin/bash
  251. export PATH="/usr/lib/ccache:\$PATH"
  252. export HOME="/root"
  253. export DEBIAN_FRONTEND="noninteractive"
  254. export DEB_BUILD_OPTIONS="nocheck noautodbgsym"
  255. export CCACHE_TEMPDIR="/tmp"
  256. # distcc is disabled to prevent compilation issues due
  257. # to different host and cross toolchain configurations
  258. #export CCACHE_PREFIX="distcc"
  259. # uncomment for debug
  260. #export CCACHE_RECACHE="true"
  261. #export CCACHE_DISABLE="true"
  262. export DISTCC_HOSTS="$distcc_bindaddr"
  263. export DEBFULLNAME="$MAINTAINER"
  264. export DEBEMAIL="$MAINTAINERMAIL"
  265. $(declare -f display_alert)
  266. cd /root/build
  267. if [[ -n "${package_builddeps}" ]]; then
  268. # can be replaced with mk-build-deps
  269. deps=()
  270. installed=\$(
  271. dpkg-query -W -f '\${db:Status-Abbrev}|\${binary:Package}\n' '*' 2>/dev/null | \
  272. grep '^ii' | \
  273. awk -F '|' '{print \$2}' | \
  274. cut -d ':' -f 1
  275. )
  276. for packet in $package_builddeps
  277. do
  278. grep -q -x -e "\$packet" <<< "\$installed" || deps+=("\$packet")
  279. done
  280. if [[ \${#deps[@]} -gt 0 ]]; then
  281. display_alert "Installing build dependencies"
  282. apt-get -y -q update
  283. apt-get -y -q \
  284. --no-install-recommends \
  285. --show-progress \
  286. -o DPKG::Progress-Fancy=1 install "\${deps[@]}"
  287. fi
  288. fi
  289. display_alert "Copying sources"
  290. rsync -aq /root/sources/"${package_name}" /root/build/
  291. cd /root/build/"${package_name}"
  292. # copy overlay / "debianization" files
  293. [[ -d "/root/overlay/${package_name}/" ]] && rsync -aq /root/overlay/"${package_name}" /root/build/
  294. # set upstream version
  295. [[ -n "${package_upstream_version}" ]] && debchange --preserve --newversion "${package_upstream_version}" "Import from upstream"
  296. # set local version
  297. # debchange -l~orangepi${REVISION}-${builddate}+ "Custom $VENDOR release"
  298. debchange -l~orangepi"${REVISION}"+ "Custom $VENDOR release"
  299. display_alert "Building package"
  300. dpkg-buildpackage -b -us -j2
  301. if [[ \$? -eq 0 ]]; then
  302. cd /root/build
  303. # install in chroot if other libraries depend on them
  304. if [[ -n "$package_install_chroot" ]]; then
  305. display_alert "Installing packages"
  306. for p in $package_install_chroot; do
  307. dpkg -i \${p}_*.deb
  308. done
  309. fi
  310. display_alert "Done building" "$package_name $release/$arch" "ext"
  311. ls *.deb 2>/dev/null
  312. mv *.deb /root 2>/dev/null
  313. exit 0
  314. else
  315. display_alert "Failed building" "$package_name $release/$arch" "err"
  316. exit 2
  317. fi
  318. EOF
  319. chmod +x "${target_dir}"/root/build.sh
  320. }
  321. # chroot_installpackages_local
  322. #
  323. chroot_installpackages_local()
  324. {
  325. local conf=$EXTER/config/aptly-temp.conf
  326. rm -rf /tmp/aptly-temp/
  327. mkdir -p /tmp/aptly-temp/
  328. aptly -config="${conf}" repo create temp >> "${DEST}"/${LOG_SUBPATH}/install.log
  329. # NOTE: this works recursively
  330. if [[ $EXTERNAL_NEW == prebuilt ]]; then
  331. aptly -config="${conf}" repo add temp "${DEB_ORANGEPI}/extra/${RELEASE}-desktop/" >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
  332. aptly -config="${conf}" repo add temp "${DEB_ORANGEPI}/extra/${RELEASE}-utils/" >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
  333. else
  334. aptly -config="${conf}" repo add temp "${DEB_STORAGE}/extra/${RELEASE}-desktop/" >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
  335. aptly -config="${conf}" repo add temp "${DEB_STORAGE}/extra/${RELEASE}-utils/" >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
  336. fi
  337. # -gpg-key="925644A6"
  338. [[ ! -d /root/.gnupg ]] && mkdir -p /root/.gnupg
  339. aptly -keyring="$EXTER/packages/extras-buildpkgs/buildpkg-public.gpg" -secret-keyring="$EXTER/packages/extras-buildpkgs/buildpkg.gpg" -batch=true -config="${conf}" \
  340. -gpg-key="925644A6" -passphrase="testkey1234" -component=temp -distribution="${RELEASE}" publish repo temp >> "${DEST}"/${LOG_SUBPATH}/install.log
  341. #aptly -config="${conf}" -listen=":8189" serve &
  342. aptly -config="${conf}" -listen=":8189" serve >> "${DEST}"/debug/install.log 2>&1 &
  343. local aptly_pid=$!
  344. cp $EXTER/packages/extras-buildpkgs/buildpkg.key "${SDCARD}"/tmp/buildpkg.key
  345. cat <<-'EOF' > "${SDCARD}"/etc/apt/preferences.d/90-orangepi-temp.pref
  346. Package: *
  347. Pin: origin "localhost"
  348. Pin-Priority: 550
  349. EOF
  350. cat <<-EOF > "${SDCARD}"/etc/apt/sources.list.d/orangepi-temp.list
  351. deb http://localhost:8189/ $RELEASE temp
  352. EOF
  353. chroot_installpackages
  354. kill "${aptly_pid}" >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
  355. } #############################################################################
  356. # chroot_installpackages
  357. #
  358. chroot_installpackages()
  359. {
  360. local install_list=""
  361. for plugin in "${EXTER}"/packages/extras-buildpkgs/*.conf; do
  362. source "${plugin}"
  363. if [[ $(type -t package_checkinstall) == function ]] && package_checkinstall; then
  364. install_list="$install_list $package_install_target"
  365. fi
  366. unset package_install_target package_checkinstall
  367. done
  368. if [[ -n $PACKAGE_LIST_RM ]]; then
  369. install_list=$(sed -r "s/\W($(tr ' ' '|' <<< ${PACKAGE_LIST_RM}))\W/ /g" <<< " ${install_list} ")
  370. install_list="$(echo ${install_list})"
  371. fi
  372. display_alert "Installing extras-buildpkgs" "$install_list"
  373. [[ $NO_APT_CACHER != yes ]] && local apt_extra="-o Acquire::http::Proxy=\"http://${APT_PROXY_ADDR:-localhost:3142}\" -o Acquire::http::Proxy::localhost=\"DIRECT\""
  374. cat <<-EOF > "${SDCARD}"/tmp/install.sh
  375. #!/bin/bash
  376. apt-key add /tmp/buildpkg.key
  377. apt-get $apt_extra -q update
  378. apt-get -q ${apt_extra} --show-progress -o DPKG::Progress-Fancy=1 install -y ${install_list}
  379. apt-get clean
  380. apt-key del "925644A6"
  381. rm /etc/apt/sources.list.d/orangepi-temp.list 2>/dev/null
  382. rm /etc/apt/preferences.d/90-orangepi-temp.pref 2>/dev/null
  383. rm /tmp/buildpkg.key 2>/dev/null
  384. rm -- "\$0"
  385. EOF
  386. chmod +x "${SDCARD}"/tmp/install.sh
  387. chroot "${SDCARD}" /bin/bash -c "/tmp/install.sh" >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
  388. [[ -f ${SDCARD}/etc/hostapd.conf ]] && sed -i "s/^ssid=.*/ssid=OrangePi/" ${SDCARD}/etc/hostapd.conf
  389. } #############################################################################