metacc 0.3.0 → 0.5.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: d68166f1cf0263814b009c61e2b615ee36521656734bb4ef0b0e48677751fafa
4
+ data.tar.gz: 12133182cb6f8b899714eb7fdbc95868ecdbfd40fe80bbe43084ed79d0fa5e88
5
5
  SHA512:
6
- metadata.gz: bbfda6722b4f9fa4b59a6be206a1b5ca0503d77f5833dbe5ba07f46525b1f157e4ac5ca7853f0f18ae7efd4f9c2782993a0991e101fd102523323abfeea97dd1
7
- data.tar.gz: 15f59e0d6f45de8501b38aa6429624dd759d7d7b126ce18c0def60bdd2d56648f4b116483753e2f93da60c3e205ec5fd00813844e59b4975d7e3c81c01cfd590
6
+ metadata.gz: d3257e9318b06bfdc812d3a344dc3bece65e22199e4bbc68f3c5f9867593d8b1c4fa8e9376078abf0a056d75f6fa4a8782716152d50f24e6c0a314632f27d3d1
7
+ data.tar.gz: c413d78b53e7a7486a895e1595ec064173cca98235cd35ae595dcd31af9f95b4c526ce8c47b452b53955615ba0166e341a3b9e071508d457580479ca4397fb1f
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
@@ -12,32 +12,28 @@ module MetaCC
12
12
  class CLI
13
13
 
