再戰 CMake - Autostereoscopic Mixer

Jan 15 2011

stereoscopy

上次接觸 CMake,是兩年前的事了(註1、註2),那之後,除了幾次使用一些 opensource project 需要使用到外,完全沒有任何其它的經驗,整個就是停擺。最近因為要處理一個 autostereoscopic mixer 的問題,需要把之前同事寫的程式放到不同平台上去編譯,發現到這件事有如下的幾個特性:

  • 程式碼使用 C/C++ 撰寫而成。
  • 跨平台。需要不同的 make 包:Makefile for Linux, XCode for Mac, Visual Studio solution for Windows。
  • 使用到外部的 library。FreeImage & FreeImagePlus,但是並非所有平台都有 FreeImagePlus,可能得自行 make 一個出來。

於是再度繼出 CMake 來~

首要問題是,在 cmake 的官方 modules 裏頭找不到 FindFreeImage 這個 cmake module @@ 一開始以為只是因為 MacPorts 裏包的 cmake 沒有,查了一下 Ubuntu 裏頭包的也沒,實在很傷腦筋,一點都不想自己寫個 module 出來,至少不想一開始就要寫。還好我們有好朋友 google,順利在網路上找到別人寫好的 FindFreeImage.cmake。緊接著才發現,原來我們還需要的是 FreeImagePlus,這個 C++ wrapper for FreeImage,偏偏 MacPorts 裏包的 FreeImage 不包含…一時不曉得怎麼處理。雖然訝異 MacPorts 的版本不包含 Plus,也意外沒有任何 variants 可用的,但我猜,應該是有辦法透過 port 來自行設定的吧,只是我不會 :p 想說,那就下載 FreeImage 程式碼,把裏頭的 FreeImagePlus 拿出來,然後給它包個 cmake module,產生個 libfreeimageplus.a 吧。

其次,這次有兩個程式:sim, unsim,想分別將其程式碼分隔開來,再加上上述的 freeimageplus,就有三個子目錄,想試試看 cmake 怎麼處理這種狀況。所以預計,至少這三個子目錄都要有一個 CMakeLists.txt 檔吧,如果外加最外頭的一個目錄,那就是會有四個檔了。

又,在 Ubuntu 裏頭,libfreeimageplus 是有的,我想實地感受一下 cmake 的跨平台能力,於是增加個 if-else 之類的判斷,當有 libfreeimageplus 時,就直接使用,勿需另外 build 一版出來。

另外,freeimageplus 這個目錄裏頭有不少 .ccp 檔,希望有個方式可以一次把這些 .ccp 都加進來使用,不需要一個一個指定。這裏可以使用 cmake 的 FILE(GLOB ) 來做到。

最後,我想把兩個 cmake module: FindFreeImage.cmake 與 FindFreeImagePlus.cmake 放進另一個目錄,獨立出來。於是有了如下的檔案架構:

.
|-- CMakeLists.txt
|-- cmake_modules
|   |-- FindFreeImage.cmake
|   `-- FindFreeImagePlus.cmake
|-- freeimageplus
|   |-- CMakeLists.txt
|   |-- FreeImagePlus.cpp
|   |-- FreeImagePlus.h
|   |-- fipImage.cpp
|   |-- fipMemoryIO.cpp
|   |-- fipMetadataFind.cpp
|   |-- fipMultiPage.cpp
|   |-- fipTag.cpp
|   `-- fipWinImage.cpp
|-- sim
|   |-- CMakeLists.txt
|   |-- option.h
|   `-- sim.cpp
`-- unsim
    |-- CMakeLists.txt
    |-- option.h
    `-- unsim.cpp

4 directories, 18 files

個別的檔案內容:root/CMakeLists.txt

cmake_minimum_required(VERSION 2.8)

project(stereo)

set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)

find_package(FreeImage)
find_package(FreeImagePlus)

if(NOT FREEIMAGEPLUS_FOUND)
    add_subdirectory(freeimageplus)
endif()

add_subdirectory(sim)
add_subdirectory(unsim)

個別的檔案內容:root/sim/CMakeLists.txt

CMAKE_MINIMUM_REQUIRED(VERSION 2.8)

SET(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../cmake_modules)

FIND_PACKAGE(FreeImage)
FIND_PACKAGE(FreeImagePlus)

ADD_EXECUTABLE(sim sim.cpp)

INCLUDE_DIRECTORIES(${FREEIMAGE_INCLUDE_PATH})
IF(NOT FREEIMAGEPLUS_FOUND)
    INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/freeimageplus)
    LINK_DIRECTORIES(${PROJECT_BINARY_DIR}/freeimageplus)
    TARGET_LINK_LIBRARIES(sim ${FREEIMAGE_LIBRARIES} freeimageplus)
ELSE()
    TARGET_LINK_LIBRARIES(sim ${FREEIMAGE_LIBRARIES} ${FREEIMAGEPLUS_LIBRARIES})
ENDIF()

個別的檔案內容:root/unsim/CMakeLists.txt

CMAKE_MINIMUM_REQUIRED(VERSION 2.8)

SET(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../cmake_modules)

FIND_PACKAGE(FreeImage)
FIND_PACKAGE(FreeImagePlus)

ADD_EXECUTABLE(unsim unsim.cpp)

INCLUDE_DIRECTORIES(${FREEIMAGE_INCLUDE_PATH})
IF(NOT FREEIMAGEPLUS_FOUND)
    INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/freeimageplus)
    LINK_DIRECTORIES(${PROJECT_BINARY_DIR}/freeimageplus)
    TARGET_LINK_LIBRARIES(unsim ${FREEIMAGE_LIBRARIES} freeimageplus)
ELSE()
    TARGET_LINK_LIBRARIES(unsim ${FREEIMAGE_LIBRARIES} ${FREEIMAGEPLUS_LIBRARIES})
ENDIF()

個別的檔案內容:root/freeimageplus/CMakeLists.txt

CMAKE_MINIMUM_REQUIRED(VERSION 2.8)

SET(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../cmake_modules)

FIND_PACKAGE(FreeImage)
INCLUDE_DIRECTORIES(${FREEIMAGE_INCLUDE_PATH})

FILE(GLOB CPP "*.cpp")
ADD_LIBRARY(freeimageplus ${CPP})
TARGET_LINK_LIBRARIES(freeimageplus ${FREEIMAGE_LIBRARIES})

個別的檔案內容:root/cmake_modules/FindFreeImagePlus.cmake:

#
# Try to find the FreeImage library and include path.
# Once done this will define
#
# FREEIMAGEPLUS_FOUND
# FREEIMAGEPLUS_INCLUDE_PATH
# FREEIMAGEPLUS_LIBRARY
# FREEIMAGEPLUS_LIBRARIES
# 

IF (WIN32)
        FIND_PATH( FREEIMAGEPLUS_INCLUDE_PATH FreeImagePlus.h
                ${FREEIMAGE_ROOT_DIR}/include
                ${FREEIMAGE_ROOT_DIR}
                DOC "The directory where FreeImagePlus.h resides")
        FIND_LIBRARY( FREEIMAGEPLUS_LIBRARY
                NAMES FreeImagePlus freeimageplus
                PATHS
                ${FREEIMAGE_ROOT_DIR}/lib
                ${FREEIMAGE_ROOT_DIR}
                DOC "The FreeImagePlus library")
ELSE (WIN32)
        FIND_PATH( FREEIMAGEPLUS_INCLUDE_PATH FreeImagePlus.h
                /usr/include
                /usr/local/include
                /sw/include
                /opt/local/include
                DOC "The directory where FreeImagePlus.h resides")
        FIND_LIBRARY( FREEIMAGEPLUS_LIBRARY
                NAMES FreeImagePlus freeimageplus
                PATHS
                /usr/lib64
                /usr/lib
                /usr/local/lib64
                /usr/local/lib
                /sw/lib
                /opt/local/lib
                DOC "The FreeImagePlus library")
ENDIF (WIN32)

SET(FREEIMAGEPLUS_LIBRARIES ${FREEIMAGEPLUS_LIBRARY})

IF (FREEIMAGEPLUS_INCLUDE_PATH AND FREEIMAGEPLUS_LIBRARY)
        SET( FREEIMAGEPLUS_FOUND TRUE CACHE BOOL "Set to TRUE if FreeImage is found, FALSE otherwise")
ELSE (FREEIMAGEPLUS_INCLUDE_PATH AND FREEIMAGEPLUS_LIBRARY)
        SET( FREEIMAGEPLUS_FOUND FALSE CACHE BOOL "Set to TRUE if FreeImage is found, FALSE otherwise")
ENDIF (FREEIMAGEPLUS_INCLUDE_PATH AND FREEIMAGEPLUS_LIBRARY)

MARK_AS_ADVANCED(
        FREEIMAGEPLUS_FOUND 
        FREEIMAGEPLUS_LIBRARY
        FREEIMAGEPLUS_LIBRARIES
        FREEIMAGEPLUS_INCLUDE_PATH)

最後,來看看結果吧。以下是在 Mac 下,使用 cmake 產生 Makefile 供 gcc/g++/make 使用的過程。

drake@Drakes-MacBook-Pro:~/code/stereo/build$ cmake ..
-- The C compiler identification is GNU
-- The CXX compiler identification is GNU
-- Checking whether C compiler has -isysroot
-- Checking whether C compiler has -isysroot - yes
-- Checking whether C compiler supports OSX deployment target flag
-- Checking whether C compiler supports OSX deployment target flag - yes
-- Check for working C compiler: /usr/bin/gcc
-- Check for working C compiler: /usr/bin/gcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Checking whether CXX compiler has -isysroot
-- Checking whether CXX compiler has -isysroot - yes
-- Checking whether CXX compiler supports OSX deployment target flag
-- Checking whether CXX compiler supports OSX deployment target flag - yes
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/drake/code/stereo/build
drake@Drakes-MacBook-Pro:~/code/stereo/build$ make
Scanning dependencies of target freeimageplus
[ 11%] Building CXX object freeimageplus/CMakeFiles/freeimageplus.dir/fipImage.cpp.o
[ 22%] Building CXX object freeimageplus/CMakeFiles/freeimageplus.dir/fipMemoryIO.cpp.o
[ 33%] Building CXX object freeimageplus/CMakeFiles/freeimageplus.dir/fipMetadataFind.cpp.o
[ 44%] Building CXX object freeimageplus/CMakeFiles/freeimageplus.dir/fipMultiPage.cpp.o
[ 55%] Building CXX object freeimageplus/CMakeFiles/freeimageplus.dir/fipTag.cpp.o
[ 66%] Building CXX object freeimageplus/CMakeFiles/freeimageplus.dir/fipWinImage.cpp.o
[ 77%] Building CXX object freeimageplus/CMakeFiles/freeimageplus.dir/FreeImagePlus.cpp.o
Linking CXX static library ../lib/libfreeimageplus.a
/usr/bin/ranlib: file: ../lib/libfreeimageplus.a(fipWinImage.cpp.o) has no symbols
/usr/bin/ranlib: file: ../lib/libfreeimageplus.a(FreeImagePlus.cpp.o) has no symbols
[ 77%] Built target freeimageplus
Scanning dependencies of target sim
[ 88%] Building CXX object sim/CMakeFiles/sim.dir/sim.cpp.o
Linking CXX executable ../bin/sim
[ 88%] Built target sim
Scanning dependencies of target unsim
[100%] Building CXX object unsim/CMakeFiles/unsim.dir/unsim.cpp.o
Linking CXX executable ../bin/unsim
[100%] Built target unsim

接著,我也想試試使用產生 Xcode 的 project 的過程,確定順利與否:

drake@Drakes-MacBook-Pro:~/code/stereo/build$ cmake -G Xcode ..
-- The C compiler identification is GNU
-- The CXX compiler identification is GNU
-- Checking whether C compiler has -isysroot
-- Checking whether C compiler has -isysroot - yes
-- Checking whether C compiler supports OSX deployment target flag
-- Checking whether C compiler supports OSX deployment target flag - yes
-- Check for working C compiler using: Xcode
-- Check for working C compiler using: Xcode -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - failed
-- Checking whether CXX compiler has -isysroot
-- Checking whether CXX compiler has -isysroot - yes
-- Checking whether CXX compiler supports OSX deployment target flag
-- Checking whether CXX compiler supports OSX deployment target flag - yes
-- Check for working CXX compiler using: Xcode
-- Check for working CXX compiler using: Xcode -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - failed
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/drake/code/stereo/build

open stereo.project

成功!!

References:

comments powered by Disqus