1e1051a39Sopenharmony_ci# Copyright 2021 The OpenSSL Project Authors. All Rights Reserved.
2e1051a39Sopenharmony_ci#
3e1051a39Sopenharmony_ci# Licensed under the Apache License 2.0 (the "License").  You may not use
4e1051a39Sopenharmony_ci# this file except in compliance with the License.  You can obtain a copy
5e1051a39Sopenharmony_ci# in the file LICENSE in the source distribution or at
6e1051a39Sopenharmony_ci# https://www.openssl.org/source/license.html
7e1051a39Sopenharmony_ci
8e1051a39Sopenharmony_cipackage OpenSSL::Config::Query;
9e1051a39Sopenharmony_ci
10e1051a39Sopenharmony_ciuse 5.10.0;
11e1051a39Sopenharmony_ciuse strict;
12e1051a39Sopenharmony_ciuse warnings;
13e1051a39Sopenharmony_ciuse Carp;
14e1051a39Sopenharmony_ci
15e1051a39Sopenharmony_ci=head1 NAME
16e1051a39Sopenharmony_ci
17e1051a39Sopenharmony_ciOpenSSL::Config::Query - Query OpenSSL configuration info
18e1051a39Sopenharmony_ci
19e1051a39Sopenharmony_ci=head1 SYNOPSIS
20e1051a39Sopenharmony_ci
21e1051a39Sopenharmony_ci    use OpenSSL::Config::Info;
22e1051a39Sopenharmony_ci
23e1051a39Sopenharmony_ci    my $query = OpenSSL::Config::Query->new(info => \%unified_info);
24e1051a39Sopenharmony_ci
25e1051a39Sopenharmony_ci    # Query for something that's expected to give a scalar back
26e1051a39Sopenharmony_ci    my $variable = $query->method(... args ...);
27e1051a39Sopenharmony_ci
28e1051a39Sopenharmony_ci    # Query for something that's expected to give a list back
29e1051a39Sopenharmony_ci    my @variable = $query->method(... args ...);
30e1051a39Sopenharmony_ci
31e1051a39Sopenharmony_ci=head1 DESCRIPTION
32e1051a39Sopenharmony_ci
33e1051a39Sopenharmony_ciThe unified info structure, commonly known as the %unified_info table, has
34e1051a39Sopenharmony_cibecome quite complex, and a bit overwhelming to look through directly.  This
35e1051a39Sopenharmony_cimodule makes querying this structure simpler, through diverse methods.
36e1051a39Sopenharmony_ci
37e1051a39Sopenharmony_ci=head2 Constructor
38e1051a39Sopenharmony_ci
39e1051a39Sopenharmony_ci=over 4
40e1051a39Sopenharmony_ci
41e1051a39Sopenharmony_ci=item B<new> I<%options>
42e1051a39Sopenharmony_ci
43e1051a39Sopenharmony_ciCreates an instance of the B<OpenSSL::Config::Query> class.  It takes options
44e1051a39Sopenharmony_ciin keyed pair form, i.e. a series of C<< key => value >> pairs.  Available
45e1051a39Sopenharmony_cioptions are:
46e1051a39Sopenharmony_ci
47e1051a39Sopenharmony_ci=over 4
48e1051a39Sopenharmony_ci
49e1051a39Sopenharmony_ci=item B<info> =E<gt> I<HASHREF>
50e1051a39Sopenharmony_ci
51e1051a39Sopenharmony_ciA reference to a unified information hash table, most commonly known as
52e1051a39Sopenharmony_ci%unified_info.
53e1051a39Sopenharmony_ci
54e1051a39Sopenharmony_ci=item B<config> =E<gt> I<HASHREF>
55e1051a39Sopenharmony_ci
56e1051a39Sopenharmony_ciA reference to a config information hash table, most commonly known as
57e1051a39Sopenharmony_ci%config.
58e1051a39Sopenharmony_ci
59e1051a39Sopenharmony_ci=back
60e1051a39Sopenharmony_ci
61e1051a39Sopenharmony_ciExample:
62e1051a39Sopenharmony_ci
63e1051a39Sopenharmony_ci    my $info = OpenSSL::Config::Info->new(info => \%unified_info);
64e1051a39Sopenharmony_ci
65e1051a39Sopenharmony_ci=back
66e1051a39Sopenharmony_ci
67e1051a39Sopenharmony_ci=cut
68e1051a39Sopenharmony_ci
69e1051a39Sopenharmony_cisub new {
70e1051a39Sopenharmony_ci    my $class = shift;
71e1051a39Sopenharmony_ci    my %opts = @_;
72e1051a39Sopenharmony_ci
73e1051a39Sopenharmony_ci    my @messages = _check_accepted_options(\%opts,
74e1051a39Sopenharmony_ci                                           info => 'HASH',
75e1051a39Sopenharmony_ci                                           config => 'HASH');
76e1051a39Sopenharmony_ci    croak $messages[0] if @messages;
77e1051a39Sopenharmony_ci
78e1051a39Sopenharmony_ci    # We make a shallow copy of the input structure.  We might make
79e1051a39Sopenharmony_ci    # a different choice in the future...
80e1051a39Sopenharmony_ci    my $instance = { info => $opts{info} // {},
81e1051a39Sopenharmony_ci                     config => $opts{config} // {} };
82e1051a39Sopenharmony_ci    bless $instance, $class;
83e1051a39Sopenharmony_ci
84e1051a39Sopenharmony_ci    return $instance;
85e1051a39Sopenharmony_ci}
86e1051a39Sopenharmony_ci
87e1051a39Sopenharmony_ci=head2 Query methods
88e1051a39Sopenharmony_ci
89e1051a39Sopenharmony_ci=over 4
90e1051a39Sopenharmony_ci
91e1051a39Sopenharmony_ci=item B<get_sources> I<LIST>
92e1051a39Sopenharmony_ci
93e1051a39Sopenharmony_ciLIST is expected to be the collection of names of end products, such as
94e1051a39Sopenharmony_ciprograms, modules, libraries.
95e1051a39Sopenharmony_ci
96e1051a39Sopenharmony_ciThe returned result is a hash table reference, with each key being one of
97e1051a39Sopenharmony_cithese end product names, and its value being a reference to an array of
98e1051a39Sopenharmony_cisource file names that constitutes everything that will or may become part
99e1051a39Sopenharmony_ciof that end product.
100e1051a39Sopenharmony_ci
101e1051a39Sopenharmony_ci=cut
102e1051a39Sopenharmony_ci
103e1051a39Sopenharmony_cisub get_sources {
104e1051a39Sopenharmony_ci    my $self = shift;
105e1051a39Sopenharmony_ci
106e1051a39Sopenharmony_ci    my $result = {};
107e1051a39Sopenharmony_ci    foreach (@_) {
108e1051a39Sopenharmony_ci        my @sources = @{$self->{info}->{sources}->{$_} // []};
109e1051a39Sopenharmony_ci        my @staticlibs =
110e1051a39Sopenharmony_ci            grep { $_ =~ m|\.a$| } @{$self->{info}->{depends}->{$_} // []};
111e1051a39Sopenharmony_ci
112e1051a39Sopenharmony_ci        my %parts = ( %{$self->get_sources(@sources)},
113e1051a39Sopenharmony_ci                      %{$self->get_sources(@staticlibs)} );
114e1051a39Sopenharmony_ci        my @parts = map { @{$_} } values %parts;
115e1051a39Sopenharmony_ci
116e1051a39Sopenharmony_ci        my @generator =
117e1051a39Sopenharmony_ci            ( ( $self->{info}->{generate}->{$_} // [] ) -> [0] // () );
118e1051a39Sopenharmony_ci        my %generator_parts = %{$self->get_sources(@generator)};
119e1051a39Sopenharmony_ci        # if there are any generator parts, we ignore it, because that means
120e1051a39Sopenharmony_ci        # it's a compiled program and thus NOT part of the source that's
121e1051a39Sopenharmony_ci        # queried.
122e1051a39Sopenharmony_ci        @generator = () if %generator_parts;
123e1051a39Sopenharmony_ci
124e1051a39Sopenharmony_ci        my @partial_result =
125e1051a39Sopenharmony_ci            ( ( map { @{$_} } values %parts ),
126e1051a39Sopenharmony_ci              ( grep { !defined($parts{$_}) } @sources, @generator ) );
127e1051a39Sopenharmony_ci
128e1051a39Sopenharmony_ci        # Push conditionally, to avoid creating $result->{$_} with an empty
129e1051a39Sopenharmony_ci        # value
130e1051a39Sopenharmony_ci        push @{$result->{$_}}, @partial_result if @partial_result;
131e1051a39Sopenharmony_ci    }
132e1051a39Sopenharmony_ci
133e1051a39Sopenharmony_ci    return $result;
134e1051a39Sopenharmony_ci}
135e1051a39Sopenharmony_ci
136e1051a39Sopenharmony_ci=item B<get_config> I<LIST>
137e1051a39Sopenharmony_ci
138e1051a39Sopenharmony_ciLIST is expected to be the collection of names of configuration data, such
139e1051a39Sopenharmony_cias build_infos, sourcedir, ...
140e1051a39Sopenharmony_ci
141e1051a39Sopenharmony_ciThe returned result is a hash table reference, with each key being one of
142e1051a39Sopenharmony_cithese configuration data names, and its value being a reference to the value
143e1051a39Sopenharmony_cicorresponding to that name.
144e1051a39Sopenharmony_ci
145e1051a39Sopenharmony_ci=cut
146e1051a39Sopenharmony_ci
147e1051a39Sopenharmony_cisub get_config {
148e1051a39Sopenharmony_ci    my $self = shift;
149e1051a39Sopenharmony_ci
150e1051a39Sopenharmony_ci    return { map { $_ => $self->{config}->{$_} } @_ };
151e1051a39Sopenharmony_ci}
152e1051a39Sopenharmony_ci
153e1051a39Sopenharmony_ci########
154e1051a39Sopenharmony_ci#
155e1051a39Sopenharmony_ci#  Helper functions
156e1051a39Sopenharmony_ci#
157e1051a39Sopenharmony_ci
158e1051a39Sopenharmony_cisub _check_accepted_options {
159e1051a39Sopenharmony_ci    my $opts = shift;           # HASH reference (hopefully)
160e1051a39Sopenharmony_ci    my %conds = @_;             # key => type
161e1051a39Sopenharmony_ci
162e1051a39Sopenharmony_ci    my @messages;
163e1051a39Sopenharmony_ci    my %optnames = map { $_ => 1 } keys %$opts;
164e1051a39Sopenharmony_ci    foreach (keys %conds) {
165e1051a39Sopenharmony_ci        delete $optnames{$_};
166e1051a39Sopenharmony_ci    }
167e1051a39Sopenharmony_ci    push @messages, "Unknown options: " . join(', ', sort keys %optnames)
168e1051a39Sopenharmony_ci        if keys %optnames;
169e1051a39Sopenharmony_ci    foreach (sort keys %conds) {
170e1051a39Sopenharmony_ci        push @messages, "'$_' value not a $conds{$_} reference"
171e1051a39Sopenharmony_ci            if (defined $conds{$_} && defined $opts->{$_}
172e1051a39Sopenharmony_ci                && ref $opts->{$_} ne $conds{$_});
173e1051a39Sopenharmony_ci    }
174e1051a39Sopenharmony_ci    return @messages;
175e1051a39Sopenharmony_ci}
176e1051a39Sopenharmony_ci
177e1051a39Sopenharmony_ci1;
178