Cú pháp CMake để đặt và sử dụng biến là gì?


168

Tôi đang hỏi điều này như một lời nhắc nhở bản thân vào lần tới khi tôi sử dụng CMake. Nó không bao giờ dính, và kết quả Google không tuyệt vời.

Cú pháp để đặt và sử dụng các biến trong CMake là gì?

Câu trả lời:


281

Khi viết tập lệnh CMake, có rất nhiều thứ bạn cần biết về cú pháp và cách sử dụng các biến trong CMake.

Cú pháp

Chuỗi sử dụng set():

  • set(MyString "Some Text")
  • set(MyStringWithVar "Some other Text: ${MyString}")
  • set(MyStringWithQuot "Some quote: \"${MyStringWithVar}\"")

Hoặc với string():

  • string(APPEND MyStringWithContent " ${MyString}")

Danh sách sử dụng set():

  • set(MyList "a" "b" "c")
  • set(MyList ${MyList} "d")

Hoặc tốt hơn với list():

  • list(APPEND MyList "a" "b" "c")
  • list(APPEND MyList "d")

Danh sách tên tệp:

  • set(MySourcesList "File.name" "File with Space.name")
  • list(APPEND MySourcesList "File.name" "File with Space.name")
  • add_excutable(MyExeTarget ${MySourcesList})

Tài liệu

Phạm vi hoặc "Biến của tôi có giá trị gì?"

Đầu tiên là "Biến thông thường" và những điều bạn cần biết về phạm vi của chúng:

  • Biến bình thường có thể nhìn thấy các CMakeLists.txthọ được thiết lập trong và tất cả mọi thứ được gọi là từ đó ( add_subdirectory(), include(), macro()function()).
  • Các lệnh add_subdirectory()function()là đặc biệt, bởi vì chúng mở ra phạm vi riêng của chúng.
    • Các biến set(...)có nghĩa chỉ có thể nhìn thấy ở đó và chúng tạo một bản sao của tất cả các biến thông thường của mức phạm vi mà chúng được gọi từ (gọi là phạm vi cha).
    • Vì vậy, nếu bạn đang ở trong một thư mục con hoặc một chức năng, bạn có thể sửa đổi một biến đã tồn tại trong phạm vi cha với set(... PARENT_SCOPE)
    • Bạn có thể sử dụng ví dụ này trong các hàm bằng cách chuyển tên biến làm tham số hàm. Một ví dụ sẽ function(xyz _resultVar)là thiết lậpset(${_resultVar} 1 PARENT_SCOPE)
  • Mặt khác, mọi thứ bạn thiết lập include()hoặc macro()tập lệnh sẽ sửa đổi các biến trực tiếp trong phạm vi chúng được gọi từ đâu.

Thứ hai là "Cache biến toàn cầu". Những điều bạn cần biết về Cache:

  • Nếu không có biến thông thường với tên đã cho được xác định trong phạm vi hiện tại, CMake sẽ tìm kiếm mục nhập Cache phù hợp.
  • Giá trị bộ đệm được lưu trữ trong CMakeCache.txttệp trong thư mục đầu ra nhị phân của bạn.
  • Các giá trị trong Cache có thể được sửa đổi trong ứng dụng GUI của CMake trước khi chúng được tạo. Do đó, chúng - so với các biến thông thường - có a typevà a docstring. Tôi thường không sử dụng GUI vì vậy tôi sử dụng set(... CACHE INTERNAL "")để đặt các giá trị toàn cầu và liên tục của mình.

    Xin lưu ý rằng INTERNALloại biến bộ đệm có nghĩa làFORCE

  • Trong tập lệnh CMake, bạn chỉ có thể thay đổi các mục Cache hiện có nếu bạn sử dụng set(... CACHE ... FORCE)cú pháp. Hành vi này được sử dụng ví dụ như bởi chính CMake, vì thông thường nó không bắt buộc các mục nhập Cache và do đó bạn có thể xác định trước nó với một giá trị khác.

  • Bạn có thể sử dụng dòng lệnh để đặt các mục trong Cache với cú pháp cmake -D var:type=value, chỉ cmake -D var=valuehoặc với cmake -C CMakeInitialCache.cmake.
  • Bạn có thể bỏ đặt các mục trong Cache với unset(... CACHE).

Cache là toàn cầu và bạn có thể đặt chúng hầu như ở mọi nơi trong tập lệnh CMake của bạn. Nhưng tôi khuyên bạn nên suy nghĩ kỹ về nơi sử dụng các biến Cache (chúng là toàn cầu và chúng vẫn tồn tại). Tôi thường thích set_property(GLOBAL PROPERTY ...)set_property(GLOBAL APPEND PROPERTY ...)cú pháp để xác định các biến toàn cục không liên tục của riêng tôi.

Cạm bẫy biến và "Làm thế nào để gỡ lỗi thay đổi biến?"

Để tránh những cạm bẫy, bạn nên biết những điều sau về các biến:

  • Các biến cục bộ sẽ ẩn các biến được lưu trong bộ nhớ cache nếu cả hai có cùng tên
  • Các find_...lệnh - nếu thành công - hãy viết kết quả của chúng dưới dạng các biến được lưu trong bộ nhớ cache "để không có cuộc gọi nào sẽ tìm kiếm lại"
  • Danh sách trong CMake chỉ là các chuỗi có dấu phân cách dấu chấm phẩy và do đó dấu ngoặc kép rất quan trọng
    • set(MyVar a b c)đang "a;b;c"set(MyVar "a b c")"a b c"
    • Đề xuất là bạn luôn sử dụng dấu ngoặc kép với một ngoại lệ khi bạn muốn đưa ra một danh sách dưới dạng danh sách
    • Thường thích list()lệnh để xử lý danh sách
  • Toàn bộ vấn đề phạm vi được mô tả ở trên. Đặc biệt, nên sử dụng functions()thay macros()vì vì bạn không muốn các biến cục bộ của mình hiển thị trong phạm vi cha.
  • Rất nhiều biến được CMake sử dụng được đặt với project()enable_language()các lệnh gọi. Vì vậy, điều quan trọng là phải đặt một số biến trước khi các lệnh đó được sử dụng.
  • Biến môi trường có thể khác với nơi CMake tạo môi trường tạo và khi tệp tạo được đưa vào sử dụng.
    • Một thay đổi trong một biến môi trường không kích hoạt lại quá trình tạo.
    • Đặc biệt là một môi trường IDE được tạo có thể khác với dòng lệnh của bạn, vì vậy nên chuyển các biến môi trường của bạn thành một cái gì đó được lưu trữ.

Đôi khi chỉ có các biến gỡ lỗi giúp. Những điều sau đây có thể giúp bạn:

  • Đơn giản chỉ cần sử dụng printfkiểu gỡ lỗi cũ bằng cách sử dụng message()lệnh. Ngoài ra còn có một số mô-đun sẵn sàng để sử dụng với chính CMake: CMakePrintHelpers.cmake , CMakePrintSystemIn information.cmake
  • Nhìn vào CMakeCache.txttập tin trong thư mục đầu ra nhị phân của bạn. Tệp này thậm chí được tạo nếu thế hệ thực tế của môi trường tạo của bạn không thành công.
  • Sử dụng biến_watch () để xem nơi các biến của bạn được đọc / ghi / xóa.
  • Nhìn vào các thuộc tính thư mục CACHE_VARIABLESVARIABLES
  • Gọi cmake --trace ...để xem quá trình phân tích cú pháp hoàn chỉnh của CMake. Đó là loại dự trữ cuối cùng, bởi vì nó tạo ra rất nhiều đầu ra.

