metacc 0.1.0 → 0.3.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 +4 -4
- data/bin/metacc +13 -1
- data/lib/metacc/cli.rb +139 -121
- data/lib/metacc/driver.rb +78 -37
- data/lib/metacc/toolchain.rb +230 -193
- data/lib/metacc/version.rb +7 -0
- data/lib/metacc.rb +1 -6
- metadata +6 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d74b463f2d62a82dbbe422b47e112052a25c95e8c51b957f01ec2545b2f8f227
|
|
4
|
+
data.tar.gz: 510bdbcba0c031de7909db0a989141afbecd9fa33bcf7b5066cf61d3dacc6f49
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bbfda6722b4f9fa4b59a6be206a1b5ca0503d77f5833dbe5ba07f46525b1f157e4ac5ca7853f0f18ae7efd4f9c2782993a0991e101fd102523323abfeea97dd1
|
|
7
|
+
data.tar.gz: 15f59e0d6f45de8501b38aa6429624dd759d7d7b126ce18c0def60bdd2d56648f4b116483753e2f93da60c3e205ec5fd00813844e59b4975d7e3c81c01cfd590
|
data/bin/metacc
CHANGED
|
@@ -5,4 +5,16 @@ $LOAD_PATH.unshift(File.join(__dir__, "..", "lib"))
|
|
|
5
5
|
|
|
6
6
|
require "metacc/cli"
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
begin
|
|
9
|
+
MetaCC::CLI.new.run(ARGV)
|
|
10
|
+
rescue MetaCC::CLI::InvalidOption,
|
|
11
|
+
MetaCC::ToolchainNotFoundError,
|
|
12
|
+
OptionParser::AmbiguousOption,
|
|
13
|
+
OptionParser::MissingArgument,
|
|
14
|
+
OptionParser::NeedlessArgument,
|
|
15
|
+
OptionParser::InvalidArgument,
|
|
16
|
+
OptionParser::InvalidOption => e
|
|
17
|
+
warn "#{$0} error: #{e.message}"
|
|
18
|
+
rescue MetaCC::CompileError => e
|
|
19
|
+
warn e.message
|
|
20
|
+
end
|
data/lib/metacc/cli.rb
CHANGED
|
@@ -9,57 +9,23 @@ module MetaCC
|
|
|
9
9
|
#
|
|
10
10
|
# Usage:
|
|
11
11
|
# metacc <sources...> -o <output> [options] – compile source file(s)
|
|
12
|
-
#
|
|
13
|
-
# General:
|
|
14
|
-
# -Wall -Werror
|
|
15
|
-
# --std=c11 --std=c17 --std=c23
|
|
16
|
-
# --std=c++11 --std=c++14 --std=c++17 --std=c++20 --std=c++23 --std=c++26
|
|
17
|
-
#
|
|
18
|
-
# Linking:
|
|
19
|
-
# --objects / -c – compile only; don't link
|
|
20
|
-
# -l, -L - specify linker input
|
|
21
|
-
# --shared – produce a shared library
|
|
22
|
-
# --static – produce a static library
|
|
23
|
-
# --lto - enable link time optimization
|
|
24
|
-
# --strip / -s – strip unneeded symbols
|
|
25
|
-
#
|
|
26
|
-
# Code generation:
|
|
27
|
-
# -O0, -O1, -O2, -O3 - Set the optimization level
|
|
28
|
-
# -msse4.2 -mavx -mavx2 -mavx512 --arch=native - Compile for the given target
|
|
29
|
-
# --no-rtti --no-exceptions
|
|
30
|
-
# --pic
|
|
31
|
-
#
|
|
32
|
-
# Debugging:
|
|
33
|
-
# --debug / -g
|
|
34
|
-
# --asan --ubsan --msan
|
|
35
|
-
#
|
|
36
|
-
# Toolchain-specific flags (passed to Driver#compile via xflags:):
|
|
37
|
-
# --xmsvc VALUE – appended to xflags[MSVC]
|
|
38
|
-
# --xgnu VALUE – appended to xflags[GNU]
|
|
39
|
-
# --xclang VALUE – appended to xflags[Clang]
|
|
40
|
-
# --xclangcl VALUE – appended to xflags[ClangCL]
|
|
41
12
|
class CLI
|
|
42
13
|
|
|
43
|
-
# Maps long-form CLI flag names to Driver::RECOGNIZED_FLAGS symbols.
|
|
44
|
-
# Optimization-level flags are handled separately via -O LEVEL.
|
|
45
|
-
LONG_FLAGS = {
|
|
46
|
-
"lto" => :lto,
|
|
47
|
-
"asan" => :asan,
|
|
48
|
-
"ubsan" => :ubsan,
|
|
49
|
-
"msan" => :msan,
|
|
50
|
-
"no-rtti" => :no_rtti,
|
|
51
|
-
"no-exceptions" => :no_exceptions,
|
|
52
|
-
"pic" => :pic,
|
|
53
|
-
"no-semantic-interposition" => :no_semantic_interposition,
|
|
54
|
-
"no-omit-frame-pointer" => :no_omit_frame_pointer,
|
|
55
|
-
"no-strict-aliasing" => :no_strict_aliasing
|
|
56
|
-
}.freeze
|
|
57
|
-
|
|
58
14
|
WARNING_CONFIGS = {
|
|
59
15
|
"all" => :warn_all,
|
|
60
16
|
"error" => :warn_error
|
|
61
17
|
}
|
|
62
18
|
|
|
19
|
+
SANITIZERS = {
|
|
20
|
+
"address" => :asan,
|
|
21
|
+
"addr" => :asan,
|
|
22
|
+
"undefined" => :ubsan,
|
|
23
|
+
"ub" => :ubsan,
|
|
24
|
+
"memory" => :msan,
|
|
25
|
+
"mem" => :msan,
|
|
26
|
+
"leak" => :lsan
|
|
27
|
+
}
|
|
28
|
+
|
|
63
29
|
TARGETS = {
|
|
64
30
|
"sse4.2" => :sse4_2,
|
|
65
31
|
"avx" => :avx,
|
|
@@ -69,9 +35,9 @@ module MetaCC
|
|
|
69
35
|
}.freeze
|
|
70
36
|
|
|
71
37
|
STANDARDS = {
|
|
72
|
-
"c11"
|
|
73
|
-
"c17"
|
|
74
|
-
"c23"
|
|
38
|
+
"c11" => :c11,
|
|
39
|
+
"c17" => :c17,
|
|
40
|
+
"c23" => :c23,
|
|
75
41
|
"c++11" => :cxx11,
|
|
76
42
|
"c++14" => :cxx14,
|
|
77
43
|
"c++17" => :cxx17,
|
|
@@ -82,42 +48,52 @@ module MetaCC
|
|
|
82
48
|
|
|
83
49
|
# Maps --x<name> CLI option names to xflags toolchain-class keys.
|
|
84
50
|
XFLAGS = {
|
|
85
|
-
"xmsvc" =>
|
|
86
|
-
"xgnu" =>
|
|
87
|
-
"xclang" =>
|
|
88
|
-
"xclangcl" => ClangCL
|
|
51
|
+
"xmsvc" => MSVC,
|
|
52
|
+
"xgnu" => GNU,
|
|
53
|
+
"xclang" => Clang,
|
|
54
|
+
"xclangcl" => ClangCL,
|
|
55
|
+
"xtinycc" => TinyCC
|
|
89
56
|
}.freeze
|
|
90
57
|
|
|
91
|
-
def
|
|
92
|
-
|
|
58
|
+
def initialize(driver: Driver.new)
|
|
59
|
+
@driver = driver
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def run(argv)
|
|
63
|
+
input_paths, options = parse_compile_args(argv)
|
|
93
64
|
output_path = options.delete(:output_path)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
invoke(driver, input_paths, output_path, options, run: run_flag)
|
|
65
|
+
validate_options!(options[:flags], output_path, link: options[:link], run: options[:run])
|
|
66
|
+
invoke(input_paths, output_path, **options)
|
|
97
67
|
end
|
|
98
68
|
|
|
99
69
|
# Parses compile arguments.
|
|
100
|
-
# Returns [
|
|
101
|
-
def parse_compile_args(argv
|
|
70
|
+
# Returns [positional_args, options_hash].
|
|
71
|
+
def parse_compile_args(argv)
|
|
102
72
|
options = {
|
|
103
73
|
include_paths: [],
|
|
104
|
-
defs:
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
74
|
+
defs: [],
|
|
75
|
+
link: true,
|
|
76
|
+
link_paths: [],
|
|
77
|
+
libs: [],
|
|
78
|
+
output_path: nil,
|
|
79
|
+
run: false,
|
|
80
|
+
flags: [],
|
|
81
|
+
xflags: {}
|
|
111
82
|
}
|
|
112
83
|
parser = OptionParser.new
|
|
113
|
-
setup_compile_options(parser, options
|
|
114
|
-
|
|
115
|
-
[
|
|
84
|
+
setup_compile_options(parser, options)
|
|
85
|
+
input_paths = parser.permute(argv)
|
|
86
|
+
[input_paths, options]
|
|
116
87
|
end
|
|
117
88
|
|
|
118
89
|
private
|
|
119
90
|
|
|
120
|
-
def setup_compile_options(parser, options
|
|
91
|
+
def setup_compile_options(parser, options)
|
|
92
|
+
parser.require_exact = true
|
|
93
|
+
|
|
94
|
+
parser.separator ""
|
|
95
|
+
parser.separator "General options:"
|
|
96
|
+
|
|
121
97
|
parser.on("-o FILEPATH", "Output file path") do |value|
|
|
122
98
|
options[:output_path] = value
|
|
123
99
|
end
|
|
@@ -127,89 +103,131 @@ module MetaCC
|
|
|
127
103
|
parser.on("-D DEF", "Add a preprocessor definition") do |value|
|
|
128
104
|
options[:defs] << value
|
|
129
105
|
end
|
|
130
|
-
parser.on("
|
|
131
|
-
options[:flags] <<
|
|
106
|
+
parser.on("--std=STANDARD", "Specify the language standard") do |value|
|
|
107
|
+
options[:flags] << STANDARDS[value]
|
|
132
108
|
end
|
|
133
|
-
parser.on("-
|
|
134
|
-
options[:flags] <<
|
|
109
|
+
parser.on("-W OPTION", "Configure warnings") do |value|
|
|
110
|
+
options[:flags] << WARNING_CONFIGS[value]
|
|
135
111
|
end
|
|
136
|
-
parser.on("-
|
|
137
|
-
options[:
|
|
112
|
+
parser.on("-r", "--run", "Run the compiled executable after a successful build") do
|
|
113
|
+
options[:run] = true
|
|
138
114
|
end
|
|
139
|
-
|
|
140
|
-
|
|
115
|
+
|
|
116
|
+
parser.separator ""
|
|
117
|
+
parser.separator "Debugging:"
|
|
118
|
+
|
|
119
|
+
parser.on("-g", "--debug-info", "Emit debugging symbols") do
|
|
120
|
+
options[:flags] << :debug_info
|
|
141
121
|
end
|
|
142
|
-
parser.on("--
|
|
143
|
-
options[:flags] <<
|
|
122
|
+
parser.on("-S", "--sanitize SANITIZER", "Enable sanitizer (address, undefined, leak, memory)") do |value|
|
|
123
|
+
options[:flags] << SANITIZERS[value]
|
|
144
124
|
end
|
|
145
|
-
|
|
146
|
-
|
|
125
|
+
|
|
126
|
+
parser.separator ""
|
|
127
|
+
parser.separator "Optimization:"
|
|
128
|
+
|
|
129
|
+
parser.on("-O LEVEL", /\A[0-3]|s\z/, "Optimization level (0, 1, 2, 3, or s)") do |level|
|
|
130
|
+
options[:flags] << :"o#{level}"
|
|
147
131
|
end
|
|
148
|
-
parser.on("
|
|
149
|
-
options[:flags] << :
|
|
132
|
+
parser.on("--lto", "Enable link time optimization") do
|
|
133
|
+
options[:flags] << :lto
|
|
150
134
|
end
|
|
151
|
-
parser.on("-
|
|
152
|
-
options[:
|
|
135
|
+
parser.on("--omit-frame-pointer") do |value|
|
|
136
|
+
options[:flags] << :omit_frame_pointer
|
|
153
137
|
end
|
|
154
|
-
parser.on("-
|
|
155
|
-
options[:
|
|
138
|
+
parser.on("--strict-aliasing") do |value|
|
|
139
|
+
options[:flags] << :strict_aliasing
|
|
156
140
|
end
|
|
157
|
-
|
|
158
|
-
|
|
141
|
+
|
|
142
|
+
parser.separator ""
|
|
143
|
+
parser.separator "Code generation:"
|
|
144
|
+
|
|
145
|
+
parser.on("-m", "--arch=ARCH", "Target architecture") do |value|
|
|
146
|
+
options[:flags] << TARGETS[value]
|
|
159
147
|
end
|
|
160
|
-
parser.on("--
|
|
161
|
-
options[:flags] << :
|
|
148
|
+
parser.on("--pic", "Generate position independent code") do |value|
|
|
149
|
+
options[:flags] << :pic
|
|
150
|
+
end
|
|
151
|
+
parser.on("--no-rtti", "Disable runtime type information") do |value|
|
|
152
|
+
options[:flags] << :no_rtti
|
|
162
153
|
end
|
|
154
|
+
parser.on("--no-exceptions", "Disable exceptions (and unwinding info)") do |value|
|
|
155
|
+
options[:flags] << :no_exceptions
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
parser.separator ""
|
|
159
|
+
parser.separator "Linking:"
|
|
160
|
+
|
|
163
161
|
parser.on("--static", "Produce a static library") do
|
|
164
162
|
options[:flags] << :static
|
|
165
163
|
end
|
|
164
|
+
parser.on("--shared", "Produce a shared library") do |value|
|
|
165
|
+
options[:flags] << :shared
|
|
166
|
+
end
|
|
167
|
+
parser.on("--shared-compat", "Produce a shared library with full LD_PRELOAD compatability") do |value|
|
|
168
|
+
options[:flags] << :shared_compat
|
|
169
|
+
end
|
|
170
|
+
parser.on("-c", "Compile only (produce object files without linking)") do
|
|
171
|
+
options[:link] = false
|
|
172
|
+
end
|
|
173
|
+
parser.on("-l LIB", "Link against library LIB") do |value|
|
|
174
|
+
options[:libs] << value
|
|
175
|
+
end
|
|
176
|
+
parser.on("-L DIR", "Add linker library search path") do |value|
|
|
177
|
+
options[:link_paths] << value
|
|
178
|
+
end
|
|
179
|
+
|
|
166
180
|
parser.on("-s", "--strip", "Strip unneeded symbols") do
|
|
167
181
|
options[:flags] << :strip
|
|
168
182
|
end
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
parser.on("--#{name}
|
|
176
|
-
options[:xflags][
|
|
177
|
-
options[:xflags][
|
|
183
|
+
|
|
184
|
+
parser.separator ""
|
|
185
|
+
parser.separator "Compiler specific:"
|
|
186
|
+
|
|
187
|
+
XFLAGS.each do |name, toolchain_class|
|
|
188
|
+
toolchain_name = toolchain_class.name.split("::").last
|
|
189
|
+
parser.on("--#{name} FLAG", "Forward FLAG to the compiler if compiling with #{toolchain_name}") do |value|
|
|
190
|
+
options[:xflags][toolchain_class] ||= []
|
|
191
|
+
options[:xflags][toolchain_class] << value
|
|
178
192
|
end
|
|
179
193
|
end
|
|
194
|
+
|
|
195
|
+
parser.separator ""
|
|
196
|
+
parser.separator "Informational:"
|
|
197
|
+
|
|
180
198
|
parser.on_tail("--version", "Print the toolchain version and exit") do
|
|
181
|
-
puts driver
|
|
199
|
+
puts @driver.toolchain.version_banner
|
|
200
|
+
exit
|
|
201
|
+
end
|
|
202
|
+
parser.on_tail("-h", "--help", "Show this message") do
|
|
203
|
+
puts parser
|
|
182
204
|
exit
|
|
183
205
|
end
|
|
184
206
|
end
|
|
185
207
|
|
|
186
|
-
def validate_options!(flags, output_path,
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
if objects && output_path
|
|
190
|
-
warn "error: -o cannot be used with --objects"
|
|
191
|
-
exit 1
|
|
208
|
+
def validate_options!(flags, output_path, link:, run:)
|
|
209
|
+
if !link && output_path
|
|
210
|
+
raise OptionParser::InvalidOption, "cannot specify output path (-o) in compile only mode (-c)"
|
|
192
211
|
end
|
|
193
212
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
exit 1
|
|
213
|
+
if link && !output_path
|
|
214
|
+
raise OptionParser::InvalidOption, "must specify an output path (-o)"
|
|
197
215
|
end
|
|
198
216
|
|
|
199
|
-
if
|
|
200
|
-
|
|
201
|
-
exit 1
|
|
217
|
+
if run && (!link || flags.include?(:shared) || flags.include?(:static))
|
|
218
|
+
raise OptionParser::InvalidOption, "--run may not be used with -c, --shared, or --static"
|
|
202
219
|
end
|
|
203
220
|
end
|
|
204
221
|
|
|
205
|
-
def
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
222
|
+
def invoke(input_paths, desired_output_path = nil, link: true, run: false, **options)
|
|
223
|
+
if link
|
|
224
|
+
actual_output_path = @driver.compile_and_link(input_paths, desired_output_path, **options)
|
|
225
|
+
system(actual_output_path) if run
|
|
226
|
+
else
|
|
227
|
+
options.delete(:link_paths)
|
|
228
|
+
options.delete(:libs)
|
|
229
|
+
@driver.compile(input_paths, **options)
|
|
230
|
+
end
|
|
213
231
|
end
|
|
214
232
|
|
|
215
233
|
end
|
data/lib/metacc/driver.rb
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "open3"
|
|
3
4
|
require_relative "toolchain"
|
|
4
5
|
|
|
5
6
|
module MetaCC
|
|
6
7
|
|
|
7
8
|
# Raised when no supported C/C++ compiler can be found on the system.
|
|
8
|
-
class
|
|
9
|
+
class CompileError < StandardError; end
|
|
10
|
+
|
|
11
|
+
# Raised when no supported C/C++ compiler can be found on the system.
|
|
12
|
+
class ToolchainNotFoundError < StandardError; end
|
|
9
13
|
|
|
10
14
|
# Driver wraps C and C++ compile and link operations using the first
|
|
11
15
|
# available compiler found on the system (Clang, GCC, or MSVC).
|
|
@@ -13,16 +17,16 @@ module MetaCC
|
|
|
13
17
|
|
|
14
18
|
RECOGNIZED_FLAGS = Set.new(
|
|
15
19
|
%i[
|
|
16
|
-
o0 o1 o2 o3 os
|
|
17
|
-
sse4_2 avx avx2 avx512 native
|
|
18
|
-
debug lto
|
|
19
20
|
warn_all warn_error
|
|
20
21
|
c11 c17 c23
|
|
21
22
|
cxx11 cxx14 cxx17 cxx20 cxx23 cxx26
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
o0 o1 o2 o3 os lto
|
|
24
|
+
sse4_2 avx avx2 avx512 native
|
|
25
|
+
asan ubsan msan lsan
|
|
26
|
+
debug_info
|
|
27
|
+
omit_frame_pointer strict_aliasing
|
|
28
|
+
no_rtti no_exceptions
|
|
29
|
+
pic shared shared_compat static strip
|
|
26
30
|
]
|
|
27
31
|
).freeze
|
|
28
32
|
|
|
@@ -36,16 +40,48 @@ module MetaCC
|
|
|
36
40
|
# Defaults to [Clang, GNU, MSVC].
|
|
37
41
|
# @param search_paths [Array<String>] directories to search for toolchain executables
|
|
38
42
|
# before falling back to PATH. Defaults to [].
|
|
39
|
-
# @raise [
|
|
40
|
-
def initialize(prefer: [
|
|
41
|
-
search_paths: [])
|
|
43
|
+
# @raise [ToolchainNotFoundError] if no supported compiler is found.
|
|
44
|
+
def initialize(prefer: [MSVC], search_paths: [])
|
|
42
45
|
@toolchain = select_toolchain!(prefer, search_paths)
|
|
43
46
|
end
|
|
44
47
|
|
|
48
|
+
# Invokes the compiler driver for the given input files and output path,
|
|
49
|
+
# compiling and producting object files without linking.
|
|
50
|
+
#
|
|
51
|
+
# @param input_files [String, Array<String>] paths to the input files
|
|
52
|
+
# @param flags [Array<Symbol>] compiler/linker flags
|
|
53
|
+
# @param xflags [Hash{Class => String}] extra (native) compiler flags keyed by toolchain Class
|
|
54
|
+
# @param include_paths [Array<String>] directories to add with -I
|
|
55
|
+
# @param defs [Array<String>] preprocessor macros (e.g. "FOO" or "FOO=1")
|
|
56
|
+
# @param env [Hash] environment variables to set for the subprocess
|
|
57
|
+
# @param working_dir [String] working directory for the subprocess (default: ".")
|
|
58
|
+
# @raise [CompileError] if the underlying toolchain executable returns a non-zero exit status
|
|
59
|
+
def compile(
|
|
60
|
+
input_files,
|
|
61
|
+
flags: [],
|
|
62
|
+
xflags: {},
|
|
63
|
+
include_paths: [],
|
|
64
|
+
defs: [],
|
|
65
|
+
env: {},
|
|
66
|
+
working_dir: "."
|
|
67
|
+
)
|
|
68
|
+
flags = translate_flags(flags)
|
|
69
|
+
flags.concat(xflags[@toolchain.class] || [])
|
|
70
|
+
|
|
71
|
+
cmd = @toolchain.compile_command(
|
|
72
|
+
input_files,
|
|
73
|
+
flags:,
|
|
74
|
+
include_paths:,
|
|
75
|
+
defs:
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
!!run_command(cmd, env:, working_dir:)
|
|
79
|
+
end
|
|
80
|
+
|
|
45
81
|
# Invokes the compiler driver for the given input files and output path.
|
|
46
82
|
# The kind of output (object files, executable, shared library, or static
|
|
47
|
-
# library) is determined by the flags: +:
|
|
48
|
-
#
|
|
83
|
+
# library) is determined by the flags: +:shared+ or +:static+. When none of
|
|
84
|
+
# these mode flags is present, an executable is produced.
|
|
49
85
|
#
|
|
50
86
|
# @param input_files [String, Array<String>] paths to the input files
|
|
51
87
|
# @param output_path [String] path for the resulting output file
|
|
@@ -53,37 +89,45 @@ module MetaCC
|
|
|
53
89
|
# @param xflags [Hash{Class => String}] extra (native) compiler flags keyed by toolchain Class
|
|
54
90
|
# @param include_paths [Array<String>] directories to add with -I
|
|
55
91
|
# @param defs [Array<String>] preprocessor macros (e.g. "FOO" or "FOO=1")
|
|
56
|
-
# @param libs [Array<String>] library names to link (e.g. "m", "pthread")
|
|
57
92
|
# @param linker_paths [Array<String>] linker library search paths (-L / /LIBPATH:)
|
|
93
|
+
# @param libs [Array<String>] library names to link (e.g. "m", "pthread")
|
|
58
94
|
# @param env [Hash] environment variables to set for the subprocess
|
|
59
95
|
# @param working_dir [String] working directory for the subprocess (default: ".")
|
|
60
|
-
# @return [String
|
|
61
|
-
#
|
|
62
|
-
|
|
63
|
-
# @raise [ArgumentError] if output_path is nil and the :objects flag is not present
|
|
64
|
-
def invoke(
|
|
96
|
+
# @return [String] the (possibly extension-augmented) output path on success
|
|
97
|
+
# @raise [CompileError] if the underlying toolchain executable returns a non-zero exit status
|
|
98
|
+
def compile_and_link(
|
|
65
99
|
input_files,
|
|
66
100
|
output_path,
|
|
67
101
|
flags: [],
|
|
68
102
|
xflags: {},
|
|
69
103
|
include_paths: [],
|
|
70
104
|
defs: [],
|
|
105
|
+
link_paths: [],
|
|
71
106
|
libs: [],
|
|
72
|
-
linker_paths: [],
|
|
73
107
|
env: {},
|
|
74
108
|
working_dir: "."
|
|
75
109
|
)
|
|
76
|
-
output_type =
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
110
|
+
output_type = if flags.include?(:shared) then :shared
|
|
111
|
+
elsif flags.include?(:static) then :static
|
|
112
|
+
else :executable
|
|
113
|
+
end
|
|
114
|
+
output_path = apply_default_extension(output_path, output_type)
|
|
80
115
|
|
|
81
|
-
input_files = Array(input_files)
|
|
82
116
|
flags = translate_flags(flags)
|
|
83
117
|
flags.concat(xflags[@toolchain.class] || [])
|
|
84
118
|
|
|
85
|
-
|
|
86
|
-
|
|
119
|
+
cmds = @toolchain.compile_and_link_commands(
|
|
120
|
+
input_files,
|
|
121
|
+
output_path,
|
|
122
|
+
flags:,
|
|
123
|
+
include_paths:,
|
|
124
|
+
defs:,
|
|
125
|
+
libs:,
|
|
126
|
+
link_paths:
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
cmds.each { |cmd| run_command(cmd, env:, working_dir:) }
|
|
130
|
+
output_path
|
|
87
131
|
end
|
|
88
132
|
|
|
89
133
|
private
|
|
@@ -93,15 +137,8 @@ module MetaCC
|
|
|
93
137
|
toolchain = toolchain_class.new(search_paths:)
|
|
94
138
|
return toolchain if toolchain.available?
|
|
95
139
|
end
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
def output_type_from_flags(flags)
|
|
100
|
-
if flags.include?(:objects) then :objects
|
|
101
|
-
elsif flags.include?(:shared) then :shared
|
|
102
|
-
elsif flags.include?(:static) then :static
|
|
103
|
-
else :executable
|
|
104
|
-
end
|
|
140
|
+
candidate_names = candidates.map { |candidate| candidate.name.split("::").last }
|
|
141
|
+
raise ToolchainNotFoundError, "no supported C/C++ toolchain found (tried #{candidate_names.join(", ")})"
|
|
105
142
|
end
|
|
106
143
|
|
|
107
144
|
def apply_default_extension(path, output_type)
|
|
@@ -117,11 +154,15 @@ module MetaCC
|
|
|
117
154
|
raise "#{unrecognized_flag.inspect} is not a known flag"
|
|
118
155
|
end
|
|
119
156
|
|
|
157
|
+
flags << :no_omit_frame_pointer unless flags.include?(:omit_frame_pointer)
|
|
158
|
+
flags << :no_strict_aliasing unless flags.include?(:strict_aliasing)
|
|
159
|
+
|
|
120
160
|
flags.flat_map { |flag| @toolchain.flags[flag] }
|
|
121
161
|
end
|
|
122
162
|
|
|
123
163
|
def run_command(cmd, env: {}, working_dir: ".")
|
|
124
|
-
|
|
164
|
+
_out, err, status = Open3.capture3(env, *cmd, chdir: working_dir)
|
|
165
|
+
raise CompileError, err unless status.success?
|
|
125
166
|
end
|
|
126
167
|
|
|
127
168
|
end
|
data/lib/metacc/toolchain.rb
CHANGED
|
@@ -17,7 +17,7 @@ module MetaCC
|
|
|
17
17
|
@search_paths = search_paths
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
# Returns true if this toolchain's primary compiler
|
|
20
|
+
# Returns true if this toolchain's primary compiler can be found
|
|
21
21
|
def available?
|
|
22
22
|
command_available?(c)
|
|
23
23
|
end
|
|
@@ -26,14 +26,12 @@ module MetaCC
|
|
|
26
26
|
# The default implementation returns [:c, :cxx]. Subclasses that only
|
|
27
27
|
# support a subset of languages should override this method.
|
|
28
28
|
def languages
|
|
29
|
-
[
|
|
29
|
+
%i[c cxx]
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
# Returns true if +command+ is present in PATH, false otherwise.
|
|
33
33
|
# Intentionally ignores the exit status – only ENOENT (not found) matters.
|
|
34
34
|
def command_available?(command)
|
|
35
|
-
return false if command.nil?
|
|
36
|
-
|
|
37
35
|
!system(command, "--version", out: File::NULL, err: File::NULL).nil?
|
|
38
36
|
end
|
|
39
37
|
|
|
@@ -44,14 +42,31 @@ module MetaCC
|
|
|
44
42
|
|
|
45
43
|
# Returns a Hash mapping universal flags to native flags for this toolchain.
|
|
46
44
|
def flags
|
|
47
|
-
raise
|
|
45
|
+
raise "#{self.class}#flags not implemented"
|
|
48
46
|
end
|
|
49
47
|
|
|
50
48
|
# Returns the full command array for the given inputs, output, and flags.
|
|
51
49
|
# The output mode (object files, shared library, static library, or
|
|
52
50
|
# executable) is determined by the translated flags.
|
|
53
|
-
def
|
|
54
|
-
|
|
51
|
+
def compile_command(
|
|
52
|
+
input_files,
|
|
53
|
+
flags:,
|
|
54
|
+
include_paths:,
|
|
55
|
+
defs:
|
|
56
|
+
)
|
|
57
|
+
raise "#{self.class}#command not implemented"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def compile_and_link_commands(
|
|
61
|
+
input_files,
|
|
62
|
+
output_file,
|
|
63
|
+
flags:,
|
|
64
|
+
include_paths:,
|
|
65
|
+
defs:,
|
|
66
|
+
link_paths:,
|
|
67
|
+
libs:
|
|
68
|
+
)
|
|
69
|
+
raise "#{self.class}#command not implemented"
|
|
55
70
|
end
|
|
56
71
|
|
|
57
72
|
# Returns the default file extension (with leading dot, e.g. ".o") for the
|
|
@@ -102,58 +117,78 @@ module MetaCC
|
|
|
102
117
|
# GNU-compatible toolchain (gcc).
|
|
103
118
|
class GNU < Toolchain
|
|
104
119
|
|
|
105
|
-
def initialize(search_paths: [])
|
|
106
|
-
super
|
|
107
|
-
@c
|
|
120
|
+
def initialize(cc_command = "gcc", search_paths: [])
|
|
121
|
+
super(search_paths:)
|
|
122
|
+
@c = resolve_command(cc_command)
|
|
108
123
|
end
|
|
109
124
|
|
|
110
|
-
def
|
|
125
|
+
def compile_command(
|
|
126
|
+
input_files,
|
|
127
|
+
flags:,
|
|
128
|
+
include_paths:,
|
|
129
|
+
defs:
|
|
130
|
+
)
|
|
111
131
|
inc_flags = include_paths.map { |p| "-I#{p}" }
|
|
112
|
-
def_flags =
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
132
|
+
def_flags = defs.map { |d| "-D#{d}" }
|
|
133
|
+
[c, "-c", *flags, *inc_flags, *def_flags, *input_files]
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def compile_and_link_commands(
|
|
137
|
+
input_files,
|
|
138
|
+
output_file,
|
|
139
|
+
flags:,
|
|
140
|
+
include_paths:,
|
|
141
|
+
defs:,
|
|
142
|
+
link_paths:,
|
|
143
|
+
libs:
|
|
144
|
+
)
|
|
145
|
+
inc_flags = include_paths.map { |p| "-I#{p}" }
|
|
146
|
+
def_flags = defs.map { |d| "-D#{d}" }
|
|
147
|
+
lib_path_flags = link_paths.map { |p| "-L#{p}" }
|
|
148
|
+
lib_flags = libs.map { |l| "-l#{l}" }
|
|
149
|
+
[[c, *flags, *inc_flags, *def_flags, *input_files, *lib_path_flags, *lib_flags, "-o", output_file]]
|
|
118
150
|
end
|
|
119
151
|
|
|
120
152
|
GNU_FLAGS = {
|
|
121
|
-
o0:
|
|
122
|
-
o1:
|
|
123
|
-
o2:
|
|
124
|
-
o3:
|
|
125
|
-
os:
|
|
126
|
-
sse4_2:
|
|
127
|
-
avx:
|
|
128
|
-
avx2:
|
|
129
|
-
avx512:
|
|
130
|
-
native:
|
|
131
|
-
|
|
132
|
-
lto:
|
|
133
|
-
warn_all:
|
|
134
|
-
warn_error:
|
|
135
|
-
c11:
|
|
136
|
-
c17:
|
|
137
|
-
c23:
|
|
138
|
-
cxx11:
|
|
139
|
-
cxx14:
|
|
140
|
-
cxx17:
|
|
141
|
-
cxx20:
|
|
142
|
-
cxx23:
|
|
143
|
-
cxx26:
|
|
144
|
-
asan:
|
|
145
|
-
ubsan:
|
|
146
|
-
msan:
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
153
|
+
o0: ["-O0"],
|
|
154
|
+
o1: ["-O1"],
|
|
155
|
+
o2: ["-O2"],
|
|
156
|
+
o3: ["-O3"],
|
|
157
|
+
os: ["-Os"],
|
|
158
|
+
sse4_2: ["-march=x86-64-v2"], # This is a better match for /arch:SSE4.2 than -msse4_2 is
|
|
159
|
+
avx: ["-march=x86-64-v2", "-mavx"],
|
|
160
|
+
avx2: ["-march=x86-64-v3"], # This is a better match for /arch:AVX2 than -mavx2 is
|
|
161
|
+
avx512: ["-march=x86-64-v4"],
|
|
162
|
+
native: ["-march=native", "-mtune=native"],
|
|
163
|
+
debug_info: ["-g3"],
|
|
164
|
+
lto: ["-flto"],
|
|
165
|
+
warn_all: ["-Wall", "-Wextra", "-pedantic"],
|
|
166
|
+
warn_error: ["-Werror"],
|
|
167
|
+
c11: ["-std=c11"],
|
|
168
|
+
c17: ["-std=c17"],
|
|
169
|
+
c23: ["-std=c23"],
|
|
170
|
+
cxx11: ["-std=c++11"],
|
|
171
|
+
cxx14: ["-std=c++14"],
|
|
172
|
+
cxx17: ["-std=c++17"],
|
|
173
|
+
cxx20: ["-std=c++20"],
|
|
174
|
+
cxx23: ["-std=c++23"],
|
|
175
|
+
cxx26: ["-std=c++2c"],
|
|
176
|
+
asan: ["-fsanitize=address"],
|
|
177
|
+
ubsan: ["-fsanitize=undefined"],
|
|
178
|
+
msan: ["-fsanitize=memory"],
|
|
179
|
+
lsan: ["-fsanitize=leak"],
|
|
180
|
+
no_rtti: ["-fno-rtti"],
|
|
181
|
+
no_exceptions: ["-fno-exceptions", "-fno-unwind-tables"],
|
|
182
|
+
pic: ["-fPIC"],
|
|
183
|
+
omit_frame_pointer: ["-fomit-frame-pointer"],
|
|
184
|
+
no_omit_frame_pointer: ["-fno-omit-frame-pointer"],
|
|
185
|
+
strict_aliasing: ["-fstrict-aliasing"],
|
|
186
|
+
no_strict_aliasing: ["-fno-strict-aliasing"],
|
|
187
|
+
shared: ["-shared", "-Bsymbolic-non-weak-functions", "-fno-semantic-interposition"],
|
|
188
|
+
shared_compat: ["-shared"],
|
|
189
|
+
static: ["-static"],
|
|
190
|
+
strip: ["-Wl,--strip-unneeded"],
|
|
191
|
+
debug: ["-D_GLIBCXX_DEBUG", "-fasynchronous-unwind-tables"]
|
|
157
192
|
}.freeze
|
|
158
193
|
|
|
159
194
|
def flags
|
|
@@ -166,8 +201,7 @@ module MetaCC
|
|
|
166
201
|
class Clang < GNU
|
|
167
202
|
|
|
168
203
|
def initialize(search_paths: [])
|
|
169
|
-
super
|
|
170
|
-
@c = resolve_command("clang")
|
|
204
|
+
super("clang", search_paths:)
|
|
171
205
|
end
|
|
172
206
|
|
|
173
207
|
CLANG_FLAGS = GNU_FLAGS.merge(lto: ["-flto=thin"]).freeze
|
|
@@ -190,63 +224,78 @@ module MetaCC
|
|
|
190
224
|
def initialize(cl_command = "cl", search_paths: [])
|
|
191
225
|
super(search_paths:)
|
|
192
226
|
resolved_cmd = resolve_command(cl_command)
|
|
193
|
-
@c
|
|
227
|
+
@c = resolved_cmd
|
|
194
228
|
setup_msvc_environment(resolved_cmd)
|
|
195
229
|
end
|
|
196
230
|
|
|
197
|
-
def
|
|
231
|
+
def compile_command(
|
|
232
|
+
input_files,
|
|
233
|
+
flags:,
|
|
234
|
+
include_paths:,
|
|
235
|
+
defs:
|
|
236
|
+
)
|
|
198
237
|
inc_flags = include_paths.map { |p| "/I#{p}" }
|
|
199
|
-
def_flags =
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
238
|
+
def_flags = defs.map { |d| "/D#{d}" }
|
|
239
|
+
[c, "/c", *flags, *inc_flags, *def_flags, *input_files]
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def compile_and_link_commands(
|
|
243
|
+
input_files,
|
|
244
|
+
output_file,
|
|
245
|
+
flags:,
|
|
246
|
+
include_paths:,
|
|
247
|
+
defs:,
|
|
248
|
+
link_paths:,
|
|
249
|
+
libs:
|
|
250
|
+
)
|
|
251
|
+
inc_flags = include_paths.map { |p| "/I#{p}" }
|
|
252
|
+
def_flags = defs.map { |d| "/D#{d}" }
|
|
253
|
+
lib_flags = libs.map { |l| "#{l}.lib" }
|
|
254
|
+
lib_path_flags = link_paths.map { |p| "/LIBPATH:#{p}" }
|
|
255
|
+
cmd = [c, *flags, *inc_flags, *def_flags, *input_files, *lib_flags, "/Fe#{output_file}"]
|
|
256
|
+
cmd += ["/link", *lib_path_flags] unless lib_path_flags.empty?
|
|
257
|
+
[cmd]
|
|
211
258
|
end
|
|
212
259
|
|
|
213
260
|
MSVC_FLAGS = {
|
|
214
|
-
o0:
|
|
215
|
-
o1:
|
|
216
|
-
o2:
|
|
217
|
-
o3:
|
|
218
|
-
os:
|
|
219
|
-
sse4_2:
|
|
220
|
-
avx:
|
|
221
|
-
avx2:
|
|
222
|
-
avx512:
|
|
223
|
-
native:
|
|
224
|
-
|
|
225
|
-
lto:
|
|
226
|
-
warn_all:
|
|
227
|
-
warn_error:
|
|
228
|
-
c11:
|
|
229
|
-
c17:
|
|
230
|
-
c23:
|
|
231
|
-
cxx11:
|
|
232
|
-
cxx14:
|
|
233
|
-
cxx17:
|
|
234
|
-
cxx20:
|
|
235
|
-
cxx23:
|
|
236
|
-
cxx26:
|
|
237
|
-
asan:
|
|
238
|
-
ubsan:
|
|
239
|
-
msan:
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
261
|
+
o0: ["/Od"],
|
|
262
|
+
o1: ["/O1"],
|
|
263
|
+
o2: ["/O2"],
|
|
264
|
+
o3: ["/O2", "/Ob3"],
|
|
265
|
+
os: ["/O1"],
|
|
266
|
+
sse4_2: ["/arch:SSE4.2"],
|
|
267
|
+
avx: ["/arch:AVX"],
|
|
268
|
+
avx2: ["/arch:AVX2"],
|
|
269
|
+
avx512: ["/arch:AVX512"],
|
|
270
|
+
native: [],
|
|
271
|
+
debug_info: ["/Zi"],
|
|
272
|
+
lto: ["/GL"],
|
|
273
|
+
warn_all: ["/W4"],
|
|
274
|
+
warn_error: ["/WX"],
|
|
275
|
+
c11: ["/std:c11"],
|
|
276
|
+
c17: ["/std:c17"],
|
|
277
|
+
c23: ["/std:clatest"],
|
|
278
|
+
cxx11: [],
|
|
279
|
+
cxx14: ["/std:c++14"],
|
|
280
|
+
cxx17: ["/std:c++17"],
|
|
281
|
+
cxx20: ["/std:c++20"],
|
|
282
|
+
cxx23: ["/std:c++23preview"],
|
|
283
|
+
cxx26: ["/std:c++latest"],
|
|
284
|
+
asan: ["/fsanitize=address"],
|
|
285
|
+
ubsan: [],
|
|
286
|
+
msan: [],
|
|
287
|
+
lsan: [],
|
|
288
|
+
no_rtti: ["/GR-"],
|
|
289
|
+
no_exceptions: ["/EHs-", "/EHc-"],
|
|
290
|
+
pic: [],
|
|
291
|
+
omit_frame_pointer: ["/Oy"],
|
|
292
|
+
no_omit_frame_pointer: ["/Oy-"],
|
|
293
|
+
strict_aliasing: ["-fstrict-aliasing"],
|
|
294
|
+
no_strict_aliasing: ["-fno-strict-aliasing"],
|
|
295
|
+
shared: ["/LD"],
|
|
296
|
+
shared_compat: ["/LD"],
|
|
297
|
+
static: ["/c"],
|
|
298
|
+
strip: []
|
|
250
299
|
}.freeze
|
|
251
300
|
|
|
252
301
|
def flags
|
|
@@ -285,70 +334,55 @@ module MetaCC
|
|
|
285
334
|
def setup_msvc_environment(cl_command)
|
|
286
335
|
return if command_available?(cl_command)
|
|
287
336
|
|
|
288
|
-
devenv_path =
|
|
289
|
-
|
|
337
|
+
devenv_path = MSVC.vswhere("-path", "-property", "productPath") ||
|
|
338
|
+
MSVC.vswhere("-latest", "-prerelease", "-property", "productPath")
|
|
290
339
|
return unless devenv_path
|
|
291
340
|
|
|
292
|
-
vcvarsall
|
|
293
|
-
return unless vcvarsall
|
|
294
|
-
|
|
295
|
-
run_vcvarsall(vcvarsall)
|
|
341
|
+
MSVC.vcvarsall(devenv_path)
|
|
296
342
|
end
|
|
297
343
|
|
|
298
344
|
# Runs vswhere.exe with the given arguments and returns the trimmed stdout,
|
|
299
345
|
# or nil if vswhere.exe is absent, the command fails, or produces no output.
|
|
300
|
-
def
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
stdout = IO.popen([VSWHERE_PATH, *args], &:read)
|
|
346
|
+
def self.vswhere(*args)
|
|
347
|
+
path = IO.popen([VSWHERE_PATH, *args], &:read).strip
|
|
304
348
|
status = $?
|
|
305
|
-
return nil unless status.success?
|
|
306
349
|
|
|
307
|
-
path
|
|
308
|
-
path.empty? ? nil : path
|
|
350
|
+
status.success? && !path.empty? ? path : nil
|
|
309
351
|
rescue Errno::ENOENT
|
|
310
352
|
nil
|
|
311
353
|
end
|
|
312
354
|
|
|
313
|
-
#
|
|
355
|
+
# Runs vcvarsall.bat for the x64 architecture and merges the resulting
|
|
356
|
+
# environment variables into the current process's ENV so that cl.exe
|
|
357
|
+
# and related tools become available on PATH.
|
|
358
|
+
#
|
|
359
|
+
# Finds the path to vcvarsall.bat for the given devenv.exe path, or nil
|
|
314
360
|
# if it cannot be located. devenv.exe lives at:
|
|
315
361
|
# <root>\Common7\IDE\devenv.exe
|
|
316
362
|
# vcvarsall.bat lives at:
|
|
317
363
|
# <root>\VC\Auxiliary\Build\vcvarsall.bat
|
|
318
|
-
|
|
364
|
+
#
|
|
365
|
+
# Parses the output of `vcvarsall.bat … && set` and merges the resulting
|
|
366
|
+
# environment variables into the current process's ENV.
|
|
367
|
+
def MSVC.vcvarsall(devenv_path)
|
|
368
|
+
# See https://stackoverflow.com/a/19929778
|
|
369
|
+
return if ENV.has_key?("DevEnvDir")
|
|
370
|
+
|
|
371
|
+
# Calculate the location of vcvarsall.bat
|
|
319
372
|
install_root = File.expand_path("../../..", devenv_path)
|
|
373
|
+
|
|
374
|
+
# Check if a file is actually present there
|
|
320
375
|
vcvarsall = File.join(install_root, "VC", "Auxiliary", "Build", "vcvarsall.bat")
|
|
321
|
-
File.exist?(vcvarsall)
|
|
322
|
-
end
|
|
376
|
+
return unless File.exist?(vcvarsall)
|
|
323
377
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
# and related tools become available on PATH.
|
|
327
|
-
def run_vcvarsall(vcvarsall)
|
|
328
|
-
stdout = IO.popen(["cmd.exe", "/c", vcvarsall_command(vcvarsall)], &:read)
|
|
378
|
+
# Run vcvarsall.bat and dump the environment to the shell
|
|
379
|
+
output = `"#{vcvarsall}" x64 && set`
|
|
329
380
|
status = $?
|
|
330
381
|
return unless status.success?
|
|
331
382
|
|
|
332
|
-
load_vcvarsall(stdout)
|
|
333
|
-
end
|
|
334
|
-
|
|
335
|
-
# Builds the cmd.exe command string for calling vcvarsall.bat and capturing
|
|
336
|
-
# the resulting environment variables. The path is double-quoted to handle
|
|
337
|
-
# spaces; any embedded double quotes are escaped by doubling them, which is
|
|
338
|
-
# the cmd.exe convention inside a double-quoted string. Shellwords is not
|
|
339
|
-
# used here because it produces POSIX sh escaping, which is incompatible
|
|
340
|
-
# with cmd.exe syntax.
|
|
341
|
-
def vcvarsall_command(vcvarsall)
|
|
342
|
-
quoted = '"' + vcvarsall.gsub('"', '""') + '"'
|
|
343
|
-
"#{quoted} x64 && set"
|
|
344
|
-
end
|
|
345
|
-
|
|
346
|
-
# Parses the output of `vcvarsall.bat … && set` and merges the resulting
|
|
347
|
-
# environment variables into the current process's ENV.
|
|
348
|
-
def load_vcvarsall(output)
|
|
349
383
|
output.each_line do |line|
|
|
350
|
-
key,
|
|
351
|
-
next if
|
|
384
|
+
key, value = line.chomp.split("=", 2)
|
|
385
|
+
next if value.to_s.empty?
|
|
352
386
|
|
|
353
387
|
ENV[key] = value
|
|
354
388
|
end
|
|
@@ -376,11 +410,20 @@ module MetaCC
|
|
|
376
410
|
end
|
|
377
411
|
|
|
378
412
|
# TinyCC toolchain (tcc). TinyCC only supports C, not C++.
|
|
379
|
-
class TinyCC <
|
|
413
|
+
class TinyCC < GNU
|
|
380
414
|
|
|
381
415
|
def initialize(search_paths: [])
|
|
382
|
-
super
|
|
383
|
-
@
|
|
416
|
+
super("tcc", search_paths:)
|
|
417
|
+
@ar = resolve_command("ar")
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
def compile_and_link_commands(input_files, output_file, **options)
|
|
421
|
+
commands = super(input_files, output_file, **options)
|
|
422
|
+
if options[:flags].include?(:static)
|
|
423
|
+
object_files = input_files.map { |f| f.sub(/\.c\z/, ".o") }
|
|
424
|
+
commands << [@ar, "rcs", output_file, *object_files]
|
|
425
|
+
end
|
|
426
|
+
commands
|
|
384
427
|
end
|
|
385
428
|
|
|
386
429
|
# TinyCC does not support C++.
|
|
@@ -388,53 +431,47 @@ module MetaCC
|
|
|
388
431
|
[:c]
|
|
389
432
|
end
|
|
390
433
|
|
|
391
|
-
def
|
|
392
|
-
|
|
393
|
-
def_flags = definitions.map { |d| "-D#{d}" }
|
|
394
|
-
link_mode = !flags.include?("-c")
|
|
395
|
-
lib_path_flags = link_mode ? linker_include_dirs.map { |p| "-L#{p}" } : []
|
|
396
|
-
lib_flags = link_mode ? libs.map { |l| "-l#{l}" } : []
|
|
397
|
-
cmd = [c, *flags, *inc_flags, *def_flags, *input_files, *lib_path_flags, *lib_flags]
|
|
398
|
-
output.nil? ? cmd : [*cmd, "-o", output]
|
|
434
|
+
def version_banner
|
|
435
|
+
IO.popen([c, "-v", { err: :out }], &:read)
|
|
399
436
|
end
|
|
400
437
|
|
|
401
438
|
TINYCC_FLAGS = {
|
|
402
|
-
o0:
|
|
403
|
-
o1:
|
|
404
|
-
o2:
|
|
405
|
-
o3:
|
|
406
|
-
os:
|
|
407
|
-
sse4_2:
|
|
408
|
-
avx:
|
|
409
|
-
avx2:
|
|
410
|
-
avx512:
|
|
411
|
-
native:
|
|
412
|
-
|
|
413
|
-
lto:
|
|
414
|
-
warn_all:
|
|
415
|
-
warn_error:
|
|
416
|
-
c11:
|
|
417
|
-
c17:
|
|
418
|
-
c23:
|
|
419
|
-
cxx11:
|
|
420
|
-
cxx14:
|
|
421
|
-
cxx17:
|
|
422
|
-
cxx20:
|
|
423
|
-
cxx23:
|
|
424
|
-
cxx26:
|
|
425
|
-
asan:
|
|
426
|
-
ubsan:
|
|
427
|
-
msan:
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
static:
|
|
437
|
-
strip:
|
|
439
|
+
o0: [],
|
|
440
|
+
o1: ["-O1"],
|
|
441
|
+
o2: ["-O2"],
|
|
442
|
+
o3: ["-O2"],
|
|
443
|
+
os: [],
|
|
444
|
+
sse4_2: [],
|
|
445
|
+
avx: [],
|
|
446
|
+
avx2: [],
|
|
447
|
+
avx512: [],
|
|
448
|
+
native: [],
|
|
449
|
+
debug_info: ["-g"],
|
|
450
|
+
lto: [],
|
|
451
|
+
warn_all: ["-Wall"],
|
|
452
|
+
warn_error: ["-Werror"],
|
|
453
|
+
c11: [],
|
|
454
|
+
c17: [],
|
|
455
|
+
c23: [],
|
|
456
|
+
cxx11: [],
|
|
457
|
+
cxx14: [],
|
|
458
|
+
cxx17: [],
|
|
459
|
+
cxx20: [],
|
|
460
|
+
cxx23: [],
|
|
461
|
+
cxx26: [],
|
|
462
|
+
asan: [],
|
|
463
|
+
ubsan: [],
|
|
464
|
+
msan: [],
|
|
465
|
+
leak: [],
|
|
466
|
+
no_rtti: [],
|
|
467
|
+
no_exceptions: [],
|
|
468
|
+
pic: [],
|
|
469
|
+
keep_frame_pointer: [],
|
|
470
|
+
relaxed_aliasing: [],
|
|
471
|
+
shared: ["-shared"],
|
|
472
|
+
shared_compat: ["-shared"],
|
|
473
|
+
static: ["-c"],
|
|
474
|
+
strip: []
|
|
438
475
|
}.freeze
|
|
439
476
|
|
|
440
477
|
def flags
|
data/lib/metacc.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: metacc
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Praneeth Sadda
|
|
@@ -23,9 +23,13 @@ files:
|
|
|
23
23
|
- lib/metacc/cli.rb
|
|
24
24
|
- lib/metacc/driver.rb
|
|
25
25
|
- lib/metacc/toolchain.rb
|
|
26
|
+
- lib/metacc/version.rb
|
|
27
|
+
homepage: https://github.com/psadda/metacc
|
|
26
28
|
licenses:
|
|
27
29
|
- BSD-3-Clause
|
|
28
30
|
metadata:
|
|
31
|
+
homepage_uri: https://github.com/psadda/metacc
|
|
32
|
+
source_code_uri: https://github.com/psadda/metacc
|
|
29
33
|
rubygems_mfa_required: 'true'
|
|
30
34
|
rdoc_options: []
|
|
31
35
|
require_paths:
|
|
@@ -34,7 +38,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
34
38
|
requirements:
|
|
35
39
|
- - ">="
|
|
36
40
|
- !ruby/object:Gem::Version
|
|
37
|
-
version:
|
|
41
|
+
version: 3.2.0
|
|
38
42
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
39
43
|
requirements:
|
|
40
44
|
- - ">="
|