Commit 11490810 authored by Michael Pyne's avatar Michael Pyne
Browse files

Add support for building Qt5 modules.

This commit adds basic support for building Qt5 using the Qt5 support
documented at https://wiki.qt.io/Building_Qt_5_from_Git as requested in
issue #16 (and a dependency for #15).

Architecturally within kdesrc-build, Qt5 is handled as a special type of
module-set, in the same way that KDE project modules are special-cased
using 'kde-projects'. For Qt5, we use 'qt-projects', and reuse the
existing use-modules and ignore-modules options.

The first difference is that {use,ignore}-modules applies to Qt's git
*submodules*. We pass the combination of those to Qt's `init-repository`
script as a module-subset. Currently the user will need to enter at
least a use-modules declaration for other reasons, so we would want to
setup a sample qt5 configuration to include something appropriate.

Qt5 support also involves a dedicated source code updater (based on the
basic Git support already present) and a dedicated build system. The
source code updater handles the Git update for the qt5 "supermodule"
containing `init-repository` and then calls `init-repository` to
complete the rest of the process.

Unfortunately the existing async IPC code doesn't play well with this
but the worst that happens is that kdesrc-build will have 2 updates
running at once for a time (kdesrc-build will think all of Qt is updated
once the supermodule is updated).

The build system is actually fairly standard compared to the other
changes.

There's a lot that's still missing here, including:

* documentation,
* real support for Git submodules (an open feature request for a long
time),
* the per-distro list of Qt build dependencies not handled by
kdesrc-build, and
* support for things like Qt's `qt5_tool`.

