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