Skip to content

Commit

Permalink
Only require FFI on Windows
Browse files Browse the repository at this point in the history
This allows users to trade off between full-featured and
always-deployable by removing the hard dependency on a native extension.
The user of the gem needs to depend on FFI themselves to enable
`posix_spawn` on Linux.

For testing purposes, the Gemfile loads ffi if required by the environment.
  • Loading branch information
DavidS authored and sds committed Jan 12, 2019
1 parent 8c645e8 commit 576c249
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 9 deletions.
8 changes: 4 additions & 4 deletions Gemfile
Expand Up @@ -3,16 +3,16 @@ source 'http://rubygems.org'
# Specify your gem's dependencies in child_process.gemspec
gemspec

windows = RbConfig::CONFIG['host_os'].downcase =~ /mswin|msys|mingw32/

if RUBY_VERSION =~ /^1\./
gem 'tins', '< 1.7' # The 'tins' gem requires Ruby 2.x on/after this version
gem 'json', '< 2.0' # The 'json' gem drops pre-Ruby 2.x support on/after this version
gem 'term-ansicolor', '< 1.4' # The 'term-ansicolor' gem requires Ruby 2.x on/after this version

# ffi gem for Windows requires Ruby 2.x on/after this version
gem 'ffi', '< 1.9.15' if windows
gem 'ffi', '< 1.9.15' if ENV['CHILDPROCESS_POSIX_SPAWN'] == 'true' || Gem.win_platform?
elsif Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.2')
# Ruby 2.0/2.1 support only ffi before 1.10
gem 'ffi', '~> 1.9.0' if windows
gem 'ffi', '~> 1.9.0' if ENV['CHILDPROCESS_POSIX_SPAWN'] == 'true' || Gem.win_platform?
else
gem 'ffi' if ENV['CHILDPROCESS_POSIX_SPAWN'] == 'true' || Gem.win_platform?
end
2 changes: 2 additions & 0 deletions README.md
Expand Up @@ -129,6 +129,8 @@ ChildProcess.posix_spawn = true
process = ChildProcess.build(*args)
```

To be able to use this, please make sure that you have the `ffi` gem installed.

### Ensure entire process tree dies

By default, the child process does not create a new process group. This means there's no guarantee that the entire process tree will die when the child process is killed. To solve this:
Expand Down
7 changes: 3 additions & 4 deletions childprocess.gemspec
Expand Up @@ -19,12 +19,11 @@ Gem::Specification.new do |s|
s.test_files = `git ls-files -- spec/*`.split("\n")
s.require_paths = ["lib"]

s.add_runtime_dependency "ffi", "~> 1.0", ">= 1.0.11"

s.add_development_dependency "rspec", "~> 3.0"
s.add_development_dependency "yard", "~> 0.0"
s.add_development_dependency 'rake', '< 12.0'
s.add_development_dependency 'coveralls', '< 1.0'
end


# Install FFI gem if we're running on Windows
s.extensions = 'ext/mkrf_conf.rb'
end
24 changes: 24 additions & 0 deletions ext/mkrf_conf.rb
@@ -0,0 +1,24 @@
# Based on the example from https://en.wikibooks.org/wiki/Ruby_Programming/RubyGems#How_to_install_different_versions_of_gems_depending_on_which_version_of_ruby_the_installee_is_using
require 'rubygems'
require 'rubygems/command.rb'
require 'rubygems/dependency_installer.rb'

begin
Gem::Command.build_args = ARGV
rescue NoMethodError # rubocop:disable Lint/HandleExceptions
end

inst = Gem::DependencyInstaller.new

begin
if Gem.win_platform?
inst.install 'ffi', "~> 1.0", ">= 1.0.11"
end
rescue # rubocop:disable Lint/RescueWithoutErrorClass
exit(1)
end

# create dummy rakefile to indicate success
File.open(File.join(File.dirname(__FILE__), 'Rakefile'), 'w') do |f|
f.write("task :default\n")
end
7 changes: 6 additions & 1 deletion lib/childprocess.rb
Expand Up @@ -73,7 +73,12 @@ def posix_spawn?
enabled = @posix_spawn || %w[1 true].include?(ENV['CHILDPROCESS_POSIX_SPAWN'])
return false unless enabled

require 'ffi'
begin
require 'ffi'
rescue LoadError
raise ChildProcess::MissingFFIError
end

begin
require "childprocess/unix/platform/#{ChildProcess.platform_name}"
rescue LoadError
Expand Down
11 changes: 11 additions & 0 deletions lib/childprocess/errors.rb
Expand Up @@ -14,6 +14,17 @@ class InvalidEnvironmentVariable < Error
class LaunchError < Error
end

class MissingFFIError < Error
def initialize
message = "FFI is a required pre-requisite for posix_spawn, falling back to default implementation. " +
"Please add it to your deployment to unlock this functionality. " +
"If you believe this is an error, please file a bug at http://github.com/enkessler/childprocess/issues"

super(message)
end

end

class MissingPlatformError < Error
def initialize
message = "posix_spawn is not yet supported on #{ChildProcess.platform_name} (#{RUBY_PLATFORM}), falling back to default implementation. " +
Expand Down

0 comments on commit 576c249

Please sign in to comment.