Làm vs làm mã mẫu?


117

Tôi đã tự hỏi liệu có bất kỳ mã mẫu nào cho Makefiles ( make) và CMakeLists.txt( cmake) mà cả hai đều làm cùng một điều (sự khác biệt duy nhất là một cái được viết bằng makevà cái kia bằng cmake).

Tôi đã thử tìm kiếm 'cmake vs make', nhưng tôi không bao giờ tìm thấy bất kỳ so sánh mã nào. Sẽ thực sự hữu ích nếu hiểu được sự khác biệt, ngay cả khi chỉ đối với một trường hợp đơn giản.


20
+1 Đây là một câu hỏi hay; khi tôi bắt đầu với cmaketôi cũng muốn điều này. Nhưng tôi nghi ngờ bạn sẽ tìm thấy nó bởi vì các khả năng không liên kết tốt giữa cái này với cái kia. Nếu bạn cố làm ra cmakevẻ như vậy make, bạn sẽ khiến bản thân trở nên tồi tệ. Tốt nhất chỉ nên bắt đầu lại từ đầu. Những thứ tầm thường makelại có liên quan khá nhiều cmakevà ngược lại.
Ernest Friedman-Hill

1
@ ErnestFriedman-Hill bạn có biết thêm chi tiết về điều đó không? Vì vậy, makecmakekhác biệt đến mức chúng nên được coi là bổ sung thay vì công cụ cạnh tranh?
Ehtesh Choudhury

2
@Shurane - cmake không tự xây dựng bất cứ thứ gì; nó tạo ra các tệp Makefiles (và các tập lệnh xây dựng tương tự khác), sau đó bạn chạy. Do đó, bất cứ khi nào bạn đang viết các tệp cmake, bạn phải nghĩ xem một lệnh có nên áp dụng trong thời gian tạo hay trong thời gian xây dựng hay không. Một số hành động - ví dụ: sao chép một tập hợp các tệp được ký tự đại diện tại thời điểm xây dựng - khá phức tạp, so với " cp *.x $(OUTDIR)" bạn sẽ viết trong Makefile. Có lẽ một phần khó chịu nhất đối với tôi là những Makefiles tạo ra là do thiết kế hoàn toàn nonportable và không linh hoạt (tiếp theo)
Ernest Friedman-Hill

1
(tiếp theo) Bạn thậm chí không thể di chuyển thư mục nguồn trên cùng một máy mà không chạy lại cmake để tạo lại Makefile! Vì vậy, sự lựa chọn không phải là giữa cmake và make, mà là giữa việc tự viết các tệp Makefiles di động hoặc sử dụng cmake để tạo các tệp không di động trên mỗi máy xây dựng (và cho rằng bạn có thể sử dụng Cygwin hoặc mingw trên Windows, tôi thường thấy cách trước dễ dàng hơn. )
Ernest Friedman-Hill

1
một câu hỏi hay, nhưng không có câu trả lời cụ thể, vì cả hai công cụ đều cố gắng giải quyết một vấn đề khác nhau. cmake lấy thông tin về cách xây dựng chương trình tạo ra các tệp makefiles xây dựng chương trình. Do đó cmake là một ngôn ngữ với các quy tắc xây dựng trừu tượng và gnu make là một giải pháp phụ thuộc thực thi các chương trình trên một đường truyền biểu đồ xoay chiều có hướng.
Alex

Câu trả lời:


118

Makefile sau đây xây dựng một tệp thực thi được đặt tên progtừ các nguồn prog1.c, prog2.c, prog3.c and main.c. progđược liên kết chống lại libmystatlib.alibmydynlib.socả hai cũng được xây dựng từ nguồn. Ngoài ra, progsử dụng thư viện libstuff.atrong stuff/libvà tiêu đề của nó trong stuff/include. Makefile theo mặc định xây dựng một mục tiêu phát hành, nhưng cũng cung cấp một mục tiêu gỡ lỗi:

