mkmf-lite 0.7.5 → 0.8.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3d3876fab22593f1d61c44718bc4fa4a582ce639230745ebed95c61b99a2a164
4
- data.tar.gz: 74188f32f661c473e575412c095ea7cdd78493af4a25bfbde1934bad604b04ec
3
+ metadata.gz: b780dd7e2e0e1b2be89941ce19c6cd891de5318b53cb199e6b3cbfead5c174be
4
+ data.tar.gz: 7b8afafe384f433f17b309416b24a467c488696b6b2e98560c4f2e545bfd9c77
5
5
  SHA512:
6
- metadata.gz: 30e2efbd8e802dbcf0237a9a5a514e7f39218702a0713ae7868135b06b9052e43be975e4a4ca9e347135f437ff1d48bbeaf8692d7d76db523b76f26aaaf1072d
7
- data.tar.gz: d7c9e01c1cdcb5f8ad94a7a2a82c16c143f2e4140da64a78c7f399a69e15830270b99c290c0844577cd738a136266490fa1cec5e376e45d50e80c4d8ad2448ca
6
+ metadata.gz: 1a80776330561d06936541572c984baec486bbacce69fa15bf07c3e8578981791a2769e17d94e793781f5203339b037860a5ba7d86bed1d67b65216885cde910
7
+ data.tar.gz: 98dc7c4df08d005d8628b4509f1b58059a2995bc321798138315d794d98ffadad2ef48b383afe5374ac66b162e8c5c9108376ceb6e2d33701e89cf596b8731ae
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGES.md CHANGED
@@ -1,3 +1,19 @@
1
+ ## 0.8.1 - 30-Jun-2026
2
+ * Improved generated C formatting for size, offset, and constant probes.
3
+ * Simplified struct member probes to use a compile-only member check.
4
+ * Added regression coverage for generated C, linker arguments, library names,
5
+ and parallel probe invocations.
6
+ * Expanded documentation for portability, diagnostics, FFI use, and
7
+ memoization.
8
+
9
+ ## 0.8.0 - 29-Jun-2026
10
+ * Removed implicit Linux-style library flags from compiler probes.
11
+ * Compile probes now use argv-style command execution and per-probe temporary
12
+ directories.
13
+ * Improved support for include directories with spaces.
14
+ * Added FreeBSD CI coverage.
15
+ * Updated RuboCop configuration for current rubocop-rspec.
16
+
1
17
  ## 0.7.5 - 24-Dec-2025
2
18
  * Automatically include homebrew lib for have_library on Macs.
3
19
  * Modified the have_library to work with or without leading "lib".
data/README.md CHANGED
@@ -10,7 +10,9 @@ A light version of mkmf designed for use within programs.
10
10
  `gem cert --add <(curl -Ls https://raw.githubusercontent.com/djberg96/mkmf-lite/main/certs/djberg96_pub.pem)`
11
11
 
12
12
  ## Prerequisites
13
- A C compiler somewhere on your system.
13
+ A C compiler somewhere on your system. `mkmf-lite` uses the compiler Ruby was
14
+ configured with when possible, falling back to common compiler names on your
15
+ `PATH`.
14
16
 
15
17
  ## Synopsis
