Μια σύντομη εισαγωγή στα «Makefiles» στην ανάπτυξη λογισμικού ανοιχτού κώδικα με το GNU Make


Το GNU Make είναι ένα βοηθητικό πρόγραμμα ανάπτυξης που καθορίζει τα μέρη μιας συγκεκριμένης βάσης κώδικα που πρόκειται να μεταγλωττιστούν εκ νέου και μπορεί να εκδώσει εντολές για την εκτέλεση αυτών των λειτουργιών στη βάση κώδικα. Το συγκεκριμένο βοηθητικό πρόγραμμα make μπορεί να χρησιμοποιηθεί με οποιαδήποτε γλώσσα προγραμματισμού με την προϋπόθεση ότι η μεταγλώττιση τους μπορεί να γίνει από το φλοιό με την έκδοση εντολών.

Για να χρησιμοποιήσουμε το GNU Make, πρέπει να έχουμε κάποιο σύνολο κανόνων που να καθορίζουν τη σχέση μεταξύ διαφορετικών αρχείων στο πρόγραμμά μας και εντολές για την ενημέρωση κάθε αρχείου. Αυτά γράφονται σε ένα ειδικό αρχείο που ονομάζεται «makefile». Η εντολή «make» χρησιμοποιεί τη βάση δεδομένων «makefile» και τους τελευταίους χρόνους τροποποίησης των αρχείων για να αποφασίσει τα οποία όλα τα αρχεία πρέπει να μεταγλωττιστούν ξανά.

Περιεχόμενα ενός Makefile

Γενικά, τα "makefiles" περιέχουν 5 είδη πραγμάτων και συγκεκριμένα: σιωπηρούς κανόνες, ρητούς κανόνες, ορισμούς μεταβλητών , οδηγίες και σχόλια.

  1. Ένας ρητός κανόνας καθορίζει τον τρόπο δημιουργίας/επαναδημιουργίας ενός ή περισσότερων αρχείων (που ονομάζονται στόχοι, θα εξηγηθούν αργότερα) και πότε να κάνετε το ίδιο.
  2. Ένας σιωπηρός κανόνας καθορίζει τον τρόπο δημιουργίας/διαμόρφωσης ενός ή περισσότερων αρχείων με βάση τα ονόματά τους. Περιγράφει πώς ένα όνομα αρχείου προορισμού σχετίζεται με ένα αρχείο με όνομα παρόμοιο με τον στόχο.
  3. Ένας ορισμός μεταβλητής είναι μια γραμμή που καθορίζει μια τιμή συμβολοσειράς για μια μεταβλητή που θα αντικατασταθεί αργότερα.
  4. Μια οδηγία είναι μια οδηγία για τη make για να κάνει κάτι ιδιαίτερο κατά την ανάγνωση του makefile.
  5. Το σύμβολο "#" χρησιμοποιείται αντιπροσωπεύει την αρχή ενός σχολίου μέσα σε makefiles . Μια γραμμή που ξεκινά με «#» απλώς αγνοείται.

Δομή των MakeFiles

Οι πληροφορίες που λένε στη make πώς να μεταγλωττίσετε ξανά ένα σύστημα προέρχονται από την ανάγνωση μιας βάσης δεδομένων που ονομάζεται makefile. Ένα απλό makefile θα αποτελείται από κανόνες της ακόλουθης σύνταξης:

target ... : prerequisites ... 
	recipe 
... 
...

Ως στόχος ορίζεται το αρχείο εξόδου που δημιουργείται από το πρόγραμμα. Μπορεί επίσης να είναι ψευδείς στόχοι, οι οποίοι θα εξηγηθούν παρακάτω. Στα παραδείγματα αρχείων προορισμού περιλαμβάνονται εκτελέσιμα, αρχεία αντικειμένων ή ψευδείς στόχοι όπως clean, εγκατάσταση, απεγκατάσταση κ.λπ.

Μια προαπαιτούμενη είναι ένα αρχείο που χρησιμοποιείται ως είσοδος για τη δημιουργία των αρχείων προορισμού.

Μια συνταγή είναι η ενέργεια που κάνει για τη δημιουργία του αρχείου προορισμού βάσει των προαπαιτούμενων. Είναι απαραίτητο να βάλετε τον χαρακτήρα tab πριν από κάθε συνταγή μέσα στα makefiles εκτός και αν καθορίσουμε τη μεταβλητή '.RECIPEPREFIX' για να ορίσουμε κάποιον άλλο χαρακτήρα ως πρόθεμα στη συνταγή.

Ένα δείγμα Makefile

final: main.o end.o inter.o start.o
	gcc -o final main.o end.o inter.o start.o
main.o: main.c global.h
	gcc -c main.c
end.o: end.c local.h global.h
	gcc -c end.c
inter.o: inter.c global.h
	gcc -c inter.c
start.o: start.c global.h
	gcc -c start.c
clean:
	rm -f main.o end.o inter.o start.o

Στο παραπάνω παράδειγμα χρησιμοποιήσαμε 4 C αρχεία προέλευσης και δύο αρχεία κεφαλίδας για τη δημιουργία του εκτελέσιμου τελικού. Εδώ κάθε αρχείο «.o» αποτελεί ταυτόχρονα στόχο και προαπαιτούμενο μέσα στο makefile. Τώρα ρίξτε μια ματιά στο τελευταίο όνομα στόχου clean. Είναι απλώς μια ενέργεια και όχι ένα αρχείο προορισμού.

