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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGES.md +16 -0
- data/README.md +78 -5
- data/ROADMAP.md +148 -0
- data/lib/mkmf/lite.rb +58 -73
- data/lib/mkmf/templates/check_offsetof.erb +1 -3
- data/lib/mkmf/templates/check_sizeof.erb +2 -3
- data/lib/mkmf/templates/check_valueof.erb +8 -1
- data/lib/mkmf/templates/have_struct_member.erb +1 -1
- data/mkmf-lite.gemspec +1 -1
- data/spec/mkmf_lite_spec.rb +116 -1
- data.tar.gz.sig +0 -0
- metadata +3 -4
- metadata.gz.sig +0 -0
- data/conftest.c +0 -5
- data/conftest.exe +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b780dd7e2e0e1b2be89941ce19c6cd891de5318b53cb199e6b3cbfead5c174be
|
|
4
|
+
data.tar.gz: 7b8afafe384f433f17b309416b24a467c488696b6b2e98560c4f2e545bfd9c77
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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-
|
|
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.
|
|
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
|
-
|
|
56
|
+
"/Fe#{output_file}"
|
|
57
57
|
else
|
|
58
|
-
'-o
|
|
58
|
+
['-o', output_file]
|
|
59
59
|
end
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
-
memoize :cpp_out_file
|
|
63
|
-
|
|
64
62
|
def cpp_libraries
|
|
65
|
-
|
|
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
|
|
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}" }
|
|
236
|
+
directories.flatten.map { |dir| "-I#{dir}" }
|
|
245
237
|
end
|
|
246
238
|
|
|
247
|
-
def build_compile_command(command_options = nil, library_options = nil)
|
|
248
|
-
|
|
249
|
-
|
|
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
|
-
|
|
261
|
-
|
|
262
|
-
|
|
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
|
-
|
|
265
|
-
|
|
252
|
+
command_parts
|
|
253
|
+
end
|
|
266
254
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
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.
|
|
314
|
-
File.
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
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
|
-
|
|
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.
|
|
348
|
-
File.
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
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.
|
|
@@ -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
|
-
|
|
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
|
}
|
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.8.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
|
@@ -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.
|
|
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.
|
|
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:
|
|
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
data/conftest.exe
DELETED
|
Binary file
|