pro2cmake: Be more faithful in the representation of operations
Do a better approximation of =, +=, *= and -=. Change-Id: I94765532f278deaac330b27cd5a3f41f319c6477 Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
This commit is contained in:
parent
f0aa8fa48e
commit
19874e2381
@ -119,9 +119,74 @@ def map_source_to_fs(base_dir: str, file: str, source: str) -> typing.Optional[s
|
|||||||
return os.path.join(base_dir, source)
|
return os.path.join(base_dir, source)
|
||||||
|
|
||||||
|
|
||||||
|
class Operation:
|
||||||
|
def __init__(self, value):
|
||||||
|
if isinstance(value, list):
|
||||||
|
self._value = value
|
||||||
|
else:
|
||||||
|
self._value = [str(value),]
|
||||||
|
|
||||||
|
def process(self, input):
|
||||||
|
assert(False)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
assert(False)
|
||||||
|
|
||||||
|
|
||||||
|
class AddOperation(Operation):
|
||||||
|
def process(self, input):
|
||||||
|
return input + self._value
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return '+({})'.format(self._value)
|
||||||
|
|
||||||
|
|
||||||
|
class UniqueAddOperation(Operation):
|
||||||
|
def process(self, input):
|
||||||
|
result = input
|
||||||
|
for v in self._value:
|
||||||
|
if not v in result:
|
||||||
|
result += [v,]
|
||||||
|
return result
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return '*({})'.format(self._value)
|
||||||
|
|
||||||
|
|
||||||
|
class SetOperation(Operation):
|
||||||
|
def process(self, input):
|
||||||
|
return self._value
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return '=({})'.format(self._value)
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveOperation(Operation):
|
||||||
|
def __init__(self, value):
|
||||||
|
super().__init__(value)
|
||||||
|
|
||||||
|
def process(self, input):
|
||||||
|
input_set = set(input)
|
||||||
|
result = []
|
||||||
|
for v in self._value:
|
||||||
|
if v in input_set:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
result += ['-{}'.format(v),]
|
||||||
|
return result
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return '-({})'.format(self._value)
|
||||||
|
|
||||||
|
|
||||||
class Scope:
|
class Scope:
|
||||||
def __init__(self, file: typing.Optional[str]=None, condition: str='', base_dir: str='') -> None:
|
def __init__(self, parent_scope: typing.Optional['Scope'],
|
||||||
self._parent = None # type: Scope
|
file: typing.Optional[str]=None, condition: str='', base_dir: str='') -> None:
|
||||||
|
if parent_scope:
|
||||||
|
parent_scope._add_child(self)
|
||||||
|
else:
|
||||||
|
self._parent = None
|
||||||
|
|
||||||
self._basedir = base_dir
|
self._basedir = base_dir
|
||||||
if file:
|
if file:
|
||||||
self._currentdir = os.path.dirname(file)
|
self._currentdir = os.path.dirname(file)
|
||||||
@ -133,27 +198,14 @@ class Scope:
|
|||||||
self._file = file
|
self._file = file
|
||||||
self._condition = map_condition(condition)
|
self._condition = map_condition(condition)
|
||||||
self._children = [] # type: List[Scope]
|
self._children = [] # type: List[Scope]
|
||||||
self._values = {} # type: Dict[str, List[str]]
|
self._operations = {} # type: Dict[str]
|
||||||
|
|
||||||
def merge(self, other: 'Scope') -> None:
|
def merge(self, other: 'Scope') -> None:
|
||||||
for c in other._children:
|
for c in other._children:
|
||||||
self.add_child(c)
|
self._add_child(c)
|
||||||
other.set_basedir(self._basedir)
|
|
||||||
|
|
||||||
for k in self._values.keys():
|
for k in other._operations.keys():
|
||||||
if k == 'TEMPLATE':
|
self._operations[key] = other._operations[k]
|
||||||
assert other.get(k, []) == self.get(k, [])
|
|
||||||
else:
|
|
||||||
self.append_value(k, other.get(k, []))
|
|
||||||
|
|
||||||
for k in other._values.keys():
|
|
||||||
if k not in self._values:
|
|
||||||
self.set_value(k, other.get(k))
|
|
||||||
|
|
||||||
def set_basedir(self, dir: str) -> None:
|
|
||||||
self._basedir = dir
|
|
||||||
for c in self._children:
|
|
||||||
c.set_basedir(dir)
|
|
||||||
|
|
||||||
def basedir(self) -> str:
|
def basedir(self) -> str:
|
||||||
return self._basedir
|
return self._basedir
|
||||||
@ -161,9 +213,27 @@ class Scope:
|
|||||||
def currentdir(self) -> str:
|
def currentdir(self) -> str:
|
||||||
return self._currentdir
|
return self._currentdir
|
||||||
|
|
||||||
|
def diff(self, key: str, default: typing.Optional[List[str]]=[]) -> List[str]:
|
||||||
|
mine = self.get(key, default)
|
||||||
|
|
||||||
|
if self._parent:
|
||||||
|
parent = self._parent.get(key, default)
|
||||||
|
if (parent == mine):
|
||||||
|
return []
|
||||||
|
|
||||||
|
parent_set = set(parent)
|
||||||
|
mine_set = set(mine)
|
||||||
|
|
||||||
|
added = [x for x in mine if not x in parent_set]
|
||||||
|
removed = [x for x in parent if not x in mine_set]
|
||||||
|
|
||||||
|
return added + list('# {}'.format(x) for x in removed)
|
||||||
|
return mine
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def FromDict(file: str, statements, cond: str = '', base_dir: str = ''):
|
def FromDict(parent_scope: typing.Optional['Scope'],
|
||||||
scope = Scope(file, cond, base_dir)
|
file: str, statements, cond: str = '', base_dir: str = ''):
|
||||||
|
scope = Scope(parent_scope, file, cond, base_dir)
|
||||||
for statement in statements:
|
for statement in statements:
|
||||||
if isinstance(statement, list): # Handle skipped parts...
|
if isinstance(statement, list): # Handle skipped parts...
|
||||||
assert not statement
|
assert not statement
|
||||||
@ -174,16 +244,19 @@ class Scope:
|
|||||||
key = statement.get('key', '')
|
key = statement.get('key', '')
|
||||||
value = statement.get('value', [])
|
value = statement.get('value', [])
|
||||||
assert key != ''
|
assert key != ''
|
||||||
|
print('#### {}: {} = {}.'.format(operation, key, value))
|
||||||
|
|
||||||
if key in ('HEADERS', 'SOURCES', 'INCLUDEPATH') or key.endswith('_HEADERS') or key.endswith('_SOURCES'):
|
if key in ('HEADERS', 'SOURCES', 'INCLUDEPATH') or key.endswith('_HEADERS') or key.endswith('_SOURCES'):
|
||||||
value = [map_to_file(v, scope.basedir(), scope.currentdir()) for v in value]
|
value = [map_to_file(v, scope.basedir(), scope.currentdir()) for v in value]
|
||||||
|
|
||||||
if operation == '=':
|
if operation == '=':
|
||||||
scope.set_value(key, value)
|
scope._append_operation(key, SetOperation(value))
|
||||||
elif operation == '-=':
|
elif operation == '-=':
|
||||||
scope.substract_value(key, value)
|
scope._append_operation(key, RemoveOperation(value))
|
||||||
elif operation == '+=' or operation == '*=':
|
elif operation == '+=':
|
||||||
scope.append_value(key, value)
|
scope._append_operation(key, AddOperation(value))
|
||||||
|
elif operation == '*=':
|
||||||
|
scope._append_operation(key, UniqueAddOperation(value))
|
||||||
else:
|
else:
|
||||||
print('Unexpected operation "{}" in scope with condition {}.'.format(operation, cond))
|
print('Unexpected operation "{}" in scope with condition {}.'.format(operation, cond))
|
||||||
assert(False)
|
assert(False)
|
||||||
@ -192,33 +265,36 @@ class Scope:
|
|||||||
|
|
||||||
condition = statement.get('condition', None)
|
condition = statement.get('condition', None)
|
||||||
if condition:
|
if condition:
|
||||||
child = Scope.FromDict(file, statement.get('statements'), condition, scope.basedir())
|
child = Scope.FromDict(scope, file, statement.get('statements'), condition, scope.basedir())
|
||||||
scope.add_child(child)
|
|
||||||
|
|
||||||
else_statements = statement.get('else_statements')
|
else_statements = statement.get('else_statements')
|
||||||
if else_statements:
|
if else_statements:
|
||||||
child = Scope.FromDict(file, else_statements, 'NOT ' + condition, scope.basedir())
|
child = Scope.FromDict(scope, file, else_statements, 'NOT ' + condition, scope.basedir())
|
||||||
scope.add_child(child)
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
loaded = statement.get('loaded', None)
|
loaded = statement.get('loaded')
|
||||||
if loaded:
|
if loaded:
|
||||||
scope.append_value('_LOADED', loaded)
|
scope._append_operation('_LOADED', UniqueAddOperation(loaded))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
option = statement.get('option', None)
|
option = statement.get('option', None)
|
||||||
if option:
|
if option:
|
||||||
scope.append_value('_OPTION', option)
|
scope._append_operation('_OPTION', UniqueAddOperation(option))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
included = statement.get('included', None)
|
included = statement.get('included', None)
|
||||||
if included:
|
if included:
|
||||||
scope.append_value('_INCLUDED',
|
scope.append_operation('_INCLUDED', UniqueAddOperation(map_to_file(included, scope.basedir(), scope.currentdir())))
|
||||||
map_to_file(included, scope.basedir(), scope.currentdir()))
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
return scope
|
return scope
|
||||||
|
|
||||||
|
def _append_operation(self, key: str, op: Operation) -> None:
|
||||||
|
if key in self._operations:
|
||||||
|
self._operations[key].append(op)
|
||||||
|
else:
|
||||||
|
self._operations[key] = [op,]
|
||||||
|
|
||||||
def file(self) -> str:
|
def file(self) -> str:
|
||||||
return self._file or ''
|
return self._file or ''
|
||||||
|
|
||||||
@ -228,38 +304,10 @@ class Scope:
|
|||||||
def condition(self) -> str:
|
def condition(self) -> str:
|
||||||
return self._condition
|
return self._condition
|
||||||
|
|
||||||
def _push_down_TEMPLATE(self, template: str) -> None:
|
def _add_child(self, scope: 'Scope') -> None:
|
||||||
self.set_value('TEMPLATE', [template, ])
|
|
||||||
for c in self._children:
|
|
||||||
c._push_down_TEMPLATE(template)
|
|
||||||
|
|
||||||
def add_child(self, scope: 'Scope') -> None:
|
|
||||||
scope._parent = self
|
scope._parent = self
|
||||||
if not scope._rawTemplate():
|
|
||||||
scope._push_down_TEMPLATE(self.getTemplate())
|
|
||||||
self._children.append(scope)
|
self._children.append(scope)
|
||||||
|
|
||||||
def set_value(self, key: str, value: List[str]) -> None:
|
|
||||||
self._values[key] = value
|
|
||||||
|
|
||||||
def append_value(self, key: str, value: Union[str, List[str]]) -> None:
|
|
||||||
array = self._values.get(key, [])
|
|
||||||
if isinstance(value, str):
|
|
||||||
array.append(value)
|
|
||||||
elif isinstance(value, list):
|
|
||||||
array += value
|
|
||||||
else:
|
|
||||||
assert False
|
|
||||||
self._values[key] = array
|
|
||||||
|
|
||||||
def substract_value(self, key: str, value: Union[str, List[str]]) -> None:
|
|
||||||
if isinstance(value, str):
|
|
||||||
to_remove = [value, ]
|
|
||||||
if isinstance(value, list):
|
|
||||||
to_remove = value
|
|
||||||
|
|
||||||
self.append_value(key, ['-{}'.format(v) for v in to_remove])
|
|
||||||
|
|
||||||
def children(self) -> List['Scope']:
|
def children(self) -> List['Scope']:
|
||||||
return self._children
|
return self._children
|
||||||
|
|
||||||
@ -270,28 +318,34 @@ class Scope:
|
|||||||
else:
|
else:
|
||||||
print('{}Scope {} in {} with condition: {}.'.format(ind, self._file, self._basedir, self._condition))
|
print('{}Scope {} in {} with condition: {}.'.format(ind, self._file, self._basedir, self._condition))
|
||||||
print('{}Keys:'.format(ind))
|
print('{}Keys:'.format(ind))
|
||||||
for k in sorted(self._values.keys()):
|
for k in sorted(self._operations.keys()):
|
||||||
print('{} {} = "{}"'.format(ind, k, self._values[k]))
|
print('{} {} = "{}"'.format(ind, k, self._operations.get(k, [])))
|
||||||
print('{}Children:'.format(ind))
|
print('{}Children:'.format(ind))
|
||||||
for c in self._children:
|
for c in self._children:
|
||||||
c.dump(indent=indent + 1)
|
c.dump(indent=indent + 1)
|
||||||
|
|
||||||
def get(self, key: str, default=None) -> List[str]:
|
def get(self, key: str, default=None) -> List[str]:
|
||||||
default = default or []
|
result = []
|
||||||
return self._values.get(key, default)
|
|
||||||
|
if self._parent:
|
||||||
|
result = self._parent.get(key, default)
|
||||||
|
else:
|
||||||
|
if default:
|
||||||
|
if isinstance(default, list):
|
||||||
|
result = default
|
||||||
|
else:
|
||||||
|
result = [str(default),]
|
||||||
|
|
||||||
|
for op in self._operations.get(key, []):
|
||||||
|
result = op.process(result)
|
||||||
|
return result
|
||||||
|
|
||||||
def getString(self, key: str, default: str = '') -> str:
|
def getString(self, key: str, default: str = '') -> str:
|
||||||
v = self.get(key)
|
v = self.get(key, default)
|
||||||
if isinstance(v, list):
|
|
||||||
if len(v) == 0:
|
if len(v) == 0:
|
||||||
return default
|
return default
|
||||||
assert len(v) == 1
|
assert len(v) == 1
|
||||||
return v[0]
|
return v[0]
|
||||||
elif isinstance(v, str):
|
|
||||||
return v
|
|
||||||
else:
|
|
||||||
assert False
|
|
||||||
return default
|
|
||||||
|
|
||||||
def getTemplate(self) -> str:
|
def getTemplate(self) -> str:
|
||||||
return self.getString('TEMPLATE', 'app')
|
return self.getString('TEMPLATE', 'app')
|
||||||
@ -425,7 +479,7 @@ def handle_subdir(scope: Scope, cm_fh: IO[str], *, indent: int = 0) -> None:
|
|||||||
cm_fh.write('{}add_subdirectory({})\n'.format(ind, sd))
|
cm_fh.write('{}add_subdirectory({})\n'.format(ind, sd))
|
||||||
elif os.path.isfile(full_sd):
|
elif os.path.isfile(full_sd):
|
||||||
subdir_result = parseProFile(full_sd, debug=False)
|
subdir_result = parseProFile(full_sd, debug=False)
|
||||||
subdir_scope = Scope.FromDict(full_sd, subdir_result.asDict().get('statements'),
|
subdir_scope = Scope.FromDict(scope, full_sd, subdir_result.asDict().get('statements'),
|
||||||
'', scope.basedir())
|
'', scope.basedir())
|
||||||
|
|
||||||
cmakeify_scope(subdir_scope, cm_fh, indent=indent + 1)
|
cmakeify_scope(subdir_scope, cm_fh, indent=indent + 1)
|
||||||
@ -490,8 +544,8 @@ def write_sources_section(cm_fh: IO[str], scope: Scope, *, indent: int=0,
|
|||||||
if plugin_type:
|
if plugin_type:
|
||||||
cm_fh.write('{} TYPE {}\n'.format(ind, plugin_type[0]))
|
cm_fh.write('{} TYPE {}\n'.format(ind, plugin_type[0]))
|
||||||
|
|
||||||
sources = scope.get('SOURCES') + scope.get('HEADERS') + scope.get('OBJECTIVE_SOURCES') + scope.get('NO_PCH_SOURCES') + scope.get('FORMS')
|
sources = scope.diff('SOURCES') + scope.diff('HEADERS') + scope.diff('OBJECTIVE_SOURCES') + scope.diff('NO_PCH_SOURCES') + scope.diff('FORMS')
|
||||||
resources = scope.get('RESOURCES')
|
resources = scope.diff('RESOURCES')
|
||||||
if resources:
|
if resources:
|
||||||
qrc_only = True
|
qrc_only = True
|
||||||
for r in resources:
|
for r in resources:
|
||||||
@ -512,19 +566,21 @@ def write_sources_section(cm_fh: IO[str], scope: Scope, *, indent: int=0,
|
|||||||
for l in sort_sources(sources):
|
for l in sort_sources(sources):
|
||||||
cm_fh.write('{} {}\n'.format(ind, l))
|
cm_fh.write('{} {}\n'.format(ind, l))
|
||||||
|
|
||||||
if scope.get('DEFINES'):
|
defines = scope.diff('DEFINES')
|
||||||
|
if defines:
|
||||||
cm_fh.write('{} DEFINES\n'.format(ind))
|
cm_fh.write('{} DEFINES\n'.format(ind))
|
||||||
for d in scope.get('DEFINES'):
|
for d in defines:
|
||||||
d = d.replace('=\\\\\\"$$PWD/\\\\\\"', '="${CMAKE_CURRENT_SOURCE_DIR}/"')
|
d = d.replace('=\\\\\\"$$PWD/\\\\\\"', '="${CMAKE_CURRENT_SOURCE_DIR}/"')
|
||||||
cm_fh.write('{} {}\n'.format(ind, d))
|
cm_fh.write('{} {}\n'.format(ind, d))
|
||||||
if scope.get('INCLUDEPATH'):
|
includes = scope.diff('INCLUDEPATH')
|
||||||
|
if includes:
|
||||||
cm_fh.write('{} INCLUDE_DIRECTORIES\n'.format(ind))
|
cm_fh.write('{} INCLUDE_DIRECTORIES\n'.format(ind))
|
||||||
for i in scope.get('INCLUDEPATH'):
|
for i in includes:
|
||||||
cm_fh.write('{} {}\n'.format(ind, i))
|
cm_fh.write('{} {}\n'.format(ind, i))
|
||||||
|
|
||||||
dependencies = [map_qt_library(q) for q in scope.get('QT') if map_qt_library(q) not in known_libraries]
|
dependencies = [map_qt_library(q) for q in scope.diff('QT') if map_qt_library(q) not in known_libraries]
|
||||||
dependencies += [map_qt_library(q) for q in scope.get('QT_FOR_PRIVATE') if map_qt_library(q) not in known_libraries]
|
dependencies += [map_qt_library(q) for q in scope.diff('QT_FOR_PRIVATE') if map_qt_library(q) not in known_libraries]
|
||||||
dependencies += scope.get('QMAKE_USE_PRIVATE') + scope.get('LIBS_PRIVATE') + scope.get('LIBS')
|
dependencies += scope.diff('QMAKE_USE_PRIVATE') + scope.diff('LIBS_PRIVATE') + scope.diff('LIBS')
|
||||||
if dependencies:
|
if dependencies:
|
||||||
cm_fh.write('{} LIBRARIES\n'.format(ind))
|
cm_fh.write('{} LIBRARIES\n'.format(ind))
|
||||||
is_framework = False
|
is_framework = False
|
||||||
@ -714,10 +770,8 @@ def do_include(scope: Scope, *, debug: bool=False) -> None:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
include_result = parseProFile(include_file, debug=debug)
|
include_result = parseProFile(include_file, debug=debug)
|
||||||
include_scope = Scope.FromDict(include_file, include_result.asDict().get('statements'),
|
include_scope = Scope.FromDict(scope, include_file, include_result.asDict().get('statements'),
|
||||||
'', dir)
|
'', dir)
|
||||||
if not include_scope._rawTemplate():
|
|
||||||
include_scope._push_down_TEMPLATE(scope.getTemplate())
|
|
||||||
|
|
||||||
do_include(include_scope)
|
do_include(include_scope)
|
||||||
|
|
||||||
@ -744,7 +798,7 @@ def main() -> None:
|
|||||||
print(parseresult.asDict())
|
print(parseresult.asDict())
|
||||||
print('\n#### End of parser result dictionary.\n')
|
print('\n#### End of parser result dictionary.\n')
|
||||||
|
|
||||||
file_scope = Scope.FromDict(file, parseresult.asDict().get('statements'))
|
file_scope = Scope.FromDict(None, file, parseresult.asDict().get('statements'))
|
||||||
|
|
||||||
if args.debug_pro_structure or args.debug:
|
if args.debug_pro_structure or args.debug:
|
||||||
print('\n\n#### .pro/.pri file structure:')
|
print('\n\n#### .pro/.pri file structure:')
|
||||||
|
Loading…
Reference in New Issue
Block a user