xref: /OK3568_Linux_fs/yocto/poky/meta/classes/license_image.bbclass (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1ROOTFS_LICENSE_DIR = "${IMAGE_ROOTFS}/usr/share/common-licenses"
2
3# This requires LICENSE_CREATE_PACKAGE=1 to work too
4COMPLEMENTARY_GLOB[lic-pkgs] = "*-lic"
5
6python() {
7    if not oe.data.typed_value('LICENSE_CREATE_PACKAGE', d):
8        features = set(oe.data.typed_value('IMAGE_FEATURES', d))
9        if 'lic-pkgs' in features:
10            bb.error("'lic-pkgs' in IMAGE_FEATURES but LICENSE_CREATE_PACKAGE not enabled to generate -lic packages")
11}
12
13python write_package_manifest() {
14    # Get list of installed packages
15    license_image_dir = d.expand('${LICENSE_DIRECTORY}/${IMAGE_NAME}')
16    bb.utils.mkdirhier(license_image_dir)
17    from oe.rootfs import image_list_installed_packages
18    from oe.utils import format_pkg_list
19
20    pkgs = image_list_installed_packages(d)
21    output = format_pkg_list(pkgs)
22    with open(os.path.join(license_image_dir, 'package.manifest'), "w+") as package_manifest:
23        package_manifest.write(output)
24}
25
26python license_create_manifest() {
27    import oe.packagedata
28    from oe.rootfs import image_list_installed_packages
29
30    build_images_from_feeds = d.getVar('BUILD_IMAGES_FROM_FEEDS')
31    if build_images_from_feeds == "1":
32        return 0
33
34    pkg_dic = {}
35    for pkg in sorted(image_list_installed_packages(d)):
36        pkg_info = os.path.join(d.getVar('PKGDATA_DIR'),
37                                'runtime-reverse', pkg)
38        pkg_name = os.path.basename(os.readlink(pkg_info))
39
40        pkg_dic[pkg_name] = oe.packagedata.read_pkgdatafile(pkg_info)
41        if not "LICENSE" in pkg_dic[pkg_name].keys():
42            pkg_lic_name = "LICENSE:" + pkg_name
43            pkg_dic[pkg_name]["LICENSE"] = pkg_dic[pkg_name][pkg_lic_name]
44
45    rootfs_license_manifest = os.path.join(d.getVar('LICENSE_DIRECTORY'),
46                        d.getVar('IMAGE_NAME'), 'license.manifest')
47    write_license_files(d, rootfs_license_manifest, pkg_dic, rootfs=True)
48}
49
50def write_license_files(d, license_manifest, pkg_dic, rootfs=True):
51    import re
52    import stat
53
54    bad_licenses = (d.getVar("INCOMPATIBLE_LICENSE") or "").split()
55    bad_licenses = expand_wildcard_licenses(d, bad_licenses)
56
57    exceptions = (d.getVar("INCOMPATIBLE_LICENSE_EXCEPTIONS") or "").split()
58    with open(license_manifest, "w") as license_file:
59        for pkg in sorted(pkg_dic):
60            remaining_bad_licenses = oe.license.apply_pkg_license_exception(pkg, bad_licenses, exceptions)
61            incompatible_licenses = incompatible_pkg_license(d, remaining_bad_licenses, pkg_dic[pkg]["LICENSE"])
62            if incompatible_licenses:
63                bb.fatal("Package %s cannot be installed into the image because it has incompatible license(s): %s" %(pkg, ' '.join(incompatible_licenses)))
64            else:
65                incompatible_licenses = incompatible_pkg_license(d, bad_licenses, pkg_dic[pkg]["LICENSE"])
66                if incompatible_licenses:
67                    oe.qa.handle_error('license-incompatible', "Including %s with incompatible license(s) %s into the image, because it has been allowed by exception list." %(pkg, ' '.join(incompatible_licenses)), d)
68            try:
69                (pkg_dic[pkg]["LICENSE"], pkg_dic[pkg]["LICENSES"]) = \
70                    oe.license.manifest_licenses(pkg_dic[pkg]["LICENSE"],
71                    remaining_bad_licenses, canonical_license, d)
72            except oe.license.LicenseError as exc:
73                bb.fatal('%s: %s' % (d.getVar('P'), exc))
74
75            if not "IMAGE_MANIFEST" in pkg_dic[pkg]:
76                # Rootfs manifest
77                license_file.write("PACKAGE NAME: %s\n" % pkg)
78                license_file.write("PACKAGE VERSION: %s\n" % pkg_dic[pkg]["PV"])
79                license_file.write("RECIPE NAME: %s\n" % pkg_dic[pkg]["PN"])
80                license_file.write("LICENSE: %s\n\n" % pkg_dic[pkg]["LICENSE"])
81
82                # If the package doesn't contain any file, that is, its size is 0, the license
83                # isn't relevant as far as the final image is concerned. So doing license check
84                # doesn't make much sense, skip it.
85                if pkg_dic[pkg]["PKGSIZE:%s" % pkg] == "0":
86                    continue
87            else:
88                # Image manifest
89                license_file.write("RECIPE NAME: %s\n" % pkg_dic[pkg]["PN"])
90                license_file.write("VERSION: %s\n" % pkg_dic[pkg]["PV"])
91                license_file.write("LICENSE: %s\n" % pkg_dic[pkg]["LICENSE"])
92                license_file.write("FILES: %s\n\n" % pkg_dic[pkg]["FILES"])
93
94            for lic in pkg_dic[pkg]["LICENSES"]:
95                lic_file = os.path.join(d.getVar('LICENSE_DIRECTORY'),
96                                        pkg_dic[pkg]["PN"], "generic_%s" %
97                                        re.sub(r'\+', '', lic))
98                # add explicity avoid of CLOSED license because isn't generic
99                if lic == "CLOSED":
100                   continue
101
102                if not os.path.exists(lic_file):
103                    oe.qa.handle_error('license-file-missing',
104                                       "The license listed %s was not in the "\
105                                       "licenses collected for recipe %s"
106                                       % (lic, pkg_dic[pkg]["PN"]), d)
107    oe.qa.exit_if_errors(d)
108
109    # Two options here:
110    # - Just copy the manifest
111    # - Copy the manifest and the license directories
112    # With both options set we see a .5 M increase in core-image-minimal
113    copy_lic_manifest = d.getVar('COPY_LIC_MANIFEST')
114    copy_lic_dirs = d.getVar('COPY_LIC_DIRS')
115    if rootfs and copy_lic_manifest == "1":
116        rootfs_license_dir = d.getVar('ROOTFS_LICENSE_DIR')
117        bb.utils.mkdirhier(rootfs_license_dir)
118        rootfs_license_manifest = os.path.join(rootfs_license_dir,
119                os.path.split(license_manifest)[1])
120        if not os.path.exists(rootfs_license_manifest):
121            oe.path.copyhardlink(license_manifest, rootfs_license_manifest)
122
123        if copy_lic_dirs == "1":
124            for pkg in sorted(pkg_dic):
125                pkg_rootfs_license_dir = os.path.join(rootfs_license_dir, pkg)
126                bb.utils.mkdirhier(pkg_rootfs_license_dir)
127                pkg_license_dir = os.path.join(d.getVar('LICENSE_DIRECTORY'),
128                                            pkg_dic[pkg]["PN"])
129
130                pkg_manifest_licenses = [canonical_license(d, lic) \
131                        for lic in pkg_dic[pkg]["LICENSES"]]
132
133                licenses = os.listdir(pkg_license_dir)
134                for lic in licenses:
135                    pkg_license = os.path.join(pkg_license_dir, lic)
136                    pkg_rootfs_license = os.path.join(pkg_rootfs_license_dir, lic)
137
138                    if re.match(r"^generic_.*$", lic):
139                        generic_lic = canonical_license(d,
140                                re.search(r"^generic_(.*)$", lic).group(1))
141
142                        # Do not copy generic license into package if isn't
143                        # declared into LICENSES of the package.
144                        if not re.sub(r'\+$', '', generic_lic) in \
145                                [re.sub(r'\+', '', lic) for lic in \
146                                 pkg_manifest_licenses]:
147                            continue
148
149                        if oe.license.license_ok(generic_lic,
150                                bad_licenses) == False:
151                            continue
152
153                        # Make sure we use only canonical name for the license file
154                        generic_lic_file = "generic_%s" % generic_lic
155                        rootfs_license = os.path.join(rootfs_license_dir, generic_lic_file)
156                        if not os.path.exists(rootfs_license):
157                            oe.path.copyhardlink(pkg_license, rootfs_license)
158
159                        if not os.path.exists(pkg_rootfs_license):
160                            os.symlink(os.path.join('..', generic_lic_file), pkg_rootfs_license)
161                    else:
162                        if (oe.license.license_ok(canonical_license(d,
163                                lic), bad_licenses) == False or
164                                os.path.exists(pkg_rootfs_license)):
165                            continue
166
167                        oe.path.copyhardlink(pkg_license, pkg_rootfs_license)
168            # Fixup file ownership and permissions
169            for walkroot, dirs, files in os.walk(rootfs_license_dir):
170                for f in files:
171                    p = os.path.join(walkroot, f)
172                    os.lchown(p, 0, 0)
173                    if not os.path.islink(p):
174                        os.chmod(p, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH)
175                for dir in dirs:
176                    p = os.path.join(walkroot, dir)
177                    os.lchown(p, 0, 0)
178                    os.chmod(p, stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH)
179
180
181
182def license_deployed_manifest(d):
183    """
184    Write the license manifest for the deployed recipes.
185    The deployed recipes usually includes the bootloader
186    and extra files to boot the target.
187    """
188
189    dep_dic = {}
190    man_dic = {}
191    lic_dir = d.getVar("LICENSE_DIRECTORY")
192
193    dep_dic = get_deployed_dependencies(d)
194    for dep in dep_dic.keys():
195        man_dic[dep] = {}
196        # It is necessary to mark this will be used for image manifest
197        man_dic[dep]["IMAGE_MANIFEST"] = True
198        man_dic[dep]["PN"] = dep
199        man_dic[dep]["FILES"] = \
200            " ".join(get_deployed_files(dep_dic[dep]))
201        with open(os.path.join(lic_dir, dep, "recipeinfo"), "r") as f:
202            for line in f.readlines():
203                key,val = line.split(": ", 1)
204                man_dic[dep][key] = val[:-1]
205
206    lic_manifest_dir = os.path.join(d.getVar('LICENSE_DIRECTORY'),
207                                    d.getVar('IMAGE_NAME'))
208    bb.utils.mkdirhier(lic_manifest_dir)
209    image_license_manifest = os.path.join(lic_manifest_dir, 'image_license.manifest')
210    write_license_files(d, image_license_manifest, man_dic, rootfs=False)
211
212    link_name = d.getVar('IMAGE_LINK_NAME')
213    if link_name:
214        lic_manifest_symlink_dir = os.path.join(d.getVar('LICENSE_DIRECTORY'),
215                                    link_name)
216        # remove old symlink
217        if os.path.islink(lic_manifest_symlink_dir):
218            os.unlink(lic_manifest_symlink_dir)
219
220        # create the image dir symlink
221        if lic_manifest_dir != lic_manifest_symlink_dir:
222            os.symlink(lic_manifest_dir, lic_manifest_symlink_dir)
223
224def get_deployed_dependencies(d):
225    """
226    Get all the deployed dependencies of an image
227    """
228
229    deploy = {}
230    # Get all the dependencies for the current task (rootfs).
231    taskdata = d.getVar("BB_TASKDEPDATA", False)
232    pn = d.getVar("PN")
233    depends = list(set([dep[0] for dep
234                    in list(taskdata.values())
235                    if not dep[0].endswith("-native") and not dep[0] == pn]))
236
237    # To verify what was deployed it checks the rootfs dependencies against
238    # the SSTATE_MANIFESTS for "deploy" task.
239    # The manifest file name contains the arch. Because we are not running
240    # in the recipe context it is necessary to check every arch used.
241    sstate_manifest_dir = d.getVar("SSTATE_MANIFESTS")
242    archs = list(set(d.getVar("SSTATE_ARCHS").split()))
243    for dep in depends:
244        for arch in archs:
245            sstate_manifest_file = os.path.join(sstate_manifest_dir,
246                    "manifest-%s-%s.deploy" % (arch, dep))
247            if os.path.exists(sstate_manifest_file):
248                deploy[dep] = sstate_manifest_file
249                break
250
251    return deploy
252get_deployed_dependencies[vardepsexclude] = "BB_TASKDEPDATA"
253
254def get_deployed_files(man_file):
255    """
256    Get the files deployed from the sstate manifest
257    """
258
259    dep_files = []
260    excluded_files = []
261    with open(man_file, "r") as manifest:
262        all_files = manifest.read()
263    for f in all_files.splitlines():
264        if ((not (os.path.islink(f) or os.path.isdir(f))) and
265                not os.path.basename(f) in excluded_files):
266            dep_files.append(os.path.basename(f))
267    return dep_files
268
269ROOTFS_POSTPROCESS_COMMAND:prepend = "write_package_manifest; license_create_manifest; "
270do_rootfs[recrdeptask] += "do_populate_lic"
271
272python do_populate_lic_deploy() {
273    license_deployed_manifest(d)
274    oe.qa.exit_if_errors(d)
275}
276
277addtask populate_lic_deploy before do_build after do_image_complete
278do_populate_lic_deploy[recrdeptask] += "do_populate_lic do_deploy"
279
280python license_qa_dead_symlink() {
281    import os
282
283    for root, dirs, files in os.walk(d.getVar('ROOTFS_LICENSE_DIR')):
284        for file in files:
285            full_path = root + "/" + file
286            if os.path.islink(full_path) and not os.path.exists(full_path):
287                bb.error("broken symlink: " + full_path)
288}
289IMAGE_QA_COMMANDS += "license_qa_dead_symlink"
290