Skip to content

Commit 5b720e1

Browse files
committed
Merge branch 'dev-postprocess-lddtree'
process dynamic libs by blacklisting lddtree, instead of whitelist
2 parents 652451f + 2303ed1 commit 5b720e1

File tree

3 files changed

+150
-52
lines changed

3 files changed

+150
-52
lines changed

.github/workflows/daily-mps.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,6 @@ permissions:
55
contents: write
66

77
on:
8-
push:
9-
branches:
10-
- 'dev-*'
118
schedule:
129
- cron: '0 0 * * 0'
1310
workflow_dispatch:

Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,12 @@ RUN wget -O mps-release-1.118.0.tar.gz https://github.com/Ravenbrook/mps/archive
5050
cd .. && rm -rf mps-release-1.118.0 mps-release-1.118.0.tar.gz
5151

5252
RUN apt-get update && apt-get install -y \
53-
xorg libx11-dev libgtk-3-dev libxaw7-dev \
53+
xorg libx11-dev libgtk-3-dev libxaw7-dev libwebkit2gtk-4.0-dev \
5454
libjpeg-dev libgif-dev libtiff-dev libxmp-dev \
5555
libsqlite3-dev libmagickcore-dev libmagickwand-dev \
5656
libwebp-dev libotf-dev libcairo-dev libjansson-dev \
5757
libgnutls28-dev libxpm-dev libncurses-dev \
58-
git texinfo && \
58+
git texinfo pax-utils && \
5959
rm -rf /var/lib/apt/lists/*
6060

6161
ADD scripts /work/scripts

scripts/postprocess.py

Lines changed: 148 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#!/usr/bin/env python3
22
# -*- coding: utf-8 -*-
33

4+
import typing as tp
45
import glob
56
import shutil
67
import logging
@@ -20,6 +21,13 @@ def _glob_one(p):
2021
logging.info(f'Glob {p} -> {res[0]}')
2122
return Path(res[0])
2223

24+
def _is_relative_to(a, b):
25+
try:
26+
a.relative_to(b)
27+
return True
28+
except ValueError:
29+
return False
30+
2331
emacs_version = str(_glob_one(APPDIR / 'bin/emacs-*')).split('-')[-1]
2432
logging.info(f'Emacs version: {emacs_version}')
2533

@@ -56,56 +64,149 @@ def _glob_one(p):
5664
shutil.copy2(_glob_one('/lib/*/libc.so.6'), gccjit_lib_path / 'libc.so.6')
5765
os.symlink('libc.so.6', str(gccjit_lib_path / 'libc.so'))
5866

