fpm 1.3.3 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -90,7 +90,15 @@ class get_metadata(Command):
90
90
 
91
91
  output = open(self.output, "w")
92
92
  if hasattr(json, 'dumps'):
93
- output.write(json.dumps(data, indent=2))
93
+ def default_to_str(obj):
94
+ """ Fall back to using __str__ if possible """
95
+ # This checks if the class of obj defines __str__ itself,
96
+ # so we don't fall back to an inherited __str__ method.
97
+ if "__str__" in type(obj).__dict__:
98
+ return str(obj)
99
+ return json.JSONEncoder.default(self, obj)
100
+
101
+ output.write(json.dumps(data, indent=2, default=default_to_str))
94
102
  else:
95
103
  # For Python 2.5 and Debian's python-json
96
104
  output.write(json.write(data))
@@ -7,7 +7,7 @@ require "fileutils"
7
7
  require "tmpdir"
8
8
  require "json"
9
9
 
10
- # Support for python packages.
10
+ # Support for python packages.
11
11
  #
12
12
  # This supports input, but not output.
13
13
  #
@@ -28,7 +28,7 @@ class FPM::Package::Python < FPM::Package
28
28
  "is used instead", :default => nil
29
29
  option "--pypi", "PYPI_URL",
30
30
  "PyPi Server uri for retrieving packages.",
31
- :default => "http://pypi.python.org/simple"
31
+ :default => "https://pypi.python.org/simple"
32
32
  option "--package-prefix", "NAMEPREFIX",
33
33
  "(DEPRECATED, use --package-name-prefix) Name to prefix the package " \
34
34
  "name with." do |value|
@@ -69,6 +69,10 @@ class FPM::Package::Python < FPM::Package
69
69
  "current python interpreter (sys.executable). This option is equivalent " \
70
70
  "to appending 'build_scripts --executable PYTHON_EXECUTABLE' arguments " \
71
71
  "to 'setup.py install' command."
72
+ option "--disable-dependency", "python_package_name",
73
+ "The python package name to remove from dependency list",
74
+ :multivalued => true, :attribute_name => :python_disable_dependency,
75
+ :default => []
72
76
 
73
77
  private
74
78
 
@@ -88,7 +92,7 @@ class FPM::Package::Python < FPM::Package
88
92
  setup_py = path_to_package
89
93
  end
90
94
 
91
- if !File.exists?(setup_py)
95
+ if !File.exist?(setup_py)
92
96
  logger.error("Could not find 'setup.py'", :path => setup_py)
93
97
  raise "Unable to find python package; tried #{setup_py}"
94
98
  end
@@ -104,7 +108,7 @@ class FPM::Package::Python < FPM::Package
104
108
  # part should go elsewhere.
105
109
  path = package
106
110
  # If it's a path, assume local build.
107
- if File.directory?(path) or (File.exists?(path) and File.basename(path) == "setup.py")
111
+ if File.directory?(path) or (File.exist?(path) and File.basename(path) == "setup.py")
108
112
  return path
109
113
  end
110
114
 
@@ -127,7 +131,7 @@ class FPM::Package::Python < FPM::Package
127
131
  "--build-directory", target, want_pkg)
128
132
  else
129
133
  logger.debug("using pip", :pip => attributes[:python_pip])
130
- safesystem(attributes[:python_pip], "install", "--no-deps", "--no-install", "-i", attributes[:python_pypi], "-U", "--build", target, want_pkg)
134
+ safesystem(attributes[:python_pip], "install", "--no-deps", "--no-install", "--no-use-wheel", "-i", attributes[:python_pypi], "-U", "--build", target, want_pkg)
131
135
  end
132
136
 
133
137
  # easy_install will put stuff in @tmpdir/packagename/, so find that:
@@ -145,7 +149,7 @@ class FPM::Package::Python < FPM::Package
145
149
  attributes[:python_package_name_prefix] = attributes[:python_package_prefix]
146
150
  end
147
151
 
