📄 mkfiles.pl
字号:
#!/usr/bin/env perl## Cross-platform Makefile generator.## Reads the file `Recipe' to determine the list of generated# executables and their component objects. Then reads the source# files to compute #include dependencies. Finally, writes out the# various target Makefiles.# PuTTY specifics which could still do with removing:# - Mac makefile is not portabilised at all. Include directories# are hardwired, and also the libraries are fixed. This is# mainly because I was too scared to go anywhere near it.# - sbcsgen.pl is still run at startup.use FileHandle;use Cwd;open IN, "Recipe" or do { # We want to deal correctly with being run from one of the # subdirs in the source tree. So if we can't find Recipe here, # try one level up. chdir ".."; open IN, "Recipe" or die "unable to open Recipe file\n";};# HACK: One of the source files in `charset' is auto-generated by# sbcsgen.pl. We need to generate that _now_, before attempting# dependency analysis.eval 'chdir "charset"; require "sbcsgen.pl"; chdir ".."';@srcdirs = ("./");$divert = undef; # ref to scalar in which text is currently being put$help = ""; # list of newline-free lines of help text$project_name = "project"; # this is a good enough default%makefiles = (); # maps makefile types to output makefile pathnames%makefile_extra = (); # maps makefile types to extra Makefile text%programs = (); # maps prog name + type letter to listref of objects/resources%groups = (); # maps group name to listref of objects/resourceswhile (<IN>) { # Skip comments (unless the comments belong, for example because # they're part of a diversion). next if /^\s*#/ and !defined $divert; chomp; split; if ($_[0] eq "!begin" and $_[1] eq "help") { $divert = \$help; next; } if ($_[0] eq "!end") { $divert = undef; next; } if ($_[0] eq "!name") { $project_name = $_[1]; next; } if ($_[0] eq "!srcdir") { push @srcdirs, $_[1]; next; } if ($_[0] eq "!makefile" and &mfval($_[1])) { $makefiles{$_[1]}=$_[2]; next;} if ($_[0] eq "!begin") { if (&mfval($_[1])) { $divert = \$makefile_extra{$_[1]}; } else { $divert = \$dummy; } next; } # If we're gathering help text, keep doing so. if (defined $divert) { ${$divert} .= "$_\n"; next; } # Ignore blank lines. next if scalar @_ == 0; # Now we have an ordinary line. See if it's an = line, a : line # or a + line. @objs = @_; if ($_[0] eq "+") { $listref = $lastlistref; $prog = undef; die "$.: unexpected + line\n" if !defined $lastlistref; } elsif ($_[1] eq "=") { $groups{$_[0]} = [] if !defined $groups{$_[0]}; $listref = $groups{$_[0]}; $prog = undef; shift @objs; # eat the group name } elsif ($_[1] eq ":") { $listref = []; $prog = $_[0]; shift @objs; # eat the program name } else { die "$.: unrecognised line type\n"; } shift @objs; # eat the +, the = or the : while (scalar @objs > 0) { $i = shift @objs; if ($groups{$i}) { foreach $j (@{$groups{$i}}) { unshift @objs, $j; } } elsif (($i eq "[G]" or $i eq "[C]" or $i eq "[M]" or $i eq "[X]" or $i eq "[U]") and defined $prog) { $type = substr($i,1,1); } else { push @$listref, $i; } } if ($prog and $type) { die "multiple program entries for $prog [$type]\n" if defined $programs{$prog . "," . $type}; $programs{$prog . "," . $type} = $listref; } $lastlistref = $listref;}close IN;# Now retrieve the complete list of objects and resource files, and# construct dependency data for them. While we're here, expand the# object list for each program, and complain if its type isn't set.@prognames = sort keys %programs;%depends = ();@scanlist = ();foreach $i (@prognames) { ($prog, $type) = split ",", $i; # Strip duplicate object names. $prev = undef; @list = grep { $status = ($prev ne $_); $prev=$_; $status } sort @{$programs{$i}}; $programs{$i} = [@list]; foreach $j (@list) { # Dependencies for "x" start with "x.c". # Dependencies for "x.res" start with "x.rc". # Dependencies for "x.rsrc" start with "x.r". # Both types of file are pushed on the list of files to scan. # Libraries (.lib) don't have dependencies at all. if ($j =~ /^(.*)\.res$/) { $file = "$1.rc"; $depends{$j} = [$file]; push @scanlist, $file; } elsif ($j =~ /^(.*)\.rsrc$/) { $file = "$1.r"; $depends{$j} = [$file]; push @scanlist, $file; } elsif ($j =~ /\.lib$/) { # libraries don't have dependencies } else { $file = "$j.c"; $depends{$j} = [$file]; push @scanlist, $file; } }}# Scan each file on @scanlist and find further inclusions.# Inclusions are given by lines of the form `#include "otherfile"'# (system headers are automatically ignored by this because they'll# be given in angle brackets). Files included by this method are# added back on to @scanlist to be scanned in turn (if not already# done).## Resource scripts (.rc) can also include a file by means of a line# ending `ICON "filename"'. Files included by this method are not# added to @scanlist because they can never include further files.## In this pass we write out a hash %further which maps a source# file name into a listref containing further source file names.%further = ();while (scalar @scanlist > 0) { $file = shift @scanlist; next if defined $further{$file}; # skip if we've already done it $resource = ($file =~ /\.rc$/ ? 1 : 0); $further{$file} = []; $dirfile = &findfile($file); open IN, "$dirfile" or die "unable to open source file $file\n"; while (<IN>) { chomp; /^\s*#include\s+\"([^\"]+)\"/ and do { push @{$further{$file}}, $1; push @scanlist, $1; next; }; /ICON\s+\"([^\"]+)\"\s*$/ and do { push @{$further{$file}}, $1; next; } } close IN;}# Now we're ready to generate the final dependencies section. For# each key in %depends, we must expand the dependencies list by# iteratively adding entries from %further.foreach $i (keys %depends) { %dep = (); @scanlist = @{$depends{$i}}; foreach $i (@scanlist) { $dep{$i} = 1; } while (scalar @scanlist > 0) { $file = shift @scanlist; foreach $j (@{$further{$file}}) { if ($dep{$j} != 1) { $dep{$j} = 1; push @{$depends{$i}}, $j; push @scanlist, $j; } } }# printf "%s: %s\n", $i, join ' ',@{$depends{$i}};}# Validation of input.sub mfval($) { my ($type) = @_; # Returns true if the argument is a known makefile type. Otherwise, # prints a warning and returns false; if (grep { $type eq $_ } ("vc","vcproj","cygwin","borland","lcc","gtk","mpw")) { return 1; } warn "$.:unknown makefile type '$type'\n"; return 0;}# Utility routines while writing out the Makefiles.sub dirpfx { my ($path) = shift @_; my ($sep) = shift @_; my $ret = "", $i; while (($i = index $path, $sep) >= 0) { $path = substr $path, ($i + length $sep); $ret .= "..$sep"; } return $ret;}sub findfile { my ($name) = @_; my $dir, $i, $outdir = ""; unless (defined $findfilecache{$name}) { $i = 0; foreach $dir (@srcdirs) { $outdir = $dir, $i++ if -f "$dir$name"; } die "multiple instances of source file $name\n" if $i > 1; $findfilecache{$name} = $outdir . $name; } return $findfilecache{$name};}sub objects { my ($prog, $otmpl, $rtmpl, $ltmpl, $prefix, $dirsep) = @_; my @ret; my ($i, $x, $y); @ret = (); foreach $i (@{$programs{$prog}}) { $x = ""; if ($i =~ /^(.*)\.(res|rsrc)/) { $y = $1; ($x = $rtmpl) =~ s/X/$y/; } elsif ($i =~ /^(.*)\.lib/) { $y = $1; ($x = $ltmpl) =~ s/X/$y/; } else { ($x = $otmpl) =~ s/X/$i/; } push @ret, $x if $x ne ""; } return join " ", @ret;}sub splitline { my ($line, $width, $splitchar) = @_; my ($result, $len); $len = (defined $width ? $width : 76); $splitchar = (defined $splitchar ? $splitchar : '\\'); while (length $line > $len) { $line =~ /^(.{0,$len})\s(.*)$/ or $line =~ /^(.{$len,}?\s(.*)$/; $result .= $1 . " ${splitchar}\n\t\t"; $line = $2; $len = 60; } return $result . $line;}sub deps { my ($otmpl, $rtmpl, $prefix, $dirsep, $depchar, $splitchar) = @_; my ($i, $x, $y); my @deps, @ret; @ret = (); $depchar ||= ':'; foreach $i (sort keys %depends) { if ($i =~ /^(.*)\.(res|rsrc)/) { next if !defined $rtmpl; $y = $1; ($x = $rtmpl) =~ s/X/$y/; } else { ($x = $otmpl) =~ s/X/$i/; } @deps = @{$depends{$i}}; @deps = map { $_ = &findfile($_); s/\//$dirsep/g; $_ = $prefix . $_; } @deps; push @ret, {obj => $x, deps => [@deps]}; } return @ret;}sub prognames { my ($types) = @_; my ($n, $prog, $type); my @ret; @ret = (); foreach $n (@prognames) { ($prog, $type) = split ",", $n; push @ret, $n if index($types, $type) >= 0; } return @ret;}sub progrealnames { my ($types) = @_; my ($n, $prog, $type); my @ret; @ret = (); foreach $n (@prognames) { ($prog, $type) = split ",", $n; push @ret, $prog if index($types, $type) >= 0; } return @ret;}sub manpages { my ($types,$suffix) = @_; # assume that all UNIX programs have a man page if($suffix eq "1" && $types =~ /X/) { return map("$_.1", &progrealnames($types)); } return ();}# Now we're ready to output the actual Makefiles.if (defined $makefiles{'cygwin'}) { $dirpfx = &dirpfx($makefiles{'cygwin'}, "/"); ##-- CygWin makefile open OUT, ">$makefiles{'cygwin'}"; select OUT; print "# Makefile for $project_name under cygwin.\n". "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n". "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n"; # gcc command line option is -D not /D ($_ = $help) =~ s/=\/D/=-D/gs; print $_; print "\n". "# You can define this path to point at your tools if you need to\n". "# TOOLPATH = c:\\cygwin\\bin\\ # or similar, if you're running Windows\n". "# TOOLPATH = /pkg/mingw32msvc/i386-mingw32msvc/bin/\n". "CC = \$(TOOLPATH)gcc\n". "RC = \$(TOOLPATH)windres\n". "# Uncomment the following two lines to compile under Winelib\n". "# CC = winegcc\n". "# RC = wrc\n". "# You may also need to tell windres where to find include files:\n". "# RCINC = --include-dir c:\\cygwin\\include\\\n". "\n". &splitline("CFLAGS = -mno-cygwin -Wall -O2 -D_WINDOWS -DDEBUG -DWIN32S_COMPAT". " -D_NO_OLDNAMES -DNO_MULTIMON " .
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -