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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a7c664ec9bf7164bf409808c01388459d95b09eb9612b71c68297d930ea9d350
4
- data.tar.gz: 3a8f12c1501e564f82d9c263cf4945c8bb689c8955b09b5dd9b1ead95358cf45
3
+ metadata.gz: 3d9c342d9489450f8b80b156619a98decb058a71acf2e6a2c36ec4cde5dda441
4
+ data.tar.gz: ac9f4c41b3acf1d0a1aec013f91b6ed08ca8ef43874103d72cca93cbf60214e9
5
5
  SHA512:
6
- metadata.gz: a68f4616208fb9d39417fc8bca4568fd15b39e6b809756c21be58dc127e4109b48a7377b9f64b8a5e149fdc47df0712f4b03ab508c3d4f8e0fa94f0c33ac08b2
7
- data.tar.gz: 38b2296af87d4182f66a2926191c1858fcb5c0c95cb9521f15f7845c69b2a2c3fc79fe8356e8e399e6ce7be23aa88fc8e226a0a72d1b089a58f0354681c6f3fa
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: %i[clobber compile test]
21
+ task default: [:clobber, :compile, :test]
25
22
 
26
- CLEAN.add("{ext,lib}/**/*.{o,so}")
23
+ CLEAN.add("{ext,lib}/**/*.{o,so}", "pkg")
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RCEE
4
4
  module System
5
- VERSION = "0.1.0"
5
+ VERSION = "0.4.0"
6
6
  end
7
7
  end
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.1.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: 2021-09-02 00:00:00.000000000 Z
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.2.15
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.