Subject: Re: bug#9578: make uninstall fails after make install-data in empty prefix.

From: Stefano Lattarini <[email-masked]>
Date: Sat, 24 Sep 2011 15:31:22 +0200

On Thursday 22 September 2011, Nick Bowler wrote:
> Hello,
Hi Nick, thanks for the report.

> I noticed a problem testing the uninstall target of my
> automake-generated Makefiles.  If the install prefix does not exist
> before installation, automake will create the necessary directories.
> But if you run a plain "make install-data" (without make install-exec),
> the directories for executables are (unsurprisingly) not created.  The
> problem is that "make uninstall" fails in this case:
>
I'm not yet sure whether this should be considered a bug or a feature...
I'm inclined to agree with you that it's more of a bug though, so I'll
push the attached patch to maint in a few days if nobody objects.  This
change is not completely obvious, so any review would be appreciated.

Notice that the test coverage added by the patch is far from perfect, but
I'd rather improve it in the `testsuite-work' branch only, so that I'll
be able to use TAP and won't be forced to write 10 or so new "sister
tests" instead.

Thanks,
   Stefano
From 21011463ca951d397be0ea89e58b278ca8f40c2f Mon Sep 17 00:00:00 2001
Message-Id: <21011463c[email-masked]>
From: Stefano Lattarini <[email-masked]>
Date: Fri, 23 Sep 2011 22:50:14 +0200
Subject: [PATCH] uninstall: "make uninstall" before "make install" works

This change fixes automake bug#9578.

* lib/am/inst-vars.am (am__uninstall_files_from_dir): New internal
macro, that defines a shell code fragment to uninstall files from
a given directory.
* lib/am/data.am (uninstall-%DIR%%PRIMARY%): Use it, to reduce code
duplication and improve consistency and correctness.
* lib/am/libs.am (uninstall-%DIR%LIBRARIES): Likewise.
* lib/am/lisp.am (uninstall-%DIR%LISP): Likewise.
* lib/am/mans.am (uninstall-man%SECTION%): Likewise.
* lib/am/python.am (uninstall-%DIR%LIBRARIES): Likewise.
* lib/am/scripts.am (uninstall-%DIR%SCRIPTS): Likewise.
* tests/uninstall-pr9578.test: New test.
* tests/uninstall-fail.test: New test.
* tests/Makefile.am (TESTS): Add them.
* NEWS, THANKS: Update.

Report by Nick Bowler.
---
 ChangeLog                      |   20 ++++++++++++
 Makefile.in                    |   10 ++++--
 NEWS                           |    4 ++
 THANKS                         |    1 +
 doc/Makefile.in                |   14 +++++----
 lib/Automake/Makefile.in       |   14 +++++----
 lib/Automake/tests/Makefile.in |    6 ++++
 lib/Makefile.in                |   14 +++++----
 lib/am/Makefile.in             |   10 ++++--
 lib/am/data.am                 |    4 +--
 lib/am/inst-vars.am            |   18 +++++++++++
 lib/am/libs.am                 |    4 +--
 lib/am/lisp.am                 |    8 +----
 lib/am/mans.am                 |    8 +----
 lib/am/python.am               |   15 ++++-----
 lib/am/scripts.am              |    4 +--
 m4/Makefile.in                 |   14 +++++----
 tests/Makefile.am              |    2 +
 tests/Makefile.in              |    8 +++++
 tests/uninstall-fail.test      |   64 +++++++++++++++++++++++++++++++++++++++
 tests/uninstall-pr9578.test    |   65 ++++++++++++++++++++++++++++++++++++++++
 21 files changed, 248 insertions(+), 59 deletions(-)
 create mode 100755 tests/uninstall-fail.test
 create mode 100755 tests/uninstall-pr9578.test

