Testing if binary symlink is found with GNU make - makefile

I have a GNU makefile that looks something like this:
SOME_BINARY := $(shell `which some-binary` --help 2> /dev/null)
all: test_if_some_binary_exists
test_if_some_binary_exists:
ifdef SOME_BINARY
#echo "some-binary found!"
else
#$(error 'some-binary' not found!)
endif
Let's say I already have a binary called some-binary, which takes the option --help and writes a help message to standard error:
$ some-binary --help
Some-Binary v1.2.3 (c) 2015 Foo Bar Baz
...
However, it is really a symbolic link to a file located elsewhere on the file system:
$ ls -l `which some-binary`
lrwxr-xr-x 1 alexpreynolds admin 34 Aug 18 15:01 /usr/local/bin/some-binary -> ../Cellar/some-binary/1.2.3/bin/some-binary
When I run make all or make test_if_some_binary_exists, then I get an error as if the binary does not exist (when in fact it does):
$ make test_if_some_binary_exists
makefile:9: *** 'some-binary' not found!. Stop.
How do I correctly test for existence of a binary — potentially a symbolic link — within a GNU makefile?

Related

GNU Make: "dir not expected at this moment"

I have a makefile including the following lines:
buildrepo:
#$(call make-repo)
define make-repo
for dir in $(C_SRCS_DIR); \
do \
mkdir -p $(OBJDIR)/$$dir; \
done
endef
On the line with the commands for dir in $(C_SRCS_DIR); \ I get the following error message:
"dir not expected at this moment"
make: *** [buildrepo] Error 255
I am using GNU make.
Can anybody tell me what is going wrong?
Actually this for ... in ... ; do ... done statement is a Unix command not a GNU make command, therefore I guess you are using a Windows machine (or any other one). You have to find the equivalent for your system.
But GNU make has a foreach function which works like this :
$(foreach dir,$(C_SRCS_DIR),mkdir -p $(OBJDIR)/$(dir);)
Also note that in your very specific case (not related to GNU make but to Windows) you can create all the dirs without a for/foreach loop, just like this :
mkdir -p $(addprefix $(OBJDIR)/,$(C_SRCS_DIR))

Setting PATH within Makefile on Mac OS X (but it works on Linux)

I'm able to set PATH in a Makefile on Linux but not Mac OS X. With OS X, the PATH gets set but doesn't get used. Here's a demonstration:
On CentOS 6 with bash 4.1.2(1)-release and GNU Make 3.81
$ pwd
/tmp/experiment
$ find * -type f -ls
132133 4 -rw-rw-r-- 1 tlim tlim 113 Aug 26 11:01 Makefile
132132 4 -rwxrwxr-x 1 tlim tlim 28 Aug 26 10:59 bin/myprog
$ cat Makefile
export PATH := $(shell /bin/pwd)/bin:$(PATH)
what:
#echo PATH=$$PATH
#echo PATH=$(PATH)
which myprog
myprog
$ cat bin/myprog
#!/bin/bash
echo HERE I AM.
$ make what
PATH=/tmp/experiment/bin:/usr/stack/bin:/usr/lib64/qt-3.3/bin:/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin
PATH=/tmp/experiment/bin:/usr/stack/bin:/usr/lib64/qt-3.3/bin:/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin
which myprog
/tmp/experiment/bin/myprog
myprog
HERE I AM.
Here's the exact same files run on a Mac OS X 10.9.4 with GNU Make 3.81:
$ pwd
/tmp/experiment
$ find * -type f -ls
51838886 8 -rw-rw-r-- 1 tal wheel 113 Aug 26 07:01 Makefile
51838887 8 -rwxrwxr-x 1 tal wheel 28 Aug 26 06:59 bin/myprog
$ cat Makefile
export PATH := $(shell /bin/pwd)/bin:$(PATH)
what:
#echo PATH=$$PATH
#echo PATH=$(PATH)
which myprog
myprog
$ cat bin/myprog
#!/bin/bash
echo HERE I AM.
$ make what
PATH=/tmp/experiment/bin:/Users/tal/bin:/opt/local/sbin:/opt/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/opt/X11/bin
PATH=/tmp/experiment/bin:/Users/tal/bin:/opt/local/sbin:/opt/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/opt/X11/bin
which myprog
/tmp/experiment/bin/myprog
myprog
make: myprog: No such file or directory
make: *** [what] Error 1
As you can see, the PATH is getting set and exported into the environment. Both "echo $PATH" and "which myprog" show that the path is set. However, when running "myprog" the shell can't find it.
Update: If I add these two lines to the what recipe in the Makefile:
#echo SHELL=$$SHELL
#echo SHELL=$(SHELL)
Both Linux and Mac output:
SHELL=/bin/bash
SHELL=/bin/sh
Both have SHELL equal to /bin/bash in the environment before running make. This makes me think that having SHELL set in Makefile is just magic and is one of the many reason that The GNU Make Manual says to always set SHELL.
It turns out the PATH variable is special and isn't exported unless "SHELL" is also set. In other words, if I add this one line to the Makefile it works on both Mac and Linux:
SHELL=/bin/bash
This, of course, makes me wonder why the Mac is different. Hopefully by documenting this here others that see this problem will also benefit. Plus, I'd like to know why the Mac is different, if it really is.

gnu make message file not found

