blob: b4db46c56f8ae73422ccf648bbed8eb824396dcd [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;
use Getopt::Long;
use File::Spec::Functions qw( rel2abs catdir catfile updir );
use FindBin qw( $Bin );
use File::stat qw( stat );
# Process command line arguments.
my ( @probes, @user_files, @charm_files );
my $outfile;
GetOptions(
'probes:s' => \@probes,
'files=s' => \@user_files,
'out=s' => \$outfile,
);
my $usage = <<END_USAGE;
Usage:
meld.pl --files=FILES [--probes=PROBES] --out=OUTFILE
* probes -- A comma separated list of Charmonizer Probe names, e.g.
"Integers", "Integers,Floats,LargeFiles". Defaults to all probes if not
supplied.
* files -- A comma separated list of files to be appended verbatim. At
least one file must be present and it must define main().
* out -- The output file.
END_USAGE
die $usage unless @user_files;
die $usage unless $outfile;
$outfile = rel2abs($outfile);
@user_files = split( /,/, join( ',', @user_files ) );
@probes = split( /,/, join( ',', @probes ) );
# Slurp all user files.
my %user_file_content;
for my $file (@user_files) {
$user_file_content{$file} = slurp($file);
}
# Make sure we are in the charmonizer dir.
chdir( catdir( $Bin, updir() ) );
# Default to including all Probes.
if ( !@probes ) {
my $probe_dir = catdir(qw( src Charmonizer Probe ));
opendir( my $dh, $probe_dir ) or die "Can't opendir '$probe_dir': $!";
@probes = map { $_ =~ s/\.c$//; $_ } grep {/\.c$/} readdir $dh;
}
my @core = qw(
CFlags
CLI
Compiler
ConfWriter
ConfWriterC
ConfWriterPerl
ConfWriterPython
ConfWriterRuby
HeaderChecker
Make
OperatingSystem
Util
);
# Add Core headers.
for ( 'Defines', @core ) {
push @charm_files, catfile( qw( src Charmonizer Core ), "$_.h" );
}
push @charm_files, catfile(qw( src Charmonizer Probe.h ));
# Add specified Probe headers in lexically sorted order.
for ( sort @probes ) {
push @charm_files, catfile( qw( src Charmonizer Probe ), "$_.h" );
}
# Add Core implementation files.
for (@core) {
push @charm_files, catfile( qw( src Charmonizer Core ), "$_.c" );
}
push @charm_files, catfile(qw( src Charmonizer Probe.c ));
# Add Probe implementation files in lexically sorted order.
for ( sort @probes ) {
push @charm_files, catfile( qw( src Charmonizer Probe ), "$_.c" );
}
# Don't write out unless there has been an update.
if ( -e $outfile ) {
my $outfile_mtime = stat($outfile)->mtime;
my $needs_update;
for my $dependency ( @charm_files, @user_files ) {
die "Can't find '$dependency'" unless -e $dependency;
if ( stat($dependency)->mtime > $outfile_mtime ) {
$needs_update = 1;
last;
}
}
if ($needs_update) {
unlink $outfile;
}
else {
exit;
}
}
# Start the composite.
open( my $out_fh, '>', $outfile )
or die "Can't open '$outfile': $!";
binmode $out_fh;
print $out_fh meld_start();
# Process core files.
for my $file (@charm_files) {
print $out_fh pare_charm_file($file);
}
# Process user specified files.
for my $file (@user_files) {
my $content = $user_file_content{$file};
# Comment out pound-includes for files being inlined.
$content =~ s|^(#include "Charmonizer[^\n]+)\n|/* $1 */\n|msg;
print $out_fh line_directive(1, $file);
print $out_fh $content;
}
close $out_fh or die "Can't close '$outfile': $!";
exit;
sub pare_charm_file {
my $path = shift;
my $content = slurp($path);
my $num_newlines = $content =~ tr/\n/\n/;
# Strip license header.
$content =~ s#/\* Licensed to the Apache.+?\*/\n+##s
or die "Couldn't find ASF license header in '$path'";
# Remove opening C++ guards (if this is a header).
$content =~ s/^#ifdef __cplusplus.*?#endif\n+//ms;
# Add a #line directive.
my $new_num_newlines = $content =~ tr/\n/\n/;
my $starting_line = 1 + $num_newlines - $new_num_newlines;
$content = line_directive($starting_line, $path) . $content;
# Remove closing C++ guards (if this is a header).
$content =~ s/^#ifdef __cplusplus.*?#endif\n+//ms;
# Comment out pound-includes for files being inlined.
$content =~ s|^(#include "Charmonizer[^\n]+)\n|/* $1 */\n|msg;
return <<END_STUFF;
/***************************************************************************/
$content
END_STUFF
}
sub slurp {
my $path = shift;
open( my $fh, '<', $path ) or die "Can't open '$path': $!";
return do { local $/; <$fh> };
}
sub line_directive {
my ( $lineno, $file ) = @_;
$file =~ s'\\'/'g;
return qq|#line $lineno "$file"\n|;
}
sub meld_start {
return <<END_STUFF;
/* This is an auto-generated file -- do not edit directly. */
/* 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_STUFF
}