30

In my makefile I have a variable with a list of directories, like this:

DIRS = /usr /usr/share/ /lib

Now, I need to create PATH variable from it, which is basically the same, but uses semicolon as a separator:

PATH = /usr:/usr/share/:/lib

How do I do that? I mean, how do I join elements of DIRS list with semicolons, instead of spaces?

dmckee --- ex-moderator kitten
  • 98,632
  • 24
  • 142
  • 234
Maxim Sloyko
  • 15,176
  • 9
  • 43
  • 49

3 Answers3

41

You can use the $(subst) command, combined with a little trick to get a variable that has a value of a single space:

p = /usr /usr/share /lib
noop=
space = $(noop) $(noop)

all:
        @echo $(subst $(space),:,$(p))
tripleee
  • 175,061
  • 34
  • 275
  • 318
Eric Melski
  • 16,432
  • 3
  • 38
  • 52
  • 3
    You also can use `$(eval)` instead of `$(noop)` there, thus eliminating the need for defining noop. – Ivan Tarasov Jan 19 '11 at 19:08
  • 2
    Seem like a black magic, but described in [the manual](http://www.gnu.org/software/make/manual/make.html#Syntax-of-Functions) – hiroshi Jun 19 '12 at 01:22
  • Actually, changed my mind. I think Dave's $(eval) solution is better. It avoids polluting the variable name space not only with $(noop) but also $(space). – Christopher Smith Sep 13 '13 at 20:25
16

Cleanest Form (that I can find):

classpathify = $(subst $(eval ) ,:,$(wildcard $1))
cp = a b c d/*.jar

target:
    echo $(call classpathify,$(cp))
# prints a:b:c:d/1.jar:d/2.jar

Notes:

  • Turning it into a pseudo-function makes the intention clearer than doing a bunch of arcane string manipulation inline.
  • Note the trailing space after eval: $(eval ). Without this, make interprets $(eval) as a variable rather than calling the eval function. If the trailing space is omitted, you could replace eval with some_undefined_variable and the text replacement will still happen. However, if you run make with the --warn-undefined-variable flag, you'll get a warning that eval or some_undefined_variable is undefined.
  • I included the $(wildcard) function because you almost always use these two together when specifying a classpath
  • Make sure not to put any extra spaces in after the commas or you will get something like "::a:b:c:d:e".
Shammel Lee
  • 4,092
  • 1
  • 17
  • 20
Dave Dopson
  • 41,600
  • 19
  • 95
  • 85
  • One thing I'd add: Now that java supports the "-cp lib/*" syntax, you can avoid doing the $(wildcard $1), which is probably a good thing to do as the tendency for Java projects to use a ton of JAR's can lead to cases where you exceed length limits on certain platforms. – Christopher Smith Sep 13 '13 at 20:35
  • 2
    A more generic form: joinwith = $(subst $(eval) ,$1,$2) $(call joinwith, -o , $(list_of_files_separated_by_spaces)) – Jay M Oct 30 '17 at 13:28
3

You use ':' as separator, so you are on Linux. Consider using bash tool chain to replace continuous spaces by single colon

PATH := $(shell echo $(DIRS) | sed "s/ \+/:/g")
tanghao
  • 71
  • 5