Forum: Compiler & IDEs Bräuchte ein paar Tipps für großes Projekt mit gcc und make


von Jürgen S. (jsachs)


Lesenswert?

Hallo,

ich weiss nicht ob das so richtig hier her passt, aber ich finde keinen 
besseren Platz.
Kurz zu dem/die Projekten. Ich brauche da ein paar Tipps wie man das mit 
Make lösen kann.

Es handelt sich um ein Projekt das bisher aus 3 ATmegas, mit 
unterschiedlichem Takt, besteht.
Kurz zur groben bisherigen File Struktur
source/global_files/
source/tx_main/bootloader
source/tx_main/main
source/tx_main/lcd
source/rx_main/bootloader
source/rx_main/main
source/pc/

unter global Files liegen bisher Include Files die in allen anderen 
referenziert werden mit defines, structs etc.
Hier sollen nun auch "c" Files hin mit Routinen die überall gleich sind. 
Da zum Teil die ATmegas unterschiedlich sind und die Taktfrequenz, fällt 
eine lib weg.

Nun dachte ich, ich kann die "c" File im make das z.B. in 
"source/tx_main/main" liegt relativ einbinden in der Art: 
"../../global_files" so wie bei den Includes. Das geht leider nicht. 
Hier kommt eine Fehlermeldung das das Object File nicht erzeugt werden 
konnte, da dass object Directory fehlt (müsste ich genau nochmal 
ausprobieren wenn notwendig).

Wie löst man so etwas ? Das wird doch bestimmt in vielen Projekten 
benötigt.
Rekursive makes ?
Wie gebe ich dann Parameter wie CPU und FCPU weiter ?
Wäre es dann ein make das alle "Projekt Teile" auf einmal erstellt ?

Falls wichtig, ich entwickle unter Linux und das Projekt liegt unter SVN 
Kontrolle. Von "Soft" links würde ich gerne Abstand nehmen.

Für Tipps bin ich dankbar
Juergen

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

In meinen — allerdings nur privaten — Projekten habe ich mir dazu ein 
kleines Quell-Repository angelegt, weil wie gesagt Libs ausscheiden 
wegen mangelnder Skalierbarkeit (SFRs, Defines, F_CPU, Port-Definitionen 
im Projekt, etc).

Im Makefile gebe ich dann die Quellen an, die aus dem Repository 
stammen:
1
COMMON      = ../common
2
COMMON_SRC  = uart-simple.c uart.h soft-reset.h ...
3
SRC         = main.c uart-simple.c uartio.c ...
und im projektunabhängigen Makefileschnippel dann
1
.PHONY: clean
2
.PHONY: copies copyclean dependclean realclean
3
4
.PRECIOUS: $(COMMON_SRC)
5
6
copies: $(COMMON_SRC)
7
8
copyclean:
9
  rm -f $(wildcard $(COMMON_SRC))
10
11
realclean: dependclean copyclean clean
12
13
$(COMMON_SRC): % : $(COMMON)/%
14
  cp $< $@
15
16
dependclean:
17
  rm -f .depend
18
19
.depend: $(COMMON_SRC) $(EXTRA_DEPEND) $(SRC)
20
  $(CC) $(CCLANG) -MM $(SRC) -mmcu=$(MCU_TARGET) $(DEFS) $(INCLUDES) \
21
    | sed -e 's/\.o\:/.s:/' \
22
    > .depend
23
24
-include .depend
SRC sind die "normalen" Quellen, die Compiliert/Assembliert werden und 
COMMON_SRC die sachen aus dem Repository.

D.h. die Quellen aus dem Repository werden einmal ins Projektverzeichnis 
kopiert und bleiben dann dort und können dann dort auch überschrieben 
werden (bei lokalen Änderungen ist es sinnvoll sie aus COMMON_SRC zu 
entfernen).

Ansonsten wird bei einer Änderung im Repository die Arbeitskopie 
überschrieben.

Anstatt zu kopieren, kann man natürlich auch aus SVN oder git 
auschecken; für meine Bastlprojekte führe ich kein VCS.

Dieser Ansatz ist dann praktikabel, wenn die Quellen im Quell-Repository 
hinreichend stabil und allgemein genug sind, um nicht doch für jedes 
Projekt wieder Anpassungen machenzu müssen.

