metacc 0.3.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: d74b463f2d62a82dbbe422b47e112052a25c95e8c51b957f01ec2545b2f8f227
4
- data.tar.gz: 510bdbcba0c031de7909db0a989141afbecd9fa33bcf7b5066cf61d3dacc6f49
3
+ metadata.gz: bb94d9b47a11ec5a78ea57ae19417e6423287918287f675e43579af8015e4c63
4
+ data.tar.gz: b1237b3e41f2ae7ae68066441d742892ca77fd0427dc3e7b8f02b7f92049e535
5
5
  SHA512:
6
- metadata.gz: bbfda6722b4f9fa4b59a6be206a1b5ca0503d77f5833dbe5ba07f46525b1f157e4ac5ca7853f0f18ae7efd4f9c2782993a0991e101fd102523323abfeea97dd1
7
- data.tar.gz: 15f59e0d6f45de8501b38aa6429624dd759d7d7b126ce18c0def60bdd2d56648f4b116483753e2f93da60c3e205ec5fd00813844e59b4975d7e3c81c01cfd590
6
+ metadata.gz: 4774d4160c9939259634b2625b28a2975360c477c56c53ac689b4208defb698c4fbcbccf829f540583dfb94cf801fc62664e94cc900f52c64c28bfd6f763247c
7
+ data.tar.gz: dfd5493d3789ea3f18dbd1d7a6a15c345fe6eaeb262e022af7a79039bdd7b2707416312fc08e1ca86b333d91520dbb41457f6be5006d30157af934d4fe8b1497
data/bin/metacc CHANGED
@@ -7,8 +7,7 @@ require "metacc/cli"
7
7
 
8
8
  begin
9
9
  MetaCC::CLI.new.run(ARGV)
10
- rescue MetaCC::CLI::InvalidOption,
11
- MetaCC::ToolchainNotFoundError,
10
+ rescue MetaCC::ToolchainNotFoundError,
12
11
  OptionParser::AmbiguousOption,
13
12
  OptionParser::MissingArgument,
14
13
  OptionParser::NeedlessArgument,
data/lib/metacc/cli.rb CHANGED
@@ -17,13 +17,9 @@ module MetaCC
17
17
  }
18
18
 
19
19
  SANITIZERS = {
20
- "address" => :asan,
21
- "addr" => :asan,
22
- "undefined" => :ubsan,
23
- "ub" => :ubsan,
24
- "memory" => :msan,
25
- "mem" => :msan,
26
- "leak" => :lsan
20
+ nil => :sanitize_default,
21
+ "memory" => :sanitize_memory,
22
+ "thread" => :sanitize_thread,
27
23
  }
28
24
 
29
25
  TARGETS = {
@@ -77,6 +73,7 @@ module MetaCC
77
73
  libs: [],
78
74
  output_path: nil,
79
75
  run: false,
76
+ dry_run: false,
80
77
  flags: [],
81
78
  xflags: {}
82
79
  }
@@ -106,12 +103,19 @@ module MetaCC
106
103
  parser.on("--std=STANDARD", "Specify the language standard") do |value|
107
104
  options[:flags] << STANDARDS[value]
108
105
  end
109
- parser.on("-W OPTION", "Configure warnings") do |value|
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|
110
111
  options[:flags] << WARNING_CONFIGS[value]
111
112
  end
112
113
  parser.on("-r", "--run", "Run the compiled executable after a successful build") do
113
114
  options[:run] = true
114
115
  end
116
+ parser.on("--dry-run", "Print the command that would be issued") do
117
+ options[:dry_run] = true
118
+ end
115
119
 
116
120
  parser.separator ""
117
121
  parser.separator "Debugging:"
@@ -119,7 +123,13 @@ module MetaCC
119
123
  parser.on("-g", "--debug-info", "Emit debugging symbols") do
120
124
  options[:flags] << :debug_info
121
125
  end
122
- parser.on("-S", "--sanitize SANITIZER", "Enable sanitizer (address, undefined, leak, memory)") do |value|
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|
123
133
  options[:flags] << SANITIZERS[value]
124
134
  end
125
135
 
@@ -176,7 +186,6 @@ module MetaCC
176
186
  parser.on("-L DIR", "Add linker library search path") do |value|
177
187
  options[:link_paths] << value
178
188
  end
179
-
180
189
  parser.on("-s", "--strip", "Strip unneeded symbols") do
181
190
  options[:flags] << :strip
182
191
  end
@@ -214,19 +223,31 @@ module MetaCC
214
223
  raise OptionParser::InvalidOption, "must specify an output path (-o)"
215
224
  end
216
225
 
217
- if run && (!link || flags.include?(:shared) || flags.include?(:static))
218
- raise OptionParser::InvalidOption, "--run may not be used with -c, --shared, or --static"
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"
228
+ end
229
+
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)"
219
236
  end
220
237
  end
221
238
 
222
239
  def invoke(input_paths, desired_output_path = nil, link: true, run: false, **options)
240
+ result = nil
223
241
  if link
224
- actual_output_path = @driver.compile_and_link(input_paths, desired_output_path, **options)
225
- 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]
226
244
  else
227
245
  options.delete(:link_paths)
228
246
  options.delete(:libs)
229
- @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(" ") }
230
251
  end
231
252
  end
