Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Check if a program exists from a Makefile

People also ask

How do I know if a file is present in Makefile?

myApp is for myApplication i.e. the filename by the build process. If you just want to avoid make stopping if the file does not exist, rm -rf myApp could be an alternative. Or preceding the command with a dash ( -rm myApp ) to make make ignore the error from rm (it will however print an ugly message).

How do I use IFEQ in Makefile?

The ifeq directive begins the conditional, and specifies the condition. It contains two arguments, separated by a comma and surrounded by parentheses. Variable substitution is performed on both arguments and then they are compared.

How do I run a command in Makefile?

@anon58192932 That specific iteration, to execute a command, is usually done in a shell syntax fragment in a build recipe, so it occurs in the shell, rather than in make: for x in $(FILES); do command $$x; done . Note the doubled up $$ which passes a single $ to the shell.

What is shell in Makefile?

$(shell) is a special function in gmake that runs an external command and captures the output for use in the makefile. For example, you could get the current working directory like this: CWD=$(shell pwd) all: @echo This makefile lives in $(CWD).


Sometimes you need a Makefile to be able to run on different target OS's and you want the build to fail early if a required executable is not in PATH rather than to run for a possibly long time before failing.

The excellent solution provided by engineerchuan requires making a target. However, if you have many executables to test and your Makefile has many independent targets, each of which requires the tests, then each target requires the test target as a dependency. That makes for a lot of extra typing as well as processing time when you make more than one target at a time.

The solution provided by 0xf can test for an executable without making a target. That saves a lot of typing and execution time when there are multiple targets that can be built either separately or together.

My improvement to the latter solution is to use the which executable (where in Windows), rather than to rely on there being a --version option in each executable, directly in the GNU Make ifeq directive, rather than to define a new variable, and to use the GNU Make error function to stop the build if a required executable is not in ${PATH}. For example, to test for the lzop executable:

 ifeq (, $(shell which lzop))
 $(error "No lzop in $(PATH), consider doing apt-get install lzop")
 endif

If you have several executables to check, then you might want to use a foreach function with the which executable:

EXECUTABLES = ls dd dudu lxop
K := $(foreach exec,$(EXECUTABLES),\
        $(if $(shell which $(exec)),some string,$(error "No $(exec) in PATH")))

Note the use of the := assignment operator that is required in order to force immediate evaluation of the RHS expression. If your Makefile changes the PATH, then instead of the last line above you will need:

        $(if $(shell PATH=$(PATH) which $(exec)),some string,$(error "No $(exec) in PATH")))

This should give you output similar to:

ads$ make
Makefile:5: *** "No dudu in PATH.  Stop.

I mixed the solutions from @kenorb and @0xF and got this:

DOT := $(shell command -v dot 2> /dev/null)

all:
ifndef DOT
    $(error "dot is not available please install graphviz")
endif
    dot -Tpdf -o pres.pdf pres.dot 

It works beautifully because "command -v" doesn't print anything if the executable is not available, so the variable DOT never gets defined and you can just check it whenever you want in your code. In this example I'm throwing an error, but you could do something more useful if you wanted.

If the variable is available, "command -v" performs the inexpensive operation of printing the command path, defining the DOT variable.


is this what you did?

check: PYTHON-exists
PYTHON-exists: ; @which python > /dev/null
mytarget: check
.PHONY: check PYTHON-exists

credit to my coworker.


Use the shell function to call your program in a way that it prints something to standard output. For example, pass --version.

GNU Make ignores the exit status of the command passed to shell. To avoid the potential "command not found" message, redirect standard error to /dev/null.

Then you may check the result using ifdef, ifndef, $(if) etc.

YOUR_PROGRAM_VERSION := $(shell your_program --version 2>/dev/null)

all:
ifdef YOUR_PROGRAM_VERSION
    @echo "Found version $(YOUR_PROGRAM_VERSION)"
else
    @echo Not found
endif

As a bonus, the output (such as program version) might be useful in other parts of your Makefile.


Cleaned up some of the existing solutions here...

REQUIRED_BINS := composer npm node php npm-shrinkwrap
$(foreach bin,$(REQUIRED_BINS),\
    $(if $(shell command -v $(bin) 2> /dev/null),$(info Found `$(bin)`),$(error Please install `$(bin)`)))

The $(info ...) you can exclude if you want this to be quieter.

This will fail fast. No target required.


My solution involves a little helper script1 that places a flag file if all required commands exist. This comes with the advantage that the check for the required commands is only done once and not on every make invocation.

check_cmds.sh

#!/bin/bash

NEEDED_COMMANDS="jlex byaccj ant javac"

for cmd in ${NEEDED_COMMANDS} ; do
    if ! command -v ${cmd} &> /dev/null ; then
        echo Please install ${cmd}!
        exit 1
    fi
done

touch .cmd_ok

Makefile

.cmd_ok:
    ./check_cmds.sh

build: .cmd_ok target1 target2

1 More about the command -v technique can be found here.


I am personally defining a require target which runs before all the others. This target simply runs the version commands of all requirements one at a time and prints appropriate error messages if the command is invalid.

all: require validate test etc

require:
    @echo "Checking the programs required for the build are installed..."
    @shellcheck --version >/dev/null 2>&1 || (echo "ERROR: shellcheck is required."; exit 1)
    @derplerp --version >/dev/null 2>&1 || (echo "ERROR: derplerp is required."; exit 1) 

# And the rest of your makefile below.

The output of the below script is

Checking the programs required for the build are installed...
ERROR: derplerp is required.
makefile:X: recipe for target 'prerequisites' failed
make: *** [prerequisites] Error 1