| # 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; |
| |
| package Clownfish::Build; |
| |
| my $IS_CPAN_DIST; |
| |
| BEGIN { |
| $IS_CPAN_DIST = -e 'cfcore'; |
| |
| if (!$IS_CPAN_DIST) { |
| unshift @INC, |
| '../../compiler/perl/blib/lib', |
| '../../compiler/perl/blib/arch', |
| '../../compiler/perl/lib'; # blib dir might not exist yet. |
| } |
| } |
| |
| use base qw( |
| Clownfish::CFC::Perl::Build |
| Clownfish::CFC::Perl::Build::Charmonic |
| ); |
| |
| our $VERSION = '0.006003'; |
| $VERSION = eval $VERSION; |
| |
| use File::Spec::Functions qw( catdir catfile updir rel2abs ); |
| use File::Path qw( rmtree ); |
| use File::Copy qw( move ); |
| use Config; |
| use Carp; |
| use Cwd qw( getcwd ); |
| |
| my @BASE_PATH = __PACKAGE__->cf_base_path; |
| |
| my $COMMON_SOURCE_DIR = catdir( @BASE_PATH, 'common' ); |
| my $CFC_DIR = catdir( @BASE_PATH, updir(), 'compiler', 'perl' ); |
| my $XS_SOURCE_DIR = 'xs'; |
| my $CFC_BUILD = catfile( $CFC_DIR, 'Build' ); |
| my $LIB_DIR = 'lib'; |
| my $CORE_SOURCE_DIR; |
| my $TEST_SOURCE_DIR; |
| my $CHARMONIZER_C; |
| if ($IS_CPAN_DIST) { |
| $CORE_SOURCE_DIR = 'cfcore'; |
| $TEST_SOURCE_DIR = 'cftest'; |
| $CHARMONIZER_C = 'charmonizer.c'; |
| } |
| else { |
| $CORE_SOURCE_DIR = catdir( @BASE_PATH, 'core' ); |
| $TEST_SOURCE_DIR = catdir( @BASE_PATH, 'test' ); |
| $CHARMONIZER_C = catfile( $COMMON_SOURCE_DIR, 'charmonizer.c' ); |
| } |
| |
| sub new { |
| my ( $class, %args ) = @_; |
| $args{include_dirs} = [ $CORE_SOURCE_DIR, $XS_SOURCE_DIR ]; |
| $args{clownfish_params} = { |
| autogen_header => _autogen_header(), |
| include => [], # Don't use default includes. |
| source => [ $CORE_SOURCE_DIR, $TEST_SOURCE_DIR ], |
| modules => [ |
| { |
| name => 'Clownfish', |
| parcels => [ 'Clownfish' ], |
| make_target => 'core_objects', |
| c_source_dirs => [ $XS_SOURCE_DIR ], |
| }, |
| { |
| name => 'Clownfish::Test', |
| parcels => [ 'TestClownfish' ], |
| make_target => 'test_objects', |
| }, |
| ], |
| }; |
| my $self = $class->SUPER::new( recursive_test_files => 1, %args ); |
| |
| # Fix for MSVC: Although the generated XS should be C89-compliant, it |
| # must be compiled in C++ mode like the rest of the code due to a |
| # mismatch between the sizes of the C++ bool type and the emulated bool |
| # type. (The XS code is compiled with Module::Build's extra compiler |
| # flags, not the Clownfish cflags.) |
| if ($Config{cc} =~ /^cl\b/) { |
| my $extra_cflags = $self->extra_compiler_flags; |
| push @$extra_cflags, '/TP'; |
| $self->extra_compiler_flags(@$extra_cflags); |
| } |
| |
| $self->charmonizer_params( create_makefile => 1 ); |
| $self->charmonizer_params( charmonizer_c => $CHARMONIZER_C ); |
| |
| return $self; |
| } |
| |
| sub _run_make { |
| my ( $self, %params ) = @_; |
| my @command = @{ $params{args} }; |
| my $dir = $params{dir}; |
| my $current_directory = getcwd(); |
| chdir $dir if $dir; |
| unshift @command, 'CC=' . $self->config('cc'); |
| if ( $self->config('cc') =~ /^cl\b/ ) { |
| unshift @command, "-f", "Makefile.MSVC"; |
| } |
| elsif ( $^O =~ /mswin/i ) { |
| unshift @command, "-f", "Makefile.MinGW"; |
| } |
| unshift @command, "$Config{make}"; |
| system(@command) and confess("$Config{make} failed"); |
| chdir $current_directory if $dir; |
| } |
| |
| sub ACTION_cfc { |
| my $self = shift; |
| return if $IS_CPAN_DIST; |
| my $old_dir = getcwd(); |
| chdir($CFC_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_code { |
| my $self = shift; |
| |
| $self->SUPER::ACTION_code; |
| |
| $self->cf_copy_include_file( 'XSBind.h' ); |
| |
| # Check whether 'use Clownfish::Test' succeeds. If this fails, it is |
| # probably caused by a failure to find symbols from Clownfish.so, |
| # indicating a build problem. In this case, make the build fail, so |
| # we get the full build log on CPAN Testers. Also print contents of |
| # the generated Makefile. |
| my $error = system("$^X -Mblib -MClownfish::Test -e1"); |
| if ($error) { |
| print STDERR "Build succeeded, but 'use Clownfish::Test' failed.\n"; |
| if ($ENV{AUTOMATED_TESTING}) { |
| my $makefile = do { local(@ARGV, $/) = 'Makefile'; <> }; |
| print STDERR "\nContents of Makefile:\n$makefile"; |
| } |
| die; |
| } |
| } |
| |
| sub ACTION_clownfish { |
| my $self = shift; |
| |
| $self->depends_on('cfc'); |
| |
| $self->SUPER::ACTION_clownfish; |
| |
| # Make sure to remove empty directory. |
| $self->add_to_cleanup( catdir( $LIB_DIR, 'Clownfish', 'Docs' ) ); |
| } |
| |
| sub ACTION_compile_custom_xs { |
| my $self = shift; |
| |
| $self->depends_on('charmony'); |
| |
| # Add extra compiler flags from Charmonizer. |
| my $charm_cflags = $self->charmony('EXTRA_CFLAGS'); |
| if ($charm_cflags) { |
| my $cf_cflags = $self->clownfish_params('cflags'); |
| if ($cf_cflags) { |
| $cf_cflags .= " $charm_cflags"; |
| } |
| else { |
| $cf_cflags = $charm_cflags; |
| } |
| $self->clownfish_params( cflags => $cf_cflags ); |
| } |
| |
| # Add extra linker flags from Charmonizer. |
| my $charm_ldflags = $self->charmony('EXTRA_LDFLAGS'); |
| if ($charm_ldflags) { |
| my $extra_ldflags = $self->extra_linker_flags; |
| push @$extra_ldflags, $self->split_like_shell($charm_ldflags); |
| $self->extra_linker_flags(@$extra_ldflags); |
| } |
| |
| $self->SUPER::ACTION_compile_custom_xs; |
| } |
| |
| sub _valgrind_base_command { |
| return |
| "PERL_DESTRUCT_LEVEL=2 CLOWNFISH_VALGRIND=1 valgrind " |
| . "--leak-check=yes " |
| . "--show-reachable=yes " |
| . "--dsymutil=yes " |
| . "--suppressions=../../devel/conf/cfruntime-perl.supp "; |
| } |
| |
| # Run the entire test suite under Valgrind. |
| # |
| # For this to work, the test suite must be run under a debugging Perl. |
| # |
| # A custom suppressions file will probably be needed -- use your judgment. |
| # To pass in one or more local suppressions files, provide a comma separated |
| # list like so: |
| # |
| # $ ./Build test_valgrind --suppressions=foo.supp,bar.supp |
| sub ACTION_test_valgrind { |
| my $self = shift; |
| # Debian's debugperl uses the Config.pm of the standard system perl |
| # so -DDEBUGGING won't be detected. |
| die "Must be run under a perl that was compiled with -DDEBUGGING" |
| unless $self->config('ccflags') =~ /-D?DEBUGGING\b/ |
| || $^X =~ /\bdebugperl\b/; |
| $self->depends_on('code'); |
| |
| # 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; |
| |
| 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 _autogen_header { |
| 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 |
| } |
| |
| # Run the cleanup targets for independent prerequisite builds. |
| sub _clean_prereq_builds { |
| my $self = shift; |
| if ( -e $CFC_BUILD ) { |
| my $old_dir = getcwd(); |
| chdir $CFC_DIR; |
| system("$^X Build realclean") |
| and die "Clownfish clean failed"; |
| chdir $old_dir; |
| } |
| } |
| |
| sub ACTION_clean { |
| my $self = shift; |
| _clean_prereq_builds($self); |
| $self->SUPER::ACTION_clean; |
| } |
| |
| sub ACTION_dist { |
| my $self = shift; |
| |
| die("Module::Build 0.40_11 is required for ./Build dist") |
| if $Module::Build::VERSION < 0.40_11; |
| |
| # Create POD. |
| $self->depends_on('clownfish'); |
| rmtree("autogen"); |
| |
| # We build our Perl release tarball from a subdirectory rather than from |
| # the top-level $REPOS_ROOT. Because some assets we need are outside this |
| # directory, we need to copy them in. |
| my %to_copy = ( |
| '../../CHANGES' => 'CHANGES', |
| '../../CONTRIBUTING.md' => 'CONTRIBUTING.md', |
| '../../LICENSE' => 'LICENSE', |
| '../../NOTICE' => 'NOTICE', |
| '../../README.md' => 'README.md', |
| $CORE_SOURCE_DIR => 'cfcore', |
| $TEST_SOURCE_DIR => 'cftest', |
| $CHARMONIZER_C => 'charmonizer.c', |
| ); |
| print "Copying files...\n"; |
| while ( my ( $from, $to ) = each %to_copy ) { |
| confess("'$to' already exists") if -e $to; |
| system("cp -R $from $to") and confess("cp failed"); |
| } |
| move( "MANIFEST", "MANIFEST.bak" ) or die "move() failed: $!"; |
| $self->depends_on("manifest"); |
| $self->SUPER::ACTION_dist; |
| |
| # Now that the tarball is packaged up, delete the copied assets. |
| rmtree($_) for values %to_copy; |
| unlink("META.yml"); |
| unlink("META.json"); |
| move( "MANIFEST.bak", "MANIFEST" ) or die "move() failed: $!"; |
| } |
| |
| 1; |
| |