fpm 0.4.0pre3 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELIST CHANGED
@@ -11,7 +11,6 @@
11
11
  - rpm support: Add --rpm-rpmbuild-define for passing a --define flag to rpmbuild
12
12
  (via Naresh V)
13
13
  - PHP pear source support (fpm -s pear ...) (via Andrew Gaffney)
14
- TODO(sissel): pull/163, make sure --post-process is supported
15
14
 
16
15
  0.3.10 (Oct 10, 2011)
17
16
  - Allow taking a list of files/inputs on stdin with '-' or with the --inputs
@@ -33,12 +33,12 @@ class FPM::Command < Clamp::Command
33
33
  "A path to prefix files with when building the target package. This may " \
34
34
  "be necessary for all input packages. For example, the 'gem' type will" \
35
35
  "prefix with your gem directory automatically."
36
- option ["-p", "--package"], "OUTPUT",
37
- "The package file path to output.", :default => "NAME-FULLVERSION.ARCH.TYPE"
36
+ option ["-p", "--package"], "OUTPUT", "The package file path to output."
38
37
  option ["-n", "--name"], "NAME", "The name to give to the package"
39
38
  option "--verbose", :flag, "Enable verbose output"
40
39
  option "--debug", :flag, "Enable debug output"
41
- option ["-v", "--version"], "VERSION", "The version to give to the package"
40
+ option ["-v", "--version"], "VERSION", "The version to give to the package",
41
+ :default => 1.0
42
42
  option "--iteration", "ITERATION",
43
43
  "The iteration to give to the package. RPM calls this the 'release'. " \
44
44
  "FreeBSD calls it 'PORTREVISION'. Debian calls this 'debian_revision'",
@@ -78,7 +78,8 @@ class FPM::Command < Clamp::Command
78
78
  end # --replaces
79
79
  option "--config-files", "CONFIG_FILES",
80
80
  "Mark a file in the package as being a config file. This uses 'conffiles'" \
81
- " in debs and %config in rpm." do |val|
81
+ " in debs and %config in rpm. You can specify a directory to have it " \
82
+ "scanned marking all files found as config files" do |val|
82
83
  @config_files ||= []
83
84
  @config_files << val
84
85
  end # --config-files
@@ -98,28 +99,6 @@ class FPM::Command < Clamp::Command
98
99
  @exclude_pattern ||= []
99
100
  @exclude_pattern << val
100
101
  end # -x / --exclude
101
- option "--post-install", "FILE",
102
- "a script to be run after package installation",
103
- :attribute_name => :after_install do |val|
104
- File.expand_path(val) # Get the full path to the script
105
- end # --post-install
106
- option "--pre-install", "FILE",
107
- "a script to be run before package installation",
108
- :attribute_name => :before_install do |val|
109
- File.expand_path(val) # Get the full path to the script
110
- end # --pre-install
111
- # TODO(sissel): Name the flag --post-remove for clarity
112
- option "--post-uninstall", "FILE",
113
- "a script to be run after package removal",
114
- :attribute_name => :after_remove do |val|
115
- File.expand_path(val) # Get the full path to the script
116
- end # --post-uninstall
117
- # TODO(sissel): Name the flag --pre-remove for clarity
118
- option "--pre-uninstall", "FILE",
119
- "a script to be run before package removal",
120
- :attribute_name => :before_remove do |val|
121
- File.expand_path(val) # Get the full path to the script
122
- end # --pre-uninstall
123
102
  option "--description", "DESCRIPTION", "Add a description for this package.",
124
103
  :default => "no description"
125
104
  option "--url", "URI", "Add a url for this package.",
@@ -127,13 +106,50 @@ class FPM::Command < Clamp::Command
127
106
  option "--inputs", "INPUTS_PATH",
128
107
  "The path to a file containing a newline-separated list of " \
129
108
  "files and dirs to use as input."
109
+
110
+ option "--post-install", "FILE",
111
+ "(DEPRECATED, use --after-install) a script to be run after " \
112
+ "package installation" do |val|
113
+ @after_install = File.expand_path(val) # Get the full path to the script
114
+ end # --post-install (DEPRECATED)
115
+ option "--pre-install", "FILE",
116
+ "(DEPRECATED, use --before-install) a script to be run before " \
117
+ "package installation" do |val|
118
+ @before_install = File.expand_path(val) # Get the full path to the script
119
+ end # --pre-install (DEPRECATED)
120
+ option "--post-uninstall", "FILE",
121
+ "(DEPRECATED, use --after-remove) a script to be run after " \
122
+ "package removal" do |val|
123
+ @after_remove = File.expand_path(val) # Get the full path to the script
124
+ end # --post-uninstall (DEPRECATED)
125
+ option "--pre-uninstall", "FILE",
126
+ "(DEPRECATED, use --before-remove) a script to be run before " \
127
+ "package removal" do |val|
128
+ @before_remove = File.expand_path(val) # Get the full path to the script
129
+ end # --pre-uninstall (DEPRECATED)
130
+
131
+ option "--after-install", "FILE",
132
+ "a script to be run after package installation" do |val|
133
+ File.expand_path(val) # Get the full path to the script
134
+ end # --after-install
135
+ option "--before-install", "FILE",
136
+ "a script to be run before package installation" do |val|
137
+ File.expand_path(val) # Get the full path to the script
138
+ end # --pre-install
139
+ option "--after-remove", "FILE",
140
+ "a script to be run after package removal" do |val|
141
+ File.expand_path(val) # Get the full path to the script
142
+ end # --after-remove
143
+ option "--before-remove", "FILE",
144
+ "a script to be run before package removal" do |val|
145
+ File.expand_path(val) # Get the full path to the script
146
+ end # --before-remove
147
+
130
148
  parameter "[ARGS] ...",
