metacc 0.2.0 → 0.4.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: a14fc8ca9c502275a4cc8105906722c167fb58cf6a269559748da22cf88f83d3
4
- data.tar.gz: f56a289ba4240caf683f8614f5317252753032ad8902cce314a0ab43380d05fd
3
+ metadata.gz: bb94d9b47a11ec5a78ea57ae19417e6423287918287f675e43579af8015e4c63
4
+ data.tar.gz: b1237b3e41f2ae7ae68066441d742892ca77fd0427dc3e7b8f02b7f92049e535
5
5
  SHA512:
6
- metadata.gz: 2b5c6055a1445b3e0341fbde3da0ca9b76e2cfe021c0c7694be91233a2eb2c68de7f063cc5c2b9b953b697fdfa41f3a949b168eb33790d8544fedeeef90f222a
7
- data.tar.gz: 30b826250c85ce5d7b57c83f81714d7ac51b6e911abed4ea8371e3bc36540f0b29779aeced94b398b7d590ffbbbf608c7ab2929f0d9743a8967824edad8bea59
6
+ metadata.gz: 4774d4160c9939259634b2625b28a2975360c477c56c53ac689b4208defb698c4fbcbccf829f540583dfb94cf801fc62664e94cc900f52c64c28bfd6f763247c
7
+ data.tar.gz: dfd5493d3789ea3f18dbd1d7a6a15c345fe6eaeb262e022af7a79039bdd7b2707416312fc08e1ca86b333d91520dbb41457f6be5006d30157af934d4fe8b1497
data/bin/metacc CHANGED
@@ -7,7 +7,12 @@ require "metacc/cli"
7
7
 
8
8
  begin
9
9
  MetaCC::CLI.new.run(ARGV)
10
- rescue MetaCC::CLI::InvalidOption, MetaCC::ToolchainNotFoundError => e
10
+ rescue MetaCC::ToolchainNotFoundError,
11
+ OptionParser::AmbiguousOption,
12
+ OptionParser::MissingArgument,
13
+ OptionParser::NeedlessArgument,
14
+ OptionParser::InvalidArgument,
15
+ OptionParser::InvalidOption => e
11
16
  warn "#{$0} error: #{e.message}"
12
17
  rescue MetaCC::CompileError => e
13
18
  warn e.message
data/lib/metacc/cli.rb CHANGED
@@ -9,57 +9,19 @@ 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
- # -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
+ nil => :sanitize_default,
21
+ "memory" => :sanitize_memory,
22
+ "thread" => :sanitize_thread,
23
+ }
24
+
63
25
  TARGETS = {
64
26
  "sse4.2" => :sse4_2,
65
27
  "avx" => :avx,
@@ -85,7 +47,8 @@ module MetaCC
85
47
  "xmsvc" => MSVC,
86
48
  "xgnu" => GNU,
87
49
  "xclang" => Clang,
88
- "xclangcl" => ClangCL
50
+ "xclangcl" => ClangCL,
51
+ "xtinycc" => TinyCC
89
52
  }.freeze
90
53
 
91
54
  def initialize(driver: Driver.new)
@@ -110,6 +73,7 @@ module MetaCC
110
73
  libs: [],
111
74
  output_path: nil,
112
75
  run: false,
76
+ dry_run: false,
113
77
  flags: [],
114
78
  xflags: {}
115
79
  }
@@ -122,6 +86,11 @@ module MetaCC
122
86
  private
123
87
 
124
88
  def setup_compile_options(parser, options)
89
+ parser.require_exact = true
90
+
91
+ parser.separator ""
92
+ parser.separator "General options:"
93
+
125
94
  parser.on("-o FILEPATH", "Output file path") do |value|
126
95
  options[:output_path] = value
127
96
  end
@@ -131,26 +100,85 @@ module MetaCC
131
100
  parser.on("-D DEF", "Add a preprocessor definition") do |value|
132
101
  options[:defs] << value
133
102
  end
