aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xemcc40
-rwxr-xr-xtests/runner.py64
-rw-r--r--tools/shared.py30
3 files changed, 103 insertions, 31 deletions
diff --git a/emcc b/emcc
index 0bf4fd1f..f19e614e 100755
--- a/emcc
+++ b/emcc
@@ -542,6 +542,15 @@ for i in range(len(sys.argv)-1):
sys.argv = sys.argv[:i] + sys.argv[i+2:]
break
+specified_target = target
+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
+target_basename = unsuffixed_basename(target)
+
+if '.' in target:
+ final_suffix = target.split('.')[-1]
+else:
+ final_suffix = ''
+
if header: # header or such
if len(sys.argv) >= 3: # if there is a source and a target, then copy, otherwise do nothing
sys.argv = filter(lambda arg: not arg.startswith('-I'), sys.argv)
@@ -753,7 +762,7 @@ try:
if i > 0:
prev = newargs[i-1]
- if prev in ['-MT', '-install_name']: continue # ignore this gcc-style argument
+ if prev in ['-MT', '-install_name', '-I', '-L']: continue # ignore this gcc-style argument
if not arg.startswith('-') and (arg.endswith(SOURCE_SUFFIXES + BITCODE_SUFFIXES + DYNAMICLIB_SUFFIXES + ASSEMBLY_SUFFIXES) or shared.Building.is_ar(arg)): # we already removed -o <target>, so all these should be inputs
newargs[i] = ''
@@ -791,6 +800,17 @@ try:
newargs = [ arg for arg in newargs if arg is not '' ]
+ # -c means do not link in gcc, and for us, the parallel is to not go all the way to JS, but stop at bitcode
+ has_dash_c = '-c' in newargs
+ if has_dash_c:
+ assert has_source_inputs, 'Must have source code inputs to use -c'
+ target = target_basename + '.o'
+ final_suffix = 'o'
+
+ # do not link in libs when just generating object code (not an 'executable', i.e. JS, or a library)
+ if ('.' + final_suffix) in BITCODE_SUFFIXES:
+ libs = []
+
# Find library files
for lib in libs:
if DEBUG: print >> sys.stderr, 'emcc: looking for library "%s"' % lib
@@ -818,22 +838,6 @@ try:
newargs += CC_ADDITIONAL_ARGS
- specified_target = target
- 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
-
- target_basename = unsuffixed_basename(target)
-
- # -c means do not link in gcc, and for us, the parallel is to not go all the way to JS, but stop at bitcode
- has_dash_c = '-c' in newargs
- if has_dash_c:
- assert has_source_inputs, 'Must have source code inputs to use -c'
- target = target_basename + '.o'
-
- if '.' in target:
- final_suffix = target.split('.')[-1]
- else:
- final_suffix = ''
-
assert not (Compression.on and final_suffix != 'html'), 'Compression only works when generating HTML'
# If we are using embind and generating JS, now is the time to link in bind.cpp
@@ -906,7 +910,7 @@ try:
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
- if DEBUG: print >> sys.stderr, 'emcc: link: ' + str(temp_files)
+ if DEBUG: print >> sys.stderr, 'emcc: link: ' + str(temp_files), specified_target
shared.Building.link(temp_files, specified_target, remove_duplicates=remove_duplicates)
exit(0)
diff --git a/tests/runner.py b/tests/runner.py
index cef14e95..3dfef3e9 100755
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -7850,6 +7850,70 @@ f.close()
self.assertContained('hello from lib', run_js(os.path.join(self.get_dir(), 'a.out.js')))
assert not os.path.exists('a.out') and not os.path.exists('a.exe'), 'Must not leave unneeded linker stubs'
+ def test_multiply_defined_libsymbols(self):
+ lib = "int mult() { return 1; }"
+ lib_name = os.path.join(self.get_dir(), 'libA.c')
+ open(lib_name, 'w').write(lib)
+ a2 = "void x() {}"
+ a2_name = os.path.join(self.get_dir(), 'a2.c')
+ open(a2_name, 'w').write(a2)
+ b2 = "void y() {}"
+ b2_name = os.path.join(self.get_dir(), 'b2.c')
+ open(b2_name, 'w').write(b2)
+ main = r'''
+ #include <stdio.h>
+ int mult();
+ int main() {
+ printf("result: %d\n", mult());
+ return 0;
+ }
+ '''
+ main_name = os.path.join(self.get_dir(), 'main.c')
+ open(main_name, 'w').write(main)
+
+ Building.emcc(lib_name, output_filename='libA.so')
+
+ Building.emcc(a2_name, ['-L.', '-lA'])
+ Building.emcc(b2_name, ['-L.', '-lA'])
+
+ Building.emcc(main_name, ['-L.', '-lA', a2_name+'.o', b2_name+'.o'], output_filename='a.out.js')
+
+ self.assertContained('result: 1', run_js(os.path.join(self.get_dir(), 'a.out.js')))
+
+ def test_multiply_defined_libsymbols_2(self):
+ a = "int x() { return 55; }"
+ a_name = os.path.join(self.get_dir(), 'a.c')
+ open(a_name, 'w').write(a)
+ b = "int y() { return 2; }"
+ b_name = os.path.join(self.get_dir(), 'b.c')
+ open(b_name, 'w').write(b)
+ c = "int z() { return 5; }"
+ c_name = os.path.join(self.get_dir(), 'c.c')
+ open(c_name, 'w').write(c)
+ main = r'''
+ #include <stdio.h>
+ int x();
+ int y();
+ int z();
+ int main() {
+ printf("result: %d\n", x() + y() + z());
+ return 0;
+ }
+ '''
+ main_name = os.path.join(self.get_dir(), 'main.c')
+ open(main_name, 'w').write(main)
+
+ Building.emcc(a_name) # a.c.o
+ Building.emcc(b_name) # b.c.o
+ Building.emcc(c_name) # c.c.o
+ lib_name = os.path.join(self.get_dir(), 'libLIB.a')
+ 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')
+
+ self.assertContained('result: 62', run_js(os.path.join(self.get_dir(), 'a.out.js')))
+
def test_abspaths(self):
# Includes with absolute paths are generally dangerous, things like -I/usr/.. will get to system local headers, not our portable ones.
diff --git a/tools/shared.py b/tools/shared.py
index e292dbf3..1f9c8fe5 100644
--- a/tools/shared.py
+++ b/tools/shared.py
@@ -731,6 +731,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
os.makedirs(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:
print >> sys.stderr, 'Warning: Archive %s appears to be empty (recommendation: link an .so instead of .a)' % f
else:
@@ -741,20 +742,23 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
Popen([LLVM_AR, 'x', 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))
- needed = False # We add or do not add the entire archive. We let llvm dead code eliminate parts we do not need, instead of
- # doing intra-dependencies between archive contents
- for content in contents:
- 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)
- if new_symbols.defs.intersection(unresolved_symbols) or len(files) == 1:
- needed = True
- if needed:
+ added_contents = set()
+ added = True
+ while added: # recursively traverse until we have everything we need
+ added = False
for content in contents:
- if Building.is_bitcode(content):
- new_symbols = Building.llvm_nm(content)
- 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(content)
+ 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
+ if new_symbols.defs.intersection(unresolved_symbols) or len(files) == 1:
+ if Building.is_bitcode(content):
+ #print >> sys.stderr, ' adding object', content
+ 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(content)
+ added_contents.add(content)
+ added = True
finally:
os.chdir(cwd)
try_delete(target)