Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for a wayland variant of Qt for Linux #266

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions package-system/Qt/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
# This docker file uses ubuntu 20.04 as the base image to install the dependencies to build Qt from source
#

FROM public.ecr.aws/ubuntu/ubuntu:20.04_stable
FROM amd64/ubuntu:20.04

WORKDIR /data/workspace

# Initilize apt cache
RUN apt-get clean && apt-get update
RUN apt-get clean && apt-get update && apt-get upgrade

# Setup time zone and locale data (necessary for SSL and HTTPS packages)
RUN DEBIAN_FRONTEND="noninteractive" apt-get -y install tzdata locales keyboard-configuration
Expand All @@ -23,6 +23,7 @@ RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && \
update-locale LANG=en_US.UTF-8

ENV LANG=en_US.UTF-8
ENV ENABLE_QT_WAYLAND=0

# Install the development packages needed to build Qt from source
RUN apt-get install -y qtbase5-dev \
Expand All @@ -40,10 +41,19 @@ RUN apt-get install -y qtbase5-dev \
libgbm-dev \
libxext-dev \
libfontconfig1-dev \
libtiff-dev
libtiff-dev \
libwayland-dev \
libwayland-egl1-mesa \
libwayland-server0 \
libgles2-mesa-dev \
libdrm-dev

# Prepare a target folder within the container to install the build artifacts tp
RUN mkdir -p /data/workspace/qt

RUN git clone --single-branch --recursive --branch v5.15.1 git://code.qt.io/qt/qtwayland.git && \
ln -s /data/workspace/qtwayland/include/QtWaylandCompositor/5.15.1/QtWaylandCompositor/private/qwayland-server-qt-texture-sharing-unstable-v1.h /data/workspace/qtwayland/src/compositor/qwayland-server-qt-texture-sharing-unstable-v1.h && \
ln -s /data/workspace/qtwayland/include/QtWaylandCompositor/5.15.1/QtWaylandCompositor/private/wayland-qt-texture-sharing-unstable-v1-server-protocol.h /data/workspace/qtwayland/src/compositor/wayland-qt-texture-sharing-unstable-v1-server-protocol.h

# Copy the build script specific to this Docker script in order to execute the build
COPY docker_build_qt_linux.sh /data/workspace/
58 changes: 58 additions & 0 deletions package-system/Qt/Dockerfile.wayland
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@

# Copyright (c) Contributors to the Open 3D Engine Project.
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#

# This docker file uses ubuntu 20.04 as the base image to install the dependencies to build Qt from source
#

FROM public.ecr.aws/ubuntu/ubuntu:20.04_stable

WORKDIR /data/workspace

# Initilize apt cache
RUN apt-get clean && apt-get update

# Setup time zone and locale data (necessary for SSL and HTTPS packages)
RUN DEBIAN_FRONTEND="noninteractive" apt-get -y install tzdata locales keyboard-configuration

RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && \
dpkg-reconfigure --frontend=noninteractive locales && \
update-locale LANG=en_US.UTF-8

ENV LANG=en_US.UTF-8
ENV ENABLE_QT_WAYLAND=1

# Install the development packages needed to build Qt from source
RUN apt-get install -y qtbase5-dev \
build-essential \
perl \
python3 \
git \
'^libxcb.*-dev' \
libx11-xcb-dev \
libglu1-mesa-dev \
libxrender-dev \
libxi-dev \
libxkbcommon-dev \
libxkbcommon-x11-dev \
libgbm-dev \
libxext-dev \
libfontconfig1-dev \
libtiff-dev \
libwayland-dev \
libwayland-egl1-mesa \
libwayland-server0 \
libgles2-mesa-dev

# Prepare a target folder within the container to install the build artifacts tp
RUN mkdir -p /data/workspace/qt

RUN git clone --single-branch --recursive --branch v5.15.1 git://code.qt.io/qt/qtwayland.git && \
ln -s /data/workspace/qtwayland/include/QtWaylandCompositor/5.15.1/QtWaylandCompositor/private/qwayland-server-qt-texture-sharing-unstable-v1.h /data/workspace/qtwayland/src/compositor/qwayland-server-qt-texture-sharing-unstable-v1.h && \
ln -s /data/workspace/qtwayland/include/QtWaylandCompositor/5.15.1/QtWaylandCompositor/private/wayland-qt-texture-sharing-unstable-v1-server-protocol.h /data/workspace/qtwayland/src/compositor/wayland-qt-texture-sharing-unstable-v1-server-protocol.h