Cú pháp đặc biệt

  • Biến môi trường
    • Bạn có thể đọc $ENV{...}và viết set(ENV{...} ...)các biến môi trường
  • Biểu thức máy phát điện
    • Biểu thức trình tạo $<...>chỉ được đánh giá khi trình tạo của CMake ghi môi trường tạo (so sánh với các biến thông thường được thay thế "tại chỗ" bởi trình phân tích cú pháp)
    • Rất tiện dụng, ví dụ như trong các dòng lệnh của trình biên dịch / trình liên kết và trong các môi trường đa cấu hình
  • Người giới thiệu
    • Với ${${...}}bạn có thể đặt tên biến trong một biến và tham chiếu nội dung của nó.
    • Thường được sử dụng khi đưa ra một tên biến là tham số hàm / macro.
  • Giá trị không đổi (xem if()lệnh)
    • Với if(MyVariable)bạn có thể trực tiếp kiểm tra một biến cho đúng / sai (không cần ở đây để kèm theo ${...})
    • True nếu thường xuyên là 1, ON, YES, TRUE, Y, hoặc một số khác không.
    • False nếu hằng số là 0, OFF, NO, FALSE, N, IGNORE, NOTFOUND, chuỗi rỗng, hoặc kết thúc trong hậu tố -NOTFOUND.
    • Cú pháp này thường được sử dụng cho một cái gì đó như if(MSVC), nhưng nó có thể gây nhầm lẫn cho một người không biết phím tắt cú pháp này.
  • Thay thế đệ quy
    • Bạn có thể xây dựng tên biến bằng cách sử dụng các biến. Sau khi CMake đã thay thế các biến, nó sẽ kiểm tra lại nếu kết quả là một biến. Đây là tính năng rất mạnh được sử dụng trong chính CMake, ví dụ như là một mẫuset(CMAKE_${lang}_COMPILER ...)
    • Nhưng hãy lưu ý điều này có thể khiến bạn đau đầu trong if()các lệnh. Dưới đây là một ví dụ trong đó CMAKE_CXX_COMPILER_ID"MSVC"MSVC"1":
      • if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") là đúng, bởi vì nó đánh giá if("1" STREQUAL "1")
      • if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") là sai, bởi vì nó đánh giá if("MSVC" STREQUAL "1")
      • Vì vậy, giải pháp tốt nhất ở đây sẽ là - xem ở trên - để trực tiếp kiểm tra if(MSVC)
    • Tin vui là điều này đã được sửa trong CMake 3.1 với sự ra đời của chính sách CMP0054 . Tôi khuyên bạn nên luôn luôn đặt cmake_policy(SET CMP0054 NEW)thành "chỉ diễn giải các if()đối số dưới dạng các biến hoặc từ khóa khi không được trích dẫn."
  • các option()lệnh
    • Chủ yếu chỉ là các chuỗi được lưu trong bộ nhớ cache mà chỉ có thể ONhoặc OFFchúng cho phép xử lý một số đặc biệt, ví dụ như phụ thuộc
    • Nhưng hãy lưu ý , đừng nhầm optionvới setlệnh. Giá trị được cung cấp optionthực sự chỉ là "giá trị ban đầu" (được chuyển một lần vào bộ đệm trong bước cấu hình đầu tiên) và sau đó được người dùng thay đổi thông qua GUI của CMake .

Người giới thiệu


Khi tôi sử dụng, if ("${MyString}" ...)tôi thấy cảnh báo : Policy CMP0054 is not set: Only interpret if() arguments as variables or keywords when unquoted. Xem, ví dụ, Xây dựng 367 . Có ý kiến ​​gì không?
jww

Và unquoting ${MyString}dẫn đến một loạt các lỗi cho "đối số nếu được ..." như CMake lỗi gần nếu: “nếu đối số cho” tiếp theo parantheses, “KHÔNG”, “bằng” và tương tự .
jww

@jww Cảnh báo có nghĩa là MyStringcó chứa một tên biến mà sau đó sẽ lại được tham chiếu lại. Tôi tin rằng không ai thực sự muốn OLDhành vi đó . Vì vậy, theo quan điểm của tôi, nó hoàn toàn an toàn và tương thích ngược với việc chỉ đặt chính sách CMP0054thành NEW(xem thảo luận tại đây ).
Florian

@jww Và cách an toàn nhất để tránh những vấn đề / cảnh báo đó là chỉ cần làm if (MyString ...)(nếu đó là mã của bạn đưa ra cảnh báo).
Florian

Cảm ơn. Chúng tôi đã xóa tất cả các lần xuất hiện ${MyString}và thay thế bằng MyString(hoặc tôi tin rằng chúng tôi đã xóa tất cả). Vẫn không có niềm vui: Xây dựng 372 . Crap thậm chí không đến từ mã của chúng tôi. Nó dường như đến từ CMake. Dòng 283 là if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC").
jww

18

Dưới đây là một vài ví dụ cơ bản để bắt đầu nhanh và bẩn.

Một biến mục

Đặt biến:

SET(INSTALL_ETC_DIR "etc")

Sử dụng biến:

SET(INSTALL_ETC_CROND_DIR "${INSTALL_ETC_DIR}/cron.d")

Biến đa mục (ví dụ: danh sách)

Đặt biến:

SET(PROGRAM_SRCS
        program.c
        program_utils.c
        a_lib.c
        b_lib.c
        config.c
        )

Sử dụng biến:

add_executable(program "${PROGRAM_SRCS}")

Tài liệu CMake về các biến


1

$ENV{FOO}để sử dụng, nơi FOOđang được chọn từ biến môi trường. mặt khác sử dụng như là ${FOO}, FOOmột số biến khác. Để cài đặt, SET(FOO "foo")sẽ được sử dụng trong CMake.

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.