131
149
  "Inputs to the source package type. For the 'dir' type, this is the files" \
132
150
  " and directories you want to include in the package. For others, like " \
133
151
  "'gem', it specifies the packages to download and use as the gem input",
134
152
  :attribute_name => :args
135
-
136
- # package-level settings
137
153
  def settings
138
154
  @settings ||= {}
139
155
  end
@@ -207,6 +223,17 @@ class FPM::Command < Clamp::Command
207
223
  input.input(arg)
208
224
  end
209
225
 
226
+ # If --inputs was specified, read it as a file.
227
+ if !inputs.nil?
228
+ if !File.exists?(inputs)
229
+ @logger.fatal("File given for --inputs does not exist (#{inputs})")
230
+ return 1
231
+ end
232
+
233
+ # Read each line as a path
234
+ File.new(inputs, "r").each_line { |path| input.input(path) }
235
+ end
236
+
210
237
  # Override package settings if they are not the default flag values
211
238
  # the below proc essentially does:
212
239
  #
@@ -239,16 +266,36 @@ class FPM::Command < Clamp::Command
239
266
  input.provides += provides
240
267
  input.replaces += replaces
241
268
 
242
- input.scripts[:before_install] = before_install # --pre-install
243
- input.scripts[:after_install] = after_install # --post-install
244
- input.scripts[:before_remove] = before_remove # --pre-uninstall
245
- input.scripts[:after_remove] = after_remove # --post-uninstall
269
+ setscript = proc do |scriptname|
270
+ path = self.send(scriptname)
271
+ # Skip scripts not set
272
+ next if path.nil?
273
+
274
+ # 'self.send(scriptname) == self.before_install == --before-install
275
+ if !File.exists?(path)
276
+ $stderr.puts("No such file (for #{scriptname.to_s}): #{path.inspect}")
277
+ return 1
278
+ end
279
+ input.scripts[scriptname] = File.read(path)
280
+ end
281
+
282
+ setscript.call(:before_install)
283
+ setscript.call(:after_install)
284
+ setscript.call(:before_remove)
285
+ setscript.call(:after_remove)
246
286
 
247
287
  # Convert to the output type
248
288
  output = input.convert(output_class)
249
289
 
250
- # Write the output somewhere
251
- output.output(output.to_s(package))
290
+ # Write the output somewhere, package can be nil if no --package is specified,
291
+ # and that's OK.
292
+ begin
293
+ output.output(output.to_s(package))
294
+ rescue FPM::Package::FileAlreadyExists => e
295
+ @logger.fatal(e.message)
296
+ return 1
297
+ end
298
+
252
299
  return 0
253
300
  ensure
254
301
  input.cleanup unless input.nil?
@@ -13,6 +13,13 @@ class FPM::Package
13
13
 
14
14
  # This class is raised if there's something wrong with a setting in the package.
15
15
  class InvalidArgument < StandardError; end
16
+
17
+ # This class is raised when a file already exists when trying to write.
18
+ class FileAlreadyExists < StandardError
19
+ def to_s
20
+ return "File already exists, refusing to continue: #{super}"
21
+ end # def to_s
22
+ end # class FileAlreadyExists
16
23
 
17
24
  # The name of this package
18
25
  attr_accessor :name
@@ -109,8 +116,15 @@ class FPM::Package
109
116
  @maintainer = "<#{ENV["USER"]}@#{Socket.gethostname}>"
110
117
  end
111
118
 
119
+ # Set attribute defaults based on flags
120
+ # This allows you to define command line options with default values
121
+ # that also are obeyed if fpm is used programmatically.
122
+ self.class.default_attributes do |attribute, value|
123
+ attributes[attribute] = value
124
+ end
125
+
112
126
  @name = nil
113
- @architecture = "all"
127
+ @architecture = "native"
114
128
  @description = "no description given"
115
129
  @version = nil
116
130
  @epoch = nil
@@ -119,6 +133,19 @@ class FPM::Package
119
133
  @category = "default"
120
134
  @license = "unknown"
121
135
  @vendor = "none"
136
+
137
+ # Iterate over all the options and set defaults
138
+ if self.class.respond_to?(:declared_options)
139
+ self.class.declared_options.each do |option|
140
+ with(option.attribute_name) do |attr|
141
+ # clamp makes option attributes available as accessor methods
142
+ # do --foo-bar is available as 'foo_bar'
143
+ # make these available as package attributes.
144
+ attr = "#{attr}?" if !respond_to?(attr)
145
+ input.attributes[attr.to_sym] = send(attr) if respond_to?(attr)
146
+ end
147
+ end
148
+ end
122
149
 
123
150
  @provides = []
124
151
  @conflicts = []
@@ -264,7 +291,8 @@ class FPM::Package
264
291
  .gsub("NAME", name.to_s) \
265
292
  .gsub("FULLVERSION", fullversion) \
266
293
  .gsub("VERSION", version.to_s) \
267
- .gsub("EPOCH", epoch.to_s)
294
+ .gsub("ITERATION", iteration.to_s) \
295
+ .gsub("EPOCH", epoch.to_s) \
268
296
  .gsub("TYPE", type.to_s)
269
297
  end # def to_s
270
298
 
@@ -330,6 +358,15 @@ class FPM::Package
330
358
  end
331
359
  end # def apply_options
332
360
 
361
+ def default_attributes(&block)
362
+ return if @options.nil?
363
+ @options.each do |flag, param, help, options, block|
364
+ attr = flag.first.gsub(/^-+/, "").gsub(/-/, "_")
365
+ attr += "?" if param == :flag
366
+ yield attr.to_sym, options[:default]
367
+ end
368
+ end # def default_attributes
369
+
333
370
  # Get the type of this package class.
334
371
  #
335
372
  # For "Foo::Bar::BAZ" this will return "baz"
@@ -4,8 +4,10 @@ require "fpm/package"
4
4
  require "fpm/errors"
5
5
  require "fpm/util"
6
6
  require "fileutils"
7
- require "insist"
8
7
 
8
+ # Support for debian packages (.deb files)
9
+ #
10
+ # This class supports both input and output of packages.
9
11
  class FPM::Package::Deb < FPM::Package
10
12
  # Map of what scripts are named.
