Skip to content

Commit 6a5925a

Browse files
authored
[emcc.py] Simplify passing arguments to clang in compile-only mode (#23454)
This change avoids removing the `-o` flag and the input files when only compiling. We delay the splitting out of the linker flags and the input files until we know actually do to do linking. This effect of this is that the compile-only phase is now much simply because it doesn't need to re-inject the input files and the `-o` flag. This also as the effect of keeping the order of those flags preserved with respect to other command line flags when calling clang to do compilation.
1 parent 797fcd9 commit 6a5925a

File tree

2 files changed

+46
-49
lines changed

2 files changed

+46
-49
lines changed

emcc.py

Lines changed: 44 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ class EmccOptions:
139139
def __init__(self):
140140
self.target = ''
141141
self.output_file = None
142+
self.input_files = []
142143
self.no_minify = False
143144
self.post_link = False
144145
self.save_temps = False
@@ -659,21 +660,23 @@ def run(args):
659660
# settings until we reach the linking phase.
660661
settings.limit_settings(COMPILE_TIME_SETTINGS)
661662

662-
newargs, input_files = phase_setup(options, state, newargs)
663+
phase_setup(options, state, newargs)
663664

664665
if options.reproduce:
665666
create_reproduce_file(options.reproduce, args)
666667

667668
if state.mode == Mode.POST_LINK_ONLY:
668-
if len(input_files) != 1:
669+
if len(options.input_files) != 1:
669670
exit_with_error('--post-link requires a single input file')
671+
separate_linker_flags(options, state, newargs)
670672
# Delay import of link.py to avoid processing this file when only compiling
671673
from tools import link
672-
link.run_post_link(input_files[0][1], options, state)
674+
link.run_post_link(options.input_files[0], options, state)
673675
return 0
674676

675-
## Compile source code to object files
676-
linker_inputs = phase_compile_inputs(options, state, newargs, input_files)
677+
# Compile source code to object files
678+
# When only compiling this function never returns.
679+
linker_inputs = phase_compile_inputs(options, state, newargs)
677680

678681
if state.mode == Mode.COMPILE_AND_LINK:
679682
# Delay import of link.py to avoid processing this file when only compiling
@@ -711,11 +714,13 @@ def phase_parse_arguments(state):
711714
settings.WARN_DEPRECATED = 0
712715

713716
for i in range(len(newargs)):
714-
if newargs[i] in ('-l', '-L', '-I', '-z', '--js-library'):
717+
if newargs[i] in ('-l', '-L', '-I', '-z', '--js-library', '-o'):
715718
# Scan for flags that can be written as either one or two arguments
716719
# and normalize them to the single argument form.
717720
if newargs[i] == '--js-library':
718721
newargs[i] += '='
722+
if len(newargs) <= i + 1:
723+
exit_with_error(f"option '{newargs[i]}' requires an argument")
719724
newargs[i] += newargs[i + 1]
720725
newargs[i + 1] = ''
721726

@@ -748,10 +753,8 @@ def phase_parse_arguments(state):
748753
return options, newargs
749754

750755

751-
@ToolchainProfiler.profile_block('setup')
752-
def phase_setup(options, state, newargs):
753-
"""Second phase: configure and setup the compiler based on the specified settings and arguments.
754-
"""
756+
def separate_linker_flags(options, state, newargs):
757+
newargs = list(newargs)
755758

756759
if settings.RUNTIME_LINKED_LIBS:
757760
newargs += settings.RUNTIME_LINKED_LIBS
@@ -769,7 +772,6 @@ def phase_setup(options, state, newargs):
769772
# based on a full understanding of gcc params, right now we just assume that
770773
# what is left contains no more |-x OPT| things
771774
skip = False
772-
has_header_inputs = False
773775
for i in range(len(newargs)):
774776
if skip:
775777
skip = False
@@ -792,10 +794,9 @@ def get_next_arg():
792794
# https://bugs.python.org/issue1311
793795
if not os.path.exists(arg) and arg != os.devnull:
794796
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)
795-
file_suffix = get_file_suffix(arg)
796-
if file_suffix in HEADER_ENDINGS:
797-
has_header_inputs = True
798797
input_files.append((i, arg))
798+
elif arg.startswith('-o'):
799+
newargs[i] = ''
799800
elif arg.startswith('-L'):
800801
state.add_link_flag(i, arg)
801802
elif arg.startswith('-l'):
@@ -824,9 +825,15 @@ def get_next_arg():
824825

