diff options
-rw-r--r-- | meta/classes/npm.bbclass | 20 | ||||
-rw-r--r-- | meta/lib/oe/package.py | 32 | ||||
-rw-r--r-- | scripts/lib/recipetool/create.py | 28 | ||||
-rw-r--r-- | scripts/lib/recipetool/create_npm.py | 57 |
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() { cp -a ${S}/* ${D}${libdir}/node_modules/${PN}/ --no-preserve=ownership } +python populate_packages_prepend () { + instdir = d.expand('${D}${libdir}/node_modules/${PN}') + extrapackages = oe.package.npm_split_package_dirs(instdir) + pkgnames = extrapackages.keys() + d.prependVar('PACKAGES', '%s ' % ' '.join(pkgnames)) + for pkgname in pkgnames: + pkgrelpath, pdata = extrapackages[pkgname] + pkgpath = '${libdir}/node_modules/${PN}/' + pkgrelpath + expanded_pkgname = d.expand(pkgname) + d.setVar('FILES_%s' % expanded_pkgname, pkgpath) + if pdata: + version = pdata.get('version', None) + if version: + d.setVar('PKGV_%s' % expanded_pkgname, version.encode("utf8")) + description = pdata.get('description', None) + if description: + d.setVar('SUMMARY_%s' % expanded_pkgname, description.replace(u"\u2018", "'").replace(u"\u2019", "'").encode("utf8")) + d.appendVar('RDEPENDS_%s' % d.getVar('PN', True), ' %s' % ' '.join(pkgnames)) +} + FILES_${PN} += " \ ${libdir}/node_modules/${PN} \ " 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): shlib_provider[s[0]] = {} shlib_provider[s[0]][s[1]] = (dep_pkg, s[2]) return shlib_provider + + +def npm_split_package_dirs(pkgdir): + """ + Work out the packages fetched and unpacked by BitBake's npm fetcher + Returns a dict of packagename -> (relpath, package.json) ordered + such that it is suitable for use in PACKAGES and FILES + """ + from collections import OrderedDict + import json + packages = {} + for root, dirs, files in os.walk(pkgdir): + if os.path.basename(root) == 'node_modules': + for dn in dirs: + relpth = os.path.relpath(os.path.join(root, dn), pkgdir) + pkgitems = ['${PN}'] + for pathitem in relpth.split('/'): + if pathitem == 'node_modules': + continue + pkgitems.append(pathitem) + pkgname = '-'.join(pkgitems) + pkgfile = os.path.join(root, dn, 'package.json') + data = None + if os.path.exists(pkgfile): + with open(pkgfile, 'r') as f: + data = json.loads(f.read()) + packages[pkgname] = (relpth, data) + # We want the main package for a module sorted *after* its subpackages + # (so that it doesn't otherwise steal the files for the subpackage), so + # this is a cheap way to do that whilst still having an otherwise + # alphabetical sort + 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): # Apply the handlers handled = [] + handled.append(('license', licvalues)) if args.binary: classes.append('bin_package') @@ -815,6 +816,33 @@ def guess_license(srctree): return licenses +def split_pkg_licenses(licvalues, packages, outlines, fallback_licenses=None, pn='${PN}'): + """ + Given a list of (license, path, md5sum) as returned by guess_license(), + a dict of package name to path mappings, write out a set of + package-specific LICENSE values. + """ + pkglicenses = {pn: []} + for license, licpath, _ in licvalues: + for pkgname, pkgpath in packages.iteritems(): + if licpath.startswith(pkgpath + '/'): + if pkgname in pkglicenses: + pkglicenses[pkgname].append(license) + else: + pkglicenses[pkgname] = [license] + break + else: + # Accumulate on the main package + pkglicenses[pn].append(license) + outlicenses = {} + for pkgname in packages: + license = ' '.join(list(set(pkglicenses.get(pkgname, ['Unknown'])))) + if license == 'Unknown' and pkgname in fallback_licenses: + license = fallback_licenses[pkgname] + outlines.append('LICENSE_%s = "%s"' % (pkgname, license)) + outlicenses[pkgname] = license.split() + return outlicenses + def read_pkgconfig_provides(d): pkgdatadir = d.getVar('PKGDATA_DIR', True) 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 @@ import logging import json -from recipetool.create import RecipeHandler +from recipetool.create import RecipeHandler, split_pkg_licenses logger = logging.getLogger('recipetool') class NpmRecipeHandler(RecipeHandler): + def _handle_license(self, data): + ''' + Handle the license value from an npm package.json file + ''' + license = None + if 'license' in data: + license = data['license'] + if isinstance(license, dict): + license = license.get('type', None) + return None + def process(self, srctree, classes, lines_before, lines_after, handled, extravalues): + import oe + from collections import OrderedDict + if 'buildsystem' in handled: return False + def read_package_json(fn): + with open(fn, 'r') as f: + return json.loads(f.read()) + files = RecipeHandler.checkfiles(srctree, ['package.json']) if files: - with open(files[0], 'r') as f: - data = json.loads(f.read()) + data = read_package_json(files[0]) if 'name' in data and 'version' in data: extravalues['PN'] = data['name'] extravalues['PV'] = data['version'] @@ -40,6 +57,40 @@ class NpmRecipeHandler(RecipeHandler): lines_before.append('SUMMARY = "%s"' % data['description']) if 'homepage' in data: lines_before.append('HOMEPAGE = "%s"' % data['homepage']) + + # Split each npm module out to is own package + npmpackages = oe.package.npm_split_package_dirs(srctree) + for item in handled: + if isinstance(item, tuple): + if item[0] == 'license': + licvalues = item[1] + break + if licvalues: + # Augment the license list with information we have in the packages + licenses = {} + license = self._handle_license(data) + if license: + licenses['${PN}'] = license + for pkgname, pkgitem in npmpackages.iteritems(): + _, pdata = pkgitem + license = self._handle_license(pdata) + if license: + licenses[pkgname] = license + # Now write out the package-specific license values + # We need to strip out the json data dicts for this since split_pkg_licenses + # isn't expecting it + packages = OrderedDict((x,y[0]) for x,y in npmpackages.iteritems()) + packages['${PN}'] = '' + pkglicenses = split_pkg_licenses(licvalues, packages, lines_after, licenses) + all_licenses = list(set([item for pkglicense in pkglicenses.values() for item in pkglicense])) + # Go back and update the LICENSE value since we have a bit more + # information than when that was written out (and we know all apply + # vs. there being a choice, so we can join them with &) + for i, line in enumerate(lines_before): + if line.startswith('LICENSE = '): + lines_before[i] = 'LICENSE = "%s"' % ' & '.join(all_licenses) + break + return True return False |