Skip to content

Commit

Permalink
ffi/loadlib: add module (#1938)
Browse files Browse the repository at this point in the history
Provide a `ffi.loadlib` helper, replacing `ffi.load` and
`util.ffiLoadCandidates` for loading our libraries.
  • Loading branch information
benoit-pierre authored Sep 23, 2024
1 parent fd34cb2 commit 7b07c9e
Show file tree
Hide file tree
Showing 23 changed files with 126 additions and 150 deletions.
1 change: 1 addition & 0 deletions Makefile.defs
Original file line number Diff line number Diff line change
Expand Up @@ -1037,6 +1037,7 @@ eval "$$($(LUAROCKS_BINARY) path)";
export TESSDATA_DIR="$$PWD/data";
./luajit -e 'require "busted.runner" {standalone = false}' /dev/null
--exclude-tags=notest
--helper=ffi/loadlib.lua
--output=gtest
--sort-files
"$$@";
Expand Down
2 changes: 1 addition & 1 deletion ffi/blitbuffer.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2676,7 +2676,7 @@ BB.TYPE_TO_BPP = {

BB.has_cblitbuffer = false
-- Load the C blitter, and default to using it if available
BB.has_cblitbuffer, cblitbuffer = pcall(ffi.load, "blitbuffer")
BB.has_cblitbuffer, cblitbuffer = pcall(ffi.loadlib, "blitbuffer")
if BB.has_cblitbuffer then
-- If we can, assume we'll want to use it
use_cblitbuffer = true
Expand Down
10 changes: 1 addition & 9 deletions ffi/crypto.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,7 @@ LuaJIT FFI wrapper for libcrypto (OpenSSL).
local ffi = require("ffi")
require("ffi/crypto_h")

local libcrypto
if ffi.os == "Windows" then
libcrypto = ffi.load("libs/libcrypto.dll")
elseif ffi.os == "OSX" then
libcrypto = ffi.load("libs/libcrypto.1.1.dylib")
else
libcrypto = ffi.load("libs/libcrypto.so.1.1")
end

local libcrypto = ffi.loadlib("crypto", "1.1")
local crypto = {}

function crypto.pbkdf2_hmac_sha1(pass, salt, iterations, key_len)
Expand Down
9 changes: 1 addition & 8 deletions ffi/freetype.lua
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,7 @@ local C = ffi.C
-- the header definitions
require("ffi/freetype_h")

local ft2
if ffi.os == "Windows" then
ft2 = ffi.load("libs/libfreetype-6.dll")
elseif ffi.os == "OSX" then
ft2 = ffi.load("libs/libfreetype.6.dylib")
else
ft2 = ffi.load("libs/libfreetype.so.6")
end
local ft2 = ffi.loadlib("freetype", "6")

local freetypelib = ffi.new("FT_Library[1]")
assert(ft2.FT_Init_FreeType(freetypelib) == 0, "Couldn't initialize Freetype!")
Expand Down
2 changes: 1 addition & 1 deletion ffi/harfbuzz.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
local coverage = require("ffi/harfbuzz_coverage")
local ffi = require("ffi")
local hb = ffi.load("libs/libharfbuzz." .. (ffi.os == "OSX" and "0.dylib" or "so.0"))
local hb = ffi.loadlib("harfbuzz", "0")
local HB = setmetatable({}, {__index = hb})

require("ffi/harfbuzz_h")
Expand Down
4 changes: 2 additions & 2 deletions ffi/input_pocketbook.lua
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ require("ffi/inkview_h")
-- emulates those in a semaphore step-locked thread.
local compat, compat2 = inkview, inkview
if not pcall(function() local _ = inkview.PrepareForLoop end) then
compat = ffi.load("inkview-compat")
compat = ffi.loadlib("inkview-compat")
compat2 = compat
elseif not pcall(function() local _ = inkview.GetTouchInfoI end) then
compat2 = ffi.load("inkview-compat")
compat2 = ffi.loadlib("inkview-compat")
end

local input = {
Expand Down
9 changes: 1 addition & 8 deletions ffi/jpeg.lua
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,10 @@ local BB = require("ffi/blitbuffer")
require("ffi/posix_h")
require("ffi/turbojpeg_h")

local turbojpeg
-- The turbojpeg library symbols are versioned, so it should always be
-- backward compatible: the major & patch numbers are always 0, and when
-- a new API version is made available, the minor number is incremented.
if ffi.os == "Windows" then
turbojpeg = ffi.load("libs/libturbojpeg.dll")
elseif ffi.os == "OSX" then
turbojpeg = ffi.load("libs/libturbojpeg.0.3.0.dylib")
else
turbojpeg = ffi.load("libs/libturbojpeg.so.0.3.0")
end
local turbojpeg = ffi.loadlib("turbojpeg", "0.3.0", "turbojpeg")

local Jpeg = {}

Expand Down
13 changes: 2 additions & 11 deletions ffi/koptcontext.lua
Original file line number Diff line number Diff line change
Expand Up @@ -72,17 +72,8 @@ local C = ffi.C
require("ffi/koptcontext_h")
require("ffi/leptonica_h")
local Blitbuffer = require("ffi/blitbuffer")
local leptonica, k2pdfopt
if ffi.os == "Windows" then
leptonica = ffi.load("libs/libleptonica-6.dll")
k2pdfopt = ffi.load("libs/libk2pdfopt-2.dll")
elseif ffi.os == "OSX" then
leptonica = ffi.load("libs/libleptonica.6.dylib")
k2pdfopt = ffi.load("libs/libk2pdfopt.2.dylib")
else
leptonica = ffi.load("libs/libleptonica.so.6")
k2pdfopt = ffi.load("libs/libk2pdfopt.so.2")
end
local leptonica = ffi.loadlib("leptonica", "6")
local k2pdfopt = ffi.loadlib("k2pdfopt", "2")

local KOPTContext = {
k2pdfopt = k2pdfopt -- offer the libraries' functions to other users
Expand Down
81 changes: 81 additions & 0 deletions ffi/loadlib.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
local ffi = require("ffi")
local android = ffi.os == "Linux" and os.getenv("IS_ANDROID") and require("android")
local log = android and android.LOGI or print

local lib_search_path
local lib_basic_format
local lib_version_format

local function libname(name, version)
return string.format(version and lib_version_format or lib_basic_format, name, version)
end

local function findlib(...)
local name, version = ...
if not name then
return
end
log("ffi.findlib: " .. name .. (version and (" [" .. version .. "]") or ""))
local lib = libname(name, version)
local path = package.searchpath(lib, lib_search_path, "/", "/")
if path then
return path
end
return findlib(select(3, ...))
end

local ffi_load = ffi.load

ffi.load = function(lib, global)
log("ffi.load: " .. lib .. (global and " (RTLD_GLOBAL)" or ""))
return ffi_load(lib, global)
end

ffi.loadlib = function(...)
local lib = findlib(...) or libname(...)
return ffi.load(lib)
end

if android then
-- Note: our libraries are not versioned on Android.
lib_search_path = android.nativeLibraryDir .. "/?"
lib_basic_format = "lib%s.so"
lib_version_format = "lib%s.so"
-- Android need some custom code for KOReader Lua modules loaded
-- with `require("libs/libkoreader-xxx")`, but actually stored
-- under the application's directory for native libraries.
table.insert(package.loaders, 1, function (modulename)
if modulename:sub(1, 17) ~= "libs/libkoreader-" then
return
end
local path = android.nativeLibraryDir .. "/" .. libname(modulename:sub(9))
log(string.format("package.loadlib: %s [%s]", path, modulename))
return package.loadlib(path, "luaopen_" .. modulename:sub(18))
end)
elseif ffi.os == "Linux" then
lib_search_path = "libs/?"
lib_basic_format = "lib%s.so"
lib_version_format = "lib%s.so.%s"
elseif ffi.os == "OSX" then
-- Apple M1 homebrew installs libraries outside of default search paths,
-- and dyld environment variables are sip-protected on MacOS, cf.
-- https://github.com/Homebrew/brew/issues/13481#issuecomment-1181592842
local libprefix = os.getenv("KO_DYLD_PREFIX")
if not libprefix then
local std_out = io.popen("brew --prefix", "r")
if std_out then
libprefix = std_out:read("*line")
std_out:close()
end
end
lib_search_path = "libs/?"
if libprefix then
lib_search_path = lib_search_path .. ";" .. libprefix .. "/lib/?"
end
lib_basic_format = "lib%s.dylib"
lib_version_format = "lib%s.%s.dylib"
end

log("lib_search_path: " .. lib_search_path)
log("lib_basic_format: " .. lib_basic_format)
log("lib_version_format: " .. lib_version_format)
2 changes: 1 addition & 1 deletion ffi/mupdf.lua
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ require("ffi/posix_h") -- for malloc

local BlitBuffer = require("ffi/blitbuffer")

local W = ffi.load("libs/libwrap-mupdf.so")
local W = ffi.loadlib("wrap-mupdf")
local M = W

--- @fixme: Don't make cache_size too low, at least not until we bump MµPDF,
Expand Down
8 changes: 1 addition & 7 deletions ffi/pic.lua
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,7 @@ local giflib
local ensure_giflib_loaded = function()
if giflib then return end
require("ffi/giflib_h")
if ffi.os == "Windows" then
giflib = ffi.load("libs/libgif-7.dll")
elseif ffi.os == "OSX" then
giflib = ffi.load("libs/libgif.7.dylib")
else
giflib = ffi.load("libs/libgif.so.7")
end
giflib = ffi.loadlib("gif", "7")
end

local GifPage = PicPage:extend{}
Expand Down
9 changes: 1 addition & 8 deletions ffi/png.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,7 @@ Currently, this is a LuaJIT FFI wrapper for lodepng lib.
local ffi = require("ffi")
require("ffi/lodepng_h")

local lodepng
if ffi.os == "Windows" then
lodepng = ffi.load("libs/liblodepng.dll")
elseif ffi.os == "OSX" then
lodepng = ffi.load("libs/liblodepng.dylib")
else
lodepng = ffi.load("libs/liblodepng.so")
end
local lodepng = ffi.loadlib("lodepng")

local Png = {}

Expand Down
9 changes: 1 addition & 8 deletions ffi/utf8proc.lua
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,7 @@ local C = ffi.C
require("ffi/posix_h")
require("ffi/utf8proc_h")

local libutf8proc
if ffi.os == "Windows" then
libutf8proc = ffi.load("libs/libutf8proc-3.dll")
elseif ffi.os == "OSX" then
libutf8proc = ffi.load("libs/libutf8proc.3.dylib")
else
libutf8proc = ffi.load("libs/libutf8proc.so.3")
end
local libutf8proc = ffi.loadlib("utf8proc", "3")

local Utf8Proc = {}

Expand Down
48 changes: 9 additions & 39 deletions ffi/util.lua
Original file line number Diff line number Diff line change
Expand Up @@ -60,28 +60,10 @@ int WideCharToMultiByte(
);
]]

local getlibprefix = function()
-- Apple M1 homebrew installs libraries outside of default searchpaths,
-- and dyld environment variables are sip-protected on MacOS, cf. https://github.com/Homebrew/brew/issues/13481#issuecomment-1181592842
local libprefix = os.getenv("KO_DYLD_PREFIX")

if not libprefix then
local std_out = io.popen("brew --prefix", "r")
if std_out then
libprefix = std_out:read("*line")
std_out:close()
end
end

return libprefix
end

require("ffi/posix_h")

local util = {}

util.KO_DYLD_PREFIX = ffi.os == "OSX" and getlibprefix() or ""

if ffi.os == "Windows" then
util.gettime = function()
local ft = ffi.new('FILETIME[1]')[0]
Expand Down Expand Up @@ -644,18 +626,6 @@ function util.multiByteToUTF8(str, codepage)
end
end

function util.ffiLoadCandidates(candidates)
local lib_loaded, lib
for _, candidate in ipairs(candidates) do
lib_loaded, lib = pcall(ffi.load, candidate)
if lib_loaded then
return lib
end
end
-- we failed, lib is the error message
return false, lib
end

local isAndroid = nil
--- Returns true if Android.
-- For now, we just check if the "android" module can be loaded.
Expand All @@ -674,15 +644,15 @@ local libSDL2 = nil
--- Returns SDL2 library
function util.loadSDL2()
if libSDL2 == nil then
local candidates, err
if ffi.os == "OSX" then
candidates = {"libs/libSDL2-2.0.dylib", util.KO_DYLD_PREFIX .. "/lib/libSDL2-2.0.0.dylib", "SDL2"}
else
candidates = {"libs/libSDL2-2.0.so.0", "libSDL2-2.0.so.0", "SDL2"}
end
libSDL2, err = util.ffiLoadCandidates(candidates)
if not libSDL2 then
print("SDL2 not loaded:", err)
local ok
ok, libSDL2 = pcall(ffi.loadlib,
"SDL2-2.0", 0,
"SDL2-2.0", nil,
"SDL2", nil
)
if not ok then
print("SDL2 not loaded:", libSDL2)
libSDL2 = false
end
end
return libSDL2 or nil
Expand Down
4 changes: 2 additions & 2 deletions ffi/webp.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ local BB = require("ffi/blitbuffer")

require("ffi/libwebp_h")

-- local libwebp = ffi.load("libs/libwebp." .. (ffi.os == "OSX" and "7.dylib" or "so.7"))
-- local libwebp = ffi.loadlib("webp", "7")
-- We only need the stuff provided by libwebpdemux.so (which itself uses libwebp.so)
local libwebpdemux = ffi.load("libs/libwebpdemux." .. (ffi.os == "OSX" and "2.dylib" or "so.2"))
local libwebpdemux = ffi.loadlib("webpdemux", "2")

local Webp = {
webp_decoder = nil,
Expand Down
9 changes: 1 addition & 8 deletions ffi/zipwriter.lua
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,7 @@ local ffi = require "ffi"
require "ffi/zlib_h"

-- We only need to wrap 2 zlib functions to make a zip file
local _zlib
if ffi.os == "Windows" then
_zlib = ffi.load("libs/libz1.dll")
elseif ffi.os == "OSX" then
_zlib = ffi.load("libs/libz.1.dylib")
else
_zlib = ffi.load("libs/libz.so.1")
end
local _zlib = ffi.loadlib("z", 1)
local function zlibCompress(data)
local n = _zlib.compressBound(#data)
local buf = ffi.new("uint8_t[?]", n)
Expand Down
9 changes: 1 addition & 8 deletions ffi/zlib.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,7 @@ LuaJIT FFI wrapper for zlib.
local ffi = require("ffi")
require("ffi/zlib_h")

local libz
if ffi.os == "Windows" then
libz = ffi.load("libs/libz1.dll")
elseif ffi.os == "OSX" then
libz = ffi.load("libs/libz.1.dylib")
else
libz = ffi.load("libs/libz.so.1")
end
local libz = ffi.loadlib("z", 1)

local zlib = {}

Expand Down
9 changes: 1 addition & 8 deletions ffi/zstd.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,7 @@ local C = ffi.C
require("ffi/posix_h")
require("ffi/zstd_h")

local zst
if ffi.os == "Windows" then
zst = ffi.load("libs/libzstd.dll")
elseif ffi.os == "OSX" then
zst = ffi.load("libs/libzstd.1.dylib")
else
zst = ffi.load("libs/libzstd.so.1")
end
local zst = ffi.loadlib("zstd", "1")

local zstd = {}

Expand Down
2 changes: 1 addition & 1 deletion spec/unit/font_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ describe("Low level font interfaces", function()
it("FreeType and HarfBuzz FFI should work", function()
assert.has_no.errors(function()
-- HL wrappers are omitted deliberately - faster bisect in case those fail, but we don't.
local ft = ffi.load("libs/libfreetype." .. (ffi.os == "OSX" and "6.dylib" or "so.6"))
local ft = ffi.loadlib("freetype", "6")
require("ffi/freetype_h")
local hb = require("ffi/harfbuzz")

Expand Down
2 changes: 1 addition & 1 deletion spec/unit/koptcontext_spec.lua
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
local ffi = require("ffi")
local KOPTContext = require("ffi/koptcontext")
local mupdf = require("ffi/mupdf")
local k2pdfopt = ffi.load("libs/libk2pdfopt.so.2")
local k2pdfopt = ffi.loadlib("k2pdfopt", "2")

local sample_pdf = "spec/base/unit/data/Alice.pdf"
local paper_pdf = "spec/base/unit/data/Paper.pdf"
Expand Down
Loading

0 comments on commit 7b07c9e

Please sign in to comment.