diff --git a/cmake/FindLibGit2.cmake b/cmake/FindLibGit2.cmake new file mode 100644 index 000000000..787b5319c --- /dev/null +++ b/cmake/FindLibGit2.cmake @@ -0,0 +1,78 @@ +# +# CMake Find LibGit2 Library by Parra Studios +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +# + +# Find libgit2 library and include paths +# +# LibGit2_FOUND - True if LibGit2 was found +# LibGit2_INCLUDE_DIR - LibGit2 headers path +# LibGit2_VERSION - LibGit2 version +# LibGit2_VERSION_MAJOR - LibGit2 major version +# LibGit2_VERSION_MINOR - LibGit2 minor version +# LibGit2_VERSION_REVISION - LibGit2 patch version +# LibGit2_LIBRARY - LibGit2 shared library +# LibGit2_LIBRARY_DIR - LibGit2 shared library folder +# + +# Prevent vervosity if already included +if(LibGit2_LIBRARY) + set(LibGit2_FIND_QUIETLY TRUE) +endif() + +# Include package manager +include(FindPackageHandleStandardArgs) + +# Find via PkgConfig +find_package(PkgConfig QUIET) +pkg_check_modules(PKG_GIT2 QUIET libgit2) + +if(NOT LibGit2_DEFINITIONS) + set(LibGit2_DEFINITIONS ${PKG_GIT2_CFLAGS_OTHER}) +endif() + +if(NOT LibGit2_INCLUDE_DIR) + find_path(LibGit2_INCLUDE_DIR + NAMES git2.h + HINTS ${PKG_GIT2_INCLUDE_DIRS} + ) +endif() + +if(NOT LibGit2_VERSION AND LibGit2_INCLUDE_DIR) + file(STRINGS "${LibGit2_INCLUDE_DIR}/git2/version.h" LibGit2_VERSION_MAJOR REGEX "^#define LIBGIT2_VER_MAJOR +([0-9]+)") + string(REGEX MATCH "([0-9]+)$" LibGit2_VERSION_MAJOR ${LibGit2_VERSION_MAJOR}) + + file(STRINGS "${LibGit2_INCLUDE_DIR}/git2/version.h" LibGit2_VERSION_MINOR REGEX "^#define LIBGIT2_VER_MINOR +([0-9]+)") + string(REGEX MATCH "([0-9]+)$" LibGit2_VERSION_MINOR ${LibGit2_VERSION_MINOR}) + + file(STRINGS "${LibGit2_INCLUDE_DIR}/git2/version.h" LibGit2_VERSION_REVISION REGEX "^#define LIBGIT2_VER_REVISION +([0-9]+)") + string(REGEX MATCH "([0-9]+)$" LibGit2_VERSION_REVISION ${LibGit2_VERSION_REVISION}) + + set(LibGit2_VERSION "${LibGit2_VERSION_MAJOR}.${LibGit2_VERSION_MINOR}.${LibGit2_VERSION_REVISION}") +endif() + +if(NOT LibGit2_LIBRARY) + find_library(LibGit2_LIBRARY + NAMES git2 + HINTS ${PKG_GIT2_LIBRARY_DIRS} + ) +endif() + +set(LibGit2_LIBRARIES ${LibGit2_LIBRARY}) +set(LibGit2_INCLUDE_DIRS ${LibGit2_INCLUDE_DIR}) +get_filename_component(LibGit2_LIBRARY_DIR ${LibGit2_LIBRARY} DIRECTORY) + +# Define package +find_package_handle_standard_args(LibGit2 + FOUND_VAR + LibGit2_FOUND + REQUIRED_VARS + LibGit2_LIBRARY + LibGit2_LIBRARY_DIR + LibGit2_INCLUDE_DIR + LibGit2_INCLUDE_DIRS + VERSION_VAR + LibGit2_VERSION +) + +mark_as_advanced(LibGit2_LIBRARY LibGit2_LIBRARY_DIR LibGit2_INCLUDE_DIR) diff --git a/source/loaders/c_loader/source/c_loader_impl.cpp b/source/loaders/c_loader/source/c_loader_impl.cpp index 52ead7b7c..4ad6689bf 100644 --- a/source/loaders/c_loader/source/c_loader_impl.cpp +++ b/source/loaders/c_loader/source/c_loader_impl.cpp @@ -81,6 +81,8 @@ typedef struct loader_impl_c_handle_base_type virtual ~loader_impl_c_handle_base_type() {} + virtual bool recursive_includes() = 0; + virtual int discover(loader_impl impl, context ctx) = 0; virtual const void *symbol(std::string &name) = 0; @@ -136,6 +138,11 @@ typedef struct loader_impl_c_handle_tcc_type : loader_impl_c_handle_base_type } } + bool recursive_includes() + { + return false; + } + bool initialize(loader_impl_c c_impl) { this->state = tcc_new(); @@ -232,6 +239,11 @@ typedef struct loader_impl_c_handle_dynlink_type : loader_impl_c_handle_base_typ } } + bool recursive_includes() + { + return true; + } + bool initialize(loader_impl_c c_impl, const loader_path path) { std::string lib_path_str(path); @@ -1022,12 +1034,18 @@ static int c_loader_impl_discover_signature(loader_impl impl, loader_impl_c_hand symbol_name.insert(0, 1, '_'); #endif + if (scope_get(sp, symbol_name.c_str()) != NULL) + { + log_write("metacall", LOG_LEVEL_WARNING, "Symbol '%s' redefined, skipping the function", func_name.c_str()); + return 0; + } + const void *address = c_handle->symbol(symbol_name); if (address == NULL) { - log_write("metacall", LOG_LEVEL_ERROR, "Symbol '%s' not found, skipping the function", func_name.c_str()); - return 1; + log_write("metacall", LOG_LEVEL_WARNING, "Symbol '%s' not found, skipping the function", func_name.c_str()); + return 0; } loader_impl_c_function c_function = new loader_impl_c_function_type(address); @@ -1080,9 +1098,13 @@ static CXChildVisitResult c_loader_impl_discover_visitor(CXCursor cursor, CXCurs { c_loader_impl_discover_visitor_data visitor_data = static_cast(data); - if (clang_Location_isFromMainFile(clang_getCursorLocation(cursor)) == 0) + /* Include recursively when disabled, include only the header inlcuded is populated when enabled */ + if (visitor_data->c_handle->recursive_includes() == false) { - return CXChildVisit_Continue; + if (clang_Location_isFromMainFile(clang_getCursorLocation(cursor)) == 0) + { + return CXChildVisit_Continue; + } } CXCursorKind kind = clang_getCursorKind(cursor); @@ -1102,6 +1124,7 @@ static CXChildVisitResult c_loader_impl_discover_visitor(CXCursor cursor, CXCurs static int c_loader_impl_discover_ast(loader_impl impl, loader_impl_c_handle_base c_handle, context ctx) { + loader_impl_c c_impl = static_cast(loader_impl_get(impl)); c_loader_impl_discover_visitor_data_type data = { impl, c_handle, @@ -1109,12 +1132,24 @@ static int c_loader_impl_discover_ast(loader_impl impl, loader_impl_c_handle_bas 0 }; + std::vector includes; + std::vector command_line_args; + + /* Otherwise, check the execution paths */ + for (auto exec_path : c_impl->execution_paths) + { + includes.push_back("-I" + exec_path); + command_line_args.push_back(includes.back().c_str()); + } + for (std::string file : c_handle->files) { - CXIndex index = clang_createIndex(0, 0); + /* Define the command line arguments (simulating compiler flags) */ + CXIndex index = clang_createIndex(0, 1); CXTranslationUnit unit = clang_parseTranslationUnit( index, - file.c_str(), nullptr, 0, + file.c_str(), + command_line_args.data(), command_line_args.size(), nullptr, 0, CXTranslationUnit_None); diff --git a/source/loaders/node_loader/source/node_loader_port.cpp b/source/loaders/node_loader/source/node_loader_port.cpp index ed681f1e9..a072f934f 100644 --- a/source/loaders/node_loader/source/node_loader_port.cpp +++ b/source/loaders/node_loader/source/node_loader_port.cpp @@ -281,6 +281,94 @@ napi_value node_loader_port_metacall_await(napi_env env, napi_callback_info info return promise; } +/** +* @brief +* Define an execution path into a runtime +* +* @param[in] env +* N-API reference to the enviroment +* +* @param[in] info +* Reference to the call information +* +* @return +* TODO: Not implemented yet +*/ +napi_value node_loader_port_metacall_execution_path(napi_env env, napi_callback_info info) +{ + const size_t args_size = 2; + size_t argc = args_size, tag_length, path_length; + napi_value argv[args_size]; + + /* Get arguments */ + napi_status status = napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr); + + node_loader_impl_exception(env, status); + + /* Get tag length */ + status = napi_get_value_string_utf8(env, argv[0], nullptr, 0, &tag_length); + + node_loader_impl_exception(env, status); + + /* Allocate tag */ + char *tag = new char[tag_length + 1]; + + if (tag == nullptr) + { + napi_throw_error(env, nullptr, "MetaCall could not define an execution path, tag allocation failed"); + return nullptr; + } + + /* Get tag */ + status = napi_get_value_string_utf8(env, argv[0], tag, tag_length + 1, &tag_length); + + node_loader_impl_exception(env, status); + + /* Get path length */ + status = napi_get_value_string_utf8(env, argv[1], nullptr, 0, &path_length); + + node_loader_impl_exception(env, status); + + size_t path_size = path_length + 1; + + /* Allocate path */ + char *path = new char[path_size]; + + if (path == nullptr) + { + napi_throw_error(env, nullptr, "MetaCall could not define an execution path, path allocation failed"); + delete[] tag; + return nullptr; + } + + /* Get path */ + status = napi_get_value_string_utf8(env, argv[1], path, path_size, &path_length); + + node_loader_impl_exception(env, status); + + /* Obtain NodeJS loader implementation */ + loader_impl impl = loader_get_impl(node_loader_tag); + loader_impl_node node_impl = (loader_impl_node)loader_impl_get(impl); + + /* Store current reference of the environment */ + node_loader_impl_env(node_impl, env); + + /* Define execution path */ + if (metacall_execution_path(tag, path) != 0) + { + napi_throw_error(env, nullptr, "MetaCall could not define an execution path"); + } + + /* Release current reference of the environment */ + // node_loader_impl_env(node_impl, nullptr); + + delete[] tag; + delete[] path; + + /* TODO: Return value and logs */ + return nullptr; +} + napi_value node_loader_port_metacall_load_from_file(napi_env env, napi_callback_info info) { /* TODO: Detect if input argument types are valid */ @@ -628,6 +716,176 @@ napi_value node_loader_port_metacall_load_from_memory_export(napi_env env, napi_ return v_exports; } +/** +* @brief +* Load a package by tag +* +* @param[in] env +* N-API reference to the enviroment +* +* @param[in] info +* Reference to the call information +* +* @return +* TODO: Not implemented yet +*/ +napi_value node_loader_port_metacall_load_from_package(napi_env env, napi_callback_info info) +{ + const size_t args_size = 2; + size_t argc = args_size, tag_length, package_length; + napi_value argv[args_size]; + + /* Get arguments */ + napi_status status = napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr); + + node_loader_impl_exception(env, status); + + /* Get tag length */ + status = napi_get_value_string_utf8(env, argv[0], nullptr, 0, &tag_length); + + node_loader_impl_exception(env, status); + + /* Allocate tag */ + char *tag = new char[tag_length + 1]; + + if (tag == nullptr) + { + napi_throw_error(env, nullptr, "MetaCall could not load from package, tag allocation failed"); + return nullptr; + } + + /* Get tag */ + status = napi_get_value_string_utf8(env, argv[0], tag, tag_length + 1, &tag_length); + + node_loader_impl_exception(env, status); + + /* Get package length */ + status = napi_get_value_string_utf8(env, argv[1], nullptr, 0, &package_length); + + node_loader_impl_exception(env, status); + + size_t package_size = package_length + 1; + + /* Allocate package */ + char *package = new char[package_size]; + + if (package == nullptr) + { + napi_throw_error(env, nullptr, "MetaCall could not load from package, package allocation failed"); + delete[] tag; + return nullptr; + } + + /* Get package */ + status = napi_get_value_string_utf8(env, argv[1], package, package_size, &package_length); + + node_loader_impl_exception(env, status); + + /* Obtain NodeJS loader implementation */ + loader_impl impl = loader_get_impl(node_loader_tag); + loader_impl_node node_impl = (loader_impl_node)loader_impl_get(impl); + + /* Store current reference of the environment */ + node_loader_impl_env(node_impl, env); + + /* Load the package */ + if (metacall_load_from_package(tag, package, NULL) != 0) + { + napi_throw_error(env, nullptr, "MetaCall could not load a package"); + } + + /* Release current reference of the environment */ + // node_loader_impl_env(node_impl, nullptr); + + delete[] tag; + delete[] package; + + /* TODO: Return value and logs */ + return nullptr; +} + +napi_value node_loader_port_metacall_load_from_package_export(napi_env env, napi_callback_info info) +{ + const size_t args_size = 2; + size_t argc = args_size, tag_length, package_length; + napi_value argv[args_size]; + + /* Get arguments */ + napi_status status = napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr); + + node_loader_impl_exception(env, status); + + /* Get tag length */ + status = napi_get_value_string_utf8(env, argv[0], nullptr, 0, &tag_length); + + node_loader_impl_exception(env, status); + + /* Allocate tag */ + char *tag = new char[tag_length + 1]; + + if (tag == nullptr) + { + napi_throw_error(env, nullptr, "MetaCall could not load from memory, tag allocation failed"); + return nullptr; + } + + /* Get tag */ + status = napi_get_value_string_utf8(env, argv[0], tag, tag_length + 1, &tag_length); + + node_loader_impl_exception(env, status); + + /* Get package length */ + status = napi_get_value_string_utf8(env, argv[1], nullptr, 0, &package_length); + + node_loader_impl_exception(env, status); + + size_t package_size = package_length + 1; + + /* Allocate package */ + char *package = new char[package_size]; + + if (package == nullptr) + { + napi_throw_error(env, nullptr, "MetaCall could not load from package, package allocation failed"); + delete[] tag; + return nullptr; + } + + /* Get package */ + status = napi_get_value_string_utf8(env, argv[1], package, package_size, &package_length); + + node_loader_impl_exception(env, status); + + /* Obtain NodeJS loader implementation */ + loader_impl impl = loader_get_impl(node_loader_tag); + loader_impl_node node_impl = (loader_impl_node)loader_impl_get(impl); + + /* Store current reference of the environment */ + node_loader_impl_env(node_impl, env); + + void *handle = NULL; + + /* Load package from package */ + if (metacall_load_from_package(tag, package, &handle) != 0) + { + napi_throw_error(env, nullptr, "MetaCall could not load from package"); + } + + /* Release current reference of the environment */ + // node_loader_impl_env(node_impl, nullptr); + + delete[] tag; + delete[] package; + + void *exports = metacall_handle_export(handle); + + napi_value v_exports = node_loader_impl_value_to_napi(node_impl, env, exports); + + node_loader_impl_finalizer(env, v_exports, exports); + + return v_exports; +} + /** * @brief * Loads a script from configuration path @@ -816,10 +1074,13 @@ void node_loader_port_exports(napi_env env, napi_value exports) x(metacall); \ x(metacallfms); \ x(metacall_await); \ + x(metacall_execution_path); \ x(metacall_load_from_file); \ x(metacall_load_from_file_export); \ x(metacall_load_from_memory); \ x(metacall_load_from_memory_export); \ + x(metacall_load_from_package); \ + x(metacall_load_from_package_export); \ x(metacall_load_from_configuration); \ x(metacall_load_from_configuration_export); \ x(metacall_inspect); \ diff --git a/source/ports/node_port/index.d.ts b/source/ports/node_port/index.d.ts index 7b1d6d1bd..3be9a4809 100644 --- a/source/ports/node_port/index.d.ts +++ b/source/ports/node_port/index.d.ts @@ -1,10 +1,13 @@ declare module 'metacall' { export function metacall(name: string, ...args: any): any; export function metacallfms(name: string, buffer: string): any; + export function metacall_execution_path(tag: string, path: string): number; export function metacall_load_from_file(tag: string, paths: string[]): number; export function metacall_load_from_file_export(tag: string, paths: string[]): any; export function metacall_load_from_memory(tag: string, code: string): number; export function metacall_load_from_memory_export(tag: string, code: string): any; + export function metacall_load_from_package(tag: string, pkg: string): number; + export function metacall_load_from_package_export(tag: string, pkg: string): any; export function metacall_load_from_configuration(path: string): number; export function metacall_load_from_configuration_export(path: string): any; export function metacall_inspect(): any; diff --git a/source/ports/node_port/index.js b/source/ports/node_port/index.js index 8d690b6d8..6d15c028e 100644 --- a/source/ports/node_port/index.js +++ b/source/ports/node_port/index.js @@ -102,6 +102,18 @@ const metacall_await = (name, ...args) => { return addon.metacall_await(name, ...args); }; +const metacall_execution_path = (tag, path) => { + if (Object.prototype.toString.call(tag) !== '[object String]') { + throw Error('Tag should be a string indicating the id of the loader to be used [py, rb, cs, js, node, mock...].'); + } + + if (Object.prototype.toString.call(path) !== '[object String]') { + throw Error('The path should be of string type.'); + } + + return addon.metacall_execution_path(tag, path); +}; + const metacall_load_from_file = (tag, paths) => { if (Object.prototype.toString.call(tag) !== '[object String]') { throw Error('Tag should be a string indicating the id of the loader to be used [py, rb, cs, js, node, mock...].'); @@ -150,6 +162,30 @@ const metacall_load_from_memory_export = (tag, code) => { return addon.metacall_load_from_memory_export(tag, code); }; +const metacall_load_from_package = (tag, pkg) => { + if (Object.prototype.toString.call(tag) !== '[object String]') { + throw Error('Tag should be a string indicating the id of the loader to be used [py, rb, cs, js, node, mock...].'); + } + + if (Object.prototype.toString.call(pkg) !== '[object String]') { + throw Error('Package should be a string with the id or path to the package.'); + } + + return addon.metacall_load_from_package(tag, pkg); +}; + +const metacall_load_from_package_export = (tag, pkg) => { + if (Object.prototype.toString.call(tag) !== '[object String]') { + throw Error('Tag should be a string indicating the id of the loader to be used [py, rb, cs, js, node, mock...].'); + } + + if (Object.prototype.toString.call(pkg) !== '[object String]') { + throw Error('Package should be a string with the id or path to the package.'); + } + + return addon.metacall_load_from_package_export(tag, pkg); +}; + const metacall_load_from_configuration = (path) => { if (Object.prototype.toString.call(path) !== '[object String]') { throw Error('Path should be a string indicating the path where the metacall.json is located.'); @@ -207,10 +243,13 @@ const module_exports = { metacallfms, metacall_await, metacall_inspect, + metacall_execution_path, metacall_load_from_file, metacall_load_from_file_export, metacall_load_from_memory, metacall_load_from_memory_export, + metacall_load_from_package, + metacall_load_from_package_export, metacall_load_from_configuration, metacall_load_from_configuration_export, metacall_handle, diff --git a/source/tests/CMakeLists.txt b/source/tests/CMakeLists.txt index eb70ae1c7..aca1cfdaa 100644 --- a/source/tests/CMakeLists.txt +++ b/source/tests/CMakeLists.txt @@ -128,6 +128,7 @@ add_subdirectory(metacall_node_reentrant_test) add_subdirectory(metacall_node_port_test) add_subdirectory(metacall_node_port_await_test) add_subdirectory(metacall_node_port_rs_test) +add_subdirectory(metacall_node_port_c_lib_test) add_subdirectory(metacall_node_python_port_mock_test) add_subdirectory(metacall_node_python_port_ruby_test) add_subdirectory(metacall_node_python_ruby_test) diff --git a/source/tests/metacall_node_port_c_lib_test/CMakeLists.txt b/source/tests/metacall_node_port_c_lib_test/CMakeLists.txt new file mode 100644 index 000000000..0f4436b10 --- /dev/null +++ b/source/tests/metacall_node_port_c_lib_test/CMakeLists.txt @@ -0,0 +1,167 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_C OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_NODE) + return() +endif() + +# +# External dependencies +# + +find_package(LibGit2) + +if(NOT LibGit2_FOUND) + message(WARNING "LibGit2 libraries not found, skipping test metacall-node-port-c-lib-test") + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-port-c-lib-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_node_port_c_lib_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} + + # NodeJS Port path + METACALL_NODE_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/node_port/index.js" + + # LibGit2 paths + LIBGIT2_LIBRARY_DIR="${LibGit2_LIBRARY_DIR}" + LIBGIT2_INCLUDE_DIR="${LibGit2_INCLUDE_DIR}" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Linker options +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_port + node_loader + c_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_node_port_c_lib_test/source/main.cpp b/source/tests/metacall_node_port_c_lib_test/source/main.cpp new file mode 100644 index 000000000..11ddf3f59 --- /dev/null +++ b/source/tests/metacall_node_port_c_lib_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 - 2024 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_node_port_c_lib_test/source/metacall_node_port_c_lib_test.cpp b/source/tests/metacall_node_port_c_lib_test/source/metacall_node_port_c_lib_test.cpp new file mode 100644 index 000000000..faa944974 --- /dev/null +++ b/source/tests/metacall_node_port_c_lib_test/source/metacall_node_port_c_lib_test.cpp @@ -0,0 +1,57 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2024 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 +#include + +class metacall_node_port_c_lib_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_port_c_lib_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + static const char buffer[] = + /* NodeJS */ + "const assert = require('assert');\n" + "const { metacall_execution_path, metacall_load_from_package_export } = require('" METACALL_NODE_PORT_PATH "');\n" + /* C Lib Paths */ + "metacall_execution_path('c', '" LIBGIT2_INCLUDE_DIR "');\n" + "metacall_execution_path('c', '" LIBGIT2_LIBRARY_DIR "');\n" + /* C Lib Require */ + "const git2 = metacall_load_from_package_export('c', 'git2');\n" + "const { git_libgit2_init, git_libgit2_shutdown } = git2;\n" + "console.log(git2);\n" + /* C Lib Assert */ + "assert(git_libgit2_init() >= 0, 'libgit2 initialization failed');\n" + "git_libgit2_shutdown();\n" + "\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + + metacall_destroy(); +} diff --git a/tools/metacall-environment.ps1 b/tools/metacall-environment.ps1 index 1638de22c..dd2191520 100755 --- a/tools/metacall-environment.ps1 +++ b/tools/metacall-environment.ps1 @@ -126,6 +126,27 @@ function Set-Nodejs { Write-Output "-DNodeJS_EXECUTABLE=""$NodeDir/node.exe""" >> $EnvOpts Write-Output "-DNodeJS_LIBRARY_NAME=""libnode.dll""" >> $EnvOpts Write-Output "-DNodeJS_LIBRARY_NAME_PATH=""$NodeDir/lib/libnode.dll""" >> $EnvOpts + + if ($Arguments -contains "c") { + # Required for test source/tests/metacall_node_port_c_lib_test + if (!(Test-Path -Path "$DepsDir\libgit2")) { + # Clone libgit2 + git clone --depth 1 --branch v1.8.4 https://github.com/libgit2/libgit2 + } + + $InstallDir = "$DepsDir\libgit2\build\dist" + + mkdir "$DepsDir\libgit2\build" + mkdir "$InstallDir" + Set-Location "$DepsDir\libgit2\build" + + cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTS=OFF -DBUILD_CLI=OFF .. + cmake --build . "-j$((Get-CimInstance Win32_ComputerSystem).NumberOfLogicalProcessors)" + cmake --install . --prefix "$InstallDir" + + Write-Output "-DLibGit2_LIBRARY=""$InstallDir\lib\git2.lib""" >> $EnvOpts + Write-Output "-DLibGit2_INCLUDE_DIR=""$InstallDir\include""" >> $EnvOpts + } } function Set-Java { diff --git a/tools/metacall-environment.sh b/tools/metacall-environment.sh index 304fa6a02..d2983330b 100755 --- a/tools/metacall-environment.sh +++ b/tools/metacall-environment.sh @@ -464,11 +464,18 @@ sub_nodejs(){ cd $ROOT_DIR if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ $INSTALL_C = 1 ]; then + # Required for test source/tests/metacall_node_port_c_lib_test + INSTALL_LIBGIT2="libgit2-dev" + else + INSTALL_LIBGIT2="" + fi + if [ "${LINUX_DISTRO}" = "debian" ] || [ "${LINUX_DISTRO}" = "ubuntu" ]; then # Note that Python is required for GYP - $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends python3 g++ make nodejs npm curl + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends python3 g++ make nodejs npm curl $INSTALL_LIBGIT2 elif [ "${LINUX_DISTRO}" = "alpine" ]; then - $SUDO_CMD apk add --no-cache python3 g++ make nodejs nodejs-dev npm curl + $SUDO_CMD apk add --no-cache python3 g++ make nodejs nodejs-dev npm curl $INSTALL_LIBGIT2 # Build dependencies (note libexecinfo-dev is not available in Alpine 3.17) $SUDO_CMD apk add --no-cache --repository=https://dl-cdn.alpinelinux.org/alpine/v3.16/main linux-headers libexecinfo libexecinfo-dev @@ -557,6 +564,12 @@ sub_nodejs(){ # Configure NPM path echo "-DNPM_ROOT=$NODE_PREFIX/bin" >> $CMAKE_CONFIG_PATH + if [ $INSTALL_C = 1 ]; then + # Required for test source/tests/metacall_node_port_c_lib_test + brew install libgit2@1.8 + brew link libgit2@1.8 --force --overwrite + fi + # fi fi }