diff --git a/ChangeLog b/ChangeLog
index 47aee92..4347a89 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,23 @@
+2011-09-24  Stefano Lattarini  <[email-masked]>
+
+	uninstall: "make uninstall" before "make install" works
+	This change fixes automake bug#9578.
+	* lib/am/inst-vars.am (am__uninstall_files_from_dir): New internal
+	macro, that defines a shell code fragment to uninstall files from
+	a given directory.
+	* lib/am/data.am (uninstall-%DIR%%PRIMARY%): Use it, to reduce code
+	duplication and improve consistency and correctness.
+	* lib/am/libs.am (uninstall-%DIR%LIBRARIES): Likewise.
+	* lib/am/lisp.am (uninstall-%DIR%LISP): Likewise.
+	* lib/am/mans.am (uninstall-man%SECTION%): Likewise.
+	* lib/am/python.am (uninstall-%DIR%LIBRARIES): Likewise.
+	* lib/am/scripts.am (uninstall-%DIR%SCRIPTS): Likewise.
+	* tests/uninstall-pr9578.test: New test.
+	* tests/uninstall-fail.test: New test.
+	* tests/Makefile.am (TESTS): Add them.
+	* NEWS, THANKS: Update.
+	Report by Nick Bowler.
+
 2011-09-22  Stefano Lattarini  <[email-masked]>
 
 	tests: fix tests on aclocal search path precedences
diff --git a/Makefile.in b/Makefile.in
index 35a9cbd..a8c3244 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -91,6 +91,12 @@ am__nobase_list = $(am__nobase_strip_setup); \
 am__base_list = \
   sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
   sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+  test -z "$$files" \
+    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+         cd "$$dir" && rm -f $$files; }; \
+  }
 am__installdirs = "$(DESTDIR)$(bindir)"
 SCRIPTS = $(bin_SCRIPTS)
 SOURCES =
@@ -447,9 +453,7 @@ uninstall-binSCRIPTS:
 	@list='$(bin_SCRIPTS)'; test -n "$(bindir)" || exit 0; \
 	files=`for p in $$list; do echo "$$p"; done | \
 	       sed -e 's,.*/,,;$(transform)'`; \
-	test -n "$$list" || exit 0; \
-	echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
-	cd "$(DESTDIR)$(bindir)" && rm -f $$files
+	dir='$(DESTDIR)$(bindir)'; $(am__uninstall_files_from_dir)
 
 # This directory's subdirectories are mostly independent; you can cd
 # into them and run `make' without going through this Makefile.
diff --git a/NEWS b/NEWS
index b696977..54838d0 100644
--- a/NEWS
+++ b/NEWS
@@ -61,6 +61,10 @@ Bugs fixed in 1.11.0a:
 
 * Long standing bugs:
 
+  - A "make uninstall" issued before a "make install", or after a mere
+    "make install-data" or a mere "make install-exec" does not spuriously
+    fail anymore.
+
   - Automake now warns about more primary/directory invalid combinations,
     such as "doc_LIBRARIES" or "pkglib_PROGRAMS".
 
diff --git a/THANKS b/THANKS
index f83e1fc..b840088 100644
--- a/THANKS
+++ b/THANKS
@@ -244,6 +244,7 @@ Motoyuki Kasahara	[email-masked]
 Nathanael Nerode	[email-masked]
 Nelson H. F. Beebe	[email-masked]
 Nicholas Wourms		[email-masked]
+Nick Bowler		[email-masked]
 Nicolas Joly		[email-masked]
 Nicolas Thiery		[email-masked]
 NightStrike		[email-masked]
diff --git a/doc/Makefile.in b/doc/Makefile.in
index 3034dcb..23630ab 100644
--- a/doc/Makefile.in
+++ b/doc/Makefile.in
@@ -105,6 +105,12 @@ am__nobase_list = $(am__nobase_strip_setup); \
 am__base_list = \
   sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
   sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+  test -z "$$files" \
+    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+         cd "$$dir" && rm -f $$files; }; \
+  }
 man1dir = $(mandir)/man1
 NROFF = nroff
 MANS = $(dist_man1_MANS)
@@ -478,9 +484,7 @@ uninstall-man1:
 	files=`{ for i in $$list; do echo "$$i"; done; \
 	} | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \
 	      -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
-	test -z "$$files" || { \
-	  echo " ( cd '$(DESTDIR)$(man1dir)' && rm -f" $$files ")"; \
-	  cd "$(DESTDIR)$(man1dir)" && rm -f $$files; }
+	dir='$(DESTDIR)$(man1dir)'; $(am__uninstall_files_from_dir)
 install-dist_docDATA: $(dist_doc_DATA)
 	@$(NORMAL_INSTALL)
 	test -z "$(docdir)" || $(MKDIR_P) "$(DESTDIR)$(docdir)"
@@ -498,9 +502,7 @@ uninstall-dist_docDATA:
 	@$(NORMAL_UNINSTALL)
 	@list='$(dist_doc_DATA)'; test -n "$(docdir)" || list=; \
 	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
-	test -n "$$files" || exit 0; \
-	echo " ( cd '$(DESTDIR)$(docdir)' && rm -f" $$files ")"; \
-	cd "$(DESTDIR)$(docdir)" && rm -f $$files
+	dir='$(DESTDIR)$(docdir)'; $(am__uninstall_files_from_dir)
 
 ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
 	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
diff --git a/lib/Automake/Makefile.in b/lib/Automake/Makefile.in
index baccf7d..e23e0ae 100644
--- a/lib/Automake/Makefile.in
+++ b/lib/Automake/Makefile.in
@@ -96,6 +96,12 @@ am__nobase_list = $(am__nobase_strip_setup); \
 am__base_list = \
   sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
   sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+  test -z "$$files" \
+    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+         cd "$$dir" && rm -f $$files; }; \
+  }
 am__installdirs = "$(DESTDIR)$(perllibdir)" "$(DESTDIR)$(perllibdir)"
 DATA = $(dist_perllib_DATA) $(nodist_perllib_DATA)
 RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive	\
@@ -316,9 +322,7 @@ uninstall-dist_perllibDATA:
 	@$(NORMAL_UNINSTALL)
 	@list='$(dist_perllib_DATA)'; test -n "$(perllibdir)" || list=; \
 	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
-	test -n "$$files" || exit 0; \
-	echo " ( cd '$(DESTDIR)$(perllibdir)' && rm -f" $$files ")"; \
-	cd "$(DESTDIR)$(perllibdir)" && rm -f $$files
+	dir='$(DESTDIR)$(perllibdir)'; $(am__uninstall_files_from_dir)
 install-nodist_perllibDATA: $(nodist_perllib_DATA)
 	@$(NORMAL_INSTALL)
 	test -z "$(perllibdir)" || $(MKDIR_P) "$(DESTDIR)$(perllibdir)"
@@ -336,9 +340,7 @@ uninstall-nodist_perllibDATA:
 	@$(NORMAL_UNINSTALL)
 	@list='$(nodist_perllib_DATA)'; test -n "$(perllibdir)" || list=; \
 	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
-	test -n "$$files" || exit 0; \
-	echo " ( cd '$(DESTDIR)$(perllibdir)' && rm -f" $$files ")"; \
-	cd "$(DESTDIR)$(perllibdir)" && rm -f $$files
+	dir='$(DESTDIR)$(perllibdir)'; $(am__uninstall_files_from_dir)
 
 # This directory's subdirectories are mostly independent; you can cd
 # into them and run `make' without going through this Makefile.
diff --git a/lib/Automake/tests/Makefile.in b/lib/Automake/tests/Makefile.in
index 17a2002..14a8122 100644
--- a/lib/Automake/tests/Makefile.in
+++ b/lib/Automake/tests/Makefile.in
@@ -101,6 +101,12 @@ am__nobase_list = $(am__nobase_strip_setup); \
 am__base_list = \
   sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
   sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+  test -z "$$files" \
+    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+         cd "$$dir" && rm -f $$files; }; \
+  }
 # Restructured Text title and section.
 am__rst_title = sed 's/.*/   &   /;h;s/./=/g;p;x;p;g;p;s/.*//'
 am__rst_section = sed 'p;s/./=/g;p;g'
diff --git a/lib/Makefile.in b/lib/Makefile.in
index bc2a6c0..9caa64a 100644
--- a/lib/Makefile.in
+++ b/lib/Makefile.in
@@ -98,6 +98,12 @@ am__nobase_list = $(am__nobase_strip_setup); \
 am__base_list = \
   sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
   sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+  test -z "$$files" \
+    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+         cd "$$dir" && rm -f $$files; }; \
+  }
 am__installdirs = "$(DESTDIR)$(pkgvdatadir)" "$(DESTDIR)$(scriptdir)"
 DATA = $(dist_pkgvdata_DATA) $(dist_script_DATA)
 RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive	\
@@ -287,9 +293,7 @@ uninstall-dist_pkgvdataDATA:
 	@$(NORMAL_UNINSTALL)
 	@list='$(dist_pkgvdata_DATA)'; test -n "$(pkgvdatadir)" || list=; \
 	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
