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.
Files changed (92) hide show
  1. checksums.yaml +7 -0
  2. data/.gdbinit +21 -0
  3. data/.gitignore +18 -0
  4. data/.rubocop.yml +96 -0
  5. data/.ruby-version +1 -0
  6. data/CHANGELOG.md +6 -0
  7. data/CMakeLists.txt +4 -0
  8. data/CODE_OF_CONDUCT.md +84 -0
  9. data/Gemfile +21 -0
  10. data/Gemfile.lock +98 -0
  11. data/LICENSE.txt +21 -0
  12. data/README.md +63 -0
  13. data/Rakefile +41 -0
  14. data/ext/ruble/.gitignore +5 -0
  15. data/ext/ruble/CMakeLists.txt +157 -0
  16. data/ext/ruble/RuBLEHelpers.cmake +240 -0
  17. data/ext/ruble/bindings/Adapter.cpp +193 -0
  18. data/ext/ruble/bindings/Adapter.hpp +85 -0
  19. data/ext/ruble/bindings/Characteristic.cpp +171 -0
  20. data/ext/ruble/bindings/Characteristic.hpp +132 -0
  21. data/ext/ruble/bindings/Descriptor.cpp +34 -0
  22. data/ext/ruble/bindings/Descriptor.hpp +69 -0
  23. data/ext/ruble/bindings/Peripheral.cpp +212 -0
  24. data/ext/ruble/bindings/Peripheral.hpp +108 -0
  25. data/ext/ruble/bindings/RuBLE.cpp +115 -0
  26. data/ext/ruble/bindings/Service.cpp +112 -0
  27. data/ext/ruble/bindings/Service.hpp +61 -0
  28. data/ext/ruble/bindings/common.hpp +48 -0
  29. data/ext/ruble/bindings/globals.cpp +43 -0
  30. data/ext/ruble/cmake.mk +62 -0
  31. data/ext/ruble/concerns/CharacteristicValueTracker.cpp +18 -0
  32. data/ext/ruble/concerns/CharacteristicValueTracker.hpp +40 -0
  33. data/ext/ruble/concerns/Rubyable.cpp +4 -0
  34. data/ext/ruble/concerns/Rubyable.hpp +46 -0
  35. data/ext/ruble/config.h.in +25 -0
  36. data/ext/ruble/containers/ByteArray.cpp +64 -0
  37. data/ext/ruble/containers/ByteArray.hpp +161 -0
  38. data/ext/ruble/containers/Callback.hpp +52 -0
  39. data/ext/ruble/containers/NamedBitSet.hpp +140 -0
  40. data/ext/ruble/containers/NamedBitSet.ipp +71 -0
  41. data/ext/ruble/extconf.rb +30 -0
  42. data/ext/ruble/management/Registry.cpp +63 -0
  43. data/ext/ruble/management/Registry.hpp +170 -0
  44. data/ext/ruble/management/RegistryFactory.hpp +113 -0
  45. data/ext/ruble/management/RubyQueue.cpp +152 -0
  46. data/ext/ruble/management/RubyQueue.hpp +69 -0
  47. data/ext/ruble/modularize.diff +28 -0
  48. data/ext/ruble/types/SimpleBLE.hpp +21 -0
  49. data/ext/ruble/types/declarations.hpp +91 -0
  50. data/ext/ruble/types/helpers.hpp +12 -0
  51. data/ext/ruble/types/ruby.hpp +36 -0
  52. data/ext/ruble/types/stl.hpp +41 -0
  53. data/ext/ruble/utils/RubyCallbackTraits.cpp +28 -0
  54. data/ext/ruble/utils/RubyCallbackTraits.hpp +48 -0
  55. data/ext/ruble/utils/async.cpp +10 -0
  56. data/ext/ruble/utils/async.hpp +76 -0
  57. data/ext/ruble/utils/containers.hpp +41 -0
  58. data/ext/ruble/utils/exception_handling.cpp +50 -0
  59. data/ext/ruble/utils/exception_handling.hpp +53 -0
  60. data/ext/ruble/utils/garbage_collection.cpp +82 -0
  61. data/ext/ruble/utils/garbage_collection.hpp +22 -0
  62. data/ext/ruble/utils/hash.cpp +83 -0
  63. data/ext/ruble/utils/hash.hpp +52 -0
  64. data/ext/ruble/utils/hexadecimal.hpp +116 -0
  65. data/ext/ruble/utils/human_type_names.hpp +38 -0
  66. data/ext/ruble/utils/inspection.cpp +24 -0
  67. data/ext/ruble/utils/inspection.hpp +108 -0
  68. data/ext/ruble/utils/ruby.hpp +103 -0
  69. data/ext/ruble/utils/ruby_context.hpp +73 -0
  70. data/lib/ruble/build/.rubocop.yml +19 -0
  71. data/lib/ruble/build/boost.rb +34 -0
  72. data/lib/ruble/build/cmake.rb +134 -0
  73. data/lib/ruble/build/core_ext.rb +5 -0
  74. data/lib/ruble/build/data/bundler.rb +24 -0
  75. data/lib/ruble/build/data/extension.rb +101 -0
  76. data/lib/ruble/build/data/os.rb +21 -0
  77. data/lib/ruble/build/data/rice.rb +24 -0
  78. data/lib/ruble/build/data.rb +22 -0
  79. data/lib/ruble/build/extconf.rb +76 -0
  80. data/lib/ruble/build/github_repo.rb +129 -0
  81. data/lib/ruble/build/simpleble.rb +56 -0
  82. data/lib/ruble/build.rb +28 -0
  83. data/lib/ruble/version.rb +7 -0
  84. data/lib/ruble.rb +46 -0
  85. data/lib/tasks/dev/dev_tasks.rb +130 -0
  86. data/lib/tasks/dev/pager.rb +218 -0
  87. data/lib/tasks/dev/paths.rb +30 -0
  88. data/lib/tasks/dev/state_hash.rb +65 -0
  89. data/lib/tasks/dev.rake +41 -0
  90. data/lib/tasks/simpleble.rake +29 -0
  91. data/sig/rubble.rbs +4 -0
  92. 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> &reg) {
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,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kernel
4
+ def is_any_of?(*klasses) = klasses.any? { is_a?(_1) }
5
+ 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