From b60fa530d9af05e66fc9f0da74f2df05f80ce2a7 Mon Sep 17 00:00:00 2001 From: Marius Storm-Olsen Date: Tue, 20 Mar 2012 09:00:28 +0100 Subject: Add build script for Qt 5 The build script will build each module individually, and install them if needed, in the correct order. Change-Id: I9416e624b080b8b25241270e909bd120a4028137 Reviewed-by: Lars Knoll --- build | 408 +++++++++++++++++++++++++++++++++++++++++++++++++++++ build.dependencies | 51 +++++++ 2 files changed, 459 insertions(+) create mode 100755 build create mode 100644 build.dependencies diff --git a/build b/build new file mode 100755 index 00000000..6414260b --- /dev/null +++ b/build @@ -0,0 +1,408 @@ +#!/usr/bin/perl +############################################################################# +## +## Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +## Contact: http://www.qt-project.org/ +## +## This file is part of the utilities of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:LGPL$ +## GNU Lesser General Public License Usage +## This file may be used under the terms of the GNU Lesser General Public +## License version 2.1 as published by the Free Software Foundation and +## appearing in the file LICENSE.LGPL included in the packaging of this +## file. Please review the following information to ensure the GNU Lesser +## General Public License version 2.1 requirements will be met: +## http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +## +## In addition, as a special exception, Nokia gives you certain additional +## rights. These rights are described in the Nokia Qt LGPL Exception +## version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU General +## Public License version 3.0 as published by the Free Software Foundation +## and appearing in the file LICENSE.GPL included in the packaging of this +## file. Please review the following information to ensure the GNU General +## Public License version 3.0 requirements will be met: +## http://www.gnu.org/copyleft/gpl.html. +## +## Other Usage +## Alternatively, this file may be used in accordance with the terms and +## conditions contained in a signed written agreement between you and Nokia. +## +## +## +## +## +## +## $QT_END_LICENSE$ +## +############################################################################# + +use v5.008; +use strict; +use warnings; + +package Qt::Build; + +use Carp qw( confess ); +use English qw( -no_match_vars ); +use Getopt::Long qw( GetOptionsFromArray ); +use Pod::Usage qw( pod2usage ); +use Cwd qw( getcwd ); +use Config; +use thread; +use threads::shared; + +# Like `system', but possibly log the command, and die on non-zero exit code +sub exe +{ + my ($self, @cmd) = @_; + + print "+ @cmd\n" unless ($self->{quiet}); + + if (system(@cmd) != 0) { + confess "@cmd exited with status $CHILD_ERROR"; + } + + return; +} + +sub dropPrivileges() +{ + my ($self) = @_; + + if ($> == 0) { # EUID == 0: must drop if possible + local $! = undef; + if ($< != 0) { # UID != 0 (run through setuid). swap UID with EUID + ($(, $)) = ($), $(); + die "Cannot lower gid privileges: $!" if $!; + ($<, $>) = ($>, $<); + die "Cannot lower uid privileges: $!" if $!; + } else { # UID == 0: run through sudo? + if (defined $ENV{SUDO_GID}) { + $) = "$ENV{SUDO_GID} $ENV{SUDO_GID}"; + die "Cannot lower gid privileges: $!" if $!; + } + if (defined $ENV{SUDO_UID}) { + $> = $ENV{SUDO_UID}; + die "Cannot lower uid privileges: $!" if $!; + } + } + } +} + +sub exeHighPriv() +{ + my ($self, @cmd) = @_; + return $self->exe(@cmd); +} + +sub exeLowPriv() +{ + my ($self, @cmd) = @_; + + if ("$Config{osname}" =~ /mswin/i) { + # Just like exeHighPriv for now + return $self->exe(@cmd); + } else { + my $ret; + my $pid = fork(); + die "Couldn't fork" unless defined $pid; + if ($pid == 0) { + $self->dropPrivileges; + $ret = $self->exe(@cmd); + exit $ret; + } else { + waitpid($pid, 0); + return $?; + } + } +} + +sub new +{ + my ($class, @arguments) = @_; + + my $self = {}; + bless $self, $class; + + $self->parse_arguments(@arguments); + $self->detect_configuration; + + my $depfile = "build.dependencies"; + my $result; + our (%build_dependencies, %build_commands); + + # following variables may be expanded in the evaluation below + my $MAKEOPTS = $self->{'MAKEOPTS'}; + my $MAKE = $self->{'MAKE'}; + + unless ($result = do $depfile) { + die "build couldn't parse $depfile: $@" if $@; + die "build couldn't execute $depfile: $!" unless defined $result; + } + + $self->{'deps'} = \%build_dependencies; + $self->{'cmds'} = \%build_commands; + + return $self; +} + +sub parse_arguments +{ + my ($self, @args) = @_; + + %{$self} = (%{$self}, + 'verbose' => 0, + 'continue' => 0, + 'jobs' => 1, + 'build-submodules' => [], + ); + + GetOptionsFromArray(\@args, + 'verbose|v:1' => \$self->{'verbose'}, + 'continue' => \$self->{'continue'}, + 'jobs|j:1' => \$self->{'jobs'}, + 'help|?' => sub { pod2usage(1); }, + ) || pod2usage(2); + + push(@{$self->{'build-submodules'}}, @args) if (@args); + + return; +} + +sub detect_configuration +{ + my ($self) = @_; + + die "You need to configure Qt before you try to build it, aborting." if (!-e 'qtbase/.qmake.cache'); + + $self->{'MAKEOPTS'} = "-s -j $self->{'jobs'}"; + $self->{'MAKE'} = "make"; + + if ("$Config{osname}" =~ /(ms|cyg)win/i) { + use File::Which; + my $exe = which("nmake.exe"); + $exe = which("jom.exe") if (defined $exe && which("jom.exe")); + $exe = which("mingw32-make") if (!defined $exe); + + # Use the /MP compiler option, if using nmake, to use all CPU threads when compiling + if ($exe =~ 'nmake') { + use Env qw(@CL); + unshift @CL, '/MP'; + } + + $self->{'MAKE'} = $exe if (defined $exe); + $self->{'MAKEOPTS'} = "/s" if (defined $exe && $exe =~ /nmake/); + } + + if (-e 'qtbase/bin') { + use Cwd qw(abs_path); + use Env qw(@PATH); + my $abs_path = abs_path('qtbase/bin'); + unshift @PATH, "$abs_path"; + } +} + +sub find_pro_file +{ + my ($self, $dir) = @_; + my $D; + if (opendir($D,$dir)) { + ($dir =~ /\/$/) || ($dir .= "/"); + foreach my $file (sort readdir($D)) { + if ($file =~ /^.*\.pro$/) { + closedir($D); + return $file; + } + } + closedir($D); + } +} + +sub eliminate_empty_modules +{ + my ($self) = @_; + foreach my $repo (keys(%{$self->{'deps'}})) { + if (!$self->find_pro_file($repo)) { + printf "Missing module %s, ignored\n", $repo; + delete $self->{'deps'}->{$repo}; + } + } +} + +sub check_build_module +{ + my ($self, $module) = @_; + my @missing_link; + foreach my $submod (split(/,/, $self->{'deps'}->{$module})) { + next if ($submod =~ /:s$/); # Soft dependency + if (defined $self->{'deps'}->{$submod}) { + push(@missing_link, $self->check_build_module($submod)); + } else { + push(@missing_link, $submod); + } + } + return @missing_link; +} + +sub check_build_modules +{ + my ($self, $fail) = @_; + my $letsdie = 0; + foreach my $module (@{$self->{'build-submodules'}}) { + if (defined $self->{'deps'}->{$module}) { + my @missing_link = $self->check_build_module($module); + if (scalar @missing_link) { + $letsdie = 1; + my $mods = join(", ", @missing_link); + print STDERR "Ignoring module '$module': requires $mods\n"; + } + } else { + print STDERR "No module named '$module'\n"; + $letsdie = 1; + } + } + die "FAIL: Missing module dependencies, build aborted." if ($letsdie && $fail); +} + +sub resolve_soft_dependencies +{ + my ($self) = @_; + + foreach my $module (keys(%{$self->{'deps'}})) { + my @deps = split(/,/, $self->{'deps'}->{$module}); + my @newdeps; + foreach my $dep (@deps) { + if ($dep =~ /(.*):s$/) { + push(@newdeps, $1) if (defined $self->{'deps'}->{$1}) + } else { + push(@newdeps, $dep); + } + } + $self->{'deps'}->{$module} = join(",", @newdeps); + } +} + +sub mark_as_finished +{ + my ($self, $doneModule) = @_; + + delete $self->{'deps'}->{$doneModule}; + foreach my $module (keys(%{$self->{'deps'}})) { + my @deps = split(/,/, $self->{'deps'}->{$module}); + @deps = grep { $_ !~ /$doneModule/ } @deps; + $self->{'deps'}->{$module} = join(",", @deps); + } +} + +sub get_next_modules +{ + my ($self, $module) = @_; + + my @nextModules; + my $deps = $self->{'deps'}->{$module}; + return if (!defined $deps); + $self->{'seenHash'}->{$module}++; + if ($deps eq '') { + push (@nextModules, $module); + return @nextModules; + } + + foreach my $dep (split(/,/, $deps)) { + push (@nextModules, $self->get_next_modules($dep)) unless $self->{'seenHash'}->{$module}; + } + + return @nextModules; +} + +sub get_all_next_modules +{ + my ($self) = @_; + + $self->{'seenHash'} = (); + + my @nextModules; + foreach my $module (@{$self->{'build-submodules'}}) { + my @mods = $self->get_next_modules($module); + push(@nextModules, @mods); + } + + my %seen = (); + my @uniqModules; + foreach my $item (@nextModules) { + push(@uniqModules, $item) unless $seen{$item}++; + } + + return @uniqModules; +} + +sub build_project +{ + my ($self, $module) = @_; + my $build_command = $self->{'cmds'}->{$module}; + $build_command = "qmake -r && $self->{MAKE} $self->{MAKEOPTS}" if (!defined $build_command); + exeLowPriv("cd $module && $build_command") && die "'cd $module && $build_command' failed: $?"; + exeHighPriv("cd $module && $self->{MAKE} install") && die "'cd $module && $self->{MAKE} install failed: $?"; + $self->mark_as_finished($module); + return 0; +} + +sub build_qt +{ + my ($self) = @_; + + printf "OS Name ........ %s\n", $Config{osname}; + printf "Verbose ........ %s\n", ($self->{'verbose'} ? $self->{'verbose'} : "no"); + printf "Continue ....... %s\n", ($self->{'continue'} ? "yes" : "no"); + printf "Jobs ........... %d\n", $self->{'jobs'}; + + my $path = $ENV{'PATH'}; + print "PATH $path\n"; + + print "Modules to build:\n"; + my $mods = "(all present)"; + $mods = join(", ", @{$self->{'build-submodules'}}) if (@{$self->{'build-submodules'}}); + print " $mods\n"; + + while (my @modules = $self->get_all_next_modules) { + my @modules = $self->get_all_next_modules; + foreach my $module (@modules) { + print "build $module...\n"; + $self->build_project($module); + } + } + + print "build done!\n"; + + return 0; +} + +sub run +{ + my ($self) = @_; + + $self->eliminate_empty_modules; + + if (scalar @{$self->{'build-submodules'}} > 0) { + $self->check_build_modules(1); + } else { + push(@{$self->{'build-submodules'}}, keys(%{$self->{'deps'}})); + $self->check_build_modules(0); + } + + $self->resolve_soft_dependencies; + + $self->build_qt; + +# print Dumper($self); + + return; +} + +#============================================================================== + +Qt::Build->new(@ARGV)->run if (!caller); +1; diff --git a/build.dependencies b/build.dependencies new file mode 100644 index 00000000..f92f5442 --- /dev/null +++ b/build.dependencies @@ -0,0 +1,51 @@ +# Platform independent modules + +# Dependencies separated with comma ',' +# Dependencies with ':s' appended indicate soft dependencies, which +# means that they are a dependency if the module is present, if not +# they are ignored. + +use Config; +%build_dependencies = ( + "qlalr" => "qtbase", + "qt3d" => "qtbase,qtdeclarative", + "qtbase" => "", + "qtconnectivity" => "qtsystems", + "qtdeclarative" => "qtbase,qtxmlpatterns,qtjsbackend,qtsvg:s", + "qtdoc" => "qtbase,qtdeclarative", + "qtdocgallery" => "qtbase,qtscript,qtdeclarative,qtjsondb:s", + "qtfeedback" => "qtbase,qtmultimedia,qtdeclarative", + "qtgraphicaleffects" => "qtbase,qtdeclarative,qtsvg,qtxmlpatterns", + "qtimageformats" => "qtbase", + "qtjsbackend" => "qtbase", + "qtjsondb" => "qtbase,qtdeclarative,qtxmlpatterns", + "qtlocation" => "qtbase,qtdeclarative,qt3d,qtjsondb", + "qtmultimedia" => "qtbase,qtdeclarative", + "qtphonon" => "qtbase", + "qtpim" => "qtdeclarative,qtjsondb:s", + "qtqa" => "qtbase", + "qtquick1" => "qtbase,qtscript,qtxmlpatterns,qtsvg:s", + "qtscript" => "qtbase", + "qtsensors" => "qtbase,qtdeclarative", + "qtsvg" => "qtbase", + "qtsystems" => "qtbase,qtdeclarative,qtjsondb:s", + "qttools" => "qtbase,qtscript,qtdeclarative,qtquick1,qtwebkit:s", + "qttranslations" => "qttools", + "qtwebkit" => "qtbase,qtscript,qtdeclarative,qtquick1,qtlocation", + "qtwebkit-examples-and-demos" => "qtwebkit", + "qtxmlpatterns" => "qtbase", +); + +%build_commands = ( + "qtwebkit" => "QMAKEPATH=Tools/qmake qmake && make", +); + +# Platform specific modules + +if ("$Config{osname}" =~ /linux/i) { + $build_dependencies{"qtwaysland"} = "qtbase"; +} + +if ("$Config{osname}" =~ /(ms|cyg)win/i) { + $build_dependencies{"qtactiveqt"} = "qtbase"; +} -- cgit v1.2.3