Làm cách nào để tạo Makefile cho các dự án C với các thư mục con SRC, OBJ và BIN?


95

Một vài tháng trước, tôi đã nghĩ ra những Makefilebài tập chung sau đây cho các bài tập ở trường:

# ------------------------------------------------
# Generic Makefile
#
# Author: yanick.rochon@gmail.com
# Date  : 2010-11-05
#
# Changelog :
#   0.01 - first version
# ------------------------------------------------

# project name (generate executable with this name)
TARGET   = projectname

CC       = gcc -std=c99 -c
# compiling flags here
CFLAGS   = -Wall -I.

LINKER   = gcc -o
# linking flags here
LFLAGS   = -Wall

SOURCES  := $(wildcard *.c)
INCLUDES := $(wildcard *.h)
OBJECTS  := $(SOURCES:.c=*.o)
rm       = rm -f

$(TARGET): obj
    @$(LINKER) $(TARGET) $(LFLAGS) $(OBJECTS)
    @echo "Linking complete!"

obj: $(SOURCES) $(INCLUDES)
    @$(CC) $(CFLAGS) $(SOURCES)
    @echo "Compilation complete!"

clean:
    @$(rm) $(TARGET) $(OBJECTS)
    @echo "Cleanup complete!"

Điều này về cơ bản sẽ biên dịch mọi tệp .c.htệp để tạo .otệp và tệp thực thi projectnametất cả trong cùng một thư mục.

