mirror of
https://github.com/NixOS/nix
synced 2025-06-27 08:31:16 +02:00
Integrate perl with the other meson builds
One big dev shell!
This commit is contained in:
parent
e0b4691754
commit
a83d95e26e
20 changed files with 52 additions and 27 deletions
1
src/perl/.version
Symbolic link
1
src/perl/.version
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../.version
|
2
src/perl/.yath.rc.in
Normal file
2
src/perl/.yath.rc.in
Normal file
|
@ -0,0 +1,2 @@
|
|||
[test]
|
||||
-I=rel(@lib_dir@)
|
7
src/perl/MANIFEST
Normal file
7
src/perl/MANIFEST
Normal file
|
@ -0,0 +1,7 @@
|
|||
Changes
|
||||
Makefile.PL
|
||||
MANIFEST
|
||||
Nix.xs
|
||||
README
|
||||
t/Nix.t
|
||||
lib/Nix.pm
|
25
src/perl/lib/Nix/Config.pm.in
Normal file
25
src/perl/lib/Nix/Config.pm.in
Normal file
|
@ -0,0 +1,25 @@
|
|||
package Nix::Config;
|
||||
|
||||
use MIME::Base64;
|
||||
use Nix::Store;
|
||||
|
||||
$version = "@PACKAGE_VERSION@";
|
||||
|
||||
$binDir = Nix::Store::getBinDir;
|
||||
$storeDir = Nix::Store::getStoreDir;
|
||||
|
||||
%config = ();
|
||||
|
||||
sub readConfig {
|
||||
my $config = "$confDir/nix.conf";
|
||||
return unless -f $config;
|
||||
|
||||
open CONFIG, "<$config" or die "cannot open '$config'";
|
||||
while (<CONFIG>) {
|
||||
/^\s*([\w\-\.]+)\s*=\s*(.*)$/ or next;
|
||||
$config{$1} = $2;
|
||||
}
|
||||
close CONFIG;
|
||||
}
|
||||
|
||||
return 1;
|
61
src/perl/lib/Nix/CopyClosure.pm
Normal file
61
src/perl/lib/Nix/CopyClosure.pm
Normal file
|
@ -0,0 +1,61 @@
|
|||
package Nix::CopyClosure;
|
||||
|
||||
use utf8;
|
||||
use strict;
|
||||
use Nix::Config;
|
||||
use Nix::Store;
|
||||
use Nix::SSH;
|
||||
use List::Util qw(sum);
|
||||
use IPC::Open2;
|
||||
|
||||
|
||||
sub copyToOpen {
|
||||
my ($from, $to, $sshHost, $storePaths, $includeOutputs, $dryRun, $useSubstitutes) = @_;
|
||||
|
||||
$useSubstitutes = 0 if $dryRun || !defined $useSubstitutes;
|
||||
|
||||
# Get the closure of this path.
|
||||
my @closure = reverse(topoSortPaths(computeFSClosure(0, $includeOutputs,
|
||||
map { followLinksToStorePath $_ } @{$storePaths})));
|
||||
|
||||
# Send the "query valid paths" command with the "lock" option
|
||||
# enabled. This prevents a race where the remote host
|
||||
# garbage-collect paths that are already there. Optionally, ask
|
||||
# the remote host to substitute missing paths.
|
||||
syswrite($to, pack("L<x4L<x4L<x4", 1, 1, $useSubstitutes)) or die;
|
||||
writeStrings(\@closure, $to);
|
||||
|
||||
# Get back the set of paths that are already valid on the remote host.
|
||||
my %present;
|
||||
$present{$_} = 1 foreach readStrings($from);
|
||||
|
||||
my @missing = grep { !$present{$_} } @closure;
|
||||
return if !@missing;
|
||||
|
||||
my $missingSize = 0;
|
||||
$missingSize += (queryPathInfo($_, 1))[3] foreach @missing;
|
||||
|
||||
printf STDERR "copying %d missing paths (%.2f MiB) to '$sshHost'...\n",
|
||||
scalar(@missing), $missingSize / (1024**2);
|
||||
return if $dryRun;
|
||||
|
||||
# Send the "import paths" command.
|
||||
syswrite($to, pack("L<x4", 4)) or die;
|
||||
exportPaths(fileno($to), @missing);
|
||||
readInt($from) == 1 or die "remote machine '$sshHost' failed to import closure\n";
|
||||
}
|
||||
|
||||
|
||||
sub copyTo {
|
||||
my ($sshHost, $storePaths, $includeOutputs, $dryRun, $useSubstitutes) = @_;
|
||||
|
||||
# Connect to the remote host.
|
||||
my ($from, $to) = connectToRemoteNix($sshHost, []);
|
||||
|
||||
copyToOpen($from, $to, $sshHost, $storePaths, $includeOutputs, $dryRun, $useSubstitutes);
|
||||
|
||||
close $to;
|
||||
}
|
||||
|
||||
|
||||
1;
|
325
src/perl/lib/Nix/Manifest.pm
Normal file
325
src/perl/lib/Nix/Manifest.pm
Normal file
|
@ -0,0 +1,325 @@
|
|||
package Nix::Manifest;
|
||||
|
||||
use utf8;
|
||||
use strict;
|
||||
use DBI;
|
||||
use DBD::SQLite;
|
||||
use Cwd;
|
||||
use File::stat;
|
||||
use File::Path;
|
||||
use Fcntl ':flock';
|
||||
use MIME::Base64;
|
||||
use Nix::Config;
|
||||
use Nix::Store;
|
||||
|
||||
our @ISA = qw(Exporter);
|
||||
our @EXPORT = qw(readManifest writeManifest addPatch parseNARInfo fingerprintPath);
|
||||
|
||||
|
||||
sub addNAR {
|
||||
my ($narFiles, $storePath, $info) = @_;
|
||||
|
||||
$$narFiles{$storePath} = []
|
||||
unless defined $$narFiles{$storePath};
|
||||
|
||||
my $narFileList = $$narFiles{$storePath};
|
||||
|
||||
my $found = 0;
|
||||
foreach my $narFile (@{$narFileList}) {
|
||||
$found = 1 if $narFile->{url} eq $info->{url};
|
||||
}
|
||||
|
||||
push @{$narFileList}, $info if !$found;
|
||||
}
|
||||
|
||||
|
||||
sub addPatch {
|
||||
my ($patches, $storePath, $patch) = @_;
|
||||
|
||||
$$patches{$storePath} = []
|
||||
unless defined $$patches{$storePath};
|
||||
|
||||
my $patchList = $$patches{$storePath};
|
||||
|
||||
my $found = 0;
|
||||
foreach my $patch2 (@{$patchList}) {
|
||||
$found = 1 if
|
||||
$patch2->{url} eq $patch->{url} &&
|
||||
$patch2->{basePath} eq $patch->{basePath};
|
||||
}
|
||||
|
||||
push @{$patchList}, $patch if !$found;
|
||||
|
||||
return !$found;
|
||||
}
|
||||
|
||||
|
||||
sub readManifest_ {
|
||||
my ($manifest, $addNAR, $addPatch) = @_;
|
||||
|
||||
# Decompress the manifest if necessary.
|
||||
if ($manifest =~ /\.bz2$/) {
|
||||
open MANIFEST, "$Nix::Config::bzip2 -d < $manifest |"
|
||||
or die "cannot decompress '$manifest': $!";
|
||||
} else {
|
||||
open MANIFEST, "<$manifest"
|
||||
or die "cannot open '$manifest': $!";
|
||||
}
|
||||
|
||||
my $inside = 0;
|
||||
my $type;
|
||||
|
||||
my $manifestVersion = 2;
|
||||
|
||||
my ($storePath, $url, $hash, $size, $basePath, $baseHash, $patchType);
|
||||
my ($narHash, $narSize, $references, $deriver, $copyFrom, $system, $compressionType);
|
||||
|
||||
while (<MANIFEST>) {
|
||||
chomp;
|
||||
s/\#.*$//g;
|
||||
next if (/^$/);
|
||||
|
||||
if (!$inside) {
|
||||
|
||||
if (/^\s*(\w*)\s*\{$/) {
|
||||
$type = $1;
|
||||
$type = "narfile" if $type eq "";
|
||||
$inside = 1;
|
||||
undef $storePath;
|
||||
undef $url;
|
||||
undef $hash;
|
||||
undef $size;
|
||||
undef $narHash;
|
||||
undef $narSize;
|
||||
undef $basePath;
|
||||
undef $baseHash;
|
||||
undef $patchType;
|
||||
undef $system;
|
||||
$references = "";
|
||||
$deriver = "";
|
||||
$compressionType = "bzip2";
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (/^\}$/) {
|
||||
$inside = 0;
|
||||
|
||||
if ($type eq "narfile") {
|
||||
&$addNAR($storePath,
|
||||
{ url => $url, hash => $hash, size => $size
|
||||
, narHash => $narHash, narSize => $narSize
|
||||
, references => $references
|
||||
, deriver => $deriver
|
||||
, system => $system
|
||||
, compressionType => $compressionType
|
||||
});
|
||||
}
|
||||
|
||||
elsif ($type eq "patch") {
|
||||
&$addPatch($storePath,
|
||||
{ url => $url, hash => $hash, size => $size
|
||||
, basePath => $basePath, baseHash => $baseHash
|
||||
, narHash => $narHash, narSize => $narSize
|
||||
, patchType => $patchType
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
elsif (/^\s*StorePath:\s*(\/\S+)\s*$/) { $storePath = $1; }
|
||||
elsif (/^\s*CopyFrom:\s*(\/\S+)\s*$/) { $copyFrom = $1; }
|
||||
elsif (/^\s*Hash:\s*(\S+)\s*$/) { $hash = $1; }
|
||||
elsif (/^\s*URL:\s*(\S+)\s*$/) { $url = $1; }
|
||||
elsif (/^\s*Compression:\s*(\S+)\s*$/) { $compressionType = $1; }
|
||||
elsif (/^\s*Size:\s*(\d+)\s*$/) { $size = $1; }
|
||||
elsif (/^\s*BasePath:\s*(\/\S+)\s*$/) { $basePath = $1; }
|
||||
elsif (/^\s*BaseHash:\s*(\S+)\s*$/) { $baseHash = $1; }
|
||||
elsif (/^\s*Type:\s*(\S+)\s*$/) { $patchType = $1; }
|
||||
elsif (/^\s*NarHash:\s*(\S+)\s*$/) { $narHash = $1; }
|
||||
elsif (/^\s*NarSize:\s*(\d+)\s*$/) { $narSize = $1; }
|
||||
elsif (/^\s*References:\s*(.*)\s*$/) { $references = $1; }
|
||||
elsif (/^\s*Deriver:\s*(\S+)\s*$/) { $deriver = $1; }
|
||||
elsif (/^\s*ManifestVersion:\s*(\d+)\s*$/) { $manifestVersion = $1; }
|
||||
elsif (/^\s*System:\s*(\S+)\s*$/) { $system = $1; }
|
||||
|
||||
# Compatibility;
|
||||
elsif (/^\s*NarURL:\s*(\S+)\s*$/) { $url = $1; }
|
||||
elsif (/^\s*MD5:\s*(\S+)\s*$/) { $hash = "md5:$1"; }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
close MANIFEST;
|
||||
|
||||
return $manifestVersion;
|
||||
}
|
||||
|
||||
|
||||
sub readManifest {
|
||||
my ($manifest, $narFiles, $patches) = @_;
|
||||
readManifest_($manifest,
|
||||
sub { addNAR($narFiles, @_); },
|
||||
sub { addPatch($patches, @_); } );
|
||||
}
|
||||
|
||||
|
||||
sub writeManifest {
|
||||
my ($manifest, $narFiles, $patches, $noCompress) = @_;
|
||||
|
||||
open MANIFEST, ">$manifest.tmp"; # !!! check exclusive
|
||||
|
||||
print MANIFEST "version {\n";
|
||||
print MANIFEST " ManifestVersion: 3\n";
|
||||
print MANIFEST "}\n";
|
||||
|
||||
foreach my $storePath (sort (keys %{$narFiles})) {
|
||||
my $narFileList = $$narFiles{$storePath};
|
||||
foreach my $narFile (@{$narFileList}) {
|
||||
print MANIFEST "{\n";
|
||||
print MANIFEST " StorePath: $storePath\n";
|
||||
print MANIFEST " NarURL: $narFile->{url}\n";
|
||||
print MANIFEST " Compression: $narFile->{compressionType}\n";
|
||||
print MANIFEST " Hash: $narFile->{hash}\n" if defined $narFile->{hash};
|
||||
print MANIFEST " Size: $narFile->{size}\n" if defined $narFile->{size};
|
||||
print MANIFEST " NarHash: $narFile->{narHash}\n";
|
||||
print MANIFEST " NarSize: $narFile->{narSize}\n" if $narFile->{narSize};
|
||||
print MANIFEST " References: $narFile->{references}\n"
|
||||
if defined $narFile->{references} && $narFile->{references} ne "";
|
||||
print MANIFEST " Deriver: $narFile->{deriver}\n"
|
||||
if defined $narFile->{deriver} && $narFile->{deriver} ne "";
|
||||
print MANIFEST " System: $narFile->{system}\n" if defined $narFile->{system};
|
||||
print MANIFEST "}\n";
|
||||
}
|
||||
}
|
||||
|
||||
foreach my $storePath (sort (keys %{$patches})) {
|
||||
my $patchList = $$patches{$storePath};
|
||||
foreach my $patch (@{$patchList}) {
|
||||
print MANIFEST "patch {\n";
|
||||
print MANIFEST " StorePath: $storePath\n";
|
||||
print MANIFEST " NarURL: $patch->{url}\n";
|
||||
print MANIFEST " Hash: $patch->{hash}\n";
|
||||
print MANIFEST " Size: $patch->{size}\n";
|
||||
print MANIFEST " NarHash: $patch->{narHash}\n";
|
||||
print MANIFEST " NarSize: $patch->{narSize}\n" if $patch->{narSize};
|
||||
print MANIFEST " BasePath: $patch->{basePath}\n";
|
||||
print MANIFEST " BaseHash: $patch->{baseHash}\n";
|
||||
print MANIFEST " Type: $patch->{patchType}\n";
|
||||
print MANIFEST "}\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
close MANIFEST;
|
||||
|
||||
rename("$manifest.tmp", $manifest)
|
||||
or die "cannot rename $manifest.tmp: $!";
|
||||
|
||||
|
||||
# Create a bzipped manifest.
|
||||
unless (defined $noCompress) {
|
||||
system("$Nix::Config::bzip2 < $manifest > $manifest.bz2.tmp") == 0
|
||||
or die "cannot compress manifest";
|
||||
|
||||
rename("$manifest.bz2.tmp", "$manifest.bz2")
|
||||
or die "cannot rename $manifest.bz2.tmp: $!";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Return a fingerprint of a store path to be used in binary cache
|
||||
# signatures. It contains the store path, the base-32 SHA-256 hash of
|
||||
# the contents of the path, and the references.
|
||||
sub fingerprintPath {
|
||||
my ($storePath, $narHash, $narSize, $references) = @_;
|
||||
die if substr($storePath, 0, length($Nix::Config::storeDir)) ne $Nix::Config::storeDir;
|
||||
die if substr($narHash, 0, 7) ne "sha256:";
|
||||
# Convert hash from base-16 to base-32, if necessary.
|
||||
$narHash = "sha256:" . convertHash("sha256", substr($narHash, 7), 1)
|
||||
if length($narHash) == 71;
|
||||
die if length($narHash) != 59;
|
||||
foreach my $ref (@{$references}) {
|
||||
die if substr($ref, 0, length($Nix::Config::storeDir)) ne $Nix::Config::storeDir;
|
||||
}
|
||||
return "1;" . $storePath . ";" . $narHash . ";" . $narSize . ";" . join(",", @{$references});
|
||||
}
|
||||
|
||||
|
||||
# Parse a NAR info file.
|
||||
sub parseNARInfo {
|
||||
my ($storePath, $content, $requireValidSig, $location) = @_;
|
||||
|
||||
my ($storePath2, $url, $fileHash, $fileSize, $narHash, $narSize, $deriver, $system, $sig);
|
||||
my $compression = "bzip2";
|
||||
my @refs;
|
||||
|
||||
foreach my $line (split "\n", $content) {
|
||||
return undef unless $line =~ /^(.*): (.*)$/;
|
||||
if ($1 eq "StorePath") { $storePath2 = $2; }
|
||||
elsif ($1 eq "URL") { $url = $2; }
|
||||
elsif ($1 eq "Compression") { $compression = $2; }
|
||||
elsif ($1 eq "FileHash") { $fileHash = $2; }
|
||||
elsif ($1 eq "FileSize") { $fileSize = int($2); }
|
||||
elsif ($1 eq "NarHash") { $narHash = $2; }
|
||||
elsif ($1 eq "NarSize") { $narSize = int($2); }
|
||||
elsif ($1 eq "References") { @refs = split / /, $2; }
|
||||
elsif ($1 eq "Deriver") { $deriver = $2; }
|
||||
elsif ($1 eq "System") { $system = $2; }
|
||||
elsif ($1 eq "Sig") { $sig = $2; }
|
||||
}
|
||||
|
||||
return undef if $storePath ne $storePath2 || !defined $url || !defined $narHash;
|
||||
|
||||
my $res =
|
||||
{ url => $url
|
||||
, compression => $compression
|
||||
, fileHash => $fileHash
|
||||
, fileSize => $fileSize
|
||||
, narHash => $narHash
|
||||
, narSize => $narSize
|
||||
, refs => [ @refs ]
|
||||
, deriver => $deriver
|
||||
, system => $system
|
||||
};
|
||||
|
||||
if ($requireValidSig) {
|
||||
# FIXME: might be useful to support multiple signatures per .narinfo.
|
||||
|
||||
if (!defined $sig) {
|
||||
warn "NAR info file '$location' lacks a signature; ignoring\n";
|
||||
return undef;
|
||||
}
|
||||
my ($keyName, $sig64) = split ":", $sig;
|
||||
return undef unless defined $keyName && defined $sig64;
|
||||
|
||||
my $publicKey = $Nix::Config::binaryCachePublicKeys{$keyName};
|
||||
if (!defined $publicKey) {
|
||||
warn "NAR info file '$location' is signed by unknown key '$keyName'; ignoring\n";
|
||||
return undef;
|
||||
}
|
||||
|
||||
my $fingerprint;
|
||||
eval {
|
||||
$fingerprint = fingerprintPath(
|
||||
$storePath, $narHash, $narSize,
|
||||
[ map { "$Nix::Config::storeDir/$_" } @refs ]);
|
||||
};
|
||||
if ($@) {
|
||||
warn "cannot compute fingerprint of '$location'; ignoring\n";
|
||||
return undef;
|
||||
}
|
||||
|
||||
if (!checkSignature($publicKey, decode_base64($sig64), $fingerprint)) {
|
||||
warn "NAR info file '$location' has an incorrect signature; ignoring\n";
|
||||
return undef;
|
||||
}
|
||||
|
||||
$res->{signedBy} = $keyName;
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
|
||||
return 1;
|
110
src/perl/lib/Nix/SSH.pm
Normal file
110
src/perl/lib/Nix/SSH.pm
Normal file
|
@ -0,0 +1,110 @@
|
|||
package Nix::SSH;
|
||||
|
||||
use utf8;
|
||||
use strict;
|
||||
use File::Temp qw(tempdir);
|
||||
use IPC::Open2;
|
||||
|
||||
our @ISA = qw(Exporter);
|
||||
our @EXPORT = qw(
|
||||
@globalSshOpts
|
||||
readN readInt readString readStrings
|
||||
writeInt writeString writeStrings
|
||||
connectToRemoteNix
|
||||
);
|
||||
|
||||
|
||||
our @globalSshOpts = split ' ', ($ENV{"NIX_SSHOPTS"} or "");
|
||||
|
||||
|
||||
sub readN {
|
||||
my ($bytes, $from) = @_;
|
||||
my $res = "";
|
||||
while ($bytes > 0) {
|
||||
my $s;
|
||||
my $n = sysread($from, $s, $bytes);
|
||||
die "I/O error reading from remote side\n" if !defined $n;
|
||||
die "got EOF while expecting $bytes bytes from remote side\n" if !$n;
|
||||
$bytes -= $n;
|
||||
$res .= $s;
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
|
||||
sub readInt {
|
||||
my ($from) = @_;
|
||||
return unpack("L<x4", readN(8, $from));
|
||||
}
|
||||
|
||||
|
||||
sub readString {
|
||||
my ($from) = @_;
|
||||
my $len = readInt($from);
|
||||
my $s = readN($len, $from);
|
||||
readN(8 - $len % 8, $from) if $len % 8; # skip padding
|
||||
return $s;
|
||||
}
|
||||
|
||||
|
||||
sub readStrings {
|
||||
my ($from) = @_;
|
||||
my $n = readInt($from);
|
||||
my @res;
|
||||
push @res, readString($from) while $n--;
|
||||
return @res;
|
||||
}
|
||||
|
||||
|
||||
sub writeInt {
|
||||
my ($n, $to) = @_;
|
||||
syswrite($to, pack("L<x4", $n)) or die;
|
||||
}
|
||||
|
||||
|
||||
sub writeString {
|
||||
my ($s, $to) = @_;
|
||||
my $len = length $s;
|
||||
my $req .= pack("L<x4", $len);
|
||||
$req .= $s;
|
||||
$req .= "\000" x (8 - $len % 8) if $len % 8;
|
||||
syswrite($to, $req) or die;
|
||||
}
|
||||
|
||||
|
||||
sub writeStrings {
|
||||
my ($ss, $to) = @_;
|
||||
writeInt(scalar(@{$ss}), $to);
|
||||
writeString($_, $to) foreach @{$ss};
|
||||
}
|
||||
|
||||
|
||||
sub connectToRemoteNix {
|
||||
my ($sshHost, $sshOpts, $extraFlags) = @_;
|
||||
|
||||
$extraFlags ||= "";
|
||||
|
||||
# Start ‘nix-store --serve’ on the remote host.
|
||||
my ($from, $to);
|
||||
# FIXME: don't start a shell, start ssh directly.
|
||||
my $pid = open2($from, $to, "exec ssh -x -a $sshHost @globalSshOpts @{$sshOpts} nix-store --serve --write $extraFlags");
|
||||
|
||||
# Do the handshake.
|
||||
my $magic;
|
||||
eval {
|
||||
my $SERVE_MAGIC_1 = 0x390c9deb; # FIXME
|
||||
my $clientVersion = 0x200;
|
||||
syswrite($to, pack("L<x4L<x4", $SERVE_MAGIC_1, $clientVersion)) or die;
|
||||
$magic = readInt($from);
|
||||
};
|
||||
die "unable to connect to '$sshHost'\n" if $@;
|
||||
die "did not get valid handshake from remote host\n" if $magic != 0x5452eecb;
|
||||
|
||||
my $serverVersion = readInt($from);
|
||||
die "unsupported server version\n" if $serverVersion < 0x200 || $serverVersion >= 0x300;
|
||||
|
||||
return ($from, $to, $pid);
|
||||
}
|
||||
|
||||
|
||||
1;
|
45
src/perl/lib/Nix/Store.pm
Normal file
45
src/perl/lib/Nix/Store.pm
Normal file
|
@ -0,0 +1,45 @@
|
|||
package Nix::Store;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
require Exporter;
|
||||
|
||||
our @ISA = qw(Exporter);
|
||||
|
||||
our %EXPORT_TAGS = ( 'all' => [ qw( ) ] );
|
||||
|
||||
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
|
||||
|
||||
our @EXPORT = qw(
|
||||
StoreWrapper
|
||||
StoreWrapper::new
|
||||
StoreWrapper::isValidPath StoreWrapper::queryReferences StoreWrapper::queryPathInfo StoreWrapper::queryDeriver StoreWrapper::queryPathHash
|
||||
StoreWrapper::queryPathFromHashPart
|
||||
StoreWrapper::topoSortPaths StoreWrapper::computeFSClosure followLinksToStorePath StoreWrapper::exportPaths StoreWrapper::importPaths
|
||||
StoreWrapper::addToStore StoreWrapper::makeFixedOutputPath
|
||||
StoreWrapper::derivationFromPath
|
||||
StoreWrapper::addTempRoot
|
||||
StoreWrapper::queryRawRealisation
|
||||
|
||||
hashPath hashFile hashString convertHash
|
||||
signString checkSignature
|
||||
getBinDir getStoreDir
|
||||
setVerbosity
|
||||
);
|
||||
|
||||
our $VERSION = '0.15';
|
||||
|
||||
sub backtick {
|
||||
open(RES, "-|", @_) or die;
|
||||
local $/;
|
||||
my $res = <RES> || "";
|
||||
close RES or die;
|
||||
return $res;
|
||||
}
|
||||
|
||||
require XSLoader;
|
||||
XSLoader::load('Nix::Store', $VERSION);
|
||||
|
||||
1;
|
||||
__END__
|
434
src/perl/lib/Nix/Store.xs
Normal file
434
src/perl/lib/Nix/Store.xs
Normal file
|
@ -0,0 +1,434 @@
|
|||
#include "config-util.h"
|
||||
#include "config-store.h"
|
||||
|
||||
#include "EXTERN.h"
|
||||
#include "perl.h"
|
||||
#include "XSUB.h"
|
||||
|
||||
/* Prevent a clash between some Perl and libstdc++ macros. */
|
||||
#undef do_open
|
||||
#undef do_close
|
||||
|
||||
#include "derivations.hh"
|
||||
#include "realisation.hh"
|
||||
#include "globals.hh"
|
||||
#include "store-api.hh"
|
||||
#include "posix-source-accessor.hh"
|
||||
|
||||
#include <sodium.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using namespace nix;
|
||||
|
||||
static bool libStoreInitialized = false;
|
||||
|
||||
struct StoreWrapper {
|
||||
ref<Store> store;
|
||||
};
|
||||
|
||||
MODULE = Nix::Store PACKAGE = Nix::Store
|
||||
PROTOTYPES: ENABLE
|
||||
|
||||
TYPEMAP: <<HERE
|
||||
StoreWrapper * O_OBJECT
|
||||
|
||||
OUTPUT
|
||||
O_OBJECT
|
||||
sv_setref_pv( $arg, CLASS, (void*)$var );
|
||||
|
||||
INPUT
|
||||
O_OBJECT
|
||||
if ( sv_isobject($arg) && (SvTYPE(SvRV($arg)) == SVt_PVMG) ) {
|
||||
$var = ($type)SvIV((SV*)SvRV( $arg ));
|
||||
}
|
||||
else {
|
||||
warn( \"${Package}::$func_name() -- \"
|
||||
\"$var not a blessed SV reference\");
|
||||
XSRETURN_UNDEF;
|
||||
}
|
||||
HERE
|
||||
|
||||
#undef dNOOP // Hack to work around "error: declaration of 'Perl___notused' has a different language linkage" error message on clang.
|
||||
#define dNOOP
|
||||
|
||||
void
|
||||
StoreWrapper::DESTROY()
|
||||
|
||||
StoreWrapper *
|
||||
StoreWrapper::new(char * s = nullptr)
|
||||
CODE:
|
||||
static std::shared_ptr<Store> _store;
|
||||
try {
|
||||
if (!libStoreInitialized) {
|
||||
initLibStore();
|
||||
libStoreInitialized = true;
|
||||
}
|
||||
if (items == 1) {
|
||||
_store = openStore();
|
||||
RETVAL = new StoreWrapper {
|
||||
.store = ref<Store>{_store}
|
||||
};
|
||||
} else {
|
||||
RETVAL = new StoreWrapper {
|
||||
.store = openStore(s)
|
||||
};
|
||||
}
|
||||
} catch (Error & e) {
|
||||
croak("%s", e.what());
|
||||
}
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
|
||||
void init()
|
||||
CODE:
|
||||
if (!libStoreInitialized) {
|
||||
initLibStore();
|
||||
libStoreInitialized = true;
|
||||
}
|
||||
|
||||
|
||||
void setVerbosity(int level)
|
||||
CODE:
|
||||
verbosity = (Verbosity) level;
|
||||
|
||||
|
||||
int
|
||||
StoreWrapper::isValidPath(char * path)
|
||||
CODE:
|
||||
try {
|
||||
RETVAL = THIS->store->isValidPath(THIS->store->parseStorePath(path));
|
||||
} catch (Error & e) {
|
||||
croak("%s", e.what());
|
||||
}
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
|
||||
SV *
|
||||
StoreWrapper::queryReferences(char * path)
|
||||
PPCODE:
|
||||
try {
|
||||
for (auto & i : THIS->store->queryPathInfo(THIS->store->parseStorePath(path))->references)
|
||||
XPUSHs(sv_2mortal(newSVpv(THIS->store->printStorePath(i).c_str(), 0)));
|
||||
} catch (Error & e) {
|
||||
croak("%s", e.what());
|
||||
}
|
||||
|
||||
|
||||
SV *
|
||||
StoreWrapper::queryPathHash(char * path)
|
||||
PPCODE:
|
||||
try {
|
||||
auto s = THIS->store->queryPathInfo(THIS->store->parseStorePath(path))->narHash.to_string(HashFormat::Nix32, true);
|
||||
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
|
||||
} catch (Error & e) {
|
||||
croak("%s", e.what());
|
||||
}
|
||||
|
||||
|
||||
SV *
|
||||
StoreWrapper::queryDeriver(char * path)
|
||||
PPCODE:
|
||||
try {
|
||||
auto info = THIS->store->queryPathInfo(THIS->store->parseStorePath(path));
|
||||
if (!info->deriver) XSRETURN_UNDEF;
|
||||
XPUSHs(sv_2mortal(newSVpv(THIS->store->printStorePath(*info->deriver).c_str(), 0)));
|
||||
} catch (Error & e) {
|
||||
croak("%s", e.what());
|
||||
}
|
||||
|
||||
|
||||
SV *
|
||||
StoreWrapper::queryPathInfo(char * path, int base32)
|
||||
PPCODE:
|
||||
try {
|
||||
auto info = THIS->store->queryPathInfo(THIS->store->parseStorePath(path));
|
||||
if (!info->deriver)
|
||||
XPUSHs(&PL_sv_undef);
|
||||
else
|
||||
XPUSHs(sv_2mortal(newSVpv(THIS->store->printStorePath(*info->deriver).c_str(), 0)));
|
||||
auto s = info->narHash.to_string(base32 ? HashFormat::Nix32 : HashFormat::Base16, true);
|
||||
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
|
||||
mXPUSHi(info->registrationTime);
|
||||
mXPUSHi(info->narSize);
|
||||
AV * refs = newAV();
|
||||
for (auto & i : info->references)
|
||||
av_push(refs, newSVpv(THIS->store->printStorePath(i).c_str(), 0));
|
||||
XPUSHs(sv_2mortal(newRV((SV *) refs)));
|
||||
AV * sigs = newAV();
|
||||
for (auto & i : info->sigs)
|
||||
av_push(sigs, newSVpv(i.c_str(), 0));
|
||||
XPUSHs(sv_2mortal(newRV((SV *) sigs)));
|
||||
} catch (Error & e) {
|
||||
croak("%s", e.what());
|
||||
}
|
||||
|
||||
SV *
|
||||
StoreWrapper::queryRawRealisation(char * outputId)
|
||||
PPCODE:
|
||||
try {
|
||||
auto realisation = THIS->store->queryRealisation(DrvOutput::parse(outputId));
|
||||
if (realisation)
|
||||
XPUSHs(sv_2mortal(newSVpv(realisation->toJSON().dump().c_str(), 0)));
|
||||
else
|
||||
XPUSHs(sv_2mortal(newSVpv("", 0)));
|
||||
} catch (Error & e) {
|
||||
croak("%s", e.what());
|
||||
}
|
||||
|
||||
|
||||
SV *
|
||||
StoreWrapper::queryPathFromHashPart(char * hashPart)
|
||||
PPCODE:
|
||||
try {
|
||||
auto path = THIS->store->queryPathFromHashPart(hashPart);
|
||||
XPUSHs(sv_2mortal(newSVpv(path ? THIS->store->printStorePath(*path).c_str() : "", 0)));
|
||||
} catch (Error & e) {
|
||||
croak("%s", e.what());
|
||||
}
|
||||
|
||||
|
||||
SV *
|
||||
StoreWrapper::computeFSClosure(int flipDirection, int includeOutputs, ...)
|
||||
PPCODE:
|
||||
try {
|
||||
StorePathSet paths;
|
||||
for (int n = 2; n < items; ++n)
|
||||
THIS->store->computeFSClosure(THIS->store->parseStorePath(SvPV_nolen(ST(n))), paths, flipDirection, includeOutputs);
|
||||
for (auto & i : paths)
|
||||
XPUSHs(sv_2mortal(newSVpv(THIS->store->printStorePath(i).c_str(), 0)));
|
||||
} catch (Error & e) {
|
||||
croak("%s", e.what());
|
||||
}
|
||||
|
||||
|
||||
SV *
|
||||
StoreWrapper::topoSortPaths(...)
|
||||
PPCODE:
|
||||
try {
|
||||
StorePathSet paths;
|
||||
for (int n = 0; n < items; ++n) paths.insert(THIS->store->parseStorePath(SvPV_nolen(ST(n))));
|
||||
auto sorted = THIS->store->topoSortPaths(paths);
|
||||
for (auto & i : sorted)
|
||||
XPUSHs(sv_2mortal(newSVpv(THIS->store->printStorePath(i).c_str(), 0)));
|
||||
} catch (Error & e) {
|
||||
croak("%s", e.what());
|
||||
}
|
||||
|
||||
|
||||
SV *
|
||||
StoreWrapper::followLinksToStorePath(char * path)
|
||||
CODE:
|
||||
try {
|
||||
RETVAL = newSVpv(THIS->store->printStorePath(THIS->store->followLinksToStorePath(path)).c_str(), 0);
|
||||
} catch (Error & e) {
|
||||
croak("%s", e.what());
|
||||
}
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
|
||||
void
|
||||
StoreWrapper::exportPaths(int fd, ...)
|
||||
PPCODE:
|
||||
try {
|
||||
StorePathSet paths;
|
||||
for (int n = 1; n < items; ++n) paths.insert(THIS->store->parseStorePath(SvPV_nolen(ST(n))));
|
||||
FdSink sink(fd);
|
||||
THIS->store->exportPaths(paths, sink);
|
||||
} catch (Error & e) {
|
||||
croak("%s", e.what());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
StoreWrapper::importPaths(int fd, int dontCheckSigs)
|
||||
PPCODE:
|
||||
try {
|
||||
FdSource source(fd);
|
||||
THIS->store->importPaths(source, dontCheckSigs ? NoCheckSigs : CheckSigs);
|
||||
} catch (Error & e) {
|
||||
croak("%s", e.what());
|
||||
}
|
||||
|
||||
|
||||
SV *
|
||||
hashPath(char * algo, int base32, char * path)
|
||||
PPCODE:
|
||||
try {
|
||||
Hash h = hashPath(
|
||||
PosixSourceAccessor::createAtRoot(path),
|
||||
FileIngestionMethod::Recursive, parseHashAlgo(algo)).first;
|
||||
auto s = h.to_string(base32 ? HashFormat::Nix32 : HashFormat::Base16, false);
|
||||
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
|
||||
} catch (Error & e) {
|
||||
croak("%s", e.what());
|
||||
}
|
||||
|
||||
|
||||
SV * hashFile(char * algo, int base32, char * path)
|
||||
PPCODE:
|
||||
try {
|
||||
Hash h = hashFile(parseHashAlgo(algo), path);
|
||||
auto s = h.to_string(base32 ? HashFormat::Nix32 : HashFormat::Base16, false);
|
||||
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
|
||||
} catch (Error & e) {
|
||||
croak("%s", e.what());
|
||||
}
|
||||
|
||||
|
||||
SV * hashString(char * algo, int base32, char * s)
|
||||
PPCODE:
|
||||
try {
|
||||
Hash h = hashString(parseHashAlgo(algo), s);
|
||||
auto s = h.to_string(base32 ? HashFormat::Nix32 : HashFormat::Base16, false);
|
||||
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
|
||||
} catch (Error & e) {
|
||||
croak("%s", e.what());
|
||||
}
|
||||
|
||||
|
||||
SV * convertHash(char * algo, char * s, int toBase32)
|
||||
PPCODE:
|
||||
try {
|
||||
auto h = Hash::parseAny(s, parseHashAlgo(algo));
|
||||
auto s = h.to_string(toBase32 ? HashFormat::Nix32 : HashFormat::Base16, false);
|
||||
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
|
||||
} catch (Error & e) {
|
||||
croak("%s", e.what());
|
||||
}
|
||||
|
||||
|
||||
SV * signString(char * secretKey_, char * msg)
|
||||
PPCODE:
|
||||
try {
|
||||
auto sig = SecretKey(secretKey_).signDetached(msg);
|
||||
XPUSHs(sv_2mortal(newSVpv(sig.c_str(), sig.size())));
|
||||
} catch (Error & e) {
|
||||
croak("%s", e.what());
|
||||
}
|
||||
|
||||
|
||||
int checkSignature(SV * publicKey_, SV * sig_, char * msg)
|
||||
CODE:
|
||||
try {
|
||||
STRLEN publicKeyLen;
|
||||
unsigned char * publicKey = (unsigned char *) SvPV(publicKey_, publicKeyLen);
|
||||
if (publicKeyLen != crypto_sign_PUBLICKEYBYTES)
|
||||
throw Error("public key is not valid");
|
||||
|
||||
STRLEN sigLen;
|
||||
unsigned char * sig = (unsigned char *) SvPV(sig_, sigLen);
|
||||
if (sigLen != crypto_sign_BYTES)
|
||||
throw Error("signature is not valid");
|
||||
|
||||
RETVAL = crypto_sign_verify_detached(sig, (unsigned char *) msg, strlen(msg), publicKey) == 0;
|
||||
} catch (Error & e) {
|
||||
croak("%s", e.what());
|
||||
}
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
|
||||
SV *
|
||||
StoreWrapper::addToStore(char * srcPath, int recursive, char * algo)
|
||||
PPCODE:
|
||||
try {
|
||||
auto method = recursive ? FileIngestionMethod::Recursive : FileIngestionMethod::Flat;
|
||||
auto path = THIS->store->addToStore(
|
||||
std::string(baseNameOf(srcPath)),
|
||||
PosixSourceAccessor::createAtRoot(srcPath),
|
||||
method, parseHashAlgo(algo));
|
||||
XPUSHs(sv_2mortal(newSVpv(THIS->store->printStorePath(path).c_str(), 0)));
|
||||
} catch (Error & e) {
|
||||
croak("%s", e.what());
|
||||
}
|
||||
|
||||
|
||||
SV *
|
||||
StoreWrapper::makeFixedOutputPath(int recursive, char * algo, char * hash, char * name)
|
||||
PPCODE:
|
||||
try {
|
||||
auto h = Hash::parseAny(hash, parseHashAlgo(algo));
|
||||
auto method = recursive ? FileIngestionMethod::Recursive : FileIngestionMethod::Flat;
|
||||
auto path = THIS->store->makeFixedOutputPath(name, FixedOutputInfo {
|
||||
.method = method,
|
||||
.hash = h,
|
||||
.references = {},
|
||||
});
|
||||
XPUSHs(sv_2mortal(newSVpv(THIS->store->printStorePath(path).c_str(), 0)));
|
||||
} catch (Error & e) {
|
||||
croak("%s", e.what());
|
||||
}
|
||||
|
||||
|
||||
SV *
|
||||
StoreWrapper::derivationFromPath(char * drvPath)
|
||||
PREINIT:
|
||||
HV *hash;
|
||||
CODE:
|
||||
try {
|
||||
Derivation drv = THIS->store->derivationFromPath(THIS->store->parseStorePath(drvPath));
|
||||
hash = newHV();
|
||||
|
||||
HV * outputs = newHV();
|
||||
for (auto & i : drv.outputsAndOptPaths(*THIS->store)) {
|
||||
hv_store(
|
||||
outputs, i.first.c_str(), i.first.size(),
|
||||
!i.second.second
|
||||
? newSV(0) /* null value */
|
||||
: newSVpv(THIS->store->printStorePath(*i.second.second).c_str(), 0),
|
||||
0);
|
||||
}
|
||||
hv_stores(hash, "outputs", newRV((SV *) outputs));
|
||||
|
||||
AV * inputDrvs = newAV();
|
||||
for (auto & i : drv.inputDrvs.map)
|
||||
av_push(inputDrvs, newSVpv(THIS->store->printStorePath(i.first).c_str(), 0)); // !!! ignores i->second
|
||||
hv_stores(hash, "inputDrvs", newRV((SV *) inputDrvs));
|
||||
|
||||
AV * inputSrcs = newAV();
|
||||
for (auto & i : drv.inputSrcs)
|
||||
av_push(inputSrcs, newSVpv(THIS->store->printStorePath(i).c_str(), 0));
|
||||
hv_stores(hash, "inputSrcs", newRV((SV *) inputSrcs));
|
||||
|
||||
hv_stores(hash, "platform", newSVpv(drv.platform.c_str(), 0));
|
||||
hv_stores(hash, "builder", newSVpv(drv.builder.c_str(), 0));
|
||||
|
||||
AV * args = newAV();
|
||||
for (auto & i : drv.args)
|
||||
av_push(args, newSVpv(i.c_str(), 0));
|
||||
hv_stores(hash, "args", newRV((SV *) args));
|
||||
|
||||
HV * env = newHV();
|
||||
for (auto & i : drv.env)
|
||||
hv_store(env, i.first.c_str(), i.first.size(), newSVpv(i.second.c_str(), 0), 0);
|
||||
hv_stores(hash, "env", newRV((SV *) env));
|
||||
|
||||
RETVAL = newRV_noinc((SV *)hash);
|
||||
} catch (Error & e) {
|
||||
croak("%s", e.what());
|
||||
}
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
|
||||
void
|
||||
StoreWrapper::addTempRoot(char * storePath)
|
||||
PPCODE:
|
||||
try {
|
||||
THIS->store->addTempRoot(THIS->store->parseStorePath(storePath));
|
||||
} catch (Error & e) {
|
||||
croak("%s", e.what());
|
||||
}
|
||||
|
||||
|
||||
SV * getBinDir()
|
||||
PPCODE:
|
||||
XPUSHs(sv_2mortal(newSVpv(settings.nixBinDir.c_str(), 0)));
|
||||
|
||||
|
||||
SV * getStoreDir()
|
||||
PPCODE:
|
||||
XPUSHs(sv_2mortal(newSVpv(settings.nixStore.c_str(), 0)));
|
47
src/perl/lib/Nix/Utils.pm
Normal file
47
src/perl/lib/Nix/Utils.pm
Normal file
|
@ -0,0 +1,47 @@
|
|||
package Nix::Utils;
|
||||
|
||||
use utf8;
|
||||
use File::Temp qw(tempdir);
|
||||
|
||||
our @ISA = qw(Exporter);
|
||||
our @EXPORT = qw(checkURL uniq writeFile readFile mkTempDir);
|
||||
|
||||
$urlRE = "(?: [a-zA-Z][a-zA-Z0-9\+\-\.]*\:[a-zA-Z0-9\%\/\?\:\@\&\=\+\$\,\-\_\.\!\~\*]+ )";
|
||||
|
||||
sub checkURL {
|
||||
my ($url) = @_;
|
||||
die "invalid URL '$url'\n" unless $url =~ /^ $urlRE $ /x;
|
||||
}
|
||||
|
||||
sub uniq {
|
||||
my %seen;
|
||||
my @res;
|
||||
foreach my $name (@_) {
|
||||
next if $seen{$name};
|
||||
$seen{$name} = 1;
|
||||
push @res, $name;
|
||||
}
|
||||
return @res;
|
||||
}
|
||||
|
||||
sub writeFile {
|
||||
my ($fn, $s) = @_;
|
||||
open TMP, ">$fn" or die "cannot create file '$fn': $!";
|
||||
print TMP "$s" or die;
|
||||
close TMP or die;
|
||||
}
|
||||
|
||||
sub readFile {
|
||||
local $/ = undef;
|
||||
my ($fn) = @_;
|
||||
open TMP, "<$fn" or die "cannot open file '$fn': $!";
|
||||
my $s = <TMP>;
|
||||
close TMP or die;
|
||||
return $s;
|
||||
}
|
||||
|
||||
sub mkTempDir {
|
||||
my ($name) = @_;
|
||||
return tempdir("$name.XXXXXX", CLEANUP => 1, DIR => $ENV{"TMPDIR"} // $ENV{"XDG_RUNTIME_DIR"} // "/tmp")
|
||||
|| die "cannot create a temporary directory";
|
||||
}
|
59
src/perl/lib/Nix/meson.build
Normal file
59
src/perl/lib/Nix/meson.build
Normal file
|
@ -0,0 +1,59 @@
|
|||
# Nix-Perl Scripts
|
||||
#============================================================================
|
||||
|
||||
|
||||
|
||||
# Sources
|
||||
#-------------------------------------------------
|
||||
|
||||
nix_perl_store_xs = files('Store.xs')
|
||||
|
||||
nix_perl_scripts = files(
|
||||
'CopyClosure.pm',
|
||||
'Manifest.pm',
|
||||
'SSH.pm',
|
||||
'Store.pm',
|
||||
'Utils.pm',
|
||||
)
|
||||
|
||||
foreach f : nix_perl_scripts
|
||||
fs.copyfile(f)
|
||||
endforeach
|
||||
|
||||
|
||||
# Targets
|
||||
#---------------------------------------------------
|
||||
|
||||
nix_perl_scripts += configure_file(
|
||||
output : 'Config.pm',
|
||||
input : 'Config.pm.in',
|
||||
configuration : nix_perl_conf,
|
||||
)
|
||||
|
||||
nix_perl_store_cc = custom_target(
|
||||
'Store.cc',
|
||||
output : 'Store.cc',
|
||||
input : nix_perl_store_xs,
|
||||
command : [xsubpp, '@INPUT@', '-output', '@OUTPUT@'],
|
||||
)
|
||||
|
||||
# Build Nix::Store Library
|
||||
#-------------------------------------------------
|
||||
nix_perl_store_lib = library(
|
||||
'Store',
|
||||
sources : nix_perl_store_cc,
|
||||
name_prefix : '',
|
||||
install : true,
|
||||
install_mode : 'rwxr-xr-x',
|
||||
install_dir : join_paths(nix_perl_install_dir, 'auto', 'Nix', 'Store'),
|
||||
dependencies : nix_perl_store_dep_list,
|
||||
)
|
||||
|
||||
|
||||
# Install Scripts
|
||||
#---------------------------------------------------
|
||||
install_data(
|
||||
nix_perl_scripts,
|
||||
install_mode : 'rw-r--r--',
|
||||
install_dir : join_paths(nix_perl_install_dir,'Nix'),
|
||||
)
|
160
src/perl/meson.build
Normal file
160
src/perl/meson.build
Normal file
|
@ -0,0 +1,160 @@
|
|||
# Nix-Perl Meson build
|
||||
#============================================================================
|
||||
|
||||
|
||||
# init project
|
||||
#============================================================================
|
||||
project (
|
||||
'nix-perl',
|
||||
'cpp',
|
||||
version : files('.version'),
|
||||
meson_version : '>= 1.1',
|
||||
license : 'LGPL-2.1-or-later',
|
||||
)
|
||||
|
||||
# setup env
|
||||
#-------------------------------------------------
|
||||
fs = import('fs')
|
||||
cpp = meson.get_compiler('cpp')
|
||||
nix_perl_conf = configuration_data()
|
||||
nix_perl_conf.set('PACKAGE_VERSION', meson.project_version())
|
||||
|
||||
|
||||
# set error arguments
|
||||
#-------------------------------------------------
|
||||
error_args = [
|
||||
'-Wno-pedantic',
|
||||
'-Wno-non-virtual-dtor',
|
||||
'-Wno-unused-parameter',
|
||||
'-Wno-variadic-macros',
|
||||
'-Wdeprecated-declarations',
|
||||
'-Wno-missing-field-initializers',
|
||||
'-Wno-unknown-warning-option',
|
||||
'-Wno-unused-variable',
|
||||
'-Wno-literal-suffix',
|
||||
'-Wno-reserved-user-defined-literal',
|
||||
'-Wno-duplicate-decl-specifier',
|
||||
'-Wno-pointer-bool-conversion',
|
||||
]
|
||||
|
||||
add_project_arguments(
|
||||
cpp.get_supported_arguments(error_args),
|
||||
language : 'cpp',
|
||||
)
|
||||
|
||||
|
||||
# set install directories
|
||||
#-------------------------------------------------
|
||||
prefix = get_option('prefix')
|
||||
libdir = join_paths(prefix, get_option('libdir'))
|
||||
|
||||
# Dependencies
|
||||
#============================================================================
|
||||
|
||||
# Required Programs
|
||||
#-------------------------------------------------
|
||||
xz = find_program('xz')
|
||||
xsubpp = find_program('xsubpp')
|
||||
perl = find_program('perl')
|
||||
curl = find_program('curl')
|
||||
yath = find_program('yath', required : false)
|
||||
|
||||
# Required Libraries
|
||||
#-------------------------------------------------
|
||||
bzip2_dep = dependency('bzip2')
|
||||
curl_dep = dependency('libcurl')
|
||||
libsodium_dep = dependency('libsodium')
|
||||
|
||||
nix_store_dep = dependency('nix-store')
|
||||
|
||||
|
||||
# Finding Perl Headers is a pain. as they do not have
|
||||
# pkgconfig available, are not in a standard location,
|
||||
# and are installed into a version folder. Use the
|
||||
# Perl binary to give hints about perl include dir.
|
||||
#-------------------------------------------------
|
||||
perl_archname = run_command(
|
||||
perl, '-e', 'use Config; print $Config{archname};', check: true).stdout()
|
||||
perl_version = run_command(
|
||||
perl, '-e', 'use Config; print $Config{version};', check: true).stdout()
|
||||
perl_archlibexp = run_command(
|
||||
perl, '-e', 'use Config; print $Config{archlibexp};', check: true).stdout()
|
||||
perl_site_libdir = run_command(
|
||||
perl, '-e', 'use Config; print $Config{installsitearch};', check: true).stdout()
|
||||
nix_perl_install_dir = join_paths(
|
||||
libdir, 'perl5', 'site_perl', perl_version, perl_archname)
|
||||
|
||||
|
||||
# print perl hints for logs
|
||||
#-------------------------------------------------
|
||||
message('Perl archname: @0@'.format(perl_archname))
|
||||
message('Perl version: @0@'.format(perl_version))
|
||||
message('Perl archlibexp: @0@'.format(perl_archlibexp))
|
||||
message('Perl install site: @0@'.format(perl_site_libdir))
|
||||
message('Assumed Nix-Perl install dir: @0@'.format(nix_perl_install_dir))
|
||||
|
||||
# Now find perl modules
|
||||
#-------------------------------------------------
|
||||
perl_check_dbi = run_command(
|
||||
perl,
|
||||
'-e', 'use DBI; use DBD::SQLite;',
|
||||
'-I@0@'.format(get_option('dbi_path')),
|
||||
'-I@0@'.format(get_option('dbd_sqlite_path')),
|
||||
check: true
|
||||
)
|
||||
|
||||
if perl_check_dbi.returncode() == 2
|
||||
error('The Perl modules DBI and/or DBD::SQLite are missing.')
|
||||
else
|
||||
message('Found Perl Modules: DBI, DBD::SQLite.')
|
||||
endif
|
||||
|
||||
|
||||
|
||||
# declare perl dependency
|
||||
#-------------------------------------------------
|
||||
perl_dep = declare_dependency(
|
||||
dependencies : cpp.find_library(
|
||||
'perl',
|
||||
has_headers : [
|
||||
join_paths(perl_archlibexp, 'CORE', 'perl.h'),
|
||||
join_paths(perl_archlibexp, 'CORE', 'EXTERN.h')],
|
||||
dirs : [
|
||||
join_paths(perl_archlibexp, 'CORE'),
|
||||
],
|
||||
),
|
||||
include_directories : join_paths(perl_archlibexp, 'CORE'),
|
||||
)
|
||||
|
||||
# declare dependencies
|
||||
#-------------------------------------------------
|
||||
nix_perl_store_dep_list = [
|
||||
perl_dep,
|
||||
bzip2_dep,
|
||||
curl_dep,
|
||||
libsodium_dep,
|
||||
nix_store_dep,
|
||||
]
|
||||
|
||||
# # build
|
||||
# #-------------------------------------------------
|
||||
lib_dir = join_paths('lib', 'Nix')
|
||||
subdir(lib_dir)
|
||||
|
||||
if get_option('tests').enabled()
|
||||
yath_rc_conf = configuration_data()
|
||||
yath_rc_conf.set('lib_dir', lib_dir)
|
||||
yath_rc = configure_file(
|
||||
output : '.yath.rc',
|
||||
input : '.yath.rc.in',
|
||||
configuration : yath_rc_conf,
|
||||
)
|
||||
subdir('t')
|
||||
test(
|
||||
'nix-perl-test',
|
||||
yath,
|
||||
args : ['test'],
|
||||
workdir : meson.current_build_dir(),
|
||||
depends : [nix_perl_store_lib],
|
||||
)
|
||||
endif
|
27
src/perl/meson.options
Normal file
27
src/perl/meson.options
Normal file
|
@ -0,0 +1,27 @@
|
|||
# Nix-Perl build options
|
||||
#============================================================================
|
||||
|
||||
|
||||
# compiler args
|
||||
#============================================================================
|
||||
|
||||
option(
|
||||
'tests',
|
||||
type : 'feature',
|
||||
value : 'disabled',
|
||||
description : 'run nix-perl tests')
|
||||
|
||||
|
||||
# Location of Perl Modules
|
||||
#============================================================================
|
||||
option(
|
||||
'dbi_path',
|
||||
type : 'string',
|
||||
value : '/usr',
|
||||
description : 'path to perl::dbi')
|
||||
|
||||
option(
|
||||
'dbd_sqlite_path',
|
||||
type : 'string',
|
||||
value : '/usr',
|
||||
description : 'path to perl::dbd-SQLite')
|
77
src/perl/package.nix
Normal file
77
src/perl/package.nix
Normal file
|
@ -0,0 +1,77 @@
|
|||
{ lib
|
||||
, fileset
|
||||
, stdenv
|
||||
, perl
|
||||
, perlPackages
|
||||
, meson
|
||||
, ninja
|
||||
, pkg-config
|
||||
, nix-store
|
||||
, curl
|
||||
, bzip2
|
||||
, xz
|
||||
, boost
|
||||
, libsodium
|
||||
, darwin
|
||||
, versionSuffix ? ""
|
||||
}:
|
||||
|
||||
perl.pkgs.toPerlModule (stdenv.mkDerivation (finalAttrs: {
|
||||
pname = "nix-perl";
|
||||
version = lib.fileContents ./.version + versionSuffix;
|
||||
|
||||
src = fileset.toSource {
|
||||
root = ./.;
|
||||
fileset = fileset.unions ([
|
||||
./MANIFEST
|
||||
./lib
|
||||
./meson.build
|
||||
./meson.options
|
||||
] ++ lib.optionals finalAttrs.doCheck [
|
||||
./.yath.rc.in
|
||||
./t
|
||||
]);
|
||||
};
|
||||
|
||||
nativeBuildInputs = [
|
||||
meson
|
||||
ninja
|
||||
pkg-config
|
||||
];
|
||||
|
||||
buildInputs = [
|
||||
nix-store
|
||||
curl
|
||||
bzip2
|
||||
xz
|
||||
perl
|
||||
boost
|
||||
]
|
||||
++ lib.optional (stdenv.isLinux || stdenv.isDarwin) libsodium
|
||||
++ lib.optional stdenv.isDarwin darwin.apple_sdk.frameworks.Security;
|
||||
|
||||
# `perlPackages.Test2Harness` is marked broken for Darwin
|
||||
doCheck = !stdenv.isDarwin;
|
||||
|
||||
nativeCheckInputs = [
|
||||
perlPackages.Test2Harness
|
||||
];
|
||||
|
||||
preConfigure =
|
||||
# "Inline" .version so its not a symlink, and includes the suffix
|
||||
''
|
||||
echo ${finalAttrs.version} > .version
|
||||
'';
|
||||
|
||||
mesonFlags = [
|
||||
(lib.mesonOption "dbi_path" "${perlPackages.DBI}/${perl.libPrefix}")
|
||||
(lib.mesonOption "dbd_sqlite_path" "${perlPackages.DBDSQLite}/${perl.libPrefix}")
|
||||
(lib.mesonEnable "tests" finalAttrs.doCheck)
|
||||
];
|
||||
|
||||
mesonCheckFlags = [
|
||||
"--print-errorlogs"
|
||||
];
|
||||
|
||||
enableParallelBuilding = true;
|
||||
}))
|
13
src/perl/t/init.t
Normal file
13
src/perl/t/init.t
Normal file
|
@ -0,0 +1,13 @@
|
|||
use strict;
|
||||
use warnings;
|
||||
use Test2::V0;
|
||||
|
||||
use Nix::Store;
|
||||
|
||||
my $s = new Nix::Store("dummy://");
|
||||
|
||||
my $res = $s->isValidPath("/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar");
|
||||
|
||||
ok(!$res, "should not have path");
|
||||
|
||||
done_testing;
|
15
src/perl/t/meson.build
Normal file
15
src/perl/t/meson.build
Normal file
|
@ -0,0 +1,15 @@
|
|||
# Nix-Perl Tests
|
||||
#============================================================================
|
||||
|
||||
|
||||
# src
|
||||
#---------------------------------------------------
|
||||
|
||||
nix_perl_tests = files(
|
||||
'init.t',
|
||||
)
|
||||
|
||||
|
||||
foreach f : nix_perl_tests
|
||||
fs.copyfile(f)
|
||||
endforeach
|
Loading…
Add table
Add a link
Reference in a new issue