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