Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Perl CPAN Support, Broken tar --strip-components Command, Fix #1518

Closed
wants to merge 7 commits into from
28 changes: 19 additions & 9 deletions lib/fpm/package/cpan.rb
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ def input(package)
self.vendor = case metadata["author"]
when String; metadata["author"]
when Array; metadata["author"].join(", ")
# for Class::Data::Inheritable and others with blank 'author' field, fix "Invalid package configuration: Unexpected CPAN 'author' field type: NilClass. This is a bug."
when NilClass; "No Vendor Or Author Provided"
else
raise FPM::InvalidPackageConfiguration, "Unexpected CPAN 'author' field type: #{metadata["author"].class}. This is a bug."
end if metadata.include?("author")
Expand Down Expand Up @@ -142,7 +144,9 @@ def input(package)
cpanm_flags += ["--force"] if attributes[:cpan_cpanm_force?]
cpanm_flags += ["--verbose"] if attributes[:cpan_verbose?]

safesystem(attributes[:cpan_cpanm_bin], *cpanm_flags)
# Run cpanm with stdin enabled so that ExtUtils::MakeMaker does not prompt user for input
# safesystem(attributes[:cpan_cpanm_bin], *cpanm_flags)
safesystemin("", attributes[:cpan_cpanm_bin], *cpanm_flags)

