New URL for NEMO forge!   http://forge.nemo-ocean.eu

Since March 2022 along with NEMO 4.2 release, the code development moved to a self-hosted GitLab.
This present forge is now archived and remained online for history.
Subsystem.pm in vendors/lib/FCM/System/Make/Share – NEMO

source: vendors/lib/FCM/System/Make/Share/Subsystem.pm @ 10669

Last change on this file since 10669 was 10669, checked in by nicolasmartin, 5 years ago

Import latest FCM release from Github into the repository for testing

File size: 10.0 KB
Line 
1# ------------------------------------------------------------------------------
2# (C) British Crown Copyright 2006-17 Met Office.
3#
4# This file is part of FCM, tools for managing and building source code.
5#
6# FCM is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation, either version 3 of the License, or
9# (at your option) any later version.
10#
11# FCM is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with FCM. If not, see <http://www.gnu.org/licenses/>.
18# ------------------------------------------------------------------------------
19use strict;
20use warnings;
21
22# ------------------------------------------------------------------------------
23package FCM::System::Make::Share::Subsystem;
24use base qw{Exporter};
25
26our @EXPORT = qw{
27    _config_parse
28    _config_parse_class_prop
29    _config_parse_prop
30    _config_parse_inherit_hook_prop
31    _config_unparse_class_prop
32    _config_unparse_join
33    _config_unparse_prop
34    _prop
35    _prop0
36    _props
37};
38
39use FCM::Context::ConfigEntry;
40use FCM::Context::Make::Share::Property;
41use FCM::System::Exception;
42use Storable qw{dclone};
43use Text::ParseWords qw{shellwords};
44
45use constant {PROP_DEFAULT => 0, PROP_NS_OK => 1};
46
47# Aliases
48my $E = 'FCM::System::Exception';
49
50# Parses a configuration entry into the context.
51sub _config_parse {
52    my ($attrib_ref, $ctx, $entry, $label) = @_;
53    my %config_parser_of = (
54        'prop' => \&_config_parse_prop,
55        %{$attrib_ref->{config_parser_of}},
56    );
57    if (!$label || !exists($config_parser_of{$label})) {
58        return;
59    }
60    $config_parser_of{$label}->($attrib_ref, $ctx, $entry);
61    1;
62}
63
64# Parses a configuration entry into the subsystem property.
65sub _config_parse_class_prop {
66    my ($attrib_ref, $entry, $label) = @_;
67    if ($label ne 'prop') {
68        return;
69    }
70    if (@{$entry->get_ns_list()}) {
71        return $E->throw($E->CONFIG_NS, $entry);
72    }
73    my @keys = grep {$_ ne 'class'} keys(%{$entry->get_modifier_of()});
74    if (grep {!exists($attrib_ref->{prop_of}{$_})} @keys) {
75        return $E->throw($E->CONFIG_MODIFIER, $entry);
76    }
77    for my $key (@keys) {
78        $attrib_ref->{prop_of}{$key}[PROP_DEFAULT] = $entry->get_value();
79    }
80    1;
81}
82
83# Reads the ?.prop declaration from a config entry.
84sub _config_parse_prop {
85    my ($attrib_ref, $ctx, $entry) = @_;
86    for my $key (keys(%{$entry->get_modifier_of()})) {
87        my $prop = $ctx->get_prop_of($key);
88        if (!defined($prop)) {
89            if (!defined(_prop_default($attrib_ref, $key))) {
90                return $E->throw($E->CONFIG_MODIFIER, $entry);
91            }
92            $prop = FCM::Context::Make::Share::Property->new({id => $key});
93            $ctx->get_prop_of()->{$key} = $prop;
94        }
95        my $prop_ctx;
96        if (defined($entry->get_value())) {
97            $prop_ctx = $prop->CTX_VALUE->new({value => $entry->get_value()});
98        }
99        if (!@{$entry->get_ns_list()}) {
100            @{$entry->get_ns_list()} = (q{});
101        }
102        for my $ns (@{$entry->get_ns_list()}) {
103            if ($ns && !_prop_ns_ok($attrib_ref, $key)) {
104                return $E->throw($E->CONFIG_NS, $entry);
105            }
106            if (defined($prop_ctx)) {
107                $prop->get_ctx_of()->{$ns} = $prop_ctx;
108            }
109            elsif (exists($prop->get_ctx_of()->{$ns})) {
110                delete($prop->get_ctx_of()->{$ns});
111            }
112        }
113    }
114}
115
116# A hook command for the "inherit/use" declaration, inherit properties.
117sub _config_parse_inherit_hook_prop {
118    my ($attrib_ref, $ctx, $i_ctx) = @_;
119    while (my ($key, $i_prop) = each(%{$i_ctx->get_prop_of()})) {
120        if (!defined($ctx->get_prop_of($key))) {
121            $ctx->get_prop_of()->{$key} = dclone($i_prop);
122        }
123        my %prop_ctx_of = %{$ctx->get_prop_of($key)->get_ctx_of()};
124        while (my ($ns, $i_prop_ctx) = each(%{$i_prop->get_ctx_of()})) {
125            if (    !exists($prop_ctx_of{$ns})
126                ||  $prop_ctx_of{$ns}->get_inherited()
127            ) {
128                my $prop_ctx = dclone($i_prop_ctx);
129                $prop_ctx->set_inherited(1);
130                $ctx->get_prop_of($key)->get_ctx_of()->{$ns} = $prop_ctx;
131            }
132        }
133    }
134}
135
136# Serializes a list of words.
137sub _config_unparse_join {
138    join(
139        q{ },
140        (map {my $s = $_; $s =~ s{(["'\s])}{\\$1}gxms; $s} grep {defined()} @_),
141    );
142}
143
144# Entries of the class prop settings.
145sub _config_unparse_class_prop {
146    my ($attrib_ref, $id) = @_;
147    map {
148        my $key = $_;
149        FCM::Context::ConfigEntry->new({
150            label       => join(q{.}, $id, 'prop'),
151            modifier_of => {'class' => 1, $key => 1},
152            value       => $attrib_ref->{prop_of}{$key}[PROP_DEFAULT],
153        });
154    } sort keys(%{$attrib_ref->{prop_of}});
155}
156
157# Entries of the prop settings.
158sub _config_unparse_prop {
159    my ($attrib_ref, $ctx) = @_;
160    my $label = join(q{.}, $ctx->get_id(), 'prop');
161    my %prop_of = %{$ctx->get_prop_of()};
162    map {
163        my $key = $_;
164        my $setting = $prop_of{$key};
165        map {
166            my $ns = $_;
167            my $prop_ctx = $setting->get_ctx_of()->{$ns};
168            $prop_ctx->get_inherited()
169            ? ()
170            : FCM::Context::ConfigEntry->new({
171                label       => $label,
172                modifier_of => {$key => 1},
173                ns_list     => ($ns ? [$ns] : []),
174                value       => $prop_ctx->get_value(),
175            });
176        } sort(keys(%{$setting->get_ctx_of()}));
177    } sort(keys(%prop_of));
178}
179
180# Returns the value of a named property (for a given $ns).
181sub _prop {
182    my ($attrib_ref, $id, $ctx, $ns) = @_;
183    my $setting = defined($ctx) ? $ctx->get_prop_of()->{$id} : undef;
184    if (!defined($ctx) || !defined($setting)) {
185        return _prop_default($attrib_ref, $id);
186    }
187    if (!_prop_ns_ok($attrib_ref, $id) || !$ns) {
188        my $prop_ctx = $setting->get_ctx();
189        return (
190              defined($prop_ctx) ? $prop_ctx->get_value()
191            :                      _prop_default($attrib_ref, $id)
192        );
193    }
194    my %prop_ctx_of = %{$setting->get_ctx_of()};
195    my $iter_ref
196        = $attrib_ref->{util}->ns_iter($ns, $attrib_ref->{util}->NS_ITER_UP);
197    while (defined(my $item = $iter_ref->())) {
198        if (exists($prop_ctx_of{$item}) && defined($prop_ctx_of{$item})) {
199            return $prop_ctx_of{$item}->get_value();
200        }
201    }
202    return _prop_default($attrib_ref, $id);
203}
204
205# Returns the first non-space value of a $setting for a given $ns.
206sub _prop0 {
207    (_props(@_))[0];
208}
209
210# Returns all suitable values of a $setting for a given $ns.
211sub _props {
212    my $prop = _prop(@_);
213    shellwords($prop ? $prop : q{});
214}
215
216# Returns the default value of a named property.
217sub _prop_default {
218    my ($attrib_ref, $id) = @_;
219    if (!exists($attrib_ref->{prop_of}{$id})) {
220        return;
221    }
222    $attrib_ref->{prop_of}{$id}[PROP_DEFAULT];
223}
224
225# Returns true if the given property can accept a name-space.
226sub _prop_ns_ok {
227    my ($attrib_ref, $id) = @_;
228        exists($attrib_ref->{prop_of}{$id})
229    &&  exists($attrib_ref->{prop_of}{$id}[PROP_NS_OK])
230    &&  $attrib_ref->{prop_of}{$id}[PROP_NS_OK]
231    ;
232}
233
234# ------------------------------------------------------------------------------
2351;
236__END__
237
238=head1 NAME
239
240FCM::System::Make::Share::Subsystem
241
242=head1 SYNOPSIS
243
244    use FCM::System::Make::Share::Subsystem;
245
246=head1 DESCRIPTION
247
248Provides common "local" functions for a make subsystem.
249
250=head1 FUNCTIONS
251
252The following functions are automatically exported by this module.
253
254=over 4
255
256=item _config_parse(\%attrib,$ctx,$entry,$label)
257
258Reads a configuration $entry into the $ctx context. The $label is the label of
259the $entry, but with the prefix (which should be the same as $ctx->get_id() plus
260a dot) removed.
261
262=item _config_parse_class_prop(\%attrib,$entry,$label)
263
264Reads a configuration $entry into the subsystem default property
265$attrib{prop_of}. The $label is the label of the $entry, but with the prefix
266(the subsystem ID plus a dot) removed.
267
268=item _config_parse_prop(\%attrib,$ctx,$entry)
269
270Reads a property configuration $entry into the $ctx context. This method may
271die() with a FCM::System::Exception on error. If the property modifier is
272invalid for the given subsystem, it returns an exception with the CODE
273FCM::System::Exception->CONFIG_MODIFIER. If the property does not support a
274namespace, it returns an exception with the CODE
275FCM::System::Exception->CONFIG_NS.
276
277=item _config_parse_inherit_hook_prop(\%attrib,$ctx,$i_ctx)
278
279The $ctx context is the current subsystem context and the $i_ctx context is the
280inherited subsystem context. Inherits property settings from $i_ctx into $ctx.
281
282=item _config_unparse_join(@list)
283
284Joins the @list into a string that can be parsed again by shellwords.
285
286=item _config_unparse_class_prop(\%attrib,$id)
287
288Turns the default properties in the current subsystem into a list of
289configuration entries. $id is the ID of the current subsystem.
290
291=item _config_unparse_prop(\%attrib,$ctx)
292
293Turns the properties in $ctx into a list of configuration entries.
294
295=item _prop(\%attrib,$id,$ctx,$ns)
296
297Returns the value of property $id. If the property does not exist, it returns
298undef. If the property is not defined in $ctx, it returns the default value. If
299the property is defined in $ctx, it returns the defined value in $ctx. If $ns is
300set and a name-space is allowed for the property, it walks the name-space to
301attempt to return the nearest value of the property for the given name-space.
302
303=item _prop0(\%attrib,$id,$ctx,$ns)
304
305Shorthand for (_props(\%attrib,$id,$ctx,$ns))[0].
306
307=item _props(\%attrib,$id,$ctx,$ns)
308
309Shorthand for shellwords(_prop(\%attrib,$id,$ctx,$ns)).
310
311=back
312
313=head1 DEPENDENCIES
314
315The %attrib argument to the functions in this module may require the following
316keys to be set correctly: {config_parser_of}, {prop_of}, {util}.
317
318=head1 COPYRIGHT
319
320(C) Crown copyright Met Office. All rights reserved.
321
322=cut
Note: See TracBrowser for help on using the repository browser.