diff options
-rw-r--r-- | .build.yml | 9 | ||||
-rw-r--r-- | .fossil-settings/ignore-glob | 1 | ||||
-rw-r--r-- | CHANGELOG.md | 44 | ||||
-rw-r--r-- | Makefile | 1 | ||||
-rwxr-xr-x | configure | 14 | ||||
-rwxr-xr-x | contrib/cpt-chbuild | 33 | ||||
-rw-r--r-- | docs/Makefile | 9 | ||||
-rw-r--r-- | docs/cpt.org | 88 | ||||
-rw-r--r-- | docs/cpt.texi | 120 | ||||
-rw-r--r-- | docs/cpt.txt | 190 | ||||
-rw-r--r-- | examples/hooks/clean-packages | 7 | ||||
-rw-r--r-- | examples/hooks/makewhatis | 10 | ||||
-rw-r--r-- | spec/01_lib_spec.sh | 37 | ||||
-rw-r--r-- | spec/02_src_spec.sh | 2 | ||||
-rwxr-xr-x | src/cpt-alternatives | 78 | ||||
-rwxr-xr-x | src/cpt-install | 4 | ||||
-rw-r--r-- | src/cpt-lib.in | 313 | ||||
-rwxr-xr-x | src/cpt-remove | 4 | ||||
-rw-r--r-- | tests/repository/contrib-dummy-pkg/meta | 1 | ||||
-rw-r--r-- | www/index.md | 6 |
20 files changed, 749 insertions, 222 deletions
@@ -5,9 +5,10 @@ packages: - bison - curl - rsync - - shellcheck + - emacs-nox - texinfo - gzip + - xz tasks: - install-pax: | git clone --quiet https://github.com/carbslinux/otools @@ -19,13 +20,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 403c3be..e3ce638 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,46 @@ this project _somewhat_ adheres to [Semantic Versioning]. [Semantic Versioning]: https://semver.org/spec/v2.0.0.html +[UNRELEASED] +-------------------------------------------------------------------------------- + +### 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). + +### 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. + +### 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 -------------------------------------------------------------------------------- @@ -43,7 +83,7 @@ this project _somewhat_ adheres to [Semantic Versioning]. [6.2.1] - 2021-09-20 ---------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- ### Fixed - `cpt-fork` follows symbolic links when forking packages. @@ -52,7 +92,7 @@ this project _somewhat_ adheres to [Semantic Versioning]. [6.2.0] - 2021-08-14 ---------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- ### BLAKE3 checksums @@ -13,6 +13,7 @@ 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 $@ @@ -1,6 +1,6 @@ #!/bin/sh -e -version=6.2.4 +version=Fossil die() { printf '%s: %s\n' "${0##*/}" "$*" >&2 @@ -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 diff --git a/contrib/cpt-chbuild b/contrib/cpt-chbuild index dfceab0..7a71c33 100755 --- a/contrib/cpt-chbuild +++ b/contrib/cpt-chbuild @@ -26,7 +26,7 @@ parser_definition() { global_options silent } -# shellcheck disable=1091 +# shellcheck source=../src/cpt-lib . cpt-lib die() { @@ -45,9 +45,9 @@ cd "${cac_dir:?}" # Remove the existing tarball and the chroot directory, so that they can be # downloaded again. -[ "$redownload" ] && rm -rf carbs-rootfs.tar.xz \ - carbs-rootfs.tar.xz.sum \ - carbs-chroot +[ "$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" @@ -59,12 +59,18 @@ cd "${cac_dir:?}" 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) @@ -73,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" @@ -83,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/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 adf45f7..3896909 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 @@ -370,8 +396,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 +853,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 @@ -1194,7 +1268,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 5ff1757..5c792a9 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 @@ -491,10 +529,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 +1009,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 +1032,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 @@ -1387,7 +1489,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. diff --git a/docs/cpt.txt b/docs/cpt.txt index e965da7..c89f02a 100644 --- a/docs/cpt.txt +++ b/docs/cpt.txt @@ -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). + + +4.1.1 CPT Base +-------------- - 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'. + 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 ~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -412,10 +445,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 +664,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 +915,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 +972,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,7 +999,7 @@ 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 @@ -943,13 +1025,13 @@ development manual for *Carbs Packaging Tools*. For development logs see 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 @@ -964,7 +1046,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 @@ -982,7 +1064,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 @@ -990,14 +1072,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 @@ -1007,14 +1089,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 @@ -1030,7 +1112,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 @@ -1042,7 +1124,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 @@ -1056,7 +1138,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 @@ -1076,7 +1158,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 @@ -1084,7 +1166,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 @@ -1098,7 +1180,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 @@ -1106,7 +1188,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 @@ -1118,10 +1200,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 @@ -1138,7 +1220,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 @@ -1146,7 +1228,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 @@ -1164,7 +1246,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, @@ -1173,17 +1255,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 @@ -1204,7 +1286,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 @@ -1212,7 +1294,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 @@ -1220,7 +1302,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 @@ -1253,17 +1335,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 @@ -1283,7 +1365,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 @@ -1309,7 +1391,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/spec/01_lib_spec.sh b/spec/01_lib_spec.sh index b56ac32..6612618 100644 --- a/spec/01_lib_spec.sh +++ b/spec/01_lib_spec.sh @@ -162,15 +162,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 +172,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 +183,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..5286ab9 100644 --- a/spec/02_src_spec.sh +++ b/spec/02_src_spec.sh @@ -20,7 +20,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 diff --git a/src/cpt-alternatives b/src/cpt-alternatives index 3c98753..87946f0 100755 --- a/src/cpt-alternatives +++ b/src/cpt-alternatives @@ -2,31 +2,83 @@ # List and swap to alternatives parser_definition() { - setup REST help:usage -- "usage: ${0##*/} [-] [package file]" - global_options silent + 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 } 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-install b/src/cpt-install index 78da9a8..8bafba6 100755 --- a/src/cpt-install +++ b/src/cpt-install @@ -27,6 +27,10 @@ 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%#*} diff --git a/src/cpt-lib.in b/src/cpt-lib.in index 23a75ec..27d57e4 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() { @@ -75,6 +76,47 @@ colors_enabled() { 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. This leaves us + # with the following awk script, because the POSIX shell is not up for the + # job without super ugly hacks. + awk 'function fv(s) { + for (sp in e) { + split (e[sp],t) + for (j in t) if (s == t[j]) return 0 + } return 1 + } + function el(_l) {for(i in e){_l=_l" "i;}; return _l;} + function ce(t) {if (!(t in e)) e[t]="";} + function err(s) {print "Dependency cycle deteced between: " s; exit 1;} + {ce($1);$1!=$2&&e[$1]=e[$1]" "$2;} + END { + do {p=el() + for (s in e) { + if (fv(s)) { + pr=s" "pr + split(e[s],t) + for(i in t){ce(t[i]);} + delete e[s] + } + } c=el() + } while (p != c) + if (length(p)!=0) err(p); + print pr + }' +} + trap_set() { # Function to set the trap value. case ${1:-cleanup} in @@ -90,20 +132,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 @@ -510,6 +548,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" \ @@ -551,23 +592,26 @@ 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 ;} - - if [ "$2" ]; then - logv "$2" "Running $1 hook" - else - logv "Running $1 hook" - fi + # 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 - TYPE=${1:-null} PKG=${2:-null} DEST=${3:-null} . "$CPT_HOOK" - CPT_HOOK=$oldCPT_HOOK + # 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 } # An optional argument could be provided to enforce a compression algorithm. @@ -844,12 +888,9 @@ 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 - + contains "$pkgs" "$1" || { + pkgs="$pkgs $1 " + [ "$2" = raw ] && _dep_append "$1" "$1" while read -r dep type || [ "$dep" ]; do # Skip comments and empty lines. [ "${dep##\#*}" ] || continue @@ -862,6 +903,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 "$1" "$dep" + fi + # Recurse through the dependencies of the child packages. Forward # the 'tree' operation. if [ "$2" = tree ]; then @@ -871,12 +922,14 @@ 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 " } } +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 @@ -884,9 +937,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 @@ -945,11 +999,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 @@ -1087,6 +1140,7 @@ pkg_build() { # separately from those detected as dependencies. explicit="$explicit $pkg " } done + pkg_depends_commit [ "$pkg_update" ] || explicit_build=$explicit @@ -1321,6 +1375,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). @@ -1342,7 +1399,7 @@ pkg_conflicts() { # temporary manifest to be parsed. 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 @@ -1351,7 +1408,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 @@ -1367,12 +1424,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 @@ -1423,13 +1480,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." @@ -1491,13 +1548,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}" @@ -1562,10 +1619,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 @@ -1573,7 +1631,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" @@ -1584,7 +1642,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" } @@ -1647,7 +1705,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" @@ -1659,8 +1717,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() { @@ -1675,7 +1733,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 @@ -1712,7 +1770,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" } @@ -1947,7 +2005,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 @@ -1961,7 +2024,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 @@ -1982,6 +2045,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" @@ -1992,18 +2062,17 @@ pkg_updates(){ cpt-install cpt log "Updated the package manager" - log "Re-run 'cpt update' to update your system" - - exit 0 + 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 } - [ "$outdated" ] || { - log "Everything is up to date" - return - } - - log "Packages to update: ${outdated% }" - # Tell 'pkg_build' to always prompt before build. pkg_update=1 @@ -2019,12 +2088,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. @@ -2035,13 +2104,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 @@ -2116,12 +2192,36 @@ 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 "$1" "$_ret" + out "$_ret" +} + +_tmp_create() { + # Create given file to the temporary directory and return its name + create_tmp + _ret=$(_tmp_name "$1") + # False positive, we are not reading from the file. + # shellcheck disable=2094 + out "$_ret" 3>> "$_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() { @@ -2135,6 +2235,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 && { @@ -2149,25 +2252,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 @@ -2233,6 +2345,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)\+)' diff --git a/src/cpt-remove b/src/cpt-remove index ae007eb..f5a5abf 100755 --- a/src/cpt-remove +++ b/src/cpt-remove @@ -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/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 2dc1fc5..eb150ee 100644 --- a/www/index.md +++ b/www/index.md @@ -27,12 +27,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.1 ([2021-08-04](/timeline?c=6.1.1)) +### Latest Release: 6.2.3 ([2022-02-02](/timeline?c=6.2.3)) - [Download](/uvlist?byage=1) - [Changelog](/doc/trunk/CHANGELOG.md) |