Browse Source

CMake: Restructure multi-configuration binary directory layout

This changes the structure to use paths like `build/Release/lib`
instead of `build/lib/Release`, so that the `build/Release` directory
more closely mimics the structure of `build` when in single-configuration
mode.
Sam Edwards 6 years ago
parent
commit
059c78bade

+ 9 - 4
CMakeLists.txt

@@ -34,9 +34,6 @@ include(Interrogate)        # Defines target_interrogate AND add_python_module
 include(RunPzip)            # Defines run_pzip function
 include(Versioning)         # Hooks 'add_library' to apply VERSION/SOVERSION
 
-# Add the include path for source and header files generated by CMake
-include_directories("${PROJECT_BINARY_DIR}/include/${CMAKE_CFG_INTDIR}")
-
 # Determine which trees to build.
 option(BUILD_DTOOL  "Build the dtool source tree." ON)
 option(BUILD_PANDA  "Build the panda source tree." ON)
@@ -90,7 +87,15 @@ if(INTERROGATE_PYTHON_INTERFACE)
   # for pytest before adding this test.  If the user doesn't have pytest, we'd
   # like for the tests to fail.
 
-  add_test(pytest "${PYTHON_EXECUTABLE}" -m pytest "${PROJECT_SOURCE_DIR}/tests")
+  if(CMAKE_CFG_INTDIR STREQUAL ".")
+    set(_workdir "${PROJECT_BINARY_DIR}")
+  else()
+    set(_workdir "${PROJECT_BINARY_DIR}/$<CONFIG>")
+  endif()
+
+  add_test(NAME pytest
+    COMMAND "${PYTHON_EXECUTABLE}" -m pytest "${PROJECT_SOURCE_DIR}/tests"
+    WORKING_DIRECTORY "${_workdir}")
 endif()
 
 # Generate the Panda3DConfig.cmake file so find_package(Panda3D) works, and

+ 17 - 126
cmake/macros/Python.cmake

@@ -6,7 +6,6 @@
 # Functions:
 #   add_python_target(target [source1 [source2 ...]])
 #   install_python_package(path [ARCH/LIB])
-#   ensure_python_init(path [ARCH] [ROOT] [OVERWRITE])
 #
 
 #
@@ -52,12 +51,21 @@ function(add_python_target target)
   target_link_libraries(${target} PKG::PYTHON)
 
   if(BUILD_SHARED_LIBS)
+    set(_outdir "${PROJECT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${slash_namespace}")
+
     set_target_properties(${target} PROPERTIES
-      LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/${slash_namespace}"
+      LIBRARY_OUTPUT_DIRECTORY "${_outdir}"
       OUTPUT_NAME "${basename}"
       PREFIX ""
       SUFFIX "${PYTHON_EXTENSION_SUFFIX}")
 
+    # This is explained over in CompilerFlags.cmake
+    foreach(_config ${CMAKE_CONFIGURATION_TYPES})
+      string(TOUPPER "${_config}" _config)
+      set_target_properties(${target} PROPERTIES
+        LIBRARY_OUTPUT_DIRECTORY_${_config} "${_outdir}")
+    endforeach(_config)
+
     if(PYTHON_ARCH_INSTALL_DIR)
       install(TARGETS ${target} EXPORT "${export}" COMPONENT "${component}" DESTINATION "${PYTHON_ARCH_INSTALL_DIR}/${slash_namespace}")
     endif()
@@ -71,12 +79,6 @@ function(add_python_target target)
 
   endif()
 
-  set(keywords OVERWRITE ARCH)
-  if(NOT slash_namespace MATCHES ".*/.*")
-    list(APPEND keywords ROOT)
-  endif()
-  ensure_python_init("${PROJECT_BINARY_DIR}/${slash_namespace}" ${keywords})
-
 endfunction(add_python_target)
 
 #
@@ -131,7 +133,13 @@ function(install_python_package package_name)
     endif()
   endforeach(arg)
 
