27

Given a colon-delimited list of paths, getting a space-delimited list with GNU Make is straightforward:

CPATHS := /usr/bin/foo:/usr/bin/baz:/usr/bin/baz
SPATHS := $(subst :, ,$(CPATHS))

However, I couldn't find a nice way to go the opposite direction. The following hack does work (at least if sed is installed) but I'm pretty sure there will be a nicer way to solve this just using Make's internal functions.

SPATHS := /usr/bin/foo /usr/bin/baz /usr/bin/baz
CPATHS := $(shell echo $(SPATHS) > tmp; sed 's/ \+/:/g' tmp; rm tmp)
Daniel Widdis
  • 8,424
  • 13
  • 41
  • 63
5gon12eder
  • 24,280
  • 5
  • 45
  • 92

2 Answers2

32

The only tricky part here is to define a literal space:

space := $(subst ,, )

SPATHS := /usr/bin/foo /usr/bin/baz /usr/bin/baz
CPATHS := $(subst $(space),:,$(SPATHS))
Eldar Abusalimov
  • 24,387
  • 4
  • 67
  • 71
  • 10
    I prefer to use `empty :=` then `space := $(empty) $(empty)`. Just a bit more clear and doesn't rely on a side-effect. But both will work. – MadScientist May 13 '12 at 14:28
  • 1
    Cool, thanks. I think I will use `SPACE := $(eval) $(eval)` to get a space token. I was thinking that `subst` operates on the words individually and therefore doesn't see the spaces between them. – 5gon12eder May 13 '12 at 14:47
  • 6
    Another option is to use `space := $(subst ,, )`. – Eldar Abusalimov Feb 15 '13 at 11:28
  • 1
    The best answer is: SPACE := $() $() because it works on every version of make and takes only one line :). Your answer works well on make 4.2.x, but it doesn't work on make 4.3: * WARNING: Backward-incompatibility! Previously appending using '+=' to an empty variable would result in a value starting with a space. Now the initial space is only added if the variable already contains some value. Similarly, appending an empty string does not add a trailing space. – jakson May 07 '20 at 15:07
  • @jakson thank you, I didn't know that. I updated the answer to use `$(subst ,, )`. While your snippet works too, I suspect it can cause warnings when `make` is launched with `--warn-undefined-variables`. – Eldar Abusalimov May 08 '20 at 17:20
  • @EldarAbusalimov, you right about warnings, so the only one-line answer is yours (subst-way). PS MadScientist answer works without warnings (define empty). – jakson May 09 '20 at 15:15
21

The shortest way to get a literal space would be via $() $(). Thus:

$(subst $() $(),:,$(CPATHS))

Or, for brevity:

_=$() $()
$(subst $(_),:,$(CPATHS))

It is perhaps an interesting curiosity that the same trick works with cmake's macros, i.e. that ${} is a separator but introduces no whitespace by itself.

Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • 1
    You don't need the second `$()`. `$(subst $() ,:,$(CPATHS))` works. The `subst` function doesn't trim whitespace from its arguments, so all you really need to do is to force `make` to stop eating whitespace after it scans the function name. – Matt Whitlock Nov 22 '22 at 05:17
  • @MattWhitlock Good to know! That probably goes for Cmake too - I have to check! – Kuba hasn't forgotten Monica Jan 28 '23 at 11:55