Makefile - Hướng dẫn nhanh

Việc biên dịch các tệp mã nguồn có thể mệt mỏi, đặc biệt là khi bạn phải bao gồm một số tệp nguồn và gõ lệnh biên dịch mỗi khi bạn cần biên dịch. Makefiles là giải pháp để đơn giản hóa công việc này.

Makefiles là các tệp định dạng đặc biệt giúp xây dựng và quản lý các dự án một cách tự động.

Ví dụ: giả sử chúng ta có các tệp nguồn sau.

  • main.cpp
  • hello.cpp
  • factorial.cpp
  • functions.h

main.cpp

Sau đây là mã cho tệp nguồn main.cpp:

#include <iostream>

using namespace std;

#include "functions.h"

int main(){
   print_hello();
   cout << endl;
   cout << "The factorial of 5 is " << factorial(5) << endl;
   return 0;
}

hello.cpp

Đoạn mã dưới đây dành cho tệp nguồn hello.cpp -

#include <iostream>

using namespace std;

#include "functions.h"

void print_hello(){
   cout << "Hello World!";
}

factorial.cpp

Mã cho factorial.cpp được cung cấp bên dưới:

#include "functions.h"

int factorial(int n){
   
   if(n!=1){
      return(n * factorial(n-1));
   } else return 1;
}

functions.h

Sau đây là mã cho fnctions.h -

void print_hello();
int factorial(int n);

Cách đơn giản để biên dịch tệp và lấy tệp thực thi là chạy lệnh -

gcc  main.cpp hello.cpp factorial.cpp -o hello

Lệnh này tạo ra hello nhị phân. Trong ví dụ này, chúng tôi chỉ có bốn tệp và chúng tôi biết trình tự của các lệnh gọi hàm. Do đó, có thể gõ lệnh trên và chuẩn bị một tệp nhị phân cuối cùng.

Tuy nhiên, đối với một dự án lớn, nơi chúng ta có hàng nghìn tệp mã nguồn, việc duy trì các bản dựng nhị phân trở nên khó khăn.

Các makelệnh cho phép bạn quản lý các chương trình lớn hoặc nhóm chương trình. Khi bạn bắt đầu viết các chương trình lớn, bạn nhận thấy rằng việc biên dịch lại các chương trình lớn mất nhiều thời gian hơn so với việc biên dịch lại các chương trình ngắn. Hơn nữa, bạn nhận thấy rằng bạn thường chỉ làm việc trên một phần nhỏ của chương trình (chẳng hạn như một chức năng đơn lẻ), và phần lớn chương trình còn lại là không thay đổi.

Trong phần tiếp theo, chúng ta sẽ xem cách chuẩn bị một makefile cho dự án của chúng ta.

Các makechương trình cho phép bạn sử dụng macro, tương tự như các biến. Macro được định nghĩa trong cặp Makefile as =. Một ví dụ đã được hiển thị bên dưới -

MACROS  = -me
PSROFF  = groff -Tps
DITROFF = groff -Tdvi
CFLAGS  = -O -systype bsd43
LIBS    = "-lncurses -lm -lsdl"
MYFACE  = ":*)"

Macro đặc biệt

Trước khi phát hành bất kỳ lệnh nào trong tập quy tắc đích, có một số macro đặc biệt được xác định trước -

  • $ @ là tên của tệp sẽ được tạo.

  • $? là tên của những người phụ thuộc đã thay đổi.

Ví dụ, chúng ta có thể sử dụng một quy tắc như sau:

hello: main.cpp hello.cpp factorial.cpp
   $(CC) $(CFLAGS) $? $(LDFLAGS) -o $@

Alternatively:

hello: main.cpp hello.cpp factorial.cpp
   $(CC) $(CFLAGS) [email protected] $(LDFLAGS) -o $@

Trong ví dụ này, $ @ đại diện cho hello và $? hoặc $ @. cpp chọn tất cả các tệp nguồn đã thay đổi.

