LINUX.ORG.RU

Дёргание SOшки из Love файла - обходной путь.

 , , , ,


3

1

Ну… не совсем из love файла, но около того.
В общем так, для начала сразу скажу что делать так не надо.
Но если очень хочется то можно.

В чём суть, вы например написали программу/игру на Love2D и вам вдруг ну очень хочется добавить к ней вашу библиотеку типа libcool.so в которой что-то делается и вы эту библиотеку вызываете из вашего lua кода, только вот беда. Если вы создали love файл mycoolgame.love и положили в него libcool.so ваш код не сможет загрузить библиотеку libcool.so ибо mycoolgame.love это zip архив. Ну, не получится и всё тут. Но если очень хочется то есть черезжопный метод, мы внутри нашего love файла создадим архив с нашей библиотекой и возможно дополнительными файлами, при запуске, мы монтируем этот архив и распаковываем в каталог игры и автоматически добавляем пути до распакованных файлов в cpath и path.

Например вот наша библиотека которую мы хотим распространять вместе с нашей игрой прямо в love файле.

#include <stdio.h>

#ifdef LUAJIT
#include <luajit-2.1/lua.h>
#include <luajit-2.1/lualib.h>
#include <luajit-2.1/lauxlib.h>
#else
#include <lua5.1/lua.h>
#include <lua5.1/lualib.h>
#include <lua5.1/lauxlib.h>
#endif

int example_c_function(lua_State* L)
{
    int a = lua_tointeger(L,1);
    a+=a;
    lua_pushnumber(L,a);
    lua_pushstring(L,"hello from c");
    return 2;
}

int luaopen_lib(lua_State* L)
{
    static const struct luaL_Reg nativeFuncLib [] =
    {
         {"example_c_function", example_c_function},
         {NULL, NULL}
    };
    luaL_register(L, "lib", nativeFuncLib);
    return 1;
}

Соберём её

gcc -DLUAJIT=1 main.c `pkg-config --libs --cflags luajit` --shared -o lib.so

Аахивируем её

mkdir libs
cp lib.so libs/lib.so
zip -r9 libs.zip libs

А вот собственно сама суть и сам механизм распаковки и импорта путей.

function autoreq(zip)
    if not zip then
       print("[autoreq] failed get zip archive for unpack, argument is 'nil'")
       return false;
    end
    local function unpack(dirname,mount_point,base)
        local items = love.filesystem.getDirectoryItems(mount_point)
        if items then
            for _,val in pairs(items) do
                local path = dirname..'/'..val;
                local path_mount = mount_point..'/'..val;
                if love.filesystem.getInfo(path_mount,'directory') then
                   package.path  = package.path  ..';'..base..'/'..path..'/?.lua;';
                   package.cpath = package.cpath ..';'..base..'/'..path..'/?.so;';
                   love.filesystem.createDirectory(path)
                   unpack(path,path_mount,base)
                 elseif love.filesystem.getInfo(path_mount,'file')then
                        print("[autoreq] unpack -> "..val)
                        love.filesystem.write(path,love.filesystem.read(path_mount));
                else
                   print("[autoreq] ok -> "..val)
                end
            end
        end
    end
    local base = love.filesystem.getSaveDirectory();
    print("[autoreq] check depends in '"..base.."'")
    local dirname = "libs"
    local mount_point = "_autoreq_libs_"
    local dir = love.filesystem.createDirectory(dirname)
    local dir = love.filesystem.createDirectory(mount_point)
    data, err = love.filesystem.newFileData(zip)
    if not data then
       print("[autoreq] failed get zip archive for unpack -> '"..zip.."'")
       return false;
    end
    local success,msg = love.filesystem.mount(data,mount_point)
    unpack(dirname,mount_point,base);
    love.filesystem.setRequirePath ( package.path  );
    love.filesystem.setCRequirePath( package.cpath );
    local success,msg = love.filesystem.unmount(data);
    love.filesystem.remove(mount_point);
    print("[autoreq] all done okey")
    return true;
