mkmf-lite 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 221bc258ee206eb8c19c1705bdcaa769326508d901d8f8720d1b60e7e704ca5c
4
- data.tar.gz: 3383b7a19d218372b56c2e8a1938af79dcdd2b4636c4fc555069190ccfcb5cb5
3
+ metadata.gz: f47fa10fbba01d8138ccf00130db6de0a655ade9160bbf1f63c66a49f0180a85
4
+ data.tar.gz: 89ebf0df0a2ba2763f288f063766f886050e6cec2320875429499aeeb5f6697c
5
5
  SHA512:
6
- metadata.gz: 53fd358a2dcdab183376c2d809e4c305b5ff022a81e0aa21a75041019745194adc946e1ed54005ea74a0a17f3b7b476efc7227576ae40bf0b3533b64ebce84b6
7
- data.tar.gz: 291f1e2ac0b2eb7c84501dd5d82d8309569951230f5742f712ab6951bc8bcc7e2c164e96cf944923d93b61d486e9863ccaf9413bfe00cc3b0c4ecdd41c614f12
6
+ metadata.gz: 5941960fbc5d1adbeda16c00cb056d75c816b07d5d9a361f62457dec029fe8bbb096957a7ce5b63ccf7220f12f0d97550e39a018e013c7d7309fbfe12cc8f020
7
+ data.tar.gz: aaf1b5436a3949512180a187909273fbf8c8738cb3f6bf46dad9c9e9b5707584b3db7a9ea9fceaba77384a2a904e58488f3621887f42ecdd848937ce3a8c134f
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGES.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## 0.9.0 - 5-Jul-2026
2
+ * Added `Mkmf::Lite.configure` for process-wide compiler, include directory,
3
+ library directory, compile flag, and link flag defaults.
4
+ * Configuration changes now clear memoized probe results on existing
5
+ `Mkmf::Lite` extenders.
6
+
7
+ ## 0.8.1 - 30-Jun-2026
8
+ * Improved generated C formatting for size, offset, and constant probes.
9
+ * Simplified struct member probes to use a compile-only member check.
10
+ * Added regression coverage for generated C, linker arguments, library names,
11
+ and parallel probe invocations.
12
+ * Expanded documentation for portability, diagnostics, FFI use, and
13
+ memoization.
14
+
1
15
  ## 0.8.0 - 29-Jun-2026
2
16
  * Removed implicit Linux-style library flags from compiler probes.
3
17
  * Compile probes now use argv-style command execution and per-probe temporary
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,97 @@ 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
- As of version 0.6.0 it memoizes the results of any checks that you make
47
- since they wouldn't ever change without requiring a reboot/restart of your
48
- server, container, etc, anyway.
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
+ ## Configuration
113
+ Use `Mkmf::Lite.configure` to set process-wide defaults that should apply to
114
+ all probes:
115
+
116
+ ```ruby
117
+ Mkmf::Lite.configure do |config|
118
+ config.compiler = 'clang'
119
+ config.include_dirs = ['/opt/local/include']
120
+ config.lib_dirs = ['/opt/local/lib']
121
+ config.cflags = ['-D_GNU_SOURCE']
122
+ config.ldflags = ['-Wl,-rpath,/opt/local/lib']
123
+ end
124
+ ```
125
+
126
+ The same configuration object is available from any object that extends
127
+ `Mkmf::Lite`, so local setup code can call `configure` there as well.
128
+
129
+ ## Memoization
130
+ As of version 0.6.0, public probe results are memoized for the lifetime of the
131
+ object that extends `Mkmf::Lite`. The method arguments are part of the cache
132
+ key, so `have_header('foo.h')` and `have_header('foo.h', '/usr/local/include')`
133
+ are distinct checks.
134
+
135
+ This fits the normal use case where compiler configuration, headers, and system
136
+ libraries do not change while the Ruby process is running. Calling
137
+ `Mkmf::Lite.configure` clears memoized probe results on objects that already
138
+ extended `Mkmf::Lite`, so repeated checks use the updated defaults.
49
139
 
50
140
  ## Known Issues
51
141
  JRuby may emit warnings on some platforms.
@@ -54,7 +144,7 @@ JRuby may emit warnings on some platforms.
54
144
  Apache-2.0
55
145
 
56
146
  ## Copyright
57
- (C) 2010-2025 Daniel J. Berger
147
+ (C) 2010-2026 Daniel J. Berger
58
148
  All Rights Reserved
59
149
 
60
150
  ## Warranty
data/ROADMAP.md CHANGED
@@ -1,104 +1,150 @@
1
1
  # Roadmap