#Makefile    
CC = gcc
CPP = g++
RANLIB = ar rcs
RELEASE = -c -O3 
DEBUG = -c -g -D_DEBUG
INCDIR = -I./stuff/include
LIBDIR = -L./stuff/lib -L.
LIBS = -lstuff -lmystatlib -lmydynlib
CFLAGS = $(RELEASE)

PROGOBJS = prog1.o prog2.o prog3.o

prog: main.o $(PROGOBJS) mystatlib mydynlib
    $(CC) main.o $(PROGOBJS) $(LIBDIR) $(LIBS) -o prog 
debug: CFLAGS=$(DEBUG)
debug: prog

mystatlib: mystatlib.o
    $(RANLIB) libmystatlib.a mystatlib.o
mydynlib: mydynlib.o
    $(CPP) -shared mydynlib.o -o libmydynlib.so

%.o: %.c
    $(CC) $(CFLAGS) $(INCDIR) $< -o $@ 
%.o: %.cpp
    $(CPP) $(CFLAGS) $(INCDIR) -fPIC  $< -o $@ 

Đây là một CMakeLists.txt(gần như) hoàn toàn giống nhau, với một số nhận xét để nhấn mạnh những điểm tương đồng với Makefile:

#CMakeLists.txt     
cmake_minimum_required(VERSION 2.8)                    # stuff not directly
project(example)                                       # related to building

include_directories(${CMAKE_SOURCE_DIR}/stuff/include) # -I flags for compiler
link_directories(${CMAKE_SOURCE_DIR}/stuff/lib)        # -L flags for linker

set(PROGSRC prog1.c prog2.c prog3.c)                   # define variable 

add_executable(prog main.c ${PROGSRC})                 # define executable target prog, specify sources
target_link_libraries(prog mystatlib mydynlib stuff)   # -l flags for linking prog target

add_library(mystatlib STATIC mystatlib.c)              # define static library target mystatlib, specify sources

add_library(mydynlib SHARED mydynlib.cpp)              # define shared library target mydynlib, specify sources
#extra flags for linking mydynlib
set_target_properties(mydynlib PROPERTIES POSITION_INDEPENDENT_CODE TRUE) 
#alternatively:
#set_target_properties(mydynlib PROPERTIES COMPILE_FLAGS "-fPIC")

Trong ví dụ đơn giản này, sự khác biệt quan trọng nhất là:

  • CMake nhận ra trình biên dịch nào sẽ sử dụng cho loại nguồn nào. Ngoài ra, nó gọi ra chuỗi lệnh phù hợp cho từng loại mục tiêu. Do đó, không có đặc tả rõ ràng về các lệnh như $(CC) ..., $(RANLIB) ...v.v.

  • Tất cả các cờ trình biên dịch / liên kết thông thường xử lý việc bao gồm tệp tiêu đề, thư viện, v.v. được thay thế bằng các lệnh độc lập nền tảng / xây dựng hệ thống độc lập.

  • Cờ gỡ lỗi được bao gồm theo một trong hai cách thiết lập các biến CMAKE_BUILD_TYPEđể "Debug", hoặc bằng cách đi qua nó để CMake khi giản gọi chương trình: cmake -DCMAKE_BUILD_TYPE:STRING=Debug.

  • CMake cũng cung cấp nền tảng độc lập bao gồm cờ '-fPIC' (thông qua thuộc POSITION_INDEPENDENT_CODEtính) và nhiều loại khác. Tuy nhiên, các cài đặt khó hiểu hơn có thể được thực hiện bằng tay trong CMake cũng như trong Makefile (bằng cách sử dụng COMPILE_FLAGS và các thuộc tính tương tự). Tất nhiên CMake thực sự bắt đầu tỏa sáng khi các thư viện của bên thứ ba (như OpenGL) được đưa vào theo cách di động.

  • Quá trình xây dựng có một bước nếu bạn sử dụng Makefile, đó là nhập makevào dòng lệnh. Đối với CMake, có hai bước: Đầu tiên, bạn cần thiết lập môi trường xây dựng của mình (bằng cách nhập cmake <source_dir>vào thư mục xây dựng của bạn hoặc bằng cách chạy một số ứng dụng khách GUI). Điều này tạo ra một Makefile hoặc một cái gì đó tương đương, tùy thuộc vào hệ thống xây dựng mà bạn chọn (ví dụ: tạo trên Unixes hoặc VC ++ hoặc MinGW + Msys trên Windows). Hệ thống xây dựng có thể được chuyển cho CMake như một tham số; tuy nhiên, CMake đưa ra các lựa chọn mặc định hợp lý tùy thuộc vào cấu hình hệ thống của bạn. Thứ hai, bạn thực hiện bản dựng thực tế trong hệ thống bản dựng đã chọn.

