xref: /aosp_15_r20/external/grpc-grpc/Rakefile (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
1# -*- ruby -*-
2require 'rake/extensiontask'
3require 'rspec/core/rake_task'
4require 'rubocop/rake_task'
5require 'bundler/gem_tasks'
6require 'fileutils'
7require 'tmpdir'
8
9require_relative 'build_config.rb'
10
11load 'tools/distrib/rake_compiler_docker_image.rb'
12
13# Add rubocop style checking tasks
14RuboCop::RakeTask.new(:rubocop) do |task|
15  task.options = ['-c', 'src/ruby/.rubocop.yml']
16  # add end2end tests to formatter but don't add generated proto _pb.rb's
17  task.patterns = ['src/ruby/{lib,spec}/**/*.rb', 'src/ruby/end2end/*.rb']
18end
19
20spec = Gem::Specification.load('grpc.gemspec')
21
22Gem::PackageTask.new(spec) do |pkg|
23end
24
25# Add the extension compiler task
26Rake::ExtensionTask.new('grpc_c', spec) do |ext|
27  ext.source_pattern = '**/*.{c,h}'
28  ext.ext_dir = File.join('src', 'ruby', 'ext', 'grpc')
29  ext.lib_dir = File.join('src', 'ruby', 'lib', 'grpc')
30  ext.cross_compile = true
31  ext.cross_platform = [
32    'x86-mingw32', 'x64-mingw32', 'x64-mingw-ucrt',
33    'x86_64-linux', 'x86-linux', 'aarch64-linux',
34    'x86_64-darwin', 'arm64-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 += 'BUILDDIR=/tmp '
113  env += "V=#{verbose} "
114  env += "GRPC_RUBY_BUILD_PROCS=#{nproc_override} "
115
116  out = GrpcBuildConfig::CORE_WINDOWS_DLL
117
118  # propagate env variables with ccache configuration to the rake-compiler-dock docker container
119  # and setup ccache symlinks as needed.
120  # TODO(jtattermusch): deduplicate creation of prepare_ccache_cmd
121  prepare_ccache_cmd = "export GRPC_BUILD_ENABLE_CCACHE=\"#{ENV.fetch('GRPC_BUILD_ENABLE_CCACHE', '')}\" && "
122  prepare_ccache_cmd += "export CCACHE_SECONDARY_STORAGE=\"#{ENV.fetch('CCACHE_SECONDARY_STORAGE', '')}\" && "
123  prepare_ccache_cmd += "export PATH=\"$PATH:/usr/local/bin\" && "
124  prepare_ccache_cmd += "source tools/internal_ci/helper_scripts/prepare_ccache_symlinks_rc "
125
126  selected_build_configs.each do |opt|
127    env_comp = "CC=#{opt[:cross]}-gcc "
128    env_comp += "CXX=#{opt[:cross]}-g++ "
129    env_comp += "LD=#{opt[:cross]}-gcc "
130    env_comp += "LDXX=#{opt[:cross]}-g++ "
131    run_rake_compiler(opt[:platform], <<~EOT)
132      #{prepare_ccache_cmd} && \
133      gem update --system --no-document && \
134      #{env} #{env_comp} make -j#{nproc_override} #{out} && \
135      #{opt[:cross]}-strip -x -S #{out} && \
136      cp #{out} #{opt[:out]}
137    EOT
138  end
139end
140
141desc 'Build the native gem file under rake_compiler_dock. Optionally one can pass argument to build only native gem for a chosen platform.'
142task 'gem:native', [:plat] do |t, args|
143  verbose = ENV['V'] || '0'
144
145  grpc_config = ENV['GRPC_CONFIG'] || 'opt'
146  ruby_cc_versions = ['3.3.0', '3.2.0', '3.1.0', '3.0.0', '2.7.0'].join(':')
147  selected_plat = "#{args[:plat]}"
148
149  # use env variable to set artifact build paralellism
150  nproc_override = ENV['GRPC_RUBY_BUILD_PROCS'] || `nproc`.strip
151
152  # propagate env variables with ccache configuration to the rake-compiler-dock docker container
153  # and setup ccache symlinks as needed.
154  prepare_ccache_cmd = "export GRPC_BUILD_ENABLE_CCACHE=\"#{ENV.fetch('GRPC_BUILD_ENABLE_CCACHE', '')}\" && "
155  prepare_ccache_cmd += "export CCACHE_SECONDARY_STORAGE=\"#{ENV.fetch('CCACHE_SECONDARY_STORAGE', '')}\" && "
156  prepare_ccache_cmd += "export PATH=\"$PATH:/usr/local/bin\" && "
157  prepare_ccache_cmd += "source tools/internal_ci/helper_scripts/prepare_ccache_symlinks_rc "
158
159  supported_windows_platforms = ['x86-mingw32', 'x64-mingw32', 'x64-mingw-ucrt']
160  supported_unix_platforms = ['x86_64-linux', 'x86-linux', 'aarch64-linux', 'x86_64-darwin', 'arm64-darwin']
161  supported_platforms = supported_windows_platforms + supported_unix_platforms
162
163  if selected_plat.empty?
164    # build everything
165    windows_platforms = supported_windows_platforms
166    unix_platforms = supported_unix_platforms
167  else
168    # build only selected platform
169    if supported_windows_platforms.include?(selected_plat)
170      windows_platforms = [selected_plat]
171      unix_platforms = []
172    elsif supported_unix_platforms.include?(selected_plat)
173      windows_platforms = []
174      unix_platforms = [selected_plat]
175    else
176      fail "Unsupported platform '#{selected_plat}' passed as an argument."
177    end
178  end
179
180  # Create the windows dlls or create the empty placeholders
181  Rake::Task['dlls'].execute(plat: windows_platforms)
182
183  windows_platforms.each do |plat|
184    run_rake_compiler(plat, <<~EOT)
185      #{prepare_ccache_cmd} && \
186      gem update --system --no-document && \
187      bundle && \
188      bundle exec rake clean && \
189      bundle exec rake native:#{plat} pkg/#{spec.full_name}-#{plat}.gem pkg/#{spec.full_name}.gem \
190        RUBY_CC_VERSION=#{ruby_cc_versions} \
191        V=#{verbose} \
192        GRPC_CONFIG=#{grpc_config} \
193        GRPC_RUBY_BUILD_PROCS=#{nproc_override}
194    EOT
195  end
196
197  # Truncate grpc_c.*.ruby files because they're for Windows only and we don't want
198  # them to take up space in the gems that don't target windows.
199  File.truncate('grpc_c.32-msvcrt.ruby', 0)
200  File.truncate('grpc_c.64-msvcrt.ruby', 0)
201  File.truncate('grpc_c.64-ucrt.ruby', 0)
202
203  `mkdir -p src/ruby/nativedebug/symbols`
204  # TODO(apolcyn): make debug symbols work on apple platforms.
205  # Currently we hit "objcopy: grpc_c.bundle: file format not recognized"
206  # TODO(apolcyn): make debug symbols work on aarch64 linux.
207  # Currently we hit "objcopy: Unable to recognise the format of the input file `grpc_c.so'"
208  unix_platforms_without_debug_symbols = ['x86_64-darwin', 'arm64-darwin', 'aarch64-linux']
209
210  unix_platforms.each do |plat|
211    if unix_platforms_without_debug_symbols.include?(plat)
212      debug_symbols_dir = ''
213    else
214      debug_symbols_dir = File.join(Dir.pwd, 'src/ruby/nativedebug/symbols')
215    end
216    run_rake_compiler(plat, <<~EOT)
217      #{prepare_ccache_cmd} && \
218      gem update --system --no-document && \
219      bundle && \
220      bundle exec rake clean && \
221      export GRPC_RUBY_DEBUG_SYMBOLS_OUTPUT_DIR=#{debug_symbols_dir} && \
222      bundle exec rake native:#{plat} pkg/#{spec.full_name}-#{plat}.gem pkg/#{spec.full_name}.gem \
223        RUBY_CC_VERSION=#{ruby_cc_versions} \
224        V=#{verbose} \
225        GRPC_CONFIG=#{grpc_config} \
226        GRPC_RUBY_BUILD_PROCS=#{nproc_override}
227    EOT
228  end
229  # Generate debug symbol packages to complement the native libraries we just built
230  unix_platforms.each do |plat|
231    unless unix_platforms_without_debug_symbols.include?(plat)
232      `bash src/ruby/nativedebug/build_package.sh #{plat}`
233      `cp src/ruby/nativedebug/pkg/*.gem pkg/`
234    end
235  end
236end
237
238# Define dependencies between the suites.
239task 'suite:wrapper' => [:compile, :rubocop]
240task 'suite:idiomatic' => 'suite:wrapper'
241task 'suite:bidi' => 'suite:wrapper'
242task 'suite:server' => 'suite:wrapper'
243task 'suite:pb' => 'suite:server'
244
245desc 'Compiles the gRPC extension then runs all the tests'
246task all: ['suite:idiomatic', 'suite:bidi', 'suite:pb', 'suite:server']
247task default: :all
248