Hallo zusammen,
die Frage mal banal klingen, ich sehe aber so langsam den Wald vor
lauter Bäumen nicht mehr.
Ich schreibe momentan eine Firmware für einen STM32F103RB in der CooCox
IDE. Ich glaube zwar nicht, daß die Welt auf mein Projekt gewartet hat -
will es aber dennoch als Open Source veröffentlichen (ob als warnendes
Beispiel oder als Inspiration kann sich ja jeder selbst aussuchen). Dazu
gehört natürlich auch ein ordentliches makefile.
"Nicht einfacher als das", habe ich gedacht. Immerhin sind die
Projekteinstellungen von CooCox ja eine gut lesbare XML-Datei, die sich
problemlos mit einem Script in ein makefile umwandeln lassen sollte.
Und jetzt kommt das Problem: Welches Makefile eignet sich als Template?
Sucht man nach "STM32F10x" und "makefile", ergeben sich schon viele
Treffer. Aber welches nimmt man? Der folgende Treffer (
https://gist.github.com/mcufreaks/5224642 ) sah zuerst sehr
vielversprechend aus- ich verstehe nur nicht, wo die Projektdateien
hinkommen sollten.
Das "GNU-make-Handbuch" habe ich hier. Aber irgendwie ist das ungefähr
wie fremden Quelltext zu lesen, wenn man in der Programmiersprache noch
nicht über das "Hallo Welt" hinausgekommen ist.
Der langen Frage kurzer Sinn: Kann mir jemand ein lesbares oder
zumindest funktionierende Makefile für diese Plattform empfehlen?
Viele Grüße
W.T.
Ich hatte in Beitrag "Entwickeln für STM32 ohne IDE" meine
Makefile für die STM32F4 vorgestellt, wenn du Linker-Script,
Compilerflags und CMSIS nach den Beispielen für dem F1 umbaust, sollte
die Makefile auch damit funktionieren.
Also ich habe hier mal einen Makefile für das STM32F4. Wenn du das nun
nach deinem Beispiel bei Github umbaust, müsste das funktionieren.
Einzig, was mich auch etwas gewundert hat(es ist im Makefile nirgends zu
finden), ist dass die Source-Dateien im interordner src liegen.
MAKEFILE
# put your *.o targets here, make should handle the rest!
SRCS = delay_taster.c system_stm32f4xx.c
# all the files will be generated with this name (main.elf, main.bin,
main.hex, etc)
PROJ_NAME=main
# that's it, no need to change anything below this line!
###################################################
CC=arm-none-eabi-gcc
OBJCOPY=arm-none-eabi-objcopy
CFLAGS = -g -O2 -Wall -Tstm32_flash.ld
CFLAGS += -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork
CFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16
###################################################
vpath %.c src
vpath %.a lib
ROOT=$(shell pwd)
CFLAGS += -Iinc -Ilib -Ilib/inc
CFLAGS += -Ilib/inc/core -Ilib/inc/peripherals
SRCS += lib/startup_stm32f4xx.s # add startup file to build
OBJS = $(SRCS:.c=.o)
###################################################
.PHONY: lib proj
all: lib proj
lib:
$(MAKE) -C lib
proj: $(PROJ_NAME).elf
$(PROJ_NAME).elf: $(SRCS)
$(CC) $(CFLAGS) $^ -o $@ -Llib -lstm32f4
$(OBJCOPY) -O ihex $(PROJ_NAME).elf $(PROJ_NAME).hex
$(OBJCOPY) -O binary $(PROJ_NAME).elf $(PROJ_NAME).bin
clean:
rm -f *.o
rm -f $(PROJ_NAME).elf
rm -f $(PROJ_NAME).hex
rm -f $(PROJ_NAME).bin
OK, das makefile von Lukus scheint sich auf andere Makefiles, die beim
CMSIS beiliegen sollten, die ich aber nicht habe, zu verlassen.
Also probiere ich erst einmal bei dem anderen mein Glück.
Das schlägt allerdings (nachdem man die vom Forum zerstörten Tabs
wiederhergestellt hat) bei der Zeile "make -C lib" fehl mit der
Fehlermeldung: make: *** lib: No such file or directory. Schluss.
Allerdings bin ich mir auch noch nicht sicher, was mit den beiden
vpath-Zeilen gemeint ist.
Walter Tarpan schrieb:> Hallo Lukas,> das sieht auf den ersten Blick schon recht gut aus. Aber welchen Zweck> erfüllt das python-Script?
Das sign.py?
Äh, ja hätte man vielleicht im README erwähnen sollen…
Ich hatte mal damit experimentiert, die Hauptapplikation im Flash nach
hinten zu verschieben, um im ersten Flash-Sektor mithilfe der von
sign.py erzeugten Prüfsumme die Applikation verifizieren zu können und
bei Fehlschlag in den DFU-Bootloader zu fallen, sodass das Gerät —
soweit die Theorie — 'unbrickbar' wird.
In der aktuellen Makefile wir sign.py aber so aufgerufen, dass es
garnichts macht. main.bin.signed ist also mit main.bin identisch.
Das stm32_flash.py bedient OpenOCD um die main.bin zu flashen.
EDIT:
Walter Tarpan schrieb:> OK, das makefile von Lukas scheint sich auf andere Makefiles, die beim> CMSIS beiliegen sollten, die ich aber nicht habe, zu verlassen.
Nene, das sind Makefiles, die in epm-Paketen (siehe
https://github.com/carrotIndustries/epm und
https://github.com/carrotIndustries/epm-availables ) vorhanden sind.
Ziel war es den Komfort, den man von den ganzen neumodischen
Web-Frameworks und Sprachen (node.js, python, ruby) hinsichtlich
Paketmanagement halbwegs in die Embedded-Welt zu tragen.
Da du oben was von CooCox schriebst, rate ich mal, dass du auf Windows
arbeitest, keine Ahnung inwieweit der epm damit auf die Nase fällt… (nie
getestet)
Lukas K. schrieb:> epm-Paketen (siehe> https://github.com/carrotIndustries/epm und> https://github.com/carrotIndustries/epm-availables ) vorhanden sind.> Ziel war es den Komfort, den man von den ganzen neumodischen> Web-Frameworks und Sprachen (node.js, python, ruby) hinsichtlich> Paketmanagement halbwegs in die Embedded-Welt zu tragen.
OK, mit Paket-Managern kenne ich mich zwar nicht aus. Aber ich glaube,
den Komfort brauche ich auch nicht- schließlich geht es um ein einziges
Makefile.
Ich habe mal ein Mini-Projekt gemacht, das sich mit CooCox bauen läßt,
um nicht die Übersicht zu verlieren.
Was mir auffällt, daß CooCox bei seinem Build (Konsolenausgabe siehe
Anhang) gar kein *.s-Startup-File hat. (Ich habe die Konsolenausgabe zur
besseren Lesbarkeit etwas gekürzt - Sprich: Pfade gekürzt und
umgebrochen.)
Eigentlich wundert mich, daß der Konsolenaufruf so kurz ist. Oder
enthält mir da CooCox etwas vor?
Walter Tarpan schrieb:> Das schlägt allerdings (nachdem man die vom Forum zerstörten Tabs> wiederhergestellt hat) bei der Zeile "make -C lib" fehl mit der> Fehlermeldung: make: *** lib: No such file or directory. Schluss.
Das liegt daran, dass du da im Makefile auf bei dir nicht vorhandene
Verzeichnisse zugreifen will.
Also bei mir ibt es die Verzeichnisse:
src; lib; inc
Die Namen sind ja selbsterklärend ;)
Für genauere Infos musst su hier schauen:
https://github.com/jeremyherbert/stm32-templates Und da im Unterordner
stm32f4.
für vpath musst hier
schauen:http://www.gnu.org/software/make/manual/make.html#General-Search
Ich glaube das ist(nach überfliegen des Textes)
dafür da, wo make nach was bestimmtem suchen soll(in diesem Fall *.c
und *.a)
Würde mir obenstehendes makefile gerne auf eigene Bedürfnisse umbauen.
Grundsätzlich verstehe ich aber den Schleifenmechanismus von make nicht.
Wie es aussieht, sind folgende Zeilen wesentlich beteiligt:
1
#Dies sind die am Projekt beteiligten Quelldateien
2
#das Startup File wird später noch dazugehängt
3
SRCS=delay_taster.csystem_stm32f4xx.c
4
5
#Projektname hängt ab von den Quellen (SRCS)
6
$(PROJ_NAME).elf:$(SRCS)
7
8
#das Projekt wird durch (mehrfachen) Aufruf von CC zusammengebaut
9
$(CC)$(CFLAGS)$^-o$@-Llib-lstm32f4
Soweit ich weis, muss CC pro Quelldatei aufgerufen werden und dabei
immer der jeweilige Name der Quelldatei übergeben werden. Das passiert
mit
$^ oder $@ (?) und anschliessend muss gelinkt werden. Wo findet der
Schleifenmechanismus bzw. der wiederholte Aufruf von CC statt und was
ist das Kriterium für make zur Terminierung der Schleife ? Leider bin
ich dazu in div. make Beschreibungen auch nicht schlauer geworden.
Janvi schrieb:> Grundsätzlich verstehe ich aber den Schleifenmechanismus von make nicht.
Welchen Schleifenmechanismus?
> Wie es aussieht, sind folgende Zeilen wesentlich beteiligt:> SRCS = delay_taster.c system_stm32f4xx.c
Hier wird die rekursive Variable SRCS definiert.
> #Projektname hängt ab von den Quellen (SRCS)> $(PROJ_NAME).elf: $(SRCS)
Hier wird eine Regel definiert.
Links stehen die Targets, rechts die Voraussetzungen dafür.
> #das Projekt wird durch (mehrfachen) Aufruf von CC zusammengebaut> $(CC) $(CFLAGS) $^ -o $@ -Llib -lstm32f4
Hier steht ein Befehl. Die Variablen werden aufgelöst.
Zwei besondere Variable sind $^ und $@, es sind sogenannte automatische
Variable (gibt sieben davon in make).
$^ steht für alle Dateinamen der Voraussetzungen, wobei Duplikate schon
entfernt wurden.
$@ steht für den Dateinamen des Targets.
> Soweit ich weis, muss CC pro Quelldatei aufgerufen werden und dabei> immer der jeweilige Name der Quelldatei übergeben werden. Das passiert> mit> $^ oder $@ (?) und anschliessend muss gelinkt werden. Wo findet der> Schleifenmechanismus bzw. der wiederholte Aufruf von CC statt und was> ist das Kriterium für make zur Terminierung der Schleife ? Leider bin> ich dazu in div. make Beschreibungen auch nicht schlauer geworden.
Vereinfacht gesagt, besteht das Makefile aus Regeln. Die werden
ausgewertet und wenn in einer Regel für eine oder mehrere der
vorausgesetzten Dateien wiederum Regeln existieren, werden diese zuerst
ausgewertet. Das Ganze dann rekursiv.
Gibt natürlich noch viele Spezialitäten wie Wildcards, spezielle
Targets, Funktionen, Macros und vieles mehr. Das sollte aber für Deine
Zwecke größtenteils uninteressant sein.
Walter T. schrieb:> Der langen Frage kurzer Sinn: Kann mir jemand ein lesbares oder> zumindest funktionierende Makefile für diese Plattform empfehlen?
Nein.
Es ist hier wieder einmal genau so, wie sonst auch immer: Keiner der
Leute, die auf Makefiles schwören, kann es wirklich.
Stattdessen wird immer wieder irgend ein altes Makefile hergenommen,
daran herum-modifiziert und ausprobiert bis es irgendwie funktioniert.
Ich für meinen Teil hab da das Kind mit dem Bade ausgeschüttet und auf
Makefiles komplettiko verzichtet. Es ist für kleinere Projekte viel
einfacher, sämtliche Compiler- Assembler- und Linker-aufrufe in eine
poplige Batchdatei zu schreiben und später zwecks Übersetzung einfach
dort mit dem modernen Faustkeil (Maus) draufzuhauen.
W.S.
W.S. schrieb:> Walter T. schrieb:>> Der langen Frage kurzer Sinn: Kann mir jemand ein lesbares oder>> zumindest funktionierende Makefile für diese Plattform empfehlen?>> Nein.>> Es ist hier wieder einmal genau so, wie sonst auch immer: Keiner der> Leute, die auf Makefiles schwören, kann es wirklich.
So ein Quark. Warum schließt Du von Dir auf andere?
> Stattdessen wird immer wieder irgend ein altes Makefile hergenommen,> daran herum-modifiziert und ausprobiert bis es irgendwie funktioniert.
Ist vielleicht Deine Vorgehensweise.
> Ich für meinen Teil hab da das Kind mit dem Bade ausgeschüttet und auf
Immerhin erkannt...
> Makefiles komplettiko verzichtet. Es ist für kleinere Projekte viel> einfacher, sämtliche Compiler- Assembler- und Linker-aufrufe in eine> poplige Batchdatei zu schreiben und später zwecks Übersetzung einfach> dort mit dem modernen Faustkeil (Maus) draufzuhauen.
Nö, das ist pauschal falsch. Ein Makefile für kleinere Projekte ist
mindestens ebenso schnell erstellt. Ist ja schließlich keine
Wissenschaft, und mit Sicherheit einfacher als embedded-Programmierung
an sich.
@janvi:
Besorg Dir mal ein gutes Tutorial, da sollte schon alles drin stehen,
was Du für Deine Zwecke brauchst. Das ist nämlich gar nicht so viel.
Ich werf noch das hier in die Runde, das ist ein bare minimum makefile
projekt für STM32F401RE, direkt verwendbar mit dem NUCLEO-F401RE
Demo-Board von ST.
https://github.com/prof7bit/bare_metal_stm32f401xe
W.S. schrieb:> Es ist für kleinere Projekte viel einfacher, sämtliche Compiler-> Assembler- und Linker-aufrufe in eine poplige Batchdatei zu schreiben
Ein Makefile für diese Aufrufe ist genauso einfach:
1
blinky:
2
compile ...
3
assemble ...
4
link ...
Und spätestens, wenn man Targets fürs Flashen/Debuggen/Starten
hinzufügen will, wird das Makefile einfacher als mehrere Batchdateien.
Für kleine Projekt lohnen sich solche Spezialitäten wie implizite Regeln
oder hunderte verschachtelte Variablen wirklich nur, wenn man eine
Vorlage verwendet, die man eh schon verstanden hat.
W.S. schrieb:> sämtliche Compiler- Assembler- und Linker-aufrufe in eine> poplige Batchdatei zu schreiben und später zwecks Übersetzung einfach> dort mit dem modernen Faustkeil (Maus) draufzuhauen.
"Wer als Werkzeug nur einen Hammer hat, sieht in jedem Problem einen
Nagel"
Makefiles sind simpel. Wirklich.
Nicht komplizierter als C-Programme jedenfalls. Im einfachsten Fall
reichen fünf Zeilen:
1
TARGET=xx
2
3
$(TARGET): xx.o
4
cc -o xx $<
5
6
%.o:%.c
7
cc -c $<
Das Target hängt von (ein oder mehreren) Objektfiles ab, die wiederum
von .c-Dateien abhängen. Wie man aus dem einen das andere macht, steht
in den Rules.
Alles andere ist nur Farbe und Politur.
Markus F. schrieb:> Alles andere ist nur Farbe und Politur.
Bis dann das automatisch erstellen von Dependencies auftaucht. Dann ist
wieder Zägen, Schrauben und Schleifen angesagt ;-)
Die Grundregeln von Make erscheinen klar. Allerdings habe ich bislang
immer für jedes Quellfile eine Regel zum erstellen des dazugehörenden
Objekts gemacht. (Bislang auch mit anderen Compilern als GCC) Hat den
Vorteil, daß man z. Bsp. eine einzelnde Datei mit anderen Debug oder
Optimierungsschaltern übersetzen kann wenn man daran gerade debuggt.
Nachteil ist, dass jedesmal wenn im Projekt ein neues File dazukommt
(oder wegfällt bzw. zusammengefasst wird) muß an x Stellen geändert
werden. Mehrere Stellen im Makefile und im Linkerscript.
Daher würde ich den Ansatz mit Schleife bzw. Rekursion gerne näher
verstehen und offensichtlich bin ich nicht der einzige der sowas nur
über probieren rauskriegt.
Es scheint ja, daß man die Files dann so aufbauen kann, daß man die
Quellfiles eben nur in eine Variable schreibt und irgendwas dafür sorgt
dass gcc mehr als einmal aufgerufen wird obwohl man nur eine Zeile als
Aufruf angibt. An der Stelle ist GCC ja auch etwas unübersichtlich da er
intern mehrere Vorgänge inkl. Link u. U. selbst aufrufen kann. Es könnte
also genausogut sein, dass CC eine Liste von Datein abarbeitet und dazu
von make nur einmal aufgerufen wird.
Übrigens habe ich "make" von Helmut Herold (Addison Wesley) als
Beschreibung mit Beispielen und Erklärungen in deutscher Sprache
durchgelesen.
$@ ist dort als "aktuelles Ziel" beschrieben und
$^ ist überhaupt nicht erklärt und taucht auch in keinem Beispiel auf
scheint aber für obenstehende Tricksereien unentbehrlich.
Rekursion habe ich im makefile bislang so verstanden, daß im makefile
includes von weiteren Files sind die wieder auf das eigene Verweisen
aber offensichtlich gibt es noch andere Mechanismen
J. V. schrieb:> Allerdings habe ich bislang immer für jedes Quellfile eine Regel zum> erstellen des dazugehörenden Objekts gemacht. [...]Hat den Vorteil,> daß man z. Bsp. eine einzelnde Datei mit anderen Debug oder> Optimierungsschaltern übersetzen kann wenn man daran gerade debuggt.
Das geht auch mit impliziten Regeln:
1
%.o: %.c
2
$(CC) $(CFLAGS) -c -o $@ $<
3
4
EinzelneDatei.o: CFLAGS = -g -O42
> Rekursion habe ich im makefile bislang so verstanden, daß im makefile> includes von weiteren Files sind die wieder auf das eigene Verweisen
Mit "=" definierte Variablen sind rekursiv (z.B: X = $(Y)).
Die andere Rekursion (aus "Recursive Make Considered Harmful") betrifft
Unterverzeichnisse, für die Make rekursiv aufgerufen wird.
J. V. schrieb:> Die Grundregeln von Make erscheinen klar. Allerdings habe ich bislang> immer für jedes Quellfile eine Regel zum erstellen des dazugehörenden> Objekts gemacht. (Bislang auch mit anderen Compilern als GCC) Hat den> Vorteil, daß man z. Bsp. eine einzelnde Datei mit anderen Debug oder> Optimierungsschaltern übersetzen kann wenn man daran gerade debuggt.
das kann man auch einfacher erreichen, ohne gleich alle Vorteile von
make herzuschenken. Compiler-Optionen lassen sich in Abhängigkeit vom
aktuellen Target setzen:
1
CFLAGS=-O3
2
objs/xxx.o:CFLAGS=-g3
definiert beispielsweise, dass CFLAGS auf "-g3" gesetzt wird, wenn das
aktuelle Target objs/xxx.o heisst. Alle anderen Objektfiles werden mit
dem Defaultwert von CFLAGS übersetzt, nur dieses eine aber eben mit
"-g3".
> Nachteil ist, dass jedesmal wenn im Projekt ein neues File dazukommt> (oder wegfällt bzw. zusammengefasst wird) muß an x Stellen geändert> werden.
Der Sinn von make ist, genau sowas zu vermeiden. Sonst könntest Du auch
eine Batch-Datei verwwenden.
> $@ ist dort als "aktuelles Ziel" beschrieben und> $^ ist überhaupt nicht erklärt und taucht auch in keinem Beispiel auf
$^ ist eine Liste aller Abhängigkeiten des aktuellen Ziels, enthält also
eine Liste aller Files die benötigt werden, um $@ zu erzeugen.
Dr. Pinguin schrieb:> So ein Quark. Warum schließt Du von Dir auf andere?
Ach, noch ein selbsternannter Pinguin-Doktor.
Tja, an den Folgebeiträgen sieht man ziemlich deutlich, daß es offenbar
doch wesentlich einfacher ist, bei kleineren Projekten auf Zeugs wie
make schlichtweg zu verzichten und sich mit ner ganz simplen Batchdatei
zu begnügen.
Aber jeder macht sich seine Probleme aam liebsten halt selbst...
W.S.
W.S. schrieb:> mit ner ganz simplen Batchdatei zu begnügen
Batchdatei wäre mir zu umständlich. Es gibt kein denkbares Szenario beim
Kompilieren und Linken bei dem ne Batchdatei kleiner, einfacher oder
leserlicher wäre als ein Makefile gleich einfacher Funktion.
> $^ ist eine Liste aller Abhängigkeiten
für eine Lister aller Abhängigkeiten steht in anderen Manuals $+
und was ist dann $< schon wieder ?
> Mit "=" definierte Variablen sind rekursiv
Von rekursiven Programmen oder Funktionen bzw. reentrant fähigem Code
habe ich schon gehört aber von rekursiven Variablen noch nie. (Ist das
bei einem Interpreter?) Mal ein Versuch zur Erklärung bzw. wie ich
jetzt annehme dass die Sache funktionieren könnte:
Variablenname = Inhalt1 Inhalt2 ... Inhaltn
funktioniert logisch so ähnlich wie ein typedef in C, also z. Bsp.
enum Ampel{rot, gelb, grün};
oder vielleicht eher praktisch so ähnlich wie ein struct
struct Ampel{rot, gelb, grün);
Zum Interpretationszeitpunkt des Makefiles richtet make einen Zeiger auf
das erste Element (im Bsp. rot). Taucht der Variablenname des Ampeltyps
jetzt in einer Abhängigkeitsbeschreibung auf, so wird die Zeile zur
Erzeugung des Ziels ausgeführt. Für die Quelldatei wird anstelle von $^
der aktuell angezeigte Inhalt der Variablen vom Typ Ampel eingesetzt.
Danach wird der Zeiger weitergerückt und nachgeschaut ob noch ein
weiteres Element im Struct folgt. Falls ja, wird der Aufruf zum Erzeugen
des Ziels wiederholt, falls nein ist die Sache (Zeile) erledigt bzw wird
nachgeschaut ob es noch eine Folgezeile mit Vorschrift zur Erzeugung des
Ziels gibt.
Weil beim Aufruf des Compilers nicht nur die Quelldatei sondern auch der
Name der auszugebenden Objektdatei angegeben werden muß, scheint es
diese Zeile zu geben:
OBJS = $(SRC:.c=.o)
Für jedes Element von SRC welches mit .c endet, wird das gleiche Element
in OBJS eingetragen und das Ende .c durch eine Endung .o ersetzt (?) Die
Bedeutungen von $ oder : in der Zeile sind nicht offensichtlich und
können entweder nur durch Ausprobieren / Raten oder durch eine Anleitung
mit Erklärungen und Beispielen verständlich gemacht werden. Es bleibt
zuletzt noch das Rätsel der Namenswahl von OBJS welches im Makefile nur
ein einziges Mal vorkommt und $@ trotzdem darauf Bezug nehmen kann.
(vordefinierter Name?) Jedenfalls überfordern die GNU Erfinder meine
Inutition hierbei bei weitem wenn sie annmehmen daß dies alles
selbsterklärend ist.
J. V. schrieb:> [Fehlinterpretationen, Falschvermutungen]>> Jedenfalls überfordern die GNU Erfinder meine> Inutition hierbei bei weitem wenn sie annmehmen daß dies alles> selbsterklärend ist.
Das nehmen sie mitnichten an.
Sie nehmen stattdessen an daß Du das Manual mal grob überfliegst und
dann bookmarkst (zum jederzeitigen Nachschlagen) und dann mit ein paar
leicht verständlichen Tutorials anfängst die dich mit vielen einfachen
Beispielen und Erklärungen behutsam in die Materie einführen.
Im Anhang mal ein Makefile, wie ich es aktuell für verschiedene
Mikrocontroller-Familien nutze. Dabei nutze ich nur eigene Bibliotheken,
Linkerfiles etc. und habe einen "festen Aufbau":
- src enthält alle Quelltext-Dateien
- inc enthält ale Projekt-spezifischen Includes
- build hier werden die Objektfiles abgelegt
Mittels "make flash" wird ein Hexfile (bei mir: Motorola-Format) für das
Flash erzeugt und gleich programmiert. Das minimiert das ganze Handling
und man kann sich mehr auf den Code konzentrieren. Mittels "make run"
wird das Projekt so gebaut, dass es komplett im RAM läuft und dann
dorthin übertragen und gestartet. Dafür verwende ich ein getrenntes
Linkerscript. Und mittels "make lib" wird eine linkbare Bibliothek
erzeugt, wobei ich vorher noch die main.c umbenenne, damit mir main()
nicht mit in die Bibliothek rutscht.
Die makefiles für andere Controller-Plattfoermen und den PC sind dabei
fast identisch, auch wenn manchmal der SDCC zum Einsatz kommt.
Angefangen habe ich auch mit Compile-Scripten, aber wenn man wegen jeder
kleinen Änderung das komplette Projekt neu übersetzen muß, wird das auf
die Dauer nervig.
Jörg
J. V. schrieb:> Jedenfalls überfordern die GNU Erfinder meine> Inutition hierbei bei weitem wenn sie annmehmen daß dies alles> selbsterklärend ist.
In Anlehnung an einen Werbespruch:
"Wer hat's erfunden?"
Die GNU's jedenfalls nicht. make ist so alt wie Unix, GNU make ist ein
Klon davon (mit ein paar Erweiterungen).
Das heisst, dass das schon ein paar mehr Leute als bloß "die irren
Linux-Jünger" verstanden haben. Falls das ein Anreiz ist, sich mal
**richtig** reinzudenken.
> Dr. Pinguin schrieb:> es sind sogenannte automatische Variable (gibt sieben davon in make)
Die 7 automatische Variablen von (Gnu)Make nach gefühlten 7 Jahren Make
Studium (Im deutschen Make-Buch von Helmut Herold sind nicht mal alle 7
erwähnt)
$@ enthält den Dateinamen der Zieldatei der davorstehenden Regel
$% Dateiname des Archivs wenn Zieldatei der davorstehenden Regel
in einem Bibliotheksarchiv steht
$< Dateiname der ersten Abhängigkeit der davorstehenden Regel
(Zuwas soll das gut sein ? Wenn weitere Abhängigk. Header sind ?)
$? Dateinamen aller Abhängigkeiten der davorstehenden Regel,
wenn Datum/Uhrzeit des Dateinamens neuer als die Zieldatei ist.
$* Dateiname der Zieldatei der davorstehenden Regel ohne Erweiterung
Nützlich zur Konstruktion von Dateinamen mit anderen Endungen,
Kann mit % wieder gelesen bzw. woanders eingesetzt werden
$+ (bei nmake $**) enhält alle Abhängigkeiten der davorstehenden Regel
$^ (nicht bei nmake) wie $+ jedoch ohne Duplikate (zu was gut ?)
Es scheint, dass gcc auch eine Liste von Quell-Dateien akzeptiert
Rekursive Variablen sind mir in Sinn und Funktion weiter nicht plausibel
und vielleicht trifft dies auch den Kern meiner ersten Frage nach dem
„Schleifenmechanismus“.
PFAD = /USR/HOME
PFAD = $(PFAD):/data/dir
mag als Variable rekursiv sein, kann aber nicht terminieren weshalb es
PFAD := /data/dir gibt
(oder ist das += ?) Aber es scheint, daß ich weitere 7 Jahre in
Stallmannschen Original-Files lesen muß da der Thread hier schon
reichlich verfahren ist. Vielleicht schaut aber doch mal wieder ein
netter Linuxer vorbei der noch andere Tipps als Manual lesen hat ...
sucht in ./src/ rekursiv nach .c und .s Dateien. Zuerst wird mit find
nach den Source-Dateien gesucht. (-> CSOURCES). Dann wird src durch
build ersetzt und .c bzw. .s in .o
Der Aufruf erfolgt eigentlich "von hinten", d.h. die Regel für die
Source wird von der Regel für die Objektdateien aufgerufen. Da ich oft C
und ASM mixe, habe ich dafür unterschiedliche Regeln vorgesehen, z.B.
wenn man einen "externen" Assembler verwendet.
Kompiliert wird mit:
1
bin: dir $(COBJECTS) $(AOBJECTS)
2
...
3
build/%.o: ./%.c
4
mkdir -p $(dir $@)
5
$(CC) $(CFLAGS) -c -o $@ $<
6
7
build/%.o: ./%.s
8
mkdir -p $(dir $@)
9
$(CC) $(CFLAGS) -c -o $@ $<
10
11
dir:
12
mkdir -p build
13
...
Man braucht halt find dazu, aber das sollte ja eh auf jedem
Linux-System vorhanden sein. Ich benutze das schon seit ein paar Jahren
so, genauer erklären kann ich es jetzt auch nicht.
Im Anhang noch ein Makefile, wie ich es auf dem PC benutze. Letztendlich
ist es von der Struktur her fast identisch.
Hab' ich so ähnlich eine Weile auch mal benutzt, es dann aber (wegen
schlechter Erfahrungen) wieder gelassen. Sicherheit vor Bequemlichkeit.
Es hat den Nachteil, dass man (insbesondere bei komplexen und/oder
grossen Projekten) u.U. nicht merkt, wenn eine Quelldatei im Source-Tree
verloren geht oder (möglicherweise schlimmer) sich eine "einschmuggelt"
die da nichts verloren hat.
Mit der Suche nach so einem Fehler kann man u.U. Stunden verbringen.
Mir ist es lieber, wenn das Makefile "weiss", was zum Projekt gehört und
was nicht (also eine explizite "SRCS-Variable" oder ähnliches hat). Dann
kann man auch mal eine Quelle, die eigentlich nur dazu gedacht ist,
irgendwas auszuprobieren, im Sourceverzeichnis liegen lassen, ohne dass
gleich Schlimmeres passiert.
Markus F. schrieb:> Das heisst, dass das schon ein paar mehr Leute als bloß "die irren> Linux-Jünger" verstanden haben. Falls das ein Anreiz ist, sich mal> **richtig** reinzudenken.
Es gibt schier unendlich vieles, was mal aktueller Stand ser Technik war
und inzwischen obsolet ist. z.B. Feuersteinklingen und Faustkeil.
Das Prinzip ist ubiquitär. Denk mal zum Beispiel an tar. Inzwischen gibt
es keine Lochbänder mehr, aber "die irren Linux-Jünger" benutzen das
immer noch, bloß weil sie nicht im Geringsten verstanden haben, wozu die
Neuerung beim zip Format denn gut sein sollen.
Auf dem Lochband konnte man von Hand anhand der 256 Byte langen nul
Vorläufe und der oktal gestanzten Header erkennen, wo im Archiv eine
Datei denn losgeht und sogar, wie sie heißt und wie lang sie ist. Wer
kann das bei einem tar auf dem Stick? oder gar bei einem tgz irgendwo?
Aber die Funktionalität bei zip, daß man tatsächlich auf alle einzelnen
Files im Archiv separat zugreifen kann, ohne alles zuvor auszupacken,
haben diese ewig Gestrigen nicht kapiert.
Genau so geht es mit den Makefiles. Sind Schnee von gestern, ach was sag
ich, von vorgestern. Der ursprüngliche Sinn ist perdue, der bestand
nämlich aus dem datumsabhängigen Kompilieren nur der Dateien, die es
nötig hatten - weil damals die Rechner eben dramatisch langsamer waren.
Ob nun heutzutage ein PC bei deinem µC-Programm 1 Sekunde oder nur 100
ms zum kompletten Übersetzen von allen Quellen braucht, ist völlig egal.
Stattdessen erwarten die Make-Jünger, daß man sich in die durchaus nicht
triviale Syntax von Make einarbeitet - aus meiner Sicht so überflüssig
wie ein Kropf.
Jaja, richtig gute Programmiersprachen haben sowas ins Grundkonzept
eingebaut, wie (allen voran) Pascal mit seiner Teilung von Interface ud
Implementation. Aber das nur zu Anheizen der Stimmung...
W.S.
W.S. schrieb:
> Genau so geht es mit den Makefiles. Sind Schnee von gestern, ach was sag> ich, von vorgestern. Der ursprüngliche Sinn ist perdue, der bestand> nämlich aus dem datumsabhängigen Kompilieren nur der Dateien, die es> nötig hatten - weil damals die Rechner eben dramatisch langsamer waren.
Warte nur, bis alle Atomkraftwerke abgeschaltet sind, der Strom
rationiert wird und der PC mit 3 Watt auskommen muss!
Obwohl, dann bekommt "Batch File" vielleicht wieder seine ursprüngliche
Bedeutung - wenn Leute ohne Kühlschrank Rechenleistung verkaufen...
;)
P.S.: Makefiles für bestimmte Chips sind noch kranker als Batch Files.
W.S. schrieb:> Markus F. schrieb:>> Das heisst, dass das schon ein paar mehr Leute als bloß "die irren>> Linux-Jünger" verstanden haben. Falls das ein Anreiz ist, sich mal>> **richtig** reinzudenken.>> Es gibt schier unendlich vieles, was mal aktueller Stand ser Technik war> und inzwischen obsolet ist. z.B. Feuersteinklingen und Faustkeil.>> Das Prinzip ist ubiquitär.
Super Wort. Musste ich googeln, aber werde ich mir merken.
> Denk mal zum Beispiel an tar. Inzwischen gibt> es keine Lochbänder mehr, aber "die irren Linux-Jünger" benutzen das> immer noch, bloß weil sie nicht im Geringsten verstanden haben, wozu die> Neuerung beim zip Format denn gut sein sollen.>> Auf dem Lochband konnte man von Hand anhand der 256 Byte langen nul> Vorläufe und der oktal gestanzten Header erkennen, wo im Archiv eine> Datei denn losgeht und sogar, wie sie heißt und wie lang sie ist. Wer> kann das bei einem tar auf dem Stick? oder gar bei einem tgz irgendwo?>> Aber die Funktionalität bei zip, daß man tatsächlich auf alle einzelnen> Files im Archiv separat zugreifen kann, ohne alles zuvor auszupacken,> haben diese ewig Gestrigen nicht kapiert.>
Erfreulicherweise gibt's für jeden Zweck passende Werkzeuge und ihr
vernünftiger Einsatz ist nur durch die Intelligenz des Anwenders
beschränkt.
Zip ist prima, um (einige wenige) Dateien zu komprimieren. Versuch' mal
ein 10TB Magnetband mit zip zu füllen. Da werden deine Millisekunden
plötzlich reichlich lang. Wenn Du's dann doch geschafft hast und nach
fünf Jahren das vorletzte Bit gekippt ist, kannst Du mit zip kein
einziges Byte des Bandes mehr lesen. Tar kann das.
Wer als Werkzeug nur einen Hammer hat, sieht in jedem Problem einen
Nagel.
> Genau so geht es mit den Makefiles. Sind Schnee von gestern, ach was sag> ich, von vorgestern. Der ursprüngliche Sinn ist perdue, der bestand> nämlich aus dem datumsabhängigen Kompilieren nur der Dateien, die es> nötig hatten - weil damals die Rechner eben dramatisch langsamer waren.>> Ob nun heutzutage ein PC bei deinem µC-Programm 1 Sekunde oder nur 100> ms zum kompletten Übersetzen von allen Quellen braucht, ist völlig egal.> Stattdessen erwarten die Make-Jünger, daß man sich in die durchaus nicht> triviale Syntax von Make einarbeitet - aus meiner Sicht so überflüssig> wie ein Kropf.>
Selbe Geschichte. Trivialprogramme, die nur aus drei Quellfiles
bestehen, lassen sich natürlich ohne grosse Einschränkungen "einfach so"
zusammenklopfen, ohne Hirnschmalz in ein Makefile zu stecken.
Meine Programme sind üblicherweise ein wenig komplexer, als dass sie
schon nach einer Sekunde übersetzt wären und ich bin ehrlich gesagt
nicht gewillt, meine kostbare Lebenszeit damit zu vergeuden, auf ein
dumbes Batchfile zu warten, das Dinge tut, die gar nicht notwendig sind.
Wem's schon zu viel ist, ein bisschen Denkleistung in ein Makefile zu
stecken und damit den Build-Prozess intelligent und stabil zu gestalten,
weil Computer ja heute ach so schnell sind und alles viel besser können,
dessen Code möchte ich gar nicht erst sehen.
Wer als Werkzeug nur einen Hammer ...
Schönen Sonntag!
Ein Makefile, das nur unter Linux funktioniert, finde ich für ein
µC-Programm nicht sinnvoll. (Für Linux-Installationspakete sieht das
natürlich anders aus.)
Für mich hat ein Makefile (u.A.) gerade den Zweck, nicht mehr auf eine
bestimmte build-Umgebung angewiesen zu sein, sondern Projekte
plattformübergreifend erzeugen zu können.
Walter T. schrieb:> Ein Makefile, das nur unter Linux funktioniert, finde ich für ein> µC-Programm nicht sinnvoll.
Make und die anderen GNU Tools gibts ja zum Glück auch für Windows, man
muss schon ganz schönen Mist bauen um versehentlich ein Makefile zu
produzieren das unter Linux funktioniert aber nicht unter Cygwin.
>man muss schon ganz schönen Mist bauen um versehentlich ein Makefile zu>produzieren das unter Linux funktioniert aber nicht unter Cygwin.
So arg braucht man keinen Mist zu bauen sondern nur in der Community
anhören daß man eben die Manuals lesen soll anstelle faul zufragen. Bei
freier Software haben Andere schon den Mist für einen gebaut und man
braucht sich nur wundern warum man zu blöde dazu ist und nix geht:
https://bugs.launchpad.net/gcc-arm-embedded/+bug/1282943
Wie gut, daß man da alle 1282843 known bugs nachlesen kann und im
Gegensatz zu kommerziellen Compilern mit vielleicht funktionierenden
IDEs kostet das dann auch nix extra. Da schadet es dann zumindest nichts
wenn man beim Anschauen sofort sieht ob eine automatische Variable jetzt
rekursiv ist oder nicht. Die GNU kompatible Teilmenge der automatischen
Variablen sind im Buch von Helmut Herold übrigens als "interne Makros"
beschrieben wo man als Neuling auch jederzeit alleine draufkommen kann
um dann gleich zu wissen ob und was man selbst falsch gemacht hat.
Jedenfalls lächeln verschiedene Leute um mich rum immer nur wenn ich mal
wieder erzähle, daß man seine Quellen nicht in "Eigene Dateien" ablegen
soll, weil das dazugehörenden Programme den Pfad wegen dem Leerzeichen
sonst nicht mehr findet. Und das mit den Umlauten in Dateinamen ist
genauso aktuell und kaum von vorgestern.
Walter T. schrieb:> Für mich hat ein Makefile (u.A.) gerade den Zweck, nicht mehr auf eine> bestimmte build-Umgebung angewiesen zu sein, sondern Projekte> plattformübergreifend erzeugen zu können.
Ach Walter, du wirst immer an irgend einer Stelle eingreifen müssen -
und sei es nur, um die nötigen Pfadvorgaben auf einer anderen Maschine
oder einem anderen Toolset anzupassen.
Insofern ist diese ganze Diskussion unsinnig. Diejenigen, die selbst für
ein 10 Quellen Projekt ein Makefile aufsetzen (oder zumindest dies hier
behaupten), wird man mit Vernunft nicht umstimmen können und diejenigen,
denen das zu blöd ist, greifen zu irgend einer IDE, in der Hofnung, daß
die es ihnen besorgt. Und beide Sorten glauben den Verstand mit
Schöpfkellen gelöffelt zu haben.
Ach, was soll's.
W.S.
W.S. schrieb:> Ach, was soll's.
Genau. Was soll's.
Für mich gehört zu ernsthafter Softwareentwicklung nicht nur das
Verständnis der Programmiersprache und der Algorithmen, sondern genauso
auch das für die Tools (das betrifft nicht nur make, sondern auch die
Toolchain und das (ein) VCS). Nur in der (richtigen) Kombination der
Tools und der richtigen Anwendung ist vernünftige Reproduzierbarkeit
(=Qualität) zu gewährleisten.
Ob das Build-Tool nun make oder CMake oder noch was anderes ist, ist
eigentlich wurscht, aber der Standard ist nun mal make.
Und um auch noch was Konstruktiv(er)es beizutragen: meine Makefiles
enthalten meist folgendes Target, das manchmal ganz hilfreich ist, wenn
man nicht mehr durchblickt:
Habe bei Jörg Wolfram jetzt kräfig abgeschrieben und es funktioniert.
Leider weis ich nicht warum.
Die Abhängigkeit für den Compiler %,o: %.c hatte ich zunächst
1
$(OBJS): $(CSOURCES)
Damit wird gcc nur einmal mit einer Liste von Quelldateien aufgerufen,
was wohl grundsätzlich geht mir aber unübersichtlich erscheint und im
konkreten Fall zudem zu Fehlern führt. (fatal error: cannot specify -o
with -c with multiple files)
Mit %.o: %.c wird gcc pro Quelle je einmal aufgerufen. Das ist gut aber
ich weis nicht warum. Ist das eine (implizite?) Pattern-Regel?
Eigentlich müssten dafür ja die Variablen $@ und $^ zuständig sein und
die werden zwischen funktionierender und nicht funktionierender Version
nicht mal verändert. Ein Tausch von $^ in $? bringt scheinbar auch keine
Änderung.