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 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.