






                   Probleme durch rekursives Make


             _P_e_t_e_r _M_i_l_l_e_r           Aus dem Amerikanischen
         millerp@canb.auug.org.au   ins  Deutsche  uber-
                                         setzt von
                                       _U_l_r_i_k_e _A_m_o_o_r_e
                                    uamoore@cmmagazin.de


                              VVoorrwwoorrtt
         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 Ana-
         lyse 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          28 September 2009               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.




    11..  EEiinnffuuhhrruunngg

    Die traditionellen Produktionsmethoden fur groe UNIX Softwa-
    reentwicklungsprojekte  sind  als  _r_e_k_u_r_s_i_v_e_s  _M_a_k_e  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:
                          +++
                          ++_P+_r|_o_j_e_c_t
                           ++++Mmaokdeufliel1e
                           |++ +|Makefile
                           | + +|source1.c
                           | + +|_e_t_c_._._.
                           ++++m+odule2
                             + +|Makefile
                             + +|source2.c
                             + +|_e_t_c_._._.

    Diese  verschachtelte  Modulhierarchie  kann  beliebig  aus-
    geweitet  werden.  Reale  Projekte  haben oft zwei oder drei
    Ebenen.



    -----------
    Copyright (C) 1997 Peter Miller
    German  translation Copyright (C) 2002 CM-Magazin.



    Ulrike Amoore         28 September 2009               Page 2





    CM Magazin, Dec-2002          Probleme durch rekursives Make


    11..11..  KKeennnnttnniissssee vvoorraauussggeesseettzztt

    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.

    22..  DDaass PPrroobblleemm

    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          28 September 2009               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
    _m_a_k_e itself is the problem.  This paper is working from  the
    premise  that  _m_a_k_e  does nnoott have a bug, that _m_a_k_e does nnoott
    have a design flaw.  The problem is not in _m_a_k_e at all,  but
    rather  in  the  input given to _m_a_k_e - the way _m_a_k_e is being
    used.

    33..  AAnnaallyyssee

    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.

    33..11..  GGaannzzpprroojjeekktt--MMaakkee

    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         28 September 2009               Page 4





    CM Magazin, Dec-2002          Probleme durch rekursives Make


                           +++
                           ++_P+_r|_o_j_e_c_t
                            + +|Mmaakienf.icle
                            + +|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          28 September 2009               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.

    33..22..  RReekkuurrssiivveess MMaakkee

    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:
                          +++
                          ++_P+_r|_o_j_e_c_t
                           ++++Maanktefile
                           |++ +|Makefile
                           | - +|main.c
                           ++++b+ee
                             + +|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         28 September 2009               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          28 September 2009               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.

    33..33..  HHeerrkkoommmmlliicchhee LLoossuunnggeenn

    Es  gibt  drei  herkommliche  Korrekturmoglichkeiten fur die
    oben beschriebene Storung.

    33..33..11..  UUmmssttrruukkttuurriieerruunngg

    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         28 September 2009               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.

    33..33..22..  WWiieeddeerrhhoolluunngg

    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.

    33..33..33..  DDeess GGuutteenn zzuu vviieell

    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          28 September 2009               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.

    44..  VVoorrssoorrggee

    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.

         "_A_b_e_r_, _a_b_e_r_, _a_b_e_r_._._. _D_a_s  _k_o_n_n_e_n  _S_i_e  _d_o_c_h  _n_i_c_h_t
         _m_a_c_h_e_n_!",  hore  ich  Sie  jammern,  "_e_i_n _e_i_n_z_i_g_e_s
         _M_a_k_e_f_i_l_e _i_s_t _v_i_e_l _z_u _g_r_o_, _m_a_n _k_a_n_n  _e_s  _g_a_r  _n_i_c_h_t
         _w_a_r_t_e_n_, _e_s _i_s_t _v_i_e_l _z_u _s_c_h_w_i_e_r_i_g_, _d_i_e _R_e_g_e_l_n _d_a_f_u_r
         _z_u _s_c_h_r_e_i_b_e_n_,  _d_e_r  _P_l_a_t_z  _i_m  _H_a_u_p_t_s_p_e_i_c_h_e_r  _w_i_r_d
         _n_i_c_h_t _a_u_s_r_e_i_c_h_e_n_, _i_c_h _w_i_l_l _n_u_r _m_e_i_n_e_n _k_l_e_i_n_e_n _T_e_i_l
         _b_a_u_e_n_, _d_a_s _B_u_i_l_d _w_i_r_d _v_i_e_l _z_u _l_a_n_g_e _d_a_u_e_r_n_. _E_s _i_s_t
         _s_c_h_l_i_c_h_t_w_e_g _n_i_c_h_t _p_r_a_k_t_i_k_a_b_e_l_."

    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         28 September 2009              Page 10





    CM Magazin, Dec-2002          Probleme durch rekursives Make


    44..11..  EEiinn eeiinnzziiggeess MMaakkeeffiillee iisstt zzuu ggrroo

    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.

    44..22..  EEiinn eeiinnzziiggeess MMaakkeeffiillee kkaannnn mmaann nniicchhtt wwaarrtteenn

    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.

    44..33..  EEss iisstt zzuu sscchhwwiieerriigg,, ddiiee RReeggeellnn zzuu ffoorrmmuulliieerreenn

    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          28 September 2009              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.

    44..44..  IIcchh wwiillll ddoocchh nnuurr mmeeiinn kklleeiinneess TTeeiillpprroodduukktt bbaauueenn

    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  _P_r_o_j_e_k_t_e  _i_m  _V_e_r_g_l_e_i_c_h  _z_u
    _S_a_n_d_k_a_s_t_e_n.








    Ulrike Amoore         28 September 2009              Page 12





    CM Magazin, Dec-2002          Probleme durch rekursives Make


    44..55..  DDaass BBaauueenn ddaauueerrtt zzuu llaannggee

    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.

    44..55..11..  BBuuiillddss vvoonn PPrroojjeekktteenn

    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          28 September 2009              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 _E_f_f_i_z_i_e_n_t_e _M_a_k_e_f_i_l_e_s.

    44..55..22..  BBuuiillddss wwaahhrreenndd ddeerr EEnnttwwiicckklluunngg

    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.

    44..66..  IIhhrree SSppeeiicchheerrkkaappaazziittaatteenn ssiinndd eerrsscchhooppfftt

    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         28 September 2009              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.

    44..77..  WWaarruumm eerrsstteelllltt mmaann ddeenn DDAAGG nniicchhtt iinn ddeenn MMoodduulleenn??

    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.

    55..  EEffffiizziieennttee MMaakkeeffiilleess

    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          28 September 2009              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.

    55..11..  VVeerrzzooggeerrttee  AAuusswweerrttuunngg

    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         28 September 2009              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          28 September 2009              Page 17





    CM Magazin, Dec-2002          Probleme durch rekursives Make


    Zeichenkettenmanipuation in Make.

    55..22..  UUnnmmiitttteellbbaarree AAuusswweerrttuunngg

    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.

    55..33..  IInncclluuddee--DDaatteeiieenn

    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.

    55..44..  AAbbhhaannggiiggkkeeiitteenn

    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         28 September 2009              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:

         _D_a_t_e_i.o: _D_a_t_e_i.c _I_n_c_l_u_d_e.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 _z_u _w_e_n_i_g _b_a_u_e_n. 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          28 September 2009              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.

         _D_a_t_e_i.d _D_a_t_e_i.o: _D_a_t_e_i.c _I_n_c_l_u_d_e.h

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






    Ulrike Amoore         28 September 2009              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          28 September 2009              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.

    55..55..  MMuullttiipplliikkaattoorr

    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.

    66..  PPrroojjeekkttee iimm VVeerrgglleeiicchh zzuu SSaannddkkaasstteenn

    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  _a_u_f_z_u_s_t_a_p_e_l_n.  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         28 September 2009              Page 22





    CM Magazin, Dec-2002          Probleme durch rekursives Make


                      +          +
                     +_M+_a_s_t_e_r _S_o_u_r+_c+_e
                     +   main.c +   _C_o_m_b_i_n_e_d _V_i_e_w
                    +   parse.y+       main.c
                     _S_a_n_d_-_B_o_x    +     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.

    66..11..  VVPPAATTHH--SSeemmaannttiikk

    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.

    77..  DDaass GGeessaammttbbiilldd

    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          28 September 2009              Page 23





    CM Magazin, Dec-2002          Probleme durch rekursives Make


                          +++
                          ++_P+_r|_o_j_e_c_t
                           ++++Maanktefile
                           |++ +|module.mk
                           | - +|main.c
                           ++++b+ee
                           | + +|module.mk
                           | + +|parse.y
                           + +|depend.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         28 September 2009              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.

    77..11..  NNeebbeenneeffffeekkttee

    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          28 September 2009              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.

    88..  LLiitteerraattuurrssttuuddiiee

    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.

    88..11..  DDaass OOrriiggiinnaall

    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.

    88..22..  GGNNUU--MMaakkee

    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         28 September 2009              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.

    88..33..  PPrroojjeekkttvveerrwwaallttuunngg mmiitt MMaakkeeffiilleess

    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.

    88..44..  BBSSDD--MMaakkee

    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          28 September 2009              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.

    99..  ZZuussaammmmeennffaassssuunngg

    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.

    99..11..  PPrroojjeekkttuubbeerrggrreeiiffeennddee AAbbhhaannggiiggkkeeiitteenn

    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         28 September 2009              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? ...

    99..22..  DDiiee RReennddiittee

    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          28 September 2009              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?

    1100..  LLiitteerraattuurrvveerrwweeiissee


         ddeebboo8888:: Adam de Boor (1988).  _P_M_a_k_e _- _A _T_u_t_o_r_i_a_l.  Uni-
    versity of California, Berkeley

         ffeelldd7788:: Stuart I. Feldman (1978).  _M_a_k_e _- _A _P_r_o_g_r_a_m _f_o_r
    _M_a_i_n_t_a_i_n_i_n_g _C_o_m_p_u_t_e_r _P_r_o_g_r_a_m_s.  Bell Laboratories  Computing
    Science Technical Report 57

         ssttaall9933::  Richard M. Stallman and Roland McGrath (1993).
    _G_N_U _M_a_k_e_: _A _P_r_o_g_r_a_m _f_o_r _D_i_r_e_c_t_i_n_g _R_e_c_o_m_p_i_l_a_t_i_o_n.  Free Soft-
    ware Foundation, Inc.

         ttaallbb9911::  Steve  Talbott (1991).  _M_a_n_a_g_i_n_g _P_r_o_j_e_c_t_s _w_i_t_h
    _M_a_k_e_, _2_n_d _E_d.  O'Reilly & Associates, Inc.








    Ulrike Amoore         28 September 2009              Page 30





    CM Magazin, Dec-2002          Probleme durch rekursives Make


                    +--------------------------+
                    |Miller,   P.A.    (1998), |
                    |_R_e_c_u_r_s_i_v_e _M_a_k_e _C_o_n_s_i_d_e_r_e_d |
                    |_H_a_r_m_f_u_l, 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.

    1111..  UUbbeerr ddeenn AAuuttoorr

    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          28 September 2009              Page 31