# Copy the build script specific to this Docker script in order to execute the build
COPY docker_build_qt_linux.sh /data/workspace/
288 changes: 288 additions & 0 deletions package-system/Qt/FindQt.cmake.wayland
Original file line number Diff line number Diff line change
@@ -0,0 +1,288 @@
#
# Copyright (c) Contributors to the Open 3D Engine Project.
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
#

if(TARGET 3rdParty::Qt::Core) # Check we are not called multiple times
return()
endif()

set(QT_PACKAGE_NAME qt)

set(QT_PATH "${CMAKE_CURRENT_LIST_DIR}/qt-wayland" CACHE STRING "The root path to Qt" FORCE)
mark_as_advanced(QT_PATH)
if(NOT EXISTS ${QT_PATH})
message(FATAL_ERROR "Cannot find 3rdParty library ${QT_PACKAGE_NAME} on path ${QT_PATH}")
endif()

# Force-set QtCore's version here to ensure CMake detects Qt's existence and allows AUTOMOC to work
set(Qt5Core_VERSION_MAJOR "5" CACHE STRING "Qt's major version" FORCE)
set(Qt5Core_VERSION_MINOR "15" CACHE STRING "Qt's minor version" FORCE)
set(Qt5Core_VERSION_PATCH "2" CACHE STRING "Qt's patch version" FORCE)
mark_as_advanced(Qt5Core_VERSION_MAJOR)
mark_as_advanced(Qt5Core_VERSION_MINOR)
mark_as_advanced(Qt5Core_VERSION_PATCH)

set(QT5_COMPONENTS
Core
Concurrent
Gui
LinguistTools
Network
OpenGL
Svg
Test
Widgets
Xml
)

include(${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME}/Qt_${PAL_PLATFORM_NAME_LOWERCASE}.cmake)

list(APPEND CMAKE_PREFIX_PATH ${QT_LIB_PATH}/cmake/Qt5)

# Clear the cache for found DIRs
unset(Qt5_DIR CACHE)
foreach(component ${QT5_COMPONENTS})
unset(Qt5${component}_DIR CACHE)
endforeach()
unset(Qt5Positioning_DIR CACHE)
unset(Qt5PrintSupport_DIR CACHE)
unset(Qt5Qml_DIR CACHE)
unset(Qt5QmlModels_DIR CACHE)
unset(Qt5Quick_DIR CACHE)

# Populate the Qt5 configurations
find_package(Qt5
COMPONENTS ${QT5_COMPONENTS}
REQUIRED
NO_CMAKE_PACKAGE_REGISTRY
)

# Now create libraries that wrap the dependency so we can refer to them in our format
foreach(component ${QT5_COMPONENTS})
if(TARGET Qt5::${component})

# Convert the includes to system includes
get_target_property(system_includes Qt5::${component} INTERFACE_INCLUDE_DIRECTORIES)
set_target_properties(Qt5::${component} PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "") # Clear it in case someone refers to it
ly_target_include_system_directories(TARGET Qt5::${component}
INTERFACE ${system_includes}
)

# Alias the target with our prefix
add_library(3rdParty::Qt::${component} ALIAS Qt5::${component})
mark_as_advanced(Qt5${component}_DIR) # Hiding from GUI

# Qt only has debug and release, we map the configurations we use in o3de. We map all the configurations
# except debug to release
foreach(conf IN LISTS CMAKE_CONFIGURATION_TYPES)
string(TOUPPER ${conf} UCONF)
ly_qt_configuration_mapping(${UCONF} MAPPED_CONF)
set_target_properties(Qt5::${component} PROPERTIES
MAP_IMPORTED_CONFIG_${UCONF} ${MAPPED_CONF}
)
endforeach()

endif()
endforeach()

# Some extra DIR variables we want to hide from GUI
mark_as_advanced(Qt5_DIR) # Hiding from GUI
mark_as_advanced(Qt5LinguistTools_DIR) # Hiding from GUI, this variable comes from the LinguistTools module
mark_as_advanced(Qt5Positioning_DIR)
mark_as_advanced(Qt5PrintSupport_DIR)
mark_as_advanced(Qt5Qml_DIR)
mark_as_advanced(Qt5QmlModels_DIR)
mark_as_advanced(Qt5Quick_DIR)

