Skip to content

Commit 7013ed9

Browse files
authored
Simplify file extension handling (#6615)
1 parent afe93d6 commit 7013ed9

File tree

2 files changed

+49
-56
lines changed

2 files changed

+49
-56
lines changed

emcc.py

Lines changed: 49 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
from subprocess import PIPE
4040

4141
from tools import shared, system_libs, client_mods, js_optimizer, jsrun
42-
from tools.shared import suffix, unsuffixed, unsuffixed_basename, WINDOWS, safe_copy, safe_move, run_process, asbytes, read_and_preprocess, exit_with_error, DEBUG
42+
from tools.shared import unsuffixed, unsuffixed_basename, WINDOWS, safe_copy, safe_move, run_process, asbytes, read_and_preprocess, exit_with_error, DEBUG
4343
from tools.response_file import substitute_response_files
4444
import tools.line_endings
4545
from tools.toolchain_profiler import ToolchainProfiler
@@ -64,6 +64,7 @@
6464
SOURCE_ENDINGS = C_ENDINGS + CXX_ENDINGS + OBJC_ENDINGS + OBJCXX_ENDINGS + SPECIAL_ENDINGLESS_FILENAMES
6565
C_ENDINGS = C_ENDINGS + SPECIAL_ENDINGLESS_FILENAMES # consider the special endingless filenames like /dev/null to be C
6666

67+
JS_CONTAINING_ENDINGS = ('.js', '.mjs', '.html')
6768
BITCODE_ENDINGS = ('.bc', '.o', '.obj', '.lo')
6869
DYNAMICLIB_ENDINGS = ('.dylib', '.so') # Windows .dll suffix is not included in this list, since those are never linked to directly on the command line.
6970
STATICLIB_ENDINGS = ('.a',)
@@ -79,8 +80,6 @@
7980

8081
LIB_PREFIXES = ('', 'lib')
8182

82-
JS_CONTAINING_SUFFIXES = ('js', 'mjs', 'html')
83-
EXECUTABLE_SUFFIXES = JS_CONTAINING_SUFFIXES + ('wasm',)
8483

8584
DEFERRED_RESPONSE_FILES = ('EMTERPRETIFY_BLACKLIST', 'EMTERPRETIFY_WHITELIST', 'EMTERPRETIFY_SYNCLIST')
8685

@@ -633,12 +632,16 @@ def filter_emscripten_options(argv):
633632

634633
# ---------------- Utilities ---------------
635634

635+
def suffix(name):
636+
"""Return the file extension"""
637+
return os.path.splitext(name)[1]
638+
636639
seen_names = {}
637640

638641
def uniquename(name):
639642
if name not in seen_names:
640643
seen_names[name] = str(len(seen_names))
641-
return unsuffixed(name) + '_' + seen_names[name] + (('.' + suffix(name)) if suffix(name) else '')
644+
return unsuffixed(name) + '_' + seen_names[name] + suffix(name)
642645

643646
# ---------------- End configs -------------
644647

@@ -662,28 +665,23 @@ def uniquename(name):
662665
target = specified_target if specified_target is not None else 'a.out.js' # specified_target is the user-specified one, target is what we will generate
663666
target_basename = unsuffixed_basename(target)
664667

665-
if '.' in target:
666-
final_suffix = target.split('.')[-1]
667-
else:
668-
final_suffix = ''
668+
final_suffix = suffix(target)
669669

670670
temp_dir = shared.get_emscripten_temp_dir()
671671

672672
def in_temp(name):
673673
return os.path.join(temp_dir, os.path.basename(name))
674674

675-
# Parses the essential suffix of a filename, discarding Unix-style version numbers in the name. For example for 'libz.so.1.2.8' returns '.so'
676-
def filename_type_suffix(filename):
677-
for i in reversed(filename.split('.')[1:]):
678-
if not i.isdigit():
679-
return i
680-
return ''
681-
682-
def filename_type_ending(filename):
675+
def get_file_suffix(filename):
676+
"""Parses the essential suffix of a filename, discarding Unix-style version
677+
numbers in the name. For example for 'libz.so.1.2.8' returns '.so'"""
683678
if filename in SPECIAL_ENDINGLESS_FILENAMES:
684679
return filename
685-
suffix = filename_type_suffix(filename)
686-
return '' if not suffix else ('.' + suffix)
680+
while filename:
681+
filename, suffix = os.path.splitext(filename)
682+
if not suffix[1:].isdigit():
683+
return suffix
684+
return ''
687685

688686
def optimizing(opts):
689687
return '-O0' not in opts
@@ -832,25 +830,25 @@ def detect_fixed_language_mode(args):
832830
'-current_version', '-I', '-L', '-include-pch'):
833831
continue # ignore this gcc-style argument
834832

