Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 1 | # Recipe creation tool - create command build system handlers |
| 2 | # |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 3 | # Copyright (C) 2014-2016 Intel Corporation |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 4 | # |
| 5 | # This program is free software; you can redistribute it and/or modify |
| 6 | # it under the terms of the GNU General Public License version 2 as |
| 7 | # published by the Free Software Foundation. |
| 8 | # |
| 9 | # This program is distributed in the hope that it will be useful, |
| 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | # GNU General Public License for more details. |
| 13 | # |
| 14 | # You should have received a copy of the GNU General Public License along |
| 15 | # with this program; if not, write to the Free Software Foundation, Inc., |
| 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| 17 | |
| 18 | import re |
| 19 | import logging |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 20 | import glob |
| 21 | from recipetool.create import RecipeHandler, validate_pv |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 22 | |
| 23 | logger = logging.getLogger('recipetool') |
| 24 | |
| 25 | tinfoil = None |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 26 | plugins = None |
| 27 | |
| 28 | def plugin_init(pluginlist): |
| 29 | # Take a reference to the list so we can use it later |
| 30 | global plugins |
| 31 | plugins = pluginlist |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 32 | |
| 33 | def tinfoil_init(instance): |
| 34 | global tinfoil |
| 35 | tinfoil = instance |
| 36 | |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 37 | |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 38 | class CmakeRecipeHandler(RecipeHandler): |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 39 | def process(self, srctree, classes, lines_before, lines_after, handled, extravalues): |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 40 | if 'buildsystem' in handled: |
| 41 | return False |
| 42 | |
| 43 | if RecipeHandler.checkfiles(srctree, ['CMakeLists.txt']): |
| 44 | classes.append('cmake') |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 45 | values = CmakeRecipeHandler.extract_cmake_deps(lines_before, srctree, extravalues) |
| 46 | classes.extend(values.pop('inherit', '').split()) |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 47 | for var, value in values.items(): |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 48 | lines_before.append('%s = "%s"' % (var, value)) |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 49 | lines_after.append('# Specify any options you want to pass to cmake using EXTRA_OECMAKE:') |
| 50 | lines_after.append('EXTRA_OECMAKE = ""') |
| 51 | lines_after.append('') |
| 52 | handled.append('buildsystem') |
| 53 | return True |
| 54 | return False |
| 55 | |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 56 | @staticmethod |
| 57 | def extract_cmake_deps(outlines, srctree, extravalues, cmakelistsfile=None): |
| 58 | # Find all plugins that want to register handlers |
| 59 | logger.debug('Loading cmake handlers') |
| 60 | handlers = [] |
| 61 | for plugin in plugins: |
| 62 | if hasattr(plugin, 'register_cmake_handlers'): |
| 63 | plugin.register_cmake_handlers(handlers) |
| 64 | |
| 65 | values = {} |
| 66 | inherits = [] |
| 67 | |
| 68 | if cmakelistsfile: |
| 69 | srcfiles = [cmakelistsfile] |
| 70 | else: |
| 71 | srcfiles = RecipeHandler.checkfiles(srctree, ['CMakeLists.txt']) |
| 72 | |
| 73 | # Note that some of these are non-standard, but probably better to |
| 74 | # be able to map them anyway if we see them |
| 75 | cmake_pkgmap = {'alsa': 'alsa-lib', |
| 76 | 'aspell': 'aspell', |
| 77 | 'atk': 'atk', |
| 78 | 'bison': 'bison-native', |
| 79 | 'boost': 'boost', |
| 80 | 'bzip2': 'bzip2', |
| 81 | 'cairo': 'cairo', |
| 82 | 'cups': 'cups', |
| 83 | 'curl': 'curl', |
| 84 | 'curses': 'ncurses', |
| 85 | 'cvs': 'cvs', |
| 86 | 'drm': 'libdrm', |
| 87 | 'dbus': 'dbus', |
| 88 | 'dbusglib': 'dbus-glib', |
| 89 | 'egl': 'virtual/egl', |
| 90 | 'expat': 'expat', |
| 91 | 'flex': 'flex-native', |
| 92 | 'fontconfig': 'fontconfig', |
| 93 | 'freetype': 'freetype', |
| 94 | 'gettext': '', |
| 95 | 'git': '', |
| 96 | 'gio': 'glib-2.0', |
| 97 | 'giounix': 'glib-2.0', |
| 98 | 'glew': 'glew', |
| 99 | 'glib': 'glib-2.0', |
| 100 | 'glib2': 'glib-2.0', |
| 101 | 'glu': 'libglu', |
| 102 | 'glut': 'freeglut', |
| 103 | 'gobject': 'glib-2.0', |
| 104 | 'gperf': 'gperf-native', |
| 105 | 'gnutls': 'gnutls', |
| 106 | 'gtk2': 'gtk+', |
| 107 | 'gtk3': 'gtk+3', |
| 108 | 'gtk': 'gtk+3', |
| 109 | 'harfbuzz': 'harfbuzz', |
| 110 | 'icu': 'icu', |
| 111 | 'intl': 'virtual/libintl', |
| 112 | 'jpeg': 'jpeg', |
| 113 | 'libarchive': 'libarchive', |
| 114 | 'libiconv': 'virtual/libiconv', |
| 115 | 'liblzma': 'xz', |
| 116 | 'libxml2': 'libxml2', |
| 117 | 'libxslt': 'libxslt', |
| 118 | 'opengl': 'virtual/libgl', |
| 119 | 'openmp': '', |
| 120 | 'openssl': 'openssl', |
| 121 | 'pango': 'pango', |
| 122 | 'perl': '', |
| 123 | 'perllibs': '', |
| 124 | 'pkgconfig': '', |
| 125 | 'png': 'libpng', |
| 126 | 'pthread': '', |
| 127 | 'pythoninterp': '', |
| 128 | 'pythonlibs': '', |
| 129 | 'ruby': 'ruby-native', |
| 130 | 'sdl': 'libsdl', |
| 131 | 'sdl2': 'libsdl2', |
| 132 | 'subversion': 'subversion-native', |
| 133 | 'swig': 'swig-native', |
| 134 | 'tcl': 'tcl-native', |
| 135 | 'threads': '', |
| 136 | 'tiff': 'tiff', |
| 137 | 'wget': 'wget', |
| 138 | 'x11': 'libx11', |
| 139 | 'xcb': 'libxcb', |
| 140 | 'xext': 'libxext', |
| 141 | 'xfixes': 'libxfixes', |
| 142 | 'zlib': 'zlib', |
| 143 | } |
| 144 | |
| 145 | pcdeps = [] |
| 146 | libdeps = [] |
| 147 | deps = [] |
| 148 | unmappedpkgs = [] |
| 149 | |
| 150 | proj_re = re.compile('project\s*\(([^)]*)\)', re.IGNORECASE) |
| 151 | pkgcm_re = re.compile('pkg_check_modules\s*\(\s*[a-zA-Z0-9-_]+\s*(REQUIRED)?\s+([^)\s]+)\s*\)', re.IGNORECASE) |
| 152 | pkgsm_re = re.compile('pkg_search_module\s*\(\s*[a-zA-Z0-9-_]+\s*(REQUIRED)?((\s+[^)\s]+)+)\s*\)', re.IGNORECASE) |
| 153 | findpackage_re = re.compile('find_package\s*\(\s*([a-zA-Z0-9-_]+)\s*.*', re.IGNORECASE) |
| 154 | findlibrary_re = re.compile('find_library\s*\(\s*[a-zA-Z0-9-_]+\s*(NAMES\s+)?([a-zA-Z0-9-_ ]+)\s*.*') |
| 155 | checklib_re = re.compile('check_library_exists\s*\(\s*([^\s)]+)\s*.*', re.IGNORECASE) |
| 156 | include_re = re.compile('include\s*\(\s*([^)\s]*)\s*\)', re.IGNORECASE) |
| 157 | subdir_re = re.compile('add_subdirectory\s*\(\s*([^)\s]*)\s*([^)\s]*)\s*\)', re.IGNORECASE) |
| 158 | dep_re = re.compile('([^ ><=]+)( *[<>=]+ *[^ ><=]+)?') |
| 159 | |
| 160 | def find_cmake_package(pkg): |
| 161 | RecipeHandler.load_devel_filemap(tinfoil.config_data) |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 162 | for fn, pn in RecipeHandler.recipecmakefilemap.items(): |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 163 | splitname = fn.split('/') |
| 164 | if len(splitname) > 1: |
| 165 | if splitname[0].lower().startswith(pkg.lower()): |
| 166 | if splitname[1] == '%s-config.cmake' % pkg.lower() or splitname[1] == '%sConfig.cmake' % pkg or splitname[1] == 'Find%s.cmake' % pkg: |
| 167 | return pn |
| 168 | return None |
| 169 | |
| 170 | def interpret_value(value): |
| 171 | return value.strip('"') |
| 172 | |
| 173 | def parse_cmake_file(fn, paths=None): |
| 174 | searchpaths = (paths or []) + [os.path.dirname(fn)] |
| 175 | logger.debug('Parsing file %s' % fn) |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 176 | with open(fn, 'r', errors='surrogateescape') as f: |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 177 | for line in f: |
| 178 | line = line.strip() |
| 179 | for handler in handlers: |
| 180 | if handler.process_line(srctree, fn, line, libdeps, pcdeps, deps, outlines, inherits, values): |
| 181 | continue |
| 182 | res = include_re.match(line) |
| 183 | if res: |
| 184 | includefn = bb.utils.which(':'.join(searchpaths), res.group(1)) |
| 185 | if includefn: |
| 186 | parse_cmake_file(includefn, searchpaths) |
| 187 | else: |
| 188 | logger.debug('Unable to recurse into include file %s' % res.group(1)) |
| 189 | continue |
| 190 | res = subdir_re.match(line) |
| 191 | if res: |
| 192 | subdirfn = os.path.join(os.path.dirname(fn), res.group(1), 'CMakeLists.txt') |
| 193 | if os.path.exists(subdirfn): |
| 194 | parse_cmake_file(subdirfn, searchpaths) |
| 195 | else: |
| 196 | logger.debug('Unable to recurse into subdirectory file %s' % subdirfn) |
| 197 | continue |
| 198 | res = proj_re.match(line) |
| 199 | if res: |
| 200 | extravalues['PN'] = interpret_value(res.group(1).split()[0]) |
| 201 | continue |
| 202 | res = pkgcm_re.match(line) |
| 203 | if res: |
| 204 | res = dep_re.findall(res.group(2)) |
| 205 | if res: |
| 206 | pcdeps.extend([interpret_value(x[0]) for x in res]) |
| 207 | inherits.append('pkgconfig') |
| 208 | continue |
| 209 | res = pkgsm_re.match(line) |
| 210 | if res: |
| 211 | res = dep_re.findall(res.group(2)) |
| 212 | if res: |
| 213 | # Note: appending a tuple here! |
| 214 | item = tuple((interpret_value(x[0]) for x in res)) |
| 215 | if len(item) == 1: |
| 216 | item = item[0] |
| 217 | pcdeps.append(item) |
| 218 | inherits.append('pkgconfig') |
| 219 | continue |
| 220 | res = findpackage_re.match(line) |
| 221 | if res: |
| 222 | origpkg = res.group(1) |
| 223 | pkg = interpret_value(origpkg) |
| 224 | found = False |
| 225 | for handler in handlers: |
| 226 | if handler.process_findpackage(srctree, fn, pkg, deps, outlines, inherits, values): |
| 227 | logger.debug('Mapped CMake package %s via handler %s' % (pkg, handler.__class__.__name__)) |
| 228 | found = True |
| 229 | break |
| 230 | if found: |
| 231 | continue |
| 232 | elif pkg == 'Gettext': |
| 233 | inherits.append('gettext') |
| 234 | elif pkg == 'Perl': |
| 235 | inherits.append('perlnative') |
| 236 | elif pkg == 'PkgConfig': |
| 237 | inherits.append('pkgconfig') |
| 238 | elif pkg == 'PythonInterp': |
| 239 | inherits.append('pythonnative') |
| 240 | elif pkg == 'PythonLibs': |
| 241 | inherits.append('python-dir') |
| 242 | else: |
| 243 | # Try to map via looking at installed CMake packages in pkgdata |
| 244 | dep = find_cmake_package(pkg) |
| 245 | if dep: |
| 246 | logger.debug('Mapped CMake package %s to recipe %s via pkgdata' % (pkg, dep)) |
| 247 | deps.append(dep) |
| 248 | else: |
| 249 | dep = cmake_pkgmap.get(pkg.lower(), None) |
| 250 | if dep: |
| 251 | logger.debug('Mapped CMake package %s to recipe %s via internal list' % (pkg, dep)) |
| 252 | deps.append(dep) |
| 253 | elif dep is None: |
| 254 | unmappedpkgs.append(origpkg) |
| 255 | continue |
| 256 | res = checklib_re.match(line) |
| 257 | if res: |
| 258 | lib = interpret_value(res.group(1)) |
| 259 | if not lib.startswith('$'): |
| 260 | libdeps.append(lib) |
| 261 | res = findlibrary_re.match(line) |
| 262 | if res: |
| 263 | libs = res.group(2).split() |
| 264 | for lib in libs: |
| 265 | if lib in ['HINTS', 'PATHS', 'PATH_SUFFIXES', 'DOC', 'NAMES_PER_DIR'] or lib.startswith(('NO_', 'CMAKE_', 'ONLY_CMAKE_')): |
| 266 | break |
| 267 | lib = interpret_value(lib) |
| 268 | if not lib.startswith('$'): |
| 269 | libdeps.append(lib) |
| 270 | if line.lower().startswith('useswig'): |
| 271 | deps.append('swig-native') |
| 272 | continue |
| 273 | |
| 274 | parse_cmake_file(srcfiles[0]) |
| 275 | |
| 276 | if unmappedpkgs: |
| 277 | outlines.append('# NOTE: unable to map the following CMake package dependencies: %s' % ' '.join(list(set(unmappedpkgs)))) |
| 278 | |
| 279 | RecipeHandler.handle_depends(libdeps, pcdeps, deps, outlines, values, tinfoil.config_data) |
| 280 | |
| 281 | for handler in handlers: |
| 282 | handler.post_process(srctree, libdeps, pcdeps, deps, outlines, inherits, values) |
| 283 | |
| 284 | if inherits: |
| 285 | values['inherit'] = ' '.join(list(set(inherits))) |
| 286 | |
| 287 | return values |
| 288 | |
| 289 | |
| 290 | class CmakeExtensionHandler(object): |
| 291 | '''Base class for CMake extension handlers''' |
| 292 | def process_line(self, srctree, fn, line, libdeps, pcdeps, deps, outlines, inherits, values): |
| 293 | ''' |
| 294 | Handle a line parsed out of an CMake file. |
| 295 | Return True if you've completely handled the passed in line, otherwise return False. |
| 296 | ''' |
| 297 | return False |
| 298 | |
| 299 | def process_findpackage(self, srctree, fn, pkg, deps, outlines, inherits, values): |
| 300 | ''' |
| 301 | Handle a find_package package parsed out of a CMake file. |
| 302 | Return True if you've completely handled the passed in package, otherwise return False. |
| 303 | ''' |
| 304 | return False |
| 305 | |
| 306 | def post_process(self, srctree, fn, pkg, deps, outlines, inherits, values): |
| 307 | ''' |
| 308 | Apply any desired post-processing on the output |
| 309 | ''' |
| 310 | return |
| 311 | |
| 312 | |
| 313 | |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 314 | class SconsRecipeHandler(RecipeHandler): |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 315 | def process(self, srctree, classes, lines_before, lines_after, handled, extravalues): |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 316 | if 'buildsystem' in handled: |
| 317 | return False |
| 318 | |
| 319 | if RecipeHandler.checkfiles(srctree, ['SConstruct', 'Sconstruct', 'sconstruct']): |
| 320 | classes.append('scons') |
| 321 | lines_after.append('# Specify any options you want to pass to scons using EXTRA_OESCONS:') |
| 322 | lines_after.append('EXTRA_OESCONS = ""') |
| 323 | lines_after.append('') |
| 324 | handled.append('buildsystem') |
| 325 | return True |
| 326 | return False |
| 327 | |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 328 | |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 329 | class QmakeRecipeHandler(RecipeHandler): |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 330 | def process(self, srctree, classes, lines_before, lines_after, handled, extravalues): |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 331 | if 'buildsystem' in handled: |
| 332 | return False |
| 333 | |
| 334 | if RecipeHandler.checkfiles(srctree, ['*.pro']): |
| 335 | classes.append('qmake2') |
| 336 | handled.append('buildsystem') |
| 337 | return True |
| 338 | return False |
| 339 | |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 340 | |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 341 | class AutotoolsRecipeHandler(RecipeHandler): |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 342 | def process(self, srctree, classes, lines_before, lines_after, handled, extravalues): |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 343 | if 'buildsystem' in handled: |
| 344 | return False |
| 345 | |
| 346 | autoconf = False |
| 347 | if RecipeHandler.checkfiles(srctree, ['configure.ac', 'configure.in']): |
| 348 | autoconf = True |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 349 | values = AutotoolsRecipeHandler.extract_autotools_deps(lines_before, srctree, extravalues) |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 350 | classes.extend(values.pop('inherit', '').split()) |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 351 | for var, value in values.items(): |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 352 | lines_before.append('%s = "%s"' % (var, value)) |
| 353 | else: |
| 354 | conffile = RecipeHandler.checkfiles(srctree, ['configure']) |
| 355 | if conffile: |
| 356 | # Check if this is just a pre-generated autoconf configure script |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 357 | with open(conffile[0], 'r', errors='surrogateescape') as f: |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 358 | for i in range(1, 10): |
| 359 | if 'Generated by GNU Autoconf' in f.readline(): |
| 360 | autoconf = True |
| 361 | break |
| 362 | |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 363 | if autoconf and not ('PV' in extravalues and 'PN' in extravalues): |
| 364 | # Last resort |
| 365 | conffile = RecipeHandler.checkfiles(srctree, ['configure']) |
| 366 | if conffile: |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 367 | with open(conffile[0], 'r', errors='surrogateescape') as f: |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 368 | for line in f: |
| 369 | line = line.strip() |
| 370 | if line.startswith('VERSION=') or line.startswith('PACKAGE_VERSION='): |
| 371 | pv = line.split('=')[1].strip('"\'') |
| 372 | if pv and not 'PV' in extravalues and validate_pv(pv): |
| 373 | extravalues['PV'] = pv |
| 374 | elif line.startswith('PACKAGE_NAME=') or line.startswith('PACKAGE='): |
| 375 | pn = line.split('=')[1].strip('"\'') |
| 376 | if pn and not 'PN' in extravalues: |
| 377 | extravalues['PN'] = pn |
| 378 | |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 379 | if autoconf: |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 380 | lines_before.append('') |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 381 | lines_before.append('# NOTE: if this software is not capable of being built in a separate build directory') |
| 382 | lines_before.append('# from the source, you should replace autotools with autotools-brokensep in the') |
| 383 | lines_before.append('# inherit line') |
| 384 | classes.append('autotools') |
| 385 | lines_after.append('# Specify any options you want to pass to the configure script using EXTRA_OECONF:') |
| 386 | lines_after.append('EXTRA_OECONF = ""') |
| 387 | lines_after.append('') |
| 388 | handled.append('buildsystem') |
| 389 | return True |
| 390 | |
| 391 | return False |
| 392 | |
| 393 | @staticmethod |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 394 | def extract_autotools_deps(outlines, srctree, extravalues=None, acfile=None): |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 395 | import shlex |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 396 | |
| 397 | # Find all plugins that want to register handlers |
| 398 | logger.debug('Loading autotools handlers') |
| 399 | handlers = [] |
| 400 | for plugin in plugins: |
| 401 | if hasattr(plugin, 'register_autotools_handlers'): |
| 402 | plugin.register_autotools_handlers(handlers) |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 403 | |
| 404 | values = {} |
| 405 | inherits = [] |
| 406 | |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 407 | # Hardcoded map, we also use a dynamic one based on what's in the sysroot |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 408 | progmap = {'flex': 'flex-native', |
| 409 | 'bison': 'bison-native', |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 410 | 'm4': 'm4-native', |
| 411 | 'tar': 'tar-native', |
| 412 | 'ar': 'binutils-native', |
| 413 | 'ranlib': 'binutils-native', |
| 414 | 'ld': 'binutils-native', |
| 415 | 'strip': 'binutils-native', |
| 416 | 'libtool': '', |
| 417 | 'autoconf': '', |
| 418 | 'autoheader': '', |
| 419 | 'automake': '', |
| 420 | 'uname': '', |
| 421 | 'rm': '', |
| 422 | 'cp': '', |
| 423 | 'mv': '', |
| 424 | 'find': '', |
| 425 | 'awk': '', |
| 426 | 'sed': '', |
| 427 | } |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 428 | progclassmap = {'gconftool-2': 'gconf', |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 429 | 'pkg-config': 'pkgconfig', |
| 430 | 'python': 'pythonnative', |
| 431 | 'python3': 'python3native', |
| 432 | 'perl': 'perlnative', |
| 433 | 'makeinfo': 'texinfo', |
| 434 | } |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 435 | |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 436 | pkg_re = re.compile('PKG_CHECK_MODULES\(\s*\[?[a-zA-Z0-9_]*\]?,\s*\[?([^,\]]*)\]?[),].*') |
| 437 | pkgce_re = re.compile('PKG_CHECK_EXISTS\(\s*\[?([^,\]]*)\]?[),].*') |
| 438 | lib_re = re.compile('AC_CHECK_LIB\(\s*\[?([^,\]]*)\]?,.*') |
| 439 | libx_re = re.compile('AX_CHECK_LIBRARY\(\s*\[?[^,\]]*\]?,\s*\[?([^,\]]*)\]?,\s*\[?([a-zA-Z0-9-]*)\]?,.*') |
| 440 | progs_re = re.compile('_PROGS?\(\s*\[?[a-zA-Z0-9_]*\]?,\s*\[?([^,\]]*)\]?[),].*') |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 441 | dep_re = re.compile('([^ ><=]+)( [<>=]+ [^ ><=]+)?') |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 442 | ac_init_re = re.compile('AC_INIT\(\s*([^,]+),\s*([^,]+)[,)].*') |
| 443 | am_init_re = re.compile('AM_INIT_AUTOMAKE\(\s*([^,]+),\s*([^,]+)[,)].*') |
| 444 | define_re = re.compile('\s*(m4_)?define\(\s*([^,]+),\s*([^,]+)\)') |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 445 | version_re = re.compile('([0-9.]+)') |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 446 | |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 447 | defines = {} |
| 448 | def subst_defines(value): |
| 449 | newvalue = value |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 450 | for define, defval in defines.items(): |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 451 | newvalue = newvalue.replace(define, defval) |
| 452 | if newvalue != value: |
| 453 | return subst_defines(newvalue) |
| 454 | return value |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 455 | |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 456 | def process_value(value): |
| 457 | value = value.replace('[', '').replace(']', '') |
| 458 | if value.startswith('m4_esyscmd(') or value.startswith('m4_esyscmd_s('): |
| 459 | cmd = subst_defines(value[value.index('(')+1:-1]) |
| 460 | try: |
| 461 | if '|' in cmd: |
| 462 | cmd = 'set -o pipefail; ' + cmd |
| 463 | stdout, _ = bb.process.run(cmd, cwd=srctree, shell=True) |
| 464 | ret = stdout.rstrip() |
| 465 | except bb.process.ExecutionError as e: |
| 466 | ret = '' |
| 467 | elif value.startswith('m4_'): |
| 468 | return None |
| 469 | ret = subst_defines(value) |
| 470 | if ret: |
| 471 | ret = ret.strip('"\'') |
| 472 | return ret |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 473 | |
| 474 | # Since a configure.ac file is essentially a program, this is only ever going to be |
| 475 | # a hack unfortunately; but it ought to be enough of an approximation |
| 476 | if acfile: |
| 477 | srcfiles = [acfile] |
| 478 | else: |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 479 | srcfiles = RecipeHandler.checkfiles(srctree, ['acinclude.m4', 'configure.ac', 'configure.in']) |
| 480 | |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 481 | pcdeps = [] |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 482 | libdeps = [] |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 483 | deps = [] |
| 484 | unmapped = [] |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 485 | |
| 486 | RecipeHandler.load_binmap(tinfoil.config_data) |
| 487 | |
| 488 | def process_macro(keyword, value): |
| 489 | for handler in handlers: |
| 490 | if handler.process_macro(srctree, keyword, value, process_value, libdeps, pcdeps, deps, outlines, inherits, values): |
| 491 | return |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 492 | logger.debug('Found keyword %s with value "%s"' % (keyword, value)) |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 493 | if keyword == 'PKG_CHECK_MODULES': |
| 494 | res = pkg_re.search(value) |
| 495 | if res: |
| 496 | res = dep_re.findall(res.group(1)) |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 497 | if res: |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 498 | pcdeps.extend([x[0] for x in res]) |
| 499 | inherits.append('pkgconfig') |
| 500 | elif keyword == 'PKG_CHECK_EXISTS': |
| 501 | res = pkgce_re.search(value) |
| 502 | if res: |
| 503 | res = dep_re.findall(res.group(1)) |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 504 | if res: |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 505 | pcdeps.extend([x[0] for x in res]) |
| 506 | inherits.append('pkgconfig') |
| 507 | elif keyword in ('AM_GNU_GETTEXT', 'AM_GLIB_GNU_GETTEXT', 'GETTEXT_PACKAGE'): |
| 508 | inherits.append('gettext') |
| 509 | elif keyword in ('AC_PROG_INTLTOOL', 'IT_PROG_INTLTOOL'): |
| 510 | deps.append('intltool-native') |
| 511 | elif keyword == 'AM_PATH_GLIB_2_0': |
| 512 | deps.append('glib-2.0') |
| 513 | elif keyword in ('AC_CHECK_PROG', 'AC_PATH_PROG', 'AX_WITH_PROG'): |
| 514 | res = progs_re.search(value) |
| 515 | if res: |
| 516 | for prog in shlex.split(res.group(1)): |
| 517 | prog = prog.split()[0] |
| 518 | for handler in handlers: |
| 519 | if handler.process_prog(srctree, keyword, value, prog, deps, outlines, inherits, values): |
| 520 | return |
| 521 | progclass = progclassmap.get(prog, None) |
| 522 | if progclass: |
| 523 | inherits.append(progclass) |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 524 | else: |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 525 | progdep = RecipeHandler.recipebinmap.get(prog, None) |
| 526 | if not progdep: |
| 527 | progdep = progmap.get(prog, None) |
| 528 | if progdep: |
| 529 | deps.append(progdep) |
| 530 | elif progdep is None: |
| 531 | if not prog.startswith('$'): |
| 532 | unmapped.append(prog) |
| 533 | elif keyword == 'AC_CHECK_LIB': |
| 534 | res = lib_re.search(value) |
| 535 | if res: |
| 536 | lib = res.group(1) |
| 537 | if not lib.startswith('$'): |
| 538 | libdeps.append(lib) |
| 539 | elif keyword == 'AX_CHECK_LIBRARY': |
| 540 | res = libx_re.search(value) |
| 541 | if res: |
| 542 | lib = res.group(2) |
| 543 | if not lib.startswith('$'): |
| 544 | header = res.group(1) |
| 545 | libdeps.append((lib, header)) |
| 546 | elif keyword == 'AC_PATH_X': |
| 547 | deps.append('libx11') |
| 548 | elif keyword in ('AX_BOOST', 'BOOST_REQUIRE'): |
| 549 | deps.append('boost') |
| 550 | elif keyword in ('AC_PROG_LEX', 'AM_PROG_LEX', 'AX_PROG_FLEX'): |
| 551 | deps.append('flex-native') |
| 552 | elif keyword in ('AC_PROG_YACC', 'AX_PROG_BISON'): |
| 553 | deps.append('bison-native') |
| 554 | elif keyword == 'AX_CHECK_ZLIB': |
| 555 | deps.append('zlib') |
| 556 | elif keyword in ('AX_CHECK_OPENSSL', 'AX_LIB_CRYPTO'): |
| 557 | deps.append('openssl') |
| 558 | elif keyword == 'AX_LIB_CURL': |
| 559 | deps.append('curl') |
| 560 | elif keyword == 'AX_LIB_BEECRYPT': |
| 561 | deps.append('beecrypt') |
| 562 | elif keyword == 'AX_LIB_EXPAT': |
| 563 | deps.append('expat') |
| 564 | elif keyword == 'AX_LIB_GCRYPT': |
| 565 | deps.append('libgcrypt') |
| 566 | elif keyword == 'AX_LIB_NETTLE': |
| 567 | deps.append('nettle') |
| 568 | elif keyword == 'AX_LIB_READLINE': |
| 569 | deps.append('readline') |
| 570 | elif keyword == 'AX_LIB_SQLITE3': |
| 571 | deps.append('sqlite3') |
| 572 | elif keyword == 'AX_LIB_TAGLIB': |
| 573 | deps.append('taglib') |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 574 | elif keyword in ['AX_PKG_SWIG', 'AC_PROG_SWIG']: |
| 575 | deps.append('swig-native') |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 576 | elif keyword == 'AX_PROG_XSLTPROC': |
| 577 | deps.append('libxslt-native') |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 578 | elif keyword in ['AC_PYTHON_DEVEL', 'AX_PYTHON_DEVEL', 'AM_PATH_PYTHON']: |
| 579 | pythonclass = 'pythonnative' |
| 580 | res = version_re.search(value) |
| 581 | if res: |
| 582 | if res.group(1).startswith('3'): |
| 583 | pythonclass = 'python3native' |
| 584 | # Avoid replacing python3native with pythonnative |
| 585 | if not pythonclass in inherits and not 'python3native' in inherits: |
| 586 | if 'pythonnative' in inherits: |
| 587 | inherits.remove('pythonnative') |
| 588 | inherits.append(pythonclass) |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 589 | elif keyword == 'AX_WITH_CURSES': |
| 590 | deps.append('ncurses') |
| 591 | elif keyword == 'AX_PATH_BDB': |
| 592 | deps.append('db') |
| 593 | elif keyword == 'AX_PATH_LIB_PCRE': |
| 594 | deps.append('libpcre') |
| 595 | elif keyword == 'AC_INIT': |
| 596 | if extravalues is not None: |
| 597 | res = ac_init_re.match(value) |
| 598 | if res: |
| 599 | extravalues['PN'] = process_value(res.group(1)) |
| 600 | pv = process_value(res.group(2)) |
| 601 | if validate_pv(pv): |
| 602 | extravalues['PV'] = pv |
| 603 | elif keyword == 'AM_INIT_AUTOMAKE': |
| 604 | if extravalues is not None: |
| 605 | if 'PN' not in extravalues: |
| 606 | res = am_init_re.match(value) |
| 607 | if res: |
| 608 | if res.group(1) != 'AC_PACKAGE_NAME': |
| 609 | extravalues['PN'] = process_value(res.group(1)) |
| 610 | pv = process_value(res.group(2)) |
| 611 | if validate_pv(pv): |
| 612 | extravalues['PV'] = pv |
| 613 | elif keyword == 'define(': |
| 614 | res = define_re.match(value) |
| 615 | if res: |
| 616 | key = res.group(2).strip('[]') |
| 617 | value = process_value(res.group(3)) |
| 618 | if value is not None: |
| 619 | defines[key] = value |
| 620 | |
| 621 | keywords = ['PKG_CHECK_MODULES', |
| 622 | 'PKG_CHECK_EXISTS', |
| 623 | 'AM_GNU_GETTEXT', |
| 624 | 'AM_GLIB_GNU_GETTEXT', |
| 625 | 'GETTEXT_PACKAGE', |
| 626 | 'AC_PROG_INTLTOOL', |
| 627 | 'IT_PROG_INTLTOOL', |
| 628 | 'AM_PATH_GLIB_2_0', |
| 629 | 'AC_CHECK_PROG', |
| 630 | 'AC_PATH_PROG', |
| 631 | 'AX_WITH_PROG', |
| 632 | 'AC_CHECK_LIB', |
| 633 | 'AX_CHECK_LIBRARY', |
| 634 | 'AC_PATH_X', |
| 635 | 'AX_BOOST', |
| 636 | 'BOOST_REQUIRE', |
| 637 | 'AC_PROG_LEX', |
| 638 | 'AM_PROG_LEX', |
| 639 | 'AX_PROG_FLEX', |
| 640 | 'AC_PROG_YACC', |
| 641 | 'AX_PROG_BISON', |
| 642 | 'AX_CHECK_ZLIB', |
| 643 | 'AX_CHECK_OPENSSL', |
| 644 | 'AX_LIB_CRYPTO', |
| 645 | 'AX_LIB_CURL', |
| 646 | 'AX_LIB_BEECRYPT', |
| 647 | 'AX_LIB_EXPAT', |
| 648 | 'AX_LIB_GCRYPT', |
| 649 | 'AX_LIB_NETTLE', |
| 650 | 'AX_LIB_READLINE' |
| 651 | 'AX_LIB_SQLITE3', |
| 652 | 'AX_LIB_TAGLIB', |
| 653 | 'AX_PKG_SWIG', |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 654 | 'AC_PROG_SWIG', |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 655 | 'AX_PROG_XSLTPROC', |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 656 | 'AC_PYTHON_DEVEL', |
| 657 | 'AX_PYTHON_DEVEL', |
| 658 | 'AM_PATH_PYTHON', |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 659 | 'AX_WITH_CURSES', |
| 660 | 'AX_PATH_BDB', |
| 661 | 'AX_PATH_LIB_PCRE', |
| 662 | 'AC_INIT', |
| 663 | 'AM_INIT_AUTOMAKE', |
| 664 | 'define(', |
| 665 | ] |
| 666 | |
| 667 | for handler in handlers: |
| 668 | handler.extend_keywords(keywords) |
| 669 | |
| 670 | for srcfile in srcfiles: |
| 671 | nesting = 0 |
| 672 | in_keyword = '' |
| 673 | partial = '' |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 674 | with open(srcfile, 'r', errors='surrogateescape') as f: |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 675 | for line in f: |
| 676 | if in_keyword: |
| 677 | partial += ' ' + line.strip() |
| 678 | if partial.endswith('\\'): |
| 679 | partial = partial[:-1] |
| 680 | nesting = nesting + line.count('(') - line.count(')') |
| 681 | if nesting == 0: |
| 682 | process_macro(in_keyword, partial) |
| 683 | partial = '' |
| 684 | in_keyword = '' |
| 685 | else: |
| 686 | for keyword in keywords: |
| 687 | if keyword in line: |
| 688 | nesting = line.count('(') - line.count(')') |
| 689 | if nesting > 0: |
| 690 | partial = line.strip() |
| 691 | if partial.endswith('\\'): |
| 692 | partial = partial[:-1] |
| 693 | in_keyword = keyword |
| 694 | else: |
| 695 | process_macro(keyword, line.strip()) |
| 696 | break |
| 697 | |
| 698 | if in_keyword: |
| 699 | process_macro(in_keyword, partial) |
| 700 | |
| 701 | if extravalues: |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 702 | for k,v in list(extravalues.items()): |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 703 | if v: |
| 704 | if v.startswith('$') or v.startswith('@') or v.startswith('%'): |
| 705 | del extravalues[k] |
| 706 | else: |
| 707 | extravalues[k] = v.strip('"\'').rstrip('()') |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 708 | |
| 709 | if unmapped: |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 710 | outlines.append('# NOTE: the following prog dependencies are unknown, ignoring: %s' % ' '.join(list(set(unmapped)))) |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 711 | |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 712 | RecipeHandler.handle_depends(libdeps, pcdeps, deps, outlines, values, tinfoil.config_data) |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 713 | |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 714 | for handler in handlers: |
| 715 | handler.post_process(srctree, libdeps, pcdeps, deps, outlines, inherits, values) |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 716 | |
| 717 | if inherits: |
| 718 | values['inherit'] = ' '.join(list(set(inherits))) |
| 719 | |
| 720 | return values |
| 721 | |
| 722 | |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 723 | class AutotoolsExtensionHandler(object): |
| 724 | '''Base class for Autotools extension handlers''' |
| 725 | def process_macro(self, srctree, keyword, value, process_value, libdeps, pcdeps, deps, outlines, inherits, values): |
| 726 | ''' |
| 727 | Handle a macro parsed out of an autotools file. Note that if you want this to be called |
| 728 | for any macro other than the ones AutotoolsRecipeHandler already looks for, you'll need |
| 729 | to add it to the keywords list in extend_keywords(). |
| 730 | Return True if you've completely handled the passed in macro, otherwise return False. |
| 731 | ''' |
| 732 | return False |
| 733 | |
| 734 | def extend_keywords(self, keywords): |
| 735 | '''Adds keywords to be recognised by the parser (so that you get a call to process_macro)''' |
| 736 | return |
| 737 | |
| 738 | def process_prog(self, srctree, keyword, value, prog, deps, outlines, inherits, values): |
| 739 | ''' |
| 740 | Handle an AC_PATH_PROG, AC_CHECK_PROG etc. line |
| 741 | Return True if you've completely handled the passed in macro, otherwise return False. |
| 742 | ''' |
| 743 | return False |
| 744 | |
| 745 | def post_process(self, srctree, fn, pkg, deps, outlines, inherits, values): |
| 746 | ''' |
| 747 | Apply any desired post-processing on the output |
| 748 | ''' |
| 749 | return |
| 750 | |
| 751 | |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 752 | class MakefileRecipeHandler(RecipeHandler): |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 753 | def process(self, srctree, classes, lines_before, lines_after, handled, extravalues): |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 754 | if 'buildsystem' in handled: |
| 755 | return False |
| 756 | |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 757 | makefile = RecipeHandler.checkfiles(srctree, ['Makefile', 'makefile', 'GNUmakefile']) |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 758 | if makefile: |
| 759 | lines_after.append('# NOTE: this is a Makefile-only piece of software, so we cannot generate much of the') |
| 760 | lines_after.append('# recipe automatically - you will need to examine the Makefile yourself and ensure') |
| 761 | lines_after.append('# that the appropriate arguments are passed in.') |
| 762 | lines_after.append('') |
| 763 | |
| 764 | scanfile = os.path.join(srctree, 'configure.scan') |
| 765 | skipscan = False |
| 766 | try: |
| 767 | stdout, stderr = bb.process.run('autoscan', cwd=srctree, shell=True) |
| 768 | except bb.process.ExecutionError as e: |
| 769 | skipscan = True |
| 770 | if scanfile and os.path.exists(scanfile): |
| 771 | values = AutotoolsRecipeHandler.extract_autotools_deps(lines_before, srctree, acfile=scanfile) |
| 772 | classes.extend(values.pop('inherit', '').split()) |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 773 | for var, value in values.items(): |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 774 | if var == 'DEPENDS': |
| 775 | lines_before.append('# NOTE: some of these dependencies may be optional, check the Makefile and/or upstream documentation') |
| 776 | lines_before.append('%s = "%s"' % (var, value)) |
| 777 | lines_before.append('') |
| 778 | for f in ['configure.scan', 'autoscan.log']: |
| 779 | fp = os.path.join(srctree, f) |
| 780 | if os.path.exists(fp): |
| 781 | os.remove(fp) |
| 782 | |
| 783 | self.genfunction(lines_after, 'do_configure', ['# Specify any needed configure commands here']) |
| 784 | |
| 785 | func = [] |
| 786 | func.append('# You will almost certainly need to add additional arguments here') |
| 787 | func.append('oe_runmake') |
| 788 | self.genfunction(lines_after, 'do_compile', func) |
| 789 | |
| 790 | installtarget = True |
| 791 | try: |
Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 792 | stdout, stderr = bb.process.run('make -n install', cwd=srctree, shell=True) |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 793 | except bb.process.ExecutionError as e: |
| 794 | if e.exitcode != 1: |
| 795 | installtarget = False |
| 796 | func = [] |
| 797 | if installtarget: |
| 798 | func.append('# This is a guess; additional arguments may be required') |
| 799 | makeargs = '' |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 800 | with open(makefile[0], 'r', errors='surrogateescape') as f: |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 801 | for i in range(1, 100): |
| 802 | if 'DESTDIR' in f.readline(): |
| 803 | makeargs += " 'DESTDIR=${D}'" |
| 804 | break |
| 805 | func.append('oe_runmake install%s' % makeargs) |
| 806 | else: |
| 807 | func.append('# NOTE: unable to determine what to put here - there is a Makefile but no') |
| 808 | func.append('# target named "install", so you will need to define this yourself') |
| 809 | self.genfunction(lines_after, 'do_install', func) |
| 810 | |
| 811 | handled.append('buildsystem') |
| 812 | else: |
| 813 | lines_after.append('# NOTE: no Makefile found, unable to determine what needs to be done') |
| 814 | lines_after.append('') |
| 815 | self.genfunction(lines_after, 'do_configure', ['# Specify any needed configure commands here']) |
| 816 | self.genfunction(lines_after, 'do_compile', ['# Specify compilation commands here']) |
| 817 | self.genfunction(lines_after, 'do_install', ['# Specify install commands here']) |
| 818 | |
| 819 | |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 820 | class VersionFileRecipeHandler(RecipeHandler): |
| 821 | def process(self, srctree, classes, lines_before, lines_after, handled, extravalues): |
| 822 | if 'PV' not in extravalues: |
| 823 | # Look for a VERSION or version file containing a single line consisting |
| 824 | # only of a version number |
| 825 | filelist = RecipeHandler.checkfiles(srctree, ['VERSION', 'version']) |
| 826 | version = None |
| 827 | for fileitem in filelist: |
| 828 | linecount = 0 |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 829 | with open(fileitem, 'r', errors='surrogateescape') as f: |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 830 | for line in f: |
| 831 | line = line.rstrip().strip('"\'') |
| 832 | linecount += 1 |
| 833 | if line: |
| 834 | if linecount > 1: |
| 835 | version = None |
| 836 | break |
| 837 | else: |
| 838 | if validate_pv(line): |
| 839 | version = line |
| 840 | if version: |
| 841 | extravalues['PV'] = version |
| 842 | break |
| 843 | |
| 844 | |
| 845 | class SpecFileRecipeHandler(RecipeHandler): |
| 846 | def process(self, srctree, classes, lines_before, lines_after, handled, extravalues): |
| 847 | if 'PV' in extravalues and 'PN' in extravalues: |
| 848 | return |
| 849 | filelist = RecipeHandler.checkfiles(srctree, ['*.spec'], recursive=True) |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 850 | valuemap = {'Name': 'PN', |
| 851 | 'Version': 'PV', |
| 852 | 'Summary': 'SUMMARY', |
| 853 | 'Url': 'HOMEPAGE', |
| 854 | 'License': 'LICENSE'} |
| 855 | foundvalues = {} |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 856 | for fileitem in filelist: |
| 857 | linecount = 0 |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 858 | with open(fileitem, 'r', errors='surrogateescape') as f: |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 859 | for line in f: |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 860 | for value, varname in valuemap.items(): |
| 861 | if line.startswith(value + ':') and not varname in foundvalues: |
| 862 | foundvalues[varname] = line.split(':', 1)[1].strip() |
| 863 | break |
| 864 | if len(foundvalues) == len(valuemap): |
| 865 | break |
Brad Bishop | d7bf8c1 | 2018-02-25 22:55:05 -0500 | [diff] [blame] | 866 | # Drop values containing unexpanded RPM macros |
| 867 | for k in list(foundvalues.keys()): |
| 868 | if '%' in foundvalues[k]: |
| 869 | del foundvalues[k] |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 870 | if 'PV' in foundvalues: |
| 871 | if not validate_pv(foundvalues['PV']): |
| 872 | del foundvalues['PV'] |
| 873 | license = foundvalues.pop('LICENSE', None) |
| 874 | if license: |
| 875 | liccomment = '# NOTE: spec file indicates the license may be "%s"' % license |
| 876 | for i, line in enumerate(lines_before): |
| 877 | if line.startswith('LICENSE ='): |
| 878 | lines_before.insert(i, liccomment) |
| 879 | break |
| 880 | else: |
| 881 | lines_before.append(liccomment) |
| 882 | extravalues.update(foundvalues) |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 883 | |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 884 | def register_recipe_handlers(handlers): |
Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 885 | # Set priorities with some gaps so that other plugins can insert |
| 886 | # their own handlers (so avoid changing these numbers) |
| 887 | handlers.append((CmakeRecipeHandler(), 50)) |
| 888 | handlers.append((AutotoolsRecipeHandler(), 40)) |
| 889 | handlers.append((SconsRecipeHandler(), 30)) |
| 890 | handlers.append((QmakeRecipeHandler(), 20)) |
| 891 | handlers.append((MakefileRecipeHandler(), 10)) |
| 892 | handlers.append((VersionFileRecipeHandler(), -1)) |
| 893 | handlers.append((SpecFileRecipeHandler(), -1)) |