fpm-itchio 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.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELIST +629 -0
  3. data/CONTRIBUTORS +26 -0
  4. data/LICENSE +21 -0
  5. data/bin/fpm +8 -0
  6. data/lib/fpm.rb +18 -0
  7. data/lib/fpm/command.rb +642 -0
  8. data/lib/fpm/errors.rb +4 -0
  9. data/lib/fpm/namespace.rb +4 -0
  10. data/lib/fpm/package.rb +524 -0
  11. data/lib/fpm/package/cpan.rb +378 -0
  12. data/lib/fpm/package/deb.rb +887 -0
  13. data/lib/fpm/package/dir.rb +207 -0
  14. data/lib/fpm/package/empty.rb +13 -0
  15. data/lib/fpm/package/gem.rb +224 -0
  16. data/lib/fpm/package/npm.rb +120 -0
  17. data/lib/fpm/package/osxpkg.rb +164 -0
  18. data/lib/fpm/package/p5p.rb +124 -0
  19. data/lib/fpm/package/pacman.rb +397 -0
  20. data/lib/fpm/package/pear.rb +117 -0
  21. data/lib/fpm/package/pkgin.rb +35 -0
  22. data/lib/fpm/package/puppet.rb +120 -0
  23. data/lib/fpm/package/pyfpm/__init__.py +1 -0
  24. data/lib/fpm/package/pyfpm/get_metadata.py +104 -0
  25. data/lib/fpm/package/python.rb +317 -0
  26. data/lib/fpm/package/rpm.rb +583 -0
  27. data/lib/fpm/package/sh.rb +69 -0
  28. data/lib/fpm/package/solaris.rb +95 -0
  29. data/lib/fpm/package/tar.rb +74 -0
  30. data/lib/fpm/package/virtualenv.rb +145 -0
  31. data/lib/fpm/package/zip.rb +63 -0
  32. data/lib/fpm/rake_task.rb +59 -0
  33. data/lib/fpm/util.rb +253 -0
  34. data/lib/fpm/version.rb +3 -0
  35. data/templates/deb.erb +52 -0
  36. data/templates/deb/changelog.erb +5 -0
  37. data/templates/deb/ldconfig.sh.erb +13 -0
  38. data/templates/deb/postinst_upgrade.sh.erb +62 -0
  39. data/templates/deb/postrm_upgrade.sh.erb +46 -0
  40. data/templates/deb/preinst_upgrade.sh.erb +41 -0
  41. data/templates/deb/prerm_upgrade.sh.erb +39 -0
  42. data/templates/osxpkg.erb +11 -0
  43. data/templates/p5p_metadata.erb +12 -0
  44. data/templates/pacman.erb +47 -0
  45. data/templates/pacman/INSTALL.erb +41 -0
  46. data/templates/puppet/package.pp.erb +34 -0
  47. data/templates/puppet/package/remove.pp.erb +13 -0
  48. data/templates/rpm.erb +261 -0
  49. data/templates/rpm/filesystem_list +14514 -0
  50. data/templates/sh.erb +367 -0
  51. data/templates/solaris.erb +15 -0
  52. metadata +265 -0
