qi.in 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612
  1. #! /bin/sh -
  2. # Copyright (C) 2016-2020 Matias Fonzo <selk@dragora.org>
  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. # EXIT STATUS
  17. # 0 = Successful completion
  18. # 1 = Minor common errors (e.g: help usage, support not available)
  19. # 2 = Command execution error
  20. # 3 = Integrity check error for compressed files
  21. # 4 = File empty, not regular, or expected
  22. # 5 = Empty or not defined variable
  23. # 6 = Package already installed
  24. # 10 = Network manager error
  25. PROGRAM="${0##*/}"
  26. LC_ALL=C; # Override locale settings.
  27. umask 022; # Remove write permission for group and other.
  28. export LC_ALL
  29. ### Functions
  30. usage()
  31. {
  32. printf '%s' \
  33. "Usage: $PROGRAM COMMAND [OPTIONS] [FILE]...
  34. "'A simple but well-integrated package manager.
  35. List of commands:
  36. warn Warn about files that will be installed
  37. install Install packages
  38. remove Remove packages
  39. upgrade Upgrade packages
  40. extract Extract packages for debugging purposes
  41. create Create a .tlz package from directory
  42. build Build packages using recipe names
  43. order Resolve build order through .order files
  44. Options when installing, removing, or upgrading software packages:
  45. -f, --force Force upgrade of pre-existing packages
  46. -k, --keep Keep package directory when remove/upgrade
  47. -p, --prune Prune conflicts
  48. -P, --packagedir=<dir> Set directory for package installations
  49. -t, --targetdir=<dir> Set target directory for symbolic links
  50. -r, --rootdir=<dir> Use the fully qualified named directory as
  51. the root directory for all qi operations
  52. Note: the target directory and the package
  53. directory will be relative to the specified
  54. directory, excepting the graft log file
  55. Options when building software packages using recipes:
  56. -a, --architecture Set architecture name for the package
  57. -j, --jobs Parallel jobs for the compiler
  58. -k, --keep Keep ${srcdir} or ${destdir} when build
  59. -S, --skip-questions Skip questions on completed recipes
  60. -1, --increment Increment release number (${release} + 1)
  61. -n, --no-package Do not create a .tlz package
  62. -i, --install Install package after the build
  63. -u, --upgrade Upgrade package after the build
  64. -o, --outdir=<dir> Where the packages produced will be written
  65. -w, --worktree=<dir> Where archives, patches, recipes are expected
  66. -s, --sourcedir=<dir> Where compressed sources will be found
  67. Other options:
  68. -N, --no-rc Do not read the configuration file
  69. -v, --verbose Be verbose (an extra -v gives more)
  70. -L, --show-location Print default directory locations and exit
  71. -h, --help Display this help and exit
  72. -V, --version Output version information and exit
  73. Some influential environment variables:
  74. TMPDIR Temporary directory for sources
  75. QICFLAGS C compiler flags
  76. QICXXFLAGS C++ compiler flags
  77. QILDFLAGS Linker flags
  78. SOURCE_DATE_EPOCH Last modification time for created packages
  79. When FILE is -, read standard input.
  80. Exit status: 0 for a normal exit, 1 for minor common errors (help usage,
  81. support not available, etc), 2 to indicate a command execution error;
  82. 3 for integrity check error on compressed files, 4 for empty, not
  83. regular, or expected files, 5 for empty or not defined variables,
  84. 6 when a package already exist, 10 for network manager errors.
  85. Qi home page: https://www.dragora.org
  86. '
  87. }
  88. warn()
  89. {
  90. printf '%s\n' "$@" 1>&2
  91. }
  92. is_readable()
  93. {
  94. if test -e "$1"
  95. then
  96. if ! test -r "$1"
  97. then
  98. echo "${PROGRAM}: cannot read ${1}: Permission denied" 1>&2
  99. return 1
  100. fi
  101. else
  102. echo "${PROGRAM}: cannot access ${1}: No such file or directory" 1>&2
  103. return 1
  104. fi
  105. }
  106. # Portable alternative to the file operator -nt (among shells)
  107. is_newer()
  108. {
  109. if test -n "$(find "$1" -prune -newer "$2" -print)"
  110. then
  111. return 0
  112. fi
  113. return 1
  114. }
  115. # Determine whether $2 matches pattern $1
  116. fnmatch()
  117. {
  118. case $2 in
  119. $1)
  120. return 0
  121. ;;
  122. *)
  123. return 1
  124. ;;
  125. esac
  126. }
  127. chkstatus_or_exit()
  128. {
  129. status=$?
  130. if test $status -ne 0
  131. then
  132. echo "^ Return status = $status" 1>&2
  133. exit "${1-2}"; # If not given, defaults to 2
  134. fi
  135. unset -v status
  136. }
  137. readconfig()
  138. {
  139. if test "${_readconfig:-readconfig}" = readconfig
  140. then
  141. is_readable "$HOME/.qirc" 2> /dev/null && _rcfile="$HOME/.qirc";
  142. echo "Importing configuration file from \`${_rcfile}' ..."
  143. . "$_rcfile" || chkstatus_or_exit 5
  144. fi
  145. unset -v _readconfig
  146. }
  147. ### Mode functions
  148. mode_build()
  149. {
  150. recipe=$1
  151. echo ""
  152. echo "{@} Building \"${recipe}\" ..."
  153. # A recipe is any valid regular file. Qi sets priorities for reading
  154. # a recipe, the order in which qi looks for a recipe is:
  155. #
  156. # 1. Current working directory.
  157. #
  158. # 2. If the specified path name does not contain "recipe" as the last
  159. # component. Qi will complete it by adding "recipe" to the path
  160. # name.
  161. #
  162. # 3. If the recipe is not in the current working directory, it will be
  163. # searched under '${worktree}/recipes'. The last component will be
  164. # completed adding "recipe" to the specified path name.
  165. if test ! -f "$recipe"
  166. then
  167. if test -f "${recipe}/recipe"
  168. then
  169. recipe="${recipe}/recipe"
  170. elif test -f "${worktree}/recipes/${recipe}/recipe"
  171. then
  172. recipe="${worktree}/recipes/${recipe}/recipe"
  173. fi
  174. fi
  175. test -f "$recipe" || {
  176. warn "\`${recipe}' is not a regular file."
  177. return 4
  178. }
  179. # Complain if the file name is not "recipe"
  180. if test "${recipe##*/}" != recipe
  181. then
  182. warn "\`${recipe}' is not a valid recipe name."
  183. return 4
  184. fi
  185. # Start preparations to import the recipe
  186. # Get working directory and base name of the recipe
  187. CWD="$(dirname -- "$recipe")"
  188. recipe="$(basename -- "$recipe")"
  189. # Check readability for load the recipe on success
  190. is_readable "${CWD}/$recipe" || exit 4
  191. # Re-create external directories
  192. mkdir -p -- "${worktree}/archive" \
  193. "${worktree}/patches" \
  194. "${worktree}/recipes" \
  195. "$tardir"
  196. # Variables treatment for the current and the next recipe.
  197. #
  198. # Unset special variables that can only be predefined in
  199. # the recipe and does not come from '${sysconfdir}/qirc'
  200. unset -v \
  201. srcdir destdir pkgname pkgversion pkgcategory program version release \
  202. fetch description homepage license replace full_pkgname \
  203. CFLAGS CXXFLAGS LDFLAGS
  204. # The following variables must be restored, later
  205. save_arch="${save_arch:=$arch}"
  206. save_jobs="${save_jobs:=$jobs}"
  207. save_outdir="${save_outdir:=$outdir}"
  208. save_opt_nopkg="${save_opt_nopkg:=$opt_nopkg}"
  209. # Reset variable values in case of return
  210. arch="$save_arch"
  211. jobs="$save_jobs"
  212. outdir="$save_outdir"
  213. opt_nopkg="$save_opt_nopkg"
  214. # The following variables cannot be redefined on the recipe
  215. readonly worktree netget rsync
  216. # Import the recipe
  217. . "${CWD}/$recipe"
  218. chkstatus_or_exit
  219. # Check for required variables
  220. if test -z "$program"
  221. then
  222. warn "${recipe}: The variable 'program' is not defined."
  223. exit 5
  224. fi
  225. if test -z "$version"
  226. then
  227. warn "${recipe}: The variable 'version' is not defined."
  228. exit 5
  229. fi
  230. if test -z "$release"
  231. then
  232. warn "${recipe}: The variable 'release' is not defined."
  233. exit 5
  234. fi
  235. # Pre-settings before to start building
  236. # Increment the release number if the option was given
  237. if test "$opt_incr_release" = opt_incr_release
  238. then
  239. release=$(( release + 1 ))
  240. fi
  241. # Allow the dot as definition for 'tardir'
  242. if test "$tardir" = .
  243. then
  244. tardir="$CWD"
  245. fi
  246. # Set default values for the following special variables
  247. pkgname="${pkgname:=$program}"
  248. pkgversion="${pkgversion:=$version}"
  249. srcdir="${srcdir:=${program}-$version}"
  250. destdir="${destdir:=${TMPDIR}/package-$pkgname}"
  251. # If 'pkgcategory' has been defined, prefix it using the "at" symbol
  252. if test -n "$pkgcategory"
  253. then
  254. pkgcategory="@${pkgcategory}"
  255. fi
  256. # Compose the full package name
  257. full_pkgname="${full_pkgname:=${pkgname}_${pkgversion}_${arch}-${release}${pkgcategory}}"
  258. # Use 'arch' as suffix for 'outdir' to have a well-organized package output
  259. outdir="${outdir}/${arch}"
  260. # If a package is going to be created the existence of a
  261. # previous build will be detected and reported. Under normal
  262. # conditions the recipe is built as long as it is newer than
  263. # the produced package, if not, we warn to the user about it.
  264. # Rebuilding the package is possible (through the force ;-)
  265. if test "$opt_nopkg" != opt_nopkg && \
  266. { test "$opt_force" != opt_force && \
  267. test -e "${outdir}/${full_pkgname}.tlz" ; }
  268. then
  269. if is_newer "${CWD}/$recipe" "${outdir}/${full_pkgname}.tlz"
  270. then
  271. warn \
  272. "" \
  273. "The recipe is more RECENT than the detected package:" \
  274. "" \
  275. "$( stat -c "%y %n" "${CWD}/$recipe" )" \
  276. "$( stat -c "%y %n" "${outdir}/${full_pkgname}.tlz" )" \
  277. "" \
  278. " This recipe will be processed ..." \
  279. ""
  280. elif test -e "${CWD}/post-install" && \
  281. is_newer "${CWD}/post-install" "${CWD}/$recipe"
  282. then
  283. warn \
  284. "" \
  285. "The post-install script is more RECENT than the recipe:" \
  286. "" \
  287. "$( stat -c "%y %n" "${CWD}/post-install" )" \
  288. "$( stat -c "%y %n" "${CWD}/$recipe" )" \
  289. "" \
  290. " The recipe will be re-processed ..." \
  291. ""
  292. touch "${CWD}/$recipe"
  293. elif test "$opt_skipqsts" = opt_skipqsts
  294. then
  295. warn "Recipe for '${full_pkgname}.tlz': Ignored." ""
  296. return 0
  297. else
  298. warn \
  299. "" \
  300. "This recipe ALREADY produced a package:" \
  301. "$( stat -c "%y %n" "${outdir}/${full_pkgname}.tlz" )" \
  302. "" \
  303. "The recipe is still OLDER than the produced package:" \
  304. "$( stat -c "%y %n" "${CWD}/$recipe" )" \
  305. "" \
  306. " Probably nothing has changed." \
  307. ""
  308. # In non-interactive mode, the user is asked about
  309. # rebuilding the package. In interactive mode,
  310. # the user need to pass the option explicitly
  311. if test ! -t 0
  312. then
  313. printf '%s\n' \
  314. "Do you want to rebuild this package?" \
  315. "1) Yes, built it" \
  316. "2) No, skip it [default]" \
  317. "3) Resume, skipping completed recipes" \
  318. "Enter an option number:" > /dev/tty
  319. IFS= read -r ANSWER < /dev/tty || exit 2;
  320. case $ANSWER in
  321. 1*)
  322. echo "$ANSWER" > /dev/tty
  323. unset -v ANSWER
  324. ;;
  325. 3*)
  326. unset -v ANSWER
  327. echo "=== Building UNPROCESSED/MODIFIED recipes ..." > /dev/tty
  328. echo ""
  329. opt_skipqsts=opt_skipqsts
  330. readonly opt_skipqsts
  331. return 0
  332. ;;
  333. *)
  334. unset -v ANSWER
  335. echo "Recipe for '${full_pkgname}.tlz': Cancelled." > /dev/tty
  336. echo ""
  337. return 0
  338. ;;
  339. esac
  340. else
  341. warn "Use the --force option to reprocess ${CWD}/$recipe." ""
  342. return 6;
  343. fi
  344. fi
  345. fi
  346. # Fetch remote sources
  347. echo "=== Fetching remote sources if needed ..."
  348. if test -n "$fetch"
  349. then
  350. for origin in $fetch
  351. do
  352. _source="${origin##*/}"
  353. echo "=== Looking for \"$_source\" ..."
  354. echo "=== Verifying checksum file \`${tardir}/${_source}.sha256'"
  355. if test -e "${tardir}/${_source}.sha256"
  356. then
  357. ( cd -- "$tardir" && sha256sum - ) < "${tardir}/${_source}.sha256"
  358. chkstatus_or_exit
  359. continue;
  360. else
  361. warn "${_source}.sha256: Checksum file does not exist, yet."
  362. fi
  363. # Download source or resume if allowed
  364. if test ! -e "${tardir}/$_source"
  365. then
  366. warn " Can't find \"${_source}\" at \"${tardir}\";" \
  367. "attempting to get it from ${origin%/*} ..."
  368. fi
  369. case $origin in
  370. rsync://*)
  371. (
  372. cd -- "$tardir" && $rsync "$origin" || exit $?
  373. sha256sum "$_source" > "${_source}.sha256"
  374. ); chkstatus_or_exit 10
  375. ;;
  376. *://*)
  377. (
  378. cd -- "$tardir" && $netget "$origin" || exit $?
  379. sha256sum "$_source" > "${_source}.sha256"
  380. ); chkstatus_or_exit 10
  381. ;;
  382. *)
  383. warn "${PROGRAM}: Unrecognized protocol for ${origin}."
  384. exit 4
  385. esac
  386. done
  387. unset -v origin _source
  388. else
  389. warn "${recipe}: The variable 'fetch' is empty."
  390. fi
  391. # Prepare special directories for build the source,
  392. # the destination and the output of the package
  393. echo "=== Preparing directories ..."
  394. if test -z "$keep_srcdir"
  395. then
  396. if test -e "${TMPDIR}/$srcdir"
  397. then
  398. rm -r -- "${TMPDIR:?}/$srcdir" || chkstatus_or_exit
  399. echo "removed directory: '${TMPDIR}/$srcdir'"
  400. fi
  401. else
  402. warn \
  403. "WARNING: The variable 'keep_srcdir' has been set (${keep_srcdir})."
  404. fi
  405. if test -z "$keep_destdir"
  406. then
  407. if test -e "$destdir"
  408. then
  409. rm -r -- "$destdir" || chkstatus_or_exit
  410. echo "removed directory: '$destdir'"
  411. fi
  412. mkdir -p -- "$destdir" || chkstatus_or_exit
  413. echo "mkdir: created directory '$destdir'"
  414. fi
  415. if test ! -e "$outdir"
  416. then
  417. mkdir -p -- "$outdir" || chkstatus_or_exit
  418. echo "mkdir: created directory '$outdir'"
  419. fi
  420. echo "=== Changing to '${TMPDIR}' ..."
  421. cd -- "$TMPDIR" || chkstatus_or_exit
  422. # Set trap before to run the build() function in order
  423. # to catch the return status, exit code 2 if fails
  424. trap 'chkstatus_or_exit 2' EXIT HUP INT QUIT ABRT TERM
  425. # Determine if the debugging indicators of the shell should be
  426. # retained, assuming that it has been previously passed
  427. case $- in *x*)
  428. _xtrace_flag=_xtrace_flag_is_set ;;
  429. esac
  430. echo "=== Running the 'build' function ..."
  431. build
  432. unset -f build
  433. # Turn off possible shell flags coming from the recipe
  434. set +e
  435. if test "${_xtrace_flag:+$_xtrace_flag}" != _xtrace_flag_is_set
  436. then
  437. set +x
  438. fi
  439. # Reset given signals
  440. trap - EXIT HUP INT QUIT ABRT TERM
  441. # If 'destdir' is empty, the package won't be created
  442. if rmdir -- "$destdir" 2> /dev/null
  443. then
  444. warn "The package \"${full_pkgname}.tlz\" won't be created. 'destdir' is empty."
  445. opt_nopkg=opt_nopkg
  446. fi
  447. # Create (make) the package
  448. if test "$opt_nopkg" != opt_nopkg
  449. then
  450. # Edit the recipe when 'release' is incremented
  451. if test "$opt_incr_release" = opt_incr_release
  452. then
  453. echo ",s/^\\(release\\)=.*/\\1=${release}/"$'\nw' | \
  454. ed "${CWD}/$recipe" || chkstatus_or_exit
  455. fi
  456. mkdir -p -- "${destdir}/var/lib/qi" || chkstatus_or_exit
  457. # Include a copy of the recipe into the package
  458. cp -p "${CWD}/$recipe" \
  459. "${destdir}/var/lib/qi/${full_pkgname}.recipe" && \
  460. chmod 644 "${destdir}/var/lib/qi/${full_pkgname}.recipe"
  461. chkstatus_or_exit
  462. # Detect post-install script for inclusion
  463. if test -f "${CWD}/post-install"
  464. then
  465. echo "${CWD}/post-install: Detected."
  466. cp -p "${CWD}/post-install" \
  467. "${destdir}/var/lib/qi/${full_pkgname}.sh" && \
  468. chmod 644 "${destdir}/var/lib/qi/${full_pkgname}.sh"
  469. chkstatus_or_exit
  470. fi
  471. # Detect declared package name(s) for later replacement
  472. if test -n "$replace"
  473. then
  474. warn \
  475. "=== The following package names has been declared for replacement:" \
  476. "$replace"
  477. : > "${destdir}/var/lib/qi/${full_pkgname}.replace"
  478. for item in $replace
  479. do
  480. printf '%s\n' "$replace" >> \
  481. "${destdir}/var/lib/qi/${full_pkgname}.replace"
  482. done
  483. unset -v item
  484. fi
  485. # Create (external) meta file for package information,
  486. # make a copy of it for the package database
  487. echo "=== Creating meta file ${full_pkgname}.tlz.txt ..."
  488. do_meta > "${outdir}/${full_pkgname}.tlz.txt" || chkstatus_or_exit
  489. cp -p "${outdir}/${full_pkgname}.tlz.txt" \
  490. "${destdir}/var/lib/qi/${full_pkgname}.txt" || chkstatus_or_exit
  491. # Produce the package
  492. cd -- "$destdir" && mode_create "${outdir}/${full_pkgname}.tlz"
  493. fi
  494. # Back to the current working directory
  495. cd -- "$CWD" || chkstatus_or_exit
  496. echo "=== Deleting 'srcdir', 'destdir' ..."
  497. if test "$opt_keep" != opt_keep
  498. then
  499. if test -z "$keep_srcdir"
  500. then
  501. srcdir="${srcdir%%/*}"; # Directory name without parents.
  502. if test -e "${TMPDIR}/$srcdir"
  503. then
  504. rm -r -- "${TMPDIR:?}/$srcdir" || chkstatus_or_exit
  505. echo "removed directory: '${TMPDIR}/$srcdir'"
  506. fi
  507. fi
  508. if test -z "$keep_destdir"
  509. then
  510. if test -e "$destdir"
  511. then
  512. rm -r -- "$destdir" || chkstatus_or_exit
  513. echo "removed directory: '$destdir'"
  514. fi
  515. fi
  516. else
  517. warn \
  518. " Request via --keep to preserve 'srcdir' or 'destdir' has been made:" \
  519. "${TMPDIR}/$srcdir" \
  520. "$destdir"
  521. fi
  522. # Install or upgrade the package after build
  523. if test "$opt_nopkg" != opt_nopkg
  524. then
  525. if test "$opt_install" = opt_install
  526. then
  527. mode_install "${outdir}/${full_pkgname}.tlz"
  528. elif test "$opt_upgrade" = opt_upgrade
  529. then
  530. mode_upgrade "${outdir}/${full_pkgname}.tlz"
  531. fi
  532. fi
  533. echo "{@} All done for \"${CWD}/${recipe}\"."
  534. echo ""
  535. }
  536. mode_create()
  537. {
  538. directory="$(dirname -- "$1")"
  539. # Perform sanity checks
  540. if ! fnmatch '/?*' "$directory"
  541. then
  542. warn "${PROGRAM}: Output directory \`${directory}' is not fully qualified"
  543. exit 4
  544. fi
  545. is_readable "$directory" || exit 4
  546. name="$(basename -- "$1")"
  547. echo ""
  548. echo "{#} Creating package name \`${name}' ..."
  549. if test "$name" = "${name%.tlz}"
  550. then
  551. warn "Package format '$name' not supported." \
  552. "It should be \"name_version_architecture-release[@pkgcategory].tlz\""
  553. exit 4
  554. fi
  555. # Pass extra options for tarlz(1)
  556. if test -n "$SOURCE_DATE_EPOCH"
  557. then
  558. tarlz_opts="--mtime=@${SOURCE_DATE_EPOCH}"
  559. fi
  560. ( umask 022 ; tarlz --solid -9 $tarlz_opts -cvf - -- * ) \
  561. > "${directory}/$name"
  562. chkstatus_or_exit 3
  563. unset -v tarlz_opts
  564. ( cd -- "$directory" && sha256sum "$name" > "${name}.sha256" )
  565. chkstatus_or_exit 4
  566. echo "{#} Package \"${name}\" created on ${directory}."
  567. # Remove used variables
  568. unset -v directory name
  569. echo ""
  570. }
  571. mode_remove()
  572. {
  573. expunge="${packagedir}/$(basename -- "$1" .tlz)"
  574. echo ""
  575. echo "<<< Removing package \`$rootdir${expunge}' ..."
  576. # Complain if the package directory does not exist
  577. test -e "$rootdir${expunge}" || {
  578. warn "Package directory '$rootdir${expunge}' does not exist."
  579. return 4
  580. }
  581. # Complain if the package directory cannot be well-read
  582. is_readable "$rootdir${expunge}" || exit 4
  583. # Remove package from Graft control
  584. # Scan for possible conflicts, stop if arise
  585. if test "$opt_prune" != opt_prune
  586. then
  587. echo "=== Checking for possible conflicts ..."
  588. if graft -d -n $graft_r -t "$targetdir" "$expunge" 2>&1 | \
  589. grep "^CONFLICT"
  590. then
  591. warn "" \
  592. " A conflict occurred during uninstallation;" \
  593. "Unless the --prune option is given, this package will be PRESERVED."
  594. return 6;
  595. fi
  596. fi
  597. # Remove objects (files, links or directories) from the target
  598. # directory that are in conflict with the package directory
  599. echo "=== Pruning any conflict ..."
  600. graft -p -D -u $graft_r -t "$targetdir" "$expunge"
  601. chkstatus_or_exit 2
  602. echo "=== Disabling links ..."
  603. graft -d -D -u $graft_v $graft_r -t "$targetdir" "$expunge"
  604. chkstatus_or_exit 2
  605. # Delete package directory
  606. if test "$opt_keep" != opt_keep
  607. then
  608. echo "=== Deleting package directory, if exists as such ..."
  609. if test -e "${rootdir}$expunge"
  610. then
  611. rm -r -- "${rootdir}$expunge" || chkstatus_or_exit
  612. echo "removed directory: '${rootdir}$expunge'"
  613. fi
  614. fi
  615. # Remove used variables
  616. unset -v expunge
  617. echo ""
  618. }
  619. mode_install()
  620. {
  621. # Complain if the package cannot be well-read
  622. is_readable "$1" || exit 4
  623. # Complain if the package does not end in .tlz
  624. if ! fnmatch '*.tlz' "$1"
  625. then
  626. warn "\`${1}' does not end in .tlz"
  627. return 4
  628. fi
  629. # Make preparations to install the package
  630. # Get the filename
  631. name="$(basename -- "$1" .tlz)"
  632. echo ""
  633. echo ">>> Installing package \`${name}' ..."
  634. echo "=== Checking tarball integrity ..."
  635. tarlz --missing-crc -tf "$1" > /dev/null
  636. chkstatus_or_exit 3
  637. # To accept random directory from the upgrade mode
  638. _packagedir="$2"
  639. _packagedir="${_packagedir:=$packagedir}"
  640. # Create package directory using 'name'
  641. if ! test -d "$rootdir${_packagedir}/$name"
  642. then
  643. mkdir -p -- "$rootdir${_packagedir}/$name" || chkstatus_or_exit
  644. echo "mkdir: created directory '$rootdir${_packagedir}/$name'"
  645. fi
  646. # Scan for possible conflicts, stop if arise
  647. if test "$opt_prune" != opt_prune
  648. then
  649. echo "=== Checking for possible conflicts ..."
  650. if graft -i -n $graft_r -t "$targetdir" "${_packagedir}/$name" 2>&1 | \
  651. grep "^CONFLICT"
  652. then
  653. warn "" \
  654. " A conflict occurred during installation;" \
  655. "Unless the --prune option is given, this package won't be LINKED."
  656. return 6;
  657. fi
  658. fi
  659. echo "=== Decompressing package ..."
  660. ( cd -- "$rootdir${_packagedir}/$name" && tarlz -xpf - ) < "$1"
  661. chkstatus_or_exit 3
  662. # Transite package to Graft control
  663. # Remove objects (files, links or directories) from the target
  664. # directory that are in conflict with the package directory
  665. echo "=== Pruning any conflict ..."
  666. graft -p -D -u $graft_r -t "$targetdir" "${_packagedir}/$name"
  667. chkstatus_or_exit 2
  668. echo "=== Enabling symbolic links ..."
  669. graft -i -P $graft_v $graft_r -t "$targetdir" "${_packagedir}/$name"
  670. chkstatus_or_exit 2
  671. # Avoid unnecessary runs coming from the upgrade mode,
  672. # this is when the incoming package is **pre-installed**
  673. if test "$_isUpgrade" != _isUpgrade.on
  674. then
  675. # Show package description
  676. if test -r "$rootdir${_packagedir}/${name}/var/lib/qi/${name}.txt"
  677. then
  678. awk '/^#/' "$rootdir${_packagedir}/${name}/var/lib/qi/${name}.txt"
  679. elif test -r "${1}.txt"
  680. then
  681. # From external meta file (current directory)
  682. awk '/^#/' "${1}.txt"
  683. else
  684. warn "Description file not found for '$name'."
  685. fi
  686. # Check and run the post-install script if exist
  687. if test -r "$rootdir${_packagedir}/${name}/var/lib/qi/${name}.sh"
  688. then
  689. echo "=== Running post-install script for \`${name}' ..."
  690. (
  691. # Rely on 'targetdir' if 'rootdir' is empty
  692. cd -- "${rootdir:=$targetdir}"/ && \
  693. . "$rootdir${_packagedir}/${name}/var/lib/qi/${name}.sh"
  694. )
  695. fi
  696. # Check if there are declared packages for replacement
  697. if test -r "$rootdir${_packagedir}/${name}/var/lib/qi/${name}.replace"
  698. then
  699. while read -r line
  700. do
  701. for replace in "$rootdir${_packagedir}/${line%%_*}"_*
  702. do
  703. if ! test -e "$replace"
  704. then
  705. warn "<^> Declared package \`${replace}' does not exist. (ignoring)"
  706. continue;
  707. fi
  708. replace="${replace##*/}"
  709. # The search for the package to be replaced cannot
  710. # be the same to the incoming package, even to the
  711. # temporary location coming from the upgrade mode
  712. if test "$replace" = "$name" || \
  713. test "_x_${replace}" = "_x_${PRVLOC##*/}"
  714. then
  715. continue;
  716. fi
  717. warn "WARNING: Replacing package \`${replace}' ..."
  718. # Since the links belongs to the new package, only
  719. # those which are not in conflict can be deleted.
  720. # To complete, we will remove the package directory
  721. graft -d -D -u $graft_r \
  722. -t "$targetdir" "$replace" > /dev/null 2>&1
  723. rm -rf -- "$rootdir${_packagedir}/$replace"
  724. done
  725. done < "$rootdir${_packagedir}/${name}/var/lib/qi/${name}.replace"
  726. fi
  727. fi
  728. # Remove used variables
  729. unset -v name _packagedir
  730. echo ""
  731. }
  732. mode_order()
  733. {
  734. # Complain if the file cannot be well-read
  735. is_readable "$1" || exit 4
  736. # Complain if the file does not end in .order
  737. if ! fnmatch '*.order' "$1"
  738. then
  739. warn "\`${1}' does not end in .order"
  740. return 4
  741. fi
  742. # Get a clean list of the file while printing its contents in reverse
  743. # order. The last `awk 'in the pipeline eliminates the non-consecutive
  744. # lines, the duplicates. Blank lines, colons and parentheses are
  745. # simply ignored, comment lines beginning with '#' are allowed
  746. awk \
  747. '{ gsub( /:|^#(.*)$|\([^)]*)|^$/,"" ); for( i=NF; i > 0; i-- ) print $i }' \
  748. "$1" | awk '!s[$0]++'
  749. }
  750. mode_upgrade()
  751. {
  752. # Complain if the package does not end in .tlz
  753. if ! fnmatch '*.tlz' "$1"
  754. then
  755. warn "\`${1}' does not end in .tlz"
  756. return 4
  757. fi
  758. # Get the filename
  759. incoming="$(basename -- "$1" .tlz)"
  760. echo ""
  761. echo "{%} Upgrading package \`${incoming}' ..."
  762. # Check package pre-existence
  763. if test "$opt_force" != opt_force && \
  764. test -e "$rootdir${packagedir}/$incoming"
  765. then
  766. warn \
  767. "" \
  768. " The package to be upgraded already exist;" \
  769. "Unless the --force option is given, this package won't be UPGRADED."
  770. return 6;
  771. fi
  772. # Ignore some signals until the upgrade process is complete
  773. trap "" HUP INT QUIT ABRT TERM
  774. # Check blacklisted packages
  775. echo "=== Checking blacklist ..."
  776. for item in $blacklist
  777. do
  778. case $item in
  779. ${incoming}*)
  780. warn \
  781. "Package name declared on the blacklist \"${incoming}\"." \
  782. "" \
  783. " This package will be INSTALLED instead of being upgraded."
  784. opt_prune=opt_prune mode_install "$1"
  785. return 0
  786. ;;
  787. esac
  788. done
  789. unset -v item
  790. # Prepare the package to install it in a temporary but custom location
  791. # Set random directory under 'rootdir/packagedir' using 'incoming' as name
  792. PRVLOC=$(mktemp -dp "$rootdir${packagedir}" ${incoming}.XXXXXXXXXXXX) || exit 2
  793. echo "=== Pre-installing package in temporary location ..."
  794. opt_prune=opt_prune # Turn on prune operation.
  795. _isUpgrade=_isUpgrade.on mode_install "$1" "$PRVLOC" > /dev/null
  796. _isUpgrade=_isUpgrade.off
  797. echo "=== Looking for installations under the same name ..."
  798. for long_name in "$rootdir${packagedir}/${incoming%%_*}"_*
  799. do
  800. # Check if it is a valid directory and if the incoming
  801. # package is not equivalent to a previous installation;
  802. # also check if the package to be deleted is equivalent
  803. # to the temporary, private location.
  804. if ! test -d "$long_name"
  805. then
  806. continue;
  807. fi
  808. if test "${long_name##*/}" = "$incoming"
  809. then
  810. continue;
  811. fi
  812. if test "$long_name" = "$PRVLOC"
  813. then
  814. continue;
  815. fi
  816. warn "Proceeding to the removal of ${long_name} ..."
  817. # A package directory will be preserved if --keep is given
  818. mode_remove "${long_name##*/}" > /dev/null
  819. done
  820. unset -v long_name
  821. # Re-install the package removing the temporary location
  822. mode_install "$1"
  823. opt_prune=opt_prune.off # Turn off prune operation.
  824. echo "=== Deleting temporary location ..."
  825. rm -rf -- "$PRVLOC" || chkstatus_or_exit
  826. echo "removed directory: '$PRVLOC'"
  827. echo "{%} Upgraded from \"${1}\"."
  828. # Remove remaining variables
  829. unset -v incoming PRVLOC
  830. echo ""
  831. # Reset given signals
  832. trap - HUP INT QUIT ABRT TERM
  833. }
  834. mode_warn()
  835. {
  836. # Complain if the package cannot be well-read
  837. is_readable "$1" || exit 4
  838. # Complain if the package does not end in .tlz
  839. if ! fnmatch '*.tlz' "$1"
  840. then
  841. warn "\`${1}' does not end in .tlz"
  842. return 4
  843. fi
  844. # List content of files excluding directories
  845. while test -f "$1"
  846. do
  847. tarlz -tvvf "$1" | awk '!/^drwx/'
  848. chkstatus_or_exit 3
  849. shift;
  850. done
  851. return 0
  852. }
  853. mode_extract()
  854. {
  855. # Perform sanity checks before package extraction
  856. is_readable "$1" || exit 4
  857. test -f "$1" || {
  858. warn "\`${1}' is not a regular file."
  859. return 4
  860. }
  861. # Preparations to extract the package
  862. name="$(basename -- "$1" .tlz)"
  863. # Set random directory under 'TMPDIR' using 'name'
  864. PRVDIR=$(mktemp -dp "$TMPDIR" ${name}.XXXXXXXXXXXX) || exit 2
  865. # Trap to remove 'PRVDIR' on disruptions
  866. trap 'rm -rf -- "$PRVDIR"' HUP INT ABRT TERM
  867. # Create 'PRVDIR' removing access for all but user
  868. ( umask 077 ; mkdir -- "$PRVDIR" )
  869. mkdir -p -- "$PRVDIR"
  870. chmod 700 -- "$PRVDIR"
  871. echo ""
  872. echo "--- Extracting package \`${name}' ..."
  873. ( umask 000 ; cd -- "$PRVDIR" && tarlz -xvf - ) < "$1"
  874. if test $? -ne 0
  875. then
  876. # Try to remove (empty) 'PRVDIR' on failure
  877. rmdir -- "$PRVDIR"
  878. exit 3;
  879. fi
  880. echo ""
  881. echo "\"${name}\" has been extracted on \`${PRVDIR}'"
  882. # Reset given signals
  883. trap - HUP INT ABRT TERM
  884. # Remove used variables
  885. unset -v name PRVDIR
  886. }
  887. ### Extra functions to be used during the modes
  888. unpack()
  889. {
  890. for file in "$@"
  891. do
  892. case $file in
  893. *.tar)
  894. tar -tf "$file" > /dev/null && \
  895. tar -xpf "$file"
  896. chkstatus_or_exit 3
  897. ;;
  898. *.tar.gz | *.tgz | *.tar.Z )
  899. gzip -cd "$file" | tar -tf - > /dev/null && \
  900. gzip -cd "$file" | tar -xpf -
  901. chkstatus_or_exit 3
  902. ;;
  903. *.tar.bz2 | *.tbz2 | *.tbz )
  904. bzip2 -cd "$file" | tar -tf - > /dev/null && \
  905. bzip2 -cd "$file" | tar -xpf -
  906. chkstatus_or_exit 3
  907. ;;
  908. *.tar.lz | *.tlz )
  909. lzip -cd "$file" | tar -tf - > /dev/null && \
  910. lzip -cd "$file" | tar -xpf -
  911. chkstatus_or_exit 3
  912. ;;
  913. *.tar.xz | *.txz )
  914. xz -cd "$file" | tar -tf - > /dev/null && \
  915. xz -cd "$file" | tar -xpf -
  916. chkstatus_or_exit 3
  917. ;;
  918. *.zip | *.ZIP )
  919. unzip -t "$file" > /dev/null && \
  920. unzip "$file" > /dev/null
  921. chkstatus_or_exit 3
  922. ;;
  923. *.gz)
  924. gzip -t "$file" && \
  925. gzip -cd "$file" > "$(basename -- "$file" .gz)"
  926. chkstatus_or_exit 3
  927. ;;
  928. *.Z)
  929. gzip -t "$file" && \
  930. gzip -cd "$file" > "$(basename -- "$file" .Z)"
  931. chkstatus_or_exit 3
  932. ;;
  933. *.bz2)
  934. bzip2 -t "$file" && \
  935. bzip2 -cd "$file" > "$(basename -- "$file" .bz2)"
  936. chkstatus_or_exit 3
  937. ;;
  938. *.lz)
  939. lzip -t "$file" && \
  940. lzip -cd "$file" > "$(basename -- "$file" .lz)"
  941. chkstatus_or_exit 3
  942. ;;
  943. *.xz)
  944. xz -t "$file" && \
  945. xz -cd "$file" > "$(basename -- "$file" .xz)"
  946. chkstatus_or_exit 3
  947. ;;
  948. *)
  949. warn "${PROGRAM}: cannot unpack ${file}: Unsupported extension"
  950. exit 1
  951. esac
  952. done
  953. unset -v file
  954. }
  955. do_meta()
  956. {
  957. # Extract information from the recipe to create the meta file.
  958. #
  959. # The package description is pre-formatted in 78 columns,
  960. # the '#' character and a space is added as prefix to conform
  961. # the 80 columns in total
  962. printf '%s\n' "$description" | fold -w 78 | awk '$0="# " $0'
  963. printf '%s' \
  964. "
  965. QICFLAGS=\"$QICFLAGS\"
  966. QICXXFLAGS=\"$QICXXFLAGS\"
  967. QILDFLAGS=\"$QILDFLAGS\"
  968. pkgname=$pkgname
  969. pkgversion=$pkgversion
  970. arch=$arch
  971. release=$release
  972. pkgcategory=\"${pkgcategory#@*}\"
  973. full_pkgname=$full_pkgname
  974. blurb=\"$(printf '%s\n' "$description" | sed -e '/^$/d;2q')\"
  975. homepage=\"$homepage\"
  976. license=\"$license\"
  977. fetch=\"$fetch\"
  978. replace=\"$replace\"
  979. "
  980. }
  981. ### Default values
  982. packagedir=@PACKAGEDIR@
  983. targetdir=@TARGETDIR@
  984. blacklist="perl5 graft tarlz plzip musl glibc coreutils bash mksh"
  985. _rcfile=@SYSCONFDIR@/qirc
  986. opt_install=opt_install.off
  987. opt_upgrade=opt_upgrade.off
  988. opt_force=opt_force.off
  989. opt_keep=opt_keep.off
  990. opt_incr_release=opt_incr_release.off
  991. opt_skipqsts=opt_skipqsts.off
  992. opt_nopkg=opt_nopkg.off
  993. opt_prune=opt_prune.off
  994. verbose=0
  995. rootdir=""
  996. arch=@ARCH@
  997. jobs=1
  998. mode=""
  999. _readstdin=""
  1000. graft_v=""
  1001. graft_r=""
  1002. _isUpgrade=_isUpgrade.off
  1003. keep_srcdir=""
  1004. keep_destdir=""
  1005. TMPDIR="${TMPDIR:-/usr/src/qi/build}"
  1006. QICFLAGS="${QICFLAGS:--g0 -O2}"
  1007. QICXXFLAGS="${QICXXFLAGS:--g0 -O2}"
  1008. QILDFLAGS="${QILDFLAGS:--s}"
  1009. worktree=/usr/src/qi
  1010. tardir=${worktree}/sources
  1011. outdir=/var/cache/qi/packages
  1012. netget="wget -c -w1 -t3 --no-check-certificate"
  1013. rsync="rsync -v -a -L -z -i --progress"
  1014. configure_args="--prefix=@PREFIX@ --libexecdir=@LIBEXECDIR@ --bindir=@BINDIR@ --sbindir=@SBINDIR@ --sysconfdir=@SYSCONFDIR@ --localstatedir=@LOCALSTATEDIR@"
  1015. infodir=@INFODIR@
  1016. mandir=@MANDIR@
  1017. docdir=@DOCDIR@
  1018. # Store (default) directory locations
  1019. QI_TARGETDIR=$targetdir
  1020. QI_PACKAGEDIR=$packagedir
  1021. QI_WORKTREE=$worktree
  1022. QI_TARDIR=$tardir
  1023. QI_OUTDIR=$outdir
  1024. ### Parse commands and options
  1025. validate_mode()
  1026. {
  1027. if test -n "$mode"
  1028. then
  1029. warn "${PROGRAM}: First defined command: ${mode#*_}" \
  1030. "Switching to another command is not allowed (${1})."
  1031. exit 1
  1032. fi
  1033. }
  1034. validate_option()
  1035. {
  1036. if test -z "$2"
  1037. then
  1038. warn "${PROGRAM}: The '${1}' option requires an argument" \
  1039. "Try '${PROGRAM} --help' for more information."
  1040. exit 1
  1041. fi
  1042. }
  1043. validate_directory()
  1044. {
  1045. if test ! -d "$2"
  1046. then
  1047. warn "${PROGRAM}: ${1} \"${2}\" must be a valid directory name"
  1048. exit 1
  1049. fi
  1050. }
  1051. validate_digit()
  1052. {
  1053. name="$1"
  1054. shift
  1055. # Taken from https://mywiki.wooledge.org/BashFAQ/054
  1056. case ${1#[-+]} in
  1057. '')
  1058. warn "${PROGRAM}: The '${name}' option has no defined value"
  1059. exit 1
  1060. ;;
  1061. *.*.*)
  1062. warn "${PROGRAM}: The '${name}' option has more than one decimal point on it \"${1}\""
  1063. exit 1
  1064. ;;
  1065. *[!0-9]*)
  1066. warn "${PROGRAM}: The '${name}' option contains a non-valid digit on it \"${1}\""
  1067. exit 1
  1068. ;;
  1069. esac
  1070. unset -v name
  1071. }
  1072. while test $# -gt 0
  1073. do
  1074. case $1 in
  1075. warn)
  1076. validate_mode warn
  1077. mode=mode_warn
  1078. ;;
  1079. install)
  1080. validate_mode install
  1081. readconfig
  1082. mode=mode_install
  1083. ;;
  1084. remove)
  1085. validate_mode remove
  1086. readconfig
  1087. mode=mode_remove
  1088. ;;
  1089. upgrade)
  1090. validate_mode upgrade
  1091. readconfig
  1092. mode=mode_upgrade
  1093. ;;
  1094. extract)
  1095. validate_mode extract
  1096. readconfig
  1097. mode=mode_extract
  1098. ;;
  1099. create)
  1100. validate_mode create
  1101. readconfig
  1102. mode=mode_create
  1103. ;;
  1104. order)
  1105. validate_mode order
  1106. readconfig
  1107. mode=mode_order
  1108. ;;
  1109. build)
  1110. validate_mode build
  1111. readconfig
  1112. mode=mode_build
  1113. ;;
  1114. --no-rc | -N )
  1115. _readconfig=readconfig.off
  1116. ;;
  1117. --install | -i )
  1118. opt_install=opt_install
  1119. ;;
  1120. --upgrade | -u )
  1121. opt_upgrade=opt_upgrade
  1122. ;;
  1123. --force | -f )
  1124. opt_force=opt_force
  1125. ;;
  1126. --keep | -k )
  1127. opt_keep=opt_keep
  1128. ;;
  1129. --prune | -p )
  1130. opt_prune=opt_prune
  1131. ;;
  1132. --packagedir | -P )
  1133. validate_option "$1" "$2"
  1134. packagedir="$2"
  1135. validate_directory "$1" "$packagedir"
  1136. shift
  1137. ;;
  1138. --packagedir=*)
  1139. validate_option "$1" "$2"
  1140. packagedir="${1#*=}"
  1141. validate_directory "$1" "$packagedir"
  1142. ;;
  1143. --targetdir | -t )
  1144. validate_option "$1" "$2"
  1145. targetdir="$2"
  1146. validate_directory "$1" "$targetdir"
  1147. shift
  1148. ;;
  1149. --targetdir=*)
  1150. validate_option "$1" "$2"
  1151. targetdir="${1#*=}"
  1152. validate_directory "$1" "$targetdir"
  1153. ;;
  1154. --rootdir | -r )
  1155. validate_option "$1" "$2"
  1156. rootdir="$2"
  1157. validate_directory "$1" "$rootdir"
  1158. shift
  1159. ;;
  1160. --rootdir=*)
  1161. validate_option "$1" "$2"
  1162. rootdir="${1#*=}"
  1163. validate_directory "$1" "$rootdir"
  1164. ;;
  1165. --outdir | -o )
  1166. validate_option "$1" "$2"
  1167. outdir="$2"
  1168. validate_directory "$1" "$outdir"
  1169. shift
  1170. ;;
  1171. --outdir=*)
  1172. validate_option "$1" "$2"
  1173. outdir="${1#*=}"
  1174. validate_directory "$1" "$outdir"
  1175. ;;
  1176. --worktree | -w )
  1177. validate_option "$1" "$2"
  1178. worktree="$2"
  1179. validate_directory "$1" "$worktree"
  1180. shift
  1181. ;;
  1182. --worktree=*)
  1183. validate_option "$1" "$2"
  1184. worktree="${1#*=}"
  1185. validate_directory "$1" "$worktree"
  1186. ;;
  1187. --sourcedir | -s )
  1188. validate_option "$1" "$2"
  1189. tardir="$2"
  1190. validate_directory "$1" "$tardir"
  1191. shift
  1192. ;;
  1193. --sourcedir=*)
  1194. validate_option "$1" "$2"
  1195. tardir="${1#*=}"
  1196. validate_directory "$1" "$tardir"
  1197. ;;
  1198. --architecture | -a )
  1199. validate_option "$1" "$2"
  1200. arch="$2"
  1201. shift
  1202. ;;
  1203. --arch=*)
  1204. validate_option "$1" "$2"
  1205. arch="${1#*=}"
  1206. ;;
  1207. --jobs | -j )
  1208. jobs="$2"
  1209. validate_digit "$1" "$jobs"
  1210. shift
  1211. ;;
  1212. -j[0-9]*)
  1213. jobs="${1#-j*}"
  1214. validate_digit '-j' "$jobs"
  1215. ;;
  1216. --jobs=*)
  1217. jobs="${1#*=}"
  1218. validate_digit '--jobs=' "$jobs"
  1219. ;;
  1220. --no-package | -n )
  1221. opt_nopkg=opt_nopkg
  1222. ;;
  1223. --increment | -1 )
  1224. opt_incr_release=opt_incr_release
  1225. ;;
  1226. --skip-questions | -S )
  1227. opt_skipqsts=opt_skipqsts
  1228. ;;
  1229. --verbose | -v )
  1230. verbose=$(( verbose + 1 ))
  1231. ;;
  1232. -vv)
  1233. # ^ Trick for a second -v.
  1234. verbose=2
  1235. ;;
  1236. --show-location | -L )
  1237. printf '%s\n' \
  1238. "QI_TARGETDIR=$QI_TARGETDIR" \
  1239. "QI_PACKAGEDIR=$QI_PACKAGEDIR" \
  1240. "QI_WORKTREE=$QI_WORKTREE" \
  1241. "QI_TARDIR=$QI_TARDIR" \
  1242. "QI_OUTDIR=$QI_OUTDIR"
  1243. exit 0
  1244. ;;
  1245. --help | --hel | --he | --h | '--?' | -help | -hel | -he | -h | '-?' | \
  1246. help )
  1247. usage
  1248. exit 0
  1249. ;;
  1250. --version | --versio | --versi | --vers | \
  1251. -version | -versio | -versi | -vers | -V | version )
  1252. echo "$PROGRAM version @VERSION@"
  1253. exit 0
  1254. ;;
  1255. '-')
  1256. _readstdin=readstdin
  1257. break
  1258. ;;
  1259. --)
  1260. shift
  1261. break; # End of options.
  1262. ;;
  1263. -*)
  1264. warn "qi: Unrecognized option: $1" \
  1265. "Try '${PROGRAM} --help' for more information."
  1266. exit 1
  1267. ;;
  1268. *)
  1269. break; # No more options.
  1270. ;;
  1271. esac
  1272. shift
  1273. done
  1274. unset -f \
  1275. readconfig validate_mode validate_option validate_directory validate_digit
  1276. # When there are no arguments, show the help
  1277. if test $# -eq 0
  1278. then
  1279. usage
  1280. exit 1
  1281. fi
  1282. unset -f usage
  1283. # Program sanity check
  1284. for need in awk basename chmod cp dirname find fold graft grep \
  1285. mkdir mktemp rm rmdir sed sha256sum stat tarlz ; \
  1286. do
  1287. if ! \command -v $need > /dev/null
  1288. then
  1289. warn "${PROGRAM}: Prerequisite \`${need}' not found in PATH"
  1290. exit 2
  1291. fi
  1292. done
  1293. unset -v need
  1294. # Set verbosity level/flag
  1295. if test "$verbose" -gt 0
  1296. then
  1297. if test "$verbose" -eq 1
  1298. then
  1299. graft_v=-v
  1300. else
  1301. graft_v=-V
  1302. fi
  1303. fi
  1304. # Read standard input if FILE is -, or when
  1305. # FILE is not connected to a terminal
  1306. if test "$_readstdin" = readstdin
  1307. then
  1308. if test -t 0
  1309. then
  1310. warn "qi: I won't read from a connected terminal." \
  1311. "Try '${PROGRAM} --help' for more information."
  1312. exit 1
  1313. fi
  1314. # Unset positional parameters setting $# to zero
  1315. set --
  1316. # Assign remaining arguments to the positional parameters
  1317. while read -r input
  1318. do
  1319. set -- "$@" "$input"
  1320. done
  1321. fi
  1322. unset -v _readstdin
  1323. if test -z "$mode"
  1324. then
  1325. warn "qi: We need at least one (valid) command." \
  1326. "Try '${PROGRAM} --help' for more information."
  1327. exit 4
  1328. fi
  1329. # Validate 'packagedir' and 'targetdir' as canonical directories
  1330. # The single slash '/' does not qualify here
  1331. if ! fnmatch '/?*' "$packagedir"
  1332. then
  1333. warn "${PROGRAM}: Package directory \`${packagedir}' is not fully qualified"
  1334. exit 4
  1335. fi
  1336. if test ! -d "$packagedir"
  1337. then
  1338. warn "${PROGRAM}: Package directory \`${packagedir}' does not exist"
  1339. exit 4
  1340. fi
  1341. # The single slash '/' is valid here
  1342. if ! fnmatch '/*' "$targetdir"
  1343. then
  1344. warn "${PROGRAM}: Target directory \`${targetdir}' is not fully qualified"
  1345. exit 4
  1346. fi
  1347. if test ! -d "$targetdir"
  1348. then
  1349. warn "${PROGRAM}: Target directory \`${targetdir}' does not exist"
  1350. exit 4
  1351. fi
  1352. # Validate 'rootdir' directory
  1353. if test -n "$rootdir"
  1354. then
  1355. if test -d "$rootdir" && test "$rootdir" != /
  1356. then
  1357. rootdir="${rootdir%/}" # Remove slash from the end.
  1358. # A workaround for graft-2.13+. The specified directory is
  1359. # relative to the log file, we prepend it inside 'rootdir'
  1360. eval "$(graft -L)" ; GRAFT_LOGFILE="${GRAFT_LOGFILE:=/var/log/graft}"
  1361. mkdir -p -- "$rootdir$(dirname -- "$GRAFT_LOGFILE")" || chkstatus_or_exit
  1362. # Compose 'rootdir' and log file option to be used with graft(1)
  1363. graft_r="-r $rootdir -l $GRAFT_LOGFILE"
  1364. # Unset variables coming from eval
  1365. unset -v GRAFT_PERL GRAFT_LOGFILE GRAFT_TARGETDIR GRAFT_PACKAGEDIR
  1366. else
  1367. warn "${PROGRAM}: Root directory \`${rootdir}' is not fully qualified"
  1368. exit 4
  1369. fi
  1370. readonly rootdir
  1371. export rootdir
  1372. fi
  1373. # Ensure 'TMPDIR' creation to prefix temporary files
  1374. if test ! -d "$TMPDIR"
  1375. then
  1376. mkdir -p -- "$TMPDIR" || chkstatus_or_exit
  1377. fi
  1378. readonly TMPDIR packagedir targetdir
  1379. # Process each package or recipe provided on the command-line
  1380. for package in "$@"
  1381. do
  1382. $mode "$package"
  1383. done