scons: add ParseSourceList method
ParseSourceList() can be used to parse a source list file and returns the source files defined in it. It is supposed to be used like this # get the list of source files from C_SOURCES in Makefile.sources sources = env.ParseSourceList('Makefile.sources', 'C_SOURCES') The syntax of a source list file is compatible with GNU Make. This effectively allows SConscript and Makefile to share the source lists. Acked-by: José Fonseca <jfonseca@vmware.com> Acked-by: Chad Versace <chad@chad-versace.us>
This commit is contained in:
@@ -42,6 +42,7 @@ import SCons.Scanner
|
||||
|
||||
import fixes
|
||||
|
||||
import source_list
|
||||
|
||||
def quietCommandLines(env):
|
||||
# Quiet command lines
|
||||
@@ -229,6 +230,40 @@ def createPkgConfigMethods(env):
|
||||
env.AddMethod(pkg_use_modules, 'PkgUseModules')
|
||||
|
||||
|
||||
def parse_source_list(env, filename, names=None):
|
||||
# parse the source list file
|
||||
parser = source_list.SourceListParser()
|
||||
src = env.File(filename).srcnode()
|
||||
sym_table = parser.parse(src.abspath)
|
||||
|
||||
if names:
|
||||
if isinstance(names, basestring):
|
||||
names = [names]
|
||||
|
||||
symbols = names
|
||||
else:
|
||||
symbols = sym_table.keys()
|
||||
|
||||
# convert the symbol table to source lists
|
||||
src_lists = {}
|
||||
for sym in symbols:
|
||||
val = sym_table[sym]
|
||||
src_lists[sym] = [f for f in val.split(' ') if f]
|
||||
|
||||
# if names are given, concatenate the lists
|
||||
if names:
|
||||
srcs = []
|
||||
for name in names:
|
||||
srcs.extend(src_lists[name])
|
||||
|
||||
return srcs
|
||||
else:
|
||||
return src_lists
|
||||
|
||||
def createParseSourceListMethod(env):
|
||||
env.AddMethod(parse_source_list, 'ParseSourceList')
|
||||
|
||||
|
||||
def generate(env):
|
||||
"""Common environment generation code"""
|
||||
|
||||
@@ -240,6 +275,7 @@ def generate(env):
|
||||
createConvenienceLibBuilder(env)
|
||||
createCodeGenerateMethod(env)
|
||||
createPkgConfigMethods(env)
|
||||
createParseSourceListMethod(env)
|
||||
|
||||
# for debugging
|
||||
#print env.Dump()
|
||||
|
123
scons/source_list.py
Normal file
123
scons/source_list.py
Normal file
@@ -0,0 +1,123 @@
|
||||
"""Source List Parser
|
||||
|
||||
The syntax of a source list file is a very small subset of GNU Make. These
|
||||
features are supported
|
||||
|
||||
operators: +=, :=
|
||||
line continuation
|
||||
non-nested variable expansion
|
||||
comment
|
||||
|
||||
The goal is to allow Makefile's and SConscript's to share source listing.
|
||||
"""
|
||||
|
||||
class SourceListParser(object):
|
||||
def __init__(self):
|
||||
self._reset()
|
||||
|
||||
def _reset(self, filename=None):
|
||||
self.filename = filename
|
||||
|
||||
self.line_no = 1
|
||||
self.line_cont = ''
|
||||
self.symbol_table = {}
|
||||
|
||||
def _error(self, msg):
|
||||
raise RuntimeError('%s:%d: %s' % (self.filename, self.line_no, msg))
|
||||
|
||||
def _next_dereference(self, val, cur):
|
||||
"""Locate the next $(...) in value."""
|
||||
deref_pos = val.find('$', cur)
|
||||
if deref_pos < 0:
|
||||
return (-1, -1)
|
||||
elif val[deref_pos + 1] != '(':
|
||||
self._error('non-variable dereference')
|
||||
|
||||
deref_end = val.find(')', deref_pos + 2)
|
||||
if deref_end < 0:
|
||||
self._error('unterminated variable dereference')
|
||||
|
||||
return (deref_pos, deref_end + 1)
|
||||
|
||||
def _expand_value(self, val):
|
||||
"""Perform variable expansion."""
|
||||
expanded = ''
|
||||
cur = 0
|
||||
while True:
|
||||
deref_pos, deref_end = self._next_dereference(val, cur)
|
||||
if deref_pos < 0:
|
||||
expanded += val[cur:]
|
||||
break
|
||||
|
||||
sym = val[(deref_pos + 2):(deref_end - 1)]
|
||||
expanded += val[cur:deref_pos] + self.symbol_table[sym]
|
||||
cur = deref_end
|
||||
|
||||
return expanded
|
||||
|
||||
def _parse_definition(self, line):
|
||||
"""Parse a variable definition line."""
|
||||
op_pos = line.find('=')
|
||||
op_end = op_pos + 1
|
||||
if op_pos < 0:
|
||||
self._error('not a variable definition')
|
||||
|
||||
if op_pos > 0 and line[op_pos - 1] in [':', '+']:
|
||||
op_pos -= 1
|
||||
else:
|
||||
self._error('only := and += are supported')
|
||||
|
||||
# set op, sym, and val
|
||||
op = line[op_pos:op_end]
|
||||
sym = line[:op_pos].strip()
|
||||
val = self._expand_value(line[op_end:].lstrip())
|
||||
|
||||
if op == ':=':
|
||||
self.symbol_table[sym] = val
|
||||
elif op == '+=':
|
||||
self.symbol_table[sym] += ' ' + val
|
||||
|
||||
def _parse_line(self, line):
|
||||
"""Parse a source list line."""
|
||||
# more lines to come
|
||||
if line and line[-1] == '\\':
|
||||
# spaces around "\\\n" are replaced by a single space
|
||||
if self.line_cont:
|
||||
self.line_cont += line[:-1].strip() + ' '
|
||||
else:
|
||||
self.line_cont = line[:-1].rstrip() + ' '
|
||||
return 0
|
||||
|
||||
# combine with previous lines
|
||||
if self.line_cont:
|
||||
line = self.line_cont + line.lstrip()
|
||||
self.line_cont = ''
|
||||
|
||||
if line:
|
||||
begins_with_tab = (line[0] == '\t')
|
||||
|
||||
line = line.lstrip()
|
||||
if line[0] != '#':
|
||||
if begins_with_tab:
|
||||
self._error('recipe line not supported')
|
||||
else:
|
||||
self._parse_definition(line)
|
||||
|
||||
return 1
|
||||
|
||||
def parse(self, filename):
|
||||
"""Parse a source list file."""
|
||||
if self.filename != filename:
|
||||
fp = open(filename)
|
||||
lines = fp.read().splitlines()
|
||||
fp.close()
|
||||
|
||||
try:
|
||||
self._reset(filename)
|
||||
for line in lines:
|
||||
self.line_no += self._parse_line(line)
|
||||
except:
|
||||
self._reset()
|
||||
raise
|
||||
|
||||
return self.symbol_table
|
Reference in New Issue
Block a user