File Coverage

blib/lib/Config/Maker/Metaconfig.pm
Criterion Covered Total %
statement 102 117 87.1
branch 25 40 62.5
condition 3 6 50.0
subroutine 17 17 100.0
pod 0 3 0.0
total 147 183 80.3


line stmt bran cond sub pod time code
1             package Config::Maker::Metaconfig;
2              
3 9     9   70 use utf8;
  9         16  
  9         82  
4 9     9   277 use warnings;
  9         18  
  9         298  
5 9     9   54 use strict;
  9         20  
  9         311  
6              
7 9     9   53 use Carp;
  9         14  
  9         12228  
8 9     9   57 use File::Spec;
  9         19  
  9         242  
9 9     9   52 use File::Basename;
  9         17  
  9         1013  
10 9     9   13405 use File::Temp qw(tempfile);
  9         144638  
  9         868  
11              
12 9     9   88 use Config::Maker;
  9         20  
  9         693  
13 9     9   62 use Config::Maker::Type;
  9         27  
  9         206  
14 9     9   57 use Config::Maker::Config;
  9         21  
  9         275  
15 9     9   56 use Config::Maker::Driver;
  9         28  
  9         34595  
16              
17             sub type {
18 117     117 0 791 Config::Maker::Type->new(@_);
19             }
20              
21             # Top-level element "search-path"
22              
23             my $search = type(
24             name => 'search-path',
25             format => [simple => [zero_list => 'string']],
26             contexts => [opt => '/'],
27             );
28              
29             # Top-level element "output-dir"
30              
31             my $output = type(
32             name => 'output-dir',
33             format => [simple => ['string']],
34             contexts => [opt => '/'],
35             );
36              
37             # Top-level element "cache-dir"
38              
39             my $cached = type(
40             name => 'cache-dir',
41             format => [simple => ['string']],
42             contexts => [opt => '/'],
43             );
44              
45             # Top-level element "config"
46              
47             my $config = type(
48             name => 'config',
49             format => ['named_group' => ['string']],
50             contexts => [any => '/'],
51             );
52              
53             # The template element
54              
55             my $template = type(
56             name => 'template',
57             format => ['anon_group'],
58             contexts => [any => $config],
59             );
60              
61             my $src = type(
62             name => 'src',
63             format => [simple => ['string']],
64             contexts => [one => $template],
65             );
66              
67             my $out = type(
68             name => 'out',
69             format => [simple => ['string']],
70             contexts => [opt => $template],
71             );
72              
73             my $command = type(
74             name => 'command',
75             format => [simple => ['string']],
76             contexts => [opt => $template],
77             );
78             $template->addchecks(mand => 'out|command');
79              
80             my $cache = type(
81             name => 'cache',
82             format => [simple => ['string']],
83             contexts => [opt => $template],
84             );
85              
86             my $enc = type(
87             name => 'enc',
88             format => [simple => ['string']],
89             contexts => [opt => $template],
90             );
91              
92             # Metatypes...
93              
94             sub metatype {
95 27     27 0 61 my ($name) = @_;
96 27         147 type(
97             name => $name,
98             format => [simple => ['string']],
99             contexts => [opt => '//'],
100             );
101             }
102              
103             metatype('meta');
104             metatype('template');
105             metatype('output');
106              
107             # And now the real code...
108              
109             sub _qual($$) {
110 89     89   378 my ($file, $dir) = @_;
111 89 100       2712 return unless $file;
112 86 50       1762 if(File::Spec->file_name_is_absolute($file)) {
113 0         0 return $file;
114             } else {
115 86         900 return File::Spec->rel2abs($file, $dir);
116             }
117             }
118              
119             sub _get_cfg {
120 45     45   447 Config::Maker::Config->new(@_);
121             }
122              
123             our @unlink;
124              
125             sub do {
126 45     45 0 111198 my ($class, $metaname, $noinst, $force) = @_;
127            
128 45         609 my $meta = Config::Maker::Config->new($metaname)->{root};
129              
130 45         311 local @Config::Maker::path = @{$meta->getval('search-path', ['/etc/'])};
  45         340  
131 45         126 { local $"=', '; DBG "Search path: @Config::Maker::path"; }
  45         126  
  45         278  
132              
133 45         217 my $outdir = $meta->getval('output-dir', '/etc/');
134 45         224 DBG "Output-dir: $outdir";
135              
136 45         192 my $cachedir = $meta->getval('cache-dir', '/var/cache/configit/');
137 45         244 DBG "Cache-dir: $cachedir";
138              
139             # For each config file and each template...
140 45         255 for my $cfg ($meta->get('config')) {
141 45         276 LOG "Processing config $cfg";
142 45         276 my $conf = _get_cfg($cfg->{-value});
143 45         293 for my $tmpl ($cfg->get('template')) {
144 45         103 my ($fh, $name, $cache, $output);
145              
146             # Find output name...
147 45         190 $output = $tmpl->get('out');
148 45 100       197 if($output) {
149 42         185 $output = _qual($output, $outdir);
150 42         6682 ($fh, $name) = tempfile(
151             basename($output, qr/\..*/) . ".cmXXXXXX",
152             DIR => dirname($output));
153             } else {
154 3         9 $output = '';
155 3         15 ($fh, $name) = tempfile(
156             basename($tmpl->get1('src'), qr/\..*/) . ".cmXXXXXXXX",
157             DIR => File::Spec->tmpdir);
158             }
159 45         373160 DBG "Using $name as temporary for $tmpl output";
160 45         136 push @unlink, $name;
161              
162             # Find cache name...
163 45         219 $cache = $tmpl->get('cache');
164 45 100       172 if($cache) {
165 8         37 $cache = _qual($cache, $cachedir);
166 8         149 $fh = Config::Maker::Tee->new($fh, $cache);
167             }
168              
169             # Set up the magical elements for the config...
170 45         313 $conf->set_meta(meta => $meta);
171 45         185 $conf->set_meta(template => $tmpl);
172 45         215 $conf->set_meta(output => $output);
173             # Process the thing...
174 45         238 Config::Maker::Driver->process(
175             $tmpl->get1('src'),
176             $conf, $fh, $tmpl->get('enc'),
177             );
178              
179 43         349 $tmpl->{-data} = [$fh, $name, $cache];
180 43         42550 close $fh;
181             }
182             }
183              
184             # Now, for each template install the temporary file...
185 43 50       212 if($noinst) {
186 0         0 for my $tmpl ($meta->get('config/template')) {
187 0         0 my ($fh, $name, $cache) = @{$tmpl->{-data}};
  0         0  
188 0 0 0     0 if($cache && $fh->cmpcache) {
189 0         0 LOG "Output of ".$tmpl->get('src')." unchanged";
190 0 0       0 next unless $force;
191             }
192 0         0 @unlink = grep { $_ ne $name } @unlink;
  0         0  
193 0         0 my $dest;
194 0 0       0 if($dest = _qual($tmpl->get('out'), $outdir)) {
195 0         0 print STDOUT "Install: $name $dest\n";
196             }
197 0 0       0 if($dest = $tmpl->get('command')) {
198 0         0 print STDOUT "Invoke: $dest < $name\n";
199             }
200             }
201             } else {
202 43         313 for my $tmpl ($meta->get('config/template')) {
203 43         95 my ($fh, $name, $cache) = @{$tmpl->{-data}};
  43         278  
204 43 100 100     283 if($cache && $fh->cmpcache) {
205 5         27 LOG "Output of ".$tmpl->get('src')." unchanged";
206 5 100       51 next unless $force;
207             }
208 39         81 my $dest;
209 39 100       186 if($dest = _qual($tmpl->get('out'), $outdir)) {
210 36         4275 LOG "Installing $dest";
211 36 50       4902 rename $name, $dest
212             or croak "Failed to install $dest: $!";
213 36         134 @unlink = grep { $_ ne $name } @unlink;
  53         251  
214 36         88 $name = $dest;
215             }
216 39 100       232 if($dest = $tmpl->get('command')) {
217 5         25 LOG "Invoking $dest";
218 5         11688 my $pid = fork;
219 5 50       615 croak "Failed to fork: $!" unless defined $pid;
220 5 100       264 unless($pid) { # The child...
221 2         1147 open STDIN, '<', $name;
222 2         0 exec $dest;
223 0         0 die "Failed to exec $dest: $!";
224             }
225             # The parent...
226 3 50       49593063 waitpid($pid, 0) != -1
227             or croak "Wait failed: $!";
228 3 50       117 croak "Command failed: $?" if "$?";
229             }
230 37 100       912 if($cache) {
231 4         26 $fh->savecache;
232             }
233             }
234             }
235             # should be done...(!)
236             }
237              
238             END {
239 7     7   8193 foreach(@unlink) {
240 7 50       860 unlink $_ or warn "Unlinking `$_' failed: $!";
241             }
242             }
243              
244             1;
245              
246             __END__