From a46640c36e791cd91f9dfc335d26ab2ff7af657c Mon Sep 17 00:00:00 2001 From: lahiker42 Date: Wed, 1 Oct 2008 00:29:30 +0000 Subject: [PATCH] packaging tape for protobuf-c. git-svn-id: https://protobuf-c.googlecode.com/svn/trunk@69 00440858-1255-0410-a3e6-75ea37f81c3a --- Makefile.am | 12 + configure.ac | 37 +- pkgwriteinfo.in | 25 + scripts/pkgwrite | 3733 ++++++++++++++++++++++++++++ src/google/protobuf-c/protobuf-c.c | 13 - 5 files changed, 3806 insertions(+), 14 deletions(-) create mode 100644 pkgwriteinfo.in create mode 100755 scripts/pkgwrite diff --git a/Makefile.am b/Makefile.am index af437a6..99eb9bd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1 +1,13 @@ SUBDIRS = src + +EXTRA_DIST = scripts pkgwriteinfo.in + +# --- packages --- +DEBARCH = @ARCH@ +deb: + test -r protobuf-c-@VERSION@.tar.gz || $(MAKE) dist + $(srcdir)/scripts/pkgwrite --format=debian \ + --tarball=protobuf-c-@VERSION@.tar.gz \ + --output=protobuf-c-packages \ + --pkgwriteinfo-file=pkgwriteinfo \ + --arch=$(DEBARCH) diff --git a/configure.ac b/configure.ac index ba9d75d..40b7a22 100644 --- a/configure.ac +++ b/configure.ac @@ -7,5 +7,40 @@ AC_PROG_CC AC_PROG_CXX AC_PROG_LIBTOOL AC_PATH_PROG(PROTOC, protoc) +AC_CHECK_HEADERS(inttypes.h) -AC_OUTPUT( Makefile src/Makefile src/test/Makefile ) +dnl ------ define IS_LITTLE_ENDIAN ------ +knows_endianness=0 +AC_CHECK_HEADERS([endian.h], [has_endian_h=1; knows_endianness=1], [has_endian_h=0]) +if test $knows_endianness = 1 ; then + AC_TRY_COMPILE([#include ], [ + switch (1) { case __LITTLE_ENDIAN: break; + case __BYTE_ORDER: break; } ], + [is_little_endian=0], [is_little_endian=1]) +else + AC_CHECK_HEADERS([mach/endian.h], [has_mach_endian_h=1; knows_endianness=1], [has_mach_endian_h=0]) + AC_TRY_COMPILE([#include ],[ + switch (1) { case __LITTLE_ENDIAN: break; + case __BYTE_ORDER: break; } + ], + [is_little_endian=0], [is_little_endian=1]) + if test $knows_endianness = 0; then + AC_MSG_CHECKING([for little-endianness via runtime check]) + AC_RUN_IFELSE([#include + int main() { + uint32_t v = 0x01020304; + return memcmp (&v, "\4\3\2\1", 4) == 0 ? 0 : 1; + } + ], [is_little_endian=1; result=yes], [is_little_endian=0; result=no]) + AC_MSG_RESULT($result) + fi +fi + +if test $is_little_endian = 1; then + echo "Your system IS little-endian" 1>&2 +else + echo "Your system IS NOT little-endian" 1>&2 +fi +AC_DEFINE_UNQUOTED(IS_LITTLE_ENDIAN, $is_little_endian) + +AC_OUTPUT( Makefile src/Makefile src/test/Makefile pkgwriteinfo ) diff --git a/pkgwriteinfo.in b/pkgwriteinfo.in new file mode 100644 index 0000000..694f68e --- /dev/null +++ b/pkgwriteinfo.in @@ -0,0 +1,25 @@ +Package: protobuf-c +Section: libs +Group: Development/Libraries +Priority: low +Author: David Benson +Packager: Dave Benson +Packager-Email: daveb@ffem.org +Version: @VERSION@ +Release: 1 +License: private +Synopsis: C bindings for protocol-buffers +Description: protobuf or protocol-buffers is google's + language for describing extensible binary data formats. + +Build: normal + +Target: {MAIN} +Files: /usr/bin/protoc-c +Files: /usr/include/google/protobuf-c/*.h +Files: /usr/lib/libprotobuf-c.* +Which-Build: normal +Synopsis: C bindings for protocol-buffers +Description: protobuf or protocol-buffers is google's + language for describing extensible binary data formats. + diff --git a/scripts/pkgwrite b/scripts/pkgwrite new file mode 100755 index 0000000..02acd8a --- /dev/null +++ b/scripts/pkgwrite @@ -0,0 +1,3733 @@ +#! /usr/bin/perl -w + +# pkgwrite 0.0.8: Build native packages from source code +# and pkgwriteinfo files. +# +# This file contains POD documentation: try `perldoc pkgwrite' for help. +# +# Copyright Dave Benson , 2000-2001. + +# Requirements: +# perl 5.004 or higher, +# and native packaging tools for the output packages, that is: +# For redhat `rpm'. And for debian `dpkg' and `dpkg-buildpackage'. + +# pkgwrite +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public +# License along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# --- +# +# This software may also be licensed under the terms of +# the GNU Lesser General Public License, version 2 or higher. +# (This is so that the pkgwrite script may be legally included +# with LGPL'd software.) +# +# --- +# +# Furthermore, the packages and intermediary files generated +# with pkgwrite are specifically excluded from the terms of +# this license. Hence, pkgwrite adds no additional restrictions to +# the package's licensing, but they must comply with the +# licensing on the other source code, libraries and tools used +# to generate the package. + +# Finally, this contains the rpmrc that is distributed with +# rpm 4.0: CVS entry: rpmrc.in 2.28.2.2 2000/09/13. +# That file had no copyright info. The RPM source code, +# like pkgwrite, is licenced under both the LGPL and the GPL. + +require 5.004; +use Carp; +use Config; # for signal names +use Symbol qw(gensym); + +# Table of Contents of the Source Code. +# Section 0: Initialization & Configuration. +# Section 1: Helper functions. +# Section 2: parse_pkgwriteinfo_file: Return package information. +# Section 3: Verify that a package is correctly formed. +# Section 4: ChangeLog parsing code. +# Section 5: Redhat tape. +# Section 6: Debian tape. +# Section 7: High-level API: Make a package from a tarball. +# Section 8: Usage message. +# Section 9: Main program. +# Section 10: POD Documention. + + +#===================================================================== +# Section 0: Initialization & Configuration. +#===================================================================== +# --- function prototypes --- +sub dump_list ($); # print a builtin table to stdout +sub initialize_packaging_tables (); # redhat/debian package mgnt specifics +sub initialize_signal_tables (); # signal number to id mappings +sub add_automatic_conflicts ($); # add conflicts if file sets intersect + +# --- global initialization --- +$initial_dir = `pwd`; +chomp ($initial_dir); +initialize_packaging_tables (); +initialize_signal_tables (); +$PKGWRITE_VERSION = '0.0.8'; + +# --- configuration --- +# set to 0 for debugging: don't delete working directories. +$do_cleanup = 1; +$do_cleanup = $ENV{DO_CLEANUP} if defined($ENV{DO_CLEANUP}); + +# force the changelog to refer to this exact version. +$strict_changelog_checking = 1; + +# whether to perform an extra packaging sanity test phase. +$do_sanity_check = 1; + +# for the changelog, which distribution should be listed? +# XXX: hm, what if a single package changes dist? look at +# packages in stable for an example, i guess... (actually +# this isn't a problem if you *always* use pkgwrite, +# but that's annoying.) +$debian_dist = 'unstable'; + +# options to pass to ./configure (these differ due on different +# distros due to different standard directories -- most likely, +# the redhat usage will converge to the debian/fhsstnd.) +@common_config_flags = ( '--sysconfdir=/etc', + '--prefix=/usr', + '--quiet' ); +$redhat_config_flags = join(' ', + @common_config_flags + ); +$debian_config_flags = join(' ', + @common_config_flags, + '--mandir=/usr/share/man', + '--infodir=/usr/share/info', + '--datadir=/usr/share'); + +# Directories where shared libraries are kept: if we install a library +# in any of these directories we need to run `ldconfig'. +@ldconfig_dirs = ( '/usr/lib', '/lib', '/usr/X11/lib', + '/usr/X11R6/lib', '/usr/lib/X11' ); + +# --- configuration --- +# gzip/gunzip programs + arguments that can act as filters. +$gzip = 'gzip -9 -c -'; +$gunzip = 'gzip -d -c -'; + +# flags which cause `tar' to extract a file to stdout from an archive. +# usage: tar $untar_to_stdout ARCHIVE FILE +# or zcat ARCHIVE | tar $untar_to_stdout - FILE +# (perhaps this isn't portable?) +$untar_to_stdout = "xOf"; + +# Location of the optional system-wide rpmrc file. +$system_rpmrc_file = "/var/lib/rpm/rpmrc"; + +# Standard build architectures. Yes, hardcoding this sucks... +$default_redhat_archs = "i386 alpha sparc sparc64"; + +# --- configure-time substitutions (!) --- +# The RHS of these assignments is a autoconf-substitution; +# in pkgwrite.in there are autoconf AC_SUBST()d variables, +# and in pkgwrite these is a 0 or 1 to indicate +# whether `configure' detected a suitable version of these +# packaging systems. +$has_rpm_support = 1; +$has_dpkg_support = 1; + +# --- build hash tables of fixed strings --- +sub initialize_packaging_tables () +{ + # --- Debian packaging values --- + # valid values for the Priority: field. + for (qw(required important standard optional extra)) + { + $DPKG_PRIORITY_LEVELS{$_} = $_; + } + + # valid values for the Section: field. + for (qw(admin base comm contrib devel doc editors electronics + games graphics hamradio interpreters libs mail + math misc net news non-free oldlibs otherosfs + shells sound tex text utils web x11)) + { + $DPKG_SECTIONS{$_} = $_; + } + + # --- Redhat packaging values --- + for ('Amusements/Games', 'Amusements/Graphics', 'Applications/Archiving', + 'Applications/Communications', 'Applications/Databases', + 'Applications/Editors', 'Applications/Emulators', + 'Applications/Engineering', 'Applications/File', + 'Applications/Internet', 'Applications/Multimedia', + 'Applications/Productivity', 'Applications/Publishing', + 'Applications/System', 'Applications/Text', + 'Development/Debuggers', 'Development/Languages', + 'Development/Libraries', 'Development/System', + 'Development/Tools', 'Documentation', + 'System Environment/Base', 'System Environment/Daemons', + 'System Environment/Kernel', 'System Environment/Libraries', + 'System Environment/Shells', 'User Interface/Desktops', + 'User Interface/X', 'User Interface/X Hardware Support') + { + $RPM_GROUPS{lc($_)} = $_; + } +} + +# initialize_signal_tables: Build a lookup-table mapping from +# signal number to the signal's name (the part which follows +# SIG in the C macro: for example: HUP, KILL, TERM, SEGV, ABRT, etc) +# based on perl's configuration. +sub initialize_signal_tables () +{ + defined $Config{sig_name} || die "No sigs?"; + my $i = 0; + foreach $name (split(' ', $Config{sig_name})) + { + #$signo{$name} = $i; + $signame[$i] = $name; + $i++; + } +} + + +#===================================================================== +# Section 1: Helper functions. +#===================================================================== +# --- run: run a program or die --- +sub run($) +{ + my $command = $_[0]; + print STDERR " running: $command\n"; + my $rv = system ($command); + if ($rv != 0) + { + if ($rv < 256) + { + die "command `$command' killed by signal " . $signame[$rv]; + } + else + { + die "command `$command' exited with status " . ($rv >> 8); + } + } +} + +# safe_mkdir: make a directory if it doesn't exist, +# or die with an error message. +sub safe_mkdir($) +{ + croak "safe_mkdir(undef) called" unless defined $_[0]; + mkdir ($_[0], 0755) or croak "mkdir($_[0]) failed"; +} + +# check_field(OBJECT, HASH-ENTRY, FIELD) +# +# Verify that OBJECT->{HASH-ENTRY} is value, or die with a diagnostic. +sub check_field($$$) +{ + if (!defined($_[0]->{$_[1]})) + { + my $type = $_[0]->{type}; + my $name = $_[0]->{name}; + $name = "(unknown name)" unless defined $name; + $type = "packaging" unless defined $type; + die "field `$_[2]' required in $type for $name"; + } +} + +# undef_or_empty: Test if an array reference is undefined or of zero length. +sub undef_or_empty ($) +{ + return 1 unless defined $_[0]; + my $array = $_[0]; + return 1 unless scalar (@$array); + return 0; +} + +# write_entries: Write out optional field entries from a hash-table $object. +# $field_list and $hash_entries are parallel lists. +sub write_entries ($$$$) +{ + my ($fh, $object, $field_list, $hash_entries) = @_; + my $count = scalar(@$field_list); + my $i; + for ($i = 0; $i < $count; $i++) + { + my $hentry = $hash_entries->[$i]; + my $fname = $field_list->[$i]; + if (defined($object->{$hentry})) + { + print $fh $fname, ": ", $object->{$hentry}, "\n"; + } + } +} + +# make a full path from a possibly relative path specified +# on the command line. +sub make_absolute($) +{ + if ($_[0] !~ m,^/,) + { + return "$initial_dir/" . $_[0]; + } + else + { + return $_[0]; + } +} + +# write_list_to_file(\@LIST, $FNAME) +# Write each element of LIST on a separate line to a file named $FNAME. +sub write_list_to_file ($$) +{ + my ($list, $fname) = @_; + open Z, ">$fname" or die "write_list_to_file: couldn't create $fname"; + for (@$list) + { + print Z "$_\n"; + } + close Z; +} + +# make_file: Create a file with the contents of a given string. +sub make_file ($$) +{ + my ($fname, $contents) = @_; + open Z, ">$fname" or die "make_file: couldn't create $fname"; + print Z $contents; + close Z; +} + +# maybe_print_make_dirs(\%MADE_DIRECTORIES, $FILE_HANDLE, $DIRECTORY): +# +# Print a script to make the directories and subdirectories +# of $dir, recording made directories in MADE_DIRECTORIES +# to avoid duplication. +sub maybe_print_make_dirs ($$$) +{ + my ($made_dirs, $fh, $dir) = @_; + my $cur = ''; + $cur = ($dir =~ s,^/+,,) ? '' : '.'; + for (split /\//, $dir) + { + $cur .= "/$_"; + if (!defined($made_dirs->{$cur})) + { + print $fh "\ttest -d $cur || mkdir $cur\n"; + $made_dirs->{$cur} = 1; + } + } +} + +# string_to_boolean: take the normal yes/no/false/true/0/1 mess +# and output 0, 1, or undef. +sub string_to_boolean ($) +{ + my $s = $_[0]; + return 0 if ($s eq '0' + || $s eq 'f' || $s eq 'F' + || $s eq 'n' || $s eq 'N' + || $s eq 'no' || $s eq 'NO' + || $s eq 'false' || $s eq 'FALSE'); + return 1 if ($s eq '1' + || $s eq 't' || $s eq 'T' + || $s eq 'y' || $s eq 'Y' + || $s eq 'yes' || $s eq 'YES' + || $s eq 'true' || $s eq 'TRUE'); + return undef; +} + +# Read lines from FILE-HEADER until a non-empty line is encountered. +# Return undef if no nonempty line is encountered. +sub skip_empty_lines ($) +{ + my $fh = $_[0]; + while (<$fh>) + { + chomp; + return $_ if /\S/; + } + return undef; +} + +# From the basename of a manpage (eg zshall.1.gz or exit.3tcl) +# find the man-section (resp. 1 or 3). +sub get_manpage_section ($) +{ + my $b = $_[0]; + $b =~ s/\.gz$//; + $b =~ s/\.bz2$//; + $b =~ s/\.Z$//; + $b =~ s/^.*\.//; + if ($b =~ /^(\d+)/) + { + return $b; + } + die "Couldn't figure out what man-section $_[0] was in"; +} + + + +# --- Creating and destroying the temporary working area --- +sub get_tmp_dir() { + return $ENV{'TMPDIR'} || $ENV{'TEMPDIR'} || "/tmp"; +} +sub get_work_dir () +{ + # Make a working directory. + if (!defined($global_work_dir)) + { + $global_work_dir = get_tmp_dir() . "/mkpkg-$$-$ENV{USER}"; + mkdir ($global_work_dir, 0755) + or die "couldn't make tmp directory $global_work_dir"; + } + $SIG{__DIE__} = sub { remove_work_dir (); }; + return $global_work_dir; +} + +sub remove_work_dir () +{ + if (defined($global_work_dir)) + { + $SIG{__DIE__} = sub {}; + run ("rm -rf $global_work_dir") if $do_cleanup; + undef($global_work_dir); + } +} + + + +#===================================================================== +# Section 2: parse_pkgwriteinfo_file: Return package information. +#===================================================================== +# DATA STRUCTURES +# +# The return value is a Package, a hash-table with the following fields: +# name => NAME-OF-PACKAGE +# output_name => NAME-OF-PACKAGE [sometimes mangled for co-installation] +# section => DEBIAN-SECTION +# group => REDHAT-GROUP +# priority => DEBIAN-PRIORITY +# author => AUTHOR +# builds => \@BUILDS +# targets => \@TARGETS +# url => URL +# summary => SUMMARY +# license => LICENSE +# version => UPSTREAM-VERSION +# release => RELEASE +# packager => PACKAGER_FULLNAME +# packager_email = PACKAGER_EMAIL +# changelog => CHANGELOG-FILE +# fullname => PACKAGE-VERSION-RELEASE +# upstream_fullname => PACKAGE-VERSION +# needs_noarch => [01] +# needs_arch_specific => [01] +# upstream_is_packager => [01] Whether the packager and author are the same. +# (The default is 1, meaning TRUE) +# changelog_file => changelog.gz or changelog.Debian.gz +# +# Each Target's information is a hash-table with the following fields: +# name => TARGET-NAME [without PACKAGE-, or {MAIN}] +# summary => SUMMARY +# description => DESCRIPTION +# section => DEBIAN-SECTION [default to package's section] +# group => REDHAT-GROUP [default to package's group] +# files => \@PATTERNS +# conffiles => \@PATTERNS +# manpages => \@MANPAGES [just the basename -- no path] +# arch_indep => [01] [whether this target contains compiled code] +# build_name => BUILD-NAME +# build => BUILD +# installed_docs => \@DOCS [docs the package installs] +# source_docs => \@DOCS [docs from the package tarball] +# +# Each Build's information is a hash-table with the following fields: +# name => BUILD-NAME [or {MAIN} for the default] +# configure_flags => FLAGS [addl flags to pass to the configure script] +# configure_envars => ENVARS [NAME=VALUE pairs...] +# make_flags => FLAGS [addl flags to pass to make & make install] +# build_flags => FLAGS [addl flags to pass to make] +# install_flags => FLAGS [addl flags to pass to make install] +# extra_build_targets => \@T [addl `make' targets to build] +# extra_install_targets => \@T [addl `make' targets to install] + +sub parse_pkgwriteinfo_file($) +{ + open PINFO, "$_[0]" or die "couldn't open $_[0]"; + my $package; + my @lines = (); + + # --- + # Break the line up into sections starting with Target, Package, Build, + # and call the appropriate parser on those blocks, + # which are functions named handle_{target,package,build}. + # + # Each of those functions takes a list of lines as arguments + # and returns a hash-table reference of the appropriate {'type'}. + + # Parse the first block, which is always Package:. + while () + { + # Ignore whitespace. + next if /^\s*$/; + + # Ignore comments. + next if /^\s*#/; + + chomp; + $line = $_; + if ($line =~ /^Target:/ || $line =~ /^Build:/) + { + # Clear out the current lines as the main package entry. + $package = handle_package (@lines); + die unless defined $package; + + # This line begins the new block. + @lines = ( $line ); + last; + } + else + { + push @lines, $_; + } + } + while () + { + # Ignore whitespace. + next if /^\s*$/; + + # Ignore comments. + next if /^\s*#/; + + chomp; + $line = $_; + + # Are we are a block boundary? + if (($line =~ /^Target:/) || ($line =~ /^Build:/)) + { + # What type of block did we just complete? + if ($lines[0] =~ /^Target:/) + { + # Parse the Target. + my $target = handle_target (@lines); + die unless defined $target; + $target->{package} = $package; + my $targets = $package->{targets}; + push @$targets, $target; + } + else + { + # Parse the Build. + my $build = handle_build (@lines); + die unless defined $build; + $build->{package} = $package; + my $builds = $package->{builds}; + push @$builds, $build; + + # Compute the name of the src rpm, which will also + # be the prefix for all the other packages. + my $name = $package->{output_name}; + $name .= ("-" . $build->{name}) if ($build->{name} ne '{MAIN}'); + $build->{package_name} = $name; + $build->{package} = $package; + } + @lines = ( $line ); + next; + } + else + { + push @lines, $line; + } + } + if (scalar (@lines) > 0) + { + die "The last block in a pkgwriteinfo file must always be a target" + unless ($lines[0] =~ /^Target:/i); + + $target = handle_target (@lines); + die unless defined $target; + my $targets = $package->{targets}; + push @$targets, $target; + } + { + my $targets = $package->{targets}; + for my $target (@$targets) + { + for my $inherited (qw(group)) + { + if (!defined($target->{$inherited})) + { + $target->{$inherited} = $package->{$inherited} + } + } + } + } + my %BUILDS_BY_NAME = (); + { + my $targets = $package->{targets}; + my $builds = $package->{builds}; + die "malformed package: no targets" if scalar (@$targets) == 0; + die "malformed package: no builds" if scalar (@$builds) == 0; + for (@$builds) + { + $BUILDS_BY_NAME{$_->{name}} = $_; + } + my $needs_noarch = 0; + my $needs_arch_specific = 0; + for my $target (@$targets) + { + my $build = $target->{build_name}; + my $name = $target->{name}; + $build = '{MAIN}' unless defined $build; + if (!defined $BUILDS_BY_NAME{$build}) + { + die "no build for target $name named $build"; + } + if ($target->{arch_indep}) + { + $needs_noarch = 1; + } + else + { + $needs_arch_specific = 1; + } + $target->{build} = $BUILDS_BY_NAME{$build}; + $target->{package_name} = $package->{output_name}; + $target->{package} = $package; + if ($name ne '{MAIN}') + { + $target->{package_name} .= "-$name"; + } + } + $package->{needs_noarch} = $needs_noarch; + $package->{needs_arch_specific} = $needs_arch_specific; + } + + # Add conflicts based purely on `Files:' lists. + add_automatic_conflicts ($package); + return $package; +} + +# --- handle_package: create a $package from a pkgwrite intro --- +sub handle_package +{ + my $package = {}; + $package->{type} = 'package'; + $package->{targets} = []; + $package->{builds} = []; + $package->{upstream_is_packager} = 1; + my $in_description = 0; + for (@_) + { + if ($in_description) + { + if (/^\S/) + { + $in_description = 0; + } + else + { + s/^\s//; + $package->{description} .= "\n$_"; + next; + } + } + + if (/^Package:\s*(.*)/) + { + $package->{name} = $1; + } + elsif (/^Output-Package:\s*(.*)/) + { + $package->{output_name} = $1; + } + elsif (/^Section:\s*(.*)/) + { + $package->{section} = $1; + } + elsif (/^Group:\s*(.*)/) + { + $package->{group} = $1; + } + elsif (/^Priority:\s*(.*)/) + { + $package->{priority} = $1; + } + elsif (/^Home-Page:\s*(.*)/) + { + $package->{home_page} = $1; + } + elsif (/^Source-Url:\s*(.*)/) + { + $package->{url} = $1; + } + elsif (/^Version:\s*(.*)/) + { + $package->{version} = $1; + } + elsif (/^Release:\s*(.*)/) + { + $package->{release} = $1; + } + elsif (/^Change[lL]og:\s*(.*)/) + { + $package->{changelog} = $1; + } + elsif (/^Author:\s*(.*)/) + { + my $author = $1; + $package->{authors} = [] unless defined $package->{authors}; + my $authors = $package->{authors}; + push @$authors, $author; + } + elsif (/^Description:\s*(.*)/) + { + $package->{description} = $1; + $in_description = 1; + } + elsif (/^Synopsis:\s*(.*)/) + { + $package->{summary} = $1; + } + elsif (/^License:\s*(.*)/) + { + $package->{license} = $1; + } + elsif (/^Packager:\s*(.*)/) + { + $package->{packager} = $1; + } + elsif (/^Packager-Email:\s*(.*)/) + { + $package->{packager_email} = $1; + } + elsif (/^Upstream-is-Packager:\s*(.*)/i) + { + $package->{upstream_is_packager} = $1; + } + else + { + chomp; + die "unparsable line in pkgwriteinfo file: $_"; + } + } + + $package->{changelog_file} + = $package->{upstream_is_packager} ? "changelog.gz" : "changelog.Debian.gz"; + + # check that all the needed fields have been found. + check_field ($package, 'name', 'Package'); + check_field ($package, 'section', 'Section'); + check_field ($package, 'version', 'Version'); + check_field ($package, 'release', 'Release'); + check_field ($package, 'priority', 'Priority'); + check_field ($package, 'authors', 'Author'); + check_field ($package, 'description', 'Description'); + check_field ($package, 'summary', 'Synopsis'); + check_field ($package, 'license', 'License'); + check_field ($package, 'packager', 'Packager'); + check_field ($package, 'packager_email', 'Packager-Email'); + + $package->{output_name} = $package->{name} unless defined $package->{output_name}; + $package->{upstream_fullname} = $package->{name} . '-' . $package->{version}; + $package->{fullname} = $package->{output_name} + . '-' . $package->{version} + . '-' . $package->{release}; + $package->{lcname} = lc($package->{name}); + + return $package; +} + +# --- handle_target: create a $target from a pkgwriteinfo entry --- +sub handle_target +{ + my $target = {}; + my $in_description = 0; + $target->{type} = 'target'; + $target->{manpages} = []; + $target->{source_docs} = []; + $target->{installed_docs} = []; + $target->{arch_indep} = 0; + $target->{debian_data} = {}; + + for (@_) + { + if ($in_description) + { + if (/^\S/) + { + $in_description = 0; + } + else + { + s/^\s//; + $target->{description} .= "\n$_"; + next; + } + } + + if (/^Target:\s*(.*)/) + { + $target->{name} = $1; + } + elsif (/^Depends:\s*(.*)/) + { + my $dep = $1; + if (defined($target->{depends})) + { + $target->{depends} = $target->{depends} . ", " . $dep; + } + else + { + $target->{depends} = $dep; + } + } + elsif (/^Redhat-Requires:\s*(.*)/) + { + $target->{redhat_requires} = $1; + } + elsif (/^Conflicts:\s*(.*)/) + { + my $conflict = $1; + if (defined($target->{conflicts})) + { + $target->{conflicts} = $target->{conflicts} . ", " . $conflict; + } + else + { + $target->{conflicts} = $conflict; + } + } + elsif (/^Redhat-Conflicts:\s*(.*)/) + { + $target->{redhat_conflicts} = $1; + } + elsif (/^Synopsis:\s*(.*)/) + { + $target->{summary} = $1; + } + elsif (/^Files:\s*(.*)/) + { + my $pattern = $1; + $target->{files} = [] unless defined $target->{files}; + my $files = $target->{files}; + push @$files, $pattern; + } + elsif (/^Description:\s*(.*)/) + { + $target->{description} = $1; + $in_description = 1; + } + elsif (/^Man-Page:\s*(.*)/) + { + my $manpage = $1; + my $manpages = $target->{manpages}; + push @$manpages, $manpage; + } + elsif (/^Doc:\s*(.*)/) + { + my $doc = $1; + my $docs = $target->{installed_docs}; + push @$docs, $doc; + } + elsif (/^Source-Doc:\s*(.*)/) + { + my $doc = $1; + my $docs = $target->{source_docs}; + push @$docs, $doc; + } + elsif (/^Platform-Independent:\s*(.*)/) + { + $target->{arch_indep} = string_to_boolean ($1); + } + elsif (/^Needs-[lD]dconfig:\s*(.*)/) + { + $target->{needs_ldconfig} = string_to_boolean ($1); + } + elsif (/^Which-Build:\s*(.*)/) + { + $target->{build_name} = $1; + } + else + { + chomp; + die "unparsable line in pkgwriteinfo file: $_"; + } + } + + # check that all the needed fields have been found. + check_field ($target, 'name', 'Target'); + + if (undef_or_empty ($target->{installed_docs}) + && undef_or_empty ($target->{source_docs}) + && undef_or_empty ($target->{files})) + { + die "either Files, Doc, Source-Doc are required but missing in target " + . $target->{name}; + } + + if ($target->{name} ne '{MAIN}') + { + check_field ($target, 'description', 'Description'); + check_field ($target, 'summary', 'Synopsis'); + } + + # --- Figure out other information about this target. --- + + # Figure out if ldconfig must be run after this package + # is installed. + if (!defined ($target->{needs_ldconfig})) + { + my $needs_ldconfig = 0; + my $files = $target->{files}; + $files = [] unless defined $files; + PER_FILE: for my $file (@$files) + { + my $tmp = $file; + $tmp =~ s,/[^/]+$,,; + for my $dir (@ldconfig_dirs) + { + if ($tmp eq $dir) + { + $needs_ldconfig = 1; + last PER_FILE; + } + } + } + $target->{needs_ldconfig} = $needs_ldconfig; + } + + return $target; +} + +# --- handle_build: create a $build from a pkgwriteinfo entry --- +sub handle_build +{ + my $build = {}; + $build->{type} = 'build'; + $build->{extra_build_targets} = []; + $build->{extra_install_targets} = []; + for (@_) + { + if (/^Build:\s*(.*)/) + { + $build->{name} = $1; + } + elsif (/^Configure-Flags:\s*(.*)/) + { + $build->{configure_flags} = $1; + } + elsif (/^Configure-Envars:\s*(.*)/) + { + $build->{configure_envars} = $1; + } + elsif (/^Make-Flags:\s*(.*)/) + { + $build->{make_flags} = $1; + } + elsif (/^Install-Flags:\s*(.*)/) + { + $build->{install_flags} = $1; + } + elsif (/^Build-Flags:\s*(.*)/) + { + $build->{build_flags} = $1; + } + elsif (/^Extra-Build-Targets:\s*(.*)/) + { + my $list = $build->{extra_build_targets}; + push @$list, $1; + } + elsif (/^Extra-Install-Targets:\s*(.*)/) + { + my $list = $build->{extra_install_targets}; + push @$list, $1; + } + else + { + die "unrecognized line under Build ($_)"; + } + } + return $build; +} + +# --- add_automatic_conflicts --- +# Add packages to eachothers conflict lists whenever they +# have Files: entries that are the same and don't have wildcards. +sub add_automatic_conflicts ($) +{ + my ($package) = @_; + my $targets = $package->{targets}; + + # a table: defined ($conflict_table->{A}->{B}) => A and B conflict. + my $conflict_table = {}; + + # a table mapping a filename that doesn't contain + # wildcards, to a list of packages containing that file. + my $by_installed_file = {}; + + # Traverse all the targets, flush one $conflict_table. + # + # Basically, if any two packages are in the same list, + # they must have a conflict. + for my $target (@$targets) + { + my $files = $target->{files}; + for my $file (@$files) + { + next if $file =~ /[\*\?\[\]]/; + my $list = $by_installed_file->{$file}; + $list = $by_installed_file->{$file} = [] unless defined ($list); + push @$list, $target; + } + } + + my %name_to_target = (); + + # Build a table of all the conflicted package pairs, + # by scanning the above lists. + for my $conflicted_targets (values %$by_installed_file) + { + my $count = scalar (@$conflicted_targets); + next if ($count < 2); + for (my $i = 0; $i < $count; $i++) + { + my $t1 = $conflicted_targets->[$i]; + my $p1 = $t1->{package_name}; + $name_to_target{$p1} = $t1; + for (my $j = 0; $j < $count; $j++) + { + next if $i == $j; + my $t2 = $conflicted_targets->[$j]; + my $p2 = $t2->{package_name}; + my $t = $conflict_table->{$p1}; + $t = $conflict_table->{$p1} = {} unless defined $t; + $t->{$p2} = 1; + } + } + } + + # Add those conflicts, unless they have been explicitly mentioned already. + my $file_a; + my $hash_b; + while (($name_a, $hash_b) = each %$conflict_table) + { + my $target_a = $name_to_target{$name_a}; + my $cstring = $target_a->{conflicts}; + + # Compute the initial list of conflicted packages. + my @conflicts; + if (!defined ($cstring) || $cstring eq '') + { + @conflicts = (); + } + else + { + @conflicts = map { s/^\s+//; s/\s+$//; $_ } (split /,/, $cstring); + } + + for my $name_b (keys %$hash_b) + { + my $preconflicted = 0; + my $target_b = $name_to_target{$name_a}; + for (@conflicts) + { + if (/^$target_b / || ($_ eq $target_b)) + { + $preconflicted = 1; + last; + } + } + + # Add a conflict if needed. + unless ($preconflicted) + { + if (!defined ($cstring) || $cstring eq '') + { + $target_a->{conflicts} = $name_b; + } + else + { + $target_a->{conflicts} .= ", $name_b"; + } + } + } + } +} + + +#===================================================================== +# Section 3: Verify that a package is correctly formed. +#===================================================================== + +# Run a user-specified function (a predicate) on a package and all its targets. +# (only possible b/c they are both hash-tables). +# +# Return the first package for which the predicate returns true, +# or `undef' if none is found. +sub search_package_and_targets ($$) +{ + my ($package, $func) = @_; + + return $package if &$func ($package); + + my $targets = $package->{targets}; + for my $target (@$targets) + { + return $target if &$func ($target); + } + return undef; +} + +# check that a package is valid. +sub sanity_check_package ($) +{ + my $package = $_[0]; + my $targets = $package->{targets}; + my $builds = $package->{builds}; + + # Verify that $package->{group} and $package->{section} are valid. + my $invalid = search_package_and_targets + ($package, + sub { + my $p = $_[0]; + return 0 unless defined $p->{group}; + return 0 if defined $RPM_GROUPS{lc($p->{group})}; + print STDERR "WARNING: " + . "The Group: `" . $p->{group} + . "'is unknown.\n"; + return 1; + }); + if (defined($invalid)) + { + die "try pkgwrite --query-list=rpm-groups"; + } + $invalid = search_package_and_targets + ($package, + sub { + my $p = $_[0]; + return 0 unless defined $p->{section}; + my $s = lc ($p->{section}); + return 0 if defined $DPKG_SECTIONS{$s}; + print STDERR "WARNING: " + . "The Section: `" . $p->{section} + . "'is unknown.\n"; + return 1; + }); + if (defined($invalid)) + { + die "invalid Section: try pkgwrite --query-list=deb-sections for a list"; + } + + # Verify that documentation doesn't contain wildcards. + $invalid = search_package_and_targets + ($package, + sub { + my $p = $_[0]; + my @docs; + my $sd = $p->{source_docs}; + my $id = $p->{installed_docs}; + @docs = ( ((defined $id) ? @$id : ()), + ((defined $sd) ? @$sd : ()) ); + for (@docs) + { + if (/\*/ || /\?/) + { + print STDERR "WARNING: " + . "documentation specifications " + . "may not contain wildcards." + . "($_)\n"; + return 1; + } + } + return 0; + }); + if (defined($invalid)) + { + die $invalid->{type} . " " . $invalid->{name} + . " had an invalid documentation entry"; + } + + # Verify that the manpages are in valid sections. + $invalid = search_package_and_targets + ($package, + sub { + my $p = $_[0]; + my $m = $p->{manpages}; + my @manpages = (defined ($m) ? @$m : ()); + for (@manpages) + { + if (!defined (get_manpage_section ($_))) + { + print STDERR "WARNING: " + . "manpage $_ was not in any " + . "known man section.\n"; + return 1; + } + if (/\//) + { + print STDERR "ERROR: " + . "manpage $_ contained a /; it " + . "should be a bare filename.\n"; + return 1; + } + } + return 0; + }); + if (defined ($invalid)) + { + die "bad manpage section in $invalid->{type} $invalid->{name}" + } + + # Verify that none of the packages have the same names. + my %used_names = (); + for my $t (@$targets) + { + my $pname = $t->{package_name}; + die "two packages are named $pname" if defined $used_names{$pname}; + $used_names{$pname} = 1; + } + + # Verify that no two installed documents in a single package + # have the same name. + for my $t (@$targets) + { + my $pname = $t->{package_name}; + my @installed_docs = (); + my $d1 = $t->{installed_docs}; + my $d2 = $t->{source_docs}; + for my $d (@$d1, @$d2) + { + next unless $d =~ m,([^/]+)/?$,; + my $base = $1; + if (defined ($used_names{$base})) + { + die "two documents in $pname are named $base"; + } + $used_names{$base} = 1; + } + } +} + + +#===================================================================== +# Section 4: ChangeLog parsing code. +#===================================================================== +# create a new changelog parser. +# doesn't parse any entries. +# +# you may pass in anything that may be open()d for reading: a filename +# or `program |'. +sub changelog_parser_new ($) +{ + my $fh = gensym (); + open $fh, "$_[0]" or return undef; + my $cp = {}; + $cp->{fh} = $fh; + $cp->{filename} = $_[0]; + $cp->{lineno} = 0; + return $cp; +} + +# skip empty lines, but also keep around the line number for error reporting. +sub _cp_skip_empty_lines ($) +{ + my $rv = skip_empty_lines ($_[0]->{fh}); + $_[0]->{lineno} = $.; + return $rv; +} + +# parse one entry: store it in the public fields of $changelog_parser: +# {package} => PACKAGE_NAME +# {version} => VERSION-RELEASE +# {change_text} => raw text of the changes +# {full_name} => NAME-OF-AUTHOR +# {email} => EMAIL-OF-AUTHOR +# {date} => ENTRY-DATE (in rfc 822, eg date -R) +# return whether this fields are valid. +sub changelog_parser_advance ($) +{ + my $cp = $_[0]; + my $package_line = _cp_skip_empty_lines ($cp); + return 0 unless defined $package_line; + if ($package_line !~ /^([a-zA-Z0-9\-_]+)\s\(([^\(\)]+)\)/) + { + die "error parsing changelog package line ($package_line) (" + . $cp->{filename} . ", line " . $cp->{lineno} . ")"; + } + $cp->{package} = $1; + $cp->{version} = $2; + + my $byline; + + # grab the text. + my $text = _cp_skip_empty_lines ($cp); + my $was_empty = 0; + + # got a header line, but nothing else: that's an error. + if (!defined ($text)) + { + die "no changelog entry was encountered: premature eof at " + . $cp->{filename} . ", line " . $cp->{lineno}; + } + $text .= "\n"; + + # Data to be parsed from the packager's byline + # (which we will do in order to detect the end-of-record anyway). + my $full_name; + my $email; + my $date; + my $got_byline = 0; + my $fh = $cp->{fh}; + while (defined ($text)) + { + my $line = scalar (<$fh>); + last unless defined $line; + + if ($line =~ /^\s+-- ([^<>]+) <([^<>]+)>\s+(.*)/) + { + $full_name = $1; + $email = $2; + $date = $3; + $got_byline = 1; + last; + } + $text .= $line; + } + $text =~ s/\n\n+$/\n/s; + + # parse the byline. + if (!$got_byline) + { + die "missing byline at " . $cp->{filename} . ", line " . $cp->{lineno}; + } + $cp->{change_text} = $text; + $cp->{full_name} = $full_name; + $cp->{email} = $email; + $cp->{date} = $date; + + return 1; +} + +# Write a debian-style changelog entry. +# (on a debian system, look in /usr/share/doc/packaging-manual/manual.text.gz) +sub changelog_write_debian_style ($$) +{ + my ($cp, $ofh) = @_; + my $lcpackage = lc($cp->{package}); + print $ofh $lcpackage, + " (", $cp->{version}, ") $debian_dist; urgency=low\n\n", + $cp->{change_text}, + "\n -- ", $cp->{full_name}, + " <", $cp->{email}, "> ", $cp->{date}, "\n\n"; +} + +# Write a redhat-style changelog entry. +# (see http://www.rpm.org/RPM-HOWTO/build.html#CHANGELOG) +sub changelog_write_redhat_style ($$) +{ + my ($cp, $ofh) = @_; + + # Convert the date from + # `date -R' format (complies with rfc 822) + # to + # `date +"%a %b %d %Y"' format + # HMM: it'd be nice to support an rfc 822 date parser here, + # but i'm hesitant unless such a beast comes with perl. + if ($cp->{date} !~ /^([A-Z][a-z][a-z]),\s+(\d+) ([A-Z][a-z][a-z]) (\d+)/) + { + die "could not parse date " . $cp->{date}; + } + my ($day_of_week, $day_of_month, $month_shortname, $year) = ($1, $2, $3, $4); + $day_of_month = "0$day_of_month" unless $day_of_month >= 10; + my $short_date = "$day_of_week $month_shortname $day_of_month $year"; + + print $ofh "* $short_date ", $cp->{full_name}, " <", $cp->{email}, ">\n\n"; + + # XXX: not part of any packaging standard. + print $ofh " Version ", $cp->{version}, "\n"; + + for (split /\n/, $cp->{change_text}) + { + # hmm, convert * to - for readability (?!?) + s/^ \*/ -/; + print $ofh $_, "\n"; + } + print $ofh "\n\n"; +} + +# destroy the changelog parser. +sub changelog_parser_destroy ($) +{ + my $cp = $_[0]; + if (! close ($cp->{fh})) + { + die "error running $cp->{filename}: $?"; + } +} + + +#===================================================================== +# Section 5: Redhat tape. +#===================================================================== +# if $make_script == 1, +# return a list of commands (newline-terminated) +# to concatenate into a script which will copy said +# docs into /usr/doc/PACKAGE-VERSION-RELEASE. +# +# if $make_script == 0, just return a list of files, +# `${prefix}/doc/PACKAGE-VERSION-RELEASE/FILENAME'. +sub redhat_make_install_doc_section ($$) +{ + my ($package, $build) = @_; + my @cmds = (); + my $targets = $package->{targets}; + my $docs = $target->{installed_docs}; + my $buildfullname = $build->{package_name} . '-' . $package->{version}; + my $docdir = "%{_topdir}/%{prefix}/doc/" . join ('-', + $package->{name}, + $package->{version}, + $package->{release}); + + # the union of all source docs for this build. + my %source_docs = (); + + # the union of all installed docs for this build. + my %installed_docs = (); + + for my $target (@$targets) + { + # skip unrelated targets. + next unless $target->{build} == $build; + + my $doc_list; + + $doc_list = $target->{source_docs}; + for (@$doc_list) { $source_docs{$_} = 1 } + + $doc_list = $target->{installed_docs}; + for (@$doc_list) { $installed_docs{$_} = 1 } + } + + # if there is no documentation to install, skip out rather than make + # an empty doc directory. + if (scalar(keys(%source_docs)) == 0 && scalar(keys(%installed_docs)) == 0) + { + return; + } + + # mkdir -p isn't generally portable, but it works under redhat. hmm... + push @cmds, "test -d $docdir || mkdir -p $docdir\n"; + + for my $doc (sort keys %source_docs) + { + # Produce commands to copy $src_doc from the unpacked tarball + # to the $docdir. + my $base; + $doc =~ m,([^/]+/?)$,; + $base = $1; + if ($doc =~ m,/$,) + { + # directory + push @cmds, "rm -rf $docdir/$base\n", + "cp -dpr %{_builddir}/$buildfullname/$doc $docdir/$base\n"; + } + else + { + # file. + push @cmds, "cp -dpf %{_builddir}/$buildfullname/$doc $docdir/\n"; + } + } + + for my $doc (sort keys %installed_docs) + { + # Produce commands to copy $installed_doc from the installed + # area to the $docdir. + my $base; + $doc =~ m,([^/]+/?)$,; + $base = $1; + + # Try to avoid copying a file over itself. + my $srcpath = "%{_topdir}/$doc"; + my $dstpath = "$docdir/$base"; + $srcpath =~ s,/+$,,; $srcpath =~ s,//+,/,; + $dstpath =~ s,/+$,,; $dstpath =~ s,//+,/,; + next if $srcpath eq $dstpath; + + # Write the script if needed. + if ($doc =~ m,/$,) + { + # directory + push @cmds, "rm -rf $docdir/$base\n", + "cp -r %{_topdir}/$doc $docdir/$base\n"; + } + else + { + # file. + push @cmds, "cp -dpf %{_topdir}/$doc $docdir/\n"; + } + } + return @cmds; +} + +# output a list of file including their path. +sub redhat_make_doc_section_list ($$$) +{ + my ($fh, $package, $target) = @_; + my $docs; + my $docdir = "%{prefix}/doc/" . join ('-', + $package->{name}, + $package->{version}, + $package->{release}); + $docs = $target->{installed_docs}; + for (@$docs) + { + next unless m/([^\/]+\/?)$/; + push @docs, "$docdir/$1"; + } + $docs = $target->{source_docs}; + for (@$docs) + { + next unless m/([^\/]+\/?)$/; + push @docs, "$docdir/$1"; + } + return @docs; +} + +# Print either an opening or closing architecture conditional +# for a certain target. +sub print_arch_conditional ($$$) +{ + my ($fh, $target, $is_open) = @_; + my $arch_indep = $target->{arch_indep}; + if ($is_open) + { + print $fh ($arch_indep ? "%ifarch noarch\n" : "%ifnarch noarch\n"); + } + else + { + print $fh "%endif # ", ($arch_indep ? "noarch" : "!noarch"), "\n"; + } +} + +sub make_redhat_specfile($$$) +{ + my ($package, $build, $spec_file_out) = @_; + + open SPECFILE, ">$spec_file_out" or die "could not create $spec_file_out: $!"; + + my $rh_requires = compute_redhat_requires ($package); + my $download_url = $package->{url}; + print SPECFILE "\%define prefix /usr\n"; + + # For now, disable this "fascist" option. + # For some reason, our .tar.gz winds up in the list of + # files, which kills us. + print SPECFILE "\%define _unpackaged_files_terminate_build 0\n"; + + write_entries ('SPECFILE', $build, + [qw(Name)], + [qw(package_name)]); + write_entries ('SPECFILE', $package, + [qw(Version Release Copyright Vendor URL + Group Summary Provides License)], + [qw(version release copyright vendor home_page + group summary output_name license)]); + + + # Table of all the manpages we will need to gzip. + my %build_manpages = (); + + # The primary target, that is, the target whose name is the + # build's name (the same as the .src.rpm will assume). + my $primary_target; + + # Compute the primary target and build_manpages. + my $targets = $package->{targets}; + for my $target (@$targets) + { + next unless $target->{build} == $build; + + if ($target->{name} eq $build->{name}) + { + $primary_target = $target; + } + my $manpages = $target->{manpages}; + for my $manpage (@$manpages) + { + $build_manpages{$manpage} = 1; + } + } + + my $tarball = $package->{name} . '-' . $package->{version} . ".tar.gz"; + print SPECFILE "Source: $tarball\n"; + #print SPECFILE "Packager: $packager_id\n"; + print SPECFILE "Prefix: /usr\n"; + print SPECFILE "BuildRoot: %{_topdir}\n"; + # Print flags that actually pertain to the primary target. + + my $needs_arch_conditionals = 0; + if ($package->{needs_noarch}) + { + if ($package->{needs_arch_specific}) + { + print SPECFILE "BuildArch: noarch $default_redhat_archs\n"; + $needs_arch_conditionals = 1; + } + else + { + print SPECFILE "BuildArch: noarch\n"; + } + } + if (defined ($primary_target)) + { + # Print dependencies and conflicts. + my $rh_requires = compute_redhat_requires ($primary_target); + print SPECFILE "Requires: $rh_requires\n" if ($rh_requires ne ''); + + my $rh_conflicts = compute_redhat_conflicts ($primary_target); + print SPECFILE "Conflicts: $rh_conflicts\n" if ($rh_conflicts ne ''); + } + + print SPECFILE "%description\n", + make_debian_description ($package->{description}), + "\n"; + + my $config_flags; + $config_flags = $redhat_config_flags + . " " . + (defined ($build->{configure_flags}) + ? $build->{configure_flags} + : ''); + my $env_settings = (defined ($build->{configure_envars}) + ? "$build->{configure_envars} " + : ""); + my $make_flags = defined ($build->{make_flags}) + ? $build->{make_flags} : ''; + my $build_flags = defined ($build->{build_flags}) + ? $build->{build_flags} : ''; + my $install_flags = defined ($build->{install_flags}) + ? $build->{install_flags} : ''; + $build_flags .= " $make_flags"; + $install_flags .= " $make_flags"; + my $full_main_package = "$package->{name}-$package->{version}"; + my $full_target_package = "$build->{package_name}-$package->{version}"; + print SPECFILE + "%prep\n", + "rm -rf \$RPM_BUILD_DIR/$full_main_package\n", + "rm -rf \$RPM_BUILD_DIR/$full_target_package\n", + "zcat \$RPM_SOURCE_DIR/$full_main_package.tar.gz | tar -xvf -\n", + ($full_main_package eq $full_target_package + ? '' + : "mv $full_main_package $full_target_package\n"), + "%build\n", + "test -d \$RPM_BUILD_DIR || mkdir -p \$RPM_BUILD_DIR\n", + "cd \$RPM_BUILD_DIR/$full_target_package\n", + "$env_settings ./configure $config_flags\n", + "make PREFIX=/usr $build_flags\n\n"; + + my $make_targets = $build->{extra_build_targets}; + for (@$make_targets) + { + print SPECFILE "make $_ PREFIX=/usr $build_flags\n"; + } + print SPECFILE + "%install\n", + "cd \$RPM_BUILD_DIR/$full_target_package\n", + "make install PREFIX=/usr DESTDIR=%{_topdir} $install_flags\n"; + $make_targets = $build->{extra_install_targets}; + for (@$make_targets) + { + print SPECFILE "make $_ PREFIX=/usr DESTDIR=%{_topdir} $install_flags\n"; + } + + print SPECFILE redhat_make_install_doc_section ($package, $build); + + for my $manpage (keys %build_manpages) + { + my $man_section = get_manpage_section ($manpage); + print SPECFILE "gzip -9 -f %{_topdir}/usr/man/man$man_section/$manpage\n"; + } + print SPECFILE "\n"; + + print SPECFILE + "%clean\n", + "rm -rf %{_builddir}\n", + "mkdir %{_builddir}\n", # without the mkdir, this doesn't run twice... + "\n\n"; + + if (defined ($primary_target)) + { + if ($needs_arch_conditionals) + { + print_arch_conditional ('SPECFILE', $primary_target, 1) + } + if ($primary_target->{needs_ldconfig}) + { + # This is from the gtk+.spec file. + # Usually they are all pretty godly, but i'll be damned + # if this works :) Furthermore all documentation alludes + # to the next method; -p is wholy undocumented as far as i can tell. + #print SPECFILE "%post -p /sbin/ldconfig\n\n", + # "%postun -p /sbin/ldconfig\n\n"; + + print SPECFILE "%post\n", + "/sbin/ldconfig\n\n", + "%postun\n", + "/sbin/ldconfig\n\n", + } + write_redhat_files_section ('SPECFILE', $primary_target); + if ($needs_arch_conditionals) + { + print_arch_conditional ('SPECFILE', $primary_target, 0) + } + } + + for my $target (@$targets) + { + my $rh_requires = compute_redhat_requires ($target); + my $target_name = $target->{package_name}; + + # Only process $target if it pertains to the current $build. + next if $target->{build} != $build; + + # For the main package, the remaining data are + # handled in the main preamble. + next if ($target->{name} eq $build->{name}); + + if ($needs_arch_conditionals) + { + print_arch_conditional ('SPECFILE', $target, 1); + } + + # Various standard fields for the non-major package. + # XXX: should probably try to avoid needless `-n' flags. + print SPECFILE "%package -n $target_name\n"; + write_entries ('SPECFILE', $target, + [qw(Group)], + [qw(group)]); + print SPECFILE "Requires: $rh_requires\n" if ($rh_requires ne ''); + + my $rh_conflicts = compute_redhat_conflicts ($target); + print SPECFILE "Conflicts: $rh_conflicts\n" if ($rh_conflicts ne ''); + + # Summary is required for Redhat packages, + # so we checked the availability of the Synopsis: + # field on input, so an undef'd $summary should never occur. + my $summary = $target->{summary}; + die unless defined $summary; + print SPECFILE "Summary: $summary\n"; + + if ($target->{needs_ldconfig}) + { + print SPECFILE "%post -n $target_name -p /sbin/ldconfig\n\n", + "%postun -n $target_name -p /sbin/ldconfig\n\n"; + } + + # Likewise, Description is mandatory. + my $desc = $target->{description}; + if (!defined($desc)) { $desc = $package->{description}; } + die unless defined $desc; + print SPECFILE "%description -n $target_name\n$desc\n"; + + # Print the %files section (Based on Files: in the pkgwriteinfo file.) + write_redhat_files_section ('SPECFILE', $target); + if ($needs_arch_conditionals) + { + print_arch_conditional ('SPECFILE', $target, 0); + } + print SPECFILE "\n\n"; + } + + # If there is a changelog, include it in the specfile. + # This is annoying b/c there is not necessarily any unpacked + # source code around... + my $changelog = $package->{changelog}; + if (defined ($changelog)) + { + my $cl_file; + if ($package->{package_source_dir}) + { + $cl_file = $package->{package_source_dir} . "/$changelog"; + } + else + { + # perform the necessary `tar' command. + $cl_file = "$gunzip < " . $package->{package_tarball} + . " | tar $untar_to_stdout - " + . $package->{upstream_fullname} . "/$changelog |"; + } + my $cp = changelog_parser_new ($cl_file); + print SPECFILE "\n%changelog\n"; + my $first_time = 1; + while (changelog_parser_advance ($cp)) + { + if ($first_time) + { + my $ver = $package->{version} . '-' . $package->{release}; + if ($strict_changelog_checking && $cp->{version} ne $ver) + { + die "package version in changelog (" . $cp->{version} . ")" + . " and in pkgwriteinfo file ($ver) do not match!"; + } + $first_time = 0; + } + changelog_write_redhat_style ($cp, 'SPECFILE'); + } + changelog_parser_destroy ($cp); + } + close SPECFILE; +} + +sub write_redhat_files_section($$) +{ + my ($fh, $target) = @_; + print $fh '%files -n ', $target->{package_name}, "\n"; + my $files_list = $target->{files}; + if (defined($files_list)) + { + print $fh '%defattr(-, root, root)', "\n"; + for my $wildcard (@$files_list) + { + my $tmp = $wildcard; + $tmp =~ s,^/usr/,%{prefix}/,; + print $fh "$tmp\n"; + } + } + my $manpages = $target->{manpages}; + for my $manpage (@$manpages) + { + my $section = get_manpage_section ($manpage); + print $fh "%{prefix}/man/man$section/$manpage.gz\n" + } + for my $doc (redhat_make_doc_section_list ($fh, $target->{package}, $target)) + { + print $fh "%doc $doc\n"; + } +} + +# --- print canned rpmrc --- +# This is based on the spec file distributed with rpm 4.0. +# See comments in the legal section of pkgwrite. +sub print_canned_rpmrc ($) +{ + my $fh = $_[0]; + for (qw(i386 i486 i586 i686 athlon)) + { print $fh "optflags: $_ -O2 -march=$_\n" } + for (qw(ia64 mipseb mipsel ia64)) + { print $fh "optflags: $_ -O2\n" } + print $fh "optflags: alpha -O2 -mieee\n"; + for (qw(ev5 ev56 pca56 ev6 ev67)) + { print $fh "optflags: alpha$_ -O2 -mieee -mcpu=$_\n" } + print $fh "optflags: sparc -O2 -m32 -mtune=ultrasparc\n"; + print $fh "optflags: sparcv9 -O2 -m32 -mcpu=ultrasparc\n"; + print $fh "optflags: sparc64 -O2 -m64 -mcpu=ultrasparc\n"; + print $fh "optflags: ppc -O2 -fsigned-char\n"; + for (qw(parisc hppa1.0 hppa1.1 hppa1.2 hppa2.0)) + { print $fh "optflags: $_ -O2 -mpa-risc-1-0\n" } + for (qw(armv4b armv4l)) + { print $fh "optflags: $_ -O2 -fsigned-char -fomit-frame-pointer\n" } + for (qw(m68k atarist atariste ataritt falcon atariclone milan hades)) + { print $fh "optflags: $_ -O2 -fomit-frame-pointer\n" } + for (qw(athlon i686 i586 i486 i386)) + { print $fh "arch_canon: $_: $_ 1\n" } + for (qw(alpha alphaev5 alphaev56 alphapca56 alphaev6 alphaev67)) + { print $fh "arch_canon: $_: $_ 2\n" } + for (qw(alpha alphaev5 alphaev56 alphapca56 alphaev6 alphaev67)) + { print $fh "arch_canon: $_: sparc 3\n" } + for (qw(sparcv9)) + { print $fh "arch_canon: $_: $_ 3\n" } + print $fh <<'EOF'; +arch_canon: mipseb: mipseb 4 +arch_canon: ppc: ppc 5 +arch_canon: m68k: m68k 6 +arch_canon: IP: sgi 7 +arch_canon: rs6000: rs6000 8 +arch_canon: ia64: ia64 9 +arch_canon: sparc64:sparc64 10 +arch_canon: sun4u: sparc64 10 +arch_canon: mipsel: mipsel 11 +arch_canon: armv4b: armv4b 12 +arch_canon: armv4l: armv4l 12 +EOF + for (qw(atarist atariste ataritt falcon atariclone milan hades)) + { print $fh "arch_canon: $_: m68kmint 13\n" } + for (qw(s398 i370)) + { print $fh "arch_canon: $_: $_ 14\n" } + + print $fh <<'EOF'; +# Canonical OS names and numbers + +os_canon: Linux: Linux 1 +os_canon: IRIX: Irix 2 +# This is wrong +os_canon: SunOS5: solaris 3 +os_canon: SunOS4: SunOS 4 + +os_canon: AmigaOS: AmigaOS 5 +os_canon: AIX: AIX 5 +os_canon: HP-UX: hpux10 6 +os_canon: OSF1: osf1 7 +os_canon: osf4.0: osf1 7 +os_canon: osf3.2: osf1 7 +os_canon: FreeBSD: FreeBSD 8 +os_canon: SCO_SV: SCO_SV3.2v5.0.2 9 +os_canon: IRIX64: Irix64 10 +os_canon: NEXTSTEP: NextStep 11 +os_canon: BSD/OS: BSD_OS 12 +os_canon: machten: machten 13 +os_canon: CYGWIN32_NT: cygwin32 14 +os_canon: CYGWIN32_95: cygwin32 15 +os_canon: UNIX_SV: MP_RAS: 16 +os_canon: MiNT: FreeMiNT 17 +os_canon: OS/390: OS/390 18 +os_canon: VM/ESA: VM/ESA 19 +os_canon: Linux/390: OS/390 20 +os_canon: Linux/ESA: VM/ESA 20 +EOF + + for (qw(s398 i370)) + { print $fh "arch_canon: $_: $_ 14\n" } + for (qw(osfmach3_i686 osfmach3_i586 osfmach3_i486 osfmach3_i386 + athlon i686 i586 i486 i386)) + { print $fh "buildarchtranslate: $_: i386\n" } + for (qw(ia64)) + { print $fh "buildarchtranslate: $_: ia64\n" } + for (qw(osfmach3_ppc powerpc powerppc)) + { print $fh "buildarchtranslate: $_: ppc\n" } + for (qw(alphaev5 alphaev56 alphapca56 alphaev6 alphaev67)) + { print $fh "buildarchtranslate: $_: alpha\n" } + for (qw(sun4c sun4d sun4m sparcv9)) + { print $fh "buildarchtranslate: $_: sparc\n" } + for (qw(sun4u)) + { print $fh "buildarchtranslate: $_: sparc64\n" } + for (qw(atarist atariste ataritt falcon atariclone milan hades)) + { print $fh "buildarchtranslate: $_: m68kmint\n" } + for ('alphaev67: alphaev6', 'alphaev6: alphapca56', 'alphapca56: alphaev56', + 'alphaev56: alphaev5', 'alphaev5: alpha', 'alpha: axp noarch', + 'ia64: noarch', 'athlon: i686', 'i686: i586', 'i586: i486', + 'i486: i386', 'i386: noarch', 'osfmach3_i686: i686 osfmach3_i586', + 'osfmach3_i586: i586 osfmach3_i486', + 'osfmach3_i486: i486 osfmach3_i386', 'osfmach3_i386: i486', + 'osfmach3_ppc: ppc', 'powerpc: ppc', 'powerppc: ppc', + 'sun4c: sparc', 'sun4d: sparc', 'sun4m: sparc', 'sun4u: sparc64', + 'sparc64: sparcv9', 'sparcv9: sparc', 'sparc: noarch', + 'ppc: rs6000', 'rs6000: noarch', 'mipseb: noarch', 'mipsel: noarch', + 'hppa2.0: hppa1.2', 'hppa1.2: hppa1.1', 'hppa1.1: hppa1.0', + 'hppa1.0: parisc', 'parisc: noarch', + 'armv4b: noarch', 'armv4l: noarch', + 'atarist: m68kmint noarch', 'atariste: m68kmint noarch', + 'ataritt: m68kmint noarch', 'falcon: m68kmint noarch', + 'atariclone: m68kmint noarch', 'milan: m68kmint noarch', + 'hades: m68kmint noarch', 's390: i370', 'i370: noarch', + 'ia64: noarch') + { print $fh "arch_compat: $_\n" } + print $fh <<'EOF'; +os_compat: IRIX64: IRIX +os_compat: solaris2.7: solaris2.3 solaris2.4 solaris2.5 solaris2.6 +os_compat: solaris2.6: solaris2.3 solaris2.4 solaris2.5 +os_compat: solaris2.5: solaris2.3 solaris2.4 +os_compat: solaris2.4: solaris2.3 + +os_compat: hpux11.00: hpux10.30 +os_compat: hpux10.30: hpux10.20 +os_compat: hpux10.20: hpux10.10 +os_compat: hpux10.10: hpux10.01 +os_compat: hpux10.01: hpux10.00 +os_compat: hpux10.00: hpux9.07 +os_compat: hpux9.07: hpux9.05 +os_compat: hpux9.05: hpux9.04 + +os_compat: osf4.0: osf3.2 osf1 + +os_compat: ncr-sysv4.3: ncr-sysv4.2 + +os_compat: FreeMiNT: mint MiNT TOS +os_compat: MiNT: FreeMiNT mint TOS +os_compat: mint: FreeMiNT MiNT TOS +os_compat: TOS: FreeMiNT MiNT mint + +buildarch_compat: ia64: noarch + +buildarch_compat: athlon: i686 +buildarch_compat: i686: i586 +buildarch_compat: i586: i486 +buildarch_compat: i486: i386 +buildarch_compat: i386: noarch + +buildarch_compat: sun4c: noarch +buildarch_compat: sun4d: noarch +buildarch_compat: sun4m: noarch +buildarch_compat: sun4u: noarch +buildarch_compat: sparc64: noarch +buildarch_compat: sparcv9: sparc +buildarch_compat: sparc: noarch + +buildarch_compat: alphaev67: alphaev6 +buildarch_compat: alphaev6: alphapca56 +buildarch_compat: alphapca56: alphaev56 +buildarch_compat: alphaev56: alphaev5 +buildarch_compat: alphaev5: alpha +buildarch_compat: alpha: noarch + +buildarch_compat: m68k: noarch +buildarch_compat: ppc: noarch +buildarch_compat: mipsel: noarch +buildarch_compat: mipseb: noarch +buildarch_compat: armv4b: noarch +buildarch_compat: armv4l: noarch +buildarch_compat: parisc: noarch + +buildarch_compat: atarist: m68kmint noarch +buildarch_compat: atariste: m68kmint noarch +buildarch_compat: ataritt: m68kmint noarch +buildarch_compat: falcon: m68kmint noarch +buildarch_compat: atariclone: m68kmint noarch +buildarch_compat: milan: m68kmint noarch +buildarch_compat: hades: m68kmint noarch + +buildarch_compat: ia64: noarch +buildarch_compat: s390: noarch +EOF +} + +# --- make redhat dir --- +# +# Make a bunch of files needed to produce an rpm +# and writes them in $dir, which it creates. +# +# Return a list of commands that should be run to build +# the RPM given this directory. +# +# Also needs a distribution tarball (made by `make dist') +# and an architecture. +sub make_redhat_dir ($$$$) +{ + my ($package, $dir, $tarball, $arch) = @_; + + run ("rm -rf $dir"); + safe_mkdir ("$dir"); + safe_mkdir ("$dir/rpm-tmp"); + safe_mkdir ("$dir/rpm-tmp/usr"); + safe_mkdir ("$dir/rpm-tmp/usr/src"); + safe_mkdir ("$dir/rpm-tmp/usr/src/redhat"); + safe_mkdir ("$dir/tmp"); + for (qw(SOURCES SPECS BUILD RPMS SRPMS)) + { + safe_mkdir ("$dir/rpm-tmp/usr/src/redhat/$_"); + } + run ("cp $tarball $dir/rpm-tmp/usr/src/redhat/SOURCES"); + chdir ($dir) or die; + $dir = `pwd`; + chomp($dir); + #$REDHAT_DIR = $dir; + $BUILD_TOP = "$dir/tmp"; + $STAGING_TOP = "$dir/rpm-tmp"; + + my $tmp_rpm_macros_fname = "$dir/rpmmacros.tmp"; + my $tmp_rpm_rc_fname = "$dir/rpmrc.tmp"; + + open T, ">$tmp_rpm_macros_fname" + or die "couldn't create $tmp_rpm_macros_fname"; + print T <<"EOF"; +%_builddir ${BUILD_TOP} +%_buildshell /bin/sh +%_dbpath %{_var}/lib/rpm +%_defaultdocdir %{_usr}/doc +%_instchangelog 5 +%_rpmdir ${STAGING_TOP}/usr/src/redhat/RPMS +%_rpmfilename %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm +%_signature none +%_sourcedir ${STAGING_TOP}/usr/src/redhat/SOURCES +%_specdir ${STAGING_TOP}/usr/src/redhat/SPECS +%_srpmdir ${STAGING_TOP}/usr/src/redhat/SRPMS +%_srcrpmdir ${STAGING_TOP}/usr/src/redhat/SRPMS +%_tmppath /tmp +%_topdir ${STAGING_TOP} +EOF + close T; + + + open (RPMRC, ">$tmp_rpm_rc_fname") or die "couldn't create $tmp_rpm_rc_fname"; + print RPMRC "macrofiles: ", + join (':', '/usr/lib/rpm/macros', + '/usr/lib/rpm/%{_target}/macros', + '/etc/rpm/macros', + '/etc/rpm/%{_target}/macros', + $tmp_rpm_macros_fname); + print RPMRC "\n\n\n"; + + # If available, include the system-installed version + # (w/o its `macrofiles:' lines), instead of the "canned" one; + # (which actually just comes from rpm 4.0, see above); + # the system version should be /var/lib/rpm/rpmrc. + if (-r $system_rpmrc_file) + { + open SYSRPMRC, $system_rpmrc_file or die "internal error: " . + "couldn't open $system_rpmrc_file"; + while () + { + next if /^\s*macrofiles:/i; + print RPMRC $_; + } + close SYSRPMRC; + } + else + { + print_canned_rpmrc ('RPMRC'); + } + + close RPMRC; + + my $builds = $package->{builds}; + my @commands = (); + my $targets = $package->{targets}; + for my $build (@$builds) + { + my $tmp_specfile_name; + if ($build->{name} eq '{MAIN}') + { + $tmp_specfile_name = "$dir/" . $package->{name} . ".spec"; + } + else + { + $tmp_specfile_name = "$dir/" . $package->{name} . + '-' . $build->{name} . ".spec"; + } + + make_redhat_specfile($package, $build, $tmp_specfile_name); + push @commands, "test -d $BUILD_TOP || mkdir $BUILD_TOP"; + + my $cmd = join (' ', + "rpmbuild", + "--target=$arch", + "--rcfile $tmp_rpm_rc_fname", + "-ba", + "$tmp_specfile_name"); + push @commands, $cmd; + } + return @commands; +} + + +# --- Automatic Depedency Conversions (Guessing involved) --- +sub deb_to_rh_package_spec_list ($) +{ + my $depends = $_[0]; + my @pieces = (); + for (split /,/, $depends) + { + s/^\s+//; + s/\s+$//; + s/[()]//g; + push @pieces, $_; + } + return join(', ', @pieces); +} + +# +# convert debian-style dependencies to redhat-style. +# (redhat calls the line `Requires') +# debian/pkgwrite format: +#Depends: libglade0, libglib1.2 (>= 1.2.0), libgtk1.2 (>= 1.2.5), libxml1, libz1, xlib6g, mpg123 +#Requires: libglade >= 0.11, gtk+ >= 1.2, libxml >= 1.7, mpg123 +# As you can see, this cannot necessarily be automated, +# so we support Redhat-Requires: for override purposes. +sub compute_redhat_requires($) +{ + my $object = $_[0]; + if (defined($object->{redhat_requires})) + { + return $object->{redhat_requires}; + } + elsif (defined($object->{depends})) + { + return deb_to_rh_package_spec_list ($object->{depends}); + } + else + { + return ''; + } +} +sub compute_redhat_conflicts($) +{ + my $object = $_[0]; + if (defined($object->{redhat_conflicts})) + { + return $object->{redhat_conflicts}; + } + elsif (defined($object->{conflicts})) + { + return deb_to_rh_package_spec_list ($object->{conflicts}); + } + else + { + return ''; + } +} + + +#===================================================================== +# Section 6: Debian tape. +#===================================================================== +sub copy_docs ($$$$) +{ + my ($fh, $basedir, $list, $targetdocdir) = @_; + my @rv = (); + + for my $doc (@$list) + { + my $docbase; + next unless $doc =~ m,([^/]+)/?$,; + $docbase = $1; + if ($doc =~ /\/$/) + { + print $fh "\t( cd $basedir/$doc/.. && \\\n", + "\t tar cf - $docbase | \\\n", + "\t $gzip || exit 1 \\\n", + "\t) > $targetdocdir/$docbase.tar.gz\n"; + push @rv, $docbase; + } + elsif ($doc =~ /\.gz$/) + { + print $fh "\tcp -dp $basedir/$doc $targetdocdir\n"; + push @rv, $docbase; + } + else + { + print $fh "\t$gzip < $basedir/$doc > $targetdocdir/$docbase.gz\n"; + push @rv, "$docbase.gz"; + } + } +} + +# debian_docs_from_list: return a list of full paths to documentation. +sub debian_docs_from_list ($) +{ + my $list = $_[0]; + + # Treat directories, gzipped and non-gzipped files differently. + return map { my $base; + if (/\//) + { + $base = $_; + } + elsif (/\.gz$/) + { + s/^.*\///; + $base = $_; + } + else + { + s/^.*\///; + $base = "$_.gz"; + } + "/usr/share/doc/" . $target->{package_name} . "/$base"; + } @$list; +} + +# --- Create a debian `rules' file. --- +sub write_rules_file ($$) +{ + my ($fname, $package) = @_; + open RULES, ">$fname" or die "couldn't create rules file ($fname)"; + print RULES <<'EOF'; +#!/usr/bin/make -f +EOF + print RULES "CONFIG_OPTS = $debian_config_flags\n"; + + print RULES <<'EOF'; +# --- make the configure script itself (only needed from CVS) --- +configure: + if test -x configure ; then \ + true ; \ + else \ + if test -x bootstrap ; then \ + ./bootstrap ; \ + elif test -x autogen.sh ; then \ + ./autogen.sh ; \ + fi \ + fi + test -x configure +EOF + + # Different "builds" -- these are completely rebuilt versions + # of the source code, usually with just different configure flags, etc. + my $builds = $package->{builds}; + my $install_targets = ''; + my $binary_package_targets = ''; + for my $build (@$builds) + { + my $suffix; my $path; + if ($build->{name} eq '{MAIN}') + { + $suffix = ''; + $path = 'MAIN'; + } + else + { + $path = $build->{name}; + $suffix = "-$path"; + } + my $cfg_envars = $build->{configure_envars}; + my $cfg_flags = $build->{configure_flags}; + + print RULES '#', ('='x69), "\n", + '# Methods for building', $build->{name}, "\n", + '#', ('='x69), "\n"; + + # figure out how to run configure. + print RULES <<"EOF"; +configured$suffix: configured$suffix.pkgwrite-stamp +configured$suffix.pkgwrite-stamp: configure +EOF + $has_configure = 1; + if ($has_configure) + { + print RULES "\ttest -d debian/BUILDS || mkdir debian/BUILDS\n"; + print RULES "\trm -rf debian/BUILDS/$path\n"; + print RULES "\tmkdir debian/BUILDS/$path\n"; + print RULES "\tcd debian/BUILDS/$path && \\\n"; + print RULES "\t$cfg_envars \\\n" if defined $cfg_envars; + $cfg_flags = '' unless defined ($cfg_flags); + $cfg_flags .= " $debian_config_flags"; + print RULES "\t../../../configure $cfg_flags\n"; + print RULES "\ttouch configured$suffix.pkgwrite-stamp\n"; + } + print RULES "\n"; + + # figure out how to build. + my $build_flags = join (' ', + $build->{make_flags} || '', + $build->{build_flags} || ''); + print RULES <<"EOF"; +build$suffix: build$suffix.pkgwrite-stamp +build$suffix.pkgwrite-stamp: configured$suffix.pkgwrite-stamp + cd debian/BUILDS/$path && \$(MAKE) PREFIX=/usr $build_flags +EOF + my $make_targets = $build->{extra_build_targets}; + for (@$make_targets) + { + print RULES "\tcd debian/BUILDS/$path && \\\n", + "\t\t\$(MAKE) PREFIX=/usr $_ $build_flags\n"; + } + + # figure out how to install. + my $make_flags = $build->{make_flags}; + my $install_flags = $build->{install_flags}; + $install_flags = '' unless defined $install_flags; + $install_flags .= " $make_flags" if defined $make_flags; + + print RULES <<"EOF"; +install$suffix: install$suffix.pkgwrite-stamp +install$suffix.pkgwrite-stamp: build$suffix.pkgwrite-stamp + test -d debian/INSTALLS || mkdir debian/INSTALLS + test -d debian/INSTALLS/$path || mkdir debian/INSTALLS/$path + cd debian/BUILDS/$path && \\ + \$(MAKE) PREFIX=/usr DESTDIR=`pwd`/../../INSTALLS/$path \\ + $install_flags install +EOF + $make_targets = $build->{extra_install_targets}; + for (@$make_targets) + { + print RULES "\tcd debian/BUILDS/$path && \\\n", + "\t\t\$(MAKE) PREFIX=/usr \\\n", + "\t\tDESTDIR=`pwd`/../../INSTALLS/$path \\\n", + "\t\t$install_flags $_\n"; + } + print RULES <<"EOF"; + touch install$suffix.pkgwrite-stamp + rm -f target-dist.pkgwrite-stamp +EOF + $install_stamp_targets .= " install$suffix.pkgwrite-stamp"; + $install_targets .= " install$suffix"; + } + + + print RULES '#', ('='x69), "\n", + "# Copying files into per-target directories.\n", + '#', ('='x69), "\n", + "target-dist: target-dist.pkgwrite-stamp\n", + "target-dist.pkgwrite-stamp: $install_stamp_targets\n"; + my $targets = $package->{targets}; + my %MADE_DIRS = (); + for my $target (@$targets) + { + my $suffix = ''; + + my $pname = $target->{package_name}; + # Move file from the build area to a target directory. + my $files = $target->{files}; + + # figure out the target's build's directory name. + my $buildname = $target->{build}->{name}; + if ($buildname eq '{MAIN}') + { + $buildname = 'MAIN'; + } + + # find the string to suffix makefile targets with. + if ($target->{name} ne '{MAIN}') + { + $suffix = "-" . $target->{name}; + } + for my $pattern (@$files) + { + my $dir = $pattern; + my $cp_command = 'cp -dp'; + $cp_command = 'cp -dpr' if $dir =~ s,/$,,; + + # Are there any wild-cards in the directory part + # of the pattern: they will require a different + # strategy. + if ($pattern =~ /.*[\?\*].*\/.*/) + { + print RULES "\tset -e ; (cd debian/INSTALLS/$buildname ; tar cf - ./$pattern) | ( cd debian/TARGETS/$pname ; tar xf -)\n"; + next; + } + + $dir =~ s,/[^/]+$,,; + maybe_print_make_dirs (\%MADE_DIRS, + 'RULES', + "debian/TARGETS/$pname/$dir"); + print RULES "\t$cp_command debian/INSTALLS/$buildname/$pattern debian/TARGETS/$pname/$dir\n"; + } + my $manpages = $target->{manpages}; + for my $manpage (@$manpages) + { + my $section = get_manpage_section ($manpage); + maybe_print_make_dirs (\%MADE_DIRS, 'RULES', + "debian/TARGETS/$pname/usr/share/man/man$section"); + my $syspath = "usr/share/man/man$section/$manpage"; + print RULES "\tgzip -9 -c < debian/INSTALLS/$buildname/$syspath > debian/TARGETS/$pname/$syspath.gz\n"; + } + my $inst_doc_dir = "debian/INSTALLS/$buildname/usr/share/doc"; + my $target_doc_dir = "debian/TARGETS/$pname/usr/share/doc"; + $target_doc_dir .= "/" . $target->{package_name}; + maybe_print_make_dirs (\%MADE_DIRS, 'RULES', $target_doc_dir); + copy_docs ('RULES', "debian/INSTALLS/$buildname", + $target->{installed_docs}, $target_doc_dir); + copy_docs ('RULES', ".", $target->{source_docs}, $target_doc_dir); + + # Copy the Changelog, if any, and add it to the list + # of files for this target. + print RULES "\tgzip -9 -c < debian/changelog > ", + "$target_doc_dir/", $package->{changelog_file}, "\n"; + + $binary_package_targets .= " binary-package-target$suffix"; + } + print RULES "\ttouch target-dist.pkgwrite-stamp\n"; + print RULES "\trm -f $binary_package_targets\n"; + + print RULES "\n\n"; + print RULES '#', ('='x69), "\n", + "# Methods for creating binary packages.\n", + '#', ('='x69), "\n"; + + # I don't think there's ever any reason to build + # the arch-indep files separately, but someday we + # might conceivable support a Platform-Independent flag + # for builds; then they could be placed here. + print RULES "# Build architecture-independent files here.\n", + "binary-indep:\n", + "\ttrue\n\n"; + + print RULES "# Build architecture-dependent files here.\n"; + for my $target (@$targets) + { + my $pname = $target->{package_name}; + my $build = $target->{build}; + $pname = 'MAIN' if $pname eq '{MAIN}'; + my $lcpname = lc($pname); + + my $suffix = ''; + $suffix = ("-" . $target->{name}) if ($target->{name} ne '{MAIN}'); + + # build this binary package. + print RULES <<"EOF"; +binary-package-target$suffix: binary-package-target$suffix.pkgwrite-stamp +binary-package-target$suffix.pkgwrite-stamp: target-dist.pkgwrite-stamp + # Compose DEBIAN directory (in debian/TARGETS/$pname) + test -d debian/TARGETS/$pname/DEBIAN || mkdir debian/TARGETS/$pname/DEBIAN + chmod o-w debian/TARGETS/$pname/DEBIAN + dpkg-gencontrol -p$lcpname -Pdebian/TARGETS/$pname +EOF + my $ddata = $target->{debian_data}; + + # copy various worker scripts into the DEBIAN directory. + for my $variant (qw(preinst postinst prerm postrm)) + { + if (defined ($ddata->{"needs_" . $variant})) + { + my $script = $target->{package_name} . ".$variant"; + print RULES "\tcp debian/$script debian/TARGETS/$pname/DEBIAN\n"; + print RULES "\tchmod +x debian/TARGETS/$pname/DEBIAN/$script\n"; + } + } + + print RULES <<"EOF"; + # Build the package. + dpkg-deb --build debian/TARGETS/$pname .. + touch binary-package-target$suffix.pkgwrite-stamp +EOF + } + + print RULES "# Debian standard targets.\n"; + print RULES "binary-package: $binary_package_targets\n"; + print RULES "binary-arch: $binary_package_targets\n"; + print RULES "binary: binary-indep binary-arch\n\n"; + print RULES "# these files may not be created by targets of their name.\n"; + print RULES ".PHONY: build clean binary-indep binary-arch binary\n"; + print RULES ".PHONY:$binary_package_targets\n"; + print RULES ".PHONY:$install_targets\n"; + + close (RULES); + chmod (0755, $fname) or die "could not make `$fname' executable: $!"; +} + +sub make_debian_description($) +{ + my @new_desc = (); + my $old_desc = $_[0]; + for (split /\n/, $old_desc) + { + if (/^\S/) + { push @new_desc, " $_" } + elsif (/\S/) + { push @new_desc, $_ } + else + { push @new_desc, " ." } + } + return join ("\n", @new_desc); +} + +sub make_debian_directory($$) +{ + my ($package, $source_dir) = @_; + my $output_dir = "$source_dir/debian"; + + mkdir("$output_dir", 0755) + or die "couldn't make the directory `$output_dir': $!"; + + # --- Create control file --- + open CONTROL, ">$output_dir/control" or die "couldn't create control file"; + write_entries ('CONTROL', $package, + [qw(Source Section Priority)], + [qw(lcname section priority)]); + if (defined($package->{authors})) + { + my $author0 = $package->{authors}->[0]; + print CONTROL "Maintainer: $author0\n"; + } + print CONTROL "Standards-Version: 3.0.1\n\n"; + + my $targets = $package->{targets}; + for my $target (@$targets) + { + my $target_name = $target->{package_name}; + my $deb_description; + if (defined($target->{description})) + { + $deb_description = make_debian_description ($target->{description}); + } + else + { + $deb_description = make_debian_description ($package->{description}); + } + my $target_arch = 'any'; + $target_arch = 'all' if $target->{arch_indep}; + print CONTROL "Package: " . lc($target_name) . "\n", + "Architecture: ", + ($target->{arch_indep} ? "all" : "any"), + "\n"; + my $depends = $target->{depends}; + print CONTROL "Depends: $depends\n" if defined $depends; + my $conflicts = $target->{conflicts}; + print CONTROL "Conflicts: $conflicts\n" if defined $conflicts; + print CONTROL "Description: $deb_description\n\n"; + } + close CONTROL; + + for my $target (@$targets) + { + my $target_name = $target->{package_name}; + my $subname_dot; + if ($target->{name} eq '{MAIN}') + { + $subname_dot = ""; + } + else + { + $subname_dot = "$target_name."; + } + + # Create the list of all installed files (including docs). + my $list = $target->{files}; + my $docdir = "/usr/share/doc/" . $target->{package_name}; + $list = [] unless defined $list; + my @files = ( + @$list, + debian_docs_from_list ($target->{source_docs}), + debian_docs_from_list ($target->{installed_docs}), + "$docdir/" . $package->{changelog_file} + ); + + # --- Create the .files files --- + write_list_to_file (\@files, "$output_dir/$target_name.files"); + + if (defined $target->{conffiles}) + { + # --- Create the .conffile files --- + write_list_to_file ($target->{conffiles}, + "$output_dir/$target_name.conffiles"); + } + + # --- If ldconfig is needed, create .prerm and .postinst files. --- + # XXX: i gotta learn if prerm is really correct. + if ($target->{needs_ldconfig}) + { + my $do_ldconfig_script = <<'EOF'; +#! /bin/sh + +if test -x /sbin/ldconfig ; then + /sbin/ldconfig +else + ldconfig +fi +EOF + make_file ("$output_dir/$target_name.postinst", + $do_ldconfig_script); + make_file ("$output_dir/$target_name.prerm", + $do_ldconfig_script); + $target->{debian_data}->{needs_prerm} = 1; + $target->{debian_data}->{needs_postinst} = 1; + } + } + + my $prefab_entry; + { + my $package_name = $package->{name}; + my $version = $package->{version}; + my $release = $package->{release}; + my $packager = $package->{packager}; + my $packager_email = $package->{packager_email}; + my $date = `date -R` ; chomp ($date); + die unless defined $package_name; + die unless defined $version; + die unless defined $release; + die unless defined $packager; + die unless defined $packager_email; + $prefab_entry = + "$package_name ($version-$release) $debian_dist; urgency=low\n\n" . + " * packaged by pkgwrite\n" . + "\n" . + " -- $packager <$packager_email> $date\n"; + } + $changelog = $package->{changelog}; + if (defined($changelog)) + { + $changelog = "$source_dir/$changelog"; + my $cp = changelog_parser_new ($changelog); + die "couldn't read $changelog" unless defined $cp; + my $got_first = 0; + open OCL, ">$output_dir/changelog" or die "couldn't create changelog"; + while (changelog_parser_advance ($cp)) + { + # Verify that the changelog matches package/version. + unless ($got_first) + { + if ($cp->{package} ne $package->{name}) + { + die "package name from changelog (" . $cp->{package} . ") " + . "and according to the pkgwriteinfo " + . "(" . $package->{name} . ")" + . " do not match!"; + } + my $ver = $package->{version} . '-' . $package->{release}; + if ($cp->{version} ne $ver) + { + if ($strict_changelog_checking) + { + die "package version in changelog (".$cp->{version}.")" + . " and in pkgwriteinfo file ($ver) do not match!"; + } + else + { + print OCL $prefab_entry; + } + } + $got_first = 1; + } + + changelog_write_debian_style ($cp, 'OCL'); + } + close OCL; + changelog_parser_destroy ($cp); + } + else + { + make_file ("$output_dir/changelog", $prefab_entry); + } + + # --- Create rules file --- + write_rules_file ("$output_dir/rules", $package); +} + + +#===================================================================== +# Section 7: Make a package from a tarball. +#===================================================================== +sub make_package($$$$$) +{ + my ($package, $tarball, $vendor, $arch, $output_dir) = @_; + + my $workdir = get_work_dir (); + chdir ($workdir) or die "couldn't cd $workdir"; + + # Assert: Various paths cannot be relative. + die unless $tarball =~ m,^/,; + die unless $output_dir =~ m,^/,; + + # Make the packages. + if ($vendor eq 'redhat') + { + my $rhdir = "$workdir/redhat"; + my @rpm_commands = make_redhat_dir ($package, $rhdir, $tarball, $arch); + chdir "$rhdir" or die "couldn't cd to $rhdir"; + for my $rpm_command (@rpm_commands) + { + run ("$rpm_command"); + } + my $rpmdir = "$workdir/redhat/rpm-tmp/usr/src/redhat"; + run ("cp $rpmdir/SRPMS/*.rpm $rpmdir/RPMS/*.rpm $output_dir"); + } + elsif ($vendor eq 'debian') + { + my $ddir = "$workdir/debian"; + mkdir ($ddir, 0775) or die "couldn't make the directory $ddir"; + + # Untar. + chdir ($ddir) or die "couldn't cd to $ddir"; + run ("zcat $tarball | tar xf -"); + + # Make the packaging directory. + my $dir; + $dir = "$ddir/" . $package->{name} . '-' . $package->{version}; + + make_debian_directory ($package, "$dir"); + chdir ($dir) or die "couldn't cd to $dir"; + run ("cd $dir && dpkg-buildpackage -rfakeroot -uc -us"); + run ("mv $ddir/*.deb $output_dir"); + my $dprefix = $package->{name} + . "_" . $package->{version} + . "-" . $package->{release}; + + # Move the debian source package. + run ("mv $ddir/$dprefix.tar.gz $output_dir"); + run ("mv $ddir/$dprefix*.changes $output_dir"); + run ("mv $ddir/$dprefix*.dsc $output_dir"); + } + else + { + die "i can only make `redhat' and `debian' packages"; + } +} + + +#===================================================================== +# Section 8: Usage message. +#===================================================================== +sub usage +{ + print STDERR <<"EOF"; +usage: pkgwrite (--tarball=TARBALL | --srcdir=SRCDIR) + [--pkgwriteinfo-file=pkgwriteinfo] + [--no-cleanup] + [--no-strict-changelog] + [--debian-dist=DIST] + --format=(redhat|debian|all) + --output=OUTPUT + --arch=ARCH + +Make either redhat or debian packages for source code and +a pkgwriteinfo file. + +(Run pkgwrite --extra-help for unusual command-line flags.) + +EOF + print STDERR "The system supports .rpm building.\n" if $has_rpm_support; + print STDERR "The system supports .deb building.\n" if $has_dpkg_support; + exit (1); +} + +sub extra_usage +{ + print STDERR <<"EOF"; +usage: pkgwrite [flags] + +Querying hardcoded lists of values in pkgwrite: + + pkgwrite --query-list={deb-sections,rpm-groups,deb-priorities} +EOF + exit (1); +} + +sub version +{ + print "This is pkgwrite, version $PKGWRITE_VERSION.\n"; + exit 0; +} + + + + +#===================================================================== +# Section 9: Main program. +#===================================================================== +my $pkg_tarball; +my $pkg_sourcedir; +my $pkgwriteinfo_file; +my $pkg_format; +my $pkg_arch; +my $output_dir; + +# --- Process arguments --- +while (defined($arg=shift(@ARGV))) + { + # Arguments that cause us to print something and exit. + if ($arg eq '--help') { usage () } + if ($arg eq '--extra-help') { extra_usage () } + if ($arg eq '--version') { version () } + if ($arg =~ s/--query-list=//) { dump_list ($arg); } + + # Other flags. + if ($arg eq '--no-strict-changelog') { $strict_changelog_checking = 0; } + elsif ($arg eq '--skip-sanity-check') { $do_sanity_check = 0; } + elsif ($arg eq '--no-cleanup') { $do_cleanup = 0; } + elsif ($arg =~ s/--tarball=//) { $pkg_tarball = $arg; } + elsif ($arg =~ s/--source-dir=//) { $pkg_sourcedir = $arg; } + elsif ($arg =~ s/--output=//) { $output_dir = $arg; } + elsif ($arg =~ s/--pkgwriteinfo-file=//){ $pkgwriteinfo_file = $arg; } + elsif ($arg =~ s/--debian-dist=//) { $debian_dist = $arg; } + elsif ($arg =~ s/--arch=//) { $pkg_arch = $arg; } + elsif ($arg =~ s/--format=//) { $pkg_format = $arg; } + elsif (($arg eq '--tarball') + || ($arg eq '--source-dir') + || ($arg eq '--output') + || ($arg eq '--pkgwriteinfo-file') + || ($arg eq '--format') + || ($arg eq '--arch') + || ($arg eq '--debian-dist') + || ($arg eq '--query-list')) + { + my $newarg = shift(@ARGV); + die "$arg requires a parameter" unless defined $newarg; + if ($arg eq '--tarball') { $pkg_tarball = $newarg; } + elsif ($arg eq '--source-dir') { $pkg_sourcedir = $newarg; } + elsif ($arg eq '--pkgwriteinfo-file'){ $pkgwriteinfo_file = $newarg; } + elsif ($arg eq '--output') { $output_dir = $newarg; } + elsif ($arg eq '--arch') { $pkg_arch = $newarg; } + elsif ($arg eq '--debian-dist') { $debian_dist = $newarg; } + elsif ($arg eq '--format') { $pkg_format = $newarg; } + elsif ($arg eq '--query-list') { dump_list ($newarg); } + else { die "internal error" } + } + else + { + warn "unrecognized argument: $arg"; + usage (); + } + } + +unless (defined($pkg_tarball) || defined($pkg_sourcedir)) + { + die "either --tarball or --source-dir must be specified" + } +unless (defined ($pkg_format)) + { + die "--format must be specified"; + } +unless (defined ($output_dir)) + { + die "--output must be specified"; + } +unless (defined ($pkg_arch)) + { + die "--arch must be specified"; + } +if ($pkg_format ne 'all' + && $pkg_format ne 'debian' + && $pkg_format ne 'redhat') + { + my $valid = join (', ', qw(all debian redhat)); + die "format `$pkg_format' was invalid: only $valid are known"; + } +if (($pkg_format eq 'all' || $pkg_format eq 'redhat') + && !$has_rpm_support) + { + die "cannot make RPMs on this system"; + } +if (($pkg_format eq 'all' || $pkg_format eq 'debian') + && !$has_dpkg_support) + { + die "cannot make Debian packages on this system"; + } + + +# Make all paths absolute. +# +# We are about to `chdir' all over the place. +# While technically `make_absolute' is cheesy +# (b/c eg it doesn't work if `pwd` has been deleted), +# it least it works in practice, +# and makes the programming much easier. +if (defined($pkg_tarball)) + { $pkg_tarball = make_absolute ($pkg_tarball) } +if (defined($pkg_sourcedir)) + { $pkg_sourcedir = make_absolute ($pkg_sourcedir) } +if (defined($pkgwriteinfo_file)) + { $pkgwriteinfo_file = make_absolute ($pkgwriteinfo_file) } +if (defined($output_dir)) + { $output_dir = make_absolute ($output_dir); + run ("mkdir -p $output_dir"); } +if (defined($debian_changelog)) + { $debian_changelog = make_absolute ($debian_changelog) } + + +# Create pkgwriteinfo_file if necessary. +if (!defined($pkgwriteinfo_file)) + { + # Build a temporary working area to monkey with the source code. + my $workdir = get_work_dir (); + my $edir = "$workdir/pkgwriteinfo-tmp-extract"; + mkdir ($edir, 0755) or die; + my $tmp_source_dir; + + # Extract the source code, to (hopefully) find a pkgwriteinfo{,.in} file. + if (defined($pkg_tarball)) + { + run ("cd $edir ; $gunzip < $pkg_tarball | tar xf -"); + + # find the tmp source directory inside $edir. + opendir X, "$edir" or die "error scanning $edir"; + for (readdir X) + { + next if $_ eq '.'; + next if $_ eq '..'; + next unless -d "$edir/$_"; + $tmp_source_dir = "$edir/$_"; + last; + } + closedir X; + } + else + { + $tmp_source_dir = $pkg_sourcedir; + } + die "could not find directory in tarball" unless defined $tmp_source_dir; + + # If there isn't a pkgwriteinfo file, try running configure. + if (!-e "$tmp_source_dir/pkgwriteinfo") + { + if (-e "$tmp_source_dir/pkgwriteinfo.in") + { + if (! -x "$tmp_source_dir/configure") + { + # Uh, maybe `bootstrap' or `autogen.sh' will help? + if (-x "$tmp_source_dir/bootstrap") + { + run ("cd $tmp_source_dir; ./bootstrap"); + run ("cd $tmp_source_dir; ./configure --quiet"); + } + elsif (-x "$tmp_source_dir/autogen.sh") + { + run ("cd $tmp_source_dir; ./autogen.sh --quiet"); + } + } + else + { + # Excellent: configure it. + run ("cd $tmp_source_dir; ./configure --quiet"); + } + unless (-e "$tmp_source_dir/pkgwriteinfo.in") + { + warn "pkgwriteinfo is probably needed in AC_OUTPUT"; + } + } + else + { + die "could not find pkgwriteinfo{.in} in the tarball"; + } + } + + # If there isn't a pkgwriteinfo file, it's an error. + if (!-e "$tmp_source_dir/pkgwriteinfo") + { + die "couldn't make pkgwriteinfo file"; + } + + # Copy the pkgwriteinfo file about and remove $edir. + $pkgwriteinfo_file = "$workdir/pkgwriteinfo"; + run ("cp $tmp_source_dir/pkgwriteinfo $pkgwriteinfo_file"); + run ("rm -rf $edir"); + } + +# --- Build the package(s) --- +my $package = parse_pkgwriteinfo_file ($pkgwriteinfo_file); +die "couldn't parse package from $pkgwriteinfo_file" unless defined $package; + +# Sanity check the package. +sanity_check_package ($package) if ($do_sanity_check); + +$package->{package_tarball} = $pkg_tarball; +$package->{package_source_dir} = $pkg_sourcedir; + +# Produce a list of formats. +my @format_list; +if ($pkg_format eq 'all') + { + @format_list = qw(debian redhat) + } +else + { + @format_list = ( $pkg_format ); + } + +# Make all the desired formats. +for my $format (@format_list) + { + make_package ($package, $pkg_tarball, $format, $pkg_arch, $output_dir); + } + +# Clean up. +remove_work_dir (); + +exit (0); + +# --- Miscellaneous "main" helper functions --- + +# dump_table: Print the values of a hash-table reference to standard-output. +sub dump_table ($) +{ + my $table = $_[0]; + for (sort keys %$table) + { + print $table->{$_}, "\n"; + } + exit (0); +} + +# dump_list: dump one of the standard builtin hashtables to standard output. +sub dump_list ($) +{ + my $list = $_[0]; + if ($list eq 'deb-sections') { dump_table (\%DPKG_SECTIONS); } + elsif ($list eq 'deb-priorities') { dump_table (\%DPKG_PRIORITY_LEVELS); } + elsif ($list eq 'rpm-groups') { dump_table (\%RPM_GROUPS); } + else { die "unknown list to dump (to --query-list): $list" } +} + + +#===================================================================== +# Section 10: POD Documention. +#===================================================================== +=pod + +=head1 NAME + +pkgwrite - Make RedHat and Debian packages from the same source. + +=head1 SYNOPSIS + + pkgwrite (--tarball=TARBALL | --srcdir=SRCDIR) \ + [--pkgwriteinfo-file=pkgwriteinfo] \ + --arch=ARCH \ + --format={redhat,debian} + [options] + + + pkgwrite --query-list=LISTNAME + +=head1 DESCRIPTION + +pkgwrite takes a standard automake package, either from a source directory +or from a distributed tarball, and a C input file +and makes either RedHat or Debian packages. + +The actual package source code must be specified either by +directory (using --srcdir) or from a tarball created with `make dist' +(using --tarball). + +Additional packaging information is taken from pkgwriteinfo. +If --pkgwriteinfo-file is omitted, pkgwriteinfo from the source +directory or tarball is taken instead. (after configure is run, +so you might generally use a pkgwriteinfo.in). + +There are a few command-line parameters which affect the package-making: + +=over 4 + +=item --no-strict-changelog + +Don't force the user's changelog and pkgwriteinfo file to +have the same version. (If the packaging system +requires that the changelog's latest entry be equal to +the package's version, then pkgwrite will generate a +changelog entry. This happens under Debian.) + +=item --no-cleanup + +Don't remove the temporary directory (which will +be C). Useful for debugging. + +=item --debian-dist=DIST + +Generate packaging for the given debian distribution: +this mostly affects the changelog so +setting DIST to C or C is recommended. + +=item --skip-sanity-check + +Disable the usual checks that the package is valid. +The package may still partially work, even if a sanity-check +would normally fail. + +=back + +=head1 TERMINOLOGY + +=over 4 + +=item package + +A family of packages for various distributions +which all come from one tarball and one pkgwriteinfo file. + +=item build + +A compilation of the source code into binaries. +Some packages require multiple builds, for example, to make debugging +and nondebugging versions of a libraries. +Normally you just use the C<{MAIN}> build. + +=item target + +A single set of installed files in a package. +Simple packages only have a single target C<{MAIN}> +because the package is an all-or-nothing proposition. + +Some packages contain many parts, not all applicable to all users. +These packages should be broken in to different targets. + +For example, a client/server based application might be +conveniently packaged C, C, C. +That way, users without X can use the curses version without +installing gtk+, often the clients and servers are run exclusively on +different machines, so installing both is a waste of disk space. + +=back + +The resulting system-dependent binary's name is just +the main C name if the C is C<{MAIN}>; +otherwise it will be the C and C separated by +a hyphen (C<->). + +=head1 MAINTAINING CHANGELOGS + +We recommend that you maintain a changelog in debian format, +here is an example: + + gdam (0.934-1) unstable; urgency=low + + * many bug fixes + * split into many packages + + -- David Benson Wed, 17 Jan 2000 13:09:36 -0800 + +(No spaces for each version banner; 2 spaces on each bullet; +1 space before the packager byline.) + +If you don't maintain a changelog, we will generate a changelog +with just this version of the package in it. + +You should specify the changelog using the C directive. + +=head1 EXAMPLES + +Here are a few examples of common types of packages. +The pkgwrite distribution includes these packages +inside the C directory. + +=head2 EXAMPLE: SINGLE-TARGET PACKAGE + +The most common type of package has one set of files +it installs or uninstalls: there are no packaged bits or pieces. +(A Target in pkgwrite terminology is the installed set of files.) + +Here is the pkgwrite file from the single-target example +included with the pkgwrite distribution: + + Package: aa + Section: text + Group: Applications/Text + Priority: low + Home-Page: NONE + Source-Url: NONE + Author: David Benson + Version: 0.0.8 + Release: 1 + Synopsis: test package aa + Packager: daveb + Packager-Email: daveb@ffem.org + License: NONE + Description: test package (aa). + + Build: {MAIN} + + Target: {MAIN} + Files: /usr/bin/dummy-* + Synopsis: test a (single-target package) + +This package's name is C; this file will produce a binary RPM +named C. + +=over 4 + +=item * + +we wanted to name this C, but debian packages must be at least two +letters. + +=item * + +$ARCH is the target architecture, for example +C, C, or C. + +=item * + +The Target C<{MAIN}> is special, it +means "don't use any suffix" -- the package's +name is to be C. For any other Target line, +if STRING was specified, the resulting +RPM or deb would have the name C. + +=item * + +Each wildcard from Files lines describe a file or files to move +into that target. + +=item * + +Unlisted files will not wind up in any binary package. + +=back + +=head2 EXAMPLE: MULTI-TARGET PACKAGE + +A multi-target, single-build package is a package that need +only be compiled once, but which must be separated into +several system packages, because the targets appeal to +different users or have different dependencies. + +Here is the pkgwriteinfo file from the example multi-target single-build +package: + + + Package: bb + Section: text + Group: Applications/Text + Priority: low + Home-Page: NONE + Source-Url: NONE + Author: David Benson + Version: 0.0 + Release: 1 + Synopsis: test package bb + Packager: daveb + Packager-Email: daveb@ffem.org + License: NONE + Description: test package (bb). + + Build: {MAIN} + + Target: a + Files: /usr/bin/bb-a + Synopsis: part a of package bb + Description: whatever (bb-a) + + Target: b + Files: /usr/bin/bb-b + Synopsis: part b of package bb + Description: whatever (bb-b) + +In this package, only a single default Build: is required. +Some packages may require the C or +C fields in order to compile correctly. + +By default, all the targets use the C<{MAIN}> Build. + +Then each package contains a default file list, +a description and a synopsis. + +=head2 EXAMPLE: MULTI-BUILD PACKAGE + +The most complex type of package must be built multiple times, +with different configure or make flags. Each target must then +refer to the build from which it was produced, +using the C field (the default is C<{MAIN}>). + +Here is the example of such a package from the C +distribution: + + Package: cc + Section: text + Group: Applications/Text + Priority: low + Home-Page: NONE + Source-Url: NONE + Author: David Benson + Version: 0.0 + Release: 1 + Synopsis: test package cc + Packager: daveb + Packager-Email: daveb@ffem.org + License: NONE + Description: test package (cc). + + Build: nond + Configure-Flags: --program-suffix=-nondebug + + Build: d + Configure-Flags: --program-suffix=-debug + + Target: nondebug + Which-Build: nond + Files: /usr/bin/test-nondebug + Synopsis: nondebug package cc + Description: whatever (cc-nondebug) + + Target: debug + Which-Build: d + Files: /usr/bin/test-debug + Synopsis: debug package cc + Description: whatever (cc-debug) + +Each Build section corresponds to a complete configure, build, install phase. +In this package, the C build just wants configure to be run + configure --program-suffix=-nondebug ... +whereas for the C build, + configure --program-suffix=-debug ... +(Note that the ... will be somewhat different from distribution to +distribution) + +Also, it is often convenient to use the same names for the builds and +the targets. We would rename C as C and C as C +if this were a real package -- we did this to discuss it more +conveniently. + +It is perfectly possible to have more than one Target pointing to the same +Build, just as multi-target single-build packages do. +But the opposite is not allowed: a Target must specify exactly one +Build. + +=head1 HARDCODED VALUES + +Many lists and values are hardcoded into pkgwrite. +You may query these lists through the --query-list flag. +Here are the lists you may obtain in this manner: + +=over 4 + +=item deb-sections + +Known allowed Section: fields for debian packages. + +=item rpm-groups + +Known allowed Group: fields for redhat packages. + +=item deb-priority + +Known allowed Priority: fields for debian packages. + +=back + +For example to get a list of allowed values for the Section: +field, use C. + +=head1 PKGWRITE'S pkgwriteinfo FORMAT + +This (long) section describes the file +that describes the targets to build from a tarball. +This description file is called a C file. + +The C file consists of one package description part, +then a number of Build sections, +then a number of Target sections. + +=head2 PER-PACKAGE INFO + +The package file should begin with a section that describes +the overall source code of the package: + + Package: gdam + Section: sound + Group: Multimedia/Sound + Priority: optional + Home-Page: http://ffem.org/gdam + Source-Url: http://ffem.org/gdam/downloads/gdam-0.0.930.tar.gz + Version: 0.0.930 + Release: 1 + Author: David Benson + +Here is a description of each allowed field: + +=over 4 + +=item Package + +Name of the source package. + +=item Output-Package + +Sometimes, all packages from a tarball use a somewhat +different name than the tarball itself. +This is used as a bit of a hack to support co-installation +of multiple versions of a package. This is like +gtk, which has gtk1.2 and gtk2.0 packages, which can be installed concurrently. + +=item Version + +The version number given to this version of the package +by its maintainer. + +=item Release + +Increment this each time a new package is released without +a corresponding upstream version-number change. + +=item Section + +The debian section this package belongs in. + +=item Group + +The redhat group this package belongs in. + +=item Priority + +Priority of this package (debian-style). + +=item Home-Page + +A URL giving the home page for this package or project. + +=item Source-Url + +A URL describing how to download this package. + +=item Author + +An author of this package, with email optional. + +=item Synopsis + +Under one line summary of this target. + +=item Description + +Multiple-line description of this target. + +=item Packager + +Full name of the person who made this packaging. + +=item Packager-Email + +Email address at which to reach the packager. + +=item Changelog + +Specify the location of a Debian format changelog to include with +the package (it will be converted to another standard format, if needed) + +=item Upstream-is-Packager + +Whether the packager (the person running C) +is the same as the upstream maintainer of a package. + +Right now, this only affects the filename used for the changelog, +it will either be C if they are the same person, +or C if they are not. + +=back + +=head2 PER-TARGET INFO + +For each output binary package there must be a "target" section: + + Target: xmms-plugins + Depends: gdam-clients-gtk, gdam-server, xmms + Synopsis: GDAM XMMS plugin support + Description: use XMMS visualization plugins with GDAM. + Files: /usr/bin/gdamxmmsvishelper + +=over 4 + +=item Target + +Name of this target. The name of the package that results will +be prepended with SOURCE-; in this example the package's name +is C. + +=item Platform-Independent + +Set this to C if this package will be installable on +any architecture (it contains no system-specific or compiled code). + +Set this to C for packages containing compiled, +architecture-specific binaries. + +=item Depends + +Debian-formatted dependency lists for this package. + +=item Redhat-Requires + +Specify the redhat packages that this one depends on. +(We will try to compute this from the C lines by default; +this is just in case we cannot guess correctly.) + +=item Conflicts + +Debian-formatted list of packages that conflict with this one. + +=item Redhat-Conflicts + +Redhat-formatted list of packages that conflict with this one. +Computed from C by default. + +=item Synopsis + +Under one line summary of this target. + +=item Man-Page + +The basename of a man page, for example C. +It will automatically be installed into the correct +section directory based on its extension. + +=item Doc + +Miscellaneous documentation. +Each path is assumed to be inside the installed area. + +It will be always be copied into the distribution's +documentation area, and it will be gzip'd if that is needed. +Also, whole directories will be recursively copied, +if the entry ends with a /, for example, + + Doc: /usr/share/doc/gdam/example-configs/ + +=item Source-Doc + +Miscellaneous documentation that is not normally +installed by this package's makefile: + +These must be files directly from the distributed tarball. +They will be always be copied into the distribution's +documentation area and will be gzip'd if that is needed. +Whole directories will be recursively copied, +if the entry ends with a /. + +=item Description + +Multiple-line description of this target. + +=item Which-Build + +Name of the build whose files should be used. +(Defaults to C<{MAIN}>). + +=item Files + +A wildcard matching ordinary files to be distributed with +this target. + +=back + +=head2 PER-BUILD INFO + +=over 4 + +=item Build + +The name of this build. The default build should be named +C<{MAIN}>. + +=item Configure-Flags + +Options to be passed to the C script. + +=item Configure-Envars + +Space-separated environment variables to add when configuring. + +=item Make-Flags + +Extra parameters to the C program during both +the build and the install phase. + +=item Build-Flags + +Extra parameters to the C program during just +the build phase. + +=item Install-Flags + +Extra parameters to the C program during just +the install phase. + +=item Extra-Build-Targets + +Another C target that is necessary to build the package, +that is not run by the default target. This must not +require root priviledges. (You may specify +any number of C lines.) + +=item Extra-Install-Targets + +Another C target that is necessary to install the package, +that is not run by the default target. On Debian systems, these commands +may require root or fakeroot. (You may specify +any number of C lines.) + +=back + +=head1 DEBUGGING + +If you have problems, here are some hints: + +=over 4 + +=item * + +If you are familiar with one of the packaging systems (Redhat or Debian) +that is not working, try building that with either +the C<--no-cleanup> flag or equivalently have the C +environment variable set to C<0>. (You can find the +directory where work was done by running: C) + +=item * + +Otherwise, redirect the output to a log file and read the whole thing. +Some packaging systems output a log of messages +even after a fatal error. + +=item * + +Then, try and cut out pieces of the C +file until you locate the problem. +Report a bug if the problem is clear. + +=back + +=head1 COMMON PROBLEMS + +Here are some trivial mistakes made often: + +=over 4 + +=item * + +C is generated by C from C. +It is easy to forget to rerun C and very slow. + +I don't know of any easy solution to forgetting +at the moment, but you may just run C +instead, in order save time. + +=back + +=head1 RELEASING PACKAGES + +Of course, you can and should work however you want, +but for your information, I will write the testing process +I use for releasing packages. + +I always make C packages that use +C inside the tarball. + +It is important to note that my packages always +support C and C using C, +and that these targets only C if the tarball +isn't present! + +=over 4 + +=item 1 + +Check out the package from CVS. Then either use whichever +bootstrap script is provided: my recent packages use C +(as recommended by the C authors), but many packages +use C<./autogen.sh>, which traditionally also runs C<./configure>. + +If you used C, you will need to run C<./configure>. + +Then make the tarball using C. + +=item 2 + +On a RedHat machine and a Debian machine, +untar the tarball and copy that same tarball +into the PACKAGE-VERSION directory that was created. +(This way, the exact same tarball will be reused). + +=item 3 + +On each system, run C<./configure>, then +C and C, respectively. + +=item 4 + +Copy all the returned packages, the C tarball, +into a directory which you will upload. + +=item 5 + +Test install those packages on machines. + +I also inspect them with the following commands: + + System| RedHat Debian + Action | + --------------+------------------------------------------------ + List of files | rpm -qpl *.rpm dpkg-deb -c *.deb + General data | rpm -qpi *.rpm dpkg-deb -e *.deb + +=back + +=head1 EXAMPLE AUTOMAKE FILES + +TODO + +=head1 AUTHOR + +Written by Dave Benson . + +=cut diff --git a/src/google/protobuf-c/protobuf-c.c b/src/google/protobuf-c/protobuf-c.c index d844f1e..d81f205 100644 --- a/src/google/protobuf-c/protobuf-c.c +++ b/src/google/protobuf-c/protobuf-c.c @@ -18,22 +18,9 @@ #include /* for occasional printf()s */ #include /* for abort(), malloc() etc */ #include /* for strlen(), memcpy(), memmove() */ -#include /* for __BYTE_ORDER, __LITTLE_ENDIAN */ -#define DO_LITTLE_ENDIAN_OPTIMIZATIONS 0 #define PRINT_UNPACK_ERRORS 1 -#if DO_LITTLE_ENDIAN_OPTIMIZATIONS -# if (__LITTLE_ENDIAN == __BYTE_ORDER) -# define IS_LITTLE_ENDIAN 1 -# else -# define IS_LITTLE_ENDIAN 0 -# endif -#else -# define IS_LITTLE_ENDIAN 0 -#endif - - #include "protobuf-c.h" #define MAX_UINT64_ENCODED_SIZE 10