Công dụng nào là find_package () nếu bạn cần chỉ định CMAKE_MODULE_PATH?


167

Tôi đang cố gắng để hệ thống xây dựng đa nền tảng hoạt động bằng CMake. Bây giờ phần mềm có một vài phụ thuộc. Tôi tự biên dịch chúng và cài đặt chúng trên hệ thống của mình.

Một số tệp ví dụ đã được cài đặt:

-- Installing: /usr/local/share/SomeLib/SomeDir/somefile
-- Installing: /usr/local/share/SomeLib/SomeDir/someotherfile
-- Installing: /usr/local/lib/SomeLib/somesharedlibrary
-- Installing: /usr/local/lib/SomeLib/cmake/FindSomeLib.cmake
-- Installing: /usr/local/lib/SomeLib/cmake/HelperFile.cmake

Bây giờ CMake có một tệp find_package()mở Find*.cmaketệp và tìm kiếm sau thư viện trên hệ thống và xác định một số biến như SomeLib_FOUNDv.v.

CMakeLists.txt của tôi chứa một cái gì đó như thế này:

set(CMAKE_MODULE_PATH "/usr/local/lib/SomeLib/cmake/;${CMAKE_MODULE_PATH}")
find_package(SomeLib REQUIRED)

Lệnh đầu tiên xác định nơi CMake tìm kiếm sau Find*.cmakevà tôi đã thêm thư mục SomeLibnơi FindSomeLib.cmakecó thể tìm thấy, do đó find_package()hoạt động như mong đợi.

Nhưng điều này thật kỳ lạ bởi vì một trong những lý do tại sao find_package()tồn tại là để thoát khỏi các đường dẫn được mã hóa cứng không chéo.

Làm thế nào điều này thường được thực hiện? Tôi có nên sao chép cmake/thư mục của SomeLibdự án của mình và thiết lập CMAKE_MODULE_PATHtương đối không?


Mô hình đó có vẻ rất kỳ lạ với tôi. Các thư viện sử dụng CMake không được phép tiết lộ mô-đun 'tìm' của họ theo cách này. Làm thế nào bạn nghĩ ra một cách như vậy để tìm ra "Một số người"? Và đó là lib?
SirDarius

2
Một cái gì đó tương tự được thực hiện trong cmake.org/Wiki/ . Và đó là OGRE.
MarcDefiant 23/12/13

2
Phần bạn liên kết để đề cập đến điều này: "Vì CMake (hiện tại) không gửi nó, nên bạn sẽ phải gửi nó trong dự án của mình." Đây là những gì tôi đã làm trong flvmeta để tìm LibYAML (xem github.com/noirotm/flvmeta/tree/master/cmake/modules ). Đường dẫn mô-đun trỏ đến thư mục này, bên trong dự án của tôi.
SirDarius

3
Tôi thường sao chép các mô-đun FindXXX vào dự án của mình và đặt CMAKE_MODULE_PATH (nếu các mô-đun đó không có trong CMake), tôi cũng đã thấy mô hình này nhiều lần trong các dự án khác
szx

Câu trả lời:


214

Lệnh find_packagecó hai chế độ: Modulechế độ và Configchế độ. Bạn đang cố gắng sử dụng Modulechế độ khi bạn thực sự cần Configchế độ.

Chế độ mô-đun

Find<package>.cmaketập tin nằm trong dự án của bạn. Một cái gì đó như thế này:

CMakeLists.txt
cmake/FindFoo.cmake
cmake/FindBoo.cmake

CMakeLists.txt Nội dung:

list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
find_package(Foo REQUIRED) # FOO_INCLUDE_DIR, FOO_LIBRARIES
find_package(Boo REQUIRED) # BOO_INCLUDE_DIR, BOO_LIBRARIES

include_directories("${FOO_INCLUDE_DIR}")
include_directories("${BOO_INCLUDE_DIR}")
add_executable(Bar Bar.hpp Bar.cpp)
target_link_libraries(Bar ${FOO_LIBRARIES} ${BOO_LIBRARIES})

Lưu ý rằng CMAKE_MODULE_PATHcó mức độ ưu tiên cao và có thể hữu ích khi bạn cần viết lại Find<package>.cmaketệp tiêu chuẩn .

Chế độ cấu hình (cài đặt)

<package>Config.cmaketập tin nằm bên ngoài và được sản xuất bởi install lệnh của dự án khác ( Fooví dụ).

foo thư viện:

> cat CMakeLists.txt 
cmake_minimum_required(VERSION 2.8)
project(Foo)

add_library(foo Foo.hpp Foo.cpp)
install(FILES Foo.hpp DESTINATION include)
install(TARGETS foo DESTINATION lib)
install(FILES FooConfig.cmake DESTINATION lib/cmake/Foo)

Phiên bản đơn giản hóa của tập tin cấu hình:

> cat FooConfig.cmake 
add_library(foo STATIC IMPORTED)
find_library(FOO_LIBRARY_PATH foo HINTS "${CMAKE_CURRENT_LIST_DIR}/../../")
set_target_properties(foo PROPERTIES IMPORTED_LOCATION "${FOO_LIBRARY_PATH}")

Theo dự án mặc định được cài đặt trong CMAKE_INSTALL_PREFIXthư mục:

> cmake -H. -B_builds
> cmake --build _builds --target install
-- Install configuration: ""
-- Installing: /usr/local/include/Foo.hpp
-- Installing: /usr/local/lib/libfoo.a
-- Installing: /usr/local/lib/cmake/Foo/FooConfig.cmake

Chế độ cấu hình (sử dụng)

Sử dụng find_package(... CONFIG)để bao gồm FooConfig.cmakevới mục tiêu được nhập foo:

> cat CMakeLists.txt 
cmake_minimum_required(VERSION 2.8)
project(Boo)

# import library target `foo`
find_package(Foo CONFIG REQUIRED)

add_executable(boo Boo.cpp Boo.hpp)
target_link_libraries(boo foo)
> cmake -H. -B_builds -DCMAKE_VERBOSE_MAKEFILE=ON
> cmake --build _builds
Linking CXX executable Boo
/usr/bin/c++ ... -o Boo /usr/local/lib/libfoo.a

Lưu ý rằng mục tiêu nhập khẩu cấu hình cao . Xem câu trả lời của tôi .

Cập nhật


1
Câu trả lời của bạn rất hay. Tuy nhiên, ví dụ tại github phức tạp hơn có thể là IMO. Trong trường hợp phổ biến khi thư mục con (mô-đun) xuất một tạo phẩm duy nhất, giả sử một lib cùng với các tiêu đề, bạn không cần phải tạo tùy chỉnh * Config.cmake. Kết quả là cấu hình có thể được cắt giảm đáng kể. Tôi nghĩ rằng tôi sẽ làm một ví dụ tương tự bản thân mình.
Dimitris

2
@Dimitris Vâng, nó có thể được đơn giản hóa một chút. Tôi đã cập nhật ví dụ github để bây giờ nó không sử dụng configure_package_config_file. Nhân tiện, nếu bạn có bất kỳ đề nghị nào khác, bạn có thể gửi cho tôi yêu cầu kéo.

1
@rusio Đây là ví dụ của tôi . Nó hỗ trợ xây dựng nguyên khối (tất cả các mô-đun từ thư mục gốc) hoặc các bản dựng tự trị (mỗi mô-đun riêng biệt, yêu cầu cài đặt).
Dimitris

1
@Dimitris Được rồi, giờ tôi hiểu rồi. Thông thường tệp mà bạn "tối ưu hóa đi" phục vụ để tải thêm nội dung như find_dependency . Tôi nghĩ rằng đó là một mẫu tốt để bắt đầu vì vậy tôi sẽ giữ nó ngay cả khi nó không được sử dụng trên thực tế. Phần còn lại của mã trông đơn giản hơn vì bạn thiếu một số chức năng như phiên bản, xuất cho dll, bố trí với bin/lib(thử cài đặt thực thi và chạy nó trên windows). Và không gian tên trông rất đẹp, vì vậy tôi cũng sẽ giữ chúng :) Tôi cũng đã thêm bản monolithicdựng.

1
Mỗi ví dụ của bạn rất hữu ích cho tôi. Cảm ơn cả hai người!
zmb

2

Nếu bạn đang chạy cmakeđể SomeLibtự tạo (nói như một phần của siêu máy tính), hãy xem xét sử dụng Sổ đăng ký gói người dùng . Điều này không yêu cầu đường dẫn mã hóa cứng và đa nền tảng. Trên Windows (bao gồm mingw64), nó hoạt động thông qua sổ đăng ký. Nếu bạn kiểm tra danh sách các tiền tố cài đặt được xây dựng theo CONFIGchế độ của lệnh find_packages () , bạn sẽ thấy rằng Sổ đăng ký gói người dùng là một trong các yếu tố.

Giới thiệu ngắn gọn

Liên kết các mục tiêu SomeLibmà bạn cần bên ngoài dự án bên ngoài đó bằng cách thêm chúng vào một bộ xuất trong các CMakeLists.txttệp nơi chúng được tạo:

add_library(thingInSomeLib ...)
install(TARGETS thingInSomeLib Export SomeLib-export DESTINATION lib)

Tạo một XXXConfig.cmaketệp cho SomeLib${CMAKE_CURRENT_BUILD_DIR}và lưu trữ vị trí này trong Sổ đăng ký gói người dùng bằng cách thêm hai lệnh gọi để xuất () vào CMakeLists.txtliên kết với SomeLib:

export(EXPORT SomeLib-export NAMESPACE SomeLib:: FILE SomeLibConfig.cmake) # Create SomeLibConfig.cmake
export(PACKAGE SomeLib)                                                    # Store location of SomeLibConfig.cmake

Phát hành cam kết của bạn find_package(SomeLib REQUIRED)trong CMakeLists.txttệp của dự án phụ thuộc vào SomeLibmà không có "đường dẫn mã hóa cứng không đa nền tảng" mày mò với CMAKE_MODULE_PATH.

Khi nó có thể là phương pháp đúng

Cách tiếp cận này có lẽ phù hợp nhất cho các tình huống trong đó bạn sẽ không bao giờ sử dụng phần mềm của bạn ở phần dưới của thư mục bản dựng (ví dụ: bạn đang biên dịch chéo và không bao giờ cài đặt bất cứ thứ gì trên máy của bạn hoặc bạn đang xây dựng phần mềm chỉ để chạy thử nghiệm trong thư mục bản dựng), vì nó tạo một liên kết đến tệp .cmake trong đầu ra "bản dựng" của bạn, có thể là tạm thời.

Nhưng nếu bạn không bao giờ thực sự cài đặt SomeLibtrong quy trình làm việc của mình, việc gọi EXPORT(PACKAGE <name>)cho phép bạn tránh đường dẫn được mã hóa cứng. Và, tất nhiên, nếu bạn đang cài đặt SomeLib, bạn có thể biết nền tảng của mình CMAKE_MODULE_PATH, v.v., vì vậy câu trả lời tuyệt vời của @ user2288008 sẽ giúp bạn giải quyết.


1

Bạn không cần chỉ định đường dẫn mô-đun mỗi se. CMake vận chuyển với tập lệnh find_package tích hợp sẵn và vị trí của chúng nằm trong CMAKE_MODULE_PATH mặc định.

Trường hợp sử dụng bình thường hơn cho các dự án phụ thuộc đã được CMakeified sẽ sử dụng lệnh bên ngoài của CMake và sau đó bao gồm tệp Use [Project] .cmake từ dự án con. Nếu bạn chỉ cần tập lệnh Tìm [Project] .cmake, hãy sao chép nó ra khỏi dự án con và vào mã nguồn của dự án của riêng bạn, sau đó bạn sẽ không cần phải tăng CMAKE_MODULE_PATH để tìm dự án con ở cấp hệ thống.


12
their location is in the default CMAKE_MODULE_PATHtheo mặc định CMAKE_MODULE_PATHlà trống

Có thể xác nhận bình luận của @ user2288008 trong năm 2018. CMAKE_MODULE_PATHtrống trên Windows.
Jeroen

Đây là một biến số cụ thể của dự án, dành cho các mô-đun vận chuyển với dự án của bạn. "Theo mặc định, nó trống, dự định sẽ được thiết lập bởi dự án." cmake.org/cmake/help/latest/variable/CMAKE_MODULE_PATH.html
Farway

1

Làm thế nào điều này thường được thực hiện? Tôi có nên sao chép cmake/thư mục của someLib vào dự án của mình và đặt CMAKE_MODULE_PATH tương đối không?

Nếu bạn không tin tưởng CMake có mô-đun đó, thì - vâng, hãy làm điều đó - loại: Sao chép find_SomeLib.cmakevà các phụ thuộc của nó vào cmake/thư mục của bạn . Đó là những gì tôi làm như một dự phòng. Đó là một giải pháp xấu.

Lưu ý rằng mỗi FindFoo.cmakemô-đun là một cầu nối giữa sự phụ thuộc vào nền tảng và tính độc lập với nền tảng - chúng tìm kiếm ở nhiều nơi dành riêng cho nền tảng để có được các đường dẫn trong các biến có tên là độc lập với nền tảng.

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.