Upload
others
View
11
Download
0
Embed Size (px)
Citation preview
貓也會的CMake
高效率開發跨平台專案
Jim Huang ( 黃敬群 )web: http://jserv.sayya.org/blog: http://blog.linux.org.tw/email: <jserv.tw @ gmail.com>
TOSSUG 心得分享 - Mar 18, 2008
It was stupid easy.KDE開發者Matt Rogers如是說
● 鄭玄作易贊及易論云《易》– 簡易
– 變易
– 不易
● make– GNU make, BSD make, Plan9 Mk– imake (x11), dmake (openoffice), qmake
(qt), nmake (Microsoft), Rake (ruby), ...– WMake (Watcom), WSCons, jam, FTjam, ant
(Apache), automake, autoconf, ...
GNU 軟體建構流程
configure.in
Makefile.am
autoconf
automake Makefile.in
configure
Developer User
./configure
GenerateMakefile
Checkfor required
libraries
Makefile
Sourcefiles
Sourcefiles [autoscan] [configure.scan] configure.ac
configure.ac
[acinclude.m4]
autoconf
[autoheader]
configure
[config.h.in]
automakeMakefile.amMakefile.in
configure
[config.h.in]
Makefile.inconfig.status
config.log
config.cache
[config.h]
Makefilemake
Starting,occasional
Developer, on development system
Installer, on target system
distribute
Executables, libraries, documents, etc.End User
面臨的困擾● 「何時會有 MS-Windows版本?」
– 連基本的編譯動作都不知如何下手
● 「 BSD上沒有這些函式庫」– 需要便利有效的環境偵測機制
● 「我討厭 Automake!」
– 不知不覺變成 Autofool● 「天呀,條件編譯好麻煩」
– <pcman>花在 automake的時間還比較多
Autotools sucks
% make lovemake: *** No rule to make target `love'. Stop.
CMake再次愛上make
cmake● 訴求:跨平台、高度可擴充、整合● 不只是 build system:針對目標端輸出 build
control/project files– UNIX : Makefile/KDevelop– MS-Visual Studio : Workspace/Projects– MacOS X : XCode
● KDE4掛保證CMakeLists.txt CMake Native Build System
Native Build ToolsExecutablesLibraries
cmake vs. automake(1)
● Automake (+ autoconf + libtool + make + sh + perl + m4)
% ./configure --prefix=/foo --enable-debug
% make
# make install
● CMake (+ make)
% cmake -DCMAKE_INSTALL_PREFIX=/foo -DCMAKE_BUILD_TYPE=debugfull .
% make
# make install
● 乍看沒什麼,但是 cmake就是快!
– kdelibs4建構時間比 kdelibs3快 40%– cmake只需 automake空間使用量的一成
cmake vs. automake(2)
使用 cmake 使用 automake
cmake: source/binary tree(1)
● Source tree– CMake輸入檔 (CMakeLists.txt)– 原始程式碼
● Binary tree / Build tree– 平台相依的建構輸出
● Makefile, GNUmakefile, .dsp, .xcodebuild– 執行檔、函式庫
● *.exe, *.so, *.dll, *.lib
CMakeLists.txt CMake Native Build System
Native Build ToolsExecutablesLibraries
CMakeLists.txtSUBDIRS(Dir1 Dir2)
Dir1/CMakeLists.txt
Dir2/CMakeLists.txt
Source Tree
Project
Common
Algorithms
BasicFilter
Numerics
IO
Project
Common
Algorithms
BasicFilter
Numerics
IO
Binary Tree
建議的方式
Out Source Build
InSourceBuild
cmake: source/binary tree(2)
CMakeLists.txt# Project namePROJECT(HELLO)
# Source code subdirectoriesSUBDIRS(src test)
# Create a library from the hello.cxx fileADD_LIBRARY(Hello hello.c)
# Location of library include filesINCLUDE_DIRECTORIES(${HELLO_SOURCE_DIR}/src)
# Library location for the linkerLINK_DIRECTORIES(${HELLO_BINARY_DIR}/src)
# helloDemo executable built from demo.cADD_EXECUTABLE(helloDemo demo.c)
# Link the executable to the Hello library.TARGET_LINK_LIBRARIES(helloDemo Hello)
CMake::Modules● automake採用m4 macro偵測環境之組態● CMake採用若干module$ dpkg L cmake | grep Modules
/usr/share/cmake2.4/Modules
/usr/share/cmake2.4/Modules/AddFileDependencies.cmake
/usr/share/cmake2.4/Modules/CheckCCompilerFlag.cmake
/usr/share/cmake2.4/Modules/CheckCSourceCompiles.cmake
...
/usr/share/cmake2.4/Modules/FindASPELL.cmake
/usr/share/cmake2.4/Modules/FindAVIFile.cmake
/usr/share/cmake2.4/Modules/FindBoost.cmake
/usr/share/cmake2.4/Modules/FindBZip2.cmake
...
CMake cacheCMakeCache.txt● 用途:表示建構環境的組態
– 只要 cache存在,即可免除重新偵測● 相當於 Autotools的 config.status
● 於組態設定階段更新
– Unix: ccmake– MS-Windows: CmakeSetup
● cmake -C <initial-cache>– Pre-load a script to populate the cache.
操作:命令列$ cd Projects
$ cmake projectFoo G”Visual Studio 7” DBAR:BOOL=1
● cmake [options] <path-to-source/path-to-existing-build>
● -D <var>:<type>=<value>– 建立 cmake cache項目
● -G <generator-name>– 指定平台相依的編譯產生器
● 透過 UI修改每項 cache內容● UNIX – ccmake, qt4-cmake (v2.6)● MS-Windows - CMakeSetup
操作:互動介面
CMakeSetupCMakeSetup ccmakeccmake
撰寫 CMakeLists.txt● 使用 CMake提供的 scripting language
– Comments – Commands– Lists– Variables– Control structures
# 本行為註解
COMMAND(參數 1 參數 2 ...)
A;B;C # 以分號區隔
IF(條件成立 )
${變數名稱 }
● 語法:
● 範例:
CMakeLists.txt::CommandsCOMMAND(參數 1 “參數 2 包含空白 ABC”
${A_LIST} “${A_STRING}”)
TARGET_LINK_LIBRARIES(myTarget
lib1 lib2)
FIND_LIBRARY(MY_LIB NAMES my1 my2
PATHS /foo /bar)
注意:不區分大小寫
● 永遠是字串形式● 在組態設定階段被決定● 透過 SET一類的 Commands所指定● 範例:
CMakeLists.txt::Variables
SET(A_LIST ${A_LIST} foo)
SET(A_STRING “${A_STRING} bar”)
● IF
● FOREACH
● MACRO
CMakeLists.txt::Control StructuresIF(CONDITION) MESSAGE(“Yes”)ELSE(CONDITION) MESSAGE(“No”)ENDIF(CONDITION)
FOREACH(k A B C) MESSAGE(“${k}: ${${k}}”)ENDFOREACH(k)
MACRO(MY_MACRO arg1 arg2) SET(${arg1} “${${arg2}}”)ENDMACRO(MY_MACRO)MY_MACRO(A B)
範例: Hello World
● PROJECT– 非必要,但建議加入
● ADD_EXECUTABLE– 自指定的 list中產生執行檔
PROJECT( helloworld )SET( hello_SRCS hello.c )ADD_EXECUTABLE( hello ${hello_SRCS} )
CMakeLists.txt
#include <stdio.h>int main(){
printf("Hello World!");return 0;
}
hello.c
建立執行檔
● ADD_LIBRARY– 自指定的 list中產生靜態函式庫
● 指定 SHARED則產生動態函式庫 (.dll / .so)
PROJECT( helloworld )SET( hello_SRCS hello.c )ADD_LIBRARY( hello SHARED ${hello_SRCS} )
CMakeLists.txt
#include <stdio.h>int hello(){
printf("Hello World!");return 0;
}
hello.c
建立函式庫
範例: fdclock
PROJECT( fdclock )FIND_PACKAGE( PkgConfig )PKG_CHECK_MODULES( cairo REQUIRED cairo )INCLUDE_DIRECTORIES( ${cairo_INCLUDE_DIRS} )LINK_DIRECTORIES( ${cairo_LIBRARY_DIRS} )
SET( fdclock_SRCS fdclock.c fdface.c fdhand.c fdlogo.c findargb.c )ADD_EXECUTABLE( fdclock ${fdclock_SRCS} )TARGET_LINK_LIBRARIES( fdclock ${cairo_LIBRARIES} )INSTALL( TARGETS fdclock DESTINATION ${CMAKE_INSTALL_PREFIX}/bin )
CMakeLists.txt
AC_PREREQ(2.57)AC_INIT(FULLPACKAGENAME,VERSION, BUGREPORTADDRESS)AC_CONFIG_SRCDIR([fdclock.c])AM_INIT_AUTOMAKE([distbzip2])AM_MAINTAINER_MODEAC_CONFIG_HEADER([config.h])AC_PROG_CCAC_HEADER_STDCPKG_CHECK_MODULES(FDCLOCK, cairo)AC_CONFIG_FILES([Makefile])AC_OUTPUT configure.ac
INCLUDES = @FDCLOCK_CFLAGS@fdclock_SOURCES = \
fdclock.c fdhand.c findargb.c \fdlogo.c fdface.c
bin_PROGRAMS = fdclockfdclock_LDADD = @FDCLOCK_LIBS@
Makefile.am
AC_PREREQ(2.57)AC_INIT(FULLPACKAGENAME,VERSION, BUGREPORTADDRESS)AC_CONFIG_SRCDIR([fdclock.c])AM_INIT_AUTOMAKE([distbzip2])AM_MAINTAINER_MODEAC_CONFIG_HEADER([config.h])AC_PROG_CCAC_HEADER_STDCPKG_CHECK_MODULES(FDCLOCK, cairo)AC_CONFIG_FILES([Makefile])AC_OUTPUT configure.ac
包袱:●configure – 176k●Makefile.in – 19k●Depcomp – 16k●missing – 11k●install.sh 9k
案例探討: KDE
$ cd Projects/build
$ cmake .. \
GKDevelop3
$ kdevelop foo.project
CTest/Dart● If it's not tested, it doesn't work!
– Unit testing的重要性– Test-Driven Development– 透過 CMake整合 Unit Test
● 途徑:確保 nightly build得以 ...– 在所有支援的平台建構
– 符合預期行為 PROJECT( ProductA )...ENABLE_TESTING()ADD_EXECUTABLE( foo )ADD_TEST(footest foo )...
CMakeLists.txt
$ make Experimental
QTestLib● Qt4的一部分
– KDE/Qt應用程式可直接運用● 特徵
– Lightweight: 6k LoC, 60 exported symbols.
– Self-contained
– Rapid testing
– Data-driven testing
– Basic GUI testing
– IDE friendly
– Thread-safety
– Type-safety
– Easily extendable
#include <QtCore/QObject>
class KLocaleTest : public QObject{ Q_OBJECT
private Q_SLOTS: void testReadTime(); ...};
klocaletest.h
#include "klocaletest.h"#include <qtest_kde.h>#include <klocale.h>
QTEST_KDEMAIN_CORE(KLocaleTest)
void KLocaleTest::readTime(){ KLocale* locale = KGlobal::locale(); bool ok = false; QCOMPARE(locale>readTime("11:22:33", &ok), QTime(11,22,33)); QVERIFY(ok);}#include "klocaletest.moc"
klocaletest.cpp
QTestLib巨集:QCOMPARE(a, b)QVERIFY(bool)QVERIFY2(bool, "some bug happened")
...SET(klocaletest_SOURCES klocaletest.cpp)KDE4_AUTOMOC(${klocaletest_SOURCES})KDE4_ADD_UNIT_TEST(klocaletest ${klocaletest_SOURCES})TARGET_LINK_LIBRARIES(klocaletest ${KDE4_KDECORE_LIBS}
${QT_QTTEST_LIBRARY})# Many tests > use macro, see kdecore/tests for exampleKDECORE_UNIT_TESTS( klocaletest klocalizedstringtest ...) CMakeLists.txt
$ ./klocaletest********* Start testing of KLocaleTest *********Config: Using QTest library 4.3.0, Qt 4.3.0PASS : KLocaleTest::initTestCase()PASS : KLocaleTest::readTime()PASS : KLocaleTest::cleanupTestCase()Totals: 3 passed, 0 failed, 0 skipped********* Finished testing of KLocaleTest *********
Verify Output
QTEST_KDEMAIN_CORE(KLocaleTest)void KlocaleTest::readTime() { QCOMPARE(...readTime("...", &ok), QTime(...)); QVERIFY(ok);}
./klocaletest********* Start testing of KLocaleTest *********Config: Using QTest library 4.3.0, Qt 4.3.0PASS : KLocaleTest::initTestCase()FAIL! : KLocaleTest::readTime() Compared values are not the same Actual (locale>readTime("11:22:33", &ok)): 11:22:33.000 Expected (QTime(11,22,34)): 11:22:34.000 Loc: [/d/kde/src/4/kdetoys/tests/klocaletest.cpp(13)]PASS : KLocaleTest::cleanupTestCase()Totals: 2 passed, 1 failed, 0 skipped********* Finished testing of KLocaleTest *********
Verify Output
$ make && make test
$ svn commit
Running tests...Start processing testsTest project /d/kde/build/4/kdelibs/kdecore/tests 1/ 36 Testing klocaletest Passed 2/ 36 Testing klocalizedstringtest Passed [...] 36/ 36 Testing kmimetypetest Passed
100% tests passed, 0 tests failed out of 36
Regression Testing
案例探討:嵌入式系統
嵌入式系統的設計挑戰跨平台開發 , 異質性環境 , 多重目標環境 ,
多樣性 , 相對封閉的環境 , ...
一般開發端與目標端是同樣的環境或相似的硬體配置
跨平台可能完全變換風貌
種種未知情況
SET(SRC_LIST main.c utils/foo.c utils/bar.c)
# add the definitions (or switches) you likeADD_DEFINITIONS(O2 fnostrictaliasing)
# use the eCos support coming with CMakeINCLUDE(UseEcos)
# add the ecos/install/include/ directory to the header search pathECOS_ADD_INCLUDE_DIRECTORIES()
# we want to crosscompile for an Intel XScale processorECOS_USE_ARM_ELF_TOOLS()
ECOS_ADD_EXECUTABLE(sampleapp ${SRC_LIST} )
sampleapp/ < the main directory | +CMakeLists.txt < specifies the source files and how to build the application from them | +main.c < one of the source files, the rest is skipped here | +ecos/ < below this directory the compilation of eCos will take place | +ecos.ecc < the eCos config file | +install/lib/ < here you will find the eCos libraries, libtarget.a, libtarget.ld | +install/include/ < here you will find the eCos header files, this directory will be part if the compiler include path
目錄結構
CMakeLists.txt
bigproject/ < the main directory | +CMakeLists.txt < list the subdirectories | +firmware/ < the root directory for all eCos applications | | | +CMakeLists.txt < include UseEcos.cmake and list the subdirectories | | | +common/ < common/ is a directory which contains sources and headers generally | | | +helloworld/ < an embedded application project directory | | | +CMakeLists.txt < list the subdirectories | | | +ProjectSources.txt < list all source files of your application in this file | | | +main.cpp < one of the source files, the other ones are skipped here | | | +ecossynth/ < here the application will be compiled for the synth target | | | | | +CMakeLists.txt < adjust the compiler settings here | | | | | +ecos/ < below this directory the eCos compilation will take place | | | +ecosiq80321/ < here comes all the stuff from above again, but now for the | | Intel IQ80321 processor | | | +ecosppc/ < and now for a power pc target | +host/ < the root directory for all host tools | +CMakeLists.txt < list the subdirectories | +sometool/ < some tool you might need
針對不同平台調整的目錄結構
參考資料● Wikipedia的詞目 : http://zh.wikipedia.org/wiki/CMake
● Why the KDE project switched to CMake — and how: http://lwn.net/Articles/188693/
● 〈 CMake and Friends〉 , Alexander Neundof● Qt Reference Manual: http://doc.trolltech.com/4.2
● KDE Tutorials: http://developer.kde.org/documentation/tutorials/writingunittests/writingunittests.html
● 〈 Using eCos with CMake〉 : http://linuxdevices.com/articles/AT6762290643.html