fpm-itchio 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
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