CMake มีไว้ทำอะไร?
อ้างอิงจาก Wikipedia:
CMake เป็นซอฟต์แวร์ [... ] สำหรับจัดการกระบวนการสร้างซอฟต์แวร์โดยใช้วิธีการที่ไม่ขึ้นกับคอมไพเลอร์ ได้รับการออกแบบมาเพื่อรองรับลำดับชั้นของไดเร็กทอรีและแอพพลิเคชั่นที่ขึ้นอยู่กับหลายไลบรารี ใช้ร่วมกับสภาพแวดล้อมการสร้างแบบเนทีฟเช่น make, Xcode ของ Apple และ Microsoft Visual Studio
ด้วย CMake คุณไม่จำเป็นต้องรักษาการตั้งค่าแยกต่างหากสำหรับสภาพแวดล้อมคอมไพเลอร์ / บิลด์ของคุณอีกต่อไป คุณมีการกำหนดค่าเดียวและใช้ได้กับหลายสภาพแวดล้อม
CMake สามารถสร้างโซลูชัน Microsoft Visual Studio โครงการ Eclipse หรือเขาวงกต Makefile จากไฟล์เดียวกันโดยไม่ต้องเปลี่ยนแปลงอะไรเลย
เนื่องจากมีไดเรกทอรีจำนวนมากที่มีรหัสอยู่ในนั้น CMake จะจัดการการอ้างอิงทั้งหมดสร้างคำสั่งซื้อและงานอื่น ๆ ที่โครงการของคุณต้องทำก่อนที่จะสามารถรวบรวมได้ มันไม่ได้รวบรวมอะไรเลย ในการใช้ CMake คุณต้องแจ้งให้ทราบ (โดยใช้ไฟล์คอนฟิกูเรชันที่เรียกว่า CMakeLists.txt) ว่าคุณต้องคอมไพล์ไฟล์ปฏิบัติการอะไรไลบรารีใดที่ลิงก์ไปยังไดเร็กทอรีใดในโปรเจ็กต์ของคุณและสิ่งที่อยู่ในนั้นรวมถึงรายละเอียดอื่น ๆ เช่นแฟล็ก หรือสิ่งอื่นที่คุณต้องการ (CMake มีประสิทธิภาพมาก)
หากตั้งค่าอย่างถูกต้องคุณจะใช้ CMake เพื่อสร้างไฟล์ทั้งหมดที่ "สภาพแวดล้อมการสร้างแบบเนทีฟ" ที่คุณเลือกต้องใช้ในการทำงาน โดยค่าเริ่มต้นใน Linux หมายถึง Makefiles ดังนั้นเมื่อคุณเรียกใช้ CMake มันจะสร้างไฟล์จำนวนมากสำหรับการใช้งานของมันเองบวกกับไฟล์บางMakefile
ไฟล์ สิ่งที่คุณต้องทำหลังจากนั้นคือพิมพ์ "make" ในคอนโซลจากโฟลเดอร์รูททุกครั้งที่คุณแก้ไขโค้ดของคุณเสร็จและ bam จะทำการคอมไพล์และเชื่อมโยงปฏิบัติการ
CMake ทำงานอย่างไร? มันทำอะไร?
นี่คือตัวอย่างการตั้งค่าโครงการที่ฉันจะใช้ตลอด:
simple/
CMakeLists.txt
src/
tutorial.cxx
CMakeLists.txt
lib/
TestLib.cxx
TestLib.h
CMakeLists.txt
build/
เนื้อหาของแต่ละไฟล์จะถูกแสดงและกล่าวถึงในภายหลัง
CMake ตั้งค่าโปรเจ็กต์ของคุณตามรูท CMakeLists.txt
ของโปรเจ็กต์ของคุณและทำในไดเร็กทอรีใดก็ได้ที่คุณเรียกใช้cmake
จากคอนโซล การทำเช่นนี้จากโฟลเดอร์ที่ไม่ใช่รูทของโปรเจ็กต์ของคุณจะสร้างสิ่งที่เรียกว่าบิลด์นอกแหล่งซึ่งหมายถึงไฟล์ที่สร้างระหว่างการคอมไพล์ (ไฟล์ obj, ไฟล์ lib, ไฟล์ปฏิบัติการที่คุณรู้) จะถูกวางไว้ในโฟลเดอร์ดังกล่าว แยกต่างหากจากรหัสจริง ช่วยลดความยุ่งเหยิงและเป็นที่ต้องการสำหรับเหตุผลอื่น ๆ ด้วยซึ่งฉันจะไม่พูดถึง
ฉันไม่รู้ว่าจะเกิดอะไรขึ้นถ้าคุณดำเนินการcmake
กับสิ่งอื่นที่ไม่ใช่รูCMakeLists.txt
ท
ในตัวอย่างนี้เนื่องจากฉันต้องการให้ทุกอย่างอยู่ในbuild/
โฟลเดอร์ก่อนอื่นฉันต้องไปที่นั่นจากนั้นส่ง CMake ไดเรกทอรีที่มีรูทCMakeLists.txt
อยู่
cd build
cmake ..
โดยค่าเริ่มต้นจะตั้งค่าทุกอย่างโดยใช้ Makefiles ตามที่ฉันได้กล่าวไป นี่คือลักษณะของโฟลเดอร์ build ในตอนนี้:
simple/build/
CMakeCache.txt
cmake_install.cmake
Makefile
CMakeFiles/
(...)
src/
CMakeFiles/
(...)
cmake_install.cmake
Makefile
lib/
CMakeFiles/
(...)
cmake_install.cmake
Makefile
ไฟล์เหล่านี้คืออะไร? สิ่งเดียวที่คุณต้องกังวลเกี่ยวกับเป็น Makefile และโฟลเดอร์โครงการ
สังเกตsrc/
และlib/
โฟลเดอร์ เหล่านี้ได้รับการสร้างขึ้นเพราะจุดที่จะให้พวกเขาใช้คำสั่งsimple/CMakeLists.txt
add_subdirectory(<folder>)
คำสั่งนี้จะบอก CMake ที่จะมองในโฟลเดอร์กล่าวอีกCMakeLists.txt
ไฟล์และดำเนินการที่สคริปต์เพื่อเพิ่มไดเรกทอรีย่อยด้วยวิธีนี้ทุกต้องมีCMakeLists.txt
ไฟล์ภายใน ในโครงการนี้simple/src/CMakeLists.txt
อธิบายถึงวิธีการสร้างไฟล์ปฏิบัติการจริงและsimple/lib/CMakeLists.txt
อธิบายวิธีการสร้างไลบรารี ทุกเป้าหมายที่CMakeLists.txt
อธิบายจะถูกวางโดยค่าเริ่มต้นในไดเรกทอรีย่อยภายในโครงสร้างโครงสร้าง ดังนั้นหลังจากนั้นอย่างรวดเร็ว
make
ในคอนโซลเสร็จสิ้นbuild/
มีการเพิ่มไฟล์บางไฟล์:
simple/build/
(...)
lib/
libTestLib.a
(...)
src/
Tutorial
(...)
โปรเจ็กต์ถูกสร้างขึ้นและไฟล์ปฏิบัติการพร้อมที่จะดำเนินการ คุณจะทำอย่างไรถ้าคุณต้องการให้ไฟล์เรียกทำงานอยู่ในโฟลเดอร์เฉพาะ? ตั้งค่าตัวแปร CMake เหมาะสมหรือเปลี่ยนแปลงคุณสมบัติของเป้าหมายที่เฉพาะเจาะจงได้ เพิ่มเติมเกี่ยวกับตัวแปร CMake ในภายหลัง
ฉันจะบอก CMake ว่าจะสร้างโครงการของฉันได้อย่างไร
นี่คือเนื้อหาอธิบายของแต่ละไฟล์ในไดเร็กทอรีต้นทาง:
simple/CMakeLists.txt
:
cmake_minimum_required(VERSION 2.6)
project(Tutorial)
# Add all subdirectories in this project
add_subdirectory(lib)
add_subdirectory(src)
ควรตั้งค่าเวอร์ชันขั้นต่ำที่จำเป็นเสมอตามคำเตือนที่ CMake จะพ่นเมื่อคุณไม่ทำ ใช้ CMake เวอร์ชันใดก็ได้
สามารถใช้ชื่อโปรเจ็กต์ของคุณได้ในภายหลังและบอกใบ้ถึงข้อเท็จจริงที่คุณสามารถจัดการได้มากกว่าหนึ่งโปรเจ็กต์จากไฟล์ CMake เดียวกัน ฉันจะไม่เจาะลึกถึงเรื่องนั้น
ดังที่ได้กล่าวไว้ก่อนหน้านี้ให้add_subdirectory()
เพิ่มโฟลเดอร์ในโครงการซึ่งหมายความว่า CMake คาดว่าจะมีCMakeLists.txt
ภายในซึ่งจะเรียกใช้ก่อนดำเนินการต่อ อย่างไรก็ตามหากคุณมีฟังก์ชัน CMake ที่กำหนดไว้คุณสามารถใช้งานได้จากCMakeLists.txt
s อื่น ๆในไดเรกทอรีย่อย แต่คุณต้องกำหนดก่อนที่จะใช้add_subdirectory()
ไม่เช่นนั้นจะไม่พบ CMake ฉลาดกว่าเกี่ยวกับไลบรารีดังนั้นนี่เป็นครั้งเดียวที่คุณจะพบปัญหาประเภทนี้
simple/lib/CMakeLists.txt
:
add_library(TestLib TestLib.cxx)
ในการสร้างไลบรารีของคุณเองคุณต้องตั้งชื่อแล้วแสดงรายการไฟล์ทั้งหมดที่สร้างขึ้นจาก ตรงไปตรงมา. ถ้ามันจำเป็นไฟล์อื่นจะได้รับการรวบรวมคุณจะเขียนแทนfoo.cxx
add_library(TestLib TestLib.cxx foo.cxx)
นี้ยังสามารถใช้ได้กับไฟล์ในไดเรกทอรีอื่น ๆ add_library(TestLib TestLib.cxx ${CMAKE_SOURCE_DIR}/foo.cxx)
เช่น เพิ่มเติมเกี่ยวกับตัวแปร CMAKE_SOURCE_DIR ในภายหลัง
สิ่งที่คุณสามารถทำได้อีกอย่างคือระบุว่าคุณต้องการไลบรารีที่ใช้ร่วมกัน ตัวอย่าง: add_library(TestLib SHARED TestLib.cxx)
. ไม่ต้องกลัวนี่คือจุดที่ CMake เริ่มทำให้ชีวิตของคุณง่ายขึ้น ไม่ว่าจะแชร์หรือไม่ก็ตามตอนนี้สิ่งที่คุณต้องจัดการเพื่อใช้ไลบรารีที่สร้างด้วยวิธีนี้คือชื่อที่คุณตั้งไว้ที่นี่ ตอนนี้ชื่อของไลบรารีนี้คือ TestLib และคุณสามารถอ้างอิงได้จากทุกที่ในโครงการ CMake จะพบมัน
มีวิธีที่ดีกว่าในการแสดงรายการการอ้างอิงหรือไม่? ใช่แน่นอน ตรวจสอบด้านล่างสำหรับข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้
simple/lib/TestLib.cxx
:
#include <stdio.h>
void test() {
printf("testing...\n");
}
simple/lib/TestLib.h
:
#ifndef TestLib
#define TestLib
void test();
#endif
simple/src/CMakeLists.txt
:
# Name the executable and all resources it depends on directly
add_executable(Tutorial tutorial.cxx)
# Link to needed libraries
target_link_libraries(Tutorial TestLib)
# Tell CMake where to look for the .h files
target_include_directories(Tutorial PUBLIC ${CMAKE_SOURCE_DIR}/lib)
คำสั่งadd_executable()
ทำงานเหมือนกับadd_library()
ยกเว้นแน่นอนว่ามันจะสร้างไฟล์ปฏิบัติการแทน ตอนนี้ไฟล์ปฏิบัติการนี้สามารถอ้างอิงเป็นเป้าหมายสำหรับสิ่งต่างๆเช่นtarget_link_libraries()
. เนื่องจาก tutorial.cxx ใช้รหัสที่พบในไลบรารี TestLib คุณจึงชี้ให้ CMake ดังที่แสดง
ในทำนองเดียวกันไฟล์. h ใด ๆ # รวมโดยแหล่งที่มาใด ๆadd_executable()
ที่ไม่ได้อยู่ในไดเร็กทอรีเดียวกันกับแหล่งที่มาจะต้องถูกเพิ่มเข้าไป หากไม่ใช่target_include_directories()
คำสั่งlib/TestLib.h
จะไม่พบเมื่อคอมไพล์บทช่วยสอนดังนั้นทั้งlib/
โฟลเดอร์จึงถูกเพิ่มไปยังไดเร็กทอรี include เพื่อค้นหา #includes นอกจากนี้คุณอาจเห็นคำสั่งinclude_directories()
ที่ทำงานในลักษณะคล้ายกันยกเว้นว่าคุณไม่ต้องการให้คุณระบุเป้าหมายเนื่องจากจะตั้งค่าทันทีทั่วโลกสำหรับไฟล์ปฏิบัติการทั้งหมด อีกครั้งฉันจะอธิบาย CMAKE_SOURCE_DIR ในภายหลัง
simple/src/tutorial.cxx
:
#include <stdio.h>
#include "TestLib.h"
int main (int argc, char *argv[])
{
test();
fprintf(stdout, "Main\n");
return 0;
}
สังเกตว่าไฟล์ "TestLib.h" รวมอยู่อย่างไร ไม่จำเป็นต้องรวมเส้นทางแบบเต็ม: CMake target_include_directories()
ดูแลทุกสิ่งที่อยู่เบื้องหลังต้องขอบคุณ
เทคนิคการพูดในแหล่งต้นไม้ง่ายๆเช่นนี้คุณสามารถทำได้โดยไม่ต้องCMakeLists.txt
ภายใต้lib/
และsrc/
และเพียงแค่เพิ่มสิ่งที่ต้องการที่จะadd_executable(Tutorial src/tutorial.cxx)
simple/CMakeLists.txt
ขึ้นอยู่กับคุณและความต้องการของโครงการของคุณ
ฉันควรรู้อะไรอีกบ้างเพื่อใช้ CMake อย่างถูกต้อง
(หัวข้อ AKA ที่เกี่ยวข้องกับความเข้าใจของคุณ)
การค้นหาและใช้แพ็คเกจ : คำตอบสำหรับคำถามนี้อธิบายได้ดีกว่าที่ฉันเคยทำได้
การประกาศตัวแปรและฟังก์ชั่นการใช้โฟลว์การควบคุม ฯลฯ : ดูบทช่วยสอนนี้ซึ่งอธิบายถึงพื้นฐานของสิ่งที่ CMake นำเสนอตลอดจนเป็นการแนะนำที่ดีโดยทั่วไป
ตัวแปร CMake : มีมากมายดังนั้นสิ่งที่ตามมาคือหลักสูตรความผิดพลาดที่จะพาคุณไปถูกทาง CMake wiki เป็นสถานที่ที่ดีในการรับข้อมูลเชิงลึกเพิ่มเติมเกี่ยวกับตัวแปรและสิ่งอื่น ๆ เช่นกัน
คุณอาจต้องการแก้ไขตัวแปรบางตัวโดยไม่ต้องสร้างโครงสร้างสร้างใหม่ ใช้ ccmake สำหรับสิ่งนี้ (มันแก้ไขCMakeCache.txt
ไฟล์) อย่าลืมc
กำหนดค่าเมื่อเสร็จสิ้นกับการเปลี่ยนแปลงแล้วจึงg
จัดทำ makefiles ด้วยการกำหนดค่าที่อัปเดต
อ่านบทแนะนำที่อ้างถึงก่อนหน้านี้เพื่อเรียนรู้เกี่ยวกับการใช้ตัวแปร แต่เรื่องยาวสั้น:
set(<variable name> value)
เพื่อเปลี่ยนหรือสร้างตัวแปร
${<variable name>}
ที่จะใช้มัน
CMAKE_SOURCE_DIR
: ไดเร็กทอรีรากของซอร์ส ในตัวอย่างก่อนหน้านี้จะเท่ากับ/simple
CMAKE_BINARY_DIR
: ไดเร็กทอรีรากของบิลด์ ในตัวอย่างก่อนหน้านี้จะเท่ากับsimple/build/
แต่ถ้าคุณวิ่งcmake simple/
จากโฟลเดอร์เช่นfoo/bar/etc/
นั้นการอ้างอิงถึงในการที่สร้างต้นไม้ก็จะกลายเป็นCMAKE_BINARY_DIR
/foo/bar/etc
CMAKE_CURRENT_SOURCE_DIR
: ไดเรกทอรีที่ปัจจุบันCMakeLists.txt
อยู่ในวิธีการนี้มันเปลี่ยนแปลงตลอด:. พิมพ์นี้จากsimple/CMakeLists.txt
อัตราผลตอบแทน/simple
และการพิมพ์ได้จากอัตราผลตอบแทนsimple/src/CMakeLists.txt
/simple/src
CMAKE_CURRENT_BINARY_DIR
: คุณเข้าใจแล้ว เส้นทางนี้ไม่เพียงขึ้นอยู่กับโฟลเดอร์ที่สร้างอยู่ แต่ยังขึ้นอยู่กับตำแหน่งCMakeLists.txt
ของสคริปต์ปัจจุบันด้วย
เหตุใดสิ่งเหล่านี้จึงสำคัญ ไฟล์ต้นฉบับจะไม่อยู่ในโครงสร้างการสร้าง ถ้าคุณพยายามบางอย่างเช่นtarget_include_directories(Tutorial PUBLIC ../lib)
ในตัวอย่างก่อนหน้านี้เส้นทางที่จะเป็นเมื่อเทียบกับการสร้างต้นไม้ที่จะบอกว่ามันจะเป็นเหมือนการเขียนซึ่งจะดูภายใน${CMAKE_BINARY_DIR}/lib
simple/build/lib/
ไม่มีไฟล์. h อยู่ในนั้น libTestLib.a
ที่มากที่สุดที่คุณจะได้พบกับ คุณต้องการ${CMAKE_SOURCE_DIR}/lib
แทน
CMAKE_CXX_FLAGS
: แฟล็กเพื่อส่งต่อไปยังคอมไพเลอร์ในกรณีนี้คือคอมไพเลอร์ C ++ นอกจากนี้ที่น่าสังเกตคือCMAKE_CXX_FLAGS_DEBUG
จะใช้แทนหากCMAKE_BUILD_TYPE
ตั้งค่าเป็นดีบัก มีมากกว่านี้; ตรวจสอบวิกิ CMake
CMAKE_RUNTIME_OUTPUT_DIRECTORY
: บอก CMake ว่าจะวางไฟล์ปฏิบัติการทั้งหมดเมื่อสร้าง นี่เป็นการตั้งค่าส่วนกลาง ตัวอย่างเช่นคุณสามารถตั้งค่าbin/
และวางทุกอย่างไว้ที่นั่นอย่างเรียบร้อย EXECUTABLE_OUTPUT_PATH
คล้ายกัน แต่เลิกใช้แล้วในกรณีที่คุณสะดุด
CMAKE_LIBRARY_OUTPUT_DIRECTORY
: ในทำนองเดียวกันการตั้งค่าส่วนกลางเพื่อบอก CMake ว่าจะวางไฟล์ไลบรารีทั้งหมดไว้ที่ใด
คุณสมบัติเป้าหมาย : คุณสามารถตั้งค่าคุณสมบัติที่ส่งผลต่อเป้าหมายเดียวไม่ว่าจะเป็นไฟล์ปฏิบัติการหรือไลบรารี (หรือที่เก็บถาวร ... คุณจะได้รับแนวคิด) นี่คือตัวอย่างที่ดีในการใช้งาน (ด้วยset_target_properties()
.
มีวิธีง่ายๆในการเพิ่มแหล่งที่มาให้กับเป้าหมายโดยอัตโนมัติหรือไม่? ใช้ GLOBเพื่อแสดงรายการทุกอย่างในไดเร็กทอรีที่กำหนดภายใต้ตัวแปรเดียวกัน ตัวอย่างไวยากรณ์คือFILE(GLOB <variable name> <directory>/*.cxx)
.
คุณสามารถระบุประเภทการสร้างต่างๆได้หรือไม่? ใช่แม้ว่าฉันจะไม่แน่ใจเกี่ยวกับวิธีการทำงานหรือข้อ จำกัด ของสิ่งนี้ อาจต้องใช้ if / then'ning บ้าง แต่ CMake ให้การสนับสนุนขั้นพื้นฐานโดยไม่ต้องกำหนดค่าอะไรเช่นค่าเริ่มต้นCMAKE_CXX_FLAGS_DEBUG
สำหรับตัวอย่างเช่น คุณสามารถตั้งค่าทั้งสร้างประเภทของคุณจากภายในCMakeLists.txt
ไฟล์ผ่านset(CMAKE_BUILD_TYPE <type>)
หรือโทร CMake cmake -DCMAKE_BUILD_TYPE=Debug
จากคอนโซลที่มีธงที่เหมาะสมตัวอย่างเช่น
ตัวอย่างที่ดีของโครงการที่ใช้ CMake? Wikipedia มีรายชื่อโครงการโอเพ่นซอร์สที่ใช้ CMake หากคุณต้องการตรวจสอบสิ่งนั้น แบบฝึกหัดออนไลน์ไม่ได้เป็นเพียงสิ่งที่ทำให้ฉันผิดหวังในเรื่องนี้อย่างไรก็ตามคำถาม Stack Overflowนี้มีการตั้งค่า CMake ที่ค่อนข้างเจ๋งและเข้าใจง่าย มันคุ้มค่าที่จะดู
การใช้ตัวแปรจาก CMake ในโค้ดของคุณ : นี่เป็นตัวอย่างที่รวดเร็วและสกปรก (ดัดแปลงจากบทช่วยสอนอื่น ๆ ):
simple/CMakeLists.txt
:
project (Tutorial)
# Setting variables
set (Tutorial_VERSION_MAJOR 1)
set (Tutorial_VERSION_MINOR 1)
# Configure_file(<input> <output>)
# Copies a file <input> to file <output> and substitutes variable values referenced in the file content.
# So you can pass some CMake variables to the source code (in this case version numbers)
configure_file (
"${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
"${PROJECT_SOURCE_DIR}/src/TutorialConfig.h"
)
simple/TutorialConfig.h.in
:
// Configured options and settings
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
ไฟล์ผลลัพธ์ที่สร้างโดย CMake simple/src/TutorialConfig.h
:
// Configured options and settings
#define Tutorial_VERSION_MAJOR 1
#define Tutorial_VERSION_MINOR 1
ด้วยการใช้สิ่งเหล่านี้อย่างชาญฉลาดคุณสามารถทำสิ่งดีๆเช่นปิดห้องสมุดเป็นต้น ฉันขอแนะนำให้ดูบทช่วยสอนดังกล่าวเนื่องจากมีสิ่งที่ก้าวหน้ากว่าเล็กน้อยซึ่งจะเป็นประโยชน์อย่างมากกับโครงการขนาดใหญ่ไม่ช้าก็เร็ว
สำหรับสิ่งอื่น Stack Overflow เต็มไปด้วยคำถามเฉพาะและคำตอบที่กระชับซึ่งเหมาะสำหรับทุกคนยกเว้นผู้ที่ไม่ได้ฝึกหัด