blob: 8bc1503a72a939fd8c3d847dd0bd0db33779c344 [file] [log] [blame]
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
use strict;
use warnings;
use lib '../clownfish/blib/arch';
use lib '../clownfish/blib/lib';
use lib 'clownfish/blib/arch';
use lib 'clownfish/blib/lib';
package Lucy::Build::CBuilder;
BEGIN { our @ISA = "ExtUtils::CBuilder"; }
use Config;
my %cc;
sub new {
my ( $class, %args ) = @_;
require ExtUtils::CBuilder;
if ( $ENV{LUCY_VALGRIND} ) {
$args{config} ||= {};
$args{config}{optimize} ||= $Config{optimize};
$args{config}{optimize} =~ s/\-O\d+/-O1/g;
}
my $self = $class->SUPER::new(%args);
$cc{"$self"} = $args{'config'}->{'cc'};
return $self;
}
sub get_cc { $cc{"$_[0]"} }
sub DESTROY {
my $self = shift;
delete $cc{"$self"};
}
# This method isn't implemented by CBuilder for Windows, so we issue a basic
# link command that works on at least one system and hope for the best.
sub link_executable {
my ( $self, %args ) = @_;
if ( $self->get_cc eq 'cl' ) {
my ( $objects, $exe_file ) = @args{qw( objects exe_file )};
$self->do_system("link /out:$exe_file @$objects");
return $exe_file;
}
else {
return $self->SUPER::link_executable(%args);
}
}
package Lucy::Build;
use base qw( Module::Build );
use File::Spec::Functions
qw( catdir catfile curdir splitpath updir no_upwards );
use File::Path qw( mkpath rmtree );
use File::Copy qw( copy move );
use File::Find qw( find );
use Module::Build::ModuleInfo;
use Config;
use Env qw( @PATH );
use Fcntl;
use Carp;
use Cwd qw( getcwd );
BEGIN { unshift @PATH, curdir() }
sub extra_ccflags {
my $self = shift;
my $extra_ccflags = defined $ENV{CFLAGS} ? "$ENV{CFLAGS} " : "";
my $gcc_version
= $ENV{REAL_GCC_VERSION}
|| $self->config('gccversion')
|| undef;
if ( defined $gcc_version ) {
$gcc_version =~ /^(\d+(\.\d+))/
or die "Invalid GCC version: $gcc_version";
$gcc_version = $1;
}
if ( defined $ENV{LUCY_DEBUG} ) {
if ( defined $gcc_version ) {
$extra_ccflags .= "-DLUCY_DEBUG ";
$extra_ccflags
.= "-DPERL_GCC_PEDANTIC -std=gnu99 -pedantic -Wall ";
$extra_ccflags .= "-Wextra " if $gcc_version >= 3.4; # correct
$extra_ccflags .= "-Wno-variadic-macros "
if $gcc_version > 3.4; # at least not on gcc 3.4
}
}
if ( $ENV{LUCY_VALGRIND} and defined $gcc_version ) {
$extra_ccflags .= "-fno-inline-functions ";
}
# Compile as C++ under MSVC.
if ( $self->config('cc') eq 'cl' ) {
$extra_ccflags .= '/TP ';
}
if ( defined $gcc_version ) {
# Tell GCC explicitly to run with maximum options.
if ( $extra_ccflags !~ m/-std=/ ) {
$extra_ccflags .= "-std=gnu99 ";
}
if ( $extra_ccflags !~ m/-D_GNU_SOURCE/ ) {
$extra_ccflags .= "-D_GNU_SOURCE ";
}
}
return $extra_ccflags;
}
=for Rationale
When the distribution tarball for the Perl binding of Lucy is built, core/,
charmonizer/, and any other needed files/directories are copied into the
perl/ directory within the main Lucy directory. Then the distro is built from
the contents of the perl/ directory, leaving out all the files in ruby/, etc.
However, during development, the files are accessed from their original
locations.
=cut
my $is_distro_not_devel = -e 'core';
my $base_dir = $is_distro_not_devel ? curdir() : updir();
my $CHARMONIZE_EXE_PATH = 'charmonize' . $Config{_exe};
my $CHARMONIZER_ORIG_DIR = catdir( $base_dir, 'charmonizer' );
my $CHARMONIZER_SRC_DIR = catdir( $CHARMONIZER_ORIG_DIR, 'src' );
my $SNOWSTEM_SRC_DIR
= catdir( $base_dir, qw( modules analysis snowstem source ) );
my $SNOWSTEM_INC_DIR = catdir( $SNOWSTEM_SRC_DIR, 'include' );
my $SNOWSTOP_SRC_DIR
= catdir( $base_dir, qw( modules analysis snowstop source ) );
my $CORE_SOURCE_DIR = catdir( $base_dir, 'core' );
my $CLOWNFISH_DIR = catdir( $base_dir, 'clownfish' );
my $CLOWNFISH_BUILD = catfile( $CLOWNFISH_DIR, 'Build' );
my $AUTOGEN_DIR = 'autogen';
my $XS_SOURCE_DIR = 'xs';
my $LIB_DIR = 'lib';
my $XS_FILEPATH = catfile( $LIB_DIR, "Lucy.xs" );
my $AUTOBIND_PM_PATH = catfile( $LIB_DIR, 'Lucy', 'Autobinding.pm' );
sub new { shift->SUPER::new( recursive_test_files => 1, @_ ) }
# Build the charmonize executable.
sub ACTION_charmonizer {
my $self = shift;
# Gather .c and .h Charmonizer files.
my $charm_source_files
= $self->rscan_dir( $CHARMONIZER_SRC_DIR, qr/Charmonizer.+\.[ch]$/ );
my $charmonize_c = catfile( $CHARMONIZER_ORIG_DIR, 'charmonize.c' );
my @all_source = ( $charmonize_c, @$charm_source_files );
# Don't compile if we're up to date.
return if $self->up_to_date( \@all_source, $CHARMONIZE_EXE_PATH );
print "Building $CHARMONIZE_EXE_PATH...\n\n";
my $cbuilder
= Lucy::Build::CBuilder->new( config => { cc => $self->config('cc') },
);
my @o_files;
for (@all_source) {
next unless /\.c$/;
next if m#Charmonizer/Test#;
my $o_file = $cbuilder->object_file($_);
$self->add_to_cleanup($o_file);
push @o_files, $o_file;
next if $self->up_to_date( $_, $o_file );
$cbuilder->compile(
source => $_,
include_dirs => [$CHARMONIZER_SRC_DIR],
extra_compiler_flags => $self->extra_ccflags,
);
}
$self->add_to_cleanup($CHARMONIZE_EXE_PATH);
my $exe_path = $cbuilder->link_executable(
objects => \@o_files,
exe_file => $CHARMONIZE_EXE_PATH,
);
}
# Run the charmonizer executable, creating the charmony.h file.
sub ACTION_charmony {
my $self = shift;
my $charmony_path = 'charmony.h';
$self->dispatch('charmonizer');
return if $self->up_to_date( $CHARMONIZE_EXE_PATH, $charmony_path );
print "\nWriting $charmony_path...\n\n";
# Clean up after Charmonizer if it doesn't succeed on its own.
$self->add_to_cleanup("_charm*");
$self->add_to_cleanup("*.pdb");
$self->add_to_cleanup($charmony_path);
# Prepare arguments to charmonize.
my $cc = $self->config('cc');
my $flags = $self->config('ccflags') . ' ' . $self->extra_ccflags;
my $verbosity = $ENV{DEBUG_CHARM} ? 2 : 1;
$flags =~ s/"/\\"/g;
if ( $ENV{CHARM_VALGRIND} ) {
system( "valgrind --leak-check=yes ./$CHARMONIZE_EXE_PATH $cc "
. "\"$flags\" $verbosity" )
and die "Failed to write charmony.h";
}
else {
system("./$CHARMONIZE_EXE_PATH \"$cc\" \"$flags\" $verbosity")
and die "Failed to write charmony.h: $!";
}
}
sub _compile_clownfish {
my $self = shift;
require Clownfish::Hierarchy;
require Clownfish::Binding::Perl;
require Clownfish::Binding::Perl::Class;
# Compile Clownfish.
my $hierarchy = Clownfish::Hierarchy->new(
source => $CORE_SOURCE_DIR,
dest => $AUTOGEN_DIR,
);
$hierarchy->build;
# Process all __BINDING__ blocks.
my $pm_filepaths = $self->rscan_dir( $LIB_DIR, qr/\.pm$/ );
my @pm_filepaths_with_xs;
for my $pm_filepath (@$pm_filepaths) {
open( my $pm_fh, '<', $pm_filepath )
or die "Can't open '$pm_filepath': $!";
my $pm_content = do { local $/; <$pm_fh> };
my ($autobind_frag)
= $pm_content =~ /^__BINDING__\s*(.*?)(?:^__\w+__|\Z)/sm;
if ($autobind_frag) {
push @pm_filepaths_with_xs, $pm_filepath;
eval $autobind_frag;
confess("Invalid __BINDING__ from $pm_filepath: $@") if $@;
}
}
my $binding = Clownfish::Binding::Perl->new(
parcel => 'Lucy',
hierarchy => $hierarchy,
lib_dir => $LIB_DIR,
boot_class => 'Lucy',
header => $self->autogen_header,
footer => '',
);
return ( $hierarchy, $binding, \@pm_filepaths_with_xs );
}
sub ACTION_pod {
my $self = shift;
$self->dispatch("build_clownfish");
$self->_write_pod(@_);
}
sub _write_pod {
my ( $self, $binding ) = @_;
if ( !$binding ) {
( undef, $binding ) = $self->_compile_clownfish;
}
my $pod_files = $binding->prepare_pod( lib_dir => $LIB_DIR );
print "Writing POD...\n";
while ( my ( $filepath, $pod ) = each %$pod_files ) {
$self->add_to_cleanup($filepath);
unlink $filepath;
sysopen( my $pod_fh, $filepath, O_CREAT | O_EXCL | O_WRONLY )
or confess("Can't open '$filepath': $!");
print $pod_fh $pod;
}
}
sub ACTION_build_clownfish {
my $self = shift;
my $old_dir = getcwd();
chdir($CLOWNFISH_DIR);
if ( !-f 'Build' ) {
print "\nBuilding Clownfish compiler... \n";
system("$^X Build.PL");
system("$^X Build code");
print "\nFinished building Clownfish compiler.\n\n";
}
chdir($old_dir);
}
sub ACTION_clownfish {
my $self = shift;
$self->dispatch('charmony');
$self->dispatch('build_clownfish');
# Create destination dir, copy xs helper files.
if ( !-d $AUTOGEN_DIR ) {
mkdir $AUTOGEN_DIR or die "Can't mkdir '$AUTOGEN_DIR': $!";
}
$self->add_to_cleanup($AUTOGEN_DIR);
my $pm_filepaths = $self->rscan_dir( $LIB_DIR, qr/\.pm$/ );
my $cfh_filepaths = $self->rscan_dir( $CORE_SOURCE_DIR, qr/\.cfh$/ );
# Don't bother parsing Clownfish files if everything's up to date.
return
if $self->up_to_date(
[ @$cfh_filepaths, @$pm_filepaths ],
[ $XS_FILEPATH, $AUTOGEN_DIR, ]
);
# Write out all autogenerated files.
print "Parsing Clownfish files...\n";
my ( $hierarchy, $perl_binding, $pm_filepaths_with_xs )
= $self->_compile_clownfish;
require Clownfish::Binding::Core;
my $core_binding = Clownfish::Binding::Core->new(
hierarchy => $hierarchy,
dest => $AUTOGEN_DIR,
header => $self->autogen_header,
footer => '',
);
print "Writing Clownfish autogenerated files...\n";
my $modified = $core_binding->write_all_modified;
if ($modified) {
unlink('typemap');
print "Writing typemap...\n";
$self->add_to_cleanup('typemap');
$perl_binding->write_xs_typemap;
}
# Rewrite XS if either any .cfh files or relevant .pm files were modified.
$modified ||=
$self->up_to_date( \@$pm_filepaths_with_xs, $XS_FILEPATH )
? 0
: 1;
if ($modified) {
$self->add_to_cleanup($XS_FILEPATH);
$self->add_to_cleanup($AUTOBIND_PM_PATH);
$perl_binding->write_boot;
$perl_binding->write_bindings;
$self->_write_pod($perl_binding);
}
# Touch autogenerated files in case the modifications were inconsequential
# and didn't trigger a rewrite, so that we won't have to check them again
# next pass.
if (!$self->up_to_date(
[ @$cfh_filepaths, @$pm_filepaths_with_xs ], $XS_FILEPATH
)
)
{
utime( time, time, $XS_FILEPATH ); # touch
}
if (!$self->up_to_date(
[ @$cfh_filepaths, @$pm_filepaths_with_xs ], $AUTOGEN_DIR
)
)
{
utime( time, time, $AUTOGEN_DIR ); # touch
}
}
# Write ppport.h, which supplies some XS routines not found in older Perls and
# allows us to use more up-to-date XS API while still supporting Perls back to
# 5.8.3.
#
# The Devel::PPPort docs recommend that we distribute ppport.h rather than
# require Devel::PPPort itself, but ppport.h isn't compatible with the Apache
# license.
sub ACTION_ppport {
my $self = shift;
if ( !-e 'ppport.h' ) {
require Devel::PPPort;
$self->add_to_cleanup('ppport.h');
Devel::PPPort::WriteFile();
}
}
sub ACTION_suppressions {
my $self = shift;
my $LOCAL_SUPP = 'local.supp';
return
if $self->up_to_date( '../devel/bin/valgrind_triggers.pl',
$LOCAL_SUPP );
# Generate suppressions.
print "Writing $LOCAL_SUPP...\n";
$self->add_to_cleanup($LOCAL_SUPP);
my $command
= "yes | "
. $self->_valgrind_base_command
. "--gen-suppressions=yes "
. $self->perl
. " ../devel/bin/valgrind_triggers.pl 2>&1";
my $suppressions = `$command`;
$suppressions =~ s/^==.*?\n//mg;
my $rule_number = 1;
while ( $suppressions =~ /<insert.a.*?>/ ) {
$suppressions =~ s/^\s*<insert.a.*?>/{\n <core_perl_$rule_number>/m;
$rule_number++;
}
# Change e.g. fun:_vgrZU_libcZdsoZa_calloc to fun:calloc
$suppressions =~ s/fun:\w+_((m|c|re)alloc)/fun:$1/g;
# Write local suppressions file.
open( my $supp_fh, '>', $LOCAL_SUPP )
or confess("Can't open '$LOCAL_SUPP': $!");
print $supp_fh $suppressions;
}
sub _valgrind_base_command {
return
"PERL_DESTRUCT_LEVEL=2 LUCY_VALGRIND=1 valgrind "
. "--leak-check=yes "
. "--show-reachable=yes "
. "--num-callers=10 "
. "--suppressions=../devel/conf/lucyperl.supp ";
}
sub ACTION_test_valgrind {
my $self = shift;
die "Must be run under a perl that was compiled with -DDEBUGGING"
unless $self->config('ccflags') =~ /-D?DEBUGGING\b/;
$self->dispatch('code');
$self->dispatch('suppressions');
# Unbuffer STDOUT, grab test file names and suppressions files.
$|++;
my $t_files = $self->find_test_files; # not public M::B API, may fail
my $valgrind_command = $self->_valgrind_base_command;
$valgrind_command .= "--suppressions=local.supp ";
if ( my $local_supp = $self->args('suppressions') ) {
for my $supp ( split( ',', $local_supp ) ) {
$valgrind_command .= "--suppressions=$supp ";
}
}
# Iterate over test files.
my @failed;
for my $t_file (@$t_files) {
# Run test file under Valgrind.
print "Testing $t_file...";
die "Can't find '$t_file'" unless -f $t_file;
my $command = "$valgrind_command $^X -Mblib $t_file 2>&1";
my $output = "\n" . ( scalar localtime(time) ) . "\n$command\n";
$output .= `$command`;
# Screen-scrape Valgrind output, looking for errors and leaks.
if ( $?
or $output =~ /ERROR SUMMARY:\s+[^0\s]/
or $output =~ /definitely lost:\s+[^0\s]/
or $output =~ /possibly lost:\s+[^0\s]/
or $output =~ /still reachable:\s+[^0\s]/ )
{
print " failed.\n";
push @failed, $t_file;
print "$output\n";
}
else {
print " succeeded.\n";
}
}
# If there are failed tests, print a summary list.
if (@failed) {
print "\nFailed "
. scalar @failed . "/"
. scalar @$t_files
. " test files:\n "
. join( "\n ", @failed ) . "\n";
exit(1);
}
}
sub ACTION_compile_custom_xs {
my $self = shift;
$self->dispatch('ppport');
require ExtUtils::ParseXS;
my $cbuilder
= Lucy::Build::CBuilder->new( config => { cc => $self->config('cc') },
);
my $archdir = catdir( $self->blib, 'arch', 'auto', 'Lucy', );
mkpath( $archdir, 0, 0777 ) unless -d $archdir;
my @include_dirs = (
curdir(), $CORE_SOURCE_DIR, $AUTOGEN_DIR, $XS_SOURCE_DIR,
$CHARMONIZER_SRC_DIR, $SNOWSTEM_INC_DIR
);
my @objects;
# Compile C source files.
my $c_files = [];
push @$c_files, @{ $self->rscan_dir( $CORE_SOURCE_DIR, qr/\.c$/ ) };
push @$c_files, @{ $self->rscan_dir( $XS_SOURCE_DIR, qr/\.c$/ ) };
push @$c_files, @{ $self->rscan_dir( $CHARMONIZER_SRC_DIR, qr/\.c$/ ) };
push @$c_files, @{ $self->rscan_dir( $AUTOGEN_DIR, qr/\.c$/ ) };
push @$c_files, @{ $self->rscan_dir( $SNOWSTEM_SRC_DIR, qr/\.c$/ ) };
push @$c_files, @{ $self->rscan_dir( $SNOWSTOP_SRC_DIR, qr/\.c$/ ) };
for my $c_file (@$c_files) {
my $o_file = $c_file;
my $ccs_file = $c_file;
$o_file =~ s/\.c/$Config{_o}/;
$ccs_file =~ s/\.c/.ccs/;
push @objects, $o_file;
next if $self->up_to_date( $c_file, $o_file );
$self->add_to_cleanup($o_file);
$self->add_to_cleanup($ccs_file);
$cbuilder->compile(
source => $c_file,
extra_compiler_flags => $self->extra_ccflags,
include_dirs => \@include_dirs,
object_file => $o_file,
);
}
# .xs => .c
my $perl_binding_c_file = catfile( $LIB_DIR, 'Lucy.c' );
$self->add_to_cleanup($perl_binding_c_file);
if ( !$self->up_to_date( $XS_FILEPATH, $perl_binding_c_file ) ) {
ExtUtils::ParseXS::process_file(
filename => $XS_FILEPATH,
prototypes => 0,
output => $perl_binding_c_file,
);
}
# .c => .o
my $lucy_pm_file = catfile( $LIB_DIR, 'Lucy.pm' );
my $info = Module::Build::ModuleInfo->new_from_file($lucy_pm_file);
my $version = $info->version;
my $perl_binding_o_file = catfile( $LIB_DIR, "Lucy$Config{_o}" );
unshift @objects, $perl_binding_o_file;
$self->add_to_cleanup($perl_binding_o_file);
if ( !$self->up_to_date( $perl_binding_c_file, $perl_binding_o_file ) ) {
$cbuilder->compile(
source => $perl_binding_c_file,
extra_compiler_flags => $self->extra_ccflags,
include_dirs => \@include_dirs,
object_file => $perl_binding_o_file,
# 'defines' is an undocumented parameter to compile(), so we
# should officially roll our own variant and generate compiler
# flags. However, that involves writing a bunch of
# platform-dependent code, so we'll just take the chance that this
# will break.
defines => {
VERSION => qq|"$version"|,
XS_VERSION => qq|"$version"|,
},
);
}
# Create .bs bootstrap file, needed by Dynaloader.
my $bs_file = catfile( $archdir, "Lucy.bs" );
$self->add_to_cleanup($bs_file);
if ( !$self->up_to_date( $perl_binding_o_file, $bs_file ) ) {
require ExtUtils::Mkbootstrap;
ExtUtils::Mkbootstrap::Mkbootstrap($bs_file);
if ( !-f $bs_file ) {
# Create file in case Mkbootstrap didn't do anything.
open( my $fh, '>', $bs_file )
or confess "Can't open $bs_file: $!";
}
utime( (time) x 2, $bs_file ); # touch
}
# Clean up after CBuilder under MSVC.
$self->add_to_cleanup('compilet*');
$self->add_to_cleanup('*.ccs');
$self->add_to_cleanup( catfile( 'lib', 'Lucy.ccs' ) );
$self->add_to_cleanup( catfile( 'lib', 'Lucy.def' ) );
$self->add_to_cleanup( catfile( 'lib', 'Lucy_def.old' ) );
$self->add_to_cleanup( catfile( 'lib', 'Lucy.exp' ) );
$self->add_to_cleanup( catfile( 'lib', 'Lucy.lib' ) );
$self->add_to_cleanup( catfile( 'lib', 'Lucy.lds' ) );
$self->add_to_cleanup( catfile( 'lib', 'Lucy.base' ) );
# .o => .(a|bundle)
my $lib_file = catfile( $archdir, "Lucy.$Config{dlext}" );
if ( !$self->up_to_date( [ @objects, $AUTOGEN_DIR ], $lib_file ) ) {
# TODO: use Charmonizer to determine whether pthreads are userland.
my $link_flags = $Config{osname} =~ /openbsd/i ? '-pthread ' : '';
$cbuilder->link(
module_name => 'Lucy',
objects => \@objects,
lib_file => $lib_file,
extra_linker_flags => $link_flags,
);
}
}
sub ACTION_code {
my $self = shift;
$self->dispatch('clownfish');
$self->dispatch('compile_custom_xs');
$self->SUPER::ACTION_code;
}
sub autogen_header {
my $self = shift;
return <<"END_AUTOGEN";
/***********************************************
!!!! DO NOT EDIT !!!!
This file was auto-generated by Build.PL.
***********************************************/
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
END_AUTOGEN
}
sub ACTION_dist {
my $self = shift;
$self->dispatch('pod');
# We build our Perl release tarball from $REPOS_ROOT/perl, rather than
# from the top-level.
#
# Because some items we need are outside this directory, we need to copy a
# bunch of stuff. After the tarball is packaged up, we delete the copied
# directories.
my @items_to_copy = qw(
core
modules
charmonizer
devel
clownfish
CHANGES
LICENSE
NOTICE
README
);
print "Copying files...\n";
for my $item (@items_to_copy) {
confess("'$item' already exists") if -e $item;
system("cp -R ../$item $item");
}
$self->dispatch('manifest');
my $no_index = $self->_gen_pause_exclusion_list;
$self->meta_add( { no_index => $no_index } );
$self->SUPER::ACTION_dist;
# Clean up.
print "Removing copied files...\n";
rmtree($_) for @items_to_copy;
unlink("META.yml");
move("MANIFEST.bak", "MANIFEST") or die "move() failed: $!";
}
# Generate a list of files for PAUSE, search.cpan.org, etc to ignore.
sub _gen_pause_exclusion_list {
my $self = shift;
# Only exclude files that are actually on-board.
open( my $man_fh, '<', 'MANIFEST' ) or die "Can't open MANIFEST: $!";
my @manifest_entries = <$man_fh>;
chomp @manifest_entries;
my @excluded_files;
for my $entry (@manifest_entries) {
# Allow README and Changes.
next if $entry =~ m#^(README|Changes)#;
# Allow public modules.
if ( $entry =~ m#^(perl/)?lib\b.+\.(pm|pod)$# ) {
open( my $fh, '<', $entry ) or die "Can't open '$entry': $!";
my $content = do { local $/; <$fh> };
next if $content =~ /=head1\s*NAME/;
}
# Disallow everything else.
push @excluded_files, $entry;
}
# Exclude redacted modules.
if ( eval { require "buildlib/Lucy/Redacted.pm" } ) {
my @redacted = map {
my @parts = split( /\W+/, $_ );
catfile( $LIB_DIR, @parts ) . '.pm'
} Lucy::Redacted->redacted, Lucy::Redacted->hidden;
push @excluded_files, @redacted;
}
my %uniquifier;
@excluded_files = sort grep { !$uniquifier{$_}++ } @excluded_files;
return { file => \@excluded_files };
}
sub ACTION_semiclean {
my $self = shift;
print "Cleaning up most build files.\n";
my @candidates
= grep { $_ !~ /(charmonizer|^_charm|charmony|charmonize|snowstem)/ }
$self->cleanup;
for my $path ( map { glob($_) } @candidates ) {
next unless -e $path;
rmtree($path);
confess("Failed to remove '$path'") if -e $path;
}
}
sub ACTION_clean {
my $self = shift;
if ( -e $CLOWNFISH_BUILD ) {
system("$^X $CLOWNFISH_BUILD clean")
and die "Clownfish clean failed";
}
$self->SUPER::ACTION_clean;
}
sub ACTION_realclean {
my $self = shift;
if ( -e $CLOWNFISH_BUILD ) {
system("$^X $CLOWNFISH_BUILD realclean")
and die "Clownfish realclean failed";
}
$self->SUPER::ACTION_realclean;
}
1;
__END__