





    


                   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


                              VVoorrwwoorrtt
         Zur Produktion grosser 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  regelmassig  auftreten, und
         legt dar, dass sie alle Symptome  desselben  Prob-
         lems  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  ubermassig empfindlich auf Veranderungen
         von Quellkode 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 grosser Projekte angesam-
         melten Volksweisheiten. Einige der von  Vertretern



    Peter Miller           17 August 2024                 Page 1





    CM Magazin, Dec-2002          Probleme durch rekursives Make


         dieser Volksweisheiten vorgebrachten Einwande wer-
         den  hier  untersucht  und   erweisen   sich   als
         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.




    11..  EEiinnffuuhhrruunngg

    Die traditionellen Produktionsmethoden fur grosse UNIX Soft-
    wareentwicklungsprojekte  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 stosst, wenn man bei der Entwicklung  von  Software-
    projekten  mit  dem rekursiven Make arbeitet. Ausserdem wird
    eine einfache Losung aufgezeigt und einige ihrer  Auswirkun-
    gen werden untersucht.

    Durch  Rekursives Make erhalt man einen Verzeichnisbaum, der
    ungefahr so aussieht:
                          +++
                          ++_P+_r|_o_j_e_c_t
                           ++++Mmaokdeufliel1e
                           |++ +|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          17 August 2024                 Page 2





    CM Magazin, Dec-2002          Probleme durch rekursives Make


    11..11..  KKeennnnttnniissssee vvoorraauussggeesseettzztt

    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.

    22..  DDaass PPrroobblleemm

    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           17 August 2024                 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 unregelmassig 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 nnoott have a bug, that _m_a_k_e does nnoott
    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.

    33..  AAnnaallyyssee

    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.

    33..11..  GGaannzzpprroojjeekktt--MMaakkee

    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          17 August 2024                 Page 4





    CM Magazin, Dec-2002          Probleme durch rekursives Make


                           +++
                           ++_P+_r|_o_j_e_c_t
                            + +|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           17 August 2024                 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.

    33..22..  RReekkuurrssiivveess MMaakkee

    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 folgendermassen:
                          +++
                          ++_P+_r|_o_j_e_c_t
                           ++++Maanktefile
                           |++ +|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          17 August 2024                 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           17 August 2024                 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  Masse
    nicht kompatibel. D. h. das Programm funktioniert nicht.

    33..33..  HHeerrkkoommmmlliicchhee LLoossuunnggeenn

    Es  gibt  drei  herkommliche  Korrekturmoglichkeiten fur die
    oben beschriebene Storung.

    33..33..11..  UUmmssttrruukkttuurriieerruunngg

    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.  Schliesslich  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          17 August 2024                 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  ausser  Kraft  setzt,  wodurch diese Losung
    nutzlos wird. Ausserdem fuhren  alle  untergeordneten  Makes
    ebenfalls mehrere Aktionen gleichzeitig aus.

    33..33..22..  WWiieeddeerrhhoolluunngg

    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.

    33..33..33..  DDeess GGuutteenn zzuu vviieell

    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           17 August 2024                 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 einschliesslich 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  ausser  Kraft  setzt,  wodurch diese Losung
    nutzlos wird,  weil  alle  der  untergeordneten  Makes  ihre
    Builds  gleichzeitig  durchfuhren(clean und dann all), wobei
    sie sich standig gegenseitig storen.

    44..  VVoorrssoorrggee

    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_s_s_, _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          17 August 2024                Page 10





    CM Magazin, Dec-2002          Probleme durch rekursives Make


    44..11..  EEiinn eeiinnzziiggeess MMaakkeeffiillee iisstt zzuu ggrroossss

    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 Gesamtgrosse des Makefiles  und  seiner  Includes  nicht
    unbedingt  grosser als die des beim rekursiven Vorgehen ver-
    wendeten Makefiles.

    44..22..  EEiinn eeiinnzziiggeess MMaakkeeffiillee kkaannnn mmaann nniicchhtt wwaarrtteenn

    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.

    44..33..  EEss iisstt zzuu sscchhwwiieerriigg,, ddiiee RReeggeellnn zzuu ffoorrmmuulliieerreenn

    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 weiss 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           17 August 2024                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.

    44..44..  IIcchh wwiillll ddoocchh nnuurr mmeeiinn kklleeiinneess TTeeiillpprroodduukktt bbaauueenn

    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          17 August 2024                Page 12





    CM Magazin, Dec-2002          Probleme durch rekursives Make


    44..55..  DDaass BBaauueenn ddaauueerrtt zzuu llaannggee

    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.

    44..55..11..  BBuuiillddss vvoonn PPrroojjeekktteenn

    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.  Ausserdem  muss  Make  uber   die
    Includeabhangigkeiten  informiert werden und man muss unter-
    suchen, ob implizite Regeln (z. B. .y - .c) anwendbar  sind.

    Um  den  DAG  zu erstellen, muss Make fur 3000 Dateien deren
    Anderungsdatum ermitteln und ausserdem 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           17 August 2024                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.)
    ubermassig  abzudecken.  Das  kann lange Zeit dauern, beson-
    ders, wenn beides zusammenkommt. Aber es ist nicht  fur  die
    besonders 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.

    44..55..22..  BBuuiillddss wwaahhrreenndd ddeerr EEnnttwwiicckklluunngg

    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 (einschliesslich 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.

    44..66..  IIhhrree SSppeeiicchheerrkkaappaazziittaatteenn ssiinndd eerrsscchhooppfftt

    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          17 August 2024                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.

    44..77..  WWaarruumm eerrsstteelllltt mmaann ddeenn DDAAGG nniicchhtt iinn ddeenn MMoodduulleenn??

    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 grossen 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.

    55..  EEffffiizziieennttee MMaakkeeffiilleess

    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           17 August 2024                Page 15





    CM Magazin, Dec-2002          Probleme durch rekursives Make


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

    55..11..  VVeerrzzooggeerrttee  AAuusswweerrttuunngg

    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, heisst 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          17 August 2024                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,  gemass
      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           17 August 2024                Page 17





    CM Magazin, Dec-2002          Probleme durch rekursives Make


    Zeichenkettenmanipuation in Make.

    55..22..  UUnnmmiitttteellbbaarree AAuusswweerrttuunngg

    Moderne Make-Ausfuhrungen haben einen :=  Zuordnungsoperator
    fur unmittelbare Auswertung. Das oben genannte Beispiel kann
    folgendermassen 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.

    55..33..  IInncclluuddee--DDaatteeiieenn

    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.

    55..44..  AAbbhhaannggiiggkkeeiitteenn

    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          17 August 2024                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           17 August 2024                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 gross 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 folgendermassen
    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          17 August 2024                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
    grossen  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           17 August 2024                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.

    55..55..  MMuullttiipplliikkaattoorr

    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.

    66..  PPrroojjeekkttee iimm VVeerrgglleeiicchh zzuu SSaannddkkaasstteenn

    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
    grossen  Softwareprojekten fuhrt dagegen oft zu bizarren und
    wundervollen Verzeichnisstrukturen, damit die Entwickler  in
    verschiedenen  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          17 August 2024                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 ausserdem, 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 schliesslich
    das Makefile der Master-Source des Projekts ein.

    66..11..  VVPPAATTHH--SSeemmaannttiikk

    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.

    77..  DDaass GGeessaammttbbiilldd

    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,  ausser  dass  die
    tieferliegenden  Makefiles  durch  modulspezifische Include-
    Dateien ersetzt werden:





    Peter Miller           17 August 2024                Page 23





    CM Magazin, Dec-2002          Probleme durch rekursives Make


                          +++
                          ++_P+_r|_o_j_e_c_t
                           ++++Maanktefile
                           |++ +|module.mk
                           | - +|main.c
                           ++++b+ee
                           | + +|module.mk
                           | + +|parse.y
                           + +|depend.sh


    Das Makefile sieht folgendermassen 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 folgendermassen aus:

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



    Ulrike Amoore          17 August 2024                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 gross 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, folgendermassen 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.

    77..11..  NNeebbeenneeffffeekkttee

    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           17 August 2024                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.

    88..  LLiitteerraattuurrssttuuddiiee

    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   hasslicher   Build-Probleme   in   ganzlich   unter-
    schiedlichen  Projekten  aber mit gemeinsamen Symptomen kon-
    frontiert sah. Indem er von den einzelnen Projekten  Abstand
    nahm  und  die Gemeinsamkeiten der Probleme eingehend unter-
    suchte, wurde ihm moglich, eine Regelmassigkeit 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 ein-
    mal 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.

    88..11..  DDaass OOrriiggiinnaall

    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.

    88..22..  GGNNUU--MMaakkee

    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          17 August 2024                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 grosseres System bilden, anlegen  wollen."

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

    88..33..  PPrroojjeekkttvveerrwwaallttuunngg mmiitt MMaakkeeffiilleess

    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.

    88..44..  BBSSDD--MMaakkee

    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           17 August 2024                Page 27





    CM Magazin, Dec-2002          Probleme durch rekursives Make


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

    Das ist eine  kurze  und  pragnante  Zusammenfassung  dieses
    Artikels.

    99..  ZZuussaammmmeennffaassssuunngg

    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  Grossteil  der
    Beschreibung kann man  durch  Verwendung  von  Make-Include-
    Dateien  in  den einzelnen Verzeichnissen, die die Teilmenge
    der  Projektdateien   in   den   jeweiligen   Verzeichnissen
    beschreiben, handhaben. 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.

    99..11..  PPrroojjeekkttuubbeerrggrreeiiffeennddee AAbbhhaannggiiggkkeeiitteenn

    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          17 August 2024                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 grosser, 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? ...

    99..22..  DDiiee RReennddiittee

    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           17 August 2024                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?

    1100..  LLiitteerraattuurrvveerrwweeiissee


         ddeebboo8888:: Adam de Boor (1988).  _P_M_a_k_e _- _A _T_u_t_o_r_i_a_l.  Uni-
    versity of California, Berkeley

         ffeelldd7788:: 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

         ssttaall9933:: 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.

         ttaallbb9911:: 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          17 August 2024                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.

    1111..  UUbbeerr ddeenn AAuuttoorr

    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           17 August 2024                Page 31