134
- parser.on("-O LEVEL", /\A[0-3]|s\z/, "Optimization level (0–3)") do |level|
103
+ parser.on("--std=STANDARD", "Specify the language standard") do |value|
104
+ options[:flags] << STANDARDS[value]
105
+ end
106
+ parser.on(
107
+ "-W OPTION", "Configure warnings",
108
+ " -Wall => Enable a broad range of warnings",
109
+ " -Werror => Treat warnings as errrs"
110
+ ) do |value|
111
+ options[:flags] << WARNING_CONFIGS[value]
112
+ end
113
+ parser.on("-r", "--run", "Run the compiled executable after a successful build") do
114
+ options[:run] = true
115
+ end
116
+ parser.on("--dry-run", "Print the command that would be issued") do
117
+ options[:dry_run] = true
118
+ end
119
+
120
+ parser.separator ""
121
+ parser.separator "Debugging:"
122
+
123
+ parser.on("-g", "--debug-info", "Emit debugging symbols") do
124
+ options[:flags] << :debug_info
125
+ end
126
+ parser.on(
127
+ "-S", "--sanitize=[SANITIZER]",
128
+ "Enable sanitizer(s)",
129
+ " --sanitize => address, undefined, leak",
130
+ " --sanitize=memory => memory",
131
+ " --sanitize=thread => thread",
132
+ ) do |value|
133
+ options[:flags] << SANITIZERS[value]
134
+ end
135
+
136
+ parser.separator ""
137
+ parser.separator "Optimization:"
138
+
139
+ parser.on("-O LEVEL", /\A[0-3]|s\z/, "Optimization level (0, 1, 2, 3, or s)") do |level|
135
140
  options[:flags] << :"o#{level}"
136
141
  end
137
- parser.on("-m", "--arch ARCH", "Target architecture") do |value|
142
+ parser.on("--lto", "Enable link time optimization") do
143
+ options[:flags] << :lto
144
+ end
145
+ parser.on("--omit-frame-pointer") do |value|
146
+ options[:flags] << :omit_frame_pointer
147
+ end
148
+ parser.on("--strict-aliasing") do |value|
149
+ options[:flags] << :strict_aliasing
150
+ end
151
+
152
+ parser.separator ""
153
+ parser.separator "Code generation:"
154
+
155
+ parser.on("-m", "--arch=ARCH", "Target architecture") do |value|
138
156
  options[:flags] << TARGETS[value]
139
157
  end
140
- parser.on("-g", "--debug", "Emit debugging symbols") do
141
- options[:flags] << :debug
158
+ parser.on("--pic", "Generate position independent code") do |value|
159
+ options[:flags] << :pic
142
160
  end
143
- parser.on("--std STANDARD", "Specify the language standard") do |value|
144
- options[:flags] << STANDARDS[value]
161
+ parser.on("--no-rtti", "Disable runtime type information") do |value|
162
+ options[:flags] << :no_rtti
145
163
  end
146
- parser.on("-W OPTION", "Configure warnings") do |value|
147
- options[:flags] << WARNING_CONFIGS[value]
164
+ parser.on("--no-exceptions", "Disable exceptions (and unwinding info)") do |value|
165
+ options[:flags] << :no_exceptions
148
166
  end
149
- parser.on("-c", "Produce object files") do
150
- options[:link] = false
167
+
168
+ parser.separator ""
169
+ parser.separator "Linking:"
170
+
171
+ parser.on("--static", "Produce a static library") do
172
+ options[:flags] << :static
151
173
  end
152
- parser.on("-r", "--run", "Run the compiled executable after a successful build") do
153
- options[:run] = true
174
+ parser.on("--shared", "Produce a shared library") do |value|
175
+ options[:flags] << :shared
176
+ end
177
+ parser.on("--shared-compat", "Produce a shared library with full LD_PRELOAD compatability") do |value|
178
+ options[:flags] << :shared_compat
179
+ end
180
+ parser.on("-c", "Compile only (produce object files without linking)") do
181
+ options[:link] = false
154
182
  end
155
183
  parser.on("-l LIB", "Link against library LIB") do |value|
156
184
  options[:libs] << value
