Browse Source

CMake: Move __init__.py generation to Python.cmake

This also allows us to generate a suitable __init__.py for fixing
up PATH and/or __path__ on platforms that need it to properly
import Python extension modules.
Sam Edwards 7 years ago
parent
commit
53920e7aee
4 changed files with 120 additions and 9 deletions
  1. 0 2
      CMakeLists.txt
  2. 0 4
      cmake/macros/Interrogate.cmake
  3. 120 0
      cmake/macros/Python.cmake
  4. 0 3
      direct/CMakeLists.txt

+ 0 - 2
CMakeLists.txt

@@ -172,8 +172,6 @@ except ImportError as err:
 ")
 ")
   endforeach()
   endforeach()
 
 
-  file(WRITE "${PROJECT_BINARY_DIR}/pandac/__init__.py" "")
-
   # Now install ourselves:
   # Now install ourselves:
   install_python_package("${PROJECT_BINARY_DIR}/pandac" LIB)
   install_python_package("${PROJECT_BINARY_DIR}/pandac" LIB)
 endif()
 endif()

+ 0 - 4
cmake/macros/Interrogate.cmake

@@ -302,10 +302,6 @@ endfunction(add_python_module)
 
 
 
 
 if(INTERROGATE_PYTHON_INTERFACE AND BUILD_SHARED_LIBS)
 if(INTERROGATE_PYTHON_INTERFACE AND BUILD_SHARED_LIBS)
-  # We have to create an __init__.py so that Python 2.x can recognize 'panda3d'
-  # as a package.
-  file(WRITE "${PROJECT_BINARY_DIR}/panda3d/__init__.py" "")
-
   # The Interrogate path needs to be installed to the architecture-dependent
   # The Interrogate path needs to be installed to the architecture-dependent
   # Python directory.
   # Python directory.
   install_python_package("${PROJECT_BINARY_DIR}/panda3d")
   install_python_package("${PROJECT_BINARY_DIR}/panda3d")

+ 120 - 0
cmake/macros/Python.cmake

@@ -6,6 +6,7 @@
 # Functions:
 # Functions:
 #   add_python_target(target [source1 [source2 ...]])
 #   add_python_target(target [source1 [source2 ...]])
 #   install_python_package(path [ARCH/LIB])
 #   install_python_package(path [ARCH/LIB])
+#   ensure_python_init(path [ARCH] [ROOT] [OVERWRITE])
 #
 #
 
 
 #
 #
@@ -47,6 +48,12 @@ function(add_python_target target)
     install(TARGETS ${target} DESTINATION lib)
     install(TARGETS ${target} DESTINATION lib)
   endif()
   endif()
 
 
+  set(keywords OVERWRITE ARCH)
+  if(NOT underscore_namespace MATCHES ".*_.*")
+    list(APPEND keywords ROOT)
+  endif()
+  ensure_python_init("${PROJECT_BINARY_DIR}/${slash_namespace}" ${keywords})
+
 endfunction(add_python_target)
 endfunction(add_python_target)
 
 
 #
 #
@@ -90,6 +97,8 @@ function(install_python_package path)
       COMMAND "${PYTHON_EXECUTABLE}" -OO -m compileall -q "${relpath}")
       COMMAND "${PYTHON_EXECUTABLE}" -OO -m compileall -q "${relpath}")
   endif()
   endif()
 
 