825826
newargs = [a for a in newargs if a]
826827

827-
# SSEx is implemented on top of SIMD128 instruction set, but do not pass SSE flags to LLVM
828-
# so it won't think about generating native x86 SSE code.
829-
newargs = [x for x in newargs if x not in SIMD_INTEL_FEATURE_TOWER and x not in SIMD_NEON_FLAGS]
828+
return newargs, input_files
829+
830+
831+
@ToolchainProfiler.profile_block('setup')
832+
def phase_setup(options, state, newargs):
833+
"""Second phase: configure and setup the compiler based on the specified settings and arguments.
834+
"""
835+
836+
has_header_inputs = any(get_file_suffix(f) in HEADER_ENDINGS for f in options.input_files)
830837

831838
if options.post_link:
832839
state.mode = Mode.POST_LINK_ONLY
@@ -928,8 +935,6 @@ def get_next_arg():
928935
if settings.USE_SDL == 2 or settings.USE_SDL_MIXER == 2 or settings.USE_SDL_GFX == 2:
929936
default_setting('GL_ENABLE_GET_PROC_ADDRESS', 1)
930937

931-
return (newargs, input_files)
932-
933938

934939
def filter_out_link_flags(args):
935940
rtn = []
@@ -954,7 +959,7 @@ def is_link_flag(flag):
954959

955960

956961
@ToolchainProfiler.profile_block('compile inputs')
957-
def phase_compile_inputs(options, state, compile_args, input_files):
962+
def phase_compile_inputs(options, state, newargs):
958963
if shared.run_via_emxx:
959964
compiler = [shared.CLANG_CXX]
960965
else:
@@ -978,24 +983,21 @@ def get_language_mode(args):
978983
return removeprefix(item, '-x')
979984
return ''
980985

981-
language_mode = get_language_mode(compile_args)
986+
language_mode = get_language_mode(newargs)
982987
use_cxx = 'c++' in language_mode or shared.run_via_emxx
983988

984989
def get_clang_command():
985-
return compiler + get_cflags(state.orig_args, use_cxx) + compile_args
990+
return compiler + get_cflags(state.orig_args, use_cxx)
986991

987992
def get_clang_command_preprocessed():
988-
return compiler + get_clang_flags(state.orig_args) + compile_args
993+
return compiler + get_clang_flags(state.orig_args)
989994

990995
def get_clang_command_asm():
991-
return compiler + get_target_flags() + compile_args
996+
return compiler + get_target_flags()
992997

993998
# preprocessor-only (-E/-M) support
994999
if state.mode == Mode.PREPROCESS_ONLY:
995-
inputs = [i[1] for i in input_files]
996-
cmd = get_clang_command() + inputs
997-
if options.output_file:
998-
cmd += ['-o', options.output_file]
1000+
cmd = get_clang_command() + newargs
9991001
# Do not compile, but just output the result from preprocessing stage or
10001002
# output the dependency rule. Warning: clang and gcc behave differently
10011003
# with -MF! (clang seems to not recognize it)
@@ -1005,34 +1007,26 @@ def get_clang_command_asm():
10051007

10061008
# Precompiled headers support
10071009
if state.mode == Mode.PCH:
1008-
inputs = [i[1] for i in input_files]
1009-
for header in inputs:
1010-
if shared.suffix(header) not in HEADER_ENDINGS:
1011-
exit_with_error(f'cannot mix precompiled headers with non-header inputs: {inputs} : {header}')
1012-
cmd = get_clang_command() + inputs
1013-
if options.output_file:
1014-
cmd += ['-o', options.output_file]
1010+
cmd = get_clang_command() + newargs
10151011
logger.debug(f"running (for precompiled headers): {cmd[0]} {' '.join(cmd[1:])}")
10161012
shared.exec_process(cmd)
10171013
assert False, 'exec_process does not return'
10181014

