当写好一个或多个模块后,可以将它构建、打包成"tar.gz",以便让别人安装或者上传到CPAN(如果愿意的话)。对于模块的使用者来说,也不用再使用 use lib 'LIB_PATH' 来找pm文件,安装后一般都会安装到 @INC 路径下,然后直接使用 use MODULE 即可。

Module::Starter

在很早以前,使用h2xz工具,但它实在太古老了。现在很多人使用 Dist::Zilla 模块来创建并安装模块。不过本文只是基础,只介绍 Module::Starter 构建和打包的方法。

$ cpan -i Module::Starter

Module::Starter模块提供了一个命令行程序module-starter,它可以用来构建模块。

例如,构建一个名为My::Number::Utilities的模块(模块可以不用存在,它会自动创建一些文件结构)

$ module-starter --module "My::Number::Utilities" --author="ma long shuai" --email="79123@qq.com"

上面第一条命令将会创建如下的目录树:

$ tree My-Number-Utilities
My-Number-Utilities
├── Changes
├── ignore.txt
├── lib
│   └── My
│       └── Number
│           └── Utilities.pm
├── Makefile.PL
├── MANIFEST
├── README
├── t
│   ├── 00-load.t
│   ├── manifest.t
│   ├── pod-coverage.t
│   └── pod.t
└── xt
    └── boilerplate.t
  • Changes:包含程序版本的变化信息
  • MANIFEST:列出发布的模块版本中必须包含的每个文件
  • Makefile.PL:是Perl程序创建的Makefile,Makefile文件中包含了如何编译和构建程序的说明。如果不是非常熟悉Makefile格式,千万不能修改,一个tab和空格的不同可能都会导致编译出错。
  • README:包含了如何构建和安装程序的文档,一般来说,程序的说明文档会嵌入在此文件中。
  • ignore.txt:是类似于git/svn版本控制系统的文件忽略模板,你可能会经常拷贝该文件到你的版本控制系统中。
  • lib/:该目录包含了实际要安装的模块。
  • t/:该目录包含了模块测试相关的文件。
  • 上面的文件可能已经包含了一些内容,例如lib/My/Number/Utilities.pm文件中已经自动包含了一些编译指示,还包含了默认的pod文档。

    $ head -n 10 My-Number-Utilities/lib/My/Number/Utilities.pm 
    package My::Number::Utilities;
    use 5.006;
    use strict;
    use warnings;
    =head1 NAME
    My::Number::Utilities - The great new My::Number::Utilities!
    

    默认情况下,Module::Starter构建的模式是Makefile.PL,如果想要构建Build.PL,可以加上--builder=Module::Build或者无需选项参数的选项--mb

    $ module-starter --builder=Module::Build --module "My::Number::Utilities" --author="ma long shuai" --email="79123@qq.com"
    $ module-starter --mb --module "My::Number::Utilities" --author="ma long shuai" --email="79123@qq.com"
    

    因为每次在module-starter命令行上都写author和email选项很麻烦,可以在家目录下创建一个文件$HOME/.module-starter/config(该目录可由环境变量MODULE_STARTER_DIR设置,只需在该环境变量指定的目录下包含config文件即可。例如windows下.module-starter目录可能会有些问题),包含每次想要在命令行中省略的选项即可,例如:

    author: ma long shuai
    email: 79123@qq.com
    builder: Module::Build
    verbose: 1
    

    以后再执行module-stater时,只需一个--module选项即可:

    module-starter --module=My::Module
    

    现在,只需将自己写的pm模块文件拷贝覆盖到My-Number-Utilities/lib/My/Number/Utilities.pm,将独立的pod文档放到pm同目录下即可。然后就可以编译、安装模块了。

    perl Makefile.PL
    make test
    make install
    perl Build.PL
    ./Build
    ./Build test
    ./Build install
    

    上面的过程中,只要不是error,出现了warning可以忽略。

    最后,执行下面两种命令,可将模块打包成".tar.gz"格式的文件。

    make dist
    ./Build dist
    

    例如,生成Build格式的包:

    $ ./Build dist
    Created META.yml and META.json
    Creating Cat-New-0.01
    Creating Cat-New-0.01.tar.gz
    

    Makefile.PL和Build.PL

    虽然Makefile.PL和Build.PL不建议修改,但小心翼翼点也可以修改。

    Makefile.PL

    先看Makefile.PL文件的前几行:

    use 5.006;
    use strict;
    use warnings;
    use ExtUtils::MakeMaker;
    WriteMakefile(
        NAME             => 'My::Number::Utilities',
        AUTHOR           => q{ma long shuai <79123@qq.com>},
        VERSION_FROM     => 'lib/My/Number/Utilities.pm',
        ABSTRACT_FROM    => 'lib/My/Number/Utilities.pm',
        LICENSE          => 'artistic_2',
        PL_FILES         => {},
        MIN_PERL_VERSION => '5.006',
        CONFIGURE_REQUIRES => {
            'ExtUtils::MakeMaker' => '0',
        BUILD_REQUIRES => {
            'Test::More' => '0',
        PREREQ_PM => {
            #'ABC'              => '1.6',
            #'Foo::Bar::Module' => '5.0401',
        dist  => { COMPRESS => 'gzip -9f', SUFFIX => 'gz', },
        clean => { FILES => 'My-Number-Utilities-*' },
    

    主要是其中的PREREQ_PM项,它表示安装此模块时所依赖的模块,也就是要安装此模块,必须先安装该项指定的模块。它是一个hash结构,每个模块名是键,其值是模块的版本号,表示最低要安装该版本的模块。

    此外,如果想要安装一些perl程序文件,而非模块文件,可以如下书写:

    WriteMakefile(
        EXE_FILES => [ qw( scripts/barnyard.pl ) ],
    

    上面的要求是将barnyard.pl程序文件放在scripts目录下。通过这种方式,即使是没有包含任何模块,也能构建perl程序包。在安装模块的时候,这个文件会放进/usr/local/bin目录下。

    Build.PL

    再看Build.PL文件的前几行:

    use 5.006;
    use strict;
    use warnings;
    use Module::Build;
    my $builder = Module::Build->new(
        module_name         => 'Cat',
        license             => 'artistic_2',
        dist_author         => q{ma long shuai <79123@qq.com>},
        dist_version_from   => 'lib/Cat.pm',
        release_status      => 'stable',
        configure_requires => {
            'Module::Build' => '0',
        build_requires => {
            'Test::More' => '0',
        requires => {
            #'ABC'              => '1.6',
            #'Foo::Bar::Module' => '5.0401',
        add_to_cleanup     => [ 'Cat-*' ],
    $builder->create_build_script();
    

    主要是其中的requires项,它表示安装此模块时所依赖的模块。

    此外,如果想要安装一些perl程序文件,而非模块文件,可以如下书写:

    my $builder = Module::Build->new(
        script_files => [ qw(scripts/barnyard.pl) ],
    

    上面的要求是将barnyard.pl程序文件放在scripts目录下。

    当执行Module::Starter的module-starter时,会调用ExtUtil::Makefile

    构建多个模块

    如果想一次性构建多个模块,且知道各模块名称,则可以:

    $ module-starter --module=Animal,Cow,Horse,Mouse
    

    它会以第一个模块名称作为顶级目录,然后在lib中创建各模块文件。

    参考如下目录结构:

    $ tree Animal/
    Animal/
    ├── Changes
    ├── lib
    │   ├── Animal.pm
    │   ├── Cow.pm
    │   ├── Horse.pm
    │   └── Mouse.pm
    ├── Makefile.PL
    ├── MANIFEST
    ├── README
    ├── t
    │   ├── 00-load.t
    │   ├── manifest.t
    │   ├── pod-coverage.t
    │   └── pod.t
    └── xt
        └── boilerplate.t
    

    如果模块是多层次的(包含双冒号的),则构建的目录结构如下:

    $ module-starter --module=Animal::Rule,Cow::Rule,Horse,Mouse
    $ tree Animal-Rule
    Animal-Rule/
    ├── Changes
    ├── lib
    │   ├── Animal
    │   │   └── Rule.pm
    │   ├── Cow
    │   │   └── Rule.pm
    │   ├── Horse.pm
    │   └── Mouse.pm
    ├── Makefile
    ├── Makefile.PL
    ├── MANIFEST
    ├── MYMETA.json
    ├── MYMETA.yml
    ├── README
    ├── t
    │   ├── 00-load.t
    │   ├── manifest.t
    │   ├── pod-coverage.t
    │   └── pod.t
    └── xt
        └── boilerplate.t
    

    添加新模块

    如果想要向已有模块目录添加一个新模块,需要使用Module::Starter::AddModule插件,不过得先安装:

    $ cpan -i Module::Starter::AddModule
    

    然后在Module::Starter的配置文件$HOME/.module-starter/config中加入一行:

    author: ma long shuai
    email: 79123@qq.com
    verbose: 1
    plugins: Module::Starter::AddModule  # 新加此行
    

    然后就可以添加新模块:

    $ module-starter --module=Sheep --dist=Animal-Rule
    

    其中--dist指定要添加到的模块目录,如果已经在Animal-Rule目录下,则使用相对路径--dist=.即可。

    作者:骏马金龙
    Linux运维交流群:921383787