blob: 4f3e21ad40fcd7bcec7b048e584bab1671647410 [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001def runstrip(arg):
2 # Function to strip a single file, called from split_and_strip_files below
3 # A working 'file' (one which works on the target architecture)
4 #
5 # The elftype is a bit pattern (explained in split_and_strip_files) to tell
6 # us what type of file we're processing...
7 # 4 - executable
8 # 8 - shared library
9 # 16 - kernel module
10
Patrick Williamsc0f7c042017-02-23 20:41:17 -060011 import stat, subprocess
Patrick Williamsc124f4f2015-09-15 14:41:29 -050012
13 (file, elftype, strip) = arg
14
15 newmode = None
16 if not os.access(file, os.W_OK) or os.access(file, os.R_OK):
17 origmode = os.stat(file)[stat.ST_MODE]
18 newmode = origmode | stat.S_IWRITE | stat.S_IREAD
19 os.chmod(file, newmode)
20
Brad Bishop6e60e8b2018-02-01 10:27:11 -050021 stripcmd = [strip]
Patrick Williamsc124f4f2015-09-15 14:41:29 -050022
23 # kernel module
24 if elftype & 16:
Brad Bishop6e60e8b2018-02-01 10:27:11 -050025 stripcmd.extend(["--strip-debug", "--remove-section=.comment",
26 "--remove-section=.note", "--preserve-dates"])
Patrick Williamsc124f4f2015-09-15 14:41:29 -050027 # .so and shared library
28 elif ".so" in file and elftype & 8:
Brad Bishop6e60e8b2018-02-01 10:27:11 -050029 stripcmd.extend(["--remove-section=.comment", "--remove-section=.note", "--strip-unneeded"])
Patrick Williamsc124f4f2015-09-15 14:41:29 -050030 # shared or executable:
31 elif elftype & 8 or elftype & 4:
Brad Bishop6e60e8b2018-02-01 10:27:11 -050032 stripcmd.extend(["--remove-section=.comment", "--remove-section=.note"])
Patrick Williamsc124f4f2015-09-15 14:41:29 -050033
Brad Bishop6e60e8b2018-02-01 10:27:11 -050034 stripcmd.append(file)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050035 bb.debug(1, "runstrip: %s" % stripcmd)
36
37 try:
Brad Bishop6e60e8b2018-02-01 10:27:11 -050038 output = subprocess.check_output(stripcmd, stderr=subprocess.STDOUT)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050039 except subprocess.CalledProcessError as e:
40 bb.error("runstrip: '%s' strip command failed with %s (%s)" % (stripcmd, e.returncode, e.output))
41
42 if newmode:
43 os.chmod(file, origmode)
44
45 return
46
47
Brad Bishopd7bf8c12018-02-25 22:55:05 -050048def strip_execs(pn, dstdir, strip_cmd, libdir, base_libdir, qa_already_stripped=False):
49 """
50 Strip executable code (like executables, shared libraries) _in_place_
51 - Based on sysroot_strip in staging.bbclass
52 :param dstdir: directory in which to strip files
53 :param strip_cmd: Strip command (usually ${STRIP})
54 :param libdir: ${libdir} - strip .so files in this directory
55 :param base_libdir: ${base_libdir} - strip .so files in this directory
56 :param qa_already_stripped: Set to True if already-stripped' in ${INSANE_SKIP}
57 This is for proper logging and messages only.
58 """
59 import stat, errno, oe.path, oe.utils, mmap
60
61 # Detect .ko module by searching for "vermagic=" string
62 def is_kernel_module(path):
63 with open(path) as f:
64 return mmap.mmap(f.fileno(), 0, prot=mmap.PROT_READ).find(b"vermagic=") >= 0
65
66 # Return type (bits):
67 # 0 - not elf
68 # 1 - ELF
69 # 2 - stripped
70 # 4 - executable
71 # 8 - shared library
72 # 16 - kernel module
73 def is_elf(path):
74 exec_type = 0
Brad Bishop316dfdd2018-06-25 12:45:53 -040075 ret, result = oe.utils.getstatusoutput("file -b '%s'" % path)
Brad Bishopd7bf8c12018-02-25 22:55:05 -050076
77 if ret:
78 bb.error("split_and_strip_files: 'file %s' failed" % path)
79 return exec_type
80
81 if "ELF" in result:
82 exec_type |= 1
83 if "not stripped" not in result:
84 exec_type |= 2
85 if "executable" in result:
86 exec_type |= 4
87 if "shared" in result:
88 exec_type |= 8
89 if "relocatable" in result and is_kernel_module(path):
90 exec_type |= 16
91 return exec_type
92
93 elffiles = {}
94 inodes = {}
95 libdir = os.path.abspath(dstdir + os.sep + libdir)
96 base_libdir = os.path.abspath(dstdir + os.sep + base_libdir)
97 exec_mask = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
98 #
99 # First lets figure out all of the files we may have to process
100 #
101 for root, dirs, files in os.walk(dstdir):
102 for f in files:
103 file = os.path.join(root, f)
104
105 try:
106 ltarget = oe.path.realpath(file, dstdir, False)
107 s = os.lstat(ltarget)
108 except OSError as e:
109 (err, strerror) = e.args
110 if err != errno.ENOENT:
111 raise
112 # Skip broken symlinks
113 continue
114 if not s:
115 continue
116 # Check its an excutable
117 if s[stat.ST_MODE] & exec_mask \
118 or ((file.startswith(libdir) or file.startswith(base_libdir)) and ".so" in f) \
119 or file.endswith('.ko'):
120 # If it's a symlink, and points to an ELF file, we capture the readlink target
121 if os.path.islink(file):
122 continue
123
124 # It's a file (or hardlink), not a link
125 # ...but is it ELF, and is it already stripped?
126 elf_file = is_elf(file)
127 if elf_file & 1:
128 if elf_file & 2:
129 if qa_already_stripped:
130 bb.note("Skipping file %s from %s for already-stripped QA test" % (file[len(dstdir):], pn))
131 else:
132 bb.warn("File '%s' from %s was already stripped, this will prevent future debugging!" % (file[len(dstdir):], pn))
133 continue
134
135 if s.st_ino in inodes:
136 os.unlink(file)
137 os.link(inodes[s.st_ino], file)
138 else:
139 # break hardlinks so that we do not strip the original.
140 inodes[s.st_ino] = file
141 bb.utils.copyfile(file, file)
142 elffiles[file] = elf_file
143
144 #
145 # Now strip them (in parallel)
146 #
147 sfiles = []
148 for file in elffiles:
149 elf_file = int(elffiles[file])
150 sfiles.append((file, elf_file, strip_cmd))
151
152 oe.utils.multiprocess_exec(sfiles, runstrip)
153
154
155
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500156def file_translate(file):
157 ft = file.replace("@", "@at@")
158 ft = ft.replace(" ", "@space@")
159 ft = ft.replace("\t", "@tab@")
160 ft = ft.replace("[", "@openbrace@")
161 ft = ft.replace("]", "@closebrace@")
162 ft = ft.replace("_", "@underscore@")
163 return ft
164
165def filedeprunner(arg):
166 import re, subprocess, shlex
167
168 (pkg, pkgfiles, rpmdeps, pkgdest) = arg
169 provides = {}
170 requires = {}
171
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500172 file_re = re.compile(r'\s+\d+\s(.*)')
173 dep_re = re.compile(r'\s+(\S)\s+(.*)')
174 r = re.compile(r'[<>=]+\s+\S*')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500175
176 def process_deps(pipe, pkg, pkgdest, provides, requires):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500177 file = None
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500178 for line in pipe.split("\n"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500179
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500180 m = file_re.match(line)
181 if m:
182 file = m.group(1)
183 file = file.replace(pkgdest + "/" + pkg, "")
184 file = file_translate(file)
185 continue
186
187 m = dep_re.match(line)
188 if not m or not file:
189 continue
190
191 type, dep = m.groups()
192
193 if type == 'R':
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500194 i = requires
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500195 elif type == 'P':
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500196 i = provides
197 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500198 continue
199
200 if dep.startswith("python("):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500201 continue
202
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500203 # Ignore all perl(VMS::...) and perl(Mac::...) dependencies. These
204 # are typically used conditionally from the Perl code, but are
205 # generated as unconditional dependencies.
206 if dep.startswith('perl(VMS::') or dep.startswith('perl(Mac::'):
207 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500208
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500209 # Ignore perl dependencies on .pl files.
210 if dep.startswith('perl(') and dep.endswith('.pl)'):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500211 continue
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500212
213 # Remove perl versions and perl module versions since they typically
214 # do not make sense when used as package versions.
215 if dep.startswith('perl') and r.search(dep):
216 dep = dep.split()[0]
217
218 # Put parentheses around any version specifications.
219 dep = r.sub(r'(\g<0>)',dep)
220
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500221 if file not in i:
222 i[file] = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500223 i[file].append(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500224
225 return provides, requires
226
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500227 output = subprocess.check_output(shlex.split(rpmdeps) + pkgfiles, stderr=subprocess.STDOUT).decode("utf-8")
228 provides, requires = process_deps(output, pkg, pkgdest, provides, requires)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500229
230 return (pkg, provides, requires)
231
232
233def read_shlib_providers(d):
234 import re
235
236 shlib_provider = {}
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500237 shlibs_dirs = d.getVar('SHLIBSDIRS').split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500238 list_re = re.compile('^(.*)\.list$')
239 # Go from least to most specific since the last one found wins
240 for dir in reversed(shlibs_dirs):
241 bb.debug(2, "Reading shlib providers in %s" % (dir))
242 if not os.path.exists(dir):
243 continue
244 for file in os.listdir(dir):
245 m = list_re.match(file)
246 if m:
247 dep_pkg = m.group(1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600248 try:
249 fd = open(os.path.join(dir, file))
250 except IOError:
251 # During a build unrelated shlib files may be deleted, so
252 # handle files disappearing between the listdirs and open.
253 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500254 lines = fd.readlines()
255 fd.close()
256 for l in lines:
257 s = l.strip().split(":")
258 if s[0] not in shlib_provider:
259 shlib_provider[s[0]] = {}
260 shlib_provider[s[0]][s[1]] = (dep_pkg, s[2])
261 return shlib_provider
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500262
263
264def npm_split_package_dirs(pkgdir):
265 """
266 Work out the packages fetched and unpacked by BitBake's npm fetcher
267 Returns a dict of packagename -> (relpath, package.json) ordered
268 such that it is suitable for use in PACKAGES and FILES
269 """
270 from collections import OrderedDict
271 import json
272 packages = {}
273 for root, dirs, files in os.walk(pkgdir):
274 if os.path.basename(root) == 'node_modules':
275 for dn in dirs:
276 relpth = os.path.relpath(os.path.join(root, dn), pkgdir)
277 pkgitems = ['${PN}']
278 for pathitem in relpth.split('/'):
279 if pathitem == 'node_modules':
280 continue
281 pkgitems.append(pathitem)
282 pkgname = '-'.join(pkgitems).replace('_', '-')
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500283 pkgname = pkgname.replace('@', '')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500284 pkgfile = os.path.join(root, dn, 'package.json')
285 data = None
286 if os.path.exists(pkgfile):
287 with open(pkgfile, 'r') as f:
288 data = json.loads(f.read())
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600289 packages[pkgname] = (relpth, data)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500290 # We want the main package for a module sorted *after* its subpackages
291 # (so that it doesn't otherwise steal the files for the subpackage), so
292 # this is a cheap way to do that whilst still having an otherwise
293 # alphabetical sort
294 return OrderedDict((key, packages[key]) for key in sorted(packages, key=lambda pkg: pkg + '~'))