OpenWalnutUtils.cmake 39.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
#---------------------------------------------------------------------------
#
# Project: OpenWalnut ( http://www.openwalnut.org )
#
# Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
# For more information see http:#www.openwalnut.org/copying
#
# This file is part of OpenWalnut.
#
# OpenWalnut is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# OpenWalnut is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with OpenWalnut. If not, see <http:#www.gnu.org/licenses/>.
#
#---------------------------------------------------------------------------

25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
# This function converts a given filename to a proper target name. This is very useful if you want to define custom targets for
# files and need a unique name.
# _filename the filename to convert
# _target returns the proper target string
FUNCTION( FILE_TO_TARGETSTRING _filename _target )
    # strip the whole path up to src
    STRING( REGEX REPLACE "^.*/src" "src" fileExcaped "${_filename}" ) 

    # remove all those ugly chars
    STRING( REGEX REPLACE "[^A-Za-z0-9]" "X" fileExcaped "${fileExcaped}" )

    # done. Return value
    SET( ${_target} "${fileExcaped}" PARENT_SCOPE )
ENDFUNCTION( FILE_TO_TARGETSTRING )

# Gets the major, minor and patch number from a version string.
# _VersionString the string to split
# _Major the major version number - result
# _Minor the minor version number - result
# _Patch the patch number - result
FUNCTION( SPLIT_VERSION_STRING _VersionString _Major _Minor _Patch )
  STRING( STRIP _VersionString ${_VersionString} )
  STRING( REGEX MATCH "^[0-9]+" _MajorProxy "${_VersionString}" )
  STRING( REGEX MATCH "[^0-9][0-9]+[^0-9]" _MinorProxy "${_VersionString}" )
  STRING( REGEX MATCH "[0-9]+" _MinorProxy "${_MinorProxy}" )
  STRING( REGEX MATCH "[0-9]+$" _PatchProxy "${_VersionString}" )
  SET( ${_Major} "${_MajorProxy}" PARENT_SCOPE )
  SET( ${_Minor} "${_MinorProxy}" PARENT_SCOPE )
  SET( ${_Patch} "${_PatchProxy}" PARENT_SCOPE )
ENDFUNCTION( SPLIT_VERSION_STRING )
55

