LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (http://www.linuxquestions.org/questions/programming-9/)
-   -   Makefile index of list element (http://www.linuxquestions.org/questions/programming-9/makefile-index-of-list-element-873209/)

shamgar03 04-05-2011 05:50 PM

Makefile index of list element
 
So I have been trying for 8 hours to try to get the index of an element from a list in a Makefile. The problem is that after I get the index using all the methods I have tried, the index cant be used in the "word" function:

for instance:
$(OUTPUT2) : INDEX = $(shell echo $(OUTPUT2) | sed -r -e "s/[ \t]+/\n/g" | grep -n $@ | sed 's/^\([0-9]*\):.*/\1/')

will create a variable INDEX defined specifically for each member of OUTPUT2, so that each output knows its index. Unfortunately, when I pass this $(INDEX) into word, it doesn't work:

$(OUTPUT2) : $(word $(INDEX), $(INPUT1)) $(word $(INDEX), $(INPUT2))
echo $(INAME) $(TMPBASE) $@

and I get the error:
Makefile:16: *** non-numeric first argument to `word' function: 'num'. Stop.

Any ideas out there? I feel like if I could just convert a string to a Makefile acceptable number this would just work....

ntubski 04-08-2011 05:17 PM

The problem is the order of evaluation: the prerequisite is exanded in the first phase so INDEX is not defined, consider the following example:
Code:

~/tmp$ cat Makefile
target : VAR := Makefile
target : $(VAR)
        @echo $@ depends on '"$<"'
~/tmp$ make target
target depends on ""

You'll need to use .SECONDARYEXPANSION to get this to work:
Code:

~/tmp$ cat Makefile
.SECONDEXPANSION:
target : VAR := Makefile
target : $$(VAR)
        @echo $@ depends on '"$<"'
~/tmp$ make target
target depends on "Makefile"

See How make Reads a Makefile and Secondary Expansion.

shamgar03 04-11-2011 05:23 PM

I've posted an example here: http://paste.pocoo.org/show/369981/
but the .SECONDEXPANSION doesn't fix the problem. There is a fundamental problem in the fact that sed doens't provide an integer to make. Hence the continued warning:

Makefile:21: *** non-numeric first argument to `word' function: ''. Stop.

dwhitney67 04-11-2011 07:51 PM

<post removed>

shamgar03 04-11-2011 08:18 PM

The problem is that I have multiple inputs and multiple outputs to the makefile that correspond with each other. I am actually trying to use this for grid jobs, because I got tired of manually writing scripts to check to see prerequisite were up to date. So I might have two sets of 3 inputs, and 2 two sets of 4 outputs:

inputA1 + inputA2 => outputA1 => outputA2 + outputA3
inputB1 + inputB2 => outputB1 => outputB2 + outputB3

but none of this have names that are consistent. So I need to be able to refer to the corresponding members of the input, or intermediate values by index, so I don't have to repeat:

outputA3 outputA2: outputA1
...

outputA1: inputA1 inputA2
...

for each group. ie, I wouldn't have to also write

outputB3 outputB2: outputB1
...

outputB1: inputB1 inputB2
...

again for group B, and of course for group C etc

ntubski 04-11-2011 09:19 PM

Quote:

There is a fundamental problem in the fact that sed doens't provide an integer to make. Hence the continued warning:

Makefile:21: *** non-numeric first argument to `word' function: ''. Stop.
The error message indicates that sed is providing an empty string to make, which is because it's getting an empty string as input, which is because $(INDEX) isn't defined at the time the prerequisite is expanded.

Quote:

Originally Posted by shamgar03 (Post 4321517)
I've posted an example here: http://paste.pocoo.org/show/369981/
but the .SECONDEXPANSION doesn't fix the problem.

It's not enough to just put .SECONDEXPANSION you also have to escape the expression to prevent it from getting evaluated the first time:
Code:

$$(word $$(INDEX), $$(INPUT1)) $$(word $$(INDEX), $$(INPUT2))

shamgar03 04-12-2011 02:10 PM

Thanks that really helped. For some reason I find Makefiles to be extremely confusing.

Code:


#!/usr/bin/make
#space separated inputs and corresponding outputs

TMPDIR = /tmp/maketmp/

.SECONDEXPANSION:
INPUT1 =  test1A.txt test2A.txt
INPUT2 =  test1B.txt test2B.txt
OUTPUT1 = test1A_out.txt test2A_out.txt
OUTPUT2 = test1B_out.txt test2B_out.txt

HASHES = $(shell md5sum $(INPUT1))

all: $$(OUTPUT1) $$(OUTPUT2)

$(OUTPUT1) : INDEX = $(shell echo $(OUTPUT1) | sed -r -e "s/[ \t]+/\n/g" | grep -n $@ | sed 's/^\([0-9]*\):.*/\1/')
$(OUTPUT1) : $$(word $$(INDEX), $$(INPUT1)) $$(word $$(INDEX), $$(INPUT2))
        echo $(word $(INDEX), $(INPUT1)) + $(word $(INDEX), $(INPUT2)) > $@

$(OUTPUT2) : INDEX = $(shell echo $(OUTPUT2) | sed -r -e "s/[ \t]+/\n/g" | grep -n $@ | sed 's/^\([0-9]*\):.*/\1/')
$(OUTPUT2) : $$(word $$(INDEX), $$(INPUT1)) $$(word $$(INDEX), $$(INPUT2))
        echo $(word $(INDEX), $(INPUT1)) - $(word $(INDEX), $(INPUT2)) > $@


zub7 04-29-2013 09:06 AM

There's maybe a shorter and clearly stuff that I made when I read this old post.
I put here the contribution for future readers.
In my example seems that you can use "index" directly in the "word" function. Regards.

Code:

list1='a b c d e f g'
list2='A B C D E F G'

index = $(words $(shell a="$(2)";echo $${a/$(1)*/$(1)} ))
swap  = $(word $(call index,$(1),$(2)),$(3))

all:
        echo f is $(call swap,f,$(list1),$(list2))



All times are GMT -5. The time now is 01:53 AM.