เป็นไปได้ไหมที่ CMake จะสร้างทั้งไลบรารีแบบคงที่และแบบแบ่งใช้


141

แหล่งที่มาเดียวกันทั้งหมดต้องการเพียงแค่รุ่นคงที่และใช้ร่วมกันทั้งคู่ ง่ายที่จะทำ?

คำตอบ:


123

ใช่มันง่ายพอสมควร เพียงใช้คำสั่ง "add_library" สองคำสั่ง:

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

แม้ว่าคุณจะมีไฟล์ต้นฉบับมากมายคุณก็จะวางรายการของแหล่งข้อมูลไว้ในตัวแปร cmake ดังนั้นมันก็ยังง่ายที่จะทำ

บน Windows คุณอาจตั้งชื่อที่แตกต่างกันเนื่องจากมีไฟล์ ".lib" สำหรับทั้งที่ใช้ร่วมกันและแบบคงที่ แต่บน Linux และ Mac คุณสามารถตั้งชื่อให้ห้องสมุดทั้งสองในชื่อเดียวกัน (เช่นlibMyLib.aและlibMyLib.so):

set_target_properties(MyLibStatic PROPERTIES OUTPUT_NAME MyLib)

แต่ฉันไม่แนะนำให้ตั้งชื่อไลบรารีทั้งแบบคงที่และแบบไดนามิก ฉันชอบที่จะใช้ชื่อที่แตกต่างกันเพราะมันทำให้ง่ายต่อการเลือกการเชื่อมโยงแบบคงที่และแบบไดนามิกบนบรรทัดการคอมไพล์สำหรับเครื่องมือที่ลิงก์ไปยังไลบรารี ฉันมักจะเลือกชื่อเช่นlibMyLib.so(แชร์) และlibMyLib_static.a(คงที่) (ชื่อเหล่านั้นจะเป็นชื่อบน linux)


หวังว่าพวกเขาจะมีชื่อเดียวกัน แต่ก็ดี คำถามอื่น: คุณสามารถบอก CMake ให้ลิงก์สแตติกไลบรารีกับไลบรารีที่แชร์ได้หรือไม่
gct

เพิ่มเติมเกี่ยวกับ "ชื่อเดียวกัน": หากคุณอยู่บน Windows และต้องการชื่อเดียวกันสำหรับทั้งสองไลบรารีและคุณไม่ต้องการไฟล์. lib ที่ใช้ร่วมกันคุณสามารถสร้าง. lib และ static .dll ที่ใช้ร่วมกันได้ แต่คุณต้องใช้ไฟล์. lib ร่วมกันหากคุณใช้ไลบรารีของคุณสำหรับการเชื่อมโยงเวลาคอมไพล์ปกติ
Christopher Bruns

1
ฉันไม่แน่ใจว่าฉันเข้าใจคำถามของคุณเกี่ยวกับการเชื่อมโยงห้องสมุดคงที่เข้ากับห้องสมุดสาธารณะ
Christopher Bruns

5
โปรดทราบว่านี่ไม่ใช่วิธีที่แนะนำให้ทำอีกต่อไป สำหรับโครงการที่ไม่ได้มีขนาดเล็ก (โครงการที่ใช้เวลาไม่กี่นาทีในการรวบรวมไม่กี่วินาที) การหลีกเลี่ยงการรวบรวมเวลาสองเท่าเป็นสิ่งมหัศจรรย์ ดู user465139 คำตอบด้านล่างสำหรับการใช้งาน Object Library หรือเอกสาร: cmake.org/cmake/help/v3.8/command/…
KymikoLoco

3
@KymikoLoco: วิธีการ Object Library ช่วยลดเวลาในการคอมไพล์ลงครึ่งหนึ่ง แต่ต้องใช้ไลบรารีแบบคงที่เพื่อสร้างรหัสอิสระของตำแหน่ง (เช่นกับ-fPIC) ซึ่งเพิ่มจำนวนเล็กน้อยของค่าใช้จ่ายในการรันไทม์เมื่อใช้ไลบรารีแบบคงที่เหล่านั้น ดังนั้นเพื่อประสิทธิภาพสูงสุดคำตอบนี้ยังดีที่สุด
John Zwinck

95

ตั้งแต่ CMake เวอร์ชัน 2.8.8, คุณสามารถใช้ "ห้องสมุดวัตถุ" ที่จะหลีกเลี่ยงการสะสมของไฟล์ที่ซ้ำวัตถุ การใช้ตัวอย่างของ Christopher Bruns ของไลบรารีที่มีไฟล์ต้นฉบับสองไฟล์:

