├── MIT ├── Makefile ├── README.md ├── common ├── ccm.skel ├── clean-chroot-manager64.in └── zsh-completion ├── contrib ├── README.md ├── bar │ └── PKGBUILD └── foo │ └── PKGBUILD └── doc └── clean-chroot-manager.1 /MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013-2024 graysky 2 | 3 | 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: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | 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. 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | VERSION = 2.233 2 | PN = clean-chroot-manager 3 | 4 | PREFIX ?= /usr 5 | BINDIR = $(PREFIX)/bin 6 | DOCDIR = $(PREFIX)/share/doc/$(PN)-$(VERSION) 7 | MANDIR = $(PREFIX)/share/man/man1 8 | SKELDIR = $(PREFIX)/share/$(PN) 9 | ZSHDIR = $(PREFIX)/share/zsh/site-functions 10 | RM = rm -f 11 | Q = @ 12 | 13 | all: 14 | $(Q)echo -e '\033[1;32mSetting version\033[0m' 15 | $(Q)sed 's/@VERSION@/'$(VERSION)'/' common/$(PN)64.in > common/$(PN)64 16 | 17 | install-bin: 18 | $(Q)echo -e '\033[1;32mInstalling main scripts and skel config...\033[0m' 19 | install -Dm755 common/$(PN)64 "$(DESTDIR)$(BINDIR)/$(PN)64" 20 | # default is to run in 64-bit mode so make shortcut without suffix 21 | ln -s $(PN)64 "$(DESTDIR)$(BINDIR)/ccm" 22 | # failsafe is to make a ccm64 and ccm32 for each build mode 23 | ln -s $(PN)64 "$(DESTDIR)$(BINDIR)/ccm64" 24 | install -Dm644 common/ccm.skel "$(DESTDIR)$(SKELDIR)/ccm.skel" 25 | install -d "$(DESTDIR)$(ZSHDIR)" 26 | install -m644 common/zsh-completion "$(DESTDIR)/$(ZSHDIR)/_ccm" 27 | 28 | install-man: 29 | $(Q)echo -e '\033[1;32mInstalling manpage...\033[0m' 30 | install -Dm644 doc/$(PN).1 "$(DESTDIR)$(MANDIR)/$(PN).1" 31 | gzip -9 "$(DESTDIR)$(MANDIR)/$(PN).1" 32 | ln -s $(PN).1.gz "$(DESTDIR)$(MANDIR)/ccm.1.gz" 33 | 34 | install: install-bin install-man 35 | 36 | uninstall: 37 | $(Q)$(RM) "$(DESTDIR)$(BINDIR)/$(PN)64" 38 | $(Q)$(RM) "$(DESTDIR)$(BINDIR)/ccm64" 39 | $(Q)$(RM) "$(DESTDIR)$(BINDIR)/ccm" 40 | $(Q)$(RM) "$(DESTDIR)$(MANDIR)/$(PN).1.gz" 41 | $(Q)$(RM) -r "$(DESTDIR)$(SKELDIR)" 42 | $(Q)$(RM) "$(DESTDIR)/$(ZSHDIR)/_ccm" 43 | 44 | clean: 45 | $(RM) common/$(PN)64 46 | 47 | .PHONY: help install-bin install-man install uninstall clean 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # clean-chroot-manager 2 | Wrapper script to manage buildroots when building packages under Arch Linux. 3 | 4 | ## Why use it? 5 | Ccm provides several advantages over the standard arch-build scripts: 6 | * Automatically manages a local repo so dependencies that you build are pulled transparently from that local repo. 7 | * Automatically setups and uses distcc to speed up compilation (if enabled). 8 | 9 | Managing a local repo is helpful if building a package that has a dependency that also has to be built (i.e. one that is not available from the Arch repos). Another key point of differentiation is that ccm can build packages using distcc. 10 | 11 | For example, let's say that we want to build "bar" from the AUR. "Bar" has a build dependency of "foo" which is also in the AUR. Rather than first building "foo", then installing "foo", then building "bar", and finally removing "foo", the local repo will save a copy of foo.pkg.tar.xz which is indexed automatically therein. Pacman within the buildroot is aware of the "foo" package thanks to the local repo. So, when the user tries to build "bar", pacman will silently grabs foo.pkg.tar.xz from the local repo as any other dependency. 12 | 13 | ## Download 14 | AUR Package: https://aur.archlinux.org/packages/clean-chroot-manager 15 | 16 | ## Setup 17 | `$XDG_CONFIG_HOME/clean-chroot-manager.conf` will be created on the first invocation of ccm and contains all user managed settings. Edit this file prior to running ccm a second time. Make sure the user running ccm has sudo rights to execute `/usr/bin/clean-chroot-manager` or `/usr/bin/ccm`. 18 | 19 | ## Options 20 | | Command | Description | 21 | | :---: | --- | 22 | | a | Add packages in current dir to the local repo. | 23 | | c | Create the buildroot. | 24 | | cd | Create the buildroot with distcc enabled (if you do not want to set up in the config file). | 25 | | cp | Purge all files in the CCACHE_DIR (optional if building with ccache). | 26 | | d | Delete all packages in the local repo without nuking the entire build (i.e. the packages you built to date). | 27 | | l | List the contents of the local repo (i.e. the packages you built to date). | 28 | | N | Nuke the buildroot and the external repo (if defined). | 29 | | n | Nuke the buildroot (delete it and everything under it). | 30 | | p | Preview settings. Show some bits about the buildroot itself. | 31 | | R | Repackage the current package if built. The equivalent of `makepkg -sR` in the buildroot. | 32 | | s | Run makepkg in build mode under the buildroot. The equivalent of `makepkg -s` in the buildroot. | 33 | | S | Run makepkg in build mode under the buildroot without first cleaning it. Useful for rebuilds without dirtying the pristine buildroot or when building packages with many of the same deps. | 34 | | t | Toggle [core-testing]/[extra-testing] on/off in the buildroot and update packages accordingly (upgrade or downgrade). | 35 | | u | Update the packages inside the buildroot. The equivalent of `pacman -Syu` in the buildroot. | 36 | 37 | ## Example Usage 38 | Create a clean 64-bit buildroot under the path defined in the aforementioned config file: 39 | ``` 40 | $ sudo ccm c 41 | ``` 42 | 43 | Attempt to build the package in the clean 64-bit buildroot. If successful, the package will be added to a local repo so that it will be available for use as a dependency for building other packages: 44 | ``` 45 | $ cd /path/to/PKGBUILD 46 | $ sudo ccm s 47 | ``` 48 | 49 | List out the contents of the 64-bit buildroot's local repo assuming something has been built. Useful to see what is present: 50 | ``` 51 | $ sudo ccm l 52 | ``` 53 | Deletes everything under the top level of the 64-bit buildroot effectively removing it from the system: 54 | ``` 55 | $ sudo ccm n 56 | ``` 57 | 58 | ## Tips 59 | * Since ccm requires sudo rights, consider making an alias for invoking it as such in your ~/.bashrc or the like. For example: 60 | 61 | ``` 62 | alias ccm='sudo ccm' 63 | ``` 64 | * If you have multiple PCs on your LAN, consider having them help you compile via distcc which is supported within ccm. See `$XDG_CONFIG_HOME/clean-chroot-manager.conf` for setup instructions. 65 | * If your machine has lots of memory, consider locating the buildroot to tmpfs to avoid disk usage/minimize access times. One way is to simply define a directory to mount as tmpfs like so in `/etc/fstab`: 66 | 67 | `tmpfs /scratch tmpfs nodev,size=20G 0 0` 68 | 69 | In order to have the expected `CHROOTPATH64` directory created, we can use a systemd tmpfile like so: 70 | ``` 71 | /etc/tmpfiles.d/ccm_dirs.conf 72 | d /scratch/.buildroot 0750 foo users - 73 | 74 | Note that this is only needed if the location of the buildroot are on a volatile filesystem like tmpfs. 75 | ``` 76 | -------------------------------------------------------------------------------- /common/ccm.skel: -------------------------------------------------------------------------------- 1 | # Fully qualified path for build root. 2 | # This should not use a variable like $HOME. If your machine has lots 3 | # of memory, consider locating this to tmpfs to avoid usage to the disk and 4 | # to minimize access times but know that unless you copy the contents to 5 | # physical media, it will not survive a reboot. See the manpage for tips. 6 | CHROOTPATH64="/scratch/.buildroot" 7 | 8 | # Number of threads makepkg in the clean chroot will use when building. 9 | # The typical rule is physical cores + 1. 10 | THREADS=9 11 | 12 | # Optionally uncomment and define a custom pacman.conf and/or a custom 13 | # makepkg.conf for the buildroot using a fully qualified path below. 14 | # Leaving these two undefined to use the system files. 15 | 16 | #CUSTOM_PACMAN_CONF='/usr/share/devtools/pacman.conf.d/extra-testing.conf' 17 | #CUSTOM_MAKEPKG_CONF='/usr/share/devtools/makepkg.conf.d/x86_64.conf' 18 | 19 | # Optionally uncomment and define a custom location and name for the local chroot 20 | # package repo. 21 | #REPO="/home/foo/localrepo" 22 | #REPO_NAME='chroot_local' 23 | 24 | # Optionally uncomment to pass the --nocheck flag to the build which will skip 25 | # the check function in the PKGBUILD is it is present. 26 | #NOCHECK=1 27 | 28 | # Optionally define the format of compression for compiled packages. Leave this 29 | # undefined to use the Arch default. 30 | PKGEXT= 31 | 32 | # If set, the value defined will be used in the buildroot's packages. 33 | PACKAGER= 34 | 35 | # Set this variable to anything if you want to run namcap on the built package. 36 | RUNNAMCAP= 37 | 38 | # Set this to anything if you want makepkg to build using distcc for faster 39 | # compilation. You must have distcc nodes properly configured on volunteers 40 | # you define below. It does NOT need to be running on the native environment. 41 | # 42 | # Alternatively, you can invoke ccm with the 'cd' flag to create the chroot 43 | # with distcc enabled rather than editing this value. 44 | # 45 | # For more on distcc, see: https://wiki.archlinux.org/index.php/Distcc 46 | RUNDISTCC= 47 | 48 | # This is only needed for users planning to build with distcc. Take care to 49 | # include the localhost plus all volunteers you define below. As a rule of thumb, 50 | # set to about twice the total number of available server CPUs. See the distcc 51 | # man page for more info. 52 | DISTCC_THREADS= 53 | 54 | # Define all machines in the distcc cluster below using the distcc syntax of: 55 | # "hostname/threads" and remember to list localhost/n first, followed by your 56 | # volunteer nodes listed in decreasing order of CPU power. Additional supported 57 | # options are passed through, see the manpage for distcc. 58 | # 59 | # In my experience, one sees best results using twice the number of physical 60 | # cores on the volunteer machines. In the example below foo is a quad and bar 61 | # is a dual. 62 | #DISTCC_HOSTS="localhost/9 foo/8 bar/4" 63 | 64 | # To build with ccache in the buildroot, uncomment and define the directory where 65 | # ccache will store its data below. If you're using a custom makepkg.conf 66 | # (see below), you MUST enable ccache that file in addition to defining the path 67 | # below. For more info about ccache, see: https://wiki.archlinux.org/index.php/ccache 68 | # 69 | #CCACHE_DIR="/scratch/.ccache" 70 | 71 | # Optionally uncomment and define the file ccm will keep as a log of built packages 72 | # including the full path. If undefined $HOME/ccm-build.log will be created automatically 73 | #BUILDLOG= 74 | -------------------------------------------------------------------------------- /common/clean-chroot-manager64.in: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # clean-chroot-manager by graysky 4 | # 5 | 6 | # shellcheck disable=1090 7 | 8 | VERS='@VERSION@' 9 | PKG='clean-chroot-manager' 10 | SKEL="/usr/share/$PKG/ccm.skel" 11 | 12 | ### Begin insert of Arch script 13 | # Avoid any encoding problems 14 | export LANG=C 15 | 16 | # check if messages are to be printed using color 17 | unset ALL_OFF BOLD BLUE GREEN RED YELLOW 18 | if [[ -t 2 ]]; then 19 | # prefer terminal safe colored and bold text when tput is supported 20 | if tput setaf 0 &>/dev/null; then 21 | ALL_OFF="$(tput sgr0)" 22 | BOLD="$(tput bold)" 23 | BLUE="${BOLD}$(tput setaf 4)" 24 | GREEN="${BOLD}$(tput setaf 2)" 25 | RED="${BOLD}$(tput setaf 1)" 26 | YELLOW="${BOLD}$(tput setaf 3)" 27 | else 28 | ALL_OFF="\e[1;0m" 29 | BOLD="\e[1;1m" 30 | BLUE="${BOLD}\e[1;34m" 31 | GREEN="${BOLD}\e[1;32m" 32 | RED="${BOLD}\e[1;31m" 33 | YELLOW="${BOLD}\e[1;33m" 34 | fi 35 | fi 36 | readonly ALL_OFF BOLD BLUE GREEN RED YELLOW 37 | 38 | ### End insert of Arch script 39 | 40 | if [[ -z "$SUDO_USER" ]]; then 41 | if logname &>/dev/null; then 42 | USER=$(logname) 43 | fi 44 | elif [[ "$SUDO_USER" = "root" ]]; then 45 | mesg="Cannot determine your username." 46 | echo -e "${RED}==> ERROR:${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}"; exit 1 47 | else 48 | USER="$SUDO_USER" 49 | fi 50 | 51 | HOMEDIR="$(getent passwd "$USER" | cut -d: -f6)" 52 | 53 | # allow user to override from cli thus using multiple files as needed 54 | XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOMEDIR/.config}" 55 | CFGFILE=${CFGFILE:-$XDG_CONFIG_HOME/$PKG.conf} 56 | 57 | # dependency checks probably not needed but they do not hurt 58 | if ! command -v mkarchroot >/dev/null 2>&1; then 59 | mesg="devtools is required to use this script." 60 | echo -e "${RED}==> ERROR:${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}"; exit 1 61 | fi 62 | 63 | check_config() { 64 | if [[ $EUID -ne 0 ]]; then 65 | local mesg="This script must be called as root." 66 | echo -e "${RED}==> ERROR:${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}"; exit 1 67 | fi 68 | 69 | if [[ ! -f $SKEL ]]; then 70 | local mesg="$SKEL is missing. Reinstall this package to continue." 71 | echo -e "${RED}==> ERROR:${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}"; exit 1 72 | fi 73 | 74 | if [[ ! -f "$CFGFILE" ]]; then 75 | echo -e "${BOLD}------------------------------------------------------------${ALL_OFF}" 76 | echo -e "${BOLD} No config file found so creating a fresh one in:${ALL_OFF}" 77 | echo -e "${BOLD}${BLUE} $XDG_CONFIG_HOME/$PKG.conf${ALL_OFF}" 78 | echo 79 | echo -e "${BOLD} Edit this file before invoking $PKG again.${ALL_OFF}" 80 | echo -e "${BOLD}------------------------------------------------------------${ALL_OFF}" 81 | su -c "install -Dm644 $SKEL $CFGFILE" "$USER" 82 | CORES=$(( $(nproc --all) + 1 )) 83 | sed -i "/^THREADS=/ s,9,$CORES," "$XDG_CONFIG_HOME/$PKG.conf" 84 | exit 0 85 | else 86 | . "$CFGFILE" 87 | 88 | # parse config file for correctness 89 | if [[ ! -d "$CHROOTPATH64" ]]; then 90 | mkdir -p "$CHROOTPATH64" || { 91 | local mesg="Invalid CHROOTPATH64 defined in $CFGFILE" 92 | echo -e "${RED}==> ERROR:${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}"; exit 1 93 | } 94 | fi 95 | 96 | if ! [[ "$THREADS" =~ ^[0-9]+$ ]]; then 97 | local mesg="Invalid setting for THREADS defined in $SKEL" 98 | echo -e "${RED}==> ERROR:${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}"; exit 1 99 | fi 100 | fi 101 | 102 | if [[ -n "$DISTCC_THREADS" ]]; then 103 | if ! [[ "$DISTCC_THREADS" =~ ^[0-9]+$ ]]; then 104 | local mesg="Invalid setting for DISTCC_THREADS defined in $SKEL" 105 | echo -e "${RED}==> ERROR:${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}"; exit 1 106 | fi 107 | fi 108 | 109 | # ccache must be present on the host system or else devtools scripts 110 | # will throw an error. 111 | # probably redundant to add a check here but might be more straight forward 112 | # for users if we do... will leave it out for now. 113 | 114 | if [[ -z ${CCACHE_DIR+x} ]]; then 115 | # no ccache is being used 116 | true 117 | else 118 | # make sure we have a dir with correct permissions for ccache to use 119 | if [[ ! -d "$CCACHE_DIR" ]]; then 120 | local mesg="Invalid CCACHE_DIR defined in $CFGFILE" 121 | echo -e "${RED}==> ERROR:${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}"; exit 1 122 | fi 123 | 124 | if sudo -u "$USER" -H sh -c "[ ! -w \"$CCACHE_DIR\" ]"; then 125 | local mesg="$USER has no write permissions on $CCACHE_DIR" 126 | echo -e "${RED}==> ERROR:${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}"; exit 1 127 | fi 128 | fi 129 | 130 | # identify status of testing repo 131 | PACFILE="$CHROOTPATH64/root/etc/pacman.conf" 132 | if [[ -f "$PACFILE" ]]; then 133 | if grep -q '^#\[core-testing\]' "$PACFILE"; then 134 | # testing repo is not enabled 135 | export TESTING="Disabled" 136 | elif grep -q '^\[core-testing\]' "$PACFILE"; then 137 | # testing repo is enabled 138 | export TESTING="Enabled" 139 | fi 140 | fi 141 | 142 | # if user defined a trailing slash remove it here 143 | if [[ -n "$REPO" ]]; then 144 | REPO="${REPO%/}" 145 | # check for external path 146 | if [[ ! -d "$REPO" ]]; then 147 | # make the empty dir for the repo in root's copy of the buildroot 148 | # note that we still have to modify pacman.conf to add a line for the repo 149 | # but only after it is populated with packages which is in the indexit function 150 | [[ -d "$REPO" ]] || mkdir "$REPO" 151 | fi 152 | if sudo -u "$USER" -H sh -c "[ ! -w \"$REPO\" ]"; then 153 | local mesg="User $USER has no write permissions on $REPO" 154 | echo -e "${RED}==> ERROR:${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}"; exit 1 155 | fi 156 | else 157 | REPO="${CHROOTPATH64%/}/root/repo" 158 | export INTERNAL_REPO=1 159 | fi 160 | 161 | if [[ -z "$REPO_NAME" ]]; then 162 | REPO_NAME="chroot_local" 163 | fi 164 | 165 | if [[ -z "$BUILDLOG" ]]; then 166 | BUILDLOG="$HOMEDIR"/ccm-build.log 167 | elif [[ ! -w "$BUILDLOG" ]]; then 168 | local mesg="User $USER has no write permissions to BUILDLOG $BUILDLOG" 169 | echo -e "${RED}==> ERROR:${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}"; exit 1 170 | fi 171 | 172 | if [[ ! -f "$BUILDLOG" ]]; then 173 | su -c "echo -e 'Date\tPackage\tTime' > $BUILDLOG" "$USER" 174 | fi 175 | } 176 | 177 | check_buildroot() { 178 | if [[ ! -f "$CHROOTPATH64"/root/.arch-chroot ]]; then 179 | local mesg="No buildroot found; create one using the 'c' option and try again." 180 | echo -e "${RED}==> ERROR:${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}"; exit 1 181 | fi 182 | } 183 | 184 | check_active() { 185 | if pidof -q -x makechrootpkg; then 186 | mesg="Active build detected. Try again when not building." 187 | echo -e "${RED}==> ERROR:${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}"; exit 1 188 | fi 189 | } 190 | 191 | create() { 192 | if [[ -f "$CHROOTPATH64"/root/.arch-chroot ]]; then 193 | local mesg="Working directory $CHROOTPATH64 already exists." 194 | echo -e "${RED}==> ERROR:${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}"; exit 1 195 | fi 196 | 197 | local pkgs=('base-devel') 198 | 199 | if [[ -n "$RUNDISTCC" ]]; then 200 | pkgs+=('distcc') 201 | fi 202 | 203 | [[ -d "$CCACHE_DIR" ]] && pkgs+=('ccache') 204 | 205 | if [[ -f "$CUSTOM_PACMAN_CONF" ]]; then 206 | insargs+=(-C "$CUSTOM_PACMAN_CONF") 207 | else 208 | # with pacman 7.0.0 having a setting of 'DownloadUser = alpm' in /etc/pacman.conf 209 | # will cause the creation of the chroot to break so this is a hacky fix 210 | if [[ -f /etc/pacman.conf ]]; then 211 | sed '/DownloadUser = alpm/d' /tmp/ccm-temp-pac.conf 212 | insargs+=(-C /tmp/ccm-temp-pac.conf) 213 | fi 214 | fi 215 | 216 | if [[ -f "$CUSTOM_MAKEPKG_CONF" ]]; then 217 | insargs+=(-M "$CUSTOM_MAKEPKG_CONF") 218 | else 219 | # there can be disconnects in variables between makepkg.conf provided by the pacman 220 | # package and the one provided by the devtools package so in order to maintain 221 | # consistent behavior of scripts like extra-x86_64-build and others listed on the wiki 222 | # under the "Convenience way" section (link below), we use the devtools 223 | # makepkg-x86_64.conf rather than the one defined on the live system 224 | # 225 | # https://wiki.archlinux.org/title/DeveloperWiki:Building_in_a_clean_chroot#Convenience_way 226 | insargs+=(-M /usr/share/devtools/makepkg.conf.d/x86_64.conf) 227 | fi 228 | 229 | if ! mkarchroot "${insargs[@]}" "$CHROOTPATH64"/root "${pkgs[@]}"; then 230 | exit 1 231 | fi 232 | 233 | if [[ -n "$RUNDISTCC" ]]; then 234 | if [[ $DISTCCFAIL -ne 1 ]]; then 235 | sed -i -e '/#DISTCC_HOSTS/ s,#,,' \ 236 | -i -e "/^DISTCC_HOSTS/ s|=\"\"|=\"$DISTCC_HOSTS\"|" \ 237 | -i -e '/^BUILDENV/ s,!distcc,distcc,' "$CHROOTPATH64"/root/etc/makepkg.conf 238 | fi 239 | fi 240 | 241 | [[ -d "$CCACHE_DIR" ]] && 242 | sed -i -e '/^BUILDENV/ s,!ccache,ccache,' "$CHROOTPATH64"/root/etc/makepkg.conf 243 | 244 | [[ -n "$PKGEXT" ]] && 245 | sed -i -e "s/^PKGEXT.*/PKGEXT=\"$PKGEXT\"/" "$CHROOTPATH64"/root/etc/makepkg.conf 246 | 247 | # Create the internal repo, if it doesn't exist yet 248 | if [[ ! -d "$REPO" ]]; then 249 | # make the empty dir for the repo in root's copy of the buildroot 250 | # note that we still have to modify pacman.conf to add a line for the repo 251 | # but only after it is populated with packages which is in the indexit function 252 | [[ -d "$REPO" ]] || mkdir "$REPO" 253 | chown :alpm -R "$REPO" 254 | chown "$USER" "$REPO" 255 | fi 256 | # pacman 7.0.0 requires alpm gid for local repos 257 | if [[ -d "$REPO" ]]; then 258 | dir_gid=$(stat -c "%G" "$REPO") 259 | if [[ "$dir_gid" == "alpm" ]]; then 260 | true 261 | else 262 | chown :alpm -R "$REPO" 263 | fi 264 | fi 265 | if sudo -u "$USER" -H sh -c "[ ! -w \"$REPO\" ]"; then 266 | local mesg="User $USER has no write permissions on $REPO" 267 | echo -e "${RED}==> ERROR:${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}"; exit 1 268 | fi 269 | 270 | # if we are using an external repo and if it has a db, add it to pacman.conf if not do nothing 271 | if [[ $INTERNAL_REPO -ne 1 ]]; then 272 | if [[ -f "$REPO/$REPO_NAME.db.tar.gz" ]]; then 273 | if ! grep -q "$REPO_NAME" "$CHROOTPATH64/root/etc/pacman.conf"; then 274 | # add a local repo to buildroot 275 | sed -i "/\[core-testing\]/i \ 276 | # Added by clean-chroot-manager\n[$REPO_NAME]\nSigLevel = Never\nServer = file://$REPO\n" \ 277 | "$CHROOTPATH64/root/etc/pacman.conf" 278 | fi 279 | fi 280 | fi 281 | } 282 | 283 | addit() { 284 | if ! su -c "rsync -rlxDu --exclude '*.log' ./*.pkg.tar* $REPO/" "$USER" &>/dev/null; then 285 | local mesg="No packages found to add to local repo." 286 | echo -e "${RED}==> ERROR:${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}"; exit 1 287 | fi 288 | } 289 | 290 | build() { 291 | args=('-u') 292 | [[ $NOCHECK -eq 1 ]] && postargs+=('--nocheck') 293 | [[ $CLEANIT -eq 1 ]] && args+=('-c') 294 | [[ -n "$RUNNAMCAP" ]] && args+=('-n') 295 | [[ -d "$CCACHE_DIR" ]] && args+=(-d "$CCACHE_DIR:/build/.ccache") 296 | [[ -d "$DISTCC_DIR" ]] && args+=(-d "$DISTCC_DIR:/build/.distcc") 297 | 298 | if [[ -f "$CUSTOM_MAKEPKG_CONF" ]]; then 299 | # makechrootpkg reads MAKEFLAGS and PACKAGER from the defined makepkg.conf 300 | # so honor those values here by not prefixing the call to makechrootpkg 301 | #echo makechrootpkg "${args[@]}" -r "$CHROOTPATH64" -- "${postargs[@]}" 302 | start=$(date +%s) 303 | if ! nice -n 19 makechrootpkg "${args[@]}" -r "$CHROOTPATH64" -- "${postargs[@]}"; then 304 | exit 1 305 | fi 306 | finish=$(date +%s) 307 | else 308 | # not using a custom makepkg.conf which means we're using the one on the live system so 309 | # we honor the values for MAKEFLAGS and PACKAGER in ~/.config/clean-chroot-manager.conf 310 | start=$(date +%s) 311 | if ! PACKAGER="$PACKAGER" MAKEFLAGS=-j$THREADS nice -n 19 makechrootpkg "${args[@]}" -r "$CHROOTPATH64" -- "${postargs[@]}"; then 312 | exit 1 313 | fi 314 | finish=$(date +%s) 315 | fi 316 | 317 | # makepkg can can fail to build without throwing an error code so stop if 318 | # no .pkg.tar* is present in the dir 319 | [[ -n $(find . -maxdepth 1 -type f -name '*.pkg.tar*') ]] || exit 1 320 | } 321 | 322 | indexit() { 323 | local mesg="Adding all packages in current dir to buildroot repo..." 324 | echo -e "${YELLOW}---->${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}" 325 | 326 | # external repos with db files are already setup when we made the buildroot 327 | # but for internal repos, if this is the first time a package has been built, 328 | # append the local repo to the buildroot's pacman.conf 329 | 330 | if ! grep -q "$REPO_NAME" "$CHROOTPATH64/root/etc/pacman.conf"; then 331 | # add a local repo to buildroot 332 | sed -i "/\[core-testing\]/i # Added by clean-chroot-manager\n[$REPO_NAME]\nSigLevel = Never\nServer = file://$REPO\n" "$CHROOTPATH64/root/etc/pacman.conf" 333 | 334 | # copy this pacman.conf back to the user copy if it exists... it may not if the user creates a 335 | # fresh buildroot and wants to populate it with pre-built pacakges 336 | if [[ -f "$CHROOTPATH64/$USER/etc/pacman.conf" ]]; then 337 | cp "$CHROOTPATH64/root/etc/pacman.conf" "$CHROOTPATH64/$USER/etc/pacman.conf" 338 | fi 339 | fi 340 | 341 | # it could be that the user is building for both i686 and x86_64 342 | # in which case we don't want to pollute the pure x86_64 repo 343 | # with i686 packages so only process 'x86_64' and 'any' types 344 | GLOBIGNORE="*namcap.log" 345 | for i in *.pkg.tar*; do 346 | su -c "cp $i $REPO" "$USER" || exit 1 347 | su -c "repo-add $REPO/$REPO_NAME.db.tar.gz $REPO/$i" "$USER" || exit 1 348 | 349 | ### do we really want this? 350 | # make sure that the buildroot package matches the live pacman cache package 351 | # to avoid a checksum error if the user builds the same package wo/ bumping the pkgver 352 | #[[ -f "/var/cache/pacman/pkg/$i" ]] && rm -f "/var/cache/pacman/pkg/$i" 353 | done 354 | unset GLOBIGNORE 355 | 356 | if [[ -n "$start" ]]; then 357 | diff=$(echo "$finish-$start" | bc) 358 | if [[ $diff -gt 86400 ]]; then 359 | howlong=$(printf '%d days, %02d:%02d:%02d\n' $((diff/86400)) $(((diff%86400)/3600)) $(((diff%3600)/60)) $((diff%60))) 360 | else 361 | howlong=$(printf '%02d:%02d:%02d\n' $((diff/3600)) $(((diff%3600)/60)) $((diff%60))) 362 | fi 363 | mesg="Total build time was $howlong" 364 | echo -e "${YELLOW}---->${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}" 365 | fi 366 | 367 | # The rm statement above can return 1 if the file to remove is not found, 368 | # causing the function to return a non-zero error code even if everything 369 | # went fine. If we've made it to this point, the build was run 370 | # successfully, so return 0 instead 371 | return 0 372 | } 373 | 374 | update() { 375 | local mesg="Updating the buildroot..." 376 | echo -e "${YELLOW}---->${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}" 377 | arch-nspawn "$CHROOTPATH64"/root pacman -Syu --noconfirm 378 | } 379 | 380 | repocheck() { 381 | if [[ ! -f "$REPO/$REPO_NAME.db.tar.gz" ]]; then 382 | local mesg="Local repo is empty. Build or manually populate packages first." 383 | echo -e "${RED}==> ERROR:${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}"; exit 1 384 | fi 385 | } 386 | 387 | list() { 388 | local mesg="Listing out packages in buildroot repo..." 389 | echo -e "${GREEN}==>${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}" 390 | ls --color -lhF -I 'chroot*' "$REPO" 391 | } 392 | 393 | delete() { 394 | mapfile -t packages < <(pacman -Sl chroot_local | awk '{print $2}') 395 | mapfile -t files < <(find "$REPO" -name '*.pkg.tar*') 396 | 397 | for i in "${packages[@]}"; do 398 | # TODO: make this less hacky 399 | # if run multiple times repo-remove will throw errors about missing packages 400 | repo-remove -q "$REPO/$REPO_NAME.db.tar.gz" "$i" 2>/dev/null 401 | done 402 | 403 | for i in "${files[@]}"; do 404 | rm -f "$i" 405 | done 406 | } 407 | 408 | testing() { 409 | if [[ "$TESTING" = "Disabled" ]]; then 410 | # switch on testing 411 | local mesg="Enabling [*-testing] in buildroot..." 412 | echo -e "${YELLOW}---->${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}" 413 | sed -i -e '/^#\[core-testing\]/ s,#,,' \ 414 | -i -e '/^\[core-testing\]/{$!N; s,#,,}' "$CHROOTPATH64"/root/etc/pacman.conf 415 | 416 | sed -i -e '/^#\[extra-testing\]/ s,#,,' \ 417 | -i -e '/^\[extra-testing\]/{$!N; s,#,,}' "$CHROOTPATH64"/root/etc/pacman.conf 418 | 419 | local mesg="Forcing an update to use any affected packages..." 420 | echo -e "${YELLOW}---->${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}" 421 | arch-nspawn "$CHROOTPATH64"/root pacman -Syu --noconfirm 422 | elif [[ "$TESTING" = "Enabled" ]]; then 423 | # switch off testing 424 | local mesg="Disabling [*-testing] in buildroot..." 425 | echo -e "${YELLOW}---->${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}" 426 | 427 | sed -i -e '/^\[core-testing\]/ s,\[,#\[,' \ 428 | -i -e '/^#\[core-testing\]/{$!N; s,I,#I,}' "$CHROOTPATH64"/root/etc/pacman.conf 429 | 430 | sed -i -e '/^\[extra-testing\]/ s,\[,#\[,' \ 431 | -i -e '/^#\[extra-testing\]/{$!N; s,I,#I,}' "$CHROOTPATH64"/root/etc/pacman.conf 432 | 433 | local mesg="Downgrading affected packages if any..." 434 | echo -e "${YELLOW}---->${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}" 435 | arch-nspawn "$CHROOTPATH64"/root pacman -Syuu --noconfirm 436 | fi 437 | } 438 | 439 | preview() { 440 | echo -e "${BOLD}$PKG v$VERS${ALL_OFF}" 441 | echo 442 | if [[ -f "$CHROOTPATH64"/root/.arch-chroot ]]; then 443 | HERE=1 444 | PRESENT="${BOLD}($(du -sh "$CHROOTPATH64" 2>/dev/null|awk '{ print $1 }'))${ALL_OFF}" 445 | else 446 | PRESENT="${BOLD}${RED}(None present)${ALL_OFF}" 447 | fi 448 | 449 | echo -en "${BOLD} buildroot path:" 450 | echo -e "$(tput cr)$(tput cuf 18)${BLUE}$CHROOTPATH64/${ALL_OFF}${BOLD} $PRESENT${ALL_OFF}" 451 | 452 | if [[ -n "$CCACHE_DIR" ]]; then 453 | CCSIZE=$(du -sh "$CCACHE_DIR" 2>/dev/null|awk '{ print $1 }') 454 | # if null contents write out a pretty message 455 | [[ "$CCSIZE" = "0" ]] && CCSIZE="${BOLD}${RED}(Empty cache)${ALL_OFF}" || CCSIZE="${BOLD}($CCSIZE)${ALL_OFF}" 456 | echo -en "${BOLD} ccache path:" 457 | echo -e "$(tput cr)$(tput cuf 18)${BLUE}$CCACHE_DIR/${ALL_OFF} $CCSIZE" 458 | fi 459 | 460 | if [[ -f "$CUSTOM_PACMAN_CONF" ]]; then 461 | echo -en "${BOLD} custom config:" 462 | echo -e "$(tput cr)$(tput cuf 18)$CUSTOM_PACMAN_CONF${ALL_OFF}${BOLD}${ALL_OFF}" 463 | fi 464 | 465 | if [[ -f "$CUSTOM_MAKEPKG_CONF" ]]; then 466 | echo -en "${BOLD} custom config:" 467 | echo -e "$(tput cr)$(tput cuf 18)$CUSTOM_MAKEPKG_CONF${ALL_OFF}${BOLD}${ALL_OFF}" 468 | # use threads value from the custom config 469 | BR_THREADS=$(grep MAKEFLAGS= "$CUSTOM_MAKEPKG_CONF") 470 | THREADS="${BR_THREADS//[^0-9]/}" 471 | fi 472 | 473 | if [[ -d "$REPO" ]]; then 474 | if ! find "$REPO" -maxdepth 0 -type d -empty |grep -q .; then 475 | REPOSIZE="${BOLD}$(du -sh "$REPO" 2>/dev/null|awk '{ print $1 }')${ALL_OFF}" 476 | else 477 | REPOSIZE="${BOLD}${RED}(Empty repo)${ALL_OFF}" 478 | fi 479 | else 480 | REPOSIZE="${BOLD}${RED}(Empty repo)${ALL_OFF}" 481 | fi 482 | echo 483 | echo -en "${BOLD} repo path:" 484 | echo -e "$(tput cr)$(tput cuf 18)${BLUE}$REPO/${ALL_OFF}${BOLD}${ALL_OFF} $REPOSIZE" 485 | echo -en "${BOLD} repo name:" 486 | echo -e "$(tput cr)$(tput cuf 18)$REPO_NAME${ALL_OFF}${BOLD}${ALL_OFF}" 487 | 488 | if [[ HERE -eq 1 ]]; then 489 | echo 490 | echo -en "${BOLD} buildroot threads:" 491 | echo -e "$(tput cr)$(tput cuf 21)${BOLD}$THREADS${ALL_OFF}" 492 | 493 | if [[ $(grep -c '^#\[core-testing]' "$CHROOTPATH64"/root/etc/pacman.conf) -eq 0 ]]; then 494 | TESTING_C="${BOLD}${GREEN}[*-testing]${ALL_OFF}" 495 | else 496 | TESTING_C="${RED}[*-testing]${ALL_OFF}${BOLD}" 497 | fi 498 | 499 | if [[ $(grep '^BUILDENV' "$CHROOTPATH64"/root/etc/makepkg.conf | grep -c '!distcc') -eq 0 ]]; then 500 | DISTCC_C="${BOLD}${GREEN}distcc${ALL_OFF}" 501 | else 502 | DISTCC_C="${RED}distcc${ALL_OFF}${BOLD}" 503 | fi 504 | 505 | if [[ $(grep '^BUILDENV' "$CHROOTPATH64"/root/etc/makepkg.conf | grep -c '!ccache') -eq 0 ]]; then 506 | CCACHE_C="${BOLD}${GREEN}ccache${ALL_OFF}" 507 | else 508 | CCACHE_C="${RED}ccache${ALL_OFF}${BOLD}" 509 | fi 510 | 511 | echo -en "${BOLD} buildroot options:" 512 | echo -e "$(tput cr)$(tput cuf 21)${BOLD}$TESTING_C $DISTCC_C $CCACHE_C${ALL_OFF}" 513 | 514 | if pidof -q -x makechrootpkg; then 515 | etime=$( ps --no-headers -o %t "$(pidof -x sudo ccm | awk '{ print $(NF) }')" ) 516 | echo; echo -en "${BOLD} building, elasped:" 517 | echo -e "$(tput cr)$(tput cuf 21)${BOLD}${etime/ /}${ALL_OFF}" 518 | fi 519 | fi 520 | } 521 | 522 | ## 523 | # Copied from https://git.archlinux.org/devtools.git/tree/lib/archroot.sh 524 | ## 525 | 526 | ## 527 | # Returns if the $path is a the root of a btrfs subvolume (including 528 | # the top-level subvolume). 529 | # 530 | # usage : is_subvolume( $path ) 531 | # return : true if it is, false if not 532 | ## 533 | is_subvolume() { 534 | [[ -e "$1" && "$(stat -f -c %T "$1")" == btrfs && "$(stat -c %i "$1")" == 256 ]] 535 | } 536 | 537 | ## 538 | # Find all btrfs subvolumes under and including $path and delete them. 539 | # 540 | # usage : subvolume_delete_recursive( $path ) 541 | # return : 0 if it was successful, 1 if not. 542 | ## 543 | subvolume_delete_recursive() { 544 | local subvol 545 | is_subvolume "$1" || return 0 546 | while IFS= read -d $'\0' -r subvol; do 547 | if ! subvolume_delete_recursive "$subvol"; then 548 | return 1 549 | fi 550 | done < <(find "$1" -mindepth 1 -xdev -depth -inum 256 -print0) 551 | if ! btrfs subvolume delete "$1" &>/dev/null; then 552 | error "Unable to delete subvolume %s" "$subvol" 553 | return 1 554 | fi 555 | return 0 556 | } 557 | 558 | nuke() { 559 | if [[ $(stat -f -c %T "$CHROOTPATH64") == btrfs ]]; then 560 | for i in "$CHROOTPATH64"/*; do 561 | if [ -d "$i" ]; then 562 | subvolume_delete_recursive "$i" || return 255 563 | else 564 | rm -f "$i" 565 | fi 566 | done 567 | else 568 | rm -rf "${CHROOTPATH64:?}"/* 569 | fi 570 | } 571 | 572 | purgecache() { 573 | local mesg="Purging ccache..." 574 | echo -e "${GREEN}==>${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}" 575 | if [[ $(stat -f -c %T "$CCACHE_DIR") == btrfs ]]; then 576 | for i in "$CCACHE_DIR"/*; do 577 | if [ -d "$i" ]; then 578 | subvolume_delete_recursive "$i" || return 255 579 | else 580 | rm -f "$i" 581 | fi 582 | done 583 | else 584 | rm -rf "${CCACHE_DIR:?}"/* 585 | fi 586 | } 587 | 588 | distcc_check() { 589 | # this is a catch-all function to check for a running distcc and to 590 | # override the THREADS value due to https://bugs.archlinux.org/task/64349 591 | # which to is not fixed even though it is closed 592 | 593 | # if the buildroot is already created, see if distcc is enabled and 594 | # simply set RUNDISTCC which may or may not be set in the config file 595 | # if the user created the buildroot with the 'cd' switch 596 | 597 | if [[ -f "$CHROOTPATH64/root/etc/makepkg.conf" ]]; then 598 | if grep -q -i '^BUILDENV=(distcc' "$CHROOTPATH64/root/etc/makepkg.conf"; then 599 | # a buildroot is present with distcc enabled 600 | RUNDISTCC=y 601 | fi 602 | fi 603 | 604 | # RUNDISTCC can also be forced via the 'cd' swtich as well as set in the config 605 | if [[ -n "$RUNDISTCC" ]]; then 606 | # fail if distcc_threads aren't defined 607 | if [[ -z "$DISTCC_THREADS" ]]; then 608 | local mesg="Define DISTCC_THREADS in $CFGFILE to build with distcc." 609 | echo -e "${RED}==> ERROR:${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}"; exit 1 610 | fi 611 | # need to prefix the call to makechrootpkg with threads value 612 | # https://bugs.archlinux.org/task/64349 613 | THREADS=$DISTCC_THREADS 614 | fi 615 | } 616 | 617 | logbuild() { 618 | source PKGBUILD 619 | source /usr/share/makepkg/util/pkgbuild.sh 620 | 621 | local pname=$(extract_global_variable "pkgname" 0 output && echo "$output") 622 | local pver=$(get_full_version) 623 | echo -e "$(date +%c)\t$pname-$pver\t$howlong" >> "$BUILDLOG" 624 | } 625 | 626 | case "$1" in 627 | a) 628 | check_config && addit && indexit 629 | ;; 630 | c) 631 | check_config && check_active && distcc_check && create 632 | ;; 633 | cd) 634 | # force running with distcc to override the config file setting 635 | check_config && check_active && distcc_check 636 | RUNDISTCC=y 637 | create 638 | ;; 639 | pc) 640 | check_config && purgecache 641 | ;; 642 | t) 643 | check_config && check_active && check_buildroot && testing 644 | # no need to call update since testing function handles this 645 | ;; 646 | d) 647 | check_config && repocheck 648 | mesg="Deleting all packages and index in buildroot repo..." 649 | echo -e "${GREEN}==>${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}" 650 | delete 651 | ;; 652 | l) 653 | check_config && repocheck && list 654 | ;; 655 | n) 656 | mesg="Deleting the entire buildroot..." 657 | echo -e "${GREEN}==>${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}" 658 | check_config && check_active && nuke 659 | ;; 660 | N) 661 | mesg="Deleting the entire buildroot and external repo..." 662 | echo -e "${GREEN}==>${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}" 663 | check_config && check_active && delete && nuke 664 | ;; 665 | p) 666 | check_config && distcc_check && preview 667 | ;; 668 | R) 669 | check_config 670 | if [[ ! -f "$CHROOTPATH64"/root/.arch-chroot ]]; then 671 | mesg="No buildroot has been created/nothing to repackage." 672 | echo -e "${RED}==> ERROR:${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}"; exit 1 673 | fi 674 | 675 | postargs+=('-Rf') 676 | mesg="Attempting to repackage..." 677 | echo -e "${YELLOW}---->${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}" 678 | # flag to NOT add a '-c' to the args array 679 | CLEANIT=0 680 | 681 | check_active && build && indexit 682 | ;; 683 | s) 684 | check_config && check_active && distcc_check 685 | if [[ ! -f "$CHROOTPATH64"/root/.arch-chroot ]]; then 686 | mesg="No buildroot has been created so making one now..." 687 | echo -e "${YELLOW}---->${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}" 688 | create 689 | fi 690 | 691 | mesg="Attempting to build package..." 692 | echo -e "${YELLOW}---->${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}" 693 | 694 | # flag to add '-c' to the args array 695 | CLEANIT=1 696 | build && indexit && logbuild 697 | ;; 698 | S) 699 | check_config && check_active && distcc_check 700 | if [[ ! -f "$CHROOTPATH64"/root/.arch-chroot ]]; then 701 | mesg="No buildroot has been created so making one now..." 702 | echo -e "${YELLOW}---->${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}" 703 | create 704 | fi 705 | 706 | mesg="Attempting to build package..." 707 | echo -e "${YELLOW}---->${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}" 708 | 709 | # flag to NOT add a '-c' to the args array 710 | CLEANIT=0 711 | build && indexit && logbuild 712 | ;; 713 | u) 714 | check_config && check_active && check_buildroot && update 715 | ;; 716 | *) 717 | echo -e "${BOLD}$PKG v$VERS${ALL_OFF}" 718 | echo 719 | echo -e "${BOLD} Usage: ${RED}$0${ALL_OFF}${BOLD} ${ALL_OFF}${BLUE}[option]${ALL_OFF}" 720 | echo 721 | echo -e "${BOLD} --- SETUP AND BREAKDOWN ---" 722 | echo -e "${BOLD} c) Create the buildroot${ALL_OFF}" 723 | echo -e "${BOLD} cd) Create the buildroot with distcc${ALL_OFF}" 724 | echo -e "${BOLD} N) Nuke the buildroot and external repo (if defined)${ALL_OFF}" 725 | echo -e "${BOLD} n) Nuke the buildroot${ALL_OFF}" 726 | echo -e "${BOLD} t) Toggle [*-testing] on/off${ALL_OFF}" 727 | echo 728 | if [[ -n "$CCACHE_DIR" ]]; then 729 | echo -e "${BOLD} Target: ${BLUE}$CCACHE_DIR${ALL_OFF}" 730 | echo -e "${BOLD} pc) Purge the ccache${ALL_OFF}" 731 | echo 732 | fi 733 | echo -e "${BOLD} --- BUILDING ---${ALL_OFF}" 734 | echo -e "${BOLD} R) Repackage (passes '-Rf' to default)${ALL_OFF}" 735 | echo -e "${BOLD} S) Run makepkg but do not clean first${ALL_OFF}" 736 | echo -e "${BOLD} s) Run makepkg (default)${ALL_OFF}" 737 | echo 738 | echo -e "${BOLD} --- LOCAL REPO ---${ALL_OFF}" 739 | echo -e "${BOLD} a) Add packages in current dir to the local repo${ALL_OFF}" 740 | echo -e "${BOLD} d) Delete all packages in the local repo${ALL_OFF}" 741 | echo -e "${BOLD} l) List packages in local repo${ALL_OFF}" 742 | echo 743 | echo -e "${BOLD} --- MISCELLANEOUS ---" 744 | echo -e "${BOLD} p) Preview settings${ALL_OFF}" 745 | echo -e "${BOLD} u) Update clean buildroot${ALL_OFF}" 746 | ;; 747 | esac 748 | 749 | # vim:set ts=2 sw=2 et: 750 | -------------------------------------------------------------------------------- /common/zsh-completion: -------------------------------------------------------------------------------- 1 | #compdef clean-chroot-manager clean-chroot-manager64 ccm ccm64 2 | 3 | _ccm() { 4 | local -a options 5 | 6 | options=('c:create the clean chroot' 7 | 'cd:create the clean chroot with distcc' 8 | 'N:nuke the clean chroot and external repo if defined' 9 | 'n:nuke the clean chroot' 10 | 't:toggle [*-testing] on or off' 11 | 'R:repackage' 12 | 'S:run makepkg in the clean chroot but do not clean first' 13 | 's:run makepkg in the clean chroot' 14 | 'a:add packages in current dir to local repo' 15 | 'd:delete all packages in the local repo' 16 | 'l:list packages in the local repo' 17 | 'p:preview settings' 18 | 'u:update the clean chroot manually') 19 | 20 | _describe 'options' options 21 | } 22 | 23 | _ccm 24 | -------------------------------------------------------------------------------- /contrib/README.md: -------------------------------------------------------------------------------- 1 | ### Dependency test 2 | To test the internal repo, use ccm to build the package `foo` first and then the package `bar` 3 | * `bar` has a dependency of `foo` so if something is not right, ccm will fail to build `bar`: 4 | ``` 5 | ... 6 | ==> Making package: test-bar 1-1 (Sun May 18 05:12:50 2025) 7 | ==> Retrieving sources... 8 | ==> Making package: test-bar 1-1 (Sun May 18 09:12:52 2025) 9 | ==> Checking runtime dependencies... 10 | ==> Installing missing dependencies... 11 | error: target not found: test-foo 12 | ==> ERROR: 'pacman' failed to install missing dependencies. 13 | ==> Missing dependencies: 14 | -> test-foo 15 | ==> Checking buildtime dependencies... 16 | ==> ERROR: Could not resolve all dependencies. 17 | ``` 18 | -------------------------------------------------------------------------------- /contrib/bar/PKGBUILD: -------------------------------------------------------------------------------- 1 | pkgname=bar 2 | pkgver=1.0 3 | pkgrel=1 4 | pkgdesc="dummy package" 5 | arch=(any) 6 | license=('GPL') 7 | depends=(foo) 8 | 9 | package() { 10 | true 11 | } 12 | -------------------------------------------------------------------------------- /contrib/foo/PKGBUILD: -------------------------------------------------------------------------------- 1 | pkgname=foo 2 | pkgver=1.0 3 | pkgrel=1 4 | pkgdesc="dummy package" 5 | arch=(any) 6 | license=('GPL') 7 | depends=() 8 | 9 | package() { 10 | true 11 | } 12 | -------------------------------------------------------------------------------- /doc/clean-chroot-manager.1: -------------------------------------------------------------------------------- 1 | .\" Text automatically generated by txt2man 2 | .TH clean-chroot-manager 1 "21 May 2023" "" "" 3 | .SH NAME 4 | \fBClean-chroot-manager \fP- Wrapper script to manage buildroots when building packages under Arch Linux. 5 | \fB 6 | .SH DESCRIPTION 7 | ccm provides a "one-click" solution for building packages in a clean buildroot. Two key points that differentiate using ccm from the using the arch-build-scripts alone: 8 | .IP \(bu 3 9 | ccm automatically manages a local "staging" repo, so anything you build (dependencies from the AUR or more current versions of repo packages, etc.) are transparently pulled from that local repo. 10 | .IP \(bu 3 11 | ccm can optionally build with distcc and ccache. 12 | .PP 13 | To expand on point 1: let's say that we want to build a package called "bar" from the AUR but bar has a build dependency of another AUR package called "foo." Rather than first building foo, then installing foo, then building bar, and finally removing foo, the local repo will save a copy of the foo package which is indexed automatically therein. Pacman within the buildroot is aware of the foo package thanks to the local repo. When we try to build bar, pacman silently grabs the foo package from the local repo as it would any other dependency. 14 | .PP 15 | To expand on point 2: distcc allows one to distribute compilation tasks to other PCs on the network to build packages faster. So long as distcc is properly setup, ccm can build using it, and again, this will be a "one-click" operation from the user's prospective. For more on distcc and how to setup using it under Arch, see the wiki page: https://wiki.archlinux.org/index.php/Distcc 16 | .SH SETUP 17 | $XDG_CONFIG_HOME/clean-chroot-manager.conf (referred to as "the config file" hereafter) will be created on the first invocation of ccm and contains all user managed settings. Edit this file prior to running ccm a second time. Make sure the user running ccm has sudo rights to execute /usr/bin/clean-chroot-manager or /usr/bin/ccm. 18 | .SH USAGE 19 | sudo ccm [option] 20 | .SH OPTIONS 21 | .TP 22 | .B 23 | a 24 | Add any packages in the current directory to the local repo without building them. Useful if you already built something and simply want to copy it in the local repo. 25 | .TP 26 | .B 27 | c 28 | Create a clean buildroot. 29 | .TP 30 | .B 31 | cd 32 | Create a clean buildroot with distcc enabled. This is a shortcut/override for defining USE_DISTCC in the config file. 33 | .TP 34 | .B 35 | d 36 | Delete the ENTIRE contents of the local repo. 37 | .TP 38 | .B 39 | l 40 | List the contents of the local repo (i.e. the packages built to date) should any exist. 41 | .TP 42 | .B 43 | N 44 | Nuke the clean buildroot and external repo (if defined). 45 | .TP 46 | .B 47 | n 48 | Nuke the clean buildroot (delete it and everything under it). 49 | .TP 50 | .B 51 | p 52 | Preview settings. Show some bits about the buildroot itself. 53 | .TP 54 | .B 55 | pc 56 | Purge cache. Delete all files in the CCACHE_DIR (optional if building with ccache). 57 | .TP 58 | .B 59 | S 60 | Run makepkg in build mode under the buildroot but do not clean it. Useful if building a series of packages with highly similar deps. 61 | .TP 62 | .B 63 | s 64 | Run makepkg in build mode under the buildroot. The equivalent of `makepkg \fB-s\fP` in the buildroot. 65 | .TP 66 | .B 67 | R 68 | Repackage the current package if built. The equivalent of `makepkg \fB-sR\fP` in the buildroot. 69 | .TP 70 | .B 71 | t 72 | Toggle [*\fB-testing\fP] on/off in the buildroot. This function will enable or disable the testing repos in the buildroot and also take care of upgrading/downgrading any affected packages. 73 | .TP 74 | .B 75 | u 76 | Update the packages within the buildroot. The equivalent of `pacman \fB-Syu\fP` in the buildroot. 77 | .SH TIPS 78 | .IP \(bu 3 79 | Since ccm requires sudo rights, consider making an alias in ~/.bashrc or the like. For example: alias ccm='sudo ccm' 80 | .IP \(bu 3 81 | If you have multiple PCs on your LAN, consider having them help you compile via distcc which is supported within ccm. See $XDG_CONFIG_HOME/clean-chroot-manager.conf for setup instructions. 82 | .IP \(bu 3 83 | If your machine has lots of memory (>16G is probably the minimum), consider locating the buildroot to a tmpfs partition to minimize access times and avoid disk usage. Know that all data will be lost upon a reboot and that some builds require lots of space so take care when considering it. 84 | .PP 85 | One way is to simply define a directory to mount as tmpfs like so in /etc/fstab: 86 | .PP 87 | .nf 88 | .fam C 89 | tmpfs /scratch tmpfs nodev,size=20G 0 0 90 | 91 | .fam T 92 | .fi 93 | In order to have the expected CHROOTPATH directories created, we can use a systemd tmpfile like so: 94 | .PP 95 | .nf 96 | .fam C 97 | /etc/tmpfiles.d/ccm_dirs.conf 98 | d /scratch/.chroot64 0750 foo users - 99 | 100 | .fam T 101 | .fi 102 | .SH USAGE EXAMPLES 103 | Create a clean buildroot: 104 | .PP 105 | .nf 106 | .fam C 107 | $ sudo ccm c 108 | 109 | .fam T 110 | .fi 111 | Attempt to build the package in the clean buildroot. If successful, the package will be added to a local repo so that it will be available for use as a dependency for building other packages: 112 | .PP 113 | .nf 114 | .fam C 115 | $ cd /path/to/PKGBUILD 116 | $ sudo ccm s 117 | 118 | .fam T 119 | .fi 120 | List out the contents of the local repo assuming something has been built: 121 | .PP 122 | .nf 123 | .fam C 124 | $ sudo ccm l 125 | 126 | .fam T 127 | .fi 128 | Deletes everything under the top level of the buildroot effectively removing it from the system: 129 | .PP 130 | .nf 131 | .fam C 132 | $ sudo ccm n 133 | 134 | .fam T 135 | .fi 136 | .SH BUGS 137 | .IP \(bu 3 138 | Not really a bug, but know that if you modify your config file after you created a buildroot, the changes will not be effective until you nuke and rebuild the buildroot. 139 | .PP 140 | Discover a bug? Please open an issue on the project page linked below. 141 | .SH ONLINE 142 | Project page: https://github.com/graysky2/clean-chroot-manager 143 | .SH AUTHOR 144 | graysky (graysky AT archlinux DOT us). 145 | --------------------------------------------------------------------------------