Child pages
  • Building an IPS manifest
Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 4 Next »

When I build a third-party package, I publish it into an IPS repo using the following steps:

1. Put the metadata into a file called "metadata". Most of this information can come from a template file, eg a build Makefile or something. For example:

set name=pkg.name value=my-package
set name=pkg.description value="My Great Package"
set name=description value="My Great Package"
license /path/to/license/file license=BSD

2. Put the package dependencies into a file called "deps". This is hard to generate automatically, one (good, but incomplete) approach might be to use ldd to find all the linked libraries and then use pkg search to find which package provides them. I tend to calculate the dependencies once and commit them as part of the build. For example:

depend fmri=pkg:/library/zlib type=require
depend fmri=pkg:/system/library/math type=require
depend fmri=pkg:/library/expat type=require

3. Do a "make install" into a local install root, for example:

DESTDIR=insroot make install

4. Move to the install root and run the attached script to generate the file "payload". For example:

perl dir2manifest.pl usr > ../payload

Here's the script:

#!/usr/bin/perl -w

# Scan a directory tree and output manifest information that can be passed
# to pkgsend include
#
# Currently this just detects directories, files, hardlinks and symlinks.

use strict;
use File::Spec;

my $dir = shift || die "usage: $0 dir\n";

if ($dir =~ m{^/}) {
    $dir =~ s{^/}{};
    chdir "/";
}

# Calculate the relative path from src to dst
# File::Spec->abs2rel($dstfile, $srcdir)
sub relativepath {
    my (@src) = File::Spec->splitdir(shift);
    my $dstfile = shift;
    pop @src;
    my $srcdir = File::Spec->catdir(@src);
    return File::Spec->abs2rel($dstfile, $srcdir);
}

my @dirs;
my @files;
my @links;
my @symlinks;

# Core directories - ideally we should depend on some package
# that provides these, but that package doesn't exist right now.
# Until then just make sure they are root:sys instead of root:bin
my %coredir;
foreach my $f qw(usr usr/share usr/gnu/share opt etc) { $coredir{$f} = 1; }
open FIND, "find -H $dir -type d -print|";
while (<FIND>) {
    chomp;
    if (exists $coredir{$_}) {
	push @dirs, "dir mode=0755 owner=root group=sys path=$_";
    } else {
	push @dirs, "dir mode=0755 owner=root group=bin path=$_";
    }
}
close FIND or die "$0: error running find:$!";

# Indexed by inode, this lets us detect and recreate hard linked files
my %installed;
open FIND, "find -H $dir -type f -print|";
while (<FIND>) {
    chomp;
    next if  m{(^|/)(\.packlist|perllocal.pod)$};
    my ($inode) = (stat $_)[1];
    if (exists $installed{$inode}) {
	my $target = relativepath($_, $installed{$inode});
	push @links, "hardlink path=$_ target=$target";
    } else {
	my $mode = '0444';
	if (-x $_) {
	    $mode = '0555';
	} else {
	    # There must be a better way than running file(1)
	    my $t = `file $_`;
	    $mode = '0555' if $t =~ m{:\tELF};
	}
	push @files, "file $_ mode=$mode owner=root group=bin path=$_";
	$installed{$inode} = $_;
    }
}
close FIND or die "$0: error running find:$!";

open FIND, "find -H $dir -type l -print|";
while (<FIND>) {
    chomp;
    my $target = relativepath($_, readlink $_);
    push @symlinks, "link path=$_ target=$target";
}
close FIND or die "$0: error running find:$!";

# TODO: look for other installable things

print join "\n", @dirs, (sort @files), (sort @links), (sort @symlinks), '';

5. Join "metadata", "deps", and "payload" together into "manifest":

cat metadata deps payload > manifest

6. Upload the package:

eval `pkgsend -s $REPO open "my-package@1.7"`
pkgsend -s $REPO include -d insroot manifest
if [ $? != 0 ]; then
   pkgsend -s $REPO close -A
else
   pkgsend -s $REPO close
fi
  • No labels