14
14
  WARNING_CONFIGS = {
15
- "all" => :warn_all,
15
+ "all" => :warn_all,
16
16
  "error" => :warn_error
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 = {
30
26
  "sse4.2" => :sse4_2,
31
- "avx" => :avx,
32
- "avx2" => :avx2,
27
+ "avx" => :avx,
28
+ "avx2" => :avx2,
33
29
  "avx512" => :avx512,
34
30
  "native" => :native
35
31
  }.freeze
36
32
 
37
33
  STANDARDS = {
38
- "c11" => :c11,
39
- "c17" => :c17,
40
- "c23" => :c23,
34
+ "c11" => :c11,
35
+ "c17" => :c17,
36
+ "c23" => :c23,
41
37
  "c++11" => :cxx11,
42
38
  "c++14" => :cxx14,
43
39
  "c++17" => :cxx17,
@@ -48,11 +44,11 @@ module MetaCC
48
44
 
49
45
  # Maps --x<name> CLI option names to xflags toolchain-class keys.
50
46
  XFLAGS = {
51
- "xmsvc" => MSVC,
52
- "xgnu" => GNU,
53
- "xclang" => Clang,
47
+ "xmsvc" => MSVC,
48
+ "xgnu" => GNU,
49
+ "xclang" => Clang,
54
50
  "xclangcl" => ClangCL,
55
- "xtinycc" => TinyCC
51
+ "xtinycc" => TinyCC
56
52
  }.freeze
57
53
 
58
54
  def initialize(driver: Driver.new)
@@ -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
 
@@ -132,10 +142,10 @@ module MetaCC
132
142
  parser.on("--lto", "Enable link time optimization") do
133
143
  options[:flags] << :lto
134
144
  end
135
- parser.on("--omit-frame-pointer") do |value|
145
+ parser.on("--omit-frame-pointer") do
136
146
  options[:flags] << :omit_frame_pointer
137
147
  end
138
- parser.on("--strict-aliasing") do |value|
148
+ parser.on("--strict-aliasing") do
139
149
  options[:flags] << :strict_aliasing
140
150
  end
141
151
 
@@ -145,13 +155,13 @@ module MetaCC
145
155
  parser.on("-m", "--arch=ARCH", "Target architecture") do |value|
146
156
  options[:flags] << TARGETS[value]
147
157
  end
148
- parser.on("--pic", "Generate position independent code") do |value|
158
+ parser.on("--pic", "Generate position independent code") do
149
159
  options[:flags] << :pic
150
160
  end
151
- parser.on("--no-rtti", "Disable runtime type information") do |value|
161
+ parser.on("--no-rtti", "Disable runtime type information") do
152
162
  options[:flags] << :no_rtti
153
163
  end
154
- parser.on("--no-exceptions", "Disable exceptions (and unwinding info)") do |value|
164
+ parser.on("--no-exceptions", "Disable exceptions (and unwinding info)") do
155
165
  options[:flags] << :no_exceptions
156
166
  end
157
167
 
@@ -161,10 +171,10 @@ module MetaCC
161
171
  parser.on("--static", "Produce a static library") do
162
172
  options[:flags] << :static
163
173
  end
164
- parser.on("--shared", "Produce a shared library") do |value|
174
+ parser.on("--shared", "Produce a shared library") do
165
175
  options[:flags] << :shared
166
176
  end
167
- parser.on("--shared-compat", "Produce a shared library with full LD_PRELOAD compatability") do |value|
177
+ parser.on("--shared-compat", "Produce a shared library with full LD_PRELOAD compatability") do
168
178
  options[:flags] << :shared_compat
169
179
  end
170
180
  parser.on("-c", "Compile only (produce object files without linking)") do
@@ -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
@@ -207,27 +216,44 @@ module MetaCC
207
216
 
208
217
  def validate_options!(flags, output_path, link:, run:)
209
218
  if !link && output_path
210
- raise OptionParser::InvalidOption, "cannot specify output path (-o) in compile only mode (-c)"
219
+ raise OptionParser::InvalidOption,
220
+ "cannot specify output path (-o) in compile only mode (-c)"
211
221
  end
212
222
 
213
223
  if link && !output_path
214
- raise OptionParser::InvalidOption, "must specify an output path (-o)"
224
+ raise OptionParser::InvalidOption,
225
+ "must specify an output path (-o)"
226
+ end
227
+
228
+ if run && (!link || %i[static shared shared_compat].any? { |f| flags.include?(f) })
229
+ raise OptionParser::InvalidOption,
230
+ "--run cannot be used with -c, --static, --shared, or --shared-compat"
215
231
  end
216
232
 
217
- if run && (!link || flags.include?(:shared) || flags.include?(:static))
218
- raise OptionParser::InvalidOption, "--run may not be used with -c, --shared, or --static"
233
+ if !link && %i[static shared shared_compat].any? { |f| flags.include?(f) }
234
+ raise OptionParser::InvalidOption,
235
+ "compile only mode (-c) cannot be used with --static, --shared, or --shared-compat"
236
+ end
237
+
238
+ if flags.include?(:debug_info) && flags.include?(:strip)
239
+ raise OptionParser::InvalidOption,
240
+ "--debug-info (-g) cannot be combined with --strip (-s)"
219
241
  end
220
242
  end
221
243
 
222
244
  def invoke(input_paths, desired_output_path = nil, link: true, run: false, **options)
245
+ result = nil
223
246
  if link
224
- actual_output_path = @driver.compile_and_link(input_paths, desired_output_path, **options)
225
- system(actual_output_path) if run
247
+ result = @driver.compile_and_link(input_paths, desired_output_path, **options)
248
+ system(result) if run && !options[:dry_run]
226
249
  else
227
250
  options.delete(:link_paths)
228
251
  options.delete(:libs)
229
- @driver.compile(input_paths, **options)
252
+ result = @driver.compile(input_paths, **options)
230
253
  end
254
+ return unless options[:dry_run]
255
+
256
+ puts(result.map { |cmd| cmd.join(" ") })
231
257
  end
232
258
 
233
259
  end
data/lib/metacc/driver.rb CHANGED
@@ -15,20 +15,29 @@ 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
- 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
- debug_info
27
- omit_frame_pointer strict_aliasing
28
- no_rtti no_exceptions
29
- pic shared shared_compat static strip
30
- ]
31
- ).freeze
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
+ :warn_all,
25
+ :warn_error,
26
+ :debug_info,
27
+ :omit_frame_pointer,
28
+ :strict_aliasing,
29
+ :no_rtti,
30
+ :no_exceptions,
31
+ :pic,
32
+ :shared,
33
+ :shared_compat,
34
+ :static,
35
+ :strip,
36
+ *LANGUAGE_STD_FLAGS,
37
+ *ARCHITECTURE_FLAGS,
38
+ *OPTIMIZATION_FLAGS,
39
+ *DBG_SANITIZE_FLAGS
40
+ ]).freeze
32
41
 
33
42
  # The detected toolchain (a Toolchain subclass instance).
34
43
  attr_reader :toolchain
@@ -37,11 +46,11 @@ module MetaCC
37
46
  #
38
47
  # @param prefer [Array<Class>] toolchain classes to probe, in priority order.
39
48
  # Each element must be a Class derived from Toolchain.
40
- # Defaults to [Clang, GNU, MSVC].
49
+ # Defaults to [Clang, GCC, MSVC].
41
50
  # @param search_paths [Array<String>] directories to search for toolchain executables
