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