Cross package maker. DEB/RPM generation or conversion. Derived from jordansissel/fpm.

⌈⌋ ⎇ branch:  cross package maker


Check-in [bf199ef7ab]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:- consolidate the remaining old fpm things into package/
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: bf199ef7ab334a7194b636838cf9d4ef3173f9de
User & Date: jls@semicomplete.com 2012-03-02 09:21:05
Context
2012-03-02
09:24
purge old stuff check-in: 0de986a2c7 user: jls@semicomplete.com tags: trunk
09:21
- consolidate the remaining old fpm things into package/ check-in: bf199ef7ab user: jls@semicomplete.com tags: trunk
09:20
don't sleep check-in: b7e0fa71df user: jls@semicomplete.com tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Added lib/fpm/package/npm.rb.









































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
require "fpm/namespace"
require "fpm/source"
require "fpm/util"
require "fileutils"

class FPM::Source::Npm < FPM::Source
  def get_source(params)
    @npm = @paths.first
  end # def get_source

  def download(npm_name, version=nil)
  end # def download

  def get_metadata
    # set self[:...] values
    # :name
    # :maintainer
    # :url
    # :category
    # :dependencies
  end # def get_metadata

  def make_tarball!(tar_path, builddir)
    tmpdir = "#{tar_path}.dir"
    installdir = "#{tmpdir}/#{::Gem::dir}"
    ::FileUtils.mkdir_p(installdir)
    args = ["gem", "install", "--quiet", "--no-ri", "--no-rdoc",
       "--install-dir", installdir, "--ignore-dependencies", @paths.first]
    safesystem(*args)
    tar(tar_path, ".", tmpdir)

    # TODO(sissel): Make a helper method.
    safesystem(*["gzip", "-f", tar_path])
  end

end # class FPM::Source::Gem

Added lib/fpm/package/pear.rb.



















































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
require "fpm/namespace"
require "fpm/source"
require "fileutils"
require "fpm/util"