I do not know why I am getting these error messages from the shell or where ever they are coming from?
I've simplified a make file. Here is the make file simple.mk
# simple trial makefile
$(warning Making where CURDIR is $(CURDIR))
$(warning $(shell ls -ld "$(CURDIR)" ) )
$(shell "ls -l $(CURDIR)" )
$(shell "\ls -l /home/me/BitHoist/run/objects" )
I run it.
me $ make -f simple.mk
simple.mk:2: Making where CURDIR is /home/me/BitHoist/source
simple.mk:3: drwxrwxr-x. 2 me me 4096 Apr 27 18:37 /home/me/BitHoist/source
/bin/sh: ls -l /home/me/BitHoist/source: No such file or directory
/bin/sh: \ls -l /home/me/BitHoist/run/objects: No such file or directory
make: *** No targets. Stop.
me $
I figured out one solution, enclose in $(warning )
I do not know why I am getting these error messages?
/bin/sh: ls -l /home/me/BitHoist/source: No such file or directory
/bin/sh: \ls -l /home/me/BitHoist/run/objects: No such file or directory
Seems to be an error message from sh.
Robert
Running $(shell "ls -l $(CURDIR)" ) is like typing "ls -l /home/me/BitHoist/source" to the shell prompt (including quotes).
Try that and you'll see you'll get the same error you get from the makefile. Make passes those quotes along to the shell verbatim, so the shell is trying to run a program literally named ls -l /home/me/BitHoist/source, which is obviously not a real program name. Hence the error.

ln command creates broken links

I'm having trouble using the ln command in a makefile.
This is a part of my makefile for creating a dynamic library:
NAME := Net
DDIR := dynamic_library
DLIB := $(DDIR)/lib$(NAME).so
MAJOR := 1
MINOR := 0
VERSION := $(MAJOR).$(MINOR)
$(DLIB).$(VERSION): $(OBJD)
g++ -shared -Wl,-soname,$(DLIB).$(MAJOR) $^ -o $#
$(DLIB): $(DLIB).$(VERSION)
ln -sf $(DLIB).$(VERSION) $(DLIB)
ln -sf $(DLIB).$(VERSION) $(DLIB).$(MAJOR)
OBJD are my .o files.
I'm trying to create the links next to libNet.so.1.0 which is in dynamic_library/ .
The ln commands create broken links with incomplete target addresses but in the correct destination.
I've tried adding ./ and / before the sources but they don't work.
Any help would be appreciated
EDIT
found the answer through trial and error
apparently we're supposed to add ../ before the sources. I have no idea why though.
if anybody has a better way, please answer.
Some of the variations of ln command are:
ln -s abs_path_to_link_target rel_path_from_current_dir_to_link_source
ln -s rel_path_from_link_src_to_target rel_path_from_current_dir_to_link_source
But the following, which you were trying to use, is not one of them:
ln -s rel_path_from_current_dir_to_link_target ...
Your makefile has another subtle error, namely, the link source, does not depend on the changes to the link target, it only depends on the existence of the link target.
And another problem, is that you have a "side effect", when you are making $(DLIB) target. I am guessing you are a software eng, so you know that side effects are bad for parallelism, cause race conditions, and make code hard to read.
Also, one should always use automatic variables such as $#, and depend everything on the Makefile.
Finally, I am hoping that you know why you are using -f. Some of the responses above, including mine :), do not use it. It is very important in the Makefile context, don't drop it.
Bearing these points in mind, the cleanest and correct way to do this would be:
$(DLIB) $(DLIB).$(MAJOR): Makefile | $(DLIB).$(VERSION)
ln -sf $(abspath $|) $#
g++ -shared -Wl,-soname,$(DLIB).$(MAJOR) $^ -o $#
Isn't it supposed to be -Wl,-soname,$(DLIB).$(VERSION)?
ln -s creates a link at the destination location with a link target of your source path (not the full path the path you give the ln command). If your link destination is a directory below your source directory and you aren't using full paths to the source then you need the ../ so that the link target is ../source.file instead of source.file as that would make the link point to a file in the same directory.
Compare:
$ ln -s bar foo
$ readlink foo
bar
$ readlink -f foo
/tmp/bar
$ ln -s ../bar foo
$ readlink foo
../bar
$ readlink -f foo
/foo
$ ln -s /tmp/bar foo
$ readlink foo
/tmp/bar
$ readlink -f foo
/tmp/bar

How to conditionalize a makefile based upon grep results?

I'm looking for a way to bail out of a makefile if a certain string is not found when checking the version of a tool.
The grep expression I'm looking to match is:
dplus -VV | grep 'build date and time: Nov 1 2009 19:31:28'
which returns a matching line if the proper version of dplus is installed.
How do I work a conditional into my makefile based upon this expression?
Here's another way that works in GNU Make:
DPLUSVERSION = $(shell dplus -VV | grep 'build date and time: Nov 1 2009 19:31:28')
target_of_interest: do_things do_things_that_uses_dplus
do_things:
...
do_things_that_uses_dplus:
ifeq ($(DPLUSVERSION),)
$(error proper version of dplus not installed)
endif
...
This target can be something real, or just a PHONY target on which the real ones depend.
Here is one way:
.PHONY: check_dplus
check_dplus:
dplus -VV | grep -q "build date and time: Nov 1 2009 19:31:28"
If grep finds no match, it should give
make: *** [check_dplus] Error 1
Then have your other targets depend on the check_dplus target.
If this is gnu make, you can do
your-target: $(objects)
ifeq (your-condition)
do-something
else
do-something-else
endif
See here for Makefile contionals
If your make doesn't support conditionals, you can always do
your-target:
dplus -VV | grep -q "build date and time: Nov 1 2009 19:31:28" || $(MAKE) -s another-target; exit 0
do-something
another-target:
do-something-else

Resources