ruble 0.0.3.alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gdbinit +21 -0
- data/.gitignore +18 -0
- data/.rubocop.yml +96 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +6 -0
- data/CMakeLists.txt +4 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +21 -0
- data/Gemfile.lock +98 -0
- data/LICENSE.txt +21 -0
- data/README.md +63 -0
- data/Rakefile +41 -0
- data/ext/ruble/.gitignore +5 -0
- data/ext/ruble/CMakeLists.txt +157 -0
- data/ext/ruble/RuBLEHelpers.cmake +240 -0
- data/ext/ruble/bindings/Adapter.cpp +193 -0
- data/ext/ruble/bindings/Adapter.hpp +85 -0
- data/ext/ruble/bindings/Characteristic.cpp +171 -0
- data/ext/ruble/bindings/Characteristic.hpp +132 -0
- data/ext/ruble/bindings/Descriptor.cpp +34 -0
- data/ext/ruble/bindings/Descriptor.hpp +69 -0
- data/ext/ruble/bindings/Peripheral.cpp +212 -0
- data/ext/ruble/bindings/Peripheral.hpp +108 -0
- data/ext/ruble/bindings/RuBLE.cpp +115 -0
- data/ext/ruble/bindings/Service.cpp +112 -0
- data/ext/ruble/bindings/Service.hpp +61 -0
- data/ext/ruble/bindings/common.hpp +48 -0
- data/ext/ruble/bindings/globals.cpp +43 -0
- data/ext/ruble/cmake.mk +62 -0
- data/ext/ruble/concerns/CharacteristicValueTracker.cpp +18 -0
- data/ext/ruble/concerns/CharacteristicValueTracker.hpp +40 -0
- data/ext/ruble/concerns/Rubyable.cpp +4 -0
- data/ext/ruble/concerns/Rubyable.hpp +46 -0
- data/ext/ruble/config.h.in +25 -0
- data/ext/ruble/containers/ByteArray.cpp +64 -0
- data/ext/ruble/containers/ByteArray.hpp +161 -0
- data/ext/ruble/containers/Callback.hpp +52 -0
- data/ext/ruble/containers/NamedBitSet.hpp +140 -0
- data/ext/ruble/containers/NamedBitSet.ipp +71 -0
- data/ext/ruble/extconf.rb +30 -0
- data/ext/ruble/management/Registry.cpp +63 -0
- data/ext/ruble/management/Registry.hpp +170 -0
- data/ext/ruble/management/RegistryFactory.hpp +113 -0
- data/ext/ruble/management/RubyQueue.cpp +152 -0
- data/ext/ruble/management/RubyQueue.hpp +69 -0
- data/ext/ruble/modularize.diff +28 -0
- data/ext/ruble/types/SimpleBLE.hpp +21 -0
- data/ext/ruble/types/declarations.hpp +91 -0
- data/ext/ruble/types/helpers.hpp +12 -0
- data/ext/ruble/types/ruby.hpp +36 -0
- data/ext/ruble/types/stl.hpp +41 -0
- data/ext/ruble/utils/RubyCallbackTraits.cpp +28 -0
- data/ext/ruble/utils/RubyCallbackTraits.hpp +48 -0
- data/ext/ruble/utils/async.cpp +10 -0
- data/ext/ruble/utils/async.hpp +76 -0
- data/ext/ruble/utils/containers.hpp +41 -0
- data/ext/ruble/utils/exception_handling.cpp +50 -0
- data/ext/ruble/utils/exception_handling.hpp +53 -0
- data/ext/ruble/utils/garbage_collection.cpp +82 -0
- data/ext/ruble/utils/garbage_collection.hpp +22 -0
- data/ext/ruble/utils/hash.cpp +83 -0
- data/ext/ruble/utils/hash.hpp +52 -0
- data/ext/ruble/utils/hexadecimal.hpp +116 -0
- data/ext/ruble/utils/human_type_names.hpp +38 -0
- data/ext/ruble/utils/inspection.cpp +24 -0
- data/ext/ruble/utils/inspection.hpp +108 -0
- data/ext/ruble/utils/ruby.hpp +103 -0
- data/ext/ruble/utils/ruby_context.hpp +73 -0
- data/lib/ruble/build/.rubocop.yml +19 -0
- data/lib/ruble/build/boost.rb +34 -0
- data/lib/ruble/build/cmake.rb +134 -0
- data/lib/ruble/build/core_ext.rb +5 -0
- data/lib/ruble/build/data/bundler.rb +24 -0
- data/lib/ruble/build/data/extension.rb +101 -0
- data/lib/ruble/build/data/os.rb +21 -0
- data/lib/ruble/build/data/rice.rb +24 -0
- data/lib/ruble/build/data.rb +22 -0
- data/lib/ruble/build/extconf.rb +76 -0
- data/lib/ruble/build/github_repo.rb +129 -0
- data/lib/ruble/build/simpleble.rb +56 -0
- data/lib/ruble/build.rb +28 -0
- data/lib/ruble/version.rb +7 -0
- data/lib/ruble.rb +46 -0
- data/lib/tasks/dev/dev_tasks.rb +130 -0
- data/lib/tasks/dev/pager.rb +218 -0
- data/lib/tasks/dev/paths.rb +30 -0
- data/lib/tasks/dev/state_hash.rb +65 -0
- data/lib/tasks/dev.rake +41 -0
- data/lib/tasks/simpleble.rake +29 -0
- data/sig/rubble.rbs +4 -0
- metadata +263 -0
@@ -0,0 +1,116 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
#include "types/declarations.hpp"
|
4
|
+
|
5
|
+
#include <cstddef>
|
6
|
+
#include <string>
|
7
|
+
#include <iostream>
|
8
|
+
#include <memory>
|
9
|
+
#include <type_traits>
|
10
|
+
#include <format>
|
11
|
+
#include <ranges>
|
12
|
+
#include <boost/algorithm/hex.hpp>
|
13
|
+
|
14
|
+
namespace RuBLE::Utils {
|
15
|
+
namespace Hexadecimal {
|
16
|
+
namespace ranges = std::ranges;
|
17
|
+
namespace views = std::views;
|
18
|
+
using namespace std::string_view_literals;
|
19
|
+
|
20
|
+
constexpr const auto hex_string_prefix = "0x"sv;
|
21
|
+
constexpr const auto hex_prefix_len = hex_string_prefix.length();
|
22
|
+
|
23
|
+
// Testing:
|
24
|
+
template <bool prefix, std::integral T>
|
25
|
+
std::string to_hex_string(const T &num);
|
26
|
+
template <bool prefix, typename T> requires std::is_pointer_v<T>
|
27
|
+
std::string to_hex_addr(const T &ptr);
|
28
|
+
template <bool prefix, typename T>
|
29
|
+
std::string to_hex_addr(const std::shared_ptr<T> &ptr);
|
30
|
+
template <bool prefix, typename T> requires std::is_base_of_v<Rice::Object, T>
|
31
|
+
std::string to_hex_addr(const T &obj);
|
32
|
+
template <bool prefix, typename T>
|
33
|
+
std::string to_hex_addr(const T &obj);
|
34
|
+
template<bool prefix, ranges::viewable_range T>
|
35
|
+
[[maybe_unused]] std::string to_hex_data(const T &t);
|
36
|
+
template<bool prefix, class T>
|
37
|
+
[[maybe_unused]] std::string to_hex_data(const T &t);
|
38
|
+
// End Testing
|
39
|
+
|
40
|
+
template <bool prefix = true, std::integral T = void>
|
41
|
+
std::string ref_to_hex_string(const T &num) {
|
42
|
+
constexpr std::size_t result_len = std::max(std::size_t(2), sizeof (T) * 2) + (prefix ? 2 : 0);
|
43
|
+
constexpr const auto fmt_str = prefix ? "{:#0{}x}" : "{:0{}x}";
|
44
|
+
return std::format(fmt_str, num, result_len);
|
45
|
+
}
|
46
|
+
|
47
|
+
template <bool prefix = true, typename T = void> requires std::is_pointer_v<T>
|
48
|
+
std::string ptr_to_hex_addr(const T &ptr) {
|
49
|
+
return to_hex_string<prefix>(reinterpret_cast<uintptr_t>(std::to_address(ptr)));
|
50
|
+
}
|
51
|
+
|
52
|
+
template <bool prefix = true, typename T = void>
|
53
|
+
std::string sptr_to_hex_addr(const std::shared_ptr<T> &ptr) {
|
54
|
+
return to_hex_addr<prefix>(ptr.get());
|
55
|
+
}
|
56
|
+
|
57
|
+
template <bool prefix = true, typename T = void> requires std::is_base_of_v<Rice::Object, T>
|
58
|
+
std::string rb_to_hex_addr(const T &obj) {
|
59
|
+
return to_hex_string<prefix>(obj.value());
|
60
|
+
}
|
61
|
+
|
62
|
+
template <bool prefix = true, typename T = void>
|
63
|
+
std::string ref_to_hex_addr(const T &obj) {
|
64
|
+
return to_hex_addr<prefix>(&obj);
|
65
|
+
}
|
66
|
+
|
67
|
+
template<bool prefix = true, ranges::viewable_range T = void>
|
68
|
+
std::string range_to_hex_data(const T &t) {
|
69
|
+
constexpr std::size_t prefix_len = prefix ? hex_prefix_len : 0;
|
70
|
+
constexpr auto null_byte = static_cast<std::byte>(0);
|
71
|
+
constexpr auto is_null = [](const std::byte &b) { return null_byte == b; };
|
72
|
+
|
73
|
+
// we can't simply use `std::as_bytes(t) | views::transform | views::join` due to
|
74
|
+
// https://en.cppreference.com/mwiki/index.php?title=cpp/ranges/join_view&oldid=155702#Notes
|
75
|
+
|
76
|
+
std::string output = prefix ? std::string(hex_string_prefix) : "";
|
77
|
+
auto span = std::span(t.begin(), t.end());
|
78
|
+
|
79
|
+
constexpr auto byte_to_int = [](const std::byte &byte) -> uint8_t {
|
80
|
+
return std::to_integer<uint8_t>(byte);
|
81
|
+
};
|
82
|
+
|
83
|
+
auto in = std::as_bytes(span) | views::drop_while(is_null) | views::transform(byte_to_int);
|
84
|
+
boost::algorithm::hex_lower(in.begin(), in.end(), std::back_inserter(output));
|
85
|
+
|
86
|
+
std::size_t moved_hex_pairs = output.length() - prefix_len;
|
87
|
+
|
88
|
+
if (moved_hex_pairs == 0) output.append("00");
|
89
|
+
return output;
|
90
|
+
}
|
91
|
+
|
92
|
+
template<bool prefix = true, class T>
|
93
|
+
std::string ref_to_hex_data(const T &t) {
|
94
|
+
auto span = std::span(&t, 1);
|
95
|
+
return to_hex_data<prefix, decltype(span)>(span);
|
96
|
+
}
|
97
|
+
|
98
|
+
// Testing:
|
99
|
+
template <bool prefix = true, std::integral T = void>
|
100
|
+
std::string to_hex_string(const T &num) { return ref_to_hex_string(num); }
|
101
|
+
template <bool prefix = true, typename T = void> requires std::is_pointer_v<T>
|
102
|
+
std::string to_hex_addr(const T &ptr) { return ptr_to_hex_addr(ptr); }
|
103
|
+
template <bool prefix = true, typename T = void>
|
104
|
+
std::string to_hex_addr(const std::shared_ptr<T> &ptr) { return sptr_to_hex_addr(ptr); }
|
105
|
+
template <bool prefix = true, typename T = void> requires std::is_base_of_v<Rice::Object, T>
|
106
|
+
std::string to_hex_addr(const T &obj) { return rb_to_hex_addr(obj); }
|
107
|
+
template <bool prefix = true, typename T = void>
|
108
|
+
std::string to_hex_addr(const T &obj) { return ref_to_hex_addr(obj); }
|
109
|
+
template<bool prefix = true, ranges::viewable_range T>
|
110
|
+
[[maybe_unused]] std::string to_hex_data(const T &t) { return range_to_hex_data(t); }
|
111
|
+
template<bool prefix = true, class T>
|
112
|
+
[[maybe_unused]] std::string to_hex_data(const T &t) { return ref_to_hex_data(t); }
|
113
|
+
} // Hexadecimal
|
114
|
+
using namespace Hexadecimal;
|
115
|
+
} // RuBLE::Utils
|
116
|
+
|
@@ -0,0 +1,38 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
#include <string>
|
4
|
+
#include <typeinfo>
|
5
|
+
#include <type_traits>
|
6
|
+
#include <concepts>
|
7
|
+
#include <any>
|
8
|
+
#include <boost/core/demangle.hpp>
|
9
|
+
#include <iostream>
|
10
|
+
|
11
|
+
#include <rice/rice.hpp>
|
12
|
+
#include <rice/stl.hpp>
|
13
|
+
|
14
|
+
|
15
|
+
namespace RuBLE::Utils {
|
16
|
+
namespace HumanTypeNames {
|
17
|
+
using boost::core::demangle;
|
18
|
+
|
19
|
+
// need to predeclare, since the templated fns call the fully specified version
|
20
|
+
template<typename T>
|
21
|
+
constexpr std::string human_type_name();
|
22
|
+
|
23
|
+
template<typename T> //requires (!std::convertible_to<T, const std::type_info&>)
|
24
|
+
constexpr std::string human_type_name(const T &) { return human_type_name<T>(); }
|
25
|
+
|
26
|
+
template<typename = void>
|
27
|
+
constexpr std::string human_type_name(const std::type_info &type) {
|
28
|
+
return demangle(type.name());
|
29
|
+
}
|
30
|
+
|
31
|
+
template<typename T>
|
32
|
+
constexpr std::string human_type_name() { return human_type_name(typeid(T)); }
|
33
|
+
|
34
|
+
template<>
|
35
|
+
[[maybe_unused]] constexpr std::string human_type_name(const std::any &any) { return human_type_name(any.type()); }
|
36
|
+
}
|
37
|
+
using namespace HumanTypeNames;
|
38
|
+
} // RuBLE::Utils
|
@@ -0,0 +1,24 @@
|
|
1
|
+
#include "inspection.hpp"
|
2
|
+
|
3
|
+
namespace RuBLE::Utils::Inspection {
|
4
|
+
[[maybe_unused]] std::string basic_object_inspect_start(const Rice::Object &o) {
|
5
|
+
std::ostringstream oss;
|
6
|
+
oss << "#<" << o.class_name();
|
7
|
+
oss << ":" << to_hex_string(reinterpret_cast<uint64_t>(o.value()));
|
8
|
+
return oss.str();
|
9
|
+
}
|
10
|
+
std::string ruby_quote_string(const std::string &str) {
|
11
|
+
return ensure_ruby<std::string>([&str]() -> std::string {
|
12
|
+
Object obj = Object(rb_cString).call("new", str);
|
13
|
+
return ruby_inspect(obj);
|
14
|
+
});
|
15
|
+
}
|
16
|
+
|
17
|
+
std::string ruby_inspect(Rice::Object obj) {
|
18
|
+
auto fn = [&obj]() -> std::string {
|
19
|
+
VALUE rb_str = obj.call("inspect").value();
|
20
|
+
return Rice::String(rb_str).str();
|
21
|
+
};
|
22
|
+
return ensure_ruby<std::string>(fn);
|
23
|
+
}
|
24
|
+
}
|
@@ -0,0 +1,108 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
#include "types/declarations.hpp"
|
4
|
+
#include "types/ruby.hpp"
|
5
|
+
#include "types/SimpleBLE.hpp"
|
6
|
+
#include "utils/human_type_names.hpp"
|
7
|
+
#include "utils/hexadecimal.hpp"
|
8
|
+
#include <boost/describe.hpp>
|
9
|
+
#include <boost/describe/enum.hpp>
|
10
|
+
#include <boost/describe/enum_to_string.hpp>
|
11
|
+
#include <boost/describe/enumerators.hpp>
|
12
|
+
#include <boost/describe/modifier_description.hpp>
|
13
|
+
|
14
|
+
namespace SimpleBLE {
|
15
|
+
BOOST_DESCRIBE_ENUM(
|
16
|
+
BluetoothAddressType,
|
17
|
+
PUBLIC,
|
18
|
+
RANDOM,
|
19
|
+
UNSPECIFIED
|
20
|
+
)
|
21
|
+
}
|
22
|
+
using boost::describe::boost_enum_descriptor_fn;
|
23
|
+
|
24
|
+
namespace RuBLE::Utils {
|
25
|
+
namespace Inspection {
|
26
|
+
std::string ruby_inspect(Object obj);
|
27
|
+
|
28
|
+
std::string ruby_quote_string(const std::string &str);
|
29
|
+
|
30
|
+
// From https://www.boost.org/doc/libs/1_83_0/libs/describe/doc/html/describe.html#example_printing_enums_rt
|
31
|
+
template<class Enum_T> requires std::is_enum_v<Enum_T>
|
32
|
+
struct enumerator_trait { const Enum_T value; const std::string name; };
|
33
|
+
|
34
|
+
template<class Enum_T> requires std::is_enum_v<Enum_T>
|
35
|
+
constexpr auto enumerator_traits_as_array() {
|
36
|
+
constexpr auto impl = []<template<class... T> class L, class... T>( L<T...> ) ->
|
37
|
+
std::array<enumerator_trait<Enum_T>, sizeof...(T)> {
|
38
|
+
return { { { T::value, T::name }... } };
|
39
|
+
};
|
40
|
+
return impl(boost::describe::describe_enumerators<Enum_T>());
|
41
|
+
}
|
42
|
+
|
43
|
+
template<typename Enum_T> requires std::is_enum_v<Enum_T>
|
44
|
+
Rice::Enum<Enum_T> define_described_enum(char const* name, Rice::Module &module) {
|
45
|
+
using Enum_DT = Rice::Enum<Enum_T>;
|
46
|
+
Enum_DT rb_type = define_enum<Enum_T>(name, module);
|
47
|
+
|
48
|
+
const auto traits = enumerator_traits_as_array<Enum_T>();
|
49
|
+
for (const enumerator_trait<Enum_T> &item : traits) rb_type.define_value(item.name, item.value);
|
50
|
+
return rb_type;
|
51
|
+
}
|
52
|
+
|
53
|
+
template<typename Enum_T> requires std::is_enum_v<Enum_T>
|
54
|
+
Rice::Enum<Enum_T> define_described_enum(Rice::Module &module) {
|
55
|
+
return define_described_enum(human_type_name<Enum_T>(), module);
|
56
|
+
}
|
57
|
+
|
58
|
+
template<typename Enum_T>
|
59
|
+
requires std::is_enum_v<Enum_T>
|
60
|
+
std::string enum_rb_type_name(const Enum_T &enumVal) {
|
61
|
+
Object rbEnumItem = Rice::detail::To_Ruby<Enum_T>().convert(enumVal);
|
62
|
+
return rbEnumItem.class_name().str();
|
63
|
+
}
|
64
|
+
|
65
|
+
template<typename Enum_T>
|
66
|
+
requires std::is_enum_v<Enum_T>
|
67
|
+
std::string enum_val_as_string(const Enum_T &enumVal) {
|
68
|
+
constexpr const char *fallback = "(unrecognized)";
|
69
|
+
return boost::describe::enum_to_string<Enum_T>(enumVal, fallback);
|
70
|
+
}
|
71
|
+
|
72
|
+
template<typename Enum_T>
|
73
|
+
requires std::is_enum_v<Enum_T>
|
74
|
+
[[maybe_unused]] std::string enum_val_inspect(const Enum_T &enumVal) {
|
75
|
+
return enum_rb_type_name(enumVal) + "::" + enum_val_as_string(enumVal);
|
76
|
+
}
|
77
|
+
|
78
|
+
// split out the closing '>' into its own function to make enhancing this method easier
|
79
|
+
[[maybe_unused]] std::string basic_object_inspect_start(const Rice::Object &o);
|
80
|
+
|
81
|
+
// template <HasRubyObject T>
|
82
|
+
// std::string basic_object_inspect_start(T &t) {
|
83
|
+
// Object obj = t.self();
|
84
|
+
// return basic_object_inspect_start(obj);
|
85
|
+
// }
|
86
|
+
|
87
|
+
template<class T>
|
88
|
+
// requires (!HasRubyObject<T>)
|
89
|
+
constexpr std::string basic_object_inspect_start(T &t) {
|
90
|
+
std::ostringstream oss;
|
91
|
+
oss << "#<" << human_type_name<T>() << ":" << to_hex_addr<true, T>(t);
|
92
|
+
return oss.str();
|
93
|
+
}
|
94
|
+
|
95
|
+
[[maybe_unused]] constexpr std::string basic_object_inspect(auto &o) {
|
96
|
+
return basic_object_inspect_start(o) + ">";
|
97
|
+
}
|
98
|
+
} // Inspection
|
99
|
+
using namespace Inspection;
|
100
|
+
} // RuBLE::Utils
|
101
|
+
|
102
|
+
template<typename Key, class ProxyClass, class Value>
|
103
|
+
constexpr std::ostream &operator<<(std::ostream &os, const RuBLE::Registry<Key, ProxyClass, Value> ®) {
|
104
|
+
return os << reg.to_s();
|
105
|
+
}
|
106
|
+
|
107
|
+
template<const auto &FIELDS>
|
108
|
+
constexpr std::ostream &operator<<(std::ostream &os, const RuBLE::NamedBitSet<FIELDS> &nbs) { return os << nbs.to_s(); }
|
@@ -0,0 +1,103 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
#include "types/ruby.hpp"
|
4
|
+
#include "utils/ruby_context.hpp"
|
5
|
+
#include "utils/exception_handling.hpp"
|
6
|
+
//#include "types/stl.hpp"
|
7
|
+
#include <ruby/thread.h>
|
8
|
+
#include <memory>
|
9
|
+
#include <functional>
|
10
|
+
|
11
|
+
namespace RuBLE {
|
12
|
+
namespace Utils::Ruby {
|
13
|
+
template <typename T, typename Fn = std::function<T()>>
|
14
|
+
T ensure_ruby(const Fn &fn) {
|
15
|
+
if (in_ruby) return fn();
|
16
|
+
RbThreadCallFn<> invoke = [](void *fnPtr) -> void * {
|
17
|
+
volatile auto in_ruby_guard = in_ruby.assert_in_ruby_guard();
|
18
|
+
auto &fnRef = *reinterpret_cast<Fn *>(fnPtr);
|
19
|
+
return new T(std::move(fnRef()));
|
20
|
+
};
|
21
|
+
void *resultPtrVoid = rb_thread_call_with_gvl(invoke, const_cast<std::remove_const_t<Fn>*>(&fn));
|
22
|
+
T *resultPtr = reinterpret_cast<T*>(resultPtrVoid);
|
23
|
+
T &&result = std::move(*resultPtr);
|
24
|
+
delete resultPtr;
|
25
|
+
return std::move(result);
|
26
|
+
}
|
27
|
+
|
28
|
+
template <typename T, typename... Args>
|
29
|
+
[[maybe_unused]] T ensuring_ruby(std::function<T(Args...)> &&fn, Args &&...args) {
|
30
|
+
auto wrapper = [&fn, &args...]() -> T { return std::invoke(fn, args...); };
|
31
|
+
return ensure_ruby<T>(wrapper);
|
32
|
+
}
|
33
|
+
|
34
|
+
template <typename T, typename... Args>
|
35
|
+
T cpp_protect(std::function<T(Args...)> &&fn, Args &&...args) {
|
36
|
+
auto wrapper = [&fn, &args...]() -> T {
|
37
|
+
try {
|
38
|
+
return std::invoke(fn, args...);
|
39
|
+
} catch (const std::exception &ex) {
|
40
|
+
ExceptionHandling::throw_with_backtrace(ex);
|
41
|
+
}
|
42
|
+
};
|
43
|
+
return Rice::detail::cpp_protect(wrapper);
|
44
|
+
}
|
45
|
+
|
46
|
+
[[maybe_unused]] constexpr VALUE to_ruby_value(const VALUE &val) { return val; }
|
47
|
+
|
48
|
+
[[maybe_unused]] constexpr VALUE to_ruby_value(const RiceObject auto &obj) { return obj.value(); }
|
49
|
+
|
50
|
+
[[maybe_unused]] constexpr VALUE to_ruby_value(const HasRubyObject auto &obj) { return obj.self(); }
|
51
|
+
constexpr VALUE to_ruby_value(const HasRubyObject auto *obj) { return to_ruby_value(*obj); }
|
52
|
+
|
53
|
+
template <class T> requires HasRubyObject<typename T::element_type> &&
|
54
|
+
std::same_as<std::shared_ptr<typename T::element_type>, T>
|
55
|
+
constexpr VALUE to_ruby_value(const T &obj) { return to_ruby_value(*obj); }
|
56
|
+
|
57
|
+
template <RiceObject T = Rice::Object>
|
58
|
+
[[maybe_unused]] constexpr T to_ruby_object(const auto &val) { return T { to_ruby_value(val) }; }
|
59
|
+
|
60
|
+
#if false
|
61
|
+
// TODO: test me!
|
62
|
+
template <typename T, typename... Args>
|
63
|
+
RubyThreadId ruby_thread_create(std::function<T(Args...)> &&fn, Args &&...args) {
|
64
|
+
using FnT = std::function<void()>;
|
65
|
+
FnT invoker = [&fn, &args...]() { std::invoke(fn, args...); };
|
66
|
+
constexpr RbThreadCreateFn<void> wrapper = [](void *ptr) -> VALUE {
|
67
|
+
FnT invoker = *reinterpret_cast<FnT*>(ptr);
|
68
|
+
invoker();
|
69
|
+
return Qnil;
|
70
|
+
};
|
71
|
+
RubyThreadId tid = rb_thread_create(wrapper, &invoker);
|
72
|
+
return tid;
|
73
|
+
}
|
74
|
+
|
75
|
+
// TODO: See if we can figure out how to get this to work (see https://stackoverflow.com/q/28746744)
|
76
|
+
// template <typename T = void>
|
77
|
+
// VALUE ruby_thread_create(RbThreadCreateFn<T> fn, T *arg = nullptr) {
|
78
|
+
// constexpr RbThreadCreateFn<void> wrapper = [&fn](void *ptr) -> VALUE {
|
79
|
+
// return fn(reinterpret_cast<T*>(ptr));
|
80
|
+
// };
|
81
|
+
// return rb_thread_create(wrapper, arg);
|
82
|
+
// }
|
83
|
+
//
|
84
|
+
//
|
85
|
+
// template <typename T = void, typename Ret = void, typename UbfArgT = void>
|
86
|
+
// Ret *ruby_thread_call_without_gvl(RbThreadCallFn<Ret, T> fn,
|
87
|
+
// T *arg = nullptr,
|
88
|
+
// RbUbfFn<UbfArgT> &ubf = nullptr,
|
89
|
+
// UbfArgT *ubfArg = nullptr) {
|
90
|
+
// RbThreadCreateFn<void> wrapper = [&fn](void *ptr) -> void * {
|
91
|
+
// return fn(reinterpret_cast<T*>(ptr));
|
92
|
+
// };
|
93
|
+
// RbUbfFn<void> ubfWrapper = [&ubf](void *ptr) -> void {
|
94
|
+
// return ubf(reinterpret_cast<T*>(ptr));
|
95
|
+
// };
|
96
|
+
// void *ret = rb_thread_call_without_gvl(wrapper, arg, ubf, ubfWrapper);
|
97
|
+
// return reinterpret_cast<Ret*>(ret);
|
98
|
+
// }
|
99
|
+
#endif
|
100
|
+
}
|
101
|
+
namespace Ruby = Utils::Ruby;
|
102
|
+
using Utils::Ruby::ensure_ruby;
|
103
|
+
}
|
@@ -0,0 +1,73 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
#include <cstdint>
|
4
|
+
#include <utility>
|
5
|
+
|
6
|
+
namespace RuBLE::Utils::Ruby::Context {
|
7
|
+
class InRuby;
|
8
|
+
class InRubyGuard {
|
9
|
+
InRuby *_inRuby = nullptr;
|
10
|
+
|
11
|
+
[[maybe_unused]] constexpr InRubyGuard(InRuby &inRuby);
|
12
|
+
public:
|
13
|
+
constexpr InRubyGuard() = default;
|
14
|
+
constexpr InRubyGuard(const InRubyGuard&) = delete;
|
15
|
+
constexpr InRubyGuard &operator=(const InRubyGuard&) = delete;
|
16
|
+
constexpr InRubyGuard(InRubyGuard&&) noexcept ;
|
17
|
+
constexpr InRubyGuard &operator=(InRubyGuard&&) noexcept ;
|
18
|
+
constexpr ~InRubyGuard();
|
19
|
+
constexpr operator bool() const { return _inRuby != nullptr; }
|
20
|
+
|
21
|
+
friend class InRuby;
|
22
|
+
};
|
23
|
+
|
24
|
+
class InRuby {
|
25
|
+
private:
|
26
|
+
uint64_t _use_count = 0;
|
27
|
+
public:
|
28
|
+
using Guard = InRubyGuard;
|
29
|
+
constexpr InRuby() = default;
|
30
|
+
InRuby(const InRuby &) = delete;
|
31
|
+
InRuby(InRuby &&) = delete;
|
32
|
+
InRuby &operator=(InRuby &&) = delete;
|
33
|
+
InRuby &operator=(const InRuby &) = delete;
|
34
|
+
|
35
|
+
// similar to lock_guard: this->state() will return true
|
36
|
+
// as long as at least one result of the assert_in_ruby_guard fn is maintained
|
37
|
+
constexpr Guard assert_in_ruby_guard() {
|
38
|
+
return { *this };
|
39
|
+
}
|
40
|
+
[[nodiscard]] constexpr const auto &use_count() const { return _use_count; }
|
41
|
+
[[nodiscard]] constexpr bool state() const { return use_count() > 0; }
|
42
|
+
constexpr operator bool() const { return state(); }
|
43
|
+
friend class InRubyGuard;
|
44
|
+
};
|
45
|
+
|
46
|
+
[[maybe_unused]] constexpr InRubyGuard::InRubyGuard(InRuby &inRuby) : _inRuby(&inRuby) {
|
47
|
+
if (_inRuby != nullptr) ++_inRuby->_use_count;
|
48
|
+
}
|
49
|
+
constexpr InRubyGuard::~InRubyGuard() {
|
50
|
+
if (_inRuby != nullptr) --_inRuby->_use_count;
|
51
|
+
}
|
52
|
+
|
53
|
+
constexpr InRubyGuard::InRubyGuard(InRubyGuard &&other) noexcept {
|
54
|
+
*this = std::move(other);
|
55
|
+
}
|
56
|
+
|
57
|
+
constexpr InRubyGuard &InRubyGuard::operator=(InRubyGuard &&other) noexcept {
|
58
|
+
InRuby *old = std::exchange(_inRuby, other._inRuby);
|
59
|
+
if (_inRuby != nullptr) ++_inRuby->_use_count;
|
60
|
+
if (old != nullptr) --old->_use_count;
|
61
|
+
other._inRuby = nullptr;
|
62
|
+
return *this;
|
63
|
+
}
|
64
|
+
}
|
65
|
+
|
66
|
+
namespace RuBLE {
|
67
|
+
namespace Utils {
|
68
|
+
namespace RubyContext = Ruby::Context;
|
69
|
+
}
|
70
|
+
namespace RubyContext = Utils::RubyContext;
|
71
|
+
using RubyContext::InRuby;
|
72
|
+
extern thread_local InRuby in_ruby;
|
73
|
+
}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
inherit_from:
|
2
|
+
- ../../../.rubocop.yml
|
3
|
+
|
4
|
+
AllCops:
|
5
|
+
include:
|
6
|
+
- '**/*.rb'
|
7
|
+
ActiveSupportExtensionsEnabled: true
|
8
|
+
|
9
|
+
Metrics/CyclomaticComplexity:
|
10
|
+
Enabled: false
|
11
|
+
|
12
|
+
Metrics/PerceivedComplexity:
|
13
|
+
Enabled: false
|
14
|
+
|
15
|
+
Metrics/ClassLength:
|
16
|
+
Enabled: false
|
17
|
+
|
18
|
+
Style/RescueModifier:
|
19
|
+
Enabled: false
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuBLE
|
4
|
+
module Build
|
5
|
+
class Boost < GithubRepo
|
6
|
+
GITHUB_REPO = 'boostorg/boost'
|
7
|
+
OS_ARCHIVE_EXT = {
|
8
|
+
# ** = TODO: confirm standard tools on this OS support these
|
9
|
+
/\Alinux/ => 'tar.xz',
|
10
|
+
/\A(macos|darwin)/ => 'tar.xz', # **
|
11
|
+
/\A(ms)?win(32|64|dows)/ => '7z', # **
|
12
|
+
/\Amingw|msys/ => 'tar.gz', # **
|
13
|
+
:fallback => 'tar.gz', # **
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
memoize def asset_extension
|
17
|
+
OS_ARCHIVE_EXT.detect do |_k, v|
|
18
|
+
OS.host_os === v # rubocop:disable Style/CaseEquality
|
19
|
+
end&.first || OS_ARCHIVE_EXT.fetch(:fallback)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Static makes sense for distribution if we can do it
|
23
|
+
memoize def asset_filename = "#{release_name}.#{asset_extension}".freeze
|
24
|
+
memoize def matching_asset = assets.fetch(asset_filename).freeze
|
25
|
+
memoize def extract_src_path = '[UNUSED]'
|
26
|
+
|
27
|
+
memoize def config_data
|
28
|
+
super.dup.tap do |data|
|
29
|
+
data[:archive_path] = extract_src_path
|
30
|
+
end.freeze
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'debug'
|
4
|
+
module RuBLE
|
5
|
+
module Build
|
6
|
+
module CMake
|
7
|
+
CMAKE_LITERALS = {
|
8
|
+
nil => 'NULL',
|
9
|
+
true => 'ON',
|
10
|
+
false => 'OFF',
|
11
|
+
}.freeze
|
12
|
+
CMAKE_KEY_SEPARATOR = '_'
|
13
|
+
STRINGY_TYPES = [ String, Symbol, Pathname ].freeze
|
14
|
+
|
15
|
+
def cmake_generate(data, path: nil)
|
16
|
+
enum = cmake_serialized_enumerator(data)
|
17
|
+
return enum.reduce(String.new) do |memo, obj|
|
18
|
+
memo << "\n" unless memo.empty?
|
19
|
+
memo << obj
|
20
|
+
end unless path
|
21
|
+
|
22
|
+
path = Pathname.new(path)
|
23
|
+
path.open(File::WRONLY|File::CREAT) do |f|
|
24
|
+
# f.sync = true
|
25
|
+
enum.each { |field| f.puts(field) }
|
26
|
+
end
|
27
|
+
path
|
28
|
+
end
|
29
|
+
|
30
|
+
def cmake_serialize(value)
|
31
|
+
if value.is_a?(Array)
|
32
|
+
array = cmake_serialize_array(value)
|
33
|
+
array.map { cmake_escape(_1, quote: true) }.join(' ')
|
34
|
+
elsif value.is_any_of?(*STRINGY_TYPES)
|
35
|
+
cmake_escape(value.to_s)
|
36
|
+
elsif CMAKE_LITERALS.key?(value)
|
37
|
+
CMAKE_LITERALS[value]
|
38
|
+
elsif value.is_a?(Numeric)
|
39
|
+
value.to_s # no escaping necessary
|
40
|
+
else
|
41
|
+
raise ArgumentError, "Unrecognized value #{value.inspect}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def cmake_serializable?(value)
|
46
|
+
value.is_any_of?(Array, Hash, *STRINGY_TYPES, Numeric) || CMAKE_LITERALS.key?(value)
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def cmake_escape(str, quote: true)
|
52
|
+
result = str.gsub(/[\\"$]/) do |match|
|
53
|
+
%{\\#{match[0]}}
|
54
|
+
end
|
55
|
+
quote ? %{"#{result}"} : result
|
56
|
+
end
|
57
|
+
|
58
|
+
def cmake_serialized_enumerator(data)
|
59
|
+
cmake_flatten_hash(data).lazy.map do |k, v|
|
60
|
+
%{set(#{k} #{cmake_serialize(v)})}
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def cmake_serialize_array(array)
|
65
|
+
elements = array.compact
|
66
|
+
unless elements.all? { _1.is_any_of?(*STRINGY_TYPES, Numeric) }
|
67
|
+
debugger if defined?(debugger)
|
68
|
+
raise ArgumentError, 'Array must contain only String-y elements'
|
69
|
+
end
|
70
|
+
elements
|
71
|
+
end
|
72
|
+
|
73
|
+
def cmake_flatten_hash(hash)
|
74
|
+
final = {}
|
75
|
+
hash = hash.dup if hash.frozen?
|
76
|
+
until hash.empty?
|
77
|
+
hash.transform_keys!(&:to_s)
|
78
|
+
|
79
|
+
# Join arrays and make stringy values `String`s
|
80
|
+
hash.transform_values! do |value|
|
81
|
+
next value unless value.is_any_of?(Array, *STRINGY_TYPES)
|
82
|
+
next value.to_s unless value.is_a?(Array) # if stringy, just make it a string
|
83
|
+
|
84
|
+
value.map(&:to_s)
|
85
|
+
end
|
86
|
+
hash.select { _2.is_a?(Array) }.keys.each do |k|
|
87
|
+
if final.key?(k)
|
88
|
+
warn "Duplicate hash key #{k.inspect}. Ignoring new value"
|
89
|
+
next
|
90
|
+
end
|
91
|
+
final[k] = hash.delete(k)
|
92
|
+
end
|
93
|
+
|
94
|
+
unrecognized_keys = hash.reject { |_k, v| cmake_serializable?(v) }.keys
|
95
|
+
if unrecognized_keys.any?
|
96
|
+
raise ArgumentError, "Unrecognized values in hash at keys #{unrecognized_keys}"
|
97
|
+
end
|
98
|
+
|
99
|
+
# Flatten hashes
|
100
|
+
hash_keys = hash.select { |_k, v| v.is_a?(Hash) }.keys
|
101
|
+
hash_keys.each do |k|
|
102
|
+
subhash = hash.delete(k)
|
103
|
+
subhash = subhash.dup if subhash.frozen?
|
104
|
+
|
105
|
+
flat_subhash = cmake_flatten_hash(subhash)
|
106
|
+
flat_subhash.transform_keys! { [k, _1].join(CMAKE_KEY_SEPARATOR) }
|
107
|
+
hash.merge!(flat_subhash) do |k, old_val, _new_val|
|
108
|
+
warn "Duplicate hash key #{k.inspect}. Ignoring new value"
|
109
|
+
|
110
|
+
old_val
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Move completed keys into `final` hash
|
115
|
+
completed_keys = hash.select do |_k, v|
|
116
|
+
v.is_any_of?(String, Numeric) || CMAKE_LITERALS.include?(v)
|
117
|
+
end.keys
|
118
|
+
|
119
|
+
completed_keys.each do |k|
|
120
|
+
v = hash.delete(k)
|
121
|
+
if final.key?(k)
|
122
|
+
warn "Duplicate hash key #{k.inspect}. Ignoring new value"
|
123
|
+
next
|
124
|
+
end
|
125
|
+
|
126
|
+
final[k] = v
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
final
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuBLE
|
4
|
+
module Build
|
5
|
+
module Data
|
6
|
+
module Bundler
|
7
|
+
# Based on https://www.rubydoc.info/gems/bundler/Bundler%2FEnv%2Eenvironment
|
8
|
+
include Memery
|
9
|
+
|
10
|
+
memoize def bundler_data
|
11
|
+
{
|
12
|
+
platform: Gem.platforms.map(&:to_s),
|
13
|
+
config_dir: Pathname.new(Gem::ConfigFile::SYSTEM_WIDE_CONFIG_FILE).dirname,
|
14
|
+
rubygems_version: Gem::VERSION,
|
15
|
+
gem_home: Pathname.new(Gem.dir),
|
16
|
+
gem_path: Gem.path.join(File::PATH_SEPARATOR),
|
17
|
+
user_home: Pathname.new(Gem.user_dir),
|
18
|
+
bindir: Pathname.new(Gem.bindir),
|
19
|
+
}.freeze
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|