# Special case for Qt::Gui, we are using the private headers...
ly_target_include_system_directories(TARGET Qt5::Gui
INTERFACE "${Qt5Gui_PRIVATE_INCLUDE_DIRS}"
)

# Another special case: Qt:Widgets, we are also using private headers
ly_target_include_system_directories(TARGET Qt5::Widgets
INTERFACE "${Qt5Widgets_PRIVATE_INCLUDE_DIRS}"
)

# Qt plugins/translations/aux files.
# We create libraries that wraps them so they get deployed properly.
# This used to be deployed through winqtdeploy/macqtdeploy, however, those tools
# are old and unmaintaned, macqtdeploy takes long times to run
add_library(3rdParty::Qt::Core::Translations INTERFACE IMPORTED GLOBAL)
file(GLOB tranlation_files ${QT_PATH}/translations/qt_*.qm)
if(tranlation_files)
ly_add_target_files(TARGETS 3rdParty::Qt::Core::Translations
FILES ${tranlation_files}
OUTPUT_SUBDIRECTORY translations
)
endif()
ly_add_dependencies(Qt5::Core 3rdParty::Qt::Core::Translations)

# plugins, each platform will define the files it has and the OUTPUT_SUBDIRECTORY
set(QT_PLUGINS
Network
Gui
Widgets
)
foreach(plugin ${QT_PLUGINS})
add_library(3rdParty::Qt::${plugin}::Plugins INTERFACE IMPORTED GLOBAL)
ly_add_dependencies(Qt5::${plugin} 3rdParty::Qt::${plugin}::Plugins)
endforeach()
include(${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME}/QtPlugin_${PAL_PLATFORM_NAME_LOWERCASE}.cmake)

# UIC executable
unset(QT_UIC_EXECUTABLE CACHE)
find_program(QT_UIC_EXECUTABLE uic HINTS "${QT_PATH}/bin")
mark_as_advanced(QT_UIC_EXECUTABLE) # Hiding from GUI

# RCC executable
unset(AUTORCC_EXECUTABLE CACHE)
find_program(AUTORCC_EXECUTABLE rcc HINTS "${QT_PATH}/bin")
mark_as_advanced(AUTORCC_EXECUTABLE) # Hiding from GUI
set(Qt5Core_RCC_EXECUTABLE "${AUTORCC_EXECUTABLE}" CACHE FILEPATH "Qt's resource compiler, used by qt5_add_resources" FORCE)
mark_as_advanced(Qt5Core_RCC_EXECUTABLE) # Hiding from GUI

# LRELEASE executable
unset(QT_LRELEASE_EXECUTABLE CACHE)
find_program(QT_LRELEASE_EXECUTABLE lrelease HINTS "${QT_PATH}/bin")
mark_as_advanced(QT_LRELEASE_EXECUTABLE) # Hiding from GUI
if(NOT QT_LRELEASE_EXECUTABLE)
message(FATAL_ERROR "Qt's lrelease executbale not found")
endif()
set(Qt5_LRELEASE_EXECUTABLE "${QT_LRELEASE_EXECUTABLE}" CACHE FILEPATH "Qt's lrelease executable, used by qt5_add_translation" FORCE)
mark_as_advanced(Qt5_LRELEASE_EXECUTABLE) # Hiding from GUI

#! ly_qt_uic_target: handles qt's ui files by injecting uic generation
#
# AUTOUIC has issues to detect changes in UIC files and trigger regeneration:
# https://gitlab.kitware.com/cmake/cmake/-/issues/18741
# So instead, we are going to manually wrap the files. We dont use qt5_wrap_ui because
# it outputs to ${CMAKE_CURRENT_BINARY_DIR}/ui_${outfile}.h and we want to follow the
# same folder structure that AUTOUIC uses
#
function(ly_qt_uic_target TARGET)

get_target_property(all_ui_sources ${TARGET} SOURCES)
list(FILTER all_ui_sources INCLUDE REGEX "^.*\\.ui$")
if(NOT all_ui_sources)
message(FATAL_ERROR "Target ${TARGET} contains AUTOUIC but doesnt have any .ui file")
endif()

