2 # Copyright (C) 2016-2019 Matias Fonzo <selk@dragora.org>
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.
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.
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/>.
18 # 0 = Successful completion
19 # 1 = Minor common errors (e.g: help usage, support not available)
20 # 2 = Command execution error
21 # 3 = Integrity check error for compressed files
22 # 4 = File empty, not regular, or expected
23 # 5 = Empty or not defined variable
24 # 6 = Package already installed
25 # 10 = Network manager error
32 "Qi - A user-friendly package manager." \
34 "Usage: $PROGRAM [OPTION...] [FILE]..." \
37 " -b Build packages using recipe names" \
38 " -c Create .tlz package from directory" \
39 " -d Delete packages" \
40 " -i Install packages" \
41 " -o Resolve build order through .order files" \
42 " -u Update packages (implies -i, -d with -p)" \
43 " -w Warn about files that will be linked" \
44 " -x Extract a package for debugging purposes" \
47 " -N Do not read the configuration file" \
48 " -P <DIR> Package directory for installations." \
49 " Only valid for -i, -d, or -u options" \
50 " -f This option can force the build of a recipe," \
51 " or force the update of a pre-existing package." \
52 " Only valid for -b, -u options" \
53 " -t <DIR> Target directory for symbolic links." \
54 " Only valid for -i, -d, or -u options" \
55 " -k Keep \`\${srcdir}' or \`\${destdir}' in build mode," \
56 " keep package directory in delete mode." \
57 " Only valid for -b, -d or -u options" \
58 " -p Prune conflicts on package installations" \
59 " -r <DIR> Use the fully qualified named directory as the" \
60 " root directory for all qi operations. The target" \
61 " directory and package directory will be relative to" \
62 " the specified directory, including the log file for" \
64 " -v Be verbose (a 2nd -v gives more)" \
66 "Options for 'build' mode (-b):" \
67 " -O <DIR> Where the packages produced are written" \
68 " -W <DIR> Where archives, patches, and recipes are expected" \
69 " -Z <DIR> Where (compressed) sources will be found" \
70 " -a Architecture to use [detected]" \
71 " -j Parallel jobs for the compiler" \
72 " -1 Increment release number (\`\${release}' + 1)" \
73 " -n Don't create a .tlz package" \
74 " -S Selects the option to skip completed recipes" \
76 "Informative options:" \
77 " -L Print default directory locations" \
78 " -h Display this help and exit" \
79 " -V Output version information" \
81 "Some influential environment variables:" \
82 " TMPDIR Temporary directory for sources" \
83 " QICFLAGS C compiler flags" \
84 " QICXXFLAGS C++ compiler flags" \
85 " QILDFLAGS Linker flags" \
87 "When FILE is -, read standard input." \
94 "$PROGRAM @VERSION@" \
95 "Copyright (C) 2016-2019 Matias Andres Fonzo." \
96 "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>" \
97 "This is free software: you are free to change and redistribute it." \
98 "There is NO WARRANTY, to the extent permitted by law."
103 printf "%s\n" "$@" 1>&2
112 echo "${PROGRAM}: cannot read ${1}: Permission denied" 1>&2
116 echo "${PROGRAM}: cannot access ${1}: No such file or directory" 1>&2
121 # Portable alternative to the file operator -nt (among shells)
124 if test -n "$(find $1 -prune -newer $2 -print)"
131 # Determine whether $2 matches pattern $1
148 if test $status -ne 0
150 echo "Return status = $status" 1>&2
151 exit ${1-2}; # If not given, defaults to 2
161 is_readable
"$HOME/.qirc" 2> /dev
/null
&& RCFILE
="$HOME/.qirc";
163 echo "Processing \`${RCFILE}' ..."
165 test -f "$RCFILE" ||
{
166 warn
"${RCFILE} is not a regular file."
171 while IFS
='=' read -r variable value
174 \
#* | "") # Ignore commented or blank lines
179 # Set variable name avoiding code execution
180 eval "$variable=\${value}"
191 # A recipe is any valid regular file, the current working directory
192 # has priority over the working tree (or where the recipes reside).
193 # The 'worktree' is the second place where to find a recipe. Also,
194 # we complete the possibility of using the directory name to invoke
195 # a recipe if it contains "recipe" as valid file name.
197 if test ! -f "$recipe"
199 if test -f "${recipe}/recipe"
201 recipe
="${recipe}/recipe"
202 elif test -f "${worktree}/recipes/${recipe}/recipe"
204 recipe
="${worktree}/recipes/${recipe}/recipe"
208 test -f "$recipe" ||
{
209 warn
"\`${recipe}' is not a regular file."
213 # Complain if the file name is not "recipe"
215 if test "${recipe##*/}" != recipe
217 warn
"\`${recipe}' is not a valid recipe name."
221 # Start preparations to import the recipe
223 # Separate the directory name from the file name,
224 # getting its absolute path and base name
226 CWD
=$
(CDPATH
= cd -P -- $
(dirname -- "$recipe") && printf "$PWD")
227 recipe
=$
(basename -- "$recipe")
229 # Check readability for load the recipe on success
231 is_readable
"${CWD}/$recipe" ||
exit 4
233 # Find target architecture if 'arch' is not set
236 arch
=$
(${CC:-cc} -dumpmachine 2> /dev
/null
) || arch
=unknown
237 arch
="${arch%%-*}" # Get the rid of target triplet.
240 # Re-create external directories
241 mkdir
-p -- "${worktree}/archive" \
242 "${worktree}/patches" \
243 "${worktree}/recipes" \
246 # Variables treatment for the current and next recipe.
248 # Unset special variables that can only be predefined on
249 # the recipe and not coming from ${sysconfdir}/qirc
251 unset srcdir destdir pkgname pkgversion program version release \
252 fetch description homepage license replace full_pkgname \
253 CFLAGS CXXFLAGS LDFLAGS
255 # The following variables must be saved and restored
256 save_arch
="${save_arch:=$arch}"
257 save_jobs
="${save_jobs:=$jobs}"
258 save_outdir
="${save_outdir:=$outdir}"
259 save_opt_nopkg
="${save_opt_nopkg:=$opt_nopkg}"
261 # Reset variable values in case of return
265 opt_nopkg
=$save_opt_nopkg
267 # The following variables cannot be redefined on the recipe
268 readonly worktree netget rsync
272 echo "{@} Building from ${CWD}/$recipe ..."
275 # Check for required variables
276 if test -z "$program"
278 warn
"${recipe}: The variable 'program' is not defined."
281 if test -z "$version"
283 warn
"${recipe}: The variable 'version' is not defined."
286 if test -z "$release"
288 warn
"${recipe}: The variable 'release' is not defined."
292 # Pre-settings before to start building
294 # Increment the release number if the option was given
295 if test "$opt_incr_release" = opt_incr_release
297 release
=$
(( release
+ 1 ))
300 # Allow the dot as definition for 'tardir'
301 if test "$tardir" = .
306 # Set default values for the following special variables
308 pkgname
="${pkgname:=$program}"
309 pkgversion
="${pkgversion:=$version}"
310 srcdir
="${srcdir:=${program}-$version}"
311 destdir
="${destdir:=${TMPDIR}/package-$pkgname}"
313 # Complete package name adding 'pkgversion-arch+release'
314 full_pkgname
="${full_pkgname:=$pkgname-${pkgversion}-${arch}+${release}}"
316 # If a package is going to be created, the existence of a
317 # previous build will be detected and reported. Under normal
318 # conditions the recipe is built as long as it is newer than
319 # the produced package, if not, we warn to the user about it.
320 # Rebuilding the package is possible (through the force ;-)
322 if test "$opt_nopkg" != opt_nopkg
&& \
323 { test "$opt_force" != opt_force
&& \
324 test -e "${outdir}/${full_pkgname}.tlz" ; }
326 if is_newer "${CWD}/$recipe" "${outdir}/${full_pkgname}.tlz
"
330 "The recipe is
more RECENT than the detected package
:" \
332 "$
( stat
-c "%y %n" "${CWD}/$recipe" )" \
333 "$
( stat
-c "%y %n" "${outdir}/${full_pkgname}.tlz" )" \
335 " This recipe will be processed ...
" \
337 elif test -e "${CWD}/post-install
" && \
338 is_newer "${CWD}/post-install
" "${CWD}/$recipe"
342 "The post-install
script is
more RECENT than the recipe
:" \
344 "$
( stat
-c "%y %n" "${CWD}/post-install" )" \
345 "$
( stat
-c "%y %n" "${CWD}/$recipe" )" \
347 " The recipe will be re-processed ...
" \
349 touch "${CWD}/$recipe"
350 elif test "$opt_skipqsts" = opt_skipqsts
352 warn "Recipe
for '${full_pkgname}.tlz': Ignored.
" ""
357 "This recipe ALREADY produced a package
:" \
358 "$
( stat
-c "%y %n" "${outdir}/${full_pkgname}.tlz" )" \
360 "The recipe is still OLDER than the produced package
:" \
361 "$
( stat
-c "%y %n" "${CWD}/$recipe" )" \
363 " Probably nothing has changed.
" \
366 # In non-interactive mode, the user is asked about
367 # rebuilding the package. In interactive mode,
368 # the user need to pass the option explicitly
372 "Do you want to rebuild this package?
" \
374 "2) No
, skip it
[default
]" \
375 "3) Resume
, skipping completed recipes
" \
376 "Enter an option number
:" > /dev/tty
377 IFS= read -r ANSWER < /dev/tty || exit 2;
380 echo "$ANSWER" > /dev/tty
385 echo "Building UNPROCESSED
/MODIFIED recipes ...
" > /dev/tty
387 opt_skipqsts=opt_skipqsts
388 readonly opt_skipqsts
393 echo "Recipe
for '${full_pkgname}.tlz': Cancelled.
" > /dev/tty
399 warn "Use the
-f option to reprocess
${CWD}/$recipe.
" ""
405 # Fetch remote sources
407 echo "Fetching remote sources
if needed ...
"
412 _source="${origin##*/}"
414 echo "Looking
for \"$_source\" ...
"
416 echo "Verifying checksum
file \"${_source}.sha256
\" from
'${tardir}'"
417 if test -e "${tardir}/${_source}.sha256
"
419 ( cd -- "$tardir" && sha256sum - ) < "${tardir}/${_source}.sha256
"
423 warn "${_source}.sha256
: Checksum
file does not exist
, yet.
"
426 # Download source or resume if allowed
428 if test ! -e "${tardir}/$_source"
430 warn " Can
't find $_source in ${tardir};" \
431 "attempting to get it from ${origin%/*} ..."
437 cd -- "$tardir" && $rsync $origin || exit $?
438 sha256sum $_source > ${_source}.sha256
439 ); chkstatus_or_exit 10
443 cd -- "$tardir" && $netget $origin || exit $?
444 sha256sum $_source > ${_source}.sha256
445 ); chkstatus_or_exit 10
448 warn "${PROGRAM}: Unrecognized protocol for ${origin}."
454 warn "The variable 'fetch
' is empty."
457 # Prepare special directories for build the source,
458 # the destination and the output of the package
460 echo "Preparing directories ..."
462 if test -d "${TMPDIR}/$srcdir" && test -z "$keep_srcdir"
464 rm -rf -- "${TMPDIR}/$srcdir" || chkstatus_or_exit
465 echo "removed directory: '${TMPDIR}/$srcdir'"
468 if test -d "$destdir"
470 rm -rf -- "$destdir" || chkstatus_or_exit
471 echo "removed directory: '$destdir'"
473 mkdir -p -- "$destdir" || chkstatus_or_exit
474 echo "mkdir: created directory '$destdir'"
476 if test ! -d "$outdir"
478 mkdir -p -- "$outdir" || chkstatus_or_exit
479 echo "mkdir: created directory '$outdir'"
482 echo "Entering to 'TMPDIR
': $TMPDIR ..."
483 cd -- "$TMPDIR" || chkstatus_or_exit
485 # Set trap before to run the build() function in order
486 # to catch the return status, exit code 2 if fails
488 trap 'chkstatus_or_exit
2' EXIT HUP INT QUIT ABRT TERM
490 # Determine if the debugging indicators of the shell should be
491 # retained, assuming that it has been previously passed
493 _xtrace_flag_is_set=xtrace_flag_is_set ;;
496 echo "Running build() ..."
500 # Turn off possible shell flags coming from the recipe
503 if test "$_xtrace_flag_is_set" != xtrace_flag_is_set
508 # Reset given signals
509 trap - EXIT HUP INT QUIT ABRT TERM
511 # If 'destdir
' is empty, the package won't be created
512 if rmdir -- "$destdir" 2> /dev
/null
514 warn
"The package \"${full_pkgname}.tlz\" won't be created. 'destdir' is empty."
518 # Create (make) the package
520 if test "$opt_nopkg" != opt_nopkg
522 # Edit the recipe when 'release' is incremented
523 if test "$opt_incr_release" = opt_incr_release
525 echo ",s/^\(release\)=.*/\1=${release}/"$
'\nw' | \
526 ed
"${CWD}/$recipe" || chkstatus_or_exit
529 mkdir
-p -- "${destdir}/var/lib/qi" || chkstatus_or_exit
531 # Include a recipe copy into the package
532 cp -p "${CWD}/$recipe" \
533 "${destdir}/var/lib/qi/${full_pkgname}.recipe" && \
534 chmod 644 "${destdir}/var/lib/qi/${full_pkgname}.recipe" || chkstatus_or_exit
536 # Detect post-install script for inclusion
538 if test -f "${CWD}/post-install"
540 echo "${CWD}/post-install: Detected."
541 cp -p "${CWD}/post-install" \
542 "${destdir}/var/lib/qi/${full_pkgname}.sh" && \
543 chmod 644 "${destdir}/var/lib/qi/${full_pkgname}.sh" || chkstatus_or_exit
546 # Detect declared package names for later replacement
548 if test -n "$replace"
551 "The following package names has been declared for replacement:" \
554 rm -f "${destdir}/var/lib/qi/${full_pkgname}.replace"
557 echo "$replace" >> "${destdir}/var/lib/qi/${full_pkgname}.replace"
562 # Create meta file for the package information
563 echo "Creating meta file ${full_pkgname}.tlz.txt ..."
564 do_meta
> "${outdir}/${full_pkgname}.tlz.txt" || chkstatus_or_exit
566 # Make a copy of it for the database
567 cp -p "${outdir}/${full_pkgname}.tlz.txt" \
568 "${destdir}/var/lib/qi/${full_pkgname}.txt" || chkstatus_or_exit
570 # Produce the package
571 cd -- "$destdir" && create_mode
"${outdir}/${full_pkgname}.tlz"
574 # Back to the current working directory
575 cd -- "$CWD" || chkstatus_or_exit
577 # Delete 'srcdir' or 'destdir' if -k was not given
578 if test "$opt_keep" != opt_keep
580 echo "Deleting temporary directories ..."
582 srcdir
="${srcdir%%/*}" # Directory name without parents.
584 if test -r "${TMPDIR}/$srcdir"
586 if test -z "$keep_srcdir"
589 cd -- "$TMPDIR" && rm -rf -- "$srcdir" && \
590 echo "removed directory: '${TMPDIR}/${srcdir}'"
593 warn
"The variable 'keep_srcdir' has been set;" \
594 "'${TMPDIR}/${srcdir}' will not be deleted."
598 if test -r "$destdir"
600 rm -rf -- "$destdir" || chkstatus_or_exit
601 echo "removed directory: '$destdir'"
605 # Install or update the package if -i or -u was passed
606 if test "$opt_nopkg" != opt_nopkg
608 if test "$opt_install" = opt_install
610 install_mode
"${outdir}/${full_pkgname}.tlz"
611 elif test "$opt_update" = opt_update
613 upgrade_mode
"${outdir}/${full_pkgname}.tlz"
618 echo "All done for ${CWD}/${recipe}."
624 directory
=$
(dirname -- "$1")
626 # Perform sanity checks
628 if ! fnmatch
'/?*' "$directory"
630 warn
"${PROGRAM}: Output directory \`${directory}' is not fully qualified"
633 is_readable
"$directory" ||
exit 4
635 name
=$
(basename -- "$1")
637 if test "$name" = "${name%.tlz}"
639 warn
"Package format '$name' not supported." \
640 "It should be \"name-version-architecture+release.tlz\""
644 echo "{#} Creating package $name ..."
646 ( umask 022 ; tarlz
--solid -9 -cvf - * ) > "${directory}/$name"
649 ( cd -- "$directory" && sha256sum
$name > ${name}.sha256
)
652 echo "Package created on \`${directory}/${name}'."
655 # Remove used variables
661 expunge
="${packagedir}/$(basename -- $1 .tlz)"
663 echo "{<} Deleting package $rootdir${expunge} ..."
665 # Complain if the package directory does not exist
667 test -d "$rootdir${expunge}" ||
{
668 warn
"Package directory '$rootdir${expunge}' does not exist."
672 # Complain if the package directory cannot be well-read
674 is_readable
"$rootdir${expunge}" ||
exit 4
676 # Remove package from Graft control
678 # Scan for possible conflicts, stop if arise
679 if test "$opt_prune" != opt_prune
681 echo "Checking for possible conflicts ..."
682 if graft
-d -n $graft_r -t "$targetdir" "$expunge" 2>&1 | \
686 " A conflict occurred during uninstallation;" \
687 "Unless the -p option is given, this package will be PRESERVED."
692 # Ignore some signals up to completing the deinstallation
693 trap "" HUP INT QUIT ABRT TERM
695 # Remove objects (files, links or directories) from the target
696 # directory that are in conflict with the package directory
698 echo "Pruning any conflict ..."
699 graft
-p -D -u $graft_r -t "$targetdir" "$expunge"
702 echo "Disabling links ..."
703 graft
-d -D -u $graft_v $graft_r -t "$targetdir" "$expunge"
706 # Delete package directory
707 if test "$opt_keep" != opt_keep
709 echo "Deleting package directory, if it exists as such ..."
711 if test -d "${rootdir}$expunge"
713 rm -rf -- "${rootdir}$expunge" || chkstatus_or_exit
714 echo "removed directory: '${rootdir}$expunge'"
718 # Reset given signals
719 trap - HUP INT QUIT ABRT TERM
721 # Remove used variables
727 # Complain if the package cannot be well-read
729 is_readable
"$1" ||
exit 4
731 # Complain if the package does not end in .tlz
733 if ! fnmatch
'*.tlz' "$1"
735 warn
"\`${1}' does not end in .tlz"
739 # Make preparations to install the package
741 echo "{>} Installing package $1 ..."
744 name
=$
(basename -- "$1" .tlz
)
746 echo "Checking tarball integrity ..."
747 tarlz
-tf "$1" > /dev
/null
750 # Create package directory using 'name'
751 if ! test -d "$rootdir${packagedir}/$name"
753 mkdir
-p -- "$rootdir${packagedir}/$name" || chkstatus_or_exit
754 echo "mkdir: created directory '$rootdir${packagedir}/$name'"
757 # Scan for possible conflicts, stop if arise
758 if test "$opt_prune" != opt_prune
760 echo "Checking for possible conflicts ..."
761 if graft
-i -n $graft_r -t "$targetdir" "${packagedir}/$name" 2>&1 | \
765 " A conflict occurred during installation;" \
766 "Unless the -p option is given, this package won't be LINKED."
771 # Ignore some signals up to completing the installation
772 trap "" HUP INT QUIT ABRT TERM
774 echo "Decompressing $1 ..."
775 ( cd -- "$rootdir${packagedir}/$name" && tarlz
-xf - ) < "$1"
778 # Transite package to Graft control
780 # Remove objects (files, links or directories) from the target
781 # directory that are in conflict with the package directory
782 echo "Pruning any conflict ..."
783 graft
-p -D -u $graft_r -t "$targetdir" "${packagedir}/$name"
786 echo "Enabling symbolic links ..."
787 graft
-i -P $graft_v $graft_r -t "$targetdir" "${packagedir}/$name"
790 # Avoid unnecessary runs coming from the upgrade_mode(),
791 # this is when the incoming package is **pre-installed**
793 if test "$_isUpgrade" != _isUpgrade.on
795 # Show package description
796 if test -r "$rootdir${packagedir}/${name}/var/lib/qi/${name}.txt"
798 grep '^#' "$rootdir${packagedir}/${name}/var/lib/qi/${name}.txt"
799 elif test -r "${1}.txt"
801 # From external meta file (current directory)
804 warn
"Description file not found for '$name'."
807 # Check and run the post-install script if exist
808 if test -r "$rootdir${packagedir}/${name}/var/lib/qi/${name}.sh"
810 echo "Running post-install script for \`${name}' ..."
812 # Rely on 'targetdir' if 'rootdir' is empty
813 cd -- "${rootdir:-$targetdir}"/ && \
814 .
"$rootdir${packagedir}/${name}/var/lib/qi/${name}.sh"
818 # Check if there are declared packages for replacement
819 if test -r "$rootdir${packagedir}/${name}/var/lib/qi/${name}.replace"
823 for replace
in "$rootdir${packagedir}/$(pkgbase $line)"-*
825 if ! test -e "$replace"
827 warn
"${replace}: Declared package does not exist. (ignored)"
831 replace
="${replace##*/}"
833 # The search for the package to be replaced cannot
834 # be the same to the incoming package, even to the
835 # temporary location coming from the upgrade_mode()
836 if test "$replace" = "$name" ||
test "$replace" = "${PRVLOC##*/}"
841 warn
"WARNING: Replacing package \`${replace}' ..."
843 # Since the links belongs to the new package, only
844 # those which are not in conflict can be deleted.
845 # To complete, we will remove the package directory
847 graft
-d -D -u $graft_r \
848 -t "$targetdir" "$replace" > /dev
/null
2>&1
850 rm -rf -- "$rootdir${packagedir}/$replace"
852 done < "$rootdir${packagedir}/${name}/var/lib/qi/${name}.replace"
857 # Reset given signals
858 trap - HUP INT QUIT ABRT TERM
860 # Remove used variables
865 # Complain if the file cannot be well-read
867 is_readable
"$1" ||
exit 4
869 # Complain if the file does not end in .order
871 if ! fnmatch
'*.order' "$1"
873 warn
"\`${1}' does not end in .order"
877 # Get a clean list of the file while printing its contents in reverse
878 # order. The last `awk 'in the pipeline eliminates the non-consecutive
879 # lines, the duplicates. Blank lines, colons and parentheses are
880 # simply ignored, comment lines beginning with '#' are allowed
883 '{ gsub( /:|^#(.*)$|\([^)]*)|^$/,"" ); for( i=NF; i > 0; i-- ) print $i }' \
884 "$1" |
awk '!s[$0]++'
889 # Complain if the package does not end in .tlz
891 if ! fnmatch
'*.tlz' "$1"
893 warn
"\`${1}' does not end in .tlz"
898 incoming
=$
(basename -- "$1" .tlz
)
900 echo "{^} Upgrading to $incoming ..."
902 # Check packages in the blacklist for installation
904 echo "Checking blacklist ..."
905 for item
in $blacklist
909 if test ! -e "$rootdir${packagedir}/$incoming"
913 " The package declared in the blacklist will be" \
914 "installed instead of being updated ..." \
916 opt_prune
=opt_prune install_mode
"$1"
920 if is_newer
"$1" "$rootdir${packagedir}/$incoming"
924 "Incoming package is more RECENT than the installed package:" \
926 "$( stat -c "%y
%n
" "$1" )" \
927 "$( stat -c "%y
%n
" "$rootdir${packagedir}/$incoming" )" \
929 " The package declared in the blacklist will be" \
930 " installed as part of the upgrade process ..." \
932 opt_prune
=opt_prune install_mode
"$1"
933 touch "$rootdir${packagedir}/$incoming"
938 "Blacklisted package is already up-to-date:" \
940 "$( stat -c "%y
%n
" "$rootdir${packagedir}/$incoming" )" \
941 "$( stat -c "%y
%n
" "$1" )" \
950 # Check package pre-existence
951 if test "$opt_force" != opt_force
&& \
952 test -e "$rootdir${packagedir}/$incoming"
956 " The package to be updated already exist;" \
957 "Unless the -f option is given, this package won't be UPGRADED."
961 # Prepare the package to install it in a temporary location
963 # Set random directory using packagedir as prefix and 'incoming' as suffix
964 PRVLOC
=$
(mktemp
-dp "$rootdir${packagedir}" ${incoming}.XXXXXXXXXXXX
) ||
exit 2
966 # Pre-install the package in the custom 'packagedir'
968 save_packagedir
="$rootdir${packagedir}"
971 echo "Pre-installing package in a temporary location ..."
972 opt_prune
=opt_prune
# Turn on prune operation.
973 _isUpgrade
=_isUpgrade.on install_mode
"$1" > /dev
/null
974 _isUpgrade
=_isUpgrade.off
976 # Restore variable before looking for old packages
977 packagedir
=$save_packagedir
978 unset save_packagedir
980 echo "Looking for installations under the same name ..."
981 for long_name
in "$rootdir${packagedir}/$(pkgbase $incoming)"*
983 found
="${long_name##*/}"
985 # The search for the package to be deleted
986 # cannot be the same to the temporary location
987 test "$long_name" = "$PRVLOC" && continue;
989 fnmatch
"$(pkgbase $found)*" "$incoming" ||
continue;
990 echo "${long_name}: Detected."
992 # A package directory is preserved if -k is given
993 delete_mode
"$found" > /dev
/null
995 unset long_name found
997 # Re-install the package removing the temporary location
1000 opt_prune
=opt_prune.off
# Turn off prune operation.
1002 echo "Deleting temporary location ..."
1003 rm -rf -- "$PRVLOC" || chkstatus_or_exit
1004 echo "removed directory: '$PRVLOC'"
1007 echo "Successful upgrade to '${incoming}'."
1009 # Remove remaining variables
1010 unset incoming PRVLOC
1015 # Complain if the package cannot be well-read
1017 is_readable
"$1" ||
exit 4
1019 # Complain if the package does not end in .tlz
1021 if ! fnmatch
'*.tlz' "$1"
1023 warn
"\`${1}' does not end in .tlz"
1027 # List content of files excluding directories
1030 tarlz
-tvvf "$1" |
awk '!/^drwx/'
1039 # Perform sanity checks before package extraction
1041 is_readable
"$1" ||
exit 4
1044 warn
"\`${1}' is not a regular file."
1048 # Preparations to extract the package
1050 name
=$
(basename -- "$1" .tlz
)
1052 # Set random directory using 'name' as prefix
1053 PRVDIR
="${TMPDIR}/${name}.${RANDOM-0}$$"
1055 # Trap to remove 'PRVDIR' on disruptions
1056 trap "rm -rf -- $PRVDIR" HUP INT ABRT TERM
1058 # Create 'PRVDIR' removing access for all but user
1059 ( umask 077 ; mkdir
-- $PRVDIR )
1060 mkdir
-p -m 700 -- $PRVDIR
1062 echo "Extracting package $name ..."
1063 ( umask 000 ; cd -- $PRVDIR && tarlz
-xvf - ) < "$1"
1066 # Try to remove (empty) 'PRVDIR' on failure
1070 echo "$name has been extracted on $PRVDIR"
1072 # Reset given signals
1073 trap - HUP INT ABRT TERM
1075 # Remove used variables
1079 #### Extra functions used in the modes
1083 string
=$
(basename -- "$1" .tlz
)
1085 # Match cases to print the package name.
1087 # We will take into account the four segments removing
1088 # the last two to print the package (long) name
1091 echo "${string%-*-*}"
1107 tar -tf "$file" > /dev
/null
&& \
1111 *.
tar.gz |
*.tgz |
*.
tar.Z
)
1112 gzip -cd "$file" |
tar -tf - > /dev
/null
&& \
1113 gzip -cd "$file" |
tar -xpf -
1116 *.
tar.bz2 |
*.tbz2 |
*.tbz
)
1117 bzip2 -cd "$file" |
tar -tf - > /dev
/null
&& \
1118 bzip2 -cd "$file" |
tar -xpf -
1122 tarlz
-tf "$file" > /dev
/null
&& \
1127 xz
-cd "$file" |
tar -tf - > /dev
/null
&& \
1128 xz
-cd "$file" |
tar -xpf -
1132 unzip -t "$file" > /dev
/null
&& \
1133 unzip "$file" > /dev
/null
1137 gzip -t "$file" && \
1138 gzip -cd "$file" > "$(basename -- $file .gz)"
1142 gzip -t "$file" && \
1143 gzip -cd "$file" > "$(basename -- $file .Z)"
1147 bzip2 -t "$file" && \
1148 bzip2 -cd "$file" > "$(basename -- $file .bz2)"
1152 lzip
-t "$file" && \
1153 lzip
-cd "$file" > "$(basename -- $file .lz)"
1158 xz
-cd "$file" > "$(basename -- $file .xz)"
1162 warn
"${PROGRAM}: cannot unpack ${file}: Unsupported extension"
1171 # Extract information from the recipe to create the meta file.
1173 # The package description is pre-formatted in 78 columns,
1174 # the '#' character and a space is added as prefix to conform
1175 # 80 columns in total
1178 $(echo "$description" | fold -w 78 | awk '$0="# " $0')
1180 QICFLAGS="$QICFLAGS"
1181 QICXXFLAGS="$QICXXFLAGS"
1182 QILDFLAGS="$QILDFLAGS"
1186 blurb="$(echo "$description" | sed -e '/^$/d;2q')"
1187 homepage="$homepage"
1198 packagedir
=@PACKAGEDIR@
1199 targetdir
=@TARGETDIR@
1200 blacklist
="perl graft tarlz plzip musl glibc"
1202 RCFILE
=@SYSCONFDIR@
/qirc
1203 opt_install
=opt_install.off
1204 opt_update
=opt_update.off
1205 opt_force
=opt_force.off
1206 opt_keep
=opt_keep.off
1207 opt_incr_release
=opt_incr_release.off
1208 opt_skipqsts
=opt_skipqsts.off
1209 opt_nopkg
=opt_nopkg.off
1210 opt_prune
=opt_prune.off
1217 _isUpgrade
=_isUpgrade.off
1218 TMPDIR
="${TMPDIR:=/usr/src/qi/build}"
1219 QICFLAGS
="${QICFLAGS:=-g0 -Os}"
1220 QICXXFLAGS
="${QICXXFLAGS:=$QICFLAGS}"
1221 QILDFLAGS
="${QILDFLAGS:=-s}"
1222 worktree
=/usr
/src
/qi
1223 tardir
=${worktree}/sources
1224 outdir
=/var
/cache
/qi
/packages
1225 netget
="wget -c -w1 -t3 --no-check-certificate"
1226 rsync
="rsync -v -a -L -z -i --progress"
1227 configure_args
="--prefix=@PREFIX@ --libexecdir=@LIBEXECDIR@ --bindir=@BINDIR@ --sbindir=@SBINDIR@ --sysconfdir=@SYSCONFDIR@ --localstatedir=@LOCALSTATEDIR@"
1232 # Store (default) directory locations
1233 QI_TARGETDIR
=$targetdir
1234 QI_PACKAGEDIR
=$packagedir
1235 QI_WORKTREE
=$worktree
1241 while getopts :bcdiouwxLNP
:t
:fkvO
:W
:Z
:a
:j
:1nSpr
:hV name
1264 if test "$mode" = build_mode
1266 opt_install
=opt_install
1278 if test "$mode" = build_mode
1280 opt_update
=opt_update
1291 "QI_TARGETDIR=$QI_TARGETDIR" \
1292 "QI_PACKAGEDIR=$QI_PACKAGEDIR" \
1293 "QI_WORKTREE=$QI_WORKTREE" \
1294 "QI_TARDIR=$QI_TARDIR" \
1295 "QI_OUTDIR=$QI_OUTDIR"
1302 packagedir
="$OPTARG"
1314 verbose
=$
(( verbose
+ 1 ))
1332 opt_incr_release
=opt_incr_release
1338 opt_skipqsts
=opt_skipqsts
1355 warn
"Option '-${OPTARG}' requires an argument"
1360 warn
"Illegal option -- '-${OPTARG}'"
1366 shift $
(( OPTIND
- 1 ))
1374 # Program sanity check
1375 for need
in awk basename chmod cp dirname find fold graft
grep \
1376 mkdir mktemp
rm rmdir sed sha256sum stat tarlz
1378 if ! type $need 1> /dev
/null
2> /dev
/null
1380 warn
"${PROGRAM}: Prerequisite \`${need}' not found in PATH"
1386 # Determine verbosity level/flag
1388 if test "$verbose" -gt 1
1393 # Read standard input if FILE is -, or when FILE
1394 # is not connected to a terminal.
1396 if test "$1" = - ||
test ! -t 0
1398 # Unset positional parameters setting $# to zero
1401 # Assign remaining arguments to the positional parameters
1404 set -- "$@" "$input"
1408 # We need at least one operating mode
1415 umask 022; # Remove write permission for group and other.
1417 # Validate 'packagedir' and 'targetdir' as canonical directories
1419 # The single slash '/' does not qualify here
1420 if ! fnmatch
'/?*' "$packagedir"
1422 warn
"${PROGRAM}: Package directory \`${packagedir}' is not fully qualified"
1425 if test ! -d "$packagedir"
1427 warn
"${PROGRAM}: Package directory \`${packagedir}' does not exist"
1431 # The single slash '/' is valid here
1432 if ! fnmatch
'/*' "$targetdir"
1434 warn
"${PROGRAM}: Target directory \`${targetdir}' is not fully qualified"
1437 if test ! -d "$targetdir"
1439 warn
"${PROGRAM}: Target directory \`${targetdir}' does not exist"
1443 # Validate 'rootdir' directory
1445 if test -n "$rootdir"
1447 if test -d "$rootdir" && test "$rootdir" != /
1449 # Remove slash from the end
1450 rootdir
="${rootdir%/}"
1452 # A workaround for graft-2.13+. The specified directory is
1453 # relative to the log file, we prepend it inside the rootdir
1455 eval "$(graft -L)" ; GRAFT_LOGFILE
="${GRAFT_LOGFILE:=/var/log/graft}"
1456 mkdir
-p -- "$rootdir$(dirname -- $GRAFT_LOGFILE)" || chkstatus_or_exit
1458 # Compose 'rootdir' and log file option to be used with graft(1)
1459 graft_r
="-r $rootdir -l $GRAFT_LOGFILE"
1461 # Unset variables coming from eval
1462 unset GRAFT_PERL GRAFT_LOGFILE GRAFT_TARGETDIR GRAFT_PACKAGEDIR
1464 warn
"${PROGRAM}: Root directory \`${rootdir}' is not fully qualified"
1470 # Ensure 'TMPDIR' creation to prefix temporary files
1472 if test ! -d "$TMPDIR"
1474 mkdir
-p -- "$TMPDIR" || chkstatus_or_exit
1478 # Process each package or recipe provided on the command-line