diff options
Diffstat (limited to 'lib/Automake/LangHandling.pm')
-rw-r--r-- | lib/Automake/LangHandling.pm | 484 |
1 files changed, 484 insertions, 0 deletions
diff --git a/lib/Automake/LangHandling.pm b/lib/Automake/LangHandling.pm new file mode 100644 index 000000000..bfbbb003c --- /dev/null +++ b/lib/Automake/LangHandling.pm @@ -0,0 +1,484 @@ +# Copyright (C) 2018 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package Automake::LangHandling; + +use Automake::Condition qw (TRUE FALSE); +use Automake::ChannelDefs; +use Automake::Global; +use Automake::Language; +use Automake::Location; +use Automake::Options; +use Automake::Requires; +use Automake::Rule; +use Automake::SilentRules; +use Automake::Utils; +use Automake::Variable; +use Automake::VarDef; +use Automake::Wrap qw (makefile_wrap); +use Exporter; +use File::Basename; + +use vars '@ISA', '@EXPORT'; + +@ISA = qw (Exporter); + +@EXPORT = qw (check_user_variables lang_sub_obj lang_header_rewrite + lang_vala_rewrite lang_yacc_rewrite lang_yaccxx_rewrite + lang_lex_rewrite lang_lexxx_rewrite lang_java_rewrite + lang_vala_finish_target lang_vala_finish + lang_vala_target_hook lang_yacc_target_hook + lang_lex_target_hook yacc_lex_finish_helper + lang_yacc_finish lang_lex_finish resolve_linker + saw_extension register_language derive_suffix + pretty_print_rule); + +# check_user_variables (@LIST) +# ---------------------------- +# Make sure each variable VAR in @LIST does not exist, suggest using AM_VAR +# otherwise. +sub check_user_variables +{ + my @dont_override = @_; + foreach my $flag (@dont_override) + { + my $var = var $flag; + if ($var) + { + for my $cond ($var->conditions->conds) + { + if ($var->rdef ($cond)->owner == VAR_MAKEFILE) + { + msg_cond_var ('gnu', $cond, $flag, + "'$flag' is a user variable, " + . "you should not override it;\n" + . "use 'AM_$flag' instead"); + } + } + } + } +} + +################################################################ +# +# Functions to handle files of each language. + +# Each 'lang_X_rewrite($DIRECTORY, $BASE, $EXT)' function follows a +# simple formula: Return value is LANG_SUBDIR if the resulting object +# file should be in a subdir if the source file is, LANG_PROCESS if +# file is to be dealt with, LANG_IGNORE otherwise. + +# Much of the actual processing is handled in +# handle_single_transform. These functions exist so that +# auxiliary information can be recorded for a later cleanup pass. +# Note that the calls to these functions are computed, so don't bother +# searching for their precise names in the source. + +# This is just a convenience function that can be used to determine +# when a subdir object should be used. +sub lang_sub_obj () +{ + return option 'subdir-objects' ? LANG_SUBDIR : LANG_PROCESS; +} + +# Rewrite a single header file. +sub lang_header_rewrite +{ + # Header files are simply ignored. + return LANG_IGNORE; +} + +# Rewrite a single Vala source file. +sub lang_vala_rewrite +{ + my ($directory, $base, $ext) = @_; + + (my $newext = $ext) =~ s/vala$/c/; + return (LANG_SUBDIR, $newext); +} + +# Rewrite a single yacc/yacc++ file. +sub lang_yacc_rewrite +{ + my ($directory, $base, $ext) = @_; + + my $r = lang_sub_obj; + (my $newext = $ext) =~ tr/y/c/; + return ($r, $newext); +} +sub lang_yaccxx_rewrite { lang_yacc_rewrite (@_); }; + +# Rewrite a single lex/lex++ file. +sub lang_lex_rewrite +{ + my ($directory, $base, $ext) = @_; + + my $r = lang_sub_obj; + (my $newext = $ext) =~ tr/l/c/; + return ($r, $newext); +} +sub lang_lexxx_rewrite { lang_lex_rewrite (@_); }; + +# Rewrite a single Java file. +sub lang_java_rewrite +{ + return LANG_SUBDIR; +} + +# The lang_X_finish functions are called after all source file +# processing is done. Each should handle defining rules for the +# language, etc. A finish function is only called if a source file of +# the appropriate type has been seen. + +sub lang_vala_finish_target +{ + my ($self, $name) = @_; + + my $derived = canonicalize ($name); + my $var = var "${derived}_SOURCES"; + return unless $var; + + my @vala_sources = grep { /\.(vala|vapi)$/ } ($var->value_as_list_recursive); + + # For automake bug#11229. + return unless @vala_sources; + + foreach my $vala_file (@vala_sources) + { + my $c_file = $vala_file; + if ($c_file =~ s/(.*)\.vala$/$1.c/) + { + $c_file = "\$(srcdir)/$c_file"; + $output_rules .= "$c_file: \$(srcdir)/${derived}_vala.stamp\n" + . "\t\@if test -f \$@; then :; else rm -f \$(srcdir)/${derived}_vala.stamp; fi\n" + . "\t\@if test -f \$@; then :; else \\\n" + . "\t \$(MAKE) \$(AM_MAKEFLAGS) \$(srcdir)/${derived}_vala.stamp; \\\n" + . "\tfi\n"; + $clean_files{$c_file} = MAINTAINER_CLEAN; + } + } + + # Add rebuild rules for generated header and vapi files + my $flags = var ($derived . '_VALAFLAGS'); + if ($flags) + { + my $lastflag = ''; + foreach my $flag ($flags->value_as_list_recursive) + { + if (grep (/$lastflag/, ('-H', '-h', '--header', '--internal-header', + '--vapi', '--internal-vapi', '--gir'))) + { + my $headerfile = "\$(srcdir)/$flag"; + $output_rules .= "$headerfile: \$(srcdir)/${derived}_vala.stamp\n" + . "\t\@if test -f \$@; then :; else rm -f \$(srcdir)/${derived}_vala.stamp; fi\n" + . "\t\@if test -f \$@; then :; else \\\n" + . "\t \$(MAKE) \$(AM_MAKEFLAGS) \$(srcdir)/${derived}_vala.stamp; \\\n" + . "\tfi\n"; + + # valac is not used when building from dist tarballs + # distribute the generated files + push_dist_common ($headerfile); + $clean_files{$headerfile} = MAINTAINER_CLEAN; + } + $lastflag = $flag; + } + } + + my $compile = $self->compile; + + # Rewrite each occurrence of 'AM_VALAFLAGS' in the compile + # rule into '${derived}_VALAFLAGS' if it exists. + my $val = "${derived}_VALAFLAGS"; + $compile =~ s/\(AM_VALAFLAGS\)/\($val\)/ + if set_seen ($val); + + # VALAFLAGS is a user variable (per GNU Standards), + # it should not be overridden in the Makefile... + check_user_variables 'VALAFLAGS'; + + my $dirname = dirname ($name); + + # Only generate C code, do not run C compiler + $compile .= " -C"; + + my $verbose = verbose_flag ('VALAC'); + my $silent = silent_flag (); + my $stampfile = "\$(srcdir)/${derived}_vala.stamp"; + + $output_rules .= + "\$(srcdir)/${derived}_vala.stamp: @vala_sources\n". +# Since the C files generated from the vala sources depend on the +# ${derived}_vala.stamp file, we must ensure its timestamp is older than +# those of the C files generated by the valac invocation below (this is +# especially important on systems with sub-second timestamp resolution). +# Thus we need to create the stamp file *before* invoking valac, and to +# move it to its final location only after valac has been invoked. + "\t${silent}rm -f \$\@ && echo stamp > \$\@-t\n". + "\t${verbose}\$(am__cd) \$(srcdir) && $compile @vala_sources\n". + "\t${silent}mv -f \$\@-t \$\@\n"; + + push_dist_common ($stampfile); + + $clean_files{$stampfile} = MAINTAINER_CLEAN; +} + +# Add output rules to invoke valac and create stamp file as a witness +# to handle multiple outputs. This function is called after all source +# file processing is done. +sub lang_vala_finish () +{ + my ($self) = @_; + + foreach my $prog (keys %known_programs) + { + lang_vala_finish_target ($self, $prog); + } + + while (my ($name) = each %known_libraries) + { + lang_vala_finish_target ($self, $name); + } +} + +# The built .c files should be cleaned only on maintainer-clean +# as the .c files are distributed. This function is called for each +# .vala source file. +sub lang_vala_target_hook +{ + my ($self, $aggregate, $output, $input, %transform) = @_; + + $clean_files{$output} = MAINTAINER_CLEAN; +} + +# This is a yacc helper which is called whenever we have decided to +# compile a yacc file. +sub lang_yacc_target_hook +{ + my ($self, $aggregate, $output, $input, %transform) = @_; + + # If some relevant *YFLAGS variable contains the '-d' flag, we'll + # have to to generate special code. + my $yflags_contains_minus_d = 0; + + foreach my $pfx ("", "${aggregate}_") + { + my $yflagsvar = var ("${pfx}YFLAGS"); + next unless $yflagsvar; + # We cannot work reliably with conditionally-defined YFLAGS. + if ($yflagsvar->has_conditional_contents) + { + msg_var ('unsupported', $yflagsvar, + "'${pfx}YFLAGS' cannot have conditional contents"); + } + else + { + $yflags_contains_minus_d = 1 + if grep (/^-d$/, $yflagsvar->value_as_list_recursive); + } + } + + if ($yflags_contains_minus_d) + { + # Found a '-d' that applies to the compilation of this file. + # Add a dependency for the generated header file, and arrange + # for that file to be included in the distribution. + + # The extension of the output file (e.g., '.c' or '.cxx'). + # We'll need it to compute the name of the generated header file. + (my $output_ext = basename ($output)) =~ s/.*(\.[^.]+)$/$1/; + + # We know that a yacc input should be turned into either a C or + # C++ output file. We depend on this fact (here and in yacc.am), + # so check that it really holds. + my $lang = $languages{$extension_map{$output_ext}}; + prog_error "invalid output name '$output' for yacc file '$input'" + if (!$lang || ($lang->name ne 'c' && $lang->name ne 'cxx')); + + (my $header_ext = $output_ext) =~ s/c/h/g; + # Quote $output_ext in the regexp, so that dots in it are taken + # as literal dots, not as metacharacters. + (my $header = $output) =~ s/\Q$output_ext\E$/$header_ext/; + + foreach my $cond (Automake::Rule::define (${header}, 'internal', + RULE_AUTOMAKE, TRUE, + INTERNAL)) + { + my $condstr = $cond->subst_string; + $output_rules .= + "$condstr${header}: $output\n" + # Recover from removal of $header + . "$condstr\t\@if test ! -f \$@; then rm -f $output; else :; fi\n" + . "$condstr\t\@if test ! -f \$@; then \$(MAKE) \$(AM_MAKEFLAGS) $output; else :; fi\n"; + } + # Distribute the generated file, unless its .y source was + # listed in a nodist_ variable. (handle_source_transform() + # will set DIST_SOURCE.) + push_dist_common ($header) + if $transform{'DIST_SOURCE'}; + + # The GNU rules say that yacc/lex output files should be removed + # by maintainer-clean. However, if the files are not distributed, + # then we want to remove them with "make clean"; otherwise, + # "make distcheck" will fail. + $clean_files{$header} = $transform{'DIST_SOURCE'} ? MAINTAINER_CLEAN : CLEAN; + } + # See the comment above for $HEADER. + $clean_files{$output} = $transform{'DIST_SOURCE'} ? MAINTAINER_CLEAN : CLEAN; +} + +# This is a lex helper which is called whenever we have decided to +# compile a lex file. +sub lang_lex_target_hook +{ + my ($self, $aggregate, $output, $input, %transform) = @_; + # The GNU rules say that yacc/lex output files should be removed + # by maintainer-clean. However, if the files are not distributed, + # then we want to remove them with "make clean"; otherwise, + # "make distcheck" will fail. + $clean_files{$output} = $transform{'DIST_SOURCE'} ? MAINTAINER_CLEAN : CLEAN; +} + +# This is a helper for both lex and yacc. +sub yacc_lex_finish_helper () +{ + return if defined $language_scratch{'lex-yacc-done'}; + $language_scratch{'lex-yacc-done'} = 1; + + # FIXME: for now, no line number. + require_conf_file ($configure_ac, FOREIGN, 'ylwrap'); + define_variable ('YLWRAP', "$am_config_aux_dir/ylwrap", INTERNAL); +} + +sub lang_yacc_finish () +{ + return if defined $language_scratch{'yacc-done'}; + $language_scratch{'yacc-done'} = 1; + + reject_var 'YACCFLAGS', "'YACCFLAGS' obsolete; use 'YFLAGS' instead"; + + yacc_lex_finish_helper; +} + + +sub lang_lex_finish () +{ + return if defined $language_scratch{'lex-done'}; + $language_scratch{'lex-done'} = 1; + + yacc_lex_finish_helper; +} + + +# Given a hash table of linker names, pick the name that has the most +# precedence. This is lame, but something has to have global +# knowledge in order to eliminate the conflict. Add more linkers as +# required. +sub resolve_linker +{ + my (%linkers) = @_; + + foreach my $l (qw(GCJLINK OBJCXXLINK CXXLINK F77LINK FCLINK OBJCLINK UPCLINK)) + { + return $l if defined $linkers{$l}; + } + return 'LINK'; +} + +# Called to indicate that an extension was used. +sub saw_extension +{ + my ($ext) = @_; + $extension_seen{$ext} = 1; +} + +# register_language (%ATTRIBUTE) +# ------------------------------ +# Register a single language. +# Each %ATTRIBUTE is of the form ATTRIBUTE => VALUE. +sub register_language +{ + my (%option) = @_; + + # Set the defaults. + $option{'autodep'} = 'no' + unless defined $option{'autodep'}; + $option{'linker'} = '' + unless defined $option{'linker'}; + $option{'flags'} = [] + unless defined $option{'flags'}; + $option{'output_extensions'} = sub { return ( '.$(OBJEXT)', '.lo' ) } + unless defined $option{'output_extensions'}; + $option{'nodist_specific'} = 0 + unless defined $option{'nodist_specific'}; + + my $lang = new Automake::Language (%option); + + # Fill indexes. + $extension_map{$_} = $lang->name foreach @{$lang->extensions}; + $languages{$lang->name} = $lang; + my $link = $lang->linker; + if ($link) + { + if (exists $link_languages{$link}) + { + prog_error ("'$link' has different definitions in " + . $lang->name . " and " . $link_languages{$link}->name) + if $lang->link ne $link_languages{$link}->link; + } + else + { + $link_languages{$link} = $lang; + } + } + + # Update the pattern of known extensions. + accept_extensions (@{$lang->extensions}); + + # Update the suffix rules map. + foreach my $suffix (@{$lang->extensions}) + { + foreach my $dest ($lang->output_extensions->($suffix)) + { + register_suffix_rule (INTERNAL, $suffix, $dest); + } + } +} + +# derive_suffix ($EXT, $OBJ) +# -------------------------- +# This function is used to find a path from a user-specified suffix $EXT +# to $OBJ or to some other suffix we recognize internally, e.g. 'cc'. +sub derive_suffix +{ + my ($source_ext, $obj) = @_; + + while (!$extension_map{$source_ext} && $source_ext ne $obj) + { + my $new_source_ext = next_in_suffix_chain ($source_ext, $obj); + last if not defined $new_source_ext; + $source_ext = $new_source_ext; + } + + return $source_ext; +} + + +# Pretty-print something and append to '$output_rules'. +sub pretty_print_rule +{ + $output_rules .= makefile_wrap (shift, shift, @_); +} + +1; |