148
- begin
152
+ begin
149
153
  json_test_code = [
150
154
  "try:",
151
155
  " import json",
@@ -219,7 +223,7 @@ class FPM::Package::Python < FPM::Package
219
223
  self.name = self.name.downcase if attributes[:python_downcase_name?]
220
224
 
221
225
  if !attributes[:no_auto_depends?] and attributes[:python_dependencies?]
222
- self.dependencies = metadata["dependencies"].collect do |dep|
226
+ metadata["dependencies"].each do |dep|
223
227
  dep_re = /^([^<>!= ]+)\s*(?:([<>!=]{1,2})\s*(.*))?$/
224
228
  match = dep_re.match(dep)
225
229
  if match.nil?
@@ -228,6 +232,8 @@ class FPM::Package::Python < FPM::Package
228
232
  end
229
233
  name, cmp, version = match.captures
230
234
 
235
+ next if attributes[:python_disable_dependency].include?(name)
236
+
231
237
  # convert == to =
232
238
  if cmp == "=="
233
239
  logger.info("Converting == dependency requirement to =", :dependency => dep )
@@ -241,7 +247,8 @@ class FPM::Package::Python < FPM::Package
241
247
 
242
248
  # convert dependencies from python-Foo to python-foo
243
249
  name = name.downcase if attributes[:python_downcase_dependencies?]
244
- "#{name} #{cmp} #{version}"
250
+
251
+ self.dependencies << "#{name} #{cmp} #{version}"
245
252
  end
246
253
  end # if attributes[:python_dependencies?]
247
254
  end # def load_package_info
@@ -267,7 +274,7 @@ class FPM::Package::Python < FPM::Package
267
274
 
268
275
  prefix = "/"
269
276
  prefix = attributes[:prefix] unless attributes[:prefix].nil?
270
-
277
+
271
278
  # Some setup.py's assume $PWD == current directory of setup.py, so let's
272
279
  # chdir first.
273
280
  ::Dir.chdir(project_dir) do
@@ -29,7 +29,7 @@ class FPM::Package::RPM < FPM::Package
29
29
  "bzip2" => "w9.bzdio"
30
30
  } unless defined?(COMPRESSION_MAP)
31
31
 
32
- option "--use-file-permissions", :flag,
32
+ option "--use-file-permissions", :flag,
33
33
  "Use existing file permissions when defining ownership and modes."
34
34
 
35
35
  option "--user", "USER", "Set the user to USER in the %files section. Overrides the user when used with use-file-permissions setting."
@@ -55,6 +55,8 @@ class FPM::Package::RPM < FPM::Package
55
55
  next rpmbuild_define
56
56
  end
57
57
 
58
+ option "--dist", "DIST-TAG", "Set the rpm distribution."
59
+
58
60
  option "--digest", DIGEST_ALGORITHM_MAP.keys.join("|"),
59
61
  "Select a digest algorithm. md5 works on the most platforms.",
60
62
  :default => "md5" do |value|
@@ -98,7 +100,7 @@ class FPM::Package::RPM < FPM::Package
98
100
  option "--attr", "ATTRFILE",
99
101
  "Set the attribute for a file (%attr).",
100
102
  :multivalued => true, :attribute_name => :attrs
101
-
103
+
102
104
  option "--init", "FILEPATH", "Add FILEPATH as an init script",
103
105
  :multivalued => true do |file|
104
106
  next File.expand_path(file)
@@ -149,14 +151,14 @@ class FPM::Package::RPM < FPM::Package
149
151
  match = trigger.match(/^(\[.*\]|)(.*): (.*)$/)
150
152
  @logger.fatal("Trigger '#{trigger_type}' definition can't be parsed ('#{trigger}')") unless match
151
153
  opt, pkg, file = match.captures
152
- @logger.fatal("File given for --trigger-#{trigger_type} does not exist (#{file})") unless File.exists?(file)
154
+ @logger.fatal("File given for --trigger-#{trigger_type} does not exist (#{file})") unless File.exist?(file)
153
155
  rpm_trigger << [pkg, File.read(file), opt.tr('[]','')]
154
156
  next rpm_trigger
155
157
  end
156
158
  end
157
-
159
+
158
160
  private
159
-
161
+
160
162
  # Fix path name
161
163
  # Replace [ with [\[] to make rpm not use globs
162
164
  # Replace * with [*] to make rpm not use globs
@@ -289,7 +291,7 @@ class FPM::Package::RPM < FPM::Package
289
291
  script_path = self.attributes[scriptname]
290
292
  # Skip scripts not set
291
293
  next if script_path.nil?
