rutie 0.0.3 → 0.0.5

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: 6fa10034548299f63ef9b9b712473a3f310d03dfa94584dc0ed019e17e1f1eb2
4
- data.tar.gz: c0669fe21c5199593c9f91722d3806f237b706cdf934fd15fb8d05040c2ea0b1
3
+ metadata.gz: 1b64238b8adf58283d6b5f5af059aa5c55fc639b511d028cfe1acbf1ec60f34d
4
+ data.tar.gz: baa343484d7ee30aa776bd1b964c9da48f3be00bd3087e92616d9cdab81ab88b
5
5
  SHA512:
6
- metadata.gz: 0224ea3c295ad89c77e745bd930bd6c45b5bff6b7f1e5d06eb8605509afcb07b5ab9a2f4856f2dbb053996666c056abe52a6c75dc0097de5d81a34617626e8a3
7
- data.tar.gz: 5735ae6207958315e76799c5edf535b7f25b7b939efbf625b268a5b81de861da6d2017c3c4195e19e9acc288ce05f568418b59bade7ee9c1256b2206ff34d9dd
6
+ metadata.gz: b39f0e34b4481f8465dac15e91560f91a1911eaeb296755197eb1d21cd2410e5e536bccaf656773961ecfb1213d11894674440fca36eb87de967995a59c754cc
7
+ data.tar.gz: d2697285b96743651f9298346fe20766be9cb1715d61b34d835d607684e2f029046995273e8bf4f0f3eb99574a8da51d68aaf2e271636ff420ef36c864c6bc82
@@ -0,0 +1,58 @@
1
+ require 'rake'
2
+ require 'rbconfig'
3
+
4
+ class Rutie
5
+ # Rake tasks that build a Rutie extension so `ruby` can load it:
6
+ #
7
+ # # Rakefile
8
+ # require 'rutie/rake_task'
9
+ # Rutie::RakeTask.new
10
+ #
11
+ # Rake::TestTask.new(test: 'rutie:build') { |t| ... }
12
+ #
13
+ # `rutie:build` runs `cargo build --release`. On macOS it builds with
14
+ # `NO_LINK_RUTIE=1` and `-undefined dynamic_lookup` instead, so the extension
15
+ # uses the libruby of the `ruby` process loading it rather than linking its
16
+ # own (with a static Ruby that would be a second VM that never booted).
17
+ # `rutie:clean` runs `cargo clean`.
18
+ class RakeTask
19
+ include Rake::DSL
20
+
21
+ # `release: false` builds the debug profile, for `Rutie.new(..., release: 'debug')`.
22
+ # `cargo_args` are extra arguments for cargo, such as `%w[--features foo]`.
23
+ def initialize(release: true, cargo_args: [], os: nil)
24
+ @release = release
25
+ @cargo_args = cargo_args
26
+ @os = os # for testing purposes
27
+
28
+ define_tasks
29
+ end
30
+
31
+ # The environment and command `rutie:build` runs.
32
+ def build_command
33
+ profile = @release ? ['--release'] : []
34
+
35
+ if macos?
36
+ [{ 'NO_LINK_RUTIE' => '1' },
37
+ 'cargo', 'rustc', *profile, *@cargo_args, '--', '-C', 'link-args=-Wl,-undefined,dynamic_lookup']
38
+ else
39
+ [{}, 'cargo', 'build', *profile, *@cargo_args]
40
+ end
41
+ end
42
+
43
+ private
44
+ def define_tasks
45
+ namespace :rutie do
46
+ desc 'Build the Rust extension'
47
+ task(:build) { sh(*build_command) }
48
+
49
+ desc 'Remove the Rust build output (cargo clean)'
50
+ task(:clean) { sh 'cargo', 'clean' }
51
+ end
52
+ end
53
+
54
+ def macos?
55
+ (@os || RbConfig::CONFIG['host_os']) =~ /darwin|mac os/
56
+ end
57
+ end
58
+ end
data/lib/rutie.rb CHANGED
@@ -1,4 +1,11 @@
1
1
  class Rutie
2
+ # Raised by #init when the compiled Rust library is not where Rutie looks for
3
+ # it. A LoadError, so code that rescues LoadError keeps working.
4
+ class LibraryNotFound < LoadError; end
5
+
6
+ # Raised by #init when the library has no function with the init name.
7
+ class InitFunctionNotFound < LoadError; end
8
+
2
9
  def initialize(project_name, **opts)
3
10
  @os = opts.fetch(:os) { nil } # for testing purposes
4
11
 
@@ -9,23 +16,68 @@ class Rutie
9
16
  @full_lib_path = opts.fetch(:lib_path) { nil }
10
17
  end
11
18
 
19
+ # The path of the compiled library. With no `lib_path` option, Rutie looks in
20
+ # `$CARGO_TARGET_DIR/<release>` when that variable is set, then in
21
+ # `../target/<release>` relative to `dir`, and returns the first that holds
22
+ # the library (or the first candidate when none does).
12
23
  def ffi_library(dir)
13
- file = [ @lib_prefix, @project_name, '.', @lib_suffix ]
24
+ candidates = library_candidates(dir)
14
25
 
15
- File.join(lib_path(dir), file.join())
26
+ candidates.find { |path| File.exist?(path) } || candidates.first
16
27
  end
17
28
 
18
- def init(c_init_method_name, dir)
29
+ # Loads the compiled Rust library and calls its init function, which defaults
30
+ # to `Init_<project_name>`:
31
+ #
32
+ # Rutie.new(:my_ext).init __dir__
33
+ # Rutie.new(:my_ext).init 'Init_my_ext', __dir__
34
+ def init(c_init_method_name = "Init_#{@project_name}", dir)
35
+ library = ffi_library(dir)
36
+ raise LibraryNotFound, library_not_found_message(dir) unless File.exist?(library)
37
+
19
38
  require 'fiddle'
