mkmf-lite 0.7.5 → 0.8.0

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: 221bc258ee206eb8c19c1705bdcaa769326508d901d8f8720d1b60e7e704ca5c
4
+ data.tar.gz: 3383b7a19d218372b56c2e8a1938af79dcdd2b4636c4fc555069190ccfcb5cb5
5
5
  SHA512:
6
- metadata.gz: 30e2efbd8e802dbcf0237a9a5a514e7f39218702a0713ae7868135b06b9052e43be975e4a4ca9e347135f437ff1d48bbeaf8692d7d76db523b76f26aaaf1072d
7
- data.tar.gz: d7c9e01c1cdcb5f8ad94a7a2a82c16c143f2e4140da64a78c7f399a69e15830270b99c290c0844577cd738a136266490fa1cec5e376e45d50e80c4d8ad2448ca
6
+ metadata.gz: 53fd358a2dcdab183376c2d809e4c305b5ff022a81e0aa21a75041019745194adc946e1ed54005ea74a0a17f3b7b476efc7227576ae40bf0b3533b64ebce84b6
7
+ data.tar.gz: 291f1e2ac0b2eb7c84501dd5d82d8309569951230f5742f712ab6951bc8bcc7e2c164e96cf944923d93b61d486e9863ccaf9413bfe00cc3b0c4ecdd41c614f12
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGES.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## 0.8.0 - 29-Jun-2026
2
+ * Removed implicit Linux-style library flags from compiler probes.
3
+ * Compile probes now use argv-style command execution and per-probe temporary
4
+ directories.
5
+ * Improved support for include directories with spaces.
6
+ * Added FreeBSD CI coverage.
7
+ * Updated RuboCop configuration for current rubocop-rspec.
8
+
1
9
  ## 0.7.5 - 24-Dec-2025
2
10
  * Automatically include homebrew lib for have_library on Macs.
3
11
  * Modified the have_library to work with or without leading "lib".
data/ROADMAP.md ADDED
@@ -0,0 +1,104 @@
1
+ # Roadmap
2
+
3
+ This branch focuses on making `mkmf-lite` more platform-neutral, safer to use
4
+ inside applications, and easier to validate across Unix-like systems. The main
5
+ goal is to avoid FreeBSD-specific special cases where a more general portability
6
+ improvement would solve the same problem.
7
+
8
+ ## 0.8.0 - Portability Foundation
9
+
10
+ Keep the public API stable and harden the existing implementation.
11
+
12
+ * Replace hard-coded default libraries in `cpp_libraries`.
13
+ * Stop assuming Linux-style `rt`, `dl`, `crypt`, and `m` availability.
14
+ * Fix the clang branch that currently emits `-Lrt`, `-Ldl`, `-Lcrypt`, and
15
+ `-Lm`; these are library search path flags, not library link flags.
16
+ * Prefer Ruby's `RbConfig` values where they are appropriate, and only add
17
+ explicit libraries when a probe requires them.
18
+ * Use a per-probe temporary directory instead of shared `conftest.c` and
19
+ `conftest.exe` files in `Dir.tmpdir`.
20
+ * Avoid global `Dir.chdir` during probe compilation when practical.
21
+ * Replace shell-built compiler command strings with argv-style execution.
22
+ * Capture compiler diagnostics internally without emitting unwanted output.
23
+ * Add FreeBSD CI coverage, likely through Cirrus CI or a GitHub Actions
24
+ FreeBSD runner.
25
+
26
+ ## 0.9.0 - API Improvements
27
+
28
+ Add clearer extension points while preserving the existing positional API.
29
+
30
+ * Add keyword options for include directories, library directories, compile
31
+ flags, and link flags.
32
+ * Consider examples such as:
33
+
34
+ ```ruby
35
+ have_header('foo.h', include_dirs: ['/usr/local/include'])
36
+ have_library(
37
+ 'foo',
38
+ 'foo_init',
39
+ headers: ['foo.h'],
40
+ lib_dirs: ['/usr/local/lib']
41
+ )
42
+ ```
43
+
44
+ * Add access to the last failed compile command and diagnostics.
45
+ * Consider a small configuration API for global defaults such as compiler,
46
+ include paths, library paths, and extra flags.
47
+ * Keep memoized public probes, but document when memoization applies and how
48
+ callers should think about process lifetime.
49
+
50
+ ## 1.0.0 - Stable Contract
51
+
52
+ Finalize the behavior expected by downstream users.
53
+
54
+ * Document the supported public API and compatibility expectations.
55
+ * Remove unnecessary runtime dependencies where feasible.
56
+ * Document compiler selection, probe memoization, error handling, and platform
57
+ support.
58
+ * Keep the library focused on lightweight compile/link probes rather than
59
+ becoming a replacement for stdlib `mkmf`.
60
+
61
+ ## C Probe Correctness
62
+
63
+ These improvements can land in the earliest release where they fit cleanly.
64
+
65
+ * Print `sizeof` results using `size_t` and `%zu`.
66
+ * Print `offsetof` results with an appropriate unsigned size format.
67
+ * Avoid casting sizes and offsets down to `int`.
68
+ * Review function probes under stricter C modes, where implicit declarations
69
+ and old-style function assumptions may fail.
70
+ * Consider compile-time assertions for probes that only need success or failure.
71
+
72
+ ## Dependency Reduction
73
+
74
+ Reducing dependencies is useful, but should not distract from the portability
75
+ foundation.
76
+
77
+ * Remove `ptools` if it is only needed for `File.which`.
78
+ * Replace `File.which` with a small internal executable lookup based on
79
+ `ENV['PATH']`.
80
+ * Reevaluate `memoist` after the probe and command execution behavior is stable.
81
+
82
+ ## Test Coverage
83
+
84
+ The test suite should exercise command construction and failure modes, not only
85
+ successful local probes.
86
+
87
+ * Add specs for generated compiler/linker arguments.
88
+ * Test include and library directories containing spaces.
89
+ * Test library names with and without a leading `lib` prefix.
90
+ * Test parallel probe invocations to catch temporary file collisions.
91
+ * Add mocked `RbConfig` coverage for Linux, macOS, FreeBSD, Windows/MSVC, and
92
+ JRuby.
93
+ * Add a regression test proving clang does not receive bogus `-Lrt`-style
94
+ flags.
95
+
96
+ ## Documentation
97
+
98
+ Documentation should make the portability model explicit.
99
+
100
+ * Explain that probes compile tiny C programs using Ruby's configured compiler.
101
+ * Clarify how `mkmf-lite` differs from stdlib `mkmf`.
102
+ * Add FFI-oriented examples for common Unix-like use cases.
103
+ * Document how to pass include and library paths for ports-installed libraries,
104
+ especially `/usr/local/include` and `/usr/local/lib` on FreeBSD.
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.0'
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.
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.0'
7
7
  spec.author = 'Daniel J. Berger'
