Created
July 31, 2011 03:24
-
-
Save FurryHead/1116342 to your computer and use it in GitHub Desktop.
Makefile generator in Perl
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/perl -w | |
use strict; | |
use Getopt::Std; | |
########The script defaults######### | |
my $make_outfile = "PerlMakefile"; | |
my $exe_outfile = "p.out"; | |
my $compiler = "gcc"; | |
my $src_dir = "./src"; | |
my $obj_dir = "./obj"; | |
my $ext = "c"; | |
my $lflags = ""; | |
my $cflags = ""; | |
my $rm = "rm -f"; | |
#################################### | |
my %deps; | |
sub get_deps { | |
my ($fname) = @_; | |
if ($fname !~ /(\/|\\)/) { | |
$fname = $src_dir."/".$fname; | |
} | |
open(CFILE, "<$fname") || die("Error: No such file '$fname' "); | |
foreach my $line (<CFILE>) { | |
chomp $line; | |
if ($line =~ /^# *include *"(.+)"/i) { | |
$line =~ s/^# *include *"(.+).h"/$1/i; | |
$line = $src_dir."/".$line; | |
(my $fobj = $fname) =~ s/((\.$ext)|(\.h))/\.o/i; | |
$fobj =~ s/$src_dir/$obj_dir/i; | |
if (not $deps{$fobj}) { | |
$deps{$fobj} = (); | |
} | |
${$deps{$fobj}}{$line.".".$ext} = 1 if (-f $line.".".$ext); | |
${$deps{$fobj}}{$line.".h"} = 1 if (-f $line.".h"); | |
${$deps{$fobj}}{$fname} = 1; | |
&get_deps($line.".".$ext) if (-f $line.".".$ext && not ${$deps{$fobj}}{$line.".".$ext}); | |
&get_deps($line.".h") if (-f $line.".h" && not ${$deps{$fobj}}{$line.".h"}); | |
} | |
} | |
close(CFILE); | |
} | |
sub print_help { | |
print <<EOF | |
FurryHead's Makefile generator. | |
Usage: $0 [-h] [-p] [-e <exe_name>] [-m <makefile_name>] [-s <src_dir>] [-o <obj_dir>] [-t <ext>] [-k <compiler>] [-l "extra link flags"] [-c "extra compile flags"] | |
-e Specifies the (project) executable output file name. Defaults to '${exe_outfile}' | |
-m Specifies the output Makefile name. Defaults to '${make_outfile}' | |
-s Specifies the directory containing the source files. Defaults to '${src_dir}' | |
-o Specifies the directory containing the object files. Defaults to '${obj_dir}' | |
-t The file extension to scan for deps, for example "cxx". Defaults to '${ext}' | |
-k The compiler to use, i.e. "g++" or "gcc". Defaults to '${compiler}' | |
-c Anything you'd normally put in CFLAGS= (compile flags/options). Defaults to '${cflags}' | |
-l Anything you'd normally put in LFLAGS= (link flags/options). Defaults to '${lflags}' | |
-r The command to execute for removing files (make clean). Defaults to '${rm}' | |
-p (Linux only) Adds total progress updates to compile steps. (I.e. "3 of 4 remaining") | |
-h Prints this help. | |
The generator defaults can be set at the top of the script file ($0) | |
EOF | |
; | |
} | |
my @original_args = @ARGV; | |
my %args; | |
getopts("hpm:o:s:e:t:k:c:l:r:", \%args) || &print_help && exit; | |
&print_help && exit if $args{h}; | |
my $starttime = scalar time; | |
$make_outfile = $args{m} if ($args{m}); | |
$exe_outfile = $args{e} if $args{e}; | |
$src_dir = $args{s} if $args{s}; | |
$obj_dir = $args{o} if $args{o}; | |
$ext = $args{t} if $args{t}; | |
$compiler = $args{k} if $args{k}; | |
$cflags = $args{c} if $args{c}; | |
$lflags = $args{l} if $args{l}; | |
$rm = $args{r} if $args{r}; | |
my $progress = ($args{p} ? 1 : 0); | |
print "Gathering dependencies...\n"; | |
my $time = time; | |
opendir(SDIR, $src_dir) || die("No such directory: $src_dir"); | |
while (my $f = readdir(SDIR)) { | |
next unless ($f =~ /\.(${ext}|[h])$/); | |
&get_deps($src_dir."/".$f); | |
} | |
closedir(SDIR); | |
print "Finished in " . (time - $time) . " seconds.\n"; | |
my @objs = keys(%deps); | |
print "Generating Makefile ($make_outfile)...\n"; | |
$time = time; | |
open MAKEFILE, ">$make_outfile"; | |
print MAKEFILE <<EOF | |
CC=$compiler | |
OUT=$exe_outfile | |
RM=$rm | |
EOF | |
; | |
if ($progress) { | |
print MAKEFILE <<EOF | |
# I don't really understand this myself, | |
# I copied it off a post on stackoverflow.com | |
# But it lets me display how many steps in the build remain | |
ifndef BUILD | |
sixteen := x x x x x x x x x x x x x x x x | |
MAX := \$(foreach x,\$(sixteen),\$(sixteen)) | |
T := \$(shell \$(MAKE) -nrRf \$(firstword \$(MAKEFILE_LIST)) \$(MAKECMDGOALS) BUILD="COUNTTHIS" | grep -c "COUNTTHIS") | |
N := \$(wordlist 1,\$T,\$(MAX)) | |
counter = \$(words \$N)\$(eval N := \$(wordlist 2,\$(words \$N),\$N)) | |
BUILD = \$(counter)/\$(T) | |
endif | |
EOF | |
; | |
} | |
print MAKEFILE <<EOF | |
OBJS=@{objs} | |
CFLAGS=$cflags | |
LFLAGS=$lflags | |
all : \$(OUT) | |
\techo "Finished." | |
\$(OUT) : \$(OBJS) | |
EOF | |
; | |
if ($progress) { | |
print MAKEFILE "\techo \"(\$(BUILD)) Linking \$(OUT)...\""; | |
} else { | |
print MAKEFILE "\techo \"Linking \$(OUT)...\""; | |
} | |
print MAKEFILE "\n\t\$(CC) \$(LFLAGS) \$(OBJS) -o \$(OUT)\n\n"; | |
foreach my $obj (sort(@objs)) { | |
my %written; | |
my @f; | |
foreach my $z (sort(keys(%{$deps{$obj}}))) { | |
(my $o = $z) =~ s/\.(h|$ext)$//i; | |
my $ob = $o.".o"; | |
$ob =~ s/$src_dir/$obj_dir/i; | |
if ($ob ne $obj && !$written{$ob} && -f $o.".h" && -f $o.".$ext" && !$written{$z}) { | |
$written{$ob} = 1; | |
$written{$o.".$ext"} = 1; | |
$written{$o.".h"} = 1; | |
push(@f, $ob); | |
} else { | |
if (!$written{$z}) { | |
$written{$z} = 1; | |
push(@f, $z); | |
} | |
} | |
} | |
print MAKEFILE "$obj :"; | |
@f = sort(@f); | |
#these two loops are to ensure the source files | |
#get listed first (and object files last), | |
#otherwise make/gcc complains | |
foreach my $t (@f) { | |
next if ($t =~ /\.o$/); | |
print MAKEFILE " $t"; | |
} | |
foreach my $t (@f) { | |
next if ($t !~ /\.o$/); | |
print MAKEFILE " $t"; | |
} | |
print MAKEFILE "\n"; | |
if ($progress) { | |
print MAKEFILE "\techo \"(\$(BUILD)) Compiling \$< to \$@...\"\n"; | |
} else { | |
print MAKEFILE "\techo \"Compiling \$< to \$@...\"\n"; | |
} | |
print MAKEFILE "\t\$(CC) \$(CFLAGS) -c \$< -o \$@\n"; | |
print MAKEFILE "\n"; | |
} | |
print MAKEFILE <<EOF | |
clean: | |
\t\$(RM) \$(OUT) | |
\t\$(RM) \$(OBJS) | |
.SILENT: | |
EOF | |
; | |
close MAKEFILE; | |
print "Finished in " . (time - $time) . " seconds.\n"; | |
print "Makefile generation complete. Total time taken: " . (time - $starttime) . " seconds\n"; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment