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

⌈⌋ ⎇ branch:  cross package maker


Check-in [78fca7c39d]

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

Overview
Comment:- purge pre-refactor stuff - move python package to new api
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 78fca7c39d8a47022bf7f8620e88edfb6b453780
User & Date: jls@semicomplete.com 2012-03-01 06:26:03
Context
2012-03-02
07:17
- Get things together enough that this works again: fpm -s dir -t rpm -d "zsh" -n testing ~/.zshrc - Move to using clamp instead of optparse check-in: aac642a70e user: jls@semicomplete.com tags: trunk
2012-03-01
06:26
- purge pre-refactor stuff - move python package to new api check-in: 78fca7c39d user: jls@semicomplete.com tags: trunk
01:15
- disable all the dumb stuff (like brp-repack-jars, library stripping, etc) rpmbuild does by default. FINALLY. - improve the spec to behave correctly with the new package api. check-in: ec3280e511 user: jls@semicomplete.com tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to lib/fpm.rb.

1
2
3
4
5
6

7
8
9
10
11
12
13
14
15
16
17
18
require "fpm/namespace"
require "fpm/builder"

require "fpm/package/dir"
require "fpm/package/gem"
require "fpm/package/rpm"

#require "fpm/target/deb"
#require "fpm/target/rpm"
#require "fpm/target/solaris"
#require "fpm/target/puppet"

#require "fpm/source"
#require "fpm/source/dir"
#require "fpm/source/gem"
#require "fpm/source/pear"
#require "fpm/source/python"
#require "fpm/source/rpm"
#require "fpm/source/tar"

<




>












1

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
require "fpm/namespace"


require "fpm/package/dir"
require "fpm/package/gem"
require "fpm/package/rpm"
require "fpm/package/python"
#require "fpm/target/deb"
#require "fpm/target/rpm"
#require "fpm/target/solaris"
#require "fpm/target/puppet"

#require "fpm/source"
#require "fpm/source/dir"
#require "fpm/source/gem"
#require "fpm/source/pear"
#require "fpm/source/python"
#require "fpm/source/rpm"
#require "fpm/source/tar"

Deleted lib/fpm/builder.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
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
require "fileutils"
require "fpm/util"
require "pathname"

