|
6 | 6 |
|
7 | 7 | require 'mkmf'
|
8 | 8 |
|
| 9 | +PACKAGE_ROOT_DIR = File.expand_path(File.join(File.dirname(__FILE__), '..', '..')) |
| 10 | + |
| 11 | +REQUIRED_MINI_PORTILE_VERSION = "~> 2.8.2" # keep this version in sync with the one in the gemspec |
| 12 | + |
| 13 | +RE2_HELP_MESSAGE = <<~HELP |
| 14 | + USAGE: ruby #{$0} [options] |
| 15 | +
|
| 16 | + Flags that are always valid: |
| 17 | +
|
| 18 | + --use-system-libraries |
| 19 | + --enable-system-libraries |
| 20 | + Use system libraries instead of building and using the packaged libraries. This is the default. |
| 21 | +
|
| 22 | + --disable-system-libraries |
| 23 | + Use the packaged libraries, and ignore the system libraries. This overrides `--use-system-libraries`. |
| 24 | +
|
| 25 | + Flags only used when using system libraries: |
| 26 | +
|
| 27 | + Related to re2 library: |
| 28 | +
|
| 29 | + --with-re2-dir=DIRECTORY |
| 30 | + Look for re2 headers and library in DIRECTORY. |
| 31 | +
|
| 32 | + Environment variables used: |
| 33 | +
|
| 34 | + CC |
| 35 | + Use this path to invoke the compiler instead of `RbConfig::CONFIG['CC']` |
| 36 | +
|
| 37 | + CPPFLAGS |
| 38 | + If this string is accepted by the C preprocessor, add it to the flags passed to the C preprocessor |
| 39 | +
|
| 40 | + CFLAGS |
| 41 | + If this string is accepted by the compiler, add it to the flags passed to the compiler |
| 42 | +
|
| 43 | + LDFLAGS |
| 44 | + If this string is accepted by the linker, add it to the flags passed to the linker |
| 45 | +
|
| 46 | + LIBS |
| 47 | + Add this string to the flags passed to the linker |
| 48 | +HELP |
| 49 | + |
| 50 | +# |
| 51 | +# utility functions |
| 52 | +# |
| 53 | +def config_system_libraries? |
| 54 | + enable_config("system-libraries", true) do |_, default| |
| 55 | + arg_config("--use-system-libraries", default) |
| 56 | + end |
| 57 | +end |
| 58 | + |
| 59 | +def concat_flags(*args) |
| 60 | + args.compact.join(" ") |
| 61 | +end |
| 62 | + |
| 63 | +def do_help |
| 64 | + print(RE2_HELP_MESSAGE) |
| 65 | + exit!(0) |
| 66 | +end |
| 67 | + |
| 68 | +# |
| 69 | +# main |
| 70 | +# |
| 71 | +do_help if arg_config('--help') |
| 72 | + |
9 | 73 | if ENV["CC"]
|
10 | 74 | RbConfig::MAKEFILE_CONFIG["CC"] = ENV["CC"]
|
11 | 75 | RbConfig::CONFIG["CC"] = ENV["CC"]
|
|
16 | 80 | RbConfig::CONFIG["CXX"] = ENV["CXX"]
|
17 | 81 | end
|
18 | 82 |
|
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 |
| -] |
| 83 | +def build_extension |
| 84 | + $CFLAGS << " -Wall -Wextra -funroll-loops" |
30 | 85 |
|
31 |
| -dir_config("re2", header_dirs, lib_dirs) |
| 86 | + # Pass -x c++ to force gcc to compile the test program |
| 87 | + # as C++ (as it will end in .c by default). |
| 88 | + compile_options = "-x c++" |
32 | 89 |
|
33 |
| -$CFLAGS << " -Wall -Wextra -funroll-loops" |
| 90 | + have_library("stdc++") |
| 91 | + have_header("stdint.h") |
| 92 | + have_func("rb_str_sublen") |
34 | 93 |
|
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++" |
38 |
| - |
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 |
| 94 | + unless have_library("re2") |
| 95 | + abort "You must have re2 installed and specified with --with-re2-dir, please see https://github.com/google/re2/wiki/Install" |
| 96 | + end |
46 | 97 |
|
47 |
| -minimal_program = <<SRC |
| 98 | + minimal_program = <<SRC |
48 | 99 | #include <re2/re2.h>
|
49 | 100 | int main() { return 0; }
|
50 | 101 | SRC
|
51 | 102 |
|
52 |
| -re2_requires_version_flag = checking_for("re2 that requires explicit C++ version flag") do |
53 |
| - !try_compile(minimal_program, compile_options) |
54 |
| -end |
| 103 | + re2_requires_version_flag = checking_for("re2 that requires explicit C++ version flag") do |
| 104 | + !try_compile(minimal_program, compile_options) |
| 105 | + end |
| 106 | + |
| 107 | + if re2_requires_version_flag |
| 108 | + # Recent versions of re2 depend directly on abseil, which requires a |
| 109 | + # compiler with C++14 support (see |
| 110 | + # https://github.com/abseil/abseil-cpp/issues/1127 and |
| 111 | + # https://github.com/abseil/abseil-cpp/issues/1431). However, the |
| 112 | + # `std=c++14` flag doesn't appear to suffice; we need at least |
| 113 | + # `std=c++17`. |
| 114 | + 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| |
| 115 | + checking_for("re2 that compiles with #{std} standard") do |
| 116 | + if try_compile(minimal_program, compile_options + " -std=#{std}") |
| 117 | + compile_options << " -std=#{std}" |
| 118 | + $CPPFLAGS << " -std=#{std}" |
55 | 119 |
|
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}" |
68 |
| - |
69 |
| - true |
| 120 | + true |
| 121 | + end |
70 | 122 | end
|
71 | 123 | end
|
72 | 124 | end
|
73 |
| -end |
74 | 125 |
|
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 |
| 126 | + # Determine which version of re2 the user has installed. |
| 127 | + # Revision d9f8806c004d added an `endpos` argument to the |
| 128 | + # generic Match() function. |
| 129 | + # |
| 130 | + # To test for this, try to compile a simple program that uses |
| 131 | + # the newer form of Match() and set a flag if it is successful. |
| 132 | + checking_for("RE2::Match() with endpos argument") do |
| 133 | + test_re2_match_signature = <<SRC |
83 | 134 | #include <re2/re2.h>
|
84 | 135 |
|
85 | 136 | int main() {
|
|
91 | 142 | }
|
92 | 143 | SRC
|
93 | 144 |
|
94 |
| - if try_compile(test_re2_match_signature, compile_options) |
95 |
| - $defs.push("-DHAVE_ENDPOS_ARGUMENT") |
| 145 | + if try_compile(test_re2_match_signature, compile_options) |
| 146 | + $defs.push("-DHAVE_ENDPOS_ARGUMENT") |
| 147 | + end |
96 | 148 | end
|
97 |
| -end |
98 | 149 |
|
99 |
| -checking_for("RE2::Set::Match() with error information") do |
100 |
| - test_re2_set_match_signature = <<SRC |
| 150 | + checking_for("RE2::Set::Match() with error information") do |
| 151 | + test_re2_set_match_signature = <<SRC |
101 | 152 | #include <vector>
|
102 | 153 | #include <re2/re2.h>
|
103 | 154 | #include <re2/set.h>
|
|
115 | 166 | }
|
116 | 167 | SRC
|
117 | 168 |
|
118 |
| - if try_compile(test_re2_set_match_signature, compile_options) |
119 |
| - $defs.push("-DHAVE_ERROR_INFO_ARGUMENT") |
| 169 | + if try_compile(test_re2_set_match_signature, compile_options) |
| 170 | + $defs.push("-DHAVE_ERROR_INFO_ARGUMENT") |
| 171 | + end |
120 | 172 | end
|
121 | 173 | end
|
122 | 174 |
|
| 175 | +def process_recipe(name, version) |
| 176 | + require "rubygems" |
| 177 | + gem("mini_portile2", REQUIRED_MINI_PORTILE_VERSION) # gemspec is not respected at install time |
| 178 | + require "mini_portile2" |
| 179 | + message("Using mini_portile version #{MiniPortile::VERSION}\n") |
| 180 | + |
| 181 | + MiniPortileCMake.new(name, version).tap do |recipe| |
| 182 | + recipe.target = File.join(PACKAGE_ROOT_DIR, "ports") |
| 183 | + recipe.configure_options += ['-DCMAKE_CXX_STANDARD=17', '-DCMAKE_POSITION_INDEPENDENT_CODE=ON'] |
| 184 | + |
| 185 | + yield recipe |
| 186 | + |
| 187 | + checkpoint = "#{recipe.target}/#{recipe.name}-#{recipe.version}-#{recipe.host}.installed" |
| 188 | + |
| 189 | + if File.exist?(checkpoint) |
| 190 | + message("Building re2 with a packaged version of #{name}-#{version}.\n") |
| 191 | + else |
| 192 | + message(<<~EOM) |
| 193 | + ---------- IMPORTANT NOTICE ---------- |
| 194 | + Building re2 with a packaged version of #{name}-#{version}. |
| 195 | + Configuration options: #{recipe.configure_options.shelljoin} |
| 196 | + EOM |
| 197 | + |
| 198 | + unless recipe.patch_files.empty? |
| 199 | + message("The following patches are being applied:\n") |
| 200 | + |
| 201 | + recipe.patch_files.each do |patch| |
| 202 | + message(" - %s\n" % File.basename(patch)) |
| 203 | + end |
| 204 | + end |
| 205 | + |
| 206 | + recipe.cook |
| 207 | + |
| 208 | + FileUtils.touch(checkpoint) |
| 209 | + end |
| 210 | + |
| 211 | + recipe.activate |
| 212 | + end |
| 213 | +end |
| 214 | + |
| 215 | +def build_with_system_libraries |
| 216 | + header_dirs = [ |
| 217 | + "/usr/local/include", |
| 218 | + "/opt/homebrew/include", |
| 219 | + "/usr/include" |
| 220 | + ] |
| 221 | + |
| 222 | + lib_dirs = [ |
| 223 | + "/usr/local/lib", |
| 224 | + "/opt/homebrew/lib", |
| 225 | + "/usr/lib" |
| 226 | + ] |
| 227 | + |
| 228 | + dir_config("re2", header_dirs, lib_dirs) |
| 229 | + |
| 230 | + build_extension |
| 231 | +end |
| 232 | + |
| 233 | +def build_with_vendored_libraries |
| 234 | + message "Building re2 using packaged libraries.\n" |
| 235 | + |
| 236 | + require 'yaml' |
| 237 | + dependencies = YAML.load_file(File.join(PACKAGE_ROOT_DIR, 'dependencies.yml')) |
| 238 | + |
| 239 | + abseil_recipe = process_recipe('abseil', dependencies['abseil']['version']) do |recipe| |
| 240 | + recipe.files = [{ |
| 241 | + url: "https://github.com/abseil/abseil-cpp/archive/refs/tags/#{recipe.version}.tar.gz", |
| 242 | + sha256: dependencies['abseil']['sha256'] |
| 243 | + }] |
| 244 | + recipe.configure_options += ['-DABSL_PROPAGATE_CXX_STD=ON'] |
| 245 | + end |
| 246 | + |
| 247 | + re2_recipe = process_recipe('libre2', dependencies['libre2']['version']) do |recipe| |
| 248 | + recipe.files = [{ |
| 249 | + url: "https://github.com/google/re2/releases/download/#{recipe.version}/re2-#{recipe.version}.tar.gz", |
| 250 | + sha256: dependencies['libre2']['sha256'] |
| 251 | + }] |
| 252 | + recipe.configure_options += ["-DCMAKE_PREFIX_PATH=#{abseil_recipe.path}", '-DCMAKE_CXX_FLAGS=-DNDEBUG'] |
| 253 | + end |
| 254 | + |
| 255 | + recipes = [abseil_recipe, re2_recipe] |
| 256 | + include_dirs = recipes.map { |recipe| File.join(recipe.path, 'include') } |
| 257 | + lib_dirs = recipes.map { |recipe| File.join(recipe.path, 'lib') } |
| 258 | + dir_config('re2', include_dirs, lib_dirs) |
| 259 | + |
| 260 | + pkg_config_paths = [ |
| 261 | + "#{abseil_recipe.path}/lib/pkgconfig", |
| 262 | + "#{re2_recipe.path}/lib/pkgconfig" |
| 263 | + ].join(':') |
| 264 | + |
| 265 | + pkg_config_paths = "#{ENV['PKG_CONFIG_PATH']}:#{pkg_config_paths}" if ENV['PKG_CONFIG_PATH'] |
| 266 | + |
| 267 | + ENV['PKG_CONFIG_PATH'] = pkg_config_paths |
| 268 | + pc_file = File.join(re2_recipe.path, 'lib', 'pkgconfig', 're2.pc') |
| 269 | + if pkg_config(pc_file) |
| 270 | + # See https://bugs.ruby-lang.org/issues/18490, broken in Ruby 3.1 but fixed in Ruby 3.2. |
| 271 | + flags = xpopen(['pkg-config', '--libs', '--static', pc_file], err: %i[child out], &:read) |
| 272 | + flags.split.each { |flag| append_ldflags(flag) } if $?.success? |
| 273 | + else |
| 274 | + raise 'Please install the `pkg-config` utility!' |
| 275 | + end |
| 276 | + |
| 277 | + build_extension |
| 278 | +end |
| 279 | + |
| 280 | +if config_system_libraries? |
| 281 | + build_with_system_libraries |
| 282 | +else |
| 283 | + build_with_vendored_libraries |
| 284 | +end |
| 285 | + |
123 | 286 | create_makefile("re2")
|
0 commit comments