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