1# -*- ruby -*- 2require 'rake/extensiontask' 3require 'rspec/core/rake_task' 4require 'rubocop/rake_task' 5require 'bundler/gem_tasks' 6require 'fileutils' 7 8require_relative 'build_config.rb' 9 10load 'tools/distrib/rake_compiler_docker_image.rb' 11 12# Add rubocop style checking tasks 13RuboCop::RakeTask.new(:rubocop) do |task| 14 task.options = ['-c', 'src/ruby/.rubocop.yml'] 15 # add end2end tests to formatter but don't add generated proto _pb.rb's 16 task.patterns = ['src/ruby/{lib,spec}/**/*.rb', 'src/ruby/end2end/*.rb'] 17end 18 19spec = Gem::Specification.load('grpc.gemspec') 20 21Gem::PackageTask.new(spec) do |pkg| 22end 23 24# Add the extension compiler task 25Rake::ExtensionTask.new('grpc_c', spec) do |ext| 26 ext.source_pattern = '**/*.{c,h}' 27 ext.ext_dir = File.join('src', 'ruby', 'ext', 'grpc') 28 ext.lib_dir = File.join('src', 'ruby', 'lib', 'grpc') 29 ext.cross_compile = true 30 ext.cross_platform = [ 31 'x86-mingw32', 'x64-mingw32', 'x64-mingw-ucrt', 32 'x86_64-linux', 'x86-linux', 33 'x86_64-darwin', 'arm64-darwin', 34 'universal-darwin' 35 ] 36 ext.cross_compiling do |spec| 37 spec.files = spec.files.select { 38 |file| file.start_with?( 39 "src/ruby/bin/", "src/ruby/ext/", "src/ruby/lib/", "src/ruby/pb/") 40 } 41 spec.files += %w( etc/roots.pem grpc_c.32-msvcrt.ruby grpc_c.64-msvcrt.ruby grpc_c.64-ucrt.ruby ) 42 end 43end 44 45CLEAN.add "src/ruby/lib/grpc/[0-9].[0-9]", "src/ruby/lib/grpc/grpc_c.{bundle,so}" 46 47# Define the test suites 48SPEC_SUITES = [ 49 { id: :wrapper, title: 'wrapper layer', files: %w(src/ruby/spec/*.rb) }, 50 { id: :idiomatic, title: 'idiomatic layer', dir: %w(src/ruby/spec/generic), 51 tags: ['~bidi', '~server'] }, 52 { id: :bidi, title: 'bidi tests', dir: %w(src/ruby/spec/generic), 53 tag: 'bidi' }, 54 { id: :server, title: 'rpc server thread tests', dir: %w(src/ruby/spec/generic), 55 tag: 'server' }, 56 { id: :pb, title: 'protobuf service tests', dir: %w(src/ruby/spec/pb) } 57] 58namespace :suite do 59 SPEC_SUITES.each do |suite| 60 desc "Run all specs in the #{suite[:title]} spec suite" 61 RSpec::Core::RakeTask.new(suite[:id]) do |t| 62 ENV['COVERAGE_NAME'] = suite[:id].to_s 63 spec_files = [] 64 suite[:files].each { |f| spec_files += Dir[f] } if suite[:files] 65 66 if suite[:dir] 67 suite[:dir].each { |f| spec_files += Dir["#{f}/**/*_spec.rb"] } 68 end 69 helper = 'src/ruby/spec/spec_helper.rb' 70 spec_files << helper unless spec_files.include?(helper) 71 72 t.pattern = spec_files 73 t.rspec_opts = "--tag #{suite[:tag]}" if suite[:tag] 74 if suite[:tags] 75 t.rspec_opts = suite[:tags].map { |x| "--tag #{x}" }.join(' ') 76 end 77 end 78 end 79end 80 81desc 'Build the Windows gRPC DLLs for Ruby. The argument contains the list of platforms for which to build dll. Empty placeholder files will be created for platforms that were not selected.' 82task 'dlls', [:plat] do |t, args| 83 grpc_config = ENV['GRPC_CONFIG'] || 'opt' 84 verbose = ENV['V'] || '0' 85 # use env variable to set artifact build paralellism 86 nproc_override = ENV['GRPC_RUBY_BUILD_PROCS'] || `nproc`.strip 87 plat_list = args[:plat] 88 89 build_configs = [ 90 { cross: 'x86_64-w64-mingw32', out: 'grpc_c.64-ucrt.ruby', platform: 'x64-mingw-ucrt' }, 91 { cross: 'x86_64-w64-mingw32', out: 'grpc_c.64-msvcrt.ruby', platform: 'x64-mingw32' }, 92 { cross: 'i686-w64-mingw32', out: 'grpc_c.32-msvcrt.ruby', platform: 'x86-mingw32' } 93 ] 94 selected_build_configs = [] 95 build_configs.each do |config| 96 if plat_list.include?(config[:platform]) 97 # build the DLL (as grpc_c.*.ruby) 98 selected_build_configs.append(config) 99 else 100 # create an empty grpc_c.*.ruby file as a placeholder 101 FileUtils.touch config[:out] 102 end 103 end 104 105 env = 'CPPFLAGS="-D_WIN32_WINNT=0x600 -DNTDDI_VERSION=0x06000000 -DUNICODE -D_UNICODE -Wno-unused-variable -Wno-unused-result -DCARES_STATICLIB -Wno-error=conversion -Wno-sign-compare -Wno-parentheses -Wno-format -DWIN32_LEAN_AND_MEAN" ' 106 env += 'CFLAGS="-Wno-incompatible-pointer-types" ' 107 env += 'CXXFLAGS="-std=c++14 -fno-exceptions" ' 108 env += 'LDFLAGS=-static ' 109 env += 'SYSTEM=MINGW32 ' 110 env += 'EMBED_ZLIB=true ' 111 env += 'EMBED_OPENSSL=true ' 112 env += 'EMBED_CARES=true ' 113 env += 'BUILDDIR=/tmp ' 114 env += "V=#{verbose} " 115 env += "GRPC_RUBY_BUILD_PROCS=#{nproc_override} " 116 117 out = GrpcBuildConfig::CORE_WINDOWS_DLL 118 119 # propagate env variables with ccache configuration to the rake-compiler-dock docker container 120 # and setup ccache symlinks as needed. 121 # TODO(jtattermusch): deduplicate creation of prepare_ccache_cmd 122 prepare_ccache_cmd = "export GRPC_BUILD_ENABLE_CCACHE=\"#{ENV.fetch('GRPC_BUILD_ENABLE_CCACHE', '')}\" && " 123 prepare_ccache_cmd += "export CCACHE_SECONDARY_STORAGE=\"#{ENV.fetch('CCACHE_SECONDARY_STORAGE', '')}\" && " 124 prepare_ccache_cmd += "export PATH=\"$PATH:/usr/local/bin\" && " 125 prepare_ccache_cmd += "source tools/internal_ci/helper_scripts/prepare_ccache_symlinks_rc " 126 127 selected_build_configs.each do |opt| 128 env_comp = "CC=#{opt[:cross]}-gcc " 129 env_comp += "CXX=#{opt[:cross]}-g++ " 130 env_comp += "LD=#{opt[:cross]}-gcc " 131 env_comp += "LDXX=#{opt[:cross]}-g++ " 132 run_rake_compiler(opt[:platform], <<~EOT) 133 #{prepare_ccache_cmd} && \ 134 gem update --system --no-document && \ 135 #{env} #{env_comp} make -j#{nproc_override} #{out} && \ 136 #{opt[:cross]}-strip -x -S #{out} && \ 137 cp #{out} #{opt[:out]} 138 EOT 139 end 140end 141 142desc 'Build the native gem file under rake_compiler_dock. Optionally one can pass argument to build only native gem for a chosen platform.' 143task 'gem:native', [:plat] do |t, args| 144 verbose = ENV['V'] || '0' 145 146 grpc_config = ENV['GRPC_CONFIG'] || 'opt' 147 ruby_cc_versions = ['3.2.0', '3.1.0', '3.0.0', '2.7.0', '2.6.0'].join(':') 148 selected_plat = "#{args[:plat]}" 149 150 # use env variable to set artifact build paralellism 151 nproc_override = ENV['GRPC_RUBY_BUILD_PROCS'] || `nproc`.strip 152 153 # propagate env variables with ccache configuration to the rake-compiler-dock docker container 154 # and setup ccache symlinks as needed. 155 prepare_ccache_cmd = "export GRPC_BUILD_ENABLE_CCACHE=\"#{ENV.fetch('GRPC_BUILD_ENABLE_CCACHE', '')}\" && " 156 prepare_ccache_cmd += "export CCACHE_SECONDARY_STORAGE=\"#{ENV.fetch('CCACHE_SECONDARY_STORAGE', '')}\" && " 157 prepare_ccache_cmd += "export PATH=\"$PATH:/usr/local/bin\" && " 158 prepare_ccache_cmd += "source tools/internal_ci/helper_scripts/prepare_ccache_symlinks_rc " 159 160 supported_windows_platforms = ['x86-mingw32', 'x64-mingw32', 'x64-mingw-ucrt'] 161 supported_unix_platforms = ['x86_64-linux', 'x86-linux', 'x86_64-darwin', 'arm64-darwin'] 162 supported_platforms = supported_windows_platforms + supported_unix_platforms 163 164 if selected_plat.empty? 165 # build everything 166 windows_platforms = supported_windows_platforms 167 unix_platforms = supported_unix_platforms 168 else 169 # build only selected platform 170 if supported_windows_platforms.include?(selected_plat) 171 windows_platforms = [selected_plat] 172 unix_platforms = [] 173 elsif supported_unix_platforms.include?(selected_plat) 174 windows_platforms = [] 175 unix_platforms = [selected_plat] 176 else 177 fail "Unsupported platform '#{selected_plat}' passed as an argument." 178 end 179 end 180 181 # Create the windows dlls or create the empty placeholders 182 Rake::Task['dlls'].execute(plat: windows_platforms) 183 184 windows_platforms.each do |plat| 185 run_rake_compiler(plat, <<~EOT) 186 #{prepare_ccache_cmd} && \ 187 gem update --system --no-document && \ 188 bundle && \ 189 bundle exec rake clean && \ 190 bundle exec rake native:#{plat} pkg/#{spec.full_name}-#{plat}.gem pkg/#{spec.full_name}.gem \ 191 RUBY_CC_VERSION=#{ruby_cc_versions} \ 192 V=#{verbose} \ 193 GRPC_CONFIG=#{grpc_config} \ 194 GRPC_RUBY_BUILD_PROCS=#{nproc_override} 195 EOT 196 end 197 198 # Truncate grpc_c.*.ruby files because they're for Windows only and we don't want 199 # them to take up space in the gems that don't target windows. 200 File.truncate('grpc_c.32-msvcrt.ruby', 0) 201 File.truncate('grpc_c.64-msvcrt.ruby', 0) 202 File.truncate('grpc_c.64-ucrt.ruby', 0) 203 204 unix_platforms.each do |plat| 205 run_rake_compiler(plat, <<~EOT) 206 #{prepare_ccache_cmd} && \ 207 gem update --system --no-document && \ 208 bundle && \ 209 bundle exec rake clean && \ 210 bundle exec rake native:#{plat} pkg/#{spec.full_name}-#{plat}.gem pkg/#{spec.full_name}.gem \ 211 RUBY_CC_VERSION=#{ruby_cc_versions} \ 212 V=#{verbose} \ 213 GRPC_CONFIG=#{grpc_config} \ 214 GRPC_RUBY_BUILD_PROCS=#{nproc_override} 215 EOT 216 end 217end 218 219# Define dependencies between the suites. 220task 'suite:wrapper' => [:compile, :rubocop] 221task 'suite:idiomatic' => 'suite:wrapper' 222task 'suite:bidi' => 'suite:wrapper' 223task 'suite:server' => 'suite:wrapper' 224task 'suite:pb' => 'suite:server' 225 226desc 'Compiles the gRPC extension then runs all the tests' 227task all: ['suite:idiomatic', 'suite:bidi', 'suite:pb', 'suite:server'] 228task default: :all 229