re2 1.7.0 → 2.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/ext/re2/extconf.rb CHANGED
@@ -5,6 +5,107 @@
5
5
  # Released under the BSD Licence, please see LICENSE.txt
6
6
 
7
7
  require 'mkmf'
8
+ require_relative 'recipes'
9
+
10
+ RE2_HELP_MESSAGE = <<~HELP
11
+ USAGE: ruby #{$0} [options]
12
+
13
+ Flags that are always valid:
14
+
15
+ --enable-system-libraries
16
+ Use system libraries instead of building and using the packaged libraries.
17
+
18
+ --disable-system-libraries
19
+ Use the packaged libraries, and ignore the system libraries. This is the default.
20
+
21
+
22
+ Flags only used when using system libraries:
23
+
24
+ Related to re2 library:
25
+
26
+ --with-re2-dir=DIRECTORY
27
+ Look for re2 headers and library in DIRECTORY.
28
+
29
+
30
+ Flags only used when building and using the packaged libraries:
31
+
32
+ --enable-cross-build
33
+ Enable cross-build mode. (You probably do not want to set this manually.)
34
+
35
+
36
+ Environment variables used:
37
+
38
+ CC
39
+ Use this path to invoke the compiler instead of `RbConfig::CONFIG['CC']`
40
+
41
+ CPPFLAGS
42
+ If this string is accepted by the C preprocessor, add it to the flags passed to the C preprocessor
43
+
44
+ CFLAGS
45
+ If this string is accepted by the compiler, add it to the flags passed to the compiler
46
+
47
+ LDFLAGS
48
+ If this string is accepted by the linker, add it to the flags passed to the linker
49
+
50
+ LIBS
51
+ Add this string to the flags passed to the linker
52
+ HELP
53
+
54
+ #
55
+ # utility functions
56
+ #
57
+ def config_system_libraries?
58
+ enable_config("system-libraries", ENV.key?('RE2_USE_SYSTEM_LIBRARIES'))
59
+ end
60
+
61
+ def config_cross_build?
62
+ enable_config("cross-build")
63
+ end
64
+
65
+ def concat_flags(*args)
66
+ args.compact.join(" ")
67
+ end
68
+
69
+ def do_help
70
+ print(RE2_HELP_MESSAGE)
71
+ exit!(0)
72
+ end
73
+
74
+ def darwin?
75
+ RbConfig::CONFIG["target_os"].include?("darwin")
76
+ end
77
+
78
+ def windows?
79
+ RbConfig::CONFIG["target_os"].match?(/mingw|mswin/)
80
+ end
81
+
82
+ def freebsd?
83
+ RbConfig::CONFIG["target_os"].include?("freebsd")
84
+ end
85
+
86
+ def target_host
87
+ # We use 'host' to set compiler prefix for cross-compiling. Prefer host_alias over host. And
88
+ # prefer i686 (what external dev tools use) to i386 (what ruby's configure.ac emits).
89
+ host = RbConfig::CONFIG["host_alias"].empty? ? RbConfig::CONFIG["host"] : RbConfig::CONFIG["host_alias"]
90
+ host.gsub(/i386/, "i686")
91
+ end
92
+
93
+ def target_arch
94
+ RbConfig::CONFIG['arch']
95
+ end
96
+
97
+ def with_temp_dir
98
+ Dir.mktmpdir do |temp_dir|
99
+ Dir.chdir(temp_dir) do
100
+ yield
101
+ end
102
+ end
103
+ end
104
+
105
+ #
106
+ # main
107
+ #
108
+ do_help if arg_config('--help')
8
109
 
9
110
  if ENV["CC"]
10
111
  RbConfig::MAKEFILE_CONFIG["CC"] = ENV["CC"]
@@ -16,70 +117,58 @@ if ENV["CXX"]
16
117
  RbConfig::CONFIG["CXX"] = ENV["CXX"]
17
118
  end
18
119
 
