Οδηγός για το εργαλείο
make
Έχει γίνει ένα Update στο παρακάτω
Βασικές πληροφορίες για το make...
Τί είναι το make και γιατί το χρησιμοποιούμε? Το make είναι ένα εργαλείο που, το χρησιμοποιούμε όταν έχουμε να δουλέψουμε με πολλά αρχεία! Το make ελέγχει ποια αρχεία έχουν αλλάξει και τα μεταγλωττίζει αυτόματα όταν χρειάζεται. Με την λέξη "αυτόματα" εννοώ οτι θα πρέπει να τρέξετε μια μόνο εντολή και όχι ν εντολές σε περίπτωση που δεν το χρησιμοποιούσαμε. Εντελώς θεωρητικά θα αναφέρω ένα παράδειγμα. Σε περίπτωση που είχατε 10 αρχεία και 2 βιλβιοθήκες για να βγάλετε ένα εκτελέσιμο αρχείο θα πρέπει να τρέχατε και σαν την παρακάτω εντολή:
- Κώδικας: Επιλογή όλων
gcc file1.c file2.c file3.c ... file10.c -o executable_program
Τι θα είχαμε καταφέρει με αυτόν τον τρόπο? Να κάνουμε compile τα 10 αρχεία κατάληξης .c και να δημιουργήσουμε ένα εκτελέσιμο αρχείο executable_prog που τρέχοντας το εκτελούνται και τα 10 αρχεία της C! Ένα σημαντικό πρόβλημα είναι οτι εάν για παράδειγμα αλλάξουμε μια γραμμή στο 3ο αρχείο για να ανανεώσουμε το εκτελέσιμο μας θα πρέπει να κάνουμε compile ξανά τα 10 αρχεία και προφανώς αυτό δεν το θέλουμε γιατί στην πραγματικότητα μπορεί να είναι 100 αρχεία(κοστίζει σε χρόνο). Αυτό που θέλουμε να κάνουμε τώρα είναι το ίδιο αλλα χρησιμοποιώντας το make. Θα σας ήταν χρήσιμο αν εκτελέσετε το παρακάτω παράδειγμα πρώτα.
Για να μπορέσετε να δείτε την διαφορά εκτελέστε το παράδειγμα αυτό. Έχουμε 4 αρχειάκια και θέλουμε να κάνουμε ένα εκτελέσιμο! Για λόγο χρόνου σας τα έχω έτοιμα, απλά κάντε copy/paste και συνεχίστε. Προσοχή!!! Σε κάθε αρχείο δώστε το όνομα που βλέπετε στο 1ο σχόλιο!
Ονομάστε το παρακάτω αρχείο factorial.cpp
- Κώδικας: Επιλογή όλων
#include "functions.h"
int factorial(int n){
if(n!=1){
return(n * factorial(n-1));
}
else return 1;
}
Ονομάστε το παρακάτω αρχείο functions.h
- Κώδικας: Επιλογή όλων
void print_hello();
int factorial(int n);
Ονομάστε το παρακάτω αρχείο hello.cpp
- Κώδικας: Επιλογή όλων
#include <iostream>
#include "functions.h"
using namespace std;
void print_hello(){
cout << "Hello World!";
}
Ονομάστε το παρακάτω αρχείο main.cpp
- Κώδικας: Επιλογή όλων
#include <iostream>
#include "functions.h"
using namespace std;
int main(){
print_hello();
cout << endl;
cout << "The factorial of 5 is " << factorial(5) << endl;
return 0;
}
Προτείνω να τα βάλετε σε έναν φάκελο και να συνεχίσετε τρέχοντας την εντολή:
- Κώδικας: Επιλογή όλων
g++ hello.cpp factorial.cpp main.cpp -o hello
θα δημιουργηθεί ένα εκτελέσιμο ονόματι hello και μπορείτε να το τρέξετε εκτελώντας το παρακάτω,
- Κώδικας: Επιλογή όλων
./hello
Τι είναι τα Makefiles...
Αν τρέξετε την εντολή
- Κώδικας: Επιλογή όλων
make



