The curse of make (svr)

Sounds like it should be a movie title. The curse of the black-hearted and arcane makefiles.
On one of our projects we're constantly adding subdirectories of tests that need to be picked up by the harness. Somebody started to place the names of the subdirectories in a variable in the Makefile, so it looked like:
SUBDIRS=acl mount umount
This is all very fine and well until we start having 50+ directories, and a policy which states that the subdirectories have to be kept in order and that you can't exceed 80 characters per line (queue lots and lots of backslashes and rearranging of lines every time you add a new subdirectory). This is sub-optimal, so I changed the SUBDIRS variable like so:
SUBDIRS :sh =  /bin/ls \*/[Mm]akefile | sed -e '@/[Mm]akefile@@g'
The :sh tells make that the SUBDIRS variable is set by the output from the ls command filtered through the sed command. Short and simple.
Welcome to the world of development. Sometimes we need to EXCLUDE certain subdirectories - the functionality isn't there (it happens), the command line options change (ui reviews cause this a lot).
Fixing this is a bit more difficult. The first try is normally:
This does not work!!!!
ALL_SUBDIRS :sh =  /bin/ls \*/[Mm]akefile | sed -e '@/[Mm]akefile@@g'
EXCLUDE_SUBDIRS = cli

SUBDIRS :sh = \\
        for all in $(ALL_SUBDIRS); do \\
                for excl in $(EXCLUDE_SUBDIRS); do \\
                        if [ "$excl" = "$all" ]; then \\
                                all=""; \\
                        fi; \\
                done; \\
                echo $all; \\
        done
The reason why this does not work is that at the first pass, the variables ALL_SUBIRS and EXCLUDE_SUBDIRS are not set (it's a make thing), so what we need to do is add a level of indirection to the solution.
ALL_SUBDIRS :sh =  /bin/ls \*/[Mm]akefile | sed -e '@/[Mm]akefile@@g'
EXCLUDE_SUBDIRS = cli

FOO = \\
        for all in $(ALL_SUBDIRS); do \\
                for excl in $(EXCLUDE_SUBDIRS); do \\
                        if [ "$excl" = "$all" ]; then \\
                                all=""; \\
                        fi; \\
                done; \\
                echo $all; \\
        done

SUBDIRS = $(FOO:sh)
This works, but it contains a subtle bug! If you don't have any entries in EXCLUDE_SUBDIRS, then the expansion fails with a syntax error: sh: syntax error at line 1: `;' unexpected . This error is caused as the code: for var in <>; do requires a token, so we have to change it once more:
ALL_SUBDIRS :sh =  /bin/ls \*/[Mm]akefile | sed -e '@/[Mm]akefile@@g'
EXCLUDE_SUBDIRS = cli

FOO = \\
        for all in $(ALL_SUBDIRS); do \\
                for excl in $(EXCLUDE_SUBDIRS) WILLNEVERMATCH; do \\
                        if [ "$excl" = "$all" ]; then \\
                                all=""; \\
                        fi; \\
                done; \\
                echo $all; \\
        done

SUBDIRS = $(FOO:sh)
but this means that we need to go through the loop even if EXCLUDE_SUBDIRS is never set, so more changes:
ALL_SUBDIRS :sh =  /bin/ls \*/[Mm]akefile | sed -e '@/[Mm]akefile@@g'
EXCLUDE_SUBDIRS = cli

FOO = \\
        if [ -n "$EXCLUDE_SUBDIRS" ]; then \\
                for all in $(ALL_SUBDIRS); do \\
                        for excl in $(EXCLUDE_SUBDIRS) WILLNEVERMATCH; do \\
                                if [ "$excl" = "$all" ]; then \\
                                        all=""; \\
                                fi; \\
                        done; \\
                        echo $all; \\
                done; \\
        else\\
                echo $(ALL_SUBDIRS); \\
        fi

SUBDIRS = $(FOO:sh)
This has two lessons associated with it.
  • Make is not easy
  • remember to test all the variants of a solution
Comments:

Post a Comment:
Comments are closed for this entry.
About

petesh

Search

Archives
« April 2014
MonTueWedThuFriSatSun
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    
       
Today