fpm 0.4.42 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|