Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
emscripten-core
GitHub Repository: emscripten-core/emscripten
Path: blob/main/emcc.py
6162 views
1
#!/usr/bin/env python3
2
# Copyright 2011 The Emscripten Authors. All rights reserved.
3
# Emscripten is available under two separate licenses, the MIT license and the
4
# University of Illinois/NCSA Open Source License. Both these licenses can be
5
# found in the LICENSE file.
6
7
"""emcc - compiler helper script
8
=============================
9
10
emcc is a drop-in replacement for a compiler like gcc or clang.
11
12
See emcc --help for details.
13
14
emcc can be influenced by a few environment variables:
15
16
EMCC_DEBUG - "1" will log out useful information during compilation, as well as
17
save each compiler step as an emcc-* file in the temp dir
18
(by default /tmp/emscripten_temp). "2" will save additional emcc-*
19
steps, that would normally not be separately produced (so this
20
slows down compilation).
21
"""
22
23
import logging
24
import os
25
import shlex
26
import shutil
27
import sys
28
import tarfile
29
from dataclasses import dataclass
30
from enum import Enum, auto, unique
31
32
from tools import (
33
building,
34
cache,
35
cmdline,
36
compile,
37
config,
38
diagnostics,
39
shared,
40
system_libs,
41
utils,
42
)
43
from tools.cmdline import CLANG_FLAGS_WITH_ARGS, options
44
from tools.response_file import substitute_response_files
45
from tools.settings import COMPILE_TIME_SETTINGS, default_setting, settings, user_settings
46
from tools.shared import DEBUG, DYLIB_EXTENSIONS, in_temp
47
from tools.toolchain_profiler import ToolchainProfiler
48
from tools.utils import exit_with_error, get_file_suffix, read_file, unsuffixed_basename
49
50
logger = logging.getLogger('emcc')
51
52
# In git checkouts of emscripten `bootstrap.py` exists to run post-checkout
53
# steps. In packaged versions (e.g. emsdk) this file does not exist (because
54
# it is excluded in tools/install.py) and these steps are assumed to have been
55
# run already.
56
if os.path.exists(utils.path_from_root('.git')) and os.path.exists(utils.path_from_root('bootstrap.py')):
57
import bootstrap
58
bootstrap.check()
59
60
PREPROCESSED_EXTENSIONS = {'.i', '.ii'}
61
ASSEMBLY_EXTENSIONS = {'.s'}
62
HEADER_EXTENSIONS = {'.h', '.hxx', '.hpp', '.hh', '.H', '.HXX', '.HPP', '.HH'}
63
SOURCE_EXTENSIONS = {
64
'.c', '.i', # C
65
'.cppm', '.pcm', '.cpp', '.cxx', '.cc', '.c++', '.CPP', '.CXX', '.C', '.CC', '.C++', '.ii', # C++
66
'.m', '.mi', '.mm', '.mii', # ObjC/ObjC++
67
'.bc', '.ll', # LLVM IR
68
'.S', # asm with preprocessor
69
os.devnull, # consider the special endingless filenames like /dev/null to be C
70
} | PREPROCESSED_EXTENSIONS
71
72
LINK_ONLY_FLAGS = {
73
'--bind', '--closure', '--cpuprofiler', '--embed-file',
74
'--emit-symbol-map', '--emrun', '--exclude-file', '--extern-post-js',
75
'--extern-pre-js', '--ignore-dynamic-linking', '--js-library',
76
'--js-transform', '--oformat', '--output_eol', '--output-eol',
77
'--post-js', '--pre-js', '--preload-file', '--profiling-funcs',
78
'--proxy-to-worker', '--shell-file', '--source-map-base',
79
'--threadprofiler', '--use-preload-plugins',
80
}
81
82
83
@unique
84
class Mode(Enum):
85
# Used any time we are not linking, including PCH, pre-processing, etc
86
COMPILE_ONLY = auto()
87
# Only when --post-link is specified
88
POST_LINK_ONLY = auto()
89
# This is the default mode, in the absence of any flags such as -c, -E, etc
90
COMPILE_AND_LINK = auto()
91
92
93
@dataclass
94
class LinkFlag:
95
"""Used to represent a linker flag.
96
97
The flag value is stored along with a bool that distinguishes input
98
files from non-files.
99
100
A list of these is returned by separate_linker_flags.
101
"""
102
value: str
103
is_file: int
104
105
106
class EmccState:
107
def __init__(self, args):
108
self.mode = Mode.COMPILE_AND_LINK
109
# Using tuple here to prevent accidental mutation
110
self.orig_args = tuple(args)
111
112
113
def create_reproduce_file(name, args):
114
def make_relative(filename):
115
filename = os.path.normpath(os.path.abspath(filename))
116
filename = os.path.splitdrive(filename)[1]
117
filename = filename[1:]
118
return filename
119
120
root = unsuffixed_basename(name)
121
with tarfile.open(name, 'w') as reproduce_file:
122
reproduce_file.add(utils.path_from_root('emscripten-version.txt'), os.path.join(root, 'version.txt'))
123
124
with shared.get_temp_files().get_file(suffix='.tar') as rsp_name:
125
with open(rsp_name, 'w') as rsp:
126
ignore_next = False
127
output_arg = None
128
129
for arg in args:
130
ignore = ignore_next
131
ignore_next = False
132
if arg.startswith('--reproduce='):
133
continue
134
135
if len(arg) > 2 and arg.startswith('-o'):
136
rsp.write('-o\n')
137
arg = arg[3:]
138
output_arg = True
139
ignore = True
140
141
if output_arg:
142
# If -o path contains directories, "emcc @response.txt" will likely
143
# fail because the archive we are creating doesn't contain empty
144
# directories for the output path (-o doesn't create directories).
145
# Strip directories to prevent the issue.
146
arg = os.path.basename(arg)
147
output_arg = False
148
149
if not arg.startswith('-') and not ignore:
150
relpath = make_relative(arg)
151
rsp.write(relpath + '\n')
152
reproduce_file.add(arg, os.path.join(root, relpath))
153
else:
154
rsp.write(arg + '\n')
155
156
if ignore:
157
continue
158
159
if arg in CLANG_FLAGS_WITH_ARGS:
160
ignore_next = True
161
162
if arg == '-o':
163
output_arg = True
164
165
reproduce_file.add(rsp_name, os.path.join(root, 'response.txt'))
166
167
168
@ToolchainProfiler.profile()
169
def main(args):
170
if shared.run_via_emxx:
171
clang = shared.CLANG_CXX
172
else:
173
clang = shared.CLANG_CC
174
175
# Special case the handling of `-v` because it has a special/different meaning
176
# when used with no other arguments. In particular, we must handle this early
177
# on, before we inject EMCC_CFLAGS. This is because tools like cmake and
178
# autoconf will run `emcc -v` to determine the compiler version and we don't
179
# want that to break for users of EMCC_CFLAGS.
180
if len(args) == 2 and args[1] == '-v':
181
# autoconf likes to see 'GNU' in the output to enable shared object support
182
print(cmdline.version_string(), file=sys.stderr)
183
return shared.check_call([clang, '-v'] + compile.get_target_flags(), check=False).returncode
184
185
# Additional compiler flags that we treat as if they were passed to us on the
186
# commandline
187
if EMCC_CFLAGS := os.environ.get('EMCC_CFLAGS'):
188
args += shlex.split(EMCC_CFLAGS)
189
190
if DEBUG:
191
logger.warning(f'invocation: {shlex.join(args)} (in {os.getcwd()})')
192
193
# Strip args[0] (program name)
194
args = args[1:]
195
196
# Handle some global flags
197
198
# read response files very early on
199
try:
200
args = substitute_response_files(args)
201
except OSError as e:
202
exit_with_error(e)
203
204
if '--help' in args:
205
# Documentation for emcc and its options must be updated in:
206
# site/source/docs/tools_reference/emcc.rst
207
# This then gets built (via: `make -C site text`) to:
208
# site/build/text/docs/tools_reference/emcc.txt
209
# This then needs to be copied to its final home in docs/emcc.txt from where
210
# we read it here. We have CI rules that ensure its always up-to-date.
211
print(read_file(utils.path_from_root('docs/emcc.txt')))
212
213
print('''
214
------------------------------------------------------------------
215
216
emcc: supported targets: llvm bitcode, WebAssembly, NOT elf
217
(autoconf likes to see elf above to enable shared object support)
218
''')
219
return 0
220
221
## Process argument and setup the compiler
222
state = EmccState(args)
223
newargs = cmdline.parse_arguments(state.orig_args)
224
225
if not shared.SKIP_SUBPROCS:
226
shared.check_sanity()
227
228
# Begin early-exit flag handling.
229
230
if '--version' in args:
231
print(cmdline.version_string())
232
print('''\
233
Copyright (C) 2026 the Emscripten authors (see AUTHORS.txt)
234
This is free and open source software under the MIT license.
235
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
236
''')
237
return 0
238
239
if '-dumpversion' in args: # gcc's doc states "Print the compiler version [...] and don't do anything else."
240
print(utils.EMSCRIPTEN_VERSION)
241
return 0
242
243
if '-dumpmachine' in args or '-print-target-triple' in args or '--print-target-triple' in args:
244
print(shared.get_llvm_target())
245
return 0
246
247
if '-print-search-dirs' in args or '--print-search-dirs' in args:
248
print(f'programs: ={config.LLVM_ROOT}')
249
print(f'libraries: ={cache.get_lib_dir(absolute=True)}')
250
return 0
251
252
if '-print-libgcc-file-name' in args or '--print-libgcc-file-name' in args:
253
settings.limit_settings(None)
254
compiler_rt = system_libs.Library.get_usable_variations()['libcompiler_rt']
255
print(compiler_rt.get_path(absolute=True))
256
return 0
257
258
print_file_name = [a for a in args if a.startswith(('-print-file-name=', '--print-file-name='))]
259
if print_file_name:
260
libname = print_file_name[-1].split('=')[1]
261
system_libpath = cache.get_lib_dir(absolute=True)
262
fullpath = os.path.join(system_libpath, libname)
263
if os.path.isfile(fullpath):
264
print(fullpath)
265
else:
266
print(libname)
267
return 0
268
269
# End early-exit flag handling
270
271
if 'EMMAKEN_NO_SDK' in os.environ:
272
exit_with_error('EMMAKEN_NO_SDK is no longer supported. The standard -nostdlib and -nostdinc flags should be used instead')
273
274
if 'EMMAKEN_COMPILER' in os.environ:
275
exit_with_error('`EMMAKEN_COMPILER` is no longer supported.\n' +
276
'Please use the `LLVM_ROOT` and/or `COMPILER_WRAPPER` config settings instead')
277
278
if 'EMMAKEN_CFLAGS' in os.environ:
279
exit_with_error('`EMMAKEN_CFLAGS` is no longer supported, please use `EMCC_CFLAGS` instead')
280
281
if 'EMCC_REPRODUCE' in os.environ:
282
options.reproduce = os.environ['EMCC_REPRODUCE']
283
284
# For internal consistency, ensure we don't attempt to read or write any link time
285
# settings until we reach the linking phase.
286
settings.limit_settings(COMPILE_TIME_SETTINGS)
287
288
phase_setup(state)
289
290
if '-print-resource-dir' in args or any(a.startswith('--print-prog-name') for a in args):
291
shared.exec_process([clang] + compile.get_cflags(tuple(args)) + args)
292
assert False, 'exec_process should not return'
293
294
if '--cflags' in args:
295
# Just print the flags we pass to clang and exit. We need to do this after
296
# phase_setup because the setup sets things like SUPPORT_LONGJMP.
297
cflags = compile.get_cflags(x for x in args if x != '--cflags')
298
print(shlex.join(cflags))
299
return 0
300
301
if options.reproduce:
302
create_reproduce_file(options.reproduce, args)
303
304
if state.mode == Mode.POST_LINK_ONLY:
305
if len(options.input_files) != 1:
306
exit_with_error('--post-link requires a single input file')
307
linker_args = separate_linker_flags(newargs)[1]
308
linker_args = [f.value for f in linker_args]
309
# Delay import of link.py to avoid processing this file when only compiling
310
from tools import link # noqa: PLC0415
311
link.run_post_link(options.input_files[0], options, linker_args)
312
return 0
313
314
# Compile source code to object files
315
# When only compiling this function never returns.
316
linker_args = phase_compile_inputs(options, state, newargs)
317
318
if state.mode == Mode.COMPILE_AND_LINK:
319
# Delay import of link.py to avoid processing this file when only compiling
320
from tools import link
321
return link.run(options, linker_args)
322
else:
323
logger.debug('stopping after compile phase')
324
return 0
325
326
327
def separate_linker_flags(newargs):
328
"""Process argument list separating out compiler args and linker args.
329
330
- Linker flags include input files and are returned a list of LinkFlag objects.
331
- Compiler flags are those to be passed to `clang -c`.
332
"""
333
334
compiler_args = []
335
linker_args = []
336
337
def add_link_arg(flag, is_file=False):
338
linker_args.append(LinkFlag(flag, is_file))
339
340
skip = False
341
for i in range(len(newargs)):
342
if skip:
343
skip = False
344
continue
345
346
arg = newargs[i]
347
if arg in CLANG_FLAGS_WITH_ARGS:
348
skip = True
349
350
def get_next_arg():
351
if len(newargs) <= i + 1:
352
exit_with_error(f"option '{arg}' requires an argument")
353
return newargs[i + 1]
354
355
if not arg.startswith('-') or arg == '-':
356
if not os.path.exists(arg) and arg != '-':
357
exit_with_error('%s: No such file or directory ("%s" was expected to be an input file, based on the commandline arguments provided)', arg, arg)
358
add_link_arg(arg, True)
359
elif arg == '-z':
360
add_link_arg(arg)
361
add_link_arg(get_next_arg())
362
elif arg.startswith('-Wl,'):
363
for flag in arg.split(',')[1:]:
364
add_link_arg(flag)
365
elif arg == '-Xlinker':
366
add_link_arg(get_next_arg())
367
elif arg == '-s' or arg.startswith(('-l', '-L', '--js-library=', '-z', '-u')):
368
add_link_arg(arg)
369
elif not arg.startswith('-o') and arg not in ('-nostdlib', '-nostartfiles', '-nolibc', '-nodefaultlibs', '-s'):
370
# All other flags are for the compiler
371
compiler_args.append(arg)
372
if skip:
373
compiler_args.append(get_next_arg())
374
375
return compiler_args, linker_args
376
377
378
@ToolchainProfiler.profile_block('setup')
379
def phase_setup(state):
380
"""Second phase: configure and setup the compiler based on the specified settings and arguments.
381
"""
382
383
has_header_inputs = any(get_file_suffix(f) in HEADER_EXTENSIONS for f in options.input_files)
384
385
if options.post_link:
386
state.mode = Mode.POST_LINK_ONLY
387
elif has_header_inputs or options.dash_c or options.dash_S or options.syntax_only or options.dash_E or options.dash_M:
388
state.mode = Mode.COMPILE_ONLY
389
390
if state.mode == Mode.COMPILE_ONLY:
391
for key in user_settings:
392
if key not in COMPILE_TIME_SETTINGS:
393
diagnostics.warning(
394
'unused-command-line-argument',
395
"linker setting ignored during compilation: '%s'" % key)
396
for arg in state.orig_args:
397
if arg in LINK_ONLY_FLAGS:
398
diagnostics.warning(
399
'unused-command-line-argument',
400
"linker flag ignored during compilation: '%s'" % arg)
401
402
if settings.SIDE_MODULE:
403
settings.RELOCATABLE = 1
404
405
if 'USE_PTHREADS' in user_settings:
406
settings.PTHREADS = settings.USE_PTHREADS
407
408
# Pthreads and Wasm Workers require targeting shared Wasm memory (SAB).
409
if settings.PTHREADS or settings.WASM_WORKERS:
410
settings.SHARED_MEMORY = 1
411
412
if 'DISABLE_EXCEPTION_CATCHING' in user_settings and 'EXCEPTION_CATCHING_ALLOWED' in user_settings:
413
# If we get here then the user specified both DISABLE_EXCEPTION_CATCHING and EXCEPTION_CATCHING_ALLOWED
414
# on the command line. This is no longer valid so report either an error or a warning (for
415
# backwards compat with the old `DISABLE_EXCEPTION_CATCHING=2`
416
if user_settings['DISABLE_EXCEPTION_CATCHING'] in ('0', '2'):
417
diagnostics.warning('deprecated', 'DISABLE_EXCEPTION_CATCHING=X is no longer needed when specifying EXCEPTION_CATCHING_ALLOWED')
418
else:
419
exit_with_error('DISABLE_EXCEPTION_CATCHING and EXCEPTION_CATCHING_ALLOWED are mutually exclusive')
420
421
if settings.EXCEPTION_CATCHING_ALLOWED:
422
settings.DISABLE_EXCEPTION_CATCHING = 0
423
424
if settings.WASM_EXCEPTIONS:
425
if user_settings.get('DISABLE_EXCEPTION_CATCHING') == '0':
426
exit_with_error('DISABLE_EXCEPTION_CATCHING=0 is not compatible with -fwasm-exceptions')
427
if user_settings.get('DISABLE_EXCEPTION_THROWING') == '0':
428
exit_with_error('DISABLE_EXCEPTION_THROWING=0 is not compatible with -fwasm-exceptions')
429
# -fwasm-exceptions takes care of enabling them, so users aren't supposed to
430
# pass them explicitly, regardless of their values
431
if 'DISABLE_EXCEPTION_CATCHING' in user_settings or 'DISABLE_EXCEPTION_THROWING' in user_settings:
432
diagnostics.warning('emcc', 'you no longer need to pass DISABLE_EXCEPTION_CATCHING or DISABLE_EXCEPTION_THROWING when using Wasm exceptions')
433
settings.DISABLE_EXCEPTION_CATCHING = 1
434
settings.DISABLE_EXCEPTION_THROWING = 1
435
436
if user_settings.get('ASYNCIFY') == '1':
437
diagnostics.warning('emcc', 'ASYNCIFY=1 is not compatible with -fwasm-exceptions. Parts of the program that mix ASYNCIFY and exceptions will not compile.')
438
439
if user_settings.get('SUPPORT_LONGJMP') == 'emscripten':
440
exit_with_error('SUPPORT_LONGJMP=emscripten is not compatible with -fwasm-exceptions')
441
442
if settings.DISABLE_EXCEPTION_THROWING and not settings.DISABLE_EXCEPTION_CATCHING:
443
exit_with_error("DISABLE_EXCEPTION_THROWING was set (probably from -fno-exceptions) but is not compatible with enabling exception catching (DISABLE_EXCEPTION_CATCHING=0). If you don't want exceptions, set DISABLE_EXCEPTION_CATCHING to 1; if you do want exceptions, don't link with -fno-exceptions")
444
445
if options.target.startswith('wasm64'):
446
default_setting('MEMORY64', 1)
447
448
if settings.MEMORY64 and options.target.startswith('wasm32'):
449
exit_with_error('wasm32 target is not compatible with -sMEMORY64')
450
451
# Wasm SjLj cannot be used with Emscripten EH
452
if settings.SUPPORT_LONGJMP == 'wasm':
453
# DISABLE_EXCEPTION_THROWING is 0 by default for Emscripten EH throwing, but
454
# Wasm SjLj cannot be used with Emscripten EH. We error out if
455
# DISABLE_EXCEPTION_THROWING=0 is explicitly requested by the user;
456
# otherwise we disable it here.
457
if user_settings.get('DISABLE_EXCEPTION_THROWING') == '0':
458
exit_with_error('SUPPORT_LONGJMP=wasm cannot be used with DISABLE_EXCEPTION_THROWING=0')
459
# We error out for DISABLE_EXCEPTION_CATCHING=0, because it is 1 by default
460
# and this can be 0 only if the user specifies so.
461
if user_settings.get('DISABLE_EXCEPTION_CATCHING') == '0':
462
exit_with_error('SUPPORT_LONGJMP=wasm cannot be used with DISABLE_EXCEPTION_CATCHING=0')
463
default_setting('DISABLE_EXCEPTION_THROWING', 1)
464
465
# SUPPORT_LONGJMP=1 means the default SjLj handling mechanism, which is 'wasm'
466
# if Wasm EH is used and 'emscripten' otherwise.
467
if settings.SUPPORT_LONGJMP == 1:
468
if settings.WASM_EXCEPTIONS:
469
settings.SUPPORT_LONGJMP = 'wasm'
470
else:
471
settings.SUPPORT_LONGJMP = 'emscripten'
472
473
474
@ToolchainProfiler.profile_block('compile inputs')
475
def phase_compile_inputs(options, state, newargs):
476
if shared.run_via_emxx:
477
compiler = [shared.CLANG_CXX]
478
else:
479
compiler = [shared.CLANG_CC]
480
481
if config.COMPILER_WRAPPER:
482
logger.debug('using compiler wrapper: %s', config.COMPILER_WRAPPER)
483
compiler.insert(0, config.COMPILER_WRAPPER)
484
485
system_libs.ensure_sysroot()
486
487
def get_clang_command():
488
return compiler + compile.get_cflags(state.orig_args)
489
490
def get_clang_command_preprocessed():
491
return compiler + compile.get_clang_flags(state.orig_args)
492
493
def get_clang_command_asm():
494
return compiler + compile.get_target_flags()
495
496
if state.mode == Mode.COMPILE_ONLY:
497
if options.output_file and get_file_suffix(options.output_file) == '.bc' and not settings.LTO and '-emit-llvm' not in state.orig_args:
498
diagnostics.warning('emcc', '.bc output file suffix used without -flto or -emit-llvm. Consider using .o extension since emcc will output an object file, not a bitcode file')
499
if all(get_file_suffix(i) in ASSEMBLY_EXTENSIONS for i in options.input_files):
500
cmd = get_clang_command_asm() + newargs
501
else:
502
cmd = get_clang_command() + newargs
503
shared.exec_process(cmd)
504
assert False, 'exec_process should not return'
505
506
# In COMPILE_AND_LINK we need to compile source files too, but we also need to
507
# filter out the link flags
508
assert state.mode == Mode.COMPILE_AND_LINK
509
assert not options.dash_c
510
compile_args, linker_args = separate_linker_flags(newargs)
511
512
# Map of file basenames to how many times we've seen them. We use this to generate
513
# unique `_NN` suffix for object files in cases when we are compiling multiple sources that
514
# have the same basename. e.g. `foo/utils.c` and `bar/utils.c` on the same command line.
515
seen_names = {}
516
517
def uniquename(name):
518
if name not in seen_names:
519
# No suffix needed the first time we see given name.
520
seen_names[name] = 1
521
return name
522
523
unique_suffix = '_%d' % seen_names[name]
524
seen_names[name] += 1
525
base, ext = os.path.splitext(name)
526
return base + unique_suffix + ext
527
528
def get_object_filename(input_file):
529
objfile = unsuffixed_basename(input_file) + '.o'
530
return in_temp(uniquename(objfile))
531
532
def compile_source_file(input_file):
533
logger.debug(f'compiling source file: {input_file}')
534
output_file = get_object_filename(input_file)
535
ext = get_file_suffix(input_file)
536
if ext in ASSEMBLY_EXTENSIONS:
537
cmd = get_clang_command_asm()
538
elif ext in PREPROCESSED_EXTENSIONS:
539
cmd = get_clang_command_preprocessed()
540
else:
541
cmd = get_clang_command()
542
if ext == '.pcm':
543
cmd = [c for c in cmd if not c.startswith('-fprebuilt-module-path=')]
544
cmd += compile_args + ['-c', input_file, '-o', output_file]
545
if options.requested_debug == '-gsplit-dwarf':
546
# When running in COMPILE_AND_LINK mode we compile objects to a temporary location
547
# but we want the `.dwo` file to be generated in the current working directory,
548
# like it is under clang. We could avoid this hack if we use the clang driver
549
# to generate the temporary files, but that would also involve using the clang
550
# driver to perform linking which would be a big change.
551
cmd += ['-Xclang', '-split-dwarf-file', '-Xclang', unsuffixed_basename(input_file) + '.dwo']
552
cmd += ['-Xclang', '-split-dwarf-output', '-Xclang', unsuffixed_basename(input_file) + '.dwo']
553
shared.check_call(cmd)
554
if not shared.SKIP_SUBPROCS:
555
assert os.path.exists(output_file)
556
if options.save_temps:
557
shutil.copyfile(output_file, utils.unsuffixed_basename(input_file) + '.o')
558
return output_file
559
560
# Compile input files individually to temporary locations.
561
for arg in linker_args:
562
if not arg.is_file:
563
continue
564
input_file = arg.value
565
file_suffix = get_file_suffix(input_file)
566
if file_suffix in SOURCE_EXTENSIONS | ASSEMBLY_EXTENSIONS or (options.dash_c and file_suffix == '.bc'):
567
arg.value = compile_source_file(input_file)
568
elif file_suffix in DYLIB_EXTENSIONS:
569
logger.debug(f'using shared library: {input_file}')
570
elif building.is_ar(input_file):
571
logger.debug(f'using static library: {input_file}')
572
elif options.input_language:
573
arg.value = compile_source_file(input_file)
574
elif input_file == '-':
575
exit_with_error('-E or -x required when input is from standard input')
576
else:
577
# Default to assuming the inputs are object files and pass them to the linker
578
pass
579
580
return [f.value for f in linker_args]
581
582
583
if __name__ == '__main__':
584
try:
585
sys.exit(main(sys.argv))
586
except KeyboardInterrupt:
587
logger.debug('KeyboardInterrupt')
588
sys.exit(1)
589
590