1# Copyright (C) 2003-2021 Free Software Foundation, Inc.
2
3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by
5# the Free Software Foundation; either version 2, or (at your option)
6# any later version.
7
8# This program is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11# GNU General Public License for more details.
12
13# You should have received a copy of the GNU General Public License
14# along with this program.  If not, see <https://www.gnu.org/licenses/>.
15
16package Automake::Options;
17
18use 5.006;
19use strict;
20use warnings FATAL => 'all';
21
22use Exporter;
23
24use Automake::Config;
25use Automake::ChannelDefs;
26use Automake::Channels;
27use Automake::Version;
28
29our @ISA = qw (Exporter);
30our @EXPORT = qw (option global_option
31		  set_option set_global_option
32		  unset_option unset_global_option
33		  process_option_list process_global_option_list
34		  set_strictness $strictness $strictness_name
35		  &FOREIGN &GNU &GNITS);
36
37=head1 NAME
38
39Automake::Options - keep track of Automake options
40
41=head1 SYNOPSIS
42
43  use Automake::Options;
44
45  # Option lookup and setting.
46  $opt = option 'name';
47  $opt = global_option 'name';
48  set_option 'name', 'value';
49  set_global_option 'name', 'value';
50  unset_option 'name';
51  unset_global_option 'name';
52
53  # Batch option setting.
54  process_option_list $location, @names;
55  process_global_option_list $location, @names;
56
57  # Strictness lookup and setting.
58  set_strictness 'foreign';
59  set_strictness 'gnu';
60  set_strictness 'gnits';
61  if ($strictness >= GNU) { ... }
62  print "$strictness_name\n";
63
64=head1 DESCRIPTION
65
66This packages manages Automake's options and strictness settings.
67Options can be either local or global.  Local options are set using an
68C<AUTOMAKE_OPTIONS> variable in a F<Makefile.am> and apply only to
69this F<Makefile.am>.  Global options are set from the command line or
70passed as an argument to C<AM_INIT_AUTOMAKE>, they apply to all
71F<Makefile.am>s.
72
73=cut
74
75# Values are the Automake::Location of the definition.
76our %_options;        # From AUTOMAKE_OPTIONS
77our %_global_options; # From AM_INIT_AUTOMAKE or the command line.
78
79# Whether process_option_list has already been called for the current
80# Makefile.am.
81our $_options_processed;
82# Whether process_global_option_list has already been called.
83our $_global_options_processed;
84
85=head2 Constants
86
87=over 4
88
89=item FOREIGN
90
91=item GNU
92
93=item GNITS
94
95Strictness constants used as values for C<$strictness>.
96
97=back
98
99=cut
100
101# Constants to define the "strictness" level.
102use constant FOREIGN => 0;
103use constant GNU     => 1;
104use constant GNITS   => 2;
105
106=head2 Variables
107
108=over 4
109
110=item C<$strictness>
111
112The current strictness.  One of C<FOREIGN>, C<GNU>, or C<GNITS>.
113
114=item C<$strictness_name>
115
116The current strictness name.  One of C<'foreign'>, C<'gnu'>, or C<'gnits'>.
117
118=back
119
120=cut
121
122# Strictness levels.
123our ($strictness, $strictness_name);
124
125# Strictness level as set on command line.
126our ($_default_strictness, $_default_strictness_name);
127
128
129=head2 Functions
130
131=over 4
132
133=item C<Automake::Options::reset>
134
135Reset the options variables for the next F<Makefile.am>.
136
137In other words, this gets rid of all local options in use by the
138previous F<Makefile.am>.
139
140=cut
141
142sub reset ()
143{
144  $_options_processed = 0;
145  %_options = %_global_options;
146  # The first time we are run,
147  # remember the current setting as the default.
148  if (defined $_default_strictness)
149    {
150      $strictness = $_default_strictness;
151      $strictness_name = $_default_strictness_name;
152    }
153  else
154    {
155      $_default_strictness = $strictness;
156      $_default_strictness_name = $strictness_name;
157    }
158}
159
160=item C<$value = option ($name)>
161
162=item C<$value = global_option ($name)>
163
164Query the state of an option.  If the option is unset, this
165returns the empty list.  Otherwise it returns the option's value,
166as set by C<set_option> or C<set_global_option>.
167
168Note that C<global_option> should be used only when it is
169important to make sure an option hasn't been set locally.
170Otherwise C<option> should be the standard function to
171check for options (be they global or local).
172
173=cut
174
175sub option ($)
176{
177  my ($name) = @_;
178  return () unless defined $_options{$name};
179  return $_options{$name};
180}
181
182sub global_option ($)
183{
184  my ($name) = @_;
185  return () unless defined $_global_options{$name};
186  return $_global_options{$name};
187}
188
189=item C<set_option ($name, $value)>
190
191=item C<set_global_option ($name, $value)>
192
193Set an option.  By convention, C<$value> is usually the location
194of the option definition.
195
196=cut
197
198sub set_option ($$)
199{
200  my ($name, $value) = @_;
201  $_options{$name} = $value;
202}
203
204sub set_global_option ($$)
205{
206  my ($name, $value) = @_;
207  $_global_options{$name} = $value;
208}
209
210
211=item C<unset_option ($name)>
212
213=item C<unset_global_option ($name)>
214
215Unset an option.
216
217=cut
218
219sub unset_option ($)
220{
221  my ($name) = @_;
222  delete $_options{$name};
223}
224
225sub unset_global_option ($)
226{
227  my ($name) = @_;
228  delete $_global_options{$name};
229}
230
231
232=item C<process_option_list (@list)>
233
234=item C<process_global_option_list (@list)>
235
236Process Automake's option lists.  C<@list> should be a list of hash
237references with keys C<option> and C<where>, where C<option> is an
238option as they occur in C<AUTOMAKE_OPTIONS> or C<AM_INIT_AUTOMAKE>,
239and C<where> is the location where that option occurred.
240
241These functions should be called at most once for each set of options
242having the same precedence; i.e., do not call it twice for two options
243from C<AM_INIT_AUTOMAKE>.
244
245Return 0 on error, 1 otherwise.
246
247=cut
248
249# $BOOL
250# _option_is_from_configure ($OPTION, $WHERE)
251# ----------------------------------------------
252# Check that the $OPTION given in location $WHERE is specified with
253# AM_INIT_AUTOMAKE, not with AUTOMAKE_OPTIONS.
254sub _option_is_from_configure ($$)
255{
256  my ($opt, $where)= @_;
257  return 1
258    if $where->get =~ /^configure\./;
259  error $where,
260        "option '$opt' can only be used as argument to AM_INIT_AUTOMAKE\n" .
261        "but not in AUTOMAKE_OPTIONS makefile statements";
262  return 0;
263}
264
265# $BOOL
266# _is_valid_easy_option ($OPTION)
267# -------------------------------
268# Explicitly recognize valid automake options that require no
269# special handling by '_process_option_list' below.
270sub _is_valid_easy_option ($)
271{
272  my $opt = shift;
273  return scalar grep { $opt eq $_ } qw(
274    check-news
275    color-tests
276    dejagnu
277    dist-bzip2
278    dist-lzip
279    dist-xz
280    dist-zip
281    dist-zstd
282    info-in-builddir
283    no-define
284    no-dependencies
285    no-dist
286    no-dist-built-sources
287    no-dist-gzip
288    no-exeext
289    no-installinfo
290    no-installman
291    no-texinfo.tex
292    nostdinc
293    readme-alpha
294    serial-tests
295    parallel-tests
296    silent-rules
297    std-options
298    subdir-objects
299  );
300}
301
302# $BOOL
303# _process_option_list (\%OPTIONS, @LIST)
304# ------------------------------------------
305# Process a list of options.  \%OPTIONS is the hash to fill with options
306# data.  @LIST is a list of options as get passed to public subroutines
307# process_option_list() and process_global_option_list() (see POD
308# documentation above).
309sub _process_option_list (\%@)
310{
311  my ($options, @list) = @_;
312  my @warnings = ();
313  my $ret = 1;
314
315  foreach my $h (@list)
316    {
317      local $_ = $h->{'option'};
318      my $where = $h->{'where'};
319      $options->{$_} = $where;
320      if ($_ eq 'gnits' || $_ eq 'gnu' || $_ eq 'foreign')
321        {
322          set_strictness ($_);
323        }
324      # TODO: Remove this special check in Automake 3.0.
325      elsif (/^(.*\/)?ansi2knr$/)
326        {
327          # Obsolete (and now removed) de-ANSI-fication support.
328          error ($where,
329                 "automatic de-ANSI-fication support has been removed");
330          $ret = 0;
331        }
332      # TODO: Remove this special check in Automake 3.0.
333      elsif ($_ eq 'cygnus')
334        {
335          error $where, "support for Cygnus-style trees has been removed";
336          $ret = 0;
337        }
338      # TODO: Remove this special check in Automake 3.0.
339      elsif ($_ eq 'dist-lzma')
340        {
341          error ($where, "support for lzma-compressed distribution " .
342                         "archives has been removed");
343          $ret = 0;
344        }
345      # TODO: Make this a fatal error in Automake 2.0.
346      elsif ($_ eq 'dist-shar')
347        {
348          msg ('obsolete', $where,
349               "support for shar distribution archives is deprecated.\n" .
350               "  It will be removed in Automake 2.0");
351        }
352      # TODO: Make this a fatal error in Automake 2.0.
353      elsif ($_ eq 'dist-tarZ')
354        {
355          msg ('obsolete', $where,
356               "support for distribution archives compressed with " .
357               "legacy program 'compress' is deprecated.\n" .
358               "  It will be removed in Automake 2.0");
359        }
360      elsif (/^filename-length-max=(\d+)$/)
361        {
362          delete $options->{$_};
363          $options->{'filename-length-max'} = [$_, $1];
364        }
365      elsif ($_ eq 'tar-v7' || $_ eq 'tar-ustar' || $_ eq 'tar-pax')
366        {
367          if (not _option_is_from_configure ($_, $where))
368            {
369              $ret = 0;
370            }
371          for my $opt ('tar-v7', 'tar-ustar', 'tar-pax')
372            {
373              next
374                if $opt eq $_ or ! exists $options->{$opt};
375              error ($where,
376                     "options '$_' and '$opt' are mutually exclusive");
377              $ret = 0;
378            }
379        }
380      elsif (/^\d+\.\d+(?:\.\d+)?[a-z]?(?:-[A-Za-z0-9]+)?$/)
381        {
382          # Got a version number.
383          if (Automake::Version::check ($VERSION, $&))
384            {
385              error ($where, "require Automake $_, but have $VERSION");
386              $ret = 0;
387            }
388        }
389      elsif (/^(?:--warnings=|-W)(.*)$/)
390        {
391          my @w = map { { cat => $_, loc => $where} } split (',', $1);
392          push @warnings, @w;
393        }
394      elsif (! _is_valid_easy_option $_)
395        {
396          error ($where, "option '$_' not recognized");
397          $ret = 0;
398        }
399    }
400
401  # We process warnings here, so that any explicitly-given warning setting
402  # will take precedence over warning settings defined implicitly by the
403  # strictness.
404  foreach my $w (@warnings)
405    {
406      msg 'unsupported', $w->{'loc'},
407          "unknown warning category '$w->{'cat'}'"
408        if switch_warning $w->{cat};
409    }
410
411  return $ret;
412}
413
414sub process_option_list (@)
415{
416  prog_error "local options already processed"
417    if $_options_processed;
418  $_options_processed = 1;
419  _process_option_list (%_options, @_);
420}
421
422sub process_global_option_list (@)
423{
424  prog_error "global options already processed"
425    if $_global_options_processed;
426  $_global_options_processed = 1;
427  _process_option_list (%_global_options, @_);
428}
429
430=item C<set_strictness ($name)>
431
432Set the current strictness level.
433C<$name> should be one of C<'foreign'>, C<'gnu'>, or C<'gnits'>.
434
435=cut
436
437# Set strictness.
438sub set_strictness ($)
439{
440  $strictness_name = $_[0];
441
442  Automake::ChannelDefs::set_strictness ($strictness_name);
443
444  if ($strictness_name eq 'gnu')
445    {
446      $strictness = GNU;
447    }
448  elsif ($strictness_name eq 'gnits')
449    {
450      $strictness = GNITS;
451    }
452  elsif ($strictness_name eq 'foreign')
453    {
454      $strictness = FOREIGN;
455    }
456  else
457    {
458      prog_error "level '$strictness_name' not recognized";
459    }
460}
461
4621;
463