Browse Source

CMake: Modify install_python_package() so it keeps the build-dir copy up-to-date too

Sam Edwards 6 years ago
parent
commit
49865c103a
4 changed files with 112 additions and 42 deletions
  1. 29 23
      cmake/macros/Python.cmake
  2. 78 0
      cmake/scripts/CopyPython.cmake
  3. 4 18
      direct/CMakeLists.txt
  4. 1 1
      panda/CMakeLists.txt

+ 29 - 23
cmake/macros/Python.cmake

@@ -80,9 +80,12 @@ function(add_python_target target)
 endfunction(add_python_target)
 
 #
-# Function: install_python_package(path [ARCH/LIB] [COMPONENT component])
+# Function: install_python_package(name [SOURCE path] [ARCH/LIB] [COMPONENT component])
 #
-# Installs the Python package which was built at `path`.
+# Installs the Python package `name` (which may have its source at `path`).
+#
+# The package is copied to (or created in) the build directory so that the user
+# may import it before the install step.
 #
 # Note that this handles more than just installation; it will also invoke
 # Python's compileall utility to pregenerate .pyc/.pyo files.  This will only
@@ -96,10 +99,11 @@ endfunction(add_python_target)
 # documentation for more information on what this does).  The default is
 # "Python".
 #
-function(install_python_package path)
+function(install_python_package package_name)
   set(type "LIB")
+  unset(keyword)
   set(component "Python")
-  set(component_keyword OFF)
+  unset(src_path)
   foreach(arg ${ARGN})
     if(arg STREQUAL "ARCH")
       set(type "ARCH")
@@ -108,11 +112,18 @@ function(install_python_package path)
       set(type "LIB")
 
     elseif(arg STREQUAL "COMPONENT")
-      set(component_keyword ON)
+      set(keyword "${arg}")
 
-    elseif(component_keyword)
+    elseif(keyword STREQUAL "COMPONENT")
       set(component "${arg}")
-      set(component_keyword OFF)
+      unset(keyword)
+
+    elseif(arg STREQUAL "SOURCE")
+      set(keyword "${arg}")
+
+    elseif(keyword STREQUAL "SOURCE")
+      set(src_path "${arg}")
+      unset(keyword)
 
     else()
       message(FATAL_ERROR "install_python_package got unexpected argument: ${ARGN}")
@@ -120,26 +131,21 @@ function(install_python_package path)
     endif()
   endforeach(arg)
 
-  get_filename_component(package_name "${path}" NAME)
-  set(custom_target "bytecompile_${package_name}")
-
-  file(RELATIVE_PATH relpath "${PROJECT_BINARY_DIR}" "${path}")
+  set(path "${PROJECT_BINARY_DIR}/${package_name}")
 
+  set(args -D "OUTPUT_DIR=${path}")
+  if(src_path)
+    list(APPEND args -D "SOURCE_DIR=${src_path}")
+  endif()
   if(PYTHON_EXECUTABLE)
-    add_custom_target(${custom_target} ALL)
-    add_custom_command(
-      TARGET ${custom_target}
-      WORKING_DIRECTORY "${PROJECT_BINARY_DIR}"
-      COMMAND "${PYTHON_EXECUTABLE}" -m compileall -q "${relpath}")
-    add_custom_command(
-      TARGET ${custom_target}
-      WORKING_DIRECTORY "${PROJECT_BINARY_DIR}"
-      COMMAND "${PYTHON_EXECUTABLE}" -OO -m compileall -q "${relpath}")
+    list(APPEND args -D "PYTHON_EXECUTABLES=${PYTHON_EXECUTABLE}")
   endif()
+  add_custom_target(${package_name} ALL
+    COMMAND ${CMAKE_COMMAND}
+      ${args}
+      -P "${CMAKE_SOURCE_DIR}/cmake/scripts/CopyPython.cmake")
 
