#!@PERL@
use Cwd;
# Authors: William Lott, Nick Kramer
# rcs-header: $Header: /scm/cvs/src/tools/shared-misc/gen-makefile.in,v 1.52.4.1 2004/10/13 05:57:42 housel Exp $
# Usage: gen-makefile [-pplatforms.descr] directory
#
# gen-makefile turns a single Makegen file into a Makefile.
# gen-makefile does the bulk of the work for mk-build-tree, which
# calls gen-makefile once on each Makegen in the tree. The main
# reason we split gen-makefile from mk-build-tree is that gen-makefile
# uses eval, so we need a fresh environment for each Makegen file to
# keep them from accidentally pissing on each other.
#
# All Defaults files must define the following variables: $target_name
# and $srcroot. Many Defaults files also define $destdir and $D2C.
# (See below for a full description of all available variables)
#
# gen-makefile provides a variety of functions which Makegen files can
# call. These functions include:
#
# compile_subdirs(@subdirs)
# emit_library_rule($lidfile, $extradeps, $extraflags, @keywords)
# emit_c_file_rule($base_file_name, @header_files)
# emit_cc_file_rule($base_file_name, @header_files)
# emit_melange_rule($base_file_name, @dependencies)
# emit_parsergen_rule($base_file_name)
# install($dest_subdir, @files)
# install_from_src($dest_subdir, @files)
# install_executable_from_src($dest_subdir, @files)
# emit_mindycomp_rule($dylan_source)
# emit_dbc_link_rule($libname, $dbc_files)
# emit_mindycomp_library_rule($libname, $source_files)
# makegen_include($include_file)
# unknown_platform_error()
# convert_path_separator($path)
#
# Complete documentation on these functions can be found at the end of
# this file. Note that many functions can take a bunch of file names.
# Some do this by accepting a single string with the filenames
# separated by spaces; others accept a variable number of arguments
# with one argument per filename. The former style uses a variable
# beginning with a dollar sign ($), the latter style uses variables
# beginning with an at (@) sign. (Ideally there'd be some
# consistency, but that's asking an awful lot...)
#
# ### How hard could it be to use only varargs? It looks like only
# the mindycomp ones violate this convention...
#
# gen-makefile also defines a number of global variables. Most of the
# variables of interest to Defaults and Makegen writers can be found
# in set_default_defaults(). Some other ones of interest:
#
# $target_name
# The name of the target platform, which is used to calculate
# %target_platform.
# $host_name
# The name of the host platform, which is used to calculate
# %host_platform. This variable defaults to $target_name.
# %target_platform
# The platforms.descr entry for the target platform. Keywords
# are lowercase and use underbars instead of dashes. This
# variable is automatically derived from $target_name.
# %target_features
# The features this target platform defines. Usage:
# $target_features{'some_feature'} is 1 if defined, 0
# otherwise. (Note that the feature names are lowercase and
# use underbars instead of dashes) This variable is
# automatically derived from %target_platform.
# %host_platform
# The platforms.descr entry for the host platform. Keywords
# are lowercase and use underbars instead of dashes. This
# variable is automatically derived from $host_name.
# $srcroot
# The root of the source tree. Must be an absolute path.
# $buildroot
# The root of the build tree. Must be an absolute path, and
# it must be equivalent to the directory mk-build-tree was run
# from. The purpose of this variable is to allow the user to
# choose an alternate wording of the build root -- ie,
# "/afs/cs/whatever" instead of "/afs/cs.cmu.edu/whatever".
# $subdir
# The source dir for this Makegen file is located at
# "$srcroot/$subdir", and the current build dir is located at
# "$buildroot/$subdir".
# $destdir
# The root directory that files will be copied to when "make
# install" is run. a.k.a. the "exec prefix"
# $bindir
# $libdir
# $includedir
# $sysconfdir
# Directories in which we install dylan executables, libraries,
# include files and platforms.descr.
# Defaults to $destdir/bin, $destdir/lib/dylan, $destdir/include,
# $destdir/etc.
# $gen_makefile
# Where to run gen-makefile from for calls from makefiles.
# $shared
# True if shared libraries will be built.
# $static
# True if static libraries will be built.
#
# In addition, the d2c Makegens use the following variables:
#
# $enable_d2c
# $enable_mindy
# $enable_mindy_bootstrap
# Top level code
#
# gen-makefile uses the following global variables internally:
#
# %all_platforms
# Keys are names of platforms, value is 1 if we know of the
# platform, 0 otherwise.
# LIDFILE
# A file handle that has special meaning in some places.
# $orig_buildroot
# Used to make sure $buildroot isn't changed by a Makegen.
# $defaults
# Full path name of the Defaults file.
# $makefile
# The name of the makefile we're currently generating.
# $dot_obj
# Shorthand for $target_platform{'object-filename-suffix'}.
# $dot_exe
# Shorthand for $target_platform{'executable-filename-suffix'}
# $dot_lib
# Shorthand for $target_platform{'library-filename-suffix'}
# $lib_prefix
# Shorthand for $target_platform{'library-filename-prefix'}
# $lidfile_hit_eof
# A global that gets around the lack of multiple return values.
# $platforms_dot_descr
# Full path name of "platforms.descr"
#
# gen-makefile also eval's into existence the following sets of variables:
#
# %platform_platformName
# where "platformName" is the name of a platforms.descr entry.
# @makefileTarget_dependencies
# where "makefileTarget" is the name of a Makefile target
# @makefileTarget_commands
# where "makefileTarget" is the name of a Makefile target
#
# gen-makefile supports the following top-level makefile targets:
# compile (default), install, clean, and cc_files (compiles C code
# generated by d2c).
$usage_message = "Usage: $0 [-pPlatforms.descr] [directory]\n";
@ARGV <= 2 || die $usage_message;
if ($ARGV[0] =~ /-p(.*)/) {
$platforms_dot_descr = $1;
shift(@ARGV);
} else {
$platforms_dot_descr = ($ENV{'DYLANDIR'} || '@prefix@') .
'/share/dylan/platforms.descr';
}
if (@ARGV == 0) {
push(@ARGV, '.');
}
@ARGV == 1 || die $usage_message;
chdir($ARGV[0]) || die "Can't cd to $ARGV[0]: $!\n";
$lidfile_hit_eof = 0;
$lidfile_line = 1;
do set_default_defaults();
do find_Defaults_file();
do read_Defaults_file();
do do_Makegen_file();
exit(0);
# Core functionality of gen-makefile
# set_default_defaults() -- internal
#
# Sets up the default values we use if something isn't specified in
# the Defaults file. There is no return value, only a lot of side
# effects to global variables.
#
sub set_default_defaults {
# Set the default defaults.
$DYLANDIR = 0;
$D2C = '$(DYLANDIR)/bin/d2c';
$D2CFLAGS = '-L$(DYLANDIR)/lib/dylan';
$CFLAGS = '-g';
$CPPFLAGS = '-I$(DYLANDIR)/include';
$MELANGE = 'melange' . $dot_exe;
$PARSERGEN = 'parsergen' . $dot_exe;
$MC = 'mindycomp' . $dot_exe;
# There is no MINDYFLAGS, because that depends on the library name.
$MINDY = 'mindy' . $dot_exe;
$gen_makefile = 'gen-makefile';
$install_data = "cp";
$install_program = "install";
$install_library = "install";
}
# find_Defaults_file() -- internal
#
# Finds the Defaults file and sets $defaults, $buildroot, and
# $orig_buildroot appropriately.
#
sub find_Defaults_file {
local($cwd) = cwd(); # cwd is more portable than `pwd`;
chomp($cwd); # chomp is safer than chop
$buildroot = $cwd;
$subdir = '';
# Change //d/path into d:/path on NT. Only a problem on drives
# other than C:. (We can't conditionalize this because we don't
# yet know which platform we're running on.)
$buildroot =~ s|^//(\w)/|\1:/|;
until (-e ($defaults = $buildroot . '/Defaults')) {
($buildroot =~ /^(.*)\/([^\/]+)$/)
|| die("Can't find Defaults and hence can't tell where "
. "the root is.\n");
$subdir = $2 . '/' . $subdir;
$buildroot = $1;
}
# Save the buildroot.
$orig_buildroot = $buildroot;
}
# read_Defaults_file() -- internal
#
# Reads in the Defaults file which find_Defaults_file found, and sets
# up a variety of global variables that depend on things in the
# Defaults file.
#
sub read_Defaults_file {
# Slurp in the defaults.
do $defaults;
die("Problem loading $defaults:\n $@\n") if $@;
&read_platforms_dot_descr();
$target_name || die("Must set variable \$target_name in Defaults file\n");
$host_name = $host_name || $target_name; # $host defaults to $target
%target_platform = &get_platform($target_name);
%host_platform = &get_platform($host_name);
# Extract features from target platform
local($feature_name);
for $feature_name (split(/\s+/, $target_platform{'default_features'})) {
$feature_name =~ tr/-A-Z/_a-z/;
$features{$feature_name} = 1;
}
$target_platform{'platform_name'} =~ /\-(\w+)\-/;
$debug_platform_name = $1;
$debug_platform_name =~ tr/a-z/A-Z/;
$debug_platform_name = "-DGD_PLATFORM_$debug_platform_name";
# Create some useful shorthands (internal use only)
$makefile = $host_platform{'makefile_name'};
$dot_obj = $target_platform{'object_filename_suffix'};
$dot_exe = $target_platform{'executable_filename_suffix'};
# There can be multiple library suffixes, and we only need the
# preferred one.
($dot_lib) = split(/\s+/, $target_platform{'library_filename_suffix'});
$lib_prefix = $target_platform{'library_filename_prefix'};
unless($target_platform{'link_shared_library_command'} ne '') {
$shared = 0;
}
if ($shared) {
$dot_obj = $target_platform{'shared_object_filename_suffix'};
($dot_lib) = split(/\s+/,
$target_platform{'shared_library_filename_suffix'});
}
# Check to see if buildroot changed to something else.
unless ($buildroot eq $orig_buildroot) {
### On win32, this code doesn't appear to work correctly in
### that it *always* thinks the two are equivalent. Oh well,
### no harm done...
local($root_inode) = (stat($buildroot))[1];
local($orig_inode) = (stat($orig_buildroot))[1];
unless ($root_inode == $orig_inode) {
die("Defaults changed \$buildroot to:\n $buildroot\n"
. "but that is a different directory than:\n"
. " $orig_buildroot\n");
}
&assert_path_is_absolute($buildroot, "\$buildroot");
$defaults = $buildroot . '/Defaults';
}
# Make sure they set srcroot. And set it to an absolute pathname.
$srcroot || die('$srcroot not set in Defaults.\n');
&assert_path_is_absolute($srcroot, "\$srcroot");
# Tack the subdir onto srcroot. Note: subdir is empty or ends
# with a /, hence the chop.
chop($srcdir = $srcroot.'/'.$subdir);
# Likewise for builddir
chop($builddir = $buildroot.'/'.$subdir);
if ($destdir) {
unless ($libdir) {
$libdir = $destdir.'/lib/dylan';
}
unless ($bindir) {
$bindir = $destdir.'/bin';
}
unless ($includedir) {
$includedir = $destdir.'/include';
}
unless ($sysconfdir) {
$sysconfdir = $destdir.'/etc';
}
}
$srcroot = &convert_path_separator($srcroot);
$srcdir = &convert_path_separator($srcdir);
$buildroot = &convert_path_separator($buildroot);
$builddir = &convert_path_separator($builddir);
$destdir = &convert_path_separator($destdir);
$libdir = &convert_path_separator($libdir);
$bindir = &convert_path_separator($bindir);
$includedir = &convert_path_separator($includedir);
$sysconfdir = &convert_path_separator($sysconfdir);
$MC = &convert_path_separator($MC);
$MINDY = &convert_path_separator($MINDY);
$D2C = &convert_path_separator($D2C);
$MELANGE = &convert_path_separator($MELANGE);
$PARSERGEN = &convert_path_separator($PARSERGEN);
}
# do_Makegen_file() -- internal
#
# Reads in the Makegen file and creates an appropriate Makefile.
#
sub do_Makegen_file {
# Find the Makegen file.
local($makegen) = $srcdir . '/Makegen';
(-e $makegen) || die("No $makegen\n");
# Start the makefile.
open(MAKEFILE, ">,$makefile") || die("Can't open ,$makefile: $!\n");
select(MAKEFILE);
print "### This makefile is machine generated. Don't expect any edits to survive.\n";
print "### Generated for target $target_name hosted by $host_name\n\n";
print "SRCROOT=$srcroot\n";
print "SRCDIR=$srcdir\n";
print 'VPATH=$(SRCDIR)' . "\n";
print "BUILDROOT=$buildroot\n";
print "top_builddir=$buildroot\n";
print "BUILDDIR=$builddir\n";
print "DESTDIR=\n"; # GNU DESTDIR, not the $destdir variable.
if ($host_platform{'make_supports_phony_targets?'}) {
print "\n.PHONY: default compile install uninstall clean\n\n";
}
print "default: compile\n\n";
# Slurp in the generator.
do $makegen;
die("Problem loading $makegen:\n $@\n") if $@;
if (@files_to_clean) {
push(@clean_commands, "-rm -f @files_to_clean");
if ($shared) {
push(@clean_commands, "-rm -rf .libs");
}
}
push(@GNUmakefile_dependencies, $gen_makefile,
$defaults, $makegen);
push(@GNUmakefile_commands,
"$gen_makefile -p$platforms_dot_descr .");
&emit_rule('compile');
$destdir && do emit_rule ('install');
$destdir && do emit_rule ('uninstall');
&emit_rule('clean');
if ($host_platform{'makefiles_can_rebuild_themselves?'}) {
print $makefile, ': ', join(' ', eval('@GNUmakefile_dependencies'));
print "\n";
foreach $rule (eval('@GNUmakefile_commands')) {
print "\t", $rule, "\n";
}
print "\n";
}
&emit_rule('cc_files');
if ($enable_mindy) {
&emit_rule('dbc_only');
}
close(MAKEFILE);
rename(",$makefile", $makefile);
}
# Internal utility functions
# assert_path_is_absolute($path, $variable) -- internal
#
# Decides if the path is an absolute path; signals an error if it
# isn't. On a win32 system, an absolute path must have a drive
# letter. Make sure you've set %host_platform before you call this
# function. $variable is used only in error messages.
#
sub assert_path_is_absolute {
local($path, $variable) = @_;
local($win32_absolute) = ($path =~ /^w:\//);
local($unix_absolute) = ($path =~ /^\//);
local($ok, $explanation);
# We do a lot of extra work here so we can provide better error
# messages.
if ($win32_absolute && $host_platform{'uses_drive_letters?'}) {
$ok = 1;
} elsif ($unix_absolute && !$host_platform{'uses_drive_letters?'}) {
$ok = 1;
} elsif ($unix_absolute && $host_platform{'uses_drive_letters?'}) {
$ok = 0;
$explanation = "(You must include a drive letter)";
} elsif ($win32_absolute && !$host_platform{'uses_drive_letters?'}) {
$ok = 0;
$explanation = "(You included a drive letter on a Unix machine?!?)";
} else {
# path is not absolute by anyone's definition, which doesn't
# merit further explanation.
$ok = 0;
$explanation = "";
}
$ok = 1;
if (! $ok) {
if ($explanation) {
die "$variable is not absolute:\n $path\n$explanation\n";
} else {
die "$variable is not absolute:\n $path\n";
}
}
}
# emit_rule($target) -- internal
#
# Emits a rule, looking at the global state to decide what the
# dependencies are and how to build the target.
#
sub emit_rule {
local($rule) = @_;
print $rule, ': ', join(' ', eval('@'.$rule.'_dependencies')), "\n";
foreach $rule (eval('@'.$rule.'_commands')) {
print "\t", $rule, "\n";
}
print "\n";
}
# compile_subdir($subdir) -- internal
#
# Generates the makefile rule to visit a single subdirectory.
#
sub compile_subdir {
local($subdir) = &convert_path_separator(@_);
push(@compile_commands,
sprintf($host_platform{'recursive_make_command'},
$subdir, ""));
push(@install_commands,
sprintf($host_platform{'recursive_make_command'},
$subdir, "install"));
unshift(@uninstall_commands,
sprintf($host_platform{'recursive_make_command'},
$subdir, "uninstall"));
push(@clean_commands,
sprintf($host_platform{'recursive_make_command'},
$subdir, "clean"));
push(@cc_files_commands,
sprintf($host_platform{'recursive_make_command'},
$subdir, "cc_files"));
push(@dbc_only_commands,
sprintf($host_platform{'recursive_make_command'},
$subdir, "dbc_only"));
}
# maybe_emit_d2c_defines() -- internal
#
# If they haven't already been emitted, emit the Makefile header,
# which defines various Makefile variables like $(D2C).
#
sub maybe_emit_d2c_defines {
unless ($d2c_defines) {
if ($CC) {
print("CC=$CC\n");
}
if($enable_debug) {
$CFLAGS = $target_platform{'default_c_compiler_debug_flags'};
} elsif ($enable_profiling) {
$CFLAGS = $target_platform{'default_c_compiler_profile_flags'};
} else {
$CFLAGS = $target_platform{'default_c_compiler_flags'};
}
$CFLAGS =~ s/-I%s//;
print <<EOF;
D2C = $D2C
D2CFLAGS = $D2CFLAGS
CFLAGS = $CFLAGS
CPPFLAGS = $CPPFLAGS $debug_platform_name
COPTS = $COPTS
MELANGE = $MELANGE
PARSERGEN = $PARSERGEN
MC = $MC
MINDY = $MINDY
EOF
if ($host_platform{'environment_variables_can_be_exported?'}) {
local($EXPORT = "export");
#if ($target_name eq 'x86-win32-vc') {
# $EXPORT="set";
#}
print "$EXPORT CCFLAGS = \$(CFLAGS) \$(CPPFLAGS) \$(COPTS)\n\n";
if ($LIBTOOL) {
print "$EXPORT LIBTOOL = $LIBTOOL\n\n";
}
if ($GC_LIBS) {
print "$EXPORT GC_LIBS = $GC_LIBS\n\n";
}
if ($DYLANPATH) {
if ($enable_mindy_bootstrap) {
print "$EXPORT DYLANPATH = $DYLANPATH\n\n";
} else {
print "$EXPORT DYLANPATH =\n\n";
}
};
if ($DYLANDIR) {
print "$EXPORT DYLANDIR = $DYLANDIR\n\n";
}
} else {
print "LIBTOOL = $LIBTOOL\n\n";
}
$d2c_defines = 1;
}
}
# parse_dylan_header() -- internal
#
# Reads the Dylan header (keyword: value, ...) from the file LIDFILE,
# which is already open. (The file doesn't have to be a lid file, it
# could be something else like a platforms.descr) Returns an
# associative array where the keys are header keywords (mashed to
# lower case), and the values are the header values.
#
# In contrast to the Dylan version, keywords can not appear more than
# once. If they do, the last value will be used. Multi-line values
# are supported, though.
#
# We also set the global $lidfile_hit_eof, so you can know why the
# header ended. This is useful for reading in platform.descr files,
# where you call parse_dylan_header repeatedly.
#
sub parse_dylan_header {
local(%contents);
local($last_keyword); # for multi-line values
while (<LIDFILE>) {
$lidfile_line = $lidfile_line + 1;
# remember, in Perl "." is any character other than newline.
# Also, Perl handles all newline hassles by turning win32
# style CRLFs into LFs (\n's) before it shows us the string.
if ( ($target_name =~ /x86-cygnus-gcc*/) || ($target_name eq 'x86-win32-vc') || ($target_name =~ /x86-mingw32-gcc*/)) {
$_ =~ s/\r//g; # Get rid of bogus carriage returns
}
if (/^\s*$/) { # if blank line, break out of loop
$lidfile_hit_eof = 0;
return %contents;
} elsif (m|^//.*$|) {
# comment line, ignore
} elsif (/^\s+(.*)$/) {
# Continuation line -- part of a multi-line value
$contents{$last_keyword} .= ' ' . $1;
} else {
/^([-A-Za-z0-9_!&*<>|^\$\%\@\?]+):\s+(.*)\s+$/
|| die "Bad keyword line in $lid_file, line $lidfile_line\n";
local($keyword) = $1;
local($value) = $2;
if ($value eq '#f' | $value eq '#F') {
$value = 0;
} elsif ($value eq '#t' | $value eq '#T') {
$value = 1;
}
$keyword =~ tr/-A-Z/_a-z/;
$contents{$keyword} = $value;
$last_keyword = $keyword;
}
}
$lidfile_hit_eof = 1;
return %contents;
}
# parse_lid_file($filename) -- internal
#
# Reads in the LID file, and returns an associative array where the
# keys are header keywords (mashed to lower case), and the values are
# the header values. As a magic special case, the keyword 'files'
# contains all the files in the body of the lid file.
#
sub parse_lid_file {
local($lid_file) = @_;
local(%contents);
local ($dot_static) = $target_platform{'object_filename_suffix'};
open(LIDFILE, $srcdir.'/'.$lid_file.'.lid')
|| die("Can't open $lid_file.lid: $!\n");
%contents = &parse_dylan_header();
# Read the filenames
# a .o in the Lid file is a hack to get a foreign .o put in the archive.
# Just ignore em, since there should be a seperate C rule to clean, etc.
while (<LIDFILE>) {
$lidfile_line = $lidfile_line + 1;
if ( ($target_name =~ /x86-cygnus-gcc*/) || ($target_name eq 'x86-win32-vc') || ($target_name =~ /x86-mingw32-gcc*/) ) {
$_ =~ s/\r//g; # Get rid of bogus carriage returns
}
if ($_ !~ m/.*$dot_obj$/ && $_ !~ m/.*$dot_static$/) {
chop; # kill newline
$contents{'files'} .= " $_";
}
}
# replace multiple spaces with single spaces
$contents{'files'} =~ s/\s+/ /g;
# strip leading whitespace, which tends to screw up other parts of
# gen-makefile
$contents{'files'} =~ s/^\s+//;
close(LIDFILE);
return %contents;
}
# read_platforms_dot_descr() -- internal
#
# Read the platforms.descr file (used to be called targets.descr).
# Each platform description will get it's own associative array named
# %platform_platformName. (For example, %platform_x86_win32) See
# parse_dylan_header for a description of the arrays.
#
# In addition to the per-platform arrays, there is a global
# associative array %all_platforms which is used to list all known
# platforms. The key is the name of a platform; the value is 1 if it
# is a known platform, 0 otherwise.
#
sub read_platforms_dot_descr {
### Should have better way of specifying location of .descr
open(LIDFILE, $platforms_dot_descr)
|| die("Can't open $platforms_dot_descr: $!\n");
while (! $lidfile_hit_eof) {
local(%platform) = &parse_dylan_header();
local(@keywords) = keys(%platform);
if ($#keywords > 0) {
# if a real header, not a blank line or comment block
local($platform_name) = $platform{'platform_name'};
$platform_name =~ tr/-A-Z/_a-z/; # mash to lowercase; - to _
if ($platform{'inherit_from'}) {
local($key);
foreach $parent (split(/\s+/, $platform{'inherit_from'})) {
local(%parent_plat) = &get_platform($parent);
local($key);
foreach $key (keys(%parent_plat)) {
if (!defined $platform{$key}) {
$platform{$key} = $parent_plat{$key};
} elsif ($key eq 'default_features') {
$platform{$key} =
$platform{$key} . " " . $parent_plat{$key};
}
}
}
}
# Copy the %platforms array into the appropriate global
local($eval_string) = '%' . 'platform_' . $platform_name
. ' = %' . 'platform';
eval($eval_string);
$all_platforms{$platform_name} = 1;
}
}
close(LIDFILE);
}
# get_platform($platform_name) -- internal
#
# Return the platform (associative array) that corresponds to
# $platform_name. This function will lowercase the platform name and
# translate dashes into underbars for you.
#
sub get_platform {
local($platform_name) = @_;
$platform_name =~ tr/-A-Z/_a-z/;
$all_platforms{$platform_name} || die("Unknown platform: $platform_name");
local(%platform) = eval('%platform_' . $platform_name);
return %platform;
}
# Subroutines people can call from Makegen files. See top of file for
# summary.
# compile_subdirs(@subdirs) -- exported
#
# Generates the makefile rule to visit a bunch of subdirs subdirectory.
#
sub compile_subdirs {
local($dir);
foreach $dir (@_) {
do compile_subdir($dir);
}
}
# emit_library_rule($lidfile, $extradeps, $extraflags, @keywords) -- exported
#
# Emit the Makefile rule to build a Dylan library. $lidfile is the
# name of the lidfile, $extradeps are dependencies in addition to the
# lid file and the "obvious" dependencies (used libraries and any file
# mentioned in the lid file). $extraflags are any extra flags you
# want to pass to d2c, esp. "-L../somedir". $extradeps are any
# additional dependencies you want to specify for the library being
# built (such as $buildroot/force.timestamp).
#
# Keywords recognized:
#
# 'compile'
# Compile this with d2c and add it to the list of libraries compiled by
# the 'compile' makefile target.
# 'install'
# Adds this library/executable to the list of files installed
# by the 'install' makefile target.
# 'no-mindy'
# By default, a target is created for dbc_only; this target
# is not added to any makefile targets, but it can be manually
# created by running "make dbc_only". This makes for easy debugging.
# 'no-mindy' suppresses the creation of this target, which is useful
# when the Mindy version of the library does not use the same source
# files as the d2c version.
# 'compile-mindy'
# Add libname.dbc to the 'compile' makefile target (possibly in
# addition to the d2c version of the library.) This flag does not make
# much sense with 'no-mindy'.
# 'no-d2c'
# Like no-mindy, only suppress emission of d2c rules.
#
# The return value is a two element array. The first element is the
# target which would have been used if the 'compile' flag were
# specified. The second element of the array is the target which
# would have been used if the 'compile-mindy' flag were specified.
# This return value is most useful when bootstrapping a compiler from
# Mindy, but I'm sure others can think of additional uses.
#
sub emit_library_rule {
local($lidfilename, $extradeps, $extraflags, @keywords) = @_;
do maybe_emit_d2c_defines();
local(%lidfile) = &parse_lid_file($lidfilename);
local($library) = $lidfile{'library'}
|| die("No library: header in $lidfilename.lid.\n");
$library =~ tr/A-Z/a-z/;
local($unit_prefix);
if($enable_bootstrap
&& $bootstrap_compiler eq "d2c"
&& $lid_format_version < 2) {
$unit_prefix = $lidfile{'unit_prefix'} || "$library";
} else {
$unit_prefix = "$library";
};
local($executable) = $lidfile{'executable'};
local($executable_filename) = $executable . $dot_exe;
local($shared_library) = $shared && ($lidfile{'shared_library'} ne 'no');
local($dot_static) = $target_platform{'object_filename_suffix'};
local($d2c_target);
if ($enable_d2c) {
local($file);
for $file (split(' ', $lidfile{'files'})) {
$file =~ s/\.dylan$//; # chop off ".dylan"
if ($&) { # This was a "something.dylan"
push(@files_to_clean, "$file.c", "$file$dot_obj");
} else {
# This is a *.o or something that is handled by the
# emit_c_file_rule ...
next;
}
if ($shared && $static) {
push(@files_to_clean, "$file$dot_static");
}
}
push(@files_to_clean,
"$unit_prefix-init.c", "$unit_prefix-init$dot_obj");
push(@files_to_clean,
"$unit_prefix-heap.c", "$unit_prefix-heap$dot_obj");
if($shared && $static) {
push(@files_to_clean, "$unit_prefix-init$dot_static");
push(@files_to_clean, "$unit_prefix-heap$dot_static");
}
if ($executable) {
push(@files_to_clean, $executable_filename, "$unit_prefix-global-heap.c", "$unit_prefix-global-inits.c");
if($shared && $static) {
push(@files_to_clean, "$unit_prefix-global-heap$dot_static", "$unit_prefix-global-inits$dot_static");
}
push(@files_to_clean, "$unit_prefix-global-heap$dot_obj", "$unit_prefix-global-inits$dot_obj");
if (grep(/^compile$/, @keywords)) {
push(@compile_dependencies, $executable_filename);
grep(/^install$/, @keywords)
&& do install_executable($bindir, $executable_filename);
}
$d2c_target = $executable_filename;
}
else {
local($foo_lib_du, $libfoo_a);
$foo_lib_du = "$library.lib.du";
$libfoo_a = "$lib_prefix$unit_prefix-dylan$dot_lib";
push(@files_to_clean, $foo_lib_du, $libfoo_a);
if($shared && $static) {
local($lib_static)
= $target_platform{'library_filename_suffix'};
push(@files_to_clean, "$lib_prefix$unit_prefix-dylan$lib_static");
}
if (grep(/^compile$/, @keywords)) {
push(@compile_dependencies, $foo_lib_du);
if (grep(/^install$/, @keywords)) {
do install($libdir, $foo_lib_du);
do install_library($libdir, $libfoo_a, $shared_library);
}
}
$d2c_target = $foo_lib_du;
}
}
if($extraflags ne '') {
print STDERR "gen-makefile: Makegen file contains extraflags: ";
print STDERR "'$extraflags'\n";
}
if (! grep(/^no-d2c$/, @keywords) && $enable_d2c) {
print "$d2c_target: $srcdir/$lidfilename.lid $extradeps\n";
my $profile_flag = $enable_profiling ? "--profile" : "";
if ($shared_library) {
print "\t\$(D2C) -M $profile_flag \$(D2CFLAGS) --rpath=$libdir $extraflags ",
"$srcdir/$lidfilename.lid\n";
if ($static && !$executable) {
local ($dot_lib)
= split(/\s+/,
$target_platform{'library_filename_suffix'});
print "\trm -f $lib_prefix$unit_prefix-dylan$dot_lib\n";
print "\tln -s .libs/$lib_prefix$unit_prefix-dylan$dot_lib",
" $lib_prefix$unit_prefix-dylan$dot_lib\n";
push(@files_to_clean, " $lib_prefix$unit_prefix-dylan$dot_lib");
}
} else {
print "\t\$(D2C) -M $profile_flag \$(D2CFLAGS) --static $extraflags ",
"$srcdir/$lidfilename.lid\n";
}
local ($make_name) = $host_platform{'make_command'};
local($cc_files_cmd) = "$make_name -f cc-" . $unit_prefix
. "-files.mak";
push(@cc_files_commands, $cc_files_cmd);
print "\ninclude $unit_prefix.dep\n\n";
unless (-e "$unit_prefix.dep") {
open(DEP, ">$unit_prefix.dep")
|| die("Can't create $unit_prefix.dep: $!\n");
close(DEP);
}
}
if (! grep(/^no-mindy$/, @keywords) && $enable_mindy) {
&emit_mindycomp_library_rule($library, $lidfile{'files'}, %lidfile);
}
if (grep(/^compile-mindy$/, @keywords) && $enable_mindy) {
push(@compile_dependencies, "$library-lib.dbc");
grep(/^install$/, @keywords)
&& do install($libdir, "$library-lib.dbc");
}
local(@return_val);
if ($enable_d2c) {
push(@return_val, $d2c_target);
}
if ($enable_mindy) {
push(@return_val, "$library-lib.dbc");
}
return @return_val;
}
# emit_c_file_rule($base_file_name, @header_files) -- exported
#
# Emit the rule to compile a C file into an object file. Dependencies
# will be the source file and all @header_files.
#
sub emit_c_file_rule {
local ($basefile, @headers) = @_;
local ($objfile) = "$basefile$dot_obj";
do maybe_emit_d2c_defines();
print "$objfile: \$(SRCDIR)/$basefile.c";
foreach $header (@headers) {
print " \$(SRCDIR)/$header";
}
if ($shared) {
print "\n\t\$(LIBTOOL) --mode=compile \$(CC) \$(CCFLAGS) -I\$(SRCDIR)";
} else {
print "\n\t\$(CC) \$(CCFLAGS) -I\$(SRCDIR)";
}
print " -c \$(SRCDIR)/$basefile.c -o $objfile\n\n";
if ($target_name eq $host_name) {
push(@compile_dependencies, $objfile);
} else {
push(@cc_files_dependencies, $objfile);
};
push(@files_to_clean, $objfile);
if($static && $shared) {
local ($dot_static) = $target_platform{'object_filename_suffix'};
push(@files_to_clean, "$basefile$dot_static");
}
}
#
# Emit the rule to compile an assembly file into an object file. Dependencies
# will be the source file and all @header_files.
#
sub emit_asm_file_rule {
local ($basefile, $srcfile, @headers) = @_;
local ($objfile) = "$basefile$dot_obj";
do maybe_emit_d2c_defines();
print "$objfile: \$(SRCDIR)/$srcfile.s";
foreach $header (@headers) {
print " \$(SRCDIR)/$header";
}
if ($shared) {
print "\n\t\$(LIBTOOL) --mode=compile \$(CC) \$(CFLAGS) \$(CPPFLAGS) -I\$(SRCDIR)";
} else {
print "\n\t\$(CC) \$(CFLAGS) \$(CPPFLAGS) -I\$(SRCDIR)";
}
print " -c \$(SRCDIR)/$srcfile.s -o $objfile\n\n";
if ($target_name eq $host_name) {
push(@compile_dependencies, $objfile);
} else {
push(@cc_files_dependencies, $objfile);
};
push(@files_to_clean, $objfile);
}
#
# Emit the rule to compile an assembly file (that requires
# preprocessing) into an object file. Dependencies will be the source
# file and all @header_files.
#
sub emit_cppasm_file_rule {
local ($basefile, $srcfile, @headers) = @_;
local ($objfile) = "$basefile$dot_obj";
do maybe_emit_d2c_defines();
print "$objfile: \$(SRCDIR)/$srcfile.s";
foreach $header (@headers) {
print " \$(SRCDIR)/$header";
}
if ($shared) {
print "\n\t\$(LIBTOOL) --mode=compile \$(CC) \$(CFLAGS) \$(CPPFLAGS) -I\$(SRCDIR)";
} else {
print "\n\t\$(CC) \$(CFLAGS) \$(CPPFLAGS) -I\$(SRCDIR)";
}
print " -x assembler-with-cpp"; # Added to provide the "cpp" part
print " -c \$(SRCDIR)/$srcfile.s -o $objfile\n\n";
if ($target_name eq $host_name) {
push(@compile_dependencies, $objfile);
} else {
push(@cc_files_dependencies, $objfile);
};
push(@files_to_clean, $objfile);
}
# emit_cc_file_rule($base_file_name, @header_files) -- exported
#
# Just like emit_c_file_rule, except uses .cc as an extension instead
# of .c (C++ files instead of C files). I don't think this function
# is currently used by any of our Makegens.
#
sub emit_cc_file_rule {
local ($basefile, @headers) = @_;
local ($objfile) = "$basefile$dot_obj";
do maybe_emit_d2c_defines();
print "$objfile: \$(SRCDIR)/$basefile.cc";
foreach $header (@headers) {
print " \$(SRCDIR)/$header";
}
if ($shared) {
print "\n\t\$(LIBTOOL) --mode=compile \$(CC) \$(CFLAGS) -I\$(SRCDIR) ";
} else {
print "\n\t\$(CC) \$(CFLAGS) -I\$(SRCDIR) ";
}
print "-c \$(SRCDIR)/$basefile.cc -o $objfile\n\n";
if ($target_name eq $host_name) {
push(@compile_dependencies, $objfile);
} else {
push(@cc_files_dependencies, $objfile);
};
push(@files_to_clean, $objfile);
}
# emit_melange_rule($base_file_name, @dependencies) -- exported
#
# Emits the rule to turn a Melange input (.intr) file into a .dylan
# file.
sub emit_melange_rule {
local ($basefile, @headers) = @_;
do maybe_emit_d2c_defines();
print "$basefile.dylan: \$(SRCDIR)/$basefile.intr";
foreach $header (@headers) {
print " \$(SRCDIR)/$header";
}
print "\n";
printf("\t$host_platform{'recursive_make_command'}\n", $MELANGE_DIR, "");
print "\t\$(MELANGE) --d2c \$(CPPFLAGS) -I\$(SRCDIR) ";
if($EXTRA_INCLUDE_DIR ne "") {
print "-I$EXTRA_INCLUDE_DIR ";
}
print "\$(SRCDIR)/$basefile.intr $basefile.dylan, && mv $basefile.dylan, $basefile.dylan \n\n";
push(@compile_dependencies, "$basefile.dylan");
push(@files_to_clean, "$basefile.dylan");
}
# emit_parsergen_rule($base_file_name) -- exported
#
# Emit the rule to Melange a .input file into a .dylan file.
#
sub emit_parsergen_rule {
local ($basefile) = @_;
do maybe_emit_d2c_defines();
print "$basefile.dylan: \$(SRCDIR)/$basefile.input\n";
printf("\t$host_platform{'recursive_make_command'}\n", $PARSERGEN_DIR, "");
print "\t\$(PARSERGEN) ";
print "\$(SRCDIR)/$basefile.input $basefile.dylan\n\n";
push(@compile_dependencies, "$basefile.dylan");
push(@files_to_clean, "$basefile.dylan");
}
# install_one_file($from_file, $to_file, $executable_permissions) -- internal
#
# Emits the rule to copy $from_file into $to_file when "make install"
# is run. If $executable_permissions is true (non-zero), then the
# installed file will have executable permissions.
#
# This may seem like a good candidate for moving to platforms.descr.
# Unfortunately, there's a lot more going on than merely choosing the
# name of the "copy file" command.
#
sub install_one_file {
local ($from_file, $to_file, $executable_permissions) = @_;
# GNU DESTDIR, not the $destdir variable.
unless ($host_platform{'default_features'} =~ /compiled-for-win32/i) {
$to_file = "\${DESTDIR}$to_file";
}
print "\n$to_file: $from_file\n";
if ( ($host_platform{'default_features'} =~ /compiled-for-win32/i)
|| ($host_platform{'default_features'} =~ /compiled-for-mingw32/i))
{
# Host is a win32 platform
local ($rule) = "\tcopy $from_file $to_file\n\n";
$rule =~ tr|/|\\|; # translate / to \ for MS-Windows
print $rule;
} elsif ($host_platform{'default_features'} =~ /compiled-for-unix/i) {
if ($executable_permissions) {
print "\t$install_program $from_file $to_file\n\n";
} else {
print "\t$install_data $from_file $to_file\n\n";
};
} else {
die("The host is neither Unix nor win32, so I don't know how\n"
. "to install your files.");
}
push(@install_dependencies, $to_file);
push(@uninstall_commands, "rm -f $to_file");
}
# install($dest_dir, @files) -- exported
#
# Installs a bunch of files from the build dir into $dest_dir, normally a
# subdirectory of $destdir.
#
sub install {
local ($dest_dir, @files) = @_;
local ($file, $dst);
# Don't generate any install rules unless destdir is set.
$destdir || return;
foreach $file (@files) {
$dst = "$dest_dir/$file";
&install_one_file($file, $dst, 0);
}
}
# install_executable($dest_dir, @files) -- exported
#
# Installs a bunch of files from the build dir into $dest_dir, normally a
# subdirectory of $destdir. Makes them executable.
#
sub install_executable {
local ($dest_dir, @files) = @_;
local ($file, $dst);
# Don't generate any install rules unless destdir is set.
$destdir || return;
foreach $file (@files) {
$dst = "$dest_dir/$file";
&install_one_file($file, $dst, 1);
}
}
# install_from_src($dest_dir, @files) -- exported
#
# Installs a bunch of files from the source dir into dest_dir. Useful when you
# just want to copy Perl scripts into the dest dir.
#
sub install_from_src {
local ($dest_dir, @files) = @_;
local ($file, $dst);
# Don't generate any install rules unless destdir is set.
$destdir || return;
foreach $file (@files) {
$dst = "$dest_dir/$file";
&install_one_file("$srcdir/$file", $dst, 0);
}
}
# install_executable_from_src($dest_dir, @files) -- exported
#
# Like install_from_src, except makes sure the file has executable
# permissions for everyone (this is not relevant on win32, only unix).
#
sub install_executable_from_src {
local ($dest_dir, @files) = @_;
local ($file, $dst);
# Don't generate any install rules unless destdir is set.
$destdir || return;
foreach $file (@files) {
$dst = "$dest_dir/$file";
&install_one_file("$srcdir/$file", $dst, 1);
}
}
# install_library_from_src($dest_dir, $library, $shared_library) -- exported
#
# Installs library in the given location. If it's shared, use libtool
# to do it.
#
sub install_library {
local ($dest_dir, $library, $shared_library) = @_;
local ($gnu_dest_dir) = $dest_dir;
# Don't generate any install rules unless destdir is set.
$dest_dir || return;
unless ($host_platform{'default_features'} =~ /compiled-for-win32/i) {
$gnu_dest_dir = "\${DESTDIR}$gnu_dest_dir";
}
print "\n$gnu_dest_dir/$library: $library\n";
print "\t$install_library $library $gnu_dest_dir/$library\n\n";
push(@install_dependencies, "$gnu_dest_dir/$library");
}
# emit_mindycomp_rule($dylan_source) -- exported
#
# Emits a rule to run mindycomp on the file. $dylan_source shouldn't
# have an extension. If the file doesn't exist in the source, hopefully
# someone will create it in the build area.
#
# emit_mindycomp_library_rule is the primary, perhaps even the only,
# user of this function.
#
sub emit_mindycomp_rule {
local($source, $flags) = @_;
if (-e "$srcdir/$source.dylan") {
$da_source = "\$(SRCDIR)/$source.dylan";
} else {
$da_source = "$source.dylan";
}
print "$source.dbc: $da_source\n";
print "\t\$(MC) $flags ";
print "-o $source.dbc $da_source\n";
}
# emit_dbc_link_rule($libname, $dbc_files) -- exported
#
# Links a bunch of .dbc files into a single .dbc file.
#
sub emit_dbc_link_rule {
local($libname, $objects) = @_;
print "\n$libname: $objects\n";
if ($host_platform{'use_dbclink?'}) {
print "\tdbclink $libname $objects\n\n";
} else {
print "\tcat $objects > $libname\n\n";
}
}
# emit_mindycomp_library_rule($libname, $source_files) -- exported
#
# Generates a rule for compiling the library with Mindy. Usually this
# function is invoked via emit_library_rule, but you can call it on
# your own if you want. The resulting file will be called
# foo-lib.dbc, and will *not* be added to the list of compile
# dependencies. Also, this command only builds libraries, not
# executables. To build an executable, we need to know all the other
# libraries that go into it, which is beyond the scope of this
# function.
#
sub emit_mindycomp_library_rule {
local($libname, $lid_sources, %lidfile) = @_;
$libname =~ tr/A-Z/a-z/;
local($uppercase_libname) = $libname;
$uppercase_libname =~ tr/-a-z/_A-Z/;
local($file, @sources, @objects);
foreach $file (split(/\s+/, $lid_sources)) {
$file =~ s/\.dylan$//; # chop off ".dylan"
push(@sources, $file); # emit_mindycomp_rule doesn't want ".dylan"
push(@objects, "$file.dbc");
push(@files_to_clean, "$file.dbc");
}
print "$uppercase_libname", "_OBJS = ", join(" ", @objects), "\n\n";
push(@dbc_only_dependencies, "$libname-lib.dbc");
push(@files_to_clean, "$libname-lib.dbc");
local $flags = "-l$libname";
local $feature;
foreach $feature (split /\s+/, $target_platform{'default_features'}) {
$flags = "$flags -D$feature";
}
foreach $feature (split /\s+/, $lidfile{'features'}) {
if($feature =~ /~(.*)/) {
$flags = "$flags -U$1";
} else {
$flags = "$flags -D$feature";
}
}
local($source);
foreach $source (@sources) {
&emit_mindycomp_rule($source, $flags);
}
&emit_dbc_link_rule("$libname-lib.dbc", "\$(${uppercase_libname}_OBJS)");
}
# makegen_include($include_file) -- exported
#
# makegen_include is like C's #include. The main reason to use
# makegen_include is that it correctly handles the case where the
# srcdir is not the same as the build dir (ie, it looks for the include
# file in the srcdir).
#
# There's a certain danger of variable capture with the variable
# $include_file, so don't use that variable in your included files.
#
sub makegen_include {
local($include_file) = @_;
$include_file = $srcdir . '/' . $include_file;
(-e $include_file) || die("No $include_file\n");
push(@GNUmakefile_dependencies, $include_file);
# Convert \ back to /, because that's how "do" likes its filenames
$include_file =~ tr|\\|/|;
do $include_file;
}
# unknown_platform_error() -- exported
#
# Tries to give a useful error message when a Makegen doesn't
# understand the platform.
#
sub unknown_platform_error {
die "Unknown CPU type for platform " . $target_name . "\n"
. "All known features: " . $target_platform{'default_features'} . "\n";
}
# convert_path_separator($path) -- exported
#
# Converts a path which uses / into one that uses the path separator
# appropriate for the host platform.
#
sub convert_path_separator {
local($path) = @_;
local($separator) = $host_platform{'path_separator'};
$separator =~ s|\\\\|\\|g; # translate \\ into \ (Dylan escaping rules)
$path =~ s|/|$separator|g;
return $path;
}
syntax highlighted by Code2HTML, v. 0.9.1