download_kernel_archives.sh 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  1. #!/usr/bin/env bash
  2. set -euo pipefail
  3. PHASE="${1:-all}"
  4. shift || true
  5. RUN_ALL=false
  6. ANDROID_VERSION="${1:-${ANDROID_VERSION:-}}"
  7. KERNEL_VERSION="${2:-${KERNEL_VERSION:-}}"
  8. OS_PATCH_LEVEL="${3:-${OS_PATCH_LEVEL:-}}"
  9. if [[ "$PHASE" == "prepare" || "$PHASE" == "all" ]]; then
  10. if [[ -z "$ANDROID_VERSION" || -z "$KERNEL_VERSION" || -z "$OS_PATCH_LEVEL" ]]; then
  11. echo "usage: $0 <prepare|parse|sync|post|all> <android_version> <kernel_version> <os_patch_level>" >&2
  12. exit 2
  13. fi
  14. fi
  15. require_env() {
  16. local key="$1"
  17. if [[ -z "${!key:-}" ]]; then
  18. echo "missing env: ${key}" >&2
  19. exit 2
  20. fi
  21. }
  22. fetch_gitiles_text() {
  23. local manifest_base="$1"
  24. local file_name="$2"
  25. curl -LfsS -H "User-Agent: actions-download-kernel" "${manifest_base}${file_name}?format=TEXT" | base64 -d
  26. }
  27. prepare_manifest() {
  28. local formatted_branch="${ANDROID_VERSION}-${KERNEL_VERSION}-${OS_PATCH_LEVEL}"
  29. local manifest_branch="common-${formatted_branch}"
  30. local manifest_ref="refs/heads/${manifest_branch}"
  31. local manifest_base="https://android.googlesource.com/kernel/manifest/+/${manifest_ref}/"
  32. local deprecated=false
  33. if git ls-remote https://android.googlesource.com/kernel/common "${formatted_branch}" 2>/dev/null | rg -q deprecated; then
  34. deprecated=true
  35. fi
  36. if [[ -n "${GITHUB_OUTPUT:-}" ]]; then
  37. printf 'deprecated=%s\n' "$(echo "$deprecated" | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_OUTPUT"
  38. fi
  39. echo "Manifest branch: ${manifest_branch}"
  40. echo "Deprecated: ${deprecated}"
  41. TMP_DIR="$(mktemp -d)"
  42. export TMP_DIR
  43. if [[ -n "${GITHUB_ENV:-}" ]]; then
  44. {
  45. echo "TMP_DIR=${TMP_DIR}"
  46. echo "ANDROID_VERSION=${ANDROID_VERSION}"
  47. echo "KERNEL_VERSION=${KERNEL_VERSION}"
  48. echo "OS_PATCH_LEVEL=${OS_PATCH_LEVEL}"
  49. echo "FORMATTED_BRANCH=${formatted_branch}"
  50. echo "MANIFEST_BRANCH=${manifest_branch}"
  51. echo "MANIFEST_REF=${manifest_ref}"
  52. echo "MANIFEST_BASE=${manifest_base}"
  53. echo "DEPRECATED=${deprecated}"
  54. } >> "$GITHUB_ENV"
  55. fi
  56. local manifest_xml
  57. manifest_xml="$(fetch_gitiles_text "${manifest_base}" default.xml)"
  58. if [[ "$deprecated" == "true" ]]; then
  59. manifest_xml="$(printf '%s' "$manifest_xml" | sed "s/\"${formatted_branch//\//\\/}\"/\"deprecated\\/${formatted_branch//\//\\/}\"/g")"
  60. fi
  61. printf '%s' "$manifest_xml" > "${TMP_DIR}/manifest.xml"
  62. }
  63. parse_manifest() {
  64. require_env TMP_DIR
  65. python3 - <<'PY' > "${TMP_DIR}/parsed.jsonl"
  66. import json
  67. import os
  68. import urllib.parse
  69. import xml.etree.ElementTree as ET
  70. manifest_origin = "https://android.googlesource.com"
  71. with open(os.path.join(os.environ["TMP_DIR"], "manifest.xml"), "r", encoding="utf-8", errors="replace") as f:
  72. root = ET.fromstring(f.read())
  73. remotes = {}
  74. for r in root.findall("remote"):
  75. name = r.get("name")
  76. fetch = (r.get("fetch") or "").strip()
  77. if not name or not fetch:
  78. continue
  79. if fetch.startswith("http://") or fetch.startswith("https://"):
  80. remotes[name] = fetch.rstrip("/")
  81. else:
  82. remotes[name] = urllib.parse.urljoin(manifest_origin + "/", fetch.rstrip("/") + "/").rstrip("/")
  83. default = root.find("default")
  84. def_remote = default.get("remote") if default is not None else None
  85. def_rev = default.get("revision") if default is not None else None
  86. projects = []
  87. link_copy = []
  88. for p in root.findall("project"):
  89. name = p.get("name")
  90. if not name:
  91. continue
  92. path = p.get("path", name)
  93. remote_name = p.get("remote", def_remote)
  94. rev = p.get("revision", def_rev)
  95. base_url = remotes.get(remote_name) if remote_name else None
  96. if not base_url or not rev:
  97. continue
  98. projects.append({"name": name, "path": path, "rev": rev, "base_url": base_url})
  99. for child in list(p):
  100. if child.tag in ("linkfile", "copyfile"):
  101. link_copy.append({"project_path": path, "tag": child.tag, "src": child.get("src"), "dest": child.get("dest")})
  102. for prj in projects:
  103. print(json.dumps({"type": "project", **prj}, separators=(",", ":")))
  104. for op in link_copy:
  105. print(json.dumps({"type": "op", **op}, separators=(",", ":")))
  106. PY
  107. jq -r 'select(.type=="project") | [.name,.path,.rev,.base_url] | @tsv' "${TMP_DIR}/parsed.jsonl" > "${TMP_DIR}/projects.txt"
  108. jq -r 'select(.type=="op") | [.tag,.project_path,(.src//""),(.dest//"")] | @tsv' "${TMP_DIR}/parsed.jsonl" > "${TMP_DIR}/ops.txt"
  109. }
  110. if [[ "$PHASE" == "prepare" ]]; then
  111. prepare_manifest
  112. exit 0
  113. fi
  114. if [[ "$PHASE" == "parse" ]]; then
  115. parse_manifest
  116. exit 0
  117. fi
  118. if [[ "$PHASE" == "all" ]]; then
  119. prepare_manifest
  120. parse_manifest
  121. RUN_ALL=true
  122. PHASE="sync"
  123. fi
  124. require_env TMP_DIR
  125. TARGET_REPO="${GITHUB_REPOSITORY:-}"
  126. GITHUB_TOKEN_VALUE="${GITHUB_TOKEN:-}"
  127. release_json="${TMP_DIR}/release.json"
  128. assets_json="${TMP_DIR}/assets.json"
  129. get_or_create_release() {
  130. if [[ -z "$TARGET_REPO" || -z "$GITHUB_TOKEN_VALUE" ]]; then
  131. return 1
  132. fi
  133. if curl -LfsS -H "Authorization: token ${GITHUB_TOKEN_VALUE}" -H "Accept: application/vnd.github.v3+json" \
  134. "https://api.github.com/repos/${TARGET_REPO}/releases/tags/toolchain-cache" > "$release_json"; then
  135. return 0
  136. fi
  137. curl -LfsS -X POST -H "Authorization: token ${GITHUB_TOKEN_VALUE}" -H "Accept: application/vnd.github.v3+json" \
  138. -H "Content-Type: application/json" \
  139. -d '{"tag_name":"toolchain-cache","name":"Toolchains Mirror Cache","draft":false,"prerelease":false}' \
  140. "https://api.github.com/repos/${TARGET_REPO}/releases" > "$release_json" || true
  141. curl -LfsS -H "Authorization: token ${GITHUB_TOKEN_VALUE}" -H "Accept: application/vnd.github.v3+json" \
  142. "https://api.github.com/repos/${TARGET_REPO}/releases/tags/toolchain-cache" > "$release_json"
  143. }
  144. refresh_assets() {
  145. if [[ -z "$TARGET_REPO" || -z "$GITHUB_TOKEN_VALUE" ]]; then
  146. printf '[]' > "$assets_json"
  147. return 0
  148. fi
  149. if ! curl -LfsS -H "Authorization: token ${GITHUB_TOKEN_VALUE}" -H "Accept: application/vnd.github.v3+json" \
  150. "https://api.github.com/repos/${TARGET_REPO}/releases/tags/toolchain-cache" > "$release_json"; then
  151. get_or_create_release || true
  152. fi
  153. jq -c '.assets // []' "$release_json" > "$assets_json" || printf '[]' > "$assets_json"
  154. }
  155. upload_asset() {
  156. local upload_url_template="$1"
  157. local file_path="$2"
  158. local asset_name="$3"
  159. local upload_url
  160. upload_url="${upload_url_template%%\{*}"
  161. local url="${upload_url}?name=$(python3 - <<PY
  162. import urllib.parse
  163. print(urllib.parse.quote("${asset_name}", safe=""))
  164. PY
  165. )"
  166. local http_code
  167. http_code="$(curl -LsS -X POST \
  168. -H "Authorization: token ${GITHUB_TOKEN_VALUE}" \
  169. -H "Accept: application/vnd.github.v3+json" \
  170. -H "Content-Type: application/octet-stream" \
  171. -H "User-Agent: actions-download-kernel" \
  172. --data-binary "@${file_path}" \
  173. -o /dev/null -w "%{http_code}" \
  174. "$url" || true)"
  175. if [[ "$http_code" == "200" || "$http_code" == "201" || "$http_code" == "422" ]]; then
  176. return 0
  177. fi
  178. return 1
  179. }
  180. download_release_asset() {
  181. local api_url="$1"
  182. local out_path="$2"
  183. curl -LfsS \
  184. -H "Authorization: token ${GITHUB_TOKEN_VALUE}" \
  185. -H "Accept: application/octet-stream" \
  186. -H "User-Agent: actions-download-kernel" \
  187. "$api_url" -o "$out_path"
  188. }
  189. sha256_file() {
  190. sha256sum "$1" | awk '{print $1}'
  191. }
  192. toolchain_label_for() {
  193. local name="$1"
  194. if [[ "$name" == *"clang/host/linux-x86"* ]]; then echo "clang"; return 0; fi
  195. if [[ "$name" == *"prebuilts/rust"* ]]; then echo "rust"; return 0; fi
  196. if [[ "$name" == *"prebuilts/clang-tools"* ]]; then echo "clang-tools"; return 0; fi
  197. if [[ "$name" == *"platform/prebuilts/build-tools"* ]]; then echo "build-tools"; return 0; fi
  198. if [[ "$name" == *"kernel/prebuilts/build-tools"* ]]; then echo "kernel-build-tools"; return 0; fi
  199. if [[ "$name" == *"platform/prebuilts/bazel/linux-x86_64"* ]]; then echo "bazel-linux-x86_64"; return 0; fi
  200. if [[ "$name" == *"platform/prebuilts/jdk/jdk11"* ]]; then echo "jdk11"; return 0; fi
  201. if [[ "$name" == *"toolchain/prebuilts/ndk/r23"* ]]; then echo "ndk-r23"; return 0; fi
  202. if [[ "$name" == *"platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.17-4.8"* ]]; then echo "gcc-glibc2.17-4.8"; return 0; fi
  203. return 1
  204. }
  205. googlesource_urls() {
  206. local repo_url="$1"
  207. local rev="$2"
  208. if [[ "$rev" == refs/* ]]; then
  209. printf '%s/+archive/%s.tar.gz\n' "$repo_url" "$(python3 - <<PY
  210. import urllib.parse
  211. print(urllib.parse.quote("${rev}", safe="/"))
  212. PY
  213. )"
  214. return 0
  215. fi
  216. printf '%s/+archive/%s.tar.gz\n' "$repo_url" "$(python3 - <<PY
  217. import urllib.parse
  218. print(urllib.parse.quote("refs/heads/${rev}", safe="/"))
  219. PY
  220. )"
  221. printf '%s/+archive/%s.tar.gz\n' "$repo_url" "$(python3 - <<PY
  222. import urllib.parse
  223. print(urllib.parse.quote("refs/tags/${rev}", safe="/"))
  224. PY
  225. )"
  226. printf '%s/+archive/%s.tar.gz\n' "$repo_url" "$(python3 - <<PY
  227. import urllib.parse
  228. print(urllib.parse.quote("${rev}", safe="/"))
  229. PY
  230. )"
  231. }
  232. github_urls() {
  233. local repo_url="$1"
  234. local rev="$2"
  235. if [[ "$rev" == refs/* ]]; then
  236. printf '%s/archive/%s.tar.gz\n' "$repo_url" "$rev"
  237. return 0
  238. fi
  239. printf '%s/archive/refs/heads/%s.tar.gz\n' "$repo_url" "$rev"
  240. printf '%s/archive/refs/tags/%s.tar.gz\n' "$repo_url" "$rev"
  241. printf '%s/archive/%s.tar.gz\n' "$repo_url" "$rev"
  242. }
  243. gitlab_urls() {
  244. local repo_url="$1"
  245. local rev="$2"
  246. local base
  247. base="$(basename "$repo_url")"
  248. local qrev
  249. qrev="$(python3 - <<PY
  250. import urllib.parse
  251. print(urllib.parse.quote("${rev}", safe=""))
  252. PY
  253. )"
  254. printf '%s/-/archive/%s/%s-%s.tar.gz\n' "$repo_url" "$qrev" "$base" "$qrev"
  255. }
  256. extract_archive() {
  257. local archive_path="$1"
  258. local dest_dir="$2"
  259. local strip="$3"
  260. mkdir -p "$dest_dir"
  261. if [[ "$strip" == "1" ]]; then
  262. tar -xzf "$archive_path" -C "$dest_dir" --strip-components 1
  263. else
  264. tar -xzf "$archive_path" -C "$dest_dir"
  265. fi
  266. }
  267. try_extract_from_cache() {
  268. local label="$1"
  269. local rev="$2"
  270. local dest_dir="$3"
  271. local strip="$4"
  272. if [[ -z "$TARGET_REPO" || -z "$GITHUB_TOKEN_VALUE" ]]; then
  273. return 1
  274. fi
  275. refresh_assets
  276. local meta_name="${label}-${rev}.cache.json"
  277. local meta_url
  278. meta_url="$(jq -r --arg n "$meta_name" '.[] | select(.name==$n) | .url' "$assets_json" | head -n 1)"
  279. local parts=()
  280. if [[ -n "$meta_url" && "$meta_url" != "null" ]]; then
  281. local meta_path="${TMP_DIR}/${meta_name}"
  282. download_release_asset "$meta_url" "$meta_path" || return 1
  283. mapfile -t parts < <(jq -r '.parts[]?.name' "$meta_path" 2>/dev/null || true)
  284. if [[ "${#parts[@]}" -eq 0 ]]; then
  285. return 1
  286. fi
  287. else
  288. mapfile -t parts < <(jq -r --arg p "${label}-${rev}.tar.gz" '.[] | select(.name|startswith($p)) | .name' "$assets_json" | sort)
  289. if [[ "${#parts[@]}" -eq 0 ]]; then
  290. return 1
  291. fi
  292. fi
  293. local downloaded_parts=()
  294. for part in "${parts[@]}"; do
  295. local url
  296. url="$(jq -r --arg n "$part" '.[] | select(.name==$n) | .url' "$assets_json" | head -n 1)"
  297. if [[ -z "$url" || "$url" == "null" ]]; then
  298. return 1
  299. fi
  300. local out="${TMP_DIR}/${part}"
  301. download_release_asset "$url" "$out" || return 1
  302. downloaded_parts+=("$out")
  303. done
  304. if [[ "${#downloaded_parts[@]}" -eq 1 && "${downloaded_parts[0]}" == *.tar.gz ]]; then
  305. extract_archive "${downloaded_parts[0]}" "$dest_dir" "$strip"
  306. return 0
  307. fi
  308. local merged="${TMP_DIR}/${label}-${rev}.tar.gz"
  309. cat "${downloaded_parts[@]}" > "$merged"
  310. extract_archive "$merged" "$dest_dir" "$strip"
  311. return 0
  312. }
  313. ensure_cached() {
  314. local label="$1"
  315. local rev="$2"
  316. local src_dir="$3"
  317. if [[ -z "$TARGET_REPO" || -z "$GITHUB_TOKEN_VALUE" ]]; then
  318. return 0
  319. fi
  320. get_or_create_release || return 0
  321. local upload_url_template
  322. upload_url_template="$(jq -r '.upload_url // empty' "$release_json")"
  323. if [[ -z "$upload_url_template" ]]; then
  324. return 0
  325. fi
  326. refresh_assets
  327. local meta_name="${label}-${rev}.cache.json"
  328. if jq -e --arg n "$meta_name" '.[] | select(.name==$n)' "$assets_json" >/dev/null 2>&1; then
  329. return 0
  330. fi
  331. local work_dir="${TMP_DIR}/cache-${label}-${rev}"
  332. mkdir -p "$work_dir"
  333. local base_filename="${label}-${rev}.tar.gz"
  334. local archive_path="${work_dir}/${base_filename}"
  335. tar -I "gzip -1" -cf "$archive_path" -C "$src_dir" .
  336. local max_part=$((1900*1024*1024))
  337. local meta_path="${work_dir}/${meta_name}"
  338. if [[ "$(stat -c '%s' "$archive_path")" -le "$max_part" ]]; then
  339. upload_asset "$upload_url_template" "$archive_path" "$base_filename" || true
  340. python3 - <<PY > "$meta_path"
  341. import json, os, hashlib
  342. p="${archive_path}"
  343. h=hashlib.sha256()
  344. with open(p,"rb") as f:
  345. for c in iter(lambda:f.read(1024*1024), b""):
  346. h.update(c)
  347. meta={"format":1,"label":"${label}","rev":"${rev}","archive":"${base_filename}","parts":[{"name":"${base_filename}","size":os.path.getsize(p),"sha256":h.hexdigest()}]}
  348. print(json.dumps(meta,separators=(',',':')))
  349. PY
  350. upload_asset "$upload_url_template" "$meta_path" "$meta_name" || true
  351. return 0
  352. fi
  353. split -b "$max_part" -d -a 2 "$archive_path" "${archive_path}.part"
  354. python3 - <<PY > "$meta_path"
  355. import json, os, glob, hashlib
  356. parts=[]
  357. for p in sorted(glob.glob("${archive_path}.part"*)):
  358. name=os.path.basename(p)
  359. h=hashlib.sha256()
  360. with open(p,"rb") as f:
  361. for c in iter(lambda:f.read(1024*1024), b""):
  362. h.update(c)
  363. parts.append({"name":name,"size":os.path.getsize(p),"sha256":h.hexdigest()})
  364. meta={"format":1,"label":"${label}","rev":"${rev}","archive":"${base_filename}","parts":parts}
  365. print(json.dumps(meta,separators=(',',':')))
  366. PY
  367. for part_path in "${archive_path}.part"*; do
  368. upload_asset "$upload_url_template" "$part_path" "$(basename "$part_path")" || true
  369. done
  370. upload_asset "$upload_url_template" "$meta_path" "$meta_name" || true
  371. }
  372. sync_one_project() {
  373. local name="$1"
  374. local path="$2"
  375. local rev="$3"
  376. local base_url="$4"
  377. local dest_dir
  378. if [[ "$path" == "." || "$path" == "./" ]]; then
  379. dest_dir="$(pwd)"
  380. else
  381. dest_dir="$(pwd)/$path"
  382. fi
  383. mkdir -p "$dest_dir"
  384. local label=""
  385. if label="$(toolchain_label_for "$name" 2>/dev/null)"; then
  386. if try_extract_from_cache "$label" "$rev" "$dest_dir" "0"; then
  387. echo "Synced ${name} -> ${path}"
  388. return 0
  389. fi
  390. fi
  391. local repo_url="${base_url}/${name}"
  392. local urls=()
  393. local strip="0"
  394. if [[ "$base_url" == *github.com* ]]; then
  395. mapfile -t urls < <(github_urls "$repo_url" "$rev")
  396. strip="1"
  397. elif [[ "$base_url" == *googlesource.com* || "$base_url" == *android.googlesource.com* ]]; then
  398. mapfile -t urls < <(googlesource_urls "$repo_url" "$rev")
  399. strip="0"
  400. elif [[ "$base_url" == *git.codelinaro.org* || "$base_url" == *gitlab* ]]; then
  401. mapfile -t urls < <(gitlab_urls "$repo_url" "$rev")
  402. strip="1"
  403. else
  404. echo "[FAIL] ${name} -> ${path} rev=${rev} remote=${base_url} err=no supported remote" >&2
  405. return 1
  406. fi
  407. local tmp_archive="${TMP_DIR}/dl-$(echo -n "${name}-${rev}" | sha256sum | awk '{print $1}').tar.gz"
  408. local ok=false
  409. local last_err=""
  410. for u in "${urls[@]}"; do
  411. if curl -LfsS --retry 5 --connect-timeout 30 -o "$tmp_archive" "$u" >/dev/null 2>&1; then
  412. extract_archive "$tmp_archive" "$dest_dir" "$strip"
  413. ok=true
  414. break
  415. else
  416. last_err="curl failed: $u"
  417. continue
  418. fi
  419. done
  420. if [[ "$ok" != "true" ]]; then
  421. echo "[FAIL] ${name} -> ${path} rev=${rev} remote=${base_url} err=${last_err}" >&2
  422. for u in "${urls[@]}"; do
  423. echo " url=${u}" >&2
  424. done
  425. return 1
  426. fi
  427. if [[ "$path" == "prebuilts/build-tools" ]]; then
  428. if [[ ! -f "${dest_dir}/BUILD.bazel" ]]; then
  429. echo "[FAIL] ${name} -> ${path} rev=${rev} remote=${base_url} err=BUILD.bazel missing after extract" >&2
  430. return 1
  431. fi
  432. if ! rg -q "py_toolchain" "${dest_dir}/BUILD.bazel"; then
  433. echo "[FAIL] ${name} -> ${path} rev=${rev} remote=${base_url} err=BUILD.bazel missing py_toolchain" >&2
  434. return 1
  435. fi
  436. fi
  437. if [[ -n "$label" ]]; then
  438. ensure_cached "$label" "$rev" "$dest_dir" || true
  439. fi
  440. echo "Synced ${name} -> ${path}"
  441. }
  442. projects_jsonl="${TMP_DIR}/parsed.jsonl"
  443. project_lines="${TMP_DIR}/projects.txt"
  444. op_lines="${TMP_DIR}/ops.txt"
  445. if [[ ! -f "$project_lines" || ! -f "$op_lines" ]]; then
  446. jq -r 'select(.type=="project") | [.name,.path,.rev,.base_url] | @tsv' "$projects_jsonl" > "$project_lines"
  447. jq -r 'select(.type=="op") | [.tag,.project_path,(.src//""),(.dest//"")] | @tsv' "$projects_jsonl" > "$op_lines"
  448. fi
  449. if [[ "$PHASE" != "post" ]]; then
  450. fail_count=0
  451. while IFS=$'\t' read -r name path rev base_url; do
  452. if ! sync_one_project "$name" "$path" "$rev" "$base_url"; then
  453. fail_count=$((fail_count+1))
  454. fi
  455. done < "$project_lines"
  456. if [[ "$fail_count" -ne 0 ]]; then
  457. exit 1
  458. fi
  459. fi
  460. if [[ "$PHASE" == "sync" && "$RUN_ALL" != "true" ]]; then
  461. exit 0
  462. fi
  463. if [[ "$PHASE" != "post" && "$RUN_ALL" != "true" ]]; then
  464. exit 0
  465. fi
  466. build_tools_dir="$(pwd)/prebuilts/build-tools"
  467. expected="${build_tools_dir}/path/linux-x86/python3"
  468. if [[ ! -e "$expected" ]]; then
  469. mkdir -p "$(dirname "$expected")"
  470. candidate="$(find "$build_tools_dir" -type f -name python3 -print -quit 2>/dev/null || true)"
  471. if [[ -z "$candidate" ]]; then
  472. candidate="$(command -v python3 || true)"
  473. fi
  474. if [[ -n "$candidate" ]]; then
  475. rm -f "$expected" 2>/dev/null || true
  476. ln -s "$(python3 - <<PY
  477. import os
  478. print(os.path.relpath("${candidate}", os.path.dirname("${expected}")))
  479. PY
  480. )" "$expected" || true
  481. fi
  482. fi
  483. while IFS=$'\t' read -r tag project_path src dest; do
  484. if [[ -z "$src" || -z "$dest" ]]; then
  485. continue
  486. fi
  487. src_path="$(pwd)/${project_path}/${src}"
  488. dest_path="$(pwd)/${dest}"
  489. mkdir -p "$(dirname "$dest_path")"
  490. if [[ "$tag" == "linkfile" ]]; then
  491. rm -f "$dest_path" 2>/dev/null || true
  492. ln -s "$(python3 - <<PY
  493. import os
  494. print(os.path.relpath("${src_path}", os.path.dirname("${dest_path}")))
  495. PY
  496. )" "$dest_path"
  497. else
  498. cp -p "$src_path" "$dest_path"
  499. fi
  500. done < "$op_lines"
  501. exit 0