Fixes to plug-in loading in and outside of pyodide

This commit is contained in:
Thomas Krijnen
2026-05-07 14:43:26 +02:00
parent 2257d7930a
commit e893552f24
19 changed files with 642 additions and 46 deletions
@@ -21,6 +21,12 @@
#include <boost/algorithm/string/case_conv.hpp>
namespace ifcopenshell {
namespace plugin {
PLUGIN_API std::filesystem::path add_search_paths_or_default(manager& manager, std::filesystem::path (*default_search_path)());
}
}
namespace {
constexpr const char* opencascade_geometry_ifc_writer_plugin_prefix = "geometry.serialization.";
}
@@ -43,7 +49,7 @@ std::filesystem::path IfcGeom::opencascade_geometry_ifc_writer_plugin_directory(
void IfcGeom::load_opencascade_geometry_ifc_writer_plugins(opencascade_geometry_ifc_writer_registry& registry) {
ifcopenshell::plugin::manager manager;
manager.add_search_path(opencascade_geometry_ifc_writer_plugin_directory());
ifcopenshell::plugin::add_search_paths_or_default(manager, &opencascade_geometry_ifc_writer_plugin_directory);
for (const auto& path : manager.discover(opencascade_geometry_ifc_writer_plugin_prefix)) {
auto module = manager.load(path);
@@ -58,7 +64,7 @@ void IfcGeom::load_opencascade_geometry_ifc_writer_plugins(opencascade_geometry_
bool IfcGeom::load_opencascade_geometry_ifc_writer_plugin(opencascade_geometry_ifc_writer_registry& registry, const std::string& schema_name) {
ifcopenshell::plugin::manager manager;
manager.add_search_path(opencascade_geometry_ifc_writer_plugin_directory());
ifcopenshell::plugin::add_search_paths_or_default(manager, &opencascade_geometry_ifc_writer_plugin_directory);
const auto expected_schema = boost::to_upper_copy(schema_name);
const auto basename = std::string(opencascade_geometry_ifc_writer_plugin_prefix) + boost::to_lower_copy(schema_name);
+8 -2
View File
@@ -24,6 +24,12 @@
#include <algorithm>
#include <stdexcept>
namespace ifcopenshell {
namespace plugin {
PLUGIN_API std::filesystem::path add_search_paths_or_default(manager& manager, std::filesystem::path (*default_search_path)());
}
}
namespace {
constexpr const char* kernel_plugin_prefix = "geometry.kernel.";
@@ -51,7 +57,7 @@ std::filesystem::path ifcopenshell::geometry::kernels::kernel_plugin_directory()
void ifcopenshell::geometry::kernels::load_kernel_plugins(kernel_registry& registry) {
plugin::manager manager;
manager.add_search_path(kernel_plugin_directory());
plugin::add_search_paths_or_default(manager, &kernel_plugin_directory);
for (const auto& path : manager.discover(kernel_plugin_prefix)) {
auto module = manager.load(path);
@@ -66,7 +72,7 @@ void ifcopenshell::geometry::kernels::load_kernel_plugins(kernel_registry& regis
bool ifcopenshell::geometry::kernels::load_kernel_plugin(kernel_registry& registry, const std::string& backend_id) {
plugin::manager manager;
manager.add_search_path(kernel_plugin_directory());
plugin::add_search_paths_or_default(manager, &kernel_plugin_directory);
const auto plugin_name = kernel_plugin_name(backend_id);
const auto basename = std::string(kernel_plugin_prefix) + plugin_name;
+75 -22
View File
@@ -27,13 +27,85 @@
#include <algorithm>
#include <cstring>
#include <iostream>
#include <vector>
namespace ifcopenshell {
namespace plugin {
PLUGIN_API std::filesystem::path add_search_paths_or_default(manager& manager, std::filesystem::path (*default_search_path)());
}
}
namespace {
constexpr const char* kernel_plugin_prefix = "geometry.kernel.";
std::string kernel_key(const std::string& backend_id) {
return boost::to_lower_copy(backend_id);
}
bool is_prefix(const std::string& text, const std::string& prefix) {
return !prefix.empty() && text.rfind(prefix, 0) == 0;
}
void register_kernel_module(ifcopenshell::geometry::kernels::kernel_registry& registry, const ifcopenshell::plugin::module& module) {
auto register_plugin = module.get_alias<ifcopenshell::geometry::kernels::register_kernel_plugin_fn>(
ifcopenshell::geometry::kernels::kernel_plugin_registration_symbol());
register_plugin(registry, module);
}
struct kernel_match {
std::string backend_id;
ifcopenshell::plugin::module module;
bool has_module = false;
};
void consider_kernel_info(kernel_match& match, const std::string& geometry_library_lower, const ifcopenshell::geometry::kernels::kernel_info& info) {
const auto backend_id = kernel_key(info.backend_id);
if (is_prefix(geometry_library_lower, backend_id) && backend_id.size() > match.backend_id.size()) {
match.backend_id = backend_id;
match.has_module = false;
}
}
kernel_match find_kernel_match(ifcopenshell::geometry::kernels::kernel_registry& registry, const std::string& geometry_library_lower) {
kernel_match match;
for (const auto& info : registry.kernels()) {
consider_kernel_info(match, geometry_library_lower, info);
}
ifcopenshell::plugin::manager manager;
ifcopenshell::plugin::add_search_paths_or_default(manager, &ifcopenshell::geometry::kernels::kernel_plugin_directory);
for (const auto& path : manager.discover(kernel_plugin_prefix)) {
ifcopenshell::plugin::module module;
try {
module = manager.load(path);
} catch (const std::exception& e) {
std::cerr << "[ifcopenshell.plugin] skip kernel plugin " << path << ": " << e.what() << std::endl;
continue;
}
if (module.meta().kind_ != ifcopenshell::plugin::kind::kernel) {
continue;
}
ifcopenshell::geometry::kernels::kernel_registry plugin_registry;
register_kernel_module(plugin_registry, module);
for (const auto& info : plugin_registry.kernels()) {
const auto backend_id = kernel_key(info.backend_id);
if (is_prefix(geometry_library_lower, backend_id) && backend_id.size() > match.backend_id.size()) {
match.backend_id = backend_id;
match.module = module;
match.has_module = true;
}
}
}
if (!match.backend_id.empty() && !registry.has(match.backend_id) && match.has_module) {
register_kernel_module(registry, match.module);
}
return match;
}
}
void ifcopenshell::geometry::kernels::kernel_registry::bind(const kernel_info& info, create_fn create, const plugin::module& module) {
@@ -90,30 +162,11 @@ std::unique_ptr<ifcopenshell::geometry::kernels::AbstractKernel> ifcopenshell::g
throw ifcopenshell::exception("Invalid hybrid kernel " + geometry_library);
}
std::vector<std::string> candidates;
candidates.push_back(geometry_library_lower);
for (auto pos = geometry_library_lower.find('-'); pos != std::string::npos; pos = geometry_library_lower.find('-', pos + 1)) {
candidates.push_back(geometry_library_lower.substr(0, pos));
}
std::sort(candidates.begin(), candidates.end(), [](const auto& a, const auto& b) {
return a.size() > b.size();
});
for (const auto& candidate : candidates) {
if (!registry.has(candidate) && load_kernel_plugin(registry, candidate)) {
break;
}
}
std::string matched_backend_id;
for (const auto& info : registry.kernels()) {
const auto backend_id = kernel_key(info.backend_id);
if (geometry_library_lower.rfind(backend_id, 0) == 0 && backend_id.size() > matched_backend_id.size()) {
matched_backend_id = backend_id;
}
}
const auto match = find_kernel_match(registry, geometry_library_lower);
const auto& matched_backend_id = match.backend_id;
if (matched_backend_id.empty()) {
throw ifcopenshell::exception("Invalid hybrid kernel " + geometry_library);
throw ifcopenshell::exception("Invalid hybrid kernel; no match for prefix of " + geometry_library_lower);
}
kernels.push_back(registry.create(matched_backend_id, file, settings));
+5 -1
View File
@@ -22,10 +22,14 @@
#include <boost/dll/alias.hpp>
#ifdef IFOPSH_SIMPLE_KERNEL
#define cgal_plugin cgalsimple_plugin
#endif
namespace ifcopenshell {
namespace geometry {
namespace kernels {
namespace cgal_plugin {
namespace cgal_plugin {
#ifdef IFOPSH_SIMPLE_KERNEL
constexpr const char* plugin_name = "cgalsimple";
+8 -2
View File
@@ -21,6 +21,12 @@
#include <boost/algorithm/string/case_conv.hpp>
namespace ifcopenshell {
namespace plugin {
PLUGIN_API std::filesystem::path add_search_paths_or_default(manager& manager, std::filesystem::path (*default_search_path)());
}
}
namespace {
constexpr const char* mapping_plugin_prefix = "geometry.mapping.";
}
@@ -43,7 +49,7 @@ std::filesystem::path ifcopenshell::geometry::impl::mapping_plugin_directory() {
void ifcopenshell::geometry::impl::load_mapping_plugins(mapping_registry& registry) {
plugin::manager manager;
manager.add_search_path(mapping_plugin_directory());
plugin::add_search_paths_or_default(manager, &mapping_plugin_directory);
for (const auto& path : manager.discover(mapping_plugin_prefix)) {
auto module = manager.load(path);
@@ -58,7 +64,7 @@ void ifcopenshell::geometry::impl::load_mapping_plugins(mapping_registry& regist
bool ifcopenshell::geometry::impl::load_mapping_plugin(mapping_registry& registry, const std::string& schema_name) {
plugin::manager manager;
manager.add_search_path(mapping_plugin_directory());
plugin::add_search_paths_or_default(manager, &mapping_plugin_directory);
const auto expected_schema = boost::to_upper_copy(schema_name);
const auto basename = std::string(mapping_plugin_prefix) + boost::to_lower_copy(schema_name);
+8 -2
View File
@@ -21,6 +21,12 @@
#include <boost/algorithm/string/case_conv.hpp>
namespace ifcopenshell {
namespace plugin {
PLUGIN_API std::filesystem::path add_search_paths_or_default(manager& manager, std::filesystem::path (*default_search_path)());
}
}
namespace {
constexpr const char* tree_plugin_prefix = "geometry.tree.";
}
@@ -42,7 +48,7 @@ std::filesystem::path ifcopenshell::geometry::trees::tree_plugin_directory() {
void ifcopenshell::geometry::trees::load_tree_plugins(tree_registry& registry) {
plugin::manager manager;
manager.add_search_path(tree_plugin_directory());
plugin::add_search_paths_or_default(manager, &tree_plugin_directory);
for (const auto& path : manager.discover(tree_plugin_prefix)) {
auto module = manager.load(path);
@@ -57,7 +63,7 @@ void ifcopenshell::geometry::trees::load_tree_plugins(tree_registry& registry) {
bool ifcopenshell::geometry::trees::load_tree_plugin(tree_registry& registry, const std::string& backend_id) {
plugin::manager manager;
manager.add_search_path(tree_plugin_directory());
plugin::add_search_paths_or_default(manager, &tree_plugin_directory);
const auto plugin_name = boost::to_lower_copy(backend_id);
const auto basename = std::string(tree_plugin_prefix) + plugin_name;
@@ -101,8 +101,11 @@ __all__ = [
"entity_instance",
"file",
"guid",
"get_plugin_search_paths",
"ifcopenshell_wrapper",
"rocksdb_lazy_instance",
"clear_plugin_search_paths",
"set_plugin_search_paths",
"sqlite",
"sqlite_entity",
"stream",
@@ -116,6 +119,18 @@ except:
pass
def set_plugin_search_paths(paths: Sequence[Union[os.PathLike, str]]) -> None:
ifcopenshell_wrapper.set_plugin_search_paths([os.fspath(path) for path in paths])
def get_plugin_search_paths() -> tuple[str, ...]:
return tuple(ifcopenshell_wrapper.get_plugin_search_paths())
def clear_plugin_search_paths() -> None:
ifcopenshell_wrapper.clear_plugin_search_paths()
class Error(Exception):
"""Error used when a generic problem occurs"""
@@ -1696,6 +1696,7 @@ class type_declaration(declaration):
class uninitialized_tag: ...
def arrange_polygons(polygons): ...
def clear_plugin_search_paths() -> None: ...
def clear_schemas(): ...
def construct_iterator(geometry_library, settings, file, num_threads): ...
def construct_iterator_with_include_exclude(geometry_library, settings, file, elems, include, num_threads): ...
@@ -1707,6 +1708,7 @@ def create_epeck(*args): ...
def create_shape(*args): ...
def flatten(deep): ...
def get_feature(x): ...
def get_plugin_search_paths() -> tuple[str, ...]: ...
def get_info_cpp(v, include_identifier=True): ...
def get_log(): ...
def guess_file_type(fn): ...
@@ -1726,6 +1728,7 @@ def schema_by_name(arg1: str) -> schema_definition: ...
def schema_names() -> tuple[str, ...]: ...
def serialise(schema_name, shape_str, advanced=True): ...
def set_feature(x, v): ...
def set_plugin_search_paths(paths) -> None: ...
def set_log_format_json(): ...
def set_log_format_text(): ...
def stream_from_string(data): ...
+1
View File
@@ -89,6 +89,7 @@ foreach(schema ${SCHEMA_VERSIONS})
set_target_properties(${SCHEMA_PLUGIN_TARGET} PROPERTIES
OUTPUT_NAME "ifcopenshell.parse.schema.ifc${schema}"
)
ifcopenshell_plugin_target(${SCHEMA_PLUGIN_TARGET})
if(WASM_BUILD)
target_link_options(${SCHEMA_PLUGIN_TARGET} PRIVATE "SHELL:-s SIDE_MODULE=1" "-O1")
+9 -3
View File
@@ -27,6 +27,12 @@
#include "schemas/Header_section_schema.h"
namespace ifcopenshell {
namespace plugin {
PLUGIN_API std::filesystem::path add_search_paths_or_default(manager& manager, std::filesystem::path (*default_search_path)());
}
}
bool ifcopenshell::declaration::is(const std::string& name) const {
const std::string* name_ptr = &name;
if (std::any_of(name.begin(), name.end(), [](char character) { return std::islower(character); })) {
@@ -115,7 +121,7 @@ namespace {
bool load_schema_plugin(ifcopenshell::schema_registry& registry, const std::string& schema_name) {
ifcopenshell::plugin::manager manager;
manager.add_search_path(ifcopenshell::schema_plugin_directory());
ifcopenshell::plugin::add_search_paths_or_default(manager, &ifcopenshell::schema_plugin_directory);
const auto expected_key = schema_key(schema_name);
const auto basename = std::string(schema_plugin_prefix) + boost::to_lower_copy(schema_name);
@@ -196,7 +202,7 @@ std::filesystem::path ifcopenshell::schema_plugin_directory() {
void ifcopenshell::load_schema_plugins(schema_registry& registry) {
plugin::manager manager;
manager.add_search_path(schema_plugin_directory());
plugin::add_search_paths_or_default(manager, &schema_plugin_directory);
for (const auto& path : manager.discover(schema_plugin_prefix)) {
auto module = manager.load(path);
@@ -247,7 +253,7 @@ std::vector<std::string> ifcopenshell::schema_registry::names() {
}
plugin::manager manager;
manager.add_search_path(schema_plugin_directory());
plugin::add_search_paths_or_default(manager, &schema_plugin_directory);
for (const auto& path : manager.discover(schema_plugin_prefix)) {
auto module = manager.load(path);
if (module.meta().kind_ == plugin::kind::parse_schema && !module.meta().schema.empty()) {
+26
View File
@@ -245,5 +245,31 @@
#include "../serializers/RocksDbSerializer.h"
%}
%{
#include <string>
#include <vector>
namespace ifcopenshell {
namespace plugin {
void set_search_paths(const std::vector<std::string>& paths);
std::vector<std::string> search_paths();
void clear_search_paths();
}
}
%}
%inline %{
void set_plugin_search_paths(const std::vector<std::string>& paths) {
ifcopenshell::plugin::set_search_paths(paths);
}
std::vector<std::string> get_plugin_search_paths() {
return ifcopenshell::plugin::search_paths();
}
void clear_plugin_search_paths() {
ifcopenshell::plugin::clear_search_paths();
}
%}
%include "IfcGeomWrapper.i"
%include "IfcParseWrapper.i"
+132 -7
View File
@@ -30,6 +30,8 @@
#include <algorithm>
#include <filesystem>
#include <iostream>
#include <mutex>
#include <sstream>
#include <stdexcept>
@@ -37,6 +39,50 @@ namespace {
using plugin_abi_fn = ifcopenshell::plugin::abi_info();
using plugin_metadata_fn = ifcopenshell::plugin::metadata();
std::mutex& configured_search_paths_mutex() {
static std::mutex mutex;
return mutex;
}
std::vector<std::string>& configured_search_paths() {
static std::vector<std::string> paths;
return paths;
}
std::string path_string(const std::filesystem::path& path) {
return path.string();
}
void plugin_debug(const std::string& message) {
std::cerr << "[ifcopenshell.plugin] " << message << std::endl;
}
const char* plugin_kind_name(ifcopenshell::plugin::kind kind) {
switch (kind) {
case ifcopenshell::plugin::kind::parse_schema:
return "parse_schema";
case ifcopenshell::plugin::kind::mapping:
return "mapping";
case ifcopenshell::plugin::kind::kernel:
return "kernel";
case ifcopenshell::plugin::kind::tree:
return "tree";
case ifcopenshell::plugin::kind::document_serializer:
return "document_serializer";
case ifcopenshell::plugin::kind::geometry_serializer:
return "geometry_serializer";
case ifcopenshell::plugin::kind::opencascade_geometry_ifc_writer:
return "opencascade_geometry_ifc_writer";
default:
return "unknown";
}
}
std::vector<std::string> configured_search_paths_copy() {
std::lock_guard<std::mutex> lock(configured_search_paths_mutex());
return configured_search_paths();
}
std::string compiler_id() {
#if defined(_MSC_VER)
return "msvc";
@@ -74,11 +120,7 @@ namespace {
}
std::vector<std::string> platform_basenames(const std::string& basename) {
const auto decorated = decorated_basename(basename);
return {
decorated,
"lib" + decorated
};
return {decorated_basename(basename)};
}
#ifdef _WIN32
@@ -99,6 +141,15 @@ namespace {
#endif
}
namespace ifcopenshell {
namespace plugin {
PLUGIN_API void set_search_paths(const std::vector<std::string>& paths);
PLUGIN_API std::vector<std::string> search_paths();
PLUGIN_API void clear_search_paths();
PLUGIN_API std::filesystem::path add_search_paths_or_default(manager& manager, std::filesystem::path (*default_search_path)());
}
}
struct ifcopenshell::plugin::module::data {
metadata metadata_;
std::filesystem::path path_;
@@ -149,6 +200,7 @@ boost::dll::shared_library& ifcopenshell::plugin::module::library() const {
ifcopenshell::plugin::manager::manager() = default;
void ifcopenshell::plugin::manager::add_search_path(const std::filesystem::path& path) {
plugin_debug("add_search_path " + path_string(path));
search_paths_.push_back(path);
}
@@ -161,11 +213,14 @@ std::vector<std::filesystem::path> ifcopenshell::plugin::manager::discover(const
const auto suffix = boost::dll::shared_library::suffix().string();
const auto basename_prefixes = platform_basenames(basename_prefix);
plugin_debug("discover prefix='" + basename_prefix + "' suffix='" + suffix + "' search_paths=" + std::to_string(search_paths_.size()));
for (const auto& search_path : search_paths_) {
if (!std::filesystem::exists(search_path) || !std::filesystem::is_directory(search_path)) {
plugin_debug("discover skip missing/non-directory search path " + path_string(search_path));
continue;
}
plugin_debug("discover scan " + path_string(search_path));
for (const auto& entry : std::filesystem::directory_iterator(search_path)) {
if (!entry.is_regular_file()) {
continue;
@@ -183,12 +238,14 @@ std::vector<std::filesystem::path> ifcopenshell::plugin::manager::discover(const
continue;
}
plugin_debug("discover candidate " + path_string(entry.path()));
result.push_back(entry.path());
}
}
std::sort(result.begin(), result.end());
result.erase(std::unique(result.begin(), result.end()), result.end());
plugin_debug("discover result count=" + std::to_string(result.size()));
return result;
}
@@ -197,14 +254,18 @@ std::vector<std::filesystem::path> ifcopenshell::plugin::manager::discover_exact
const auto suffix = boost::dll::shared_library::suffix().string();
const auto basename_candidates = platform_basenames(basename);
plugin_debug("discover_exact basename='" + basename + "' suffix='" + suffix + "' search_paths=" + std::to_string(search_paths_.size()));
for (const auto& search_path : search_paths_) {
if (!std::filesystem::exists(search_path) || !std::filesystem::is_directory(search_path)) {
plugin_debug("discover_exact skip missing/non-directory search path " + path_string(search_path));
continue;
}
for (const auto& candidate : basename_candidates) {
const auto path = search_path / (candidate + suffix);
plugin_debug("discover_exact probe " + path_string(path));
if (std::filesystem::is_regular_file(path)) {
plugin_debug("discover_exact candidate " + path_string(path));
result.push_back(path);
}
}
@@ -212,13 +273,18 @@ std::vector<std::filesystem::path> ifcopenshell::plugin::manager::discover_exact
std::sort(result.begin(), result.end());
result.erase(std::unique(result.begin(), result.end()), result.end());
plugin_debug("discover_exact result count=" + std::to_string(result.size()));
return result;
}
ifcopenshell::plugin::module ifcopenshell::plugin::manager::load(const std::filesystem::path& path) const {
plugin_debug("load " + path_string(path));
#ifdef _WIN32
dll_error_mode_guard error_mode_guard;
const auto load_mode = boost::dll::load_mode::load_with_altered_search_path;
#elif defined(__EMSCRIPTEN__)
// Pyodide side modules resolve shared IfcOpenShell symbols from modules loaded earlier.
const auto load_mode = boost::dll::load_mode::rtld_global;
#else
const auto load_mode = boost::dll::load_mode::default_mode;
#endif
@@ -226,6 +292,8 @@ ifcopenshell::plugin::module ifcopenshell::plugin::manager::load(const std::file
auto abi = library->get_alias<plugin_abi_fn>("ifcopenshell_plugin_abi_v1")();
validate_abi(abi);
auto metadata = library->get_alias<plugin_metadata_fn>("ifcopenshell_plugin_metadata_v1")();
plugin_debug(std::string("load metadata kind=") + plugin_kind_name(metadata.kind_) +
" id='" + metadata.id + "' schema='" + metadata.schema + "' format='" + metadata.format + "'");
auto data = std::make_shared<module::data>();
data->metadata_ = metadata;
@@ -234,6 +302,45 @@ ifcopenshell::plugin::module ifcopenshell::plugin::manager::load(const std::file
return module(data);
}
PLUGIN_API void ifcopenshell::plugin::set_search_paths(const std::vector<std::string>& paths) {
std::lock_guard<std::mutex> lock(configured_search_paths_mutex());
configured_search_paths() = paths;
plugin_debug("set configured search paths count=" + std::to_string(configured_search_paths().size()));
for (const auto& path : configured_search_paths()) {
plugin_debug("configured search path " + path);
}
}
PLUGIN_API std::vector<std::string> ifcopenshell::plugin::search_paths() {
const auto paths = configured_search_paths_copy();
plugin_debug("get configured search paths count=" + std::to_string(paths.size()));
return paths;
}
PLUGIN_API void ifcopenshell::plugin::clear_search_paths() {
std::lock_guard<std::mutex> lock(configured_search_paths_mutex());
configured_search_paths().clear();
plugin_debug("cleared configured search paths");
}
PLUGIN_API std::filesystem::path ifcopenshell::plugin::add_search_paths_or_default(
manager& manager, std::filesystem::path (*default_search_path)())
{
const auto paths = configured_search_paths_copy();
if (!paths.empty()) {
plugin_debug("using configured plugin search paths; default module directory will not be resolved");
for (const auto& path : paths) {
manager.add_search_path(path);
}
return {};
}
plugin_debug("using default plugin search path from module directory");
const auto path = default_search_path();
manager.add_search_path(path);
return path;
}
ifcopenshell::plugin::abi_info ifcopenshell::plugin::host_abi() {
abi_info abi;
abi.debug_build = is_debug_build();
@@ -245,11 +352,20 @@ ifcopenshell::plugin::abi_info ifcopenshell::plugin::host_abi() {
void ifcopenshell::plugin::validate_abi(const abi_info& abi) {
const auto host = host_abi();
plugin_debug("validate_abi plugin_api=" + std::to_string(abi.plugin_api_version) +
" host_api=" + std::to_string(host.plugin_api_version) +
" plugin_compiler='" + abi.compiler_id + " " + abi.compiler_version + "'" +
" host_compiler='" + host.compiler_id + " " + host.compiler_version + "'" +
" plugin_pointer_size=" + std::to_string(abi.pointer_size) +
" host_pointer_size=" + std::to_string(host.pointer_size) +
" plugin_debug=" + std::to_string(abi.debug_build) +
" host_debug=" + std::to_string(host.debug_build));
if (abi.plugin_api_version == host.plugin_api_version &&
abi.pointer_size == host.pointer_size &&
abi.debug_build == host.debug_build &&
abi.compiler_id == host.compiler_id &&
abi.compiler_version == host.compiler_version) {
plugin_debug("validate_abi compatible");
return;
}
@@ -260,32 +376,41 @@ void ifcopenshell::plugin::validate_abi(const abi_info& abi) {
stream << ", host compiler " << host.compiler_id << " " << host.compiler_version << ")";
stream << " (plugin pointer size " << abi.pointer_size << ", host pointer size " << host.pointer_size << ")";
stream << " (plugin debug " << abi.debug_build << ", host debug " << host.debug_build << ")";
plugin_debug(stream.str());
throw std::runtime_error(stream.str());
}
std::filesystem::path ifcopenshell::plugin::module_directory(const void* symbol) {
plugin_debug("resolve module_directory for symbol " + std::to_string(reinterpret_cast<std::uintptr_t>(symbol)));
#ifdef _WIN32
HMODULE module_handle = nullptr;
if (!GetModuleHandleExW(
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
reinterpret_cast<LPCWSTR>(symbol),
&module_handle)) {
plugin_debug("module_directory failed in GetModuleHandleExW");
throw std::runtime_error("Unable to resolve module path");
}
wchar_t buffer[MAX_PATH];
const DWORD length = GetModuleFileNameW(module_handle, buffer, MAX_PATH);
if (length == 0) {
plugin_debug("module_directory failed in GetModuleFileNameW");
throw std::runtime_error("Unable to read module filename");
}
return std::filesystem::path(std::wstring(buffer, length)).parent_path();
const auto directory = std::filesystem::path(std::wstring(buffer, length)).parent_path();
plugin_debug("module_directory resolved " + path_string(directory));
return directory;
#else
Dl_info info;
if (dladdr(symbol, &info) == 0 || !info.dli_fname) {
plugin_debug("module_directory failed in dladdr");
throw std::runtime_error("Unable to resolve module path");
}
return std::filesystem::path(info.dli_fname).parent_path();
const auto directory = std::filesystem::path(info.dli_fname).parent_path();
plugin_debug("module_directory resolved " + path_string(directory));
return directory;
#endif
}
+6
View File
@@ -1,4 +1,10 @@
from pathlib import Path
import ifcopenshell
ifcopenshell.set_plugin_search_paths([str(Path(ifcopenshell.__file__).parent)])
model = ifcopenshell.file()
import ifcopenshell.api
import ifcopenshell.api.aggregate
import ifcopenshell.api.context
+8 -1
View File
@@ -77,7 +77,14 @@
const micropip = pyodide.pyimport("micropip");
await micropip.install("typing-extensions");
document.querySelector("#status2").innerHTML = "Loading IfcOpenShell";
await micropip.install("wheels/ifcopenshell-0.8.3+34a1bc6-cp313-cp313-emscripten_4_0_9_wasm32.whl");
// await micropip.install("wheels/ifcopenshell-0.8.6-cp313-cp313-emscripten_4_0_9_wasm32.whl");
await micropip.install("wheels/split2/ifcopenshell-0.8.6-cp313-cp313-pyodide_2025_0_wasm32.whl");
await micropip.install("wheels/split2/ifcopenshell_parse_schema_ifc4-0.8.6-cp313-cp313-pyodide_2025_0_wasm32.whl");
await micropip.install("wheels/split2/ifcopenshell_pure_python-0.8.6-py3-none-any.whl");
await micropip.install("wheels/split2/ifcopenshell_geometry_kernel_cgalsimple-0.8.6-cp313-cp313-pyodide_2025_0_wasm32.whl");
await micropip.install("wheels/split2/ifcopenshell_geometry_kernel_opencascade-0.8.6-cp313-cp313-pyodide_2025_0_wasm32.whl");
document.body.className = '';
+11 -2
View File
@@ -26,6 +26,12 @@
#include <mutex>
#include <set>
namespace ifcopenshell {
namespace plugin {
PLUGIN_API std::filesystem::path add_search_paths_or_default(manager& manager, std::filesystem::path (*default_search_path)());
}
}
namespace {
std::string document_serializer_key(const std::string& format) {
@@ -50,8 +56,11 @@ std::string document_serializer_plugin_prefix(const std::string& format = std::s
}
void add_document_serializer_search_paths(ifcopenshell::plugin::manager& manager) {
const auto directory = ifcopenshell::serializers::document_serializer_plugin_directory();
manager.add_search_path(directory);
const auto directory = ifcopenshell::plugin::add_search_paths_or_default(
manager, &ifcopenshell::serializers::document_serializer_plugin_directory);
if (directory.empty()) {
return;
}
const auto sibling_directory = directory.parent_path().parent_path() / "serializers" / directory.filename();
if (sibling_directory != directory && std::filesystem::exists(sibling_directory)) {
+11 -2
View File
@@ -24,6 +24,12 @@
#include <mutex>
#include <set>
namespace ifcopenshell {
namespace plugin {
PLUGIN_API std::filesystem::path add_search_paths_or_default(manager& manager, std::filesystem::path (*default_search_path)());
}
}
namespace {
std::string geometry_serializer_plugin_prefix(const std::string& format = std::string()) {
@@ -52,8 +58,11 @@ std::string geometry_serializer_format_from_extension(const std::string& extensi
}
void add_geometry_serializer_search_paths(ifcopenshell::plugin::manager& manager) {
const auto directory = ifcopenshell::serializers::geometry_serializer_plugin_directory();
manager.add_search_path(directory);
const auto directory = ifcopenshell::plugin::add_search_paths_or_default(
manager, &ifcopenshell::serializers::geometry_serializer_plugin_directory);
if (directory.empty()) {
return;
}
const auto sibling_directory = directory.parent_path().parent_path() / "serializers" / directory.filename();
if (sibling_directory != directory && std::filesystem::exists(sibling_directory)) {