GNU make
White

Minh Tú Anh viết ngày 13/08/2018

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

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

Minh Tú Anh

11 bài viết.
8 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
8 5
Đặt vấn đề mình đang phải đọc một quyển ebook tiếng anh, khổ nỗi tiếng anh thì có hạn. Vì vậy mình thường xuyên phải alt+tab giữa ebook vs google ...
Minh Tú Anh viết 1 tháng trước
8 5
White
6 0
1. Intent là gì ? Intents là một thành phần quan trọng trong android. Nó cho phép các thành phần ứng dụng có thể yêu cầu các hàm từ các thành phần ...
Minh Tú Anh viết 1 năm trước
6 0
White
5 0
Xin chào các bạn. Vào chủ đề chính luôn nhé, hôm nay mình sẽ hướng dẫn các bạn sử dụng fragment trong android. Đây là một thành phần khá quan trọng...
Minh Tú Anh viết 1 năm trước
5 0
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
11 bài viết.
8 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!