2
2
 
3
- This branch focuses on making `mkmf-lite` more platform-neutral, safer to use
4
- inside applications, and easier to validate across Unix-like systems. The main
5
- goal is to avoid FreeBSD-specific special cases where a more general portability
6
- improvement would solve the same problem.
7
-
8
- ## 0.8.0 - Portability Foundation
9
-
10
- Keep the public API stable and harden the existing implementation.
11
-
12
- * Replace hard-coded default libraries in `cpp_libraries`.
13
- * Stop assuming Linux-style `rt`, `dl`, `crypt`, and `m` availability.
14
- * Fix the clang branch that currently emits `-Lrt`, `-Ldl`, `-Lcrypt`, and
15
- `-Lm`; these are library search path flags, not library link flags.
16
- * Prefer Ruby's `RbConfig` values where they are appropriate, and only add
17
- explicit libraries when a probe requires them.
18
- * Use a per-probe temporary directory instead of shared `conftest.c` and
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
19
16
  `conftest.exe` files in `Dir.tmpdir`.
20
- * Avoid global `Dir.chdir` during probe compilation when practical.
21
- * Replace shell-built compiler command strings with argv-style execution.
22
- * Capture compiler diagnostics internally without emitting unwanted output.
23
- * Add FreeBSD CI coverage, likely through Cirrus CI or a GitHub Actions
24
- FreeBSD runner.
25
-
26
- ## 0.9.0 - API Improvements
27
-
28
- Add clearer extension points while preserving the existing positional API.
29
-
30
- * Add keyword options for include directories, library directories, compile
31
- flags, and link flags.
32
- * Consider examples such as:
33
-
34
- ```ruby
35
- have_header('foo.h', include_dirs: ['/usr/local/include'])
36
- have_library(
37
- 'foo',
38
- 'foo_init',
39
- headers: ['foo.h'],
40
- lib_dirs: ['/usr/local/lib']
41
- )
42
- ```
43
-
44
- * Add access to the last failed compile command and diagnostics.
45
- * Consider a small configuration API for global defaults such as compiler,
46
- include paths, library paths, and extra flags.
47
- * Keep memoized public probes, but document when memoization applies and how
48
- callers should think about process lifetime.
49
-
50
- ## 1.0.0 - Stable Contract
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.
51
21
 
52
- Finalize the behavior expected by downstream users.
22
+ ## 0.9.0 - C Probe Correctness
53
23
 
54
- * Document the supported public API and compatibility expectations.
55
- * Remove unnecessary runtime dependencies where feasible.
56
- * Document compiler selection, probe memoization, error handling, and platform
57
- support.
58
- * Keep the library focused on lightweight compile/link probes rather than
59
- becoming a replacement for stdlib `mkmf`.
24
+ Improve the generated C probes first, keeping the Ruby API stable and avoiding
25
+ changes that could surprise existing callers.
60
26
 
61
- ## C Probe Correctness
27
+ ### Generated C Types
62
28
 
63
- These improvements can land in the earliest release where they fit cleanly.
29
+ Improve generated C for sizes, offsets, and stricter compilers.
64
30
 
65
- * Print `sizeof` results using `size_t` and `%zu`.
66
- * Print `offsetof` results with an appropriate unsigned size format.
67
- * Avoid casting sizes and offsets down to `int`.
68
- * Review function probes under stricter C modes, where implicit declarations
69
- and old-style function assumptions may fail.
70
- * Consider compile-time assertions for probes that only need success or failure.
31
+ Status: Done.
71
32
 
72
- ## Dependency Reduction
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.
73
38
 
74
- Reducing dependencies is useful, but should not distract from the portability
75
- foundation.
39
+ ### Function Probes
76
40
 
77
- * Remove `ptools` if it is only needed for `File.which`.
78
- * Replace `File.which` with a small internal executable lookup based on
79
- `ENV['PATH']`.
80
- * Reevaluate `memoist` after the probe and command execution behavior is stable.
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.
81
57
 
82
- ## Test Coverage
58
+ ### Diagnostics
83
59
 
84
- The test suite should exercise command construction and failure modes, not only
85
- successful local probes.
60
+ Make failed probes easier to understand without printing unexpected output.
86
61
 
87
- * Add specs for generated compiler/linker arguments.
88
- * Test include and library directories containing spaces.
89
- * Test library names with and without a leading `lib` prefix.
90
- * Test parallel probe invocations to catch temporary file collisions.
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.
91
77
  * Add mocked `RbConfig` coverage for Linux, macOS, FreeBSD, Windows/MSVC, and
92
78
  JRuby.
