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

⌈⌋ ⎇ branch:  cross package maker


Check-in [fd4ea6c3d6]

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

Overview
Comment:add doc comments
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: fd4ea6c3d6a98aabb5ff9edd5b55f822f424fdc1
User & Date: jls@semicomplete.com 2012-03-17 23:12:35
Context
2012-03-19
05:55
Add package scripts spec to rpm check-in: 343caae46d user: jls@semicomplete.com tags: trunk
2012-03-17
23:12
add doc comments check-in: fd4ea6c3d6 user: jls@semicomplete.com tags: trunk
23:12
add more default specs check-in: 5013cad036 user: jls@semicomplete.com tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to lib/fpm/package/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
require "fpm/package"
require "backports"
require "fileutils"
require "find"
require "socket"






class FPM::Package::Dir < FPM::Package
  private















  def input(path)
    @logger.debug("Copying", :input => path)
    @logger["method"] = "input"
    ::Dir.chdir(@attributes[:chdir] || ".") do
      if @attributes[:prefix]
        clone(path, File.join(staging_path, @attributes[:prefix]))
      else
        clone(path, staging_path)
      end
    end

    # Set some defaults. This is useful because other package types
    # can include license data from themselves (rpms, gems, etc),
    # but to make sure a simple dir -> rpm works without having
    # to specify a license.
    self.license = "unknown"
    self.vendor = [ENV["USER"], Socket.gethostname].join("@")
  ensure

    @logger.remove("method")
  end # def input


  def output(dir)
    dir = File.expand_path(dir)
    ::Dir.chdir(staging_path) do
      @logger["method"] = "output"
      clone(".", dir)
    end
  ensure






>
>
>
>
>



>
>
>
>
>
>
>
>
>
>
>
>
>
>


















>



>







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 "fpm/package"
require "backports"
require "fileutils"
require "find"
require "socket"

# A directory package.
#
# This class supports both input and output. As a note, 'output' will
# only emit the files, not any metadata. This is an effective way
# to extract another package type.
class FPM::Package::Dir < FPM::Package
  private

  # Add a new path to this package.
  #
  # If the path is a directory, it is copied recursively. The behavior
  # of the copying is modified by the :chdir and :prefix attributes.
  #
  # If :prefix is set, the destination path is prefixed with that value.
  # If :chdir is set, the current directory is changed to that value
  # during the copy.
  #
  # Example: Copy /etc/X11 into this package as /opt/xorg/X11:
  #
  #     package.attributes[:prefix] = "/opt/xorg"
  #     package.attributes[:chdir] = "/etc"
  #     package.input("X11")
  def input(path)
    @logger.debug("Copying", :input => path)
    @logger["method"] = "input"
    ::Dir.chdir(@attributes[:chdir] || ".") do
      if @attributes[:prefix]
        clone(path, File.join(staging_path, @attributes[:prefix]))
      else
        clone(path, staging_path)
      end
    end

    # Set some defaults. This is useful because other package types
    # can include license data from themselves (rpms, gems, etc),
    # but to make sure a simple dir -> rpm works without having
    # to specify a license.
    self.license = "unknown"
    self.vendor = [ENV["USER"], Socket.gethostname].join("@")
  ensure
    # Clean up any logger context we added.
    @logger.remove("method")
  end # def input

  # Output this package to the given directory.
  def output(dir)
    dir = File.expand_path(dir)
    ::Dir.chdir(staging_path) do
      @logger["method"] = "output"
      clone(".", dir)
    end
  ensure
56
57
58
59
60
61
62



63
64
65
66
67
68
69
    Find.find(source).each do |file|
      next if source == file && File.directory?(file) # ignore the directory itself
      target = File.join(destination, file)
      copy(file, target)
    end
  end # def clone




  def copy(source, destination)
    directory = File.dirname(destination)
    if !File.directory?(directory)
      FileUtils.mkdir_p(directory)
    end

    # Create a directory if this path is a directory







>
>
>







77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
    Find.find(source).each do |file|
      next if source == file && File.directory?(file) # ignore the directory itself
      target = File.join(destination, file)
      copy(file, target)
    end
  end # def clone

  # Copy, recursively, from source to destination.
  #
  # Files will be hardlinked if possible, but copied otherwise.
  def copy(source, destination)
    directory = File.dirname(destination)
    if !File.directory?(directory)
      FileUtils.mkdir_p(directory)
    end

    # Create a directory if this path is a directory

Changes to 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
require "fpm/namespace"
require "fpm/package"
require "fileutils"
require "fpm/util"




