engine-hacks 0.2.0.pre1
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 +7 -0
- data/.rspec +3 -0
- data/Gemfile +12 -0
- data/README.md +29 -0
- data/Rakefile +16 -0
- data/engine-hacks.gemspec +44 -0
- data/ext/engine_hacks/engine_hacks.c +64 -0
- data/ext/engine_hacks/extconf.rb +3 -0
- data/lib/engine-hacks/cruby.rb +27 -0
- data/lib/engine-hacks/jruby.rb +28 -0
- data/lib/engine-hacks/version.rb +5 -0
- data/lib/engine-hacks.rb +8 -0
- data/sig/engine-hacks.rbs +15 -0
- metadata +63 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 95e4571cc6ce855e88da5f20d9ffcbbb075eb338843240cbc0de0b853064bacf
|
4
|
+
data.tar.gz: d37295c1c30e192554bbec47cde9484ad9440475b46f5033d4355e778e7f406a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3441f2db1981c5ef615e57b9ca1e856b09fd6d126b979a507a235232f76aec9b195b055b79aae1a6758362ab67e5ad5bb72ba8a01268f2a4730fea6e6733090d
|
7
|
+
data.tar.gz: 415660b5d73eeadcee378092e9f8902bf6cfaa35d7b13c9da36e48aa858d18a67dfcca906ececf29a98e681282a91c6150d55ecd689cc5fd9905832c712d052a
|
data/.rspec
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# EngineHacks Library
|
2
|
+
|
3
|
+
Ruby Engine-specific extensions to enable SubSpawn.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
For most users, install one the other SubSpawn gems directly instead.
|
8
|
+
|
9
|
+
Otherwise, install the gem and add to the application's Gemfile by executing:
|
10
|
+
|
11
|
+
$ bundle add engine-hacks
|
12
|
+
|
13
|
+
If bundler is not being used to manage dependencies, install the gem by executing:
|
14
|
+
|
15
|
+
$ gem install engine-hacks
|
16
|
+
|
17
|
+
## Usage
|
18
|
+
|
19
|
+
For most users, use one of the other SubSpawn API's directly.
|
20
|
+
|
21
|
+
```rb
|
22
|
+
require 'engine-hacks'
|
23
|
+
```
|
24
|
+
|
25
|
+
An RBS file exists for this gem.
|
26
|
+
|
27
|
+
## Development
|
28
|
+
|
29
|
+
See parent SubSpawn readme
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bundler/gem_tasks"
|
4
|
+
require "rspec/core/rake_task"
|
5
|
+
|
6
|
+
RSpec::Core::RakeTask.new(:spec)
|
7
|
+
|
8
|
+
|
9
|
+
require 'rake/extensiontask'
|
10
|
+
|
11
|
+
Rake::ExtensionTask.new "engine_hacks" do |ext|
|
12
|
+
ext.lib_dir = "lib/engine-hacks"
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
task default: :build
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/engine-hacks/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "engine-hacks"
|
7
|
+
spec.version = EngineHacks::VERSION
|
8
|
+
spec.authors = ["Patrick Plenefisch"]
|
9
|
+
spec.email = ["simonpatp@gmail.com"]
|
10
|
+
|
11
|
+
spec.summary = "Engine-specific hacks to enable implement spawn in Ruby"
|
12
|
+
spec.description = "A SubSpawn subproject to provide c/java extensions to modify non-ruby-modifiable classes necessary to implementing SubSpawn, or any other spawn/popen API in pure Ruby"
|
13
|
+
final_github = "https://github.com/byteit101/subspawn"
|
14
|
+
spec.homepage = final_github
|
15
|
+
spec.required_ruby_version = ">= 2.6.0"
|
16
|
+
|
17
|
+
#spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'"
|
18
|
+
|
19
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
20
|
+
spec.metadata["source_code_uri"] = final_github
|
21
|
+
spec.metadata["changelog_uri"] = final_github
|
22
|
+
|
23
|
+
# Specify which files should be added to the gem when it is released.
|
24
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
25
|
+
spec.files = Dir.chdir(__dir__) do
|
26
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
27
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
28
|
+
end
|
29
|
+
end
|
30
|
+
spec.require_paths = ["lib"]
|
31
|
+
|
32
|
+
if RUBY_PLATFORM =~ /java/
|
33
|
+
spec.platform = "java"
|
34
|
+
else
|
35
|
+
spec.platform = Gem::Platform::RUBY
|
36
|
+
spec.extensions = ["ext/engine_hacks/extconf.rb"]
|
37
|
+
end
|
38
|
+
|
39
|
+
# For more information and examples about making a new gem, check out our
|
40
|
+
# guide at: https://bundler.io/guides/creating_gem.html
|
41
|
+
|
42
|
+
# You can use Ruby's license, or any of the JRuby tri-license options
|
43
|
+
spec.licenses = ["Ruby", "EPL-2.0", "LGPL-2.1-or-later"]
|
44
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
#include "ruby/ruby.h"
|
2
|
+
#include "ruby/io.h"
|
3
|
+
#include "ruby/intern.h"
|
4
|
+
|
5
|
+
// global to store the thread local name to read for $? and $CHILD_STATUS
|
6
|
+
static VALUE status_holder;
|
7
|
+
|
8
|
+
|
9
|
+
// $? accessor. returns `Thread.current[status_holder[0]]`
|
10
|
+
static VALUE
|
11
|
+
get_CHILD_STATUS(ID _x, VALUE *_y)
|
12
|
+
{
|
13
|
+
return rb_thread_local_aref(rb_thread_current(), SYM2ID(RARRAY_AREF(status_holder, 0)));
|
14
|
+
}
|
15
|
+
|
16
|
+
// overwrites old $? definition to our defintion, and save the new status thread local name
|
17
|
+
static VALUE
|
18
|
+
install_status(VALUE self, VALUE new_status)
|
19
|
+
{
|
20
|
+
rb_define_virtual_variable("$?", get_CHILD_STATUS, 0);
|
21
|
+
rb_define_virtual_variable("$CHILD_STATUS", get_CHILD_STATUS, 0);
|
22
|
+
|
23
|
+
// reset, and save the new state
|
24
|
+
rb_ary_clear(status_holder);
|
25
|
+
new_status = rb_to_symbol(new_status);
|
26
|
+
rb_ary_push(status_holder, new_status);
|
27
|
+
|
28
|
+
// return the name, converted
|
29
|
+
return new_status;
|
30
|
+
}
|
31
|
+
|
32
|
+
static VALUE
|
33
|
+
join_pipes(VALUE self, VALUE reader, VALUE writer)
|
34
|
+
{
|
35
|
+
rb_io_set_write_io(reader, writer);
|
36
|
+
|
37
|
+
// ensure flags are correct
|
38
|
+
RFILE(writer)->fptr->mode |= FMODE_SYNC|FMODE_DUPLEX;
|
39
|
+
RFILE(reader)->fptr->mode |= FMODE_SYNC|FMODE_DUPLEX;
|
40
|
+
RFILE(writer)->fptr->mode &= ~FMODE_READABLE;
|
41
|
+
RFILE(reader)->fptr->mode &= ~FMODE_WRITABLE;
|
42
|
+
|
43
|
+
// not functional, but what IO.popen does as API
|
44
|
+
rb_ivar_set(reader, rb_intern("@tied_io_for_writing"), writer);
|
45
|
+
return reader;
|
46
|
+
|
47
|
+
// TODO: we may need to worry that MRI io.c sets pipe_finalize?
|
48
|
+
}
|
49
|
+
|
50
|
+
void Init_engine_hacks(void)
|
51
|
+
{
|
52
|
+
VALUE EngineHacks = rb_define_module("EngineHacks");
|
53
|
+
VALUE EngineHacksMRI = rb_define_module_under(EngineHacks, "MRI");
|
54
|
+
|
55
|
+
// hold the status in an array, so we can mark the array, and avoid fiddling with making
|
56
|
+
// our own marking stuff for the GC. if we don't mark as gc, SEGV
|
57
|
+
status_holder = rb_ary_new();
|
58
|
+
rb_global_variable(&status_holder);
|
59
|
+
|
60
|
+
// Exposed methods. Unsafe, do not call these directly, use the cruby.rb wrapper functions only!
|
61
|
+
rb_define_singleton_method(EngineHacksMRI, "install_status!", install_status, 1);
|
62
|
+
rb_define_singleton_method(EngineHacksMRI, "join_io", join_pipes, 2);
|
63
|
+
|
64
|
+
}
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'engine_hacks/engine_hacks'
|
2
|
+
require 'English'
|
3
|
+
|
4
|
+
# TODO: this doesn't actually work
|
5
|
+
#alias $BUILTIN_CHILD_STATUS $?
|
6
|
+
|
7
|
+
module EngineHacks
|
8
|
+
|
9
|
+
def self.use_child_status symbol
|
10
|
+
@symbol = symbol.to_sym
|
11
|
+
MRI.install_status! symbol
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.child_status= value
|
15
|
+
if defined? @symbol
|
16
|
+
Thread.current[@symbol] = value
|
17
|
+
else
|
18
|
+
nil # not installed yet
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.duplex_io(read, write)
|
23
|
+
raise ArgumentError.new("Read argument must be IO") unless read.is_a? IO
|
24
|
+
raise ArgumentError.new("Write argument must be IO") unless write.is_a? IO
|
25
|
+
MRI.join_io(read, write)
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
|
2
|
+
module EngineHacks
|
3
|
+
|
4
|
+
def self.use_child_status symbol
|
5
|
+
# No-op on JRuby
|
6
|
+
nil
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.child_status= status
|
10
|
+
require 'jruby'
|
11
|
+
JRuby.runtime.current_context.last_exit_status = status
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.duplex_io(read, write)
|
15
|
+
raise ArgumentError.new("Read argument must be IO") unless read.is_a? IO
|
16
|
+
raise ArgumentError.new("Write argument must be IO") unless write.is_a? IO
|
17
|
+
require 'jruby'
|
18
|
+
readf = JRuby.ref(read).open_file
|
19
|
+
writef = JRuby.ref(read).open_file
|
20
|
+
raise ArgumentError.new("Read argument must be JRuby IO") if readf.nil?
|
21
|
+
raise ArgumentError.new("Write argument must be JRuby IO") if writef.nil?
|
22
|
+
readf.tied_io_for_writing = write
|
23
|
+
modes = writef.class
|
24
|
+
readf.mode = (readf.mode & ~modes::WRITABLE) | modes::SYNC | modes::DUPLEX
|
25
|
+
writef.mode = (writef.mode & ~modes::READABLE) | modes::SYNC | modes::DUPLEX
|
26
|
+
return read
|
27
|
+
end
|
28
|
+
end
|
data/lib/engine-hacks.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
module EngineHacks
|
2
|
+
# Makes $? use the thread-local variable named
|
3
|
+
# Returns nil if thread-local hack not necessary, the
|
4
|
+
# Symbol otherwise
|
5
|
+
def self.use_child_status: (String | Symbol) -> ?Symbol
|
6
|
+
|
7
|
+
# Sets $? to the given object. returns nil if use_child_status hasn't
|
8
|
+
# been called yet
|
9
|
+
def self.child_status=: (Object) -> ?Object
|
10
|
+
|
11
|
+
# Returns an IO that may be either of the input arguments or a new
|
12
|
+
# IO that is a duplex IO where .read calls read.read, and .write calls
|
13
|
+
# write.write, etc.
|
14
|
+
def self.duplex_io: (IO read, IO write) -> IO
|
15
|
+
end
|
metadata
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: engine-hacks
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0.pre1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Patrick Plenefisch
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-05-01 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: A SubSpawn subproject to provide c/java extensions to modify non-ruby-modifiable
|
14
|
+
classes necessary to implementing SubSpawn, or any other spawn/popen API in pure
|
15
|
+
Ruby
|
16
|
+
email:
|
17
|
+
- simonpatp@gmail.com
|
18
|
+
executables: []
|
19
|
+
extensions:
|
20
|
+
- ext/engine_hacks/extconf.rb
|
21
|
+
extra_rdoc_files: []
|
22
|
+
files:
|
23
|
+
- ".rspec"
|
24
|
+
- Gemfile
|
25
|
+
- README.md
|
26
|
+
- Rakefile
|
27
|
+
- engine-hacks.gemspec
|
28
|
+
- ext/engine_hacks/engine_hacks.c
|
29
|
+
- ext/engine_hacks/extconf.rb
|
30
|
+
- lib/engine-hacks.rb
|
31
|
+
- lib/engine-hacks/cruby.rb
|
32
|
+
- lib/engine-hacks/jruby.rb
|
33
|
+
- lib/engine-hacks/version.rb
|
34
|
+
- sig/engine-hacks.rbs
|
35
|
+
homepage: https://github.com/byteit101/subspawn
|
36
|
+
licenses:
|
37
|
+
- Ruby
|
38
|
+
- EPL-2.0
|
39
|
+
- LGPL-2.1-or-later
|
40
|
+
metadata:
|
41
|
+
homepage_uri: https://github.com/byteit101/subspawn
|
42
|
+
source_code_uri: https://github.com/byteit101/subspawn
|
43
|
+
changelog_uri: https://github.com/byteit101/subspawn
|
44
|
+
post_install_message:
|
45
|
+
rdoc_options: []
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 2.6.0
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
requirements: []
|
59
|
+
rubygems_version: 3.5.3
|
60
|
+
signing_key:
|
61
|
+
specification_version: 4
|
62
|
+
summary: Engine-specific hacks to enable implement spawn in Ruby
|
63
|
+
test_files: []
|