rcee_system 0.1.0 → 0.4.0
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 +4 -4
- data/README.md +100 -0
- data/Rakefile +4 -7
- data/lib/rcee/system/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3d9c342d9489450f8b80b156619a98decb058a71acf2e6a2c36ec4cde5dda441
|
4
|
+
data.tar.gz: ac9f4c41b3acf1d0a1aec013f91b6ed08ca8ef43874103d72cca93cbf60214e9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b83a0f8d28252951cd5dfc5b799210791667edaf57e1452cda34d09cb2f5e1b73333152e409492ae8c3f2b2b1b213f83873c97519b30d10392a8b1d1c1ce2c30
|
7
|
+
data.tar.gz: 2f7a57d86c58a7d8e3500f757d73c7115acb764ea417406b100fe5a8d9992d88a3e9846772a9dd65accbbfa1fb445b2027ba2b5509aae322a7d89511a9426c95
|
data/README.md
CHANGED
@@ -1,2 +1,102 @@
|
|
1
|
+
# RCEE::System
|
1
2
|
|
2
3
|
This gem is part of the Ruby C Extensions Explained project at https://github.com/flavorjones/ruby-c-extensions-explained
|
4
|
+
|
5
|
+
## Context
|
6
|
+
|
7
|
+
In the `isolated` gem, I mentioned that one goal of a C extension might be to optimize performance. This is the case for BCrypt.
|
8
|
+
|
9
|
+
But there's another, more common, reason to write a C extension, which is to talk to a third-party library. Many Ruby gems use C extensions solely to integrate with a third-party library. Some examples:
|
10
|
+
|
11
|
+
- nokogiri → libxml2, libxslt, libgumbo
|
12
|
+
- psych → libyaml
|
13
|
+
- sqlite3 → libsqlite3
|
14
|
+
- rmagick → libMagick
|
15
|
+
- grpc → libgrpc
|
16
|
+
|
17
|
+
These gems have a thin-ish wrapper of Ruby and C that work together to make the library's features available as idiomatic Ruby.
|
18
|
+
|
19
|
+
|
20
|
+
## Summary
|
21
|
+
|
22
|
+
This gem, as well as all the following gems, will call `libyaml` as an example third-party integration, and will require that `libyaml` be installed ahead of time on the target system.
|
23
|
+
|
24
|
+
Some real-world gems that use this "system" strategy are ruby-sqlite3 and rmagick.
|
25
|
+
|
26
|
+
|
27
|
+
## Details
|
28
|
+
|
29
|
+
This gem's C code is located in `ext/system/system.c`:
|
30
|
+
|
31
|
+
``` C
|
32
|
+
static VALUE
|
33
|
+
rb_system_extension_class_do_something(VALUE self)
|
34
|
+
{
|
35
|
+
int major, minor, patch;
|
36
|
+
|
37
|
+
yaml_get_version(&major, &minor, &patch);
|
38
|
+
|
39
|
+
return rb_sprintf("libyaml version %d.%d.%d", major, minor, patch);
|
40
|
+
}
|
41
|
+
```
|
42
|
+
|
43
|
+
That's pretty simple, but is enough to demonstrate the integration with `libyaml` works.
|
44
|
+
|
45
|
+
The `extconf.rb` is still simple (and similar to `isolated/ext/isolated/extconf.rb` but contains this additional block:
|
46
|
+
|
47
|
+
``` ruby
|
48
|
+
unless find_header("yaml.h") && find_library("yaml", "yaml_get_version")
|
49
|
+
abort("\nERROR: *** could not find libyaml development environment ***\n\n")
|
50
|
+
end
|
51
|
+
```
|
52
|
+
|
53
|
+
`find_header` and `find_library` are `MakeMakefile` helper methods which will search your system's standard directories looking for files. If it finds them, it makes sure the compile step will be able to find `yaml.h`, and the link step will be able to find the `libyaml` library file.
|
54
|
+
|
55
|
+
We ask `find_header` to look for the `yaml.h` header file because that's what our C code needs (see `ext/system/system.h`). We ask `find_library` to look for a library named `libyaml` and check that it has the function `yaml_get_version()` defined in it.
|
56
|
+
|
57
|
+
(We don't need to call `find_library` for every function we intend to use; we just need to provide one function from the library so that `MakeMakefile` can verify that linking will succeed.)
|
58
|
+
|
59
|
+
If these methods succeed, the `Makefile` recipe looks something like this. Note the include directory is added for the compile step, and the library directory and name are added to the link step.
|
60
|
+
|
61
|
+
``` sh
|
62
|
+
# `create_makefile` recipe is something like this
|
63
|
+
|
64
|
+
# compile phase:
|
65
|
+
gcc -c -I/path/to/ruby/include -I/path/to/libyaml/include system.c -o system.o
|
66
|
+
|
67
|
+
# link phase:
|
68
|
+
gcc -shared \
|
69
|
+
-L/path/to/ruby/lib -lruby \
|
70
|
+
-L/path/to/libyaml/lib -lyaml \
|
71
|
+
-lc -lm \
|
72
|
+
system.o -o system.so
|
73
|
+
```
|
74
|
+
|
75
|
+
If you run `ldd` on the generated `system.so` you should see `libyaml` listed, something like:
|
76
|
+
|
77
|
+
``` text
|
78
|
+
libyaml-0.so.2 => /usr/lib/x86_64-linux-gnu/libyaml-0.so.2 (0x00007f345a3dc000)
|
79
|
+
```
|
80
|
+
|
81
|
+
|
82
|
+
## Testing
|
83
|
+
|
84
|
+
See [.github/workflows/system.yml](../.github/workflows/system.yml)
|
85
|
+
|
86
|
+
Key things to note:
|
87
|
+
|
88
|
+
- matrix across all supported Rubies and platforms
|
89
|
+
- use the github action `MSP-Greg/setup-ruby-pkgs@v1` to install system libraries on each platform
|
90
|
+
|
91
|
+
|
92
|
+
## What Can Go Wrong
|
93
|
+
|
94
|
+
In addition to what's enumerated in `isolated`'s README ...
|
95
|
+
|
96
|
+
If `MakeMakefile` methods fail to find the third-party library (or fail to compile and link against it), then the user will see an error message, and have to go figure out how to install `libyaml` on their system.
|
97
|
+
|
98
|
+
If the third-party library is installed into non-standard directories by the package manager, your `extconf.rb` may need special logic. `rmagick` needs to do a lot of this.
|
99
|
+
|
100
|
+
If the third-party library has compile-time flags to control whether features are turned on or off, then your `extconf.rb` may need to test for that with `have_func` and your C code will need to handle the case where those methods aren't implemented.
|
101
|
+
|
102
|
+
The version of the third-party library may be older or newer than you expected, and either contain bugs or be missing new features, which also require additional code complexity.
|
data/Rakefile
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "bundler/gem_tasks"
|
4
|
-
require "rake/testtask"
|
5
4
|
require "rubygems/package_task"
|
5
|
+
require "rake/testtask"
|
6
|
+
require "rake/extensiontask"
|
6
7
|
|
7
8
|
rcee_system_spec = Bundler.load_gemspec("rcee_system.gemspec")
|
8
9
|
Gem::PackageTask.new(rcee_system_spec).define
|
@@ -13,14 +14,10 @@ Rake::TestTask.new(:test) do |t|
|
|
13
14
|
t.test_files = FileList["test/**/*_test.rb"]
|
14
15
|
end
|
15
16
|
|
16
|
-
require "rake/extensiontask"
|
17
|
-
|
18
|
-
task build: :compile
|
19
|
-
|
20
17
|
Rake::ExtensionTask.new("system") do |ext|
|
21
18
|
ext.lib_dir = "lib/rcee/system"
|
22
19
|
end
|
23
20
|
|
24
|
-
task default:
|
21
|
+
task default: [:clobber, :compile, :test]
|
25
22
|
|
26
|
-
CLEAN.add("{ext,lib}/**/*.{o,so}")
|
23
|
+
CLEAN.add("{ext,lib}/**/*.{o,so}", "pkg")
|
data/lib/rcee/system/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rcee_system
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Dalessio
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-05-19 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Part of a project to explain how Ruby C extensions work.
|
14
14
|
email:
|
@@ -47,7 +47,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
47
47
|
- !ruby/object:Gem::Version
|
48
48
|
version: '0'
|
49
49
|
requirements: []
|
50
|
-
rubygems_version: 3.
|
50
|
+
rubygems_version: 3.3.5
|
51
51
|
signing_key:
|
52
52
|
specification_version: 4
|
53
53
|
summary: Example gem demonstrating a basic C extension.
|