gon1332 Δημοσ. 14 Ιουλίου 2014 Δημοσ. 14 Ιουλίου 2014 Καλησπέρα σας, σκαλίζω εδώ και 2 μέρες ένα project που έχω σε C. Δε μου άρεσε που είχε χύμα τα αρχεία κώδικα, οπότε είπα να τα μαζέψω σε φακέλους. Το θέμα μου τώρα είναι ότι ένα σημείο του makefile δεν εκτελείται ενώ πριν όλα δούλευαν μια χαρά. Το tree του project είναι το εξής: fort320 |-- include | |-- DebugInfo | | `-- errcheck.h | |-- InputBuffer | | `-- mylist.h | |-- IR | | `-- AST.h | |-- SymbolTable | | `-- hash_t.h | `-- Utils | `-- utils.h |-- makefile |-- makefile~ |-- mytest.f `-- src |-- fort320_la.l |-- fort320_main.c |-- fort320_sa.y `-- lib |-- DebugInfo | `-- errcheck.c |-- InputBuffer | `-- mylist.c |-- IR | `-- AST.c |-- SymbolTable | `-- hash_t.c `-- Utils `-- utils.c 13 directories, 16 files To makefile μου είναι το εξής: #=-------------------------------------------=# # Makefile for lexical and syntactic analyzer # # FORT320 # #=-------------------------------------------=# OBJS = fort320_ast_t.o fort320_utils.o fort320_list.o (1)fort320_sa.o\ fort320_la.o fort320_main.o fort320_hash_t.o fort320_errchck.o # Paths # INCLUDE = ./include LIB = ./src/lib SURFACE = ./src BUILD = ./build # Compiler stuff # CC = gcc CFLAGS = -pedantic# -g -Wall -Wextra CLIBS = -lfl -lm fort320: build_dir $(OBJS) $(CC) $(CFLAGS) $(OBJS) -o ./fort320 $(CLIBS) build_dir: mkdir $(BUILD) # Our extra libraries # fort320_utils.o: $(LIB)/Utils/utils.c $(INCLUDE)/Utils/utils.h $(CC) $(CFLAGS) -c $(LIB)/Utils/utils.c -o $(BUILD)/fort320_utils.o fort320_list.o: $(LIB)/InputBuffer/mylist.c $(INCLUDE)/InputBuffer/mylist.h $(CC) $(CFLAGS) -c $(LIB)/InputBuffer/mylist.c -o $(BUILD)/fort320_list.o fort320_hash_t.o: $(LIB)/SymbolTable/hash_t.c $(INCLUDE)/SymbolTable/hash_t.h $(CC) $(CFLAGS) -c $(LIB)/SymbolTable/hash_t.c -o $(BUILD)/fort320_hash_t.o fort320_ast_t.o: $(LIB)/IR/AST.c $(INCLUDE)/IR/AST.h $(CC) $(CFLAGS) -c $(LIB)/IR/AST.c -o $(BUILD)/fort320_ast_t.o fort320_errchck.o: $(LIB)/DebugInfo/errcheck.c $(INCLUDE)/DebugInfo/errcheck.h $(CC) $(CFLAGS) -c $(LIB)/DebugInfo/errcheck.c -o $(BUILD)/fort320_errchck.o # Syntactical Parser # (1)fort320_sa.o: (2)$(SURFACE)/fort320_sa.c $(CC) $(CFLAGS) -c $(SURFACE)/fort320_sa.c -o $(BUILD)/fort320_sa.o (2)fort320_sa.c: $(SURFACE)/fort320_sa.y bison -dv $(SURFACE)/fort320_sa.y mv fort320_sa.tab.c $(SURFACE)/fort320_sa.c cmp -s fort320_sa.tab.h tok.h || cp fort320_sa.tab.h tok.h # Lexical Analyzer # fort320_la.o: fort320_la.c $(CC) $(CFLAGS) -c $(SURFACE)/fort320_la.c -o $(BUILD)/fort320_la.o fort320_la.c: $(SURFACE)/fort320_la.l flex $(SURFACE)/fort320_la.l mv lex.yy.c $(SURFACE)/fort320_la.c # The main program # fort320_main.o: $(SURFACE)/fort320_main.c $(CC) $(CFLAGS) -c $(SURFACE)/fort320_main.c -o $(BUILD)/fort320_main.o #fort320_la.o fort320_sa.o fort320_main.o: $(INCLUDE)/InputBuffer/mylist.h #fort320_la.o fort320_main.o : tok.h clean: rm -f $(SURFACE)/*~ $(INCLUDE)/*~ $(LIB)/*~ $(INCLUDE)/DebugInfo/*~\ $(INCLUDE)/InputBuffer/*~ $(INCLUDE)/IR/*~ $(INCLUDE)/SymbolTable/*~\ $(INCLUDE)/Utils/*~ $(LIB)/DebugInfo/*~ $(LIB)/InputBuffer/*~\ $(LIB)/IR/*~ $(LIB)/SymbolTable/*~ $(LIB)/Utils/*~ rm -f -r $(BUILD) rm -f $(SURFACE)/fort320_la.c $(SURFACE)/lex.yy.c $(SURFACE)/fort320_sa.c\ $(SURFACE)/tok.h $(SURFACE)/fort320_sa.tab.c $(SURFACE)/fort320_sa.tab.h\ $(SURFACE)/fort320_sa.output ./fort320 Δρόμος προβλήματος σε γραμμές: 5 ---> 41 ---> 44. Μέσα σε παρενθέσεις έχω τη σειρά των βημάτων που περιμένω. To πρόβλημά μου είναι ότι αφού δημιουργηθούν με επιτυχία τα .ο μέχρι το fort320_list.o, για αυτό που έχω επιλέξει όταν φτάσει στο βήμα (2) μου τρέχει αυτές τις εντολές: yacc src/fort320_sa.y mv -f y.tab.c src/fort320_sa.c ενώ εγώ περίμενα: bison -dv $(SURFACE)/fort320_sa.y mv fort320_sa.tab.c $(SURFACE)/fort320_sa.c cmp -s fort320_sa.tab.h tok.h || cp fort320_sa.tab.h tok.h Γνωρίζει κανείς τι μπορεί να φταίει; Σε περίπτωση που βοηθήσει, όταν είχα ξεκινήσει την εργασία δούλευα σε Ubuntu 13.10 ενώ τώρα δουλεύω σε Kali. Σημείωση: Είναι λίγο (έως πολύ) μπάχαλο. Αν έχετε να προτείνετε κάτι για να συμμαζέψω το makefile χτυπήστε άφοβα. Δεν έχω κάτσει να μάθω τα in και τα outs του make, οπότε αντιμετωπίζω συχνά προβληματάκια.
imitheos Δημοσ. 14 Ιουλίου 2014 Δημοσ. 14 Ιουλίου 2014 fort320 `-- src |-- fort320_sa.y To makefile μου είναι το εξής: fort320: build_dir $(OBJS) $(CC) $(CFLAGS) $(OBJS) -o ./fort320 $(CLIBS) build_dir: mkdir $(BUILD) fort320_sa.o: (2)$(SURFACE)/fort320_sa.c $(CC) $(CFLAGS) -c $(SURFACE)/fort320_sa.c -o $(BUILD)/fort320_sa.o fort320_sa.c: $(SURFACE)/fort320_sa.y bison -dv $(SURFACE)/fort320_sa.y mv fort320_sa.tab.c $(SURFACE)/fort320_sa.c cmp -s fort320_sa.tab.h tok.h || cp fort320_sa.tab.h tok.h To πρόβλημά μου είναι ότι αφού δημιουργηθούν με επιτυχία τα .ο μέχρι το fort320_list.o, για αυτό που έχω επιλέξει όταν φτάσει στο βήμα (2) μου τρέχει αυτές τις εντολές: yacc src/fort320_sa.y mv -f y.tab.c src/fort320_sa.c ενώ εγώ περίμενα: bison -dv $(SURFACE)/fort320_sa.y mv fort320_sa.tab.c $(SURFACE)/fort320_sa.c cmp -s fort320_sa.tab.h tok.h || cp fort320_sa.tab.h tok.h Γνωρίζει κανείς τι μπορεί να φταίει; Όταν τρέχεις σκέτο make χωρίς κάποιο target, το make προσπαθεί να φτιάξει το πρώτο target που θα βρει. Αυτό στη περίπτωσή σου είναι το fort320. Οι εξαρτήσεις του target σου είναι build_dir και τα OBJS. Παίρνει με τη σειρά τις εξαρτήσεις οπότε αφού φτιάξει τον κατάλογο αρχίζει το compile των OBJS. Κάποια στιγμή έρχεται η σειρά του fort320_sa.o το οποίο έχει ως εξάρτηση το src/fort320_sa.c. Υπάρχει όμως κάποιο rule στο makefile σου για το src/fort320_sa.c ? When in doubt, "make -r". Τρέχοντας make -r παίρνουμε το παρακάτω μήνυμα που σε ενημερώνει ότι δεν έχεις κάποιο rule (hint: έχεις ξεχάσει το src/). make: *** No rule to make target `src/fort320_sa.c', needed by `fort320_sa.o'. Stop. Το gmake είναι για το π...τσο. Για την ακρίβεια προσπαθεί να βοηθήσει τον developer και έτσι έχει 500 δικές του recipes που έχουν ως αποτέλεσμα να μπορείς να κάνεις compile πολύπλοκα trees με μόλις 2-3 γραμμές Makefile. Δυστυχώς όμως η εξυπνάδα του προκαλεί τέτοια προβλήματα. Θα μου πεις τα άλλα rules γιατί έπαιζαν ? Και εκείνα με τη μανίσια recipe του gmake έπαιζαν απλά επειδή η δική σου ήταν παρόμοια δεν φαινόταν. Αν τρέξω "make -d" με το Makefile σου παίρνω: Considering target file `fort320_sa.o'. Considering target file `src/fort320_sa.c'. File `src/fort320_sa.c' does not exist. Looking for an implicit rule for `src/fort320_sa.c'. Trying implicit prerequisite `src/fort320_sa.y'. Found an implicit rule for `src/fort320_sa.c'. Must remake target `src/fort320_sa.c'. Invoking builtin recipe to update target `src/fort320_sa.c'. yacc src/fort320_sa.y Αν αλλάξω τη γραμμή στο Makefile σε src/fort320_sa.c τότε παίρνω: Considering target file `fort320_sa.o'. Considering target file `src/fort320_sa.c'. File `src/fort320_sa.c' does not exist. Must remake target `src/fort320_sa.c'. Invoking recipe from Makefile:45 to update target `src/fort320_sa.c'. bison -dv ./src/fort320_sa.y Όπως βλέπεις, τώρα δεν τρέχει την "builtin" συνταγή αλλά αυτήν του Makefile και τρέχει κανονικά bison. 2
gon1332 Δημοσ. 14 Ιουλίου 2014 Μέλος Δημοσ. 14 Ιουλίου 2014 Χίλια ευχαριστώ!!! Η λύση του προβλήματος θα ήταν η επόμενή μου ερώτηση, οπότε μ'ένα σμπάρο 2 τρυγόνια. Η ερώτηση μου θα ήταν αν μπορούμε να γράψουμε κάτι τέτοιο: src/fort320_sa.c: $(SURFACE)/fort320_sa.y bison -dv $(SURFACE)/fort320_sa.y mv fort320_sa.tab.c $(SURFACE)/fort320_sa.c cmp -s fort320_sa.tab.h $(SURFACE)/tok.h ||\ mv fort320_sa.tab.h $(SURFACE)/tok.h Δηλαδή να μπορώ να βάλω κάποιο path στο target. Η επόμενη ερώτησή μου, η οποία προκύπτει από νέο θέμα που μου δημιουργήθηκε τώρα, το οποίο όμως ξέρω πως να το λύση στη χειρότερη περίπτωση, απλά ψάχνω για κάτι πιο κομψό. Στο τελικό target που πρέπει να δημιουργήσω, στη σειρά 19, τα object files βρίσκονται στο φάκελο build. Τα object files όμως δεν έχουν το πρόθεμα build/. Δοκίμασα αλλά δε μπορεί να εφαρμοστεί κάποιου είδους επιμεριστική έτσι: ort320: build_dir $(OBJS) $(CC) $(CFLAGS) $(BUILD)/$(OBJS) -o ./fort320 $(CLIBS) Τα περιεχόμενα της $(BUILD) εφαρμόζονται μόνο στο πρώτο .o. Μία άλλη λύση θα ήταν να βάλω μπροστά από κάθε .ο στην κατηγορία $(OBJ) το πρόθεμα build/. Αυτό θα ήταν σκότωμα όμως. Υπάρχει κάτι πιο έξυπνο που μπορώ να κάνω;
imitheos Δημοσ. 15 Ιουλίου 2014 Δημοσ. 15 Ιουλίου 2014 Στο τελικό target που πρέπει να δημιουργήσω, στη σειρά 19, τα object files βρίσκονται στο φάκελο build. Τα object files όμως δεν έχουν το πρόθεμα build/. Δοκίμασα αλλά δε μπορεί να εφαρμοστεί κάποιου είδους επιμεριστική έτσι: ort320: build_dir $(OBJS) $(CC) $(CFLAGS) $(BUILD)/$(OBJS) -o ./fort320 $(CLIBS) Τα περιεχόμενα της $(BUILD) εφαρμόζονται μόνο στο πρώτο .o. Μία άλλη λύση θα ήταν να βάλω μπροστά από κάθε .ο στην κατηγορία $(OBJ) το πρόθεμα build/. Αυτό θα ήταν σκότωμα όμως. Υπάρχει κάτι πιο έξυπνο που μπορώ να κάνω; Ναι έτσι εννοείται πως το BUILD θα πάει μόνο στο πρώτο .o. Υπάρχουν πολλές λύσεις για αυτό άλλες πιο δόκιμες και άλλες πιο αδόκιμες. Αυτό που θα βρεις ως πιο εύκολη λύση στους περισσότερους οδηγούς είναι να χρησιμοποιήσεις τα VPATH / vpath. Και στο προηγούμενο σου πρόβλημα ήταν η πιο εύκολη λύση. Δυστυχώς δεν θεωρείται καλή πρακτική και όχι άδικα. vpath %.o ./build Ας υποθέσουμε ότι προσθέτεις αυτό στο Makefile σου δηλαδή λες στο make να ψάχνει ό,τι τελειώνει σε .o στον κατάλογο build. Αυτό δημιουργεί ένα πρόβλημα. Την πρώτη φορά που θα τρέξεις make θα γίνουν σωστά compile τα .o αλλά το link θα αποτύχει με μήνυμα λάθους ότι δεν βρίσκει τα objects. Αν σε αυτό το σημείο ξανατρέξεις make θα παίξει σωστά. Αυτό γίνεται γιατί την δεύτερη φορά υπάρχουν τα *.o στον build κατάλογο οπότε τα βρίσκει. Αντί να χρησιμοποιήσεις vpath και άλλες τέτοιες χαζομάρες, μπορείς όπως είπες να προσθέσεις σε κάθε .o το πρόθεμα build. Εννοείται πως μπορείς να το κάνεις πιο έξυπνα από χειροκίνητα ένα-ένα. OBJS = fort320_ast_t.o fort320_utils.o fort320_list.o (1)fort320_sa.o\ fort320_la.o fort320_main.o fort320_hash_t.o fort320_errchck.o blah blah BUILD = ./build Το συγκεκριμένο τμήμα είναι το παραπάνω. Το GMake υποστηρίζει την εντολή addprefix η οποία υπάρχει για αυτή ακριβώς τη δουλειά. Οπότε κάνουμε τις αλλαγές που φαίνονται παρακάτω. OBJ_FILES = fort320_ast_t.o fort320_utils.o fort320_list.o (1)fort320_sa.o\ fort320_la.o fort320_main.o fort320_hash_t.o fort320_errchck.o blah blah BUILD = ./build OBJS=$(addprefix $(BUILD)/, $(OBJ_FILES)) Η addprefix θα προσθέσει το $(BUILD)/ (δηλαδή το ./build/) σε κάθε λέξη της μεταβλητής OBJ_FILES. Το αποτέλεσμα αποθηκεύεται στην μεταβλητή OBJS που χρησιμοποιεί το υπόλοιπο Makefile σου. Επειδή όμως άλλαξες τα OBJS θα έχεις το ίδιο πρόβλημα που είχες και πριν. Θα πρέπει οι κανόνες να λένε $(BUILD)/fort320_ast_t.o αντί σκέτο fort320_ast_t.o Edit: OBJ_FILES = fort320_ast_t.o fort320_utils.o fort320_list.o \ fort320_main.o fort320_hash_t.o fort320_errchck.o # Paths # INCLUDE = ./include LIB = ./src/lib SURFACE = ./src BUILD = ./build OBJS=$(addprefix $(BUILD)/, $(OBJ_FILES)) fort320: build_dir $(OBJS) $(CC) $(CFLAGS) $(OBJS) -o ./fort320 $(CLIBS) build_dir: mkdir -p $(BUILD) $(BUILD)/%.o: $(CC) $(CFLAGS) -c -o $@ $< # Our extra libraries # $(BUILD)/fort320_utils.o: $(LIB)/Utils/utils.c $(INCLUDE)/Utils/utils.h $(BUILD)/fort320_list.o: $(LIB)/InputBuffer/mylist.c $(INCLUDE)/InputBuffer/mylist.h $(BUILD)/fort320_hash_t.o: $(LIB)/SymbolTable/hash_t.c $(INCLUDE)/SymbolTable/hash_t.h $(BUILD)/fort320_ast_t.o: $(LIB)/IR/AST.c $(INCLUDE)/IR/AST.h $(BUILD)/fort320_errchck.o: $(LIB)/DebugInfo/errcheck.c $(INCLUDE)/DebugInfo/errcheck.h $(BUILD)/fort320_sa.o: fort320_sa.c $(BUILD)/fort320_main.o: $(SURFACE)/fort320_main.c Μια απλοποίηση που μπορείς να κάνεις είναι η παραπάνω. Αν εξαιρέσεις τα δύο objects σου με τα bison, κτλ όλα τα υπόλοιπα χρησιμοποιούν την ίδια συνταγή. Κάνουν compile την πρώτη εξάρτηση που είναι το .c file με όνομα αυτό του target. Έτσι έσβησα τις συνταγές και άφησα μόνο τους ορισμούς των εξαρτήσεων.Παράλληλα έβαλα ένα κανόνα ο οποίος τρέχει για όλα τα .o αρχεία στον κατάλογο BUILD και καλεί τον CC με τάδε CFLAGS και κάνει compile το αρχείο $< (δηλαδή την πρώτη εξάρτηση) και με όνομα παραγόμενου αρχείου το $@ (δηλαδή το όνομα του target πχ fort320_utils.o) Δοκίμασε το και πες μου αν σου λειτουργεί σωστά. Σε μένα λειτουργεί αλλά τα αρχεία μου είναι μηδενικά οπότε δεν μπορώ να το τεστάρω σίγουρα. 1
gon1332 Δημοσ. 15 Ιουλίου 2014 Μέλος Δημοσ. 15 Ιουλίου 2014 Δεν έχω λόγια. Μου έμαθες κάποια πράγματα για τα Makefiles που δύσκολα θα μάθαινα μόνος μου. Δοκίμασα ό,τι μου είπες και μετά από λίγο παίδεμα τα βρήκα και δούλεψε! #=-------------------------------------------=# # Makefile for lexical and syntactic analyzer # # FORT320 # #=-------------------------------------------=# OBJ_FILES = fort320_ast_t.o fort320_utils.o fort320_list.o\ fort320_sa.o fort320_la.o\ fort320_hash_t.o fort320_errcheck.o fort320_main.o EXEC = ./fort320 # Paths # INCLUDE = ./include LIB = ./src/lib SURFACE = ./src BUILD = ./build OBJS = $(addprefix $(BUILD)/, $(OBJ_FILES)) # Compiler stuff # CC = gcc CFLAGS = -pedantic# -g -Wall -Wextra CLIBS = -lfl -lm fort320: build_dir $(OBJS) $(CC) $(CFLAGS) $(OBJS) -o $(EXEC) $(CLIBS) build_dir: mkdir -p $(BUILD) $(BUILD)/%.o: $(CC) $(CFLAGS) -c -o $@ $< # Our extra libraries # $(BUILD)/fort320_utils.o: $(LIB)/Utils/utils.c $(INCLUDE)/Utils/utils.h $(BUILD)/fort320_list.o: $(LIB)/InputBuffer/mylist.c $(INCLUDE)/InputBuffer/mylist.h $(BUILD)/fort320_hash_t.o: $(LIB)/SymbolTable/hash_t.c $(INCLUDE)/SymbolTable/hash_t.h $(BUILD)/fort320_ast_t.o: $(LIB)/IR/AST.c $(INCLUDE)/IR/AST.h $(BUILD)/fort320_errcheck.o: $(LIB)/DebugInfo/errcheck.c $(INCLUDE)/DebugInfo/errcheck.h # Syntactical Parser # $(BUILD)/fort320_sa.o: $(SURFACE)/fort320_sa.c $(SURFACE)/fort320_sa.c: $(SURFACE)/fort320_sa.y bison -dv $(SURFACE)/fort320_sa.y mv fort320_sa.output $(SURFACE)/fort320_sa.output mv fort320_sa.tab.c $(SURFACE)/fort320_sa.c cmp -s fort320_sa.tab.h $(SURFACE)/tok.h ||\ mv fort320_sa.tab.h $(SURFACE)/tok.h # Lexical Analyzer # $(BUILD)/fort320_la.o: $(SURFACE)/fort320_la.c $(SURFACE)/fort320_la.c: $(SURFACE)/fort320_la.l flex $(SURFACE)/fort320_la.l mv lex.yy.c $(SURFACE)/fort320_la.c # The main program # $(BUILD)/fort320_main.o: $(SURFACE)/fort320_main.c $(CC) $(CFLAGS) -c $(SURFACE)/fort320_main.c -o $(BUILD)/fort320_main.o #fort320_la.o fort320_sa.o fort320_main.o: $(INCLUDE)/InputBuffer/mylist.h #fort320_la.o fort320_main.o : tok.h clean: rm -f $(SURFACE)/*~ $(INCLUDE)/*~ $(LIB)/*~ $(INCLUDE)/DebugInfo/*~\ $(INCLUDE)/InputBuffer/*~ $(INCLUDE)/IR/*~ $(INCLUDE)/SymbolTable/*~\ $(INCLUDE)/Utils/*~ $(LIB)/DebugInfo/*~ $(LIB)/InputBuffer/*~\ $(LIB)/IR/*~ $(LIB)/SymbolTable/*~ $(LIB)/Utils/*~ rm -f -r $(BUILD) rm -f $(SURFACE)/fort320_la.c $(SURFACE)/lex.yy.c $(SURFACE)/fort320_sa.c\ $(SURFACE)/tok.h $(SURFACE)/fort320_sa.tab.c $(SURFACE)/fort320_sa.tab.h\ $(SURFACE)/fort320_sa.output ./fort320 Ένα μεγάλο μπράβο για την ολοκληρωμένη απάντηση. Όχι μόνο επειδή μου έλυσες το πρόβλημα, αλλά για το μεράκι σου. Κάτι άλλο που ήθελα να ρωτήσω. Τα makefiles για τεράστια projects (linux, gcc, llvm) πως τα φτιάχνουν; Δουλεύουν με κάποιο IDE και αυτό τα παράγει αυτοματοποιημένα; Στο source tree του llvm μπορεί να δει κανείς πάρα πολλά makefiles σε υποφακέλους τα οποία κάνουν include ένα βασικό κ.ο.κ. Μαζί με αυτή την ερώτηση κολλάει και το άλλο μεγάλο ερώτημα. Υπάρχουν στυλ οργάνωσης project ή αφήνεται στα χέρια του σχεδιαστή; Τα περισσότερα projects ακολουθούν την οργάνωση που μπορεί να προσφέρει ένα IDE; Υπάρχουν γενικά κανόνες; Για παράδειγμα ένα παράδειγμα προς αποφυγήν είναι ο gcc. Είναι πραγματικά τεράστιος και ένα μεγάλο μέρος αρχείων είναι χύμα. Σε αντίθετη περίπτωση ο clang είναι παράδειγμα προς μίμηση.
imitheos Δημοσ. 15 Ιουλίου 2014 Δημοσ. 15 Ιουλίου 2014 $(BUILD)/%.o: $(CC) $(CFLAGS) -c -o $@ $< Απλά να έχεις στο νου σου ότι το $< ισοδυναμεί μόνο με την πρώτη εξάρτηση. Το έβαλα έτσι επειδή είχες "τάδε.ο: δείνα.c δείνα.h" ώστε να σου πάρει μόνο το .c και όχι και το .h. Αν κάποιο object είχε 2 .c δεν θα έπαιζε σωστά. fort320: build_dir $(OBJS) $(CC) $(CFLAGS) $(OBJS) -o $(EXEC) $(CLIBS) build_dir: mkdir -p $(BUILD) Τώρα που έπαιξε μπορείς να κάνεις την ίδια απλοποίηση και εδώ. Αν δεν έχεις σκοπό ποτέ να έχεις το EXEC διαφορετικό από το target (δηλαδή αν πάντα EXEC == fort320) τότε δεν χρειάζεται να υπάρχει το EXEC αλλά μπορεί να αντικατασταθεί από το $@. Ομοίως το OBJS μπορεί να αντικατασταθεί με το $^ που σημαίνει όλες τις εξαρτήσεις. Έτσι μια πρώτη μορφή είναι η εξής: fort320: build_dir $(OBJS) $(CC) $(CFLAGS) $^ -o $@ $(CLIBS) Με αυτή τη μορφή όμως έχουμε ένα πρόβλημα. Εκτός από τα object files, το fort320 έχει και το build_dir ως εξάρτηση οπότε το gcc θα λάβει και αυτό ως όρισμα και θα σου πει ότι δεν υπάρχει κάτι τέτοιο. Για αυτό το λόγο έχουν υλοποιηθεί οι "order-only" εξαρτήσεις. Η τελική λοιπόν μορφή γίνεται ως εξής: fort320: $(OBJS) | build_dir $(CC) $(CFLAGS) $^ -o $@ $(CLIBS) Αυτή η μορφή έχει δύο συνέπειες. Αφενός το build_dir target εκτελείται σωστά πριν τα υπόλοιπα όταν χρειάζεται να δημιουργηθεί ο κατάλογος (όπως γινόταν και με τη δική σου μορφή), αφετέρου το link δουλεύει σωστά γιατί το $^ αγνοεί τις order-only εξαρτήσεις (εμφανίζονται με το $| αν θέλεις κάπου να τις χρησιμοποιήσεις). Κάτι άλλο που ήθελα να ρωτήσω. Τα makefiles για τεράστια projects (linux, gcc, llvm) πως τα φτιάχνουν; Δουλεύουν με κάποιο IDE και αυτό τα παράγει αυτοματοποιημένα; Στο source tree του llvm μπορεί να δει κανείς πάρα πολλά makefiles σε υποφακέλους τα οποία κάνουν include ένα βασικό κ.ο.κ.Στο linux και σε κάποια άλλα projects έχουμε πολύπλοκα Makefiles (που θα μπορούσαμε να πούμε ότι δεν είναι καν Makefiles) τα οποία αναπτύσσονται χειροκίνητα. Τα περισσότερα άλλα projects χρησιμοποιούν κάποιο generator (όπως autotools, cmake, scons, κτλ) στο οποίο γράφεις μια περιγραφή του τι χρειάζεται το project σου και μετά τρέχοντας το generator παράγεται το Makefile το οποίο μπορεί να έχει checks για διάφορα λειτουργικά αυτόματα και 1002 άλλα πράγματα. Μαζί με αυτή την ερώτηση κολλάει και το άλλο μεγάλο ερώτημα. Υπάρχουν στυλ οργάνωσης project ή αφήνεται στα χέρια του σχεδιαστή; Τα περισσότερα projects ακολουθούν την οργάνωση που μπορεί να προσφέρει ένα IDE; Υπάρχουν γενικά κανόνες; Για παράδειγμα ένα παράδειγμα προς αποφυγήν είναι ο gcc. Είναι πραγματικά τεράστιος και ένα μεγάλο μέρος αρχείων είναι χύμα. Σε αντίθετη περίπτωση ο clang είναι παράδειγμα προς μίμηση.Οδηγοί υπάρχουν διάφοροι αλλά η τελική απόφαση είναι του developer. Σε μεγάλα project ίσως υπάρχει κάποια πολιτική που να το ορίζει. Όπως δηλαδή υπάρχει κάποιο αρχείο που ορίζει το Coding Style μπορεί να υπάρχει και αρχείο που να λέει τι όνομα θα παίρνει το κάθε αρχείο και σε ποιον κατάλογο θα πηγαίνει.
gon1332 Δημοσ. 15 Ιουλίου 2014 Μέλος Δημοσ. 15 Ιουλίου 2014 Απλά να έχεις στο νου σου ότι το $< ισοδυναμεί μόνο με την πρώτη εξάρτηση. Το έβαλα έτσι επειδή είχες "τάδε.ο: δείνα.c δείνα.h" ώστε να σου πάρει μόνο το .c και όχι και το .h. Αν κάποιο object είχε 2 .c δεν θα έπαιζε σωστά. Τώρα που έπαιξε μπορείς να κάνεις την ίδια απλοποίηση και εδώ. Αν δεν έχεις σκοπό ποτέ να έχεις το EXEC διαφορετικό από το target (δηλαδή αν πάντα EXEC == fort320) τότε δεν χρειάζεται να υπάρχει το EXEC αλλά μπορεί να αντικατασταθεί από το $@. Ομοίως το OBJS μπορεί να αντικατασταθεί με το $^ που σημαίνει όλες τις εξαρτήσεις. Έτσι μια πρώτη μορφή είναι η εξής: fort320: build_dir $(OBJS) $(CC) $(CFLAGS) $^ -o $@ $(CLIBS) Με αυτή τη μορφή όμως έχουμε ένα πρόβλημα. Εκτός από τα object files, το fort320 έχει και το build_dir ως εξάρτηση οπότε το gcc θα λάβει και αυτό ως όρισμα και θα σου πει ότι δεν υπάρχει κάτι τέτοιο. Για αυτό το λόγο έχουν υλοποιηθεί οι "order-only" εξαρτήσεις. Η τελική λοιπόν μορφή γίνεται ως εξής: fort320: $(OBJS) | build_dir $(CC) $(CFLAGS) $^ -o $@ $(CLIBS) Αυτή η μορφή έχει δύο συνέπειες. Αφενός το build_dir target εκτελείται σωστά πριν τα υπόλοιπα όταν χρειάζεται να δημιουργηθεί ο κατάλογος (όπως γινόταν και με τη δική σου μορφή), αφετέρου το link δουλεύει σωστά γιατί το $^ αγνοεί τις order-only εξαρτήσεις (εμφανίζονται με το $| αν θέλεις κάπου να τις χρησιμοποιήσεις). Πολλά νέα πράγματα. Ευχαριστώ πολύ για το χρόνο σου. Θα το δοκιμάσω κι αυτό στις επόμενες μέρες. Τελικά έχει πολύ πράγμα από πίσω το Makefile. Θα στείλω απάντηση μόλις το δοκιμάσω. Μία ερώτηση σχετική με όλα αυτά είναι η εξής. Έτσι όπως το έχω κάνει μέχρι τώρα πρέπει όταν κάνω include κάποιο header να βάζω paths της μορφής: include "../../include/IR/AST.h" Σίγουρα θα υπάρχει κάποιος τρόπος να έχω σα default κάποιο path και απλά ό,τι γράφω στο include να γίνεται append σε αυτό το path. Λογικά αυτή η λειτουργικότητα θα γίνεται μέσω Makefile. Συγνώμη που κουράζω εδώ. Σίγουρα θα μπορούσα να το βρω και με ένα search στο google, αλλά πραγματικά θα ήθελα μία απάντηση από εδώ. Τουλάχιστον θα μείνει και στο post στα ελληνικά για περαιτέρω αναφορές. Στο linux και σε κάποια άλλα projects έχουμε πολύπλοκα Makefiles (που θα μπορούσαμε να πούμε ότι δεν είναι καν Makefiles) τα οποία αναπτύσσονται χειροκίνητα. Τα περισσότερα άλλα projects χρησιμοποιούν κάποιο generator (όπως autotools, cmake, scons, κτλ) στο οποίο γράφεις μια περιγραφή του τι χρειάζεται το project σου και μετά τρέχοντας το generator παράγεται το Makefile το οποίο μπορεί να έχει checks για διάφορα λειτουργικά αυτόματα και 1002 άλλα πράγματα. Οδηγοί υπάρχουν διάφοροι αλλά η τελική απόφαση είναι του developer. Σε μεγάλα project ίσως υπάρχει κάποια πολιτική που να το ορίζει. Όπως δηλαδή υπάρχει κάποιο αρχείο που ορίζει το Coding Style μπορεί να υπάρχει και αρχείο που να λέει τι όνομα θα παίρνει το κάθε αρχείο και σε ποιον κατάλογο θα πηγαίνει. Μάλιστα. Άρα κάποιος Software Engineer πρέπει να ξέρει τί κάνει από την αρχή και επίσης να γνωρίζει άριστα τα εργαλεία του. Εδώ το Makefile. Να υποθέσω γνώσεις και εμπειρία οργάνωσης ενός project αποκτιέται από το διάβασμα καλά δομημένων/οργανωμένων open source projects.
imitheos Δημοσ. 15 Ιουλίου 2014 Δημοσ. 15 Ιουλίου 2014 Μία ερώτηση σχετική με όλα αυτά είναι η εξής. Έτσι όπως το έχω κάνει μέχρι τώρα πρέπει όταν κάνω include κάποιο header να βάζω paths της μορφής: include "../../include/IR/AST.h" Σίγουρα θα υπάρχει κάποιος τρόπος να έχω σα default κάποιο path και απλά ό,τι γράφω στο include να γίνεται append σε αυτό το path. Λογικά αυτή η λειτουργικότητα θα γίνεται μέσω Makefile. Συγνώμη που κουράζω εδώ. Σίγουρα θα μπορούσα να το βρω και με ένα search στο google, αλλά πραγματικά θα ήθελα μία απάντηση από εδώ. Τουλάχιστον θα μείνει και στο post στα ελληνικά για περαιτέρω αναφορές. Το make διαβάζει τους κανόνες που ορίζεις και τρέχει συνταγές για να ευκολύνει το building. Δεν γνωρίζει όμως τι αρχεία έχεις. Μπορείς να του ορίσεις ένα κανόνα για ένα αρχείο mitsos.c που να τρέχει συνταγή bmp2jpeg και να μετατρέπει το αρχείο από BMP σε JPEG. Αν το αρχείο mitsos.c είναι BMP αντί για κώδικας C, τότε θα παίξει τζάμι. Δεν γνωρίζει δηλαδή ούτε καν τα .c αρχεία που του λες μέσα στο Makefile πόσο μάλλον το header file που γίνεται include μέσα σε ένα .c. Αυτό που θέλεις είναι δουλειά του GCC και όχι του make οπότε δεν μπορεί να σε βοηθήσει άμεσα. Μπορεί όμως έμμεσα. Στο Makefile σου έχεις ορίσει μια μεταβλητή CFLAGS την οποία δίνεις στο GCC. Σε αυτή λοιπόν τη μεταβλητή μπορείς να πεις στον GCC να ψάχνει σε όποιο path θέλεις. Ένας τρόπος να το κάνεις είναι να αλλάξεις στα .c αρχεία το παραπάνω #include και να το κάνεις #include "IR/AST.h". Έπειτα στο Makefile προσθέτεις στην μεταβλητή CFLAGS το -Iinclude/ <-- αγγλικό κεφαλαίο γιώτα Έτσι ο gcc θα ψάχνει για headers και στον κατάλογο include (όπως αυτός φαίνεται από το top level του project στο οποίο τρέχει το make) οπότε το αρχείο που θα γίνεται include θα είναι το top_level (από make) / include (από την παράμετρο -I) / IR/AST.h (από το #include στο αρχείο .c) δηλαδή το σωστό path. 3
Προτεινόμενες αναρτήσεις
Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε
Πρέπει να είστε μέλος για να αφήσετε σχόλιο
Δημιουργία λογαριασμού
Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!
Δημιουργία νέου λογαριασμούΣύνδεση
Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.
Συνδεθείτε τώρα