Có thể yêu cầu CMake xây dựng cả phiên bản tĩnh và chia sẻ của cùng một thư viện không?


141

Cùng một nguồn, tất cả những thứ đó, chỉ muốn một phiên bản tĩnh và chia sẻ cả. Dễ làm gì?

Câu trả lời:


123

Vâng, nó vừa phải dễ dàng. Chỉ cần sử dụng hai lệnh "add_l Library":

add_library(MyLib SHARED source1.c source2.c)
add_library(MyLibStatic STATIC source1.c source2.c)

Ngay cả khi bạn có nhiều tệp nguồn, bạn sẽ đặt danh sách các nguồn trong một biến cmake, do đó vẫn dễ thực hiện.

Trên Windows, có lẽ bạn nên đặt cho mỗi thư viện một tên khác nhau, vì có tệp ".lib" cho cả chia sẻ và tĩnh. Nhưng trên Linux và Mac, bạn thậm chí có thể đặt cho cả hai thư viện cùng tên (ví dụ libMyLib.alibMyLib.so):

set_target_properties(MyLibStatic PROPERTIES OUTPUT_NAME MyLib)

Nhưng tôi không khuyên bạn nên đặt cùng tên phiên bản tĩnh và động của thư viện. Tôi thích sử dụng các tên khác nhau vì điều đó giúp dễ dàng hơn trong việc chọn liên kết tĩnh so với động trên dòng biên dịch cho các công cụ liên kết đến thư viện. Thông thường tôi chọn tên như libMyLib.so(chia sẻ) và libMyLib_static.a(tĩnh). (Đó sẽ là tên trên linux.)


Đã hy vọng họ có cùng tên, nhưng ồ. Một câu hỏi khác: Bạn có thể bảo CMake liên kết các thư viện tĩnh vào thư viện dùng chung khi có thể không?
gct

Tìm hiểu thêm về "cùng tên": Nếu bạn đang ở trên Windows và muốn cùng tên cho cả hai thư viện và bạn không cần tệp .lib được chia sẻ, có thể tạo tệp .lib tĩnh và tệp chia sẻ. Nhưng bạn cần tệp .lib được chia sẻ đó nếu bạn đang sử dụng thư viện của mình để liên kết thời gian biên dịch thông thường.
Christopher Bruns

1
Tôi không chắc tôi hiểu câu hỏi của bạn về việc liên kết các thư viện tĩnh vào thư viện dùng chung.
Christopher Bruns

5
Lưu ý rằng đây không phải là cách được đề xuất để làm điều đó nữa. Đối với các dự án có quy mô không tầm thường (những dự án mất vài phút, không phải vài giây để biên dịch), việc tránh nhân đôi thời gian biên dịch là điều tuyệt vời. Xem user465139 trả lời bên dưới để biết cách sử dụng Thư viện đối tượng hoặc các tài liệu: cmake.org/cmake/help/v3.8/command/ mẹo
KymikoLoco

3
@KymikoLoco: Cách tiếp cận Thư viện đối tượng thực sự làm giảm một nửa thời gian biên dịch, nhưng nó yêu cầu các thư viện tĩnh được xây dựng dưới dạng Mã độc lập vị trí (nghĩa là với -fPIC), bổ sung một lượng nhỏ chi phí thời gian chạy khi các thư viện tĩnh đó được sử dụng. Vì vậy, để có hiệu suất tối đa, câu trả lời này vẫn là tốt nhất.
John Zwinck

95

Kể từ phiên bản CMake 2.8.8, bạn có thể sử dụng "thư viện đối tượng" để tránh việc biên dịch trùng lặp các tệp đối tượng . Sử dụng ví dụ của Christopher Bruns về một thư viện có hai tệp nguồn:

# list of source files
set(libsrc source1.c source2.c)

# this is the "object library" target: compiles the sources only once
add_library(objlib OBJECT ${libsrc})

# shared libraries need PIC
set_property(TARGET objlib PROPERTY POSITION_INDEPENDENT_CODE 1)

# shared and static libraries built from the same object files
add_library(MyLib_shared SHARED $<TARGET_OBJECTS:objlib>)
add_library(MyLib_static STATIC $<TARGET_OBJECTS:objlib>)