@@ -158,59 +186,71 @@ module MetaCC
158
186
  parser.on("-L DIR", "Add linker library search path") do |value|
159
187
  options[:link_paths] << value
160
188
  end
161
- parser.on("--shared", "Produce a shared library") do
162
- options[:flags] << :shared
163
- end
164
- parser.on("--static", "Produce a static library") do
165
- options[:flags] << :static
166
- end
167
189
  parser.on("-s", "--strip", "Strip unneeded symbols") do
168
190
  options[:flags] << :strip
169
191
  end
170
- LONG_FLAGS.each do |name, sym|
171
- parser.on("--#{name}") do
172
- options[:flags] << sym
173
- end
174
- end
175
- XFLAGS.each do |name, tc_class|
176
- parser.on("--#{name} VALUE", "Pass VALUE to the #{tc_class} toolchain") do |value|
177
- options[:xflags][tc_class] ||= []
178
- options[:xflags][tc_class] << value
192
+
193
+ parser.separator ""
194
+ parser.separator "Compiler specific:"
195
+
196
+ XFLAGS.each do |name, toolchain_class|
197
+ toolchain_name = toolchain_class.name.split("::").last
198
+ parser.on("--#{name} FLAG", "Forward FLAG to the compiler if compiling with #{toolchain_name}") do |value|
199
+ options[:xflags][toolchain_class] ||= []
200
+ options[:xflags][toolchain_class] << value
179
201
  end
180
202
  end
203
+
204
+ parser.separator ""
205
+ parser.separator "Informational:"
206
+
181
207
  parser.on_tail("--version", "Print the toolchain version and exit") do
182
208
  puts @driver.toolchain.version_banner
183
209
  exit
184
210
  end
211
+ parser.on_tail("-h", "--help", "Show this message") do
212
+ puts parser
213
+ exit
214
+ end
185
215
  end
186
216
 
187
217
  def validate_options!(flags, output_path, link:, run:)
188
218
  if !link && output_path
189
- raise InvalidOption, "cannot specify output path (-o) in compile only mode (-c)"
219
+ raise OptionParser::InvalidOption, "cannot specify output path (-o) in compile only mode (-c)"
190
220
  end
191
221
 
192
222
  if link && !output_path
193
- raise InvalidOption, "must specify an output path (-o)"
223
+ raise OptionParser::InvalidOption, "must specify an output path (-o)"
224
+ end
225
+
226
+ if run && (!link || %i[static shared shared_compat].any? { |f| flags.include?(f) })
227
+ raise OptionParser::InvalidOption, "--run cannot be used with -c, --static, --shared, or --shared-compat"
194
228
  end
195
229
 
196
- if run && (!link || flags.include?(:shared) || flags.include?(:static))
197
- raise InvalidOption, "--run may not be used with -c, --shared, or --static"
230
+ if !link && %i[static shared shared_compat].any? { |f| flags.include?(f) }
231
+ raise OptionParser::InvalidOption, "compile only mode (-c) cannot be used with --static, --shared, or --shared-compat"
232
+ end
233
+
234
+ if flags.include?(:debug_info) && flags.include?(:strip)
235
+ raise OptionParser::InvalidOption, "--debug-info (-g) cannot be combined with --strip (-s)"
198
236
  end
199
237
  end
200
238
 
201
239
  def invoke(input_paths, desired_output_path = nil, link: true, run: false, **options)
240
+ result = nil
202
241
  if link
203
- actual_output_path = @driver.compile_and_link(input_paths, desired_output_path, **options)
204
- system(actual_output_path) if run
242
+ result = @driver.compile_and_link(input_paths, desired_output_path, **options)
243
+ system(result) if run && !options[:dry_run]
205
244
  else
206
245
  options.delete(:link_paths)
207
246
  options.delete(:libs)
208
- @driver.compile(input_paths, **options)
247
+ result = @driver.compile(input_paths, **options)
248
+ end
249
+ if options[:dry_run]
250
+ puts result.map { |cmd| cmd.join(" ") }
209
251
  end
210
252
  end
211
253
 
212
- class InvalidOption < StandardError; end
213
-
214
254
  end
