pallet 1.2.1 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +42 -0
- data/README +55 -0
- data/lib/installer.rb +106 -0
- data/lib/pallet.rb +18 -8
- data/lib/pallet/deb.rb +387 -268
- data/share/debian/source/changelog +7 -0
- data/share/debian/source/control +19 -0
- data/share/debian/source/rules +25 -0
- data/share/debian/source/substvars +2 -0
- metadata +16 -26
- data/lib/dependencies.rb +0 -44
- data/lib/install_spec.rb +0 -89
- data/share/debian/changelog +0 -7
- data/share/debian/compat +0 -1
- data/share/debian/conffiles +0 -1
- data/share/debian/config +0 -8
- data/share/debian/control +0 -12
- data/share/debian/copyright +0 -7
- data/share/debian/dirs +0 -1
- data/share/debian/docs +0 -1
- data/share/debian/install +0 -1
- data/share/debian/postinst +0 -3
- data/share/debian/postrm +0 -3
- data/share/debian/rules +0 -61
- data/share/debian/templates +0 -10
data/CHANGELOG
CHANGED
@@ -1,3 +1,45 @@
|
|
1
|
+
pallet (1.5.0)
|
2
|
+
|
3
|
+
Release Notes:
|
4
|
+
|
5
|
+
This is largely a development release. Those wanting fully-featured
|
6
|
+
package building should use the latest stable release (1.2.1). Some
|
7
|
+
features of 1.2.1 are not currently supported, due to the complete
|
8
|
+
rework of the build process for Debian packages.
|
9
|
+
|
10
|
+
If building an individual Debian file for personal use and
|
11
|
+
publishing, "rake package:deb" will put the Debian package into
|
12
|
+
"pkg/#{project-name}-#{version}-{arch}.deb".
|
13
|
+
|
14
|
+
If building for upload to a Debian repository, the command
|
15
|
+
"rake package:deb:clean" will create the Debian build directory
|
16
|
+
ready for packaging. "dpkg-buildpackage -rfakeroot" will then
|
17
|
+
generate the Debian binary and source packages, as well as a
|
18
|
+
.changes file.
|
19
|
+
|
20
|
+
Improvements:
|
21
|
+
|
22
|
+
* Debian packaging rebuilt from scratch. No longer dependent on
|
23
|
+
debhelper and less dependent on a Debian environment
|
24
|
+
* Debian packages support Suggests, Recommends, and all other
|
25
|
+
package associations
|
26
|
+
* debian directory no longer blown away unless it's explicitly
|
27
|
+
managed by pallet (through use of a debian/.pallet-stamp flag)
|
28
|
+
|
29
|
+
Regressions:
|
30
|
+
|
31
|
+
* Debian packages no longer support crontabs, init.d scripts, and
|
32
|
+
other niceties that came for "free" with debhelper
|
33
|
+
* no longer truly supports building of Debian packages that use
|
34
|
+
compiled binaries; shared library dependencies and a few other
|
35
|
+
things are no longer handled, that debhelper did
|
36
|
+
* the Debian .changes file and other artefacts of building a source
|
37
|
+
package are no longer generated
|
38
|
+
|
39
|
+
Bugfixes:
|
40
|
+
|
41
|
+
* Debian packages are built in pkg, like other package types.
|
42
|
+
|
1
43
|
pallet (1.2.1)
|
2
44
|
|
3
45
|
Improvements:
|
data/README
CHANGED
@@ -0,0 +1,55 @@
|
|
1
|
+
=Pallet
|
2
|
+
|
3
|
+
* Author: Stephen Touset
|
4
|
+
* Website: http://pallet.rubyforge.org
|
5
|
+
* Rubyforge page: http://rubyforge.org/projects/pallet/
|
6
|
+
* SVN: svn://rubyforge.org/var/svn/pallet/trunk
|
7
|
+
|
8
|
+
See COPYRIGHT for distribution terms.
|
9
|
+
|
10
|
+
==Description
|
11
|
+
|
12
|
+
A Rakefile-based tool to easily build different package types (such as .deb, .gem) from the same source. Currently builds Debian packages and Ruby Gems, but is trivially extensible to provide support for other packaging formats (.tar.gz, .rpm, etc.).
|
13
|
+
|
14
|
+
==Usage
|
15
|
+
|
16
|
+
Include the pallet library in a new or existing Rakefile for your project. Start by including the pallet library in your Rakefile and defining project information that will be used in both the gem and deb rake packaging task options:
|
17
|
+
|
18
|
+
require 'pallet'
|
19
|
+
|
20
|
+
Pallet.new('projectname', "0.0.1") do |p|
|
21
|
+
p.author = 'Your Name'
|
22
|
+
p.email = 'your@email.com'
|
23
|
+
p.summary = 'Summary of your project'
|
24
|
+
end
|
25
|
+
|
26
|
+
Within the pallet block you can specify options for the gem and deb rake packaging tasks:
|
27
|
+
|
28
|
+
...
|
29
|
+
p.packages << Pallet::Gem.new(p) do |gem|
|
30
|
+
gem.depends = ['rake']
|
31
|
+
gem.requirements = ['fakeroot', 'dpkg-dev']
|
32
|
+
gem.files.include FileList['share/**/*']
|
33
|
+
end
|
34
|
+
|
35
|
+
p.packages << Pallet::Deb.new(p) do |deb|
|
36
|
+
deb.architecture = 'all'
|
37
|
+
deb.depends = ['rake', 'fakeroot', 'dpkg-dev']
|
38
|
+
deb.files = [InstallSpec['lib', '/usr/lib/ruby/1.8'],
|
39
|
+
InstallSpec['data', '/usr/share'],
|
40
|
+
InstallSpec['docs', '/usr/share/docs'],]
|
41
|
+
deb.commands.document = 'rake doc'
|
42
|
+
end
|
43
|
+
...
|
44
|
+
|
45
|
+
You should follow the installation policies of your distro when you package a .deb for public use.
|
46
|
+
|
47
|
+
==Hacking
|
48
|
+
|
49
|
+
Please feel free to contribute code to improve Pallet! Some areas that need attention:
|
50
|
+
|
51
|
+
* More package types (tar.gz, .rpm, etc.)
|
52
|
+
* A pallet rakefile generator
|
53
|
+
* Integration with other rakefile tools.
|
54
|
+
|
55
|
+
Contact Stephen Touset <stephen@ nospam @ touset.org> with diffs or inquiries.
|
data/lib/installer.rb
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
class Installer
|
4
|
+
|
5
|
+
attr_accessor :src, :dest
|
6
|
+
attr_accessor :user, :group
|
7
|
+
attr_accessor :mode, :dmode
|
8
|
+
|
9
|
+
# Initializes the installer. Will install files in _src_ to _dest_,
|
10
|
+
# using the options provided. Allowed options are _user_ (the file
|
11
|
+
# owner), _group_ (the group owner), and _mode_. If none are provided
|
12
|
+
# user and group default to +root+, and the mode defaults to the
|
13
|
+
# file's current permissions on the filesystem. _dmode_ is the mode
|
14
|
+
# for any directories created.
|
15
|
+
#
|
16
|
+
# If no block is provided, the Installer will glob all files in _src_.
|
17
|
+
# If a block is provided, it must return a list of files (scoped to
|
18
|
+
# the _src_ directory) that will be installed.
|
19
|
+
#
|
20
|
+
# All files copied will maintain their directory structure under the
|
21
|
+
# _src_ given. _src_ and _dest_ default to the current working
|
22
|
+
# directory, if not given.
|
23
|
+
#
|
24
|
+
# Examples:
|
25
|
+
#
|
26
|
+
# # 'foo/**/*' => 'bar'
|
27
|
+
# Installer.new('foo', 'bar')
|
28
|
+
#
|
29
|
+
# # 'foo/*.rb' => 'bar'
|
30
|
+
# Installer.new('foo', 'bar') { FileList['*.rb'] }
|
31
|
+
#
|
32
|
+
# # 'foo/bar/**/*' => 'baz/bar'
|
33
|
+
# Installer.new('foo', 'baz') { FileList['bar/**/*'] }
|
34
|
+
#
|
35
|
+
def initialize(src = '.', dest = '.', options = {}, &block)
|
36
|
+
|
37
|
+
self.src = src
|
38
|
+
self.dest = dest
|
39
|
+
|
40
|
+
self.user = options[:user] || 'root'
|
41
|
+
self.group = options[:group] || 'root'
|
42
|
+
self.mode = options[:mode]
|
43
|
+
self.dmode = options[:dmode]
|
44
|
+
|
45
|
+
@block = block || lambda { FileList['**/*'] }
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
# Installs the files specified during initialization to
|
50
|
+
def install(prefix)
|
51
|
+
|
52
|
+
##############################################
|
53
|
+
# Begin sadly misplace bit of code
|
54
|
+
# - We do this section here, since not all files may have been
|
55
|
+
# - generated by the time of initialization
|
56
|
+
##############################################
|
57
|
+
|
58
|
+
files = []
|
59
|
+
src = self.src
|
60
|
+
dest = File.join(prefix, self.dest)
|
61
|
+
|
62
|
+
if File.directory?(src)
|
63
|
+
# let the user specify files from within that directory, otherwise
|
64
|
+
# assume a complete glob
|
65
|
+
FileUtils.chdir(src) { files = @block.call.to_a }
|
66
|
+
else
|
67
|
+
# src is an individual file
|
68
|
+
src, files = File.dirname(self.src), File.basename(self.src)
|
69
|
+
end
|
70
|
+
|
71
|
+
# check for special files like device nodes
|
72
|
+
specials = files.map {|f| File.join(src, f) }.find_all do |f|
|
73
|
+
!File.file?(f) and !File.directory?(f)
|
74
|
+
end
|
75
|
+
|
76
|
+
raise ArgumentError, "special files #{specials.join ', '} are not allowed" \
|
77
|
+
unless specials.empty?
|
78
|
+
|
79
|
+
##############################################
|
80
|
+
# End sadly misplaced bit of code
|
81
|
+
##############################################
|
82
|
+
|
83
|
+
# split into files and dirs, so we can handle directory permissions
|
84
|
+
# correctly
|
85
|
+
files, dirs = files.partition {|f| File.file?(File.join(src, f)) }
|
86
|
+
|
87
|
+
# make sure the parent directory gets created
|
88
|
+
FileUtils.mkdir_p(dest) unless File.directory?(dest)
|
89
|
+
|
90
|
+
# sorting by length is an "easy" way of making sure parent
|
91
|
+
# directories are handled first
|
92
|
+
dirs.sort_by {|dir| dir.length }.each do |dir|
|
93
|
+
FileUtils.mkdir(File.join(dest, dir), :mode => dmode) \
|
94
|
+
unless File.directory?(File.join(dest, dir))
|
95
|
+
FileUtils.chown(user, group, dest)
|
96
|
+
end
|
97
|
+
|
98
|
+
# install the files into their proper place
|
99
|
+
files.each do |file|
|
100
|
+
FileUtils.install(File.join(src, file), File.join(dest, file), :mode => mode)
|
101
|
+
FileUtils.chown(user, group, File.join(dest, file))
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
data/lib/pallet.rb
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'rake'
|
3
3
|
|
4
|
-
require '
|
5
|
-
require 'pallet/gem'
|
6
|
-
require 'install_spec'
|
4
|
+
require 'installer'
|
7
5
|
|
8
6
|
class Pallet
|
9
7
|
|
10
|
-
VERSION = '1.
|
8
|
+
VERSION = '1.5.0'
|
11
9
|
NAMESPACE = 'package'
|
12
10
|
|
11
|
+
PACKAGE_DIR = 'pkg'
|
12
|
+
|
13
13
|
# The name of the project.
|
14
14
|
attr_accessor :name
|
15
15
|
|
@@ -54,8 +54,9 @@ class Pallet
|
|
54
54
|
self.description = ''
|
55
55
|
self.packages = []
|
56
56
|
|
57
|
-
# let block continue initialization
|
58
|
-
|
57
|
+
# let block continue initialization, don't bother testing for
|
58
|
+
# block_given? since we need a block
|
59
|
+
yield self
|
59
60
|
|
60
61
|
# check that we have _some_ package to build
|
61
62
|
if packages.empty?
|
@@ -85,7 +86,9 @@ class Pallet
|
|
85
86
|
task :clean
|
86
87
|
|
87
88
|
desc 'Clean up all package files'
|
88
|
-
task :clobber
|
89
|
+
task :clobber do
|
90
|
+
rm_r PACKAGE_DIR, :force => true
|
91
|
+
end
|
89
92
|
|
90
93
|
# define a task for all supported packages
|
91
94
|
self.packages.each do |package|
|
@@ -106,4 +109,11 @@ class Pallet
|
|
106
109
|
|
107
110
|
end
|
108
111
|
|
109
|
-
|
112
|
+
class String
|
113
|
+
def margin(indicator = '|')
|
114
|
+
gsub(/^\s*#{Regexp.escape(indicator)}/, '')
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
require 'pallet/deb'
|
119
|
+
require 'pallet/gem'
|
data/lib/pallet/deb.rb
CHANGED
@@ -1,311 +1,430 @@
|
|
1
|
-
|
1
|
+
require 'find'
|
2
2
|
|
3
|
+
class Pallet::Deb
|
4
|
+
|
5
|
+
# The name the generated rake task will use. Defaults to 'deb'.
|
6
|
+
attr_accessor :task_name
|
7
|
+
|
8
|
+
# A list of prerequisite tasks that will be completed before the
|
9
|
+
# packaging task starts.
|
10
|
+
attr_accessor :prerequisites
|
11
|
+
|
12
|
+
# An array of Debian build dependencies needed in order to build the
|
13
|
+
# package for this project. Will always include any dependencies
|
14
|
+
# implied by pallet.
|
15
|
+
attr_accessor :build_depends
|
16
|
+
|
17
|
+
# An array of Debian dependencies.
|
18
|
+
attr_accessor :depends
|
19
|
+
|
20
|
+
# An array of Debian recommended packages.
|
21
|
+
attr_accessor :recommends
|
22
|
+
|
23
|
+
# An array of Debian suggested packages.
|
24
|
+
attr_accessor :suggests
|
25
|
+
|
26
|
+
# An array of Debian conflicting packages.
|
27
|
+
attr_accessor :conflicts
|
28
|
+
|
29
|
+
# An array of Debian features this package provides.
|
30
|
+
attr_accessor :provides
|
31
|
+
|
32
|
+
# An array of Debian packages this package enhances.
|
33
|
+
attr_accessor :enhances
|
34
|
+
|
35
|
+
# A hash with each key being a file glob, and each value being the
|
36
|
+
# destination directory of files matching that glob. Do not include
|
37
|
+
# files specified in +changelog+, +copyright+, +docs+, +scripts+, or
|
3
38
|
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
# Automatic builder for Debian packages. Lets users automagically
|
7
|
-
# build a deb after specifying a few customizations.
|
8
|
-
#
|
9
|
-
# == Usage
|
10
|
-
#
|
11
|
-
# At a bare minimum, users must specify lists of files, and where to
|
12
|
-
# place them. The syntax for doing so is not obvious, so we will
|
13
|
-
# demonstrate by example.
|
39
|
+
# TODO: flesh out meaning
|
14
40
|
#
|
15
|
-
|
16
|
-
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
41
|
+
attr_accessor :files
|
42
|
+
|
43
|
+
# An enumerable object containing a list of all project documentation
|
44
|
+
# to include in the Debian package. If a directory is specified in the
|
45
|
+
# list, all files under that directory will be included as
|
46
|
+
# documentation. TODO: blah
|
47
|
+
attr_accessor :docs
|
48
|
+
|
49
|
+
# TODO: yeah...
|
50
|
+
attr_accessor :configs
|
51
|
+
|
52
|
+
# A hash pointing to the location of scripts the Debian package should
|
53
|
+
# include. # TODO: flesh this out
|
54
|
+
attr_accessor :scripts
|
55
|
+
|
56
|
+
# The machine architecture the package can be built to support.
|
57
|
+
# Defaults to 'all', which indicates that the project is
|
58
|
+
# machine-independent. Should be changed to 'any' if the project
|
59
|
+
# requires compilation to machine language, but should run on any
|
60
|
+
# architecture.
|
61
|
+
attr_accessor :architecture
|
62
|
+
|
63
|
+
# The location on the filesystem of the Debian-style project
|
64
|
+
# changelog. One will be created for you if it is not specified.
|
65
|
+
attr_accessor :changelog
|
66
|
+
|
67
|
+
# The location of the package's copyright. Must be present.
|
68
|
+
attr_accessor :copyright
|
69
|
+
|
70
|
+
# The urgency of the package. Defaults to 'low'.
|
71
|
+
attr_accessor :urgency
|
72
|
+
|
73
|
+
# The section the package has been classified into. Defaults to
|
74
|
+
# 'misc'. See the Debian Policy Manual for more information.
|
75
|
+
attr_accessor :section
|
76
|
+
|
77
|
+
# The distribution this package should be installed in. Defaults to
|
78
|
+
# 'unstable'.
|
79
|
+
attr_accessor :distribution
|
80
|
+
|
81
|
+
# The priority level of the package. Defaults to 'extra'.
|
82
|
+
attr_accessor :priority
|
83
|
+
|
84
|
+
# The latest version of the Debian policy we're compliant with.
|
85
|
+
POLICY_VERSION = '3.7.2.2'
|
86
|
+
|
87
|
+
# Location of the source package et al.
|
88
|
+
BUILD_DIR = 'debian'
|
89
|
+
|
90
|
+
# Filesystem location of template files
|
91
|
+
TEMPLATE_LOCATION = [
|
92
|
+
File.join(File.dirname(__FILE__), *%w{.. .. share debian}),
|
93
|
+
File.join(*%w{/ usr share pallet debian})
|
94
|
+
].find {|f| File.directory? f }
|
95
|
+
|
96
|
+
# A list of all build dependencies required by pallet.
|
97
|
+
PALLET_DEPENDS = ["pallet (>= #{Pallet::VERSION})"] << %w{rake}
|
98
|
+
PALLET_SUGGESTS = ['fakeroot']
|
99
|
+
|
20
100
|
#
|
21
|
-
#
|
22
|
-
|
23
|
-
#
|
24
|
-
|
25
|
-
# end
|
101
|
+
# Creates a new specification for building a deb. Yields itself to the
|
102
|
+
# block passed for further initialization. Must be passed an instance
|
103
|
+
# of the pallet with information such as version, author, package
|
104
|
+
# name, etc. already configured.
|
26
105
|
#
|
27
|
-
#
|
106
|
+
# Can be passed a hash whose key is the pallet, and whose value is
|
107
|
+
# either an array of Rake tasks this task depends on, or a single
|
108
|
+
# dependent Rake task.
|
28
109
|
#
|
29
|
-
# Pallet::Deb
|
30
|
-
#
|
31
|
-
# Otherwise, it will sign with the key associated with the
|
32
|
-
# maintainer's email address, if it is avaialble.
|
110
|
+
# # creates a new Pallet::Deb packager
|
111
|
+
# p.packages << Pallet::Deb.new(p)
|
33
112
|
#
|
34
|
-
#
|
113
|
+
# # creates a new Pallet::Deb packager, that depends on other tasks
|
114
|
+
# p.packages << Pallet::Deb.new(p => [:build, :doc])
|
35
115
|
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
# * Can only be used to build a single package, rather than multiple
|
39
|
-
# packages from one source.
|
40
|
-
# * Packages are installed into .., rather than pkg/
|
41
|
-
# * No doc-base integration
|
116
|
+
# # changes the task name to 'package:debian'
|
117
|
+
# p.packages << Pallet::Deb.new(p, 'debian')
|
42
118
|
#
|
43
|
-
|
44
|
-
|
45
|
-
# The name the generated rake task will use. Defaults to 'deb'.
|
46
|
-
attr_accessor :task_name
|
47
|
-
|
48
|
-
# An array of Debian build dependencies needed in order to build
|
49
|
-
# the package for this project. Defaults to any core build
|
50
|
-
# requirements introduced by pallet (currently only debhelper (>=
|
51
|
-
# 5) and dpkg-dev).
|
52
|
-
attr_accessor :build_depends
|
53
|
-
|
54
|
-
# All Debian packages this project depends on.
|
55
|
-
attr_accessor :depends
|
56
|
-
|
57
|
-
# Any additional requirements not satisfiable through the Debian
|
58
|
-
# packaging system. For instance, Ruby gems. Any requirements
|
59
|
-
# provided this way will be presented to the user through a
|
60
|
-
# Debconf dialog upon installation.
|
61
|
-
attr_accessor :requirements
|
62
|
-
|
63
|
-
# An array of any tasks which should run and complete successfully
|
64
|
-
# before the Debian package is built.
|
65
|
-
attr_accessor :prerequisites
|
66
|
-
|
67
|
-
# A struct containing a few statically-named commands that can
|
68
|
-
# replace parts of the Pallet::Deb build process. Currently
|
69
|
-
# allows assignment to the _build_, _install_, _package_,
|
70
|
-
# _document_, and _clean_ instance variables.
|
71
|
-
#
|
72
|
-
# _commands.build_ defaults to 'make', 'ant', or nil depending on
|
73
|
-
# whether or not a Makefile, build.xml, or no build file is
|
74
|
-
# detected, respectively.
|
75
|
-
#
|
76
|
-
# _commands.clean_ deafults to 'rake clean'. It can be overridden
|
77
|
-
# if you have another method of cleaning your build directory.
|
78
|
-
#
|
79
|
-
# _commands.install_ defaults to nil. If your project already
|
80
|
-
# provides a command that installs all files needed for the
|
81
|
-
# project into a prefixed directory, you can specify this rather
|
82
|
-
# than providing InstallSpecs to _Pallet::Deb#files_. However,
|
83
|
-
# this command +must+ be able to support a "DESTDIR=$DIR"-style
|
84
|
-
# parameter which determines the install prefix.
|
85
|
-
#
|
86
|
-
# _commands.package_ defaults to nil. If this is specified, the
|
87
|
-
# +entire+ build process will consist of calling this command.
|
88
|
-
# This might be desirable if your project already provides a
|
89
|
-
# _debian_ directory and a _make deb_ command to build the Debian
|
90
|
-
# package. Otherwise, leave this at its default.
|
91
|
-
#
|
92
|
-
# _commands.document_ defaults to nil. If specified, this command
|
93
|
-
# is run to build any automatically-generated documentation.
|
94
|
-
#
|
95
|
-
attr_accessor :commands
|
96
|
-
|
97
|
-
|
98
|
-
# An array of InstallSpec objects pointing to files and their
|
99
|
-
# destination in the finished package. May be omitted if
|
100
|
-
# _commands.install_ is provided.
|
101
|
-
attr_accessor :files
|
102
|
-
|
103
|
-
# A FileList, array, or other enumerable object containing a list
|
104
|
-
# of all project documentation to include in the Debian pacakge.
|
105
|
-
attr_accessor :docs
|
106
|
-
|
107
|
-
# The location on the filesystem of the project Changelog.
|
108
|
-
# Defaults to 'CHANGELOG', if one exists.
|
109
|
-
attr_accessor :changelog
|
110
|
-
|
111
|
-
# A hash pointing to the location of scripts the Debian package
|
112
|
-
# should include. The key in the hash is the debhelper-reconigzed
|
113
|
-
# type of script (cron.d, postinst, postrm, init.d, etc.). The
|
114
|
-
# value is its location in your project hierarchy.
|
115
|
-
attr_accessor :scripts
|
116
|
-
|
117
|
-
# The machine architecture the package can be built to support.
|
118
|
-
# Defaults to 'any', but 'all' should be used for languages that
|
119
|
-
# aren't compiled to a specific platform.
|
120
|
-
attr_accessor :architecture
|
121
|
-
|
122
|
-
# The version of the Debian policy we support
|
123
|
-
DEBIAN_POLICY_VERSION = '3.7.2'
|
124
|
-
|
125
|
-
# Mapping of build spec files to the command that invokes them. We
|
126
|
-
# don't include rake here, since most Ruby programs don't need
|
127
|
-
# "building", and there is no idiomatic rake task used for
|
128
|
-
# building extensions.
|
129
|
-
BUILD_COMMANDS = { 'Makefile' => 'make --silent',
|
130
|
-
'build.xml' => 'ant', }
|
131
|
-
|
132
|
-
# Build dependencies required by pallet, which will always be
|
133
|
-
# added to the debian/control file.
|
134
|
-
BUILD_DEPENDS = ['debhelper (> 5.0)', 'dpkg-dev']
|
135
|
-
|
136
|
-
# Default places to look for documentation.
|
137
|
-
DEFAULT_DOCS = FileList[%w{README* TODO* doc*}]
|
138
|
-
|
139
|
-
# Default changelog search locations.
|
140
|
-
DEFAULT_CHANGELOG = FileList[%w{CHANGELOG*}]
|
141
|
-
|
142
|
-
#
|
143
|
-
# Creates a new specification for building a deb. Yields itself to
|
144
|
-
# the blocked passed for further initialization.
|
145
|
-
#
|
146
|
-
def initialize(pallet)
|
147
|
-
|
148
|
-
@pallet = pallet
|
149
|
-
|
150
|
-
# configure defaults
|
151
|
-
self.task_name = :deb
|
152
|
-
self.depends = []
|
153
|
-
self.build_depends = []
|
154
|
-
self.requirements = []
|
155
|
-
self.prerequisites = []
|
156
|
-
self.files = []
|
157
|
-
self.docs = DEFAULT_DOCS
|
158
|
-
self.changelog = DEFAULT_CHANGELOG.first
|
159
|
-
self.scripts = {}
|
160
|
-
self.commands = Struct.new(:build, :clean, :install, :package, :document).new
|
161
|
-
self.architecture = 'any'
|
162
|
-
|
163
|
-
# try and determine a default build command
|
164
|
-
BUILD_COMMANDS.find do |file, command|
|
165
|
-
self.commands.build = command if File.exist? file
|
166
|
-
end
|
167
|
-
|
168
|
-
# default packaging command; adds GPG key if it's in the environment
|
169
|
-
self.commands.package = "dpkg-buildpackage -rfakeroot -b"
|
170
|
-
self.commands.package << " -k#{ENV['GPG_KEY_ID']}" if ENV['GPG_KEY_ID']
|
119
|
+
def initialize(pallet, task_name = 'deb')
|
171
120
|
|
172
|
-
|
173
|
-
self.commands.clean = 'rake --silent clean'
|
121
|
+
@pallet, self.prerequisites = deconstruct_hash(pallet)
|
174
122
|
|
175
|
-
|
176
|
-
yield self if block_given?
|
123
|
+
self.task_name = task_name
|
177
124
|
|
178
|
-
|
179
|
-
|
180
|
-
|
125
|
+
# file locations
|
126
|
+
self.files = []
|
127
|
+
self.docs = []
|
128
|
+
self.configs = []
|
129
|
+
self.scripts = {}
|
130
|
+
|
131
|
+
# package flags
|
132
|
+
self.architecture = 'all'
|
133
|
+
self.distribution = 'unstable'
|
134
|
+
self.priority = 'extra'
|
135
|
+
self.section = 'misc'
|
136
|
+
self.urgency = 'low'
|
137
|
+
|
138
|
+
# dependencies
|
139
|
+
self.build_depends = []
|
140
|
+
self.depends = []
|
141
|
+
self.recommends = []
|
142
|
+
self.suggests = []
|
143
|
+
self.conflicts = []
|
144
|
+
self.provides = []
|
145
|
+
self.enhances = []
|
146
|
+
|
147
|
+
# send me off to be configured
|
148
|
+
yield self if block_given?
|
149
|
+
|
150
|
+
# get rid of changelog, copyright if user specified them in docs
|
151
|
+
# self.docs.delete(self.changelog)
|
152
|
+
# self.docs.delete(self.copyright)
|
181
153
|
|
182
|
-
|
154
|
+
end
|
183
155
|
|
184
|
-
|
156
|
+
def define
|
185
157
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
158
|
+
desc 'Build this project into a Debian package'
|
159
|
+
task self.task_name => [ "#{self.task_name}:clean",
|
160
|
+
"#{self.task_name}:build",
|
161
|
+
"#{self.task_name}:package", ]
|
162
|
+
|
163
|
+
namespace self.task_name do
|
164
|
+
|
165
|
+
# perform the actual package build
|
166
|
+
task :build => self.prerequisites
|
167
|
+
|
168
|
+
# package the project
|
169
|
+
task :package => %w{dh:testdir dh:testroot
|
170
|
+
dh:install dh:installdocs
|
171
|
+
dh:installchangelogs
|
172
|
+
dh:compress dh:installdeb
|
173
|
+
dh:gencontrol} do
|
174
|
+
mkdir Pallet::PACKAGE_DIR unless File.directory?(Pallet::PACKAGE_DIR)
|
175
|
+
system *%W{dpkg-deb --build #{path_for(:bpkg)} #{path_for(:pkg)}}
|
190
176
|
end
|
191
177
|
|
192
|
-
|
193
|
-
task
|
194
|
-
# don't call debian/rules clean if we've already been called
|
195
|
-
# through Make; this might not be entirely reliable...
|
196
|
-
ENV['MAKELEVEL'] or system 'fakeroot debian/rules clean' if File.exist? 'debian/rules'
|
197
|
-
end
|
178
|
+
desc 'Clean the Debian build tree'
|
179
|
+
task :clean => %w{dh:clean}
|
198
180
|
|
199
|
-
|
200
|
-
task
|
201
|
-
rm_r
|
202
|
-
files = Dir["../#{@pallet.name}_#{@pallet.version}*.changes"] +
|
203
|
-
Dir["../#{@pallet.name}_#{@pallet.version}*.deb"]
|
204
|
-
files.each {|f| rm f if File.exist? f }
|
181
|
+
desc 'Remove all Debian files generated by pallet'
|
182
|
+
task :clobber do
|
183
|
+
rm_r path_for(:spkg) if File.exist? path_for(:pstamp)
|
205
184
|
end
|
185
|
+
|
186
|
+
namespace :dh do
|
206
187
|
|
207
|
-
|
188
|
+
# perform a clean of the entire debian tree
|
189
|
+
task :clean => %w{testdir testroot} do
|
190
|
+
rm_r path_for(:bpkg), :force => true
|
191
|
+
rm path_for(:spkg, 'files'), :force => true
|
192
|
+
end
|
208
193
|
|
209
|
-
|
194
|
+
# become fakeroot if not root already; god this is evil
|
195
|
+
task :testroot do
|
196
|
+
unless Process.uid == 0
|
197
|
+
begin
|
198
|
+
exec("fakeroot #{$0} #{ARGV.join(' ')}")
|
199
|
+
rescue Errno::ENOENT => ex
|
200
|
+
fail(<<-MESSAGE.margin.gsub(%r{\n}, ' '))
|
201
|
+
|fakeroot must be installed if packaging is not
|
202
|
+
|performed as root
|
203
|
+
MESSAGE
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
210
207
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
208
|
+
# ensure that we're in the proper directory
|
209
|
+
task :testdir => path_for(:spkg)
|
210
|
+
|
211
|
+
# install binary package directory
|
212
|
+
task :install => path_for(:bpkg)
|
213
|
+
|
214
|
+
# install changelogs
|
215
|
+
task :installchangelogs => path_for(:docs, 'changelog.Debian')
|
216
|
+
|
217
|
+
# install documentation directory
|
218
|
+
task :installdocs => path_for(:docs)
|
219
|
+
|
220
|
+
# compress non-html, non-css, non-compressed files
|
221
|
+
task :compress => [ path_for(:docs), path_for(:man), path_for(:info) ] do
|
222
|
+
[:docs, :man, :info].each do |doc|
|
223
|
+
Dir[path_for(doc, '*')].each do |filename|
|
224
|
+
next unless File.file?(filename)
|
225
|
+
next if File.size(filename) < 4096
|
226
|
+
next if filename =~ %r{\.(html|css)$}
|
227
|
+
next if filename =~ %r{\.(bz2|gz|zip)$}
|
228
|
+
next if filename =~ %r{changelog}
|
229
|
+
File.open(filename) do |f|
|
230
|
+
Zlib::GzipWriter.open("#{filename}.gz") do |gz|
|
231
|
+
gz.write f.read
|
232
|
+
end
|
233
|
+
end
|
234
|
+
File.unlink filename
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
215
238
|
|
216
|
-
|
217
|
-
|
218
|
-
|
239
|
+
task :fixperms do
|
240
|
+
[:docs, :man].each do |doc|
|
241
|
+
Dir[path_for(doc, '**', '*')].each do |f|
|
242
|
+
dirs, files = f.partition {|f| File.directory?(f) }
|
243
|
+
chown 'root', 'root', dirs
|
244
|
+
chown 'root', 'root', files
|
245
|
+
end
|
246
|
+
end
|
247
|
+
Dir[path_for(:bpkg, '**', '*')].each do |f|
|
248
|
+
mode = File.stat(f)
|
249
|
+
case
|
250
|
+
when File.dirname(f) =~ %r{/bin/} then chmod(f, mode & 0755)
|
251
|
+
when File.dirname(f) =~ %r{/usr/games} then chmod(f, mode & 0755)
|
252
|
+
when File.dirname(f) =~ %r{/etc/init.d} then chmod(f, mode & 0755)
|
253
|
+
when File.directory?(f) then chmod(f, mode & 0755)
|
254
|
+
else chmod(f, mode & 0644)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
219
258
|
|
220
|
-
|
221
|
-
mkdir 'debian'
|
259
|
+
task :installdeb => path_for(:ctrl)
|
222
260
|
|
223
|
-
|
224
|
-
Dir[templates + '/**/*'].each do |file|
|
261
|
+
task :gencontrol => path_for(:ctrl, 'control')
|
225
262
|
|
226
|
-
|
227
|
-
|
228
|
-
# gsub our replacements in the file in memory, then write back
|
229
|
-
# out to the destination
|
230
|
-
begin
|
231
|
-
s = File.open(file, File::RDONLY)
|
232
|
-
d = File.open(dest, File::WRONLY | File::CREAT)
|
233
|
-
d.write replacements.inject(s.readlines.join) {|contents, rep| contents.gsub %r|##{rep[0]}#|, rep[1] }
|
234
|
-
ensure
|
235
|
-
s.close
|
236
|
-
d.close
|
237
|
-
end
|
263
|
+
end
|
238
264
|
|
265
|
+
# debian/
|
266
|
+
file path_for(:spkg) do
|
267
|
+
mkdir path_for(:spkg)
|
268
|
+
touch path_for(:pstamp)
|
269
|
+
install self.changelog, path_for(:spkg, 'changelog') if self.changelog
|
270
|
+
install self.copyright, path_for(:spkg, 'copyright') \
|
271
|
+
rescue raise ArgumentError, 'copyright file must be present'
|
272
|
+
inflate_templates(path_for(:spkg), *Dir[path_for(:stmpl, '*')])
|
239
273
|
end
|
240
274
|
|
241
|
-
#
|
242
|
-
|
275
|
+
# debian/foo
|
276
|
+
file path_for(:bpkg) => path_for(:spkg) do
|
277
|
+
mkdir_p path_for(:bpkg)
|
278
|
+
files.each {|i| i.install(path_for(:bpkg)) }
|
279
|
+
end
|
243
280
|
|
244
|
-
#
|
245
|
-
|
246
|
-
|
281
|
+
# debian/foo/usr/share/doc/foo
|
282
|
+
file path_for(:docs) => path_for(:bpkg) do
|
283
|
+
mkdir_p path_for(:docs)
|
284
|
+
install path_for(:spkg, 'copyright'), path_for(:docs, 'copyright')
|
285
|
+
docs.each {|i| i.install(path_for(:docs)) }
|
286
|
+
end
|
287
|
+
|
288
|
+
# debian/foo/usr/share/doc/foo/changelog.Debian
|
289
|
+
file path_for(:docs, 'changelog.Debian') => path_for(:docs) do
|
290
|
+
install path_for(:spkg, 'changelog'), path_for(:docs, 'changelog.Debian')
|
247
291
|
end
|
248
292
|
|
249
|
-
|
293
|
+
# man/info
|
294
|
+
file path_for(:man)
|
295
|
+
file path_for(:info)
|
250
296
|
|
251
|
-
|
252
|
-
|
253
|
-
|
297
|
+
# debian/foo/DEBIAN
|
298
|
+
file path_for(:ctrl) => path_for(:bpkg) do
|
299
|
+
mkdir_p path_for(:ctrl)
|
300
|
+
[:postinst, :preinst, :postrm, :prerm].each do |script|
|
301
|
+
cp scripts[script], path_for(:ctrl) if scripts[script]
|
302
|
+
end
|
303
|
+
File.open(path_for(:ctrl, 'conffiles'), 'w') do |f|
|
304
|
+
# cd into the directory, so filelists from this location will
|
305
|
+
# be accurate
|
306
|
+
chdir(path_for(:bpkg)) do
|
307
|
+
files = (Dir['etc/**/*'] + configs.to_a).find_all {|f| File.file? f }
|
308
|
+
f.write files.join("\n")
|
309
|
+
end
|
310
|
+
end
|
311
|
+
end
|
254
312
|
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
'CHANGELOG' => lambda { self.changelog },
|
260
|
-
'CLEAN' => lambda { self.commands.clean },
|
261
|
-
'CONFFILES' => lambda { conffiles },
|
262
|
-
'DATE' => lambda { %x{822-date}.chomp },
|
263
|
-
'DEPENDS' => lambda { self.depends.join ', ' },
|
264
|
-
'DESCRIPTION' => lambda { @pallet.description.gsub(/\n/, "\n ") },
|
265
|
-
'DIRECTORIES' => lambda { directories },
|
266
|
-
'DOCS' => lambda { system self.commands.document if self.commands.document; self.docs.join "\n" },
|
267
|
-
'EMAIL' => lambda { @pallet.email },
|
268
|
-
'FILES' => lambda { system self.commands.build if self.commands.build; install },
|
269
|
-
'INSTALL' => lambda { self.commands.install ? "#{self.commands.install} DESTDIR=debian/#{@pallet.name}" : nil },
|
270
|
-
'MAKE' => lambda { self.commands.build },
|
271
|
-
'MODE' => lambda { self.architecture == 'all' ? 'indep' : 'arch' },
|
272
|
-
'PACKAGE' => lambda { @pallet.name },
|
273
|
-
'POLICY' => lambda { DEBIAN_POLICY_VERSION },
|
274
|
-
'REQUIREMENTS' => lambda { self.requirements.map {|r| ' * ' + r }.join "\n" },
|
275
|
-
'SUMMARY' => lambda { @pallet.summary },
|
276
|
-
'UNUSED-MODE' => lambda { self.architecture == 'all' ? 'arch' : 'indep' },
|
277
|
-
'USERNAME' => lambda { @pallet.author },
|
278
|
-
'VERSION' => lambda { @pallet.version },
|
279
|
-
}.instance_eval { each {|k,v| self[k] = v.call.to_s } }
|
280
|
-
end
|
313
|
+
# debian/foo/DEBIAN/control
|
314
|
+
file path_for(:ctrl, 'control') => path_for(:ctrl) do
|
315
|
+
system *%W{dpkg-gencontrol -Pdebian/#{@pallet.name} -p#{@pallet.name}}
|
316
|
+
end
|
281
317
|
|
282
|
-
#
|
283
|
-
# Generate the complete list of directories needed.
|
284
|
-
#
|
285
|
-
def directories
|
286
|
-
self.files.inject([]) {|m, s| m + s.directories.values }.uniq.join "\n"
|
287
318
|
end
|
288
319
|
|
289
|
-
#
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
320
|
+
# have clean/clobber depend on our custom clean/clobber
|
321
|
+
task :clean => "#{self.task_name}:clean"
|
322
|
+
task :clobber => "#{self.task_name}:clobber"
|
323
|
+
|
324
|
+
end
|
325
|
+
|
326
|
+
private
|
327
|
+
|
328
|
+
#
|
329
|
+
# Will deconstruct a hash parameter into an array consisting of its
|
330
|
+
# one key and an array containing its value. Useful for deconstructing
|
331
|
+
# Rake-style "task_name => [dependency]" style args.
|
332
|
+
#
|
333
|
+
def deconstruct_hash(hash)
|
334
|
+
case hash
|
335
|
+
when Hash
|
336
|
+
fail "Too many task names: #{hash.keys.join(' ')}" if hash.size > 1
|
337
|
+
fail "No task name given" if hash.size < 1
|
338
|
+
pallet = hash.keys[0]
|
339
|
+
deps = hash[pallet]
|
340
|
+
deps = [deps] if [String, Symbol, Regexp, Proc].find {|f| f === deps }
|
341
|
+
[pallet, deps]
|
342
|
+
else
|
343
|
+
[hash, []]
|
297
344
|
end
|
345
|
+
end
|
346
|
+
|
347
|
+
#
|
348
|
+
# Moves templates to the destination directory, inflating any
|
349
|
+
# parameters inside the template to their value in +replacements+.
|
350
|
+
#
|
351
|
+
def inflate_templates(destination, *files)
|
352
|
+
|
353
|
+
files.each do |file|
|
354
|
+
|
355
|
+
dest = File.join(destination, File.basename(file))
|
356
|
+
next if File.exist?(dest)
|
357
|
+
|
358
|
+
# gsub our replacements in the file in memory, then write back out
|
359
|
+
# to the destination
|
360
|
+
begin
|
361
|
+
s = File.open(file, File::RDONLY)
|
362
|
+
d = File.open(dest, File::WRONLY | File::CREAT,
|
363
|
+
File.stat(file).mode & 0777)
|
364
|
+
d.write(replacements.inject(s.readlines.join) do |contents, rep|
|
365
|
+
contents.gsub %r|##{rep[0]}#|, rep[1]
|
366
|
+
end)
|
367
|
+
ensure
|
368
|
+
s.close if s
|
369
|
+
d.close if d
|
370
|
+
end
|
298
371
|
|
299
|
-
#
|
300
|
-
# Returns a list of configuration files in _files_, from the
|
301
|
-
# perspective of the destination on the filesystem when the
|
302
|
-
# package is installed
|
303
|
-
#
|
304
|
-
def conffiles
|
305
|
-
self.files.inject([]) do |m,s|
|
306
|
-
m + (s.attributes[:conf] ? s.files.map {|src, dst| dst } : [])
|
307
|
-
end.join "\n"
|
308
372
|
end
|
309
373
|
|
310
374
|
end
|
375
|
+
|
376
|
+
def replacements
|
377
|
+
@replacements_cache ||= {
|
378
|
+
'ARCHITECTURE' => self.architecture,
|
379
|
+
'BUILD_DEPS' => self.build_depends.join(', '),
|
380
|
+
'CONFLICTS' => self.conflicts,
|
381
|
+
'DATE' => Time.now.rfc822,
|
382
|
+
'DEPENDS' => self.depends.join(', '),
|
383
|
+
'DESCRIPTION' => @pallet.description.gsub(%r{\n}, "\n "),
|
384
|
+
'DISTRIBUTION' => self.distribution,
|
385
|
+
'EMAIL' => @pallet.email,
|
386
|
+
'ENHANCES' => self.enhances,
|
387
|
+
'PACKAGE' => @pallet.name,
|
388
|
+
'PALLET_DEPS' => PALLET_DEPENDS.join(', '),
|
389
|
+
'POLICY' => POLICY_VERSION,
|
390
|
+
'PRIORITY' => self.priority,
|
391
|
+
'PROVIDES' => self.provides,
|
392
|
+
'RECOMMENDS' => self.recommends,
|
393
|
+
'SECTION' => self.section,
|
394
|
+
'SUMMARY' => @pallet.summary,
|
395
|
+
'SUGGESTS' => self.suggests,
|
396
|
+
'TARGET-UNUSED' => target[1],
|
397
|
+
'TARGET-USED' => target[0],
|
398
|
+
'TASKNAME' => self.task_name,
|
399
|
+
'VERSION' => @pallet.version,
|
400
|
+
'URGENCY' => self.urgency,
|
401
|
+
'USERNAME' => @pallet.author,
|
402
|
+
}.instance_eval { each {|k,v| self[k] = v.to_s } }
|
403
|
+
end
|
404
|
+
|
405
|
+
#
|
406
|
+
# Returns the build target for the project: indep or arch
|
407
|
+
#
|
408
|
+
def target
|
409
|
+
targets = %w{indep arch}
|
410
|
+
architecture == 'all' ? targets : targets.reverse
|
411
|
+
end
|
412
|
+
|
413
|
+
def path_for(type, *filename)
|
414
|
+
@dir_cache ||= {
|
415
|
+
:spkg => [BUILD_DIR],
|
416
|
+
:pstamp => [BUILD_DIR, '.pallet-stamp'],
|
417
|
+
:bpkg => [BUILD_DIR, @pallet.name],
|
418
|
+
:ctrl => [BUILD_DIR, @pallet.name, 'DEBIAN'],
|
419
|
+
:docs => [BUILD_DIR, @pallet.name, 'usr', 'share', 'doc', @pallet.name],
|
420
|
+
:man => [BUILD_DIR, 'usr', 'share', 'man'],
|
421
|
+
:info => [BUILD_DIR, 'usr', 'share', 'info'],
|
422
|
+
:tmpl => [TEMPLATE_LOCATION],
|
423
|
+
:stmpl => [TEMPLATE_LOCATION, %w{source}],
|
424
|
+
:btmpl => [TEMPLATE_LOCATION, %w{binary}],
|
425
|
+
:pkg => [Pallet::PACKAGE_DIR, "#{@pallet.name}_#{@pallet.version}_#{self.architecture}.deb"],
|
426
|
+
}
|
427
|
+
File.join(@dir_cache[type], *filename)
|
428
|
+
end
|
429
|
+
|
311
430
|
end
|