Dazu gehören zB UART-Implementierungen, die zwar noch vom aktuellen µC 
anhängen per -mmcu oder avr/io.h, aber sonst keine Änderungen mehr 
benötigen ausser zB per Makro ob man eine Poll-Version mächte oder eine 
gepufferte, Interrupt-getriebene Verson. Anderer typischer Use-Case sind 
Makros für die Port-Zugriffe wie SET(PORT_LED), MAKE_IN(PORT_LED), 
MAKE_OUT(PORT_LED), TOGGLE(PORT_LED), etc.

Daneben gibt es bestimmt auch viele andere, professionellere Ansätze ;-) 
Aber für meine Projekte tut's das schon seit langem und es ist einfach, 
ein neues Projekt zusammenzustöpseln.

von Klaus F. (kfalser)


Lesenswert?

Jürgen Sachs schrieb:
> Wie löst man so etwas ? Das wird doch bestimmt in vielen Projekten
> benötigt.
> Rekursive makes ?
Ob Du es rekursives Make nennst oder nicht, aber das Aufteilen in je ein 
Makefile für jedes zu erzeugende Programm ist sicher sinnvoll. Die 
einzelnen Makes werden dann kleiner und übersichtlicher.
Ein übergeordnetes Makefile ruft dann diese 3 makes auf.
Du musst eben auch beachten, dass die komplierten Objects für die 
gemeinsam benutzten Quellen in separate Verzeichnisse müssen, weil sie 
für unterschiedliche Prozessoren kompiliert werden müssen.
Ich würde zusätzliche Verzeichnisse anlegen

implement/tx_main
implement/rx_main
implement/pc

und in diesen macht je ein makefile aus den relativ referenzierbaren 
Quellen das jeweilige Programm.
Damit die Quellen gefunden werden musst Du entweder eine eigen Regel 
schreiben, z.B.
%.o : ../source/global_files/%.c
    $(CC) ...

mit der man aus .c files in ../source/global_files ein gleichnamiges obj 
im aktuellen Verzeichnis erzeugst, oder du arbeitest mit VPATH, damit 
kann man auch den Ort von Quellen angeben.

> Wie gebe ich dann Parameter wie CPU und FCPU weiter ?
Über die Parameter für das make
make CPU=$(CPU) FCPU=$(FCPU)

> Wäre es dann ein make das alle "Projekt Teile" auf einmal erstellt ?
Ja, siehe oben

von Wolfgang H. (Firma: AknF) (wolfgang_horn)


Lesenswert?

Hi, Jürgen,

> Es handelt sich um ein Projekt das bisher aus 3 ATmegas, mit
> unterschiedlichem Takt, besteht.

Verstanden.
Aber wo habe ich überlesen, wie die Konfiguration nun aussehen soll?
Vielleicht ein Atmega, der die  drei bisherigen Programme enthält, der 
in jede der bisherigen drei Fassungen gesetzt werden kann und dann 
anhand eines Merkmals erkennt, wo er sitzt und welches Programm 
abzuarbeiten hat?

Oder soll das beim Kompilieren geschehen?


Ansonsten - ich habe mit GCC schon .c-Dateien aus anderen Directories 
eingebunden. Aber die main.c, bzw. die mit dem Namen des Programms, 
befand sich dabei immer in dem Directory, in dem ich dann auch 
kompiliert habe.

Ciao
Wolfgang Horn

von Jsachs (Gast)


Lesenswert?

jeder Teil
- tx_main
-rx_main
-tx_lcd
benutzt einen eigenen Prozessor, der vom Typ her aber fest steht.

also tx_main hat einen bootloader und den "main" part der auf einem 
atmega32 mit 16MHz läuft.
Analog für die rx_main (m32@16MHz) und tx_lcd (m16@8MHz).
Diese benutzen teile aus global Files (I2C, Uart, Umwandelung Routinen)

Es ist auch ausreichend immer nur ein Projekt zu erstellen

Gruss
Juergen

von Wolfgang H. (Firma: AknF) (wolfgang_horn)


Lesenswert?

Danke, Jsachs,

jetzt sehe ich klar.
Aber ich bekenne auch - den prozessorspezifischen Bootloader habe ich 
immer getrennt von der Application entwickelt.
Defines und Teile der Sourcen waren identisch.
Die Fertigung hat dann mit Boundary Scan beides geflasht.

Ciao
Wolfgang Horn

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Jürgen Sachs schrieb:
> Hier kommt eine Fehlermeldung das das Object File nicht erzeugt werden
> konnte, da dass object Directory fehlt (müsste ich genau nochmal
> ausprobieren wenn notwendig).

