1
1
#!/usr/bin/env python3
2
2
# -*- coding: utf-8 -*-
3
3
4
+ import typing as tp
4
5
import glob
5
6
import shutil
6
7
import logging
@@ -20,6 +21,13 @@ def _glob_one(p):
20
21
logging .info (f'Glob { p } -> { res [0 ]} ' )
21
22
return Path (res [0 ])
22
23
24
+ def _is_relative_to (a , b ):
25
+ try :
26
+ a .relative_to (b )
27
+ return True
28
+ except ValueError :
29
+ return False
30
+
23
31
emacs_version = str (_glob_one (APPDIR / 'bin/emacs-*' )).split ('-' )[- 1 ]
24
32
logging .info (f'Emacs version: { emacs_version } ' )
25
33
@@ -56,56 +64,149 @@ def _glob_one(p):
56
64
shutil .copy2 (_glob_one ('/lib/*/libc.so.6' ), gccjit_lib_path / 'libc.so.6' )
57
65
os .symlink ('libc.so.6' , str (gccjit_lib_path / 'libc.so' ))
58
66
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' ,
91
196
]
92
197
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
107
208
continue
108
- logging .info (f'Copying { libpath } ' )
209
+ logging .info (f'Copying { name } ( { libpath } ) ' )
109
210
dst_path = APPDIR / 'lib' / libpath .name
110
211
shutil .copy2 (libpath , dst_path )
111
212
0 commit comments