diff options
author | Alexandru Croitor <alexandru.croitor@qt.io> | 2019-09-30 17:02:08 +0200 |
---|---|---|
committer | Tobias Hunger <tobias.hunger@qt.io> | 2019-10-01 14:16:14 +0000 |
commit | 1b0710a39380c7f75bba2d777e137f247fcbd5b7 (patch) | |
tree | ef4656e8275117366679f17cf756190274a7c928 | |
parent | f23b7e1476ded2be43d5e3f03195f522d307b163 (diff) |
pro2cmake: Handle SOURCES subtractions
SOURCES -= foo.cpp statements should now be handled correctly by the
script. The script uses the same principle as for subdir handling:
go through all scopes to collect source removals, and then generate
new scopes with total conditions that take into account both
additions and subtractions and their conditions.
Should handle NO_PCH_SOURCES case as well.
Tested on gui.pro, network.pro and qtconnectivity bluetooth.pro.
Change-Id: I0cc913ef64cecf09e8ada4e6a3ce3ccb4365b44e
Reviewed-by: Tobias Hunger <tobias.hunger@qt.io>
-rwxr-xr-x | util/cmake/pro2cmake.py | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index 96d9712bb2..b10df35ffb 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -68,6 +68,7 @@ from typing import ( FrozenSet, Tuple, Match, + Type, ) from special_case_helper import SpecialCaseHandler from helper import ( @@ -1886,6 +1887,12 @@ def write_source_file_list( for key in keys: sources += scope.get_files(key, use_vpath=True) + # Remove duplicates, like in the case when NO_PCH_SOURCES ends up + # adding the file to SOURCES, but SOURCES might have already + # contained it before. Preserves order in Python 3.7+ because + # dict keys are ordered. + sources = list(dict.fromkeys(sources)) + write_list(cm_fh, sources, cmake_parameter, indent, header=header, footer=footer) @@ -2398,6 +2405,144 @@ def write_wayland_part(cm_fh: typing.IO[str], target: str, scope:Scope, indent: cm_fh.write(f"\n{spaces(indent)}endif()\n") +def handle_source_subtractions(scopes: List[Scope]): + """ + Handles source subtractions like SOURCES -= painting/qdrawhelper.cpp + by creating a new scope with a new condition containing all addition + and subtraction conditions. + + Algorithm is as follows: + - Go through each scope and find files in SOURCES starting with "-" + - Save that file and the scope condition in modified_sources dict. + - Remove the file from the found scope (optionally remove the + NO_PCH_SOURCES entry for that file as well). + - Go through each file in modified_sources dict. + - Find scopes where the file is added, remove the file from that + scope and save the condition. + - Create a new scope just for that file with a new simplified + condition that takes all the other conditions into account. + """ + + def remove_file_from_operation( + scope: Scope, ops_key: str, file: str, op_type: Type[Operation] + ) -> bool: + """ + Remove a source file from an operation in a scope. + Example: remove foo.cpp from any operations that have + ops_key="SOURCES" in "scope", where the operation is of + type "op_type". + + The implementation is very rudimentary and might not work in + all cases. + + Returns True if a file was found and removed in any operation. + """ + file_removed = False + ops = scope._operations.get(ops_key, list()) + for op in ops: + if not isinstance(op, op_type): + continue + if file in op._value: + op._value.remove(file) + file_removed = True + for include_child_scope in scope._included_children: + file_removed = file_removed or remove_file_from_operation( + include_child_scope, ops_key, file, op_type + ) + return file_removed + + def join_all_conditions(set_of_alternatives: Set[str]): + final_str = "" + if set_of_alternatives: + alternatives = [f"({alternative})" for alternative in set_of_alternatives] + final_str = " OR ".join(sorted(alternatives)) + return final_str + + modified_sources: Dict[str, Dict[str, Union[Set[str], bool]]] = {} + + new_scopes = [] + top_most_scope = scopes[0] + + for scope in scopes: + sources = scope.get_files("SOURCES") + for file in sources: + # Find subtractions. + if file.startswith("-"): + file_without_minus = file[1:] + + if file_without_minus not in modified_sources: + modified_sources[file_without_minus] = {} + + subtractions = modified_sources[file_without_minus].get("subtractions", set()) + + # Add the condition to the set of conditions and remove + # the file subtraction from the processed scope, which + # will be later re-added in a new scope. + if scope.condition: + subtractions.add(scope.total_condition) + remove_file_from_operation(scope, "SOURCES", file_without_minus, RemoveOperation) + if subtractions: + modified_sources[file_without_minus]["subtractions"] = subtractions + + # In case if the source is also listed in a + # NO_PCH_SOURCES operation, remove it from there as + # well, and add it back later. + no_pch_source_removed = remove_file_from_operation( + scope, "NO_PCH_SOURCES", file_without_minus, AddOperation + ) + if no_pch_source_removed: + modified_sources[file_without_minus]["add_to_no_pch_sources"] = True + + for modified_source in modified_sources: + additions = modified_sources[modified_source].get("additions", set()) + subtractions = modified_sources[modified_source].get("subtractions", set()) + add_to_no_pch_sources = modified_sources[modified_source].get( + "add_to_no_pch_sources", False + ) + + for scope in scopes: + sources = scope.get_files("SOURCES") + if modified_source in sources: + # Remove the source file from any addition operations + # that mention it. + remove_file_from_operation(scope, "SOURCES", modified_source, AddOperation) + if scope.total_condition: + additions.add(scope.total_condition) + + # Construct a condition that takes into account all addition + # and subtraction conditions. + addition_str = join_all_conditions(additions) + if addition_str: + addition_str = f"({addition_str})" + subtraction_str = join_all_conditions(subtractions) + if subtraction_str: + subtraction_str = f"NOT ({subtraction_str})" + + condition_str = addition_str + if condition_str and subtraction_str: + condition_str += " AND " + condition_str += subtraction_str + condition_simplified = simplify_condition(condition_str) + + # Create a new scope with that condition and add the source + # operations. + new_scope = Scope( + parent_scope=top_most_scope, + file=top_most_scope.file, + condition=condition_simplified, + base_dir=top_most_scope.basedir, + ) + new_scope.total_condition = condition_simplified + new_scope._append_operation("SOURCES", AddOperation([modified_source])) + if add_to_no_pch_sources: + new_scope._append_operation("NO_PCH_SOURCES", AddOperation([modified_source])) + + new_scopes.append(new_scope) + + # Add all the newly created scopes. + scopes += new_scopes + + def write_main_part( cm_fh: IO[str], name: str, @@ -2424,6 +2569,12 @@ def write_main_part( # Merge scopes based on their conditions: scopes = merge_scopes(scopes) + # Handle SOURCES -= foo calls, and merge scopes one more time + # because there might have been several files removed with the same + # scope condition. + handle_source_subtractions(scopes) + scopes = merge_scopes(scopes) + assert len(scopes) assert scopes[0].total_condition == "ON" |