Skip to content

Commit

Permalink
separate installation logic out of input methods for gem and cpan
Browse files Browse the repository at this point in the history
  • Loading branch information
NicholasBHubbard committed Aug 30, 2023
1 parent b085edc commit 89cbea2
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 21 deletions.
42 changes: 39 additions & 3 deletions lib/fpm/package.rb
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,11 @@ def to_s

attr_accessor :directories

# Strings that contain the shell code for building/installing a package. These
# are needed by source-based package types.
attr_accessor :build_procedure
attr_accessor :install_procedure

# Any other attributes specific to this package.
# This is where you'd put rpm, deb, or other specific attributes.
attr_accessor :attributes
Expand Down Expand Up @@ -190,6 +195,13 @@ def type
self.class.type
end # def type

# Source-based package types (such as SRPM and SDEB) should override this method to return true
public
def source_pkg?
return nil
end # def source_pkg?
private

# Convert this package to a new package type
def convert(klass)
logger.info("Converting #{self.type} to #{klass.type}")
Expand All @@ -206,6 +218,18 @@ def convert(klass)
:@name, :@provides, :@replaces, :@scripts, :@url, :@vendor, :@version,
:@directories, :@staging_path, :@attrs
]

# source packages dont need to actually build the source, instead they need
# the shell code for the build and install procedures.
if pkg.source_pkg?()
h = shell_code() # h is a two elem hash ...
@build_procedure = h[:build_procedure]
@install_procedure = h[:install_procedure]
ivars += [:@build_procedure, :@install_procedure]
else # binary packages need to be built and installed to the staging_path
build_and_install
end

ivars.each do |ivar|
#logger.debug("Copying ivar", :ivar => ivar, :value => instance_variable_get(ivar),
#:from => self.type, :to => pkg.type)
Expand Down Expand Up @@ -240,9 +264,12 @@ def converted_from(origin)
# The idea is that you can keep pumping in new things to a package
# for later conversion or output.
#
# Implementations are expected to put files relevant to the 'input' in the
# staging_path
def input(thing_to_input)
# Implementations are expected to leave their prepared files in the
# staging_path.
#
# It is important to note that the build/installation of the
# package now happens in the convert() method, not input().
def input(thing_to_prepare)
raise NotImplementedError.new("#{self.class.name} does not yet support " \
"reading #{self.type} packages")
end # def input
Expand All @@ -253,6 +280,15 @@ def output(path)
"creating #{self.type} packages")
end # def output

def shell_code
raise NotImplementedError.new("#{self.class.name} does not yet know how to produce " \
"shell code for its build/install procedures")
end # def shell_code

def build_and_install
raise NotImplementedError.new("TODO")
end # def build_and_install

def staging_path(path=nil)
@staging_path ||= Stud::Temporary.directory("package-#{type}-staging")

Expand Down
45 changes: 30 additions & 15 deletions lib/fpm/package/cpan.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,25 +51,25 @@ def input(package)
require "json"

if File.exist?(package)
moduledir = package
@moduledir = package
result = {}
else
result = search(package)
tarball = download(result, version)
moduledir = unpack(tarball)
@moduledir = unpack(tarball)
end

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

# Merge the MetaCPAN query result and the metadata pulled from the local
Expand Down Expand Up @@ -128,9 +128,9 @@ def input(package)
logger.info("Installing any build or configure dependencies")

if attributes[:cpan_sandbox_non_core?]
cpanm_flags = ["-L", build_path("cpan"), moduledir]
cpanm_flags = ["-L", build_path("cpan"), @moduledir]
else
cpanm_flags = ["-l", build_path("cpan"), moduledir]
cpanm_flags = ["-l", build_path("cpan"), @moduledir]
end

# This flag causes cpanm to ONLY download dependencies, skipping the target
Expand Down Expand Up @@ -195,8 +195,10 @@ def input(package)
end
end
end #no_auto_depends
end # def input

::Dir.chdir(moduledir) do
def build_and_install
::Dir.chdir(@moduledir) do
# TODO(sissel): install build and config dependencies to resolve
# build/configure requirements.
# META.yml calls it 'configure_requires' and 'build_requires'
Expand Down Expand Up @@ -279,7 +281,7 @@ def input(package)
if ::Dir.entries(parent).sort == ['.', '..'].sort
FileUtils.rmdir parent
else
break

end
end
end
Expand All @@ -299,7 +301,20 @@ def input(package)
self.architecture = "native"
end
end
end
end # def build_and_install

def shell_code
build_procedure = ""
install_procedure = ""
if File.exist?("Build.PL") # Module::Build
build_procedure = "perl Build.PL\n./Build\n#{attributes[:cpan_test?] ? "./Build test\n" : "" }"
install_procedure = "./Build install\n"
elsif File.exist?("Makefile.PL") # ExtUtils::MakeMaker
build_procedure = "perl Makefile.PL\nmake\n#{attributes[:cpan_test?] ? "make test\n" : "" }"
install_procedure = "make install\n"
end
return { :build_procedure => build_procedure, :install_procedure => install_procedure }
end # def shell_code

def unpack(tarball)
directory = build_path("module")
Expand Down
15 changes: 12 additions & 3 deletions lib/fpm/package/gem.rb
Original file line number Diff line number Diff line change
Expand Up @@ -76,16 +76,25 @@ def staging_path(path=nil)

def input(gem)
# 'arg' is the name of the rubygem we should unpack.
path_to_gem = download_if_necessary(gem, version)
@path_to_gem = download_if_necessary(gem, version)

# Got a good gem now (downloaded or otherwise)
#
# 1. unpack it into staging_path
# 2. take the metadata from it and update our wonderful package with it.
load_package_info(path_to_gem)
install_to_staging(path_to_gem)
load_package_info(@path_to_gem)
end # def input

def build_and_install
install_to_staging(@path_to_gem)
end # def build_and_install

def shell_code
build_procedure = ""
install_procedure = "gem install #{File.basename(@path_to_gem)}"
return { :build_procedure => build_procedure, :install_procedure => install_procedure }
end # def shell_code

def download_if_necessary(gem, gem_version)
path = gem
if !File.exist?(path)
Expand Down

0 comments on commit 89cbea2

Please sign in to comment.