├── .gitignore
├── .travis.yml
├── CMakeLists.txt
├── LICENSE
├── README.md
├── __init__.py
├── build.py
├── config.ini
├── prefab.gitignore
├── scripts
├── __init__.py
├── common.py
├── finalize.py
├── interrogate.py
└── setup.py
└── source
├── config_module.cpp
└── config_module.h
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyd
2 | *.pyc
3 | *.so
4 | *.pdb
5 |
6 | # Various output names
7 | win_*/
8 | linux_*/
9 | osx_*/
10 | Source/Interrogate*
11 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: cpp
2 | sudo: required
3 | dist: trusty
4 | compiler: gcc
5 | addons:
6 | apt:
7 | sources:
8 | - sourceline: "deb http://archive.panda3d.org/ubuntu/ trusty-dev main"
9 | packages:
10 | - cmake
11 | - libeigen3-dev
12 | - libfreetype6-dev
13 | - libbullet-dev
14 | - panda3d1.10
15 |
16 | script:
17 | - export PYTHONPATH=${PYTHONPATH}:/usr/lib/python2.7/dist-packages
18 | - export PYTHONPATH=${PYTHONPATH}:/usr/share/panda3d
19 | - echo -e "\nmodule_name=CI_TEST\n" >> config.ini
20 | - python2.7 build.py
21 | - python2.7 -c "import panda3d; import CI_TEST; print(dir(CI_TEST))"
22 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 |
2 | set(PROJECT_NAME CACHE STRING "Module")
3 |
4 | cmake_minimum_required (VERSION 2.6)
5 | project (${PROJECT_NAME})
6 |
7 | # Project configuration
8 | set(CMAKE_BUILD_TYPE "Release")
9 |
10 | # Libraries and options set by the module builder
11 | set(HAVE_LIB_EIGEN CACHE BOOL TRUE)
12 | set(HAVE_LIB_BULLET CACHE BOOL FALSE)
13 | set(HAVE_LIB_FREETYPE CACHE BOOL FALSE)
14 | set(OPTIMIZE CACHE STRING "3")
15 | set(IGATE_VERBOSE CACHE STRING "0")
16 | set(INTERROGATE_LIB CACHE STRING "p3interrogatedb")
17 | set(PYTHON_EXECUTABLE CACHE STRING "python")
18 | set(THIRDPARTY_WIN_DIR CACHE STRING "")
19 |
20 |
21 | # --- User controllable variables ---
22 |
23 | # Whether to enable SSE. This only has effect on 32 bit.
24 | # Should probably match the setting for the Panda3D build
25 | set(ENABLE_SSE2 CACHE BOOL 1)
26 | set(TOUCHINPUT_ENABLED CACHE BOOL 0)
27 |
28 |
29 | # --- End of user variables --
30 |
31 | # Reset cmake flags - we user our own
32 | set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "")
33 | set(CMAKE_CXX_FLAGS_RELEASE "")
34 | set(CMAKE_CXX_FLAGS_MINSIZEREL "")
35 |
36 |
37 | set(LIBRARIES "")
38 |
39 | # Windows - 32 and 64 bit
40 | if (WIN32)
41 | if (CMAKE_CL_64 STREQUAL "1")
42 | message(STATUS "Bitness: 64 bit ('${CMAKE_CL_64}')")
43 | set(IS_64_BIT TRUE)
44 | set(IS_32_BIT FALSE)
45 | else()
46 | message(STATUS "Bitness: 32 bit ('${CMAKE_CL_64}')")
47 | set(IS_64_BIT TRUE)
48 | set(IS_32_BIT FALSE)
49 | endif()
50 |
51 | set(PYTHONVER CACHE STRING "27")
52 |
53 | # Find panda path
54 | execute_process(
55 | COMMAND "${PYTHON_EXECUTABLE}" "-B" "scripts/common.py" "--print-sdk-path"
56 | OUTPUT_VARIABLE WIN_PANDA_PATH
57 | WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
58 | message(STATUS "Detected panda3d installation: ${WIN_PANDA_PATH}")
59 |
60 |
61 | # Determine bitness
62 | set(THIRDPARTY_SUFFIX "-x64")
63 | if (IS_32_BIT)
64 | set(THIRDPARTY_SUFFIX "")
65 | endif()
66 |
67 |
68 |
69 | # Find visual studio version
70 | if (MSVC10)
71 | set(MSVC_VERSION_SHORT "vc10")
72 | elseif (MSVC12)
73 | set(MSVC_VERSION_SHORT "vc12")
74 | elseif(MSVC14)
75 | set(MSVC_VERSION_SHORT "vc14")
76 | else()
77 | message(WARMING "Unable to determine visual studio version, assuming Visual Studio 2015")
78 | set(MSVC_VERSION_SHORT "vc14")
79 | endif()
80 | message(STATUS "Detected visual studio version: ${MSVC_VERSION_SHORT}")
81 |
82 |
83 | set(THIRDPARTY_DIR ${THIRDPARTY_WIN_DIR})
84 | message(STATUS "Detected thirdparty directory: ${THIRDPARTY_DIR}")
85 |
86 |
87 | # Find lib and include directory
88 | set(PANDA_LIBRARY_DIRS "${WIN_PANDA_PATH}/lib")
89 | set(PANDA_INCLUDE_DIR "${WIN_PANDA_PATH}/include")
90 |
91 | if (NOT (EXISTS "${PANDA_LIBRARY_DIRS}"))
92 | message(WARNING "Panda3D library directory does not exist! Expected it at: ${PANDA_LIBRARY_DIRS}")
93 | endif()
94 |
95 | set(PYTHON_LIBRARIES "${WIN_PANDA_PATH}/python/libs/python${PYTHONVER}.lib")
96 | set(PYTHON_INCLUDE_DIRS "${WIN_PANDA_PATH}/python/include")
97 |
98 | # [LIB] Bullet
99 | if(HAVE_LIB_BULLET)
100 |
101 | if (NOT (EXISTS "${PANDA_INCLUDE_DIR}/bullet_includes.h"))
102 | message(FATAL_ERROR "This module requires the bullet library, but your Panda3D Build was copmiled without bullet support! Use --use-bullet when compiling.")
103 | endif()
104 |
105 | include_directories("${THIRDPARTY_DIR}/bullet/include/")
106 | link_directories("${THIRDPARTY_DIR}/bullet/lib/")
107 |
108 | if(EXISTS "${THIRDPARTY_DIR}/bullet/lib/LinearMath_x64.lib")
109 | set(LIBRARIES "${LIBRARIES};LinearMath_x64;BulletCollision_x64;BulletDynamics_x64;BulletSoftBody_x64")
110 | else()
111 | set(LIBRARIES "${LIBRARIES};LinearMath;BulletCollision;BulletDynamics;BulletSoftBody")
112 | endif()
113 | endif()
114 |
115 | # [LIB] Eigen 3
116 | if (HAVE_LIB_EIGEN)
117 | include_directories("${THIRDPARTY_DIR}/eigen/include")
118 |
119 | # Eigen build flags (sync with Panda)
120 | if (HAVE_LIB_EIGEN)
121 | add_definitions("/DEIGEN_MPL2_ONLY=")
122 | if ((OPTIMIZE STREQUAL "3") OR (OPTIMIZE STREQUAL "4"))
123 | add_definitions("/DEIGEN_NO_DEBUG=")
124 | if (MSVC)
125 | # Squeeze out a bit more performance on MSVC builds...
126 | # Only do this if EIGEN_NO_DEBUG is also set, otherwise it
127 | # will turn them into runtime assertions.
128 | add_definitions("/DEIGEN_NO_STATIC_ASSERT=")
129 | endif()
130 | endif()
131 | endif()
132 | endif()
133 |
134 | # [LIB] Freetype
135 | if (HAVE_LIB_FREETYPE)
136 | include_directories("${THIRDPARTY_DIR}/freetype/include")
137 | include_directories("${THIRDPARTY_DIR}/freetype/include/freetype2")
138 | link_directories("${THIRDPARTY_DIR}/freetype/lib")
139 | set(LIBRARIES "${LIBRARIES};freetype")
140 | endif()
141 |
142 | # Check if all supplied files exist
143 | set(CHECK_PATHS ${PANDA_LIBRARY_DIRS} ${PANDA_INCLUDE_DIR} ${PYTHON_LIBRARIES} ${PYTHON_INCLUDE_DIRS})
144 | foreach(PATH ${CHECK_PATHS})
145 | if(NOT (EXISTS "${PATH}"))
146 | message(FATAL_ERROR "The supplied path '${PATH}' could not be found!")
147 | endif()
148 | endforeach()
149 |
150 | # Link panda libraries
151 | set(PANDA_LIBRARIES "libpanda;libpandaexpress;libp3dtool;libp3dtoolconfig;libp3direct;${INTERROGATE_LIB}")
152 | if(HAVE_LIB_BULLET)
153 | set(PANDA_LIBRARIES "libpandabullet;${PANDA_LIBRARIES}")
154 | endif()
155 | link_directories("${PANDA_LIBRARY_DIRS}")
156 |
157 | else()
158 | set(PYTHONVERDOT CACHE STRING "2.7")
159 | set(Python_ADDITIONAL_VERSIONS ${PYTHONVERDOT})
160 | find_package(PythonLibs REQUIRED)
161 |
162 | # [LIB] Bullet
163 | if (HAVE_LIB_BULLET)
164 | find_package(Bullet REQUIRED)
165 | include_directories(${BULLET_INCLUDE_DIRS})
166 | set(LIBRARIES "${LIBRARIES};${BULLET_LIBRARIES}")
167 | endif()
168 |
169 | # [LIB] Eigen 3
170 | if (HAVE_LIB_EIGEN)
171 | if (${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
172 | include_directories(BEFORE "/usr/local/include/eigen3/")
173 | else()
174 | include_directories(BEFORE "/usr/include/eigen3/")
175 | endif()
176 |
177 | # Eigen build flags (sync with Panda)
178 | if (HAVE_LIB_EIGEN)
179 | add_definitions("/DEIGEN_MPL2_ONLY=")
180 | if ((OPTIMIZE STREQUAL "3") OR (OPTIMIZE STREQUAL "4"))
181 | add_definitions("/DEIGEN_NO_DEBUG=")
182 | endif()
183 | endif()
184 | endif()
185 |
186 | # [LIB] Freetype
187 | if(HAVE_LIB_FREETYPE)
188 | find_package(Freetype REQUIRED)
189 | include_directories(${FREETYPE_INCLUDE_DIRS})
190 | set(LIBRARIES "${LIBRARIES};${FREETYPE_LIBRARIES}")
191 | endif()
192 |
193 | # Locate the Panda3D headers
194 | find_path(PANDA_INCLUDE_DIR dtoolbase.h PATH_SUFFIXES panda3d)
195 |
196 | # Locate the Panda3D libraries
197 | set(REQ_LIBRARIES panda pandaexpress p3dtool p3dtoolconfig p3direct ${INTERROGATE_LIB})
198 | if(HAVE_LIB_BULLET)
199 | set(REQ_LIBRARIES pandabullet ${REQ_LIBRARIES})
200 | endif()
201 |
202 | set(PANDA_LIBRARIES "")
203 | foreach (lib ${REQ_LIBRARIES})
204 | find_library(PANDA_LIBRARY_${lib} ${lib} PATH_SUFFIXES panda3d)
205 | set(PANDA_LIBRARIES "${PANDA_LIBRARIES};${PANDA_LIBRARY_${lib}}")
206 | endforeach()
207 |
208 | find_package_handle_standard_args(Panda DEFAULT_MSG
209 | PANDA_LIBRARIES PANDA_INCLUDE_DIR)
210 |
211 | if (NOT EXISTS ${PANDA_INCLUDE_DIR})
212 | # Okay, the standard package handling failed. Try finding a local panda3d installation
213 |
214 | # Find panda path
215 | execute_process(
216 | COMMAND "${PYTHON_EXECUTABLE}" "-B" "scripts/common.py" "--print-sdk-path"
217 | OUTPUT_VARIABLE LOCAL_PANDA_PATH
218 | WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
219 |
220 | if (NOT EXISTS "${LOCAL_PANDA_PATH}/include")
221 | message(FATAL_ERROR "Could not find system wide panda3d headers, and no local installation was found!")
222 | endif()
223 |
224 | set(PANDA_INCLUDE_DIR "${LOCAL_PANDA_PATH}/include")
225 |
226 | set(PANDA_LIBRARIES "")
227 | foreach(lib ${REQ_LIBRARIES})
228 | find_library(PANDA_LIBRARY_${lib} ${lib} PATHS "${LOCAL_PANDA_PATH}/lib/")
229 | set(PANDA_LIBRARIES "${PANDA_LIBRARIES};${PANDA_LIBRARY_${lib}}")
230 | endforeach()
231 |
232 | endif()
233 |
234 | endif()
235 |
236 | # Find core.so/core.pyd path
237 | execute_process(
238 | COMMAND "${PYTHON_EXECUTABLE}" "-B" "scripts/common.py" "--print-core-path"
239 | OUTPUT_VARIABLE PANDA_CORE_PATH
240 | WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
241 |
242 |
243 | # Link panda includes / libraries
244 | include_directories("${PANDA_INCLUDE_DIR}")
245 | include_directories("${PYTHON_INCLUDE_DIRS}")
246 |
247 | # Run interrogate over the files
248 | execute_process(
249 | COMMAND "${PYTHON_EXECUTABLE}" "-B" "scripts/interrogate.py" "${PROJECT_NAME}" "${IGATE_VERBOSE}"
250 | OUTPUT_VARIABLE output
251 | ERROR_VARIABLE errors
252 | RESULT_VARIABLE return_code
253 | WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
254 |
255 | if (NOT ("${IGATE_VERBOSE}" STREQUAL "0" ))
256 | message(STATUS "IGATE stdout = ${output}")
257 | message(STATUS "IGATE stderr = ${errors}")
258 | endif()
259 |
260 | if(NOT ("${return_code}" STREQUAL "0"))
261 | message(FATAL_ERROR "Interrogate failed: ${output} ${errors}")
262 | endif()
263 |
264 | # Set compiler flags
265 | if (MSVC)
266 |
267 | # This matches the makepanda options
268 |
269 | if (IS_64_BIT)
270 | add_definitions("/favor:blend /DWIN64_VC /DWIN64 /bigobj")
271 | endif()
272 |
273 | add_definitions("/wd4996 /wd4275 /wd4267 /wd4101 /wd4273")
274 |
275 | if (TOUCHINPUT_ENABLED)
276 | # If touchinput is enabled
277 | add_definitions("/DWINVER=0x601")
278 | else()
279 | # If touchinput is disabled
280 | # add_definitions("/DWINVER=0x501")
281 | endif()
282 |
283 | if(IS_32_BIT AND ENABLE_SSE2)
284 | # If SSE is enabled
285 | add_definitions("/arch:SSE2")
286 | endif()
287 |
288 | # Different optimization settings
289 | if (OPTIMIZE STREQUAL "1")
290 | add_definitions("/MDd /Zi /RTCs /GS")
291 | elseif(OPTIMIZE STREQUAL "2")
292 | add_definitions("/MDd /Zi")
293 | elseif(OPTIMIZE STREQUAL "3")
294 | add_definitions("/MD /Zi /GS- /O2 /Ob2 /Oi /Ot /fp:fast")
295 | elseif(OPTIMIZE STREQUAL "4")
296 | add_definitions("/MD /Zi /GS- /Ox /Ob2 /Oi /Ot /fp:fast /DFORCE_INLINING /DNDEBUG /GL /LTCG")
297 | add_definitions("/Oy /Zp16")
298 | else()
299 | message(FATAL_ERROR "Invalid optimize value! Was: '${OPTIMIZE}'")
300 | endif()
301 |
302 | add_definitions("/Zm300 /DWIN32_VC /DWIN32")
303 |
304 | # No exceptions
305 | add_definitions("/D_HAS_EXCEPTIONS=0")
306 |
307 | # No RTTI
308 | add_definitions("/GR-")
309 |
310 | # Warning level 3 should be enough
311 | add_definitions("/W3")
312 |
313 | # Special case for 32 bit
314 | if(IS_32_BIT)
315 | add_definitions("/machine:x86")
316 | endif()
317 |
318 | # Use multi-core compilation
319 | add_definitions("/MP")
320 | # add_definitions("/GM-")
321 |
322 |
323 | else()
324 | # Silence CMake warning on macOS
325 | set(CMAKE_MACOSX_RPATH TRUE)
326 | # Assuming GCC here
327 |
328 | add_definitions("-ftemplate-depth-70 -fPIC -c")
329 |
330 | # On mac
331 | if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
332 | add_definitions("-Wno-deprecated-declarations")
333 | endif()
334 |
335 | add_definitions("-pthread")
336 | add_definitions("-std=c++11")
337 |
338 | # No exceptions
339 | add_definitions("-fno-exceptions")
340 |
341 | # Differnt optimization settings
342 | if (OPTIMIZE STREQUAL "1")
343 | add_definitions("-ggdb -D_DEBUG")
344 | elseif(OPTIMIZE STREQUAL "2")
345 | add_definitions("-O1 -D_DEBUG")
346 | elseif(OPTIMIZE STREQUAL "3")
347 | add_definitions("-O2")
348 | add_definitions("-ffast-math -fno-finite-math-only -fno-unsafe-math-optimizations")
349 | elseif(OPTIMIZE STREQUAL "4")
350 | add_definitions("-O3 -DNDEBUG -ffast-math -fno-unsafe-math-optimizations -fno-rtti")
351 | else()
352 | message(FATAL_ERROR "Invalid optimize value! Was: '${OPTIMIZE}'")
353 | endif()
354 |
355 | endif()
356 |
357 | # Define the module name
358 | add_definitions("/DPB_MODULE=${PROJECT_NAME}")
359 | add_definitions("/DPB_CFG_MODULE=${PROJECT_NAME}")
360 |
361 | # Collect sources for compiling
362 | file(GLOB_RECURSE SOURCES source/*.cpp source/*.cxx source/*.I source/*.hpp source/*.h source/*.cc source/*.c)
363 | include_directories("source/")
364 | set(SOURCES ${SOURCES_H} ${SOURCES})
365 |
366 | # Collect subdirs for compiling
367 | file(GLOB POSSIBLE_DIRS RELATIVE ${CMAKE_CURRENT_LIST_DIR} source/*)
368 | foreach(PDIR ${POSSIBLE_DIRS})
369 | if (IS_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/${PDIR})
370 | include_directories("${PDIR}")
371 | file(GLOB POSSIBLE_SUB_DIRS RELATIVE ${CMAKE_CURRENT_LIST_DIR}/${PDIR} ${PDIR}/*)
372 | foreach(PSUBDIR ${POSSIBLE_SUB_DIRS})
373 | if (IS_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/${PDIR}/${PSUBDIR})
374 | include_directories("${PDIR}/${PSUBDIR}")
375 | endif()
376 | endforeach()
377 | endif()
378 | endforeach()
379 |
380 |
381 | if ((EXISTS "${CMAKE_CURRENT_LIST_DIR}/additional_libs.cmake"))
382 | include("${CMAKE_CURRENT_LIST_DIR}/additional_libs.cmake")
383 | endif()
384 |
385 |
386 | # Build library
387 | if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
388 | # macOS won't let us link a .so with another .so, so make a .dylib
389 | add_library(${PROJECT_NAME} SHARED ${SOURCES})
390 | # Python doesn't detect .dylibs, so rename it .so
391 | set(CMAKE_SHARED_LIBRARY_SUFFIX ".so")
392 | else()
393 | add_library(${PROJECT_NAME} MODULE ${SOURCES})
394 | endif()
395 |
396 | # Don't add lib prefix on Linux
397 | set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "")
398 |
399 | # Add the required libraries
400 | target_link_libraries(${PROJECT_NAME} ${PYTHON_LIBRARIES} ${PANDA_LIBRARIES} ${LIBRARIES})
401 |
402 | if(WIN32)
403 | # # Eventually link core.pyd?
404 | else()
405 | set(CMAKE_MODULE_LINKER_FLAGS ${PANDA_CORE_PATH})
406 | endif()
407 |
408 | # After building, copy the file to the current directory
409 | add_custom_command(
410 | TARGET ${PROJECT_NAME}
411 | POST_BUILD
412 | COMMAND "${PYTHON_EXECUTABLE}" "-B" "${CMAKE_CURRENT_LIST_DIR}/scripts/finalize.py" "${PROJECT_NAME}"
413 | WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
414 |
415 | # Make shared library paths absolute on macOS
416 | if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
417 | execute_process(
418 | COMMAND "${PYTHON_EXECUTABLE}" "-B" "scripts/common.py" "--print-lib-path"
419 | OUTPUT_VARIABLE PANDA_LIB_PATH
420 | WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
421 | execute_process(
422 | COMMAND "${PYTHON_EXECUTABLE}" "-B" "scripts/common.py" "--print-short-version"
423 | OUTPUT_VARIABLE PANDA_SHORT_VERSION
424 | WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
425 | )
426 |
427 | foreach(lib ${REQ_LIBRARIES})
428 | add_custom_command(
429 | TARGET ${PROJECT_NAME}
430 | POST_BUILD
431 | COMMAND "install_name_tool" "-change" "@loader_path/../lib/lib${lib}.${PANDA_SHORT_VERSION}.dylib" "${PANDA_LIB_PATH}/lib${lib}.dylib" "${PROJECT_NAME}${CMAKE_SHARED_LIBRARY_SUFFIX}"
432 | WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
433 | endforeach()
434 | endif()
435 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 tobspr
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://travis-ci.org/tobspr/P3DModuleBuilder)
2 |
3 | # Panda3D Module Builder
4 |
5 | This tool allows you to seamlessly mix your C++ and Python code for the
6 | Panda3D Game Engine.
7 |
8 | It makes compiling your C++ code the matter of a single mouse-click.
9 |
10 |
11 | ## Features
12 |
13 | - Automatic Python bindings using `interrogate`
14 | - Works on Windows, Linux and Mac
15 |
16 | ## Getting started
17 |
18 |
19 | #### 1. Clone this repository
20 |
21 | You can use the download-zip button, or clone this repository. Copy it to a
22 | suitable path in your project.
23 |
24 | #### 2. Write your source code
25 |
26 | You can now start to write your C++ code and store it in the `source/` directory.
27 | Here's a simple example you can start with (save it as `source/example.h` for example):
28 |
29 | ```cpp
30 | #ifndef EXAMPLE_H
31 | #define EXAMPLE_H
32 |
33 | #include "pandabase.h"
34 |
35 |
36 | BEGIN_PUBLISH // This exposes all functions in this block to python
37 |
38 | inline int multiply(int a, int b) {
39 | return a * b;
40 | }
41 |
42 | END_PUBLISH
43 |
44 |
45 | class ExampleClass {
46 | PUBLISHED: // Exposes all functions in this scope, use instead of "public:"
47 | inline int get_answer() {
48 | return 42;
49 | };
50 | };
51 |
52 |
53 | #endif EXAMPLE_H
54 | ```
55 |
56 | #### 3. Compile the module
57 |
58 | After you wrote your C++ code, run `python build.py`. It will ask you for
59 | a module name, for this example we will choose "TestModule".
60 |
61 | When the compilation finished, there should now be a `TestModule.pyd` / `TestModule.so` (depending on your platform) generated.
62 |
63 | #### 4. Use your module
64 |
65 | Using your compiled module is straightforward:
66 |
67 | ```python
68 | import panda3d.core # Make sure you import this first before importing your module
69 |
70 | import TestModule
71 |
72 | print(TestModule.multiply(3, 4)) # prints 12
73 |
74 | example = TestModule.ExampleClass()
75 | print(example.get_answer()) # prints 42
76 |
77 | ```
78 |
79 |
80 |
81 | ####
82 |
83 | ## Requirements
84 |
85 | - The Panda3D SDK (get it here)
86 | - CMake 2.6 or higher (get it here)
87 | - windows only: The thirdparty folder installed in the Panda3D sdk folder (See here)
88 |
89 |
90 | **For compiling on Windows 32 bit:**
91 |
92 | - Visual Studio 2010/2015
93 |
94 | **For compiling on Windows 64 bit:**
95 |
96 | - Visual Studio 2010/2015
97 | - Windows SDK 7.1 (be sure to tick the VC++ 64 bit compilers option)
98 |
99 |
100 | ## Advanced configuration
101 |
102 | **Please clean up your built directories after changing the configuration! You can
103 | do so with passing `--clean` in the command line.**
104 |
105 |
106 | ### Command Line
107 | Command line options are:
108 |
109 | - `--optimize=N` to override the optimize option. This overrides the option set in the `config.ini`
110 | - `--clean` to force a clean rebuild
111 |
112 | ### config.ini
113 | Further adjustments can be made in the `config.ini` file:
114 |
115 | - You can set `generate_pdb` to `0` or `1` to control whether a `.pdb` file is generated.
116 | - You can set `optimize` to change the optimization. This has to match the `--optimize=` option of your Panda3D Build.
117 | - You can set `require_lib_eigen` to `1` to require the Eigen 3 library
118 | - You can set `require_lib_bullet` to `1` to require the Bullet library
119 | - You can set `require_lib_freetype` to `1` to require the Freetype library
120 | - You can set `verbose_igate` to `1` or `2` to get detailed interrogate output (1 = verbose, 2 = very verbose)
121 |
122 | ### Additional libaries
123 |
124 | If you want to include additional (external) libraries, you can create a
125 | cmake file named `additional_libs.cmake` in the folder of the module builder,
126 | which will then get included during the build.
127 |
128 | If you would like to include the protobuf library for example, your cmake file could look like this:
129 |
130 | ```cmake
131 | find_package(Protobuf REQUIRED)
132 | include_directories(${PROTOBUF_INCLUDE_DIRS})
133 | set(LIBRARIES "${LIBRARIES};${PROTOBUF_LIBRARIES}")
134 |
135 | ```
136 |
137 |
--------------------------------------------------------------------------------
/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tobspr/P3DModuleBuilder/7572305a178fa44e63cbcb2f34926bebf3c8f520/__init__.py
--------------------------------------------------------------------------------
/build.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | # Important: import panda3d as the very first library - otherwise it crashes
4 | import panda3d.core # noqa
5 |
6 | import sys
7 | import os
8 | import argparse
9 | from os.path import join, realpath, dirname
10 |
11 | # Change into the current directory
12 | os.chdir(dirname(realpath(__file__)))
13 |
14 | from scripts.common import get_ini_conf, write_ini_conf # noqa
15 | from scripts.setup import make_output_dir, run_cmake, run_cmake_build
16 |
17 | if __name__ == "__main__":
18 |
19 | # Arguments
20 | parser = argparse.ArgumentParser(description="P3DModuleBuilder")
21 | parser.add_argument(
22 | '--optimize', type=int, default=None,
23 | help="Optimize level, should match the one used for the Panda3D build",)
24 | parser.add_argument(
25 | "--clean", action="store_true", help="Forces a clean rebuild")
26 | args = parser.parse_args()
27 |
28 | # Python 2 compatibility
29 | if sys.version_info.major > 2:
30 | raw_input = input
31 |
32 | config_file = join(dirname(realpath(__file__)), "config.ini")
33 | config = get_ini_conf(config_file)
34 |
35 | # Find cached module name
36 | if "module_name" not in config or not config["module_name"]:
37 | module_name = str(raw_input("Enter a module name: "))
38 | config["module_name"] = module_name.strip()
39 |
40 | # Check for outdated parameters
41 | for outdated_param in ["vc_version", "use_lib_eigen", "use_lib_bullet", "use_lib_freetype"]:
42 | if outdated_param in config:
43 | print("WARNING: Removing obsolete parameter '" + outdated_param + "', is now auto-detected.")
44 | del config[outdated_param]
45 |
46 | # Write back config
47 | write_ini_conf(config, config_file)
48 |
49 | # Just execute the build script
50 | make_output_dir(clean=args.clean)
51 | run_cmake(config, args)
52 | run_cmake_build(config, args)
53 |
54 | print("Success!")
55 | sys.exit(0)
56 |
--------------------------------------------------------------------------------
/config.ini:
--------------------------------------------------------------------------------
1 | generate_pdb=1
2 | optimize=3
3 | require_lib_bullet=0
4 | require_lib_eigen=0
5 | require_lib_freetype=0
6 | verbose_igate=0
7 |
--------------------------------------------------------------------------------
/prefab.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Prefab gitignore which can be used by the user to ignore the files generated
3 | # by the module builder
4 |
5 | *.pyd
6 | *.pyc
7 | *.so
8 | *.pdb
9 |
10 | # Various output names
11 | win_*/
12 | linux_*/
13 | source/interrogate*
14 |
15 | # Source and script files
16 | scripts/
17 | CMakeLists.txt
18 | build.py
19 | LICENSE
20 |
--------------------------------------------------------------------------------
/scripts/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tobspr/P3DModuleBuilder/7572305a178fa44e63cbcb2f34926bebf3c8f520/scripts/__init__.py
--------------------------------------------------------------------------------
/scripts/common.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | Common functions for the build system
4 |
5 | """
6 |
7 | from __future__ import print_function
8 |
9 | import locale
10 | import sys
11 | import subprocess
12 | import platform
13 |
14 | from os.path import dirname, realpath, join, isdir, isfile
15 | from os import makedirs
16 | from sys import argv, stdout, stderr, exit
17 | from panda3d.core import PandaSystem, Filename, ExecutionEnvironment
18 |
19 |
20 | class MSVCVersion(object):
21 | def __init__(self, msc_ver, cmake_str, suffix):
22 | self.version = msc_ver
23 | self.cmake_str = cmake_str
24 | self.suffix = suffix
25 |
26 | @property
27 | def compiler_search_string(self):
28 | return "MSC v." + str(self.version)
29 |
30 | MSVC_VERSIONS = [
31 | MSVCVersion(1400, "Visual Studio 8 2005", "vc80"),
32 | MSVCVersion(1500, "Visual Studio 9 2008", "vc90"),
33 | MSVCVersion(1600, "Visual Studio 10 2010", "vc100"),
34 | MSVCVersion(1700, "Visual Studio 11 2012", "vc110"),
35 | MSVCVersion(1800, "Visual Studio 12 2013", "vc120"),
36 | MSVCVersion(1900, "Visual Studio 14 2015", "vc140"),
37 |
38 | MSVCVersion(1910, "Visual Studio 15 2017", "vc141"),
39 | MSVCVersion(1911, "Visual Studio 15 2017", "vc141"),
40 | MSVCVersion(1912, "Visual Studio 15 2017", "vc141"),
41 | MSVCVersion(1913, "Visual Studio 15 2017", "vc141"),
42 | MSVCVersion(1914, "Visual Studio 15 2017", "vc141"),
43 | MSVCVersion(1915, "Visual Studio 15 2017", "vc141"),
44 | MSVCVersion(1916, "Visual Studio 15 2017", "vc141"),
45 |
46 | MSVCVersion(1920, "Visual Studio 16 2019", "vc142"),
47 | MSVCVersion(1921, "Visual Studio 16 2019", "vc142"),
48 | MSVCVersion(1922, "Visual Studio 16 2019", "vc142"),
49 | MSVCVersion(1923, "Visual Studio 16 2019", "vc142"),
50 | MSVCVersion(1924, "Visual Studio 16 2019", "vc142"),
51 | MSVCVersion(1925, "Visual Studio 16 2019", "vc142"),
52 | MSVCVersion(1926, "Visual Studio 16 2019", "vc142"),
53 | MSVCVersion(1927, "Visual Studio 16 2019", "vc142"),
54 | MSVCVersion(1928, "Visual Studio 16 2019", "vc142"),
55 | MSVCVersion(1929, "Visual Studio 16 2019", "vc142"),
56 |
57 | MSVCVersion(1930, "Visual Studio 17 2022", "vc143"),
58 | MSVCVersion(1931, "Visual Studio 17 2022", "vc143")
59 | ]
60 |
61 | def get_output_name():
62 | """ Returns the name of the output dir, depending on the system architecture """
63 | compiler_suffix = ""
64 | if is_windows():
65 | compiler_suffix = "_" + get_panda_msvc_version().suffix
66 |
67 | version_suffix = "panda" + PandaSystem.get_version_string()
68 |
69 | return PandaSystem.getPlatform().lower() + "_{}_py{}{}{}".format(
70 | version_suffix, sys.version_info.major,
71 | sys.version_info.minor, compiler_suffix)
72 |
73 |
74 | def get_script_dir():
75 | """ Returns the name of the directory the scripts are located in """
76 | return dirname(realpath(__file__))
77 |
78 |
79 | def get_basepath():
80 | """ Returns the basepath, based on the script dir """
81 | return join(get_script_dir(), "..")
82 |
83 |
84 | def get_output_dir():
85 | """ Returns the output directory where CMake generates the build files into """
86 | return realpath(join(get_basepath(), get_output_name()))
87 |
88 |
89 | def get_python_dir():
90 | """ Returns the directory of the python installation """
91 | return dirname(sys.executable)
92 |
93 |
94 | def is_subdirectory(base, subdir):
95 | """ Returns whether subdir is the same or subdirectory of base """
96 | base_real = realpath(base)
97 | sub_real = realpath(subdir)
98 | return sub_real.startswith(base_real)
99 |
100 |
101 | def is_installed_via_pip():
102 | """ Returns whether panda3d has been installed via pip and thus has a different path layout """
103 | import panda3d
104 | python_base = get_python_dir()
105 | p3d_module = dirname(panda3d.__file__)
106 | return is_subdirectory(python_base, p3d_module)
107 |
108 |
109 | def get_panda_sdk_path():
110 | """ Returns the path of the panda3d sdk, under windows """
111 | # Import the base panda3d module
112 | import panda3d
113 |
114 | # Path of the module
115 | p3d_module = dirname(panda3d.__file__)
116 | p3d_sdk = join(p3d_module, "..")
117 |
118 | # Convert it to a valid filename
119 | fname = Filename.from_os_specific(p3d_sdk)
120 | fname.make_absolute()
121 | return fname.to_os_specific()
122 |
123 |
124 | def get_panda_core_lib_path():
125 | """ Returns of the path of the core panda3d module, either core.pyd on windows
126 | or core.so on linux. This is an absolute path """
127 | import panda3d.core
128 | return panda3d.core.__file__
129 |
130 |
131 | def find_in_sdk(folder, filename, on_error=""):
132 | """ Finds the required folder in the sdk, requiring that it contains the given filename """
133 | return first_existing_path([folder], required_file=filename, base_dir=get_panda_sdk_path(), on_error=on_error)
134 |
135 | def get_panda_bin_path():
136 | """ Returns the path to the panda3d binaries """
137 | if is_windows():
138 | return find_in_sdk("bin", "interrogate.exe", on_error="Failed to find binary path")
139 | elif is_linux() or is_freebsd():
140 | libpath = get_panda_lib_path()
141 | search = [
142 | join(libpath, "../bin"),
143 | "/usr/bin",
144 | "/usr/local/bin",
145 | ]
146 | return first_existing_path(search, "interrogate")
147 | elif is_macos():
148 | return find_in_sdk("bin", "interrogate", on_error="Failed to find binary path")
149 | raise NotImplementedError("Unsupported OS")
150 |
151 |
152 | def get_panda_lib_path():
153 | """ Returns the path to the panda3d libraries """
154 | if is_windows():
155 | return find_in_sdk("lib", "libpanda.lib")
156 | elif is_linux() or is_macos() or is_freebsd():
157 | return dirname(ExecutionEnvironment.get_dtool_name())
158 | raise NotImplementedError("Unsupported OS")
159 |
160 |
161 | def get_panda_include_path():
162 | """ Returns the path to the panda3d includes """
163 | if is_windows() or is_macos():
164 | return find_in_sdk("include", "dtoolbase.h")
165 | elif is_linux() or is_freebsd():
166 | libpath = get_panda_lib_path()
167 | search = [
168 | join(libpath, "../include/"),
169 | "/usr/include/panda3d",
170 | "/usr/local/include/panda3d"
171 | ]
172 | return first_existing_path(search, "dtoolbase.h")
173 | raise NotImplementedError("Unsupported OS")
174 |
175 |
176 | def first_existing_path(paths, required_file=None, base_dir=None, on_error=""):
177 | """ Returns the first path out of a given list of paths which exists.
178 | If required_file is set, the path additionally has to contain the given
179 | filename """
180 | for pth in paths:
181 | if base_dir:
182 | pth = join(base_dir, pth)
183 | if isdir(pth) and (required_file is None or isfile(join(pth, required_file))):
184 | return realpath(pth)
185 | if on_error:
186 | print_error("\n" + on_error + "\n")
187 | print_error("We tried to find a folder or file on the following paths:")
188 | for pth in paths:
189 | print_error("[-]", realpath(join(base_dir, pth)))
190 | fatal_error("Failed to locate path")
191 |
192 |
193 | def is_64_bit():
194 | """ Returns whether the build system is 64 bit (=True) or 32 bit (=False) """
195 | return PandaSystem.get_platform() in ["win_amd64"]
196 |
197 |
198 | def is_windows():
199 | """ Returns whether the build system is windows """
200 | return platform.system().lower() == "windows"
201 |
202 |
203 | def is_linux():
204 | """ Returns wheter the build system is linux """
205 | return platform.system().lower() == "linux"
206 |
207 | def is_macos():
208 | """ Returns whether the build system is macos (darwin) """
209 | return platform.system().lower() == "darwin"
210 |
211 | def is_freebsd():
212 | """ Returns whether the build system is freebsd """
213 | return platform.system().lower() == "freebsd"
214 |
215 |
216 | def get_compiler_name():
217 | """ Returns the name of the used compiler, either 'MSC', 'GCC' or 'CLANG' """
218 | full_name = PandaSystem.get_compiler()
219 | compiler_name = full_name.split()[0]
220 | return compiler_name.upper()
221 |
222 | def decode_str(s):
223 | if sys.version_info.major >= 3:
224 | if isinstance(s, str):
225 | return s.encode("ascii", "ignore").decode("ascii", "ignore")
226 | else:
227 | return str(s)
228 | else:
229 | return s.encode("ascii", "ignore")
230 |
231 |
232 | def fatal_error(*args):
233 | """ Prints an error to stderr and then exits with a nonzero status code """
234 | print("\n\n[!] FATAL ERROR:", *[decode_str(i) for i in args], file=stderr)
235 | exit(1)
236 |
237 |
238 | def debug_out(*args):
239 | """ Prints a debug output string """
240 | print(*[decode_str(i) for i in args])
241 |
242 |
243 | def print_error(*args):
244 | """ Prints a debug output string """
245 | print(*[decode_str(i) for i in args], file=sys.stderr)
246 |
247 |
248 | def try_makedir(dirname):
249 | """ Tries to make the specified dir, but in case it fails it does nothing """
250 | debug_out("Creating directory", dirname)
251 | try:
252 | makedirs(dirname)
253 | except:
254 | pass
255 |
256 |
257 | def try_execute(*args, **kwargs):
258 | """ Tries to execute the given process, if everything wents good, it just
259 | returns, otherwise it prints the output to stderr and exits with a nonzero
260 | status code """
261 | error_formatter = kwargs.get("error_formatter", None) # Fix for Py < 3
262 | debug_out("Executing command: ", ' '.join(args), "\n")
263 | try:
264 | process = subprocess.Popen(args, shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
265 | line = process.stdout.readline()
266 | output = line
267 | while line:
268 | debug_out(line.decode(locale.getpreferredencoding(), errors="ignore").rstrip("\r\n"))
269 | line = process.stdout.readline()
270 | output += line
271 | process.wait()
272 | if process.returncode != 0:
273 | if error_formatter:
274 | error_formatter(decode_str(output))
275 | raise Exception("Process had non-zero returncode:", process.returncode)
276 |
277 | except subprocess.CalledProcessError as msg:
278 | debug_out("Process error:")
279 | output = msg.output.decode(locale.getpreferredencoding(), errors="ignore")
280 | if error_formatter:
281 | error_formatter(decode_str(output))
282 | else:
283 | debug_out(output)
284 | fatal_error("Subprocess returned no-zero statuscode!")
285 |
286 |
287 | def join_abs(*args):
288 | """ Behaves like os.path.join, but replaces stuff like '/../' """
289 | joined = join(*args)
290 | fname = Filename.from_os_specific(joined)
291 | fname.make_absolute()
292 | return fname.to_os_generic()
293 |
294 |
295 | def get_ini_conf(fname):
296 | """ Very simple one-lined .ini file reader, with no error checking """
297 | with open(fname, "r") as handle:
298 | return {i.split("=")[0].strip(): i.split("=")[-1].strip() for i in handle.readlines() if i.strip()} # noqa
299 |
300 |
301 | def write_ini_conf(config, fname):
302 | """ Very simple .ini file writer, with no error checking """
303 | with open(fname, "w") as handle:
304 | handle.write(''.join("{}={}\n".format(k, v) for k, v in sorted(config.items())))
305 |
306 | def get_panda_msvc_version():
307 | """ Returns the MSVC version panda was built with """
308 | compiler = PandaSystem.get_compiler()
309 | for msvc_version in MSVC_VERSIONS:
310 | if msvc_version.compiler_search_string in compiler:
311 | return msvc_version
312 |
313 | print("FATAL ERROR: Unable to detect visual studio version of your Panda3D Build!", file=sys.stderr)
314 | print("Unknown compiler string was: '" + compiler + "'", file=sys.stderr)
315 | print("", file=sys.stderr)
316 | print("Known visual studio versions are:", file=sys.stderr)
317 | for msvc_version in MSVC_VERSIONS:
318 | print("-", msvc_version.cmake_str, "(" + msvc_version.compiler_search_string + ")", file=sys.stderr)
319 | print("", file=sys.stderr)
320 | fatal_error("Unable to determine compiler")
321 |
322 | def get_panda_short_version():
323 | return PandaSystem.getVersionString().replace(".0", "")
324 |
325 | def have_eigen():
326 | """ Returns whether this panda3d build has eigen support """
327 | return PandaSystem.get_global_ptr().has_system("eigen")
328 |
329 | def have_bullet():
330 | """ Returns whether this panda3d build has bullet support """
331 | try:
332 | import panda3d.bullet
333 | except Exception as msg:
334 | return False
335 |
336 | return PandaSystem.get_global_ptr().has_system("Bullet")
337 |
338 | def have_freetype():
339 | """ Returns whether this panda3d build has freetype support """
340 | return PandaSystem.get_global_ptr().has_system("Freetype")
341 |
342 |
343 | def get_win_thirdparty_dir():
344 | """ Returns the path of the thirdparty directory, windows only """
345 | msvc_suffix = get_panda_msvc_version().suffix
346 |
347 | # The thirdparty directory is named "vc14" for example instead of "vc140"
348 | if msvc_suffix.endswith("0"):
349 | msvc_suffix = msvc_suffix[:-1]
350 |
351 | bit_suffix = "-x64" if is_64_bit() else ""
352 | full_suffix = "-" + msvc_suffix + bit_suffix
353 |
354 | possible_dirs = []
355 |
356 | for base_dir in [".", "..", "../..", "thirdparty", "thirdparty" + full_suffix]:
357 | for thirdparty_suffix in ["", full_suffix]:
358 | for folder_suffix in ["", full_suffix]:
359 | possible_dirs.append(join(
360 | base_dir, "thirdparty" + thirdparty_suffix, "win-libs" + folder_suffix))
361 |
362 | error_msg = ("The thirdparty directory could not be found. You can get it from "
363 | "https://www.panda3d.org/forums/viewtopic.php?f=9&t=18775 by downloading "
364 | "a prebuilt version or by compiling it yourself.")
365 | return first_existing_path(possible_dirs, base_dir=get_panda_sdk_path(), on_error=error_msg)
366 |
367 |
368 | if __name__ == "__main__":
369 |
370 | # Command line scripts
371 |
372 | if len(argv) != 2:
373 | fatal_error("USAGE: ppython common.py