# Top-level CMakeLists.txt file for the epa_build project that simply builds # and installs external projects configured in the subdirectories # below this one using ExternalProject_Add commands configured with # the appropriate build, test, and dependency information. # Copyright (C) 2013-2017 Alan W. Irwin # This file 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 2.1 of the License, or (at your option) any later version. # This file 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 this file; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA project(epa_build NONE) message(STATUS "CMake version = ${CMAKE_VERSION}") message(STATUS "CMAKE_SYSTEM = ${CMAKE_SYSTEM}") message(STATUS "CMAKE_GENERATOR = ${CMAKE_GENERATOR}") message(STATUS "CMAKE_INSTALL_PREFIX = ${CMAKE_INSTALL_PREFIX}") cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR) set(EPA_BASE ${CMAKE_BINARY_DIR}/epa_build) message(STATUS "EPA_BASE = ${EPA_BASE}") # We keep the variable name as EPA_BASE (to be used in the subdirectories # along with all the other variables with EPA_ suffix. But the property # name must be EP_BASE in order for ExternalProject_Add to work correctly. set_directory_properties(PROPERTIES EP_BASE ${EPA_BASE}) # Set epa_build variables to control builds in general # Test whether there is access to the MSYS platform on the PATH. Note # this is a different question than whether you are using the "MSYS # Makefiles" generator since some other generators (e.g., "MinGW # Makefiles") can have MSYS on the path so long as sh.exe is renamed # or removed from that PATH. Note, we search for the automatic # installer app, mingw-get.exe and the bash.exe shell to check # (approximately) whether we have access to the MSYS platform, and # then we test that platform (very approximately) for completeness by # making sure unzip (necessary for some build configurations to unpack # their packages) is installed (using mingw-get). find_program(MINGW_GET_EXECUTABLE mingw-get) find_program(BASH_EXECUTABLE bash) if(MINGW_GET_EXECUTABLE AND BASH_EXECUTABLE AND NOT CYGWIN) set(MSYS_PLATFORM ON) else(MINGW_GET_EXECUTABLE AND BASH_EXECUTABLE AND NOT CYGWIN) set(MSYS_PLATFORM OFF) endif(MINGW_GET_EXECUTABLE AND BASH_EXECUTABLE AND NOT CYGWIN) message(STATUS "WIN32 = ${WIN32}") message(STATUS "MINGW = ${MINGW}") message(STATUS "MSYS = ${MSYS}") message(STATUS "MSYS_PLATFORM = ${MSYS_PLATFORM}") message(STATUS "CYGWIN = ${CYGWIN}") if(WIN32 AND NOT MSYS_PLATFORM) message(FATAL_ERROR "epa_build does not work correctly on Windows without the MSYS platform. Install the MSYS platform (perhaps without sh.exe depending on CMake generator) on Windows.") endif(WIN32 AND NOT MSYS_PLATFORM) # List of executables required by epa_build CMake logic. set(executables_LIST # ExternalProject workarounds touch # Unpacking unzip # Updating mkdir cp patch # General env # Not specifically required by epa_build (yet), but some # individual project configurations will not work unless this is # available so might as well check it here. bash make python uname sed ln chmod ) foreach(executable ${executables_LIST}) string(TOUPPER ${executable} EXECUTABLE) find_program(${EXECUTABLE}_EXECUTABLE ${executable}) if(NOT ${EXECUTABLE}_EXECUTABLE) if(MSYS_PLATFORM) message(STATUS "${executable} missing from your MSYS platform. Use mingw-get to install it.") endif(MSYS_PLATFORM) message(FATAL_ERROR "${executable} must be on your PATH in order for epa_build to work correctly") endif(NOT ${EXECUTABLE}_EXECUTABLE) endforeach(executable ${executables_LIST}) # Get the Python version. execute_process( COMMAND ${PYTHON_EXECUTABLE} -c "from distutils import sysconfig; print(sysconfig.get_python_version())" OUTPUT_VARIABLE PYTHON_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE ) message(STATUS "PYTHON_VERSION = ${PYTHON_VERSION}") # Determine whether OS is 64-bits from uname -m "machine name" field. execute_process( COMMAND ${UNAME_EXECUTABLE} -m OUTPUT_VARIABLE UNAME_MACHINE OUTPUT_STRIP_TRAILING_WHITESPACE ) # Determine desired install permissions on shared objects. if(CMAKE_INSTALL_SO_NO_EXE) set(SO_NUMERICAL_PERMISSIONS 644) else(CMAKE_INSTALL_SO_NO_EXE) set(SO_NUMERICAL_PERMISSIONS 755) endif(CMAKE_INSTALL_SO_NO_EXE) # This will need refinement as more platforms tested, but it works on # Intel/AMD Linux hardware and also MinGW/MSYS on 32-bit Windows for # now. if(UNAME_MACHINE STREQUAL x86_64) set(EPA_HAVE_64_BIT_OS ON) else(UNAME_MACHINE STREQUAL x86_64) set(EPA_HAVE_64_BIT_OS OFF) endif(UNAME_MACHINE STREQUAL x86_64) # Use modified version of the CMake 2.8.12 ExternalProject module # where the tar.xz processing has been fixed. set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}) include(ExternalProject) if(MSYS_PLATFORM) # Useful function to convert Windows list of semicolon-delimited # PATHs to the equivalent list of MSYS PATHs (exactly like the # colon-delimited Unix list of PATHs except the driver letters are # specified as the initial one-character component of each of the # PATHs). For example, this function will transform the Windows # list of PATHs, "z:\path1;c:\path2" to "/z/path1:/c/path2". function(determine_msys_path MSYS_PATH NATIVE_PATH) #message(STATUS "NATIVE_PATH = ${NATIVE_PATH}") string(REGEX REPLACE "^\([a-zA-z]\):" "/\\1" PATH "${NATIVE_PATH}") string(REGEX REPLACE ";\([a-zA-z]\):" ";/\\1" PATH "${PATH}") string(REGEX REPLACE ";" ":" PATH "${PATH}") file(TO_CMAKE_PATH "${PATH}" PATH) #message(STATUS "MSYS_PATH = ${PATH}") set(${MSYS_PATH} ${PATH} PARENT_SCOPE) endfunction(determine_msys_path) endif(MSYS_PLATFORM) function(epa_boilerplate _ignored_dependencies_LIST _PACKAGE _dependencies_LIST _dependencies_targets _EPA_PATH _source_PATH ) #message(STATUS "DEBUG DEPENDENCY HANDLING: entering ${_PACKAGE} = ${${_PACKAGE}}") get_property(saved_started_subdirectories_LIST GLOBAL PROPERTY EPA_started_subdirectories_LIST) set_property(GLOBAL APPEND PROPERTY EPA_started_subdirectories_LIST ${${_PACKAGE}}) get_property(started_subdirectories_LIST GLOBAL PROPERTY EPA_started_subdirectories_LIST) # Remove dependencies that should be ignored. if(${_dependencies_LIST} AND ${_ignored_dependencies_LIST}) list(REMOVE_ITEM ${_dependencies_LIST} ${${_ignored_dependencies_LIST}}) endif(${_dependencies_LIST} AND ${_ignored_dependencies_LIST}) # Check for missing configurations and eliminate those from ${_dependencies_LIST}. foreach(build_configuration ${${_dependencies_LIST}}) if(NOT EXISTS ${CMAKE_SOURCE_DIR}/${build_configuration}/CMakeLists.txt) message(STATUS "Warning: A build_configuration for ${build_configuration} does not exist so it is assumed this dependency of ${${_PACKAGE}} has been installed on your platform by means other than epa_build.") list(REMOVE_ITEM ${_dependencies_LIST} ${build_configuration}) endif(NOT EXISTS ${CMAKE_SOURCE_DIR}/${build_configuration}/CMakeLists.txt) endforeach(build_configuration ${${_dependences_LIST}}) #message(STATUS "DEBUG DEPENDENCY HANDLING: ${_PACKAGE} = ${${_PACKAGE}} has the following list of expurgated dependencies: ${${_dependencies_LIST}}") # Initial value of finished_subdirectories_LIST required for following # loop logic to work correctly. get_property(finished_subdirectories_LIST GLOBAL PROPERTY EPA_finished_subdirectories_LIST) foreach(build_configuration ${${_dependencies_LIST}}) list(FIND started_subdirectories_LIST ${build_configuration} index) if(index GREATER -1) message(FATAL_ERROR "Circular dependency: package ${build_configuration} depends on package ${${_PACKAGE}} which depends on package ${build_configuration}.") endif(index GREATER -1) list(FIND finished_subdirectories_LIST ${build_configuration} index) if(index LESS 0) # Only if build_configuration is not in finished_subdirectories_LIST. add_subdirectory( ${CMAKE_SOURCE_DIR}/${build_configuration} ${CMAKE_BINARY_DIR}/${build_configuration} ) # GLOBAL property EPA_finished_subdirectories_LIST has the subdirectory # name appended to it for the above add_subdirectory call. # So must update finished_subdirectories_LIST # in order for the next test of build_configuration to be valid. get_property(finished_subdirectories_LIST GLOBAL PROPERTY EPA_finished_subdirectories_LIST) endif(index LESS 0) endforeach(build_configuration ${${_dependencies_LIST}}) set(${_dependencies_targets}) foreach(build_configuration ${${_dependencies_LIST}}) list(APPEND ${_dependencies_targets} build_${build_configuration}) endforeach(build_configuration ${${_dependences_LIST}}) # Data that is related to the PATH that must be used. if(MSYS_PLATFORM) determine_msys_path(${_EPA_PATH} "${${_EPA_PATH}}") # Must have all elements of env command in MSYS platform form determine_msys_path(${_source_PATH} "${EPA_BASE}/Source/build_${${_PACKAGE}}") else(MSYS_PLATFORM) set(${_source_PATH} "${EPA_BASE}/Source/build_${${_PACKAGE}}") endif(MSYS_PLATFORM) #message(STATUS "DEBUG: (modified for ${${_PACKAGE}}) ${_EPA_PATH} = ${${_EPA_PATH}}") # Update global properties consistent with a successful conclusion of this # function call. set_property(GLOBAL PROPERTY EPA_started_subdirectories_LIST ${saved_started_subdirectories_LIST}) set_property(GLOBAL APPEND PROPERTY EPA_build_targets_LIST build_${${_PACKAGE}}) set_property(GLOBAL APPEND PROPERTY EPA_finished_subdirectories_LIST ${${_PACKAGE}}) # Temporary debugging of dependencies. #get_property(finished_subdirectories_LIST GLOBAL PROPERTY EPA_finished_subdirectories_LIST) #message(STATUS "DEBUG DEPENDENCY HANDLING: finished_subdirectories_LIST = ${finished_subdirectories_LIST}") # Propagate changed output arguments to parent scope of function. set(${_dependencies_LIST} ${${_dependencies_LIST}} PARENT_SCOPE) set(${_dependencies_targets} ${${_dependencies_targets}} PARENT_SCOPE) set(${_EPA_PATH} ${${_EPA_PATH}} PARENT_SCOPE) set(${_source_PATH} ${${_source_PATH}} PARENT_SCOPE) #message(STATUS "DEBUG DEPENDENCY HANDLING: leaving ${_PACKAGE} = ${${_PACKAGE}}") endfunction(epa_boilerplate) # This gives the full pathname of the associated build tool for at # least the "Unix Makefiles", "Ninja", "MSYS Makefiles", "MinGW # Makefiles", and "NMake Makefiles JOM" CMake generators. set(EPA_BUILD_COMMAND "${CMAKE_MAKE_PROGRAM}") # Actual make programme used for autotools builds. set(EPA_MAKE_COMMAND ${MAKE_EXECUTABLE}) set(EPA_CTEST_COMMAND "${CMAKE_CTEST_COMMAND}") if(MSYS_PLATFORM) # On the MSYS platform, the env command (used to set relevant # environment variables for the commands below in the */CMakeLists.txt # scripts) requires all full paths be in the MSYS platform form. determine_msys_path(EPA_BUILD_COMMAND "${EPA_BUILD_COMMAND}") determine_msys_path(EPA_MAKE_COMMAND "${EPA_MAKE_COMMAND}") determine_msys_path(EPA_CTEST_COMMAND "${EPA_CTEST_COMMAND}") determine_msys_path(EPA_CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") determine_msys_path(MSYS_CMAKE_COMMAND "${CMAKE_COMMAND}") # Propagate the overall CMake generator and install prefix to all CMake-based # software project builds. set(EPA_CMAKE_COMMAND ${MSYS_CMAKE_COMMAND} "-G${CMAKE_GENERATOR}" -DCMAKE_INSTALL_PREFIX:PATH=${EPA_CMAKE_INSTALL_PREFIX}) # Propagate CMAKE_C_COMPILER, CMAKE_CXX_COMPILER, and # CMAKE_RC_COMPILER to all CMake-based software project builds. # (jom currently requires this as a workaround). if(CMAKE_C_COMPILER) determine_msys_path(MSYS_CMAKE_C_COMPILER "${CMAKE_C_COMPILER}") list(APPEND EPA_CMAKE_COMMAND -DCMAKE_C_COMPILER:FILEPATH=${MSYS_CMAKE_C_COMPILER}) endif(CMAKE_C_COMPILER) if(CMAKE_CXX_COMPILER) determine_msys_path(MSYS_CMAKE_CXX_COMPILER "${CMAKE_CXX_COMPILER}") list(APPEND EPA_CMAKE_COMMAND -DCMAKE_CXX_COMPILER:FILEPATH=${MSYS_CMAKE_CXX_COMPILER}) endif(CMAKE_CXX_COMPILER) if(CMAKE_RC_COMPILER) determine_msys_path(MSYS_CMAKE_RC_COMPILER "${CMAKE_RC_COMPILER}") list(APPEND EPA_CMAKE_COMMAND -DCMAKE_RC_COMPILER:FILEPATH=${MSYS_CMAKE_RC_COMPILER}) endif(CMAKE_RC_COMPILER) # No extra dropped dependencies for the MSYS_PLATFORM case. set(extra_ignored_dependencies_list) else(MSYS_PLATFORM) # Propagate the overall CMake generator and install prefix to all CMake-based # software project builds. set(EPA_CMAKE_COMMAND ${CMAKE_COMMAND} "-G${CMAKE_GENERATOR}" -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_INSTALL_PREFIX}) set(EPA_CMAKE_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}) # Drop xmlcatalog-wrapper dependency since that is only suitable for # the MSYS_PLATFORM case. set(extra_ignored_dependencies_list xmlcatalog-wrapper) endif(MSYS_PLATFORM) # Propagate the install prefix to autotools-based builds set(EPA_CONFIGURE_COMMAND configure --prefix=${EPA_CMAKE_INSTALL_PREFIX}) if(CMAKE_VERBOSE_MAKEFILE) # Not sure whether all contemplated back-ends support this or not, but # most should. list(APPEND EPA_CMAKE_COMMAND -DCMAKE_VERBOSE_MAKEFILE=ON) # Some autotools build projects give verbose make results when V=1 # and I don't think it will interfere with the rest. list(APPEND EPA_MAKE_COMMAND V=1) endif(CMAKE_VERBOSE_MAKEFILE) # These options run the PLplot comprehensive test script (for # either/both the noninteractive and interactive subsets of the tests) # for the exact same build environment (i.e., cmake options, # environment variables, buildtools that have been built, and # dependencies that have been built) as used for the epa_build of # plplot and plplot_lite. option(COMPREHENSIVE_PLPLOT_TEST_NONINTERACTIVE "Use comprehensive noninteractive test for PLplot (which requires no interaction by the user but does require substantial CPU time and ~4GB of disk space)" OFF) option(COMPREHENSIVE_PLPLOT_TEST_INTERACTIVE "Use comprehensive interactive test for PLplot (which requires interaction by the user to click through some of the tests)" OFF) # The parallel versions are for software packages that # do not have race conditions for parallel builds or tests. set(NUMBER_PARALLEL_JOBS 4 CACHE STRING "Number of parallel jobs") set(EPA_PARALLEL_BUILD_COMMAND "${EPA_BUILD_COMMAND}" -j${NUMBER_PARALLEL_JOBS}) set(EPA_PARALLEL_CTEST_COMMAND "${EPA_CTEST_COMMAND}" -j${NUMBER_PARALLEL_JOBS}) # For autotools based builds. option(AUTOTOOLS_PARALLEL_BUILD "Build autotools projects using parallel make unless a specific project is expressely configured otherwise" ON) if(AUTOTOOLS_PARALLEL_BUILD) set(EPA_PARALLEL_MAKE_COMMAND "${EPA_MAKE_COMMAND}" -j${NUMBER_PARALLEL_JOBS}) else(AUTOTOOLS_PARALLEL_BUILD) set(EPA_PARALLEL_MAKE_COMMAND "${EPA_MAKE_COMMAND}") endif(AUTOTOOLS_PARALLEL_BUILD) message(STATUS "EPA_CMAKE_COMMAND = ${EPA_CMAKE_COMMAND}") message(STATUS "EPA_CONFIGURE_COMMAND = ${EPA_CONFIGURE_COMMAND}") message(STATUS "EPA_BUILD_COMMAND = ${EPA_BUILD_COMMAND}") message(STATUS "EPA_PARALLEL_BUILD_COMMAND = ${EPA_PARALLEL_BUILD_COMMAND}") message(STATUS "EPA_MAKE_COMMAND = ${EPA_MAKE_COMMAND}") message(STATUS "EPA_PARALLEL_MAKE_COMMAND = ${EPA_PARALLEL_MAKE_COMMAND}") message(STATUS "EPA_CTEST_COMMAND = ${EPA_CTEST_COMMAND}") message(STATUS "EPA_PARALLEL_CTEST_COMMAND = ${EPA_PARALLEL_CTEST_COMMAND}") # Put ${CMAKE_INSTALL_PREFIX}/bin on the PATH as well for those cases # where some executable built and installed by epa_build needs # to be found by another project being configured by epa_build. if(MSYS_PLATFORM) set(BP_ORIGINAL_NATIVE_PATH "$ENV{PATH};${CMAKE_INSTALL_PREFIX}/bin") else(MSYS_PLATFORM) set(BP_ORIGINAL_NATIVE_PATH "$ENV{PATH}:${CMAKE_INSTALL_PREFIX}/bin") endif(MSYS_PLATFORM) set(EPA_PATH "${BP_ORIGINAL_NATIVE_PATH}") # This option not used currently, but instead of removing it, comment # it out because it might be useful later if we ever configure a project # where we want a choice between an ordinary download and repository access. # option(PREFER_DOWNLOAD "Prefer to obtain source code for projects using a simple download of tarball rather than via svn, git, hg, or bzr repository access" ON) # Now include the build configurations for certain targets and their # dependencies. option(BUILD_THE_BUILDTOOLS "Build the buildtools (such as cmake) used for the rest of the builds" OFF) if(BUILD_THE_BUILDTOOLS) set(executables_LIST sh ) foreach(executable ${executables_LIST}) string(TOUPPER ${executable} EXECUTABLE) find_program(${EXECUTABLE}_EXECUTABLE ${executable}) if(NOT ${EXECUTABLE}_EXECUTABLE) if(MSYS_PLATFORM) message(STATUS "${executable} missing from your MSYS platform. If sh is missing, use a generator other than 'MinGW Makefiles'. If something else is missing use mingw-get to install it.") endif(MSYS_PLATFORM) message(FATAL_ERROR "${executable} must be on your PATH in order for epa_build to work correctly") endif(NOT ${EXECUTABLE}_EXECUTABLE) endforeach(executable ${executables_LIST}) # List sufficient buildtool configurations so they will suck in # the remaining buildtool configurations via dependencies. # Order does not matter because the dependency logic takes # care of any ordering issues. set(subdirectories_LIST pkg-config ##subversion swig tk # itcl version 4 is integrated into tcl # itk version 4 is not integrated into tk (or tcl). itk # iwidgets (version 4.1.0) iwidgets # itcl version 3 is an independent project itcl3 # itk version 3 is an independent project itk3 # iwidgets (version 4.0.1) iwidgets4.0 ) if(NOT MSYS_PLATFORM) # Required system version of libcurl, an essential cmake build # dependency, is not currently (until the epa_build project # configures that build) available for the MSYS_PLATFORM. So only # allow the potential to build the cmake buildtool for the # non-MSYS platform case. And for the MSYS platform case use a # binary version of CMake supplied by Kitware. list(APPEND subdirectories_LIST cmake) # epa_build of lapack_blas and valgrind not tested on MSYS_PLATFORM yet # so add them here for the time being. list(APPEND subdirectories_LIST lapack_blas valgrind) endif(NOT MSYS_PLATFORM) else(BUILD_THE_BUILDTOOLS) # List sufficient normal package configurations so they will suck in # the remaining configurations via dependencies. # Order does not matter because the dependency logic takes # care of any ordering issues. set(subdirectories_LIST ndiff uncrustify plplot plplot_lite gtk+ libLASi wxwidgets # qt4_lite install interferes with qt5_lite build and vice versa. # Therefore, we have commented out the build configuration of # qt4_lite and have chosen to use the build_configuration of # qt5_lite instead. # qt4_lite qt5_lite freetype ) if(NOT MSYS_PLATFORM) # octave_lite install depends on lapack_blas install with # build tools but that only available for the current case: list(APPEND subdirectories_LIST octave_lite) endif(NOT MSYS_PLATFORM) endif(BUILD_THE_BUILDTOOLS) set_property(GLOBAL PROPERTY EPA_started_subdirectories_LIST) set_property(GLOBAL PROPERTY EPA_finished_subdirectories_LIST) set_property(GLOBAL PROPERTY EPA_build_targets_LIST) #message(STATUS "DEBUG DEPENDENCY HANDLING (for outer subdirectories loop): subdirectories_LIST = ${subdirectories_LIST}") # Using a while loop seems obvious here # because each add_subdirectory command can generate others # so that several elements of the subdirectories list could be knocked # off per loop iteration. However, that is not a good idea in # practice because it turns out that fatal CMake errors don't stop the # loop (which I presume is a bug) so it just continues indefinitely. Ugh! # So use foreach RANGE loop instead with maximum number of loop # interations equal to LENGTH_subdirectories_LIST and break out of it # early if necessary if the subdirectories_LIST has become exhausted. list(LENGTH subdirectories_LIST LENGTH_subdirectories_LIST) foreach(idummy RANGE ${LENGTH_subdirectories_LIST}) list(GET subdirectories_LIST 0 subdirectory) # This uses EPA_started_subdirectories_LIST internally to check for # circular dependencies, and updates EPA_finished_subdirectories_LIST # and EPA_build_targets_LIST that are used here. add_subdirectory(${subdirectory}) get_property(finished_subdirectories_LIST GLOBAL PROPERTY EPA_finished_subdirectories_LIST) list(REMOVE_ITEM subdirectories_LIST ${finished_subdirectories_LIST}) # message(STATUS "DEBUG DEPENDENCY HANDLING (for outer subdirectories loop): visited ${subdirectory}") # message(STATUS "DEBUG DEPENDENCY HANDLING (for outer subdirectories loop): subdirectories_LIST = ${subdirectories_LIST}") list(LENGTH subdirectories_LIST LENGTH_subdirectories_LIST) if(LENGTH_subdirectories_LIST EQUAL 0) break() endif(LENGTH_subdirectories_LIST EQUAL 0) endforeach(idummy RANGE ${LENGTH_subdirectories_LIST}) add_custom_target(build_all) get_property(build_targets_LIST GLOBAL PROPERTY EPA_build_targets_LIST) message(STATUS "The target build_all has the following dependent targets: ${build_targets_LIST}") add_dependencies(build_all ${build_targets_LIST})