summaryrefslogtreecommitdiff
path: root/lib/Automake/LangHandling.pm
blob: 04062692c18822784bd3d967d04e9f60349600b1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
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
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
# 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::TmpModule;
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::RuleDef;
use Automake::SilentRules;
use Automake::Utils;
use Automake::Variable;
use Automake::VarDef;
use Automake::Wrap qw (makefile_wrap);
use Exporter 'import';
use File::Basename;

use vars qw (@EXPORT);

@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 handle_emacs_lisp handle_python
    handle_java);

# 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, @_);
}


sub handle_emacs_lisp ()
{
  my @elfiles = am_install_var ('-candist', 'lisp', 'LISP',
                                'lisp', 'noinst');

  return if ! @elfiles;

  define_pretty_variable ('am__ELFILES', TRUE, INTERNAL,
			  map { $_->[1] } @elfiles);
  define_pretty_variable ('am__ELCFILES', TRUE, INTERNAL,
			  '$(am__ELFILES:.el=.elc)');
  # This one can be overridden by users.
  define_pretty_variable ('ELCFILES', TRUE, INTERNAL, '$(LISP:.el=.elc)');

  push @all, '$(ELCFILES)';

  require_variables ($elfiles[0][0], "Emacs Lisp sources seen", TRUE,
		     'EMACS', 'lispdir');
}

sub handle_python ()
{
  my @pyfiles = am_install_var ('-defaultdist', 'python', 'PYTHON',
                                'noinst');
  return if ! @pyfiles;

  require_variables ($pyfiles[0][0], "Python sources seen", TRUE, 'PYTHON');
  require_conf_file ($pyfiles[0][0], FOREIGN, 'py-compile');
  define_variable ('py_compile', "$am_config_aux_dir/py-compile", INTERNAL);
}

sub handle_java ()
{
    my @sourcelist = am_install_var ('-candist',
                                     'java', 'JAVA',
                                     'noinst', 'check');
    return if ! @sourcelist;

    my @prefixes = am_primary_prefixes ('JAVA', 1,
				        'noinst', 'check');

    my $dir;
    my @java_sources = ();
    foreach my $prefix (@prefixes)
      {
        (my $curs = $prefix) =~ s/^(?:nobase_)?(?:dist_|nodist_)?//;

	next
	  if $curs eq 'EXTRA';

        push @java_sources, '$(' . $prefix . '_JAVA' . ')';

	if (defined $dir)
	  {
	    err_var "${curs}_JAVA", "multiple _JAVA primaries in use"
	     unless $curs eq $dir;
	  }

	$dir = $curs;
      }

    define_pretty_variable ('am__java_sources', TRUE, INTERNAL,
                            "@java_sources");

    if ($dir eq 'check')
      {
        push (@check, "class$dir.stamp");
      }
    else
      {
        push (@all, "class$dir.stamp");
      }
}


1;