Có hai macro đặc biệt hơn được sử dụng trong các quy tắc ngầm. Họ là -

  • $ <tên của tệp liên quan đã gây ra hành động.

  • $ * tiền tố được chia sẻ bởi các tệp đích và tệp phụ thuộc.

Quy tắc ngầm phổ biến dành cho việc xây dựng tệp .o (đối tượng) ngoài .cpp (tệp nguồn).

.cpp.o:
   $(CC) $(CFLAGS) -c $<

Alternatively:

.cpp.o:
   $(CC) $(CFLAGS) -c $*.c

Macro thông thường

Có nhiều macro mặc định khác nhau. Bạn có thể thấy chúng bằng cách gõ "make -p" để in ra các giá trị mặc định. Hầu hết đều khá rõ ràng từ các quy tắc mà chúng được sử dụng.

Các biến được xác định trước này, tức là các macro được sử dụng trong các quy tắc ngầm định thuộc hai lớp. Chúng như sau:

  • Macro là tên của chương trình (chẳng hạn như CC)

  • Macro có chứa đối số của chương trình (chẳng hạn như CFLAGS).

Dưới đây là bảng một số biến phổ biến được sử dụng làm tên của chương trình trong quy tắc tích hợp sẵn của makefiles:

Sr.No Biến & Mô tả
1

AR

Chương trình lưu trữ-duy trì; mặc định là `ar '.

2

AS

Chương trình biên dịch tập tin hợp ngữ; mặc định là 'as'.

3

CC

Chương trình biên dịch chương trình C; mặc định là `cc '.

4

CO

Chương trình kiểm tra tệp từ RCS; mặc định là `co '.

5

CXX

Chương trình biên dịch chương trình C ++; mặc định là `g ++ '.

6

CPP

Chương trình chạy bộ tiền xử lý C, với kết quả là đầu ra tiêu chuẩn; mặc định là `$ (CC) -E '.

7

FC

Chương trình biên dịch hoặc xử lý trước các chương trình Fortran và Ratfor; mặc định là `f77 '.

số 8

GET

Chương trình trích xuất một tập tin từ SCCS; mặc định là 'get'.

9

LEX

Chương trình sử dụng để biến ngữ pháp Lex thành mã nguồn; mặc định là `lex '.

10

YACC

Chương trình sử dụng để biến ngữ pháp Yacc thành mã nguồn; mặc định là `yacc '.

11

LINT

Chương trình sử dụng để chạy lint trên mã nguồn; mặc định là `` lint ''.

12

M2C

Chương trình sử dụng để biên dịch mã nguồn Modula-2; mặc định là `m2c '.

13

PC

Chương trình biên dịch chương trình Pascal; mặc định là `pc '.

14

MAKEINFO

Chương trình chuyển đổi tệp nguồn Texinfo thành tệp Info; mặc định là `makeinfo '.

15

TEX

Chương trình tạo tệp dvi TeX từ nguồn TeX; mặc định là `tex '.

16

TEXI2DVI

Chương trình tạo file dvi TeX từ nguồn Texinfo; mặc định là `texi2dvi '.

17

WEAVE

Chương trình dịch Web sang TeX; mặc định là `` dệt ''.

18

CWEAVE

Chương trình dịch C Web sang TeX; mặc định là `cweave '.

19

TANGLE

Chương trình dịch Web sang Pascal; mặc định là `rối '.

20

CTANGLE

Chương trình dịch C Web sang C; mặc định là `ctangle '.

21

RM

Lệnh xóa tệp; mặc định là `rm -f '.

Đây là bảng các biến có giá trị là đối số bổ sung cho các chương trình trên. Các giá trị mặc định cho tất cả các giá trị này là chuỗi trống, trừ khi có ghi chú khác.

Sr.No. Biến & Mô tả
1

ARFLAGS

Cờ để cung cấp cho chương trình duy trì lưu trữ; mặc định là `rv '.