56
# Recursively searches compile files (headers, sources).
57 58 59 60 61 62 63 64 65 66
# _DirString: where to search
# _CPPFiles contains the cpp files afterwards
# _HFiles contains the h files afterwards, without tests
# _TestFiles contains only the test headers
FUNCTION( COLLECT_COMPILE_FILES _DirString _CPPFiles _HFiles _TestFiles )
    # recursively get all files
    FILE( GLOB_RECURSE CPP_FILES ${_DirString}/*.cpp )
    FILE( GLOB_RECURSE H_FILES   ${_DirString}/*.h )
    FILE( GLOB_RECURSE TEST_FILES   ${_DirString}/*_test.h )

67
    # exclude some special dirs
68
    FOREACH( file ${H_FILES} )
69
        # the test directories should be excluded from normal compilation completely
70
        STRING( REGEX MATCH "^.*\\/test\\/.*" IsTest "${file}" )
71 72
        # ext sources should be build seperatly 
        STRING( REGEX MATCH "^.*\\/ext\\/.*" IsExternal "${file}" )
73
        IF( IsTest )
74
            LIST( REMOVE_ITEM H_FILES ${file} )
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
        ENDIF()
        IF( IsExternal )
            LIST( REMOVE_ITEM H_FILES ${file} )
        ENDIF()
    ENDFOREACH( file )
    FOREACH( file ${CPP_FILES} )
        # the test directories should be excluded from normal compilation completely
        STRING( REGEX MATCH "^.*\\/test\\/.*" IsTest "${file}" )
        # ext sources should be build seperatly 
        STRING( REGEX MATCH "^.*\\/ext\\/.*" IsExternal "${file}" )
        IF( IsTest )
            LIST( REMOVE_ITEM CPP_FILES ${file} )
        ENDIF()
        IF( IsExternal )
            LIST( REMOVE_ITEM CPP_FILES ${file} )
        ENDIF()
91
    ENDFOREACH( file )
92

93 94 95 96 97
    SET( ${_CPPFiles} "${CPP_FILES}" PARENT_SCOPE )
    SET( ${_HFiles} "${H_FILES}" PARENT_SCOPE )
    SET( ${_TestFiles} "${TEST_FILES}" PARENT_SCOPE )
ENDFUNCTION( COLLECT_COMPILE_FILES )

98 99 100 101 102 103 104 105 106
# Recursively searches shader files with extension glsl.
# _DirString where to search
# _GLSLFiles the list of files found
FUNCTION( COLLECT_SHADER_FILES _DirString _GLSLFiles )
    # recursively get all files
    FILE( GLOB_RECURSE GLSL_FILES ${_DirString}/*.glsl )
    SET( ${_GLSLFiles} "${GLSL_FILES}" PARENT_SCOPE )
ENDFUNCTION( COLLECT_SHADER_FILES )

107 108 109 110 111 112 113 114
# Add tests and copy their fixtures by specifiying the test headers. This is completely automatic and is done recusrively (fixtures). The test
# filenames need to be absolute. The fixtures are searched in CMAKE_CURRENT_SOURCE_DIR.
# _TEST_FILES the list of test header files.
# _TEST_TARGET for which target are the tests? This is added as link library for each test too.
# Third, unnamed parameter: additional dependencies as list
FUNCTION( SETUP_TESTS _TEST_FILES _TEST_TARGET )
    # Only do something if needed
    IF( OW_COMPILE_TESTS )
115 116 117
        # Abort if no tests are present
        LIST( LENGTH _TEST_FILES TestFileListLength )
        IF( ${TestFileListLength} STREQUAL "0" )
118
            # MESSAGE( STATUS "No tests for target ${_TEST_TARGET}. You should always consider unit testing!" )
119 120 121
            RETURN()
        ENDIF()

122 123 124 125
        # the optional parameter is an additional dependencies list
        SET( _DEPENDENCIES ${ARGV2} )

        # target directory for fixtures
126
        SET( FixtureTargetDirectory "${CMAKE_BINARY_DIR}/fixtures/${_TEST_TARGET}/" )
127 128 129 130 131

        # -------------------------------------------------------------------------------------------------------------------------------------------
        # Setup CXX test
        # -------------------------------------------------------------------------------------------------------------------------------------------

132 133 134
        # Also create a list of paths where the tests where found
        SET( FixturePaths )

135 136 137 138 139 140 141
        # Add each found test and create a target for it
        FOREACH( testfile ${_TEST_FILES} )
            # strip path and extension
            STRING( REGEX REPLACE "^.*/" "" StrippedTestFile "${testfile}" )
            STRING( REGEX REPLACE "\\..*$" "" PlainTestFileName "${StrippedTestFile}" )
            STRING( REGEX REPLACE "_test$" "" TestFileClass "${PlainTestFileName}" )

142 143 144 145 146 147
            # also extract test path
            STRING( REPLACE "${StrippedTestFile}" "fixtures" TestFileFixturePath "${testfile}" )
            IF( EXISTS "${TestFileFixturePath}" AND IS_DIRECTORY "${TestFileFixturePath}" )
                LIST( APPEND FixturePaths ${TestFileFixturePath} )
            ENDIF()

148 149 150 151 152
            # create some name for the test
            SET( UnitTestName "unittest_${TestFileClass}" )

            # create the test-target
            CXXTEST_ADD_TEST( ${UnitTestName} "${UnitTestName}.cc" ${testfile} )
153
            TARGET_LINK_LIBRARIES( ${UnitTestName} ${_TEST_TARGET} ${CMAKE_STANDARD_LIBRARIES} ${_DEPENDENCIES} )
154 155 156 157 158 159 160 161 162 163

            # unfortunately, the tests search their fixtures relative to their working directory. So we add a preprocessor define containing the
            # path to the fixtures. This is quite ugly but I do not know how to ensure that the working directory of tests can be modified.
            SET_SOURCE_FILES_PROPERTIES( "${UnitTestName}.cc" PROPERTIES COMPILE_DEFINITIONS "W_FIXTURE_PATH=std::string( \"${FixtureTargetDirectory}\" )" )
        ENDFOREACH( testfile )

        # -------------------------------------------------------------------------------------------------------------------------------------------
        # Search fixtures
        # -------------------------------------------------------------------------------------------------------------------------------------------

164 165 166 167 168
        # REMOVE_DUPLICATES throws an error if list is empty. So we check this here
        LIST( LENGTH FixturePaths ListLength )
        IF( NOT ${ListLength} STREQUAL "0" )
            # the list may contain duplicates
            LIST( REMOVE_DUPLICATES FixturePaths )
169

170 171 172 173 174 175 176
            # ---------------------------------------------------------------------------------------------------------------------------------------
            # Create copy target for each fixture directory
            # ---------------------------------------------------------------------------------------------------------------------------------------

            # for each fixture, copy to build dir
            FOREACH( FixtureDir ${FixturePaths} )
                # we need a unique name for each fixture dir as target
177 178
                FILE_TO_TARGETSTRING( ${FixtureDir} FixtureDirEscaped )

179 180 181
                # finally, create the copy target
                ADD_CUSTOM_TARGET( ${_TEST_TARGET}_CopyFixtures_${FixtureDirEscaped}
                    COMMAND ${CMAKE_COMMAND} -E copy_directory "${FixtureDir}" "${FixtureTargetDirectory}"
182
                    COMMENT "Copy fixtures of ${_TEST_TARGET} from ${FixtureDir} to ${FixtureTargetDirectory}."
183 184 185 186
                )
                ADD_DEPENDENCIES( ${_TEST_TARGET} ${_TEST_TARGET}_CopyFixtures_${FixtureDirEscaped} )
            ENDFOREACH( FixtureDir )
        ENDIF()
187 188 189
    ENDIF( OW_COMPILE_TESTS )
ENDFUNCTION( SETUP_TESTS )

190 191 192
# This function sets up the build system to ensure that the specified list of shaders is available after build in the target directory. It
# additionally setups the install targets. Since build and install structure are the same, specify only relative targets here which are used for
# both.
193
# _Shaders list of shaders
194 195
# _TargetDir the directory where to put the shaders. Relative to ${PROJECT_BINARY_DIR} and install dir. You should avoid ".." stuff. This can
# break the install targets
196 197
# _Component the name of the install component
FUNCTION( SETUP_SHADERS _Shaders _TargetDir _Component )
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
    EXECUTE_PROCESS( COMMAND ${CMAKE_COMMAND} -E make_directory ${_TargetDir} )

    # should we copy or link them?
    SET( ShaderOperation "copy_if_different" )
    IF ( OW_LINK_SHADERS ) # link
         SET( ShaderOperation "create_symlink" )
    ENDIF( OW_LINK_SHADERS )

    # now do the operation for each shader into build dir
    FOREACH( fname ${_Shaders} )
        # We need the plain filename (create_symlink needs it)
        STRING( REGEX REPLACE "^.*/" "" StrippedFileName "${fname}" )

        # construct final filename
        SET( targetFile "${PROJECT_BINARY_DIR}/${_TargetDir}/${StrippedFileName}" )

        # if the file already exists as non-symlink and we use the "create_symlink" command, we first need to remove the files
        IF( NOT IS_SYMLINK ${targetFile} AND OW_LINK_SHADERS )
            # before creating the symlink, remove the old files or cmake will not create the symlinks (as intended)
            EXECUTE_PROCESS( COMMAND ${CMAKE_COMMAND} -E remove -f ${targetFile} )
        ELSEIF( IS_SYMLINK ${targetFile} AND NOT OW_LINK_SHADERS )
            # also handle the inverse case. The files exist as symlinks but copies should be used. Remove symlinks!
            EXECUTE_PROCESS( COMMAND ${CMAKE_COMMAND} -E remove -f ${targetFile} )
        ENDIF()

        # let cmake do it
        EXECUTE_PROCESS( COMMAND ${CMAKE_COMMAND} -E ${ShaderOperation} ${fname} ${targetFile} )
    ENDFOREACH( fname )

    # now add install targets for each shader. All paths are relative to the current source dir.
    FOREACH( fname ${_Shaders} )
        INSTALL( FILES ${fname} DESTINATION ${_TargetDir} 
                                COMPONENT ${_Component}
               )
    ENDFOREACH( fname )
233 234
ENDFUNCTION( SETUP_SHADERS )

235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
# Sets up the stylecheck mechanism. Use this to add your codes to the stylecheck mechanism.
# _TargetName the name of the target which gets added here
# _CheckFiles the list of files to check
# _Excludes a list of exclusion rules. These are regular expressions.
FUNCTION( SETUP_STYLECHECKER _TargetName _CheckFiles _Excludes )

    # to exlude some files, check each file against each exlusion rule
    FOREACH( filename ${_CheckFiles} )
        FOREACH( excludeRule ${_Excludes} )
            STRING( REGEX MATCH "${excludeRule}" IsExcluded "${filename}" )
            IF( IsExcluded )
                LIST( REMOVE_ITEM _CheckFiles ${filename} )
            ENDIF( IsExcluded )
        ENDFOREACH( excludeRule )
    ENDFOREACH( filename )
250 251

    # the stylechecker allows coloring the output. Enable if color make is active
252 253 254 255 256 257 258 259 260 261
    IF( CMAKE_COLOR_MAKEFILE )
        SET( STYLECHECK_OPTIONS "--color" )
    ELSE()
        SET( STYLECHECK_OPTIONS "" )
    ENDIF()

    # Further system specific options
    IF( CMAKE_HOST_WIN32 )
        SET( XARGS_OPTIONS "-n 128" )
    ELSEIF( CMAKE_HOST_UNIX )
262
        SET( XARGS_OPTIONS "-n 64" )
263 264 265 266 267
    ELSE()
        SET( XARGS_OPTIONS "" )
    ENDIF()

    # Export our filtered file list to a file in build dir
268
    SET( BrainLinterListFile "${PROJECT_BINARY_DIR}/brainlint/brainlintlist_${_TargetName}" )
269 270 271
    LIST( REMOVE_ITEM _CheckFiles "" )
    STRING( REPLACE ";" "\n" _CheckFiles "${_CheckFiles}" )
    FILE( WRITE ${BrainLinterListFile} "${_CheckFiles}\n" )
272 273 274

    # add a new target for this lib
    ADD_CUSTOM_TARGET( stylecheck_${_TargetName}
275
                       COMMAND  cat ${BrainLinterListFile} | xargs ${XARGS_OPTIONS} ${PROJECT_SOURCE_DIR}/../tools/style/brainlint/brainlint.py ${STYLECHECK_OPTIONS} 2>&1 | grep -iv 'Total errors found: 0$$' | cat
276 277 278 279 280 281 282 283
                       WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
                       COMMENT "Check if ${_TargetName} complies to CodingStandard"
    )

    # make the stylecheck taret depend upon this
   ADD_DEPENDENCIES( stylecheck "stylecheck_${_TargetName}" )
ENDFUNCTION( SETUP_STYLECHECKER )

284 285
# This function handles local and global resources needed for program execution. This is a generic function which allows any resource in a source directory
# to be copied  to a target directory in build and install directory. Use this for module resources or the OpenWalnut resources.
286 287 288
#
# For convenience, this function first checks in _source_path, and if nothing was found there, in ${CMAKE_CURRENT_SOURCE_DIR}/${_source_path}]
# _source_path path to a directory whose CONTENTS get copied/installed.
289 290 291 292
# _destination_path where to put the resources. This MUST be relative to the install/build dir (${PROJECT_BINARY_DIR})
# _component the component to which this is related. This must be unique for every call.
# _installcomponent to which installation component does this resource belong? (i.e. MODULES ...)
FUNCTION( SETUP_RESOURCES_GENERIC _source_path _destination_path _component _installcomponent )
293
    # as all the resources with the correct directory structure reside in ../resources, this target is very easy to handle
294
    SET( ResourcesPath "${_source_path}" )
295 296 297 298 299 300

    # if the given dir does not exists here, it might be relative? lets try to check this against the current source directory
    IF( NOT IS_DIRECTORY "${ResourcesPath}" )
      SET( ResourcesPath ${CMAKE_CURRENT_SOURCE_DIR}/${ResourcesPath} )
    ENDIF()

301 302
    # remove trailing slashes if any
    STRING( REGEX REPLACE "/$" "" ResourcesPath "${ResourcesPath}" )
Alexander Wiebel's avatar
Alexander Wiebel committed
303
    # this ensures we definitely have one slash at the end
304 305 306 307
    SET( ResourcesPath "${ResourcesPath}/" )

    IF( IS_DIRECTORY "${ResourcesPath}" )
        MESSAGE( STATUS "Found resources in ${ResourcesPath}" )
308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325

        ADD_CUSTOM_TARGET( ResourceConfiguration_${_component}
            ALL
            COMMAND ${CMAKE_COMMAND} -E copy_directory "${ResourcesPath}" "${PROJECT_BINARY_DIR}/${_destination_path}/"
            COMMENT "Copying resources for ${_component} to build directory"
        )

        # Also specify install target
        INSTALL( DIRECTORY ${ResourcesPath}
                 DESTINATION "${_destination_path}"
                 COMPONENT ${_installcomponent}
                 PATTERN "bin/*"            # binaries need to be executable
                     PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE
                                 GROUP_READ GROUP_EXECUTE
                                 WORLD_READ WORLD_EXECUTE
                 )
    ENDIF()
ENDFUNCTION( SETUP_RESOURCES_GENERIC )
326

327 328 329 330 331 332 333 334 335 336 337
# This function handles global resources needed for program execution. Place your resources in "${CMAKE_CURRENT_SOURCE_DIR}/../resources/". They
# get copied to the build directory and a proper install target is provided too. 
# _component the component to which the resources belong, must be unique
# _resource the exact resource under resources-directory
FUNCTION( SETUP_GLOBAL_RESOURCES _resource _component )
    # just forward the call with the proper parameter
    SETUP_RESOURCES_GENERIC( "${PROJECT_SOURCE_DIR}/../resources/${_resource}/"  # source dir
                             "."                            # target dir - global resources always put into the build/install dir directly
                             ${_component}                  # component
                             ${_component}                  # install component is same as component as it is unique
    )
338 339 340 341 342 343 344 345 346 347

    # allow platform dependent setup
    STRING( TOLOWER ${CMAKE_SYSTEM_NAME} PLATFORM_NAME_LOWER )
    # just forward the call with the proper parameter
    SETUP_RESOURCES_GENERIC( "${PROJECT_SOURCE_DIR}/../resources/platformDependent/${_resource}/${PLATFORM_NAME_LOWER}"  # source dir
                             "."                            # target dir - global resources always put into the build/install dir directly
                             ${_component}_${PLATFORM_NAME_LOWER}  # component
                             ${_component}                  # install component is same as component as it is unique
    )

348
ENDFUNCTION( SETUP_GLOBAL_RESOURCES )
349

350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
# This function copies the typical source docs (README, AUTHORS, CONTRIBUTORS and Licence files to the specified directory.
# _component the component to which the resources belong
# _targetDirRelative the relative target dir.
FUNCTION( SETUP_COMMON_DOC _target _component )
    SETUP_ADDITIONAL_FILES( ${_target} 
                            ${_component}
                            "${PROJECT_SOURCE_DIR}/../README"
                            "${PROJECT_SOURCE_DIR}/../AUTHORS"
                            "${PROJECT_SOURCE_DIR}/../CONTRIBUTORS"
                          )

    # If the user did not disable license-copying, do it
    # NOTE: use this "double-negative" to use the fact that undefined variables yield FALSE.
    IF( NOT OW_PACKAGE_NOCOPY_LICENSE )
        SETUP_ADDITIONAL_FILES( ${_target} 
                                ${_component}
                                "${PROJECT_SOURCE_DIR}/../COPYING"
                                "${PROJECT_SOURCE_DIR}/../COPYING.LESSER"
                              )
    ENDIF()
ENDFUNCTION( SETUP_COMMON_DOC )

372
# This function configures the specified file in the given resource. This is especially useful for scripts where to replace certain
373 374 375 376 377
# CMake variables (like pkg-config files).
# _resource the name of the resource in the resources directory
# _file the file in the the resource to configure
# _component the install component to which the file belongs
FUNCTION( SETUP_CONFIGURED_FILE _resource _file _component )
378 379 380 381 382 383 384
    GET_FILENAME_COMPONENT( filepath ${_file} PATH )
    SETUP_CONFIGURED_FILE_TO( ${_resource} ${_file} ${_component} ${filepath} )
ENDFUNCTION( SETUP_CONFIGURED_FILE )

# This function configures the specified file in the given resource. This is especially useful for scripts where to replace certain
# CMake variables (like pkg-config files).
# _resource the name of the resource in the resources directory
385
# _file the file in the resource to configure
386 387 388
# _component the install component to which the file belongs
# _destinationPath where to place the file as relative path (do not include filename, relative to project binary root)
FUNCTION( SETUP_CONFIGURED_FILE_TO _resource _file _component _destinationPath )
389
    SET( ResourcesPath "${PROJECT_SOURCE_DIR}/../resources/${_resource}/" )
390 391
    GET_FILENAME_COMPONENT( filename ${_file} NAME )
    CONFIGURE_FILE( "${ResourcesPath}/${_file}" "${PROJECT_BINARY_DIR}/${_destinationPath}/${filename}" @ONLY )
392
    # Install the file
393 394
    INSTALL( FILES "${PROJECT_BINARY_DIR}/${_destinationPath}/${filename}" DESTINATION "${_destinationPath}"
                                                                           COMPONENT ${_component}
395
           )
396
ENDFUNCTION( SETUP_CONFIGURED_FILE_TO )
397

398 399 400
# This function eases the process of copying and installing additional files which not reside in the resource path.
# It creates a target (ALL is depending on it) AND the INSTALL operation.
# _destination where to put them. This MUST be relative to the build dir and install dir.
401
# _component the name of the component for these files
402
# _OTHERS you can add an arbitrary list of additional arguments which represent the files to copy.
403
FUNCTION( SETUP_ADDITIONAL_FILES _destination _component )
404
    FOREACH( _file ${ARGN} )
405
        # only do if it exists
406
        IF( EXISTS ${_file} )
407 408 409 410 411 412 413 414 415 416 417 418 419 420 421
            # create useful target name
            FILE_TO_TARGETSTRING( ${_file} fileTarget )
            # add a copy target
            ADD_CUSTOM_TARGET( CopyAdditionalFile_${fileTarget}_${_component}
                ALL
                COMMAND ${CMAKE_COMMAND} -E make_directory "${PROJECT_BINARY_DIR}/${_destination}/"
                COMMAND ${CMAKE_COMMAND} -E copy "${_file}" "${PROJECT_BINARY_DIR}/${_destination}/"
                COMMENT "Copying file ${_file}"
            )

            # add a INSTALL operation for this file
            INSTALL( FILES ${_file} DESTINATION ${_destination}
                                    COMPONENT ${_component}
                   )
        ENDIF()
422 423 424 425 426 427 428
    ENDFOREACH() 
ENDFUNCTION( SETUP_ADDITIONAL_FILES )

# This function copies a given directory or its contents to the specified destination. Since cmake is quite strange in handling directories
# somehow, we needed to trick here. 
# _destination where to put the directory/its contents. Realtive to build dir and install dir.
# _directory the directory to copy
429
# _component the name of the component for these files
430 431
# _contents if TRUE, the contents of _directory are copied into _destination. If FALSE, _destination as-is is copied to _destination/.. (sorry
#           for this weird stuff. Complain at cmake mailing list ;-))
432
FUNCTION( SETUP_ADDITIONAL_DIRECTORY _destination _directory _component _contents )
433 434 435 436 437 438
    # create a nice target name
    FILE_TO_TARGETSTRING( ${_directory} directoryTarget )

    # add a copy target
    # this copies the CONTENTS of the specified directory into the specified destination dir.
    # NOTE: cmake -E says, that copying a directory with the copy command is pssible. But on my system it isn't.
439
    ADD_CUSTOM_TARGET( CopyAdditionalDirectory_${directoryTarget}_${_component}
440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458
        ALL
        COMMAND ${CMAKE_COMMAND} -E make_directory "${PROJECT_BINARY_DIR}/${_destination}/"
        COMMAND ${CMAKE_COMMAND} -E copy_directory "${_directory}" "${PROJECT_BINARY_DIR}/${_destination}"
        COMMENT "Copying directory ${_directory}"
    )

    # we need to distinquish here whether the user wants to copy the contents of the specified directory or the whole directory.
    # NOTE: unfortunately, the semantics of cmake -E and INSTALL are different. We need to fix this with this hack.
    IF( _contents )
        # OK, the user wants us to copy the contents of the specified _directory INTO the dpecified destination
        SET(  InstallDestination "${_destination}" )
    ELSE()
        # see "cmake -E " for help. The copy_directory copies its contents and copy copies the directory as is.
        SET(  InstallDestination "${_destination}/../" )
    ENDIF()

    # add a INSTALL operation for this file
    INSTALL( DIRECTORY ${_directory}
             DESTINATION ${InstallDestination}
459
             COMPONENT ${_component}
460 461 462
           )
ENDFUNCTION( SETUP_ADDITIONAL_DIRECTORY )

463 464 465 466 467 468 469 470 471
# Function to setup and library install target. It contains all the permission and namelink magic needed.
# _libName the library to install (needs to be a targed added with ADD_LIBRARY).
# _targetRelative the relative target dir. You should use OW_LIBRARY_DIR_RELATIVE in most cases
# _component the name of the component to which the lib belongs. If you use some strange things here, consider updating the package configuration
#            too as it uses these components
FUNCTION( SETUP_LIB_INSTALL _libName _targetRelative _component )
    # NOTE: we need two separate install targets here since the namelink to the lib (libopenwalnut.so -> linopenwalnut.so.1.2.3) is only needed
    # in the DEV release. Have a look at NAMELINK_SKIP and NAMELINK_ONLY
    INSTALL( TARGETS ${_libName}
472 473 474 475 476 477
                ARCHIVE # NOTE: this is needed on windows (*.dll.a)
                    DESTINATION ${_targetRelative} 
                    PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE 
                                GROUP_READ GROUP_EXECUTE  
                                WORLD_READ WORLD_EXECUTE
                RUNTIME # NOTE: this is needed on windows (*.dll)
478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501
                    DESTINATION ${_targetRelative} 
                    PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE 
                                GROUP_READ GROUP_EXECUTE  
                                WORLD_READ WORLD_EXECUTE
                LIBRARY # NOTE: this is needed for all the others
                    DESTINATION ${_targetRelative}
                    PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE
                                GROUP_READ GROUP_EXECUTE
                                WORLD_READ WORLD_EXECUTE
                    NAMELINK_SKIP
             COMPONENT ${_component}
    )
ENDFUNCTION( SETUP_LIB_INSTALL )

# Function which handles typical "developer" install targets. It creates the symlink (namelink) for the lib and copies the headers to some
# include directory.
# _libName the devel target for this lib (needs to be a target name created with ADD_LIBRARY)
# _targetRelative the relative path to the lib
# _headers a list of headers. (Lists are ;-separated). This function strips CMAKE_CURRENT_SOURCE_DIR from each path!
# _headerTargetRelative relative target dir for the includes
# _component the name of the component.
FUNCTION( SETUP_DEV_INSTALL _libName _targetRelative _headers _headerTargetRelative _component )
    INSTALL( TARGETS ${_libName}
                ARCHIVE # NOTE: this is needed on windows
502 503 504 505 506
                    DESTINATION ${_targetRelative} 
                    PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE
                                GROUP_READ GROUP_EXECUTE
                                WORLD_READ WORLD_EXECUTE
                RUNTIME # NOTE: this is needed on windows (*.dll)
507 508 509 510 511 512 513 514 515 516 517 518 519
                    DESTINATION ${_targetRelative} 
                    PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE 
                                GROUP_READ GROUP_EXECUTE  
                                WORLD_READ WORLD_EXECUTE
                LIBRARY # NOTE: this is needed for all the others
                    DESTINATION ${_targetRelative}
                    PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE
                                GROUP_READ GROUP_EXECUTE
                                WORLD_READ WORLD_EXECUTE
                    NAMELINK_ONLY
             COMPONENT ${_component}
    )

520
    # we want to copy the headers to. Unfortunately, cmake's install command does not preserve the directory structure.
521 522 523 524 525 526 527 528 529 530
    FOREACH( _header ${${_headers}} )
        STRING( REGEX MATCH "(.*)[/\\]" directory ${_header} )
        STRING( REPLACE "${CMAKE_CURRENT_SOURCE_DIR}" "" directoryRelative ${directory} )
        INSTALL( FILES ${_header} 
                    DESTINATION ${_headerTargetRelative}/${directoryRelative}
                    COMPONENT ${_component}
               )
    ENDFOREACH()
ENDFUNCTION( SETUP_DEV_INSTALL )

531 532 533 534
# This function tries to find a proper version string. It therefore uses the file src/../VERSION and mercurial. If the file exists and is not
# empty, the contents of it get combined with the mercurial results if mercurial is installed. If not, only the file content will be used. If
# both methods fail, a default string is used.
# _version the returned version string
535
# _api_version returns only the API-version loaded from the version file. This is useful to set CMake version info for release compilation
536
# _default a default string you specify if all version check methods fail
537
FUNCTION( GET_VERSION_STRING _version _api_version )
538
    # Undef the OW_VERSION variable
539
    UNSET( OW_VERSION_GIT )
540 541 542 543 544 545
    UNSET( OW_VERSION_FILE )

    # Grab version file.
    SET( OW_VERSION_FILENAME ${PROJECT_SOURCE_DIR}/../VERSION )
    IF( EXISTS ${OW_VERSION_FILENAME} )
        # Read the version file
546 547
        FILE( READ ${OW_VERSION_FILENAME} OW_VERSION_FILE_CONTENT )
        # The first regex will mathc 
548
        STRING( REGEX REPLACE ".*[^#]VERSION=([0-9]+\\.[0-9]+\\.[0-9]+[_~a-z,A-Z,0-9]*(\\+gitX?[0-9]*)?).*" "\\1"  OW_VERSION_FILE  ${OW_VERSION_FILE_CONTENT} ) 
549 550 551 552 553
        STRING( COMPARE EQUAL ${OW_VERSION_FILE} ${OW_VERSION_FILE_CONTENT}  OW_VERSION_FILE_INVALID )
        IF( OW_VERSION_FILE_INVALID )
            UNSET( OW_VERSION_FILE )
        ENDIF()

554 555 556 557 558
        # this is ugly because, if the version file is empty, the OW_VERSION_FILE content is "". Unfortunately, this is not UNDEFINED as it would be
        # by SET( VAR "" ) ... so set it manually
        IF( OW_VERSION_FILE STREQUAL "" )
            UNSET( OW_VERSION_FILE )
        ENDIF()
559 560 561 562
    ENDIF()
    # if the version file could not be parsed, print error
    IF( NOT OW_VERSION_FILE )
        MESSAGE( FATAL_ERROR "Could not parse \"${PROJECT_SOURCE_DIR}/../VERSION\"." )
563 564
    ENDIF()

565 566
    # Use git to query version information.
    # -> the nice thing is: if git is not available, no compilation errors anymore
567
    # NOTE: it is run insde the project source directory
568
    EXECUTE_PROCESS( COMMAND git rev-parse --short HEAD OUTPUT_VARIABLE OW_VERSION_GIT RESULT_VARIABLE gitRetVar 
569 570
                     WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
                    )
571 572 573 574
    IF( NOT ${gitRetVar} STREQUAL 0 )
        UNSET( OW_VERSION_GIT )
        # be more nice if we do not find git version. The simply strip the +git tag.
        STRING( REGEX REPLACE "\\+gitX" "" OW_VERSION ${OW_VERSION_FILE} )
575
    ELSE()
576
        # if we have the mercurial info -> complement the version string
577
        STRING( REGEX REPLACE "\n$" "" OW_VERSION_GIT "${OW_VERSION_GIT}" )
578
        STRING( REGEX REPLACE "gitX" "git${OW_VERSION_GIT}" OW_VERSION ${OW_VERSION_FILE} )
579
    ENDIF()
580 581 582
  
    SET( ${_version} ${OW_VERSION} PARENT_SCOPE )

583 584
    # we need to separate the API version too. This basically is the same as the release version,
    # but without the Git statement
585 586 587
    STRING( REGEX REPLACE "([0-9]+\\.[0-9]+\\.[0-9]).*" "\\1"  OW_API_VERSION ${OW_VERSION} ) 
    SET( ${_api_version} ${OW_API_VERSION} PARENT_SCOPE )

588 589
ENDFUNCTION( GET_VERSION_STRING )

590 591 592 593 594 595 596 597 598 599
# This functions adds a custom target for generating the specified version header. This is very useful if you want to include build-time version
# information in your code. If your executable or lib is created in the same CMakeLists as this function is called, just add the
# _OW_VERSION_Header filename you specified here to your compile files. If you call this and add target XYZ in a subdirectory, you NEED to add
# the lines: 
#  ADD_DEPENDENCIES( XYZ OW_generate_version_header )
#  SET_SOURCE_FILES_PROPERTIES( ${OW_VERSION_HEADER} PROPERTIES GENERATED ON )
# This is needed since CMake can only use the ADD_CUSTOM_COMMAND output as dependency if the custom command was called inside the SAME directory
# as the target is. If not, an additional target needs to be defined and CMake needs information about generated files.
#
# _OW_VERSION_HEADER the filename where to store the header. Should be absolute.
600 601
# _PREFIX the string used as prefix in the header. Useful if you have multiple version headers.
FUNCTION( SETUP_VERSION_HEADER _OW_VERSION_HEADER _PREFIX )
602 603 604 605
    # This ensures that an nonexisting .git/index file won't cause a compile error (do not know how to make target)
    SET( GIT_DEP "" )
    IF( EXISTS ${PROJECT_SOURCE_DIR}/../.git/index )
        SET( GIT_DEP ${PROJECT_SOURCE_DIR}/../.git/index )
606
    ENDIF()
607 608 609

    # The file WVersion.* needs the version definition.
    ADD_CUSTOM_COMMAND( OUTPUT ${_OW_VERSION_HEADER}
610
        DEPENDS ${PROJECT_SOURCE_DIR}/../VERSION ${GIT_DEP}
611
                        COMMAND ${CMAKE_COMMAND} -D PROJECT_SOURCE_DIR:STRING=${PROJECT_SOURCE_DIR} -D HEADER_FILENAME:STRING=${_OW_VERSION_HEADER} -D PREFIX:STRING=${_PREFIX} -P OpenWalnutVersion.cmake
612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628
                        WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/../tools/cmake/
                        COMMENT "Creating Version Header ${_OW_VERSION_HEADER}."
    )

    # this is needed if the file is also a dependency of an target XYZ in a CMakeLists file in some subdirectory. Then, you should add 
    # ADD_DEPENDENCIES( XYZ OW_generate_version_header )
    ADD_CUSTOM_TARGET( OW_generate_version_header DEPENDS ${_OW_VERSION_HEADER} )
ENDFUNCTION( SETUP_VERSION_HEADER )

# Comvenience function which does what SETUP_VERSION_HEADER requests from you if you need the version header in some CMakeLists different from
# the one which called SETUP_VERSION_HEADER. It uses ${OW_VERSION_HEADER} automatically. So it needs to be set.
#
# _target: the target name which needs the header
FUNCTION( SETUP_USE_VERSION_HEADER _target )
    SET_SOURCE_FILES_PROPERTIES( ${OW_VERSION_HEADER} PROPERTIES GENERATED ON )
    ADD_DEPENDENCIES( ${_target} OW_generate_version_header )
ENDFUNCTION( SETUP_USE_VERSION_HEADER )
629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689

# Automatically add a module. Do not use this function if your module nees additional dependencies or similar. For more flexibility, use your own
# CMakeLists in combination with the SETUP_MODULE function. The Code for the module is searched in ${CMAKE_CURRENT_SOURCE_DIR}/${_MODULE_NAME}.
# It loads the CMakeLists in the module dir if there is any.
# _MODULE_NAME the name of the module
# Optional second Parameter: list of additional dependencies
# Optional third Parameter: list of style-excludes as regexp.
FUNCTION( ADD_MODULE _MODULE_NAME )
    SET( MODULE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/${_MODULE_NAME} )

    # is there a CMakeLists.txt? If yes, use it.
    IF( EXISTS ${MODULE_SOURCE_DIR}/CMakeLists.txt )
        ADD_SUBDIRECTORY( ${_MODULE_NAME} )
        RETURN()
    ENDIF()

    # Optional second parameter: style exclusion list
    SET( _DEPENDENCIES ${ARGV1} )
    SET( _EXCLUDES ${ARGV2} )

    # -----------------------------------------------------------------------------------------------------------------------------------------------
    # Setup for compilation
    # -----------------------------------------------------------------------------------------------------------------------------------------------

    # Let this function do the job. It sets up tests and copies shaders automatically. It additionally configures the stylecheck mechanism for this
    # module.
    SETUP_MODULE( ${_MODULE_NAME}                # name of the module
                 "${MODULE_SOURCE_DIR}"
                 "${_DEPENDENCIES}"
                 "${_EXCLUDES}"
    )
ENDFUNCTION( ADD_MODULE )

# Comfortably setup a module for compilation. This automatically handles the target creation, stylecheck and tests (with fixtures).
# _MODULE_NAME the name of the module
# _MODULE_SOURCE_DIR where to finx the code for the module
# _MODULE_DEPENDENCIES additional dependencies can be added here. This is a list. Use ";" to add multiple additional dependencies
# _MODULE_STYLE_EXCLUDES exclude files from stylecheck matching these regular expressions (list)
FUNCTION( SETUP_MODULE _MODULE_NAME _MODULE_SOURCE_DIR _MODULE_DEPENDENCIES _MODULE_STYLE_EXCLUDES )
    # -----------------------------------------------------------------------------------------------------------------------------------------------
    # Some common setup
    # -----------------------------------------------------------------------------------------------------------------------------------------------

    # setup the target directories and names
    SET( MODULE_NAME ${_MODULE_NAME} )
    SET( MODULE_TARGET_DIR_RELATIVE ${OW_MODULE_DIR_RELATIVE}/${MODULE_NAME} )
    SET( MODULE_TARGET_RESOURCE_DIR_RELATIVE ${OW_SHARE_DIR_RELATIVE}/modules/${MODULE_NAME} )
    SET( MODULE_TARGET_DIR ${PROJECT_BINARY_DIR}/${MODULE_TARGET_DIR_RELATIVE} )
    SET( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${MODULE_TARGET_DIR} )
    SET( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${MODULE_TARGET_DIR} )
    SET( MODULE_SOURCE_DIR ${_MODULE_SOURCE_DIR} )

    # -----------------------------------------------------------------------------------------------------------------------------------------------
    # Add sources as target
    # -----------------------------------------------------------------------------------------------------------------------------------------------

    # Collect the compile-files for this target
    COLLECT_COMPILE_FILES( "${MODULE_SOURCE_DIR}" TARGET_CPP_FILES TARGET_H_FILES TARGET_TEST_FILES )

    # Setup the target
    ADD_LIBRARY( ${MODULE_NAME} SHARED ${TARGET_CPP_FILES} ${TARGET_H_FILES} )
690 691 692 693 694 695 696 697 698 699 700 701 702 703

    # Some Linux distributions need to explicitly link against X11. We add this lib here.
    IF( CMAKE_HOST_SYSTEM MATCHES "Linux" )
        SET( ADDITIONAL_TARGET_LINK_LIBRARIES "X11" )
    ENDIF()

    TARGET_LINK_LIBRARIES( ${MODULE_NAME} ${CMAKE_STANDARD_LIBRARIES}
                                          ${OW_LIB_OPENWALNUT} 
                                          ${Boost_LIBRARIES}
                                          ${OPENGL_gl_LIBRARY}
                                          ${OPENSCENEGRAPH_LIBRARIES} 
                                          ${ADDITIONAL_TARGET_LINK_LIBRARIES} 
                                          ${_MODULE_DEPENDENCIES}
                         )
704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743

    # Set the version of the library.
    SET_TARGET_PROPERTIES( ${MODULE_NAME} PROPERTIES
        VERSION ${OW_LIB_VERSION}
        SOVERSION ${OW_SOVERSION}
    )

    # Do not forget the install targets
    SETUP_LIB_INSTALL( ${MODULE_NAME} ${MODULE_TARGET_DIR_RELATIVE} "MODULES" )

    # -----------------------------------------------------------------------------------------------------------------------------------------------
    # Test Setup
    # -----------------------------------------------------------------------------------------------------------------------------------------------

    # Setup tests of this target
    SETUP_TESTS( "${TARGET_TEST_FILES}" "${MODULE_NAME}" "${_MODULE_DEPENDENCIES}" )

    # -----------------------------------------------------------------------------------------------------------------------------------------------
    # Copy Shaders
    # -----------------------------------------------------------------------------------------------------------------------------------------------

    COLLECT_SHADER_FILES( ${MODULE_SOURCE_DIR} TARGET_GLSL_FILES )
    SETUP_SHADERS( "${TARGET_GLSL_FILES}" "${MODULE_TARGET_RESOURCE_DIR_RELATIVE}/shaders" "MODULES" )

    # -----------------------------------------------------------------------------------------------------------------------------------------------
    # Copy Additional Resources
    # -----------------------------------------------------------------------------------------------------------------------------------------------
    SETUP_RESOURCES_GENERIC( "${MODULE_SOURCE_DIR}/resources" ${MODULE_TARGET_RESOURCE_DIR_RELATIVE} "${_MODULE_NAME}" "MODULES" )

    # -----------------------------------------------------------------------------------------------------------------------------------------------
    # Style Checker
    # -----------------------------------------------------------------------------------------------------------------------------------------------

    # setup the stylechecker. Ignore the platform specific stuff.
    SETUP_STYLECHECKER( "${MODULE_NAME}"
                        "${TARGET_CPP_FILES};${TARGET_H_FILES};${TARGET_TEST_FILES};${TARGET_GLSL_FILES}"  # add all these files to the stylechecker
                        "${_MODULE_STYLE_EXCLUDES}" )                                                      # exlude some ugly files

ENDFUNCTION( SETUP_MODULE )