summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Eggleton <paul.eggleton@linux.intel.com>2015-12-22 17:03:02 +1300
committerRichard Purdie <richard.purdie@linuxfoundation.org>2015-12-28 09:25:13 +0000
commitdb5f9645ad3ffb4e9be7075d71cb1b13341f5195 (patch)
tree3c4bfcfc698840c310d069f26b57863fde877528
parent6a7661b8005fadad10bde494131e27406e1e45b8 (diff)
downloadpoky-db5f9645ad3ffb4e9be7075d71cb1b13341f5195.tar.gz
recipetool: create: support extracting name and version from build scripts
Some build systems (notably autotools) support declaring the name and version of the program being built; since we need those for the recipe we can attempt to extract them. It's a little fuzzy as they are often omitted or may not be appropriately formatted for our purposes, but it does work on a reasonable number of software packages to be useful. (From OE-Core rev: 3b3fd33190d89c09e62126eea0e45aa84fe5442e) Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
-rw-r--r--meta/lib/oeqa/selftest/devtool.py2
-rw-r--r--meta/lib/oeqa/selftest/recipetool.py2
-rw-r--r--scripts/lib/recipetool/create.py130
-rw-r--r--scripts/lib/recipetool/create_buildsys.py282
-rw-r--r--scripts/lib/recipetool/create_buildsys_python.py2
5 files changed, 344 insertions, 74 deletions
diff --git a/meta/lib/oeqa/selftest/devtool.py b/meta/lib/oeqa/selftest/devtool.py
index 0a44ae71c8..8faea93784 100644
--- a/meta/lib/oeqa/selftest/devtool.py
+++ b/meta/lib/oeqa/selftest/devtool.py
@@ -279,7 +279,7 @@ class DevtoolTests(DevtoolBase):
279 self.assertIn('_git.bb', recipefile, 'Recipe file incorrectly named') 279 self.assertIn('_git.bb', recipefile, 'Recipe file incorrectly named')
280 checkvars = {} 280 checkvars = {}
281 checkvars['S'] = '${WORKDIR}/git' 281 checkvars['S'] = '${WORKDIR}/git'
282 checkvars['PV'] = '1.0+git${SRCPV}' 282 checkvars['PV'] = '1.11+git${SRCPV}'
283 checkvars['SRC_URI'] = url 283 checkvars['SRC_URI'] = url
284 checkvars['SRCREV'] = '${AUTOREV}' 284 checkvars['SRCREV'] = '${AUTOREV}'
285 self._test_recipe_contents(recipefile, checkvars, []) 285 self._test_recipe_contents(recipefile, checkvars, [])
diff --git a/meta/lib/oeqa/selftest/recipetool.py b/meta/lib/oeqa/selftest/recipetool.py
index b1f1d2ab90..bbfce7c394 100644
--- a/meta/lib/oeqa/selftest/recipetool.py
+++ b/meta/lib/oeqa/selftest/recipetool.py
@@ -395,7 +395,7 @@ class RecipetoolTests(RecipetoolBase):
395 checkvars['LICENSE'] = 'LGPLv2.1' 395 checkvars['LICENSE'] = 'LGPLv2.1'
396 checkvars['LIC_FILES_CHKSUM'] = 'file://COPYING;md5=7fbc338309ac38fefcd64b04bb903e34' 396 checkvars['LIC_FILES_CHKSUM'] = 'file://COPYING;md5=7fbc338309ac38fefcd64b04bb903e34'
397 checkvars['S'] = '${WORKDIR}/git' 397 checkvars['S'] = '${WORKDIR}/git'
398 checkvars['PV'] = '1.0+git${SRCPV}' 398 checkvars['PV'] = '1.11+git${SRCPV}'
399 checkvars['SRC_URI'] = srcuri 399 checkvars['SRC_URI'] = srcuri
400 checkvars['DEPENDS'] = 'libpng pango libx11 libxext jpeg' 400 checkvars['DEPENDS'] = 'libpng pango libx11 libxext jpeg'
401 inherits = ['autotools', 'pkgconfig'] 401 inherits = ['autotools', 'pkgconfig']
diff --git a/scripts/lib/recipetool/create.py b/scripts/lib/recipetool/create.py
index 5c249ab0c6..6c7b9fd7e8 100644
--- a/scripts/lib/recipetool/create.py
+++ b/scripts/lib/recipetool/create.py
@@ -41,10 +41,17 @@ def tinfoil_init(instance):
41 41
42class RecipeHandler(): 42class RecipeHandler():
43 @staticmethod 43 @staticmethod
44 def checkfiles(path, speclist): 44 def checkfiles(path, speclist, recursive=False):
45 results = [] 45 results = []
46 for spec in speclist: 46 if recursive:
47 results.extend(glob.glob(os.path.join(path, spec))) 47 for root, _, files in os.walk(path):
48 for fn in files:
49 for spec in speclist:
50 if fnmatch.fnmatch(fn, spec):
51 results.append(os.path.join(root, fn))
52 else:
53 for spec in speclist:
54 results.extend(glob.glob(os.path.join(path, spec)))
48 return results 55 return results
49 56
50 def genfunction(self, outlines, funcname, content, python=False, forcespace=False): 57 def genfunction(self, outlines, funcname, content, python=False, forcespace=False):
@@ -70,10 +77,14 @@ class RecipeHandler():
70 outlines.append('}') 77 outlines.append('}')
71 outlines.append('') 78 outlines.append('')
72 79
73 def process(self, srctree, classes, lines_before, lines_after, handled): 80 def process(self, srctree, classes, lines_before, lines_after, handled, extravalues):
74 return False 81 return False
75 82
76 83
84def validate_pv(pv):
85 if not pv or '_version' in pv.lower() or pv[0] not in '0123456789':
86 return False
87 return True
77 88
78def supports_srcrev(uri): 89def supports_srcrev(uri):
79 localdata = bb.data.createCopy(tinfoil.config_data) 90 localdata = bb.data.createCopy(tinfoil.config_data)
@@ -152,7 +163,12 @@ def create_recipe(args):
152 srcuri = '' 163 srcuri = ''
153 srctree = args.source 164 srctree = args.source
154 165
155 outfile = args.outfile 166 if args.outfile and os.path.isdir(args.outfile):
167 outfile = None
168 outdir = args.outfile
169 else:
170 outfile = args.outfile
171 outdir = None
156 if outfile and outfile != '-': 172 if outfile and outfile != '-':
157 if os.path.exists(outfile): 173 if os.path.exists(outfile):
158 logger.error('Output file %s already exists' % outfile) 174 logger.error('Output file %s already exists' % outfile)
@@ -196,28 +212,29 @@ def create_recipe(args):
196 lines_before.append('') 212 lines_before.append('')
197 213
198 # FIXME This is kind of a hack, we probably ought to be using bitbake to do this 214 # FIXME This is kind of a hack, we probably ought to be using bitbake to do this
199 # we'd also want a way to automatically set outfile based upon auto-detecting these values from the source if possible 215 pn = None
200 recipefn = os.path.splitext(os.path.basename(outfile))[0] 216 pv = None
201 fnsplit = recipefn.split('_') 217 if outfile:
202 if len(fnsplit) > 1: 218 recipefn = os.path.splitext(os.path.basename(outfile))[0]
203 pn = fnsplit[0] 219 fnsplit = recipefn.split('_')
204 pv = fnsplit[1] 220 if len(fnsplit) > 1:
205 else: 221 pn = fnsplit[0]
206 pn = recipefn 222 pv = fnsplit[1]
207 pv = None 223 else:
224 pn = recipefn
208 225
209 if args.version: 226 if args.version:
210 pv = args.version 227 pv = args.version
211 228
229 if args.name:
230 pn = args.name
231
212 if pv and pv not in 'git svn hg'.split(): 232 if pv and pv not in 'git svn hg'.split():
213 realpv = pv 233 realpv = pv
214 else: 234 else:
215 realpv = None 235 realpv = None
216 236
217 if srcuri: 237 if not srcuri:
218 if realpv:
219 srcuri = srcuri.replace(realpv, '${PV}')
220 else:
221 lines_before.append('# No information for SRC_URI yet (only an external source tree was specified)') 238 lines_before.append('# No information for SRC_URI yet (only an external source tree was specified)')
222 lines_before.append('SRC_URI = "%s"' % srcuri) 239 lines_before.append('SRC_URI = "%s"' % srcuri)
223 (md5value, sha256value) = checksums 240 (md5value, sha256value) = checksums
@@ -232,13 +249,7 @@ def create_recipe(args):
232 lines_before.append('SRCREV = "%s"' % srcrev) 249 lines_before.append('SRCREV = "%s"' % srcrev)
233 lines_before.append('') 250 lines_before.append('')
234 251
235 if srcsubdir and pv:
236 if srcsubdir == "%s-%s" % (pn, pv):
237 # This would be the default, so we don't need to set S in the recipe
238 srcsubdir = ''
239 if srcsubdir: 252 if srcsubdir:
240 if pv and pv not in 'git svn hg'.split():
241 srcsubdir = srcsubdir.replace(pv, '${PV}')
242 lines_before.append('S = "${WORKDIR}/%s"' % srcsubdir) 253 lines_before.append('S = "${WORKDIR}/%s"' % srcsubdir)
243 lines_before.append('') 254 lines_before.append('')
244 255
@@ -276,8 +287,74 @@ def create_recipe(args):
276 classes.append('bin_package') 287 classes.append('bin_package')
277 handled.append('buildsystem') 288 handled.append('buildsystem')
278 289
290 extravalues = {}
279 for handler in handlers: 291 for handler in handlers:
280 handler.process(srctree, classes, lines_before, lines_after, handled) 292 handler.process(srctree, classes, lines_before, lines_after, handled, extravalues)
293
294 if not realpv:
295 realpv = extravalues.get('PV', None)
296 if realpv:
297 if not validate_pv(realpv):
298 realpv = None
299 else:
300 realpv = realpv.lower().split()[0]
301 if '_' in realpv:
302 realpv = realpv.replace('_', '-')
303 if not pn:
304 pn = extravalues.get('PN', None)
305 if pn:
306 if pn.startswith('GNU '):
307 pn = pn[4:]
308 if ' ' in pn:
309 # Probably a descriptive identifier rather than a proper name
310 pn = None
311 else:
312 pn = pn.lower()
313 if '_' in pn:
314 pn = pn.replace('_', '-')
315
316 if not outfile:
317 if not pn:
318 logger.error('Unable to determine short program name from source tree - please specify name with -N/--name or output file name with -o/--outfile')
319 # devtool looks for this specific exit code, so don't change it
320 sys.exit(15)
321 else:
322 if srcuri and srcuri.startswith(('git://', 'hg://', 'svn://')):
323 outfile = '%s_%s.bb' % (pn, srcuri.split(':', 1)[0])
324 elif realpv:
325 outfile = '%s_%s.bb' % (pn, realpv)
326 else:
327 outfile = '%s.bb' % pn
328 if outdir:
329 outfile = os.path.join(outdir, outfile)
330 # We need to check this again
331 if os.path.exists(outfile):
332 logger.error('Output file %s already exists' % outfile)
333 sys.exit(1)
334
335 lines = lines_before
336 lines_before = []
337 skipblank = True
338 for line in lines:
339 if skipblank:
340 skipblank = False
341 if not line:
342 continue
343 if line.startswith('S = '):
344 if realpv and pv not in 'git svn hg'.split():
345 line = line.replace(realpv, '${PV}')
346 if pn:
347 line = line.replace(pn, '${BPN}')
348 if line == 'S = "${WORKDIR}/${BPN}-${PV}"':
349 skipblank = True
350 continue
351 elif line.startswith('SRC_URI = '):
352 if realpv:
353 line = line.replace(realpv, '${PV}')
354 elif line.startswith('PV = '):
355 if realpv:
356 line = re.sub('"[^+]*\+', '"%s+' % realpv, line)
357 lines_before.append(line)
281 358
282 outlines = [] 359 outlines = []
283 outlines.extend(lines_before) 360 outlines.extend(lines_before)
@@ -469,9 +546,10 @@ def register_commands(subparsers):
469 help='Create a new recipe', 546 help='Create a new recipe',
470 description='Creates a new recipe from a source tree') 547 description='Creates a new recipe from a source tree')
471 parser_create.add_argument('source', help='Path or URL to source') 548 parser_create.add_argument('source', help='Path or URL to source')
472 parser_create.add_argument('-o', '--outfile', help='Specify filename for recipe to create', required=True) 549 parser_create.add_argument('-o', '--outfile', help='Specify filename for recipe to create')
473 parser_create.add_argument('-m', '--machine', help='Make recipe machine-specific as opposed to architecture-specific', action='store_true') 550 parser_create.add_argument('-m', '--machine', help='Make recipe machine-specific as opposed to architecture-specific', action='store_true')
474 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') 551 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')
552 parser_create.add_argument('-N', '--name', help='Name to use within recipe (PN)')
475 parser_create.add_argument('-V', '--version', help='Version to use within recipe (PV)') 553 parser_create.add_argument('-V', '--version', help='Version to use within recipe (PV)')
476 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') 554 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')
477 parser_create.set_defaults(func=create_recipe) 555 parser_create.set_defaults(func=create_recipe)
diff --git a/scripts/lib/recipetool/create_buildsys.py b/scripts/lib/recipetool/create_buildsys.py
index 931ef3b33f..0aff59e229 100644
--- a/scripts/lib/recipetool/create_buildsys.py
+++ b/scripts/lib/recipetool/create_buildsys.py
@@ -17,7 +17,7 @@
17 17
18import re 18import re
19import logging 19import logging
20from recipetool.create import RecipeHandler, read_pkgconfig_provides 20from recipetool.create import RecipeHandler, read_pkgconfig_provides, validate_pv
21 21
22logger = logging.getLogger('recipetool') 22logger = logging.getLogger('recipetool')
23 23
@@ -27,13 +27,17 @@ def tinfoil_init(instance):
27 global tinfoil 27 global tinfoil
28 tinfoil = instance 28 tinfoil = instance
29 29
30
30class CmakeRecipeHandler(RecipeHandler): 31class CmakeRecipeHandler(RecipeHandler):
31 def process(self, srctree, classes, lines_before, lines_after, handled): 32 def process(self, srctree, classes, lines_before, lines_after, handled, extravalues):
32 if 'buildsystem' in handled: 33 if 'buildsystem' in handled:
33 return False 34 return False
34 35
35 if RecipeHandler.checkfiles(srctree, ['CMakeLists.txt']): 36 if RecipeHandler.checkfiles(srctree, ['CMakeLists.txt']):
36 classes.append('cmake') 37 classes.append('cmake')
38 values = CmakeRecipeHandler.extract_cmake_deps(lines_before, srctree, extravalues)
39 for var, value in values.iteritems():
40 lines_before.append('%s = "%s"' % (var, value))
37 lines_after.append('# Specify any options you want to pass to cmake using EXTRA_OECMAKE:') 41 lines_after.append('# Specify any options you want to pass to cmake using EXTRA_OECMAKE:')
38 lines_after.append('EXTRA_OECMAKE = ""') 42 lines_after.append('EXTRA_OECMAKE = ""')
39 lines_after.append('') 43 lines_after.append('')
@@ -41,8 +45,26 @@ class CmakeRecipeHandler(RecipeHandler):
41 return True 45 return True
42 return False 46 return False
43 47
48 @staticmethod
49 def extract_cmake_deps(outlines, srctree, extravalues, cmakelistsfile=None):
50 values = {}
51
52 if cmakelistsfile:
53 srcfiles = [cmakelistsfile]
54 else:
55 srcfiles = RecipeHandler.checkfiles(srctree, ['CMakeLists.txt'])
56
57 proj_re = re.compile('project\(([^)]*)\)', re.IGNORECASE)
58 with open(srcfiles[0], 'r') as f:
59 for line in f:
60 res = proj_re.match(line.strip())
61 if res:
62 extravalues['PN'] = res.group(1).split()[0]
63
64 return values
65
44class SconsRecipeHandler(RecipeHandler): 66class SconsRecipeHandler(RecipeHandler):
45 def process(self, srctree, classes, lines_before, lines_after, handled): 67 def process(self, srctree, classes, lines_before, lines_after, handled, extravalues):
46 if 'buildsystem' in handled: 68 if 'buildsystem' in handled:
47 return False 69 return False
48 70
@@ -56,7 +78,7 @@ class SconsRecipeHandler(RecipeHandler):
56 return False 78 return False
57 79
58class QmakeRecipeHandler(RecipeHandler): 80class QmakeRecipeHandler(RecipeHandler):
59 def process(self, srctree, classes, lines_before, lines_after, handled): 81 def process(self, srctree, classes, lines_before, lines_after, handled, extravalues):
60 if 'buildsystem' in handled: 82 if 'buildsystem' in handled:
61 return False 83 return False
62 84
@@ -67,14 +89,14 @@ class QmakeRecipeHandler(RecipeHandler):
67 return False 89 return False
68 90
69class AutotoolsRecipeHandler(RecipeHandler): 91class AutotoolsRecipeHandler(RecipeHandler):
70 def process(self, srctree, classes, lines_before, lines_after, handled): 92 def process(self, srctree, classes, lines_before, lines_after, handled, extravalues):
71 if 'buildsystem' in handled: 93 if 'buildsystem' in handled:
72 return False 94 return False
73 95
74 autoconf = False 96 autoconf = False
75 if RecipeHandler.checkfiles(srctree, ['configure.ac', 'configure.in']): 97 if RecipeHandler.checkfiles(srctree, ['configure.ac', 'configure.in']):
76 autoconf = True 98 autoconf = True
77 values = AutotoolsRecipeHandler.extract_autotools_deps(lines_before, srctree) 99 values = AutotoolsRecipeHandler.extract_autotools_deps(lines_before, srctree, extravalues)
78 classes.extend(values.pop('inherit', '').split()) 100 classes.extend(values.pop('inherit', '').split())
79 for var, value in values.iteritems(): 101 for var, value in values.iteritems():
80 lines_before.append('%s = "%s"' % (var, value)) 102 lines_before.append('%s = "%s"' % (var, value))
@@ -88,6 +110,22 @@ class AutotoolsRecipeHandler(RecipeHandler):
88 autoconf = True 110 autoconf = True
89 break 111 break
90 112
113 if autoconf and not ('PV' in extravalues and 'PN' in extravalues):
114 # Last resort
115 conffile = RecipeHandler.checkfiles(srctree, ['configure'])
116 if conffile:
117 with open(conffile[0], 'r') as f:
118 for line in f:
119 line = line.strip()
120 if line.startswith('VERSION=') or line.startswith('PACKAGE_VERSION='):
121 pv = line.split('=')[1].strip('"\'')
122 if pv and not 'PV' in extravalues and validate_pv(pv):
123 extravalues['PV'] = pv
124 elif line.startswith('PACKAGE_NAME=') or line.startswith('PACKAGE='):
125 pn = line.split('=')[1].strip('"\'')
126 if pn and not 'PN' in extravalues:
127 extravalues['PN'] = pn
128
91 if autoconf: 129 if autoconf:
92 lines_before.append('# NOTE: if this software is not capable of being built in a separate build directory') 130 lines_before.append('# NOTE: if this software is not capable of being built in a separate build directory')
93 lines_before.append('# from the source, you should replace autotools with autotools-brokensep in the') 131 lines_before.append('# from the source, you should replace autotools with autotools-brokensep in the')
@@ -102,7 +140,7 @@ class AutotoolsRecipeHandler(RecipeHandler):
102 return False 140 return False
103 141
104 @staticmethod 142 @staticmethod
105 def extract_autotools_deps(outlines, srctree, acfile=None): 143 def extract_autotools_deps(outlines, srctree, extravalues=None, acfile=None):
106 import shlex 144 import shlex
107 import oe.package 145 import oe.package
108 146
@@ -122,6 +160,9 @@ class AutotoolsRecipeHandler(RecipeHandler):
122 lib_re = re.compile('AC_CHECK_LIB\(\[?([a-zA-Z0-9]*)\]?, .*') 160 lib_re = re.compile('AC_CHECK_LIB\(\[?([a-zA-Z0-9]*)\]?, .*')
123 progs_re = re.compile('_PROGS?\(\[?[a-zA-Z0-9]*\]?, \[?([^,\]]*)\]?[),].*') 161 progs_re = re.compile('_PROGS?\(\[?[a-zA-Z0-9]*\]?, \[?([^,\]]*)\]?[),].*')
124 dep_re = re.compile('([^ ><=]+)( [<>=]+ [^ ><=]+)?') 162 dep_re = re.compile('([^ ><=]+)( [<>=]+ [^ ><=]+)?')
163 ac_init_re = re.compile('AC_INIT\(([^,]+), *([^,]+)[,)].*')
164 am_init_re = re.compile('AM_INIT_AUTOMAKE\(([^,]+), *([^,]+)[,)].*')
165 define_re = re.compile(' *(m4_)?define\(([^,]+), *([^,]+)\)')
125 166
126 # Build up lib library->package mapping 167 # Build up lib library->package mapping
127 shlib_providers = oe.package.read_shlib_providers(tinfoil.config_data) 168 shlib_providers = oe.package.read_shlib_providers(tinfoil.config_data)
@@ -157,55 +198,157 @@ class AutotoolsRecipeHandler(RecipeHandler):
157 else: 198 else:
158 raise 199 raise
159 200
201 defines = {}
202 def subst_defines(value):
203 newvalue = value
204 for define, defval in defines.iteritems():
205 newvalue = newvalue.replace(define, defval)
206 if newvalue != value:
207 return subst_defines(newvalue)
208 return value
209
210 def process_value(value):
211 value = value.replace('[', '').replace(']', '')
212 if value.startswith('m4_esyscmd(') or value.startswith('m4_esyscmd_s('):
213 cmd = subst_defines(value[value.index('(')+1:-1])
214 try:
215 if '|' in cmd:
216 cmd = 'set -o pipefail; ' + cmd
217 stdout, _ = bb.process.run(cmd, cwd=srctree, shell=True)
218 ret = stdout.rstrip()
219 except bb.process.ExecutionError as e:
220 ret = ''
221 elif value.startswith('m4_'):
222 return None
223 ret = subst_defines(value)
224 if ret:
225 ret = ret.strip('"\'')
226 return ret
227
160 # Since a configure.ac file is essentially a program, this is only ever going to be 228 # Since a configure.ac file is essentially a program, this is only ever going to be
161 # a hack unfortunately; but it ought to be enough of an approximation 229 # a hack unfortunately; but it ought to be enough of an approximation
162 if acfile: 230 if acfile:
163 srcfiles = [acfile] 231 srcfiles = [acfile]
164 else: 232 else:
165 srcfiles = RecipeHandler.checkfiles(srctree, ['configure.ac', 'configure.in']) 233 srcfiles = RecipeHandler.checkfiles(srctree, ['acinclude.m4', 'configure.ac', 'configure.in'])
234
166 pcdeps = [] 235 pcdeps = []
167 deps = [] 236 deps = []
168 unmapped = [] 237 unmapped = []
169 unmappedlibs = [] 238 unmappedlibs = []
170 with open(srcfiles[0], 'r') as f: 239
171 for line in f: 240 def process_macro(keyword, value):
172 if 'PKG_CHECK_MODULES' in line: 241 if keyword == 'PKG_CHECK_MODULES':
173 res = pkg_re.search(line) 242 res = pkg_re.search(value)
174 if res: 243 if res:
175 res = dep_re.findall(res.group(1)) 244 res = dep_re.findall(res.group(1))
176 if res:
177 pcdeps.extend([x[0] for x in res])
178 inherits.append('pkgconfig')
179 if line.lstrip().startswith('AM_GNU_GETTEXT'):
180 inherits.append('gettext')
181 elif 'AC_CHECK_PROG' in line or 'AC_PATH_PROG' in line:
182 res = progs_re.search(line)
183 if res: 245 if res:
184 for prog in shlex.split(res.group(1)): 246 pcdeps.extend([x[0] for x in res])
185 prog = prog.split()[0] 247 inherits.append('pkgconfig')
186 progclass = progclassmap.get(prog, None) 248 elif keyword == 'AM_GNU_GETTEXT':
187 if progclass: 249 inherits.append('gettext')
188 inherits.append(progclass) 250 elif keyword == 'AC_CHECK_PROG' or keyword == 'AC_PATH_PROG':
251 res = progs_re.search(value)
252 if res:
253 for prog in shlex.split(res.group(1)):
254 prog = prog.split()[0]
255 progclass = progclassmap.get(prog, None)
256 if progclass:
257 inherits.append(progclass)
258 else:
259 progdep = progmap.get(prog, None)
260 if progdep:
261 deps.append(progdep)
189 else: 262 else:
190 progdep = progmap.get(prog, None) 263 if not prog.startswith('$'):
191 if progdep: 264 unmapped.append(prog)
192 deps.append(progdep) 265 elif keyword == 'AC_CHECK_LIB':
193 else: 266 res = lib_re.search(value)
194 if not prog.startswith('$'): 267 if res:
195 unmapped.append(prog) 268 lib = res.group(1)
196 elif 'AC_CHECK_LIB' in line: 269 libdep = recipelibmap.get(lib, None)
197 res = lib_re.search(line) 270 if libdep:
271 deps.append(libdep)
272 else:
273 if libdep is None:
274 if not lib.startswith('$'):
275 unmappedlibs.append(lib)
276 elif keyword == 'AC_PATH_X':
277 deps.append('libx11')
278 elif keyword == 'AC_INIT':
279 if extravalues is not None:
280 res = ac_init_re.match(value)
198 if res: 281 if res:
199 lib = res.group(1) 282 extravalues['PN'] = process_value(res.group(1))
200 libdep = recipelibmap.get(lib, None) 283 pv = process_value(res.group(2))
201 if libdep: 284 if validate_pv(pv):
202 deps.append(libdep) 285 extravalues['PV'] = pv
203 else: 286 elif keyword == 'AM_INIT_AUTOMAKE':
204 if libdep is None: 287 if extravalues is not None:
205 if not lib.startswith('$'): 288 if 'PN' not in extravalues:
206 unmappedlibs.append(lib) 289 res = am_init_re.match(value)
207 elif 'AC_PATH_X' in line: 290 if res:
208 deps.append('libx11') 291 if res.group(1) != 'AC_PACKAGE_NAME':
292 extravalues['PN'] = process_value(res.group(1))
293 pv = process_value(res.group(2))
294 if validate_pv(pv):
295 extravalues['PV'] = pv
296 elif keyword == 'define(':
297 res = define_re.match(value)
298 if res:
299 key = res.group(2).strip('[]')
300 value = process_value(res.group(3))
301 if value is not None:
302 defines[key] = value
303
304 keywords = ['PKG_CHECK_MODULES',
305 'AM_GNU_GETTEXT',
306 'AC_CHECK_PROG',
307 'AC_PATH_PROG',
308 'AC_CHECK_LIB',
309 'AC_PATH_X',
310 'AC_INIT',
311 'AM_INIT_AUTOMAKE',
312 'define(',
313 ]
314 for srcfile in srcfiles:
315 nesting = 0
316 in_keyword = ''
317 partial = ''
318 with open(srcfile, 'r') as f:
319 for line in f:
320 if in_keyword:
321 partial += ' ' + line.strip()
322 if partial.endswith('\\'):
323 partial = partial[:-1]
324 nesting = nesting + line.count('(') - line.count(')')
325 if nesting == 0:
326 process_macro(in_keyword, partial)
327 partial = ''
328 in_keyword = ''
329 else:
330 for keyword in keywords:
331 if keyword in line:
332 nesting = line.count('(') - line.count(')')
333 if nesting > 0:
334 partial = line.strip()
335 if partial.endswith('\\'):
336 partial = partial[:-1]
337 in_keyword = keyword
338 else:
339 process_macro(keyword, line.strip())
340 break
341
342 if in_keyword:
343 process_macro(in_keyword, partial)
344
345 if extravalues:
346 for k,v in extravalues.items():
347 if v:
348 if v.startswith('$') or v.startswith('@') or v.startswith('%'):
349 del extravalues[k]
350 else:
351 extravalues[k] = v.strip('"\'').rstrip('()')
209 352
210 if unmapped: 353 if unmapped:
211 outlines.append('# NOTE: the following prog dependencies are unknown, ignoring: %s' % ' '.join(unmapped)) 354 outlines.append('# NOTE: the following prog dependencies are unknown, ignoring: %s' % ' '.join(unmapped))
@@ -240,7 +383,7 @@ class AutotoolsRecipeHandler(RecipeHandler):
240 383
241 384
242class MakefileRecipeHandler(RecipeHandler): 385class MakefileRecipeHandler(RecipeHandler):
243 def process(self, srctree, classes, lines_before, lines_after, handled): 386 def process(self, srctree, classes, lines_before, lines_after, handled, extravalues):
244 if 'buildsystem' in handled: 387 if 'buildsystem' in handled:
245 return False 388 return False
246 389
@@ -307,6 +450,53 @@ class MakefileRecipeHandler(RecipeHandler):
307 self.genfunction(lines_after, 'do_install', ['# Specify install commands here']) 450 self.genfunction(lines_after, 'do_install', ['# Specify install commands here'])
308 451
309 452
453class VersionFileRecipeHandler(RecipeHandler):
454 def process(self, srctree, classes, lines_before, lines_after, handled, extravalues):
455 if 'PV' not in extravalues:
456 # Look for a VERSION or version file containing a single line consisting
457 # only of a version number
458 filelist = RecipeHandler.checkfiles(srctree, ['VERSION', 'version'])
459 version = None
460 for fileitem in filelist:
461 linecount = 0
462 with open(fileitem, 'r') as f:
463 for line in f:
464 line = line.rstrip().strip('"\'')
465 linecount += 1
466 if line:
467 if linecount > 1:
468 version = None
469 break
470 else:
471 if validate_pv(line):
472 version = line
473 if version:
474 extravalues['PV'] = version
475 break
476
477
478class SpecFileRecipeHandler(RecipeHandler):
479 def process(self, srctree, classes, lines_before, lines_after, handled, extravalues):
480 if 'PV' in extravalues and 'PN' in extravalues:
481 return
482 filelist = RecipeHandler.checkfiles(srctree, ['*.spec'], recursive=True)
483 pn = None
484 pv = None
485 for fileitem in filelist:
486 linecount = 0
487 with open(fileitem, 'r') as f:
488 for line in f:
489 if line.startswith('Name:') and not pn:
490 pn = line.split(':')[1].strip()
491 if line.startswith('Version:') and not pv:
492 pv = line.split(':')[1].strip()
493 if pv or pn:
494 if pv and not 'PV' in extravalues and validate_pv(pv):
495 extravalues['PV'] = pv
496 if pn and not 'PN' in extravalues:
497 extravalues['PN'] = pn
498 break
499
310def register_recipe_handlers(handlers): 500def register_recipe_handlers(handlers):
311 # These are in a specific order so that the right one is detected first 501 # These are in a specific order so that the right one is detected first
312 handlers.append(CmakeRecipeHandler()) 502 handlers.append(CmakeRecipeHandler())
@@ -314,3 +504,5 @@ def register_recipe_handlers(handlers):
314 handlers.append(SconsRecipeHandler()) 504 handlers.append(SconsRecipeHandler())
315 handlers.append(QmakeRecipeHandler()) 505 handlers.append(QmakeRecipeHandler())
316 handlers.append(MakefileRecipeHandler()) 506 handlers.append(MakefileRecipeHandler())
507 handlers.append((VersionFileRecipeHandler(), -1))
508 handlers.append((SpecFileRecipeHandler(), -1))
diff --git a/scripts/lib/recipetool/create_buildsys_python.py b/scripts/lib/recipetool/create_buildsys_python.py
index 266c423547..4d65c962b6 100644
--- a/scripts/lib/recipetool/create_buildsys_python.py
+++ b/scripts/lib/recipetool/create_buildsys_python.py
@@ -159,7 +159,7 @@ class PythonRecipeHandler(RecipeHandler):
159 def __init__(self): 159 def __init__(self):
160 pass 160 pass
161 161
162 def process(self, srctree, classes, lines_before, lines_after, handled): 162 def process(self, srctree, classes, lines_before, lines_after, handled, extravalues):
163 if 'buildsystem' in handled: 163 if 'buildsystem' in handled:
164 return False 164 return False
165 165