Skip to content

Commit 800c110

Browse files
authored
Merge pull request #5004 from juj/fix_llvm_nm_and_ar_race_conditions
fix_llvm_nm_and_ar_race_condition
2 parents eac8e0b + 972fb4e commit 800c110

File tree

1 file changed

+38
-11
lines changed

1 file changed

+38
-11
lines changed

tools/shared.py

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1236,17 +1236,20 @@ def warn_if_duplicate_entries(archive_contents, archive_filename_hint=''):
12361236
logging.warning(' duplicate: %s' % curr)
12371237
warned.add(curr)
12381238

1239+
# N.B. This function creates a temporary directory specified by the 'dir' field in the returned dictionary. Caller
1240+
# is responsible for cleaning up those files after done.
12391241
def extract_archive_contents(f):
12401242
try:
12411243
cwd = os.getcwd()
1242-
temp_dir = os.path.join(tempfile.gettempdir(), f.replace('/', '_').replace('\\', '_').replace(':', '_') + '.archive_contents') # TODO: Make sure this is nice and sane
1244+
temp_dir = tempfile.mkdtemp('_archive_contents', 'emscripten_temp_')
12431245
safe_ensure_dirs(temp_dir)
12441246
os.chdir(temp_dir)
12451247
contents = filter(lambda x: len(x) > 0, Popen([LLVM_AR, 't', f], stdout=PIPE).communicate()[0].split('\n'))
12461248
warn_if_duplicate_entries(contents, f)
12471249
if len(contents) == 0:
12481250
logging.debug('Archive %s appears to be empty (recommendation: link an .so instead of .a)' % f)
12491251
return {
1252+
'returncode': 0,
12501253
'dir': temp_dir,
12511254
'files': []
12521255
}
@@ -1257,10 +1260,15 @@ def extract_archive_contents(f):
12571260
dirname = os.path.dirname(content)
12581261
if dirname:
12591262
safe_ensure_dirs(dirname)
1260-
Popen([LLVM_AR, 'xo', f], stdout=PIPE).communicate() # if absolute paths, files will appear there. otherwise, in this directory
1261-
contents = filter(os.path.exists, map(os.path.abspath, contents))
1262-
contents = filter(Building.is_bitcode, contents)
1263+
proc = Popen([LLVM_AR, 'xo', f], stdout=PIPE, stderr=PIPE)
1264+
stdout, stderr = proc.communicate() # if absolute paths, files will appear there. otherwise, in this directory
1265+
contents = map(os.path.abspath, contents)
1266+
nonexisting_contents = filter(lambda x: not os.path.exists(x), contents)
1267+
if len(nonexisting_contents) != 0:
1268+
raise Exception('llvm-ar failed to extract file(s) ' + str(nonexisting_contents) + ' from archive file ' + f + '! Error:' + str(stdout) + str(stderr))
1269+
12631270
return {
1271+
'returncode': proc.returncode,
12641272
'dir': temp_dir,
12651273
'files': contents
12661274
}
@@ -1270,20 +1278,23 @@ def extract_archive_contents(f):
12701278
os.chdir(cwd)
12711279

12721280
return {
1281+
'returncode': 1,
12731282
'dir': None,
12741283
'files': []
12751284
}
12761285

12771286
class ObjectFileInfo:
1278-
def __init__(self, defs, undefs, commons):
1287+
def __init__(self, returncode, output, defs=set(), undefs=set(), commons=set()):
1288+
self.returncode = returncode
1289+
self.output = output
12791290
self.defs = defs
12801291
self.undefs = undefs
12811292
self.commons = commons
12821293

12831294
# Due to a python pickling issue, the following two functions must be at top level, or multiprocessing pool spawn won't find them.
12841295

12851296
def g_llvm_nm_uncached(filename):
1286-
return Building.llvm_nm_uncached(filename, stdout=PIPE, stderr=None)
1297+
return Building.llvm_nm_uncached(filename)
12871298

12881299
def g_multiprocessing_initializer(*args):
12891300
for item in args:
@@ -1608,6 +1619,8 @@ def parallel_llvm_nm(files):
16081619
object_contents = pool.map(g_llvm_nm_uncached, files)
16091620

16101621
for i in range(len(files)):
1622+
if object_contents[i].returncode != 0:
1623+
raise Exception('llvm-nm failed on file ' + files[i] + ': return code ' + str(object_contents[i].returncode) + ', error: ' + object_contents[i].output)
16111624
Building.uninternal_nm_cache[files[i]] = object_contents[i]
16121625
return object_contents
16131626

@@ -1629,9 +1642,16 @@ def read_link_inputs(files):
16291642
# Archives contain objects, so process all archives first in parallel to obtain the object files in them.
16301643
pool = Building.get_multiprocessing_pool()
16311644
object_names_in_archives = pool.map(extract_archive_contents, archive_names)
1645+
def clean_temporary_archive_contents_directory(directory):
1646+
def clean_at_exit():
1647+
try_delete(directory)
1648+
if directory: atexit.register(clean_at_exit)
16321649

16331650
for n in range(len(archive_names)):
1651+
if object_names_in_archives[n]['returncode'] != 0:
1652+
raise Exception('llvm-ar failed on archive ' + archive_names[n] + '!')
16341653
Building.ar_contents[archive_names[n]] = object_names_in_archives[n]['files']
1654+
clean_temporary_archive_contents_directory(object_names_in_archives[n]['dir'])
16351655

16361656
for o in object_names_in_archives:
16371657
for f in o['files']:
@@ -1873,27 +1893,34 @@ def parse_symbols(output, include_internal=False):
18731893
( include_internal and status in ['W', 't', 'T', 'd', 'D']): # FIXME: using WTD in the previous line fails due to llvm-nm behavior on OS X,
18741894
# so for now we assume all uppercase are normally defined external symbols
18751895
defs.append(symbol)
1876-
return ObjectFileInfo(set(defs), set(undefs), set(commons))
1896+
return ObjectFileInfo(0, None, set(defs), set(undefs), set(commons))
18771897

18781898
internal_nm_cache = {} # cache results of nm - it can be slow to run
18791899
uninternal_nm_cache = {}
18801900
ar_contents = {} # Stores the object files contained in different archive files passed as input
18811901

18821902
@staticmethod
1883-
def llvm_nm_uncached(filename, stdout=PIPE, stderr=None, include_internal=False):
1903+
def llvm_nm_uncached(filename, stdout=PIPE, stderr=PIPE, include_internal=False):
18841904
# LLVM binary ==> list of symbols
1885-
output = Popen([LLVM_NM, filename], stdout=stdout, stderr=stderr).communicate()[0]
1886-
return Building.parse_symbols(output, include_internal)
1905+
proc = Popen([LLVM_NM, filename], stdout=stdout, stderr=stderr)
1906+
stdout, stderr = proc.communicate()
1907+
if proc.returncode == 0:
1908+
return Building.parse_symbols(stdout, include_internal)
1909+
else:
1910+
return ObjectFileInfo(proc.returncode, str(stdout) + str(stderr))
18871911

18881912
@staticmethod
1889-
def llvm_nm(filename, stdout=PIPE, stderr=None, include_internal=False):
1913+
def llvm_nm(filename, stdout=PIPE, stderr=PIPE, include_internal=False):
18901914
if include_internal and filename in Building.internal_nm_cache:
18911915
return Building.internal_nm_cache[filename]
18921916
elif not include_internal and filename in Building.uninternal_nm_cache:
18931917
return Building.uninternal_nm_cache[filename]
18941918

18951919
ret = Building.llvm_nm_uncached(filename, stdout, stderr, include_internal)
18961920

1921+
if ret.returncode != 0:
1922+
raise Exception('llvm-nm failed on file ' + filename + ': return code ' + str(ret.returncode) + ', error: ' + ret.output)
1923+
18971924
if include_internal: Building.internal_nm_cache[filename] = ret
18981925
else: Building.uninternal_nm_cache[filename] = ret
18991926

0 commit comments

Comments
 (0)