Δεδομένου ότι συνήθως δεν το χρειαζόμαστε αυτό κατά τη μεταγλώττιση, δεν είναι γραμμένο ως προαπαιτούμενο σε άλλους κανόνες. Οι στόχοι που δεν αναφέρονται σε αρχεία αλλά είναι απλώς ενέργειες ονομάζονται ψευδείς στόχοι. Δεν θα έχουν καμία προϋπόθεση όπως άλλα αρχεία προορισμού.

Πώς το GNU κάνει να επεξεργάζεται ένα Makefile

Από προεπιλογή το make ξεκινά με τον πρώτο στόχο στο 'makefile' και ονομάζεται " προεπιλεγμένος στόχος'. Λαμβάνοντας υπόψη το παράδειγμά μας, έχουμε ως πρώτο στόχο μας το τελικό. Δεδομένου ότι οι προϋποθέσεις του περιλαμβάνουν άλλα αρχεία αντικειμένων, αυτά πρέπει να ενημερωθούν πριν από τη δημιουργία του τελικού. Κάθε μία από αυτές τις προϋποθέσεις επεξεργάζεται σύμφωνα με τους δικούς της κανόνες.

Η εκ νέου μεταγλώττιση πραγματοποιείται εάν γίνουν τροποποιήσεις σε αρχεία πηγής ή αρχεία κεφαλίδας ή εάν τοαρχείο αντικειμένου δεν υπάρχει καθόλου. Μετά την εκ νέου μεταγλώττιση των απαραίτητων αρχείων αντικειμένων, το make αποφασίζει εάν θα επανασυνδέσει το τελικό ή όχι. Αυτό πρέπει να γίνει εάν το αρχείο τελικό δεν υπάρχει ή εάν κάποιο από τα αρχεία αντικειμένων είναι νεότερο από αυτό.

Επομένως, εάν αλλάξουμε το αρχείο inter.c, τότε κατά την εκτέλεση του make θα μεταγλωττίσει ξανά το αρχείο προέλευσης για ενημέρωση το αρχείο αντικειμένου inter.o και, στη συνέχεια, συνδέστε το final.

Χρήση μεταβλητών μέσα στο Makefiles

Στο παράδειγμά μας, έπρεπε να παραθέσουμε όλα τα αρχεία αντικειμένων δύο φορές στον κανόνα για τελικό όπως φαίνεται παρακάτω.

final: main.o end.o inter.o start.o
	gcc -o final main.o end.o inter.o start.o

Προκειμένου να αποφευχθούν τέτοιες αντιγραφές, μπορούμε να εισάγουμε μεταβλητές για την αποθήκευση της λίστας των αρχείων αντικειμένων που χρησιμοποιούνται μέσα στο makefile. Χρησιμοποιώντας τη μεταβλητή OBJ μπορούμε να ξαναγράψουμε το δείγμα makefile σε ένα παρόμοιο που φαίνεται παρακάτω.

OBJ = main.o end.o inter.o start.o
final: $(OBJ)
	gcc -o final $(OBJ)
main.o: main.c global.h
	gcc -c main.c
end.o: end.c local.h global.h
	gcc -c end.c
inter.o: inter.c global.h
	gcc -c inter.c
start.o: start.c global.h
	gcc -c start.c
clean:
	rm -f $(OBJ)

Κανόνες για τον καθαρισμό του καταλόγου προέλευσης

Όπως είδαμε στο παράδειγμα makefile, μπορούμε να ορίσουμε κανόνες γιαεκκαθάριση του καταλόγου προέλευσης αφαιρώντας τα αρχεία ανεπιθύμητων αντικειμένων μετά τη μεταγλώττιση. Ας υποθέσουμε ότι έχουμε ένα αρχείο προορισμού που ονομάζεται clean. Πώς μπορείτε να κάνετε να διαφοροποιήσετε τις δύο παραπάνω καταστάσεις; Εδώ έρχεται η έννοια των ψευδών στόχων.

Ένας ψεύτικος στόχος είναι αυτός που δεν είναι στην πραγματικότητα το όνομα ενός αρχείου, αλλά είναι απλώς ένα όνομα για μια συνταγή που πρέπει να εκτελείται κάθε φορά που γίνεται ένα ρητό αίτημα από το makefile<. Ένας κύριος λόγος για να χρησιμοποιήσετε το ψεύτικο στόχο είναι να αποφύγετε μια σύγκρουση με ένα αρχείο με το ίδιο όνομα. Ένας άλλος λόγος είναι η βελτίωση της απόδοσης.

Για να εξηγήσω αυτό το πράγμα, θα αποκαλύψω μια απροσδόκητη ανατροπή. Η συνταγή για clean δεν θα εκτελεστεί από προεπιλογή κατά την εκτέλεση του make. Αντίθετα, είναι απαραίτητο να επικαλεστείτε το ίδιο εκδίδοντας την εντολή make clean.

.PHONY: clean
clean:
	rm -f $(OBJ)

Τώρα προσπαθήστε να δημιουργήσετε makefiles για τη δική σας βάση κώδικα. Μην διστάσετε να σχολιάσετε εδώ με τις αμφιβολίες σας.