232
253
 
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[
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[
20
25
  warn_all warn_error
21
- c11 c17 c23
22
- cxx11 cxx14 cxx17 cxx20 cxx23 cxx26
23
- o0 o1 o2 o3 os lto
24
- sse4_2 avx avx2 avx512 native
25
- asan ubsan msan lsan
26
26
  debug_info
27
27
  omit_frame_pointer strict_aliasing
28
28
  no_rtti no_exceptions
29
29
  pic shared shared_compat static strip
30
- ]
31
- ).freeze
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,21 @@ 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
+
157
177
  flags << :no_omit_frame_pointer unless flags.include?(:omit_frame_pointer)
158
178
  flags << :no_strict_aliasing unless flags.include?(:strict_aliasing)
159
179
 
@@ -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: [])
@@ -173,10 +202,9 @@ module MetaCC
173
202
  cxx20: ["-std=c++20"],
174
203
  cxx23: ["-std=c++23"],
175
204
  cxx26: ["-std=c++2c"],
176
- asan: ["-fsanitize=address"],
177
- ubsan: ["-fsanitize=undefined"],
178
- msan: ["-fsanitize=memory"],
179
- lsan: ["-fsanitize=leak"],
205
+ sanitize_default: ["-fsanitize=address,undefined,leak"],
206
+ sanitize_memory: [],
207
+ sanitize_thread: ["-fsanitize=thread"],
180
208
  no_rtti: ["-fno-rtti"],
181
209
  no_exceptions: ["-fno-exceptions", "-fno-unwind-tables"],
182
210
  pic: ["-fPIC"],
@@ -191,6 +219,14 @@ module MetaCC
191
219
  debug: ["-D_GLIBCXX_DEBUG", "-fasynchronous-unwind-tables"]
192
220
  }.freeze
193
221
 
222
+ end
223
+
224
+ class GCC < GNU
225
+
226
+ def initialize(search_paths: [])
227
+ super("gcc", search_paths:)
228
+ end
229
+
194
230
  def flags
195
231
  GNU_FLAGS
196
232
  end
@@ -204,7 +240,10 @@ module MetaCC
204
240
  super("clang", search_paths:)
205
241
  end
206
242
 
207
- 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
208
247
 
209
248
  def flags
210
249
  CLANG_FLAGS
@@ -281,17 +320,16 @@ module MetaCC
281
320
  cxx20: ["/std:c++20"],
282
321
  cxx23: ["/std:c++23preview"],
283
322
  cxx26: ["/std:c++latest"],
284
- asan: ["/fsanitize=address"],
285
- ubsan: [],
286
- msan: [],
287
- lsan: [],
323
+ sanitize_default: ["/fsanitize=address"],
324
+ sanitize_memory: [],
325
+ sanitize_thread: [],
288
326
  no_rtti: ["/GR-"],
289
327
  no_exceptions: ["/EHs-", "/EHc-"],
290
328
  pic: [],
291
329
  omit_frame_pointer: ["/Oy"],
292
330
  no_omit_frame_pointer: ["/Oy-"],
293
- strict_aliasing: ["-fstrict-aliasing"],
294
- no_strict_aliasing: ["-fno-strict-aliasing"],
331
+ strict_aliasing: [],
332
+ no_strict_aliasing: [],
295
333
  shared: ["/LD"],
296
334
  shared_compat: ["/LD"],
297
335
  static: ["/c"],
@@ -366,7 +404,7 @@ module MetaCC
366
404
  # environment variables into the current process's ENV.
367
405
  def MSVC.vcvarsall(devenv_path)
368
406
  # See https://stackoverflow.com/a/19929778
369
- return if ENV.has_key?("DevEnvDir")
407
+ return if ENV.key?("DevEnvDir")
370
408
 
371
409
  # Calculate the location of vcvarsall.bat
372
410
  install_root = File.expand_path("../../..", devenv_path)
@@ -375,10 +413,21 @@ module MetaCC
375
413
  vcvarsall = File.join(install_root, "VC", "Auxiliary", "Build", "vcvarsall.bat")
376
414
  return unless File.exist?(vcvarsall)
377
415
 
378
- # Run vcvarsall.bat and dump the environment to the shell
379
- output = `"#{vcvarsall}" x64 && set`
380
- status = $?
381
- 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
382
431
 
383
432
  output.each_line do |line|
384
433
  key, value = line.chomp.split("=", 2)
@@ -399,8 +448,11 @@ module MetaCC
399
448
  end
400
449
 
401
450
  CLANG_CL_FLAGS = MSVC_FLAGS.merge(
402
- o3: ["/Ot"], # Clang-CL treats /Ot as -O3
403
- 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"]
404
456
  ).freeze
405
457
 
406
458
  def flags
@@ -436,42 +488,43 @@ module MetaCC
436
488
  end
437
489
 
438
490
  TINYCC_FLAGS = {
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: []
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: []
475
528
  }.freeze
476
529
 
477
530
  def flags
@@ -480,4 +533,7 @@ module MetaCC
480
533
 
481
534
  end
482
535
 
536
+ class AutoToolchain
537
+ end
538
+
483
539
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module MetaCC
4
4
 
5
- VERSION = "0.3.0"
5
+ VERSION = "0.4.0"
6
6
 
7
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.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Praneeth Sadda