Từ các tài liệu CMake :

Một thư viện đối tượng biên dịch các tệp nguồn nhưng không lưu trữ hoặc liên kết các tệp đối tượng của chúng vào một thư viện. Thay vào đó, các mục tiêu khác được tạo bởi add_library()hoặc add_executable()có thể tham chiếu các đối tượng bằng cách sử dụng biểu thức của biểu mẫu dưới dạng $<TARGET_OBJECTS:objlib>nguồn, trong đó objlib là tên thư viện đối tượng.

Nói một cách đơn giản, add_library(objlib OBJECT ${libsrc})lệnh này hướng dẫn CMake biên dịch các tệp nguồn thành các tệp *.ođối tượng. Tập hợp các *.otệp này sau đó được gọi là $<TARGET_OBJECT:objlib>trong hai add_library(...)lệnh gọi các lệnh tạo thư viện thích hợp xây dựng các thư viện tĩnh và thư viện từ cùng một tập các tệp đối tượng. Nếu bạn có nhiều tệp nguồn, thì việc biên dịch các *.otệp có thể mất nhiều thời gian; với các thư viện đối tượng bạn chỉ biên dịch chúng một lần.

Cái giá bạn phải trả là các tệp đối tượng phải được xây dựng dưới dạng mã độc lập với vị trí vì các thư viện dùng chung cần điều này (libs tĩnh không quan tâm). Lưu ý rằng mã độc lập với vị trí có thể kém hiệu quả hơn, vì vậy nếu bạn nhắm đến hiệu suất tối đa thì bạn sẽ tìm đến các thư viện tĩnh. Hơn nữa, nó dễ dàng hơn để phân phối các thực thi được liên kết tĩnh.


3
Điều này hoạt động giống như một cơ duyên đối với tôi - lời cảnh báo duy nhất là các target_link_libraries()cuộc gọi tiếp theo phụ thuộc vào thư viện của bạn không thể sử dụng thư viện đối tượng trên mạng để liên kết với nhau; những người phải nhắm mục tiêu các thư viện chia sẻ hoặc tĩnh mới (và có thể được sao chép). Nhưng trái với kinh nghiệm của những người bình luận đầu tiên, điều này khá hữu ích và cho phép tôi loại bỏ tất cả các mục tiêu trùng lặp và cắt giảm tất cả CMakeLists.txtcác tệp của mình gần một nửa.
cá2000

1
Bạn có cần "thoát" obblib khi đặt thuộc tính đích không? tức là set_property (TARGET $ {objlib} PROPERTY ...) so với set_property (TARGET objlib PROPERTY ...)
gnac

1
Bất cứ ai hạ thấp điều này ... người đó có thể đưa ra lời giải thích về những gì anh ta coi là không chính xác? Hơn nữa bởi vì đây là cách được đề xuất để làm những gì OP muốn, hãy xem các tài liệu CMake.
Laryx Decidua

1
@ user465139 Có lẽ bạn nên giải thích lý do tại sao nên sử dụng lại các tệp đối tượng cho cả mục tiêu tĩnh và chia sẻ. Đặc biệt, kiến ​​thức chung về SO vẫn còn rất khó hiểu về nó, cũ / tài liệu lưu trữ cũng không giúp làm rõ nó, ví dụ. cmake.org/pipermail/cmake/2008-March/020315.html Cần có một lời giải thích chắc chắn về hiện trạng. ps Không phải tôi là người đã đánh giá thấp
mloskot

2
@gnac Tôi không thể xác nhận điều này. Trong trường hợp của tôi, set_propertychỉ hoạt động khi tôi sử dụng objlibvà không khi sử dụng ${objlib}. Vì vậy, có lẽ câu trả lời này có thể được sửa chữa?
josch

22

Nói chung không cần phải lặp lại ADD_LIBRARYcác cuộc gọi cho mục đích của bạn. Chỉ cần sử dụng

