Cách thích hợp để sử dụng `pkg-config` từ` cmake` là gì?


81

Nhìn quanh trên mạng, tôi thấy rất nhiều mã như thế này:

include(FindPkgConfig)
pkg_search_module(SDL2 REQUIRED sdl2)

target_include_directories(app SYSTEM PUBLIC ${SDL2_INCLUDE_DIRS}
target_link_libraries(app ${SDL2_LIBRARIES})

Tuy nhiên, đó có vẻ là cách làm sai vì nó chỉ sử dụng các thư mục và thư viện bao gồm, nhưng bỏ qua các định nghĩa, đường dẫn thư viện và các cờ khác có thể được trả về pkg-config.

Cách chính xác để thực hiện việc này và đảm bảo rằng tất cả các cờ biên dịch và liên kết được trả về pkg-configđều được trình biên dịch sử dụng app? Và có một lệnh duy nhất để thực hiện điều này, tức là một cái gì đó như thế target_use(app SDL2)nào?

giới thiệu:

Câu trả lời:


28

Nếu bạn đang sử dụng cmake và pkg-config theo cách khá bình thường, giải pháp này sẽ hoạt động.

Tuy nhiên, nếu bạn có một thư viện tồn tại trong một số thư mục phát triển (chẳng hạn như / home / me / hack / lib), thì việc sử dụng các phương pháp khác được thấy ở đây không thể định cấu hình đường dẫn trình liên kết. Các thư viện không được tìm thấy dưới các vị trí cài đặt điển hình sẽ dẫn đến lỗi trình liên kết, chẳng hạn như /usr/bin/ld: cannot find -lmy-hacking-library-1.0. Giải pháp này khắc phục lỗi trình liên kết cho trường hợp đó.

Một vấn đề khác có thể là các tệp pkg-config không được cài đặt ở nơi bình thường và các đường dẫn pkg-config cho dự án cần được thêm vào bằng cách sử dụng biến môi trường PKG_CONFIG_PATH trong khi cmake đang chạy (xem các câu hỏi khác về Stack Overflow liên quan đến điều này). Giả sử bạn đã thiết lập đúng đường dẫn pkg-config, giải pháp này cũng khắc phục được sự cố đó.

Giải pháp tóm tắt cho phiên bản cuối cùng này của CMakeLists.txt đang hoạt động:

cmake_minimum_required(VERSION 3.14)
project(ya-project C)

# the `pkg_check_modules` function is created with this call
find_package(PkgConfig REQUIRED) 

# these calls create special `PkgConfig::<MODULE>` variables
pkg_check_modules(MY_PKG REQUIRED IMPORTED_TARGET any-package)
pkg_check_modules(YOUR_PKG REQUIRED IMPORTED_TARGET ya-package)

add_executable(program-name file.c ya.c)

target_link_libraries(program-name PUBLIC
        PkgConfig::MY_PKG
        PkgConfig::YOUR_PKG)

Lưu ý rằng target_link_librarieskhông thay đổi các lệnh của trình liên kết. Nó cũng truyền bá các thuộc tính CÔNG KHAI khác của các mục tiêu được chỉ định như: cờ trình biên dịch, định nghĩa trình biên dịch, bao gồm đường dẫn, v.v.


5
IMPORTED_TARGETyêu cầu CMake 3.6 hoặc mới hơn.
Cris Luengo

nếu bạn phản đối điều này, vui lòng đảm bảo và nhận xét tại sao bạn không tán thành để chúng tôi có thể cải thiện câu trả lời.
activedecay

62

Đầu tiên, cuộc gọi:

include(FindPkgConfig)

nên được thay thế bằng:

find_package(PkgConfig)

Cuộc find_package()gọi linh hoạt hơn và cho phép các tùy chọn như REQUIREDtự động thực hiện những việc mà người ta sẽ phải thực hiện theo cách thủ công include().

Thứ hai, pkg-confignên tránh gọi thủ công khi có thể. CMake đi kèm với một bộ định nghĩa gói phong phú, được tìm thấy trong Linux /usr/share/cmake-3.0/Modules/Find*cmake. Các tùy chọn này cung cấp nhiều tùy chọn và sự lựa chọn cho người dùng hơn là một cuộc gọi thô tới pkg_search_module().

Đối với target_use()lệnh giả định đã đề cập , CMake đã tích hợp sẵn lệnh đó với PUBLIC | PRIVATE | INTERFACE. Một lệnh gọi like target_include_directories(mytarget PUBLIC ...)sẽ làm cho các thư mục bao gồm được tự động sử dụng trong mọi mục tiêu sử dụng mytarget, ví dụ target_link_libraries(myapp mytarget). Tuy nhiên, cơ chế này dường như chỉ dành cho các thư viện được tạo trong CMakeLists.txttệp và không hoạt động đối với các thư viện được mua bằng pkg_search_module(). Cuộc gọi add_library(bar SHARED IMPORTED)có thể được sử dụng cho điều đó, nhưng tôi chưa xem xét điều đó.

Đối với câu hỏi chính, điều này ở đây hoạt động trong hầu hết các trường hợp:

find_package(PkgConfig REQUIRED)
pkg_check_modules(SDL2 REQUIRED sdl2)
...
target_link_libraries(testapp ${SDL2_LIBRARIES})
target_include_directories(testapp PUBLIC ${SDL2_INCLUDE_DIRS})
target_compile_options(testapp PUBLIC ${SDL2_CFLAGS_OTHER})

Hàm SDL2_CFLAGS_OTHERchứa các định nghĩa và các cờ khác cần thiết để biên dịch thành công. Các lá cờ SDL2_LIBRARY_DIRSSDL2_LDFLAGS_OTHERtuy nhiên vẫn bị bỏ qua, không biết mức độ thường xuyên đó sẽ trở thành một vấn đề.

Tài liệu khác tại đây http://www.cmake.org/cmake/help/v3.0/module/FindPkgConfig.html


4
Tôi đồng ý rằng pkg-config nên tránh NẾU một Find * .cmake tồn tại, nhưng điều đó vẫn không phải là trường hợp cho các phiên bản mới nhất của cmake trong năm 2016.
Cubic

3
Điều này không hoạt động nếu các thư viện không có trong các thư mục mặc định. link_directories () có thể là một giải pháp thay thế, nhưng nó là toàn cầu.
Henry Hu

Cách tiếp cận này không hoạt động đối với vcpkg . Tôi có thể định vị SDL2_image mà không có đường dẫn mã hóa cứng không !?
user2023370,

@HenryHu, bạn có thể chỉ ra cách điều này sẽ được thực hiện nếu đây là tình huống trong cấu hình trong câu trả lời này không?
Tim Visée,

2
Yêu cầu một công cụ xây dựng như CMake để kết hợp các phương pháp heuristics để xem xét mọi thư viện trên thế giới là vô nghĩa, đó không phải là vai trò của nó. Pkg-config được thiết kế để tác giả lib hoặc người bảo trì pkg / distro có trách nhiệm cung cấp nó cho người dùng. Và nếu kế hoạch này được tuân theo, cách chính xác để sử dụng lib luôn là gọi pkg-config.
Johan Boulé

10

Thật hiếm khi người ta chỉ cần liên kết với SDL2. Câu trả lời phổ biến hiện nay sử dụng cách pkg_search_module()kiểm tra các mô-đun đã cho và sử dụng câu trả lời hoạt động đầu tiên.

Nhiều khả năng bạn muốn liên kết với SDL2 và SDL2_Mixer và SDL2_TTF, v.v. pkg_check_modules()kiểm tra tất cả các mô-đun đã cho.

# sdl2 linking variables
find_package(PkgConfig REQUIRED)
pkg_check_modules(SDL2 REQUIRED sdl2 SDL2_ttf SDL2_mixer SDL2_image)

# your app
file(GLOB SRC "my_app/*.c")
add_executable(my_app ${SRC})
target_link_libraries(my_app ${SDL2_LIBRARIES})
target_include_directories(my_app PUBLIC ${SDL2_INCLUDE_DIRS})
target_compile_options(my_app PUBLIC ${SDL2_CFLAGS_OTHER})

Tuyên bố từ chối trách nhiệm: Tôi sẽ chỉ nhận xét về câu trả lời của chính Grumbel nếu tôi có đủ tín dụng đường phố với stackoverflow.


2
Không khuyến khích các tập tin nguồn lấp lánh.
liberforce

1
Đối với tôi, target_link_libraries(my_app ${SDL2_LINK_LIBRARIES})làm việc tốt hơn.
stefan

2
@liberforce Globbing tệp nguồn là một phương pháp hay, đó là lỗi của CMake nếu nó bị lỗi.
Johan Boulé

1
@ JohanBoulé: Không, không phải. Bạn có thể yêu cầu một nhà phát triển thêm một loạt các tệp cục bộ và để công cụ hoạt động trên máy tính của họ, và không giới hạn tất cả các tệp cần thiết. Sau đó, họ đẩy những thay đổi của họ và nó sẽ bị phá vỡ đối với những người khác. Chắc chắn, điều này có thể bị bắt gặp bởi một số tích hợp liên tục, nhưng đây chỉ là vấn đề rõ ràng nhất. Các Meson xây dựng hệ thống đã chọn để không thực hiện tập tin globing , và phát triển một cách rõ ràng CMake nản globbing . Rõ ràng là tốt hơn ngầm.
liberforce

1
@liberforce Tôi đã thấy lập luận đó nhiều hơn nhiều lần so với vấn đề thực tế mà nó đưa ra. Meson chống lại, build2 cho. Sẽ không có nó, giống như tab và không gian.
Johan Boulé

6

Hầu hết các câu trả lời có sẵn không thể định cấu hình tiêu đề cho pkg-configthư viện. Sau khi suy ngẫm về Tài liệu cho FindPkgConfig, tôi đã nghĩ ra một giải pháp cung cấp những thứ đó:

include(FindPkgConfig)
if(NOT PKG_CONFIG_FOUND)
  message(FATAL_ERROR "pkg-config not found!" )
endif()
 
pkg_check_modules(<some-lib> REQUIRED IMPORTED_TARGET <some-lib>)
 
target_link_libraries(<my-target> PkgConfig::<some-lib>)

( Thay thế mục tiêu của bạn ở vị trí <my-target>và bất kỳ thư viện nào ở vị trí <some-lib>, tương ứng. )

Các IMPORTED_TARGETtùy chọn có vẻ là chìa khóa và làm cho tất cả mọi thứ sau đó phát hành theo PkgConfig::namespace. Đây là tất cả những gì được yêu cầu và cũng là tất cả những gì cần phải có.


MẸO: in cmake var sau khi chạy pkg_check_modulesđể xem các vars stackoverflow.com/a/9328525/1211174
Oak

0
  1. Không có lệnh như vậy target_use. Nhưng tôi biết một số dự án đã viết một lệnh như vậy để sử dụng nội bộ của họ. Nhưng mọi dự án đều muốn vượt qua các cờ hoặc định nghĩa bổ sung, do đó, không có ý nghĩa gì nếu có nó trong CMake nói chung. Một lý do khác để không có nó là các thư viện mẫu C ++ như Eigen, không có thư viện mà bạn chỉ có một loạt các tệp bao gồm.

  2. Cách được mô tả thường đúng. Nó có thể khác nhau đối với một số thư viện, sau đó bạn sẽ phải thêm _LDFLAGShoặc _CFLAGS. Thêm một lý do để không có target_use. Nếu nó không hiệu quả với bạn, hãy hỏi một câu hỏi mới cụ thể về SDL2 hoặc bất kỳ thư viện nào bạn muốn sử dụng.


-2

Nếu bạn cũng đang tìm cách thêm các định nghĩa từ thư viện, thì add_definitionshướng dẫn sẽ có ở đó. Tài liệu có thể được tìm thấy ở đây , cùng với nhiều cách khác để thêm cờ trình biên dịch.

Đoạn mã sau sử dụng hướng dẫn này để thêm GTKGL vào dự án:

pkg_check_modules(GTKGL REQUIRED gtkglext-1.0)
include_directories(${GTKGL_INCLUDE_DIRS})
link_directories(${GTKGL_LIBRARY_DIRS})
add_definitions(${GTKGL_CFLAGS_OTHER})
set(LIBS ${LIBS} ${GTKGL_LIBRARIES})

target_link_libraries([insert name of program] ${LIBS})

3
Không sử dụng include_directoriesvv nó sẽ lây nhiễm phạm vi toàn cầu! Sử dụng target_include_directoriesvv
Dawid Drozd
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.