Newer
Older
#!/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.
#
# FIXME: no attempt made to handle !forceobj in the project files.
use warnings;
use FileHandle;
use File::Basename;
use Cwd;
use Digest::SHA qw(sha512_hex);
if ($#ARGV >= 0 and ($ARGV[0] eq "-u" or $ARGV[0] eq "-U")) {
# Convenience for Unix users: -u means that after we finish what
# we're doing here, we also run mkauto.sh and then 'configure' in
# the Unix subdirectory. So it's a one-stop shop for regenerating
# the actual end-product Unix makefile.
#
# Arguments supplied after -u go to configure.
#
# -U is identical, but runs 'configure' at the _top_ level, for
# people who habitually do that.
$do_unix = ($ARGV[0] eq "-U" ? 2 : 1);
shift @ARGV;
@confargs = @ARGV;
}
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, and licence.h is likewise generated by licence.pl. We
# need to generate those _now_, before attempting dependency analysis.
eval 'chdir "charset"; require "./sbcsgen.pl"; chdir ".."; select STDOUT;';
eval 'require "./licence.pl"; select STDOUT;';
$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/resources
@_ = split;
# If we're gathering help text, keep doing so.
if (defined $divert) {
if ((defined $_[0]) && $_[0] eq "!end") {
}
next;
}
# Skip comments and blank lines.
next if /^\s*#/ or scalar @_ == 0;
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 "!specialobj" and &mfval($_[1])) { $specialobj{$_[1]}->{$_[2]} = 1; next;}
if ($_[0] eq "!cflags" and &mfval($_[1])) {
($rest = $_) =~ s/^\s*\S+\s+\S+\s+\S+\s*//; # find rest of input line
if ($rest eq "") {
# Make sure this file doesn't get lumped together with any
# other file's cflags.
$rest = "F" . $_[2];
} else {
# Give this file a specific set of cflags, but permit it to
# go together with other files using the same set.
$rest = "C" . $rest;
}
$cflags{$_[1]}->{$_[2]} = $rest;
next;
}
if ($_[0] eq "!forceobj") { $forceobj{$_[1]} = 1; next; }
if ($_[0] eq "!begin") {
if ($_[1] =~ /^>(.*)/) {
$divert = \$auxfiles{$1};
} elsif (&mfval($_[1])) {
$sect = $_[2] ? $_[2] : "end";
$divert = \($makefile_extra{$_[1]}->{$sect});
} else {
$dummy = '';
$divert = \$dummy;
}
next;
}
# If we're gathering help/verbatim 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 && $_[1] eq "=") {
$groups{$_[0]} = [] if !defined $groups{$_[0]};
$listref = $groups{$_[0]};
$prog = undef;
shift @objs; # eat the group name
} elsif ($#_ >= 1 && $_[1] eq ":") {
$listref = [];
$prog = $_[0];
shift @objs; # eat the program name
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 =~ /^\[([A-Z]*)\]$/) and defined $prog) {
$type = substr($i,1,(length $i)-2);
die "unrecognised program type for $prog [$type]\n"
if ! grep { $type eq $_ } qw(G C X U MX XT UT);
} 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;
foreach $aux (sort keys %auxfiles) {
open AUX, ">$aux";
print AUX $auxfiles{$aux};
close AUX;
}
# 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.
@list = grep { $status = ($prev ne $_); $prev=$_; $status }
sort @{$programs{$i}};
$programs{$i} = [@list];
foreach $j (@list) {
# Dependencies for "x" start with "x.c" or "x.m" (depending on
# which one exists).
# 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 !~ /\./) {
$file = "$j.c";
$file = "$j.m" unless &findfile($file);
$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"';
# - a line ending `RT_MANIFEST "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 = ();
%allsourcefiles = (); # this is wanted by some makefiles
while (scalar @scanlist > 0) {
$file = shift @scanlist;
next if defined $further{$file}; # skip if we've already done it
$further{$file} = [];
$dirfile = &findfile($file);
$allsourcefiles{$dirfile} = 1;
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;
};
/(RT_MANIFEST|ICON)\s+\"([^\"]+)\"\s*$/ and do {
push @{$further{$file}}, $2;
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}}) {
$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","lcc","devcppproj","gtk","unix",
"am","osx","vstudio10","vstudio12","clangcl")) {
return 1;
}
warn "$.:unknown makefile type '$type'\n";
return 0;
}
# Utility routines while writing out the Makefiles.
sub def {
my ($x) = shift @_;
return (defined $x) ? $x : "";
}
sub dirpfx {
my ($path) = shift @_;
my ($sep) = shift @_;
while (($i = index $path, $sep) >= 0 ||
($j = index $path, "/") >= 0) {
if ($i >= 0 and ($j < 0 or $i < $j)) {
$path = substr $path, ($i + length $sep);
} else {
$path = substr $path, ($j + 1);
}
$ret .= "..$sep";
sub findfile {
my ($name) = @_;
my $i;
my $outdir = undef;
unless (defined $findfilecache{$name}) {
$i = 0;
if (-f "$dir$name") {
$outdir = $dir;
$i++;
$outdir =~ s/^\.\///;
}
}
die "multiple instances of source file $name\n" if $i > 1;
$findfilecache{$name} = (defined $outdir ? $outdir . $name : undef);
return $findfilecache{$name};
}
sub objects {
my ($prog, $otmpl, $rtmpl, $ltmpl, $prefix, $dirsep) = @_;
my @ret;
my ($i, $x, $y);
($otmpl, $rtmpl, $ltmpl) = map { defined $_ ? $_ : "" } ($otmpl, $rtmpl, $ltmpl);
@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/;
} elsif ($i !~ /\./) {
($x = $otmpl) =~ s/X/$i/;
}
push @ret, $x if $x ne "";
}
return join " ", @ret;
sub special {
my ($prog, $suffix) = @_;
my @ret;
my ($i, $x, $y);
($otmpl, $rtmpl, $ltmpl) = map { defined $_ ? $_ : "" } ($otmpl, $rtmpl, $ltmpl);
@ret = ();
foreach $i (@{$programs{$prog}}) {
if (substr($i, (length $i) - (length $suffix)) eq $suffix) {
push @ret, $i;
}
}
return (scalar @ret) ? (join " ", @ret) : undef;
}
sub splitline {
my ($line, $width, $splitchar) = @_;
$len = (defined $width ? $width : 76);
$splitchar = (defined $splitchar ? $splitchar : '\\');
while (length $line > $len) {
$line =~ /^(.{0,$len})\s(.*)$/ or $line =~ /^(.{$len,})?\s(.*)$/;
$result .= $1;
$result .= " ${splitchar}\n\t\t" if $2 ne '';
$line = $2;
$len = 60;
}
return $result . $line;
}
sub deps {
my ($otmpl, $rtmpl, $prefix, $dirsep, $mftyp, $depchar, $splitchar) = @_;
my ($i, $x, $y);
@ret = ();
$depchar ||= ':';
foreach $i (sort keys %depends) {
next if $specialobj{$mftyp}->{$i};
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, obj_orig => $i, 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 ();
}
$orig_dir = cwd;
# Now we're ready to output the actual Makefiles.
if (defined $makefiles{'clangcl'}) {
$dirpfx = &dirpfx($makefiles{'clangcl'}, "/");
##-- Makefile for cross-compiling using clang-cl, lld-link, and
## MinGW's windres for resource compilation.
#
# This makefile allows a complete Linux-based cross-compile, but
# using the real Visual Studio header files and libraries. In
# order to run it, you will need:
#
# - clang-cl, llvm-rc and lld-link on your PATH.
# * I built these from the up-to-date LLVM project trunk git
# repositories, as of 2018-05-29.
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
# - case-mashed copies of the Visual Studio include directories.
# * On a real VS installation, run vcvars32.bat and look at
# the resulting value of %INCLUDE%. Take a full copy of each
# of those directories, and inside the copy, for each
# include file that has an uppercase letter in its name,
# make a lowercased symlink to it. Additionally, one of the
# directories will contain files called driverspecs.h and
# specstrings.h, and those will need symlinks called
# DriverSpecs.h and SpecStrings.h.
# * Now, on Linux, define the environment variable INCLUDE to
# be a list, separated by *semicolons* (in the Windows
# style), of those directories, but before all of them you
# must also include lib/clang/5.0.0/include from the clang
# installation area (which contains in particular a
# clang-compatible stdarg.h overriding the Visual Studio
# one).
# - similarly case-mashed copies of the library directories.
# * Again, on a real VS installation, run vcvars32 or
# vcvarsx86_amd64 (as appropriate), look at %LIB%, make a
# copy of each directory, and provide symlinks within that
# directory so that all the files can be opened as
# lowercase.
# * Then set LIB to be a semicolon-separated list of those
# directories (but you'll need to change which set of
# directories depending on whether you want to do a 32-bit
# or 64-bit build).
# - for a 64-bit build, set 'Platform=x64' in the environment as
# well, or else on the make command line.
# * This is a variable understood only by this makefile - none
# of the tools we invoke will know it - but it's consistent
# with the way the VS scripts like vcvarsx86_amd64.bat set
# things up, and since the environment has to change
# _anyway_ between 32- and 64-bit builds (different set of
# paths in $LIB) it's reasonable to have the choice of
# compilation target driven by another environment variable
# set in parallel with that one.
# - for older versions of the VS libraries you may also have to
# set EXTRA_console and/or EXTRA_windows to the name of an
# object file manually extracted from one of those libraries.
# * This is because old VS seems to manage its startup code by
# having libcmt.lib contain lots of *crt0.obj objects, one
# for each possible user entry point (main, WinMain and the
# wide-char versions of both), of which the linker arranges
# to include the right one by special-case code. But lld
# only seems to mimic half of that code - it does include
# the right crt0 object, but it doesn't also deliberately
# _avoid_ including the _wrong_ ones, and since all those
# objects define a common set of global symbols for other
# parts of the library to use, lld may well select an
# arbitrary one of them the first time it sees a reference
# to one of those global symbols, and then later also select
# the _right_ one for the application's entry point, causing
# a multiple-definitions crash.
# * So the workaround is to explicitly include the right
# *crt0.obj file on the linker command line before lld even
# begins searching libraries. Hence, for a console
# application, you might extract crt0.obj from the library
# in question and set EXTRA_console=crt0.obj, and for a GUI
# application, do the same with wincrt0.obj. Then this
# makefile will include the right one of those objects
# alongside the matching /subsystem linker option.
# - also for older versions of the VS libraries, you may also
# have to set EXTRA_libs to include extra library files.
open OUT, ">$makefiles{'clangcl'}"; select OUT;
print
"# Makefile for cross-compiling $project_name using clang-cl, lld-link,\n".
"# and llvm-rc, using GNU make on Linux.\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";
print $help;
print
"\n".
"CCCMD = clang-cl\n".
"ifeq (\$(Platform),x64)\n".
"CCTARGET = x86_64-pc-windows-msvc18.0.0\n".
"PLATFORMCFLAGS =\n".
"else ifeq (\$(Platform),arm)\n".
"CCTARGET = arm-pc-windows-msvc18.0.0\n".
"PLATFORMCFLAGS = /D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE /GS-\n".
"else ifeq (\$(Platform),arm64)\n".
"CCTARGET = arm64-pc-windows-msvc18.0.0\n".
"PLATFORMCFLAGS = /D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE /GS-\n".
"else\n".
"CCTARGET = i386-pc-windows-msvc18.0.0\n".
"PLATFORMCFLAGS =\n".
"RC = \$(RCCMD) /c 1252 \n".
"RCPREPROC = \$(CCCMD) /P /TC\n".
"LD = lld-link\n".
"\n".
"# C compilation flags\n".
&splitline("CFLAGS = --target=\$(CCTARGET) /nologo /W3 /O1 -Wvla " .
(join " ", map {"-I$dirpfx$_"} @srcdirs) .
" /D_WINDOWS /D_WIN32_WINDOWS=0x500 /DWINVER=0x500 ".
"/D_CRT_SECURE_NO_WARNINGS /D_WINSOCK_DEPRECATED_NO_WARNINGS").
"LFLAGS = /incremental:no /dynamicbase /nxcompat\n".
&splitline("RCPPFLAGS = ".(join " ", map {"-I$dirpfx$_"} @srcdirs).
" -DWIN32 -D_WIN32 -DWINVER=0x0400")." \$(RCFL)\n".
"\n".
&def($makefile_extra{'clangcl'}->{'vars'}) .
"\n".
"\n";
print &splitline("all:" . join "", map { " \$(BUILDDIR)$_.exe" } &progrealnames("G:C"));
print "\n\n";
foreach $p (&prognames("G:C")) {
($prog, $type) = split ",", $p;
$objstr = &objects($p, "\$(BUILDDIR)X.obj", "\$(BUILDDIR)X.res", undef);
print &splitline("\$(BUILDDIR)$prog.exe: " . $objstr), "\n";
$objstr = &objects($p, "\$(BUILDDIR)X.obj", "\$(BUILDDIR)X.res", "X.lib");
$subsys = ($type eq "G") ? "windows" : "console";
print &splitline("\t\$(LD) \$(LFLAGS) \$(XLFLAGS) ".
"/out:\$(BUILDDIR)$prog.exe ".
"/lldmap:\$(BUILDDIR)$prog.map ".
"/subsystem:$subsys\$(SUBSYSVER) ".
"\$(EXTRA_$subsys) $objstr \$(EXTRA_libs)")."\n\n";
foreach $d (&deps("\$(BUILDDIR)X.obj", "\$(BUILDDIR)X.res", $dirpfx, "/", "vc")) {
$extradeps = $forceobj{$d->{obj_orig}} ? ["*.c","*.h","*.rc"] : [];
my $rule;
my @deps = @{$d->{deps}};
my @incdeps = grep { m!\.rc2?$! } @deps;
my @rcdeps = grep { ! m!\.rc2$! } @deps;
my $rc = $deps[0];
my $rcpp = $rc;
$rcpp =~ s!.*/!!;
$rcpp =~ s/\.rc$/.rcpp/;
$rcpp = "\$(BUILDDIR)" . $rcpp;
$rule = "\$(RC) ".$rcpp." /FO ".$d->{obj};
$rc_pp_rules .= &splitline(
sprintf("%s: %s", $rcpp, join " ", @incdeps)) ."\n" .
"\t\$(RCPREPROC) \$(RCPPFLAGS) /Fi\$\@ \$<\n\n";
$rule = "\$(CC) /Fo\$(BUILDDIR) \$(COMPAT) \$(CFLAGS) \$(XFLAGS) /c \$<";
print &splitline(sprintf("%s: %s", $d->{obj},
join " ", @$extradeps, @rcdeps)), "\n";
print "\t" . $rule . "\n\n";
print "\n" . $rc_pp_rules;
print &def($makefile_extra{'clangcl'}->{'end'});
print "\nclean:\n".
&splitline("\trm -f \$(BUILDDIR)*.obj \$(BUILDDIR)*.exe ".
"\$(BUILDDIR)*.rcpp \$(BUILDDIR)*.res \$(BUILDDIR)*.map ".
"\$(BUILDDIR)*.exe.manifest")."\n";
select STDOUT; close OUT;
}
if (defined $makefiles{'cygwin'}) {
##-- MinGW/CygWin makefile (called 'cygwin' for historical reasons)
open OUT, ">$makefiles{'cygwin'}"; select OUT;
print
"# Makefile for $project_name under MinGW, Cygwin, or Winelib.\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
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".
"# TOOLPATH = i686-w64-mingw32-\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 = -Wall -O2 -std=gnu99 -Wvla -D_WINDOWS".
" -DWIN32S_COMPAT -D_NO_OLDNAMES -D__USE_MINGW_ANSI_STDIO=1 " .
(join " ", map {"-I$dirpfx$_"} @srcdirs)) .
"\n".
&splitline("RCFLAGS = \$(RCINC) --define WIN32=1 --define _WIN32=1 ".
"--define WINVER=0x0400 ".(join " ", map {"-I$dirpfx$_"} @srcdirs))."\n".
&def($makefile_extra{'cygwin'}->{'vars'}) .
"\n".
".SUFFIXES:\n".
"\n";
print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("G:C"));
print "\n\n";
foreach $p (&prognames("G:C")) {
($prog, $type) = split ",", $p;
$objstr = &objects($p, "X.o", "X.res.o", undef);
print &splitline($prog . ".exe: " . $objstr), "\n";
my $mw = $type eq "G" ? " -mwindows" : "";
$libstr = &objects($p, undef, undef, "-lX");
print &splitline("\t\$(CC)" . $mw . " \$(LDFLAGS) -o \$@ " .
"-Wl,-Map,$prog.map " .
$objstr . " $libstr", 69), "\n\n";
foreach $d (&deps("X.o", "X.res.o", $dirpfx, "/", "cygwin")) {
if ($forceobj{$d->{obj_orig}}) {
printf ("%s: FORCE\n", $d->{obj});
} else {
print &splitline(sprintf("%s: %s", $d->{obj},
join " ", @{$d->{deps}})), "\n";
}
if ($d->{obj} =~ /\.res\.o$/) {
print "\t\$(RC) \$(RCFL) \$(RCFLAGS) ".$d->{deps}->[0]." -o ".$d->{obj}."\n\n";
} else {
print "\t\$(CC) \$(COMPAT) \$(CFLAGS) \$(XFLAGS) -c ".$d->{deps}->[0]."\n\n";
}
print "\n";
print &def($makefile_extra{'cygwin'}->{'end'});
print "\nclean:\n".
"\trm -f *.o *.exe *.res.o *.so *.map\n".
"\n".
"FORCE:\n";
select STDOUT; close OUT;
if (defined $makefiles{'vc'}) {
##-- Visual C++ makefile
open OUT, ">$makefiles{'vc'}"; select OUT;
print
"# Makefile for $project_name under Visual C.\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";
print $help;
print
"\n".
"# If you rename this file to `Makefile', you should change this line,\n".
"# so that the .rsp files still depend on the correct makefile.\n".
"MAKEFILE = Makefile.vc\n".
"\n".
"# C compilation flags\n".
"CFLAGS = /nologo /W3 /O1 " .
(join " ", map {"-I$dirpfx$_"} @srcdirs) .
" /D_WINDOWS /D_WIN32_WINDOWS=0x500 /DWINVER=0x500 /D_CRT_SECURE_NO_WARNINGS /D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE\n".
"LFLAGS = /incremental:no /dynamicbase /nxcompat\n".
"RCFLAGS = ".(join " ", map {"-I$dirpfx$_"} @srcdirs).
" -DWIN32 -D_WIN32 -DWINVER=0x0400\n".
&def($makefile_extra{'vc'}->{'vars'}) .
"\n".
"\n";
print &splitline("all:" . join "", map { " \$(BUILDDIR)$_.exe" } &progrealnames("G:C"));
print "\n\n";
foreach $p (&prognames("G:C")) {
($prog, $type) = split ",", $p;
$objstr = &objects($p, "\$(BUILDDIR)X.obj", "\$(BUILDDIR)X.res", undef);
print &splitline("\$(BUILDDIR)$prog.exe: " . $objstr), "\n";
$objstr = &objects($p, "\$(BUILDDIR)X.obj", "\$(BUILDDIR)X.res", "X.lib");
$subsys = ($type eq "G") ? "windows" : "console";
$inlinefilename = "link_$prog";
print "\ttype <<$inlinefilename\n";
@objlist = split " ", $objstr;
@objlines = ("");
foreach $i (@objlist) {
if (length($objlines[$#objlines] . " $i") > 72) {
push @objlines, "";
}
$objlines[$#objlines] .= " $i";
}
for ($i=0; $i<=$#objlines; $i++) {
print "$objlines[$i]\n";
}
print "<<\n";
print "\tlink \$(LFLAGS) \$(XLFLAGS) -out:\$(BUILDDIR)$prog.exe -map:\$(BUILDDIR)$prog.map -nologo -subsystem:$subsys\$(SUBSYSVER) \@$inlinefilename\n\n";
foreach $d (&deps("\$(BUILDDIR)X.obj", "\$(BUILDDIR)X.res", $dirpfx, "\\", "vc")) {
$extradeps = $forceobj{$d->{obj_orig}} ? ["*.c","*.h","*.rc"] : [];
print &splitline(sprintf("%s: %s", $d->{obj},
join " ", @$extradeps, @{$d->{deps}})), "\n";
if ($d->{obj} =~ /.res$/) {
print "\trc /Fo@{[$d->{obj}]} \$(RCFL) -r \$(RCFLAGS) ".$d->{deps}->[0],"\n\n";
}
}
print "\n";
foreach $real_srcdir ("", @srcdirs) {
$srcdir = $real_srcdir;
if ($srcdir ne "") {
$srcdir =~ s!/!\\!g;
$srcdir = $dirpfx . $srcdir;
$srcdir =~ s!\\\.\\!\\!;
}
# The double colon at the end of the line makes this a
# 'batch-mode inference rule', which means that nmake will
# aggregate multiple invocations of the rule and issue just
# one cl command with multiple source-file arguments. That
# noticeably speeds up builds, since starting up the cl
# process is a noticeable overhead and now has to be done far
# fewer times.
print "${srcdir}.c.obj::\n\tcl /Fo\$(BUILDDIR) \$(COMPAT) \$(CFLAGS) \$(XFLAGS) /c \$<\n\n";
}
print &def($makefile_extra{'vc'}->{'end'});
print "\nclean: tidy\n".
"\t-del \$(BUILDDIR)*.exe\n\n".
"tidy:\n".
"\t-del \$(BUILDDIR)*.obj\n".
"\t-del \$(BUILDDIR)*.res\n".
"\t-del \$(BUILDDIR)*.pch\n".
"\t-del \$(BUILDDIR)*.aps\n".
"\t-del \$(BUILDDIR)*.ilk\n".
"\t-del \$(BUILDDIR)*.pdb\n".
"\t-del \$(BUILDDIR)*.rsp\n".
"\t-del \$(BUILDDIR)*.dsp\n".
"\t-del \$(BUILDDIR)*.dsw\n".
"\t-del \$(BUILDDIR)*.ncb\n".
"\t-del \$(BUILDDIR)*.opt\n".
"\t-del \$(BUILDDIR)*.plg\n".
"\t-del \$(BUILDDIR)*.map\n".
"\t-del \$(BUILDDIR)*.idb\n".
"\t-del \$(BUILDDIR)debug.log\n";
select STDOUT; close OUT;
}
if (defined $makefiles{'vcproj'}) {
$dirpfx = &dirpfx($makefiles{'vcproj'}, "\\");
##-- MSVC 6 Workspace and projects
#
# Note: All files created in this section are written in binary
# mode, because although MSVC's command-line make can deal with
# LF-only line endings, MSVC project files really _need_ to be
# CRLF. Hence, in order for mkfiles.pl to generate usable project
# files even when run from Unix, I make sure all files are binary
# and explicitly write the CRLFs.
#
# Create directories if necessary
mkdir $makefiles{'vcproj'}
if(! -d $makefiles{'vcproj'});
chdir $makefiles{'vcproj'};
@deps = &deps("X.obj", "X.res", $dirpfx, "\\", "vcproj");
%all_object_deps = map {$_->{obj} => $_->{deps}} @deps;
# Create the project files
# Get names of all Windows projects (GUI and console)
my @prognames = &prognames("G:C");
foreach $progname (@prognames) {
create_vc_project(\%all_object_deps, $progname);
}
# Create the workspace file
open OUT, ">$project_name.dsw"; binmode OUT; select OUT;
print
"Microsoft Developer Studio Workspace File, Format Version 6.00\r\n".
"# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!\r\n".
"\r\n".
"###############################################################################\r\n".
"\r\n";
# List projects
foreach $progname (@prognames) {
($windows_project, $type) = split ",", $progname;
print "Project: \"$windows_project\"=\".\\$windows_project\\$windows_project.dsp\" - Package Owner=<4>\r\n";
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
}
print
"\r\n".
"Package=<5>\r\n".
"{{{\r\n".
"}}}\r\n".
"\r\n".
"Package=<4>\r\n".
"{{{\r\n".
"}}}\r\n".
"\r\n".
"###############################################################################\r\n".
"\r\n".
"Global:\r\n".
"\r\n".
"Package=<5>\r\n".
"{{{\r\n".
"}}}\r\n".
"\r\n".
"Package=<3>\r\n".
"{{{\r\n".
"}}}\r\n".
"\r\n".
"###############################################################################\r\n".
"\r\n";
select STDOUT; close OUT;
chdir $orig_dir;
sub create_vc_project {
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
my ($all_object_deps, $progname) = @_;
# Construct program's dependency info
%seen_objects = ();
%lib_files = ();
%source_files = ();
%header_files = ();
%resource_files = ();
@object_files = split " ", &objects($progname, "X.obj", "X.res", "X.lib");
foreach $object_file (@object_files) {
next if defined $seen_objects{$object_file};
$seen_objects{$object_file} = 1;
if($object_file =~ /\.lib$/io) {
$lib_files{$object_file} = 1;
next;
}
$object_deps = $all_object_deps{$object_file};
foreach $object_dep (@$object_deps) {
if($object_dep =~ /\.c$/io) {
$source_files{$object_dep} = 1;
next;
}
if($object_dep =~ /\.h$/io) {
$header_files{$object_dep} = 1;
next;
}
if($object_dep =~ /\.(rc|ico)$/io) {
$resource_files{$object_dep} = 1;
next;
}
}
}
$libs = join " ", sort keys %lib_files;
@source_files = sort keys %source_files;
@header_files = sort keys %header_files;
@resources = sort keys %resource_files;
($windows_project, $type) = split ",", $progname;
mkdir $windows_project
if(! -d $windows_project);
chdir $windows_project;
$subsys = ($type eq "G") ? "windows" : "console";
open OUT, ">$windows_project.dsp"; binmode OUT; select OUT;
print
"# Microsoft Developer Studio Project File - Name=\"$windows_project\" - Package Owner=<4>\r\n".
"# Microsoft Developer Studio Generated Build File, Format Version 6.00\r\n".
"# ** DO NOT EDIT **\r\n".
"\r\n".
"# TARGTYPE \"Win32 (x86) Application\" 0x0101\r\n".
"\r\n".
"CFG=$windows_project - Win32 Debug\r\n".
"!MESSAGE This is not a valid makefile. To build this project using NMAKE,\r\n".
"!MESSAGE use the Export Makefile command and run\r\n".
"!MESSAGE \r\n".
"!MESSAGE NMAKE /f \"$windows_project.mak\".\r\n".
"!MESSAGE \r\n".
"!MESSAGE You can specify a configuration when running NMAKE\r\n".
"!MESSAGE by defining the macro CFG on the command line. For example:\r\n".
"!MESSAGE \r\n".
"!MESSAGE NMAKE /f \"$windows_project.mak\" CFG=\"$windows_project - Win32 Debug\"\r\n".
"!MESSAGE \r\n".
"!MESSAGE Possible choices for configuration are:\r\n".
"!MESSAGE \r\n".
"!MESSAGE \"$windows_project - Win32 Release\" (based on \"Win32 (x86) Application\")\r\n".
"!MESSAGE \"$windows_project - Win32 Debug\" (based on \"Win32 (x86) Application\")\r\n".
"!MESSAGE \r\n".
"\r\n".
"# Begin Project\r\n".
"# PROP AllowPerConfigDependencies 0\r\n".
"# PROP Scc_ProjName \"\"\r\n".
"# PROP Scc_LocalPath \"\"\r\n".
"CPP=cl.exe\r\n".
"MTL=midl.exe\r\n".
"RSC=rc.exe\r\n".
"\r\n".
"!IF \"\$(CFG)\" == \"$windows_project - Win32 Release\"\r\n".
"\r\n".
"# PROP BASE Use_MFC 0\r\n".
"# PROP BASE Use_Debug_Libraries 0\r\n".
"# PROP BASE Output_Dir \"Release\"\r\n".
"# PROP BASE Intermediate_Dir \"Release\"\r\n".
"# PROP BASE Target_Dir \"\"\r\n".
"# PROP Use_MFC 0\r\n".
"# PROP Use_Debug_Libraries 0\r\n".
"# PROP Output_Dir \"Release\"\r\n".
"# PROP Intermediate_Dir \"Release\"\r\n".
"# PROP Ignore_Export_Lib 0\r\n".
"# PROP Target_Dir \"\"\r\n".
"# ADD BASE CPP /nologo /W3 /GX /O2 ".
(join " ", map {"/I \"..\\..\\$dirpfx$_\""} @srcdirs) .
" /D \"WIN32\" /D \"NDEBUG\" /D \"_WINDOWS\" /D \"_MBCS\" /YX /FD /c\r\n".
"# ADD CPP /nologo /W3 /GX /O2 ".
(join " ", map {"/I \"..\\..\\$dirpfx$_\""} @srcdirs) .
" /D \"WIN32\" /D \"NDEBUG\" /D \"_WINDOWS\" /D \"_MBCS\" /YX /FD /c\r\n".
"# ADD BASE MTL /nologo /D \"NDEBUG\" /mktyplib203 /win32\r\n".
"# ADD MTL /nologo /D \"NDEBUG\" /mktyplib203 /win32\r\n".
"# ADD BASE RSC /l 0x809 /d \"NDEBUG\"\r\n".
"# ADD RSC /l 0x809 /d \"NDEBUG\"\r\n".
"BSC32=bscmake.exe\r\n".
"# ADD BASE BSC32 /nologo\r\n".
"# ADD BSC32 /nologo\r\n".
"LINK32=link.exe\r\n".
"# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:$subsys /machine:I386\r\n".
"# ADD LINK32 $libs /nologo /subsystem:$subsys /machine:I386\r\n".
"# SUBTRACT LINK32 /pdb:none\r\n".
"\r\n".
"!ELSEIF \"\$(CFG)\" == \"$windows_project - Win32 Debug\"\r\n".
"\r\n".
"# PROP BASE Use_MFC 0\r\n".
"# PROP BASE Use_Debug_Libraries 1\r\n".
"# PROP BASE Output_Dir \"Debug\"\r\n".
"# PROP BASE Intermediate_Dir \"Debug\"\r\n".
"# PROP BASE Target_Dir \"\"\r\n".
"# PROP Use_MFC 0\r\n".
"# PROP Use_Debug_Libraries 1\r\n".
"# PROP Output_Dir \"Debug\"\r\n".
"# PROP Intermediate_Dir \"Debug\"\r\n".
"# PROP Ignore_Export_Lib 0\r\n".
"# PROP Target_Dir \"\"\r\n".
"# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od ".
(join " ", map {"/I \"..\\..\\$dirpfx$_\""} @srcdirs) .
" /D \"WIN32\" /D \"_DEBUG\" /D \"_WINDOWS\" /D \"_MBCS\" /YX /FD /GZ /c\r\n".
"# ADD CPP /nologo /W3 /Gm /GX /ZI /Od ".
(join " ", map {"/I \"..\\..\\$dirpfx$_\""} @srcdirs) .
" /D \"WIN32\" /D \"_DEBUG\" /D \"_WINDOWS\" /D \"_MBCS\" /YX /FD /GZ /c\r\n".
"# ADD BASE MTL /nologo /D \"_DEBUG\" /mktyplib203 /win32\r\n".
"# ADD MTL /nologo /D \"_DEBUG\" /mktyplib203 /win32\r\n".
"# ADD BASE RSC /l 0x809 /d \"_DEBUG\"\r\n".
"# ADD RSC /l 0x809 /d \"_DEBUG\"\r\n".
"BSC32=bscmake.exe\r\n".
"# ADD BASE BSC32 /nologo\r\n".
"# ADD BSC32 /nologo\r\n".
"LINK32=link.exe\r\n".
"# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:$subsys /debug /machine:I386 /pdbtype:sept\r\n".
"# ADD LINK32 $libs /nologo /subsystem:$subsys /debug /machine:I386 /pdbtype:sept\r\n".
"# SUBTRACT LINK32 /pdb:none\r\n".
"\r\n".
"!ENDIF \r\n".
"\r\n".
"# Begin Target\r\n".
"\r\n".
"# Name \"$windows_project - Win32 Release\"\r\n".
"# Name \"$windows_project - Win32 Debug\"\r\n".
"# Begin Group \"Source Files\"\r\n".
"\r\n".
"# PROP Default_Filter \"cpp;c;cxx;rc;def;r;odl;idl;hpj;bat\"\r\n";
foreach $source_file (@source_files) {
print
"# Begin Source File\r\n".
"\r\n".
"SOURCE=..\\..\\$source_file\r\n";
if($source_file =~ /ssh\.c/io) {
# Disable 'Edit and continue' as Visual Studio can't handle the macros
print
"\r\n".
"!IF \"\$(CFG)\" == \"$windows_project - Win32 Release\"\r\n".