-	test -n "$$files" || exit 0; \
-	echo " ( cd '$(DESTDIR)$(pkgvdatadir)' && rm -f" $$files ")"; \
-	cd "$(DESTDIR)$(pkgvdatadir)" && rm -f $$files
+	dir='$(DESTDIR)$(pkgvdatadir)'; $(am__uninstall_files_from_dir)
 install-dist_scriptDATA: $(dist_script_DATA)
 	@$(NORMAL_INSTALL)
 	test -z "$(scriptdir)" || $(MKDIR_P) "$(DESTDIR)$(scriptdir)"
@@ -307,9 +311,7 @@ uninstall-dist_scriptDATA:
 	@$(NORMAL_UNINSTALL)
 	@list='$(dist_script_DATA)'; test -n "$(scriptdir)" || list=; \
 	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
-	test -n "$$files" || exit 0; \
-	echo " ( cd '$(DESTDIR)$(scriptdir)' && rm -f" $$files ")"; \
-	cd "$(DESTDIR)$(scriptdir)" && rm -f $$files
+	dir='$(DESTDIR)$(scriptdir)'; $(am__uninstall_files_from_dir)
 
 # This directory's subdirectories are mostly independent; you can cd
 # into them and run `make' without going through this Makefile.
diff --git a/lib/am/Makefile.in b/lib/am/Makefile.in
index 649c1bf..194f1cb 100644
--- a/lib/am/Makefile.in
+++ b/lib/am/Makefile.in
@@ -89,6 +89,12 @@ am__nobase_list = $(am__nobase_strip_setup); \
 am__base_list = \
   sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
   sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+  test -z "$$files" \
+    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+         cd "$$dir" && rm -f $$files; }; \
+  }
 am__installdirs = "$(DESTDIR)$(amdir)"
 DATA = $(dist_am_DATA)
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
@@ -279,9 +285,7 @@ uninstall-dist_amDATA:
 	@$(NORMAL_UNINSTALL)
 	@list='$(dist_am_DATA)'; test -n "$(amdir)" || list=; \
 	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
-	test -n "$$files" || exit 0; \
-	echo " ( cd '$(DESTDIR)$(amdir)' && rm -f" $$files ")"; \
-	cd "$(DESTDIR)$(amdir)" && rm -f $$files
+	dir='$(DESTDIR)$(amdir)'; $(am__uninstall_files_from_dir)
 tags: TAGS
 TAGS:
 
diff --git a/lib/am/data.am b/lib/am/data.am
index 36af717..1817447 100644
--- a/lib/am/data.am
+++ b/lib/am/data.am
@@ -73,9 +73,7 @@ uninstall-%DIR%%PRIMARY%:
 	@list='$(%DIR%_%PRIMARY%)'; test -n "$(%NDIR%dir)" || list=; \
 ?BASE?	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
 ?!BASE?	$(am__nobase_strip_setup); files=`$(am__nobase_strip)`; \
-	test -n "$$files" || exit 0; \
-	echo " ( cd '$(DESTDIR)$(%NDIR%dir)' && rm -f" $$files ")"; \
-	cd "$(DESTDIR)$(%NDIR%dir)" && rm -f $$files
+	dir='$(DESTDIR)$(%NDIR%dir)'; $(am__uninstall_files_from_dir)
 endif %?INSTALL%

diff --git a/lib/am/inst-vars.am b/lib/am/inst-vars.am
index a49cfe9..477513f 100644
--- a/lib/am/inst-vars.am
+++ b/lib/am/inst-vars.am
@@ -15,6 +15,7 @@
 ## along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 if %?FIRST%
+
 ## These variables help stripping any $(VPATH) that some
 ## Make implementations prepend before VPATH-found files.
 ## The issue is discussed at length in distdir.am.
@@ -52,4 +53,21 @@ am__nobase_list = $(am__nobase_strip_setup); \
 am__base_list = \
   sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
   sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+
+## A shell code fragment to uninstall files from a given directory.
+## It expects the $dir and $files shell variables to be defined respectively
+## to the directory where the files to be removed are, and to the list of
+## such files.
+am__uninstall_files_from_dir = { \
+## Some rm implementations complain if `rm -f' is used without arguments.
+  test -z "$$files" \
+## At least Solaris /bin/sh still lacks `test -e', so we use the multiple
+## tests below instead.  We expect $dir to be either non-existent or a
+## directory, so the failure we'll experience if it is a regular file
+## is indeed desired and welcome (better to fail loudly thasn silently).
+    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+         cd "$$dir" && rm -f $$files; }; \
+  }
+
 endif %?FIRST%
