
| Current Path : /var/www/web-klick.de/dsh/10_customer2017/1204__intel/ |
Linux ift1.ift-informatik.de 5.4.0-216-generic #236-Ubuntu SMP Fri Apr 11 19:53:21 UTC 2025 x86_64 |
| Current File : /var/www/web-klick.de/dsh/10_customer2017/1204__intel/CSgen.pm |
package CSgen;
use strict;
use Params::Validate qw(:all);
use CCAPI;
our $VERSION = "0.7";
# Attributes:
# on integ branch: BASELINE_COUNTER = S4G_(\d+)
# on task branch: BASE_ACS = "string" - path of the ACS
sub new
{
my $class = shift;
my $this = {};
bless $this, $class;
$this->initialize(@_);
return $this;
}
sub initialize
{
my $this = shift;
my %args = validate(@_, {
ACS => { type => HASHREF },
CND => { type => HASHREF },
VERBOSITY => { type => SCALAR, default => 0 },
TASK_BRANCH => { type => SCALAR|UNDEF, optional => 1 },
INTEGRATE => { type => SCALAR|UNDEF, default => '' },
TIMESTAMP => { type => SCALAR, optional => 1 },
INCLUDE_FILE => { type => SCALAR|UNDEF, optional => 1 },
INCLUDE_SELECTOR => { type => SCALAR|UNDEF, optional => 1 },
LOADRULES => { type => ARRAYREF, optional => 1 },
LOG_INFO => { type => CODEREF, optional => 1 },
LOG_WARN => { type => CODEREF, optional => 1 }
});
# API check
if($args{INTEGRATE} && $args{TASK_BRANCH}) {
die "[FATAL] generate_CS() cannot use INTEGRATE and TASK_BRANCH at the same time.\n";
}
@$this{keys %args} = values %args;
$this->{LOG_INFO} ||= sub { print @_ };
$this->{LOG_WARN} ||= sub { warn @_ };
return $this;
}
sub log_info
{
my ($this, $verbose, @args) = @_;
$this->{LOG_INFO}->(@args) if $verbose <= $this->{VERBOSITY};
}
sub log_warn
{
my ($this, $verbose, @args) = @_;
$this->{LOG_WARN}->(@args) if $verbose <= $this->{VERBOSITY};
}
sub config
{
my $this = shift;
my %args = @_;
foreach(keys %args) {
my $val = $args{$_};
if(defined $val) {
$this->{$_} = $val;
} else {
delete $this->{$_};
}
}
1;
}
sub generate_CS
{
my $this = shift;
my %args = validate(@_, {
COMMENTS => { type => ARRAYREF, default => [] }
});
# API check
# from the ACS
my $cnd_rev_selector = $this->{ACS}->getCnDSelector;
my %CxD = $this->{ACS}->getComponents;
my %Vobs = $this->{ACS}->getVobs;
# from the component and domain definitions
my %Domains = $this->{CND}->getDomains;
my %Components = $this->{CND}->getComponents;
my $cnd_rev_raw = $this->{CND}->getRawRevision;
# merge VOB list
my %vob_seen;
my @vobs = map { $this->get_VOB_tag(PATH => $_) } keys %Vobs;
$this->{VOBS} = \@vobs;
my @cs;
my %branches;
Component:
foreach my $comp (sort keys %CxD) {
my %dom_defs = %{$CxD{$comp}};
unless($Components{$comp}) {
$this->log_warn(0, "[WARNING] No component definition for '$comp' found, skipping it.\n");
next Component;
}
my @c_paths = @{$Components{$comp}};
Domain:
foreach my $dom (sort keys %dom_defs) {
unless($Domains{$dom}) {
$this->log_warn(0, "[WARNING] No domain definition for '$dom' found, skipping it.\n");
next Domain;
}
my @d_paths = @{$Domains{$dom}};
# calculate the selector for the component/domain combination:
my $selector = $dom_defs{$dom};
my @cs_sel = $this->get_expanded_selector(SELECTOR => $selector);
unless(@cs_sel) {
$this->log_warn(0, "[ERROR] Illegal selector '$selector' for $comp x $dom, skipping it.\n");
next Domain;
}
Component_Path:
foreach my $c_path (@c_paths) {
unless($c_path =~ m{^//(.*?)/*$}) {
$this->log_warn(0, "[ERROR] Component definition for '$comp' must start with //vob-tag; it is however '$c_path', so skipping it.\n");
next Component_Path;
}
my $c_path_stripped = $1;
Domain_Path:
foreach my $d_path (@d_paths) {
unless($d_path =~ m{^//(.*?)/*$}) {
$this->log_warn(0, "[ERROR] Domain definition for '$dom' must start with // - it is however '$d_path', so skipping it.\n");
next Domain_Path;
}
my @d_chunks = split(/\/+/, $1);
my @c_chunks = split(/\/+/, $c_path_stripped);
# now we have everything
my $res = '/';
while(@c_chunks || @d_chunks) {
unless(@c_chunks) {
$res .= '/' . shift(@d_chunks);
next;
}
unless(@d_chunks) {
$res .= '/' . shift(@c_chunks);
next;
}
my $c_chunk = shift(@c_chunks);
my $d_chunk = shift(@d_chunks);
if($c_chunk eq $d_chunk || $d_chunk eq '*') {
$res .= '/' . $c_chunk;
next;
}
elsif($c_chunk eq '*') {
$res .= '/' . $c_chunk;
next;
}
else {
$this->log_warn(1, "[WARNING] Unsupported path pattern combination C($c_path) x D($d_path), skipping it.\n");
next Domain_Path;
}
} # end path chunks
# we have the path, now for the selector
push(@cs, [ $res, @cs_sel ]);
} # end domain paths
} # end component paths
} # end domain definitions
} # end component definitions
# fallback: all VOBs
foreach my $vob (keys %Vobs) {
my @cs_sel = $this->get_expanded_selector(SELECTOR => $Vobs{$vob});
push(@cs, [$vob, @cs_sel]);
}
# include file
my $include = '';
if($this->{INCLUDE_FILE} && $this->{INCLUDE_SELECTOR}) {
my $inc_file = $this->get_VOB_tag(PATH => $this->{INCLUDE_FILE});
# environment variable expansion
$inc_file =~ s/\${([^}]+)}/$ENV{$1}/g;
$inc_file =~ s/\$(\w+)/$ENV{$1}/g;
$inc_file =~ s#/+#\\#g if $^O =~ /win/i;
# get the selector
my @sel = $this->get_expanded_selector(SELECTOR => $this->{INCLUDE_SELECTOR});
foreach(@sel) {
s/\s*-mkbranch.*//g; # don't want this
}
$this->log_info(0,"[INFO] using include file $inc_file and selector '@sel'\n");
$include = $this->_CCAPI->get_config_spec_include(PATH => $inc_file,
SELECTORS => [ @sel, '/main/LATEST' ]);
} # end include
# construct the config spec
my @expanded_cs = (
"# generated ".localtime()." by $0 v$::VERSION (CSgen.pm v$VERSION)",
@{$args{COMMENTS}},
'',
'element * CHECKEDOUT'
);
# include - if any
push(@expanded_cs, $include) if $include;
# consider the task branch
if($this->{TASK_BRANCH}) {
push(@expanded_cs,
"# task branch: $this->{TASK_BRANCH}",
"element * .../$this->{TASK_BRANCH}/LATEST",
''
);
}
# now for the component x domain and VOB selectors
foreach(sort { $b->[0] cmp $a->[0] } @cs) {
my ($path, @vs) = @$_;
my $vpath = $this->get_VOB_tag(PATH => $path);
push(@expanded_cs, map { "element $vpath/... $_" } @vs);
}
# ... and the final fallback for new elements
push(@expanded_cs, $this->{TASK_BRANCH} ?
("element * /main/0 -mkbranch $this->{TASK_BRANCH}") :
(map { "element * $_" } $this->get_expanded_selector(SELECTOR => 'main.NULL')));
# load rules for snapshot views
if($this->{LOADRULES}) { # custom load rules
my @rules = @{$this->{LOADRULES}};
if(@rules) {
# we have custom rules
push(@expanded_cs, "# custom load rules",
map { "load " . $this->get_VOB_tag(PATH => $_) } @rules);
} else {
# load all VOBs
foreach my $vob (@vobs) {
push(@expanded_cs, "# default load rules - all VOBs",
"load $vob");
}
}
}
# slashes to backslashes on Windows
if($^O =~ /win/i) {
foreach(@expanded_cs) {
s{/}{\\}g;
}
}
return @expanded_cs;
}
# this must be callable as static method
# we might check for VOB existence here
sub get_VOB_tag
{
my $this = shift;
my %args = validate(@_, {
PATH => { type => SCALAR }
});
my $path = $args{PATH};
if($^O =~ /win/i) {
$path =~ s{^//([^/]+)}{\\$1};
} else {
$path =~ s{^//([^/]+)}{/vobs/$1};
}
return $path;
}
sub _CCAPI
{
my $this = shift;
return $this->{_ccapi} ||= CCAPI->new(VOBS => $this->{VOBS},
LOG_INFO => $this->{LOG_INFO},
LOG_WARN => $this->{LOG_WARN},
VERBOSITY => $this->{VERBOSITY});
}
sub get_VOBs
{
my $this = shift;
return unless $this->{VOBS};
return @{$this->{VOBS}};
}
# now returns list!
sub get_branch_foundation
{
my $this = shift;
my %args = validate(@_, {
BRANCH => { type => SCALAR }
});
if(defined $this->{_branch_root_cache}->{$args{BRANCH}}) {
return @{$this->{_branch_root_cache}->{$args{BRANCH}}};
}
my $CCAPI = $this->_CCAPI;
my $brl = $CCAPI->get_branch_root_label(BRANCH => $args{BRANCH});
my @labels = $this->get_baseline_labels(BASELINE => $brl);
$this->{_branch_root_cache}->{$args{BRANCH}} = \@labels;
return @labels;
#return qw(MAIN_1); # MOCK_UP
}
sub get_recommended_baseline
{
my $this = shift;
my %args = validate(@_, {
BRANCH => { type => SCALAR }
});
return $this->{_branch_recommended_cache}->{$args{BRANCH}}
if defined $this->{_branch_recommended_cache}->{$args{BRANCH}};
my $CCAPI = $this->_CCAPI;
return $this->{_branch_recommended_cache}->{$args{BRANCH}} =
$CCAPI->get_recommended_baseline(BRANCH => $args{BRANCH});
}
sub get_baseline_labels
{
my $this = shift;
my %args = validate(@_, {
BASELINE => { type => SCALAR }
});
if($this->{_baseline_label_cache}->{$args{BASELINE}}) {
return @{$this->{_baseline_label_cache}->{$args{BASELINE}}};
}
my $CCAPI = $this->_CCAPI;
my @labels = $CCAPI->get_baseline_labels(%args);
$this->{_baseline_label_cache}->{$args{BASELINE}} = \@labels;
return @labels;
}
sub get_expanded_selector
{
my $this = shift;
my %args = validate(@_, {
SELECTOR => { type => SCALAR },
});
# process the selector; branch is here the integration branch!
my ($branch, $version, $opt_s) = $args{SELECTOR} =~ m{^(.+)\.([^./]+)(/.+|)$};
unless($branch && $version) {
$this->log_warn(0, "[ERROR] Illegal selector '$args{SELECTOR}'\n");
return;
}
# parse options
my %opts;
while($opt_s =~ m{/(\w+)}g) {
$opts{lc($1)}++;
}
my @cs_sel;
my $head_label;
# NONE - do not display this all
if($version eq 'NONE') {
@cs_sel = qw(-none);
}
# NULL - version /main/0, which is empty by definition
elsif($version eq 'NULL') {
@cs_sel =
($this->{INTEGRATE} && $this->{INTEGRATE} ne 'main')
? ("/main/0 -mkbranch ".$this->{INTEGRATE})
: $opts{ro}
? qw(/main/0 -nocheckout)
: qw(/main/0);
}
# LATEST - show the latest version on the branch
elsif($version eq 'LATEST') {
if($branch eq 'main') {
@cs_sel = (
'/main/LATEST' . # branch rule
($opts{freeze} || $this->{INTEGRATE} ? " -time ".$this->get_timestamp() : '').
($opts{ro} ? ' -nocheckout' : '')
);
} else {
# the integration branch foundation must be a full or incremental baseline!
# TODO here we could also read the (task branch) ACS
my @brls = $this->get_branch_foundation(BRANCH => $branch);
if(@brls) {
@cs_sel = (
# branch rule:
".../$branch/LATEST" .
($opts{freeze} || $this->{INTEGRATE} ? " -time ".$this->get_timestamp() : '').
($opts{ro} ? ' -nocheckout' : ''),
# branch baseline rule:
map { "$_ -mkbranch $branch" . ($opts{ro} ? ' -nocheckout' : '') }
@brls
);
} else {
$this->log_warn(0, "[ERROR] Will skip '$branch.LATEST' rule.\n");
}
}
}
# HEAD - the latest baseline on the branch
elsif($version eq 'HEAD') {
$head_label = $this->get_recommended_baseline(BRANCH => $branch);
unless($head_label) {
$this->log_warn(0, "[ERROR] No HEAD baseline label defined on branch '$branch'.\n");
return;
}
goto BaselineLabel;
}
# a label name
# TODO use "-nocheckout" here?
elsif($version =~ /^\w+$/) {
$head_label = $version;
BaselineLabel:
my @labels = $this->get_baseline_labels(BASELINE => $head_label);
my $full = pop @labels; # last must be a full baseline
if($this->{INTEGRATE}) {
if($branch eq 'main') {
@cs_sel = (
# first, the same baseline labels as task branch, but w/o mkbranch
# ...incremental ones, then full:
@labels,
$full,
# then any branch instance, which may not yet been labeled
"/main/LATEST -time ".$this->get_timestamp(),
# fallback for newly created elements
"/main/LATEST"
);
} else { # not main branch
@cs_sel = (
# first, the same baseline labels as task branch, but w/o mkbranch
# ...incremental ones:
@labels,
# ... and the full one; consider first instances on the branch:
".../$branch/$full",
# in between any branch instance, which may not yet been labeled
# important for creation of new branch instances (see -mkbranch below)
".../$branch/LATEST -time ".$this->get_timestamp(),
# fallback for newly created elements
".../$branch/LATEST",
# ... again the fully labeled baseline, which may be attached to elements
# on any parent branch:
"$full -mkbranch $branch",
# ... and finally the branch foundation, with mkbranch for the integ branch
# this must be a full or incremental baseline for now (no ACS)
map { "$_ -mkbranch $branch" } $this->get_branch_foundation(BRANCH => $branch)
);
}
}
elsif($this->{TASK_BRANCH}) {
@cs_sel = map { "$_ -mkbranch $this->{TASK_BRANCH}" .
($opts{ro} ? ' -nocheckout' : '') } (@labels, $full);
}
else {
@cs_sel = map { $_ . ($opts{ro} ? ' -nocheckout' : '') } (@labels, $full);
}
}
else {
$this->log_warn(0, "[ERROR] Illegal version definition '$version'\n");
return;
}
return @cs_sel;
}
my @months = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
sub time_to_cstime
{
my ($this, $mytime) = @_;
# 0 1 2 3 4 5 6 7 8
#($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)
my @t = localtime($mytime);
return $t[3].'-'.$months[$t[4]].'-'.($t[5]+1900).'.'. # date
sprintf("%02d:%02d:%02d", $t[2], $t[1], $t[0]); # time
}
sub get_timestamp
{
my $this = shift;
unless($this->{TIMESTAMP}) {
$this->{TIMESTAMP} = $this->time_to_cstime(time());
}
return $this->{TIMESTAMP};
}
1;