itsi-server 0.2.21 → 0.2.22

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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/Cargo.lock +25 -38
  3. data/Cargo.toml +4 -0
  4. data/Rakefile +39 -7
  5. data/ext/itsi_scheduler/Cargo.toml +1 -1
  6. data/ext/itsi_server/Cargo.toml +1 -1
  7. data/ext/itsi_server/src/server/signal.rs +7 -5
  8. data/lib/itsi/server/native_extension.rb +34 -0
  9. data/lib/itsi/server/version.rb +1 -1
  10. data/lib/itsi/server.rb +10 -2
  11. data/vendor/rb-sys-build/.cargo-ok +1 -0
  12. data/vendor/rb-sys-build/.cargo_vcs_info.json +6 -0
  13. data/vendor/rb-sys-build/Cargo.lock +294 -0
  14. data/vendor/rb-sys-build/Cargo.toml +71 -0
  15. data/vendor/rb-sys-build/Cargo.toml.orig +32 -0
  16. data/vendor/rb-sys-build/LICENSE-APACHE +190 -0
  17. data/vendor/rb-sys-build/LICENSE-MIT +21 -0
  18. data/vendor/rb-sys-build/src/bindings/sanitizer.rs +185 -0
  19. data/vendor/rb-sys-build/src/bindings/stable_api.rs +247 -0
  20. data/vendor/rb-sys-build/src/bindings/wrapper.h +71 -0
  21. data/vendor/rb-sys-build/src/bindings.rs +280 -0
  22. data/vendor/rb-sys-build/src/cc.rs +421 -0
  23. data/vendor/rb-sys-build/src/lib.rs +12 -0
  24. data/vendor/rb-sys-build/src/rb_config/flags.rs +101 -0
  25. data/vendor/rb-sys-build/src/rb_config/library.rs +132 -0
  26. data/vendor/rb-sys-build/src/rb_config/search_path.rs +57 -0
  27. data/vendor/rb-sys-build/src/rb_config.rs +906 -0
  28. data/vendor/rb-sys-build/src/utils.rs +53 -0
  29. metadata +25 -11
  30. data/ext/itsi_server/target/release/build/clang-sys-0dae18670e690c25/out/common.rs +0 -355
  31. data/ext/itsi_server/target/release/build/clang-sys-0dae18670e690c25/out/dynamic.rs +0 -276
  32. data/ext/itsi_server/target/release/build/clang-sys-0dae18670e690c25/out/macros.rs +0 -49
  33. data/ext/itsi_server/target/release/build/oid-registry-71b994a322b296ec/out/oid_db.rs +0 -537
  34. data/ext/itsi_server/target/release/build/rb-sys-9f9831ab50fb86db/out/bindings-0.9.124-mri-arm64-darwin24-2.7.8.rs +0 -6234
  35. data/ext/itsi_server/target/release/build/rb-sys-9f9831ab50fb86db/out/bindings-0.9.124-mri-arm64-darwin24-3.4.5.rs +0 -8936
  36. data/ext/itsi_server/target/release/build/rb-sys-9f9831ab50fb86db/out/bindings-0.9.124-mri-arm64-darwin24-4.0.1.rs +0 -9060
  37. data/ext/itsi_server/target/release/build/typenum-11265e44e46de3b7/out/tests.rs +0 -20563