-  set(path "${PROJECT_BINARY_DIR}/${package_name}")
+  if(NOT DEFINED src_path AND type STREQUAL "ARCH" AND WIN32 AND NOT CYGWIN)
+    # Win32 needs a special fixup so the DLLs in "bin" can be on the path;
+    # let's set src_path to the directory containing our fixup __init__.py
+    set(src_path "${CMAKE_SOURCE_DIR}/cmake/templates/win32_python")
+  endif()
+
+  set(path "${PROJECT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${package_name}")
 
   set(args -D "OUTPUT_DIR=${path}")
   if(src_path)
@@ -153,120 +161,3 @@ function(install_python_package package_name)
   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)

+ 20 - 0
cmake/templates/win32_python/__init__.py

@@ -0,0 +1,20 @@
+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

+ 21 - 4
dtool/CompilerFlags.cmake

@@ -29,9 +29,9 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
 
 # Set up the output directory structure, mimicking that of makepanda
 set(CMAKE_BINARY_DIR "${CMAKE_BINARY_DIR}/cmake")
-set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin")
-set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib")
-set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib")
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/bin")
+set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib")
+set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib")
 set(MODULE_DESTINATION "lib")
 
 # Runtime code assumes that dynamic modules have a "lib" prefix; Windows
@@ -44,12 +44,29 @@ if(WIN32)
   set(CMAKE_STATIC_LIBRARY_PREFIX "lib")
 
   # On Windows, modules (DLLs) are located in bin; lib is just for .lib files
-  set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin")
+  set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/bin")
   if(BUILD_SHARED_LIBS)
     set(MODULE_DESTINATION "bin")
   endif()
 endif()
 
+# Since we're using CMAKE_CFG_INTDIR to put everything in a
+# configuration-specific subdirectory when building on a multi-config
+# generator, we need to suppress the usual configuration name appending
+# behavior of CMake. In CMake 3.4+, it will suppress this behavior
+# automatically if the *_OUTPUT_DIRECTORY property contains a generator
+# expresssion, but:
+# a) As of this writing we support as early as CMake 3.0.2
+# b) ${CMAKE_CFG_INTDIR} doesn't actually expand to a generator expression
+#
+# So, to solve both of these, let's just do this:
+foreach(_type RUNTIME ARCHIVE LIBRARY)
+  foreach(_config ${CMAKE_CONFIGURATION_TYPES})
+    string(TOUPPER "${_config}" _config)
+    set(CMAKE_${_type}_OUTPUT_DIRECTORY_${_config} "${CMAKE_${_type}_OUTPUT_DIRECTORY}")
+  endforeach(_config)
+endforeach(_type)
+
 # Set warning levels
 if(MSVC)
   string(APPEND CMAKE_C_FLAGS " /W3")

+ 2 - 2
dtool/LocalSetup.cmake

@@ -193,8 +193,8 @@ else()
   set(intdir "${CMAKE_BUILD_TYPE}")
 endif()
 
-configure_file(dtool_config.h.in "${PROJECT_BINARY_DIR}/include/${intdir}/dtool_config.h")
-install(FILES "${PROJECT_BINARY_DIR}/include/${intdir}/dtool_config.h"
+configure_file(dtool_config.h.in "${PROJECT_BINARY_DIR}/${intdir}/include/dtool_config.h")
+install(FILES "${PROJECT_BINARY_DIR}/${intdir}/include/dtool_config.h"
   COMPONENT CoreDevel
   DESTINATION include/panda3d)
 

+ 1 - 1
dtool/src/dtoolbase/CMakeLists.txt

@@ -91,7 +91,7 @@ add_component_library(p3dtoolbase NOINIT SYMBOL BUILDING_DTOOL_DTOOLBASE
 # Help other libraries find the autogenerated headers
 target_include_directories(p3dtoolbase PUBLIC
   $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include/${CMAKE_CFG_INTDIR}>)
+  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/${CMAKE_CFG_INTDIR}>/include)
 target_link_libraries(p3dtoolbase PKG::EIGEN PKG::THREADS)
 target_interrogate(p3dtoolbase ${P3DTOOLBASE_SOURCES} EXTENSIONS ${P3DTOOLBASE_IGATEEXT})