A tip, a question, and feature request ...

Magnus Thor Torfason <[email protected]> Tue, 09 Apr 2013 15:45:17 -0400
Newsgroups gmane.comp.gnu.stow.general
Message-ID <[email protected]>
Dear Adam (and others),

I recently came across stow, and it is a real life-saver. Thank you!

So, I guess this mail is part tip, part question, and part feature 
request. So here goes ...


THE TIP

In the documentation, there is some discussion about the issues with 
compiling software for a different location than it is eventually going 
to be installed with (Section 12, Compile-time vs Install-time). But it 
seems to me that instead of waiting for emacs and others to individually 
fix this, it is better to just make stow work with the programs as they 
function today. Here's how:

We assume that we want to use /usr/local for the target directory and 
/opt/stow for the stow directory. First, we initialize stow by cleaning 
out any old crud that already exists in /usr/local using the following 
command:

   > stow --adopt-all orphans

We now have a stow package (in /opt/stow/orphans) containing all the 
crud, and /usr/local is clean. When we want to install a new package, 
say, the moe text editor, we first make sure that there is definitely no 
crud in /usr/local (and it should all be gone now):

   > chkstow -a

and then we type:

   > cd src
   > wget http://ftp.gnu.org/gnu/moe/moe-1.5.tar.gz
   > tar zxvf moe-1.5.tar.gz
   > cd moe-1.5
   > ./configure
   > make
   > make install

Whoa! This installed moe into /usr/local, not stow. That's OK, though, 
because now we are going to do this:

   > stow --adopt-all moe-1.5

Boom, we are done, now we have a stow package for moe in 
/opt/stow/moe-1.5 and we can uninstall, reinstall or upgrade moe 
whenever we want.


QUESTION

Is there any reason one should not follow this approach?


ANSWER (*)

Why, yes there is a good reason: stow does not accept the --adopt-all 
switch! Which is why I actually do this instead:

   > stow.adopt.all moe-1.5

Where stow.adopt.all is a bash function:
stow.adopt.all ()
{
     if [ "$1" == "" ] || [ "$2" != "" ]; then
         echo "Incorrect number of arguments to stow.adopt.all (expected 
exactly one).";
         return 1;
     fi;
     DIR=/opt/stow;
     TARGET=/usr/local;
     PACKAGE="$1";
     chkstow -t $TARGET -a | sed "s+^Unstowed file: 
$TARGET+$DIR/$PACKAGE+" | xargs -l mktouch;
     stow --adopt -vv $PACKAGE
}

This requires mktouch, which is a shell script:
#!/bin/sh

if [ "$1" == "" ] || [ "$2" != "" ];  then
     echo "Incorrect number of arguments to mktouch (expected exactly one)."
     exit 1
fi

mkdir -p "$(dirname "$1")"
touch "$1"
# end of mktouch

So the deal here? Find all aliens in /usr/local and touch their 
equivalent in the stow package directory of choice, because "stow 
--adopt" (the real one, that currently exists) will only adopt files if 
it is trying to install those specific files. Only after creating the 
structure (of empty files) within the package directory do we run "stow 
--adopt", which then replaces all these empty files with the recently 
installed files.

This takes care of most of the work, but there are two more caveats. 
First, for this to work, one must not use the (awesome) folding feature 
of stow, because if there are directory symlinks in /usr/local, "make 
install" will probably end up installing stuff into the stow package 
directories, which will screw everything up. So my .stowrc looks like 
this (I've elected to keep my stow packages in /opt/stow instead of 
/usr/local/stow partly because of personal preference, but also because 
it makes some of my workarounds simpler, since I can then assume that 
there should be absolutely nothing inside /usr/local apart from symlinks 
that point to .../opt/stow/...). So again, here is my .stowrc:

-d /opt/stow
-t /usr/local
--no-folding

Now we are 95% there. The only other issue is that "stow --adopt" (and 
"chkstow -a") does not correctly handle alien symlinks. For example, 
mc-4.6.1 installs the following symlinks in bin: "mcedit -> mc" and 
"mcview -> mc". Stow will not detect these as aliens. So I have one more 
addition in my profile:

alias stow.aliens='find /usr/local -type f -o -type l | xargs ls -l | 
grep -v "opt/stow"'

I have not implemented this into my stow.adopt.all function, so I need 
to manually run this after any installation to make sure that there are 
no symlinks, and if there are I have to manually move them from the stow 
directory, and then reinstall the package.

Now, this takes me to my actual question: "If we had a 'stow --adopt-all 
<package>' command, would this then not be a pretty good way to do stow 
installations?"


FEATURE REQUEST

And finally, assuming that the answer to my last question is yes, here 
are some feature requests that, if implemented, would make this a robust 
usage pattern:

  - Add a --adopt-all switch to stow, which would cause stow to adopt
    any alien file in the target directory. In this case, one might
    even imagine that the existence of a corresponding file in the
    stow/package directory would be perceived to be a conflict (I
    would expect the actual use case for this switch to always or
    almost always be executed with an empty or (even better) absent
    stow/package directory).

  - Update stow and chkstow so that they correctly handle alien symlinks.
    This would apply to the following commands at least:
      > stow --adopt <package>
      > stow --adopt-all <package>
      > chkstow -a
    It seems to me that chkstow -a would have to be aware of the
    location of the stow directory to be able to do this,
    probably by adding a --dir param to chkstow.

And then a few other, lower priority, feature requests
while I am at it:

   - Update chkstow -l so that it works with a custom stow
     directory location (this would also require awareness of
     stow directory location)

   - It would be very nice if chkstow had the same default
     options for target dir as stow does, and perhaps if
     it could even have a .chkstowrc file?

   - And even nicer would be if ther was a --rcfile param
     to stow, maybe even for chkstow as well:
       > stow --rcfile=~/my-stowrc-for-homedir <...>
       > stow --rcfile=~/my-stowrc-for-usrlocal <...>
       > chkstow --rcfile=~/my-chkstowrc-for-homedir <...>
       > chkstow --rcfile=~/my-chkstowrc-for-usrlocal <...>

Well, that's asking for a lot, I know! But please take this and use as 
an input to future development in whatever way you want. I think that 
this would be a neat way to get out of the dependency on programs 
playing nice with different compile and install locations, and so 
increase the number of situations where stow could be used. Also, since 
the default install location (/usr/local) is typically simpler to 
configure in any randomly downloaded source package, it would make 
compilation even easier.

I hope you find these thoughts and suggestions useful.

Best,
Magnus