






                   Probleme durch rekursives Make


             [4mPeter[24m [4mMiller[24m           Aus dem Amerikanischen
         millerp@canb.auug.org.au   ins  Deutsche  uber-
                                         setzt von
                                       [4mUlrike[24m [4mAmoore[0m
                                    uamoore@cmmagazin.de


                              [1mVorwort[0m
         Zur Produktion groer UNIX-Projekte  verwendet  man
         traditionellerweise  rekursives  Make. Bei manchen
         Projekten fuhrt das  zu  sehr  langen  Produktion-
         szeiten, was insbesondere, wenn man nur eine Datei
         andern  mochte,  unvertretbar  ist.  Als  wir  das
         Phanomen  naher untersuchten, stellte sich heraus,
         dass eine Reihe  von  Problemen,  die  voneinander
         unabhangig  zu  sein  schienen,  gemeinsam fur die
         Verzogerung  verantwortlich  waren.  Eine   genaue
         Analyse zeigte jedoch eine gemeinsame Ursache.
         Dieser  Artikel  beschaftigt  sich mit einigen der
         Probleme, die im Zusammenhang  mit  der  Anwendung
         von  rekursivem Make regelmaig auftreten, und legt
         dar, dass sie  alle  Symptome  desselben  Problems
         sind. Diese Symptome haben die UNIX-Anwender lange
         als unumgangliche Tatsachen hingenommen, aber  sie
         mussen  nicht langer geduldet werden. Dazu gehort,
         dass ein rekursives Make oft sehr  lange  braucht,
         um  herauszufinden, dass es nichts zu tun braucht,
         dass es zu viel oder zu wenig  tut  oder  dass  es
         ubermaig  empfindlich  auf Veranderungen von Quel-
         lkode reagiert und fur  eine  ungestorte  Funktion
         den  standigen  Eingriff in das Makefile notwendig
         macht.
         Man kann die Losung  fur  diese  Probleme  finden,
         indem   man  sein  Augenmerk  erst  einmal  darauf
         richtet, wie Make grundsatzlich arbeitet, und dann
         die   Auswirkungen   analysiert,   die  durch  das
         Hinzufugen von rekursivem Make, hervorgerufen wer-
         den.  Die  Analyse zeigt, dass das Problem von der
         kunstlichen Einteilung des  Builds  in  gesonderte
         Teilmengen  herruhrt.  Das  wiederum  fuhrt zu den
         beschriebenen Symptomen. Um die Symptome  zu  ver-
         meiden,  ist  es  lediglich  notwendig  diese Ein-
         teilung zu verhindern, d. h. einen einzigen  Make-
         Durchgang zu veranlassen, was nicht bedeutet, dass
         es nur ein einziges Makefile gibt.
         Diese   Schlussfolgerung    widerspricht    vielen
         bezuglich  der  Produktion groer Projekte angesam-
         melten Volksweisheiten. Einige der von  Vertretern
         dieser Volksweisheiten vorgebrachten Einwande wer-
         den  hier  untersucht  und   erweisen   sich   als



    Peter Miller          21 December 2003                Page 1





    CM Magazin, Dec-2002          Probleme durch rekursives Make


         unbegrundet.   Die   praktische  Umsetzung  dieser
         Schlussfolgerung fuhrt  zu  wesentlich  ermutigen-
         deren  Ergebnissen.  Wird  diese  Methode  laufend
         weiterentwickelt,   werden   Verbesserungen    der
         Effizienz  erheblich schneller als erwartet sicht-
         bar, ohne dass die Modularitat  aufgegeben  werden
         muss. Die Durchfuhrung eines Ganzprojekt-Makes ist
         nicht so schwierig  umzusetzen,  wie  es  zunachst
         erscheint.




    [1m1.  Einfuhrung[0m

    Die traditionellen Produktionsmethoden fur groe UNIX Softwa-
    reentwicklungsprojekte  sind  als  [4mrekursives[24m  [4mMake[24m  bekannt
    geworden. Der Name verweist auf die Verwendung einer Hierar-
    chie von Verzeichnissen, die die Quelldateien fur  die  Mod-
    ule,  aus denen das Projekt besteht, beinhalten, wobei jedes
    der Unterverzeichnisse ein Makefile enthalt, das die  Regeln
    und   Anweisungen  fur  das  Make-Programm  beschreibt.  Das
    vollstandige Projekt-Build wird durchgefuhrt, indem man  das
    Haupt-Makefile   veranlasst,  in  allen  Unterverzeichnissen
    wiederum Make aufzurufen.

    Dieser Artikel untersucht ein paar wesentliche Probleme, auf
    die  man stot, wenn man bei der Entwicklung von Softwarepro-
    jekten mit dem rekursiven Make arbeitet. Auerdem  wird  eine
    einfache  Losung  aufgezeigt  und  einige ihrer Auswirkungen
    werden untersucht.

    Durch Rekursives Make erhalt man einen Verzeichnisbaum,  der
    ungefahr so aussieht:
                          +++
                          ++-[4mP[24m+[4mr[24m+[4moject[0m
                           ++++Mmaokdeufliel1e
                           |++-++Makefile
                           | +-++source1.c
                           | +-++[4metc...[0m
                           ++++m+o+dule2
                             +-++Makefile
                             +-++source2.c
                             +-++[4metc...[0m
                                |
    Diese  verschachtelte  Modulhierarchie  kann  beliebig  aus-
    geweitet werden. Reale Projekte haben  oft  zwei  oder  drei
    Ebenen.


    -----------
    Copyright   (C)  1997  Peter  Miller;  All  rights
    reserved.
    German translation Copyright (C) 2002  CM-Magazin.



    Ulrike Amoore         21 December 2003                Page 2





    CM Magazin, Dec-2002          Probleme durch rekursives Make


    [1m1.1.  Kenntnisse vorausgesetzt[0m

    Dieser  Artikel  setzt voraus, dass Sie mit Softwareentwick-
    lung auf Unix, dem Make-Programm, den Grundsatzen von C-Pro-
    grammierung und mit Dateiabhangigkeiten vertraut ist.

    Weiterhin  geht  er  davon  aus, dass Sie GNU-Make auf ihrem
    System installiert haben  und  seine  Funktionen  mehr  oder
    weniger  gut  kennen.  Falls Sie eine eingeschrankte Version
    verwenden,  kann  es  sein,  dass  Ihnen  einige  der  unten
    beschriebenen Funktionen nicht zur Verfugung stehen.

    [1m2.  Das Problem[0m

    Es  gibt eine Vielzahl von Problemen mit rekursivem Make; in
    der Praxis begegnet man ihnen normalerweise  taglich.   Hier
    sind einige dieser Probleme aufgelistet:

    +o Es  ist sehr schwierig, die Reihenfolge der Rekursion kor-
      rekt in die Unterverzeichnisse zu formulieren.  Diese Rei-
      henfolge  nicht sehr stabil und ab und zu muss man sie von
      Hand korrigieren.  Je mehr Verzeichnisse es gibt  oder  je
      mehr  Ebenen dem Verzeichnisbaum hinzugefugt werden, desto
      unstabiler wird diese Reihenfolge.

    +o Es ist oft notwendig, die Unterverzeichnisse mehr als ein-
      mal zu durchlaufen, um das ganze System herzustellen.  Das
      fuhrt naturlich zu langeren Build-Zeiten.

    +o Da die Produktionszeiten sonst  unvertretbar  lang  waren,
      was  mit einer Unproduktivitat der Entwickler gleichbedeu-
      tend ware, lasst man einige  Informationen  bezuglich  der
      Abhangigkeiten  der  Verzeichnisse untereinander weg.  Das
      fuhrt in der Regel dazu, dass einige Produkte nicht  aktu-
      alisiert  werden,  obwohl  sie es sollten, wodurch haufige
      Produktionen  von  Null   an   erforderlich   werden,   um
      sicherzustellen, dass tatsachlich alles aufgebaut wird.

    +o Da die Abhangigkeiten zwischen den Verzeichnissen entweder
      weggelassen werden oder zu schwer auszudrucken sind,  wer-
      den  die  Makefiles  oft  so geschrieben, dass sie zu viel
      machen, um sicherzustellen, dass  wirklich  nichts  ausge-
      lassen wurde.

    +o Die  Ungenauigkeit  der  Abhangigkeiten oder einfach deren
      Fehlen kann zur Folge haben, dass ein Produkt  sich  nicht
      fehlerfrei  bauen  lasst.   Dadurch  wird eine sorgfaltige
      Kontrolle  des  Build-Prozesses  durch  einen   Entwickler
      erforderlich.

    +o Eine  andere Folge des oben Beschriebenen ist, dass manche
      Projekte von den Moglichkeiten der Parallelisierung  durch
      Make  nicht  profitieren  konnen,  weil  das  Build offen-
      sichtlich Unsinn macht.



    Peter Miller          21 December 2003                Page 3





    CM Magazin, Dec-2002          Probleme durch rekursives Make


    Nicht jedes Projekt hat alle diese Probleme.  Wenn sie  auf-
    tauchen,  tun  sie  das  oft unregelmaig und werden dann als
    unerklarbare einmalige Macken abgetan.   In  diesem  Artikel
    sollen eine Reihe Symptome, die uber eine langen Zeitraum in
    der  Praxis  beobachtet  wurden,  miteinander  in  Beziehung
    gesetzt  werden.   Es  folgen eine systematische Analyse und
    ein Losungsvorschlag.

    It must be emphasized that this paper does not suggest  that
    [4mmake[24m  itself is the problem.  This paper is working from the
    premise that [4mmake[24m does [1mnot [22mhave a bug, that  [4mmake[24m  does  [1mnot[0m
    have  a design flaw.  The problem is not in [4mmake[24m at all, but
    rather in the input given to [4mmake[24m - the way  [4mmake[24m  is  being
    used.

    [1m3.  Analyse[0m

    Bevor  es  moglich  ist,  sich diesen scheinbar in keinerlei
    Beziehung zueinander stehenden Problemen zuzuwenden, ist  es
    notwendig,  zu  verstehen,  was  Make  bewirkt  und  wie  es
    arbeitet. Dann erst kann man die  Auswirkungen,  die  rekur-
    sives Make auf das Verhalten von Make hat, betrachten.

    [1m3.1.  Ganzprojekt-Make[0m

    Make  ist ein Expertensystem. Sie versehen es mit einem Satz
    Regeln, nach denen gebaut werden soll,  und  einem  Zielpro-
    dukt, das gebaut werden soll. Die Regeln konnen in paarweise
    geordnete Abhangigkeiten zwischen  den  Dateien  zergliedert
    werden.  Make  liest  die  Regeln  und  ermittelt,  wie  das
    angegebene  Zielprodukt  gebaut  werden  soll.   Sobald   es
    entschieden  hat,  wie  das  Zielprodukt  konstruiert werden
    soll, verfahrt es entsprechend. Make ermittelt die Konstruk-
    tionsweise,  indem  es einen gerichteten azyklischen Graphen
    erstellt, den DAG (directed acyclic graph), der vielen  Stu-
    denten  der Computerwissenschaften vertraut ist. Die Knoten-
    punkte dieses Graphs sind die Dateien des Systems, die  Kan-
    ten stellen die Abhangigkeiten zwischen den Dateien dar. Die
    Kanten des Graphen sind  gerichtet,  da  die  Abhangigkeiten
    paarweise  geordnet  sind,  wodurch  ein  azyklischer  Graph
    entsteht - was in einem ungerichteten Graphen wie ein Zyklus
    aussieht, wird im gerichteten Graphen durch die Richtung der
    Kanten aufgelost.

    In diesem Artikel wird eine kleines Beispielprojekt fur  die
    Analyse  benutzt.  Obwohl  die  Anzahl  der Dateien in diese
    Beispiel klein ist, ist  es  komplex  genug,  um  alle  oben
    beschriebenen Probleme mit rekursivem Make zu demonstrieren.
    Zuerst einmal wird das Projekt  in  einer  nicht  rekursiven
    Form vorgestellt.







    Ulrike Amoore         21 December 2003                Page 4





    CM Magazin, Dec-2002          Probleme durch rekursives Make


                           +++
                           ++-[4mP[24m+[4mr[24m+[4moject[0m
                            +-++Mmaakienf.icle
                            +-++parse.c
                            +-++parse.h
                              ++


    Das Makefile in diesem kleinen Projekt sieht so aus:

                    +--------------------------+
                    |OBJ = main.o parse.o      |
                    |prog: $(OBJ)              |
                    |  $(CC) -o $@ $(OBJ)      |
                    |main.o: main.c parse.h    |
                    |  $(CC) -c main.c         |
                    |parse.o: parse.c parse.h  |
                    |  $(CC) -c parse.c        |
                    +--------------------------+
    Ein  paar  der impliziten Regeln von Make sind hier explizit
    aufgeschrieben, um es fur Sie einfacher zu machen, das Make-
    file in den zugehorigen DAG umzuformen.

    Das  oben  genannte Makefile kann als DAG in folgender Weise
    dargestellt werden:
                     
                                prog



                          main.o   parse.o


                      main.c   parse.h  parse.c



    Aufgrund der Pfeile, die die  Ordnung  der  Beziehungen  der
    Dateien  zueinander  ausdrucken,  handelt  es  sich um einen
    azyklischen  Graphen.  Wenn  es  den  Pfeilen  zufolge  eine
    kreisformige Abhangigkeit gabe, lage ein Fehler vor.

    Beachten  Sie  bitte,  dass  die  Objektdateien (.o) von den
    Include-Dateien (.h) abhangig sind,  obwohl  es  die  Quell-
    dateien  (.c) sind, die das Einfugen vornehmen. Das hat fol-
    genden Grund: Wird eine  Include-Datei  geandert,  sind  die
    Objektdateien nicht mehr aktuell, nicht die Quelldateien.

    Der  zweite  Schritt  des Make-Prozesses ist eine Postorder-
    Traversierung des DAG. Das  bedeutet,  dass  die  abhangigen
    Knotenpunkte  zuerst besucht werden. Die eigentliche Reihen-
    folge der Traversierung ist nicht festgelegt, aber die meis-
    ten  Make-Anwendungen gehen von oben nach unten und bei Kan-
    ten unter demselben Knotenpunkt von links  nach  rechts  und



    Peter Miller          21 December 2003                Page 5





    CM Magazin, Dec-2002          Probleme durch rekursives Make


    die  meisten  Projekte  verlassen  sich  stillschweigend auf
    dieses Verhalten. Die  zuletzt  geanderten  Versionen  aller
    Dateien  werden  untersucht  und  eine  weiter oben liegende
    Datei wird als nicht mehr aktuell  eingestuft,  wenn  irgen-
    deine  der darunterliegenden Dateien, von denen sie abhangig
    ist, junger ist. Wenn eine Datei als nicht mehr akuell klas-
    sifiziert  wurde,  wird die zu der entsprechenden Graphkante
    gehorende  Aktion  ausgefuhrt  (in  dem  oben   aufgefuhrten
    Beispiel  ware das ein Kompilations- oder ein Bindeprozess).

    Die Anwendung von rekursivem Make beeinflusst  beide  Phasen
    der  Make-Operation: Es veranlasst Make, einen ungenauen DAG
    zu erstellen und es zwingt Make, den DAG  in  einer  unange-
    brachten Reihenfolge zu traversieren.

    [1m3.2.  Rekursives Make[0m

    Um  die  Auswirkungen  des  rekursiven Makes zu untersuchen,
    teilen wir das obige Beispiel  in  zwei  Module  ein.  Jedes
    Modul  hat  sein  eigenes  Makefile und ein Makefile fur die
    oberste Ebene, deren Aufgabe es ist, jedes  der  Modul-Make-
    files aufzurufen.

    Dieses  Beispiel  ist absichtlich konstruiert und zwar durch
    und  durch.  Aber  jede  Modularitat  in  Projekten  ist  in
    gewisser  Weise ein Konstrukt. Bedenken Sie: Bei vielen Pro-
    jekten ebnet der Linker am Ende alles wieder ein.

    Die Verzeichnisstruktur ist folgendermaen:
                          +++
                          ++-[4mP[24m+[4mr[24m+[4moject[0m
                           ++++Maanktefile
                           |++-++Makefile
                           | +-++main.c
                           ++++b+e+e
                             +-++Makefile
                             +-++parse.c
                             +-++parse.h
                                |

    Das Makefile der obersten Ebene  sieht  oft  sehr  wie  eine
    Shell-Befehlsdatei aus:

                  +-------------------------------+
                  |MODULES = ant bee              |
                  |all:                           |
                  |  for dir in $(MODULES); do \  |
                  |    (cd $$dir; ${MAKE} all); \ |
                  |  done                         |
                  +-------------------------------+
    Das ant/Makefile sieht so aus:






    Ulrike Amoore         21 December 2003                Page 6





    CM Magazin, Dec-2002          Probleme durch rekursives Make


                  +------------------------------+
                  |all: main.o                   |
                  |main.o: main.c ../bee/parse.h |
                  |  $(CC) -I../bee -c main.c    |
                  +------------------------------+
    und der entsprechende DAG sieht so aus:
                         
                               main.o



                          main.c    parse.h

    Das bee/Makefile sieht so aus:

                   +----------------------------+
                   |OBJ = ../ant/main.o parse.o |
                   |all: prog                   |
                   |prog: (OBJ)                 |
                   |  $(CC) -o $@ $(OBJ)        |
                   |parse.o: parse.c parse.h    |
                   |  $(CC) -c parse.c          |
                   +----------------------------+
    und der entsprechende DAG sieht so aus:
                       
                              prog



                        main.o    parse.o


                             parse.h  parse.c



    Schauen  Sie  sich die DAGs genau an. Sie stellen fest, dass
    keiner von ihnen komplett ist. Beiden DAGs fehlen Knoten und
    Kanten.  Wenn  die  vollstandige Produktion von der obersten
    Ebene ausgefuhrt wird, funktioniert alles.

    Aber was passiert, wenn eine kleine Anderung eintritt?   Was
    wurde  passieren, wenn parse.c und parse.h von einer parse.y
    Yacc Grammatik erzeugt wurden? Dann wurden  folgende  Zeilen
    dem bee/Makefile hinzugefugt:

                    +--------------------------+
                    |parse.c parse.h: parse.y  |
                    |  $(YACC) -d parse.y      |
                    |  mv y.tab.c parse.c      |
                    |  mv y.tab.h parse.h      |
                    +--------------------------+
    Und die entsprechenden Veranderungen des DAGs sahen so aus:




    Peter Miller          21 December 2003                Page 7





    CM Magazin, Dec-2002          Probleme durch rekursives Make


                       
                              prog



                        main.o    parse.o


                             parse.h  parse.c



                                  parse.y



    Diese Veranderung hat eine einfache Folge: Wenn parse.y edi-
    tiert wird, wird main.o nicht correct aufgebaut.  Das  ruhrt
    daher,  dass  der  DAG fur ant nur einige der Abhangigkeiten
    von main.o kennt, wahrend der DAG fur bee  sogar  keine  von
    ihnen kennt.

    Um  zu verstehen, warum das passiert, ist es notwendig, sich
    die Aktionen, die Make von der obersten  Ebene  aus  untern-
    immt, anzuschauen. Nehmen Sie einmal an, dass das Projekt in
    sich konsistent ist. Jetzt editieren Sie  parse.y,  so  dass
    die    generierte   parse.h   nicht-triviale   Veranderungen
    aufweist. Wenn nun das Haupt-Make aufgerufen wird, wird erst
    ant  und  dann  bee  besucht. Aber ant/main.o ist noch nicht
    rekompiliert, weil bee/parse.h noch nicht regeneriert  wurde
    und  deshalb  noch  nicht  anzeigt,  dass  main.o nicht mehr
    aktuell ist. Das ist auch immer noch nicht  der  Fall,  wenn
    das  rekursive  Make  bee besucht, wobei parse.c und parse.h
    und zuletzt parse.o rekonstruiert werden. Wenn das  Programm
    gelinkt  wird,  sind  main.o  und parse.o in erheblichem Mae
    nicht kompatibel. D. h. das Programm funktioniert nicht.

    [1m3.3.  Herkommliche Losungen[0m

    Es gibt drei  herkommliche  Korrekturmoglichkeiten  fur  die
    oben beschriebene Storung.

    [1m3.3.1.  Umstrukturierung[0m

    Die  erste  ist, die Anordnung der Module in dem Haupt-Make-
    file von Hand zu berichtigen. Die  Frage  ist,  warum  diese
    Korrektur uberhaupt notwendig ist. Schlielich soll Make eine
    Expertensystem sein. Hat Make irgendeinen Fehler  oder  ging
    etwas anderes schief?

    Zur  Beantwortung  dieser  Frage muss man nicht den Graphen,
    sondern  die  Anordnung  der   Traversierung   des   Graphen
    anschauen.  Um  fehlerlos  zu  arbeiten, muss Make eine Pos-
    torder-Traversierung vornehmen, aber dadurch, dass  der  DAG



    Ulrike Amoore         21 December 2003                Page 8





    CM Magazin, Dec-2002          Probleme durch rekursives Make


    in  zwei Teile geteilt wurde, war es Make nicht mehr moglich
    den Graphen in der notwendigen Reihenfolge zu traversieren -
    stattdessen   wurde   von   dem   Projekt  eine  Reihenfolge
    vorgeschrieben. Eine Reihenfolge, die, wenn man  den  Origi-
    nalgraphen betrachtet, schlichtweg falsch ist. Indem man das
    Haupt-Makefile korrigiert, stellt man eine Reihenfolge  her,
    die  der,  die  Make  hatte  benutzen  konnen,  ahnlich ist.
    Solange, bis die nachste Abhangigkeit hinzugefugt wird...

    Bitte beachten Sie, dass paralleles Build  (make  -j)  viele
    der  bei  der  manuellen Umstrukturierung gemachten Annahmen
    stillschweigend auer Kraft setzt, wodurch diese Losung nutz-
    los  wird.  Auerdem  fuhren alle untergeordneten Makes eben-
    falls mehrere Aktionen gleichzeitig aus.

    [1m3.3.2.  Wiederholung[0m

    Bei der zweiten herkommlichen Losung lasst  man  das  Haupt-
    Makefile  mehrere  Male  durchlaufen.  Das sieht ungefahr so
    aus:

                  +-------------------------------+
                  |MODULES = ant bee              |
                  |all:                           |
                  |  for dir in $(MODULES); do \  |
                  |    (cd $$dir; ${MAKE} all); \ |
                  |  done                         |
                  |  for dir in $(MODULES); do \  |
                  |    (cd $$dir; ${MAKE} all); \ |
                  |  done                         |
                  +-------------------------------+

    Dadurch wird die Produktionszeit verdoppelt.  Aber  das  ist
    nicht  alles:  Es  gibt keine Garantie, dass zwei Durchlaufe
    ausreichen! Die Hochstzahl der Durchlaufe ist  nicht  einmal
    proportional  zu der Anzahl der Module, sondern proportional
    zu der Anzahl der Graphkanten, die Modulgrenzen kreuzen.

    [1m3.3.3.  Des Guten zu viel[0m

    Wir haben schon ein Beispiel  gesehen,  bei  dem  rekursives
    Make  zu  wenig baute, aber ein anderes verbreitetes Problem
    ist, dass zu viel gebaut wird. Bei der dritten herkommlichen
    Losung  fugt  man  dem  ant/Makefile  sogar noch mehr Zeilen
    hinzu:

                    +--------------------------+
                    |.PHONY: ../bee/parse.h    |
                    |../bee/parse.h:           |
                    |    cd ../bee; \          |
                    |    make clean; \         |
                    |    make all              |
                    +--------------------------+




    Peter Miller          21 December 2003                Page 9





    CM Magazin, Dec-2002          Probleme durch rekursives Make


    Das bedeutet, dass immer wenn main.o  gebaut  wird,  parse.h
    als nicht aktuell betrachtet wird. Alle Inhalte von bee wer-
    den jedesmal von neuem gebaut  einschlielich  parse.h.  Auch
    main.o  wird  immer  wieder neu gebaut, selbst wenn alles in
    sich konsistent war.

    Beachten Sie bitte, dass bei dieser Losung make -j (paralle-
    les    Build)    viele    der    angenommenen    Anordnungen
    stillschweigend auer Kraft setzt, wodurch diese Losung nutz-
    los  wird,  weil  alle der untergeordneten Makes ihre Builds
    gleichzeitig durchfuhren(clean und dann all), wobei sie sich
    standig gegenseitig storen.

    [1m4.  Vorsorge[0m

    Die  obige  Analyse  basiert auf einer einfachen Aktion: Der
    DAG wurde kunstlich  in  unvollstandige  Stucke  unterteilt.
    Diese  Unterteilung hat all die Probleme, die man von rekur-
    siv verwendetem Make kennt, zur Folge.

    Hat Make es nicht richtig verstanden? Nein,  das  ist  nicht
    der  Grund.  Hier  liegt  ein Fall des uralten GIGO-Prinzips
    (Garbage in, Garbage out) vor: Wo man Mull hereintut,  kommt
    Mull heraus. Unvollstandige Makefiles sind fehlerhafte Make-
    files.

    Wenn Sie diese Probleme vermeiden wollen, zerlegen  Sie  den
    DAG  nicht  in einzelne Teile. Verwenden sie stattdessen ein
    einziges Makefile fur das ganze Projekt.  Die  Rekursion  an
    sich ist nicht schadlich, sondern das verstummelte Makefile,
    das man fur die Rekursion verwendet, ist falsch. Es ist kein
    Fehler von Make, dass das rekursive Make nicht funktioniert;
    es macht das bestmogliche aus den mangelhaften Eingabeinfor-
    mationen.

         "[4mAber,[24m  [4maber,[24m  [4maber...[24m  [4mDas[24m  [4mkonnen[24m [4mSie[24m [4mdoch[24m [4mnicht[0m
         [4mmachen![24m", hore  ich  Sie  jammern,  "[4mein[24m  [4meinziges[0m
         [4mMakefile[24m  [4mist[24m  [4mviel[24m  [4mzu[24m [4mgro,[24m [4mman[24m [4mkann[24m [4mes[24m [4mgar[24m [4mnicht[0m
         [4mwarten,[24m [4mes[24m [4mist[24m [4mviel[24m [4mzu[24m [4mschwierig,[24m [4mdie[24m [4mRegeln[24m [4mdafur[0m
         [4mzu[24m  [4mschreiben,[24m  [4mder[24m  [4mPlatz[24m  [4mim[24m  [4mHauptspeicher[24m [4mwird[0m
         [4mnicht[24m [4mausreichen,[24m [4mich[24m [4mwill[24m [4mnur[24m [4mmeinen[24m [4mkleinen[24m [4mTeil[0m
         [4mbauen,[24m [4mdas[24m [4mBuild[24m [4mwird[24m [4mviel[24m [4mzu[24m [4mlange[24m [4mdauern.[24m [4mEs[24m [4mist[0m
         [4mschlichtweg[24m [4mnicht[24m [4mpraktikabel.[24m"

    Das sind stichhaltige Einwande und oft ziehen  Make-Benutzer
    aus  ihnen den Schluss, dass es keinerlei kurz- oder langer-
    fristigen Nutzen fur sie bringen wurde, ihren  Build-Prozess
    einmal zu uberarbeiten. Diese Schlussfolgerung ist auf uber-
    holten,  falschen  Annahmen  gegrundet,  die   sich   jedoch
    hartnackig halten.

    In  den  nachsten  Abschnitten wird der Reihe nach auf jeden
    dieser Einwande eingegangen.




    Ulrike Amoore         21 December 2003               Page 10





    CM Magazin, Dec-2002          Probleme durch rekursives Make


    [1m4.1.  Ein einziges Makefile ist zu gro[0m

    Ware die vollstandige Produktionsbeschreibung fur das  ganze
    Projekt  in  einem  einzigen  Makefile  untergebracht, wurde
    dieser Satz sicherlich zutreffen. Aber  moderne  Make-Imple-
    mentierungen  kennen  Include-Anweisungen. Dadurch, dass ein
    entscheidendes Fragment jedes Moduls einschlossen wird,  ist
    die Gesamtgroe des Makefiles und seiner Includes nicht unbe-
    dingt groer als die des beim rekursiven Vorgehen verwendeten
    Makefiles.

    [1m4.2.  Ein einziges Makefile kann man nicht warten[0m

    Ein Haupt-Makefile, das eine Referenz auf ein Fragment jedes
    Moduls enthalt, ist nicht komplexer als das beim  rekursiven
    Make verwendete. Da der DAG nicht zerstuckelt ist, ist diese
    Art Makefile sogar  weniger  komplex  und  damit  besser  zu
    warten,  einfach weil weniger Korrekturen notwendig sind, um
    seine Funktionstuchtigkeit zu erhalten.

    Rekursive Makefiles beinhalten  eine  Menge  Wiederholungen.
    Bei  vielen  Projekten wird dieses Problem durch die Verwen-
    dung von Include-Dateien gelost. Wenn man ein einziges Make-
    file  fur  das  Projekt  benutzt, fallt der Bedarf an diesen
    "allgemeinen" Include-Dateien weg - das  eine  Makefile  ist
    der allgemeine Teil.

    [1m4.3.  Es ist zu schwierig, die Regeln zu formulieren[0m

    Man  muss  lediglich  den Verzeichnisteil an einer Reihe von
    Stellen in die Dateinamen einfugen. Das ist notwendig,  weil
    Make  vom Verzeichnis der obersten Ebene ausgefuhrt wird; in
    dem aktuellen Verzeichnis erscheint  die  Datei  nicht.  Man
    muss sich nicht darum kummern, wo die Ausgabedatei ausdruck-
    lich in einer Regel genannt wird.

    GCC erlaubt im Zusammenhang mit der -c-Option eine -o-Option
    und  GNU-Make  wei das. Daraus folgt eine implizite Kompila-
    tionsregel, die die  Ausgabedatei  an  die  richtige  Stelle
    setzt.  Altere  und weniger intelligente Compiler lassen die
    -o-Option mit der -c-Option jedoch vielleicht nicht  zu  und
    lassen  die  Objektdatei  im  Verzeichnis der obersten Ebene
    zuruck  (d.  h.  im  falschen  Verzeichnis).  Es  gibt  drei
    Moglichkeiten,  wie  Sie  diesen  Fehler korrigieren konnen:
    Entweder Sie beschaffen sich GNU-Make  und  GCC,  sie  uber-
    schreiben die Build-Regel durch eine korrekt funktionierende
    oder Sie beschweren sich bei ihrem Handler.

    Auch  K&R;  C-Compiler  beginnen  den  in  Anfuhrungszeichen
    notierten  Includepfad (#include "filename.h") vom aktuellen
    Verzeichnis aus. Das bedeutet, dass sie nicht das ausfuhren,
    was Sie wollen. ANSI C-konforme C-Compiler aber beginnen den
    in Anfuhrungszeichen notierten Includepfad von dem Verzeich-
    nis  aus,  in  dem die Quelldatei erscheint; hier sind keine



    Peter Miller          21 December 2003               Page 11





    CM Magazin, Dec-2002          Probleme durch rekursives Make


    Veranderungen der Quelle notwendig. Falls Sie keinen ANSI C-
    konformen  C-Compiler  besitzen,  sollten  Sie  in  Betracht
    ziehen, so bald wie moglich einen GCC auf  Ihrem  System  zu
    installieren.

    [1m4.4.  Ich will doch nur mein kleines Teilprodukt bauen[0m

    Die  meiste  Zeit  sind  Entwickler  mitten  im  Projektbaum
    beschaftigt. Sie bearbeiten ein  oder  zwei  Dateien  lassen
    dann  Make durchlaufen, was ihre Veranderungen ubersezt, und
    probieren sie aus. Diesen Arbeitsschritt fuhren sie  taglich
    Dutzende  oder  Hunderte  Male aus. Es ware absurd, wenn sie
    gezwungen waren jedesmal das ganze Projekt zu bauen.

    Entwickler habe immer die Option, ein besonderes Zielprodukt
    fur  Make  zu  definieren. Das gilt immer, wir verlassen uns
    lediglich gewohnlich  auf  das  im  Makefile  des  aktuellen
    Verzeichnisses    vorgegebene    Zielprodukt,    um   unsere
    Befehlszeile zu verkurzen. Man  kann  also  auch  mit  einem
    Ganzprojekt-Makefile  sein  kleines Teilprodukt bauen, indem
    man einfach ein bestimmtes Zielprodukt definiert und,  falls
    die Befehlszeile zu lang wird, ein Pseudonym verwendet.

    Es  stellt  sich  aber auch die Frage, ob es immer so absurd
    ist, das ganze Projekt zu bauen. Wenn z. B.  eine  in  einem
    Modul  vorgenommene Anderung in anderen Modulen Auswirkungen
    hat, weil eine Abhangigkeit existiert,  die  dem  Entwickler
    nicht  bewusst ist (aber dem Makefile ist sie bewusst), ware
    es dann nicht besser, wenn der Entwickler das so schnell wie
    moglich  herausfindet?   Solche Abhangigkeiten werden gefun-
    den, weil der DAG  vollstandiger  ist  als  beim  rekursiven
    Vorgehen.

    In  den  seltensten  Fallen  sind Entwickler erfahrene, alte
    Hasen, die jede einzelne der Millionen von Zeilen  Kode  des
    Produktes   auswendig   kennen.  Meistens  haben  sie  einen
    zeitlich begrenzten Vertrag oder  sie  sind  jungere  Mitar-
    beiter.  Sie  wollen  naturlich nicht, dass Auswirkungen wie
    die eben beschriebene entdeckt werden, nachdem Ihre Anderun-
    gen  in  den  Hauptkode  einfugt  wurden, sondern wurden sie
    gerne ganz in Ruhe  in  ihrem  lokalen  Arbeitsbereich  ent-
    decken, weit weg vom Hauptkode.

    Wenn  Sie  "nur  Ihr kleines Teilprodukt" bauen wollen, weil
    Sie befurchten, dass ein Bau des ganzen Projekts infolge der
    Verzeichnisstruktur,  die  Sie  in  Ihrem  Projekt verwendet
    haben, die Master Source des  Projekts  beschadigen  konnte,
    lesen  Sie  bitte  das  Kapitel  [4mProjekte[24m  [4mim[24m  [4mVergleich[24m  [4mzu[0m
    [4mSandkasten[24m.








    Ulrike Amoore         21 December 2003               Page 12





    CM Magazin, Dec-2002          Probleme durch rekursives Make


    [1m4.5.  Das Bauen dauert zu lange[0m

    Diese Aussage kann man fur eine von zwei Situationen machen:
    1.  Die  Durchfuhrung des Make eines ganzen Projekts dauert,
    obwohl alles aktualisiert ist, unvermeidlich sehr lange.  2.
    Diese  unvermeidbaren  Verzogerungen sind unakzeptabel, wenn
    der Entwickler die eine Datei, die er verandert hat, schnell
    kompilieren und linken will.

    [1m4.5.1.  Builds von Projekten[0m

    Stellen  Sie  sich  ein  hypothetisches Projekt vor mit 1000
    Quelldateien (.c), von denen jede  ihre  Aufrufschnittstelle
    hat,  welche in der zugehorigen Include-Datei (.h) mit Defi-
    nitionen,   Typvereinbarungen   und   Funktionsdeklarationen
    definiert ist. Diese 1000 Quelldateien beinhalten ihre eige-
    nen Interfacedefinitionen und zusatzlich die  Interfacedefi-
    nitionen  aller  Module, die sie aufrufen konnen. Diese 1000
    Quelldateien werden in  1000  Objektdateien  ubersetzt,  die
    wiederum  zu einem ausfuhrbaren Programm gebunden werden. In
    diesem System gibt es  etwa  3000  Dateien,  uber  die  Make
    informiert  werden muss. Auerdem muss Make uber die Include-
    abhangigkeiten informiert werden und man  muss  untersuchen,
    ob implizite Regeln (z. B. .y - .c) anwendbar sind.

    Um  den  DAG  zu erstellen, muss Make fur 3000 Dateien deren
    Anderungsdatum ermitteln und  auerdem  noch  fur  etwa  2000
    zusatzliche   Dateien,  abhangig  davon,  welche  impliziten
    Regeln Ihr Make kennt und welche  Ihr  Makefile  nicht  aus-
    geschaltet  hat. Auf dem bescheidenen 66MHz i486 des Authors
    dauert das etwa 10 Sekunden;  auf  systemeigenen  Laufwerken
    auf  schnelleren Hardwarebasen geht es sogar noch schneller.
    Mit NFS uber 10MB Ehernet dauert es ebenfalls etwa 10 Sekun-
    den, gleichgultig, von welcher Hardwarebasis die Aktion aus-
    gefuhrt wird.

    Das ist eine erstaunliche Statistik. Stellen Sie sich einmal
    vor,  dass  Sie  ihn  der  Lage  sind, eine einzige von 1000
    Quelldateien in nur 10 Sekunden - zuzuglich der Zeit fur das
    Kompilieren selbst - zu kompilieren.

    Die  Dateien auf 100 Module zu verteilen und den Prozess als
    ein rekursives Make durchzufuhren, dauert immerhin 25 Sekun-
    den.  Die  wiederholte  Prozesserzeugung fur die untergeord-
    neten  Make-Aufrufe  nehmen  eine  relativ  lange  Zeit   in
    Anspruch.

    Aber  warten  Sie  einen  Moment!  Bei  realen Projekten mit
    weniger als 1000 Dateien dauert es sehr viel langer  als  25
    Sekunden  bis  Make herausgefunden hat, das es nichts zu tun
    hat. Fur manche Projekte ware es ein  Fortschritt,  wenn  es
    nur  25  Minuten dauerte. Dieses Beispiel zeigt uns, dass es
    nicht die Anzahl der Dateien ist, die bremst (das dauert nur
    10   Sekunden),  und  es  ist  auch  nicht  die  wiederholte



    Peter Miller          21 December 2003               Page 13





    CM Magazin, Dec-2002          Probleme durch rekursives Make


    Prozesserzeugung fur die untergeordneten  Make-Aufrufe  (die
    dauert  nur  15 Sekunden).  Was aber nimmt dann so viel Zeit
    in Anspruch?

    Bei  traditionellen  Losungen  des  durch  rekursives   Make
    enstandenen Problems werden die untergeordneten Make-Aufrufe
    oft uber das hier beschriebene  Minimum  erhoht:  z.  B.  um
    vielfaltige  Wiederholungen  (3.3.2.)  durchzufuhren oder um
    Modulgrenzen   uberschreitende    Abhangigkeiten    (3.3.3.)
    ubermaig  abzudecken. Das kann lange Zeit dauern, besonders,
    wenn beides zusammenkommt. Aber es ist nicht fur die  beson-
    ders langen Produktionszeiten verantwortlich. Was nimmt also
    noch soviel Zeit in Anspruch?

    Die Komplexitat des Makefiles  ist  so  zeitaufwendig.  Mehr
    daruber im Kapitel [4mEffiziente[24m [4mMakefiles[24m.

    [1m4.5.2.  Builds wahrend der Entwicklung[0m

    Wenn  es - wie bei dem Beispiel mit den 100 Dateien - nur 10
    Sekunden dauert, herauszufinden welche Datei  neu  ubersetzt
    werden  muss,  wird  die  Produktivitat der Entwickler nicht
    bedeutend  eingeschrankt,  wenn  sie  ein   Ganzprojekt-Make
    durchfuhren   anstatt  eines  modulspezifischen  Makes.  Der
    Vorteil  fur  das  Projekt  ist,  dass  der  modulzentrierte
    Entwickler  in  entscheidenden  Momenten  (und  nur  in  den
    entscheidenden) daran erinnert wird, dass seine Arbeit  auch
    weitgehendere Auswirkungen hat.

    Die  standige  Verwendung  von C-Include-Dateien, die genaue
    Interface-Definitionen  (einschlielich  Funktionsprototypen)
    enthalten,  wurde  in  vielen  Fallen zu Ubersetzungsfehlern
    fuhren, was wiederum  ein  fehlerhaftes  Produkt  zur  Folge
    hatte.  Werden  Builds  fur  das ganze Projekt durchgefuhrt,
    entdecken Entwickler solche Fehler  sehr  fruh  im  Entwick-
    lungsprozess  und  sind  in  der  Lage Fehlerbehebungen dann
    vorzunehmen, wenn sie den geringsten Aufwand verursachen.

    [1m4.6.  Ihre Speicherkapazitaten sind erschopft[0m

    Das ist der interessanteste Einwand.  Irgenwann  einmal  vor
    langer  Zeit auf einem Mikroprozessor weit, weit weg ist das
    vielleicht sogar vorgekommen. Als  Feldman  das  erste  Make
    entwickelte,  schrieb man das Jahr 1978 und er benutzte eine
    PDP11. Unix-Prozesse waren auf 64B Daten beschrankt.

    Solch ein Rechner wurde bei dem oben genannte  Beispiel  mit
    seinen  3000 in dem Ganzprojekt-Makefile genau beschriebenen
    Dateien warscheinlich nicht  zulassen,  einen  DAG  und  die
    Regeln im Hauptspeicher zu halten.

    Aber  wir  benutzen  keine  PDP11  mehr.  Die physische Spe-
    icherkapazitat eines kleinen modernen  Computers  uberschre-
    itet  10MB und der virtuelle Speicher oft 100MB. Ein Projekt



    Ulrike Amoore         21 December 2003               Page 14





    CM Magazin, Dec-2002          Probleme durch rekursives Make


    mit hunderttausenden  Quelldateien  ist  notwendig,  um  die
    virtuelle  Speicherkapazitat eines kleinen modernen Rechners
    zu erschopfen. Da das Beispielprojekt mit 1000  Quelldateien
    weniger als 100KB Speicherplatz in Anspruch nimmt (probieren
    Sie es aus, Sie werden sehen, dass es stimmt), ist  es  sehr
    unwahrscheinlich,  dass  irgendein Projekt, das man in einem
    einzigen Verzeichnisbaum auf einem einzigen Laufwerk verwal-
    ten kann, die Speicherkapazitaten ihres Rechners ubersteigt.

    [1m4.7.  Warum erstellt man den DAG nicht in den Modulen?[0m

    Oben  wurde  erlautert, dass die Grunde fur die Probleme bei
    rekursivem Make in dem unvollstandigen DAG zu  suchen  sind.
    Dem  zufolge  kann  das Problem gelost werden, indem man die
    fehlenden  Abhangigkeiten   wieder   hinzufugt,   ohne   die
    existierende Investition in das rekursive Make aufzugeben.

    +o Der  Entwickler darf es jedoch nicht vergessen. Die Folgen
      tragt nicht er, sondern die Entwickler der anderen  Module
      bekommen  sie  zu  spuren.  Es gibt keine bessere Methode,
      einen Entwickler daran zu erinnern, etwas zu tun, als  den
      Zorn der Kollegen.

    +o Es  ist  schwierig,  herauszufinden, an welcher Stelle die
      Anderungen vorgenommen werden mussen. Moglicherweise  muss
      jedes   Makefile   im  ganzen  Projekt  auf  erforderliche
      Anderungen hin untersucht  werden.  Naturlich  konnen  Sie
      auch darauf warten, dass Ihre Kollegen die Stellen fur Sie
      finden.

    +o Die  Includeabhangigkeiten   werden   unnotigerweise   neu
      berechnet  oder  werden  nicht  korrekt interpretiert. Das
      passiert, weil Make auf Zeichenketten basiert,  wodurch  .
      und ../ant zwei verschiedene Stellen sind, selbst wenn Sie
      im ant-Verzeichnis stehen. Das  ist  von  Bedeutung,  wenn
      Includeabhangigkeiten automatisch berechnet werden, wie es
      bei allen groen Projekten der Fall ist.

    Indem man sicherstellt, dass jedes Makefile vollstandig ist,
    kommt  man  an den Punkt, dass das Makefile wenigstens eines
    Moduls bereits die Informationen  des  Ganzprojekt-Makefiles
    umfasst  (Sie  durfen nicht vergessen, dass diese Module ein
    einziges Projekt  formen  und  daher  miteinander  verbunden
    sind), wodurch das rekursive Make uberflussig wird.

    [1m5.  Effiziente Makefiles[0m

    Das  zentrale  Thema  dieses  Artikels sind die semantischen
    Nebenwirkungen des  kunstlichen  In-Stucke-Zerteilens  eines
    Makefiles,   das  notwendig  ist,  um  ein  rekursives  Make
    durchzufuhren. Wenn Sie jedoch viele Makefiles  haben,  wird
    die  Geschwindigkeit, in der Make diese Menge Dateien inter-
    pretieren kann, ebenfalls zum Thema.




    Peter Miller          21 December 2003               Page 15





    CM Magazin, Dec-2002          Probleme durch rekursives Make


    Builds konnen aus zwei Grunden ubermaig  lange  dauern:  Die
    herkommlichen  Korrekturen  fur  den zerteilten DAG bauen zu
    viel oder Ihr Makefile ist nicht effizient.

    [1m5.1.  Verzogerte  Auswertung[0m

    Make muss den Text eines Makefiles irgendwie aus einer Text-
    datei  lesen und verstehen, so dass ein DAG erstellt und die
    angegebenen Aktionen den Kanten  zugeordnet  werden  konnen.
    All das wird im Speicher festgehalten.

    Die  Eingabesprache  fur  Makefiles ist irrefuhrend einfach.
    Sie ist textbasiert, im Gegensatz zu den  tokenbasierten  z.
    B.  fur  C  und AWK. Das ist ein entscheidender Unterschied,
    der Neulingen genauso wie Experten oft entgeht. Make tut das
    absolute  Minimum,  um  die Eingabezeilen zu verarbeiten und
    sie im Hauptspeicher zu verstauen.

    Folgende Zuordnung ist ein Beispiel dafur:

                    +--------------------------+
                    |OBJ = main.o parse.o      |
                    +--------------------------+
    Wenn Menschen das lesen, heit das, der  Variablen  OBJ  sind
    zwei  Dateinamen  main.o  und  parse.o zugeordnet. Aber Make
    versteht das ganz anders. OBJ wird die  Zeichenfolge  main.o
    parse.o zugeordnet. Und es wird noch schlimmer:

                    +--------------------------+
                    |SRC = main.c parse.c      |
                    |OBJ = $(SRC:.c=.o)        |
                    +--------------------------+
    In diesem Fall erwartet der Mensch, dass OBJ durch Make zwei
    Dateinamen zugeordnet werden,  aber  Make  ordnet  in  Wirk-
    lichkeit  die Zeichenkette $(SRC:,c=.o) zu. Das ruhrt daher,
    dass es sich um eine Makrosprache mit verzogerter Auswertung
    handelt  im  Gegensatz zu einer mit Variablen und sofortiger
    Auswertung.

    Wenn es Ihnen nicht zu schwierig erscheint, schauen Sie sich
    die folgende Makefile an:

                   +-----------------------------+
                   |SRC = $(shell echo 'Ouch!' \ |
                   |  1>&2 ; echo *.[cy])        |
                   |OBJ = \                      |
                   |  $(patsubst %.c,%.o,\       |
                   |    $(filter %.c,$(SRC))) \  |
                   |  $(patsubst %.y,%.o,\       |
                   |    $(filter %.y,$(SRC)))    |
                   |test: $(OBJ)                 |
                   |  $(CC) -o $@ $(OBJ)         |
                   +-----------------------------+
    Wie oft wird das Shell-Kommando ausgefuhrt? Autsch! Er wird,



    Ulrike Amoore         21 December 2003               Page 16





    CM Magazin, Dec-2002          Probleme durch rekursives Make


    allein um den DAG zu konstruieren,  zweimal  ausgefuhrt  und
    noch  zweimal,  wenn  die Regel ausgefuhrt werden muss. Wenn
    dieser Shell-Befehl nichts  komplexes  oder  zeitaufwendiges
    ausfuhrt (was er aber normalerweise tut), braucht er viermal
    so lang, wie erwartet.

    Aber es lohnt sich, andere  Teile  dieses  OBJ-Makros  naher
    anzuschauen.  Jedesmal, wenn es genannt wird, werden riesige
    Mengen von Vorgangen abgewickelt:

    +o Der Parameter fur  Shell  ist  eine  einzige  Zeichenkette
      (alle Built-in-Funktionen haben als Parameter eine einzige
      Zeichenkette). Die Zeichenkette wird  in  der  untergeord-
      neten Shell ausgefuhrt und die Standardausgabe dieses Kom-
      mandos wird wieder  eingegeben,  wobei  Zeilenumbruche  in
      Leerschritte  verwandelt  werden.  Das  Ergebnis  ist eine
      einzige Zeichenkette.

    +o Der Parameter zum Filtern ist eine  einzige  Zeichenkette.
      Dieser  Parameter  wird beim ersten Komma in zwei Zeichen-
      ketten zerlegt. Jede der beiden Zeichenketten wird dann in
      durch  Leerschritte  getrennte  Teilketten aufspalten. Die
      erste Reihe entspricht den  Mustern  und  die  zweite  den
      Dateinamen.  Dann  wird  fur jede Musterteilkette, mit der
      eine Dateinamenteilkette ubereinstimmt, der  Dateiname  in
      die Ausgabe eingefugt. Sobald die Ausgabe vollstandig ist,
      werden die Teilketten wieder zu einer einzigen durch Leer-
      schritte unterteilten Zeichenkette zusammengesetzt.

    +o Der   Parameter   zur  Musterersetzung  ist  eine  einzige
      Zeichenkette.  Dieser  Parameter  wird  beim  ersten   und
      zweiten  Komma  in  drei  Ketten  aufgespalten. Die dritte
      Kette wird dann in durch Leerschritte getrennte Teilketten
      zerteilt,  die den Dateinamen entsprechen. Dann wird jeder
      Dateiname, der mit der ersten  Kette  ubereinstimmt,  gema
      der  zweiten  Kette  ersetzt. Wenn ein Dateiname nicht mit
      der ersten Kette ubereinstimmt, passiert  er  unverandert.
      Sobald  die  Ausgabe vollstandig erzeugt wurde, werden die
      Teilketten wieder zu  einer  einzigen  durch  Leerschritte
      unterteilten Zeichenkette zusammengesetzt.

    Sie  sehen,  wie  oft  diese  Zeichenketten aufgespalten und
    wieder zusammengestzt werden. Sie sehen,  auf  wieviel  ver-
    schiedene  Weisen  das passiert. Das ist langsam. In unserem
    Beispiel gibt es nur zwei Dateien,  aber  stellen  Sie  sich
    vor,  wie  lange  diese  Prozeduren fur 1000 Dateien dauern.
    Diese Aktion viermal auszufuhren, ist sehr ineffizient.

    Wenn Sie ein einfaches Make, das uber  keine  Ersetzung  und
    keine  Built-in-Funktionen verfugt, benutzen, beeintrachtigt
    Sie das nicht. Aber ein modernes Make  hat  viele  Built-in-
    Funktionen   und   kann   sogar   nebenbei   Shell-Kommandos
    ausfuhren. Die Semantik der Textmanipulation von Make fuhrt,
    verglichen  mit  C  oder  AWK,  zu  einer sehr CPU-intesiven



    Peter Miller          21 December 2003               Page 17





    CM Magazin, Dec-2002          Probleme durch rekursives Make


    Zeichenkettenmanipuation in Make.

    [1m5.2.  Unmittelbare Auswertung[0m

    Moderne Make-Ausfuhrungen haben einen :=  Zuordnungsoperator
    fur unmittelbare Auswertung. Das oben genannte Beispiel kann
    folgendermaen neu geschrieben werden:

                  +------------------------------+
                  |SRC := $(shell echo 'Ouch!' \ |
                  |  1>&2 ; echo *.[cy])         |
                  |OBJ := \                      |
                  |  $(patsubst %.c,%.o,\        |
                  |    $(filter %.c,$(SRC))) \   |
                  |  $(patsubst %.y,%.o,\        |
                  |    $(filter %.y,$(SRC)))     |
                  |test: $(OBJ)                  |
                  |  $(CC) -o $@ $(OBJ)          |
                  +------------------------------+
    Beachten Sie, dass beide Zuordnungen Zuordnungen fur  unmit-
    telbare  Auswertung  sind.  Ware  die erste keine, wurde das
    Shell-Kommando immer zweimal  ausgefuhrt  werden.  Ware  die
    zweite  keine, wurden die aufwendigen Ersetzungen mindestens
    zweimal, moglicherweise sogar viermal durchgefuhrt werden.

    Als Daumenregel gilt: Benutzen  Sie  immer  Zuordnungen  fur
    unmittelbare  Auswertung,  es  sei denn Sie wunschen bewusst
    eine verzogerte Auswertung.

    [1m5.3.  Include-Dateien[0m

    Viele Makefiles fuhren denselben Textbearbeitungsvorgang (z.
    B.  die  o.  g.  Filter)  bei jedem einzelnen Make-Durchlauf
    erneut durch; das Ergebnis  dieser  Prozeduren  andert  sich
    jedoch  selten.  Wenn  es  auch  praktisch  ist, ist es doch
    effizienter, die Ergebnisse der Textbearbeitung einer  Datei
    zu speichern und diese Datei in das Makefile einzufugen.

    [1m5.4.  Abhangigkeiten[0m

    Seien  Sie  nicht  geizig mit Include-Dateien. Man kann sie,
    verglichen mit $(shell), mit relativ wenig Aufwand lesen und
    sie beintrachtigen auch die Effizienz nur wenig.

    Um  ein  Beispiel  dafur  zu  zeigen,  ist  es  erst  einmal
    notwendig,  eine  nutzliche  Eigenschaft  des  GNU-Makes  zu
    beschreiben:  Wenn  Make das Makefile gelesen hat und einige
    ihrer  Include-Dateien  sind  nicht   mehr   aktuell   (oder
    existieren  noch  nicht),  werden  sie  neu erzeugt und Make
    beginnt noch einmal. Das fuhrt dazu,  dass  Make  jetzt  mit
    aktuellen  Include-Dateien  arbeitet.  Diese  Funktion  kann
    genutzt werden, um ein automatische Berechnung der  Include-
    Dateiabhangigkeiten  fur C-Quellen zu realisieren. Die nahe-
    liegendste Art, es auszufuhren,  hat  jedoch  einen  kleinen



    Ulrike Amoore         21 December 2003               Page 18





    CM Magazin, Dec-2002          Probleme durch rekursives Make


    Fehler.

                    +--------------------------+
                    |SRC := $(wildcard *.c)    |
                    |OBJ := $(SRC:.c=.o)       |
                    |test: $(OBJ)              |
                    |  $(CC) -o $@ $(OBJ)      |
                    |include dependencies      |
                    |dependencies: $(SRC)      |
                    |  depend.sh $(CFLAGS) \   |
                    |    $(SRC) > $@           |
                    +--------------------------+
    Das depend.sh-Skript druckt seine Zeilen in folgender Weise:

         [4mDatei[24m.o: [4mDatei[24m.c [4mInclude[24m.h ...

    Die einfachste Art, das umzusetzen, ist, GCC  zu  verwenden,
    aber Sie brauchen eine entsprechendes AWK-Skript oder C-Pro-
    gramm, wenn Sie einen anderen Compiler verwenden:

                    +--------------------------+
                    |#!/bin/sh                 |
                    |gcc -MM -MG "$@"          |
                    +--------------------------+
    Diese  Methode,  C-Includeabhangikeiten   aufzufinden,   hat
    mehrere  ernste  Mangel, aber der verbreiteteste Mangel ist,
    dass die Abhangigkeitendatei selbst nicht von den C-Include-
    Dateien  abhangig  ist.  Das  bedeutet,  dass  sie nicht neu
    erzeugt wird, wenn sich Include-Dateien verandern.  Es  gibt
    im   DAG   keine   Kante   zwischen  einem  Knotenpunkt  der
    Abhangigkeiten und einem  Knotenpunkt  der  Include-Dateien.
    Wenn  sich  eine  Include-Datei andert, weil sie eine andere
    Datei  aufnimmt   (verschachtelte   Include),   werden   die
    Abhangigkeiten  nicht  neu  bestimmt  und  die  C-Datei wird
    moglicherweise nicht neu  ubersetzt,  wodurch  das  Programm
    nicht korrekt neu gebaut wird.

    Das ist ein klassischer Fall von [4mzu[24m [4mwenig[24m [4mbauen[24m. Das Problem
    wird dadurch verursacht, dass man Make unzulangliche  Infor-
    mationen  gibt und es dadurch veranlasst, einen unzureichen-
    den DAG zu erstellen und das falsche Ergebnis zu liefern.

    Die traditionelle Losung ist, zu viel zu bauen:














    Peter Miller          21 December 2003               Page 19





    CM Magazin, Dec-2002          Probleme durch rekursives Make


                    +--------------------------+
                    |SRC := $(wildcard *.c)    |
                    |OBJ := $(SRC:.c=.o)       |
                    |test: $(OBJ)              |
                    |  $(CC) -o $@ $(OBJ)      |
                    |include dependencies      |
                    |.PHONY: dependencies      |
                    |dependencies: $(SRC)      |
                    |  depend.sh $(CFLAGS) \   |
                    |    $(SRC) > $@           |
                    +--------------------------+
    Jetzt werden selbst, wenn das Projekt aktualisiert ist,  die
    Abhangigkeiten  neu  ermittelt.  Wenn Projekte gro sind, ist
    das eine erhebliche Zeitverschwendung  und  kann  einer  der
    Hauptgrunde  dafur  sein,  dass  Make sehr lange braucht, um
    herauszufinden, dass nichts zu tun ist.

    Es gibt ein zweites Problem: Wenn  sich  irgendeine  der  C-
    Dateien  verandert,  werden  wieder  fur  alle C-Dateien die
    Includeabhangigkeiten neu berechnet. Das ist  genauso  wenig
    effizient,  als  hatte  man  ein Makefile, das folgendermaen
    aussieht:

                    +--------------------------+
                    |prog: $(SRC)              |
                    |  $(CC) -o $@ $(SRC)      |
                    +--------------------------+
    In exakter Entsprechung zum C-Fall benotigt  man  hier  eine
    Zwischenform.  Dieser verleiht man gewohnlich ein .d-Suffix.
    Dank der Tatsache, dass man in einer Include-Anweisung  mehr
    als  eine Datei benennen kann, existiert keine Notwendigkeit
    alle .d-Dateien zu verknupfen:

                  +------------------------------+
                  |SRC := $(wildcard *.c)        |
                  |OBJ := $(SRC:.c=.o)           |
                  |test: $(OBJ)                  |
                  |  $(CC) -o $@ $(OBJ)          |
                  |include $(OBJ:.o=.d)          |
                  |%.d: %.c                      |
                  |  depend.sh $(CFLAGS) $* > $@ |
                  +------------------------------+

    Diese Form erfordert noch  eine  Korrektur:  Genau  wie  die
    Objektdateien  (.o) hangen die Abhangigkeitsdateien (.d) von
    den Quelldateien und den Include-Dateien ab.

         [4mDatei[24m.d [4mDatei[24m.o: [4mDatei[24m.c [4mInclude[24m.h

    Das bedeutet, dass man wieder an dem depend.sh-Skript herum-
    basteln muss:






    Ulrike Amoore         21 December 2003               Page 20





    CM Magazin, Dec-2002          Probleme durch rekursives Make


                +-----------------------------------+
                |#!/bin/sh                          |
                |gcc -MM -MG "$@" |                 |
                |sed -e 's@^\(.*\)\.o:@\1.d \1.o:@' |
                +-----------------------------------+

    Durch  dieses Vorgehen bei der Bestimmung der Abhangigkeiten
    der Include-Dateien erhoht sich die Anzahl der Dateien,  die
    das Makfile beinhaltet, im Gegensatz zur herkommlichen Meth-
    ode.  Dateien zu offnen ist jedoch  weniger  aufwendig,  als
    jedesmal  wieder alle Abhangigkeiten zu ermitteln. Normaler-
    weise bearbeitet ein Entwickler ein oder zwei Dateien, bevor
    er  ein  Re-Build  durchfuhrt; bei dieser Methode wird genau
    die betroffene  Abhangigkeitsdatei  wiederhergestellt  (oder
    mehr  als  eine,  wenn  Sie  eine  Include-Datei  bearbeitet
    haben). Unterm Strich erfordert diese Vorgehensweise weniger
    Rechenarbeit durch die CPU und weniger Zeitaufwand.

    Muss  bei  einem  Build  nichts  gemacht  werden,  tut  Make
    tatsachlich nichts und findet das auch sehr schnell  heraus.

    Die  beschriebene  Technik nimmt jedoch an, dass Ihr Projekt
    vollstandig in ein  Verzeichnis  hineinpasst.  Das  ist  bei
    groen  Projekten  gewohnlich  nicht der Fall. Also muss noch
    einmal an dem depend.sh-Skript herumgebastelt werden:

                   +-----------------------------+
                   |#!/bin/sh                    |
                   |DIR="$1"                     |
                   |shift 1                      |
                   |case "$DIR" in               |
                   |"" | ".")                    |
                   |gcc -MM -MG "$@" |           |
                   |sed -e 's@^\(.*\)\.o:@\1.d \ |
                   |  \1.o:@'                    |
                   |;;                           |
                   |*)                           |
                   |gcc -MM -MG "$@" |           |
                   |sed -e "s@^\(.*\)\.o:@$DIR/\ |
                   |\1.d $DIR/\1.o:@"            |
                   |;;                           |
                   |esac                         |
                   +-----------------------------+
    Und die Regel muss auch geandert werden, damit das Verzeich-
    nis,  wie das Skript annimmt, als erster Parameter ubergeben
    wird.

                    +---------------------------+
                    |%.d: %.c                   |
                    |  depend.sh `dirname $*` \ |
                    |    $(CFLAGS) $* > $@      |
                    +---------------------------+
    Bitte beachten Sie, dass die Namen  der  .d-Dateien  relativ
    zum  Verzeichnis  der obersten Ebene sind. Man kann sie auch



    Peter Miller          21 December 2003               Page 21





    CM Magazin, Dec-2002          Probleme durch rekursives Make


    so schreiben, dass sie von jeder Ebene aus  benutzt  werden,
    aber das geht uber den Rahmen dieses Artikels hinaus.

    [1m5.5.  Multiplikator[0m

    Alle  in  diesem  Kapitel beschriebenen Ineffizienzen hangen
    zusammen. Wenn Sie 100 Makefile-Interpretationen  fur  jedes
    Modul  einmal  durchfuhren,  kann es sehr lange dauern, 1000
    Quelldateien zu uberprufen; ebenso wenn  die  Interpretation
    komplexe   Bearbeitungsprozesse   erfordert   oder  unnotige
    Arbeiten ausfuhrt, oder beides.  Das  Ganzprojekt-Make  muss
    auf  der  andere  Seite  nur  ein  einziges  Makefile inter-
    pretieren.

    [1m6.  Projekte im Vergleich zu Sandkasten[0m

    Das oben Besprochene setzt voraus, das ein Projekt  sich  im
    Rahmen  eines einzigen Verzeichnisbaums befindet und das ist
    in der Regel der Idealfall. Die Arbeitswirklichkeit in groen
    Softwareprojekten  fuhrt dagegen oft zu bizarren und wunder-
    vollen Verzeichnisstrukturen, damit die Entwickler  in  ver-
    schiedenen  Bereichen  des  Projekts  arbeiten  konnen, ohne
    vollstandige Kopien des  Projekts  zu  benotigen  und  damit
    wertvollen Speicherplatz zu verschwenden.

    Sie  konnen das hier vorgeschlagene Ganzprojekt-Make unprak-
    tisch finden, weil es nicht  zu  den  entwickelten  Methoden
    Ihres Entwicklungsprozesses passt.

    Das  hier  vorgestellte Ganzprojekt-Make hat eine Auswirkung
    auf die Entwicklungsmethoden: Es schafft eine sauberere  und
    einfachere  Produktionsumgebung  fur  Ihre Entwickler. Indem
    man die VPATH-Funktion von Make anwendet,  ist  es  moglich,
    dass  Sie  nur  jene  Dateien, die Sie bearbeiten mussen, in
    Ihren privaten Arbeitsbereich, oft auch Sandkasten  genannt,
    kopieren.

    Die  einfachste Weise, zu erklaren, was VPATH tut, ist, eine
    Analogie zu dem Suchpfad fur  Include-Dateien  herzustellen,
    indem  man -I-Pfadoptionen fur den C-Compiler benutzt. Diese
    Optionen beschreiben,  wo  man  nach  Dateien  suchen  muss,
    genauso  wie  VPATH-Make  anweist, wo es nach Dateien suchen
    muss.

    Durch die Verwendung von VPATH wird es moglich, die sich  im
    Sandkasten  befindenden  Dateien  oben auf der Master-Source
    des Projekts [4maufzustapeln[24m.  Auf  diese  Weise  bekommen  die
    Dateien, die sich im Sandkasten befinden, Vorrang. Make ver-
    wendet jedoch den  gesamten  Dateienverband,  um  ein  Build
    durchzufuhren.







    Ulrike Amoore         21 December 2003               Page 22





    CM Magazin, Dec-2002          Probleme durch rekursives Make


                      +          +
                     +[4mM[24m+[4master[24m [4mSour[24m+[4mc[24m+[4me[0m
                     +   main.c +   [4mCombined[24m [4mView[0m
                    +   parse.y+       main.c
                     [4mSand-Box[24m    +     parse.y
                      main.c    ++   variable.c
                                +
                    variable.c +


    In  dieser Umgebung hat der Sandkasten dieselbe Baumstruktur
    wie die Master-Source (Stammdatenquelle) des  Projekts.  Das
    ermoglicht  den  Entwicklern,  gefahrlos modulubergreifenden
    Kode  zu  verandern   z.   B.   bei   der   Anderung   einer
    Modulschnittstelle.

    Es  ermoglicht  auerdem, den Sandkasten physicalisch von der
    Master-Source zu trennen, ihn z. B auf einem anderen  Laufw-
    erk  oder in Ihrem privaten Arbeitsbereich anzulegen und der
    Master-Source des Projekts einen  schreibgeschutzten  Status
    zu  verleihen,  sofern  Sie  ein strenges Check-in-Verfahren
    haben (oder sich zulegen wollen).

    Bitte beachten Sie: Sie mussen nicht nur ihrem Entwicklungs-
    Makefilec  eine VPATH-Reihe hinzufugen, sondern auch die -I-
    Optionen dem CFLAGS-Makro, damit  der  C-Compiler  denselben
    Pfad  wie  Make  benutzt.  Das  kann  man  einfach mit einem
    3-Zeilen-Makefile in Ihrem Arbeitsbereich durchfuhren -  Sie
    definieren  erst  ein Makro, dann VPATH und fugen schlielich
    das Makefile der Master-Source des Projekts ein.

    [1m6.1.  VPATH-Semantik[0m

    Um das eben Erlauterte anzuwenden, mussen Sie GNU-Make  3.76
    oder  eine  neuere  Version verwenden. Fur fruhere GNU-Make-
    Versionen benotigen  Sie  Paul  Smith's  VPATH+  Patch.  Den
    konnen  Sie bei ftp://ftp.wellfleet.com/netman/psmith/gmake/
    beziehen.

    Die POSIX-Semantiken von VPATH sind unzulanglich genauso wie
    viele  existierende  Make-Ausfuhrungen. Vielleicht uberlegen
    Sie sich, GNU-Make zu installieren.

    [1m7.  Das Gesamtbild[0m

    In diesem Kapitel werden alle auf den vorangegangenen Seiten
    erorterten  Aspekte  zusammengefugt  und das Beispielprojekt
    mit seinen getrennten Modulen  wird  vorgestellt,  aber  mit
    einem Gesamtprojekt-Makefile. Die Verzeichnisstruktur unter-
    scheidet sich  wenig  von  der  rekursiven,  auer  dass  die
    tieferliegenden  Makefiles  durch  modulspezifische Include-
    Dateien ersetzt werden:





    Peter Miller          21 December 2003               Page 23





    CM Magazin, Dec-2002          Probleme durch rekursives Make


                          +++
                          ++-[4mP[24m+[4mr[24m+[4moject[0m
                           ++++Maanktefile
                           |++-++module.mk
                           | +-++main.c
                           ++++b+e+e
                           | +-++module.mk
                           | +-++parse.y
                           +-++de|pend.sh
                              |

    Das Makefile sieht folgendermaen aus:

          +-----------------------------------------------+
          |MODULES := ant bee                             |
          |# look for include files in                    |
          |#   each of the modules                        |
          |CFLAGS += $(patsubst %,-I%,\                   |
          |  $(MODULES))                                  |
          |# extra libraries if required                  |
          |LIBS :=                                        |
          |# each module will add to this                 |
          |SRC :=                                         |
          |# include the description for                  |
          |#   each module                                |
          |include $(patsubst %,\                         |
          |    %/module.mk,$(MODULES))                    |
          |# determine the object files                   |
          |OBJ :=                    \                    |
          |  $(patsubst %.c,%.o,     \                    |
          |    $(filter %.c,$(SRC))) \                    |
          |  $(patsubst %.y,%.o,     \                    |
          |    $(filter %.y,$(SRC)))                      |
          |# link the program                             |
          |prog: $(OBJ)                                   |
          |  $(CC) -o $@ $(OBJ) $(LIBS)                   |
          |# include the C include                        |
          |#   dependencies                               |
          |include $(OBJ:.o=.d)                           |
          |# calculate C include                          |
          |#   dependencies                               |
          |%.d: %.c                                       |
          |  depend.sh `dirname $*.c` $(CFLAGS) $*.c > $@ |
          +-----------------------------------------------+
    Das erscheint sehr lang, aber es enthalt alle ublichen  Ele-
    mente  an  einer Stelle, so dass die Make-Include-Dateien in
    den einzelnen Modulen sehr kurz sein konnen.

    Die ant/module.mk Datei sieht folgendermaen aus:

                    +--------------------------+
                    |SRC += ant/main.c         |
                    +--------------------------+
    Die bee/module.mk Datei sieht so aus:



    Ulrike Amoore         21 December 2003               Page 24





    CM Magazin, Dec-2002          Probleme durch rekursives Make


                    +--------------------------+
                    |SRC += bee/parse.y        |
                    |LIBS += -ly               |
                    |%.c %.h: %.y              |
                    |  $(YACC) -d $*.y         |
                    |  mv y.tab.c $*.c         |
                    |  mv y.tab.h $*.h         |
                    +--------------------------+

    Beachten Sie bitte, dass  die  Built-in-Regeln  fur  die  C-
    Dateien  verwendet werden, aber wir benotigen ein spezielles
    yacc-Verfahren, um die erzeugte .h-Datei zu bekommen.

    Die Einsparungen in diesem Beispiel wirken unerheblich, weil
    das Makefile der obersten Ebene so gro ist. Aber stellen Sie
    sich vor, es handele sich um 100 Module, jedes mit ein  paar
    funktionellen,  modulspezifischen Zeilen. Insgesamt erreicht
    man oft, ohne an Modularitat zu verlieren, einen  geringeren
    Zeitaufwand als bei Verwendung eines rekursiven Makes.

    Der  entsprechende DAG des Makefiles sieht, nachdem man alle
    Einfugungen vorgenommen hat, folgendermaen aus:
                     
                                prog



                          main.o   parse.o
                            main.d|  parse.d|
                                  |         |
                      main.c   parse.h  parse.c



                                   parse.y



    Die      Knotenpunkte      und      Kanten      fur      die
    Inklude-Dateiabhangikeitsdateien  sind  ebenfalls vorhanden,
    da sie wichtig  sind,  damit  Make  fehlerlos  funktionieren
    kann.

    [1m7.1.  Nebeneffekte[0m

    Es  gibt  eine  Reihe wunschenswerter Nebeneffekte, wenn man
    ein einziges Makefile verwendet.

    +o Die -j-Option des GNU-Makes fur parallel  laufende  Builds
      funktioniert  besser  als  vorher.  Sie  kann  sogar  mehr
      voneinander unabhangige Vorgange  zugleich  ausfuhren  und
      leidet nicht mehr an subtilen Fehlern.





    Peter Miller          21 December 2003               Page 25





    CM Magazin, Dec-2002          Probleme durch rekursives Make


    +o Die -k-Option des allgemeinen Makes, die dafur sorgt, dass
      Make selbst angesichts von Fehlern  so  weit  wie  moglich
      weiter arbeitet, funktioniert ebenfalls besser als vorher.
      Sie findet sogar mehr Material, mit dem sie weiterarbeiten
      kann.

    [1m8.  Literaturstudie[0m

    Wie  ist es moglich das wir 20 Jahre lang Make falsch einge-
    setzt haben? Wie ist es  moglich,  dass  das  Verhalten  von
    Make,   das  wir  bisher  seiner  begrenzten  Funktionalitat
    zugeschrieben haben, sich nun als falsche Anwendung von Make
    herausstellt?

    Der  Autor  begann  uber  die  Ideen,  die in diesem Artikel
    vorgestellt werden, nachzudenken,  als  er  sich  mit  einer
    Reihe  halicher Build-Probleme in ganzlich unterschiedlichen
    Projekten aber mit gemeinsamen Symptomen  konfrontiert  sah.
    Indem  er  von  den einzelnen Projekten Abstand nahm und die
    Gemeinsamkeiten der Probleme  eingehend  untersuchte,  wurde
    ihm moglich, eine Regelmaigkeit zu erkennen. Die meisten von
    uns sind zu sehr mit den Flickarbeiten fur eine  fehlerfreie
    Funktion  des  mangelhaften Builds beschaftigt, als dass sie
    Zeit  finden  wurden,  die  Sache  einmal  mit  Distanz   zu
    begutachten    und    sich    einen    Gesamteindruck    der
    Schwierigkeiten zu verschaffen.  Besonders  dann,  wenn  das
    fragliche  Produkt  offensichtlich  arbeitet und das seit 20
    Jahren.

    Es ist interssant, dass die Probleme des rekursiven Makes in
    den  einschlagigen  Buchern,  auf die sich Unixprogrammierer
    verlassen, wenn sie prazisen praktischen Rat benotigen, kaum
    erwahnt werden.

    [1m8.1.  Das Original[0m

    Das  originale Make-Handbuch [feld78] enthalt keinen Hinweis
    auf rekursives Make, und erst  recht  keine  Erorterung  der
    relativen   Vorteile  des  Ganzprojekt-Makes  gegenuber  dem
    rekursiven Make.

    Es uberrascht nicht, dass  das  Originalhandbuch  rekursives
    Make  nicht erwahnte. Damals passten Unix-Pojekte gewohnlich
    in eine einziges Verzeichnis.

    Das ist vielleicht auch ein Grund, warum sich das "ein Make-
    file  in  jedem  Verzeichnis"-Konzept  so in der kollektiven
    Unix-Entwicklungsdenkweise festsetzte.

    [1m8.2.  GNU-Make[0m

    Das GNU-Make-Handbuch [stal93] beschaftigt sich auf mehreren
    Seiten  mit  dem  rekursiven  Make;  die  Erlauterung seiner
    Vorzuge  oder  der  Technik  ist  auf   folgende   Bemerkung



    Ulrike Amoore         21 December 2003               Page 26





    CM Magazin, Dec-2002          Probleme durch rekursives Make


    reduziert:

         "Diese  Technik  ist  nutzlich, wenn Sie getrennte
         Makefiles fur verschiedene Teilsysteme, die zusam-
         men ein groeres System bilden, anlegen wollen."

    Kein  Wort  uber  die  Schwierigkeiten,  auf  die  Sie stoen
    konnten.

    [1m8.3.  Projektverwaltung mit Makefiles[0m

    Das Nutshell-Make-Handbuch  [talb91]  preist  das  rekursive
    Make besonders als dem Ganzprojekt-Make uberlegen an:

         Der  "sauberste" Weg, ein Build zu erstellen, ist,
         indem man in  jedem  Verzeichnis  ein  gesondertes
         Makefile  anlegt  und sie durch ein Haupt-Makefile
         verbindet, das eine rekursive  Make-Funktion  her-
         vorruft.  Wenn diese Technik auch umstandlich ist,
         ist  sie  doch  leichter  zu  verwalten  als  eine
         einzige,  riesenhafte Datei, die mehrere Verzeich-
         nisse abdeckt."  (Seite 65)

    Das widerspricht genau dem Rat, den das Buch nur zwei  Para-
    graphen weiter vorne erteilt:

         "Make  ist  am  glucklichsten,  wenn Sie all seine
         Dateien in einem Verzeichnis lassen" (Seite 64)

    Aber das Buch versaumt es, den Widerspruch in diesen  beiden
    Aussagen  zu  erortern, und fahrt stattdessen fort, eine der
    herkommlichen Moglichkeiten zu beschreiben, mit der man  die
    Symptome    eines   durch   rekursives   Make   verursachten
    unvollstandigen DAGs zu umgehen versucht.

    Dieses Buch bietet einen Anhaltspunkt, warum rekursives Make
    seit  so  vielen Jahren in dieser Weise verwendet wurde. Sie
    sehen, wie die beiden  o.  g.  Aussagen  das  Konzept  eines
    Verzeichnisses  mit dem Konzept eines Makefiles verwechseln.

    Dieser Artikel legt eine  einfache  Anderung  der  Denkweise
    nahe:  In  Verzeichnisbaumen,  egal  wie verzweigt sie sind,
    werden Dateien gespeichert; Makefiles dagegen sind dazu  da,
    die Beziehungen dieser Dateien untereinander zu beschreiben,
    gleichgultig wieviele Dateien es sind.

    [1m8.4.  BSD-Make[0m

    Die Anleitung fur BSD-Make [debo88]  erwahnt  das  rekursive
    Make  uberhaupt  nicht,  aber  sie  ist  eines  der  wenigen
    Handbucher,  das,  wenn  auch  sehr  kurz,  tatsachlich  die
    Beziehung  zwischen  Makefile und DAG beschreibt (Seite 30).
    Daher stammt auch dieses wunderbare Zitat:




    Peter Miller          21 December 2003               Page 27





    CM Magazin, Dec-2002          Probleme durch rekursives Make


         "Falls Make nicht das macht, was Sie erwarten, ist
         die Wahrscheinlichkeit sehr gro, dass das Makefile
         falsch ist."  (Seite 10)

    Das ist eine  kurze  und  pragnante  Zusammenfassung  dieses
    Artikels.

    [1m9.  Zusammenfassung[0m

    Dieser  Artikel  erlautert  einige  miteinander in Beziehung
    stehende Probleme und zeigt, dass es sich nicht, wie  allge-
    mein  angenommen, um Make anhaftende Unzulanglichkeiten han-
    delt, sondern dass sie eine  Folge  davon  sind,  dass  Make
    falsche  Informationen  eingegben  wurden. Hier arbeitet das
    alte "Garbage in, Garbage out"-Prinzip (wo Mull hineinkommt,
    kommt  Mull  heraus). Der Fehler besteht darin, dass man das
    Makefile in unvollstandige  Teilstucke  zerlegt,  denn  Make
    kann nur mit einem vollstandigen DAG fehlerfrei arbeiten.

    Das  erfordert  ein  Umdenken.  In  Verzeichnisbaume  werden
    lediglich Dateien gespeichert, in Makefiles hingegen  werden
    Hinweise  auf  die  Informationen  uber  die Beziehungen der
    Dateien untereinander gespeichert.  Bringen  Sie  das  nicht
    durcheinander,  denn  es ist genauso wichtig die Beziehungen
    zwischen    Dateien    in    verschiedenen    Verzeichnissen
    wiederzugeben,  wie die Beziehungen zwischen Dateien im gle-
    ichen Verzeichniss. Daraus folgt, dass es genau ein Makefile
    fur ein Projekt geben sollte, aber der Groteil der Beschrei-
    bung kann man durch Verwendung von  Make-Include-Dateien  in
    den einzelnen Verzeichnissen, die die Teilmenge der Projekt-
    dateien in den jeweiligen Verzeichnissen beschreiben,  hand-
    haben.  Dieses  Vorgehen  ist  genauso modular, als ware ein
    Makefile in jedem Verzeichnis.

    Es wurde gezeigt, dass bei Verwendung  des  Ganzprojekt-Make
    eine  Entwicklungsproduktion  und eine Vollproduktion gleich
    kurze Laufzeiten haben. Angesichts der  gleichen  Laufzeiten
    wiegen  die  durch  die  genaueren  Abhangigkeiten erzielten
    Vorteile umso schwerer und  bewirken,  dass  dieser  Prozess
    tatsachlich schneller und genauer lauft, als wenn das rekur-
    sive Make angewendet wurde.

    [1m9.1.  Projektubergreifende Abhangigkeiten[0m

    In Unternehmen mit einer starken Neigung zur Mehrfachverwen-
    dung  kann  die  Verwirklichung eines Ganzprojekt-Makes eine
    Herausforderung darstellen. Sich dieser  Herausforderung  zu
    stellen,  mag  erfordern, dass man sich einen Gesamteindruck
    der Situation verschafft.

    +o Es kann sein, dass ein Modul von zwei Programmen gemeinsam
      verwendet  wird,  weil  die  Programme nahe verwandt sind.
      Naturlich gehoren die zwei  Programme  und  das  gemeinsam
      genutzte  Modul  zu demselben Projekt (das Modul kann auch



    Ulrike Amoore         21 December 2003               Page 28





    CM Magazin, Dec-2002          Probleme durch rekursives Make


      unabhangig  sein,  die  Programme   jedoch   nicht).   Die
      Abhangigkeiten  mussen  ausdrucklich  angegeben werden und
      Anderungen des Moduls ziehen nach sich,  dass  beide  Pro-
      gramme  entsprechend neu ubersetzt und neu gebunden werden
      mussen. Vereinigt man sie alle in einem einzigen  Projekt,
      kann das Ganzprojekt-Make dies leisten.

    +o Es  ist moglich, dass ein Modul von zwei Projekten gemein-
      sam genutzt  wird,  weil  ihr  Wirkungsbereich  ineinander
      greift.  Moglicherweise  ist  Ihr  Projekt groer, als Ihre
      gegenwartige  Verzeichnisstuktur   aufnehmen   kann.   Die
      Abhangigkeiten  mussen  ausdrucklich  angegeben werden und
      Anderungen am Modul ziehen nach sich, dass  beide  Pojekte
      entsprechend neu ubersetzt und neu gebunden werden mussen.
      Vereinigt man sie alle in einem einzigen Projekt, kann das
      Ganzprojekt-Make dies leisten.

    +o Es ist normal, die Kanten zwischen Ihrem Projekt und ihrem
      Betriebssystem  oder  dritten   installierten   Werkzeugen
      wegzulassen.  Das ist so normal, dass sie in den Makefiles
      in diesem Artikel und bei  den  vordefinierten  Regeln  im
      Make-Programm  ignoriert  werden. Module, die von mehreren
      Projekten gemeinsam genutzt werden, konnten in diese Kate-
      gorie fallen. Werden sie geandert, bauen Sie Ihre Projekte
      extra neu oder beziehen die Anderungen stillschweigend bei
      der  nachsten  Produktion  mit ein. In beiden Fallen geben
      Sie die  Abhangigkeiten  nicht  ausdrucklich  an  und  das
      Ganzprojekt-Make findet keine Anwendung.

    +o Es  ist der Wiederverwendung moglicherweise dienlich, wenn
      das Modul als Schablone verwendet und Abweichnungen  zwis-
      chen  den  Projekten  als normal angesehen werden. Kopiert
      man  des  Modul  fur  jedes  Projekt,   konnte   man   die
      Abhangigkeiten  ausdrucklich  angeben. Es hat jedoch einen
      zusatzlichen Aufwand zur Folge, wenn  an  dem  gemeinsamen
      Teil Anderungen erforderlich werden.

    In  einer  von  Mehrfachverwendung  stark gepragten Umgebung
    wird das Strukturieren von Abhangigkeiten zu einer Ubung  in
    Risikomanagement.   Welche  Gefahr  besteht,  dass  fehlende
    Stucke des DAGs Ihr Pojekt schadigen?  Wie wichtig  ist  es,
    neu  zu  bauen,  wenn ein Modul sich verandert? Welches sind
    die Folgen, wenn es nicht automatisch neu  produziert  wird?
    Woher  wissen  Sie  ,  wann  ein  Neubau notwendig ist, wenn
    Abhangigkeiten nicht ausdrucklich genannt sind? Welches sind
    die Konsequenzen, wenn man vergisst, neu zu bauen? ...

    [1m9.2.  Die Rendite[0m

    Einige der Techniken, die in diesem Artikel beschrieben wur-
    den, beschleunigen ihren Buildvorgang sogar dann,  wenn  Sie
    rekursives Make beibehalten. Aber das ist nicht das Anliegen
    dieses Artikels, eher ein nutzlicher Umweg. Die Hauptaussage
    ist,   dass   Sie  korrektere  Produktionen  Ihres  Projekts



    Peter Miller          21 December 2003               Page 29





    CM Magazin, Dec-2002          Probleme durch rekursives Make


    erhalten, wenn Sie das Ganzprojekt-Make anstatt  des  rekur-
    siven Makes anwenden.

    +o Make bedarf nicht mehr und oft sogar weniger Zeit, um her-
      auszufinden, das es nichts zu tun braucht.

    +o Die gesamte Eingabe fur Make ist nicht  umfangreicher  und
      komplexer,  oft sogar weniger umfangreich und weniger kom-
      plex.

    +o Im Ganzen sind die Eingabedaten  fur  Make  nicht  weniger
      modular, als bei Anwendung des rekursiven Makes.

    +o Die  Pflege des Makefiles ist nicht aufwendiger, oft sogar
      geringer.

    Die angeblichen Nachteile der Anwendung  eines  Ganzprojekt-
    Makes   gegenuber   dem   rekursiven  Make  sind  oft  nicht
    uberpruft.  Wieviel  Zeit   wird   damit   verbracht,   her-
    auszufinden,   warum  Make  etwas  Unerwartetes  getan  hat?
    Wieviel Zeit wird  damit  verbracht,  an  dem  Build-Prozess
    herumzubasteln?  Diese  Tatigkeiten  werden oft als normaler
    Entwicklungsaufwand angesehen.

    Die Projektproduktion ist eine wesentliche  Tatigkeit.  Wenn
    sie  schlecht funktioniert, werden auch die Entwicklung, die
    Fehlerbehebung und das Testen  in  Mitleidenschaft  gezogen.
    Die  Projektproduktion  sollte so einfach sein, dass sie der
    neuste Mitarbeiter - ohne eine noch  so  kurze  schriftliche
    Anleitung  -  sofort durchfuhren kann. Sie sollte so einfach
    sein,  dass  sie  so  gut  wie  keinen   Entwicklungsaufwand
    erfordert. Ist Ihr Produktionsprozess so einfach?

    [1m10.  Literaturverweise[0m


         [1mdebo88: [22mAdam de Boor (1988).  [4mPMake[24m [4m-[24m [4mA[24m [4mTutorial[24m.  Uni-
    versity of California, Berkeley

         [1mfeld78: [22mStuart I. Feldman (1978).  [4mMake[24m [4m-[24m [4mA[24m [4mProgram[24m [4mfor[0m
    [4mMaintaining[24m  [4mComputer[24m [4mPrograms[24m.  Bell Laboratories Computing
    Science Technical Report 57

         [1mstal93: [22mRichard M. Stallman and Roland McGrath  (1993).
    [4mGNU[24m [4mMake:[24m [4mA[24m [4mProgram[24m [4mfor[24m [4mDirecting[24m [4mRecompilation[24m.  Free Soft-
    ware Foundation, Inc.

         [1mtalb91: [22mSteve Talbott (1991).  [4mManaging[24m  [4mProjects[24m  [4mwith[0m
    [4mMake,[24m [4m2nd[24m [4mEd[24m.  O'Reilly & Associates, Inc.








    Ulrike Amoore         21 December 2003               Page 30





    CM Magazin, Dec-2002          Probleme durch rekursives Make


                    +--------------------------+
                    |Miller,    P.A.   (1998), |
                    |[4mRecursive[24m [4mMake[24m [4mConsidered[24m |
                    |[4mHarmful[24m, AUUGN Journal of |
                    |AUUG  Inc.,  19(1),   pp. |
                    |14-25.                    |
                    +--------------------------+
    See http://www.cmmagazin.de December 2002 issue for the HTML
    version of the transaltion.

    [1m11.  Uber den Autor[0m

    Peter Miller hat viele Jahre lang in der Software R&D Indus-
    trie gearbeitet, vor allem auf UNIX-Systemen. In dieser Zeit
    schrieb er Werkzeuge wie Aegis (ein System des Software Con-
    figuration Management) und Cook (eine weitere Make-Version),
    beide kann man frei uber das Internet beziehen. Die  Betreu-
    ung  bei der Verwendung dieser Werkzeuge auf vielen Internet
    Sites  ermoglichte  den  Einblick,  der  zu  diesem  Artikel
    fuhrte.

    Wenn  Sie  Interesse am Bezug der freien Software des Autors
    haben,  besuchen  Sie  bitte:   http://www.canb.auug.org.au-
    /~millerp/

































    Peter Miller          21 December 2003               Page 31


