office-gobmx/solenv/bin/build.pl
2004-12-03 15:42:11 +00:00

2142 lines
68 KiB
Perl
Raw Blame History

:
eval 'exec perl -S $0 ${1+"$@"}'
if 0;
#*************************************************************************
#
# $RCSfile: build.pl,v $
#
# $Revision: 1.129 $
#
# last change: $Author: vg $ $Date: 2004-12-03 16:42:11 $
#
# The Contents of this file are made available subject to the terms of
# either of the following licenses
#
# - GNU Lesser General Public License Version 2.1
# - Sun Industry Standards Source License Version 1.1
#
# Sun Microsystems Inc., October, 2000
#
# GNU Lesser General Public License Version 2.1
# =============================================
# Copyright 2000 by Sun Microsystems, Inc.
# 901 San Antonio Road, Palo Alto, CA 94303, USA
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License version 2.1, as published by the Free Software Foundation.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
# MA 02111-1307 USA
#
#
# Sun Industry Standards Source License Version 1.1
# =================================================
# The contents of this file are subject to the Sun Industry Standards
# Source License Version 1.1 (the "License"); You may not use this file
# except in compliance with the License. You may obtain a copy of the
# License at http://www.openoffice.org/license.html.
#
# Software provided under this License is provided on an "AS IS" basis,
# WITHOUT WARRUNTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING,
# WITHOUT LIMITATION, WARRUNTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
# MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
# See the License for the specific provisions governing your rights and
# obligations concerning the Software.
#
# The Initial Developer of the Original Code is: Sun Microsystems, Inc..
#
# Copyright: 2000 by Sun Microsystems, Inc.
#
# All Rights Reserved.
#
# Contributor(s): _______________________________________
#
#
#
#*************************************************************************
#
# build - build entire project
#
use Config;
use POSIX;
use Cwd qw (cwd);
use File::Path;
use lib ("$ENV{SOLARENV}/bin/modules");
if (defined $ENV{COMMON_ENV_TOOLS}) {
unshift(@INC, "$ENV{COMMON_ENV_TOOLS}/modules");
require CopyPrj; import CopyPrj;
};
my $log = undef;
if (defined $ENV{CWS_WORK_STAMP}) {
require Cws; import Cws;
require CwsConfig; import CwsConfig;
require CvsModule; import CvsModule;
require GenInfoParser; import GenInfoParser;
require IO::Handle; import IO::Handle;
eval { require Logging; import Logging; };
$log = Logging->new() if (!$@);
};
my $enable_multiprocessing = 1;
if ($ENV{GUI} eq 'WNT') {
eval { require Win32::Process; import Win32::Process; };
$enable_multiprocessing = 0 if ($@);
};
### for XML file format
#use lib ("/home/vg119683/work/modules");
eval { require XMLBuildListParser; import XMLBuildListParser; };
$enable_xml = 1 if (!$@);
#### script id #####
( $script_name = $0 ) =~ s/^.*\b(\w+)\.pl$/$1/;
$id_str = ' $Revision: 1.129 $ ';
$id_str =~ /Revision:\s+(\S+)\s+\$/
? ($script_rev = $1) : ($script_rev = "-");
print "$script_name -- version: $script_rev\n";
#########################
# #
# Globale Variablen #
# #
#########################
if (defined $ENV{CWS_WORK_STAMP}) {
$vcsid = CwsConfig->new()->vcsid();
};
$modules_number++;
$perl = "";
$remove_commando = "";
if ( $^O eq 'MSWin32' ) {
$perl = "$ENV{COMSPEC} -c perl5";
$remove_commando = "rmdir /S /Q";
$nul = '> NULL';
} else {
use Cwd 'chdir';
$perl = 'perl';
$remove_commando = 'rm -rf';
$nul = '> /dev/null';
};
$QuantityToBuild = 0;
# delete $pid when not needed
%projects_deps_hash = (); # hash of projects with no dependencies,
# that could be built now
%broken_build = (); # hash of hashes of the modules,
# where build was broken (error occurred)
%folders_hashes = ();
%running_children = ();
$dependencies_hash = 0;
$cmd_file = '';
$BuildAllParents = 0;
$show = 0;
$deliver = 0;
%LocalDepsHash = ();
%BuildQueue = ();
%PathHash = ();
%PlatformHash = ();
%AliveDependencies = ();
%global_deps_hash = (); # hash of dependencies of the all modules
%broken_modules_hashes = (); # hash of modules hashes, which cannot be built further
@broken_modules_names = (); # array of modules, which cannot be built further
@dmake_args = ();
%dead_parents = ();
$CurrentPrj = '';
$no_projects = 0;
$only_dependent = 0;
$build_from = '';
$build_from_opt = '';
$build_since = '';
$dlv_switch = '';
$child = 0;
%processes_hash = ();
%module_announced = ();
$prepare = ''; # prepare for following incompatible build
$ignore = '';
@ignored_errors = ();
%incompatibles = ();
%force_deliver = ();
$only_platform = ''; # the only platform to prepare
$only_common = ''; # the only common output tree to delete when preparing
%build_modes = ();
$maximal_processes = 0; # the max number of the processes run
%modules_types = (); # modules types ('mod', 'img', 'lnk') hash
%platforms = (); # platforms available or being working with
%platforms_to_copy = (); # copy output trees for the platforms when --prepare
$tmp_dir = get_tmp_dir(); # temp directory for checkout
# $dmake_batch = undef; #
@possible_build_lists = ('build.lst', 'build.xlist'); # build lists names
%build_lists_hash = (); # hash of arrays $build_lists_hash{$module} = \($path, $xml_list_object)
$pre_job = ' announce'; # job to add for not-single module build
$post_job = ' deliver'; # -"-
%windows_procs = ();
### main ###
&get_options;
&get_build_modes;
%deliver_env = ();
if ($prepare) {
&get_platforms(\%platforms);
@modules_built = ();
$deliver_env{'BUILD_SOSL'}++;
$deliver_env{'COMMON_OUTDIR'}++;
$deliver_env{'DLLSUFFIX'}++;
$deliver_env{'GUI'}++;
$deliver_env{'INPATH'}++;
$deliver_env{'OFFENV_PATH'}++;
$deliver_env{'OUTPATH'}++;
$deliver_env{'L10N_framework'}++;
};
$StandDir = &get_stand_dir();
&provide_consistency if (defined $ENV{CWS_WORK_STAMP} && defined($log));
$deliver_commando = $ENV{DELIVER};
$deliver_commando .= ' '. $dlv_switch if ($dlv_switch);
$ENV{mk_tmp}++;
%prj_platform = ();
$check_error_string = '';
$dmake = '';
# $dmake_bin = '';
$dmake_args = '';
$echo = '';
$new_line = "\n";
&get_commands();
unlink ($cmd_file);
if ($cmd_file) {
if (open (CMD_FILE, ">>$cmd_file")) {
select CMD_FILE;
$echo = 'echo ';
$new_line = $echo."\"\"\n";
print "\@$echo off\npushd\n" if ($ENV{GUI} ne 'UNX');
} else {
&print_error ("Cannot open file $cmd_file");
};
} elsif ($show) {
select STDERR;
};
print $new_line;
&BuildAll();
cancel_build() if (scalar keys %broken_build);
@TotenEltern = keys %dead_parents;
if ($#TotenEltern != -1) {
my ($DeadPrj);
print $new_line.$new_line;
print $echo."WARNING! Project(s):\n";
foreach $DeadPrj (@TotenEltern) {
print $echo."$DeadPrj\n";
};
print $new_line;
print $echo."not found and couldn't be built. Maybe you should correct build lists.\n";
print $new_line;
};
if (($ENV{GUI} ne 'UNX') && $cmd_file) {
print "popd\n";
};
$ENV{mk_tmp} = '';
if ($cmd_file) {
close CMD_FILE;
print STDOUT "Script $cmd_file generated\n";
};
if ($ignore && scalar @ignored_errors) {
print STDERR "\nERROR: next directories could not be built:\n";
foreach (@ignored_errors) {
print STDERR "\t$_\n";
};
print STDERR "\nERROR: please check these directories and build the corresponding module(s) anew!!\n\n";
do_exit(1);
};
&finish_logging;
do_exit(0);
#########################
# #
# Procedures #
# #
#########################
#
# procedure retrieves build list path
# (all possibilities are taken into account)
#
sub get_build_list_path {
my $module = shift;
my @possible_dirs = ($module, $module. '.lnk');
foreach (@possible_dirs) {
my $possible_dir_path = $StandDir.$_.'/prj/';
if (-d $possible_dir_path) {
foreach (@possible_build_lists) {
my $possible_build_list_path = $possible_dir_path . $_;
return $possible_build_list_path if (-f $possible_build_list_path);
}
print_error("There's no build list for $module");
};
};
$dead_parents{$module}++;
return retrieve_build_list($module);
};
#
# Get dependencies hash of the current and all parent projects
#
sub GetParentDeps {
my (%parents_deps_hash, $module, $parent);
my $prj_dir = shift;
my $deps_hash = shift;
my @UnresolvedParents = get_parents_array($prj_dir);
$parents_deps_hash{$_}++ foreach (@UnresolvedParents);
$$deps_hash{$prj_dir} = \%parents_deps_hash;
while ($module = pop(@UnresolvedParents)) {
my %parents_deps_hash = ();
#my @parents_array = get_parents_array($module);
$parents_deps_hash{$_}++ foreach (get_parents_array($module));
$$deps_hash{$module} = \%parents_deps_hash;
foreach $Parent (keys %parents_deps_hash) {
#if ((!defined($$deps_hash{$Parent})) && (!defined($$deps_hash{$Parent . '.lnk'}))) {
if (!defined($$deps_hash{$Parent})) {
push (@UnresolvedParents, $Parent);
};
};
};
&check_deps_hash($deps_hash);
};
#
# Build everything that should be built
#
sub BuildAll {
if ($BuildAllParents) {
my ($Prj, $PrjDir, $orig_prj);
&GetParentDeps( $CurrentPrj, \%global_deps_hash);
modules_classify(keys %global_deps_hash);
&prepare_build_from(\%global_deps_hash) if ($build_from);
&prepare_incompatible_build(\%global_deps_hash) if ($incompatible);
if ($build_from_opt || $build_since) {
&prepare_build_from_opt(\%global_deps_hash);
};
$modules_number = scalar keys %global_deps_hash;
if ($QuantityToBuild) {
&build_multiprocessing;
return;
};
while ($Prj = &PickPrjToBuild(\%global_deps_hash)) {
if (!defined $dead_parents{$Prj}) {
if (!defined $module_announced{$Prj}) {
print $new_line;
if (scalar keys %broken_build) {
print $echo . "Skipping project $Prj because of error(s)\n";
&RemoveFromDependencies($Prj, \%global_deps_hash);
next;
};
&print_announce($Prj);
if ($modules_types{$Prj} eq 'mod') {
$PrjDir = &CorrectPath($StandDir.$Prj);
&mark_force_deliver($Prj, $PrjDir) if (defined $ENV{CWS_WORK_STAMP} && defined($log));
&get_deps_hash($Prj, \%LocalDepsHash);
&BuildDependent(\%LocalDepsHash);
my $deliver_commando = &get_deliver_commando($Prj);
if ($cmd_file) {
print "$deliver_commando\n";
} else {
system ("$deliver_commando") if (!$show && ($Prj ne $CurrentPrj) && !$deliver);
};
print $check_error_string;
};
};
};
&RemoveFromDependencies($Prj, \%global_deps_hash);
$no_projects = 0;
};
} else {
store_build_list_content($CurrentPrj);
&get_deps_hash($CurrentPrj, \%LocalDepsHash);
&BuildDependent(\%LocalDepsHash);
};
};
#
# Start build on a given project
#
sub dmake_dir {
my ($new_BuildDir, $OldBuildDir, $error_code);
my $BuildDir = shift;
# if ((!(-d $BuildDir)) && (defined $ENV{CWS_WORK_STAMP} && defined($log))) {
# $OldBuildDir = $BuildDir;
# my $modified_path = $PathHash{$folder_nick};
# $modified_path =~ s/^([^\\\/]+)/$1\.lnk/;
# $BuildDir = &CorrectPath($StandDir . $modified_path);
# };
# my $missing_dir;
# $missing_dir = $OldBuildDir if ($OldBuildDir);
# $missing_dir = $BuildDir if (!$missing_dir);
&print_error("$BuildDir not found!!\n") if (!-d $BuildDir);
if (!(-d $BuildDir)) {
$new_BuildDir = $BuildDir;
$new_BuildDir =~ s/_simple//g;
if ((-d $new_BuildDir)) {
print("\nTrying $new_BuildDir, $BuildDir not found!!\n");
$BuildDir = $new_BuildDir;
} else {
&print_error("\n$BuildDir not found!!\n");
}
}
if ($cmd_file) {
print "cd $BuildDir\n";
print $check_error_string;
print $echo.$BuildDir."\n";
print "$dmake\n";
print $check_error_string;
} else {
print "$BuildDir\n";
};
&RemoveFromDependencies($BuildDir, \%LocalDepsHash) if (!$child);
if (!$cmd_file && !$show) {
chdir $BuildDir;
cwd();
$error_code = system ("$dmake");
if ($error_code && $ignore) {
push(@ignored_errors, $BuildDir);
$error_code = 0;
};
};
if ($child) {
my $oldfh = select STDERR;
$| = 1;
select $oldfh;
$| =1;
_exit($? >> 8) if ($? && ($? != -1));
_exit(0);
} elsif ($error_code && ($error_code != -1)) {
&print_error("Error $? occurred while making $BuildDir");
};
};
#
# Procedure stores information about build list (and)
# build list object in build_lists_hash
#
sub store_build_list_content {
my $module = shift;
my $build_list_path = get_build_list_path($module);
return undef if (!defined $build_list_path);
return if (!$build_list_path);
my $xml_list = undef;
if ($build_list_path =~ /\.xlist$/o) {
print_error("XMLBuildListParser.pm couldn\'t be found, so XML format for build lists is not enabled") if (!defined $enable_xml);
$xml_list = XMLBuildListParser->new();
if (!$xml_list->loadXMLFile($build_list_path)) {
print_error("Cannot use $build_list_path");
};
$build_lists_hash{$module} = $xml_list;
} else {
if (open (BUILD_LST, $build_list_path)) {
my @build_lst = <BUILD_LST>;
$build_lists_hash{$module} = \@build_lst;
close BUILD_LST;
return;
}
$dead_parents{$module}++;
};
}
#
# Get string (list) of parent projects to build
#
sub get_parents_array {
my $module = shift;
store_build_list_content($module);
my $build_list_ref = $build_lists_hash{$module};
if (ref($build_list_ref) eq 'XMLBuildListParser') {
my @modes_array = split('\s' , $ENV{BUILD_TYPE});
return $build_list_ref->getModuleDependencies(\@modes_array);
};
foreach (@$build_list_ref) {
if ($_ =~ /#/) {
if ($`) {
$_ = $`;
} else {
next;
};
};
s/\r\n//;
if ($_ =~ /\:+\s+/) {
return pick_for_build_type($');
};
};
return ();
};
#
# get folders' platform infos
#
sub get_prj_platform {
my $build_list_ref = shift;
my ($prj_alias, $line);
foreach(@$build_list_ref) {
s/\r\n//;
$line++;
if ($_ =~ /nmake/) {
if ($' =~ /\s+-\s+(\w+)[,\S+]*\s+(\S+)/ ) {
my $platform = $1;
my $alias = $2;
&print_error ("There is no correct alias set in the line $line!") if ($alias eq 'NULL');
&mark_platform($alias, $platform);
} else {
&print_error("Misspelling in line: \n$_");
};
};
};
#seek(BUILD_LST, 0, 0);
};
#
# Procedure populate the dependencies hash with
# information from XML build list object
#
sub get_deps_from_object {
my ($module, $build_list_object, $dependencies_hash) = @_;
foreach my $dir ($build_list_object->getJobDirectories("make", $ENV{GUI})) {
$PathHash{$dir} = $StandDir . $module;
$PathHash{$dir} .= $dir if ($dir ne '/');
my %deps_hash = ();
foreach my $dep ($build_list_object->getJobDependencies($dir, "make", $ENV{GUI})) {
$deps_hash{$dep}++;
};
$$dependencies_hash{$dir} = \%deps_hash;
};
};
#
# Getting hashes of all internal dependencies and additional
# information for given project
#
sub get_deps_hash {
my ($dummy, $module_to_build, $module_path);
%DeadDependencies = ();
$module_to_build = shift;
$module_path = &CorrectPath($StandDir.$module_to_build);
my $dependencies_hash = shift;
chdir $module_path;
cwd();
if ($deliver) {
if ($cmd_file) {
print "$deliver_commando\n";
} else {
system ("$deliver_commando") if (!$show);
};
return;
};
my $build_list_ref = $build_lists_hash{$module_to_build};
delete $build_lists_hash{$module_to_build};
if (ref($build_list_ref) eq 'XMLBuildListParser') {
&get_deps_from_object($module_to_build, $build_list_ref, $dependencies_hash);
} else {
get_prj_platform($build_list_ref);
foreach (@$build_list_ref) {
if ($_ =~ /#/o) {
next if (!$`);
$_ = $`;
};
s/\r\n//;
if ($_ =~ /nmake/o) {
my ($Platform, $Dependencies, $Dir, $DirAlias);
my %deps_hash = ();
$Dependencies = $';
$dummy = $`;
$dummy =~ /(\S+)\s+(\S+)/o;
$Dir = $2;
$Dependencies =~ /(\w+)/o;
$Platform = $1;
$Dependencies = $';
while ($Dependencies =~ /,(\w+)/o) {
$Dependencies = $';
};
$Dependencies =~ /\s+(\S+)\s+/o;
$DirAlias = $1;
if (!&CheckPlatform($Platform)) {
$DeadDependencies{$DirAlias}++;
next;
};
$PlatformHash{$DirAlias}++;
$Dependencies = $';
&print_error("$module_to_build/prj/build.lst has wrongly written dependencies string:\n$_\n") if (!$Dependencies);
$deps_hash{$_}++ foreach (GetDependenciesArray($Dependencies));
$$dependencies_hash{$DirAlias} = \%deps_hash;
$BuildQueue{$DirAlias}++;
if ($Dir =~ /(\\|\/)/o) {
$Dir = $module_to_build . $1 . $';
} else {$Dir = $module_to_build;};
$PathHash{$DirAlias} = CorrectPath($StandDir . $Dir);
};
};
foreach my $alias (keys %DeadDependencies) {
next if defined $AliveDependencies{$alias};
if (!&IsHashNative($alias)) {
&RemoveFromDependencies($alias, $dependencies_hash);
delete $DeadDependencies{$alias};
};
};
};
check_deps_hash($dependencies_hash);
resolve_aliases($dependencies_hash, \%PathHash);
#add_pre_job($dependencies_hash);
#add_post_job($dependencies_hash) if ($module_to_build ne $CurrentPrj);
};
#
# procedure adds $pre_job to each module's dependancy hash
#
sub add_pre_job {
my $dependencies_hash = shift;
# $pre_job is independent while all other jobs are dependent from it
foreach (keys %$dependencies_hash) {
$deps_hash = $$dependencies_hash{$_};
$$deps_hash{$pre_job}++;
};
$$dependencies_hash{$pre_job} = {};
};
#
# procedure adds $post_job to each module's dependancy hash
#
sub add_post_job {
# $post_job is dependent from all jobs
my %deps_hash = ();
$deps_hash{$_}++ foreach (keys %$dependencies_hash);
$$dependencies_hash{$post_job} = \%deps_hash;
};
#
# this procedure converts aliases to absolute paths
#
sub resolve_aliases {
my ($dependencies_hash, $PathHash) = @_;
foreach my $dir_alias (keys %$dependencies_hash) {
my $aliases_hash_ref = $$dependencies_hash{$dir_alias};
my %paths_hash = ();
foreach (keys %$aliases_hash_ref) {
$paths_hash{$$PathHash{$_}}++;
};
delete $$dependencies_hash{$dir_alias};
$$dependencies_hash{$$PathHash{$dir_alias}} = \%paths_hash;
};
};
#
# mark platform in order to prove if alias has been used according to specs
#
sub mark_platform {
my $prj_alias = shift;
if (exists $prj_platform{$prj_alias}) {
$prj_platform{$prj_alias} = 'all';
} else {
$prj_platform{$prj_alias} = shift;
};
};
#
# Convert path from abstract (with '\' and/or '/' delimiters)
# to system-independent
#
sub CorrectPath {
$_ = shift;
if ( ($^O eq 'MSWin32') && (!defined $ENV{SHELL})) {
s/\//\\/g;
} else {;
s/\\/\//g;
};
return $_;
};
sub check_dmake {
#print "Checking dmake...";
# my $dmake_batch = CorrectPath("$tmp_dir/dmake.bat");
if ($QuantityToBuild && ($ENV{GUI} eq 'WNT') && ($ENV{USE_SHELL} eq '4nt')) {
if (open(DMAKEVERSION, "where dmake |")) {
my @output = <DMAKEVERSION>;
close DMAKEVERSION;
$dmake_bin = $output[0];
$dmake_bin =~ /(\b)$/;
$dmake_bin = $`;
};
return if (-e $dmake_bin);
} elsif (open(DMAKEVERSION, "dmake -V |")) {
# if (open(DMAKEVERSION, "dmake -V |")) {
my @dmake_version = <DMAKEVERSION>;
close DMAKEVERSION;
# if ($dmake_version[0] =~ /^dmake\s\-\sCopyright\s\(c\)/) {
# print " Using version $1\n" if ($dmake_version[0] =~ /Version\s(\d+\.*\d*)/);
# };
return;
};
my $error_message = 'dmake: Command not found.';
$error_message .= ' Please rerun bootstrap' if (!defined $log);
&print_error($error_message);
};
#
# Get platform-dependent commands
#
sub get_commands {
my $arg = '';
# Setting alias for dmake
$dmake = 'dmake';
&check_dmake;
if ($cmd_file) {
if ($ENV{GUI} eq 'UNX') {
$check_error_string = "if \"\$?\" != \"0\" exit\n";
} else {
$check_error_string = "if \"\%?\" != \"0\" quit\n";
};
};
$dmake_args = join(' ', 'dmake', @dmake_args);
while ($arg = pop(@dmake_args)) {
$dmake .= ' '.$arg;
};
# if (($ENV{GUI} eq 'WNT') && $QuantityToBuild) {
# print_error("There is no such executable $_4nt_exe") if (!-e $_4nt_exe);
# $dmake_batch = generate_4nt_batch();
# };
};
#
# Procedure prooves if current dir is a root dir of the drive
#
sub IsRootDir {
my ($Dir);
$Dir = shift;
if ( (($ENV{GUI} eq 'UNX') ||
($ENV{GUI} eq 'MACOSX')) &&
($Dir eq '/')) {
return 1;
} elsif ( (($ENV{GUI} eq 'WNT') ||
($ENV{GUI} eq 'WIN') ||
($ENV{GUI} eq 'OS2')) &&
($Dir =~ /\S:\/$/)) {
return 1;
} else {
return 0;
};
};
#
# Procedure retrieves list of projects to be built from build.lst
#
sub get_stand_dir {
if (!(defined $ENV{GUI})) {
$ENV{mk_tmp} = '';
die "No environment set\n";
};
my $StandDir;
do {
$StandDir = cwd();
foreach (@possible_build_lists) {# ('build.lst', 'build.xlist');
if (-e 'prj/'.$_) {
$StandDir =~ /([\.\w]+$)/;
$StandDir = $`;
$CurrentPrj = $1;
return $StandDir;
} elsif (&IsRootDir($StandDir)) {
$ENV{mk_tmp} = '';
&print_error ('Found no project to build');
};
}
}
while (chdir '..');
};
#
# Picks project which can be built now from hash and then deletes it from hash
#
sub PickPrjToBuild {
my $DepsHash = shift;
handle_dead_children() if ($QuantityToBuild);
my $Prj = &FindIndepPrj($DepsHash);
delete $$DepsHash{$Prj};
return $Prj;
};
#
# Make a decision if the project should be built on this platform
#
sub CheckPlatform {
my $Platform = shift;
return 1 if ($Platform eq 'all');
return 1 if (($ENV{GUI} eq 'WIN') && ($Platform eq 'w'));
return 1 if (($ENV{GUI} eq 'UNX') && ($Platform eq 'u'));
return 1 if (($ENV{GUI} eq 'MAC') && ($Platform eq 'm'));
return 1 if (($ENV{GUI} eq 'OS2') && ($Platform eq 'p'));
return 1 if (($ENV{GUI} eq 'WNT') &&
(($Platform eq 'w') || ($Platform eq 'n')));
return 0;
};
#
# Remove project to build ahead from dependencies and make an array
# of all from given project dependent projects
#
sub RemoveFromDependencies {
my ($ExclPrj, $i, $Prj, $Dependencies);
$ExclPrj = shift;
my $ExclPrj_orig = '';
$ExclPrj_orig = $` if ($ExclPrj =~ /\.lnk$/o);
$Dependencies = shift;
foreach $Prj (keys %$Dependencies) {
my $prj_deps_hash = $$Dependencies{$Prj};
delete $$prj_deps_hash{$ExclPrj} if defined $$prj_deps_hash{$ExclPrj};
# foreach (keys %$prj_deps_hash) {print ("$_ ")};
# foreach $i (0 .. $#{$$Dependencies{$Prj}}) {
# if ((${$$Dependencies{$Prj}}[$i] eq $ExclPrj) ||
# (${$$Dependencies{$Prj}}[$i] eq $ExclPrj_orig)) {
# splice (@{$$Dependencies{$Prj}}, $i, 1);
# $i = 0;
# last;
# };
# };
};
};
#
# Check the hash for consistency
#
sub check_deps_hash {
my $deps_hash_ref = shift;
return if (!scalar keys %$deps_hash_ref);
my %deps_hash = %$deps_hash_ref;
my $consistent;
foreach $key (keys %$deps_hash_ref) {
my %values_hash = %{$$deps_hash_ref{$key}};
$deps_hash{$key} = \%values_hash;
};
do {
$consistent = '';
foreach $key (keys %deps_hash) {
$local_deps_ref = $deps_hash{$key};
if (!scalar keys %$local_deps_ref) {
&RemoveFromDependencies($key, \%deps_hash);
delete $deps_hash{$key};
$consistent = 1;
};
};
} while ($consistent && (scalar keys %deps_hash));
return if ($consistent);
print STDERR "Fatal error:";
foreach (keys %deps_hash) {
print STDERR "\n\t$_ depends on: ";
foreach my $i (keys %{$deps_hash{$_}}) {
print STDERR (' ', $i);
};
};
if ($child) {
my $oldfh = select STDERR;
$| = 1;
_do_exit(1);
} else {
&print_error ("There are dead or circular dependencies\n");
};
};
#
# Find project with no dependencies left.
#
sub FindIndepPrj {
my ($Prj, @Prjs, $Dependencies, $i);
my $children = &children_number;
return '' if ($children && ($children >= $QuantityToBuild));
$Dependencies = shift;
@Prjs = keys %$Dependencies;
if ($#Prjs != -1) {
foreach $Prj (@Prjs) {
next if (&IsHashNative($Prj));
my $PrjDeps = $$Dependencies{$Prj};
return $Prj if (!scalar keys %$PrjDeps);
};
return '';
} else {
$no_projects = 1;
return '';
};
};
#
# Check if given entry is HASH-native, that is not a user-defined data
#
sub IsHashNative {
my $Prj = shift;
return 1 if ($Prj =~ /^HASH\(0x[\d | a | b | c | d | e | f]{6,}\)/);
return 0;
};
#
# Getting array of dependencies from the string given
#
sub GetDependenciesArray {
my ($DepString, @Dependencies, $ParentPrj, $prj, $string);
@Dependencies = ();
$DepString = shift;
$string = $DepString;
$prj = shift;
while ($DepString !~ /^NULL/o) {
&print_error("Project $prj has wrongly written dependencies string:\n $string") if (!$DepString);
$DepString =~ /(\S+)\s*/o;
$ParentPrj = $1;
$DepString = $';
if ($ParentPrj =~ /\.(\w+)$/o) {
$ParentPrj = $`;
if (($prj_platform{$ParentPrj} ne $1) &&
($prj_platform{$ParentPrj} ne 'all')) {
&print_error ("$ParentPrj\.$1 is a wrongly dependency identifier!\nCheck if it is platform dependent");
};
$AliveDependencies{$ParentPrj}++ if (&CheckPlatform($1));
push(@Dependencies, $ParentPrj);
} else {
if ((exists($prj_platform{$ParentPrj})) &&
($prj_platform{$ParentPrj} ne 'all') ) {
&print_error("$ParentPrj is a wrongly used dependency identifier!\nCheck if it is platform dependent");
};
push(@Dependencies, $ParentPrj);
};
};
return @Dependencies;
};
#
# Getting current directory list
#
sub GetDirectoryList {
my ($Path);
$Path = shift;
opendir(CurrentDirList, $Path);
@DirectoryList = readdir(CurrentDirList);
closedir(CurrentDirList);
return @DirectoryList;
};
sub finish_logging {
return if ($show || (!defined $log));
my $message = shift;
$message = 'SUCCESS.' if (!$message);
$message .= " Built $modules_number modules.";
$log->end_log_extended($script_name,$vcsid,$message);
};
sub print_error {
my $message = shift;
rmtree(CorrectPath($tmp_dir), 0, 1) if ($tmp_dir);
$modules_number -= scalar keys %global_deps_hash;
$modules_number -= 1;
&finish_logging("FAILURE: " . $message);
print STDERR "\nERROR: $message\n";
$ENV{mk_tmp} = '';
close CMD_FILE if ($cmd_file);
unlink ($cmd_file);
do_exit(1) if (!$child);
};
sub usage {
print STDERR "\nbuild\n";
print STDERR "Syntax: build [--all|-a[:prj_name]]|[--from|-f prj_name1[:prj_name2] [prj_name3 [...]]]|[--since|-c prj_name] [--with_branches|-b]|[--prepare|-p][:platform]] [--deliver|-d [--dlv_switch deliver_switch]]] [-P processes] [--show|-s] [--help|-h] [--file|-F] [--ignore|-i] [--version|-V] [--mode|-m OOo[,SO[,EXT]] [-- dmake_options] \n";
print STDERR "Example: build --from sfx2\n";
print STDERR " - build projects including current one from sfx2\n";
print STDERR "Example: build --all:sfx2\n";
print STDERR " - the same as --all, but skip all projects that have been already built when using \"--all\" switch before sfx2\n";
print STDERR "Keys: --all - build all projects from very beginning till current one\n";
print STDERR " --from - build all projects dependent from the specified (including it) till current one\n";
print STDERR " --mode OOo - build only projects needed for OpenOffice.org\n";
print STDERR " --prepare- - clear all projects for incompatible build from prj_name till current one [for platform] (cws version)\n";
print STDERR " --with_branches- build all projects in neighbour branches and current branch starting from actual project\n";
print STDERR " --since - build all projects beginning from the specified till current one (the same as \"--all:prj_name\", but skipping prj_name)\n";
print STDERR " --show - show what is going to be built\n";
print STDERR " --file - generate command file file_name\n";
print STDERR " --deliver - only deliver, no build (usable for \'-all\' and \'-from\' keys)\n";
print STDERR " -P - start multiprocessing build, with number of processes passed (UNIXes only)\n";
print STDERR " --dlv_switch - use deliver with the switch specified\n";
print STDERR " --help - print help info\n";
print STDERR " --ignore - force tool to ignore errors\n";
print STDERR "Default: - build current project\n";
print STDERR "Keys that are not listed above would be passed to dmake\n";
};
sub init_logging {
return if ($show || (!defined $log));
my $parameter_list = '';
foreach (@ARGV) {$parameter_list .= "$_\;"};
$parameter_list = $` if ($parameter_list =~ /;$/o);
my $childws = $ENV{CWS_WORK_STAMP};
my $masterws = $ENV{WORK_STAMP};
return if (!defined( $childws ) || !defined( $masterws ));
$log->start_log_extended($script_name, $parameter_list, $masterws, $childws);
};
#
# Get all options passed
#
sub get_options {
my $arg;
&init_logging;
while ($arg = shift @ARGV) {
$arg =~ /^-P$/ and $QuantityToBuild = shift @ARGV and next;
$arg =~ /^-P(\d+)$/ and $QuantityToBuild = $1 and next;
$arg =~ /^--all$/ and $BuildAllParents = 1 and next;
$arg =~ /^-a$/ and $BuildAllParents = 1 and next;
$arg =~ /^--show$/ and $show = 1 and next;
$arg =~ /^-s$/ and $show = 1 and next;
$arg =~ /^--deliver$/ and $deliver = 1 and next;
$arg =~ /^-d$/ and $deliver = 1 and next;
$arg =~ /^--dlv_switch$/ and $dlv_switch = &get_switch_options and next;
$arg =~ /^--file$/ and $cmd_file = shift @ARGV and next;
$arg =~ /^-F$/ and $cmd_file = shift @ARGV and next;
$arg =~ /^--with_branches$/ and $BuildAllParents = 1
and $build_from = shift @ARGV and next;
$arg =~ /^-b$/ and $BuildAllParents = 1
and $build_from = shift @ARGV and next;
$arg =~ /^--all:(\S+)$/ and $BuildAllParents = 1
and $build_from_opt = $1 and next;
$arg =~ /^-a:(\S+)$/ and $BuildAllParents = 1
and $build_from_opt = $1 and next;
if ($arg =~ /^--from$/ || $arg =~ /^-f$/) {
$BuildAllParents = 1;
&get_incomp_projects;
next;
};
$arg =~ /^--prepare$/ and $prepare = 1 and next;
$arg =~ /^-p$/ and $prepare = 1 and next;
$arg =~ /^--prepare:/ and $prepare = 1 and $only_platform = $' and next;
$arg =~ /^-p:/ and $prepare = 1 and $only_platform = $' and next;
$arg =~ /^--since$/ and $BuildAllParents = 1
and $build_since = shift @ARGV and next;
$arg =~ /^-c$/ and $BuildAllParents = 1
and $build_since = shift @ARGV and next;
$arg =~ /^-s$/ and $BuildAllParents = 1
and $build_since = shift @ARGV and next;
$arg =~ /^--help$/ and &usage and do_exit(0);
$arg =~ /^-h$/ and &usage and do_exit(0);
$arg =~ /^--ignore$/ and $ignore = 1 and next;
$arg =~ /^-i$/ and $ignore = 1 and next;
$arg =~ /^--version$/ and do_exit(0);
$arg =~ /^-V$/ and do_exit(0);
$arg =~ /^-m$/ and &get_modes and next;
$arg =~ /^--mode$/ and &get_modes and next;
if ($arg =~ /^--$/) {
&get_dmake_args;
next;
};
push (@dmake_args, $arg);
};
&print_error('Switches --with_branches and --all collision') if ($build_from && $build_from_opt);
&print_error('Switches --with_branches and --since collision') if ($build_from && $build_since);
$cmd_file = '' if ($show);
$incompatible = scalar keys %incompatibles;
if ($prepare && !$incompatible) {
&print_error("--prepare is for use with --from switch only!\n");
};
if ($QuantityToBuild) {
if ($ignore) {
print_error("Cannot ignore errors in multiprocessing build");
};
if (!$enable_multiprocessing) {
print_error("Cannot load Win32::Process module for multiprocessing build");
};
};
if ($only_platform) {
$only_common = 'common';
$only_common .= '.pro' if ($only_platform =~ /\.pro$/);
};
# Default build modes(for OpenOffice.org)
$ENV{BUILD_TYPE} = 'OOo EXT' if (!defined $ENV{BUILD_TYPE});
@ARGV = @dmake_args;
};
sub get_dmake_args {
my $arg;
while ($arg = shift @ARGV) {
next if ($arg =~ /^--$/);
push (@dmake_args, $arg);
};
};
#
# get all options without '-'
#
sub get_switch_options {
my $string = '';
my $option = '';
while ($option = shift @ARGV) {
if (!($option =~ /^-+/)) {
$string .= '-' . $option;
$string .= ' ';
} else {
unshift(@ARGV, $option);
last;
};
};
$string =~ s/\s$//;
return $string;
};
#
# cancel build when one of children has error exit code
#
sub cancel_build {
$modules_number -= scalar keys %global_deps_hash;
my $log_string = 'FAILURE. Build is broken in modules: ';
if ($BuildAllParents) {
$modules_number -= scalar @broken_modules_names;
print "\n";
print scalar @broken_modules_names;
print " module(s): ";
foreach (@broken_modules_names) {
print "\n\t$_";
$log_string .= " $_";
# &RemoveFromDependencies($_, \%global_deps_hash);
};
finish_logging($log_string);
print "\nneed(s) to be rebuilt\n\nReason(s):\n\n";
foreach (keys %broken_build) {
print "ERROR: error " . $broken_build{$_} . " occurred while making $_\n";
};
print "\nAttention: if you build and deliver the above module(s) you may prolongue your the build issuing command \"build --from @broken_modules_names\n";
} else {
&finish_logging($log_string . $CurrentPrj);
# if ($ENV{GUI} eq 'WNT') {
while (children_number()) {
handle_dead_children();
sleep 1;
}
foreach (keys %broken_build) {
print "ERROR: error " . $broken_build{$_} . " occurred while making $_\n";
};
# } else {
# kill 9 => -$$;
# };
};
print "\n";
do_exit(1);
};
#
# Function for storing error in multiprocessing AllParents build
#
sub store_error {
my ($pid, $error_code) = @_;
my $child_nick = $processes_hash{$pid};
$broken_modules_hashes{$folders_hashes{$child_nick}}++;
$broken_build{$child_nick} = $error_code;
};
#
# child handler (clears (or stores info about) the terminated child)
#
sub handle_dead_children {
return if (!children_number());
do {
my $pid = 0;
if ($ENV{GUI} eq 'WNT') {
foreach $pid (keys %processes_hash) {
my $exit_code = undef;
my $proc_obj = $windows_procs{$pid};
$proc_obj->GetExitCode($exit_code);
if ( $exit_code != 259 ) {
store_error($pid, $exit_code) if ($exit_code);
clear_from_child($pid);
delete $windows_procs{$pid};
};
}
} else {
if (($pid = waitpid( -1, &WNOHANG)) > 0) {
store_error($pid, $?) if ($?);
clear_from_child($pid);
};
};
sleep 1 if (children_number() >= $QuantityToBuild);
} while(children_number() >= $QuantityToBuild);
};
sub clear_from_child {
my $pid = shift;
my $child_nick = $processes_hash{$pid};
&RemoveFromDependencies($child_nick,
$folders_hashes{$child_nick});
$running_children{$folders_hashes{$child_nick}}--;
delete $processes_hash{$pid};
$only_dependent = 0;
};
#
# Build the entire project according to queue of dependencies
#
sub BuildDependent {
$dependencies_hash = shift;
my $pid = 0;
my $child_nick = '';
$running_children{$dependencies_hash} = 0 if (!defined $running_children{$dependencies_hash});
while ($child_nick = &PickPrjToBuild($dependencies_hash)) {
if (($QuantityToBuild)) { # multiprocessing not for $BuildAllParents (-all etc)!!
do {
handle_dead_children();
if (defined $broken_modules_hashes{$dependencies_hash}) {
return if ($BuildAllParents);
last;
};
# start current child & all
# that could be started now
start_child($child_nick) if ($child_nick);
$child_nick = PickPrjToBuild($dependencies_hash);
if (!$child_nick) {
return if ($BuildAllParents);
sleep 1 if (!$no_projects);
};
} while (!$no_projects);
return if ($BuildAllParents);
while (children_number()) {
handle_dead_children();
sleep 1;
};
if (defined $broken_modules_hashes{$dependencies_hash}) {
cancel_build();
}
mp_success_exit();
} else {
dmake_dir($child_nick);
};
$child_nick = '';
};
};
sub children_number {
return scalar keys %processes_hash;
};
sub start_child {
my $child_nick = shift;
my $pid = undef;
my $children_running;
my $oldfh = select STDOUT;
$| = 1;
if ($ENV{GUI} eq 'WNT') {
print "$child_nick\n";
my $process_obj = undef;
my $rc = Win32::Process::Create($process_obj, $dmake_bin,
$dmake_args,
0, 0, #NORMAL_PRIORITY_CLASS,
$child_nick);
# my $rc = Win32::Process::Create($process_obj, $_4nt_exe,
# "/c $dmake_batch",
# 0, NORMAL_PRIORITY_CLASS,
# $child_nick);
print_error("Cannot start child process") if (!$rc);
$pid = $process_obj->GetProcessID();
$windows_procs{$pid} = $process_obj;
} else {
if ($pid = fork) { # parent
} elsif (defined $pid) { # child
select $oldfh;
$child = 1;
&dmake_dir($child_nick);
do_exit(1);
};
};
select $oldfh;
$processes_hash{$pid} = $child_nick;
$children_running = children_number();
print 'Running processes: ', $children_running, "\n";
$maximal_processes = $children_running if ($children_running > $maximal_processes);
$folders_hashes{$child_nick} = $dependencies_hash;
$running_children{$dependencies_hash}++;
};
#
# Build everything that should be built multiprocessing version
#
sub build_multiprocessing {
my $Prj;
my @build_queue = (); # array, containing queue of projects
# to build
do {
while ($Prj = PickPrjToBuild(\%global_deps_hash)) {
my $module_type = $modules_types{$Prj};
if (($module_type eq 'lnk') || ($module_type eq 'img')) {
print_announce($Prj);
RemoveFromDependencies($Prj, \%global_deps_hash);
next;
};
mark_force_deliver($Prj, CorrectPath($StandDir.$Prj)) if (defined $ENV{CWS_WORK_STAMP});
push @build_queue, $Prj;
$projects_deps_hash{$Prj} = {};
&get_deps_hash($Prj, $projects_deps_hash{$Prj});
};
if (!$Prj) {
cancel_build() if (!scalar @build_queue);
sleep(1);
}
build_actual_queue(\@build_queue);
# if (scalar keys %broken_modules_hashes) {
# do {
# sleep(1);
# handle_dead_children();
# build_actual_queue(\@build_queue);
# } while (children_number());
# cancel_build();
# };
} while (scalar (keys %global_deps_hash));
# Let all children finish their work
while (children_number()) {
handle_dead_children();
sleep 1;
}
cancel_build() if (scalar keys %broken_build);
mp_success_exit();
};
sub mp_success_exit {
print "\nMultiprocessing build is finished\n";
print "Maximum number of processes run: $maximal_processes\n";
do_exit(0);
};
#
# Here the built queue is built as long as possible
#
sub build_actual_queue {
my $build_queue = shift;
my $i = 0;
do {
while ($i <= (scalar(@$build_queue) - 1)) {
$Prj = $$build_queue[$i];
if (defined $broken_modules_hashes{$projects_deps_hash{$Prj}}) {
push (@broken_modules_names, $Prj);
splice (@$build_queue, $i, 1);
next;
};
announce_module($Prj) if (!(defined $module_announced{$Prj}));
$only_dependent = 0;
$no_projects = 0;
&BuildDependent($projects_deps_hash{$Prj});
if ($no_projects &&
!$running_children{$projects_deps_hash{$Prj}} &&
!defined $broken_modules_hashes{$projects_deps_hash{$Prj}})
{
chdir(&CorrectPath($StandDir.$Prj));
system (get_deliver_commando($Prj)) if (!$show && ($Prj ne $CurrentPrj));
RemoveFromDependencies($Prj, \%global_deps_hash);
splice (@$build_queue, $i, 1);
next;
};
$i++;
handle_dead_children();
};
$i = 0;
} while (!&are_all_dependent($build_queue));
};
#
# Print announcement for module just started
#
sub announce_module {
my $Prj = shift;
&print_announce($Prj);
$module_announced{$Prj}++;
};
sub print_announce {
my $Prj = shift;
my $prj_type = $modules_types{$Prj};
my $text;
if ($prj_type eq 'lnk') {
$text = "Skipping link to $Prj\n";
} elsif ($prj_type eq 'img') {
return if (defined $module_announced{$`});
$text = "Skipping incomplete $Prj\n";
} else {
$text = "Building project $Prj\n";
};
print $echo . "=============\n";
print $echo . $text;
print $echo . "=============\n";
};
sub are_all_dependent {
my $build_queue = shift;
my $folder = '';
foreach my $prj (@$build_queue) {
$folder = &FindIndepPrj($projects_deps_hash{$prj});
return '' if ($folder);
};
return '1';
};
#
# Procedure checks out module or its image ($prj_name/prj)
#
sub checkout_module {
my ($prj_name, $image, $path) = @_;
return '' if (!defined($ENV{CWS_WORK_STAMP}));
$path = $StandDir if (!$path);
my $cws = Cws->new();
$cws->child($ENV{CWS_WORK_STAMP});
$cws->master($ENV{WORK_STAMP});
my $cvs_module = &get_cvs_module($cws, $prj_name);
&print_error("Cannot get cvs_module for $prj_name") if (!$cvs_module);
my ($master_branch_tag, $cws_branch_tag, $cws_root_tag, $master_milestone_tag) = $cws->get_tags();
$cvs_module->verbose(1);
$cvs_module->{MODULE} .= '/prj' if ($image);
if ($show && ($path ne $tmp_dir)) {
print "Checking out $prj_name...\n";
return;
};
$cvs_module->checkout($path, $master_milestone_tag, '');
# Quick hack, should not be there
# if Heiner's Cws has error handling
if (!-d &CorrectPath($path.'/'.$prj_name)) {
$cvs_module->checkout($path, '', '');
if (!-d &CorrectPath($path.'/'.$prj_name)) {
$dead_parents{$prj_name}++;
print STDERR ("Cannot checkout $prj_name. Check if you have to login to server or all build dependencies are consistent\n");
return;
};
};
return 1 if ($image);
copy_output_trees($prj_name, $path) if (defined $only_platform);
};
#
# Procedure unpacks output trees after checkout
#
sub copy_output_trees {
return if (!defined $log);
return if (!scalar keys %platforms_to_copy);
my $module_name = shift;
my $src_dest = shift;
print "copyprj $module_name\n";
# hash, that should contain all the
# data needed by CopyPrj module
my %ENVHASH = ();
my %projects_to_copy = ();
$ENVHASH{'projects_hash'} = \%projects_to_copy;
$ENVHASH{'prj_to_copy'} = '';
$ENVHASH{'platforms_hash'} = \%platforms_to_copy;
$ENVHASH{'no_otree'} = 1;
$ENVHASH{'no_path'} = 1;
$ENVHASH{'only_otree'} = 1;
$ENVHASH{'only_update'} = 0;
$ENVHASH{'last_minor'} = 0;
$ENVHASH{'spec_src'} = 0;
$ENVHASH{'dest'} = "$src_dest";
$ENVHASH{'i_server'} = '';
$ENVHASH{'current_dir'} = cwd();
$ENVHASH{'remote'} = '';
# hack for SO environment
$ENVHASH{'SRC_ROOT'} = '/so/ws/' . $ENV{WORK_STAMP} . '/src';
$ENVHASH{'SRC_ROOT'} .= $ENV{UPDMINOREXT} if (defined $ENV{UPDMINOREXT});
$projects_to_copy{$module_name}++;
CopyPrj::copy_projects(\%ENVHASH);
};
#
# Procedure defines if the local directory is a
# complete module, an image or a link
# return values: lnk link
# img incomplete (image)
# mod complete (module)
#
sub modules_classify {
my @modules = @_;
foreach my $module (sort @modules) {
if (-d $StandDir.$module) {
$modules_types{$module} = 'mod';
next;
};
if (-e $StandDir.$module.'.lnk') {
$modules_types{$module} = 'lnk';
next;
};
$modules_types{$module} = 'img';
};
# opendir DIRHANDLE, $StandDir.$Prj;
# my @dir_content = readdir(DIRHANDLE);
# closedir(DIRHANDLE);
# # Check if there only 2 entries: CVS & prj
# # dirty, but must work
# if (scalar(@dir_content) <= 4) {
# foreach (@dir_content) {
# return 'mod' if ( ($_ ne 'CVS') &&
# ($_ ne 'prj') &&
# (!(/^\.+$/o)) );
# };
# return 'img';
# };
# return 'mod';
};
#
# This procedure provides consistency for cws
# for optimized build (ie in case of -with_branches, -all:prj_name
# and -since switches)
#
sub provide_consistency {
&check_dir;
foreach $var_ref (\$build_from, \$build_from_opt, \$build_since) {
if ($$var_ref) {
return if (-d $StandDir.$$var_ref);
$$var_ref .= '.lnk' and return if (-d $StandDir.$$var_ref.'.lnk');
my $current_dir = cwd();
&checkout_module($$var_ref, 'image');
chdir $current_dir;
cwd();
return;
};
};
};
#
# Retrieve CvsModule object for passed module.
# (Heiner's proprietary :)
#
sub get_cvs_module
{
my $cws = shift;
my $module = shift;
my $cvs_module = CvsModule->new();
my ($method, $vcsid, $server, $repository) = get_cvs_root($cws, $module);
return undef if !($method && $vcsid && $server && $repository);
$cvs_module->module($module);
$cvs_module->cvs_method($method);
$cvs_module->vcsid($vcsid);
$cvs_module->cvs_server($server);
$cvs_module->cvs_repository($repository);
return $cvs_module;
};
#
# Try to get cvs coordinates via module link
#
sub get_link_cvs_root{
my $module = shift;
my $cvs_root_file = $StandDir.$module.'.lnk'.'/CVS/Root';
if (!open(CVS_ROOT, $cvs_root_file)) {
#print STDERR "Attention: cannot read $cvs_root_file!!\n";
return '';
};
my @cvs_root = <CVS_ROOT>;
close CVS_ROOT;
$cvs_root[0] =~ s/[\r\n]+//o;
return $cvs_root[0] if (!($cvs_root[0] =~ /\^\s*$/));
return '';
};
#
# Find out which CVS server holds the module, returns
# the elements of CVSROOT.
# (Heiner's proprietary)
#
sub get_cvs_root
{
my $cws = shift;
my $module = shift;
my $cvsroot = &get_link_cvs_root($module);
if (!$cvsroot) {
my $master = $cws->master();
my $workspace_lst = get_workspace_lst();
my $workspace_db = GenInfoParser->new();
my $success = $workspace_db->load_list($workspace_lst);
if ( !$success ) {
print_error("Can't load workspace list '$workspace_lst'.", 4);
}
my $key = "$master/drives/o:/projects/$module/scs";
$cvsroot = $workspace_db->get_value($key);
if ( !$cvsroot ) {
print STDERR "\nWarning: No such module '$module' for '$master' in workspace database $workspace_lst. Maybe you should correct build lists.\n";
$dead_parents{$module}++;
return (undef, undef, undef, undef);
}
};
my ($dummy1, $method, $user_at_server, $repository) = split(/:/, $cvsroot);
my ($dummy2, $server) = split(/@/, $user_at_server);
if ( ! ($method && $server && $repository ) ) {
print_error("Can't determine CVS server for module '$module'.", 0);
return (undef, undef, undef, undef);
}
return ($method, $vcsid, $server, $repository);
};
#
# Get the workspace list ('stand.lst'), either from 'localini'
# or, if this is not possible, from 'globalini.
# (Heiner's proprietary :)
#
sub get_workspace_lst
{
my $home;
if ( $^O eq 'MSWin32' ) {
$home = $ENV{TEMP};
}
else {
$home = $ENV{HOME};
}
my $localini = "$home/localini";
if ( ! -f "$localini/stand.lst" ) {
my $globalini = get_globalini();
return "$globalini/stand.lst";
}
return "$localini/stand.lst";
}
#
# Procedure clears up module for incompatible build
#
sub ensure_clear_module {
my $module = shift;
my $module_type = $modules_types{$module};
my $lnk_name = $module . '.lnk';
if ($module_type eq 'mod') {
if (-e ($StandDir.$lnk_name)) {
print "Last checkout for $module seems to have been interrupted...\n";
print "Checking it out again...\n";
#rmtree("$StandDir$module", 0, 1);
$module_type = 'lnk';
} else {
&clear_module($module);
return;
};
};
if ($module_type eq 'lnk') {
print "\nBreaking link $lnk_name...\n";
return if ($show);
&checkout_module($module);
my $action = '';
if ( $^O eq 'MSWin32' ) {
if(!rename("$StandDir$lnk_name", "$StandDir$module.backup.lnk")) {
$action = 'rename';
};
} else {
if(!unlink $StandDir.$lnk_name) {
$action = 'remove';
}
};
&print_error("Cannot $action $StandDir$lnk_name. Please $action it manually") if ($action);
} else {
print "Checking out consistent " . $module . "...\n";
&checkout_module ($module) if (!$show);
};
};
#
# Procedure removes output tree from the module (without common trees)
#
sub clear_module {
my $Prj = shift;
print "Removing module's $Prj output trees...\n";
print "\n" and return if ($show);
opendir DIRHANDLE, $StandDir.$Prj;
my @dir_content = readdir(DIRHANDLE);
closedir(DIRHANDLE);
foreach (@dir_content) {
next if (/^\.+$/);
my $dir = &CorrectPath($StandDir.$Prj.'/'.$_);
if ((!-d $dir.'/CVS') && &is_output_tree($dir)) {
#print "I would delete $dir\n";
rmtree("$dir", 0, 1);
if (defined $SIG{__WARN__} && -d $dir) {
&print_error("Cannot delete $dir");
};
};
};
};
#
# Figure out if the directory is an output tree
#
sub is_output_tree {
my $dir = shift;
$dir =~ /([\w\d\.]+)$/;
$_ = $1;
return '1' if (defined $platforms{$_});
if ($only_common) {
return '1' if ($_ eq $only_common);
} else {
return '1' if (/^common$/);
return '1' if (/^common\.pro$/);
};
return '';
};
sub get_tmp_dir {
my $tmp_dir;
if( defined($ENV{TMP}) ) {
$tmp_dir = $ENV{TMP} . '/';
} else {
$tmp_dir = '/tmp/';
}
$tmp_dir .= $$ while (-d $tmp_dir);
$tmp_dir = CorrectPath($tmp_dir);
eval {mkpath($tmp_dir)};
print_error("Cannot create temporary directory for checkout in $tmp_dir") if ($@);
return $tmp_dir;
};
sub retrieve_build_list {
my $module = shift;
# First try to get global depencies from solver's build.lst if such exists
my $solver_inc_dir = "$ENV{SOLARVER}/common";
$solver_inc_dir .= $ENV{PROEXT} if (defined $ENV{PROEXT});
$solver_inc_dir .= '/inc';
$solver_inc_dir .= $ENV{UPDMINOREXT} if (defined $ENV{UPDMINOREXT});
$solver_inc_dir .= "/$module";
$solver_inc_dir = CorrectPath($solver_inc_dir);
print STDERR "Fetching dependencies for module $module from solver...";
foreach (@possible_build_lists) {
my $possible_build_lst = "$solver_inc_dir/$_";
if (-e $possible_build_lst) {
print " ok\n";
return $possible_build_lst;
};
}
print STDERR " failed...\n";
print STDERR "Fetching from CVS... ";
if (!checkout_module($module, 'image', $tmp_dir)) {
print " failed\n";
if (!defined $dead_parents{$module}) {
print STDERR "WARNING: Cannot figure out CWS for $module. Forgot to set CWS?\n";
}
return undef;
};
# no need to announce this module
print " ok\n";
eval {
mkpath($solver_inc_dir) if (!-e $solver_inc_dir);
};
print_error("Cannot create $solver_inc_dir") if (!-d $solver_inc_dir);
my $success;
foreach (@possible_build_lists) {
my $tmp_build_lst = $tmp_dir . '/' . $module . '/prj/' . $_;
$possible_build_lst = undef;
next if (!-e $tmp_build_lst);
$possible_build_lst = $solver_inc_dir . '/' .$_;
my @from_stat = stat($tmp_build_lst);
if (!File::Copy::move($tmp_build_lst, $solver_inc_dir)) {
print_error("Cannot copy build list to $solver_inc_dir");
};
$success++;
my @to_stat = stat($possible_build_lst);
$from_stat[9]-- if $from_stat[9] % 2;
utime ($from_stat[9], $from_stat[9], $possible_build_lst);
last;
};
rmtree(CorrectPath($tmp_dir . '/' . $module), 0, 1);
return undef if (!$success);
return $possible_build_lst;
};
#
# Removes projects which it is not necessary to build
# in incompatible build
#
sub prepare_incompatible_build {
my ($prj, $deps_hash);
$deps_hash = shift;
foreach (keys %incompatibles) {
my $incomp_prj = $_;
$incomp_prj .= '.lnk' if (!defined $$deps_hash{$_});
delete $incompatibles{$_};
$incompatibles{$incomp_prj} = $$deps_hash{$incomp_prj};
delete $$deps_hash{$incomp_prj};
}
while ($prj = &PickPrjToBuild($deps_hash)) {
&RemoveFromDependencies($prj, $deps_hash);
&RemoveFromDependencies($prj, \%incompatibles);
};
foreach (keys %incompatibles) {
$$deps_hash{$_} = $incompatibles{$_};
};
if ($build_from_opt) {
&prepare_build_from_opt($deps_hash);
delete $$deps_hash{$build_from_opt};
};
@modules_built = keys %$deps_hash;
clear_delivered() if ($prepare);
my $old_output_tree = '';
foreach $prj (sort keys %$deps_hash) {
if ($prepare) {
ensure_clear_module($prj);
} else {
next if ($show);
my $message;
if ($modules_types{$prj} ne 'mod') {
$message = "$prj is not a complete module!";
} elsif (-d &CorrectPath($StandDir.$prj.'/'. $ENV{INPATH})) {
$old_output_tree++;
};
&print_error("$message Prepare workspace with --prepare switch!") if ($message);
};
};
if ($build_from_opt) {
$$deps_hash{$build_from_opt} = ();
$build_from_opt = '';
};
if ($old_output_tree) {
print STDERR "\nAttention: Some module(s) contain old output tree(s)! If you are performing an incompatible build, please break the build with Ctrl+C and prepare the workspace with --prepare switch!\n\n";
sleep(10);
};
print "\nPreparation finished\n\n" and do_exit(0) if ($prepare);
};
#
# Removes projects which it is not necessary to build
# with -with_branches switch
#
sub prepare_build_from {
my ($prj, $deps_hash);
$deps_hash = shift;
my %from_deps_hash = (); # hash of dependencies of the -from project
&GetParentDeps($build_from, \%from_deps_hash);
foreach $prj (keys %from_deps_hash) {
delete $$deps_hash{$prj};
&RemoveFromDependencies($prj, $deps_hash);
};
};
#
# Removes projects which it is not necessary to build
# with --all:prj_name or --since switch
#
sub prepare_build_from_opt {
my ($prj, $deps_hash, $border_prj);
$deps_hash = shift;
$border_prj = $build_from_opt if ($build_from_opt);
$border_prj = $build_since if ($build_since);
while ($prj = &PickPrjToBuild($deps_hash)) {
$orig_prj = '';
$orig_prj = $` if ($prj =~ /\.lnk$/o);
if (($border_prj ne $prj) &&
($border_prj ne $orig_prj)) {
&RemoveFromDependencies($prj, $deps_hash);
next;
} else {
if ($build_from_opt) {
$$deps_hash{$prj} = ();
} else {
&RemoveFromDependencies($prj, $deps_hash);
};
return;
};
};
};
sub get_modes {
my $option = '';
while ($option = shift @ARGV) {
if ($option =~ /^-+/) {
unshift(@ARGV, $option);
return;
} else {
if ($option =~ /,/) {
$build_modes{$`}++;
unshift(@ARGV, $') if ($');
} else {$build_modes{$option}++;};
};
};
$build_modes{$option}++;
};
sub get_incomp_projects {
my $option = '';
while ($option = shift @ARGV) {
if ($option =~ /^-+/) {
unshift(@ARGV, $option);
return;
} else {
if ($option =~ /(:)/) {
$option = $`;
&print_error("-from switch collision") if ($build_from_opt);
$build_from_opt = $';
};
$incompatibles{$option}++;
};
};
};
sub get_platforms {
my $platforms_ref = shift;
if ($only_platform) {
$$platforms_ref{$only_platform}++;
$platforms_ref = \%platforms_to_copy;
};
my $solver = $ENV{SOLARVERSION};
my ($iserverbin, @platforms_conf);
$iserverbin = "i_server -d ";
$iserverbin .= $ENV{SOLAR_ENV_ROOT} . '/b_server/config/stand.lst -i ';
my $workstamp = $ENV{WORK_STAMP};
@platforms_conf = `$iserverbin $workstamp/Environments -l`;
if ( $platforms_conf[0] =~ /Environments/ ) {
shift @platforms_conf;
}
foreach (@platforms_conf) {
s/\s//g;
my $s_path = $solver . '/' . $_;
$$platforms_ref{$_}++ if (-e $s_path);
};
delete $platforms_to_copy{$only_platform} if (defined $only_platform);
&print_error("There is no platform found!!") if (!scalar keys %platforms);
};
#
# This procedure clears solver from delivered
# by the modules to be build
#
sub clear_delivered {
print "Clearing up delivered\n";
my %backup_vars;
foreach my $platform (keys %platforms) {
print "\nRemoving delivered for $platform\n";
my %solar_vars = ();
&read_ssolar_vars($platform, \%solar_vars);
foreach (keys %solar_vars) {
if (!defined $backup_vars{$_}) {
$backup_vars{$_} = $ENV{$_};
};
$ENV{$_} = $solar_vars{$_};
};
my $undeliver = "$deliver_commando -delete $nul";
foreach my $module (sort @modules_built) {
my $module_path = &CorrectPath($StandDir.$module);
print "Removing delivered from module $module\n";
next if ($show);
my $current_dir = cwd();
chdir($module_path.'.lnk') or chdir($module_path);
if (system($undeliver)) {
$ENV{$_} = $backup_vars{$_} foreach (keys %backup_vars);
&print_error("Cannot run: $undeliver");
}
chdir $current_dir;
cwd();
};
};
$ENV{$_} = $backup_vars{$_} foreach (keys %backup_vars);
};
#
# Run setsolar for given platform and
# write all variables needed in %solar_vars hash
#
sub read_ssolar_vars {
my ($setsolar, $entries_file, $tmp_file);
$setsolar = $ENV{ENV_ROOT} . '/etools/setsolar.pl';
my ($platform, $solar_vars) = @_;
if ( $^O eq 'MSWin32' ) {
$tmp_file = $ENV{TEMP} . "\\solar.env.$$.tmp";
} else {
$setsolar = '/net/jumbo.germany/cvs/buildenv/etools/setsolar.pl' if ! -e $setsolar;
$tmp_file = $ENV{HOME} . "/.solar.env.$$.tmp";
};
print_error('There is no setsolar found') if !-e $setsolar;
my $pro = "";
if ($platform =~ /\.pro$/) {
$pro = "-pro";
$platform = $`;
};
my $param = "-$ENV{WORK_STAMP} $pro $platform";
my $ss_comando = "$perl $setsolar -file $tmp_file $param $nul";
$entries_file = '/CVS/Entries';
if (system($ss_comando)) {
unlink $tmp_file;
&print_error("Cannot run commando:\n$ss_comando");
};
&get_solar_vars($solar_vars, $tmp_file);
};
#
# read variables to hash
#
sub get_solar_vars {
my ($solar_vars, $file) = @_;
my ($var, $value);
open SOLARTABLE, "<$file" or die "can<61>t open solarfile $file";
while(<SOLARTABLE>) {
s/\r\n//o;
next if(!/^\w+\s+(\w+)/o);
next if (!defined $deliver_env{$1});
$var = $1;
if ( $^O eq 'MSWin32' ) {
/$var=(\S+)$/o;
$value = $1;
} else {
/\'(\S+)\'$/o;
$value = $1;
};
$$solar_vars{$var} = $value;
};
close SOLARTABLE;
unlink $file;
}
#
# Procedure checks out the module when we're
# in link
#
sub checkout_current_module {
my $module_name = shift;
my $link_name = $module_name . '.lnk';
chdir $StandDir;
cwd();
print "\nBreaking link to module $module_name";
&checkout_module($module_name);
if (!-d $module_name) {
&print_error("Cannot checkout $module_name");
};
my $action;
if ( $^O eq 'MSWin32' ) {
$action = 'rename' if (!rename($link_name,
$module_name.'.backup.lnk'));
} else {
$action = 'remove' if (!unlink $link_name);
};
&print_error("Cannot $action $link_name. Please $action it manually") if ($action);
chdir $module_name;
cwd();
};
sub check_dir {
my $start_dir = cwd();
my @dir_entries = split(/[\\\/]/, $start_dir);
my $current_module = $dir_entries[$#dir_entries];
$current_module = $` if ($current_module =~ /(\.lnk)$/);
my $link_name = $ENV{SRC_ROOT}.'/'.$current_module.'.lnk';
if ( $^O eq 'MSWin32' ) {
$start_dir =~ s/\\/\//go;
$link_name =~ s/\\/\//go;
if (lc($start_dir) eq lc($link_name)) {
&checkout_current_module($current_module);
};
} elsif ((-l $link_name) && (chdir $link_name)) {
if ($start_dir eq cwd()) {
# we're dealing with link => fallback to SRC_ROOT under UNIX
$StandDir = $ENV{SRC_ROOT}.'/';
&checkout_current_module($current_module);
return;
} else {
chdir $start_dir;
cwd();
};
};
};
sub mark_force_deliver {
my ($module_name, $module_path) = @_;
# my $cws_tag_string = 'Tcws_' . lc($ENV{WORK_STAMP}.'_'.$ENV{CWS_WORK_STAMP});
my $cvs_tag_file = $module_path . '/CVS/Tag';
return if (!open CVSTAG, "<$cvs_tag_file");
my @tag = <CVSTAG>;
close CVSTAG;
$tag[0] =~ /^(\S+)/o;
$force_deliver{$module_name}++ if ($1 =~ /^Tcws_/o);
};
sub get_deliver_commando {
my $module_name = shift;
return $deliver_commando if (!defined $force_deliver{$module_name});
return $deliver_commando . ' -force';
};
#
# Store all available build modi in %build_modes
#
sub get_build_modes {
return if (scalar keys %build_modes);
if (defined $ENV{BUILD_TYPE}) {
if ($ENV{BUILD_TYPE} =~ /\s+/o) {
my @build_modes = split (/\s+/, $ENV{BUILD_TYPE});
$build_modes{$_}++ foreach (@build_modes);
} else {
$build_modes{$ENV{BUILD_TYPE}}++;
};
return;
};
};
#
# pick only the modules, that should be built for
# build types from %build_modes
#
sub pick_for_build_type {
my $modules = shift;
my @mod_array = split(/\s+/, $modules);
print_error("Wrongly written dependencies string:\n $modules\n") if ($mod_array[$#mod_array] ne 'NULL');
pop @mod_array;
my @modules_to_build;
#my $new_modules = '';
foreach (@mod_array) {
if (/(\w+):(\S+)/o) {
push(@modules_to_build, $2) if (defined $build_modes{$1});
next;
};
#next if (/^NULL$/);
push(@modules_to_build, $_);
};
return @modules_to_build;
};
sub do_exit {
my $exit_code = shift;
rmtree(CorrectPath($tmp_dir), 0, 1) if ($tmp_dir);
if ($exit_code) {
# &finish_logging("error occured");
} else {
# &finish_logging;
};
exit($exit_code);
};