mkmf-lite 0.9.0 → 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: f47fa10fbba01d8138ccf00130db6de0a655ade9160bbf1f63c66a49f0180a85
4
- data.tar.gz: 89ebf0df0a2ba2763f288f063766f886050e6cec2320875429499aeeb5f6697c
3
+ metadata.gz: 9473f0ca052ce32539ba3af2204a88c77f531e7bd4707a7199f7cfb4e55e700b
4
+ data.tar.gz: bc661abc68604445379cfaf8fdb56efa26b1b2b8c2162ac1d4f4d64922c8a36f
5
5
  SHA512:
6
- metadata.gz: 5941960fbc5d1adbeda16c00cb056d75c816b07d5d9a361f62457dec029fe8bbb096957a7ce5b63ccf7220f12f0d97550e39a018e013c7d7309fbfe12cc8f020
7
- data.tar.gz: aaf1b5436a3949512180a187909273fbf8c8738cb3f6bf46dad9c9e9b5707584b3db7a9ea9fceaba77384a2a904e58488f3621887f42ecdd848937ce3a8c134f
6
+ metadata.gz: 4b85efddc463304f178f995a34031524505c8f927b8546ea70186376b8263244d7dcd2ea7f162da376b73b53e2880dcda3a154a9db3367f8589c6a8c8e27560e
7
+ data.tar.gz: 6e0cf9e41d699e939001847ab41ef1f9d6aa3ab792666d294938b51d770471a6477b2a6a9a338dd76147c6cc6d0a685d859bd93925c82e72d71b325698e62524
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGES.md CHANGED
@@ -1,3 +1,10 @@
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
+
1
8
  ## 0.9.0 - 5-Jul-2026
2
9
  * Added `Mkmf::Lite.configure` for process-wide compiler, include directory,
3
10
  library directory, compile flag, and link flag defaults.
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
 
data/lib/mkmf/lite.rb CHANGED
@@ -51,8 +51,22 @@ module Mkmf
51
51
  end
52
52
  end
53
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
+
54
68
  # The version of the mkmf-lite library
55
- MKMF_LITE_VERSION = '0.9.0'
69
+ MKMF_LITE_VERSION = '0.9.1'
56
70
 
57
71
  class << self
58
72
  def configuration
@@ -159,6 +173,10 @@ module Mkmf
159
173
  Mkmf::Lite.configuration
160
174
  end
161
175
 
176
+ def diagnostics
177
+ @diagnostics
178
+ end
179
+
162
180
  # Check for the presence of the given +header+ file. You may optionally
163
181
  # provide a list of directories to search.
164
182
  #
@@ -180,6 +198,7 @@ module Mkmf
180
198
  # Returns true if found, or false if not found.
181
199
  #
182
200
  def have_func(function, headers = [])
201
+ headers_provided = !Array(headers).flatten.empty?
183
202
  headers = get_header_string(headers)
184
203
 
185
204
  erb_ptr = ERB.new(read_template('have_func_pointer.erb'))
@@ -188,9 +207,9 @@ module Mkmf
188
207
  ptr_code = erb_ptr.result(binding)
189
208
  std_code = erb_std.result(binding)
190
209
 
191
- # Check for just the function pointer first. If that fails, then try
192
- # to compile with the function declaration.
193
- 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))
194
213
  end
195
214
 
196
215
  memoize :have_func
@@ -397,14 +416,14 @@ module Mkmf
397
416
  :output_file => output_file
398
417
  )
399
418
 
400
- _stdout, stderr, status = Open3.capture3(*command)
419
+ stdout, stderr, status = Open3.capture3(*command)
420
+ store_diagnostics(command, stdout, stderr, status, code)
401
421
 
402
422
  if status.success?
403
423
  output, = Open3.capture2(output_file)
404
424
  result = output.chomp.to_i
405
425
  else
406
- message = "Failed to compile source code with command '#{command.shelljoin}':\n#{stderr}===\n#{code}==="
407
- raise StandardError, message
426
+ raise StandardError, diagnostic_message
408
427
  end
409
428
  end
410
429
 
@@ -427,11 +446,33 @@ module Mkmf
427
446
  :output_file => output_file