42
51
  # before falling back to PATH. Defaults to [].
43
52
  # @raise [ToolchainNotFoundError] if no supported compiler is found.
44
- def initialize(prefer: [MSVC], search_paths: [])
53
+ def initialize(prefer: [Clang, GCC, MSVC], search_paths: [])
45
54
  @toolchain = select_toolchain!(prefer, search_paths)
46
55
  end
47
56
 
@@ -58,12 +67,13 @@ module MetaCC
58
67
  # @raise [CompileError] if the underlying toolchain executable returns a non-zero exit status
59
68
  def compile(
60
69
  input_files,
61
- flags: [],
62
- xflags: {},
70
+ flags: [],
71
+ xflags: {},
63
72
  include_paths: [],
64
- defs: [],
65
- env: {},
66
- working_dir: "."
73
+ defs: [],
74
+ env: {},
75
+ working_dir: ".",
76
+ dry_run: false
67
77
  )
68
78
  flags = translate_flags(flags)
69
79
  flags.concat(xflags[@toolchain.class] || [])
@@ -75,6 +85,8 @@ module MetaCC
75
85
  defs:
76
86
  )
77
87
 
88
+ return [cmd] if dry_run
89
+
78
90
  !!run_command(cmd, env:, working_dir:)
79
91
  end
80
92
 
@@ -98,17 +110,18 @@ module MetaCC
98
110
  def compile_and_link(
99
111
  input_files,
100
112
  output_path,
101
- flags: [],
102
- xflags: {},
113
+ flags: [],
114
+ xflags: {},
103
115
  include_paths: [],
104
- defs: [],
105
- link_paths: [],
106
- libs: [],
107
- env: {},
108
- working_dir: "."
116
+ defs: [],
117
+ link_paths: [],
118
+ libs: [],
119
+ env: {},
120
+ working_dir: ".",
121
+ dry_run: false
109
122
  )
110
- output_type = if flags.include?(:shared) then :shared
111
- elsif flags.include?(:static) then :static
123
+ output_type = if flags.include?(:shared) then :shared
124
+ elsif flags.include?(:static) then :static
112
125
  else :executable
113
126
  end
114
127
  output_path = apply_default_extension(output_path, output_type)
@@ -126,6 +139,8 @@ module MetaCC
126
139
  link_paths:
127
140
  )
128
141
 
142
+ return cmds if dry_run
143
+
129
144
  cmds.each { |cmd| run_command(cmd, env:, working_dir:) }
130
145
  output_path
131
146
  end
@@ -149,11 +164,21 @@ module MetaCC
149
164
  end
150
165
 
151
166
  def translate_flags(flags)
152
- unrecognized_flag = flags.find { |flag| !RECOGNIZED_FLAGS.include?(flag) }
167
+ unrecognized_flag = flags.find { |flag| !ALL_FLAGS.include?(flag) }
153
168
  if unrecognized_flag
154
169
  raise "#{unrecognized_flag.inspect} is not a known flag"
155
170
  end
156
171
 
172
+ lang_flags, flags = flags.partition { |flag| LANGUAGE_STD_FLAGS.include?(flag) }
173
+ arch_flags, flags = flags.partition { |flag| ARCHITECTURE_FLAGS.include?(flag) }
174
+ optm_flags, flags = flags.partition { |flag| OPTIMIZATION_FLAGS.include?(flag) }
175
+ sant_flags, flags = flags.partition { |flag| DBG_SANITIZE_FLAGS.include?(flag) }
176
+
177
+ flags << lang_flags.last unless lang_flags.empty?
178
+ flags << arch_flags.last unless arch_flags.empty?
179
+ flags << optm_flags.last unless optm_flags.empty?
180
+ flags << sant_flags.last unless sant_flags.empty?
181
+
157
182
  flags << :no_omit_frame_pointer unless flags.include?(:omit_frame_pointer)
158
183
  flags << :no_strict_aliasing unless flags.include?(:strict_aliasing)
