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

⌈⌋ ⎇ branch:  cross package maker


Check-in [c436022fd2]

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

Overview
Comment:Merge remote-tracking branch 'upstream/master'
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: c436022fd2426206d7959fb06f9b6dbc968b0c2c
User & Date: ben@tisdall.org.uk 2014-01-09 20:17:07
Context
2014-01-20
11:34
Merge remote-tracking branch 'upstream/master' check-in: 039c34b96e user: ben@tisdall.org.uk tags: trunk
2014-01-09
20:17
Merge remote-tracking branch 'upstream/master' check-in: c436022fd2 user: ben@tisdall.org.uk tags: trunk
2014-01-08
20:08
Don't put gem_bin_path in packages if there's no need. This should fix #612. check-in: b7ecd0fed7 user: jls@semicomplete.com tags: trunk
2013-09-25
14:07
Merge remote-tracking branch 'upstream/master' Conflicts: templates/rpm.erb check-in: ce682e6ea9 user: astephens@drh.net tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to CHANGELIST.



1





2






3








































4
5
6
7
8
9
10


?.?.? (???)





  - cpan: catch more cases of perllocal.pod and delete them (#510)






  - python: safer gathering of python module dependencies.









































0.4.42 (July 23, 2013)
  - dir: make source=destination mappings behave the same way 'rsync -a' does
    with respect to source and destination paths.

0.4.41 (July 17, 2013)
  - cpan: handle cases where modules don't specify a license
>
>
|
>
>
>
>
>
|
>
>
>
>
>
>

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







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
1.0.1 (December 7, 2013)
  - deb: Correctly handle --config-files given with a leading / (Jordan Sissel)

1.0.0 (December 5, 2013)
  - Config file of flags is now supported. Searches for $HOME/.fpm and
    $PWD/.fpm. If both exist, $HOME is loaded first so $PWD can override.
    (Pranay Kanwar)
  - pkgin: Basic support for SmartOS/pkgsrc's pkgin format. (#567, Brian Akins)
  - cpan: catch more cases of perllocal.pod and delete them 
    (#510, Jordan Sissel)
  - cpan: Correctly support module version selection (#518, Matt Sharpe)
  - cpan: include builddeps in PERL5LIB when running cpan tests 
    (#500, Matt Sharpe)
  - cpan: Avoid old system perl modules when doing module builds 
    (#442, #513; Matt Sharpe)
  - python: safer gathering of python module dependencies.
  - python: better handling of unicode strings in python package metadata
    (#575, Bruno Renié)
  - cpan: Support 'http_proxy' env var. (#491, Patrick Cable)
  - deb: --deb-user and --deb-group both default to 'root' now 
    (#504, Pranay Kanwar)
  - deb: convert '>' to '>>' in deb version constraints 
    (#503, #439, Pranay Kanwar)
  - deb: Warn if epoch is set. Just so you know what's going on, since
    the default filename doesn't include the epoch. (#502, Pranay Kanwar)
  - deb,rpm: --config-files is now recursive if you give it a directory.
    This seems to be the most expected behavior by users. 
    (#171, #506; Pranay Kanwar)
  - dir: Respect -C when using path mapping (#498, #507; Pranay Kanwar)
  - rpm: Add --rpm-ignore-iteration-in-dependencies to let you to depend
    on any release (aka iteration) of the same version of a package.
    (#364, #508; Pranay Kanwar)
  - dir: Handle copying of special files when possible 
    (#347, #511, #539, #561; Pranay Kanwar)
  - rpm: Don't mistake symlinks as actual directories (#521, Nathan Huff)
  - npm: Choose an alternate npm registry with --npm-registry (#445, #524; 
    Matt Sharpe)
  - cpan: Choose an alternate cpan server with --cpan-mirror. Additionally, you
    can use --cpan-mirror-only to only use this mirror for metadata queries.
    (#524, Matt Sharpe)
  - deb: Fix broken --deb-changelog flag (#543, #544; Tray Torrance)
  - deb: When --deb-upstart is given, automatically create an upstart-sysv
    symlink /etc/init.d/<name> to /lib/init/upstart-job (#545, Igor Galić)
  - rpm: Fix bug when generating spec file listings on files with strange
    characters in the names. (#547, Chris Chandler)
  - dir: Fix bug where the new directory mapping feature would cause you not
    to be able to select files with '=' in the name for packaging. 
    (#556, #554; Pranay Kanwar)
  - python: Fix some unicode string issues in package metadata 
    (#575, Bruno Renié)
  - gem-rpm: Now respects the --gem-package-name-prefix when generating the
    'rubygem(name)' provides statement (#585, Stepan Stipl)
  - deb: Downcase and replace underscores with dashes in 'provides' list.
    (#591, Eric Connell)
  - deb: Fix a lintian complaint about md5sums permissions (#593, Sam Crang)
  - cpan: Modules with 'MYMETA' files are now supported (#573, Michael Donlon)

0.4.42 (July 23, 2013)
  - dir: make source=destination mappings behave the same way 'rsync -a' does
    with respect to source and destination paths.

0.4.41 (July 17, 2013)
  - cpan: handle cases where modules don't specify a license

Changes to Makefile.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
GEMSPEC=$(shell ls *.gemspec)
VERSION=$(shell awk -F\" '/VERSION =/ { print $$2 }' lib/fpm/version.rb)
NAME=$(shell awk -F\" '/spec.name/ { print $$2 }' $(GEMSPEC))
GEM=$(NAME)-$(VERSION).gem

.PHONY: test
test:
	rm -rf .yardoc
	sh notify-failure.sh ruby test/all.rb

.PHONY: testloop
testloop:
	while true; do \
		$(MAKE) test; \
		$(MAKE) wait-for-changes; \
	done








|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
GEMSPEC=$(shell ls *.gemspec)
VERSION=$(shell awk -F\" '/VERSION =/ { print $$2 }' lib/fpm/version.rb)
NAME=$(shell awk -F\" '/spec.name/ { print $$2 }' $(GEMSPEC))
GEM=$(NAME)-$(VERSION).gem

.PHONY: test
test:
	rm -rf .yardoc
	sh notify-failure.sh rspec

.PHONY: testloop
testloop:
	while true; do \
		$(MAKE) test; \
		$(MAKE) wait-for-changes; \
	done

Changes to README.md.

91
92
93
94
95
96
97

98
99
100
101
102
103
104

Sources:

* gem (even autodownloaded for you)
* python modules (autodownload for you)
* pear (also downloads for you)
* directories

* rpm
* deb
* node packages (npm)

Targets:

* deb







>







91
92
93
94
95
96
97
98
99
100
101
102
103
104
105

Sources:

* gem (even autodownloaded for you)
* python modules (autodownload for you)
* pear (also downloads for you)
* directories
* tar(.gz) archives
* rpm
* deb
* node packages (npm)

Targets:

* deb
131
132
133
134
135
136
137
138








































139
140
141
142
  the [mailing list](http://groups.google.com/group/fpm-users).
* If you want to send patches, best way is to fork this repo and send me a pull
  request. If you don't know git, I also accept diff(1) formatted patches -
  whatever is most comfortable for you.
* Want to lurk about and see what others are doing? IRC (#fpm on
  irc.freenode.org) is a good place for this as is the 
  [mailing list](http://groups.google.com/group/fpm-users)









































## More Documentation

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









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




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
  the [mailing list](http://groups.google.com/group/fpm-users).
* If you want to send patches, best way is to fork this repo and send me a pull
  request. If you don't know git, I also accept diff(1) formatted patches -
  whatever is most comfortable for you.
* Want to lurk about and see what others are doing? IRC (#fpm on
  irc.freenode.org) is a good place for this as is the 
  [mailing list](http://groups.google.com/group/fpm-users)

### Contributing by forking from GitHub

First, create a github account if you do not already have one.  Log in to
github and go to [the main fpm github page](https://github.com/jordansissel/fpm).

At the top right, click on the button labeled "Fork".  This will put a forked
copy of the main fpm repo into your account.  Next, clone your account's github
repo of fpm.  For example:

    $ git clone git@github.com:yourusername/fpm.git

If you don't already have the bundler gem installed, install it now:

    $ gem install bundler

Now change to the root of the fpm repo and run:

    $ bundle install

This will install all of the dependencies required for running fpm from source.
Most importantly, you should see the following output from the bundle command
when it lists the fpm gem:

    ...
    Using json (1.8.1) 
    Using fpm (0.4.42) from source at .
    Using hitimes (1.2.1) 
    ...

Next, run make in root of the fpm repo.  If there are any problems (such as
missing dependencies) you should receive an error

At this point, the fpm command should run directly from the code in your cloned
repo.  Now simply make whatever changes you want, commit the code, and push
your commit back to master.

If you think your changes are ready to be merged back to the main fpm repo, you
can generate a pull request on the github website for your repo and send it in
for review.

## More Documentation

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

Changes to Vagrantfile.

16
17
18
19
20
21
22




23


    centos6.vm.box_url = "http://vagrant-jls.objects.dreamhost.com/Debian-6.0.7-amd64-netboot.box"
  end

  config.vm.provision :puppet do |puppet|
    puppet.manifests_path = "test"
    puppet.manifest_file = "vagrant.pp"
  end




end









>
>
>
>
|
>
>
16
17
18
19
20
21
22
23
24
25
26
27
28
29
    centos6.vm.box_url = "http://vagrant-jls.objects.dreamhost.com/Debian-6.0.7-amd64-netboot.box"
  end

  config.vm.provision :puppet do |puppet|
    puppet.manifests_path = "test"
    puppet.manifest_file = "vagrant.pp"
  end

config.vm.define :smartos do |smartos|
    smartos.vm.box = "smartos-base1310-64-virtualbox-20130806.box"
    smartos.vm.box_url = "http://dlc-int.openindiana.org/aszeszo/vagrant/smartos-base1310-64-virtualbox-20130806.box"
  end

end

Changes to lib/fpm.rb.

10
11
12
13
14
15
16

require "fpm/package/cpan"
require "fpm/package/pear"
require "fpm/package/empty"
require "fpm/package/puppet"
require "fpm/package/python"
require "fpm/package/osxpkg"
require "fpm/package/solaris"








>
10
11
12
13
14
15
16
17
require "fpm/package/cpan"
require "fpm/package/pear"
require "fpm/package/empty"
require "fpm/package/puppet"
require "fpm/package/python"
require "fpm/package/osxpkg"
require "fpm/package/solaris"
require "fpm/package/pkgin"

Changes to lib/fpm/command.rb.

48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
    "the package type to use as input (gem, rpm, python, etc)",
    :attribute_name => :input_type
  option "-C", "CHDIR",
    "Change directory to here before searching for files",
    :attribute_name => :chdir
  option "--prefix", "PREFIX",
    "A path to prefix files with when building the target package. This may " \
    "be necessary for all input packages. For example, the 'gem' type will" \
    "prefix with your gem directory automatically."
  option ["-p", "--package"], "OUTPUT", "The package file path to output."
  option ["-f", "--force"], :flag, "Force output even if it will overwrite an " \
    "existing file", :default => false
  option ["-n", "--name"], "NAME", "The name to give to the package"
  option "--verbose", :flag, "Enable verbose output"
  option "--debug", :flag, "Enable debug output"







|







48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
    "the package type to use as input (gem, rpm, python, etc)",
    :attribute_name => :input_type
  option "-C", "CHDIR",
    "Change directory to here before searching for files",
    :attribute_name => :chdir
  option "--prefix", "PREFIX",
    "A path to prefix files with when building the target package. This may " \
    "be necessary for all input packages. For example, the 'gem' type will " \
    "prefix with your gem directory automatically."
  option ["-p", "--package"], "OUTPUT", "The package file path to output."
  option ["-f", "--force"], :flag, "Force output even if it will overwrite an " \
    "existing file", :default => false
  option ["-n", "--name"], "NAME", "The name to give to the package"
  option "--verbose", :flag, "Enable verbose output"
  option "--debug", :flag, "Enable debug output"
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
    :attribute_name => :replaces

  option "--config-files", "CONFIG_FILES",
    "Mark a file in the package as being a config file. This uses 'conffiles'" \
    " in debs and %config in rpm. If you have multiple files to mark as " \
    "configuration files, specify this flag multiple times.",
    :multivalued => true, :attribute_name => :config_files
  option "--directories", "DIRECTORIES", "Mark a directory as being owned " \
    "by the package", :multivalued => true, :attribute_name => :directories
  option ["-a", "--architecture"], "ARCHITECTURE",
    "The architecture name. Usually matches 'uname -m'. For automatic values," \
    " you can use '-a all' or '-a native'. These two strings will be " \
    "translated into the correct value for your platform and target package type."
  option ["-m", "--maintainer"], "MAINTAINER",
    "The maintainer of this package.",







|







99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
    :attribute_name => :replaces

  option "--config-files", "CONFIG_FILES",
    "Mark a file in the package as being a config file. This uses 'conffiles'" \
    " in debs and %config in rpm. If you have multiple files to mark as " \
    "configuration files, specify this flag multiple times.",
    :multivalued => true, :attribute_name => :config_files
  option "--directories", "DIRECTORIES", "Recursively mark a directory as being owned " \
    "by the package", :multivalued => true, :attribute_name => :directories
  option ["-a", "--architecture"], "ARCHITECTURE",
    "The architecture name. Usually matches 'uname -m'. For automatic values," \
    " you can use '-a all' or '-a native'. These two strings will be " \
    "translated into the correct value for your platform and target package type."
  option ["-m", "--maintainer"], "MAINTAINER",
    "The maintainer of this package.",
416
417
418
419
420
421
422
423

424
425
426
427
428

429


430
431
432
433
434
435
436
437
  def run(*args)
    @logger = Cabin::Channel.get
    @logger.subscribe(STDOUT)

    # fpm initialization files, note the order of the following array is
    # important, try .fpm in users home directory first and then the current
    # directory
    rc_files = [File.join(ENV['HOME'],'.fpm'), '.fpm']


    rc_files.each do |rc_file|
      if File.readable? rc_file
        @logger.warn("Loading flags from rc file #{rc_file}")
        File.readlines(rc_file).each do |line|

          Shellwords.shellsplit(line).each do |e|


            ARGV << e
          end
        end
      end
    end

    super(*args)
  end # def run







|
>





>
|
>
>
|







416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
  def run(*args)
    @logger = Cabin::Channel.get
    @logger.subscribe(STDOUT)

    # fpm initialization files, note the order of the following array is
    # important, try .fpm in users home directory first and then the current
    # directory
    rc_files = [ ".fpm" ]
    rc_files << File.join(ENV["HOME"], ".fpm") if ENV["HOME"]

    rc_files.each do |rc_file|
      if File.readable? rc_file
        @logger.warn("Loading flags from rc file #{rc_file}")
        File.readlines(rc_file).each do |line|
          # reverse becasue 'unshift' pushes onto the left side of the array.
          Shellwords.shellsplit(line).reverse.each do |arg|
            # Put '.fpm'-file flags *before* the command line flags
            # so that we the CLI can override the .fpm flags
            ARGV.unshift(arg)
          end
        end
      end
    end

    super(*args)
  end # def run

Changes to lib/fpm/package.rb.

191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207





208
209
210
211
212
213
214
    exclude

    pkg = klass.new
    pkg.cleanup_staging # purge any directories that may have been created by klass.new

    # copy other bits
    ivars = [
      :@architecture, :@attributes, :@category, :@config_files, :@conflicts,
      :@dependencies, :@description, :@epoch, :@iteration, :@license, :@maintainer,
      :@name, :@provides, :@replaces, :@scripts, :@url, :@vendor, :@version,
      :@directories, :@staging_path
    ]
    ivars.each do |ivar|
      #@logger.debug("Copying ivar", :ivar => ivar, :value => instance_variable_get(ivar),
                    #:from => self.type, :to => pkg.type)
      pkg.instance_variable_set(ivar, instance_variable_get(ivar))
    end






    pkg.converted_from(self.class)
    return pkg
  end # def convert

  # This method is invoked on a package when it has been covered to a new
  # package format. The purpose of this method is to do any extra conversion







|









>
>
>
>
>







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
    exclude

    pkg = klass.new
    pkg.cleanup_staging # purge any directories that may have been created by klass.new

    # copy other bits
    ivars = [
      :@architecture, :@category, :@config_files, :@conflicts,
      :@dependencies, :@description, :@epoch, :@iteration, :@license, :@maintainer,
      :@name, :@provides, :@replaces, :@scripts, :@url, :@vendor, :@version,
      :@directories, :@staging_path
    ]
    ivars.each do |ivar|
      #@logger.debug("Copying ivar", :ivar => ivar, :value => instance_variable_get(ivar),
                    #:from => self.type, :to => pkg.type)
      pkg.instance_variable_set(ivar, instance_variable_get(ivar))
    end

    # Attributes are special! We do not want to remove the default values of
    # the destination package type unless their value is specified on the
    # source package object.
    pkg.attributes.merge!(self.attributes)

    pkg.converted_from(self.class)
    return pkg
  end # def convert

  # This method is invoked on a package when it has been covered to a new
  # package format. The purpose of this method is to do any extra conversion
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
    if @attributes.include?(:prefix)
      installdir = staging_path(@attributes[:prefix])
    else
      installdir = staging_path
    end

    Find.find(installdir) do |path|
      match_path = path.sub("#{installdir}/", '')

      attributes[:excludes].each do |wildcard|
        @logger.debug("Checking path against wildcard", :path => match_path, :wildcard => wildcard)

        if File.fnmatch(wildcard, match_path)
          @logger.info("Removing excluded path", :path => match_path, :matches => wildcard)
          FileUtils.remove_entry_secure(path)







|







367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
    if @attributes.include?(:prefix)
      installdir = staging_path(@attributes[:prefix])
    else
      installdir = staging_path
    end

    Find.find(installdir) do |path|
      match_path = path.sub("#{installdir.chomp('/')}/", '')

      attributes[:excludes].each do |wildcard|
        @logger.debug("Checking path against wildcard", :path => match_path, :wildcard => wildcard)

        if File.fnmatch(wildcard, match_path)
          @logger.info("Removing excluded path", :path => match_path, :matches => wildcard)
          FileUtils.remove_entry_secure(path)

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

36
37
38
39
40
41
42



43
44
45

46
47
48
49
50
51
52





53
54
55
56
57
58
59
        #"me know by filing an issue: " \
        #"https://github.com/jordansissel/fpm/issues"
    #end
    #require "ftw" # for http access
    require "net/http"
    require "json"




    result = search(package)
    tarball = download(result, version)
    moduledir = unpack(tarball)


    # Read package metadata (name, version, etc)
    if File.exists?(File.join(moduledir, "META.json"))
      metadata = JSON.parse(File.read(File.join(moduledir, ("META.json"))))
    elsif File.exists?(File.join(moduledir, ("META.yml")))
      require "yaml"
      metadata = YAML.load_file(File.join(moduledir, ("META.yml")))





    else
      raise FPM::InvalidPackageConfiguration, 
        "Could not find package metadata. Checked for META.json and META.yml"
    end
    self.version = metadata["version"]
    self.description = metadata["abstract"]








>
>
>
|
|
|
>







>
>
>
>
>







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
        #"me know by filing an issue: " \
        #"https://github.com/jordansissel/fpm/issues"
    #end
    #require "ftw" # for http access
    require "net/http"
    require "json"

    if (attributes[:cpan_local_module?])
      moduledir = package
    else
      result = search(package)
      tarball = download(result, version)
      moduledir = unpack(tarball)
    end

    # Read package metadata (name, version, etc)
    if File.exists?(File.join(moduledir, "META.json"))
      metadata = JSON.parse(File.read(File.join(moduledir, ("META.json"))))
    elsif File.exists?(File.join(moduledir, ("META.yml")))
      require "yaml"
      metadata = YAML.load_file(File.join(moduledir, ("META.yml")))
    elsif File.exists?(File.join(moduledir, "MYMETA.json"))
      metadata = JSON.parse(File.read(File.join(moduledir, ("MYMETA.json"))))
    elsif File.exists?(File.join(moduledir, ("MYMETA.yml")))
      require "yaml"
      metadata = YAML.load_file(File.join(moduledir, ("MYMETA.yml")))
    else
      raise FPM::InvalidPackageConfiguration, 
        "Could not find package metadata. Checked for META.json and META.yml"
    end
    self.version = metadata["version"]
    self.description = metadata["abstract"]

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

224
225
226
227
228
229
230
231



232
233
234
235
236
237
238
      self.architecture = parse.call("Architecture")
      self.category = parse.call("Section")
      self.license = parse.call("License") || self.license
      self.maintainer = parse.call("Maintainer")
      self.name = parse.call("Package")
      self.url = parse.call("Homepage")
      self.vendor = parse.call("Vendor") || self.vendor
      self.provides = parse.call("Provides") || self.provides




      # The description field is a special flower, parse it that way.
      # The description is the first line as a normal Description field, but also continues
      # on future lines indented by one space, until the end of the file. Blank
      # lines are marked as ' .'
      description = control[/^Description: .*/m].split(": ", 2).last
      self.description = description.gsub(/^ /, "").gsub(/^\.$/, "")







|
>
>
>







224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
      self.architecture = parse.call("Architecture")
      self.category = parse.call("Section")
      self.license = parse.call("License") || self.license
      self.maintainer = parse.call("Maintainer")
      self.name = parse.call("Package")
      self.url = parse.call("Homepage")
      self.vendor = parse.call("Vendor") || self.vendor
      with(parse.call("Provides")) do |provides_str|
        next if provides_str.nil?
        self.provides = provides_str.split(/\s*,\s*/)
      end

      # The description field is a special flower, parse it that way.
      # The description is the first line as a normal Description field, but also continues
      # on future lines indented by one space, until the end of the file. Blank
      # lines are marked as ' .'
      description = control[/^Description: .*/m].split(": ", 2).last
      self.description = description.gsub(/^ /, "").gsub(/^\.$/, "")
296
297
298
299
300
301
302

303
304
305
306
307
308
309

    # unpack the data.tar.{gz,bz2,xz} from the deb package into staging_path
    safesystem("ar p #{package} #{datatar} " \
               "| tar #{compression} -xf - -C #{staging_path}")
  end # def extract_files

  def output(output_path)

    output_check(output_path)
    # Abort if the target path already exists.

    # create 'debian-binary' file, required to make a valid debian package
    File.write(build_path("debian-binary"), "2.0\n")

    # If we are given --deb-shlibs but no --after-install script, we







>







299
300
301
302
303
304
305
306
307
308
309
310
311
312
313

    # unpack the data.tar.{gz,bz2,xz} from the deb package into staging_path
    safesystem("ar p #{package} #{datatar} " \
               "| tar #{compression} -xf - -C #{staging_path}")
  end # def extract_files

  def output(output_path)
    self.provides = self.provides.collect { |p| fix_provides(p) }
    output_check(output_path)
    # Abort if the target path already exists.

    # create 'debian-binary' file, required to make a valid debian package
    File.write(build_path("debian-binary"), "2.0\n")

    # If we are given --deb-shlibs but no --after-install script, we
410
411
412
413
414
415
416




417
418
419
420
421
422
423
    @logger.log("Created deb package", :path => output_path)
  end # def output

  def converted_from(origin)
    self.dependencies = self.dependencies.collect do |dep|
      fix_dependency(dep)
    end.flatten




  end # def converted_from

  def debianize_op(op)
    # Operators in debian packaging are <<, <=, =, >= and >>
    # So any operator like < or > must be replaced
    {:< => "<<", :> => ">>"}[op.to_sym] or op
  end







>
>
>
>







414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
    @logger.log("Created deb package", :path => output_path)
  end # def output

  def converted_from(origin)
    self.dependencies = self.dependencies.collect do |dep|
      fix_dependency(dep)
    end.flatten
    self.provides = self.provides.collect do |provides|
      fix_provides(provides)
    end.flatten
      
  end # def converted_from

  def debianize_op(op)
    # Operators in debian packaging are <<, <=, =, >= and >>
    # So any operator like < or > must be replaced
    {:< => "<<", :> => ">>"}[op.to_sym] or op
  end
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
    if name =~ /[A-Z]/
      @logger.warn("Downcasing dependency '#{name}' because deb packages " \
                   " don't work so good with uppercase names")
      dep = dep.gsub(name_re) { |n| n.downcase }
    end

    if dep.include?("_")
      @logger.warn("Replacing underscores with dashes in '#{dep}' because " \
                   "debs don't like underscores")
      dep = 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]







|







449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
    if name =~ /[A-Z]/
      @logger.warn("Downcasing dependency '#{name}' because deb packages " \
                   " don't work so good with uppercase names")
      dep = dep.gsub(name_re) { |n| n.downcase }
    end

    if dep.include?("_")
      @logger.warn("Replacing dependency underscores with dashes in '#{dep}' because " \
                   "debs don't like underscores")
      dep = 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]
478
479
480
481
482
483
484

















485
486
487
488
489
490
491
      name, version = m[1..2]
      return ["#{name} (>> #{version})"]
    else
      # otherwise the dep is probably fine
      return dep.rstrip
    end
  end # def fix_dependency


















  def control_path(path=nil)
    @control_path ||= build_path("control")
    FileUtils.mkdir(@control_path) if !File.directory?(@control_path)

    if path.nil?
      return @control_path







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







486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
      name, version = m[1..2]
      return ["#{name} (>> #{version})"]
    else
      # otherwise the dep is probably fine
      return dep.rstrip
    end
  end # def fix_dependency

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

    if provides.include?("_")
      @logger.warn("Replacing 'provides' underscores with dashes in '#{provides}' because " \
                   "debs don't like underscores")
      provides = provides.gsub("_", "-")
    end
    return provides.rstrip
  end

  def control_path(path=nil)
    @control_path ||= build_path("control")
    FileUtils.mkdir(@control_path) if !File.directory?(@control_path)

    if path.nil?
      return @control_path
571
572
573
574
575
576
577


578
579
580
581
582
583
584

  def write_conffiles
    return unless config_files.any?

    # scan all conf file paths for files and add them
    allconfigs = []
    config_files.each do |path|


      cfg_path = File.expand_path(path, staging_path)
      begin
        Find.find(cfg_path) do |p|
          allconfigs << p.gsub("#{staging_path}/", '') if File.file? p
        end
      rescue Errno::ENOENT => e
        raise FPM::InvalidPackageConfiguration,







>
>







596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611

  def write_conffiles
    return unless config_files.any?

    # scan all conf file paths for files and add them
    allconfigs = []
    config_files.each do |path|
      # Strip leading /
      path = path[1..-1] if path[0,1] == "/"
      cfg_path = File.expand_path(path, staging_path)
      begin
        Find.find(cfg_path) do |p|
          allconfigs << p.gsub("#{staging_path}/", '') if File.file? p
        end
      rescue Errno::ENOENT => e
        raise FPM::InvalidPackageConfiguration,
627
628
629
630
631
632
633

634
635
636
637
638
639
640
641
642
643
644
645

    if not md5_sums.empty?
      File.open(control_path("md5sums"), "w") do |out|
        md5_sums.each do |path, md5|
          out.puts "#{md5} #{path}"
        end
      end

    end
  end # def write_md5sums

  def to_s(format=nil)
    # Default format if nil
    # git_1.7.9.3-1_amd64.deb
    return super("NAME_FULLVERSION_ARCH.TYPE") if format.nil?
    return super(format)
  end # def to_s

  public(:input, :output, :architecture, :name, :prefix, :converted_from, :to_s)
end # class FPM::Target::Deb







>












654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673

    if not md5_sums.empty?
      File.open(control_path("md5sums"), "w") do |out|
        md5_sums.each do |path, md5|
          out.puts "#{md5} #{path}"
        end
      end
      File.chmod(0644, control_path("md5sums"))
    end
  end # def write_md5sums

  def to_s(format=nil)
    # Default format if nil
    # git_1.7.9.3-1_amd64.deb
    return super("NAME_FULLVERSION_ARCH.TYPE") if format.nil?
    return super(format)
  end # def to_s

  public(:input, :output, :architecture, :name, :prefix, :converted_from, :to_s)
end # class FPM::Target::Deb

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

39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
  def input(path)
    chdir = attributes[:chdir] || "."

    # Support mapping source=dest
    # This mapping should work the same way 'rsync -a' does
    #   Meaning 'rsync -a source dest'
    #   and 'source=dest' in fpm work the same as the above rsync
    if path =~ /.=./
      origin, destination = path.split("=", 2)

      if File.directory?(origin) && origin[-1,1] == "/"
        chdir = chdir == '.' ? origin : File.join(chdir, origin)
        source = "."
      else
        origin_dir = File.dirname(origin)







|







39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
  def input(path)
    chdir = attributes[:chdir] || "."

    # Support mapping source=dest
    # This mapping should work the same way 'rsync -a' does
    #   Meaning 'rsync -a source dest'
    #   and 'source=dest' in fpm work the same as the above rsync
    if path =~ /.=./ && !File.exists?(chdir == '.' ? path : File.join(chdir, path))
      origin, destination = path.split("=", 2)

      if File.directory?(origin) && origin[-1,1] == "/"
        chdir = chdir == '.' ? origin : File.join(chdir, origin)
        source = "."
      else
        origin_dir = File.dirname(origin)

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

182
183
184
185
186
187
188












189
190
191
192
193
194
195
196
197
198
      bin_path = File.join(staging_path, gem_bin)
    end

    args += ["--bindir", bin_path]
    ::FileUtils.mkdir_p(bin_path)
    args << gem_path
    safesystem(*args)












  end # def install_to_staging
  
  # Sanitize package name.
  # This prefixes the package name with 'rubygem' (but depends on the attribute
  # :gem_package_name_prefix
  def fix_name(name)
    return [attributes[:gem_package_name_prefix], name].join("-")
  end # def fix_name
  public(:input, :output)
end # class FPM::Package::Gem







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










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
      bin_path = File.join(staging_path, gem_bin)
    end

    args += ["--bindir", bin_path]
    ::FileUtils.mkdir_p(bin_path)
    args << gem_path
    safesystem(*args)

    # Delete bin_path if it's empty, and any empty parents (#612)
    # Above, we mkdir_p bin_path because rubygems aborts if the parent
    # directory doesn't exist, for example:
    #   ERROR:  While executing gem ... (Errno::ENOENT)
    #       No such file or directory - /tmp/something/weird/bin
    tmp = bin_path
    while ::Dir.entries(tmp).size == 2 || tmp == "/"  # just [ "..", "." ] is an empty directory
      @logger.info("Deleting empty bin_path", :path => tmp)
      ::Dir.rmdir(tmp)
      tmp = File.dirname(tmp)
    end
  end # def install_to_staging
  
  # Sanitize package name.
  # This prefixes the package name with 'rubygem' (but depends on the attribute
  # :gem_package_name_prefix
  def fix_name(name)
    return [attributes[:gem_package_name_prefix], name].join("-")
  end # def fix_name
  public(:input, :output)
end # class FPM::Package::Gem

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

102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
    delete_these = [".depdb", ".depdblock", ".filemap", ".lock", ".channel", "cache", "temp", "download", ".channels", ".registry"]
    Find.find(staging_path) do |path|
      if File.file?(path)
        @logger.info("replacing staging_path in file", :replace_in => path, :staging_path => staging_path)
        begin
          content = File.read(path).gsub(/#{Regexp.escape(staging_path)}/, "")
          File.write(path, content)
        rescue ArgumentError
          @logger.warn("error replacing staging_path in file", :replace_in => path)
        end
      end
      FileUtils.rm_r(path) if delete_these.include?(File.basename(path))
    end

  end # def input
end # class FPM::Package::PEAR







|
|







102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
    delete_these = [".depdb", ".depdblock", ".filemap", ".lock", ".channel", "cache", "temp", "download", ".channels", ".registry"]
    Find.find(staging_path) do |path|
      if File.file?(path)
        @logger.info("replacing staging_path in file", :replace_in => path, :staging_path => staging_path)
        begin
          content = File.read(path).gsub(/#{Regexp.escape(staging_path)}/, "")
          File.write(path, content)
        rescue ArgumentError => e
          @logger.warn("error replacing staging_path in file", :replace_in => path, :error => e)
        end
      end
      FileUtils.rm_r(path) if delete_these.include?(File.basename(path))
    end

  end # def input
end # class FPM::Package::PEAR

Added lib/fpm/package/pkgin.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
class FPM::Package::Pkgin < FPM::Package

  def output(output_path)
    output_check(output_path)

    File.write(build_path("build-info"), `pkg_info -X pkg_install | egrep '^(MACHINE_ARCH|OPSYS|OS_VERSION|PKGTOOLS_VERSION)'`)

    cwd = ::Dir.pwd
    ::Dir.chdir(staging_path)

    files = []
    Find.find(".") do |path|
      stat = File.lstat(path)
      next unless stat.symlink? or stat.file?
      files << path
    end
    ::Dir.chdir(cwd)

    File.write(build_path("packlist"), files.sort.join("\n"))

    File.write(build_path("comment"),  self.description + "\n")

    File.write(build_path("description"), self.description + "\n")

    args = [ "-B", build_path("build-info"), "-c", build_path("comment"), "-d", build_path("description"), "-f", build_path("packlist"), "-I", "/opt/local", "-p", staging_path,  "-U", "#{cwd}/#{name}-#{self.version}-#{iteration}.tgz" ]
    safesystem("pkg_create", *args)

  end

  def iteration
    return @iteration ? @iteration : 1
  end

end

Changes to lib/fpm/package/pyfpm/get_metadata.py.

1
2

3
4
5
6
7
8











9
10
11
12
13
14
15
from distutils.core import Command
import os

import pkg_resources
try:
    import json
except ImportError:
    import simplejson as json













# 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):


>






>
>
>
>
>
>
>
>
>
>
>







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
from distutils.core import Command
import os
import sys
import pkg_resources
try:
    import json
except ImportError:
    import simplejson as json

PY3 = sys.version_info[0] == 3

if PY3:
    def u(s):
        return s
else:
    def u(s):
        if isinstance(u, unicode):
            return u
        return s.decode('utf-8')


# 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):
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

        return deps

    def run(self):
        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 self.distribution.has_ext_modules():







|
|
|







56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72

        return deps

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

        if self.distribution.has_ext_modules():

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

51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
  option "--install-bin", "BIN_PATH", "The path to where python scripts " \
    "should be installed to."
  option "--install-lib", "LIB_PATH", "The path to where python libs " \
    "should be installed to (default depends on your python installation). " \
    "Want to what your target platform is using? Run this: " \
    "python -c 'from distutils.sysconfig import get_python_lib; " \
    "print get_python_lib()'"
  option "--install-data", "DATA_PATH", "The path to where data should be." \
    "installed to. This is equivalent to 'python setup.py --install-data " \
    "DATA_PATH"
  option "--dependencies", :flag, "Include requirements defined in setup.py" \
    " as dependencies.", :default => true
  option "--obey-requirements-txt", :flag, "Use a requirements.txt file" \
    "in the top-level directory of the python package for dependency " \
    "detection.", :default => false

  private

  # Input a package.
  #







|




|







51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
  option "--install-bin", "BIN_PATH", "The path to where python scripts " \
    "should be installed to."
  option "--install-lib", "LIB_PATH", "The path to where python libs " \
    "should be installed to (default depends on your python installation). " \
    "Want to what your target platform is using? Run this: " \
    "python -c 'from distutils.sysconfig import get_python_lib; " \
    "print get_python_lib()'"
  option "--install-data", "DATA_PATH", "The path to where data should be " \
    "installed to. This is equivalent to 'python setup.py --install-data " \
    "DATA_PATH"
  option "--dependencies", :flag, "Include requirements defined in setup.py" \
    " as dependencies.", :default => true
  option "--obey-requirements-txt", :flag, "Use a requirements.txt file " \
    "in the top-level directory of the python package for dependency " \
    "detection.", :default => false

  private

  # Input a package.
  #

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

130
131
132
133
134
135
136

137
138
139


140
141
142
143
144

145
146
147
148
149
150
151
    name = name.gsub("[", "[\\[]")
    name = name.gsub("*", "[*]")
    name = name.gsub("?", "[?]")
    name = name.gsub("%", "[%]")
  end

  def rpm_file_entry(file)

    file = rpm_fix_name(file)
    return file unless attributes[:rpm_use_file_permissions?]



    stat = File.stat(file.gsub(/\"/, '').sub(/^\//,''))
    user = Etc.getpwuid(stat.uid).name
    group = Etc.getgrgid(stat.gid).name
    mode = stat.mode
    return sprintf("%%attr(%o, %s, %s) %s\n", mode & 4095 , user, group, file)

  end


  # Handle any architecture naming conversions.
  # For example, debian calls amd64 what redhat calls x86_64, this
  # method fixes those types of things.
  def architecture







>



>
>
|
|
|
|
|
>







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
    name = name.gsub("[", "[\\[]")
    name = name.gsub("*", "[*]")
    name = name.gsub("?", "[?]")
    name = name.gsub("%", "[%]")
  end

  def rpm_file_entry(file)
    original_file = file
    file = rpm_fix_name(file)
    return file unless attributes[:rpm_use_file_permissions?]

    # Stat the original filename in the relative staging path
    ::Dir.chdir(staging_path) do
      stat = File.stat(original_file.gsub(/\"/, '').sub(/^\//,''))
      user = Etc.getpwuid(stat.uid).name
      group = Etc.getgrgid(stat.gid).name
      mode = stat.mode
      return sprintf("%%attr(%o, %s, %s) %s\n", mode & 4095 , user, group, file)
    end
  end


  # Handle any architecture naming conversions.
  # For example, debian calls amd64 what redhat calls x86_64, this
  # method fixes those types of things.
  def architecture
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
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
        end
      end
      self.dependencies = fixed_deps

      # Convert 'rubygem-foo' provides values to 'rubygem(foo)'
      # since that's what most rpm packagers seem to do.
      self.provides = self.provides.collect do |provides|
        first, remainder = provides.split("-", 2)
        if first == "rubygem"
          name, remainder = remainder.split(" ", 2)
          # yield rubygem(name)...

          "rubygem(#{name})#{remainder ? " #{remainder}" : ""}"
        else
          provides
        end
      end
      self.dependencies = self.dependencies.collect do |dependency|
        first, remainder = dependency.split("-", 2)
        if first == "rubygem"
          name, remainder = remainder.split(" ", 2)
          "rubygem(#{name})#{remainder ? " #{remainder}" : ""}"
        else
          dependency
        end
      end
      #self.provides << "rubygem(#{self.name})"
    end

    # Convert != dependency as Conflict =, as rpm doesn't understand !=
    self.dependencies = self.dependencies.select do |dep|
      name, op, version = dep.split(/\s+/)
      dep_ok = true
      if op == '!='
        self.conflicts << "#{name} = #{version}"
        dep_ok = false
      end
      dep_ok
    end

    # Convert any dashes in version strings to underscores.
    self.dependencies = self.dependencies.collect do |dep|
      name, op, version = dep.split(/\s+/)
      if !version.nil? and version.include?("-")
        @logger.warn("Dependency package '#{name}' version '#{version}' " \
                     "includes dashes, converting to underscores")
        version = version.gsub(/-/, "_")
        "#{name} #{op} #{version}"
      else
        dep
      end
    end

    # if --ignore-iteration-in-dependencies is true convert foo = X, to
    # foo >= X , foo < X+1
    if self.attributes[:rpm_ignore_iteration_in_dependencies?]
      self.dependencies = self.dependencies.collect do |dep|
        name, op, version = dep.split(/\s+/)
        if op == '='
          nextversion = version.split('.').collect { |v| v.to_i }







<
|
<
|
>
|





|
|
|
|




<













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







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
223
224
225













226
227
228
229
230
231
232
        end
      end
      self.dependencies = fixed_deps

      # Convert 'rubygem-foo' provides values to 'rubygem(foo)'
      # since that's what most rpm packagers seem to do.
      self.provides = self.provides.collect do |provides|

        # Tries to match rubygem_prefix [1], gem_name [2] and version [3] if present

        # and return it in rubygem_prefix(gem_name) form
        if name=/^(#{attributes[:gem_package_name_prefix]})-([^\s]+)\s*(.*)$/.match(provides)
          "#{name[1]}(#{name[2]})#{name[3] ? " #{name[3]}" : ""}"
        else
          provides
        end
      end
      self.dependencies = self.dependencies.collect do |dependency|
        # Tries to match rubygem_prefix [1], gem_name [2] and version [3] if present
        # and return it in rubygem_prefix(gem_name) form
        if name=/^(#{attributes[:gem_package_name_prefix]})-([^\s]+)\s*(.*)$/.match(dependency)
          "#{name[1]}(#{name[2]})#{name[3] ? " #{name[3]}" : ""}"
        else
          dependency
        end
      end

    end

    # Convert != dependency as Conflict =, as rpm doesn't understand !=
    self.dependencies = self.dependencies.select do |dep|
      name, op, version = dep.split(/\s+/)
      dep_ok = true
      if op == '!='
        self.conflicts << "#{name} = #{version}"
        dep_ok = false
      end
      dep_ok
    end














    # if --ignore-iteration-in-dependencies is true convert foo = X, to
    # foo >= X , foo < X+1
    if self.attributes[:rpm_ignore_iteration_in_dependencies?]
      self.dependencies = self.dependencies.collect do |dep|
        name, op, version = dep.split(/\s+/)
        if op == '='
          nextversion = version.split('.').collect { |v| v.to_i }

Changes to lib/fpm/util.rb.

1
2
3
4
5
6
7
8
9
10

11
12
13

14
15
16
17
18
19
20
21
require "fpm/namespace"
require "childprocess"
require "ffi"

# Some utility functions
module FPM::Util
  extend FFI::Library
  ffi_lib FFI::Library::LIBC

  # mknod is __xmknod in glibc

  begin
    attach_function :mknod, :mknod, [:string, :uint32, :ulong], :int
  rescue FFI::NotFoundError

    attach_function :mknod, :__xmknod, [:string, :uint32, :ulong], :int
  end

  # Raised if safesystem cannot find the program to run.
  class ExecutableNotFound < StandardError; end

  # Raised if a safesystem program exits nonzero
  class ProcessFailed < StandardError; end









|
>

|

>
|







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/namespace"
require "childprocess"
require "ffi"

# Some utility functions
module FPM::Util
  extend FFI::Library
  ffi_lib FFI::Library::LIBC

  # mknod is __xmknod in glibc a wrapper around mknod to handle
  # various stat struct formats. See bits/stat.h in glibc source
  begin
    attach_function :mknod, :mknod, [:string, :uint, :ulong], :int
  rescue FFI::NotFoundError
    # glibc/io/xmknod.c int __xmknod (int vers, const char *path, mode_t mode, dev_t *dev)
    attach_function :xmknod, :__xmknod, [:int, :string, :uint, :pointer], :int
  end

  # Raised if safesystem cannot find the program to run.
  class ExecutableNotFound < StandardError; end

  # Raised if a safesystem program exits nonzero
  class ProcessFailed < StandardError; end
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
  # Get the recommended 'tar' command for this platform.
  def tar_cmd
    # Rely on gnu tar for solaris and OSX.
    case %x{uname -s}.chomp
    when "SunOS"
      return "gtar"
    when "Darwin"

      return "gnutar"



    else
      return "tar"
    end
  end # def tar_cmd

  # Run a block with a value.
  # Useful in lieu of assigning variables 
  def with(value, &block)
    block.call(value)
  end # def with














  def copy_entry(src, dst)
    case File.ftype(src)
    when 'fifo'
    when 'characterSpecial'
    when 'blockSpecial'
    when 'socket'
      st = File.stat(src)
      rc = mknod(dst, st.mode, st.dev)
      raise SystemCallError.new("mknod error", FFI.errno) if rc == -1


    else
      FileUtils.copy_entry(src, dst)
    end
  end
end # module FPM::Util







>
|
>
>
>










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



|
<
<
<

|

>
>





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
  # Get the recommended 'tar' command for this platform.
  def tar_cmd
    # Rely on gnu tar for solaris and OSX.
    case %x{uname -s}.chomp
    when "SunOS"
      return "gtar"
    when "Darwin"
      # Try running gnutar, it was renamed(??) in homebrew to 'gtar' at some point, I guess? I don't know.
      ["gnutar", "gtar"].each do |tar|
        system("#{tar} > /dev/null 2> /dev/null")
        return tar unless $?.exitstatus == 127
      end
    else
      return "tar"
    end
  end # def tar_cmd

  # Run a block with a value.
  # Useful in lieu of assigning variables 
  def with(value, &block)
    block.call(value)
  end # def with

  # wrapper around mknod ffi calls
  def mknod_w(path, mode, dev)
    rc = -1
    case %x{uname -s}.chomp
    when 'Linux'
      # bits/stat.h #define _MKNOD_VER_LINUX  0
      rc = xmknod(0, path, mode, FFI::MemoryPointer.new(dev))
    else
      rc = mknod(path, mode, dev)
    end
    rc
  end

  def copy_entry(src, dst)
    case File.ftype(src)
    when 'fifo', 'characterSpecial', 'blockSpecial', 'socket'



      st = File.stat(src)
      rc = mknod_w(dst, st.mode, st.dev)
      raise SystemCallError.new("mknod error", FFI.errno) if rc == -1
    when 'directory'
      FileUtils.mkdir(dst) unless File.exists? dst
    else
      FileUtils.copy_entry(src, dst)
    end
  end
end # module FPM::Util

Changes to lib/fpm/version.rb.

1
2
3
module FPM
  VERSION = "0.4.42"
end

|

1
2
3
module FPM
  VERSION = "1.0.1"
end

Changes to spec/fpm/package/deb_spec.rb.

116
117
118
119
120
121
122
123



124
125
126
127
128
129
130
      @original.name = "name"
      @original.version = "123"
      @original.iteration = "100"
      @original.epoch = "5"
      @original.architecture = "all"
      @original.dependencies << "something > 10"
      @original.dependencies << "hello >= 20"
      @original.provides = "#{@original.name} = #{@original.version}"




      @original.conflicts = ["foo < 123"]
      @original.attributes[:deb_breaks] = ["baz < 123"]

      @original.attributes[:deb_build_depends_given?] = true
      @original.attributes[:deb_build_depends] ||= []
      @original.attributes[:deb_build_depends] << 'something-else > 0.0.0'







|
>
>
>







116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
      @original.name = "name"
      @original.version = "123"
      @original.iteration = "100"
      @original.epoch = "5"
      @original.architecture = "all"
      @original.dependencies << "something > 10"
      @original.dependencies << "hello >= 20"
      @original.provides << "#{@original.name} = #{@original.version}"

      # Test to cover PR#591 (fix provides names)
      @original.provides << "Some-SILLY_name"

      @original.conflicts = ["foo < 123"]
      @original.attributes[:deb_breaks] = ["baz < 123"]

      @original.attributes[:deb_build_depends_given?] = true
      @original.attributes[:deb_build_depends] ||= []
      @original.attributes[:deb_build_depends] << 'something-else > 0.0.0'
165
166
167
168
169
170
171
172




173
174
175
176
177
178
179
        @original.dependencies.each do |dep|
          insist { @input.dependencies }.include?(dep)
        end
      end

      it "should ignore versions and conditions in 'provides' (#280)" do
        # Provides is an array because rpm supports multiple 'provides'
        insist { @input.provides } == [ @original.name ]




      end
    end # package attributes

    # This section mainly just verifies that 'dpkg-deb' can parse the package.
    context "when read with dpkg", :if => have_dpkg_deb do
      def dpkg_field(field)
        return %x{dpkg-deb -f #{@target} #{field}}.chomp







|
>
>
>
>







168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
        @original.dependencies.each do |dep|
          insist { @input.dependencies }.include?(dep)
        end
      end

      it "should ignore versions and conditions in 'provides' (#280)" do
        # Provides is an array because rpm supports multiple 'provides'
        insist { @input.provides }.include?(@original.name)
      end

      it "should fix capitalization and underscores-to-dashes (#591)" do
        insist { @input.provides }.include?("some-silly-name")
      end
    end # package attributes

    # This section mainly just verifies that 'dpkg-deb' can parse the package.
    context "when read with dpkg", :if => have_dpkg_deb do
      def dpkg_field(field)
        return %x{dpkg-deb -f #{@target} #{field}}.chomp

Changes to spec/fpm/package/dir_spec.rb.

93
94
95
96
97
98
99






100











101
        Dir.mkdir(File.join(tmpdir, "a"))
        File.write(File.join(tmpdir, "a", "b"), "hello world")
        subject.input(File.join(tmpdir, input))
        subject.output(output)
        insist { File }.exist?(File.join(output, expected_file))
      end
    end






  end











end # describe FPM::Package::Dir







>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>

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
        Dir.mkdir(File.join(tmpdir, "a"))
        File.write(File.join(tmpdir, "a", "b"), "hello world")
        subject.input(File.join(tmpdir, input))
        subject.output(output)
        insist { File }.exist?(File.join(output, expected_file))
      end
    end

    it "should not map existing paths with = in them" do
      File.write(File.join(tmpdir, "a=b"), "hello world")
      subject.input(File.join(tmpdir, "a=b"))
      subject.output(output)
      insist { File }.exist?(File.join(output, tmpdir, "a=b"))
    end

    it "should not map existing paths with = in them and obey :chdir and :prefix attributes" do
      Dir.mkdir(File.join(tmpdir, "a"))
      File.write(File.join(tmpdir,"a",  "a=b"), "hello world")
      subject.attributes[:chdir] = tmpdir
      subject.attributes[:prefix] = "/foo"
      subject.input(File.join("a", "a=b"))
      subject.output(output)
      insist { File }.exist?(File.join(output, "foo", "a", "a=b"))
    end
  end
end # describe FPM::Package::Dir

Changes to spec/fpm/package/rpm_spec.rb.

330
331
332
333
334
335
336












337
338
339
340
341
342
343
      Dir.mkdir(subject.staging_path("/example"))
      File.write(subject.staging_path("/example/%name%"), "Hello")
      subject.output(@target)

      rpm = ::RPM::File.new(@target)
      insist { rpm.files } == [ "/example/%name%" ]
    end













    it "should permit spaces in filenames (issue #164)" do
      File.write(subject.staging_path("file with space"), "Hello")

      # This will raise an exception if rpmbuild fails.
      subject.output(@target)
      rpm = ::RPM::File.new(@target)







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







330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
      Dir.mkdir(subject.staging_path("/example"))
      File.write(subject.staging_path("/example/%name%"), "Hello")
      subject.output(@target)

      rpm = ::RPM::File.new(@target)
      insist { rpm.files } == [ "/example/%name%" ]
    end

    it "should escape '%' characters in filenames while preserving permissions" do
      Dir.mkdir(subject.staging_path("/example"))
      File.write(subject.staging_path("/example/%name%"), "Hello")
      File.chmod(01777,subject.staging_path("/example/%name%"))
      subject.attributes[:rpm_use_file_permissions?] = true
      subject.output(@target)

      rpm = ::RPM::File.new(@target)
      insist { rpm.files } == [ "/example/%name%" ]
      insist { `rpm -qlv -p #{@target}`.chomp.split.first } == "-rwxrwxrwt"
    end

    it "should permit spaces in filenames (issue #164)" do
      File.write(subject.staging_path("file with space"), "Hello")

      # This will raise an exception if rpmbuild fails.
      subject.output(@target)
      rpm = ::RPM::File.new(@target)

Added spec/fpm/package_convert_spec.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
require "spec_setup"
require "fpm" # local

describe "FPM::Package#convert" do

  let(:gem_package_name_prefix) { 'rubygem19' }
  let(:default_rpm_compression) { 'gzip' }

  subject do
    source = FPM::Package::Gem.new
    prefix = source.attributes[:gem_package_name_prefix ] = 'rubygem19'
    name = source.name = "whatever"
    version = source.version = "1.0"
    source.provides << "#{prefix}-#{name} = #{version}"
    source.convert(FPM::Package::RPM)
  end

  it "applies the default attributes for target format" do
    insist { subject.attributes[:rpm_compression] } == default_rpm_compression
  end

  it "remembers attributes applied to source" do
    insist { subject.attributes[:gem_package_name_prefix] } == gem_package_name_prefix
  end

  it "should list provides matching the gem_package_name_prefix (#585)" do
    insist { subject.provides }.include?("rubygem19(whatever) = 1.0")
  end
end

Changes to templates/rpm.erb.

1
2
3
4
5
6
7
8
9
10
# Hello packaging friend!
#
# If you find yourself using this 'fpm --edit' feature frequently, it 
# a sign that fpm is missing a feature! I welcome your feature requests!
# Please visit the following URL and ask for a feature that helps you never
# need to edit this file again! :)
#   https://github.com/jordansissel/fpm/issues
# ------------------------------------------------------------------------

# Disable the stupid stuff rpm distros include in the build process by default:


|







1
2
3
4
5
6
7
8
9
10
# Hello packaging friend!
#
# If you find yourself using this 'fpm --edit' feature frequently, it is
# a sign that fpm is missing a feature! I welcome your feature requests!
# Please visit the following URL and ask for a feature that helps you never
# need to edit this file again! :)
#   https://github.com/jordansissel/fpm/issues
# ------------------------------------------------------------------------

# Disable the stupid stuff rpm distros include in the build process by default: