How can I simplify this Makefile to make it less repetitive? - makefile

Make is one of those technologies where I go back and forth between whether or not I understand it.
This is certainly one instance where I know I must be doing something wrong, since Make was developed to make these tasks less repetitive.
all: 24.1 24.2 24.3
24.1:
evm install emacs-24.1-bin || true
emacs --version
emacs --batch -L . -l ert -l test/tests.el -f ert-run-tests-batch-and-exit
24.2:
evm install emacs-24.2-bin || true
emacs --version
emacs --batch -L . -l ert -l test/tests.el -f ert-run-tests-batch-and-exit
24.3:
evm install emacs-24.3-bin || true
emacs --version
emacs --batch -L . -l ert -l test/tests.el -f ert-run-tests-batch-and-exit
How can I edit this Makefile to only lay out the test sequence once but be able to test against multiple versions?

Try this:
VERSIONS = 24.1 24.2 24.3
all :: $(VERSIONS)
$(VERSIONS) ::
evm install emacs-$#-bin || true
emacs --version
emacs --batch -L . -l ert -l test/tests.el -f ert-run-tests-batch-and-exit
The :: is a special kind of rule, that puts the target as phony (and has other properties, too).

How about:
all: 24.1 24.2 24.3
%:
evm install emacs-$#-bin || true
emacs --version
emacs --batch -L . -l ert -l test/tests.el -f ert-run-tests-batch-and-exit

I have to admit that turning to ‘last resort’ strategies always makes me queasy: it feels as if going against the grain of the tool. BSD make on the other hand allows explicit looping constructs, hence getting rid of the repetitive rules is straightforward:
VERSIONS = 24.1 24.2 24.3
all: ${VERSIONS}
.for VERSION in ${VERSIONS}
${VERSION}:
evm install emacs-${VERSION}-bin || true
emacs --version
emacs --batch -L . -l ert -l test/tests.el -f ert-run-tests-batch-and-exit
.endfor
I’m well aware that this solution almost surely won’t help you at all; switching make implementation is almost certainly out of the question. BSD make is sorely underrepresented though, so I thought it might be useful for other people to have an alternative approach documented.
As MadScientist correctly pointed out, GNU make doesn’t support any of the ‘dot constructs’ like .for, which are special to BSD make. However, this question suggests a few other looping techniques that might be applicable to GNU make: How to write loop in a Makefile?

Related

How to execute a make target on file change automatically?

