Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

I'm trying to write a portable CMake script for a simple Qt application. The target platforms are Win and Mac OSX. But as you can see, it's quite a monster already.

Are there any CMake profs? Can you see any essentially wrong approaches used in this script? I bet there are a few.

CMAKE_MINIMUM_REQUIRED( VERSION 2.8.6 )

##################################################################################################################################

MACRO( CHOOSE_QT path )
    FILE( GLOB QTROOTS "${path}/bin" )
    FIND_PROGRAM( QT_QMAKE_EXECUTABLE NAMES qmake qmake4 qmake-qt4 qmake-mac PATHS ${QTROOTS} )
ENDMACRO( CHOOSE_QT path )

MACRO( ADD_FILES_TO_FILTER rootFilterName rootFilterPath files )
    FOREACH( curFile ${files} )
        FILE( RELATIVE_PATH curFilter "${CMAKE_CURRENT_SOURCE_DIR}/${rootFilterPath}" "${CMAKE_CURRENT_SOURCE_DIR}/${curFile}" )
        FILE( RELATIVE_PATH test "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/${curFile}" )
        GET_FILENAME_COMPONENT( curFilter ${curFilter} PATH )
        SET( curFilter "${rootFilterName}/${curFilter}" )
        STRING( REPLACE "/" "\\\\" curFilter ${curFilter} )
        SOURCE_GROUP( ${curFilter} FILES ${curFile} )
    ENDFOREACH( curFile )
ENDMACRO( ADD_FILES_TO_FILTER rootFilterName rootFilterPath files )

MACRO( TO_RELATIVE_PATHS filePaths )
    SET( resPaths "" )
    FOREACH( curPath ${${filePaths}} )
        FILE( RELATIVE_PATH relPath ${CMAKE_CURRENT_SOURCE_DIR} ${curPath} )
        SET( resPaths ${resPaths} ${relPath} )
    ENDFOREACH( curPath )
    SET( ${filePaths} ${resPaths} )
ENDMACRO( TO_RELATIVE_PATHS filePaths )

MACRO( COPY_TO_BUNDLE resourcePath bundlePath )
    LIST( APPEND BUNDLE_COPY_RESOURCES ${resourcePath} )
    SET_SOURCE_FILES_PROPERTIES( ${resourcePath} PROPERTIES MACOSX_PACKAGE_LOCATION ${bundlePath} )
ENDMACRO( COPY_TO_BUNDLE )

MACRO( ADD_FRAMEWORK fwname fwpath appname )
    TARGET_LINK_LIBRARIES( ${appname} ${fwpath}/${fwname} )
    MESSAGE( STATUS "Framework ${fwname} found at ${fwpath}" )
ENDMACRO()

MACRO( ADD_SYSTEM_FRAMEWORK fwname appname )
    FIND_LIBRARY( FRAMEWORK_${fwname} NAMES ${fwname} PATHS ${CMAKE_OSX_SYSROOT}/System/Library PATH_SUFFIXES Frameworks NO_DEFAULT_PATH )
    if( ${FRAMEWORK_${fwname}} STREQUAL FRAMEWORK_${fwname}-NOTFOUND )
        MESSAGE( ERROR ": Framework ${fwname} not found" )
    else()
        ADD_FRAMEWORK( ${fwname} ${FRAMEWORK_${fwname}} ${appname} )
    endif()
ENDMACRO( ADD_SYSTEM_FRAMEWORK )

##################################################################################################################################

# Define project settings
PROJECT( TrackerSoftware )
SET( APP_NAME "TRACKer" )

# Find Qt library
if( WIN32 )
    CHOOSE_QT( "${CMAKE_CURRENT_SOURCE_DIR}/cxx/thirdparty/Qt" )
endif( WIN32 )
SET( CMAKE_AUTOMOC TRUE )
FIND_PACKAGE( Qt4 REQUIRED )

# Find Boost library
if( WIN32 )
    SET( BOOST_ROOT "cxx/thirdparty/boost" )
elseif( APPLE )
    SET( BOOST_ROOT "osx/FRP/vendors/libraries/include/boost" )
endif( WIN32 )
FIND_PACKAGE( Boost REQUIRED )

# Collect all required files for build
FILE( GLOB_RECURSE headers RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "cxx/src/RTCS/include/*.h" )
FILE( GLOB_RECURSE sources RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "cxx/src/RTCS/src/*.cpp" )
FILE( GLOB_RECURSE resources RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "cxx/src/*.qrc" )
FILE( GLOB_RECURSE win_resources RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "cxx/src/*.rc" )
FILE( GLOB_RECURSE forms RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "cxx/src/*.ui" )
SET( thirdparty_sources "cxx/thirdparty/SimpleCrypt/simplecrypt.cpp" )
if( APPLE )
    FILE( GLOB_RECURSE mac_sources RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "cxx/src/RTCS/src/*.m" )
    LIST( APPEND sources ${mac_sources} )
endif( APPLE )

# Preprocess forms
FILE( RELATIVE_PATH buildDir ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} )
QT4_WRAP_UI( form_headers ${forms} )
TO_RELATIVE_PATHS( form_headers )
ADD_FILES_TO_FILTER( "Forms" "${buildDir}/src/RTCS/include" "${form_headers}" )

# Preprocess resources
QT4_ADD_RESOURCES( resources_rcc ${resources} )
TO_RELATIVE_PATHS( resources_rcc )
ADD_FILES_TO_FILTER( "Resources" "${buildDir}" "${resources_rcc}" )

# Mark all sources as ObjectiveC++
if( APPLE )
    foreach( curSource ${sources} )
        SET_SOURCE_FILES_PROPERTIES( ${curSource} PROPERTIES COMPILE_FLAGS "-x objective-c++" )
    endforeach( curSource )
endif( APPLE )

# Set all link libraries directories - it should be specified Before any targets creation
if( WIN32 )
    LINK_DIRECTORIES( "${CMAKE_CURRENT_SOURCE_DIR}/cxx/thirdparty/boost/lib" )
    LINK_DIRECTORIES( "${CMAKE_CURRENT_SOURCE_DIR}/cxx/thirdparty/libtwitcurl/lib/x86" )
    LINK_DIRECTORIES( "${CMAKE_CURRENT_SOURCE_DIR}/cxx/thirdparty/id3lib/lib/x86" )
    LINK_DIRECTORIES( "${CMAKE_CURRENT_SOURCE_DIR}/cxx/thirdparty/curl/curl/lib/lib/x86" )
    LINK_DIRECTORIES( "${CMAKE_CURRENT_SOURCE_DIR}/cxx/thirdparty/log4qt/lib/x86" )
    LINK_DIRECTORIES( "${CMAKE_CURRENT_SOURCE_DIR}/cxx/thirdparty/WinSparkle/lib/x86/$(ConfigurationName)" )
    LINK_DIRECTORIES( "${CMAKE_CURRENT_SOURCE_DIR}/cxx/thirdparty/fervor-auto/lib/$(Configuration)" )
    LINK_DIRECTORIES( "${CMAKE_CURRENT_SOURCE_DIR}/cxx/thirdparty/quazip/lib/$(Configuration)" )
elseif( APPLE )
    LINK_DIRECTORIES( "${CMAKE_CURRENT_SOURCE_DIR}/osx/FRP/vendors/libraries/lib" )
endif( WIN32 )

# Set up Bundle settings for the Mac OSX
if( APPLE )
    SET( MACOSX_BUNDLE true )
    SET( MACOSX_BUNDLE_SHORT_VERSION_STRING 0.7-beta2 )
    SET( MACOSX_BUNDLE_VERSION 0.7-beta2 )
    SET( MACOSX_BUNDLE_LONG_VERSION_STRING Version 0.7-beta2 )
    #SET( CMAKE_OSX_ARCHITECTURES ppc;i386 ) #Comment out if not universal binary

    # Add a bundle icon
    SET( MACOSX_BUNDLE_ICON_FILE multimonIcon.icns )
    COPY_TO_BUNDLE( "osx/FRP/${MACOSX_BUNDLE_ICON_FILE}" Resources )

    # Copy all private frameworks into the bundle
    #COPY_TO_BUNDLE( "osx/FRP/vendors/libraries/lib/Sparkle.framework" Frameworks )
    #foreach( curFramework ${QT_LIBRARIES} )
        #COPY_TO_BUNDLE( "${curFramework}" Frameworks )
    #endforeach( curFramework )

    # Fixup bundle, copy dynamic libraries into app bundle
    SET( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}/out )
    MESSAGE( STATUS "!!! BUNDLE PATH: ${EXECUTABLE_OUTPUT_PATH}/${APP_NAME}.app" )
    SET( APPS "\${CMAKE_INSTALL_PREFIX}/${APP_NAME}.app" ) # paths to executables
    SET( DIRS "${CMAKE_SOURCE_DIR}/osx/FRP/vendors/libraries/lib" ) # directories to search for prerequisites
    INSTALL( CODE "
        include(BundleUtilities)
        fixup_bundle(\"${APPS}\" \"\" \"${DIRS}\")
    ")
endif( APPLE )

# Create executable
if( WIN32 )
    ADD_EXECUTABLE( Tracker WIN32 ${headers} ${sources} ${form_headers} ${resources_rcc} ${thirdparty_sources} ${win_resources} )
    SET_TARGET_PROPERTIES( Tracker PROPERTIES OUTPUT_NAME "tracker" )
    SET_TARGET_PROPERTIES( Tracker PROPERTIES COMPILE_FLAGS "/Zc:wchar_t-" )
elseif( APPLE )
    ADD_EXECUTABLE( Tracker MACOSX_BUNDLE ${headers} ${sources} ${form_headers} ${resources_rcc} ${thirdparty_sources} ${BUNDLE_COPY_RESOURCES} )
    SET_TARGET_PROPERTIES( Tracker PROPERTIES OUTPUT_NAME ${APP_NAME} )
    SET( CMAKE_XCODE_ATTRIBUTE_GCC_VERSION "com.apple.compilers.llvm.clang.1_0" )
endif( WIN32 )

# Set filters for project according to its namespaces
ADD_FILES_TO_FILTER( "Headers" "cxx/src/RTCS/include" "${headers}" )
ADD_FILES_TO_FILTER( "Sources" "cxx/src/RTCS/src" "${sources}" )
ADD_FILES_TO_FILTER( "Resources" "cxx/src/RTCS/rc" "${win_resources}" )
ADD_FILES_TO_FILTER( "Thirdparty" "cxx/thirdparty" "${thirdparty_sources}" )

# Set additional include directories
INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_BINARY_DIR} )
INCLUDE_DIRECTORIES( "cxx/src" )
INCLUDE_DIRECTORIES( "cxx/src/RTCS/include" )
INCLUDE_DIRECTORIES( "cxx/thirdparty" )

# Configure Qt
SET( QT_USE_QTNETWORK TRUE )
SET( QT_USE_QTSQL TRUE )
SET( QT_USE_QTSCRIPT TRUE )
SET( QT_USE_QTXML TRUE )
SET( QT_USE_QTWEBKIT TRUE )
INCLUDE( ${QT_USE_FILE} )
ADD_DEFINITIONS( ${QT_DEFINITIONS} )
TARGET_LINK_LIBRARIES( Tracker ${QT_LIBRARIES} )
TARGET_LINK_LIBRARIES( Tracker ${QT_QTMAIN_LIBRARY} )

# Add boost support
INCLUDE_DIRECTORIES( ${Boost_INCLUDE_DIR} )
TARGET_LINK_LIBRARIES( Tracker ${Boost_LIBRARIES} )

# Add other libs include dirs
INCLUDE_DIRECTORIES( "cxx/thirdparty/SimpleCrypt" )
INCLUDE_DIRECTORIES( "cxx/thirdparty/id3lib/include" )
INCLUDE_DIRECTORIES( "cxx/thirdparty/libtwitcurl" )
INCLUDE_DIRECTORIES( "cxx/thirdparty/curl/curl/include" )
INCLUDE_DIRECTORIES( "cxx/thirdparty/log4qt/include" )
INCLUDE_DIRECTORIES( "cxx/thirdparty/fervor-auto" )

# Add other libs to link
if( WIN32 )
    TARGET_LINK_LIBRARIES( Tracker debug "twitcurlD.lib" )
    TARGET_LINK_LIBRARIES( Tracker debug "id3libD.lib" )
    TARGET_LINK_LIBRARIES( Tracker debug "libcurlD.lib" )
    TARGET_LINK_LIBRARIES( Tracker debug "log4qtD.lib" )

    TARGET_LINK_LIBRARIES( Tracker optimized "twitcurl.lib" )
    TARGET_LINK_LIBRARIES( Tracker optimized "id3lib.lib" )
    TARGET_LINK_LIBRARIES( Tracker optimized "libcurl.lib" )
    TARGET_LINK_LIBRARIES( Tracker optimized "log4qt.lib" )

    TARGET_LINK_LIBRARIES( Tracker "fervor.lib" )
    TARGET_LINK_LIBRARIES( Tracker "quazip.lib" )

    TARGET_LINK_LIBRARIES( Tracker "Wininet.lib" )
    TARGET_LINK_LIBRARIES( Tracker "ws2_32.lib" )
    TARGET_LINK_LIBRARIES( Tracker "winmm.lib" )
    TARGET_LINK_LIBRARIES( Tracker "wldap32.lib" )
    TARGET_LINK_LIBRARIES( Tracker "Shell32.lib" )
    TARGET_LINK_LIBRARIES( Tracker "Version.lib" )
