summaryrefslogtreecommitdiffstats
path: root/util
diff options
context:
space:
mode:
authorAlexandru Croitor <alexandru.croitor@qt.io>2020-07-27 11:10:48 +0200
committerAlexandru Croitor <alexandru.croitor@qt.io>2020-07-31 12:55:32 +0200
commitd65283ebd01c221d0f03a2873c19dc3723de536c (patch)
tree06ddf325fc60ae0322227155f51be2c0d75452e0 /util
parentb3b1e4737808bc2f2ccaef88d133404877ebce58 (diff)
CMake: Teach pro2cmake to convert load(qt_app)
Changes pro2cmake to handle load(qt_app) projects and write out qt_internal_add_app calls. Also adds handling of macOS and Windows specific resource files for Qt apps only. Task-number: QTBUG-85757 Change-Id: I994d8d19ab2ae366a985cab7894b97d6a278a56f Reviewed-by: Cristian Adam <cristian.adam@qt.io>
Diffstat (limited to 'util')
-rwxr-xr-xutil/cmake/pro2cmake.py246
1 files changed, 233 insertions, 13 deletions
diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py
index 823be0d07b..37f0b845f5 100755
--- a/util/cmake/pro2cmake.py
+++ b/util/cmake/pro2cmake.py
@@ -325,6 +325,7 @@ def set_up_cmake_api_calls():
api[1]["qt_add_module"] = "add_qt_module"
api[1]["qt_add_plugin"] = "add_qt_plugin"
api[1]["qt_add_tool"] = "add_qt_tool"
+ api[2]["qt_internal_add_app"] = "qt_internal_add_app"
api[1]["qt_add_test"] = "add_qt_test"
api[1]["qt_add_test_helper"] = "add_qt_test_helper"
api[1]["qt_add_manual_test"] = "add_qt_manual_test"
@@ -341,6 +342,7 @@ def set_up_cmake_api_calls():
api[2]["qt_add_module"] = "qt_add_module"
api[2]["qt_add_plugin"] = "qt_internal_add_plugin"
api[2]["qt_add_tool"] = "qt_add_tool"
+ api[2]["qt_internal_add_app"] = "qt_internal_add_app"
api[2]["qt_add_test"] = "qt_add_test"
api[2]["qt_add_test_helper"] = "qt_add_test_helper"
api[2]["qt_add_manual_test"] = "qt_add_manual_test"
@@ -956,6 +958,7 @@ class Scope(object):
self._parent_include_line_no = parent_include_line_no
self._is_public_module = False
self._has_private_module = False
+ self._is_internal_qt_app = False
def __repr__(self):
return (
@@ -995,6 +998,16 @@ class Scope(object):
def has_private_module(self) -> bool:
return self._has_private_module
+ @property
+ def is_internal_qt_app(self) -> bool:
+ is_app = self._is_internal_qt_app
+ current_scope = self
+ while not is_app and current_scope.parent:
+ current_scope = current_scope.parent
+ is_app = current_scope.is_internal_qt_app
+
+ return is_app
+
def can_merge_condition(self):
if self._condition == "else":
return False
@@ -2086,7 +2099,7 @@ def write_3rd_party_include_paths(
for i in includes:
# CMake generator expressions don't seem to like relative paths.
# Make them absolute relative to the source dir.
- if not os.path.isabs(i) and not i.startswith("$"):
+ if is_path_relative_ish(i):
i = f"${{CMAKE_CURRENT_SOURCE_DIR}}/{i}"
i = f"$<BUILD_INTERFACE:{i}>"
processed_includes.append(i)
@@ -2465,9 +2478,59 @@ def write_repc_files(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0)
cm_fh.write(")\n")
-def write_generic_cmake_command(cm_fh: IO[str], command_name: str, arguments: List[str]):
+def write_generic_cmake_command(cm_fh: IO[str], command_name: str, arguments: List[str],
+ indent: int = 0):
+ ind = spaces(indent)
arguments_str = " ".join(arguments)
- cm_fh.write(f"{command_name}({arguments_str})\n")
+ cm_fh.write(f"{ind}{command_name}({arguments_str})\n")
+
+
+def write_set_target_properties(cm_fh: IO[str],
+ targets: List[str],
+ properties: List[str],
+ indent: int = 0):
+ ind = spaces(indent)
+ command_name = 'set_target_properties'
+ arguments_ind = spaces(indent + 1)
+
+ prop_pairs = [(properties[i] + ' ' + properties[i + 1]) for i in range(0, len(properties), 2)]
+ properties_str = f"\n{arguments_ind}" + f"\n{arguments_ind}".join(prop_pairs)
+
+ if len(targets) == 1:
+ targets_str = targets[0] + ' '
+ else:
+ targets_str = f"\n{arguments_ind}" + f"\n{arguments_ind}".join(targets) + f"\n{arguments_ind}"
+
+ cm_fh.write(f"{ind}{command_name}({targets_str}PROPERTIES{properties_str}\n{ind})\n")
+
+
+def write_set_source_files_properties(cm_fh: IO[str],
+ files: List[str],
+ properties: List[str],
+ indent: int = 0):
+ ind = spaces(indent)
+ command_name = 'set_source_files_properties'
+ arguments_ind = spaces(indent + 1)
+
+ prop_pairs = [(properties[i] + ' ' + properties[i + 1]) for i in range(0, len(properties), 2)]
+ properties_str = f"\n{arguments_ind}" + f"\n{arguments_ind}".join(prop_pairs)
+
+ if len(files) == 1:
+ targets_str = files[0] + ' '
+ else:
+ targets_str = f"\n{arguments_ind}" + f"\n{arguments_ind}".join(files) + f"\n{arguments_ind}"
+
+ cm_fh.write(f"{ind}{command_name}({targets_str}PROPERTIES{properties_str}\n{ind})\n")
+
+
+def write_target_sources(cm_fh: IO[str],
+ target: str,
+ sources: List[str],
+ visibility: str = 'PRIVATE',
+ indent: int = 0):
+ command_name = 'target_sources'
+ header = f'{command_name}({target} {visibility}\n'
+ write_list(cm_fh, sources, '', indent, footer=f')', header=header)
def expand_project_requirements(scope: Scope, skip_message: bool = False) -> str:
@@ -2665,13 +2728,7 @@ def write_wayland_part(cm_fh: IO[str], target: str, scope: Scope, indent: int =
if len(client_sources) == 0 and len(server_sources) == 0:
return
- condition = "ON"
- if scope.total_condition:
- condition = map_to_cmake_condition(scope.total_condition)
-
- if condition != "ON":
- cm_fh.write(f"\n{spaces(indent)}if({condition})\n")
- indent += 1
+ condition, indent = write_scope_condition_begin(cm_fh, scope, indent=indent)
if len(client_sources) != 0:
cm_fh.write(f"\n{spaces(indent)}qt6_generate_wayland_protocol_client_sources({target}\n")
@@ -2687,9 +2744,133 @@ def write_wayland_part(cm_fh: IO[str], target: str, scope: Scope, indent: int =
)
cm_fh.write(f"{spaces(indent)})\n")
+ write_scope_condition_end(cm_fh, condition, indent=indent)
+
+
+def write_scope_condition_begin(cm_fh: IO[str], scope: Scope, indent: int = 0) -> Tuple[str, int]:
+ condition = "ON"
+ if scope.total_condition:
+ condition = map_to_cmake_condition(scope.total_condition)
+
+ if condition != "ON":
+ cm_fh.write(f"\n{spaces(indent)}if({condition})\n")
+ indent += 1
+
+ return condition, indent
+
+
+def write_scope_condition_end(cm_fh: IO[str], condition: str, indent: int = 0) -> int:
if condition != "ON":
indent -= 1
- cm_fh.write(f"\n{spaces(indent)}endif()\n")
+ cm_fh.write(f"{spaces(indent)}endif()\n")
+ return indent
+
+
+def is_path_relative_ish(path: str) -> bool:
+ if not os.path.isabs(path) and not path.startswith("$"):
+ return True
+ return False
+
+
+def absolutify_path(path: str, base_dir: str = '${CMAKE_CURRENT_SOURCE_DIR}') -> str:
+ if not path:
+ return path
+ if is_path_relative_ish(path):
+ path = posixpath.join(base_dir, path)
+ return path
+
+
+def write_version_part(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0):
+ if scope.is_internal_qt_app:
+ version_value = scope.get_string("VERSION")
+ if version_value:
+ version_value = re.sub(r"\$\${QT_VERSION\}", "${PROJECT_VERSION}", version_value)
+ target_description = scope.expandString("QMAKE_TARGET_DESCRIPTION")
+
+ if version_value or target_description:
+ condition, indent = write_scope_condition_begin(cm_fh, scope, indent=indent)
+
+ properties = []
+ if version_value:
+ properties.extend(['QT_TARGET_VERSION', f'"{version_value}"'])
+ if target_description:
+ properties.extend(['QT_TARGET_DESCRIPTION', f'"{target_description}"'])
+
+ if properties:
+ write_set_target_properties(cm_fh, [target], properties, indent=indent)
+
+ write_scope_condition_end(cm_fh, condition, indent=indent)
+
+
+def write_darwin_part(cm_fh: IO[str], target: str, scope: Scope, main_scope_target_name: str = '',
+ indent: int = 0):
+ if scope.is_internal_qt_app:
+ # Embed custom provided Info.plist file.
+ info_plist = scope.expandString("QMAKE_INFO_PLIST")
+ info_plist = absolutify_path(info_plist)
+
+ icon_path = scope.expandString("ICON")
+ icon_basename = ''
+
+ new_output_name = None
+ current_scope_output_name = scope.TARGET
+ if current_scope_output_name != main_scope_target_name:
+ new_output_name = current_scope_output_name
+
+ if icon_path:
+ icon_basename = os.path.basename(icon_path)
+
+ if info_plist or icon_path or new_output_name:
+ condition, indent = write_scope_condition_begin(cm_fh, scope, indent=indent)
+
+ properties = []
+ if info_plist:
+ properties.extend(['MACOSX_BUNDLE_INFO_PLIST', f'"{info_plist}"'])
+ properties.extend(['MACOSX_BUNDLE', 'TRUE'])
+ if icon_path:
+ properties.extend(['MACOSX_BUNDLE_ICON_FILE', f'"{icon_basename}"'])
+
+ if new_output_name:
+ properties.extend(['OUTPUT_NAME', f'"{new_output_name}"'])
+
+ if properties:
+ write_set_target_properties(cm_fh, [target], properties, indent=indent)
+ if icon_path:
+ source_properties = ['MACOSX_PACKAGE_LOCATION', 'Resources']
+ write_set_source_files_properties(cm_fh, [icon_path], source_properties,
+ indent=indent)
+ write_target_sources(cm_fh, target, [icon_path], indent=indent)
+
+ write_scope_condition_end(cm_fh, condition, indent=indent)
+
+
+def write_windows_part(cm_fh: IO[str], target: str, scope: Scope, indent: int = 0):
+ if scope.is_internal_qt_app:
+ # Handle CONFIG += console assignments.
+ is_console = "console" in scope.get("CONFIG")
+ rc_file = scope.expandString("RC_FILE")
+ rc_file = absolutify_path(rc_file)
+ rc_icons = scope.expandString("RC_ICONS")
+ rc_icons = absolutify_path(rc_icons)
+
+ if is_console or rc_file or rc_icons:
+ condition, indent = write_scope_condition_begin(cm_fh, scope, indent=indent)
+
+ properties = []
+ if is_console:
+ properties.extend(['WIN32_EXECUTABLE', 'FALSE'])
+
+ if rc_file:
+ properties.extend(['QT_TARGET_WINDOWS_RC_FILE', f'"{rc_file}"'])
+
+ if rc_icons:
+ properties.extend(['QT_TARGET_RC_ICONS', f'"{rc_icons}"'])
+
+ if properties:
+ write_set_target_properties(cm_fh, [target],
+ properties, indent=indent)
+
+ write_scope_condition_end(cm_fh, condition, indent=indent)
def handle_source_subtractions(scopes: List[Scope]):
@@ -2945,6 +3126,12 @@ def write_main_part(
write_wayland_part(cm_fh, name, scopes[0], indent)
+ write_windows_part(cm_fh, name, scopes[0], indent)
+
+ write_darwin_part(cm_fh, name, scopes[0], main_scope_target_name=name, indent=indent)
+
+ write_version_part(cm_fh, name, scopes[0], indent)
+
if "warn_off" in scope.get("CONFIG"):
write_generic_cmake_command(cm_fh, "qt_disable_warnings", [name])
@@ -2965,6 +3152,9 @@ def write_main_part(
c.reset_visited_keys()
write_android_part(cm_fh, name, c, indent=indent)
write_wayland_part(cm_fh, name, c, indent=indent)
+ write_windows_part(cm_fh, name, c, indent=indent)
+ write_darwin_part(cm_fh, name, c, main_scope_target_name=name, indent=indent)
+ write_version_part(cm_fh, name, c, indent=indent)
write_extend_target(cm_fh, name, c, target_ref=target_ref, indent=indent)
write_simd_part(cm_fh, name, c, indent=indent)
@@ -3065,12 +3255,13 @@ def write_generic_library(cm_fh: IO[str], scope: Scope, *, indent: int = 0) -> s
return target_name
-def forward_target_info(scope: Scope, extra: [str]):
+
+def forward_target_info(scope: Scope, extra: [str], skip: Optional[Dict[str]] = None):
s = scope.get_string("QMAKE_TARGET_PRODUCT")
if s:
extra.append(f"TARGET_PRODUCT \"{s}\"")
s = scope.get_string("QMAKE_TARGET_DESCRIPTION")
- if s:
+ if s and (not skip or 'QMAKE_TARGET_DESCRIPTION' not in skip):
extra.append(f"TARGET_DESCRIPTION \"{s}\"")
s = scope.get_string("QMAKE_TARGET_COMPANY")
if s:
@@ -3079,6 +3270,7 @@ def forward_target_info(scope: Scope, extra: [str]):
if s:
extra.append(f"TARGET_COPYRIGHT \"{s}\"")
+
def write_module(cm_fh: IO[str], scope: Scope, *, indent: int = 0) -> str:
module_name = scope.TARGET
if not module_name.startswith("Qt"):
@@ -3171,6 +3363,30 @@ def write_tool(cm_fh: IO[str], scope: Scope, *, indent: int = 0) -> str:
return tool_name, "${target_name}"
+def write_qt_app(cm_fh: IO[str], scope: Scope, *, indent: int = 0) -> str:
+ app_name = scope.TARGET
+
+ extra = []
+
+ target_info_skip = {}
+ target_info_skip['QMAKE_TARGET_DESCRIPTION'] = True
+ forward_target_info(scope, extra, target_info_skip)
+
+ write_main_part(
+ cm_fh,
+ app_name,
+ "App",
+ get_cmake_api_call("qt_internal_add_app"),
+ scope,
+ indent=indent,
+ known_libraries={"Qt::Core"},
+ extra_lines=extra,
+ extra_keys=["CONFIG"],
+ )
+
+ return app_name
+
+
def write_test(cm_fh: IO[str], scope: Scope, gui: bool = False, *, indent: int = 0) -> str:
test_name = scope.TARGET
assert test_name
@@ -3809,6 +4025,10 @@ def handle_app_or_lib(
elif "qt_tool" in scope.get("_LOADED"):
assert not is_example
target, target_ref = write_tool(cm_fh, scope, indent=indent)
+ elif "qt_app" in scope.get("_LOADED"):
+ assert not is_example
+ scope._is_internal_qt_app = True
+ target = write_qt_app(cm_fh, scope, indent=indent)
else:
if "testcase" in config or "testlib" in config or "qmltestcase" in config:
assert not is_example