1use strict; 2 3use 5.005; 4 5use Data::Dumper; 6use File::Spec; 7 8use lib 'inc'; 9 10use Alzabo::Build; 11 12use Getopt::Long qw( :config pass_through ); 13 14my %opts; 15GetOptions( 'dist' => \$opts{dist}, 16 'root:s' => \$opts{root}, 17 'pg!' => \$opts{pg}, 18 'mysql!' => \$opts{mysql}, 19 'automated' => \$opts{automated}, 20 'help' => \$opts{help}, 21 ); 22$opts{automated} = 1; 23 24if ( $opts{help} ) 25{ 26 print <<'EOF'; 27 28 perl Build.PL [--automated] [--pg] [--mysql] 29 30This script accepts several options: 31 32 --automated Run without prompts 33 34 --pg Include prereqs for PostgreSQL support 35 36 --mysql Include prereqs for MySQL support 37 38 --root Root dir for storing Alzabos schemas 39 40 --help What you are reading 41 42EOF 43 44 exit; 45} 46 47{ 48 my ( $config, $prereqs, $tests ); 49 unless ( $opts{dist} ) 50 { 51 ( $config, $prereqs, $tests ) = config(); 52 write_config_module($config); 53 } 54 else 55 { 56 $prereqs = dist_prereqs(); 57 $config = {}; 58 } 59 60 my $build = 61 Alzabo::Build->new( module_name => 'Alzabo', 62 license => 'perl', 63 %$prereqs, 64 sign => 1, 65 ); 66 67 $build->create_build_script; 68 69 $build->notes( test_config => $tests ); 70 71 $build->add_to_cleanup( File::Spec->catdir( 't', 'schemas' ), 72 File::Spec->catfile( 'lib', 'Alzabo', 'Config.pm' ), 73 ); 74} 75 76sub config 77{ 78 # try to see if there is an existing Alzabo installation 79 eval { require Alzabo; }; 80 eval { require Alzabo::Config; }; 81 82 if ( ! $@ && 83 %Alzabo::Config::CONFIG && 84 defined Alzabo::Config::root_dir() && 85 length Alzabo::Config::root_dir() && 86 -d Alzabo::Config::root_dir() && 87 Alzabo::Config::available_schemas() && $Alzabo::VERSION < 0.55 ) 88 { 89 print <<'EOF'; 90 91You appear to have schemas created with an older version of Alzabo. 92If you want to continue to use these, you may need to run the 93convert.pl script in the eg/ directory _before_ installing this 94version of Alzabo. 95 96For newer versions, starting with the transition from 0.64 to 0.65, 97Alzabo automatically converts schemas as needed. 98 99EOF 100 101 exit unless Module::Build->y_n( ' Continue?', 'no' ); 102 } 103 104 my %config; 105 106 $config{root_dir} = root_dir(); 107 108 my ( $prereqs, $tests ) = features(); 109 110 my $test_config = test_config($tests); 111 112 return \%config, $prereqs, $test_config; 113} 114 115sub root_dir 116{ 117 my $root_dir = 118 ( $opts{root} 119 ? $opts{root} 120 : %Alzabo::Config::CONFIG 121 ? Alzabo::Config::root_dir() 122 : find_possible_root() 123 ); 124 125 return $root_dir if $opts{automated}; 126 127 print <<'EOF'; 128 129Please select a root directory for Alzabo (schema files will be stored 130under this root. 131EOF 132 133 return Module::Build->prompt( ' Alzabo root?', $root_dir ); 134} 135 136sub find_possible_root 137{ 138 my @dirs; 139 140 if ( $^O =~ /win/i ) 141 { 142 # A bit too thorough? 143 foreach ('C'..'Z') 144 { 145 unshift @dirs, "$_:\\Program Files"; 146 } 147 } 148 else 149 { 150 @dirs = qw( /var/lib /usr/local ); 151 } 152 153 unshift @dirs, '/opt' if $^O =~ /solaris/i; 154 155 foreach (@dirs) 156 { 157 $_ .= '/alzabo'; 158 159 return $_ if -e $_; 160 } 161 162 return ''; 163} 164 165sub features 166{ 167 # These are always needed 168 my %prereqs = default_prereqs(); 169 170 my ( %tests ); 171 172 # extra prereqs for certain features 173 my %features = 174 ( mysql => { phrase => 'to use the MySQL driver', 175 requires => { 'DBD::mysql' => 2.1017 }, 176 test => 'mysql', 177 }, 178 179 pg => { phrase => 'to use the PostgreSQL driver', 180 requires => { 'DBD::Pg' => 1.13, 181 'Text::Balanced' => 0, 182 'Digest::MD5' => 0, 183 }, 184 test => 'pg', 185 }, 186 ); 187 188 if ( $opts{automated} ) 189 { 190 for my $k ( grep { $opts{$_} } keys %features ) 191 { 192 _add_to_prereqs( \%prereqs, $features{$k} ); 193 $tests{$k} = 1; 194 } 195 196 return \%prereqs, \%tests; 197 } 198 199 print <<'EOF'; 200 201The following questions pertain to optional features of Alzabo. These 202questions help the installer determine what additional system checks 203to perform. 204 205EOF 206 207 foreach my $feature ( map { $features{$_} } sort keys %features ) 208 { 209 print "\n"; 210 211 my $has = 1; 212 my $mods = ''; 213 foreach my $type ( qw( requires recommends ) ) 214 { 215 if ( $feature->{$type} ) 216 { 217 my $text = "$type"; 218 while ( my ( $mod, $ver ) = each %{ $feature->{$type} } ) 219 { 220 $text .= " $mod"; 221 $text .= " ($ver)" if $ver; 222 223 $has = 0 224 unless Module::Build->check_installed_version( $mod, $ver ); 225 } 226 227 $mods .= ' and ' if $mods; 228 $mods .= $text; 229 } 230 } 231 232 print "\u$feature->{phrase} $mods.\n"; 233 234 my $wanted = 235 Module::Build->y_n( " Do you want $feature->{phrase}?", $has ? 'yes' : 'no' ); 236 237 if ($wanted) 238 { 239 _add_to_prereqs( \%prereqs, $feature ); 240 241 $tests{ $feature->{test} } = 1 if exists $feature->{test}; 242 } 243 } 244 245 return \%prereqs, \%tests; 246} 247 248sub _add_to_prereqs 249{ 250 my $prereqs = shift; 251 my $feature = shift; 252 253 foreach my $type ( grep { $feature->{$_} } qw( requires recommends ) ) 254 { 255 $prereqs->{$type} = { %{ $prereqs->{$type} }, 256 %{ $feature->{$type} }, 257 }; 258 } 259} 260 261sub default_prereqs 262{ 263 return 264 ( requires => 265 { 'Class::Factory::Util' => 1.3, 266 'DBI' => minimum_dbi_version(), 267 'Digest::MD5' => 0, 268 'Exception::Class' => 0.97, 269 'Params::Validate' => 0.58, 270 'Scalar::Util' => 1.01, 271 'Storable' => 0.7, 272 'Test::Simple' => 0.47, 273 'Test::Harness' => 1.26, 274 'Tie::IxHash' => 0, 275 'Time::HiRes' => 0, 276 perl => 5.006, 277 }, 278 recommends => {}, 279 build_requires => { 'Pod::Man' => 1.14 }, 280 ); 281} 282 283sub dist_prereqs 284{ 285 my %prereqs = default_prereqs(); 286 287 $prereqs{requires}{DBI} = 1.21; 288 289 $prereqs{recommends}{'DBD::mysql'} = 2.1017; 290 $prereqs{recommends}{'DBD::Pg'} = 1.13; 291 292 return \%prereqs; 293} 294 295sub minimum_dbi_version 296{ 297 if ( eval { require DBI } && $DBI::VERSION == 1.24 ) 298 { 299 warn <<'EOF'; 300You appear to have DBI version 1.24 installed. This version has a bug 301which causes major problems with Alzabo. Please upgrade or downgrade. 302EOF 303 return 1.25; 304 } 305 306 return 1.21; 307} 308 309sub write_config_module 310{ 311 my $config = shift; 312 313 # config items that the config module cares about 314 my @keys = qw( root_dir ); 315 316 my $file = File::Spec->catfile( 'inc', 'Alzabo', 'Config.pm.tmpl' ); 317 local *MOD; 318 open MOD, "<$file" 319 or die "can't open $file: $!\n"; 320 my $mod = join '', <MOD>; 321 close MOD 322 or die "can't close $file: $!\n"; 323 324 my $c = "(\n"; 325 foreach my $k (@keys) 326 { 327 my $val; 328 if ( length $config->{$k} ) 329 { 330 $val = "'$config->{$k}'"; 331 } 332 else 333 { 334 $val = "undef"; 335 } 336 337 $c .= "'$k' => $val,\n"; 338 } 339 $c .= ")"; 340 341 $mod =~ s/"'CONFIG'"/$c/; 342 343 my $config_pm = File::Spec->catfile( 'lib', 'Alzabo', 'Config.pm' ); 344 open MOD, '>', $config_pm 345 or die "can't write to $config_pm: $!\n"; 346 print MOD $mod 347 or die "can't write to $config_pm: $!\n"; 348 close MOD 349 or die "can't close $config_pm: $!\n"; 350} 351 352sub test_config 353{ 354 my $tests = shift; 355 356 return if $opts{automated}; 357 358 my @config; 359 360 my %names = ( mysql => 'Mysql', 361 pg => 'Postgres', 362 oracle => 'Oracle' ); 363 364 foreach my $t ( sort keys %$tests ) 365 { 366 my $name = $names{$t}; 367 368 print <<'EOF'; 369 370The information from the following questions are used solely for 371testing the pieces of Alzabo that require a real database for proper 372testing. 373EOF 374 375 my $do = Module::Build->prompt( " Do tests with $name RDBMS?", 'yes' ); 376 next unless $do =~ /^y/i; 377 378 print <<"EOF"; 379 380Please provide a username that can be used to connect to the $name 381RDBMS? This user must have the ability to create a new 382database/schema. 383EOF 384 385 my $user = Module::Build->prompt( ' Username?' ); 386 my $password; 387 if ($user) 388 { 389 $password = Module::Build->prompt( " Password for $user?" ); 390 } 391 392 print <<"EOF"; 393 394What host is the $name RDBMS located on. Press enter to skip this if 395the database server is located on the localhost or can be determined 396in another way (for example, Oracle can use TNS to find the database). 397EOF 398 399 my $host = Module::Build->prompt( ' Host?' ); 400 401 print <<"EOF"; 402 403What port is the $name RDBMS located on. Press enter to skip this. 404EOF 405 406 my $port = Module::Build->prompt( ' Port?' ); 407 408 print <<'EOF'; 409 410Please provide a database name that can be used for testing. A 411database/schema with this name will be created and dropped during the 412testing process. 413EOF 414 415 my $db_name = Module::Build->prompt( ' Database name?', "test_alzabo_$t" ); 416 417 push @config, 418 { rdbms => $t, 419 user => $user, 420 password => $password, 421 host => $host, 422 port => $port, 423 schema_name => $db_name, 424 }; 425 } 426 427 return \@config; 428} 429