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 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.
@@ -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
@@ -1,15 +1,15 @@
1
1
  require 'rubygems'
2
2
  require 'rake'
3
3
 
4
- require 'pallet/deb'
5
- require 'pallet/gem'
6
- require 'install_spec'
4
+ require 'installer'
7
5
 
8
6
  class Pallet
9
7
 
10
- VERSION = '1.2.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
- yield self if block_given?
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
- require 'dependencies'
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'
@@ -1,311 +1,430 @@
1
- class Pallet
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
- # = Pallet::Deb
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
- # To create a Debian package, moving everything under 'lib/' to
16
- # '/usr/lib/ruby/1.8/', everything in 'bin/' to '/usr/bin/', and all
17
- # documentation under 'doc/' to the Debian default documentation
18
- # directory, under the subdirectory HTML, you would write the
19
- # following:
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
- # Pallet::Deb.new(pallet) do |deb|
22
- #--
23
- # TODO: EXAMPLE!!
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
- # === GPG Integration
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 will sign its built Debian packages with the GPG key
30
- # id in the environment variable $GPG_KEY_ID, if it is set.
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
- # == Caveats
113
+ # # creates a new Pallet::Deb packager, that depends on other tasks
114
+ # p.packages << Pallet::Deb.new(p => [:build, :doc])
35
115
  #
36
- # * Uses dpkg-buildpackage, which is not very flexible. Should be
37
- # rewritten later to do everything on its own.
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
- class Deb
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
- # default clean command
173
- self.commands.clean = 'rake --silent clean'
121
+ @pallet, self.prerequisites = deconstruct_hash(pallet)
174
122
 
175
- # send me off to be configured
176
- yield self if block_given?
123
+ self.task_name = task_name
177
124
 
178
- # append our core build-dependencies to any others specified, so
179
- # the user doesn't have to
180
- self.build_depends |= BUILD_DEPENDS
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
- end
154
+ end
183
155
 
184
- def define
156
+ def define
185
157
 
186
- desc 'Build this project into a Debian package'
187
- task self.task_name => self.prerequisites do
188
- debianize unless File.directory? 'debian'
189
- packagize
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
- task 'clean' => ['clean:deb']
193
- task 'clean:deb' do
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
- task 'clobber' => ['clobber:deb']
200
- task 'clobber:deb' do
201
- rm_r 'debian' rescue nil
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
- end
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
- private
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
- # Build the _debian/_ directory under the project root
213
- #
214
- def debianize
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
- locations = [File.dirname(__FILE__) + '/../../share/debian',
217
- '/usr/share/pallet/debian']
218
- templates = locations.find {|f| puts f; File.directory? f }
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
- # make the target directory
221
- mkdir 'debian'
259
+ task :installdeb => path_for(:ctrl)
222
260
 
223
- # copy/gsub template files into place
224
- Dir[templates + '/**/*'].each do |file|
261
+ task :gencontrol => path_for(:ctrl, 'control')
225
262
 
226
- dest = 'debian/' + File.basename(file)
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
- # mark debian/rules as executable
242
- chmod 0755, 'debian/rules'
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
- # relocate any additional scripts into the debian directory
245
- scripts.each_pair do |type, location|
246
- ln_sf "../#{location}", "debian/#{type}"
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
- end
293
+ # man/info
294
+ file path_for(:man)
295
+ file path_for(:info)
250
296
 
251
- def packagize
252
- system self.commands.package
253
- end
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
- def replacements
256
- @replacements_cache ||= {
257
- 'ARCHITECTURE' => lambda { self.architecture },
258
- 'BUILD_DEPS' => lambda { self.build_depends.join ', ' },
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
- # Generate a list of "install" commands to move files into the
291
- # correct directory.
292
- #
293
- def install
294
- self.files.inject([]) do |m,s|
295
- m + s.files.map {|src, dst| "#{src} #{File.dirname(dst)}" }
296
- end.join "\n"
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