about summary refs log tree commit homepage
path: root/lib/PublicInbox/XapHelperCxx.pm
diff options
context:
space:
mode:
authorEric Wong <e@80x24.org>2023-08-24 01:22:33 +0000
committerEric Wong <e@80x24.org>2023-08-24 07:47:51 +0000
commitb18ecb7707e83cb8cb38c3736aecd984999ca0a7 (patch)
tree0f159212810c98aa07d26b6f7f28f4b8dbc9b302 /lib/PublicInbox/XapHelperCxx.pm
parentcf96412eb8f193ebd334fae340b2d91b6b7f2afe (diff)
downloadpublic-inbox-b18ecb7707e83cb8cb38c3736aecd984999ca0a7.tar.gz
This allows us to perform the expensive "dump_ibx" operations in
native C++ code using the Xapian C++ library.  This provides the
majority of the speedup with the -cindex --associate switch.

Eventually this may be expanded to cover all uses of Xapian
within the project to ensure we have access to Xapian APIs which
aren't available in XS|SWIG bindings; and also for
ease-of-installation on systems which don't provide
pre-packaged Perl Xapian bindings (e.g. OpenBSD 7.3) but
do provide Xapian development libraries.

Most of the C++ code is still C, as I'm not remotely familiar
with C++ compared to C.  I suspect many users and potential
hackers being from git, Linux kernel, and glibc world are in the
same boat.
Diffstat (limited to 'lib/PublicInbox/XapHelperCxx.pm')
-rw-r--r--lib/PublicInbox/XapHelperCxx.pm93
1 files changed, 93 insertions, 0 deletions
diff --git a/lib/PublicInbox/XapHelperCxx.pm b/lib/PublicInbox/XapHelperCxx.pm
new file mode 100644
index 00000000..4571676b
--- /dev/null
+++ b/lib/PublicInbox/XapHelperCxx.pm
@@ -0,0 +1,93 @@
+# Copyright (C) all contributors <meta@public-inbox.org>
+# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
+
+# Just-ahead-of-time builder for the lib/PublicInbox/xap_helper.h shim.
+# I never want users to be without source code for repairs, so this
+# aims to replicate the feel of a scripting language using C++.
+# The resulting executable is not linked to Perl in any way.
+package PublicInbox::XapHelperCxx;
+use v5.12;
+use PublicInbox::Spawn;
+use PublicInbox::Search;
+my $dir = ($ENV{PERL_INLINE_DIRECTORY} //
+        die('BUG: PERL_INLINE_DIRECTORY unset')) . '/cxx';
+my $bin = "$dir/xap_helper";
+my ($srcpfx) = (__FILE__ =~ m!\A(.+/)[^/]+\z!);
+my @srcs = map { $srcpfx.$_ } qw(xap_helper.h);
+my @pm_dep = map { $srcpfx.$_ } qw(Search.pm CodeSearch.pm);
+my $xflags = ($ENV{CXXFLAGS} // '-Wall -ggdb3 -O0') . ' ' .
+        ($ENV{LDFLAGS} // '-Wl,-O1 -Wl,--compress-debug-sections=zlib') .
+        qq{ -DTHREADID=}.PublicInbox::Search::THREADID;
+
+sub xflags_chg () {
+        open my $fh, '<', "$dir/XFLAGS" or return 1;
+        chomp(my $prev = <$fh>);
+        $prev ne $xflags;
+}
+
+sub build () {
+        if (!-d $dir) {
+                my $err;
+                mkdir($dir) or $err = $!;
+                die "mkdir($dir): $err" if !-d $dir;
+        }
+        use autodie;
+        require File::Temp;
+        require PublicInbox::CodeSearch;
+        my ($prog) = ($bin =~ m!/([^/]+)\z!);
+        my $pkg_config = $ENV{PKG_CONFIG} // 'pkg-config';
+        my $tmp = File::Temp->newdir(DIR => $dir) // die "newdir: $!";
+        my $src = "$tmp/$prog.cpp";
+        open my $fh, '>', $src;
+        for (@srcs) {
+                say $fh qq(# line 1 "$_");
+                open my $rfh, '<', $_;
+                local $/;
+                print $fh readline($rfh);
+        }
+        print $fh PublicInbox::Search::generate_cxx();
+        print $fh PublicInbox::CodeSearch::generate_cxx();
+        close $fh;
+
+        my $cmd = "$pkg_config --libs --cflags xapian-core";
+        chomp(my $fl = `$cmd`);
+        die "$cmd failed: \$?=$?" if $?;
+        my $cxx = $ENV{CXX} // 'c++';
+        $cmd = "$cxx $src $fl $xflags -o $tmp/$prog";
+        system($cmd) and die "$cmd failed: \$?=$?";
+        my $cf = "$tmp/XFLAGS";
+        open $fh, '>', $cf;
+        say $fh $xflags;
+        close $fh;
+        # not quite atomic, but close enough :P
+        rename("$tmp/$_", "$dir/$_") for ($prog, 'XFLAGS');
+}
+
+sub check_build () {
+        use Time::HiRes qw(stat);
+        my $ctime = 0;
+        my @bin = stat($bin) or return build();
+        for (@srcs, @pm_dep) {
+                my @st = stat($_) or die "stat $_: $!";
+                if ($st[10] > $ctime) {
+                        $ctime = $st[10];
+                        return build() if $ctime > $bin[10];
+                }
+        }
+        xflags_chg() ? build() : 0;
+}
+
+sub start (@) {
+        check_build();
+        my @cmd;
+        if (my $v = $ENV{VALGRIND}) {
+                $v = 'valgrind -v' if $v eq '1';
+                @cmd = split(/\s+/, $v);
+        }
+        push @cmd, $bin, @_;
+        my $prog = $cmd[0];
+        $cmd[0] =~ s!\A.*?/([^/]+)\z!$1!;
+        exec { $prog } @cmd;
+}
+
+1;