159
184
 
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rbconfig"
4
+
5
+ module MetaCC
6
+
7
+ module Platform
8
+
9
+ def self.windows?
10
+ @windows ||= RbConfig::CONFIG["host_os"].match?(/mswin|mingw32|windows/)
11
+ end
12
+
13
+ def self.cygwin?
14
+ @cygwin ||= RbConfig::CONFIG["host_os"].match?(/cygwin/)
15
+ end
16
+
17
+ def self.apple?
18
+ @apple ||= RbConfig::CONFIG["host_os"].match?(/darwin/)
19
+ end
20
+
21
+ def self.executable_ext
22
+ if windows? || cygwin?
23
+ ".exe"
24
+ elsif apple?
25
+ ".dylib"
26
+ else
27
+ ""
28
+ end
29
+ end
30
+
31
+ def self.executable_ext
32
+ windows? || cygwin? ? ".exe" : ""
33
+ end
34
+
35
+ def self.shared_library_ext
36
+ if windows? || cygwin?
37
+ ".dll"
38
+ elsif apple?
39
+ ".dylib"
40
+ else
41
+ ".so"
42
+ end
43
+ end
44
+
45
+ end
46
+
47
+ end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "rbconfig"
3
+ require "tempfile"
4
+ require_relative "platform"
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"
@@ -49,7 +78,7 @@ module MetaCC
49
78
  # The output mode (object files, shared library, static library, or
50
79
  # executable) is determined by the translated flags.