Các nguồn và hướng dẫn xây dựng có sẵn tại https://github.com/rhoelzel/make_cmake .


3
Makefile có quá phức tạp không? Bằng cách sử dụng CPPFLAGSthay vì, INCDIRngười ta có thể sử dụng các quy tắc tích hợp sẵn và lệnh gọi rõ ràng của trình biên dịch sẽ là thừa. Tương tự như vậy đối với việc xử lý ar, các quy tắc tích hợp cũng có thể bao gồm điều đó. Ngoài ra, tại sao phải thiết lập CPPCCrõ ràng? Chúng đã được đặt thành các giá trị tốt bởi makechúng là các biến được xác định trước. makecũng nhận ra trình biên dịch nào sẽ sử dụng cho loại nguồn nào, rất nhiều quy tắc tích hợp sẵn.
Christian Hujer

1
Và nhiều biến trong số này nên được gán với :=thay vì =.
Christian Hujer

1
Nhìn vào mô tả, cmakecó thể so sánh với automakehơn make.
ivan_pozdeev 15/02/16

Makefile được cung cấp có thể giảm xuống còn 3/4 dòng. Bạn NÊN chỉ định INCLUDESthay vì INCDIR. Bạn không cần quy tắc% .o:%. C.
shuva 14/03/18

6

Lấy một số phần mềm sử dụng CMake làm hệ thống xây dựng của nó (có rất nhiều dự án nguồn mở để bạn lựa chọn làm ví dụ). Lấy mã nguồn và cấu hình nó bằng CMake. Đọc các trang kết quả và tận hưởng.

Một điều cần lưu ý rằng những công cụ đó không ánh xạ 1-1. Sự khác biệt rõ ràng nhất là CMake quét tìm sự phụ thuộc giữa các tệp khác nhau (ví dụ: tiêu đề C và tệp nguồn), trong khi đó lại để lại điều đó cho các tác giả makefile.


4

Nếu câu hỏi này là về Makefileđầu ra mẫu của CMakeList.txttệp thì vui lòng kiểm tra các nguồn cmake-backend và tạo một nguồn như vậy Makefile. Nếu không thì thêm vào câu trả lời của @Roberto Tôi đang cố gắng làm cho nó đơn giản bằng cách ẩn các chi tiết.

Chức năng CMake

Trong khi Makelà công cụ linh hoạt cho các quy tắc và công thức, CMakelà một lớp trừu tượng cũng bổ sung tính năng cấu hình.

Đồng bằng của tôi CMakeLists.txtsẽ giống như sau,

cmake_minimum_required(VERSION 2.8)
project(example)
file(GLOB testapp_SOURCES *.cc)
add_executable(testapp ${testapp_SOURCES})

Lưu ý, việc CMakeẩn howbản dựng có thể được thực hiện. Chúng tôi chỉ xác định whatlà đầu vào và đầu ra.

Các CMakeLists.txtchứa danh sách các chức năng cuộc gọi được xác định bởi cmake.

(Hàm CMake) Vs Đưa ra các quy tắc