diff --git a/lib/am/libs.am b/lib/am/libs.am
index eec62a1..29f630b 100644
--- a/lib/am/libs.am
+++ b/lib/am/libs.am
@@ -87,9 +87,7 @@ uninstall-%DIR%LIBRARIES:
 	@list='$(%DIR%_LIBRARIES)'; test -n "$(%NDIR%dir)" || list=; \
 ?BASE?	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
 ?!BASE?	$(am__nobase_strip_setup); files=`$(am__nobase_strip)`; \
-	test -n "$$files" || exit 0; \
-	echo " ( cd '$(DESTDIR)$(%NDIR%dir)' && rm -f "$$files" )"; \
-	cd "$(DESTDIR)$(%NDIR%dir)" && rm -f $$files
+	dir='$(DESTDIR)$(%NDIR%dir)'; $(am__uninstall_files_from_dir)
 endif %?INSTALL%

diff --git a/lib/am/lisp.am b/lib/am/lisp.am
index ab45b30..6ffcdbf 100644
--- a/lib/am/lisp.am
+++ b/lib/am/lisp.am
@@ -128,12 +128,8 @@ uninstall-%DIR%LISP:
 	list='$(%DIR%_LISP)'; \
 ?BASE?	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
 ?!BASE?	$(am__nobase_strip_setup); files=`$(am__nobase_strip)`; \
-	test -n "$$files" || exit 0; \
-	filesc=`echo "$$files" | sed 's|$$|c|'`; \
-	echo " ( cd '$(DESTDIR)$(%NDIR%dir)' && rm -f" $$files ")"; \
-	cd "$(DESTDIR)$(%NDIR%dir)" && rm -f $$files || exit $$?; \
-	echo " ( cd '$(DESTDIR)$(%NDIR%dir)' && rm -f" $$filesc ")"; \
-	cd "$(DESTDIR)$(%NDIR%dir)" && rm -f $$filesc
+	files="$$files "`echo "$$files" | sed 's|$$|c|'`; \
+	dir='$(DESTDIR)$(%NDIR%dir)'; $(am__uninstall_files_from_dir)
 endif %?INSTALL%

