Skip to content

Commit c374fdd

Browse files
committed
Teach recursive lipo to handle lipo within "copied" subdirectories.
Recursive lipo handles a mix of directories where some subdirectories build only a single slice for the host and other subdirectories build universal binaries (e.g., for a target). The former are lipo'd together, while the latter were copied verbatim. The `--copy-subdirs` option indicated that an entire subtree should be copied verbatim, and this was applied to `lib/swift` to cover all of the target libraries, with single-file exceptions provided by `--explicit-src-files`. However, this doesn't account for host content under `lib/swift` that needs to be lipo'd, i.e., the newly-introduced shared libraries in `lib/swift/host` that are part of host tools. Revise the semantics of `--copy-subdirs`. Instead of copying the entire directory verbatim, perform the normal recursion into these subdirectories. When there are executable files that aren't identical, check whether they have overlapping architectures: if they don't, lipo them. If they do, and we're in a "copied" subdirectory, take one of the files because they're considered equivalent. This gives a more fine-grained semantics to `--copy-subdirs` that allows us to both lipo executables/shared libraries and also construct Swift modules.
1 parent 5b8cd58 commit c374fdd

File tree

1 file changed

+59
-20
lines changed

1 file changed

+59
-20
lines changed

utils/recursive-lipo

Lines changed: 59 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,44 @@ def merge_file_lists(src_root_dirs, explicit_src_files, skip_files,
3737
return file_list
3838

3939

40+
# Check whether the given file occurs in any of the subpaths provided.
41+
def in_any_subpath(file, subpaths):
42+
current_path = file
43+
while current_path != '':
44+
current_dir = os.path.dirname(current_path)
45+
if current_dir in subpaths:
46+
return True
47+
current_path = current_dir
48+
49+
return False
50+
51+
# Determine if the file paths contain any overlapping architectures.
52+
def overlapping_lipo_architectures(file_paths, lipo_executable):
53+
lipo_cmd = [lipo_executable, "-archs"]
54+
55+
known_archs=[]
56+
for file in file_paths:
57+
archs=shell.capture(lipo_cmd + [file], echo=False).split(' ')
58+
for arch in archs:
59+
arch=arch.strip()
60+
if arch in known_archs:
61+
return True
62+
known_archs.append(arch)
63+
return False
64+
65+
# Copy the file
66+
def copy_file(file, src_root_dirs, dest_root_dir):
67+
for dir in src_root_dirs:
68+
orig_file=os.path.join(dir, file)
69+
if os.path.exists(orig_file):
70+
dest_file=os.path.join(
71+
dest_root_dir, os.path.relpath(orig_file, dir))
72+
print("-- Copying file %s" % dest_file)
73+
shutil.copy2(orig_file, dest_file)
74+
return
75+
76+
print("-- Warning: unable to copy file %s" % file)
77+
4078
def merge_lipo_files(src_root_dirs, file_list, copy_verbatim_subpaths,
4179
dest_root_dir, verbose=False, lipo_executable="lipo"):
4280
"""Recursively merges and runs lipo on all files from file_list in
@@ -73,32 +111,33 @@ def merge_lipo_files(src_root_dirs, file_list, copy_verbatim_subpaths,
73111
os.remove(dest_path)
74112
os.symlink(os.readlink(file_paths[0]), dest_path)
75113
elif all([os.path.isdir(item) for item in file_paths]):
76-
# It's a subdir in all found instances.
77-
# See if we should copy verbatim or create the destination subdir.
78-
if file in copy_verbatim_subpaths:
79-
print("-- Copying subdir verbatim %s" % dest_path)
80-
if os.path.isdir(dest_path):
81-
shutil.rmtree(dest_path)
82-
shutil.copytree(file_paths[0], dest_path, symlinks=True)
83-
else:
84-
print("-- Creating subdir %s" % dest_path)
85-
if not os.path.isdir(dest_path):
86-
os.makedirs(dest_path)
114+
# It's a subdir in all found instances. Create the destination
115+
# subdir.
116+
print("-- Creating subdir %s" % dest_path)
117+
if not os.path.isdir(dest_path):
118+
os.makedirs(dest_path)
87119
elif all([os.path.isfile(item) for item in file_paths]):
88120
# It's a regular file in all found instances, see if they're
89121
# identical.
90122
if all([filecmp.cmp(item, file_paths[0]) for item in file_paths]):
91123
# All instances are identical, just copy the unique file.
92-
print("-- Copying file %s" % dest_path)
93-
shutil.copy2(file_paths[0], dest_path)
124+
copy_file(file, src_root_dirs, dest_root_dir)
94125
elif all([os.access(item, os.X_OK) for item in file_paths]):
95-
# Multiple instances are different and executable, try lipo.
96-
if verbose:
97-
print("-- Running lipo %s to %s" % (file_paths, dest_path))
126+
if (overlapping_lipo_architectures(file_paths,
127+
lipo_executable) and
128+
in_any_subpath(file, copy_verbatim_subpaths)):
129+
# We're allowed to copy verbatim from this subpath, and
130+
# lipo will fail due to overlapping architectures, so
131+
# copy the file directly.
132+
copy_file(file, src_root_dirs, dest_root_dir)
98133
else:
99-
print("-- Running lipo %s" % dest_path)
100-
shell.call(lipo_cmd + ["-output", dest_path] + file_paths,
101-
echo=verbose)
134+
# Multiple instances are different and executable, try lipo.
135+
if verbose:
136+
print("-- Running lipo %s to %s" % (file_paths, dest_path))
137+
else:
138+
print("-- Running lipo %s" % dest_path)
139+
shell.call(lipo_cmd + ["-output", dest_path] + file_paths,
140+
echo=verbose)
102141
else:
103142
# Multiple instances are different, and they're not executable.
104143
print(
@@ -164,7 +203,7 @@ binaries.
164203
subdir.strip('/') for subdir in args.copy_subdirs.split()]
165204

166205
file_list = merge_file_lists(args.src_root_dirs, explicit_sources,
167-
skip_files, copy_verbatim_subpaths)
206+
skip_files, ())
168207

169208
if args.verbose:
170209
print("Discovered files and dirs: %s" % file_list)

0 commit comments

Comments
 (0)