xref: /openbmc/openbmc/poky/meta/lib/oe/sdk.py (revision 90ca747a)
1#
2# SPDX-License-Identifier: GPL-2.0-only
3#
4
5from abc import ABCMeta, abstractmethod
6from oe.utils import execute_pre_post_process
7from oe.manifest import *
8from oe.package_manager import *
9import os
10import shutil
11import glob
12import traceback
13
14class Sdk(object, metaclass=ABCMeta):
15    def __init__(self, d, manifest_dir):
16        self.d = d
17        self.sdk_output = self.d.getVar('SDK_OUTPUT')
18        self.sdk_native_path = self.d.getVar('SDKPATHNATIVE').strip('/')
19        self.target_path = self.d.getVar('SDKTARGETSYSROOT').strip('/')
20        self.sysconfdir = self.d.getVar('sysconfdir').strip('/')
21
22        self.sdk_target_sysroot = os.path.join(self.sdk_output, self.target_path)
23        self.sdk_host_sysroot = self.sdk_output
24
25        if manifest_dir is None:
26            self.manifest_dir = self.d.getVar("SDK_DIR")
27        else:
28            self.manifest_dir = manifest_dir
29
30        self.remove(self.sdk_output, True)
31
32        self.install_order = Manifest.INSTALL_ORDER
33
34    @abstractmethod
35    def _populate(self):
36        pass
37
38    def populate(self):
39        self.mkdirhier(self.sdk_output)
40
41        # call backend dependent implementation
42        self._populate()
43
44        # Don't ship any libGL in the SDK
45        self.remove(os.path.join(self.sdk_output, self.sdk_native_path,
46                         self.d.getVar('libdir_nativesdk').strip('/'),
47                         "libGL*"))
48
49        # Fix or remove broken .la files
50        self.remove(os.path.join(self.sdk_output, self.sdk_native_path,
51                         self.d.getVar('libdir_nativesdk').strip('/'),
52                         "*.la"))
53
54        # Link the ld.so.cache file into the hosts filesystem
55        link_name = os.path.join(self.sdk_output, self.sdk_native_path,
56                                 self.sysconfdir, "ld.so.cache")
57        self.mkdirhier(os.path.dirname(link_name))
58        os.symlink("/etc/ld.so.cache", link_name)
59
60        execute_pre_post_process(self.d, self.d.getVar('SDK_POSTPROCESS_COMMAND'))
61
62    def movefile(self, sourcefile, destdir):
63        try:
64            # FIXME: this check of movefile's return code to None should be
65            # fixed within the function to use only exceptions to signal when
66            # something goes wrong
67            if (bb.utils.movefile(sourcefile, destdir) == None):
68                raise OSError("moving %s to %s failed"
69                        %(sourcefile, destdir))
70        #FIXME: using umbrella exc catching because bb.utils method raises it
71        except Exception as e:
72            bb.debug(1, "printing the stack trace\n %s" %traceback.format_exc())
73            bb.error("unable to place %s in final SDK location" % sourcefile)
74
75    def mkdirhier(self, dirpath):
76        try:
77            bb.utils.mkdirhier(dirpath)
78        except OSError as e:
79            bb.debug(1, "printing the stack trace\n %s" %traceback.format_exc())
80            bb.fatal("cannot make dir for SDK: %s" % dirpath)
81
82    def remove(self, path, recurse=False):
83        try:
84            bb.utils.remove(path, recurse)
85        #FIXME: using umbrella exc catching because bb.utils method raises it
86        except Exception as e:
87            bb.debug(1, "printing the stack trace\n %s" %traceback.format_exc())
88            bb.warn("cannot remove SDK dir: %s" % path)
89
90    def install_locales(self, pm):
91        # This is only relevant for glibc
92        if self.d.getVar("TCLIBC") != "glibc":
93            return
94
95        linguas = self.d.getVar("SDKIMAGE_LINGUAS")
96        if linguas:
97            import fnmatch
98            # Install the binary locales
99            if linguas == "all":
100                pm.install_glob("nativesdk-glibc-binary-localedata-*.utf-8", sdk=True)
101            else:
102                pm.install(["nativesdk-glibc-binary-localedata-%s.utf-8" % \
103                           lang for lang in linguas.split()])
104            # Generate a locale archive of them
105            target_arch = self.d.getVar('SDK_ARCH')
106            rootfs = oe.path.join(self.sdk_host_sysroot, self.sdk_native_path)
107            localedir = oe.path.join(rootfs, self.d.getVar("libdir_nativesdk"), "locale")
108            generate_locale_archive(self.d, rootfs, target_arch, localedir)
109            # And now delete the binary locales
110            pkgs = fnmatch.filter(pm.list_installed(), "nativesdk-glibc-binary-localedata-*.utf-8")
111            pm.remove(pkgs)
112        else:
113            # No linguas so do nothing
114            pass
115
116
117class RpmSdk(Sdk):
118    def __init__(self, d, manifest_dir=None, rpm_workdir="oe-sdk-repo"):
119        super(RpmSdk, self).__init__(d, manifest_dir)
120
121        self.target_manifest = RpmManifest(d, self.manifest_dir,
122                                           Manifest.MANIFEST_TYPE_SDK_TARGET)
123        self.host_manifest = RpmManifest(d, self.manifest_dir,
124                                         Manifest.MANIFEST_TYPE_SDK_HOST)
125
126        rpm_repo_workdir = "oe-sdk-repo"
127        if "sdk_ext" in d.getVar("BB_RUNTASK"):
128            rpm_repo_workdir = "oe-sdk-ext-repo"
129
130        self.target_pm = RpmPM(d,
131                               self.sdk_target_sysroot,
132                               self.d.getVar('TARGET_VENDOR'),
133                               'target',
134                               rpm_repo_workdir=rpm_repo_workdir
135                               )
136
137        self.host_pm = RpmPM(d,
138                             self.sdk_host_sysroot,
139                             self.d.getVar('SDK_VENDOR'),
140                             'host',
141                             "SDK_PACKAGE_ARCHS",
142                             "SDK_OS",
143                             rpm_repo_workdir=rpm_repo_workdir
144                             )
145
146    def _populate_sysroot(self, pm, manifest):
147        pkgs_to_install = manifest.parse_initial_manifest()
148
149        pm.create_configs()
150        pm.write_index()
151        pm.update()
152
153        pkgs = []
154        pkgs_attempt = []
155        for pkg_type in pkgs_to_install:
156            if pkg_type == Manifest.PKG_TYPE_ATTEMPT_ONLY:
157                pkgs_attempt += pkgs_to_install[pkg_type]
158            else:
159                pkgs += pkgs_to_install[pkg_type]
160
161        pm.install(pkgs)
162
163        pm.install(pkgs_attempt, True)
164
165    def _populate(self):
166        execute_pre_post_process(self.d, self.d.getVar("POPULATE_SDK_PRE_TARGET_COMMAND"))
167
168        bb.note("Installing TARGET packages")
169        self._populate_sysroot(self.target_pm, self.target_manifest)
170
171        self.target_pm.install_complementary(self.d.getVar('SDKIMAGE_INSTALL_COMPLEMENTARY'))
172
173        self.target_pm.run_intercepts(populate_sdk='target')
174
175        execute_pre_post_process(self.d, self.d.getVar("POPULATE_SDK_POST_TARGET_COMMAND"))
176
177        if not bb.utils.contains("SDKIMAGE_FEATURES", "package-management", True, False, self.d):
178            self.target_pm.remove_packaging_data()
179
180        bb.note("Installing NATIVESDK packages")
181        self._populate_sysroot(self.host_pm, self.host_manifest)
182        self.install_locales(self.host_pm)
183
184        self.host_pm.run_intercepts(populate_sdk='host')
185
186        execute_pre_post_process(self.d, self.d.getVar("POPULATE_SDK_POST_HOST_COMMAND"))
187
188        if not bb.utils.contains("SDKIMAGE_FEATURES", "package-management", True, False, self.d):
189            self.host_pm.remove_packaging_data()
190
191        # Move host RPM library data
192        native_rpm_state_dir = os.path.join(self.sdk_output,
193                                            self.sdk_native_path,
194                                            self.d.getVar('localstatedir_nativesdk').strip('/'),
195                                            "lib",
196                                            "rpm"
197                                            )
198        self.mkdirhier(native_rpm_state_dir)
199        for f in glob.glob(os.path.join(self.sdk_output,
200                                        "var",
201                                        "lib",
202                                        "rpm",
203                                        "*")):
204            self.movefile(f, native_rpm_state_dir)
205
206        self.remove(os.path.join(self.sdk_output, "var"), True)
207
208        # Move host sysconfig data
209        native_sysconf_dir = os.path.join(self.sdk_output,
210                                          self.sdk_native_path,
211                                          self.d.getVar('sysconfdir',
212                                                        True).strip('/'),
213                                          )
214        self.mkdirhier(native_sysconf_dir)
215        for f in glob.glob(os.path.join(self.sdk_output, "etc", "rpm*")):
216            self.movefile(f, native_sysconf_dir)
217        for f in glob.glob(os.path.join(self.sdk_output, "etc", "dnf", "*")):
218            self.movefile(f, native_sysconf_dir)
219        self.remove(os.path.join(self.sdk_output, "etc"), True)
220
221
222class OpkgSdk(Sdk):
223    def __init__(self, d, manifest_dir=None):
224        super(OpkgSdk, self).__init__(d, manifest_dir)
225
226        self.target_conf = self.d.getVar("IPKGCONF_TARGET")
227        self.host_conf = self.d.getVar("IPKGCONF_SDK")
228
229        self.target_manifest = OpkgManifest(d, self.manifest_dir,
230                                            Manifest.MANIFEST_TYPE_SDK_TARGET)
231        self.host_manifest = OpkgManifest(d, self.manifest_dir,
232                                          Manifest.MANIFEST_TYPE_SDK_HOST)
233
234        ipk_repo_workdir = "oe-sdk-repo"
235        if "sdk_ext" in d.getVar("BB_RUNTASK"):
236            ipk_repo_workdir = "oe-sdk-ext-repo"
237
238        self.target_pm = OpkgPM(d, self.sdk_target_sysroot, self.target_conf,
239                                self.d.getVar("ALL_MULTILIB_PACKAGE_ARCHS"),
240                                ipk_repo_workdir=ipk_repo_workdir)
241
242        self.host_pm = OpkgPM(d, self.sdk_host_sysroot, self.host_conf,
243                              self.d.getVar("SDK_PACKAGE_ARCHS"),
244                                ipk_repo_workdir=ipk_repo_workdir)
245
246    def _populate_sysroot(self, pm, manifest):
247        pkgs_to_install = manifest.parse_initial_manifest()
248
249        if (self.d.getVar('BUILD_IMAGES_FROM_FEEDS') or "") != "1":
250            pm.write_index()
251
252        pm.update()
253
254        for pkg_type in self.install_order:
255            if pkg_type in pkgs_to_install:
256                pm.install(pkgs_to_install[pkg_type],
257                           [False, True][pkg_type == Manifest.PKG_TYPE_ATTEMPT_ONLY])
258
259    def _populate(self):
260        execute_pre_post_process(self.d, self.d.getVar("POPULATE_SDK_PRE_TARGET_COMMAND"))
261
262        bb.note("Installing TARGET packages")
263        self._populate_sysroot(self.target_pm, self.target_manifest)
264
265        self.target_pm.install_complementary(self.d.getVar('SDKIMAGE_INSTALL_COMPLEMENTARY'))
266
267        self.target_pm.run_intercepts(populate_sdk='target')
268
269        execute_pre_post_process(self.d, self.d.getVar("POPULATE_SDK_POST_TARGET_COMMAND"))
270
271        if not bb.utils.contains("SDKIMAGE_FEATURES", "package-management", True, False, self.d):
272            self.target_pm.remove_packaging_data()
273
274        bb.note("Installing NATIVESDK packages")
275        self._populate_sysroot(self.host_pm, self.host_manifest)
276        self.install_locales(self.host_pm)
277
278        self.host_pm.run_intercepts(populate_sdk='host')
279
280        execute_pre_post_process(self.d, self.d.getVar("POPULATE_SDK_POST_HOST_COMMAND"))
281
282        if not bb.utils.contains("SDKIMAGE_FEATURES", "package-management", True, False, self.d):
283            self.host_pm.remove_packaging_data()
284
285        target_sysconfdir = os.path.join(self.sdk_target_sysroot, self.sysconfdir)
286        host_sysconfdir = os.path.join(self.sdk_host_sysroot, self.sysconfdir)
287
288        self.mkdirhier(target_sysconfdir)
289        shutil.copy(self.target_conf, target_sysconfdir)
290        os.chmod(os.path.join(target_sysconfdir,
291                              os.path.basename(self.target_conf)), 0o644)
292
293        self.mkdirhier(host_sysconfdir)
294        shutil.copy(self.host_conf, host_sysconfdir)
295        os.chmod(os.path.join(host_sysconfdir,
296                              os.path.basename(self.host_conf)), 0o644)
297
298        native_opkg_state_dir = os.path.join(self.sdk_output, self.sdk_native_path,
299                                             self.d.getVar('localstatedir_nativesdk').strip('/'),
300                                             "lib", "opkg")
301        self.mkdirhier(native_opkg_state_dir)
302        for f in glob.glob(os.path.join(self.sdk_output, "var", "lib", "opkg", "*")):
303            self.movefile(f, native_opkg_state_dir)
304
305        self.remove(os.path.join(self.sdk_output, "var"), True)
306
307
308class DpkgSdk(Sdk):
309    def __init__(self, d, manifest_dir=None):
310        super(DpkgSdk, self).__init__(d, manifest_dir)
311
312        self.target_conf_dir = os.path.join(self.d.getVar("APTCONF_TARGET"), "apt")
313        self.host_conf_dir = os.path.join(self.d.getVar("APTCONF_TARGET"), "apt-sdk")
314
315        self.target_manifest = DpkgManifest(d, self.manifest_dir,
316                                            Manifest.MANIFEST_TYPE_SDK_TARGET)
317        self.host_manifest = DpkgManifest(d, self.manifest_dir,
318                                          Manifest.MANIFEST_TYPE_SDK_HOST)
319
320        deb_repo_workdir = "oe-sdk-repo"
321        if "sdk_ext" in d.getVar("BB_RUNTASK"):
322            deb_repo_workdir = "oe-sdk-ext-repo"
323
324        self.target_pm = DpkgPM(d, self.sdk_target_sysroot,
325                                self.d.getVar("PACKAGE_ARCHS"),
326                                self.d.getVar("DPKG_ARCH"),
327                                self.target_conf_dir,
328                                deb_repo_workdir=deb_repo_workdir)
329
330        self.host_pm = DpkgPM(d, self.sdk_host_sysroot,
331                              self.d.getVar("SDK_PACKAGE_ARCHS"),
332                              self.d.getVar("DEB_SDK_ARCH"),
333                              self.host_conf_dir,
334                              deb_repo_workdir=deb_repo_workdir)
335
336    def _copy_apt_dir_to(self, dst_dir):
337        staging_etcdir_native = self.d.getVar("STAGING_ETCDIR_NATIVE")
338
339        self.remove(dst_dir, True)
340
341        shutil.copytree(os.path.join(staging_etcdir_native, "apt"), dst_dir)
342
343    def _populate_sysroot(self, pm, manifest):
344        pkgs_to_install = manifest.parse_initial_manifest()
345
346        pm.write_index()
347        pm.update()
348
349        for pkg_type in self.install_order:
350            if pkg_type in pkgs_to_install:
351                pm.install(pkgs_to_install[pkg_type],
352                           [False, True][pkg_type == Manifest.PKG_TYPE_ATTEMPT_ONLY])
353
354    def _populate(self):
355        execute_pre_post_process(self.d, self.d.getVar("POPULATE_SDK_PRE_TARGET_COMMAND"))
356
357        bb.note("Installing TARGET packages")
358        self._populate_sysroot(self.target_pm, self.target_manifest)
359
360        self.target_pm.install_complementary(self.d.getVar('SDKIMAGE_INSTALL_COMPLEMENTARY'))
361
362        self.target_pm.run_intercepts(populate_sdk='target')
363
364        execute_pre_post_process(self.d, self.d.getVar("POPULATE_SDK_POST_TARGET_COMMAND"))
365
366        self._copy_apt_dir_to(os.path.join(self.sdk_target_sysroot, "etc", "apt"))
367
368        if not bb.utils.contains("SDKIMAGE_FEATURES", "package-management", True, False, self.d):
369            self.target_pm.remove_packaging_data()
370
371        bb.note("Installing NATIVESDK packages")
372        self._populate_sysroot(self.host_pm, self.host_manifest)
373        self.install_locales(self.host_pm)
374
375        self.host_pm.run_intercepts(populate_sdk='host')
376
377        execute_pre_post_process(self.d, self.d.getVar("POPULATE_SDK_POST_HOST_COMMAND"))
378
379        self._copy_apt_dir_to(os.path.join(self.sdk_output, self.sdk_native_path,
380                                           "etc", "apt"))
381
382        if not bb.utils.contains("SDKIMAGE_FEATURES", "package-management", True, False, self.d):
383            self.host_pm.remove_packaging_data()
384
385        native_dpkg_state_dir = os.path.join(self.sdk_output, self.sdk_native_path,
386                                             "var", "lib", "dpkg")
387        self.mkdirhier(native_dpkg_state_dir)
388        for f in glob.glob(os.path.join(self.sdk_output, "var", "lib", "dpkg", "*")):
389            self.movefile(f, native_dpkg_state_dir)
390        self.remove(os.path.join(self.sdk_output, "var"), True)
391
392
393
394def sdk_list_installed_packages(d, target, rootfs_dir=None):
395    if rootfs_dir is None:
396        sdk_output = d.getVar('SDK_OUTPUT')
397        target_path = d.getVar('SDKTARGETSYSROOT').strip('/')
398
399        rootfs_dir = [sdk_output, os.path.join(sdk_output, target_path)][target is True]
400
401    img_type = d.getVar('IMAGE_PKGTYPE')
402    if img_type == "rpm":
403        arch_var = ["SDK_PACKAGE_ARCHS", None][target is True]
404        os_var = ["SDK_OS", None][target is True]
405        return RpmPkgsList(d, rootfs_dir).list_pkgs()
406    elif img_type == "ipk":
407        conf_file_var = ["IPKGCONF_SDK", "IPKGCONF_TARGET"][target is True]
408        return OpkgPkgsList(d, rootfs_dir, d.getVar(conf_file_var)).list_pkgs()
409    elif img_type == "deb":
410        return DpkgPkgsList(d, rootfs_dir).list_pkgs()
411
412def populate_sdk(d, manifest_dir=None):
413    env_bkp = os.environ.copy()
414
415    img_type = d.getVar('IMAGE_PKGTYPE')
416    if img_type == "rpm":
417        RpmSdk(d, manifest_dir).populate()
418    elif img_type == "ipk":
419        OpkgSdk(d, manifest_dir).populate()
420    elif img_type == "deb":
421        DpkgSdk(d, manifest_dir).populate()
422
423    os.environ.clear()
424    os.environ.update(env_bkp)
425
426def get_extra_sdkinfo(sstate_dir):
427    """
428    This function is going to be used for generating the target and host manifest files packages of eSDK.
429    """
430    import math
431
432    extra_info = {}
433    extra_info['tasksizes'] = {}
434    extra_info['filesizes'] = {}
435    for root, _, files in os.walk(sstate_dir):
436        for fn in files:
437            if fn.endswith('.tgz'):
438                fsize = int(math.ceil(float(os.path.getsize(os.path.join(root, fn))) / 1024))
439                task = fn.rsplit(':',1)[1].split('_',1)[1].split(',')[0]
440                origtotal = extra_info['tasksizes'].get(task, 0)
441                extra_info['tasksizes'][task] = origtotal + fsize
442                extra_info['filesizes'][fn] = fsize
443    return extra_info
444
445if __name__ == "__main__":
446    pass
447