end

autoreq("libs.zip"); -- вызываем распаковку архива с библиотекой и импорта путей
require('lib'); -- вызываем нашу библиотеку

function love.load()
print(lib.example_c_function()) -- вызываем функцию из неё
end

Создаём love файл с нашей программой и её зависимостями

zip -r9 coolgame.love  main.lua libs.zip

Запускаем

love coolgame.love 
[autoreq] check depends in '/home/dron/.local/share/love/coolgame'
[autoreq] unpack -> lib.so
[autoreq] all done okey
0	hello from c

Всё работает, наша soшка может распространяться в обычном love файле. Будет создана такая структура каталогов. В автоматическом каталоге игры.

dron@gnu:~/.local/share/love$ tree 
.
└── coolgame
    └── libs
        └── libs
            └── lib.so

4 directories, 1 file
dron@gnu:~/.local/share/love$ 

Ну вот собственно и всё. На деле можно в lib.zip насовать произвольные файлы, с произвольными каталогами, например выполнить сборку openssl/luasec/luasocket

luarocks --tree `pwd`/luasec install openssl
luarocks --tree `pwd`/luasec install luasec
zip -9 -r libs.zip luasec -x 'luasec/lib/luarocks*'

И получившийся libs.zip просто добавить в свой love файл тем самым получив всё что нужно для работы с https в вашей программе. Я так и сделал в своей проверялке новостей например. Работать будет всё абсолютно прозрачно, ничего в коде учитывать и менять не надо. В том и прелесть. Ну разве что один раз вызвать autoreq('libs.zip') и всё.

Да, теряется смысл в переносимости love файлов ведь теперь там платформоспецифичные библиотеки таскаются. Но это просто вариант таскания с собой soшки если уж надо, но без всяких appimage упаковок и прочего, по иному дёрнуть внешнюю библиотеку из своей поставки из архива, просто никак нельзя. Но порой вот надо бывает. Я голову ломал довольно долго если честно пока не допёрло. Может кому пригодится.

Вроде всё. Досвиданья.

★★★★★

Последнее исправление: LINUX-ORG-RU (всего исправлений: 2)

Название autoreq звучит глупо лучше autopath какой. Но это вычищенный код, у меня там ещё и require("blabla") производится от имён soшек чтобы вообще всё автоматически, но я это убрал так как там специфичные вызовы и вам они ненужны и было бы непонятно. Ну, как-то так. Осталось название не к месту, ну и ладно.

LINUX-ORG-RU ★★★★★
() автор топика

Да, теряется смысл в переносимости love файлов ведь теперь там платформоспецифичные библиотеки таскаются.

Обычно, когда дело доходит до таскания бинарников, то собирают экзешник, в который статически линкуют и сам lua, и все используемые бибилотеки, и (байт)код всех используемых pure-lua модулей. И никаких зипов)

annulen ★★★★★
()

Получается, что это что-то вроде love.filesystem.mount()? Или у тебя как-то по-другому?

Как тебе такой способ:

function love_load_so(soname, init_function)
    local ffi = require("ffi")
          ffi.cdef("int memfd_create(const char *name, unsigned int flags);")
    local fd = ffi.C.memfd_create(soname, 0)
    if fd > 0 then
        local fdfile = "/proc/self/fd/" .. fd
        local f = io.open(fdfile, "w")
              f:write(love.filesystem.read(soname))
              f:close()
        local lib = package.loadlib(fdfile, init_function)
        return lib()
    end
end

local mylib = love_load_so("mylib.so", "luaopen_mylib")
      mylib.myfun()


Не создаётся никаких файлов, всё в памяти. Но такой способ, естественно, только для Линукса.

anonymous
()
Ответ на: комментарий от annulen

Да, но, нет. Вернее, придётся отказаться от например libmpeg123,OpenALsoft и ещё чего-то там. Они под LGPL и всё в статику собирать немножко лицензионно неудобно. Ну или своё лицензировать соответствующие. Но так то да, за некоторыми исключениями всё можно собрать ровно в 1 бинарник. Но только я ещё не пробовал. Да и пока нужды такой нет.

LINUX-ORG-RU ★★★★★
() автор топика
Ответ на: комментарий от LINUX-ORG-RU

Они под LGPL и всё в статику собирать немножко лицензионно неудобно.

Они из системы берутся или идут свои в комплекте? Если второе и игра опенсорсная, то проблем особых нет. Если проприетарная, то да, неудобно, возня с объектниками того не стоит.

annulen ★★★★★
()
Ответ на: комментарий от annulen

В дистрибутивах из системы. И SDL2 и прочее прочее. Если конпелять, то всё на твоё усмотрение конечно.

Если второе и игра опенсорсная, то проблем особых нет.

Ну как нет, есть. Я считаю бредом что лицензия выбирается по наитию и зависит от типа сборки. А не от разумного её выбора. Ну, тут тема для целого выступления. Но да неудобно немножечко, даже в случае сохранения простой разрешительной лицензии. Хотя казалось бы, весь си и луа код можно хранить отдельно, но вот дистрибуция, это уже другое. Ну да ладно. Есть множество вариантов таскать с собой зависимости. tar.gz никто не отменял, а сейчас есть и более дружественные варианты типа appimage сборка всего в один исполняемый файл это больше вопрос перфекционизма, хотя порой это очень удобно, тут не поспорить.

LINUX-ORG-RU ★★★★★
() автор топика
Последнее исправление: LINUX-ORG-RU (всего исправлений: 2)
Ответ на: комментарий от LINUX-ORG-RU

В дистрибутивах из системы. И SDL2 и прочее прочее.

Если речь про дистрибутивный пакет, который поддерживается мейнтейнером в официальном репозитории, то тут вообще никаких проблем. Даже явно несовместимости лицензий можно обойти, если соответствующие бинарники лежат в разных пакетах (пример: Qt под LPGL + OpenSSL<3.0 под своей лицензией).

Ну как нет, есть. Я считаю бредом что лицензия выбирается по наитию и зависит от типа сборки. А не от разумного её выбора.

Что-то не улавливаю суть. LGPL — лицензия очень удобная. Она совместима с любыми опенсорсными лицензиями, даже с вещами вроде CDDL. И никому не навязывает себя использовать, в отличие от GPL.

сборка всего в один исполняемый файл это больше вопрос перфекционизма

В принципе да, но пользователям такой подход нравится.

annulen ★★★★★
()
Ответ на: комментарий от annulen

Если речь про дистрибутивный пакет, который поддерживается мейнтейнером в официальном репозитории, то тут вообще никаких проблем

Согласен, тем и удобны love файлы так как я знаю что love2d есть в поставке любого дистра. И не над вообще парится на счёт того как запускать, хоть linux,хоть bsd хоть даже haiku.

Что-то не улавливаю суть. LGPL — лицензия очень удобная.

Это я относительно варианта статической сборки, а так да. Всё верно.

но пользователям такой подход нравится.

Да он всем нравится, когда программа просто работает это прекрасно и для пользователей и для разрабов. В динамично развивающемся линуксе (и не только) ты часто становишься рабом своей программы и вынужден её поддерживать потому что с завидной периодичностью ломаются зависимости. Но, это большая и многогранная песня и петь её можно долго и есть множество всяких но. В целом да, если есть вариант собрать всё в статику пусть не как основной вариант, а как дополнительный то это хорошо. Особенно когда дело касается игр, ведь игры это программы в себе и вопросы их работоспособности стоят гораздо строже и серьёзнее чем в большинстве другого софта. Игры это вообще один из самых серьёзных классов ПО. :)

LINUX-ORG-RU ★★★★★
() автор топика
Ответ на: комментарий от LINUX-ORG-RU

тем и удобны love файлы так как я знаю что love2d есть в поставке любого дистра

Но если использовать зависимости из дистра, а саму игру не опакечивать, то можно нарваться на бинарную несовместимость. Хотя тут смотря какие либы, у SDL и OpenAL должно быть всё хорошо со стабильностью ABI, но так не у всех, вообще не у всех.

annulen ★★★★★
()
Ответ на: комментарий от LINUX-ORG-RU

Это я относительно варианта статической сборки

LGPL не запрещает статическую сборку, и ей совсем наплевать на тип линковки в случае, если игру может пересобрать из исходников любой желающий.

В динамично развивающемся линуксе (и не только) ты часто становишься рабом своей программы и вынужден её поддерживать потому что с завидной периодичностью ломаются зависимости

А если собрать статический бинарник, который ни от чего не зависит, кроме ядра, то он будет работать вечно. Ну, возможно, с некоторыми оговорками. Я недавно, например, узнал, что в Gentoo у glibc есть юз-флаг hash-sysv-compat и он по дефолту выключен, а для очень старых бинарников это нужно.

annulen ★★★★★
()
Ответ на: комментарий от annulen

Если писать правильно, учитывая версии love без своего кода в библиотеках, только чистый lua код. То бинарная совместимость побоку. В love файле по хорошему должны быть лишь lua скрипты и ресурсы типа, музыки, картинок,шрифтов и подобного. А дальше если в системе есть love то оно всё это просто запускает. В том и смысл love файлов что у тебя чистый и переносимый на любую платформу код, задача love этот кож запустить. А уж как, дело десятое. Если собирать love самому и поставлять самому, то никаких уже системных библиотек кроме libc,libGL и что там ещё нужно для SDL2. Короче тут в целом вопрос касающийся всех. На крайний случай как бы парадоксально не звучало один из самых переносимых варантов это архив для винды с love файлом запущенный в wine =)

LINUX-ORG-RU ★★★★★
() автор топика
Ответ на: комментарий от annulen

узнал, что в Gentoo у glibc есть юз-флаг hash-sysv-compat и он по дефолту выключен, а для очень старых бинарников это нужно.

Ну, в целом linux это как по мне вообще не про бинарную переносимость, это скорее наоборот лютая антипереносимость. На любой чих по хорошему пересборка, а если не повезёт правка исходников ещё. А если очень не повезёт то правка исходников зависимостей. На то будет ли твоя программа хотя бы запускаться через года три всем плевать и скорее всего нет, не будет твоя программа запускаться если ты полагаешься на дистрибутив который обновляется как хочет и куда хочет. Либо ты с собой таскаешь вообще всё что только можешь унести, либо ты на постоянной основе пляшешь вокруг своей программы постоянно внося правки успевая за дистрибутивами и апстримом либо у тебя есть хоть какая то абстракция и некое подобие платформы где основные моменты решают за тебя, а ты держишь себя строго в рамках продуманной хоть как-то архитектуры обуславливающей просто запуск ПО.

Ой короче, сложна. Всё ситуативное и каждый частный случай уникальный. Как только твоя программа делает что-то кроме показа картинки и воспроизведения звука через библиотеки прослойки типа SDL2 то добро пожаловать в АД. Поэтому лучшее средство для переносимости и умение себя ограничивать и жить следуя разумным компромиссам, если выходишь за рамки то всё, будь добр соответствовать темпу жизни и бороться возникшими проблемами, ну или обходить их. Ну или болт положить на некий пласт пользователей. Или ещё что.

А если собрать статический бинарник, который ни от чего не зависит, кроме ядра, то он будет работать вечно.

Возможно, но собрать например GTK+ программу так что-бы она работала на любом дистрибутиве. Эта задачка мягко говоря, эдакая.

LINUX-ORG-RU ★★★★★
() автор топика
Последнее исправление: LINUX-ORG-RU (всего исправлений: 1)