From 1805a95195a4ade2c07528e2f12a10234bb3c94f Mon Sep 17 00:00:00 2001 From: Michael Pyne Date: Sun, 11 Aug 2013 20:20:20 -0400 Subject: [PATCH] kde-projects: Add support for "Logical Branch Groups". We need a better name for this feature. But basically, this allows for creating kde-project repository-wide "branch groups". For instance, you could list the git branch to use for the "stable Qt/KDE 4" release/maintenance branch, the "new development Qt/KDE 4" development branch (typically master, but that has been changing), and the "upcoming next major release" branch (for us, this means 'frameworks' sometimes, means 'master' sometimes, other modules may have different ideas). By using the 'branch-group' option (which remains to be further documented) you can say that you want to defer to the metadata about the repository branches stored in kde-build-metadata/logical-module-structure. kdesrc-build should even track branches for you as they are updated by the Release Team, if you simply want to hang out on stable but compile your own stuff. Note that this support requires Perl's JSON module (which should come with Perl 5.14 if I understand it right, but you probably want to install JSON::XS from CPAN). JSON support is only required for this feature, it is not a new overall dependency. See also: http://community.kde.org/Infrastructure/Project_Metadata --- CMakeLists.txt | 4 + modules/ksb/BuildContext.pm | 25 +++++++ modules/ksb/Module.pm | 13 ++++ modules/ksb/Module/BranchGroupResolver.pm | 89 +++++++++++++++++++++++ modules/ksb/Updater/Git.pm | 2 - modules/ksb/Updater/KDEProject.pm | 27 +++++++ modules/ksb/Updater/KDEProjectMetadata.pm | 26 ++++++- 7 files changed, 183 insertions(+), 3 deletions(-) create mode 100644 modules/ksb/Module/BranchGroupResolver.pm diff --git a/CMakeLists.txt b/CMakeLists.txt index b90224a..6800a74 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,6 +42,10 @@ if (KDESRC_BUILD_INSTALL_MODULES) modules/ksb/BuildSystem/Qt4.pm DESTINATION ${KDESRC_BUILD_MODULE_INSTALL_PREFIX}/ksb/BuildSystem) + install(FILES + modules/ksb/Module/BranchGroupResolver.pm + DESTINATION ${KDESRC_BUILD_MODULE_INSTALL_PREFIX}/ksb/Module) + install(FILES modules/ksb/ModuleSet/KDEProjects.pm modules/ksb/ModuleSet/Null.pm diff --git a/modules/ksb/BuildContext.pm b/modules/ksb/BuildContext.pm index 20c1ce1..8c16234 100644 --- a/modules/ksb/BuildContext.pm +++ b/modules/ksb/BuildContext.pm @@ -22,6 +22,7 @@ use ksb::Debug; use ksb::Util; use ksb::PhaseList; use ksb::Module; +use ksb::Module::BranchGroupResolver; use ksb::Updater::KDEProjectMetadata; use ksb::Version qw(scriptVersion); use File::Temp qw(tempfile); @@ -44,6 +45,7 @@ my %defaultGlobalOptions = ( "async" => 1, "binpath" => '', "branch" => "", + "branch-group" => "", # Overrides branch, uses JSON data. "build-dir" => "build", "build-system-only" => "", "build-when-unchanged" => 1, # Safe default @@ -137,6 +139,7 @@ sub new ignore_list => [ ], # List of XML paths to ignore completely. kde_projects_filehandle => undef, # Filehandle to read database from. kde_projects_metadata => undef, # See ksb::Module::KDEProjects + logical_module_resolver => undef, # For branch-group option. ); # Merge all new options into our self-hash. @@ -943,4 +946,26 @@ sub setKDEProjectMetadataModule return; } +# Returns a ksb::Module::BranchGroupResolver which can be used to efficiently +# determine a git branch to use for a given kde-projects module (when the +# branch-group option is in use), as specified at +# http://community.kde.org/Infrastructure/Project_Metadata. +sub moduleBranchGroupResolver +{ + my $self = shift; + + if (!$self->{logical_module_resolver}) { + my $metadataModule = $self->getKDEProjectMetadataModule(); + + croak_internal("Tried to use branch-group, but needed data wasn't loaded!") + unless $metadataModule; + + my $resolver = ksb::Module::BranchGroupResolver->new( + $metadataModule->scm()->logicalModuleGroups()); + $self->{logical_module_resolver} = $resolver; + } + + return $self->{logical_module_resolver}; +} + 1; diff --git a/modules/ksb/Module.pm b/modules/ksb/Module.pm index 4b2c551..ce43857 100644 --- a/modules/ksb/Module.pm +++ b/modules/ksb/Module.pm @@ -934,6 +934,19 @@ sub fullpath return $pathinfo{'fullpath'}; } +# Returns the "full kde-projects path" for the module. As should be obvious by +# the description, this only works for modules with an scm type that is a +# Updater::KDEProject (or its subclasses). +sub fullProjectPath +{ + my $self = shift; + my $path = $self->getOption('#xml-full-path', 'module') || + croak_internal("Tried to ask for full path of a module $_ that doesn't have one!"); + + return $path; +} + + # Subroutine to return the name of the destination directory for the # checkout and build routines. Based on the dest-dir option. The return # value will be relative to the src/build dir. The user may use the diff --git a/modules/ksb/Module/BranchGroupResolver.pm b/modules/ksb/Module/BranchGroupResolver.pm new file mode 100644 index 0000000..5587be7 --- /dev/null +++ b/modules/ksb/Module/BranchGroupResolver.pm @@ -0,0 +1,89 @@ +package ksb::Module::BranchGroupResolver; + +# This provides an object that can be used to lookup the appropriate git branch +# to use for a given KDE project module and given desired logical branch group, using +# supplied JSON data (from kde-build-metadata). +# +# See also http://community.kde.org/Infrastructure/Project_Metadata + +use strict; +use warnings; +use v5.10; + +our $VERSION = '0.10'; + +use List::Util qw(first); + +sub new +{ + my ($class, $jsonData) = @_; + + my $self = { }; + my @keys = qw/layers groups/; + + # Copy just the objects we want over. + @{$self}{@keys} = @{$jsonData}{@keys}; + + # Extract wildcarded groups separately as they are handled separately + # later. Note that the specific catch-all group '*' is itself handled + # as a special case in findModuleBranch. This is important so that + # findModuleBranch can assume all these groups have at least '/*'. + + $self->{wildcardedGroups} = { + map { ($_, $self->{groups}->{$_}) } + grep { substr($_,-2) eq '/*' } + keys %{$self->{groups}} + }; + + bless $self, $class; + return $self; +} + +# Returns the branch for the given logical group and module specifier. This +# function should not be called if the module specifier does not actually +# exist. +sub _findLogicalGroup +{ + my ($self, $module, $logicalGroup) = @_; + + # Using defined-or and still returning undef is on purpose, silences + # warning about use of undefined value. + return $self->{groups}->{$module}->{$logicalGroup} // undef; +} + +sub findModuleBranch +{ + my ($self, $module, $logicalGroup) = @_; + + if (exists $self->{groups}->{$module}) { + return $self->_findLogicalGroup($module, $logicalGroup); + } + + my %catchAllGroupStats = map { + # Map module search spec to prefix string that is required for a match + $_ => substr($_, 0, rindex ($_, '/') + 1) + } keys %{$self->{wildcardedGroups}}; + + # Sort longest required-prefix to the top... first match that is valid will + # then also be the right match. + my @orderedCandidates = sort { + $catchAllGroupStats{$b} cmp $catchAllGroupStats{$a} + } keys %catchAllGroupStats; + + my $match = first { + substr($module, 0, length $catchAllGroupStats{$_}) eq + $catchAllGroupStats{$_} + } @orderedCandidates; + + if ($match) { + return $self->_findLogicalGroup($match, $logicalGroup); + } + + if (exists $self->{groups}->{'*'}) { + return $self->_findLogicalGroup('*', $logicalGroup); + } + + return; +} + +1; diff --git a/modules/ksb/Updater/Git.pm b/modules/ksb/Updater/Git.pm index cf2302b..b27284b 100644 --- a/modules/ksb/Updater/Git.pm +++ b/modules/ksb/Updater/Git.pm @@ -380,8 +380,6 @@ sub updateExistingClone # Returns the user-selected branch for the given module, or 'master' if no # branch was selected. -# -# First parameter is the module name. sub getBranch { my $self = assert_isa(shift, 'ksb::Updater::Git'); diff --git a/modules/ksb/Updater/KDEProject.pm b/modules/ksb/Updater/KDEProject.pm index 63c67b3..1868c94 100644 --- a/modules/ksb/Updater/KDEProject.pm +++ b/modules/ksb/Updater/KDEProject.pm @@ -12,9 +12,36 @@ our $VERSION = '0.10'; use ksb::Updater::Git; our @ISA = qw(ksb::Updater::Git); +use ksb::Debug; + sub name { return 'proj'; } +# Overrides ksb::Updater::Git's version to return the right branch based off +# a logical branch-group, if one is set. +sub getBranch +{ + my $self = shift; + my $module = $self->module(); + my $branchGroup = $module->getOption('branch-group'); + + return $self->SUPER::getBranch() if !$branchGroup; + + # If we're using a logical group we need to query the global build context + # to resolve it. + my $ctx = $module->buildContext(); + my $resolver = $ctx->moduleBranchGroupResolver(); + my $modulePath = $module->fullProjectPath(); + my $branch = $resolver->findModuleBranch($modulePath, $branchGroup); + + if (!$branch) { + whisper ("No specific branch set for $modulePath and $branchGroup, using b[master]"); + $branch = 'master'; + } + + return $branch; +} + 1; diff --git a/modules/ksb/Updater/KDEProjectMetadata.pm b/modules/ksb/Updater/KDEProjectMetadata.pm index 6b9ed2f..20d5774 100644 --- a/modules/ksb/Updater/KDEProjectMetadata.pm +++ b/modules/ksb/Updater/KDEProjectMetadata.pm @@ -7,7 +7,7 @@ use strict; use warnings; use v5.10; -our $VERSION = '0.10'; +our $VERSION = '0.20'; use ksb::Util; use ksb::Debug; @@ -15,6 +15,8 @@ use ksb::Updater::KDEProject; our @ISA = qw(ksb::Updater::KDEProject); +my $haveJson = eval { require JSON; JSON->import(); 1; }; + sub name { return 'metadata'; @@ -41,4 +43,26 @@ sub ignoredModules return @ignoreModules; } +# If JSON support is present, and the metadata has already been downloaded +# (e.g. with ->updateInternal), returns a hashref to the logical module group +# data contained within the kde-build-metadata, decoded from its JSON format. +# See http://community.kde.org/Infrastructure/Project_Metadata +sub logicalModuleGroups +{ + my $self = shift; + my $path = $self->module()->fullpath('source') . "/logical-module-structure"; + + croak_runtime("Logical module groups require the Perl JSON module") + unless $haveJson; + + my $fh = pretend_open($path) or + croak_internal("Unable to read logical module structure: $!"); + + # The 'local $/' disables line-by-line reading; slurps the whole file + my $json_hashref = do { local $/; decode_json(<$fh>); }; + close $fh; + + return $json_hashref; +} + 1;