11
13
  SCRIPT_MAP = {
@@ -50,6 +52,8 @@ class FPM::Package::Deb < FPM::Package
50
52
  value.to_i
51
53
  end
52
54
 
55
+ private
56
+
53
57
  # Return the architecture. This will default to native if not yet set.
54
58
  # It will also try to use dpkg and 'uname -m' to figure out what the
55
59
  # native 'architecture' value should be.
@@ -63,11 +67,11 @@ class FPM::Package::Deb < FPM::Package
63
67
  else
64
68
  @architecture = %x{uname -m}.chomp
65
69
  end
66
- elsif @architecture == "x86_64"
70
+ end
71
+ if @architecture == "x86_64"
67
72
  # Debian calls x86_64 "amd64"
68
73
  @architecture = "amd64"
69
74
  end
70
-
71
75
  return @architecture
72
76
  end # def architecture
73
77
 
@@ -182,10 +186,11 @@ class FPM::Package::Deb < FPM::Package
182
186
  end # def extract_files
183
187
 
184
188
  def output(output_path)
185
- insist { File.directory?(build_path) } == true
189
+ # Abort if the target path already exists.
190
+ raise FileAlreadyExists.new(output_path) if File.exists?(output_path)
186
191
 
187
192
  # create 'debian-binary' file, required to make a valid debian package
188
- File.write(build_path("debian-binary"), "2.0")
193
+ File.write(build_path("debian-binary"), "2.0\n")
189
194
 
190
195
  write_control_tarball
191
196
 
@@ -325,6 +330,7 @@ class FPM::Package::Deb < FPM::Package
325
330
 
326
331
  @logger.debug("Writing control file", :path => control)
327
332
  File.write(control, control_data)
333
+ edit_file(control) if attributes[:edit?]
328
334
  end
329
335
  end # def write_control
330
336
 
@@ -333,13 +339,14 @@ class FPM::Package::Deb < FPM::Package
333
339
  # SCRIPT_MAP is a map from the package ':after_install' to debian
334
340
  # 'post_install' names
335
341
  def write_scripts
336
- SCRIPT_MAP.each do |script, filename|
337
- next if scripts[script].nil?
342
+ SCRIPT_MAP.each do |scriptname, filename|
343
+ next if scripts[scriptname].nil?
338
344
 
339
- with(control_path(filename)) do |path|
340
- FileUtils.cp(scripts[script], filename)
345
+ with(control_path(filename)) do |controlscript|
346
+ @logger.debug("Writing control script", :source => filename, :target => controlscript)
347
+ File.write(controlscript, scripts[scriptname])
341
348
  # deb maintainer scripts are required to be executable
342
- File.chmod(0755, filename)
349
+ File.chmod(0755, controlscript)
343
350
  end
344
351
  end
345
352
  end # def write_scripts
@@ -363,5 +370,13 @@ class FPM::Package::Deb < FPM::Package
363
370
  File.chmod(0755, control_path("templates"))
364
371
  end
365
372
  end # def write_debconf
366
- public(:input, :output)
373
+
374
+ def to_s(format=nil)
375
+ # Default format if nil
376
+ # git_1.7.9.3-1_amd64.deb
377
+ return super("NAME_FULLVERSION_ARCH.TYPE") if format.nil?
378
+ return super(format)
379
+ end # def to_s
380
+
381
+ public(:input, :output, :architecture, :name, :converted_from, :to_s)
367
382
  end # class FPM::Target::Deb
@@ -4,9 +4,28 @@ require "fileutils"
4
4
  require "find"
5
5
  require "socket"
6
6
 
7
+ # A directory package.
8
+ #
9
+ # This class supports both input and output. As a note, 'output' will
10
+ # only emit the files, not any metadata. This is an effective way
11
+ # to extract another package type.
7
12
  class FPM::Package::Dir < FPM::Package
8
13
  private
9
14
 
15
+ # Add a new path to this package.
16
+ #
17
+ # If the path is a directory, it is copied recursively. The behavior
18
+ # of the copying is modified by the :chdir and :prefix attributes.
19
+ #
20
+ # If :prefix is set, the destination path is prefixed with that value.
21
+ # If :chdir is set, the current directory is changed to that value
22
+ # during the copy.
23
+ #
24
+ # Example: Copy /etc/X11 into this package as /opt/xorg/X11:
25
+ #
26
+ # package.attributes[:prefix] = "/opt/xorg"
27
+ # package.attributes[:chdir] = "/etc"
28
+ # package.input("X11")
10
29
  def input(path)
11
30
  @logger.debug("Copying", :input => path)
12
31
  @logger["method"] = "input"
@@ -25,9 +44,11 @@ class FPM::Package::Dir < FPM::Package
25
44
  self.license = "unknown"
26
45
  self.vendor = [ENV["USER"], Socket.gethostname].join("@")
27
46
  ensure
47
+ # Clean up any logger context we added.
28
48
  @logger.remove("method")
29
49
  end # def input
30
50
 
51
+ # Output this package to the given directory.
31
52
  def output(dir)
32
53
  dir = File.expand_path(dir)
33
54
  ::Dir.chdir(staging_path) do
@@ -60,6 +81,9 @@ class FPM::Package::Dir < FPM::Package
60
81
  end
61
82
  end # def clone
62
83
 
84
+ # Copy, recursively, from source to destination.
85
+ #
86
+ # Files will be hardlinked if possible, but copied otherwise.
63
87
  def copy(source, destination)
64
88
  directory = File.dirname(destination)
65
89
  if !File.directory?(directory)
@@ -12,9 +12,10 @@ require "fpm/util"
12
12
  # The following attributes are supported:
13
13
  #
14
14
  # * :gem_bin_path
15
- # * :package_name_prefix
15
+ # * :gem_package_name_prefix
16
16
  # * :gem_gem
17
17
  class FPM::Package::Gem < FPM::Package
18
+ # Flags '--foo' will be accessable as attributes[:gem_foo]
18
19
  option "--bin-path", "DIRECTORY", "The directory to install gem executables",
19
20
  :default => ::Gem::bindir
20
21
  option "--package-prefix", "NAMEPREFIX",
@@ -29,6 +30,10 @@ class FPM::Package::Gem < FPM::Package
29
30
  option "--gem", "PATH_TO_GEM",
30
31
  "The path to the 'gem' tool (defaults to 'gem' and searches " \
31
32
  "your $PATH)", :default => "gem"
33
+ option "--fix-name", :flag, "Should the target package name be prefixed?",
34
+ :default => true
35
+ option "--fix-dependencies", :flag, "Should the package dependencies be " \
36
+ "prefixed?", :default => true
32
37
 
33
38
  def input(gem)
34
39
  # 'arg' is the name of the rubygem we should unpack.
@@ -97,7 +102,14 @@ class FPM::Package::Gem < FPM::Package
97
102
  attributes[:gem_package_name_prefix] = attributes[:gem_package_prefix]
98
103
  end
99
104
 
100
- self.name = [attributes[:gem_package_name_prefix], spec.name].join("-")
105
+ # name prefixing is optional, if enabled, a name 'foo' will become
106
+ # 'rubygem-foo' (depending on what the gem_package_name_prefix is)
107
+ self.name = spec.name
108
+ if attributes[:gem_fix_name?]
109
+ self.name = fix_name(spec.name)
110
+ end
111
+
112
+ #self.name = [attributes[:gem_package_name_prefix], spec.name].join("-")
101
113
  self.license = (spec.license or "no license listed in #{File.basename(file)}")
102
114
  self.version = spec.version.to_s
103
115
  self.vendor = spec.author
@@ -132,7 +144,12 @@ class FPM::Package::Gem < FPM::Package
132
144
 
133
145
  # Some reqs can be ">= a, < b" versions, let's handle that.
134
146
  reqs.to_s.split(/, */).each do |req|
135
- self.dependencies << "#{attributes[:gem_package_name_prefix]}-#{dep.name} #{req}"
147
+ if attributes[:gem_fix_dependencies?]
148
+ name = fix_name(dep.name)
149
+ else
150
+ name = dep.name
151
+ end
152
+ self.dependencies << "#{name} #{req}"
136
153
  end
137
154
  end # runtime_dependencies
138
155
  end # ::Gem::Package
@@ -157,6 +174,12 @@ class FPM::Package::Gem < FPM::Package
157
174
  args << gem_path
158
175
  safesystem(*args)
159
176
  end # def install_to_staging
160
-
177
+
178
+ # Sanitize package name.
179
+ # This prefixes the package name with 'rubygem' (but depends on the attribute
180
+ # :gem_package_name_prefix
181
+ def fix_name(name)
182
+ return [attributes[:gem_package_name_prefix], name].join("-")
183
+ end # def fix_name
161
184
  public(:input, :output)
162
185
  end # class FPM::Package::Gem
@@ -3,10 +3,20 @@ require "fpm/package"
3
3
  require "fileutils"
4
4
  require "fpm/util"
5
5
 
6
+ # This provides PHP PEAR package support.
7
+ #
8
+ # This provides input support, but not output support.
6
9
  class FPM::Package::PEAR < FPM::Package
7
- option "--package-prefix", "PREFIX",
8
- "Name prefix for python package", :default => "php-pear"
10
+ option "--package-name-prefix", "PREFIX",
11
+ "Name prefix for pear package", :default => "php-pear"
9
12
 
13
+ # Input a PEAR package.
14
+ #
15
+ # The parameter is a PHP PEAR package name.
16
+ #
17
+ # Attributes that affect behavior here:
18
+ # * :prefix - changes the install root, default is /usr/share
19
+ # * :pear_package_name_prefix - changes the
10
20
  def input(input_package)
11
21
  if !program_in_path?("pear")
12
22
  raise ExecutableNotFound.new("pear")
@@ -21,7 +31,7 @@ class FPM::Package::PEAR < FPM::Package
21
31
  @logger.info("Fetching package information", :package => input_package)
22
32
  pear_cmd = "pear -c #{config} remote-info #{input_package}"
23
33
  name = %x{#{pear_cmd} | sed -ne '/^Package\s*/s/^Package\s*//p'}.chomp
24
- self.name = "#{attributes[:pear_package_prefix]}-#{name}"
34
+ self.name = "#{attributes[:pear_package_name_prefix]}-#{name}"
25
35
  self.version = %x{#{pear_cmd} | sed -ne '/^Latest\s*/s/^Latest\s*//p'}.chomp
26
36
  self.description = %x{#{pear_cmd} | sed -ne '/^Summary\s*/s/^Summary\s*//p'}.chomp
27
37
 
@@ -7,7 +7,18 @@ require "fileutils"
7
7
  require "tmpdir"
8
8
  require "json"
9
9
 
10
+ # Support for python packages.
11
+ #
12
+ # This supports input, but not output.
13
+ #
14
+ # Example:
15
+ #
16
+ # # Download the django python package:
17
+ # pkg = FPM::Package::Python.new
18
+ # pkg.input("Django")
19
+ #
10
20
  class FPM::Package::Python < FPM::Package
21
+ # Flags '--foo' will be accessable as attributes[:python_foo]
11
22
  option "--bin", "PYTHON_EXECUTABLE",
12
23
  "The path to the python executable you wish to run.", :default => "python"
13
24
  option "--easyinstall", "EASYINSTALL_EXECUTABLE",
@@ -15,9 +26,30 @@ class FPM::Package::Python < FPM::Package
15
26
  option "--pypi", "PYPI_URL",
16
27
  "PyPi Server uri for retrieving packages.",
17
28
  :default => "http://pypi.python.org/simple"
18
- option "--package-prefix", "PREFIX",
19
- "Name prefix for python package", :default => "python"
20
-
29
+ option "--package-prefix", "NAMEPREFIX",
30
+ "(DEPRECATED, use --package-name-prefix) Name to prefix the package " \
31
+ "name with." do |value|
32
+ @logger.warn("Using deprecated flag: --package-prefix. Please use " \
33
+ "--package-name-prefix")
34
+ value
35
+ end
36
+ option "--package-name-prefix", "PREFIX", "Name to prefix the package " \
37
+ "name with.", :default => "python"
38
+ option "--fix-name", :flag, "Should the target package name be prefixed?",
39
+ :default => true
40
+ option "--fix-dependencies", :flag, "Should the package dependencies be " \
41
+ "prefixed?", :default => true
42
+
43
+
44
+ private
45
+
46
+ # Input a package.
47
+ #
48
+ # The 'package' can be any of:
49
+ #
50
+ # * A name of a package on pypi (ie; easy_install some-package)
51
+ # * The path to a directory containing setup.py
52
+ # * The path to a setup.py
21
53
  def input(package)
22
54
  path_to_package = download_if_necessary(package, version)
23
55
 
@@ -36,7 +68,11 @@ class FPM::Package::Python < FPM::Package
36
68
  install_to_staging(setup_py)
37
69
  end # def input
38
70
 
71
+ # Download the given package if necessary. If version is given, that version
72
+ # will be downloaded, otherwise the latest is fetched.
39
73
  def download_if_necessary(package, version=nil)
74
+ # TODO(sissel): this should just be a 'download' method, the 'if_necessary'
75
+ # part should go elsewhere.
40
76
  path = package
41
77
  # If it's a path, assume local build.
42
78
  if File.directory?(path) or (File.exists?(path) and File.basename(path) == "setup.py")
@@ -51,30 +87,32 @@ class FPM::Package::Python < FPM::Package
51
87
  want_pkg = "#{package}==#{version}"
52
88
  end
53
89
 
54
- # TODO(sissel): support a settable path to 'easy_install'
55
- # TODO(sissel): support a tunable for uthe url to pypi
56
- safesystem("easy_install", "-i", "http://pypi.python.org/simple",
57
- "--editable", "-U", "--build-directory", build_path, want_pkg)
90
+ target = build_path(package)
91
+ FileUtils.mkdir(target) unless File.directory?(target)
92
+
93
+ safesystem(attributes[:python_easyinstall], "-i", attributes[:python_pypi],
94
+ "--editable", "-U", "--build-directory", target, want_pkg)
58
95
 
59
96
  # easy_install will put stuff in @tmpdir/packagename/, so find that:
60
97
  # @tmpdir/somepackage/setup.py
61
- dirs = ::Dir.glob(File.join(build_path, "*"))
98
+ dirs = ::Dir.glob(File.join(target, "*"))
62
99
  if dirs.length != 1
63
100
  raise "Unexpected directory layout after easy_install. Maybe file a bug? The directory is #{build_path}"
64
101
  end
65
102
  return dirs.first
66
103
  end # def download
67
104
 
105
+ # Load the package information like name, version, dependencies.
68
106
  def load_package_info(setup_py)
69
- if !attributes.include?(:package_name_prefix)
70
- attributes[:package_name_prefix] = "python"
107
+ if !attributes[:python_package_prefix].nil?
108
+ attributes[:python_package_name_prefix] = attributes[:python_package_prefix]
71
109
  end
72
110
 
73
111
  # Add ./pyfpm/ to the python library path
74
112
  pylib = File.expand_path(File.dirname(__FILE__))
75
- setup_cmd = "env PYTHONPATH=#{pylib} #{self.attributes[:python_bin]} #{setup_py} --command-packages=pyfpm get_metadata"
113
+ setup_cmd = "env PYTHONPATH=#{pylib} #{attributes[:python_bin]} #{setup_py} --command-packages=pyfpm get_metadata"
76
114
  output = ::Dir.chdir(File.dirname(setup_py)) { `#{setup_cmd}` }
77
- puts output
115
+ @logger.warn("json output from setup.py", :data => output)
78
116
  metadata = JSON.parse(output[/\{.*\}/msx])
79
117
 
80
118
  self.architecture = metadata["architecture"]
@@ -83,11 +121,20 @@ class FPM::Package::Python < FPM::Package
83
121
  self.version = metadata["version"]
84
122
  self.url = metadata["url"]
85
123
 
86
- self.name = fix_name(metadata["name"])
124
+ # name prefixing is optional, if enabled, a name 'foo' will become
125
+ # 'python-foo' (depending on what the python_package_name_prefix is)
126
+ if attributes[:python_fix_name?]
127
+ self.name = fix_name(metadata["name"])
128
+ else
129
+ self.name = metadata["name"]
130
+ end
87
131
 
88
132
  self.dependencies += metadata["dependencies"].collect do |dep|
89
133
  name, cmp, version = dep.split
90
- name = fix_name(name)
134
+ # dependency name prefixing is optional, if enabled, a name 'foo' will
135
+ # become 'python-foo' (depending on what the python_package_name_prefix
136
+ # is)
137
+ name = fix_name(name) if attributes[:python_fix_dependencies?]
91
138
  "#{name} #{cmp} #{version}"
92
139
  end
93
140
  end # def load_package_info
@@ -101,25 +148,32 @@ class FPM::Package::Python < FPM::Package
101
148
  if name.start_with?("python")
102
149
  # If the python package is called "python-foo" strip the "python-" part while
103
150
  # prepending the package name prefix.
104
- return [attributes[:package_name_prefix], name.gsub(/^python-/, "")].join("-")
151
+ return [attributes[:python_package_name_prefix], name.gsub(/^python-/, "")].join("-")
105
152
  else
106
- return [attributes[:package_name_prefix], name].join("-")
153
+ return [attributes[:python_package_name_prefix], name].join("-")
107
154
  end
108
155
  end # def fix_name
109
156
 
157
+ # Install this package to the staging directory
110
158
  def install_to_staging(setup_py)
111
159
  dir = File.dirname(setup_py)
112
160
 
113
161
  # Some setup.py's assume $PWD == current directory of setup.py, so let's
114
162
  # chdir first.
115
163
  ::Dir.chdir(dir) do
164
+
165
+ # Install with a specific prefix if requested
116
166
  if attributes[:prefix]
117
167
  safesystem(attributes[:python_bin], "setup.py", "install", "--prefix",
118
168
  File.join(staging_path, attributes[:prefix]))
119
169
  else
170
+ # Otherwise set the root in staging_path
171
+ # TODO(sissel): there needs to be a way to force
120
172
  safesystem(attributes[:python_bin], "setup.py", "install", "--root",
121
173
  staging_path)
122
174
  end
123
175
  end
124
176
  end # def install_to_staging
177
+
178
+ public(:input)
125
179
  end # class FPM::Package::Python
@@ -67,7 +67,6 @@ class FPM::Package::RPM < FPM::Package
67
67
 
68
68
  self.architecture = tags[:arch]
69
69
  self.category = tags[:group]
70
- self.config_files = config_files
71
70
  self.description = tags[:description]
72
71
  self.epoch = (tags[:epoch] || [nil]).first # for some reason epoch is an array
73
72
  self.iteration = tags[:release]
@@ -78,16 +77,28 @@ class FPM::Package::RPM < FPM::Package
78
77
  self.vendor = tags[:vendor]
79
78
  self.version = tags[:version]
80
79
 
81
- # TODO(sissel): Collect {pre,post}{install,uninstall} scripts
80
+ self.scripts[:before_install] = tags[:prein]
81
+ self.scripts[:after_install] = tags[:postin]
82
+ self.scripts[:before_remove] = tags[:preun]
83
+ self.scripts[:after_remove] = tags[:postun]
84
+ # TODO(sissel): prefix these scripts above with a shebang line if there isn't one?
85
+ # Also taking into account the value of tags[preinprog] etc, something like:
86
+ # #!#{tags[:preinprog]}
87
+ # #{tags[prein]}
82
88
  # TODO(sissel): put 'trigger scripts' stuff into attributes
83
- # TODO(sissel): support provides
84
- # TODO(sissel): support conflicts
85
- # TODO(sissel): support replaces
86
89
 
87
90
  self.dependencies += rpm.requires.collect do |name, operator, version|
88
91
  [name, operator, version].join(" ")
89
92
  end
93
+ self.conflicts += rpm.conflicts.collect do |name, operator, version|
94
+ [name, operator, version].join(" ")
95
+ end
96
+ self.provides += rpm.provides.collect do |name, operator, version|
97
+ [name, operator, version].join(" ")
98
+ end
90
99
  #input.replaces += replaces
100
+
101
+ self.config_files += rpm.config_files
91
102
 
92
103
  # Extract to the staging directory
93
104
  rpm.extract(staging_path)
@@ -112,11 +123,6 @@ class FPM::Package::RPM < FPM::Package
112
123
  edit_file(specfile) if attributes[:edit?]
113
124
 
114
125
  args << specfile
115
- #if defines.empty?
116
- #args = prefixargs + spec
117
- #else
118
- #args = prefixargs + defines.collect{ |define| ["--define", define] }.flatten + spec
119
- #end
120
126
 
121
127
  @logger.info("Running rpmbuild", :args => args)
122
128
  safesystem(*args)
@@ -129,5 +135,10 @@ class FPM::Package::RPM < FPM::Package
129
135
  @logger.log("Created rpm", :path => output_path)
130
136
  end # def output
131
137
 
132
- public(:input, :output, :converted_from)
138
+ def to_s(format=nil)
139
+ return super("NAME-VERSION-ITERATION.ARCH.TYPE") if format.nil?
140
+ return super(format)
141
+ end # def to_s
142
+
143
+ public(:input, :output, :converted_from, :architecture, :to_s)
133
144
  end # class FPM::Package::RPM
@@ -31,6 +31,9 @@ Requires: <%= req %>
31
31
  <% provides.each do |prov| -%>
32
32
  Provides: <%= prov %>
33
33
  <% end -%>
34
+ <% conflicts.each do |conflict| -%>
35
+ Conflicts: <%= conflict %>
36
+ <% end -%>
34
37
  <% replaces.each do |repl| -%>
35
38
  <%# The closes equivalent in RPM to "replaces" is "Obsoletes" -%>
36
39
  Obsoletes: <%= repl %>
@@ -63,36 +66,39 @@ fi
63
66
  %clean
64
67
  # noop
65
68
 
66
- <% if scripts["pre-install"] -%>
69
+ <% if scripts[:before_install] -%>
67
70
  %pre
68
- <%= File.read(scripts["pre-install"]) %>
71
+ <%= scripts[:before_install] %>
69
72
 
70
73
  <% end -%>
71
- <% if scripts["post-install"] -%>
74
+ <% if scripts[:after_install] -%>
72
75
  %post
73
- <%= File.read(scripts["post-install"]) %>
76
+ <%= scripts[:after_install] %>
74
77
 
75
78
  <% end -%>
76
- <% if scripts["pre-uninstall"] -%>
79
+ <% if scripts[:before_remove] -%>
77
80
  %preun
78
- <%= File.read(scripts["pre-uninstall"]) %>
81
+ <%= scripts[:before_remove] %>
79
82
 
80
83
  <% end -%>
81
- <% if scripts["post-uninstall"] -%>
84
+ <% if scripts[:after_remove] -%>
82
85
  %postun
83
- <%= File.read(scripts["post-uninstall"]) %>
86
+ <%= scripts[:after_remove] %>
84
87
 
85
88
  <% end -%>
86
89
  %files
87
90
  %defattr(-,root,root,-)
88
91
  <%# Output config files and then regular files. -%>
89
- <%= config_files.collect { |c| '%config ' + c }.join("\n") -%>
90
- <%# list only files, not directories? %>
92
+ <% config_files.each do |path| -%>
93
+ %config <%= path %>
94
+ <% end -%>
95
+ <%# list only files, not directories? -%>
91
96
  <%=
92
- # Reject directories, then prefix files with "/", then make sure
93
- # paths with spaces are quoted. I hate rpm so much.
97
+ # Reject directories or config files already listed, then prefix files with
98
+ # "/", then make sure paths with spaces are quoted. I hate rpm so much.
94
99
  files.reject { |f| File.directory?(File.join(staging_path, f)) } \
95
100
  .collect { |f| "/#{f}" } \
101
+ .reject { |f| config_files.include?(f) } \
96
102
  .collect { |f| f[/\s/] and "\"#{f}\"" or f } \
97
103
  .join("\n")
98
104
  %>
metadata CHANGED
@@ -1,19 +1,19 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fpm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0pre3
5
- prerelease: 5
4
+ version: 0.4.0
5
+ prerelease:
6
6
  platform: ruby
7
7
  authors:
8
8
  - Jordan Sissel
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-03-09 00:00:00.000000000 Z
12
+ date: 2012-03-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: json
16
- requirement: &14151240 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,43 +21,63 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *14151240
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
25
30
  - !ruby/object:Gem::Dependency
26
31
  name: cabin
27
- requirement: &14150740 !ruby/object:Gem::Requirement
32
+ requirement: !ruby/object:Gem::Requirement
28
33
  none: false
29
34
  requirements:
30
35
  - - ~>
31
36
  - !ruby/object:Gem::Version
32
- version: 0.4.2
37
+ version: 0.4.3
33
38
  type: :runtime
34
39
  prerelease: false
35
- version_requirements: *14150740
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 0.4.3
36
46
  - !ruby/object:Gem::Dependency
37
47
  name: backports
38
- requirement: &14150240 !ruby/object:Gem::Requirement
48
+ requirement: !ruby/object:Gem::Requirement
39
49
  none: false
40
50
  requirements:
41
- - - =
51
+ - - '='
42
52
  - !ruby/object:Gem::Version
43
53
  version: 2.3.0
44
54
  type: :runtime
45
55
  prerelease: false
46
- version_requirements: *14150240
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: 2.3.0
47
62
  - !ruby/object:Gem::Dependency
48
63
  name: arr-pm
49
- requirement: &14149860 !ruby/object:Gem::Requirement
64
+ requirement: !ruby/object:Gem::Requirement
50
65
  none: false
51
66
  requirements:
52
- - - ! '>='
67
+ - - ~>
53
68
  - !ruby/object:Gem::Version
54
- version: '0'
69
+ version: 0.0.7
55
70
  type: :runtime
56
71
  prerelease: false
57
- version_requirements: *14149860
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: 0.0.7
58
78
  - !ruby/object:Gem::Dependency
59
79
  name: clamp
60
- requirement: &14149400 !ruby/object:Gem::Requirement
80
+ requirement: !ruby/object:Gem::Requirement
61
81
  none: false
62
82
  requirements:
63
83
  - - ! '>='
@@ -65,10 +85,15 @@ dependencies:
65
85
  version: '0'
66
86
  type: :runtime
67
87
  prerelease: false
68
- version_requirements: *14149400
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
69
94
  - !ruby/object:Gem::Dependency
70
95
  name: rush
71
- requirement: &14148980 !ruby/object:Gem::Requirement
96
+ requirement: !ruby/object:Gem::Requirement
72
97
  none: false
73
98
  requirements:
74
99
  - - ! '>='
@@ -76,10 +101,15 @@ dependencies:
76
101
  version: '0'
77
102
  type: :development
78
103
  prerelease: false
79
- version_requirements: *14148980
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
80
110
  - !ruby/object:Gem::Dependency
81
111
  name: rspec
82
- requirement: &14148560 !ruby/object:Gem::Requirement
112
+ requirement: !ruby/object:Gem::Requirement
83
113
  none: false
84
114
  requirements:
85
115
  - - ! '>='
@@ -87,18 +117,28 @@ dependencies:
87
117
  version: '0'
88
118
  type: :development
89
119
  prerelease: false
90
- version_requirements: *14148560
91
- - !ruby/object:Gem::Dependency
92
- name: insist
93
- requirement: &14148140 !ruby/object:Gem::Requirement
120
+ version_requirements: !ruby/object:Gem::Requirement
94
121
  none: false
95
122
  requirements:
96
123
  - - ! '>='
97
124
  - !ruby/object:Gem::Version
98
125
  version: '0'
99
- type: :runtime
126
+ - !ruby/object:Gem::Dependency
127
+ name: insist
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ~>
132
+ - !ruby/object:Gem::Version
133
+ version: 0.0.3
134
+ type: :development
100
135
  prerelease: false
101
- version_requirements: *14148140
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ~>
140
+ - !ruby/object:Gem::Version
141
+ version: 0.0.3
102
142
  description: Convert directories, rpms, python eggs, rubygems, and more to rpms, debs,
103
143
  solaris packages and more. Win at package management without wasting pointless hours
104
144
  debugging bad rpm specs!
@@ -155,12 +195,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
155
195
  required_rubygems_version: !ruby/object:Gem::Requirement
156
196
  none: false
157
197
  requirements:
158
- - - ! '>'
198
+ - - ! '>='
159
199
  - !ruby/object:Gem::Version
160
- version: 1.3.1
200
+ version: '0'
161
201
  requirements: []
162
202
  rubyforge_project:
163
- rubygems_version: 1.8.16
203
+ rubygems_version: 1.8.18
164
204
  signing_key:
165
205
  specification_version: 3
166
206
  summary: fpm - package building and mangling