Makefile : how to increment a variable when you call it? (var++ in bash)
Asked Answered
S

3

7

Here is part of my makefile :

LISTEINC = $(DEST)/file.inc $(DEST)/otherfile.inc $(DEST)/anotherfile.inc
compteur = 1

$(DEST)/file: $(LISTEINC)
       #action

$(DEST)/%.inc: $(DEST)/%.jpg
      ./script $< $compteur $(DEST) > $@

How to have the variable compteur at 1 for file, 2 for otherfile, 3 for anotherfile?

$((compteur++)) would work in bash script, but here I really don't know what the equivalent is. I tried many combination of $$ () ++ +1 etc... Nothing worked. Anyone could help me please?

Spang answered 7/12, 2015 at 20:31 Comment(4)
I just tried this : ./script $< $(compteur) $(DEST) > $@; compteur=$(shell echo $$(($(compteur)+1)) ). Sadly it doesn't work. The makefile execute ./script $< 1 $(DEST); compteur=2 and then it forgets that compteur was incremented... and for next file it is again 1 and 2. Why it doesn't keep the change of the variable compteur in memory?Spang
Why have the bash tag in here at all? Even when you have a line in a makefile evaluated by a shell, that shell is /bin/sh, not bash.Israelisraeli
...and as for why a change made in the manner proposed in your comment doesn't stick, that's because every time make invokes a shell command, it starts a new shell. Each such shell is a separate, distinct program invocation, so of course they don't share memory.Israelisraeli
...that said, conceptually, this kind of thing isn't a great fit for makefiles. I wouldn't be surprised if any answer you got became very unpredictable when parallel builds were in use, not to mention generating different values between clean and unclear builds.Israelisraeli
L
12

It can be done with eval :

$(eval compteur=$(shell echo $$(($(compteur)+1))))

From the manual :

The eval function is very special: it allows you to define new makefile constructs that are not constant; which are the result of evaluating other variables and functions. The argument to the eval function is expanded, then the results of that expansion are parsed as makefile syntax. The expanded results can define new make variables, targets, implicit or explicit rules, etc

Lecce answered 7/12, 2015 at 23:17 Comment(0)
V
4

Every time a line of receipt for some target should be evaluated, it uses own shell instance. So you just cannot modify shell variable, used for building file $(DEST)/otherfile.inc, while build file $(DEST)/file.inc.

Instead of shell variable compteur, you may use make variable:

$(DEST)/%.inc: $(DEST)/%.jpg
      ./script $< $(compteur) $(DEST) > $@

which has different values for different targets. It can be achieved by using target-specific variable values technique:

$(DEST)/file.inc: compteur = 1
$(DEST)/otherfile.inc: compteur = 2
$(DEST)/another.inc: compteur = 3

If you want to generate these variable-assignment rules, you are free to use any make facilities, working at parsing stage (as opposite to building stage, when make executes receipts for targets). E.g., you may change variable using shell after each variable-assignment rule is generated:

# For target, given by the first parameter, set current *compteur* value.
# After issuing the rule, issue new value for being assigned to *compteur*.
define set_compteur
$(1): compteur = $(compteur) # Variable-assignment rule
compteur = $(shell echo $$(($(compteur)+1))) # Update variable's value
endef

$(foreach t,$(LISTEINC),$(eval $(call set_compteur, $(t))))

Alternatively, you may create list of possible compteur values once, and use this list while generate variable-assignment rules:

# List of possible indicies in the list *LISTEINC*: 1 2 ...
indicies = $(shell seq $(words $(LISTEINC)))
# Corresponded *compteur* values actually are same as indicies.
compteurs = $(indicies)

# For target, specified with index, given by the first parameter, set corresponded *compteur* value.
define set_compteur
$(word $(1),$(LISTEINC)): compteur = $(word $(1),$(compteurs))
endef

$(foreach i,$(indicies),$(eval $(call set_compteur, $(i))))

Unlike to the first snippet, which parsing stage calls shell for every target in the list LISTEINC, the second snippet calls shell only once (seq command), which is faster.

Vargas answered 8/12, 2015 at 12:40 Comment(1)
This is by far the best answer.Chaps
E
1

Not sure if still relevant (the question is old...), but here is how you increment in GNU Make. Notes:

  1. The only operation that is implemented is increment.
  2. Numbers are reverse decimals with spaces between digits, because Make.
  3. Empty string is a valid zero (the number 0, that is, not the digit).
  4. Presumably this is good enough, because people asking for just the increment usually only want uniqueness.
  5. Obviously the choice of base 10 is entirely arbitrary. Tweak the next_* map to taste.
  6. Rendering "numbers" sans spaces or reversing them are left as exercises for the reader. :)
    define inc
      $(if $(1),$(call inc-rec,$(next_$(firstword $(1))),$(wordlist 2,$(words $(1)),$(1))),1)
    endef
    define inc-rec
      $(if $(filter 0,$(1)),0 $(call inc,$(2)),$(1) $(2))
    endef
    next_0 := 1
    next_1 := 2
    next_2 := 3
    next_3 := 4
    next_4 := 5
    next_5 := 6
    next_6 := 7
    next_7 := 8
    next_8 := 9
    next_9 := 0
Eugeniaeugenics answered 27/1, 2017 at 1:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.