How do I make an automake rule in an .mk file that deletes a directory upon cleaning? - automake

I would like to delete a another directory when I run make clean, but I can't figure out how to code it up in the rules.
I have tried
clean-local:
-rm -f del.dir
but that didn't work.
I also tried
CLEANFILES = del.dir
but that also didn't work.
Any suggestions would be appreciated,

clean-local should work, but the command to erase a directory is rm -rf dir, not rm -f dir.

Related

Different ways to make "make clean" idempotent without suppressing errors or using rm -rf?

I have a makefile project which generates 2 folders a build one and a deps one. I would like to be able to run make clean as many times as I want and the result would be to only delete the folders and the files in them the first time (idempotent make clean essentially).
Any subsequent time it wouldn't throw any errors, it would just do nothing.
Additionally because I accidentally deleted my home folder once by using rm -rf in a makefile, I would like to avoid using that as well.
I have tried various combinations of using rm -r, rm -f, rmdirs and/or adding a prefix (-) which will only suppress the errors.
Additionally I know I can solve this by using something like the following
if [-d "./build"]; then
rm -r ./build
fi
Do you guys have any other solutions?
Not a fan of clean targets, just tell users to delete the build/ folder (I like your hygiene!).
rm -f is the correct solution. -rf with a folder is fine. If you are feeling paranoid, just protect yourself.
.PHONY: clean
clean:
rm -rf $(or ${build-dir},$(error $$build-dir does not exist))
Here, make will expand the recipe before giving it to the shell. If the expansion of $build-dir is empty, make will expand the $(error …) and will stop, issue the error message and an error, and not even get as far as running the rm.
If you really don't want to use -f with rm, then
-rm -r ${build-dir}
will cause make to ignore any error, but is a bit noisy.
rm -r ${build-dir} || :
will attempt to run the rm. If that succeeds then your task is done and make is happy. If the rm fails, the shell will run its built-in : command which simply returns success, and make is again happy.
find paths/ -delete may be useful, although as written it will fail if paths don't exist. There are many options and it depends on exactly what you want to do.
But rm -f is by far the standard for simple removal.
Remember that you can use || [ $$? -eq 1 ] or similar to suppress specific errors - note the doubled $ for make's own quoting.
Another way of achieving idempotency is to rename things to temporary names and later either rename them into place or delete them, but I'm not sure if that's relevant here.
"because I accidentally deleted my home folder once by
using rm -rf, I'd like to avoid it"
Removing rm -rf doesn't actually make much sense, since it does exactly what you want. It's only your fear that you want to find different solution, but that would be dangerous as well :-)
I'd suggest to use rm -rf but very carefully. I was using it for years and nothing bad happened. My example of use in the Makefile:
lang_dir = lang
domain = messages
clean:
rm -rf $(lang_dir) $(domain).pot tmp_err
How could you compromise that? There's no way. Be careful but do not irrationally fear rm -rf just because it is THE INFAMOUS rm -rf :-)
If you are super unsure, you might require a manual confirmation for recursive removal by adding option -I:
rm -rfI target1 target2 ...
From the rm(1) manual page:
-I prompt once before removing more than three files, or when removing recursively.
Less intrusive than -i, while still giving protection against most mistakes
Whatever solution you use, rm -rf, if [ -d ... ]; then rm -r ...; fi, find ... -delete etc. - you'll always have the same fundamental problem: if the thing you're trying to delete contains a variable dereference, and the variable is accidentally empty, you will delete the wrong directory.
So there's rm -I (which might not work on a build server), and also rm --preserve-root, and also safe-rm.
If you're paranoid, you could have a directory called e.g. cleanups which contains links to the directories you want to clean, and have your makefile have:
clean:
rm -rf cleanups/*/*
rmdir $(readlink cleanups/*)
In the rm call above, the first star finds the links, second deletes everything in these directories, NOT including files starting with . (i.e. hidden files).
The rmdir line removes these empty directories, if they are indeed empty, assuming there are no spaces in the link names.

Generic check to avoid dangerous rm's in makefile

I have a Makefile, attached to a project I am semi-maintaining, but did not originally write. This Makefile does, at the moment, have a nasty habit of running "rm *" on a make clean run when certain variables are not defined.
It includes, for example:
rm $(SOMEDIR)/$(SOMEPREFIX)* and rm $(SOMEPREFIX)*
There are several sets of variables like this. Is there a simple way to make sure that we're not ever going to accidentally call something like rm * or rm /*?
I could, of course, check all of the combinations of $SOMEDIR and $SOMEPREFIX individually, but it'd be nice to do something safer that doesn't require each individual variable/command to be checked.
Any ideas that don't involve me rewriting the whole thing?
Very frequently the list of generated files that you want to delete on make clean can be computed from a list of (precious) source files:
ROOTDIR = .
OBJDIR = objdir
$(SRCS) = $(wildcard *.c)
$(OBJS) = $(patsubst %.c,$(ROOTDIR)/$(OBJDIR)/%.o,$(SRCS))
To avoid catastrophic make clean consequences, prefer deleting computed file lists over unpredictable glob patterns:
clean:
rm -f $(OBJS)
is much better than:
clean:
rm -f $(ROOTDIR)/$(OBJDIR)/*
#Renaud Pacalet's is a better solution. If for some reason it doesn't fit your situation, you can give the variables a value if otherwise unset using ?=.
E.g.
ROOTDIR ?= /tmp/nonexistant
OBJDIR ?= NOPE
Or maybe just put an error check at the top to prompt the user to set the variables appropriately. (An empty string in if triggers the else case)
$(if $(ROOTDIR),,$(error Please set ROOTDIR))
$(if $(OBJDIR),,$(error Please set OBJDIR))
Safest solution I've found so far is to avoid rm -r at all costs.
Instead, use rm specifying the files individually, possibly using globs or via find -exec command. Or at least use rm -r but with literal strings as part of the parameters.
Finally, remove parent directories with rmdir.
So in other words there doesn't seem to be a bullet-proof way considering accidental/malicious changes to the Makefile variables.
As an example, instead of doing this:
clean:
rm -f -r $(VENDOR)
Better do this:
clean:
rm -f -r $(VENDOR)/*.com
rm -f -r $(VENDOR)/*.org
rm -f $(VENDOR)/modules.txt
rmdir $(VENDOR) 2> /dev/null || true

Using rm -rf with a directory

I tried to search on SO, but not able to find the difference between the following commands. if I have a directory named dir, how the below commands differ?
rm -rf dir/*
rm -rf dir/
rm -rf dir
Also how do the user permissions on the directory affect the outcome, if the id running the command is not the owner or not even in the group of the owner?
I am adding the command to do rm -rf in a shell script I am working on and need help in understanding the difference between the above commands.
rm -rf dir/*
Removes files within the directory (without removing the directory itself).
Note, hidden files won't be removed.
rm -rf dir/
Trailing slash indicates that dir is a directory. If it was a file, it wouldn't get removed. In your case this is identical to rm -rf dir, but in general it differs (see below)
rm -rf dir
In your case, identical to the one above.
In general, tools such as rm usually follow IEEE/OpenGroup standards when it comes to pathname resolution, which means that dir/ is equivalent to dir/.. One implication of that is that if diris a symlink to a directory rm -rf dir/ will remove the content of the directory (including the hidden files) but not the link or the directory itself, whereas rm -rf dir will only remove the symlink.
You need to have write permissions on a file or directory that you are removing, plus exec permissions on a directory that rm needs to traverse to remove files. You can read more about Unix filesystem permissions here.

Automake: Run a script before traversing subdirectories

I have a build system setup for a library using the GNU autotools. The library has subdirectories for source and header files. It also contains a script in the top directory which auto-generates a source and header file (not the same as the config file) in the appropriate subdirectories. It is necessary that these files are generated before make is performed in the subdirectories.
What is the simplest way to have the script run before subdirectories are traversed (i.e. when user calls make, the script is ran before traversing SUBDIRS)? I have tried adding rules like all-local with no success.
EDIT:
Top-level directory:
configure.ac Makefile.am src include myscript.sh
Makefile.am:
EXTRA_DIST = myscript.sh
ACLOCAL_AMFLAGS = ${ACLOCAL_FLAGS} -I m4
SUBDIRS = src include
.PHONY: gen-script
gen-script:
./myscript.sh
src/Makefile.am:
AM_CPPFLAGS = -I$(top_srcdir)/include
lib_LTLIBRARIES = libmylib.la
libmylib_la_SOURCES = \
file1.cxx \
file2.cxx \
autogen-file.cxx
clean-local:
rm -f autogen-file.cxx
include/Makefile.am:
nobase_include_HEADERS = \
file1.h \
file2.h \
autogen-file.h
clean-local:
rm -f autogen-file.h
I think that the best solution would be to get rid of the recursive make and only have the top-level Makefile.am. Then you'd simply add a rule
include/autogen-file.h src/autogen-file.cxx: myscript.sh
${SHELL} $<
and list include/autogen-file.h in BUILT_SOURCES.
If you want to keep your recursive structure (even if this is considered harmful), you could place the rule to generate the files into the Makefile.ams in the sub-directories.
In src/Makefile.am:
autogen-file.cxx: ../myscript.sh
cd ${top_srcdir} && ${SHELL} myscript.sh
And in include/Makefile.am:
autogen-file.h: ../myscript.sh
cd ${top_srcdir} && ${SHELL} myscript.sh
By the way, how often do you need to re-build those? If the generated files only depend on the project configuration, you could simply run myscript.sh at the end of your configure.ac and delete the generated files only on make distclean.
What you can do is force the current directory to run before the rest of the subdirectories with
SUBDIRS = . src include
More seriously, though, you should not use recursive automake. Particularly if your structure is relatively simple as it seems to be. If you have interests in looking up how to do that you can see my documentation on Autotools Mythbuser.

Clean up unmentioned intermediate files in Make

I have a Makefile that builds a shapefile as an intermediate steps.
.INTERMEDIATE : senate_boundaries.shp
senate_boundaries.shp : Senate\ shape\ files.zip
unzip -j "$<"
A full shapefile comes with more than just a .shp, but also a .prj file, a .dbf file, and a bunch of others. These files are created when "Senate shape files.zip" is unzipped.
These other files are never an explicit target or dependency.
.INTERMEDIATE : senate_boundaries.prj senate_boundaries.dbf
does not seem to do anything.
How can I tell Make to clean up these other files?
You can add something like this to your recipe:
rm -f $(wildcard Senate\shape\*.prj)
But that will only work for that one file and you would have to manually add each extension to get rid of.
so something like this might do the trick:
rm -f $(shell ls Senate\shape\ | grep -v .shp&)
Another option is to unzip into a temp directory and then copy the file you want out and remove the the temp directory.

Resources