#! /usr/bin/env perl

# ABSTRACT: create pkg-config metadata files
# PODNAME: mkpkgconfig

use strict;
use warnings;

use Getopt::Long::Descriptive;
use File::Basename;
use File::Spec::Functions;
use Carp;

our $VERSION = 'v2.0.0';

use constant {
    ALL_VARS       => 1,
    REQUESTED_VARS => 2,
    NEEDED_VARS    => 3,
};

our %VarsAuto = (
    exec_prefix => '${prefix}',

    bindir  => '${exec_prefix}/bin',
    sbindir => '${exec_prefix}/sbin',

    libdir     => '${exec_prefix}/lib',
    pkg_libdir => '${libdir}/${package}',

    libexecdir     => '${exec_prefix}/libexec',
    pkg_libexecdir => '${libexecdir}/${package}',

    datarootdir => '${prefix}/share',

    datadir     => '${datarootdir}',
    pkg_datadir => '${datadir}/${package}',

    sysconfdir     => '${prefix}/etc',
    pkg_sysconfdir => '${sysconfdir}/${package}',

    localstatedir     => '${prefix}/var',
    pkg_localstatedir => '${localstatedir}/${package}',

    includedir     => '${prefix}/include',
    pkg_includedir => '${includedir}/${package}',

    docdir  => '${datarootdir}/doc/${package}',
    infodir => '${datarootdir}/info',
    mandir  => '${datarootdir}/man',
);

my %KeywordOptions = (
    Name               => { required => 1 },
    Description        => { required => 1 },
    Requires           => { required => 0 },
    Libs               => { required => 0 },
    Conflicts          => { required => 0 },
    Cflags             => { required => 0 },
    URL                => { required => 0 },
);




main( @ARGV ) unless caller;

