fpm 0.4.42 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +15 -0
- data/CHANGELIST +54 -0
- data/lib/fpm.rb +1 -0
- data/lib/fpm/command.rb +25 -4
- data/lib/fpm/package.rb +7 -2
- data/lib/fpm/package/cpan.rb +72 -24
- data/lib/fpm/package/deb.rb +68 -9
- data/lib/fpm/package/dir.rb +32 -6
- data/lib/fpm/package/gem.rb +1 -1
- data/lib/fpm/package/npm.rb +7 -0
- data/lib/fpm/package/pkgin.rb +35 -0
- data/lib/fpm/package/pyfpm/__init__.pyc +0 -0
- data/lib/fpm/package/pyfpm/get_metadata.py +21 -6
- data/lib/fpm/package/pyfpm/get_metadata.pyc +0 -0
- data/lib/fpm/package/python.rb +10 -8
- data/lib/fpm/package/rpm.rb +59 -18
- data/lib/fpm/util.rb +42 -1
- data/lib/fpm/version.rb +1 -1
- data/templates/rpm.erb +2 -17
- metadata +40 -51
data/lib/fpm/package/dir.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "fpm/package"
|
2
|
+
require "fpm/util"
|
2
3
|
require "backports"
|
3
4
|
require "fileutils"
|
4
5
|
require "find"
|
@@ -42,14 +43,15 @@ class FPM::Package::Dir < FPM::Package
|
|
42
43
|
# This mapping should work the same way 'rsync -a' does
|
43
44
|
# Meaning 'rsync -a source dest'
|
44
45
|
# and 'source=dest' in fpm work the same as the above rsync
|
45
|
-
if path =~ /.=./
|
46
|
+
if path =~ /.=./ && !File.exists?(chdir == '.' ? path : File.join(chdir, path))
|
46
47
|
origin, destination = path.split("=", 2)
|
47
48
|
|
48
49
|
if File.directory?(origin) && origin[-1,1] == "/"
|
49
|
-
chdir = origin
|
50
|
+
chdir = chdir == '.' ? origin : File.join(chdir, origin)
|
50
51
|
source = "."
|
51
52
|
else
|
52
|
-
|
53
|
+
origin_dir = File.dirname(origin)
|
54
|
+
chdir = chdir == '.' ? origin_dir : File.join(chdir, origin_dir)
|
53
55
|
source = File.basename(origin)
|
54
56
|
end
|
55
57
|
else
|
@@ -63,8 +65,18 @@ class FPM::Package::Dir < FPM::Package
|
|
63
65
|
destination = File.join(staging_path, destination)
|
64
66
|
|
65
67
|
@logger["method"] = "input"
|
66
|
-
|
67
|
-
|
68
|
+
begin
|
69
|
+
::Dir.chdir(chdir) do
|
70
|
+
begin
|
71
|
+
clone(source, destination)
|
72
|
+
rescue Errno::ENOENT => e
|
73
|
+
raise FPM::InvalidPackageConfiguration,
|
74
|
+
"Cannot package the path '#{source}', does it exist?"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
rescue Errno::ENOENT => e
|
78
|
+
raise FPM::InvalidPackageConfiguration,
|
79
|
+
"Cannot chdir to '#{chdir}'. Does it exist?"
|
68
80
|
end
|
69
81
|
|
70
82
|
# Set some defaults. This is useful because other package types
|
@@ -105,6 +117,20 @@ class FPM::Package::Dir < FPM::Package
|
|
105
117
|
# /tmp/example/hello/world
|
106
118
|
def clone(source, destination)
|
107
119
|
@logger.debug("Cloning path", :source => source, :destination => destination)
|
120
|
+
# Edge case check; abort if the temporary directory is the source.
|
121
|
+
# If the temporary dir is the same path as the source, it causes
|
122
|
+
# fpm to recursively (and forever) copy the staging directory by
|
123
|
+
# accident (#542).
|
124
|
+
if File.expand_path(source) == File.expand_path(::Dir.tmpdir)
|
125
|
+
raise FPM::InvalidPackageConfiguration,
|
126
|
+
"A source directory cannot be the root of your temporary " \
|
127
|
+
"directory (#{::Dir.tmpdir}). fpm uses the temporary directory " \
|
128
|
+
"to stage files during packaging, so this setting would have " \
|
129
|
+
"caused fpm to loop creating staging directories and copying " \
|
130
|
+
"them into your package! Oops! If you are confused, maybe you could " \
|
131
|
+
"check your TMPDIR or TEMPDIR environment variables?"
|
132
|
+
end
|
133
|
+
|
108
134
|
# For single file copies, permit file destinations
|
109
135
|
if File.file?(source) && !File.directory?(destination)
|
110
136
|
if destination[-1,1] == "/"
|
@@ -155,7 +181,7 @@ class FPM::Package::Dir < FPM::Package
|
|
155
181
|
rescue Errno::ENOENT, Errno::EXDEV, Errno::EPERM
|
156
182
|
# Hardlink attempt failed, copy it instead
|
157
183
|
@logger.debug("Copying", :source => source, :destination => destination)
|
158
|
-
|
184
|
+
copy_entry(source, destination)
|
159
185
|
rescue Errno::EEXIST
|
160
186
|
sane_path = destination.gsub(staging_path, "")
|
161
187
|
@logger.error("Cannot copy file, the destination path is probably a directory and I attempted to write a file.", :path => sane_path, :staging => staging_path)
|
data/lib/fpm/package/gem.rb
CHANGED
@@ -89,7 +89,7 @@ class FPM::Package::Gem < FPM::Package
|
|
89
89
|
|
90
90
|
def load_package_info(gem_path)
|
91
91
|
|
92
|
-
spec = YAML.load(%x{#{attributes[:gem_gem]}
|
92
|
+
spec = YAML.load(%x{#{attributes[:gem_gem]} specification #{gem_path} --yaml})
|
93
93
|
|
94
94
|
if !attributes[:gem_package_prefix].nil?
|
95
95
|
attributes[:gem_package_name_prefix] = attributes[:gem_package_prefix]
|
data/lib/fpm/package/npm.rb
CHANGED
@@ -11,6 +11,9 @@ class FPM::Package::NPM < FPM::Package
|
|
11
11
|
option "--package-name-prefix", "PREFIX", "Name to prefix the package " \
|
12
12
|
"name with.", :default => "node"
|
13
13
|
|
14
|
+
option "--registry", "NPM_REGISTRY",
|
15
|
+
"The npm registry to use instead of the default."
|
16
|
+
|
14
17
|
private
|
15
18
|
def input(package)
|
16
19
|
# Notes:
|
@@ -21,6 +24,10 @@ class FPM::Package::NPM < FPM::Package
|
|
21
24
|
"global" => "true"
|
22
25
|
}
|
23
26
|
|
27
|
+
if attributes.include?(:npm_registry) && !attributes[:npm_registry].nil?
|
28
|
+
settings["registry"] = attributes[:npm_registry]
|
29
|
+
end
|
30
|
+
|
24
31
|
if attributes.include?(:prefix) && !attributes[:prefix].nil?
|
25
32
|
settings["prefix"] = staging_path(attributes[:prefix])
|
26
33
|
else
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class FPM::Package::Pkgin < FPM::Package
|
2
|
+
|
3
|
+
def output(output_path)
|
4
|
+
output_check(output_path)
|
5
|
+
|
6
|
+
File.write(build_path("build-info"), `pkg_info -X pkg_install | egrep '^(MACHINE_ARCH|OPSYS|OS_VERSION|PKGTOOLS_VERSION)'`)
|
7
|
+
|
8
|
+
cwd = ::Dir.pwd
|
9
|
+
::Dir.chdir(staging_path)
|
10
|
+
|
11
|
+
files = []
|
12
|
+
Find.find(".") do |path|
|
13
|
+
stat = File.lstat(path)
|
14
|
+
next unless stat.symlink? or stat.file?
|
15
|
+
files << path
|
16
|
+
end
|
17
|
+
::Dir.chdir(cwd)
|
18
|
+
|
19
|
+
File.write(build_path("packlist"), files.sort.join("\n"))
|
20
|
+
|
21
|
+
File.write(build_path("comment"), self.description + "\n")
|
22
|
+
|
23
|
+
File.write(build_path("description"), self.description + "\n")
|
24
|
+
|
25
|
+
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" ]
|
26
|
+
safesystem("pkg_create", *args)
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
def iteration
|
31
|
+
return @iteration ? @iteration : 1
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
Binary file
|
@@ -1,11 +1,23 @@
|
|
1
1
|
from distutils.core import Command
|
2
2
|
import os
|
3
|
+
import sys
|
3
4
|
import pkg_resources
|
4
5
|
try:
|
5
6
|
import json
|
6
7
|
except ImportError:
|
7
8
|
import simplejson as json
|
8
9
|
|
10
|
+
PY3 = sys.version_info[0] == 3
|
11
|
+
|
12
|
+
if PY3:
|
13
|
+
def u(s):
|
14
|
+
return s
|
15
|
+
else:
|
16
|
+
def u(s):
|
17
|
+
if isinstance(u, unicode):
|
18
|
+
return u
|
19
|
+
return s.decode('utf-8')
|
20
|
+
|
9
21
|
|
10
22
|
# Note, the last time I coded python daily was at Google, so it's entirely
|
11
23
|
# possible some of my techniques below are outdated or bad.
|
@@ -17,12 +29,14 @@ class get_metadata(Command):
|
|
17
29
|
user_options = [
|
18
30
|
('load-requirements-txt', 'l',
|
19
31
|
"load dependencies from requirements.txt"),
|
20
|
-
|
32
|
+
("output=", "o", "output destination for metadata json")
|
33
|
+
]
|
21
34
|
boolean_options = ['load-requirements-txt']
|
22
35
|
|
23
36
|
def initialize_options(self):
|
24
37
|
self.load_requirements_txt = False
|
25
38
|
self.cwd = None
|
39
|
+
self.output = None
|
26
40
|
|
27
41
|
def finalize_options(self):
|
28
42
|
self.cwd = os.getcwd()
|
@@ -46,9 +60,9 @@ class get_metadata(Command):
|
|
46
60
|
data = {
|
47
61
|
"name": self.distribution.get_name(),
|
48
62
|
"version": self.distribution.get_version(),
|
49
|
-
"author": "%s <%s>" % (
|
50
|
-
self.distribution.get_author(),
|
51
|
-
self.distribution.get_author_email(),
|
63
|
+
"author": u("%s <%s>") % (
|
64
|
+
u(self.distribution.get_author()),
|
65
|
+
u(self.distribution.get_author_email()),
|
52
66
|
),
|
53
67
|
"description": self.distribution.get_description(),
|
54
68
|
"license": self.distribution.get_license(),
|
@@ -74,8 +88,9 @@ class get_metadata(Command):
|
|
74
88
|
|
75
89
|
data["dependencies"] = final_deps
|
76
90
|
|
91
|
+
output = open(self.output, "w")
|
77
92
|
if hasattr(json, 'dumps'):
|
78
|
-
|
93
|
+
output.write(json.dumps(data, indent=2))
|
79
94
|
else:
|
80
95
|
# For Python 2.5 and Debian's python-json
|
81
|
-
|
96
|
+
output.write(json.write(data))
|
Binary file
|
data/lib/fpm/package/python.rb
CHANGED
@@ -55,12 +55,12 @@ class FPM::Package::Python < FPM::Package
|
|
55
55
|
"Want to what your target platform is using? Run this: " \
|
56
56
|
"python -c 'from distutils.sysconfig import get_python_lib; " \
|
57
57
|
"print get_python_lib()'"
|
58
|
-
option "--install-data", "DATA_PATH", "The path to where data should be
|
58
|
+
option "--install-data", "DATA_PATH", "The path to where data should be " \
|
59
59
|
"installed to. This is equivalent to 'python setup.py --install-data " \
|
60
60
|
"DATA_PATH"
|
61
61
|
option "--dependencies", :flag, "Include requirements defined in setup.py" \
|
62
62
|
" as dependencies.", :default => true
|
63
|
-
option "--obey-requirements-txt", :flag, "Use a requirements.txt file" \
|
63
|
+
option "--obey-requirements-txt", :flag, "Use a requirements.txt file " \
|
64
64
|
"in the top-level directory of the python package for dependency " \
|
65
65
|
"detection.", :default => false
|
66
66
|
|
@@ -148,8 +148,9 @@ class FPM::Package::Python < FPM::Package
|
|
148
148
|
setup_dir = File.dirname(setup_py)
|
149
149
|
|
150
150
|
output = ::Dir.chdir(setup_dir) do
|
151
|
+
tmp = build_path("metadata.json")
|
151
152
|
setup_cmd = "env PYTHONPATH=#{pylib} #{attributes[:python_bin]} " \
|
152
|
-
"setup.py --command-packages=pyfpm get_metadata"
|
153
|
+
"setup.py --command-packages=pyfpm get_metadata --output=#{tmp}"
|
153
154
|
|
154
155
|
if attributes[:python_obey_requirements_txt?]
|
155
156
|
setup_cmd += " --load-requirements-txt"
|
@@ -160,16 +161,17 @@ class FPM::Package::Python < FPM::Package
|
|
160
161
|
# details.
|
161
162
|
@logger.info("fetching package metadata", :setup_cmd => setup_cmd)
|
162
163
|
|
163
|
-
|
164
|
-
|
164
|
+
success = safesystem(setup_cmd)
|
165
|
+
#%x{#{setup_cmd}}
|
166
|
+
if !success
|
165
167
|
@logger.error("setup.py get_metadata failed", :command => setup_cmd,
|
166
168
|
:exitcode => $?.exitstatus)
|
167
169
|
raise "An unexpected error occurred while processing the setup.py file"
|
168
170
|
end
|
169
|
-
|
171
|
+
File.read(tmp)
|
170
172
|
end
|
171
|
-
@logger.debug("
|
172
|
-
metadata = JSON.parse(output
|
173
|
+
@logger.debug("result from `setup.py get_metadata`", :data => output)
|
174
|
+
metadata = JSON.parse(output)
|
173
175
|
@logger.info("object output of get_metadata", :json => metadata)
|
174
176
|
|
175
177
|
self.architecture = metadata["architecture"]
|
data/lib/fpm/package/rpm.rb
CHANGED
@@ -113,6 +113,11 @@ class FPM::Package::RPM < FPM::Package
|
|
113
113
|
next rpmbuild_filter_from_requires
|
114
114
|
end
|
115
115
|
|
116
|
+
option "--ignore-iteration-in-dependencies", :flag,
|
117
|
+
"For '=' (equal) dependencies, allow iterations on the specified " \
|
118
|
+
"version. Default is to be specific. This option allows the same " \
|
119
|
+
"version of a package but any iteration is permitted"
|
120
|
+
|
116
121
|
private
|
117
122
|
|
118
123
|
# Fix path name
|
@@ -129,14 +134,18 @@ class FPM::Package::RPM < FPM::Package
|
|
129
134
|
end
|
130
135
|
|
131
136
|
def rpm_file_entry(file)
|
137
|
+
original_file = file
|
132
138
|
file = rpm_fix_name(file)
|
133
139
|
return file unless attributes[:rpm_use_file_permissions?]
|
134
140
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
141
|
+
# Stat the original filename in the relative staging path
|
142
|
+
::Dir.chdir(staging_path) do
|
143
|
+
stat = File.stat(original_file.gsub(/\"/, '').sub(/^\//,''))
|
144
|
+
user = Etc.getpwuid(stat.uid).name
|
145
|
+
group = Etc.getgrgid(stat.gid).name
|
146
|
+
mode = stat.mode
|
147
|
+
return sprintf("%%attr(%o, %s, %s) %s\n", mode & 4095 , user, group, file)
|
148
|
+
end
|
140
149
|
end
|
141
150
|
|
142
151
|
|
@@ -184,25 +193,23 @@ class FPM::Package::RPM < FPM::Package
|
|
184
193
|
# Convert 'rubygem-foo' provides values to 'rubygem(foo)'
|
185
194
|
# since that's what most rpm packagers seem to do.
|
186
195
|
self.provides = self.provides.collect do |provides|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
#
|
191
|
-
"rubygem(#{name})#{remainder ? " #{remainder}" : ""}"
|
196
|
+
# Tries to match rubygem_prefix [1], gem_name [2] and version [3] if present
|
197
|
+
# and return it in rubygem_prefix(gem_name) form
|
198
|
+
if name=/^(#{attributes[:gem_package_name_prefix]})-([^\s]+)\s*(.*)$/.match(provides)
|
199
|
+
"#{name[1]}(#{name[2]})#{name[3] ? " #{name[3]}" : ""}"
|
192
200
|
else
|
193
201
|
provides
|
194
202
|
end
|
195
203
|
end
|
196
204
|
self.dependencies = self.dependencies.collect do |dependency|
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
"
|
205
|
+
# Tries to match rubygem_prefix [1], gem_name [2] and version [3] if present
|
206
|
+
# and return it in rubygem_prefix(gem_name) form
|
207
|
+
if name=/^(#{attributes[:gem_package_name_prefix]})-([^\s]+)\s*(.*)$/.match(dependency)
|
208
|
+
"#{name[1]}(#{name[2]})#{name[3] ? " #{name[3]}" : ""}"
|
201
209
|
else
|
202
210
|
dependency
|
203
211
|
end
|
204
212
|
end
|
205
|
-
#self.provides << "rubygem(#{self.name})"
|
206
213
|
end
|
207
214
|
|
208
215
|
# Convert != dependency as Conflict =, as rpm doesn't understand !=
|
@@ -229,6 +236,23 @@ class FPM::Package::RPM < FPM::Package
|
|
229
236
|
end
|
230
237
|
end
|
231
238
|
|
239
|
+
# if --ignore-iteration-in-dependencies is true convert foo = X, to
|
240
|
+
# foo >= X , foo < X+1
|
241
|
+
if self.attributes[:rpm_ignore_iteration_in_dependencies?]
|
242
|
+
self.dependencies = self.dependencies.collect do |dep|
|
243
|
+
name, op, version = dep.split(/\s+/)
|
244
|
+
if op == '='
|
245
|
+
nextversion = version.split('.').collect { |v| v.to_i }
|
246
|
+
nextversion[-1] += 1
|
247
|
+
nextversion = nextversion.join(".")
|
248
|
+
@logger.warn("Converting dependency #{dep} to #{name} >= #{version}, #{name} < #{nextversion}")
|
249
|
+
["#{name} >= #{version}", "#{name} < #{nextversion}"]
|
250
|
+
else
|
251
|
+
dep
|
252
|
+
end
|
253
|
+
end.flatten
|
254
|
+
end
|
255
|
+
|
232
256
|
end # def converted
|
233
257
|
|
234
258
|
def input(path)
|
@@ -316,7 +340,7 @@ class FPM::Package::RPM < FPM::Package
|
|
316
340
|
|
317
341
|
Find.find(staging_path) do |path|
|
318
342
|
next if path == staging_path
|
319
|
-
if File.directory? path
|
343
|
+
if File.directory? path and !File.symlink? path
|
320
344
|
add_path = path.gsub(/^#{staging_path}/,'')
|
321
345
|
self.directories << add_path if not fs_dirs.include? add_path
|
322
346
|
end
|
@@ -326,7 +350,7 @@ class FPM::Package::RPM < FPM::Package
|
|
326
350
|
alldirs = []
|
327
351
|
self.directories.each do |path|
|
328
352
|
Find.find(File.join(staging_path, path)) do |subpath|
|
329
|
-
if File.directory? subpath
|
353
|
+
if File.directory? subpath and !File.symlink? subpath
|
330
354
|
alldirs << subpath.gsub(/^#{staging_path}/, '')
|
331
355
|
end
|
332
356
|
end
|
@@ -334,12 +358,29 @@ class FPM::Package::RPM < FPM::Package
|
|
334
358
|
self.directories = alldirs
|
335
359
|
end
|
336
360
|
|
337
|
-
|
361
|
+
# scan all conf file paths for files and add them
|
362
|
+
allconfigs = []
|
363
|
+
self.config_files.each do |path|
|
364
|
+
cfg_path = File.expand_path(path, staging_path)
|
365
|
+
Find.find(cfg_path) do |p|
|
366
|
+
allconfigs << p.gsub("#{staging_path}/", '') if File.file? p
|
367
|
+
end
|
368
|
+
end
|
369
|
+
allconfigs.sort!.uniq!
|
370
|
+
|
371
|
+
self.config_files = allconfigs.map { |x| File.join(self.prefix, x) }
|
338
372
|
|
339
373
|
(attributes[:rpm_rpmbuild_define] or []).each do |define|
|
340
374
|
args += ["--define", define]
|
341
375
|
end
|
342
376
|
|
377
|
+
# copy all files from staging to BUILD dir
|
378
|
+
Find.find(staging_path) do |path|
|
379
|
+
src = path.gsub(/^#{staging_path}/, '')
|
380
|
+
dst = File.join(build_path, build_sub_dir, src)
|
381
|
+
copy_entry(path, dst)
|
382
|
+
end
|
383
|
+
|
343
384
|
rpmspec = template("rpm.erb").result(binding)
|
344
385
|
specfile = File.join(build_path("SPECS"), "#{name}.spec")
|
345
386
|
File.write(specfile, rpmspec)
|
data/lib/fpm/util.rb
CHANGED
@@ -1,8 +1,21 @@
|
|
1
1
|
require "fpm/namespace"
|
2
2
|
require "childprocess"
|
3
|
+
require "ffi"
|
3
4
|
|
4
5
|
# Some utility functions
|
5
6
|
module FPM::Util
|
7
|
+
extend FFI::Library
|
8
|
+
ffi_lib FFI::Library::LIBC
|
9
|
+
|
10
|
+
# mknod is __xmknod in glibc a wrapper around mknod to handle
|
11
|
+
# various stat struct formats. See bits/stat.h in glibc source
|
12
|
+
begin
|
13
|
+
attach_function :mknod, :mknod, [:string, :uint, :ulong], :int
|
14
|
+
rescue FFI::NotFoundError
|
15
|
+
# glibc/io/xmknod.c int __xmknod (int vers, const char *path, mode_t mode, dev_t *dev)
|
16
|
+
attach_function :xmknod, :__xmknod, [:int, :string, :uint, :pointer], :int
|
17
|
+
end
|
18
|
+
|
6
19
|
# Raised if safesystem cannot find the program to run.
|
7
20
|
class ExecutableNotFound < StandardError; end
|
8
21
|
|
@@ -46,7 +59,9 @@ module FPM::Util
|
|
46
59
|
process.start
|
47
60
|
stdout_w.close; stderr_w.close
|
48
61
|
@logger.debug('Process is running', :pid => process.pid)
|
49
|
-
|
62
|
+
# Log both stdout and stderr as 'info' because nobody uses stderr for
|
63
|
+
# actually reporting errors and as a result 'stderr' is a misnomer.
|
64
|
+
@logger.pipe(stdout_r => :info, stderr_r => :info)
|
50
65
|
|
51
66
|
process.wait
|
52
67
|
success = (process.exit_code == 0)
|
@@ -113,4 +128,30 @@ module FPM::Util
|
|
113
128
|
def with(value, &block)
|
114
129
|
block.call(value)
|
115
130
|
end # def with
|
131
|
+
|
132
|
+
# wrapper around mknod ffi calls
|
133
|
+
def mknod_w(path, mode, dev)
|
134
|
+
rc = -1
|
135
|
+
case %x{uname -s}.chomp
|
136
|
+
when 'Linux'
|
137
|
+
# bits/stat.h #define _MKNOD_VER_LINUX 0
|
138
|
+
rc = xmknod(0, path, mode, FFI::MemoryPointer.new(dev))
|
139
|
+
else
|
140
|
+
rc = mknod(path, mode, dev)
|
141
|
+
end
|
142
|
+
rc
|
143
|
+
end
|
144
|
+
|
145
|
+
def copy_entry(src, dst)
|
146
|
+
case File.ftype(src)
|
147
|
+
when 'fifo', 'characterSpecial', 'blockSpecial', 'socket'
|
148
|
+
st = File.stat(src)
|
149
|
+
rc = mknod_w(dst, st.mode, st.dev)
|
150
|
+
raise SystemCallError.new("mknod error", FFI.errno) if rc == -1
|
151
|
+
when 'directory'
|
152
|
+
FileUtils.mkdir(dst) unless File.exists? dst
|
153
|
+
else
|
154
|
+
FileUtils.copy_entry(src, dst)
|
155
|
+
end
|
156
|
+
end
|
116
157
|
end # module FPM::Util
|