blob: 98277f74c9622a484d93eb88519e2921aa805f3e [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001# Recipe creation tool - create command plugin
2#
Brad Bishopd7bf8c12018-02-25 22:55:05 -05003# Copyright (C) 2014-2017 Intel Corporation
Patrick Williamsc124f4f2015-09-15 14:41:29 -05004#
Brad Bishopc342db32019-05-15 21:57:59 -04005# SPDX-License-Identifier: GPL-2.0-only
Patrick Williamsc124f4f2015-09-15 14:41:29 -05006#
Patrick Williamsc124f4f2015-09-15 14:41:29 -05007
8import sys
9import os
10import argparse
11import glob
12import fnmatch
13import re
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050014import json
Patrick Williamsc124f4f2015-09-15 14:41:29 -050015import logging
16import scriptutils
Patrick Williamsc0f7c042017-02-23 20:41:17 -060017from urllib.parse import urlparse, urldefrag, urlsplit
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050018import hashlib
Brad Bishop6e60e8b2018-02-01 10:27:11 -050019import bb.fetch2
Patrick Williamsc124f4f2015-09-15 14:41:29 -050020logger = logging.getLogger('recipetool')
21
22tinfoil = None
23plugins = None
24
Brad Bishop6e60e8b2018-02-01 10:27:11 -050025def log_error_cond(message, debugonly):
26 if debugonly:
27 logger.debug(message)
28 else:
29 logger.error(message)
30
31def log_info_cond(message, debugonly):
32 if debugonly:
33 logger.debug(message)
34 else:
35 logger.info(message)
36
Patrick Williamsc124f4f2015-09-15 14:41:29 -050037def plugin_init(pluginlist):
38 # Take a reference to the list so we can use it later
39 global plugins
40 plugins = pluginlist
41
42def tinfoil_init(instance):
43 global tinfoil
44 tinfoil = instance
45
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050046class RecipeHandler(object):
47 recipelibmap = {}
48 recipeheadermap = {}
49 recipecmakefilemap = {}
50 recipebinmap = {}
51
Brad Bishop6e60e8b2018-02-01 10:27:11 -050052 def __init__(self):
53 self._devtool = False
54
Patrick Williamsc124f4f2015-09-15 14:41:29 -050055 @staticmethod
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050056 def load_libmap(d):
57 '''Load library->recipe mapping'''
58 import oe.package
59
60 if RecipeHandler.recipelibmap:
61 return
62 # First build up library->package mapping
63 shlib_providers = oe.package.read_shlib_providers(d)
Brad Bishop6e60e8b2018-02-01 10:27:11 -050064 libdir = d.getVar('libdir')
65 base_libdir = d.getVar('base_libdir')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050066 libpaths = list(set([base_libdir, libdir]))
67 libname_re = re.compile('^lib(.+)\.so.*$')
68 pkglibmap = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -060069 for lib, item in shlib_providers.items():
70 for path, pkg in item.items():
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050071 if path in libpaths:
72 res = libname_re.match(lib)
73 if res:
74 libname = res.group(1)
75 if not libname in pkglibmap:
76 pkglibmap[libname] = pkg[0]
77 else:
78 logger.debug('unable to extract library name from %s' % lib)
79
80 # Now turn it into a library->recipe mapping
Brad Bishop6e60e8b2018-02-01 10:27:11 -050081 pkgdata_dir = d.getVar('PKGDATA_DIR')
Patrick Williamsc0f7c042017-02-23 20:41:17 -060082 for libname, pkg in pkglibmap.items():
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050083 try:
84 with open(os.path.join(pkgdata_dir, 'runtime', pkg)) as f:
85 for line in f:
86 if line.startswith('PN:'):
87 RecipeHandler.recipelibmap[libname] = line.split(':', 1)[-1].strip()
88 break
89 except IOError as ioe:
90 if ioe.errno == 2:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080091 logger.warning('unable to find a pkgdata file for package %s' % pkg)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050092 else:
93 raise
94
95 # Some overrides - these should be mapped to the virtual
96 RecipeHandler.recipelibmap['GL'] = 'virtual/libgl'
97 RecipeHandler.recipelibmap['EGL'] = 'virtual/egl'
98 RecipeHandler.recipelibmap['GLESv2'] = 'virtual/libgles2'
99
100 @staticmethod
101 def load_devel_filemap(d):
102 '''Build up development file->recipe mapping'''
103 if RecipeHandler.recipeheadermap:
104 return
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500105 pkgdata_dir = d.getVar('PKGDATA_DIR')
106 includedir = d.getVar('includedir')
107 cmakedir = os.path.join(d.getVar('libdir'), 'cmake')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500108 for pkg in glob.glob(os.path.join(pkgdata_dir, 'runtime', '*-dev')):
109 with open(os.path.join(pkgdata_dir, 'runtime', pkg)) as f:
110 pn = None
111 headers = []
112 cmakefiles = []
113 for line in f:
114 if line.startswith('PN:'):
115 pn = line.split(':', 1)[-1].strip()
116 elif line.startswith('FILES_INFO:'):
117 val = line.split(':', 1)[1].strip()
118 dictval = json.loads(val)
119 for fullpth in sorted(dictval):
120 if fullpth.startswith(includedir) and fullpth.endswith('.h'):
121 headers.append(os.path.relpath(fullpth, includedir))
122 elif fullpth.startswith(cmakedir) and fullpth.endswith('.cmake'):
123 cmakefiles.append(os.path.relpath(fullpth, cmakedir))
124 if pn and headers:
125 for header in headers:
126 RecipeHandler.recipeheadermap[header] = pn
127 if pn and cmakefiles:
128 for fn in cmakefiles:
129 RecipeHandler.recipecmakefilemap[fn] = pn
130
131 @staticmethod
132 def load_binmap(d):
133 '''Build up native binary->recipe mapping'''
134 if RecipeHandler.recipebinmap:
135 return
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500136 sstate_manifests = d.getVar('SSTATE_MANIFESTS')
137 staging_bindir_native = d.getVar('STAGING_BINDIR_NATIVE')
138 build_arch = d.getVar('BUILD_ARCH')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500139 fileprefix = 'manifest-%s-' % build_arch
140 for fn in glob.glob(os.path.join(sstate_manifests, '%s*-native.populate_sysroot' % fileprefix)):
141 with open(fn, 'r') as f:
142 pn = os.path.basename(fn).rsplit('.', 1)[0][len(fileprefix):]
143 for line in f:
144 if line.startswith(staging_bindir_native):
145 prog = os.path.basename(line.rstrip())
146 RecipeHandler.recipebinmap[prog] = pn
147
148 @staticmethod
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500149 def checkfiles(path, speclist, recursive=False, excludedirs=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500150 results = []
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500151 if recursive:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500152 for root, dirs, files in os.walk(path, topdown=True):
153 if excludedirs:
154 dirs[:] = [d for d in dirs if d not in excludedirs]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500155 for fn in files:
156 for spec in speclist:
157 if fnmatch.fnmatch(fn, spec):
158 results.append(os.path.join(root, fn))
159 else:
160 for spec in speclist:
161 results.extend(glob.glob(os.path.join(path, spec)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500162 return results
163
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500164 @staticmethod
165 def handle_depends(libdeps, pcdeps, deps, outlines, values, d):
166 if pcdeps:
167 recipemap = read_pkgconfig_provides(d)
168 if libdeps:
169 RecipeHandler.load_libmap(d)
170
171 ignorelibs = ['socket']
172 ignoredeps = ['gcc-runtime', 'glibc', 'uclibc', 'musl', 'tar-native', 'binutils-native', 'coreutils-native']
173
174 unmappedpc = []
175 pcdeps = list(set(pcdeps))
176 for pcdep in pcdeps:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600177 if isinstance(pcdep, str):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500178 recipe = recipemap.get(pcdep, None)
179 if recipe:
180 deps.append(recipe)
181 else:
182 if not pcdep.startswith('$'):
183 unmappedpc.append(pcdep)
184 else:
185 for item in pcdep:
186 recipe = recipemap.get(pcdep, None)
187 if recipe:
188 deps.append(recipe)
189 break
190 else:
191 unmappedpc.append('(%s)' % ' or '.join(pcdep))
192
193 unmappedlibs = []
194 for libdep in libdeps:
195 if isinstance(libdep, tuple):
196 lib, header = libdep
197 else:
198 lib = libdep
199 header = None
200
201 if lib in ignorelibs:
202 logger.debug('Ignoring library dependency %s' % lib)
203 continue
204
205 recipe = RecipeHandler.recipelibmap.get(lib, None)
206 if recipe:
207 deps.append(recipe)
208 elif recipe is None:
209 if header:
210 RecipeHandler.load_devel_filemap(d)
211 recipe = RecipeHandler.recipeheadermap.get(header, None)
212 if recipe:
213 deps.append(recipe)
214 elif recipe is None:
215 unmappedlibs.append(lib)
216 else:
217 unmappedlibs.append(lib)
218
219 deps = set(deps).difference(set(ignoredeps))
220
221 if unmappedpc:
222 outlines.append('# NOTE: unable to map the following pkg-config dependencies: %s' % ' '.join(unmappedpc))
223 outlines.append('# (this is based on recipes that have previously been built and packaged)')
224
225 if unmappedlibs:
226 outlines.append('# NOTE: the following library dependencies are unknown, ignoring: %s' % ' '.join(list(set(unmappedlibs))))
227 outlines.append('# (this is based on recipes that have previously been built and packaged)')
228
229 if deps:
230 values['DEPENDS'] = ' '.join(deps)
231
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500232 @staticmethod
233 def genfunction(outlines, funcname, content, python=False, forcespace=False):
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500234 if python:
235 prefix = 'python '
236 else:
237 prefix = ''
238 outlines.append('%s%s () {' % (prefix, funcname))
239 if python or forcespace:
240 indent = ' '
241 else:
242 indent = '\t'
243 addnoop = not python
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500244 for line in content:
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500245 outlines.append('%s%s' % (indent, line))
246 if addnoop:
247 strippedline = line.lstrip()
248 if strippedline and not strippedline.startswith('#'):
249 addnoop = False
250 if addnoop:
251 # Without this there'll be a syntax error
252 outlines.append('%s:' % indent)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500253 outlines.append('}')
254 outlines.append('')
255
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500256 def process(self, srctree, classes, lines_before, lines_after, handled, extravalues):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500257 return False
258
259
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500260def validate_pv(pv):
261 if not pv or '_version' in pv.lower() or pv[0] not in '0123456789':
262 return False
263 return True
264
265def determine_from_filename(srcfile):
266 """Determine name and version from a filename"""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600267 if is_package(srcfile):
268 # Force getting the value from the package metadata
269 return None, None
270
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500271 if '.tar.' in srcfile:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600272 namepart = srcfile.split('.tar.')[0]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500273 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600274 namepart = os.path.splitext(srcfile)[0]
275 namepart = namepart.lower().replace('_', '-')
276 if namepart.endswith('.src'):
277 namepart = namepart[:-4]
278 if namepart.endswith('.orig'):
279 namepart = namepart[:-5]
280 splitval = namepart.split('-')
281 logger.debug('determine_from_filename: split name %s into: %s' % (srcfile, splitval))
282
283 ver_re = re.compile('^v?[0-9]')
284
285 pv = None
286 pn = None
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500287 if len(splitval) == 1:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600288 # Try to split the version out if there is no separator (or a .)
289 res = re.match('^([^0-9]+)([0-9.]+.*)$', namepart)
290 if res:
291 if len(res.group(1)) > 1 and len(res.group(2)) > 1:
292 pn = res.group(1).rstrip('.')
293 pv = res.group(2)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500294 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600295 pn = namepart
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500296 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600297 if splitval[-1] in ['source', 'src']:
298 splitval.pop()
299 if len(splitval) > 2 and re.match('^(alpha|beta|stable|release|rc[0-9]|pre[0-9]|p[0-9]|[0-9]{8})', splitval[-1]) and ver_re.match(splitval[-2]):
300 pv = '-'.join(splitval[-2:])
301 if pv.endswith('-release'):
302 pv = pv[:-8]
303 splitval = splitval[:-2]
304 elif ver_re.match(splitval[-1]):
305 pv = splitval.pop()
306 pn = '-'.join(splitval)
307 if pv and pv.startswith('v'):
308 pv = pv[1:]
309 logger.debug('determine_from_filename: name = "%s" version = "%s"' % (pn, pv))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500310 return (pn, pv)
311
312def determine_from_url(srcuri):
313 """Determine name and version from a URL"""
314 pn = None
315 pv = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600316 parseres = urlparse(srcuri.lower().split(';', 1)[0])
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500317 if parseres.path:
318 if 'github.com' in parseres.netloc:
319 res = re.search(r'.*/(.*?)/archive/(.*)-final\.(tar|zip)', parseres.path)
320 if res:
321 pn = res.group(1).strip().replace('_', '-')
322 pv = res.group(2).strip().replace('_', '.')
323 else:
324 res = re.search(r'.*/(.*?)/archive/v?(.*)\.(tar|zip)', parseres.path)
325 if res:
326 pn = res.group(1).strip().replace('_', '-')
327 pv = res.group(2).strip().replace('_', '.')
328 elif 'bitbucket.org' in parseres.netloc:
329 res = re.search(r'.*/(.*?)/get/[a-zA-Z_-]*([0-9][0-9a-zA-Z_.]*)\.(tar|zip)', parseres.path)
330 if res:
331 pn = res.group(1).strip().replace('_', '-')
332 pv = res.group(2).strip().replace('_', '.')
333
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500334 if not pn and not pv:
335 if parseres.scheme not in ['git', 'gitsm', 'svn', 'hg']:
336 srcfile = os.path.basename(parseres.path.rstrip('/'))
337 pn, pv = determine_from_filename(srcfile)
338 elif parseres.scheme in ['git', 'gitsm']:
339 pn = os.path.basename(parseres.path.rstrip('/')).lower().replace('_', '-')
340 if pn.endswith('.git'):
341 pn = pn[:-4]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500342
343 logger.debug('Determined from source URL: name = "%s", version = "%s"' % (pn, pv))
344 return (pn, pv)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500345
346def supports_srcrev(uri):
347 localdata = bb.data.createCopy(tinfoil.config_data)
348 # This is a bit sad, but if you don't have this set there can be some
349 # odd interactions with the urldata cache which lead to errors
350 localdata.setVar('SRCREV', '${AUTOREV}')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600351 try:
352 fetcher = bb.fetch2.Fetch([uri], localdata)
353 urldata = fetcher.ud
354 for u in urldata:
355 if urldata[u].method.supports_srcrev():
356 return True
357 except bb.fetch2.FetchError as e:
358 logger.debug('FetchError in supports_srcrev: %s' % str(e))
359 # Fall back to basic check
360 if uri.startswith(('git://', 'gitsm://')):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500361 return True
362 return False
363
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500364def reformat_git_uri(uri):
365 '''Convert any http[s]://....git URI into git://...;protocol=http[s]'''
366 checkuri = uri.split(';', 1)[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600367 if checkuri.endswith('.git') or '/git/' in checkuri or re.match('https?://github.com/[^/]+/[^/]+/?$', checkuri):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500368 # Appends scheme if the scheme is missing
369 if not '://' in uri:
370 uri = 'git://' + uri
371 scheme, host, path, user, pswd, parms = bb.fetch2.decodeurl(uri)
372 # Detection mechanism, this is required due to certain URL are formatter with ":" rather than "/"
373 # which causes decodeurl to fail getting the right host and path
374 if len(host.split(':')) > 1:
375 splitslash = host.split(':')
Brad Bishop316dfdd2018-06-25 12:45:53 -0400376 # Port number should not be split from host
377 if not re.match('^[0-9]+$', splitslash[1]):
378 host = splitslash[0]
379 path = '/' + splitslash[1] + path
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500380 #Algorithm:
381 # if user is defined, append protocol=ssh or if a protocol is defined, then honor the user-defined protocol
382 # if no user & password is defined, check for scheme type and append the protocol with the scheme type
383 # finally if protocols or if the url is well-formed, do nothing and rejoin everything back to normal
384 # Need to repackage the arguments for encodeurl, the format is: (scheme, host, path, user, password, OrderedDict([('key', 'value')]))
385 if user:
386 if not 'protocol' in parms:
387 parms.update({('protocol', 'ssh')})
388 elif (scheme == "http" or scheme == 'https' or scheme == 'ssh') and not ('protocol' in parms):
389 parms.update({('protocol', scheme)})
390 # Always append 'git://'
391 fUrl = bb.fetch2.encodeurl(('git', host, path, user, pswd, parms))
392 return fUrl
393 else:
394 return uri
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500395
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600396def is_package(url):
397 '''Check if a URL points to a package'''
398 checkurl = url.split(';', 1)[0]
399 if checkurl.endswith(('.deb', '.ipk', '.rpm', '.srpm')):
400 return True
401 return False
402
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500403def create_recipe(args):
404 import bb.process
405 import tempfile
406 import shutil
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600407 import oe.recipeutils
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500408
409 pkgarch = ""
410 if args.machine:
411 pkgarch = "${MACHINE_ARCH}"
412
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600413 extravalues = {}
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500414 checksums = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500415 tempsrc = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600416 source = args.source
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500417 srcsubdir = ''
418 srcrev = '${AUTOREV}'
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500419 srcbranch = ''
420 scheme = ''
421 storeTagName = ''
422 pv_srcpv = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600423
424 if os.path.isfile(source):
425 source = 'file://%s' % os.path.abspath(source)
426
427 if scriptutils.is_src_url(source):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400428 # Warn about github archive URLs
429 if re.match('https?://github.com/[^/]+/[^/]+/archive/.+(\.tar\..*|\.zip)$', source):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800430 logger.warning('github archive files are not guaranteed to be stable and may be re-generated over time. If the latter occurs, the checksums will likely change and the recipe will fail at do_fetch. It is recommended that you point to an actual commit or tag in the repository instead (using the repository URL in conjunction with the -S/--srcrev option).')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500431 # Fetch a URL
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600432 fetchuri = reformat_git_uri(urldefrag(source)[0])
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500433 if args.binary:
434 # Assume the archive contains the directory structure verbatim
435 # so we need to extract to a subdirectory
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600436 fetchuri += ';subdir=${BP}'
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500437 srcuri = fetchuri
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500438 rev_re = re.compile(';rev=([^;]+)')
439 res = rev_re.search(srcuri)
440 if res:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500441 if args.srcrev:
442 logger.error('rev= parameter and -S/--srcrev option cannot both be specified - use one or the other')
443 sys.exit(1)
444 if args.autorev:
445 logger.error('rev= parameter and -a/--autorev option cannot both be specified - use one or the other')
446 sys.exit(1)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500447 srcrev = res.group(1)
448 srcuri = rev_re.sub('', srcuri)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500449 elif args.srcrev:
450 srcrev = args.srcrev
451
452 # Check whether users provides any branch info in fetchuri.
453 # If true, we will skip all branch checking process to honor all user's input.
454 scheme, network, path, user, passwd, params = bb.fetch2.decodeurl(fetchuri)
455 srcbranch = params.get('branch')
456 if args.srcbranch:
457 if srcbranch:
458 logger.error('branch= parameter and -B/--srcbranch option cannot both be specified - use one or the other')
459 sys.exit(1)
460 srcbranch = args.srcbranch
461 nobranch = params.get('nobranch')
462 if nobranch and srcbranch:
463 logger.error('nobranch= cannot be used if you specify a branch')
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500464 sys.exit(1)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500465 tag = params.get('tag')
466 if not srcbranch and not nobranch and srcrev != '${AUTOREV}':
467 # Append nobranch=1 in the following conditions:
468 # 1. User did not set 'branch=' in srcuri, and
469 # 2. User did not set 'nobranch=1' in srcuri, and
470 # 3. Source revision is not '${AUTOREV}'
471 params['nobranch'] = '1'
472 if tag:
473 # Keep a copy of tag and append nobranch=1 then remove tag from URL.
474 # Bitbake fetcher unable to fetch when {AUTOREV} and tag is set at the same time.
475 storeTagName = params['tag']
476 params['nobranch'] = '1'
477 del params['tag']
478 if scheme == 'npm':
479 params['noverify'] = '1'
480 fetchuri = bb.fetch2.encodeurl((scheme, network, path, user, passwd, params))
481
482 tmpparent = tinfoil.config_data.getVar('BASE_WORKDIR')
483 bb.utils.mkdirhier(tmpparent)
484 tempsrc = tempfile.mkdtemp(prefix='recipetool-', dir=tmpparent)
485 srctree = os.path.join(tempsrc, 'source')
486
487 try:
488 checksums, ftmpdir = scriptutils.fetch_url(tinfoil, fetchuri, srcrev, srctree, logger, preserve_tmp=args.keep_temp)
489 except scriptutils.FetchUrlFailure as e:
490 logger.error(str(e))
491 sys.exit(1)
492
493 if ftmpdir and args.keep_temp:
494 logger.info('Fetch temp directory is %s' % ftmpdir)
495
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500496 dirlist = os.listdir(srctree)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500497 filterout = ['git.indirectionsymlink']
498 dirlist = [x for x in dirlist if x not in filterout]
499 logger.debug('Directory listing (excluding filtered out):\n %s' % '\n '.join(dirlist))
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500500 if len(dirlist) == 1:
501 singleitem = os.path.join(srctree, dirlist[0])
502 if os.path.isdir(singleitem):
503 # We unpacked a single directory, so we should use that
504 srcsubdir = dirlist[0]
505 srctree = os.path.join(srctree, srcsubdir)
506 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500507 check_single_file(dirlist[0], fetchuri)
508 elif len(dirlist) == 0:
509 if '/' in fetchuri:
510 fn = os.path.join(tinfoil.config_data.getVar('DL_DIR'), fetchuri.split('/')[-1])
511 if os.path.isfile(fn):
512 check_single_file(fn, fetchuri)
513 # If we've got to here then there's no source so we might as well give up
514 logger.error('URL %s resulted in an empty source tree' % fetchuri)
515 sys.exit(1)
516
517 # We need this checking mechanism to improve the recipe created by recipetool and devtool
518 # is able to parse and build by bitbake.
519 # If there is no input for branch name, then check for branch name with SRCREV provided.
520 if not srcbranch and not nobranch and srcrev and (srcrev != '${AUTOREV}') and scheme in ['git', 'gitsm']:
521 try:
522 cmd = 'git branch -r --contains'
523 check_branch, check_branch_err = bb.process.run('%s %s' % (cmd, srcrev), cwd=srctree)
524 except bb.process.ExecutionError as err:
525 logger.error(str(err))
526 sys.exit(1)
527 get_branch = [x.strip() for x in check_branch.splitlines()]
528 # Remove HEAD reference point and drop remote prefix
529 get_branch = [x.split('/', 1)[1] for x in get_branch if not x.startswith('origin/HEAD')]
530 if 'master' in get_branch:
531 # If it is master, we do not need to append 'branch=master' as this is default.
532 # Even with the case where get_branch has multiple objects, if 'master' is one
533 # of them, we should default take from 'master'
534 srcbranch = ''
535 elif len(get_branch) == 1:
536 # If 'master' isn't in get_branch and get_branch contains only ONE object, then store result into 'srcbranch'
537 srcbranch = get_branch[0]
538 else:
539 # If get_branch contains more than one objects, then display error and exit.
540 mbrch = '\n ' + '\n '.join(get_branch)
541 logger.error('Revision %s was found on multiple branches: %s\nPlease provide the correct branch with -B/--srcbranch' % (srcrev, mbrch))
542 sys.exit(1)
543
544 # Since we might have a value in srcbranch, we need to
545 # recontruct the srcuri to include 'branch' in params.
546 scheme, network, path, user, passwd, params = bb.fetch2.decodeurl(srcuri)
547 if srcbranch:
548 params['branch'] = srcbranch
549
550 if storeTagName and scheme in ['git', 'gitsm']:
551 # Check srcrev using tag and check validity of the tag
552 cmd = ('git rev-parse --verify %s' % (storeTagName))
553 try:
554 check_tag, check_tag_err = bb.process.run('%s' % cmd, cwd=srctree)
555 srcrev = check_tag.split()[0]
556 except bb.process.ExecutionError as err:
557 logger.error(str(err))
558 logger.error("Possibly wrong tag name is provided")
559 sys.exit(1)
560 # Drop tag from srcuri as it will have conflicts with SRCREV during recipe parse.
561 del params['tag']
562 srcuri = bb.fetch2.encodeurl((scheme, network, path, user, passwd, params))
563
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600564 if os.path.exists(os.path.join(srctree, '.gitmodules')) and srcuri.startswith('git://'):
565 srcuri = 'gitsm://' + srcuri[6:]
566 logger.info('Fetching submodules...')
567 bb.process.run('git submodule update --init --recursive', cwd=srctree)
568
569 if is_package(fetchuri):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500570 localdata = bb.data.createCopy(tinfoil.config_data)
571 pkgfile = bb.fetch2.localpath(fetchuri, localdata)
572 if pkgfile:
573 tmpfdir = tempfile.mkdtemp(prefix='recipetool-')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600574 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600575 if pkgfile.endswith(('.deb', '.ipk')):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500576 stdout, _ = bb.process.run('ar x %s' % pkgfile, cwd=tmpfdir)
577 stdout, _ = bb.process.run('tar xf control.tar.gz', cwd=tmpfdir)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600578 values = convert_debian(tmpfdir)
579 extravalues.update(values)
580 elif pkgfile.endswith(('.rpm', '.srpm')):
581 stdout, _ = bb.process.run('rpm -qp --xml %s > pkginfo.xml' % pkgfile, cwd=tmpfdir)
582 values = convert_rpm_xml(os.path.join(tmpfdir, 'pkginfo.xml'))
583 extravalues.update(values)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500584 finally:
585 shutil.rmtree(tmpfdir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500586 else:
587 # Assume we're pointing to an existing source tree
588 if args.extract_to:
589 logger.error('--extract-to cannot be specified if source is a directory')
590 sys.exit(1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600591 if not os.path.isdir(source):
592 logger.error('Invalid source directory %s' % source)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500593 sys.exit(1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600594 srctree = source
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500595 srcuri = ''
596 if os.path.exists(os.path.join(srctree, '.git')):
597 # Try to get upstream repo location from origin remote
598 try:
599 stdout, _ = bb.process.run('git remote -v', cwd=srctree, shell=True)
600 except bb.process.ExecutionError as e:
601 stdout = None
602 if stdout:
603 for line in stdout.splitlines():
604 splitline = line.split()
605 if len(splitline) > 1:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600606 if splitline[0] == 'origin' and scriptutils.is_src_url(splitline[1]):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500607 srcuri = reformat_git_uri(splitline[1])
608 srcsubdir = 'git'
609 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500610
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500611 if args.src_subdir:
612 srcsubdir = os.path.join(srcsubdir, args.src_subdir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500613 srctree_use = os.path.abspath(os.path.join(srctree, args.src_subdir))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500614 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500615 srctree_use = os.path.abspath(srctree)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500616
617 if args.outfile and os.path.isdir(args.outfile):
618 outfile = None
619 outdir = args.outfile
620 else:
621 outfile = args.outfile
622 outdir = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500623 if outfile and outfile != '-':
624 if os.path.exists(outfile):
625 logger.error('Output file %s already exists' % outfile)
626 sys.exit(1)
627
628 lines_before = []
629 lines_after = []
630
631 lines_before.append('# Recipe created by %s' % os.path.basename(sys.argv[0]))
632 lines_before.append('# This is the basis of a recipe and may need further editing in order to be fully functional.')
633 lines_before.append('# (Feel free to remove these comments when editing.)')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600634 # We need a blank line here so that patch_recipe_lines can rewind before the LICENSE comments
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500635 lines_before.append('')
636
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500637 # We'll come back and replace this later in handle_license_vars()
638 lines_before.append('##LICENSE_PLACEHOLDER##')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600639
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500640 handled = []
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500641 classes = []
642
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500643 # FIXME This is kind of a hack, we probably ought to be using bitbake to do this
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500644 pn = None
645 pv = None
646 if outfile:
647 recipefn = os.path.splitext(os.path.basename(outfile))[0]
648 fnsplit = recipefn.split('_')
649 if len(fnsplit) > 1:
650 pn = fnsplit[0]
651 pv = fnsplit[1]
652 else:
653 pn = recipefn
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500654
655 if args.version:
656 pv = args.version
657
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500658 if args.name:
659 pn = args.name
660 if args.name.endswith('-native'):
661 if args.also_native:
662 logger.error('--also-native cannot be specified for a recipe named *-native (*-native denotes a recipe that is already only for native) - either remove the -native suffix from the name or drop --also-native')
663 sys.exit(1)
664 classes.append('native')
665 elif args.name.startswith('nativesdk-'):
666 if args.also_native:
667 logger.error('--also-native cannot be specified for a recipe named nativesdk-* (nativesdk-* denotes a recipe that is already only for nativesdk)')
668 sys.exit(1)
669 classes.append('nativesdk')
670
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500671 if pv and pv not in 'git svn hg'.split():
672 realpv = pv
673 else:
674 realpv = None
675
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500676 if not srcuri:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500677 lines_before.append('# No information for SRC_URI yet (only an external source tree was specified)')
678 lines_before.append('SRC_URI = "%s"' % srcuri)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500679 for key, value in sorted(checksums.items()):
680 lines_before.append('SRC_URI[%s] = "%s"' % (key, value))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500681 if srcuri and supports_srcrev(srcuri):
682 lines_before.append('')
683 lines_before.append('# Modify these as desired')
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500684 # Note: we have code to replace realpv further down if it gets set to some other value
685 scheme, _, _, _, _, _ = bb.fetch2.decodeurl(srcuri)
686 if scheme in ['git', 'gitsm']:
687 srcpvprefix = 'git'
688 elif scheme == 'svn':
689 srcpvprefix = 'svnr'
690 else:
691 srcpvprefix = scheme
692 lines_before.append('PV = "%s+%s${SRCPV}"' % (realpv or '1.0', srcpvprefix))
693 pv_srcpv = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600694 if not args.autorev and srcrev == '${AUTOREV}':
695 if os.path.exists(os.path.join(srctree, '.git')):
696 (stdout, _) = bb.process.run('git rev-parse HEAD', cwd=srctree)
Brad Bishopc342db32019-05-15 21:57:59 -0400697 srcrev = stdout.rstrip()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500698 lines_before.append('SRCREV = "%s"' % srcrev)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500699 if args.provides:
700 lines_before.append('PROVIDES = "%s"' % args.provides)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500701 lines_before.append('')
702
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600703 if srcsubdir and not args.binary:
704 # (for binary packages we explicitly specify subdir= when fetching to
705 # match the default value of S, so we don't need to set it in that case)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500706 lines_before.append('S = "${WORKDIR}/%s"' % srcsubdir)
707 lines_before.append('')
708
709 if pkgarch:
710 lines_after.append('PACKAGE_ARCH = "%s"' % pkgarch)
711 lines_after.append('')
712
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500713 if args.binary:
714 lines_after.append('INSANE_SKIP_${PN} += "already-stripped"')
715 lines_after.append('')
716
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500717 if args.fetch_dev:
718 extravalues['fetchdev'] = True
719 else:
720 extravalues['fetchdev'] = None
721
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500722 # Find all plugins that want to register handlers
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500723 logger.debug('Loading recipe handlers')
724 raw_handlers = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500725 for plugin in plugins:
726 if hasattr(plugin, 'register_recipe_handlers'):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500727 plugin.register_recipe_handlers(raw_handlers)
728 # Sort handlers by priority
729 handlers = []
730 for i, handler in enumerate(raw_handlers):
731 if isinstance(handler, tuple):
732 handlers.append((handler[0], handler[1], i))
733 else:
734 handlers.append((handler, 0, i))
735 handlers.sort(key=lambda item: (item[1], -item[2]), reverse=True)
736 for handler, priority, _ in handlers:
737 logger.debug('Handler: %s (priority %d)' % (handler.__class__.__name__, priority))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500738 setattr(handler, '_devtool', args.devtool)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500739 handlers = [item[0] for item in handlers]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500740
741 # Apply the handlers
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500742 if args.binary:
743 classes.append('bin_package')
744 handled.append('buildsystem')
745
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500746 for handler in handlers:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500747 handler.process(srctree_use, classes, lines_before, lines_after, handled, extravalues)
748
749 extrafiles = extravalues.pop('extrafiles', {})
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600750 extra_pn = extravalues.pop('PN', None)
751 extra_pv = extravalues.pop('PV', None)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500752
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600753 if extra_pv and not realpv:
754 realpv = extra_pv
755 if not validate_pv(realpv):
756 realpv = None
757 else:
758 realpv = realpv.lower().split()[0]
759 if '_' in realpv:
760 realpv = realpv.replace('_', '-')
761 if extra_pn and not pn:
762 pn = extra_pn
763 if pn.startswith('GNU '):
764 pn = pn[4:]
765 if ' ' in pn:
766 # Probably a descriptive identifier rather than a proper name
767 pn = None
768 else:
769 pn = pn.lower()
770 if '_' in pn:
771 pn = pn.replace('_', '-')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500772
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500773 if srcuri and not realpv or not pn:
774 name_pn, name_pv = determine_from_url(srcuri)
775 if name_pn and not pn:
776 pn = name_pn
777 if name_pv and not realpv:
778 realpv = name_pv
779
780 licvalues = handle_license_vars(srctree_use, lines_before, handled, extravalues, tinfoil.config_data)
781
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500782 if not outfile:
783 if not pn:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500784 log_error_cond('Unable to determine short program name from source tree - please specify name with -N/--name or output file name with -o/--outfile', args.devtool)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500785 # devtool looks for this specific exit code, so don't change it
786 sys.exit(15)
787 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600788 if srcuri and srcuri.startswith(('gitsm://', 'git://', 'hg://', 'svn://')):
789 suffix = srcuri.split(':', 1)[0]
790 if suffix == 'gitsm':
791 suffix = 'git'
792 outfile = '%s_%s.bb' % (pn, suffix)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500793 elif realpv:
794 outfile = '%s_%s.bb' % (pn, realpv)
795 else:
796 outfile = '%s.bb' % pn
797 if outdir:
798 outfile = os.path.join(outdir, outfile)
799 # We need to check this again
800 if os.path.exists(outfile):
801 logger.error('Output file %s already exists' % outfile)
802 sys.exit(1)
803
804 # Move any extra files the plugins created to a directory next to the recipe
805 if extrafiles:
806 if outfile == '-':
807 extraoutdir = pn
808 else:
809 extraoutdir = os.path.join(os.path.dirname(outfile), pn)
810 bb.utils.mkdirhier(extraoutdir)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600811 for destfn, extrafile in extrafiles.items():
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500812 shutil.move(extrafile, os.path.join(extraoutdir, destfn))
813
814 lines = lines_before
815 lines_before = []
816 skipblank = True
817 for line in lines:
818 if skipblank:
819 skipblank = False
820 if not line:
821 continue
822 if line.startswith('S = '):
823 if realpv and pv not in 'git svn hg'.split():
824 line = line.replace(realpv, '${PV}')
825 if pn:
826 line = line.replace(pn, '${BPN}')
827 if line == 'S = "${WORKDIR}/${BPN}-${PV}"':
828 skipblank = True
829 continue
830 elif line.startswith('SRC_URI = '):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500831 if realpv and not pv_srcpv:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500832 line = line.replace(realpv, '${PV}')
833 elif line.startswith('PV = '):
834 if realpv:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500835 # Replace the first part of the PV value
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500836 line = re.sub('"[^+]*\+', '"%s+' % realpv, line)
837 lines_before.append(line)
838
839 if args.also_native:
840 lines = lines_after
841 lines_after = []
842 bbclassextend = None
843 for line in lines:
844 if line.startswith('BBCLASSEXTEND ='):
845 splitval = line.split('"')
846 if len(splitval) > 1:
847 bbclassextend = splitval[1].split()
848 if not 'native' in bbclassextend:
849 bbclassextend.insert(0, 'native')
850 line = 'BBCLASSEXTEND = "%s"' % ' '.join(bbclassextend)
851 lines_after.append(line)
852 if not bbclassextend:
853 lines_after.append('BBCLASSEXTEND = "native"')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500854
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500855 postinst = ("postinst", extravalues.pop('postinst', None))
856 postrm = ("postrm", extravalues.pop('postrm', None))
857 preinst = ("preinst", extravalues.pop('preinst', None))
858 prerm = ("prerm", extravalues.pop('prerm', None))
859 funcs = [postinst, postrm, preinst, prerm]
860 for func in funcs:
861 if func[1]:
862 RecipeHandler.genfunction(lines_after, 'pkg_%s_${PN}' % func[0], func[1])
863
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500864 outlines = []
865 outlines.extend(lines_before)
866 if classes:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500867 if outlines[-1] and not outlines[-1].startswith('#'):
868 outlines.append('')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500869 outlines.append('inherit %s' % ' '.join(classes))
870 outlines.append('')
871 outlines.extend(lines_after)
872
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600873 if extravalues:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600874 _, outlines = oe.recipeutils.patch_recipe_lines(outlines, extravalues, trailing_newline=False)
875
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500876 if args.extract_to:
877 scriptutils.git_convert_standalone_clone(srctree)
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500878 if os.path.isdir(args.extract_to):
879 # If the directory exists we'll move the temp dir into it instead of
880 # its contents - of course, we could try to always move its contents
881 # but that is a pain if there are symlinks; the simplest solution is
882 # to just remove it first
883 os.rmdir(args.extract_to)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500884 shutil.move(srctree, args.extract_to)
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500885 if tempsrc == srctree:
886 tempsrc = None
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500887 log_info_cond('Source extracted to %s' % args.extract_to, args.devtool)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500888
889 if outfile == '-':
890 sys.stdout.write('\n'.join(outlines) + '\n')
891 else:
892 with open(outfile, 'w') as f:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600893 lastline = None
894 for line in outlines:
895 if not lastline and not line:
896 # Skip extra blank lines
897 continue
898 f.write('%s\n' % line)
899 lastline = line
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500900 log_info_cond('Recipe %s has been created; further editing may be required to make it fully functional' % outfile, args.devtool)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500901
902 if tempsrc:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600903 if args.keep_temp:
904 logger.info('Preserving temporary directory %s' % tempsrc)
905 else:
906 shutil.rmtree(tempsrc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500907
908 return 0
909
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500910def check_single_file(fn, fetchuri):
911 """Determine if a single downloaded file is something we can't handle"""
912 with open(fn, 'r', errors='surrogateescape') as f:
913 if '<html' in f.read(100).lower():
914 logger.error('Fetching "%s" returned a single HTML page - check the URL is correct and functional' % fetchuri)
915 sys.exit(1)
916
917def split_value(value):
918 if isinstance(value, str):
919 return value.split()
920 else:
921 return value
922
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600923def handle_license_vars(srctree, lines_before, handled, extravalues, d):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500924 lichandled = [x for x in handled if x[0] == 'license']
925 if lichandled:
926 # Someone else has already handled the license vars, just return their value
927 return lichandled[0][1]
928
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600929 licvalues = guess_license(srctree, d)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500930 licenses = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600931 lic_files_chksum = []
932 lic_unknown = []
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500933 lines = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600934 if licvalues:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600935 for licvalue in licvalues:
936 if not licvalue[0] in licenses:
937 licenses.append(licvalue[0])
938 lic_files_chksum.append('file://%s;md5=%s' % (licvalue[1], licvalue[2]))
939 if licvalue[0] == 'Unknown':
940 lic_unknown.append(licvalue[1])
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600941 if lic_unknown:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500942 lines.append('#')
943 lines.append('# The following license files were not able to be identified and are')
944 lines.append('# represented as "Unknown" below, you will need to check them yourself:')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600945 for licfile in lic_unknown:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500946 lines.append('# %s' % licfile)
947
948 extra_license = split_value(extravalues.pop('LICENSE', []))
949 if '&' in extra_license:
950 extra_license.remove('&')
951 if extra_license:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600952 if licenses == ['Unknown']:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500953 licenses = extra_license
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600954 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500955 for item in extra_license:
956 if item not in licenses:
957 licenses.append(item)
958 extra_lic_files_chksum = split_value(extravalues.pop('LIC_FILES_CHKSUM', []))
959 for item in extra_lic_files_chksum:
960 if item not in lic_files_chksum:
961 lic_files_chksum.append(item)
962
963 if lic_files_chksum:
964 # We are going to set the vars, so prepend the standard disclaimer
965 lines.insert(0, '# WARNING: the following LICENSE and LIC_FILES_CHKSUM values are best guesses - it is')
966 lines.insert(1, '# your responsibility to verify that the values are complete and correct.')
967 else:
968 # Without LIC_FILES_CHKSUM we set LICENSE = "CLOSED" to allow the
969 # user to get started easily
970 lines.append('# Unable to find any files that looked like license statements. Check the accompanying')
971 lines.append('# documentation and source headers and set LICENSE and LIC_FILES_CHKSUM accordingly.')
972 lines.append('#')
973 lines.append('# NOTE: LICENSE is being set to "CLOSED" to allow you to at least start building - if')
974 lines.append('# this is not accurate with respect to the licensing of the software being built (it')
975 lines.append('# will not be in most cases) you must specify the correct value before using this')
976 lines.append('# recipe for anything other than initial testing/development!')
977 licenses = ['CLOSED']
978
979 if extra_license and sorted(licenses) != sorted(extra_license):
980 lines.append('# NOTE: Original package / source metadata indicates license is: %s' % ' & '.join(extra_license))
981
982 if len(licenses) > 1:
983 lines.append('#')
984 lines.append('# NOTE: multiple licenses have been detected; they have been separated with &')
985 lines.append('# in the LICENSE value for now since it is a reasonable assumption that all')
986 lines.append('# of the licenses apply. If instead there is a choice between the multiple')
987 lines.append('# licenses then you should change the value to separate the licenses with |')
988 lines.append('# instead of &. If there is any doubt, check the accompanying documentation')
989 lines.append('# to determine which situation is applicable.')
990
991 lines.append('LICENSE = "%s"' % ' & '.join(licenses))
992 lines.append('LIC_FILES_CHKSUM = "%s"' % ' \\\n '.join(lic_files_chksum))
993 lines.append('')
994
995 # Replace the placeholder so we get the values in the right place in the recipe file
996 try:
997 pos = lines_before.index('##LICENSE_PLACEHOLDER##')
998 except ValueError:
999 pos = -1
1000 if pos == -1:
1001 lines_before.extend(lines)
1002 else:
1003 lines_before[pos:pos+1] = lines
1004
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001005 handled.append(('license', licvalues))
1006 return licvalues
1007
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001008def get_license_md5sums(d, static_only=False):
1009 import bb.utils
1010 md5sums = {}
1011 if not static_only:
1012 # Gather md5sums of license files in common license dir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001013 commonlicdir = d.getVar('COMMON_LICENSE_DIR')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001014 for fn in os.listdir(commonlicdir):
1015 md5value = bb.utils.md5_file(os.path.join(commonlicdir, fn))
1016 md5sums[md5value] = fn
1017 # The following were extracted from common values in various recipes
1018 # (double checking the license against the license file itself, not just
1019 # the LICENSE value in the recipe)
1020 md5sums['94d55d512a9ba36caa9b7df079bae19f'] = 'GPLv2'
1021 md5sums['b234ee4d69f5fce4486a80fdaf4a4263'] = 'GPLv2'
1022 md5sums['59530bdf33659b29e73d4adb9f9f6552'] = 'GPLv2'
1023 md5sums['0636e73ff0215e8d672dc4c32c317bb3'] = 'GPLv2'
1024 md5sums['eb723b61539feef013de476e68b5c50a'] = 'GPLv2'
1025 md5sums['751419260aa954499f7abaabaa882bbe'] = 'GPLv2'
1026 md5sums['393a5ca445f6965873eca0259a17f833'] = 'GPLv2'
1027 md5sums['12f884d2ae1ff87c09e5b7ccc2c4ca7e'] = 'GPLv2'
1028 md5sums['8ca43cbc842c2336e835926c2166c28b'] = 'GPLv2'
1029 md5sums['ebb5c50ab7cab4baeffba14977030c07'] = 'GPLv2'
1030 md5sums['c93c0550bd3173f4504b2cbd8991e50b'] = 'GPLv2'
1031 md5sums['9ac2e7cff1ddaf48b6eab6028f23ef88'] = 'GPLv2'
1032 md5sums['4325afd396febcb659c36b49533135d4'] = 'GPLv2'
1033 md5sums['18810669f13b87348459e611d31ab760'] = 'GPLv2'
1034 md5sums['d7810fab7487fb0aad327b76f1be7cd7'] = 'GPLv2' # the Linux kernel's COPYING file
1035 md5sums['bbb461211a33b134d42ed5ee802b37ff'] = 'LGPLv2.1'
1036 md5sums['7fbc338309ac38fefcd64b04bb903e34'] = 'LGPLv2.1'
1037 md5sums['4fbd65380cdd255951079008b364516c'] = 'LGPLv2.1'
1038 md5sums['2d5025d4aa3495befef8f17206a5b0a1'] = 'LGPLv2.1'
1039 md5sums['fbc093901857fcd118f065f900982c24'] = 'LGPLv2.1'
1040 md5sums['a6f89e2100d9b6cdffcea4f398e37343'] = 'LGPLv2.1'
1041 md5sums['d8045f3b8f929c1cb29a1e3fd737b499'] = 'LGPLv2.1'
1042 md5sums['fad9b3332be894bab9bc501572864b29'] = 'LGPLv2.1'
1043 md5sums['3bf50002aefd002f49e7bb854063f7e7'] = 'LGPLv2'
1044 md5sums['9f604d8a4f8e74f4f5140845a21b6674'] = 'LGPLv2'
1045 md5sums['5f30f0716dfdd0d91eb439ebec522ec2'] = 'LGPLv2'
1046 md5sums['55ca817ccb7d5b5b66355690e9abc605'] = 'LGPLv2'
1047 md5sums['252890d9eee26aab7b432e8b8a616475'] = 'LGPLv2'
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001048 md5sums['3214f080875748938ba060314b4f727d'] = 'LGPLv2'
1049 md5sums['db979804f025cf55aabec7129cb671ed'] = 'LGPLv2'
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001050 md5sums['d32239bcb673463ab874e80d47fae504'] = 'GPLv3'
1051 md5sums['f27defe1e96c2e1ecd4e0c9be8967949'] = 'GPLv3'
1052 md5sums['6a6a8e020838b23406c81b19c1d46df6'] = 'LGPLv3'
1053 md5sums['3b83ef96387f14655fc854ddc3c6bd57'] = 'Apache-2.0'
1054 md5sums['385c55653886acac3821999a3ccd17b3'] = 'Artistic-1.0 | GPL-2.0' # some perl modules
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001055 md5sums['54c7042be62e169199200bc6477f04d1'] = 'BSD-3-Clause'
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001056 return md5sums
1057
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001058def crunch_license(licfile):
1059 '''
1060 Remove non-material text from a license file and then check
1061 its md5sum against a known list. This works well for licenses
1062 which contain a copyright statement, but is also a useful way
1063 to handle people's insistence upon reformatting the license text
1064 slightly (with no material difference to the text of the
1065 license).
1066 '''
1067
1068 import oe.utils
1069
1070 # Note: these are carefully constructed!
1071 license_title_re = re.compile('^\(?(#+ *)?(The )?.{1,10} [Ll]icen[sc]e( \(.{1,10}\))?\)?:?$')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001072 license_statement_re = re.compile('^(This (project|software) is( free software)? (released|licen[sc]ed)|(Released|Licen[cs]ed)) under the .{1,10} [Ll]icen[sc]e:?$')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001073 copyright_re = re.compile('^(#+)? *Copyright .*$')
1074
1075 crunched_md5sums = {}
1076 # The following two were gleaned from the "forever" npm package
1077 crunched_md5sums['0a97f8e4cbaf889d6fa51f84b89a79f6'] = 'ISC'
1078 crunched_md5sums['eecf6429523cbc9693547cf2db790b5c'] = 'MIT'
1079 # https://github.com/vasi/pixz/blob/master/LICENSE
1080 crunched_md5sums['2f03392b40bbe663597b5bd3cc5ebdb9'] = 'BSD-2-Clause'
1081 # https://github.com/waffle-gl/waffle/blob/master/LICENSE.txt
1082 crunched_md5sums['e72e5dfef0b1a4ca8a3d26a60587db66'] = 'BSD-2-Clause'
1083 # https://github.com/spigwitmer/fakeds1963s/blob/master/LICENSE
1084 crunched_md5sums['8be76ac6d191671f347ee4916baa637e'] = 'GPLv2'
1085 # https://github.com/datto/dattobd/blob/master/COPYING
1086 # http://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/docs/GPLv2.TXT
1087 crunched_md5sums['1d65c5ad4bf6489f85f4812bf08ae73d'] = 'GPLv2'
1088 # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
1089 # http://git.neil.brown.name/?p=mdadm.git;a=blob;f=COPYING;h=d159169d1050894d3ea3b98e1c965c4058208fe1;hb=HEAD
1090 crunched_md5sums['fb530f66a7a89ce920f0e912b5b66d4b'] = 'GPLv2'
1091 # https://github.com/gkos/nrf24/blob/master/COPYING
1092 crunched_md5sums['7b6aaa4daeafdfa6ed5443fd2684581b'] = 'GPLv2'
1093 # https://github.com/josch09/resetusb/blob/master/COPYING
1094 crunched_md5sums['8b8ac1d631a4d220342e83bcf1a1fbc3'] = 'GPLv3'
1095 # https://github.com/FFmpeg/FFmpeg/blob/master/COPYING.LGPLv2.1
1096 crunched_md5sums['2ea316ed973ae176e502e2297b574bb3'] = 'LGPLv2.1'
1097 # unixODBC-2.3.4 COPYING
1098 crunched_md5sums['1daebd9491d1e8426900b4fa5a422814'] = 'LGPLv2.1'
1099 # https://github.com/FFmpeg/FFmpeg/blob/master/COPYING.LGPLv3
1100 crunched_md5sums['2ebfb3bb49b9a48a075cc1425e7f4129'] = 'LGPLv3'
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001101 # https://raw.githubusercontent.com/eclipse/mosquitto/v1.4.14/epl-v10
1102 crunched_md5sums['efe2cb9a35826992b9df68224e3c2628'] = 'EPL-1.0'
1103 # https://raw.githubusercontent.com/eclipse/mosquitto/v1.4.14/edl-v10
1104 crunched_md5sums['0a9c78c0a398d1bbce4a166757d60387'] = 'EDL-1.0'
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001105 lictext = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001106 with open(licfile, 'r', errors='surrogateescape') as f:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001107 for line in f:
1108 # Drop opening statements
1109 if copyright_re.match(line):
1110 continue
1111 elif license_title_re.match(line):
1112 continue
1113 elif license_statement_re.match(line):
1114 continue
1115 # Squash spaces, and replace smart quotes, double quotes
1116 # and backticks with single quotes
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001117 line = oe.utils.squashspaces(line.strip())
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001118 line = line.replace(u"\u2018", "'").replace(u"\u2019", "'").replace(u"\u201c","'").replace(u"\u201d", "'").replace('"', '\'').replace('`', '\'')
1119 if line:
1120 lictext.append(line)
1121
1122 m = hashlib.md5()
1123 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001124 m.update(' '.join(lictext).encode('utf-8'))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001125 md5val = m.hexdigest()
1126 except UnicodeEncodeError:
1127 md5val = None
1128 lictext = ''
1129 license = crunched_md5sums.get(md5val, None)
1130 return license, md5val, lictext
1131
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001132def guess_license(srctree, d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001133 import bb
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001134 md5sums = get_license_md5sums(d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001135
1136 licenses = []
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001137 licspecs = ['*LICEN[CS]E*', 'COPYING*', '*[Ll]icense*', 'LEGAL*', '[Ll]egal*', '*GPL*', 'README.lic*', 'COPYRIGHT*', '[Cc]opyright*', 'e[dp]l-v10']
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001138 licfiles = []
1139 for root, dirs, files in os.walk(srctree):
1140 for fn in files:
1141 for spec in licspecs:
1142 if fnmatch.fnmatch(fn, spec):
1143 fullpath = os.path.join(root, fn)
1144 if not fullpath in licfiles:
1145 licfiles.append(fullpath)
1146 for licfile in licfiles:
1147 md5value = bb.utils.md5_file(licfile)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001148 license = md5sums.get(md5value, None)
1149 if not license:
1150 license, crunched_md5, lictext = crunch_license(licfile)
1151 if not license:
1152 license = 'Unknown'
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001153 licenses.append((license, os.path.relpath(licfile, srctree), md5value))
1154
1155 # FIXME should we grab at least one source file with a license header and add that too?
1156
1157 return licenses
1158
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001159def split_pkg_licenses(licvalues, packages, outlines, fallback_licenses=None, pn='${PN}'):
1160 """
1161 Given a list of (license, path, md5sum) as returned by guess_license(),
1162 a dict of package name to path mappings, write out a set of
1163 package-specific LICENSE values.
1164 """
1165 pkglicenses = {pn: []}
1166 for license, licpath, _ in licvalues:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001167 for pkgname, pkgpath in packages.items():
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001168 if licpath.startswith(pkgpath + '/'):
1169 if pkgname in pkglicenses:
1170 pkglicenses[pkgname].append(license)
1171 else:
1172 pkglicenses[pkgname] = [license]
1173 break
1174 else:
1175 # Accumulate on the main package
1176 pkglicenses[pn].append(license)
1177 outlicenses = {}
1178 for pkgname in packages:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001179 license = ' '.join(list(set(pkglicenses.get(pkgname, ['Unknown'])))) or 'Unknown'
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001180 if license == 'Unknown' and pkgname in fallback_licenses:
1181 license = fallback_licenses[pkgname]
1182 outlines.append('LICENSE_%s = "%s"' % (pkgname, license))
1183 outlicenses[pkgname] = license.split()
1184 return outlicenses
1185
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001186def read_pkgconfig_provides(d):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001187 pkgdatadir = d.getVar('PKGDATA_DIR')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001188 pkgmap = {}
1189 for fn in glob.glob(os.path.join(pkgdatadir, 'shlibs2', '*.pclist')):
1190 with open(fn, 'r') as f:
1191 for line in f:
1192 pkgmap[os.path.basename(line.rstrip())] = os.path.splitext(os.path.basename(fn))[0]
1193 recipemap = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001194 for pc, pkg in pkgmap.items():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001195 pkgdatafile = os.path.join(pkgdatadir, 'runtime', pkg)
1196 if os.path.exists(pkgdatafile):
1197 with open(pkgdatafile, 'r') as f:
1198 for line in f:
1199 if line.startswith('PN: '):
1200 recipemap[pc] = line.split(':', 1)[1].strip()
1201 return recipemap
1202
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001203def convert_debian(debpath):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001204 value_map = {'Package': 'PN',
1205 'Version': 'PV',
1206 'Section': 'SECTION',
1207 'License': 'LICENSE',
1208 'Homepage': 'HOMEPAGE'}
1209
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001210 # FIXME extend this mapping - perhaps use distro_alias.inc?
1211 depmap = {'libz-dev': 'zlib'}
1212
1213 values = {}
1214 depends = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001215 with open(os.path.join(debpath, 'control'), 'r', errors='surrogateescape') as f:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001216 indesc = False
1217 for line in f:
1218 if indesc:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001219 if line.startswith(' '):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001220 if line.startswith(' This package contains'):
1221 indesc = False
1222 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001223 if 'DESCRIPTION' in values:
1224 values['DESCRIPTION'] += ' ' + line.strip()
1225 else:
1226 values['DESCRIPTION'] = line.strip()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001227 else:
1228 indesc = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001229 if not indesc:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001230 splitline = line.split(':', 1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001231 if len(splitline) < 2:
1232 continue
1233 key = splitline[0]
1234 value = splitline[1].strip()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001235 if key == 'Build-Depends':
1236 for dep in value.split(','):
1237 dep = dep.split()[0]
1238 mapped = depmap.get(dep, '')
1239 if mapped:
1240 depends.append(mapped)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001241 elif key == 'Description':
1242 values['SUMMARY'] = value
1243 indesc = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001244 else:
1245 varname = value_map.get(key, None)
1246 if varname:
1247 values[varname] = value
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001248 postinst = os.path.join(debpath, 'postinst')
1249 postrm = os.path.join(debpath, 'postrm')
1250 preinst = os.path.join(debpath, 'preinst')
1251 prerm = os.path.join(debpath, 'prerm')
1252 sfiles = [postinst, postrm, preinst, prerm]
1253 for sfile in sfiles:
1254 if os.path.isfile(sfile):
1255 logger.info("Converting %s file to recipe function..." %
1256 os.path.basename(sfile).upper())
1257 content = []
1258 with open(sfile) as f:
1259 for line in f:
1260 if "#!/" in line:
1261 continue
1262 line = line.rstrip("\n")
1263 if line.strip():
1264 content.append(line)
1265 if content:
1266 values[os.path.basename(f.name)] = content
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001267
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001268 #if depends:
1269 # values['DEPENDS'] = ' '.join(depends)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001270
1271 return values
1272
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001273def convert_rpm_xml(xmlfile):
1274 '''Converts the output from rpm -qp --xml to a set of variable values'''
1275 import xml.etree.ElementTree as ElementTree
1276 rpmtag_map = {'Name': 'PN',
1277 'Version': 'PV',
1278 'Summary': 'SUMMARY',
1279 'Description': 'DESCRIPTION',
1280 'License': 'LICENSE',
1281 'Url': 'HOMEPAGE'}
1282
1283 values = {}
1284 tree = ElementTree.parse(xmlfile)
1285 root = tree.getroot()
1286 for child in root:
1287 if child.tag == 'rpmTag':
1288 name = child.attrib.get('name', None)
1289 if name:
1290 varname = rpmtag_map.get(name, None)
1291 if varname:
1292 values[varname] = child[0].text
1293 return values
1294
1295
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001296def register_commands(subparsers):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001297 parser_create = subparsers.add_parser('create',
1298 help='Create a new recipe',
1299 description='Creates a new recipe from a source tree')
1300 parser_create.add_argument('source', help='Path or URL to source')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001301 parser_create.add_argument('-o', '--outfile', help='Specify filename for recipe to create')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001302 parser_create.add_argument('-p', '--provides', help='Specify an alias for the item provided by the recipe')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001303 parser_create.add_argument('-m', '--machine', help='Make recipe machine-specific as opposed to architecture-specific', action='store_true')
1304 parser_create.add_argument('-x', '--extract-to', metavar='EXTRACTPATH', help='Assuming source is a URL, fetch it and extract it to the directory specified as %(metavar)s')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001305 parser_create.add_argument('-N', '--name', help='Name to use within recipe (PN)')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001306 parser_create.add_argument('-V', '--version', help='Version to use within recipe (PV)')
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001307 parser_create.add_argument('-b', '--binary', help='Treat the source tree as something that should be installed verbatim (no compilation, same directory structure)', action='store_true')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001308 parser_create.add_argument('--also-native', help='Also add native variant (i.e. support building recipe for the build host as well as the target machine)', action='store_true')
1309 parser_create.add_argument('--src-subdir', help='Specify subdirectory within source tree to use', metavar='SUBDIR')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001310 group = parser_create.add_mutually_exclusive_group()
1311 group.add_argument('-a', '--autorev', help='When fetching from a git repository, set SRCREV in the recipe to a floating revision instead of fixed', action="store_true")
1312 group.add_argument('-S', '--srcrev', help='Source revision to fetch if fetching from an SCM such as git (default latest)')
1313 parser_create.add_argument('-B', '--srcbranch', help='Branch in source repository if fetching from an SCM such as git (default master)')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001314 parser_create.add_argument('--keep-temp', action="store_true", help='Keep temporary directory (for debugging)')
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001315 parser_create.add_argument('--fetch-dev', action="store_true", help='For npm, also fetch devDependencies')
1316 parser_create.add_argument('--devtool', action="store_true", help=argparse.SUPPRESS)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001317 parser_create.add_argument('--mirrors', action="store_true", help='Enable PREMIRRORS and MIRRORS for source tree fetching (disabled by default).')
1318 parser_create.set_defaults(func=create_recipe)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001319