Note: If you reply to this, please CC me (I'm not on the list). A co-worker and I happened to be talking about different packaging mechanisms, and what most people really need. What always tends to be a problem is tracking non-packaged installed tarballs (the old ./configure; make; make install routine). My co-worker is very technically adept, but he concedes that he doesn't bother installing from tarballs because they get lost on the system; removing them becomes a pain.
This got us thinking to how such installations could be tracked, and it turns out to be a pretty simple idea that we thought of. Since most GNU-tool-based tarballs use install(1) to move files into the system, why not setup a little catch inside of install(1) to record what it's putting where? So I created a little patch (attached) to install(1) so that each time it copies a file, it writes a small log to the file named in $INSTALL_LOG, if it's set. The idea is that you set $INSTALL_LOG to a filename before you run "make install", run "make install", and then you can stuff the logfile away after running the install, needing only the logfile later to deinstall the program. Of course, program installs can be more complex than simply copying files, but that sort of leads me to my next point... What does this have over "make uninstall"? Well, it's simpler to use, for one. You also don't need to keep the source tree around for the Makefiles merely to remove a program, and execute code to deinstall. The deinstallation procedure is quite simple; the log messages are merely lines of "install: /foo/bar". This is human-readable, and you can 'see' what was installed, too, easily. E.g., from just installing nilsimsa: install: /usr/local/doc/nilsimsa/index.html install: /usr/local/doc/nilsimsa/index-1.html install: /usr/local/doc/nilsimsa/index-2.html install: /usr/local/doc/nilsimsa/index-3.html install: /usr/local/doc/nilsimsa/index-4.html install: /usr/local/doc/nilsimsa/index-5.html install: /usr/local/doc/nilsimsa/index-6.html install: /usr/local/bin/nilsimsa The goal: you manipulate this easily with text-processing utils. Maybe people are looking for this functionality, maybe not. I think it's a nice, clean solution that is much better than relying on "make uninstall". "make uninstall" has the potential to be much more powerful, but takes a huge complexity hit. Note that install(1) doesn't record creating directories, only copying files. The make_path was external to install.c, and I wanted keep my changes minimal. Any comments/ideas on this subject are appreciated. Have people thought of this before and been shot down? Disclaimer: I don't like writing C, so you're likely to find a problem in the attached patches. However, again, it's really a simple patch. I didn't write any docs for it, because I don't know TeX or roff. Oh, and I created a test too for this functionality (also attached). -- Frank Tobin http://www.neverending.org/~ftobin/
#! /bin/sh # Test $INSTALL_LOG stuff # Note that the tests below use `ginstall', not install, because # that's the name of the binary in ../../src. if test "$VERBOSE" = yes; then set -x ginstall --version fi . $srcdir/../envvar-check pwd=`pwd` to=to INSTALL_LOG=log export INSTALL_LOG from=from expected=expected trap "cd $pwd; rm -rf $INSTALL_LOG $from $to $expected" 0 1 2 3 15 fail=0 echo foo > $from echo "install: $to" > $expected # Before 4.0q, this would mistakenly create $file, not `dest' # in no-dir1/no-dir2/. ginstall $from $to || fail=1 cmp $expected log || fail=1 exit $fail
diff -ur fileutils-4.1.orig/src/install.c fileutils-4.1/src/install.c --- fileutils-4.1.orig/src/install.c Mon Dec 25 06:07:36 2000 +++ fileutils-4.1/src/install.c Fri Mar 15 00:01:40 2002 @@ -94,6 +94,9 @@ static int install_file_in_file PARAMS ((const char *from, const char *to, const struct cp_options *x)); static void get_ids PARAMS ((void)); +static void log_add_install PARAMS ((const char *to)); +static void log_finalize PARAMS ((void)); +static void log_init PARAMS ((const char *path)); static void strip PARAMS ((const char *path)); void usage PARAMS ((int status)); @@ -124,6 +127,9 @@ /* If nonzero, install a directory instead of a regular file. */ static int dir_arg; +/* The filehandle of the log (if any) */ +static FILE *log_file; + static struct option const long_options[] = { {"backup", optional_argument, NULL, 'b'}, @@ -191,6 +197,7 @@ struct cp_options x; int n_files; char **file; + char *log_fn = NULL; program_name = argv[0]; setlocale (LC_ALL, ""); @@ -206,11 +213,16 @@ strip_files = 0; dir_arg = 0; umask (0); + log_file = NULL; /* FIXME: consider not calling getenv for SIMPLE_BACKUP_SUFFIX unless we'll actually use backup_suffix_string. */ backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX"); + log_fn = getenv("INSTALL_LOG"); + if (log_fn) + log_init(log_fn); + while ((optc = getopt_long (argc, argv, "bcsDdg:m:o:pvV:S:", long_options, NULL)) != -1) { @@ -346,6 +358,8 @@ } } + log_finalize(); + exit (errors); } @@ -392,6 +406,7 @@ { if (copy_file (from, to, x)) return 1; + log_add_install(to); if (strip_files) strip (to); if (change_attributes (to)) @@ -588,6 +603,32 @@ else group_id = (gid_t) -1; } + + +static void +log_add_install (const char *to) +{ + if (log_file) + fprintf(log_file, "install: %s\n", to); +} + + +static void +log_finalize (void) +{ + if (log_file) + fclose(log_file); +} + + +static void +log_init (const char *path) +{ + log_file = fopen (path, "a"); + if (!log_file) + error (0, 1, "cannot open log %s", path); +} + void usage (int status) diff -ur fileutils-4.1.orig/tests/install/Makefile.am fileutils-4.1/tests/install/Makefile.am --- fileutils-4.1.orig/tests/install/Makefile.am Sun Feb 27 09:10:57 2000 +++ fileutils-4.1/tests/install/Makefile.am Thu Mar 14 23:48:59 2002 @@ -1,7 +1,7 @@ ## Process this file with automake to produce Makefile.in -*-Makefile-*-. AUTOMAKE_OPTIONS = 1.3 gnits -TESTS = basic-1 create-leading +TESTS = basic-1 create-leading create-log EXTRA_DIST = $(TESTS) TESTS_ENVIRONMENT = \ PATH=`pwd`/../../src:$$PATH