@@ -0,0 +1,906 @@
1
+ use std::{
2
+ collections::{hash_map::Keys, HashMap},
3
+ env,
4
+ path::PathBuf,
5
+ process::Command,
6
+ };
7
+
8
+ use regex::Regex;
9
+ mod flags;
10
+ mod library;
11
+ mod search_path;
12
+
13
+ use library::*;
14
+ use search_path::*;
15
+ use std::ffi::OsString;
16
+
17
+ use crate::{
18
+ debug_log, memoize,
19
+ utils::{is_msvc, shellsplit},
20
+ };
21
+
22
+ use self::flags::Flags;
23
+
24
+ /// Extracts structured information from raw compiler/linker flags to make
25
+ /// compiling Ruby gems easier.
26
+ #[derive(Debug, PartialEq, Eq)]
27
+ pub struct RbConfig {
28
+ pub search_paths: Vec<SearchPath>,
29
+ pub libs: Vec<Library>,
30
+ pub link_args: Vec<String>,
31
+ pub cflags: Vec<String>,
32
+ pub blocklist_lib: Vec<String>,
33
+ pub blocklist_link_arg: Vec<String>,
34
+ use_rpath: bool,
35
+ value_map: HashMap<String, String>,
36
+ }
37
+
38
+ impl Default for RbConfig {
39
+ fn default() -> Self {
40
+ Self::new()
41
+ }
42
+ }
43
+
44
+ impl RbConfig {
45
+ /// Creates a new, blank `RbConfig`. You likely want to use `RbConfig::current()` instead.
46
+ pub(crate) fn new() -> RbConfig {
47
+ RbConfig {
48
+ blocklist_lib: vec![],
49
+ blocklist_link_arg: vec![],
50
+ search_paths: Vec::new(),
51
+ libs: Vec::new(),
52
+ link_args: Vec::new(),
53
+ cflags: Vec::new(),
54
+ value_map: HashMap::new(),
55
+ use_rpath: false,
56
+ }
57
+ }
58
+
59
+ /// All keys in the `RbConfig`'s value map.
60
+ pub fn all_keys(&self) -> Keys<'_, String, String> {
61
+ self.value_map.keys()
62
+ }
63
+
64
+ /// Instantiates a new `RbConfig` for the current Ruby.
65
+ pub fn current() -> RbConfig {
66
+ println!("cargo:rerun-if-env-changed=RUBY");
67
+
68
+ let mut rbconfig = RbConfig::new();
69
+
70
+ // Never use the current Ruby's RbConfig if we're cross compiling, or
71
+ // else bad things happen
72
+ let parsed = if rbconfig.is_cross_compiling() {
73
+ HashMap::new()
74
+ } else {
75
+ let output = memoize!(String: {
76
+ let ruby = env::var_os("RUBY").unwrap_or_else(|| OsString::from("ruby"));
77
+
78
+ let config = Command::new(ruby)
79
+ .arg("--disable-gems")
80
+ .arg("-rrbconfig")
81
+ .arg("-e")
82
+ .arg("print RbConfig::CONFIG.map {|kv| kv.join(\"\x1F\")}.join(\"\x1E\")")
83
+ .output()
84
+ .unwrap_or_else(|e| panic!("ruby not found: {}", e));
85
+ if !config.status.success() {
86
+ panic!("non-zero exit status while dumping RbConfig: {:?}", config);
87
+ }
88
+ String::from_utf8(config.stdout).expect("RbConfig value not UTF-8!")
89
+ });
90
+
91
+ let mut parsed = HashMap::new();
92
+ for line in output.split('\x1E') {
93
+ let mut parts = line.splitn(2, '\x1F');
94
+ if let (Some(key), Some(val)) = (parts.next(), parts.next()) {
95
+ parsed.insert(key.to_owned(), val.to_owned());
96
+ }
97
+ }
98
+ parsed
99
+ };
100
+
101
+ parsed.get("cflags").map(|f| rbconfig.push_cflags(f));
102
+ parsed.get("DLDFLAGS").map(|f| rbconfig.push_dldflags(f));
103
+
104
+ rbconfig.value_map = parsed;
105
+
106
+ rbconfig
107
+ }
108
+
109
+ /// Pushes the `LIBRUBYARG` flags so Ruby will be linked.
110
+ pub fn link_ruby(&mut self, is_static: bool) -> &mut Self {
111
+ let Some(libdir) = self.get("libdir") else {
112
+ return self;
113
+ };
114
+
115
+ self.push_search_path(libdir.as_str());
116
+ self.push_dldflags(&format!("-L{}", libdir));
117
+
118
+ let librubyarg = if is_static {
119
+ self.get("LIBRUBYARG_STATIC")
120
+ } else {
121
+ self.get("LIBRUBYARG_SHARED")
122
+ };
123
+
124
+ let librubyarg = match librubyarg {
125
+ Some(lib) => lib,
126
+ None => {
127
+ debug_log!("WARN: LIBRUBYARG not found in RbConfig, skipping linking Ruby");
128
+ return self;
129
+ }
130
+ };
131
+
132
+ if is_msvc() {
133
+ for lib in librubyarg.split_whitespace() {
134
+ self.push_library(lib);
135
+ }
136
+
137
+ let mut to_link: Vec<String> = vec![];
138
+
139
+ if let Some(libs) = self.get("LIBS") {
140
+ to_link.extend(libs.split_whitespace().map(|s| s.to_string()));
141
+ }
142
+
143
+ if let Some(libs) = self.get("LOCAL_LIBS") {
144
+ to_link.extend(libs.split_whitespace().map(|s| s.to_string()));
145
+ }
146
+
147
+ for lib in to_link {
148
+ self.push_library(lib);
149
+ }
150
+ } else {
151
+ self.push_dldflags(&librubyarg);
152
+
153
+ if cfg!(unix) {
154
+ self.use_rpath();
155
+ }
156
+ }
157
+
158
+ self
159
+ }
160
+
161
+ /// Get the name for libruby-static (i.e. `ruby.3.1-static`).
162
+ pub fn libruby_static_name(&self) -> String {
163
+ let Some(lib) = self.get("LIBRUBY_A") else {
164
+ return format!("{}-static", self.libruby_so_name());
165
+ };
166
+
167
+ lib.trim_start_matches("lib")
168
+ .trim_end_matches(".a")
169
+ .to_string()
170
+ }
171
+
172
+ /// Get the name for libruby (i.e. `ruby.3.1`)
173
+ pub fn libruby_so_name(&self) -> String {
174
+ self.get("RUBY_SO_NAME")
175
+ .unwrap_or_else(|| "ruby".to_string())
176
+ }
177
+
178
+ /// Get the platform for the current ruby.
179
+ pub fn platform(&self) -> String {
180
+ self.get("platform")
181
+ .unwrap_or_else(|| self.get("arch").expect("arch not found"))
182
+ }
183
+
184
+ /// Filter the libs, removing the ones that are not needed.
185
+ pub fn blocklist_lib(&mut self, name: &str) -> &mut RbConfig {
186
+ self.blocklist_lib.push(name.to_string());
187
+ self
188
+ }
189
+
190
+ /// Blocklist a link argument.
191
+ pub fn blocklist_link_arg(&mut self, name: &str) -> &mut RbConfig {
192
+ self.blocklist_link_arg.push(name.to_string());
193
+ self
194
+ }
195
+
196
+ /// Returns the current ruby program version.
197
+ pub fn ruby_version_slug(&self) -> String {
198
+ let ver = if let Some(progv) = self.get("RUBY_PROGRAM_VERSION") {
199
+ progv
200
+ } else if let Some(major_minor) = self.major_minor() {
201
+ format!(
202
+ "{}.{}.{}",
203
+ major_minor.0,
204
+ major_minor.1,
205
+ self.get("TEENY").unwrap_or_else(|| "0".to_string())
206
+ )
207
+ } else if let Some(fallback) = self.get("ruby_version") {
208
+ fallback
209
+ } else {
210
+ panic!("RUBY_PROGRAM_VERSION not found")
211
+ };
212
+
213
+ format!("{}-{}-{}", self.ruby_engine(), self.platform(), ver)
214
+ }
215
+
216
+ /// Get the CPPFLAGS from the RbConfig, making sure to subsitute variables.
217
+ pub fn cppflags(&self) -> Vec<String> {
218
+ if let Some(cppflags) = self.get("CPPFLAGS") {
219
+ let flags = self.subst_shell_variables(&cppflags);
220
+ shellsplit(flags)
221
+ } else {
222
+ vec![]
223
+ }
224
+ }
225
+
226
+ /// Returns true if the current Ruby is cross compiling.
227
+ pub fn is_cross_compiling(&self) -> bool {
228
+ if let Some(cross) = self.get("CROSS_COMPILING") {
229
+ cross == "yes" || cross == "1"
230
+ } else {
231
+ false
232
+ }
233
+ }
234
+
235
+ /// Returns the value of the given key from the either the matching
236
+ /// `RBCONFIG_{key}` environment variable or `RbConfig::CONFIG[{key}]` hash.
237
+ pub fn get(&self, key: &str) -> Option<String> {
238
+ self.try_rbconfig_env(key)
239
+ .or_else(|| self.try_value_map(key))
240
+ }
241
+
242
+ /// Enables the use of rpath for linking.
243
+ pub fn use_rpath(&mut self) -> &mut RbConfig {
244
+ self.use_rpath = true;
245
+ self
246
+ }
247
+
248
+ /// Push cflags string
249
+ pub fn push_cflags(&mut self, cflags: &str) -> &mut Self {
250
+ for flag in shellsplit(cflags) {
251
+ if !self.cflags.contains(&flag) {
252
+ self.cflags.push(flag.to_string());
253
+ }
254
+ }
255
+
256
+ self
257
+ }
258
+
259
+ /// Get major/minor version tuple of Ruby
260
+ pub fn major_minor(&self) -> Option<(u32, u32)> {
261
+ let major = self.get("MAJOR").map(|v| v.parse::<u32>())?.ok()?;
262
+ let minor = self.get("MINOR").map(|v| v.parse::<u32>())?.ok()?;
263
+ Some((major, minor))
264
+ }
265
+
266
+ /// Get the rb_config output for cargo
267
+ pub fn cargo_args(&self) -> Vec<String> {
268
+ let mut result = vec![];
269
+
270
+ let mut search_paths = vec![];
271
+
272
+ for search_path in &self.search_paths {
273
+ result.push(format!("cargo:rustc-link-search={}", search_path));
274
+ search_paths.push(search_path.name.as_str());
275
+ }
276
+
277
+ for lib in &self.libs {
278
+ if !self.blocklist_lib.iter().any(|b| lib.name.contains(b)) {
279
+ result.push(format!("cargo:rustc-link-lib={}", lib));
280
+ }
281
+
282
+ if self.use_rpath && !lib.is_static() {
283
+ result.push(format!("cargo:rustc-link-arg=-Wl,-rpath,{}", lib));
284
+ }
285
+ }
286
+
287
+ for link_arg in &self.link_args {
288
+ if !self.blocklist_link_arg.iter().any(|b| link_arg == b) {
289
+ result.push(format!("cargo:rustc-link-arg={}", link_arg));
290
+ }
291
+ }
292
+
293
+ result
294
+ }
295
+
296
+ /// Print to rb_config output for cargo
297
+ pub fn print_cargo_args(&self) {
298
+ let cargo_args = self.cargo_args();
299
+
300
+ for arg in &cargo_args {
301
+ println!("{}", arg);
302
+ }
303
+
304
+ debug_log!("INFO: printing cargo args ({:?})", cargo_args);
305
+
306
+ let encoded_cargo_args = cargo_args.join("\x1E");
307
+ let encoded_cargo_args = encoded_cargo_args.replace('\n', "\x1F");
308
+
309
+ println!("cargo:encoded_cargo_args={}", encoded_cargo_args);
310
+ }
311
+
312
+ /// Adds items to the rb_config based on a string from LDFLAGS/DLDFLAGS
313
+ pub fn push_dldflags(&mut self, input: &str) -> &mut Self {
314
+ let input = self.subst_shell_variables(input);
315
+ let split_args = Flags::new(input.as_str());
316
+
317
+ let search_path_regex = Regex::new(r"^-L\s*(?P<name>.*)$").unwrap();
318
+ let lib_regex_short = Regex::new(r"^-l\s*(?P<name>\w+\S+)$").unwrap();
319
+ let lib_regex_long = Regex::new(r"^--library=(?P<name>\w+\S+)$").unwrap();
320
+ let dynamic_lib_regex = Regex::new(r"^-l\s*:lib(?P<name>\S+).(so|dylib|dll)$").unwrap();
321
+ let framework_regex_short = Regex::new(r"^-F\s*(?P<name>.*)$").unwrap();
322
+ let framework_regex_long = Regex::new(r"^-framework\s*(?P<name>.*)$").unwrap();
323
+
324
+ for arg in split_args {
325
+ let arg = arg.trim().to_owned();
326
+
327
+ if let Some(name) = capture_name(&search_path_regex, &arg) {
328
+ self.push_search_path(name.as_str());
329
+ } else if let Some(name) = capture_name(&lib_regex_long, &arg) {
330
+ self.push_library(name);
331
+ } else if let Some(name) = capture_name(&lib_regex_short, &arg) {
332
+ if name.contains("ruby") && name.contains("-static") {
333
+ self.push_library((LibraryKind::Static, name));
334
+ } else {
335
+ self.push_library(name);
336
+ }
337
+ } else if let Some(name) = capture_name(&dynamic_lib_regex, &arg) {
338
+ self.push_library((LibraryKind::Dylib, name));
339
+ } else if let Some(name) = capture_name(&framework_regex_short, &arg) {
340
+ self.push_search_path((SearchPathKind::Framework, name));
341
+ } else if let Some(name) = capture_name(&framework_regex_long, &arg) {
342
+ self.push_library((LibraryKind::Framework, name));
343
+ } else {
344
+ self.push_link_arg(arg);
345
+ }
346
+ }
347
+
348
+ self
349
+ }
350
+
351
+ /// Sets a value for a key
352
+ pub fn set_value_for_key(&mut self, key: &str, value: String) {
353
+ self.value_map.insert(key.to_owned(), value);
354
+ }
355
+
356
+ // Check if has ABI version
357
+ pub fn has_ruby_dln_check_abi(&self) -> bool {
358
+ let Some((major, minor)) = self.major_minor() else {
359
+ return false;
360
+ };
361
+
362
+ let patchlevel = self
363
+ .get("PATCHLEVEL")
364
+ .and_then(|v| v.parse::<i32>().ok())
365
+ .unwrap_or(-1);
366
+
367
+ // Ruby has ABI version on version 3.2 and later only on development
368
+ // versions
369
+ (major > 3 || (major == 3 && minor >= 2))
370
+ && patchlevel == -1
371
+ && !cfg!(target_family = "windows")
372
+ }
373
+
374
+ /// The RUBY_ENGINE we are building for
375
+ pub fn ruby_engine(&self) -> RubyEngine {
376
+ if let Some(engine) = self.get("ruby_install_name") {
377
+ match engine.as_str() {
378
+ "ruby" => RubyEngine::Mri,
379
+ "jruby" => RubyEngine::JRuby,
380
+ "truffleruby" => RubyEngine::TruffleRuby,
381
+ _ => RubyEngine::Mri, // not sure how stable this is, so default to MRI to avoid breaking things
382
+ }
383
+ } else {
384
+ RubyEngine::Mri
385
+ }
386
+ }
387
+
388
+ // Examines the string from shell variables and expands them with values in the value_map
389
+ fn subst_shell_variables(&self, input: &str) -> String {
390
+ let mut result = String::new();
391
+ let mut chars = input.chars().enumerate();
392
+
393
+ while let Some((_, c)) = chars.next() {
394
+ if c == '$' {
395
+ if let Some((i, c)) = chars.next() {
396
+ if c == '(' {
397
+ let start = i + 1;
398
+ let mut end = start;
399
+
400
+ for (i, c) in chars.by_ref() {
401
+ if c == ')' {
402
+ end = i;
403
+ break;
404
+ }
405
+ }
406
+
407
+ let key = &input[start..end];
408
+
409
+ if let Some(val) = self.get(key) {
410
+ result.push_str(&val);
411
+ } else if let Some(val) = env::var_os(key) {
412
+ result.push_str(&val.to_string_lossy());
413
+ } else {
414
+ // Consume whitespace
415
+ chars.next();
416
+ }
417
+ } else {
418
+ result.push(c);
419
+ }
420
+ }
421
+ } else {
422
+ result.push(c);
423
+ }
424
+ }
425
+
426
+ result
427
+ }
428
+
429
+ pub fn have_ruby_header<T: AsRef<str>>(&self, header: T) -> bool {
430
+ let Some(ruby_include_dir) = self.get("rubyhdrdir") else {
431
+ return false;
432
+ };
433
+ PathBuf::from(ruby_include_dir)
434
+ .join(header.as_ref())
435
+ .exists()
436
+ }
437
+
438
+ fn push_search_path<T: Into<SearchPath>>(&mut self, path: T) -> &mut Self {
439
+ let path = path.into();
440
+
441
+ if !self.search_paths.contains(&path) {
442
+ self.search_paths.push(path);
443
+ }
444
+
445
+ self
446
+ }
447
+
448
+ fn push_library<T: Into<Library>>(&mut self, lib: T) -> &mut Self {
449
+ let lib = lib.into();
450
+
451
+ if !self.libs.contains(&lib) {
452
+ self.libs.push(lib);
453
+ }
454
+
455
+ self
456
+ }
457
+
458
+ fn push_link_arg<T: Into<String>>(&mut self, arg: T) -> &mut Self {
459
+ let arg = arg.into();
460
+
461
+ if !self.link_args.contains(&arg) {
462
+ self.link_args.push(arg);
463
+ }
464
+
465
+ self
466
+ }
467
+
468
+ fn try_value_map(&self, key: &str) -> Option<String> {
469
+ self.value_map
470
+ .get(key)
471
+ .map(|val| val.trim_matches('\n').to_owned())
472
+ }
473
+
474
+ fn try_rbconfig_env(&self, key: &str) -> Option<String> {
475
+ let key = format!("RBCONFIG_{}", key);
476
+ println!("cargo:rerun-if-env-changed={}", key);
477
+ env::var(key).map(|v| v.trim_matches('\n').to_owned()).ok()
478
+ }
479
+ }
480
+
481
+ #[derive(Debug, PartialEq, Eq, Clone, Copy)]
482
+ pub enum RubyEngine {
483
+ Mri,
484
+ TruffleRuby,
485
+ JRuby,
486
+ }
487
+
488
+ impl std::fmt::Display for RubyEngine {
489
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
490
+ match self {
491
+ RubyEngine::Mri => write!(f, "mri"),
492
+ RubyEngine::TruffleRuby => write!(f, "truffleruby"),
493
+ RubyEngine::JRuby => write!(f, "jruby"),
494
+ }
495
+ }
496
+ }
497
+
498
+ fn capture_name(regex: &Regex, arg: &str) -> Option<String> {
499
+ regex
500
+ .captures(arg)
501
+ .map(|cap| cap.name("name").unwrap().as_str().trim().to_owned())
502
+ }
503
+
504
+ #[cfg(test)]
505
+ mod tests {
506
+ use super::*;
507
+ use std::{sync::Mutex, vec};
508
+
509
+ lazy_static::lazy_static! {
510
+ static ref ENV_LOCK: Mutex<()> = Mutex::new(());
511
+ }
512
+
513
+ fn with_locked_env<F, T>(f: F) -> T
514
+ where
515
+ F: FnOnce() -> T,
516
+ {
517
+ let _guard = ENV_LOCK.lock().unwrap();
518
+ f()
519
+ }
520
+
521
+ #[test]
522
+ fn test_extract_lib_search_paths() {
523
+ let mut rb_config = RbConfig::new();
524
+ rb_config.push_dldflags("-L/usr/local/lib -L/usr/lib");
525
+ assert_eq!(
526
+ rb_config.search_paths,
527
+ vec!["/usr/local/lib".into(), "/usr/lib".into()]
528
+ );
529
+ }
530
+
531
+ #[test]
532
+ fn test_search_path_basic() {
533
+ let mut rb_config = RbConfig::new();
534
+ rb_config.push_dldflags("-L/usr/local/lib");
535
+
536
+ assert_eq!(rb_config.search_paths, vec!["native=/usr/local/lib".into()]);
537
+ }
538
+
539
+ #[test]
540
+ fn test_search_path_space() {
541
+ let mut rb_config = RbConfig::new();
542
+ rb_config.push_dldflags("-L /usr/local/lib");
543
+
544
+ assert_eq!(rb_config.search_paths, vec!["/usr/local/lib".into()]);
545
+ }
546
+
547
+ #[test]
548
+ fn test_search_path_space_in_path() {
549
+ let mut rb_config = RbConfig::new();
550
+ rb_config.push_dldflags("-L/usr/local/my lib");
551
+
552
+ assert_eq!(
553
+ rb_config.search_paths,
554
+ vec!["native=/usr/local/my lib".into()]
555
+ );
556
+ }
557
+
558
+ #[test]
559
+ fn test_simple_lib() {
560
+ let mut rb_config = RbConfig::new();
561
+ rb_config.push_dldflags("-lfoo");
562
+
563
+ assert_eq!(rb_config.libs, ["foo".into()]);
564
+ }
565
+
566
+ #[test]
567
+ fn test_lib_with_nonascii() {
568
+ let mut rb_config = RbConfig::new();
569
+ rb_config.push_dldflags("-lws2_32");
570
+
571
+ assert_eq!(rb_config.libs, ["ws2_32".into()]);
572
+ }
573
+
574
+ #[test]
575
+ fn test_simple_lib_space() {
576
+ let mut rb_config = RbConfig::new();
577
+ rb_config.push_dldflags("-l foo");
578
+
579
+ assert_eq!(rb_config.libs, ["foo".into()]);
580
+ }
581
+
582
+ #[test]
583
+ fn test_verbose_lib_space() {
584
+ let mut rb_config = RbConfig::new();
585
+ rb_config.push_dldflags("--library=foo");
586
+
587
+ assert_eq!(rb_config.libs, ["foo".into()]);
588
+ }
589
+
590
+ #[test]
591
+ fn test_dylib_with_colon_space() {
592
+ let mut rb_config = RbConfig::new();
593
+ rb_config.push_dldflags("-l :libssp.dylib");
594
+
595
+ assert_eq!(rb_config.libs, ["dylib=ssp".into()]);
596
+ }
597
+
598
+ #[test]
599
+ fn test_so_with_colon_space() {
600
+ let mut rb_config = RbConfig::new();
601
+ rb_config.push_dldflags("-l :libssp.so");
602
+
603
+ assert_eq!(rb_config.libs, ["dylib=ssp".into()]);
604
+ }
605
+
606
+ #[test]
607
+ fn test_dll_with_colon_space() {
608
+ let mut rb_config = RbConfig::new();
609
+ rb_config.push_dldflags("-l :libssp.dll");
610
+
611
+ assert_eq!(rb_config.libs, ["dylib=ssp".into()]);
612
+ }
613
+
614
+ #[test]
615
+ fn test_framework() {
616
+ let mut rb_config = RbConfig::new();
617
+ rb_config.push_dldflags("-F/some/path");
618
+
619
+ assert_eq!(rb_config.search_paths, ["framework=/some/path".into()]);
620
+ }
621
+
622
+ #[test]
623
+ fn test_framework_space() {
624
+ let mut rb_config = RbConfig::new();
625
+ rb_config.push_dldflags("-F /some/path");
626
+
627
+ assert_eq!(
628
+ rb_config.search_paths,
629
+ [SearchPath {
630
+ kind: SearchPathKind::Framework,
631
+ name: "/some/path".into(),
632
+ }]
633
+ );
634
+ }
635
+
636
+ #[test]
637
+ fn test_framework_arg_real() {
638
+ let mut rb_config = RbConfig::new();
639
+ rb_config.push_dldflags("-framework CoreFoundation");
640
+
641
+ assert_eq!(
642
+ rb_config.libs,
643
+ [Library {
644
+ kind: LibraryKind::Framework,
645
+ name: "CoreFoundation".into(),
646
+ }]
647
+ );
648
+ }
649
+
650
+ #[test]
651
+ fn test_libruby_static() {
652
+ let mut rb_config = RbConfig::new();
653
+ rb_config.push_dldflags("-lruby.3.1-static");
654
+
655
+ assert_eq!(
656
+ rb_config.cargo_args(),
657
+ ["cargo:rustc-link-lib=static=ruby.3.1-static"]
658
+ );
659
+ }
660
+
661
+ #[test]
662
+ fn test_libruby_dynamic() {
663
+ let mut rb_config = RbConfig::new();
664
+ rb_config.push_dldflags("-lruby.3.1");
665
+
666
+ assert_eq!(rb_config.cargo_args(), ["cargo:rustc-link-lib=ruby.3.1"]);
667
+ }
668
+
669
+ #[test]
670
+ fn test_non_lib_dash_l() {
671
+ let mut rb_config = RbConfig::new();
672
+ rb_config.push_dldflags("test_rubygems_20220413-976-lemgf9/prefix");
673
+
674
+ assert_eq!(
675
+ rb_config.link_args,
676
+ vec!["test_rubygems_20220413-976-lemgf9/prefix"]
677
+ );
678
+ }
679
+
680
+ #[test]
681
+ fn test_real_dldflags() {
682
+ let mut rb_config = RbConfig::new();
683
+ rb_config.push_dldflags("-L/Users/ianks/.asdf/installs/ruby/3.1.1/lib -L/opt/homebrew/opt/openssl@1.1/lib -Wl,-undefined,dynamic_lookup -Wl,-multiply_defined,suppress");
684
+
685
+ assert_eq!(
686
+ rb_config.link_args,
687
+ vec![
688
+ "-Wl,-undefined,dynamic_lookup",
689
+ "-Wl,-multiply_defined,suppress"
690
+ ]
691
+ );
692
+ assert_eq!(
693
+ rb_config.search_paths,
694
+ vec![
695
+ SearchPath {
696
+ kind: SearchPathKind::Native,
697
+ name: "/Users/ianks/.asdf/installs/ruby/3.1.1/lib".to_string()
698
+ },
699
+ SearchPath {
700
+ kind: SearchPathKind::Native,
701
+ name: "/opt/homebrew/opt/openssl@1.1/lib".to_string()
702
+ },
703
+ ]
704
+ );
705
+ }
706
+
707
+ #[test]
708
+ fn test_crazy_cases() {
709
+ let mut rb_config = RbConfig::new();
710
+ rb_config.push_dldflags("-F /something -l:libssp.a -static-libgcc ");
711
+
712
+ assert_eq!(rb_config.link_args, vec!["-l:libssp.a", "-static-libgcc"]);
713
+ assert_eq!(
714
+ rb_config.search_paths,
715
+ vec![SearchPath {
716
+ kind: SearchPathKind::Framework,
717
+ name: "/something".to_string()
718
+ },]
719
+ );
720
+ }
721
+
722
+ #[test]
723
+ fn test_printing_cargo_args() {
724
+ let mut rb_config = RbConfig::new();
725
+ rb_config.push_dldflags("-L/Users/ianks/.asdf/installs/ruby/3.1.1/lib");
726
+ rb_config.push_dldflags("-lfoo");
727
+ rb_config.push_dldflags("-static-libgcc");
728
+ let result = rb_config.cargo_args();
729
+
730
+ assert_eq!(
731
+ vec![
732
+ "cargo:rustc-link-search=native=/Users/ianks/.asdf/installs/ruby/3.1.1/lib",
733
+ "cargo:rustc-link-lib=foo",
734
+ "cargo:rustc-link-arg=-static-libgcc"
735
+ ],
736
+ result
737
+ );
738
+ }
739
+
740
+ #[test]
741
+ fn test_use_rpath() {
742
+ let mut rb_config = RbConfig::new();
743
+ rb_config.push_dldflags("-lfoo");
744
+
745
+ assert_eq!(vec!["cargo:rustc-link-lib=foo"], rb_config.cargo_args());
746
+
747
+ rb_config.use_rpath();
748
+
749
+ assert_eq!(
750
+ vec![
751
+ "cargo:rustc-link-lib=foo",
752
+ "cargo:rustc-link-arg=-Wl,-rpath,foo"
753
+ ],
754
+ rb_config.cargo_args()
755
+ );
756
+ }
757
+
758
+ #[test]
759
+ fn test_link_mswin() {
760
+ with_locked_env(|| {
761
+ let old_var = env::var("TARGET").ok();
762
+ env::set_var("TARGET", "x86_64-pc-windows-msvc");
763
+
764
+ let mut rb_config = RbConfig::new();
765
+ rb_config.set_value_for_key("LIBRUBYARG_SHARED", "x64-vcruntime140-ruby320.lib".into());
766
+ rb_config.set_value_for_key("libdir", "D:/ruby-mswin/lib".into());
767
+ rb_config.set_value_for_key("LIBS", "user32.lib".into());
768
+ rb_config.link_ruby(false);
769
+
770
+ assert_eq!(
771
+ vec![
772
+ "cargo:rustc-link-search=native=D:/ruby-mswin/lib",
773
+ "cargo:rustc-link-lib=x64-vcruntime140-ruby320",
774
+ "cargo:rustc-link-lib=user32",
775
+ ],
776
+ rb_config.cargo_args()
777
+ );
778
+
779
+ if let Some(old_var) = old_var {
780
+ env::set_var("TARGET", old_var);
781
+ } else {
782
+ env::remove_var("TARGET");
783
+ }
784
+ })
785
+ }
786
+
787
+ #[test]
788
+ fn test_link_static() {
789
+ with_locked_env(|| {
790
+ let mut rb_config = RbConfig::new();
791
+ rb_config.set_value_for_key("LIBRUBYARG_STATIC", "-lruby-static".into());
792
+ rb_config.set_value_for_key("libdir", "/opt/ruby".into());
793
+
794
+ rb_config.link_ruby(true);
795
+
796
+ assert_eq!(
797
+ vec![
798
+ "cargo:rustc-link-search=native=/opt/ruby",
799
+ "cargo:rustc-link-lib=static=ruby-static",
800
+ ],
801
+ rb_config.cargo_args()
802
+ );
803
+ });
804
+ }
805
+
806
+ #[test]
807
+ fn test_prioritizes_rbconfig_env() {
808
+ with_locked_env(|| {
809
+ env::set_var("RBCONFIG_libdir", "/foo");
810
+ let rb_config = RbConfig::new();
811
+
812
+ assert_eq!(rb_config.get("libdir"), Some("/foo".into()));
813
+
814
+ env::remove_var("RBCONFIG_libdir");
815
+ });
816
+ }
817
+
818
+ #[test]
819
+ fn test_never_loads_shell_rbconfig_if_cross_compiling() {
820
+ with_locked_env(|| {
821
+ env::set_var("RBCONFIG_CROSS_COMPILING", "yes");
822
+
823
+ let rb_config = RbConfig::current();
824
+
825
+ assert!(rb_config.value_map.is_empty());
826
+ });
827
+ }
828
+
829
+ #[test]
830
+ fn test_loads_shell_rbconfig_if_not_cross_compiling() {
831
+ with_locked_env(|| {
832
+ env::set_var("RBCONFIG_CROSS_COMPILING", "no");
833
+
834
+ let rb_config = RbConfig::current();
835
+
836
+ assert!(!rb_config.value_map.is_empty());
837
+ });
838
+ }
839
+
840
+ #[test]
841
+ fn test_libstatic() {
842
+ let mut rb_config = RbConfig::new();
843
+ rb_config.push_dldflags("-l:libssp.a");
844
+
845
+ assert_eq!(rb_config.link_args, ["-l:libssp.a".to_string()]);
846
+ }
847
+
848
+ #[test]
849
+ fn test_link_arg_blocklist() {
850
+ let mut rb_config = RbConfig::new();
851
+ rb_config.blocklist_link_arg("-Wl,--compress-debug-sections=zlib");
852
+ rb_config.blocklist_link_arg("-s");
853
+ rb_config.push_dldflags(
854
+ "-lfoo -Wl,--compress-debug-sections=zlib -s -somethingthatshouldnotbeblocked",
855
+ );
856
+
857
+ assert_eq!(
858
+ vec![
859
+ "cargo:rustc-link-lib=foo",
860
+ "cargo:rustc-link-arg=-somethingthatshouldnotbeblocked"
861
+ ],
862
+ rb_config.cargo_args()
863
+ );
864
+ }
865
+
866
+ #[test]
867
+ fn test_has_ruby_dln_check_abi() {
868
+ // Helper to create RbConfig with specific version
869
+ fn make_config(major: &str, minor: &str, patchlevel: &str) -> RbConfig {
870
+ let mut rb_config = RbConfig::new();
871
+ rb_config.set_value_for_key("MAJOR", major.into());
872
+ rb_config.set_value_for_key("MINOR", minor.into());
873
+ rb_config.set_value_for_key("PATCHLEVEL", patchlevel.into());
874
+ rb_config
875
+ }
876
+
877
+ // Ruby 3.1.x (any patchlevel) - too old
878
+ assert!(!make_config("3", "1", "-1").has_ruby_dln_check_abi());
879
+ assert!(!make_config("3", "1", "0").has_ruby_dln_check_abi());
880
+
881
+ // Ruby 3.2.0-dev (patchlevel -1) - should have ABI check
882
+ #[cfg(not(target_family = "windows"))]
883
+ assert!(make_config("3", "2", "-1").has_ruby_dln_check_abi());
884
+
885
+ // Ruby 3.2.0 release (patchlevel 0) - no ABI check
886
+ assert!(!make_config("3", "2", "0").has_ruby_dln_check_abi());
887
+
888
+ // Ruby 3.3.0-dev - should have ABI check
889
+ #[cfg(not(target_family = "windows"))]
890
+ assert!(make_config("3", "3", "-1").has_ruby_dln_check_abi());
891
+
892
+ // Ruby 4.0.0-dev - should have ABI check (this was the bug!)
893
+ #[cfg(not(target_family = "windows"))]
894
+ assert!(make_config("4", "0", "-1").has_ruby_dln_check_abi());
895
+
896
+ // Ruby 4.0.0 release - no ABI check
897
+ assert!(!make_config("4", "0", "0").has_ruby_dln_check_abi());
898
+
899
+ // Ruby 4.1.0-dev - should have ABI check
900
+ #[cfg(not(target_family = "windows"))]
901
+ assert!(make_config("4", "1", "-1").has_ruby_dln_check_abi());
902
+
903
+ // Ruby 2.7.x - too old
904
+ assert!(!make_config("2", "7", "-1").has_ruby_dln_check_abi());
905
+ }
906
+ }