xref: /AOO41X/main/solenv/bin/download_external_dependencies.pl (revision 489cdf4fc4ccb2ceabbd43dc2c1af211d9e7490e)
1*489cdf4fSarielch#!/usr/bin/env perl
26a22bca6SAndre Fischer
396dbeebcSAndre Fischer#**************************************************************
496dbeebcSAndre Fischer#
596dbeebcSAndre Fischer#  Licensed to the Apache Software Foundation (ASF) under one
696dbeebcSAndre Fischer#  or more contributor license agreements.  See the NOTICE file
796dbeebcSAndre Fischer#  distributed with this work for additional information
896dbeebcSAndre Fischer#  regarding copyright ownership.  The ASF licenses this file
996dbeebcSAndre Fischer#  to you under the Apache License, Version 2.0 (the
1096dbeebcSAndre Fischer#  "License"); you may not use this file except in compliance
1196dbeebcSAndre Fischer#  with the License.  You may obtain a copy of the License at
1296dbeebcSAndre Fischer#
1396dbeebcSAndre Fischer#    http://www.apache.org/licenses/LICENSE-2.0
1496dbeebcSAndre Fischer#
1596dbeebcSAndre Fischer#  Unless required by applicable law or agreed to in writing,
1696dbeebcSAndre Fischer#  software distributed under the License is distributed on an
1796dbeebcSAndre Fischer#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
1896dbeebcSAndre Fischer#  KIND, either express or implied.  See the License for the
1996dbeebcSAndre Fischer#  specific language governing permissions and limitations
2096dbeebcSAndre Fischer#  under the License.
2196dbeebcSAndre Fischer#
2296dbeebcSAndre Fischer#**************************************************************
2396dbeebcSAndre Fischer
246a22bca6SAndre Fischer=head1 NAME
256a22bca6SAndre Fischer
266a22bca6SAndre Fischer    download_external_libraries.pl - Load missing tarballs specified in main/external_libs.lst.
276a22bca6SAndre Fischer
286a22bca6SAndre Fischer=head1 SYNOPSIS
296a22bca6SAndre Fischer
306a22bca6SAndre Fischer    For downloading external libraries (typically from the main/bootstrap script):
316a22bca6SAndre Fischer
326a22bca6SAndre Fischer    download_external_libraries(<data-file-name>);
336a22bca6SAndre Fischer
346a22bca6SAndre Fischer=head1 DESCRIPTION
356a22bca6SAndre Fischer
366a22bca6SAndre Fischer    The contents of the main/external_libs.lst file are used to determine the
376a22bca6SAndre Fischer    external library tarballs that are missing from ext_sources/.
386a22bca6SAndre Fischer
396a22bca6SAndre Fischer    Individual libraries can be ignored depending on the values of environment variables.
406a22bca6SAndre Fischer
416a22bca6SAndre Fischer    Format of the main/external_libs.lst file:
426a22bca6SAndre Fischer
436a22bca6SAndre Fischer    The file is line based.
446a22bca6SAndre Fischer    Comments start with a # and go to the end of the line and are ignored.
456a22bca6SAndre Fischer    Lines that are empty or contain only spaces and/or comments are ignored.
466a22bca6SAndre Fischer
476a22bca6SAndre Fischer    All other lines can have one of two forms:
486a22bca6SAndre Fischer    - A variable definition of the form <name>=<value>.
496a22bca6SAndre Fischer    - A conditional block start in the form "if (<expression>)"
506a22bca6SAndre Fischer
516a22bca6SAndre Fischer    Variables defined in a conditional block are only visible in this block and
526a22bca6SAndre Fischer    replace the definition of global variables and variables earlier in the same
536a22bca6SAndre Fischer    block.
546a22bca6SAndre Fischer    Some variables have special names:
5510e20387SAndre Fischer    - MD5 is the expected MD5 checksum of the library tarball.
5610e20387SAndre Fischer    - SHA1 is the expected SHA1 checksum of the library tarball.
576a22bca6SAndre Fischer    - URL1 to URL9 specify from where to download the tarball.  The urls are tried in order.
5810e20387SAndre Fischer      The first successful download (download completed and checksum match) stops the iteration.
596a22bca6SAndre Fischer
606a22bca6SAndre Fischer    Expressions are explained below in the comment of EvaluateExpression().
616a22bca6SAndre Fischer
626a22bca6SAndre Fischer    A library is only regarded if its conditional expression evaluates to 1.
636a22bca6SAndre Fischer
646a22bca6SAndre Fischer    Example:
656a22bca6SAndre Fischer
666a22bca6SAndre Fischer    DefaultSite=http://some-internet-site.org
676a22bca6SAndre Fischer    if ( true )
686a22bca6SAndre Fischer        MD5 = 0123456789abcdef0123456789abcdef
696a22bca6SAndre Fischer        name = library-1.0.tar.gz
706a22bca6SAndre Fischer        URL1 = http://some-other-internet-site.org/another-name.tgz
716a22bca6SAndre Fischer        URL2 = $(DefaultSite)$(MD5)-$(name)
726a22bca6SAndre Fischer
736a22bca6SAndre Fischer    This tries to load a library first from some-other-internet-site.org and if
746a22bca6SAndre Fischer    that fails from some-internet-site.org.  The library is stored as $(MD5)-$(name)
756a22bca6SAndre Fischer    even when it is loaded as another-name.tgz.
766a22bca6SAndre Fischer
776a22bca6SAndre Fischer=cut
786a22bca6SAndre Fischer
796a22bca6SAndre Fischer
806a22bca6SAndre Fischeruse strict;
816a22bca6SAndre Fischer
826a22bca6SAndre Fischeruse File::Spec;
836a22bca6SAndre Fischeruse File::Path;
846a22bca6SAndre Fischeruse File::Basename;
856a22bca6SAndre Fischeruse Digest::MD5;
8610e20387SAndre Fischeruse Digest::SHA;
876a22bca6SAndre Fischeruse URI;
88f253223cSAndre Fischeruse LWP::UserAgent;
896a22bca6SAndre Fischer
906a22bca6SAndre Fischermy $Debug = 1;
916a22bca6SAndre Fischer
926a22bca6SAndre Fischermy $LocalEnvironment = undef;
936a22bca6SAndre Fischermy $GlobalEnvironment = {};
946a22bca6SAndre Fischermy @Missing = ();
956a22bca6SAndre Fischer
966a22bca6SAndre Fischer
976a22bca6SAndre Fischer
986a22bca6SAndre Fischer
996a22bca6SAndre Fischer=head3 ProcessDataFile
1006a22bca6SAndre Fischer
1016a22bca6SAndre Fischer    Read the data file, typically named main/external_libs.lst, find the external
1026a22bca6SAndre Fischer    library tarballs that are not yet present in ext_sources/ and download them.
1036a22bca6SAndre Fischer
1046a22bca6SAndre Fischer=cut
1056a22bca6SAndre Fischersub ProcessDataFile ($)
1066a22bca6SAndre Fischer{
1076a22bca6SAndre Fischer    my $filename = shift;
1086a22bca6SAndre Fischer
1096a22bca6SAndre Fischer    my $destination = $ENV{'TARFILE_LOCATION'};
1106a22bca6SAndre Fischer
1116a22bca6SAndre Fischer    die "can not open data file $filename" if ! -e $filename;
1126a22bca6SAndre Fischer
1136a22bca6SAndre Fischer    my $current_selector_value = 1;
1146a22bca6SAndre Fischer    my @URLHeads = ();
1156a22bca6SAndre Fischer    my @download_requests = ();
1166a22bca6SAndre Fischer
1176a22bca6SAndre Fischer    open my $in, $filename;
1186a22bca6SAndre Fischer    while (my $line = <$in>)
1196a22bca6SAndre Fischer    {
1206a22bca6SAndre Fischer        # Remove leading and trailing space and comments
1216a22bca6SAndre Fischer        $line =~ s/^\s+//;
1226a22bca6SAndre Fischer        $line =~ s/\s+$//;
1236a22bca6SAndre Fischer        $line =~ s/\s*#.*$//;
1246a22bca6SAndre Fischer
1256a22bca6SAndre Fischer        # Ignore empty lines.
1266a22bca6SAndre Fischer        next if $line eq "";
1276a22bca6SAndre Fischer
1286a22bca6SAndre Fischer        # An "if" statement starts a new block.
1296a22bca6SAndre Fischer        if ($line =~ /^\s*if\s*\(\s*(.*?)\s*\)\s*$/)
1306a22bca6SAndre Fischer        {
1316a22bca6SAndre Fischer            ProcessLastBlock();
1326a22bca6SAndre Fischer
1336a22bca6SAndre Fischer            $LocalEnvironment = { 'selector' => $1 };
1346a22bca6SAndre Fischer        }
1356a22bca6SAndre Fischer
1366a22bca6SAndre Fischer        # Lines of the form name = value define a local variable.
1376a22bca6SAndre Fischer        elsif ($line =~ /^\s*(\S+)\s*=\s*(.*?)\s*$/)
1386a22bca6SAndre Fischer        {
1396a22bca6SAndre Fischer            if (defined $LocalEnvironment)
1406a22bca6SAndre Fischer            {
1416a22bca6SAndre Fischer                $LocalEnvironment->{$1} = $2;
1426a22bca6SAndre Fischer            }
1436a22bca6SAndre Fischer            else
1446a22bca6SAndre Fischer            {
1456a22bca6SAndre Fischer                $GlobalEnvironment->{$1} = $2;
1466a22bca6SAndre Fischer            }
1476a22bca6SAndre Fischer        }
1486a22bca6SAndre Fischer        else
1496a22bca6SAndre Fischer        {
1506a22bca6SAndre Fischer            die "can not parse line $line\n";
1516a22bca6SAndre Fischer        }
1526a22bca6SAndre Fischer    }
1536a22bca6SAndre Fischer
1546a22bca6SAndre Fischer    ProcessLastBlock();
1556a22bca6SAndre Fischer
1566a22bca6SAndre Fischer    Download(\@download_requests, \@URLHeads);
1576a22bca6SAndre Fischer}
1586a22bca6SAndre Fischer
1596a22bca6SAndre Fischer
1606a22bca6SAndre Fischer
1616a22bca6SAndre Fischer
1626a22bca6SAndre Fischer=head3 ProcessLastBlock
1636a22bca6SAndre Fischer
1646a22bca6SAndre Fischer    Process the last definition of an external library.
1656a22bca6SAndre Fischer    If there is not last block, true for the first "if" statement, then the call is ignored.
1666a22bca6SAndre Fischer
1676a22bca6SAndre Fischer=cut
1686a22bca6SAndre Fischersub ProcessLastBlock ()
1696a22bca6SAndre Fischer{
1706a22bca6SAndre Fischer    # Return if no block is defined.
1716a22bca6SAndre Fischer    return if ! defined $LocalEnvironment;
1726a22bca6SAndre Fischer
1736a22bca6SAndre Fischer    # Ignore the block if the selector does not match.
1746a22bca6SAndre Fischer    if ( ! EvaluateExpression(SubstituteVariables($LocalEnvironment->{'selector'})))
1756a22bca6SAndre Fischer    {
1766a22bca6SAndre Fischer        printf("ignoring %s because its prerequisites are not fulfilled\n", GetValue('name'));
1776a22bca6SAndre Fischer    }
1786a22bca6SAndre Fischer    else
1796a22bca6SAndre Fischer    {
1806a22bca6SAndre Fischer        my $name = GetValue('name');
18110e20387SAndre Fischer        my $checksum = GetChecksum();
1826a22bca6SAndre Fischer
1830aabba3aSAndre Fischer        if ( ! IsPresent($name, $checksum))
18410e20387SAndre Fischer        {
18510e20387SAndre Fischer            AddDownloadRequest($name, $checksum);
1866a22bca6SAndre Fischer        }
1876a22bca6SAndre Fischer    }
1886a22bca6SAndre Fischer}
1896a22bca6SAndre Fischer
1906a22bca6SAndre Fischer
1916a22bca6SAndre Fischer
1926a22bca6SAndre Fischer
19310e20387SAndre Fischer=head3 AddDownloadRequest($name, $checksum)
1946a22bca6SAndre Fischer
1956a22bca6SAndre Fischer    Add a request for downloading the library $name to @Missing.
1966a22bca6SAndre Fischer    Collect all available URL[1-9] variables as source URLs.
1976a22bca6SAndre Fischer
1986a22bca6SAndre Fischer=cut
19910e20387SAndre Fischersub AddDownloadRequest ($$)
2006a22bca6SAndre Fischer{
20110e20387SAndre Fischer    my ($name, $checksum) = @_;
2026a22bca6SAndre Fischer
2036a22bca6SAndre Fischer    print "adding download request for $name\n";
2046a22bca6SAndre Fischer
2056a22bca6SAndre Fischer    my $urls = [];
2066a22bca6SAndre Fischer    my $url = GetValue('URL');
2076a22bca6SAndre Fischer    push @$urls, SubstituteVariables($url) if (defined $url);
2086a22bca6SAndre Fischer    for (my $i=1; $i<10; ++$i)
2096a22bca6SAndre Fischer    {
2106a22bca6SAndre Fischer        $url = GetValue('URL'.$i);
2116a22bca6SAndre Fischer        next if ! defined $url;
2126a22bca6SAndre Fischer        push @$urls, SubstituteVariables($url);
2136a22bca6SAndre Fischer    }
2146a22bca6SAndre Fischer
21510e20387SAndre Fischer    push @Missing, [$name, $checksum, $urls];
21610e20387SAndre Fischer}
21710e20387SAndre Fischer
21810e20387SAndre Fischer
21910e20387SAndre Fischer
22010e20387SAndre Fischer
22110e20387SAndre Fischer=head3 GetChecksum()
22210e20387SAndre Fischer
22310e20387SAndre Fischer    When either MD5 or SHA1 are variables in the current scope then return
22410e20387SAndre Fischer    a reference to a hash with two entries:
22510e20387SAndre Fischer        'type' is either 'MD5' or 'SHA1', the type or algorithm of the checksum,
22610e20387SAndre Fischer        'value' is the actual checksum
22710e20387SAndre Fischer    Otherwise undef is returned.
22810e20387SAndre Fischer
22910e20387SAndre Fischer=cut
23010e20387SAndre Fischersub GetChecksum()
23110e20387SAndre Fischer{
23210e20387SAndre Fischer    my $checksum = GetValue("MD5");
23310e20387SAndre Fischer    if (defined $checksum && $checksum ne "")
23410e20387SAndre Fischer    {
23510e20387SAndre Fischer        return { 'type' => 'MD5', 'value' => $checksum };
23610e20387SAndre Fischer    }
23710e20387SAndre Fischer    elsif (defined ($checksum=GetValue("SHA1")) && $checksum ne "")
23810e20387SAndre Fischer    {
23910e20387SAndre Fischer        return { 'type' => 'SHA1', 'value' => $checksum };
24010e20387SAndre Fischer    }
24110e20387SAndre Fischer    else
24210e20387SAndre Fischer    {
24310e20387SAndre Fischer        return undef;
24410e20387SAndre Fischer    }
2456a22bca6SAndre Fischer}
2466a22bca6SAndre Fischer
2476a22bca6SAndre Fischer
2486a22bca6SAndre Fischer
2496a22bca6SAndre Fischer
2506a22bca6SAndre Fischer=head3 GetValue($variable_name)
2516a22bca6SAndre Fischer
2526a22bca6SAndre Fischer    Return the value of the variable with name $variable_name from the local
2536a22bca6SAndre Fischer    environment or, if not defined there, the global environment.
2546a22bca6SAndre Fischer
2556a22bca6SAndre Fischer=cut
2566a22bca6SAndre Fischersub GetValue ($)
2576a22bca6SAndre Fischer{
2586a22bca6SAndre Fischer    my $variable_name = shift;
2596a22bca6SAndre Fischer
2606a22bca6SAndre Fischer    my $candidate = $LocalEnvironment->{$variable_name};
2616a22bca6SAndre Fischer    return $candidate if defined $candidate;
2626a22bca6SAndre Fischer
2636a22bca6SAndre Fischer    return $GlobalEnvironment->{$variable_name};
2646a22bca6SAndre Fischer}
2656a22bca6SAndre Fischer
2666a22bca6SAndre Fischer
2676a22bca6SAndre Fischer
2686a22bca6SAndre Fischer=head3 SubstituteVariables($text)
2696a22bca6SAndre Fischer
2706a22bca6SAndre Fischer    Replace all references to variables in $text with the respective variable values.
2716a22bca6SAndre Fischer    This is done repeatedly until no variable reference remains.
2726a22bca6SAndre Fischer
2736a22bca6SAndre Fischer=cut
2746a22bca6SAndre Fischersub SubstituteVariables ($)
2756a22bca6SAndre Fischer{
2766a22bca6SAndre Fischer    my $text = shift;
2776a22bca6SAndre Fischer
2786a22bca6SAndre Fischer    my $infinite_recursion_guard = 100;
2796a22bca6SAndre Fischer    while ($text =~ /^(.*?)\$\(([^)]+)\)(.*)$/)
2806a22bca6SAndre Fischer    {
2816a22bca6SAndre Fischer        my ($head,$name,$tail) = ($1,$2,$3);
2826a22bca6SAndre Fischer        my $value = GetValue($name);
2830aabba3aSAndre Fischer        die "can not evaluate variable $name" if ! defined $value;
2846a22bca6SAndre Fischer        $text = $head.$value.$tail;
2856a22bca6SAndre Fischer
2866a22bca6SAndre Fischer        die "(probably) detected an infinite recursion in variable definitions" if --$infinite_recursion_guard<=0;
2876a22bca6SAndre Fischer    }
2886a22bca6SAndre Fischer
2896a22bca6SAndre Fischer    return $text;
2906a22bca6SAndre Fischer}
2916a22bca6SAndre Fischer
2926a22bca6SAndre Fischer
2936a22bca6SAndre Fischer
2946a22bca6SAndre Fischer
2956a22bca6SAndre Fischer=head3 EvaluateExpression($expression)
2966a22bca6SAndre Fischer
2976a22bca6SAndre Fischer    Evaluate the $expression of an "if" statement to either 0 or 1.  It can
2986a22bca6SAndre Fischer    be a single term (see EvaluateTerm for a description), or several terms
2996a22bca6SAndre Fischer    separated by either all ||s or &&s.  A term can also be an expression
3006a22bca6SAndre Fischer    enclosed in parantheses.
3016a22bca6SAndre Fischer
3026a22bca6SAndre Fischer=cut
3036a22bca6SAndre Fischersub EvaluateExpression ($)
3046a22bca6SAndre Fischer{
3056a22bca6SAndre Fischer    my $expression = shift;
3066a22bca6SAndre Fischer
3076a22bca6SAndre Fischer    # Evaluate sub expressions enclosed in parantheses.
3086a22bca6SAndre Fischer    while ($expression =~ /^(.*)\(([^\(\)]+)\)(.*)$/)
3096a22bca6SAndre Fischer    {
3106a22bca6SAndre Fischer        $expression = $1 . (EvaluateExpression($2) ? " true " : " false ") . $3;
3116a22bca6SAndre Fischer    }
3126a22bca6SAndre Fischer
3136a22bca6SAndre Fischer    if ($expression =~ /&&/ && $expression =~ /\|\|/)
3146a22bca6SAndre Fischer    {
3156a22bca6SAndre Fischer        die "expression can contain either && or || but not both at the same time";
3166a22bca6SAndre Fischer    }
3176a22bca6SAndre Fischer    elsif ($expression =~ /&&/)
3186a22bca6SAndre Fischer    {
3196a22bca6SAndre Fischer        foreach my $term (split (/\s*&&\s*/,$expression))
3206a22bca6SAndre Fischer        {
3216a22bca6SAndre Fischer            return 0 if ! EvaluateTerm($term);
3226a22bca6SAndre Fischer        }
3236a22bca6SAndre Fischer        return 1;
3246a22bca6SAndre Fischer    }
3256a22bca6SAndre Fischer    elsif ($expression =~ /\|\|/)
3266a22bca6SAndre Fischer    {
3276a22bca6SAndre Fischer        foreach my $term (split (/\s*\|\|\s*/,$expression))
3286a22bca6SAndre Fischer        {
3296a22bca6SAndre Fischer            return 1 if EvaluateTerm($term);
3306a22bca6SAndre Fischer        }
3316a22bca6SAndre Fischer        return 0;
3326a22bca6SAndre Fischer    }
3336a22bca6SAndre Fischer    else
3346a22bca6SAndre Fischer    {
3356a22bca6SAndre Fischer        return EvaluateTerm($expression);
3366a22bca6SAndre Fischer    }
3376a22bca6SAndre Fischer}
3386a22bca6SAndre Fischer
3396a22bca6SAndre Fischer
3406a22bca6SAndre Fischer
3416a22bca6SAndre Fischer
3426a22bca6SAndre Fischer=head3 EvaluateTerm($term)
3436a22bca6SAndre Fischer
3446a22bca6SAndre Fischer    Evaluate the $term to either 0 or 1.
3456a22bca6SAndre Fischer    A term is either the literal "true", which evaluates to 1, or an expression
3466a22bca6SAndre Fischer    of the form NAME=VALUE or NAME!=VALUE.  NAME is the name of an environment
3476a22bca6SAndre Fischer    variable and VALUE any string.  VALUE may be empty.
3486a22bca6SAndre Fischer
3496a22bca6SAndre Fischer=cut
3506a22bca6SAndre Fischersub EvaluateTerm ($)
3516a22bca6SAndre Fischer{
3526a22bca6SAndre Fischer    my $term = shift;
3536a22bca6SAndre Fischer
3546a22bca6SAndre Fischer    if ($term =~ /^\s*([a-zA-Z_0-9]+)\s*(==|!=)\s*(.*)\s*$/)
3556a22bca6SAndre Fischer    {
3566a22bca6SAndre Fischer        my ($variable_name, $operator, $given_value) = ($1,$2,$3);
3576a22bca6SAndre Fischer        my $variable_value = $ENV{$variable_name};
3586a22bca6SAndre Fischer        $variable_value = "" if ! defined $variable_value;
3596a22bca6SAndre Fischer
3606a22bca6SAndre Fischer        if ($operator eq "==")
3616a22bca6SAndre Fischer        {
3626a22bca6SAndre Fischer            return $variable_value eq $given_value;
3636a22bca6SAndre Fischer        }
3646a22bca6SAndre Fischer        elsif ($operator eq "!=")
3656a22bca6SAndre Fischer        {
3666a22bca6SAndre Fischer            return $variable_value ne $given_value;
3676a22bca6SAndre Fischer        }
3686a22bca6SAndre Fischer        else
3696a22bca6SAndre Fischer        {
3706a22bca6SAndre Fischer            die "unknown operator in term $term";
3716a22bca6SAndre Fischer        }
3726a22bca6SAndre Fischer    }
3736a22bca6SAndre Fischer    elsif ($term =~ /^\s*true\s*$/i)
3746a22bca6SAndre Fischer    {
3756a22bca6SAndre Fischer        return 1;
3766a22bca6SAndre Fischer    }
3776a22bca6SAndre Fischer    elsif ($term =~ /^\s*false\s*$/i)
3786a22bca6SAndre Fischer    {
3796a22bca6SAndre Fischer        return 0;
3806a22bca6SAndre Fischer    }
3816a22bca6SAndre Fischer    else
3826a22bca6SAndre Fischer    {
3836a22bca6SAndre Fischer        die "term $term is not of the form <environment-variable> (=|==) <value>";
3846a22bca6SAndre Fischer    }
3856a22bca6SAndre Fischer}
3866a22bca6SAndre Fischer
3876a22bca6SAndre Fischer
3886a22bca6SAndre Fischer
3896a22bca6SAndre Fischer
39010e20387SAndre Fischer=head IsPresent($name, $given_checksum)
3916a22bca6SAndre Fischer
3926a22bca6SAndre Fischer    Check if an external library tar ball with the basename $name already
3936a22bca6SAndre Fischer    exists in the target directory TARFILE_LOCATION.  The basename is
39410e20387SAndre Fischer    prefixed with the MD5 or SHA1 checksum.
39510e20387SAndre Fischer    If the file exists then its checksum is compared to the given one.
3966a22bca6SAndre Fischer
3976a22bca6SAndre Fischer=cut
3986a22bca6SAndre Fischersub IsPresent ($$)
3996a22bca6SAndre Fischer{
40010e20387SAndre Fischer    my ($name, $given_checksum) = @_;
4016a22bca6SAndre Fischer
40210e20387SAndre Fischer    my $filename = File::Spec->catfile($ENV{'TARFILE_LOCATION'}, $given_checksum->{'value'}."-".$name);
40310e20387SAndre Fischer    return 0 unless -f $filename;
4046a22bca6SAndre Fischer
40510e20387SAndre Fischer    # File exists.  Check if its checksum is correct.
40610e20387SAndre Fischer    my $checksum;
4070aabba3aSAndre Fischer    if ( ! defined $given_checksum)
4080aabba3aSAndre Fischer    {
4090aabba3aSAndre Fischer        print "no checksum given, can not verify\n";
4100aabba3aSAndre Fischer        return 1;
4110aabba3aSAndre Fischer    }
4120aabba3aSAndre Fischer    elsif ($given_checksum->{'type'} eq "MD5")
41310e20387SAndre Fischer    {
4146a22bca6SAndre Fischer        my $md5 = Digest::MD5->new();
4156a22bca6SAndre Fischer        open my $in, $filename;
4166a22bca6SAndre Fischer        $md5->addfile($in);
41710e20387SAndre Fischer        $checksum = $md5->hexdigest();
41810e20387SAndre Fischer    }
41910e20387SAndre Fischer    elsif ($given_checksum->{'type'} eq "SHA1")
4206a22bca6SAndre Fischer    {
42110e20387SAndre Fischer        my $sha1 = Digest::SHA->new("1");
42210e20387SAndre Fischer        open my $in, $filename;
42310e20387SAndre Fischer        $sha1->addfile($in);
42410e20387SAndre Fischer        $checksum = $sha1->hexdigest();
42510e20387SAndre Fischer    }
42610e20387SAndre Fischer    else
42710e20387SAndre Fischer    {
42810e20387SAndre Fischer        die "unsupported checksum type (not MD5 or SHA1)";
42910e20387SAndre Fischer    }
43010e20387SAndre Fischer
43110e20387SAndre Fischer    if ($given_checksum->{'value'} ne $checksum)
43210e20387SAndre Fischer    {
43310e20387SAndre Fischer        # Checksum does not match.  Delete the file.
43410e20387SAndre Fischer        print "$name exists, but checksum does not match => deleting\n";
4350aabba3aSAndre Fischer        unlink($filename);
4366a22bca6SAndre Fischer        return 0;
4376a22bca6SAndre Fischer    }
4386a22bca6SAndre Fischer    else
4396a22bca6SAndre Fischer    {
44010e20387SAndre Fischer        printf("%s exists, %s checksum is OK\n", $name, $given_checksum->{'type'});
4416a22bca6SAndre Fischer        return 1;
4426a22bca6SAndre Fischer    }
4436a22bca6SAndre Fischer}
4446a22bca6SAndre Fischer
4456a22bca6SAndre Fischer
4466a22bca6SAndre Fischer
4476a22bca6SAndre Fischer
4486a22bca6SAndre Fischer=head3 Download
4496a22bca6SAndre Fischer
4506a22bca6SAndre Fischer    Download a set of files specified by @Missing.
4516a22bca6SAndre Fischer
45210e20387SAndre Fischer    For http URLs there may be an optional checksum.  If it is present then downloaded
45310e20387SAndre Fischer    files that do not match that checksum lead to abortion of the current process.
4546a22bca6SAndre Fischer    Files that have already been downloaded are not downloaded again.
4556a22bca6SAndre Fischer
4566a22bca6SAndre Fischer=cut
4576a22bca6SAndre Fischersub Download ()
4586a22bca6SAndre Fischer{
4596a22bca6SAndre Fischer    my $download_path = $ENV{'TARFILE_LOCATION'};
4606a22bca6SAndre Fischer
4616a22bca6SAndre Fischer    if (scalar @Missing > 0)
4626a22bca6SAndre Fischer    {
4636a22bca6SAndre Fischer        printf("downloading %d missing tar ball%s to %s\n",
4646a22bca6SAndre Fischer               scalar @Missing, scalar @Missing>0 ? "s" : "",
4656a22bca6SAndre Fischer               $download_path);
4666a22bca6SAndre Fischer    }
4676a22bca6SAndre Fischer    else
4686a22bca6SAndre Fischer    {
4696a22bca6SAndre Fischer        print "all external libraries present\n";
4706a22bca6SAndre Fischer        return;
4716a22bca6SAndre Fischer    }
4726a22bca6SAndre Fischer
4736a22bca6SAndre Fischer    # Download the missing files.
474*489cdf4fSarielch    my $all_downloaded = 1;
4756a22bca6SAndre Fischer    for my $item (@Missing)
4766a22bca6SAndre Fischer    {
47710e20387SAndre Fischer        my ($name, $checksum, $urls) = @$item;
4786a22bca6SAndre Fischer
479*489cdf4fSarielch        my $downloaded = 0;
4806a22bca6SAndre Fischer        foreach my $url (@$urls)
4816a22bca6SAndre Fischer        {
482*489cdf4fSarielch            $downloaded = DownloadFile(
4830aabba3aSAndre Fischer                defined $checksum
4840aabba3aSAndre Fischer                    ? $checksum->{'value'}."-".$name
4850aabba3aSAndre Fischer                    : $name,
4860aabba3aSAndre Fischer                $url,
4870aabba3aSAndre Fischer                $checksum);
488*489cdf4fSarielch            last if $downloaded
4896a22bca6SAndre Fischer        }
490*489cdf4fSarielch        $all_downloaded &&= $downloaded;
4916a22bca6SAndre Fischer    }
492*489cdf4fSarielch    die "some needed files could not be downloaded!" if !$all_downloaded;
4936a22bca6SAndre Fischer}
4946a22bca6SAndre Fischer
4956a22bca6SAndre Fischer
4966a22bca6SAndre Fischer
4976a22bca6SAndre Fischer
49810e20387SAndre Fischer=head3 DownloadFile($name,$URL,$checksum)
4996a22bca6SAndre Fischer
5006a22bca6SAndre Fischer    Download a single external library tarball.  It origin is given by $URL.
50110e20387SAndre Fischer    Its destination is $(TARFILE_LOCATION)/$checksum-$name.
5026a22bca6SAndre Fischer
5036a22bca6SAndre Fischer=cut
5046a22bca6SAndre Fischersub DownloadFile ($$$)
5056a22bca6SAndre Fischer{
5066a22bca6SAndre Fischer    my $name = shift;
5076a22bca6SAndre Fischer    my $URL = shift;
50810e20387SAndre Fischer    my $checksum = shift;
5096a22bca6SAndre Fischer
5106a22bca6SAndre Fischer    my $filename = File::Spec->catfile($ENV{'TARFILE_LOCATION'}, $name);
5116a22bca6SAndre Fischer
5126a22bca6SAndre Fischer    my $temporary_filename = $filename . ".part";
5136a22bca6SAndre Fischer
5146a22bca6SAndre Fischer    print "downloading to $temporary_filename\n";
5150aabba3aSAndre Fischer    my $out;
5160aabba3aSAndre Fischer    open $out, ">$temporary_filename";
5176a22bca6SAndre Fischer    binmode($out);
5186a22bca6SAndre Fischer
51910e20387SAndre Fischer    # Prepare checksum
52010e20387SAndre Fischer    my $digest;
52110e20387SAndre Fischer    if (defined $checksum && $checksum->{'type'} eq "SHA1")
52210e20387SAndre Fischer    {
52310e20387SAndre Fischer        # Use SHA1 only when explicitly requested (by the presence of a "SHA1=..." line.)
52410e20387SAndre Fischer        $digest = Digest::SHA->new("1");
52510e20387SAndre Fischer    }
52610e20387SAndre Fischer    elsif ( ! defined $checksum || $checksum->{'type'} eq "MD5")
52710e20387SAndre Fischer    {
52810e20387SAndre Fischer        # Use MD5 when explicitly requested or when no checksum type is given.
52910e20387SAndre Fischer        $digest = Digest::MD5->new();
53010e20387SAndre Fischer    }
53110e20387SAndre Fischer    else
53210e20387SAndre Fischer    {
53310e20387SAndre Fischer        die "checksum type ".$checksum->{'type'}." is not supported";
53410e20387SAndre Fischer    }
5356a22bca6SAndre Fischer
5366a22bca6SAndre Fischer    # Download the extension.
537f253223cSAndre Fischer    my $success = 0;
538*489cdf4fSarielch
539*489cdf4fSarielch    my $agent = LWP::UserAgent->new();
540*489cdf4fSarielch    $agent->env_proxy;
541*489cdf4fSarielch    my $response = $agent->get($URL);
542*489cdf4fSarielch
543*489cdf4fSarielch    $success = $response->is_success;
544f253223cSAndre Fischer    if ($success)
545f253223cSAndre Fischer    {
546*489cdf4fSarielch        my $content = $response->content;
547f253223cSAndre Fischer        open $out, ">$temporary_filename";
548f253223cSAndre Fischer        binmode($out);
549f253223cSAndre Fischer        print $out $content;
550f253223cSAndre Fischer        $digest->add($content);
551f253223cSAndre Fischer    }
552f253223cSAndre Fischer    else
553f253223cSAndre Fischer    {
554*489cdf4fSarielch        print "download from $URL failed (" . $response->status_line . ")\n";
555f253223cSAndre Fischer    }
556*489cdf4fSarielch    close($out);
5576a22bca6SAndre Fischer
558*489cdf4fSarielch    # When download was successful then check the checksum and rename the .part file
5596a22bca6SAndre Fischer    # into the actual extension name.
560f253223cSAndre Fischer    if ($success)
5616a22bca6SAndre Fischer    {
56210e20387SAndre Fischer        my $file_checksum = $digest->hexdigest();
56310e20387SAndre Fischer        if (defined $checksum)
5646a22bca6SAndre Fischer        {
56510e20387SAndre Fischer            if ($checksum->{'value'} eq $file_checksum)
5666a22bca6SAndre Fischer            {
56710e20387SAndre Fischer                printf("%s checksum is OK\n", $checksum->{'type'});
5686a22bca6SAndre Fischer            }
5696a22bca6SAndre Fischer            else
5706a22bca6SAndre Fischer            {
5716a22bca6SAndre Fischer                unlink($temporary_filename);
57210e20387SAndre Fischer                printf("    %s checksum does not match (%s instead of %s)\n",
573a90d3987SAndrea Pescetti                       $checksum->{'type'},
57410e20387SAndre Fischer                       $file_checksum,
575a90d3987SAndrea Pescetti                       $checksum->{'value'});
5766a22bca6SAndre Fischer                return 0;
5776a22bca6SAndre Fischer            }
5786a22bca6SAndre Fischer        }
5796a22bca6SAndre Fischer        else
5806a22bca6SAndre Fischer        {
58110e20387SAndre Fischer            # The datafile does not contain a checksum to match against.
58210e20387SAndre Fischer            # Display the one that was calculated for the downloaded file so that
58310e20387SAndre Fischer            # it can be integrated manually into the data file.
58410e20387SAndre Fischer            printf("checksum not given, md5 of file is %s\n", $file_checksum);
58510e20387SAndre Fischer            $filename = File::Spec->catfile($ENV{'TARFILE_LOCATION'}, $file_checksum . "-" . $name);
5866a22bca6SAndre Fischer        }
5876a22bca6SAndre Fischer
5886a22bca6SAndre Fischer        rename($temporary_filename, $filename) || die "can not rename $temporary_filename to $filename";
5896a22bca6SAndre Fischer        return 1;
5906a22bca6SAndre Fischer    }
5916a22bca6SAndre Fischer    else
5926a22bca6SAndre Fischer    {
5936a22bca6SAndre Fischer        unlink($temporary_filename);
5946a22bca6SAndre Fischer        print "    download failed\n";
5956a22bca6SAndre Fischer        return 0;
5966a22bca6SAndre Fischer    }
5976a22bca6SAndre Fischer}
5986a22bca6SAndre Fischer
5996a22bca6SAndre Fischer
6006a22bca6SAndre Fischer
6016a22bca6SAndre Fischer
6026a22bca6SAndre Fischer=head3 CheckDownloadDestination ()
6036a22bca6SAndre Fischer
6046a22bca6SAndre Fischer    Make sure that the download destination $TARFILE_LOCATION does exist.  If
6056a22bca6SAndre Fischer    not, then the directory is created.
6066a22bca6SAndre Fischer
6076a22bca6SAndre Fischer=cut
6086a22bca6SAndre Fischersub CheckDownloadDestination ()
6096a22bca6SAndre Fischer{
6106a22bca6SAndre Fischer    my $destination = $ENV{'TARFILE_LOCATION'};
6116a22bca6SAndre Fischer    die "ERROR: no destination defined! please set TARFILE_LOCATION!" if ($destination eq "");
6126a22bca6SAndre Fischer
6136a22bca6SAndre Fischer    if ( ! -d $destination)
6146a22bca6SAndre Fischer    {
6156a22bca6SAndre Fischer        File::Path::make_path($destination);
6166a22bca6SAndre Fischer        die "ERROR: can't create \$TARFILE_LOCATION" if  ! -d $destination;
6176a22bca6SAndre Fischer    }
6186a22bca6SAndre Fischer}
6196a22bca6SAndre Fischer
6206a22bca6SAndre Fischer
6216a22bca6SAndre Fischer
6226a22bca6SAndre Fischer
6236a22bca6SAndre Fischer=head3 ProvideSpecialTarball ($url,$name,$name_converter)
6246a22bca6SAndre Fischer
6256a22bca6SAndre Fischer    A few tarballs need special handling.  That is done here.
6266a22bca6SAndre Fischer
6276a22bca6SAndre Fischer=cut
6286a22bca6SAndre Fischersub ProvideSpecialTarball ($$$)
6296a22bca6SAndre Fischer{
6306a22bca6SAndre Fischer    my $url = shift;
6316a22bca6SAndre Fischer    my $name = shift;
6326a22bca6SAndre Fischer    my $name_converter = shift;
6336a22bca6SAndre Fischer
6346a22bca6SAndre Fischer    return unless defined $url && $url ne "";
6356a22bca6SAndre Fischer
6366a22bca6SAndre Fischer    # See if we can find the executable.
6376a22bca6SAndre Fischer    my ($SOLARENV,$OUTPATH,$EXEEXT) =  ($ENV{'SOLARENV'},$ENV{'OUTPATH'},$ENV{'EXEEXT'});
6386a22bca6SAndre Fischer    $SOLARENV = "" unless defined $SOLARENV;
6396a22bca6SAndre Fischer    $OUTPATH = "" unless defined $OUTPATH;
6406a22bca6SAndre Fischer    $EXEEXT = "" unless defined $EXEEXT;
6416a22bca6SAndre Fischer    if (-x File::Spec->catfile($SOLARENV, $OUTPATH, "bin", $name.$EXEEXT))
6426a22bca6SAndre Fischer    {
6436a22bca6SAndre Fischer        print "found $name executable\n";
6446a22bca6SAndre Fischer        return;
6456a22bca6SAndre Fischer    }
6466a22bca6SAndre Fischer
6476a22bca6SAndre Fischer    # Download the source from the URL.
6486a22bca6SAndre Fischer    my $basename = basename(URI->new($url)->path());
6496a22bca6SAndre Fischer    die unless defined $basename;
6506a22bca6SAndre Fischer
6516a22bca6SAndre Fischer    if (defined $name_converter)
6526a22bca6SAndre Fischer    {
6536a22bca6SAndre Fischer        $basename = &{$name_converter}($basename);
6546a22bca6SAndre Fischer    }
6556a22bca6SAndre Fischer
6566a22bca6SAndre Fischer    # Has the source tar ball already been downloaded?
6576a22bca6SAndre Fischer    my @candidates = glob(File::Spec->catfile($ENV{'TARFILE_LOCATION'}, "*-" . $basename));
6586a22bca6SAndre Fischer    if (scalar @candidates > 0)
6596a22bca6SAndre Fischer    {
6606a22bca6SAndre Fischer        # Yes.
6616a22bca6SAndre Fischer        print "$basename exists\n";
6626a22bca6SAndre Fischer        return;
6636a22bca6SAndre Fischer    }
6646a22bca6SAndre Fischer    else
6656a22bca6SAndre Fischer    {
6666a22bca6SAndre Fischer        # No, download it.
6676a22bca6SAndre Fischer        print "downloading $basename\n";
6686a22bca6SAndre Fischer        DownloadFile($basename, $url, undef);
6696a22bca6SAndre Fischer    }
6706a22bca6SAndre Fischer}
6716a22bca6SAndre Fischer
6726a22bca6SAndre Fischer
6736a22bca6SAndre Fischer
6746a22bca6SAndre Fischer
6756a22bca6SAndre Fischer
6766a22bca6SAndre Fischer# The main() functionality.
6776a22bca6SAndre Fischer
6786a22bca6SAndre Fischerdie "usage: $0 <data-file-name>" if scalar @ARGV != 1;
6796a22bca6SAndre Fischermy $data_file = $ARGV[0];
6806a22bca6SAndre FischerCheckDownloadDestination();
6816a22bca6SAndre FischerProcessDataFile($data_file);
6826a22bca6SAndre FischerProvideSpecialTarball($ENV{'DMAKE_URL'}, "dmake", undef);
6836a22bca6SAndre FischerProvideSpecialTarball(
6846a22bca6SAndre Fischer    $ENV{'EPM_URL'},
6856a22bca6SAndre Fischer    "epm",
6866a22bca6SAndre Fischer    sub{$_[0]=~s/-source//; return $_[0]});
687