Trong Makefilenhững rules and recipesđược sử dụng thay cho functions. Ngoài functiontính năng -like, rules and recipescung cấp chuỗi. Tối giản của tôi Makefilesẽ trông giống như sau,

-include "executable.mk"
TARGETS=testapp.bin
all:${TARGETS}

Trong khi di executable.mkchúc giống như sau,

SOURCES=$(wildcard *.cpp)
OBJECTS=$(SOURCES:.cpp=.o)
DEPS=$(SOURCES:.cpp=.d)

%.bin:$(OBJECTS)
    $(CC) $(CFLAGS) -o $@ $^ $(LFLAGS) $(LIBS)

.PHONY: all clean

clean:
    $(RM) $(OBJECTS) $(DEPS) $(TARGETS)

-include $(DEPS)

Bắt đầu lại từ đầu, tôi sẽ bắt đầu với những thứ Makefilenhư sau,

all: testapp.bin

testapp.bin:sourcea.o sourcb.o
    $(CC) $(CFLAGS) -o $@ $^ $(LFLAGS) $(LIBS)

.PHONY: all clean

clean:
    $(RM) $(OBJECTS) testapp.bin

Tôi lấy đoạn mã này từ đây và đã sửa đổi nó. Lưu ý rằng một số quy tắc ngầm được thêm vào tệp này có thể được tìm thấy trong tài liệu hướng dẫn makefile. Một số biến ẩn cũng có liên quan ở đây.

Lưu ý, điều đó Makefilecung cấp chi tiết recipecho thấy howviệc xây dựng có thể được thực hiện. Có thể ghi executable.mkđể giữ các chi tiết được xác định trong một tệp. Bằng cách đó, makefile có thể được giảm bớt như tôi đã trình bày trước đó.

Các biến nội bộ trong CMakeMake

Bây giờ đã được nâng cao hơn một chút, CMakechúng ta có thể đặt cờ trình biên dịch như sau,

set(CMAKE_C_FLAGS "-Wall")

Vui lòng tìm hiểu thêm về CMakecác biến mặc định trong CMakeCache.txttệp. Các CMakemã trên sẽ tương đương với Makemã dưới đây,

CFLAGS = -Wall

Lưu ý rằng CFLAGS là một biến nội bộ Make, theo cùng một cách, CMAKE_C_FLAGSlà biến nội bộ trong CMake.

thêm đường dẫn bao gồm và thư viện trong CMake

Chúng ta có thể làm điều đó cmakebằng cách sử dụng các hàm.

target_include_directories(testapp PRIVATE "myincludes")
list(APPEND testapp_LIBRARIES
    mytest mylibrarypath
)
target_link_libraries(testapp ${testapp_LIBRARIES})

Vs thêm bao gồm và đường dẫn thư viện trong Make

Chúng ta có thể thêm bao gồm và thư viện bằng cách thêm các dòng như sau,

INCLUDES += -Imyincludes
LIBS += -Lmylibrarypath -lmytest

Lưu ý rằng các dòng trên có thể được tạo từ các công cụ tạo tự động hoặc pkg-config. (mặc dù Makefile không phụ thuộc vào các công cụ tự động cấu hình)

CMake config / tweek

Thông thường, có thể tạo một số config.htệp giống như auto-configcác công cụ bằng cách sử dụng configure_filehàm. Có thể làm thêm các chức năng tùy chỉnh viết lừa. Và cuối cùng chúng ta có thể chọn một cấu hình như sau,

cmake --build . --config "Release"

Có thể thêm một số tùy chọn có thể định cấu hình bằng cách sử dụng option chức năng.

Cấu hình / tinh chỉnh Makefile

Nếu bằng cách nào đó, chúng ta cần biên dịch nó với một số cờ gỡ lỗi, chúng ta có thể gọi những thứ maketương tự,

make CXXFLAGS=NDEBUG

Tôi nghĩ rằng các biến nội bộ, Makefile-rulesCMake-functionslà khởi đầu tốt cho việc so sánh, chúc may mắn với việc đào sâu hơn.

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.