2011年3月30日

[CMake] 自動新增 library 的相依 (Work-Around)

CMake 的好處之一就是對彈性. 除了專案第一個被讀取的 CMakeLists.txt 比較冗長外, 新增子目錄很容易就可以繼承其母目錄的各個屬性. 實務上, 也許會讓每個子目錄自成一個 library, 最後再全部連結. 好處是每個模組獨立, 方便單獨驗證; 模組新增或移除容易; memory-map 容易根據模組去調整等. 缺點是最後連結時, 要能去把這些 library 找出來; 而且 library 間可能會有相互的相依性問題.




如果要將 library 全部放在某個目錄, 可以寫成下面的樣子. 如果是自行寫 link-script, 就很容易找到. 可是這樣還是沒解決掉 link 前去判讀 dependent.

SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/myLibraries)

如果最後 link 的檔案是最後被執行, 且其為獨立的 CMakeLists.txt 則下面的方法算是一個拙劣但是可行的方式.

ADD_CUSTOM_COMMAND(OUTPUT ${someFile}
                   DEPENDS ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/*
                   COMMAND touch ${someFile}
                   COMMENT "Touch someFile for re-link libraries."
                  )
ADD_CUSTOM_TARGET(${myTarget} ALL
                  DEPENDS ${someFile}
                  COMMAND echo some-commands.
                  COMMENT "re-Link"
                 )

前面提到, CMake 的屬性具繼承關係, 但是卻不具回溯性. 換言之, 子目錄裡宣告的變數無法讓母目錄繼續使用. 不過卻可透過宣告 PARENT_SCOPE 達到讓母目錄使用的目的. 如下面的例子. 但是缺點是, 中繼的目錄就算不使用該變數, 還是得將該變數再宣告一次 PARENT_SCOPE, 好讓上一層的母目錄可以得到該變數的變化, 如新增一個 library. 否則變數的有效性就會被中繼目錄給破壞.

SET(subVar "subVar myValue" PARENT_SCOPE)

使用 Global 變數則是另一個變通的方法, 透過 MACRO 來新增一個 library. 如下面的例子. 缺點是每新增一個 library 必須記得呼叫該 macro.


Project/CMakeLists.txt
...
SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/myLibraries)
LINK_DIRECTORIES(${CMAKE_ARCHIVE_OUTPUT_DIRECTORY})

MACRO(AddMyLib arg)
    SET_PROPERTY(GLOBAL APPEND PROPERTY myLibs "${arg}")
ENDMACRO(AddMyLib)

ADD_SUBDIRECTORY(myLibA)
ADD_SUBDIRECTORY(myLibB)
ADD_SUBDIRECTORY(myExe)
...

Project/myLibA/CMakeLists.txt
...
ADD_LIBRARY(myLibA ${mySRC_LIST})
AddMyLib(myLibA)
...

Project/myLibB/CMakeLists.txt
...
ADD_LIBRARY(myLibB ${mySRC_LIST})
AddMyLib(myLibB)
...

Project/myExe/CMakeLists.txt
...
ADD_EXECUTABLE(myExe ${mySRC_LIST})
GET_PROPERTY(myLibsList GLOBAL PROPERTY myLibs)
# For cross-reference between libraries. TODO: Workaround solution....
FOREACH(myll ${myLibsList})
    TARGET_LINK_LIBRARIES(myExe -l${myll})
ENDFOREACH()
TARGET_LINK_LIBRARIES(myExe ${myLibsList})
...

沒有留言: