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

⌈⌋ ⎇ branch:  cross package maker


Check-in [844cac32bc]

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

Overview
Comment:Merge branch 'master' of https://github.com/jordansissel/fpm
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 844cac32bc77fef78bb2d01ead57dbacf8a5a298
User & Date: jls@semicomplete.com 2011-03-21 22:29:38
Context
2011-03-29
18:06
- Compensate for some gems or rubygems versions not having 'license' or possibly other fields. Assume "unknown" check-in: 7a6f3fe4bc user: jls@semicomplete.com tags: trunk
2011-03-21
22:29
Merge branch 'master' of https://github.com/jordansissel/fpm check-in: 844cac32bc user: jls@semicomplete.com tags: trunk
18:48
- version bump for dependency fix check-in: beae8d3a63 user: jls@semicomplete.com tags: trunk
2011-02-11
02:56
Merge branch 'master' of https://github.com/jordansissel/fpm check-in: e6c3399ae7 user: jls@semicomplete.com tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to README.md.

56
57
58
59
60
61
62











63
64
65
66
* node packages (npm)

Targets:

* deb
* rpm












## Other Documentation

[See the wiki for more docs](https://github.com/jordansissel/fpm/wiki)








>
>
>
>
>
>
>
>
>
>
>




56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
* node packages (npm)

Targets:

* deb
* rpm

## Something broken?

FPM lacks automated testing (though I have that planned).

To compensate for lack of automated testing, should you find any bugs that
would prevent you from using fpm yourself, please let me know (file a ticket,
find me on IRC, email me, etc) and I'll fix it as quickly as I can (usually
blocker bugs get fixed within a few minutes of me finding out about such a bug)

If you have feature requests, feel free to send them my way.

## Other Documentation

[See the wiki for more docs](https://github.com/jordansissel/fpm/wiki)

Changes to bin/fpm.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
require "fpm"

def main(args)
  settings = OpenStruct.new
  settings.exclude = []

  opts = OptionParser.new do |opts|
    opts.banner = "Usage: #{$0} [options]"
    
    # TODO(sissel): Maybe this should be '-o OUTPUT' ?
    opts.on("-p PACKAGEFILE", "--package PACKAGEFILE",
            "The package file to manage") do |path|
      if path =~ /^\//
        settings.package_path = path
      else
        settings.package_path = "#{Dir.pwd}/#{path}"







<
<







10
11
12
13
14
15
16


17
18
19
20
21
22
23
require "fpm"

def main(args)
  settings = OpenStruct.new
  settings.exclude = []

  opts = OptionParser.new do |opts|


    # TODO(sissel): Maybe this should be '-o OUTPUT' ?
    opts.on("-p PACKAGEFILE", "--package PACKAGEFILE",
            "The package file to manage") do |path|
      if path =~ /^\//
        settings.package_path = path
      else
        settings.package_path = "#{Dir.pwd}/#{path}"
78
79
80
81
82
83
84


















85
86
87
88
89
90
91
92
93
94
            "Exclude paths matching pattern (according to tar --exclude)") do |pattern|
      settings.exclude << pattern
    end # -x / --exclude

  end # OptionParser

  opts.parse!(args)



















  builder = FPM::Builder.new(settings, args)
  builder.assemble!
  puts "Created #{builder.output}"

  return 0
end # def main

ret = main(ARGV)
exit(ret != nil ? ret : 0)







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










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
            "Exclude paths matching pattern (according to tar --exclude)") do |pattern|
      settings.exclude << pattern
    end # -x / --exclude

  end # OptionParser

  opts.parse!(args)

  ok = true
  if settings.package_type.nil?
    $stderr.puts "Missing package target type (no -t flag?)"
    ok = false
  end

  if settings.source_type.nil?
    $stderr.puts "Missing package source type (no -s flag?)"
    ok = false
  end

  if !ok
    $stderr.puts "There were errors; see above."
    $stderr.puts 
    $stderr.puts opts.help
    return 1
  end

  builder = FPM::Builder.new(settings, args)
  builder.assemble!
  puts "Created #{builder.output}"

  return 0
end # def main

ret = main(ARGV)
exit(ret != nil ? ret : 0)

Added examples/jruby/Makefile.























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
NAME=jruby
VERSION=1.6.0.RC2
DOWNLOAD=http://jruby.org.s3.amazonaws.com/downloads/$(VERSION)/$(TARBALL)
TARBALL=$(NAME)-bin-$(VERSION).tar.gz
TARDIR=$(NAME)-$(VERSION)

PREFIX=/opt/jruby

.PHONY: default
default: package

.PHONY: clean
clean:
	rm -f $(NAME)-* $(NAME)_* || true
	rm -fr $(TARDIR) || true
	rm -f *.deb

$(TARBALL):
	wget "$(DOWNLOAD)"

$(TARDIR): $(TARBALL)
	tar -zxf $(TARBALL)

.PHONY: package
package: $(TARDIR)
	fpm -s dir -t deb -v $(VERSION) -n $(NAME) -d "sun-java6-bin (>> 0)" \
		-a all -d "sun-java6-jre (>> 0)" --prefix $(PREFIX) -C $(TARDIR) .

Added examples/jruby/README.md.



































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Usage:

    make package

Should make the package. Try installing:

    sudo dpkg -i jruby-1.6.0.RC2-1.all.deb

Now try it:

    % /opt/jruby/bin/jirb 
    >> require "java"
    => true
    >> java.lang.System.out.println("Hello")
    Hello
    => nil

Changes to fpm.gemspec.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Gem::Specification.new do |spec|
  files = []
  dirs = %w{lib bin templates}
  dirs.each do |dir|
    files += Dir["#{dir}/**/*"]
  end

  rev = Time.now.strftime("%Y%m%d%H%M%S")
  spec.name = "fpm"
  spec.version = "0.2.4"
  spec.summary = "fpm - package building and mangling"
  spec.description = "Turn directories into packages. Fix broken packages. Win the package management game."
  spec.add_dependency("json")
  spec.files = files
  spec.require_paths << "lib"
  spec.bindir = "bin"
  spec.executables << "fpm"







<

|







1
2
3
4
5
6
7

8
9
10
11
12
13
14
15
16
Gem::Specification.new do |spec|
  files = []
  dirs = %w{lib bin templates}
  dirs.each do |dir|
    files += Dir["#{dir}/**/*"]
  end


  spec.name = "fpm"
  spec.version = "0.2.9"
  spec.summary = "fpm - package building and mangling"
  spec.description = "Turn directories into packages. Fix broken packages. Win the package management game."
  spec.add_dependency("json")
  spec.files = files
  spec.require_paths << "lib"
  spec.bindir = "bin"
  spec.executables << "fpm"

Changes to lib/fpm/builder.rb.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
require 'fileutils'
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.start_with? '/'
        o
      else 
        File.join(@working_dir, o)
      end
    end
  end












|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
require 'fileutils'
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

Changes to lib/fpm/package.rb.

41
42
43
44
45
46
47


48
49
50
51
52
53
54
55
  # a summary or description of the package
  attr_accessor :summary
  
  def initialize(source)
    @source = source

    @name = source[:name] # || fail


    @version = source[:version] # || fail

    @dependencies = source[:dependencies] || []
    # Iteration can be nil. If nil, the fpm package implementation is expected
    # to handle any default value that should be instead.
    @iteration = source[:iteration] 
    @url = source[:url] || "http://nourlgiven.example.com/no/url/given"
    @category = source[:category] || "default"







>
>
|







41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
  # a summary or description of the package
  attr_accessor :summary
  
  def initialize(source)
    @source = source

    @name = source[:name] # || fail

    # Default version is 1.0 in case nobody told us a specific version.
    @version = source[:version] || "1.0"

    @dependencies = source[:dependencies] || []
    # Iteration can be nil. If nil, the fpm package implementation is expected
    # to handle any default value that should be instead.
    @iteration = source[:iteration] 
    @url = source[:url] || "http://nourlgiven.example.com/no/url/given"
    @category = source[:category] || "default"

Changes to 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
require "fpm/source"
require "fileutils"

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]

      self[:prefix] = self[:prefix][1..-1] if self[:prefix] =~ /^\//

      # Prefix all files with a path.
      ::FileUtils.mkdir_p(self[:prefix])
      paths.each do |path|
        # Trim @root (--chdir)
        path = path[@root.size .. -1] if path.start_with?(@root)

        # Copy to self[:prefix] (aka --prefix)
        if File.directory?(path)
          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
        system(*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






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

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










>

>
|
<
|




















>
>
>
>
>













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

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)
        path = path[@root.size .. -1] if path.start_with?(@root)

        # Copy to self[:prefix] (aka --prefix)
        if File.directory?(path)
          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
        system(*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
        system("ls #{builddir}/tarbuild")
        tar(tar_path, ".")
      end
    else
      tar(tar_path, paths)
    end

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

Changes to lib/fpm/target/deb.rb.

23
24
25
26
27
28
29








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

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

  end # def build








end # class FPM::Deb








>
>
>
>
>
>
>
>


23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
    # create debian-binary
    File.open("debian-binary", "w") { |f| f.puts "2.0" }

    # pack up the .deb
    system("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
end # class FPM::Deb

Changes to lib/fpm/target/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
require "fpm/package"

class FPM::Target::Rpm < FPM::Package
  def specfile(builddir)
    "#{builddir}/#{name}.spec"
  end

  def build!(params)
    raise "No package name given. Can't assemble package" if !@name
    Dir.mkdir("BUILD")
    args = ["rpmbuild", "-ba", 
           "--define", "buildroot #{Dir.pwd}/BUILD",
           "--define", "_topdir #{Dir.pwd}",
           "--define", "_sourcedir #{Dir.pwd}",
           "--define", "_rpmdir #{params[:output]}",
           "#{name}.spec"]
    ret = system(*args)
    if !ret
      raise "rpmbuild failed"





    end

  end
end









|




|



|
>
>
>
>
>




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/package"

class FPM::Target::Rpm < FPM::Package
  def specfile(builddir)
    "#{builddir}/#{name}.spec"
  end

  def build!(params)
    raise "No package name given. Can't assemble package" if !@name
    %w(BUILD RPMS SRPMS SOURCES SPECS).each { |d| Dir.mkdir(d) }
    args = ["rpmbuild", "-ba", 
           "--define", "buildroot #{Dir.pwd}/BUILD",
           "--define", "_topdir #{Dir.pwd}",
           "--define", "_sourcedir #{Dir.pwd}",
           "--define", "_rpmdir #{Dir.pwd}/RPMS",
           "#{name}.spec"]
    ret = system(*args)
    if !ret
      raise "rpmbuild failed (exit code: #{$?.exitstatus})"
    end

    Dir["#{Dir.pwd}/RPMS/**/*.rpm"].each do |path|
      # This should only output one rpm, should we verify this?
      system("mv", path, params[:output])
    end

  end
end

Changes to templates/deb.erb.

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
Package: <%= name %>
Version: <%= version %><%= iteration && '-'+iteration.to_s %>
Architecture: <%= architecture %>
Maintainer: <%= maintainer or "<unknown>" %>
<% if dependencies.size > 0 %>
<%
  properdeps = dependencies.collect do |d|
    # Convert strings 'foo >= bar' to 'foo (>= bar)'
    if d =~ /\(/
      # nothing
    else
      da = d.split(/ +/)



      d = "#{da[0]} (#{da[1]} #{da[2]})"
    end

    # Convert gem ~> X.Y.Z to '>= X.Y.Z' and << X.Y+1.0
    if d =~ /\(~>/
      name, version = d.gsub(/[()~>]/, "").split(/ +/)[0..1]
      nextversion = version.split(".").collect { |v| v.to_i }
      nextversion[1] += 1
      nextversion[2] = 0 
      nextversion = nextversion.join(".")
      ["#{name} (>= #{version})", "#{name} (<< #{nextversion})"]
    else
      d
    end
  end
%>Depends: <%= properdeps.flatten.join(", ") %>
<% end %>
Standards-Version: 3.9.1
Section: <%= category or "unknown" %> 
Priority: extra
Homepage: <%= url or "http://nourlgiven.example.com/" %>
Description: <%= summary or "no summary given" %>
  <%= summary or "no description given"%>






|

|


|
>
>
>
|



|
|






|










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
Package: <%= name %>
Version: <%= version %><%= iteration && '-'+iteration.to_s %>
Architecture: <%= architecture %>
Maintainer: <%= maintainer or "<unknown>" %>
<% if dependencies.size > 0 %>
<%
  properdeps = dependencies.collect do |dep|
    # Convert strings 'foo >= bar' to 'foo (>= bar)'
    if dep =~ /\(/
      # nothing
    else
      da = dep.split(/ +/)
      if da.size == 1
        da += [">", "0"]
      end
      dep = "#{da[0]} (#{da[1]} #{da[2]})"
    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 }
      nextversion[1] += 1
      nextversion[2] = 0 
      nextversion = nextversion.join(".")
      ["#{name} (>= #{version})", "#{name} (<< #{nextversion})"]
    else
      dep
    end
  end
%>Depends: <%= properdeps.flatten.join(", ") %>
<% end %>
Standards-Version: 3.9.1
Section: <%= category or "unknown" %> 
Priority: extra
Homepage: <%= url or "http://nourlgiven.example.com/" %>
Description: <%= summary or "no summary given" %>
  <%= summary or "no description given"%>