Class: Libpng::Recipe

Inherits:
MiniPortileCMake
  • Object
show all
Defined in:
lib/libpng/recipe.rb

Overview

MiniPortile-based recipe for building libpng from source. Mirrors the pattern used by emf2svg-ruby: during gem install, ext/extconf.rb invokes Recipe#cook, which downloads the libpng source tarball, runs configure + make, and installs the shared library into the gem's lib/ directory. The pre-compiled gems (built via rake gem:native:<plat>) ship the .so/.dylib/.dll and disable extconf.rb entirely.

Constant Summary collapse

LIBPNG_URL =

Pinned libpng source URL + sha256. Bump deliberately to refresh the upstream — and remember to update both fields together.

"https://downloads.sourceforge.net/project/libpng/libpng16/#{Libpng::LIBPNG_VERSION}/libpng-#{Libpng::LIBPNG_VERSION}.tar.gz".freeze
LIBPNG_SHA256 =

sha256 of the libpng-X.Y.Z.tar.gz tarball. Verify with:

curl -sL <URL> | shasum -a 256
'8c9b05b675ca7301a458df2c2e46f26e1d41ff36b8863f8c33530bc58c2e6225'.freeze
ROOT =
Pathname.new(File.expand_path('../..', __dir__))

Instance Method Summary collapse

Constructor Details

#initializeRecipe

Returns a new instance of Recipe.



25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/libpng/recipe.rb', line 25

def initialize
  super('libpng', Libpng::LIBPNG_VERSION)

  @files << {
    url: LIBPNG_URL,
    sha256: LIBPNG_SHA256
  }

  @target = ROOT.join(@target).to_s
  @printed = {}
  setup_cross_compile if cross_compile?
end

Instance Method Details

#checkpointObject



53
54
55
# File 'lib/libpng/recipe.rb', line 53

def checkpoint
  File.join(@target, "#{name}-#{version}-#{target_platform}.installed")
end

#configure_defaultsObject



57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/libpng/recipe.rb', line 57

def configure_defaults
  # Build a static+self-contained shared library: we only ship the .so
  # to consumers, so libpng's transitive dep on zlib must be linked in.
  opts = super
  opts << '-DPNG_SHARED=ON'
  opts << '-DPNG_STATIC=OFF'
  opts << '-DPNG_TESTS=OFF'
  opts << '-DPNG_FRAMEWORK=OFF'
  # macOS: avoid the .framework build; we want a plain .dylib.
  opts << '-DCMAKE_INSTALL_LIBDIR=lib'
  opts << '-DCMAKE_BUILD_TYPE=Release'
  opts
end

#cookObject



48
49
50
51
# File 'lib/libpng/recipe.rb', line 48

def cook
  super
  FileUtils.touch(checkpoint)
end

#cook_if_notObject

libpng ships a CMake build alongside the autotools one. We use CMake for consistency with mini_portile2's defaults and to handle Windows cleanly (autotools on native Windows is fragile). No additional configuration is needed; defaults build PNG_SHARED=ON and PNG_STATIC=OFF, which is what we want.



44
45
46
# File 'lib/libpng/recipe.rb', line 44

def cook_if_not
  cook unless File.exist?(checkpoint)
end

#execute(action, command, command_opts = {}) ⇒ Object



100
101
102
# File 'lib/libpng/recipe.rb', line 100

def execute(action, command, command_opts = {})
  super(action, command, command_opts.merge(debug: false))
end

#installObject



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/libpng/recipe.rb', line 71

def install
  super
  # After `make install`, the shared lib lives under ports/<name>/<ver>/.
  # On Linux/macOS that's lib/. On Windows, CMake's GNUInstallDirs puts
  # the .dll in bin/ and the import library (.dll.a) in lib/ — we only
  # ship the .dll, so search both.
  libs = Dir.glob(File.join(port_path, shared_lib_install_glob))
  raise "no libpng shared lib produced under #{port_path}" if libs.empty?

  target_dir = ROOT.join('lib', 'libpng')
  FileUtils.mkdir_p(target_dir)
  FileUtils.cp_r(libs, target_dir, verbose: true)

  verify_libs
end

#message(text) ⇒ Object



104
105
106
107
108
109
110
111
112
113
# File 'lib/libpng/recipe.rb', line 104

def message(text)
  return super unless text.start_with?("\rDownloading")

  match = text.match(/(\rDownloading .*)\(\s*\d+%\)/)
  pattern = match ? match[1] : text
  return if @printed[pattern]

  @printed[pattern] = true
  super
end

#verify_libsObject



87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/libpng/recipe.rb', line 87

def verify_libs
  each_built_lib do |path|
    out, st = Open3.capture2("file #{path}")
    raise "Failed to query file #{path}: #{out}" unless st.exitstatus.zero?

    next if target_format.eql?('skip')

    raise "Invalid file format '#{out}', /#{target_format.source}/ expected" unless target_format.match?(out)

    message("Verifying #{path} ... OK\n")
  end
end