diff options
author | Sean Whitton <spwhitton@spwhitton.name> | 2022-11-11 23:32:12 -0700 |
---|---|---|
committer | Sean Whitton <spwhitton@spwhitton.name> | 2022-11-12 11:23:53 -0700 |
commit | f223f38fcab3c94402603d1fadb2d6fa0ac3d05a (patch) | |
tree | 8036d67f8e96d335368d8b1ff19d3c98770fd754 /lib-src | |
parent | 74585ec4711667c76ecdad7eb53590cb912501ba (diff) | |
download | dotfiles-f223f38fcab3c94402603d1fadb2d6fa0ac3d05a.tar.gz |
GNU Stow -> hstow, and follow-up tidying & simplifications
Diffstat (limited to 'lib-src')
-rw-r--r-- | lib-src/mr/config | 175 | ||||
-rw-r--r-- | lib-src/mr/stow | 273 | ||||
-rwxr-xr-x | lib-src/stow/chkstow | 113 | ||||
-rwxr-xr-x | lib-src/stow/stow | 665 |
4 files changed, 25 insertions, 1201 deletions
diff --git a/lib-src/mr/config b/lib-src/mr/config index 06af1b6e..9cd84fe2 100644 --- a/lib-src/mr/config +++ b/lib-src/mr/config @@ -23,6 +23,11 @@ git_isclean = git is-clean git_clean = git clean -xdff +stow = hstow stow "$MR_REPO" +unstow = hstow unstow "$MR_REPO" +restow = hstow restow "$MR_REPO" +adopt = hstow adopt "$MR_REPO" + # --- Plugin for dgit repos # actually shipped with upstream mr, but use an include command that @@ -40,17 +45,6 @@ include = cat ~/src/dotfiles/lib-src/mr/dgit # after we include the dgit lib, which also redefines git_update) git_update = git pull-safe -# --- Adam Spiers' plugin for managing dotfile symlinks with mr - -# actually shipped with upstream mr, but use an include command that -# will always work -include = - # stow is not available on Windows - if [ -e "$HOME/src/dotfiles/lib-src/mr/stow" ] \ - && ! [ "$(perl -e 'print $^O')" = "msys" ]; then - cat "$HOME/src/dotfiles/lib-src/mr/stow" - fi - # --- joeyh's code for specifying what machine we're on for repo skip # --- tests, plus my code for detecting Git-on-Windows @@ -106,66 +100,6 @@ lib = # --- standard procedures lib = - homedir_mkdirs() { - ( - cd $HOME - mkdir -p \ - .ssh \ - tmp \ - src \ - lib \ - mnt \ - local/mutt \ - local/src \ - local/bin \ - local/big \ - local/lib \ - local/log \ - local/pub \ - local/tmp \ - local/auth \ - local/info - # lib/athena \ - # lib/backup \ - # local/anacron/spool \ - chmod 700 local/auth - # [ -L "src/build-area" ] || ln -s -T /tmp/debuild src/build-area - [ -e "Downloads" ] || ln -s tmp Downloads - # clean up after additions to .stow-local-ignore - find bin lib -type l 2>/dev/null | while read -r link; do - if readlink "$link" | grep --quiet "^[../]*/.STOW/"; then - rm "$link" - fi - done - # cleanup some old dirs if they're empty - find \ - bin \ - lib/aid \ - lib/backup \ - lib/perl5 \ - lib/hooks \ - lib/athena \ - lib/bins \ - lib/img \ - lib/mr \ - lib/src \ - local/anacron/spool \ - -type d -empty -delete 2>/dev/null ||: - ) - } - # specify files that should automatically be adopted because - # programs convert them from symlinks to regular files. Arguments - # to this function should be paths relative to the stow target - # (usually $HOME) - always_adopt () { - for f in $@; do - if ! [ -L "$STOW_TARGET/$f" ]; then - # ignore errors; if it doesn't work, the user will - # have to fix up manually - mv 2>/dev/null "$STOW_TARGET/$f" "$MR_REPO/$f" || true - fi - done - } # export plain text Org agenda in post_ hooks of ~/doc repo (not currently used) export_org_agenda () { if on athena; then @@ -179,72 +113,18 @@ lib = # --- primary dotfiles repository -# TODO we need to unstow before switching branches, and stow -# afterwards, or else do some sort of automatic cleanup of dangling -# symlinks before a restow, so a broken situation is easy to fix? -# Also see kill-broken-stowed-symlink() in .bashrc. -# -# Note that the scan is expensive. So actually we probably don't want -# the cleanup to happen automatically. Also, it should exclude -# lib/annex, src/*/ (but not src/) because I don't stow files into -# those dirs. -# -# Maybe just run it as part of sysmaint -# -# Hmm. Situation is not as bad as I thought. stow manages to clean -# up quite a few of the symlinks. So running it as part of sysmaint -# seems like a sufficient fix - [src/dotfiles] checkout = git clone https://git.spwhitton.name/dotfiles.git dotfiles -stowable = true -# we have a script to update master, and all other branches should -# only be checked out and committed to on a single host +# We have a script to update master, and all other branches should be checked +# out and committed to on only a single host, so no need to pull them, and +# they'll always be rebaseable. update = git dotfiles-update-master -push = git push origin master -# use `git dotfiles-rebase` instead -# rebase = -# # usual rebasing pattern. Per dotfiles repo policy (excluding -# # win32 case), the branch being rebased will always be rebaseable -# # on master, since it is only checked out and committed to on this -# # host -# branch="$(git rev-parse --abbrev-ref HEAD)" -# hostname="$(hostname -s)" -# if [ "$branch" = "win32" -o "$branch" = "$hostname" -o "$branch" = "develacc-$hostname" ]; then -# git rebase master -# fi -fixups = - # Use a rebase workflow as I'm the only committer - git config pull.rebase true - # Pushing and pulling are always done explicitly - for head in $(git for-each-ref --format='%(refname)' refs/heads/); do - branch=$(echo "$head" | cut -d/ -f3) - git branch --unset-upstream "$branch" 2>/dev/null || true - done - git config push.default nothing - # this is just for M-x magit-status - git config remote.pushDefault origin - # - homedir_mkdirs - chmod -Rf u+rwX,go= $HOME/local/auth/* || true - # eventually move the following two lines from fixups to post_checkout - install-git-hooks dotfiles - git config user.signingkey 8DC2487E51ABDD90B5C4753F0F56D0553B6D411B - # eventually drop this - rm -f .git/hooks/post-checkout{,_01gpgsign} - -# clean-ups so that initial stow will be successful -pre_stow = - homedir_mkdirs - $HOME/src/dotfiles/bin/unskel - # these will often end up created, e.g. by insinuate-dotfiles script - rm -f $HOME/.gnupg/{gpg.conf,gpg-agent.conf,dirmngr.conf,.gpg-v21-migrated} - -# this file frequently gets desymlinked -pre_unstow_append = always_adopt .config/mimeapps.list -pre_restow_append = always_adopt .config/mimeapps.list -pre_stow_append = always_adopt .config/mimeapps.list -pre_update_append = always_adopt .config/mimeapps.list +push = git dotfiles-rebase +# Restowing is expensive, and most dangling symlinks into ~/.STOW do no harm, +# so we leave it to be run manually -- bstraph stows but does not restow. +# Possibly restowing could be done by locmaint, or we could have hstow skip +# annex/ and src/, into which I don't stow anything. +fixups = bstraph # --- private dotfiles repositories @@ -252,7 +132,9 @@ pre_update_append = always_adopt .config/mimeapps.list checkout = git clone athenag:libpriv.git priv update = git annex sync --content cloud origin push = git annex sync --content cloud origin -stowable = true +post_update = + hstow stow ~/lib/priv + load-trustdb sync = mr autoci && git annex sync --no-commit --content cloud origin skip = lazy @@ -261,6 +143,11 @@ post_checkout = git annex init git annex enableremote cloud git annex group . backup + # Delete any pubring.kbx created by INSINUATE-DOTFILES Consfigurator + # property / 'insinuate-dotfiles' shell script: don't want to adopt it. + rm -f ~/.gnupg/pubring.kbx + hstow stow ~/lib/priv + load-trustdb fixups = chmod 600 .passwddb.pet \ @@ -282,21 +169,18 @@ fixups = git config mrrepo.review-unused false autoci = + hstow stow ~/lib/priv # to perform adoptions git annex add .passwddb.pet .labbook.gpg .gnupg/pubring.kbx git commit -a -m \ "auto passwddb, pubring and labbook commit on $(hostname -s)" || true pre_update = mr autoci -# since dotfiles repo also stows into ~/.gnupg, and athpriv repo stows -# into ~/.duply, make the dirs first -pre_stow = homedir_mkdirs -post_stow = load-trustdb - [src/athpriv] checkout = git clone demeterp:athpriv athpriv pre_update = on athena || git annex sync origin athenah pre_push = on athena || git annex sync --content origin athenah -stowable = true +post_update = hstow stow ~/src/athpriv +post_checkout = hstow stow ~/src/athpriv skip = ! mine post_checkout = @@ -321,15 +205,6 @@ autoci = git annex add News/* git commit News -m"auto commit of Gnus score files" ||: -# since priv repo also stows into ~/.duply, make the dir first -pre_stow = homedir_mkdirs - -# r2e always desymlinks this file -pre_stow_append = always_adopt .config/rss2email.cfg -pre_unstow_append = always_adopt .config/rss2email.cfg -pre_restow_append = always_adopt .config/rss2email.cfg -pre_update_append = always_adopt .config/rss2email.cfg - # --- hosts configuration [src/propellor] diff --git a/lib-src/mr/stow b/lib-src/mr/stow deleted file mode 100644 index 254bd0c3..00000000 --- a/lib-src/mr/stow +++ /dev/null @@ -1,273 +0,0 @@ -# Plug-in to use GNU Stow to manage symlinks whose targets lie in a -# repository managed with myrepos -# -# The standard use case is for managing dotfiles inside one's home -# directory. -# -# Original author (2011): -# Adam Spiers <mr@adamspiers.org> -# -# This version reworked (2016, 2017) & maintained (2017) by: -# Sean Whitton <spwhitton@spwhitton.name> - -# BASIC USAGE INSTRUCTIONS -# -# To make mr use this file, add a line like this inside the [DEFAULT] -# section of your ~/.mrconfig: -# -# include = cat /usr/share/mr/stow -# -# and then inside each [repo] section of your ~/.mrconfig for -# which you want the contents to be stowed, add this line: -# -# stowable = true -# -# You must have at least version 2.1.0 of stow available. [1] -# -# If stow is not in your $PATH, you can export STOW_COMMAND to tell -# this plug-in where it is. -# -# The default behaviour is to stow on checkout, and restow on update. -# The manual actions 'stow', 'restow', 'unstow' and 'adopt' are also -# available. -# -# By default, ~/.STOW is used as the stow directory, and ~ as the -# target directory. You can export STOW_DIR and STOW_TARGET to -# override these defaults. -# -# DEALING WITH APPLICATIONS THAT MISTREAT SYMLINKS -# -# Some programs will replace a symlink to a stowed file with a regular -# copy of the file, and a subset of these will do this even if they -# haven't edited the file. This will cause stow operations to fail. -# -# To deal with this, run 'mr adopt'. This will move the modified file -# into your repository, and restore the usual symlink. Then you can -# use your VCS tools ('git diff', 'hg diff') to decide whether you -# want to keep the changes. -# -# FOLDING -# -# By default, this library passes --no-folding to stow. This allows -# you to have more than one repository stowing files into a single -# subdirectory in your home directory. For example, you might have a -# private and a public repository both stowing into ~/.gnupg. If you -# don't want this behaviour, set MR_FOLD. For example, in a -# repository's myrepos config section or in [DEFAULT]: -# -# lib = MR_FOLD= -# -# FIXUPS THAT CREATE FILES TO BE STOWED -# -# Stowing is automatically performed via post_checkout, and restowing -# via post_update, as can be seen from below (search for 'Automatic -# actions'). Note that these run before fixups, which allows fixups -# to refer to stowed files, but isn't ideal if the fixups are -# responsible for creating the stow package's installation image, -# e.g. via a typical './configure && make install' sequence. Here's a -# suggested mrconfig chunk to handle this particular use case: -# -# stowable = true -# lib = -# STOW_PKG_TYPE=directory -# STOW_NO_AUTOMATIC_ACTIONS=yes -# mr_pre_unstow () { -# install-info --delete --info-dir=$HOME/share/info $STOW_PKG_PATH/share/info/*.info -# } -# mr_post_stow () { -# install-info --info-dir=$HOME/share/info $STOW_PKG_PATH/share/info/*.info -# } -# fixups = -# if ! [ -e configure ]; then -# bash ./autogen.sh -# fi -# set_stow_common_opts -# ./configure --prefix=$STOW_PKG_PATH -# make install prefix=$STOW_PKG_PATH -# rm $STOW_PKG_PATH/share/info/dir -# mr_restow_regardless -# -# [1] Older versions could create a frankenstein ~/.git/ directory -# containing symlinks to multiple .git/ sub-directories in different -# stow packages! 2.1.0 onwards does not have this problem - it -# supports local per-directory .stow-local-ignore and global -# ~/.stow-global-ignore files, and even without configuration of -# these, it chooses sensible default ignore lists which prevent -# stowing of a package's .git/ sub-directory. These ignore lists are -# also ideal if you only want to stow a subset of a stow package's -# contents. - -lib = - : ${STOW_DIR:=$HOME/.STOW} - : ${STOW_TARGET:=$HOME} - STOW_NAME=$(echo "$MR_REPO" | tr / _) - # - if ! [ -d "$STOW_TARGET" ]; then mkdir -p "$STOW_TARGET"; fi - if ! [ -d "$STOW_DIR" ]; then mkdir -p "$STOW_DIR" ; fi - if ! [ -f "$STOW_DIR/.stow" ]; then touch "$STOW_DIR/.stow"; fi - # - #MR_STOWABLE=no - is_stowable () { - [ -z "$MR_DISABLE_STOW" ] && - ( cd "$MR_REPO" && mr stowable >/dev/null 2>&1 ) - #[ "$MR_STOWABLE" = yes ] - } - stowable_then_continue () { - if is_stowable; then - return 0 - else - if [ -n "$1" ]; then - info "$STOW_NAME isn't stowable; skipping $MR_ACTION" - fi - return 1 - fi - } - # - set_stow_common_opts () { - : ${STOW_PKG_TYPE:=symlink} - STOW_PKG_PATH="$STOW_DIR/$STOW_NAME" - # canonicalise -t and -d params with readlink if available - # stow can fail if they aren't canonical - if which readlink >/dev/null 2>&1; then - stow_common_opts="-t $(readlink -f $STOW_TARGET) -d $(readlink -f $STOW_DIR)" - else - stow_common_opts="-t $STOW_TARGET -d $STOW_DIR" - fi - STOW="${STOW_COMMAND:-$HOME/src/dotfiles/lib-src/stow/stow}" - case "`$STOW --version`" in - 'version 1.*') - stow_common_opts="$stow_common_opts -p" - ;; - *) - ;; - esac - if [ -n "$MR_STOW_OPTIONS" ]; then - stow_common_opts="$stow_common_opts $MR_STOW_OPTIONS" - fi - if [ -n "$MR_STOW_OVER" ]; then - stow_common_opts="$stow_common_opts --override=$MR_STOW_OVER" - fi - if ! (: "${MR_FOLD?}") 2>/dev/null; then - stow_common_opts="$stow_common_opts --no-folding" - fi - } - # - mr_stow () { - stowable_then_continue || return 0 - set_stow_common_opts - ensure_package_exists - command "$STOW" $stow_common_opts "$@" "$STOW_NAME" - mr_post_stow - info "Stowed $STOW_NAME" - } - mr_restow_if_already_stowed () { - stowable_then_continue || return 0 - if ! [ -L "$STOW_PKG_PATH" ]; then - info "$MR_REPO wasn't stowed yet; won't restow." - return - fi - mr_restow_regardless "$@" - } - mr_restow_regardless () { - stowable_then_continue || return 0 - set_stow_common_opts - ensure_package_exists - mr_pre_unstow - command "$STOW" -R $stow_common_opts "$@" "$STOW_NAME" - mr_post_stow - info "Restowed $STOW_NAME" - } - mr_pre_unstow () { - : # This can be "overridden" by the lib section of a repo definition - #info "no mr_pre_unstow hook" - } - mr_post_stow () { - : # This can be "overridden" by the lib section of a repo definition - #info "no mr_post_stow hook" - } - mr_unstow () { - stowable_then_continue || return 0 - set_stow_common_opts - if ! [ -d "$STOW_PKG_PATH" ]; then - info "$MR_REPO wasn't stowed yet in $STOW_PKG_PATH; can't unstow." - return - fi - mr_pre_unstow - command "$STOW" -D $stow_common_opts "$@" "$STOW_NAME" - if [ "$STOW_PKG_TYPE" = 'symlink' ]; then - rm -f "$STOW_PKG_PATH" - fi - info "Unstowed $STOW_NAME" - } - # - ensure_symlink_exists () { - [ $# = 2 ] || error "CONFIG BUG: Usage: ensure_symlink_exists SYMLINK TARGET" - symlink="$1" - required_target="$2" - if [ -L "$symlink" ]; then - actual_target="`readlink $symlink`" - if [ "$actual_target" = "$required_target" ]; then - return - else - error "Symlink $symlink already points to $actual_target, cannot point to $required_target; aborting." - fi - fi - if [ -e "$symlink" ]; then - error "Cannot create symlink $symlink - already exists; aborting." - fi - ln -s "$required_target" "$symlink" - } - # - mr_adopt () { - stowable_then_continue || return 0 - set_stow_common_opts - ensure_package_exists - mr_pre_unstow - command "$STOW" --adopt $stow_common_opts "$@" "$STOW_NAME" - mr_post_stow - info "Stowed $STOW_NAME with adoption" - } - # - ensure_package_exists () { - case "$STOW_PKG_TYPE" in - symlink) - ensure_symlink_exists "$STOW_PKG_PATH" "$MR_REPO" - ;; - directory) - [ -e "$STOW_PKG_PATH" ] || mkdir "$STOW_PKG_PATH" - [ -d "$STOW_PKG_PATH" ] || error "Expected $STOW_PKG_PATH to be a directory; aborting." - if [ -L "$STOW_PKG_PATH" ]; then - error "Didn't expect $STOW_PKG_PATH to be a symlink; aborting." - fi - ;; - *) - error "Unrecognised value '$STOW_PKG_TYPE' for \$STOW_PKG_TYPE; aborting." - ;; - esac - } - -#stowable = is_stowable -stowable = false -showstowable = - if is_stowable; then - echo "$STOW_NAME is stowable" - else - echo "$STOW_NAME is not stowable" - fi - -# Automatic actions -post_checkout_append = [ -n "$STOW_NO_AUTOMATIC_ACTIONS" ] || mr_stow -#post_update_append = mr_restow_if_already_stowed -post_update_append = [ -n "$STOW_NO_AUTOMATIC_ACTIONS" ] || mr_restow_regardless - -# Manual actions -stow = mr_stow "$@" -stowover = MR_STOW_OVER=. mr_stow "$@" -unstow = mr_unstow "$@" -restow = mr_restow_regardless "$@" -restowover = MR_STOW_OVER=. mr_restow_regardless "$@" -adopt = mr_adopt "$@" - -# Local variables: -# mode: sh -# End: diff --git a/lib-src/stow/chkstow b/lib-src/stow/chkstow deleted file mode 100755 index a74d1b90..00000000 --- a/lib-src/stow/chkstow +++ /dev/null @@ -1,113 +0,0 @@ -#!/usr/bin/env perl - -use strict; -use warnings; - -require 5.006_001; - -use File::Find; -use Getopt::Long; - -my $DEFAULT_TARGET = '/usr/local/'; - -our $Wanted = \&bad_links; -our %Package = (); -our $Stow_dir = ''; -our $Target = $DEFAULT_TARGET; - -# put the main loop into a block so that tests can load this as a module -if ( not caller() ) { - if (@ARGV == 0) { - usage(); - } - process_options(); - #check_stow($Target, $Wanted); - check_stow(); -} - -sub process_options { - GetOptions( - 'b|badlinks' => sub { $Wanted = \&bad_links }, - 'a|aliens' => sub { $Wanted = \&aliens }, - 'l|list' => sub { $Wanted = \&list }, - 't|target=s' => \$Target, - ) or usage(); - return; -} - -sub usage { - print <<"EOT"; -USAGE: chkstow [options] - -Options: - -t DIR, --target=DIR Set the target directory to DIR - (default is $DEFAULT_TARGET) - -b, --badlinks Report symlinks that point to non-existent files - -a, --aliens Report non-symlinks in the target directory - -l, --list List packages in the target directory - ---badlinks is the default mode. -EOT - exit(0); -} - -sub check_stow { - #my ($Target, $Wanted) = @_; - - my (%options) = ( - wanted => $Wanted, - preprocess => \&skip_dirs, - ); - - find(\%options, $Target); - - if ($Wanted == \&list) { - delete $Package{''}; - delete $Package{'..'}; - - if (keys %Package) { - print map "$_\n", sort(keys %Package); - } - } - return; -} - -sub skip_dirs { - # skip stow source and unstowed targets - if (-e ".stow" || -e ".notstowed" ) { - warn "skipping $File::Find::dir\n"; - return (); - } - else { - return @_; - } -} - -# checking for files that do not link to anything -sub bad_links { - -l && !-e && print "Bogus link: $File::Find::name\n"; -} - -# checking for files that are not owned by stow -sub aliens { - !-l && !-d && print "Unstowed file: $File::Find::name\n"; -} - -# just list the packages in the the target directory -# FIXME: what if the stow dir is not called 'stow'? -sub list { - if (-l) { - $_ = readlink; - s{\A(?:\.\./)+stow/}{}g; - s{/.*}{}g; - $Package{$_} = 1; - } -} - -1; # Hey, it's a module! - -# Local variables: -# mode: perl -# cperl-indent-level: 4 -# End: -# vim: ft=perl diff --git a/lib-src/stow/stow b/lib-src/stow/stow deleted file mode 100755 index b94dc88d..00000000 --- a/lib-src/stow/stow +++ /dev/null @@ -1,665 +0,0 @@ -#!/usr/bin/env perl - -# GNU Stow - manage the installation of multiple software packages -# Copyright (C) 1993, 1994, 1995, 1996 by Bob Glickstein -# Copyright (C) 2000, 2001 Guillaume Morin -# Copyright (C) 2007 Kahlil Hodgson -# Copyright (C) 2011 Adam Spiers -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, see <http://www.gnu.org/licenses/>. - -=head1 NAME - -stow - software package installation manager - -=head1 SYNOPSIS - -stow [ options ] package ... - -=head1 DESCRIPTION - -This manual page describes GNU Stow 2.2.2, a program for managing -the installation of software packages. This is not the definitive -documentation for stow; for that, see the info manual. - -Stow is a tool for managing the installation of multiple software -packages in the same run-time directory tree. One historical -difficulty of this task has been the need to administer, upgrade, -install, and remove files in independent packages without confusing -them with other files sharing the same filesystem space. For instance, -it is common to install Perl and Emacs in F</usr/local>. When one -does so, one winds up (as of Perl 4.036 and Emacs 19.22) with the -following files in F</usr/local/man/man1>: F<a2p.1>; F<ctags.1>; -F<emacs.1>; F<etags.1>; F<h2ph.1>; F<perl.1>; and F<s2p.1>. Now -suppose it's time to uninstall Perl. Which man pages get removed? -Obviously F<perl.1> is one of them, but it should not be the -administrator's responsibility to memorize the ownership of individual -files by separate packages. - -The approach used by Stow is to install each package into its own -tree, then use symbolic links to make it appear as though the files -are installed in the common tree. Administration can be performed in -the package's private tree in isolation from clutter from other -packages. Stow can then be used to update the symbolic links. The -structure of each private tree should reflect the desired structure in -the common tree; i.e. (in the typical case) there should be a F<bin> -directory containing executables, a F<man/man1> directory containing -section 1 man pages, and so on. - -Stow was inspired by Carnegie Mellon's Depot program, but is -substantially simpler and safer. Whereas Depot required database files -to keep things in sync, Stow stores no extra state between runs, so -there's no danger (as there was in Depot) of mangling directories when -file hierarchies don't match the database. Also unlike Depot, Stow -will never delete any files, directories, or links that appear in a -Stow directory (e.g., F</usr/local/stow/emacs>), so it's always -possible to rebuild the target tree (e.g., F</usr/local>). - -=head1 TERMINOLOGY - -A "package" is a related collection of files and directories that -you wish to administer as a unit -- e.g., Perl or Emacs -- and that -needs to be installed in a particular directory structure -- e.g., -with F<bin>, F<lib>, and F<man> subdirectories. - -A "target directory" is the root of a tree in which one or more -packages wish to B<appear> to be installed. A common, but by no means -the only such location is F</usr/local>. The examples in this manual -page will use F</usr/local> as the target directory. - -A "stow directory" is the root of a tree containing separate -packages in private subtrees. When Stow runs, it uses the current -directory as the default stow directory. The examples in this manual -page will use F</usr/local/stow> as the stow directory, so that -individual packages will be, for example, F</usr/local/stow/perl> and -F</usr/local/stow/emacs>. - -An "installation image" is the layout of files and directories -required by a package, relative to the target directory. Thus, the -installation image for Perl includes: a F<bin> directory containing -F<perl> and F<a2p> (among others); an F<info> directory containing -Texinfo documentation; a F<lib/perl> directory containing Perl -libraries; and a F<man/man1> directory containing man pages. - -A "package directory" is the root of a tree containing the -installation image for a particular package. Each package directory -must reside in a stow directory -- e.g., the package directory -F</usr/local/stow/perl> must reside in the stow directory -F</usr/local/stow>. The "name" of a package is the name of its -directory within the stow directory -- e.g., F<perl>. - -Thus, the Perl executable might reside in -F</usr/local/stow/perl/bin/perl>, where F</usr/local> is the target -directory, F</usr/local/stow> is the stow directory, -F</usr/local/stow/perl> is the package directory, and F<bin/perl> -within is part of the installation image. - -A "symlink" is a symbolic link. A symlink can be "relative" or -"absolute". An absolute symlink names a full path; that is, one -starting from F</>. A relative symlink names a relative path; that -is, one not starting from F</>. The target of a relative symlink is -computed starting from the symlink's own directory. Stow only creates -relative symlinks. - -=head1 OPTIONS - -The stow directory is assumed to be the value of the C<STOW_DIR> -environment variable or if unset the current directory, and the target -directory is assumed to be the parent of the current directory (so it -is typical to execute F<stow> from the directory F</usr/local/stow>). -Each F<package> given on the command line is the name of a package in -the stow directory (e.g., F<perl>). By default, they are installed -into the target directory (but they can be deleted instead using -C<-D>). - -=over 4 - -=item -n - -=item --no - -Do not perform any operations that modify the filesystem; merely show -what would happen. - -=item -d DIR - -=item --dir=DIR - -Set the stow directory to C<DIR> instead of the current directory. -This also has the effect of making the default target directory be the -parent of C<DIR>. - -=item -t DIR - -=item --target=DIR - -Set the target directory to C<DIR> instead of the parent of the stow -directory. - -=item -v - -=item --verbose[=N] - -Send verbose output to standard error describing what Stow is -doing. Verbosity levels are 0, 1, 2, 3, and 4; 0 is the default. -Using C<-v> or C<--verbose> increases the verbosity by one; using -`--verbose=N' sets it to N. - -=item -S - -=item --stow - -Stow the packages that follow this option into the target directory. -This is the default action and so can be omitted if you are only -stowing packages rather than performing a mixture of -stow/delete/restow actions. - -=item -D - -=item --delete - -Unstow the packages that follow this option from the target directory rather -than installing them. - -=item -R - -=item --restow - -Restow packages (first unstow, then stow again). This is useful -for pruning obsolete symlinks from the target tree after updating -the software in a package. - -=item --adopt - -B<Warning!> This behaviour is specifically intended to alter the -contents of your stow directory. If you do not want that, this option -is not for you. - -When stowing, if a target is encountered which already exists but is a -plain file (and hence not owned by any existing stow package), then -normally Stow will register this as a conflict and refuse to proceed. -This option changes that behaviour so that the file is moved to the -same relative place within the package's installation image within the -stow directory, and then stowing proceeds as before. So effectively, -the file becomes adopted by the stow package, without its contents -changing. - -=item --no-folding - -Disable folding of newly stowed directories when stowing, and -refolding of newly foldable directories when unstowing. - -=item --ignore=REGEX - -Ignore files ending in this Perl regex. - -=item --defer=REGEX - -Don't stow files beginning with this Perl regex if the file is already -stowed to another package. - -=item --override=REGEX - -Force stowing files beginning with this Perl regex if the file is -already stowed to another package. - -=item -V - -=item --version - -Show Stow version number, and exit. - -=item -h - -=item --help - -Show Stow command syntax, and exit. - -=back - -=head1 INSTALLING PACKAGES - -The default action of Stow is to install a package. This means -creating symlinks in the target tree that point into the package tree. -Stow attempts to do this with as few symlinks as possible; in other -words, if Stow can create a single symlink that points to an entire -subtree within the package tree, it will choose to do that rather than -create a directory in the target tree and populate it with symlinks. - -For example, suppose that no packages have yet been installed in -F</usr/local>; it's completely empty (except for the F<stow> -subdirectory, of course). Now suppose the Perl package is installed. -Recall that it includes the following directories in its installation -image: F<bin>; F<info>; F<lib/perl>; F<man/man1>. Rather than -creating the directory F</usr/local/bin> and populating it with -symlinks to F<../stow/perl/bin/perl> and F<../stow/perl/bin/a2p> (and -so on), Stow will create a single symlink, F</usr/local/bin>, which -points to F<stow/perl/bin>. In this way, it still works to refer to -F</usr/local/bin/perl> and F</usr/local/bin/a2p>, and fewer symlinks -have been created. This is called "tree folding", since an entire -subtree is "folded" into a single symlink. - -To complete this example, Stow will also create the symlink -F</usr/local/info> pointing to F<stow/perl/info>; the symlink -F</usr/local/lib> pointing to F<stow/perl/lib>; and the symlink -F</usr/local/man> pointing to F<stow/perl/man>. - -Now suppose that instead of installing the Perl package into an empty -target tree, the target tree is not empty to begin with. Instead, it -contains several files and directories installed under a different -system-administration philosophy. In particular, F</usr/local/bin> -already exists and is a directory, as are F</usr/local/lib> and -F</usr/local/man/man1>. In this case, Stow will descend into -F</usr/local/bin> and create symlinks to F<../stow/perl/bin/perl> and -F<../stow/perl/bin/a2p> (etc.), and it will descend into -F</usr/local/lib> and create the tree-folding symlink F<perl> pointing -to F<../stow/perl/lib/perl>, and so on. As a rule, Stow only descends -as far as necessary into the target tree when it can create a -tree-folding symlink. - -The time often comes when a tree-folding symlink has to be undone -because another package uses one or more of the folded subdirectories -in its installation image. This operation is called "splitting open" -a folded tree. It involves removing the original symlink from the -target tree, creating a true directory in its place, and then -populating the new directory with symlinks to the newly-installed -package B<and> to the old package that used the old symlink. For -example, suppose that after installing Perl into an empty -F</usr/local>, we wish to install Emacs. Emacs's installation image -includes a F<bin> directory containing the F<emacs> and F<etags> -executables, among others. Stow must make these files appear to be -installed in F</usr/local/bin>, but presently F</usr/local/bin> is a -symlink to F<stow/perl/bin>. Stow therefore takes the following -steps: the symlink F</usr/local/bin> is deleted; the directory -F</usr/local/bin> is created; links are made from F</usr/local/bin> to -F<../stow/emacs/bin/emacs> and F<../stow/emacs/bin/etags>; and links -are made from F</usr/local/bin> to F<../stow/perl/bin/perl> and -F<../stow/perl/bin/a2p>. - -When splitting open a folded tree, Stow makes sure that the symlink -it is about to remove points inside a valid package in the current stow -directory. - -=head2 Stow will never delete anything that it doesn't own. - -Stow "owns" everything living in the target tree that points into a -package in the stow directory. Anything Stow owns, it can recompute if -lost. Note that by this definition, Stow doesn't "own" anything -B<in> the stow directory or in any of the packages. - -If Stow needs to create a directory or a symlink in the target tree -and it cannot because that name is already in use and is not owned by -Stow, then a conflict has arisen. See the "Conflicts" section in the -info manual. - -=head1 DELETING PACKAGES - -When the C<-D> option is given, the action of Stow is to delete a -package from the target tree. Note that Stow will not delete anything -it doesn't "own". Deleting a package does B<not> mean removing it from -the stow directory or discarding the package tree. - -To delete a package, Stow recursively scans the target tree, skipping -over the stow directory (since that is usually a subdirectory of the -target tree) and any other stow directories it encounters (see -"Multiple stow directories" in the info manual). Any symlink it -finds that points into the package being deleted is removed. Any -directory that contained only symlinks to the package being deleted is -removed. Any directory that, after removing symlinks and empty -subdirectories, contains only symlinks to a single other package, is -considered to be a previously "folded" tree that was "split open." -Stow will re-fold the tree by removing the symlinks to the surviving -package, removing the directory, then linking the directory back to -the surviving package. - -=head1 SEE ALSO - -The full documentation for F<stow> is maintained as a Texinfo manual. -If the F<info> and F<stow> programs are properly installed at your site, the command - - info stow - -should give you access to the complete manual. - -=head1 BUGS - -Please report bugs in Stow using the Debian bug tracking system. - -Currently known bugs include: - -=over 4 - -=item * The empty-directory problem. - -If package F<foo> includes an empty directory -- say, F<foo/bar> -- -then if no other package has a F<bar> subdirectory, everything's fine. -If another stowed package F<quux>, has a F<bar> subdirectory, then -when stowing, F<targetdir/bar> will be "split open" and the contents -of F<quux/bar> will be individually stowed. So far, so good. But when -unstowing F<quux>, F<targetdir/bar> will be removed, even though -F<foo/bar> needs it to remain. A workaround for this problem is to -create a file in F<foo/bar> as a placeholder. If you name that file -F<.placeholder>, it will be easy to find and remove such files when -this bug is fixed. - -=item * - -When using multiple stow directories (see "Multiple stow directories" -in the info manual), Stow fails to "split open" tree-folding symlinks -(see "Installing packages" in the info manual) that point into a stow -directory which is not the one in use by the current Stow -command. Before failing, it should search the target of the link to -see whether any element of the path contains a F<.stow> file. If it -finds one, it can "learn" about the cooperating stow directory to -short-circuit the F<.stow> search the next time it encounters a -tree-folding symlink. - -=back - -=head1 AUTHOR - -This man page was originally constructed by Charles Briscoe-Smith from -parts of Stow's info manual, and then converted to POD format by Adam -Spiers. The info manual contains the following notice, which, as it -says, applies to this manual page, too. The text of the section -entitled "GNU General Public License" can be found in the file -F</usr/share/common-licenses/GPL> on any Debian GNU/Linux system. If -you don't have access to a Debian system, or the GPL is not there, -write to the Free Software Foundation, Inc., 59 Temple Place, Suite -330, Boston, MA, 02111-1307, USA. - -=head1 COPYRIGHT - -Copyright (C) -1993, 1994, 1995, 1996 by Bob Glickstein <bobg+stow@zanshin.com>; -2000, 2001 by Guillaume Morin; -2007 by Kahlil Hodgson; -2011 by Adam Spiers; -and others. - -Permission is granted to make and distribute verbatim copies of this -manual provided the copyright notice and this permission notice are -preserved on all copies. - -Permission is granted to copy and distribute modified versions of this -manual under the conditions for verbatim copying, provided also that -the section entitled "GNU General Public License" is included with the -modified manual, and provided that the entire resulting derived work -is distributed under the terms of a permission notice identical to -this one. - -Permission is granted to copy and distribute translations of this -manual into another language, under the above conditions for modified -versions, except that this permission notice may be stated in a -translation approved by the Free Software Foundation. - -=cut - -use strict; -use warnings; - -require 5.006_001; - -use POSIX qw(getcwd); -use Getopt::Long; - -use lib "$ENV{HOME}/src/dotfiles/perl5"; -use Stow; -use Stow::Util qw(parent error); - -my $ProgramName = $0; -$ProgramName =~ s{.*/}{}; - -main() unless caller(); - -sub main { - my ($options, $pkgs_to_unstow, $pkgs_to_stow) = process_options(); - - my $stow = new Stow(%$options); - # current dir is now the target directory - - $stow->plan_unstow(@$pkgs_to_unstow); - $stow->plan_stow (@$pkgs_to_stow); - - my %conflicts = $stow->get_conflicts; - - if (%conflicts) { - foreach my $action ('unstow', 'stow') { - next unless $conflicts{$action}; - foreach my $package (sort keys %{ $conflicts{$action} }) { - warn "WARNING! ${action}ing $package would cause conflicts:\n"; - #if $stow->get_action_count > 1; - foreach my $message (sort @{ $conflicts{$action}{$package} }) { - warn " * $message\n"; - } - } - } - warn "All operations aborted.\n"; - exit 1; - } - else { - if ($options->{simulate}) { - warn "WARNING: in simulation mode so not modifying filesystem.\n"; - return; - } - - $stow->process_tasks(); - } -} - - -#===== SUBROUTINE =========================================================== -# Name : process_options() -# Purpose : parse command line options -# Parameters: none -# Returns : (\%options, \@pkgs_to_unstow, \@pkgs_to_stow) -# Throws : a fatal error if a bad command line option is given -# Comments : checks @ARGV for valid package names -#============================================================================ -sub process_options { - my %options = (); - my @pkgs_to_unstow = (); - my @pkgs_to_stow = (); - my $action = 'stow'; - - unshift @ARGV, get_config_file_options(); - #$,="\n"; print @ARGV,"\n"; # for debugging rc file - - Getopt::Long::config('no_ignore_case', 'bundling', 'permute'); - GetOptions( - \%options, - 'verbose|v:+', 'help|h', 'simulate|n|no', - 'version|V', 'compat|p', 'dir|d=s', 'target|t=s', - 'adopt', 'no-folding', - - # clean and pre-compile any regex's at parse time - 'ignore=s' => - sub { - my $regex = $_[1]; - push @{$options{ignore}}, qr($regex\z); - }, - - 'override=s' => - sub { - my $regex = $_[1]; - push @{$options{override}}, qr(\A$regex); - }, - - 'defer=s' => - sub { - my $regex = $_[1]; - push @{$options{defer}}, qr(\A$regex); - }, - - # a little craziness so we can do different actions on the same line: - # a -D, -S, or -R changes the action that will be performed on the - # package arguments that follow it. - 'D|delete' => sub { $action = 'unstow' }, - 'S|stow' => sub { $action = 'stow' }, - 'R|restow' => sub { $action = 'restow' }, - - # Handler for non-option arguments - '<>' => - sub { - if ($action eq 'restow') { - push @pkgs_to_unstow, $_[0]; - push @pkgs_to_stow, $_[0]; - } - elsif ($action eq 'unstow') { - push @pkgs_to_unstow, $_[0]; - } - else { - push @pkgs_to_stow, $_[0]; - } - }, - ) or usage(); - - usage() if $options{help}; - version() if $options{version}; - - sanitize_path_options(\%options); - check_packages(\@pkgs_to_unstow, \@pkgs_to_stow); - - return (\%options, \@pkgs_to_unstow, \@pkgs_to_stow); -} - -sub sanitize_path_options { - my ($options) = @_; - - if (exists $options->{dir}) { - $options->{dir} =~ s/\A +//; - $options->{dir} =~ s/ +\z//; - } - else { - $options->{dir} = exists $ENV{STOW_DIR} ? $ENV{STOW_DIR} : getcwd(); - } - - if (exists $options->{target}) { - $options->{target} =~ s/\A +//; - $options->{target} =~ s/ +\z//; - } - else { - $options->{target} = parent($options->{dir}) || '.'; - } -} - -sub check_packages { - my ($pkgs_to_stow, $pkgs_to_unstow) = @_; - - if (not @$pkgs_to_stow and not @$pkgs_to_unstow) { - usage("No packages to stow or unstow"); - } - - # check package arguments - for my $package (@$pkgs_to_stow, @$pkgs_to_unstow) { - $package =~ s{/+$}{}; # delete trailing slashes - if ($package =~ m{/}) { - error("Slashes are not permitted in package names"); - } - } -} - - -#===== SUBROUTINE ============================================================ -# Name : get_config_file_options() -# Purpose : search for default settings in any .stowrc files -# Parameters: none -# Returns : a list of default options -# Throws : no exceptions -# Comments : prepends the contents of '~/.stowrc' and '.stowrc' to the command -# : line so they get parsed just like normal arguments. (This was -# : hacked in so that Emil and I could set different preferences). -#============================================================================= -sub get_config_file_options { - my @defaults = (); - for my $file ("$ENV{HOME}/.stowrc", '.stowrc') { - if (-r $file) { - warn "Loading defaults from $file\n"; - open my $FILE, '<', $file - or die "Could not open $file for reading\n"; - while (my $line = <$FILE>){ - chomp $line; - push @defaults, split " ", $line; - } - close $FILE or die "Could not close open file: $file\n"; - } - } - return @defaults; -} - -#===== SUBROUTINE =========================================================== -# Name : usage() -# Purpose : print program usage message and exit -# Parameters: $msg => string to prepend to the usage message -# Returns : n/a -# Throws : n/a -# Comments : if 'msg' is given, then exit with non-zero status -#============================================================================ -sub usage { - my ($msg) = @_; - - if ($msg) { - print "$ProgramName: $msg\n\n"; - } - - print <<"EOT"; -$ProgramName (GNU Stow) version $Stow::VERSION - -SYNOPSIS: - - $ProgramName [OPTION ...] [-D|-S|-R] PACKAGE ... [-D|-S|-R] PACKAGE ... - -OPTIONS: - - -d DIR, --dir=DIR Set stow dir to DIR (default is current dir) - -t DIR, --target=DIR Set target to DIR (default is parent of stow dir) - - -S, --stow Stow the package names that follow this option - -D, --delete Unstow the package names that follow this option - -R, --restow Restow (like stow -D followed by stow -S) - - --ignore=REGEX Ignore files ending in this Perl regex - --defer=REGEX Don't stow files beginning with this Perl regex - if the file is already stowed to another package - --override=REGEX Force stowing files beginning with this Perl regex - if the file is already stowed to another package - --adopt (Use with care!) Import existing files into stow package - from target. Please read docs before using. - -p, --compat Use legacy algorithm for unstowing - - -n, --no, --simulate Do not actually make any filesystem changes - -v, --verbose[=N] Increase verbosity (levels are 0,1,2,3; - -v or --verbose adds 1; --verbose=N sets level) - -V, --version Show stow version number - -h, --help Show this help - -Report bugs to: bug-stow\@gnu.org -Stow home page: <http://www.gnu.org/software/stow/> -General help using GNU software: <http://www.gnu.org/gethelp/> -EOT - exit defined $msg ? 1 : 0; -} - -sub version { - print "$ProgramName (GNU Stow) version $Stow::VERSION\n"; - exit 0; -} - -1; # This file is required by t/stow.t - -# Local variables: -# mode: perl -# cperl-indent-level: 4 -# end: -# vim: ft=perl |