ruble 0.0.3.alpha

Sign up to get free protection for your applications and to get access to all the features.
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