$> man cmake | grep -A6 '^ *BUILD_SHARED_LIBS$' 
   BUILD_SHARED_LIBS
          Global flag to cause add_library to create shared libraries if on.

          If present and true, this will cause all libraries to be built shared unless the library was
          explicitly added as a static library.  This variable is often added to projects as an OPTION
          so  that each user of a project can decide if they want to build the project using shared or
          static libraries.

trong khi xây dựng, đầu tiên (trong một thư mục ngoài nguồn) với -DBUILD_SHARED_LIBS:BOOL=ONOFFtrong thư mục khác.


43
Điều này dường như không xây dựng CẢ HAI phiên bản tĩnh và chia sẻ, mà tôi nghĩ là câu hỏi này là gì.
Nick Desaulniers

0

Có thể đóng gói mọi thứ trong cùng một hơi thở tổng hợp, như được đề xuất trong các câu trả lời trước, nhưng tôi sẽ khuyên bạn nên chống lại nó, vì cuối cùng thì đó là một bản hack chỉ hoạt động cho các dự án đơn giản. Ví dụ: tại một số điểm, các cờ khác nhau cho các phiên bản khác nhau của thư viện (đặc biệt trên Windows nơi các cờ thường được sử dụng để chuyển giữa các biểu tượng xuất hoặc không). Hoặc như đã đề cập ở trên, bạn có thể muốn đặt .libcác tệp vào các thư mục khác nhau tùy thuộc vào việc chúng tương ứng với các thư viện tĩnh hoặc chia sẻ. Mỗi rào cản đó sẽ yêu cầu một bản hack mới.

Có thể rõ ràng, nhưng một thay thế chưa được đề cập trước đây là biến loại thư viện thành tham số:

set( ${PROJECT_NAME}_LIBTYPE CACHE STRING "library type" )
set_property( CACHE ${PROJECT_NAME}_LIBTYPE PROPERTY STRINGS "SHARED;STATIC" )
add_library( ${PROJECT_NAME} ${PROJECT_NAME}_LIBTYPE ${SOURCE_FILES} )

Có các phiên bản chia sẻ và tĩnh của thư viện trong hai cây nhị phân khác nhau giúp dễ dàng xử lý các tùy chọn biên dịch khác nhau. Tôi không thấy bất kỳ nhược điểm nghiêm trọng nào trong việc giữ cho các cây biên dịch khác biệt, đặc biệt nếu các phần tổng hợp của bạn được tự động hóa.

Lưu ý rằng ngay cả khi bạn có ý định tương tác các phần tổng hợp bằng OBJECTthư viện trung gian (với các cảnh báo được đề cập ở trên, vì vậy bạn cần một lý do thuyết phục để làm như vậy), bạn vẫn có thể đặt các thư viện cuối vào hai dự án khác nhau.


-2

Nó thực sự có thể. Như @Christopher Bruns đã nói trong câu trả lời của mình, bạn cần thêm hai phiên bản của thư viện:

set(libsrc source1.c source2.c source3.c)
add_library(mylib-static STATIC ${libsrc})
add_library(mylib-shared SHARED ${libsrc})

Sau đó, như được mô tả ở đây , bạn cần xác định rằng cả hai mục tiêu nên sử dụng cùng một tên đầu ra và không ghi đè lên các tệp của nhau:

SET_TARGET_PROPERTIES(mylib-static PROPERTIES OUTPUT_NAME mylib CLEAN_DIRECT_OUTPUT 1)
SET_TARGET_PROPERTIES(mylib-shared PROPERTIES OUTPUT_NAME mylib CLEAN_DIRECT_OUTPUT 1)

Bằng cách này, bạn sẽ nhận được cả libmylib.a và libmylib.so (trên Linux) hoặc mylib.lib và mylib.dll (trên Windows).


10
Điều này là không cần thiết khi sử dụng các phiên bản CMake trên 2.8. [0?], Vì thuộc tính đã bị xóa trong năm 2009 và hành vi mà nó cung cấp hiện là mặc định. Điều này có thể hữu ích cho những người dưới 2,8, nhưng nếu bạn vẫn đang sử dụng CMake <2.7, tôi khuyên bạn nên nâng cấp. github.com/Kitware/CMake/commit/
Mạnh
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.