Skip to content

Commit

Permalink
create and install whl for Python bindings (tested on Mac)
Browse files Browse the repository at this point in the history
  • Loading branch information
LiangliangNan committed Jan 10, 2025
1 parent 6699cab commit 1b804cf
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 43 deletions.
28 changes: 19 additions & 9 deletions easy3d/util/resource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,26 +33,36 @@ namespace easy3d {

namespace resource {

static std::string resource_dir = file_system::convert_to_native_style(Easy3D_RESOURCE_DIR);
static std::string easy3d_resource_dir = file_system::convert_to_native_style(Easy3D_RESOURCE_DIR);

bool is_valid_resource_dir(const std::string& dir) {
return file_system::is_directory(dir) &&
file_system::is_directory(dir + "/shaders") &&
file_system::is_directory(dir + "/fonts") &&
file_system::is_directory(dir + "/colormaps") &&
file_system::is_directory(dir + "/textures");
}

// Sets the resource directory (containing color maps, shaders, textures, fonts, etc.)
void initialize(const std::string& res_dir) {
resource_dir = file_system::convert_to_native_style(res_dir);
void initialize(const std::string& dir) {
if (is_valid_resource_dir(dir)) {
VLOG_N_TIMES(1, 1) << "resource directory: " << dir;
easy3d_resource_dir = file_system::convert_to_native_style(dir);
}
}

// resource directory (containing color maps, shaders, textures, fonts, etc.)
std::string directory() {
// first check if the resource directory (with the Easy3D distribution) exist
static std::string& dir = resource_dir;
if (file_system::is_directory(dir)) {
static std::string& dir = easy3d_resource_dir;
if (is_valid_resource_dir(dir)) {
VLOG_N_TIMES(1, 1) << "resource directory: " << dir;
return dir;
}

std::string parent = file_system::executable_directory();
dir = file_system::convert_to_native_style(parent + "/resources");
if (file_system::is_directory(dir))
if (is_valid_resource_dir(dir))
return dir;
else {
// For macOS, if reached here, we may need to move "up" three times, because
Expand All @@ -61,18 +71,18 @@ namespace easy3d {
// Debug/Release sub-folder, so we may try four times up at most.
parent = file_system::parent_directory(parent);
dir = file_system::convert_to_native_style(parent + "/resources");
if (file_system::is_directory(dir))
if (is_valid_resource_dir(dir))
return dir;
else {
for (int i = 0; i < 4; ++i) {
parent = file_system::parent_directory(parent);
dir = file_system::convert_to_native_style(parent + "/resources");
if (file_system::is_directory(dir))
if (is_valid_resource_dir(dir))
return dir;
}
}
// if still could not find it, show an error and return the current working directory
LOG_N_TIMES(1, ERROR) << "could not find the resource directory";
LOG_N_TIMES(1, ERROR) << "could not find the resource directory: " << easy3d_resource_dir;
return file_system::current_working_directory();
}
}
Expand Down
54 changes: 32 additions & 22 deletions python/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,10 @@ set_target_properties(${PROJECT_NAME} PROPERTIES
)



# The following commands are actually not necessary for generating bindings.
# ----------------------------------------------------------------------------------------------------------------------
# The code below is actually not necessary for generating bindings.
# They are here to make it easier for the installation of the generated Python module.
# ----------------------------------------------------------------------------------------------------------------------

# RPATH settings for macOS and Linux
if (APPLE)
Expand All @@ -161,15 +162,6 @@ endif ()
# Ensure the directory exists
file(MAKE_DIRECTORY ${Easy3D_PYTHON_PACKAGE_DIR}/easy3d)

# Generate the __init__.py file for the Python package, which imports the compiled module
file(WRITE ${Easy3D_PYTHON_PACKAGE_DIR}/easy3d/__init__.py
"# Alias for the ${EASY3D_MODULE_NAME} module\n"
"try:\n"
" from .${EASY3D_MODULE_NAME} import *\n"
"except ImportError:\n"
" from ${EASY3D_MODULE_NAME} import *\n"
)

# Post-build command to copy the compiled module to the Python package directory.
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo "Copying ${EASY3D_MODULE_NAME}${EASY3D_MODULE_SUFFIX} to ${Easy3D_PYTHON_PACKAGE_DIR}/easy3d/..."
Expand Down Expand Up @@ -266,19 +258,37 @@ endfunction()
# Use the above function to copy the dependencies of the compiled module to the Python package directory.
copy_runtime_dependencies(${PROJECT_NAME} ${Easy3D_PYTHON_PACKAGE_DIR}/easy3d/)

# Platform-specific dynamic library extensions
set(CONF_Easy3D_VERSION ${Easy3D_VERSION_STRING})
set(CONF_EASY3D_MODULE_NAME ${EASY3D_MODULE_NAME})
set(CONF_EASY3D_MODULE_SUFFIX ${EASY3D_MODULE_SUFFIX})

# Copy pyproject.toml
# Configure and create pyproject.toml
configure_file(${CMAKE_CURRENT_LIST_DIR}/pyproject.toml.in ${Easy3D_PYTHON_PACKAGE_DIR}/pyproject.toml @ONLY)
# Copy the MANIFEST.in file
file(COPY ${CMAKE_CURRENT_LIST_DIR}/MANIFEST.in DESTINATION ${Easy3D_PYTHON_PACKAGE_DIR})

# Copy the readme file
file(COPY ${CMAKE_CURRENT_LIST_DIR}/ReadMe.md DESTINATION ${Easy3D_PYTHON_PACKAGE_DIR}/easy3d)
# Copy the resource files
file(COPY ${Easy3D_ROOT}/resources/colormaps DESTINATION ${Easy3D_PYTHON_PACKAGE_DIR}/easy3d/resources)
file(COPY ${Easy3D_ROOT}/resources/fonts DESTINATION ${Easy3D_PYTHON_PACKAGE_DIR}/easy3d/resources)
file(COPY ${Easy3D_ROOT}/resources/shaders DESTINATION ${Easy3D_PYTHON_PACKAGE_DIR}/easy3d/resources)
file(COPY ${Easy3D_ROOT}/resources/textures DESTINATION ${Easy3D_PYTHON_PACKAGE_DIR}/easy3d/resources)
file(COPY ${Easy3D_ROOT}/resources DESTINATION ${Easy3D_PYTHON_PACKAGE_DIR}/easy3d)

# Generate the __init__.py file for the Python package
file(WRITE ${Easy3D_PYTHON_PACKAGE_DIR}/easy3d/__init__.py
"# Alias for the ${EASY3D_MODULE_NAME} module\n"
"import os\n"
"import sys\n"
"\n"
"try:\n"
" from .${EASY3D_MODULE_NAME} import *\n"
"except ImportError:\n"
" from ${EASY3D_MODULE_NAME} import *\n"
"\n"
"# Get the path of the current module (__init__.py)\n"
"module_path = os.path.abspath(__file__)\n"
"# Get the directory containing the module\n"
"resource_dir = os.path.join(os.path.dirname(module_path), \"resources\")\n"
"# Initialize the resource directory\n"
"initialize_resource_directory(resource_dir=resource_dir)\n"
)

# Generate the MANIFEST.in file for creating the whl file
file(WRITE ${Easy3D_PYTHON_PACKAGE_DIR}/MANIFEST.in
"include ReadMe.md\n"
"include LICENSE.txt\n"
"recursive-include easy3d/resources *\n"
)
3 changes: 0 additions & 3 deletions python/MANIFEST.in

This file was deleted.

30 changes: 24 additions & 6 deletions python/bindings/easy3d/util/initializer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,31 @@
PYBIND11_MAKE_OPAQUE(std::shared_ptr<void>)
#endif


void bind_easy3d_util_initializer(pybind11::module_& m)
{
// easy3d::initialize(bool, bool, bool, const std::string &) file:easy3d/util/initializer.h line:52
m.def("initialize", []() -> void { return easy3d::initialize(); }, "");
m.def("initialize", [](bool const & a0) -> void { return easy3d::initialize(a0); }, "", pybind11::arg("info_to_stdout"));
m.def("initialize", [](bool const & a0, bool const & a1) -> void { return easy3d::initialize(a0, a1); }, "", pybind11::arg("info_to_stdout"), pybind11::arg("use_log_file"));
m.def("initialize", [](bool const & a0, bool const & a1, bool const & a2) -> void { return easy3d::initialize(a0, a1, a2); }, "", pybind11::arg("info_to_stdout"), pybind11::arg("use_log_file"), pybind11::arg("use_setting_file"));
m.def("initialize", (void (*)(bool, bool, bool, const std::string &)) &easy3d::initialize, "Initialization of Easy3D.\n \n\n This function initializes logging, setting, and resources. Internally it calls (and is thus identical\n to calling) logging::initialize(), setting::initialize(), and resource::initialize().\n For more fine-grained initializations, please refer to the documentation of these functions.\n \n\n to log messages at a the level to standard output.\n and (including levels are always logged to standard output.\n \n\n to create a \".log\" file (which will be created next to the executable program).\n \n\n to create an \".ini\" file (which will be created next to the executable program).\n This setting file stores the default rendering parameters. Users can modify this file to change the default\n rendering parameters, then the changes will be effective for the future.\n \n\n The resource directory containing color maps, shaders, textures, fonts, etc.\n is the default value, which is the directory coming with the Easy3D distribution.\n In most cases you should use the default resource directory (unless you want to use different resources).\n\n \n logging::initialize(), setting::initialize(), and resource::initialize().\n\nC++: easy3d::initialize(bool, bool, bool, const std::string &) --> void", pybind11::arg("info_to_stdout"), pybind11::arg("use_log_file"), pybind11::arg("use_setting_file"), pybind11::arg("resource_dir"));

}
// Bind the initialize function using a lambda
m.def("initialize",
[&](bool info_to_stdout, bool use_log_file, bool use_setting_file) {
// Call the actual Easy3D initialize function
easy3d::initialize(info_to_stdout, use_log_file, use_setting_file);
},
pybind11::arg("info_to_stdout") = false,
pybind11::arg("use_log_file") = true,
pybind11::arg("use_setting_file") = false,
R"(
Initialization of Easy3D.
This function initializes logging, setting, and resources.
Parameters:
info_to_stdout (bool): True to log messages at the INFO level to standard output.
WARNING and ERROR (including FATAL) levels are always logged to standard output.
use_log_file (bool): True to create a ".log" file (which will be created next to the executable program).
use_setting_file (bool): True to create an ".ini" file (which will be created next to the executable program).
This setting file stores the default rendering parameters. Users can modify this file to change the default
rendering parameters, then the changes will be effective for the future.
)"
);
}
3 changes: 1 addition & 2 deletions python/bindings/easy3d/util/resource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@
void bind_easy3d_util_resource(pybind11::module_& m)
{
// easy3d::resource::initialize(const std::string &) file:easy3d/util/resource.h line:45
m.def("initialize", []() -> void { return easy3d::resource::initialize(); }, "");
m.def("initialize", (void (*)(const std::string &)) &easy3d::resource::initialize, "Initializes the resource directory (that contains color maps, shaders, textures, fonts, etc.).\n \n\n is the default value, which is the directory coming with the Easy3D\n distribution. In most cases you should use the default resource directory (unless you want to use\n different resources).\n\nC++: easy3d::resource::initialize(const std::string &) --> void", pybind11::arg("resource_dir"));
m.def("initialize_resource_directory", (void (*)(const std::string &)) &easy3d::resource::initialize, "Initializes the resource directory (that contains colormaps, shaders, textures, fonts, etc.)", pybind11::arg("resource_dir"));

// easy3d::resource::directory() file:easy3d/util/resource.h line:48
m.def("resource_directory", (std::string (*)()) &easy3d::resource::directory, "Returns the resource directory (containing color maps, shaders, textures, fonts, etc.)\n\nC++: easy3d::resource::directory() --> std::string");
Expand Down
2 changes: 1 addition & 1 deletion python/pyproject.toml.in
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "easy3d"
version = "@CONF_Easy3D_VERSION@"
version = "@Easy3D_VERSION_STRING@"
description = "Python bindings for the Easy3D library"
authors = [
{ name = "Liangliang Nan", email = "[email protected]" }
Expand Down

0 comments on commit 1b804cf

Please sign in to comment.