Rakefile 9.2 KB

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