261
281
return $self->get('Pkg Status');
284
# This function is the main entry point into the package build. It
285
# provides a top-level exception handler and does the initial setup
286
# including initiating logging and creating host chroot. The nested
287
# run_ functions it calls are separate in order to permit running
288
# cleanup tasks in a strict order.
265
290
my $self = shift;
267
$self->set_status('building');
269
$self->set('Pkg Start Time', time);
270
$self->set('Pkg End Time', $self->get('Pkg Start Time'));
272
# Acquire the architecture we're building for.
273
$self->set('Arch', $self->get_conf('ARCH'));
275
my $dist = $self->get_conf('DISTRIBUTION');
276
if (!defined($dist) || !$dist) {
277
$self->log("No distribution defined\n");
281
if ($self->get('Invalid Source')) {
282
$self->log("Invalid source: " . $self->get('DSC') . "\n");
283
$self->log("Skipping " . $self->get('Package') . " \n");
284
$self->set_status('failed');
289
if ($self->get_conf('CHROOT_MODE') eq 'schroot') {
290
$chroot_info = Sbuild::ChrootInfoSchroot->new($self->get('Config'));
292
$chroot_info = Sbuild::ChrootInfoSudo->new($self->get('Config'));
295
# TODO: Get package name from build object
296
if (!$self->open_build_log()) {
300
# Set a chroot to run commands in host
301
my $host = $self->get('Host');
303
# Host execution defaults (set streams)
304
my $host_defaults = $host->get('Defaults');
305
$host_defaults->{'STREAMIN'} = $devnull;
306
$host_defaults->{'STREAMOUT'} = $self->get('Log Stream');
307
$host_defaults->{'STREAMERR'} = $self->get('Log Stream');
293
$self->check_abort();
295
$self->set_status('building');
297
$self->set('Pkg Start Time', time);
298
$self->set('Pkg End Time', $self->get('Pkg Start Time'));
300
# Acquire the architecture we're building for.
301
$self->set('Arch', $self->get_conf('ARCH'));
303
my $dist = $self->get_conf('DISTRIBUTION');
304
if (!defined($dist) || !$dist) {
305
Sbuild::Exception::Build->throw(error => "No distribution defined",
306
failstage => "init");
309
if ($self->get('Invalid Source')) {
310
Sbuild::Exception::Build->throw(error => "Invalid source " . $self->get('DSC'),
311
failstage => "init");
314
# TODO: Get package name from build object
315
if (!$self->open_build_log()) {
316
Sbuild::Exception::Build->throw(error => "Failed to open build log",
317
failstage => "init");
320
# Set a chroot to run commands in host
321
my $host = $self->get('Host');
323
# Host execution defaults (set streams)
324
my $host_defaults = $host->get('Defaults');
325
$host_defaults->{'STREAMIN'} = $devnull;
326
$host_defaults->{'STREAMOUT'} = $self->get('Log Stream');
327
$host_defaults->{'STREAMERR'} = $self->get('Log Stream');
329
$self->check_abort();
334
if ($e = Exception::Class->caught('Sbuild::Exception::Build')) {
336
$self->set_status($e->status);
338
$self->set_status("failed");
340
$self->set('Pkg Fail Stage', $e->failstage);
345
# Pack up source if needed and then run the main chroot session.
346
# Close log during return/failure.
351
$self->check_abort();
352
$self->run_pack_source();
353
$self->check_abort();
354
$self->run_chroot_session();
357
# Log exception info and set status and fail stage prior to
360
if ($e = Exception::Class->caught('Sbuild::Exception::Build')) {
361
$self->log_error("$e\n");
362
$self->log_info($e->info."\n")
365
$self->set_status($e->status);
367
$self->set_status("failed");
369
$self->set('Pkg Fail Stage', $e->failstage);
372
$self->close_build_log();
379
# Pack up local source tree
380
sub run_pack_source {
383
$self->check_abort();
309
384
# Build the source package if given a Debianized source directory
310
385
if ($self->get('Debian Source Dir')) {
311
$self->set('Pkg Fail Stage', 'pack-source');
312
386
$self->log_subsection("Build Source Package");
314
388
$self->log_subsubsection('clean');
339
$self->log_error("Failed to build source package");
345
my $session = $chroot_info->create('chroot',
346
$self->get_conf('DISTRIBUTION'),
347
$self->get_conf('CHROOT'),
348
$self->get_conf('ARCH'));
350
# Run pre build external commands
351
$self->run_external_commands("pre-build-commands",
352
$self->get_conf('LOG_EXTERNAL_COMMAND_OUTPUT'),
353
$self->get_conf('LOG_EXTERNAL_COMMAND_ERROR'));
355
if (!$session->begin_session()) {
356
$self->log("Error creating chroot session: skipping " .
357
$self->get('Package') . "\n");
358
$self->set_status('failed');
359
goto cleanup_unlocked_session;
414
Sbuild::Exception::Build->throw(error => "Failed to clean source directory",
415
failstage => "pack-source");
420
# Create main chroot session and package resolver. Creates a lock in
421
# the chroot to prevent concurrent chroot usage (only important for
422
# non-snapshot chroots). Ends chroot session on return/failure.
423
sub run_chroot_session {
427
$self->check_abort();
429
if ($self->get_conf('CHROOT_MODE') eq 'schroot') {
430
$chroot_info = Sbuild::ChrootInfoSchroot->new($self->get('Config'));
432
$chroot_info = Sbuild::ChrootInfoSudo->new($self->get('Config'));
435
my $host = $self->get('Host');
437
my $session = $chroot_info->create('chroot',
438
$self->get_conf('DISTRIBUTION'),
439
$self->get_conf('CHROOT'),
440
$self->get_conf('ARCH'));
442
# Run pre build external commands
443
$self->check_abort();
444
$self->run_external_commands("pre-build-commands",
445
$self->get_conf('LOG_EXTERNAL_COMMAND_OUTPUT'),
446
$self->get_conf('LOG_EXTERNAL_COMMAND_ERROR'));
448
$self->check_abort();
449
if (!$session->begin_session()) {
450
Sbuild::Exception::Build->throw(error => "Error creating chroot session: skipping " .
451
$self->get('Package'),
452
failstage => "create-session");
455
$self->set('Session', $session);
457
$self->check_abort();
458
my $chroot_arch = $self->chroot_arch();
459
if ($self->get('Arch') ne $chroot_arch) {
460
Sbuild::Exception::Build->throw(error => "Build architecture (" . $self->get('Arch') .
461
") is not the same as the chroot architecture (" .
463
info => "Please specify the correct architecture with --arch, or use a chroot of the correct architecture",
464
failstage => "create-session");
467
$self->set('Chroot Dir', $session->get('Location'));
468
# TODO: Don't hack the build location in; add a means to customise
469
# the chroot directly. i.e. allow changing of /build location.
470
$self->set('Chroot Build Dir',
471
tempdir($self->get('Package') . '-XXXXXX',
472
DIR => $session->get('Location') . "/build"));
475
$filter = $session->strip_chroot_path($self->get('Chroot Build Dir'));
477
$self->build_log_filter($filter, 'BUILDDIR');
478
$filter = $session->get('Location');
480
$self->build_log_filter($filter , 'CHROOT');
481
# Need tempdir to be writable and readable by sbuild group.
482
$self->check_abort();
483
$session->run_command(
484
{ COMMAND => ['chown', 'sbuild:sbuild',
485
$session->strip_chroot_path($self->get('Chroot Build Dir'))],
489
Sbuild::Exception::Build->throw(error => "Failed to set sbuild group ownership on chroot build dir",
490
failstage => "create-build-dir");
492
$self->check_abort();
493
$session->run_command(
494
{ COMMAND => ['chmod', '0770',
495
$session->strip_chroot_path($self->get('Chroot Build Dir'))],
499
Sbuild::Exception::Build->throw(error => "Failed to set sbuild group ownership on chroot build dir",
500
failstage => "create-build-dir");
503
$self->check_abort();
504
# Needed so chroot commands log to build log
505
$session->set('Log Stream', $self->get('Log Stream'));
506
$host->set('Log Stream', $self->get('Log Stream'));
508
# Chroot execution defaults
509
my $chroot_defaults = $session->get('Defaults');
510
$chroot_defaults->{'DIR'} =
511
$session->strip_chroot_path($self->get('Chroot Build Dir'));
512
$chroot_defaults->{'STREAMIN'} = $devnull;
513
$chroot_defaults->{'STREAMOUT'} = $self->get('Log Stream');
514
$chroot_defaults->{'STREAMERR'} = $self->get('Log Stream');
515
$chroot_defaults->{'ENV'}->{'LC_ALL'} = 'POSIX';
516
$chroot_defaults->{'ENV'}->{'SHELL'} = '/bin/sh';
518
my $resolver = get_resolver($self->get('Config'), $session, $host);
519
$resolver->set('Log Stream', $self->get('Log Stream'));
520
$resolver->set('Arch', $self->get('Arch'));
521
$resolver->set('Chroot Build Dir', $self->get('Chroot Build Dir'));
522
$self->set('Dependency Resolver', $resolver);
524
# Lock chroot so it won't be tampered with during the build.
525
$self->check_abort();
526
if (!$session->lock_chroot($self->get('Package_SVersion'), $$, $self->get_conf('USERNAME'))) {
527
Sbuild::Exception::Build->throw(error => "Error locking chroot session: skipping " .
528
$self->get('Package'),
529
failstage => "lock-session");
532
$self->check_abort();
533
$self->run_chroot_session_locked();
537
my $session = $self->get('Session');
539
($self->get_conf('PURGE_SESSION') eq 'always' ||
540
($self->get_conf('PURGE_SESSION') eq 'successful' &&
541
$self->get_status() eq 'successful')) ? 1 : 0;
543
$session->end_session();
545
$self->log("Keeping session: " . $session->get('Session ID') . "\n");
362
548
$self->set('Session', $session);
364
my $chroot_arch = $self->chroot_arch();
365
if ($self->get('Arch') ne $chroot_arch) {
366
$self->log_error("Build architecture (" . $self->get('Arch') .
367
") is not the same as the chroot architecture (" .
368
$chroot_arch . ")\n");
369
$self->log_info("Please specify the correct architecture with --arch, or use a chroot of the correct architecture\n");
370
$self->set_status('failed');
371
goto cleanup_unlocked_session;
374
$self->set('Chroot Dir', $session->get('Location'));
375
# TODO: Don't hack the build location in; add a means to customise
376
# the chroot directly. i.e. allow changing of /build location.
377
$self->set('Chroot Build Dir',
378
tempdir($self->get_conf('USERNAME') . '-' .
379
$self->get('Package_SVersion') . '-' .
380
$self->get('Arch') . '-XXXXXX',
381
DIR => $session->get('Location') . "/build"));
383
# Needed so chroot commands log to build log
384
$session->set('Log Stream', $self->get('Log Stream'));
385
$host->set('Log Stream', $self->get('Log Stream'));
387
# Chroot execution defaults
388
my $chroot_defaults = $session->get('Defaults');
389
$chroot_defaults->{'DIR'} =
390
$session->strip_chroot_path($self->get('Chroot Build Dir'));
391
$chroot_defaults->{'STREAMIN'} = $devnull;
392
$chroot_defaults->{'STREAMOUT'} = $self->get('Log Stream');
393
$chroot_defaults->{'STREAMERR'} = $self->get('Log Stream');
394
$chroot_defaults->{'ENV'}->{'LC_ALL'} = 'POSIX';
395
$chroot_defaults->{'ENV'}->{'SHELL'} = '/bin/sh';
397
my $resolver = get_resolver($self->get('Config'), $session, $host);
398
$resolver->set('Log Stream', $self->get('Log Stream'));
399
$resolver->set('Arch', $self->get('Arch'));
400
$resolver->set('Chroot Build Dir', $self->get('Chroot Build Dir'));
401
$self->set('Dependency Resolver', $resolver);
403
# Lock chroot so it won't be tampered with during the build.
404
if (!$session->lock_chroot($self->get('Package_SVersion'), $$, $self->get_conf('USERNAME'))) {
405
goto cleanup_unlocked_session;
551
if ($e = Exception::Class->caught('Sbuild::Exception::Build')) {
556
# Run tasks in a *locked* chroot. Update and upgrade packages.
557
# Unlocks chroot on return/failure.
558
sub run_chroot_session_locked {
562
my $session = $self->get('Session');
563
my $resolver = $self->get('Dependency Resolver');
565
$self->check_abort();
568
$self->check_abort();
569
$self->run_chroot_update();
571
$self->check_abort();
572
$self->run_fetch_install_packages();
575
my $session = $self->get('Session');
576
my $resolver = $self->get('Dependency Resolver');
578
$resolver->cleanup();
579
# Unlock chroot now it's cleaned up and ready for other users.
580
$session->unlock_chroot();
583
if ($e = Exception::Class->caught('Sbuild::Exception::Build')) {
588
sub run_chroot_update {
590
my $resolver = $self->get('Dependency Resolver');
592
if ($self->get_conf('APT_CLEAN') || $self->get_conf('APT_UPDATE') ||
593
$self->get_conf('APT_DISTUPGRADE') || $self->get_conf('APT_UPGRADE')) {
594
$self->log_subsection('Update chroot');
410
597
# Clean APT cache.
411
$self->set('Pkg Fail Stage', 'apt-get-clean');
598
$self->check_abort();
412
599
if ($self->get_conf('APT_CLEAN')) {
413
600
if ($resolver->clean()) {
414
601
# Since apt-clean was requested specifically, fail on
415
602
# error when not in buildd mode.
416
603
$self->log("apt-get clean failed\n");
417
604
if ($self->get_conf('SBUILD_MODE') ne 'buildd') {
418
$self->set_status('failed');
419
goto cleanup_session;
605
Sbuild::Exception::Build->throw(error => "apt-get clean failed",
606
failstage => "apt-get-clean");
424
611
# Update APT cache.
425
$self->set('Pkg Fail Stage', 'apt-get-update');
612
$self->check_abort();
426
613
if ($self->get_conf('APT_UPDATE')) {
427
614
if ($resolver->update()) {
428
615
# Since apt-update was requested specifically, fail on
429
616
# error when not in buildd mode.
430
$self->log("apt-get update failed\n");
431
$self->set_status('failed');
432
goto cleanup_session;
617
if ($self->get_conf('SBUILD_MODE') ne 'buildd') {
618
Sbuild::Exception::Build->throw(error => "apt-get update failed",
619
failstage => "apt-get-update");
436
624
# Upgrade using APT.
625
$self->check_abort();
437
626
if ($self->get_conf('APT_DISTUPGRADE')) {
438
$self->set('Pkg Fail Stage', 'apt-get-distupgrade');
439
627
if ($self->get_conf('APT_DISTUPGRADE')) {
440
628
if ($resolver->distupgrade()) {
441
629
# Since apt-distupgrade was requested specifically, fail on
442
630
# error when not in buildd mode.
443
$self->log("apt-get dist-upgrade failed\n");
444
631
if ($self->get_conf('SBUILD_MODE') ne 'buildd') {
445
$self->set_status('failed');
446
goto cleanup_session;
632
Sbuild::Exception::Build->throw(error => "apt-get dist-upgrade failed",
633
failstage => "apt-get-dist-upgrade");
450
637
} elsif ($self->get_conf('APT_UPGRADE')) {
451
$self->set('Pkg Fail Stage', 'apt-get-upgrade');
452
638
if ($self->get_conf('APT_UPGRADE')) {
453
639
if ($resolver->upgrade()) {
454
640
# Since apt-upgrade was requested specifically, fail on
455
641
# error when not in buildd mode.
456
$self->log("apt-get upgrade failed\n");
457
642
if ($self->get_conf('SBUILD_MODE') ne 'buildd') {
458
$self->set_status('failed');
459
goto cleanup_session;
643
Sbuild::Exception::Build->throw(error => "apt-get upgrade failed",
644
failstage => "apt-get-upgrade");
465
$self->set('Pkg Fail Stage', 'fetch-src');
466
if (!$self->fetch_source_files()) {
467
goto cleanup_packages;
470
# Display message about chroot setup script option use being deprecated
471
if ($self->get_conf('CHROOT_SETUP_SCRIPT')) {
472
my $msg = "setup-hook option is deprecated. It has been superceded by ";
473
$msg .= "the chroot-setup-commands feature. setup-hook script will be ";
474
$msg .= "run via chroot-setup-commands.\n";
475
$self->log_warning($msg);
478
# Run specified chroot setup commands
479
$self->run_external_commands("chroot-setup-commands",
480
$self->get_conf('LOG_EXTERNAL_COMMAND_OUTPUT'),
481
$self->get_conf('LOG_EXTERNAL_COMMAND_ERROR'));
483
$self->set('Install Start Time', time);
484
$self->set('Install End Time', $self->get('Install Start Time'));
485
$resolver->add_dependencies('CORE', join(", ", @{$self->get_conf('CORE_DEPENDS')}) , "", "", "");
486
if (!$resolver->install_deps('core', 'CORE')) {
487
$self->log("Core source dependencies not satisfied; skipping\n");
488
goto cleanup_packages;
491
$resolver->add_dependencies('ESSENTIAL', $self->read_build_essential(), "", "", "");
494
$snapshot = "gcc-snapshot" if ($self->get_conf('GCC_SNAPSHOT'));
495
$resolver->add_dependencies('GCC_SNAPSHOT', $snapshot , "", "", "");
497
# Add additional build dependencies specified on the command-line.
498
# TODO: Split dependencies into an array from the start to save
500
$resolver->add_dependencies('MANUAL',
501
join(", ", @{$self->get_conf('MANUAL_DEPENDS')}),
502
join(", ", @{$self->get_conf('MANUAL_DEPENDS_INDEP')}),
503
join(", ", @{$self->get_conf('MANUAL_CONFLICTS')}),
504
join(", ", @{$self->get_conf('MANUAL_CONFLICTS_INDEP')}));
506
$resolver->add_dependencies($self->get('Package'),
507
$self->get('Build Depends'),
508
$self->get('Build Depends Indep'),
509
$self->get('Build Conflicts'),
510
$self->get('Build Conflicts Indep'));
512
$self->set('Pkg Fail Stage', 'install-deps');
513
if (!$resolver->install_deps($self->get('Package'),
514
'ESSENTIAL', 'GCC_SNAPSHOT', 'MANUAL',
515
$self->get('Package'))) {
516
$self->log("Source-dependencies not satisfied; skipping " .
517
$self->get('Package') . "\n");
518
goto cleanup_packages;
520
$self->set('Install End Time', time);
522
$resolver->dump_build_environment();
524
$self->prepare_watches(keys %{$resolver->get('Changes')->{'installed'}});
526
if ($self->build()) {
527
$self->set_status('successful');
529
$self->set_status('failed');
532
# Run specified chroot cleanup commands
533
$self->run_external_commands("chroot-cleanup-commands",
534
$self->get_conf('LOG_EXTERNAL_COMMAND_OUTPUT'),
535
$self->get_conf('LOG_EXTERNAL_COMMAND_ERROR'));
537
if ($self->get('Pkg Status') eq "successful") {
538
$self->log_subsection("Post Build");
541
$self->run_lintian();
544
$self->run_piuparts();
546
# Run post build external commands
547
$self->run_external_commands("post-build-commands",
548
$self->get_conf('LOG_EXTERNAL_COMMAND_OUTPUT'),
549
$self->get_conf('LOG_EXTERNAL_COMMAND_ERROR'));
651
# Fetch sources, run setup, fetch and install core and package build
652
# deps, then run build. Cleans up build directory and uninstalls
653
# build depends on return/failure.
654
sub run_fetch_install_packages {
657
$self->check_abort();
659
my $session = $self->get('Session');
660
my $resolver = $self->get('Dependency Resolver');
662
$self->check_abort();
663
if (!$self->fetch_source_files()) {
664
Sbuild::Exception::Build->throw(error => "Failed to fetch source files",
665
failstage => "fetch-src");
668
# Display message about chroot setup script option use being deprecated
669
if ($self->get_conf('CHROOT_SETUP_SCRIPT')) {
670
my $msg = "setup-hook option is deprecated. It has been superceded by ";
671
$msg .= "the chroot-setup-commands feature. setup-hook script will be ";
672
$msg .= "run via chroot-setup-commands.\n";
673
$self->log_warning($msg);
676
# Run specified chroot setup commands
677
$self->check_abort();
678
$self->run_external_commands("chroot-setup-commands",
679
$self->get_conf('LOG_EXTERNAL_COMMAND_OUTPUT'),
680
$self->get_conf('LOG_EXTERNAL_COMMAND_ERROR'));
682
$self->check_abort();
683
$self->set('Install Start Time', time);
684
$self->set('Install End Time', $self->get('Install Start Time'));
685
$resolver->add_dependencies('CORE', join(", ", @{$self->get_conf('CORE_DEPENDS')}) , "", "", "");
686
if (!$resolver->install_deps('core', 'CORE')) {
687
Sbuild::Exception::Build->throw(error => "Core build dependencies not satisfied; skipping",
688
failstage => "install-deps");
691
$resolver->add_dependencies('ESSENTIAL', $self->read_build_essential(), "", "", "");
694
$snapshot = "gcc-snapshot" if ($self->get_conf('GCC_SNAPSHOT'));
695
$resolver->add_dependencies('GCC_SNAPSHOT', $snapshot , "", "", "");
697
# Add additional build dependencies specified on the command-line.
698
# TODO: Split dependencies into an array from the start to save
700
$resolver->add_dependencies('MANUAL',
701
join(", ", @{$self->get_conf('MANUAL_DEPENDS')}),
702
join(", ", @{$self->get_conf('MANUAL_DEPENDS_INDEP')}),
703
join(", ", @{$self->get_conf('MANUAL_CONFLICTS')}),
704
join(", ", @{$self->get_conf('MANUAL_CONFLICTS_INDEP')}));
706
$resolver->add_dependencies($self->get('Package'),
707
$self->get('Build Depends'),
708
$self->get('Build Depends Indep'),
709
$self->get('Build Conflicts'),
710
$self->get('Build Conflicts Indep'));
712
$self->check_abort();
713
if (!$resolver->install_deps($self->get('Package'),
714
'ESSENTIAL', 'GCC_SNAPSHOT', 'MANUAL',
715
$self->get('Package'))) {
716
Sbuild::Exception::Build->throw(error => "Package build dependencies not satisfied; skipping",
717
failstage => "install-deps");
719
$self->set('Install End Time', time);
721
$self->check_abort();
722
$resolver->dump_build_environment();
724
$self->check_abort();
725
$self->prepare_watches(keys %{$resolver->get('Changes')->{'installed'}});
727
$self->check_abort();
728
if ($self->build()) {
729
$self->set_status('successful');
731
$self->set('Pkg Fail Stage', "build");
732
$self->set_status('failed');
735
# Run specified chroot cleanup commands
736
$self->check_abort();
737
$self->run_external_commands("chroot-cleanup-commands",
738
$self->get_conf('LOG_EXTERNAL_COMMAND_OUTPUT'),
739
$self->get_conf('LOG_EXTERNAL_COMMAND_ERROR'));
741
if ($self->get('Pkg Status') eq "successful") {
742
$self->log_subsection("Post Build");
745
$self->check_abort();
746
$self->run_lintian();
749
$self->check_abort();
750
$self->run_piuparts();
752
# Run post build external commands
753
$self->check_abort();
754
$self->run_external_commands("post-build-commands",
755
$self->get_conf('LOG_EXTERNAL_COMMAND_OUTPUT'),
756
$self->get_conf('LOG_EXTERNAL_COMMAND_ERROR'));
761
$self->log_subsection("Cleanup");
762
my $session = $self->get('Session');
763
my $resolver = $self->get('Dependency Resolver');
554
765
my $purge_build_directory =
555
766
($self->get_conf('PURGE_BUILD_DIRECTORY') eq 'always' ||
556
767
($self->get_conf('PURGE_BUILD_DIRECTORY') eq 'successful' &&