การใช้ CMake ช่วยให้เรากำหนดซอฟต์แวร์ที่เราต้องการสร้างเพียงครั้งเดียว หลังจากนั้นเราสามารถพัฒนามันโดยใช้เครื่องมือที่เราชอบบนแพลตฟอร์มใดๆ ก็ได้ CMake จึงอำนวยช่วยความสะดวกให้กับโปรแกรมเมอร์ภาษา C++ ที่ต้องการสร้างซอฟต์แวร์ที่รันบนหลายๆ แพลตฟอร์มอย่างมาก ในบทความนี้เราจะกล่าวถึงวิธีการใช้งาน CMake จัดการซอฟต์แวร์โปรเจคง่ายๆ เป็นการปูพื้นฐานก่อนจะกล่าวถึงการใช้งานที่สลับซับซ้อนขึ้นต่อไป
ปัญหาของการเขียนโปรแกรมสำหรับหลายแพลตฟอร์ม
การเขียนโปรแกรมภาษา C++ ให้คอมไพล์และรันบนหลายๆ แพลตฟอร์ม (เขียนครั้งเดียวแต่รันได้ทั้งบน Windows, Unix, Mac, ฯลฯ) นั่นเป็นเรื่องที่ทำให้โปรแกรมเมอร์ปวดเศียรเวียนเกล้าอย่างหนักมาหลายคนแล้ว เหตุก็เพราะว่า แพลตฟอร์มต่างๆ กันนั้นมี "สภาพแวดล้อม" สำหรับพัฒนาโปรแกรมภาษา C++ ที่ไม่เหมือนกันเอาเสียเลย เราอาจสามารถสรุปปัญหาที่โปรแกรมเมอร์เจอไว้เป็นข้อๆ ได้ดังต่อไปนี้- ไลบรารีภาษา C/C++ บนแพลตฟอร์มต่างๆ ไม่เหมือนกัน เช่น ใน Linux และ Mac จะมีไฟล์ unistd.h แต่ใน Windows ไม่มี หรือใน Mac มีระบบไลบรารีที่เรียกว่า bundle และ framework ซึ่งไม่ปรากฏอยู่ในแพลตฟอร์มอื่นๆ
- คอมไพเลอร์ที่ใช้เป็นคนละตัวกัน กล่าวคือ โปรแกรมเมอร์ C++ ใน Windows จะใช้ cl ของ Microsoft ส่วนโปรแกรมเมอร์บน Linux และ Mac จะใช้ gcc หรือ g++ ของ GNU Project นี่หมายความว่าภาษา C++ ใน Windows จะไม่เหมือนกับภาษา C++ ใน Unix และ Mac เป๊ะ และมีข้อแตกต่างยิบย่อยที่ทำให้ซอร์สโค้ดที่เขียนสำหรับแพลตฟอร์มหนึ่งไม่สา มารถคอมไพล์บนอีกแพลตฟอร์มหนึ่งได้
- เครื่องมือที่ใช้ในการ build โปรแกรมไม่เหมือนกัน โปรแกรมเมอร์บน Windows จะใช้ Visual Studio พวกที่ใช้ Linux ก็จะเขียน Makefile แล้วรัน make ส่วนขา Mac ก็จะใช้ Xcode
Preprocessor Macro
เราสามารถหลีกเลี่ยงสองปัญหาแรกบางส่วนได้ด้วยการเขียน preprocessor macro เพื่อให้คอมไพเลอร์เลือกใช้โค้ดส่วนที่ถูกต้อง ยกตัวอย่าง เช่น หากเราต้องการชื่อของไดเรคทอรีที่โปรแกรมทำงานอยู่ในปัจจุบัน (current working directory ย่อว่า cwd) แล้ว- ใน Linux และ Mac เราจะต้องใช้คำสั่ง getcwd ซึ่งอยู่ใน unistd.h
- แต่ใน Windows เราจะต้องใช้คำสั่ง _getcwd ซึ่งอยู่ใน direct.h
UNIQ22c83b4d695a4d2f-geshi-00000000-QINU
ส่วนเวลาเรียกใช้คำสั่ง เราสามารถเขียนโค้ดต่อไปนี้ได้
UNIQ22c83b4d695a4d2f-geshi-00000001-QINU
เครื่องมือและสภาพแวดล้อมที่แตกต่าง
อย่างไรก็ดีการเขียน preprocessor macro ก็ยังไม่สามารถแก้ปัญหาได้ทั้งหมด เนื่องจากคอมไพเลอร์ของแต่ละแพลตฟอร์มมีวิธีการเรียกใช้และการกำหนดออปชันที่แตกต่างกัน นอกจากนี้ การคอมไพล์โปรแกรมภาษา C++ เป็นกระบวนการที่ซับซ้อน ดังนั้นแพลตฟอร์มต่างๆ จึงมีเครื่องมือสำหรับกำหนดกระบวนการนี้เป็นของตัวเองถึงแม้ว่าในปัจจุบัน make จะเป็นเครื่องมือมาตรฐานสำหรับการสร้างซอฟต์แวร์ภาษา C++ ในแพลตฟอร์มที่สืบเชื้อสายมาจาก Unix ทั้งหลาย นอกจากนี้ใน Windows มี nmake ให้ใช้ด้วย อย่างไรก็ดีการใช้ make ทำให้โปรแกรมเมอร์ต้องสละความสะดวกสบายของ IDE ที่ตนถนัด (เนื่องจากผู้เขียนใช้งาน Visual C++ จนติด การต้องไปเขียน Makefile เองจึงเป็นเรื่องที่ทรมานยิ่ง)
แต่ปัญหาที่ใหญ่ที่สุดเกิดขึ้นเมื่อซอฟต์แวร์ที่เราสร้างใช้ไลบรารีภายนอกที่เราไม่ได้เขียนเอง เนื่องจากแพลตฟอร์มต่างๆ มีที่เก็บไลบรารีที่แตกต่างกัน เช่น ใน Mac จะเก็บอยู่ในไดเรคทอรี /Library ส่วนใน Unix อาจเก็บอยู่ที่ /usr หรือ /usr/local ส่วนใน Windows นั้นไม่มีที่เก็บที่แน่นอน เป็นเรื่องของโปรแกรมเมอร์ที่จะต้องรู้ที่เก็บไลบรารีเอง
ในแพลตฟอร์ม Unix มีชุดของเครื่องมือที่เรียกว่า Autotools ซึ่งสามารถตรวจว่าระบบที่จะทำการคอมไพล์ไฟล์มีไลบรารีที่ต้องการให้ใช้หรือไม่ รวมทั้งสร้างไฟล์ชื่อ config.h ซึ่งมี preprocessor macro ซึ่งช่วยให้เรา include ไฟล์และเลือกใช้คำสั่งที่ถูกต้องได้ดังที่กล่าวไว้ข้างบน แต่การใช้ Autotools บังคับให้เราต้องใช้ make ไปโดยปริยาย
ดังนั้นจึงการดีอย่างยิ่งหากเรามีเครื่องมือที่ช่วยให้เรากำหนดซอฟต์แวร์ที่เราต้องการสร้างเพียงครั้งเดียว (ขั้นนี้เรียกว่าการทำ software project configuration และเพื่อความง่ายเราจะแทน "ซอฟต์แวร์ที่จะสร้าง" ด้วยคำว่า "โปรเจค" แทน) แล้วอนุญาตให้เราสามารถคอมไพล์ซอฟต์แวร์นี้ด้วยเครื่องมืออะไรก็ได้ที่เราถนัดบนแพลตฟอร์มที่เราถนัด แต่ในขณะเดียวกันเราก็ยังสามารถพัฒนาซอฟต์แวร์เดียวกันนี้บนแพลตฟอร์มอื่นได้ด้วย
CMake
CMake เป็นเครื่องมือที่ช่วยตอบสนองความต้องการข้างต้นได้ จากมุมมองของผู้ใช้ CMake แล้วกระบวนการคอมไพล์โปรแกรมสามารถแยกออกเป็นสามขั้นตอนดังต่อไปนี้- เขียนไฟล์ชื่อ CMakeLists.txt เพื่อกำหนดโปรเจค
- รัน CMake เพื่อสร้าง build script ที่เฉพาะเจาะจงกับแพลตฟอร์มและเครื่องมือที่เราใชัพัฒนาโปรแกรม
- build โปรแกรมด้วยเครื่องมือดังกล่าว
การติดตั้ง CMake
คุณสามารถดาวน์โหลด CMake จากหน้าดาวน์โหลดของเวบไซต์อย่างเป็นทางการ โดยเลือก installer ที่เหมาะสมกับแพลตฟอร์มที่คุณใช้ผู้ใช้ Debian หรือ Ubuntu สามารถติดตั้ง CMake ผ่าน apt-get ได้ด้วยการสั่ง
> sudo apt-get cmakeส่วนผู้ใช้ Mac ที่ใช้ MacPorts สามารถติดตั้งได้โดยการสั่ง
> sudo port install cmakeหลังจากติดตั้งแล้วเราสามารถทดสอบว่า CMake ถูกติดตั้งเรียบร้อยดีหรือไม่ด้วยการสั่ง
> cmakeใน command prompt ซึ่งหลังจากสั่งแล้วคุณควรจะพบว่า CMake พิมพ์วิธีการใช้มันอย่างคร่าวๆ รวมทั้งออปชันต่างๆ ที่คุณสามารถกำหนดได้ออกมาทางหน้าจอ โดยตอนท้ายของข้อความที่ CMake พิมพ์ออกมาจะมีส่วนที่กล่าวถึง "Generators" ซึ่งบอกว่าขณะนี้ CMake สามารถสร้าง build script ที่ใช้กับเครื่องมือใดได้บ้าง
เมื่อผู้เขียนรันคำสั่ง cmake บนภายใต้ระบบปฏิบัติการ Mac OSX แล้วได้ผลลัพธ์ดังต่อไปนี้
cmake version 2.6-patch 4 Usage cmake [options] <path-to-source> cmake [options] <path-to-existing-build> Options <<< ข้อความแสดงออปชันต่างๆ ที่ผู้ใช้สามารถกำหนดได้ >>> Generators The following generators are available on this platform: Unix Makefiles = Generates standard UNIX makefiles. Xcode = Generate XCode project files. CodeBlocks - Unix Makefiles = Generates CodeBlocks project files. Eclipse CDT4 - Unix Makefiles = Generates Eclipse CDT 4.0 project files. KDevelop3 = Generates KDevelop 3 project files. KDevelop3 - Unix Makefiles = Generates KDevelop 3 project files.แต่ถ้ารันมันภายใต้ Windows XP แล้วจะได้ผลลัพธ์ดังต่อไปนี้
cmake version 2.8.1 Usage cmake [options] <path-to-source> cmake [options] <path-to-existing-build> Options <<< ข้อความแสดงออปชันต่างๆ ที่ผู้ใช้สามารถกำหนดได้ >>> Generators The following generators are available on this platform: Borland Makefiles = Generates Borland makefiles. MSYS Makefiles = Generates MSYS makefiles. MinGW Makefiles = Generates a make file for use with mingw32-make. NMake Makefiles = Generates NMake makefiles. NMake Makefiles JOM = Generates JOM makefiles. Unix Makefiles = Generates standard UNIX makefiles. Visual Studio 10 = Generates Visual Studio 10 project files. Visual Studio 10 Win64 = Generates Visual Studio 10 Win64 project files. Visual Studio 6 = Generates Visual Studio 6 project files. Visual Studio 7 = Generates Visual Studio .NET 2002 project files. Visual Studio 7 .NET 2003 = Generates Visual Studio .NET 2003 project files. Visual Studio 8 2005 = Generates Visual Studio .NET 2005 project files. Visual Studio 8 2005 Win64 = Generates Visual Studio .NET 2005 Win64 project files. Visual Studio 9 2008 = Generates Visual Studio 9 2008 project files. Visual Studio 9 2008 Win64 = Generates Visual Studio 9 2008 Win64 project files. Watcom WMake = Generates Watcom WMake makefiles. CodeBlocks - MinGW Makefiles = Generates CodeBlocks project files. CodeBlocks - NMake Makefiles = Generates CodeBlocks project files. CodeBlocks - Unix Makefiles = Generates CodeBlocks project files. Eclipse CDT4 - MinGW Makefiles = Generates Eclipse CDT 4.0 project files. Eclipse CDT4 - NMake Makefiles = Generates Eclipse CDT 4.0 project files. Eclipse CDT4 - Unix Makefiles = Generates Eclipse CDT 4.0 project files.ผลลัพธ์ทั้งสองแสดงให้เห็นว่าเราสามารถสร้าง build script สำหรับเครื่องมือที่แตกต่างกันได้หลายชนิด และ CMake ยังรู้ด้วยว่าในแพลตฟอร์มต่างๆ กันมีเครื่องมือที่แตกต่างกัน
Hello World ด้วย CMake
ในส่วนนี้เราจะทำการสร้างโปรแกรมที่ง่ายที่สุดด้วย CMake ก่อนอื่นให้คุณสร้างไดเรคทอรีชื่อ helloworld ซึ่งมีโครงสร้างดังต่อไปนี้helloworld/ CMakeLists.txt main.cppโดยที่ main.cpp มีเนื้อหาตามธรรมเนียมดังนี้
UNIQ22c83b4d695a4d2f-geshi-00000002-QINU
ไฟล์ CMakeLists.txt เป็นพิมพ์เขียวของโปรเจค ซึ่งในกรณีนี้มันมีเนื้อหาดังต่อไปนี้
PROJECT(sample) CMAKE_MINIMUM_REQUIRED(VERSION 2.6) ADD_EXECUTABLE(helloworld main.cpp)บรรทัดแรกของไฟล์บอกว่าโปรเจคของเรามีชื่อว่า sample ส่วนบรรทัดที่สองเป็นการเช็คว่า CMake ที่เราใช้นั้นใหม่กว่าเวอร์ชัน 2.6 บรรทัดที่สามเป็นบรรทัดบอกว่าเราจะทำการสร้างไฟล์ executable เพียงไฟล์เดียวชื่อ helloworld ด้วยการคอมไพล์ main.cpp
ตอนนี้เราพร้อมที่จะสร้าง build script ด้วย CMake แล้ว อันดับแรกให้ทำการเปลี่ยนไดเรคทอรีไปยังไดเรคทอรี helloworld ที่เราเพิ่งจะสร้างขึ้น
> cd helloworldแล้วจึงสั่ง
> cmake .เพื่อบอกให้ CMake สร้าง build script โดยอ่าน CMakeLists.txt จากไดเรคทอรีปัจจุบัน การสั่งแบบนี้สอดคล้องกับการใช้ CMake โดยสั่ง
cmake [options] <path-to-source>ที่เราเคยเห็นแล้วจากการสั่ง cmake เปล่าๆ ข้างบน
ผลลัพธ์
เนื่องจากเราไม่ได้กำหนดว่าจะให้ CMake ใช้ generator ตัวใด CMake จึงเลือกใช้ generator ตัวที่ถูกเลือกเป็นตัวเลือกอัตโนมัติสำหรับแพลตฟอร์มที่เรารันมันเวลาที่ผู้เขียนใช้ Mac ผู้เขียนพบว่า CMake จะสร้าง "Unix Makefiles" ดังนั้นหากเราดูไฟล์ในไดเรคทอรี helloworld หล้งรัน CMake จะมีไฟล์ดังต่อไปนี้
> ls CMakeCache.txt Makefile main.cpp CMakeFiles cmake_install.cmake CMakeLists.txtโดยไฟล์ที่เป็นผลลัพธ์ที่สำคัญที่สุดคือ Makefile ซึ่งทำให้เราสามารถสั่ง
> makeแล้วจะได้ไฟล์ executable ชื่อ helloworld ตามที่เราต้องการ
ในทางกลับกัน ถ้าผู้เขียนใช้ Windows แล้ว CMake จะใช้ generator ชื่อ "Visual Studio 9 2008" เพื่อสร้างไฟล์โปรเจคสำหรับ Visual C++ 2008 เมื่อผู้เขียนสำรวจดูไดเรคทอรี helloworld ก็พบว่ามีไฟล์ดังต่อไปนี้
C:\***\helloworld>dir Volume in drive C is ******** Volume Serial Number is ****-**** Directory of C:\***\helloworld 05/08/2010 08:00 PM <DIR> . 05/08/2010 08:00 PM <DIR> .. 05/08/2010 08:00 PM 23,520 ALL_BUILD.vcproj 05/08/2010 08:00 PM 12,587 CMakeCache.txt 05/08/2010 08:00 PM <DIR> CMakeFiles 05/08/2010 07:57 PM 91 CMakeLists.txt 05/08/2010 08:00 PM 1,473 cmake_install.cmake 05/08/2010 08:00 PM 26,038 helloworld.vcproj 05/08/2010 07:56 PM 92 main.cpp 05/08/2010 08:00 PM 3,151 sample.sln 05/08/2010 08:00 PM 20,415 ZERO_CHECK.vcproj 8 File(s) 87,367 bytes 3 Dir(s) 5,633,990,656 bytes freeซึ่งผู้เขียนสามารถใช้ Microsoft Visual Studio 2009 เปิดไฟล์ sample.sln เพื่อคอมไพล์ main.cpp ให้กลายเป็น helloworld.exe ได้
เลือก Generator เอง
เราสามารถกำหนดให้ CMake ใช้ generator ตัวอื่นๆ ที่ไม่ใช้ตัวที่มันเลือกให้อัตโนมัติได้ ด้วยการใช้ออปชัน -G "ชื่อ generator" ยกตัวอย่างเช่น หากเราต้องการใช้ Xcode ในการพัฒนาซอฟต์แวร์บนเครื่อง Mac แทนที่จะใช้ Unix Makefiles เราสามารถสั่ง> cmake -G "Xcode" .แทนการสั่ง cmake . ที่กล่าวไปในส่วนที่แล้ว ในกรณีของโปรเจค sample ข้างบน เราได้จะได้ไดเรคทอรีชื่อ sample.xcodeproj ซึ่งสามารถใช้ Xcode เปิดได้
คุณสามารถชื่อของ generator ที่ใช้ได้ในแต่ละแพลตฟอร์มได้จากส่วน "Generators" ของข้อความที่ CMake พิมพ์ออกมาเมื่อเราสั่ง cmake เฉยๆ ใน command prompt
อย่างไรก็ดี หากคุณใช้สั่ง cmake . สร้าง build script ลงในไดเรคทอรี helloworld เสร็จแล้วพยายามจะเปลี่ยน generator ที่ใช้ด้วยการสั่ง cmake -G "Xcode" . แล้ว CMake จะบ่นว่า
CMake Error: Error: generator : Xcode Does not match the generator used previously: Unix Makefiles Either remove the CMakeCache.txt file or choose a different binary directoryปัญหานี้เกิดขึ้นจากข้อจำกัดของ CMake ที่ว่า ในไดเรคทอรีหนึ่งๆ มันสามารถสร้าง build script ของเครื่องมือได้เพียงเครื่องมือเดียวเท่านั้น หากเราต้องการเปลี่ยนเครื่องมือใหม่ เราจะต้องทำให้ CMake "ลืม" build script ของเครื่องมือเดิมด้วยการลบไฟล์ CMakeCache.txt แล้วจึงสั่ง cmake -G "Xcode" . ใหม่อีกรอบ หรือไม่ก็ไปสร้าง build script ในไดเรคทอรีใหม่เลย
In-Source Build และ Out-of-Source Build
สังเกตว่าในการ build โปรเจค sample ในส่วนที่แล้วนั้น ได้เรคทอรี helloworld บรรจุดทั้งซอร์สโค้ด (มีสองไฟล์คือ main.cpp และ CMakeLists.txt) และไฟล์อื่นๆ ที่ CMake สร้างขึ้นไว้ทั้งคู่ การทำเช่นนี้เรียกว่าการทำ in-source build และมันมีข้อเสียดังต่อไปนี้1. ไดเรคทอรีที่เก็บซอร์สโค้ดสามารถเก็บ build script ของเครื่องมือได้เครื่องมือเดียวเท่านั้น จึงเกิดความไม่สะดวกอย่างยิ่งเวลาต้องการสร้าง build script สำหรับเครื่องมือหลายๆ เครื่องมือ
2. ไฟล์ของ CMake ปะปนกับไฟล์ซอร์สโค้ด ทำให้การจัดการซอร์สโค้ดทำได้ยากขึ้น โดยเฉพาะเวลาที่เราเก็บซอร์สโค้ดโดยใช้ซอฟต์แวร์ version control ซึ่งเราจะต้องคอยมาบอกระบุว่าไฟล์ไหนเป็นซอร์สโค้ด (ซึ่งต้องถูกเก็บใน version control) และไฟล์ไหนเป็นไฟล์ที่ CMake สร้างขึ้น (ซึ่งไม่ควรถูกเก็บใน version control)
ในทางกลับกัน out-of-source build คือการแยกไดเรคทอรีที่เก็บซอร์สโค้ด (CMake เรียกไดเรคทอรีนี้ว่า source directory) และไดเรคทอรีที่เก็บ build script และข้อมูลอื่นๆ (CMake เรียกไดเรคทอรีนี้ว่า binary directory) ให้เป็นคนละไดเรคทอรีกัน การทำ out-of-source build ช่วยให้เราหลีกเลี่ยงปัญหาข้างต้นทั้งสองข้อได้โดยสิ้นเชิง โดยแลกมากับการจัดระเบียบวิธีการเก็บไฟล์ใหม่ และการพิมพ์ค่ำสั่งเพิ่มอีกเล็กน้อยตอนเรียก CMake เท่านั้น
จัดระเบียบไฟล์ใหม่
เราอาจจัดระเบียบไฟล์เพื่อทำ out-of-source build โดยเอาซอร์สโค้ดทั้งหมดไปใส่ไว้ในไดเรคทอรีชื่อ src ซึ่งทำหน้าที่เป็น source directory และสร้างไดเรคทอรีว่างชื่อ build เพื่อทำหน้าที่เป็น build directory ดังนั้นสำหรับโปรเจค sample เราอาจจัดระเบียบไฟล์ใหม่ดังต่อไปนี้helloworld/ build/ src/ CMakeLists.txt main.cpp
เรียก CMake จาก build directory
หลังจากนั้น เวลาเราจะสร้าง build script ก็ให้เปลี่ยนไดเรคทอรีไปยัง build:> cd helloworld/buildแล้วจึงสั่ง cmake แล้วให้ source directory เป็น argument:
> cmake ../srcเมื่อสั่งแล้วไฟล์ build script ทุกอย่างจะถูกบรรจุอยุ่ใน build และเมื่อเราเรียก make (สมมติว่าเราเขียนโปรแกรมอยู่บนเครื่อง Mac) แล้วเราจะได้ไฟล์ helloworld อยู่ในไดเรคทอรี build ด้วย
หลังจากที่เราสั่ง cmake โดยให้ source directory เป็น argument ไปเป็นครั้งแรกแล้ว หากเราเข้าไปใน build directory อีกครั้งแล้วสั่ง cmake . อีกครั้ง CMake จะ update ไฟล์ใน build directory ใหม่โดยใช้ generator ตัวเดิม ทำให้เราไม่ต้องพิมพ์ตำแหน่งของ source directory อีกต่อไปเลย ทำให้เกิดความสะดวกขึ้นมาก
ไม่มีความคิดเห็น:
แสดงความคิดเห็น