2

ASFLAGS

Các cờ bổ sung để cung cấp cho trình hợp dịch khi được gọi rõ ràng trên tệp `.s 'hoặc` .S'.

3

CFLAGS

Cờ bổ sung để cung cấp cho trình biên dịch C.

4

CXXFLAGS

Cờ bổ sung để cung cấp cho trình biên dịch C.

5

COFLAGS

Cờ bổ sung để cung cấp cho chương trình đồng RCS.

6

CPPFLAGS

Cờ bổ sung để cung cấp cho bộ tiền xử lý C và các chương trình sử dụng nó (chẳng hạn như trình biên dịch C và Fortran).

7

FFLAGS

Cờ bổ sung để cung cấp cho trình biên dịch Fortran.

số 8

GFLAGS

Cờ phụ để cung cấp cho chương trình nhận SCCS.

9

LDFLAGS

Các cờ bổ sung để cung cấp cho trình biên dịch khi chúng được cho là gọi trình liên kết, `ld '.

10

LFLAGS

Cờ phụ để trao cho Lex.

11

YFLAGS

Cờ phụ để tặng cho Yacc.

12

PFLAGS

Các cờ bổ sung để cung cấp cho trình biên dịch Pascal.

13

RFLAGS

Cờ bổ sung để cung cấp cho trình biên dịch Fortran cho các chương trình Ratfor.

14

LINTFLAGS

Cờ phụ để cung cấp cho xơ vải.

NOTE - Bạn có thể hủy bỏ tất cả các biến được sử dụng bởi các quy tắc ngầm với tùy chọn '-R' hoặc '--no-nội trang-biến'.

Bạn cũng có thể xác định macro tại dòng lệnh như hình dưới đây:

make CPP = /home/courses/cop4530/spring02

Rất phổ biến là một tệp nhị phân cuối cùng sẽ phụ thuộc vào các mã nguồn và tệp tiêu đề nguồn khác nhau. Sự phụ thuộc rất quan trọng vì chúng cho phépmakeĐã biết về nguồn cho bất kỳ mục tiêu nào. Hãy xem xét ví dụ sau:

hello: main.o factorial.o hello.o
   $(CC) main.o factorial.o hello.o -o hello

Ở đây, chúng tôi nói với makehello phụ thuộc vào các tệp main.o, factorial.o và hello.o. Do đó, bất cứ khi nào có thay đổi trong bất kỳ tệp đối tượng nào trong số này,make sẽ hành động.

Đồng thời, chúng ta cần nói với makecách chuẩn bị tệp .o. Do đó, chúng ta cũng cần phải xác định những phụ thuộc đó như sau:

main.o: main.cpp functions.h
   $(CC) -c main.cpp

factorial.o: factorial.cpp functions.h
   $(CC) -c factorial.cpp

hello.o: hello.cpp functions.h
   $(CC) -c hello.cpp

Bây giờ chúng ta sẽ tìm hiểu các quy tắc cho Makefile.

Cú pháp chung của quy tắc đích Makefile là:

target [target...] : [dependent ....]
[ command ...]

Trong đoạn mã trên, các đối số trong ngoặc là tùy chọn và dấu chấm lửng có nghĩa là một hoặc nhiều. Ở đây, lưu ý rằng tab để nói đầu mỗi lệnh là bắt buộc.

Một ví dụ đơn giản được đưa ra bên dưới, nơi bạn xác định một quy tắc để làm cho mục tiêu của bạn chào từ ba tệp khác.

hello: main.o factorial.o hello.o
   $(CC) main.o factorial.o hello.o -o hello

NOTE - Trong ví dụ này, bạn sẽ phải đưa ra các quy tắc để tạo tất cả các tệp đối tượng từ các tệp nguồn.

Ngữ nghĩa rất đơn giản. Khi bạn nói "tạo mục tiêu",maketìm quy tắc đích áp dụng; và, nếu bất kỳ người phụ thuộc nào mới hơn mục tiêu,makethực hiện từng lệnh một (sau khi thay thế macro). Nếu bất kỳ phụ thuộc nào phải được thực hiện, điều đó xảy ra đầu tiên (vì vậy bạn có một đệ quy).

Makechấm dứt nếu bất kỳ lệnh nào trả về trạng thái lỗi. Quy tắc sau sẽ được hiển thị trong trường hợp như vậy:

clean:
   -rm *.o *~ core paper

Makebỏ qua trạng thái trả về trên các dòng lệnh bắt đầu bằng dấu gạch ngang. Ví dụ, ai quan tâm nếu không có tệp lõi?

Makelặp lại các lệnh, sau khi thay thế macro để cho bạn biết điều gì đang xảy ra. Đôi khi bạn có thể muốn tắt nó đi. Ví dụ -

install:
   @echo You must be root to install

Mọi người đã mong đợi một số mục tiêu nhất định trong Makefiles. Bạn luôn nên duyệt trước. Tuy nhiên, điều hợp lý là phải tìm thấy tất cả (hoặc chỉ tạo), cài đặt và dọn dẹp.

  • make all - Nó biên dịch mọi thứ để bạn có thể kiểm tra cục bộ trước khi cài đặt ứng dụng.

  • make install - Nó cài đặt các ứng dụng ở đúng nơi.

  • make clean - Nó làm sạch các ứng dụng, loại bỏ các tệp thực thi, mọi tệp tạm thời, tệp đối tượng, v.v.

Quy tắc ngầm định của Makefile

Lệnh này phải hoạt động trong mọi trường hợp khi chúng ta xây dựng một x thực thi từ mã nguồn x.cpp. Điều này có thể được phát biểu như một quy tắc ngầm -

.cpp:
   $(CC) $(CFLAGS) [email protected] $(LDFLAGS) -o $@

Quy tắc ngầm này cho biết cách tạo x ra khỏi xc - chạy cc trên xc và gọi đầu ra là x. Quy tắc này là ngầm vì không có mục tiêu cụ thể nào được đề cập. Nó có thể được sử dụng trong mọi trường hợp.

Một quy tắc ngầm phổ biến khác là xây dựng tệp .o (đối tượng) ngoài .cpp (tệp nguồn).

.cpp.o:
   $(CC) $(CFLAGS) -c $<

alternatively

.cpp.o:
   $(CC) $(CFLAGS) -c $*.cpp

Makecó thể tự động tạo tệp ao, sử dụng cc -c trên tệp .c tương ứng. Các quy tắc này được tích hợp sẵn trongmake, và bạn có thể tận dụng lợi thế này để rút gọn Makefile của mình. Nếu bạn chỉ cho biết các tệp .h trong dòng phụ thuộc của Makefile mà mục tiêu hiện tại phụ thuộc vào,makesẽ biết rằng tệp .cfile tương ứng đã được yêu cầu. Bạn không phải bao gồm lệnh cho trình biên dịch.

Điều này làm giảm Makefile hơn nữa, như hình dưới đây -

OBJECTS = main.o hello.o factorial.o
hello: $(OBJECTS)
   cc $(OBJECTS) -o hello
hellp.o: functions.h

main.o: functions.h 
factorial.o: functions.h

Makesử dụng một đích đặc biệt, có tên .SUFFIXES để cho phép bạn xác định các hậu tố của riêng mình. Ví dụ: hãy tham khảo dòng phụ thuộc được đưa ra bên dưới:

.SUFFIXES: .foo .bar

Nó thông báo make rằng bạn sẽ sử dụng các hậu tố đặc biệt này để đưa ra các quy tắc của riêng mình.

Tương tự như cách makeđã biết cách tạo tệp .o từ tệp .c , bạn có thể xác định các quy tắc theo cách sau:

.foo.bar:
   tr '[A-Z][a-z]' '[N-Z][A-M][n-z][a-m]' < $< > $@
.c.o:
   $(CC) $(CFLAGS) -c $<

Quy tắc đầu tiên cho phép bạn tạo tệp .bar từ tệp .foo . Về cơ bản nó xáo trộn tệp. Quy tắc thứ hai là quy tắc mặc định được sử dụng bởimakeđể tạo tệp .o từ tệp .c .

Có rất nhiều chỉ thị có sẵn dưới nhiều hình thức khác nhau. Cácmakechương trình trên hệ thống của bạn có thể không hỗ trợ tất cả các chỉ thị. Vì vậy, hãy kiểm tra xemmake hỗ trợ các chỉ thị mà chúng tôi đang giải thích ở đây. GNU make hỗ trợ các chỉ thị này.

Chỉ thị có điều kiện

Các chỉ thị có điều kiện là -

  • Các ifeqchỉ thị bắt đầu điều kiện và chỉ định điều kiện. Nó chứa hai đối số, được phân tách bằng dấu phẩy và được bao quanh bởi dấu ngoặc đơn. Việc thay thế biến được thực hiện trên cả hai đối số và sau đó chúng được so sánh. Các dòng của makefile theo sau ifeq được tuân theo nếu hai đối số khớp nhau; nếu không thì chúng bị bỏ qua.

  • Các ifneqchỉ thị bắt đầu điều kiện và chỉ định điều kiện. Nó chứa hai đối số, được phân tách bằng dấu phẩy và được bao quanh bởi dấu ngoặc đơn. Việc thay thế biến được thực hiện trên cả hai đối số và sau đó chúng được so sánh. Các dòng của makefile theo sau ifneq được tuân theo nếu hai đối số không khớp; nếu không thì chúng bị bỏ qua.

  • Các ifdefchỉ thị bắt đầu điều kiện và chỉ định điều kiện. Nó chứa một đối số duy nhất. Nếu đối số đã cho là đúng thì điều kiện trở thành đúng.

  • Các ifndefchỉ thị bắt đầu điều kiện và chỉ định điều kiện. Nó chứa một đối số. Nếu đối số đã cho là sai thì điều kiện trở thành đúng.

  • Các elseLệnh làm cho các dòng sau được tuân theo nếu điều kiện trước đó không thành công. Trong ví dụ trên, điều này có nghĩa là lệnh liên kết thay thế thứ hai được sử dụng bất cứ khi nào lệnh thay thế đầu tiên không được sử dụng. Nó là tùy chọn để có một khác trong một điều kiện.

  • Các endifchỉ thị kết thúc điều kiện. Mọi điều kiện phải kết thúc bằng endif.

Cú pháp của chỉ thị điều kiện

Cú pháp của một điều kiện đơn giản không có điều kiện khác như sau:

conditional-directive
   text-if-true
endif

Text-if-true có thể là bất kỳ dòng văn bản nào, được coi là một phần của makefile nếu điều kiện là true. Nếu điều kiện sai, không có văn bản nào được sử dụng thay thế.

Cú pháp của một điều kiện phức tạp như sau:

conditional-directive
   text-if-true
else
   text-if-false
endif

Nếu điều kiện là đúng, text-if-true được sử dụng; nếu không, văn bản-nếu-sai được sử dụng. Văn bản-nếu-sai có thể là bất kỳ số dòng văn bản nào.

Cú pháp của chỉ thị điều kiện giống nhau cho dù điều kiện đơn giản hay phức tạp. Có bốn chỉ thị khác nhau kiểm tra các điều kiện khác nhau. Chúng như đã cho -

ifeq (arg1, arg2)
ifeq 'arg1' 'arg2'
ifeq "arg1" "arg2"
ifeq "arg1" 'arg2'
ifeq 'arg1' "arg2"

Các chỉ thị trái ngược của các điều kiện trên như sau:

ifneq (arg1, arg2)
ifneq 'arg1' 'arg2'
ifneq "arg1" "arg2"
ifneq "arg1" 'arg2'
ifneq 'arg1' "arg2"

Ví dụ về chỉ thị có điều kiện

libs_for_gcc = -lgnu
normal_libs =

foo: $(objects)
ifeq ($(CC),gcc)
   $(CC) -o foo $(objects) $(libs_for_gcc)
else
   $(CC) -o foo $(objects) $(normal_libs)
endif

Chỉ thị bao gồm

Các include directive cho phép makeđể tạm ngừng đọc makefile hiện tại và đọc một hoặc nhiều makefile khác trước khi tiếp tục. Chỉ thị là một dòng trong makefile trông như sau:

include filenames...

Tên tệp có thể chứa các mẫu tên tệp trình bao. Khoảng trắng thừa được cho phép và bỏ qua ở đầu dòng, nhưng không cho phép một tab. Ví dụ: nếu bạn có ba tệp `.mk ', đó là` `a.mk',` b.mk ', và `c.mk', và $ (bar) thì nó sẽ mở rộng thành bish bash và sau đó là biểu hiện.

