summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Eggleton <paul.eggleton@linux.intel.com>2016-03-09 17:48:52 +1300
committerRichard Purdie <richard.purdie@linuxfoundation.org>2016-03-09 17:00:29 +0000
commit91455005b65db1b6caededbcac0f08a0cfb660f3 (patch)
tree5b7e917a5560985f743d99475b722bc726f00660
parentd46827cfd322554b57c3fc774b4d914f6e449d75 (diff)
downloadpoky-91455005b65db1b6caededbcac0f08a0cfb660f3.tar.gz
recipetool: create: split npm module dependencies into packages
Rather than rolling all of an npm module's dependencies into the same package, split them into one module per package, setting the SUMMARY and PKGV values from the package.json file for each package. Additionally, mark each package with the appropriate license using the license scanning we already do, falling back to the license stated in the package.json file for the module if unknown. All of this is mostly in aid of ensuring all modules and their licenses now show up in the manifests for the image. Additionally we set the main LICENSE value more concretely once we've calculated the per-package licenses, since we have more information at that point. (From OE-Core rev: 8226805f83d21e7c1d2ba21969f3e8ee4b137496) Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
-rw-r--r--meta/classes/npm.bbclass20
-rw-r--r--meta/lib/oe/package.py32
-rw-r--r--scripts/lib/recipetool/create.py28
-rw-r--r--scripts/lib/recipetool/create_npm.py57
4 files changed, 134 insertions, 3 deletions
diff --git a/meta/classes/npm.bbclass b/meta/classes/npm.bbclass
index be76056c55..b5db99d2b9 100644
--- a/meta/classes/npm.bbclass
+++ b/meta/classes/npm.bbclass
@@ -18,6 +18,26 @@ npm_do_install() {
18 cp -a ${S}/* ${D}${libdir}/node_modules/${PN}/ --no-preserve=ownership 18 cp -a ${S}/* ${D}${libdir}/node_modules/${PN}/ --no-preserve=ownership
19} 19}
20 20
21python populate_packages_prepend () {
22 instdir = d.expand('${D}${libdir}/node_modules/${PN}')
23 extrapackages = oe.package.npm_split_package_dirs(instdir)
24 pkgnames = extrapackages.keys()
25 d.prependVar('PACKAGES', '%s ' % ' '.join(pkgnames))
26 for pkgname in pkgnames:
27 pkgrelpath, pdata = extrapackages[pkgname]
28 pkgpath = '${libdir}/node_modules/${PN}/' + pkgrelpath
29 expanded_pkgname = d.expand(pkgname)
30 d.setVar('FILES_%s' % expanded_pkgname, pkgpath)
31 if pdata:
32 version = pdata.get('version', None)
33 if version:
34 d.setVar('PKGV_%s' % expanded_pkgname, version.encode("utf8"))
35 description = pdata.get('description', None)
36 if description:
37 d.setVar('SUMMARY_%s' % expanded_pkgname, description.replace(u"\u2018", "'").replace(u"\u2019", "'").encode("utf8"))
38 d.appendVar('RDEPENDS_%s' % d.getVar('PN', True), ' %s' % ' '.join(pkgnames))
39}
40
21FILES_${PN} += " \ 41FILES_${PN} += " \
22 ${libdir}/node_modules/${PN} \ 42 ${libdir}/node_modules/${PN} \
23" 43"
diff --git a/meta/lib/oe/package.py b/meta/lib/oe/package.py
index f176446b8b..dea443d658 100644
--- a/meta/lib/oe/package.py
+++ b/meta/lib/oe/package.py
@@ -123,3 +123,35 @@ def read_shlib_providers(d):
123 shlib_provider[s[0]] = {} 123 shlib_provider[s[0]] = {}
124 shlib_provider[s[0]][s[1]] = (dep_pkg, s[2]) 124 shlib_provider[s[0]][s[1]] = (dep_pkg, s[2])
125 return shlib_provider 125 return shlib_provider
126
127
128def npm_split_package_dirs(pkgdir):
129 """
130 Work out the packages fetched and unpacked by BitBake's npm fetcher
131 Returns a dict of packagename -> (relpath, package.json) ordered
132 such that it is suitable for use in PACKAGES and FILES
133 """
134 from collections import OrderedDict
135 import json
136 packages = {}
137 for root, dirs, files in os.walk(pkgdir):
138 if os.path.basename(root) == 'node_modules':
139 for dn in dirs:
140 relpth = os.path.relpath(os.path.join(root, dn), pkgdir)
141 pkgitems = ['${PN}']
142 for pathitem in relpth.split('/'):
143 if pathitem == 'node_modules':
144 continue
145 pkgitems.append(pathitem)
146 pkgname = '-'.join(pkgitems)
147 pkgfile = os.path.join(root, dn, 'package.json')
148 data = None
149 if os.path.exists(pkgfile):
150 with open(pkgfile, 'r') as f:
151 data = json.loads(f.read())
152 packages[pkgname] = (relpth, data)
153 # We want the main package for a module sorted *after* its subpackages
154 # (so that it doesn't otherwise steal the files for the subpackage), so
155 # this is a cheap way to do that whilst still having an otherwise
156 # alphabetical sort
157 return OrderedDict((key, packages[key]) for key in sorted(packages, key=lambda pkg: pkg + '~'))
diff --git a/scripts/lib/recipetool/create.py b/scripts/lib/recipetool/create.py
index 718f2aaf5b..43c07848c2 100644
--- a/scripts/lib/recipetool/create.py
+++ b/scripts/lib/recipetool/create.py
@@ -544,6 +544,7 @@ def create_recipe(args):
544 544
545 # Apply the handlers 545 # Apply the handlers
546 handled = [] 546 handled = []
547 handled.append(('license', licvalues))
547 548
548 if args.binary: 549 if args.binary:
549 classes.append('bin_package') 550 classes.append('bin_package')
@@ -815,6 +816,33 @@ def guess_license(srctree):
815 816
816 return licenses 817 return licenses
817 818
819def split_pkg_licenses(licvalues, packages, outlines, fallback_licenses=None, pn='${PN}'):
820 """
821 Given a list of (license, path, md5sum) as returned by guess_license(),
822 a dict of package name to path mappings, write out a set of
823 package-specific LICENSE values.
824 """
825 pkglicenses = {pn: []}
826 for license, licpath, _ in licvalues:
827 for pkgname, pkgpath in packages.iteritems():
828 if licpath.startswith(pkgpath + '/'):
829 if pkgname in pkglicenses:
830 pkglicenses[pkgname].append(license)
831 else:
832 pkglicenses[pkgname] = [license]
833 break
834 else:
835 # Accumulate on the main package
836 pkglicenses[pn].append(license)
837 outlicenses = {}
838 for pkgname in packages:
839 license = ' '.join(list(set(pkglicenses.get(pkgname, ['Unknown']))))
840 if license == 'Unknown' and pkgname in fallback_licenses:
841 license = fallback_licenses[pkgname]
842 outlines.append('LICENSE_%s = "%s"' % (pkgname, license))
843 outlicenses[pkgname] = license.split()
844 return outlicenses
845
818def read_pkgconfig_provides(d): 846def read_pkgconfig_provides(d):
819 pkgdatadir = d.getVar('PKGDATA_DIR', True) 847 pkgdatadir = d.getVar('PKGDATA_DIR', True)
820 pkgmap = {} 848 pkgmap = {}
diff --git a/scripts/lib/recipetool/create_npm.py b/scripts/lib/recipetool/create_npm.py
index 0e33cc9a1e..4bf6caed5c 100644
--- a/scripts/lib/recipetool/create_npm.py
+++ b/scripts/lib/recipetool/create_npm.py
@@ -17,20 +17,37 @@
17 17
18import logging 18import logging
19import json 19import json
20from recipetool.create import RecipeHandler 20from recipetool.create import RecipeHandler, split_pkg_licenses
21 21
22logger = logging.getLogger('recipetool') 22logger = logging.getLogger('recipetool')
23 23
24 24
25class NpmRecipeHandler(RecipeHandler): 25class NpmRecipeHandler(RecipeHandler):
26 def _handle_license(self, data):
27 '''
28 Handle the license value from an npm package.json file
29 '''
30 license = None
31 if 'license' in data:
32 license = data['license']
33 if isinstance(license, dict):
34 license = license.get('type', None)
35 return None
36
26 def process(self, srctree, classes, lines_before, lines_after, handled, extravalues): 37 def process(self, srctree, classes, lines_before, lines_after, handled, extravalues):
38 import oe
39 from collections import OrderedDict
40
27 if 'buildsystem' in handled: 41 if 'buildsystem' in handled:
28 return False 42 return False
29 43
44 def read_package_json(fn):
45 with open(fn, 'r') as f:
46 return json.loads(f.read())
47
30 files = RecipeHandler.checkfiles(srctree, ['package.json']) 48 files = RecipeHandler.checkfiles(srctree, ['package.json'])
31 if files: 49 if files:
32 with open(files[0], 'r') as f: 50 data = read_package_json(files[0])
33 data = json.loads(f.read())
34 if 'name' in data and 'version' in data: 51 if 'name' in data and 'version' in data:
35 extravalues['PN'] = data['name'] 52 extravalues['PN'] = data['name']
36 extravalues['PV'] = data['version'] 53 extravalues['PV'] = data['version']
@@ -40,6 +57,40 @@ class NpmRecipeHandler(RecipeHandler):
40 lines_before.append('SUMMARY = "%s"' % data['description']) 57 lines_before.append('SUMMARY = "%s"' % data['description'])
41 if 'homepage' in data: 58 if 'homepage' in data:
42 lines_before.append('HOMEPAGE = "%s"' % data['homepage']) 59 lines_before.append('HOMEPAGE = "%s"' % data['homepage'])
60
61 # Split each npm module out to is own package
62 npmpackages = oe.package.npm_split_package_dirs(srctree)
63 for item in handled:
64 if isinstance(item, tuple):
65 if item[0] == 'license':
66 licvalues = item[1]
67 break
68 if licvalues:
69 # Augment the license list with information we have in the packages
70 licenses = {}
71 license = self._handle_license(data)
72 if license:
73 licenses['${PN}'] = license
74 for pkgname, pkgitem in npmpackages.iteritems():
75 _, pdata = pkgitem
76 license = self._handle_license(pdata)
77 if license:
78 licenses[pkgname] = license
79 # Now write out the package-specific license values
80 # We need to strip out the json data dicts for this since split_pkg_licenses
81 # isn't expecting it
82 packages = OrderedDict((x,y[0]) for x,y in npmpackages.iteritems())
83 packages['${PN}'] = ''
84 pkglicenses = split_pkg_licenses(licvalues, packages, lines_after, licenses)
85 all_licenses = list(set([item for pkglicense in pkglicenses.values() for item in pkglicense]))
86 # Go back and update the LICENSE value since we have a bit more
87 # information than when that was written out (and we know all apply
88 # vs. there being a choice, so we can join them with &)
89 for i, line in enumerate(lines_before):
90 if line.startswith('LICENSE = '):
91 lines_before[i] = 'LICENSE = "%s"' % ' & '.join(all_licenses)
92 break
93
43 return True 94 return True
44 95
45 return False 96 return False