if(AUTOGEN_BUILD_DIR)
set(gen_dir ${AUTOGEN_BUILD_DIR})
else()
set(gen_dir ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_autogen/include)
endif()

foreach(ui_source ${all_ui_sources})

get_filename_component(filename ${ui_source} NAME_WE)
get_filename_component(dir ${ui_source} DIRECTORY)
if(IS_ABSOLUTE ${dir})
file(RELATIVE_PATH dir ${CMAKE_CURRENT_SOURCE_DIR} ${dir})
endif()

set(outfolder ${gen_dir}/${dir})
set(outfile ${outfolder}/ui_${filename}.h)
get_filename_component(infile ${ui_source} ABSOLUTE)

file(MAKE_DIRECTORY ${outfolder})
add_custom_command(OUTPUT ${outfile}
COMMAND ${QT_UIC_EXECUTABLE} -o ${outfile} ${infile}
MAIN_DEPENDENCY ${infile} VERBATIM
COMMENT "UIC ${infile}"
)

set_source_files_properties(${infile} PROPERTIES SKIP_AUTOUIC TRUE)
set_source_files_properties(${outfile} PROPERTIES
SKIP_AUTOMOC TRUE
SKIP_AUTOUIC TRUE
GENERATED TRUE
)
list(APPEND all_ui_wrapped_sources ${outfile})

endforeach()

# Add files to the target
target_sources(${TARGET} PRIVATE ${all_ui_wrapped_sources})
source_group("Generated Files" FILES ${all_ui_wrapped_sources})

# Add include directories relative to the generated folder
# query for the property first to avoid the "NOTFOUND" in a list
get_property(has_includes TARGET ${TARGET} PROPERTY INCLUDE_DIRECTORIES SET)
if(has_includes)
get_property(all_include_directories TARGET ${TARGET} PROPERTY INCLUDE_DIRECTORIES)
foreach(dir ${all_include_directories})
if(IS_ABSOLUTE ${dir})
file(RELATIVE_PATH dir ${CMAKE_CURRENT_SOURCE_DIR} ${dir})
endif()
list(APPEND new_includes ${gen_dir}/${dir})
endforeach()
endif()
list(APPEND new_includes ${gen_dir})
target_include_directories(${TARGET} PRIVATE ${new_includes})

endfunction()

#! ly_add_translations: adds translations (ts) to a target.
#
# This wrapper will generate a qrc file with those translations and add the files under "prefix" and add them to
# the indicated targets. These files will be added under the "Generated Files" filter
#
# \arg:TARGETS name of the targets that the translations will be added to
# \arg:PREFIX prefix where the translation will be located within the qrc file
# \arg:FILES translation files to add
#
function(ly_add_translations)

set(options)
set(oneValueArgs PREFIX)
set(multiValueArgs TARGETS FILES)

cmake_parse_arguments(ly_add_translations "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})

# Validate input arguments
if(NOT ly_add_translations_TARGETS)
message(FATAL_ERROR "You must provide at least one target")
endif()
if(NOT ly_add_translations_FILES)
message(FATAL_ERROR "You must provide at least a translation file")
endif()

qt5_add_translation(TRANSLATED_FILES ${ly_add_translations_FILES})

set(qrc_file_contents
"<RCC>
<qresource prefix=\"/${ly_add_translations_PREFIX}\">
")
foreach(file ${TRANSLATED_FILES})
get_filename_component(filename ${file} NAME)
string(APPEND qrc_file_contents " <file>${filename}</file>
")
endforeach()
string(APPEND qrc_file_contents " </qresource>
</RCC>
")
set(qrc_file_path ${CMAKE_CURRENT_BINARY_DIR}/i18n_${ly_add_translations_PREFIX}.qrc)
file(WRITE
${qrc_file_path}
${qrc_file_contents}
)
set_source_files_properties(
${TRANSLATED_FILES}
${qrc_file_path}
PROPERTIES
GENERATED TRUE
SKIP_AUTORCC TRUE
)
qt5_add_resources(RESOURCE_FILE ${qrc_file_path})

foreach(target ${ly_add_translations_TARGETS})
target_sources(${target} PRIVATE "${TRANSLATED_FILES};${qrc_file_path};${RESOURCE_FILE}")
endforeach()

endfunction()
Loading
Loading