--- /dev/null
+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 <<END;
+#include <stdio.h>
+#include <zmq.h>
+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;
--- /dev/null
+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<Build.PL> 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<configure> 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<libzmq.so> 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<CFLAGS> 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<GitHub project|https://github.com/chazmcgarvey/p5-Alien-ZMQ>
+
+=item * L<ZMQ> - good perl bindings for zeromq
+
+=item * L<ZeroMQ|http://www.zeromq.org/> - official libzmq website
+
+=back
+
+=head1 ACKNOWLEDGEMENTS
+
+The design and implementation of this module were influenced by other L<Alien>
+modules, including L<Alien::GMP> and L<Alien::Tidyp>.
+
+=method inc_version
+
+Get the version number of libzmq as a dotted version string according to the
+F<zmq.h> header file.
+
+=cut
+
+sub inc_version { }
+
+=method lib_version
+
+Get the version number of libzmq as a dotted version string according to the
+F<libzmq.so> file.
+
+=cut
+
+sub lib_version { }
+
+=method inc_dir
+
+Get the directory containing the F<zmq.h> header file.
+
+=cut
+
+sub inc_dir { }
+
+=method lib_dir
+
+Get the directory containing the F<libzmq.so> 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<inc_dir>.
+
+=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<lib_dir>, plus C<-lzmq>.
+
+=cut
+
+sub libs {
+ "-L'" . lib_dir . "' -lzmq";
+}
+
+1;