include foo *.mk $(bar)

is equivalent to:

include foo a.mk b.mk c.mk bish bash

Khi mà makexử lý một chỉ thị bao gồm, nó tạm ngừng đọc makefile và lần lượt đọc từ từng tệp được liệt kê. Khi điều đó kết thúc,make tiếp tục đọc makefile trong đó chỉ thị xuất hiện.

Chỉ thị ghi đè

Nếu một biến đã được đặt bằng một đối số lệnh, thì các phép gán thông thường trong makefile sẽ bị bỏ qua. Nếu bạn muốn đặt biến trong makefile mặc dù nó đã được đặt bằng một đối số lệnh, bạn có thể sử dụng chỉ thị ghi đè, là một dòng có dạng như sau−

override variable = value

or

override variable := value

Các makechương trình là một tiện ích thông minh và hoạt động dựa trên những thay đổi bạn thực hiện trong các tệp nguồn của mình. Nếu bạn có bốn tệp main.cpp, hello.cpp, factorial.cpp và functions.h, thì tất cả các tệp còn lại phụ thuộc vào functions.h và main.cpp phụ thuộc vào cả hello.cpp và factorial.cpp. Do đó, nếu bạn thực hiện bất kỳ thay đổi nào trong các hàm.h, thìmakebiên dịch lại tất cả các tệp nguồn để tạo tệp đối tượng mới. Tuy nhiên, nếu bạn thực hiện bất kỳ thay đổi nào trong main.cpp, vì điều này không phụ thuộc vào bất kỳ tệp nào khác, thì chỉ tệp main.cpp được biên dịch lại, còn help.cpp và factorial.cpp thì không.

