mkmf-lite 0.8.1 → 0.9.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: b780dd7e2e0e1b2be89941ce19c6cd891de5318b53cb199e6b3cbfead5c174be
4
- data.tar.gz: 7b8afafe384f433f17b309416b24a467c488696b6b2e98560c4f2e545bfd9c77
3
+ metadata.gz: 9473f0ca052ce32539ba3af2204a88c77f531e7bd4707a7199f7cfb4e55e700b
4
+ data.tar.gz: bc661abc68604445379cfaf8fdb56efa26b1b2b8c2162ac1d4f4d64922c8a36f
5
5
  SHA512:
6
- metadata.gz: 1a80776330561d06936541572c984baec486bbacce69fa15bf07c3e8578981791a2769e17d94e793781f5203339b037860a5ba7d86bed1d67b65216885cde910
7
- data.tar.gz: 98dc7c4df08d005d8628b4509f1b58059a2995bc321798138315d794d98ffadad2ef48b383afe5374ac66b162e8c5c9108376ceb6e2d33701e89cf596b8731ae
6
+ metadata.gz: 4b85efddc463304f178f995a34031524505c8f927b8546ea70186376b8263244d7dcd2ea7f162da376b73b53e2880dcda3a154a9db3367f8589c6a8c8e27560e
7
+ data.tar.gz: 6e0cf9e41d699e939001847ab41ef1f9d6aa3ab792666d294938b51d770471a6477b2a6a9a338dd76147c6cc6d0a685d859bd93925c82e72d71b325698e62524
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGES.md CHANGED
@@ -1,3 +1,16 @@
1
+ ## 0.9.1 - 7-Jul-2026
2
+ * Updated function probes to avoid implicit declarations and old-style function
3
+ pointer syntax while preserving symbol-only checks.
4
+ * Added a public diagnostics reader for the most recent compiler probe.
5
+ * Improved value probe failures with captured compiler stdout, stderr, exit
6
+ status, command, and generated source.
7
+
8
+ ## 0.9.0 - 5-Jul-2026
9
+ * Added `Mkmf::Lite.configure` for process-wide compiler, include directory,
10
+ library directory, compile flag, and link flag defaults.
11
+ * Configuration changes now clear memoized probe results on existing
12
+ `Mkmf::Lite` extenders.
13
+
1
14
  ## 0.8.1 - 30-Jun-2026
2
15
  * Improved generated C formatting for size, offset, and constant probes.
3
16
  * Simplified struct member probes to use a compile-only member check.
data/README.md CHANGED
@@ -63,11 +63,28 @@ an extension, does not write an `mkmf.log`, and keeps its API inside
63
63
  Boolean probes such as `have_header`, `have_func`, `have_library`, and
64
64
  `have_struct_member` are quiet by default and return `true` or `false`.
65
65
 
66
+ After any probe compiles generated C, call `diagnostics` on the object that
67
+ extends `Mkmf::Lite` to inspect the most recent compile attempt. It returns an
68
+ object with `command`, `stdout`, `stderr`, `exit_status`, `source`, and
69
+ `success?`.
70
+
66
71
  Value probes such as `check_valueof`, `check_sizeof`, and `check_offsetof`
67
72
  raise `StandardError` if their generated C cannot be compiled. The exception
68
- message includes the compiler command, compiler stderr, and generated source so
73
+ message includes the compiler command, compiler output, and generated source so
69
74
  you can see what failed without hunting for a separate log file.
70
75
 
76
+ ```ruby
77
+ class Probe
78
+ extend Mkmf::Lite
79
+
80
+ unless have_header('missing_header.h')
81
+ warn diagnostics.command.join(' ')
82
+ warn diagnostics.stderr
83
+ warn diagnostics.source
84
+ end
85
+ end
86
+ ```
87
+
71
88
  ## FFI examples
72
89
  Use `mkmf-lite` to decide which declarations are safe to expose through FFI:
73
90
 
@@ -109,6 +126,23 @@ class LocalHeader
109
126
  end
110
127
  ```
111
128
 
129
+ ## Configuration
130
+ Use `Mkmf::Lite.configure` to set process-wide defaults that should apply to
131
+ all probes:
132
+
133
+ ```ruby
134
+ Mkmf::Lite.configure do |config|
135
+ config.compiler = 'clang'
136
+ config.include_dirs = ['/opt/local/include']
137
+ config.lib_dirs = ['/opt/local/lib']
138
+ config.cflags = ['-D_GNU_SOURCE']
139
+ config.ldflags = ['-Wl,-rpath,/opt/local/lib']
140
+ end
141
+ ```
142
+
143
+ The same configuration object is available from any object that extends
144
+ `Mkmf::Lite`, so local setup code can call `configure` there as well.
145
+
112
146
  ## Memoization
113
147
  As of version 0.6.0, public probe results are memoized for the lifetime of the
114
148
  object that extends `Mkmf::Lite`. The method arguments are part of the cache
@@ -116,9 +150,9 @@ key, so `have_header('foo.h')` and `have_header('foo.h', '/usr/local/include')`
116
150
  are distinct checks.
117
151
 
118
152
  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.
153
+ libraries do not change while the Ruby process is running. Calling
154
+ `Mkmf::Lite.configure` clears memoized probe results on objects that already
155
+ extended `Mkmf::Lite`, so repeated checks use the updated defaults.
122
156
 
123
157
  ## Known Issues
124
158
  JRuby may emit warnings on some platforms.
data/lib/mkmf/lite.rb CHANGED
@@ -15,8 +15,88 @@ module Mkmf
15
15
  module Lite
16
16
  extend Memoist
17
17
 
18
+ # Stores process-wide defaults for compiler probe commands.
19
+ class Configuration
20
+ attr_accessor :compiler
21
+ attr_reader :include_dirs, :lib_dirs, :cflags, :ldflags
22
+
23
+ def initialize
24
+ @compiler = nil
25
+ @include_dirs = []
26
+ @lib_dirs = []
27
+ @cflags = []
28
+ @ldflags = []
29
+ end
30
+
31
+ def include_dirs=(dirs)
32
+ @include_dirs = normalize_options(dirs)
33
+ end
34
+
35
+ def lib_dirs=(dirs)
36
+ @lib_dirs = normalize_options(dirs)
37
+ end
38
+
39
+ def cflags=(flags)
40
+ @cflags = normalize_options(flags)
41
+ end
42
+
43
+ def ldflags=(flags)
44
+ @ldflags = normalize_options(flags)
45
+ end
46
+
47
+ private
48
+
49
+ def normalize_options(options)
50
+ Array(options).flatten.compact.map(&:to_s)
51
+ end
52
+ end
53
+
54
+ # Captures the most recent compiler probe details for inspection.
55
+ Diagnostic = Struct.new(
56
+ :command,
57
+ :stdout,
58
+ :stderr,
59
+ :exit_status,
60
+ :source,
61
+ :keyword_init => true
62
+ ) do
63
+ def success?
64
+ exit_status == 0
65
+ end
66
+ end
67
+
18
68
  # The version of the mkmf-lite library
19
- MKMF_LITE_VERSION = '0.8.1'
69
+ MKMF_LITE_VERSION = '0.9.1'
70
+
71
+ class << self
72
+ def configuration
73
+ @configuration ||= Configuration.new
74
+ end
75
+
76
+ def configure
77
+ yield configuration
78
+ reset_memoized_results
79
+ configuration
80
+ end
81
+
82
+ def extended(object)
83
+ configured_objects << object
84
+ end
85
+
86
+ private
87
+
88
+ def configured_objects
89
+ @configured_objects ||= []
90
+ end
91
+
92
+ def reset_memoized_results
93
+ ([self] + configured_objects).each do |object|
94
+ object.instance_variables.grep(/^@_memoized_/).each do |ivar|
95
+ object.remove_instance_variable(ivar)
96
+ end
97
+ end
98
+ end
99
+ end
20
100
 
21
101
  private
22
102
 
@@ -38,7 +118,7 @@ module Mkmf
38
118
 
39
119
  # rubocop:disable Layout/LineLength
40
120
  def cpp_command
41
- command = RbConfig::CONFIG['CC'] || RbConfig::CONFIG['CPP'] || File.which('cc') || File.which('gcc') || File.which('cl')
121
+ command = configuration.compiler || RbConfig::CONFIG['CC'] || RbConfig::CONFIG['CPP'] || File.which('cc') || File.which('gcc') || File.which('cl')
42
122
  raise StandardError, 'Compiler not found' unless command
43
123
 
44
124
  command
@@ -68,6 +148,8 @@ module Mkmf
68
148
  def cpp_library_paths
69
149
  paths = []
70
150
 
151
+ paths.concat(build_library_directory_options(configuration.lib_dirs))
152
+
71
153
  # Add Homebrew library paths on macOS
72
154
  if RbConfig::CONFIG['host_os'].match?(/darwin/)
73
155
  # Apple Silicon Macs
@@ -83,6 +165,18 @@ module Mkmf
83
165
 
84
166
  public
85
167
 
168
+ def configure(&block)
169
+ Mkmf::Lite.configure(&block)
170
+ end
171
+
172
+ def configuration
173
+ Mkmf::Lite.configuration
174
+ end
175
+
176
+ def diagnostics
177
+ @diagnostics
178
+ end
179
+
86
180
  # Check for the presence of the given +header+ file. You may optionally
87
181
  # provide a list of directories to search.
88
182
  #
@@ -104,6 +198,7 @@ module Mkmf
104
198
  # Returns true if found, or false if not found.
105
199
  #
106
200
  def have_func(function, headers = [])
201
+ headers_provided = !Array(headers).flatten.empty?
107
202
  headers = get_header_string(headers)
108
203
 
109
204
  erb_ptr = ERB.new(read_template('have_func_pointer.erb'))
@@ -112,9 +207,9 @@ module Mkmf
112
207
  ptr_code = erb_ptr.result(binding)
113
208
  std_code = erb_std.result(binding)
114
209
 
115
- # Check for just the function pointer first. If that fails, then try
116
- # to compile with the function declaration.
117
- try_to_compile(ptr_code) || try_to_compile(std_code)
210
+ # Check for a header declaration first. If no headers were provided,
211
+ # fall back to an explicit declaration so symbol-only probes still work.
212
+ try_to_compile(ptr_code) || (!headers_provided && try_to_compile(std_code))
118
213
  end
119
214
 
120
215
  memoize :have_func
@@ -236,18 +331,34 @@ module Mkmf
236
331
  directories.flatten.map { |dir| "-I#{dir}" }
237
332
  end
238
333
 
334
+ def build_configured_compile_options(command_options)
335
+ [
336
+ build_directory_options(configuration.include_dirs),
337
+ configuration.cflags,
338
+ command_options
339
+ ].flatten.compact
340
+ end
341
+
342
+ def build_configured_link_options(library_options)
343
+ [library_options, configuration.ldflags].flatten.compact
344
+ end
345
+
346
+ def build_library_directory_options(directories)
347
+ directories.flatten.map { |dir| "-L#{dir}" }
348
+ end
349
+
239
350
  def build_compile_command(command_options = nil, library_options = nil, paths = {})
240
351
  source_file = paths.fetch(:source_file, cpp_source_file)
241
352
  output_file = paths.fetch(:output_file, 'conftest.exe')
242
353
 
243
354
  command_parts = shellwords(cpp_command)
244
- command_parts.concat(shellwords(command_options))
355
+ command_parts.concat(shellwords(build_configured_compile_options(command_options)))
245
356
  command_parts.concat(shellwords(cpp_library_paths))
246
357
  command_parts.concat(shellwords(cpp_libraries))
247
358
  command_parts.concat(shellwords(cpp_defs))
248
359
  command_parts.concat(shellwords(cpp_out_file(output_file)))
249
360
  command_parts << source_file
250
- command_parts.concat(shellwords(library_options))
361
+ command_parts.concat(shellwords(build_configured_link_options(library_options)))
251
362
 
252
363
  command_parts
253
364
  end
@@ -305,14 +416,14 @@ module Mkmf
305
416
  :output_file => output_file
306
417
  )
307
418
 
308
- _stdout, stderr, status = Open3.capture3(*command)
419
+ stdout, stderr, status = Open3.capture3(*command)
420
+ store_diagnostics(command, stdout, stderr, status, code)
309
421
 
310
422
  if status.success?
311
423
  output, = Open3.capture2(output_file)
312
424
  result = output.chomp.to_i
313
425
  else
314
- message = "Failed to compile source code with command '#{command.shelljoin}':\n#{stderr}===\n#{code}==="
315
- raise StandardError, message
426
+ raise StandardError, diagnostic_message
316
427
  end
317
428
  end
318
429
 
@@ -335,11 +446,33 @@ module Mkmf
335
446
  :output_file => output_file
336
447
  )
337
448
 
338
- _stdout, _stderr, status = Open3.capture3(*command)
449
+ stdout, stderr, status = Open3.capture3(*command)
450
+ store_diagnostics(command, stdout, stderr, status, code)
339
451
  status.success?
340
452
  end
341
453
  end
342
454
 
455
+ def store_diagnostics(command, stdout, stderr, status, source)
456
+ @diagnostics = Diagnostic.new(
457
+ :command => command.dup,
458
+ :stdout => stdout,
459
+ :stderr => stderr,
460
+ :exit_status => status.exitstatus,
461
+ :source => source
462
+ )
463
+ end
464
+
465
+ def diagnostic_message
466
+ [
467
+ "Failed to compile source code with command '#{diagnostics.command.shelljoin}':",
468
+ diagnostics.stderr,
469
+ diagnostics.stdout,
470
+ '===',
471
+ diagnostics.source,
472
+ '==='
473
+ ].reject(&:empty?).join("\n")
474
+ end
475
+
343
476
  # Slurp the contents of the template file for evaluation later.
344
477
  #
345
478
  def read_template(file)
@@ -1,10 +1,9 @@
1
1
  <%= headers %>
2
2
 
3
- int try_func(){
4
- <%= function %>();
5
- return 0;
6
- }
3
+ extern void <%= function %>(void);
7
4
 
8
- int main(){
9
- return 0;
5
+ int main(void){
6
+ void (*volatile p)(void);
7
+ p = <%= function %>;
8
+ return p == 0;
10
9
  }
@@ -1,11 +1,7 @@
1
1
  <%= headers %>
2
2
 
3
- int check_for_function(){
4
- void ((*volatile p)());
5
- p = (void ((*)()))<%= function %>;
6
- return 0;
7
- }
8
-
9
- int main(){
10
- return 0;
3
+ int main(void){
4
+ void (*volatile p)(void);
5
+ p = (void (*)(void))<%= function %>;
6
+ return p == 0;
11
7
  }
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.8.1'
6
+ spec.version = '0.9.1'
7
7
  spec.author = 'Daniel J. Berger'
8
8
  spec.license = 'Apache-2.0'
9
9
  spec.email = 'djberg96@gmail.com'
@@ -14,6 +14,16 @@ require 'tmpdir'
14
14
  RSpec.describe Mkmf::Lite do
15
15
  subject { Class.new{ |obj| obj.extend Mkmf::Lite } }
16
16
 
17
+ after do
18
+ described_class.configure do |config|
19
+ config.compiler = nil
20
+ config.include_dirs = []
21
+ config.lib_dirs = []
22
+ config.cflags = []
23
+ config.ldflags = []
24
+ end
25
+ end
26
+
17
27
  def new_probe
18
28
  Class.new{ |obj| obj.extend Mkmf::Lite }
19
29
  end
@@ -32,6 +42,82 @@ RSpec.describe Mkmf::Lite do
32
42
  probe.send(:build_compile_command, nil, ['-L/tmp/lib dir', '-lfoo'])
33
43
  end
34
44
 
45
+ def configured_compile_command(probe)
46
+ probe.send(
47
+ :build_compile_command,
48
+ nil,
49
+ ['-lfoo'],
50
+ :source_file => 'source.c',
51
+ :output_file => 'output file'
52
+ )
53
+ end
54
+
55
+ def configure_compiler_defaults
56
+ described_class.configure do |config|
57
+ config.compiler = 'custom-cc'
58
+ config.include_dirs = ['/tmp/include dir']
59
+ config.lib_dirs = ['/tmp/lib dir']
60
+ config.cflags = ['-DLOCAL_BUILD=1']
61
+ config.ldflags = ['-Wl,-rpath,/tmp/lib dir']
62
+ end
63
+ end
64
+
65
+ def write_configured_header(dir, header)
66
+ File.write(File.join(dir, header), "#define MKMF_LITE_CONFIGURED_HEADER 1\n")
67
+ end
68
+
69
+ def expect_configured_command_defaults(command)
70
+ expect(command).to include(
71
+ 'custom-cc',
72
+ '-I/tmp/include dir',
73
+ '-L/tmp/lib dir',
74
+ '-DLOCAL_BUILD=1',
75
+ '-lfoo',
76
+ '-Wl,-rpath,/tmp/lib dir'
77
+ )
78
+ end
79
+
80
+ def expect_configured_command_order(command)
81
+ expect(command.index('-DLOCAL_BUILD=1')).to be < command.index('source.c')
82
+ expect(command.index('-Wl,-rpath,/tmp/lib dir')).to be > command.index('source.c')
83
+ end
84
+
85
+ def expect_probe_to_refresh_with_configured_header(probe)
86
+ header = 'mkmf_lite_configured_header.h'
87
+
88
+ Dir.mktmpdir('mkmf lite configured') do |dir|
89
+ write_configured_header(dir, header)
90
+ expect(probe.have_header(header)).to be(false)
91
+ described_class.configure { |config| config.include_dirs = [dir] }
92
+ expect(probe.have_header(header)).to be(true)
93
+ end
94
+ end
95
+
96
+ def missing_header
97
+ 'mkmf_lite_missing_header.h'
98
+ end
99
+
100
+ def expect_failed_diagnostics_for(probe, source_text)
101
+ diagnostics = probe.diagnostics
102
+
103
+ expect(diagnostics.command).to be_a(Array)
104
+ expect(diagnostics.stdout).to be_a(String)
105
+ expect(diagnostics.stderr).to be_a(String)
106
+ expect(diagnostics.exit_status).to be_a(Integer)
107
+ expect(diagnostics.exit_status).not_to eq(0)
108
+ expect(diagnostics.source).to include(source_text)
109
+ expect(diagnostics).not_to be_success
110
+ end
111
+
112
+ def expect_value_probe_error(error, probe, source_text)
113
+ expect(error.message).to include(
114
+ 'Failed to compile source code with command',
115
+ probe.diagnostics.command.shelljoin,
116
+ probe.diagnostics.stderr,
117
+ source_text
118
+ )
119
+ end
120
+
35
121
  let(:st_type) { 'struct stat' }
36
122
  let(:st_member) { 'st_uid' }
37
123
  let(:st_header) { 'sys/stat.h' }
@@ -39,7 +125,7 @@ RSpec.describe Mkmf::Lite do
39
125
 
40
126
  describe 'constants' do
41
127
  example 'version information' do
42
- expect(described_class::MKMF_LITE_VERSION).to eq('0.8.1')
128
+ expect(described_class::MKMF_LITE_VERSION).to eq('0.9.1')
43
129
  expect(described_class::MKMF_LITE_VERSION).to be_frozen
44
130
  end
45
131
  end
@@ -85,6 +171,22 @@ RSpec.describe Mkmf::Lite do
85
171
  expect(subject.have_func('printfx', 'stdio.h')).to be(false)
86
172
  end
87
173
 
174
+ example 'have_func supports symbol-only checks without caller-provided headers' do
175
+ skip('getpid is POSIX-specific') if File::ALT_SEPARATOR
176
+
177
+ expect(subject.have_func('getpid')).to be(true)
178
+ end
179
+
180
+ example 'have_func templates avoid implicit declarations and old-style prototypes' do
181
+ pointer_source = template_source('have_func_pointer.erb')
182
+ declaration_source = template_source('have_func.erb')
183
+
184
+ expect(pointer_source).to include('int main(void)', 'void (*volatile p)(void)')
185
+ expect(pointer_source).not_to include('(*)()', '<%= function %>();')
186
+ expect(declaration_source).to include('extern void <%= function %>(void)')
187
+ expect(declaration_source).not_to include('<%= function %>();')
188
+ end
189
+
88
190
  example 'have_func requires at least one argument' do
89
191
  expect{ subject.have_func }.to raise_error(ArgumentError)
90
192
  end
@@ -302,6 +404,55 @@ RSpec.describe Mkmf::Lite do
302
404
  end
303
405
  end
304
406
 
407
+ describe 'configuration' do
408
+ example 'exposes global compiler and flag defaults' do
409
+ configure_compiler_defaults
410
+
411
+ command = configured_compile_command(subject)
412
+
413
+ expect_configured_command_defaults(command)
414
+ expect_configured_command_order(command)
415
+ end
416
+
417
+ example 'configuration can be set from an object that extends Mkmf::Lite' do
418
+ subject.configure { |config| config.cflags = '-DMKMF_LITE_CONFIGURED=1' }
419
+
420
+ expect(subject.configuration.cflags).to eq(['-DMKMF_LITE_CONFIGURED=1'])
421
+ end
422
+
423
+ example 'configuration changes clear memoized probe results' do
424
+ expect_probe_to_refresh_with_configured_header(new_probe)
425
+ end
426
+ end
427
+
428
+ describe 'diagnostics' do
429
+ example 'starts with no diagnostics' do
430
+ expect(subject.diagnostics).to be_nil
431
+ end
432
+
433
+ example 'records failed boolean probes without printing output' do
434
+ probe = new_probe
435
+
436
+ expect{ expect(probe.have_header(missing_header)).to be(false) }.not_to output.to_stdout
437
+ expect_failed_diagnostics_for(probe, missing_header)
438
+ end
439
+
440
+ example 'records successful probe diagnostics' do
441
+ expect(subject.have_header('stdio.h')).to be(true)
442
+
443
+ expect(subject.diagnostics).to be_success
444
+ expect(subject.diagnostics.source).to include('stdio.h')
445
+ end
446
+
447
+ example 'uses captured diagnostics in value probe errors' do
448
+ token = 'mkmf_lite_missing_type'
449
+
450
+ expect{ subject.check_sizeof("struct #{token}") }.to raise_error(StandardError) { |error|
451
+ expect_value_probe_error(error, subject, token)
452
+ }
453
+ end
454
+ end
455
+
305
456
  describe 'parallel probe invocations' do
306
457
  example 'run independently without temporary file collisions' do
307
458
  results = Array.new(6) { Thread.new { parallel_probe_result } }.map(&:value)
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.8.1
4
+ version: 0.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel J. Berger
@@ -136,7 +136,6 @@ files:
136
136
  - LICENSE
137
137
  - MANIFEST.md
138
138
  - README.md
139
- - ROADMAP.md
140
139
  - Rakefile
141
140
  - certs/djberg96_pub.pem
142
141
  - lib/mkmf-lite.rb
@@ -178,7 +177,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
178
177
  - !ruby/object:Gem::Version
179
178
  version: '0'
180
179
  requirements: []
181
- rubygems_version: 4.0.12
180
+ rubygems_version: 4.0.15
182
181
  specification_version: 4
183
182
  summary: A lighter version of mkmf designed for use as a library
184
183
  test_files:
metadata.gz.sig CHANGED
Binary file
data/ROADMAP.md DELETED
@@ -1,148 +0,0 @@
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.