292
- if !File.exists?(script_path)
294
+ if !File.exist?(script_path)
293
295
  logger.error("No such file (for #{scriptname.to_s}): #{script_path.inspect}")
294
296
  script_errors << script_path
295
297
  end
@@ -303,7 +305,6 @@ class FPM::Package::RPM < FPM::Package
303
305
  end # def converted
304
306
 
305
307
  def rpm_get_trigger_type(flag)
306
- puts "#{flag.to_s(2)}"
307
308
  if (flag & (1 << 25)) == (1 << 25)
308
309
  :rpm_trigger_before_install
309
310
  elsif (flag & (1 << 16)) == (1 << 16)
@@ -375,13 +376,13 @@ class FPM::Package::RPM < FPM::Package
375
376
  [name, operator, version].join(" ")
376
377
  end
377
378
  #input.replaces += replaces
378
-
379
+
379
380
  self.config_files += rpm.config_files
380
381
 
381
382
  # rpms support '%dir' things for specifying empty directories to package,
382
383
  # but the rpm header itself doesn't actually record this information.
383
384
  # so there's no 'directories' to copy, so don't try to merge in the
384
- # 'directories' feature.
385
+ # 'directories' feature.
385
386
  # TODO(sissel): If you want this feature, we'll have to find scan
386
387
  # the extracted rpm for empty directories. I'll wait until someone asks for
387
388
  # this feature
@@ -414,6 +415,9 @@ class FPM::Package::RPM < FPM::Package
414
415
  args += ["--target", rpm_target]
415
416
  end
416
417
 