Trong khi biên dịch một tệp, makekiểm tra tệp đối tượng của nó và so sánh các tem thời gian. Nếu tệp nguồn có dấu thời gian mới hơn tệp đối tượng, thì nó tạo tệp đối tượng mới giả sử rằng tệp nguồn đã được thay đổi.

Tránh biên dịch

Có thể có một dự án bao gồm hàng nghìn tệp. Đôi khi bạn có thể đã thay đổi tệp nguồn nhưng bạn có thể không muốn biên dịch lại tất cả các tệp phụ thuộc vào nó. Ví dụ: giả sử bạn thêm một macro hoặc một khai báo vào tệp tiêu đề, mà các tệp khác phụ thuộc vào đó. Bảo thủ,make giả định rằng bất kỳ thay đổi nào trong tệp tiêu đề đều yêu cầu biên dịch lại tất cả các tệp phụ thuộc, nhưng bạn biết rằng chúng không cần biên dịch lại và bạn không muốn lãng phí thời gian chờ chúng biên dịch.

Nếu bạn đoán trước được sự cố trước khi thay đổi tệp tiêu đề, bạn có thể sử dụng cờ `-t '. Lá cờ này nói vớimakekhông chạy các lệnh trong quy tắc, mà là để đánh dấu mục tiêu đã cập nhật bằng cách thay đổi ngày sửa đổi cuối cùng của nó. Bạn cần làm theo quy trình này -

  • Sử dụng lệnh `make 'để biên dịch lại các tệp nguồn thực sự cần biên dịch lại.

  • Thực hiện các thay đổi trong tệp tiêu đề.

  • Sử dụng lệnh `make -t 'để đánh dấu tất cả các tệp đối tượng là cập nhật. Lần tiếp theo bạn chạy make, những thay đổi trong tệp tiêu đề không gây ra bất kỳ biên dịch lại nào.

Nếu bạn đã thay đổi tệp tiêu đề vào thời điểm một số tệp cần biên dịch lại, thì đã quá muộn để thực hiện việc này. Thay vào đó, bạn có thể sử dụng cờ `-o tệp ', cờ này đánh dấu một tệp được chỉ định là" cũ ". Điều này có nghĩa là, bản thân tệp sẽ không được làm lại và không có gì khác sẽ được làm lại trên tài khoản của nó. bạn cần làm theo quy trình này -

  • Biên dịch các tệp nguồn cần biên dịch vì lý do độc lập với tệp tiêu đề cụ thể, với `` tệp tiêu đề make -o ''. Nếu một số tệp tiêu đề có liên quan, hãy sử dụng tùy chọn `-o 'riêng biệt cho từng tệp tiêu đề.

  • Cập nhật tất cả các tệp đối tượng bằng `make -t '.