215
255
 
216
256
  end
data/lib/metacc/driver.rb CHANGED
@@ -15,20 +15,24 @@ module MetaCC
15
15
  # available compiler found on the system (Clang, GCC, or MSVC).
16
16
  class Driver
17
17
 
18
- RECOGNIZED_FLAGS = Set.new(
19
- %i[
20
- o0 o1 o2 o3 os
21
- sse4_2 avx avx2 avx512 native
22
- debug lto
18
+ LANGUAGE_STD_FLAGS = Set.new(%i[c11 c17 c23 cxx11 cxx14 cxx17 cxx20 cxx23 cxx26]).freeze
19
+ ARCHITECTURE_FLAGS = Set.new(%i[sse4_2 avx avx2 avx512 native]).freeze
20
+ OPTIMIZATION_FLAGS = Set.new(%i[o0 o1 o2 o3 os]).freeze
21
+ DBG_SANITIZE_FLAGS = Set.new(%i[sanitize_default sanitize_memory sanitize_thread]).freeze
22
+
23
+ ALL_FLAGS = Set.new([
24
+ *%i[
23
25
  warn_all warn_error
24
- c11 c17 c23
25
- cxx11 cxx14 cxx17 cxx20 cxx23 cxx26
26
- asan ubsan msan
27
- no_rtti no_exceptions pic
28
- no_semantic_interposition no_omit_frame_pointer no_strict_aliasing
29
- shared static strip
30
- ]
31
- ).freeze
26
+ debug_info
27
+ omit_frame_pointer strict_aliasing
28
+ no_rtti no_exceptions
29
+ pic shared shared_compat static strip
30
+ ],
31
+ *LANGUAGE_STD_FLAGS,
32
+ *ARCHITECTURE_FLAGS,
33
+ *OPTIMIZATION_FLAGS,
34
+ *DBG_SANITIZE_FLAGS
35
+ ]).freeze
32
36
 
33
37
  # The detected toolchain (a Toolchain subclass instance).
34
38
  attr_reader :toolchain
@@ -37,11 +41,11 @@ module MetaCC
37
41
  #
38
42
  # @param prefer [Array<Class>] toolchain classes to probe, in priority order.
39
43
  # Each element must be a Class derived from Toolchain.
40
- # Defaults to [Clang, GNU, MSVC].
44
+ # Defaults to [Clang, GCC, MSVC].
41
45
  # @param search_paths [Array<String>] directories to search for toolchain executables
42
46
  # before falling back to PATH. Defaults to [].
43
47
  # @raise [ToolchainNotFoundError] if no supported compiler is found.
44
- def initialize(prefer: [MSVC], search_paths: [])
48
+ def initialize(prefer: [Clang, GCC, MSVC], search_paths: [])
45
49
  @toolchain = select_toolchain!(prefer, search_paths)
46
50
  end
47
51
 
@@ -63,7 +67,8 @@ module MetaCC
63
67
  include_paths: [],
64
68
  defs: [],
65
69
  env: {},
66
- working_dir: "."
70
+ working_dir: ".",
71
+ dry_run: false
67
72
  )
68
73
  flags = translate_flags(flags)
69
74
  flags.concat(xflags[@toolchain.class] || [])
@@ -75,6 +80,8 @@ module MetaCC
75
80
  defs:
76
81
  )
77
82
 
83
+ return [cmd] if dry_run
84
+
78
85
  !!run_command(cmd, env:, working_dir:)
79
86
  end
80
87
 
@@ -105,7 +112,8 @@ module MetaCC
105
112
  link_paths: [],
106
113
  libs: [],
107
114
  env: {},
108
- working_dir: "."
115
+ working_dir: ".",
116
+ dry_run: false
109
117
  )
110
118
  output_type = if flags.include?(:shared) then :shared
111
119
  elsif flags.include?(:static) then :static
@@ -126,6 +134,8 @@ module MetaCC
126
134
  link_paths:
127
135
  )
128
136
 
137
+ return cmds if dry_run
138
+
129
139
  cmds.each { |cmd| run_command(cmd, env:, working_dir:) }