Bây giờ, tôi muốn nhấn mạnh điều này một chút. Làm cách nào để viết một Makefile để biên dịch một dự án C với cấu trúc thư mục sau?

 ./
 ./Makefile
 ./src/*.c;*.h
 ./obj/*.o
 ./bin/<executable>

Nói cách khác, tôi muốn có một Makefile biên dịch các nguồn C từ ./src/thành ./obj/và sau đó liên kết mọi thứ để tạo tệp thực thi trong ./bin/.

Tôi đã cố gắng đọc các Makefiles khác nhau, nhưng tôi chỉ đơn giản là không thể làm cho chúng hoạt động cho cấu trúc dự án ở trên; thay vào đó, dự án không thể biên dịch với tất cả các loại lỗi. Chắc chắn, tôi có thể sử dụng IDE hoàn chỉnh (Monodevelop, Anjuta, v.v.), nhưng thực sự tôi thích gắn bó với gEdit và thiết bị đầu cuối tốt hơn.

Có chuyên gia nào có thể cho tôi một giải pháp làm việc hoặc thông tin rõ ràng về cách có thể thực hiện điều này không? Cảm ơn bạn!

** CẬP NHẬT (v4) **

Giải pháp cuối cùng :

# ------------------------------------------------
# Generic Makefile
#
# Author: yanick.rochon@gmail.com
# Date  : 2011-08-10
#
# Changelog :
#   2010-11-05 - first version
#   2011-08-10 - added structure : sources, objects, binaries
#                thanks to http://stackoverflow.com/users/128940/beta
#   2017-04-24 - changed order of linker params
# ------------------------------------------------

# project name (generate executable with this name)
TARGET   = projectname

CC       = gcc
# compiling flags here
CFLAGS   = -std=c99 -Wall -I.

LINKER   = gcc
# linking flags here
LFLAGS   = -Wall -I. -lm

# change these to proper directories where each file should be
SRCDIR   = src
OBJDIR   = obj
BINDIR   = bin

SOURCES  := $(wildcard $(SRCDIR)/*.c)
INCLUDES := $(wildcard $(SRCDIR)/*.h)
OBJECTS  := $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o)
rm       = rm -f


$(BINDIR)/$(TARGET): $(OBJECTS)
    @$(LINKER) $(OBJECTS) $(LFLAGS) -o $@
    @echo "Linking complete!"

$(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.c
    @$(CC) $(CFLAGS) -c $< -o $@
    @echo "Compiled "$<" successfully!"

.PHONY: clean
clean:
    @$(rm) $(OBJECTS)
    @echo "Cleanup complete!"

.PHONY: remove
remove: clean
    @$(rm) $(BINDIR)/$(TARGET)
    @echo "Executable removed!"

Câu hỏi cụ thể ở đây là gì?
Oliver Charlesworth

Tôi không chắc tôi hiểu bạn muốn làm gì.
Tom

Đã cập nhật Makefile. Tôi nhận được gần, nhưng tôi gặp rắc rối với các biến tự động, vì vậy nó có vẻ nào
Yanick Rochon

Tôi chỉ tìm thấy một giải pháp. Nếu ai đó quan tâm đến việc tìm kiếm thứ gì đó tốt hơn, Makefile vẫn có thể được cải thiện.
Yanick Rochon

2
@YanickRochon Tôi không có ý chỉ trích kỹ năng tiếng Anh của bạn. Nhưng để các mục tiêu PHONY có ý nghĩa, bạn chắc chắn không thể viết CHUỐI;) gnu.org/software/make/manual/html_node/Phony-Targets.html
joni

Câu trả lời:


34

Đầu tiên, $(OBJECTS)quy tắc của bạn có vấn đề, bởi vì:

  1. nó là loại bừa bãi, khiến tất cả các nguồn trở thành điều kiện tiên quyết của mọi đối tượng,
  2. nó thường sử dụng sai nguồn (như bạn đã phát hiện với file1.ofile2.o)
  3. nó cố gắng xây dựng các tệp thực thi thay vì dừng lại ở các đối tượng và
  4. tên của target ( foo.o) không phải là những gì quy tắc sẽ thực sự tạo ra ( obj/foo.o).

Tôi đề nghị như sau:

OBJECTS  := $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o)

$(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.c
    $(CC) $(CFLAGS) -c $< -o $@
    @echo "Compiled "$<" successfully!"

Các $(TARGET)quy tắc có cùng một vấn đề rằng tên mục tiêu không thực sự mô tả những gì quy tắc xây dựng. Vì lý do đó, nếu bạn nhập makenhiều lần, Make sẽ xây dựng lại mục tiêu mỗi lần, mặc dù không có lý do gì. Một thay đổi nhỏ khắc phục điều đó:

$(BINDIR)/$(TARGET): $(OBJECTS)
    $(LINKER) $@ $(LFLAGS) $(OBJECTS)
    @echo "Linking complete!"

Sau khi tất cả theo thứ tự, bạn có thể xem xét xử lý phụ thuộc phức tạp hơn; nếu bạn sửa đổi một trong các tệp tiêu đề, thì tệp này sẽ không biết đối tượng / tệp thi hành nào phải được xây dựng lại. Nhưng điều đó có thể chờ một ngày khác.

CHỈNH SỬA:
Xin lỗi, tôi đã bỏ qua một phần của $(OBJECTS)quy tắc ở trên; Tôi đã sửa nó. (Tôi ước mình có thể sử dụng "đình công" bên trong một mẫu mã.)


với những thay đổi được đề xuất của bạn, tôi nhận được:obj/file1.o: In function 'main': \n main.c:(.text+0x0): multiple definition of 'main' \n obj/main.o:main.c:(.text+0x0): first defined here
Yanick Rochon

@Yanick Rochon: Bạn có nhiều mainchức năng không? Có thể một trong file1.cvà một trong main.c? Nếu vậy thì bạn sẽ không thể liên kết các đối tượng này; chỉ có thể có một maintrong tệp thực thi.
Beta

Không tôi không làm thế. Mọi thứ hoạt động tốt với phiên bản cuối cùng tôi đã đăng trong câu hỏi. Khi tôi thay đổi Makefile của mình theo những gì bạn đề xuất (và tôi hiểu những lợi ích của những gì bạn đang nói) đó là những gì tôi nhận được. Tôi chỉ dán file1.cnhưng nó đưa ra cùng một thông điệp cho mọi tệp của dự án. Và main.clà người duy nhất có chức năng chính ... và main.cnhập khẩu file1.hfile2.h(không có mối quan hệ nào giữa file1.cfile2.c), nhưng tôi nghi ngờ vấn đề xuất phát từ đó.
Yanick Rochon

@Yanick Rochon: Tôi đã mắc lỗi khi dán dòng đầu tiên trong $(OBJECTS)quy tắc của mình ; Tôi đã chỉnh sửa nó. Với dòng xấu tôi đã nhận ra lỗi, nhưng không phải là người bạn đã ...
Beta

6

Bạn có thể thêm -Icờ vào cờ trình biên dịch (CFLAGS) để chỉ ra nơi trình biên dịch sẽ tìm kiếm các tệp nguồn và cờ -o để chỉ ra nơi để lại tệp nhị phân:

CFLAGS   = -Wall -I./src
TARGETPATH = ./bin

$(TARGET): obj
    @$(LINKER) $(TARGETPATH)/$(TARGET) $(LFLAGS) $(OBJECTS)
    @echo "Linking complete!"

Để thả tệp đối tượng vào objthư mục, hãy sử dụng -otùy chọn khi biên dịch. Ngoài ra, hãy xem các biến$@$< tự động .

Ví dụ, hãy xem xét Makefile đơn giản này

CFLAGS= -g -Wall -O3                                                            
OBJDIR= ./obj

SRCS=$(wildcard *.c)
OBJS=$(SRCS:.c=.o )
all:$(OBJS)

%.o: %.c 
   $(CC) $(CFLAGS) -c $< -o $(OBJDIR)/$@

Cập nhật>

Bằng cách nhìn vào makefile của bạn, tôi nhận ra bạn đang sử dụng -ocờ. Tốt. Tiếp tục sử dụng nó, nhưng thêm một biến thư mục đích để chỉ ra nơi tệp đầu ra sẽ được ghi.


Bạn có thể đặc sắc hơn không? Bạn có nghĩa là thêm -l ...vào CFLAGSvà ... có đã các -olập luận để mối liên kết ( LINKER)
Yanick Rochon

Có, CFLAGS và vâng, tiếp tục sử dụng -o, chỉ cần thêm biến TARGETPATH.
Tom

Cảm ơn bạn, tôi đã thực hiện các thay đổi, nhưng có vẻ như tôi vẫn đang mất tích somethings (xem bản cập nhật về vấn đề)
Yanick Rochon

chỉ make, từ nơi Makefile ngồi
Yanick Rochon

Bạn không thể đọc lệnh đang được thực thi? ví dụ gcc -c yadayada. Khá chắc chắn có một biến mà không chứa những gì bạn mong đợi
Tom

-1

Tôi đã ngừng viết các tệp makefiles mấy ngày nay, nếu bạn muốn tìm hiểu thì hãy tiếp tục, nếu không, bạn có bộ tạo tệp tạo tệp tốt đi kèm với CDT eclipse. Nếu bạn muốn một số khả năng bảo trì / hỗ trợ nhiều dự án trong cây xây dựng của mình, hãy xem phần sau:

https://github.com/dmoulding/boilermake Tôi thấy cái này khá hay ..!


3
Dựa trên ý kiến. Không trả lời câu hỏi của OP. Giả sử môi trường Eclipse.
Nathaniel Johnson
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.