Bạn có chắc chắn muốn xóa bài viết này không ?
Bạn có chắc chắn muốn xóa bình luận này không ?
GNU make
Giả sử ta có thư mục cdemo
trong thư mục cdemo, ta có 3 file main.c, sum.c, sum.h với nội dung lần lượt như sau
main.c
#include "sum.h"
int main() {
int x;
x = sum(1,2);
printf("x = %d\n", x);
return 0;
}
sum.c
#include "sum.h"
int sum(int a, int b) {
return ((a)+(b));
}
sum.h
#include <stdio.h>
int sum(int a, int b);
Để biên dịch 3 file này, chạy lệnh
gcc -o run main.c sum.c
hoặc
gcc -c main.c
gcc -c sum.c
gcc -o run main.o sum.o
Bây giờ, chúng ta có thêm một file test.c. Chúng ta muốn biên dịch lại, chúng ta cần chạy lệnh
gcc -o run main.c sum.c test.c
Cách này không ổn khi số lượng file nhiều, và vô số vấn đề khác nữa
Vậy làm cách nào giải quyết vấn đề trên
==> Sử dụng automake
============
Mục tiêu chúng ta mong muốn là thêm bao nhiêu file .c đi nữa thì khi muốn build chỉ cần gõ lệnh make
Trong thư mục cdemo, tao một file tên là Makefile có nội dung như sau
run: main.c sum.c
gcc -o run main.c sum.c
Chạy lệnh make tương đương với gcc -o run main.c sum.c
thành phần trước dấu : là TARGET
thành phần sau dấu : là *PREREQUISITE *
Chú ý: dòng thứ 2 bắt đầu phải là dấu TAB
===========
Ta có thể sử dụng các phép gán = (gán cố định), := (giống =), += (có tác dụng giống như nối chuỗi)
TARGET = run
CSRCS += main.c \
sum.c
$(TARGET): $(CSRCS)
gcc -o $(TARGET) $(CSRCS)
Sử dụng $() hoặc ${} để lấy giá trị của các biến
\ là xuống dòng
===========
Nếu bạn đặt tên Makefile là MyMakefile1234 khác với tên mặc định thì khi gọi lệnh make file phải sử dụng tùy chọn -f
$ make -f MyMakefile1234
===========
Sử dụng wildcard để lấy tất cả các file .c có trong thư mục hiện tại
TARGET = run
CSRCS = $(wildcard *.c)
CC = gcc
$(TARGET): $(CSRCS)
$(CC) -o $(TARGET) $(CSRCS)
===========
Khi chạy Makefile, automake sẽ tìm trong thư mục hiện tại có file nào tên là run không. Nếu có thì sẽ không làm gì cả. Để tránh điều này, sử dụng .PHONY
TARGET = run
CSRCS = $(wildcard *.c)
CC = gcc
.PHONY $(TARGET)
$(TARGET): $(CSRCS)
@echo "$@ | $? | $* | $<"
$(CC) -o $@ $(CSRCS)
Thêm @ vào trước các lệnh để không in lại "lệnh" ra màn hình
$@ chính là thành phần trước dấu :
$? là danh sách sau dấu :
$< là phần tử đầu tiên của $?
$* là $< bỏ đi phần extension
Chạy lệnh "make" để build
===========
Cách trên cũng chưa ổn lắm, bởi vì nếu ta chỉ thay đổi 1 file main.c. Khi "make", cả file sum.c cũng được build lại, điều này không cần thiết.
Làm sao để biết file nào thay đổi thì ta build lại đúng file đó thôi. Ta chỉ còn cách build từng file .c ra file .o, sau đó mới linker các file .o lại để ra file thục thi
Sửa lại một chút như sau (01, 02,... là đánh chỉ số dòng để tiện giải thích thôi nha)
01 .PHONY: all, clean
02 TARGET = run
03 CSRCS = $(wildcard *.c)
04 OBJS = $(patsubst %.c, %.o, $(CSRCS)) # tương đương OBJS = sum.o main.o
05 CC = gcc
06 HDRS = sum.h
07
08 all: $(TARGET)
09 $(TARGET): $(OBJS)
10 $(CC) -o $(TARGET) $(OBJS)
11 %.o: %.c $(HDRS)
12 @echo "$< | $? | $*"
13 $(CC) -c $< -o $@
14 clean:
15 rm -R $(TARGET)
16 rm -R $(OBJS)
Để build, ta sử dụng make all, thêm make clean cho nó phong phú.
khi chạy "make all" (all được khai báo với .PHONY nên không kể cả trong cdemo có file tên là all thì vẫn cứ chạy bình thường)
Khi chajy "make all", automake sẽ thấy rằng
- à, all phụ thuộc $(TARGET)
- Nhưng chưa thấy $(TARGET) trong cdemo
- $(TARGET) lại phụ thuộc $(OBJS)
- Cũng chưa thấy $(OBJS) trong cdemo
- à, mỗi phần tử trong $(OBJS) phụ thuộc tương ứng với mỗi phần tử .c cùng tên và $(HDRS)
- phần tử .c cùng tên và $(HDRS) có rồi, triển khai luôn 2 lệnh ở dưới @echo "$< | $? | $*" và $(CC) -c $< -o $@ (nhớ là đoạn này chạy 2 lần vì $(OBJS) có 2 file là main.o và sum.o)
- $(OBJS) đã có, chạy lệnh $(CC) -o $(TARGET) $(OBJS)
Cách viết trên cũng đảm bảo thằng nào thay đổi thì chỉ build lại đúng thằng đấy.
% ở đây đóng vai trò thay thế một đoạn văn bản bất kỳ. Ví dụ, tại dòng 10, vì automake không thấy sum.o và main.o trong cdemo, nó sẽ tìm tất cả các TARGET có sum.o và main.o trong Makefile. % ở đây đóng vai trò thay thế cho sum, main
================
một số tùy chọn
-Wall xuất ra tất cả các cảnh báo
-std=c++11 nói với compiler rằng file.cpp sử dụng C++11
-g sẽ khiến g++ tạo thêm thông tin debug vào file thực thi để hoạt động với các debugger.
-DDEBUG nói rằng tôi đã define biến DEBUG rồi. vì vậy trong thân hàm có đoạn lệnh sau
#ifdef DEBUG
printf("DEBUG MODE");
#endif
thì nó sẽ in ra "DEBUG MODE"
VD: gcc -c -std=c++11 -Wall -g -DDEBUG main.cpp -o main.o
-s khi liên kết, thêm tùy chọn -s để loại bỏ các thông tin thừa, giảm kích cỡ cho file .exe.
VD: g++ -s main.o hello.o -o main
Continue...
GNU make