sub main {

    local @ARGV = @_;

    # save original options for output;
    my @sARGV = @ARGV;
    my $opt   = parse_opts();

    require App::mkpkgconfig::PkgConfig;

    my $conf = App::mkpkgconfig::PkgConfig->new;

    # add standard variables
    $conf->add_variable( prefix  => $opt->prefix )  if defined $opt->prefix;
    $conf->add_variable( package => $opt->package ) if defined $opt->package;
    $conf->add_variable( version => $opt->modversion );

    # add auto generated variables
    $conf->add_variables( \%VarsAuto )
      if defined $opt->auto;

    # override from user
    $conf->add_variables( $opt->var );

    # add standard keywords
    $conf->add_keyword( Name        => $opt->name );
    $conf->add_keyword( Description => $opt->description );
    $conf->add_keyword( Version     => '${version}' );
    for my $kwd ( qw(  Requires Libs Conflicts Cflags ) ) {
        my $mth = lc $kwd;
        $conf->add_keyword( $kwd => $opt->$mth )
          if defined $opt->$mth;
    }

    # override from user
    $conf->add_keywords( $opt->kwd );

    #<<< no tidy
    my @vars_needed =
      # output all variables, used or not
        $opt->usevars == ALL_VARS       ? $conf->variables
      # output only requested  + keyword dependencies
      : $opt->usevars == REQUESTED_VARS ? ( keys %{ $opt->var } , @{ $opt->auto // [] } )
      # only variables actually used by keywords
      : $opt->usevars == NEEDED_VARS    ? ()
      :                                   die( "unknown filter for variables: ", $opt->usevars, "\n" );
    #>>>

    $conf->write(
        $opt->output,
        write    => ( $opt->usevars == ALL_VARS ? 'all' : 'req' ),
        vars     => \@vars_needed,
        comments => [
            "This file was created by $0 ($VERSION) via",
            join( ' ', $0, @sARGV ) ] );
}


sub parse_opts {

    my %Map_usevars = (
        'all'       => ALL_VARS,
        'requested' => REQUESTED_VARS,
        'req'       => REQUESTED_VARS,
        'needed'    => NEEDED_VARS
    );

    my ( $opt, $usage ) = Getopt::Long::Descriptive::describe_options(
        "%o <options>",
        [ 'output|o=s', "output [stdout]" ],
        [
            'usevars|u:s',
            "which variables to output",
            {
                default   => { reverse %Map_usevars }->{ +NEEDED_VARS },
                callbacks => {
                    'valid output variables' => sub {
                        return 1 if defined $Map_usevars{ $_[0] };
                        die(
                            "$_[0] isn't one of 'all', 'req', 'requested', or 'used'\n"
                        );
                    }
                } }
        ],
        [ 'auto:s@', "generate a default set of variables" ],
        [
            'list-auto',
            "output a list of variables generated by the --auto option",
            { shortcircuit => 1 }
        ],

        [],
        ['Variables:'],
        [ 'var|variable=s%', 'define variables', { default => {} } ],
        [ 'prefix=s',        "'prefix' variable", ],
        [ 'package=s',       "'package' variable", ],
        [
            'modversion|modversion=s',
            '"version" variable and Version keyword)',
            { required => 1 }
        ],

        [],
        ['Keywords:'],

        [ 'kwd|keyword=s%', 'define keywords', { default => {} } ],

        (
            map { [
                    qq(\u$_|\l$_=s),
                    qq("\u$_" keyword),
                    { (
                            $KeywordOptions{$_}{required}
                              // 0 ? ( required => 1 ) : ()
                        ),
                    },
                ]
              }
              keys %KeywordOptions
        ),

        [],
        ['Miscellaneous::'],

        [ 'version|v', 'output version and exit', { shortcircuit => 1 } ],

        [
            'help|h',
            'output short help message and exit',
            { shortcircuit => 1 }
        ],

        [
            'manual|m',
            'output full manual page and exit',
            { shortcircuit => 1 }
        ],
    );

    print( $usage->text ), exit if $opt->help;
    print( $VERSION, "\n" ), exit if $opt->version;
    if ( $opt->manual ) {
        require Pod::Usage;
        Pod::Usage::pod2usage(
            { -exitval => 0, -verbose => 2, -output => \*STDOUT } );
    }

    if ( $opt->list_auto ) {
        require List::Util;
        my $length = List::Util::max( map { length( $_ ) } keys %VarsAuto );
        printf( "%-*s = %s\n", $length, $_, $VarsAuto{$_} )
          for sort keys %VarsAuto;
        exit;
    }

    if ( defined $opt->auto ) {
        my @auto = map { split /,/ } @{ $opt->auto };
        $opt->{auto} = \@auto;
    }

    $opt->{usevars} = $Map_usevars{ $opt->usevars };

    return $opt;
}



1;

#
# This file is part of App-mkpkgconfig
#
# This software is Copyright (c) 2020 by Smithsonian Astrophysical Observatory.
#
# This is free software, licensed under:
#
#   The GNU General Public License, Version 3, June 2007
#

__END__

=pod

=for :stopwords Diab Jerius Smithsonian Astrophysical Observatory

=head1 NAME

mkpkgconfig - create pkg-config metadata files

=head1 VERSION

version v2.0.0

=head1 SYNOPSIS

mkpkgconfig I<options>

=head1 DESCRIPTION

B<mkpkgconfig> creates a B<pkg-config> metadata (C<.pc>) file.
B<pkg-config> variables and keywords are defined on the command line,
variable dependencies are validated, and the configuration file is output.
"Standard" variables (such as C<$libdir>, C<$datadir>) may be automatically
created, and only variables which are used are output.

=head2 Variables and Keywords

B<pkg-config> distinguishes between I<variables> and I<keywords>. Values for both may include
interpolated variables, as in C<< Cflags: -I ${include} >>.

Some commonly used variables have dedicated command line options:

  --prefix     : base prefix for paths
  --package    : filesystem compatible package name
  --modversion : package version

(C<--modversion> sets the I<version> variable; the C<--version> flag will output the version of C<mkpkgconfig>).

C<--modversion> is required.  C<--prefix> and C<--package> may be
required if a keyword requires them or C<--auto> is set and
auto-generated variables require it.

Common keywords also have dedicated options:

  --Name
  --Conflicts
  --Description
  --Requires
  --Libs
  --Cflags
  --URL

The C<--Name> and C<--Description> options are required. The
C<Version> keyword is automatically set to C<< ${version} >>. It is
not possible to set it directly from the command line.

Other variables and keywords may be specified via the C<--var> and
C<--kwd> options, respectively:

  --var name=value
  --kwd name=value

which may be used more than once.

=head2 Automatically Generated Variables

C<mkpkgconfig> can automatically generate a number of "standard"
variables, such as I<bindir>, I<libdir>, etc, based upon the I<prefix>
variable.  Use the L</--list-auto> option to output a list of these
variables.

=head1 OPTIONS

=head2 General Options

=over

=item --output

Where the configuration file is to be written.  It defaults to the
standard output stream.

=item --usevars C<all>|C<requested>|C<needed>

Which variables should be output.  It defaults to C<needed>.

=over

=item C<all>

output all variables, needed or not, including automatically generated
ones if C<< --auto >> was specified;

=item C<requested>

output only requested variables (via-C<< -var variable=value >> or C<<
--auto=variable,... >>) and keyword dependencies;

=item C<needed>

output only those variables actually used by keywords

=back

=back

=head2 Variables

=over

=item C<--var> I<name>=I<value>

Set the variable named I<name> to I<value>.

=item C<--prefix> I<value>

Set the C<prefix> variable.

=item C<--package> I<value>

Set the C<package> variable

=item C<--modversion> I<value>

Set the C<version> value

=item C<--auto>

=item C<--auto> I<list of variables>

Generate a set of variables.  Use C<--list-auto> to see what is generated.

If passed a list of variable names, those will be output if C<--usevars> is set to C<requested>.

Individual variables may be overriden using C<--var>.

=item C<--list-auto>

Output a list of the automatically generated keywords and exit.

=back

=head3 Keywords

=over

=item C<--kwd> I<name>=I<value>

Set the keyword named I<name> to I<value>.

=item C<--Name>  I<value>

=item C<--name> I<value>

Set the I<Name> keyword.
This parameter is required.

=item C<--Description> I<value>

=item C<--description> I<value>

Set the I<Description> keyword.
This parameter is required.

=item C<--Requires> I<value>

=item C<--requires> I<value>

Set the I<Requires> keyword.

=item C<--Conflicts> I<value>

=item C<--conflicts>  I<value>

Set the I<Conflicts> keyword.

=item C<--Libs> I<value>

=item C<--libs>  I<value>

Set the I<Libs> keyword.

=item C<--Cflags> I<value>

=item C<--cflags>  I<value>

Set the I<Cflags> keyword.

=back

=head2 Miscellaneous

=over

=item --version

Output the version of B<mkpkgconfig> and exit.

=item --help

Output a short help message and exit.

=item --manual

Output the manual and exit.

=back

=head1 SUPPORT

=head2 Bugs

Please report any bugs or feature requests to bug-app-mkpkgconfig@rt.cpan.org  or through the web interface at: https://rt.cpan.org/Public/Dist/Display.html?Name=App-mkpkgconfig

=head2 Source

Source is available at

  https://gitlab.com/djerius/app-mkpkgconfig

and may be cloned from

  https://gitlab.com/djerius/app-mkpkgconfig.git

=head1 AUTHOR

Diab Jerius <djerius@cpan.org>

=head1 COPYRIGHT AND LICENSE

This software is Copyright (c) 2020 by Smithsonian Astrophysical Observatory.

This is free software, licensed under:

  The GNU General Public License, Version 3, June 2007

=cut