20
39
 
21
- Fiddle::Function.new(Fiddle.dlopen(ffi_library dir)[c_init_method_name], [], Fiddle::TYPE_VOIDP).call
40
+ handle = Fiddle.dlopen(library)
41
+ function =
42
+ begin
43
+ handle[c_init_method_name]
44
+ rescue Fiddle::DLError
45
+ raise InitFunctionNotFound, init_not_found_message(library, c_init_method_name)
46
+ end
47
+
48
+ Fiddle::Function.new(function, [], Fiddle::TYPE_VOIDP).call
22
49
  end
23
50
 
24
51
  private
25
- def lib_path(dir)
26
- path = @full_lib_path || "../target/#{@release}"
52
+ def library_file
53
+ [ @lib_prefix, @project_name, '.', @lib_suffix ].join
54
+ end
55
+
56
+ def library_candidates(dir)
57
+ file = library_file
58
+
59
+ return [File.join(File.expand_path(@full_lib_path, dir), file)] if @full_lib_path
60
+
61
+ candidates = []
62
+ target_dir = ENV['CARGO_TARGET_DIR']
63
+ # Cargo resolves a relative CARGO_TARGET_DIR against the working directory.
64
+ candidates << File.join(File.expand_path(@release, target_dir), file) unless target_dir.nil? || target_dir.empty?
65
+ candidates << File.join(File.expand_path("../target/#{@release}", dir), file)
66
+ candidates.uniq
67
+ end
68
+
69
+ def library_not_found_message(dir)
70
+ profile = @release == 'release' ? ' --release' : ''
71
+
72
+ "Rutie could not find the compiled library for #{@project_name}. " \
73
+ "Looked for: #{library_candidates(dir).join(', ')}. " \
74
+ "Build it with `cargo build#{profile}` (see Rutie::RakeTask), " \
75
+ "or pass `lib_path:` to Rutie.new."
76
+ end
27
77
 
28
- File.expand_path(path, dir)
78
+ def init_not_found_message(library, name)
79
+ "#{library} has no function #{name}. Define it in Rust as " \
80
+ "`#[no_mangle] pub extern \"C\" fn #{name}()`, or pass its name to Rutie#init."
29
81
  end
30
82
 
31
83
  def set_prefix
@@ -66,10 +118,12 @@ class Rutie
66
118
  def to_str
67
119
  @name
68
120
  end
121
+ alias to_s to_str
69
122
 
70
123
  private
124
+ # A Cargo library name: snake_case, and digits after the first character.
71
125
  def valid_name?(project)
72
- project.chars.all? {|c| c[/[a-z_]/] }
126
+ project.match?(/\A[a-z_][a-z0-9_]*\z/)
73
127
  end
74
128
 
75
129
  class InvalidProjectName < StandardError
data/rutie.gemspec CHANGED
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
 
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = 'rutie'
7
- spec.version = '0.0.3'
7
+ spec.version = '0.0.5'
8
8
  spec.authors = ['Daniel P. Clark']
9
9
  spec.email = ['6ftdan@gmail.com']
10
10
  spec.summary = 'Rutie helper methods.'
@@ -12,7 +12,9 @@ Gem::Specification.new do |spec|
12
12
  spec.homepage = 'https://github.com/danielpclark/rutie'
13
13
  spec.license = 'MIT'
14
14
 
15
- spec.files = %w[rutie.gemspec lib/rutie.rb LICENSE]
15
+ spec.files = %w[rutie.gemspec lib/rutie.rb lib/rutie/rake_task.rb LICENSE]
16
16
 
17
- spec.add_dependency 'bundler', '~> 1.16'
17
+ # Rutie#init loads the extension with Fiddle, a bundled gem rather than a
18
+ # default gem since Ruby 4.0, so Bundler needs it declared.
19
+ spec.add_dependency 'fiddle'
18
20
  end
metadata CHANGED
@@ -1,29 +1,28 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rutie
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel P. Clark
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2018-08-03 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
- name: bundler
13
+ name: fiddle
15
14
  requirement: !ruby/object:Gem::Requirement
16
15
  requirements:
17
- - - "~>"
16
+ - - ">="
18
17
  - !ruby/object:Gem::Version
19
- version: '1.16'
18
+ version: '0'
20
19
  type: :runtime
21
20
  prerelease: false
22
21
  version_requirements: !ruby/object:Gem::Requirement
23
22
  requirements:
24
- - - "~>"
23
+ - - ">="
25
24
  - !ruby/object:Gem::Version
26
- version: '1.16'
25
+ version: '0'
27
26
  description: Rutie — The Tie Between Ruby and Rust. Helper methods gem for Rutie.
28
27
  email:
29
28
  - 6ftdan@gmail.com
@@ -33,12 +32,12 @@ extra_rdoc_files: []
33
32
  files:
34
33
  - LICENSE
35
34
  - lib/rutie.rb
35
+ - lib/rutie/rake_task.rb
36
36
  - rutie.gemspec
37
37
  homepage: https://github.com/danielpclark/rutie
38
38
  licenses:
39
39
  - MIT
40
40
  metadata: {}
41
- post_install_message:
42
41
  rdoc_options: []
43
42
  require_paths:
44
43
  - lib
@@ -53,9 +52,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
53
52
  - !ruby/object:Gem::Version
54
53
  version: '0'
55
54
  requirements: []
56
- rubyforge_project:
57
- rubygems_version: 2.7.6
58
- signing_key:
55
+ rubygems_version: 4.0.20
59
56
  specification_version: 4
60
57
  summary: Rutie helper methods.
61
58
  test_files: []