835-
if os.path.islink(arg) and os.path.realpath(arg).endswith(SOURCE_ENDINGS + BITCODE_ENDINGS + DYNAMICLIB_ENDINGS + ASSEMBLY_ENDINGS + HEADER_ENDINGS):
833+
if os.path.islink(arg) and get_file_suffix(os.path.realpath(arg)) in SOURCE_ENDINGS + BITCODE_ENDINGS + DYNAMICLIB_ENDINGS + ASSEMBLY_ENDINGS + HEADER_ENDINGS:
836834
arg = os.path.realpath(arg)
837835

838836
if not arg.startswith('-'):
839837
if not os.path.exists(arg):
840838
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)
841839

842-
arg_ending = filename_type_ending(arg)
843-
if arg_ending.endswith(SOURCE_ENDINGS + BITCODE_ENDINGS + DYNAMICLIB_ENDINGS + ASSEMBLY_ENDINGS + HEADER_ENDINGS) or shared.Building.is_ar(arg): # we already removed -o <target>, so all these should be inputs
840+
file_suffix = get_file_suffix(arg)
841+
if file_suffix in SOURCE_ENDINGS + BITCODE_ENDINGS + DYNAMICLIB_ENDINGS + ASSEMBLY_ENDINGS + HEADER_ENDINGS or shared.Building.is_ar(arg): # we already removed -o <target>, so all these should be inputs
844842
newargs[i] = ''
845-
if arg_ending.endswith(SOURCE_ENDINGS):
843+
if file_suffix.endswith(SOURCE_ENDINGS):
846844
input_files.append((i, arg))
847845
has_source_inputs = True
848-
elif arg_ending.endswith(HEADER_ENDINGS):
846+
elif file_suffix.endswith(HEADER_ENDINGS):
849847
input_files.append((i, arg))
850848
has_header_inputs = True
851-
elif arg_ending.endswith(ASSEMBLY_ENDINGS) or shared.Building.is_bitcode(arg): # this should be bitcode, make sure it is valid
849+
elif file_suffix.endswith(ASSEMBLY_ENDINGS) or shared.Building.is_bitcode(arg): # this should be bitcode, make sure it is valid
852850
input_files.append((i, arg))
853-
elif arg_ending.endswith(STATICLIB_ENDINGS + DYNAMICLIB_ENDINGS):
851+
elif file_suffix.endswith(STATICLIB_ENDINGS + DYNAMICLIB_ENDINGS):
854852
# if it's not, and it's a library, just add it to libs to find later
855853
l = unsuffixed_basename(arg)
856854
for prefix in LIB_PREFIXES:
@@ -865,7 +863,7 @@ def detect_fixed_language_mode(args):
865863
input_files.append((i, arg))
866864
else:
867865
logger.warning(arg + ' is not valid LLVM bitcode')
868-
elif arg_ending.endswith(STATICLIB_ENDINGS):
866+
elif file_suffix.endswith(STATICLIB_ENDINGS):
869867
if not shared.Building.is_ar(arg):
870868
if shared.Building.is_bitcode(arg):
871869
message = arg + ': File has a suffix of a static library ' + str(STATICLIB_ENDINGS) + ', but instead is an LLVM bitcode file! When linking LLVM bitcode files, use one of the suffixes ' + str(BITCODE_ENDINGS)
@@ -909,15 +907,14 @@ def detect_fixed_language_mode(args):
909907
if has_dash_c:
910908
assert has_source_inputs or has_header_inputs, 'Must have source code or header inputs to use -c'
911909
target = target_basename + '.o'
912-
final_suffix = 'o'
910+
final_suffix = '.o'
913911
if '-E' in newargs:
914-
final_suffix = 'eout' # not bitcode, not js; but just result from preprocessing stage of the input file
912+
final_suffix = '.eout' # not bitcode, not js; but just result from preprocessing stage of the input file
915913
if '-M' in newargs or '-MM' in newargs:
916-
final_suffix = 'mout' # not bitcode, not js; but just dependency rule of the input file
917-
final_ending = ('.' + final_suffix) if len(final_suffix) else ''
914+
final_suffix = '.mout' # not bitcode, not js; but just dependency rule of the input file
918915