Trong chương này, chúng ta sẽ xem xét các tính năng khác nhau của Makefile.

Sử dụng đệ quy của Make

Sử dụng đệ quy của make nghĩa là sử dụng makenhư một lệnh trong makefile. Kỹ thuật này hữu ích khi bạn muốn các tệp tạo riêng biệt cho các hệ thống con khác nhau tạo nên một hệ thống lớn hơn. Ví dụ: giả sử bạn có một thư mục con tên là `subir 'có makefile riêng và bạn muốn makefile của thư mục chứa chạymaketrên thư mục con. Bạn có thể làm điều đó bằng cách viết đoạn mã dưới đây -

subsystem:
   cd subdir && $(MAKE)

or, equivalently:
 	
subsystem:
   $(MAKE) -C subdir

Bạn có thể viết đệ quy makechỉ bằng cách sao chép ví dụ này. Tuy nhiên, bạn cần biết về cách chúng hoạt động và lý do cũng như cách sản phẩm phụ liên quan đến sản phẩm cấp cao nhất.

Giao tiếp các biến với một sản phẩm phụ

Giá trị biến của cấp cao nhất makecó thể được chuyển cho sub-make thông qua môi trường theo yêu cầu rõ ràng. Các biến này được định nghĩa trong sub-make dưới dạng mặc định. Bạn không thể ghi đè những gì được chỉ định trong makefile được makefile con sử dụng trừ khi bạn sử dụng công tắc `-e '.