10191015
if state.mode == Mode.COMPILE_ONLY:
1020-
inputs = [i[1] for i in input_files]
1021-
if all(get_file_suffix(i) in ASSEMBLY_ENDINGS for i in inputs):
1022-
cmd = get_clang_command_asm() + inputs
1016+
if options.output_file and get_file_suffix(options.output_file) == '.bc' and not settings.LTO and '-emit-llvm' not in state.orig_args:
1017+
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')
1018+
if all(get_file_suffix(i) in ASSEMBLY_ENDINGS for i in options.input_files):
1019+
cmd = get_clang_command_asm() + newargs
10231020
else:
1024-
cmd = get_clang_command() + inputs
1025-
if options.output_file:
1026-
cmd += ['-o', options.output_file]
1027-
if get_file_suffix(options.output_file) == '.bc' and not settings.LTO and '-emit-llvm' not in state.orig_args:
1028-
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')
1021+
cmd = get_clang_command() + newargs
10291022
shared.exec_process(cmd)
10301023
assert False, 'exec_process does not return'
10311024

10321025
# In COMPILE_AND_LINK we need to compile source files too, but we also need to
10331026
# filter out the link flags
10341027
assert state.mode == Mode.COMPILE_AND_LINK
10351028
assert not options.dash_c
1029+
compile_args, input_files = separate_linker_flags(options, state, newargs)
10361030
compile_args = filter_out_link_flags(compile_args)
10371031
linker_inputs = []
10381032
seen_names = {}
@@ -1057,7 +1051,7 @@ def compile_source_file(i, input_file):
10571051
cmd = get_clang_command()
10581052
if get_file_suffix(input_file) in ['.pcm']:
10591053
cmd = [c for c in cmd if not c.startswith('-fprebuilt-module-path=')]
1060-
cmd += ['-c', input_file, '-o', output_file]
1054+
cmd += compile_args + ['-c', input_file, '-o', output_file]
10611055
if state.mode == Mode.COMPILE_AND_LINK and options.requested_debug == '-gsplit-dwarf':
10621056
# When running in COMPILE_AND_LINK mode we compile to temporary location
10631057
# but we want the `.dwo` file to be generated in the current working directory,
@@ -1481,11 +1475,8 @@ def consume_arg_file():
14811475
options.shared = True
14821476
elif check_flag('-r'):
14831477
options.relocatable = True
1484-
elif check_arg('-o'):
1485-
options.output_file = consume_arg()
14861478
elif arg.startswith('-o'):
14871479
options.output_file = removeprefix(arg, '-o')
1488-
newargs[i] = ''
14891480
elif check_arg('-target') or check_arg('--target'):
14901481
options.target = consume_arg()
14911482
if options.target not in ('wasm32', 'wasm64', 'wasm64-unknown-emscripten', 'wasm32-unknown-emscripten'):
@@ -1502,6 +1493,12 @@ def consume_arg_file():
15021493
options.dash_M = True
15031494
elif arg == '-fsyntax-only':
15041495
options.syntax_only = True
1496+
elif arg in SIMD_INTEL_FEATURE_TOWER or arg in SIMD_NEON_FLAGS:
1497+
# SSEx is implemented on top of SIMD128 instruction set, but do not pass SSE flags to LLVM
1498+
# so it won't think about generating native x86 SSE code.
1499+
newargs[i] = ''
1500+
elif arg and (arg == '-' or not arg.startswith('-')):
1501+
options.input_files.append(arg)
15051502

15061503
if should_exit:
15071504
sys.exit(0)

test/test_other.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12646,8 +12646,8 @@ def test_default_to_cxx(self):
1264612646
self.assertContained("'string' file not found", err)
1264712647

1264812648
# But it works if we pass and explicit language mode.
12649-
self.run_process([EMCC, '-c', 'cxxfoo.h', '-x', 'c++-header'])
12650-
self.run_process([EMCC, '-c', 'cxxfoo.h', '-x', 'c++'])
12649+
self.run_process([EMCC, '-x', 'c++-header', '-c', 'cxxfoo.h'])
12650+
self.run_process([EMCC, '-x', 'c++', '-c', 'cxxfoo.h'])
1265112651

1265212652
@parameterized({
1265312653
'': ([],),

0 commit comments

Comments
 (0)