Merge branch 'archive_groups' of github.com:rsturgell/emscripten into incoming
Conflicts: AUTHORS
This commit is contained in:
Коммит
447f96296b
2
AUTHORS
2
AUTHORS
|
@ -140,3 +140,5 @@ a license to everyone to use it as detailed in LICENSE.)
|
|||
* Camilo Polymeris <cpolymeris@gmail.com>
|
||||
* Markus Henschel <markus.henschel@yager.de>
|
||||
* Ophir Lojkine <ophir.lojkine@eleves.ec-nantes.fr>
|
||||
* Ryan Sturgell <ryan.sturgell@gmail.com> (copyright owned by Google, Inc.)
|
||||
|
||||
|
|
95
emcc
95
emcc
|
@ -1098,12 +1098,24 @@ try:
|
|||
|
||||
# Find input files
|
||||
|
||||
# These three arrays are used to store arguments of different types for
|
||||
# type-specific processing. In order to shuffle the arguments back together
|
||||
# after processing, all of these arrays hold tuples (original_index, value).
|
||||
# Note that the index part of the tuple can have a fractional part for input
|
||||
# arguments that expand into multiple processed arguments, as in -Wl,-f1,-f2.
|
||||
input_files = []
|
||||
libs = []
|
||||
link_flags = []
|
||||
|
||||
# All of the above arg lists entries contain indexes into the full argument
|
||||
# list. In order to add extra implicit args (embind.cc, etc) below, we keep a
|
||||
# counter for the next index that should be used.
|
||||
next_arg_index = len(newargs)
|
||||
|
||||
has_source_inputs = False
|
||||
has_header_inputs = False
|
||||
lib_dirs = [shared.path_from_root('system', 'local', 'lib'),
|
||||
shared.path_from_root('system', 'lib')]
|
||||
libs = []
|
||||
for i in range(len(newargs)): # find input files XXX this a simple heuristic. we should really analyze based on a full understanding of gcc params,
|
||||
# right now we just assume that what is left contains no more |-x OPT| things
|
||||
arg = newargs[i]
|
||||
|
@ -1124,13 +1136,13 @@ try:
|
|||
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
|
||||
newargs[i] = ''
|
||||
if arg_ending.endswith(SOURCE_ENDINGS):
|
||||
input_files.append(arg)
|
||||
input_files.append((i, arg))
|
||||
has_source_inputs = True
|
||||
elif arg_ending.endswith(HEADER_ENDINGS):
|
||||
input_files.append(arg)
|
||||
input_files.append((i, arg))
|
||||
has_header_inputs = True
|
||||
elif arg_ending.endswith(ASSEMBLY_ENDINGS) or shared.Building.is_bitcode(arg): # this should be bitcode, make sure it is valid
|
||||
input_files.append(arg)
|
||||
input_files.append((i, arg))
|
||||
elif arg_ending.endswith(STATICLIB_ENDINGS + DYNAMICLIB_ENDINGS):
|
||||
# if it's not, and it's a library, just add it to libs to find later
|
||||
l = unsuffixed_basename(arg)
|
||||
|
@ -1139,7 +1151,7 @@ try:
|
|||
if l.startswith(prefix):
|
||||
l = l[len(prefix):]
|
||||
break
|
||||
libs.append(l)
|
||||
libs.append((i, l))
|
||||
newargs[i] = ''
|
||||
else:
|
||||
logging.warning(arg + ' is not valid LLVM bitcode')
|
||||
|
@ -1157,7 +1169,15 @@ try:
|
|||
lib_dirs.append(arg[2:])
|
||||
newargs[i] = ''
|
||||
elif arg.startswith('-l'):
|
||||
libs.append(arg[2:])
|
||||
libs.append((i, arg[2:]))
|
||||
newargs[i] = ''
|
||||
elif arg.startswith('-Wl,'):
|
||||
# Multiple comma separated link flags can be specified. Create fake
|
||||
# fractional indices for these: -Wl,a,b,c,d at index 4 becomes:
|
||||
# (4, a), (4.25, b), (4.5, c), (4.75, d)
|
||||
link_flags_to_add = arg.split(',')[1:]
|
||||
for flag_index, flag in enumerate(link_flags_to_add):
|
||||
link_flags.append((i + float(flag_index) / len(link_flags_to_add), flag))
|
||||
newargs[i] = ''
|
||||
|
||||
original_input_files = input_files[:]
|
||||
|
@ -1173,7 +1193,7 @@ try:
|
|||
final_ending = ('.' + final_suffix) if len(final_suffix) > 0 else ''
|
||||
|
||||
# Find library files
|
||||
for lib in libs:
|
||||
for i, lib in libs:
|
||||
logging.debug('looking for library "%s"', lib)
|
||||
found = False
|
||||
for prefix in LIB_PREFIXES:
|
||||
|
@ -1183,7 +1203,7 @@ try:
|
|||
path = os.path.join(lib_dir, name)
|
||||
if os.path.exists(path):
|
||||
logging.debug('found library "%s" at %s', lib, path)
|
||||
input_files.append(path)
|
||||
input_files.append((i, path))
|
||||
found = True
|
||||
break
|
||||
if found: break
|
||||
|
@ -1199,7 +1219,7 @@ try:
|
|||
return False
|
||||
else:
|
||||
return True
|
||||
input_files = [input_file for input_file in input_files if check(input_file)]
|
||||
input_files = [(i, input_file) for (i, input_file) in input_files if check(input_file)]
|
||||
|
||||
if len(input_files) == 0:
|
||||
logging.error('no input files\nnote that input files without a known suffix are ignored, make sure your input files end with one of: ' + str(SOURCE_ENDINGS + BITCODE_ENDINGS + DYNAMICLIB_ENDINGS + STATICLIB_ENDINGS + ASSEMBLY_ENDINGS + HEADER_ENDINGS))
|
||||
|
@ -1211,7 +1231,8 @@ try:
|
|||
|
||||
# If we are using embind and generating JS, now is the time to link in bind.cpp
|
||||
if bind and final_suffix in JS_CONTAINING_SUFFIXES:
|
||||
input_files.append(shared.path_from_root('system', 'lib', 'embind', 'bind.cpp'))
|
||||
input_files.append((next_arg_index, shared.path_from_root('system', 'lib', 'embind', 'bind.cpp')))
|
||||
next_arg_index += 1
|
||||
|
||||
# Apply optimization level settings
|
||||
shared.Settings.apply_opt_level(opt_level, noisy=True)
|
||||
|
@ -1331,7 +1352,8 @@ try:
|
|||
logging.warning('ALIASING_FUNCTION_POINTERS is on, function pointer comparisons may be invalid across types')
|
||||
|
||||
if shared.Settings.STB_IMAGE and final_suffix in JS_CONTAINING_SUFFIXES:
|
||||
input_files.append(shared.path_from_root('third_party', 'stb_image.c'))
|
||||
input_files.append((next_arg_index, shared.path_from_root('third_party', 'stb_image.c')))
|
||||
next_arg_index += 1
|
||||
shared.Settings.EXPORTED_FUNCTIONS += ['_stbi_load', '_stbi_load_from_memory', '_stbi_image_free']
|
||||
|
||||
if type(shared.Settings.EXPORTED_FUNCTIONS) in (list, tuple):
|
||||
|
@ -1365,9 +1387,10 @@ try:
|
|||
|
||||
# Precompiled headers support
|
||||
if has_header_inputs:
|
||||
for header in input_files:
|
||||
assert header.endswith(HEADER_ENDINGS), 'if you have one header input, we assume you want to precompile headers, and cannot have source files or other inputs as well: ' + str(input_files) + ' : ' + header
|
||||
args = newargs + shared.EMSDK_CXX_OPTS + input_files
|
||||
headers = [header for _, header in input_files]
|
||||
for header in headers:
|
||||
assert header.endswith(HEADER_ENDINGS), 'if you have one header input, we assume you want to precompile headers, and cannot have source files or other inputs as well: ' + str(headers) + ' : ' + header
|
||||
args = newargs + shared.EMSDK_CXX_OPTS + headers
|
||||
if specified_target:
|
||||
args += ['-o', specified_target]
|
||||
logging.debug("running (for precompiled headers): " + call + ' ' + ' '.join(args))
|
||||
|
@ -1388,12 +1411,12 @@ try:
|
|||
return in_temp(unsuffixed(uniquename(input_file)) + default_object_extension)
|
||||
|
||||
# First, generate LLVM bitcode. For each input file, we get base.o with bitcode
|
||||
for input_file in input_files:
|
||||
for i, input_file in input_files:
|
||||
file_ending = filename_type_ending(input_file)
|
||||
if file_ending.endswith(SOURCE_ENDINGS):
|
||||
logging.debug('compiling source file: ' + input_file)
|
||||
output_file = get_bitcode_file(input_file)
|
||||
temp_files.append(output_file)
|
||||
temp_files.append((i, output_file))
|
||||
args = newargs + ['-emit-llvm', '-c', input_file, '-o', output_file]
|
||||
if file_ending.endswith(CXX_ENDINGS):
|
||||
args += shared.EMSDK_CXX_OPTS
|
||||
|
@ -1407,18 +1430,18 @@ try:
|
|||
logging.debug('copying bitcode file: ' + input_file)
|
||||
temp_file = in_temp(unsuffixed(uniquename(input_file)) + '.o')
|
||||
shutil.copyfile(input_file, temp_file)
|
||||
temp_files.append(temp_file)
|
||||
temp_files.append((i, temp_file))
|
||||
elif file_ending.endswith(DYNAMICLIB_ENDINGS) or shared.Building.is_ar(input_file):
|
||||
logging.debug('copying library file: ' + input_file)
|
||||
temp_file = in_temp(uniquename(input_file))
|
||||
shutil.copyfile(input_file, temp_file)
|
||||
temp_files.append(temp_file)
|
||||
temp_files.append((i, temp_file))
|
||||
elif file_ending.endswith(ASSEMBLY_ENDINGS):
|
||||
if not LEAVE_INPUTS_RAW:
|
||||
logging.debug('assembling assembly file: ' + input_file)
|
||||
temp_file = in_temp(unsuffixed(uniquename(input_file)) + '.o')
|
||||
shared.Building.llvm_as(input_file, temp_file)
|
||||
temp_files.append(temp_file)
|
||||
temp_files.append((i, temp_file))
|
||||
else:
|
||||
logging.error(input_file + ': Unknown file suffix when compiling to LLVM bitcode!')
|
||||
sys.exit(1)
|
||||
|
@ -1430,10 +1453,10 @@ try:
|
|||
|
||||
# Optimize source files
|
||||
if llvm_opts > 0:
|
||||
for i, input_file in enumerate(input_files):
|
||||
for pos, (_, input_file) in enumerate(input_files):
|
||||
file_ending = filename_type_ending(input_file)
|
||||
if file_ending.endswith(SOURCE_ENDINGS):
|
||||
temp_file = temp_files[i]
|
||||
(_, temp_file) = temp_files[pos]
|
||||
logging.debug('optimizing %s', input_file)
|
||||
#if DEBUG: shutil.copyfile(temp_file, os.path.join(TEMP_DIR, 'to_opt.bc') # useful when LLVM opt aborts
|
||||
shared.Building.llvm_opt(temp_file, llvm_opts)
|
||||
|
@ -1441,26 +1464,30 @@ try:
|
|||
# If we were just asked to generate bitcode, stop there
|
||||
if final_suffix not in JS_CONTAINING_SUFFIXES:
|
||||
if not specified_target:
|
||||
for input_file in input_files:
|
||||
for _, input_file in input_files:
|
||||
safe_move(get_bitcode_file(input_file), unsuffixed_basename(input_file) + final_ending)
|
||||
else:
|
||||
if len(input_files) == 1:
|
||||
safe_move(temp_files[0], specified_target if specified_target else unsuffixed_basename(input_file) + final_ending)
|
||||
temp_output_base = unsuffixed(temp_files[0])
|
||||
_, input_file = input_files[0]
|
||||
_, temp_file = temp_files[0]
|
||||
safe_move(temp_file, specified_target if specified_target else unsuffixed_basename(input_file) + final_ending)
|
||||
temp_output_base = unsuffixed(temp_file)
|
||||
if os.path.exists(temp_output_base + '.d'):
|
||||
# There was a .d file generated, from -MD or -MMD and friends, save a copy of it to where the output resides,
|
||||
# adjusting the target name away from the temporary file name to the specified target.
|
||||
# It will be deleted with the rest of the temporary directory.
|
||||
deps = open(temp_output_base + '.d').read()
|
||||
deps = deps.replace(temp_output_base + default_object_extension, specified_target)
|
||||
with open(os.path.join(os.path.dirname(specified_target), os.path.basename(unsuffixed(input_files[0]) + '.d')), "w") as out_dep:
|
||||
with open(os.path.join(os.path.dirname(specified_target), os.path.basename(unsuffixed(input_file) + '.d')), "w") as out_dep:
|
||||
out_dep.write(deps)
|
||||
else:
|
||||
assert len(original_input_files) == 1 or not has_dash_c, 'fatal error: cannot specify -o with -c with multiple files' + str(sys.argv) + ':' + str(original_input_files)
|
||||
# We have a specified target (-o <target>), which is not JavaScript or HTML, and
|
||||
# we have multiple files: Link them
|
||||
logging.debug('link: ' + str(temp_files) + specified_target)
|
||||
shared.Building.link(temp_files, specified_target)
|
||||
# Sort arg tuples and pass the extracted values to link.
|
||||
link_args = [f for (i, f) in sorted(temp_files)]
|
||||
shared.Building.link(link_args, specified_target)
|
||||
logging.debug('stopping at bitcode')
|
||||
exit(0)
|
||||
|
||||
|
@ -1473,7 +1500,7 @@ try:
|
|||
if not LEAVE_INPUTS_RAW and \
|
||||
not shared.Settings.BUILD_AS_SHARED_LIB and \
|
||||
not shared.Settings.SIDE_MODULE: # shared libraries/side modules link no C libraries, need them in parent
|
||||
extra_files_to_link = system_libs.calculate(temp_files, in_temp, stdout, stderr)
|
||||
extra_files_to_link = system_libs.calculate([f for _, f in sorted(temp_files)], in_temp, stdout, stderr)
|
||||
else:
|
||||
extra_files_to_link = []
|
||||
|
||||
|
@ -1481,18 +1508,20 @@ try:
|
|||
|
||||
# First, combine the bitcode files if there are several. We must also link if we have a singleton .a
|
||||
if len(input_files) + len(extra_files_to_link) > 1 or \
|
||||
(not LEAVE_INPUTS_RAW and not (suffix(temp_files[0]) in BITCODE_ENDINGS or suffix(temp_files[0]) in DYNAMICLIB_ENDINGS) and shared.Building.is_ar(temp_files[0])):
|
||||
linker_inputs = temp_files + extra_files_to_link
|
||||
(not LEAVE_INPUTS_RAW and not (suffix(temp_files[0][1]) in BITCODE_ENDINGS or suffix(temp_files[0][1]) in DYNAMICLIB_ENDINGS) and shared.Building.is_ar(temp_files[0][1])):
|
||||
linker_inputs = [val for _, val in sorted(temp_files + link_flags)] + extra_files_to_link
|
||||
logging.debug('linking: ' + str(linker_inputs))
|
||||
shared.Building.link(linker_inputs, in_temp(target_basename + '.bc'), force_archive_contents=len([temp for temp in temp_files if not temp.endswith(STATICLIB_ENDINGS)]) == 0)
|
||||
shared.Building.link(linker_inputs, in_temp(target_basename + '.bc'), force_archive_contents=len([temp for i, temp in temp_files if not temp.endswith(STATICLIB_ENDINGS)]) == 0)
|
||||
final = in_temp(target_basename + '.bc')
|
||||
else:
|
||||
if not LEAVE_INPUTS_RAW:
|
||||
shutil.move(temp_files[0], in_temp(target_basename + '.bc'))
|
||||
_, temp_file = temp_files[0]
|
||||
shutil.move(temp_file, in_temp(target_basename + '.bc'))
|
||||
final = in_temp(target_basename + '.bc')
|
||||
else:
|
||||
final = in_temp(input_files[0])
|
||||
shutil.copyfile(input_files[0], final)
|
||||
_, input_file = input_files[0]
|
||||
final = in_temp(input_file)
|
||||
shutil.copyfile(input_file, final)
|
||||
|
||||
log_time('link')
|
||||
|
||||
|
|
|
@ -1027,10 +1027,85 @@ This pointer might make sense in another type signature: i: 0
|
|||
Building.emar('cr', lib_name, [a_name + '.o', b_name + '.o']) # libLIB.a with a and b
|
||||
|
||||
# a is in the lib AND in an .o, so should be ignored in the lib. We do still need b from the lib though
|
||||
Building.emcc(main_name, ['-L.', '-lLIB', a_name+'.o', c_name + '.o'], output_filename='a.out.js')
|
||||
Building.emcc(main_name, [a_name+'.o', c_name + '.o', '-L.', '-lLIB'], output_filename='a.out.js')
|
||||
|
||||
self.assertContained('result: 62', run_js(os.path.join(self.get_dir(), 'a.out.js')))
|
||||
|
||||
def test_link_group_asserts(self):
|
||||
lib_src_name = os.path.join(self.get_dir(), 'lib.c')
|
||||
open(lib_src_name, 'w').write('int x() { return 42; }')
|
||||
|
||||
main_name = os.path.join(self.get_dir(), 'main.c')
|
||||
open(main_name, 'w').write(r'''
|
||||
#include <stdio.h>
|
||||
int x();
|
||||
int main() {
|
||||
printf("result: %d\n", x());
|
||||
return 0;
|
||||
}
|
||||
''')
|
||||
|
||||
Building.emcc(lib_src_name) # lib.c.o
|
||||
lib_name = os.path.join(self.get_dir(), 'libLIB.a')
|
||||
Building.emar('cr', lib_name, [lib_src_name + '.o']) # libLIB.a with lib.c.o
|
||||
|
||||
def test(lib_args, err_expected):
|
||||
output = Popen([PYTHON, EMCC, main_name, '-o', 'a.out.js'] + lib_args, stdout=PIPE, stderr=PIPE).communicate()
|
||||
if err_expected:
|
||||
self.assertContained(err_expected, output[1])
|
||||
else:
|
||||
out_js = os.path.join(self.get_dir(), 'a.out.js')
|
||||
assert os.path.exists(out_js), '\n'.join(output)
|
||||
self.assertContained('result: 42', run_js(out_js))
|
||||
|
||||
test(['-Wl,--start-group', lib_name], '--start-group without matching --end-group')
|
||||
test(['-Wl,--start-group', lib_name, '-Wl,--start-group'], 'Nested --start-group, missing --end-group?')
|
||||
test(['-Wl,--end-group', lib_name, '-Wl,--start-group'], '--end-group without --start-group')
|
||||
test(['-Wl,--start-group', lib_name, '-Wl,--end-group'], None)
|
||||
|
||||
def test_circular_libs(self):
|
||||
def tmp_source(name, code):
|
||||
file_name = os.path.join(self.get_dir(), name)
|
||||
open(file_name, 'w').write(code)
|
||||
return file_name
|
||||
|
||||
a = tmp_source('a.c', 'int z(); int x() { return z(); }')
|
||||
b = tmp_source('b.c', 'int x(); int y() { return x(); } int z() { return 42; }')
|
||||
c = tmp_source('c.c', 'int q() { return 0; }')
|
||||
main = tmp_source('main.c', r'''
|
||||
#include <stdio.h>
|
||||
int y();
|
||||
int main() {
|
||||
printf("result: %d\n", y());
|
||||
return 0;
|
||||
}
|
||||
''')
|
||||
|
||||
Building.emcc(a) # a.c.o
|
||||
Building.emcc(b) # b.c.o
|
||||
Building.emcc(c) # c.c.o
|
||||
lib_a = os.path.join(self.get_dir(), 'libA.a')
|
||||
Building.emar('cr', lib_a, [a + '.o', c + '.o']) # libA.a with a.c.o,c.c.o
|
||||
lib_b = os.path.join(self.get_dir(), 'libB.a')
|
||||
Building.emar('cr', lib_b, [b + '.o', c + '.o']) # libB.a with b.c.o,c.c.o
|
||||
|
||||
args = ['-s', 'ERROR_ON_UNDEFINED_SYMBOLS=1', main, '-o', 'a.out.js']
|
||||
libs = [lib_a, lib_b]
|
||||
|
||||
# lib_a does not satisfy any symbols from main, so it will not be included,
|
||||
# and there will be an unresolved symbol.
|
||||
output = Popen([PYTHON, EMCC] + args + libs, stdout=PIPE, stderr=PIPE).communicate()
|
||||
self.assertContained('error: unresolved symbol: x', output[1])
|
||||
|
||||
# -Wl,--start-group and -Wl,--end-group around the libs will cause a rescan
|
||||
# of lib_a after lib_b adds undefined symbol "x", so a.c.o will now be
|
||||
# included (and the link will succeed).
|
||||
libs = ['-Wl,--start-group'] + libs + ['-Wl,--end-group']
|
||||
output = Popen([PYTHON, EMCC] + args + libs, stdout=PIPE, stderr=PIPE).communicate()
|
||||
out_js = os.path.join(self.get_dir(), 'a.out.js')
|
||||
assert os.path.exists(out_js), '\n'.join(output)
|
||||
self.assertContained('result: 42', run_js(out_js))
|
||||
|
||||
def test_redundant_link(self):
|
||||
lib = "int mult() { return 1; }"
|
||||
lib_name = os.path.join(self.get_dir(), 'libA.c')
|
||||
|
|
166
tools/shared.py
166
tools/shared.py
|
@ -1144,63 +1144,131 @@ class Building:
|
|||
unresolved_symbols = set([func[1:] for func in Settings.EXPORTED_FUNCTIONS])
|
||||
resolved_symbols = set()
|
||||
temp_dirs = []
|
||||
files = map(os.path.abspath, files)
|
||||
def make_paths_absolute(f):
|
||||
if f.startswith('-'): # skip flags
|
||||
return f
|
||||
else:
|
||||
return os.path.abspath(f)
|
||||
files = map(make_paths_absolute, files)
|
||||
# Paths of already included object files from archives.
|
||||
added_contents = set()
|
||||
# Map of archive name to list of extracted object file paths.
|
||||
ar_contents = {}
|
||||
has_ar = False
|
||||
for f in files:
|
||||
has_ar = has_ar or Building.is_ar(f)
|
||||
if not f.startswith('-'):
|
||||
has_ar = has_ar or Building.is_ar(f)
|
||||
|
||||
# If we have only one archive or the force_archive_contents flag is set,
|
||||
# then we will add every object file we see, regardless of whether it
|
||||
# resolves any undefined symbols.
|
||||
force_add_all = len(files) == 1 or force_archive_contents
|
||||
|
||||
# Considers an object file for inclusion in the link. The object is included
|
||||
# if force_add=True or if the object provides a currently undefined symbol.
|
||||
# If the object is included, the symbol tables are updated and the function
|
||||
# returns True.
|
||||
def consider_object(f, force_add=False):
|
||||
new_symbols = Building.llvm_nm(f)
|
||||
do_add = force_add or not unresolved_symbols.isdisjoint(new_symbols.defs)
|
||||
if do_add:
|
||||
logging.debug('adding object %s to link' % (f))
|
||||
# Update resolved_symbols table with newly resolved symbols
|
||||
resolved_symbols.update(new_symbols.defs)
|
||||
# Update unresolved_symbols table by adding newly unresolved symbols and
|
||||
# removing newly resolved symbols.
|
||||
unresolved_symbols.update(new_symbols.undefs.difference(resolved_symbols))
|
||||
unresolved_symbols.difference_update(new_symbols.defs)
|
||||
actual_files.append(f)
|
||||
return do_add
|
||||
|
||||
def get_archive_contents(f):
|
||||
if f in ar_contents:
|
||||
return ar_contents[f]
|
||||
|
||||
cwd = os.getcwd()
|
||||
try:
|
||||
temp_dir = os.path.join(EMSCRIPTEN_TEMP_DIR, 'ar_output_' + str(os.getpid()) + '_' + str(len(temp_dirs)))
|
||||
temp_dirs.append(temp_dir)
|
||||
safe_ensure_dirs(temp_dir)
|
||||
os.chdir(temp_dir)
|
||||
contents = filter(lambda x: len(x) > 0, Popen([LLVM_AR, 't', f], stdout=PIPE).communicate()[0].split('\n'))
|
||||
if len(contents) == 0:
|
||||
logging.debug('Archive %s appears to be empty (recommendation: link an .so instead of .a)' % f)
|
||||
else:
|
||||
for content in contents: # ar will silently fail if the directory for the file does not exist, so make all the necessary directories
|
||||
dirname = os.path.dirname(content)
|
||||
if dirname:
|
||||
safe_ensure_dirs(dirname)
|
||||
Popen([LLVM_AR, 'xo', f], stdout=PIPE).communicate() # if absolute paths, files will appear there. otherwise, in this directory
|
||||
contents = map(lambda content: os.path.join(temp_dir, content), contents)
|
||||
contents = filter(os.path.exists, map(os.path.abspath, contents))
|
||||
contents = filter(Building.is_bitcode, contents)
|
||||
ar_contents[f] = contents
|
||||
finally:
|
||||
os.chdir(cwd)
|
||||
|
||||
return contents
|
||||
|
||||
# Traverse a single archive. The object files are repeatedly scanned for
|
||||
# newly satisfied symbols until no new symbols are found. Returns true if
|
||||
# any object files were added to the link.
|
||||
def consider_archive(f):
|
||||
added_any_objects = False
|
||||
loop_again = True
|
||||
logging.debug('considering archive %s' % (f))
|
||||
contents = get_archive_contents(f)
|
||||
while loop_again: # repeatedly traverse until we have everything we need
|
||||
loop_again = False
|
||||
for content in contents:
|
||||
if content in added_contents: continue
|
||||
# Link in the .o if it provides symbols, *or* this is a singleton archive (which is apparently an exception in gcc ld)
|
||||
if consider_object(content, force_add=force_add_all):
|
||||
added_contents.add(content)
|
||||
loop_again = True
|
||||
added_any_objects = True
|
||||
logging.debug('done running loop of archive %s' % (f))
|
||||
return added_any_objects
|
||||
|
||||
current_archive_group = None
|
||||
for f in files:
|
||||
if not Building.is_ar(f):
|
||||
if f.startswith('-'):
|
||||
if f in ['--start-group', '-(']:
|
||||
assert current_archive_group is None, 'Nested --start-group, missing --end-group?'
|
||||
current_archive_group = []
|
||||
elif f in ['--end-group', '-)']:
|
||||
assert current_archive_group is not None, '--end-group without --start-group'
|
||||
# rescan the archives in the group until we don't find any more
|
||||
# objects to link.
|
||||
loop_again = True
|
||||
logging.debug('starting archive group loop');
|
||||
while loop_again:
|
||||
loop_again = False
|
||||
for archive in current_archive_group:
|
||||
if consider_archive(archive):
|
||||
loop_again = True
|
||||
logging.debug('done with archive group loop');
|
||||
current_archive_group = None
|
||||
else:
|
||||
logging.debug('Ignoring unsupported link flag: %s' % f)
|
||||
elif not Building.is_ar(f):
|
||||
if Building.is_bitcode(f):
|
||||
if has_ar:
|
||||
new_symbols = Building.llvm_nm(f)
|
||||
resolved_symbols = resolved_symbols.union(new_symbols.defs)
|
||||
unresolved_symbols = unresolved_symbols.union(new_symbols.undefs.difference(resolved_symbols)).difference(new_symbols.defs)
|
||||
actual_files.append(f)
|
||||
consider_object(f, force_add=True)
|
||||
else:
|
||||
# If there are no archives then we can simply link all valid bitcode
|
||||
# files and skip the symbol table stuff.
|
||||
actual_files.append(f)
|
||||
else:
|
||||
# Extract object files from ar archives, and link according to gnu ld semantics
|
||||
# (link in an entire .o from the archive if it supplies symbols still unresolved)
|
||||
cwd = os.getcwd()
|
||||
try:
|
||||
temp_dir = os.path.join(EMSCRIPTEN_TEMP_DIR, 'ar_output_' + str(os.getpid()) + '_' + str(len(temp_dirs)))
|
||||
temp_dirs.append(temp_dir)
|
||||
safe_ensure_dirs(temp_dir)
|
||||
os.chdir(temp_dir)
|
||||
contents = filter(lambda x: len(x) > 0, Popen([LLVM_AR, 't', f], stdout=PIPE).communicate()[0].split('\n'))
|
||||
#print >> sys.stderr, ' considering archive', f, ':', contents
|
||||
if len(contents) == 0:
|
||||
logging.debug('Archive %s appears to be empty (recommendation: link an .so instead of .a)' % f)
|
||||
else:
|
||||
for content in contents: # ar will silently fail if the directory for the file does not exist, so make all the necessary directories
|
||||
dirname = os.path.dirname(content)
|
||||
if dirname:
|
||||
safe_ensure_dirs(dirname)
|
||||
Popen([LLVM_AR, 'xo', f], stdout=PIPE).communicate() # if absolute paths, files will appear there. otherwise, in this directory
|
||||
contents = map(lambda content: os.path.join(temp_dir, content), contents)
|
||||
contents = filter(os.path.exists, map(os.path.abspath, contents))
|
||||
added_contents = set()
|
||||
added = True
|
||||
#print >> sys.stderr, ' initial undef are now ', unresolved_symbols, '\n'
|
||||
while added: # recursively traverse until we have everything we need
|
||||
#print >> sys.stderr, ' running loop of archive including for', f
|
||||
added = False
|
||||
for content in contents:
|
||||
if content in added_contents: continue
|
||||
new_symbols = Building.llvm_nm(content)
|
||||
# Link in the .o if it provides symbols, *or* this is a singleton archive (which is apparently an exception in gcc ld)
|
||||
#print >> sys.stderr, 'need', content, '?', unresolved_symbols, 'and we can supply', new_symbols.defs
|
||||
#print >> sys.stderr, content, 'DEF', new_symbols.defs, '\n'
|
||||
if new_symbols.defs.intersection(unresolved_symbols) or len(files) == 1 or force_archive_contents:
|
||||
if Building.is_bitcode(content):
|
||||
#print >> sys.stderr, ' adding object', content, '\n'
|
||||
resolved_symbols = resolved_symbols.union(new_symbols.defs)
|
||||
unresolved_symbols = unresolved_symbols.union(new_symbols.undefs.difference(resolved_symbols)).difference(new_symbols.defs)
|
||||
#print >> sys.stderr, ' undef are now ', unresolved_symbols, '\n'
|
||||
actual_files.append(content)
|
||||
added_contents.add(content)
|
||||
added = True
|
||||
#print >> sys.stderr, ' done running loop of archive including for', f
|
||||
finally:
|
||||
os.chdir(cwd)
|
||||
consider_archive(f)
|
||||
# If we're inside a --start-group/--end-group section, add to the list
|
||||
# so we can loop back around later.
|
||||
if current_archive_group is not None:
|
||||
current_archive_group.append(f)
|
||||
assert current_archive_group is None, '--start-group without matching --end-group'
|
||||
|
||||
try_delete(target)
|
||||
|
||||
# Finish link
|
||||
|
|
Загрузка…
Ссылка в новой задаче