Makefile - fail if directory doesn't exist - cross platform - makefile

I'm writing the installation part of a Makefile and I'd like to check if the project was built, and if not installation should fail. For that purpose I'd like to check if certain directories exist and if not - fail with an appropriate error (something like "please run make all first").
I also want this Makefile to work on Linux, Windows (mingw) and MacOS.
Can anyone help me with this?

If you are using GNU make or a version of make that supports order-only prerequisites, there is a natural solution. Declare these directories as order-only prerequisites of your install target:
install: | $(DIRS)
<install recipe>
$(DIRS):
#echo "please run make all first" && exit 1

If you're using the mingw make, then the answer sholud be the same for all platforms:
dirs-exist:
test -d dir1 || echo Run make install first && exit 2
test -d dir2
In For the first directory we use the or operator to print a message if the test fails. However, even though we successfully printed the message, we want the entire line to fail, so we exit with an error code of 2.
The above logic could be applied to each directory if you want to have the message in all cases. If you have a lot of directories, a loop like the following might work
test_dirs:
for d in dir1 dir2; do if test \! -d $$d; then echo run make install first; exit 2; fi
The only complexity would be if you needed this to work with nmake and cmd on windows; sounds like that's not an issue here.

#SamHarman's answer is correct (and he posted earlier), so you should accept his answer as soon as he fixes his first example. In any case, I thought I'd post this as well, which is a slightly shorter and slightly more confusing version of the same thing... :-)
DIRS=dir1 dir2 dir3
checkdirs:
[[ 1 ${DIRS:%= && -d %} ]] || ( echo "uh oh..." && false );
.PHONY: checkdirs

Related

Is it possible to run a command when the makefile finishes without an external script?

I would like to run #echo "Make complete." When the makefile finishes running. The problem is I can't figure out a way for it to do that without putting it at the end of of every option, but I wouldn't like to do that since for example the all option would echo "Make complete." multiple times. I also know I could run a script such as:
make $1
echo "Make complete."
But that solution is messy because it uses 2 files and it wouldn't work if the user didn't type an argument.
I am using GNU Make.
You can have a wrapper makefile calling the real makefile:
$(MAKECMDGOALS):
$(MAKE) -f makefile.real $#
#echo "Make complete."
There are caveats and some extra things that need doing to handle multiple targets and passing on the environment and other options, which I'm sure the SO community will add as comments to this answer.

How to create a for loop over files in Makefile that works in Linux and Windows?

What I have
I have a Makefile that loops through subdirectories and runs a command.
I'm trying to make this work both in Linux and Windows, but getting a CreateProcess(NULL, ifdef OS, ...) failed message when trying to run it.
something:
for fn in ./{*/some.file}; do
cd $${name} && echo $${name};
done
What I've tried
something:
ifdef OS
for fn in $(subdirs); do \
cd $$fn && #echo $$fn;
done
else
for fn in ./{*/some.file}; do
cd $${name} && echo $${name};
done
endif
This doesn't satisfy the requirement of finding all instances of some.file though.
What I'm trying to do
Loop over all subdirectories that have file some.file
Change directory and run a command
Any ideas how this can be accomplished?
What I'm trying to accomplish here is some dev automation for serverless. The subdirectories contain code in several programming languages and logic is ran specific to the setup. For example some.file may refer to package.json and run an npm command, or maybe it's python, or go, or rust, etc. This would give me flexibility to work with multiple languages on multiple OS and automate specific repetitive tasks like installation of packages, building, testing, etc.
The best/most portable way to handle this is not to use loops at all. Let make handle it for you. For example you can write a make rule that handles one subdirectory, then create a rule with prerequisites for each directory you want to work with:
SUBDIRS = this that other
.PHONY: $(SUBDIRS)
something: $(SUBDIRS)
$(SUBDIRS): %:
cd $# && echo $#

Two lines of shell script