But, it's successfully built for me with Qt 5.12. :)
parent 34cef6ec
Pipeline #858 passed with stage
in 1 minute and 3 seconds
......@@ -62,6 +62,7 @@ if (KDESRC_BUILD_INSTALL_MODULES)
modules/ksb/BuildSystem/KDE4.pm
modules/ksb/BuildSystem/QMake.pm
modules/ksb/BuildSystem/Qt4.pm
modules/ksb/BuildSystem/Qt5.pm
DESTINATION ${KDESRC_BUILD_MODULE_INSTALL_PREFIX}/ksb/BuildSystem)
install(FILES
......@@ -71,6 +72,7 @@ if (KDESRC_BUILD_INSTALL_MODULES)
install(FILES
modules/ksb/ModuleSet/KDEProjects.pm
modules/ksb/ModuleSet/Null.pm
modules/ksb/ModuleSet/Qt.pm
DESTINATION ${KDESRC_BUILD_MODULE_INSTALL_PREFIX}/ksb/ModuleSet)
install(FILES
......@@ -84,6 +86,7 @@ if (KDESRC_BUILD_INSTALL_MODULES)
modules/ksb/Updater/KDEProject.pm
modules/ksb/Updater/KDEProjectMetadata.pm
modules/ksb/Updater/Svn.pm
modules/ksb/Updater/Qt5.pm
DESTINATION ${KDESRC_BUILD_MODULE_INSTALL_PREFIX}/ksb/Updater)
endif()
......@@ -93,6 +96,8 @@ install(PROGRAMS ${CMAKE_CURRENT_SOURCE_DIR}/kdesrc-build-setup DESTINATION ${KD
install(PROGRAMS
${CMAKE_SOURCE_DIR}/sample-kde-env-master.sh
${CMAKE_SOURCE_DIR}/sample-xsession.sh
${CMAKE_SOURCE_DIR}/custom-qt5-libs-build-include
${CMAKE_SOURCE_DIR}/qt5-build-include
${CMAKE_SOURCE_DIR}/kf5-applications-build-include
${CMAKE_SOURCE_DIR}/kf5-extragear-build-include
${CMAKE_SOURCE_DIR}/kf5-frameworks-build-include
......
......@@ -2,7 +2,7 @@
# Script to create a configuration file for kdesrc-build.
#
# Copyright © 2011, 2016 Michael Pyne. <mpyne@kde.org>
# Copyright © 2011, 2019 Michael Pyne. <mpyne@kde.org>
# Home page: https://kdesrc-build.kde.org/
#
# This program is free software; you can redistribute it and/or modify it under
......@@ -266,12 +266,14 @@ else {
my @chosenModules = getListOptions(
"Which major module groups do you want to build?",
[
qt5 => 'Qt 5 - Base support libraries (required unless distro supplies)',
frameworks => 'KDE Frameworks 5 - Essential libraries/runtime (required)',
workspace => 'KDE Plasma 5 Desktop and workspace',
base => 'Assorted useful KF5-based applications',
pim => 'Personal Information Management software',
],
{
qt5 => 1,
frameworks => 1,
workspace => 1,
base => 1,
......@@ -335,17 +337,21 @@ EOF
print $output <<EOF;
# Autogenerated by kdesrc-build-setup. You may modify this file if desired.
global
EOF
print $output <<EOF;
# This option is used to switch development tracks for many modules at
# once. 'kf5-qt5' is the latest KF5 and Qt5-based software.
branch-group kf5-qt5
# Only set qtdir if we're building it ourselves. If user uses their own custom
# Qt they should already be setting PATH and in that case we need do nothing
# anyways.
if (grep /^qt5$/, @chosenModules) {
print $output <<EOF;
# The path to your Qt installation (default is empty, assumes Qt provided
# by system)
# qtdir ~/qt5
qtdir ~/kde/qt5
EOF
}
print $output <<EOF;
# Finds and includes *KDE*-based dependencies into the build. This makes
# it easier to ensure that you have all the modules needed, but the
......@@ -418,6 +424,19 @@ if (! -e "$basedir/kf5-frameworks-build-include") {
}
}
if (grep /^qt5$/, @chosenModules) {
print $output <<EOF;
# Refers to the qt5 file included as part of kdesrc-build. The file
# is simply read-in at this point as if you'd typed it in yourself.
include $basedir/qt5-build-include
# Support libraries that use Qt5
include $basedir/custom-qt5-libs-build-include
EOF
}
if (grep /^frameworks$/, @chosenModules) {
print $output <<EOF;
......
......@@ -20,6 +20,7 @@ use ksb::Module;
use ksb::ModuleResolver 0.20;
use ksb::ModuleSet 0.20;
use ksb::ModuleSet::KDEProjects;
use ksb::ModuleSet::Qt;
use ksb::OSSupport;
use ksb::RecursiveFH;
use ksb::DependencyResolver 0.20;
......@@ -41,7 +42,8 @@ use IO::Select;
use constant {
# We use a named remote to make some git commands work that don't accept the
# full path.
KDE_PROJECT_ID => 'kde-projects', # git-repository-base for kde_projects.xml
KDE_PROJECT_ID => 'kde-projects', # git-repository-base for sysadmin/repo-metadata
QT_PROJECT_ID => 'qt-projects', # git-repository-base for qt.io Git repo
};
### Package methods
......@@ -868,7 +870,7 @@ EOF
}
my $repoSet = $ctx->getOption('git-repository-base');
if ($selectedRepo ne KDE_PROJECT_ID &&
if ($selectedRepo ne KDE_PROJECT_ID && $selectedRepo ne QT_PROJECT_ID &&
not exists $repoSet->{$selectedRepo})
{
my $projectID = KDE_PROJECT_ID;
......@@ -992,13 +994,13 @@ sub _parseModuleSetOptions
$moduleSet = _parseModuleOptions($ctx, $fileReader, $moduleSet, qr/^end\s+module(-?set)?$/);
if ($moduleSet->getOption('repository') eq KDE_PROJECT_ID &&
!$moduleSet->isa('ksb::ModuleSet::KDEProjects'))
{
# Perl-specific note! re-blessing the module set into the right 'class'
# You'd probably have to construct an entirely new object and copy the
# members over in other languages.
# Perl-specific note! re-blessing the module set into the right 'class'
# You'd probably have to construct an entirely new object and copy the
# members over in other languages.
if ($moduleSet->getOption('repository') eq KDE_PROJECT_ID) {
bless $moduleSet, 'ksb::ModuleSet::KDEProjects';
} elsif ($moduleSet->getOption('repository') eq QT_PROJECT_ID) {
bless $moduleSet, 'ksb::ModuleSet::Qt';
}
return $moduleSet;
......
package ksb::BuildSystem::Qt5 0.10;
# Build system for the Qt5 toolkit
use strict;
use warnings;
use 5.014;
use ksb::BuildException;
use ksb::BuildSystem;
use ksb::Debug;
use ksb::Util;
use parent qw(ksb::BuildSystem);
# OVERRIDE
sub configuredModuleFileName
{
return 'Makefile';
}
# OVERRIDE
sub name
{
return 'Qt5';
}
# Return value style: boolean
sub configureInternal
{
my $self = assert_isa(shift, __PACKAGE__);
my $module = $self->module();
my $srcdir = $module->fullpath('source');
my $script = "$srcdir/configure";
if (! -e $script && !pretending())
{
error ("\tMissing configure script for r[b[$module]");
return 0;
}
my @commands = split (/\s+/, $module->getOption('configure-flags'));
push @commands, qw(-confirm-license -opensource -nomake examples -nomake tests);
# Get the user's CXXFLAGS
my $cxxflags = $module->getOption('cxxflags');
$module->buildContext()->queueEnvironmentVariable('CXXFLAGS', $cxxflags);
my $prefix = $module->getOption('prefix');
my $qtdir = $module->getOption('qtdir');
if ($prefix && $qtdir && $prefix ne $qtdir) {
warning (<<EOF);
b[y[*]
b[y[*] Building the Qt module, but the install directory for Qt is not set to the
b[y[*] Qt directory to use.
b[y[*] install directory ('prefix' option): b[$prefix]
b[y[*] Qt install to use ('qtdir' option): b[$qtdir]
b[y[*]
b[y[*] Try setting b[qtdir] to the same setting as the Qt module's b[prefix].
b[y[*]
EOF
}
$prefix ||= $qtdir; # Use qtdir for install if prefix not set
# Some users have added -prefix manually to their flags, they
# probably shouldn't anymore. :)
if (grep /^-prefix(=.*)?$/, @commands) {
warning (<<EOF);
b[y[*]
b[y[*] You have the y[-prefix] option selected in your $module configure flags.
b[y[*] kdesrc-build will correctly add the -prefix option to match your Qt
b[y[*] directory setting, so you do not need to use -prefix yourself.
b[y[*]
EOF
}
push @commands, "-prefix", $prefix;
unshift @commands, $script;
my $builddir = $module->fullpath('build');
my $old_flags = $module->getPersistentOption('last-configure-flags') || '';
my $cur_flags = get_list_digest(@commands);
if(($cur_flags ne $old_flags) ||
($module->getOption('reconfigure')) ||
1 || # TODO: Find a safe way to skip reconfiguration
(! -e "$builddir/Makefile")
)
{
note ("\tb[r[LGPL license selected for Qt]. See $srcdir/LICENSE.LGPL");
info ("\tRunning g[configure]...");
$module->setPersistentOption('last-configure-flags', $cur_flags);
return log_command($module, "configure", \@commands) == 0;
}
# Skip execution of configure.
return 1;
}
1;
......@@ -130,8 +130,8 @@ DONE
_throw("Embedded sample file missing!");
my $numCpus = `nproc 2>/dev/null` || 4;
$sampleRc =~ s/%\{num_cpus}/$numCpus/;
$sampleRc =~ s/%\{base_dir}/$baseDir/;
$sampleRc =~ s/%\{num_cpus}/$numCpus/g;
$sampleRc =~ s/%\{base_dir}/$baseDir/g;
open my $sampleFh, '>', "$ENV{HOME}/.kdesrc-buildrc"
or _throw("Couldn't open new ~/.kdesrc-buildrc: $!");
......@@ -310,20 +310,20 @@ dnf -y install
# List of all options: https://go.kde.org/u/ksboptions
global
branch-group kf5-qt5
kdedir ~/kde-5 # Where to install KF5-based software
# Uncomment this and edit value to choose a different Qt5
# qtdir /usr # Where to find Qt5
# Paths
kdedir ~/kde/usr # Where to install KF5-based software
qtdir ~/kde/qt5 # Where to find Qt5
source-dir ~/kde/src # Where sources are downloaded
build-dir ~/kde/build # Where the source build is run
ignore-kde-structure true # Use flat structure
# Will pull in KDE-based dependencies only, to save you the trouble of
# listing them all below
include-dependencies true
source-dir ~/kde/src
build-dir ~/kde/build
ignore-kde-structure true
cmake-options -DCMAKE_BUILD_TYPE=RelWithDebInfo
make-options -j%{num_cpus}
end global
......@@ -336,6 +336,11 @@ end global
# You can include other files inline using the "include" command. We do this here
# to include files which are updated with kdesrc-build.
# Qt and some Qt-using middleware libraries
include %{base_dir}/qt5-build-include
include %{base_dir}/custom-qt5-libs-build-include
# KF5 and Plasma :)
include %{base_dir}/kf5-qt5-build-include
# To change options for modules that have already been defined, use an
......@@ -343,4 +348,3 @@ include %{base_dir}/kf5-qt5-build-include
options kcoreaddons
make-options -j4
end options
......@@ -23,6 +23,7 @@ use ksb::Updater::Git;
use ksb::Updater::Bzr;
use ksb::Updater::KDEProject;
use ksb::Updater::KDEProjectMetadata;
use ksb::Updater::Qt5;
use ksb::BuildException 0.20;
......@@ -30,6 +31,7 @@ use ksb::BuildSystem 0.30;
use ksb::BuildSystem::Autotools;
use ksb::BuildSystem::QMake;
use ksb::BuildSystem::Qt4;
use ksb::BuildSystem::Qt5;
use ksb::BuildSystem::KDE4;
use ksb::BuildSystem::CMakeBootstrap;
......@@ -256,6 +258,7 @@ sub setScmType
when('l10n') { $newType = ksb::l10nSystem->new($self); }
when('svn') { $newType = ksb::Updater::Svn->new($self); }
when('bzr') { $newType = ksb::Updater::Bzr->new($self); }
when('qt5') { $newType = ksb::Updater::Qt5->new($self); }
default { $newType = undef; }
}
......@@ -290,6 +293,7 @@ sub buildSystemFromName
'cmake-bootstrap' => 'ksb::BuildSystem::CMakeBootstrap',
'kde' => 'ksb::BuildSystem::KDE4',
'qt' => 'ksb::BuildSystem::Qt4',
'qt5' => 'ksb::BuildSystem::Qt5',
'autotools' => 'ksb::BuildSystem::Autotools',
);
......
package ksb::ModuleSet::Qt 0.10;
# Class: ModuleSet::Qt
#
# This represents a collection of Qt5 source code modules that are collectively
# kept up to date by Qt's init-repository script. This module set is
# essentially uses to make sure that generated ksb::Modules use proper scm()
# and buildSystems()
#
# Use of this module-set is controlled by the 'repository' option being set to
# the magic value 'qt-projects', just as 'kde-projects' is used for KDE.
use strict;
use warnings;
use 5.014;
use parent qw(ksb::ModuleSet);
use ksb::BuildContext;
use ksb::BuildException;
use ksb::BuildSystem::Qt5;
use ksb::Debug;
use ksb::Module;
use ksb::Util;
sub _makeQt5Module
{
my $self = assert_isa(shift, __PACKAGE__);
my $ctx = assert_isa(shift, 'ksb::BuildContext');
my $newModule = ksb::Module->new($ctx, 'qt5');
$self->_initializeNewModule($newModule);
# Repo URL to the Qt5 "supermodule" that contains the documented
# init-repository script.
# See https://wiki.qt.io/Building_Qt_5_from_Git
$newModule->setOption('repository', 'https://code.qt.io/qt/qt5.git');
$newModule->setScmType('qt5');
$newModule->setBuildSystem(ksb::BuildSystem::Qt5->new($newModule));
# Convert the use-modules/ignore-modules entries into a form appropriate
# for init-repository's module-subset option.
my @modEntries = ($self->modulesToFind(), map { "-$_" } $self->modulesToIgnore());
$newModule->setOption('use-qt5-modules', join(' ', @modEntries));
return $newModule;
}
# This function should be called after options are read and build metadata is
# available in order to convert this module set to a list of ksb::Module.
#
# In our case, we will return ONLY ONE MODULE. That module will handle "sub
# modules" via the init-repository script so from kdesrc-build's perspective it
# is handled as a single unit.
#
# OVERRIDE from super class
sub convertToModules
{
my ($self, $ctx) = @_;
return $self->_makeQt5Module($ctx);
}
1;
......@@ -140,34 +140,22 @@ sub _clone
return;
}
# Either performs the initial checkout or updates the current git checkout
# for git-using modules, as appropriate.
#
# If errors are encountered, an exception is raised.
#
# Returns the number of *commits* affected.
sub updateCheckout
# Checks that the required source dir is either not already present or is empty.
# Throws an exception if that's not true.
sub _verifySafeToCloneIntoSourceDir
{
my $self = assert_isa(shift, 'ksb::Updater::Git');
my $module = $self->module();
my $srcdir = $module->fullpath('source');
if (-d "$srcdir/.git") {
# Note that this function will throw an exception on failure.
return $self->updateExistingClone();
}
else {
# Check if an existing source directory is there somehow.
if (-e "$srcdir" && !is_dir_empty($srcdir)) {
if ($module->getOption('#delete-my-patches')) {
warning ("\tRemoving conflicting source directory " .
"as allowed by --delete-my-patches");
warning ("\tRemoving b[$srcdir]");
safe_rmtree($srcdir) or
croak_internal("Unable to delete $srcdir!");
}
else {
error (<<EOF);
my ($module, $srcdir) = @_;
if (-e "$srcdir" && !is_dir_empty($srcdir)) {
if ($module->getOption('#delete-my-patches')) {
warning ("\tRemoving conflicting source directory " .
"as allowed by --delete-my-patches");
warning ("\tRemoving b[$srcdir]");
safe_rmtree($srcdir) or
croak_internal("Unable to delete $srcdir!");
}
else {
error (<<EOF);
The source directory for b[$module] does not exist. kdesrc-build would download
it, except there is already a file or directory present in the desired source
directory:
......@@ -182,14 +170,36 @@ FILES IN THE DIRECTORY.
EOF
if (-e "$srcdir/.svn") {
error ("svn status of $srcdir:");
system('svn', 'st', '--non-interactive', $srcdir);
}
croak_runtime('Conflicting source-dir present');
if (-e "$srcdir/.svn") {
error ("svn status of $srcdir:");
system('svn', 'st', '--non-interactive', $srcdir);
}
croak_runtime('Conflicting source-dir present');
}
}
return;
}
# Either performs the initial checkout or updates the current git checkout
# for git-using modules, as appropriate.
#
# If errors are encountered, an exception is raised.
#
# Returns the number of *commits* affected.
sub updateCheckout
{
my $self = assert_isa(shift, 'ksb::Updater::Git');
my $module = $self->module();
my $srcdir = $module->fullpath('source');
if (-d "$srcdir/.git") {
# Note that this function will throw an exception on failure.
return $self->updateExistingClone();
}
else {
_verifySafeToCloneIntoSourceDir($module, $srcdir);
my $git_repo = $module->getOption('repository');
......
package ksb::Updater::Qt5 0.10;
# Handles updating Qt 5 source code. Requires git but uses Qt 5's dedicated
# 'init-repository' script to keep the source up to date and coherent.
use strict;
use warnings;
use 5.014;
use parent qw(ksb::Updater::Git);
use ksb::BuildException;
use ksb::Debug;
use ksb::IPC::Null;
use ksb::Util;
sub name
{
return 'qt5';
}
# Handles calling init-repository to clone or update the appropriate Qt 5
# submodules.
#
# Returns number of commits updated (or rather, will...)
sub _updateRepository
{
my $self = assert_isa(shift, __PACKAGE__);
my $module = $self->module();
my $srcdir = $module->fullpath('source');
if (!pretending() && (! -e "$srcdir/init-repository" || ! -x _)) {
croak_runtime ("The Qt 5 repository update script could not be found, or is not executable!");
}
p_chdir($srcdir);
# See https://wiki.qt.io/Building_Qt_5_from_Git#Getting_the_source_code for
# why we skip web engine by default. As of 2019-01-12 it is only used for
# PIM or optionally within Plasma
my @modules = split(' ', $module->getOption('use-qt5-modules'));
push @modules, qw(default -qtwebengine)
unless @modules;
my $subset_arg = join(',', @modules);
# -f forces a re-update if necessary
my @command = ("$srcdir/init-repository", '-f', "--module-subset=$subset_arg");
note ("\tUsing Qt 5 modules: ", join(', ', @modules));
if (0 != log_command($module, 'init-repository', \@command)) {
croak_runtime ("Couldn't update Qt 5 repository submodules!");
}
return 1; # TODO: Count commits
}
# Updates an existing Qt5 super module checkout.
# Throws exceptions on failure, otherwise returns number of commits updated
# OVERRIDE from super class
sub updateExistingClone
{
my $self = assert_isa(shift, __PACKAGE__);
# Update init-repository and the shell of the super module itself.
my $count = $self->SUPER::updateExistingClone();
# updateRepository has init-repository work to update the source
return $count + $self->_updateRepository();
}
# Either performs the initial checkout or updates the current git checkout
# for git-using modules, as appropriate.
#
# If errors are encountered, an exception is raised.
#
# Returns the number of *commits* affected.
# OVERRIDE from super class
sub updateCheckout
{
my $self = assert_isa(shift, __PACKAGE__);
my $module = $self->module();
my $srcdir = $module->fullpath('source');
if (-d "$srcdir/.git") {
# Note that this function will throw an exception on failure.
return $self->updateExistingClone();
}
else {
$self->_verifySafeToCloneIntoSourceDir($module, $srcdir);
$self->_clone($module->getOption('repository'));
note ("\tQt update script is installed, downloading remainder of Qt");
note ("\tb[y[THIS WILL TAKE SOME TIME]");
# With the supermodule cloned, we then need to call into
# init-repository to have it complete the checkout.
return $self->_updateRepository(); # num commits
}
return 0; # num commits
}
1;
module-set qt5-set
repository qt-projects
branch 5.11 # not the most recent but recent enough
# init-repository supports catch-alls like "default" as well, in which case
# you will want to uncomment ignore-modules below to control which modules to
# leave out
use-modules qtbase qtdeclarative qtgraphicaleffects qtimageformats \
qtmultimedia qtquickcontrols qtquickcontrols2 qtscript qtsensors qtsvg \
qttools qtwayland qtwebchannel qtwebsockets qtwebview qtx11extras \
qtxmlpatterns
# ignore-modules qtwebengine
# install path. This *MUST* match your qtdir setting in kdesrc-buildrc!