428
447
  )
429
448
 
430
- _stdout, _stderr, status = Open3.capture3(*command)
449
+ stdout, stderr, status = Open3.capture3(*command)
450
+ store_diagnostics(command, stdout, stderr, status, code)
431
451
  status.success?
432
452
  end
433
453
  end
434
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
+
435
476
  # Slurp the contents of the template file for evaluation later.
436
477
  #
437
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.9.0'
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'
@@ -93,6 +93,31 @@ RSpec.describe Mkmf::Lite do
93
93
  end
94
94
  end
95
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
+
96
121
  let(:st_type) { 'struct stat' }
97
122
  let(:st_member) { 'st_uid' }
98
123
  let(:st_header) { 'sys/stat.h' }
@@ -100,7 +125,7 @@ RSpec.describe Mkmf::Lite do
100
125
 
101
126
  describe 'constants' do
102
127
  example 'version information' do
103
- expect(described_class::MKMF_LITE_VERSION).to eq('0.9.0')
128
+ expect(described_class::MKMF_LITE_VERSION).to eq('0.9.1')
104
129
  expect(described_class::MKMF_LITE_VERSION).to be_frozen
105
130
  end
106
131
  end
@@ -146,6 +171,22 @@ RSpec.describe Mkmf::Lite do
146
171
  expect(subject.have_func('printfx', 'stdio.h')).to be(false)
147
172
  end
148
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
+
149
190
  example 'have_func requires at least one argument' do
150
191
  expect{ subject.have_func }.to raise_error(ArgumentError)
151
192
  end
@@ -384,6 +425,34 @@ RSpec.describe Mkmf::Lite do
384
425
  end
385
426
  end
386
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
+
387
456
  describe 'parallel probe invocations' do
388
457
  example 'run independently without temporary file collisions' do
389
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.9.0
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
metadata.gz.sig CHANGED
@@ -1,5 +1 @@
1
- �
2
- }��/�h��lC#�~�Ƞ5� ����Y�ЧH�l�)0~j�4Z�pY߷�� -���Zq�e%��B���x��$P�Y��!w���� �.9���Q��
3
- �3S��񳕳�i���i�(n4
4
- ;� x<�kS��L��������9��Ƚ�wAp8���(��i�c�1���O���o��49����
5
- �nGg���GI����B�)Ff\��!��Sa�ŢY�_ҞG"�6پ����!�_�wۘ��|y�������;k��e����Ǽϖh �L΢��q�?0�l
1
+ :�4��اmO-F�FkZ��.�������o��܇��ER������'5����Mނ�%�~�����h~J'SY�����.�B6h��7�s��u��I���y�FI��e� i;�e�xK4V�o��z1KԒz�����CG%B,�:M�F��R�wR*5aԮSO\��9�F��v�d o�q����K���� u]C�Ɠ`�����1a>��<ծT�yi����9��;U�zRX���i劤Rms�0�*��$ I��:��dR��J�x���Z�s�����rYkw'!���z���u�l�q��4ބ?AE&(͘��s�͝���\���
data/ROADMAP.md DELETED
@@ -1,150 +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
- Status: Done.
127
-
128
- * [x] Evaluate `Mkmf::Lite.configure` for global defaults.
129
- * [x] Support compiler, include directories, library directories, compile flags,
130
- and link flags as possible defaults.
131
- * [x] Make configuration interaction with memoized probe results explicit.
132
-
133
- ### 1.0.0 - Stable Contract
134
-
135
- Finalize the behavior expected by downstream users.
136
-
137
- * Document the supported public API and compatibility expectations.
138
- * Document compiler selection, probe memoization, error handling, and platform
139
- support.
140
- * Keep the library focused on lightweight compile/link probes rather than
141
- becoming a replacement for stdlib `mkmf`.
142
-
143
- ### Dependency Reduction
144
-
145
- Reducing dependencies is useful, but should not distract from the 0.9 API work.
146
-
147
- * Remove `ptools` if it is only needed for `File.which`.
148
- * Replace `File.which` with a small internal executable lookup based on
149
- `ENV['PATH']`.
150
- * Reevaluate `memoist` after the probe and command execution behavior is stable.