-  ensure_python_init("${path}")
-
-  set(dir ${PYTHON_${type}_INSTALL_DIR})
+  set(dir "${PYTHON_${type}_INSTALL_DIR}")
   if(dir)
     install(DIRECTORY "${path}" DESTINATION "${dir}"
       COMPONENT "${component}"

+ 78 - 0
cmake/scripts/CopyPython.cmake

@@ -0,0 +1,78 @@
+# Filename: CopyPython.cmake
+#
+# Description: When run, copies Python files from a source directory to a
+#   build directory located somewhere in the project's binary path. If
+#   PYTHON_EXECUTABLES is provided, it will also invoke compileall in the
+#   destination dir(s) to precache .pyc/.pyo files.
+#
+# Usage:
+#   This script is invoked via add_custom_target, like this:
+#   cmake -D OUTPUT_DIR="/out/dir/Release"
+#         -D SOURCE_DIR="/panda3d/direct/src/"
+#         -D PYTHON_EXECUTABLES="/usr/bin/python3.6"
+#         -P CopyPython.cmake
+#
+
+if(NOT CMAKE_SCRIPT_MODE_FILE)
+  message(FATAL_ERROR "CopyPython.cmake should not be included but run in script mode.")
+  return()
+endif()
+
+if(NOT DEFINED OUTPUT_DIR)
+  message(FATAL_ERROR "OUTPUT_DIR should be defined when running CopyPython.cmake!")
+  return()
+endif()
+
+# Ensure OUTPUT_DIR exists
+file(MAKE_DIRECTORY ${OUTPUT_DIR})
+
+# If there's a SOURCE_DIR, glob for .py files and copy
+# (this is done by hand to avoid the CMake bug where it creates empty dirs)
+if(DEFINED SOURCE_DIR)
+  file(GLOB_RECURSE py_files RELATIVE "${SOURCE_DIR}" "${SOURCE_DIR}/*.py")
+  foreach(py_file ${py_files})
+    get_filename_component(py_file_parent "${py_file}" DIRECTORY)
+    if(NOT EXISTS "${SOURCE_DIR}/${py_file_parent}/__init__.py")
+      # The source file isn't part of a Python package, even though it is a .py
+      # file. Skip it.
+      continue()
+    endif()
+
+    file(TIMESTAMP "${SOURCE_DIR}/${py_file}" src_stamp)
+    file(TIMESTAMP "${OUTPUT_DIR}/${py_file}" dst_stamp)
+
+    if(NOT src_stamp STREQUAL dst_stamp)
+      file(COPY "${SOURCE_DIR}/${py_file}" DESTINATION "${OUTPUT_DIR}/${py_file_parent}")
+      set(changed YES)
+    endif()
+
+  endforeach(py_file)
+
+else()
+  # No SOURCE_DIR; assume we're outdated since our caller might be populating
+  # the OUTPUT_DIR themselves
+  set(changed YES)
+
+endif()
+
+# Make sure Python recognizes OUTPUT_DIR as a package
+if(NOT EXISTS "${OUTPUT_DIR}/__init__.py")
+  file(WRITE "${OUTPUT_DIR}/__init__.py" "")
+  set(changed YES)
+endif()
+
+# Generate .pyc files for each Python version, if our caller wants
+if(changed AND DEFINED PYTHON_EXECUTABLES)
+  foreach(interp ${PYTHON_EXECUTABLES})
+    execute_process(
+      COMMAND "${interp}" -m compileall .
+      OUTPUT_QUIET
+      WORKING_DIRECTORY "${OUTPUT_DIR}")
+
+    execute_process(
+      COMMAND "${interp}" -OO -m compileall .
+      OUTPUT_QUIET
+      WORKING_DIRECTORY "${OUTPUT_DIR}")
+
+  endforeach(interp)
+endif()

+ 4 - 18
direct/CMakeLists.txt

@@ -42,21 +42,7 @@ if(HAVE_PYTHON)
     p3motiontrail p3showbase LINK p3direct IMPORT panda3d.core COMPONENT Direct)
 
   # Copy Python source files into the build directory
-  set(DIRECT_INSTALL_DIRECTORIES
-      actor cluster controls directbase directdevices directnotify directscripts
-      directtools directutil dist distributed extensions_native filter
-      fsm gui interval leveleditor motiontrail p3d particles physics
-      showbase showutil stdpy task tkpanels tkwidgets wxwidgets)
-
-  foreach(dir ${DIRECT_INSTALL_DIRECTORIES})
-    file(COPY
-      DIRECTORY "src/${dir}"
-      DESTINATION "${PROJECT_BINARY_DIR}/direct"
-      FILES_MATCHING PATTERN "*.py")
-  endforeach()
-
-  # Install all files
-  install_python_package("${PROJECT_BINARY_DIR}/direct" LIB COMPONENT Direct)
+  install_python_package(direct SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/src" LIB COMPONENT Direct)
 
   # This bit is to generate the 'pandac' compatibility shim. It's deprecated now,
   # but in older versions of Panda3D, one would use
@@ -64,13 +50,13 @@ if(HAVE_PYTHON)
   # instead of
   # from panda3d.FOO import *
   # Generate PandaModules:
-  file(WRITE "${PROJECT_BINARY_DIR}/pandac/PandaModules.py"
+  file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/pandac/PandaModules.py"
     "\"This module is deprecated.  Import from panda3d.core and other panda3d.* modules instead.\"
 
 print(\"Warning: pandac.PandaModules is deprecated, import from panda3d.core instead\")\n")
 
   foreach(module ${ALL_INTERROGATE_MODULES})
-    file(APPEND "${PROJECT_BINARY_DIR}/pandac/PandaModules.py" "
+    file(APPEND "${CMAKE_CURRENT_BINARY_DIR}/pandac/PandaModules.py" "
 try:
     from ${module} import *
 except ImportError as err:
@@ -80,7 +66,7 @@ except ImportError as err:
   endforeach()
 
   # Now install ourselves:
-  install_python_package("${PROJECT_BINARY_DIR}/pandac" LIB COMPONENT Direct)
+  install_python_package(pandac SOURCE "${CMAKE_CURRENT_BINARY_DIR}/pandac" LIB COMPONENT Direct)
 endif()
 
 # "Direct" component contains both the Python stuff and the non-Python stuff,

+ 1 - 1
panda/CMakeLists.txt

@@ -107,7 +107,7 @@ if(INTERROGATE_PYTHON_INTERFACE)
   add_python_module(panda3d.core ${CORE_MODULE_COMPONENTS} LINK panda)
 
   if(BUILD_SHARED_LIBS)
-    install_python_package("${PROJECT_BINARY_DIR}/panda3d" ARCH)
+    install_python_package(panda3d ARCH)
   endif()
 
   if(HAVE_BULLET)