59-
# copy libraries
60-
LIB_WHITELIST = [
61-
'libMagickCore-6.Q16', # depend by emacs
62-
'libMagickWand-6.Q16', # depend by emacs
63-
'libbz2', # depend by MagickCore
64-
'libfftw3', # depend by MagickCore
65-
'libgomp', # depend by MagickWand
66-
'liblcms2', # depend by emacs, MagickWand, MagickCore
67-
'liblqr-1', # depend by MagickWand, MagickCore
68-
'libltdl', # depend by MagickCore
69-
70-
'librsvg-2', # depend by emacs
71-
'libcroco-0.6', # depend by rsvg
72-
73-
'libgif', # depend by emacs
74-
'libjansson', # depend by emacs
75-
'libotf', # depend by emacs
76-
'libsqlite3', # depend by emacs
77-
'libtinfo', # depend by emacs
78-
'libwebpdemux', # depend by emacs
79-
'libwebp', # depend by emacs
80-
81-
'libmpc', # depend by libgccjit
82-
'libmpfr', # depend by libgccjit
83-
84-
# 'libdatrie', # thai
85-
86-
# 'libpng16', # depend by emacs. but should be present in users' system by gtk, so do not include
87-
88-
'libxml2', # depend by emacs. libxml 2.14+ uses a different soname
89-
'libicuuc', # depend by libxml2
90-
'libicudata', # depend by libicuuc
67+
# ---
68+
# process dynamic libs
69+
# ---
70+
class NeededLib:
71+
72+
def __init__(self, name: str, resolved_path: Path):
73+
self.name = name
74+
self.resolved_path = resolved_path
75+
self.needed_libs = []
76+
77+
def _collect(self, result):
78+
for l in self.needed_libs:
79+
result[l.name] = l.resolved_path
80+
l._collect(result)
81+
82+
def collect(self) -> tp.Dict[str, Path]:
83+
'''
84+
Collect all nodes in current tree (except self).
85+
'''
86+
result = {}
87+
self._collect(result)
88+
return result
89+
90+
def _trim(self, name_prefix, trimmed_names: tp.Set[str]):
91+
kept_libs = []
92+
for x in self.needed_libs:
93+
if not x.name.startswith(name_prefix):
94+
kept_libs.append(x)
95+
else:
96+
trimmed_names.add(x.name)
97+
trimmed_names.update(x.collect().keys())
98+
self.needed_libs = kept_libs
99+
for x in self.needed_libs:
100+
x._trim(name_prefix, trimmed_names)
101+
102+
def trim(self, name_prefix) -> tp.Set[str]:
103+
'''
104+
Delete node whose name starts with name_prefix, along with all their children.
105+
'''
106+
trimmed_names = set()
107+
self._trim(name_prefix, trimmed_names)
108+
return trimmed_names
109+
110+
def _leading_space_count(s) -> int:
111+
n = 0
112+
while n < len(s) and s[n] == ' ':
113+
n = n + 1
114+
return n
115+
116+
def _parse_lddtree(lddtree_output: str) -> NeededLib:
117+
lines = lddtree_output.rstrip().splitlines()
118+
assert not lines[0].startswith(' ')
119+
line_i = 1
120+
121+
def _parse_node(node, expected_level):
122+
nonlocal line_i
123+
while line_i < len(lines):
124+
line = lines[line_i]
125+
level = _leading_space_count(line) // 4
126+
127+
if level > expected_level:
128+
assert level == expected_level + 1
129+
assert len(node.needed_libs) > 0
130+
_parse_node(node.needed_libs[-1], expected_level + 1)
131+
continue
132+
133+
if level < expected_level:
134+
return
135+
136+
parts = line.strip().split(' => ')
137+
assert len(parts) == 2
138+
node.needed_libs.append(
139+
NeededLib(name=parts[0], resolved_path=Path(parts[1]))
140+
)
141+
line_i = line_i + 1
142+
143+
root = NeededLib(name='', resolved_path=Path())
144+
_parse_node(root, 1)
145+
return root
146+
147+
148+
lddtree_output = subprocess.check_output(
149+
['lddtree', '-a', str(APPDIR / 'bin/emacs')],
150+
universal_newlines=True,
151+
)
152+
lddtree = _parse_lddtree(lddtree_output)
153+
154+
155+
# this is prefix
156+
LIB_BLACKLIST = [
157+
# GTK related
158+
'libgtk-3.so',
159+
'libgdk-3.so',
160+
'libgdk_pixbuf-2.0.so',
161+
'libgio-2.0.so',
162+
'libgobject-2.0.so',
163+
'libglib-2.0.so',
164+
# X related
165+
'libX', # prefix
166+
'libxcb.so',
167+
'libxcb-shape.so',
168+
'libSM.so',
169+
'libICE.so',
170+
# GUI base system
171+
'libpango',
172+
'libcairo.so',
173+
'libfreetype.so',
174+
'libfontconfig.so',
175+
'libharfbuzz.so',
176+
'libGL',
177+
'libOpenGL',
178+
'libEGL',
179+
'libdbus-1.so',
180+
# base system
181+
'libgcc_s.so',
182+
'libgnutls.so',
183+
'libstdc++.so.6',
184+
# glibc
185+
'libpthread.so',
186+
'librt.so',
187+
'libc.so',
188+
'libc_',
189+
'libdl.so',
190+
'libresolv.so',
191+
'libz.so',
192+
'libm.so',
193+
'libanl.so',
194+
'libnss_',
195+
'libutil.so',
91196
]
92197

93-
ldd_output = subprocess.check_output(['ldd', str(APPDIR / 'bin/emacs')], universal_newlines=True)
94-
for line in ldd_output.splitlines():
95-
if '=>' not in line or 'not found' in line:
96-
logging.warning(f'Unexpected ldd output: {line}')
97-
continue
98-
libpath = Path(line.split()[2])
99-
if not libpath.exists():
100-
logging.warning(f'Skipping non-exist library {libpath}')
101-
continue
102-
if libpath.parent.resolve() == (APPDIR / 'lib'):
103-
continue
104-
libname = libpath.name.split('.so')[0]
105-
if libname not in LIB_WHITELIST:
106-
logging.info(f'Skipping non-whitelisted library {libpath}')
198+
# Remove lib from blacklist
199+
# also, remove all its children
200+
for name in LIB_BLACKLIST:
201+
trimmed_names = lddtree.trim(name)
202+
for trimmed_name in trimmed_names:
203+
logging.warning(f'Removed lib {trimmed_name}')
204+
lddtree.trim(trimmed_name)
205+
206+
for name, libpath in lddtree.collect().items():
207+
if _is_relative_to(libpath.resolve(), APPDIR): # e.g. libgccjit
107208
continue
108-
logging.info(f'Copying {libpath}')
209+
logging.info(f'Copying {name} ({libpath})')
109210
dst_path = APPDIR / 'lib' / libpath.name
110211
shutil.copy2(libpath, dst_path)
111212

0 commit comments

Comments
 (0)