From d7d02e321626db4ae51ab8a66ccd12f1eb594957 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Mon, 22 Mar 2021 20:20:14 -0600 Subject: [PATCH 01/16] Embed Ruby + Python into test exe working --- .clang-format | 92 ++++++++++++++ CMakeLists.txt | 308 ++++++++++++++++++++++++---------------------- Measure.cpp | 10 ++ Measure.hpp | 37 ++++++ Measure.i | 25 ++++ Model.cpp | 13 ++ Model.hpp | 18 +++ Person.cpp | 32 ----- Person.hpp | 49 -------- Person.i | 46 ------- PythonEngine.cpp | 35 ++++++ PythonEngine.hpp | 31 +++++ RubyEngine.cpp | 37 ++++++ RubyEngine.hpp | 27 ++++ Runner.hpp | 26 ++++ ScriptEngine.hpp | 49 ++++++++ SpecialRunner.hpp | 26 ++++ main.cpp | 15 ++- 18 files changed, 597 insertions(+), 279 deletions(-) create mode 100644 .clang-format create mode 100644 Measure.cpp create mode 100644 Measure.hpp create mode 100644 Measure.i create mode 100644 Model.cpp create mode 100644 Model.hpp delete mode 100644 Person.cpp delete mode 100644 Person.hpp delete mode 100644 Person.i create mode 100644 PythonEngine.cpp create mode 100644 PythonEngine.hpp create mode 100644 RubyEngine.cpp create mode 100644 RubyEngine.hpp create mode 100644 Runner.hpp create mode 100644 ScriptEngine.hpp create mode 100644 SpecialRunner.hpp diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..2dd146e --- /dev/null +++ b/.clang-format @@ -0,0 +1,92 @@ +--- +Language: Cpp +AccessModifierOffset: -1 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Left +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: true +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: true +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: true + AfterControlStatement: false + AfterEnum: true + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: true + AfterUnion: true + AfterExternBlock: true + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: true +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakConstructorInitializersBeforeComma: false +BreakStringLiterals: true +ColumnLimit: 150 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 2 +ContinuationIndentWidth: 2 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +IncludeBlocks: Preserve +IndentCaseLabels: true +IndentWidth: 2 +IndentPPDirectives: AfterHash +IndentWrappedFunctionNames: true +NamespaceIndentation: Inner +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Left +ReflowComments: false +SpaceAfterCStyleCast: false +# SpaceAfterLogicalNot: false # No longer available in clang-format 6.0 +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +# SpaceBeforeCpp11BracedList: true # No longer available in clang-format 6.0 +# SpaceBeforeCtorInitializerColon: true # No longer available in clang-format 6.0 +# SpaceBeforeInheritanceColon: true # No longer available in clang-format 6.0 +SpaceBeforeParens: ControlStatements +# SpaceBeforeRangeBasedForLoopColon: true # No longer available in clang-format 6.0 +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +SortIncludes: false +SortUsingDeclarations: false +Standard: Cpp11 +TabWidth: 2 +UseTab: Never +... diff --git a/CMakeLists.txt b/CMakeLists.txt index a8d8a8f..c4dadc7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,24 @@ -cmake_minimum_required(VERSION 3.18.0) -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -# Do not enable compiler specific extensions, for eg on GCC use -std=c++1z (=c++17) and not -std=gnu++17 -set(CMAKE_CXX_EXTENSIONS OFF) -set(CMAKE_VERBOSE_MAKEFILE TRUE) +cmake_minimum_required(VERSION 3.17.5) + +add_library(cpp_warning_options INTERFACE) +add_library(cpp_compile_options INTERFACE) + +target_compile_features(cpp_compile_options INTERFACE cxx_std_17) + +if(MSVC) + target_compile_options(cpp_warning_options INTERFACE /W4 /Werror) +else() + target_compile_options( + cpp_warning_options INTERFACE -Wall -Wextra -Wconversion -Wpedantic -Werror) + target_compile_options(cpp_compile_options + INTERFACE -fsanitize=undefined,address) + target_link_options(cpp_compile_options INTERFACE + -fsanitize=undefined,address) +endif() +# Do not enable compiler specific extensions, for eg on GCC use -std=c++1z +# (=c++17) and not -std=gnu++17 +set(CMAKE_CXX_EXTENSIONS OFF) # Use ccache is available, has to be before "project()" find_program(CCACHE_PROGRAM ccache) @@ -13,59 +27,24 @@ if(CCACHE_PROGRAM) set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}") endif() - project(test_swig VERSION 0.0.1) # Set a default build type if none was specified if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) message(STATUS "Setting build type to 'Release' as none was specified.") - set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE) + set(CMAKE_BUILD_TYPE + Release + CACHE STRING "Choose the type of build." FORCE) # Set the possible values of build type for cmake-gui set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" - "MinSizeRel" "RelWithDebInfo") -endif() - -############################################################################### -# N I N J A # -############################################################################### - -# Xcode/Ninja generators undefined MAKE -if(CMAKE_GENERATOR MATCHES "Make") - set(MAKE "$(MAKE)") -else() - set(MAKE make) + "MinSizeRel" "RelWithDebInfo") endif() -# Ninja support: has to be atop for it to take effect before anything else is done -# Add Color Output if Using Ninja -macro(AddCXXFlagIfSupported flag test) - CHECK_CXX_COMPILER_FLAG(${flag} ${test}) - if(${${test}}) - message("adding ${flag}") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${flag}") - endif() -endmacro() - -if("Ninja" STREQUAL ${CMAKE_GENERATOR}) - # Clang - if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - AddCXXFlagIfSupported(-fcolor-diagnostics COMPILER_SUPPORTS_fcolor-diagnostics) - endif() - - # g++ - if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - # For some reason it doesn't say its supported, but it works... - # AddCXXFlagIfSupported(-fdiagnostics-color COMPILER_SUPPORTS_fdiagnostics-color) - message(STATUS "Ninja: Forcing -fdiagnostics-color=always") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color=always") - endif() -endif() - - -# Search first in the binary dir, where conan will install finders, then -# search for modules in the root dir to override cmake ones -# Start with ROOT, then PROJECT_BINARY_DIR -list(APPEND CMAKE_MODULE_PATH "${CMAKE_BINARY_DIR} ${PROJECT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}/CMake") +# Search first in the binary dir, where conan will install finders, then search +# for modules in the root dir to override cmake ones Start with ROOT, then +# CMAKE_CURRENT_BINARY_DIR +list(APPEND CMAKE_MODULE_PATH "${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}" + "${PROJECT_SOURCE_DIR}/CMake") include_directories("${CMAKE_CURRENT_BINARY_DIR}") # Add to include path @@ -73,69 +52,56 @@ include_directories("${CMAKE_CURRENT_BINARY_DIR}") # Project source directory include_directories("${PROJECT_SOURCE_DIR}") -############################################################################### -# C M A K E C O N T R O L # -############################################################################### +# ############################################################################## +# C M A K E C O N T R O L # +# ############################################################################## -# High level project configuration -# Do we actually want everything to go to CMAKE_BINARY_DIR/Products, -# so that when you build OpenStudioApplication you get both OpenStudio (core) and Application in the same place? -set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/Products") -set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/Products") -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/Products") +# High level project configuration Do we actually want everything to go to +# CMAKE_BINARY_DIR/Products, so that when you build OpenStudioApplication you +# get both OpenStudio (core) and Application in the same place? +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Products") +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Products") +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Products") -set(LIBRARY_SEARCH_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}" "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/Release" "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/Debug") +set(LIBRARY_SEARCH_DIRECTORY + "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}" + "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/Release" + "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/Debug") -# Search first in the binary dir, where conan will install finders, then -# search for modules in the root dir to override cmake ones -list(APPEND CMAKE_MODULE_PATH "${CMAKE_BINARY_DIR} ${PROJECT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}/CMake") +# Search first in the binary dir, where conan will install finders, then search +# for modules in the root dir to override cmake ones +list(APPEND CMAKE_MODULE_PATH "${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}" + "${PROJECT_SOURCE_DIR}/CMake") -####################################################################### -# R E G U L A R O U P U T A N D L I B S # -####################################################################### +# ############################################################################## +# R E G U L A R O U P U T A N D L I B S # +# ############################################################################## set(target_name mylib) -set(${target_name}_src - Person.hpp - Person.cpp -) - -add_library(${target_name} ${${target_name}_src}) -target_link_libraries(${target_name} - ${${target_name}_depends} -) -# Executable -add_executable(Test - main.cpp -) - -target_link_libraries(Test - ${target_name} -) - +set(${target_name}_src Measure.hpp Measure.cpp Model.hpp Model.cpp Runner.hpp + SpecialRunner.hpp) -####################################################################### -# S W I G # -####################################################################### +add_library(${target_name} SHARED ${${target_name}_src}) +target_link_libraries(${target_name} PRIVATE cpp_warning_options + cpp_compile_options) +# ############################################################################## +# S W I G # +# ############################################################################## option(BUILD_RUBY_BINDINGS "Build Ruby bindings" ON) # Build CSharp bindings option(BUILD_CSHARP_BINDINGS "Build CSharp bindings" OFF) -# Build Python bindings -# Requires: SWIG Python +# Build Python bindings Requires: SWIG Python option(BUILD_PYTHON_BINDINGS "Build Python bindings" ON) - # lib swig files -set(${target_name}_swig_src - Person.i -) +set(${target_name}_swig_src Measure.i) -foreach (SWIG_FILE ${${target_name}_swig_src}) +foreach(SWIG_FILE ${${target_name}_swig_src}) message("SWIG_FILE=${SWIG_FILE}") set_source_files_properties(${SWIG_FILE} PROPERTIES CPLUSPLUS ON) endforeach() @@ -147,101 +113,147 @@ include(${SWIG_USE_FILE}) enable_testing() include(CTest) - -if (BUILD_PYTHON_BINDINGS) +if(BUILD_PYTHON_BINDINGS) set(swig_target_name ${swig_target_name}_python) - find_package(Python COMPONENTS Interpreter Development REQUIRED) - - include_directories(SYSTEM ${Python_INCLUDE_DIRS}) + find_package( + Python + COMPONENTS Interpreter Development + REQUIRED) - swig_add_library(${swig_target_name} + swig_add_library( + ${swig_target_name} TYPE MODULE - LANGUAGE python - OUTPUT_DIR "${PROJECT_BINARY_DIR}/Products/python" - OUTFILE_DIR "${PROJECT_BINARY_DIR}/python_wrapper" - SOURCES ${${target_name}_swig_src} - ) - - set_target_properties(${swig_target_name} PROPERTIES OUTPUT_NAME ${target_name}) - - set_target_properties(${swig_target_name} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/python/") - set_target_properties(${swig_target_name} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/python/") - set_target_properties(${swig_target_name} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/python/") - - - swig_link_libraries(${swig_target_name} ${target_name}) - - - if (MSVC) + LANGUAGE python OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/Products/python" + OUTFILE_DIR "${CMAKE_CURRENT_BINARY_DIR}/python_wrapper" + SOURCES ${${target_name}_swig_src}) + + set_target_properties(${swig_target_name} PROPERTIES OUTPUT_NAME + ${target_name}) + + set_target_properties( + ${swig_target_name} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY + "${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/python/") + set_target_properties( + ${swig_target_name} PROPERTIES LIBRARY_OUTPUT_DIRECTORY + "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/python/") + set_target_properties( + ${swig_target_name} PROPERTIES RUNTIME_OUTPUT_DIRECTORY + "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/python/") + +# target_include_directories(${swig_target_name} PRIVATE SYSTEM +# ${Python_INCLUDE_DIRS}) + + swig_link_libraries(${swig_target_name} PUBLIC ${target_name} PRIVATE + cpp_compile_options) + +# if(MSVC) swig_link_libraries(${swig_target_name} PRIVATE Python::Module) - endif() +# endif() add_test( - NAME test_ruby_only - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/ruby - COMMAND ${Ruby_EXECUTABLE} test_ruby_only.rb - ) + NAME test_python_only + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/python + COMMAND ${Python_EXECUTABLE} test_python_only.rb) endif() if(BUILD_RUBY_BINDINGS) find_package(Ruby REQUIRED) - include_directories(SYSTEM ${Ruby_INCLUDE_DIRS}) - set(swig_target_name ${target_name}_ruby) - swig_add_library(${swig_target_name} + swig_add_library( + ${swig_target_name} TYPE MODULE - LANGUAGE ruby - OUTPUT_DIR "${PROJECT_BINARY_DIR}/Products/ruby" - OUTFILE_DIR "${PROJECT_BINARY_DIR}/ruby_wrapper" - SOURCES ${${target_name}_swig_src} - ) + LANGUAGE ruby OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/Products/ruby" + OUTFILE_DIR "${CMAKE_CURRENT_BINARY_DIR}/ruby_wrapper" + SOURCES ${${target_name}_swig_src}) + + set_target_properties(${swig_target_name} PROPERTIES OUTPUT_NAME + ${target_name}) - set_target_properties(${swig_target_name} PROPERTIES OUTPUT_NAME ${target_name}) + set_target_properties( + ${swig_target_name} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY + "${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/ruby/") + set_target_properties( + ${swig_target_name} PROPERTIES LIBRARY_OUTPUT_DIRECTORY + "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/ruby/") + set_target_properties( + ${swig_target_name} PROPERTIES RUNTIME_OUTPUT_DIRECTORY + "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ruby/") - set_target_properties(${swig_target_name} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/ruby/") - set_target_properties(${swig_target_name} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/ruby/") - set_target_properties(${swig_target_name} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ruby/") + target_include_directories( + ${swig_target_name} PRIVATE SYSTEM ${RUBY_INCLUDE_DIRS} ) - swig_link_libraries(${swig_target_name} ${target_name} ${Ruby_LIBRARIES}) + swig_link_libraries(${swig_target_name} PUBLIC ${target_name} PRIVATE + ${RUBY_LIBRARIES} cpp_compile_options) add_test( - NAME test_python_only - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/python - COMMAND ${Python_EXECUTABLE} test_python_only.py - ) + NAME test_ruby_only + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/ruby + COMMAND ${RUBY_EXECUTABLE} test_ruby_only.py) endif() - if(BUILD_RUBY_BINDINGS AND BUILD_PYTHON_BINDINGS) add_test( NAME test_pycall WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/ruby - COMMAND ${Ruby_EXECUTABLE} test_pycall.rb - ) + COMMAND ${RUBY_EXECUTABLE} test_pycall.rb) add_test( NAME test_pycall_workaround WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/ruby - COMMAND ${Ruby_EXECUTABLE} test_pycall_workaround.rb - ) + COMMAND ${RUBY_EXECUTABLE} test_pycall_workaround.rb) add_test( NAME test_pycall_use_cpp_to_convert_pointers WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/ruby - COMMAND ${Ruby_EXECUTABLE} test_pycall_use_cpp_to_convert_pointers.rb - ) + COMMAND ${RUBY_EXECUTABLE} test_pycall_use_cpp_to_convert_pointers.rb) add_test( NAME test_pycall_script WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/ruby - COMMAND ${Ruby_EXECUTABLE} test_pycall_script.rb - ) + COMMAND ${RUBY_EXECUTABLE} test_pycall_script.rb) endif() + +# Must call CMake itself in order to set the SWIG_LIB env var for +# add_custom_command +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/python_wrapper/SWIGPythonRuntime.hxx" + COMMAND + ${CMAKE_COMMAND} -E env SWIG_LIB="${SWIG_DIR}" "${SWIG_EXECUTABLE}" "-v" + "-python" -external-runtime + "${CMAKE_CURRENT_BINARY_DIR}/python_wrapper/SWIGPythonRuntime.hxx") + +# Must call CMake itself in order to set the SWIG_LIB env var for +# add_custom_command +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/ruby_wrapper/SWIGRubyRuntime.hxx" + COMMAND + ${CMAKE_COMMAND} -E env SWIG_LIB="${SWIG_DIR}" "${SWIG_EXECUTABLE}" "-v" + "-ruby" -external-runtime + "${CMAKE_CURRENT_BINARY_DIR}/ruby_wrapper/SWIGRubyRuntime.hxx") + +# Executable +add_executable( + Test + main.cpp + ScriptEngine.hpp + PythonEngine.hpp + PythonEngine.cpp + RubyEngine.hpp + RubyEngine.cpp + "${CMAKE_CURRENT_BINARY_DIR}/ruby_wrapper/SWIGRubyRuntime.hxx" + "${CMAKE_CURRENT_BINARY_DIR}/python_wrapper/SWIGPythonRuntime.hxx") + +target_link_libraries(Test PRIVATE ${target_name} cpp_compile_options + cpp_warning_options ${RUBY_LIBRARY} Python::Python) + +target_include_directories(Test PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/ruby_wrapper" "${CMAKE_CURRENT_BINARY_DIR}/python_wrapper" + ${RUBY_INCLUDE_DIRS}) + diff --git a/Measure.cpp b/Measure.cpp new file mode 100644 index 0000000..b4dc5f1 --- /dev/null +++ b/Measure.cpp @@ -0,0 +1,10 @@ +#include "Measure.hpp" + +namespace Test { +std::vector Measure::arguments(const Model& model) const { + return arguments_impl(model); +} +bool Measure::run(Runner& runner, const std::vector& args) { + return run_impl(runner, args); +} +} // namespace Test \ No newline at end of file diff --git a/Measure.hpp b/Measure.hpp new file mode 100644 index 0000000..7be1be7 --- /dev/null +++ b/Measure.hpp @@ -0,0 +1,37 @@ +#ifndef MEASURE_HPP +#define MEASURE_HPP + +#include +#include +#include +#include + +namespace Test { +class Runner; + +class Model; + +class Measure +{ + public: + Measure() = default; + Measure(const Measure&) = delete; + Measure(Measure&&) = delete; + Measure& operator=(const Measure&) = delete; + Measure& operator=(Measure&&) = delete; + virtual ~Measure() = default; + + std::vector arguments(const Model&) const; + bool run(Runner&, const std::vector& ); + + protected: + // protected virtual here allows us to easily change the internal interface + // and behavior of measure + virtual std::vector arguments_impl(const Model&) const = 0; + virtual bool run_impl(Runner&, const std::vector& ) = 0; +}; + + +} // Namespace Test + +#endif // ifndef MEASURE_HPP diff --git a/Measure.i b/Measure.i new file mode 100644 index 0000000..597b464 --- /dev/null +++ b/Measure.i @@ -0,0 +1,25 @@ +#ifndef MEASURE_I +#define MEASURE_I + +%module mylib +%module(directors="1") mylib + +%feature("director") Measure; +%feature("director") Runner; + +%include +%include +%include + +%{ + #include + #include + #include +%} + +%include +%include +%include + +#endif //MEASURE_I + diff --git a/Model.cpp b/Model.cpp new file mode 100644 index 0000000..ec5beea --- /dev/null +++ b/Model.cpp @@ -0,0 +1,13 @@ +#include "Model.hpp" + +namespace Test +{ + Model::Model(std::string name) : name_(std::move(name)) + { + + } + + const std::string &Model::get_name() const { + return name_; + } +} \ No newline at end of file diff --git a/Model.hpp b/Model.hpp new file mode 100644 index 0000000..c7116a0 --- /dev/null +++ b/Model.hpp @@ -0,0 +1,18 @@ +#ifndef MODEL_HPP +#define MODEL_HPP + +#include + +namespace Test { +class Model +{ + public: + explicit Model(std::string name); + const std::string& get_name() const; + + private: + std::string name_; +}; +} // namespace Test + +#endif diff --git a/Person.cpp b/Person.cpp deleted file mode 100644 index 0a97770..0000000 --- a/Person.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "Person.hpp" - -#include - -namespace Test { - -Person::Person(const std::string& t_name) noexcept - : m_name(t_name) { } - -Person::Person(const Person& other) - : m_name(other.m_name) { } - - -std::string Person::getName() const { - return m_name; -} - -bool Person::setName(const std::string& t_newName) { - m_name = t_newName; - return true; -} - -std::ostream& operator<<(std::ostream& os, const Test::Person& p) { - os << "Person named '" << p.getName() << "'"; - return os; -} - -std::string personName(const Person& person) { - return person.getName(); -} - -} // namespace Test diff --git a/Person.hpp b/Person.hpp deleted file mode 100644 index f89f612..0000000 --- a/Person.hpp +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef EXAMPLE_HPP -#define EXAMPLE_HPP - -#include -#include -#include - -namespace Test { - -class Person { - public: - - Person(const std::string& name) noexcept; - - Person(const Person& other); - - std::string getName() const; - bool setName(const std::string& t_newName); - - private: - std::string m_name; -}; - -std::ostream& operator<<(std::ostream&, const Test::Person&); - -// A free-standing function taking an object as argument -std::string personName(const Person& person); -inline void setName(Person &p, const std::string &newname) { - p.setName(newname); -} - -// get an integral representation of the pointer that is this Person -inline long long toInt(Person &p) { - std::clog << "original pointer: " << &p << '\n'; - const auto result = reinterpret_cast(&p); - std::clog << "toInt from C++ " << result << '\n'; - return result; -} - -// take the integer from toInt and reinterpret_cast it back into a Person *, then return that as a reference -inline Person &fromInt(long long i) { - auto *ptr = reinterpret_cast(i); - std::clog << "Reclaimed pointer: " << ptr << '\n'; - return *ptr; -} - -} // Namespace Test - -#endif // ifndef PERSON_HPP diff --git a/Person.i b/Person.i deleted file mode 100644 index ff1ad08..0000000 --- a/Person.i +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef PERSON_I -#define PERSON_I - -%module mylib - -%include -%include - -%{ - #include - #include -%} - -%include - -%extend Test::Person { - // Use the overloaded operator<< for string representation - std::string __str__() { - std::ostringstream os; - os << *$self; - return os.str(); - } - #ifdef SWIGRUBY - // get an integral representation of the pointer that is this Person - inline long long __toInt() { - std::clog << "original pointer: " << $self << '\n'; - const auto result = reinterpret_cast($self); - std::clog << "toInt from C++ " << result << '\n'; - return result; - } - - - #endif - - #ifdef SWIGPYTHON - // take the integer from toInt and reinterpret_cast it back into a Person *, then return that as a reference - static inline Test::Person& _fromInt(long long i) { - auto *ptr = reinterpret_cast(i); - std::clog << "Reclaimed pointer: " << ptr << '\n'; - return *ptr; - } - #endif -}; - -#endif //PERSON_I - diff --git a/PythonEngine.cpp b/PythonEngine.cpp new file mode 100644 index 0000000..205f6d5 --- /dev/null +++ b/PythonEngine.cpp @@ -0,0 +1,35 @@ +#include "PythonEngine.hpp" +#include +#include + +namespace Test { + +PythonEngine::PythonEngine([[maybe_unused]] const int argc, const char* argv[]) : program(Py_DecodeLocale(argv[0], nullptr)) { + if (program == nullptr) { + fprintf(stderr, "Fatal error: cannot decode argv[0]\n"); + exit(1); + } + + Py_SetProgramName(program); // optional but recommended + Py_Initialize(); +} + +PythonEngine::~PythonEngine() { + if (Py_FinalizeEx() < 0) { + exit(120); + } + PyMem_RawFree(program); +} + +ScriptObject PythonEngine::exec(std::string_view sv) { + std::string str{sv}; + PyRun_SimpleString(str.c_str()); + return ScriptObject{}; +} + +// convert the underlying object to the correct type, then return it as a void * +// so the above template function can provide it back to the caller. +void* PythonEngine::get_as(ScriptObject&, const std::type_info&) { + return nullptr; +} +} // namespace Test diff --git a/PythonEngine.hpp b/PythonEngine.hpp new file mode 100644 index 0000000..9efb690 --- /dev/null +++ b/PythonEngine.hpp @@ -0,0 +1,31 @@ +#ifndef PYTHONENGINE_included +#define PYTHONENGINE_included + +#include "ScriptEngine.hpp" + +namespace Test { +class PythonEngine final : public ScriptEngine +{ + public: + PythonEngine(const int argc, const char* argv[]); + ~PythonEngine() override; + + PythonEngine(const PythonEngine&) = delete; + PythonEngine(PythonEngine&&) = delete; + PythonEngine& operator=(const PythonEngine&) = delete; + PythonEngine& operator=(PythonEngine&&) = delete; + + ScriptObject exec(std::string_view sv) override; + + protected: + // convert the underlying object to the correct type, then return it as a void * + // so the above template function can provide it back to the caller. + void* get_as(ScriptObject& obj, const std::type_info&) override; + + private: + wchar_t *program; +}; +} + + +#endif diff --git a/RubyEngine.cpp b/RubyEngine.cpp new file mode 100644 index 0000000..a94d164 --- /dev/null +++ b/RubyEngine.cpp @@ -0,0 +1,37 @@ +#include "RubyEngine.hpp" +#include + +#ifdef __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wregister" +#endif + +#include + +#ifdef __GNUC__ +# pragma GCC diagnostic pop +#endif + +namespace Test { + +RubyEngine::RubyEngine() { + ruby_setup(); +} + +RubyEngine::~RubyEngine() { + ruby_finalize(); +} + +ScriptObject RubyEngine::exec(std::string_view sv) { + std::string str{sv}; + rb_eval_string(str.c_str()); + return ScriptObject{}; +} + +// convert the underlying object to the correct type, then return it as a void * +// so the above template function can provide it back to the caller. +void* RubyEngine::get_as(ScriptObject&, const std::type_info&) { + return nullptr; +} + +} // namespace Test diff --git a/RubyEngine.hpp b/RubyEngine.hpp new file mode 100644 index 0000000..929ff56 --- /dev/null +++ b/RubyEngine.hpp @@ -0,0 +1,27 @@ +#ifndef RUBYENGINE_included +#define RUBYENGINE_included + +#include "ScriptEngine.hpp" + +namespace Test { +class RubyEngine final : public ScriptEngine +{ + public: + RubyEngine(); + ~RubyEngine() override; + + RubyEngine(const RubyEngine &) = delete; + RubyEngine(RubyEngine &&) = delete; + RubyEngine &operator=(const RubyEngine &) = delete; + RubyEngine &operator=(RubyEngine &&) = delete; + + ScriptObject exec(std::string_view sv) override; + + protected: + // convert the underlying object to the correct type, then return it as a void * + // so the above template function can provide it back to the caller. + void* get_as(ScriptObject& obj, const std::type_info&) override; +}; +} + +#endif diff --git a/Runner.hpp b/Runner.hpp new file mode 100644 index 0000000..824a67c --- /dev/null +++ b/Runner.hpp @@ -0,0 +1,26 @@ +#ifndef TEST_SWIG_RUNNER_HPP +#define TEST_SWIG_RUNNER_HPP + +namespace Test { +class Model; + +class Runner +{ + public: + virtual ~Runner() = default; + Runner &operator=(Runner &&) = delete; + Runner &operator=(const Runner &) = delete; + Runner(Runner &&) = delete; + Runner(const Runner &) = delete; + Runner() = default; + + Model &get_current_model() { return get_current_model_impl(); } + + protected: + + virtual Model& get_current_model_impl() = 0; + +}; +} // namespace Test + +#endif //TEST_SWIG_RUNNER_HPP diff --git a/ScriptEngine.hpp b/ScriptEngine.hpp new file mode 100644 index 0000000..454cbfd --- /dev/null +++ b/ScriptEngine.hpp @@ -0,0 +1,49 @@ +#ifndef SCRIPTENGINE_included +#define SCRIPTENGINE_included + +#include +#include +#include + +namespace Test +{ + class ScriptObject { + std::any object; + friend class ScriptEngine; + }; + + class ScriptEngine { + public: + ScriptEngine() = default; + virtual ~ScriptEngine() = default; + ScriptEngine(const ScriptEngine&) = delete; + ScriptEngine(ScriptEngine&&) = delete; + ScriptEngine& operator=(const ScriptEngine&) = delete; + ScriptEngine& operator=(ScriptEngine&&) = delete; + + virtual ScriptObject exec(std::string_view sv) = 0; + + template + T &get_as(ScriptObject &obj) { + void *result = get_as(obj, typeid(T)); + if (result) { + return static_cast(result); + } else { + throw std::bad_cast(); + } + } + + protected: + // convert the underlying object to the correct type, then return it as a void * + // so the above template function can provide it back to the caller. + virtual void *get_as(ScriptObject &obj, const std::type_info &) = 0; + + static std::any &getInternals(ScriptObject &obj) noexcept { return obj.object; } + }; + + +} + +#endif + + diff --git a/SpecialRunner.hpp b/SpecialRunner.hpp new file mode 100644 index 0000000..41ba71e --- /dev/null +++ b/SpecialRunner.hpp @@ -0,0 +1,26 @@ +#ifndef TEST_SWIG_SPECIALRUNNER_HPP +#define TEST_SWIG_SPECIALRUNNER_HPP + +#include "Runner.hpp" +#include "Model.hpp" + +namespace Test +{ +// a concrete implementation of the Runner class, for testing purposes +class SpecialRunner : public Runner{ + public: + SpecialRunner(Model model) : model_(std::move(model)) + { + + } + + protected: + Model &get_current_model_impl() override { + return model_; + } + private: + Model model_; +}; +} + +#endif //TEST_SWIG_SPECIALRUNNER_HPP diff --git a/main.cpp b/main.cpp index 8c17240..212801b 100644 --- a/main.cpp +++ b/main.cpp @@ -1,7 +1,14 @@ -#include "Person.hpp" #include +#include "RubyEngine.hpp" +#include "PythonEngine.hpp" + + + +int main(const int argc, const char* argv[]) { + Test::RubyEngine ruby; + Test::PythonEngine python{argc, argv}; + + python.exec(R"(print("Hello From Python"))"); + ruby.exec(R"(puts("Hello from Ruby"))"); -int main() { - Test::Person p("John Doe"); - std::cout << p << '\n'; } From cfdb46314d1e25838294c4e0e6f72631c4f29c1a Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Wed, 24 Mar 2021 15:51:32 -0600 Subject: [PATCH 02/16] "Working" (except that it doesn't) --- CMakeLists.txt | 8 +-- Measure.cpp | 7 +- Measure.hpp | 7 +- Measure.i | 1 - Model.cpp | 22 +++--- Model.hpp | 8 ++- PythonEngine.cpp | 136 +++++++++++++++++++++++++++++++++++-- PythonEngine.hpp | 10 +-- RubyEngine.cpp | 58 ++++++++++++++-- RubyEngine.hpp | 15 ++-- ScriptEngine.hpp | 97 ++++++++++++++++---------- SpecialRunner.hpp | 18 +++-- main.cpp | 38 ++++++++++- python/measure.py | 9 --- python/test_measure.py | 22 ++++++ python/test_python_only.py | 29 -------- ruby/test_measure.rb | 14 ++++ 17 files changed, 365 insertions(+), 134 deletions(-) delete mode 100644 python/measure.py create mode 100644 python/test_measure.py delete mode 100644 python/test_python_only.py create mode 100644 ruby/test_measure.rb diff --git a/CMakeLists.txt b/CMakeLists.txt index c4dadc7..c81ab23 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,10 +10,10 @@ if(MSVC) else() target_compile_options( cpp_warning_options INTERFACE -Wall -Wextra -Wconversion -Wpedantic -Werror) - target_compile_options(cpp_compile_options - INTERFACE -fsanitize=undefined,address) - target_link_options(cpp_compile_options INTERFACE - -fsanitize=undefined,address) +# target_compile_options(cpp_compile_options +# INTERFACE -fsanitize=undefined,address) +# target_link_options(cpp_compile_options INTERFACE +# -fsanitize=undefined,address) endif() # Do not enable compiler specific extensions, for eg on GCC use -std=c++1z diff --git a/Measure.cpp b/Measure.cpp index b4dc5f1..ad91c92 100644 --- a/Measure.cpp +++ b/Measure.cpp @@ -1,10 +1,7 @@ #include "Measure.hpp" namespace Test { -std::vector Measure::arguments(const Model& model) const { - return arguments_impl(model); -} -bool Measure::run(Runner& runner, const std::vector& args) { - return run_impl(runner, args); +bool Measure::run(Runner& runner) { + return run_impl(runner); } } // namespace Test \ No newline at end of file diff --git a/Measure.hpp b/Measure.hpp index 7be1be7..30e6694 100644 --- a/Measure.hpp +++ b/Measure.hpp @@ -21,14 +21,13 @@ class Measure Measure& operator=(Measure&&) = delete; virtual ~Measure() = default; - std::vector arguments(const Model&) const; - bool run(Runner&, const std::vector& ); + virtual std::string name() = 0; + bool run(Runner&); protected: // protected virtual here allows us to easily change the internal interface // and behavior of measure - virtual std::vector arguments_impl(const Model&) const = 0; - virtual bool run_impl(Runner&, const std::vector& ) = 0; + virtual bool run_impl(Runner&) = 0; }; diff --git a/Measure.i b/Measure.i index 597b464..8fbc178 100644 --- a/Measure.i +++ b/Measure.i @@ -5,7 +5,6 @@ %module(directors="1") mylib %feature("director") Measure; -%feature("director") Runner; %include %include diff --git a/Model.cpp b/Model.cpp index ec5beea..b4f21e6 100644 --- a/Model.cpp +++ b/Model.cpp @@ -1,13 +1,17 @@ #include "Model.hpp" -namespace Test -{ - Model::Model(std::string name) : name_(std::move(name)) - { +namespace Test { +Model::Model(std::string name) : name_(std::move(name)) {} - } +const std::string& Model::getName() const { + return name_; +} - const std::string &Model::get_name() const { - return name_; - } -} \ No newline at end of file +void Model::pushOp(const std::string& op_name) { + opsPerformed_.push_back(op_name); +} + +const std::vector& Model::opsPerformed() const { + return opsPerformed_; +} +} // namespace Test \ No newline at end of file diff --git a/Model.hpp b/Model.hpp index c7116a0..c239b37 100644 --- a/Model.hpp +++ b/Model.hpp @@ -2,16 +2,22 @@ #define MODEL_HPP #include +#include namespace Test { class Model { public: explicit Model(std::string name); - const std::string& get_name() const; + const std::string& getName() const; + + void pushOp(const std::string& op_name); + + const std::vector& opsPerformed() const; private: std::string name_; + std::vector opsPerformed_; }; } // namespace Test diff --git a/PythonEngine.cpp b/PythonEngine.cpp index 205f6d5..2164ea5 100644 --- a/PythonEngine.cpp +++ b/PythonEngine.cpp @@ -1,7 +1,19 @@ #include "PythonEngine.hpp" -#include + #include +#ifdef __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif + +#include +#include "SWIGPythonRuntime.hxx" + +#ifdef __GNUC__ +# pragma GCC diagnostic pop +#endif + namespace Test { PythonEngine::PythonEngine([[maybe_unused]] const int argc, const char* argv[]) : program(Py_DecodeLocale(argv[0], nullptr)) { @@ -21,15 +33,125 @@ PythonEngine::~PythonEngine() { PyMem_RawFree(program); } -ScriptObject PythonEngine::exec(std::string_view sv) { - std::string str{sv}; - PyRun_SimpleString(str.c_str()); - return ScriptObject{}; +struct PythonObject { + PythonObject() = default; + + PythonObject(PyObject *obj) noexcept : obj_(obj) + { + if (obj_) { + Py_INCREF(obj_); + } + } + + PythonObject(const PythonObject &other) noexcept + : obj_(other.obj_) { + if (obj_) { + Py_INCREF(obj_); + } + } + + PythonObject(PythonObject &&other) noexcept + : obj_(other.obj_) { + // no reason to inc/dec, we just stole the ref counted object + // from other + other.obj_ = nullptr; + } + + PythonObject &operator=(const PythonObject &rhs) noexcept { + if (&rhs != this) { + obj_ = rhs.obj_; + + if (obj_) { + Py_INCREF(obj_); + } + } + + return *this; + } + + PythonObject &operator=(PythonObject &&rhs) noexcept { + if (&rhs != this) { + obj_ = rhs.obj_; + rhs.obj_ = nullptr; + } + + return *this; + } + + ~PythonObject() { + if (obj_) { + Py_DECREF(obj_); + } + } + + PyObject *obj_ = nullptr; +}; + +void PythonEngine::exec(std::string_view sv) { + std::string command{sv}; + + PyObject* m = PyImport_AddModule("__main__"); + if (m == nullptr) { + throw std::runtime_error("Unable to add module __main__ for python script execution"); + } + + PyObject* d = PyModule_GetDict(m); + PyObject* v = PyRun_String(command.c_str(), Py_file_input, d, d); + if (v == nullptr) { + PyErr_Print(); + throw std::runtime_error("Error executing Python code"); + } + + //decref count returned from Python + Py_DECREF(v); +} + +ScriptObject PythonEngine::eval(std::string_view sv) { + std::string command{sv}; + + PyObject* m = PyImport_AddModule("__main__"); + if (m == nullptr) { + throw std::runtime_error("Unable to add module __main__ for python script execution"); + } + + PyObject* d = PyModule_GetDict(m); + PyObject* v = PyRun_String(command.c_str(), Py_eval_input, d, d); + if (v == nullptr) { + PyErr_Print(); + throw std::runtime_error("Error executing Python code"); + } + + //share in ownership + PythonObject return_value(v); + + //decref count returned from Python + Py_DECREF(v); + + return ScriptObject{return_value}; } // convert the underlying object to the correct type, then return it as a void * // so the above template function can provide it back to the caller. -void* PythonEngine::get_as(ScriptObject&, const std::type_info&) { - return nullptr; +void* PythonEngine::getAs_impl(ScriptObject& obj, const std::type_info& ti) { + + auto val = std::any_cast(obj.object); + + const auto& type_name = getRegisteredTypeName(ti); + + void* return_value = nullptr; + + auto* type = SWIG_TypeQuery(type_name.c_str()); + + if (!type) { + throw std::runtime_error("Unable to find type in SWIG"); + } + + const auto result = SWIG_Python_ConvertPtr(val.obj_, &return_value, type, 0); + + if (!SWIG_IsOK(result)) { + throw std::runtime_error("Error getting object from SWIG/Python"); + } + + return return_value; } } // namespace Test diff --git a/PythonEngine.hpp b/PythonEngine.hpp index 9efb690..15f4dc5 100644 --- a/PythonEngine.hpp +++ b/PythonEngine.hpp @@ -15,17 +15,17 @@ class PythonEngine final : public ScriptEngine PythonEngine& operator=(const PythonEngine&) = delete; PythonEngine& operator=(PythonEngine&&) = delete; - ScriptObject exec(std::string_view sv) override; + ScriptObject eval(std::string_view sv) override; + void exec(std::string_view sv) override; protected: // convert the underlying object to the correct type, then return it as a void * // so the above template function can provide it back to the caller. - void* get_as(ScriptObject& obj, const std::type_info&) override; + void* getAs_impl(ScriptObject& obj, const std::type_info&) override; private: - wchar_t *program; + wchar_t* program; }; -} - +} // namespace Test #endif diff --git a/RubyEngine.cpp b/RubyEngine.cpp index a94d164..a4d17cd 100644 --- a/RubyEngine.cpp +++ b/RubyEngine.cpp @@ -1,12 +1,14 @@ #include "RubyEngine.hpp" + #include #ifdef __GNUC__ # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wregister" +# pragma GCC diagnostic ignored "-Wunused-parameter" #endif -#include +#include "SWIGRubyRuntime.hxx" #ifdef __GNUC__ # pragma GCC diagnostic pop @@ -22,16 +24,60 @@ RubyEngine::~RubyEngine() { ruby_finalize(); } -ScriptObject RubyEngine::exec(std::string_view sv) { +static VALUE evaluateSimpleImpl(VALUE arg) { + return rb_eval_string(StringValuePtr(arg)); +} + +VALUE evalString(const std::string& t_str) { + VALUE val = rb_str_new2(t_str.c_str()); + int error; + // save and restore the current working directory in case the call to ruby upsets it + VALUE result = rb_protect(evaluateSimpleImpl, val, &error); + if (error != 0) { + VALUE errval = rb_eval_string("$!.to_s"); + char* str = StringValuePtr(errval); + std::string err(str); + VALUE locval = rb_eval_string("$@.to_s"); + str = StringValuePtr(locval); + std::string loc(str); + throw std::runtime_error("Error '" + err + "' at " + loc); + } + + return result; +} + +ScriptObject RubyEngine::eval(std::string_view sv) { + std::string str{sv}; + return ScriptObject{evalString(str)}; +} + +void RubyEngine::exec(std::string_view sv) { std::string str{sv}; - rb_eval_string(str.c_str()); - return ScriptObject{}; + [[maybe_unused]] const auto result = evalString(str); } // convert the underlying object to the correct type, then return it as a void * // so the above template function can provide it back to the caller. -void* RubyEngine::get_as(ScriptObject&, const std::type_info&) { - return nullptr; +void* RubyEngine::getAs_impl(ScriptObject& obj, const std::type_info &ti) { + auto val = std::any_cast(obj.object); + + const auto &type_name = getRegisteredTypeName(ti); + + void *return_value = nullptr; + + auto *type = SWIG_TypeQuery(type_name.c_str()); + + if (!type) { + throw std::runtime_error("Unable to find type in SWIG"); + } + + const auto result = SWIG_ConvertPtr(val, &return_value, type, 0); + + if (!SWIG_IsOK(result)) { + throw std::runtime_error("Error getting object from SWIG/Ruby"); + } + + return return_value; } } // namespace Test diff --git a/RubyEngine.hpp b/RubyEngine.hpp index 929ff56..8eee703 100644 --- a/RubyEngine.hpp +++ b/RubyEngine.hpp @@ -10,18 +10,19 @@ class RubyEngine final : public ScriptEngine RubyEngine(); ~RubyEngine() override; - RubyEngine(const RubyEngine &) = delete; - RubyEngine(RubyEngine &&) = delete; - RubyEngine &operator=(const RubyEngine &) = delete; - RubyEngine &operator=(RubyEngine &&) = delete; + RubyEngine(const RubyEngine&) = delete; + RubyEngine(RubyEngine&&) = delete; + RubyEngine& operator=(const RubyEngine&) = delete; + RubyEngine& operator=(RubyEngine&&) = delete; - ScriptObject exec(std::string_view sv) override; + ScriptObject eval(std::string_view sv) override; + void exec(std::string_view sv) override; protected: // convert the underlying object to the correct type, then return it as a void * // so the above template function can provide it back to the caller. - void* get_as(ScriptObject& obj, const std::type_info&) override; + void* getAs_impl(ScriptObject& obj, const std::type_info&) override; }; -} +} // namespace Test #endif diff --git a/ScriptEngine.hpp b/ScriptEngine.hpp index 454cbfd..e5f5b45 100644 --- a/ScriptEngine.hpp +++ b/ScriptEngine.hpp @@ -1,49 +1,74 @@ #ifndef SCRIPTENGINE_included #define SCRIPTENGINE_included -#include #include +#include +#include #include +#include -namespace Test +namespace Test { +struct ScriptObject { - class ScriptObject { - std::any object; - friend class ScriptEngine; - }; + std::any object; +}; - class ScriptEngine { - public: - ScriptEngine() = default; - virtual ~ScriptEngine() = default; - ScriptEngine(const ScriptEngine&) = delete; - ScriptEngine(ScriptEngine&&) = delete; - ScriptEngine& operator=(const ScriptEngine&) = delete; - ScriptEngine& operator=(ScriptEngine&&) = delete; - - virtual ScriptObject exec(std::string_view sv) = 0; - - template - T &get_as(ScriptObject &obj) { - void *result = get_as(obj, typeid(T)); - if (result) { - return static_cast(result); - } else { - throw std::bad_cast(); - } - } - - protected: - // convert the underlying object to the correct type, then return it as a void * - // so the above template function can provide it back to the caller. - virtual void *get_as(ScriptObject &obj, const std::type_info &) = 0; - - static std::any &getInternals(ScriptObject &obj) noexcept { return obj.object; } - }; +class ScriptEngine +{ + public: + ScriptEngine() = default; + virtual ~ScriptEngine() = default; + ScriptEngine(const ScriptEngine&) = delete; + ScriptEngine(ScriptEngine&&) = delete; + ScriptEngine& operator=(const ScriptEngine&) = delete; + ScriptEngine& operator=(ScriptEngine&&) = delete; + virtual ScriptObject eval(std::string_view sv) = 0; -} + // execute string without expecting a return value + virtual void exec(std::string_view sv) = 0; -#endif + template + T getAs(ScriptObject& obj) { + void* result = getAs_impl(obj, typeid(T)); + if (result) { + return static_cast(result); + } else { + throw std::bad_cast(); + } + } + + template + void registerType(std::string name) { + types.emplace(std::cref(typeid(T)), std::move(name)); + } + protected: + // convert the underlying object to the correct type, then return it as a void * + // so the above template function can provide it back to the caller. + virtual void* getAs_impl(ScriptObject& obj, const std::type_info&) = 0; + const std::string& getRegisteredTypeName(const std::type_info& type) { + const auto& found_name = types.find(type); + + if (found_name != types.end()) { + return found_name->second; + } + + throw std::runtime_error("unknown type requested"); + } + + private: + struct Compare + { + bool operator()(const std::reference_wrapper &lhs, const std::reference_wrapper& rhs) { + return lhs.get().before(rhs.get()); + } + }; + + std::map, std::string, Compare> types; +}; + +} // namespace Test + +#endif diff --git a/SpecialRunner.hpp b/SpecialRunner.hpp index 41ba71e..b706fe8 100644 --- a/SpecialRunner.hpp +++ b/SpecialRunner.hpp @@ -4,23 +4,21 @@ #include "Runner.hpp" #include "Model.hpp" -namespace Test -{ +namespace Test { // a concrete implementation of the Runner class, for testing purposes -class SpecialRunner : public Runner{ +class SpecialRunner : public Runner +{ public: - SpecialRunner(Model model) : model_(std::move(model)) - { - - } + SpecialRunner(Model model) : model_(std::move(model)) {} protected: - Model &get_current_model_impl() override { + Model& get_current_model_impl() override { return model_; } - private: + + private: Model model_; }; -} +} // namespace Test #endif //TEST_SWIG_SPECIALRUNNER_HPP diff --git a/main.cpp b/main.cpp index 212801b..648dc37 100644 --- a/main.cpp +++ b/main.cpp @@ -1,8 +1,11 @@ #include +#include #include "RubyEngine.hpp" #include "PythonEngine.hpp" +#include "Measure.hpp" +#include "SpecialRunner.hpp" - +#include "Model.hpp" int main(const int argc, const char* argv[]) { Test::RubyEngine ruby; @@ -11,4 +14,37 @@ int main(const int argc, const char* argv[]) { python.exec(R"(print("Hello From Python"))"); ruby.exec(R"(puts("Hello from Ruby"))"); + ruby.registerType("Test::Measure *"); + python.registerType("Test::Measure *"); + + ruby.exec("require '/home/jason/Cpp_Swig_Ruby_Python_MCVE/ruby/test_measure.rb'"); + auto ruby_measure = ruby.eval("RubyTestMeasure.new()"); + auto* ruby_measure_from_cpp = ruby.getAs(ruby_measure); + assert(ruby_measure_from_cpp); + std::cout << "Ruby measure name: " << ruby_measure_from_cpp->name() << '\n'; + + python.exec("import sys\nsys.path.append('/home/jason/Cpp_Swig_Ruby_Python_MCVE/python/')"); + python.exec("import test_measure"); + auto python_measure = python.eval("test_measure.PythonTestMeasure()"); + auto* python_measure_from_cpp = python.getAs(python_measure); + assert(python_measure_from_cpp); + + // this all works up until this moment right here. + // At this point it doesn't call the SWIG Director for Python + // It actually calls the SWIG Director for Ruby. + // Why? Because we've broken the One Definition Rule. + // SWIG defines functions with the same names for both the Python bindings and the Ruby bindings. + // whichever one gets loaded first wins. + // + // Can we work around this? Maybe. But having both Ruby and Python linked into the same application + // is asking for many problems. + std::cout << "Python measure name: " << python_measure_from_cpp->name() << '\n'; + + Test::SpecialRunner sr(Test::Model{"MyModel"}); + ruby_measure_from_cpp->run(sr); + // python_measure_from_cpp->run(sr); + + for (const auto& op : sr.get_current_model().opsPerformed()) { + std::cout << "Op 'run' from script: " << op << '\n'; + } } diff --git a/python/measure.py b/python/measure.py deleted file mode 100644 index a2ad292..0000000 --- a/python/measure.py +++ /dev/null @@ -1,9 +0,0 @@ -import mylib - -class PythonMeasureName: - def run(self, p: mylib.Person, name: str): - print(f"Python script.py: {p.getName()}") - print(f"Changing name to {name}") - p.setName(name) - print(f"Python script.py: {p.getName()}") - return True diff --git a/python/test_measure.py b/python/test_measure.py new file mode 100644 index 0000000..18d5c04 --- /dev/null +++ b/python/test_measure.py @@ -0,0 +1,22 @@ + +import sys + +sys.path.append("/home/jason/Cpp_Swig_Ruby_Python_MCVE/cmake-build-debug/Products/python/") + +import mylib + +class PythonTestMeasure(mylib.Measure): + def __init__(self): + mylib.Measure.__init__(self) + print("Created Object") + + def run(self, r: mylib.Runner): + r.get_current_model().pushOp("Op from Python") + return True + def name(self): + return "Python Test Measure" + + +def make_measure(): + return PythonTestMeasure() + diff --git a/python/test_python_only.py b/python/test_python_only.py deleted file mode 100644 index e58e2a0..0000000 --- a/python/test_python_only.py +++ /dev/null @@ -1,29 +0,0 @@ -import sys -from pathlib import Path -#import importlib.util - -python_lib_path = Path('../build/Products/python/mylib.py').resolve() - -if not python_lib_path.exists(): - print("Error, this assumes you already built the python bindings project in /build") - raise IOError(f"Could Not find {python_lib_path}") - -# spec = importlib.util.spec_from_file_location("person", - # str(python_lib_path)) -# person = importlib.util.module_from_spec(spec) -# spec.loader.exec_module(person) -sys.path.insert(0, str(python_lib_path.parent)) -import mylib - -def get_a_python_person(): - p = mylib.Person("John") - return p - -def print_a_person(p: mylib.Person): - return mylib.personName(p) - - -if __name__ == '__main__': - p = mylib.Person("John") - print(p) - print(f"{mylib.personName(p)=}") diff --git a/ruby/test_measure.rb b/ruby/test_measure.rb new file mode 100644 index 0000000..b696db9 --- /dev/null +++ b/ruby/test_measure.rb @@ -0,0 +1,14 @@ +require '/home/jason/Cpp_Swig_Ruby_Python_MCVE/cmake-build-debug/Products/ruby/mylib.so' + +class RubyTestMeasure < Mylib::Measure + def name + return "RubyTestMeasure" + end + + def run_impl(runner) + runner.get_current_model().pushOp("A Ruby Op") + return true; + end +end + + From 977e50b3cf34cda8b03a8c11ba24d23ef7fcb575 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Fri, 7 May 2021 11:11:29 +0200 Subject: [PATCH 03/16] Change path to my machine, add some imports (gcc-10 was complaining... evcentually I switched to gcc 7.5 since 10 wouldn't build) --- .gitignore | 2 +- Measure.i | 23 ++++++++++++++++++++++- PythonEngine.cpp | 1 + RubyEngine.cpp | 1 + ScriptEngine.hpp | 4 +++- main.cpp | 4 ++-- python/test_measure.py | 2 +- ruby/test_measure.rb | 2 +- 8 files changed, 32 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 3a6adca..dd458d7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -build/ +build*/ # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/Measure.i b/Measure.i index 8fbc178..8e411a8 100644 --- a/Measure.i +++ b/Measure.i @@ -1,8 +1,29 @@ #ifndef MEASURE_I #define MEASURE_I + +%begin %{ + // ... code in begin section ... +%} + +%runtime %{ + // ... code in runtime section ... +%} + +%header %{ + // ... code in header section ... +%} + +%wrapper %{ + // ... code in wrapper section ... +%} + +%init %{ + // ... code in init section ... +%} + %module mylib -%module(directors="1") mylib +%module(directors="1") mylib %feature("director") Measure; diff --git a/PythonEngine.cpp b/PythonEngine.cpp index 2164ea5..51bdf1c 100644 --- a/PythonEngine.cpp +++ b/PythonEngine.cpp @@ -1,6 +1,7 @@ #include "PythonEngine.hpp" #include +#include #ifdef __GNUC__ # pragma GCC diagnostic push diff --git a/RubyEngine.cpp b/RubyEngine.cpp index a4d17cd..a1a4203 100644 --- a/RubyEngine.cpp +++ b/RubyEngine.cpp @@ -1,6 +1,7 @@ #include "RubyEngine.hpp" #include +#include #ifdef __GNUC__ # pragma GCC diagnostic push diff --git a/ScriptEngine.hpp b/ScriptEngine.hpp index e5f5b45..10a019d 100644 --- a/ScriptEngine.hpp +++ b/ScriptEngine.hpp @@ -6,6 +6,8 @@ #include #include #include +#include +#include namespace Test { struct ScriptObject @@ -65,7 +67,7 @@ class ScriptEngine return lhs.get().before(rhs.get()); } }; - + std::map, std::string, Compare> types; }; diff --git a/main.cpp b/main.cpp index 648dc37..721a4ee 100644 --- a/main.cpp +++ b/main.cpp @@ -17,13 +17,13 @@ int main(const int argc, const char* argv[]) { ruby.registerType("Test::Measure *"); python.registerType("Test::Measure *"); - ruby.exec("require '/home/jason/Cpp_Swig_Ruby_Python_MCVE/ruby/test_measure.rb'"); + ruby.exec("require '/home/julien/Software/Cpp_Swig_Ruby_Python_MCVE/ruby/test_measure.rb'"); auto ruby_measure = ruby.eval("RubyTestMeasure.new()"); auto* ruby_measure_from_cpp = ruby.getAs(ruby_measure); assert(ruby_measure_from_cpp); std::cout << "Ruby measure name: " << ruby_measure_from_cpp->name() << '\n'; - python.exec("import sys\nsys.path.append('/home/jason/Cpp_Swig_Ruby_Python_MCVE/python/')"); + python.exec("import sys\nsys.path.append('/home/julien/Software/Cpp_Swig_Ruby_Python_MCVE/python/')"); python.exec("import test_measure"); auto python_measure = python.eval("test_measure.PythonTestMeasure()"); auto* python_measure_from_cpp = python.getAs(python_measure); diff --git a/python/test_measure.py b/python/test_measure.py index 18d5c04..af6ea7e 100644 --- a/python/test_measure.py +++ b/python/test_measure.py @@ -1,7 +1,7 @@ import sys -sys.path.append("/home/jason/Cpp_Swig_Ruby_Python_MCVE/cmake-build-debug/Products/python/") +sys.path.append("/home/julien/Software/Cpp_Swig_Ruby_Python_MCVE/build-modif/Products/python/") import mylib diff --git a/ruby/test_measure.rb b/ruby/test_measure.rb index b696db9..33a768d 100644 --- a/ruby/test_measure.rb +++ b/ruby/test_measure.rb @@ -1,4 +1,4 @@ -require '/home/jason/Cpp_Swig_Ruby_Python_MCVE/cmake-build-debug/Products/ruby/mylib.so' +require '/home/julien/Software/Cpp_Swig_Ruby_Python_MCVE/build-modif/Products/ruby/mylib.so' class RubyTestMeasure < Mylib::Measure def name From e93286bd39d4c29001fed54fd2e62a14a7eac718 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Fri, 7 May 2021 11:11:58 +0200 Subject: [PATCH 04/16] When SWIG'ing to Python, rename "Measure" to "PythonMeasure" so that we don't have SwigDirector_Measure name clashing --- Measure.i | 8 +++++++- python/test_measure.py | 6 +++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Measure.i b/Measure.i index 8e411a8..04fdef2 100644 --- a/Measure.i +++ b/Measure.i @@ -25,12 +25,18 @@ %module mylib %module(directors="1") mylib -%feature("director") Measure; + %include %include %include +%feature("director") Measure; + +#if defined(SWIGPYTHON) +%rename (PythonMeasure) Test::Measure; +#endif + %{ #include #include diff --git a/python/test_measure.py b/python/test_measure.py index af6ea7e..7c86c31 100644 --- a/python/test_measure.py +++ b/python/test_measure.py @@ -5,10 +5,10 @@ import mylib -class PythonTestMeasure(mylib.Measure): +class PythonTestMeasure(mylib.PythonMeasure): def __init__(self): - mylib.Measure.__init__(self) - print("Created Object") + mylib.PythonMeasure.__init__(self) + print("Created Object (Python __init__)") def run(self, r: mylib.Runner): r.get_current_model().pushOp("Op from Python") From 73f7ee1c07cd8802ff400ef8e85f2b2c2767b49c Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Fri, 7 May 2021 12:33:43 +0200 Subject: [PATCH 05/16] Uncomment python_measure.run in main: it crashes --- main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.cpp b/main.cpp index 721a4ee..bbdbf5f 100644 --- a/main.cpp +++ b/main.cpp @@ -42,7 +42,7 @@ int main(const int argc, const char* argv[]) { Test::SpecialRunner sr(Test::Model{"MyModel"}); ruby_measure_from_cpp->run(sr); - // python_measure_from_cpp->run(sr); + python_measure_from_cpp->run(sr); for (const auto& op : sr.get_current_model().opsPerformed()) { std::cout << "Op 'run' from script: " << op << '\n'; From 7354cfe449a05290a377a6803bbfbb4b85a29ee8 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Fri, 7 May 2021 12:33:59 +0200 Subject: [PATCH 06/16] The python measure.py should have run_impl inside, not run! it works --- python/test_measure.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/test_measure.py b/python/test_measure.py index 7c86c31..ea1ce1f 100644 --- a/python/test_measure.py +++ b/python/test_measure.py @@ -10,7 +10,7 @@ def __init__(self): mylib.PythonMeasure.__init__(self) print("Created Object (Python __init__)") - def run(self, r: mylib.Runner): + def run_impl(self, r: mylib.Runner): r.get_current_model().pushOp("Op from Python") return True def name(self): From 77d34ef85357a7a51f32efe44f51f9430061a836 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 10 May 2021 09:46:04 +0200 Subject: [PATCH 07/16] Comparator must be const: `static assertion failed: comparison object must be invocable as const` --- ScriptEngine.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ScriptEngine.hpp b/ScriptEngine.hpp index 10a019d..85c9fb6 100644 --- a/ScriptEngine.hpp +++ b/ScriptEngine.hpp @@ -63,7 +63,7 @@ class ScriptEngine private: struct Compare { - bool operator()(const std::reference_wrapper &lhs, const std::reference_wrapper& rhs) { + bool operator()(const std::reference_wrapper &lhs, const std::reference_wrapper& rhs) const { return lhs.get().before(rhs.get()); } }; From 4774fa80a269f5a818fd9e32b23d717e0cb5bdc3 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 10 May 2021 17:09:44 +0200 Subject: [PATCH 08/16] Extend test to get and set Model name from C++, ruby, and python. Crashes --- Model.cpp | 8 +++++++- Model.hpp | 1 + main.cpp | 5 ++++- python/test_measure.py | 2 ++ ruby/test_measure.rb | 2 ++ 5 files changed, 16 insertions(+), 2 deletions(-) diff --git a/Model.cpp b/Model.cpp index b4f21e6..d2aa454 100644 --- a/Model.cpp +++ b/Model.cpp @@ -7,6 +7,12 @@ const std::string& Model::getName() const { return name_; } +bool Model::setName(const std::string& name) { + name_ = name; + return true; +} + + void Model::pushOp(const std::string& op_name) { opsPerformed_.push_back(op_name); } @@ -14,4 +20,4 @@ void Model::pushOp(const std::string& op_name) { const std::vector& Model::opsPerformed() const { return opsPerformed_; } -} // namespace Test \ No newline at end of file +} // namespace Test diff --git a/Model.hpp b/Model.hpp index c239b37..6ac5b74 100644 --- a/Model.hpp +++ b/Model.hpp @@ -10,6 +10,7 @@ class Model public: explicit Model(std::string name); const std::string& getName() const; + bool setName(const std::string& name); void pushOp(const std::string& op_name); diff --git a/main.cpp b/main.cpp index bbdbf5f..00a20d1 100644 --- a/main.cpp +++ b/main.cpp @@ -40,10 +40,13 @@ int main(const int argc, const char* argv[]) { // is asking for many problems. std::cout << "Python measure name: " << python_measure_from_cpp->name() << '\n'; - Test::SpecialRunner sr(Test::Model{"MyModel"}); + Test::SpecialRunner sr(Test::Model{"C++ Model"}); + std::cout << "Starting out with a Model in C++ called: " << sr.get_current_model().getName() << '\n'; ruby_measure_from_cpp->run(sr); python_measure_from_cpp->run(sr); + std::cout << "After Running Ruby and Python: model is named " << sr.get_current_model().getName() << '\n'; + for (const auto& op : sr.get_current_model().opsPerformed()) { std::cout << "Op 'run' from script: " << op << '\n'; } diff --git a/python/test_measure.py b/python/test_measure.py index ea1ce1f..05d7d9f 100644 --- a/python/test_measure.py +++ b/python/test_measure.py @@ -11,7 +11,9 @@ def __init__(self): print("Created Object (Python __init__)") def run_impl(self, r: mylib.Runner): + print(f"Python Model named: {r.get_current_model().getName()}") r.get_current_model().pushOp("Op from Python") + r.get_current_model().setName("Python Model") return True def name(self): return "Python Test Measure" diff --git a/ruby/test_measure.rb b/ruby/test_measure.rb index 33a768d..efc4087 100644 --- a/ruby/test_measure.rb +++ b/ruby/test_measure.rb @@ -6,6 +6,8 @@ def name end def run_impl(runner) + puts "Ruby Model named: #{runner.get_current_model().getName()}" + runner.get_current_model().setName("Ruby Model") runner.get_current_model().pushOp("A Ruby Op") return true; end From 15178e7dcd65b30d2d92a5b6f1677b3da86aafb2 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 10 May 2021 17:10:07 +0200 Subject: [PATCH 09/16] Rremove pedantic, so ruby doesn't make it crash on some compilers... --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c81ab23..a0df375 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,7 +9,7 @@ if(MSVC) target_compile_options(cpp_warning_options INTERFACE /W4 /Werror) else() target_compile_options( - cpp_warning_options INTERFACE -Wall -Wextra -Wconversion -Wpedantic -Werror) + cpp_warning_options INTERFACE -Wall -Wextra -Wconversion -Werror) # target_compile_options(cpp_compile_options # INTERFACE -fsanitize=undefined,address) # target_link_options(cpp_compile_options INTERFACE From 94097a2a051c93be7b6dc789954260e51f265279 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Tue, 11 May 2021 09:49:09 +0200 Subject: [PATCH 10/16] Works, but dynamic: Extend tests to tweak model objects. Add ability to turn off python for testing --- CMakeLists.txt | 97 +++++++++++++++++++++++++++--------------- Measure.i | 4 ++ Model.cpp | 49 +++++++++++++++------ Model.hpp | 30 +++++++------ ModelObject.cpp | 19 +++++++++ ModelObject.hpp | 19 +++++++++ PythonEngine.cpp | 2 +- main.cpp | 58 ++++++++++++++++++++++--- python/test_measure.py | 15 +++++-- ruby/test_measure.rb | 16 +++++-- 10 files changed, 236 insertions(+), 73 deletions(-) create mode 100644 ModelObject.cpp create mode 100644 ModelObject.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index a0df375..1d8b830 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -79,7 +79,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}" set(target_name mylib) -set(${target_name}_src Measure.hpp Measure.cpp Model.hpp Model.cpp Runner.hpp +set(${target_name}_src Measure.hpp Measure.cpp Model.hpp Model.cpp ModelObject.hpp ModelObject.cpp Runner.hpp SpecialRunner.hpp) add_library(${target_name} SHARED ${${target_name}_src}) @@ -115,6 +115,8 @@ include(CTest) if(BUILD_PYTHON_BINDINGS) + add_compile_definitions(WITHPYTHON) + set(swig_target_name ${swig_target_name}_python) find_package( @@ -157,6 +159,15 @@ if(BUILD_PYTHON_BINDINGS) WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/python COMMAND ${Python_EXECUTABLE} test_python_only.rb) + # Must call CMake itself in order to set the SWIG_LIB env var for + # add_custom_command + add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/python_wrapper/SWIGPythonRuntime.hxx" + COMMAND + ${CMAKE_COMMAND} -E env SWIG_LIB="${SWIG_DIR}" "${SWIG_EXECUTABLE}" "-v" + "-python" -external-runtime + "${CMAKE_CURRENT_BINARY_DIR}/python_wrapper/SWIGPythonRuntime.hxx") + endif() if(BUILD_RUBY_BINDINGS) @@ -195,6 +206,16 @@ if(BUILD_RUBY_BINDINGS) WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/ruby COMMAND ${RUBY_EXECUTABLE} test_ruby_only.py) + + # Must call CMake itself in order to set the SWIG_LIB env var for + # add_custom_command + add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/ruby_wrapper/SWIGRubyRuntime.hxx" + COMMAND + ${CMAKE_COMMAND} -E env SWIG_LIB="${SWIG_DIR}" "${SWIG_EXECUTABLE}" "-v" + "-ruby" -external-runtime + "${CMAKE_CURRENT_BINARY_DIR}/ruby_wrapper/SWIGRubyRuntime.hxx") + endif() if(BUILD_RUBY_BINDINGS AND BUILD_PYTHON_BINDINGS) @@ -221,39 +242,47 @@ if(BUILD_RUBY_BINDINGS AND BUILD_PYTHON_BINDINGS) endif() -# Must call CMake itself in order to set the SWIG_LIB env var for -# add_custom_command -add_custom_command( - OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/python_wrapper/SWIGPythonRuntime.hxx" - COMMAND - ${CMAKE_COMMAND} -E env SWIG_LIB="${SWIG_DIR}" "${SWIG_EXECUTABLE}" "-v" - "-python" -external-runtime +if (BUILD_PYTHON_BINDINGS) + +endif() + + + +if (BUILD_PYTHON_BINDINGS) + + # Executable + add_executable( + Test + main.cpp + ScriptEngine.hpp + PythonEngine.hpp + PythonEngine.cpp + RubyEngine.hpp + RubyEngine.cpp + "${CMAKE_CURRENT_BINARY_DIR}/ruby_wrapper/SWIGRubyRuntime.hxx" "${CMAKE_CURRENT_BINARY_DIR}/python_wrapper/SWIGPythonRuntime.hxx") -# Must call CMake itself in order to set the SWIG_LIB env var for -# add_custom_command -add_custom_command( - OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/ruby_wrapper/SWIGRubyRuntime.hxx" - COMMAND - ${CMAKE_COMMAND} -E env SWIG_LIB="${SWIG_DIR}" "${SWIG_EXECUTABLE}" "-v" - "-ruby" -external-runtime - "${CMAKE_CURRENT_BINARY_DIR}/ruby_wrapper/SWIGRubyRuntime.hxx") - -# Executable -add_executable( - Test - main.cpp - ScriptEngine.hpp - PythonEngine.hpp - PythonEngine.cpp - RubyEngine.hpp - RubyEngine.cpp - "${CMAKE_CURRENT_BINARY_DIR}/ruby_wrapper/SWIGRubyRuntime.hxx" - "${CMAKE_CURRENT_BINARY_DIR}/python_wrapper/SWIGPythonRuntime.hxx") - -target_link_libraries(Test PRIVATE ${target_name} cpp_compile_options - cpp_warning_options ${RUBY_LIBRARY} Python::Python) - -target_include_directories(Test PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/ruby_wrapper" "${CMAKE_CURRENT_BINARY_DIR}/python_wrapper" - ${RUBY_INCLUDE_DIRS}) + target_link_libraries(Test PRIVATE ${target_name} cpp_compile_options + cpp_warning_options ${RUBY_LIBRARY} Python::Python) + + target_include_directories(Test PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/ruby_wrapper" "${CMAKE_CURRENT_BINARY_DIR}/python_wrapper" + ${RUBY_INCLUDE_DIRS}) +else() + + # Executable + add_executable( + Test + main.cpp + ScriptEngine.hpp + RubyEngine.hpp + RubyEngine.cpp + "${CMAKE_CURRENT_BINARY_DIR}/ruby_wrapper/SWIGRubyRuntime.hxx" + ) + + target_link_libraries(Test PRIVATE ${target_name} cpp_compile_options + cpp_warning_options ${RUBY_LIBRARY}) + target_include_directories(Test PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/ruby_wrapper" + ${RUBY_INCLUDE_DIRS}) + +endif() diff --git a/Measure.i b/Measure.i index 04fdef2..b21a16d 100644 --- a/Measure.i +++ b/Measure.i @@ -1,6 +1,7 @@ #ifndef MEASURE_I #define MEASURE_I +#include %begin %{ // ... code in begin section ... @@ -35,14 +36,17 @@ #if defined(SWIGPYTHON) %rename (PythonMeasure) Test::Measure; +//%rename ("Python%s") ""; #endif %{ + #include #include #include #include %} +%include %include %include %include diff --git a/Model.cpp b/Model.cpp index d2aa454..e65aec3 100644 --- a/Model.cpp +++ b/Model.cpp @@ -1,23 +1,44 @@ #include "Model.hpp" +#include "ModelObject.hpp" namespace Test { -Model::Model(std::string name) : name_(std::move(name)) {} + Model::Model(std::string name) : name_(std::move(name)) {} -const std::string& Model::getName() const { - return name_; -} + const std::string& Model::getName() const { + return name_; + } -bool Model::setName(const std::string& name) { - name_ = name; - return true; -} + bool Model::setName(const std::string& name) { + name_ = name; + return true; + } + void Model::pushOp(const std::string& op_name) { + opsPerformed_.push_back(op_name); + } -void Model::pushOp(const std::string& op_name) { - opsPerformed_.push_back(op_name); -} + const std::vector& Model::opsPerformed() const { + return opsPerformed_; + } + + int Model::numObjects() const { + return static_cast(m_objects.size()); + } + + std::vector Model::objectNames() const { + std::vector result; + for (auto objPtr : m_objects) { + result.push_back(objPtr.getName()); + } + return result; + } + + ModelObject& Model::getObject(size_t index) { + return m_objects[index]; + } + + void Model::pushObject(const std::string& objName) { + m_objects.emplace_back(objName); + } -const std::vector& Model::opsPerformed() const { - return opsPerformed_; -} } // namespace Test diff --git a/Model.hpp b/Model.hpp index 6ac5b74..df81973 100644 --- a/Model.hpp +++ b/Model.hpp @@ -3,23 +3,29 @@ #include #include +#include "ModelObject.hpp" namespace Test { -class Model -{ - public: - explicit Model(std::string name); - const std::string& getName() const; - bool setName(const std::string& name); + class Model + { + public: + explicit Model(std::string name); + const std::string& getName() const; + bool setName(const std::string& name); - void pushOp(const std::string& op_name); + void pushOp(const std::string& op_name); - const std::vector& opsPerformed() const; + const std::vector& opsPerformed() const; - private: - std::string name_; - std::vector opsPerformed_; -}; + int numObjects() const; + std::vector objectNames() const; + ModelObject& getObject(size_t index); + void pushObject(const std::string& objName); + private: + std::string name_; + std::vector opsPerformed_; + std::vector m_objects; + }; } // namespace Test #endif diff --git a/ModelObject.cpp b/ModelObject.cpp new file mode 100644 index 0000000..0c64261 --- /dev/null +++ b/ModelObject.cpp @@ -0,0 +1,19 @@ +#include "ModelObject.hpp" + +namespace Test { + + ModelObject::ModelObject(std::string name) + : name_(std::move(name)) + { + } + + const std::string& ModelObject::getName() const { + return name_; + } + + bool ModelObject::setName(const std::string& name) { + name_ = name; + return true; + } + +} // namespace Test diff --git a/ModelObject.hpp b/ModelObject.hpp new file mode 100644 index 0000000..b4db4b8 --- /dev/null +++ b/ModelObject.hpp @@ -0,0 +1,19 @@ +#ifndef MODELOBJECT_HPP +#define MODELOBJECT_HPP + +#include + +namespace Test { + + class ModelObject { + public: + explicit ModelObject(std::string name); + const std::string& getName() const; + bool setName(const std::string& name); + + private: + std::string name_; + }; +} // namespace Test + +#endif diff --git a/PythonEngine.cpp b/PythonEngine.cpp index 51bdf1c..c58a0a7 100644 --- a/PythonEngine.cpp +++ b/PythonEngine.cpp @@ -141,7 +141,7 @@ void* PythonEngine::getAs_impl(ScriptObject& obj, const std::type_info& ti) { void* return_value = nullptr; - auto* type = SWIG_TypeQuery(type_name.c_str()); + auto* type = SWIG_Python_TypeQuery(type_name.c_str()); if (!type) { throw std::runtime_error("Unable to find type in SWIG"); diff --git a/main.cpp b/main.cpp index 00a20d1..6aa550c 100644 --- a/main.cpp +++ b/main.cpp @@ -1,21 +1,35 @@ +// CMake defines WITHPYTHON (or not) + #include #include #include "RubyEngine.hpp" + +#ifdef WITHPYTHON #include "PythonEngine.hpp" +#endif + #include "Measure.hpp" #include "SpecialRunner.hpp" #include "Model.hpp" -int main(const int argc, const char* argv[]) { +int main([[maybe_unused]] const int argc, [[maybe_unused]] const char* argv[]) { Test::RubyEngine ruby; + #ifdef WITHPYTHON Test::PythonEngine python{argc, argv}; + #endif - python.exec(R"(print("Hello From Python"))"); ruby.exec(R"(puts("Hello from Ruby"))"); + #ifdef WITHPYTHON + python.exec(R"(print("Hello From Python"))"); + #endif + ruby.registerType("Test::Measure *"); + + #ifdef WITHPYTHON python.registerType("Test::Measure *"); + #endif ruby.exec("require '/home/julien/Software/Cpp_Swig_Ruby_Python_MCVE/ruby/test_measure.rb'"); auto ruby_measure = ruby.eval("RubyTestMeasure.new()"); @@ -23,11 +37,13 @@ int main(const int argc, const char* argv[]) { assert(ruby_measure_from_cpp); std::cout << "Ruby measure name: " << ruby_measure_from_cpp->name() << '\n'; + #ifdef WITHPYTHON python.exec("import sys\nsys.path.append('/home/julien/Software/Cpp_Swig_Ruby_Python_MCVE/python/')"); python.exec("import test_measure"); auto python_measure = python.eval("test_measure.PythonTestMeasure()"); auto* python_measure_from_cpp = python.getAs(python_measure); assert(python_measure_from_cpp); + #endif // this all works up until this moment right here. // At this point it doesn't call the SWIG Director for Python @@ -38,16 +54,46 @@ int main(const int argc, const char* argv[]) { // // Can we work around this? Maybe. But having both Ruby and Python linked into the same application // is asking for many problems. - std::cout << "Python measure name: " << python_measure_from_cpp->name() << '\n'; + #ifdef WITHPYTHON + std::cout << "Python measure name: " << python_measure_from_cpp->name() << '\n'; + #endif - Test::SpecialRunner sr(Test::Model{"C++ Model"}); + auto printObjectNames = [](Test::Model& m) { + for (int i = 0; i < m.numObjects(); ++i) { + std::cout << " * " << i <<" = " << m.getObject(i).getName() << '\n'; + } + }; + + Test::Model m{"C++ Model"}; + m.pushObject("C++ object"); + Test::SpecialRunner sr(m); std::cout << "Starting out with a Model in C++ called: " << sr.get_current_model().getName() << '\n'; + std::cout << "C++: starting with " << sr.get_current_model().numObjects() << " objects\n"; + printObjectNames(sr.get_current_model()); + + std::cout << "\n\n========== START RUBY ==========\n"; + ruby_measure_from_cpp->run(sr); - python_measure_from_cpp->run(sr); - std::cout << "After Running Ruby and Python: model is named " << sr.get_current_model().getName() << '\n'; + std::cout << "========== FINISHED RUBY ==========\n\n"; + std::cout << "C++: " << sr.get_current_model().numObjects() << " objects\n"; + printObjectNames(sr.get_current_model()); + + #ifdef WITHPYTHON + std::cout << "\n\n========== START PYTHON ==========\n"; + python_measure_from_cpp->run(sr); + std::cout << "========== FINISHED PYTHON ==========\n\n"; + std::cout << "After Running Ruby and Python: model is named " << sr.get_current_model().getName() << '\n'; + #else + std::cout << "\n\n########## PYTHON ISN'T ENABLED ##########\n\n"; + std::cout << "After Running Ruby only: model is named " << sr.get_current_model().getName() << '\n'; + #endif for (const auto& op : sr.get_current_model().opsPerformed()) { std::cout << "Op 'run' from script: " << op << '\n'; } + + std::cout << "C++: " << sr.get_current_model().numObjects() << " objects\n"; + printObjectNames(sr.get_current_model()); + } diff --git a/python/test_measure.py b/python/test_measure.py index 05d7d9f..fa49215 100644 --- a/python/test_measure.py +++ b/python/test_measure.py @@ -11,9 +11,18 @@ def __init__(self): print("Created Object (Python __init__)") def run_impl(self, r: mylib.Runner): - print(f"Python Model named: {r.get_current_model().getName()}") - r.get_current_model().pushOp("Op from Python") - r.get_current_model().setName("Python Model") + m = r.get_current_model() + print(f"Python Model named: {m.getName()}") + m.pushOp("Op from Python") + m.setName("Python Model") + print(f"Python: {m.numObjects()} objects") + m.pushObject("Python Space") + + for i in range(m.numObjects()): + print(f" * {i} = {m.getObject(i).getName()}") + + m.getObject(1).setName("MODIFIED FROM PYTHON") + return True def name(self): return "Python Test Measure" diff --git a/ruby/test_measure.rb b/ruby/test_measure.rb index efc4087..a5794f3 100644 --- a/ruby/test_measure.rb +++ b/ruby/test_measure.rb @@ -6,9 +6,19 @@ def name end def run_impl(runner) - puts "Ruby Model named: #{runner.get_current_model().getName()}" - runner.get_current_model().setName("Ruby Model") - runner.get_current_model().pushOp("A Ruby Op") + m = runner.get_current_model() + puts "Ruby Model named: #{m.getName()}" + m.setName("Ruby Model") + m.pushOp("A Ruby Op") + + puts "Ruby: Model has: #{m.numObjects()} spaces" + m.pushObject("Ruby Space") + m.numObjects().times do |i| + puts "* #{i} = #{m.getObject(i).getName()}" + end + + # This crashes + m.getObject(0).setName("MODIFIED FROM RUBY") return true; end end From 318edb09d9db0cbdbdbf82691f303f83079e1610 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Tue, 11 May 2021 21:29:18 +0200 Subject: [PATCH 11/16] Prepare for a static exec: Add Conan for conan-openstudio-ruby, zlib. Add embedded files. Look for static python lib too. Add ability to turn off ruby Ruby will currently fail to undefined reference to rb_hasFile and rb_require_embedded --- CMakeLists.txt | 196 +++++++++++++++--- embedded/CMakeLists.txt | 18 ++ embedded/CreateEmbeddedSource.cpp | 107 ++++++++++ embedded/CreateEmbeddedSource.manifest.xml | 8 + embedded/EmbedFiles.cmake | 107 ++++++++++ embedded/embedded_files.cxx.in | 50 +++++ embedded/embedded_files.hxx.in | 218 +++++++++++++++++++++ empty.cpp | 0 main.cpp | 35 +++- python/test_measure.py | 7 +- ruby/test_measure.rb | 2 +- 11 files changed, 708 insertions(+), 40 deletions(-) create mode 100644 embedded/CMakeLists.txt create mode 100644 embedded/CreateEmbeddedSource.cpp create mode 100644 embedded/CreateEmbeddedSource.manifest.xml create mode 100644 embedded/EmbedFiles.cmake create mode 100644 embedded/embedded_files.cxx.in create mode 100644 embedded/embedded_files.hxx.in create mode 100644 empty.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 1d8b830..8ac7d42 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,7 +9,7 @@ if(MSVC) target_compile_options(cpp_warning_options INTERFACE /W4 /Werror) else() target_compile_options( - cpp_warning_options INTERFACE -Wall -Wextra -Wconversion -Werror) + cpp_warning_options INTERFACE -Wall -Wextra -Wconversion -Werror -Wno-unused-variable) # target_compile_options(cpp_compile_options # INTERFACE -fsanitize=undefined,address) # target_link_options(cpp_compile_options INTERFACE @@ -40,20 +40,8 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) "MinSizeRel" "RelWithDebInfo") endif() -# Search first in the binary dir, where conan will install finders, then search -# for modules in the root dir to override cmake ones Start with ROOT, then -# CMAKE_CURRENT_BINARY_DIR -list(APPEND CMAKE_MODULE_PATH "${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}" - "${PROJECT_SOURCE_DIR}/CMake") -include_directories("${CMAKE_CURRENT_BINARY_DIR}") - -# Add to include path - -# Project source directory -include_directories("${PROJECT_SOURCE_DIR}") - # ############################################################################## -# C M A K E C O N T R O L # +# C M A K E C O N T R O L # # ############################################################################## # High level project configuration Do we actually want everything to go to @@ -73,6 +61,90 @@ set(LIBRARY_SEARCH_DIRECTORY list(APPEND CMAKE_MODULE_PATH "${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}/CMake") +# Add to include path + +# Project source directory +include_directories("${PROJECT_SOURCE_DIR}") + +####################################################################### +# C O N A N # +####################################################################### + +set(CMAKE_CONAN_EXPECTED_HASH 170c3250029af321395135a3952a9045) +set(CMAKE_CONAN_VERSION "v0.16.1") + +if(EXISTS "${CMAKE_BINARY_DIR}/conan.cmake") + file(MD5 "${CMAKE_BINARY_DIR}/conan.cmake" CMAKE_CONAN_HASH) +endif() +if(NOT EXISTS "${CMAKE_BINARY_DIR}/conan.cmake" OR NOT "${CMAKE_CONAN_HASH}" MATCHES "${CMAKE_CONAN_EXPECTED_HASH}") + # Put it in CMAKE_BINARY_DIR so we don't end up with two when building OpenStudioApplication + message(STATUS "Downloading conan.cmake ${CMAKE_CONAN_VERSION} from https://github.com/conan-io/cmake-conan") + file(DOWNLOAD "https://github.com/conan-io/cmake-conan/raw/${CMAKE_CONAN_VERSION}/conan.cmake" + "${CMAKE_BINARY_DIR}/conan.cmake") +else() + message(STATUS "Using existing conan.cmake") +endif() + +include(${CMAKE_BINARY_DIR}/conan.cmake) + +conan_check(VERSION 1.21.0 REQUIRED) + +message(STATUS "RUNNING CONAN") + +conan_add_remote(NAME nrel INDEX 0 + URL https://conan.commercialbuildings.dev/artifactory/api/conan/openstudio) + +conan_add_remote(NAME bincrafters + URL https://api.bintray.com/conan/bincrafters/public-conan) + +if(BUILD_RUBY_BINDINGS) + # Track NREL/stable in general, on a feature branch this could be temporarily switched to NREL/testing + set(CONAN_RUBY "openstudio_ruby/2.7.2@nrel/testing#5cc83469365344df986cd820cca4884d") +endif() + +list(APPEND CONAN_OPTIONS "zlib:minizip=True") +list(APPEND CONAN_BUILD "missing") + +conan_cmake_run(REQUIRES + ${CONAN_RUBY} + "zlib/1.2.11#0df31fd24179543f5720ec7beb2a88d7" + BASIC_SETUP CMAKE_TARGETS NO_OUTPUT_DIRS + OPTIONS ${CONAN_OPTIONS} + BUILD ${CONAN_BUILD} + # Passes `-u, --update` to conan install: Check updates exist from upstream remotes + # That and build=outdated should ensure we track the right + # UPDATE +) + +message("CONAN_LIBS=${CONAN_LIBS}") + +# A macro to find a conan related value especially when using multi-config builds (MSVC) +# But it also works with single-config builds +macro(FindValue ValueName) + set(LocalVar "") + set(LocalVar $<$:${${ValueName}_DEBUG}>$<$:${${ValueName}_RELEASE}>$<$:${$ValueName}_RELWITHDEBINFO}>$<$:${${ValueName}_MINSIZEREL}> + ) +# list(JOIN LocalVar "" LocalVar) + string(STRIP ${LocalVar} LocalVar) + set(CURRENT_${ValueName} $,${LocalVar},${${ValueName}}>) + # For debug purposes + # message(STATUS "Found '${ValueName}' as '${CURRENT_${ValueName}}'") +endmacro() + +if(BUILD_RUBY_BINDINGS) + get_target_property(RUBY_INCLUDE_DIRS CONAN_PKG::openstudio_ruby INTERFACE_INCLUDE_DIRECTORIES) +endif() + +############################################################################### + + + +# Search first in the binary dir, where conan will install finders, then +# search for modules in the root dir to override cmake ones +# Start with ROOT, then PROJECT_BINARY_DIR +list(APPEND CMAKE_MODULE_PATH "${PROJECT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}/CMake") +include_directories("${CMAKE_CURRENT_BINARY_DIR}") + # ############################################################################## # R E G U L A R O U P U T A N D L I B S # # ############################################################################## @@ -82,7 +154,7 @@ set(target_name mylib) set(${target_name}_src Measure.hpp Measure.cpp Model.hpp Model.cpp ModelObject.hpp ModelObject.cpp Runner.hpp SpecialRunner.hpp) -add_library(${target_name} SHARED ${${target_name}_src}) +add_library(${target_name} STATIC ${${target_name}_src}) target_link_libraries(${target_name} PRIVATE cpp_warning_options cpp_compile_options) @@ -113,11 +185,18 @@ include(${SWIG_USE_FILE}) enable_testing() include(CTest) +# TODO: WE DO NEED TO EMBED STUFF, SO PUT THAT BACK! +include("embedded/EmbedFiles.cmake") +add_subdirectory("embedded") + if(BUILD_PYTHON_BINDINGS) add_compile_definitions(WITHPYTHON) - set(swig_target_name ${swig_target_name}_python) + set(swig_target_name ${target_name}_python) + + # **Only** find static libs + set(Python_USE_STATIC_LIBS TRUE) find_package( Python @@ -126,7 +205,7 @@ if(BUILD_PYTHON_BINDINGS) swig_add_library( ${swig_target_name} - TYPE MODULE + TYPE STATIC LANGUAGE python OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/Products/python" OUTFILE_DIR "${CMAKE_CURRENT_BINARY_DIR}/python_wrapper" SOURCES ${${target_name}_swig_src}) @@ -171,13 +250,14 @@ if(BUILD_PYTHON_BINDINGS) endif() if(BUILD_RUBY_BINDINGS) - find_package(Ruby REQUIRED) + add_compile_definitions(WITHRUBY) + add_compile_definitions(RUBY_EMBEDDED) set(swig_target_name ${target_name}_ruby) swig_add_library( ${swig_target_name} - TYPE MODULE + TYPE STATIC LANGUAGE ruby OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/Products/ruby" OUTFILE_DIR "${CMAKE_CURRENT_BINARY_DIR}/ruby_wrapper" SOURCES ${${target_name}_swig_src}) @@ -216,6 +296,8 @@ if(BUILD_RUBY_BINDINGS) "-ruby" -external-runtime "${CMAKE_CURRENT_BINARY_DIR}/ruby_wrapper/SWIGRubyRuntime.hxx") + set_source_files_properties("${CMAKE_CURRENT_BINARY_DIR}/ruby_wrapper/SWIGRubyRuntime.hxx" PROPERTIES COMPILE_FLAGS "-DRUBY_EMBEDDED -Wno-unused-variable" ) + endif() if(BUILD_RUBY_BINDINGS AND BUILD_PYTHON_BINDINGS) @@ -242,13 +324,7 @@ if(BUILD_RUBY_BINDINGS AND BUILD_PYTHON_BINDINGS) endif() -if (BUILD_PYTHON_BINDINGS) - -endif() - - - -if (BUILD_PYTHON_BINDINGS) +if (BUILD_PYTHON_BINDINGS AND BUILD_RUBY_BINDINGS) # Executable add_executable( @@ -263,11 +339,42 @@ if (BUILD_PYTHON_BINDINGS) "${CMAKE_CURRENT_BINARY_DIR}/python_wrapper/SWIGPythonRuntime.hxx") target_link_libraries(Test PRIVATE ${target_name} cpp_compile_options - cpp_warning_options ${RUBY_LIBRARY} Python::Python) + cpp_warning_options) + + + add_library(additional_ruby_libs empty.cpp) + add_library(ruby_libs empty.cpp) + target_link_libraries(ruby_libs PUBLIC CONAN_PKG::openstudio_ruby) + target_link_libraries(ruby_libs PUBLIC additional_ruby_libs) + + set(ALL_RUBY_BINDING_TARGETS + ${target_name}_ruby + ) + target_link_libraries(Test PRIVATE + #init_Test + ruby_libs + ${ALL_RUBY_BINDING_TARGETS} + Python::Python + ${target_name}_python + ) + + + if (UNIX AND NOT APPLE) + target_link_libraries(additional_ruby_libs anl rt) + target_link_libraries(Test PRIVATE "icui18n" "icuuc" "gmp") + elseif(WIN32) + target_link_libraries(Test PRIVATE wsock32 ws2_32 Dbghelp Shlwapi Iphlpapi) + set_target_properties(Test PROPERTIES LINK_FLAGS_DEBUG "/NODEFAULTLIB:MSVCRT") + else() + endif() + + if( APPLE ) + target_link_libraries(Test PRIVATE ${COREFOUNDATION_LIBRARY}) + endif() target_include_directories(Test PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/ruby_wrapper" "${CMAKE_CURRENT_BINARY_DIR}/python_wrapper" ${RUBY_INCLUDE_DIRS}) -else() +elseif (BUILD_RUBY_BINDINGS) # Executable add_executable( @@ -280,9 +387,40 @@ else() ) target_link_libraries(Test PRIVATE ${target_name} cpp_compile_options - cpp_warning_options ${RUBY_LIBRARY}) + cpp_warning_options) + target_link_libraries(Test PUBLIC CONAN_PKG::openstudio_ruby) + + target_link_libraries(Test PRIVATE ${target_name}_ruby) target_include_directories(Test PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/ruby_wrapper" ${RUBY_INCLUDE_DIRS}) +elseif (BUILD_PYTHON_BINDINGS) + + message("Adding exe: EMBEDDED_OUTPUT=${EMBEDDED_OUTPUT}") + + # Executable + add_executable( + Test + main.cpp + ScriptEngine.hpp + PythonEngine.hpp + PythonEngine.cpp + "${CMAKE_CURRENT_BINARY_DIR}/python_wrapper/SWIGPythonRuntime.hxx" + ) + + target_link_libraries(Test PRIVATE ${target_name} cpp_compile_options + cpp_warning_options) + + target_link_libraries(Test PRIVATE + Python::Python + ${target_name}_python + CONAN_PKG::zlib + ) + + set_target_properties(Test PROPERTIES LINK_FLAGS "-Xlinker --export-dynamic") + message("Python_LINK_OPTIONS=${Python_LINK_OPTIONS}") + + target_include_directories(Test PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/python_wrapper") + endif() diff --git a/embedded/CMakeLists.txt b/embedded/CMakeLists.txt new file mode 100644 index 0000000..262c8f0 --- /dev/null +++ b/embedded/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.10.2) + +set(CMAKE_CXX_STANDARD 11) + +add_executable(CreateEmbeddedSource + CreateEmbeddedSource.cpp +) + +target_link_libraries(CreateEmbeddedSource CONAN_PKG::zlib) + +# Add a specific manifest for CreateEmbeddedSource that will include the LongPathAware attribute, which, +# in conjunction with the regkey LongPathsEnabled=1 will make it work with paths that are longer than MAX_PATH (win10 only) +if( WIN32 ) + add_custom_command(TARGET CreateEmbeddedSource + POST_BUILD + COMMAND mt -nologo -manifest "${CMAKE_CURRENT_SOURCE_DIR}/CreateEmbeddedSource.manifest.xml" -outputresource:$ + ) +endif() diff --git a/embedded/CreateEmbeddedSource.cpp b/embedded/CreateEmbeddedSource.cpp new file mode 100644 index 0000000..3ba8ec5 --- /dev/null +++ b/embedded/CreateEmbeddedSource.cpp @@ -0,0 +1,107 @@ + +#include +#include +#include + +#include +#include +#include + +#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__) +# include +# include +# define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY) +#else +# define SET_BINARY_MODE(file) +#endif + +#define CHUNK 16384 + +int main(int argc, char *argv[]) +{ + if( argc != 5 ) { + return 1; + } + + auto infile = argv[1]; + auto outfile = argv[2]; + auto filenum = argv[3]; + auto embeddedname = argv[4]; + + int ret, flush; + unsigned have; + z_stream strm; + unsigned char in[CHUNK]; + unsigned char out[CHUNK]; + + /* allocate deflate state */ + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + ret = deflateInit(&strm, Z_DEFAULT_COMPRESSION); + if (ret != Z_OK) + return 1; + + FILE * source = fopen(infile, "rb"); + if (source==NULL) {fputs ("File error",stderr); return EXIT_FAILURE;} + + std::fstream outstream(outfile, std::fstream::out | std::fstream::trunc); + + // This is the compressed length in chars; + unsigned length = 0; + + if( outstream.is_open() ) { + outstream << "static const uint8_t embedded_file_" << filenum << "[] = {"; + do { + strm.avail_in = fread(in, 1, CHUNK, source); + if (ferror(source)) { + (void)deflateEnd(&strm); + return EXIT_FAILURE; + } + flush = feof(source) ? Z_FINISH : Z_NO_FLUSH; + strm.next_in = in; + + /* run deflate() on input until output buffer not full, finish + compression if all of source has been read in */ + do { + strm.avail_out = CHUNK; + strm.next_out = out; + ret = deflate(&strm, flush); /* no bad return value */ + assert(ret != Z_STREAM_ERROR); /* state not clobbered */ + have = CHUNK - strm.avail_out; + + for( unsigned i = 0; i != have; ++i ) { + if( length != 0 ) { + outstream << ","; + } + outstream << "0x" << std::hex << static_cast(out[i]); + ++length; + } + } while (strm.avail_out == 0); + assert(strm.avail_in == 0); /* all input will be used */ + + /* done when last data in file processed */ + } while (flush != Z_FINISH); + assert(ret == Z_STREAM_END); /* stream will be complete */ + + /* clean up and return */ + (void)deflateEnd(&strm); + + outstream << "};"; + + outstream << "\n"; + outstream << "static const char *embedded_file_name_" << filenum << " = \"" << embeddedname << "\";"; + outstream << "\n"; + outstream << "static const size_t embedded_file_len_" << filenum << " = " << std::dec << length << ";"; + outstream << std::endl; + + outstream.close(); + fclose(source); + } else{ + std::cout << "Could not open '" << outfile << "' for writing" << std::endl; + return EXIT_FAILURE; + } + + return 0; +} + diff --git a/embedded/CreateEmbeddedSource.manifest.xml b/embedded/CreateEmbeddedSource.manifest.xml new file mode 100644 index 0000000..c77c244 --- /dev/null +++ b/embedded/CreateEmbeddedSource.manifest.xml @@ -0,0 +1,8 @@ + + + + + true + + + diff --git a/embedded/EmbedFiles.cmake b/embedded/EmbedFiles.cmake new file mode 100644 index 0000000..bb47490 --- /dev/null +++ b/embedded/EmbedFiles.cmake @@ -0,0 +1,107 @@ +# FILES is a list of file paths to embed +# EMBEDDED_LOCATIONS is a indexed matched list of paths for each embedded file +# CXX_OUTPUT_FILES is a variable that will be set to a list of cxx files to compile executable or library + +function(embed_files FILES EMBEDDED_LOCATIONS CXX_OUTPUT_FILES) + list(LENGTH FILES FILES_LENGTH) + list(LENGTH EMBEDDED_LOCATIONS EMBEDDED_LOCATIONS_LENGTH) + + if( NOT (FILES_LENGTH EQUAL EMBEDDED_LOCATIONS_LENGTH) ) + message(ERROR "embed_files function must be called with FILES and EMBEDDED_LOCATIONS lists of the same length") + return() + endif() + + if( ARGV3 ) + set(NAMESPACE ${ARGV3}) + endif() + + math(EXPR END "${FILES_LENGTH} - 1") + set(NUMFILES ${FILES_LENGTH}) + + foreach(i RANGE ${END}) + list(GET FILES ${i} FILE) + list(GET EMBEDDED_LOCATIONS ${i} EMBEDDED_LOCATION) + + get_filename_component(BASE_PATH ${EMBEDDED_LOCATION} DIRECTORY) + get_filename_component(BASE_FILENAME ${EMBEDDED_LOCATION} NAME_WE) + get_filename_component(EXTENSION ${EMBEDDED_LOCATION} EXT) + + if (EXTENSION) + string(REPLACE "." "_" EXTENSION ${EXTENSION}) + endif() + + # EMBED_SOURCE_FILE is the fully qualified path to a cxx file with the embedded data + set(EMBED_SOURCE_FILE "${CMAKE_CURRENT_BINARY_DIR}/embedded_files/${BASE_PATH}/${BASE_FILENAME}${EXTENSION}.cxx") + set(EMBED_SOURCE_PATH "${CMAKE_CURRENT_BINARY_DIR}/embedded_files/${BASE_PATH}") + file(RELATIVE_PATH EMBED_SOURCE_FILE_REL_PATH ${PROJECT_BINARY_DIR} ${EMBED_SOURCE_FILE}) + list(APPEND EMBED_SOURCE_FILES ${EMBED_SOURCE_FILE}) + + # Fixup the name. We want to make sure it starts with :/ + string(SUBSTRING ${EMBEDDED_LOCATION} 0 1 EMBEDDED_LOCATION_PREFIX) + string(SUBSTRING ${EMBEDDED_LOCATION} 0 2 EMBEDDED_LOCATION_PREFIX2) + if( EMBEDDED_LOCATION_PREFIX2 STREQUAL :/ ) + # don't do anything, already in embedded form + elseif( EMBEDDED_LOCATION_PREFIX STREQUAL / ) + set( EMBEDDED_LOCATION ":${EMBEDDED_LOCATION}" ) + else() + set( EMBEDDED_LOCATION ":/${EMBEDDED_LOCATION}" ) + endif() + + message("CreateEmbeddedSource ${FILE} ${EMBED_SOURCE_FILE} ${i} ${EMBEDDED_LOCATION}") + + add_custom_command(OUTPUT ${EMBED_SOURCE_FILE} + COMMAND ${CMAKE_COMMAND} -E make_directory ${EMBED_SOURCE_PATH} + COMMAND CreateEmbeddedSource + ${FILE} + ${EMBED_SOURCE_FILE} + ${i} + ${EMBEDDED_LOCATION} + DEPENDS ${FILE} + ) + + set(EMBEDDED_FILE_INCLUDES "${EMBEDDED_FILE_INCLUDES}#include <${EMBED_SOURCE_FILE_REL_PATH}>\n") + endforeach() + + # file names + foreach (arg RANGE ${END}) + set(EMBEDDED_FILE_NAMES "${EMBEDDED_FILE_NAMES} embedded_file_name_${arg}") + if (NOT arg EQUAL MAXFILECOUNT) + set(EMBEDDED_FILE_NAMES "${EMBEDDED_FILE_NAMES},") + endif() + set(EMBEDDED_FILE_NAMES "${EMBEDDED_FILE_NAMES}\n") + endforeach() + + # lengths + foreach (arg RANGE ${END}) + set(EMBEDDED_FILE_LENS "${EMBEDDED_FILE_LENS} embedded_file_len_${arg}") + if (NOT arg EQUAL MAXFILECOUNT) + set(EMBEDDED_FILE_LENS "${EMBEDDED_FILE_LENS},") + endif() + set(EMBEDDED_FILE_LENS "${EMBEDDED_FILE_LENS}\n") + endforeach() + + # datas + foreach (arg RANGE ${END}) + set(EMBEDDED_FILES "${EMBEDDED_FILES} embedded_file_${arg}") + if (NOT arg EQUAL MAXFILECOUNT) + set(EMBEDDED_FILES "${EMBEDDED_FILES},") + endif() + set(EMBEDDED_FILES "${EMBEDDED_FILES}\n") + endforeach() + + set_source_files_properties(${EMBED_SOURCE_FILES} PROPERTIES HEADER_FILE_ONLY TRUE) + + if( NAMESPACE ) + set(BEGIN_NAMESPACE "namespace ${NAMESPACE} {") + set(END_NAMESPACE "}") + endif() + + configure_file("${PROJECT_SOURCE_DIR}/embedded/embedded_files.hxx.in" "${CMAKE_CURRENT_BINARY_DIR}/embedded_files.hxx") + list(APPEND EMBED_SOURCE_FILES "${CMAKE_CURRENT_BINARY_DIR}/embedded_files.hxx") + + configure_file("${PROJECT_SOURCE_DIR}/embedded/embedded_files.cxx.in" "${CMAKE_CURRENT_BINARY_DIR}/embedded_files.cxx") + list(APPEND EMBED_SOURCE_FILES "${CMAKE_CURRENT_BINARY_DIR}/embedded_files.cxx") + + set(${CXX_OUTPUT_FILES} ${EMBED_SOURCE_FILES} PARENT_SCOPE) +endfunction() + diff --git a/embedded/embedded_files.cxx.in b/embedded/embedded_files.cxx.in new file mode 100644 index 0000000..2a27e48 --- /dev/null +++ b/embedded/embedded_files.cxx.in @@ -0,0 +1,50 @@ +#include "embedded_files.hxx" + +@BEGIN_NAMESPACE@ + +namespace embedded_files { + + static const size_t embedded_file_count = @NUMFILES@; + +@EMBEDDED_FILE_INCLUDES@ + + static const char *embedded_file_names[] = { +@EMBEDDED_FILE_NAMES@ + }; + + static const size_t embedded_file_lens[] = { +@EMBEDDED_FILE_LENS@ + }; + + static const uint8_t *embedded_files[] = { +@EMBEDDED_FILES@ + }; + + std::vector fileNames() { + static std::vector result; + if (result.empty()){ + result.reserve(embedded_file_count); + for (size_t i = 0; i < embedded_file_count; ++i) { + result.push_back(std::string(embedded_file_names[i])); + } + } + return result; + } + + std::map> files() + { + static std::map> fs; + if (fs.empty()){ + for (size_t i = 0; i < embedded_file_count; ++i) { + fs.insert(std::make_pair(std::string(embedded_file_names[i]), + std::make_pair(embedded_file_lens[i], + embedded_files[i]))); + } + } + return fs; + } + +} + +@END_NAMESPACE@ + diff --git a/embedded/embedded_files.hxx.in b/embedded/embedded_files.hxx.in new file mode 100644 index 0000000..f6b5999 --- /dev/null +++ b/embedded/embedded_files.hxx.in @@ -0,0 +1,218 @@ +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#include +#else +#include +#include +#endif +#include +#include + +typedef std::pair EmbeddedFile; + +#include +#include +#include + +#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__) +# include +# include +# define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY) +#else +# define SET_BINARY_MODE(file) +#endif + +#define CHUNK 16384 + +@BEGIN_NAMESPACE@ + + +/* Decompress from file source to file dest until stream ends or EOF. + inf() returns Z_OK on success, Z_MEM_ERROR if memory could not be + allocated for processing, Z_DATA_ERROR if the deflate data is + invalid or incomplete, Z_VERSION_ERROR if the version of zlib.h and + the version of the library linked do not match, or Z_ERRNO if there + is an error reading or writing the files. */ +inline int inf(const EmbeddedFile & file, std::vector & result) +{ + //std::vector result; + + int ret; + unsigned have; + z_stream strm; + unsigned char in[CHUNK]; + unsigned char out[CHUNK]; + + /* allocate inflate state */ + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.avail_in = 0; + strm.next_in = Z_NULL; + ret = inflateInit(&strm); + if (ret != Z_OK) + return ret; + + auto chars = file.second; + auto size = file.first; + + // point or location where we are at moving through the data array + auto begin = chars; + auto end = chars + size; + auto chunk_end = begin; + + /* decompress until deflate stream ends or end of file */ + while( begin != end ) { + if( (begin + CHUNK) < end ) { + std::copy(begin, begin + CHUNK, in); + chunk_end = begin + CHUNK; + } else { + std::copy(begin, end, in); + chunk_end = end; + } + + strm.next_in = const_cast(begin); + strm.avail_in = static_cast(chunk_end - begin); + + /* run inflate() on input until output buffer not full */ + do { + strm.avail_out = CHUNK; + strm.next_out = out; + ret = inflate(&strm, Z_NO_FLUSH); + assert(ret != Z_STREAM_ERROR); /* state not clobbered */ + switch (ret) { + case Z_NEED_DICT: + ret = Z_DATA_ERROR; /* and fall through */ + [[fallthrough]]; + case Z_DATA_ERROR: + [[fallthrough]]; + case Z_MEM_ERROR: + (void)inflateEnd(&strm); + return ret; + } + have = CHUNK - strm.avail_out; + result.insert(result.end(),out,out + have); + } while (strm.avail_out == 0); + + if( ret == Z_STREAM_END ) { + break; + } + + begin = chunk_end; + } + + /* clean up and return */ + (void)inflateEnd(&strm); + return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR; +} + + +namespace embedded_files { + std::map files(); + + std::vector fileNames(); + + inline std::string allFileNamesAsString(){ + std::string result; + for (const auto& fileName: fileNames()){ + result += (fileName + ";"); + } + return result; + } + + inline std::string findFirstFileByName(const std::string &t_filename) { + for (const auto& path : fileNames()){ + if (t_filename.size() > path.size()){ + continue; + } + if (std::equal(t_filename.rbegin(), t_filename.rend(), path.rbegin())){ + return path; + } + } + return std::string(); + } + + inline bool hasFile(const std::string &t_filename) { + const auto fs = fileNames(); + return (std::find(fs.begin(), fs.end(), t_filename) != fs.end()); + } + + inline std::string getFileAsString(const std::string &t_filename) { + const auto fs = files(); + const auto f = fs.find(t_filename); + if (f == fs.end()){ + throw std::runtime_error("Embedded file not found '" + t_filename + "'"); + } + std::vector inflated_data; + if( inf(f->second,inflated_data) != Z_OK ) { + throw std::runtime_error("Embedded file failed to inflate '" + t_filename + "'"); + } + return std::string(inflated_data.begin(),inflated_data.end()); + } + + inline void extractFile(const std::string &t_filename, const std::string &t_location) + { + const auto fs = files(); + + + const auto create_dirs = [](const std::string &t_path) { + const auto paths = [](const std::string &t_paths) { + std::vector subpaths; + const auto handle_subpath = [&subpaths](std::string &t_subpath) { + if (!t_subpath.empty()) { + subpaths.push_back(t_subpath); + t_subpath.clear(); + } + }; + + std::string curstring = ""; + for (const auto c : t_paths) { + if (c != '/') { + curstring.push_back(c); + } else { + handle_subpath(curstring); + } + } + handle_subpath(curstring); + return subpaths; + }(t_path); + + + std::string curpath = ""; + + for (const auto &p : paths) { + if (!curpath.empty()) curpath += '/'; + curpath += p; +#ifdef _MSC_VER + _mkdir(curpath.c_str()); +#else + mkdir(curpath.c_str(), 0777); +#endif + } + }; + + auto f = fs.at(t_filename); + const auto fullpath = t_location + '/' + t_filename; + create_dirs(std::string(std::begin(fullpath), std::begin(fullpath) + fullpath.rfind('/'))); + std::ofstream ofs(fullpath); + + std::vector inflated_data; + if( inf(f,inflated_data) != Z_OK ) { + throw std::runtime_error("Embedded file failed to inflate '" + t_filename + "'"); + } + + ofs.write(reinterpret_cast(inflated_data.data()), inflated_data.size()); + std::cout << "***** Extracted " << t_filename << " to: " << fullpath << " *****\n"; + } + +} + +@END_NAMESPACE@ + diff --git a/empty.cpp b/empty.cpp new file mode 100644 index 0000000..e69de29 diff --git a/main.cpp b/main.cpp index 6aa550c..a248317 100644 --- a/main.cpp +++ b/main.cpp @@ -1,8 +1,11 @@ -// CMake defines WITHPYTHON (or not) +// CMake defines WITHPYTHON / WITHRUBY (or not) #include #include + +#ifdef WITHRUBY #include "RubyEngine.hpp" +#endif #ifdef WITHPYTHON #include "PythonEngine.hpp" @@ -14,31 +17,41 @@ #include "Model.hpp" int main([[maybe_unused]] const int argc, [[maybe_unused]] const char* argv[]) { + + #ifdef WITHRUBY Test::RubyEngine ruby; + #endif + #ifdef WITHPYTHON Test::PythonEngine python{argc, argv}; #endif + #ifdef WITHRUBY ruby.exec(R"(puts("Hello from Ruby"))"); + #endif #ifdef WITHPYTHON python.exec(R"(print("Hello From Python"))"); #endif + #ifdef WITHRUBY ruby.registerType("Test::Measure *"); + #endif #ifdef WITHPYTHON python.registerType("Test::Measure *"); #endif + #ifdef WITHRUBY ruby.exec("require '/home/julien/Software/Cpp_Swig_Ruby_Python_MCVE/ruby/test_measure.rb'"); auto ruby_measure = ruby.eval("RubyTestMeasure.new()"); auto* ruby_measure_from_cpp = ruby.getAs(ruby_measure); assert(ruby_measure_from_cpp); std::cout << "Ruby measure name: " << ruby_measure_from_cpp->name() << '\n'; + #endif #ifdef WITHPYTHON - python.exec("import sys\nsys.path.append('/home/julien/Software/Cpp_Swig_Ruby_Python_MCVE/python/')"); + python.exec("import sys\nsys.path.append('/home/julien/Software/Cpp_Swig_Ruby_Python_MCVE/python/')\nprint(f'{sys.path}')"); python.exec("import test_measure"); auto python_measure = python.eval("test_measure.PythonTestMeasure()"); auto* python_measure_from_cpp = python.getAs(python_measure); @@ -71,11 +84,15 @@ int main([[maybe_unused]] const int argc, [[maybe_unused]] const char* argv[]) { std::cout << "C++: starting with " << sr.get_current_model().numObjects() << " objects\n"; printObjectNames(sr.get_current_model()); - std::cout << "\n\n========== START RUBY ==========\n"; + #ifdef WITHRUBY + std::cout << "\n\n========== START RUBY ==========\n"; + ruby_measure_from_cpp->run(sr); + std::cout << "========== FINISHED RUBY ==========\n\n"; + #else + std::cout << "\n\n########## RUBY ISN'T ENABLED ##########\n\n"; + #endif - ruby_measure_from_cpp->run(sr); - std::cout << "========== FINISHED RUBY ==========\n\n"; std::cout << "C++: " << sr.get_current_model().numObjects() << " objects\n"; printObjectNames(sr.get_current_model()); @@ -83,10 +100,14 @@ int main([[maybe_unused]] const int argc, [[maybe_unused]] const char* argv[]) { std::cout << "\n\n========== START PYTHON ==========\n"; python_measure_from_cpp->run(sr); std::cout << "========== FINISHED PYTHON ==========\n\n"; + #endif + + #if defined(WITHPYTHON) && defined(WITHRUBY) std::cout << "After Running Ruby and Python: model is named " << sr.get_current_model().getName() << '\n'; - #else - std::cout << "\n\n########## PYTHON ISN'T ENABLED ##########\n\n"; + #elif defined (WITHRUBY) std::cout << "After Running Ruby only: model is named " << sr.get_current_model().getName() << '\n'; + #elif defined(WITHPYTHON) + std::cout << "After Running Python only: model is named " << sr.get_current_model().getName() << '\n'; #endif for (const auto& op : sr.get_current_model().opsPerformed()) { diff --git a/python/test_measure.py b/python/test_measure.py index fa49215..21bf461 100644 --- a/python/test_measure.py +++ b/python/test_measure.py @@ -1,7 +1,8 @@ +# import sys +# sys.path.append("/home/julien/Software/Cpp_Swig_Ruby_Python_MCVE/build-modif/Products/python/") -import sys - -sys.path.append("/home/julien/Software/Cpp_Swig_Ruby_Python_MCVE/build-modif/Products/python/") +from time import time, ctime +print('Today is',ctime(time())) import mylib diff --git a/ruby/test_measure.rb b/ruby/test_measure.rb index a5794f3..c235cc9 100644 --- a/ruby/test_measure.rb +++ b/ruby/test_measure.rb @@ -1,4 +1,4 @@ -require '/home/julien/Software/Cpp_Swig_Ruby_Python_MCVE/build-modif/Products/ruby/mylib.so' +# require '/home/julien/Software/Cpp_Swig_Ruby_Python_MCVE/build-modif/Products/ruby/mylib.so' class RubyTestMeasure < Mylib::Measure def name From 5cca9f221cdcef09e41621ab97256f735e015457 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Tue, 11 May 2021 18:07:39 +0200 Subject: [PATCH 12/16] Python works?! (!!) (set BUILD_RUBY_BINDINGS to OFF) Embbed mylib.py (swig generated) and initialized _mylib (the statically linked _mylib.a) and eval this embedded mylib.py ```bash cmake -G Ninja -DCMAKE_EXPORT_COMPILE_COMMANDS:BOOL=ON -DCMAKE_BUILD_TYPE:String=Debug -DBUILD_RUBY_BINDINGS:BOOL=OFF .. ``` --- CMakeLists.txt | 25 ++++++++++++++++++ PythonEngine.cpp | 66 +++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 87 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8ac7d42..48dc0d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -247,6 +247,28 @@ if(BUILD_PYTHON_BINDINGS) "-python" -external-runtime "${CMAKE_CURRENT_BINARY_DIR}/python_wrapper/SWIGPythonRuntime.hxx") + message("Searching for python modules in '${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/python/*.py'") + file(GLOB_RECURSE EXTENSION_PY FOLLOW_SYMLINKS "${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/python/*.py") + + # Temporarily set it here, otherwise it doesn't exist when CMake is run the first time... Should add a DEPENDS variable to embed_files... + set (EXTENSION_PY "${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/python/mylib.py") + message("EXTENSION_PY=${EXTENSION_PY}") + foreach( _FILE ${EXTENSION_PY} ) + file(RELATIVE_PATH LOCATION ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY} ${_FILE}) + list(APPEND FILES ${_FILE}) + list(APPEND EMBEDDED_PATHS ${LOCATION}) + endforeach() + message("FILES=${FILES}") + message("EMBEDDED_PATHS=${EMBEDDED_PATHS}") + + embed_files("${FILES}" "${EMBEDDED_PATHS}" EMBEDDED_OUTPUT) + + set_source_files_properties(${EMBED_SOURCE_FILES} PROPERTIES HEADER_FILE_ONLY TRUE) + + source_group(embedded_files FILES ${EMBEDDED_OUTPUT}) + + message("EMBEDDED_OUTPUT=${EMBEDDED_OUTPUT}") + endif() if(BUILD_RUBY_BINDINGS) @@ -406,6 +428,9 @@ elseif (BUILD_PYTHON_BINDINGS) ScriptEngine.hpp PythonEngine.hpp PythonEngine.cpp + ${EMBEDDED_OUTPUT} + "${CMAKE_CURRENT_BINARY_DIR}/embedded_files.cxx" + "${CMAKE_CURRENT_BINARY_DIR}/embedded_files.hxx" "${CMAKE_CURRENT_BINARY_DIR}/python_wrapper/SWIGPythonRuntime.hxx" ) diff --git a/PythonEngine.cpp b/PythonEngine.cpp index c58a0a7..4b4aac2 100644 --- a/PythonEngine.cpp +++ b/PythonEngine.cpp @@ -9,8 +9,9 @@ #endif #include -#include "SWIGPythonRuntime.hxx" - +//#include "SWIGPythonRuntime.hxx" +#include "MeasurePYTHON_wrap.cxx" +#include "embedded_files.hxx" #ifdef __GNUC__ # pragma GCC diagnostic pop #endif @@ -23,7 +24,10 @@ PythonEngine::PythonEngine([[maybe_unused]] const int argc, const char* argv[]) exit(1); } + PyImport_AppendInittab("_mylib", PyInit__mylib); + Py_SetProgramName(program); // optional but recommended + Py_Initialize(); } @@ -96,8 +100,51 @@ void PythonEngine::exec(std::string_view sv) { throw std::runtime_error("Unable to add module __main__ for python script execution"); } - PyObject* d = PyModule_GetDict(m); - PyObject* v = PyRun_String(command.c_str(), Py_file_input, d, d); + PyObject* globalDict = PyModule_GetDict(m); + + // Find mylib.py (the python thing that swig spits out) + // PyRun_SimpleString("import sys\nsys.path.append('/home/julien/Software/Cpp_Swig_Ruby_Python_MCVE/build-modif/temp/')\nprint(f'{sys.path}')"); + + //std::string myModuleStr("def add( n1 , n2 ):\n return n1+n2"); + //PyObject* pCompiledFn = Py_CompileString( myModuleStr.c_str() , "" , Py_file_input ); + + + std::string fileContent = embedded_files::getFileAsString(":/python/mylib.py"); + // std::cout << fileContent << '\n'; + + //PyObject* pCompiledFn = Py_CompileString( fileContent.c_str() , "" , Py_file_input ) ; + // PyObject* pModule = PyImport_ExecCodeModule( "mylib" , pCompiledFn ) ; + + /* + PyObject *pyModule = PyModule_New("mylib"); + // Set properties on the new module object + PyModule_AddStringConstant(pyModule, "__file__", ""); + //PyObject *localDict = PyModule_GetDict(pyModule); // Returns a borrowed reference: no need to Py_DECREF() it once we are done + PyObject *builtins = PyEval_GetBuiltins(); // Returns a borrowed reference: no need to Py_DECREF() it once we are done + PyDict_SetItemString(globalDict, "__builtins__", builtins); + PyDict_SetItemString(globalDict, "mylib", pyModule); + + // Define code in the newly created module + PyObject *pyValue = PyRun_String(fileContent.c_str(), Py_file_input, globalDict, globalDict); + if (pyValue == NULL) { + // Handle error + std::cout << "Unable to initialize mylib\n"; + } else { + Py_DECREF(pyValue); + } +*/ + + PyObject *builtins = PyEval_GetBuiltins(); + PyObject *compile = PyDict_GetItemString(builtins, "compile"); + PyObject *code = PyObject_CallFunction(compile, "sss", fileContent.c_str(), "mylib.py", "exec"); + PyObject *pyModule = PyImport_ExecCodeModule("mylib", code); + + + // PyRun_SimpleString("import sys\nsys.path.append(':')\nprint(f'{sys.path}')"); + // PySys_SetPath(L"/home/julien/Software/Cpp_Swig_Ruby_Python_MCVE/build-modif/Products/python"); // Find mylib.py + PyRun_SimpleString("print('Hello from PythonEngine::exec')"); + + PyObject* v = PyRun_String(command.c_str(), Py_file_input, globalDict, globalDict); if (v == nullptr) { PyErr_Print(); throw std::runtime_error("Error executing Python code"); @@ -105,6 +152,9 @@ void PythonEngine::exec(std::string_view sv) { //decref count returned from Python Py_DECREF(v); + Py_DecRef( pyModule ) ; + //Py_DecRef( pyCompiledFn ) ; + } ScriptObject PythonEngine::eval(std::string_view sv) { @@ -116,6 +166,14 @@ ScriptObject PythonEngine::eval(std::string_view sv) { } PyObject* d = PyModule_GetDict(m); + + std::string fileContent = embedded_files::getFileAsString(":/python/mylib.py"); + //std::cout << fileContent << '\n'; + //PySys_SetPath(L"/home/julien/Software/Cpp_Swig_Ruby_Python_MCVE/build-modif/Products/python"); // Find mylib.py + PyRun_SimpleString("print('Hello from PythonEngine::eval')"); + //PyObject *mylib_module = PyImport_Import(PyString_FromString("mylib")); + //PyModule_AddObject(m, "mylib", mylib_module); + PyObject* v = PyRun_String(command.c_str(), Py_eval_input, d, d); if (v == nullptr) { PyErr_Print(); From 1df0ebf2de5d56382f4e62c95f20f7ef573f9428 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Tue, 11 May 2021 21:47:36 +0200 Subject: [PATCH 13/16] A shot in the dark with ruby. Having tons of problems Init_mylib doesn't work. it segfaults ``` * thread #1, name = 'Test', stop reason = signal SIGSEGV: invalid address (fault address: 0x38) frame #0: 0x00005555558bdd17 Test`rb_usascii_encoding at encoding.c:1342:45 1339 rb_encoding * 1340 rb_usascii_encoding(void) 1341 { -> 1342 return enc_table.list[ENCINDEX_US_ASCII].enc; 1343 } 1344 1345 int ``` So I'm trying to initialize all encodings etc, but I'm not getting anywhere. Trying to include GC_VALUE.hpp results in a multiple definition such as `[100%][2/2] Linking CXX executable Products/Test FAILED: Products/Test : && /usr/bin/c++ -g CMakeFiles/mylib_ruby.dir/ruby_wrapper/MeasureRUBY_wrap.cxx.o CMakeFiles/Test.dir/main.cpp.o CMakeFiles/Test.dir/PythonEngine.cpp.o CMakeFiles/Test.dir/RubyEngine.cpp.o CMakeFiles/Test.dir/embedded_scripting_wrap.cxx.o CMakeFiles/Test.dir/embedded_files.cxx.o -o Products/Test Products/libmylib.a Products/libruby_libs.a /home/julien/.pyenv/versions/3.9.1/lib/python3.9/config-3.9-x86_64-linux-gnu/libpython3.9.a Products/python/_mylib.a -licui18n -licuuc -lgmp /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/libruby-static.a /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/enc/libenc.a /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/enc/libtrans.a /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/ext/rmd160.a /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/ext/generator.a /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/ext/pty.a /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/ext/coverage.a /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/ext/objspace.a /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/ext/fcntl.a /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/ext/etc.a /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/ext/bigdecimal.a /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/ext/ripper.a /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/ext/fiddle.a /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/ext/md5.a /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/ext/fiber.a /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/ext/sha1.a /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/ext/stringio.a /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/ext/zlib.a /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/ext/strscan.a /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/ext/escape.a /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/ext/bubblebabble.a /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/ext/nonblock.a /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/ext/cparse.a /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/ext/console.a /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/ext/dbm.a /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/ext/openssl.a /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/ext/wait.a /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/ext/gdbm.a /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/ext/date_core.a /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/ext/sizeof.a /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/ext/syslog.a /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/ext/monitor.a /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/ext/sdbm.a /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/ext/nkf.a /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/ext/digest.a /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/ext/sha2.a /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/ext/readline.a /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/ext/continuation.a /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/ext/pathname.a /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/ext/socket.a /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/ext/parser.a /home/julien/.conan/data/openstudio_ruby/2.7.2/nrel/testing/package/8c98c8e7c8941d8bf2c5b65aa3303761e4c9336c/lib/ext/psych.a /home/julien/.conan/data/openssl/1.1.0l/_/_/package/75e99b627c196b65c439728670655ec3c366b334/lib/libssl.a /home/julien/.conan/data/openssl/1.1.0l/_/_/package/75e99b627c196b65c439728670655ec3c366b334/lib/libcrypto.a -ldl -lpthread /home/julien/.conan/data/zlib/1.2.11/_/_/package/24a116b2ab4212d508978c9d35c6a171a9b73fee/lib/libminizip.a /home/julien/.conan/data/zlib/1.2.11/_/_/package/24a116b2ab4212d508978c9d35c6a171a9b73fee/lib/libz.a /home/julien/.conan/data/libyaml/0.2.5/_/_/package/75e99b627c196b65c439728670655ec3c366b334/lib/libyaml.a /home/julien/.conan/data/libffi/3.3/_/_/package/75e99b627c196b65c439728670655ec3c366b334/lib/libffi.a /home/julien/.conan/data/gdbm/1.18.1/_/_/package/000d6b046920d22f02cec11ec895b1b45927eb08/lib/libgdbm_compat.a /home/julien/.conan/data/gdbm/1.18.1/_/_/package/000d6b046920d22f02cec11ec895b1b45927eb08/lib/libgdbm.a /home/julien/.conan/data/readline/8.0/_/_/package/adcbf8ecbce351b257a7edb13a6afc81d2e5c6dd/lib/libhistory.a /home/julien/.conan/data/readline/8.0/_/_/package/adcbf8ecbce351b257a7edb13a6afc81d2e5c6dd/lib/libreadline.a /home/julien/.conan/data/termcap/1.3.1/_/_/package/75e99b627c196b65c439728670655ec3c366b334/lib/libtermcap.a /home/julien/.conan/data/gmp/6.2.0/_/_/package/f18760887692af6dc18ffcc3bf0a48e1c02322ca/lib/libgmpxx.a /home/julien/.conan/data/gmp/6.2.0/_/_/package/f18760887692af6dc18ffcc3bf0a48e1c02322ca/lib/libgmp.a Products/libadditional_ruby_libs.a -lanl -lrt -lcrypt -lpthread -ldl -lutil -lm Products/libmylib.a && : CMakeFiles/Test.dir/RubyEngine.cpp.o:(.bss+0x0): multiple definition of `swig::GC_VALUE::hash_id' CMakeFiles/mylib_ruby.dir/ruby_wrapper/MeasureRUBY_wrap.cxx.o:(.bss+0x0): first defined here CMakeFiles/Test.dir/RubyEngine.cpp.o:(.bss+0x8): multiple definition of `swig::GC_VALUE::lt_id' CMakeFiles/mylib_ruby.dir/ruby_wrapper/MeasureRUBY_wrap.cxx.o:(.bss+0x8): first defined here CMakeFiles/Test.dir/RubyEngine.cpp.o:(.bss+0x10): multiple definition of `swig::GC_VALUE::gt_id' CMakeFiles/mylib_ruby.dir/ruby_wrapper/MeasureRUBY_wrap.cxx.o:(.bss+0x10): first defined here CMakeFiles/Test.dir/RubyEngine.cpp.o:(.bss+0x18): multiple definition of `swig::GC_VALUE::eq_id' CMakeFiles/mylib_ruby.dir/ruby_wrapper/MeasureRUBY_wrap.cxx.o:(.bss+0x18): first defined here CMakeFiles/Test.dir/RubyEngine.cpp.o:(.bss+0x20): multiple definition of `swig::GC_VALUE::le_id' CMakeFiles/mylib_ruby.dir/ruby_wrapper/MeasureRUBY_wrap.cxx.o:(.bss+0x20): first defined here CMakeFiles/Test.dir/RubyEngine.cpp.o:(.bss+0x28): multiple definition of `swig::GC_VALUE::ge_id' CMakeFiles/mylib_ruby.dir/ruby_wrapper/MeasureRUBY_wrap.cxx.o:(.bss+0x28): first defined here CMakeFiles/Test.dir/RubyEngine.cpp.o:(.bss+0x30): multiple definition of `swig::GC_VALUE::pos_id' CMakeFiles/mylib_ruby.dir/ruby_wrapper/MeasureRUBY_wrap.cxx.o:(.bss+0x30): first defined here CMakeFiles/Test.dir/RubyEngine.cpp.o:(.bss+0x38): multiple definition of `swig::GC_VALUE::neg_id' CMakeFiles/mylib_ruby.dir/ruby_wrapper/MeasureRUBY_wrap.cxx.o:(.bss+0x38): first defined here CMakeFiles/Test.dir/RubyEngine.cpp.o:(.bss+0x40): multiple definition of `swig::GC_VALUE::inv_id' CMakeFiles/mylib_ruby.dir/ruby_wrapper/MeasureRUBY_wrap.cxx.o:(.bss+0x40): first defined here CMakeFiles/Test.dir/RubyEngine.cpp.o:(.bss+0x48): multiple definition of `swig::GC_VALUE::add_id' CMakeFiles/mylib_ruby.dir/ruby_wrapper/MeasureRUBY_wrap.cxx.o:(.bss+0x48): first defined here CMakeFiles/Test.dir/RubyEngine.cpp.o:(.bss+0x50): multiple definition of `swig::GC_VALUE::sub_id' CMakeFiles/mylib_ruby.dir/ruby_wrapper/MeasureRUBY_wrap.cxx.o:(.bss+0x50): first defined here CMakeFiles/Test.dir/RubyEngine.cpp.o:(.bss+0x58): multiple definition of `swig::GC_VALUE::mul_id' CMakeFiles/mylib_ruby.dir/ruby_wrapper/MeasureRUBY_wrap.cxx.o:(.bss+0x58): first defined here CMakeFiles/Test.dir/RubyEngine.cpp.o:(.bss+0x60): multiple definition of `swig::GC_VALUE::div_id' CMakeFiles/mylib_ruby.dir/ruby_wrapper/MeasureRUBY_wrap.cxx.o:(.bss+0x60): first defined here CMakeFiles/Test.dir/RubyEngine.cpp.o:(.bss+0x68): multiple definition of `swig::GC_VALUE::mod_id' CMakeFiles/mylib_ruby.dir/ruby_wrapper/MeasureRUBY_wrap.cxx.o:(.bss+0x68): first defined here CMakeFiles/Test.dir/RubyEngine.cpp.o:(.bss+0x70): multiple definition of `swig::GC_VALUE::and_id' CMakeFiles/mylib_ruby.dir/ruby_wrapper/MeasureRUBY_wrap.cxx.o:(.bss+0x70): first defined here CMakeFiles/Test.dir/RubyEngine.cpp.o:(.bss+0x78): multiple definition of `swig::GC_VALUE::or_id' CMakeFiles/mylib_ruby.dir/ruby_wrapper/MeasureRUBY_wrap.cxx.o:(.bss+0x78): first defined here CMakeFiles/Test.dir/RubyEngine.cpp.o:(.bss+0x80): multiple definition of `swig::GC_VALUE::xor_id' CMakeFiles/mylib_ruby.dir/ruby_wrapper/MeasureRUBY_wrap.cxx.o:(.bss+0x80): first defined here CMakeFiles/Test.dir/RubyEngine.cpp.o:(.bss+0x88): multiple definition of `swig::GC_VALUE::lshift_id' CMakeFiles/mylib_ruby.dir/ruby_wrapper/MeasureRUBY_wrap.cxx.o:(.bss+0x88): first defined here CMakeFiles/Test.dir/RubyEngine.cpp.o:(.bss+0x90): multiple definition of `swig::GC_VALUE::rshift_id' CMakeFiles/mylib_ruby.dir/ruby_wrapper/MeasureRUBY_wrap.cxx.o:(.bss+0x90): first defined here collect2: error: ld returned 1 exit status ninja: build stopped: subcommand failed.` --- CMakeLists.txt | 130 +++- EmbeddedHelp.hpp | 39 ++ EmbeddedScripting.i | 24 + GC_Value.hpp | 120 ++++ Measure.i | 6 - RubyEngine.cpp | 540 ++++++++++++++++- RubyEngine.hpp | 1195 +++++++++++++++++++++++++++++++++++++ embedded/EmbedFiles.cmake | 2 +- embedded_help.rb | 792 ++++++++++++++++++++++++ osruby_config.h | 19 + 10 files changed, 2822 insertions(+), 45 deletions(-) create mode 100644 EmbeddedHelp.hpp create mode 100644 EmbeddedScripting.i create mode 100644 GC_Value.hpp create mode 100644 embedded_help.rb create mode 100644 osruby_config.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 48dc0d4..a23c2bf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,7 +9,7 @@ if(MSVC) target_compile_options(cpp_warning_options INTERFACE /W4 /Werror) else() target_compile_options( - cpp_warning_options INTERFACE -Wall -Wextra -Wconversion -Werror -Wno-unused-variable) + cpp_warning_options INTERFACE -Wall -Wextra -Wconversion -Werror -Wno-unused-variable -Wno-unused-parameter) # target_compile_options(cpp_compile_options # INTERFACE -fsanitize=undefined,address) # target_link_options(cpp_compile_options INTERFACE @@ -226,8 +226,9 @@ if(BUILD_PYTHON_BINDINGS) # target_include_directories(${swig_target_name} PRIVATE SYSTEM # ${Python_INCLUDE_DIRS}) - swig_link_libraries(${swig_target_name} PUBLIC ${target_name} PRIVATE - cpp_compile_options) + swig_link_libraries(${swig_target_name} + PUBLIC ${target_name} + PRIVATE cpp_compile_options) # if(MSVC) swig_link_libraries(${swig_target_name} PRIVATE Python::Module) @@ -258,16 +259,6 @@ if(BUILD_PYTHON_BINDINGS) list(APPEND FILES ${_FILE}) list(APPEND EMBEDDED_PATHS ${LOCATION}) endforeach() - message("FILES=${FILES}") - message("EMBEDDED_PATHS=${EMBEDDED_PATHS}") - - embed_files("${FILES}" "${EMBEDDED_PATHS}" EMBEDDED_OUTPUT) - - set_source_files_properties(${EMBED_SOURCE_FILES} PROPERTIES HEADER_FILE_ONLY TRUE) - - source_group(embedded_files FILES ${EMBEDDED_OUTPUT}) - - message("EMBEDDED_OUTPUT=${EMBEDDED_OUTPUT}") endif() @@ -275,15 +266,85 @@ if(BUILD_RUBY_BINDINGS) add_compile_definitions(WITHRUBY) add_compile_definitions(RUBY_EMBEDDED) + + set(CMAKE_SWIG_FLAGS ${CMAKE_SWIG_FLAGS} -fvirtual) + + set(MODULE_ROOT "${CONAN_OPENSTUDIO_RUBY_ROOT}") + if ("${MODULE_ROOT}" STREQUAL "") + set(MODULE_ROOT "${CONAN_OPENSTUDIO_RUBY_ROOT_DEBUG}") + endif() + + message("Searching for ruby modules in '${MODULE_ROOT}/lib/**/*.rb'") + file(GLOB_RECURSE EXTENSION_RB FOLLOW_SYMLINKS "${MODULE_ROOT}/lib/**/*.rb") + + foreach( _FILE ${EXTENSION_RB} ) + file(RELATIVE_PATH LOCATION ${MODULE_ROOT}/lib ${_FILE}) + list(APPEND FILES ${_FILE}) + list(APPEND EMBEDDED_PATHS ${LOCATION}) + endforeach() + + list(APPEND FILES "${CMAKE_CURRENT_SOURCE_DIR}/embedded_help.rb") + list(APPEND EMBEDDED_PATHS "embedded_help.rb") + + set_source_files_properties(EmbeddedScripting.i + PROPERTIES CPLUSPLUS ON + ) + + # TODO: replace with swig_add_library + set(SWIG_LIB "/home/julien/Software/Others/swig/swig-build/swig-install/share/swig/4.1.0/") + + add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/embedded_scripting_wrap.cxx" + COMMAND ${CMAKE_COMMAND} -E env SWIG_LIB="${SWIG_LIB}" + "${SWIG_EXECUTABLE}" + "-ruby" + "-c++" + -o "${CMAKE_CURRENT_BINARY_DIR}/embedded_scripting_wrap.cxx" + "-fvirtual" + "-I${PROJECT_SOURCE_DIR}" + "-I${PROJECT_BINARY_DIR}" + "-D_WINDOWS" + "-Fmicrosoft" + "${CMAKE_CURRENT_SOURCE_DIR}/EmbeddedScripting.i" + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/EmbeddedScripting.i" + "${CMAKE_CURRENT_BINARY_DIR}/embedded_files.hxx" + "EmbeddedHelp.hpp" + ) + + set(swig_target_name ${target_name}_ruby) + if(TRUE) swig_add_library( ${swig_target_name} - TYPE STATIC + TYPE OBJECT LANGUAGE ruby OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/Products/ruby" OUTFILE_DIR "${CMAKE_CURRENT_BINARY_DIR}/ruby_wrapper" SOURCES ${${target_name}_swig_src}) + else() + set(SWIG_WRAPPER "MeasureRUBY_wrap.cxx") + set(SWIG_WRAPPER_FULL_PATH "${CMAKE_CURRENT_BINARY_DIR}/ruby_wrapper/${SWIG_WRAPPER}") + set(MODULE "Mylib") + set(LOWER_NAME "mylib") + set(KEY_I_FILE "Measure.i") + add_custom_command( + OUTPUT "${SWIG_WRAPPER}" + COMMAND ${CMAKE_COMMAND} -E env SWIG_LIB="${SWIG_LIB}" + "${SWIG_EXECUTABLE}" + "-ruby" "-c++" "-fvirtual" "-I${PROJECT_SOURCE_DIR}" "-I${PROJECT_BINARY_DIR}" + -module "${MODULE}" -initname "${LOWER_NAME}" + "-I${PROJECT_SOURCE_DIR}/ruby" + -o "${SWIG_WRAPPER_FULL_PATH}" + "${SWIG_DEFINES}" ${SWIG_COMMON} "${KEY_I_FILE}" + DEPENDS ${this_depends} + ) + add_library( + ${swig_target_name} OBJECT + ${SWIG_WRAPPER_FULL_PATH} + ) + endif() + set_target_properties(${swig_target_name} PROPERTIES OUTPUT_NAME ${target_name}) @@ -300,9 +361,15 @@ if(BUILD_RUBY_BINDINGS) target_include_directories( ${swig_target_name} PRIVATE SYSTEM ${RUBY_INCLUDE_DIRS} ) - swig_link_libraries(${swig_target_name} PUBLIC ${target_name} PRIVATE - ${RUBY_LIBRARIES} cpp_compile_options) - + if(TRUE) + swig_link_libraries(${swig_target_name} + PUBLIC ${target_name} + PRIVATE ${RUBY_LIBRARIES} cpp_compile_options) + else() + target_link_libraries(${swig_target_name} + PUBLIC ${target_name} + PRIVATE ${RUBY_LIBRARIES} cpp_compile_options) + endif() add_test( NAME test_ruby_only WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/ruby @@ -319,9 +386,28 @@ if(BUILD_RUBY_BINDINGS) "${CMAKE_CURRENT_BINARY_DIR}/ruby_wrapper/SWIGRubyRuntime.hxx") set_source_files_properties("${CMAKE_CURRENT_BINARY_DIR}/ruby_wrapper/SWIGRubyRuntime.hxx" PROPERTIES COMPILE_FLAGS "-DRUBY_EMBEDDED -Wno-unused-variable" ) + # -Wno-deprecated-declaration, /wd4996: suppresses deprecated warning + # -Wno-register, /wd5033: ISO C++1z does not allow *register* storage class specifier + if(MSVC) + set_source_files_properties("${CMAKE_CURRENT_BINARY_DIR}/embedded_scripting_wrap.cxx" PROPERTIES COMPILE_FLAGS "-DRUBY_EMBEDDED" ) + else() + set_source_files_properties("${CMAKE_CURRENT_BINARY_DIR}/embedded_scripting_wrap.cxx" PROPERTIES COMPILE_FLAGS "-DRUBY_EMBEDDED -Wno-unused-variable" ) + endif() endif() +#message("FILES=${FILES}") +#message("EMBEDDED_PATHS=${EMBEDDED_PATHS}") + +embed_files("${FILES}" "${EMBEDDED_PATHS}" EMBEDDED_OUTPUT) + +set_source_files_properties(${EMBED_SOURCE_FILES} PROPERTIES HEADER_FILE_ONLY TRUE) + +source_group(embedded_files FILES ${EMBEDDED_OUTPUT}) + +# message("EMBEDDED_OUTPUT=${EMBEDDED_OUTPUT}") + + if(BUILD_RUBY_BINDINGS AND BUILD_PYTHON_BINDINGS) add_test( @@ -352,18 +438,22 @@ if (BUILD_PYTHON_BINDINGS AND BUILD_RUBY_BINDINGS) add_executable( Test main.cpp + "${CMAKE_CURRENT_BINARY_DIR}/ruby_wrapper/SWIGRubyRuntime.hxx" ScriptEngine.hpp PythonEngine.hpp PythonEngine.cpp RubyEngine.hpp RubyEngine.cpp - "${CMAKE_CURRENT_BINARY_DIR}/ruby_wrapper/SWIGRubyRuntime.hxx" + "${CMAKE_CURRENT_BINARY_DIR}/embedded_scripting_wrap.cxx" + ${EMBEDDED_OUTPUT} "${CMAKE_CURRENT_BINARY_DIR}/python_wrapper/SWIGPythonRuntime.hxx") target_link_libraries(Test PRIVATE ${target_name} cpp_compile_options cpp_warning_options) + add_definitions(-DRUBY_EXTCONF_H="osruby_config.h") + add_library(additional_ruby_libs empty.cpp) add_library(ruby_libs empty.cpp) target_link_libraries(ruby_libs PUBLIC CONAN_PKG::openstudio_ruby) @@ -376,10 +466,12 @@ if (BUILD_PYTHON_BINDINGS AND BUILD_RUBY_BINDINGS) #init_Test ruby_libs ${ALL_RUBY_BINDING_TARGETS} + ${target_name}_ruby Python::Python ${target_name}_python ) + # target_link_libraries(Test PUBLIC "-Wl,--allow-multiple-definition" ) if (UNIX AND NOT APPLE) target_link_libraries(additional_ruby_libs anl rt) @@ -405,6 +497,8 @@ elseif (BUILD_RUBY_BINDINGS) ScriptEngine.hpp RubyEngine.hpp RubyEngine.cpp + "${CMAKE_CURRENT_BINARY_DIR}/embedded_scripting_wrap.cxx" + ${EMBEDDED_OUTPUT} "${CMAKE_CURRENT_BINARY_DIR}/ruby_wrapper/SWIGRubyRuntime.hxx" ) diff --git a/EmbeddedHelp.hpp b/EmbeddedHelp.hpp new file mode 100644 index 0000000..f6ea1d1 --- /dev/null +++ b/EmbeddedHelp.hpp @@ -0,0 +1,39 @@ +#ifndef CLI_EMBEDDEDHELP_HPP +#define CLI_EMBEDDEDHELP_HPP + +#include +#if defined __APPLE__ +# include /* _NSGetExecutablePath */ +# include /* PATH_MAX */ +#elif defined _WIN32 +# include +#endif + +// Can't seem to find ext-init?! +//#ifdef SWIG +//%include +//#else +//# include +//#endif + +namespace embedded_help { + +inline std::string applicationFilePath() { +#ifdef __APPLE__ + char path[PATH_MAX + 1]; + uint32_t size = sizeof(path); + if (_NSGetExecutablePath(path, &size) == 0) { + return std::string(path); + } +#elif defined _WIN32 + TCHAR szPath[MAX_PATH]; + if (!GetModuleFileName(nullptr, szPath, MAX_PATH)) { + return std::string(szPath); + } +#endif + return std::string(); +} + +} // namespace embedded_help + +#endif // CLI_EMBEDDEDHELP_HPP diff --git a/EmbeddedScripting.i b/EmbeddedScripting.i new file mode 100644 index 0000000..9f28fb6 --- /dev/null +++ b/EmbeddedScripting.i @@ -0,0 +1,24 @@ +#ifndef EMBEDDED_SCRIPTING_I +#define EMBEDDED_SCRIPTING_I + +%module EmbeddedScripting + +%include + + +// DLM: including these files causes a crash, I don't know why +//%include +//%import +//%import + +%{ + #include + #include "EmbeddedHelp.hpp" +%} + +%ignore embedded_files::fileNames; + +%include +%include "EmbeddedHelp.hpp" + +#endif // EMBEDDED_SCRIPTING_I diff --git a/GC_Value.hpp b/GC_Value.hpp new file mode 100644 index 0000000..5e6447a --- /dev/null +++ b/GC_Value.hpp @@ -0,0 +1,120 @@ +#ifndef CLI_GC_VALUE_HPP +#define CLI_GC_VALUE_HPP + +namespace swig { +class GC_VALUE +{ + public: + VALUE _obj; + + static ID hash_id; + static ID lt_id; + static ID gt_id; + static ID eq_id; + static ID le_id; + static ID ge_id; + + static ID pos_id; + static ID neg_id; + static ID inv_id; + + static ID add_id; + static ID sub_id; + static ID mul_id; + static ID div_id; + static ID mod_id; + + static ID and_id; + static ID or_id; + static ID xor_id; + + static ID lshift_id; + static ID rshift_id; + + struct OpArgs + { + VALUE src; + ID id; + int nargs; + VALUE target; + }; + + public: + GC_VALUE(); + + GC_VALUE(const GC_VALUE& item); + + GC_VALUE(VALUE obj); + + ~GC_VALUE(); + + GC_VALUE& operator=(const GC_VALUE& item); + + operator VALUE() const; + + VALUE inspect() const; + + VALUE to_s() const; + + static VALUE swig_rescue_swallow(VALUE); + + static VALUE swig_rescue_funcall(VALUE p); + + bool relational_equal_op(const GC_VALUE& other, const ID& op_id, bool (*op_func)(const VALUE& a, const VALUE& b)) const; + + static bool operator_eq(const VALUE& a, const VALUE& b); + static bool operator_lt(const VALUE& a, const VALUE& b); + static bool operator_le(const VALUE& a, const VALUE& b); + static bool operator_gt(const VALUE& a, const VALUE& b); + static bool operator_ge(const VALUE& a, const VALUE& b); + + bool operator==(const GC_VALUE& other) const; + bool operator<(const GC_VALUE& other) const; + bool operator<=(const GC_VALUE& other) const; + bool operator>(const GC_VALUE& other) const; + bool operator>=(const GC_VALUE& other) const; + + bool operator!=(const GC_VALUE& other) const; + + GC_VALUE unary_op(const ID& op_id) const; + + GC_VALUE operator+() const; + GC_VALUE operator-() const; + GC_VALUE operator~() const; + + GC_VALUE binary_op(const GC_VALUE& other, const ID& op_id) const; + + GC_VALUE operator+(const GC_VALUE& other) const; + GC_VALUE operator-(const GC_VALUE& other) const; + GC_VALUE operator*(const GC_VALUE& other) const; + GC_VALUE operator/(const GC_VALUE& other) const; + GC_VALUE operator%(const GC_VALUE& other) const; + GC_VALUE operator&(const GC_VALUE& other) const; + GC_VALUE operator^(const GC_VALUE& other) const; + GC_VALUE operator|(const GC_VALUE& other) const; + GC_VALUE operator<<(const GC_VALUE& other) const; + GC_VALUE operator>>(const GC_VALUE& other) const; +}; +} // namespace swig + +ID swig::GC_VALUE::hash_id; +ID swig::GC_VALUE::lt_id; +ID swig::GC_VALUE::gt_id; +ID swig::GC_VALUE::eq_id; +ID swig::GC_VALUE::le_id; +ID swig::GC_VALUE::ge_id; +ID swig::GC_VALUE::pos_id; +ID swig::GC_VALUE::neg_id; +ID swig::GC_VALUE::inv_id; +ID swig::GC_VALUE::add_id; +ID swig::GC_VALUE::sub_id; +ID swig::GC_VALUE::mul_id; +ID swig::GC_VALUE::div_id; +ID swig::GC_VALUE::mod_id; +ID swig::GC_VALUE::and_id; +ID swig::GC_VALUE::or_id; +ID swig::GC_VALUE::xor_id; +ID swig::GC_VALUE::lshift_id; +ID swig::GC_VALUE::rshift_id; + +#endif // CLI_GC_VALUE_HPP diff --git a/Measure.i b/Measure.i index b21a16d..9812136 100644 --- a/Measure.i +++ b/Measure.i @@ -1,8 +1,6 @@ #ifndef MEASURE_I #define MEASURE_I -#include - %begin %{ // ... code in begin section ... %} @@ -26,11 +24,7 @@ %module mylib %module(directors="1") mylib - - %include -%include -%include %feature("director") Measure; diff --git a/RubyEngine.cpp b/RubyEngine.cpp index a1a4203..3012347 100644 --- a/RubyEngine.cpp +++ b/RubyEngine.cpp @@ -1,30 +1,15 @@ #include "RubyEngine.hpp" - #include #include -#ifdef __GNUC__ -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wregister" -# pragma GCC diagnostic ignored "-Wunused-parameter" -#endif - -#include "SWIGRubyRuntime.hxx" +#include "embedded_files.hxx" -#ifdef __GNUC__ -# pragma GCC diagnostic pop +// UNCOMMENT THIS! +// #define WITH_GC_VALUE 1 +#ifdef WITH_GC_VALUE +#include "GC_Value.hpp" #endif -namespace Test { - -RubyEngine::RubyEngine() { - ruby_setup(); -} - -RubyEngine::~RubyEngine() { - ruby_finalize(); -} - static VALUE evaluateSimpleImpl(VALUE arg) { return rb_eval_string(StringValuePtr(arg)); } @@ -47,6 +32,506 @@ VALUE evalString(const std::string& t_str) { return result; } + +extern "C" +{ + + void Init_EmbeddedScripting(void); + + void Init_mylib(void); + + VALUE init_rest_of_mylib(...) { + //Init_mylib(); + // rb_provide("mylib"); + // rb_provide("mylib.so"); + + //Init_mylib(); + return Qtrue; + } + + void Init_encdb(); + + //void Init_ascii(); // this is not included in libenc + void Init_big5(); + void Init_cp949(); + void Init_emacs_mule(); + void Init_euc_jp(); + void Init_euc_kr(); + void Init_euc_tw(); + void Init_gb18030(); + void Init_gb2312(); + void Init_gbk(); + void Init_iso_8859_1(); + void Init_iso_8859_10(); + void Init_iso_8859_11(); + void Init_iso_8859_13(); + void Init_iso_8859_14(); + void Init_iso_8859_15(); + void Init_iso_8859_16(); + void Init_iso_8859_2(); + void Init_iso_8859_3(); + void Init_iso_8859_4(); + void Init_iso_8859_5(); + void Init_iso_8859_6(); + void Init_iso_8859_7(); + void Init_iso_8859_8(); + void Init_iso_8859_9(); + void Init_koi8_r(); + void Init_koi8_u(); + void Init_shift_jis(); + //void Init_unicode(); // this is not included in libenc + //void Init_us_ascii(); // this is not included in libenc + void Init_utf_16be(); + void Init_utf_16le(); + void Init_utf_32be(); + void Init_utf_32le(); + //void Init_utf_8(); // this is not included in libenc + void Init_windows_1250(); + void Init_windows_1251(); + void Init_windows_1252(); + void Init_windows_1253(); + void Init_windows_1254(); + void Init_windows_1257(); + void Init_windows_31j(); + + void Init_transdb(); + + void Init_trans_big5(); + void Init_trans_chinese(); + void Init_trans_ebcdic(); + void Init_trans_emoji(); + void Init_trans_emoji_iso2022_kddi(); + void Init_trans_emoji_sjis_docomo(); + void Init_trans_emoji_sjis_kddi(); + void Init_trans_emoji_sjis_softbank(); + void Init_trans_escape(); + void Init_trans_gb18030(); + void Init_trans_gbk(); + void Init_trans_iso2022(); + void Init_trans_japanese(); + void Init_trans_japanese_euc(); + void Init_trans_japanese_sjis(); + void Init_trans_korean(); + void Init_trans_single_byte(); + void Init_trans_utf8_mac(); + void Init_trans_utf_16_32(); + + void Init_bigdecimal(); + void Init_bigdecimal(void); + void Init_continuation(void); + void Init_coverage(void); + void Init_cparse(void); + void Init_date_core(void); + void Init_digest(void); + void Init_escape(void); + void Init_etc(void); + void Init_fcntl(void); + void Init_fiber(void); + void Init_monitor(void); + void Init_fiddle(void); + void Init_generator(void); + void Init_md5(void); + void Init_nkf(void); + void Init_nonblock(void); + void Init_objspace(void); + void Init_parser(void); + void Init_pathname(void); + void Init_psych(void); + void Init_ripper(void); + void Init_rmd160(void); + void Init_sdbm(void); + void Init_sha1(void); + void Init_sha2(void); + void Init_sizeof(void); + void Init_socket(void); + void Init_stringio(void); + void Init_strscan(void); + //void Init_wait(void); + // void Init_zlib(void); + + void Init_openssl(void); + + void Init_nonblock(void); + +#ifndef _WIN32 + void Init_console(void); + void Init_dbm(void); + void Init_gdbm(void); + void Init_pty(void); + void Init_readline(void); + void Init_syslog(void); +#endif + +} + +namespace Test { + +RubyEngine::RubyEngine() { + + // ruby_setup(); + int argc = 0; + char **argv; + //Init_mylib(); + // ruby initialization for embedding is completely undocument from what we could find + // this code is based on reading the source for the official irb + ruby_sysinit(&argc, &argv); + { + RUBY_INIT_STACK; + ruby_init(); + +#ifdef WITH_GC_VALUE + swig::GC_VALUE::hash_id = rb_intern("hash"); + swig::GC_VALUE::lt_id = rb_intern("<"); + swig::GC_VALUE::gt_id = rb_intern(">"); + swig::GC_VALUE::eq_id = rb_intern("=="); + swig::GC_VALUE::le_id = rb_intern("<="); + swig::GC_VALUE::ge_id = rb_intern(">="); + + swig::GC_VALUE::pos_id = rb_intern("+@"); + swig::GC_VALUE::neg_id = rb_intern("-@"); + swig::GC_VALUE::inv_id = rb_intern("~"); + + swig::GC_VALUE::add_id = rb_intern("+"); + swig::GC_VALUE::sub_id = rb_intern("-"); + swig::GC_VALUE::mul_id = rb_intern("*"); + swig::GC_VALUE::div_id = rb_intern("/"); + swig::GC_VALUE::mod_id = rb_intern("%"); + + swig::GC_VALUE::and_id = rb_intern("&"); + swig::GC_VALUE::or_id = rb_intern("|"); + swig::GC_VALUE::xor_id = rb_intern("^"); + + swig::GC_VALUE::lshift_id = rb_intern("<<"); + swig::GC_VALUE::rshift_id = rb_intern(">>"); +#endif + + // in case any further init methods try to require files, init this first + Init_EmbeddedScripting(); + + auto embedded_extensions_string = embedded_files::getFileAsString(":/embedded_help.rb"); + + try { + evalString(embedded_extensions_string); + } catch (const std::exception& e) { + evalString(R"(STDOUT.flush)"); + std::cout << "Exception in embedded_help: " << e.what() << std::endl; // endl will flush + ruby_cleanup(1); + throw std::runtime_error("Error executing Ruby code"); + } catch (...) { + evalString(R"(STDOUT.flush)"); + std::cout << "Unknown Exception in embedded_help" << std::endl; // endl will flush + ruby_cleanup(1); + throw std::runtime_error("Error executing Ruby code"); + } + + //// encodings + Init_encdb(); + rb_provide("enc/encdb.so"); + //Init_ascii(); + //rb_provide("enc/ascii.so"); + Init_big5(); + rb_provide("enc/big5.so"); + Init_cp949(); + rb_provide("enc/cp949.so"); + Init_emacs_mule(); + rb_provide("enc/emacs_mule.so"); + Init_euc_jp(); + rb_provide("enc/euc_ip.so"); + Init_euc_kr(); + rb_provide("enc/euc_kr.so"); + Init_euc_tw(); + rb_provide("enc/euc_tw.so"); + Init_gb18030(); + rb_provide("enc/gb18030.so"); + Init_gb2312(); + rb_provide("enc/gb2312.so"); + Init_gbk(); + rb_provide("enc/gbk.so"); + Init_iso_8859_1(); + rb_provide("enc/iso_8859_1.so"); + Init_iso_8859_10(); + rb_provide("enc/iso_8859_10.so"); + Init_iso_8859_11(); + rb_provide("enc/iso_8859_11.so"); + Init_iso_8859_13(); + rb_provide("enc/iso_8859_13.so"); + Init_iso_8859_14(); + rb_provide("enc/iso_8859_14.so"); + Init_iso_8859_15(); + rb_provide("enc/iso_8859_15.so"); + Init_iso_8859_16(); + rb_provide("enc/iso_8859_16.so"); + Init_iso_8859_2(); + rb_provide("enc/iso_8859_2.so"); + Init_iso_8859_3(); + rb_provide("enc/iso_8859_3.so"); + Init_iso_8859_4(); + rb_provide("enc/iso_8859_4.so"); + Init_iso_8859_5(); + rb_provide("enc/iso_8859_5.so"); + Init_iso_8859_6(); + rb_provide("enc/iso_8859_6.so"); + Init_iso_8859_7(); + rb_provide("enc/iso_8859_7.so"); + Init_iso_8859_8(); + rb_provide("enc/iso_8859_8.so"); + Init_iso_8859_9(); + rb_provide("enc/iso_8859_9.so"); + Init_koi8_r(); + rb_provide("enc/koi8_r.so"); + Init_koi8_u(); + rb_provide("enc/koi8_u.so"); + Init_shift_jis(); + rb_provide("enc/shift_jis.so"); + //Init_unicode(); + //rb_provide("enc/unicode.so"); + //Init_us_ascii(); + //rb_provide("enc/us_ascii.so"); + Init_utf_16be(); + rb_provide("enc/utf_16be.so"); + Init_utf_16le(); + rb_provide("enc/utf_16le.so"); + Init_utf_32be(); + rb_provide("enc/utf_32be.so"); + Init_utf_32le(); + rb_provide("enc/utf_32le.so"); + //Init_utf_8(); + //rb_provide("enc/utf_8.so"); + Init_windows_1250(); + rb_provide("enc/windows_1250.so"); + Init_windows_1251(); + rb_provide("enc/windows_1251.so"); + Init_windows_1252(); + rb_provide("enc/windows_1252.so"); + Init_windows_1253(); + rb_provide("enc/windows_1253.so"); + Init_windows_1254(); + rb_provide("enc/windows_1254.so"); + Init_windows_1257(); + rb_provide("enc/windows_1257.so"); + Init_windows_31j(); + rb_provide("enc/windows_31j.so"); + + Init_transdb(); + rb_provide("enc/trans/transdb.so"); + + //Init_trans_big5(); + //rb_provide("enc/trans/big5.so"); + Init_trans_big5(); + rb_provide("enc/trans/big5.so"); + + Init_trans_chinese(); + rb_provide("enc/trans/chinese.so"); + + Init_trans_ebcdic(); + rb_provide("enc/trans/ebcdic.so"); + + Init_trans_emoji(); + rb_provide("enc/trans/emoji.so"); + + Init_trans_emoji_iso2022_kddi(); + rb_provide("enc/trans/emoji_iso2022_kddi.so"); + + Init_trans_emoji_sjis_docomo(); + rb_provide("enc/trans/emoji_sjis_docomo.so"); + + Init_trans_emoji_sjis_kddi(); + rb_provide("enc/trans/emoji_sjis_kddi.so"); + + Init_trans_emoji_sjis_softbank(); + rb_provide("enc/trans/emoji_sjis_softbank.so"); + + Init_trans_escape(); + rb_provide("enc/trans/escape.o"); + + Init_trans_gb18030(); + rb_provide("enc/trans/gb18030.o"); + + Init_trans_gbk(); + rb_provide("enc/trans/gbk.o"); + + Init_trans_iso2022(); + rb_provide("enc/trans/iso2022.o"); + + Init_trans_japanese(); + rb_provide("enc/trans/japanese.o"); + + Init_trans_japanese_euc(); + rb_provide("enc/trans/japanese_euc.o"); + + Init_trans_japanese_sjis(); + rb_provide("enc/trans/japanese_sjis.o"); + + Init_trans_korean(); + rb_provide("enc/trans/korean.o"); + + Init_trans_single_byte(); + rb_provide("enc/trans/single_byte.o"); + + Init_trans_utf8_mac(); + rb_provide("enc/trans/utf8_mac.o"); + + Init_trans_utf_16_32(); + rb_provide("enc/trans/utf_16_32.o"); + + Init_bigdecimal(); + rb_provide("bigdecimal"); + rb_provide("bigdecimal.so"); + + Init_continuation(); + rb_provide("continuation"); + rb_provide("continuation.so"); + + Init_coverage(); + rb_provide("coverage"); + rb_provide("coverage.so"); + + Init_cparse(); + rb_provide("cparse"); + rb_provide("racc/cparse"); + rb_provide("cparse.so"); + rb_provide("racc/cparse.so"); + + Init_date_core(); + rb_provide("date_core"); + rb_provide("date_core.so"); + + Init_digest(); + rb_provide("digest"); + rb_provide("digest.so"); + + Init_escape(); + rb_provide("escape"); + rb_provide("escape.so"); + + Init_etc(); + rb_provide("etc"); + rb_provide("etc.so"); + + Init_fcntl(); + rb_provide("fcntl"); + rb_provide("fcntl.so"); + + Init_fiber(); + rb_provide("fiber"); + rb_provide("fiber.so"); + + Init_fiddle(); + rb_provide("fiddle"); + rb_provide("fiddle.so"); + + Init_md5(); + rb_provide("md5"); + rb_provide("digest/md5"); + rb_provide("md5.so"); + rb_provide("digest/md5.so"); + + Init_monitor(); + rb_provide("monitor"); + rb_provide("monitor.so"); + + Init_nkf(); + rb_provide("nkf"); + rb_provide("nkf.so"); + + Init_nonblock(); + rb_provide("nonblock"); + rb_provide("nonblock.so"); + + Init_objspace(); + rb_provide("objspace"); + rb_provide("objspace.so"); + + Init_generator(); + rb_provide("json/ext/generator"); + rb_provide("json/ext/generator.so"); + + Init_parser(); + rb_provide("json/ext/parser"); + rb_provide("json/ext/parser.so"); + + Init_pathname(); + rb_provide("pathname"); + rb_provide("pathname.so"); + + Init_psych(); + rb_provide("psych"); + rb_provide("psych.so"); + + Init_ripper(); + rb_provide("ripper"); + rb_provide("ripper.so"); + + Init_rmd160(); + rb_provide("rmd160"); + rb_provide("digest/rmd160"); + rb_provide("rmd160.so"); + rb_provide("digest/rmd160.so"); + + Init_sdbm(); + rb_provide("sdbm"); + rb_provide("sdbm.so"); + + Init_sha1(); + rb_provide("sha1"); + rb_provide("digest/sha1"); + rb_provide("sha1.so"); + rb_provide("digest/sha1.so"); + + Init_sha2(); + rb_provide("sha2"); + rb_provide("digest/sha2"); + rb_provide("sha2.so"); + rb_provide("digest/sha2.so"); + + Init_sizeof(); + rb_provide("sizeof"); + rb_provide("sizeof.so"); + + Init_socket(); + rb_provide("socket"); + rb_provide("socket.so"); + + Init_stringio(); + rb_provide("stringio"); + rb_provide("stringio.so"); + + Init_strscan(); + rb_provide("strscan"); + rb_provide("strscan.so"); + + //Init_wait(); + //rb_provide("wait"); + //rb_provide("io/wait"); + //rb_provide("wait.so"); + //rb_provide("io/wait.so"); + + //Init_zlib(); + //rb_provide("zlib"); + //rb_provide("zlib.so"); + + Init_openssl(); + rb_provide("openssl"); + rb_provide("openssl.so"); + + Init_nonblock(); + rb_provide("io/nonblock"); + rb_provide("io/nonblock.so"); + + rb_provide("mylib"); + rb_provide("mylib.so"); + Init_mylib(); + auto rubymodule = rb_define_module("Mylib"); + rb_define_module_function(rubymodule, "init_rest_of_openstudio", init_rest_of_mylib, 0); + + rb_enc_set_default_external(rb_enc_from_encoding(rb_utf8_encoding())); + } +} + +RubyEngine::~RubyEngine() { + ruby_finalize(); +} + ScriptObject RubyEngine::eval(std::string_view sv) { std::string str{sv}; return ScriptObject{evalString(str)}; @@ -82,3 +567,18 @@ void* RubyEngine::getAs_impl(ScriptObject& obj, const std::type_info &ti) { } } // namespace Test + +extern "C" +{ + int rb_hasFile(const char* t_filename) { + // TODO Consider expanding this to use the path which we have artificially defined in embedded_help.rb + std::string expandedName = std::string(":/ruby/2.7.0/") + std::string(t_filename) + ".rb"; + return embedded_files::hasFile(expandedName); + } + + int rb_require_embedded(const char* t_filename) { + std::string require_script = R"(require ')" + std::string(t_filename) + R"(')"; + evalString(require_script); + return 0; + } +} diff --git a/RubyEngine.hpp b/RubyEngine.hpp index 8eee703..ee73a07 100644 --- a/RubyEngine.hpp +++ b/RubyEngine.hpp @@ -3,6 +3,661 @@ #include "ScriptEngine.hpp" +#ifdef _MSC_VER +#ifdef EWOULDBLOCK +#pragma push_macro("EWOULDBLOCK") +#undef EWOULDBLOCK +#define EWOULDBLOCK_macro_found +#endif + + +#ifdef EINPROGRESS +#pragma push_macro("EINPROGRESS") +#undef EINPROGRESS +#define EINPROGRESS_macro_found +#endif + +#ifdef EALREADY +#pragma push_macro("EALREADY") +#undef EALREADY +#define EALREADY_macro_found +#endif + +#ifdef ENOTSOCK +#pragma push_macro("ENOTSOCK") +#undef ENOTSOCK +#define ENOTSOCK_macro_found +#endif + +#ifdef EDESTADDRREQ +#pragma push_macro("EDESTADDRREQ") +#undef EDESTADDRREQ +#define EDESTADDRREQ_macro_found +#endif + +#ifdef EMSGSIZE +#pragma push_macro("EMSGSIZE") +#undef EMSGSIZE +#define EMSGSIZE_macro_found +#endif + +#ifdef EPROTOTYPE +#pragma push_macro("EPROTOTYPE") +#undef EPROTOTYPE +#define EPROTOTYPE_macro_found +#endif + +#ifdef ENOPROTOOPT +#pragma push_macro("ENOPROTOOPT") +#undef ENOPROTOOPT +#define ENOPROTOOPT_macro_found +#endif + +#ifdef EPROTONOSUPPORT +#pragma push_macro("EPROTONOSUPPORT") +#undef EPROTONOSUPPORT +#define EPROTONOSUPPORT_macro_found +#endif + +#ifdef EOPNOTSUPP +#pragma push_macro("EOPNOTSUPP") +#undef EOPNOTSUPP +#define EOPNOTSUPP_macro_found +#endif + +#ifdef EAFNOSUPPORT +#pragma push_macro("EAFNOSUPPORT") +#undef EAFNOSUPPORT +#define EAFNOSUPPORT_macro_found +#endif + +#ifdef EADDRINUSE +#pragma push_macro("EADDRINUSE") +#undef EADDRINUSE +#define EADDRINUSE_macro_found +#endif + +#ifdef EADDRNOTAVAIL +#pragma push_macro("EADDRNOTAVAIL") +#undef EADDRNOTAVAIL +#define EADDRNOTAVAIL_macro_found +#endif + +#ifdef ENETDOWN +#pragma push_macro("ENETDOWN") +#undef ENETDOWN +#define ENETDOWN_macro_found +#endif + +#ifdef ENETUNREACH +#pragma push_macro("ENETUNREACH") +#undef ENETUNREACH +#define ENETUNREACH_macro_found +#endif + +#ifdef ENETRESET +#pragma push_macro("ENETRESET") +#undef ENETRESET +#define ENETRESET_macro_found +#endif + +#ifdef ECONNABORTED +#pragma push_macro("ECONNABORTED") +#undef ECONNABORTED +#define ECONNABORTED_macro_found +#endif + +#ifdef ECONNRESET +#pragma push_macro("ECONNRESET") +#undef ECONNRESET +#define ECONNRESET_macro_found +#endif + +#ifdef ENOBUFS +#pragma push_macro("ENOBUFS") +#undef ENOBUFS +#define ENOBUFS_macro_found +#endif + +#ifdef EISCONN +#pragma push_macro("EISCONN") +#undef EISCONN +#define EISCONN_macro_found +#endif + +#ifdef ENOTCONN +#pragma push_macro("ENOTCONN") +#undef ENOTCONN +#define ENOTCONN_macro_found +#endif + +#ifdef ETIMEDOUT +#pragma push_macro("ETIMEDOUT") +#undef ETIMEDOUT +#define ETIMEDOUT_macro_found +#endif + +#ifdef ECONNREFUSED +#pragma push_macro("ECONNREFUSED") +#undef ECONNREFUSED +#define ECONNREFUSED_macro_found +#endif + +#ifdef ELOOP +#pragma push_macro("ELOOP") +#undef ELOOP +#define ELOOP_macro_found +#endif + +#ifdef EHOSTUNREACH +#pragma push_macro("EHOSTUNREACH") +#undef EHOSTUNREACH +#define EHOSTUNREACH_macro_found +#endif + +#ifdef try +#pragma push_macro("try") +#undef try +#define try_macro_found +#endif + +#ifdef except +#pragma push_macro("except") +#undef except +#define except_macro_found +#endif + +#ifdef finally +#pragma push_macro("finally") +#undef finally +#define finally_macro_found +#endif + +#ifdef leave +#pragma push_macro("leave") +#undef leave +#define leave_macro_found +#endif + +#ifdef OpenFile +#pragma push_macro("OpenFile") +#undef OpenFile +#define OpenFile_macro_found +#endif + +#ifdef CharNext +#pragma push_macro("CharNext") +#undef CharNext +#define CharNext_macro_found +#endif + + +#ifdef rb_w32_iswinnt +#pragma push_macro("rb_w32_iswinnt") +#undef rb_w +#define rb_w_macro_found32_iswinnt +#endif + +#ifdef rb_w32_iswin95 +#pragma push_macro("rb_w32_iswin95") +#undef rb_w +#define rb_w_macro_found32_iswin95 +#endif + +#ifdef getc +#pragma push_macro("getc") +#undef getc +#define getc_macro_found +#endif + +#ifdef putc +#pragma push_macro("putc") +#undef putc +#define putc_macro_found +#endif + +#ifdef fgetc +#pragma push_macro("fgetc") +#undef fgetc +#define fgetc_macro_found +#endif + +#ifdef fputc +#pragma push_macro("fputc") +#undef fputc +#define fputc_macro_found +#endif + +#ifdef getchar +#pragma push_macro("getchar") +#undef getchar +#define getchar_macro_found +#endif + +#ifdef putchar +#pragma push_macro("putchar") +#undef putchar +#define putchar_macro_found +#endif + +#ifdef fgetchar +#pragma push_macro("fgetchar") +#undef fgetchar +#define fgetchar_macro_found +#endif + +#ifdef fputchar +#pragma push_macro("fputchar") +#undef fputchar +#define fputchar_macro_found +#endif + +#ifdef utime +#pragma push_macro("utime") +#undef utime +#define utime_macro_found +#endif + +#ifdef strcasecmp +#pragma push_macro("strcasecmp") +#undef strcasecmp +#define strcasecmp_macro_found +#endif + +#ifdef strncasecmp +#pragma push_macro("strncasecmp") +#undef strncasecmp +#define strncasecmp_macro_found +#endif + +#ifdef close +#pragma push_macro("close") +#undef close +#define close_macro_found +#endif + +#ifdef fclose +#pragma push_macro("fclose") +#undef fclose +#define fclose_macro_found +#endif + +#ifdef read +#pragma push_macro("read") +#undef read +#define read_macro_found +#endif + +#ifdef write +#pragma push_macro("write") +#undef write +#define write_macro_found +#endif + +#ifdef getpid +#pragma push_macro("getpid") +#undef getpid +#define getpid_macro_found +#endif + +#ifdef sleep +#pragma push_macro("sleep") +#undef sleep +#define sleep_macro_found +#endif + +#ifdef creat +#pragma push_macro("creat") +#undef creat +#define creat_macro_found +#endif + +#ifdef eof +#pragma push_macro("eof") +#undef eof +#define eof_macro_found +#endif + +#ifdef filelength +#pragma push_macro("filelength") +#undef filelength +#define filelength_macro_found +#endif + +#ifdef mktemp +#pragma push_macro("mktemp") +#undef mktemp +#define mktemp_macro_found +#endif + +#ifdef tell +#pragma push_macro("tell") +#undef tell +#define tell_macro_found +#endif + +#ifdef unlink +#pragma push_macro("unlink") +#undef unlink +#define unlink_macro_found +#endif + +#ifdef _open +#pragma push_macro("_open") +#undef _open +#define _open_macro_found +#endif + +#ifdef sopen +#pragma push_macro("sopen") +#undef sopen +#define sopen_macro_found +#endif + +#ifdef fopen +#pragma push_macro("fopen") +#undef fopen +#define fopen_macro_found +#endif + +#ifdef fdopen +#pragma push_macro("fdopen") +#undef fdopen +#define fdopen_macro_found +#endif + +#ifdef fsopen +#pragma push_macro("fsopen") +#undef fsopen +#define fsopen_macro_found +#endif + +#ifdef fsync +#pragma push_macro("fsync") +#undef fsync +#define fsync_macro_found +#endif + +#ifdef stat +#pragma push_macro("stat") +#undef stat +#define stat_macro_found +#endif + +#ifdef execv +#pragma push_macro("execv") +#undef execv +#define execv_macro_found +#endif + +#ifdef isatty +#pragma push_macro("isatty") +#undef isatty +#define isatty_macro_found +#endif + +#ifdef mkdir +#pragma push_macro("mkdir") +#undef mkdir +#define mkdir_macro_found +#endif + +#ifdef rmdir +#pragma push_macro("rmdir") +#undef rmdir +#define rmdir_macro_found +#endif + +#ifdef isascii +#pragma push_macro("isascii") +#undef isascii +#define isascii_macro_found +#endif + +#ifdef vsnprintf +#pragma push_macro("vsnprintf") +#undef vsnprintf +#define vsnprintf_macro_found +#endif + +#ifdef snprintf +#pragma push_macro("snprintf") +#undef snprintf +#define snprintf_macro_found +#endif + +#ifdef isnan +#pragma push_macro("isnan") +#undef isnan +#define isnan_macro_found +#endif + +#ifdef finite +#pragma push_macro("finite") +#undef finite +#define finite_macro_found +#endif + +#ifdef copysign +#pragma push_macro("copysign") +#undef copysign +#define copysign_macro_found +#endif + +#ifdef scalb +#pragma push_macro("scalb") +#undef scalb +#define scalb_macro_found +#endif + +#ifdef accept +#pragma push_macro("accept") +#undef accept +#define accept_macro_found +#endif + +#ifdef bind +#pragma push_macro("bind") +#undef bind +#define bind_macro_found +#endif + +#ifdef connect +#pragma push_macro("connect") +#undef connect +#define connect_macro_found +#endif + +#ifdef FD_SET +#pragma push_macro("FD_SET") +#undef FD_SET +#define FD_SET_macro_found +#endif + +#ifdef FD_CLR +#pragma push_macro("FD_CLR") +#undef FD_CLR +#define FD_CLR_macro_found +#endif + +#ifdef FD_ISSET +#pragma push_macro("FD_ISSET") +#undef FD_ISSET +#define FD_ISSET_macro_found +#endif + +#ifdef select +#pragma push_macro("select") +#undef select +#define select_macro_found +#endif + +#ifdef getpeername +#pragma push_macro("getpeername") +#undef getpeername +#define getpeername_macro_found +#endif + +#ifdef getsockname +#pragma push_macro("getsockname") +#undef getsockname +#define getsockname_macro_found +#endif + +#ifdef getsockopt +#pragma push_macro("getsockopt") +#undef getsockopt +#define getsockopt_macro_found +#endif + +#ifdef ioctlsocket +#pragma push_macro("ioctlsocket") +#undef ioctlsocket +#define ioctlsocket_macro_found +#endif + +#ifdef listen +#pragma push_macro("listen") +#undef listen +#define listen_macro_found +#endif + +#ifdef recv +#pragma push_macro("recv") +#undef recv +#define recv_macro_found +#endif + +#ifdef recvfrom +#pragma push_macro("recvfrom") +#undef recvfrom +#define recvfrom_macro_found +#endif + +#ifdef send +#pragma push_macro("send") +#undef send +#define send_macro_found +#endif + +#ifdef sendto +#pragma push_macro("sendto") +#undef sendto +#define sendto_macro_found +#endif + +#ifdef setsockopt +#pragma push_macro("setsockopt") +#undef setsockopt +#define setsockopt_macro_found +#endif + +#ifdef shutdown +#pragma push_macro("shutdown") +#undef shutdown +#define shutdown_macro_found +#endif + +#ifdef socket +#pragma push_macro("socket") +#undef socket +#define socket_macro_found +#endif + +#ifdef gethostbyaddr +#pragma push_macro("gethostbyaddr") +#undef gethostbyaddr +#define gethostbyaddr_macro_found +#endif + +#ifdef gethostbyname +#pragma push_macro("gethostbyname") +#undef gethostbyname +#define gethostbyname_macro_found +#endif + +#ifdef gethostname +#pragma push_macro("gethostname") +#undef gethostname +#define gethostname_macro_found +#endif + +#ifdef getprotobyname +#pragma push_macro("getprotobyname") +#undef getprotobyname +#define getprotobyname_macro_found +#endif + +#ifdef getprotobynumber +#pragma push_macro("getprotobynumber") +#undef getprotobynumber +#define getprotobynumber_macro_found +#endif + +#ifdef getservbyname +#pragma push_macro("getservbyname") +#undef getservbyname +#define getservbyname_macro_found +#endif + +#ifdef getservbyport +#pragma push_macro("getservbyport") +#undef getservbyport +#define getservbyport_macro_found +#endif + +#ifdef get_osfhandle +#define OLD_get_osfhandle get_osfhandle +#undef get_osfhandle +#define get_osfhandle_macro_found +#endif + +#ifdef getcwd +#pragma push_macro("getcwd") +#undef getcwd +#define getcwd_macro_found +#endif + +#ifdef getenv +#pragma push_macro("getenv") +#undef getenv +#define getenv_macro_found +#endif + +#ifdef rename +#pragma push_macro("rename") +#undef rename +#define rename_macro_found +#endif + +#ifdef times +#pragma push_macro("times") +#undef times +#define times_macro_found +#endif + +#ifdef Sleep +#pragma push_macro("Sleep") +#undef Sleep +#define Sleep_macro_found +#endif +#endif + +#ifdef __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wregister" +# pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +#include +#include +#include + +#ifdef __GNUC__ +# pragma GCC diagnostic pop +#endif + + namespace Test { class RubyEngine final : public ScriptEngine { @@ -25,4 +680,544 @@ class RubyEngine final : public ScriptEngine }; } // namespace Test + + + +#ifdef _MSC_VER + + +#ifdef EWOULDBLOCK_macro_found +#pragma pop_macro("EWOULDBLOCK") +#undef EWOULDBLOCK_macro_found +#endif + +#ifdef EINPROGRESS_macro_found +#pragma pop_macro("EINPROGRESS") +#undef EINPROGRESS_macro_found +#endif + +#ifdef EALREADY_macro_found +#pragma pop_macro("EALREADY") +#undef EALREADY_macro_found +#endif + +#ifdef ENOTSOCK_macro_found +#pragma pop_macro("ENOTSOCK") +#undef ENOTSOCK_macro_found +#endif + +#ifdef EDESTADDRREQ_macro_found +#pragma pop_macro("EDESTADDRREQ") +#undef EDESTADDRREQ_macro_found +#endif + +#ifdef EMSGSIZE_macro_found +#pragma pop_macro("EMSGSIZE") +#undef EMSGSIZE_macro_found +#endif + +#ifdef EPROTOTYPE_macro_found +#pragma pop_macro("EPROTOTYPE") +#undef EPROTOTYPE_macro_found +#endif + +#ifdef ENOPROTOOPT_macro_found +#pragma pop_macro("ENOPROTOOPT") +#undef ENOPROTOOPT_macro_found +#endif + +#ifdef EPROTONOSUPPORT_macro_found +#pragma pop_macro("EPROTONOSUPPORT") +#undef EPROTONOSUPPORT_macro_found +#endif + +#ifdef EOPNOTSUPP_macro_found +#pragma pop_macro("EOPNOTSUPP") +#undef EOPNOTSUPP_macro_found +#endif + +#ifdef EAFNOSUPPORT_macro_found +#pragma pop_macro("EAFNOSUPPORT") +#undef EAFNOSUPPORT_macro_found +#endif + +#ifdef EADDRINUSE_macro_found +#pragma pop_macro("EADDRINUSE") +#undef EADDRINUSE_macro_found +#endif + +#ifdef EADDRNOTAVAIL_macro_found +#pragma pop_macro("EADDRNOTAVAIL") +#undef EADDRNOTAVAIL_macro_found +#endif + +#ifdef ENETDOWN_macro_found +#pragma pop_macro("ENETDOWN") +#undef ENETDOWN_macro_found +#endif + +#ifdef ENETUNREACH_macro_found +#pragma pop_macro("ENETUNREACH") +#undef ENETUNREACH_macro_found +#endif + +#ifdef ENETRESET_macro_found +#pragma pop_macro("ENETRESET") +#undef ENETRESET_macro_found +#endif + +#ifdef ECONNABORTED_macro_found +#pragma pop_macro("ECONNABORTED") +#undef ECONNABORTED_macro_found +#endif + +#ifdef ECONNRESET_macro_found +#pragma pop_macro("ECONNRESET") +#undef ECONNRESET_macro_found +#endif + +#ifdef ENOBUFS_macro_found +#pragma pop_macro("ENOBUFS") +#undef ENOBUFS_macro_found +#endif + +#ifdef EISCONN_macro_found +#pragma pop_macro("EISCONN") +#undef EISCONN_macro_found +#endif + +#ifdef ENOTCONN_macro_found +#pragma pop_macro("ENOTCONN") +#undef ENOTCONN_macro_found +#endif + +#ifdef ETIMEDOUT_macro_found +#pragma pop_macro("ETIMEDOUT") +#undef ETIMEDOUT_macro_found +#endif + +#ifdef ECONNREFUSED_macro_found +#pragma pop_macro("ECONNREFUSED") +#undef ECONNREFUSED_macro_found +#endif + +#ifdef ELOOP_macro_found +#pragma pop_macro("ELOOP") +#undef ELOOP_macro_found +#endif + +#ifdef EHOSTUNREACH_macro_found +#pragma pop_macro("EHOSTUNREACH") +#undef EHOSTUNREACH_macro_found +#endif + +#ifdef try_macro_found +#pragma pop_macro("try") +#undef try_macro_found +#endif + +#ifdef except_macro_found +#pragma pop_macro("except") +#undef except_macro_found +#endif + +#ifdef finally_macro_found +#pragma pop_macro("finally") +#undef finally_macro_found +#endif + +#ifdef leave_macro_found +#pragma pop_macro("leave") +#undef leave_macro_found +#endif + +#ifdef OpenFile_macro_found +#pragma pop_macro("OpenFile") +#undef OpenFile_macro_found +#endif + +#ifdef CharNext_macro_found +#pragma pop_macro("CharNext") +#undef CharNext_macro_found +#endif + + +#ifdef rb_w_macro_found32_iswinnt +#pragma pop_macro("rb_w32_iswinnt") +#undef rb_w_macro_found32_iswinnt +#endif + +#ifdef rb_w_macro_found32_iswin95 +#pragma pop_macro("rb_w32_iswin95") +#undef rb_w_macro_found32_iswin95 +#endif + +#ifdef getc_macro_found +#pragma pop_macro("getc") +#undef getc_macro_found +#endif + +#ifdef putc_macro_found +#pragma pop_macro("putc") +#undef putc_macro_found +#endif + +#ifdef fgetc_macro_found +#pragma pop_macro("fgetc") +#undef fgetc_macro_found +#endif + +#ifdef fputc_macro_found +#pragma pop_macro("fputc") +#undef fputc_macro_found +#endif + +#ifdef getchar_macro_found +#pragma pop_macro("getchar") +#undef getchar_macro_found +#endif + +#ifdef putchar_macro_found +#pragma pop_macro("putchar") +#undef putchar_macro_found +#endif + +#ifdef fgetchar_macro_found +#pragma pop_macro("fgetchar") +#undef fgetchar_macro_found +#endif + +#ifdef fputchar_macro_found +#pragma pop_macro("fputchar") +#undef fputchar_macro_found +#endif + +#ifdef utime_macro_found +#pragma pop_macro("utime") +#undef utime_macro_found +#endif + +#ifdef strcasecmp_macro_found +#pragma pop_macro("strcasecmp") +#undef strcasecmp_macro_found +#endif + +#ifdef strncasecmp_macro_found +#pragma pop_macro("strncasecmp") +#undef strncasecmp_macro_found +#endif + +#ifdef close_macro_found +#pragma pop_macro("close") +#undef close_macro_found +#endif + +#ifdef fclose_macro_found +#pragma pop_macro("fclose") +#undef fclose_macro_found +#endif + +#ifdef read_macro_found +#pragma pop_macro("read") +#undef read_macro_found +#endif + +#ifdef write_macro_found +#pragma pop_macro("write") +#undef write_macro_found +#endif + +#ifdef getpid_macro_found +#pragma pop_macro("getpid") +#undef getpid_macro_found +#endif + +#ifdef sleep_macro_found +#pragma pop_macro("sleep") +#undef sleep +#undef sleep_macro_found +#endif + +#ifdef creat_macro_found +#pragma pop_macro("creat") +#undef creat_macro_found +#endif + +#ifdef eof_macro_found +#pragma pop_macro("eof") +#undef eof_macro_found +#endif + +#ifdef filelength_macro_found +#pragma pop_macro("filelength") +#undef filelength_macro_found +#endif + +#ifdef mktemp_macro_found +#pragma pop_macro("mktemp") +#undef mktemp_macro_found +#endif + +#ifdef tell_macro_found +#pragma pop_macro("tell") +#undef tell_macro_found +#endif + +#ifdef unlink_macro_found +#pragma pop_macro("unlink") +#undef unlink_macro_found +#endif + +#ifdef _open_macro_found +#pragma pop_macro("_open") +#undef _open_macro_found +#endif + +#ifdef sopen_macro_found +#pragma pop_macro("sopen") +#undef sopen_macro_found +#endif + +#ifdef fopen_macro_found +#pragma pop_macro("fopen") +#undef fopen_macro_found +#endif + +#ifdef fdopen_macro_found +#pragma pop_macro("fdopen") +#undef fdopen_macro_found +#endif + +#ifdef fsopen_macro_found +#pragma pop_macro("fsopen") +#undef fsopen_macro_found +#endif + +#ifdef fsync_macro_found +#pragma pop_macro("fsync") +#undef fsync_macro_found +#endif + +#ifdef stat_macro_found +#pragma pop_macro("stat") +#undef stat_macro_found +#endif + +#ifdef execv_macro_found +#pragma pop_macro("execv") +#undef execv_macro_found +#endif + +#ifdef isatty_macro_found +#pragma pop_macro("isatty") +#undef isatty_macro_found +#endif + +#ifdef mkdir_macro_found +#pragma pop_macro("mkdir") +#undef mkdir_macro_found +#endif + +#ifdef rmdir_macro_found +#pragma pop_macro("rmdir") +#undef rmdir_macro_found +#endif + +#ifdef isascii_macro_found +#pragma pop_macro("isascii") +#undef isascii_macro_found +#endif + +#ifdef vsnprintf_macro_found +#pragma pop_macro("vsnprintf") +#undef vsnprintf_macro_found +#endif + +#ifdef snprintf_macro_found +#pragma pop_macro("snprintf") +#undef snprintf_macro_found +#endif + +#ifdef isnan_macro_found +#pragma pop_macro("isnan") +#undef isnan_macro_found +#endif + +#ifdef finite_macro_found +#pragma pop_macro("finite") +#undef finite_macro_found +#endif + +#ifdef copysign_macro_found +#pragma pop_macro("copysign") +#undef copysign_macro_found +#endif + +#ifdef scalb_macro_found +#pragma pop_macro("scalb") +#undef scalb_macro_found +#endif + +#ifdef accept_macro_found +#pragma pop_macro("accept") +#undef accept_macro_found +#endif + +#ifdef bind_macro_found +#pragma pop_macro("bind") +#undef bind_macro_found +#endif + +#ifdef connect_macro_found +#pragma pop_macro("connect") +#undef connect_macro_found +#endif + +#ifdef FD_SET_macro_found +#pragma pop_macro("FD_SET") +#undef FD_SET_macro_found +#endif + +#ifdef FD_CLR_macro_found +#pragma pop_macro("FD_CLR") +#undef FD_CLR_macro_found +#endif + +#ifdef FD_ISSET_macro_found +#pragma pop_macro("FD_ISSET") +#undef FD_ISSET_macro_found +#endif + +#ifdef select_macro_found +#pragma pop_macro("select") +#undef select_macro_found +#endif + +#ifdef getpeername_macro_found +#pragma pop_macro("getpeername") +#undef getpeername_macro_found +#endif + +#ifdef getsockname_macro_found +#pragma pop_macro("getsockname") +#undef getsockname_macro_found +#endif + +#ifdef getsockopt_macro_found +#pragma pop_macro("getsockopt") +#undef getsockopt_macro_found +#endif + +#ifdef ioctlsocket_macro_found +#pragma pop_macro("ioctlsocket") +#undef ioctlsocket_macro_found +#endif + +#ifdef listen_macro_found +#pragma pop_macro("listen") +#undef listen_macro_found +#endif + +#ifdef recv_macro_found +#pragma pop_macro("recv") +#undef recv_macro_found +#endif + +#ifdef recvfrom_macro_found +#pragma pop_macro("recvfrom") +#undef recvfrom_macro_found +#endif + +#ifdef send_macro_found +#pragma pop_macro("send") +#undef send_macro_found +#endif + +#ifdef sendto_macro_found +#pragma pop_macro("sendto") +#undef sendto_macro_found +#endif + +#ifdef setsockopt_macro_found +#pragma pop_macro("setsockopt") +#undef setsockopt_macro_found +#endif + +#ifdef shutdown_macro_found +#pragma pop_macro("shutdown") +#undef shutdown_macro_found +#endif + +#ifdef socket_macro_found +#pragma pop_macro("socket") +#undef socket_macro_found +#endif + +#ifdef gethostbyaddr_macro_found +#pragma pop_macro("gethostbyaddr") +#undef gethostbyaddr_macro_found +#endif + +#ifdef gethostbyname_macro_found +#pragma pop_macro("gethostbyname") +#undef gethostbyname_macro_found +#endif + +#ifdef gethostname_macro_found +#pragma pop_macro("gethostname") +#undef gethostname_macro_found +#endif + +#ifdef getprotobyname_macro_found +#pragma pop_macro("getprotobyname") +#undef getprotobyname_macro_found +#endif + +#ifdef getprotobynumber_macro_found +#pragma pop_macro("getprotobynumber") +#undef getprotobynumber_macro_found +#endif + +#ifdef getservbyname_macro_found +#pragma pop_macro("getservbyname") +#undef getservbyname_macro_found +#endif + +#ifdef getservbyport_macro_found +#pragma pop_macro("getservbyport") +#undef getservbyport_macro_found +#endif + +#ifdef get_osfhandle_macro_found +#pragma pop_macro("get_osfhandle") +#undef get_osfhandle_macro_found +#endif + +#ifdef getcwd_macro_found +#pragma pop_macro("getcwd") +#undef getcwd_macro_found +#endif + +#ifdef getenv_macro_found +#pragma pop_macro("getenv") +#undef getenv_macro_found +#endif + +#ifdef rename_macro_found +#pragma pop_macro("rename") +#undef rename_macro_found +#endif + +#ifdef times_macro_found +#pragma pop_macro("times") +#undef times_macro_found +#endif + +#ifdef Sleep_macro_found +#pragma pop_macro("Sleep") +#undef Sleep_macro_found +#endif +#endif + + #endif diff --git a/embedded/EmbedFiles.cmake b/embedded/EmbedFiles.cmake index bb47490..a86c5dc 100644 --- a/embedded/EmbedFiles.cmake +++ b/embedded/EmbedFiles.cmake @@ -47,7 +47,7 @@ function(embed_files FILES EMBEDDED_LOCATIONS CXX_OUTPUT_FILES) set( EMBEDDED_LOCATION ":/${EMBEDDED_LOCATION}" ) endif() - message("CreateEmbeddedSource ${FILE} ${EMBED_SOURCE_FILE} ${i} ${EMBEDDED_LOCATION}") + # message("CreateEmbeddedSource ${FILE} ${EMBED_SOURCE_FILE} ${i} ${EMBEDDED_LOCATION}") add_custom_command(OUTPUT ${EMBED_SOURCE_FILE} COMMAND ${CMAKE_COMMAND} -E make_directory ${EMBED_SOURCE_PATH} diff --git a/embedded_help.rb b/embedded_help.rb new file mode 100644 index 0000000..2dacc6f --- /dev/null +++ b/embedded_help.rb @@ -0,0 +1,792 @@ +module EmbeddedScripting + @@fileNames = EmbeddedScripting::allFileNamesAsString.split(';') + + def self.fileNames + @@fileNames + end +end + + +module Mylib + + # check if this is CLI + CLI = true + + def self.mylib_path + RbConfig.ruby + end + + def self.get_absolute_path(p) + absolute_path = "" + if p.to_s.chars.first == ':' then + p2 = String.new(p.to_s) + p2[0] = '' + absolute_path = File.expand_path p2 + + # strip Windows drive letters + if /^[A-Za-z]\:/.match(absolute_path.strip) + absolute_path = absolute_path[2..-1] + end + absolute_path = ':' + absolute_path + else + absolute_path = File.expand_path p2 + end + return absolute_path + end + + def self.preprocess_ruby_script(s) +#puts "self.preprocess_ruby_script before: #{s}" + # DLM: temporary hack to get around autoload + s.gsub!(/^\s*autoload\s*(:.*?),\s*(.*?)\s*$/, "current_module_ = Module.nesting[0].nil? ? Object : Module.nesting[0] + $autoload_hash[current_module_.to_s.to_sym] = {} if $autoload_hash[current_module_.to_s.to_sym].nil? + $autoload_hash[current_module_.to_s.to_sym][\\1] = [current_module_, \\2]\n") +#puts "self.preprocess_ruby_script after: #{s}" + return s + end +end + +BINDING = Kernel::binding() +#puts Encoding.list + +# DLM: ignore for now +#Encoding.default_external = Encoding::ASCII + +module Kernel + # ":" is our root path to the embedded file system + # make sure it is in the ruby load path + if ENV['RUBYLIB'] + ENV['RUBYLIB'].split(File::PATH_SEPARATOR).each {|lib| $LOAD_PATH.unshift(lib)} + end + $LOAD_PATH << ':' + $LOAD_PATH << ':/ruby/2.7.0' + $LOAD_PATH << ':/ruby/2.7.0/x86_64-darwin16' + $LOAD_PATH << ':/ruby/2.7.0/x86_64-darwin17' + $LOAD_PATH << ':/ruby/2.7.0/x86_64-darwin18' + $LOAD_PATH << ':/ruby/2.7.0/x64-mswin64_140' + # DLM: now done in embedded gem initialization section in mylib_cli.rb + #$LOAD_PATH << EmbeddedScripting::findFirstFileByName('mylib-standards.rb').gsub('/mylib-standards.rb', '') + #$LOAD_PATH << EmbeddedScripting::findFirstFileByName('mylib-workflow.rb').gsub('/mylib-workflow.rb', '') + $LOADED = [] + + alias :original_require_relative :require_relative + alias :original_require :require + alias :original_open :open + + RUBY_FILE_EXTS = ['.rb', '.so', '.dll'] + # Consider moving this map into mylib-gems project + # and maintain this list from there + EMBEDDED_EXT_INITS = {\ + 'libll' => 'init_libll',\ + 'liboga' => 'init_liboga',\ + 'sqlite3/sqlite3_native' => 'init_sqlite3_native',\ + 'jaro_winkler_ext' => 'init_jaro_winkler_ext',\ + 'pycall.so' => 'init_pycall',\ + 'pycall.dll' => 'init_pycall' + } + + def require_embedded_extension path + if EMBEDDED_EXT_INITS.has_key? path + $LOADED << path + EmbeddedScripting.send(EMBEDDED_EXT_INITS[path]) + return true + else + return false + end + end + + def require path + result = false + original_directory = Dir.pwd + path_with_extension = path + + begin + # Returns the extension + # (the portion of file name in path starting from the last period). + extname = File.extname(path) + + if extname.empty? or ! RUBY_FILE_EXTS.include? extname + path_with_extension = path + '.rb' + end + + if path.include? 'mylib/energyplus/find_energyplus' + return false + elsif path_with_extension.to_s.chars.first == ':' + # Give absolute embedded paths first priority + if $LOADED.include?(path_with_extension) + return true + else + return require_embedded_absolute(path_with_extension) + end + elsif path_with_extension == 'mylib.rb' + # Mylib is loaded by default and does not need to be required + return true + elsif require_embedded(path_with_extension, $LOAD_PATH) + # Load embedded files that are no required using full paths now + # This does not included the mylib-gems set of default, baked in gems + # because we want to give anything provided by --bundle the first chance + return true + else + # This will pick up files from the normal filesystem, + # including things in the --bundle + result = original_require path + end + rescue Exception => e + # This picks up the embedded gems + # Important to do this now, so that --bundle has first chance + # using rescue in normal program flow, might have poor performance + # (it does in C++) but this is perhaps the only way + # $EMBEDDED_GEM_PATH is populated in mylib_cli.rb during startup + result = require_embedded(path_with_extension, $EMBEDDED_GEM_PATH) + + # Finally try to load any supported native extensions + if not result + result = require_embedded_extension path + end + + # Give up. original_require will throw and so + # this will preserve that behavior + if not result + raise e + end + ensure + current_directory = Dir.pwd + if original_directory != current_directory + Dir.chdir(original_directory) + STDOUT.flush + end + end + + return result + end + + def require_embedded(path, search_paths) + search_paths = [] if not search_paths + search_paths.each do |p| + if p.to_s.chars.first == ':' + embedded_path = p + '/' + path + if $LOADED.include?(embedded_path) + return true + elsif EmbeddedScripting::hasFile(embedded_path) + require_embedded_absolute(embedded_path) + return true + end + end + end + return false + end + + def require_embedded_absolute path + original_directory = Dir.pwd + + $LOADED << path + s = EmbeddedScripting::getFileAsString(path) + s = Mylib::preprocess_ruby_script(s) + + result = Kernel::eval(s,BINDING,path) + + + current_directory = Dir.pwd + if original_directory != current_directory + Dir.chdir(original_directory) + STDOUT.flush + end + + return result + end + + def require_relative path + absolute_path = File.dirname(caller_locations(1,1)[0].path) + '/' + path + if absolute_path.to_s.chars.first == ':' + absolute_path = Mylib.get_absolute_path(absolute_path) + end + return require absolute_path + end + + # this function either reads a file from the embedded archive or from disk, returns file content as a string + def load_resource_relative(path, mode='r') + + absolute_path = File.dirname(caller_locations(1,1)[0].path) + '/' + path + if absolute_path.to_s.chars.first == ':' + absolute_path = Mylib.get_absolute_path(absolute_path) + end + + if EmbeddedScripting::hasFile(absolute_path) + return EmbeddedScripting::getFileAsString(absolute_path) + end + + result = "" + if File.exists?(absolute_path) + File.open(absolute_path, mode) do |file| + result = file.read + end + elsif File.exists?(path) + File.open(path, mode) do |file| + result = file.read + end + end + return result + end + + # Finds files relative to a given location whose names + # (not including file extension) match the regex + # and whose extension matches the specified extension. + # Recursively includes files in subdirectories below the given location. + # + # @param path [String] the directory to search relative to the current file. + # Use ./ for the current directory + # @param file_name_regexp [Regexp] the Ruby regular expression to match the file name + # (including the file extension). + # @return [Array] an array of absolute file paths matching the specified string + def embedded_files_relative(path, file_name_regexp) + + # Resolve the absolute path to the caller location + absolute_path = File.dirname(caller_locations(1,1)[0].path) + '/' + path + if absolute_path.chars.first == ':' + absolute_path[0] = '' + absolute_path = File.expand_path absolute_path + + # strip Windows drive letters + if /[A-Z]\:/.match(absolute_path) + absolute_path = absolute_path[2..-1] + end + absolute_path = ':' + absolute_path + end + + # Loop through all the files in the embedded system + matches = [] + EmbeddedScripting::fileNames.each do |file| + # Skip files outside of the specified directory + next unless file.start_with?(absolute_path) + # Skip files that don't match the file_name_pattern criterion + next unless File.basename(file).match(file_name_regexp) + # If here, found a match + matches << file + end + + return matches + end + + def open(name, *args) + #puts "I'm in Kernel.open!" + #STDOUT.flush + if name.to_s.chars.first == ':' then + #puts "self.open(name, *args), name = #{name}, args = #{args}" + absolute_path = Mylib.get_absolute_path(name) + #puts "absolute_path = #{absolute_path}" + if EmbeddedScripting::hasFile(absolute_path) then + string = EmbeddedScripting::getFileAsString(absolute_path) + #puts "string = #{string}" + if block_given? + # if a block is given, then a new IO is created and closed + io = StringIO.open(string) + begin + result = yield(io) + ensure + io.close + end + return result + else + return StringIO.open(string) + end + else + #puts "IO.open cannot find embedded file '#{absolute_path}' for '#{name}'" + if block_given? + # if a block is given, then a new IO is created and closed + io = StringIO.open("") + begin + result = yield(io) + ensure + io.close + end + return result + else + return "" + end + end + end + + if block_given? + # if a block is given, then a new IO is created and closed + io = original_open(name, *args) + begin + result = yield(io) + ensure + io.close + end + return result + else + return original_open(name, *args) + end + end + +end + +$autoload_hash = {} + +class Module + + alias :original_const_missing :const_missing + + def const_missing(m) + if caller_locations(1,1)[0].path.to_s.chars.first == ':' + sym = self.to_s.to_sym + + lookup = $autoload_hash[sym] + if lookup.nil? + # this seems to occur if a class and a module have the same name? + if md = /\#\/.match(self.to_s) + sym = md[1].to_sym + lookup = $autoload_hash[sym] + end + end + + if lookup.nil? + # check parent modules + while md = /(.*)\:\:.*?/.match(sym.to_s) + sym = md[1].to_sym + lookup = $autoload_hash[sym] + break if lookup + end + end + + return nil if lookup.nil? + + return nil if lookup[m].nil? + mod = lookup[m][0] + path = lookup[m][1] + + require path + + result = mod.const_get(m) + + return result + end + + original_const_missing(m) + end +end + +class IO + class << self + alias :original_read :read + alias :original_open :open + end + + def self.read(name, *args) + if name.to_s.chars.first == ':' then + #puts "self.read(name, *args), name = #{name}, args = #{args}" + #STDOUT.flush + absolute_path = Mylib.get_absolute_path(name) + #puts "absolute_path = #{absolute_path}" + #STDOUT.flush + if EmbeddedScripting::hasFile(absolute_path) then + return EmbeddedScripting::getFileAsString(absolute_path) + else + puts "IO.read cannot find embedded file '#{absolute_path}' for '#{name}'" + return "" + end + end + + #puts "self.original_read, name = #{name}, args = #{args}, block_given? = #{block_given?}" + #STDOUT.flush + + # ruby2.7+ now issues warning: "Using the last argument as keyword parameters is deprecated" + # The optional args for IO.read should be a hash. This does simple conversion before sending to IO.read + args = Hash[*args] + + return original_read(name, **args) + end + + def self.open(name, *args) + + if name.to_s.chars.first == ':' then + #puts "self.open(name, *args), name = #{name}, args = #{args}" + absolute_path = Mylib.get_absolute_path(name) + #puts "absolute_path = #{absolute_path}" + if EmbeddedScripting::hasFile(absolute_path) then + string = EmbeddedScripting::getFileAsString(absolute_path) + #puts "string = #{string}" + if block_given? + # if a block is given, then a new IO is created and closed + io = StringIO.open(string) + begin + result = yield(io) + ensure + io.close + end + return result + else + return StringIO.open(string) + end + else + puts "IO.open cannot find embedded file '#{absolute_path}' for '#{name}'" + if block_given? + # if a block is given, then a new IO is created and closed + io = StringIO.open("") + begin + result = yield(io) + ensure + io.close + end + return result + else + return "" + end + end + end + + if block_given? + # if a block is given, then a new IO is created and closed + io = self.original_open(name, *args) + begin + result = yield(io) + ensure + io.close + end + return result + else + return self.original_open(name, *args) + end + end +end + +class File + class << self + alias :original_expand_path :expand_path + alias :original_realpath :realpath + alias :original_absolute_path :absolute_path + alias :original_directory? :directory? + alias :original_file? :file? + end + + def self.expand_path(file_name, *args) + if file_name.to_s.chars.first == ':' then + #puts "self.expand_path(file_name, *args), file_name = #{file_name}, args = #{args}" + #STDOUT.flush + return Mylib.get_absolute_path(file_name) + elsif args.size == 1 && args[0].to_s.chars.first == ':' then + #puts "2 self.expand_path(file_name, *args), file_name = #{file_name}, args = #{args}" + #puts "x = #{File.join(args[0], file_name)}" + #puts "y = #{Mylib.get_absolute_path(File.join(args[0], file_name))}" + #STDOUT.flush + #return original_expand_path(file_name, *args) + return Mylib.get_absolute_path(File.join(args[0], file_name)) + end + return original_expand_path(file_name, *args) + end + + def self.absolute_path(file_name, *args) + if file_name.to_s.chars.first == ':' then + #puts "self.absolute_path(file_name, *args), file_name = #{file_name}, args = #{args}" + #STDOUT.flush + return Mylib.get_absolute_path(file_name) + elsif args.size == 1 && args[0].to_s.chars.first == ':' then + #puts "2 self.absolute_path(file_name, *args), file_name = #{file_name}, args = #{args}" + #puts "x = #{File.join(args[0], file_name)}" + #puts "y = #{Mylib.get_absolute_path(File.join(args[0], file_name))}" + #STDOUT.flush + #return original_absolute_path(file_name, *args) + return Mylib.get_absolute_path(File.join(args[0], file_name)) + end + return original_absolute_path(file_name, *args) + end + + def self.realpath(file_name, *args) + if file_name.to_s.chars.first == ':' then + #puts "self.realpath(file_name, *args), file_name = #{file_name}, args = #{args}" + #STDOUT.flush + return Mylib.get_absolute_path(file_name) + elsif args.size == 1 && args[0].to_s.chars.first == ':' then + #puts "2 self.realpath(file_name, *args), file_name = #{file_name}, args = #{args}" + #puts "x = #{File.join(args[0], file_name)}" + #puts "y = #{Mylib.get_absolute_path(File.join(args[0], file_name))}" + #STDOUT.flush + #return original_realpath(file_name, *args) + return Mylib.get_absolute_path(File.join(args[0], file_name)) + end + return original_realpath(file_name, *args) + end + + def self.directory?(file_name) + if file_name.to_s.chars.first == ':' then + absolute_path = Mylib.get_absolute_path(file_name) + EmbeddedScripting::fileNames.each do |file| + # true if a file starts with this absolute path + next unless file.start_with?(absolute_path) + + # if this is an exact match because then this is a file not a directory + return false if (file == absolute_path) + + # If here, found a match + return true + end + return false + end + return original_directory?(file_name) + end + + def self.file?(file_name) + if file_name.to_s.chars.first == ':' then + absolute_path = Mylib.get_absolute_path(file_name) + EmbeddedScripting::fileNames.each do |file| + # Skip files unless exact match + next unless (file == absolute_path) + + # If here, found a match + return true + end + return false + end + return original_file?(file_name) + end + +end + +class Dir + class << self + alias :original_glob :glob + alias :original_chdir :chdir + end + + def self.[](*args) + if block_given? + return yield(self.glob(args, 0)) + else + return self.glob(args, 0) + end + end + + def self.glob(pattern, *args) + + pattern_array = [] + if pattern.is_a? String + pattern_array = [pattern] + elsif pattern.is_a? Array + pattern_array = pattern + else + pattern_array = pattern + end + + #puts "Dir.glob pattern = #{pattern}, pattern_array = #{pattern_array}, args = #{args}" + override_args_extglob = false + + result = [] + pattern_array.each do |pattern| + + if pattern.to_s.chars.first == ':' + + # DLM: seems like this is needed for embedded paths, possibly due to leading ':' character? + override_args_extglob = true + + #puts "searching embedded files for #{pattern}" + absolute_pattern = Mylib.get_absolute_path(pattern) + #puts "absolute_pattern #{absolute_pattern}" + + EmbeddedScripting::fileNames.each do |name| + absolute_path = Mylib.get_absolute_path(name) + + if override_args_extglob + if File.fnmatch( absolute_pattern, absolute_path, File::FNM_EXTGLOB ) + #puts "#{absolute_path} is a match!" + result << absolute_path + end + else + if File.fnmatch( absolute_pattern, absolute_path, *args ) + #puts "#{absolute_path} is a match!" + result << absolute_path + end + end + + end + + else + if override_args_extglob + result.concat(self.original_glob(pattern, File::FNM_EXTGLOB)) + else + result.concat(self.original_glob(pattern, *args)) + end + end + end + + if block_given? + return yield(result) + else + return result + end + + end + + def self.chdir(dir) + if dir.to_s.chars.first == ':' + # DLM: yeah sorry, don't know what to do here + absolute_path = Mylib.get_absolute_path(dir) + if block_given? + return yield(absolute_path) + else + return absolute_path + end + end + + if block_given? + return yield(self.original_chdir(dir)) + else + return self.original_chdir(dir) + end + + end +end + +require 'fileutils' +module FileUtils + class << self + alias :original_cp_r :cp_r + alias :original_cp :cp + end + + def self.cp_r(src, dest, options = {}) + #puts "cp_r #{src} to #{dest}" + if src.to_s.chars.first == ':' then + absolute_path = Mylib.get_absolute_path(src) + + #puts "cp_r absolute_path = #{absolute_path}" + + # Loop through all he files in the embedded system + matches = [] + EmbeddedScripting::fileNames.each do |file| + # Skip files outside of the specified directory + next unless file.start_with?(absolute_path) + + # If here, found a match + matches << file + end + + if matches.count > 0 then + matches.each do |absolute_path| + #puts "do copy absolute_path = #{absolute_path}" + s = EmbeddedScripting::getFileAsString(absolute_path) + + new_dest = dest + if matches.count > 1 + #puts "dest is dir = #{absolute_path}" + new_dest = File.join(dest, File.basename(absolute_path)) + end + #puts "new_dest = #{new_dest}" + FileUtils.mkdir_p(File.dirname(new_dest)) + + File.open(new_dest, 'w') do |f| + f.puts s + end + end + return true + else + puts "FileUtils.cp_r cannot find embedded file '#{absolute_path}' for '#{src}'" + return false + end + end + + self.original_cp_r(src, dest, options) + end + + def self.cp(src, dest, options = {}) + #puts "cp #{src} to #{dest}" + if src.to_s.chars.first == ':' then + absolute_path = Mylib.get_absolute_path(src) + + # TODO: if src is a directory + + if EmbeddedScripting::hasFile(absolute_path) then + s = EmbeddedScripting::getFileAsString(absolute_path) + + if File.directory?(dest) + dest = File.join(dest, File.basename(src)) + end + FileUtils.mkdir_p(File.dirname(dest)) + + File.open(dest, 'w') do |f| + f.puts s + end + return true + else + puts "FileUtils.cp cannot find embedded file '#{absolute_path}' for '#{src}'" + return false + end + end + + self.original_cp(src, dest, options) + end + + def self.copy(src, dest, options = {}) + return self.cp(src, dest, options) + end +end + +require 'find' +module Find + class << self + alias :original_find :find + end + + def self.find(*paths, ignore_error: true) + block_given? or return enum_for(__method__, *paths, ignore_error: ignore_error) + + fs_encoding = Encoding.find("filesystem") + + all_paths = paths.collect! do |d| + + # this is overriden to ignore files on embedded file system + if d.to_s.chars.first == ':' + #puts "Find.find skipping '#{d}'" + #STDOUT.flush + nil + next + end + + raise Errno::ENOENT unless File.exist?(d) + d.dup + end + + # this is overriden to ignore files on embedded file system + all_paths.reject! {|x| x.nil?} + + all_paths.each do |path| + #puts "all_paths path = '#{path}'" + #STDOUT.flush + path = path.to_path if path.respond_to? :to_path + enc = path.encoding == Encoding::US_ASCII ? fs_encoding : path.encoding + ps = [path] + while file = ps.shift + catch(:prune) do + yield file.dup.taint + begin + s = File.lstat(file) + rescue Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR, Errno::ELOOP, Errno::ENAMETOOLONG + raise unless ignore_error + next + end + if s.directory? then + begin + fs = Dir.entries(file, encoding: enc) + rescue Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR, Errno::ELOOP, Errno::ENAMETOOLONG + raise unless ignore_error + next + end + fs.sort! + fs.reverse_each {|f| + next if f == "." or f == ".." + f = File.join(file, f) + ps.unshift f.untaint + } + end + end + end + end + end +end + +require 'rbconfig' + +module RbConfig + def RbConfig.ruby + EmbeddedScripting::applicationFilePath; + end +end diff --git a/osruby_config.h b/osruby_config.h new file mode 100644 index 0000000..246323b --- /dev/null +++ b/osruby_config.h @@ -0,0 +1,19 @@ +#ifndef INCLUDE_OSRUBY_H +#define INCLUDE_OSRUBY_H 1 + +// Force RUBY_EXTERN to be defined as "undecorated" extern for static ruby +// We want to get rid of the declspec appended by the ruby build system, because +// it causes a compiler warning that manifests as error +#ifdef RUBY_EXTERN +#undef RUBY_EXTERN +#endif + +#define RUBY_EXTERN extern + +// Unclear why Ruby defines inline as __inline in config.h, +// but we want to undo that or embedded ruby and bindings wont compile +#ifdef inline +#undef inline +#endif + +#endif From fda6ea6431d6978d698eff251ec0c9d81b2cf62a Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Tue, 11 May 2021 21:50:45 +0200 Subject: [PATCH 14/16] PFFF needle in a haystack. Turns that if I omit stl.i (or just std_vector.i really) then it works with ruby too. --- EmbeddedScripting.i | 6 ++++++ Measure.i | 8 +++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/EmbeddedScripting.i b/EmbeddedScripting.i index 9f28fb6..caaf1f8 100644 --- a/EmbeddedScripting.i +++ b/EmbeddedScripting.i @@ -11,6 +11,12 @@ //%import //%import +// Note: JM 2021-05-11 +// Regarding Dan's note above: As soon as you include something like std_vector.i => multiple redefinition of GC_VALUE +// This will end up being defined inside the Wrap.cxx file as a static ... +// The main.cpp defines it for some reason +// %include + %{ #include #include "EmbeddedHelp.hpp" diff --git a/Measure.i b/Measure.i index 9812136..a88524b 100644 --- a/Measure.i +++ b/Measure.i @@ -24,7 +24,13 @@ %module mylib %module(directors="1") mylib -%include +// Note: JM 2021-05-11 +// Regarding Dan's note above: As soon as you include something like std_vector.i (stl.i does it...) => multiple redefinition of GC_VALUE +// This will end up being defined inside the Wrap.cxx file as a static ... +// The main.cpp defines it for some reason +// %include + +%include %feature("director") Measure; From 0d99515b2808c1d8ba47e2f6407a1fc3937dcc61 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Tue, 11 May 2021 22:42:25 +0200 Subject: [PATCH 15/16] Extend the CLI to ask user to provide path to ruby/python scripts to run. --- main.cpp | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/main.cpp b/main.cpp index a248317..55478ef 100644 --- a/main.cpp +++ b/main.cpp @@ -18,6 +18,25 @@ int main([[maybe_unused]] const int argc, [[maybe_unused]] const char* argv[]) { + std::string ruby_script_path; + std::string python_script_path; + + constexpr bool enableGetLine = true; + if constexpr (enableGetLine) { + std::cout << "Enter path to ruby_script: "; + std::getline(std::cin, ruby_script_path); + + std::cout << "Enter path to python_script: "; + std::getline(std::cin, python_script_path); + } else { + ruby_script_path = "/home/julien/Software/Cpp_Swig_Ruby_Python_MCVE/ruby/test_measure.rb"; + python_script_path = "/home/julien/Software/Cpp_Swig_Ruby_Python_MCVE/python/test_measure.py"; + + // Test with relative path, expects to find it in current dir (CURRENT DIR WHERE YOU RUN THE EXE, NOT THE EXE DIR ITSELF) + ruby_script_path = "test_measure.rb"; + python_script_path = "test_measure.py"; + } + #ifdef WITHRUBY Test::RubyEngine ruby; #endif @@ -43,7 +62,14 @@ int main([[maybe_unused]] const int argc, [[maybe_unused]] const char* argv[]) { #endif #ifdef WITHRUBY - ruby.exec("require '/home/julien/Software/Cpp_Swig_Ruby_Python_MCVE/ruby/test_measure.rb'"); + // ruby.exec("require '/home/julien/Software/Cpp_Swig_Ruby_Python_MCVE/ruby/test_measure.rb'"); + std::cout << "Loading ruby_script '" << ruby_script_path << "'\n"; + if (auto pos = python_script_path.find_last_of("/\\"); pos != std::string::npos) { + ruby.exec("require '" + ruby_script_path + "'"); + } else { + ruby.exec("require_relative '" + ruby_script_path + "'"); + } + auto ruby_measure = ruby.eval("RubyTestMeasure.new()"); auto* ruby_measure_from_cpp = ruby.getAs(ruby_measure); assert(ruby_measure_from_cpp); @@ -51,9 +77,31 @@ int main([[maybe_unused]] const int argc, [[maybe_unused]] const char* argv[]) { #endif #ifdef WITHPYTHON - python.exec("import sys\nsys.path.append('/home/julien/Software/Cpp_Swig_Ruby_Python_MCVE/python/')\nprint(f'{sys.path}')"); - python.exec("import test_measure"); - auto python_measure = python.eval("test_measure.PythonTestMeasure()"); + std::cout << "Loading python_script '" << python_script_path << "'\n"; + std::string moduleName; + auto pos = python_script_path.find_last_of("/\\"); + if (pos != std::string::npos) { + std::string dirName = python_script_path.substr(0, pos); + std::string fileName = python_script_path.substr(pos+1); + auto lastindex = fileName.find_last_of("."); + moduleName = fileName.substr(0, lastindex); + + std::cout << "import sys\nsys.path.append('"+ dirName + "')\nprint(f'{sys.path}')\n\n"; + python.exec("import sys\nsys.path.append('"+ dirName + "')\nprint(f'{sys.path}')"); + std::cout << "import " + moduleName << '\n'; + python.exec("import " + moduleName); + } else { + auto lastindex = python_script_path.find_last_of("."); + moduleName = python_script_path.substr(0, lastindex); + + std::cout << "import sys\nsys.path.append('.')\nprint(f'{sys.path}')\n\n"; + python.exec("import sys\nsys.path.append('.')\nprint(f'{sys.path}')"); + std::cout << "import " + moduleName << '\n'; + python.exec("import " + moduleName); + std::cout << "ABBOOOOORT\n"; + } + + auto python_measure = python.eval(moduleName + ".PythonTestMeasure()"); auto* python_measure_from_cpp = python.getAs(python_measure); assert(python_measure_from_cpp); #endif From 16acd44fd0c61f884406240ab72b877d3ccec9d4 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Tue, 11 May 2021 22:42:39 +0200 Subject: [PATCH 16/16] Add note for getting measure class name from measure.xml or something --- main.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/main.cpp b/main.cpp index 55478ef..085f7a4 100644 --- a/main.cpp +++ b/main.cpp @@ -70,6 +70,8 @@ int main([[maybe_unused]] const int argc, [[maybe_unused]] const char* argv[]) { ruby.exec("require_relative '" + ruby_script_path + "'"); } + + // Note: The name of the class is hardcoded here (same on Python side). This is something we would need to get from the measure.xml or something auto ruby_measure = ruby.eval("RubyTestMeasure.new()"); auto* ruby_measure_from_cpp = ruby.getAs(ruby_measure); assert(ruby_measure_from_cpp);