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/.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/CMakeLists.txt b/CMakeLists.txt index a8d8a8f..a23c2bf 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 -Werror -Wno-unused-variable -Wno-unused-parameter) +# 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,129 +27,153 @@ 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") + "MinSizeRel" "RelWithDebInfo") endif() -############################################################################### -# N I N J A # -############################################################################### +# ############################################################################## +# 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 "${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") + +# 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") + +# Add to include path -# Xcode/Ninja generators undefined MAKE -if(CMAKE_GENERATOR MATCHES "Make") - set(MAKE "$(MAKE)") +# 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() - set(MAKE make) + message(STATUS "Using existing conan.cmake") 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() +include(${CMAKE_BINARY_DIR}/conan.cmake) -if("Ninja" STREQUAL ${CMAKE_GENERATOR}) - # Clang - if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - AddCXXFlagIfSupported(-fcolor-diagnostics COMPILER_SUPPORTS_fcolor-diagnostics) - endif() +conan_check(VERSION 1.21.0 REQUIRED) - # 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() +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 +) -# 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") -include_directories("${CMAKE_CURRENT_BINARY_DIR}") +message("CONAN_LIBS=${CONAN_LIBS}") -# Add to include path +# 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() -# Project source directory -include_directories("${PROJECT_SOURCE_DIR}") +if(BUILD_RUBY_BINDINGS) + get_target_property(RUBY_INCLUDE_DIRS CONAN_PKG::openstudio_ruby INTERFACE_INCLUDE_DIRECTORIES) +endif() -############################################################################### -# 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") -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") +# 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 # -####################################################################### +# ############################################################################## +# 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 ModelObject.hpp ModelObject.cpp Runner.hpp + SpecialRunner.hpp) +add_library(${target_name} STATIC ${${target_name}_src}) +target_link_libraries(${target_name} PRIVATE cpp_warning_options + cpp_compile_options) -####################################################################### -# S W I G # -####################################################################### - +# ############################################################################## +# 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 +185,361 @@ 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) +if(BUILD_PYTHON_BINDINGS) - set(swig_target_name ${swig_target_name}_python) + add_compile_definitions(WITHPYTHON) - find_package(Python COMPONENTS Interpreter Development REQUIRED) + set(swig_target_name ${target_name}_python) - include_directories(SYSTEM ${Python_INCLUDE_DIRS}) + # **Only** find static libs + set(Python_USE_STATIC_LIBS TRUE) - 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} - ) + find_package( + Python + COMPONENTS Interpreter Development + REQUIRED) - set_target_properties(${swig_target_name} PROPERTIES OUTPUT_NAME ${target_name}) + swig_add_library( + ${swig_target_name} + 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}) - 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/") + 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}) +# 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) +# 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) + + # 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") + + 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() endif() if(BUILD_RUBY_BINDINGS) - find_package(Ruby REQUIRED) - include_directories(SYSTEM ${Ruby_INCLUDE_DIRS}) + add_compile_definitions(WITHRUBY) + add_compile_definitions(RUBY_EMBEDDED) - set(swig_target_name ${target_name}_ruby) + set(CMAKE_SWIG_FLAGS ${CMAKE_SWIG_FLAGS} -fvirtual) - 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} + 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 ) - set_target_properties(${swig_target_name} PROPERTIES OUTPUT_NAME ${target_name}) + # 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_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/") - swig_link_libraries(${swig_target_name} ${target_name} ${Ruby_LIBRARIES}) + set(swig_target_name ${target_name}_ruby) + if(TRUE) + swig_add_library( + ${swig_target_name} + 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}) + + 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} ) + + 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_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) + + + # 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") + + 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( 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() + +if (BUILD_PYTHON_BINDINGS AND BUILD_RUBY_BINDINGS) + + # Executable + 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}/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) + 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} + ${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) + 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}) +elseif (BUILD_RUBY_BINDINGS) + + # Executable + add_executable( + Test + main.cpp + ScriptEngine.hpp + RubyEngine.hpp + RubyEngine.cpp + "${CMAKE_CURRENT_BINARY_DIR}/embedded_scripting_wrap.cxx" + ${EMBEDDED_OUTPUT} + "${CMAKE_CURRENT_BINARY_DIR}/ruby_wrapper/SWIGRubyRuntime.hxx" + ) + + target_link_libraries(Test PRIVATE ${target_name} cpp_compile_options + 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 + ${EMBEDDED_OUTPUT} + "${CMAKE_CURRENT_BINARY_DIR}/embedded_files.cxx" + "${CMAKE_CURRENT_BINARY_DIR}/embedded_files.hxx" + "${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/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..caaf1f8 --- /dev/null +++ b/EmbeddedScripting.i @@ -0,0 +1,30 @@ +#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 + +// 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" +%} + +%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.cpp b/Measure.cpp new file mode 100644 index 0000000..ad91c92 --- /dev/null +++ b/Measure.cpp @@ -0,0 +1,7 @@ +#include "Measure.hpp" + +namespace Test { +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 new file mode 100644 index 0000000..30e6694 --- /dev/null +++ b/Measure.hpp @@ -0,0 +1,36 @@ +#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; + + 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 bool run_impl(Runner&) = 0; +}; + + +} // Namespace Test + +#endif // ifndef MEASURE_HPP diff --git a/Measure.i b/Measure.i new file mode 100644 index 0000000..a88524b --- /dev/null +++ b/Measure.i @@ -0,0 +1,55 @@ +#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 + +// 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; + +#if defined(SWIGPYTHON) +%rename (PythonMeasure) Test::Measure; +//%rename ("Python%s") ""; +#endif + +%{ + #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..e65aec3 --- /dev/null +++ b/Model.cpp @@ -0,0 +1,44 @@ +#include "Model.hpp" +#include "ModelObject.hpp" + +namespace Test { + Model::Model(std::string name) : name_(std::move(name)) {} + + 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); + } + + 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); + } + +} // namespace Test diff --git a/Model.hpp b/Model.hpp new file mode 100644 index 0000000..df81973 --- /dev/null +++ b/Model.hpp @@ -0,0 +1,31 @@ +#ifndef MODEL_HPP +#define MODEL_HPP + +#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); + + void pushOp(const std::string& op_name); + + const std::vector& opsPerformed() const; + + 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/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..4b4aac2 --- /dev/null +++ b/PythonEngine.cpp @@ -0,0 +1,216 @@ +#include "PythonEngine.hpp" + +#include +#include + +#ifdef __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif + +#include +//#include "SWIGPythonRuntime.hxx" +#include "MeasurePYTHON_wrap.cxx" +#include "embedded_files.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)) { + if (program == nullptr) { + fprintf(stderr, "Fatal error: cannot decode argv[0]\n"); + exit(1); + } + + PyImport_AppendInittab("_mylib", PyInit__mylib); + + Py_SetProgramName(program); // optional but recommended + + Py_Initialize(); +} + +PythonEngine::~PythonEngine() { + if (Py_FinalizeEx() < 0) { + exit(120); + } + PyMem_RawFree(program); +} + +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* 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"); + } + + //decref count returned from Python + Py_DECREF(v); + Py_DecRef( pyModule ) ; + //Py_DecRef( pyCompiledFn ) ; + +} + +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); + + 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(); + 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::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_Python_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 new file mode 100644 index 0000000..15f4dc5 --- /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 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* getAs_impl(ScriptObject& obj, const std::type_info&) override; + + private: + wchar_t* program; +}; +} // namespace Test + +#endif diff --git a/RubyEngine.cpp b/RubyEngine.cpp new file mode 100644 index 0000000..3012347 --- /dev/null +++ b/RubyEngine.cpp @@ -0,0 +1,584 @@ +#include "RubyEngine.hpp" +#include +#include + +#include "embedded_files.hxx" + +// UNCOMMENT THIS! +// #define WITH_GC_VALUE 1 +#ifdef WITH_GC_VALUE +#include "GC_Value.hpp" +#endif + +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; +} + + +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)}; +} + +void RubyEngine::exec(std::string_view sv) { + std::string str{sv}; + [[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::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 + +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 new file mode 100644 index 0000000..ee73a07 --- /dev/null +++ b/RubyEngine.hpp @@ -0,0 +1,1223 @@ +#ifndef RUBYENGINE_included +#define RUBYENGINE_included + +#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 +{ + public: + RubyEngine(); + ~RubyEngine() override; + + RubyEngine(const RubyEngine&) = delete; + RubyEngine(RubyEngine&&) = delete; + RubyEngine& operator=(const RubyEngine&) = delete; + RubyEngine& operator=(RubyEngine&&) = delete; + + 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* getAs_impl(ScriptObject& obj, const std::type_info&) override; +}; +} // 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/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..85c9fb6 --- /dev/null +++ b/ScriptEngine.hpp @@ -0,0 +1,76 @@ +#ifndef SCRIPTENGINE_included +#define SCRIPTENGINE_included + +#include +#include +#include +#include +#include +#include +#include + +namespace Test { +struct ScriptObject +{ + 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 eval(std::string_view sv) = 0; + + // execute string without expecting a return value + virtual void exec(std::string_view sv) = 0; + + 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) const { + return lhs.get().before(rhs.get()); + } + }; + + std::map, std::string, Compare> types; +}; + +} // namespace Test + +#endif diff --git a/SpecialRunner.hpp b/SpecialRunner.hpp new file mode 100644 index 0000000..b706fe8 --- /dev/null +++ b/SpecialRunner.hpp @@ -0,0 +1,24 @@ +#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_; +}; +} // namespace Test + +#endif //TEST_SWIG_SPECIALRUNNER_HPP 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..a86c5dc --- /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/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/empty.cpp b/empty.cpp new file mode 100644 index 0000000..e69de29 diff --git a/main.cpp b/main.cpp index 8c17240..085f7a4 100644 --- a/main.cpp +++ b/main.cpp @@ -1,7 +1,170 @@ -#include "Person.hpp" +// CMake defines WITHPYTHON / WITHRUBY (or not) + #include +#include + +#ifdef WITHRUBY +#include "RubyEngine.hpp" +#endif + +#ifdef WITHPYTHON +#include "PythonEngine.hpp" +#endif + +#include "Measure.hpp" +#include "SpecialRunner.hpp" + +#include "Model.hpp" + +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 + + #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'"); + 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 + "'"); + } + + + // 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); + std::cout << "Ruby measure name: " << ruby_measure_from_cpp->name() << '\n'; + #endif + + #ifdef WITHPYTHON + 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 + + // 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. + #ifdef WITHPYTHON + std::cout << "Python measure name: " << python_measure_from_cpp->name() << '\n'; + #endif + + 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()); + + #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 + + + 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"; + #endif + + #if defined(WITHPYTHON) && defined(WITHRUBY) + std::cout << "After Running Ruby and Python: model is named " << sr.get_current_model().getName() << '\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()) { + std::cout << "Op 'run' from script: " << op << '\n'; + } + + std::cout << "C++: " << sr.get_current_model().numObjects() << " objects\n"; + printObjectNames(sr.get_current_model()); -int main() { - Test::Person p("John Doe"); - std::cout << p << '\n'; } 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 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..21bf461 --- /dev/null +++ b/python/test_measure.py @@ -0,0 +1,34 @@ +# 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 + +class PythonTestMeasure(mylib.PythonMeasure): + def __init__(self): + mylib.PythonMeasure.__init__(self) + print("Created Object (Python __init__)") + + def run_impl(self, r: mylib.Runner): + 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" + + +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..c235cc9 --- /dev/null +++ b/ruby/test_measure.rb @@ -0,0 +1,26 @@ +# require '/home/julien/Software/Cpp_Swig_Ruby_Python_MCVE/build-modif/Products/ruby/mylib.so' + +class RubyTestMeasure < Mylib::Measure + def name + return "RubyTestMeasure" + end + + def run_impl(runner) + 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 + +