summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEnguerrand de Ribaucourt <enguerrand.de-ribaucourt@savoirfairelinux.com>2024-08-12 14:28:27 +0200
committerRichard Purdie <richard.purdie@linuxfoundation.org>2024-08-23 09:51:36 +0100
commit01d17cd5d42d1c9c349e1249bdacffb24d8446bb (patch)
tree9ea7ba3390225d6b420f81579c2e58700990c5c8
parent1053035cbca82f177e6cf8a2d467786de7aa146c (diff)
downloadpoky-01d17cd5d42d1c9c349e1249bdacffb24d8446bb.tar.gz
recipetool: create_npm: resolve licenses defined in package.json
Some npm packages do not copy the LICENSE or COPY file into their git repository. They'll instead simply use SPDX identifiers in their package.json. A fallback for those repositories attempted to match the README file to a license file instead, which had a very low probability of success. This commit replaces this fallback with parsing the package.json and looking for the license in COMMON_LICENSE_DIR. If the license is not found, "Unknown" will still be produced. This also generates "Unknown" for packages which had no README file, which could silently not appear in the generated recipe. The user was more likely to miss them. Co-authored-by: Tanguy Raufflet <tanguy.raufflet@savoirfairelinux.com> (From OE-Core rev: 445604cfc4a5813ea635f18053cd1f673bf0b830) Signed-off-by: Tanguy Raufflet <tanguy.raufflet@savoirfairelinux.com> Signed-off-by: Enguerrand de Ribaucourt <enguerrand.de-ribaucourt@savoirfairelinux.com> Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
-rw-r--r--scripts/lib/recipetool/create_npm.py57
1 files changed, 41 insertions, 16 deletions
diff --git a/scripts/lib/recipetool/create_npm.py b/scripts/lib/recipetool/create_npm.py
index 113a89f6a6..dd0ac01c3e 100644
--- a/scripts/lib/recipetool/create_npm.py
+++ b/scripts/lib/recipetool/create_npm.py
@@ -112,40 +112,52 @@ class NpmRecipeHandler(RecipeHandler):
112 """Return the extra license files and the list of packages""" 112 """Return the extra license files and the list of packages"""
113 licfiles = [] 113 licfiles = []
114 packages = {} 114 packages = {}
115 # Licenses from package.json point to COMMON_LICENSE_DIR so we need
116 # to associate them explicitely for split_pkg_licenses()
117 fallback_licenses = dict()
115 118
116 # Handle the parent package 119 # Handle the parent package
117 packages["${PN}"] = "" 120 packages["${PN}"] = ""
118 121
119 def _licfiles_append_fallback_readme_files(destdir): 122 def _licfiles_append_fallback_package_files(destdir):
120 """Append README files as fallback to license files if a license files is missing""" 123 """Append package.json files as fallback to license files if a license files is missing"""
124 def _get_licenses_from_package_json(package_json):
125 with open(os.path.join(srctree, package_json), "r") as f:
126 data = json.load(f)
127 if "license" in data:
128 licenses = data["license"].split(" ")
129 licenses = [license.strip("()") for license in licenses if license != "OR" and license != "AND"]
130 return ["${COMMON_LICENSE_DIR}/" + license for license in licenses], licenses
131 else:
132 return [package_json], None
121 133
122 fallback = True 134 fallback = True
123 readmes = []
124 basedir = os.path.join(srctree, destdir) 135 basedir = os.path.join(srctree, destdir)
125 for fn in os.listdir(basedir): 136 for fn in os.listdir(basedir):
126 upper = fn.upper() 137 upper = fn.upper()
127 if upper.startswith("README"):
128 fullpath = os.path.join(basedir, fn)
129 readmes.append(fullpath)
130 if upper.startswith("COPYING") or "LICENCE" in upper or "LICENSE" in upper: 138 if upper.startswith("COPYING") or "LICENCE" in upper or "LICENSE" in upper:
131 fallback = False 139 fallback = False
132 if fallback: 140 if fallback:
133 for readme in readmes: 141 pkg_json = os.path.join(basedir, "package.json")
134 licfiles.append(os.path.relpath(readme, srctree)) 142 return _get_licenses_from_package_json(pkg_json)
143 return [], None
135 144
136 # Handle the dependencies 145 # Handle the dependencies
137 def _handle_dependency(name, params, destdir): 146 def _handle_dependency(name, params, destdir):
138 deptree = destdir.split('node_modules/') 147 deptree = destdir.split('node_modules/')
139 suffix = "-".join([npm_package(dep) for dep in deptree]) 148 suffix = "-".join([npm_package(dep) for dep in deptree])
140 packages["${PN}" + suffix] = destdir 149 packages["${PN}" + suffix] = destdir
141 _licfiles_append_fallback_readme_files(destdir) 150 (fallback_licfiles, common_lics) = _licfiles_append_fallback_package_files(destdir)
151 licfiles.extend(fallback_licfiles)
152 if common_lics:
153 fallback_licenses["${PN}" + suffix] = common_lics
142 154
143 with open(shrinkwrap_file, "r") as f: 155 with open(shrinkwrap_file, "r") as f:
144 shrinkwrap = json.load(f) 156 shrinkwrap = json.load(f)
145 157
146 foreach_dependencies(shrinkwrap, _handle_dependency, dev) 158 foreach_dependencies(shrinkwrap, _handle_dependency, dev)
147 159
148 return licfiles, packages 160 return licfiles, packages, fallback_licenses
149 161
150 # Handle the peer dependencies 162 # Handle the peer dependencies
151 def _handle_peer_dependency(self, shrinkwrap_file): 163 def _handle_peer_dependency(self, shrinkwrap_file):
@@ -266,18 +278,31 @@ class NpmRecipeHandler(RecipeHandler):
266 fetcher.unpack(srctree) 278 fetcher.unpack(srctree)
267 279
268 bb.note("Handling licences ...") 280 bb.note("Handling licences ...")
269 (licfiles, packages) = self._handle_licenses(srctree, shrinkwrap_file, dev) 281 (licfiles, packages, fallback_licenses) = self._handle_licenses(srctree, shrinkwrap_file, dev)
270 282
271 def _guess_odd_license(licfiles): 283 def _guess_odd_license(licfiles):
272 import bb 284 import bb
273 285
274 md5sums = get_license_md5sums(d, linenumbers=True) 286 md5sums = get_license_md5sums(d, linenumbers=True)
275 287
288 def _resolve_licfile(srctree, licfile):
289 match = re.search(r'\$\{COMMON_LICENSE_DIR\}/(.+)$', licfile)
290 if match:
291 license = match.group(1)
292 commonlicdir = d.getVar('COMMON_LICENSE_DIR')
293 return os.path.join(commonlicdir, license)
294
295 return os.path.join(srctree, licfile)
296
276 chksums = [] 297 chksums = []
277 licenses = [] 298 licenses = []
299 md5value = None
278 for licfile in licfiles: 300 for licfile in licfiles:
279 f = os.path.join(srctree, licfile) 301 f = _resolve_licfile(srctree, licfile)
280 md5value = bb.utils.md5_file(f) 302 try:
303 md5value = bb.utils.md5_file(f)
304 except FileNotFoundError:
305 logger.info("Could not determine license for '%s'" % licfile)
281 (license, beginline, endline, md5) = md5sums.get(md5value, 306 (license, beginline, endline, md5) = md5sums.get(md5value,
282 (None, "", "", "")) 307 (None, "", "", ""))
283 if not license: 308 if not license:
@@ -292,10 +317,10 @@ class NpmRecipeHandler(RecipeHandler):
292 ";endline=%s" % (endline) if endline else "", 317 ";endline=%s" % (endline) if endline else "",
293 md5 if md5 else md5value)) 318 md5 if md5 else md5value))
294 licenses.append((license, licfile, md5value)) 319 licenses.append((license, licfile, md5value))
295 return (licenses, chksums) 320 return (licenses, chksums, fallback_licenses)
296 321
297 (licenses, extravalues["LIC_FILES_CHKSUM"]) = _guess_odd_license(licfiles) 322 (licenses, extravalues["LIC_FILES_CHKSUM"], fallback_licenses) = _guess_odd_license(licfiles)
298 split_pkg_licenses([*licenses, *guess_license(srctree, d)], packages, lines_after) 323 split_pkg_licenses([*licenses, *guess_license(srctree, d)], packages, lines_after, fallback_licenses)
299 324
300 classes.append("npm") 325 classes.append("npm")
301 handled.append("buildsystem") 326 handled.append("buildsystem")