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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGES.md +13 -0
- data/README.md +38 -4
- data/lib/mkmf/lite.rb +144 -11
- data/lib/mkmf/templates/have_func.erb +5 -6
- data/lib/mkmf/templates/have_func_pointer.erb +4 -8
- data/mkmf-lite.gemspec +1 -1
- data/spec/mkmf_lite_spec.rb +152 -1
- data.tar.gz.sig +0 -0
- metadata +2 -3
- metadata.gz.sig +0 -0
- data/ROADMAP.md +0 -148
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9473f0ca052ce32539ba3af2204a88c77f531e7bd4707a7199f7cfb4e55e700b
|
|
4
|
+
data.tar.gz: bc661abc68604445379cfaf8fdb56efa26b1b2b8c2162ac1d4f4d64922c8a36f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
|
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.
|
|
120
|
-
|
|
121
|
-
|
|
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.
|
|
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
|
|
116
|
-
# to
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,11 +1,7 @@
|
|
|
1
1
|
<%= headers %>
|
|
2
2
|
|
|
3
|
-
int
|
|
4
|
-
void (
|
|
5
|
-
p = (void (
|
|
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.
|
|
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'
|
data/spec/mkmf_lite_spec.rb
CHANGED
|
@@ -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.
|
|
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.
|
|
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.
|
|
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.
|