130
140
  output_path
131
141
  end
@@ -149,11 +159,24 @@ module MetaCC
149
159
  end
150
160
 
151
161
  def translate_flags(flags)
152
- unrecognized_flag = flags.find { |flag| !RECOGNIZED_FLAGS.include?(flag) }
162
+ unrecognized_flag = flags.find { |flag| !ALL_FLAGS.include?(flag) }
153
163
  if unrecognized_flag
154
164
  raise "#{unrecognized_flag.inspect} is not a known flag"
155
165
  end
156
166
 
167
+ lang_flags, flags = flags.partition { |flag| LANGUAGE_STD_FLAGS.include?(flag) }
168
+ arch_flags, flags = flags.partition { |flag| ARCHITECTURE_FLAGS.include?(flag) }
169
+ optm_flags, flags = flags.partition { |flag| OPTIMIZATION_FLAGS.include?(flag) }
170
+ sant_flags, flags = flags.partition { |flag| DBG_SANITIZE_FLAGS.include?(flag) }
171
+
172
+ flags << lang_flags.last unless lang_flags.empty?
173
+ flags << arch_flags.last unless arch_flags.empty?
174
+ flags << optm_flags.last unless optm_flags.empty?
175
+ flags << sant_flags.last unless sant_flags.empty?
176
+
177
+ flags << :no_omit_frame_pointer unless flags.include?(:omit_frame_pointer)
178
+ flags << :no_strict_aliasing unless flags.include?(:strict_aliasing)
179
+
157
180
  flags.flat_map { |flag| @toolchain.flags[flag] }
158
181
  end
159
182
 
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "rbconfig"
4
+ require "tempfile"
4
5
 
5
6
  module MetaCC
6
7
 
@@ -40,6 +41,34 @@ module MetaCC
40
41
  IO.popen([c, "--version", { err: :out }], &:read)
41
42
  end
42
43
 
44
+ # Parses +banner_text+ (e.g. from +version_banner+) and returns the
45
+ # Toolchain subclass it most likely belongs to, or +nil+ if unrecognised.
46
+ #
47
+ # Detection is order-sensitive: ClangCL must be checked before Clang
48
+ # because both banners contain "clang", but only ClangCL's Target line
49
+ # contains "windows-msvc".
50
+ def self.detect_from_version_banner(banner_text)
51
+ case banner_text
52
+ when /windows-msvc/i then ClangCL
53
+ when /clang/i then Clang
54
+ when /\bgcc\b/i then GCC
55
+ when /microsoft/i then MSVC
56
+ when /\btcc\b/i then TinyCC
57
+ end
58
+ end
59
+
60
+ def self.from_command(command)
61
+ toolchain_class = nil
62
+ %w[--version -v].each do |flag|
63
+ banner = IO.popen([command, flag, { err: :out }], &:read)
64
+ toolchain_class = detect_from_version_banner(banner)
65
+ break if toolchain_class
66
+ rescue Errno::ENOENT
67
+ # Do nothing
68
+ end
69
+ toolchain_class&.new
70
+ end
71
+
43
72
  # Returns a Hash mapping universal flags to native flags for this toolchain.
44
73
  def flags
45
74
  raise "#{self.class}#flags not implemented"
@@ -114,7 +143,7 @@ module MetaCC
114
143
 
115
144
  end
116
145
 
117
- # GNU-compatible toolchain (gcc).
146
+ # Base class for GNU compatible (ish) toolchains
118
147
  class GNU < Toolchain
119
148
 
120
149
  def initialize(cc_command = "gcc", search_paths: [])
@@ -150,43 +179,54 @@ module MetaCC
150
179
  end
151
180
 
