aboutsummaryrefslogtreecommitdiffstats
path: root/util/mkpod2man.pl
blob: 778dc2b0918083fb8d52c0edff848713f5fba6dd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#! /usr/bin/env perl
# Copyright 2026 The OpenSSL Project Authors. All Rights Reserved.
#
# Licensed under the Apache License 2.0 (the "License").  You may not use
# this file except in compliance with the License.  You can obtain a copy
# in the file LICENSE in the source distribution or at
# https://www.openssl.org/source/license.html

use strict;
use warnings;

use Getopt::Std;
use File::Basename;
use Pod::Man;

# Format man pages without loading Pod::Man once per page.  pod2man is a
# perl program that compiles Pod::Man before it reads a single pod, and
# OpenSSL has over nine hundred pages, so running it once per page spends
# most of that time compiling the same module again and again.  Here the
# module is loaded once and the pages are then divided between a few
# forked workers, which inherit it already compiled.

# Options.  The whole manual is formatted in one run, so the section of
# each page is taken from the directory its pod lives in rather than
# given on the command line: doc/man3/BIO_s_mem.pod is section 3 and
# becomes OUTDIR/man3/BIO_s_mem.3.
our ($opt_o);    # -o OUTDIR, the directory holding the man1..man7 dirs
our ($opt_m);    # -m MANSUFFIX, appended to the section inside the page
our ($opt_d);    # -d DATE
our ($opt_r);    # -r RELEASE
our ($opt_j);    # -j JOBS

getopts('o:m:d:r:j:');
die "-o flag missing" unless defined $opt_o;
$opt_m = '' unless defined $opt_m;
$opt_d = '' unless defined $opt_d;
$opt_r = '' unless defined $opt_r;

sub cpu_count
{
    my $cpus = $ENV{"NUMBER_OF_PROCESSORS"};    # Windows sets this.

    if (!defined($cpus) && $^O =~ /linux/) {
        my $tmp = qx(nproc 2>/dev/null);

        $cpus = $tmp if $? == 0 && $tmp > 0;
    }
    if (!defined($cpus) && -r "/proc/cpuinfo") {
        my $tmp = qx(grep -c ^processor /proc/cpuinfo 2>/dev/null);

        $cpus = $tmp if $? == 0 && $tmp > 0;
    }
    if (!defined($cpus)) {
        my $tmp = qx(sysctl -n hw.ncpu 2>/dev/null);    # BSDs, macOS

        $cpus = $tmp if $? == 0 && $tmp > 0;
    }

    return defined($cpus) && $cpus > 0 ? int($cpus) : 1;
}

# Turn one pod into one man page.
sub format_page
{
    my $pod = shift;
    my $name = basename($pod, ".pod");
    my $dir = basename(dirname($pod));
    my ($section) = $dir =~ m|^man(\d)$|;

    die "Can't tell the section of $pod from its directory\n"
        unless defined $section;

    my $out = "$opt_o/man$section/$name.$section";

    # The page is current when it is newer than the pod it comes from.
    return if -e $out && -M $out < -M $pod;

    unlink $out;

    Pod::Man->new(name => uc $name,
                  section => "$section$opt_m",
                  center => "OpenSSL",
                  date => $opt_d,
                  release => $opt_r)
        ->parse_from_file($pod, $out);

    chmod 0444, $out
        or die "Can't set the mode of $out, $!\n";
}

my @pods = @ARGV;

exit 0 unless @pods;

# Only ask how many processors there are when the answer can matter;
# the count is found by running a command, and the per-page rules call
# this script with a single pod.
my $jobs = @pods > 1
    ? (defined $opt_j && $opt_j > 0 ? int($opt_j) : cpu_count())
    : 1;

$jobs = scalar @pods if $jobs > @pods;

# One page, or no reason to fork: do the work here.  This is also the path
# taken where fork() is emulated and would cost more than it saves.
if ($jobs <= 1) {
    format_page($_) foreach @pods;
    exit 0;
}

my @pids;

foreach my $worker (0 .. $jobs - 1) {
    my $pid = fork();

    die "Can't fork, $!\n" unless defined $pid;
    if (!$pid) {
        # Deal every $jobs'th page to this worker.  The pages differ a lot
        # in size, and dealing them out interleaves the large ones instead
        # of handing one worker a contiguous run of them.
        for (my $i = $worker; $i <= $#pods; $i += $jobs) {
            format_page($pods[$i]);
        }
        exit 0;
    }
    push @pids, $pid;
}

my $failed = 0;

foreach my $pid (@pids) {
    waitpid($pid, 0);
    $failed = 1 if $?;
}

die "Failed to format the manual pages\n" if $failed;