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