if !attributes[:no_auto_depends?]
found_dependencies = {}
Expand Down Expand Up @@ -208,15 +212,18 @@ def input(package)
#
if File.exist?("Build.PL")
# Module::Build is in use here; different actions required.
safesystem(attributes[:cpan_perl_bin],
# safesystem(attributes[:cpan_perl_bin], # hangs on interactive build, waiting for user input
safesystemin("", attributes[:cpan_perl_bin],
"-Mlocal::lib=#{build_path("cpan")}",
"Build.PL")
safesystem(attributes[:cpan_perl_bin],
# safesystem(attributes[:cpan_perl_bin], # hangs on interactive build, waiting for user input
safesystemin("", attributes[:cpan_perl_bin],
"-Mlocal::lib=#{build_path("cpan")}",
"./Build")

if attributes[:cpan_test?]
safesystem(attributes[:cpan_perl_bin],
# safesystem(attributes[:cpan_perl_bin], # hangs on interactive build, waiting for user input
safesystemin("", attributes[:cpan_perl_bin],
"-Mlocal::lib=#{build_path("cpan")}",
"./Build", "test")
end
Expand All @@ -233,13 +240,15 @@ def input(package)
elsif File.exist?("Makefile.PL")
if attributes[:cpan_perl_lib_path]
perl_lib_path = attributes[:cpan_perl_lib_path]
safesystem(attributes[:cpan_perl_bin],
# safesystem(attributes[:cpan_perl_bin], # hangs on interactive build, waiting for user input
safesystemin("", attributes[:cpan_perl_bin],
"-Mlocal::lib=#{build_path("cpan")}",
"Makefile.PL", "PREFIX=#{prefix}", "LIB=#{perl_lib_path}",
# Empty install_base to avoid local::lib being used.
"INSTALL_BASE=")
else
safesystem(attributes[:cpan_perl_bin],
# safesystem(attributes[:cpan_perl_bin], # hangs on interactive build, waiting for user input
safesystemin("", attributes[:cpan_perl_bin],
"-Mlocal::lib=#{build_path("cpan")}",
"Makefile.PL", "PREFIX=#{prefix}",
# Empty install_base to avoid local::lib being used.
Expand Down Expand Up @@ -304,7 +313,8 @@ def unpack(tarball)
directory = build_path("module")
::Dir.mkdir(directory)
args = [ "-C", directory, "-zxf", tarball,
"--strip-components", "1" ]
# "--strip-components", "1" ] # fails on removing leading ./Foo/ in tarball paths
%q{--transform=s,[./]*[^/]*/,,} ] # succeeds on removing leading ./Foo/ or /Foo/ or Foo/
safesystem("tar", *args)
return directory
end
Expand Down Expand Up @@ -352,7 +362,7 @@ def download(metadata, cpan_version=nil)
response = httpfetch(url)
rescue Net::HTTPServerException => e
#logger.error("Download failed", :error => response.status_line,
#:url => url)
##:url => url)
logger.error("Download failed", :error => e, :url => url)
raise FPM::InvalidPackageConfiguration, "metacpan query failed"
end
Expand All @@ -371,7 +381,7 @@ def search(package)
response = httpfetch(metacpan_url)
rescue Net::HTTPServerException => e
#logger.error("metacpan query failed.", :error => response.status_line,
#:module => package, :url => metacpan_url)
##:module => package, :url => metacpan_url)
logger.error("metacpan query failed.", :error => e.message,
:module => package, :url => metacpan_url)
raise FPM::InvalidPackageConfiguration, "metacpan query failed"
Expand Down
41 changes: 40 additions & 1 deletion lib/fpm/package/rpm.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ class FPM::Package::RPM < FPM::Package
"bzip2" => "w9.bzdio"
} unless defined?(COMPRESSION_MAP)

option "--ba", :flag, "Pass --ba to rpmbuild; build both source and binary packages."
option "--bb", :flag, "Pass --bb to rpmbuild; build binary package, not source package."
option "--bs", :flag, "Pass --bs to rpmbuild; build source package, not binary package."

option "--use-file-permissions", :flag,
"Use existing file permissions when defining ownership and modes."

Expand Down Expand Up @@ -419,7 +423,42 @@ def prefixed_path(path)
def output(output_path)
output_check(output_path)
%w(BUILD RPMS SRPMS SOURCES SPECS).each { |d| FileUtils.mkdir_p(build_path(d)) }
args = ["rpmbuild", "-bb"]



# THEN START HERE: use option to determine args below
# THEN START HERE: use option to determine args below
# THEN START HERE: use option to determine args below


logger.info("[[[ DEBUG ]]] have :rpm_ba", :rpm_ba => attributes[:rpm_ba?])
logger.info("[[[ DEBUG ]]] have :rpm_bb", :rpm_bb => attributes[:rpm_bb?])
logger.info("[[[ DEBUG ]]] have :rpm_bs", :rpm_bs => attributes[:rpm_bs?])

# args = ["rpmbuild", "-bb"] # NEED REMOVE, ORIGINAL
args = ["rpmbuild"]

if attributes[:rpm_ba?]
if attributes[:rpm_bb?]
raise "RPM build options --rpm-ba and --rpm-bb both provided, must choose only one"
elsif attributes[:rpm_bs?]
raise "RPM build options --rpm-ba and --rpm-bs both provided, must choose only one"
end
args += ["--ba"]
elsif attributes[:rpm_bb?]
if attributes[:rpm_bs?]
raise "RPM build options --rpm-bb and --rpm-bs both provided, must choose only one"
end
args += ["--bb"]
elsif attributes[:rpm_bs?]
args += ["--bs"]
else
# default to --bb, build binary packages only
args += ["--bb"]
end




if %x{uname -m}.chomp != self.architecture
rpm_target = self.architecture
Expand Down
59 changes: 51 additions & 8 deletions lib/fpm/util.rb
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ def execmd(*args)
raise ExecutableNotFound.new(program)
end

logger.debug("Running command", :args => args2)
logger.debug("Running command: " + args2.join(" "))

stdout_r, stdout_w = IO.pipe
stderr_r, stderr_w = IO.pipe
Expand All @@ -161,18 +161,23 @@ def execmd(*args)

stdout_w.close; stderr_w.close
logger.debug("Process is running", :pid => process.pid)
# DEV NOTE: disabliing this entire conditional code block allows output & cures the gcc/as freeze/hang, but re-introduces the Perl CPAN Inline::CPP interactive build hang;
# the only solution discovered so far is a "hybrid" approach whereby we allow the conditional to execute, but we disable STDOUT & STDERR,
# as seen with the 5 commented-out lines below and the 1 logger.pipe() line below which was copied directly from the else code block,
# as well as the 2 commented-out lines stdout.read & stderr.read in safesystemin()
if block_given?
args3 = []
args3.push(process) if opts[:process]
args3.push(process.io.stdin) if opts[:stdin]
args3.push(stdout_r) if opts[:stdout]
args3.push(stderr_r) if opts[:stderr]
# args3.push(stdout_r) if opts[:stdout]
# args3.push(stderr_r) if opts[:stderr]

yield(*args3)

process.io.stdin.close if opts[:stdin] and not process.io.stdin.closed?
stdout_r.close unless stdout_r.closed?
stderr_r.close unless stderr_r.closed?
# process.io.stdin.close if opts[:stdin] and not process.io.stdin.closed?
# stdout_r.close unless stdout_r.closed?
# stderr_r.close unless stderr_r.closed?
logger.pipe(stdout_r => :info, stderr_r => :info)
else
# Log both stdout and stderr as 'info' because nobody uses stderr for
# actually reporting errors and as a result 'stderr' is a misnomer.
Expand All @@ -186,6 +191,7 @@ def execmd(*args)

# Run a command safely in a way that gets reports useful errors.
def safesystem(*args)
logger.debug("[[[ DEBUG, FULL COMMAND ]]] in safesystem(), received args =\n" + args.join(" "))
# ChildProcess isn't smart enough to run a $SHELL if there's
# spaces in the first arg and there's only 1 arg.
if args.size == 1
Expand All @@ -203,13 +209,50 @@ def safesystem(*args)

if !success
raise ProcessFailed.new("#{program} failed (exit code #{exit_code})" \
". Full command was:#{args.inspect}")
". Full command was:\n" + args.join(" "))
end
return success
end # def safesystem

# Run a command safely in a way that pushes stdin to command
def safesystemin(*args)
logger.debug("[[[ DEBUG, FULL COMMAND ]]] in safesystemin(), received args =\n" + args.join(" "))
# Our first argument is our stdin
safe_stdin = args.shift()

if args.size == 1
args = [ default_shell, "-c", args[0] ]
end

if args[0].kind_of?(Hash)
env = args.shift()
exit_code = execmd(env, args) do |stdin,stdout,stderr|
stdin.write(safe_stdin)
stdin.close
stdout_r_str = stdout.read
stderr_r_str = stderr.read
end
else
exit_code = execmd(args) do |stdin,stdout,stderr|
stdin.write(safe_stdin)
stdin.close
# stdout_r_str = stdout.read
# stderr_r_str = stderr.read
end
end
program = args[0]
success = (exit_code == 0)

if !success
raise ProcessFailed.new("#{program} failed (exit code #{exit_code})" \
". Full command was:\n" + args.join(" "))
end
return success
end # def safesystemin

# Run a command safely in a way that captures output and status.
def safesystemout(*args)
logger.debug("[[[ DEBUG, FULL COMMAND ]]] in safesystemout(), received args =\n" + args.join(" "))
if args.size == 1
args = [ ENV["SHELL"], "-c", args[0] ]
end
Expand All @@ -227,7 +270,7 @@ def safesystemout(*args)

if !success
raise ProcessFailed.new("#{program} failed (exit code #{exit_code})" \
". Full command was:#{args.inspect}")
". Full command was:\n" + args.join(" "))
end
return stdout_r_str
end # def safesystemout
Expand Down