Genaue Fehlermeldung wäre sicherlich hilfreich.

Auf jeden Fall sollte sowas mit expliziten Regeln zum Compilieren
gehen, weil man dabei die Quell- und Ausgabedatei separat festlegen
kann:
1
COMMON_SRC = ../../common
2
3
...
4
5
foo.o: $(COMMON_SRC)/foo.c
6
        $(CC) $(CFLAGS) -c -o foo.o $(COMMON_SRC)/foo.c

Mit Suffixregeln gibt es keinen generischen Ansatz, wobei GNU make,
wie ich den üblichen "creeping featurism" kenne, dafür sicher irgend-
einen Hack, ähm, Hook bietet. ;)

von Jürgen S. (jsachs)


Lesenswert?

Jörg Wunsch schrieb:
> Jürgen Sachs schrieb:
>> Hier kommt eine Fehlermeldung das das Object File nicht erzeugt werden
>> konnte, da dass object Directory fehlt (müsste ich genau nochmal
>> ausprobieren wenn notwendig).
>
> Genaue Fehlermeldung wäre sicherlich hilfreich.
1
Compiling C: ../../global_files/uart.c
2
avr-gcc -c -mmcu=atmega32 -I. -gdwarf-2 -DF_CPU=16000000UL -O0 -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wundef -Wa,-adhlns=obj/../../global_files/uart.lst  -std=gnu99 -Wundef -MD -MP -MF .dep/uart.o.d ../../global_files/uart.c -o obj/../../global_files/uart.o
3
Assembler messages:
4
Fatal error: can't create obj/../../global_files/uart.o: No such file or directory
5
make: *** [obj/../../global_files/uart.o] Fehler 1
Warum der Fehler kommt ist mir klar, weil als Ursprung das "obj" 
genommen wird und nicht "."

> Auf jeden Fall sollte sowas mit expliziten Regeln zum Compilieren
> gehen, weil man dabei die Quell- und Ausgabedatei separat festlegen
> kann:
>
>
1
> COMMON_SRC = ../../common
2
> 
3
> ...
4
> 
5
> foo.o: $(COMMON_SRC)/foo.c
6
>         $(CC) $(CFLAGS) -c -o foo.o $(COMMON_SRC)/foo.c
7
>
>
> Mit Suffixregeln gibt es keinen generischen Ansatz, wobei GNU make,
> wie ich den üblichen "creeping featurism" kenne, dafür sicher irgend-
> einen Hack, ähm, Hook bietet. ;)
Verstehe ich das richtig, das ich das nicht mit
%o: $COMMON_SRC ....
Also eine Regel für alle Files machen kann...

Dann muss ich mir make nochmals genau ansehen. Und mach das durch 
"Rekursive".
Ich hab nur noch kein HowTo für make gefunden, das ich verstehe :-(

Gibt es kein "Auto make" das nur die "c" files bekommt und den rest 
Automatisch festlegt ?

Gruss
Juergen

von Klaus F. (kfalser)


Lesenswert?

Jürgen Sachs schrieb:
> Verstehe ich das richtig, das ich das nicht mit
> %o: $COMMON_SRC ....
> Also eine Regel für alle Files machen kann...

Jein.
Solche Regeln nennt man static pattern rules.

Du brauchst aber 2 Regeln, weil Du 2 "Arten" von obj-files hast: die von 
common und die vom Projekt.

OBJ_COMMON  := c1.o c2.o
OBJ_PROJECT := p1.o p2.o

#rule 1, gilt für c-files aus common
$(OBJ_COMMON) : %.o : $(COMMON_SRC)/%.c
   $(CC) .....

#rule 2, gilt für c-files aus lokalem directory
$(OBJ_PROJECT) : %.o : %.c   #rule 2,
   $(CC) .....

Jürgen Sachs schrieb:
> Gibt es kein "Auto make" das nur die "c" files bekommt und den rest
> Automatisch festlegt ?
Weiss nicht. Wäre zu einfach und wo bleibt dann der Spass :-)

von JSachs (Gast)


Lesenswert?

danke,

Teste ich am Wochenende.

juergen

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

automake gibt es natürlich, und möglicherweise könnte es das auch
können, was du da willst — aber ob du dich darin erst einarbeiten
magst, wage ich zu bezweifeln. ;-)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.