diff --git a/.gitignore b/.gitignore index cc70659661a..64dc26c3a22 100644 --- a/.gitignore +++ b/.gitignore @@ -56,7 +56,7 @@ out .ycm_extra_conf.py # XCode -build/ +^build/ *.pbxuser !default.pbxuser *.mode1v3 diff --git a/Rakefile b/Rakefile index 079df679962..89a253df433 100755 --- a/Rakefile +++ b/Rakefile @@ -10,11 +10,18 @@ RuboCop::RakeTask.new(:rubocop) do |task| task.patterns = ['src/ruby/{lib,spec}/**/*.rb'] end +spec = Gem::Specification.load('grpc.gemspec') + +Gem::PackageTask.new(spec) do |pkg| +end + # Add the extension compiler task -Rake::ExtensionTask.new 'grpc' do |ext| +Rake::ExtensionTask.new('grpc', spec) do |ext| ext.source_pattern = '**/*.{c,h}' ext.ext_dir = File.join('src', 'ruby', 'ext', 'grpc') ext.lib_dir = File.join('src', 'ruby', 'lib', 'grpc') + ext.cross_compile = true + ext.cross_platform = ['x86-mingw32', 'x64-mingw32'] end # Define the test suites @@ -51,6 +58,20 @@ namespace :suite do end end +desc 'Build the gem file under rake_compiler_dock' +task 'gem:windows' do + require 'digest' + require 'rake_compiler_dock' + version = Digest::SHA1.file('third_party/rake-compiler-dock/Dockerfile').hexdigest + image_name = 'grpc/rake-compiler-dock:' + version + cmd = "docker build -t #{image_name} third_party/rake-compiler-dock" + puts cmd + system cmd + exit 1 unless $? == 0 + ENV['RAKE_COMPILER_DOCK_IMAGE'] = image_name + RakeCompilerDock.sh "bundle && rake cross native gem RUBY_CC_VERSION=2.3.0:2.2.2:2.1.6:2.0.0" +end + # Define dependencies between the suites. task 'suite:wrapper' => [:compile, :rubocop] task 'suite:idiomatic' => 'suite:wrapper' diff --git a/grpc.gemspec b/grpc.gemspec index 47b66ae535d..c38d13cb8b1 100755 --- a/grpc.gemspec +++ b/grpc.gemspec @@ -16,7 +16,7 @@ Gem::Specification.new do |s| s.required_ruby_version = '>= 2.0.0' s.requirements << 'libgrpc ~> 0.11.0 needs to be installed' - s.files = %w( Rakefile Makefile ) + s.files = %w( Makefile ) s.files += %w( etc/roots.pem ) s.files += Dir.glob('src/ruby/bin/**/*') s.files += Dir.glob('src/ruby/ext/**/*') @@ -33,16 +33,17 @@ Gem::Specification.new do |s| s.platform = Gem::Platform::RUBY s.add_dependency 'google-protobuf', '~> 3.0.0alpha.1.1' - s.add_dependency 'googleauth', '~> 0.5.1' + s.add_dependency 'googleauth', '~> 0.5.1' - s.add_development_dependency 'bundler', '~> 1.9' - s.add_development_dependency 'logging', '~> 2.0' - s.add_development_dependency 'simplecov', '~> 0.9' - s.add_development_dependency 'rake', '~> 10.4' - s.add_development_dependency 'rake-compiler', '~> 0.9' - s.add_development_dependency 'rspec', '~> 3.2' - s.add_development_dependency 'rubocop', '~> 0.30.0' - s.add_development_dependency 'signet', '~>0.7.0' + s.add_development_dependency 'bundler', '~> 1.9' + s.add_development_dependency 'logging', '~> 2.0' + s.add_development_dependency 'simplecov', '~> 0.9' + s.add_development_dependency 'rake', '~> 10.4' + s.add_development_dependency 'rake-compiler', '~> 0.9' + s.add_development_dependency 'rake-compiler-dock', '~> 0.5' + s.add_development_dependency 'rspec', '~> 3.2' + s.add_development_dependency 'rubocop', '~> 0.30.0' + s.add_development_dependency 'signet', '~> 0.7.0' s.extensions = %w(src/ruby/ext/grpc/extconf.rb) diff --git a/src/ruby/lib/grpc.rb b/src/ruby/lib/grpc.rb index 1671ba3550b..7890e175519 100644 --- a/src/ruby/lib/grpc.rb +++ b/src/ruby/lib/grpc.rb @@ -32,8 +32,13 @@ unless ENV['GRPC_DEFAULT_SSL_ROOTS_FILE_PATH'] ENV['GRPC_DEFAULT_SSL_ROOTS_FILE_PATH'] = ssl_roots_path end +begin + require "grpc/#{RUBY_VERSION.sub(/\.\d$/, '')}/grpc" +rescue LoadError + require 'grpc/grpc' +end + require 'grpc/errors' -require 'grpc/grpc' require 'grpc/logconfig' require 'grpc/notifier' require 'grpc/version' diff --git a/templates/grpc.gemspec.template b/templates/grpc.gemspec.template index fdf87ee13fd..f015e539be1 100644 --- a/templates/grpc.gemspec.template +++ b/templates/grpc.gemspec.template @@ -18,7 +18,7 @@ s.required_ruby_version = '>= 2.0.0' s.requirements << 'libgrpc ~> 0.11.0 needs to be installed' - s.files = %w( Rakefile Makefile ) + s.files = %w( Makefile ) s.files += %w( etc/roots.pem ) s.files += Dir.glob('src/ruby/bin/**/*') s.files += Dir.glob('src/ruby/ext/**/*') @@ -35,16 +35,17 @@ s.platform = Gem::Platform::RUBY s.add_dependency 'google-protobuf', '~> 3.0.0alpha.1.1' - s.add_dependency 'googleauth', '~> 0.5.1' + s.add_dependency 'googleauth', '~> 0.5.1' - s.add_development_dependency 'bundler', '~> 1.9' - s.add_development_dependency 'logging', '~> 2.0' - s.add_development_dependency 'simplecov', '~> 0.9' - s.add_development_dependency 'rake', '~> 10.4' - s.add_development_dependency 'rake-compiler', '~> 0.9' - s.add_development_dependency 'rspec', '~> 3.2' - s.add_development_dependency 'rubocop', '~> 0.30.0' - s.add_development_dependency 'signet', '~>0.7.0' + s.add_development_dependency 'bundler', '~> 1.9' + s.add_development_dependency 'logging', '~> 2.0' + s.add_development_dependency 'simplecov', '~> 0.9' + s.add_development_dependency 'rake', '~> 10.4' + s.add_development_dependency 'rake-compiler', '~> 0.9' + s.add_development_dependency 'rake-compiler-dock', '~> 0.5' + s.add_development_dependency 'rspec', '~> 3.2' + s.add_development_dependency 'rubocop', '~> 0.30.0' + s.add_development_dependency 'signet', '~> 0.7.0' s.extensions = %w(src/ruby/ext/grpc/extconf.rb) diff --git a/third_party/rake-compiler-dock/Dockerfile b/third_party/rake-compiler-dock/Dockerfile new file mode 100644 index 00000000000..11a6bd77965 --- /dev/null +++ b/third_party/rake-compiler-dock/Dockerfile @@ -0,0 +1,109 @@ +FROM ubuntu:14.04 + +RUN apt-get -y update && \ + apt-get install -y curl git-core mingw-w64 xz-utils build-essential wget unzip + +RUN mkdir -p /opt/mingw && \ + curl -SL http://downloads.sourceforge.net/mingw-w64/i686-w64-mingw32-gcc-4.7.2-release-linux64_rubenvb.tar.xz | \ + tar -xJC /opt/mingw && \ + echo "export PATH=\$PATH:/opt/mingw/mingw32/bin" >> /etc/rubybashrc + +RUN mkdir -p /opt/mingw && \ + curl -SL http://downloads.sourceforge.net/mingw-w64/x86_64-w64-mingw32-gcc-4.7.2-release-linux64_rubenvb.tar.xz | \ + tar -xJC /opt/mingw && \ + echo "export PATH=\$PATH:/opt/mingw/mingw64/bin" >> /etc/rubybashrc + +# Add "rvm" as system group, to avoid conflicts with host GIDs typically starting with 1000 +RUN groupadd -r rvm && useradd -r -g rvm -G sudo -p "" --create-home rvm && \ + echo "source /etc/profile.d/rvm.sh" >> /etc/rubybashrc +USER rvm + +# install rvm, RVM 1.26.0+ has signed releases, source rvm for usage outside of package scripts +RUN gpg --keyserver hkp://keys.gnupg.net --recv-keys D39DC0E3 && \ + (curl -L http://get.rvm.io | sudo bash -s stable) && \ + bash -c " \ + source /etc/rubybashrc && \ + rvmsudo rvm cleanup all " + +# Import patch files for ruby and gems +COPY build/patches /home/rvm/patches/ +ENV BASH_ENV /etc/rubybashrc + +# install rubies and fix permissions on +RUN bash -c " \ + export CFLAGS='-s -O3 -fno-fast-math -fPIC' && \ + for v in 2.3.0 ; do \ + rvm install \$v --patch \$(echo ~/patches/ruby-\$v/* | tr ' ' ','); \ + done && \ + rvm cleanup all && \ + find /usr/local/rvm -type d -print0 | sudo xargs -0 chmod g+sw " + +# Install rake-compiler and typical gems in all Rubies +# do not generate documentation for gems +RUN echo "gem: --no-ri --no-rdoc" >> ~/.gemrc && \ + bash -c " \ + rvm all do gem install bundler rake-compiler hoe mini_portile rubygems-tasks json && \ + rvm 2.3.0 do gem install mini_portile2 && \ + find /usr/local/rvm -type d -print0 | sudo xargs -0 chmod g+sw " + +# Install rake-compiler's cross rubies in global dir instead of /root +RUN sudo mkdir -p /usr/local/rake-compiler && \ + sudo chown rvm.rvm /usr/local/rake-compiler && \ + ln -s /usr/local/rake-compiler ~/.rake-compiler + +# Patch rake-compiler to avoid build of ruby extensions +RUN cd /usr/local/rvm/gems/ruby-2.3.0/gems/rake-compiler-0.9.5 && patch -p1 < /home/rvm/patches/rake-compiler-0.9.5/without-exts.diff ; \ + true + +RUN bash -c "rvm use 2.3.0 --default && \ + export MAKE=\"make -j`nproc`\" CFLAGS='-s -O1 -fno-omit-frame-pointer -fno-fast-math' && \ + rake-compiler cross-ruby VERSION=2.3.0 HOST=i686-w64-mingw32 && \ + rake-compiler cross-ruby VERSION=2.3.0 HOST=x86_64-w64-mingw32 && \ + rake-compiler cross-ruby VERSION=2.2.2 HOST=i686-w64-mingw32 && \ + rake-compiler cross-ruby VERSION=2.2.2 HOST=x86_64-w64-mingw32 && \ + rake-compiler cross-ruby VERSION=2.1.6 HOST=i686-w64-mingw32 && \ + rake-compiler cross-ruby VERSION=2.1.6 HOST=x86_64-w64-mingw32 && \ + rake-compiler cross-ruby VERSION=2.0.0-p645 HOST=i686-w64-mingw32 && \ + rake-compiler cross-ruby VERSION=2.0.0-p645 HOST=x86_64-w64-mingw32 && \ + rm -rf ~/.rake-compiler/builds ~/.rake-compiler/sources && \ + find /usr/local/rvm -type d -print0 | sudo xargs -0 chmod g+sw " + +RUN bash -c " \ + rvm alias create 2.3 2.3.0 " + +USER root + +# Fix paths in rake-compiler/config.yml and add rvm and mingw-tools to the global bashrc +RUN sed -i -- "s:/root/.rake-compiler:/usr/local/rake-compiler:g" /usr/local/rake-compiler/config.yml && \ + echo "source /etc/profile.d/rvm.sh" >> /etc/bash.bashrc && \ + echo "export PATH=\$PATH:/opt/mingw/mingw32/bin" >> /etc/bash.bashrc && \ + echo "export PATH=\$PATH:/opt/mingw/mingw64/bin" >> /etc/bash.bashrc + +# Install wrappers for strip commands as a workaround for "Protocol error" in boot2docker. +COPY build/strip_wrapper /root/ +RUN mv /opt/mingw/mingw32/bin/i686-w64-mingw32-strip /opt/mingw/mingw32/bin/i686-w64-mingw32-strip.bin && \ + mv /opt/mingw/mingw64/bin/x86_64-w64-mingw32-strip /opt/mingw/mingw64/bin/x86_64-w64-mingw32-strip.bin && \ + mv /usr/bin/i686-w64-mingw32-strip /usr/bin/i686-w64-mingw32-strip.bin && \ + mv /usr/bin/x86_64-w64-mingw32-strip /usr/bin/x86_64-w64-mingw32-strip.bin && \ + ln /root/strip_wrapper /opt/mingw/mingw32/bin/i686-w64-mingw32-strip && \ + ln /root/strip_wrapper /opt/mingw/mingw64/bin/x86_64-w64-mingw32-strip && \ + ln /root/strip_wrapper /usr/bin/i686-w64-mingw32-strip && \ + ln /root/strip_wrapper /usr/bin/x86_64-w64-mingw32-strip + +RUN find / -name rbconfig.rb | while read f ; do sed -i 's/0x0501/0x0600/' $f ; done +RUN find / -name win32.h | while read f ; do sed -i 's/gettimeofday/rb_gettimeofday/' $f ; done +RUN sed -i 's/defined.__MINGW64__.$/1/' /usr/local/rake-compiler/ruby/i686-w64-mingw32/ruby-2.0.0-p645/include/ruby-2.0.0/ruby/win32.h + +# Install SIGINT forwarder +COPY build/sigfw.c /root/ +RUN gcc $HOME/sigfw.c -o /usr/local/bin/sigfw + +# Install user mapper +COPY build/runas /usr/local/bin/ + +# Install sudoers configuration +COPY build/sudoers /etc/sudoers.d/rake-compiler-dock + +ENV RUBY_CC_VERSION 2.3.0:2.2.2:2.1.6:2.0.0 + +CMD bash diff --git a/third_party/rake-compiler-dock/LICENSE.txt b/third_party/rake-compiler-dock/LICENSE.txt new file mode 100644 index 00000000000..b884ba5396d --- /dev/null +++ b/third_party/rake-compiler-dock/LICENSE.txt @@ -0,0 +1,22 @@ +Copyright (c) 2015 Lars Kanis + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/third_party/rake-compiler-dock/README.md b/third_party/rake-compiler-dock/README.md new file mode 100644 index 00000000000..0a9aa8ba78f --- /dev/null +++ b/third_party/rake-compiler-dock/README.md @@ -0,0 +1 @@ +This is a modified Dockerfile taken from [rake-compiler-dock](https://github.com/rake-compiler/rake-compiler-dock) for the purpose of building gRPC's ruby gem. diff --git a/third_party/rake-compiler-dock/build/patches/rake-compiler-0.9.5/without-exts.diff b/third_party/rake-compiler-dock/build/patches/rake-compiler-0.9.5/without-exts.diff new file mode 100644 index 00000000000..07739d33ecc --- /dev/null +++ b/third_party/rake-compiler-dock/build/patches/rake-compiler-0.9.5/without-exts.diff @@ -0,0 +1,14 @@ +diff --git a/tasks/bin/cross-ruby.rake b/tasks/bin/cross-ruby.rake +index 6acc816..6aa2a49 100644 +--- a/tasks/bin/cross-ruby.rake ++++ b/tasks/bin/cross-ruby.rake +@@ -135,8 +135,7 @@ file "#{USER_HOME}/builds/#{MINGW_HOST}/#{RUBY_CC_VERSION}/Makefile" => ["#{USER + "--build=#{RUBY_BUILD}", + '--enable-shared', + '--disable-install-doc', +- '--without-tk', +- '--without-tcl' ++ '--with-ext=' + ] + + # Force Winsock2 for Ruby 1.8, 1.9 defaults to it diff --git a/third_party/rake-compiler-dock/build/patches/ruby-1.8.7-p374/nop.patch b/third_party/rake-compiler-dock/build/patches/ruby-1.8.7-p374/nop.patch new file mode 100644 index 00000000000..fac8525da60 --- /dev/null +++ b/third_party/rake-compiler-dock/build/patches/ruby-1.8.7-p374/nop.patch @@ -0,0 +1,2 @@ +diff --git a/configure b/configure +index 55157af..6630eba 100755 diff --git a/third_party/rake-compiler-dock/build/patches/ruby-1.9.3/no_sendfile.patch b/third_party/rake-compiler-dock/build/patches/ruby-1.9.3/no_sendfile.patch new file mode 100644 index 00000000000..d8f339e8140 --- /dev/null +++ b/third_party/rake-compiler-dock/build/patches/ruby-1.9.3/no_sendfile.patch @@ -0,0 +1,13 @@ +diff --git a/configure b/configure +index 898730c..cfe6253 100755 +--- a/configure ++++ b/configure +@@ -14695,7 +14695,7 @@ for ac_func in fmod killpg wait4 waitpid fork spawnv syscall __syscall chroot ge + setsid telldir seekdir fchmod cosh sinh tanh log2 round\ + setuid setgid daemon select_large_fdset setenv unsetenv\ + mktime timegm gmtime_r clock_gettime gettimeofday poll ppoll\ +- pread sendfile shutdown sigaltstack dl_iterate_phdr ++ pread shutdown sigaltstack dl_iterate_phdr + do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` + ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" diff --git a/third_party/rake-compiler-dock/build/patches/ruby-1.9.3/nop.patch b/third_party/rake-compiler-dock/build/patches/ruby-1.9.3/nop.patch new file mode 100644 index 00000000000..fac8525da60 --- /dev/null +++ b/third_party/rake-compiler-dock/build/patches/ruby-1.9.3/nop.patch @@ -0,0 +1,2 @@ +diff --git a/configure b/configure +index 55157af..6630eba 100755 diff --git a/third_party/rake-compiler-dock/build/patches/ruby-2.3.0/no_sendfile.patch b/third_party/rake-compiler-dock/build/patches/ruby-2.3.0/no_sendfile.patch new file mode 100644 index 00000000000..915fc7b790f --- /dev/null +++ b/third_party/rake-compiler-dock/build/patches/ruby-2.3.0/no_sendfile.patch @@ -0,0 +1,12 @@ +diff --git a/configure b/configure +index ebe3d8c..a336b73 100755 +--- a/configure ++++ b/configure +@@ -18943,7 +18943,6 @@ do : + ac_fn_c_check_func "$LINENO" "sendfile" "ac_cv_func_sendfile" + if test "x$ac_cv_func_sendfile" = xyes; then : + cat >>confdefs.h <<_ACEOF +-#define HAVE_SENDFILE 1 + _ACEOF + + fi diff --git a/third_party/rake-compiler-dock/build/runas b/third_party/rake-compiler-dock/build/runas new file mode 100755 index 00000000000..b29ce31fcce --- /dev/null +++ b/third_party/rake-compiler-dock/build/runas @@ -0,0 +1,12 @@ +#!/bin/bash + +groupadd -g "$GID" "$GROUP" +mkdir -p /tmp/home +useradd -g "$GID" -u "$UID" -G rvm,sudo -p "" -b /tmp/home -m "$USER" + +HOME=$(bash <<< "echo ~$USER") +ln -s /usr/local/rake-compiler "$HOME"/.rake-compiler + +sudo -u "$USER" --set-home \ + BASH_ENV=/etc/rubybashrc \ + -- "$@" diff --git a/third_party/rake-compiler-dock/build/sigfw.c b/third_party/rake-compiler-dock/build/sigfw.c new file mode 100644 index 00000000000..291d76cec8c --- /dev/null +++ b/third_party/rake-compiler-dock/build/sigfw.c @@ -0,0 +1,43 @@ +/* + * This program handles SIGINT and forwards it to another process. + * It is intended to be run as PID 1. + * + * Docker starts processes with "docker run" as PID 1. + * On Linux, the default signal handler for PID 1 ignores any signals. + * Therefore Ctrl-C aka SIGINT is ignored per default. + */ + +#include +#include +#include + +int pid = 0; + +void +handle_sigint (int signum) +{ + if(pid) + kill(pid, SIGINT); +} + +int main(int argc, char *argv[]){ + struct sigaction new_action; + int status = -1; + + /* Set up the structure to specify the new action. */ + new_action.sa_handler = handle_sigint; + sigemptyset (&new_action.sa_mask); + new_action.sa_flags = 0; + + sigaction (SIGINT, &new_action, (void*)0); + + pid = fork(); + if(pid){ + wait(&status); + return WEXITSTATUS(status); + }else{ + status = execvp(argv[1], &argv[1]); + perror("exec"); + return status; + } +} diff --git a/third_party/rake-compiler-dock/build/strip_wrapper b/third_party/rake-compiler-dock/build/strip_wrapper new file mode 100755 index 00000000000..7f8a1346a1d --- /dev/null +++ b/third_party/rake-compiler-dock/build/strip_wrapper @@ -0,0 +1,30 @@ +#!/usr/bin/env ruby + +# Strip file on local folder instead of a Virtualbox shared folder +# to work around this bug: https://www.virtualbox.org/ticket/8463 + +require 'tempfile' +require 'fileutils' + +strip = "#{File.basename($0)}.bin" + +files = ARGV.reject{|f| f=~/^-/ }.map do |arg| + tmp = Tempfile.new 'strip' + tmp.close + FileUtils.cp arg, tmp.path + [tmp, arg] +end + +options = ARGV.select{|f| f=~/^-/ } + files.map{|t,o| t.path } + +unless system( strip, *options ) + exit 127 +end +code = $?.exitstatus + +files.each do |tmp, orig| + FileUtils.rm orig + FileUtils.cp tmp.path, orig +end + +exit code diff --git a/third_party/rake-compiler-dock/build/sudoers b/third_party/rake-compiler-dock/build/sudoers new file mode 100644 index 00000000000..f9f9b97c951 --- /dev/null +++ b/third_party/rake-compiler-dock/build/sudoers @@ -0,0 +1 @@ +Defaults env_keep += "http_proxy https_proxy ftp_proxy RCD_HOST_RUBY_PLATFORM RCD_HOST_RUBY_VERSION RCD_IMAGE RUBY_CC_VERSION"