919916
# target is now finalized, can finalize other _target s
920-
if final_suffix == 'mjs':
917+
if final_suffix == '.mjs':
921918
shared.Settings.EXPORT_ES6 = 1
922919
shared.Settings.MODULARIZE = 1
923920
js_target = target
@@ -928,7 +925,7 @@ def detect_fixed_language_mode(args):
928925
wasm_text_target = asm_target.replace('.asm.js', '.wast') # ditto, might not be used
929926
wasm_binary_target = asm_target.replace('.asm.js', '.wasm') # ditto, might not be used
930927

931-
if final_suffix == 'html' and not options.separate_asm and 'PRECISE_F32=2' in settings_changes:
928+
if final_suffix == '.html' and not options.separate_asm and 'PRECISE_F32=2' in settings_changes:
932929
options.separate_asm = True
933930
logger.warning('forcing separate asm output (--separate-asm), because -s PRECISE_F32=2 was passed.')
934931
if options.separate_asm:
@@ -958,9 +955,9 @@ def get_last_setting_change(setting):
958955

959956
# If not compiling to JS, then we are compiling to an intermediate bitcode objects or library, so
960957
# ignore dynamic linking, since multiple dynamic linkings can interfere with each other
961-
if filename_type_suffix(target) not in JS_CONTAINING_SUFFIXES or options.ignore_dynamic_linking:
958+
if get_file_suffix(target) not in JS_CONTAINING_ENDINGS or options.ignore_dynamic_linking:
962959
def check(input_file):
963-
if filename_type_ending(input_file) in DYNAMICLIB_ENDINGS:
960+
if get_file_suffix(input_file) in DYNAMICLIB_ENDINGS:
964961
if not options.ignore_dynamic_linking:
965962
logger.warning('ignoring dynamic library %s because not compiling to JS or HTML, remember to link it when compiling to JS or HTML at the end', os.path.basename(input_file))
966963
return False
@@ -973,7 +970,7 @@ def check(input_file):
973970

974971
newargs = CC_ADDITIONAL_ARGS + newargs
975972

976-
if options.separate_asm and final_suffix != 'html':
973+
if options.separate_asm and final_suffix != '.html':
977974
shared.WarningManager.warn('SEPARATE_ASM')
978975

979976
# Apply optimization level settings
@@ -1000,7 +997,7 @@ def check(input_file):
1000997

1001998
if options.bind:
1002999
# If we are using embind and generating JS, now is the time to link in bind.cpp
1003-
if final_suffix in JS_CONTAINING_SUFFIXES:
1000+
if final_suffix in JS_CONTAINING_ENDINGS:
10041001
input_files.append((next_arg_index, shared.path_from_root('system', 'lib', 'embind', 'bind.cpp')))
10051002
next_arg_index += 1
10061003

@@ -1099,22 +1096,22 @@ def check(input_file):
10991096
shared.Settings.WORKAROUND_IOS_9_RIGHT_SHIFT_BUG = 0
11001097
shared.Settings.WORKAROUND_OLD_WEBGL_UNIFORM_UPLOAD_IGNORED_OFFSET_BUG = 0
11011098

1102-
if shared.Settings.STB_IMAGE and final_suffix in JS_CONTAINING_SUFFIXES:
1099+
if shared.Settings.STB_IMAGE and final_suffix in JS_CONTAINING_ENDINGS:
11031100
input_files.append((next_arg_index, shared.path_from_root('third_party', 'stb_image.c')))
11041101
next_arg_index += 1
11051102
shared.Settings.EXPORTED_FUNCTIONS += ['_stbi_load', '_stbi_load_from_memory', '_stbi_image_free']
11061103
# stb_image 2.x need to have STB_IMAGE_IMPLEMENTATION defined to include the implementation when compiling
11071104
newargs.append('-DSTB_IMAGE_IMPLEMENTATION')
11081105

1109-
if shared.Settings.ASMFS and final_suffix in JS_CONTAINING_SUFFIXES:
1106+
if shared.Settings.ASMFS and final_suffix in JS_CONTAINING_ENDINGS:
11101107
input_files.append((next_arg_index, shared.path_from_root('system', 'lib', 'fetch', 'asmfs.cpp')))
11111108
newargs.append('-D__EMSCRIPTEN_ASMFS__=1')
11121109
next_arg_index += 1
11131110
shared.Settings.FILESYSTEM = 0
11141111
shared.Settings.FETCH = 1
11151112
options.js_libraries.append(shared.path_from_root('src', 'library_asmfs.js'))
11161113

1117-
if shared.Settings.FETCH and final_suffix in JS_CONTAINING_SUFFIXES:
1114+
if shared.Settings.FETCH and final_suffix in JS_CONTAINING_ENDINGS:
11181115
input_files.append((next_arg_index, shared.path_from_root('system', 'lib', 'fetch', 'emscripten_fetch.cpp')))
11191116
next_arg_index += 1
11201117
options.js_libraries.append(shared.path_from_root('src', 'library_fetch.js'))
@@ -1486,31 +1483,31 @@ def check(input_file):
14861483
return run_process([call] + args, check=False).returncode
14871484

14881485
def get_bitcode_file(input_file):
1489-
if final_suffix not in JS_CONTAINING_SUFFIXES:
1486+
if final_suffix not in JS_CONTAINING_ENDINGS:
14901487
# no need for a temp file, just emit to the right place
14911488
if len(input_files) == 1:
14921489
# can just emit directly to the target
14931490
if specified_target:
14941491
if specified_target.endswith('/') or specified_target.endswith('\\') or os.path.isdir(specified_target):
14951492
return os.path.join(specified_target, os.path.basename(unsuffixed(input_file))) + options.default_object_extension
14961493
return specified_target
1497-
return unsuffixed(input_file) + final_ending
1494+
return unsuffixed(input_file) + final_suffix
14981495
else:
14991496
if has_dash_c:
15001497
return unsuffixed(input_file) + options.default_object_extension
15011498
return in_temp(unsuffixed(uniquename(input_file)) + options.default_object_extension)
15021499

15031500
# Request LLVM debug info if explicitly specified, or building bitcode with -g, or if building a source all the way to JS with -g
1504-
if use_source_map(options) or ((final_suffix not in JS_CONTAINING_SUFFIXES or (has_source_inputs and final_suffix in JS_CONTAINING_SUFFIXES)) and options.requested_debug == '-g'):
1501+
if use_source_map(options) or ((final_suffix not in JS_CONTAINING_ENDINGS or (has_source_inputs and final_suffix in JS_CONTAINING_ENDINGS)) and options.requested_debug == '-g'):
15051502
# do not save llvm debug info if js optimizer will wipe it out anyhow (but if source maps are used, keep it)
1506-
if use_source_map(options) or not (final_suffix in JS_CONTAINING_SUFFIXES and options.js_opts):
1503+
if use_source_map(options) or not (final_suffix in JS_CONTAINING_ENDINGS and options.js_opts):
15071504
newargs.append('-g') # preserve LLVM debug info
15081505
options.debug_level = 4
15091506
shared.Settings.DEBUG_LEVEL = 4
15101507

15111508
# Bitcode args generation code
15121509
def get_clang_args(input_files):
1513-
file_ending = filename_type_ending(input_files[0])
1510+
file_ending = get_file_suffix(input_files[0])
15141511
args = [call] + newargs + input_files
15151512
if file_ending.endswith(CXX_ENDINGS):
15161513
args += shared.EMSDK_CXX_OPTS
@@ -1554,7 +1551,7 @@ def compile_source_file(i, input_file):
15541551

15551552
# First, generate LLVM bitcode. For each input file, we get base.o with bitcode
15561553
for i, input_file in input_files:
1557-
file_ending = filename_type_ending(input_file)
1554+
file_ending = get_file_suffix(input_file)
15581555
if file_ending.endswith(SOURCE_ENDINGS):
15591556
compile_source_file(i, input_file)
15601557
else: # bitcode
@@ -1586,7 +1583,7 @@ def compile_source_file(i, input_file):
15861583
# Optimize source files
15871584
if optimizing(options.llvm_opts):
15881585
for pos, (_, input_file) in enumerate(input_files):
1589-
file_ending = filename_type_ending(input_file)
1586+
file_ending = get_file_suffix(input_file)
15901587
if file_ending.endswith(SOURCE_ENDINGS):
15911588
temp_file = temp_files[pos][1]
15921589
logger.debug('optimizing %s', input_file)
@@ -1601,7 +1598,8 @@ def compile_source_file(i, input_file):
16011598
temp_files[pos] = (temp_files[pos][0], new_temp_file)
16021599

16031600
# Decide what we will link
1604-
stop_at_bitcode = final_suffix not in EXECUTABLE_SUFFIXES
1601+
executable_endings = JS_CONTAINING_ENDINGS + ('.wasm',)
1602+
stop_at_bitcode = final_suffix not in executable_endings
16051603

16061604
if stop_at_bitcode or not shared.Settings.WASM_BACKEND:
16071605
# Filter link flags, keeping only those that shared.Building.link knows
@@ -1617,12 +1615,12 @@ def compile_source_file(i, input_file):
16171615
if not specified_target:
16181616
assert len(temp_files) == len(input_files)
16191617
for tempf, inputf in zip(temp_files, input_files):
1620-
safe_move(tempf[1], unsuffixed_basename(inputf[1]) + final_ending)
1618+
safe_move(tempf[1], unsuffixed_basename(inputf[1]) + final_suffix)
16211619
else:
16221620
if len(input_files) == 1:
16231621
input_file = input_files[0][1]
16241622
temp_file = temp_files[0][1]
1625-
bitcode_target = specified_target if specified_target else unsuffixed_basename(input_file) + final_ending
1623+
bitcode_target = specified_target if specified_target else unsuffixed_basename(input_file) + final_suffix
16261624
if temp_file != input_file:
16271625
safe_move(temp_file, bitcode_target)
16281626
else:
@@ -1646,7 +1644,7 @@ def compile_source_file(i, input_file):
16461644
logger.debug('stopping at bitcode')
16471645
if shared.Settings.SIDE_MODULE:
16481646
exit_with_error('SIDE_MODULE must only be used when compiling to an executable shared library, and not when emitting LLVM bitcode. That is, you should be emitting a .wasm file (for wasm) or a .js file (for asm.js). Note that when compiling to a typical native suffix for a shared library (.so, .dylib, .dll; which many build systems do) then Emscripten emits an LLVM bitcode file, which you should then compile to .wasm or .js with SIDE_MODULE.')
1649-
if final_suffix.lower() in ['so', 'dylib', 'dll']:
1647+
if final_suffix.lower() in ('.so', '.dylib', '.dll'):
16501648
logger.warning('When Emscripten compiles to a typical native suffix for shared libraries (.so, .dylib, .dll) then it emits an LLVM bitcode file. You should then compile that to an emscripten SIDE_MODULE (using that flag) with suffix .wasm (for wasm) or .js (for asm.js). (You may also want to adapt your build system to emit the more standard suffix for a file with LLVM bitcode, \'.bc\', which would avoid this warning.)')
16511649
return 0
16521650

@@ -2109,7 +2107,7 @@ def get_eliminate():
21092107
generated_text_files_with_native_eols += [js_target]
21102108

21112109
# If we were asked to also generate HTML, do that
2112-
if final_suffix == 'html':
2110+
if final_suffix == '.html':
21132111
generate_html(target, options, js_target, target_basename,
21142112
asm_target, wasm_binary_target,
21152113
memfile, optimizer)

tools/shared.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3166,11 +3166,6 @@ def asbytes(s):
31663166
return s.encode('utf-8')
31673167

31683168

3169-
def suffix(name):
3170-
"""Return the file extension *not* including the '.'."""
3171-
return os.path.splitext(name)[1][1:]
3172-
3173-
31743169
def unsuffixed(name):
31753170
"""Return the filename without the extention.
31763171

0 commit comments

Comments
 (0)