isar-bootstrap.inc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. # Minimal debian root file system
  2. #
  3. # This software is a part of ISAR.
  4. # Copyright (c) Siemens AG, 2018
  5. #
  6. # SPDX-License-Identifier: MIT
  7. LICENSE = "gpl-2.0"
  8. LIC_FILES_CHKSUM = "file://${LAYERDIR_core}/licenses/COPYING.GPLv2;md5=751419260aa954499f7abaabaa882bbe"
  9. FILESPATH_prepend := "${THISDIR}/files:"
  10. SRC_URI = " \
  11. file://isar-apt.conf \
  12. file://isar-apt-fallback.conf \
  13. file://locale \
  14. file://chroot-setup.sh"
  15. PV = "1.0"
  16. DEBOOTSTRAP ?= "qemu-debootstrap"
  17. ROOTFSDIR = "${WORKDIR}/rootfs"
  18. APTPREFS = "${WORKDIR}/apt-preferences"
  19. APTSRCS = "${WORKDIR}/apt-sources"
  20. APTSRCS_INIT = "${WORKDIR}/apt-sources-init"
  21. DISTRO_BOOTSTRAP_KEYFILES = ""
  22. THIRD_PARTY_APT_KEYFILES = ""
  23. DEPLOY_ISAR_BOOTSTRAP ?= ""
  24. DISTRO_BOOTSTRAP_BASE_PACKAGES = "locales"
  25. DISTRO_BOOTSTRAP_BASE_PACKAGES_append_gnupg = ",gnupg"
  26. DISTRO_BOOTSTRAP_BASE_PACKAGES_append_https-support = "${@https_support(d)}"
  27. DISTRO_VARS_PREFIX ?= ""
  28. inherit deb-dl-dir
  29. python () {
  30. distro_bootstrap_keys = (d.getVar("DISTRO_BOOTSTRAP_KEYS") or "").split()
  31. third_party_apt_keys = (d.getVar("THIRD_PARTY_APT_KEYS") or "").split()
  32. # The cached repo key can be both for bootstrapping and apt package
  33. # installation afterwards. However, debootstrap will include the key into
  34. # the rootfs automatically thus the right place is distro_bootstrap_keys.
  35. if bb.utils.to_boolean(d.getVar('ISAR_USE_CACHED_BASE_REPO')):
  36. own_pub_key = d.getVar("BASE_REPO_KEY")
  37. if own_pub_key:
  38. distro_bootstrap_keys += own_pub_key.split()
  39. for key in distro_bootstrap_keys:
  40. d.appendVar("SRC_URI", " %s" % key)
  41. fetcher = bb.fetch2.Fetch([key], d)
  42. filename = fetcher.localpath(key)
  43. d.appendVar("DISTRO_BOOTSTRAP_KEYFILES", " %s" % filename)
  44. for key in third_party_apt_keys:
  45. d.appendVar("SRC_URI", " %s" % key)
  46. fetcher = bb.fetch2.Fetch([key], d)
  47. filename = fetcher.localpath(key)
  48. d.appendVar("THIRD_PARTY_APT_KEYFILES", " %s" % filename)
  49. }
  50. def aggregate_files(d, file_list, file_out):
  51. import shutil
  52. with open(file_out, "wb") as out_fd:
  53. for entry in file_list:
  54. entry_real = bb.parse.resolve_file(entry, d)
  55. with open(entry_real, "rb") as in_fd:
  56. shutil.copyfileobj(in_fd, out_fd, 1024*1024*10)
  57. out_fd.write("\n".encode())
  58. def parse_aptsources_list_line(source_list_line):
  59. import re
  60. s = source_list_line.strip()
  61. if not s or s.startswith("#"):
  62. return None
  63. type, s = re.split("\s+", s, maxsplit=1)
  64. if type not in ["deb", "deb-src"]:
  65. return None
  66. options = ""
  67. options_match = re.match("\[\s*(\S+=\S+(?=\s))*\s*(\S+=\S+)\s*\]\s+", s)
  68. if options_match:
  69. options = options_match.group(0).strip()
  70. s = s[options_match.end():]
  71. source, s = re.split("\s+", s, maxsplit=1)
  72. if s.startswith("/"):
  73. suite = ""
  74. else:
  75. suite, s = re.split("\s+", s, maxsplit=1)
  76. components = " ".join(s.split())
  77. return [type, options, source, suite, components]
  78. def get_apt_source_mirror(d, aptsources_entry_list):
  79. import re
  80. if bb.utils.to_boolean(d.getVar('ISAR_USE_CACHED_BASE_REPO')):
  81. premirrors = "\S* file://${REPO_BASE_DIR}/${BASE_DISTRO}\n"
  82. else:
  83. premirrors = d.getVar('DISTRO_APT_PREMIRRORS', True) or ""
  84. mirror_list = [entry.split()
  85. for entry in premirrors.split('\\n')
  86. if any(entry)]
  87. for regex, replace in mirror_list:
  88. match = re.search(regex, aptsources_entry_list[2])
  89. if match:
  90. new_aptsources_entry_list = aptsources_entry_list.copy()
  91. new_aptsources_entry_list[2] = re.sub(regex, replace,
  92. aptsources_entry_list[2],
  93. count = 1)
  94. return new_aptsources_entry_list
  95. return aptsources_entry_list
  96. def aggregate_aptsources_list(d, file_list, file_out):
  97. import shutil
  98. with open(file_out, "wb") as out_fd:
  99. for entry in file_list:
  100. entry_real = bb.parse.resolve_file(entry, d)
  101. with open(entry_real, "r") as in_fd:
  102. for line in in_fd:
  103. parsed = parse_aptsources_list_line(line)
  104. if parsed:
  105. parsed = get_apt_source_mirror(d, parsed)
  106. out_fd.write(" ".join(parsed).encode())
  107. else:
  108. out_fd.write(line.encode())
  109. out_fd.write("\n".encode())
  110. out_fd.write("\n".encode())
  111. def get_aptsources_list(d, is_host=False):
  112. if is_host:
  113. apt_sources_list = (d.getVar("HOST_DISTRO_APT_SOURCES", True) or "").split()
  114. else:
  115. apt_sources_list = (d.getVar("DISTRO_APT_SOURCES", True) or "").split()
  116. return apt_sources_list
  117. def generate_distro_sources(d, is_host=False):
  118. apt_sources_list = get_aptsources_list(d, is_host)
  119. for entry in apt_sources_list:
  120. entry_real = bb.parse.resolve_file(entry, d)
  121. with open(entry_real, "r") as in_fd:
  122. for line in in_fd:
  123. parsed = parse_aptsources_list_line(line)
  124. if parsed:
  125. parsed = get_apt_source_mirror(d, parsed)
  126. yield parsed
  127. def get_distro_primary_source_entry(d, is_host=False):
  128. apt_sources_list = get_aptsources_list(d, is_host)
  129. for source in generate_distro_sources(d, is_host):
  130. if source[0] == "deb":
  131. return source[2:]
  132. return ["", "", ""]
  133. def get_distro_have_https_source(d, is_host=False):
  134. return any(source[2].startswith("https://") for source in generate_distro_sources(d, is_host))
  135. def https_support(d, is_host=False):
  136. if get_distro_suite(d, is_host) == "stretch":
  137. return ",apt-transport-https,ca-certificates"
  138. else:
  139. return ",ca-certificates"
  140. def get_distro_needs_https_support(d, is_host=False):
  141. if get_distro_have_https_source(d, is_host):
  142. return "https-support"
  143. else:
  144. return ""
  145. def get_distro_needs_gpg_support(d):
  146. apt_keys = d.getVar("DISTRO_BOOTSTRAP_KEYS") or ""
  147. apt_keys += " " + (d.getVar("THIRD_PARTY_APT_KEYS") or "")
  148. apt_keys += " " + (d.getVar("BASE_REPO_KEY") or "")
  149. if apt_keys != " ":
  150. return "gnupg"
  151. return ""
  152. OVERRIDES_append = ":${@get_distro_needs_gpg_support(d)}"
  153. def get_distro_source(d, is_host):
  154. return get_distro_primary_source_entry(d, is_host)[0]
  155. def get_distro_suite(d, is_host):
  156. return get_distro_primary_source_entry(d, is_host)[1]
  157. def get_distro_components_argument(d, is_host):
  158. components = get_distro_primary_source_entry(d, is_host)[2]
  159. if components and components.strip():
  160. return "--components=" + ",".join(components.split())
  161. else:
  162. return ""
  163. APT_KEYS_DIR = "${WORKDIR}/aptkeys"
  164. DISTRO_BOOTSTRAP_KEYRING = "${WORKDIR}/distro-keyring.gpg"
  165. do_generate_keyrings[cleandirs] = "${APT_KEYS_DIR}"
  166. do_generate_keyrings[dirs] = "${DL_DIR}"
  167. do_generate_keyrings[vardeps] += "DISTRO_BOOTSTRAP_KEYS THIRD_PARTY_APT_KEYS"
  168. do_generate_keyrings() {
  169. if [ -n "${@d.getVar("THIRD_PARTY_APT_KEYFILES", True) or ""}" ]; then
  170. chmod 777 "${APT_KEYS_DIR}"
  171. for keyfile in ${@d.getVar("THIRD_PARTY_APT_KEYFILES", True)}; do
  172. cp "$keyfile" "${APT_KEYS_DIR}"/"$(basename "$keyfile")"
  173. done
  174. fi
  175. if [ -n "${@d.getVar("DISTRO_BOOTSTRAP_KEYFILES", True) or ""}" ]; then
  176. for keyfile in ${@d.getVar("DISTRO_BOOTSTRAP_KEYFILES", True)}; do
  177. sudo apt-key --keyring "${DISTRO_BOOTSTRAP_KEYRING}" add $keyfile
  178. cp "$keyfile" "${APT_KEYS_DIR}"/"$(basename "$keyfile")"
  179. done
  180. fi
  181. }
  182. addtask generate_keyrings before do_build after do_unpack
  183. do_apt_config_prepare[dirs] = "${WORKDIR}"
  184. do_apt_config_prepare[vardeps] += " \
  185. APTPREFS \
  186. ${DISTRO_VARS_PREFIX}DISTRO_APT_PREFERENCES \
  187. DEBDISTRONAME \
  188. APTSRCS \
  189. ${DISTRO_VARS_PREFIX}DISTRO_APT_SOURCES \
  190. DEPLOY_ISAR_BOOTSTRAP \
  191. "
  192. python do_apt_config_prepare() {
  193. apt_preferences_out = d.getVar("APTPREFS", True)
  194. apt_preferences_list = (
  195. d.getVar(d.getVar("DISTRO_VARS_PREFIX") + "DISTRO_APT_PREFERENCES", True) or ""
  196. ).split()
  197. aggregate_files(d, apt_preferences_list, apt_preferences_out)
  198. apt_sources_out = d.getVar("APTSRCS", True)
  199. apt_sources_init_out = d.getVar("APTSRCS_INIT", True)
  200. apt_sources_list = (
  201. d.getVar(d.getVar("DISTRO_VARS_PREFIX") + "DISTRO_APT_SOURCES", True) or ""
  202. ).split()
  203. aggregate_files(d, apt_sources_list, apt_sources_init_out)
  204. aggregate_aptsources_list(d, apt_sources_list, apt_sources_out)
  205. }
  206. addtask apt_config_prepare before do_bootstrap after do_unpack
  207. def get_host_release():
  208. import platform
  209. rel = platform.release()
  210. return rel
  211. do_bootstrap[vardeps] += " \
  212. DISTRO_APT_PREMIRRORS \
  213. ISAR_ENABLE_COMPAT_ARCH \
  214. ${DISTRO_VARS_PREFIX}DISTRO_APT_SOURCES \
  215. "
  216. do_bootstrap[dirs] = "${DEPLOY_DIR_BOOTSTRAP}"
  217. do_bootstrap[depends] = "base-apt:do_cache isar-apt:do_cache_config"
  218. addtask bootstrap before do_build after do_generate_keyrings
  219. isar_bootstrap() {
  220. IS_HOST=""
  221. while true; do
  222. case "$1" in
  223. --host) IS_HOST=1 ;;
  224. -*) bbfatal "$0: invalid option specified: $1" ;;
  225. *) break ;;
  226. esac
  227. shift
  228. done
  229. if [ "${ISAR_ENABLE_COMPAT_ARCH}" = "1" ]; then
  230. if [ -z "${COMPAT_DISTRO_ARCH}" ]; then
  231. bbfatal "${DISTRO_ARCH} does not have a compat arch"
  232. fi
  233. if [ "${@get_distro_suite(d, True)}-${COMPAT_DISTRO_ARCH}" = "stretch-i386" ]; then
  234. bbfatal "compat arch build for stretch-i386 not supported"
  235. fi
  236. fi
  237. debootstrap_args="--verbose --variant=minbase --include=${DISTRO_BOOTSTRAP_BASE_PACKAGES}"
  238. if [ -f "${DISTRO_BOOTSTRAP_KEYRING}" ]; then
  239. debootstrap_args="$debootstrap_args --keyring=${DISTRO_BOOTSTRAP_KEYRING}"
  240. fi
  241. if [ "${ISAR_USE_CACHED_BASE_REPO}" = "1" -a -z "${BASE_REPO_KEY}" ]; then
  242. debootstrap_args="$debootstrap_args --no-check-gpg"
  243. fi
  244. E="${@ isar_export_proxies(d)}"
  245. export IS_HOST debootstrap_args E
  246. sudo rm -rf --one-file-system "${ROOTFSDIR}"
  247. if [ "${IS_HOST}" ];then
  248. deb_dl_dir_import "${ROOTFSDIR}" "${HOST_DISTRO}"
  249. else
  250. deb_dl_dir_import "${ROOTFSDIR}" "${DISTRO}"
  251. fi
  252. sudo -E -s <<'EOSUDO'
  253. set -e
  254. if [ ${IS_HOST} ]; then
  255. ${DEBOOTSTRAP} $debootstrap_args \
  256. ${@get_distro_components_argument(d, True)} \
  257. "${@get_distro_suite(d, True)}" \
  258. "${ROOTFSDIR}" \
  259. "${@get_distro_source(d, True)}" \
  260. ${DISTRO_DEBOOTSTRAP_SCRIPT}
  261. else
  262. ${DEBOOTSTRAP} $debootstrap_args \
  263. --arch="${DISTRO_ARCH}" \
  264. ${@get_distro_components_argument(d, False)} \
  265. "${@get_distro_suite(d, False)}" \
  266. "${ROOTFSDIR}" \
  267. "${@get_distro_source(d, False)}" \
  268. ${DISTRO_DEBOOTSTRAP_SCRIPT}
  269. fi
  270. # Install apt config
  271. mkdir -p "${ROOTFSDIR}/etc/apt/preferences.d"
  272. install -v -m644 "${APTPREFS}" \
  273. "${ROOTFSDIR}/etc/apt/preferences.d/bootstrap"
  274. mkdir -p "${ROOTFSDIR}/etc/apt/sources.list.d"
  275. if [ "${ISAR_USE_CACHED_BASE_REPO}" = "1" ]; then
  276. line="file:///base-apt/${BASE_DISTRO} ${BASE_DISTRO_CODENAME} main"
  277. if [ -z "${BASE_REPO_KEY}" ]; then
  278. line="[trusted=yes] ${line}"
  279. fi
  280. echo "deb ${line}" > "${ROOTFSDIR}/etc/apt/sources.list.d/base-apt.list"
  281. echo "deb-src ${line}" >> "${ROOTFSDIR}/etc/apt/sources.list.d/base-apt.list"
  282. mkdir -p ${ROOTFSDIR}/base-apt
  283. mount --bind ${REPO_BASE_DIR} ${ROOTFSDIR}/base-apt
  284. else
  285. install -v -m644 "${APTSRCS}" \
  286. "${ROOTFSDIR}/etc/apt/sources.list.d/bootstrap.list"
  287. fi
  288. install -v -m644 "${APTSRCS_INIT}" "${ROOTFSDIR}/etc/apt/sources-list"
  289. rm -f "${ROOTFSDIR}/etc/apt/sources.list"
  290. rm -rf "${ROOTFSDIR}/var/lib/apt/lists/"*
  291. mkdir -p "${ROOTFSDIR}/etc/apt/apt.conf.d"
  292. install -v -m644 "${WORKDIR}/isar-apt.conf" \
  293. "${ROOTFSDIR}/etc/apt/apt.conf.d/50isar.conf"
  294. if [ "${@get_distro_needs_gpg_support(d)}" = "gnupg" ]; then
  295. MY_GPGHOME="$(chroot "${ROOTFSDIR}" mktemp -d /tmp/gpghomeXXXXXXXXXX)"
  296. echo "Created temporary directory ${MY_GPGHOME} for gpg-agent"
  297. export GNUPGHOME="${MY_GPGHOME}"
  298. chroot "${ROOTFSDIR}" gpg-agent --daemon
  299. APT_KEY_APPEND="--homedir ${MY_GPGHOME}"
  300. fi
  301. find ${APT_KEYS_DIR}/ -type f | while read keyfile
  302. do
  303. kfn="$(basename $keyfile)"
  304. cp $keyfile "${ROOTFSDIR}/tmp/$kfn"
  305. chroot "${ROOTFSDIR}" /usr/bin/apt-key \
  306. --keyring ${THIRD_PARTY_APT_KEYRING} ${APT_KEY_APPEND} add "/tmp/$kfn"
  307. rm "${ROOTFSDIR}/tmp/$kfn"
  308. done
  309. if [ -d "${MY_GPGHOME}" ]; then
  310. echo "Killing gpg-agent for ${MY_GPGHOME}"
  311. chroot "${ROOTFSDIR}" gpgconf --kill gpg-agent && /bin/rm -rf "${MY_GPGHOME}"
  312. fi
  313. if [ "${@get_distro_suite(d, True)}" = "stretch" ] && [ "${@get_host_release().split('.')[0]}" -lt "4" ]; then
  314. install -v -m644 "${WORKDIR}/isar-apt-fallback.conf" \
  315. "${ROOTFSDIR}/etc/apt/apt.conf.d/55isar-fallback.conf"
  316. fi
  317. # Set locale
  318. install -v -m644 "${WORKDIR}/locale" "${ROOTFSDIR}/etc/locale"
  319. sed -i '/en_US.UTF-8 UTF-8/s/^#//g' "${ROOTFSDIR}/etc/locale.gen"
  320. chroot "${ROOTFSDIR}" /usr/sbin/locale-gen
  321. # setup chroot
  322. install -v -m755 "${WORKDIR}/chroot-setup.sh" "${ROOTFSDIR}/chroot-setup.sh"
  323. "${ROOTFSDIR}/chroot-setup.sh" "setup" "${ROOTFSDIR}"
  324. # update APT
  325. mount --rbind /dev ${ROOTFSDIR}/dev
  326. mount --make-rslave ${ROOTFSDIR}/dev
  327. mount -t proc none ${ROOTFSDIR}/proc
  328. mount --rbind /sys ${ROOTFSDIR}/sys
  329. mount --make-rslave ${ROOTFSDIR}/sys
  330. export DEBIAN_FRONTEND=noninteractive
  331. if [ ${IS_HOST} ]; then
  332. chroot "${ROOTFSDIR}" /usr/bin/dpkg --add-architecture ${DISTRO_ARCH}
  333. fi
  334. if [ "${ISAR_ENABLE_COMPAT_ARCH}" = "1" ]; then
  335. chroot "${ROOTFSDIR}" /usr/bin/dpkg --add-architecture ${COMPAT_DISTRO_ARCH}
  336. fi
  337. chroot "${ROOTFSDIR}" /usr/bin/apt-get update -y
  338. chroot "${ROOTFSDIR}" /usr/bin/apt-get install -y -f
  339. chroot "${ROOTFSDIR}" /usr/bin/apt-get dist-upgrade -y \
  340. -o Debug::pkgProblemResolver=yes
  341. umount -l "${ROOTFSDIR}/dev"
  342. umount -l "${ROOTFSDIR}/proc"
  343. umount -l "${ROOTFSDIR}/sys"
  344. umount -l "${ROOTFSDIR}/base-apt" || true
  345. # Finalize debootstrap by setting the link in deploy
  346. ln -Tfsr "${ROOTFSDIR}" "${DEPLOY_ISAR_BOOTSTRAP}"
  347. EOSUDO
  348. if [ "${IS_HOST}" ];then
  349. deb_dl_dir_export "${ROOTFSDIR}" "${HOST_DISTRO}"
  350. else
  351. deb_dl_dir_export "${ROOTFSDIR}" "${DISTRO}"
  352. fi
  353. }
  354. CLEANFUNCS = "clean_deploy"
  355. clean_deploy() {
  356. rm -f "${DEPLOY_ISAR_BOOTSTRAP}"
  357. }