51
80
  def compile_command(
52
- input_files,
81
+ _input_files,
53
82
  flags:,
54
83
  include_paths:,
55
84
  defs:
@@ -58,8 +87,8 @@ module MetaCC
58
87
  end
59
88
 
60
89
  def compile_and_link_commands(
61
- input_files,
62
- output_file,
90
+ _input_files,
91
+ _output_file,
63
92
  flags:,
64
93
  include_paths:,
65
94
  defs:,
@@ -76,20 +105,11 @@ module MetaCC
76
105
  # @param output_type [:objects, :shared, :static, :executable]
77
106
  # @return [String]
78
107
  def default_extension(output_type)
79
- host_os = RbConfig::CONFIG["host_os"]
80
108
  case output_type
81
109
  when :objects then ".o"
82
- when :static then ".a"
83
- when :shared
84
- if host_os.match?(/mswin|mingw|cygwin/)
85
- ".dll"
86
- elsif host_os.match?(/darwin/)
87
- ".dylib"
88
- else
89
- ".so"
90
- end
91
- when :executable
92
- host_os.match?(/mswin|mingw|cygwin/) ? ".exe" : ""
110
+ when :static then ".a" # MingGW uses .a, not .lib
111
+ when :shared then MetaCC::Platform.shared_library_ext
112
+ when :executable then MetaCC::Platform.executable_ext
93
113
  else
94
114
  raise ArgumentError, "unknown output_type: #{output_type.inspect}"
95
115
  end
@@ -114,7 +134,7 @@ module MetaCC
114
134
 
115
135
  end
116
136
 
117
- # GNU-compatible toolchain (gcc).
137
+ # Base class for GNU compatible (ish) toolchains
118
138
  class GNU < Toolchain
119
139
 
120
140
  def initialize(cc_command = "gcc", search_paths: [])
@@ -149,6 +169,30 @@ module MetaCC
149
169
  [[c, *flags, *inc_flags, *def_flags, *input_files, *lib_path_flags, *lib_flags, "-o", output_file]]
150
170
  end
151
171
 
172
+ def self.platform_flags
173
+ if MetaCC::Platform.windows?
174
+ {
175
+ # MingGW doesn't have full UBsan support, but it can trap on detected errors.
176
+ # It does not support the address or leak sanitizers.
177
+ sanitize_default: ["-fsanitize=undefined", "-fsanitize-undefined-trap-on-error"],
178
+ # MingGW doesn't support the thread sanitizer
179
+ sanitize_thread: []
180
+ }
181
+ elsif MetaCC::Platform.apple?
182
+ # gcc's support for sanitizers on OSX/iOS is *very* spotty.
183
+ # So we disable everything.
184
+ {
185
+ sanitize_default: [],
186
+ sanitize_thread: []
187
+ }
188
+ else
189
+ # Otherwise assume Linux/BSD and keep the default
190
+ {}
191
+ end
192
+ end
193
+
194
+ private_class_method :platform_flags
195
+
152
196
  GNU_FLAGS = {
153
197
  o0: ["-O0"],
154
198
  o1: ["-O1"],
@@ -173,10 +217,9 @@ module MetaCC
173
217
  cxx20: ["-std=c++20"],
174
218
  cxx23: ["-std=c++23"],
175
219
  cxx26: ["-std=c++2c"],
176
- asan: ["-fsanitize=address"],
177
- ubsan: ["-fsanitize=undefined"],
178
- msan: ["-fsanitize=memory"],
179
- lsan: ["-fsanitize=leak"],
220
+ sanitize_default: ["-fsanitize=address,undefined,leak"],
221
+ sanitize_memory: [],
222
+ sanitize_thread: ["-fsanitize=thread"],
180
223
  no_rtti: ["-fno-rtti"],
181
224
  no_exceptions: ["-fno-exceptions", "-fno-unwind-tables"],
182
225
  pic: ["-fPIC"],
@@ -189,7 +232,15 @@ module MetaCC
189
232
  static: ["-static"],
190
233
  strip: ["-Wl,--strip-unneeded"],
191
234
  debug: ["-D_GLIBCXX_DEBUG", "-fasynchronous-unwind-tables"]
192
- }.freeze
235
+ }.merge(platform_flags).freeze
236
+
237
+ end
238
+
239
+ class GCC < GNU
240
+
241
+ def initialize(search_paths: [])
242
+ super("gcc", search_paths:)
243
+ end
193
244
 
194
245
  def flags
195
246
  GNU_FLAGS
@@ -204,7 +255,26 @@ module MetaCC
204
255
  super("clang", search_paths:)
205
256
  end
206
257
 
207
- CLANG_FLAGS = GNU_FLAGS.merge(lto: ["-flto=thin"]).freeze
258
+ def self.platform_flags
259
+ if MetaCC::Platform.windows?
260
+ {
261
+ # The leak sanitizer is not supported on Windows
262
+ sanitize_default: ["-fsanitize=address,undefined"],
263
+ # The thread sanitizer is not supported on Windows
264
+ sanitize_thread: []
265
+ }
266
+ else
267
+ # For all other platforms, stick with the defaults
268
+ {}
269
+ end
270
+ end
271
+
272
+ private_class_method :platform_flags
273
+
274
+ CLANG_FLAGS = GNU_FLAGS.merge(
275
+ lto: ["-flto=thin"],
276
+ sanitize_memory: ["-fsanitize=memory"]
277
+ ).merge(platform_flags)
208
278
 
209
279
  def flags
210
280
  CLANG_FLAGS
@@ -281,17 +351,16 @@ module MetaCC
281
351
  cxx20: ["/std:c++20"],
282
352
  cxx23: ["/std:c++23preview"],
283
353
  cxx26: ["/std:c++latest"],
284
- asan: ["/fsanitize=address"],
285
- ubsan: [],
286
- msan: [],
287
- lsan: [],
354
+ sanitize_default: ["/fsanitize=address"],
355
+ sanitize_memory: [],
356
+ sanitize_thread: [],
288
357
  no_rtti: ["/GR-"],
289
- no_exceptions: ["/EHs-", "/EHc-"],
358
+ no_exceptions: ["/EHs-", "/EHc-", "/D_HAS_EXCEPTIONS=0"],
290
359
  pic: [],
291
360
  omit_frame_pointer: ["/Oy"],
292
361
  no_omit_frame_pointer: ["/Oy-"],
293
- strict_aliasing: ["-fstrict-aliasing"],
294
- no_strict_aliasing: ["-fno-strict-aliasing"],
362
+ strict_aliasing: [],
363
+ no_strict_aliasing: [],
295
364
  shared: ["/LD"],
296
365
  shared_compat: ["/LD"],
297
366
  static: ["/c"],
@@ -364,9 +433,9 @@ module MetaCC
364
433
  #
365
434
  # Parses the output of `vcvarsall.bat … && set` and merges the resulting
366
435
  # environment variables into the current process's ENV.
367
- def MSVC.vcvarsall(devenv_path)
436
+ def self.vcvarsall(devenv_path)
368
437
  # See https://stackoverflow.com/a/19929778
369
- return if ENV.has_key?("DevEnvDir")
438
+ return if ENV.key?("DevEnvDir")
370
439
 
371
440
  # Calculate the location of vcvarsall.bat
372
441
  install_root = File.expand_path("../../..", devenv_path)
@@ -375,10 +444,21 @@ module MetaCC
375
444
  vcvarsall = File.join(install_root, "VC", "Auxiliary", "Build", "vcvarsall.bat")
376
445
  return unless File.exist?(vcvarsall)
377
446
 
378
- # Run vcvarsall.bat and dump the environment to the shell
379
- output = `"#{vcvarsall}" x64 && set`
380
- status = $?
381
- return unless status.success?
447
+ # Get environment info from vcvarsall.bat
448
+ output = nil
449
+ Tempfile.create(["vcvarsall", ".bat"]) do |file|
450
+ # Write a stub batch file that run vcvarsall.bat and dumps the
451
+ # environment to stdout
452
+ file.puts %("#{vcvarsall.gsub('"', '""')}" x64 && set)
453
+ file.flush
454
+
455
+ # Run the stub batch file
456
+ output = IO.popen(["cmd", "/c", file.path], &:read)
457
+ status = $?
458
+ return unless status.success?
459
+ rescue Errno::ENOENT
460
+ return
461
+ end
382
462
 
383
463
  output.each_line do |line|
384
464
  key, value = line.chomp.split("=", 2)
@@ -399,8 +479,11 @@ module MetaCC
399
479
  end
400
480
 
401
481
  CLANG_CL_FLAGS = MSVC_FLAGS.merge(
402
- o3: ["/Ot"], # Clang-CL treats /Ot as -O3
403
- lto: ["-flto=thin"]
482
+ lto: ["-flto=thin"],
483
+ o3: ["/Ot"], # Clang-CL treats /Ot as -O3
484
+ native: ["/clang:-march=native", "/clang:-mtune=native"],
485
+ strict_aliasing: ["/clang:-fstrict-aliasing"],
486
+ no_strict_aliasing: ["/clang:-fno-strict-aliasing"]
404
487
  ).freeze
405
488
 
406
489
  def flags
@@ -418,7 +501,7 @@ module MetaCC
418
501
  end
419
502
 
420
503
  def compile_and_link_commands(input_files, output_file, **options)
421
- commands = super(input_files, output_file, **options)
504
+ commands = super
422
505
  if options[:flags].include?(:static)
423
506
  object_files = input_files.map { |f| f.sub(/\.c\z/, ".o") }
424
507
  commands << [@ar, "rcs", output_file, *object_files]
@@ -436,42 +519,43 @@ module MetaCC
436
519
  end
437
520
 
438
521
  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: []
522
+ o0: [],
523
+ o1: ["-O1"],
524
+ o2: ["-O2"],
525
+ o3: ["-O2"],
526
+ os: [],
527
+ sse4_2: [],
528
+ avx: [],
529
+ avx2: [],
530
+ avx512: [],
531
+ native: [],
532
+ debug_info: ["-g"],
533
+ lto: [],
534
+ warn_all: ["-Wall"],
535
+ warn_error: ["-Werror"],
536
+ c11: [],
537
+ c17: [],
538
+ c23: [],
539
+ cxx11: [],
540
+ cxx14: [],
541
+ cxx17: [],
542
+ cxx20: [],
543
+ cxx23: [],
544
+ cxx26: [],
545
+ sanitize_default: [],
546
+ sanitize_memory: [],
547
+ sanitize_thread: [],
548
+ no_rtti: [],
549
+ no_exceptions: [],
550
+ pic: [],
551
+ omit_frame_pointer: [],
552
+ no_omit_frame_pointer: [],
553
+ strict_aliasing: [],
554
+ no_strict_aliasing: [],
555
+ shared: ["-shared"],
556
+ shared_compat: ["-shared"],
557
+ static: ["-c"],
558
+ strip: []
475
559
  }.freeze
476
560
 
477
561
  def flags
@@ -2,6 +2,6 @@
2
2
 
3
3
  module MetaCC
4
4
 
5
- VERSION = "0.3.0"
5
+ VERSION = "0.5.0"
6
6
 
7
7
  end
data/lib/metacc.rb CHANGED
@@ -1,4 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "metacc/driver"
4
3
  require "metacc/version"
4
+ require "metacc/platform"
5
+ require "metacc/toolchain"
6
+ require "metacc/driver"
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.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Praneeth Sadda
@@ -22,6 +22,7 @@ files:
22
22
  - lib/metacc.rb
23
23
  - lib/metacc/cli.rb
24
24
  - lib/metacc/driver.rb
25
+ - lib/metacc/platform.rb
25
26
  - lib/metacc/toolchain.rb
26
27
  - lib/metacc/version.rb
27
28
  homepage: https://github.com/psadda/metacc
@@ -38,7 +39,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
38
39
  requirements:
39
40
  - - ">="
40
41
  - !ruby/object:Gem::Version
41
- version: 3.2.0
42
+ version: 3.4.0
42
43
  required_rubygems_version: !ruby/object:Gem::Requirement
43
44
  requirements:
44
45
  - - ">="