Chuyển đổi giữa GCC và Clang / LLVM bằng CMake


269

Tôi có một số dự án được xây dựng bằng CMake và tôi muốn có thể dễ dàng chuyển đổi giữa việc sử dụng GCC hoặc Clang / LLVM để biên dịch chúng. Tôi tin rằng (vui lòng sửa lại cho tôi nếu tôi nhầm!) Để sử dụng Clang tôi cần đặt như sau:

    SET (CMAKE_C_COMPILER             "/usr/bin/clang")
    SET (CMAKE_C_FLAGS                "-Wall -std=c99")
    SET (CMAKE_C_FLAGS_DEBUG          "-g")
    SET (CMAKE_C_FLAGS_MINSIZEREL     "-Os -DNDEBUG")
    SET (CMAKE_C_FLAGS_RELEASE        "-O4 -DNDEBUG")
    SET (CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g")

    SET (CMAKE_CXX_COMPILER             "/usr/bin/clang++")
    SET (CMAKE_CXX_FLAGS                "-Wall")
    SET (CMAKE_CXX_FLAGS_DEBUG          "-g")
    SET (CMAKE_CXX_FLAGS_MINSIZEREL     "-Os -DNDEBUG")
    SET (CMAKE_CXX_FLAGS_RELEASE        "-O4 -DNDEBUG")
    SET (CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g")

    SET (CMAKE_AR      "/usr/bin/llvm-ar")
    SET (CMAKE_LINKER  "/usr/bin/llvm-ld")
    SET (CMAKE_NM      "/usr/bin/llvm-nm")
    SET (CMAKE_OBJDUMP "/usr/bin/llvm-objdump")
    SET (CMAKE_RANLIB  "/usr/bin/llvm-ranlib")

Có một cách dễ dàng để chuyển đổi giữa các biến này và các biến GCC mặc định, tốt nhất là thay đổi toàn hệ thống thay vì cụ thể của dự án (nghĩa là không chỉ thêm chúng vào CMakeLists.txt của dự án)?

Ngoài ra, có cần thiết phải sử dụng các llvm-*chương trình thay vì hệ thống mặc định khi biên dịch bằng clang thay vì gcc không? Có gì khác biệt?

Câu trả lời:


342

CMake tôn vinh các biến môi trường CCCXXkhi phát hiện trình biên dịch C và C ++ để sử dụng:

$ export CC=/usr/bin/clang
$ export CXX=/usr/bin/clang++
$ cmake ..
-- The C compiler identification is Clang
-- The CXX compiler identification is Clang

Các cờ cụ thể của trình biên dịch có thể được ghi đè bằng cách đặt chúng vào tệp CMake trên toàn hệ thống và trỏ CMAKE_USER_MAKE_RULES_OVERRIDEbiến vào đó. Tạo một tệp ~/ClangOverrides.txtcó nội dung sau:

SET (CMAKE_C_FLAGS_INIT                "-Wall -std=c99")
SET (CMAKE_C_FLAGS_DEBUG_INIT          "-g")
SET (CMAKE_C_FLAGS_MINSIZEREL_INIT     "-Os -DNDEBUG")
SET (CMAKE_C_FLAGS_RELEASE_INIT        "-O3 -DNDEBUG")
SET (CMAKE_C_FLAGS_RELWITHDEBINFO_INIT "-O2 -g")

SET (CMAKE_CXX_FLAGS_INIT                "-Wall")
SET (CMAKE_CXX_FLAGS_DEBUG_INIT          "-g")
SET (CMAKE_CXX_FLAGS_MINSIZEREL_INIT     "-Os -DNDEBUG")
SET (CMAKE_CXX_FLAGS_RELEASE_INIT        "-O3 -DNDEBUG")
SET (CMAKE_CXX_FLAGS_RELWITHDEBINFO_INIT "-O2 -g")

Hậu tố _INITsẽ làm cho CMake khởi tạo *_FLAGSbiến tương ứng với giá trị đã cho. Sau đó gọi cmaketheo cách sau:

$ cmake -DCMAKE_USER_MAKE_RULES_OVERRIDE=~/ClangOverrides.txt ..

Cuối cùng, để buộc sử dụng các binutils LLVM, hãy đặt biến nội bộ _CMAKE_TOOLCHAIN_PREFIX. Biến này được vinh danh bởi CMakeFindBinUtilsmô-đun:

$ cmake -D_CMAKE_TOOLCHAIN_PREFIX=llvm- ..

Đưa tất cả điều này với nhau, bạn có thể viết một wrapper shell trong đó thiết lập các biến môi trường CCCXXvà sau đó gọi cmakevới ghi đè biến đề cập.


1
Tôi đã làm theo câu trả lời của bạn và mọi thứ trừ CMAKE_USER_MAKE_RULES_OVERRIDEcông việc. Có vẻ như các tập tin đang bị bỏ qua (tức là mặc dùCMAKE_C_FLAGS_RELEASE được đặt thành -O4tệp ghi đè, nhưng nó hiển thị giá trị mặc định của -O3 -DNDEBUGcmake).
Rezzie

18
Lưu ý rằng phần lớn thông tin này được lưu trong bộ đệm CMakeCache.txt ở cấp cao nhất của cây xây dựng của bạn. Để chuyển đổi giữa gcc và clang, bạn nên có hai cây xây dựng hoàn toàn riêng biệt và chỉ cần cd qua lại để "chuyển đổi" trình biên dịch. Khi một cây xây dựng được tạo với một trình biên dịch nhất định, bạn không thể chuyển đổi trình biên dịch cho cây xây dựng đó.
DLRdave

@DLRdave Sử dụng hai cây xây dựng riêng biệt là một ý tưởng hợp lý; một trong những tôi đã không xem xét. Rất tiếc :) Tuy nhiên, ngay cả khi thực hiện nó trong một src/build-clangthư mục mới, các phần ghi đè đang bị bỏ qua.
Rezzie

1
@Rezzie Các cờ trong ClangOverrides.txt phải được xác định bằng hậu tố _INIT. Xem câu trả lời cập nhật.
Luân xa

8
Lưu ý cho độc giả. Nếu bạn gặp sự cố với CMake không tôn trọng các biến CCCXXbiến, có thể là do bạn cần xóa tất cả các tệp khỏi thư mục bản dựng trước. rm CMakeCache.txtcó thể không khôn ngoan
John Dibling

128

Thay đổi toàn hệ thống C ++ trên Ubuntu:

sudo apt-get install clang
sudo update-alternatives --config c++

Sẽ in một cái gì đó như thế này:

  Selection    Path              Priority   Status
------------------------------------------------------------
* 0            /usr/bin/g++       20        auto mode
  1            /usr/bin/clang++   10        manual mode
  2            /usr/bin/g++       20        manual mode

Sau đó, chỉ cần chọn clang ++.


3
Cảm ơn, tôi không biết về điều này! Mặc dù tôi đoán nó phụ thuộc vào nơi cmake đang tìm kiếm một trình biên dịch, phải không?
Ibrahim

1
@Ibrahim Cấu hình này đặt liên kết tượng trưng "c ++" vào trình biên dịch bạn đã chọn và cmake kiểm tra "c ++" theo mặc định, không phải là "g ++". Vì vậy, trừ khi cấu hình cmake rất cụ thể, điều này sẽ hoạt động tốt (và đối với tôi).
Trình phóng to

2
Tôi nhận được câu trả lời rằng "Chỉ có một thay thế trong nhóm liên kết c ++". Vui lòng mở rộng câu trả lời của bạn để bao gồm cách thêm tiếng kêu vào danh sách này
vedant

3
Hãy cẩn thận với sự thay thế này vì nó có thể dẫn đến các tác dụng phụ với hệ thống của bạn. Đã gặp sự cố với các gói như trình điều khiển nvidia biên dịch lại một số mô-đun hạt nhân trong khi cài đặt và không tương thích với tiếng kêu.
Falco

2
Nếu bạn muốn cài đặt vang-3.5, kêu vang-3.6, vv sử dụng này để thiết lập mặc định stackoverflow.com/a/30742451/1427533 như nếu không bạn sẽ nhận đượcThere is only one alternative in link group cc (providing /usr/bin/cc): /usr/bin/gcc
miguel.martin

23

Bạn có thể sử dụng lệnh tùy chọn:

option(USE_CLANG "build application with clang" OFF) # OFF is the default

và sau đó bọc các cài đặt trình biên dịch clang trong if () s:

if(USE_CLANG)
    SET (...)
    ....
endif(USE_CLANG)

Bằng cách này, nó được hiển thị dưới dạng tùy chọn cmake trong các công cụ cấu hình gui.

Để làm cho nó toàn hệ thống, tất nhiên bạn có thể sử dụng biến môi trường làm giá trị mặc định hoặc theo câu trả lời của Ferruccio.


Đó là cách tôi hiện đang thiết lập nó, nhưng rõ ràng nó cần thực hiện trên cơ sở từng dự án. Tôi đã hy vọng sẽ có một lệnh như thế cmake -DCMAKE_COMPILER_DEFINITIONS=MyLlvmDefinitions.cmake.
Rezzie

1
Bây giờ tôi hiểu những gì bạn đang cố gắng thực hiện. Tôi không biết hành vi đó có được cung cấp bởi cmake hay không, nhưng bạn có thể thử tùy chọn -C dường như tải tập lệnh trước khi bắt đầu chạy CMakeLists.txt. Mặc dù chưa thử.
Tobias Schlegel

18

Thay đổi toàn hệ thống C trên Ubuntu:

sudo update-alternatives --config cc

Thay đổi toàn hệ thống C ++ trên Ubuntu:

sudo update-alternatives --config c++

Đối với mỗi mục trên, nhấn Số lựa chọn (1) và Enter để chọn Clang:

  Selection    Path            Priority   Status
------------------------------------------------------------
* 0            /usr/bin/gcc     20        auto mode
  1            /usr/bin/clang   10        manual mode
  2            /usr/bin/gcc     20        manual mode
Press enter to keep the current choice[*], or type selection number:

7
Nếu bạn đã cài đặt clang của mình một cách thủ công và đặt nó ở một nơi không chuẩn, nó có thể không hiển thị với --config. Ví dụ: nếu trước /opt/clang-llvm-3.5/đó hãy cài đặt một thay thế mới: sudo update-alternatives --install /usr/bin/c++ c++ /opt/clang-llvm-3.5/bin/clang++ 30
CodeKid

16

Bạn có thể sử dụng cơ chế tệp toolchain của cmake cho mục đích này, xem ví dụ tại đây . Bạn viết một tệp toolchain cho mỗi trình biên dịch chứa các định nghĩa tương ứng. Tại thời điểm cấu hình, bạn chạy ví dụ

 cmake -DCMAKE_TOOLCHAIN_FILE=/path/to/clang-toolchain.cmake ..

và tất cả thông tin về trình biên dịch sẽ được đặt trong cuộc gọi dự án () từ tệp toolchain. Mặc dù trong tài liệu chỉ đề cập đến trong bối cảnh biên dịch chéo, nó cũng hoạt động tốt cho các trình biên dịch khác nhau trên cùng một hệ thống.


4
Tôi vẫn ngạc nhiên khi các câu trả lời đúng chỉ có thể được tìm thấy ở dưới cùng của một chủ đề ở đây tại SO.
Slava

10

Bạn chắc chắn không cần phải sử dụng các chương trình llvm-ar vv khác nhau:

SET (CMAKE_AR      "/usr/bin/llvm-ar")
SET (CMAKE_LINKER  "/usr/bin/llvm-ld")
SET (CMAKE_NM      "/usr/bin/llvm-nm")
SET (CMAKE_OBJDUMP "/usr/bin/llvm-objdump")
SET (CMAKE_RANLIB  "/usr/bin/llvm-ranlib")

Chúng được tạo ra để hoạt động trên định dạng nội bộ llvm và như vậy không hữu ích cho việc xây dựng ứng dụng của bạn.

Như một lưu ý -O4 sẽ gọi LTO trên chương trình của bạn mà bạn có thể không muốn (nó sẽ tăng thời gian biên dịch lên rất nhiều) và mặc định là chế độ c99 để cờ không nhất thiết phải có.


7

Nếu trình biên dịch mặc định lựa chọn bởi cmakegccvà bạn đã cài đặt clang, bạn có thể sử dụng một cách dễ dàng để biên dịch dự án của bạn với clang:

$ mkdir build && cd build
$ CXX=clang++ CC=clang cmake ..
$ make -j2

5

Theo sự giúp đỡ của cmake:

-C <initial-cache>
     Pre-load a script to populate the cache.

     When cmake is first run in an empty build tree, it creates a CMakeCache.txt file and populates it with customizable settings for the project.  This option may be used to specify a  file  from
     which to load cache entries before the first pass through the project's cmake listfiles.  The loaded entries take priority over the project's default values.  The given file should be a CMake
     script containing SET commands that use the CACHE option, not a cache-format file.

Bạn có thể tạo các tệp như gcc_compiler.txtclang_compiler.txtbao gồm tất cả cấu hình tương đối trong cú pháp CMake.

Ví dụ Clang (clang_compiler.txt):

 set(CMAKE_C_COMPILER "/usr/bin/clang" CACHE string "clang compiler" FORCE)

Sau đó chạy nó như

GCC:

cmake -C gcc_compiler.txt XXXXXXXX

Kêu vang:

cmake -C clang_compiler.txt XXXXXXXX

3

Bạn có thể sử dụng cú pháp: $ENV{environment-variable}trong của bạn CMakeLists.txtđể truy cập các biến môi trường. Bạn có thể tạo các tập lệnh khởi tạo một tập hợp các biến môi trường một cách thích hợp và chỉ cần tham chiếu đến các biến đó trong CMakeLists.txttệp của bạn .


Xin vui lòng bạn có thể xây dựng thêm một chút? Bạn có nghĩa là một kịch bản shell để xuất các biến môi trường trước khi khởi chạy cmake? Những biến nào sẽ cần phải được đặt? Hay bạn có nghĩa là một tập lệnh / bí danh chỉ gọi cmake với -DCMAKE_C_COMPILER ...vv?
Rezzie

Tôi có nghĩa là một kịch bản chỉ xuất các biến môi trường thích hợp. Bạn sẽ tạo các biến môi trường của riêng bạn và tham chiếu chúng trong tệp CMakeLists.txt.
Ferruccio

À; Tôi hiểu ý bạn là gì. Điều duy nhất là yêu cầu phải đi qua CMakeLists.txttừng dự án và yêu cầu nó truy vấn các biến mới. Tôi đã hy vọng sẽ có một cách thuận tiện để thực hiện nó trên toàn hệ thống mà không phải sửa đổi bất kỳ tệp dự án nào. Tương tự như vượt qua a CMAKE_TOOLCHAIN_FILE.
Rezzie
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.