Để chuyển xuống hoặc xuất, một biến, makethêm biến và giá trị của nó vào môi trường để chạy mỗi lệnh. Đến lượt nó, sub-make sử dụng môi trường để khởi tạo bảng giá trị biến của nó.

Các biến đặc biệt SHELL và MAKEFLAGS luôn được xuất (trừ khi bạn hủy xuất chúng). MAKEFILES được xuất nếu bạn đặt nó thành bất kỳ thứ gì.

Nếu bạn muốn xuất các biến cụ thể sang một trang con, hãy sử dụng chỉ thị xuất, như được hiển thị bên dưới:

export variable ...

Nếu bạn muốn ngăn không cho một biến được xuất, hãy sử dụng lệnh unxport, như được hiển thị bên dưới:

unexport variable ...

Biến MAKEFILES

Nếu biến môi trường MAKEFILES được xác định, makecoi giá trị của nó như một danh sách các tên (được phân tách bằng khoảng trắng) của các trang bổ sung sẽ được đọc trước các tệp khác. Điều này hoạt động giống như chỉ thị bao gồm: các thư mục khác nhau được tìm kiếm cho các tệp đó.

Việc sử dụng chính của MAKEFILES là trong giao tiếp giữa các lệnh gọi đệ quy của make.

Bao gồm tệp Header từ các thư mục khác nhau