I know nothing about shell scripting but something has come up and I need to be able to understand what two lines of code do so that I can modify a project I am working on
SDKROOT= make -C $TEMP_DIR -f $PROJECT_DIR/greg/Makefile VPATH=$PROJECT_DIR/greg || exit $?
$TEMP_DIR/greg -o $DERIVED_FILES_DIR/${INPUT_FILE_BASE}.m $INPUT_FILE_PATH
will you please explain what these two lines of code do... I know what the variables are and the path names but the rest of the syntax is confusing and foreign. Please help.
The first line:
SDKROOT= make -C $TEMP_DIR -f $PROJECT_DIR/greg/Makefile VPATH=$PROJECT_DIR/greg || exit $?
SDKROOT= sets the environment variable SDKROOT to nothing for the execution of the make command.
make is the build tool, and it's being run with the following options:
-C $TEMP_DIR: means run make in the directory $TEMP_DIR
-f $PROJECT_DIR/greg/Makefile specifies to make to use the Makefile in $PROJECT_DIR/greg
VPATH=$PROJECT_DIR/greg sets another variable, VPATH to $PROJECT_DIR/greg. VPATH specifies to make a search path for prerequisits.
|| exit $? means that if the make command fails the script should exit with the same error code as make, as $? means the return code of the last run program/command.
The second line:
$TEMP_DIR/greg -o $DERIVED_FILES_DIR/${INPUT_FILE_BASE}.m $INPUT_FILE_PATH
appears to be running the command $TEMP_DIR/greg with the option -o $DERIVED_FILES_DIR/${INPUT_FILE_BASE}.m and with some input from $INPUT_FILE_PATH. This looks like the program which may have been built from the previous line's make command, so it's hard to know exactly what it does.
EDIT
The SDKROOT is an environment variable used by XCode to say where the SDK it's using is installed. It will be a path like /Developer/SDKs/MacOSX"${HOST_VERSION}".sdk/ for instance. The value should be setup somewhere in XCode I imagine (I don't used xcode so can't be more helpful than that.). By doing SDKROOT= at the beginning of the command the value of SDKROOT will be nothing/blank. The reason for this is that the code being compiled will use resources which exist in the SDKROOT, rather than local ones; such resources may be classes, config or libraries for example.

Is there a smarter alternative to "watch make"?

