| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package LedgerSMB::Installer v0.999.11; |
|
2
|
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
241048
|
use v5.20; |
|
|
1
|
|
|
|
|
3
|
|
|
4
|
1
|
|
|
1
|
|
457
|
use experimental qw(signatures); |
|
|
1
|
|
|
|
|
3163
|
|
|
|
1
|
|
|
|
|
6
|
|
|
5
|
|
|
|
|
|
|
|
|
6
|
1
|
|
|
1
|
|
148
|
use Carp qw( croak ); |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
52
|
|
|
7
|
1
|
|
|
1
|
|
434
|
use CPAN::Meta::Requirements; |
|
|
1
|
|
|
|
|
6671
|
|
|
|
1
|
|
|
|
|
45
|
|
|
8
|
1
|
|
|
1
|
|
1092
|
use English; |
|
|
1
|
|
|
|
|
3785
|
|
|
|
1
|
|
|
|
|
8
|
|
|
9
|
1
|
|
|
1
|
|
649
|
use File::Path qw( make_path remove_tree ); |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
98
|
|
|
10
|
1
|
|
|
1
|
|
7
|
use File::Spec; |
|
|
1
|
|
|
|
|
6
|
|
|
|
1
|
|
|
|
|
31
|
|
|
11
|
1
|
|
|
1
|
|
1047
|
use File::Temp qw( tempfile ); |
|
|
1
|
|
|
|
|
25580
|
|
|
|
1
|
|
|
|
|
117
|
|
|
12
|
1
|
|
|
1
|
|
894
|
use Getopt::Long qw(GetOptionsFromArray); |
|
|
1
|
|
|
|
|
16458
|
|
|
|
1
|
|
|
|
|
5
|
|
|
13
|
1
|
|
|
1
|
|
1221
|
use HTTP::Tiny; |
|
|
1
|
|
|
|
|
55806
|
|
|
|
1
|
|
|
|
|
66
|
|
|
14
|
1
|
|
|
1
|
|
11
|
use IO::Handle; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
48
|
|
|
15
|
1
|
|
|
1
|
|
894
|
use JSON::PP; |
|
|
1
|
|
|
|
|
20110
|
|
|
|
1
|
|
|
|
|
127
|
|
|
16
|
1
|
|
|
1
|
|
13
|
use List::Util qw(uniq); |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
81
|
|
|
17
|
1
|
|
|
1
|
|
4644
|
use Module::CoreList; |
|
|
1
|
|
|
|
|
255576
|
|
|
|
1
|
|
|
|
|
341
|
|
|
18
|
1
|
|
|
1
|
|
74
|
use version; |
|
|
1
|
|
|
|
|
11
|
|
|
|
1
|
|
|
|
|
8
|
|
|
19
|
|
|
|
|
|
|
|
|
20
|
1
|
|
|
1
|
|
892
|
use Log::Any qw($log); |
|
|
1
|
|
|
|
|
11274
|
|
|
|
1
|
|
|
|
|
29
|
|
|
21
|
1
|
|
|
1
|
|
3722
|
use Log::Any::Adapter; |
|
|
1
|
|
|
|
|
455
|
|
|
|
1
|
|
|
|
|
5
|
|
|
22
|
1
|
|
|
1
|
|
616
|
use Module::CPANfile; |
|
|
1
|
|
|
|
|
15479
|
|
|
|
1
|
|
|
|
|
56
|
|
|
23
|
|
|
|
|
|
|
|
|
24
|
1
|
|
|
1
|
|
1646
|
use LedgerSMB::Installer::Configuration; |
|
|
1
|
|
|
|
|
4
|
|
|
|
1
|
|
|
|
|
9056
|
|
|
25
|
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
my $INSTALLER_VERSION = 'version CLONED'; # not intended to be the module version |
|
27
|
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
my $http = HTTP::Tiny->new( agent => 'LedgerSMB-Installer/0.1' ); |
|
29
|
|
|
|
|
|
|
my $json = JSON::PP->new->canonical; |
|
30
|
|
|
|
|
|
|
|
|
31
|
0
|
|
|
0
|
|
|
sub _post_boot_configure($class, $dss, $config) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
32
|
0
|
|
|
|
|
|
Log::Any::Adapter->set('Stdout', log_level => $config->loglevel); |
|
33
|
|
|
|
|
|
|
} |
|
34
|
|
|
|
|
|
|
|
|
35
|
0
|
|
|
0
|
|
|
sub _boot($class, $args, $options) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
36
|
0
|
|
|
|
|
|
my $dss = $class->_load_dist_support; |
|
37
|
0
|
|
|
|
|
|
my $config = LedgerSMB::Installer::Configuration->new( |
|
38
|
|
|
|
|
|
|
sys_pkgs => ($EFFECTIVE_USER_ID == 0) |
|
39
|
|
|
|
|
|
|
); |
|
40
|
|
|
|
|
|
|
|
|
41
|
0
|
|
|
|
|
|
GetOptionsFromArray( |
|
42
|
|
|
|
|
|
|
$args, |
|
43
|
|
|
|
|
|
|
$config->option_callbacks( $options ), |
|
44
|
|
|
|
|
|
|
); |
|
45
|
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
# normalize $installpath (at least cpanm needs that) |
|
47
|
|
|
|
|
|
|
# assume $locallib to be inside $installpath |
|
48
|
0
|
|
|
|
|
|
$config->normalize_paths; |
|
49
|
|
|
|
|
|
|
|
|
50
|
0
|
|
|
|
|
|
$class->_post_boot_configure( $dss, $config ); |
|
51
|
0
|
|
|
|
|
|
return ($dss, $config); |
|
52
|
|
|
|
|
|
|
} |
|
53
|
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
my $signing_key_data = <<'KEY_DATA_END'; |
|
55
|
|
|
|
|
|
|
-----BEGIN PGP PUBLIC KEY BLOCK----- |
|
56
|
|
|
|
|
|
|
Comment: Hostname: fr.pgpkeys.eu |
|
57
|
|
|
|
|
|
|
Version: Hockeypuck 2.2.4 |
|
58
|
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
xsBNBEYTN1ABCACuVLUCasiD4KnetfEHtMo+qDjKbcWauIkw/KU+gj8kN7Y+E3ye |
|
60
|
|
|
|
|
|
|
cLxtY1gJlZp0iIgnZ6aw7bPCleTXbBfXXbMyLs0+CN9cfCyXB5TU4nlh16or/vlE |
|
61
|
|
|
|
|
|
|
C+0WjEuD9Qznm805RXJGvIRPobXrxcO7aB7pT3NkKj2z//9D0w0uC+BDEl0uihZG |
|
62
|
|
|
|
|
|
|
+fIbb2ihPWO/r9ghHhyMRTibSRye09owhVpr/5gYRFtvp7OsYHaLhUD3+WXPhGIZ |
|
63
|
|
|
|
|
|
|
U7YqBFRumDNR+S8EewuKj0CBxCtC+iSFyR/fNPh50FbtgqY/9qLmZa/u42oyjrCl |
|
64
|
|
|
|
|
|
|
TM+ALCM+PutEPVEIyVoO4jluoZ8yG4IFVxyxABEBAAHNZUxlZGdlclNNQiBDb3Jl |
|
65
|
|
|
|
|
|
|
IFRlYW0gKFRoaXMgaXMgdGhlIGtleSB1c2VkIGZvciBwYWNrYWdlIHNpZ25pbmcu |
|
66
|
|
|
|
|
|
|
KSA8bGVkZ2VyLXNtYi1jb3JlQGxpc3RzLmxlZGdlcnNtYi5vcmc+wsB2BBMBAgAg |
|
67
|
|
|
|
|
|
|
BQJGEzdQAhsDBgsJCAcDAgQVAggDBBYCAwECHgECF4AACgkQOaYpVY2grxAaDwgA |
|
68
|
|
|
|
|
|
|
kp2LZNyrZDom1tBwR3aisfYFkkiuFrUZUn+fYwr7wnkq06gpYGZrNgdcanSxjGoD |
|
69
|
|
|
|
|
|
|
wf2kMeCwWAD6sao824onboJWRi1LR3Q5RKIAvYBeLZ75/hYSy3JP/YLMl047IWcU |
|
70
|
|
|
|
|
|
|
CMaqZfmXPHvkOdUblPxCaKJM8c9Sb6KxGzu4BU2Y97TvQjn9xNbq4t7Pgpu5RORH |
|
71
|
|
|
|
|
|
|
Hp96V+kZXGxzQo0gCE+6dr1JFZeTKmTkGSvkchndn5UVLguLIg9kCxxcDiS7hmTx |
|
72
|
|
|
|
|
|
|
yB+b0+kPgRUb0YVp6QqFq7O8fvzhsW8sYopBJsbArODS2qeMiwPUnKs+7qkZg8Jd |
|
73
|
|
|
|
|
|
|
j2QoHZCY06NU6yvqq/CIt8JGBBARAgAGBQJIx9P/AAoJEHc352JrCredRJwAnRFe |
|
74
|
|
|
|
|
|
|
UDqpUCw368z/Ht6FDInXnC2kAJ995NXymKbQVg478EdOrJT8h1SPRA== |
|
75
|
|
|
|
|
|
|
=a/2y |
|
76
|
|
|
|
|
|
|
-----END PGP PUBLIC KEY BLOCK----- |
|
77
|
|
|
|
|
|
|
KEY_DATA_END |
|
78
|
|
|
|
|
|
|
|
|
79
|
0
|
|
|
0
|
|
|
sub _build_install_tree($class, $dss, $config, $installpath, $version) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
80
|
0
|
|
|
|
|
|
my $archive = "ledgersmb-$version.tar.gz"; |
|
81
|
|
|
|
|
|
|
|
|
82
|
0
|
0
|
|
|
|
|
if (not -d $installpath ) { |
|
83
|
0
|
|
|
|
|
|
$log->info( "Creating installation path $installpath" ); |
|
84
|
0
|
|
|
|
|
|
make_path( $installpath ); # croaks on fatal errors |
|
85
|
|
|
|
|
|
|
} |
|
86
|
|
|
|
|
|
|
|
|
87
|
0
|
|
|
|
|
|
$log->info( "Downloading release tarball $archive" ); |
|
88
|
0
|
|
|
|
|
|
$class->_download( $installpath, $version ); |
|
89
|
|
|
|
|
|
|
|
|
90
|
0
|
|
|
|
|
|
$log->info( "Verifying tarball against gpg public key & signature" ); |
|
91
|
0
|
|
|
|
|
|
my $downloaded_tar = File::Spec->catfile( $installpath, $archive ); |
|
92
|
0
|
0
|
|
|
|
|
$dss->verify_sig( $installpath, |
|
93
|
|
|
|
|
|
|
$downloaded_tar, |
|
94
|
|
|
|
|
|
|
"$downloaded_tar.asc", |
|
95
|
|
|
|
|
|
|
$signing_key_data ) |
|
96
|
|
|
|
|
|
|
if $config->verify_sig; |
|
97
|
|
|
|
|
|
|
|
|
98
|
0
|
|
|
|
|
|
$log->info( "Extracting release tarball" ); |
|
99
|
0
|
|
|
|
|
|
$dss->untar( $downloaded_tar, |
|
100
|
|
|
|
|
|
|
$installpath, |
|
101
|
|
|
|
|
|
|
no_same_owner => 1, |
|
102
|
|
|
|
|
|
|
strip_components => 1 ); |
|
103
|
0
|
|
|
|
|
|
$config->cpanfile_path( File::Spec->catfile( $installpath, 'cpanfile' ) ); |
|
104
|
|
|
|
|
|
|
|
|
105
|
0
|
|
|
|
|
|
$log->info( "Removing extracted release tarball" ); |
|
106
|
|
|
|
|
|
|
# croaks on fatal errors: |
|
107
|
0
|
|
|
|
|
|
remove_tree( $downloaded_tar, "$downloaded_tar.asc" ); |
|
108
|
|
|
|
|
|
|
} |
|
109
|
|
|
|
|
|
|
|
|
110
|
0
|
|
|
0
|
|
|
sub _get_cpanfile($class, $config) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
111
|
0
|
0
|
|
|
|
|
return $config->cpanfile if $config->cpanfile; |
|
112
|
|
|
|
|
|
|
|
|
113
|
0
|
|
|
|
|
|
my $cpanfile; |
|
114
|
0
|
0
|
|
|
|
|
if ($config->cpanfile_path) { |
|
115
|
0
|
|
|
|
|
|
$cpanfile = $config->cpanfile_path; |
|
116
|
|
|
|
|
|
|
} |
|
117
|
|
|
|
|
|
|
else { |
|
118
|
0
|
|
|
|
|
|
my $url = |
|
119
|
|
|
|
|
|
|
sprintf("https://raw.githubusercontent.com/ledgersmb/LedgerSMB/refs/tags/%s/cpanfile", |
|
120
|
|
|
|
|
|
|
$config->effective_version); |
|
121
|
|
|
|
|
|
|
|
|
122
|
0
|
|
|
|
|
|
my $response = $http->get( $url ); |
|
123
|
0
|
0
|
|
|
|
|
unless ($response->{success}) { |
|
124
|
0
|
|
|
|
|
|
die $log->fatal("Unable to get '$url' from GitHub: $response->{content}"); |
|
125
|
|
|
|
|
|
|
} |
|
126
|
|
|
|
|
|
|
|
|
127
|
0
|
|
|
|
|
|
my ($fh, $fn) = tempfile(); |
|
128
|
0
|
|
|
|
|
|
print $fh $response->{content}; |
|
129
|
0
|
|
|
|
|
|
$fh->flush; |
|
130
|
0
|
|
|
|
|
|
$cpanfile = $fn; |
|
131
|
|
|
|
|
|
|
} |
|
132
|
|
|
|
|
|
|
|
|
133
|
0
|
|
|
|
|
|
my $decl = Module::CPANfile->load( $cpanfile ); |
|
134
|
0
|
|
|
|
|
|
$config->cpanfile( $decl ); |
|
135
|
|
|
|
|
|
|
|
|
136
|
0
|
|
|
|
|
|
return $decl; |
|
137
|
|
|
|
|
|
|
} |
|
138
|
|
|
|
|
|
|
|
|
139
|
0
|
|
|
0
|
|
|
sub _get_immediate_prereqs($class, $config) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
140
|
0
|
|
|
|
|
|
my $decl = $class->_get_cpanfile( $config ); |
|
141
|
0
|
|
|
|
|
|
return $decl->prereqs; |
|
142
|
|
|
|
|
|
|
} |
|
143
|
|
|
|
|
|
|
|
|
144
|
0
|
|
|
0
|
|
|
sub _compute_immediate_deps($class, $config) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
145
|
0
|
|
|
|
|
|
my @types = qw( requires recommends ); |
|
146
|
0
|
|
|
|
|
|
my @phases = qw( runtime ); |
|
147
|
0
|
|
|
|
|
|
my $decl = $class->_get_cpanfile( $config ); |
|
148
|
0
|
|
|
|
|
|
my $prereqs = $decl->prereqs_with( map { $_->identifier } $decl->features ); # all features |
|
|
0
|
|
|
|
|
|
|
|
149
|
0
|
|
|
|
|
|
my $effective = CPAN::Meta::Requirements->new; |
|
150
|
0
|
|
|
|
|
|
for my $phase (@phases) { |
|
151
|
0
|
|
|
|
|
|
for my $type (@types) { |
|
152
|
0
|
|
|
|
|
|
$effective->add_requirements( $prereqs->requirements_for( $phase, $type ) ); |
|
153
|
|
|
|
|
|
|
} |
|
154
|
|
|
|
|
|
|
} |
|
155
|
|
|
|
|
|
|
|
|
156
|
0
|
|
|
|
|
|
my @mods = sort { lc($a) cmp lc($b) } $effective->required_modules; |
|
|
0
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
|
|
158
|
0
|
|
|
|
|
|
$log->debug( "Direct dependency count: " . scalar(@mods) ); |
|
159
|
0
|
|
|
|
|
|
$log->trace( "- $_" ) for (sort @mods); |
|
160
|
0
|
|
|
|
|
|
return sort @mods; |
|
161
|
|
|
|
|
|
|
} |
|
162
|
|
|
|
|
|
|
|
|
163
|
0
|
|
|
0
|
|
|
sub _compute_all_deps($class, $config) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
164
|
0
|
|
|
|
|
|
my @deps = $class->_compute_immediate_deps( $config ); |
|
165
|
|
|
|
|
|
|
|
|
166
|
0
|
|
|
|
|
|
my @orig_deps = (@deps, ); |
|
167
|
0
|
|
|
|
|
|
my @last_deps = @deps; |
|
168
|
0
|
|
|
|
|
|
my %dists; |
|
169
|
0
|
|
|
|
|
|
my $iteration = 1; |
|
170
|
0
|
|
|
|
|
|
do { |
|
171
|
0
|
|
|
|
|
|
my $query = { |
|
172
|
|
|
|
|
|
|
_source => [ qw( release distribution status provides ), 'dependency.*' ], |
|
173
|
|
|
|
|
|
|
query => { |
|
174
|
|
|
|
|
|
|
bool => { |
|
175
|
|
|
|
|
|
|
must => [ |
|
176
|
|
|
|
|
|
|
{ term => { status => 'latest' } }, |
|
177
|
|
|
|
|
|
|
{ terms => { provides => [ @last_deps ] } } |
|
178
|
|
|
|
|
|
|
], |
|
179
|
|
|
|
|
|
|
filter => { |
|
180
|
|
|
|
|
|
|
match_all => {} |
|
181
|
|
|
|
|
|
|
} |
|
182
|
|
|
|
|
|
|
} |
|
183
|
|
|
|
|
|
|
} |
|
184
|
|
|
|
|
|
|
}; |
|
185
|
|
|
|
|
|
|
|
|
186
|
0
|
|
|
|
|
|
my $body = $json->encode( $query ); |
|
187
|
0
|
|
|
|
|
|
my $r = $http->request( 'POST', 'https://fastapi.metacpan.org/v1/release/_search?size=1000', |
|
188
|
|
|
|
|
|
|
{ headers => { 'Content-Type' => 'application/json' }, |
|
189
|
|
|
|
|
|
|
content => $body }); |
|
190
|
0
|
|
|
|
|
|
my $hits = $json->decode($r->{content})->{hits}; |
|
191
|
|
|
|
|
|
|
|
|
192
|
0
|
|
|
|
|
|
for my $release ($hits->{hits}->@*) { |
|
193
|
0
|
|
|
|
|
|
$dists{$release->{_source}->{distribution}} = 1; |
|
194
|
|
|
|
|
|
|
} |
|
195
|
|
|
|
|
|
|
|
|
196
|
0
|
|
|
|
|
|
my %provide; |
|
197
|
0
|
|
|
|
|
|
for my $release ($hits->{hits}->@*) { |
|
198
|
0
|
|
|
|
|
|
for my $provided ($release->{_source}->{provides}->@*) { |
|
199
|
0
|
|
|
|
|
|
$provide{$provided} = 1; |
|
200
|
|
|
|
|
|
|
} |
|
201
|
|
|
|
|
|
|
} |
|
202
|
|
|
|
|
|
|
|
|
203
|
0
|
|
|
|
|
|
my %rdeps; |
|
204
|
0
|
|
|
|
|
|
for my $release ($hits->{hits}->@*) { |
|
205
|
0
|
|
|
|
|
|
for my $dep ($release->{_source}->{dependency}->@*) { |
|
206
|
0
|
0
|
|
|
|
|
next unless $dep->{relationship} eq 'requires'; |
|
207
|
0
|
0
|
|
|
|
|
next unless $dep->{phase} eq 'runtime'; |
|
208
|
0
|
|
|
|
|
|
$rdeps{$dep->{module}} = 1; |
|
209
|
|
|
|
|
|
|
} |
|
210
|
|
|
|
|
|
|
} |
|
211
|
|
|
|
|
|
|
|
|
212
|
0
|
|
|
|
|
|
delete $rdeps{perl}; |
|
213
|
|
|
|
|
|
|
@last_deps = sort grep { |
|
214
|
0
|
|
|
|
|
|
my $m = $_; |
|
|
0
|
|
|
|
|
|
|
|
215
|
0
|
|
|
|
|
|
my $c = Module::CoreList->is_core($m); |
|
216
|
|
|
|
|
|
|
|
|
217
|
0
|
|
0
|
|
|
|
not ($provide{$m} or $c); |
|
218
|
|
|
|
|
|
|
} keys %rdeps; |
|
219
|
0
|
|
|
|
|
|
push @deps, @last_deps; |
|
220
|
|
|
|
|
|
|
|
|
221
|
0
|
|
|
|
|
|
$log->trace( "Dependency resolution iteration $iteration - " |
|
222
|
|
|
|
|
|
|
. "remaining to resolve: " . scalar(@last_deps) ); |
|
223
|
0
|
|
|
|
|
|
$iteration++; |
|
224
|
|
|
|
|
|
|
} while (@last_deps); |
|
225
|
|
|
|
|
|
|
|
|
226
|
0
|
|
|
|
|
|
my @missed = do { |
|
227
|
0
|
|
|
|
|
|
my %deps = map { $_ => 1 } @deps; |
|
|
0
|
|
|
|
|
|
|
|
228
|
0
|
|
|
|
|
|
grep { !$deps{$_} } @orig_deps; |
|
|
0
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
}; |
|
230
|
0
|
0
|
|
|
|
|
$log->error( "Unable to resolve dependencies for modules: " |
|
231
|
|
|
|
|
|
|
. join(', ', @missed) ) |
|
232
|
|
|
|
|
|
|
if (@missed); |
|
233
|
0
|
|
|
|
|
|
@deps = sort uniq (@deps, @missed); |
|
234
|
0
|
|
|
|
|
|
$log->debug( "Dependency tree size: " . scalar(@deps) ); |
|
235
|
0
|
|
|
|
|
|
$log->trace( "- $_" ) for (@deps); |
|
236
|
0
|
|
|
|
|
|
return @deps; |
|
237
|
|
|
|
|
|
|
} |
|
238
|
|
|
|
|
|
|
|
|
239
|
0
|
|
|
0
|
|
|
sub _compute_dep_pkgs($class, $dss, $config ) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
240
|
0
|
|
|
|
|
|
my @mods = $class->_compute_all_deps( $config ); |
|
241
|
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
# the array passed in gets modified; copy @mods so we still have it below |
|
243
|
0
|
|
|
|
|
|
my ($pkgs, $unmapped) = $dss->pkgs_from_modules( [ @mods ] ); |
|
244
|
|
|
|
|
|
|
|
|
245
|
0
|
|
|
|
|
|
my $c = scalar(@mods); |
|
246
|
0
|
|
|
|
|
|
my $p = scalar(keys $pkgs->%*); |
|
247
|
0
|
|
|
|
|
|
my $u = scalar($unmapped->@*); |
|
248
|
0
|
|
|
|
|
|
$log->debug( "Resolved $c modules to $p packages; $u unmapped" ); |
|
249
|
0
|
|
|
|
|
|
return ([ sort keys $pkgs->%* ], $unmapped); |
|
250
|
|
|
|
|
|
|
} |
|
251
|
|
|
|
|
|
|
|
|
252
|
0
|
|
|
0
|
|
|
sub _download($class, $installpath, $version) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
253
|
0
|
|
|
|
|
|
my $fn = "ledgersmb-$version.tar.gz"; |
|
254
|
0
|
|
0
|
|
|
|
my $url = $ENV{ARTIFACT_LOCATION} // "https://download.ledgersmb.org/f/Releases/$version/"; |
|
255
|
0
|
|
|
|
|
|
my $http = HTTP::Tiny->new; |
|
256
|
|
|
|
|
|
|
|
|
257
|
0
|
|
|
|
|
|
do { |
|
258
|
0
|
0
|
|
|
|
|
open( my $fh, '>', File::Spec->catfile($installpath, $fn)) |
|
259
|
|
|
|
|
|
|
or croak $log->fatal( "Unable to open output file $fn: $!" ); |
|
260
|
0
|
|
|
|
|
|
binmode $fh, ':raw'; |
|
261
|
0
|
|
|
|
|
|
my $r = $http->get( |
|
262
|
|
|
|
|
|
|
"$url$fn", |
|
263
|
|
|
|
|
|
|
{ |
|
264
|
0
|
|
|
0
|
|
|
data_callback => sub($data, $status) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
265
|
0
|
|
|
|
|
|
print $fh $data; |
|
266
|
|
|
|
|
|
|
} |
|
267
|
0
|
|
|
|
|
|
}); |
|
268
|
|
|
|
|
|
|
|
|
269
|
0
|
0
|
|
|
|
|
if ($r->{status} == 599) { |
|
|
|
0
|
|
|
|
|
|
|
270
|
0
|
|
|
|
|
|
croak $log->fatal( "Unable to request $url$fn: " . $r->{content} ); |
|
271
|
|
|
|
|
|
|
} |
|
272
|
|
|
|
|
|
|
elsif (not $r->{success}) { |
|
273
|
0
|
|
|
|
|
|
croak $log->fatal( "Unable to request $url$fn: $r->{status} - $r->{reason}" ); |
|
274
|
|
|
|
|
|
|
} |
|
275
|
|
|
|
|
|
|
}; |
|
276
|
|
|
|
|
|
|
|
|
277
|
0
|
|
|
|
|
|
do { |
|
278
|
0
|
|
|
|
|
|
my $r = $http->get( "$url$fn.asc" ); |
|
279
|
0
|
0
|
|
|
|
|
if ($r->{status} == 599) { |
|
|
|
0
|
|
|
|
|
|
|
280
|
0
|
|
|
|
|
|
croak $log->fatal( "Unable to request $url/$fn: " . $r->{content} ); |
|
281
|
|
|
|
|
|
|
} |
|
282
|
|
|
|
|
|
|
elsif (not $r->{success}) { |
|
283
|
0
|
|
|
|
|
|
croak $log->fatal( "Unable to request $url/$fn: $r->{status} - $r->{reason}" ); |
|
284
|
|
|
|
|
|
|
} |
|
285
|
|
|
|
|
|
|
else { |
|
286
|
0
|
0
|
|
|
|
|
open( my $fh, '>', File::Spec->catfile($installpath, "$fn.asc")) |
|
287
|
|
|
|
|
|
|
or croak $log->fatal( "Unable to open output file $fn.asc: $!" ); |
|
288
|
0
|
|
|
|
|
|
binmode $fh, ':raw'; |
|
289
|
0
|
|
|
|
|
|
print $fh $r->{content}; |
|
290
|
|
|
|
|
|
|
} |
|
291
|
|
|
|
|
|
|
}; |
|
292
|
|
|
|
|
|
|
} |
|
293
|
|
|
|
|
|
|
|
|
294
|
0
|
|
|
0
|
|
|
sub _find_executable($class, $dss, $executable, $dirs) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
295
|
0
|
|
|
|
|
|
while (my $dir = shift $dirs->@*) { |
|
296
|
0
|
|
|
|
|
|
my $exe = File::Spec->catfile( $dir, $dss->executable_name( $executable ) ); |
|
297
|
0
|
0
|
0
|
|
|
|
$log->trace( "Found $executable: $exe; but not executable" ) |
|
298
|
|
|
|
|
|
|
if -e $exe and not -x $exe; |
|
299
|
0
|
0
|
|
|
|
|
my $rv = -x $exe ? $exe : ''; |
|
300
|
|
|
|
|
|
|
|
|
301
|
0
|
0
|
|
|
|
|
if ($rv) { |
|
302
|
0
|
|
|
|
|
|
$log->debug( "Searching for $executable; found $exe" ); |
|
303
|
0
|
|
|
|
|
|
return $rv; |
|
304
|
|
|
|
|
|
|
} |
|
305
|
|
|
|
|
|
|
} |
|
306
|
0
|
|
|
|
|
|
return undef; |
|
307
|
|
|
|
|
|
|
} |
|
308
|
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
# This function is borrowed from App::Info::RDBMS::PostgreSQL v0.57 |
|
310
|
|
|
|
|
|
|
# because that is what DBD::Pg uses to identify where pg_config lives |
|
311
|
0
|
|
|
0
|
|
|
sub _search_bin_dirs($class) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
return (( exists $ENV{POSTGRES_HOME} |
|
313
|
|
|
|
|
|
|
? (File::Spec->catdir($ENV{POSTGRES_HOME}, "bin")) |
|
314
|
|
|
|
|
|
|
: () |
|
315
|
|
|
|
|
|
|
), |
|
316
|
|
|
|
|
|
|
( exists $ENV{POSTGRES_LIB} |
|
317
|
0
|
0
|
|
|
|
|
? (File::Spec->catdir($ENV{POSTGRES_LIB}, File::Spec->updir, "bin")) |
|
|
|
0
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
: () |
|
319
|
|
|
|
|
|
|
), |
|
320
|
|
|
|
|
|
|
File::Spec->path, |
|
321
|
|
|
|
|
|
|
qw(/usr/local/pgsql/bin |
|
322
|
|
|
|
|
|
|
/usr/local/postgres/bin |
|
323
|
|
|
|
|
|
|
/usr/lib/postgresql/bin |
|
324
|
|
|
|
|
|
|
/opt/pgsql/bin |
|
325
|
|
|
|
|
|
|
/usr/local/bin |
|
326
|
|
|
|
|
|
|
/usr/local/sbin |
|
327
|
|
|
|
|
|
|
/usr/bin |
|
328
|
|
|
|
|
|
|
/usr/sbin |
|
329
|
|
|
|
|
|
|
/bin), |
|
330
|
|
|
|
|
|
|
'C:\Program Files\PostgreSQL\bin'); |
|
331
|
|
|
|
|
|
|
} |
|
332
|
|
|
|
|
|
|
# end of borrowed code |
|
333
|
|
|
|
|
|
|
|
|
334
|
0
|
|
|
0
|
|
|
sub _find_pg_config($class, $dss, $config) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
335
|
0
|
|
|
|
|
|
my @dirs = $class->_search_bin_dirs; |
|
336
|
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
# TODO: Check for pg_config in $config |
|
338
|
|
|
|
|
|
|
|
|
339
|
0
|
|
|
|
|
|
return $class->_find_executable( $dss, 'pg_config', \@dirs ); |
|
340
|
|
|
|
|
|
|
} |
|
341
|
|
|
|
|
|
|
|
|
342
|
0
|
|
|
0
|
|
|
sub _find_xml2_config($class, $dss, $config) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
343
|
0
|
|
|
|
|
|
return $class->_find_executable( $dss, 'xml2-config', [ File::Spec->path ] ); |
|
344
|
|
|
|
|
|
|
} |
|
345
|
|
|
|
|
|
|
|
|
346
|
0
|
|
|
0
|
|
|
sub _find_latex($class, $dss, $config) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
347
|
0
|
|
|
|
|
|
return $class->_find_executable( $dss, 'latex', [ File::Spec->path ] ); |
|
348
|
|
|
|
|
|
|
} |
|
349
|
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
# mapping taken from File::Spec |
|
352
|
|
|
|
|
|
|
my %module = ( |
|
353
|
|
|
|
|
|
|
MSWin32 => 'win32', |
|
354
|
|
|
|
|
|
|
os2 => 'os2', |
|
355
|
|
|
|
|
|
|
VMS => 'vms', |
|
356
|
|
|
|
|
|
|
NetWare => 'win32', |
|
357
|
|
|
|
|
|
|
symbian => 'win32', |
|
358
|
|
|
|
|
|
|
dos => 'os2', |
|
359
|
|
|
|
|
|
|
cygwin => 'cygwin', |
|
360
|
|
|
|
|
|
|
amigaos => 'amigaos', |
|
361
|
|
|
|
|
|
|
linux => 'linux' # not mapped in File::Spec |
|
362
|
|
|
|
|
|
|
); |
|
363
|
|
|
|
|
|
|
|
|
364
|
0
|
|
|
0
|
|
|
sub _get_os($class) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
365
|
0
|
|
0
|
|
|
|
return $module{$^O} || 'unix'; |
|
366
|
|
|
|
|
|
|
} |
|
367
|
|
|
|
|
|
|
|
|
368
|
0
|
|
|
0
|
|
|
sub _load_dist_support($class) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
369
|
0
|
|
|
|
|
|
my $OS = $class->_get_os; |
|
370
|
|
|
|
|
|
|
|
|
371
|
0
|
|
|
|
|
|
$log->info( "Detected O/S: $OS" ); |
|
372
|
0
|
|
|
|
|
|
my $oss_class = "LedgerSMB::Installer::OS::$OS"; |
|
373
|
|
|
|
|
|
|
|
|
374
|
0
|
|
|
|
|
|
local $@ = undef; |
|
375
|
0
|
0
|
|
|
|
|
unless (eval "require $oss_class") { |
|
376
|
0
|
|
|
|
|
|
say "Unable to load $oss_class: $@"; |
|
377
|
0
|
|
|
|
|
|
say "No support for $OS"; |
|
378
|
0
|
|
|
|
|
|
exit 2; |
|
379
|
|
|
|
|
|
|
} |
|
380
|
|
|
|
|
|
|
|
|
381
|
0
|
|
|
|
|
|
my $oss = $oss_class->new; # operating system support instance |
|
382
|
0
|
|
|
|
|
|
$log->debug( "Detecting distribution" ); |
|
383
|
0
|
|
|
|
|
|
return $oss->detect_dss; # detect and return distribution support instance |
|
384
|
|
|
|
|
|
|
} |
|
385
|
|
|
|
|
|
|
|
|
386
|
0
|
|
|
0
|
0
|
|
sub compute($class, @args) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
387
|
0
|
|
|
|
|
|
my ($dss, $config) = $class->_boot( |
|
388
|
|
|
|
|
|
|
\@args, |
|
389
|
|
|
|
|
|
|
[ 'yes|y!', 'target=s', 'local-lib=s', 'log-level=s' ] |
|
390
|
|
|
|
|
|
|
); |
|
391
|
|
|
|
|
|
|
|
|
392
|
0
|
0
|
|
|
|
|
if (@args != 2) { |
|
393
|
0
|
|
|
|
|
|
die "Incorrect number of arguments"; |
|
394
|
|
|
|
|
|
|
} |
|
395
|
|
|
|
|
|
|
|
|
396
|
0
|
|
|
|
|
|
my $version = shift @args; |
|
397
|
0
|
|
|
|
|
|
$config->compute_deps( 1 ); |
|
398
|
0
|
0
|
|
|
|
|
$config->version( $version ) if defined $version; |
|
399
|
|
|
|
|
|
|
|
|
400
|
0
|
|
|
|
|
|
my $deps_outfile = $args[0]; |
|
401
|
0
|
0
|
|
|
|
|
open( my $out, '>:raw', $deps_outfile ) |
|
402
|
|
|
|
|
|
|
or die "Unable to open output file '$deps_outfile': $!"; |
|
403
|
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
|
|
405
|
0
|
0
|
|
|
|
|
unless ($dss->am_system_perl) { |
|
406
|
0
|
0
|
|
|
|
|
close( $out ) or warn $log->warn( "Unable to close output file" ); |
|
407
|
0
|
|
|
|
|
|
unlink $deps_outfile; |
|
408
|
0
|
|
|
|
|
|
die $log->fatal( "Not running the system perl; not able to re-use system packages" ); |
|
409
|
|
|
|
|
|
|
} |
|
410
|
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
###TODO: _get_immediate_prereqs may throw |
|
412
|
0
|
|
|
|
|
|
my $prereqs = $class->_get_immediate_prereqs( $config ); |
|
413
|
0
|
|
|
|
|
|
my $requirements = $prereqs->merged_requirements(); |
|
414
|
0
|
0
|
|
|
|
|
unless ($requirements->accepts_module( 'perl', $])) { |
|
415
|
0
|
0
|
|
|
|
|
close( $out ) or warn $log->warn( "Unable to close output file" ); |
|
416
|
0
|
|
|
|
|
|
unlink $deps_outfile; |
|
417
|
0
|
|
|
|
|
|
my $perl_version = version->parse( $] )->normal; |
|
418
|
0
|
|
|
|
|
|
die $log->fatal( "Perl version ($perl_version) not compliant with LedgerSMB " . $config->effective_version |
|
419
|
|
|
|
|
|
|
. "; requires: " . $requirements->requirements_for_module( 'perl' )); |
|
420
|
|
|
|
|
|
|
} |
|
421
|
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
###TODO: prepare_pkg_resolver_env may throw |
|
423
|
0
|
0
|
|
|
|
|
if ($config->effective_prepare_env) { |
|
424
|
0
|
|
|
|
|
|
$dss->prepare_pkg_resolver_env( $config ); |
|
425
|
|
|
|
|
|
|
} |
|
426
|
0
|
|
|
|
|
|
my $exception; |
|
427
|
0
|
|
|
|
|
|
do { |
|
428
|
0
|
|
|
|
|
|
local $@ = undef; |
|
429
|
0
|
|
|
|
|
|
my $failed = not eval { |
|
430
|
0
|
|
|
|
|
|
$log->info( "Computing O/S packages for declared dependencies" ); |
|
431
|
0
|
|
|
|
|
|
my ($deps, $mods) = $class->_compute_dep_pkgs( $dss, $config ); |
|
432
|
|
|
|
|
|
|
|
|
433
|
0
|
|
|
|
|
|
say $out $json->encode( { identifier => $dss->dependency_packages_identifier, |
|
434
|
|
|
|
|
|
|
packages => $deps, |
|
435
|
|
|
|
|
|
|
modules => $mods, |
|
436
|
|
|
|
|
|
|
name => $dss->name, |
|
437
|
|
|
|
|
|
|
'schema-version' => "1" } ); |
|
438
|
|
|
|
|
|
|
|
|
439
|
0
|
|
|
|
|
|
return 1; |
|
440
|
|
|
|
|
|
|
}; |
|
441
|
0
|
0
|
|
|
|
|
$exception = $@ if $failed; |
|
442
|
|
|
|
|
|
|
|
|
443
|
0
|
|
|
|
|
|
$log->info( "Dependencies written to $deps_outfile" ); |
|
444
|
0
|
0
|
|
|
|
|
if ($config->effective_uninstall_env) { |
|
445
|
0
|
|
|
|
|
|
$log->warning( "Cleaning up Perl module installation dependencies" ); |
|
446
|
0
|
|
|
|
|
|
$dss->cleanup_env($config); |
|
447
|
|
|
|
|
|
|
} |
|
448
|
|
|
|
|
|
|
}; |
|
449
|
0
|
0
|
|
|
|
|
die $exception if defined $exception; |
|
450
|
|
|
|
|
|
|
|
|
451
|
0
|
|
|
|
|
|
return 0; |
|
452
|
|
|
|
|
|
|
} |
|
453
|
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
sub download($class, @args) { |
|
455
|
|
|
|
|
|
|
} |
|
456
|
|
|
|
|
|
|
|
|
457
|
0
|
|
|
0
|
0
|
|
sub help($class, @args) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
458
|
0
|
|
|
|
|
|
my $help_text = do { |
|
459
|
0
|
|
|
|
|
|
local $/ = undef; |
|
460
|
0
|
|
|
|
|
|
; |
|
461
|
|
|
|
|
|
|
}; |
|
462
|
0
|
|
|
|
|
|
$help_text =~ s/\bSCRIPT\b/$0/g; |
|
463
|
0
|
|
|
|
|
|
say $help_text; |
|
464
|
|
|
|
|
|
|
|
|
465
|
0
|
|
|
|
|
|
return 0; |
|
466
|
|
|
|
|
|
|
} |
|
467
|
|
|
|
|
|
|
|
|
468
|
0
|
|
|
0
|
|
|
sub _module_will_install($class, $mod, $pkgs, $unmapped) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
# no packages will be installed: |
|
470
|
0
|
0
|
0
|
|
|
|
return 0 if ((not $pkgs) or (not $pkgs->@*)); |
|
471
|
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
# package in the list of those not installed through a package: |
|
473
|
0
|
0
|
|
|
|
|
return 0 if (grep { $mod eq $_ } $unmapped->@*); |
|
|
0
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
# package must be in one of the packages being installed |
|
476
|
0
|
|
|
|
|
|
return 1; |
|
477
|
|
|
|
|
|
|
} |
|
478
|
|
|
|
|
|
|
|
|
479
|
0
|
|
|
0
|
0
|
|
sub install($class, @args) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
480
|
0
|
|
|
|
|
|
my $rv = 1; |
|
481
|
0
|
|
|
|
|
|
my ($dss, $config) = $class->_boot( |
|
482
|
|
|
|
|
|
|
\@args, |
|
483
|
|
|
|
|
|
|
[ 'yes|y!', 'system-packages!', 'prepare-env!', 'target=s', |
|
484
|
|
|
|
|
|
|
'local-lib=s', 'log-level=s', 'verify-sig!' ] |
|
485
|
|
|
|
|
|
|
); |
|
486
|
0
|
|
|
|
|
|
my $version = shift @args; |
|
487
|
0
|
0
|
|
|
|
|
$config->version( $version ) if defined $version; |
|
488
|
|
|
|
|
|
|
|
|
489
|
0
|
|
|
|
|
|
my ($pkg_deps, $unmapped_mods); |
|
490
|
0
|
|
|
|
|
|
my @extra_pkgs; |
|
491
|
0
|
0
|
|
|
|
|
if ($dss->am_system_perl) { |
|
492
|
0
|
|
|
|
|
|
my $name = $dss->name; |
|
493
|
0
|
|
|
|
|
|
my $dep_pkg_id = $dss->dependency_packages_identifier; |
|
494
|
0
|
0
|
|
|
|
|
if ($config->sys_pkgs) { |
|
495
|
0
|
|
|
|
|
|
($pkg_deps, $unmapped_mods) = $config->retrieve_precomputed_deps($name, $dep_pkg_id); |
|
496
|
|
|
|
|
|
|
} |
|
497
|
0
|
0
|
|
|
|
|
if ($pkg_deps) { |
|
498
|
0
|
0
|
|
|
|
|
if ($dss->pkg_can_install()) { |
|
499
|
0
|
0
|
|
|
|
|
if ($config->effective_prepare_env) { |
|
500
|
0
|
|
|
|
|
|
$dss->prepare_builder_env( $config ); |
|
501
|
|
|
|
|
|
|
} |
|
502
|
0
|
|
|
|
|
|
goto INSTALL_SYS_PKGS; |
|
503
|
|
|
|
|
|
|
} |
|
504
|
|
|
|
|
|
|
else { |
|
505
|
0
|
|
|
|
|
|
$log->warn( "Unable to install system packages; will resort to installation of CPAN modules" ); |
|
506
|
0
|
|
|
|
|
|
$pkg_deps = undef; |
|
507
|
|
|
|
|
|
|
} |
|
508
|
|
|
|
|
|
|
} |
|
509
|
|
|
|
|
|
|
else { |
|
510
|
0
|
|
|
|
|
|
$log->warn( "No precomputed dependencies available for $name/$dep_pkg_id" ); |
|
511
|
0
|
|
|
|
|
|
$log->info( "Configuring environment for dependency computation" ); |
|
512
|
|
|
|
|
|
|
} |
|
513
|
|
|
|
|
|
|
} |
|
514
|
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
######################################################################################## |
|
516
|
|
|
|
|
|
|
# |
|
517
|
|
|
|
|
|
|
# Need to clean up on failure after this point! We have changed system state! |
|
518
|
|
|
|
|
|
|
# |
|
519
|
|
|
|
|
|
|
######################################################################################## |
|
520
|
0
|
|
|
|
|
|
$dss->prepare_extraction_env( $config ); |
|
521
|
0
|
|
|
|
|
|
$class->_build_install_tree( $dss, $config, $config->installpath, $config->effective_version ); |
|
522
|
0
|
|
|
|
|
|
my $prereqs = $class->_get_immediate_prereqs( $config ); |
|
523
|
0
|
|
|
|
|
|
my $requirements = $prereqs->merged_requirements(); |
|
524
|
|
|
|
|
|
|
|
|
525
|
0
|
0
|
|
|
|
|
unless ($requirements->accepts_module( 'perl', $])) { |
|
526
|
|
|
|
|
|
|
# BAIL: No suitable Perl here... |
|
527
|
|
|
|
|
|
|
# |
|
528
|
|
|
|
|
|
|
# well, we might want to see if Perlbrew is installed with an acceptable version? |
|
529
|
|
|
|
|
|
|
# |
|
530
|
|
|
|
|
|
|
# and if not, we could install Perlbrew here... |
|
531
|
0
|
|
|
|
|
|
die $log->fatal( "Not running a Perl version compliant with LedgerSMB " . $config->effective_version ); |
|
532
|
|
|
|
|
|
|
} |
|
533
|
|
|
|
|
|
|
|
|
534
|
0
|
0
|
|
|
|
|
if ($config->effective_prepare_env) { |
|
535
|
0
|
|
|
|
|
|
$dss->prepare_builder_env( $config ); |
|
536
|
|
|
|
|
|
|
} |
|
537
|
|
|
|
|
|
|
|
|
538
|
0
|
0
|
0
|
|
|
|
if ($dss->am_system_perl and $dss->pkg_can_install) { # and $dss->deps_can_map |
|
539
|
0
|
0
|
|
|
|
|
if ($config->effective_prepare_env) { |
|
540
|
0
|
|
|
|
|
|
$dss->prepare_pkg_resolver_env( $config ); |
|
541
|
|
|
|
|
|
|
} |
|
542
|
0
|
|
|
|
|
|
($pkg_deps, $unmapped_mods) = $class->_compute_dep_pkgs( $dss, $config ); |
|
543
|
|
|
|
|
|
|
} |
|
544
|
|
|
|
|
|
|
else { |
|
545
|
0
|
|
|
|
|
|
$unmapped_mods = [ $class->_compute_all_deps( $config ) ]; |
|
546
|
|
|
|
|
|
|
} |
|
547
|
|
|
|
|
|
|
|
|
548
|
0
|
|
|
|
|
|
$log->info( "Checking for availability of DBD::Pg" ); |
|
549
|
0
|
0
|
0
|
|
|
|
if (not eval { require DBD::Pg; 1; } # not loadable, and |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
and not $class->_module_will_install( 'DBD::Pg', $pkg_deps, $unmapped_mods )) { |
|
551
|
|
|
|
|
|
|
# don't have DBD::Pg |
|
552
|
|
|
|
|
|
|
# *and* won't install as part of $pkg_deps |
|
553
|
|
|
|
|
|
|
|
|
554
|
0
|
|
|
|
|
|
my $pg_config = $class->_find_pg_config( $dss, $config ); |
|
555
|
0
|
0
|
|
|
|
|
die $log->fatal( "Missing 'pg_config' command to build DBD::Pg" ) |
|
556
|
|
|
|
|
|
|
unless $pg_config; |
|
557
|
0
|
|
|
|
|
|
chomp( my $include_dir = `'$pg_config' --includedir` ); |
|
558
|
|
|
|
|
|
|
|
|
559
|
0
|
|
|
|
|
|
$log->debug( "Directory for PostgreSQL headers: $include_dir" ); |
|
560
|
0
|
|
|
|
|
|
my $header_file = File::Spec->catfile( $include_dir, 'libpq-fe.h' ); |
|
561
|
|
|
|
|
|
|
|
|
562
|
0
|
0
|
|
|
|
|
if (not -e $header_file) { |
|
563
|
0
|
0
|
|
|
|
|
if (not $dss->pkg_can_install) { |
|
564
|
0
|
|
|
|
|
|
die $log->fatal( "Missing 'libpq-fe.h' PostgreSQL header to build DBD::Pg" ); |
|
565
|
|
|
|
|
|
|
} |
|
566
|
|
|
|
|
|
|
|
|
567
|
0
|
|
|
|
|
|
my ($run_deps, $build_deps) = $dss->pkg_deps_dbd_pg; |
|
568
|
0
|
|
|
|
|
|
$config->mark_pkgs_for_cleanup( $build_deps ); |
|
569
|
0
|
|
|
|
|
|
push @extra_pkgs, $run_deps->@*, $build_deps->@*; |
|
570
|
|
|
|
|
|
|
} |
|
571
|
|
|
|
|
|
|
} |
|
572
|
0
|
|
|
|
|
|
elsif (eval { require DBD::Pg; 1; }) { |
|
|
0
|
|
|
|
|
|
|
|
573
|
0
|
|
|
|
|
|
$log->info( "DBD::Pg is loadable" ); |
|
574
|
|
|
|
|
|
|
} |
|
575
|
0
|
|
|
|
|
|
elsif (not grep { $_ eq 'DBD::Pg' } $unmapped_mods->@*) { |
|
576
|
0
|
|
|
|
|
|
$log->info( "DBD::Pg will be installed" ); |
|
577
|
|
|
|
|
|
|
} |
|
578
|
|
|
|
|
|
|
else { |
|
579
|
0
|
|
|
|
|
|
$log->fatal( "Internal error: DBD::Pg not available and won't be installed, but build prereqs not checked?!?!" ); |
|
580
|
|
|
|
|
|
|
} |
|
581
|
|
|
|
|
|
|
|
|
582
|
0
|
|
|
|
|
|
$log->info( "Checking for availability of LaTeX::Driver" ); |
|
583
|
0
|
0
|
0
|
|
|
|
if (not eval { require LaTeX::Driver; 1; } # loadable, and |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
and not $class->_module_will_install( 'LaTeX::Driver', $pkg_deps, $unmapped_mods )) { |
|
585
|
|
|
|
|
|
|
# don't have LaTeX::Driver |
|
586
|
|
|
|
|
|
|
# *and* won't install as part of $pkg_deps |
|
587
|
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
# testing early, because LaTeX::Driver only installs |
|
589
|
|
|
|
|
|
|
# when LaTeX is installed... |
|
590
|
|
|
|
|
|
|
|
|
591
|
0
|
|
|
|
|
|
my $latex = $class->_find_latex( $dss, $config ); |
|
592
|
0
|
0
|
|
|
|
|
if (not $latex) { |
|
593
|
0
|
0
|
|
|
|
|
if (not $dss->pkg_can_install) { |
|
594
|
0
|
|
|
|
|
|
die $log->fatal( "Missing 'latex' executable required for building 'LaTeX::Driver' module" ); |
|
595
|
|
|
|
|
|
|
} |
|
596
|
|
|
|
|
|
|
} |
|
597
|
|
|
|
|
|
|
} |
|
598
|
0
|
|
|
|
|
|
elsif (eval { require LaTeX::Driver; 1; }) { |
|
|
0
|
|
|
|
|
|
|
|
599
|
0
|
|
|
|
|
|
$log->info( "LaTeX::Driver is loadable" ); |
|
600
|
|
|
|
|
|
|
} |
|
601
|
0
|
|
|
|
|
|
elsif (not grep { $_ eq 'LaTeX::Driver' } $unmapped_mods->@*) { |
|
602
|
0
|
|
|
|
|
|
$log->info( "LaTeX::Driver will be installed" ); |
|
603
|
|
|
|
|
|
|
} |
|
604
|
|
|
|
|
|
|
else { |
|
605
|
0
|
|
|
|
|
|
$log->fatal( "Internal error: LaTeX::Driver not available and won't be installed, but build prereqs not checked?!?!" ); |
|
606
|
|
|
|
|
|
|
} |
|
607
|
0
|
0
|
|
|
|
|
if ($dss->pkg_can_install) { |
|
608
|
|
|
|
|
|
|
# installs LaTeX *and* styles required for standard templates |
|
609
|
0
|
|
|
|
|
|
my ($run_deps, $build_deps) = $dss->pkg_deps_latex; |
|
610
|
0
|
|
|
|
|
|
$config->mark_pkgs_for_cleanup( $build_deps ); |
|
611
|
0
|
|
|
|
|
|
push @extra_pkgs, $run_deps->@*, $build_deps->@*; |
|
612
|
|
|
|
|
|
|
} |
|
613
|
|
|
|
|
|
|
else { |
|
614
|
|
|
|
|
|
|
###TODO: figure out how to warn the user of minimal required |
|
615
|
|
|
|
|
|
|
# styles configuration... |
|
616
|
|
|
|
|
|
|
} |
|
617
|
|
|
|
|
|
|
|
|
618
|
0
|
|
|
|
|
|
$log->info( "Checking for availability of XML::Parser" ); |
|
619
|
0
|
0
|
0
|
|
|
|
if (not eval { require XML::Parser; 1; } |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
and not $class->_module_will_install( 'XML::Parser', $pkg_deps, $unmapped_mods)) { |
|
621
|
|
|
|
|
|
|
|
|
622
|
0
|
|
|
|
|
|
$log->info( "Checking availability of libexpat" ); |
|
623
|
0
|
|
0
|
|
|
|
my $incpath = $ENV{EXPATINCPATH} // ''; |
|
624
|
0
|
|
0
|
|
|
|
my $libpath = $ENV{EXPATLIBPATH} // ''; |
|
625
|
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
# Devel::CheckLib tries to find the C compiler when use-d |
|
627
|
|
|
|
|
|
|
# however, we might be installing it as part of the installation |
|
628
|
|
|
|
|
|
|
# process, so we don't want Devel::CheckLib to check when loading the script... |
|
629
|
0
|
|
|
|
|
|
eval "use Devel::CheckLib qw(assert_lib);"; |
|
630
|
0
|
0
|
|
|
|
|
unless (eval { assert_lib( lib => [qw(expat)], |
|
|
0
|
0
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
header => ['expat.h'], |
|
632
|
|
|
|
|
|
|
incpath => $incpath, |
|
633
|
|
|
|
|
|
|
( $libpath ? (libpath => $libpath) : () ) |
|
634
|
0
|
|
|
|
|
|
); 1; }) { |
|
635
|
0
|
0
|
|
|
|
|
if (not $dss->pkg_can_install) { |
|
636
|
|
|
|
|
|
|
###TODO: We could push Alien::Expat into @unmapped_mods |
|
637
|
|
|
|
|
|
|
# instead of bailing out here... |
|
638
|
0
|
|
|
|
|
|
die $log->fatal( "Missing XML parser library Expat blocking installation of XML::Parser" ); |
|
639
|
|
|
|
|
|
|
} |
|
640
|
|
|
|
|
|
|
else { |
|
641
|
0
|
|
|
|
|
|
my ($run_deps, $build_deps) = $dss->pkg_deps_expat; |
|
642
|
0
|
|
|
|
|
|
$config->mark_pkgs_for_cleanup( $build_deps ); |
|
643
|
0
|
|
|
|
|
|
push @extra_pkgs, $run_deps->@*, $build_deps->@*; |
|
644
|
|
|
|
|
|
|
} |
|
645
|
|
|
|
|
|
|
} |
|
646
|
|
|
|
|
|
|
else { |
|
647
|
0
|
|
|
|
|
|
$log->info( "Found expat header and library for XML::Parser compilation" ); |
|
648
|
|
|
|
|
|
|
} |
|
649
|
|
|
|
|
|
|
} |
|
650
|
0
|
|
|
|
|
|
elsif (eval { require XML::Parser; 1; }) { |
|
|
0
|
|
|
|
|
|
|
|
651
|
0
|
|
|
|
|
|
$log->info( "XML::Parser is loadable" ); |
|
652
|
|
|
|
|
|
|
} |
|
653
|
0
|
|
|
|
|
|
elsif (not grep { $_ eq 'XML::Parser' } $unmapped_mods->@*) { |
|
654
|
0
|
|
|
|
|
|
$log->info( "XML::Parser will be installed" ); |
|
655
|
|
|
|
|
|
|
} |
|
656
|
|
|
|
|
|
|
else { |
|
657
|
0
|
|
|
|
|
|
$log->fatal( "Internal error: XML::Parser not available and won't be installed, but build prereqs not checked?!?!" ); |
|
658
|
|
|
|
|
|
|
} |
|
659
|
|
|
|
|
|
|
|
|
660
|
0
|
0
|
0
|
|
|
|
if ((not eval { require XML::LibXML; 1; } |
|
|
0
|
|
0
|
|
|
|
|
|
|
0
|
|
0
|
|
|
|
|
|
661
|
|
|
|
|
|
|
and not $class->_module_will_install( 'XML::LibXML', $pkg_deps, $unmapped_mods )) |
|
662
|
0
|
|
|
|
|
|
and (not eval { require XML::Twig; 1; } |
|
|
0
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
and not $class->_module_will_install( 'XML::Twig', $pkg_deps, $unmapped_mods ))) { |
|
664
|
|
|
|
|
|
|
# don't have either XML::LibXML or XML::Twig |
|
665
|
|
|
|
|
|
|
|
|
666
|
0
|
|
|
|
|
|
my $xml2_config = $class->_find_xml2_config( $dss, $config ); |
|
667
|
0
|
0
|
|
|
|
|
if (not $xml2_config) { |
|
668
|
0
|
0
|
|
|
|
|
if (not $dss->pkg_can_install) { |
|
669
|
0
|
|
|
|
|
|
warn $log->warning("Missing 'xml2-config' executable required for building XML::LibXML -- falling back to Alien::Libxml2" ); |
|
670
|
|
|
|
|
|
|
} |
|
671
|
|
|
|
|
|
|
else { |
|
672
|
0
|
|
|
|
|
|
my ($run_deps, $build_deps) = $dss->pkg_deps_xml; |
|
673
|
0
|
|
|
|
|
|
$config->mark_pkgs_for_cleanup( $build_deps ); |
|
674
|
0
|
|
|
|
|
|
push @extra_pkgs, $run_deps->@*, $build_deps->@*; |
|
675
|
|
|
|
|
|
|
} |
|
676
|
|
|
|
|
|
|
} |
|
677
|
|
|
|
|
|
|
} |
|
678
|
|
|
|
|
|
|
|
|
679
|
0
|
0
|
0
|
|
|
|
goto PREPARE_TREE if (not $pkg_deps or not $pkg_deps->@*); |
|
680
|
|
|
|
|
|
|
|
|
681
|
0
|
|
|
|
|
|
INSTALL_SYS_PKGS: |
|
682
|
|
|
|
|
|
|
$log->info( "Installing O/S packages: " . join(' ', $pkg_deps->@*) ); |
|
683
|
0
|
|
|
|
|
|
$dss->pkg_install( $pkg_deps ); |
|
684
|
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
PREPARE_TREE: |
|
686
|
0
|
0
|
|
|
|
|
if (@extra_pkgs) { |
|
687
|
0
|
|
|
|
|
|
$log->info( "Installing build dependency packages: " . join(' ', @extra_pkgs) ); |
|
688
|
0
|
|
|
|
|
|
$dss->pkg_install( \@extra_pkgs ); |
|
689
|
|
|
|
|
|
|
} |
|
690
|
0
|
0
|
|
|
|
|
if ($config->effective_prepare_env) { |
|
691
|
0
|
|
|
|
|
|
$dss->prepare_installer_env( $config ); |
|
692
|
|
|
|
|
|
|
} |
|
693
|
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
###TODO: ideally, we pass the immediate dependencies instead of the installation path; |
|
695
|
|
|
|
|
|
|
# that allows selection of specific features in a later iteration |
|
696
|
|
|
|
|
|
|
# |
|
697
|
|
|
|
|
|
|
# Installs dependencies from cpanfile (because it respects dependency versions, |
|
698
|
|
|
|
|
|
|
# which the simple list of dependencies does not); then installs the extra |
|
699
|
|
|
|
|
|
|
# dependencies which were not satisfied from the list of unmapped_mods) |
|
700
|
0
|
|
|
|
|
|
$dss->cpanm_install( $config->installpath, $config->locallib, $unmapped_mods ); |
|
701
|
0
|
|
|
|
|
|
$rv = 0; |
|
702
|
|
|
|
|
|
|
|
|
703
|
0
|
|
|
|
|
|
$log->info( "Generating application server startup script (server-start)" ); |
|
704
|
0
|
|
|
|
|
|
$dss->generate_start_script( File::Spec->rel2abs( $config->installpath ), |
|
705
|
|
|
|
|
|
|
File::Spec->rel2abs( $config->locallib ) ); |
|
706
|
|
|
|
|
|
|
|
|
707
|
0
|
|
|
|
|
|
CLEANUP: |
|
708
|
|
|
|
|
|
|
$log->warning( "Cleaning up Perl module installation dependencies" ); |
|
709
|
0
|
|
|
|
|
|
$dss->cleanup_env($config); |
|
710
|
|
|
|
|
|
|
|
|
711
|
0
|
0
|
|
|
|
|
if ($rv) { |
|
712
|
0
|
0
|
|
|
|
|
if (-e $config->installpath) { |
|
713
|
0
|
|
|
|
|
|
$log->warning( "Cleaning up installation path" ); |
|
714
|
0
|
|
|
|
|
|
remove_tree $config->installpath; |
|
715
|
|
|
|
|
|
|
} |
|
716
|
|
|
|
|
|
|
|
|
717
|
0
|
|
|
|
|
|
say "Failed to complete server installation."; |
|
718
|
|
|
|
|
|
|
} |
|
719
|
|
|
|
|
|
|
else { |
|
720
|
0
|
|
|
|
|
|
my $installpath = $config->installpath; |
|
721
|
0
|
|
|
|
|
|
say " |
|
722
|
|
|
|
|
|
|
LedgerSMB installation into $installpath completed. |
|
723
|
|
|
|
|
|
|
|
|
724
|
|
|
|
|
|
|
The LedgerSMB server can be started using the generated |
|
725
|
|
|
|
|
|
|
startup script: |
|
726
|
|
|
|
|
|
|
|
|
727
|
|
|
|
|
|
|
\$ $installpath/server-start |
|
728
|
|
|
|
|
|
|
|
|
729
|
|
|
|
|
|
|
The server will run with its default configuration. To change |
|
730
|
|
|
|
|
|
|
the configuration, copy the file doc/conf/ledgersmb.yaml to |
|
731
|
|
|
|
|
|
|
$installpath and modify it. After modification, restart the |
|
732
|
|
|
|
|
|
|
LedgerSMB server to activate the new configuration. |
|
733
|
|
|
|
|
|
|
|
|
734
|
|
|
|
|
|
|
"; |
|
735
|
|
|
|
|
|
|
} |
|
736
|
|
|
|
|
|
|
|
|
737
|
0
|
|
|
|
|
|
return $rv; |
|
738
|
|
|
|
|
|
|
} |
|
739
|
|
|
|
|
|
|
|
|
740
|
0
|
|
|
0
|
0
|
|
sub print_id( $class, @args) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
741
|
0
|
|
|
|
|
|
my $dss = $class->_load_dist_support; |
|
742
|
0
|
|
|
|
|
|
say $dss->dependency_packages_identifier; |
|
743
|
|
|
|
|
|
|
} |
|
744
|
|
|
|
|
|
|
|
|
745
|
0
|
|
|
0
|
0
|
|
sub run($class, $cmd, @args) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
746
|
0
|
|
|
|
|
|
STDOUT->autoflush(1); |
|
747
|
0
|
|
|
|
|
|
STDERR->autoflush(1); |
|
748
|
|
|
|
|
|
|
|
|
749
|
0
|
0
|
|
|
|
|
if ($cmd =~ m/^-/) { # option(s) |
|
750
|
0
|
|
|
|
|
|
unshift @args, $cmd; |
|
751
|
0
|
|
|
|
|
|
$cmd = 'install'; |
|
752
|
|
|
|
|
|
|
} |
|
753
|
|
|
|
|
|
|
|
|
754
|
0
|
0
|
|
|
|
|
if ($cmd eq 'compute') { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
755
|
0
|
|
|
|
|
|
say $log->info( "Computing dependencies using installer $INSTALLER_VERSION" ); |
|
756
|
0
|
|
|
|
|
|
return $class->compute( @args ); |
|
757
|
|
|
|
|
|
|
} |
|
758
|
|
|
|
|
|
|
elsif ($cmd eq 'download') { |
|
759
|
0
|
|
|
|
|
|
return $class->download( @args ); |
|
760
|
|
|
|
|
|
|
} |
|
761
|
|
|
|
|
|
|
elsif ($cmd eq 'help') { |
|
762
|
0
|
|
|
|
|
|
return $class->help( @args ); |
|
763
|
|
|
|
|
|
|
} |
|
764
|
|
|
|
|
|
|
elsif ($cmd eq 'install') { |
|
765
|
0
|
|
|
|
|
|
say $log->info( "Installing LedgerSMB using installer $INSTALLER_VERSION" ); |
|
766
|
0
|
|
|
|
|
|
return $class->install( @args ); |
|
767
|
|
|
|
|
|
|
} |
|
768
|
|
|
|
|
|
|
elsif ($cmd eq 'system-id') { |
|
769
|
0
|
|
|
|
|
|
return $class->print_id( @args ); |
|
770
|
|
|
|
|
|
|
} |
|
771
|
|
|
|
|
|
|
else { |
|
772
|
0
|
|
|
|
|
|
$class->help(); |
|
773
|
0
|
|
|
|
|
|
exit 1; |
|
774
|
|
|
|
|
|
|
} |
|
775
|
|
|
|
|
|
|
} |
|
776
|
|
|
|
|
|
|
|
|
777
|
|
|
|
|
|
|
|
|
778
|
|
|
|
|
|
|
1; |
|
779
|
|
|
|
|
|
|
|
|
780
|
|
|
|
|
|
|
=head1 NAME |
|
781
|
|
|
|
|
|
|
|
|
782
|
|
|
|
|
|
|
LedgerSMB::Installer - Installer for the LedgerSMB accounting web application |
|
783
|
|
|
|
|
|
|
|
|
784
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
785
|
|
|
|
|
|
|
|
|
786
|
|
|
|
|
|
|
=head1 MAINTAINER |
|
787
|
|
|
|
|
|
|
|
|
788
|
|
|
|
|
|
|
Erik Huelsmann |
|
789
|
|
|
|
|
|
|
|
|
790
|
|
|
|
|
|
|
=head1 AUTHOR |
|
791
|
|
|
|
|
|
|
|
|
792
|
|
|
|
|
|
|
Erik Huelsmann |
|
793
|
|
|
|
|
|
|
|
|
794
|
|
|
|
|
|
|
=head1 LICENSE |
|
795
|
|
|
|
|
|
|
|
|
796
|
|
|
|
|
|
|
Copyright 2025 Erik Huelsmann |
|
797
|
|
|
|
|
|
|
|
|
798
|
|
|
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
|
799
|
|
|
|
|
|
|
of this software and associated documentation files (the "Software"), to deal |
|
800
|
|
|
|
|
|
|
in the Software without restriction, including without limitation the rights |
|
801
|
|
|
|
|
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
802
|
|
|
|
|
|
|
copies of the Software, and to permit persons to whom the Software is |
|
803
|
|
|
|
|
|
|
furnished to do so, subject to the following conditions: |
|
804
|
|
|
|
|
|
|
|
|
805
|
|
|
|
|
|
|
The above copyright notice and this permission notice shall be included in |
|
806
|
|
|
|
|
|
|
all copies or substantial portions of the Software. |
|
807
|
|
|
|
|
|
|
|
|
808
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
809
|
|
|
|
|
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
810
|
|
|
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
|
811
|
|
|
|
|
|
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
812
|
|
|
|
|
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
813
|
|
|
|
|
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|
814
|
|
|
|
|
|
|
SOFTWARE. |
|
815
|
|
|
|
|
|
|
|
|
816
|
|
|
|
|
|
|
=cut |
|
817
|
|
|
|
|
|
|
|
|
818
|
|
|
|
|
|
|
__DATA__ |