@@ -0,0 +1,164 @@
1
+ require "fpm/package"
2
+ require "fpm/util"
3
+ require "fileutils"
4
+ require "fpm/package/dir"
5
+ require 'tempfile' # stdlib
6
+ require 'pathname' # stdlib
7
+ require 'rexml/document' # stdlib
8
+
9
+ # Use an OS X pkg built with pkgbuild.
10
+ #
11
+ # Supports input and output. Requires pkgbuild and (for input) pkgutil, part of a
12
+ # standard OS X install in 10.7 and higher.
13
+ class FPM::Package::OSXpkg < FPM::Package
14
+
15
+ # Map of what scripts are named.
16
+ SCRIPT_MAP = {
17
+ :before_install => "preinstall",
18
+ :after_install => "postinstall",
19
+ } unless defined?(SCRIPT_MAP)
20
+
21
+ POSTINSTALL_ACTIONS = [ "logout", "restart", "shutdown" ]
22
+ OWNERSHIP_OPTIONS = ["recommended", "preserve", "preserve-other"]
23
+
24
+ option "--identifier-prefix", "IDENTIFIER_PREFIX",
25
+ "Reverse domain prefix prepended to package identifier, " \
26
+ "ie. 'org.great.my'. If this is omitted, the identifer " \
27
+ "will be the package name."
28
+ option "--payload-free", :flag, "Define no payload, assumes use of script options.",
29
+ :default => false
30
+ option "--ownership", "OWNERSHIP",
31
+ "--ownership option passed to pkgbuild. Defaults to 'recommended'. " \
32
+ "See pkgbuild(1).", :default => 'recommended' do |value|
33
+ if !OWNERSHIP_OPTIONS.include?(value)
34
+ raise ArgumentError, "osxpkg-ownership value of '#{value}' is invalid. " \
35
+ "Must be one of #{OWNERSHIP_OPTIONS.join(", ")}"
36
+ end
37
+ value
38
+ end
39
+
40
+ option "--postinstall-action", "POSTINSTALL_ACTION",
41
+ "Post-install action provided in package metadata. " \
42
+ "Optionally one of '#{POSTINSTALL_ACTIONS.join("', '")}'." do |value|
43
+ if !POSTINSTALL_ACTIONS.include?(value)
44
+ raise ArgumentError, "osxpkg-postinstall-action value of '#{value}' is invalid. " \
45
+ "Must be one of #{POSTINSTALL_ACTIONS.join(", ")}"
46
+ end
47
+ value
48
+ end
49
+
50
+ dont_obsolete_paths = []
51
+ option "--dont-obsolete", "DONT_OBSOLETE_PATH",
52
+ "A file path for which to 'dont-obsolete' in the built PackageInfo. " \
53
+ "Can be specified multiple times." do |path|
54
+ dont_obsolete_paths << path
55
+ end
56
+
57
+ private
58
+ # return the identifier by prepending the reverse-domain prefix
59
+ # to the package name, else return just the name
60
+ def identifier
61
+ identifier = name.dup
62
+ if self.attributes[:osxpkg_identifier_prefix]
63
+ identifier.insert(0, "#{self.attributes[:osxpkg_identifier_prefix]}.")
64
+ end
65
+ identifier
66
+ end # def identifier
67
+
68
+ # scripts_path and write_scripts cribbed from deb.rb
69
+ def scripts_path(path=nil)
70
+ @scripts_path ||= build_path("Scripts")
71
+ FileUtils.mkdir(@scripts_path) if !File.directory?(@scripts_path)
72
+
73
+ if path.nil?
74
+ return @scripts_path
75
+ else
76
+ return File.join(@scripts_path, path)
77
+ end
78
+ end # def scripts_path
79
+
80
+ def write_scripts
81
+ SCRIPT_MAP.each do |scriptname, filename|
82
+ next unless script?(scriptname)
83
+
84
+ scripts_path(filename).tap do |pkgscript|
85
+ logger.info("Writing pkg script", :source => filename, :target => pkgscript)
86
+ File.write(pkgscript, script(scriptname))
87
+ # scripts are required to be executable
88
+ File.chmod(0755, pkgscript)
89
+ end
90
+ end
91
+ end # def write_scripts
92
+
93
+ # Returns path of a processed template PackageInfo given to 'pkgbuild --info'
94
+ # note: '--info' is undocumented:
95
+ # http://managingosx.wordpress.com/2012/07/05/stupid-tricks-with-pkgbuild
96
+ def pkginfo_template_path
97
+ pkginfo_template = Tempfile.open("fpm-PackageInfo")
98
+ pkginfo_data = template("osxpkg.erb").result(binding)
99
+ pkginfo_template.write(pkginfo_data)
100
+ pkginfo_template.close
101
+ pkginfo_template.path
102
+ end # def write_pkginfo_template
103
+
104
+ # Extract name and version from PackageInfo XML
105
+ def extract_info(package)
106
+ build_path("expand").tap do |path|
107
+ doc = REXML::Document.new File.open(File.join(path, "PackageInfo"))
108
+ pkginfo_elem = doc.elements["pkg-info"]
109
+ identifier = pkginfo_elem.attribute("identifier").value
110
+ self.version = pkginfo_elem.attribute("version").value
111
+ # set name to the last dot element of the identifier
112
+ self.name = identifier.split(".").last
113
+ logger.info("inferring name #{self.name} from pkg-id #{identifier}")
114
+ end
115
+ end # def extract_info
116
+
117
+ # Take a flat package as input
118
+ def input(input_path)
119
+ # TODO: Fail if it's a Distribution pkg or old-fashioned
120
+ expand_dir = File.join(build_path, "expand")
121
+ # expand_dir must not already exist for pkgutil --expand
122
+ safesystem("pkgutil --expand #{input_path} #{expand_dir}")
123
+
124
+ extract_info(input_path)
125
+
126
+ # extract Payload
127
+ safesystem("tar -xz -f #{expand_dir}/Payload -C #{staging_path}")
128
+ end # def input
129
+
130
+ # Output a pkgbuild pkg.
131
+ def output(output_path)
132
+ output_check(output_path)
133
+
134
+ temp_info = pkginfo_template_path
135
+
136
+ args = ["--identifier", identifier,
137
+ "--info", temp_info,
138
+ "--version", version.to_s,
139
+ "--ownership", attributes[:osxpkg_ownership]]
140
+
141
+ if self.attributes[:osxpkg_payload_free?]
142
+ args << "--nopayload"
143
+ else
144
+ args += ["--root", staging_path]
145
+ end
146
+
147
+ if attributes[:before_install_given?] or attributes[:after_install_given?]
148
+ write_scripts
149
+ args += ["--scripts", scripts_path]
150
+ end
151
+ args << output_path
152
+
153
+ safesystem("pkgbuild", *args)
154
+ FileUtils.remove_file(temp_info)
155
+ end # def output
156
+
157
+ def to_s(format=nil)
158
+ return super("NAME-VERSION.pkg") if format.nil?
159
+ return super(format)
160
+ end # def to_s
161
+
162
+ public(:input, :output, :identifier, :to_s)
163
+
164
+ end # class FPM::Package::OSXpkg
@@ -0,0 +1,124 @@
1
+ require "erb"
2
+ require "fpm/namespace"
3
+ require "fpm/package"
4
+ require "fpm/errors"
5
+ require "fpm/util"
6
+
7
+ class FPM::Package::P5P < FPM::Package
8
+
9
+ option "--user", "USER",
10
+ "Set the user to USER in the prototype files.",
11
+ :default => 'root'
12
+
13
+ option "--group", "GROUP",
14
+ "Set the group to GROUP in the prototype file.",
15
+ :default => 'root'
16
+
17
+ option "--zonetype", "ZONETYPE",
18
+ "Set the allowed zone types (global, nonglobal, both)",
19
+ :default => 'value=global value=nonglobal' do |value|
20
+ case @value
21
+ when "both"
22
+ value = "value=global value=nonglobal"
23
+ else
24
+ value = "value=#{value}"
25
+ end
26
+ end # value
27
+
28
+ option "--publisher", "PUBLISHER",
29
+ "Set the publisher name for the repository",
30
+ :default => 'FPM'
31
+
32
+ option "--lint" , :flag, "Check manifest with pkglint",
33
+ :default => true
34
+
35
+ option "--validate", :flag, "Validate with pkg install",
36
+ :default => true
37
+
38
+ def architecture
39
+ case @architecture
40
+ when nil, "native"
41
+ @architecture = %x{uname -p}.chomp
42
+ when "all"
43
+ @architecture = 'i386 value=sparc'
44
+ end
45
+
46
+ return @architecture
47
+ end # def architecture
48
+
49
+ def output(output_path)
50
+
51
+ # Fixup the category to an acceptable solaris category
52
+ case @category
53
+ when nil, "default"
54
+ @category = 'Applications/System Utilities'
55
+ end
56
+
57
+ # Manifest Filename
58
+ manifest_fn = build_path("#{name}.p5m")
59
+
60
+ # Generate a package manifest.
61
+ pkg_generate = safesystemout("pkgsend", "generate", "#{staging_path}")
62
+ File.write(build_path("#{name}.p5m.1"), pkg_generate)
63
+
64
+ # Add necessary metadata to the generated manifest.
65
+ metadata_template = template("p5p_metadata.erb").result(binding)
66
+ File.write(build_path("#{name}.mog"), metadata_template)
67
+
68
+ # Combine template and filelist; allow user to edit before proceeding
69
+ File.open(manifest_fn, "w") do |manifest|
70
+ manifest.write metadata_template
71
+ manifest.write pkg_generate
72
+ end
73
+ edit_file(manifest_fn) if attributes[:edit?]
74
+
75
+ # Execute the transmogrification on the manifest
76
+ pkg_mogrify = safesystemout("pkgmogrify", manifest_fn)
77
+ File.write(build_path("#{name}.p5m.2"), pkg_mogrify)
78
+ safesystem("cp", build_path("#{name}.p5m.2"), manifest_fn)
79
+
80
+ # Evaluate dependencies.
81
+ if !attributes[:no_auto_depends?]
82
+ pkgdepend_gen = safesystemout("pkgdepend", "generate", "-md", "#{staging_path}", manifest_fn)
83
+ File.write(build_path("#{name}.p5m.3"), pkgdepend_gen)
84
+
85
+ # Allow user to review added dependencies
86
+ edit_file(build_path("#{name}.p5m.3")) if attributes[:edit?]
87
+
88
+ safesystem("pkgdepend", "resolve", "-m", build_path("#{name}.p5m.3"))
89
+ safesystem("cp", build_path("#{name}.p5m.3.res"), manifest_fn)
90
+ end
91
+
92
+ # Final format of manifest
93
+ safesystem("pkgfmt", manifest_fn)
94
+
95
+ # Final edit for lint check and packaging
96
+ edit_file(manifest_fn) if attributes[:edit?]
97
+
98
+ # Add any facets or actuators that are needed.
99
+ # TODO(jcraig): add manpage actuator to enable install wo/ man pages
100
+
101
+ # Verify the package.
102
+ if attributes[:p5p_lint] then
103
+ safesystem("pkglint", manifest_fn)
104
+ end
105
+
106
+ # Publish the package.
107
+ repo_path = build_path("#{name}_repo")
108
+ safesystem("pkgrepo", "create", repo_path)
109
+ safesystem("pkgrepo", "set", "-s", repo_path, "publisher/prefix=#{attributes[:p5p_publisher]}")
110
+ safesystem("pkgsend", "-s", repo_path,
111
+ "publish", "-d", "#{staging_path}", "#{build_path}/#{name}.p5m")
112
+ safesystem("pkgrecv", "-s", repo_path, "-a",
113
+ "-d", build_path("#{name}.p5p"), name)
114
+
115
+ # Test the package
116
+ if attributes[:p5p_validate] then
117
+ safesystem("pkg", "install", "-nvg", build_path("#{name}.p5p"), name)
118
+ end
119
+
120
+ # Cleanup
121
+ safesystem("mv", build_path("#{name}.p5p"), output_path)
122
+
123
+ end # def output
124
+ end # class FPM::Package::IPS
@@ -0,0 +1,397 @@
1
+ # -*- coding: utf-8 -*-
2
+ require "fpm/package"
3
+ require "backports"
4
+ require "fileutils"
5
+ require "find"
6
+
7
+ class FPM::Package::Pacman < FPM::Package
8
+
9
+ option "--optional-depends", "PACKAGE",
10
+ "Add an optional dependency to the pacman package.", :multivalued => true
11
+
12
+ option "--use-file-permissions", :flag,
13
+ "Use existing file permissions when defining ownership and modes"
14
+
15
+ option "--user", "USER", "The owner of files in this package", :default => 'root'
16
+
17
+ option "--group", "GROUP", "The group owner of files in this package", :default => 'root'
18
+
19
+ # The list of supported compression types. Default is xz (LZMA2)
20
+ COMPRESSION_TYPES = [ "gz", "bzip2", "xz", "none" ]
21
+
22
+ option "--compression", "COMPRESSION", "The compression type to use, must " \
23
+ "be one of #{COMPRESSION_TYPES.join(", ")}.", :default => "xz" do |value|
24
+ if !COMPRESSION_TYPES.include?(value)
25
+ raise ArgumentError, "Pacman compression value of '#{value}' is invalid. " \
26
+ "Must be one of #{COMPRESSION_TYPES.join(", ")}"
27
+ end
28
+ value
29
+ end
30
+
31
+ def initialize(*args)
32
+ super(*args)
33
+ attributes[:pacman_optional_depends] = []
34
+ end # def initialize
35
+
36
+ def architecture
37
+ case @architecture
38
+ when nil
39
+ return %x{uname -m}.chomp # default to current arch
40
+ when "amd64" # debian and pacman disagree on architecture names
41
+ return "x86_64"
42
+ when "native"
43
+ return %x{uname -m}.chomp # 'native' is current arch
44
+ when "all", "any", "noarch"
45
+ return "any"
46
+ else
47
+ return @architecture
48
+ end
49
+ end # def architecture
50
+
51
+ def iteration
52
+ return @iteration || 1
53
+ end # def iteration
54
+
55
+ def config_files
56
+ return @config_files || []
57
+ end # def config_files
58
+
59
+ def dependencies
60
+ bogus_regex = /[^\sA-Za-z0-9><=-]/
61
+ # Actually modifies depencies if they are not right
62
+ bogus_dependencies = @dependencies.grep bogus_regex
63
+ if bogus_dependencies.any?
64
+ @dependencies.reject! { |a| a =~ bogus_regex }
65
+ logger.warn("Some of the dependencies looked like they weren't package " \
66
+ "names. Such dependency entries only serve to confuse arch. " \
67
+ "I am removing them.",
68
+ :removed_dependencies => bogus_dependencies,
69
+ :fixed_dependencies => @dependencies)
70
+ end
71
+ return @dependencies
72
+ end
73
+
74
+
75
+ # This method is invoked on a package when it has been converted to a new
76
+ # package format. The purpose of this method is to do any extra conversion
77
+ # steps, like translating dependency conditions, etc.
78
+ #def converted_from(origin)
79
+ # nothing to do by default. Subclasses may implement this.
80
+ # See the RPM package class for an example.
81
+ #end # def converted_from
82
+
83
+ # Add a new source to this package.
84
+ # The exact behavior depends on the kind of package being managed.
85
+ #
86
+ # For instance:
87
+ #
88
+ # * for FPM::Package::Dir, << expects a path to a directory or files.
89
+ # * for FPM::Package::RPM, << expects a path to an rpm.
90
+ #
91
+ # The idea is that you can keep pumping in new things to a package
92
+ # for later conversion or output.
93
+ #
94
+ # Implementations are expected to put files relevant to the 'input' in the
95
+ # staging_path
96
+ def input(pacman_pkg_path)
97
+ control = {}
98
+ # Unpack the control tarball
99
+ safesystem("tar", "-C", staging_path, "-xf", pacman_pkg_path)
100
+ pkginfo = staging_path(".PKGINFO")
101
+ mtree = staging_path(".MTREE")
102
+ install = staging_path(".INSTALL")
103
+
104
+ control_contents = File.read(pkginfo)
105
+ FileUtils.rm(pkginfo)
106
+ FileUtils.rm(mtree)
107
+
108
+
109
+ control_lines = control_contents.split("\n")
110
+ control_lines.each do |line|
111
+ key, val = line.split(/ += +/, 2)
112
+ if control.has_key? key
113
+ control[key].push(val)
114
+ else
115
+ control[key] = [val]
116
+ end
117
+ end
118
+
119
+ self.name = control["pkgname"][0]
120
+
121
+ # Parse 'epoch:version-iteration' in the version string
122
+ version_re = /^(?:([0-9]+):)?(.+?)(?:-(.*))?$/
123
+ m = version_re.match(control["pkgver"][0])
124
+ if !m
125
+ raise "Unsupported version string '#{control["pkgver"][0]}'"
126
+ end
127
+ self.epoch, self.version, self.iteration = m.captures
128
+
129
+ self.maintainer = control["packager"][0]
130
+
131
+ # Arch has no notion of vendor, so...
132
+ #self.vendor =
133
+
134
+ self.url = control["url"][0]
135
+ # Groups could include more than one.
136
+ # Speaking of just taking the first entry of the field:
137
+ # A crude thing to do, but I suppose it's better than nothing.
138
+ # -- Daniel Haskin, 3/24/2015
139
+ self.category = control["group"][0] || self.category
140
+
141
+ # Licenses could include more than one.
142
+ # Speaking of just taking the first entry of the field:
143
+ # A crude thing to do, but I suppose it's better than nothing.
144
+ # -- Daniel Haskin, 3/24/2015
145
+ self.license = control["license"][0] || self.license
146
+
147
+ self.architecture = control["arch"][0]
148
+
149
+ self.dependencies = control["depend"] || self.dependencies
150
+
151
+ self.provides = control["provides"] || self.provides
152
+
153
+ self.conflicts = control["conflict"] || self.conflicts
154
+
155
+ self.replaces = control["replaces"] || self.replaces
156
+
157
+ self.description = control["pkgdesc"][0]
158
+
159
+ if control.include? "backup"
160
+ self.config_files = control["backup"].map{|file| "/" + file}
161
+ else
162
+ self.config_files = []
163
+ end
164
+
165
+ self.dependencies = control["depend"] || self.dependencies
166
+
167
+ self.attributes[:pacman_optional_depends] = control["optdepend"] || []
168
+ # There are other available attributes, but I didn't include them because:
169
+ # - makedepend: deps needed to make the arch package. But it's already
170
+ # made. It just needs to be converted at this point
171
+ # - checkdepend: See above
172
+ # - makepkgopt: See above
173
+ # - size: can be dynamically generated
174
+ # - builddate: Should be changed to time of package conversion in the new
175
+ # package, so this value should be thrown away.
176
+
177
+ if File.exist?(install)
178
+ functions = parse_install_script(install)
179
+ if functions.include?("pre_install")
180
+ self.scripts[:before_install] = functions["pre_install"].join("\n")
181
+ end
182
+ if functions.include?("post_install")
183
+ self.scripts[:after_install] = functions["post_install"].join("\n")
184
+ end
185
+ if functions.include?("pre_upgrade")
186
+ self.scripts[:before_upgrade] = functions["pre_upgrade"].join("\n")
187
+ end
188
+ if functions.include?("post_upgrade")
189
+ self.scripts[:after_upgrade] = functions["post_upgrade"].join("\n")
190
+ end
191
+ if functions.include?("pre_remove")
192
+ self.scripts[:before_remove] = functions["pre_remove"].join("\n")
193
+ end
194
+ if functions.include?("post_remove")
195
+ self.scripts[:after_remove] = functions["post_remove"].join("\n")
196
+ end
197
+ FileUtils.rm(install)
198
+ end
199
+
200
+ # Note: didn't use `self.directories`.
201
+ # Pacman doesn't really record that information, to my knowledge.
202
+
203
+ end # def input
204
+
205
+ def compression_option
206
+ case self.attributes[:pacman_compression]
207
+ when nil, "xz"
208
+ return "--xz"
209
+ when "none"
210
+ return ""
211
+ when "gz"
212
+ return "-z"
213
+ when "bzip2"
214
+ return "-j"
215
+ else
216
+ return "--xz"
217
+ end
218
+ end
219
+
220
+ def compression_ending
221
+ case self.attributes[:pacman_compression]
222
+ when nil, "xz"
223
+ return ".xz"
224
+ when "none"
225
+ return ""
226
+ when "gz"
227
+ return ".gz"
228
+ when "bzip2"
229
+ return ".bz2"
230
+ else
231
+ return ".xz"
232
+ end
233
+ end
234
+
235
+ # Output this package to the given path.
236
+ def output(output_path)
237
+ output_check(output_path)
238
+
239
+ # Copy all files from staging to BUILD dir
240
+ Find.find(staging_path) do |path|
241
+ src = path.gsub(/^#{staging_path}/, '')
242
+ dst = build_path(src)
243
+ copy_entry(path, dst, preserve=true, remove_destination=true)
244
+ copy_metadata(path, dst)
245
+ end
246
+
247
+ # This value is used later in the template for PKGINFO
248
+ size = safesystemout("du", "-sk", build_path).split(/\s+/)[0].to_i * 1024
249
+ builddate = Time.new.to_i
250
+
251
+ pkginfo = template("pacman.erb").result(binding)
252
+ pkginfo_file = build_path(".PKGINFO")
253
+ File.write(pkginfo_file, pkginfo)
254
+
255
+ if script?(:before_install) or script?(:after_install) or \
256
+ script?(:before_upgrade) or script?(:after_upgrade) or \
257
+ script?(:before_remove) or script?(:after_remove)
258
+ install_script = template("pacman/INSTALL.erb").result(binding)
259
+ install_script_file = build_path(".INSTALL")
260
+ File.write(install_script_file, install_script)
261
+ end
262
+
263
+ generate_mtree
264
+
265
+ File.expand_path(output_path).tap do |path|
266
+ ::Dir.chdir(build_path) do
267
+ safesystem(*(["tar",
268
+ compression_option,
269
+ "-cf",
270
+ path] + data_tar_flags + \
271
+ ::Dir.entries(".").reject{|entry| entry =~ /^\.{1,2}$/ }))
272
+ end
273
+ end
274
+ end # def output
275
+
276
+ def data_tar_flags
277
+ data_tar_flags = []
278
+ if attributes[:pacman_use_file_permissions?].nil?
279
+ if !attributes[:pacman_user].nil?
280
+ if attributes[:pacman_user] == 'root'
281
+ data_tar_flags += [ "--numeric-owner", "--owner", "0" ]
282
+ else
283
+ data_tar_flags += [ "--owner", attributes[:deb_user] ]
284
+ end
285
+ end
286
+
287
+ if !attributes[:pacman_group].nil?
288
+ if attributes[:pacman_group] == 'root'
289
+ data_tar_flags += [ "--numeric-owner", "--group", "0" ]
290
+ else
291
+ data_tar_flags += [ "--group", attributes[:deb_group] ]
292
+ end
293
+ end
294
+ end
295
+ return data_tar_flags
296
+ end # def data_tar_flags
297
+
298
+ def default_output
299
+ v = version
300
+ v = "#{epoch}:#{v}" if epoch
301
+ if iteration
302
+ "#{name}_#{v}-#{iteration}_#{architecture}.#{type}"
303
+ else
304
+ "#{name}_#{v}_#{architecture}.#{type}"
305
+ end
306
+ end # def default_output
307
+
308
+ def to_s(format=nil)
309
+ # Default format if nil
310
+ # git_1.7.9.3-1_amd64.deb
311
+ return super("NAME-FULLVERSION-ARCH.pkg.tar#{compression_ending}") if format.nil?
312
+ return super(format)
313
+ end # def to_s
314
+
315
+ private
316
+
317
+ def generate_mtree
318
+ ::Dir.chdir(build_path) do
319
+ cmd = "LANG=C bsdtar "
320
+ cmd += "-czf .MTREE "
321
+ cmd += "--format=mtree "
322
+ cmd += "--options='!all,use-set,type,uid,gid,mode,time,size,md5,sha256,link' "
323
+ cmd += ::Dir.entries(".").reject{|entry| entry =~ /^\.{1,2}$/ }.join(" ")
324
+ safesystem(cmd)
325
+ end
326
+ end # def generate_mtree
327
+
328
+ # KNOWN ISSUE:
329
+ # If an un-matched bracket is used in valid bash, as in
330
+ # `echo "{"`, this function will choke.
331
+ # However, to cover this case basically
332
+ # requires writing almost half a bash parser,
333
+ # and it is a very small corner case.
334
+ # Otherwise, this approach is very robust.
335
+ def gobble_function(cons,prod)
336
+ level = 1
337
+ while level > 0
338
+ line = cons.next
339
+ # Not the best, but pretty good
340
+ # short of writing an *actual* sh
341
+ # parser
342
+ level += line.count "{"
343
+ level -= line.count "}"
344
+ if level > 0
345
+ prod.push(line.rstrip())
346
+ else
347
+ fine = line.sub(/\s*[}]\s*$/, "")
348
+ if !(fine =~ /^\s*$/)
349
+ prod.push(fine.rstrip())
350
+ end
351
+ end
352
+ end
353
+ end # def gobble_function
354
+
355
+ FIND_SCRIPT_FUNCTION_LINE =
356
+ /^\s*(\w+)\s*\(\s*\)\s*\{\s*([^}]+?)?\s*(\})?\s*$/
357
+
358
+ def parse_install_script(path)
359
+ global_lines = []
360
+ look_for = Set.new(["pre_install", "post_install",
361
+ "pre_upgrade", "post_upgrade",
362
+ "pre_remove", "post_remove"])
363
+ functions = {}
364
+ look_for.each do |fname|
365
+ functions[fname] = []
366
+ end
367
+
368
+ open(path, "r") do |iscript|
369
+ lines = iscript.each
370
+ begin
371
+ while true
372
+ line = lines.next
373
+ # This regex picks up beginning names of posix shell
374
+ # functions
375
+ # Examples:
376
+ # fname() {
377
+ # fname() { echo hi }
378
+ m = FIND_SCRIPT_FUNCTION_LINE.match(line)
379
+ if not m.nil? and look_for.include? m[1]
380
+ if not m[2].nil?
381
+ functions[m[1]].push(m[2].rstrip())
382
+ end
383
+ gobble_function(lines, functions[m[1]])
384
+ else
385
+ global_lines.push(line.rstrip())
386
+ end
387
+ end
388
+ rescue StopIteration
389
+ end
390
+ end
391
+ look_for.each do |name|
392
+ # Add global lines to each function to preserve global variables, etc.
393
+ functions[name] = global_lines + functions[name]
394
+ end
395
+ return functions
396
+ end # def parse_install_script
397
+ end # class FPM::Package::Pacman