diff options
38 files changed, 1527 insertions, 469 deletions
@@ -4,10 +4,12 @@ packages: - gcc - bison - curl + - fossil - rsync - - shellcheck + - emacs-nox - texinfo - gzip + - xz tasks: - install-pax: | git clone --quiet https://github.com/carbslinux/otools @@ -19,13 +21,17 @@ tasks: git clone --quiet https://git.sr.ht/~mcf/b3sum cd b3sum sudo make PREFIX=/usr install + - install-shellspec: | + curl -fsSL https://git.io/shellspec | sudo sh -s -- -y -p /usr + - install-shellcheck: | + curl -fsLo- https://github.com/koalaman/shellcheck/releases/download/v0.8.0/shellcheck-v0.8.0.linux.x86_64.tar.xz | pax -Jr + sudo install -Dm755 shellcheck-v0.8.0/shellcheck /usr/bin/shellcheck - build: | cd cpt ./configure make - test: | cd cpt - curl -fsSL https://git.io/shellspec | sudo sh -s -- -y -p /usr make test triggers: - action: email diff --git a/.fossil-settings/ignore-glob b/.fossil-settings/ignore-glob index 98cdf27..84a1fc5 100644 --- a/.fossil-settings/ignore-glob +++ b/.fossil-settings/ignore-glob @@ -18,3 +18,4 @@ tests/etc/cpt-hook report coverage config.mk +docs/config.org diff --git a/CHANGELOG.md b/CHANGELOG.md index 5719efd..e7ee439 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,122 @@ this project _somewhat_ adheres to [Semantic Versioning]. [Semantic Versioning]: https://semver.org/spec/v2.0.0.html -[Unreleased] +[7.0.2] - 2023-02-05 +-------------------------------------------------------------------------------- + +### Fixed +- Fixed a bug that caused extra dependencies being added to the later packages + during multi-package build operations. +- Fixed file attribute issue with the `_tmp_cp()` function causing modified + dependency files to receive `600` permission bits. + + +[7.0.1] - 2023-02-05 +-------------------------------------------------------------------------------- + +### Fixed +- Made the `_tsort()` function compatible with POSIX +- Fixed dependency calculation issue in `pkg_depends()` where some packages + would be removed. +- Fixed `pkg_gentree()` not generating the dependency tree due to the dependency + calculation changes. + + +[7.0.0] - 2023-01-31 +-------------------------------------------------------------------------------- + +### Configuration Directory +- In order to simplify file locations and messing up the `/etc` directory, CPT + now uses the `/etc/cpt` directory for reading related files. The location of + your system configuration directory is defined by the `--sysconfdir` flag in + the `./configure` script, it uses `/etc` if the prefix is `/usr`. +- Since the location of the configuration can differ between installations, + `$cpt_confdir` variable can be used in programs using `cpt-lib` to get the + user's configuration directory. +- This change currently doesn't break `cpt-base`, but you are advised to + rename your configuration files. +- `/etc/cpt-base` is renamed to `/etc/cpt/base` (considering `$cpt_confdir` is + `/etc/cpt`) + +### Changes on hook behaviour +- `/etc/cpt-hook` will no longer be used. +- User hooks (as defined by `$CPT_HOOK` will be run regardless of the hook type. + I have realised that overriding user hooks on some operations was a mistake. + If the users already have the privilege to install packages, they should also + be able to run hooks without an interruption of the package manager. +- Even though `/etc/cpt-hook` file is removed, a collection of systemwide hooks + can be added to the `/etc/cpt/hooks`directory. Any file in this directory will + be sourced by the package manager when running hooks. User hooks are run + _after_ systemwide hooks are run. +- Added new hooks: `end-install` and `end-remove` that are run when + installation/removal is complete (not per-package). + +### Added +- `cpt-size` can now sort files based on size. +- `$CPT_NOSTRIP` variable can now be set to 1 in order to disable package + stripping. Make sure to add `-g` to your CFLAGS in order to keep debugging + symbols. +- `cpt-build` now accepts `-d` and `-S` options to enable `$CPT_DEBUG` and + `$CPT_NOSTRIP` respectively. + +### Changed +- `cpt-update` is now re-entrant, meaning that it is no longer needed to run the + update twice, `cpt-update` will continue the updates with the new version of + itself. +- The package manager now can handle circular dependencies and exit gracefully. + +### Fixed +- Fixed the behaviour of `cpt bi` and `cpt cbi` by merging the flag usage. +- Fixed the `aria2c` usage on `pkg_download()` function. + +### Library +- In order to get the `$deps` variable, one now has to use the new + `pkg_depends_commit()` function. + + +[6.2.4] - 2022-02-07 +-------------------------------------------------------------------------------- + +### Fixed +- Fixed a bug in missing dependency where if the user had chosen 'ldd', it would + fail to fix dependencies due to a typo. + + +[6.2.3] - 2022-02-02 +-------------------------------------------------------------------------------- + +### Fixed +- Fixed a checksum verification bug where adding an extra source did not require + checksum verification. +- `cpt-manifest-tree` now modifies the output of `tree(1)` according to the new + version. +- `cpt-reset` is now much more verbose. +- Fixed the displayed messages on `cpt-install` when it is given a tarball as an + argument. +- Fixed a faulty implementation in `pkg_tar()` where it used `pkg_find()` + instead of using the built package's database directory for gathering + information. + + +[6.2.2] - 2021-11-09 +-------------------------------------------------------------------------------- + +### Fixed +- `cpt-alternatives` now properly logs file swaps even when the original file + no longer exists. +- Minor fixes + + +[6.2.1] - 2021-09-20 +-------------------------------------------------------------------------------- + +### Fixed +- `cpt-fork` follows symbolic links when forking packages. +- Fixed "crux-like" usage in `cpt-size` +- Fixed documentation path in the manual page + + +[6.2.0] - 2021-08-14 -------------------------------------------------------------------------------- ### BLAKE3 checksums @@ -27,6 +142,18 @@ built is already installed on the system `cpt` makes sure that the generated etcsums are also backwards compatible. +### Description searching + +`cpt-search` utility has a new mode for searching through the package names and +descriptions, which is enabled by the `-q` flag. The output is really similar to +how the `apt search` command works, but the output is not meant for scripting. +Descriptions are defined by the `description` keys in the meta file. + +Instead of wildcards, the passed argument is expected to be a POSIX Basic +Regular Expression, and is interpreted by `grep`. `cpt-search` also accepts the +`-F` flag for passing literal expressions. + + ### Added - `cpt-checksum` now has the `-s` flag to generate checksums using the SHA256 digest algorithm. @@ -35,6 +162,7 @@ etcsums are also backwards compatible. - `cpt-chroot` now has the flag `-m` to disable mounting/unmounting pseudo filesystems. - This changelog is now installed by the `Makefile`. +- `cpt-chbuild` now has `-r` flag to redownload the chroot. ### Changed - `cpt-size` has been rewritten to support POSIX `du`, and to support packages @@ -42,7 +170,7 @@ etcsums are also backwards compatible. - Installation now requires to run `./configure`. -6.1.1 - 2021-08-04 +[6.1.1] - 2021-08-04 -------------------------------------------------------------------------------- ### Fixed @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2020-2021 Cem Keylan +Copyright (c) 2020-2023 Cem Keylan Copyright (c) 2019-2020 Dylan Araps Permission is hereby granted, free of charge, to any person obtaining a copy @@ -1,5 +1,5 @@ # Carbs Packaging Tools --include config.mk +include config.mk INSTALL_SH = ./tools/install.sh CONTRIB = `find contrib -name 'cpt*' ! -name '*.*'` @@ -7,13 +7,13 @@ SRC = `find src -name 'cpt*' ! -name '*.*'` BIN = ${SRC} ${CONTRIB} all: src/cpt-lib - @if ! [ -e config.mk ]; then echo "Please run './configure'"; exit 1; fi @test "${DOCS}" != yes || ${MAKE} -C docs all src/cpt-lib: src/cpt-lib.in sed -n '/^Copyright/{s,^, ",;s,$$," \\,;p}' LICENSE | \ sed -e '/@LICENSE@/r /dev/stdin' \ -e '/@LICENSE@/d' \ + -e 's|@SYSCONFDIR@|${SYSCONFDIR}|g' \ -e "s|@VERSION@|${VERSION}|g" \ -e "s|@DOCSTRING@|Call functions from the library|g" src/cpt-lib.in > $@ chmod 755 $@ @@ -22,7 +22,10 @@ shellspec: all tests/etc/cpt-hook shellspec shellcheck: all - cd src; find . ../contrib -name 'cpt*' ! -name '*.*' -exec shellcheck -e 2119 -x -f gcc {} + + @cd src; find . ../contrib -name 'cpt*' ! -name '*.*' | while read -r file; do \ + echo SHELLCHECK "$$file"; \ + shellcheck -e 2119 -x -f gcc "$$file"; \ + done test: shellspec shellcheck @@ -30,15 +33,14 @@ tests/etc/cpt-hook: ln -s ../hook-file $@ dist: docs/cpt.info - @if ! [ -e config.mk ]; then echo "Please run './configure'"; exit 1; fi ./tools/mkdist.sh "${VERSION}" install: all test "${DOCS}" != yes || ${MAKE} -C docs install [ -f docs/cpt.info ] && \ - ${INSTALL_SH} -Dm644 docs/cpt.info ${DESTDIR}${INFODIR}/cpt.info + ${INSTALL_SH} -Dm644 docs/cpt.info ${DESTDIR}${INFODIR}/cpt.info ||: [ -f docs/cpt.txt ] && \ - ${INSTALL_SH} -Dm644 docs/cpt.txt ${DESTDIR}${DOCDIR}/cpt.txt + ${INSTALL_SH} -Dm644 docs/cpt.txt ${DESTDIR}${DOCDIR}/cpt.txt ||: ${INSTALL_SH} -Dm644 CHANGELOG.md ${DESTDIR}${DOCDIR}/CHANGELOG ${INSTALL_SH} -Dm755 -t ${DESTDIR}${BINDIR} ${BIN} ${INSTALL_SH} -Dm644 -t ${DESTDIR}${MAN1} man/*.1 @@ -28,6 +28,7 @@ usage() { "Options:" \ " --prefix=dir Set prefix directory" \ " --bindir=dir User executables [PREFIX/bin]" \ + " --sysconfdir=dir System configuration directory [PREFIX/etc]" \ " --datarootdir=dir Data root directory [PREFIX/share]" \ " --mandir=dir Manual pages [DATAROOTDIR/man]" \ " --infodir=dir info documentation [DATAROOTDIR/info]" \ @@ -43,6 +44,7 @@ prefix=/usr/local # We don't want expansion # shellcheck disable=2016 { +sysconfdir='$(PREFIX)/etc' bindir='$(PREFIX)/bin' datarootdir='$(PREFIX)/share' mandir='$(DATAROOTDIR)/man' @@ -54,8 +56,9 @@ docs=auto for arg; do case $arg in -h|--help) usage ;; - --prefix=*) prefix=${arg#*=} ;; - --bindir=*) bindir=${arg#*=} ;; + --prefix=*) prefix=${arg#*=} ;; + --bindir=*) bindir=${arg#*=} ;; + --sysconfdir=*) sysconfdir=${arg#*=} ;; --mandir=*) mandir=${arg#*=} ;; --infodir=*) infodir=${arg#*=} ;; --docdir=*) docdir=${arg#*=} ;; @@ -68,6 +71,10 @@ for arg; do esac done +# If the prefix is /usr and sysconfdir is not modified, make it /etc +# shellcheck disable=2016 +[ "$prefix" = /usr ] && [ "$sysconfdir" = '$(PREFIX)/etc' ] && sysconfdir=/etc + trap 'rm -f config.mk' EXIT trap 'rm -f config.mk; exit 1' INT @@ -78,6 +85,7 @@ out "starting configuration..." cat <<EOF > config.mk PREFIX = $prefix BINDIR = $bindir +SYSCONFDIR = $sysconfdir DATAROOTDIR = $datarootdir MANDIR = $mandir INFODIR = $infodir @@ -107,6 +115,7 @@ out "DOCS = $docs" >>config.mk out "checking runtime dependencies" _check pax rsync sed awk grep b3sum _check_multi "sha256 provider" sha256sum sha256 openssl +_check_multi "download utility" curl wget wget2 aria2c axel trap - EXIT INT out "written config.mk" "Run 'make' to build cpt" diff --git a/contrib/cpt-cat b/contrib/cpt-cat index bfc9d66..45e0dd9 100755 --- a/contrib/cpt-cat +++ b/contrib/cpt-cat @@ -28,23 +28,29 @@ ## .Em version ## files. -case "$1" in - --help|-h) - printf 'usage: %s [pkg] [file...]\n' "${0##*/}" - exit 0 - ;; - '') - [ "$#" -gt 1 ] && shift - set -- "${PWD##*/}" "$@" -esac +parser_definition() { + setup REST help:usage -- "usage: ${0##*/} [pkg] [file...]" + global_options silent +} + +# shellcheck disable=1091 +. cpt-lib + +[ "$1" ] || { + # Usage such as `cpt-cat '' build` is also valid. + [ "$#" -gt 1 ] && shift + set -- "${PWD##*/}" "$@" +} pkg=$1; shift -cpt-list "$pkg" >/dev/null +pkg_list "$pkg" >/dev/null [ "$1" ] || set -- build depends sources version +# $sys_db and color variables are defined by cpt-lib +# shellcheck disable=2154 for file; do - [ -f "$CPT_ROOT/var/db/cpt/installed/$pkg/$file" ] || continue - printf '\033[1m%s:\033[m\n' "$file" >&2 - cat "$CPT_ROOT/var/db/cpt/installed/$pkg/$file" + [ -f "$sys_db/$pkg/$file" ] || continue + printf '%b%s:%b\n' "$colbold" "$file" "$colre" >&2 + cat "$sys_db/$pkg/$file" done diff --git a/contrib/cpt-chbuild b/contrib/cpt-chbuild index a58d2ee..7a71c33 100755 --- a/contrib/cpt-chbuild +++ b/contrib/cpt-chbuild @@ -3,6 +3,7 @@ ## SYNOPSIS: ## .Nm +## .Op Fl r ## .Op Ar pkg... ## DESCRIPTION: @@ -13,13 +14,19 @@ ## exist in the user's cache directory, it will download it from the Carbs Linux ## website. If any packages are given as arguments, ## .Nm -## will install those packages to this temporary chroot. +## will install those packages to this temporary chroot. If the +## .Fl r +## flag is given, +## .Nm +## will remove the rootfs tarball and directory to download it again. -case "$1" in - --help|-h) printf '\033[1;33m-> \033[m%s\n' "usage: ${0##*/} [pkg...]"; exit 0 -esac +parser_definition() { + setup REST help:usage -- "usage: ${0##*/} [-r] [pkg...]" + flag redownload -r hidden:1 + global_options silent +} -# shellcheck disable=1091 +# shellcheck source=../src/cpt-lib . cpt-lib die() { @@ -36,23 +43,34 @@ url="https://dl.carbslinux.org/releases/${arch:-$(uname -m)}/carbs-rootfs.tar.xz cd "${cac_dir:?}" +# Remove the existing tarball and the chroot directory, so that they can be +# downloaded again. +[ "$redownload" ] && as_root rm -rf carbs-rootfs.tar.xz \ + carbs-rootfs.tar.xz.sum \ + carbs-chroot + [ -f carbs-rootfs.tar.xz ] || { log "Downloading chroot tarball" - curl -fLO "$url" + pkg_download "$url" } [ -f carbs-rootfs.tar.xz.sum ] || { log "Downloading checksums" - curl -fLo- "${url%/*}/sha256sums.txt" | - grep ' carbs-rootfs.tar.xz$' > carbs-rootfs.tar.xz.sum + pkg_download "$url.sha256" carbs-rootfs.tar.xz.sum } -log "Verifying checksums" -sh256 carbs-rootfs.tar.xz | diff - carbs-rootfs.tar.xz.sum || - die "Checksum verification failed" - +# We don't want to create the rootfs as a non-priviliged user, because there may +# arise certain problems if the files inside the chroot don't belong to root. +[ "$uid" = 0 ] || { + as_root "$0" "$@" + exit $? +} [ -d carbs-chroot ] || { + log "Verifying checksums" + sh256 carbs-rootfs.tar.xz | diff - carbs-rootfs.tar.xz.sum || + die "Checksum verification failed" + log "Extracting chroot" mkdir -p carbs-chroot (cd carbs-chroot; xz -cd ../carbs-rootfs.tar.xz | pax -r) @@ -61,7 +79,8 @@ sh256 carbs-rootfs.tar.xz | diff - carbs-rootfs.tar.xz.sum || mkdir -p "${tmp_dir:?}" log "Creating temporary chroot" -cp -a carbs-chroot "${chr_dir:=$tmp_dir/chroot}" +mkdir -p "${chr_dir:=$tmp_dir/chroot}" +rsync -a carbs-chroot/ "$chr_dir" [ "$1" ] && { log "Installing extra packages" @@ -71,9 +90,5 @@ cp -a carbs-chroot "${chr_dir:=$tmp_dir/chroot}" run_hook pre-chroot "" "$chr_dir" log "Entering chroot" -if [ "$(id -u)" -eq 0 ]; then - cpt-chroot "$chr_dir" - rm -rf "$chr_dir" -else - as_root sh -c "cpt-chroot $chr_dir; rm -rf $chr_dir" -fi +cpt-chroot "$chr_dir" +rm -rf "$chr_dir" diff --git a/contrib/cpt-export b/contrib/cpt-export index d90fb6f..478ae27 100755 --- a/contrib/cpt-export +++ b/contrib/cpt-export @@ -12,41 +12,29 @@ ## .Nm ## will use the name of the current directory as the package. -case "$1" in - --help|-h) - printf 'usage: %s [pkg]\n' "${0##*/}" - exit 0 - ;; - '') set -- "${PWD##*/}" -esac - -cpt-list "$1" >/dev/null -db=$CPT_ROOT/var/db/cpt/installed - -# Grab the package's version.. -read -r ver rel 2>/dev/null < "$db/$1/version" - ### Environment variables: ### The compression method can be changed while creating a tarball, using the ### .Ev CPT_COMPRESS ### environment variable. -# Fallback to gzip if there is a typo -case "$CPT_COMPRESS" in bz2|gz|xz|zst|lz) ;; *) CPT_COMPRESS=gz; esac -# Reset the argument list. +parser_definition() { + setup REST help:usage -- "usage: ${0##*/} [pkg]" + global_options silent +} + +# shellcheck disable=1091 +. cpt-lib + +[ "$1" ] || set -- "${PWD##*/}" + +(pkg_list "$1" >/dev/null) + +# Grab the package's version.. +read -r ver rel 2>/dev/null < "$sys_db/$1/version" + tarball="$PWD/$1#$ver-$rel.tar.$CPT_COMPRESS" # Turn the list of files back into a package. cd "$CPT_ROOT/" -sed 's/^/./' "$db/$1/manifest" | -pax -wd | - -case "$CPT_COMPRESS" in - bz2) bzip2 -z ;; - gz) gzip -6 ;; - xz) xz -zT 0 ;; - zst) zstd -3 ;; - lz) lzip -6 ;; -esac > "$tarball" - -printf 'tarball created in %s\n' "$tarball" +sed 's/^/./' "$sys_db/$1/manifest" | pax -wd | compress > "$tarball" +out "tarball created in $tarball" diff --git a/contrib/cpt-fork b/contrib/cpt-fork index 21e1618..733c942 100755 --- a/contrib/cpt-fork +++ b/contrib/cpt-fork @@ -31,12 +31,12 @@ for pkg; do case "$pkg" in */*) [ -d "$pkg" ] || die "$pkg is not a directory" - cp -r "$pkg" . + cp -Hr "$pkg" . pkg=${pkg##*/} ;; *) cpt-search "$pkg" >/dev/null - cp -r "$(cpt-search --single "$pkg")" . + cp -Hr "$(cpt-search --single "$pkg")" . esac # Sometimes forked packages are from the database and not from a repository. diff --git a/contrib/cpt-manifest-tree b/contrib/cpt-manifest-tree index 599c0bd..bc38bc2 100755 --- a/contrib/cpt-manifest-tree +++ b/contrib/cpt-manifest-tree @@ -21,4 +21,4 @@ case "$1" in esac cpt-list "$1" >/dev/null printf '%s\n' "[$1]:" -tree -C --fromfile "$CPT_ROOT/var/db/cpt/installed/$1/manifest" | sed 1,2d +tree -C --fromfile "$CPT_ROOT/var/db/cpt/installed/$1/manifest" | sed 1d diff --git a/contrib/cpt-owns b/contrib/cpt-owns index 9bc7d77..1f362d7 100755 --- a/contrib/cpt-owns +++ b/contrib/cpt-owns @@ -40,7 +40,7 @@ case "$1" in esac # Strip 'CPT_ROOT' from the file path if passed and follow symlinks. -file="${1#$CPT_ROOT}" +file="${1#"$CPT_ROOT"}" dirname=$(_readlinkf "$CPT_ROOT/${file%/*}") file="$dirname/${file##*/}" diff --git a/contrib/cpt-reset b/contrib/cpt-reset index 2a1b66e..a2336e3 100755 --- a/contrib/cpt-reset +++ b/contrib/cpt-reset @@ -3,6 +3,7 @@ # # Disable word-splittng warnings as they're safe here. # shellcheck disable=SC2046 +# shellcheck source=../src/cpt-lib ## SYNOPSIS: ## .Nm @@ -11,10 +12,10 @@ ## removes all packages from the system that is not defined as a base package in ## .Pa /etc/cpt-base . -[ "$1" ] && { - printf 'usage: %s\n\nRemove all packages not defined in the base.\n' \ - "${0##*/}" - exit 0 +parser_definition() { + setup REST help:usage -- "usage: ${0##*/}" + global_options compact + msg -- '' "Remove all packages outside of base definition" } . cpt-lib @@ -27,9 +28,14 @@ set +f; for pkg in *; do contains "$base" "$pkg" || set -- "$pkg" "$@" done -[ "$1" ] && { - printf 'WARNING: This will remove \033[1m%s\033[m package(s).\n' "$#" - printf 'Base packages can be redefined in %s\n' "$CPT_ROOT/etc/cpt-base" - printf 'Continue? [Enter/Ctrl+C]\n' - read -r _ && CPT_FORCE=1 cpt-remove "$@" +[ -z "$1" ] && { + log "No package outside of the base definition could be found, not continuing." + exit 0 } + +warn "" "This is a potentially harmful operation, do NOT continue unless" +warn "" "you know exactly what you are doing. Continuing will remove $#" +warn "" "packages that are not listed in the base definition or that the" +warn "" "base packages don't depend on. See the CPT BASE section on the" +warn "" "user manual to learn more." +prompt && cpt-remove "$@" diff --git a/contrib/cpt-size b/contrib/cpt-size index 4b983a1..22a77b7 100755 --- a/contrib/cpt-size +++ b/contrib/cpt-size @@ -3,6 +3,7 @@ ## SYNOPSIS: ## .Nm +## .Op Fl st ## .Op Ar pkg... ## DESCRIPTION: @@ -10,9 +11,21 @@ ## calculates the sizes of given ## .Ar packages ## using the files from the package manifest and outputs a total size of the -## packages along with all the files associated with them. +## packages along with all the files associated with them. If no arguments have +## been given, +## .Nm +## will use the name of the current directory as an argument. +## .Pp +## The options are as follows: +## .Bl -tag -width 13n +## .It Fl s +## Sort the output by size. +## .It Fl t +## Output only the size of given packages and not individual files. parser_definition() { - setup REST help:usage -- "usage: ${0##*/} [pkg...]" + setup REST help:usage -- "usage: ${0##*/} [-st] [pkg...]" + flag sort -s hidden:1 + flag total -t hidden:1 disp :usage -h --help hidden:1 } @@ -20,14 +33,28 @@ parser_definition() { # shellcheck disable=1091 . cpt-lib +# Use the current directory if no arguments have been given. +[ "$1" ] || set -- "${PWD##*/}" + # Ensure that all the packages given as arguments are installed. pkg_list "$@" >/dev/null mkdir -p "$tmp_dir" # We don't immediately pipe into awk as we want to exit in an error. -for pkg; do sed '/\/$/d;s/./\\&/g' "$sys_db/$pkg/manifest"; done | - xargs du -k > "$tmp_dir/size" +if [ "$total" ]; then + for pkg; do + sed '/\/$/d;s/./\\&/g' "$sys_db/$pkg/manifest" | + xargs du -k | + awk -v name="$pkg" '{size+=$1}END{printf("%s %s\n", size, name)}' >> "$tmp_dir/size" + done +else + for pkg; do sed '/\/$/d;s/./\\&/g' "$sys_db/$pkg/manifest"; done | + xargs du -k > "$tmp_dir/size" +fi + +# Do a numerical sort on the file if requested. +[ "$sort" ] && sort -no "$tmp_dir/size" "$tmp_dir/size" # This awk function formats the `du` output similar to the '-hc' flags. We # could have used a shell `while read` loop to do the exact same thing, but that diff --git a/docs/Makefile b/docs/Makefile index 3a7dbf0..7c5a431 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -4,6 +4,13 @@ INSTALL_SH = ../tools/install.sh all: cpt.txt cpt.texi cpt.info +config.org: ../config.mk + printf '%s\n' '#+MACRO: version $(VERSION)' \ + '#+MACRO: conf_dir (eval (concat "=$(SYSCONFDIR)/cpt/" $$1 "="))' \ + '#+MACRO: data_dir (eval (concat "=$(DATAROOTDIR)/" $$1 "="))' > config.org + +cpt.txt cpt.texi cpt.info: cpt.org config.org + .SUFFIXES: .info .texi .org .txt .org.texi: rm -f $@ @@ -20,6 +27,6 @@ clean: rm -f cpt.info all-clean: clean - rm -f cpt.texi cpt.txt + rm -f cpt.texi cpt.txt config.org .PHONY: all clean all-clean install uninstall diff --git a/docs/cpt.org b/docs/cpt.org index 263c547..b3fc97b 100644 --- a/docs/cpt.org +++ b/docs/cpt.org @@ -6,12 +6,14 @@ #+TEXINFO_DIR_CATEGORY: Development #+TEXINFO_DIR_TITLE: Carbs Packaging Tools: (cpt) #+TEXINFO_DIR_DESC: Carbs Package Management Library +#+INCLUDE: ./config.org #+OPTIONS: html-scripts:nil todo:nil #+MACRO: index (eval (format (if (org-export-derived-backend-p org-export-current-backend 'texinfo) "%s Index\n:PROPERTIES:\n:INDEX: %s\n:DESCRIPTION: %ss mentioned in this manual\n:END:\n" "%s%s%s :noexport:\n") $1 $2 $1)) This is a reference document containing both the user-guide and the development -manual for *Carbs Packaging Tools*. For development logs see [[https://git.carbslinux.org/cpt][the git repository]]. +manual for *Carbs Packaging Tools* version {{{version}}}. For development logs +see [[https://fossil.carbslinux.org/cpt][the fossil repository]]. * Copying :PROPERTIES: @@ -40,6 +42,10 @@ it revolves around the shell library =cpt-lib=, and many tools that wrap around it. This document aims to document both the usage of the distributed tools and document the library functions. +If you happen to find something that is not properly covered by the +documentation, or an area that can be improved, please feel free to submit a +patch, or [[https://fossil.carbslinux.org/cpt/tktnew][open a ticket]]. + * Usage :PROPERTIES: :DESCRIPTION: Basic usage of Carbs Packaging Tools @@ -109,19 +115,32 @@ man cpt-build The package manager does *NOT* have a configuration file, but there are a variety of ways in order to interact with and configure the package manager. -** CPT Base +** Configuration directory + +Some features of the package manager can be configured from the files found +under {{{conf_dir}}}. Even though this doesn't sound like the premise of "no +configuration" files, these files are completely optional to the package +manager, and still the majority of configuration is done through environment +variables. The files on this directory are for configuration that don't have a +big impact on how the package manager behaves, and are not feasible to be used +inside simple environment variables (such as the base package list and +package manager hooks). + +*** CPT Base :PROPERTIES: :DESCRIPTION: Defining base packages :END: -An =/etc/cpt-base= file can be used in order to define the base to the package -manager. Base packages are the packages that receive special treatment by -utilities such as =cpt-reset=, and =cpt-orphans=. +#+CINDEX: Base packages + +The file {{{conf_dir(base)}}} can be used in order to define the base to the +package manager. Base packages are the packages that receive special treatment +by utilities such as =cpt-reset=, and =cpt-orphans=. #+begin_example # This file defines the base packages of the system. You can add or remove # package names in order to redefine the base. This file will be used by -# cpt-orphans and cpt-reset. If this file doesn't exist on /etc/cpt-base, both +# cpt-orphans and cpt-reset. If this file doesn't exist on /etc/cpt/base, both # of the tools will assume that there is no defined base, so use with caution. baselayout binutils @@ -146,6 +165,13 @@ xz zlib #+end_example +*** Systemwide hooks + +A collection of hooks can be installed under {{{conf_dir(hooks/)}}}. All of the +files installed under this directory will then be sourced by the package manager +whenever a hook is called. Some examples for system hooks can be found under the +{{{data_dir(cpt/examples/hooks/)}}} directory. + ** Environment Variables :PROPERTIES: :DESCRIPTION: Change the behaviour of cpt through environment configuration @@ -209,6 +235,13 @@ to provide detailed information. #+VINDEX: CPT_KEEPLOG If set to 1, =cpt= will keep logs regardless of operation success. +- ~CPT_NOSTRIP~ :: + + #+VINDEX: CPT_NOSTRIP + If set to 1, =cpt= will not strip debug information from the binaries. Keep in + mind that your compiler already strips most debug information during + compilation, so you also need to add ~-g~ flag to your ~$C{XX}FLAGS~ + - ~CPT_PID~ :: #+VINDEX: CPT_PID @@ -370,8 +403,10 @@ There are a variety of package hooks, mostly self explanatory: - test-fail :: Run if the ~test~ script fails - pre-install :: Run before a package is installed for each package - post-install :: Run after a package is installed for each package +- end-install :: Run after all given packages are installed - pre-remove :: Run before a package is removed for each package - post-remove :: Run after a package is removed for each package +- end-remove :: Run after all given packages are removed - pre-fetch :: Run before all repositories are fetched - post-fetch :: Run after all repositories are fetched - post-package :: Run after a tarball for a package is created @@ -825,6 +860,52 @@ files: This will load the library inside your script, and will set some environment variables that are used inside the package manager. +** Variables + +This section lists some of the variables defined by the package manager that can +be used in scripts. These variables usually cannot be defined by the user, so +they are not part of the [[* Environment Variables][variables]] section above. + +#+VINDEX: cpt_version +- =$cpt_version= :: + Package manager version. +#+VINDEX: cpt_confdir +- =$cpt_confdir= :: + Location of the CPT system configuration directory. This is usually either + =/etc/cpt= or =PREFIX/etc/cpt=. +#+VINDEX: pkg_db +- =$pkg_db= :: + Location of the package database without the root (=var/db/cpt/installed=). +#+VINDEX: sys_db +#+VINDEX: CPT_ROOT +- =$sys_db= :: + Location of the package manager database, making use of the current + =$CPT_ROOT= (=$CPT_ROOT/$pkg_db=). This is the database you probably want to + use. +#+CINDEX: Base packages +#+VINDEX: cpt_base +- =$cpt_base= :: + Location of the file that defines the base packages. + +#+CINDEX: Scripts that use CPT cache directories +If for some reason, your script interacts with the directories created and +managed by the package manager you should use the following variables instead of +the user assigned variables such as =$CPT_CACHE= or =$CPT_TMPDIR=. The variables +below are the ones used for package operations (which are assigned by using a +combination of user-assigned values and their fallbacks). + +#+VINDEX: cac_dir +- =$cac_dir= :: + Cache directory used by the package manager. +- =$src_dir= :: + Directory containing downloaded sources for packages. +- =$log_dir= :: + Directory where logs are saved. +- =$bin_dir= :: + Directory where built package tarballs are saved. +- =$tmp_dir= :: + Temporary directory for the package manager operations. + ** Option parsing :PROPERTIES: :DESCRIPTION: Easy way of parsing options with cpt-lib @@ -871,13 +952,21 @@ inside. Here is the proper way of doing it. The =global_options()= function is a simple convenience call to include flags that can be used inside most =cpt= tools. It defines the following flags: -| Flag | Long Option | Calls | -|------+---------------+--------------| -| ~-f~ | ~--force~ | =CPT_FORCE= | -| ~-y~ | ~--no-prompt~ | =CPT_PROMPT= | -| | ~--root~ | =CPT_ROOT= | -| ~-h~ | ~--help~ | =usage()= | -| ~-v~ | ~--version~ | =version()= | +| Flag | Long Option | Calls | +|------+---------------+---------------| +| ~-f~ | ~--force~ | =CPT_FORCE= | +| ~-y~ | ~--no-prompt~ | =CPT_PROMPT= | +| | ~--root~ | =CPT_ROOT= | +| ~-h~ | ~--help~ | =usage()= | +| ~-v~ | ~--version~ | =version()= | +| | ~--verbose~ | =CPT_VERBOSE= | + +This function can take two arguments: + +- =silent= :: If this argument is specified, the function does not print the usage + information defined by its flags. +- =compact= :: If this argument is specified, the function only prints the help + output of the ~--help~ and ~--version~ flags. ** Message functions :PROPERTIES: @@ -1186,7 +1275,7 @@ SEARCH_PATH=$PATH pkg_find 'cpt-*' all -x :DESCRIPTION: List system base packages :END: -This function returns the base packages as defined in =/etc/cpt-base=. If an +This function returns the base packages as defined in the base file. If an optional argument is present, it will print all package names in a single line. If it is not given any arguments, it will return one package per line. See [[CPT Base]] for more information on base packages. diff --git a/docs/cpt.texi b/docs/cpt.texi index 7487209..e6b331c 100644 --- a/docs/cpt.texi +++ b/docs/cpt.texi @@ -41,7 +41,8 @@ the section entitled "GNU Free Documentation License." @top Carbs Packaging Tools This is a reference document containing both the user-guide and the development -manual for @strong{Carbs Packaging Tools}. For development logs see @uref{https://git.carbslinux.org/cpt, the git repository}. +manual for @strong{Carbs Packaging Tools} version Fossil. For development logs +see @uref{https://fossil.carbslinux.org/cpt, the fossil repository}. @end ifnottex @menu @@ -60,11 +61,16 @@ manual for @strong{Carbs Packaging Tools}. For development logs see @uref{https: Configuration -* CPT Base:: Defining base packages +* Configuration directory:: * Environment Variables:: Change the behaviour of cpt through environment configuration * Hooks:: Use hooks to customize the package manager operations * Editing the build file during pre-build:: Modify a package build with your hooks +Configuration directory + +* CPT Base:: Defining base packages +* Systemwide hooks:: + Environment Variables * @samp{CPT_PATH}:: Set the locations of your repositories @@ -97,6 +103,7 @@ Rsync Repositories CPT Library * Calling the library:: Including the library on your code +* Variables:: * Option parsing:: Easy way of parsing options with cpt-lib * Message functions:: Communicate to users * Text functions:: Manipulate or check text @@ -160,6 +167,10 @@ it revolves around the shell library @samp{cpt-lib}, and many tools that wrap ar it. This document aims to document both the usage of the distributed tools and document the library functions. +If you happen to find something that is not properly covered by the +documentation, or an area that can be improved, please feel free to submit a +patch, or @uref{https://fossil.carbslinux.org/cpt/tktnew, open a ticket}. + @node Usage @chapter Usage @@ -226,23 +237,42 @@ The package manager does @strong{NOT} have a configuration file, but there are a variety of ways in order to interact with and configure the package manager. @menu -* CPT Base:: Defining base packages +* Configuration directory:: * Environment Variables:: Change the behaviour of cpt through environment configuration * Hooks:: Use hooks to customize the package manager operations * Editing the build file during pre-build:: Modify a package build with your hooks @end menu +@node Configuration directory +@section Configuration directory + +Some features of the package manager can be configured from the files found +under @samp{/etc/cpt/}. Even though this doesn't sound like the premise of "no +configuration" files, these files are completely optional to the package +manager, and still the majority of configuration is done through environment +variables. The files on this directory are for configuration that don't have a +big impact on how the package manager behaves, and are not feasible to be used +inside simple environment variables (such as the base package list and +package manager hooks). + +@menu +* CPT Base:: Defining base packages +* Systemwide hooks:: +@end menu + @node CPT Base -@section CPT Base +@subsection CPT Base + +@cindex Base packages -An @samp{/etc/cpt-base} file can be used in order to define the base to the package -manager. Base packages are the packages that receive special treatment by -utilities such as @samp{cpt-reset}, and @samp{cpt-orphans}. +The file @samp{/etc/cpt/base} can be used in order to define the base to the +package manager. Base packages are the packages that receive special treatment +by utilities such as @samp{cpt-reset}, and @samp{cpt-orphans}. @example # This file defines the base packages of the system. You can add or remove # package names in order to redefine the base. This file will be used by -# cpt-orphans and cpt-reset. If this file doesn't exist on /etc/cpt-base, both +# cpt-orphans and cpt-reset. If this file doesn't exist on /etc/cpt/base, both # of the tools will assume that there is no defined base, so use with caution. baselayout binutils @@ -267,6 +297,14 @@ xz zlib @end example +@node Systemwide hooks +@subsection Systemwide hooks + +A collection of hooks can be installed under @samp{/etc/cpt/hooks/}. All of the +files installed under this directory will then be sourced by the package manager +whenever a hook is called. Some examples for system hooks can be found under the +@samp{/usr/share/cpt/examples/hooks/} directory. + @node Environment Variables @section Environment Variables @@ -319,6 +357,12 @@ Absolute path to the package manager hook file. @vindex CPT_KEEPLOG If set to 1, @samp{cpt} will keep logs regardless of operation success. +@item @code{CPT_NOSTRIP} +@vindex CPT_NOSTRIP +If set to 1, @samp{cpt} will not strip debug information from the binaries. Keep in +mind that your compiler already strips most debug information during +compilation, so you also need to add @code{-g} flag to your @code{$C@{XX@}FLAGS} + @item @code{CPT_PID} @vindex CPT_PID Set the temporary build directory name. @@ -378,7 +422,7 @@ This example brings us to the next section of this document. @enumerate @item -Repository preferences +@anchor{Repository preferences}Repository preferences @cindex package conflicts @@ -396,7 +440,7 @@ CPT_PATH=$HOME/repos/personal:$HOME/repos/carbs/extra @end example @item -Setting the @samp{CPT_PATH} +@anchor{Setting the @samp{CPT_PATH}}Setting the @samp{CPT_PATH} You can set the @samp{CPT_PATH} variable on your shell configuration or your @@ -491,10 +535,14 @@ Run if the @code{test} script fails Run before a package is installed for each package @item post-install Run after a package is installed for each package +@item end-install +Run after all given packages are installed @item pre-remove Run before a package is removed for each package @item post-remove Run after a package is removed for each package +@item end-remove +Run after all given packages are removed @item pre-fetch Run before all repositories are fetched @item post-fetch @@ -967,6 +1015,7 @@ package manager library. @menu * Calling the library:: Including the library on your code +* Variables:: * Option parsing:: Easy way of parsing options with cpt-lib * Message functions:: Communicate to users * Text functions:: Manipulate or check text @@ -989,6 +1038,65 @@ files: This will load the library inside your script, and will set some environment variables that are used inside the package manager. +@node Variables +@section Variables + +This section lists some of the variables defined by the package manager that can +be used in scripts. These variables usually cannot be defined by the user, so +they are not part of the @ref{Environment Variables, , variables} section above. + +@vindex cpt_version +@table @asis +@item @samp{$cpt_version} +Package manager version. +@end table +@vindex cpt_confdir +@table @asis +@item @samp{$cpt_confdir} +Location of the CPT system configuration directory. This is usually either +@samp{/etc/cpt} or @samp{PREFIX/etc/cpt}. +@end table +@vindex pkg_db +@table @asis +@item @samp{$pkg_db} +Location of the package database without the root (@samp{var/db/cpt/installed}). +@end table +@vindex sys_db +@vindex CPT_ROOT +@table @asis +@item @samp{$sys_db} +Location of the package manager database, making use of the current +@samp{$CPT_ROOT} (@samp{$CPT_ROOT/$pkg_db}). This is the database you probably want to +use. +@end table +@cindex Base packages +@vindex cpt_base +@table @asis +@item @samp{$cpt_base} +Location of the file that defines the base packages. +@end table + +@cindex Scripts that use CPT cache directories +If for some reason, your script interacts with the directories created and +managed by the package manager you should use the following variables instead of +the user assigned variables such as @samp{$CPT_CACHE} or @samp{$CPT_TMPDIR}. The variables +below are the ones used for package operations (which are assigned by using a +combination of user-assigned values and their fallbacks). + +@vindex cac_dir +@table @asis +@item @samp{$cac_dir} +Cache directory used by the package manager. +@item @samp{$src_dir} +Directory containing downloaded sources for packages. +@item @samp{$log_dir} +Directory where logs are saved. +@item @samp{$bin_dir} +Directory where built package tarballs are saved. +@item @samp{$tmp_dir} +Temporary directory for the package manager operations. +@end table + @node Option parsing @section Option parsing @@ -1034,7 +1142,7 @@ parser_definition() @{ The @samp{global_options()} function is a simple convenience call to include flags that can be used inside most @samp{cpt} tools. It defines the following flags: -@multitable {aaaa} {aaaaaaaaaaaaa} {aaaaaaaaaaaa} +@multitable {aaaa} {aaaaaaaaaaaaa} {aaaaaaaaaaaaa} @headitem Flag @tab Long Option @tab Calls @@ -1053,8 +1161,22 @@ that can be used inside most @samp{cpt} tools. It defines the following flags: @item @code{-v} @tab @code{--version} @tab @samp{version()} +@item +@tab @code{--verbose} +@tab @samp{CPT_VERBOSE} @end multitable +This function can take two arguments: + +@table @asis +@item @samp{silent} +If this argument is specified, the function does not print the usage +information defined by its flags. +@item @samp{compact} +If this argument is specified, the function only prints the help +output of the @code{--help} and @code{--version} flags. +@end table + @node Message functions @section Message functions @@ -1373,7 +1495,7 @@ SEARCH_PATH=$PATH pkg_find 'cpt-*' all -x @node @samp{pkg_get_base()} @subsection @samp{pkg_get_base()} -This function returns the base packages as defined in @samp{/etc/cpt-base}. If an +This function returns the base packages as defined in the base file. If an optional argument is present, it will print all package names in a single line. If it is not given any arguments, it will return one package per line. See @ref{CPT Base} for more information on base packages. @@ -1399,7 +1521,7 @@ Print all packages in a single line instead of a package per line. @enumerate @item -Examples +@anchor{Examples}Examples This example uses the @samp{cpt} package for Carbs Linux. The package itself is diff --git a/docs/cpt.txt b/docs/cpt.txt index 8934c15..08433e9 100644 --- a/docs/cpt.txt +++ b/docs/cpt.txt @@ -1,10 +1,10 @@ - _______________________ + _______________________ - CARBS PACKAGING TOOLS - User Manual + CARBS PACKAGING TOOLS + User Manual - Cem Keylan - _______________________ + Cem Keylan + _______________________ Table of Contents @@ -14,7 +14,9 @@ _________________ 2. Preface 3. Usage 4. Configuration -.. 1. CPT Base +.. 1. Configuration directory +..... 1. CPT Base +..... 2. Systemwide hooks .. 2. Environment Variables ..... 1. `CPT_PATH' ..... 2. `CPT_COMPRESS' @@ -40,27 +42,28 @@ _________________ 7. Comparison Between CPT and KISS 8. CPT Library .. 1. Calling the library -.. 2. Option parsing +.. 2. Variables +.. 3. Option parsing ..... 1. Defining a parser ..... 2. `global_options()' -.. 3. Message functions +.. 4. Message functions ..... 1. `out()' ..... 2. `log()' ..... 3. `die()' ..... 4. `warn()' ..... 5. `prompt()' -.. 4. Text functions +.. 5. Text functions ..... 1. `contains()' ..... 2. `regesc()' ..... 3. `pop()' ..... 4. `sepchar()' -.. 5. Portability functions +.. 6. Portability functions ..... 1. `_seq()' ..... 2. `_stat()' ..... 3. `_readlinkf()' -.. 6. System Functions +.. 7. System Functions ..... 1. `as_root()' -.. 7. Package Functions +.. 8. Package Functions ..... 1. `pkg_build()' ..... 2. `pkg_depends()' ..... 3. `pkg_order()' @@ -74,11 +77,11 @@ _________________ This is a reference document containing both the user-guide and the -development manual for *Carbs Packaging Tools*. For development logs see -[the git repository]. +development manual for *Carbs Packaging Tools* version Fossil. For +development logs see [the fossil repository]. -[the git repository] <https://git.carbslinux.org/cpt> +[the fossil repository] <https://fossil.carbslinux.org/cpt> 1 Copying @@ -106,9 +109,15 @@ development manual for *Carbs Packaging Tools*. For development logs see aims to document both the usage of the distributed tools and document the library functions. + If you happen to find something that is not properly covered by the + documentation, or an area that can be improved, please feel free to + submit a patch, or [open a ticket]. + [kiss] <https://github.com/kisslinux/kiss> +[open a ticket] <https://fossil.carbslinux.org/cpt/tktnew> + 3 Usage ======= @@ -178,17 +187,31 @@ development manual for *Carbs Packaging Tools*. For development logs see package manager. -4.1 CPT Base -~~~~~~~~~~~~ +4.1 Configuration directory +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Some features of the package manager can be configured from the files + found under `/etc/cpt/'. Even though this doesn't sound like the + premise of "no configuration" files, these files are completely + optional to the package manager, and still the majority of + configuration is done through environment variables. The files on this + directory are for configuration that don't have a big impact on how + the package manager behaves, and are not feasible to be used inside + simple environment variables (such as the base package list and + package manager hooks). + - An `/etc/cpt-base' file can be used in order to define the base to the - package manager. Base packages are the packages that receive special - treatment by utilities such as `cpt-reset', and `cpt-orphans'. +4.1.1 CPT Base +-------------- + + The file `/etc/cpt/base' can be used in order to define the base to + the package manager. Base packages are the packages that receive + special treatment by utilities such as `cpt-reset', and `cpt-orphans'. ,---- | # This file defines the base packages of the system. You can add or remove | # package names in order to redefine the base. This file will be used by - | # cpt-orphans and cpt-reset. If this file doesn't exist on /etc/cpt-base, both + | # cpt-orphans and cpt-reset. If this file doesn't exist on /etc/cpt/base, both | # of the tools will assume that there is no defined base, so use with caution. | baselayout | binutils @@ -214,6 +237,16 @@ development manual for *Carbs Packaging Tools*. For development logs see `---- +4.1.2 Systemwide hooks +---------------------- + + A collection of hooks can be installed under `/etc/cpt/hooks/'. All of + the files installed under this directory will then be sourced by the + package manager whenever a hook is called. Some examples for system + hooks can be found under the `/usr/share/cpt/examples/hooks/' + directory. + + 4.2 Environment Variables ~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -262,6 +295,12 @@ development manual for *Carbs Packaging Tools*. For development logs see If set to 1, `cpt' will keep logs regardless of operation success. + `CPT_NOSTRIP' + If set to 1, `cpt' will not strip debug information from the + binaries. Keep in mind that your compiler already strips most + debug information during compilation, so you also need to add + `-g' flag to your `$C{XX}FLAGS' + `CPT_PID' Set the temporary build directory name. @@ -412,10 +451,14 @@ development manual for *Carbs Packaging Tools*. For development logs see Run before a package is installed for each package post-install Run after a package is installed for each package + end-install + Run after all given packages are installed pre-remove Run before a package is removed for each package post-remove Run after a package is removed for each package + end-remove + Run after all given packages are removed pre-fetch Run before all repositories are fetched post-fetch @@ -627,7 +670,7 @@ development manual for *Carbs Packaging Tools*. For development logs see mandatory file for submitting packages to Carbs Linux repositories. -[pkg_query_meta()] See section 8.7.10 +[pkg_query_meta()] See section 8.8.10 5.7 post-install @@ -878,7 +921,52 @@ development manual for *Carbs Packaging Tools*. For development logs see environment variables that are used inside the package manager. -8.2 Option parsing +8.2 Variables +~~~~~~~~~~~~~ + + This section lists some of the variables defined by the package + manager that can be used in scripts. These variables usually cannot be + defined by the user, so they are not part of the [variables] section + above. + + `$cpt_version' + Package manager version. + `$cpt_confdir' + Location of the CPT system configuration directory. This is + usually either `/etc/cpt' or `PREFIX/etc/cpt'. + `$pkg_db' + Location of the package database without the root + (`var/db/cpt/installed'). + `$sys_db' + Location of the package manager database, making use of the + current `$CPT_ROOT' (`$CPT_ROOT/$pkg_db'). This is the database + you probably want to use. + `$cpt_base' + Location of the file that defines the base packages. + + If for some reason, your script interacts with the directories created + and managed by the package manager you should use the following + variables instead of the user assigned variables such as `$CPT_CACHE' + or `$CPT_TMPDIR'. The variables below are the ones used for package + operations (which are assigned by using a combination of user-assigned + values and their fallbacks). + + `$cac_dir' + Cache directory used by the package manager. + `$src_dir' + Directory containing downloaded sources for packages. + `$log_dir' + Directory where logs are saved. + `$bin_dir' + Directory where built package tarballs are saved. + `$tmp_dir' + Temporary directory for the package manager operations. + + +[variables] See section 4.2 + + +8.3 Option parsing ~~~~~~~~~~~~~~~~~~ `cpt-lib' includes a POSIX-shell option parser inside named @@ -890,7 +978,7 @@ development manual for *Carbs Packaging Tools*. For development logs see [documentation] <https://github.com/ko1nksm/getoptions/blob/v2.5.0/README.md> -8.2.1 Defining a parser +8.3.1 Defining a parser ----------------------- Some functions are called and set automatically when you call @@ -917,29 +1005,39 @@ development manual for *Carbs Packaging Tools*. For development logs see `---- -8.2.2 `global_options()' +8.3.2 `global_options()' ------------------------ The `global_options()' function is a simple convenience call to include flags that can be used inside most `cpt' tools. It defines the following flags: - Flag Long Option Calls - ----------------------------------- - `-f' `--force' `CPT_FORCE' - `-y' `--no-prompt' `CPT_PROMPT' - `--root' `CPT_ROOT' - `-h' `--help' `usage()' - `-v' `--version' `version()' + Flag Long Option Calls + ------------------------------------ + `-f' `--force' `CPT_FORCE' + `-y' `--no-prompt' `CPT_PROMPT' + `--root' `CPT_ROOT' + `-h' `--help' `usage()' + `-v' `--version' `version()' + `--verbose' `CPT_VERBOSE' + + This function can take two arguments: + + `silent' + If this argument is specified, the function does not print the + usage information defined by its flags. + `compact' + If this argument is specified, the function only prints the help + output of the `--help' and `--version' flags. -8.3 Message functions +8.4 Message functions ~~~~~~~~~~~~~~~~~~~~~ `cpt' has various functions to print information to users. -8.3.1 `out()' +8.4.1 `out()' ------------- `out()' is a really simple function that prints messages to the @@ -954,7 +1052,7 @@ development manual for *Carbs Packaging Tools*. For development logs see `---- -8.3.2 `log()' +8.4.2 `log()' ------------- `log()' is the most commonly used message function in the package @@ -972,7 +1070,7 @@ development manual for *Carbs Packaging Tools*. For development logs see above. -8.3.3 `die()' +8.4.3 `die()' ------------- `die()' wraps the `log()' function and exits with an error (1). It @@ -980,14 +1078,14 @@ development manual for *Carbs Packaging Tools*. For development logs see function. The third argument for `log()' is set as `!>'. -8.3.4 `warn()' +8.4.4 `warn()' -------------- `warn()' is another function that wraps `log()'. In place of the third argument, it uses the word `WARNING'. -8.3.5 `prompt()' +8.4.5 `prompt()' ---------------- `prompt()' is an interactive function that waits for user input to @@ -997,14 +1095,14 @@ development manual for *Carbs Packaging Tools*. For development logs see `CPT_PROMPT' to 0. -8.4 Text functions +8.5 Text functions ~~~~~~~~~~~~~~~~~~ Following functions are used to manipulate, check, or interact with text. -8.4.1 `contains()' +8.5.1 `contains()' ------------------ `contains' function can be used to check whether a list variable @@ -1020,7 +1118,7 @@ development manual for *Carbs Packaging Tools*. For development logs see `---- -8.4.2 `regesc()' +8.5.2 `regesc()' ---------------- `regesc()' can be used to escape regular expression characters that @@ -1032,7 +1130,7 @@ development manual for *Carbs Packaging Tools*. For development logs see `---- -8.4.3 `pop()' +8.5.3 `pop()' ------------- `pop()' can be used to remove a word from a "string list" without a @@ -1046,7 +1144,7 @@ development manual for *Carbs Packaging Tools*. For development logs see `---- -8.4.4 `sepchar()' +8.5.4 `sepchar()' ----------------- This function can be used to separate characters from the given string @@ -1066,7 +1164,7 @@ development manual for *Carbs Packaging Tools*. For development logs see `---- -8.5 Portability functions +8.6 Portability functions ~~~~~~~~~~~~~~~~~~~~~~~~~ These helper functions are used so that we don't depend on non-POSIX @@ -1074,7 +1172,7 @@ development manual for *Carbs Packaging Tools*. For development logs see character. -8.5.1 `_seq()' +8.6.1 `_seq()' -------------- This function is similar to `seq(1)' except that it only takes a @@ -1088,7 +1186,7 @@ development manual for *Carbs Packaging Tools*. For development logs see `---- -8.5.2 `_stat()' +8.6.2 `_stat()' --------------- This function imitates `stat %U'. `stat' isn't defined by POSIX, and @@ -1096,7 +1194,7 @@ development manual for *Carbs Packaging Tools*. For development logs see file. If the owner cannot be found, it will return `root'. -8.5.3 `_readlinkf()' +8.6.3 `_readlinkf()' -------------------- This function was taken from [POSIX sh readlinkf library by Koichi @@ -1108,10 +1206,10 @@ development manual for *Carbs Packaging Tools*. For development logs see <https://github.com/ko1nksm/readlinkf> -8.6 System Functions +8.7 System Functions ~~~~~~~~~~~~~~~~~~~~ -8.6.1 `as_root()' +8.7.1 `as_root()' ----------------- `as_root()' calls the rest of the arguments as a different @@ -1128,7 +1226,7 @@ development manual for *Carbs Packaging Tools*. For development logs see `$CPT_SU' variable. -8.7 Package Functions +8.8 Package Functions ~~~~~~~~~~~~~~~~~~~~~ Obviously, package functions are the most important ones for @@ -1136,7 +1234,7 @@ development manual for *Carbs Packaging Tools*. For development logs see manipulate, or to otherwise interact with packages. -8.7.1 `pkg_build()' +8.8.1 `pkg_build()' ------------------- This function builds all given packages. It resolves dependencies for @@ -1154,7 +1252,7 @@ development manual for *Carbs Packaging Tools*. For development logs see `---- -8.7.2 `pkg_depends()' +8.8.2 `pkg_depends()' --------------------- This function calculates the dependencies for the requested package, @@ -1163,17 +1261,17 @@ development manual for *Carbs Packaging Tools*. For development logs see packages. -[pkg_order()] See section 8.7.3 +[pkg_order()] See section 8.8.3 -8.7.3 `pkg_order()' +8.8.3 `pkg_order()' ------------------- This function receives package names and returns `$order' and `$redro' variables that can be used for building and removing packages. -8.7.4 `pkg_owner()' +8.8.4 `pkg_owner()' ------------------- This function can be used to determine the owner of a package. The @@ -1194,7 +1292,7 @@ development manual for *Carbs Packaging Tools*. For development logs see `---- -8.7.5 `pkg_isbuilt()' +8.8.5 `pkg_isbuilt()' --------------------- This function returns with success when the given package has a built @@ -1202,7 +1300,7 @@ development manual for *Carbs Packaging Tools*. For development logs see repository. -8.7.6 `pkg_lint()' +8.8.6 `pkg_lint()' ------------------ This function checks whether a given package fits the proper package @@ -1210,7 +1308,7 @@ development manual for *Carbs Packaging Tools*. For development logs see outright* if it fails. -8.7.7 `pkg_find()' +8.8.7 `pkg_find()' ------------------ `pkg_find()' is the tool for searching packages. It accepts up to 3 @@ -1243,17 +1341,17 @@ development manual for *Carbs Packaging Tools*. For development logs see `---- -8.7.8 `pkg_get_base()' +8.8.8 `pkg_get_base()' ---------------------- - This function returns the base packages as defined in - `/etc/cpt-base'. If an optional argument is present, it will print all - package names in a single line. If it is not given any arguments, it - will return one package per line. See 4.1 for more information on base + This function returns the base packages as defined in the base + file. If an optional argument is present, it will print all package + names in a single line. If it is not given any arguments, it will + return one package per line. See 4.1.1 for more information on base packages. -8.7.9 `pkg_gentree()' +8.8.9 `pkg_gentree()' --------------------- This function generates a dependency tree for the given package. The @@ -1273,7 +1371,7 @@ development manual for *Carbs Packaging Tools*. For development logs see line. -* 8.7.9.1 Examples +* 8.8.9.1 Examples This example uses the `cpt' package for Carbs Linux. The package itself is listed to depend on `curl' and `rsync'. Here is the output @@ -1299,7 +1397,7 @@ development manual for *Carbs Packaging Tools*. For development logs see `---- -8.7.10 `pkg_query_meta()' +8.8.10 `pkg_query_meta()' ------------------------- This function is used to query the [meta file] inside package diff --git a/examples/hooks/clean-packages b/examples/hooks/clean-packages new file mode 100644 index 0000000..972eb31 --- /dev/null +++ b/examples/hooks/clean-packages @@ -0,0 +1,7 @@ +# -*- mode: sh; -*- +# clean-packages -- Remove documentation, and locales from packages. +case $TYPE in + post-build) + rm -rf "$DEST/usr/share/locale" \ + "$DEST/usr/share/doc" +esac diff --git a/examples/hooks/makewhatis b/examples/hooks/makewhatis new file mode 100644 index 0000000..5c38001 --- /dev/null +++ b/examples/hooks/makewhatis @@ -0,0 +1,10 @@ +# -*- mode: sh; -*- +# Run makewhatis if a manual page is installed, or removed +case $TYPE in + post-install|pre-remove) + grep -q "^/usr/share/man/" "$DEST/manifest" && + run_makewhatis=1 + ;; + end-install|end-remove) + [ "$run_makewhatis" ] && makewhatis "$DEST/usr/share/man" +esac diff --git a/man/cpt-build.1 b/man/cpt-build.1 index 1393aed..ec65eeb 100644 --- a/man/cpt-build.1 +++ b/man/cpt-build.1 @@ -5,7 +5,7 @@ .Nd build given packages .Sh SYNOPSIS .Nm cpt-build -.Op Fl tfy +.Op Fl dfSty .Op Fl -root Ar ROOT .Op Fl hv .Op Ar package... @@ -21,12 +21,28 @@ package. .Pp The options are as follows: .Bl -tag -width 13n +.It Fl d , -debug +Keep the build directories after operation .It Fl t , -test Run tests (if they exist) +.It Fl S , -nostrip +Don't strip debug information from the binaries +.Po +might want to add +.Fl g +to your +.Ev $CFLAGS +.Pc .It Fl f , -force Force operation .It Fl y , -no-prompt Do not prompt for confirmation +.It Fl -color Ar CPT_COLOR +Enable/disable output color +.Bo +.Sy auto , +always, never, 1, 0 +.Bc .It Fl -root Ar CPT_ROOT Use an alternate root directory .It Fl h , -help diff --git a/man/cpt-install.1 b/man/cpt-install.1 index 235c2df..dfd483c 100644 --- a/man/cpt-install.1 +++ b/man/cpt-install.1 @@ -23,6 +23,12 @@ The options are as follows: Force installation .It Fl y , -no-prompt Do not prompt for confirmation +.It Fl -color Ar CPT_COLOR +Enable/disable output color +.Bo +.Sy auto , +always, never, 1, 0 +.Bc .It Fl -root Ar CPT_ROOT Use an alternate root directory .It Fl h , -help diff --git a/man/cpt-remove.1 b/man/cpt-remove.1 index e4c2e26..6ebc415 100644 --- a/man/cpt-remove.1 +++ b/man/cpt-remove.1 @@ -21,6 +21,12 @@ The options are as follows: Force removal .It Fl y , -no-prompt Do not prompt for confirmation +.It Fl -color Ar CPT_COLOR +Enable/disable output color +.Bo +.Sy auto , +always, never, 1, 0 +.Bc .It Fl -root Ar CPT_ROOT Use an alternate root directory .It Fl h , -help diff --git a/man/cpt-search.1 b/man/cpt-search.1 index f59f7b9..6e96954 100644 --- a/man/cpt-search.1 +++ b/man/cpt-search.1 @@ -5,8 +5,15 @@ .Nd search for cpt packages .Sh SYNOPSIS .Nm -.Op Fl dso -.Op Ar query +.Op Fl ds +.Ar package... +.Nm +.Fl o +.Op Fl ds +.Nm +.Fl q +.Op Fl Fds +.Ar query .Sh DESCRIPTION .Nm can be used to search packages. Glob characters can also be used in the search. @@ -17,9 +24,19 @@ The options are as follows: Do not search the installed package database. .It Fl s , -single Only show the first instance of a package. +.It Fl q , -query +Search packages making use of package descriptions. +.It Fl F , -fixed +Run query mode interpreting the given pattern as a fixed string .It Fl o , -others Use the current directory as the package and show other instances of that package. +.It Fl -color Ar CPT_COLOR +Enable/disable output color +.Bo +.Sy auto , +always, never, 1, 0 +.Bc .It Fl h , -help Show help message .It Fl v , -version @@ -27,6 +44,30 @@ Print version information .It Fl -verbose Be more verbose .El +.Pp +The program has three modes of operations. The default operation is to search +for the packages given as positional arguments. +.Pp +If the +.Fl o +flag is specified, +.Nm +will use the name of the current directory to search for instances of other +packages with the same name. +.Pp +If the +.Fl q +flag is specified, +.Nm +will search through the name and description of packages using the given +.Ar query , +and run a case-insensitive search through +.Xr grep 1 . +If additionally the +.Fl F +flag is given, the given +.Ar query +will be considered a fixed string. .Sh EXAMPLES Below are usage examples for .Nm , @@ -21,7 +21,7 @@ program or through the web from .Lk https://carbslinux.org/docs.html . It can also be read plaintext by running .Pp -.Dl less Pa /usr/share/doc/cpt.txt +.Dl less Pa /usr/share/doc/cpt/cpt.txt .Sh AUTHOR .An Cem Keylan Aq Mt cem@ckyln.com .Sh LICENSE diff --git a/spec/01_lib_spec.sh b/spec/01_lib_spec.sh index b56ac32..b7d83f3 100644 --- a/spec/01_lib_spec.sh +++ b/spec/01_lib_spec.sh @@ -112,16 +112,16 @@ Describe 'CPT Library' Describe '_readlinkf()' mklink() { :> tests/testfile; ln -s testfile tests/testfile2 ;} - rmlink() { rm -f tests/testfile tests/testfile2 ;} + rmlink() { rm tests/testfile tests/testfile2 ;} RPWD=$(cd -P .||:; printf %s "$PWD") - Before mklink - After rmlink + BeforeEach mklink + AfterEach rmlink Parameters "#1" . "$RPWD" - "#2" "$PWD/tests/testfile2" "$RPWD/tests/testfile" + "#2" "./tests/testfile2" "$RPWD/tests/testfile" End - It "outputs the real location of the given file ($1)" - When call _readlinkf "$2" + It "outputs the real location of the given file [$1] ($2 -> $3)" + When run _readlinkf "$2" The output should eq "$3" End End @@ -151,6 +151,53 @@ Describe 'CPT Library' End End + Describe 'version control functions' + check_internet_connection() { ! curl -L git.carbslinux.org >/dev/null 2>&1 ;} + Skip if "no internet connection" check_internet_connection + Describe 'pkg_vcs_clone_git()' + tmpfos=$$ + setup() { mkdir "/tmp/test_repository.$tmpfos" && cd "/tmp/test_repository.$tmpfos" || return ;} + cleanup() { cd /tmp && rm -rf "test_repository.$tmpfos" ;} + check_version() { [ "$1" = "$(sed -n '/^version=/s/.*=//p' configure)" ] ;} + BeforeEach setup + AfterEach cleanup + It "clones the given git repository to the current directory" + When call pkg_vcs_clone_git https://git.carbslinux.org/cpt + The output should not eq "" + The stderr should not eq "" + The status should be success + Assert [ ! -d test_repository ] + Assert [ -f README.md ] + Assert check_version Fossil + End + It "clones the given tag when asked for it" + When call pkg_vcs_clone_git https://git.carbslinux.org/cpt @6.2.4 + The output should not eq "" + The stderr should not eq "" + The status should be success + Assert [ ! -d test_repository ] + Assert [ -f README.md ] + Assert check_version 6.2.4 + End + End + Describe 'pkg_vcs_clone_fossil()' + tmpfos=$$ + setup() { mkdir "/tmp/test_repository.$tmpfos" && cd "/tmp/test_repository.$tmpfos" || return ;} + cleanup() { cd /tmp && rm -rf "test_repository.$tmpfos" ;} + check_version() { [ "$1" = "$(sed -n '/^version=/s/.*=//p' configure)" ] ;} + BeforeEach setup + AfterEach cleanup + It "clones the given fossil repository to the current directory" + When call pkg_vcs_clone_fossil https://fossil.carbslinux.org/cpt + The output should not eq "" + The stderr should eq "" + The status should be success + Assert [ ! -d test_repository ] + Assert [ -f README.md ] + Assert check_version Fossil + End + End + End Describe 'package functions' Describe 'run_hook()' CPT_HOOK=$PWD/tests/hook-file @@ -162,15 +209,9 @@ Describe 'CPT Library' End It "doesn't log 'running hook' if no package is given" When call run_hook 2 '' destination - The stderr should eq "-> Running 2 hook " + The stderr should eq "-> Running 2 hook" The output should eq "$CPT_HOOK 2 null destination" End - It "uses the /etc/cpt-hook file of the root when called with a fourth arg" - When call run_hook 3 cpt destdir root - The stderr should eq "-> cpt Running 3 hook" - The output should eq "$CPT_ROOT/etc/cpt-hook 3 cpt destdir" - The variable CPT_HOOK should eq "$PWD/tests/hook-file" - End It "returns with success even when the file doesn't exist" CPT_HOOK=$PWD/some-non-existent-file When call run_hook 4 thiswillnotrun @@ -178,14 +219,6 @@ Describe 'CPT Library' The stderr should eq "" The status should be success End - It "restores the \$CPT_HOOK variable when called with root" - CPT_ROOT=$PWD/nonexistentdir - When call run_hook 5 cpt dest root - The variable CPT_ROOT should not be exist - The stderr should eq "" - The status should be success - The variable CPT_HOOK should eq "$PWD/tests/hook-file" - End End Describe 'create_tmp()' After pkg_clean @@ -197,10 +230,31 @@ Describe 'CPT Library' Describe 'pkg_get_base()' CPT_ROOT=$PWD/tests CPT_PATH=$PWD/tests/repository + cpt_base=$PWD/tests/etc/cpt-base It 'returns packages defined in base' When call pkg_get_base nonl The output should eq "dummy-pkg contrib-dummy-pkg " End End + Describe 'pkg_query_meta()' + CPT_PATH=$PWD/tests/repository + It 'queries package meta information' + When call pkg_query_meta contrib-dummy-pkg description + The output should eq "This is a dummy package" + End + It 'returns an error if there is no meta file' + When call pkg_query_meta dummy-pkg description + The status should be failure + End + It 'returns an error if the queried key is unavailable' + When call pkg_query_meta contrib-dummy-pkg license + The status should be failure + End + It "accepts full paths to the package location" + When call pkg_query_meta "$PWD/tests/repository/contrib-dummy-pkg" description + The output should eq "This is a dummy package" + The status should be success + End + End End End diff --git a/spec/02_src_spec.sh b/spec/02_src_spec.sh index be81775..2c20849 100644 --- a/spec/02_src_spec.sh +++ b/spec/02_src_spec.sh @@ -1,7 +1,8 @@ Describe 'Main toolchain' - export PATH=$PWD/src:$PATH - export CPT_ROOT=$PWD/tests/02 - export CPT_PATH=$PWD/tests/repository + PATH=$PWD/src:$PATH + CPT_ROOT=$PWD/tests/02 + CPT_PATH=$PWD/tests/repository + export PATH CPT_ROOT CPT_PATH install_dummy() { CPT_HOOK='' ./src/cpt bi dummy-pkg >/dev/null 2>&1 ;} remove_dummy() { rm -rf "${CPT_ROOT:?}/var" ;} BeforeAll install_dummy @@ -20,7 +21,7 @@ Describe 'Main toolchain' Describe '--help' It 'outputs usage information' When run script src/cpt --help - The line 1 of stderr should eq "-> Carbs Packaging Tool " + The line 1 of stderr should eq "-> Carbs Packaging Tool" End End @@ -52,7 +53,7 @@ Describe 'Main toolchain' It "expands the '$1' shortcut to '$2'" When run script src/cpt "$1" --help The status should be success - The word 2 of line 1 should eq "cpt-${2%% *}" + The word 3 of line 1 should eq "$1" End End End diff --git a/spec/03_contrib_spec.sh b/spec/03_contrib_spec.sh index 9c7589b..b3d6df4 100644 --- a/spec/03_contrib_spec.sh +++ b/spec/03_contrib_spec.sh @@ -1,8 +1,9 @@ Describe 'contrib scripts' - export PATH=$PWD/src:$PWD/contrib:$PATH - export CPT_ROOT=$PWD/tests/03 - export CPT_PATH=$PWD/tests/repository - export CPT_COMPRESS='' + PATH=$PWD/src:$PWD/contrib:$PATH + CPT_ROOT=$PWD/tests/03 + CPT_PATH=$PWD/tests/repository + CPT_COMPRESS='' + export PATH CPT_ROOT CPT_PATH CPT_COMPRESS install_tmp() { CPT_HOOK='' ./src/cpt b -y contrib-dummy-pkg >/dev/null 2>&1 CPT_HOOK='' ./src/cpt-install -y contrib-dummy-pkg >/dev/null 2>&1 @@ -17,13 +18,13 @@ Describe 'contrib scripts' It 'outputs the file contents in the given package directory' When run script ./contrib/cpt-cat "$firstpkg" The stdout should not eq "" - The line 1 of stderr should eq "$(printf '\033[1mbuild:\033[m\n')" + The line 1 of stderr should eq "build:" End It "uses the current directory for the package name if none is supplied (contrib-dummy-pkg)" cd "$CPT_PATH/contrib-dummy-pkg" || return 1 When run script "$(command -v cpt-cat)" The stdout should not eq "" - The line 1 of stderr should eq "$(printf '\033[1mbuild:\033[m\n')" + The line 1 of stderr should eq "build:" End It "exits with error if the package isn't installed" When run script ./contrib/cpt-cat somerandompackage @@ -44,13 +45,13 @@ Describe 'contrib scripts' It "outputs the given file contents in the given package directory ($1)" When run script ./contrib/cpt-cat "$firstpkg" "$1" The stdout should eq "$(cat "$CPT_ROOT/var/db/cpt/installed/$firstpkg/$1")" - The stderr should eq "$(printf '\033[1m%s:\033[m\n' "$1")" + The stderr should eq "$1:" End It "outputs the given file contents for the name of the current directory (contrib-dummy-pkg - $1)" cd "$CPT_PATH/contrib-dummy-pkg" || return 1 When run script "$(command -v cpt-cat)" "" "$1" The stdout should eq "$(cat "$CPT_ROOT/var/db/cpt/installed/${PWD##*/}/$1")" - The stderr should eq "$(printf '\033[1m%s:\033[m\n' "$1")" + The stderr should eq "$1:" End End Describe 'cpt-depends' @@ -94,6 +95,7 @@ Describe 'contrib scripts' export CPT_COMPRESS=typo When run script "$(command -v cpt-export)" contrib-dummy-pkg The stdout should eq "tarball created in $CPT_ROOT/tmp/contrib-dummy-pkg#1-1.tar.gz" + The stderr should eq "WARNING 'typo' is not a valid CPT_COMPRESS value, falling back to 'gz' " End Parameters bz2 bzip2 @@ -1,5 +1,52 @@ #!/bin/sh -ef +bi() { + # Build and install function for cpt. + # + # shellcheck disable=2317 + parser_definition() { + setup REST help:usage -- "usage: ${0##*/} bi [-dfSty] [--root ROOT] [pkg...]" + msg -- '' 'Options:' + flag CPT_TEST -t --test export:1 init:@export -- "Run tests (if they exist)" + flag CPT_DEBUG -d --debug export:1 init:@export -- "Keep the build directories after operation" + flag CPT_NOSTRIP -S --nostrip export:1 init:@export -- "Don't strip debug information from the binaries" \ + "(might want to add '-g' to your '\$CFLAGS')" + global_options + } + + eval "$(getoptions parser_definition parse "$0")" + parse "$@" + eval set -- "$REST" + cpt-build "$@" + + # When building multiple packages, cpt will already ask to install + # the packages, so no need for this here. + [ "$2" ] || cpt-install "$@" +} + +cbi() { + # Checksum, build and install. + # + # shellcheck disable=2317 + parser_definition() { + setup REST help:usage -- "usage: ${0##*/} cbi [-dfSsty] [--root ROOT] [pkg...]" + msg -- '' 'Options:' + flag CPT_TEST -t --test export:1 init:@export -- "Run tests (if they exist)" + flag CPT_DEBUG -d --debug export:1 init:@export -- "Keep the build directories after operation" + flag CPT_NOSTRIP -S --nostrip export:1 init:@export -- "Don't strip debug information from the binaries" \ + "(might want to add '-g' to your '\$CFLAGS')" + flag sha -s -- "Generate checksums using the depracated sha256 algorithm" + global_options + } + + eval "$(getoptions parser_definition parse "$0")" + parse "$@" + eval set -- "$REST" + + cpt-checksum "$@"; cpt-build "$@" + [ "$2" ] || cpt-install "$@" +} + if [ -f ./cpt-lib ]; then . ./cpt-lib; else . cpt-lib; fi # If none of the tools below are specified, we will reenable glob @@ -35,21 +82,7 @@ case "$arg" in r|remove) arg=remove ;; s|search) arg=search ;; u|update) arg=update ;; - bi) - # Build and install function for cpt. - cpt-build "$@" - - # When building multiple packages, cpt will already ask to install - # the packages, so no need for this here. - [ "$2" ] || cpt-install "$@" - exit - ;; - cbi) - # Checksum, build and install. - cpt-checksum "$@"; cpt-build "$@" - [ "$2" ] || cpt-install "$@" - exit - ;; + bi|cbi) "$arg" "$@"; exit "$?" ;; *) glob=1 ;; esac diff --git a/src/cpt-alternatives b/src/cpt-alternatives index c4c7726..87946f0 100755 --- a/src/cpt-alternatives +++ b/src/cpt-alternatives @@ -2,7 +2,11 @@ # List and swap to alternatives parser_definition() { - setup REST help:usage -- "usage: ${0##*/} [-] [package file]" + setup REST help:usage -- "usage: ${0##*/} package file" + msg -- "or: ${0##*/} [-p]" + msg -- "or: ${0##*/} -" + msg -- '' 'Options:' + flag preferred -p -- "List current owners of alternative files" global_options } @@ -10,23 +14,71 @@ if [ -f ./cpt-lib ]; then . ./cpt-lib; else . cpt-lib; fi # We don't need to be root in order to list alternatives, so skip privilege # elevation if no arguments are passed to the script. -[ -z "$1" ] || [ -w "$CPT_ROOT/" ] || [ "$uid" = 0 ] || { +[ -z "$1" ] && [ -t 0 ] || [ -w "$CPT_ROOT/" ] || [ "$uid" = 0 ] || { as_root "$0" "$@" exit $? } +list_alternatives() { + # Go over each alternative and format the file name for listing. + # (pkg_name>usr>bin>ls) + set +f + for pkg in "$sys_db/../choices/"*; do + printf '%s\n' "${pkg##*/}" + done | sed 's|>|\t/|;s|>|/|g;/\*/d' +} + +stdin_swap() { + # Swap packages by reading the standard input. + while IFS=$(printf '\t') read -r pkg path _; do + pkg_swap "$pkg" "$path" + done + exit +} + case "$1" in - -) - while read -r pkg path; do - pkg_swap "$pkg" "$path" - done - ;; + -) stdin_swap ;; '') - # Go over each alternative and format the file name for listing. - # (pkg_name>usr>bin>ls) - set +f; for pkg in "$sys_db/../choices/"*; do - printf '%s\n' "${pkg##*/}" - done | sed 's|>| /|; s|>|/|g; /\*/d' + # We still want to read the standard input when there are no arguments + # if the standard input is being used. + [ -t 0 ] || stdin_swap + + if [ "$preferred" ]; then + # We are not using the pkg_owner() function here. It's much slower + # when searching items in bulk. + altlist=$(_tmp_create altlist) + pathlist=$(_tmp_create pathlist) + owners=$(_tmp_create owners) + list_alternatives | tee "$altlist" | sed 's,^[^\t]*\t,,' > "$pathlist" + + # Save all matching items in a single file, so we don't ever read + # manifests again. + set +f + grep -Fxf "$pathlist" "$sys_db/"*/manifest > "$owners" + sys_db_esc=$(regesc "$sys_db") + + while read -r pkg path; do + case $path in + # Running regesc() for each file slows us down, don't use + # it unless we detect a regular expression to escape. + *\[*|*\$*|*\\*|*.*|*^*) path_str=$(regesc "$path") ;; + *) path_str=$path + esac + grep_str="$sys_db_esc/[^/]*/manifest:$path_str" + owns=$(grep -x -- "$grep_str" "$owners") || owns=null + owns=${owns%:*} owns=${owns%/*} owns=${owns##*/} + printf '%s\t%s\t(owned by: %s)\n' "$pkg" "$path" "$owns" + done < "$altlist" + + # We read the output of list_alternatives(), because that seems to + # be the faster option. + # list_alternatives | while read -r pkg path; do + # printf '%s %s (owned by: %s)\n' \ + # "$pkg" "$path" "$(pkg_owner -lFx "$path" || out "null")" + # done + else + list_alternatives + fi ;; *) pkg_swap "$@" ;; esac diff --git a/src/cpt-build b/src/cpt-build index 4eccbd1..eaf8849 100755 --- a/src/cpt-build +++ b/src/cpt-build @@ -2,15 +2,18 @@ # Build a package parser_definition() { - setup REST help:usage -- "usage: ${0##*/} [-tfy] [--root ROOT] [pkg...]" + setup REST help:usage -- "usage: ${0##*/} [-dfSty] [--root ROOT] [pkg...]" msg -- '' 'Options:' - flag CPT_TEST -t --test export:1 init:@export -- "Run tests (if they exist)" + flag CPT_DEBUG -d --debug export:1 init:@export -- "Keep the build directories after operation" + flag CPT_TEST -t --test export:1 init:@export -- "Run tests (if they exist)" + flag CPT_NOSTRIP -S --nostrip export:1 init:@export -- "Don't strip debug information from the binaries" \ + "(might want to add '-g' to your '\$CFLAGS')" global_options } if [ -f ./cpt-lib ]; then . ./cpt-lib; else . cpt-lib; fi -[ "$1" ] || { set -- "${PWD##*/}"; export CPT_PATH=${PWD%/*}:$CPT_PATH ;} +[ "$1" ] || { set -- "${PWD##*/}"; export CPT_PATH="${PWD%/*}:$CPT_PATH" ;} create_cache diff --git a/src/cpt-download b/src/cpt-download index d2c9aeb..231e4bb 100755 --- a/src/cpt-download +++ b/src/cpt-download @@ -9,7 +9,7 @@ case "$1" in exit 0 ;; --version|-v) version ;; - '') set -- "${PWD##*/}"; export CPT_PATH=${PWD%/*}:$CPT_PATH + '') set -- "${PWD##*/}"; export CPT_PATH="${PWD%/*}:$CPT_PATH" esac create_cache diff --git a/src/cpt-install b/src/cpt-install index 44842a3..8bafba6 100755 --- a/src/cpt-install +++ b/src/cpt-install @@ -10,7 +10,7 @@ parser_definition() { if [ -f ./cpt-lib ]; then . ./cpt-lib; else . cpt-lib; fi -[ "$1" ] || { set -- "${PWD##*/}"; export CPT_PATH=${PWD%/*}:$CPT_PATH ;} +[ "$1" ] || { set -- "${PWD##*/}"; export CPT_PATH="${PWD%/*}:$CPT_PATH" ;} [ -w "$CPT_ROOT/" ] || [ "$uid" = 0 ] || { as_root "$0" "$@" @@ -27,14 +27,21 @@ for pkg in $order; do pkg_install "$pkg"; done log "Retrieving post-installation message queue" unset msg +# After all the installations are finished, run an end-install hook. There may +# be some things that we may want to run, but not per package. +run_hook end-install "" "$CPT_ROOT" + for pkg in $order; do + # Ensure that we use package names itself, and not the tarball name if given. + pkg=${pkg##*/} pkg=${pkg%#*} + [ -f "$sys_db/$pkg/message" ] && { printf '\033[1m%s\n%s\n%s\033[m\n\n' \ "$(_multiply_char '=' 60)" \ "$pkg" \ "$(_multiply_char '=' 60)" - cat "$sys_db/$pkg/message" >&2 + cat "$sys_db/$pkg/message" msg=1 } -done +done >&2 [ "$msg" ] || log "No message in queue" diff --git a/src/cpt-lib.in b/src/cpt-lib.in index c055331..1896920 100644 --- a/src/cpt-lib.in +++ b/src/cpt-lib.in @@ -9,7 +9,7 @@ # Currently maintained by Cem Keylan. version() { - out "Carbs Packaging Tools, version @VERSION@" \ + out "Carbs Packaging Tools, version $cpt_version" \ @LICENSE@ exit 0 @@ -25,11 +25,12 @@ log() { # # All messages are printed to stderr to allow the user to hide build # output which is the only thing printed to stdout. - # - # '${3:-->}': If the 3rd argument is missing, set prefix to '->'. - # '${2:+colorb}': If the 2nd argument exists, set text style of '$1'. - printf '%b%s %b%b%s%b %s\n' \ - "$colory" "${3:-->}" "$colre" "${2:+$colorb}" "$1" "$colre" "$2" >&2 + case $# in + 1) printf '%b->%b %s\n' "$colory" "$colre" "$1" ;; + 2) printf '%b->%b %b%s%b %s\n' "$colory" "$colre" "$colorb" "$1" "$colre" "$2" ;; + 3) printf '%b%s%b %b%s%b %s\n' "$colory" "${3:-->}" "$colre" "$colorb" "$1" "$colre" "$2" ;; + *) return 1 + esac >&2 } warn() { @@ -55,12 +56,135 @@ warnv() { warn "$@" } +execv() { + # Redirect the output to /dev/null unless CPT_VERBOSE is set. + if [ "$CPT_VERBOSE" = 1 ]; then "$@"; else "$@" >/dev/null 2>&1; fi +} + die() { # Print a message and exit with '1' (error). log "$1" "$2" "!>" exit 1 } +colors_enabled() { + case ${CPT_COLOR:=auto} in + auto) [ -t 1 ] ;; + 1|always) return 0 ;; + 0|never) return 1 ;; + *) die "Unknown color value: '$CPT_COLOR'" + esac +} + +_dep_append() { + dep_graph=$(printf '%s\n%s %s\n' "$dep_graph" "$@" ;) +} + +_tsort() { + # Return a linear reverse topological sort of the piped input, so we + # generate a proper build order. Returns 1 if a dependency cycle occurs. + # + # I was really excited when I saw POSIX specified a tsort(1) implementation, + # but the specification is quite vague, it doesn't specify cycles as a + # reason of error, and implementations differ on how it's handled. coreutils + # tsort(1) exits with an error, while openbsd tsort(1) doesn't. Both + # implementations are correct according to the specification. + # + # The script below was taken from <https://gist.github.com/apainintheneck/1803fb91dde3ba048ec51d44fa6065a4> + # + # The MIT License (MIT) + # Copyright (c) 2023 Kevin Robell + # + # Permission is hereby granted, free of charge, to any person obtaining a + # copy of this software and associated documentation files (the “Software”), + # to deal in the Software without restriction, including without limitation + # the rights to use, copy, modify, merge, publish, distribute, sublicense, + # and/or sell copies of the Software, and to permit persons to whom the + # Software is furnished to do so, subject to the following conditions: + # + # The above copyright notice and this permission notice shall be included in + # all copies or substantial portions of the Software. + # + # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + # DEALINGS IN THE SOFTWARE. + awk '{ + for (i = 1; i <= NF; ++i) { + # Store each node. + nodes[$i] = 1 + if (is_child) { + child = $i + # Skip nodes that point to themselves. + # This traditionally means that the node + # is disconnected from the rest of the graph. + if (parent != child) { + # Store from parent to child. + idx = ++child_count[parent] + child_graph[parent, idx] = child + # Store count from child to parent. + ++parent_count[child] + } + } else { + parent = $i + } + # Flip switch + is_child = !is_child + } + } + END { + # Print errors to the stderr + stderr = "/dev/stderr" + + # Sanity Check + if (is_child) { + print("Error: odd number of input values: expected pairs of values") > stderr + exit(1) + } + + ##### + # Topological Sort + ##### + + # Remove unconnected nodes first. + for (node in nodes) { + if (parent_count[node] == 0 && child_count[node] == 0) { + delete nodes[node] + print(node) + } + } + + # Remove the rest of the nodes starting with those without parents. + while (length(nodes) > 0) { + removed_node = 0 + for (node in nodes) { + # Delete and print nodes without any remaining parents. + if (parent_count[node] == 0) { + delete nodes[node] + removed_node = 1 + # Decrease child_count for each parent node. + for (i = child_count[node]; i > 0; --i) { + child = child_graph[node, i] + --parent_count[child] + } + print(node) + } + } + + # If we havent removed any nodes, it means that there + # are no nodes without any remaining parents so we have + # a cycle. + if (!removed_node) { + print("Error: Cycle found") > stderr + exit(1) + } + } + }' +} + trap_set() { # Function to set the trap value. case ${1:-cleanup} in @@ -76,20 +200,16 @@ trap_set() { esac } -sepchar() ( +sepchar() { # Seperate every character on the given string without resorting to external # processes. [ "$1" ] || return 0; str=$1; set -- while [ "$str" ]; do - str_tmp=$str - for i in $(_seq $(( ${#str} - 1 ))); do - str_tmp=${str_tmp%?} - done - set -- "$@" "$str_tmp" - str=${str#$str_tmp} + set -- "$@" "${str%"${str#?}"}" + str=${str#?} done printf '%s\n' "$@" -) +} _re() { # Check that the string supplied in $2 conforms to the regular expression @@ -120,7 +240,7 @@ _stat() ( printf '%s' "$_user" ) -_readlinkf() ( +_readlinkf() { # Public domain POSIX sh readlink function by Koichi Nakashima [ "${1:-}" ] || return 1 max_symlinks=40 @@ -154,7 +274,7 @@ _readlinkf() ( target=${link#*" $target -> "} done return 1 -) +} _get_digest() { # Get digest algorithm from the given file. It looks for a header on the @@ -176,7 +296,7 @@ _get_digest() { # function. # URL: https://github.com/ko1nksm/getoptions (v2.5.0) # License: Creative Commons Zero v1.0 Universal -# shellcheck disable=2016,2086 +# shellcheck disable=2016,2086,2317 getoptions() { _error='' _on=1 _off='' _export='' _plus='' _mode='' _alt='' _rest='' _flags='' _nflags='' _opts='' _help='' _abbr='' _cmds='' _init=@empty IFS=' ' @@ -371,6 +491,7 @@ getoptions() { } # URL: https://github.com/ko1nksm/getoptions (v2.5.0) # License: Creative Commons Zero v1.0 Universal +# shellcheck disable=2317 getoptions_help() { _width='30,12' _plus='' _leading=' ' @@ -415,14 +536,34 @@ getoptions_help() { echo "}" } +# 2086: +# The lack of quotes are intentional. We do this so `getoptions()` do not try +# to parse the empty string. +# 2120: +# The library does not call this function with any positional arguments, but +# that does not mean that other programs will not do it, so this can also be +# safely ignored. +# shellcheck disable=2086,2120 global_options() { - msg -- '' 'Global Options:' - flag CPT_FORCE -f --force init:@export -- "Force operation" - flag CPT_PROMPT -y --no-prompt on:0 off:0 init:@export -- "Do not prompt for confirmation" - param CPT_ROOT --root init:@export -- "Use an alternate root directory" - disp :usage -h --help -- "Show this help message" - disp :version -v --version -- "Print version information" - flag CPT_VERBOSE --verbose init:@export -- "Be more verbose" + # These are options that are supported by most utilities. If the optional + # argument 'silent' is given, the usage will not print these options, but + # the arguments will still be accepted. Alternatively, if the 'compact' + # argument is given, the function only prints the '--help' and '--version' + # flags. Sometimes it doesn't make sense to pollute the screen with options + # that will be rarely ever used. + _h=hidden:1 + case $1 in + silent) _c=$_h ;; + compact) _c='' ;; + *) msg -- '' 'Global Options:'; _c='' _h='' + esac + flag CPT_FORCE -f --force $_h init:@export -- "Force operation" + flag CPT_PROMPT -y --no-prompt on:0 off:0 $_h init:@export -- "Do not prompt for confirmation" + param CPT_ROOT --root $_h init:@export -- "Use an alternate root directory" + param CPT_COLOR --color $_h init:@export -- "Colorize the output [default:auto]" + disp :usage -h --help $_c -- "Show this help message" + disp :version -v --version $_c -- "Print version information" + flag CPT_VERBOSE --verbose $_h init:@export -- "Be more verbose" } contains() { @@ -440,11 +581,12 @@ regesc() { pkg_download() { # $1: URL # $2: Output (Optional) - set -- "$1" "${2:-${1##*/}}" + set -- "$1" "$(_readlinkf "${2:-${1##*/}}")" case ${dl_prog##*/} in - aria2c|axel) set -- -o "$2" "$1" ;; - curl) set -- -fLo "$2" "$1" ;; - wget|wget2) set -- -O "$2" "$1" ;; + axel) set -- -o "$2" "$1" ;; + aria2c) set -- -d "${2%/*}" -o "${2##*/}" "$1" ;; + curl) set -- -fLo "$2" "$1" ;; + wget|wget2) set -- -O "$2" "$1" ;; esac "$dl_prog" "$@" || { @@ -476,6 +618,9 @@ as_root() { # We are exporting package manager variables, so that we still have the # same repository paths / access to the same cache directories etc. + # + # It doesn't matter whether CPT_HOOK is defined or not. + # shellcheck disable=2153 set -- HOME="$HOME" \ USER="$user" \ XDG_CACHE_HOME="$XDG_CACHE_HOME" \ @@ -517,23 +662,38 @@ pop() { } run_hook() { - # Store the CPT_HOOK variable so that we can revert it if it is changed. - oldCPT_HOOK=$CPT_HOOK - - # If a fourth parameter 'root' is specified, source the hook from a - # predefined location to avoid privilige escalation through user scripts. - [ "$4" ] && CPT_HOOK=$CPT_ROOT/etc/cpt-hook - - [ -f "$CPT_HOOK" ] || { CPT_HOOK=$oldCPT_HOOK; return 0 ;} + # Check that hooks exist before announcing that we are running a hook. + set +f + for hook in "$cpt_confdir/hooks/"* "$CPT_HOOK"; do + [ -f "$hook" ] && { + if [ "$2" ]; then + logv "$2" "Running $1 hook" + else + logv "Running $1 hook" + fi + break + } + done - if [ "$2" ]; then - logv "$2" "Running $1 hook" - else - logv "Running $1 hook" - fi + # Run all the hooks found in the configuration directory, and the user + # defined hook. + for hook in "$cpt_confdir/hooks/"* "$CPT_HOOK"; do + set -f + [ -f "$hook" ] || continue + TYPE=${1:-null} PKG=${2:-null} DEST=${3:-null} . "$hook" + done +} - TYPE=${1:-null} PKG=${2:-null} DEST=${3:-null} . "$CPT_HOOK" - CPT_HOOK=$oldCPT_HOOK +# An optional argument could be provided to enforce a compression algorithm. +# shellcheck disable=2120 +compress() { + case ${1:-$CPT_COMPRESS} in + bz2) bzip2 -z ;; + gz) gzip -6 ;; + xz) xz -zT 0 ;; + zst) zstd -3 ;; + lz) lzip -6 ;; + esac } decompress() { @@ -574,6 +734,18 @@ pkg_owner() { [ "$1" ] && printf '%s\n' "$1" } +pkg_owner_multi() { + set +f + + [ "$3" ] || set -- "$1" "$2" "$sys_db"/*/manifest + + grep "$@" | while read -r pkg_owner; do + pkg_owner=${pkg_owner%/*} + pkg_owner=${pkg_owner##*/} + printf '%s\n' "${pkg_owner##*/}" + done +} + pkg_isbuilt() ( # Check if a package is built or not. read -r ver rel < "$(pkg_find "$1")/version" @@ -755,7 +927,10 @@ pkg_extract() { # VCS Repository git+*|hg+*|fossil+*) backend=${src%%+*} - url=${src##${backend}+} com=${url##*[@#]} com=${com#${url%[@#]*}} + url=${src##"${backend}"+} com=${url##*[@#]} com=${com#"${url%[@#]*}"} + + # Add back @ to com + case $url in *@*) com=@$com; esac log "$1" "Cloning ${url%[#@]*}" "pkg_vcs_clone_$backend" "${url%[#@]*}" "$com" @@ -798,12 +973,10 @@ pkg_depends() { # Resolve all dependencies and generate an ordered list. # This does a depth-first search. The deepest dependencies are # listed first and then the parents in reverse order. - contains "$deps" "$1" || { - # Filter out non-explicit, aleady installed dependencies. - # Only filter installed if called from 'pkg_build()'. - [ "$pkg_build" ] && [ -z "$2" ] && - (pkg_list "$1" >/dev/null) && return - + # + # shellcheck disable=2015 + contains "$pkgs" "$1" && [ -z "$2" ] || { + [ "$2" = raw ] && _dep_append "$1" "$1" while read -r dep type || [ "$dep" ]; do # Skip comments and empty lines. [ "${dep##\#*}" ] || continue @@ -816,6 +989,16 @@ pkg_depends() { make) [ "$2" = tree ] && [ -z "${3#first-nomake}" ] && continue esac + # Filter out non-explicit, already installed dependencies if called + # from 'pkg_build()'. + [ "$pkg_build" ] && (pkg_list "$dep" >/dev/null) && continue + + if [ "$2" = explicit ] || [ "$3" ]; then + _dep_append "$dep" "$dep" + else + _dep_append "$dep" "$1" + fi + # Recurse through the dependencies of the child packages. Forward # the 'tree' operation. if [ "$2" = tree ]; then @@ -825,12 +1008,15 @@ pkg_depends() { fi done 2>/dev/null < "$(pkg_find "$1")/depends" ||: - # After child dependencies are added to the list, - # add the package which depends on them. - [ "$2" = explicit ] || [ "$3" ] || deps="$deps $1 " + pkgs="$pkgs $1 " } } +pkg_depends_commit() { + # Set deps, and cleanup dep_graph, pkgs + deps=$(printf '%s\n' "$dep_graph" | _tsort) dep_graph='' pkgs='' || warn "Dependency cycle detected" +} + pkg_order() { # Order a list of packages based on dependence and # take into account pre-built tarballs if this is @@ -838,9 +1024,10 @@ pkg_order() { order=; redro=; deps= for pkg do case $pkg in - *.tar.*) deps="$deps $pkg " ;; + *.tar.*) _dep_append "$pkg" "$pkg" ;; *) pkg_depends "$pkg" raw esac done + pkg_depends_commit # Filter the list, only keeping explicit packages. # The purpose of these two loops is to order the @@ -858,7 +1045,7 @@ pkg_strip() { # system as well as on the tarballs we ship for installation. # Package has stripping disabled, stop here. - [ -f "$mak_dir/$pkg/nostrip" ] && return + [ "$CPT_NOSTRIP" ] || [ -f "$mak_dir/$pkg/nostrip" ] && return log "$1" "Stripping binaries and libraries" @@ -883,9 +1070,12 @@ pkg_strip() { pkg_fix_deps_fullpath() { # Return the canonical path of libraries extracted by readelf. - while read -r dep _ rslv _; do - [ "$dep" = "$1" ] || continue - printf '%s\n' "$rslv" + while read -r line _ rslv _; do + [ "$line" = "$1" ] || continue + case $rslv in + ldd) out "$line" ;; + *) out "$rslv" ;; + esac done } @@ -899,11 +1089,10 @@ pkg_fix_deps() { # simplify path building. cd "$pkg_dir/$1/$pkg_db/$1" - # Make a copy of the depends file if it exists to have a - # reference to 'diff' against. + # Make a copy of the depends file if it exists to have a reference to 'diff' + # against. if [ -f depends ]; then - cp -f depends "$mak_dir/d" - dep_file=$mak_dir/d + dep_file=$(_tmp_cp depends) else dep_file=/dev/null fi @@ -912,52 +1101,72 @@ pkg_fix_deps() { pkg_name=$1 set +f; set -f -- "$sys_db/"*/manifest - # Get a list of binaries and libraries, false files - # will be found, however it's faster to get 'ldd' to check - # them anyway than to filter them out. - find "$pkg_dir/$pkg_name/" -type f 2>/dev/null | + # We create two separate files for storing dependency information. + # + # 'lddfile' is where we will be storing the output of ldd, so that we can + # reference it later. + # + # 'dep_file_list' is where we will be listing the needed files which we will + # be passing to grep. + # + lddfile=$(_tmp_create lddfile) dep_file_list=$(_tmp_create dfl) - while read -r file; do - # We call ldd regardless here, because we also use it to retrieve the - # fullpath of a library when using readelf. Best use we have here is - # saving it in a buffer, so we don't use the dynamic loader everytime we - # need to reference it. - lddbuf=$(ldd -- "$file" 2>/dev/null) ||: - - case ${elf_prog:-ldd} in - *readelf) "$elf_prog" -d "$file" 2>/dev/null ;; - *) pirntf '%s\n' "$lddbuf" ;; - esac | - while read -r dep; do - # Skip lines containing 'ldd'. - [ "${dep##*ldd*}" ] || continue - case $dep in *NEEDED*\[*\] | *'=>'*) ;; *) continue; esac - - # readelf output: - # 0x0000 (NEEDED) Shared library: [libc.so] - dep=${dep##*\[} - dep=${dep%%\]*} - - # Retrieve the fullpath of the library from our ldd buffer. - case $elf_prog in - *readelf) line=$(printf '%s\n' "$lddbuf" | - pkg_fix_deps_fullpath "$line") - esac + pkg_fix_deps_find() { + # We run the similar command twice, might as well be a function. + # Basically runs find on the package directory and executes the + # given command. + end=+; [ "$1" = ldd ] && end=';' - # ldd output: - # libc.so => /lib/ld-musl-x86_64.so.1 - dep=${dep#* => } - dep=${dep% *} - - # Figure out which package owns the file. Skip file if it is owned - # by the current package. This also handles cases where a '*-bin' - # package exists on the system, so the package manager doesn't think - # that the package we are building depends on the *-bin version of - # itself, or any other renamed versions of the same software. - pkg_owner -l "/${dep#/}\$" "$PWD/manifest" >/dev/null && continue - pkg_owner -l "/${dep#/}\$" "$@" ||: - done ||: - done >> depends + # Get a list of binaries and libraries, false files will be found, + # however it's faster to get 'ldd' to check them anyway than to filter + # them out. + # + # We are terminating exec, so no worries. + # shellcheck disable=2067 + find "$pkg_dir/$pkg_name/" -type f -exec "$@" {} "$end" 2>/dev/null | + sed 's/([^)]*) *$//' | sort -u + } + + # Record all the dependencies in the 'lddfile'. This will include all + # dependencies, including non-direct ones. Unless the user prefers ldd, + # readelf will be used to filter the non-direct dependencies out. + pkg_fix_deps_find ldd -- > "$lddfile" + + case "$elf_prog" in + *readelf) pkg_fix_deps_find "$elf_prog" -d ;; + *) cat "$lddfile" + esac | while read -r dep; do + # Skip lines containing 'ldd'. + [ "${dep##*ldd*}" ] || continue + case $dep in *NEEDED*\[*\] | *'=>'*) ;; *) continue; esac + + # readelf output: + # 0x0000 (NEEDED) Shared library: [libc.so] + dep=${dep##*\[} + dep=${dep%%\]*} + + # Retrieve the fullpath of the library from our ldd buffer. + case $elf_prog in + *readelf) dep=$(pkg_fix_deps_fullpath "$dep" < "$lddfile") + esac + + # ldd output: + # libc.so => /lib/ld-musl-x86_64.so.1 + dep=${dep#* => } + dep=${dep% *} + + # Figure out which package owns the file. Skip file if it is owned + # by the current package. This also handles cases where a '*-bin' + # package exists on the system, so the package manager doesn't think + # that the package we are building depends on the *-bin version of + # itself, or any other renamed versions of the same software. + pkg_owner -l "/${dep#/}\$" "$PWD/manifest" >/dev/null && continue + out "/${dep#/}\$" + done >> "$dep_file_list" + + # We write all the files into 'dep_file_list' so that we don't need to call + # grep on our entire database manifest hundreds of times. + pkg_owner_multi -lf "$dep_file_list" "$@" >> depends # Remove duplicate entries from the new depends file. # This removes duplicate lines looking *only* at the @@ -965,7 +1174,7 @@ pkg_fix_deps() { sort -uk1,1 -o depends depends 2>/dev/null ||: # Display a diff of the new dependencies against the old ones. - diff -U 3 "$dep_file" depends 2>/dev/null ||: + execv diff -U 3 "$dep_file" depends 2>/dev/null ||: # Remove the depends file if it is empty. [ -s depends ] || rm -f depends @@ -987,7 +1196,7 @@ pkg_manifest() ( # sed: Remove the first character in each line (./dir -> /dir) and # remove all lines which only contain '.'. find . -type d -exec printf '%s/\n' {} + -o -print | - sort -r | sed '/^\.$/d;/^\.\/$/d;ss.ss' > "${2:-$pkg_dir}/$1/$pkg_db/$1/manifest" + sort -r | sed '/^\.\/*$/d;ss.ss' > "${2:-$pkg_dir}/$1/$pkg_db/$1/manifest" ) pkg_etcsums() ( @@ -1017,23 +1226,12 @@ pkg_tar() { log "$1" "Creating tarball" # Read the version information to name the package. - read -r version release < "$(pkg_find "$1")/version" + read -r version release < "$pkg_dir/$1/$pkg_db/$1/version" # Create a tarball from the contents of the built package. cd "$pkg_dir/$1" - pax -w . | - case $CPT_COMPRESS in - bz2) bzip2 -z ;; - xz) xz -zT 0 ;; - gz) gzip -6 ;; - zst) zstd -3 ;; - lz) lzip -6 ;; - *) gzip -6 ;; # Fallback to gzip - esac \ - > "$bin_dir/$1#$version-$release.tar.$CPT_COMPRESS" - + pax -w . | compress > "$bin_dir/$1#$version-$release.tar.$CPT_COMPRESS" log "$1" "Successfully created tarball" - run_hook post-package "$1" "$bin_dir/$1#$version-$release.tar.$CPT_COMPRESS" } @@ -1052,6 +1250,7 @@ pkg_build() { # separately from those detected as dependencies. explicit="$explicit $pkg " } done + pkg_depends_commit [ "$pkg_update" ] || explicit_build=$explicit @@ -1259,7 +1458,7 @@ pkg_checksums() { pkg_verify() { # Verify all package checksums. This is achieved by generating a new set of # checksums and then comparing those with the old set. - vcmd="NR==FNR{a[\$1];next}/^git .*/{next}!((\$1)in a){exit 1}" + vcmd="NR==FNR{a[\$1];next}/^git .*/{next}!((\$1)in a){exit 1}END{if(NR/2!=FNR)exit 1}" for pkg; do repo_dir=$(pkg_find "$pkg") @@ -1286,6 +1485,9 @@ pkg_conflicts() { # Check to see if a package conflicts with another. log "$1" "Checking for package conflicts" + c_manifest=$(_tmp_create conflict-manifest) + c_conflicts=$(_tmp_create conflicts) + # Filter the tarball's manifest and select only files # and any files they resolve to on the filesystem # (/bin/ls -> /usr/bin/ls). @@ -1305,9 +1507,9 @@ pkg_conflicts() { # Combine the dirname and file values, and print them into the # temporary manifest to be parsed. - printf '%s/%s\n' "${dirname#$CPT_ROOT}" "${file##*/}" + printf '%s/%s\n' "${dirname#"$CPT_ROOT"}" "${file##*/}" - done < "$tar_dir/$1/$pkg_db/$1/manifest" > "$CPT_TMPDIR/$pid/manifest" + done < "$tar_dir/$1/$pkg_db/$1/manifest" > "$c_manifest" p_name=$1 @@ -1316,7 +1518,7 @@ pkg_conflicts() { # shellcheck disable=2046,2086 set -- $(set +f; pop "$sys_db/$p_name/manifest" from "$sys_db"/*/manifest) - [ -s "$CPT_TMPDIR/$pid/manifest" ] || return 0 + [ -s "$c_manifest" ] || return 0 # In rare cases where the system only has one package installed # and you are reinstalling that package, grep will try to read from @@ -1332,12 +1534,12 @@ pkg_conflicts() { # Store the list of found conflicts in a file as we will be using the # information multiple times. Storing it in the cache dir allows us # to be lazy as they'll be automatically removed on script end. - sed '/\/$/d' "$@" | sort "$CPT_TMPDIR/$pid/manifest" - | uniq -d > "$CPT_TMPDIR/$pid/conflict" ||: + sed '/\/$/d' "$@" | sort "$c_manifest" - | uniq -d > "$c_conflicts" ||: # Enable alternatives automatically if it is safe to do so. # This checks to see that the package that is about to be installed # doesn't overwrite anything it shouldn't in '/var/db/cpt/installed'. - "$grep" -q "/var/db/cpt/installed/" "$CPT_TMPDIR/$pid/conflict" || + "$grep" -q "/var/db/cpt/installed/" "$c_conflicts" || choice_auto=1 # Use 'grep' to list matching lines between the to @@ -1388,13 +1590,13 @@ pkg_conflicts() { log "this must be fixed in $p_name. Contact the maintainer" die "by checking 'git log' or by running 'cpt-maintainer'" } - done < "$CPT_TMPDIR/$pid/conflict" + done < "$c_conflicts" # Rewrite the package's manifest to update its location # to its new spot (and name) in the choices directory. pkg_manifest "$p_name" "$tar_dir" 2>/dev/null - elif [ -s "$CPT_TMPDIR/$pid/conflict" ]; then + elif [ -s "$c_conflicts" ]; then log "Package '$p_name' conflicts with another package" "" "!>" log "Run 'CPT_CHOICE=1 cpt i $p_name' to add conflicts" "" "!>" die "as alternatives." @@ -1427,15 +1629,22 @@ pkg_swap() { # its manifest file to reflect this. We then resort this file # so no issues arise when removing packages. cp -Pf "$CPT_ROOT/$2" "$pkg_owns>${alt#*>}" - sed "s#^$(regesc "$2")\$#${PWD#$CPT_ROOT}/$pkg_owns>${alt#*>}#" \ + sed "s#^$(regesc "$2")\$#${PWD#"$CPT_ROOT"}/$pkg_owns>${alt#*>}#" \ "../installed/$pkg_owns/manifest" | sort -r -o "../installed/$pkg_owns/manifest" + else + # If the file doesn't exist, we assume that there was a previous owner, + # but the package was then removed. We want the message to be short + # and clear, I thought of writing "Swapping [...] from 'null' to '$1'", + # but that would probably sound more like a package manager bug. Instead + # we are printing the message below which should be informative enough. + log "Installing '$2' from '$1'" fi # Convert the desired alternative to a real file and rewrite # the manifest file to reflect this. The reverse of above. mv -f "$alt" "$CPT_ROOT/$2" - sed "s#^${PWD#$CPT_ROOT}/$(regesc "$alt")\$#$2#" "../installed/$1/manifest" | + sed "s#^${PWD#"$CPT_ROOT"}/$(regesc "$alt")\$#$2#" "../installed/$1/manifest" | sort -r -o "../installed/$1/manifest" } @@ -1449,13 +1658,13 @@ pkg_etc() { mkdir -p "$CPT_ROOT/$dir" done - digest=$(_get_digest "$mak_dir/c") || digest=b3sum + digest=$(_get_digest "$_etcsums") || digest=b3sum # Handle files in /etc/ based on a 3-way checksum check. find etc ! -type d | while read -r file; do { sum_new=$("$digest" "$file") sum_sys=$(cd "$CPT_ROOT/"; "$digest" "$file") - sum_old=$("$grep" "$file$" "$mak_dir/c"); } 2>/dev/null ||: + sum_old=$("$grep" "$file$" "$_etcsums"); } 2>/dev/null ||: logv "$pkg_name" "Doing 3-way handshake for $file" outv "Previous: ${sum_old:-null}" @@ -1520,10 +1729,11 @@ pkg_remove() { # remove anything from packages that create empty directories for a # purpose (such as baselayout). manifest_list="$(set +f; pop "$sys_db/$1/manifest" from "$sys_db/"*/manifest)" + dirs="$(_tmp_name "directories")" # shellcheck disable=2086 - [ "$manifest_list" ] && grep -h '/$' $manifest_list | sort -ur > "$mak_dir/dirs" + [ "$manifest_list" ] && grep -h '/$' $manifest_list | sort -ur > "$dirs" - run_hook pre-remove "$1" "$sys_db/$1" root + run_hook pre-remove "$1" "$sys_db/$1" while read -r file; do # The file is in '/etc' skip it. This prevents the package @@ -1531,7 +1741,7 @@ pkg_remove() { [ "${file##/etc/*}" ] || continue if [ -d "$CPT_ROOT/$file" ]; then - "$grep" -Fxq "$file" "$mak_dir/dirs" 2>/dev/null && continue + "$grep" -Fxq "$file" "$dirs" 2>/dev/null && continue rmdir "$CPT_ROOT/$file" 2>/dev/null || continue else rm -f "$CPT_ROOT/$file" @@ -1542,7 +1752,7 @@ pkg_remove() { # we no longer need to block 'Ctrl+C'. trap_set cleanup - run_hook post-remove "$1" "$CPT_ROOT/" root + run_hook post-remove "$1" "$CPT_ROOT/" log "$1" "Removed successfully" } @@ -1605,7 +1815,7 @@ pkg_install() { [ "$install_dep" ] && die "$1" "Package requires ${install_dep%, }" - run_hook pre-install "$pkg_name" "$tar_dir/$pkg_name" root + run_hook pre-install "$pkg_name" "$tar_dir/$pkg_name" pkg_conflicts "$pkg_name" log "$pkg_name" "Installing package incrementally" @@ -1617,8 +1827,8 @@ pkg_install() { # If the package is already installed (and this is an upgrade) make a # backup of the manifest and etcsums files. - cp -f "$sys_db/$pkg_name/manifest" "$mak_dir/m" 2>/dev/null ||: - cp -f "$sys_db/$pkg_name/etcsums" "$mak_dir/c" 2>/dev/null ||: + _manifest=$(_tmp_cp "$sys_db/$pkg_name/manifest" 2>/dev/null) ||: + _etcsums=$(_tmp_cp "$sys_db/$pkg_name/etcsums" 2>/dev/null) ||: # This is repeated multiple times. Better to make it a function. pkg_rsync() { @@ -1633,7 +1843,7 @@ pkg_install() { pkg_etc # Remove any leftover files if this is an upgrade. - "$grep" -vFxf "$sys_db/$pkg_name/manifest" "$mak_dir/m" 2>/dev/null | + "$grep" -vFxf "$sys_db/$pkg_name/manifest" "$_manifest" 2>/dev/null | while read -r file; do file=$CPT_ROOT/$file @@ -1670,7 +1880,7 @@ pkg_install() { "$sys_db/$pkg_name/post-install" ||: fi - run_hook post-install "$pkg_name" "$sys_db/$pkg_name" root + run_hook post-install "$pkg_name" "$sys_db/$pkg_name" log "$pkg_name" "Installed successfully" } @@ -1679,7 +1889,7 @@ pkg_repository_update() { # Function to update the given package repository. cd "$1" repo_type=$(pkg_vcs_info) - repo_root=${repo_type#$PWD:} + repo_root=${repo_type#"$PWD":} repo_type=${repo_type##*:} repo_root=${repo_root%:*} contains "$repos" "$repo_root" || { repos="$repos $repo_root " @@ -1706,7 +1916,7 @@ pkg_vcs_clone_git() { git init git remote add origin "${1%[#@]*}" case $2 in - @*) git fetch -t --depth=1 origin "${2#@}" || git fetch ;; + @*) git fetch -t --depth=1 origin "${2#@}" || git fetch; set -- "$1" "${2#@}" ;; *) git fetch --depth=1 origin "$2" || git fetch esac git checkout "${2:-FETCH_HEAD}" @@ -1905,7 +2115,12 @@ pkg_updates(){ # an update. [ "$CPT_FETCH" = 0 ] || pkg_fetch - log "Checking for new package versions" + # Be quiet if we are doing self update, no need to print the same + # information twice. We add this basic function, because we will be using it + # more than once. + _not_update () { [ "$cpt_self_update" ] || "$@" ;} + + _not_update log "Checking for new package versions" set +f @@ -1919,7 +2134,7 @@ pkg_updates(){ # Compare installed packages to repository packages. [ "$db_ver-$db_rel" != "$re_ver-$re_rel" ] && { - printf '%s\n' "$pkg_name $db_ver-$db_rel ==> $re_ver-$re_rel" + _not_update printf '%s\n' "$pkg_name $db_ver-$db_rel ==> $re_ver-$re_rel" outdated="$outdated$pkg_name " } done @@ -1940,6 +2155,13 @@ pkg_updates(){ exit 0 } + [ "$outdated" ] || { + log "Everything is up to date" + return + } + + _not_update log "Packages to update: ${outdated% }" + contains "$outdated" cpt && { log "Detected package manager update" log "The package manager will be updated first" @@ -1950,18 +2172,17 @@ pkg_updates(){ cpt-install cpt log "Updated the package manager" - log "Re-run 'cpt update' to update your system" - - exit 0 - } - - [ "$outdated" ] || { - log "Everything is up to date" - return + log "Re-executing the package manager to continue the update" + + # We export this variable so that cpt knows it's running for the second + # time. We make the new process promptless, and we avoid fetching + # repositories. We are assuming that the user was already prompted once, + # and that their repositories are up to date, or they have also passed + # the '-y' or '-n' flags themselves which leads to the same outcome. + export cpt_self_update=1 + exec cpt-update -yn } - log "Packages to update: ${outdated% }" - # Tell 'pkg_build' to always prompt before build. pkg_update=1 @@ -1977,12 +2198,12 @@ pkg_updates(){ } pkg_get_base() ( - # Print the packages defined in the /etc/cpt-base file. + # Print the packages defined in the CPT base file. # If an argument is given, it prints a space seperated list instead # of a list seperated by newlines. - # cpt-base is an optional file, return with success if it doesn't exist. - [ -f "$CPT_ROOT/etc/cpt-base" ] || return 0 + # CPT base is an optional file, return with success if it doesn't exist. + [ -f "$cpt_base" ] || return 0 # If there is an argument, change the format to use spaces instead of # newlines. @@ -1993,13 +2214,20 @@ pkg_get_base() ( # subshell. That is our purpose here, thank you very much. # shellcheck disable=SC2030 while read -r pkgname _; do + # Ignore comments [ "${pkgname##\#*}" ] || continue + + # Store the package list in arguments set -- "$@" "$pkgname" + + # Retrieve the dependency tree of the package, so they are listed as + # base packages too. This ensures that no packages are broken in a + # "base reset", and the user has a working base. deps=$(pkg_gentree "$pkgname" xn) for dep in $deps; do contains "$*" "$dep" || set -- "$@" "$dep" done - done < "$CPT_ROOT/etc/cpt-base" + done < "$cpt_base" # Format variable is intentional. # shellcheck disable=2059 @@ -2024,6 +2252,7 @@ pkg_gentree() ( esac done pkg_depends "$1" tree "$make_deps" + pkg_depends_commit # Unless 'f' is given, pop the package from the list so that we don't list # the package (for example if it's part of the base package list). Normally @@ -2035,7 +2264,9 @@ pkg_gentree() ( # shellcheck disable=2086 [ -z "${2##*f*}" ] || deps=$(pop "$1" from $deps) - eval set -- "$deps" + # Word splitting is intentional. + # shellcheck disable=2086 + set -- $deps pkg_order "$@" if [ "$reverse" ]; then eval set -- "$redro"; else eval set -- "$order"; fi [ "$1" ] || return 0 @@ -2074,12 +2305,35 @@ pkg_clean() { rm -rf -- "${CPT_TMPDIR:=$cac_dir/proc}/$pid" } +_tmp_name() { + # Name a temporary file/directory + out "$tmp_dir/$1" +} + +_tmp_cp() { + # Copy given file to the temporary directory and return its name. If a + # second argument is not given, use the basename of the copied file. + _ret=${2:-${1##*/}} + _ret=$(_tmp_name "$_ret") + cp -p "$1" "$_ret" + out "$_ret" +} + +_tmp_create() { + # Create given file to the temporary directory and return its name + create_tmp + _ret=$(_tmp_name "$1") + :> "$_ret" || return 1 + out "$_ret" +} + create_tmp() { # Create the required temporary directories and set the variables which # point to them. - mkdir -p "${mak_dir:=$tmp_dir/build}" \ - "${pkg_dir:=$tmp_dir/pkg}" \ - "${tar_dir:=$tmp_dir/export}" + mak_dir=$tmp_dir/build + pkg_dir=$tmp_dir/pkg + tar_dir=$tmp_dir/export + mkdir -p "$mak_dir" "$pkg_dir" "$tar_dir" } create_cache() { @@ -2093,6 +2347,9 @@ create_cache() { { set -ef + # Package manager version. + cpt_version=@VERSION@ + # If a parser definition exists, let's run it ourselves. This makes sure we # get the variables as soon as possible. command -v parser_definition >/dev/null && { @@ -2107,25 +2364,34 @@ create_cache() { # that it doesn't change beneath us. pid=${CPT_PID:-$$} - # Create the cache directories for CPT and set the variables which point - # to them. This is seperate from temporary directories created in - # create_cache(). That's because we need these variables set on most - # occasions. - # # A temporary directory can be specified apart from the cache directory in # order to build in a user specified directory. /tmp could be used in order - # to build on ram, useful on SSDs. The user can specify CPT_TMPDIR for this. - # We create the temporary directory here to avoid permission issues that can - # arise from functions that call as_root(). - mkdir -p "${cac_dir:=${CPT_CACHE:=${XDG_CACHE_HOME:-$HOME/.cache}/cpt}}" \ - "${CPT_TMPDIR:=$cac_dir/proc}" \ - "${src_dir:=$cac_dir/sources}" \ - "${log_dir:=$cac_dir/logs}" \ - "${bin_dir:=$cac_dir/bin}" - - # We don't create the temporary $pid directory until `create_tmp()` is - # called, but we still declare its variable here. - : "${tmp_dir:=${CPT_TMPDIR:=$cac_dir/proc}/$pid}" + # to build on ram, useful on SSDs. The user can specify $CPT_TMPDIR for + # this. We now also support the usage of $XDG_RUNTIME_DIR, so the directory + # naming can be confusing to some. Here are possible $tdir names (by order + # of preference): + # + # 1. $CPT_TMPDIR + # 2. $XDG_RUNTIME_DIR/cpt + # 3. $XDG_CACHE_DIR/cpt/proc + # 4. $HOME/.cache/cpt/proc + # + # We create the main temporary directory here to avoid permission issues + # that can arise from functions that call as_root(). However, the + # $pid directories are special for each process and aren't created unless + # `create_tmp()` is used. + # + # We used to assign and create the directories at the same time using a + # shell hack, but it made the variables editable outside of the package + # manager, but we don't actually want that. Variables that are lower case + # aren't meant to be interacted or set by the user. + cac_dir=${CPT_CACHE:=${XDG_CACHE_HOME:-${HOME:?}/.cache}}/cpt + src_dir=$cac_dir/sources + log_dir=$cac_dir/logs + bin_dir=$cac_dir/bin + tdir=${CPT_TMPDIR:=${XDG_RUNTIME_DIR:-$cac_dir/proc}${XDG_RUNTIME_DIR:+/cpt}} + tmp_dir=$tdir/$pid + mkdir -p "$cac_dir" "$src_dir" "$log_dir" "$bin_dir" "$tdir" # Set the location to the repository and package database. pkg_db=var/db/cpt/installed @@ -2181,10 +2447,7 @@ create_cache() { # Make sure that the CPT_ROOT doesn't end with a '/'. This might # break some operations. - [ -z "$CPT_ROOT" ] || [ "${CPT_ROOT##*/}" ] || { - warn "" "Your CPT_ROOT variable shouldn't end with '/'" - CPT_ROOT=${CPT_ROOT%/} - } + CPT_ROOT=${CPT_ROOT%"${CPT_ROOT##*[!/]}"} # Define an optional sys_arch variable in order to provide # information to build files with architectural information. @@ -2194,6 +2457,13 @@ create_cache() { # the get go. It will be created as needed by package installation. sys_db=$CPT_ROOT/$pkg_db + # CPT system configuration directory + cpt_confdir=$CPT_ROOT@SYSCONFDIR@/cpt + + # Backwards compatibility for the old cpt-base location + cpt_base=$CPT_ROOT/etc/cpt-base + [ -f "$cpt_confdir/base" ] && cpt_base=$cpt_confdir/base + # Regular expression used in pkg_checksums() and pkg_sources() in order to # identify VCS and comments re_vcs_or_com='^(#|(fossil|git|hg)\+)' @@ -2202,16 +2472,17 @@ create_cache() { # do nothing on a normal system. mkdir -p "$CPT_ROOT/" 2>/dev/null ||: - # Set a value for CPT_COMPRESS if it isn't set. - : "${CPT_COMPRESS:=gz}" - - # Unless being piped or the user specifically doesn't want colors, set - # colors. This can of course be overriden if the user specifically want - # colors during piping. - if { [ "$CPT_COLOR" != 0 ] && [ -t 1 ] ;} || [ "$CPT_COLOR" = 1 ]; then - colory="\033[1;33m" colorb="\033[1;34m" colre="\033[m" - fi + # Set the default compression to gzip, and warn the user if the value is + # invalid. + case ${CPT_COMPRESS:=gz} in + bz2|gz|xz|zst|lz) ;; + *) warn "'$CPT_COMPRESS' is not a valid CPT_COMPRESS value, falling back to 'gz'" + CPT_COMPRESS=gz + esac + # Set colors if they are to be enabled. + # shellcheck disable=2034 + colors_enabled && colory="\033[1;33m" colorb="\033[1;34m" colre="\033[m" colbold="\033[1m" } # If the library is being called with its own name, run arguments. diff --git a/src/cpt-remove b/src/cpt-remove index cce3739..f5a5abf 100755 --- a/src/cpt-remove +++ b/src/cpt-remove @@ -10,7 +10,7 @@ parser_definition() { if [ -f ./cpt-lib ]; then . ./cpt-lib; else . cpt-lib; fi -[ "$1" ] || { set -- "${PWD##*/}"; export CPT_PATH=${PWD%/*}:$CPT_PATH ;} +[ "$1" ] || { set -- "${PWD##*/}"; export CPT_PATH="${PWD%/*}:$CPT_PATH" ;} [ -w "$CPT_ROOT/" ] || [ "$uid" = 0 ] || { as_root "$0" "$@" @@ -20,3 +20,7 @@ if [ -f ./cpt-lib ]; then . ./cpt-lib; else . cpt-lib; fi create_cache pkg_order "$@" for pkg in $redro; do pkg_remove "$pkg" "${CPT_FORCE:-check}"; done + +# After all the removals are finished, run an end-remove hook. There may +# be some things that we may want to run, but not per package. +run_hook end-remove "" "$CPT_ROOT" diff --git a/src/cpt-search b/src/cpt-search index fbe0b04..9eeac75 100755 --- a/src/cpt-search +++ b/src/cpt-search @@ -2,26 +2,64 @@ # Search for a package parser_definition() { - setup REST help:usage -- "usage: ${0##*/} [pkg...]" + setup REST help:usage -- "usage: ${0##*/} [-dFoqs] [pkg...]" msg -- '' 'Options:' flag SEARCH_PATH -d "on:$CPT_PATH" -- "Do not search the installed package database" flag all -s --single init:=1 on:'' -- "Only show the first instance of a package" - flag others -o --others -- "Use the current directory as the package" \ + flag mode -q --query on:2 -- "Search packages making use of package descriptions" + flag fflag -F --fixed -- "Run query mode interpreting the given pattern as a" \ + "fixed string" + flag mode -o --others on:1 -- "Use the current directory as the package" \ "and show other instances" - global_options + global_options compact } if [ -f ./cpt-lib ]; then . ./cpt-lib; else . cpt-lib; fi -# The 'all' variable is set by the option parser. -# shellcheck disable=2154 -case $others in - '') for pkg; do pkg_find "$pkg" "${all:+all}"; done ;; - *) pkg_find "${PWD##*/}" all | - while read -r pkg_dir; do case $pkg_dir in - "$PWD") ;; - *) printf '%s\n' "$pkg_dir" - [ "$all" ] || exit 0 - esac - done + +# The 'all' and 'mode' variables are set by the option parser, and are never +# modified in the subshell. +# shellcheck disable=2154,2030,2031 +case $mode in + '') + # Default mode of operation. + for pkg; do pkg_find "$pkg" "${all:+all}"; done + ;; + 1) + # Use the current directory as the package and show other instances. + pkg_find "${PWD##*/}" all | + while read -r pkg_dir; do + case $pkg_dir in + "$PWD") ;; + *) printf '%s\n' "$pkg_dir" + [ "$all" ] || exit 0 + esac + done + ;; + 2) + # Make a partial string search using the name and the description of all + # packages. This is a "pretty information" mode, and its output is not + # meant to be used in scripting. There is a whole library meant for + # scripting. + pkg_find \* all | + while read -r pkg_dir; do + name=${pkg_dir##*/} + desc=$(pkg_query_meta "$pkg_dir" description ||:) + + # We pipe the name and description to the given query and + # continue if it's not a match + printf '%s %s\n' "$name" "$desc" | + "$grep" "-iq${fflag:+F}" -- "$1" || continue + + read -r ver rel < "$pkg_dir/version" + printf '%b%s%b@%s %s-%s\n %s\n\n' \ + "$colorb" "$name" "$colre" \ + "$pkg_dir" \ + "$ver" "$rel" \ + "$desc" + + # I don't know why someone use the '-s' flag on this operation + # mode, but go ahead. + [ "$all" ] || exit 0 + done esac diff --git a/tests/repository/contrib-dummy-pkg/meta b/tests/repository/contrib-dummy-pkg/meta new file mode 100644 index 0000000..305bd33 --- /dev/null +++ b/tests/repository/contrib-dummy-pkg/meta @@ -0,0 +1 @@ +description: This is a dummy package diff --git a/www/index.md b/www/index.md index 99ad599..e518540 100644 --- a/www/index.md +++ b/www/index.md @@ -1,5 +1,7 @@ # Home +[![builds.sr.ht status](https://builds.sr.ht/~carbslinux/cpt.svg)](https://builds.sr.ht/~carbslinux/cpt?) + CPT is the package management toolset written for Carbs Linux. Its aim is to provide a stable, powerful, and easily used library for package management that complements the tools that come with it. It has the following features: @@ -27,12 +29,12 @@ complements the tools that come with it. It has the following features: users with the `$PATH` variable. - **Serve repositories with your method** - Package repositories can be served - in a variety of formats, they can be either local, served with Git, Mercurial, - or through the `rsync` method, with Fossil integration to be added soon. + in a variety of formats, they can be either local, served with `git`, + `mercurial`, `fossil`, or through the `rsync` method. <hr> -### Latest Release: 6.1.0 ([2021-07-22](/timeline?c=6.1.0)) +### Latest Release: 7.0.2 ([2023-02-05](/timeline?c=7.0.2)) - [Download](/uvlist?byage=1) - [Changelog](/doc/trunk/CHANGELOG.md) |