93
- * Add a regression test proving clang does not receive bogus `-Lrt`-style
94
- flags.
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.
95
86
 
96
- ## Documentation
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.
97
91
 
98
- Documentation should make the portability model explicit.
92
+ ## Later
99
93
 
100
- * Explain that probes compile tiny C programs using Ruby's configured compiler.
101
- * Clarify how `mkmf-lite` differs from stdlib `mkmf`.
102
- * Add FFI-oriented examples for common Unix-like use cases.
103
- * Document how to pass include and library paths for ports-installed libraries,
104
- especially `/usr/local/include` and `/usr/local/lib` on FreeBSD.
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.
data/lib/mkmf/lite.rb CHANGED
@@ -15,8 +15,74 @@ 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
+
18
54
  # The version of the mkmf-lite library
19
- MKMF_LITE_VERSION = '0.8.0'
55
+ MKMF_LITE_VERSION = '0.9.0'
56
+
57
+ class << self
58
+ def configuration
59
+ @configuration ||= Configuration.new
60
+ end
61
+
62
+ def configure
63
+ yield configuration
64
+ reset_memoized_results
65
+ configuration
66
+ end
67
+
68
+ def extended(object)
69
+ configured_objects << object
70
+ end
71
+
72
+ private
73
+
74
+ def configured_objects
75
+ @configured_objects ||= []
76
+ end
77
+
78
+ def reset_memoized_results
79
+ ([self] + configured_objects).each do |object|
80
+ object.instance_variables.grep(/^@_memoized_/).each do |ivar|
81
+ object.remove_instance_variable(ivar)
82
+ end
83
+ end
84
+ end
85
+ end
20
86
 
21
87
  private
22
88
 
@@ -38,7 +104,7 @@ module Mkmf
38
104
 
39
105
  # rubocop:disable Layout/LineLength
40
106
  def cpp_command
41
- command = RbConfig::CONFIG['CC'] || RbConfig::CONFIG['CPP'] || File.which('cc') || File.which('gcc') || File.which('cl')
107
+ command = configuration.compiler || RbConfig::CONFIG['CC'] || RbConfig::CONFIG['CPP'] || File.which('cc') || File.which('gcc') || File.which('cl')
42
108
  raise StandardError, 'Compiler not found' unless command
43
109
 
44
110
  command
@@ -68,6 +134,8 @@ module Mkmf
68
134
  def cpp_library_paths
69
135
  paths = []
70
136
 
137
+ paths.concat(build_library_directory_options(configuration.lib_dirs))
138
+
71
139
  # Add Homebrew library paths on macOS
72
140
  if RbConfig::CONFIG['host_os'].match?(/darwin/)
73
141
  # Apple Silicon Macs
@@ -83,6 +151,14 @@ module Mkmf
83
151
 
84
152
  public
85
153
 
154
+ def configure(&block)
155
+ Mkmf::Lite.configure(&block)
156
+ end
157
+
158
+ def configuration
159
+ Mkmf::Lite.configuration
160
+ end
161
+
86
162
  # Check for the presence of the given +header+ file. You may optionally
87
163
  # provide a list of directories to search.
88
164
  #
@@ -236,18 +312,34 @@ module Mkmf
236
312
  directories.flatten.map { |dir| "-I#{dir}" }
237
313
  end
238
314
 
315
+ def build_configured_compile_options(command_options)
316
+ [
317
+ build_directory_options(configuration.include_dirs),
318
+ configuration.cflags,
319
+ command_options
320
+ ].flatten.compact
321
+ end
322
+
323
+ def build_configured_link_options(library_options)
324
+ [library_options, configuration.ldflags].flatten.compact
325
+ end
326
+
327
+ def build_library_directory_options(directories)
328
+ directories.flatten.map { |dir| "-L#{dir}" }
329
+ end
330
+
239
331
  def build_compile_command(command_options = nil, library_options = nil, paths = {})
240
332
  source_file = paths.fetch(:source_file, cpp_source_file)
241
333
  output_file = paths.fetch(:output_file, 'conftest.exe')
242
334
 
243
335
  command_parts = shellwords(cpp_command)
244
- command_parts.concat(shellwords(command_options))
336
+ command_parts.concat(shellwords(build_configured_compile_options(command_options)))
245
337
  command_parts.concat(shellwords(cpp_library_paths))
246
338
  command_parts.concat(shellwords(cpp_libraries))
247
339
  command_parts.concat(shellwords(cpp_defs))
248
340
  command_parts.concat(shellwords(cpp_out_file(output_file)))
249
341
  command_parts << source_file
250
- command_parts.concat(shellwords(library_options))
342
+ command_parts.concat(shellwords(build_configured_link_options(library_options)))
251
343
 
252
344
  command_parts
253
345
  end
@@ -3,9 +3,7 @@
3
3
 
4
4
  <%= headers %>
5
5
 
6
- int conftest_const = (int)(offsetof(<%= struct_type %>, <%= field %>));
7
-
8
6
  int main(){
9
- printf("%d\n", conftest_const);
7
+ printf("%zu\n", offsetof(<%= struct_type %>, <%= field %>));
10
8
  return 0;
11
9
  }
@@ -1,10 +1,9 @@
1
1
  #include <stdio.h>
2
+ #include <stddef.h>
2
3
 
3
4
  <%= headers %>
4
5
 
5
- int conftest_const = (int)(sizeof(<%= type %>));
6
-
7
6
  int main(){
8
- printf("%d\n", conftest_const);
7
+ printf("%zu\n", sizeof(<%= type %>));
9
8
  return 0;
10
9
  }
@@ -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
- printf("%d\n", <%= constant %>);
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
  }
@@ -1,6 +1,6 @@
1
1
  <%= headers %>
2
2
 
3
3
  int main(){
4
- int s = (char *)&((<%= struct_type %>*)0)-><%= struct_member %> - (char *)0;
4
+ (void)sizeof(((<%= struct_type %>*)0)-><%= struct_member %>);
5
5
  return 0;
6
6
  }
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.0'
6
+ spec.version = '0.9.0'
7
7
  spec.author = 'Daniel J. Berger'
8
8
  spec.license = 'Apache-2.0'
9
9
  spec.email = 'djberg96@gmail.com'
@@ -14,6 +14,85 @@ 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
+
27
+ def new_probe
28
+ Class.new{ |obj| obj.extend Mkmf::Lite }
29
+ end
30
+
31
+ def template_source(file)
32
+ File.read(File.expand_path("../lib/mkmf/templates/#{file}", __dir__))
33
+ end
34
+
35
+ def parallel_probe_result
36
+ probe = new_probe
37
+
38
+ [probe.have_header('stdio.h'), probe.check_sizeof('int'), probe.check_valueof('EOF')]
39
+ end
40
+
41
+ def compile_command_with_linker_arguments(probe)
42
+ probe.send(:build_compile_command, nil, ['-L/tmp/lib dir', '-lfoo'])
43
+ end
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
+
17
96
  let(:st_type) { 'struct stat' }
18
97
  let(:st_member) { 'st_uid' }
19
98
  let(:st_header) { 'sys/stat.h' }
@@ -21,7 +100,7 @@ RSpec.describe Mkmf::Lite do
21
100
 
22
101
  describe 'constants' do
23
102
  example 'version information' do
24
- expect(described_class::MKMF_LITE_VERSION).to eq('0.8.0')
103
+ expect(described_class::MKMF_LITE_VERSION).to eq('0.9.0')
25
104
  expect(described_class::MKMF_LITE_VERSION).to be_frozen
26
105
  end
27
106
  end
@@ -87,6 +166,14 @@ RSpec.describe Mkmf::Lite do
87
166
  expect(subject.have_struct_member(st_type, st_member)).to be(false)
88
167
  end
89
168
 
169
+ example 'have_struct_member generates a compile-only member check' do
170
+ source = template_source('have_struct_member.erb')
171
+
172
+ expect(source).to include('(void)sizeof(((')
173
+ expect(source).not_to include('(char *)&')
174
+ expect(source).not_to include('- (char *)0')
175
+ end
176
+
90
177
  example 'have_struct_member requires at least two arguments' do
91
178
  expect{ subject.have_struct_member() }.to raise_error(ArgumentError)
92
179
  expect{ subject.have_struct_member('struct passwd') }.to raise_error(ArgumentError)
@@ -121,6 +208,23 @@ RSpec.describe Mkmf::Lite do
121
208
  expect(value).to be_a(Integer)
122
209
  expect(value).to eq(-1)
123
210
  end
211
+
212
+ example 'check_valueof returns a wider than int integer value' do
213
+ char_bit = subject.check_valueof('CHAR_BIT', 'limits.h')
214
+ ullong_size = subject.check_sizeof('unsigned long long', 'limits.h')
215
+ value = subject.check_valueof('ULLONG_MAX', 'limits.h')
216
+
217
+ expect(value).to be_a(Integer)
218
+ expect(value).to eq((1 << (ullong_size * char_bit)) - 1)
219
+ end
220
+
221
+ example 'check_valueof generates portable integer formatting' do
222
+ source = template_source('check_valueof.erb')
223
+
224
+ expect(source).to include('#include <stdint.h>', '#include <inttypes.h>')
225
+ expect(source).to include('PRIdMAX', 'PRIuMAX')
226
+ expect(source).not_to include('printf("%d')
227
+ end
124
228
  end
125
229
 
126
230
  context 'check_offsetof' do
@@ -148,6 +252,14 @@ RSpec.describe Mkmf::Lite do
148
252
  expect(size1).to eq(0)
149
253
  expect(size2).to be > size1
150
254
  end
255
+
256
+ example 'check_offsetof generates size_t output formatting' do
257
+ source = template_source('check_offsetof.erb')
258
+
259
+ expect(source).to include('printf("%zu\\n", offsetof(')
260
+ expect(source).not_to include('(int)(offsetof')
261
+ expect(source).not_to include('printf("%d')
262
+ end
151
263
  end
152
264
 
153
265
  context 'check_sizeof' do
@@ -174,6 +286,14 @@ RSpec.describe Mkmf::Lite do
174
286
  expect(size).to be_a(Integer)
175
287
  expect(size).to be > 0
176
288
  end
289
+
290
+ example 'check_sizeof generates size_t output formatting' do
291
+ source = template_source('check_sizeof.erb')
292
+
293
+ expect(source).to include('printf("%zu\\n", sizeof(')
294
+ expect(source).not_to include('(int)(sizeof')
295
+ expect(source).not_to include('printf("%d')
296
+ end
177
297
  end
178
298
 
179
299
  context 'have_library' do
@@ -187,6 +307,11 @@ RSpec.describe Mkmf::Lite do
187
307
  expect(subject.have_library('nonexistent_library_xyz')).to be(false)
188
308
  end
189
309
 
310
+ example 'have_library accepts a leading lib prefix' do
311
+ expect(subject.have_library('libm')).to be(true)
312
+ expect(subject.have_library('libm', 'sqrt', 'math.h')).to be(true)
313
+ end
314
+
190
315
  example 'have_library with function argument returns expected boolean value' do
191
316
  expect(subject.have_library('m', 'sqrt', 'math.h')).to be(true)
192
317
  expect(subject.have_library('m', 'nonexistent_function_xyz', 'math.h')).to be(false)
@@ -229,5 +354,41 @@ RSpec.describe Mkmf::Lite do
229
354
  expect(command).not_to include('-Lrt', '-Ldl', '-Lcrypt', '-Lm')
230
355
  expect(command).not_to include('-lrt', '-ldl', '-lcrypt', '-lm')
231
356
  end
357
+
358
+ example 'build_compile_command appends linker arguments' do
359
+ command_with_linker_arguments = compile_command_with_linker_arguments(subject)
360
+
361
+ expect(command_with_linker_arguments).to include('-L/tmp/lib dir', '-lfoo')
362
+ expect(command_with_linker_arguments.index('-lfoo')).to be > command_with_linker_arguments.index('conftest.c')
363
+ end
364
+ end
365
+
366
+ describe 'configuration' do
367
+ example 'exposes global compiler and flag defaults' do
368
+ configure_compiler_defaults
369
+
370
+ command = configured_compile_command(subject)
371
+
372
+ expect_configured_command_defaults(command)
373
+ expect_configured_command_order(command)
374
+ end
375
+
376
+ example 'configuration can be set from an object that extends Mkmf::Lite' do
377
+ subject.configure { |config| config.cflags = '-DMKMF_LITE_CONFIGURED=1' }
378
+
379
+ expect(subject.configuration.cflags).to eq(['-DMKMF_LITE_CONFIGURED=1'])
380
+ end
381
+
382
+ example 'configuration changes clear memoized probe results' do
383
+ expect_probe_to_refresh_with_configured_header(new_probe)
384
+ end
385
+ end
386
+
387
+ describe 'parallel probe invocations' do
388
+ example 'run independently without temporary file collisions' do
389
+ results = Array.new(6) { Thread.new { parallel_probe_result } }.map(&:value)
390
+
391
+ expect(results).to all(satisfy { |header, size, value| header && size.positive? && value == -1 })
392
+ end
232
393
  end
233
394
  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.8.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel J. Berger
@@ -178,7 +178,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
178
178
  - !ruby/object:Gem::Version
179
179
  version: '0'
180
180
  requirements: []
181
- rubygems_version: 4.0.12
181
+ rubygems_version: 4.0.15
182
182
  specification_version: 4
183
183
  summary: A lighter version of mkmf designed for use as a library
184
184
  test_files:
metadata.gz.sig CHANGED
Binary file