# 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>)

จากCMake เอกสาร :

ไลบรารีวัตถุรวบรวมไฟล์ต้นฉบับ แต่ไม่ได้เก็บถาวรหรือลิงก์ไฟล์วัตถุของพวกเขาไปยังห้องสมุด แทนเป้าหมายอื่นที่สร้างโดย add_library()หรือadd_executable()อาจอ้างอิงวัตถุโดยใช้นิพจน์ของแบบฟอร์ม$<TARGET_OBJECTS:objlib>เป็นแหล่งที่มาซึ่ง objlib เป็นชื่อไลบรารีวัตถุ

เพียงแค่ใส่add_library(objlib OBJECT ${libsrc})คำสั่งสั่งให้ CMake รวบรวมไฟล์ต้นฉบับไปยัง*.oไฟล์วัตถุ คอลเล็กชันของ*.oไฟล์นี้ถูกอ้างถึง$<TARGET_OBJECT:objlib>ในสองadd_library(...)คำสั่งที่เรียกใช้คำสั่งการสร้างไลบรารีที่เหมาะสมที่สร้างไลบรารีแบบแบ่งใช้และแบบสแตติกจากชุดของอ็อบเจ็กต์ไฟล์ชุดเดียวกัน หากคุณมีไฟล์ต้นฉบับมากมายการรวบรวม*.oไฟล์อาจใช้เวลานาน ด้วยไลบรารีวัตถุคุณรวบรวมพวกเขาเพียงครั้งเดียว

ราคาที่คุณจ่ายคือไฟล์อ็อบเจ็กต์จะต้องถูกสร้างเป็นรหัสที่ไม่ขึ้นอยู่กับตำแหน่งเพราะไลบรารี่ที่แชร์นั้นต้องการสิ่งนี้ (libs แบบสแตติกไม่สนใจ) โปรดทราบว่ารหัสที่ไม่ขึ้นอยู่กับตำแหน่งนั้นอาจมีประสิทธิภาพน้อยกว่าดังนั้นหากคุณตั้งเป้าหมายเพื่อให้ได้ประสิทธิภาพสูงสุด นอกจากนี้ยังง่ายต่อการกระจายไฟล์ที่เชื่อมโยงแบบคงที่


3
สิ่งนี้ทำงานได้อย่างมีเสน่ห์สำหรับฉัน - ข้อแม้เพียงอย่างเดียวคือการtarget_link_libraries()โทรตามลำดับ ที่ขึ้นอยู่กับห้องสมุดของคุณไม่สามารถใช้ "ห้องสมุดวัตถุ" เพื่อเชื่อมโยงกับ; สิ่งเหล่านี้จะต้องกำหนดเป้าหมายไลบรารีที่แชร์หรือสแตติกใหม่ (และอาจซ้ำกัน) แต่ตรงกันข้ามกับประสบการณ์ของนักวิจารณ์คนแรกมันค่อนข้างมีประโยชน์และอนุญาตให้ฉันลบเป้าหมายที่ซ้ำกันทั้งหมดและตัดCMakeLists.txtไฟล์ทั้งหมดของฉันเกือบครึ่ง
fish2000

1
คุณต้องการ "หลบหนี" obblib เมื่อตั้งค่าคุณสมบัติเป้าหมายหรือไม่? ie set_property (เป้าหมาย $ {objlib} คุณสมบัติ ... ) vs set_property (เป้าหมาย objlib คุณสมบัติ ... )
gnac

1
ใครก็ตามที่ลงคะแนนให้ ... คน ๆ นี้สามารถให้คำอธิบายว่าอะไรที่เขา / เธอถือว่าไม่ถูกต้อง? ทั้งหมดนี้เป็นวิธีที่แนะนำให้ทำในสิ่งที่ OP ต้องการดูเอกสารของ CMake
Laryx Decidua

1
@ user465139 บางทีคุณควรอธิบายว่าทำไมจึงควรใช้งานไฟล์อ็อบเจ็กต์อีกครั้งสำหรับเป้าหมายแบบคงที่และใช้ร่วมกัน โดยเฉพาะอย่างยิ่งความรู้ทั่วไปใน SO ยังคงสับสนอยู่มากเอกสารเก่า / เก่าไม่ได้ช่วยอธิบายให้ชัดเจนเช่น cmake.org/pipermail/cmake/2008-March/020315.html จำเป็นต้องมีคำอธิบายที่ชัดเจนเกี่ยวกับสถานะที่เป็นอยู่ ป.ล. มันไม่ใช่ฉันที่ลงคะแนน
mloskot

2
@gnac ฉันไม่สามารถยืนยันได้ ในกรณีของฉันที่set_propertyทำงานเฉพาะเมื่อผมใช้และไม่ได้เมื่อใช้objlib ${objlib}ดังนั้นบางทีคำตอบนี้สามารถแก้ไขได้?
josch

22

โดยทั่วไปไม่จำเป็นต้องทำการADD_LIBRARYโทรซ้ำเพื่อวัตถุประสงค์ของคุณ เพียงแค่ใช้ประโยชน์จาก

$> 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.

ในขณะที่สร้างสิ่งแรก (ในไดเรกทอรีนอกแหล่งหนึ่ง) ด้วย-DBUILD_SHARED_LIBS:BOOL=ONและOFFในอีกไดเรกทอรีหนึ่ง


43
ดูเหมือนจะไม่สร้างทั้งแบบคงที่และแบบแบ่งใช้ซึ่งฉันคิดว่าเป็นสิ่งที่คำถามนี้ได้รับ
Nick Desaulniers

0

เป็นไปได้ที่จะบรรจุสิ่งของในลมหายใจการรวบรวมเดียวกันตามที่แนะนำในคำตอบก่อนหน้า แต่ฉันจะแนะนำกับมันเพราะในตอนท้ายมันเป็นแฮ็คที่ใช้งานได้สำหรับโครงการง่ายๆเท่านั้น ตัวอย่างเช่นคุณอาจจำเป็นต้องใช้ค่าสถานะที่แตกต่างกันสำหรับไลบรารี่รุ่นต่าง ๆ (โดยเฉพาะใน Windows โดยทั่วไปจะใช้ค่าสถานะเพื่อสลับระหว่างการส่งออกสัญลักษณ์หรือไม่) หรือตามที่กล่าวไว้ข้างต้นคุณอาจต้องการวาง.libไฟล์ลงในไดเรกทอรีต่าง ๆ โดยขึ้นอยู่กับว่าสอดคล้องกับไลบรารีแบบคงที่หรือแบบแบ่งใช้ อุปสรรคแต่ละอย่างนั้นจะต้องมีการแฮ็กใหม่

อาจชัดเจน แต่ทางเลือกหนึ่งที่ไม่ได้กล่าวถึงก่อนหน้านี้คือการทำให้ชนิดของไลบรารีเป็นพารามิเตอร์:

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} )

การมีไลบรารีเวอร์ชันที่แบ่งใช้และแบบคงที่ในต้นไม้ไบนารีสองต้นทำให้จัดการตัวเลือกการรวบรวมที่แตกต่างกันได้ง่ายขึ้น ฉันไม่เห็นข้อเสียเปรียบที่ร้ายแรงในการเก็บต้นไม้ที่แตกต่างกันโดยเฉพาะอย่างยิ่งถ้าการรวบรวมของคุณเป็นแบบอัตโนมัติ

โปรดทราบว่าแม้ว่าคุณต้องการรวมการรวบรวมโดยใช้OBJECTไลบรารีระดับกลาง(ด้วยคำเตือนที่กล่าวถึงข้างต้นดังนั้นคุณต้องมีเหตุผลที่น่าสนใจในการทำเช่นนั้น) คุณยังสามารถมีห้องสมุดสิ้นสุดที่วางอยู่ในสองโครงการที่แตกต่างกัน


-2

เป็นไปได้แน่นอน ดังที่ @Christopher Bruns กล่าวในคำตอบของเขาคุณต้องเพิ่มไลบรารี่สองเวอร์ชัน:

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

จากนั้นตามที่อธิบายไว้ที่นี่คุณต้องระบุว่าเป้าหมายทั้งสองควรใช้ชื่อเอาต์พุตเดียวกันและไม่เขียนทับไฟล์ของกันและกัน:

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)

วิธีนี้คุณจะได้รับทั้ง libmylib.a และ libmylib.so (บน Linux) หรือ mylib.lib และ mylib.dll (บน Windows)


10
สิ่งนี้ไม่จำเป็นเมื่อใช้เวอร์ชัน CMake ด้านบน 2.8 [0?] เนื่องจากคุณสมบัติถูกลบในปี 2009 และพฤติกรรมที่มีให้เป็นค่าเริ่มต้น สิ่งนี้อาจมีประโยชน์สำหรับผู้ที่มีอายุต่ำกว่า 2.8 แต่ถ้าคุณยังใช้ CMake <2.7 อยู่ขอให้คุณอัปเกรด github.com/Kitware/CMake/commit/…
KymikoLoco
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.