|
|
#!perl use IO::File; use File::Basename; use File::Find; use Cwd; use Cwd 'abs_path';
$nprocs=`grep vendor_id /proc/cpuinfo | wc -l `; $nprocs=~s/[\n\r]//g; print "$nprocs processors found\n";
#find where to include master make file from $srcdir=getcwd; die "can't determine path to src" unless ($srcdir=~s@/src.*$@/src@);
find( { wanted=> \&handle_vpc_file } ,"$srcdir"); # search through all directories for .vpc files
@MAINTARGETS=("all", "clean", "objs"); @TARGETS=("all", "clean", "objs", "tags");
# now, write a master makefile in each dir, and a master-master makefile in ~/src foreach $dir ( keys %dir_written ) { open( MAKEOUT,">$dir/Makefile" ) || die "can't write $dir/Makefile"; foreach $target ( @TARGETS ) { print MAKEOUT ".PHONY: $target\n\n"; print MAKEOUT "$target:\n"; foreach $_ (split(/,/,$dir_written{$dir}) ) { print MAKEOUT "\tmake -j $nprocs -f $_ $target\n" if length($_); } } close MAKEOUT; }
# now, write a master makefile in ~/src open( MAKEOUT,">$srcdir/Makefile" ) || die "can't write master makefile to $srcdir"; foreach $target ( @MAINTARGETS ) { print MAKEOUT ".PHONY: $target\n\n"; print MAKEOUT "$target:\n"; foreach $dir ( keys %dir_written ) { if ($target ne "clean" ) { print MAKEOUT "\tmake -j $nprocs -C $dir $target\n"; } else { print MAKEOUT "\tmake -C $dir $target\n"; }
} } print MAKEOUT "\n\nmakefiles:\n\tperl $srcdir/devtools/bin/vpc2linuxmake.pl\n"; print MAKEOUT "\ntags:\n\tctags --languages=c++ -eR\n";
close MAKEOUT;
sub handle_vpc_file { # called for each file in the callers dir tree my $dir=$File::Find::dir; return if ( $dir=~/vpc_scripts/i ); if ( /_base\.vpc$/i ) { unless ( /hk_base\.vpc$/i ) { return; } } return if (/_inc\.vpc/i);
if (/\.vpc$/) { (%ignore_file,@DEFINES, @CPPFILES, @CXXFILES,@CFILES, @LITERAL_LIBFILES,@LIBFILES, %define_seen,%macros,%include_seen,@INCLUDEDIRS)=undef; undef $buildforlinux; undef $conf_type; undef $gccflags; $OptimizeLevel=3;
# some defines to ignore in vpc files when generating linux include files
$define_seen{'WIN32'}=1; $define_seen{'_WIN32'}=1; $define_seen{'_WINDOWS'}=1; $define_seen{'_USRDLL'}=1; $define_seen{'DEBUG'}=1; $define_seen{'_DEBUG'}=1; $define_seen{'NDEBUG'}=1; $define_seen{'_CRT_SECURE_NO_DEPRECATE'}=1; $define_seen{'_CRT_NONSTDC_NO_DEPRECATE'}=1; $define_seen{'fopen'}=1;
# print STDERR "parsing project $pname\n"; &ParseVPC($_);
$pname=lc($pname); $pname=~s/\s+/_/g; $pname=~s/[\(\)]//g; # if anything seen, output a makefile if ( $buildforlinux && ( @CPPFILES || @CXXFILES || @CFILES || @LIBFILES ) ) { print STDERR "writing project $pname\n"; $projdir=getcwd; $projdir=~s@/$@@; $dir_written{$projdir}.=",$pname.mak"; &WriteMakefile("$projdir/$pname.mak"); &WriteCodeBlocksProj("$projdir/$pname.cbp"); } else { die "no .lib or source files found in .vpc" if ( $buildforlinux ); } } }
sub WriteCodeBlocksProj { local($_)=@_;
open(CBPROJ,">$_") || die "can't write $_";
print CBPROJ <<HEADER <?xml version="1.0" encoding="UTF-8" standalone="yes" ?> <CodeBlocks_project_file> <FileVersion major="1" minor="6" /> <Project> <Option title="$pname" /> <Option pch_mode="2" /> <Option compiler="gcc" /> <Build> <Target title="Release">
</Target> </Build> HEADER ;
foreach $fl (@CPPFILES) { push @cppfiles2, $fl unless ( $ignore_file{$fl} > 0 ); }
foreach $fl (@CXXFILES) { push @cxxfiles2, $fl unless ( $ignore_file{$fl} > 0 ); }
printf CBPROJ "\t\t<Compiler>\n";
foreach $_ (@DEFINES) { print CBPROJ "\t\t\t<Add option=\"-DSWDS\" />\n"; print CBPROJ "\t\t\t<Add option=\"-D_LINUX\" />\n"; print CBPROJ "\t\t\t<Add option=\"-fpermissive\" />\n"; print CBPROJ "\t\t\t<Add option=\"-Dstricmp=strcasecmp\" />\n"; print CBPROJ "\t\t\t<Add option=\"-D_stricmp=strcasecmp\" />\n"; print CBPROJ "\t\t\t<Add option=\"-D_strnicmp=strncasecmp\" />\n"; print CBPROJ "\t\t\t<Add option=\"-Dstrnicmp=strncasecmp\" />\n"; print CBPROJ "\t\t\t<Add option=\"-D_snprintf=snprintf\" />\n"; print CBPROJ "\t\t\t<Add option=\"-D_vsnprintf=vsnprintf\" />\n"; print CBPROJ "\t\t\t<Add option=\"-D_alloca=alloca\" />\n"; print CBPROJ "\t\t\t<Add option=\"-Dstrcmpi=strcasecmp\" />\n";
print CBPROJ "\t\t\t<Add option=\"-D$_\" />\n"; }
foreach $_ (@INCLUDEDIRS) { print CBPROJ "\t\t\t<Add directory=\"$_\" />\n"; }
printf CBPROJ "\t\t</Compiler>\n";
@CPPFILES = sort(@CPPFILES); @CXXFILES = sort(@CXXFILES); @CFILES = sort(@CFILES);
# now, output obj dependencies foreach $_ (@CPPFILES, @CFILES, @CXXFILES) { unless (( $ignore_file{$_} > 0 ) || ( length($_) < 2 ) ) { ($filename,$dir,$suffix) = fileparse($_,qr/\.[^.]*/);
print CBPROJ "\t\t<Unit filename=\"".$dir . $filename. ".cpp\" />\n"; } }
print CBPROJ <<FOOTER <Extensions> <code_completion /> </Extensions> </Project> </CodeBlocks_project_file> FOOTER ;
close CBPROJ;
}
sub WriteMakefile { local($_)=@_; open(MAKEFILE,">$_") || die "can't write $_"; print MAKEFILE "NAME=$pname\n\n"; print MAKEFILE "SRCROOT=$srcdir\n"; print MAKEFILE "PROJDIR=$projdir\n"; print MAKEFILE "CONFTYPE=$conf_type\n"; print MAKEFILE "PROJECT_SPECIFIC_GCCFLAGS = $gccflags\n";
if ( int($OptimizeLevel) ) { print MAKEFILE "OLEVEL=-O$OptimizeLevel\n"; } else { print MAKEFILE "OLEVEL=\n"; } if (@DEFINES) { print MAKEFILE "DEFINES= -D",join(" -D", @DEFINES),"\n"; } if (@INCLUDEDIRS) { print MAKEFILE "INCLUDEDIRS= -I",join(" -I", @INCLUDEDIRS),"\n"; } undef @cppfiles2; undef @cxxfiles2; foreach $fl (@CPPFILES) { if ( length($fl) ) { print "warning file $fl does not exist\n" unless( -e $fl); push @cppfiles2, $fl unless ( $ignore_file{$fl} > 0 ); } } foreach $fl (@CXXFILES) { push @cxxfiles2, $fl unless ( $ignore_file{$fl} > 0 ); } if (@cppfiles2) { print MAKEFILE "CPPFILES= \\\n ", join(" \\\n ",@cppfiles2), "\n"; } if (@cxxfiles2) { print MAKEFILE "CXXFILES= \\\n ", join(" \\\n ",@cxxfiles2), "\n"; } if (@CFILES) { print MAKEFILE "CFILES= \\\n ", join(" \\\n ",@CFILES), "\n"; } if (@LIBFILES) { undef @LIBNAMES; print MAKEFILE "\nLIBFILES= \\\n"; unless( $pname=~/(tier0)|(mathlib)|(tier1)/i) { print MAKEFILE " $srcdir/lib/linux/tier1_486.a \\\n" } foreach $lib (@LIBFILES) { my @DLLNAMES=("tier0", "vstdlib", "steam_api"); unless ( $ignore_file{$lib} > 0 ) { $lib=lc($lib); my ($filename,$dir,$suffix) = fileparse($lib,qr/\.[^.]*/); my $dll=0; foreach $dllname (@DLLNAMES) { $dll=1 if ( $dllname eq $filename); } if ( $dll ) { $lib=~s@^(.*)\.lib@$1_i486.so@i; $lib=~s@/lib/.*/([^/]+)@/linux/$1@g; } else { $lib=~s/\.lib/_486.a/i; $lib=~s@/lib/(\S+)/@/lib/linux/@g; } push @LIBNAMES, $lib; } } foreach $lib (@LITERAL_LIBFILES) { unless ( $ignore_file{$lib} > 0 ) { $lib=~s/\\/\//g; $lib=~s@/linux/([a-zA-Z_0-9\.]+)$@/linux/$1@; $lib=~s@^.*/linux/([a-zA-Z_0-9]+)\.so$@$1.so@; push @LIBNAMES, $lib; } } # now, sort libs for link order foreach $lib ( sort bypriority @LIBNAMES ) { print MAKEFILE " $lib \\\n"; } print MAKEFILE "\n\n"; }
if ( $conf_type eq "dll" ) { print MAKEFILE "OUTPUT_SO_FILE=$srcdir/linux/$pname","_i486.so\n\n"; } elsif ( $conf_type eq "exe" ) { if ( $macros{'OUTBINNAME'} eq "" ) { die "Missing OUTBINNAME macro"; }
print MAKEFILE "OUTPUT_EXECUTABLE=$srcdir/linux/$macros{'OUTBINNAME'}\n\n"; }
print MAKEFILE "\n\n\# include base make file\ninclude $srcdir/devtools/makefile_base_linux.mak\n";
# now, output obj dependencies foreach $_ (@CPPFILES, @CFILES) { unless (( $ignore_file{$_} > 0 ) || ( length($_) < 2 ) ) { ($filename) = fileparse($_,qr/\.[^.]*/); print MAKEFILE getcwd,"/obj/$filename.o : $_\n\t\$(DO_CC)\n"; } } foreach $_ (@CXXFILES) { unless (( $ignore_file{$_} > 0 ) || ( length($_) < 2 ) ) { ($filename) = fileparse($_,qr/\.[^.]*/); print MAKEFILE getcwd,"/obj/$filename.oxx : $_\n\t\$(DO_CC)\n"; } }
close MAKEFILE; }
sub bypriority { # sort libs for gcc linkgoodness $priority{"mathlib"}="0005"; $priority{"tier1"}="0010"; $priority{"tier2"}="0020"; $priority{"tier3"}="0030";
my ($filenamea) = fileparse($a,qr/\.[^.]*/); my ($filenameb) = fileparse($b,qr/\.[^.]*/); $filenamea =~ s/_.86.*$//; # lose _i486 $filenameb =~ s/_.86.*$//; my $pa=$priority{$filenamea} || 1000; my $pb=$priority{$filenameb} || 1000; return $pb cmp $pa; }
sub ParseVPC { local($fname)=@_; &startreading($fname); while(&nextvpcline) { # print "$_\n"; if ( (/^\$linux/i) ) { &skipblock(0,\&handlelinuxline); } if ( (/^\$configuration/i) ) { &skipblock(0,\&handleconfigline); } elsif (/^\s*\$project/i) { &parseproject; } } }
sub massageline { # strip leading and trailing spaces and carriage returns and comments from vpc lines s/[\n\r]//g; s@//.*$@@g; s@^\s*@@g; s@\s*$@@g; }
sub submacros { # replace all macros within a line my $mac; foreach $mac (keys %macros) { s/\$$mac/$macros{$mac}/g; } }
sub startreading { # initialize recursive file reader my( $fname)=@_; $curfile=IO::File->new($fname) || die "can't open $fname"; }
sub nextvpcline { # get the next line from the file, handling line continuations, macro substitution, and $include # return 0 if out of lines my $ret=0; if ( $_ = <$curfile> ) { $ret=1; &massageline; while(s@\\$@ @) { my $old=$_; $_=<$curfile>; &massageline; $_=$old.$_; } s@\s+@ @g; my $old=$_; &submacros; # now, parse if (/\$macro (\S+) \"(\S+)\"$/i) { $macros{$1}=$2; return &nextvpcline; } s/\[\$WIN32\]//g; return &nextvpcline if (/\[\$X360\]/); if ( /^\s*[\$\#]include\s+\"(.*)\"/i) { # process $include my $incfile=$1; push @filestack, $curfile; $incfile=~s@\\@/@g; if ( $curfile=IO::File->new($incfile) ) { return &nextvpcline; } else { print STDERR "can't open include file $incfile, ignoring\n"; $curfile=pop(@filestack); return ""; }
} } else { $curfile->close; if (@filestack) { $curfile=pop(@filestack); return &nextvpcline; } else { return 0; } } return $ret; }
sub skipblock { # skip a named block in the key values, handling nested {} pairs my($empty_ok, $callback)=@_; my $lnstat=&nextvpcline; die "parse error eof in block" if ( (! $lnstat) && ( ! $empty_ok) ); my $nest=0; if (/^\{/) { $nest++; } else { die "no start block found, $_ found instead" unless($empty_ok); } while ($nest) { die "prematur eof" unless &nextvpcline; &$callback($_) if ( $callback ); $nest++ if (/^\{/); $nest-- if (/^\}/); } }
sub parseproject { # handle a project block, picking up files mentioned $pname=""; if (/^\s*\$project\s*(.*)$/i) { $pname=$1; $pname=~s@\"@@g; } local($_); my $nest=0; &nextvpcline || die "empty project?"; $nest++ if (/^\s*\{/); while($nest ) { &nextvpcline || die "premature eof in project?"; $nest++ if (/^\{/); $nest-- if (/^\}/); &CheckForFileLine($_); } }
sub CheckForFileLine { local($_)=@_; if (/^\s*\-\$File\s+(.*$)/i) { foreach $_ (split(/ /,$1)) { s/\"//g; $ignore_file{&process_path($_)} = 1; } } elsif (/^\s*\$File\s+(.*$)/i) { foreach $_ (split(/ /,$1)) { s/\"//g; &handlefile($_); } } }
sub handlefile { # given a project file (.cpp, etc), figure out what to do with it local($_)=@_;
# hardcoded exclusions for linux return if (/dx9sdk/i); return if (/_360/i); return if (/xbox_console.cpp/i); return if (/xbox_system.cpp/i); return if (/xbox_win32stubs.cpp/i); return if (/binkw32/i || /binkxenon/i );
if (/\.cpp$/) { push @CPPFILES,process_path($_); } if (/\.cxx$/) { push @CXXFILES,process_path($_); } elsif (/\.c$/) { push @CFILES,process_path($_); } elsif (/\.lib$/) { push @LIBFILES,process_path($_); } elsif (/\.a$/) { push @LITERAL_LIBFILES, process_path($_); } elsif (/\.so$/) { push @LITERAL_LIBFILES, process_path($_); } }
sub process_path { local($_)=@_; s@\\@/@g; if ( (! -e $_) && ( -e lc($_)) ) { # print STDERR "$_ does not exist try lc($_)\n"; $_=lc($_); } my $ap=abs_path($_); if ( (! length($ap) ) && length($_)) { # print "abs path of $_ is empty. bad dir?\n"; } $_=$ap; s@i686@i486@g; if ( (! -e $_) && ( -e lc($_)) ) { # print STDERR "$_ does not exist try lc($_)\n"; $_=lc($_); } # kill ..s for prettyness s@/[^/]+/\.\./@/@g; if (! -e $_) { # print STDERR "$_ does not exist\n"; } return $_; }
sub handlelinuxline { local($_)=@_; $buildforlinux = 1 if ( /^\s*\$buildforlinux.*1/i); $OptimizeLevel= $1 if (/^\s*\$OptimizerLevel\s+(\d+)/i); $buildforlinux = 1 if ( /^\s*\$buildforlinux.*1/i); $gccflags = $1 if (/^\s*\$ProjectSpecificGCCFLags\s+\"(\S+)\"/i); &CheckForFileLine($_); # allows linux-specific file includes and excludes &handleconfigline($_); # allow linux-specific #defines
}
sub CheckPreprocessorDefs { local($_)=@_; if (/^\s*\$PreprocessorDefinitions\s+\"(.*)\"/i) { foreach $_ (split(/[;,]/,$1) ) { unless( /\$/ || $define_seen{$_} || /fopen/i) { push(@DEFINES,$_); $define_seen{$_}=1; } } } } sub handleconfigline { # handle a line within a $Configuration block local($_)=@_; # the line if (/^\s*\$AdditionalIncludeDirectories\s+\"(.*)\"/i) { foreach $_ (split(/[;,]/,$1) ) { unless( /\$/ || $include_seen{$_} ) { push(@INCLUDEDIRS,process_path($_)); $include_seen{$_}=1; } } } if (/^\s*\$ConfigurationType\s*\"(.*)\"/) { undef $conf_type; $conf_type="lib" if ($1 =~ /Static Library/i); $conf_type="dll" if ($1 =~ /Dynamic Library/i); $conf_type="exe" if ($1 =~ /Application/i); print STDERR " unknown conf type $1\n" if (! length($conf_type) );
}
&CheckPreprocessorDefs($_); }
|