blob: e558b3b3d366633eb2a3009f70e465278fdfeba6 [file] [log] [blame]
#!/usr/bin/perl
# 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 Charmonizer::Build::Makefile;
use File::Find qw();
use FindBin;
use Carp qw( confess );
use Cwd qw( getcwd );
sub new {
my ( $class, %args ) = @_;
# Validate args, create object.
for (qw( dir filename obj_ext exe_ext cc )) {
defined $args{$_} or confess("Missing required param '$_'");
}
my $dir = $args{dir};
my $self = bless {
dir => $dir,
filename => $args{filename},
obj_ext => $args{obj_ext},
exe_ext => $args{exe_ext},
cc => $args{cc},
extra_cflags => $args{extra_cflags} || '',
extra_clean => $args{extra_clean} || '',
}, $class;
# Gather source paths, normalized for the target OS.
my $orig_dir = getcwd();
chdir($dir);
-d 'src' or confess("Can't find 'src' directory within '$dir'");
my ( @h_files, @c_files, @c_tests );
push @c_files, "charmonize.c";
File::Find::find(
{ wanted => sub {
if (/\.c$/) {
if (/^Test/) {
push @c_tests, $File::Find::name;
}
else {
push @c_files, $File::Find::name;
}
}
elsif (/\.h$/) {
push @h_files, $File::Find::name;
}
},
},
'src',
);
chdir($orig_dir);
$self->{c_files} = [ sort map { $self->pathify($_) } @c_files ];
$self->{h_files} = [ sort map { $self->pathify($_) } @h_files ];
$self->{c_tests} = [ sort map { $self->pathify($_) } @c_tests ];
$self->{c_test_cases}
= [ grep { $_ !~ /Test\.c/ } @{ $self->{c_tests} } ];
return $self;
}
sub pathify { confess "abstract method" }
sub unixify {
my ( $self, $path ) = @_;
$path =~ tr{\\}{/};
return $path;
}
sub winnify {
my ( $self, $path ) = @_;
$path =~ tr{/}{\\};
return $path;
}
sub objectify {
my ( $self, $c_file ) = @_;
$c_file =~ s/\.c$/$self->{obj_ext}/ or die "No match: $c_file";
return $c_file;
}
sub execify {
my ( $self, $file ) = @_;
$file =~ s/.*?(\w+)\.c$/$1$self->{exe_ext}/ or die "No match: $file";
return $file;
}
sub build_link_command {
my ( $self, %args ) = @_;
my $objects = join( " ", @{ $args{objects} } );
return "\$(CC) \$(CFLAGS) $objects -o $args{target}";
}
sub c2o_rule {
qq|.c.o:\n\t\$(CC) \$(CFLAGS) -c \$*.c -o \$@|;
}
sub test_block {
my ( $self, $c_test_case ) = @_;
my $exe = $self->execify($c_test_case);
my $obj = $self->objectify($c_test_case);
my $test_obj
= $self->pathify( $self->objectify("src/Charmonizer/Test.c") );
my $link_command = $self->build_link_command(
objects => [ $obj, $test_obj ],
target => '$@',
);
return qq|$exe: $test_obj $obj\n\t$link_command|;
}
sub clean_rule { confess "abstract method" }
sub clean_rule_posix {
qq|clean:\n\trm -f \$(CLEANABLE)|;
}
sub clean_rule_win {
qq|clean:\n\tCMD /c FOR %i IN (\$(CLEANABLE)) DO IF EXIST %i DEL /F %i|;
}
sub meld_rule { confess "abstract method" }
sub meld_rule_posix {
qq|meld:\n\t\$(PERL) buildbin/meld.pl --probes=\$(PROBES) |
. qq|--files=\$(FILES) --out=\$(OUT)|;
}
sub meld_rule_win {
qq|meld:\n\t\$(PERL) buildbin\\meld.pl --probes=\$(PROBES) |
. qq|--files=\$(FILES) --out=\$(OUT)|;
}
sub charmony_h_rule { confess "abstract method" }
sub charmony_h_rule_posix {
return <<"EOF";
\$(CHARMONY_H): \$(PROGNAME)
\t./\$(PROGNAME) --cc=\$(CC) --enable-c
EOF
}
sub charmony_h_rule_win {
return <<"EOF";
\$(CHARMONY_H): \$(PROGNAME)
\t\$(PROGNAME) --cc=\$(CC) --enable-c
EOF
}
sub test_rule { confess "abstract method" }
sub test_rule_posix {
return <<"EOF";
test: tests
\tprove ./Test*
EOF
}
sub test_rule_win {
return <<"EOF";
test: tests
\tprove Test*
EOF
}
sub gen_makefile {
my ( $self, %args ) = @_;
my ( $h_files, $c_files, $c_tests, $c_test_cases )
= @$self{qw( h_files c_files c_tests c_test_cases )};
# Derive chunks of Makefile content.
my $progname = $self->execify('charmonize.c');
my $c2o_rule = $self->c2o_rule;
my $meld_rule = $self->meld_rule;
my $charmony_h_rule = $self->charmony_h_rule;
my $test_rule = $self->test_rule;
my $progname_link_command = $self->build_link_command(
objects => ['$(OBJS)'],
target => '$(PROGNAME)',
);
my $clean_rule = $self->clean_rule;
my $objs = join " ", map { $self->objectify($_) } @$c_files;
my $test_objs = join " ", map { $self->objectify($_) } @$c_tests;
my $test_blocks = join "\n\n",
map { $self->test_block($_) } @$c_test_cases;
my $test_execs = join " ", map { $self->execify($_) } @$c_test_cases;
my $headers = join " ", @$h_files;
# Write out Makefile content.
open my $fh, ">", $self->{filename}
or die "open '$self->{filename}' failed: $!\n";
my $content = <<EOT;
# GENERATED BY $FindBin::Script: do not hand-edit!!!
# 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.
CC= $self->{cc}
DEFS=
CFLAGS= -I. -Isrc \$(DEFS) $self->{extra_cflags}
PROGNAME= $progname
CHARMONY_H= charmony.h
PROBES=
FILES=
OUT=
PERL=/usr/bin/perl
TESTS= $test_execs
OBJS= $objs
TEST_OBJS= $test_objs
HEADERS= $headers
CLEANABLE= \$(OBJS) \$(PROGNAME) \$(CHARMONY_H) \$(TEST_OBJS) \$(TESTS) $self->{extra_clean}
$c2o_rule
all: \$(PROGNAME)
$meld_rule
$charmony_h_rule
\$(PROGNAME): \$(OBJS)
\t$progname_link_command
\$(OBJS) \$(TEST_OBJS): \$(HEADERS)
\$(TEST_OBJS): \$(CHARMONY_H)
tests: \$(TESTS)
$test_blocks
$test_rule
$clean_rule
EOT
print $fh $content;
}
package Charmonizer::Build::Makefile::Posix;
BEGIN { our @ISA = qw( Charmonizer::Build::Makefile ) }
sub new {
my $class = shift;
return $class->SUPER::new(
filename => 'Makefile',
obj_ext => '.o',
exe_ext => '',
cc => 'cc',
@_
);
}
sub clean_rule { shift->clean_rule_posix }
sub meld_rule { shift->meld_rule_posix }
sub charmony_h_rule { shift->charmony_h_rule_posix }
sub test_rule { shift->test_rule_posix }
sub pathify { shift->unixify(@_) }
package Charmonizer::Build::Makefile::MSVC;
BEGIN { our @ISA = qw( Charmonizer::Build::Makefile ) }
sub new {
my $class = shift;
my $flags
= '-nologo -D_CRT_SECURE_NO_WARNINGS ' . '-D_SCL_SECURE_NO_WARNINGS';
return $class->SUPER::new(
filename => 'Makefile.MSVC',
obj_ext => '.obj',
exe_ext => '.exe',
cc => 'cl',
extra_cflags => $flags,
extra_clean => '*.pdb',
@_
);
}
sub c2o_rule {
qq|.c.obj:\n\t\$(CC) \$(CFLAGS) -c \$< -Fo\$@|;
}
sub build_link_command {
my ( $self, %args ) = @_;
my $objects = join( " ", @{ $args{objects} } );
return "link -nologo $objects /OUT:$args{target}";
}
sub pathify { shift->winnify(@_) }
sub clean_rule { shift->clean_rule_win }
sub meld_rule { shift->meld_rule_win }
sub charmony_h_rule { shift->charmony_h_rule_win }
sub test_rule { shift->test_rule_win }
package Charmonizer::Build::Makefile::MinGW;
BEGIN { our @ISA = qw( Charmonizer::Build::Makefile ) }
sub new {
my $class = shift;
return $class->SUPER::new(
filename => 'Makefile.MinGW',
obj_ext => '.o',
exe_ext => '.exe',
cc => 'gcc',
@_
);
}
sub pathify { shift->winnify(@_) }
sub clean_rule { shift->clean_rule_win }
sub meld_rule { shift->meld_rule_win }
sub charmony_h_rule { shift->charmony_h_rule_win }
sub test_rule { shift->test_rule_win }
### actual script follows
package main;
my $makefile_posix = Charmonizer::Build::Makefile::Posix->new( dir => '.' );
my $makefile_msvc = Charmonizer::Build::Makefile::MSVC->new( dir => '.' );
my $makefile_mingw = Charmonizer::Build::Makefile::MinGW->new( dir => '.' );
$makefile_posix->gen_makefile;
$makefile_msvc->gen_makefile;
$makefile_mingw->gen_makefile;
__END__
=head1 NAME
gen_charmonizer_makefiles.pl
=head1 SYNOPSIS
gen_charmonizer_makefiles.pl - keeps the Makefiles in sync with the live tree.
=head1 DESCRIPTION
Be sure to run this code from the charmonizer subdirectory (where the
existing Makefiles live).