class FPM::Builder
  # where is the package's root?
  def root
    @root ||= (@source.root || '.')
  end

  # where the package goes
  def output
    @output ||= begin
      o = @package.default_output
      if o[0,1] == "/"
        o
      else
        File.join(@working_dir, o)
      end
    end
  end

  # things to clean up afterwards
  def garbage
    @garbage ||= []
  end

  attr_reader :paths
  attr_reader :package
  attr_reader :source

  def initialize(settings, paths=[])
    @logger = Logger.new(STDERR)
    @logger.level = $DEBUG ? Logger::DEBUG : Logger::WARN

    @working_dir = Dir.pwd
    root = settings.chdir || '.'
    paths = ['.'] if paths.empty?
    @source  = source_class_for(settings.source_type || 'dir').new(
      paths, root,
      :version => settings.version,
      :iteration => settings.iteration,
      :epoch => settings.epoch,
      :name => settings.package_name,
      :prefix => settings.prefix,
      :suffix => settings.suffix,
      :exclude => settings.exclude,
      :maintainer => settings.maintainer,
      :provides => [],
      :replaces => [],
      :conflicts => [],
      :description => settings.description,
      :url => settings.url,
      :license => settings.license,
      :vendor => settings.vendor,
      :settings => settings.source
    )

    @edit = !!settings.edit

    @paths = paths
    @package = package_class_for(settings.package_type).new(@source,
			:settings => settings.target
		)
    # Append dependencies given from settings (-d flag for fpm)
    @package.dependencies += settings.dependencies if settings.dependencies
    # Append provides given from settings (--provides flag for fpm)
    @package.provides += settings.provides if settings.provides
    @package.replaces += settings.replaces if settings.replaces
    @package.conflicts += settings.conflicts if settings.conflicts
    @package.architecture = settings.architecture if settings.architecture
    @package.category = settings.category if settings.category
    @package.scripts = settings.scripts
    @package.config_files = settings.config_files

    @output = settings.package_path
    @recurse_dependencies = settings.recurse_dependencies
  end # def initialize

  def tar_path
    @tar_path ||= "#{builddir}/data.tar"
  end

  # Assemble the package
  def assemble!
    version_a = [ @source[:version], @package.iteration ].compact
    if @package.epoch
      output.gsub!(/VERSION/, "#{@package.epoch}:" + version_a.join('-'))
    else
      output.gsub!(/VERSION/, version_a.join('-'))
    end
    output.gsub!(/ARCH/, @package.architecture)

    File.delete(output) if File.exists?(output) && !File.directory?(output)

    make_builddir!

    ::Dir.chdir root do
      @source.make_tarball!(tar_path, builddir)

      # Hack to unpack before generating the spec, etc.
      # Need to formalize this feature.
      # Perhaps something like @package.prepare
      if @package.respond_to?(:unpack_data_to)
        data_tarball = File.join(builddir, "data.tar.gz")
        Dir.chdir(builddir) do
          FileUtils.mkdir_p(@package.unpack_data_to)
          safesystem("gzip -d #{data_tarball}")
          Dir.chdir(@package.unpack_data_to) do
            @source.root = Dir.pwd
            safesystem("tar -xf #{data_tarball.gsub(/\.gz$/, "")}")
          end
        end
      end

      generate_md5sums if @package.needs_md5sums
      generate_specfile
      edit_specfile if @edit
    end

    ::Dir.chdir(builddir) do
      @package.build!({
        :tarball => tar_path,
        :output => output
      })
    end

    garbage << @source.garbage if @source.respond_to?(:garbage)

    cleanup!
  end # def assemble!

  private
  def builddir
    @builddir ||= File.expand_path(
      "#{Dir.pwd}/build-#{@package.type}-#{File.basename(output)}"
    )
  end

  private
  def make_builddir!
    FileUtils.rm_rf builddir
    garbage << builddir
    FileUtils.mkdir(builddir) if !File.directory?(builddir)
  end

  # TODO: [Jay] make this better.
  private
  def package_class_for(type)
    realtype = FPM::Target.constants.find { |c| c.downcase.to_s == type }
    if !realtype
      valid_types = FPM::Target.constants.collect { |c| c.downcase }
      @logger.fatal("No such package target type #{type.inspect}; " \
                    "Valid types: #{valid_types.join(", ")}")
      raise ArgumentError, "unknown package type #{type.inspect}"
    end

    return FPM::Target.const_get(realtype)
  end

  # TODO: [Jay] make this better.
  private
  def source_class_for(type)
    realtype = FPM::Source::constants.find { |c| c.downcase.to_s == type }
    if !realtype
      valid_types = FPM::Source.constants.collect { |c| c.downcase }
      @logger.fatal("No such package source type #{type.inspect}; " \
                    "Valid types: #{valid_types.join(", ")}")
      raise ArgumentError, "unknown package type #{type.inspect}"
    end

    return FPM::Source.const_get(realtype)
  end

  private
  def cleanup!
    return [] if garbage.empty?
    FileUtils.rm_rf(garbage) && garbage.clear
  end

  private
  def generate_specfile
    @package.generate_specfile(builddir)
  end

  private
  def edit_specfile
    # TODO(sissel): support editing multiple files for targets like
    # puppet which generate multiple manifests.
    editor = ENV['FPM_EDITOR'] || ENV['EDITOR'] || 'vi'
    safesystem("#{editor} '#{package.specfile(builddir)}'")
    unless File.size? package.specfile(builddir)
      puts "Empty specfile.  Aborting."
      exit 1
    end
  end

  private
  def generate_md5sums
    md5sums = checksum(paths)
    File.open("#{builddir}/md5sums", "w") { |f| f.puts md5sums }
    md5sums
  end

  private
  def checksum(paths)
    paths.collect do |path|
      if File.directory? path
       # This should work for both cases where we use -C or not ...
        %x{ find #{path} -type f -print0 | xargs -0 md5sum }.split("\n").collect do |i|
          # Remove leading path and "./" from the md5sum output if present ...
          i = i.split(/\s+/)
          "%s  %s" % [i[0], i[1].sub(/#{path}\//, '').sub(/\.\//, '')]
        end
      elsif File.exists? path
        %x{md5sum "#{path}"}
      else
        next
      end
    end.flatten
  end # def checksum
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




























































































































































































































































































































































































































































Changes to lib/fpm/package.rb.

1

2
3
4
5
6
7
8
require "fpm/namespace"

require "socket" # for Socket.gethostname
require "cabin"
require "tmpdir"

# This class is the parent of all packages.
# If you want to implement an FPM package type, you'll inherit from this.
#

>







1
2
3
4
5
6
7
8
9
require "fpm/namespace"
require "fpm/util"
require "socket" # for Socket.gethostname
require "cabin"
require "tmpdir"

# This class is the parent of all packages.
# If you want to implement an FPM package type, you'll inherit from this.
#

Added lib/fpm/package/pyfpm/__init__.py.



>
1
__all__ = [ "list_dependencies" ]

Added lib/fpm/package/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

Added lib/fpm/package/python.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
require "fpm/namespace"
require "fpm/source"
require "fpm/util"
require "rubygems/package"
require "rubygems"
require "fileutils"
require "tmpdir"
require "json"

class FPM::Source::Python < FPM::Source
  def self.flags(opts, settings)
    settings.source[:python] = "python"
    settings.source[:easy_install] = "easy_install"
    settings.source[:pypi] = "http://pypi.python.org/simple"

    opts.on("--bin PYTHON_BINARY_LOCATION",
            "The path to the python you want to run. Default is 'python'") do |path|
      settings.source[:python] = path
    end

    opts.on("--easyinstall EASY_INSTALL_PATH",
            "The path to your easy_install tool. Default is 'easy_install'") do |path|
      settings.source[:easy_install] = path
    end

    opts.on("--pypi PYPI_SERVER",
            "PyPi Server uri for retrieving packages. Default is 'http://pypi.python.org/simple'") do |pypi|
      settings.source[:pypi] = pypi
    end

    opts.on("--package-prefix PREFIX",
            "Prefix for python packages") do |package_prefix|
      settings.source[:package_prefix] = package_prefix
    end
  end # def flags

  def input(package)
    path_to_package = download_if_necessary(package, version)
    load_package_info(path_to_package)
    install_to_staging(path_to_package)
  end # def input

  def download_if_necessary(package, version=nil)
    path = package
    # If it's a path, assume local build.
    if File.directory?(path) or (File.exists?(path) and File.basename(path) == "setup.py")
      return path
    end

    @logger.info("Trying to download", :package => package)
    @tmpdir = ::Dir.mktmpdir("python-build", ::Dir.pwd)

    if version.nil?
      want_pkg = "#{package}"
    else
      want_pkg = "#{package}==#{version}"
    end

    # TODO(sissel): support a settable path to 'easy_install'
    # TODO(sissel): support a tunable for uthe url to pypi
    safesystem("easy_install", "-i", "http://pypi.python.org/simple",
               "--editable", "-U", "--build-directory", @tmpdir, want_pkg)

    # easy_install will put stuff in @tmpdir/packagename/, so find that:
    #  @tmpdir/somepackage/setup.py
    dirs = ::Dir.glob(File.join(@tmpdir, "*"))
    if dirs.length != 1
      raise "Unexpected directory layout after easy_install. Maybe file a bug? The directory is #{@tmpdir}"
    end
    return dirs.first
  end # def download

  def load_package_info(package_path)
    if File.directory?(setup_py)
      package_path = File.join(setup_py, "setup.py")
    end

    if !File.exists?(setup_py)
      @logger.error("Could not find 'setup.py'", :path => package_path)
      raise "Unable to find python package; tried #{setup_py}"
    end

    if !attributes.include?(:package_name_prefix)
      attributes[:package_name_prefix] = "python"
    end

    pylib = File.expand_path(File.dirname(__FILE__))
    setup_cmd = "env PYTHONPATH=#{pylib} #{self[:settings][:python]} #{setup_py} --command-packages=pyfpm get_metadata"
    output = ::Dir.chdir(File.dirname(setup_py)) { `#{setup_cmd}` }
    puts output
    metadata = JSON.parse(output[/\{.*\}/msx])

    self.architecture = metadata["architecture"]
    self.description = metadata["description"]
    self.license = metadata["license"]
    self.version = metadata["version"]
    self.url = metadata["url"]

    # Sanitize package name.
    # Some PyPI packages can be named 'python-foo', so we don't want to end up
    # with a package named 'python-python-foo'.
    # But we want packages named like 'pythonweb' to be suffixed
    # 'python-pythonweb'.
    self.name = fix_name(metadata["name"])

    self[:dependencies] += metadata["dependencies"].collect do |dep|
      name, cmp, version = dep.split
      name = fix_name(name)
      "#{name} #{cmp} #{version}"
    end
  end # def load_package_info

  def fix_name(name)
    if name.start_with?("python")
      # If the python package is called "python-foo" strip the "python-" part while
      # prepending the package name prefix.
      return [attributes[:package_name_prefix], name.gsub(/^python-/, "")].join("-")
    else
      return [attributes[:package_name_prefix], name].join("-")
    end
  end # def fix_name

  def install_to_staging(package_path)
    dir = File.dirname(package_path)

    # Some setup.py's assume $PWD == current directory of setup.py, so let's
    # chdir first.
    ::Dir.chdir(dir) do
      # TODO(sissel): Make the path to 'python' tunable
      # TODO(sissel): Respect '--prefix' somewhow from the caller?
      safesystem("python", "setup.py", "install", "--prefix", staging_path)
    end
    clean
  end # def make_tarball!

  def clean
    FileUtils.rm_r(@tmpdir)
  end # def clean
end # class FPM::Source::Python

Deleted lib/fpm/source.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/util"

# Abstract class for a "thing to build a package from"
class FPM::Source
  # standard package metadata
  %w(
    name
    version
    iteration
    architecture
    maintainer
    category
    url
    description
    license
    vendor
  ).each do |attr|
    attr = :"#{attr}"
    define_method(attr) { self[attr] }
    define_method(:"#{attr}=") { |v| self[attr] = v}
  end

  def initialize
    @logger = Logger.new(STDERR)
    @logger.level = $DEBUG ? Logger::DEBUG : Logger::WARN
  end # def initialize

  # Add a new argument to this source
  # The effect of this is specific to the source's implementation,
  # but in general this means you are adding something to this source.
  def <<(arg)
    raise NotImplementedError.new
  end # def <<

end # class FPM::Source
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<








































































Deleted lib/fpm/source/dir.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
require "backports" # gem backports
require "fileutils"
require "fpm/util"

class FPM::Source::Dir < FPM::Source
  def get_metadata
    self[:name] = File.basename(File.expand_path(root))
  end

  def make_tarball!(tar_path, builddir)
    if self[:prefix]
      # Trim leading '/' from prefix
      self[:prefix] = self[:prefix][1..-1] if self[:prefix] =~ /^\//

      # Prefix all files with a path if given.
      @paths.each do |path|
        # Trim @root (--chdir)
        if @root != "." and path.start_with?(@root)
          path = path[@root.size .. -1]
        end

        # Copy to self[:prefix] (aka --prefix)
        if File.directory?(path)
          # Turn 'path' into 'path/' so rsync copies it properly.
          path = "#{path}/" if path[-1,1] != "/"
          dest = "#{builddir}/tarbuild/#{self[:prefix]}/#{path}"
        else
          dest = "#{builddir}/tarbuild/#{self[:prefix]}/#{File.dirname(path)}"
        end

        ::FileUtils.mkdir_p(dest)
        rsync = ["rsync", "-a", path, dest]
        p rsync if $DEBUG
        safesystem(*rsync)

        # FileUtils.cp_r is pretty silly about how it copies files in some
        # cases (funky permissions, etc)
        # Use rsync instead..
        #FileUtils.cp_r(path, dest)
      end

      # Prefix paths with 'prefix' if necessary.
      if self[:prefix]
        @paths = @paths.collect { |p| File.join("/", self[:prefix], p) }
      end

      ::Dir.chdir("#{builddir}/tarbuild") do
        safesystem("ls #{builddir}/tarbuild") if $DEBUG
        tar(tar_path, ".")
      end
    else
      tar(tar_path, paths)
    end

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




















































































































Deleted lib/fpm/source/gem.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
require "fpm/namespace"
require "fpm/source"
require "rubygems/package"
require "rubygems"
require "fileutils"
require "fpm/util"

class FPM::Source::Gem < FPM::Source
  def self.flags(opts, settings)
    settings.source[:gem] = "gem"

    opts.on("--bin-path DIRECTORY",
            "The directory to install gem executables") do |path|
      settings.source[:bin_path] = path
    end
    opts.on("--package-prefix PREFIX",
            "Prefix for gem packages") do |package_prefix|
      settings.source[:package_prefix] = package_prefix
    end

    opts.on("--gem PATH_TO_GEM",
            "The path to the 'gem' tool (defaults to 'gem' and searches " \
            "your $PATH)") do |path|
      settings.source[:gem] = path
    end
  end # def flags

  def get_source(params)
    gem = @paths.first
    looks_like_name_re = /^[A-Za-z0-9_-]+$/
    if !File.exists?(gem)
      if gem =~ looks_like_name_re
        download(gem, params[:version])
      else
        raise "Path '#{gem}' is not a file and does not appear to be the name of a rubygem."
      end
    end
  end # def get_source

  def can_recurse_dependencies
    true
  end

  def download(gem_name, version=nil)
    # This code mostly mutated from rubygem's fetch_command.rb
    # Code use permissible by rubygems's "GPL or these conditions below"
    # http://rubygems.rubyforge.org/rubygems-update/LICENSE_txt.html

    puts "Trying to download #{gem_name} (version=#{version || 'latest'})"
    dep = ::Gem::Dependency.new gem_name, version
    # How to handle prerelease? Some extra magic options?
    #dep.prerelease = options[:prerelease]

    if ::Gem::SpecFetcher.fetcher.respond_to?(:fetch_with_errors)
      specs_and_sources, errors =
        ::Gem::SpecFetcher.fetcher.fetch_with_errors(dep, true, true, false)
    else
      specs_and_sources =
        ::Gem::SpecFetcher.fetcher.fetch(dep, true)
      errors = "???"
    end
    spec, source_uri = specs_and_sources.sort_by { |s,| s.version }.last

    if spec.nil? then
      raise "Invalid gem? Name: #{gem_name}, Version: #{version}, Errors: #{errors}"
    end

    path = ::Gem::RemoteFetcher.fetcher.download spec, source_uri
    FileUtils.mv path, spec.file_name
    @paths = [spec.file_name]
  end

  def get_metadata
    File.open(@paths.first, 'r') do |f|
      ::Gem::Package.open(f, 'r') do |gem|
        spec = gem.metadata
        %w(
          description
          license
          summary
          version
        ).each do |field|
          self[field.to_sym] = spec.send(field) rescue "unknown"
        end

        if self[:settings][:package_prefix]
          self[:package_prefix] = self[:settings][:package_prefix]
        else
          self[:package_prefix] = "rubygem"
        end
        self[:name] = "#{self[:package_prefix]}#{self[:suffix]}-#{spec.name}"
        self[:maintainer] = spec.author
        self[:url] = spec.homepage

        # TODO [Jay]: this will be different for different
        # package managers.  Need to decide how to handle this.
        self[:category] = 'Languages/Development/Ruby'

        # if the gemspec has extensions defined, then this should be a 'native' arch.
        if !spec.extensions.empty?
          self[:architecture] = "native"
        else
          self[:architecture] = "all"
        end

        # make sure we have a description
        descriptions = [ self[:description], self[:summary], "#{spec.name} - no description given" ]
        self[:description] = descriptions.detect { |d| !(d.nil? or d.strip.empty?) }

        self[:dependencies] = []
        spec.runtime_dependencies.map do |dep|
          # rubygems 1.3.5 doesn't have 'Gem::Dependency#requirement'
          if dep.respond_to?(:requirement)
            reqs = dep.requirement.to_s.gsub(/,/, '')
          else
            reqs = dep.version_requirements
          end

          # Some reqs can be ">= a, < b" versions, let's handle that.
          reqs.to_s.split(/, */).each do |req|
            self[:dependencies] << "#{self[:package_prefix]}#{self[:suffix]}-#{dep.name} #{req}"
          end
        end # runtime_dependencies
      end # ::Gem::Package
    end # File.open (the gem)
  end # def get_metadata

  def make_tarball!(tar_path, builddir)
    tmpdir = "#{tar_path}.dir"
    gem = @paths.first
    if self[:prefix]
      installdir = "#{tmpdir}/#{self[:prefix]}"
      # TODO(sissel): Overwriting @paths is bad mojo and confusing...
      # Maybe we shouldn't?
      @paths = [ self[:prefix] ]
    else
      installdir = File.join(tmpdir, ::Gem::dir)
      @paths = [ ::Gem::dir ]
    end

    ::FileUtils.mkdir_p(installdir)
    args = [self[:settings][:gem], "install", "--quiet", "--no-ri", "--no-rdoc",
       "--install-dir", installdir, "--ignore-dependencies", "-E"]
    if self[:settings][:bin_path]
      tmp_bin_path = File.join(tmpdir, self[:settings][:bin_path])
      args += ["--bindir", tmp_bin_path]
      @paths << self[:settings][:bin_path]
      FileUtils.mkdir_p(tmp_bin_path) # Fixes #27
    end

    args << gem
    safesystem(*args)

    # make paths relative  (/foo becomes ./foo)
    tar(tar_path, @paths.collect {|p| ".#{p}"}, tmpdir)
    FileUtils.rm_r(tmpdir)

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

end # class FPM::Source::Gem
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




































































































































































































































































































































Deleted lib/fpm/source/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
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<








































































Deleted lib/fpm/source/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
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


















































































Deleted lib/fpm/source/python.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
require "fpm/namespace"
require "fpm/source"
require "fpm/util"
require "rubygems/package"
require "rubygems"
require "fileutils"
require "tmpdir"
require "json"

class FPM::Source::Python < FPM::Source
  def self.flags(opts, settings)
    settings.source[:python] = "python"
    settings.source[:easy_install] = "easy_install"
    settings.source[:pypi] ="http://pypi.python.org/simple"

    opts.on("--bin PYTHON_BINARY_LOCATION",
            "The path to the python you want to run. Default is 'python'") do |path|
      settings.source[:python] = path
    end

    opts.on("--easyinstall EASY_INSTALL_PATH",
            "The path to your easy_install tool. Default is 'easy_install'") do |path|
      settings.source[:easy_install] = path
    end

    opts.on("--pypi PYPI_SERVER",
            "PyPi Server uri for retrieving packages. Default is 'http://pypi.python.org/simple'") do |pypi|
      settings.source[:pypi] = pypi
    end

    opts.on("--package-prefix PREFIX",
            "Prefix for python packages") do |package_prefix|
      settings.source[:package_prefix] = package_prefix
    end
  end # def flags

  def get_source(params)
    package = @paths.first
    if ["setup.py", "."].include?(package)
      # Assume we're building from an existing python package.
      # Source already acquired, nothing to do!
      return
    end

    if !File.exists?(package)
      download(package, params[:version])
    else
      @paths = [ File.expand_path(package) ]
    end
  end # def get_source

  def download(package, version=nil)
    puts "Trying to download #{package} (using: #{self[:settings][:easy_install]})"
    @tmpdir = ::Dir.mktmpdir("python-build", ::Dir.pwd)

    if version.nil?
      want_pkg = "#{package}"
    else
      want_pkg = "#{package}==#{version}"
    end

    safesystem(self[:settings][:easy_install], "-i", self[:settings][:pypi],
               "--editable", "-U", "--build-directory", @tmpdir, want_pkg)

    # easy_install will put stuff in @tmpdir/packagename/, flatten that.
    #  That is, we want @tmpdir/setup.py, and start with
    #  @tmpdir/somepackage/setup.py
    dirs = ::Dir.glob(File.join(@tmpdir, "*"))
    if dirs.length != 1
      raise "Unexpected directory layout after easy_install. Maybe file a bug? The directory is #{@tmpdir}"
    end
    @paths = dirs
  end # def download

  def get_metadata
    setup_py = @paths.first
    if File.directory?(setup_py)
      setup_py = File.join(setup_py, "setup.py")
      @paths = [setup_py]
    end

    if !File.exists?(setup_py)
      raise "Unable to find python package; tried #{setup_py}"
    end

    pylib = File.expand_path(File.dirname(__FILE__))
    setup_cmd = "env PYTHONPATH=#{pylib} #{self[:settings][:python]} #{setup_py} --command-packages=pyfpm get_metadata"
    output = ::Dir.chdir(File.dirname(setup_py)) { `#{setup_cmd}` }
    puts output
    metadata = JSON.parse(output[/\{.*\}/msx])
    #p metadata

    if self[:settings][:package_prefix]
      self[:package_prefix] = self[:settings][:package_prefix]
    else
      self[:package_prefix] = "python"
    end

    self[:architecture] = metadata["architecture"]
    self[:description] = metadata["description"]
    self[:license] = metadata["license"]
    self[:version] = metadata["version"]
    self[:url] = metadata["url"]

    # Sanitize package name.
    # Some PyPI packages can be named 'python-foo', so we don't want to end up
    # with a package named 'python-python-foo'.
    # But we want packages named like 'pythonweb' to be suffixed
    # 'python-pythonweb'.
    if metadata["name"].start_with? "#{self[:package_prefix]}-"
      self[:name] = metadata["name"]
    else
      self[:name] = "#{self[:package_prefix]}#{self[:suffix]}-#{metadata["name"]}"
    end

    self[:dependencies] = metadata["dependencies"].collect do |dep|
      name, cmp, version = dep.split
      if name.start_with? "#{self[:package_prefix]}-"
        name.gsub!(/^#{self[:package_prefix]}-/, "")
      end
      "#{self[:package_prefix]}#{self[:suffix]}-#{name} #{cmp} #{version}"
    end
  end # def get_metadata

  def make_tarball!(tar_path, builddir)
    setup_py = @paths.first
    dir = File.dirname(setup_py)

    # Some setup.py's assume $PWD == current directory of setup.py, so let's
    # chdir first.
    ::Dir.chdir(dir) do
      safesystem(self[:settings][:python], "setup.py", "bdist")
    end

    dist_tar = ::Dir.glob(File.join(dir, "dist", "*.tar.gz")).first
    puts "Found dist tar: #{dist_tar}"
    puts "Copying to #{tar_path}"

    @paths = [ "." ]

    safesystem("cp", dist_tar, "#{tar_path}.gz")
  end # def make_tarball!

  def garbage
    trash = []
    trash << @tmpdir if @tmpdir
    return trash
  end # def garbage

end # class FPM::Source::Python
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<












































































































































































































































































































Deleted lib/fpm/source/rpm.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
require "fpm/source"
require "fpm/util"

class FPM::Source::RPM < FPM::Source
  def get_metadata
    @rpm = @paths.first
    self[:name] = %x{rpm -q --qf '%{name}' -p #{@rpm}}.chomp

    self[:version] = %x{rpm -q --qf '%{version}' -p #{@rpm}}.chomp
    self[:iteration] = %x{rpm -q --qf '%{release}' -p #{@rpm}}.chomp
    self[:summary] = %x{rpm -q --qf '%{summary}' -p #{@rpm}}.chomp
    #self[:description] = %x{rpm -q --qf '%{description}' -p #{@rpm}}
    self[:dependencies] = %x{rpm -qRp #{@rpm}}.split("\n")\
      .collect { |line| line.strip }

    @paths = %x{rpm -qlp #{@rpm}}.split("\n")
  end

  def make_tarball!(tar_path, builddir)
    tmpdir = "#{tar_path}.dir"
    ::Dir.mkdir(tmpdir)
    safesystem("rpm2cpio #{@rpm} | (cd #{tmpdir}; cpio -i --make-directories)")
    tar(tar_path, ".", tmpdir)
    @paths = ["."]
    # TODO(sissel): Make a helper method.
    safesystem(*["gzip", "-f", tar_path])
  end
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
























































Deleted lib/fpm/source/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
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<