8
8
  spec.license = 'Apache-2.0'
9
9
  spec.email = 'djberg96@gmail.com'
@@ -9,6 +9,7 @@ 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 } }
@@ -20,7 +21,7 @@ RSpec.describe Mkmf::Lite do
20
21
 
21
22
  describe 'constants' do
22
23
  example 'version information' do
23
- expect(described_class::MKMF_LITE_VERSION).to eq('0.7.5')
24
+ expect(described_class::MKMF_LITE_VERSION).to eq('0.8.0')
24
25
  expect(described_class::MKMF_LITE_VERSION).to be_frozen
25
26
  end
26
27
  end
@@ -39,6 +40,16 @@ RSpec.describe Mkmf::Lite do
39
40
  expect{ subject.have_header('stdio.h', '/usr/local/include') }.not_to raise_error
40
41
  expect{ subject.have_header('stdio.h', '/usr/local/include', '/usr/include') }.not_to raise_error
41
42
  end
43
+
44
+ example 'have_header accepts a directory with spaces' do
45
+ Dir.mktmpdir('mkmf lite') do |dir|
46
+ header = 'mkmf_lite_space_header.h'
47
+
48
+ File.write(File.join(dir, header), "#define MKMF_LITE_SPACE_HEADER 1\n")
49
+
50
+ expect(subject.have_header(header, dir)).to be(true)
51
+ end
52
+ end
42
53
  end
43
54
 
44
55
  context 'have_func' do
@@ -194,4 +205,29 @@ RSpec.describe Mkmf::Lite do
194
205
  expect{ subject.have_library('m', 'sqrt', 'math.h', 'bogus') }.to raise_error(ArgumentError)
195
206
  end
196
207
  end
208
+
209
+ describe 'command construction' do
210
+ let(:command_with_spaces) do
211
+ subject.send(
212
+ :build_compile_command,
213
+ ['-I/tmp/include dir'],
214
+ nil,
215
+ :source_file => 'source.c',
216
+ :output_file => 'output file'
217
+ )
218
+ end
219
+
220
+ example 'build_compile_command returns argv-style command parts' do
221
+ expect(command_with_spaces).to include('-I/tmp/include dir')
222
+ expect(command_with_spaces).to include('source.c')
223
+ expect(command_with_spaces.any? { |part| part.include?('output file') }).to be(true)
224
+ end
225
+
226
+ example 'build_compile_command does not include implicit Linux libraries' do
227
+ command = subject.send(:build_compile_command)
228
+
229
+ expect(command).not_to include('-Lrt', '-Ldl', '-Lcrypt', '-Lm')
230
+ expect(command).not_to include('-lrt', '-ldl', '-lcrypt', '-lm')
231
+ end
232
+ end
197
233
  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.0
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