class FPM::Source::Pear < FPM::Source
  def self.flags(opts, settings)
    opts.on("--package-prefix PREFIX",
            "Prefix for PEAR packages") do |package_prefix|
      settings.source[:package_prefix] = package_prefix
    end
  end # def flags

  def get_metadata
    @pear_package = @paths.first
    pear_cmd = "pear remote-info #{@pear_package}"
    self[:name] = %x{#{pear_cmd} | sed -ne '/^Package\s*/s/^Package\s*//p'}.chomp
    self[:version] = %x{#{pear_cmd} | sed -ne '/^Latest\s*/s/^Latest\s*//p'}.chomp
    self[:summary] = %x{#{pear_cmd} | sed -ne '/^Summary\s*/s/^Summary\s*//p'}.chomp
    if self[:settings][:package_prefix]
      self[:package_prefix] = self[:settings][:package_prefix]
    else
      self[:package_prefix] = "php-pear"
    end
	self[:name] = "#{self[:package_prefix]}-#{self[:name]}"
  end # def get_metadata

  def make_tarball!(tar_path, builddir)
    tmpdir = "#{tar_path}.dir"
    ::Dir.mkdir(tmpdir)
    safesystem("pear install -n -f -P #{tmpdir} #{@pear_package}")
	# Remove the stuff we don't want
	['.depdb', '.depdblock', '.filemap', '.lock'].each { |f| safesystem("find #{tmpdir} -type f -name '#{f}' -exec rm {} \\;") }
	# find exits non-zero even though it works, so we have to work around that
	safesystem("find #{tmpdir} -type d -name '.channel*' -exec rm -rf {} \\; 2>/dev/null; exit 0")
    tar(tar_path, '.', tmpdir)
    @paths = %x{find #{tmpdir} -mindepth 1 -maxdepth 1 -type d | sed -e 's:^#{tmpdir}:.:'}.split("\n")
    safesystem(*["gzip", "-f", tar_path])
  end

end # class FPM::Source::Gem

Added lib/fpm/package/puppet.rb.





















































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
require "erb"
require "fpm/namespace"
require "fpm/package"
require "fpm/errors"
require "etc"
require "fileutils"

# TODO(sissel): Add dependency checking support.
# IIRC this has to be done as a 'checkinstall' step.
class FPM::Target::Puppet < FPM::Package
  def architecture
    case @architecture
    when nil, "native"
      @architecture = %x{uname -m}.chomp
    end
    return @architecture
  end # def architecture

  # Default specfile generator just makes one specfile, whatever that is for
  # this package.
  def generate_specfile(builddir)
    paths = []
    @logger.info("PWD: #{File.join(builddir, unpack_data_to)}")
    fileroot = File.join(builddir, unpack_data_to)
    Dir.chdir(fileroot) do
      Find.find(".") do |p|
        next if p == "."
        paths << p
      end
    end
    @logger.info(paths[-1])
    manifests = %w{package.pp package/remove.pp}

    ::Dir.mkdir(File.join(builddir, "manifests"))
    manifests.each do |manifest|
      dir = File.join(builddir, "manifests", File.dirname(manifest))
      @logger.info("manifests targeting: #{dir}")
      ::Dir.mkdir(dir) if !File.directory?(dir)

      File.open(File.join(builddir, "manifests", manifest), "w") do |f|
        @logger.info("manifest: #{f.path}")
        template = template(File.join("puppet", "#{manifest}.erb"))
        ::Dir.chdir(fileroot) do
          f.puts template.result(binding)
        end
      end
    end
  end # def generate_specfile

  def unpack_data_to
    "files"
  end

  def build!(params)
    # TODO(sissel): Support these somehow, perhaps with execs and files.
    self.scripts.each do |name, path|
      case name
        when "pre-install"
        when "post-install"
        when "pre-uninstall"
        when "post-uninstall"
      end # case name
    end # self.scripts.each

    if File.exists?(params[:output])
      # TODO(sissel): Allow folks to choose output?
      @logger.error("Puppet module directory '#{params[:output]}' already " \
                    "exists. Delete it or choose another output (-p flag)")
    end

    ::Dir.mkdir(params[:output])
    builddir = ::Dir.pwd

    # Copy 'files' from builddir to :output/files
    Find.find("files", "manifests") do |path|
      @logger.info("Copying path: #{path}")
      if File.directory?(path)
        ::Dir.mkdir(File.join(params[:output], path))
      else
        FileUtils.cp(path, File.join(params[:output], path))
      end
    end
  end # def build!

  # The directory we create should just be the name of the package as the
  # module name
  def default_output
    name
  end # def default_output

  # This method is used by the puppet manifest template
  def puppetsort(hash)
    # TODO(sissel): Implement sorting that follows the puppet style guide
    # Such as, 'ensure' goes first, etc.
    return hash.to_a
  end # def puppetsort

  # Helper for user lookup
  def uid2user(uid)
    begin
      pwent = Etc.getpwuid(uid)
      return pwent.name
    rescue ArgumentError => e
      # Invalid user id? No user? Return the uid.
      @logger.warn("Failed to find username for uid #{uid}")
      return uid.to_s
    end
  end # def uid2user

  # Helper for group lookup
  def gid2group(gid)
    begin
      grent = Etc.getgrgid(gid)
      return grent.name
    rescue ArgumentError => e
      # Invalid user id? No user? Return the uid.
      @logger.warn("Failed to find group for gid #{gid}")
      return gid.to_s
    end
  end # def uid2user
end # class FPM::Target::Puppet

Added lib/fpm/package/solaris.rb.





















































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
require "erb"
require "fpm/namespace"
require "fpm/package"
require "fpm/errors"
require "fpm/util"

# TODO(sissel): Add dependency checking support.
# IIRC this has to be done as a 'checkinstall' step.
class FPM::Target::Solaris < FPM::Package
  def architecture
    case @architecture
    when nil, "native"
      @architecture = %x{uname -p}.chomp
    end
    # "all" is a valid arch according to
    # http://www.bolthole.com/solaris/makeapackage.html

    return @architecture
  end # def architecture

  def specfile(builddir)
    "#{builddir}/pkginfo"
  end

  def build!(params)
    self.scripts.each do |name, path|
      case name
        when "pre-install"
          safesystem("cp #{path} ./preinstall")
          File.chmod(0755, "./preinstall")
        when "post-install"
          safesystem("cp #{path} ./postinstall")
          File.chmod(0755, "./postinstall")
        when "pre-uninstall"
          raise FPM::InvalidPackageConfiguration.new(
            "pre-uninstall is not supported by Solaris packages"
          )
        when "post-uninstall"
          raise FPM::InvalidPackageConfiguration.new(
            "post-uninstall is not supported by Solaris packages"
          )
      end # case name
    end # self.scripts.each

    # Unpack data.tar.gz so we can build a package from it.
    Dir.mkdir("data")
    safesystem("gzip -d data.tar.gz");
    Dir.chdir("data") do
      safesystem("tar -xf ../data.tar");
    end

    #system("(echo 'i pkginfo'; pkgproto data=/) > Prototype")

    # Generate the package 'Prototype' file
    # TODO(sissel): allow setting default file owner.
    File.open("Prototype", "w") do |prototype|
      prototype.puts("i pkginfo")
      prototype.puts("i preinstall") if self.scripts["pre-install"]
      prototype.puts("i postinstall") if self.scripts["post-install"]

      # TODO(sissel): preinstall/postinstall
      # strip @prefix, since BASEDIR will set prefix via the pkginfo file
      IO.popen("pkgproto data/#{@prefix}=").each_line do |line|
        type, klass, path, mode, user, group = line.split
        # Override stuff in pkgproto
        # TODO(sissel): Make this tunable?
        user = "root"
        group = "root"
        prototype.puts([type, klass, path, mode, user, group].join(" "))
      end # popen "pkgproto ..."
    end # File prototype

    # Should create a package directory named by the package name.
    safesystem("pkgmk -o -d .")

    # Convert the 'package directory' built above to a real solaris package.
    safesystem("pkgtrans -s . #{params[:output]} #{name}")
  end # def build

  def default_output
    v = version
    v = "#{epoch}:#{v}" if epoch
    if iteration
      "#{name}_#{v}-#{iteration}_#{architecture}.#{type}"
    else
      "#{name}_#{v}_#{architecture}.#{type}"
    end
  end # def default_output
end # class FPM::Deb

Added lib/fpm/package/tar.rb.





































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
require "backports" # gem backports
require "fpm/source"
require "fpm/util"
require "fileutils"

class FPM::Source::Tar < FPM::Source
  def get_metadata
    self[:name] = @paths.first.split(".").first
  end # def get_metadata

  def make_tarball!(tar_path, builddir)
    input_tarball = @paths.first

    if input_tarball =~ /\.tar\.bz2$/
      compression = :bipz2
    elsif input_tarball =~ /\.tar\.gz$/
      compression = :gzip
    elsif input_tarball =~ /\.tar\.xz$/
      compression = :lzma
    else
      compression = :none
    end

    # Unpack the tar file
    installdir = "#{builddir}/tarbuild/#{self[:prefix]}"
    FileUtils.mkdir_p(installdir)
    flags = "-xf #{input_tarball} -C #{installdir}"
    case compression
      when :bzip2; flags += " -j"
      when :gzip; flags += " -z"
      when :lzma; flags += " --lzma"
    end
    #puts("tar #{flags}")
    #sleep 5
    safesystem("tar #{flags}")

    if self[:prefix]
      @paths = [self[:prefix]]
    else
      @paths = ["."]
    end

    ::Dir.chdir("#{builddir}/tarbuild") do
      tar(tar_path, ".")
    end

    # TODO(sissel): Make a helper method.
    safesystem(*["gzip", "-f", tar_path])
  end # def make_tarball!
end # class FPM::Source::Dir

Added lib/fpm/source/deb.rb.

















































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
require "erb"
require "fpm/namespace"
require "fpm/package"
require "fpm/errors"
require "fpm/util"

class FPM::Target::Deb < FPM::Package

  # Supported Debian control files
  CONTROL_FILES = {
    "pre-install"       => [:script, "preinst"],
    "post-install"      => [:script, "postinst"],
    "pre-uninstall"     => [:script, "prerm"],
    "post-uninstall"    => [:script, "postrm"],
    "debconf-config"    => [:script, "config"],
    "debconf-templates" => [:text,   "templates"]
  }

  def self.flags(opts, settings)
    settings.target[:deb] = "deb"

    opts.on("--ignore-iteration-in-dependencies",
            "For = dependencies, allow iterations on the specified version.  Default is to be specific.") do |x|
      settings.target[:ignore_iteration] = true
    end

    opts.on("--pre-depends DEPENDENCY", "Add DEPENDENCY as Pre-Depends.") do |dep|
      (settings.target[:pre_depends] ||= []) << dep
    end

    # Take care about the case when we want custom control file but still use fpm ...
    opts.on("--custom-control FILEPATH",
            "Custom version of the Debian control file.") do |control|
      settings.target[:control] = File.expand_path(control)
    end
    
    # Add custom debconf config file
    opts.on("--config SCRIPTPATH",
            "Add SCRIPTPATH as debconf config file.") do |config|
      settings.scripts["debconf-config"] = File.expand_path(config)
    end
    
    # Add custom debconf templates file
    opts.on("--templates FILEPATH",
            "Add FILEPATH as debconf templates file.") do |templates|
      settings.scripts["debconf-templates"] = File.expand_path(templates)
    end
  end

  def needs_md5sums
    case %x{uname -s}.chomp
    when "Darwin"
      return false
    else
      return true
    end
  end # def needs_md5sums

  def architecture
    if @architecture.nil? or @architecture == "native"
      # Default architecture should be 'native' which we'll need
      # to ask the system about.
      arch = %x{dpkg --print-architecture 2> /dev/null}.chomp
      if $?.exitstatus != 0
        arch = %x{uname -m}.chomp
        @logger.warn("Can't find 'dpkg' tool (need it to get default " \
                     "architecture!). Please specificy --architecture " \
                     "specifically. (Defaulting now to #{arch})")
      end
      @architecture = arch
    elsif @architecture == "x86_64"
      # Debian calls x86_64 "amd64"
      @architecture = "amd64"
    end

    return @architecture
  end # def architecture

  def specfile(builddir)
    "#{builddir}/control"
  end

  def name
    if @name =~ /[A-Z]/
      @logger.warn("Debian tools (dpkg/apt) don't do well with packages " \
        "that use capital letters in the name. In some cases it will " \
        "automatically downcase them, in others it will not. It is confusing." \
        "Best to not use any capital letters at all.")
      @name = @name.downcase
    end

    if @name.include?("_")
      @logger.info("Package name '#{@name}' includes underscores, converting" \
                   " to dashes")
      @name = @name.gsub(/[_]/, "-")
    end

    return @name
  end

  def build!(params)
    control_files = [ "control" ]
    if File.exists? "./md5sums"
      control_files << "md5sums"
    end

    # Use custom Debian control file when given ...
    if self.settings[:control]
      %x{cp #{self.settings[:control]} ./control 2> /dev/null 2>&1}
      @logger.warn("Unable to process custom Debian control file (exit " \
                   "code: #{$?.exitstatus}). Falling back to default " \
                   "template.") unless $?.exitstatus == 0
    end

    # place the control files
    self.scripts.each do |name, path|
      ctrl_type, ctrl_file = CONTROL_FILES[name]
      if ctrl_file
        safesystem("cp",  path, "./#{ctrl_file}")
        safesystem("chmod", "a+x", "./#{ctrl_file}") if ctrl_type == :script
        control_files << ctrl_file
      else
        raise "Unsupported script name '#{name}' (path: #{path})"
      end
    end # self.scripts.each

    if self.config_files.any?
      File.open('conffiles', 'w'){ |f| f.puts(config_files.join("\n")) }
      control_files << 'conffiles'
    end

    # Make the control
    safesystem(tar_cmd, "--numeric-owner", "--owner=0", "--group=0",
               "-zcf", "control.tar.gz", *control_files)

    # create debian-binary
    File.open("debian-binary", "w") { |f| f.puts "2.0" }

    # pack up the .deb
    safesystem("ar", "-qc", "#{params[:output]}", "debian-binary", "control.tar.gz", "data.tar.gz")
  end # def build

  def default_output
    if iteration
      "#{name}_#{version}-#{iteration}_#{architecture}.#{type}"
    else
      "#{name}_#{version}_#{architecture}.#{type}"
    end
  end # def default_output

  def fix_dependency(dep)
    if dep =~ /[\(,\|]/
      # Don't "fix" ones that could appear well formed already.
    else
      da = dep.split(/ +/)
      if da.size > 1
        # Convert strings 'foo >= bar' to 'foo (>= bar)'
        dep = "#{da[0]} (#{da[1]} #{da[2]})"
      end
    end

    name_re = /^[^ \(]+/
    name = dep[name_re]
    if name =~ /[A-Z]/
      @logger.warn("Downcasing dependency '#{name}' because deb packages " \
                   " don't work so good with uppercase names")
      dep.gsub!(name_re) { |n| n.downcase }
    end

    if dep =~ /_/
      @logger.warn("Replacing underscores with dashes in '#{dep}' because " \
                   "debs don't like underscores")
      dep.gsub!("_", "-")
    end

    # Convert gem ~> X.Y.Z to '>= X.Y.Z' and << X.Y+1.0
    if dep =~ /\(~>/
      name, version = dep.gsub(/[()~>]/, "").split(/ +/)[0..1]
      nextversion = version.split(".").collect { |v| v.to_i }
      l = nextversion.length
      nextversion[l-2] += 1
      nextversion[l-1] = 0
      nextversion = nextversion.join(".")
      return ["#{name} (>= #{version})", "#{name} (<< #{nextversion})"]
    # ignore iterations for = dependencies if flag specified
    elsif (m = dep.match(/(\S+)\s+\(= (.+)\)/)) && self.settings[:ignore_iteration]
      name, version = m[1..2]
      nextversion = version.split('.').collect { |v| v.to_i }
      nextversion[-1] += 1
      nextversion = nextversion.join(".")
      return ["#{name} (>= #{version})", "#{name} (<< #{nextversion})"]
    else
      return dep
    end
  end # def fix_dependency

  def pre_dependencies
    self.settings[:pre_depends] || []
  end # def pre_dependencies
end # class FPM::Target::Deb

Deleted lib/fpm/source/pyfpm/__init__.py.

1
__all__ = [ "list_dependencies" ]
<


Deleted lib/fpm/source/pyfpm/get_metadata.py.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
from distutils.core import Command
import json
import re
import time

# Note, the last time I coded python daily was at Google, so it's entirely
# possible some of my techniques below are outdated or bad.
# If you have fixes, let me know.

class get_metadata(Command):
  description = "get package metadata"
  user_options = []

  def initialize_options(self):
    pass
  # def initialize_options

  def finalize_options(self):
    pass
  # def finalize_options

  def run(self):
    #print type(self.distribution)
    #for i in sorted(dir(self.distribution)):
      #if i.startswith("_"):
        #continue
      ###print "%s: %r" % (i, self.__getattr__(i))
      #print "%s" % i

    data = {
      "name": self.distribution.get_name(),
      "version": self.distribution.get_version(),
      "author": "%s <%s>" % (self.distribution.get_author(),
        self.distribution.get_author_email()),
      "description": self.distribution.get_description(),
      "license": self.distribution.get_license(),
      "url": self.distribution.get_url(),
    }

    # If there are python C/extension modules, we'll want to build a native
    # arch package.
    if self.distribution.has_ext_modules():
      data["architecture"] = "native"
    else:
      data["architecture"] = "all"
    # end if

    dependencies = None
    try:
      dependencies = self.distribution.install_requires
    except:
      pass

    # In some cases (Mysql-Python) 'dependencies' is none, not empty.
    if dependencies is None:
      dependencies = []

    # Some cases (like paramiko) dependencies is actually just a string, not a
    # list
    if isinstance(dependencies, str):
      dependencies = [dependencies]

    final_deps = []
    dep_re = re.compile("([^<>= ]+)(?:\s*([<>=]{1,2})\s*([^,]*))?(?:,\s*([<>=]{1,2})\s*(.*))?$")
    for dep in dependencies:
      # python deps are strings that look like:
      # "packagename"
      # "packagename >= version"
      # Replace 'packagename' with 'python#{suffix}-packagename'
      m = dep_re.match(dep)
      if m is None:
        print "Bad dep: %s" % dep
        time.sleep(3)
      elif m.groups()[1] is None:
        name, cond, version = m.groups()[0], ">=", 0
      else:
        groups = m.groups()
        name, cond, version = groups[0:3]
        if groups[3] is not None:
          final_deps.append("%s %s %s" % (groups[0],
                                          self._replace_deprecated(groups[3]),
                                          groups[4]))
      # end if

      final_deps.append("%s %s %s" % (name,
                                      self._replace_deprecated(cond),
                                      version))
    # end for i in dependencies

    data["dependencies"] = final_deps

    #print json.dumps(data, indent=2)
    try:
      print json.dumps(data, indent=2)
    except AttributeError, e:
      # For Python 2.5 and Debian's python-json
      print json.write(data)

  # def run

  def _replace_deprecated(self, sign):
    """Replace deprecated operators"""
    return {'<': '<<', '>': '>>'}.get(sign, sign)

# class list_dependencies
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


















































































































































































































Deleted lib/fpm/target/deb.rb.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
require "erb"
require "fpm/namespace"
require "fpm/package"
require "fpm/errors"
require "fpm/util"

class FPM::Target::Deb < FPM::Package

  # Supported Debian control files
  CONTROL_FILES = {
    "pre-install"       => [:script, "preinst"],
    "post-install"      => [:script, "postinst"],
    "pre-uninstall"     => [:script, "prerm"],
    "post-uninstall"    => [:script, "postrm"],
    "debconf-config"    => [:script, "config"],
    "debconf-templates" => [:text,   "templates"]
  }

  def self.flags(opts, settings)
    settings.target[:deb] = "deb"

    opts.on("--ignore-iteration-in-dependencies",
            "For = dependencies, allow iterations on the specified version.  Default is to be specific.") do |x|
      settings.target[:ignore_iteration] = true
    end

    opts.on("--pre-depends DEPENDENCY", "Add DEPENDENCY as Pre-Depends.") do |dep|
      (settings.target[:pre_depends] ||= []) << dep
    end

    # Take care about the case when we want custom control file but still use fpm ...
    opts.on("--custom-control FILEPATH",
            "Custom version of the Debian control file.") do |control|
      settings.target[:control] = File.expand_path(control)
    end
    
    # Add custom debconf config file
    opts.on("--config SCRIPTPATH",
            "Add SCRIPTPATH as debconf config file.") do |config|
      settings.scripts["debconf-config"] = File.expand_path(config)
    end
    
    # Add custom debconf templates file
    opts.on("--templates FILEPATH",
            "Add FILEPATH as debconf templates file.") do |templates|
      settings.scripts["debconf-templates"] = File.expand_path(templates)
    end
  end

  def needs_md5sums
    case %x{uname -s}.chomp
    when "Darwin"
      return false
    else
      return true
    end
  end # def needs_md5sums

  def architecture
    if @architecture.nil? or @architecture == "native"
      # Default architecture should be 'native' which we'll need
      # to ask the system about.
      arch = %x{dpkg --print-architecture 2> /dev/null}.chomp
      if $?.exitstatus != 0
        arch = %x{uname -m}.chomp
        @logger.warn("Can't find 'dpkg' tool (need it to get default " \
                     "architecture!). Please specificy --architecture " \
                     "specifically. (Defaulting now to #{arch})")
      end
      @architecture = arch
    elsif @architecture == "x86_64"
      # Debian calls x86_64 "amd64"
      @architecture = "amd64"
    end

    return @architecture
  end # def architecture

  def specfile(builddir)
    "#{builddir}/control"
  end

  def name
    if @name =~ /[A-Z]/
      @logger.warn("Debian tools (dpkg/apt) don't do well with packages " \
        "that use capital letters in the name. In some cases it will " \
        "automatically downcase them, in others it will not. It is confusing." \
        "Best to not use any capital letters at all.")
      @name = @name.downcase
    end

    if @name.include?("_")
      @logger.info("Package name '#{@name}' includes underscores, converting" \
                   " to dashes")
      @name = @name.gsub(/[_]/, "-")
    end

    return @name
  end

  def build!(params)
    control_files = [ "control" ]
    if File.exists? "./md5sums"
      control_files << "md5sums"
    end

    # Use custom Debian control file when given ...
    if self.settings[:control]
      %x{cp #{self.settings[:control]} ./control 2> /dev/null 2>&1}
      @logger.warn("Unable to process custom Debian control file (exit " \
                   "code: #{$?.exitstatus}). Falling back to default " \
                   "template.") unless $?.exitstatus == 0
    end

    # place the control files
    self.scripts.each do |name, path|
      ctrl_type, ctrl_file = CONTROL_FILES[name]
      if ctrl_file
        safesystem("cp",  path, "./#{ctrl_file}")
        safesystem("chmod", "a+x", "./#{ctrl_file}") if ctrl_type == :script
        control_files << ctrl_file
      else
        raise "Unsupported script name '#{name}' (path: #{path})"
      end
    end # self.scripts.each

    if self.config_files.any?
      File.open('conffiles', 'w'){ |f| f.puts(config_files.join("\n")) }
      control_files << 'conffiles'
    end

    # Make the control
    safesystem(tar_cmd, "--numeric-owner", "--owner=0", "--group=0",
               "-zcf", "control.tar.gz", *control_files)

    # create debian-binary
    File.open("debian-binary", "w") { |f| f.puts "2.0" }

    # pack up the .deb
    safesystem("ar", "-qc", "#{params[:output]}", "debian-binary", "control.tar.gz", "data.tar.gz")
  end # def build

  def default_output
    if iteration
      "#{name}_#{version}-#{iteration}_#{architecture}.#{type}"
    else
      "#{name}_#{version}_#{architecture}.#{type}"
    end
  end # def default_output

  def fix_dependency(dep)
    if dep =~ /[\(,\|]/
      # Don't "fix" ones that could appear well formed already.
    else
      da = dep.split(/ +/)
      if da.size > 1
        # Convert strings 'foo >= bar' to 'foo (>= bar)'
        dep = "#{da[0]} (#{da[1]} #{da[2]})"
      end
    end

    name_re = /^[^ \(]+/
    name = dep[name_re]
    if name =~ /[A-Z]/
      @logger.warn("Downcasing dependency '#{name}' because deb packages " \
                   " don't work so good with uppercase names")
      dep.gsub!(name_re) { |n| n.downcase }
    end

    if dep =~ /_/
      @logger.warn("Replacing underscores with dashes in '#{dep}' because " \
                   "debs don't like underscores")
      dep.gsub!("_", "-")
    end

    # Convert gem ~> X.Y.Z to '>= X.Y.Z' and << X.Y+1.0
    if dep =~ /\(~>/
      name, version = dep.gsub(/[()~>]/, "").split(/ +/)[0..1]
      nextversion = version.split(".").collect { |v| v.to_i }
      l = nextversion.length
      nextversion[l-2] += 1
      nextversion[l-1] = 0
      nextversion = nextversion.join(".")
      return ["#{name} (>= #{version})", "#{name} (<< #{nextversion})"]
    # ignore iterations for = dependencies if flag specified
    elsif (m = dep.match(/(\S+)\s+\(= (.+)\)/)) && self.settings[:ignore_iteration]
      name, version = m[1..2]
      nextversion = version.split('.').collect { |v| v.to_i }
      nextversion[-1] += 1
      nextversion = nextversion.join(".")
      return ["#{name} (>= #{version})", "#{name} (<< #{nextversion})"]
    else
      return dep
    end
  end # def fix_dependency

  def pre_dependencies
    self.settings[:pre_depends] || []
  end # def pre_dependencies
end # class FPM::Target::Deb
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
















































































































































































































































































































































































































Deleted lib/fpm/target/puppet.rb.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
require "erb"
require "fpm/namespace"
require "fpm/package"
require "fpm/errors"
require "etc"
require "fileutils"

# TODO(sissel): Add dependency checking support.
# IIRC this has to be done as a 'checkinstall' step.
class FPM::Target::Puppet < FPM::Package
  def architecture
    case @architecture
    when nil, "native"
      @architecture = %x{uname -m}.chomp
    end
    return @architecture
  end # def architecture

  # Default specfile generator just makes one specfile, whatever that is for
  # this package.
  def generate_specfile(builddir)
    paths = []
    @logger.info("PWD: #{File.join(builddir, unpack_data_to)}")
    fileroot = File.join(builddir, unpack_data_to)
    Dir.chdir(fileroot) do
      Find.find(".") do |p|
        next if p == "."
        paths << p
      end
    end
    @logger.info(paths[-1])
    manifests = %w{package.pp package/remove.pp}

    ::Dir.mkdir(File.join(builddir, "manifests"))
    manifests.each do |manifest|
      dir = File.join(builddir, "manifests", File.dirname(manifest))
      @logger.info("manifests targeting: #{dir}")
      ::Dir.mkdir(dir) if !File.directory?(dir)

      File.open(File.join(builddir, "manifests", manifest), "w") do |f|
        @logger.info("manifest: #{f.path}")
        template = template(File.join("puppet", "#{manifest}.erb"))
        ::Dir.chdir(fileroot) do
          f.puts template.result(binding)
        end
      end
    end
  end # def generate_specfile

  def unpack_data_to
    "files"
  end

  def build!(params)
    # TODO(sissel): Support these somehow, perhaps with execs and files.
    self.scripts.each do |name, path|
      case name
        when "pre-install"
        when "post-install"
        when "pre-uninstall"
        when "post-uninstall"
      end # case name
    end # self.scripts.each

    if File.exists?(params[:output])
      # TODO(sissel): Allow folks to choose output?
      @logger.error("Puppet module directory '#{params[:output]}' already " \
                    "exists. Delete it or choose another output (-p flag)")
    end

    ::Dir.mkdir(params[:output])
    builddir = ::Dir.pwd

    # Copy 'files' from builddir to :output/files
    Find.find("files", "manifests") do |path|
      @logger.info("Copying path: #{path}")
      if File.directory?(path)
        ::Dir.mkdir(File.join(params[:output], path))
      else
        FileUtils.cp(path, File.join(params[:output], path))
      end
    end
  end # def build!

  # The directory we create should just be the name of the package as the
  # module name
  def default_output
    name
  end # def default_output

  # This method is used by the puppet manifest template
  def puppetsort(hash)
    # TODO(sissel): Implement sorting that follows the puppet style guide
    # Such as, 'ensure' goes first, etc.
    return hash.to_a
  end # def puppetsort

  # Helper for user lookup
  def uid2user(uid)
    begin
      pwent = Etc.getpwuid(uid)
      return pwent.name
    rescue ArgumentError => e
      # Invalid user id? No user? Return the uid.
      @logger.warn("Failed to find username for uid #{uid}")
      return uid.to_s
    end
  end # def uid2user

  # Helper for group lookup
  def gid2group(gid)
    begin
      grent = Etc.getgrgid(gid)
      return grent.name
    rescue ArgumentError => e
      # Invalid user id? No user? Return the uid.
      @logger.warn("Failed to find group for gid #{gid}")
      return gid.to_s
    end
  end # def uid2user
end # class FPM::Target::Puppet

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




















































































































































































































































Deleted lib/fpm/target/solaris.rb.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
require "erb"
require "fpm/namespace"
require "fpm/package"
require "fpm/errors"
require "fpm/util"

# TODO(sissel): Add dependency checking support.
# IIRC this has to be done as a 'checkinstall' step.
class FPM::Target::Solaris < FPM::Package
  def architecture
    case @architecture
    when nil, "native"
      @architecture = %x{uname -p}.chomp
    end
    # "all" is a valid arch according to
    # http://www.bolthole.com/solaris/makeapackage.html

    return @architecture
  end # def architecture

  def specfile(builddir)
    "#{builddir}/pkginfo"
  end

  def build!(params)
    self.scripts.each do |name, path|
      case name
        when "pre-install"
          safesystem("cp #{path} ./preinstall")
          File.chmod(0755, "./preinstall")
        when "post-install"
          safesystem("cp #{path} ./postinstall")
          File.chmod(0755, "./postinstall")
        when "pre-uninstall"
          raise FPM::InvalidPackageConfiguration.new(
            "pre-uninstall is not supported by Solaris packages"
          )
        when "post-uninstall"
          raise FPM::InvalidPackageConfiguration.new(
            "post-uninstall is not supported by Solaris packages"
          )
      end # case name
    end # self.scripts.each

    # Unpack data.tar.gz so we can build a package from it.
    Dir.mkdir("data")
    safesystem("gzip -d data.tar.gz");
    Dir.chdir("data") do
      safesystem("tar -xf ../data.tar");
    end

    #system("(echo 'i pkginfo'; pkgproto data=/) > Prototype")

    # Generate the package 'Prototype' file
    # TODO(sissel): allow setting default file owner.
    File.open("Prototype", "w") do |prototype|
      prototype.puts("i pkginfo")
      prototype.puts("i preinstall") if self.scripts["pre-install"]
      prototype.puts("i postinstall") if self.scripts["post-install"]

      # TODO(sissel): preinstall/postinstall
      # strip @prefix, since BASEDIR will set prefix via the pkginfo file
      IO.popen("pkgproto data/#{@prefix}=").each_line do |line|
        type, klass, path, mode, user, group = line.split
        # Override stuff in pkgproto
        # TODO(sissel): Make this tunable?
        user = "root"
        group = "root"
        prototype.puts([type, klass, path, mode, user, group].join(" "))
      end # popen "pkgproto ..."
    end # File prototype

    # Should create a package directory named by the package name.
    safesystem("pkgmk -o -d .")

    # Convert the 'package directory' built above to a real solaris package.
    safesystem("pkgtrans -s . #{params[:output]} #{name}")
  end # def build

  def default_output
    v = version
    v = "#{epoch}:#{v}" if epoch
    if iteration
      "#{name}_#{v}-#{iteration}_#{architecture}.#{type}"
    else
      "#{name}_#{v}_#{architecture}.#{type}"
    end
  end # def default_output
end # class FPM::Deb

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<