#!/usr/bin/env python
##
# @file
# waf Makefile.am related tools
#
# a Makefile.am to scons converter
#
# (@) 2005 LiuCougar - published under the GPL license
#
import os, re, types, sys, string, shutil, stat, glob, amtool
## Class for automatically converting Makefile.am to SConscript
#
# The class can handle simple Makefile.am 100% correct, for complex ones, manual
# fine-tunning is required. These are something that can not handle:
# - Custom make target
# - extra moc files
# - checking programs (test/EXTRA, how to enable checking?? )
# - conditional compilation
# - install dir support
# - DATA files handling
#
#
# @TODO fix installation
class AM2Waf(amtool.AMFile):
def __init__(self):
amtool.AMFile.__init__(self)
self.ignoreIncludePaths = ['$(top_srcdir)', '$(all_includes)']
self.defaultUseLibs = 'QT QTCORE QTGUI QT3SUPPORT KDE4'
self.LibHandlerMapping = {
'noinst' : 'bld.createObj(\'kde\', \'convenience\')',
'kdeinit' : 'bld.createObj()',
'lib' : 'bld.createObj(\'kde\', \'shlib\')',
'kde_module' : 'bld.createObj(\'kde\', \'module\')',
'plugin' : 'bld.createObj(\'cpp\', \'shlib\')',
}
self.ProgHandlerMapping = {
'bin' : 'bld.createObj(\'\kde\', \'program\')',
'check' : 'env.kdeobj(\'program\')\nobj.env = env.Copy()\nobj.env[\'NOAUTOINSTALL\'] = 1',
'EXTRA' : 'env.kdeobj(\'program\')\nobj.env = env.Copy()\nobj.env[\'NOAUTOINSTALL\'] = 1',
}
self.DataHandlerMapping = {
'kde_htmldir' : '',
'kde_appsdir' : 'KDEAPPS',
'kde_icondir' : 'KDEICONS',
'kde_sounddir' : '',
'kde_datadir' : 'KDEAPPS',
'kde_locale' : 'KDELOCALE',
'kde_cgidir' : '',
'kde_confdir' : 'KDECONF',
'kde_kcfgdir' : 'KDEKCFG',
'kde_mimedir' : 'KDEMIME',
'kde_toolbardir' : '',
'kde_wallpaperdir' : '',
'kde_templatesdir' : '',
'kde_bindir' : 'KDEBIN',
'kde_servicesdir' : 'KDESERV',
'kde_servicetypesdir' : 'KDESERVTYPES',
'kde_moduledir' : 'KDEMODULE',
'kde_styledir' : '',
'kde_widgetdir' : '',
'xdg_appsdir' : '',
'xdg_menudir' : '',
'xdg_directorydir' : '',
}
self.out_buf_header = """#! /usr/bin/env python
#generated from %s by am2waf.py
"""
#generate source lists
def generateSources(self):
out_buf = ""
for key in self.sources.keys():
out_buf += "\t"
out_buf += key + "_sources = \"\"\"\n"
sources = self.sources[key].split()
sources.sort()
while len(sources) >= 4:
out_buf += ' '.join(sources[:4]) + "\n"
del sources[:4]
if len(sources):
out_buf += ' '.join(sources) + "\n"
out_buf += "\"\"\"\n"
return out_buf
#generate header file lists
def generateHeaders(self):
out_buf = ""
for key in self.headers.keys():
out_buf += "\t"
out_buf += key + "_headers = \"\"\"\n"
headers = self.headers[key].split()
headers.sort()
while len(headers) >= 4:
out_buf += ' '.join(headers[:4]) + "\n"
del headers[:4]
if len(headers):
out_buf += ' '.join(headers) + "\n"
out_buf += "\"\"\"\n"
return out_buf
def generateLibadds(self, name):
reg = re.compile("(.*)lib([^/]*)\.la$")
def convertLocalLibs(lib):
lib = lib.replace('$(top_srcdir)', '#')
lib = lib.replace('$(top_builddir)', '##')
lib = lib.replace('$(srcdir)/', '')
result=reg.match(lib)
if result:
path = result.group(1)
if path.endswith('/'): path = path[:-1]
if path[:2] == "./": path = path[2:]
return (path, result.group(2))
return ('', lib)
uselibs = self.defaultUseLibs.split()
out_buf = ""
if self.libadds.has_key(name):
convertedlibs = []
libpaths = []
libs = self.libadds[name].split()
for lib in libs:
if lib[:2] == '$(' and lib[-1:] == ')':
if lib[2:6] == "LIB_":
uselibs.append(lib[6:-1])
elif lib[2:5] == "LIB":
uselibs.append(lib[5:-1])
else:
print "WARNING: UNKNOW makefile variable in LDADDS %s" % lib
else:
path, libname = convertLocalLibs(lib)
if len(path) and not libpaths.count(path):
libpaths.append(path)
convertedlibs.append(libname)
if len(convertedlibs):
out_buf += "\tobj.libpaths = '%s '\n" % ' '.join(libpaths)
out_buf += "\tobj.libs = '%s '\n" % ' '.join(convertedlibs)
out_buf += '\tobj.uselib = \'%s \'\n' % ' '.join(uselibs)
return out_buf
def generateObj(self, islib, objtype, name):
if islib:
out_buf = "\tobj = %s\n" % self.LibHandlerMapping[objtype]
else:
out_buf = "\tobj = %s\n" % self.ProgHandlerMapping[objtype]
include = ""
#TARGET
out_buf += "\tobj.target = '%s'\n" % name
#FIXME: other LDFLAGS support
#Version number
if self.ldflags.has_key(name):
flags = self.ldflags[name].split()
if islib and flags.count('-version-info') > 0:
index = flags.index('-version-info') + 1
if index < len(flags):
version = flags[index]
if version.replace(':','').isdigit():
tab = version.split(':')
if len(tab) == 3:
val = eval(tab[0])-eval(tab[2])
newVal = str(val) + "." + tab[2]+ "." + tab[1]
else:
newVal = tab[0] + "." + tab[1]+ ".0"
out_buf += "\tobj.vnum = '%s'\n" % newVal
else:
print "WARNING: version-info format is incorrect"
#SOURCES
out_buf += "\tobj.source = %s_sources\n" % name
#INCLUDES
if self.includes.has_key(name):
include = "'" + self.includes[name] + "'"
elif self.includes.has_key('_DIR_GLOBAL_'):
include = 'includes'
if include:
out_buf += "\tobj.includes = %s\n" % include
#LIBADD
out_buf += self.generateLibadds(name)
#out_buf += 'obj.execute()\n\n'
return out_buf
def generateSubdirs(self):
out_buf = ""
if len(self.subdirs):
dirs = self.subdirs.split()
if dirs.count('.'): del dirs[dirs.index('.')]
out_buf = "\tbld.add_subdirs(['%s'])\n\n" % "', '".join(dirs)
return out_buf;
def generateData(self, files, inst_dir):
def getKDEInstType(rawdir):
if rawdir[:2] != "$(":
print "ERROR: Do not understand data install dir format %s\n" % rawdir
return "",""
instype = subdir = ""
if rawdir.endswith(")"):
instype = rawdir[2:-1]
else:
reg = re.compile("\$\((.*)\)/(.*)")
result = reg.match(rawdir)
if result:
instype = result.group(1)
subdir = result.group(2)
if not len(instype) or not self.DataHandlerMapping.has_key(instype):
instype = ""
else:
instype = self.DataHandlerMapping[instype]
return (instype, subdir)
out_buf = ""
if len(files):
(insttype, subdir) = getKDEInstType(inst_dir)
if insttype:
out_buf = "\tinstall_files('%s', '%s', '%s')\n\n" % (insttype, subdir, files)
return out_buf
def generateConscript(self):
out_buf = self.out_buf_header % self.path
out_buf +="def build(bld):\n"
out_buf +="\t# process subfolders from here\n"
self.getIncludes() #fix path
out_buf += self.generateSources()
out_buf += self.generateHeaders()
#find global includes setting for self dir:
if self.includes.has_key('_DIR_GLOBAL_'):
out_buf += "\tincludes = '%s '\n\n" % self.includes['_DIR_GLOBAL_']
#generate library objects
for libtypekey in self.libs.keys():
if not self.LibHandlerMapping.has_key(libtypekey):
print "ERROR: library type %s is not defined in LibHandlerMapping" % libtypekey
return
for key in self.libs[libtypekey].split():
out_buf += self.generateObj(1, libtypekey, key)
#generate program objects
for progtypekey in self.progs.keys():
if not self.ProgHandlerMapping.has_key(progtypekey):
print "ERROR: Program type %s is not defined in ProgHandlerMapping" % progtypekey
return
#FIXME: implement test/check support
if progtypekey in ['check', 'EXTRA']:
continue
for key in self.progs[progtypekey].split():
out_buf += self.generateObj(0, progtypekey, key)
#generate data files
for dataname in self.data.keys():
out_buf += self.generateData(self.data[dataname], self.datadirs[dataname])
out_buf += self.generateSubdirs()
if len(self.defines):
out_buf += '\n"""Unhandled Defines \n'
for key in self.defines.keys():
out_buf += key + ' = ' + self.defines[key] + "\n"
out_buf += '"""\n'
if len(self.targets):
out_buf += '\n"""Unhandled Targets \n'
for key in self.targets.keys():
out_buf += key + ' = ' + self.targets[key] + "\n"
out_buf += '"""\n'
return out_buf
def getIncludes(self):
amtool.AMFile.getIncludes(self)
for key in self.includes.keys():
self.includes[key] = self.convertIncludesPath(self.includes[key])
return self.includes
def convertIncludesPath(self, paths_str):
retlist = []
paths = paths_str.split()
for path in paths:
if path[:2] == '-I':
path = path[2:]
if path in self.ignoreIncludePaths:
continue
path = path.replace('$(top_srcdir)', '#')
path = path.replace('$(top_builddir)', '#')
path = path.replace('$(srcdir)/', '')
retlist.append(self.convertMakeFileVariable(path))
return ' '.join(retlist)
if len(sys.argv) == 1:
print "am2waf [options] Makefile.am"
print "Convert a Makefile.am to waf wscript"
print "options:"
print " -o file Write generated wscript to this file"
print " -w Write generated wscript to the same dir as the Makefile.am"
print "By default, am2waf will output the generated wscript to stdout"
else:
outputfile = ""
for a in range(1,len(sys.argv)):
if sys.argv[a][:2] == '-o' and a+1 < len(sys.argv):
outputfile = sys.argv[a+1]
a += 1
elif sys.argv[a][:2] == '-w':
outputfile = "#"
for a in range(1,len(sys.argv)):
if sys.argv[a][:1] == '-':
continue
am_file = AM2Waf()
inputfile = sys.argv[a];
if not am_file.read(inputfile):
print "ERROR: can not read file %s" % inputfile
continue
if len(outputfile):
try:
if outputfile == "#":
outputfile = inputfile.replace("Makefile.am", "wscript")
dest = open(outputfile, 'w')
except:
exit
dest.write(am_file.generateConscript())
dest.close()
print "File %s is successfully created." % outputfile
else:
print am_file.generateConscript()
break
|