elseif( APPLE )
    TARGET_LINK_LIBRARIES( Tracker "libiconv.a" )
    TARGET_LINK_LIBRARIES( Tracker "libid3.a" )
    TARGET_LINK_LIBRARIES( Tracker "liblibtwitcurl.a" )
    TARGET_LINK_LIBRARIES( Tracker "libLog4Qt.a" )
    TARGET_LINK_LIBRARIES( Tracker "libz.a" )
    TARGET_LINK_LIBRARIES( Tracker "libboost_date_time.a" )
    TARGET_LINK_LIBRARIES( Tracker "libboost_iostreams.a" )
    TARGET_LINK_LIBRARIES( Tracker "libboost_serialization.a" )
    TARGET_LINK_LIBRARIES( Tracker "libboost_thread-mt.a" )
    TARGET_LINK_LIBRARIES( Tracker "curl" )

    # Add frameworks
    ADD_FRAMEWORK( "Sparkle.framework" "${CMAKE_CURRENT_SOURCE_DIR}/osx/FRP/vendors/libraries/lib" Tracker )

    # Add system frameworks
    ADD_SYSTEM_FRAMEWORK( Foundation Tracker )
    ADD_SYSTEM_FRAMEWORK( CoreFoundation Tracker )
    ADD_SYSTEM_FRAMEWORK( AppKit Tracker )
endif( WIN32 )

# Add defines
ADD_DEFINITIONS( -DQUAZIP_STATIC )
ADD_DEFINITIONS( -DBUILDING_LIBCURL )
ADD_DEFINITIONS( -DID3LIB_LINKOPTION=1 )
ADD_DEFINITIONS( -DUNICODE -D_UNICODE ) #enable unicode support
share|improve this question
    
Would [Win x64] fall under the Win32 branch? –  Mat's Mug Nov 19 '13 at 1:24
    
@retailcoder as i remember, yes, it would and it does. those are selectors among platforms like windows, linux and mac. –  Occulta Nov 19 '13 at 9:54
    
Isn't there a tool called qmake that people normally use? –  ChrisW Mar 18 at 15:06
    
I've worked with a CMake file that invoked FIND_PACKAGE (Qt4 4.8.0 COMPONENTS QtCore QtNetwork REQUIRED) and INCLUDE(${QT_USE_FILE}) early in the script, invoked QT4_WRAP_CPP once, and included ${QT_LIBRARIES} as one of the input arguments of TARGET_LINK_LIBRARIES. Other than that, it seems not to need to mention Qt, in particular there is nothing like CHOOSE_QT( "${CMAKE_CURRENT_SOURCE_DIR}/cxx/thirdparty/Qt" ). But perhaps this depends on how you install Qt. –  David K May 19 at 17:46
    
@DavidK we don't install Qt, we keep several repos of different Qt builds and use them as subrepos or just clone somewhere and set env variables for paths. So, all scripts were made to support that scheme. We do it because we have various projects that use different Qts and we don't wanna port some old projects to a new Qt, because it occasionally introduces bugs. You can read about all that mess in my question about selecting a Qt instance on stackoverflow right here stackoverflow.com/questions/12511370/… –  Occulta May 20 at 8:38

1 Answer 1

up vote 7 down vote accepted
+100

My CMake-fu is a bit rusty - probably as old as this question -, but I still see some things that could be trivially improved:

  • CMake doesn't require else() and endif() to contain the original if() expression anymore. It seems that this restriction disappeared a long time ago, so you can simplify your code by leaving empty else() and endif() expressions. Your indentation already does a great job to document where an if() is ending.

  • The remark above also applies for endmacro() and endforeach and more generally any endxxx() instruction. Note that it works even with your version: you left the endmacro() empty for ADD_FRAMEWORK.

  • Your capitalization is not consistent. You sometimes use FOREACH while you sometimes use foreach. I know that CMake is not really case-sensitive, but being consistent doesn't hurt and helps case-sensitive search in code editors.

  • There are too many occurrences of "${CMAKE_CURRENT_SOURCE_DIR}/cxx/thirdparty. You should define a ${THIRD_PARTY_DIR} variable or something to having repeating the full path over and over. It will avoid many problems if the path changes. It seems that there isn't a portable way to make it work for both WIN32 and APPLE, but doing it for WIN32 would already help.

  • You should remove commented out code. If you need an old version of your code, you should simply rely on revision control software. If you have comments like "comment out to do something", it generally means that the "something" in question should be a command line option.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.