Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve emar wrapper script #8101

Merged
merged 2 commits into from Feb 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .flake8
Expand Up @@ -3,6 +3,7 @@ ignore = E111,E114,E501,E261,E266,E121,E402,E241
# TODO(sbc): Switch from the whitelist of a blacklist such as:
# exclude = ./third_party/,emmaken.py,./tools/scons/,./tools/ports/,./tests/
filename =
*/emar.py
*/emcc.py
*/emscons.py
*/emscripten.py
Expand Down
108 changes: 60 additions & 48 deletions emar.py
Expand Up @@ -4,80 +4,92 @@
# University of Illinois/NCSA Open Source License. Both these licenses can be
# found in the LICENSE file.

'''
emar - ar helper script
=======================
"""Archive helper script

This script acts as a frontend replacement for ar. See emcc.
'''
This script acts as a frontend replacement for `ar`. See emcc.
This is needed because, unlike a traditional linker, emscripten can't handle
archive with duplicate member names. This is because emscripten extracts
archive to a temporary location and duplicate filenames will clobber each
other in this case.

"""

# TODO(sbc): Implement `ar x` within emscripten, in python, to avoid this issue
# and delete this file.

from __future__ import print_function
import hashlib
import os
import shutil
import sys

from tools.toolchain_profiler import ToolchainProfiler
from tools import shared
from tools.response_file import substitute_response_files, create_response_file

if __name__ == '__main__':
ToolchainProfiler.record_process_start()

import os, sys
from tools import shared
from tools.response_file import substitute_response_files, create_response_file

#
# Main run() function
#
def run():
DEBUG = os.environ.get('EMCC_DEBUG')
if DEBUG == "0":
DEBUG = None

sys.argv = substitute_response_files(sys.argv)

newargs = [shared.LLVM_AR] + sys.argv[1:]

if DEBUG:
print('emar:', sys.argv, ' ==> ', newargs, file=sys.stderr)
args = substitute_response_files(sys.argv)
newargs = [shared.LLVM_AR] + args[1:]

to_delete = []
if len(newargs) > 2:
if 'r' in newargs[1]:

# The 3 argment form of ar doesn't involve other files. For example
# 'ar x libfoo.a'.
if len(newargs) > 3:
cmd = newargs[1]
if 'r' in cmd:
# we are adding files to the archive.
# find the .a; everything after it is an input file.
# normally the output file is then arg 2, except in the case were the
# a or b modifiers are used in which case its arg 3.
if 'a' in cmd or 'b' in cmd:
new_member_args_start = 4
else:
new_member_args_start = 3

# we add a hash to each input, to make them unique as
# possible, as llvm-ar cannot extract duplicate names
# (and only the basename is used!)
i = 1
while i < len(newargs):
if newargs[i].endswith('.a'):
import hashlib, shutil
for j in range(i+1, len(newargs)):
orig_name = newargs[j]
full_name = os.path.abspath(orig_name)
dir_name = os.path.dirname(full_name)
base_name = os.path.basename(full_name)
h = hashlib.md5(full_name.encode('utf-8')).hexdigest()[:8]
parts = base_name.split('.')
parts[0] += '_' + h
newname = '.'.join(parts)
full_newname = os.path.join(dir_name, newname)
if not os.path.exists(full_newname):
try: # it is ok to fail here, we just don't get hashing
shutil.copyfile(orig_name, full_newname)
newargs[j] = full_newname
to_delete.append(full_newname)
except:
pass
break
i += 1

response_filename = None
if len(newargs) > 3:
for j in range(new_member_args_start, len(newargs)):
orig_name = newargs[j]
full_name = os.path.abspath(orig_name)
dir_name = os.path.dirname(full_name)
base_name = os.path.basename(full_name)
h = hashlib.md5(full_name.encode('utf-8')).hexdigest()[:8]
parts = base_name.split('.')
parts[0] += '_' + h
newname = '.'.join(parts)
full_newname = os.path.join(dir_name, newname)
if not os.path.exists(full_newname):
try: # it is ok to fail here, we just don't get hashing
shutil.copyfile(orig_name, full_newname)
newargs[j] = full_newname
to_delete.append(full_newname)
except:
pass

if shared.DEBUG:
print('emar:', sys.argv, ' ==> ', newargs, file=sys.stderr)

response_filename = create_response_file(newargs[3:], shared.get_emscripten_temp_dir())
to_delete += [response_filename]
newargs = newargs[:3] + ['@' + response_filename]

if shared.DEBUG:
print('emar:', sys.argv, ' ==> ', newargs, file=sys.stderr)

try:
return shared.run_process(newargs, stdin=sys.stdin).returncode
return shared.run_process(newargs, stdin=sys.stdin, check=False).returncode
finally:
for d in to_delete:
shared.try_delete(d)


if __name__ == '__main__':
sys.exit(run())
33 changes: 17 additions & 16 deletions tests/test_other.py
Expand Up @@ -5803,37 +5803,38 @@ def test_call_nonemterpreted_during_sleep(self):
self.assertContained('cannot have an EM_ASM on the stack when emterpreter pauses/resumes', run_js('a.out.js', stderr=STDOUT, assert_returncode=None))

def test_link_with_a_static(self):
for args in [[], ['-O2']]:
print(args)
self.clear()
create_test_file('x.c', r'''
create_test_file('x.c', r'''
int init_weakref(int a, int b) {
return a + b;
return a + b;
}
''')
create_test_file('y.c', r'''
create_test_file('y.c', r'''
static int init_weakref(void) { // inlined in -O2, not in -O0 where it shows up in llvm-nm as 't'
return 150;
return 150;
}

int testy(void) {
return init_weakref();
return init_weakref();
}
''')
create_test_file('z.c', r'''
create_test_file('z.c', r'''
extern int init_weakref(int, int);
extern int testy(void);

int main(void) {
return testy() + init_weakref(5, 6);
return testy() + init_weakref(5, 6);
}
''')
run_process([PYTHON, EMCC, 'x.c', '-o', 'x.o'])
run_process([PYTHON, EMCC, 'y.c', '-o', 'y.o'])
run_process([PYTHON, EMCC, 'z.c', '-o', 'z.o'])
run_process([PYTHON, EMAR, 'rc', 'libtest.a', 'y.o'])
run_process([PYTHON, EMAR, 'rc', 'libtest.a', 'x.o'])
run_process([PYTHON, EMRANLIB, 'libtest.a'])
run_process([PYTHON, EMCC, 'x.c', '-o', 'x.o'])
run_process([PYTHON, EMCC, 'y.c', '-o', 'y.o'])
run_process([PYTHON, EMCC, 'z.c', '-o', 'z.o'])
try_delete('libtest.a')
run_process([PYTHON, EMAR, 'rc', 'libtest.a', 'y.o'])
run_process([PYTHON, EMAR, 'rc', 'libtest.a', 'x.o'])
run_process([PYTHON, EMRANLIB, 'libtest.a'])

for args in [[], ['-O2']]:
print('args:', args)
run_process([PYTHON, EMCC, 'z.o', 'libtest.a', '-s', 'EXIT_RUNTIME=1'] + args)
run_js('a.out.js', assert_returncode=161)

Expand Down