418
+ # set the rpm dist tag
419
+ args += ["--define", "dist .#{attributes[:rpm_dist]}"] if attributes[:rpm_dist]
420
+
417
421
  args += [
418
422
  "--define", "buildroot #{build_path}/BUILD",
419
423
  "--define", "_topdir #{build_path}",
@@ -531,7 +535,10 @@ class FPM::Package::RPM < FPM::Package
531
535
  end # def epoch
532
536
 
533
537
  def to_s(format=nil)
534
- return super("NAME-VERSION-ITERATION.ARCH.TYPE") if format.nil?
538
+ if format.nil?
539
+ return super("NAME-VERSION-ITERATION.DIST.ARCH.TYPE").gsub('DIST', attributes[:rpm_dist]) if attributes[:rpm_dist]
540
+ return super("NAME-VERSION-ITERATION.ARCH.TYPE")
541
+ end
535
542
  return super(format)
536
543
  end # def to_s
537
544
 
@@ -11,7 +11,7 @@ require "digest"
11
11
  #
12
12
  # This class only supports output of packages.
13
13
  #
14
- # The sh package is a single sh file with a bzipped tar payload concatenated to the end.
14
+ # The sh package is a single sh file with a tar payload concatenated to the end.
15
15
  # The script can unpack the tarball to install it and call optional post install scripts.
16
16
  class FPM::Package::Sh < FPM::Package
17
17
 
@@ -24,12 +24,6 @@ class FPM::Package::Sh < FPM::Package
24
24
  end
25
25
 
26
26
  def create_scripts
27
- if script?(:before_install)
28
- # the scripts are kept in the payload so what would before install be if we've already
29
- # unpacked the payload?
30
- raise "sh package does not support before install scripts."
31
- end
32
-
33
27
  if script?(:after_install)
34
28
  File.write(File.join(fpm_meta_path, "after_install"), script(:after_install))
35
29
  end
@@ -75,7 +75,7 @@ class FPM::Package::Solaris < FPM::Package
75
75
  # Should create a package directory named by the package name.
76
76
  safesystem("pkgmk", "-o", "-f", "#{build_path}/Prototype", "-d", build_path)
77
77
  end
78
-
78
+
79
79
 
80
80
  # Convert the 'package directory' built above to a real solaris package.
81
81
  safesystem("pkgtrans", "-s", build_path, output_path, name)
@@ -0,0 +1,136 @@
1
+ require "fpm/namespace"
2
+ require "fpm/package"
3
+ require "fpm/util"
4
+
5
+ # Support for python virtualenv packages.
6
+ #
7
+ # This supports input, but not output.
8
+ #
9
+ class FPM::Package::Virtualenv < FPM::Package
10
+ # Flags '--foo' will be accessable as attributes[:virtualenv_foo]
11
+
12
+ option "--pypi", "PYPI_URL",
13
+ "PyPi Server uri for retrieving packages.",
14
+ :default => "https://pypi.python.org/simple"
15
+ option "--package-name-prefix", "PREFIX", "Name to prefix the package " \
16
+ "name with.", :default => "virtualenv"
17
+
18
+ option "--install-location", "DIRECTORY", "Location to which to " \
19
+ "install the virtualenv by default.", :default => "/usr/share/python" do |path|
20
+ File.expand_path(path)
21
+ end
22
+
23
+ option "--fix-name", :flag, "Should the target package name be prefixed?",
24
+ :default => true
25
+ option "--other-files-dir", "DIRECTORY", "Optionally, the contents of the " \
26
+ "specified directory may be added to the package. This is useful if the " \
27
+ "virtualenv needs configuration files, etc.", :default => nil
28
+
29
+ private
30
+
31
+ # Input a package.
32
+ #
33
+ # `package` can look like `psutil==2.2.1` or `psutil`.
34
+ def input(package)
35
+ installdir = attributes[:virtualenv_install_location]
36
+ m = /^([^=]+)==([^=]+)$/.match(package)
37
+ package_version = nil
38
+
39
+ if m
40
+ package_name = m[1]
41
+ package_version = m[2]
42
+ self.version ||= package_version
43
+ else
44
+ package_name = package
45
+ package_version = nil
46
+ end
47
+
48
+ virtualenv_name = package_name
49
+
50
+ self.name ||= package_name
51
+
52
+ if self.attributes[:virtualenv_fix_name?]
53
+ self.name = [self.attributes[:virtualenv_package_name_prefix],
54
+ self.name].join("-")
55
+ end
56
+
57
+ virtualenv_folder =
58
+ File.join(installdir,
59
+ virtualenv_name)
60
+
61
+ virtualenv_build_folder = build_path(virtualenv_folder)
62
+
63
+ ::FileUtils.mkdir_p(virtualenv_build_folder)
64
+
65
+ safesystem("virtualenv", virtualenv_build_folder)
66
+ pip_exe = File.join(virtualenv_build_folder, "bin", "pip")
67
+ python_exe = File.join(virtualenv_build_folder, "bin", "python")
68
+
69
+ # Why is this hack here? It looks important, so I'll keep it in.
70
+ safesystem(pip_exe, "install", "-U", "-i",
71
+ attributes[:virtualenv_pypi],
72
+ "pip", "distribute")
73
+ safesystem(pip_exe, "uninstall", "-y", "distribute")
74
+
75
+ safesystem(pip_exe, "install", "-i",
76
+ attributes[:virtualenv_pypi],
77
+ package)
78
+
79
+ if package_version.nil?
80
+ frozen = safesystemout(pip_exe, "freeze")
81
+ package_version = frozen[/#{package}==[^=]+$/].split("==")[1].chomp!
82
+ self.version ||= package_version
83
+ end
84
+
85
+ ::Dir[build_path + "/**/*"].each do |f|
86
+ if ! File.world_readable? f
87
+ File.lchmod(File.stat(f).mode | 444)
88
+ end
89
+ end
90
+
91
+ ::Dir.chdir(virtualenv_build_folder) do
92
+ safesystem("virtualenv-tools", "--update-path", virtualenv_folder)
93
+ end
94
+
95
+ if !attributes[:virtualenv_other_files_dir].nil?
96
+ # Copy all files from other dir to build_path
97
+ Find.find(attributes[:virtualenv_other_files_dir]) do |path|
98
+ src = path.gsub(/^#{attributes[:virtualenv_other_files_dir]}/, '')
99
+ dst = File.join(build_path, src)
100
+ copy_entry(path, dst, preserve=true, remove_destination=true)
101
+ copy_metadata(path, dst)
102
+ end
103
+ end
104
+
105
+ remove_python_compiled_files virtualenv_build_folder
106
+
107
+ # use dir to set stuff up properly, mainly so I don't have to reimplement
108
+ # the chdir/prefix stuff special for tar.
109
+ dir = convert(FPM::Package::Dir)
110
+
111
+ if attributes[:chdir]
112
+ dir.attributes[:chdir] = File.join(build_path, attributes[:chdir])
113
+ else
114
+ dir.attributes[:chdir] = build_path
115
+ end
116
+
117
+ cleanup_staging
118
+ # Tell 'dir' to input "." and chdir/prefix will help it figure out the
119
+ # rest.
120
+ dir.input(".")
121
+ @staging_path = dir.staging_path
122
+ dir.cleanup_build
123
+
124
+ end # def input
125
+
126
+ # Delete python precompiled files found in a given folder.
127
+ def remove_python_compiled_files path
128
+ logger.debug("Now removing python object and compiled files from the virtualenv")
129
+ Find.find(path) do |path|
130
+ if path.end_with? '.pyc' or path.end_with? '.pyo'
131
+ FileUtils.rm path
132
+ end
133
+ end
134
+ end
135
+ public(:input)
136
+ end # class FPM::Package::Virtualenv
@@ -42,7 +42,7 @@ class FPM::Package::Zip < FPM::Package
42
42
  # the compression type.
43
43
  def output(output_path)
44
44
  output_check(output_path)
45
-
45
+
46
46
  files = Find.find(staging_path).to_a
47
47
  safesystem("zip", output_path, *files)
48
48
  end # def output
@@ -24,6 +24,8 @@ module FPM::Util
24
24
 
25
25
  # Is the given program in the system's PATH?
26
26
  def program_in_path?(program)
27
+ # return false if path is not set
28
+ return false unless ENV['PATH']
27
29
  # Scan path to find the executable
28
30
  # Do this to help the user get a better error message.
29
31
  envpath = ENV["PATH"].split(":")
@@ -33,12 +35,12 @@ module FPM::Util
33
35
  def program_exists?(program)
34
36
  # Scan path to find the executable
35
37
  # Do this to help the user get a better error message.
36
- return program_in_path?(program) if !program.include?("/")
38
+ return program_in_path?(program) if !program.include?("/")
37
39
  return File.executable?(program)
38
40
  end # def program_exists?
39
41
 
40
42
  def default_shell
41
- shell = ENV["SHELL"]
43
+ shell = ENV["SHELL"]
42
44
  return "/bin/sh" if shell.nil? || shell.empty?
43
45
  return shell
44
46
  end
@@ -139,7 +141,7 @@ module FPM::Util
139
141
  end # def tar_cmd
140
142
 
141
143
  # Run a block with a value.
142
- # Useful in lieu of assigning variables
144
+ # Useful in lieu of assigning variables
143
145
  def with(value, &block)
144
146
  block.call(value)
145
147
  end # def with
@@ -157,7 +159,30 @@ module FPM::Util
157
159
  rc
158
160
  end
159
161
 
160
- def copy_entry(src, dst)
162
+ def copy_metadata(source, destination)
163
+ source_stat = File::lstat(source)
164
+ dest_stat = File::lstat(destination)
165
+
166
+ # If this is a hard-link, there's no metadata to copy.
167
+ # If this is a symlink, what it points to hasn't been copied yet.
168
+ return if source_stat.ino == dest_stat.ino || dest_stat.symlink?
169
+
170
+ File.utime(source_stat.atime, source_stat.mtime, destination)
171
+ mode = source_stat.mode
172
+ begin
173
+ File.lchown(source_stat.uid, source_stat.gid, destination)
174
+ rescue Errno::EPERM
175
+ # clear setuid/setgid
176
+ mode &= 01777
177
+ end
178
+
179
+ unless source_stat.symlink?
180
+ File.chmod(mode, destination)
181
+ end
182
+ end # def copy_metadata
183
+
184
+
185
+ def copy_entry(src, dst, preserve=false, remove_destination=false)
161
186
  case File.ftype(src)
162
187
  when 'fifo', 'characterSpecial', 'blockSpecial', 'socket'
163
188
  st = File.stat(src)
@@ -173,7 +198,8 @@ module FPM::Util
173
198
  if known_entry
174
199
  FileUtils.ln(known_entry, dst)
175
200
  else
176
- FileUtils.copy_entry(src, dst)
201
+ FileUtils.copy_entry(src, dst, preserve=preserve,
202
+ remove_destination=remove_destination)
177
203
  copied_entries[[st.dev, st.ino]] = dst
178
204
  end
179
205
  end # else...