+  ensure_python_init("${path}")
+
   set(dir ${PYTHON_${type}_INSTALL_DIR})
   set(dir ${PYTHON_${type}_INSTALL_DIR})
   if(dir)
   if(dir)
     install(DIRECTORY "${path}" DESTINATION "${dir}"
     install(DIRECTORY "${path}" DESTINATION "${dir}"
@@ -97,3 +106,114 @@ function(install_python_package path)
   endif()
   endif()
 
 
 endfunction(install_python_package)
 endfunction(install_python_package)
+
+#
+# Function: ensure_python_init(path [ARCH] [ROOT] [OVERWRITE])
+#
+# Makes sure that the directory - at `path` - contains a file named
+# '__init__.py', which is necessary for Python to recognize the directory as a
+# package.
+#
+# ARCH, if specified, means that this is a binary package, and the build tree
+# might contain configuration-specific subdirectories.  The __init__.py will be
+# generated with a function that ensures that the appropriate configuration
+# subdirectory is in the path.
+#
+# ROOT, if specified, means that the directory may sit directly adjacent to a
+# 'bin' directory, which should be added to the DLL search path on Windows.
+#
+# OVERWRITE causes the __init__.py file to be overwritten if one is already
+# present.
+#
+function(ensure_python_init path)
+  set(arch OFF)
+  set(root OFF)
+  set(overwrite OFF)
+
+  foreach(arg ${ARGN})
+    if(arg STREQUAL "ARCH")
+      set(arch ON)
+    elseif(arg STREQUAL "ROOT")
+      set(root ON)
+    elseif(arg STREQUAL "OVERWRITE")
+      set(overwrite ON)
+    else()
+      message(FATAL_ERROR "ensure_python_init got unexpected argument: ${arg}")
+    endif()
+  endforeach(arg)
+
+  set(init_filename "${path}/__init__.py")
+  if(EXISTS "${init_filename}" AND NOT overwrite)
+    return()
+  endif()
+
+  file(WRITE "${init_filename}" "")
+
+  if(arch AND NOT "${CMAKE_CFG_INTDIR}" STREQUAL ".")
+    # ARCH set, and this is a multi-configuration generator
+
+    set(configs "${CMAKE_CONFIGURATION_TYPES}")
+
+    # Debug should be at the end (highest preference)
+    list(REMOVE_ITEM configs "Debug")
+    list(APPEND configs "Debug")
+
+    string(REPLACE ";" "', '" configs "${configs}")
+
+    file(APPEND "${init_filename}" "
+def _fixup_path():
+    try:
+        path = __path__[0]
+    except (NameError, IndexError):
+        return # Not a package, or not on filesystem
+
+    import os
+    abspath = os.path.abspath(path)
+
+    newpath = None
+    for config in ['${configs}']:
+        cfgpath = os.path.join(abspath, config)
+        if not os.path.isdir(cfgpath):
+            continue
+
+        newpath = cfgpath
+
+        if config.lower() == os.environ.get('CMAKE_CONFIGURATION', '').lower():
+            break
+
+    if newpath:
+        __path__.insert(0, newpath)
+
+_fixup_path()
+del _fixup_path
+")
+  endif()
+
+  if(root AND WIN32 AND NOT CYGWIN)
+    # ROOT set, and this is Windows
+
+    file(APPEND "${init_filename}" "
+def _fixup_dlls():
+    try:
+        path = __path__[0]
+    except (NameError, IndexError):
+        return # Not a package, or not on filesystem
+
+    import os
+
+    relpath = os.path.relpath(path, __path__[-1])
+    dll_path = os.path.abspath(os.path.join(__path__[-1], '../bin', relpath))
+    if not os.path.isdir(dll_path):
+        return
+
+    os_path = os.environ.get('PATH', '')
+    os_path = os_path.split(os.pathsep) if os_path else []
+    os_path.insert(0, dll_path)
+    os.environ['PATH'] = os.pathsep.join(os_path)
+
+_fixup_dlls()
+del _fixup_dlls
+")
+  endif()
+
+endfunction(ensure_python_init)

+ 0 - 3
direct/CMakeLists.txt

@@ -47,9 +47,6 @@ if(HAVE_PYTHON)
       FILES_MATCHING PATTERN "*.py")
       FILES_MATCHING PATTERN "*.py")
   endforeach()
   endforeach()
 
 
-  # We also need an __init__.py file, which we have to generate:
-  file(WRITE "${PROJECT_BINARY_DIR}/direct/__init__.py" "")
-
   # Install all files
   # Install all files
   install_python_package("${PROJECT_BINARY_DIR}/direct" LIB)
   install_python_package("${PROJECT_BINARY_DIR}/direct" LIB)
 endif()
 endif()