- Κώδικας: Επιλογή όλων
make -f my_make_file
έγραψε:Το όρισμα -f μας επιτρέπει να τρέξουμε το δικό μας makefile. Μπορείτε να κάνετε πολλά περισσότερα με το make! Δείτε man make.
Κανόνες...
Για να γράψετε ένα makefile θα πρέπει να χρησιμοποιήσετε κάποια συγκεκριμενη δομή και κάποιους κανόνες. Για να καταλάβετε τι εννοώ θα τρέξετε πολύ απλά και πρόχειρα το πρώτο σας makefile!

- Κώδικας: Επιλογή όλων
<target> : <dependencies>
<action1>
<action2>...
o κανονας οριζει τρια πραγματα.
- το ονομα του αρχειου που πρεπει να ανανεωθει (στοχος - target),
- ποτε πρεπει να ανανεωθει (εξαρτησεις - dependencies) και
- πως θα ανανεωθει (ενεργειες - actions).
Οι εξαρτήσεις (dependencies) ειναι ονόματα αρχείων απο τα οποία εξαρτάται ο στόχος (target) και μπορεί να είναι και ονόματα στόχων απο άλλους κανόνες. Το make θα εκτελέσει τις ενέργειες (actions) με τη σειρά που ορίζονται όταν κάποιο απο τα αρχεία που αναφέρονται στις εξαρτήσεις εχει νεότερη ημερομηνία από το στόχο. Οι ενέργειες (actions) μπορεί να είναι ότι εντολές θέλετε και πρέπει υποχρεωτικά να ξεκινούν με ΤΑΒ. Η παράλειψη του αρχικού TAB σε κάθε γραμμή ενέργειας είναι και το συνηθέστερο λάθος στην κατασκευή του Makefile. Στην πραγματικότητα η βασική λειτουργία του make είναι ΠΟΛΥ απλή αρκεί να καταλάβετε τι γίνεται όταν το τρέχεις γι' αυτό θα δούμε παρακάτω ένα παράδειγμα εκτέλεσης.
Όταν θα τρέξουμε το makefile αυτό που θα γίνει είναι να πάει να βρεί το target(εξού και το όνομα



Θυμάστε τον φάκελο που είχαμε φτιάξει στην αρχή?

- Κώδικας: Επιλογή όλων
all:
g++ main.cpp hello.cpp factorial.cpp -o hello
Τρέχοντας το make αυτό που θα γίνει είναι να "τρέξει" το all και αυτό που εκτελείται στον στόχο all είναι g++ main.cpp hello.cpp factorial.cpp -o hello.
Οπότε τρέξτε το δικό σας makefile εκτελώντας την εντολή;
- Κώδικας: Επιλογή όλων
make -f my_make_file
τώρα τρέξτε το αρχείο hello κλασικά με
- Κώδικας: Επιλογή όλων
./hello
Ολοκληρωμένοι targets με dependencies...
Αυτό που κάναμε πριν ήταν το πιο απλό παράδειγμα για το make και συνήθως δεν λειτουργεί έτσι. Γιατί δεν έχετε χρησιμοποιήσει ακόμη τα dependencies! Και τι είναι αυτά? Γιατί να τα χρησιμοποιήσετε? Γιατί, χωρίς αυτά ΠΑΛΙ κάνετε compile 3 αρχεία στην συγκεκριμένη περίπτωση και ν αρχεία γενικά. Εδώ μπαίνουν τα dependencies και ουσιαστικά βοηθούν ώστε να μην κάνουμε compile πολλά αρχεία συγχρόνως! Σε όποιο έγινε αλλαγή σε αυτό θα γίνει compile και θα δείτε πως. Πάλι στον ιδιο φάκελο που είμαστε σβήστε τα περιττά αρχεία που δημιουργήθηκαν απο το προηγούμενο make (λογικά τα εκτελέσιμα μόνο) και κρατήστε τα 3 αρχεία κατάληξης .c και την βιβλιοθήκη .h. Το νέο αρχείο make είναι έτοιμο και είναι το:
- Κώδικας: Επιλογή όλων
all: hello clean
hello: main.o factorial.o hello.o
g++ main.o factorial.o hello.o -o hello
main.o: main.cpp
g++ -c main.cpp
factorial.o: factorial.cpp
g++ -c factorial.cpp
hello.o: hello.cpp
g++ -c hello.cpp
clean:
rm -rf *.o
ρίξτε μια ματιά και θα δείτε οτι είναι εύκολο στην κατανόηση! Ο αρχικός στόχος είναι ο all, για να γίνει αυτός θα πρέπει αν γίνουν οι στόχοι hello και clean. Για να γίνει ο hello χρειάζονται άλλοι 3 στόχοι σε καθένα απο τους οποίους χρειάζεται να γίνει ένα αρχείο compile(έτσι θα γίνεται compile ένα αρχείο κάθε φορά και κάθε φορά που γίνεται το make δεν θα κάνουμε και τα 3 αρχεία compile). Για να γίνει ο clean στόχος δεν χρειάζεται κάτι οπότε απλα εκτελείται η εντολή που διαγράφει όλα τα .o(3) αρχεία που μας είναι περιττά στο τέλος. Δεν είναι απαραίτητο αυτό απλά για ομορφιά!

Χρησιμοποιώντας μεταβλητές...
Στην αρχη καθε Makefile μπορουμε να ορισουμε μεταβλητες για να αποφευγουμε περιττες επαναληψεις και να το κανουμε πιο ευαναγνωστο. Οι ορισμοι των μεταβλητων ειναι της μορφης:
- Κώδικας: Επιλογή όλων
<ονομα_μεταβλητης> = <κειμενο>
Αν υπαρχουν κενα στην αρχη του κειμενου αγνοουνται. Οι μεταβλητες μπορουν να χρησιμοποιηθουν οπουδηποτε (ακομη και για τον ορισμο αλλων μεταβλητων). Στην τιμη μιας μεταβλητης αναφερομαστε με τη συνταξη:
- Κώδικας: Επιλογή όλων
$(<ονομα>) η ${<ονομα>}
Εστω για παραδειγμα οτι εχουμε την εργασια που περιγραψαμε παραπανω και εστω οτι θελουμε να καλεσουμε τον μεταγλωττιστη με την επιλογη -g (για debugging). Μπορουμε στην αρχη του Makefile να ορισουμε :
- Κώδικας: Επιλογή όλων
CXXFLAGS = -g
και να τροποποιησουμε τους κανονες ως εξης:
- Κώδικας: Επιλογή όλων
test_data_structures : list.o main.o
g++ -o $(CXXFLAGS) test_data_structures list.o main.o
list.o : list.C list.h
g++ -c ${CXXFLAGS} list.C
main.o : main.C
g++ -c $(CXXFLAGS) main.C
Ετσι αν θελουμε να αλλαξουμε τα ορισματα του compiler π.χ. απο -g σε -O (για βελτιστοποιηση), αρκει να αλλαξουμε μονο τον ορισμο της CXXFLAGS σε :
- Κώδικας: Επιλογή όλων
CXXFLAGS = -O
Η ιδια τακτικη μπορει να χρησιμοποιηθει και για αλλα ορισματα, π.χ. βιβλιοθηκες. Για παραδειγμα αν για την παραπανω εργασια πρεπει να χρησιμοποιησουμε και τη βιβλιοθηκη βασικων τυπων της LEDA θα προσθεσουμε στο Makefile :
- Κώδικας: Επιλογή όλων
LEDA_LIBS = -lL -lm
και θα τροποποιησουμε τον κανονα για το test_data_structures ως εξης:
- Κώδικας: Επιλογή όλων
test_data_structures : list.o main.o
g++ -o $(CFLAGS) test_data_structures list.o main.o $(LEDA_LIBS)
Σύμφωνα με τα δυο παρακάτω link,
Έτοιμο παράδειγμα για χρήση...
Σε linux συστήματα χρησιμοποιείστε το παρακάτω κώδικα(δουλειά του migf1) και απλά αλλάξτε τα ονόματα των αρχείων με τα δικά σας,
- Κώδικας: Επιλογή όλων
CC = gcc
CFLAGS = -Wall
OBJECTS = main.o ship.o ui.o ai.o
main: $(OBJECTS)
$(CC) $(CFLAGS) $(OBJECTS) -o main.exe
%.o : %.c
$(CC) $(CFLAGS) -c $<
clean:
rm -f main.exe main.o ship.o ui.o ai.o
Περισσότερες πληροφορίες για το make: http://www.gnu.org/software/make/manual/html_node/index.html