I ran into this useful tip that if you're working on files a lot and you want them to build automatically you run:
watch make
And it re-runs make every couple seconds and things get built.
However ... it seems to swallow all the output all the time. I think it could be smarter - perhaps show a stream of output but suppress Nothing to be done for 'all' so that if nothing is built the output doesn't scroll.
A few shell script approaches come to mind using a loop and grep ... but perhaps something more elegant is out there? Has anyone seen something?
Using classic gnu make and inotifywait, without interval-based polling:
watch:
while true; do \
$(MAKE) $(WATCHMAKE); \
inotifywait -qre close_write .; \
done
This way make is triggered on every file write in the current directory tree. You can specify the target by running
make watch WATCHMAKE=foo
This one-liner should do it:
while true; do make --silent; sleep 1; done
It'll run make once every second, and it will only print output when it actually does something.
Here is a one-liner:
while true; do make -q || make; sleep 0.5; done
Using make -q || make instead of just make will only run the build if there is something to be done and will not output any messages otherwise.
You can add this as a rule to your project's Makefile:
watch:
while true; do $(MAKE) -q || $(MAKE); sleep 0.5; done
And then use make watch to invoke it.
This technique will prevent Make from filling a terminal with "make: Nothing to be done for TARGET" messages.
It also does not retain a bunch of open file descriptors like some file-watcher solutions, which can lead to ulimit errors.
How about
# In the makefile:
.PHONY: continuously
continuously:
while true; do make 1>/dev/null; sleep 3; done
?
This way you can run
make continuously
and only get output if something is wrong.
Twitter Bootstrap uses the watchr ruby gem for this.
https://github.com/twbs/bootstrap/blob/v2.3.2/Makefile
https://github.com/mynyml/watchr
Edit:
After two years the watchr project seems not to be maintained anymore. Please look for another solution among the answers. Personally, if the goal is only to have a better output, i would recommend the answer from wch here
I do it this way in my Makefile:
watch:
(while true; do make build.log; sleep 1; done) | grep -v 'make\[1\]'
build.log: ./src/*
thecompiler | tee build.log
So, it will only build when my source code is newer than my build.log, and the "grep -v" stuff removes some unnecessary make output.
This shell script uses make itself to detect changes with the -q flag, and then does a full rebuild if and only if there are changes.
#!/bin/sh
while true;
do
if ! make -q "$#";
then
echo "#-> Starting build: `date`"
make "$#";
echo "#-> Build complete."
fi
sleep 0.5;
done
It does not have any dependencies apart from make.
You can pass normal make arguments (such as -C mydir) to it as they are passed on to the make command.
As requested in the question it is silent if there is nothing to build but does not swallow output when there is.
You can keep this script handy as e.g. ~/bin/watch-make to use across multiple projects.
There are several automatic build systems that do this and more - basically when you check a change into version control they will make/build - look for Continuous Integration
Simple ones are TeamCity and Hudson
#Dobes Vandermeer -- I have a script named "mkall" that runs make in every subdirectory. I could assign that script as a cron job to run every five minutes, or one minute, or thirty seconds. Then, to see the output, I'd redirect gcc results (in each individual makefile) to a log in each subdirectory.
Could something like that work for you?
It could be pretty elaborate so as to avoid makes that do nothing. For example, the script could save the modify time of each source file and do the make when that guy changes.
You could try using something like inotify-tools. It will let you watch a directory and run a command when a file is changed or saved or any of the other events that inotify can watch for. A simple script that does a watch for save and kicks off a make when a file is saved would probably be useful.
You could change your make file to output a growl (OS X) or notify-send (Linux) notification. For me in Ubuntu, that would show a notification bubble in the upper-right corner of my screen.
Then you'd only notice the build when it fails.
You'd probably want to set watch to only cycle as fast as those notifications can display (so they don't pile up).
Bit of archaeology, but I still find this question useful. Here is a modified version of #otto's answer, using fswatch (for the mac):
TARGET ?= foo
all:
#fswatch -1 . | read i && make $(TARGET)
#make -ski TARGET=$(TARGET)
%: %.go
#go build $<
#./$#

How do I setup automake and autoconf to conditionally build programs (tests or otherwise)

I currently have 10 tests in my autotoolset project. Any time I make a change to one of my src/ files and recompile, each test is rebuilt and linked. This is starting to have a considerable impact on my development time.
What is the best way to conditionally build binary programs, tests or otherwise, with GNU autotoolset? For instance, if I'm working in test/check_curl_requestheaders.cc, and I make a change, I am only going to want to recompile the library and then that one test and none of the other binaries.
I saw some mention of using automake conditionals (like WANTS_XXX) but I am not 100% certain that is what I'm looking for nor am I sure how that would be configured by autoconf.
I am sort of hoping for something that will end up looking like this:
./configure
make test/check_curl_requestheaders
or
./configure --only-build=test/check_curl_requestheaders
make
Pointers?
EDIT I'm not doing a configure before every make. If I make changes to check_curl_requestheaders, only check_curl_requestheaders is rebuilt as one would expect. The problem is that if I'm working on the RequestHeaders part of the library, and make a change to say, src/curl/requestheaders.cc, all of the the tests and other binaries are rebuilt, not just the check_curl_requestheaders. That is taking far too long, and that is what I am trying to avoid. If I have a dozen binaries, is there a way to rebuild only one of them?
I'm confused. In any project I've ever worked on, running 'make' from either ${top_builddir} or from ${top_builddir}/tests/ will not rebuild or run any tests. Tests are only built and executed for 'make check'. Are you using check_PROGRAMS in your Makefile.am?
In general, conditional compilation is handled with automake conditionals and Makefile.am snippets like:
if WANT_FOO
bin_PROGRAMS += foo
endif
but I'm certain this is not what you are looking for. It sounds like you have specified bogus dependencies in a Makefile.am, and you should post a minimal version of it/them.
PS: in your shell script, you can just do
export OUT
...
(cd src && make >> $OUT) || exit 3
When you change some source file, you should not have to reconfigure at all. Just run make again, and it should only rebuild those binaries that are actually affected by the change. So when you change test/check_curl_requestheaders, then do a plain make, then only test/check_curl_requestheaders should be rebuilt, anyway. If anything else gets rebuilt also, you have a bug in your makefile.
Of course, if you do configure first (which you should not), it is not surprising that more stuff gets rebuilt.
Edit: If you change the library, and then only want to rebuild a single test, then
make test/check_curl_requestheaders
should be sufficient. This would require you to have a target named test/check_curl_requestheaders in your toplevel makefile. That target may look like
test/%: library
make -C test $*
assuming you have a separate makefile in the test directory, and assuming that this makefile assumes that the library has already been built.
I'm not sure this is the best way to do this, but it turns out that the programs in my test folder did have their own unique make targets. However, there were some issues.
If I issue make at the top level, all of src/ and test/ are built
If I issue make at test/ level, changes to src/ won't be picked up
To solve this, I wrote a shell script that does the following:
Enter src, and build it. (if changes to src/ have happened, src/ is rebuilt)
Enter test, and build a specific binary. (this will rebuild the specific binary if it has changed and will relink of the code in src/ has been updated by the previous step)
The code is listed below:
#!/bin/sh
TYPE="$1"
WHICH="$2"
OUT="`readlink -f ./buildandrun.out`"
rm -rf $OUT
if test ! -n "$WHICH"
then
echo "Please specify which type to build"
exit 1
fi
if test ! -n "$WHICH"
then
echo "Please specify which $TYPE to build"
exit 2
fi
RV=0
echo "" >> $OUT
echo "Building src" >> $OUT
echo "" >> $OUT
cd src
make >> $OUT || RV=3
cd ..
if test $RV != 0; then exit $RV; fi
echo "" >> $OUT
echo "Building $TYPE/$WHICH" >> $OUT
echo "" >> $OUT
cd $TYPE
make "$WHICH" >> $OUT || RV=4
cd ..
if test $RV != 0; then exit $RV; fi
echo "" >> $OUT
echo "Running $TYPE/$WHICH" >> $OUT
echo "" >> $OUT
$TYPE/$WHICH || RV=5
exit $RV
This lets me do the following:
./buildandrun.sh test check_curl_requestheaders
Hopefully there will eventually be someone who can show me a more elegant solution to this problem, preferably using autoconf and automake. I have a feeling that this is probably something that these tools do out of the box and I just haven't discovered it yet.

Resources