diff --git a/CMakeLists.txt b/CMakeLists.txt index a73098190c..6e17563d4a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -172,8 +172,6 @@ except ImportError as err: ") endforeach() - file(WRITE "${PROJECT_BINARY_DIR}/pandac/__init__.py" "") - # Now install ourselves: install_python_package("${PROJECT_BINARY_DIR}/pandac" LIB) endif() diff --git a/cmake/macros/Interrogate.cmake b/cmake/macros/Interrogate.cmake index 291648e72d..84f4f9cc7e 100644 --- a/cmake/macros/Interrogate.cmake +++ b/cmake/macros/Interrogate.cmake @@ -302,10 +302,6 @@ endfunction(add_python_module) 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 # Python directory. install_python_package("${PROJECT_BINARY_DIR}/panda3d") diff --git a/cmake/macros/Python.cmake b/cmake/macros/Python.cmake index 0ede7c1c7e..524caae38e 100644 --- a/cmake/macros/Python.cmake +++ b/cmake/macros/Python.cmake @@ -6,6 +6,7 @@ # Functions: # add_python_target(target [source1 [source2 ...]]) # 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) 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) # @@ -90,6 +97,8 @@ function(install_python_package path) COMMAND "${PYTHON_EXECUTABLE}" -OO -m compileall -q "${relpath}") endif() + ensure_python_init("${path}") + set(dir ${PYTHON_${type}_INSTALL_DIR}) if(dir) install(DIRECTORY "${path}" DESTINATION "${dir}" @@ -97,3 +106,114 @@ function(install_python_package path) endif() 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) diff --git a/direct/CMakeLists.txt b/direct/CMakeLists.txt index b3cb42c78a..a1568a527c 100644 --- a/direct/CMakeLists.txt +++ b/direct/CMakeLists.txt @@ -47,9 +47,6 @@ if(HAVE_PYTHON) FILES_MATCHING PATTERN "*.py") 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_python_package("${PROJECT_BINARY_DIR}/direct" LIB) endif()