makefile variables within variables executing bash scripts

676 views Asked by At

I have a makefile that I want to run against both linux (debian/Ubuntu) and Windows/Cygwin.

I need to do something slightly different according to the running environment and I came out with the following piece of code which is supposed to store in the variable a string which is foo if the OS is linux, otherwise foo.exe:

EXECUTABLE_FILENAME=bash -c 'os_type=`uname`; if [ $os_type == "Linux" ]; then echo "foo"; else echo "foo.exe"; fi'

then I would like to use that variable in another one, something like:

GODEPBUILD=$(godep go build -o $(EXECUTABLE_FILENAME))

the previous one with/without the evaluation via $(...some stuff...) (see below for all the tests I made to make this work)

finally I would like to "run" that command somewhere else in a task within the makefile, something like:

build-all:
   @echo -e "build my project"
   $(GODEPBUILD)

I have tried many different combinations

  • with/without $(some stuff here),
  • with/without single quotes ',
  • with/without the bash "`"
  • with/without the wrap bash -c '...some commands here...'

but I get all sorts of bash errors e.g.:

  • invalid option -- 'c'
  • the go compiler complaining EXECUTABLE_FILENAME is empty with this message: flag needs an argument: -o
  • silent ignore of the $(GODEPBUILD) (I believe because somehow it evaluates to an empty command)

I am doing this chain of steps because I need to wrap many commands of the go toolchain into another tool called godep, also I need to combine this with some specific features of the Operating System (e.g. the filename thing I am stuck with). I have defined many different "commands" of the go toolchain at the beginning of the makefile e.g.:

GODEP_VERSION=godep version
GODEPRESTORE=godep restore
GODEPTEST=godep go test -v
GOCLEAN=go clean

I call these "commands" inside the makefile tasks like $(GODEP_VERSION) (or -$(GODEP_VERSION) if I need to ignore the stderr) and for me it was nice to add the "build command" in a similar fashion.

The make version is:

  • 4.1 on Ubuntu
  • 4.2.1 on Cygwin

Is this thing possible at all?

How could I achieve this combination of different "small scripts" to build the final command that make should run inside a task?

1

There are 1 answers

1
Jonathan Wakely On BEST ANSWER

Surely you want the output of the bash command as the filename, right? You are using the bash command itself as the filename. i.e. $(EXECUTABLE_FILENAME) is a string consisting of several words. When you pass that to the godep command it gets passed as multiple arguments:

godep go build -o $(EXECUTABLE_FILENAME)

becomes:

godep go build -o bash -c 'os_type=`uname`; if [ s_type == "Linux" ]; then echo "foo"; else echo "foo.exe"; fi'

which is complete nonsense (note that $o gets expanded as a make variable, but that's the least of your problems).

Surely you want to run the bash command to decide the filename. You can run a shell command with make's $(shell ...) function:

EXECUTABLE_FILENAME = $(shell os_type=`uname` ; if [ $$os_type == Linux ]; then echo foo ; else echo foo.exe ; fi)

Note I used $$os_type to refer to a shell variable, so make doesn't try to expand $os_type before invoking the shell.

But I would simplify that to something like:

EXECUTABLE_FILENAME = foo$(shell [ `uname` == Linux ] || echo .exe)

Or maybe:

EXECUTABLE_SUFFIX = $(shell [ `uname` == Linux ] || echo .exe)
EXECUTABLE_FILENAME = foo$(EXECUTABLE_SUFFIX)

Now the make variable $(EXECUTABLE_FILENAME) will expand to the filename.

The next problem is here:

GODEPBUILD=$(godep go build -o $(EXECUTABLE_FILENAME))

What is this supposed to mean? Why have you enclosed it in $(...)? That tries to run a make function called godep, surely you just want:

GODEPBUILD = godep go build -o $(EXECUTABLE_FILENAME)

Then when you use it in the recipe it will expand to either:

build-all:
   @echo -e "build my project"
   godep go build -o foo

Or:

build-all:
   @echo -e "build my project"
   godep go build -o foo.exe

N.B. as an optional (but recommended) improvement you should use := to set make variables, because otherwise they get expanded every time they are used. This would mean that every time you refer to a variable that uses $(EXECUTABLE_SUFFIX) the shell command is run again. Changing it to use := will mean it gets evaluated once:

EXECUTABLE_SUFFIX := $(shell [ `uname` == Linux ] || echo .exe)

Similarly, you can use := for the other variables too:

EXECUTABLE_FILENAME := foo$(EXECUTABLE_SUFFIX)
GODEPBUILD := godep go build -o $(EXECUTABLE_FILENAME)

(These ones don't really matter, as long as EXECUTABLE_SUFFIX used := and so already contains the filename).