แหล่งที่มาเดียวกันทั้งหมดต้องการเพียงแค่รุ่นคงที่และใช้ร่วมกันทั้งคู่ ง่ายที่จะทำ?
แหล่งที่มาเดียวกันทั้งหมดต้องการเพียงแค่รุ่นคงที่และใช้ร่วมกันทั้งคู่ ง่ายที่จะทำ?
คำตอบ:
ใช่มันง่ายพอสมควร เพียงใช้คำสั่ง "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)
-fPIC
) ซึ่งเพิ่มจำนวนเล็กน้อยของค่าใช้จ่ายในการรันไทม์เมื่อใช้ไลบรารีแบบคงที่เหล่านั้น ดังนั้นเพื่อประสิทธิภาพสูงสุดคำตอบนี้ยังดีที่สุด
ตั้งแต่ 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 แบบสแตติกไม่สนใจ) โปรดทราบว่ารหัสที่ไม่ขึ้นอยู่กับตำแหน่งนั้นอาจมีประสิทธิภาพน้อยกว่าดังนั้นหากคุณตั้งเป้าหมายเพื่อให้ได้ประสิทธิภาพสูงสุด นอกจากนี้ยังง่ายต่อการกระจายไฟล์ที่เชื่อมโยงแบบคงที่
target_link_libraries()
โทรตามลำดับ ที่ขึ้นอยู่กับห้องสมุดของคุณไม่สามารถใช้ "ห้องสมุดวัตถุ" เพื่อเชื่อมโยงกับ; สิ่งเหล่านี้จะต้องกำหนดเป้าหมายไลบรารีที่แชร์หรือสแตติกใหม่ (และอาจซ้ำกัน) แต่ตรงกันข้ามกับประสบการณ์ของนักวิจารณ์คนแรกมันค่อนข้างมีประโยชน์และอนุญาตให้ฉันลบเป้าหมายที่ซ้ำกันทั้งหมดและตัดCMakeLists.txt
ไฟล์ทั้งหมดของฉันเกือบครึ่ง
set_property
ทำงานเฉพาะเมื่อผมใช้และไม่ได้เมื่อใช้objlib
${objlib}
ดังนั้นบางทีคำตอบนี้สามารถแก้ไขได้?
โดยทั่วไปไม่จำเป็นต้องทำการ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
ในอีกไดเรกทอรีหนึ่ง
เป็นไปได้ที่จะบรรจุสิ่งของในลมหายใจการรวบรวมเดียวกันตามที่แนะนำในคำตอบก่อนหน้า แต่ฉันจะแนะนำกับมันเพราะในตอนท้ายมันเป็นแฮ็คที่ใช้งานได้สำหรับโครงการง่ายๆเท่านั้น ตัวอย่างเช่นคุณอาจจำเป็นต้องใช้ค่าสถานะที่แตกต่างกันสำหรับไลบรารี่รุ่นต่าง ๆ (โดยเฉพาะใน 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
ไลบรารีระดับกลาง(ด้วยคำเตือนที่กล่าวถึงข้างต้นดังนั้นคุณต้องมีเหตุผลที่น่าสนใจในการทำเช่นนั้น) คุณยังสามารถมีห้องสมุดสิ้นสุดที่วางอยู่ในสองโครงการที่แตกต่างกัน
เป็นไปได้แน่นอน ดังที่ @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)