From d1d1d4a38b4bf7ca878456bc3a6952bdec11a000 Mon Sep 17 00:00:00 2001 From: Charles McGarvey Date: Wed, 16 Jan 2013 12:52:02 -0700 Subject: [PATCH 1/1] initial commit --- Build.PL | 21 +++++ dist.ini | 39 +++++++++ inc/My/Build.pm | 212 +++++++++++++++++++++++++++++++++++++++++++++++ lib/Alien/ZMQ.pm | 135 ++++++++++++++++++++++++++++++ perlcritic.rc | 4 + t/00-basic.t | 20 +++++ 6 files changed, 431 insertions(+) create mode 100644 Build.PL create mode 100644 dist.ini create mode 100644 inc/My/Build.pm create mode 100644 lib/Alien/ZMQ.pm create mode 100644 perlcritic.rc create mode 100644 t/00-basic.t diff --git a/Build.PL b/Build.PL new file mode 100644 index 0000000..cd66aba --- /dev/null +++ b/Build.PL @@ -0,0 +1,21 @@ +use warnings FATAL => 'all'; +use strict; + +use lib 'inc'; +use File::Path qw/make_path/; +use My::Build; + +# prevent warnings about missing share directory +make_path("share"); + +my $builder = My::Build->new( + ##{ $plugin->get_prereqs ##} + ##{ $plugin->get_default(qw/dist_name license dist_abstract/) ##} + ##{ $plugin->get_default(qw/dist_author/) ##} + dist_version_from => "lib/Alien/ZMQ.pm", + share_dir => "share", +); +$builder->notes('zmq-version', '3.2.2'); +$builder->notes('zmq-sha1', '0e8734c773b6a757b474c16fc3c517993ba47283'); + +$builder->create_build_script; diff --git a/dist.ini b/dist.ini new file mode 100644 index 0000000..f97072a --- /dev/null +++ b/dist.ini @@ -0,0 +1,39 @@ + +name = Alien-ZMQ +version = 0.02 +author = Charles McGarvey +license = Perl_5 +copyright_holder = Charles McGarvey + +[@Filter] +-bundle = @Basic +-remove = MakeMaker + +[PodSyntaxTests] +[PodCoverageTests] +[Test::Perl::Critic] + +[PruneFiles] +filename = dist.ini + +[Clean] +[MetaJSON] +[ModuleBuild::Custom] +[PkgVersion] +[PodWeaver] + +[GitFmtChanges] +file_name = Changes +tax_regexp = ^v\d+ + +[AutoPrereqs] + +[Prereqs / ConfigureRequires] +Archive::Tar = 0 +Cwd = 0 +Digest::SHA = 0 +File::Path = 0 +File::Spec::Functions = 0 +IPC::Cmd = 0 +LWP::Simple = 0 + diff --git a/inc/My/Build.pm b/inc/My/Build.pm new file mode 100644 index 0000000..8c7d987 --- /dev/null +++ b/inc/My/Build.pm @@ -0,0 +1,212 @@ +package My::Build; + +use v5.10; +use warnings FATAL => 'all'; +use strict; +use utf8; + +use Archive::Tar; +use Cwd qw/realpath/; +use Digest::SHA qw/sha1_hex/; +use File::Path qw/remove_tree/; +use File::Spec::Functions qw/catdir catfile/; +use IPC::Cmd qw/can_run run/; +use LWP::Simple qw/getstore RC_OK/; +use Module::Build; + +use base 'Module::Build'; + +sub ACTION_code { + my $self = shift; + + $self->SUPER::ACTION_code; + + return if -e 'build-zeromq'; + $self->add_to_cleanup('build-zeromq'); + + my %args = $self->args; + my %vars; + + $self->have_c_compiler or die "C compiler not found"; + + unless (exists $args{'zmq-skip-probe'}) { + say "Probing..."; + %vars = $self->probe_zeromq; + } + + if ($vars{inc_version} && $vars{lib_version}) { + say "Found ØMQ $vars{lib_version}; skipping installation"; + } else { + say "ØMQ not found; building from source..."; + %vars = $self->install_zeromq; + } + + # write vars to ZMQ.pm + my $module = catfile qw/blib lib Alien ZMQ.pm/; + open my $LIB, "<$module" or die "Cannot read module"; + my $lib = do { local $/; <$LIB> }; + close $LIB; + $lib =~ s/^sub inc_dir.*$/sub inc_dir { "$vars{inc_dir}" }/m; + $lib =~ s/^sub lib_dir.*$/sub lib_dir { "$vars{lib_dir}" }/m; + $lib =~ s/^sub inc_version.*$/sub inc_version { v$vars{inc_version} }/m; + $lib =~ s/^sub lib_version.*$/sub lib_version { v$vars{lib_version} }/m; + my @stats = stat $module; + chmod 0644, $module; + open $LIB, ">$module" or die "Cannot write config to module"; + print $LIB $lib; + close $LIB; + chmod $stats[2], $module; + + open my $TARGET, ">build-zeromq"; + print $TARGET time, "\n"; + close $TARGET; +} + +sub probe_zeromq { + my $self = shift; + my $cb = $self->cbuilder; + + my $src = "test-$$.c"; + open my $SRC, ">$src"; + print $SRC < +#include +int main(int argc, char* argv[]) { + int major, minor, patch; + zmq_version(&major, &minor, &patch); + printf("%d.%d.%d %d.%d.%d", + ZMQ_VERSION_MAJOR, ZMQ_VERSION_MINOR, ZMQ_VERSION_PATCH, + major, minor, patch); + return 0; +} +END + close $SRC; + + my @inc_search; + my @lib_search; + + my $cflags = $self->args('zmq-cflags'); + my $libs = $self->args('zmq-libs'); + + my $pkg_version; + + my $pkg_config = $ENV{PKG_CONFIG_COMMAND} || "pkg-config"; + for my $pkg (qw/libzmq zeromq3/) { + $pkg_version = `$pkg_config $pkg --modversion`; + chomp $pkg_version; + next unless $pkg_version; + + $cflags ||= `$pkg_config $pkg --cflags`; + $libs ||= `$pkg_config $pkg --libs`; + + # use -I and -L flag arguments as extra search directories + my $inc = `$pkg_config $pkg --cflags-only-I`; + push @inc_search, map { s/^-I//; $_ } split(/\s+/, $inc); + my $lib = `$pkg_config $pkg --libs-only-L`; + push @lib_search, map { s/^-L//; $_ } split(/\s+/, $lib); + + last; + } + + my $obj = eval { + $cb->compile(source => $src, include_dirs => [@inc_search], extra_compiler_flags => $cflags); + }; + unlink $src; + return unless $obj; + + my $exe = eval { + $cb->link_executable(objects => $obj, extra_linker_flags => $libs); + }; + unlink $obj; + return unless $exe; + + my $out = `./$exe`; + unlink $exe; + my ($inc_version, $lib_version) = $out =~ /(\d\.\d\.\d) (\d\.\d\.\d)/; + + # query the compiler for include and library search paths + my $cc = $ENV{CC} || "cc"; + push @lib_search, map { + my $path = $_; + $path =~ s/^.+ =?//; + $path =~ s/\n.*$//; + -d $path ? realpath($path) : (); + } split /:/, `$cc -print-search-dirs`; + push @inc_search, map { + my $path = $_; + $path =~ s/lib(32|64)?$/include/; + $path; + } @lib_search; + + # search for the header and library files + my ($inc_dir) = grep { -f catfile($_, "zmq.h") } @inc_search; + my ($lib_dir) = grep { + -f catfile($_, "libzmq.so") || + -f catfile($_, "libzmq.dylib") || + -f catfile($_, "libzmq.dll") + } @lib_search; + + ( + inc_version => $inc_version, + lib_version => $lib_version, + pkg_version => $pkg_version, + inc_dir => $inc_dir, + lib_dir => $lib_dir, + ); +} + +sub install_zeromq { + my $self = shift; + + can_run("libtool") or die "The libtool command cannot be found"; + + my $version = $self->notes('zmq-version'); + my $sha1 = $self->notes('zmq-sha1'); + my $archive = "zeromq-$version.tar.gz"; + + say "Downloading ØMQ $version source archive from download.zeromq.org..."; + getstore("http://download.zeromq.org/$archive", $archive) == RC_OK + or die "Failed to download ØMQ source archive"; + + say "Verifying..."; + my $sha1sum = Digest::SHA->new; + open my $ARCHIVE, "<$archive"; + binmode $ARCHIVE; + $sha1sum->addfile($ARCHIVE); + close $ARCHIVE; + $sha1sum->hexdigest eq $sha1 or die "Source archive checksum mismatch"; + + say "Extracting..."; + Archive::Tar->new($archive)->extract; + unlink $archive; + + my $prefix = catdir($self->install_destination("lib"), qw/auto share dist Alien-ZMQ/); + my $basedir = $self->base_dir; + my $datadir = catdir($basedir, "share"); + my $srcdir = catdir($basedir, "zeromq-$version"); + + say "Configuring..."; + my @config = split(/\s/, $self->args('zmq-config') || ""); + chdir $srcdir; + run(command => ["./configure", "--prefix=$prefix", @config]) + or die "Failed to configure ØMQ"; + + say "Compiling..."; + run(command => ['make']) or die "Failed to make ØMQ"; + + say "Installing..."; + run(command => [qw|make install prefix=/|, "DESTDIR=$datadir"]) + or die "Failed to install ØMQ"; + + chdir $basedir; + remove_tree($srcdir); + + ( + inc_version => $version, + lib_version => $version, + inc_dir => catdir($prefix, "include"), + lib_dir => catdir($prefix, "lib"), + ); +} + +1; diff --git a/lib/Alien/ZMQ.pm b/lib/Alien/ZMQ.pm new file mode 100644 index 0000000..4c0e2f9 --- /dev/null +++ b/lib/Alien/ZMQ.pm @@ -0,0 +1,135 @@ +package Alien::ZMQ; +# ABSTRACT: detect and/or install zeromq + +use warnings; +use strict; + +=head1 DESCRIPTION + +Upon installation, the target system is probed for the presence of libzmq. If +it is not found, zeromq 3.2.2 is installed in a shared directory. In short, +modules that need libzmq can depend on this module to make sure that it is +available. + +=head1 SYNOPSIS + + use Alien::ZMQ; + + my $version = Alien::ZMQ::lib_version; + +=head1 OPTIONS + +These options to F affect the installation of this module. + +=over 4 + +=item --zmq-skip-probe + +By default, zeromq is not compiled and installed if it is detected to already +be on the system. Use this to skip those checks and always install zeromq. + +=item --zmq-config=... + +Pass extra flags to zeromq's F script. You may want to consider +passing either C<--with-pgm> or C<--with-system-pgm> if you need support for +PGM; this is not enabled by default because it is not supported by every +system. + +=item --zmq-libs=... + +Pass extra flags to the linker when probing for an existing installation of +zeromq. In particular, if your F file is installed to a special +location, you may pass flags such as C<-L/opt/libzmq2/lib -lzmq>. + +=item --zmq-cflags=... + +Pass extra flags to the compiler when probing for an existing installation of +zeromq. These flags will not be used when actually compiling zeromq from +source. For that, just use the C environment variable. + +=back + +=head1 CAVEATS + +Probing is only done upon installation, so if you are using a system-installed +version of libzmq and you uninstall or upgrade it, you will also need to +reinstall this module. + +=head1 BUGS + +Windows is not yet supported. Patches are welcome. + +=head1 SEE ALSO + +=over 4 + +=item * L + +=item * L - good perl bindings for zeromq + +=item * L - official libzmq website + +=back + +=head1 ACKNOWLEDGEMENTS + +The design and implementation of this module were influenced by other L +modules, including L and L. + +=method inc_version + +Get the version number of libzmq as a dotted version string according to the +F header file. + +=cut + +sub inc_version { } + +=method lib_version + +Get the version number of libzmq as a dotted version string according to the +F file. + +=cut + +sub lib_version { } + +=method inc_dir + +Get the directory containing the F header file. + +=cut + +sub inc_dir { } + +=method lib_dir + +Get the directory containing the F file. + +=cut + +sub lib_dir { } + +=method cflags + +Get the C compiler flags required to compile a program that uses libzmq. This +is a shortcut for constructing a C<-I> flag using C. + +=cut + +sub cflags { + "-I'" . inc_dir . "'"; +} + +=method libs + +Get the linker flags required to link a program against libzmq. This is +a shortcut for constructing a C<-L> flag using C, plus C<-lzmq>. + +=cut + +sub libs { + "-L'" . lib_dir . "' -lzmq"; +} + +1; diff --git a/perlcritic.rc b/perlcritic.rc new file mode 100644 index 0000000..261a4b2 --- /dev/null +++ b/perlcritic.rc @@ -0,0 +1,4 @@ +#severity = brutal +exclude = TestingAndDebugging::RequireUseStrict +# RequireUseStrict is a good policy, but sadly it doesn't play well with +# dzil's Pod::Weaver plugin. diff --git a/t/00-basic.t b/t/00-basic.t new file mode 100644 index 0000000..f819d60 --- /dev/null +++ b/t/00-basic.t @@ -0,0 +1,20 @@ +#!perl + +use warnings; +use strict; + +use Test::More tests => 8; + +BEGIN { + use_ok 'Alien::ZMQ'; +} + +ok Alien::ZMQ::inc_version, "include version number"; +ok Alien::ZMQ::lib_version, "library version number"; +ok Alien::ZMQ::inc_dir, "include directory path"; +ok Alien::ZMQ::inc_dir, "library directory path"; + +like Alien::ZMQ::cflags, qr/-I\S+/, "cflags has -I"; +like Alien::ZMQ::libs, qr/-L\S+/, "libs has -L"; +like Alien::ZMQ::libs, qr/-lzmq/, "libs has -lzmq"; + -- 2.45.2