diff --git a/source/metacall/CMakeLists.txt b/source/metacall/CMakeLists.txt index 27e350d8a..747eea648 100644 --- a/source/metacall/CMakeLists.txt +++ b/source/metacall/CMakeLists.txt @@ -57,6 +57,7 @@ set(headers ${include_path}/metacall_allocator.h ${include_path}/metacall_error.h ${include_path}/metacall_link.h + ${include_path}/metacall_self_register.h ) set(sources @@ -66,6 +67,7 @@ set(sources ${source_path}/metacall_allocator.c ${source_path}/metacall_error.c ${source_path}/metacall_link.c + ${source_path}/metacall_self_register.c ) if(OPTION_FORK_SAFE) diff --git a/source/metacall/include/metacall/metacall_self_register.h b/source/metacall/include/metacall/metacall_self_register.h new file mode 100644 index 000000000..aec546155 --- /dev/null +++ b/source/metacall/include/metacall/metacall_self_register.h @@ -0,0 +1,36 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2026 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef METACALL_SELF_REGISTER_H +#define METACALL_SELF_REGISTER_H 1 + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +METACALL_API int metacall_self_register_all(void); + +#ifdef __cplusplus +} +#endif + +#endif /* METACALL_SELF_REGISTER_H */ diff --git a/source/metacall/source/metacall.c b/source/metacall/source/metacall.c index fc0f1ba7b..b12d7bbff 100644 --- a/source/metacall/source/metacall.c +++ b/source/metacall/source/metacall.c @@ -22,6 +22,7 @@ #include #include +#include #include @@ -325,6 +326,12 @@ int metacall_initialize(void) log_write("metacall", LOG_LEVEL_ERROR, "Invalid MetaCall link initialization"); } + /* Self-register metacall APIs so loaded scripts can call them directly */ + if (metacall_self_register_all() != 0) + { + log_write("metacall", LOG_LEVEL_WARNING, "MetaCall self-registration failed"); + } + /* Load core plugins */ if (metacall_plugin_extension_load() != 0) { diff --git a/source/metacall/source/metacall_self_register.c b/source/metacall/source/metacall_self_register.c new file mode 100644 index 000000000..a66252f02 --- /dev/null +++ b/source/metacall/source/metacall_self_register.c @@ -0,0 +1,53 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2026 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* + * Hand-written prototype of metacall self-registration. This file will be + * replaced by build-time generated bindings once the bindgen tool exists. + * For now it registers a single API (metacall_print_info) to validate the + * end-to-end approach: scripts in any loaded language can call metacall_* + * APIs without going through the C loader. + */ + +#include +#include + +#include + +static void *metacall_self_register_print_info(size_t argc, void *args[], void *data) +{ + (void)argc; + (void)args; + (void)data; + + const char *info = metacall_print_info(); + + return metacall_value_create_string(info, info == NULL ? 0 : strlen(info)); +} + +int metacall_self_register_all(void) +{ + if (metacall_register("metacall_print_info", &metacall_self_register_print_info, NULL, METACALL_STRING, 0) != 0) + { + return 1; + } + + return 0; +} diff --git a/source/tests/CMakeLists.txt b/source/tests/CMakeLists.txt index 17a431547..a65228126 100644 --- a/source/tests/CMakeLists.txt +++ b/source/tests/CMakeLists.txt @@ -237,6 +237,7 @@ add_subdirectory(metacall_c_lib_test) add_subdirectory(metacall_c_metacall_test) add_subdirectory(metacall_version_test) add_subdirectory(metacall_loader_types_test) +add_subdirectory(metacall_self_register_test) add_subdirectory(metacall_dynlink_path_test) add_subdirectory(metacall_library_path_without_env_vars_test) add_subdirectory(metacall_ext_test) diff --git a/source/tests/metacall_self_register_test/CMakeLists.txt b/source/tests/metacall_self_register_test/CMakeLists.txt new file mode 100644 index 000000000..c80037ff6 --- /dev/null +++ b/source/tests/metacall_self_register_test/CMakeLists.txt @@ -0,0 +1,157 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-self-register-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_self_register_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + OPTION_BUILD_LOADERS_PY +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_self_register_test/source/main.cpp b/source/tests/metacall_self_register_test/source/main.cpp new file mode 100644 index 000000000..fb41f44af --- /dev/null +++ b/source/tests/metacall_self_register_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2026 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_self_register_test/source/metacall_self_register_test.cpp b/source/tests/metacall_self_register_test/source/metacall_self_register_test.cpp new file mode 100644 index 000000000..617d99172 --- /dev/null +++ b/source/tests/metacall_self_register_test/source/metacall_self_register_test.cpp @@ -0,0 +1,54 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2026 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_self_register_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_self_register_test, DefaultConstructor) +{ + ASSERT_EQ((int)0, (int)metacall_initialize()); + +#if defined(OPTION_BUILD_LOADERS_PY) + { + static const char script[] = + "def call_print_info():\n" + " return metacall_print_info()\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("py", script, sizeof(script), NULL)); + + void *ret = metacallv("call_print_info", metacall_null_args); + + ASSERT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((enum metacall_value_id)METACALL_STRING, (enum metacall_value_id)metacall_value_id(ret)); + EXPECT_EQ((int)0, strncmp(metacall_value_to_string(ret), "MetaCall", 8)); + + metacall_value_destroy(ret); + } +#endif + + metacall_destroy(); +}