Nếu bạn đã đặt các tệp tiêu đề trong các thư mục khác nhau và bạn đang chạy maketrong một thư mục khác, thì nó được yêu cầu cung cấp đường dẫn của các tệp tiêu đề. Điều này có thể được thực hiện bằng cách sử dụng tùy chọn -I trong makefile. Giả sử rằng tệp functions.h có sẵn trong thư mục / home / tutorialspoint / header và phần còn lại của các tệp có sẵn trong thư mục / home / tutorialspoint / src /, thì makefile sẽ được viết như sau:

INCLUDES = -I "/home/tutorialspoint/header"
CC = gcc
LIBS =  -lm
CFLAGS = -g -Wall
OBJ =  main.o factorial.o hello.o

hello: ${OBJ}
   ${CC} ${CFLAGS} ${INCLUDES} -o $@ ${OBJS} ${LIBS}
.cpp.o:
   ${CC} ${CFLAGS} ${INCLUDES} -c $<

Nối thêm văn bản vào các biến

Thường thì sẽ rất hữu ích khi thêm nhiều văn bản vào giá trị của một biến đã được xác định. Bạn làm điều này với một dòng có chứa dấu `+ = ', như hình -

objects += another.o

Nó nhận giá trị của các đối tượng biến và thêm văn bản `another.o 'vào nó, trước một khoảng trắng như hình dưới đây.

objects = main.o hello.o factorial.o
objects += another.o

Đoạn mã trên đặt các đối tượng thành `main.o hello.o factorial.o another.o '.

Sử dụng `+ = 'tương tự như:

objects = main.o hello.o factorial.o
objects := $(objects) another.o

Dòng tiếp tục trong Makefile

Nếu bạn không thích các dòng quá lớn trong Makefile, thì bạn có thể ngắt dòng bằng dấu gạch chéo ngược "\" như hình dưới đây -

OBJ =  main.o factorial.o \
   hello.o

is equivalent to

OBJ =  main.o factorial.o hello.o

Chạy Makefile từ Command Prompt

Nếu bạn đã chuẩn bị Makefile với tên "Makefile", thì chỉ cần viết lệnh make tại dấu nhắc lệnh và nó sẽ chạy tệp Makefile. Nhưng nếu bạn đã đặt bất kỳ tên nào khác cho Makefile, thì hãy sử dụng lệnh sau:

make -f your-makefile-name

Đây là một ví dụ về Makefile để biên dịch chương trình hello. Chương trình này bao gồm ba tệp main.cpp , factorial.cpphello.cpp .

# Define required macros here
SHELL = /bin/sh

OBJS =  main.o factorial.o hello.o
CFLAG = -Wall -g
CC = gcc
INCLUDE =
LIBS = -lm

hello:${OBJ}
   ${CC} ${CFLAGS} ${INCLUDES} -o $@ ${OBJS} ${LIBS}

clean:
   -rm -f *.o core *.core

.cpp.o:
   ${CC} ${CFLAGS} ${INCLUDES} -c $<

Bây giờ bạn có thể xây dựng chương trình của mình hello sử dụng make. Nếu bạn sẽ ra lệnhmake clean sau đó nó loại bỏ tất cả các tệp đối tượng và tệp lõi có sẵn trong thư mục hiện tại.