19
- header_dirs = [
20
- "/usr/local/include",
21
- "/opt/homebrew/include",
22
- "/usr/include"
23
- ]
24
-
25
- lib_dirs = [
26
- "/usr/local/lib",
27
- "/opt/homebrew/lib",
28
- "/usr/lib"
29
- ]
30
-
31
- dir_config("re2", header_dirs, lib_dirs)
120
+ def build_extension(static_p = false)
121
+ # Enable optional warnings but disable deprecated register warning for Ruby 2.6 support
122
+ $CFLAGS << " -Wall -Wextra -funroll-loops"
123
+ $CPPFLAGS << " -Wno-register"
32
124
 
33
- $CFLAGS << " -Wall -Wextra -funroll-loops"
125
+ # Pass -x c++ to force gcc to compile the test program
126
+ # as C++ (as it will end in .c by default).
127
+ compile_options = "-x c++"
34
128
 
35
- # Pass -x c++ to force gcc to compile the test program
36
- # as C++ (as it will end in .c by default).
37
- compile_options = "-x c++"
129
+ have_library("stdc++")
130
+ have_header("stdint.h")
38
131
 
39
- have_library("stdc++")
40
- have_header("stdint.h")
41
- have_func("rb_str_sublen")
42
-
43
- unless have_library("re2")
44
- abort "You must have re2 installed and specified with --with-re2-dir, please see https://github.com/google/re2/wiki/Install"
45
- end
132
+ if !static_p and !have_library("re2")
133
+ abort "You must have re2 installed and specified with --with-re2-dir, please see https://github.com/google/re2/wiki/Install"
134
+ end
46
135
 
47
- minimal_program = <<SRC
136
+ minimal_program = <<SRC
48
137
  #include <re2/re2.h>
49
138
  int main() { return 0; }
50
139
  SRC
51
140
 
52
- re2_requires_version_flag = checking_for("re2 that requires explicit C++ version flag") do
53
- !try_compile(minimal_program, compile_options)
54
- end
141
+ re2_requires_version_flag = checking_for("re2 that requires explicit C++ version flag") do
142
+ !try_compile(minimal_program, compile_options)
143
+ end
55
144
 
56
- if re2_requires_version_flag
57
- # Recent versions of re2 depend directly on abseil, which requires a
58
- # compiler with C++14 support (see
59
- # https://github.com/abseil/abseil-cpp/issues/1127 and
60
- # https://github.com/abseil/abseil-cpp/issues/1431). However, the
61
- # `std=c++14` flag doesn't appear to suffice; we need at least
62
- # `std=c++17`.
63
- abort "Cannot compile re2 with your compiler: recent versions require C++14 support." unless %w[c++20 c++17 c++11 c++0x].any? do |std|
64
- checking_for("re2 that compiles with #{std} standard") do
65
- if try_compile(minimal_program, compile_options + " -std=#{std}")
66
- compile_options << " -std=#{std}"
67
- $CPPFLAGS << " -std=#{std}"
145
+ if re2_requires_version_flag
146
+ # Recent versions of re2 depend directly on abseil, which requires a
147
+ # compiler with C++14 support (see
148
+ # https://github.com/abseil/abseil-cpp/issues/1127 and
149
+ # https://github.com/abseil/abseil-cpp/issues/1431). However, the
150
+ # `std=c++14` flag doesn't appear to suffice; we need at least
151
+ # `std=c++17`.
152
+ abort "Cannot compile re2 with your compiler: recent versions require C++14 support." unless %w[c++20 c++17 c++11 c++0x].any? do |std|
153
+ checking_for("re2 that compiles with #{std} standard") do
154
+ if try_compile(minimal_program, compile_options + " -std=#{std}")
155
+ compile_options << " -std=#{std}"
156
+ $CPPFLAGS << " -std=#{std}"
68
157
 
69
- true
158
+ true
159
+ end
70
160
  end
71
161
  end
72
162
  end
73
- end
74
163
 
75
- # Determine which version of re2 the user has installed.
76
- # Revision d9f8806c004d added an `endpos` argument to the
77
- # generic Match() function.
78
- #
79
- # To test for this, try to compile a simple program that uses
80
- # the newer form of Match() and set a flag if it is successful.
81
- checking_for("RE2::Match() with endpos argument") do
82
- test_re2_match_signature = <<SRC
164
+ # Determine which version of re2 the user has installed.
165
+ # Revision d9f8806c004d added an `endpos` argument to the
166
+ # generic Match() function.
167
+ #
168
+ # To test for this, try to compile a simple program that uses
169
+ # the newer form of Match() and set a flag if it is successful.
170
+ checking_for("RE2::Match() with endpos argument") do
171
+ test_re2_match_signature = <<SRC
83
172
  #include <re2/re2.h>
84
173
 
85
174
  int main() {
@@ -91,13 +180,13 @@ int main() {
91
180
  }
92
181
  SRC
93
182
 
94
- if try_compile(test_re2_match_signature, compile_options)
95
- $defs.push("-DHAVE_ENDPOS_ARGUMENT")
183
+ if try_compile(test_re2_match_signature, compile_options)
184
+ $defs.push("-DHAVE_ENDPOS_ARGUMENT")
185
+ end
96
186
  end
97
- end
98
187
 
99
- checking_for("RE2::Set::Match() with error information") do
100
- test_re2_set_match_signature = <<SRC
188
+ checking_for("RE2::Set::Match() with error information") do
189
+ test_re2_set_match_signature = <<SRC
101
190
  #include <vector>
102
191
  #include <re2/re2.h>
103
192
  #include <re2/set.h>
@@ -115,9 +204,234 @@ int main() {
115
204
  }
116
205
  SRC
117
206
 
118
- if try_compile(test_re2_set_match_signature, compile_options)
119
- $defs.push("-DHAVE_ERROR_INFO_ARGUMENT")
207
+ if try_compile(test_re2_set_match_signature, compile_options)
208
+ $defs.push("-DHAVE_ERROR_INFO_ARGUMENT")
209
+ end
120
210
  end
121
211
  end
122
212
 
213
+ def process_recipe(recipe)
214
+ cross_build_p = config_cross_build?
215
+ message "Cross build is #{cross_build_p ? "enabled" : "disabled"}.\n"
216
+
217
+ recipe.host = target_host
218
+ # Ensure x64-mingw-ucrt and x64-mingw32 use different library paths since the host
219
+ # is the same (x86_64-w64-mingw32).
220
+ recipe.target = File.join(recipe.target, target_arch) if cross_build_p
221
+
222
+ yield recipe
223
+
224
+ checkpoint = "#{recipe.target}/#{recipe.name}-#{recipe.version}-#{recipe.host}.installed"
225
+ name = recipe.name
226
+ version = recipe.version
227
+
228
+ if File.exist?(checkpoint)
229
+ message("Building re2 with a packaged version of #{name}-#{version}.\n")
230
+ else
231
+ message(<<~EOM)
232
+ ---------- IMPORTANT NOTICE ----------
233
+ Building re2 with a packaged version of #{name}-#{version}.
234
+ Configuration options: #{recipe.configure_options.shelljoin}
235
+ EOM
236
+
237
+ unless recipe.patch_files.empty?
238
+ message("The following patches are being applied:\n")
239
+
240
+ recipe.patch_files.each do |patch|
241
+ message(" - %s\n" % File.basename(patch))
242
+ end
243
+ end
244
+
245
+ # Use a temporary base directory to reduce filename lengths since
246
+ # Windows can hit a limit of 250 characters (CMAKE_OBJECT_PATH_MAX).
247
+ with_temp_dir { recipe.cook }
248
+
249
+ FileUtils.touch(checkpoint)
250
+ end
251
+
252
+ recipe.activate
253
+ end
254
+
255
+ def build_with_system_libraries
256
+ header_dirs = [
257
+ "/usr/local/include",
258
+ "/opt/homebrew/include",
259
+ "/usr/include"
260
+ ]
261
+
262
+ lib_dirs = [
263
+ "/usr/local/lib",
264
+ "/opt/homebrew/lib",
265
+ "/usr/lib"
266
+ ]
267
+
268
+ dir_config("re2", header_dirs, lib_dirs)
269
+
270
+ build_extension
271
+ end
272
+
273
+ # pkgconf v1.9.3 on Windows incorrectly sorts the output of `pkg-config
274
+ # --libs --static`, resulting in build failures: https://github.com/pkgconf/pkgconf/issues/268.
275
+ # To work around the issue, store the correct order of abseil flags here and add them manually
276
+ # for Windows.
277
+ #
278
+ # Note that `-ldbghelp` is incorrectly added before `-labsl_symbolize` in abseil:
279
+ # https://github.com/abseil/abseil-cpp/issues/1497
280
+ ABSL_LDFLAGS = %w[
281
+ -labsl_flags
282
+ -labsl_flags_internal
283
+ -labsl_flags_marshalling
284
+ -labsl_flags_reflection
285
+ -labsl_flags_private_handle_accessor
286
+ -labsl_flags_commandlineflag
287
+ -labsl_flags_commandlineflag_internal
288
+ -labsl_flags_config
289
+ -labsl_flags_program_name
290
+ -labsl_cord
291
+ -labsl_cordz_info
292
+ -labsl_cord_internal
293
+ -labsl_cordz_functions
294
+ -labsl_cordz_handle
295
+ -labsl_crc_cord_state
296
+ -labsl_crc32c
297
+ -labsl_crc_internal
298
+ -labsl_crc_cpu_detect
299
+ -labsl_hash
300
+ -labsl_city
301
+ -labsl_bad_variant_access
302
+ -labsl_low_level_hash
303
+ -labsl_raw_hash_set
304
+ -labsl_hashtablez_sampler
305
+ -labsl_exponential_biased
306
+ -labsl_bad_optional_access
307
+ -labsl_str_format_internal
308
+ -labsl_synchronization
309
+ -labsl_graphcycles_internal
310
+ -labsl_stacktrace
311
+ -labsl_symbolize
312
+ -ldbghelp
313
+ -labsl_debugging_internal
314
+ -labsl_demangle_internal
315
+ -labsl_malloc_internal
316
+ -labsl_time
317
+ -labsl_civil_time
318
+ -labsl_strings
319
+ -labsl_strings_internal
320
+ -ladvapi32
321
+ -labsl_base
322
+ -labsl_spinlock_wait
323
+ -labsl_int128
324
+ -labsl_throw_delegate
325
+ -labsl_raw_logging_internal
326
+ -labsl_log_severity
327
+ -labsl_time_zone
328
+ ].freeze
329
+
330
+ def libflag_to_filename(ldflag)
331
+ case ldflag
332
+ when /\A-l(.+)/
333
+ "lib#{Regexp.last_match(1)}.#{$LIBEXT}"
334
+ end
335
+ end
336
+
337
+ # This method does a number of things to ensure the final shared library
338
+ # is compiled statically with the vendored libraries:
339
+ #
340
+ # 1. For -L<path> flags, ensure that any `ports` paths are prioritized just
341
+ # in case there are installed libraries that might take precedence.
342
+ # 2. For -l<lib> flags, convert the library to the static library with a
343
+ # full path and substitute the absolute static library. For example,
344
+ # -lre2 maps to /path/to/ports/<arch>/libre2/<version>/lib/libre2.a.
345
+ #
346
+ # This is needed because when building the extension, Ruby appears to
347
+ # insert `-L#{RbConfig::CONFIG['exec_prefix']}/lib` first. If libre2 is
348
+ # in installed in that location then the extension will link against the
349
+ # system library instead of the vendored library.
350
+ def add_flag(arg, lib_paths)
351
+ case arg
352
+ when /\A-L(.+)\z/
353
+ # Prioritize ports' directories
354
+ lib_dir = Regexp.last_match(1)
355
+ $LIBPATH =
356
+ if lib_dir.start_with?(PACKAGE_ROOT_DIR + "/")
357
+ [lib_dir] | $LIBPATH
358
+ else
359
+ $LIBPATH | [lib_dir]
360
+ end
361
+ when /\A-l./
362
+ filename = libflag_to_filename(arg)
363
+
364
+ added = false
365
+ lib_paths.each do |path|
366
+ static_lib = File.join(path, filename)
367
+
368
+ next unless File.exist?(static_lib)
369
+
370
+ $LDFLAGS << " " << static_lib
371
+ added = true
372
+ break
373
+ end
374
+
375
+ append_ldflags(arg.shellescape) unless added
376
+ else
377
+ append_ldflags(arg.shellescape)
378
+ end
379
+ end
380
+
381
+ def add_static_ldflags(flags, lib_paths)
382
+ static_flags = flags.strip.shellsplit
383
+
384
+ if MiniPortile.windows?
385
+ static_flags.each { |flag| add_flag(flag, lib_paths) unless ABSL_LDFLAGS.include?(flag) }
386
+ ABSL_LDFLAGS.each { |flag| add_flag(flag, lib_paths) }
387
+ else
388
+ static_flags.each { |flag| add_flag(flag, lib_paths) }
389
+ end
390
+ end
391
+
392
+ def build_with_vendored_libraries
393
+ message "Building re2 using packaged libraries.\n"
394
+
395
+ abseil_recipe, re2_recipe = load_recipes
396
+
397
+ process_recipe(abseil_recipe) do |recipe|
398
+ recipe.configure_options += ['-DABSL_PROPAGATE_CXX_STD=ON', '-DCMAKE_CXX_VISIBILITY_PRESET=hidden']
399
+ end
400
+
401
+ process_recipe(re2_recipe) do |recipe|
402
+ recipe.configure_options += ["-DCMAKE_PREFIX_PATH=#{abseil_recipe.path}", '-DCMAKE_CXX_FLAGS=-DNDEBUG',
403
+ '-DCMAKE_CXX_VISIBILITY_PRESET=hidden']
404
+ end
405
+
406
+ dir_config("re2", File.join(re2_recipe.path, 'include'), File.join(re2_recipe.path, 'lib'))
407
+ dir_config("abseil", File.join(abseil_recipe.path, 'include'), File.join(abseil_recipe.path, 'lib'))
408
+
409
+ pkg_config_paths = [
410
+ "#{abseil_recipe.path}/lib/pkgconfig",
411
+ "#{re2_recipe.path}/lib/pkgconfig"
412
+ ].join(File::PATH_SEPARATOR)
413
+
414
+ pkg_config_paths = "#{ENV['PKG_CONFIG_PATH']}#{File::PATH_SEPARATOR}#{pkg_config_paths}" if ENV['PKG_CONFIG_PATH']
415
+
416
+ ENV['PKG_CONFIG_PATH'] = pkg_config_paths
417
+ pc_file = File.join(re2_recipe.path, 'lib', 'pkgconfig', 're2.pc')
418
+
419
+ raise 'Please install the `pkg-config` utility!' unless find_executable('pkg-config')
420
+
421
+ # See https://bugs.ruby-lang.org/issues/18490, broken in Ruby 3.1 but fixed in Ruby 3.2.
422
+ flags = xpopen(['pkg-config', '--libs', '--static', pc_file], err: %i[child out], &:read)
423
+
424
+ raise 'Unable to run pkg-config --libs --static' unless $?.success?
425
+
426
+ lib_paths = [File.join(abseil_recipe.path, 'lib'), File.join(re2_recipe.path, 'lib')]
427
+ add_static_ldflags(flags, lib_paths)
428
+ build_extension(true)
429
+ end
430
+
431
+ if config_system_libraries?
432
+ build_with_system_libraries
433
+ else
434
+ build_with_vendored_libraries
435
+ end
436
+
123
437
  create_makefile("re2")