class FPM::Package::PEAR < FPM::Package
  option "--package-prefix", "PREFIX",
    "Name prefix for python package", :default => "php-pear"








  def input(input_package)
    if !program_in_path?("pear")
      raise ExecutableNotFound.new("pear")
    end

    # Create a temporary config file
    @logger.debug("Creating pear config file")
    config = File.expand_path(build_path("pear.config"))
    installroot = attributes[:prefix] || "/usr/share"
    safesystem("pear", "config-create", staging_path(installroot), config)

    @logger.info("Fetching package information", :package => input_package)
    pear_cmd = "pear -c #{config} remote-info #{input_package}"
    name = %x{#{pear_cmd} | sed -ne '/^Package\s*/s/^Package\s*//p'}.chomp
    self.name = "#{attributes[:pear_package_prefix]}-#{name}"
    self.version = %x{#{pear_cmd} | sed -ne '/^Latest\s*/s/^Latest\s*//p'}.chomp
    self.description  = %x{#{pear_cmd} | sed -ne '/^Summary\s*/s/^Summary\s*//p'}.chomp

    @logger.info("Installing pear package", :package => input_package,
                  :directory => staging_path)
    ::Dir.chdir(staging_path) do
      safesystem("pear", "-c", config, "install", "-n", "-f", "-P", ".",





>
>
>

|
|

>
>
>
>
>
>
>














|







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/package"
require "fileutils"
require "fpm/util"

# This provides PHP PEAR package support.
#
# This provides input support, but not output support.
class FPM::Package::PEAR < FPM::Package
  option "--package-name-prefix", "PREFIX",
    "Name prefix for pear package", :default => "php-pear"

  # Input a PEAR package.
  #
  # The parameter is a PHP PEAR package name.
  #
  # Attributes that affect behavior here:
  # * :prefix - changes the install root, default is /usr/share
  # * :pear_package_name_prefix - changes the 
  def input(input_package)
    if !program_in_path?("pear")
      raise ExecutableNotFound.new("pear")
    end

    # Create a temporary config file
    @logger.debug("Creating pear config file")
    config = File.expand_path(build_path("pear.config"))
    installroot = attributes[:prefix] || "/usr/share"
    safesystem("pear", "config-create", staging_path(installroot), config)

    @logger.info("Fetching package information", :package => input_package)
    pear_cmd = "pear -c #{config} remote-info #{input_package}"
    name = %x{#{pear_cmd} | sed -ne '/^Package\s*/s/^Package\s*//p'}.chomp
    self.name = "#{attributes[:pear_package_name_prefix]}-#{name}"
    self.version = %x{#{pear_cmd} | sed -ne '/^Latest\s*/s/^Latest\s*//p'}.chomp
    self.description  = %x{#{pear_cmd} | sed -ne '/^Summary\s*/s/^Summary\s*//p'}.chomp

    @logger.info("Installing pear package", :package => input_package,
                  :directory => staging_path)
    ::Dir.chdir(staging_path) do
      safesystem("pear", "-c", config, "install", "-n", "-f", "-P", ".",

Changes to lib/fpm/package/python.rb.

1
2
3
4
5
6
7
8
9










10
11
12
13
14
15
16
require "fpm/namespace"
require "fpm/package"
require "fpm/util"
require "rubygems/package"
require "rubygems"
require "fileutils"
require "tmpdir"
require "json"











class FPM::Package::Python < FPM::Package
  # Flags '--foo' will be accessable  as attributes[:python_foo]
  option "--bin", "PYTHON_EXECUTABLE",
    "The path to the python executable you wish to run.", :default => "python"
  option "--easyinstall", "EASYINSTALL_EXECUTABLE",
    "The path to the easy_install executable tool", :default => "easy_install"
  option "--pypi", "PYPI_URL",









>
>
>
>
>
>
>
>
>
>







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
require "fpm/namespace"
require "fpm/package"
require "fpm/util"
require "rubygems/package"
require "rubygems"
require "fileutils"
require "tmpdir"
require "json"

# Support for python packages. 
#
# This supports input, but not output.
#
# Example:
#
#     # Download the django python package:
#     pkg = FPM::Package::Python.new
#     pkg.input("Django")
#
class FPM::Package::Python < FPM::Package
  # Flags '--foo' will be accessable  as attributes[:python_foo]
  option "--bin", "PYTHON_EXECUTABLE",
    "The path to the python executable you wish to run.", :default => "python"
  option "--easyinstall", "EASYINSTALL_EXECUTABLE",
    "The path to the easy_install executable tool", :default => "easy_install"
  option "--pypi", "PYPI_URL",
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
    "name with.", :default => "python"
  option "--fix-name", :flag, "Should the target package name be prefixed?",
    :default => true
  option "--fix-dependencies", :flag, "Should the package dependencies be " \
    "prefixed?", :default => true











  def input(package)
    path_to_package = download_if_necessary(package, version)

    if File.directory?(path_to_package)
      setup_py = File.join(path_to_package, "setup.py")
    else
      setup_py = path_to_package
    end

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

    load_package_info(setup_py)
    install_to_staging(setup_py)
  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)







>
>
>
>
>
>
>
>
>


















>
>

>
>







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
    "name with.", :default => "python"
  option "--fix-name", :flag, "Should the target package name be prefixed?",
    :default => true
  option "--fix-dependencies", :flag, "Should the package dependencies be " \
    "prefixed?", :default => true


  private

  # Input a package.
  #
  # The 'package' can be any of:
  #
  # * A name of a package on pypi (ie; easy_install some-package)
  # * The path to a directory containing setup.py
  # * The path to a setup.py
  def input(package)
    path_to_package = download_if_necessary(package, version)

    if File.directory?(path_to_package)
      setup_py = File.join(path_to_package, "setup.py")
    else
      setup_py = path_to_package
    end

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

    load_package_info(setup_py)
    install_to_staging(setup_py)
  end # def input

  # Download the given package if necessary. If version is given, that version
  # will be downloaded, otherwise the latest is fetched.
  def download_if_necessary(package, version=nil)
    # TODO(sissel): this should just be a 'download' method, the 'if_necessary'
    # part should go elsewhere.
    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)
75
76
77
78
79
80
81

82
83
84
85
86
87
88
    dirs = ::Dir.glob(File.join(target, "*"))
    if dirs.length != 1
      raise "Unexpected directory layout after easy_install. Maybe file a bug? The directory is #{build_path}"
    end
    return dirs.first
  end # def download


  def load_package_info(setup_py)
    if !attributes[:python_package_prefix].nil?
      attributes[:python_package_name_prefix] = attributes[:python_package_prefix]
    end

    # Add ./pyfpm/ to the python library path
    pylib = File.expand_path(File.dirname(__FILE__))







>







98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
    dirs = ::Dir.glob(File.join(target, "*"))
    if dirs.length != 1
      raise "Unexpected directory layout after easy_install. Maybe file a bug? The directory is #{build_path}"
    end
    return dirs.first
  end # def download

  # Load the package information like name, version, dependencies.
  def load_package_info(setup_py)
    if !attributes[:python_package_prefix].nil?
      attributes[:python_package_name_prefix] = attributes[:python_package_prefix]
    end

    # Add ./pyfpm/ to the python library path
    pylib = File.expand_path(File.dirname(__FILE__))
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
      # prepending the package name prefix.
      return [attributes[:python_package_name_prefix], name.gsub(/^python-/, "")].join("-")
    else
      return [attributes[:python_package_name_prefix], name].join("-")
    end
  end # def fix_name


  def install_to_staging(setup_py)
    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

      # Install with a specific prefix if requested
      if attributes[:prefix]
        safesystem(attributes[:python_bin], "setup.py", "install", "--prefix",
                   File.join(staging_path, attributes[:prefix]))
      else
        # Otherwise set the root in staging_path
        # TODO(sissel): there needs to be a way to force 
        safesystem(attributes[:python_bin], "setup.py", "install", "--root",
                   staging_path)
      end
    end
  end # def install_to_staging


end # class FPM::Package::Python







>



















>
>

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
      # prepending the package name prefix.
      return [attributes[:python_package_name_prefix], name.gsub(/^python-/, "")].join("-")
    else
      return [attributes[:python_package_name_prefix], name].join("-")
    end
  end # def fix_name

  # Install this package to the staging directory
  def install_to_staging(setup_py)
    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

      # Install with a specific prefix if requested
      if attributes[:prefix]
        safesystem(attributes[:python_bin], "setup.py", "install", "--prefix",
                   File.join(staging_path, attributes[:prefix]))
      else
        # Otherwise set the root in staging_path
        # TODO(sissel): there needs to be a way to force 
        safesystem(attributes[:python_bin], "setup.py", "install", "--root",
                   staging_path)
      end
    end
  end # def install_to_staging

  public(:input)
end # class FPM::Package::Python