16
18
  ```ruby
@@ -43,9 +45,80 @@ used in conjunction with FFI. Also, the source code is quite readable.
43
45
  It does not package C extensions, nor generate a log file or a Makefile. It
44
46
  does, however, require that you have a C compiler somewhere on your system.
45
47
 
46
- As of version 0.6.0 it memoizes the results of any checks that you make
47
- since they wouldn't ever change without requiring a reboot/restart of your
48
- server, container, etc, anyway.
48
+ ## Portability model
49
+ Each probe generates a tiny C program in a per-probe temporary directory and
50
+ asks Ruby's configured compiler to compile it. Boolean probes return whether
51
+ that compile or link step succeeded. Value probes compile and run a small
52
+ program that prints the requested integer value.
53
+
54
+ The generated source is intentionally small and portable. `mkmf-lite` avoids
55
+ assuming Linux-specific libraries or global temporary filenames, and it passes
56
+ compiler arguments as argv-style command parts so paths with spaces can work.
57
+
58
+ Unlike stdlib `mkmf`, this library does not create a `Makefile`, does not build
59
+ an extension, does not write an `mkmf.log`, and keeps its API inside
60
+ `Mkmf::Lite` instead of relying on top-level methods.
61
+
62
+ ## Diagnostics
63
+ Boolean probes such as `have_header`, `have_func`, `have_library`, and
64
+ `have_struct_member` are quiet by default and return `true` or `false`.
65
+
66
+ Value probes such as `check_valueof`, `check_sizeof`, and `check_offsetof`
67
+ raise `StandardError` if their generated C cannot be compiled. The exception
68
+ message includes the compiler command, compiler stderr, and generated source so
69
+ you can see what failed without hunting for a separate log file.
70
+
71
+ ## FFI examples
72
+ Use `mkmf-lite` to decide which declarations are safe to expose through FFI:
73
+
74
+ ```ruby
75
+ require 'ffi'
76
+ require 'mkmf/lite'
77
+
78
+ module LibC
79
+ extend FFI::Library
80
+ extend Mkmf::Lite
81
+
82
+ ffi_lib FFI::Library::LIBC
83
+
84
+ attach_function :getpid, [], :int if have_func('getpid', 'unistd.h')
85
+ end
86
+ ```
87
+
88
+ You can also use it to size native structs:
89
+
90
+ ```ruby
91
+ class StatLayout
92
+ extend Mkmf::Lite
93
+
94
+ STAT_SIZE = check_sizeof('struct stat', 'sys/stat.h')
95
+ UID_OFFSET = check_offsetof('struct stat', 'st_uid', 'sys/stat.h')
96
+ HAS_BIRTHTIME = have_struct_member('struct stat', 'st_birthtime', 'sys/stat.h')
97
+ end
98
+ ```
99
+
100
+ For libraries installed outside the compiler's default include path, pass
101
+ include directories to probes that accept them:
102
+
103
+ ```ruby
104
+ class LocalHeader
105
+ extend Mkmf::Lite
106
+
107
+ HAVE_FOO = have_header('foo.h', '/usr/local/include')
108
+ FOO_VERSION = check_valueof('FOO_VERSION', 'foo.h', '/usr/local/include')
109
+ end
110
+ ```
111
+
112
+ ## Memoization
113
+ As of version 0.6.0, public probe results are memoized for the lifetime of the
114
+ object that extends `Mkmf::Lite`. The method arguments are part of the cache
115
+ key, so `have_header('foo.h')` and `have_header('foo.h', '/usr/local/include')`
116
+ are distinct checks.
117
+
118
+ This fits the normal use case where compiler configuration, headers, and system
119
+ libraries do not change while the Ruby process is running. If you need to probe
120
+ again after changing the environment, create a new object or restart the
121
+ process.
49
122
 
50
123
  ## Known Issues
51
124
  JRuby may emit warnings on some platforms.
@@ -54,7 +127,7 @@ JRuby may emit warnings on some platforms.
54
127
  Apache-2.0
55
128
 
56
129
  ## Copyright
57
- (C) 2010-2025 Daniel J. Berger
130
+ (C) 2010-2026 Daniel J. Berger
58
131
  All Rights Reserved
59
132
 
60
133
  ## Warranty
data/ROADMAP.md ADDED
@@ -0,0 +1,148 @@
1
+ # Roadmap
2
+
3
+ This branch focuses on improving `mkmf-lite` without turning it into stdlib
4
+ `mkmf`. The guiding rule is to add clearer extension points and better
5
+ diagnostics while preserving the small public API that existing callers use.
6
+
7
+ ## Released
8
+
9
+ ### 0.8.0 - Portability Foundation
10
+
11
+ Released 29-Jun-2026.
12
+
13
+ * Removed implicit Linux-style library flags from compiler probes.
14
+ * Replaced shell-built compiler command strings with argv-style execution.
15
+ * Used per-probe temporary directories instead of shared `conftest.c` and
16
+ `conftest.exe` files in `Dir.tmpdir`.
17
+ * Avoided global `Dir.chdir` during probe compilation.
18
+ * Captured compiler diagnostics internally.
19
+ * Improved support for include directories with spaces.
20
+ * Added FreeBSD CI coverage.
21
+
22
+ ## 0.9.0 - C Probe Correctness
23
+
24
+ Improve the generated C probes first, keeping the Ruby API stable and avoiding
25
+ changes that could surprise existing callers.
26
+
27
+ ### Generated C Types
28
+
29
+ Improve generated C for sizes, offsets, and stricter compilers.
30
+
31
+ Status: Done.
32
+
33
+ * [x] Print `sizeof` results using `size_t` and `%zu`.
34
+ * [x] Print `offsetof` results using `size_t` and `%zu`.
35
+ * [x] Avoid casting sizes and offsets down to `int`.
36
+ * [x] Review `check_valueof` output formatting for constants wider than `int`.
37
+ * [x] Keep return values as Ruby integers.
38
+
39
+ ### Function Probes
40
+
41
+ Review function detection under stricter C modes.
42
+
43
+ * Avoid relying on implicit declarations.
44
+ * Avoid old-style function assumptions where practical.
45
+ * Keep support for checking functions with and without caller-provided headers.
46
+ * Preserve the current boolean behavior of `have_func`.
47
+
48
+ ### Compile-Only Probes
49
+
50
+ Prefer compile-time checks where a probe only needs success or failure.
51
+
52
+ Status: Done.
53
+
54
+ * [x] Consider compile-time assertions for struct member checks.
55
+ * [x] Keep generated source small and readable for diagnostics.
56
+ * [x] Avoid adding platform-specific C unless no portable form exists.
57
+
58
+ ### Diagnostics
59
+
60
+ Make failed probes easier to understand without printing unexpected output.
61
+
62
+ * Store the last compile command, stdout, stderr, exit status, and generated C
63
+ source for inspection.
64
+ * Add a public diagnostics reader with a small stable shape.
65
+ * Keep normal boolean probes quiet by default.
66
+ * Improve raised errors from `check_valueof`, `check_sizeof`, and
67
+ `check_offsetof` with captured compiler output.
68
+
69
+ ### Tests
70
+
71
+ Broaden tests around generated C, failure modes, and platform config.
72
+
73
+ * [x] Add specs for `sizeof` and `offsetof` values that should not be truncated.
74
+ * [x] Add specs for generated compiler/linker arguments.
75
+ * [x] Test library names with and without a leading `lib` prefix.
76
+ * [x] Test parallel probe invocations.
77
+ * Add mocked `RbConfig` coverage for Linux, macOS, FreeBSD, Windows/MSVC, and
78
+ JRuby.
79
+ * Add specs for captured diagnostics from failed probes.
80
+
81
+ ### Documentation
82
+
83
+ Document the portability model and diagnostic behavior.
84
+
85
+ Status: Done.
86
+
87
+ * [x] Explain that probes compile tiny C programs using Ruby's configured compiler.
88
+ * [x] Clarify how `mkmf-lite` differs from stdlib `mkmf`.
89
+ * [x] Add FFI-oriented examples for common Unix-like use cases.
90
+ * [x] Document memoization behavior and how it interacts with probe inputs.
91
+
92
+ ## Later
93
+
94
+ ### API Options
95
+
96
+ Keyword arguments may be useful, but they should wait until the lower-level
97
+ probe behavior is settled and the compatibility tradeoffs are clearer.
98
+
99
+ * Add `include_dirs:` as a keyword alternative to positional include directory
100
+ arguments.
101
+ * Add `lib_dirs:` for library search paths.
102
+ * Add `cflags:` for extra compile flags.
103
+ * Add `ldflags:` for extra link flags.
104
+ * Keep existing positional forms compatible.
105
+ * Make option handling consistent across `have_header`, `have_func`,
106
+ `have_library`, `have_struct_member`, `check_valueof`, `check_sizeof`, and
107
+ `check_offsetof`.
108
+
109
+ Example target API:
110
+
111
+ ```ruby
112
+ have_header('foo.h', include_dirs: ['/usr/local/include'])
113
+ have_library(
114
+ 'foo',
115
+ 'foo_init',
116
+ headers: ['foo.h'],
117
+ lib_dirs: ['/usr/local/lib'],
118
+ cflags: ['-D_GNU_SOURCE']
119
+ )
120
+ ```
121
+
122
+ ### Configuration
123
+
124
+ Consider a small configuration API if repeated keyword arguments become noisy.
125
+
126
+ * Evaluate `Mkmf::Lite.configure` for global defaults.
127
+ * Support compiler, include directories, library directories, compile flags,
128
+ and link flags as possible defaults.
129
+ * Make configuration interaction with memoized probe results explicit.
130
+
131
+ ### 1.0.0 - Stable Contract
132
+
133
+ Finalize the behavior expected by downstream users.
134
+
135
+ * Document the supported public API and compatibility expectations.
136
+ * Document compiler selection, probe memoization, error handling, and platform
137
+ support.
138
+ * Keep the library focused on lightweight compile/link probes rather than
139
+ becoming a replacement for stdlib `mkmf`.
140
+
141
+ ### Dependency Reduction
142
+
143
+ Reducing dependencies is useful, but should not distract from the 0.9 API work.
144
+
145
+ * Remove `ptools` if it is only needed for `File.which`.
146
+ * Replace `File.which` with a small internal executable lookup based on
147
+ `ENV['PATH']`.
148
+ * Reevaluate `memoist` after the probe and command execution behavior is stable.
data/lib/mkmf/lite.rb CHANGED
@@ -4,8 +4,8 @@ require 'erb'
4
4
  require 'rbconfig'
5
5
  require 'tmpdir'
6
6
  require 'open3'
7
+ require 'shellwords'
7
8
  require 'ptools'
8
- require 'fileutils'
9
9
  require 'memoist'
10
10
 
11
11
  # The Mkmf module serves as a namespace only.
@@ -16,7 +16,7 @@ module Mkmf
16
16
  extend Memoist
17
17
 
18
18
  # The version of the mkmf-lite library
19
- MKMF_LITE_VERSION = '0.7.5'
19
+ MKMF_LITE_VERSION = '0.8.1'
20
20
 
21
21
  private
22
22
 
@@ -51,24 +51,16 @@ module Mkmf
51
51
  'conftest.c'
52
52
  end
53
53
 
54
- def cpp_out_file
54
+ def cpp_out_file(output_file = 'conftest.exe')
55
55
  if windows_with_cl_compiler?
56
- '/Feconftest.exe'
56
+ "/Fe#{output_file}"
57
57
  else
58
- '-o conftest.exe'
58
+ ['-o', output_file]
59
59
  end
60
60
  end
61
61
 
62
- memoize :cpp_out_file
63
-
64
62
  def cpp_libraries
65
- return nil if windows_with_cl_compiler? || jruby?
66
-
67
- if cpp_command.match?(/clang/i)
68
- '-Lrt -Ldl -Lcrypt -Lm'
69
- else
70
- '-lrt -ldl -lcrypt -lm'
71
- end
63
+ nil
72
64
  end
73
65
 
74
66
  memoize :cpp_libraries
@@ -84,7 +76,7 @@ module Mkmf
84
76
  paths << '-L/usr/local/lib' if File.directory?('/usr/local/lib')
85
77
  end
86
78
 
87
- paths.empty? ? nil : paths.join(' ')
79
+ paths.empty? ? nil : paths
88
80
  end
89
81
 
90
82
  memoize :cpp_library_paths
@@ -241,35 +233,31 @@ module Mkmf
241
233
  def build_directory_options(directories)
242
234
  return nil if directories.empty?
243
235
 
244
- directories.map { |dir| "-I#{dir}" }.join(' ')
236
+ directories.flatten.map { |dir| "-I#{dir}" }
245
237
  end
246
238
 
247
- def build_compile_command(command_options = nil, library_options = nil)
248
- command_parts = [cpp_command]
249
- command_parts << command_options if command_options
250
- command_parts << cpp_library_paths if cpp_library_paths
251
- command_parts << cpp_libraries if cpp_libraries
252
- command_parts << cpp_defs
253
- command_parts << cpp_out_file
254
- command_parts << cpp_source_file
255
- command_parts << library_options if library_options
256
-
257
- command_parts.compact.join(' ')
258
- end
239
+ def build_compile_command(command_options = nil, library_options = nil, paths = {})
240
+ source_file = paths.fetch(:source_file, cpp_source_file)
241
+ output_file = paths.fetch(:output_file, 'conftest.exe')
259
242
 
260
- def with_suppressed_output
261
- stderr_orig = $stderr.dup
262
- stdout_orig = $stdout.dup
243
+ command_parts = shellwords(cpp_command)
244
+ command_parts.concat(shellwords(command_options))
245
+ command_parts.concat(shellwords(cpp_library_paths))
246
+ command_parts.concat(shellwords(cpp_libraries))
247
+ command_parts.concat(shellwords(cpp_defs))
248
+ command_parts.concat(shellwords(cpp_out_file(output_file)))
249
+ command_parts << source_file
250
+ command_parts.concat(shellwords(library_options))
263
251
 
264
- $stderr.reopen(IO::NULL)
265
- $stdout.reopen(IO::NULL)
252
+ command_parts
253
+ end
266
254
 
267
- yield
268
- ensure
269
- $stderr.reopen(stderr_orig)
270
- $stdout.reopen(stdout_orig)
271
- stderr_orig.close
272
- stdout_orig.close
255
+ def shellwords(options)
256
+ if options.is_a?(Array)
257
+ options.flatten.compact.map(&:to_s)
258
+ else
259
+ Shellwords.split(options.to_s)
260
+ end
273
261
  end
274
262
 
275
263
  # Take an array of header file names (or convert it to an array if it's a
@@ -303,56 +291,53 @@ module Mkmf
303
291
  # The code generated is expected to print a number to STDOUT, which
304
292
  # is then grabbed and returned as an integer.
305
293
  #
306
- # Note that $stderr is temporarily redirected to the null device because
307
- # we don't actually care about the reason for failure, though a Ruby
308
- # error is raised if the compilation step fails.
309
- #
310
294
  def try_to_execute(code, command_options = nil)
311
295
  result = 0
312
296
 
313
- Dir.chdir(Dir.tmpdir) do
314
- File.write(cpp_source_file, code)
315
- command = build_compile_command(command_options)
316
-
317
- compilation_successful = with_suppressed_output { system(command) }
318
-
319
- if compilation_successful
320
- conftest = File::ALT_SEPARATOR ? 'conftest.exe' : './conftest.exe'
321
-
322
- Open3.popen3(conftest) do |stdin, stdout, stderr|
323
- stdin.close
324
- stderr.close
325
- output = stdout.gets
326
- result = output&.chomp&.to_i || 0
327
- end
297
+ Dir.mktmpdir('mkmf-lite') do |dir|
298
+ source_file = File.join(dir, cpp_source_file)
299
+ output_file = File.join(dir, 'conftest.exe')
300
+ File.write(source_file, code)
301
+ command = build_compile_command(
302
+ command_options,
303
+ nil,
304
+ :source_file => source_file,
305
+ :output_file => output_file
306
+ )
307
+
308
+ _stdout, stderr, status = Open3.capture3(*command)
309
+
310
+ if status.success?
311
+ output, = Open3.capture2(output_file)
312
+ result = output.chomp.to_i
328
313
  else
329
- raise StandardError, "Failed to compile source code with command '#{command}':\n===\n#{code}==="
314
+ message = "Failed to compile source code with command '#{command.shelljoin}':\n#{stderr}===\n#{code}==="
315
+ raise StandardError, message
330
316
  end
331
317
  end
332
318
 
333
319
  result
334
- ensure
335
- FileUtils.rm_f(File.join(Dir.tmpdir, cpp_source_file))
336
- FileUtils.rm_f(File.join(Dir.tmpdir, 'conftest.exe'))
337
320
  end
338
321
 
339
322
  # Create a temporary bit of C source code in the temp directory, and
340
323
  # try to compile it. If it succeeds, return true. Otherwise, return
341
324
  # false.
342
325
  #
343
- # Note that $stderr is temporarily redirected to the null device because
344
- # we don't actually care about the reason for failure.
345
- #
346
326
  def try_to_compile(code, command_options = nil, library_options = nil)
347
- Dir.chdir(Dir.tmpdir) do
348
- File.write(cpp_source_file, code)
349
- command = build_compile_command(command_options, library_options)
350
-
351
- with_suppressed_output { system(command) }
327
+ Dir.mktmpdir('mkmf-lite') do |dir|
328
+ source_file = File.join(dir, cpp_source_file)
329
+ output_file = File.join(dir, 'conftest.exe')
330
+ File.write(source_file, code)
331
+ command = build_compile_command(
332
+ command_options,
333
+ library_options,
334
+ :source_file => source_file,
335
+ :output_file => output_file
336
+ )
337
+
338
+ _stdout, _stderr, status = Open3.capture3(*command)
339
+ status.success?
352
340
  end
353
- ensure
354
- FileUtils.rm_f(File.join(Dir.tmpdir, cpp_source_file))
355
- FileUtils.rm_f(File.join(Dir.tmpdir, 'conftest.exe'))
356
341
  end
357
342
 
358
343
  # Slurp the contents of the template file for evaluation later.
@@ -3,9 +3,7 @@
3
3
 
4
4
  <%= headers %>
5
5
 
6
- int conftest_const = (int)(offsetof(<%= struct_type %>, <%= field %>));
7
-
8
6
  int main(){
9
- printf("%d\n", conftest_const);
7
+ printf("%zu\n", offsetof(<%= struct_type %>, <%= field %>));
10
8
  return 0;
11
9
  }
@@ -1,10 +1,9 @@
1
1
  #include <stdio.h>
2
+ #include <stddef.h>
2
3
 
3
4
  <%= headers %>
4
5
 
5
- int conftest_const = (int)(sizeof(<%= type %>));
6
-
7
6
  int main(){
8
- printf("%d\n", conftest_const);
7
+ printf("%zu\n", sizeof(<%= type %>));
9
8
  return 0;
10
9
  }
@@ -1,8 +1,15 @@
1
1
  #include <stdio.h>
2
+ #include <stdint.h>
3
+ #include <inttypes.h>
2
4
 
3
5
  <%= headers %>
4
6
 
5
7
  int main(){
6
- printf("%d\n", <%= constant %>);
8
+ if ((<%= constant %>) < 0) {
9
+ printf("%" PRIdMAX "\n", (intmax_t)(<%= constant %>));
10
+ } else {
11
+ printf("%" PRIuMAX "\n", (uintmax_t)(<%= constant %>));
12
+ }
13
+
7
14
  return 0;
8
15
  }
@@ -1,6 +1,6 @@
1
1
  <%= headers %>
2
2
 
3
3
  int main(){
4
- int s = (char *)&((<%= struct_type %>*)0)-><%= struct_member %> - (char *)0;
4
+ (void)sizeof(((<%= struct_type %>*)0)-><%= struct_member %>);
5
5
  return 0;
6
6
  }
data/mkmf-lite.gemspec CHANGED
@@ -3,7 +3,7 @@ require 'rubygems'
3
3
  Gem::Specification.new do |spec|
4
4
  spec.name = 'mkmf-lite'
5
5
  spec.summary = 'A lighter version of mkmf designed for use as a library'
6
- spec.version = '0.7.5'
6
+ spec.version = '0.8.1'
7
7
  spec.author = 'Daniel J. Berger'
8
8
  spec.license = 'Apache-2.0'
9
9
  spec.email = 'djberg96@gmail.com'
@@ -9,10 +9,29 @@ require 'rubygems'
9
9
  require 'rspec'
10
10
  require 'mkmf/lite'
11
11
  require 'fileutils'
12
+ require 'tmpdir'
12
13
 
13
14
  RSpec.describe Mkmf::Lite do
14
15
  subject { Class.new{ |obj| obj.extend Mkmf::Lite } }
15
16
 
17
+ def new_probe
18
+ Class.new{ |obj| obj.extend Mkmf::Lite }
19
+ end
20
+
21
+ def template_source(file)
22
+ File.read(File.expand_path("../lib/mkmf/templates/#{file}", __dir__))
23
+ end
24
+
25
+ def parallel_probe_result
26
+ probe = new_probe
27
+
28
+ [probe.have_header('stdio.h'), probe.check_sizeof('int'), probe.check_valueof('EOF')]
29
+ end
30
+
31
+ def compile_command_with_linker_arguments(probe)
32
+ probe.send(:build_compile_command, nil, ['-L/tmp/lib dir', '-lfoo'])
33
+ end
34
+
16
35
  let(:st_type) { 'struct stat' }
17
36
  let(:st_member) { 'st_uid' }
18
37
  let(:st_header) { 'sys/stat.h' }
@@ -20,7 +39,7 @@ RSpec.describe Mkmf::Lite do
20
39
 
21
40
  describe 'constants' do
22
41
  example 'version information' do
23
- expect(described_class::MKMF_LITE_VERSION).to eq('0.7.5')
42
+ expect(described_class::MKMF_LITE_VERSION).to eq('0.8.1')
24
43
  expect(described_class::MKMF_LITE_VERSION).to be_frozen
25
44
  end
26
45
  end
@@ -39,6 +58,16 @@ RSpec.describe Mkmf::Lite do
39
58
  expect{ subject.have_header('stdio.h', '/usr/local/include') }.not_to raise_error
40
59
  expect{ subject.have_header('stdio.h', '/usr/local/include', '/usr/include') }.not_to raise_error
41
60
  end
61
+
62
+ example 'have_header accepts a directory with spaces' do
63
+ Dir.mktmpdir('mkmf lite') do |dir|
64
+ header = 'mkmf_lite_space_header.h'
65
+
66
+ File.write(File.join(dir, header), "#define MKMF_LITE_SPACE_HEADER 1\n")
67
+
68
+ expect(subject.have_header(header, dir)).to be(true)
69
+ end
70
+ end
42
71
  end
43
72
 
44
73
  context 'have_func' do
@@ -76,6 +105,14 @@ RSpec.describe Mkmf::Lite do
76
105
  expect(subject.have_struct_member(st_type, st_member)).to be(false)
77
106
  end
78
107
 
108
+ example 'have_struct_member generates a compile-only member check' do
109
+ source = template_source('have_struct_member.erb')
110
+
111
+ expect(source).to include('(void)sizeof(((')
112
+ expect(source).not_to include('(char *)&')
113
+ expect(source).not_to include('- (char *)0')
114
+ end
115
+
79
116
  example 'have_struct_member requires at least two arguments' do
80
117
  expect{ subject.have_struct_member() }.to raise_error(ArgumentError)
81
118
  expect{ subject.have_struct_member('struct passwd') }.to raise_error(ArgumentError)
@@ -110,6 +147,23 @@ RSpec.describe Mkmf::Lite do
110
147
  expect(value).to be_a(Integer)
111
148
  expect(value).to eq(-1)
112
149
  end
150
+
151
+ example 'check_valueof returns a wider than int integer value' do
152
+ char_bit = subject.check_valueof('CHAR_BIT', 'limits.h')
153
+ ullong_size = subject.check_sizeof('unsigned long long', 'limits.h')
154
+ value = subject.check_valueof('ULLONG_MAX', 'limits.h')
155
+
156
+ expect(value).to be_a(Integer)
157
+ expect(value).to eq((1 << (ullong_size * char_bit)) - 1)
158
+ end
159
+
160
+ example 'check_valueof generates portable integer formatting' do
161
+ source = template_source('check_valueof.erb')
162
+
163
+ expect(source).to include('#include <stdint.h>', '#include <inttypes.h>')
164
+ expect(source).to include('PRIdMAX', 'PRIuMAX')
165
+ expect(source).not_to include('printf("%d')
166
+ end
113
167
  end
114
168
 
115
169
  context 'check_offsetof' do
@@ -137,6 +191,14 @@ RSpec.describe Mkmf::Lite do
137
191
  expect(size1).to eq(0)
138
192
  expect(size2).to be > size1
139
193
  end
194
+
195
+ example 'check_offsetof generates size_t output formatting' do
196
+ source = template_source('check_offsetof.erb')
197
+
198
+ expect(source).to include('printf("%zu\\n", offsetof(')
199
+ expect(source).not_to include('(int)(offsetof')
200
+ expect(source).not_to include('printf("%d')
201
+ end
140
202
  end
141
203
 
142
204
  context 'check_sizeof' do
@@ -163,6 +225,14 @@ RSpec.describe Mkmf::Lite do
163
225
  expect(size).to be_a(Integer)
164
226
  expect(size).to be > 0
165
227
  end
228
+
229
+ example 'check_sizeof generates size_t output formatting' do
230
+ source = template_source('check_sizeof.erb')
231
+
232
+ expect(source).to include('printf("%zu\\n", sizeof(')
233
+ expect(source).not_to include('(int)(sizeof')
234
+ expect(source).not_to include('printf("%d')
235
+ end
166
236
  end
167
237
 
168
238
  context 'have_library' do
@@ -176,6 +246,11 @@ RSpec.describe Mkmf::Lite do
176
246
  expect(subject.have_library('nonexistent_library_xyz')).to be(false)
177
247
  end
178
248
 
249
+ example 'have_library accepts a leading lib prefix' do
250
+ expect(subject.have_library('libm')).to be(true)
251
+ expect(subject.have_library('libm', 'sqrt', 'math.h')).to be(true)
252
+ end
253
+
179
254
  example 'have_library with function argument returns expected boolean value' do
180
255
  expect(subject.have_library('m', 'sqrt', 'math.h')).to be(true)
181
256
  expect(subject.have_library('m', 'nonexistent_function_xyz', 'math.h')).to be(false)
@@ -194,4 +269,44 @@ RSpec.describe Mkmf::Lite do
194
269
  expect{ subject.have_library('m', 'sqrt', 'math.h', 'bogus') }.to raise_error(ArgumentError)
195
270
  end
196
271
  end
272
+
273
+ describe 'command construction' do
274
+ let(:command_with_spaces) do
275
+ subject.send(
276
+ :build_compile_command,
277
+ ['-I/tmp/include dir'],
278
+ nil,
279
+ :source_file => 'source.c',
280
+ :output_file => 'output file'
281
+ )
282
+ end
283
+
284
+ example 'build_compile_command returns argv-style command parts' do
285
+ expect(command_with_spaces).to include('-I/tmp/include dir')
286
+ expect(command_with_spaces).to include('source.c')
287
+ expect(command_with_spaces.any? { |part| part.include?('output file') }).to be(true)
288
+ end
289
+
290
+ example 'build_compile_command does not include implicit Linux libraries' do
291
+ command = subject.send(:build_compile_command)
292
+
293
+ expect(command).not_to include('-Lrt', '-Ldl', '-Lcrypt', '-Lm')
294
+ expect(command).not_to include('-lrt', '-ldl', '-lcrypt', '-lm')
295
+ end
296
+
297
+ example 'build_compile_command appends linker arguments' do
298
+ command_with_linker_arguments = compile_command_with_linker_arguments(subject)
299
+
300
+ expect(command_with_linker_arguments).to include('-L/tmp/lib dir', '-lfoo')
301
+ expect(command_with_linker_arguments.index('-lfoo')).to be > command_with_linker_arguments.index('conftest.c')
302
+ end
303
+ end
304
+
305
+ describe 'parallel probe invocations' do
306
+ example 'run independently without temporary file collisions' do
307
+ results = Array.new(6) { Thread.new { parallel_probe_result } }.map(&:value)
308
+
309
+ expect(results).to all(satisfy { |header, size, value| header && size.positive? && value == -1 })
310
+ end
311
+ end
197
312
  end
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mkmf-lite
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.5
4
+ version: 0.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel J. Berger
@@ -136,10 +136,9 @@ files:
136
136
  - LICENSE
137
137
  - MANIFEST.md
138
138
  - README.md
139
+ - ROADMAP.md
139
140
  - Rakefile
140
141
  - certs/djberg96_pub.pem
141
- - conftest.c
142
- - conftest.exe
143
142
  - lib/mkmf-lite.rb
144
143
  - lib/mkmf/lite.rb
145
144
  - lib/mkmf/templates/check_offsetof.erb
@@ -179,7 +178,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
179
178
  - !ruby/object:Gem::Version
180
179
  version: '0'
181
180
  requirements: []
182
- rubygems_version: 3.7.2
181
+ rubygems_version: 4.0.12
183
182
  specification_version: 4
184
183
  summary: A lighter version of mkmf designed for use as a library
185
184
  test_files:
metadata.gz.sig CHANGED
Binary file
data/conftest.c DELETED
@@ -1,5 +0,0 @@
1
- #include <foobar.h>
2
-
3
- int main(){
4
- return 0;
5
- }
data/conftest.exe DELETED
Binary file