fpm 1.6.1 → 1.14.1

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.
@@ -1,4 +1,4 @@
1
- require "backports" # gem backports
1
+ require "backports/latest" # gem backports/latest
2
2
  require "fpm/package"
3
3
  require "fpm/util"
4
4
  require "digest"
@@ -12,27 +12,11 @@ class FPM::Package::FreeBSD < FPM::Package
12
12
  :after_remove => "post-deinstall",
13
13
  } unless defined?(SCRIPT_MAP)
14
14
 
15
- def self.default_abi
16
- abi_name = %x{uname -s}.chomp
17
- abi_version = %x{uname -r}.chomp.split(".")[0]
18
- abi_arch = %x{uname -m}.chomp
19
-
20
- [abi_name, abi_version, abi_arch].join(":")
21
- end
22
-
23
- option "--abi", "ABI",
24
- "Sets the FreeBSD abi pkg field to specify binary compatibility.",
25
- :default => default_abi
26
-
27
15
  option "--origin", "ABI",
28
16
  "Sets the FreeBSD 'origin' pkg field",
29
17
  :default => "fpm/<name>"
30
18
 
31
19
  def output(output_path)
32
- # See https://github.com/jordansissel/fpm/issues/1090
33
- # require xz later, because this triggers a load of liblzma.so.5 that is
34
- # unavailable on older CentOS/RH distros.
35
- require "xz"
36
20
  output_check(output_path)
37
21
 
38
22
  # Build the packaging metadata files.
@@ -58,7 +42,7 @@ class FPM::Package::FreeBSD < FPM::Package
58
42
  pkg_version = (iteration and (iteration.to_i > 0)) ? "#{version}-#{iteration}" : "#{version}"
59
43
 