How do I write a make target that will watch for any file changes in specific folders and execute some other make target to compile files? I am looking for a way that can do this with minimal dependency on tools in addition to make itself to keep things simple.
For the watching you can use fswatch. (There's also a go version of this program which may be easier to install: fswatch) For example:
fswatch -ext cpp,c,h make -f Makefile
Anytime you change a cpp, c or h file it will run make again.
Make can be a bit slow for this, so I tend to use ninja instead, but that really depends on the size of your project.
Another option is tup, which has watching built-in:
tup monitor
But, sadly, only for linux.
You can use entr and adjust your Makefile similar to this one
.DEFAULT_GOAL := run
SHELL := /bin/bash
run:
clear && \
cp one.txt two.txt && \
rm -f _* *.l2m *.o2m && \
Ganlib < testgan2.x2m
watch:
while sleep 1 ; do find . -name '*.x2m' -o -name '*.c2m' \
| entr -d make -f ./Makefile ; done
.PHONY: run watch
followed by
$ make watch

Mac OS X /usr/bin/time verbose flag

I have been trying to run the usr/bin/time command in my terminal (Bash) with the verbose flag --verbose or -v but have repeatedly been getting this error:
/usr/bin/time: illegal option -- v
usage: time [-lp] command.
The command I have been running looks like basically like this:
/usr/bin/time -v python practice.py
Any ideas how to get this to work properly on a Mac? (I have OS X Yosemite)?
If you have homebrew, you can get GNU time by installing the gnu-time package:
brew install gnu-time
After that, it’s available as the gtime command:
$ gtime
Usage: gtime [-apvV] [-f format] [-o file] [--append] [--verbose]
[--portability] [--format=format] [--output=file] [--version]
[--help] command [arg...]
The case is similar for a lot of other homebrew-packaged GNU utilities for OSX; e.g., you can get the GNU df command with gdf, du with gdu, readlink with greadlink, etc.
The homebrew package that has most of those is coreutils, which installs about a hundred different GNU-flavored commands. Other useful packages: findutils, gnu-sed, gnu-tar.
If you don’t have homebrew installed yet, you can get it with just a single command:
Command to download and install homebrew
ruby -e "$(curl -fsSL\
https://raw.githubusercontent.com/Homebrew/install/master/install)"
I think looking at the man page the verbose flag is GNU only. Unfortunately, OSX implementation simply differs.

Bash copy verbose update in Solaris

I'm writing some small bash scripts for copiyng certain files/directories in GNU/Linux and Solaris. Everything is OK in Linux, but cp command hasn't the same options in Linux and Solaris.
Copy command is something like this:
cp -ruv $source $dest
Unfortunately I don't know how to achieve copy verbose and copy update in Solaris. Any idea?
Thanks
Unfortunately, cp under Solaris doesn't have that option. man solaris should reveal that.
Are you comfortable making your script depend on rsync?
Or, if possible, you can install the coreutils package and use GNU's cp.
I ran into a similar issue myself and found that gcp takes care of it too. I've made installing coreutils part of my standard system setup.
I run these on a new Solaris install:
pkgadd -d http://get.opencsw.org/now
pkgutil -U
pkgutil -i -y coreutils
pkgutil -a vim
pkgutil -i -y vim
pkgutil -i -y findutils
Remember to add the path - and the documentation path - to your profile, and possibly to the system profile at /etc/profile:
# Set the program path
PATH=$PATH:/usr/sfw/bin:/usr/sfw/sbin:/usr/openwin/bin:/opt/csw/bin:/usr/ccs/bin:/usr/local/bin:/usr/local
export PATH
# Set the documentation path
MANPATH="$MANPATH:/usr/share/man:/opt/sfw/man:/opt/csw/man"
export MANPATH
It sounds like you might be new to Solaris - as I am relatively new. I also do these, which shouldn't affect anything.
I set VIM as the default editor instead of VI - it's compatible, but has more features, including ANSI color, and some terminal emulators will pass your mouse clicks and scrolling through for even more flexibility:
# Set the default editor
EDITOR=vim
export EDITOR
Then if you are still using the default prompt that doesn't say anything, you might want to add some information - this version requires a Bash shell:
# Set the command prompt, which includes the username, host name, and the current path.
PS1='\u#\h:\w>'
export PS1
To recreate verbose mode, you can tee the output to the controlling terminal (/dev/tty) while the stdoout output of tee itself is passed to cp via xargs.
find /some/source/directory -type f | \
tee /dev/tty | xargs -I {} cp {} /copy/to/this-directory/
Replace the find with whatever you like, so long as it passes the paths to the files to be copied through the pipe to tee.
Tested on a standard Solaris 10 system without extra GNU utils.

Auto-install packages from inside makefile

Goal: when the user types 'make packages', automatically search for the package libx11-dev (required for my program to compile) and, if not found, install it. Here's a stripped-down version of my makefile:
PACKAGES = $(shell if [ -z $(dpkg -l | grep libx11-dev) ]; then sudo apt-get install libx11-dev; fi)
[other definitions and targets]
packages: $(PACKAGES)
When I type 'make packages', I'm prompted for the super-user password. If entered correctly, it then hangs indefinitely.
Is what I'm trying to do even possible from within the makefile? If so, how?
Thanks so much.
The problem is that the shell function acts like backticks in the shell: it takes the output to stdout and returns it as the value of the function. So, apt-get is not hanging, it's waiting for you to enter a response to some question. But you cannot see the question because make has taken the output.
The way you're doing this is not going to work. Why are you using shell instead of just writing it as a rule?
packages:
[ -z `dpkg -l | grep libx11-dev` ] && sudo apt-get install libx11-dev
.PHONY: packages
I figured out a better way, which avoids the problem of having unexpected arguments to the if statement:
if ! dpkg -l | grep libx11-dev -c >>/dev/null; then sudo apt-get install libx11-dev; fi
The -c flag on grep makes it return the number of lines in dpkg -l which contain the string libx11-dev, which will either be 0 (if uninstalled) or 1 (if installed), allowing
dpkg -l | grep libx11-dev -c
to be treated like an ordinary boolean variable.

Installing readline 6.0 on OS X

I'm trying to install readline 6 from source but run into an error during 'make install'.
Here is the end of the output after executing 'sudo make install'
( cd shlib ; make DESTDIR= install )
/bin/sh ../support/mkdirs /usr/local/lib
/bin/sh ../support/shlib-install -O darwin9.7.0 -d /usr/local/lib -b /usr/local/bin -i "/usr/bin/install -c -m 644" libhistory.6.0.dylib
/bin/sh ../support/shlib-install -O darwin9.7.0 -d /usr/local/lib -b /usr/local/bin -i "/usr/bin/install -c -m 644" libreadline.6.0.dylib
install: you may need to run ldconfig
I know that ldconfig isn't installed by default on OS X, and I read somewhere that it shouldn't be needed to fix this issue. I believe it has something to do with dynamic libraries, but I haven't been able to find out how to fix the issue, anyone have any insight?
FYI, I'm running OS X on an intel 2.4ghz macbook
thanks
P.S. I also applied the 3 available readline 6 patches before running configure and make
Actually, this isn't an error at all... it's just a notice message at the end of the install. It get this too, and my readline 6 is happily installed.
If you check /usr/local/lib and see readline there, you're done :-) No need to run any equivalent of ldconfig.
$ ls /usr/local/lib | grep readline
libreadline.6.0.dylib
libreadline.6.dylib
libreadline.a
libreadline.dylib

Resources