152
181
  GNU_FLAGS = {
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: ["-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
- no_rtti: ["-fno-rtti"],
180
- no_exceptions: ["-fno-exceptions", "-fno-unwind-tables"],
181
- pic: ["-fPIC"],
182
- no_semantic_interposition: ["-fno-semantic-interposition"],
183
- no_omit_frame_pointer: ["-fno-omit-frame-pointer"],
184
- no_strict_aliasing: ["-fno-strict-aliasing"],
185
- shared: ["-shared"],
186
- static: ["-static"],
187
- strip: ["-Wl,--strip-unneeded"]
182
+ o0: ["-O0"],
183
+ o1: ["-O1"],
184
+ o2: ["-O2"],
185
+ o3: ["-O3"],
186
+ os: ["-Os"],
187
+ sse4_2: ["-march=x86-64-v2"], # This is a better match for /arch:SSE4.2 than -msse4_2 is
188
+ avx: ["-march=x86-64-v2", "-mavx"],
189
+ avx2: ["-march=x86-64-v3"], # This is a better match for /arch:AVX2 than -mavx2 is
190
+ avx512: ["-march=x86-64-v4"],
191
+ native: ["-march=native", "-mtune=native"],
192
+ debug_info: ["-g3"],
193
+ lto: ["-flto"],
194
+ warn_all: ["-Wall", "-Wextra", "-pedantic"],
195
+ warn_error: ["-Werror"],
196
+ c11: ["-std=c11"],
197
+ c17: ["-std=c17"],
198
+ c23: ["-std=c23"],
199
+ cxx11: ["-std=c++11"],
200
+ cxx14: ["-std=c++14"],
201
+ cxx17: ["-std=c++17"],
202
+ cxx20: ["-std=c++20"],
203
+ cxx23: ["-std=c++23"],
204
+ cxx26: ["-std=c++2c"],
205
+ sanitize_default: ["-fsanitize=address,undefined,leak"],
206
+ sanitize_memory: [],
207
+ sanitize_thread: ["-fsanitize=thread"],
208
+ no_rtti: ["-fno-rtti"],
209
+ no_exceptions: ["-fno-exceptions", "-fno-unwind-tables"],
210
+ pic: ["-fPIC"],
211
+ omit_frame_pointer: ["-fomit-frame-pointer"],
212
+ no_omit_frame_pointer: ["-fno-omit-frame-pointer"],
213
+ strict_aliasing: ["-fstrict-aliasing"],
214
+ no_strict_aliasing: ["-fno-strict-aliasing"],
215
+ shared: ["-shared", "-Bsymbolic-non-weak-functions", "-fno-semantic-interposition"],
216
+ shared_compat: ["-shared"],
217
+ static: ["-static"],
218
+ strip: ["-Wl,--strip-unneeded"],
219
+ debug: ["-D_GLIBCXX_DEBUG", "-fasynchronous-unwind-tables"]
188
220
  }.freeze
189
221
 
222
+ end
223
+
224
+ class GCC < GNU
225
+
226
+ def initialize(search_paths: [])
227
+ super("gcc", search_paths:)
228
+ end
229
+
190
230
  def flags
191
231
  GNU_FLAGS
192
232
  end
@@ -200,7 +240,10 @@ module MetaCC
200
240
  super("clang", search_paths:)
201
241
  end
202
242
 
203
- CLANG_FLAGS = GNU_FLAGS.merge(lto: ["-flto=thin"]).freeze
243
+ CLANG_FLAGS = GNU_FLAGS.merge(
244
+ lto: ["-flto=thin"],
245
+ sanitize_memory: ["-fsanitize=memory"]
246
+ ).freeze
204
247
 
205
248
  def flags
206
249
  CLANG_FLAGS
@@ -254,41 +297,43 @@ module MetaCC
254
297
  end
255
298
 
256
299
  MSVC_FLAGS = {
257
- o0: ["/Od"],
258
- o1: ["/O1"],
259
- o2: ["/O2"],
260
- o3: ["/O2", "/Ob3"],
261
- os: ["/O1"],
262
- sse4_2: ["/arch:SSE4.2"],
263
- avx: ["/arch:AVX"],
264
- avx2: ["/arch:AVX2"],
265
- avx512: ["/arch:AVX512"],
266
- native: [],
267
- debug: ["/Zi"],
268
- lto: ["/GL"],
269
- warn_all: ["/W4"],
270
- warn_error: ["/WX"],
271
- c11: ["/std:c11"],
272
- c17: ["/std:c17"],
273
- c23: ["/std:clatest"],
274
- cxx11: [],
275
- cxx14: ["/std:c++14"],
276
- cxx17: ["/std:c++17"],
277
- cxx20: ["/std:c++20"],
278
- cxx23: ["/std:c++23preview"],
279
- cxx26: ["/std:c++latest"],
280
- asan: ["/fsanitize=address"],
281
- ubsan: [],
282
- msan: [],
283
- no_rtti: ["/GR-"],
284
- no_exceptions: ["/EHs-", "/EHc-"],
285
- pic: [],
286
- no_semantic_interposition: [],
287
- no_omit_frame_pointer: ["/Oy-"],
288
- no_strict_aliasing: [],
289
- shared: ["/LD"],
290
- static: ["/c"],
291
- strip: []
300
+ o0: ["/Od"],
301
+ o1: ["/O1"],
302
+ o2: ["/O2"],
303
+ o3: ["/O2", "/Ob3"],
304
+ os: ["/O1"],
305
+ sse4_2: ["/arch:SSE4.2"],
306
+ avx: ["/arch:AVX"],
307
+ avx2: ["/arch:AVX2"],
308
+ avx512: ["/arch:AVX512"],
309
+ native: [],
310
+ debug_info: ["/Zi"],
311
+ lto: ["/GL"],
312
+ warn_all: ["/W4"],
313
+ warn_error: ["/WX"],
314
+ c11: ["/std:c11"],
315
+ c17: ["/std:c17"],
316
+ c23: ["/std:clatest"],
317
+ cxx11: [],
318
+ cxx14: ["/std:c++14"],
319
+ cxx17: ["/std:c++17"],
320
+ cxx20: ["/std:c++20"],
321
+ cxx23: ["/std:c++23preview"],
322
+ cxx26: ["/std:c++latest"],
323
+ sanitize_default: ["/fsanitize=address"],
324
+ sanitize_memory: [],
325
+ sanitize_thread: [],
326
+ no_rtti: ["/GR-"],
327
+ no_exceptions: ["/EHs-", "/EHc-"],
328
+ pic: [],
329
+ omit_frame_pointer: ["/Oy"],
330
+ no_omit_frame_pointer: ["/Oy-"],
331
+ strict_aliasing: [],
332
+ no_strict_aliasing: [],
333
+ shared: ["/LD"],
334
+ shared_compat: ["/LD"],
335
+ static: ["/c"],
336
+ strip: []
292
337
  }.freeze
293
338
 
294
339
  def flags
@@ -359,7 +404,7 @@ module MetaCC
359
404
  # environment variables into the current process's ENV.
360
405
  def MSVC.vcvarsall(devenv_path)
361
406
  # See https://stackoverflow.com/a/19929778
362
- return if ENV.has_key?("DevEnvDir")
407
+ return if ENV.key?("DevEnvDir")
363
408
 
364
409
  # Calculate the location of vcvarsall.bat
365
410
  install_root = File.expand_path("../../..", devenv_path)
@@ -368,10 +413,21 @@ module MetaCC
368
413
  vcvarsall = File.join(install_root, "VC", "Auxiliary", "Build", "vcvarsall.bat")
369
414
  return unless File.exist?(vcvarsall)
370
415
 
371
- # Run vcvarsall.bat and dump the environment to the shell
372
- output = `"#{vcvarsall}" x64 && set`
373
- status = $?
374
- return unless status.success?
416
+ # Get environment info from vcvarsall.bat
417
+ output = nil
418
+ Tempfile.create(["vcvarsall", ".bat"]) do |file|
419
+ # Write a stub batch file that run vcvarsall.bat and dumps the
420
+ # environment to stdout
421
+ file.puts %Q{"#{vcvarsall.gsub('"', '""')}" x64 && set}
422
+ file.flush
423
+
424
+ # Run the stub batch file
425
+ output = IO.popen(["cmd", "/c", file.path], &:read)
426
+ status = $?
427
+ return unless status.success?
428
+ rescue Errno::ENOENT
429
+ return
430
+ end
375
431
 
376
432
  output.each_line do |line|
377
433
  key, value = line.chomp.split("=", 2)
@@ -392,8 +448,11 @@ module MetaCC
392
448
  end
393
449
 
394
450
  CLANG_CL_FLAGS = MSVC_FLAGS.merge(
395
- o3: ["/Ot"], # Clang-CL treats /Ot as -O3
396
- lto: ["-flto=thin"]
451
+ lto: ["-flto=thin"],
452
+ o3: ["/Ot"], # Clang-CL treats /Ot as -O3
453
+ native: ["/clang:-march=native", "/clang:-mtune=native"],
454
+ strict_aliasing: ["/clang:-fstrict-aliasing"],
455
+ no_strict_aliasing: ["/clang:-fno-strict-aliasing"]
397
456
  ).freeze
398
457
 
399
458
  def flags
@@ -416,6 +475,7 @@ module MetaCC
416
475
  object_files = input_files.map { |f| f.sub(/\.c\z/, ".o") }
417
476
  commands << [@ar, "rcs", output_file, *object_files]
418
477
  end
478
+ commands
419
479
  end
420
480
 
421
481
  # TinyCC does not support C++.
@@ -428,41 +488,43 @@ module MetaCC
428
488
  end
429
489
 
430
490
  TINYCC_FLAGS = {
431
- o0: [],
432
- o1: ["-O1"],
433
- o2: ["-O2"],
434
- o3: ["-O2"],
435
- os: [],
436
- sse4_2: [],
437
- avx: [],
438
- avx2: [],
439
- avx512: [],
440
- native: [],
441
- debug: ["-g"],
442
- lto: [],
443
- warn_all: ["-Wall"],
444
- warn_error: ["-Werror"],
445
- c11: [],
446
- c17: [],
447
- c23: [],
448
- cxx11: [],
449
- cxx14: [],
450
- cxx17: [],
451
- cxx20: [],
452
- cxx23: [],
453
- cxx26: [],
454
- asan: [],
455
- ubsan: [],
456
- msan: [],
457
- no_rtti: [],
458
- no_exceptions: [],
459
- pic: [],
460
- no_semantic_interposition: [],
461
- no_omit_frame_pointer: [],
462
- no_strict_aliasing: [],
463
- shared: ["-shared"],
464
- static: ["-c"],
465
- strip: []
491
+ o0: [],
492
+ o1: ["-O1"],
493
+ o2: ["-O2"],
494
+ o3: ["-O2"],
495
+ os: [],
496
+ sse4_2: [],
497
+ avx: [],
498
+ avx2: [],
499
+ avx512: [],
500
+ native: [],
501
+ debug_info: ["-g"],
502
+ lto: [],
503
+ warn_all: ["-Wall"],
504
+ warn_error: ["-Werror"],
505
+ c11: [],
506
+ c17: [],
507
+ c23: [],
508
+ cxx11: [],
509
+ cxx14: [],
510
+ cxx17: [],
511
+ cxx20: [],
512
+ cxx23: [],
513
+ cxx26: [],
514
+ sanitize_default: [],
515
+ sanitize_memory: [],
516
+ sanitize_thread: [],
517
+ no_rtti: [],
518
+ no_exceptions: [],
519
+ pic: [],
520
+ omit_frame_pointer: [],
521
+ no_omit_frame_pointer: [],
522
+ strict_aliasing: [],
523
+ no_strict_aliasing: [],
524
+ shared: ["-shared"],
525
+ shared_compat: ["-shared"],
526
+ static: ["-c"],
527
+ strip: []
466
528
  }.freeze
467
529
 
468
530
  def flags
@@ -471,4 +533,7 @@ module MetaCC
471
533
 
472
534
  end
473
535
 
536
+ class AutoToolchain
537
+ end
538
+
474
539
  end
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module MetaCC
2
4
 
3
- VERSION = "0.2.0"
5
+ VERSION = "0.4.0"
4
6
 
5
7
  end
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.2.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Praneeth Sadda