gentemplate.pm 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556
  1. package gentemplate;
  2. use strict;
  3. use warnings;
  4. use Carp;
  5. use Exporter;
  6. use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  7. @ISA = qw(Exporter);
  8. @EXPORT = qw(gentemplate);
  9. use File::Basename;
  10. sub gentemplate {
  11. my %opts = @_;
  12. my $generator = OpenSSL::GenTemplate->new(%opts);
  13. # Build mandatory header file generators
  14. foreach (@{$generator->{info}->{depends}->{""}}) { $generator->dogenerate($_); }
  15. # Build all known targets, libraries, modules, programs and scripts.
  16. # Everything else will be handled as a consequence.
  17. foreach (@{$generator->{info}->{targets}}) { $generator->dotarget($_); }
  18. foreach (@{$generator->{info}->{libraries}}) { $generator->dolib($_); }
  19. foreach (@{$generator->{info}->{modules}}) { $generator->domodule($_); }
  20. foreach (@{$generator->{info}->{programs}}) { $generator->dobin($_); }
  21. foreach (@{$generator->{info}->{scripts}}) { $generator->doscript($_); }
  22. foreach (sort keys %{$generator->{info}->{htmldocs}}) { $generator->dodocs('html', $_); }
  23. foreach (sort keys %{$generator->{info}->{mandocs}}) { $generator->dodocs('man', $_); }
  24. foreach (sort keys %{$generator->{info}->{dirinfo}}) { $generator->dodir($_); }
  25. }
  26. package OpenSSL::GenTemplate;
  27. use OpenSSL::Util;
  28. sub new {
  29. my $class = shift;
  30. my %opts = @_;
  31. my $data = {
  32. output => $opts{output},
  33. config => $opts{config} // {},
  34. disabled => $opts{disabled} // {},
  35. info => $opts{unified_info} // {},
  36. };
  37. return bless $data, $class;
  38. };
  39. sub emit {
  40. my $self = shift;
  41. my $name = shift;
  42. my %opts = @_;
  43. my $fh = $self->{output};
  44. die "No name?" unless $name;
  45. print $fh "{-\n ", $name, '(', dump_data(\%opts), ');', " \n-}"
  46. unless defined $opts{attrs}->{skip};
  47. }
  48. my $debug_resolvedepends = $ENV{BUILDFILE_DEBUG_DEPENDS};
  49. my $debug_rules = $ENV{BUILDFILE_DEBUG_RULES};
  50. # A cache of objects for which a recipe has already been generated
  51. our %cache;
  52. # collectdepends, expanddepends and reducedepends work together to make
  53. # sure there are no duplicate or weak dependencies and that they are in
  54. # the right order. This is used to sort the list of libraries that a
  55. # build depends on.
  56. sub extensionlesslib {
  57. my @result = map { $_ =~ /(\.a)?$/; $` } @_;
  58. return @result if wantarray;
  59. return $result[0];
  60. }
  61. # collectdepends dives into the tree of dependencies and returns
  62. # a list of all the non-weak ones.
  63. sub collectdepends {
  64. my $self = shift;
  65. return () unless @_;
  66. my $thing = shift;
  67. my $extensionlessthing = extensionlesslib($thing);
  68. my @listsofar = @_; # to check if we're looping
  69. my @list = @{ $self->{info}->{depends}->{$thing} //
  70. $self->{info}->{depends}->{$extensionlessthing}
  71. // [] };
  72. my @newlist = ();
  73. print STDERR "DEBUG[collectdepends] $thing > ", join(' ', @listsofar), "\n"
  74. if $debug_resolvedepends;
  75. foreach my $item (@list) {
  76. my $extensionlessitem = extensionlesslib($item);
  77. # It's time to break off when the dependency list starts looping
  78. next if grep { extensionlesslib($_) eq $extensionlessitem } @listsofar;
  79. # Don't add anything here if the dependency is weak
  80. next if defined $self->{info}->{attributes}->{depends}->{$thing}->{$item}->{'weak'};
  81. my @resolved = $self->collectdepends($item, @listsofar, $item);
  82. push @newlist, $item, @resolved;
  83. }
  84. print STDERR "DEBUG[collectdepends] $thing < ", join(' ', @newlist), "\n"
  85. if $debug_resolvedepends;
  86. @newlist;
  87. }
  88. # expanddepends goes through a list of stuff, checks if they have any
  89. # dependencies, and adds them at the end of the current position if
  90. # they aren't already present later on.
  91. sub expanddepends {
  92. my $self = shift;
  93. my @after = ( @_ );
  94. print STDERR "DEBUG[expanddepends]> ", join(' ', @after), "\n"
  95. if $debug_resolvedepends;
  96. my @before = ();
  97. while (@after) {
  98. my $item = shift @after;
  99. print STDERR "DEBUG[expanddepends]\\ ", join(' ', @before), "\n"
  100. if $debug_resolvedepends;
  101. print STDERR "DEBUG[expanddepends] - ", $item, "\n"
  102. if $debug_resolvedepends;
  103. my @middle = (
  104. $item,
  105. map {
  106. my $x = $_;
  107. my $extlessx = extensionlesslib($x);
  108. if (grep { $extlessx eq extensionlesslib($_) } @before
  109. and
  110. !grep { $extlessx eq extensionlesslib($_) } @after) {
  111. print STDERR "DEBUG[expanddepends] + ", $x, "\n"
  112. if $debug_resolvedepends;
  113. ( $x )
  114. } else {
  115. print STDERR "DEBUG[expanddepends] ! ", $x, "\n"
  116. if $debug_resolvedepends;
  117. ()
  118. }
  119. } @{$self->{info}->{depends}->{$item} // []}
  120. );
  121. print STDERR "DEBUG[expanddepends] = ", join(' ', @middle), "\n"
  122. if $debug_resolvedepends;
  123. print STDERR "DEBUG[expanddepends]/ ", join(' ', @after), "\n"
  124. if $debug_resolvedepends;
  125. push @before, @middle;
  126. }
  127. print STDERR "DEBUG[expanddepends]< ", join(' ', @before), "\n"
  128. if $debug_resolvedepends;
  129. @before;
  130. }
  131. # reducedepends looks through a list, and checks if each item is
  132. # repeated later on. If it is, the earlier copy is dropped.
  133. sub reducedepends {
  134. my @list = @_;
  135. print STDERR "DEBUG[reducedepends]> ", join(' ', @list), "\n"
  136. if $debug_resolvedepends;
  137. my @newlist = ();
  138. my %replace = ();
  139. while (@list) {
  140. my $item = shift @list;
  141. my $extensionlessitem = extensionlesslib($item);
  142. if (grep { $extensionlessitem eq extensionlesslib($_) } @list) {
  143. if ($item ne $extensionlessitem) {
  144. # If this instance of the library is explicitly static, we
  145. # prefer that to any shared library name, since it must have
  146. # been done on purpose.
  147. $replace{$extensionlessitem} = $item;
  148. }
  149. } else {
  150. push @newlist, $item;
  151. }
  152. }
  153. @newlist = map { $replace{$_} // $_; } @newlist;
  154. print STDERR "DEBUG[reducedepends]< ", join(' ', @newlist), "\n"
  155. if $debug_resolvedepends;
  156. @newlist;
  157. }
  158. # Do it all
  159. # This takes multiple inputs and combine them into a single list of
  160. # interdependent things. The returned value will include all the input.
  161. # Callers are responsible for taking away the things they are building.
  162. sub resolvedepends {
  163. my $self = shift;
  164. print STDERR "DEBUG[resolvedepends] START (", join(', ', @_), ")\n"
  165. if $debug_resolvedepends;
  166. my @all =
  167. reducedepends($self->expanddepends(map { ( $_, $self->collectdepends($_) ) } @_));
  168. print STDERR "DEBUG[resolvedepends] END (", join(', ', @_), ") : ",
  169. join(',', map { "\n $_" } @all), "\n"
  170. if $debug_resolvedepends;
  171. @all;
  172. }
  173. # dogenerate is responsible for producing all the recipes that build
  174. # generated source files. It recurses in case a dependency is also a
  175. # generated source file.
  176. sub dogenerate {
  177. my $self = shift;
  178. my $src = shift;
  179. # Safety measure
  180. return "" unless defined $self->{info}->{generate}->{$src};
  181. return "" if $cache{$src};
  182. my $obj = shift;
  183. my $bin = shift;
  184. my %opts = @_;
  185. if ($self->{info}->{generate}->{$src}) {
  186. die "$src is generated by Configure, should not appear in build file\n"
  187. if ref $self->{info}->{generate}->{$src} eq "";
  188. my $script = $self->{info}->{generate}->{$src}->[0];
  189. my %attrs = %{$self->{info}->{attributes}->{generate}->{$src} // {}};
  190. $self->emit('generatesrc',
  191. src => $src,
  192. product => $bin,
  193. generator => $self->{info}->{generate}->{$src},
  194. generator_incs => $self->{info}->{includes}->{$script} // [],
  195. generator_deps => $self->{info}->{depends}->{$script} // [],
  196. deps => $self->{info}->{depends}->{$src} // [],
  197. incs => [ defined $obj ? @{$self->{info}->{includes}->{$obj} // []} : (),
  198. defined $bin ? @{$self->{info}->{includes}->{$bin} // []} : () ],
  199. defs => [ defined $obj ? @{$self->{info}->{defines}->{$obj} // []} : (),
  200. defined $bin ? @{$self->{info}->{defines}->{$bin} // []} : () ],
  201. attrs => { %attrs },
  202. %opts);
  203. foreach (@{$self->{info}->{depends}->{$src} // []}) {
  204. $self->dogenerate($_, $obj, $bin, %opts);
  205. }
  206. # The generator itself may be is generated
  207. if ($self->{info}->{generate}->{$script}) {
  208. $self->dogenerate($script, $obj, $bin, %opts);
  209. }
  210. }
  211. $cache{$src} = 1;
  212. }
  213. sub dotarget {
  214. my $self = shift;
  215. my $target = shift;
  216. return "" if $cache{$target};
  217. $self->emit('generatetarget',
  218. target => $target,
  219. deps => $self->{info}->{depends}->{$target} // []);
  220. foreach (@{$self->{info}->{depends}->{$target} // []}) {
  221. $self->dogenerate($_);
  222. }
  223. $cache{$target} = 1;
  224. }
  225. # doobj is responsible for producing all the recipes that build
  226. # object files as well as dependency files.
  227. sub doobj {
  228. my $self = shift;
  229. my $obj = shift;
  230. return "" if $cache{$obj};
  231. my $bin = shift;
  232. my %opts = @_;
  233. if (@{$self->{info}->{sources}->{$obj} // []}) {
  234. my @srcs = @{$self->{info}->{sources}->{$obj}};
  235. my @deps = @{$self->{info}->{depends}->{$obj} // []};
  236. my @incs = ( @{$self->{info}->{includes}->{$obj} // []},
  237. @{$self->{info}->{includes}->{$bin} // []} );
  238. my @defs = ( @{$self->{info}->{defines}->{$obj} // []},
  239. @{$self->{info}->{defines}->{$bin} // []} );
  240. print STDERR "DEBUG[doobj] \@srcs for $obj ($bin) : ",
  241. join(",", map { "\n $_" } @srcs), "\n"
  242. if $debug_rules;
  243. print STDERR "DEBUG[doobj] \@deps for $obj ($bin) : ",
  244. join(",", map { "\n $_" } @deps), "\n"
  245. if $debug_rules;
  246. print STDERR "DEBUG[doobj] \@incs for $obj ($bin) : ",
  247. join(",", map { "\n $_" } @incs), "\n"
  248. if $debug_rules;
  249. print STDERR "DEBUG[doobj] \@defs for $obj ($bin) : ",
  250. join(",", map { "\n $_" } @defs), "\n"
  251. if $debug_rules;
  252. print STDERR "DEBUG[doobj] \%opts for $obj ($bin) : ", ,
  253. join(",", map { "\n $_ = $opts{$_}" } sort keys %opts), "\n"
  254. if $debug_rules;
  255. $self->emit('src2obj',
  256. obj => $obj, product => $bin,
  257. srcs => [ @srcs ], deps => [ @deps ],
  258. incs => [ @incs ], defs => [ @defs ],
  259. %opts);
  260. foreach ((@{$self->{info}->{sources}->{$obj}},
  261. @{$self->{info}->{depends}->{$obj} // []})) {
  262. $self->dogenerate($_, $obj, $bin, %opts);
  263. }
  264. }
  265. $cache{$obj} = 1;
  266. }
  267. # Helper functions to grab all applicable intermediary files.
  268. # This is particularly useful when a library is given as source
  269. # rather than a dependency. In that case, we consider it to be a
  270. # container with object file references, or possibly references
  271. # to further libraries to pilfer in the same way.
  272. sub getsrclibs {
  273. my $self = shift;
  274. my $section = shift;
  275. # For all input, see if it sources static libraries. If it does,
  276. # return them together with the result of a recursive call.
  277. map { ( $_, getsrclibs($section, $_) ) }
  278. grep { $_ =~ m|\.a$| }
  279. map { @{$self->{info}->{$section}->{$_} // []} }
  280. @_;
  281. }
  282. sub getlibobjs {
  283. my $self = shift;
  284. my $section = shift;
  285. # For all input, see if it's an intermediary file (library or object).
  286. # If it is, collect the result of a recursive call, or if that returns
  287. # an empty list, the element itself. Return the result.
  288. map {
  289. my @x = $self->getlibobjs($section, @{$self->{info}->{$section}->{$_}});
  290. @x ? @x : ( $_ );
  291. }
  292. grep { defined $self->{info}->{$section}->{$_} }
  293. @_;
  294. }
  295. # dolib is responsible for building libraries. It will call
  296. # obj2shlib if shared libraries are produced, and obj2lib in all
  297. # cases. It also makes sure all object files for the library are
  298. # built.
  299. sub dolib {
  300. my $self = shift;
  301. my $lib = shift;
  302. return "" if $cache{$lib};
  303. my %attrs = %{$self->{info}->{attributes}->{libraries}->{$lib} // {}};
  304. my @deps = ( $self->resolvedepends(getsrclibs('sources', $lib)) );
  305. # We support two types of objs, those who are specific to this library
  306. # (they end up in @objs) and those that we get indirectly, via other
  307. # libraries (they end up in @foreign_objs). We get the latter any time
  308. # someone has done something like this in build.info:
  309. # SOURCE[libfoo.a]=libbar.a
  310. # The indirect object files must be kept in a separate array so they
  311. # don't get rebuilt unnecessarily (and with incorrect auxiliary
  312. # information).
  313. #
  314. # Object files can't be collected commonly for shared and static
  315. # libraries, because we contain their respective object files in
  316. # {shared_sources} and {sources}, and because the implications are
  317. # slightly different for each library form.
  318. #
  319. # We grab all these "foreign" object files recursively with getlibobjs().
  320. unless ($self->{disabled}->{shared} || $lib =~ /\.a$/) {
  321. # If this library sources other static libraries and those
  322. # libraries are marked {noinst}, there's no need to include
  323. # all of their object files. Instead, we treat those static
  324. # libraries as dependents alongside any other library this
  325. # one depends on, and let symbol resolution do its job.
  326. my @sourced_libs = ();
  327. my @objs = ();
  328. my @foreign_objs = ();
  329. my @deps = ();
  330. foreach (@{$self->{info}->{shared_sources}->{$lib} // []}) {
  331. if ($_ !~ m|\.a$|) {
  332. push @objs, $_;
  333. } elsif ($self->{info}->{attributes}->{libraries}->{$_}->{noinst}) {
  334. push @deps, $_;
  335. } else {
  336. push @deps, $self->getsrclibs('sources', $_);
  337. push @foreign_objs, $self->getlibobjs('sources', $_);
  338. }
  339. }
  340. @deps = ( grep { $_ ne $lib } $self->resolvedepends($lib, @deps) );
  341. print STDERR "DEBUG[dolib:shlib] \%attrs for $lib : ", ,
  342. join(",", map { "\n $_ = $attrs{$_}" } sort keys %attrs), "\n"
  343. if %attrs && $debug_rules;
  344. print STDERR "DEBUG[dolib:shlib] \@deps for $lib : ",
  345. join(",", map { "\n $_" } @deps), "\n"
  346. if @deps && $debug_rules;
  347. print STDERR "DEBUG[dolib:shlib] \@objs for $lib : ",
  348. join(",", map { "\n $_" } @objs), "\n"
  349. if @objs && $debug_rules;
  350. print STDERR "DEBUG[dolib:shlib] \@foreign_objs for $lib : ",
  351. join(",", map { "\n $_" } @foreign_objs), "\n"
  352. if @foreign_objs && $debug_rules;
  353. $self->emit('obj2shlib',
  354. lib => $lib,
  355. attrs => { %attrs },
  356. objs => [ @objs, @foreign_objs ],
  357. deps => [ @deps ]);
  358. foreach (@objs) {
  359. # If this is somehow a compiled object, take care of it that way
  360. # Otherwise, it might simply be generated
  361. if (defined $self->{info}->{sources}->{$_}) {
  362. if($_ =~ /\.a$/) {
  363. $self->dolib($_);
  364. } else {
  365. $self->doobj($_, $lib, intent => "shlib", attrs => { %attrs });
  366. }
  367. } else {
  368. $self->dogenerate($_, undef, undef, intent => "lib");
  369. }
  370. }
  371. }
  372. {
  373. # When putting static libraries together, we cannot rely on any
  374. # symbol resolution, so for all static libraries used as source for
  375. # this one, as well as other libraries they depend on, we simply
  376. # grab all their object files unconditionally,
  377. # Symbol resolution will happen when any program, module or shared
  378. # library is linked with this one.
  379. my @objs = ();
  380. my @sourcedeps = ();
  381. my @foreign_objs = ();
  382. foreach (@{$self->{info}->{sources}->{$lib}}) {
  383. if ($_ !~ m|\.a$|) {
  384. push @objs, $_;
  385. } else {
  386. push @sourcedeps, $_;
  387. }
  388. }
  389. @sourcedeps = ( grep { $_ ne $lib } $self->resolvedepends(@sourcedeps) );
  390. print STDERR "DEBUG[dolib:lib] : \@sourcedeps for $_ : ",
  391. join(",", map { "\n $_" } @sourcedeps), "\n"
  392. if @sourcedeps && $debug_rules;
  393. @foreign_objs = $self->getlibobjs('sources', @sourcedeps);
  394. print STDERR "DEBUG[dolib:lib] \%attrs for $lib : ", ,
  395. join(",", map { "\n $_ = $attrs{$_}" } sort keys %attrs), "\n"
  396. if %attrs && $debug_rules;
  397. print STDERR "DEBUG[dolib:lib] \@objs for $lib : ",
  398. join(",", map { "\n $_" } @objs), "\n"
  399. if @objs && $debug_rules;
  400. print STDERR "DEBUG[dolib:lib] \@foreign_objs for $lib : ",
  401. join(",", map { "\n $_" } @foreign_objs), "\n"
  402. if @foreign_objs && $debug_rules;
  403. $self->emit('obj2lib',
  404. lib => $lib, attrs => { %attrs },
  405. objs => [ @objs, @foreign_objs ]);
  406. foreach (@objs) {
  407. $self->doobj($_, $lib, intent => "lib", attrs => { %attrs });
  408. }
  409. }
  410. $cache{$lib} = 1;
  411. }
  412. # domodule is responsible for building modules. It will call
  413. # obj2dso, and also makes sure all object files for the library
  414. # are built.
  415. sub domodule {
  416. my $self = shift;
  417. my $module = shift;
  418. return "" if $cache{$module};
  419. my %attrs = %{$self->{info}->{attributes}->{modules}->{$module} // {}};
  420. my @objs = @{$self->{info}->{sources}->{$module}};
  421. my @deps = ( grep { $_ ne $module }
  422. $self->resolvedepends($module) );
  423. print STDERR "DEBUG[domodule] \%attrs for $module :",
  424. join(",", map { "\n $_ = $attrs{$_}" } sort keys %attrs), "\n"
  425. if $debug_rules;
  426. print STDERR "DEBUG[domodule] \@objs for $module : ",
  427. join(",", map { "\n $_" } @objs), "\n"
  428. if $debug_rules;
  429. print STDERR "DEBUG[domodule] \@deps for $module : ",
  430. join(",", map { "\n $_" } @deps), "\n"
  431. if $debug_rules;
  432. $self->emit('obj2dso',
  433. module => $module,
  434. attrs => { %attrs },
  435. objs => [ @objs ],
  436. deps => [ @deps ]);
  437. foreach (@{$self->{info}->{sources}->{$module}}) {
  438. # If this is somehow a compiled object, take care of it that way
  439. # Otherwise, it might simply be generated
  440. if (defined $self->{info}->{sources}->{$_}) {
  441. $self->doobj($_, $module, intent => "dso", attrs => { %attrs });
  442. } else {
  443. $self->dogenerate($_, undef, $module, intent => "dso");
  444. }
  445. }
  446. $cache{$module} = 1;
  447. }
  448. # dobin is responsible for building programs. It will call obj2bin,
  449. # and also makes sure all object files for the library are built.
  450. sub dobin {
  451. my $self = shift;
  452. my $bin = shift;
  453. return "" if $cache{$bin};
  454. my %attrs = %{$self->{info}->{attributes}->{programs}->{$bin} // {}};
  455. my @objs = @{$self->{info}->{sources}->{$bin}};
  456. my @deps = ( grep { $_ ne $bin } $self->resolvedepends($bin) );
  457. print STDERR "DEBUG[dobin] \%attrs for $bin : ",
  458. join(",", map { "\n $_ = $attrs{$_}" } sort keys %attrs), "\n"
  459. if %attrs && $debug_rules;
  460. print STDERR "DEBUG[dobin] \@objs for $bin : ",
  461. join(",", map { "\n $_" } @objs), "\n"
  462. if @objs && $debug_rules;
  463. print STDERR "DEBUG[dobin] \@deps for $bin : ",
  464. join(",", map { "\n $_" } @deps), "\n"
  465. if @deps && $debug_rules;
  466. $self->emit('obj2bin',
  467. bin => $bin,
  468. attrs => { %attrs },
  469. objs => [ @objs ],
  470. deps => [ @deps ]);
  471. foreach (@objs) {
  472. $self->doobj($_, $bin, intent => "bin", attrs => { %attrs });
  473. }
  474. $cache{$bin} = 1;
  475. }
  476. # doscript is responsible for building scripts from templates. It will
  477. # call in2script.
  478. sub doscript {
  479. my $self = shift;
  480. my $script = shift;
  481. return "" if $cache{$script};
  482. $self->emit('in2script',
  483. script => $script,
  484. attrs => $self->{info}->{attributes}->{scripts}->{$script} // {},
  485. sources => $self->{info}->{sources}->{$script});
  486. $cache{$script} = 1;
  487. }
  488. sub dodir {
  489. my $self = shift;
  490. my $dir = shift;
  491. return "" if !exists(&generatedir) or $cache{$dir};
  492. $self->emit('generatedir',
  493. dir => $dir,
  494. deps => $self->{info}->{dirinfo}->{$dir}->{deps} // [],
  495. %{$self->{info}->{dirinfo}->{$_}->{products}});
  496. $cache{$dir} = 1;
  497. }
  498. # dodocs is responsible for building documentation from .pods.
  499. # It will call generatesrc.
  500. sub dodocs {
  501. my $self = shift;
  502. my $type = shift;
  503. my $section = shift;
  504. foreach my $doc (@{$self->{info}->{"${type}docs"}->{$section}}) {
  505. next if $cache{$doc};
  506. $self->emit('generatesrc',
  507. src => $doc,
  508. generator => $self->{info}->{generate}->{$doc});
  509. foreach ((@{$self->{info}->{depends}->{$doc} // []})) {
  510. $self->dogenerate($_, undef, undef);
  511. }
  512. $cache{$doc} = 1;
  513. }
  514. }
  515. 1;