60
44
  pkgdata = {
61
- "abi" => attributes[:freebsd_abi],
45
+ "arch" => architecture,
62
46
  "name" => name,
63
47
  "version" => pkg_version,
64
48
  "comment" => description,
@@ -92,24 +76,44 @@ class FPM::Package::FreeBSD < FPM::Package
92
76
  file.write(pkgdata.to_json + "\n")
93
77
  end
94
78
 
95
- # Create the .txz package archive from the files in staging_path.
96
- File.open(output_path, "wb") do |file|
97
- XZ::StreamWriter.new(file) do |xz|
98
- FPM::Util::TarWriter.new(xz) do |tar|
99
- # The manifests must come first for pkg.
100
- add_path(tar, "+COMPACT_MANIFEST",
101
- File.join(staging_path, "+COMPACT_MANIFEST"))
102
- add_path(tar, "+MANIFEST",
103
- File.join(staging_path, "+MANIFEST"))
104
-
105
- checksums.keys.each do |path|
106
- add_path(tar, "/" + path, File.join(staging_path, path))
107
- end
108
- end
109
- end
79
+ file_list = File.new(build_path("file_list"), "w")
80
+ files.each do |i|
81
+ file_list.puts(i)
110
82
  end
83
+ file_list.close
84
+
85
+ # Create the .txz package archive from the files in staging_path.
86
+ # We use --files-from here to keep the tar entries from having `./` as the prefix.
87
+ # This is done as a best effor to mimic what FreeBSD packages do, having everything at the top-level as
88
+ # file names, like "+MANIFEST" instead of "./+MANIFEST"
89
+ safesystem("tar", "-Jcf", output_path, "-C", staging_path, "--files-from", build_path("file_list"), "--transform", 's|^\([^+]\)|/\1|')
111
90
  end # def output
112
91
 
92
+ # Handle architecture naming conversion:
93
+ # <osname>:<osversion>:<arch>:<wordsize>[.other]
94
+ def architecture
95
+ osname = %x{uname -s}.chomp
96
+ osversion = %x{uname -r}.chomp.split('.').first
97
+
98
+ # Essentially because no testing on other platforms
99
+ arch = 'x86'
100
+
101
+ wordsize = case @architecture
102
+ when nil, 'native'
103
+ %x{getconf LONG_BIT}.chomp # 'native' is current arch
104
+ when 'arm64'
105
+ '64'
106
+ when 'amd64'
107
+ '64'
108
+ when 'i386'
109
+ '32'
110
+ else
111
+ %x{getconf LONG_BIT}.chomp # default to native, the current arch
112
+ end
113
+
114
+ return [osname, osversion, arch, wordsize].join(':')
115
+ end
116
+
113
117
  def add_path(tar, tar_path, path)
114
118
  stat = File.lstat(path)
115
119
  if stat.directory?
@@ -30,6 +30,9 @@ class FPM::Package::Gem < FPM::Package
30
30
  option "--gem", "PATH_TO_GEM",
31
31
  "The path to the 'gem' tool (defaults to 'gem' and searches " \
32
32
  "your $PATH)", :default => "gem"
33
+ option "--shebang", "SHEBANG",
34
+ "Replace the shebang in the executables in the bin path with a " \
35
+ "custom string", :default => nil
33
36
  option "--fix-name", :flag, "Should the target package name be prefixed?",
34
37
  :default => true
35
38
  option "--fix-dependencies", :flag, "Should the package dependencies be " \
@@ -41,9 +44,36 @@ class FPM::Package::Gem < FPM::Package
41
44
  option "--disable-dependency", "gem_name",
42
45
  "The gem name to remove from dependency list",
43
46
  :multivalued => true, :attribute_name => :gem_disable_dependencies
47
+ option "--embed-dependencies", :flag, "Should the gem dependencies " \
48
+ "be installed?", :default => false
44
49
 
45
50
  option "--version-bins", :flag, "Append the version to the bins", :default => false
46
51
 
52
+ option "--stagingdir", "STAGINGDIR",
53
+ "The directory where fpm installs the gem temporarily before conversion. " \
54
+ "Normally a random subdirectory of workdir."
55
+
56
+ option "--git-repo", "GIT_REPO",
57
+ "Use this git repo address as the source of the gem instead of " \
58
+ "rubygems.org.", :default => nil
59
+
60
+ option "--git-branch", "GIT_BRANCH",
61
+ "When using a git repo as the source of the gem instead of " \
62
+ "rubygems.org, use this git branch.",
63
+ :default => nil
64
+
65
+ # Override parent method
66
+ def staging_path(path=nil)
67
+ @gem_staging_path ||= attributes[:gem_stagingdir] || Stud::Temporary.directory("package-#{type}-staging")
68
+ @staging_path = @gem_staging_path
69
+
70
+ if path.nil?
71
+ return @staging_path
72
+ else
73
+ return File.join(@staging_path, path)
74
+ end
75
+ end # def staging_path
76
+
47
77
  def input(gem)
48
78
  # 'arg' is the name of the rubygem we should unpack.
49
79
  path_to_gem = download_if_necessary(gem, version)
@@ -70,21 +100,34 @@ class FPM::Package::Gem < FPM::Package
70
100
 
71
101
  logger.info("Trying to download", :gem => gem_name, :version => gem_version)
72
102
 
73
- gem_fetch = [ "#{attributes[:gem_gem]}", "fetch", gem_name]
74
-
75
- gem_fetch += ["--prerelease"] if attributes[:gem_prerelease?]
76
- gem_fetch += ["--version", gem_version] if gem_version
77
-
78
103
  download_dir = build_path(gem_name)
79
104
  FileUtils.mkdir(download_dir) unless File.directory?(download_dir)
80
105
 
81
- ::Dir.chdir(download_dir) do |dir|
82
- logger.debug("Downloading in directory #{dir}")
83
- safesystem(*gem_fetch)
106
+ if attributes[:gem_git_repo]
107
+ require "git"
108
+ logger.debug("Git cloning in directory #{download_dir}")
109
+ g = Git.clone(attributes[:gem_git_repo],gem_name,:path => download_dir)
110
+ if attributes[:gem_git_branch]
111
+ g.branch(attributes[:gem_git_branch]).checkout
112
+ g.pull('origin',attributes[:gem_git_branch])
113
+ end
114
+ gem_build = [ "#{attributes[:gem_gem]}", "build", "#{g.dir.to_s}/#{gem_name}.gemspec"]
115
+ ::Dir.chdir(g.dir.to_s) do |dir|
116
+ logger.debug("Building in directory #{dir}")
117
+ safesystem(*gem_build)
118
+ end
119
+ gem_files = ::Dir.glob(File.join(g.dir.to_s, "*.gem"))
120
+ else
121
+ gem_fetch = [ "#{attributes[:gem_gem]}", "fetch", gem_name]
122
+ gem_fetch += ["--prerelease"] if attributes[:gem_prerelease?]
123
+ gem_fetch += ["--version", gem_version] if gem_version
124
+ ::Dir.chdir(download_dir) do |dir|
125
+ logger.debug("Downloading in directory #{dir}")
126
+ safesystem(*gem_fetch)
127
+ end
128
+ gem_files = ::Dir.glob(File.join(download_dir, "*.gem"))
84
129
  end
85
130
 
86
- gem_files = ::Dir.glob(File.join(download_dir, "*.gem"))
87
-
88
131
  if gem_files.length != 1
89
132
  raise "Unexpected number of gem files in #{download_dir}, #{gem_files.length} should be 1"
90
133
  end
@@ -141,7 +184,7 @@ class FPM::Package::Gem < FPM::Package
141
184
  # composing multiple packages, it's best to explicitly include it in the provides list.
142
185
  self.provides << "#{self.name} = #{self.version}"
143
186
 
144
- if !attributes[:no_auto_depends?]
187
+ if !attributes[:no_auto_depends?] && !attributes[:gem_embed_dependencies?]
145
188
  spec.runtime_dependencies.map do |dep|
146
189
  # rubygems 1.3.5 doesn't have 'Gem::Dependency#requirement'
147
190
  if dep.respond_to?(:requirement)
@@ -177,8 +220,18 @@ class FPM::Package::Gem < FPM::Package
177
220
 
178
221
  ::FileUtils.mkdir_p(installdir)
179
222
  # TODO(sissel): Allow setting gem tool path
180
- args = [attributes[:gem_gem], "install", "--quiet", "--no-ri", "--no-rdoc",
181
- "--no-user-install", "--install-dir", installdir, "--ignore-dependencies"]
223
+ args = [attributes[:gem_gem], "install", "--quiet", "--no-user-install", "--install-dir", installdir]
224
+ if ::Gem::VERSION =~ /^[012]\./
225
+ args += [ "--no-ri", "--no-rdoc" ]
226
+ else
227
+ # Rubygems 3.0.0 changed --no-ri to --no-document
228
+ args += [ "--no-document" ]
229
+ end
230
+
231
+ if !attributes[:gem_embed_dependencies?]
232
+ args += ["--ignore-dependencies"]
233
+ end
234
+
182
235
  if attributes[:gem_env_shebang?]
183
236
  args += ["-E"]
184
237
  end
@@ -196,6 +249,22 @@ class FPM::Package::Gem < FPM::Package
196
249
  args << gem_path
197
250
  safesystem(*args)
198
251
 
252
+ # Replace the shebangs in the executables
253
+ if attributes[:gem_shebang]
254
+ ::Dir.entries(bin_path).each do |file_name|
255
+ # exclude . and ..
256
+ next if ['.', '..'].include?(file_name)
257
+ # exclude everything which is not a file
258
+ file_path = File.join(bin_path, file_name)
259
+ next unless File.ftype(file_path) == 'file'
260
+ # replace shebang in files if there is one
261
+ file = File.read(file_path)
262
+ if file.gsub!(/\A#!.*$/, "#!#{attributes[:gem_shebang]}")
263
+ File.open(file_path, 'w'){|f| f << file}
264
+ end
265
+ end
266
+ end
267
+
199
268
  # Delete bin_path if it's empty, and any empty parents (#612)
200
269
  # Above, we mkdir_p bin_path because rubygems aborts if the parent
201
270
  # directory doesn't exist, for example:
@@ -212,6 +281,21 @@ class FPM::Package::Gem < FPM::Package
212
281
  FileUtils.mv("#{bin_path}/#{bin}", "#{bin_path}/#{bin}-#{self.version}")
213
282
  end
214
283
  end
284
+
285
+ if attributes[:source_date_epoch_from_changelog?]
286
+ detect_source_date_from_changelog(installdir)
287
+ end
288
+
289
+ # Remove generated Makefile and gem_make.out files, if any; they
290
+ # are not needed, and may contain generated paths that cause
291
+ # different output on successive runs.
292
+ Find.find(installdir) do |path|
293
+ if path =~ /.*(gem_make.out|Makefile|mkmf.log)$/
294
+ logger.info("Removing no longer needed file %s to reduce nondeterminism" % path)
295
+ File.unlink(path)
296
+ end
297
+ end
298
+
215
299
  end # def install_to_staging
216
300
 
217
301
  # Sanitize package name.
@@ -220,5 +304,75 @@ class FPM::Package::Gem < FPM::Package
220
304
  def fix_name(name)
221
305
  return [attributes[:gem_package_name_prefix], name].join("-")
222
306
  end # def fix_name
307
+
308
+ # Regular expression to accept a gem changelog line, and store date & version, if any, in named capture groups.
309
+ # Supports formats suggested by http://keepachangelog.com and https://github.com/tech-angels/vandamme
310
+ # as well as other similar formats that actually occur in the wild.
311
+ # Build it in pieces for readability, and allow version and date in either order.
312
+ # Whenever you change this, add a row to the test case in spec/fpm/package/gem_spec.rb.
313
+ # Don't even try to handle dates that lack four-digit years.
314
+ # Building blocks:
315
+ P_RE_LEADIN = '^[#=]{0,3}\s?'
316
+ P_RE_VERSION_ = '[\w\.-]+\.[\w\.-]+[a-zA-Z0-9]'
317
+ P_RE_SEPARATOR = '\s[-=/(]?\s?'
318
+ P_RE_DATE1 = '\d{4}-\d{2}-\d{2}'
319
+ P_RE_DATE2 = '\w+ \d{1,2}(?:st|nd|rd|th)?,\s\d{4}'
320
+ P_RE_DATE3 = '\w+\s+\w+\s+\d{1,2},\s\d{4}'
321
+ P_RE_DATE = "(?<date>#{P_RE_DATE1}|#{P_RE_DATE2}|#{P_RE_DATE3})"
322
+ P_RE_URL = '\(https?:[-\w/.%]*\)' # In parens, per markdown
323
+ P_RE_GTMAGIC = '\[\]' # github magic version diff, per chandler
324
+ P_RE_VERSION = "\\[?(?:Version |v)?(?<version>#{P_RE_VERSION_})\\]?(?:#{P_RE_URL}|#{P_RE_GTMAGIC})?"
325
+ # The final RE's:
326
+ P_RE_VERSION_DATE = "#{P_RE_LEADIN}#{P_RE_VERSION}#{P_RE_SEPARATOR}#{P_RE_DATE}"
327
+ P_RE_DATE_VERSION = "#{P_RE_LEADIN}#{P_RE_DATE}#{P_RE_SEPARATOR}#{P_RE_VERSION}"
328
+
329
+ # Detect release date, if found, store in attributes[:source_date_epoch]
330
+ def detect_source_date_from_changelog(installdir)
331
+ name = self.name.sub("rubygem-", "") + "-" + self.version
332
+ changelog = nil
333
+ datestr = nil
334
+ r1 = Regexp.new(P_RE_VERSION_DATE)
335
+ r2 = Regexp.new(P_RE_DATE_VERSION)
336
+
337
+ # Changelog doesn't have a standard name, so check all common variations
338
+ # Sort this list using LANG=C, i.e. caps first
339
+ [
340
+ "CHANGELIST",
341
+ "CHANGELOG", "CHANGELOG.asciidoc", "CHANGELOG.md", "CHANGELOG.rdoc", "CHANGELOG.rst", "CHANGELOG.txt",
342
+ "CHANGES", "CHANGES.md", "CHANGES.txt",
343
+ "ChangeLog", "ChangeLog.md", "ChangeLog.txt",
344
+ "Changelog", "Changelog.md", "Changelog.txt",
345
+ "changelog", "changelog.md", "changelog.txt",
346
+ ].each do |changelogname|
347
+ path = File.join(installdir, "gems", name, changelogname)
348
+ if File.exist?(path)
349
+ changelog = path
350
+ File.open path do |file|
351
+ file.each_line do |line|
352
+ if line =~ /#{self.version}/
353
+ [r1, r2].each do |r|
354
+ if r.match(line)
355
+ datestr = $~[:date]
356
+ break
357
+ end
358
+ end
359
+ end
360
+ end
361
+ end
362
+ end
363
+ end
364
+ if datestr
365
+ date = Date.parse(datestr)
366
+ sec = date.strftime("%s")
367
+ attributes[:source_date_epoch] = sec
368
+ logger.debug("Gem %s has changelog date %s, setting source_date_epoch to %s" % [name, datestr, sec])
369
+ elsif changelog
370
+ logger.debug("Gem %s changelog %s did not have recognizable date for release %s" % [name, changelog, self.version])
371
+ else
372
+ logger.debug("Gem %s did not have changelog with recognized name" % [name])
373
+ # FIXME: check rubygems.org?
374
+ end
375
+ end # detect_source_date_from_changelog
376
+
223
377
  public(:input, :output)
224
378
  end # class FPM::Package::Gem
@@ -1,7 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  require "fpm/package"
3
3
  require "fpm/util"
4
- require "backports"
4
+ require "backports/latest"
5
5
  require "fileutils"
6
6
  require "find"
7
7
 
@@ -18,10 +18,10 @@ class FPM::Package::Pacman < FPM::Package
18
18
  option "--group", "GROUP", "The group owner of files in this package", :default => 'root'
19
19
 
20
20
  # The list of supported compression types. Default is xz (LZMA2)
21
- COMPRESSION_TYPES = [ "gz", "bzip2", "xz", "none" ]
21
+ COMPRESSION_TYPES = [ "gz", "bzip2", "xz", "zstd", "none" ]
22
22
 
23
23
  option "--compression", "COMPRESSION", "The compression type to use, must " \
24
- "be one of #{COMPRESSION_TYPES.join(", ")}.", :default => "xz" do |value|
24
+ "be one of #{COMPRESSION_TYPES.join(", ")}.", :default => "zstd" do |value|
25
25
  if !COMPRESSION_TYPES.include?(value)
26
26
  raise ArgumentError, "Pacman compression value of '#{value}' is invalid. " \
27
27
  "Must be one of #{COMPRESSION_TYPES.join(", ")}"
@@ -58,7 +58,7 @@ class FPM::Package::Pacman < FPM::Package
58
58
  end # def config_files
59
59
 
60
60
  def dependencies
61
- bogus_regex = /[^\sA-Za-z0-9><=-]/
61
+ bogus_regex = /[^\sA-Za-z0-9><=+._@-]/
62
62
  # Actually modifies depencies if they are not right
63
63
  bogus_dependencies = @dependencies.grep bogus_regex
64
64
  if bogus_dependencies.any?
@@ -137,7 +137,7 @@ class FPM::Package::Pacman < FPM::Package
137
137
  # Speaking of just taking the first entry of the field:
138
138
  # A crude thing to do, but I suppose it's better than nothing.
139
139
  # -- Daniel Haskin, 3/24/2015
140
- self.category = control["group"][0] || self.category
140
+ self.category = control["group"] && control["group"][0] || self.category
141
141
 
142
142
  # Licenses could include more than one.
143
143
  # Speaking of just taking the first entry of the field:
@@ -164,6 +164,10 @@ class FPM::Package::Pacman < FPM::Package
164
164
  end
165
165
 
166
166
  self.dependencies = control["depend"] || self.dependencies
167
+
168
+ if attributes[:no_auto_depends?]
169
+ self.dependencies = []
170
+ end
167
171
 
168
172
  self.attributes[:pacman_optional_depends] = control["optdepend"] || []
169
173
  # There are other available attributes, but I didn't include them because:
@@ -205,31 +209,39 @@ class FPM::Package::Pacman < FPM::Package
205
209
 
206
210
  def compression_option
207
211
  case self.attributes[:pacman_compression]
208
- when nil, "xz"
209
- return "--xz"
212
+ when nil, "zstd"
213
+ return "--zstd"
210
214
  when "none"
211
215
  return ""
212
216
  when "gz"
213
217
  return "-z"
218
+ when "xz"
219
+ return "--xz"
214
220
  when "bzip2"
215
221
  return "-j"
222
+ when "zstd"
223
+ return "--zstd"
216
224
  else
217
- return "--xz"
225
+ return "--zstd"
218
226
  end
219
227
  end
220
228
 
221
229
  def compression_ending
222
230
  case self.attributes[:pacman_compression]
223
- when nil, "xz"
224
- return ".xz"
231
+ when nil, "zstd"
232
+ return ".zst"
225
233
  when "none"
226
234
  return ""
227
235
  when "gz"
228
236
  return ".gz"
237
+ when "zx"
238
+ return ".xz"
229
239
  when "bzip2"
230
240
  return ".bz2"
241
+ when "zstd"
242
+ return ".zst"
231
243
  else
232
- return ".xz"
244
+ return ".zst"
233
245
  end
234
246
  end
235
247
 
@@ -241,7 +253,11 @@ class FPM::Package::Pacman < FPM::Package
241
253
  Find.find(staging_path) do |path|
242
254
  src = path.gsub(/^#{staging_path}/, '')
243
255
  dst = build_path(src)
244
- copy_entry(path, dst, preserve=true, remove_destination=true)
256
+ begin
257
+ copy_entry(path, dst, preserve=true, remove_destination=true)
258
+ rescue
259
+ copy_entry(path, dst, preserve=false, remove_destination=true)
260
+ end
245
261
  copy_metadata(path, dst)
246
262
  end
247
263
 
@@ -10,13 +10,14 @@ require "pleaserun/cli"
10
10
  # This does not currently support 'output'
11
11
  class FPM::Package::PleaseRun < FPM::Package
12
12
  # TODO(sissel): Implement flags.
13
-
13
+
14
14
  require "pleaserun/platform/systemd"
15
15
  require "pleaserun/platform/upstart"
16
16
  require "pleaserun/platform/launchd"
17
17
  require "pleaserun/platform/sysv"
18
18
 
19
19
  option "--name", "SERVICE_NAME", "The name of the service you are creating"
20
+ option "--chdir", "CHDIR", "The working directory used by the service"
20
21
 
21
22
  private
22
23
  def input(command)
@@ -27,6 +28,10 @@ class FPM::Package::PleaseRun < FPM::Package
27
28
  ::PleaseRun::Platform::Launchd.new("10.9"), # OS X
28
29
  ::PleaseRun::Platform::SYSV.new("lsb-3.1") # Ancient stuff
29
30
  ]
31
+ pleaserun_attributes = [ "chdir", "user", "group", "umask", "chroot", "nice", "limit_coredump",
32
+ "limit_cputime", "limit_data", "limit_file_size", "limit_locked_memory",
33
+ "limit_open_files", "limit_user_processes", "limit_physical_memory", "limit_stack_size",
34
+ "log_directory", "log_file_stderr", "log_file_stdout"]
30
35
 
31
36
  attributes[:pleaserun_name] ||= File.basename(command.first)
32
37
  attributes[:prefix] ||= "/usr/share/pleaserun/#{attributes[:pleaserun_name]}"
@@ -41,6 +46,13 @@ class FPM::Package::PleaseRun < FPM::Package
41
46
  else
42
47
  platform.name
43
48
  end
49
+ pleaserun_attributes.each do |attribute_name|
50
+ attribute = "pleaserun_#{attribute_name}".to_sym
51
+ if attributes.has_key?(attribute) and not attributes[attribute].nil?
52
+ platform.send("#{attribute_name}=", attributes[attribute])
53
+ end
54
+ end
55
+
44
56
  base = staging_path(File.join(attributes[:prefix], "#{platform.platform}/#{platform.target_version || "default"}"))
45
57
  target = File.join(base, "files")
46
58
  actions_script = File.join(base, "install_actions.sh")
@@ -47,6 +47,11 @@ class get_metadata(Command):
47
47
 
48
48
  def process_dep(self, dep):
49
49
  deps = []
50
+ if hasattr(dep, 'marker') and dep.marker:
51
+ # PEP0508 marker present
52
+ if not dep.marker.evaluate():
53
+ return deps
54
+
50
55
  if dep.specs:
51
56
  for operator, version in dep.specs:
52
57
  deps.append("%s %s %s" % (dep.project_name,
@@ -85,6 +90,11 @@ class get_metadata(Command):
85
90
  for dep in pkg_resources.parse_requirements(
86
91
  self.distribution.install_requires):
87
92
  final_deps.extend(self.process_dep(dep))
93
+ if getattr(self.distribution, 'extras_require', None):
94
+ for dep in pkg_resources.parse_requirements(
95
+ v for k, v in self.distribution.extras_require.items()
96
+ if k.startswith(':') and pkg_resources.evaluate_marker(k[1:])):
97
+ final_deps.extend(self.process_dep(dep))
88
98
 
89
99
  data["dependencies"] = final_deps
90
100
 
@@ -29,6 +29,9 @@ class FPM::Package::Python < FPM::Package
29
29
  option "--pypi", "PYPI_URL",
30
30
  "PyPi Server uri for retrieving packages.",
31
31
  :default => "https://pypi.python.org/simple"
32
+ option "--trusted-host", "PYPI_TRUSTED",
33
+ "Mark this host or host:port pair as trusted for pip",
34
+ :default => nil
32
35
  option "--package-prefix", "NAMEPREFIX",
33
36
  "(DEPRECATED, use --package-name-prefix) Name to prefix the package " \
34
37
  "name with." do |value|
@@ -73,6 +76,14 @@ class FPM::Package::Python < FPM::Package
73
76
  "The python package name to remove from dependency list",
74
77
  :multivalued => true, :attribute_name => :python_disable_dependency,
75
78
  :default => []
79
+ option "--setup-py-arguments", "setup_py_argument",
80
+ "Arbitrary argument(s) to be passed to setup.py",
81
+ :multivalued => true, :attribute_name => :python_setup_py_arguments,
82
+ :default => []
83
+ option "--internal-pip", :flag,
84
+ "Use the pip module within python to install modules - aka 'python -m pip'. This is the recommended usage since Python 3.4 (2014) instead of invoking the 'pip' script",
85
+ :attribute_name => :python_internal_pip,
86
+ :default => true
76
87
 
77
88
  private
78
89
 
@@ -123,16 +134,47 @@ class FPM::Package::Python < FPM::Package
123
134
  target = build_path(package)
124
135
  FileUtils.mkdir(target) unless File.directory?(target)
125
136
 
126
- if attributes[:python_pip].nil?
137
+ if attributes[:python_internal_pip?]
138
+ # XXX: Should we detect if internal pip is available?
139
+ attributes[:python_pip] = [ attributes[:python_bin], "-m", "pip"]
140
+ end
141
+
142
+ # attributes[:python_pip] -- expected to be a path
143
+ if attributes[:python_pip]
144
+ logger.debug("using pip", :pip => attributes[:python_pip])
145
+ # TODO: Support older versions of pip
146
+
147
+ pip = [attributes[:python_pip]] if pip.is_a?(String)
148
+ setup_cmd = [
149
+ *attributes[:python_pip],
150
+ "download",
151
+ "--no-clean",
152
+ "--no-deps",
153
+ "--no-binary", ":all:",
154
+ "-d", build_path,
155
+ "-i", attributes[:python_pypi],
156
+ ]
157
+
158
+ if attributes[:python_trusted_host]
159
+ setup_cmd += [
160
+ "--trusted-host",
161
+ attributes[:python_trusted_host],
162
+ ]
163
+ end
164
+
165
+ setup_cmd += [
166
+ "--build",
167
+ target,
168
+ want_pkg,
169
+ ]
170
+
171
+ safesystem(*setup_cmd)
172
+ else
127
173
  # no pip, use easy_install
128
174
  logger.debug("no pip, defaulting to easy_install", :easy_install => attributes[:python_easyinstall])
129
175
  safesystem(attributes[:python_easyinstall], "-i",
130
176
  attributes[:python_pypi], "--editable", "-U",
131
177
  "--build-directory", target, want_pkg)
132
- else
133
- logger.debug("using pip", :pip => attributes[:python_pip])
134
- # TODO: Support older versions of pip
135
- safesystem(attributes[:python_pip], "download", "--no-clean", "--no-deps", "--no-binary", ":all:", "-i", attributes[:python_pypi], "--build", target, want_pkg)
136
178
  end
137
179
 
138
180
  # easy_install will put stuff in @tmpdir/packagename/, so find that:
@@ -179,7 +221,7 @@ class FPM::Package::Python < FPM::Package
179
221
 
180
222
  output = ::Dir.chdir(setup_dir) do
181
223
  tmp = build_path("metadata.json")
182
- setup_cmd = "env PYTHONPATH=#{pylib} #{attributes[:python_bin]} " \
224
+ setup_cmd = "env PYTHONPATH=#{pylib}:$PYTHONPATH #{attributes[:python_bin]} " \
183
225
  "setup.py --command-packages=pyfpm get_metadata --output=#{tmp}"
184
226
 
185
227
  if attributes[:python_obey_requirements_txt?]
@@ -225,7 +267,7 @@ class FPM::Package::Python < FPM::Package
225
267
 
226
268
  if !attributes[:no_auto_depends?] and attributes[:python_dependencies?]
227
269
  metadata["dependencies"].each do |dep|
228
- dep_re = /^([^<>!= ]+)\s*(?:([<>!=]{1,2})\s*(.*))?$/
270
+ dep_re = /^([^<>!= ]+)\s*(?:([~<>!=]{1,2})\s*(.*))?$/
229
271
  match = dep_re.match(dep)
230
272
  if match.nil?
231
273
  logger.error("Unable to parse dependency", :dependency => dep)
@@ -236,7 +278,7 @@ class FPM::Package::Python < FPM::Package
236
278
  next if attributes[:python_disable_dependency].include?(name)
237
279
 
238
280
  # convert == to =
239
- if cmp == "=="
281
+ if cmp == "==" or cmp == "~="
240
282
  logger.info("Converting == dependency requirement to =", :dependency => dep )
241
283
  cmp = "="
242
284
  end
@@ -310,6 +352,13 @@ class FPM::Package::Python < FPM::Package
310
352
  flags += [ "build_scripts", "--executable", attributes[:python_scripts_executable] ]
311
353
  end
312
354
 
355
+ if !attributes[:python_setup_py_arguments].nil? and !attributes[:python_setup_py_arguments].empty?
356
+ # Add optional setup.py arguments
357
+ attributes[:python_setup_py_arguments].each do |a|
358
+ flags += [ a ]
359
+ end
360
+ end
361
+
313
362
  safesystem(attributes[:python_bin], "setup.py", "install", *flags)
314
363
  end
315
364
  end # def install_to_staging