diff --git a/lib/am/mans.am b/lib/am/mans.am
index 66a6c7e..b41a94d 100644
--- a/lib/am/mans.am
+++ b/lib/am/mans.am
@@ -119,9 +119,7 @@ if %?NOTRANS_MANS%
 ?HAVE_NOTRANS?	  sed -n '/\.%SECTION%[a-z]*$$/p'; \
 ## Extract basename of manpage, change the extension if needed.
 	} | sed 's,.*/,,;s,\.[^%SECTION%][0-9a-z]*$$,.%SECTION%,'`; \
-	test -z "$$files" || { \
-	  echo " ( cd '$(DESTDIR)$(man%SECTION%dir)' && rm -f" $$files ")"; \
-	  cd "$(DESTDIR)$(man%SECTION%dir)" && rm -f $$files; }
+	dir='$(DESTDIR)$(man%SECTION%dir)'; $(am__uninstall_files_from_dir)
 endif %?NOTRANS_MANS%
 if %?TRANS_MANS%
 ## Handle MANS without notrans_ prefix
@@ -136,7 +134,5 @@ if %?TRANS_MANS%
 ## transform, and change the extension if needed.
 	} | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^%SECTION%][0-9a-z]*$$,%SECTION%,;x' \
 	      -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
-	test -z "$$files" || { \
-	  echo " ( cd '$(DESTDIR)$(man%SECTION%dir)' && rm -f" $$files ")"; \
-	  cd "$(DESTDIR)$(man%SECTION%dir)" && rm -f $$files; }
+	dir='$(DESTDIR)$(man%SECTION%dir)'; $(am__uninstall_files_from_dir)
 endif %?TRANS_MANS%
diff --git a/lib/am/python.am b/lib/am/python.am
index 40aaa62..427c95d 100644
--- a/lib/am/python.am
+++ b/lib/am/python.am
@@ -94,16 +94,15 @@ uninstall-%DIR%PYTHON:
 ?BASE?	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
 ?!BASE?	$(am__nobase_strip_setup); files=`$(am__nobase_strip)`; \
 	test -n "$$files" || exit 0; \
+	dir='$(DESTDIR)$(%NDIR%dir)'; \
+## Also remove the .pyc and .pyo byte compiled versions.
 	filesc=`echo "$$files" | sed 's|$$|c|'`; \
 	fileso=`echo "$$files" | sed 's|$$|o|'`; \
-	echo " ( cd '$(DESTDIR)$(%NDIR%dir)' && rm -f" $$files ")"; \
-	cd "$(DESTDIR)$(%NDIR%dir)" && rm -f $$files || exit $$?; \
-## This is to remove the .pyc and .pyo byte compiled versions (a bit
-## of a hack).
-	echo " ( cd '$(DESTDIR)$(%NDIR%dir)' && rm -f" $$filesc ")"; \
-	cd "$(DESTDIR)$(%NDIR%dir)" && rm -f $$filesc || exit $$?; \
-	echo " ( cd '$(DESTDIR)$(%NDIR%dir)' && rm -f" $$fileso ")"; \
-	cd "$(DESTDIR)$(%NDIR%dir)" && rm -f $$fileso
+	st=0; \
+	for files in "$$files" "$$filesc" "$$fileso"; do \
+	  $(am__uninstall_files_from_dir) || st=$$?; \
+	done; \
+	exit $$st
 endif %?INSTALL%

diff --git a/lib/am/scripts.am b/lib/am/scripts.am
index 790cb64..346f0d3 100644
--- a/lib/am/scripts.am
+++ b/lib/am/scripts.am
@@ -83,9 +83,7 @@ uninstall-%DIR%SCRIPTS:
 ?!BASE?	$(am__nobase_strip_setup); \
 ?!BASE?	files=`$(am__nobase_strip) \
 ?!BASE?	       -e 'h;s,.*/,,;$(transform);x;s|[^/]*$$||;G;s,\n,,'`; \
-	test -n "$$list" || exit 0; \
-	echo " ( cd '$(DESTDIR)$(%NDIR%dir)' && rm -f" $$files ")"; \
-	cd "$(DESTDIR)$(%NDIR%dir)" && rm -f $$files
+	dir='$(DESTDIR)$(%NDIR%dir)'; $(am__uninstall_files_from_dir)
 endif %?INSTALL%

diff --git a/m4/Makefile.in b/m4/Makefile.in
index 2e7e547..9fb6989 100644
--- a/m4/Makefile.in
+++ b/m4/Makefile.in
@@ -89,6 +89,12 @@ am__nobase_list = $(am__nobase_strip_setup); \
 am__base_list = \
   sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
   sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+  test -z "$$files" \
+    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+         cd "$$dir" && rm -f $$files; }; \
+  }
 am__installdirs = "$(DESTDIR)$(automake_acdir)" \
 	"$(DESTDIR)$(system_acdir)"
 DATA = $(dist_automake_ac_DATA) $(dist_system_ac_DATA)
@@ -281,9 +287,7 @@ uninstall-dist_automake_acDATA:
 	@$(NORMAL_UNINSTALL)
 	@list='$(dist_automake_ac_DATA)'; test -n "$(automake_acdir)" || list=; \
 	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
-	test -n "$$files" || exit 0; \
-	echo " ( cd '$(DESTDIR)$(automake_acdir)' && rm -f" $$files ")"; \
-	cd "$(DESTDIR)$(automake_acdir)" && rm -f $$files
+	dir='$(DESTDIR)$(automake_acdir)'; $(am__uninstall_files_from_dir)
 install-dist_system_acDATA: $(dist_system_ac_DATA)
 	@$(NORMAL_INSTALL)
 	test -z "$(system_acdir)" || $(MKDIR_P) "$(DESTDIR)$(system_acdir)"
@@ -301,9 +305,7 @@ uninstall-dist_system_acDATA:
 	@$(NORMAL_UNINSTALL)
 	@list='$(dist_system_ac_DATA)'; test -n "$(system_acdir)" || list=; \
 	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
-	test -n "$$files" || exit 0; \
-	echo " ( cd '$(DESTDIR)$(system_acdir)' && rm -f" $$files ")"; \
-	cd "$(DESTDIR)$(system_acdir)" && rm -f $$files
+	dir='$(DESTDIR)$(system_acdir)'; $(am__uninstall_files_from_dir)
 tags: TAGS
 TAGS:
 
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 1d258c9..221f08a 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -852,6 +852,8 @@ txinfo32.test \
 txinfo33.test \
 transform.test \
 transform2.test \
+uninstall-pr9578.test \
+uninstall-fail.test \
 unused.test \
 upc.test \
 upc2.test \
diff --git a/tests/Makefile.in b/tests/Makefile.in
index 7e9bc20..591cfc6 100644
--- a/tests/Makefile.in
+++ b/tests/Makefile.in
@@ -104,6 +104,12 @@ am__nobase_list = $(am__nobase_strip_setup); \
 am__base_list = \
   sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
   sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+  test -z "$$files" \
+    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+         cd "$$dir" && rm -f $$files; }; \
+  }
 # Restructured Text title and section.
 am__rst_title = sed 's/.*/   &   /;h;s/./=/g;p;x;p;g;p;s/.*//'
 am__rst_section = sed 'p;s/./=/g;p;g'
@@ -1130,6 +1136,8 @@ txinfo32.test \
 txinfo33.test \
 transform.test \
 transform2.test \
+uninstall-pr9578.test \
+uninstall-fail.test \
 unused.test \
 upc.test \
 upc2.test \
diff --git a/tests/uninstall-fail.test b/tests/uninstall-fail.test
new file mode 100755
index 0000000..6ba0eba
--- /dev/null
+++ b/tests/uninstall-fail.test
@@ -0,0 +1,64 @@
+#! /bin/sh
+# Copyright (C) 2011 Free Software Foundation, Inc.
+#
+# 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, 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/>.
+
+# "make uninstall" complains and errors out on failures.
+
+# FIXME: this test only dealt with DATA primary; maybe we need sister
+# tests for other primaries too?  E.g., SCRIPTS, PROGRAMS, LISP, PYTHON,
+# etc...
+
+. ./defs || Exit 1
+
+set -e
+
+cat >> configure.in << 'END'
+AC_OUTPUT
+END
+
+cat > Makefile.am << 'END'
+data_DATA = foobar.txt
+END
+
+: > foobar.txt
+
+$ACLOCAL
+$AUTOMAKE
+$AUTOCONF
+
+inst=__inst-dir__
+
+./configure --prefix="`pwd`/$inst"
+
+mkdir $inst $inst/share
+: > $inst/share/foobar.txt
+
+chmod a-w $inst/share
+touch $inst/share/t && skip_ "cannot make directories unwritable"
+rm -f $inst/share/t
+
+$MAKE uninstall >output 2>&1 && { cat output; Exit 1; }
+cat output
+grep "rm: .*foobar\.txt" output
+
+chmod a-rwx $inst/share
+(cd $inst/share) && skip_ "cannot make directories fully unreadable"
+
+$MAKE uninstall >output 2>&1 && { cat output; Exit 1; }
+cat output
+grep "cd: .*$inst/share" output
+
+
+:
diff --git a/tests/uninstall-pr9578.test b/tests/uninstall-pr9578.test
new file mode 100755
index 0000000..3f67203
--- /dev/null
+++ b/tests/uninstall-pr9578.test
@@ -0,0 +1,65 @@
+#! /bin/sh
+# Copyright (C) 2011 Free Software Foundation, Inc.
+#
+# 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, 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/>.
+
+# Check against automake bug#9578: "make uninstall" issued before
+# "make install" or after a mere "make install-data" or a mere
+# "make install-exec" failed spuriously.
+
+. ./defs || Exit 1
+
+set -e
+
+cat >> configure.in << 'END'
+AC_OUTPUT
+END
+
+: > foo
+: > bar
+
+cat > Makefile.am << 'END'
+bin_SCRIPTS = foo
+data_DATA = bar
+END
+
+$ACLOCAL
+$AUTOMAKE
+$AUTOCONF
+
+./configure --prefix="`pwd`/inst"
+
+$MAKE uninstall
+test ! -d inst
+
+rm -rf inst
+
+$MAKE install-exec
+test -f inst/bin/foo || Exit 99 # Sanity check.
+$MAKE uninstall
+test ! -f inst/bin/foo
+
+$MAKE install-data
+test -f inst/share/bar || Exit 99 # Sanity check.
+$MAKE uninstall
+test ! -f inst/share/bar
+
+rm -rf inst
+
+$MAKE install-exec
+test -f inst/bin/foo || Exit 99 # Sanity check.
+$MAKE uninstall
+test ! -f inst/bin/foo
+
+:
-- 
1.7.2.3