• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2015 gRPC authors.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15require 'etc'
16require 'mkmf'
17require_relative '../../lib/grpc/version.rb'
18
19windows = RUBY_PLATFORM =~ /mingw|mswin/
20windows_ucrt = RUBY_PLATFORM =~ /(mingw|mswin).*ucrt/
21bsd = RUBY_PLATFORM =~ /bsd/
22darwin = RUBY_PLATFORM =~ /darwin/
23linux = RUBY_PLATFORM =~ /linux/
24cross_compiling = ENV['RCD_HOST_RUBY_VERSION'] # set by rake-compiler-dock in build containers
25# TruffleRuby uses the Sulong LLVM runtime, which is different from Apple's.
26apple_toolchain = darwin && RUBY_ENGINE != 'truffleruby'
27
28grpc_root = File.expand_path(File.join(File.dirname(__FILE__), '../../../..'))
29
30grpc_config = ENV['GRPC_CONFIG'] || 'opt'
31
32ENV['MACOSX_DEPLOYMENT_TARGET'] = '10.14'
33
34def debug_symbols_output_dir
35  d = ENV['GRPC_RUBY_DEBUG_SYMBOLS_OUTPUT_DIR']
36  return nil if d.nil? or d.size == 0
37  d
38end
39
40def maybe_remove_strip_all_linker_flag(flags)
41  if debug_symbols_output_dir
42    # Hack to prevent automatic stripping during shared library linking.
43    # rake-compiler-dock sets the -s LDFLAG when building rubies for
44    # cross compilation, and this -s flag propagates into RbConfig. Stripping
45    # during the link is problematic because it prevents us from saving
46    # debug symbols. We want to first link our shared library, then save
47    # debug symbols, and only after that strip.
48    flags = flags.split(' ')
49    flags = flags.reject {|flag| flag == '-s'}
50    flags = flags.join(' ')
51  end
52  flags
53end
54
55def env_unset?(name)
56  ENV[name].nil? || ENV[name].size == 0
57end
58
59def inherit_env_or_rbconfig(name)
60  ENV[name] = inherit_rbconfig(name) if env_unset?(name)
61end
62
63def inherit_rbconfig(name, linker_flag: false)
64  value = RbConfig::CONFIG[name] || ''
65  if linker_flag
66    value = maybe_remove_strip_all_linker_flag(value)
67  end
68  p "extconf.rb setting ENV[#{name}] = #{value}"
69  ENV[name] = value
70end
71
72def env_append(name, string)
73  ENV[name] += ' ' + string
74end
75
76# build grpc C-core
77inherit_env_or_rbconfig 'AR'
78inherit_env_or_rbconfig 'CC'
79inherit_env_or_rbconfig 'CXX'
80inherit_env_or_rbconfig 'RANLIB'
81inherit_env_or_rbconfig 'STRIP'
82inherit_rbconfig 'CPPFLAGS'
83inherit_rbconfig('LDFLAGS', linker_flag: true)
84
85ENV['LD'] = ENV['CC'] if env_unset?('LD')
86ENV['LDXX'] = ENV['CXX'] if env_unset?('LDXX')
87
88if RUBY_ENGINE == 'truffleruby'
89  # ensure we can find the system's OpenSSL
90  env_append 'CPPFLAGS', RbConfig::CONFIG['cppflags']
91end
92
93if apple_toolchain && !cross_compiling
94  ENV['AR'] = 'libtool'
95  ENV['ARFLAGS'] = '-o'
96end
97
98# Don't embed on TruffleRuby (constant-time crypto is unsafe with Sulong, slow build times)
99ENV['EMBED_OPENSSL'] = (RUBY_ENGINE != 'truffleruby').to_s
100# Don't embed on TruffleRuby (the system zlib is already linked for the zlib C extension, slow build times)
101ENV['EMBED_ZLIB'] = (RUBY_ENGINE != 'truffleruby').to_s
102
103ENV['ARCH_FLAGS'] = RbConfig::CONFIG['ARCH_FLAG']
104if apple_toolchain && !cross_compiling
105  if RUBY_PLATFORM =~ /arm64/
106    ENV['ARCH_FLAGS'] = '-arch arm64'
107  else
108    ENV['ARCH_FLAGS'] = '-arch i386 -arch x86_64'
109  end
110end
111
112env_append 'CPPFLAGS', '-DGRPC_XDS_USER_AGENT_NAME_SUFFIX="\"RUBY\""'
113
114require_relative '../../lib/grpc/version'
115env_append 'CPPFLAGS', '-DGRPC_XDS_USER_AGENT_VERSION_SUFFIX="\"' + GRPC::VERSION + '\""'
116env_append 'CPPFLAGS', '-DGRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK=1'
117
118output_dir = File.expand_path(RbConfig::CONFIG['topdir'])
119grpc_lib_dir = File.join(output_dir, 'libs', grpc_config)
120ENV['BUILDDIR'] = output_dir
121
122strip_tool = RbConfig::CONFIG['STRIP']
123strip_tool += ' -x' if apple_toolchain
124
125unless windows
126  puts 'Building internal gRPC into ' + grpc_lib_dir
127  nproc = 4
128  nproc = Etc.nprocessors if Etc.respond_to? :nprocessors
129  nproc_override = ENV['GRPC_RUBY_BUILD_PROCS']
130  unless nproc_override.nil? or nproc_override.size == 0
131    nproc = nproc_override
132    puts "Overriding make parallelism to #{nproc}"
133  end
134  make = bsd ? 'gmake' : 'make'
135  cmd = "#{make} -j#{nproc} -C #{grpc_root} #{grpc_lib_dir}/libgrpc.a CONFIG=#{grpc_config} Q="
136  puts "Building grpc native library: #{cmd}"
137  system(cmd)
138  exit 1 unless $? == 0
139end
140
141# C-core built, generate Makefile for ruby extension
142$LDFLAGS = maybe_remove_strip_all_linker_flag($LDFLAGS)
143$DLDFLAGS = maybe_remove_strip_all_linker_flag($DLDFLAGS)
144
145$CFLAGS << ' -DGRPC_RUBY_WINDOWS_UCRT' if windows_ucrt
146$CFLAGS << ' -I' + File.join(grpc_root, 'include')
147$CFLAGS << ' -g'
148
149def have_ruby_abi_version()
150  return true if RUBY_ENGINE == 'truffleruby'
151  # ruby_abi_version is only available in development versions: https://github.com/ruby/ruby/pull/6231
152  # See also discussion for Ruby 3.4 in https://github.com/grpc/grpc/pull/38338 and https://github.com/grpc/grpc/pull/38487
153  return false if RUBY_PATCHLEVEL >= 0
154
155  m = /(\d+)\.(\d+)/.match(RUBY_VERSION)
156  if m.nil?
157    puts "Failed to parse ruby version: #{RUBY_VERSION}. Assuming ruby_abi_version symbol is NOT present."
158    return false
159  end
160  major = m[1].to_i
161  minor = m[2].to_i
162  if major >= 3 and minor >= 2
163    puts "Ruby version #{RUBY_VERSION} >= 3.2. Assuming ruby_abi_version symbol is present."
164    return true
165  end
166  puts "Ruby version #{RUBY_VERSION} < 3.2. Assuming ruby_abi_version symbol is NOT present."
167  false
168end
169
170def ext_export_filename()
171  name = 'ext-export'
172  name += '-truffleruby' if RUBY_ENGINE == 'truffleruby'
173  name += '-with-ruby-abi-version' if have_ruby_abi_version()
174  name
175end
176
177ext_export_file = File.join(grpc_root, 'src', 'ruby', 'ext', 'grpc', ext_export_filename())
178$LDFLAGS << ' -Wl,--version-script="' + ext_export_file + '.gcc"' if linux
179if apple_toolchain
180  $LDFLAGS << ' -weak_framework CoreFoundation'
181  $LDFLAGS << ' -Wl,-exported_symbols_list,"' + ext_export_file + '.clang"'
182end
183
184$LDFLAGS << ' ' + File.join(grpc_lib_dir, 'libgrpc.a') unless windows
185if grpc_config == 'gcov'
186  $CFLAGS << ' -O0 -fprofile-arcs -ftest-coverage'
187  $LDFLAGS << ' -fprofile-arcs -ftest-coverage -rdynamic'
188end
189
190if grpc_config == 'dbg'
191  $CFLAGS << ' -O0'
192end
193
194# Do not statically link standard libraries on TruffleRuby as this does not work when compiling to bitcode
195if linux && RUBY_ENGINE != 'truffleruby'
196  $LDFLAGS << ' -static-libgcc -static-libstdc++'
197end
198$LDFLAGS << ' -static' if windows
199
200$CFLAGS << ' -std=c11 '
201$CFLAGS << ' -Wall '
202$CFLAGS << ' -Wextra '
203$CFLAGS << ' -pedantic '
204
205output = File.join('grpc', 'grpc_c')
206puts "extconf.rb $LDFLAGS: #{$LDFLAGS}"
207puts "extconf.rb $DLDFLAGS: #{$DLDFLAGS}"
208puts "extconf.rb $CFLAGS: #{$CFLAGS}"
209puts 'Generating Makefile for ' + output
210create_makefile(output)
211
212ruby_major_minor = /(\d+\.\d+)/.match(RUBY_VERSION).to_s
213debug_symbols = "grpc-#{GRPC::VERSION}-#{RUBY_PLATFORM}-ruby-#{ruby_major_minor}.dbg"
214
215File.open('Makefile.new', 'w') do |o|
216  o.puts 'hijack_remove_unused_artifacts: all remove_unused_artifacts'
217  o.puts
218  o.write(File.read('Makefile'))
219  o.puts
220  o.puts 'remove_unused_artifacts: $(DLLIB)'
221  # Now that the extension library has been linked, we can remove unused artifacts
222  # that take up a lot of disk space.
223  rm_obj_cmd = "rm -rf #{File.join(output_dir, 'objs')}"
224  o.puts "\t$(ECHO) Removing unused object artifacts: #{rm_obj_cmd}"
225  o.puts "\t$(Q) #{rm_obj_cmd}"
226  rm_grpc_core_libs = "rm -f #{grpc_lib_dir}/*.a"
227  o.puts "\t$(ECHO) Removing unused grpc core libraries: #{rm_grpc_core_libs}"
228  o.puts "\t$(Q) #{rm_grpc_core_libs}"
229end
230File.rename('Makefile.new', 'Makefile')
231
232if grpc_config == 'opt'
233  File.open('Makefile.new', 'w') do |o|
234    o.puts 'hijack: all strip'
235    o.puts
236    o.write(File.read('Makefile'))
237    o.puts
238    o.puts 'strip: $(DLLIB)'
239    if debug_symbols_output_dir
240      o.puts "\t$(ECHO) Saving debug symbols in #{debug_symbols_output_dir}/#{debug_symbols}"
241      o.puts "\t$(Q) objcopy --only-keep-debug $(DLLIB) #{debug_symbols_output_dir}/#{debug_symbols}"
242    end
243    o.puts "\t$(ECHO) Stripping $(DLLIB)"
244    o.puts "\t$(Q) #{strip_tool} $(DLLIB)"
245  end
246  File.rename('Makefile.new', 'Makefile')
247end
248
249if ENV['GRPC_RUBY_TEST_ONLY_WORKAROUND_MAKE_INSTALL_BUG']
250  # Note: this env var setting is intended to work around a problem observed
251  # with the ginstall command on grpc's macos automated test infrastructure,
252  # and is not  guaranteed to work in the wild.
253  # Also see https://github.com/rake-compiler/rake-compiler/issues/210.
254  puts 'Overriding the generated Makefile install target to use cp'
255  File.open('Makefile.new', 'w') do |o|
256    File.foreach('Makefile') do |i|
257      if i.start_with?('INSTALL_PROG = ')
258        override = 'INSTALL_PROG = cp'
259        puts "Replacing generated Makefile line: |#{i}|, with: |#{override}|"
260        o.puts override
261      else
262        o.puts i
263      end
264    end
265  end
266  File.rename('Makefile.new', 'Makefile')
267end
268