Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
PythonInstall.cmake 6.04 KiB
# - Install Python source files.
#  python_install(source1..sourceN DESTINATION install_dir)
# Install Python source files and byte-compile them in the directory
# ${PYTHON_INSTALL_DIR}/${install_dir}.

# Copyright (C) 2008-2009
# ASTRON (Netherlands Foundation for Research in Astronomy)
# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, softwaresupport@astron.nl
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# $Id$

# Search for the Python interpreter.
find_package(PythonInterp 3.4 REQUIRED)

# Derive the Python site-packages installation directory and build directory.
if(PYTHON_EXECUTABLE)
  set(_cmd
    "from distutils.sysconfig import get_python_lib"
    "from os.path import join"
    "print(join(
       get_python_lib(plat_specific=True, standard_lib=True, prefix=''), 
       'site-packages'))"
  )
  execute_process(
    COMMAND "${PYTHON_EXECUTABLE}" "-c" "${_cmd}"
    RESULT_VARIABLE _pyresultcode
    OUTPUT_VARIABLE _pydir
    ERROR_VARIABLE _pyerr
    OUTPUT_STRIP_TRAILING_WHITESPACE)
  if(NOT ${_pyresultcode} EQUAL 0)
    message(FATAL_ERROR "Python command failed:\n${_pyerr}")
  endif()
  
  if(NOT DEFINED PYTHON_BUILD_DIR)
    set(_PRINT_PYTHON_DIRS TRUE)
  endif()
  
  set(PYTHON_BUILD_DIR "${CMAKE_BINARY_DIR}/${_pydir}" CACHE PATH 
    "Build directory for Python extensions" FORCE)
  set(PYTHON_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${_pydir}" CACHE PATH 
    "Installation directory for Python extensions" FORCE)

  if(_PRINT_PYTHON_DIRS)
    message(STATUS "Build directory for Python extensions:        ${PYTHON_BUILD_DIR}")
    message(STATUS "Installation directory for Python extensions: ${PYTHON_INSTALL_DIR}")
  endif()
endif(PYTHON_EXECUTABLE)


#
# macro python_install
#
macro(python_install)

  # Precondition check.
  if(NOT PYTHON_EXECUTABLE)
    message(FATAL_ERROR "python_install: Python interpreter not available")
  endif(NOT PYTHON_EXECUTABLE)

  # Parse arguments.
  # apart from the python files list, there are two additional arguments
  # DESTINATION (required), where to put the py files (relative to python lib dir)
  # EXECUTABLE (optional), makes the py files executable
  string(REGEX REPLACE ";?DESTINATION.*" "" _py_files "${ARGN}")
  string(REGEX REPLACE ";?EXECUTABLE.*" "" _py_files "${_py_files}")
  string(REGEX MATCH "DESTINATION;.*" _dest_dir "${ARGN}")
  string(REGEX REPLACE "^DESTINATION;" "" _dest_dir "${_dest_dir}")
  string(REGEX REPLACE ";?EXECUTABLE.*" "" _dest_dir "${_dest_dir}")
  string(REGEX MATCH "EXECUTABLE;" _executable "${ARGN}")

  #check if optional argument EXECUTABLE is set
  #if so, then install the _py_files as EXECUTABLE type (executable)
  #else as normal files (not executable)
  if("${_executable}" STRGREATER "")
    set(INSTALL_TYPE PROGRAMS)
  else()
    set(INSTALL_TYPE FILES)
  endif("${_executable}" STRGREATER "")

  if(_py_files MATCHES "^$")
    message(FATAL_ERROR "python_install: no sources files specified")
  endif(_py_files MATCHES "^$")
  if(_dest_dir MATCHES "^$" OR _dest_dir MATCHES ";")
    message(FATAL_ERROR "python_install: destination directory invalid")
  endif(_dest_dir MATCHES "^$" OR _dest_dir MATCHES ";")

  # Set python package build/install directory.
  set(_inst_dir "${PYTHON_INSTALL_DIR}/${_dest_dir}")
  set(_build_dir "${PYTHON_BUILD_DIR}/${_dest_dir}")

  string(TOLOWER ${PACKAGE_NAME} lower_package_name)

  # Install and byte-compile each Python file.
  foreach(_py ${_py_files})
    get_filename_component(_py_path ${_py} PATH)
    get_filename_component(_py_abs ${_py} ABSOLUTE)
    
    # check if _py is a path in CMAKE_BINARY_DIR. If so, then it is most likely a configured_file. 
    # then strip the CMAKE_CURRENT_BINARY_DIR prefix.
    if(${_py} MATCHES "^(${CMAKE_CURRENT_BINARY_DIR})")
      string(REGEX REPLACE "^(${CMAKE_CURRENT_BINARY_DIR}/)" "" _py "${_py}")
      get_filename_component(_py_path ${_py} PATH)
    endif()

    # Create a symlink to each Python file; needed to mimic install tree.
    file(MAKE_DIRECTORY ${_build_dir}/${_py_path})
    execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink
      ${_py_abs} ${_build_dir}/${_py})
    install(${INSTALL_TYPE} ${_py_abs}
            DESTINATION ${_inst_dir}/${_py_path}
            COMPONENT ${lower_package_name})
    if(USE_PYTHON_COMPILATION)
      set(_py_code
        "import py_compile, os"
        "destdir = os.environ.get('DESTDIR','')"
        "print('-- Byte-compiling: %s${_inst_dir}/${_py}' % destdir)"
        "py_compile.compile('%s${DESTDIR}${_inst_dir}/${_py}' % destdir, doraise=True)")
      install(CODE 
        "execute_process(COMMAND ${PYTHON_EXECUTABLE} -c \"${_py_code}\"
                         RESULT_VARIABLE _result)
        if(NOT _result EQUAL 0)
          message(FATAL_ERROR \"Byte-compilation FAILED: \$ENV{DESTDIR}${_inst_dir}/${_py}\")
        endif(NOT _result EQUAL 0)"
        COMPONENT ${lower_package_name})
    endif(USE_PYTHON_COMPILATION)
  endforeach(_py ${_py_files})

  # Make sure that there's a __init__.py file in each build/install directory.
  string(REGEX REPLACE "/" ";" _dir_list ${_dest_dir})
  set(_init_dir)
  foreach(_dir ${_dir_list})
    set(_init_dir "${_init_dir}/${_dir}")
    execute_process(COMMAND ${CMAKE_COMMAND} -E touch
      "${PYTHON_BUILD_DIR}${_init_dir}/__init__.py")
    install(CODE 
      "execute_process(COMMAND ${CMAKE_COMMAND} -E touch 
        \"\$ENV{DESTDIR}${PYTHON_INSTALL_DIR}${_init_dir}/__init__.py\")"
        COMPONENT ${lower_package_name})
  endforeach(_dir ${_dir_list})

endmacro(python_install)