#!/usr/bin/perl use FindBin; use Mojolicious; use Mojo::File 'path'; use Mojo::Log; use Mojo::Util; use warnings; use strict; use utf8; my @vcs_0_variant_suffixes = (qw(git svn hg)); my @cfg_0_variant_suffixes = (qw(static static-compat)); my @cfg_1_variant_suffixes = (qw(doc test cli angle dynamic opengl noopengl)); my @variant_suffixes = (\@vcs_0_variant_suffixes, \@cfg_0_variant_suffixes, \@cfg_1_variant_suffixes); sub is_outdated { my ($source_path, $target_path) = @_; my $source_last_modified = (stat($source_path))[9]; my $target_last_modified = (stat($target_path))[9]; return !defined $target_last_modified || $source_last_modified > $target_last_modified; } my $log = Mojo::Log->new; my $mojolicious = Mojolicious->new; my $renderer = $mojolicious->renderer; my $pkgbuilds_dir = path($FindBin::Bin, '..', '..')->realpath; my @template_paths = ("$FindBin::Bin/templates", $pkgbuilds_dir); $log->level($ENV{LOG_LEVEL} // 'info'); $mojolicious->log($log); $renderer->paths(\@template_paths); $log->debug("Template paths:\n" . join("\n", @template_paths)); my $filter_regex = $ARGV[0]; my $install_directory = $ARGV[1] // $pkgbuilds_dir; unless (-d $install_directory) { $log->error("Output directory '$install_directory' does not exist."); exit(-1); } # add helper to render Qt dependencies sub _render_deps { my ($package_prefix, $controller, @d) = @_; my $prefix = $controller->stash('package_name_prefix'); my $suffix = $controller->stash('package_name_suffix'); my $quote = $prefix =~ qr/^(mingw-w64|android)/ ? "'" : ''; return join(' ', map { "${quote}${prefix}${package_prefix}-${_}${suffix}${quote}" } @d); } sub _render_optdeps { my ($package_prefix, $controller, %d) = @_; my $prefix = $controller->stash('package_name_prefix'); my $suffix = $controller->stash('package_name_suffix'); return join(' ', map { "'${prefix}${package_prefix}-${_}${suffix}: $d{$_}'" } sort keys %d); } for my $qt_version (qw(qt5 qt6)) { $mojolicious->helper("${qt_version}deps" => sub { _render_deps($qt_version, @_) }); $mojolicious->helper("${qt_version}optdeps" => sub { _render_optdeps($qt_version, @_) }); } # add helper to expand pkg-config style libraries into full paths for use with CMake # example: "-lfoo -lbar" => "/usr/lib/foo.a;/usr/lib/bar.a" $mojolicious->helper(expand_libs => sub { my $controller = shift; my $prefix = $controller->stash('install_prefix'); my $extension = $controller->stash('library_extension'); return join(';', map { my $library_name = $_; $library_name = $1 if $library_name =~ qr/\w*-l(.*)\w*/; "$prefix/lib/lib$library_name.$extension"; } @_); }); # define revisions of Qt modules from KDE fork my %kde_fork_revisions = ( # module => [rev, 'commit on KDE fork', 'version bump to be reverted'], activeqt => [0, '4fc1cba4c415d84a5879da29f7c459b70fbc15e9'], winextras => [0, 'ee931eba5d129284d5c33157cd7d0b9232fbee7b'], '3d' => [0, 'e1b1a0d2970fd384bd52c734a72536d8452ad070'], base => [147, '8907dedc858cc344d770a2e826d6acc516429540'], charts => [0, '393a84ad5b16a9ec93d8a44bebf1ae86e881bc06'], connectivity => [6, '70020cb64f71dcf2fd65a8a167cb785d2127e159'], datavis3d => [0, 'c887477198cae44585fe9db371db0ddf4c3b205e'], declarative => [31, '792a55bb701d233116c3731c7a53ffdb8c67e407'], gamepad => [0, '8ed95136b3c265b01db6cc33869228f41878e173'], graphicaleffects => [0, 'e33716bd6bb8926688fef20cb568e11618d08a35'], imageformats => [10, '142040e8a652e708ff6e004361f6bcfe85fefdf9'], location => [6, '5b27b8921f1f2de93573df903c47aee634209f80'], multimedia => [2, '36603a39aa590c12cbe2b192b56b29edd09a7a6b'], networkauth => [0, '3fccc9b8fdaff1252fb4a9c516868d0bbbd4384d'], quickcontrols => [0, '1ca962198a703f591efc7c8f7540fc4120746a00'], quickcontrols2 => [5, '134ca5dbef9d137a9c46faa79b0225bc650d9283'], remoteobjects => [0, 'f64e34be9ac4b7e92c63e47235c04471a1d40c93'], scxml => [0, '3f56c6b4bd1e3883581340243b4a7289807fffc9'], sensors => [0, '3011b16d63cadbb473b6aa3a535b9f0e33170c09'], serialport => [0, 'c3a7debff7a4c6ddaedb795290180dd99d7ac4be'], speech => [1, 'c41437acf07c2c4703351b07925fce3ce0e6b75d'], svg => [6, '5b1b4a99d6bc98c42a11b7a3f6c9f0b0f9e56f34'], tools => [4, 'bd0ceb7de5d0c918ae596150e95b069dca8b9150'], translations => [0, 'f7745c117041e7adf9705e1de8d71086c160dd9f'], virtualkeyboard => [0, '8b885af5ad3c2f2ff500c060a41e312ea7276e50'], webchannel => [3, '6d2f0c3a36d9b2cdcd759a464c608365a0afda98'], webglplugin => [0, '8f879e6bcf941a612c568fbfe2b49ddb1bb409cd'], websockets => [2, '9a7b9972a54137d5f2e0d49559fe58d07c90662e'], xmlpatterns => [0, '6e0917d518e07f737cc663b8d632c8021634fd3b'], ); # $rev := git rev-list --count v5.15.2..$commit_on_kde_fork # find templates; populate "pages" array my @pages; my $template_file_name = 'PKGBUILD.sh.ep'; my $top_level_dirs = $pkgbuilds_dir->list({dir => 1}); for my $top_level_dir (@$top_level_dirs) { next unless -d $top_level_dir; next unless $top_level_dir ne 'devel'; my $default_package_name = $top_level_dir->basename; my ($qt_module, $qt_major_version); if ($default_package_name =~ qr/qt(5|6)-(.*)/) { $qt_major_version = $1; $qt_module = $2; } my $kde_fork_revision; if ($qt_major_version && $qt_major_version eq '5' && $qt_module) { $kde_fork_revision = $kde_fork_revisions{$qt_module}; } my $variant_dirs = $top_level_dir->list({dir => 1}); for my $variant_dir (@$variant_dirs) { next unless -d $variant_dir; my $variant = $variant_dir->basename; my $template_file = $variant_dir->child($template_file_name); if (!-f $template_file) { # print warning; all additional Qt repos for mingw-w64 should be converted to use templates now $log->warn("No template $template_file_name present for Qt module $qt_module and variant $variant") if defined $qt_module && index($variant, 'mingw-w64') == 0 && $variant !~ qr/.*-test$/; next; } # determine files my $files = $variant_dir->list; my $patch_files = $files->grep(qr/.*\.patch/); my $qt_module_sha256_file_name = "qt$qt_module-sha256.txt"; my $qt_module_sha256_file = defined $qt_module ? $variant_dir->child($qt_module_sha256_file_name) : undef; my $qt_module_sha256 = defined $qt_module_sha256_file && -f $qt_module_sha256_file ? Mojo::Util::trim($qt_module_sha256_file->slurp) : "$qt_module_sha256_file_name missing"; # determine variant parts my $variant_prefix_part = $variant; my $variant_suffix_part = ''; if ($variant) { for my $variant_suffixes (@variant_suffixes) { for my $variant_suffix (@$variant_suffixes) { next unless $variant =~ qr/.*-$variant_suffix$/; $variant_prefix_part = substr($variant, 0, length($variant) - length($variant_suffix) - 1); $variant_suffix_part = $variant_suffix_part ? "$variant_suffix-$variant_suffix_part" : $variant_suffix; last; } } } my $package_name_prefix = $variant_prefix_part ? "$variant_prefix_part-" : ""; my $package_name_suffix = $variant_suffix_part ? "-$variant_suffix_part" : ""; my $is_static_variant = $variant_suffix_part =~ qr/static/; my $has_static_variant = $is_static_variant || -d "$default_package_name/$variant-static"; my $is_mingw = $package_name_prefix eq 'mingw-w64-'; my $package_name = "$package_name_prefix$default_package_name$package_name_suffix"; next if defined $filter_regex && $package_name !~ $filter_regex; push(@pages, { install_path => "$default_package_name/$variant/PKGBUILD", template_params => [ template => "$default_package_name/$variant/PKGBUILD", stash_variables => { variant => $variant, variant_prefix_part => $variant_prefix_part, variant_suffix_part => $variant_suffix_part, default_package_name => $default_package_name, package_name_prefix => $package_name_prefix, package_name_suffix => $package_name_suffix, package_name => $package_name, files => $files, patch_files => $patch_files, qt_major_version => $qt_major_version, qt_module => $qt_module, qt_module_sha256 => $qt_module_sha256, kde_fork_revision => $kde_fork_revision, static_variant => $is_static_variant, static_suffix => $is_static_variant ? '-static' : '', static_deps => undef, static_makedeps => undef, is_mingw => $is_mingw, library_extension => $is_static_variant ? 'a' : ($is_mingw ? 'dll.a' : 'so'), install_prefix => $is_mingw ? '/usr/$_arch' : '/usr', shared_config => !$is_static_variant, static_config => $is_static_variant || !$has_static_variant, no_libraries => 0, }, ] }); } } # render "pages" for my $page (@pages) { # process template params my $template_params = $page->{template_params}; my $template_source_path; my $template_target_path; my $template_stash_variables; if (defined $template_params) { my ($template_name, $template_args) = (@$template_params % 2 ? shift @$template_params : undef, {@$template_params}); my $template_format = ($template_args->{format} //= 'sh'); my $template_handler = $template_args->{handler} // 'ep'; $template_name //= $template_args->{template}; $template_stash_variables = delete $template_args->{stash_variables}; $template_source_path = "$template_name.$template_format.$template_handler"; $template_target_path = "$template_name.$template_format"; $template_params = $template_args; $template_params->{template} = $template_name; } # determine source path and target path my $source_path = $page->{source_path} // $template_source_path; if (!$template_params && !$source_path) { die 'page needs either template_params or source_path'; } my $output_file = path($install_directory, $page->{install_path} // $template_target_path // $source_path); # ensure output directory exists $output_file->dirname->make_path unless -f $output_file; # skip unless the target is outdated # note: Can not skip templates that easy because that would require tracking includes. if (!defined $template_params && !is_outdated($source_path, $output_file)) { $log->info("Skipping '$source_path' -> '$output_file' (target up-to-date)"); next; } # do a simple copy if (!defined $template_params) { $log->info("Copying '$source_path' -> '$output_file'"); Mojo::File->new($source_path)->copy_to($output_file); next; } # render template $log->info("Rendering '$source_path' -> '$output_file'"); my $controller = $mojolicious->build_controller; $controller->stash($template_stash_variables) if defined $template_stash_variables; my $output = $controller->render_to_string(%$template_params); $log->debug($output); $output_file->spew($output, 'UTF-8'); }