LINUX.ORG.RU

Сообщения LINUX-ORG-RU

 

Опубликован релиз кандидат эталонной реализации языка Lua 5.4.7-rc1

Новости — Разработка(не подтверждено)
Опубликован релиз кандидат эталонной реализации языка Lua 5.4.7-rc1
Группа Разработка

Язык Lua – это мощный, переносимый, легковесный, встраиваемый и простой язык программирования.
Разработанный и развиваемый Роберту Иерузалимски в PUC-Rio вот уже более 30 лет.

Это минормый релиз посвящённый исправлению ошибок и улучшению документации, к использованию приглашаются все кого затронули ошибки прошлой весрии и все кто имеет желание протестировать текущую. Если вы заметили ошибку или желаете проявить любую коммуникацию относительно языка то это возможно исключительно через список рассылки, любые патчи, рекомендации приветсвуются, но важно помнить что они с вашей стороны будут всегда нести рекомендательный характер и будут так восприняты. Разработка Lua ведётся в закрытой форме, публикуются только релизы.

Язык Lua встроен или является основой в таких программах как:

  • nmap;
  • mpv;
  • Conky;
  • Redis;
  • Ardor;
  • Apache;
  • Love2d;
  • Defold;
  • INSTEAD;
  • Celestia;
  • Awesome;
  • Vim/nvim;
  • MediaWiki;
  • Tarantool;
  • Wireshark;
  • Darktable;
  • NetBSD Kernel;
  • NGINX OpenResty;
  • Damn Small Linux;

И многих, многих других, включая возможность использования языка в ядре Linux через Lunatik разрабатываемый в лаборатории LabLua. Lua является в первою очередь библиотекой и существует для встраивания в программы как язык компаньон, но вместе с этим вполне успешно может быть использован как самостоятельный язык для решения множества прикладных задач занимающий всего 260 килобайт места в ПЗУ или добавляющий этот размер к программе если слинкован с ней статически.

>>> Подробности

 , , ,

LINUX-ORG-RU
()

Видео записывалка для вашего ПеКа

Форум — Talks

Я внезапно открыл для себя утилиту slop, очень удобная и полезная в домашних делах. Ага я слоупок.
Надо было записать кусочек экрана несколько раз и удобно выделять мышкой область на экране для записи видео.

Наваял вот, может кому пригодится.

  • Переменная use_recorder задаёт то чем вы ходите записывать
    • ffmpeg
    • recordmydesktop
  • Если передать скрипту аргументы то они прокинутся к утилите записи
    • например record --no-sound если используется recordmydesktop
      • но прокидывайе аргументы с умом рм рф и прочую копипасту туда писать не надо.

Ключи под себя

  • ffmpeg быстро пишет и сразу кодирует без звука.
  • recordmydesktop со звуком и отложенным долгим кодированием

Сам использую нубский recordmydesktop так как вокруг него не надо с бубном бегать по поводу звука (он меня сломан на ПК и чисто программный идёт по сети)

touch $HOME/.local/bin/record
#copy-paste code
chmod +x $HOME/.local/bin/record
  • Прерывать запись через ctrl+C
  • Зависимости в теле написаны какие надо.

Если кажется многословным перепишете на bash, всё можно переписать на bash :)

#!/usr/bin/env lua
-----------------------------------------
-- Depends for recordmydesktop: ogv out
-- apt install lua slop recordmydesktop
-----------------------------------------
-- Depends for ffmpeg: mp4 out
-- apt install lua slop ffmpeg
-----------------------------------------
-- Use ffmpeg or recordmydesktop recorder
--local use_recorder = 'ffmpeg'
local use_recorder = 'recordmydesktop'
-------------------------------------------------------------------------------
local separator = ' '
local arguments = table.concat({...},separator)
local visualfmt = '-b 5 -c 0.5,1.0,0.5,0.8 -o'
local slop_pipe = io.popen('slop -f "%x %y %w %h" '..visualfmt)
local x,y , w,h = slop_pipe:read('*l'):match('(%d+) (%d+) (%d+) (%d+)')
-------------------------------------------------------------------------------
local out_filename = os.date('%Y-%m-%H-%M-%S');
-------------------------------------------------------------------------------
local command = {};
-------------------------------------------------------------------------------
if use_recorder == 'ffmpeg' then
   command = table.concat(
   {
       'ffmpeg ','-f x11grab';
                 '-video_size',w..'x'..h;
                 '-grab_x',x;
                 '-grab_y',y;
                 '-i :0.0';
                 '-framerate 60';
                 '-vcodec libx264';
                  arguments;
                  out_filename..'.mp4';
   },separator)
   print('Run command:\n'..command)
   os.execute(command)
   return 0
end
-------------------------------------------------------------------------------
if use_recorder == 'recordmydesktop' then
   command = table.concat(
   {
       'recordmydesktop','-x='..x;
                         '-y='..y;
                         '--width='..w;
                         '--height='..h;
                         '--fps=60';
                          arguments;
                         '-o',out_filename..'.ogv';
   },separator)
   print('Run command:\n'..command)
   os.execute(command)
   return 0
end
-------------------------------------------------------------------------------
print("[ERROR]: Bad 'use_recorder' value.")
return 1

Вроде мелочёвка, а очень и очень удобно.
Всё. Досвиданья.

 , slop, , ,

LINUX-ORG-RU
()

Как понять что ты тупой?

Форум — Talks

Очень просто! Нужно встретить задачу которая понятна буквально на интуитивном уровне. Открыть редактор и не суметь её решить сходу.
В голове переклинивает, ты точно знаешь что тебе надо делать и ступоришься на том как делать. Замираешь и смотришь на экран как додик последний. Кабзда, если извилины шевелятся то у моих кататония. Это фиаско братан :D Я не знаю с чем это сравнить, это как налить чаю, взять ложку и думать, тебе туда соль сыпать или сахар и офигевать от того что ыт об этом вообще задумываешься. Это просто пример, такой шизы у меня нету. Но хотелось яркого сравнения.

Короче сижу я такой и думаю дай полистаю PIL четвёртое издание для Lua, а то память у меня как решето постоянно всё забываю и надо по кругу гонять одно и тоже, ну типа листаю и листаю, типа ну ага, ну эге и тут задачка банальная как банан.

Exercise 6.5: Write a function that takes an array and prints all combinations of the elements in the array.
(Hint: you can use the recursive formula for combination: C(n,m) = C(n -1, m -1) + C(n - 1, m). To generate
all C(n,m) combinations of n elements in groups of size m, you first add the first element to the result and
then generate all C(n - 1, m - 1) combinations of the remaining elements in the remaining slots; then you
remove the first element from the result and then generate all C(n - 1, m) combinations of the remaining
elements in the free slots. When n is smaller than m, there are no combinations. When m is zero, there is
only one combination, which uses no elements.)

Упражнение 6.5: Напишите функцию, которая принимает массив и выводит все комбинации элементов в массиве.
(Подсказка: вы можете использовать рекурсивную формулу для комбинации: C (n,m) = C(n -1, m -1) + C(n - 1, m). Чтобы сгенерировать
все C(n, m) комбинаций из n элементов в группах размером m, вы сначала добавляете первый элемент к результату и
затем генерируете все C(n - 1, m - 1) комбинации оставшихся элементов в оставшихся ячейках; затем вы
удаляете первый элемент из результата и затем генерируете все C(n - 1, m) комбинации оставшихся
элементов в свободных ячейках. Когда n меньше, чем m, комбинаций не существует. Когда m равно нулю, существует
только одна комбинация, в которой не используются элементы.)

Для таких же тупых как я, нужно вывести все комбинации из элементов массива, комбинации должны быть заданного размера, а не просто длинны массива, исходный массив не должен быть модифицирован, реализация должна быть на основе хвостовой рекурсии, которую Lua автоматически устраняет.

И йя не смог. Вот прям сразу взять и написать. Истории успеха это конечно здорово, когда всё получается, когда всё складно и ладно, выезжаешь во всё лишь получив вводные. Вот про это все пишут все это смакуют. А вот провалы, спотыкания на ровном месте, затупы вселенских масштабов. Они у вас бывают? А как часто? Есть тру стори, похожего характера, когда вас прям переклинивало/ет? В расчёт не берутся случаи когда вы я фиг знаю дебажите неизвестный вам протокол или делаете что-то для области знаний где для вас вся терминология в новинку, там то понятно сходу по определению невозможно ничего сделать. Но вот в обыденных ситуациях, ну или не совсем оных. Признавайтесь! Чё я один такой глупый? :D

А линукс тут при том что линукс для умных.

 , , , профнепригодность,

LINUX-ORG-RU
()

У кого на улице сугробы?

Форум — Talks

Ну не сугробы, но пару сантиметров уже навалило. Метель стоит как в феврале.

 , , , ,

LINUX-ORG-RU
()

Отрицательная масса у фундаментальных (и не только) «частиц»

Форум — Science & Engineering

Беседа с физиком теоретиком: Анатолием Григорьевичем Шкловским и его теоретические выкладки по поводу отрицательной массы и как она проявляется. Влияя и объясняя как фундаментальные так и макроскопические процессы и явления.

Кратко – Допущение существования элементарных частиц с отрицательной массой. И объяснение через них таких вещей к примеру как наличие воды на земле как косвенное следствие вытеснения протонов из земного ядра и регистрируемые сверх энергетические космические лучи с энергиями в терраэлектронвольт и многое многое другое включая прямое объяснение огромного количества других уже существующих физических эффектов, явлений, наблюдений. То есть при должной теоретической части уже подтверждается текущими экспериментами (вернее теория объясняет то что в них происходит)

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

Ты физик? Жопа горит? :) Опровергни или бабабол :D
Дискас.

 , , , ,

LINUX-ORG-RU
()

Чего Debian штормит?

Форум — Talks

Не ставится gnumeric полез поискать пакет

Service Unavailable

The server is temporarily unable to service your request due to maintenance downtime or capacity problems. Please try again later.
Apache Server at packages.debian.org Port 443

А до этого микродрама

И там сообщения ниже по ходу драмы.

Раньше такой фигни не было. Есть новости? Мол «Мы там что-то обновляем блабла»? Не могу найти.

Полез уточнять

Да удалили из testing. Но без причины (вернее она есть, но не повод для удаления)

Чёблин опять на stable отказываться или на до unstable обновляться? :)

Смысла в этом посте нет, я просто побубнить. И apt новый == дура!

Я всё понимаю это testing специально на нём и сижу, но чёт со всех сторон по мелочи, то это то сё. Для меня всё это дикость, *ть лет всё как часы, и на тебе кучка мелких пакостей разом. Gnumeric через пару дней (или прямо сейчас) вернут, не страшно. Эт я так к слову что поисковик падать стал.

 

LINUX-ORG-RU
()

DOOM 30 Лет. МЕГАВАД от русского сообщества всем в подарок.

Форум — Games

Сама новость и описание на gamedev.ru



Закидываете в один каталог

  • 30years.wad
  • DOOM2.WAD
  • GZDoom-g4.11.1-x86_64.AppImage

И запускаете

./GZDoom-g4.11.1-x86_64.AppImage -file 30years.wad

DOOM2.WAD подхватится сам

UDP: Тут ниже чёт панику наводят про звонки от киркоровых и шифровальщики. Помнитя, бинарники левые эта всегда апасна. А то мало ли… Ну и есть firejail (не советую с appimage люто тормозит, обычные архивы брать) или вируталки если что, а то мало ли чё! Воть

 , , ,

LINUX-ORG-RU
()

Вырос на годик 🥳

Форум — Talks

 , , , инкремент,

LINUX-ORG-RU
()

А у кого то работает CSGO Legacy?

Форум — Games

КС2 вышла, но непоиграть вулкана нету да и эмуляцию опкодов я ещё не допилил в форке op_emu. Но захотелось хоть в паблике побегать полчасика, качнул CSGO Legacy сменив параметрах бета версии в настройках запуска, а оно мне Ошибка сегментирования :( Смысла писать куда то им нет, поддержки игры больше нет.

Запустил под strace у меня валится после vfork Пока не особо разбирался, но пока разбираюсь может у кого вдруг работает и это чисто у меня проблемы.


Решение

cp "/home/$USER/.steam/steam/ubuntu12_32/steam-runtime/amd64/lib/x86_64-linux-gnu/libpng12.so.0.46.0" \
"/home/$USER/.steam/debian-installation/steamapps/common/Counter-Strike Global Offensive/bin/linux64/libpng12.so.0"

Путь до ubuntu12_32/... и steamapps/... может быть чуть разный

Игру запускать через терминал из каталога игры

  • ./csgo.sh Игра только с ботами

или

  • ./csgo.sh -steam Игра на паблик серверах

В ServerBrowser поломаны шрифты, оно прогружает список серверов но показывает пустоту, если кликнуть туда где должны быть названия сереров происходит подключение и можно гамать онлайн с людьми. Пока не знаю как шрифты пофиксить.

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

  • UPD переключить язык на английский в настройках игры в стиме.

 , , , ,

LINUX-ORG-RU
()

Qemu + Android x86 + Поворот экрана.

Форум — Development

Зашёл я значит сюда

Взял скрипт, перевёл его в Makefile.

#----------------------------------------#
THIS_DIR=$(shell pwd)
ANDROID_API=31
ANDROID_ARCH=x86_64
ANDROID_HOME=$(THIS_DIR)/emulator
PHONE_MODEL=Nexus_5
#----------------------------------------#
mkdir:
	mkdir -p $(ANDROID_HOME);
#----------------------------------------#
tools:
	export ANDROID_HOME=$(ANDROID_HOME); \
	sdkmanager "emulator"                \
	           "cmdline-tools;latest"    \
	           "platforms;android-31"    \
	           "platform-tools"
#----------------------------------------#
imgdep:
	export ANDROID_HOME=$(ANDROID_HOME);                 \
	$(ANDROID_HOME)/cmdline-tools/latest/bin/sdkmanager  \
	     "system-images;android-$(ANDROID_API);default;$(ANDROID_ARCH)"
#----------------------------------------#
create:
	export ANDROID_HOME=$(ANDROID_HOME);                            \
	$(ANDROID_HOME)/cmdline-tools/latest/bin/avdmanager create avd  \
	--tag default --package "system-images;android-$(ANDROID_API);default;$(ANDROID_ARCH)" --sdcard 64M \
	--device "$(PHONE_MODEL)" --name $(PHONE_MODEL)_API_$(ANDROID_API)
#----------------------------------------#
start_emulator:
	export ANDROID_HOME=$(ANDROID_HOME);                        \
	$(ANDROID_HOME)/emulator/emulator @Nexus_5_API_$(ANDROID_API)
#----------------------------------------#
create_emulator: mkdir tools imgdep create

Жму такой make create_emulator start_emulator и такой думаю, ща я буду устанавливать все игрыapk, а не копировать по тормозному bluetooh на железо… А оно мне, «Где мои 17 лет на большой каретной», предъявите говорит SSE4. Ну вот :(
Некоторые пишут что можно задать эмулятор как x86, но оно валится всё ещё на этапе загрузки. А не запуска как с x86_64

Ладна, попёрся сюда

Накатил в qemu android-x86-9.0-r2.iso пересобрал apk с поддержкой x86 и всё работает. Но!!!

Великая беда и я не нашёл в интернете ответа.
Может ли qemu сказать запущенной в нем ОС что экран перевернулся?
Или в android x86 что-то дёрнуть? Может через ndk как что дрыгнуть можно?

Просто в qemu android x86 работает в каком-то режиме планшета,
изменение размера окна просто растягивает пропорционально картинку и всё.

Запускается всё если что вот так, может важно. В ~/.bashrc функция

kvm-qrun() 
{
    kvm -drive file=$1,format=raw  --device virtio-vga-gl -display gtk,gl=on -m 4096 -cpu host -smp  4 --enable-kvm  -net user,hostfwd=tcp::9922-:22 -net nic
}

kvm-qrun android.qcow2

Есть мысли? Как экран то повернуть? Я понимаю что если получится оно реально будет верх ногами например так как откуда qemu знает что надо перевёрнутую картинку ещё раз перевернуть уже чисто для показа, но хоть как, или никак? :(

Если нет то ладно, хоть запускать можно всякое для проверки и то хорошо. Но, всё же вдруг как то можно.


UPD: Никак

 , , ,

LINUX-ORG-RU
()

И Снег и Сугробы и Ёлки на вашем ПеКа

Форум — Talks
--           Зависимости              -
---------------------------------------
-- curl    -  загрузка ёлочек         -
-- scrot   -  снимок рабочего стола   -
-- love2d  -  запуск снежка и ёлочек  -
---------------------------------------
-- Создать каталог `новый_год`        -
-- Создать в каталоге файл `main.lua` -
-- Скопировать в файл этот код.       -
-- Используя терминал запутсить из    -
-- созданного каталога программу      -
--                                    -
--   Вот так запустить  `love .`      -
---------------------------------------
--            Управление              -
---------------------------------------
-- Нажать 1 - установит фон рабочего  -
--            стола в программе, как  -
--            будто на нём идёт снег  -
---------------------------------------
-- Нажать 2 - убрать сугробы, но снег -
--            то падает и они вырастут-
--            снова, но другие уже :) -
---------------------------------------
-- Нажать 3 - Загрузить и отобразить  -
--            просту ёлочку           -
---------------------------------------
-- Нажать 4 - Загрузить и установить  -
--            нарядную новогоднюю ёлку-
---------------------------------------
-- Нажать 5 - Вернуть чёрный фон      -
---------------------------------------
--    Нажать esc или q для выхода     -
---------------------------------------
-- Программа проверяет время и если   -
---------------------------------------
-- Время 23:45 - Загружается и отобра -
--               жается простая ёлочка-
---------------------------------------
-- Время 00:00 - Загружается и отобра -
--               жается нарядная ёлка -
---------------------------------------
-- Эти действия по времени происходят -
-- автоматически предупреждая о под-  -
-- ходе и наступлении нового года гы  -
---------------------------------------
-- Устанавливать ли фон автоматически -
---------------------------------------
local autobackround = false; --вкл/выкл
---------------------------------------
local sugrob = {}
local snow   = {};
local snow_opacity = 0;
local screen_width = 0;
local screen_height= 0;
local tree_image = nil;
local background = nil;
local tree_opacity = 0;
local tree_url =
{
  [1] = 'https://i.ibb.co/f24xCMp/1.png';
  [2] = 'https://i.ibb.co/KLpSKdZ/2.png';
};
local once_call_tree_1 = true;
local once_call_tree_2 = true;
----------------------------------------
function get_app_path()
    return love.filesystem.getSaveDirectory()..'/';
end
function load_tree(id)
   if love.system.getOS() == 'Android' then
      if id == 1 then
         tree_image = love.graphics.newImage('tree_1.png');
      end
      if id == 2 then
         tree_image = love.graphics.newImage('tree_2.png');
      end
      tree_opacity = 0;
      return;
   end
   local path = get_app_path();
   os.execute('curl '..tree_url[id]..' --output '..path..'tree.png');
   if love.filesystem.getInfo('tree.png')  then
      tree_image = love.graphics.newImage('tree.png');
      tree_opacity = 0;
   end
end
function draw_tree()
    if tree_image then
    local w = love.graphics.getWidth()
    local h = love.graphics.getHeight()
    local sx,sy = w/tree_image:getWidth(),h/tree_image:getHeight();
    love.graphics.setColor(1,1,1,tree_opacity);
       love.graphics.draw(tree_image,0,0,0,sx,sy);
    end
end
function update_tree(dt)
    if tree_opacity < 1 then
       tree_opacity = tree_opacity + 0.1 * dt;
    end
end
function create_sugrob()
    if love.system.getOS() == 'Android' then
       return;
    end
    local x = love.graphics.getWidth()
    local y = love.graphics.getHeight()
    local s = x/y;
    for i=1,50 do
        local xp = love.math.random(0,x);
        local cs = love.math.random(s*25,s*75);
        table.insert(sugrob,{'fill',xp,y,cs,150})
    end
end
function draw_sugrob()
    if love.system.getOS() == 'Android' then
       return;
    end
    love.graphics.setColor(0.95,0.95,1,1);
    for i=1,#sugrob do
        love.graphics.circle(sugrob[i][1],sugrob[i][2],sugrob[i][3]+sugrob[i][5],sugrob[i][4]);
    end
end
function update_sugrob(dt)
    local x,y = love.window.getMode();
    for i=1,#sugrob do
        if sugrob[i][5] > 0 then
           sugrob[i][5] = sugrob[i][5] - love.math.random(0.0,i*0.05)
        end
    end
end
function create_snow()
    local x = love.graphics.getWidth()
    local y = love.graphics.getHeight()
    local s = x/y;
    for i=1,60 do
        local xp = love.math.random(0,x);
        local xy = love.math.random(0,y);
        local cs = love.math.random(s*1,s*10);
        table.insert(snow,{'fill',xp,xy,cs,0})
    end
end
function draw_snow()
    for i=1,#snow do
        love.graphics.setColor(0.95,0.95,1,snow_opacity);
        love.graphics.circle(snow[i][1],snow[i][2],snow[i][3],snow[i][4]+snow_opacity);
    end
end
function lerp(from, to, t)
  return t < 0.5 and from + (to-from)*t or to + (from-to)*(1-t)
end
function update_snow(dt)
    local x,y = love.window.getMode();
    if snow_opacity < 0.7 then
       snow_opacity = snow_opacity + 0.1 * dt;
    end
    for i=1,#snow do
        snow[i][3] = snow[i][3] + i * 0.5;
        if snow[i][5] == 1 then
           snow[i][2] = snow[i][2] + i * 0.25 + i * 0.5 + love.math.random(0,3);
       elseif snow[i][5] == 0 then
           snow[i][2] = snow[i][2] - i * 0.25 + i * 0.5 + love.math.random(0,3);
        end
        if(snow[i][3] > y + snow[i][4]) then
          snow[i][3] = -snow[i][4];
          snow[i][2] = love.math.random(0,x);
          snow[i][5] = love.math.random(-1,1);
        end
    end
end
local button_colors =
{
    {0.5,0.0,0.0,1},
    {0.0,0.5,1.0,1},
    {0.0,0.0,1.0,1},
    {0.5,0.5,1.0,1},
    {0.0,0.5,1.0,1},
}
local button_circles = {}
local show_controls  = false;
function create_controls()
   local x = love.graphics.getWidth()
   local y = love.graphics.getHeight()
   local pose_y = (y * 0.5);
   local radius = (x * 0.01) * 5;
   for i=1,5 do
       local pose_x = radius*i * 3.5;
       table.insert(button_circles,{'fill',pose_x,pose_y,radius,button_colors[i]})
   end
end
function draw_controls()
   if show_controls then
      local b = button_circles;
      for i=1,#button_circles do
          love.graphics.setColor(unpack(b[i][5]));
          love.graphics.circle(b[i][1],b[i][2],b[i][3],b[i][4]);
      end
   end
end
function update_controls(px,py)
   if show_controls then
       local b = button_circles;
       for i=1,#button_circles do
           local cx,cy,cr = b[i][2],b[i][3],b[i][4];
           if((px - cx)^2 + (py - cy)^2 < cr^2) then
              if i == 1 then set_background(); end
              if i == 2 then sugrob = {}; create_sugrob(); end
              if i == 3 then load_tree(1);   end
              if i == 4 then load_tree(2);   end
              if i == 5 then background=nil; end
           end
       end
    end
end
function love.displayrotated(index, orientation)
    sugrob = {};
    create_sugrob();
    snow = {};
    create_snow();
    snow_opacity = 0;
    tree_opacity = 0;
    button_circles = {};
    create_controls();
end
function love.mousepressed(x,y)
    if love.system.getOS() ~= 'Android' then
       update_controls(x,y)
       show_controls = not show_controls;
    end
end
function love.touchpressed(b,x,y)
    update_controls(x,y)
    show_controls = not show_controls;
end
function love.load()
    love.filesystem.write('init','x',1);
    if love.filesystem.getInfo('icon.png') then
       love.window.setIcon(love.image.newImageData('icon.png'));
    end
    love.window.setTitle("Маааленькой ёёёлочке холодно зимооой.")
    if love.system.getOS() == 'Android' then
       love.window.setMode(1280,720,{fullscreen=true,resizable=true,vsync=true});
       screen_width = love.graphics.getWidth()
       screen_height = love.graphics.getHeight()
    else
       love.window.setMode(0,0,{resizable=true,borderless=true})
       love.window.setFullscreen(true,'desktop');
       screen_width = love.graphics.getWidth()
       screen_height = love.graphics.getHeight()
       love.window.setMode(0,0,{resizable=true,borderless=true})
       love.window.maximize()
    end
    create_sugrob()
    create_snow()
    create_controls()
end
function set_background()
   if love.system.getOS() == 'Android' then
      return;
   end
   local x,y = love.window.getMode();
   love.window.minimize()
   love.timer.sleep(1)
   local path = get_app_path()
   local status = os.execute('scrot -a '..screen_width-x..','..screen_height-y..','..x..','..y..' -o '..path..'screen.png');
   love.window.restore()
   love.window.maximize()
   if love.filesystem.getInfo('screen.png') then
      background = love.graphics.newImage('screen.png');
   end
end
if autobackround then
   set_background()
end
function love.keypressed(key)
   if key == 'q' or key == 'escape' then
      love.event.quit()
   end
   if key == '1' then set_background(); end
   if key == '2' then sugrob = {}; create_sugrob(); end
   if key == '3' then load_tree(1);   end
   if key == '4' then load_tree(2);   end
   if key == '5' then background=nil; end
end

function love.update(dt)
    if os.date('%H%M') == '2345' then
       if once_call_tree_1 then
          load_tree(1);
          once_call_tree_1 = false;
       end
    end
    if os.date('%H%M') == '0000' then
       if once_call_tree_2 then
          load_tree(2);
          once_call_tree_2 = false;
       end
    end
    update_snow(dt)
    update_tree(dt)
    update_sugrob(dt)
end
function love.draw()
    if background then
       love.graphics.setColor(1,1,1,1);
       love.graphics.draw(background)
    end
    draw_tree();
    draw_sugrob();
    draw_snow();
    draw_controls();
end

Внешний вид. Видео. Досвиданья.


По просьбе @apt_install_lrzsz сборки


Добавил кнопки появляются и исчезают по нажатию на экране.
Как альтернатива нажатия 1,2,3,4,5.
На Андроиде часть не работает.


В истории правок этого сообщения есть прошлая версия, если с этой пробелмы, но прошлая только для ПК.

 , , ёлки, ,

LINUX-ORG-RU
()

Воспроизведение ссылок через mpv на вашем ПеКа

Форум — Talks

Суть, вешаем скрипт на горячую клавишу, например F8,
копируем или просто выделяем ссылку, нажимаем F8 и воспроизводятся видива/фота.
Мне удобно выделить ссылку с любым медиаконтентом, например

  • фильмом
  • ютубом
  • музыкой
  • картинкой
  • порнух… ой!

Нажать горячую клавишу и всё.

Зависимости: apt install mpv lua xclip

#! /bin/env lua
---------------
local function play_mpv()
    local appclip = nil;
    local youtudl = nil;
    local session = os.getenv('XDG_SESSION_TYPE');
    local have_mpv  = os.execute('mpv --version 2>/dev/null');
    local have_xclip = os.execute('xclip -version 2>/dev/null');
    local have_ytdlp  = os.execute('yt-dlp --version 2>/dev/null');
    local have_wlpaste = os.execute('wl-paste --version 2>/dev/null');
    local have_youbedl  = os.execute('youtube_dl --version 2>/dev/null');
    if(not have_mpv) then
      os.execute('notify-send "Для работы требуется mpv плеер"');
      os.exit(1)
    end
    if(not session) then
      os.execute('notify-send "Неизвестный тип сессии, задайте x11 или wayland"');
      os.exit(2);
    end
    if(have_xclip and session == 'x11') then
       appclip = 'xclip -o';
    end
    if(have_wlpaste and session == 'wayland') then
       appclip = 'wlpaste -p';
    end
    if(not appclip) then
       os.execute('notify-send "Нужно установить xclip для X11 или wl-paste для Wayland"');
       os.exit(3);
    end
    if(io.popen(appclip):read('*l'):sub(1,4)~='http') then
       os.execute('notify-send "Это не ссылка для воспроизведения"');
       os.exit(4);
    end
    youtudl = have_youbedl and 'youtube_dl' or nil;
    youtudl = have_ytdlp   and 'yt-dlp'     or youtudl;
    if(youtudl) then
       os.execute('notify-send Воспроизводится "$('..
       youtudl..' --skip-download --get-title  "$('..appclip..')")\n"$('..appclip..')"" &');
    else
       os.execute('notify-send "Неизвестное название"');
    end
    local success, meta , code = os.execute('mpv --keep-open=yes --loop "$('..appclip..')" > /dev/null 2>&1');
    if tonumber(code) ~= 0 or success == nil then
       os.execute('notify-send -u normal Ошибка-mpv:'..meta..':'..code);
       os.exit(5);
    end
end
-- run run run
-- дрынь на-на
play_mpv();

Ну и предупреждение.

- ИСПОЛЬЗОВАТЬ НА СВОЙ СТРАХ И РИСК, Я НЕ НЕСУ НИКАКОЙ ОТВЕТСТВЕННОСТИ
- ВСЁ ПРЕДОСТАВЛЯЕТСЯ КАК ЕСТЬ, ЛЮБЫЕ СОМНЕНИЯ ДОЛЖНЫ ВЕСТИ
- К ОТКАЗУ ОТ ИСПОЛЬЗОВАНИЯ
- ТЕКСТ ИЗ БУФЕРА ОБМЕНА ПОПАДАЕТ В ИНТЕРПРЕТАТОР !!!!!!!!!
- В ОБОЛОЧКУ BASH КАК СТРОКА И ЕСЛИ ТАМ ЧТО НЕ ТАК ИЛИ ЭДАК
- И ВНЕЗАПНО ИСПОЛНИЛОСЬ ЧТО-ТО НЕ ТО, ТО ЭТО ИСКЛЮЧИТЕЛЬНО ВАША ВИНА И ВАШИ ПРОБЛЕМЫ

Тест: Выделить ссылку и нажать F8 (иксы автоматически копируют выделенное есчё)

Да, примитив. Но зато очень удобно.
Досвиданья :3

 , , , , удобное

LINUX-ORG-RU
()

Трассировка вызова функций - Call Нunter

Форум — Development

Разбирал тут хлам и нашёл у себя пару поделок для трассировки вызова функций, я уже не помню что я именно хотел, вроде хотел отладить что-то замудрёное и не понимал кто кого вызывает поэтому понавтыкал трассировку, но да ладно может кому пригодится. Суть есть короткие макросы которые вставляются в конец и начало функций, и всё. При работе программы будет показано кто кого вызывает. Реализаций две, чуть разные и чуть по разному показывают. Есть ограничение на количество вызовов, в первой реализации запись вызовов явно ограничена и при превышении будет неточность так как вызовы записываются все, во второй чуть получше, там подход к фреймам вызовов и они лишь должны уместится в размер фрейма, но нет полной трассировки как в первом случае. Ну компромисс, либо так, либо сяк. По сути, это ненужный тупак, но в своё время мне помогло. Каталог в котором валялись сорцы я в своё время назвал пафосно и по детски call hunter типа охотник за вызовами или позвоните Хантеру хехехех :D Ой лол, ладно. Пусть как есть так и будет, чё уж там :D Ну, ну и всё.

Первый вариант

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define IDXMAX 1062

#define RS static struct { int ok; char * file; char *  func;}HUNTER_RECORD_TOKEN##__FILE__##__LINE__##__func__= {.file = __FILE__, .func = (char*)&__func__,.ok=1};\
hunter_record_start((void *)&HUNTER_RECORD_TOKEN##__FILE__##__LINE__##__func__);\

#define RE { HUNTER_RECORD_TOKEN##__FILE__##__LINE__##__func__.ok = 0; hunter_record_end(); };\

unsigned long long idx = 0;
struct hunter_call
{
     int    ok;
     char * file;
     char * func;
};

struct hunter_record_data
{
    struct hunter_call * call;
    unsigned long long parent;
};

struct hunter_record_data  record[IDXMAX];

unsigned long long curent=0;

void hunter_print()
{
    for (int i = 0; i < idx; ++i)
    {
        unsigned long long prev = record[i].parent;
        unsigned long long prev_index = 0;
        while(record[prev].call != record[0].call)
        {
            prev = record[prev].parent;
            prev_index++;
        }
        while(prev_index--)
        {
            printf("│   ");
        }
        if(i==0)
        {
            printf("┣━━━━━━ frame ━━━━━━━>\n");
            printf("%s:%s() \n",record[i].call->file,record[i].call->func);
        }else{
            printf("└─> %s:%s() \n",record[i].call->file,record[i].call->func);
        }
     }
     unsigned long long prev = record[curent].parent;
     unsigned long long prev_index = 0;
     while(record[prev].call != record[0].call)
     {
         prev = record[prev].parent;
         prev_index++;

     }
     prev_index++;
     while(prev_index--)
     {
        printf("│   ");
     }
     printf("┗━> backtrace from ━━━> %s:%s() \n",record[curent].call->file,record[curent].call->func);
}

int overflow = 0;
unsigned long long overflow_count=0;
void hunter_record_start(void * data)
{
    struct hunter_call * hunter = (struct hunter_call*)data;
    if(idx == IDXMAX-1)
    {
      printf("============ stack is full, please set '#define IDXMAX %lld' for more data ===========\n",++overflow_count+IDXMAX);
      overflow=1;
      return;

    }else{
        record[idx].call = hunter;
        record[idx].parent = curent;
        curent = idx;
        idx++;
    }
}

void hunter_record_end()
{
  if(overflow)
  {
      return;
  }
  if(record[curent].call->ok != 0)
  {
      printf("\n In file -> %s in funtion -> %s() not set 'RE' \n\n",record[curent].call->file,record[curent].call->func);
      exit(1);
  }
  curent = record[curent].parent;
}
/*
#undef  RS
#define RS

#undef RE
#define RE
*/
void function_5() {RS   hunter_print(); RE}
void function_4() {RS   int x= 10; while(x--) function_5();  RE}
void function_3() {RS   function_4();  function_4(); function_4();  RE}
void function_1() {RS   function_3();   RE}
void function_2() {RS   function_1();  function_1(); function_1();  RE}
int main()
{
   RS
   for (int i = 0; i < 2; ++i)
   {
       function_2();
   }
   RE
}

Выхлоп будет такой.

...
│   └─> cc.c:function_1() 
│   │   └─> cc.c:function_3() 
│   │   │   └─> cc.c:function_4() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   └─> cc.c:function_4() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   └─> cc.c:function_4() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   │   ┗━> backtrace from ━━━> cc.c:function_5()

Вторая реализация

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

#if defined( _WIN32)  || defined( _WIN64 )  || defined( __CYGWIN__ )
#include <windows.h>
#endif
enum
{
  RED     = 31,
  GREEN   = 32,
  YELLOW  = 33,
  BLUE    = 34,
  PURPULE = 35,
  AQUA    = 36,
  DEFAULT = 39,
};

//-----------------------------------------------------------------------------
static void colorset(int color)
{
#if defined( __unix__  )
    if(isatty(fileno(stderr)) && isatty(fileno(stdout)))
    {
        printf("\x1B[%dm",color);
    }
#elif defined( _WIN32)  || defined( _WIN64 )  || defined( __CYGWIN__ )
    HANDLE handle_1 = (HANDLE) _get_osfhandle(fileno(stdout));
    DWORD mode_1;
    HANDLE handle_2 = (HANDLE) _get_osfhandle(fileno(stderr));
    DWORD mode_2;
    if(GetConsoleMode(handle_1,&mode_1) && GetConsoleMode(handle_2,&mode_2))
    {
       printf("\x1B[%dm",color);
    }
#endif
}

#define IDXMAX 127

#define RS \
struct hunter_call\
{\
     int    ok;\
     char * file;\
     char * func;\
     struct hunter_call * parent;\
     struct hunter_call * children;\
     time_t time_s;\
     time_t time_e;\
     unsigned long calls;\
};\
static struct hunter_call HUNTER_RECORD_TOKEN##__FILE__##__LINE__##__func__= {.calls=0,.file = __FILE__, .func = (char*)&__func__,.ok=1,.parent=NULL,.children=NULL,.time_e=0,.time_s=0};\
hunter_record_start((void *)&HUNTER_RECORD_TOKEN##__FILE__##__LINE__##__func__);\
HUNTER_RECORD_TOKEN##__FILE__##__LINE__##__func__.time_s = clock();\

#define RE  HUNTER_RECORD_TOKEN##__FILE__##__LINE__##__func__.time_e =clock();  HUNTER_RECORD_TOKEN##__FILE__##__LINE__##__func__.ok = 0; hunter_record_end(); \


unsigned long long idx = 0;
struct hunter_call
{
     int    ok;
     char * file;
     char * func;
     struct hunter_call * parent;
     struct hunter_call * children;
     time_t time_s;
     time_t time_e;
     unsigned long calls;
};

struct hunter_call * record = NULL;
struct hunter_call * curent = NULL;

void hunter_print( )
{
    setbuf(stdout,NULL);
    curent->time_e = clock();

    struct hunter_call * buff = record;
    while(buff != NULL)
    {
        if(buff == record)
        {
            colorset(AQUA);
            printf("┣━━━━━━ frame ━━━━━━━> \n");
            colorset(DEFAULT);
            printf("%s:%s()",buff->file,buff->func);
            colorset(GREEN);
            printf(" time %f sec (calls %ld)\n", difftime(curent->time_e,buff->time_s)/CLOCKS_PER_SEC,buff->calls);
            colorset(DEFAULT);
        }else{
            printf("└─> %s:%s()",buff->file,buff->func);
            colorset(GREEN);
            printf(" time %f sec (calls %ld)", difftime(curent->time_e,buff->time_s)/CLOCKS_PER_SEC,buff->calls);
            colorset(DEFAULT);
            if(buff == curent)
            {
                printf(" ──>\n");
            }else{
                printf("\n");
            };
        }
        if(buff == curent) {break;}
        buff = buff->children;
    }
    colorset(YELLOW);
    printf("┗━> backtrace from ━━━> %s:%s() \n",curent->file,curent->func);
    colorset(DEFAULT);
}

int overflow = 0;
unsigned long long overflow_count=0;
void hunter_record_start(void * data)
{
    colorset(DEFAULT);
    struct hunter_call * hunter = (struct hunter_call*)data;
    if(curent == NULL)
    {
        curent = hunter;
        hunter->parent   = NULL;
        curent = hunter;
        record = curent;
        hunter->calls++;
    }else if(curent != hunter){
        curent->children = hunter;
        hunter->parent   = curent;
        curent = hunter;
        hunter->calls++;
    }
}

void hunter_record_end()
{
    colorset(DEFAULT);
    if(curent->ok != 0)
    {
        printf("\n In file -> %s in funtion -> %s() not set 'RE' \n\n",curent->file,curent->func);
        exit(1);
    }
    curent = curent->parent;
}
#define HP hunter_print();
/*
#undef  RS
#define RS

#undef RE
#define RE

#undef HP
#define HP
*/
void function_x() {RS  int x=1000000; while(x--){};  HP  RE}
void function_5() {RS  function_x();  HP   RE}
void function_4() {RS  function_5();   function_5(); function_5();  HP RE}
void function_3() {RS  function_4();  HP  RE}
void function_1() {RS  function_3(); HP RE}
void function_2() {RS  function_1();  function_1(); function_1(); HP RE}
void function_6() {RS  function_4();  function_2(); HP RE}
int main()
{
   RS
   for (int i = 0; i < 2; ++i)
   {
     function_6();
   }
   RE
}

Выхлоп

┣━━━━━━ frame ━━━━━━━> 
cc-list.c:main() time 0.006517 sec (calls 1)
└─> cc-list.c:function_6() time 0.006508 sec (calls 1)
└─> cc-list.c:function_4() time 0.006503 sec (calls 1)
└─> cc-list.c:function_5() time 0.006499 sec (calls 1)
└─> cc-list.c:function_x() time 0.006495 sec (calls 1) ──>
┗━> backtrace from ━━━> cc-list.c:function_x() 
┣━━━━━━ frame ━━━━━━━> 
cc-list.c:main() time 0.006615 sec (calls 1)
└─> cc-list.c:function_6() time 0.006606 sec (calls 1)
└─> cc-list.c:function_4() time 0.006601 sec (calls 1)
└─> cc-list.c:function_5() time 0.006597 sec (calls 1) ──>
┗━> backtrace from ━━━> cc-list.c:function_5() 
┣━━━━━━ frame ━━━━━━━> 
cc-list.c:main() time 0.009705 sec (calls 1)
└─> cc-list.c:function_6() time 0.009696 sec (calls 1)
└─> cc-list.c:function_4() time 0.009691 sec (calls 1)
└─> cc-list.c:function_5() time 0.002997 sec (calls 2)
└─> cc-list.c:function_x() time 0.002995 sec (calls 2) ──>
┗━> backtrace from ━━━> cc-list.c:function_x() 
┣━━━━━━ frame ━━━━━━━> 
cc-list.c:main() time 0.009788 sec (calls 1)
└─> cc-list.c:function_6() time 0.009779 sec (calls 1)
└─> cc-list.c:function_4() time 0.009774 sec (calls 1)
└─> cc-list.c:function_5() time 0.003080 sec (calls 2) ──>
┗━> backtrace from ━━━> cc-list.c:function_5() 
┣━━━━━━ frame ━━━━━━━> 
cc-list.c:main() time 0.012853 sec (calls 1)
└─> cc-list.c:function_6() time 0.012844 sec (calls 1)
└─> cc-list.c:function_4() time 0.012839 sec (calls 1)
└─> cc-list.c:function_5() time 0.002990 sec (calls 3)
└─> cc-list.c:function_x() time 0.002986 sec (calls 3) ──>
┗━> backtrace from ━━━> cc-list.c:function_x() 
┣━━━━━━ frame ━━━━━━━> 
cc-list.c:main() time 0.012935 sec (calls 1)
└─> cc-list.c:function_6() time 0.012926 sec (calls 1)
└─> cc-list.c:function_4() time 0.012921 sec (calls 1)
└─> cc-list.c:function_5() time 0.003072 sec (calls 3) ──>
┗━> backtrace from ━━━> cc-list.c:function_5() 

...

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

 , , трассировка,

LINUX-ORG-RU
()

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

Форум — Development

Ну… не совсем из 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
()

Android + sdkmanager + пути

Форум — Development

Как экспортировать пути до утилит SDK/NDK если использовать sdknamager?

Есть проект, для него устанавливаю зависимости по сборке

	export ANDROID_SDK_ROOT=$(HOME)/.love-android-build-dir/ANDROID-SDK \
	sdkmanager --install "platforms;android-33";    \
	sdkmanager --install "ndk;23.2.8568313";        \
	sdkmanager --install "platform-tools;33.0.0";   \
	sdkmanager --install "build-tools;33.0.0";      \
	sdkmanager --install "cmdline-tools;latest";    \
	sdkmanager --licenses;

Потом этот проект собирается. Всё ок

export ANDROID_SDK_ROOT=$(HOME)/.love-android-build-dir/ANDROID-SDK; \
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64; \
./gradlew assembleNormalRecord;

sdkmanager NDK + SDK файлы раскидывает вот так

dron@gnu:~/.love-android-build-dir/ANDROID-SDK$ tree -L 2
.
├── build-tools
│   ├── 33.0.0
│   └── 33.0.1
├── cmdline-tools
│   └── latest
├── licenses
│   ├── android-sdk-license
│   ├── android-sdk-preview-license
│   ├── android-sdk-preview-license-old
│   └── intel-android-extra-license
├── ndk
│   ├── 23.2.8568313
│   └── 25.2.9519653
├── platforms
│   ├── android-33
│   └── android-34
└── platform-tools
    ├── adb
    ├── dmtracedump
    ├── e2fsdroid
    ├── etc1tool
    ├── fastboot
    ├── hprof-conv
    ├── lib64
    ├── make_f2fs
    ├── make_f2fs_casefold
    ├── mke2fs
    ├── mke2fs.conf
    ├── NOTICE.txt
    ├── package.xml
    ├── sload_f2fs
    ├── source.properties
    ├── sqlite3
    └── systrace

16 directories, 19 files
dron@gnu:~/.love-android-build-dir/ANDROID-SDK$ 

Я хочу собрать luasocket для android, для этого пишу Android.mk кидаю его в исходники luasocket и теперь я хочу вызвать просто ndk-build и вот тут затык. sdkmanager не просто разворачивает окружения, а там ещё всякие ndk/23.2.8568313/blabla nkd/25.2.9519653/blabla и прочие, когда собираю проект то просто указываю ANDROID_SDK_ROOT и gradlew с прочим Ant говном понимают что и откуда из этой каши брать. Образно я тоже понимаю, но если раньше в пути нужно было просто прописать 1 раз путь до NDK/SDK + JAVA_HOME то с sdkmanager непонятно, так как он плодит кучи каталогов с кучей всего пересекающегося, разбитых по версиям, не руками же всё каждый раз прописывать? в смысле пути до утилит и прочих программ из SDK/NDK Можно ли как-то у sdkmanager или от установленных им зависимостей по сборке запросить пусти чтобы их добавить к PATH автоматически на основе того что он же наустанавливал? И просто собрать soшку через ndk-build?

Андроид боль :( Хочется ругаться и стукнуть по столу, а ещё хочется что-то сломать. Но я пока держусь :3

 , , , ,

LINUX-ORG-RU
()

Makefile для сборки Love2d под Андроид/AppImage

Форум — Development

Сидел вот тут ковырялся, пересобирал и пердолился.
Подумал что стоит выложить, дабы другие пердолились поменьше.
Ниже описание, я его для себя писал и ещё ниже сам Makefile. Никаких изысков и красивостей. Собирает и ладно.
Кому надо под себя поправите, а кому не неадо, тому не надо.

- ИСПОЛЬЗОВАТЬ НА СВОЙ СТРАХ И РИСК, Я НЕ НЕСУ НИКАКОЙ ОТВЕТСТВЕННОСТИ
- ВСЁ ПРЕДОСТАВЛЯЕТСЯ КАК ЕСТЬ, ЛЮБЫЕ СОМНЕНИЯ ДОЛЖНЫ ВЕСТИ
- К ОТКАЗУ ОТ ИСПОЛЬЗОВАНИЯ, НЕ ПОНИМАЕШЬ ЧТО ЭТО, НЕ ТРОГАЙ

Love Distrib

Makefile упрощающий создание готовых Love2D сборок c включением вашего приложения позволяет

  • Собрать apk файл для Android
  • Собрать love файл для всех
  • Собрать AppImage файл для Linux x86_64

Положить Makefile в корень Love2D программы и отредактировать

  • app_name=имя программы
  • description=однострочное описание

Для Android дополнительно поправить

  • android_app_version_name=версия.приложения любая
  • android_app_version_code=число целое число

Важно android_app_version_code должно иметь значение
всегда увеличивающееся на 1 при каждой новой сборке, это
требуется для того чтобы при установке на Android apk
файла, уже установленное приложение обновлялось. Иначе
установка не произойдёт с выводом ошибки или вообще без неё.

Теперь однократно нужно развернуть зависимости для Android

  • make android-depends

Предполагается что sudo настроен, нужно установить java и
прочие мелочи, на запрос пароля ввести пароль для sudo затем
будут сгенерированны два ключа подписей и предварительно пароль

  • debug.keystore
  • release.keystore
  • .android_pass

На вопросы всякие можно просто нажимать ENTER, а когда спросят
всё ли верно Is CN=Unknown, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=Unknown correct? написать yes и снова нажать ENTER. sdkmanager
тоже спросит, Review licenses that have not been accepted (y/N)?
согласны ли вы с условиями предоставления иструментов
для Android SDK/NDK тоже написать y и нажать ENTER
Ключи и пароль можно подменить на свои готовые
(только молю бога не совать основные ЗАТРЁТСЯ МОЖЕТ БЫТЬ!)
Пусть сгенерируется всё само. Суть в том что без подписей пакетов
Android просто молча не установит apk, просто не даст установить.

А вот теперь уже можно, редактировать свой код и пересобирать

  • make lovefile просто обычный app.love
  • make android выполнить сборку app.apk, app.abb
  • make appimage выполнить сборку app.AppImage

Первый раз apk будет собираться долго, но все последующие уже быстро
так как компиляции уже не будет, а просто будут обновляться lua файлики
из проекта. Вроде всё.


Проблемы

  • make android валится с ошибкой Решения
  • Запускать make android ещё раз и ещё раз. Бывает отпускает.
  • Грохнуть java killall java (там несколько процессов может быть) и ещё раз запустить make android
  • Удалить /.love-android-build-dir и заного выполнить
    • make android-depends подготовку зависимостей
    • make android попытку сборки apk

Нельзя отменять сборку через CTRL+C это сломает всё,
придётся вышеописанный квест пройти опять, с большой долей вероятности.
Перемудрёные механизмы системы сборки для Android дают о себе знать. :3

SRC=$(shell pwd)
app_name=appname
description=app short description
##########################################################
android_app_name=$(app_name) for Android
android_app_version_name=0.1
android_app_version_code=1
android_enableJetifier=true
android_useAndroidX=true
android_app_application_id=org.$(app_name).android
android_app_orientation=portrait
#########################################################
SECRET_PASSWORD_FOR_KEYGEN=$(HOME)/.android_pass
#########################################################
android_sign_release=true
##########################################################
DEVELOPMENT_ROOT_DIR=$(HOME)/.love-android-build-dir
DEVELOPMENT_KEYS_DIR=$(HOME)/.love-android-keys-dir
##########################################################
ANDROID_SDK_ROOT_DIR=$(DEVELOPMENT_ROOT_DIR)/ANDROID-SDK/
ANDROID_LOVE_ROOT_DIR=$(DEVELOPMENT_ROOT_DIR)/LOVE-ANDROID/
APPIMAGE_LOVE_ROOT_DIR=$(DEVELOPMENT_ROOT_DIR)/APPIMAGE/
##########################################################
PROPERTIES=$(ANDROID_LOVE_ROOT_DIR)/gradle.properties

all:
	echo "make android-depends - create depends for android apk build"
	echo "make android         - build android apk/aabb pakages"
	echo "make lovefile        - create portable app.love file"
	echo "make appimage        - create portable x86_64 executable file"

android-depends:
	$(MAKE) install-love-android;
	$(MAKE) install-system-depends;
	$(MAKE) install-android-sdk-ndk;
	$(MAKE) generate-android-debug-keys;
	$(MAKE) generate-android-release-keys;

android:
	$(MAKE) generate-android-properties;
	$(MAKE) generate-application-icons;
	$(MAKE) packeges-build;
	$(MAKE) packeges-copy;
ifeq ($(android_sign_release),true)

	$(MAKE) packeges-sign-release;
else
	$(MAKE) packeges-sign-debug;
endif
	$(MAKE) packeges-sign-verify;

lovefile:
	zip -9 -r $(app_name).love . -x Makefile -x ./*.apk -x ./*.aab -x ./*.AppImage


SRC_IMAGE_ICON="NONE"
ifneq ($(wildcard $(SRC)/icon.png),)
SRC_IMAGE_ICON=$(SRC)/icon.png
DST_IMAGE_PATH=$(ANDROID_LOVE_ROOT_DIR)/app/src/main/res/drawable
endif
ifneq ($(wildcard $(SRC)/assets/icon.png),)
SRC_IMAGE_ICON=$(SRC)/assets/icon.png
DST_IMAGE_PATH=$(ANDROID_LOVE_ROOT_DIR)/app/src/main/res/drawable
endif
ifneq ($(wildcard $(SRC)/assets/images/icon.png),)
SRC_IMAGE_ICON=$(SRC)/assets/images/icon.png
DST_IMAGE_PATH=$(ANDROID_LOVE_ROOT_DIR)/app/src/main/res/drawable
endif




appimage:lovefile
	mkdir -p $(APPIMAGE_LOVE_ROOT_DIR);
ifeq ($(wildcard $(APPIMAGE_LOVE_ROOT_DIR)/love-11.5-x86_64.AppImage),)
	wget https://github.com/love2d/love/releases/download/11.5/love-11.5-x86_64.AppImage -O $(APPIMAGE_LOVE_ROOT_DIR)/love-11.5-x86_64.AppImage
	wget https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage  -O $(APPIMAGE_LOVE_ROOT_DIR)/appimagetool-x86_64.AppImage
endif
	cd $(APPIMAGE_LOVE_ROOT_DIR);                       \
	-rm -rd squashfs-root;                              \
	chmod +x love-11.5-x86_64.AppImage;                 \
	chmod +x appimagetool-x86_64.AppImage;              \
	./love-11.5-x86_64.AppImage --appimage-extract;     \
	cat squashfs-root/bin/love $(SRC)/$(app_name).love > squashfs-root/bin/$(app_name); \
	rm  squashfs-root/bin/love;                         \
	chmod +x squashfs-root/bin/$(app_name);             \
	sed 's/love/$(app_name)/g' -i squashfs-root/AppRun; \
	convert $(SRC_IMAGE_ICON) -resize 256x256 squashfs-root/$(app_name).png;  \
	echo ""                                   > squashfs-root/love.desktop;   \
	echo "[Desktop Entry]"                   >> squashfs-root/love.desktop;   \
	echo "Name=$(app_name)"                  >> squashfs-root/love.desktop;   \
	echo "Comment=$(description)"            >> squashfs-root/love.desktop;   \
	echo "MimeType=application/x-love-game;" >> squashfs-root/love.desktop;   \
	echo "Exec=$(app_name) %f"               >> squashfs-root/love.desktop;   \
	echo "Type=Application"                  >> squashfs-root/love.desktop;   \
	echo "Categories=Game;"                  >> squashfs-root/love.desktop;   \
	echo "Terminal=false"                    >> squashfs-root/love.desktop;   \
	echo "Icon=/$(app_name)"                 >> squashfs-root/love.desktop;   \
	echo "NoDisplay=true"                    >> squashfs-root/love.desktop;   \
	./appimagetool-x86_64.AppImage squashfs-root $(SRC)/$(app_name).AppImage;



install-love-android:
	-`git clone --recurse-submodules https://github.com/love2d/love-android/ $(ANDROID_LOVE_ROOT_DIR)`

generate-android-properties:
	echo "" > $(PROPERTIES)
	echo "app.name=$(android_app_name)" >> $(PROPERTIES)
	echo "app.version_name=$(android_app_version_name)" >> $(PROPERTIES)
	echo "app.version_code=$(android_app_version_code)" >> $(PROPERTIES)
	echo "android.enableJetifier=$(android_enableJetifier)" >> $(PROPERTIES)
	echo "android.useAndroidX=$(android_useAndroidX)" >> $(PROPERTIES)
	echo "app.application_id=$(android_app_application_id)" >> $(PROPERTIES)
	echo "app.orientation=$(android_app_orientation)" >> $(PROPERTIES)

install-system-depends:
	sudo apt install imagemagick openjdk-17-jdk openjdk-17-jre-headless sdkmanager

install-android-sdk-ndk:
	mkdir -p $(ANDROID_SDK_ROOT_DIR);                \
	export ANDROID_SDK_ROOT=$(ANDROID_SDK_ROOT_DIR); \
	sdkmanager --install "platforms;android-33";    \
	sdkmanager --install "ndk;23.2.8568313";        \
	sdkmanager --install "platform-tools;33.0.0";   \
	sdkmanager --install "build-tools;33.0.0";      \
	sdkmanager --install "cmdline-tools;latest";    \
	sdkmanager --licenses;

generate-android-debug-keys:
	mkdir -p $(DEVELOPMENT_KEYS_DIR);
ifeq ($(wildcard $(SECRET_PASSWORD_FOR_KEYGEN)),"")
	uuidgen > $(SECRET_PASSWORD_FOR_KEYGEN)
endif
ifeq ($(wildcard $(DEVELOPMENT_KEYS_DIR)/debug.keystore),"")
	keytool -genkey  \
			-v -keystore $(DEVELOPMENT_KEYS_DIR)/debug.keystore \
			-storepass:file   $(SECRET_PASSWORD_FOR_KEYGEN)     \
			-alias debug      \
			-keyalg RSA       \
			-keysize 2048     \
			-validity 100000
endif

generate-android-release-keys:
	mkdir -p $(DEVELOPMENT_KEYS_DIR);
ifeq ($(wildcard $(SECRET_PASSWORD_FOR_KEYGEN)),"")
	uuidgen > $(SECRET_PASSWORD_FOR_KEYGEN)
endif
ifeq ("$(wildcard $(DEVELOPMENT_KEYS_DIR)/release.keystore)","")
	keytool -genkey  \
			-v -keystore $(DEVELOPMENT_KEYS_DIR)/release.keystore \
			-storepass:file  $(SECRET_PASSWORD_FOR_KEYGEN)        \
			-alias release    \
			-keyalg RSA       \
			-keysize 2048     \
			-validity 100000
endif

generate-application-icons:
ifneq ($(SRC_IMAGE_ICON),"NONE")
	convert $(SRC_IMAGE_ICON) -resize 72x72   $(DST_IMAGE_PATH)-hdpi/love.png
	convert $(SRC_IMAGE_ICON) -resize 48x48   $(DST_IMAGE_PATH)-mdpi/love.png
	convert $(SRC_IMAGE_ICON) -resize 96x96   $(DST_IMAGE_PATH)-xhdpi/love.png
	convert $(SRC_IMAGE_ICON) -resize 144x144 $(DST_IMAGE_PATH)-xxhdpi/love.png
	convert $(SRC_IMAGE_ICON) -resize 192x192 $(DST_IMAGE_PATH)-xxxhdpi/love.png
endif

packeges-build:
	rm $(SRC)/*.apk;                                                \
	rm $(SRC)/*.aab;                                                \
	rm $(SRC)/*.love;                                               \
	rm $(SRC)/*.AppImage;                                           \
	export ANDROID_SDK_ROOT=$(ANDROID_SDK_ROOT_DIR);                \
	export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64;            \
	cp /bin/python3 $(HOME)/.local/bin/python;                      \
	rm -rd $(ANDROID_LOVE_ROOT_DIR)/app/src/embed/assets;           \
	mkdir -p $(ANDROID_LOVE_ROOT_DIR)/app/src/embed/assets;         \
	cp -r $(SRC)/* $(ANDROID_LOVE_ROOT_DIR)/app/src/embed/assets;   \
	rm $(ANDROID_LOVE_ROOT_DIR)/app/src/embed/assets/Makefile;      \
	cp $(ANDROID_LOVE_ROOT_DIR)/license.txt $(ANDROID_LOVE_ROOT_DIR)/app/src/embed/assets/love-licenses.txt;\
	cd $(ANDROID_LOVE_ROOT_DIR);                                    \
	chmod +x gradlew;                                               \
	./gradlew assembleNormalRecord;                                 \
	./gradlew assembleEmbedNoRecordRelease;                         \
	./gradlew bundleEmbedNoRecordRelease;

packeges-copy:
	cp $(ANDROID_LOVE_ROOT_DIR)/app/build/outputs/apk/embedNoRecord/release/app-embed-noRecord-release-unsigned.apk $(SRC)/;
	cp $(ANDROID_LOVE_ROOT_DIR)/app/build/outputs/bundle/embedNoRecordRelease/app-embed-noRecord-release.aab $(SRC)/;
	cp $(ANDROID_LOVE_ROOT_DIR)/app/build/outputs/apk/normalRecord/release/app-normal-record-release-unsigned.apk $(SRC)/;

packeges-sign-debug:
	jarsigner -verbose -keystore $(DEVELOPMENT_KEYS_DIR)/debug.keystore -storepass:file  $(SECRET_PASSWORD_FOR_KEYGEN)  $(SRC)/app-embed-noRecord-release-unsigned.apk debug;
	jarsigner -verbose -keystore $(DEVELOPMENT_KEYS_DIR)/debug.keystore -storepass:file  $(SECRET_PASSWORD_FOR_KEYGEN)  $(SRC)/app-embed-noRecord-release.aab debug;
	jarsigner -verbose -keystore $(DEVELOPMENT_KEYS_DIR)/debug.keystore -storepass:file  $(SECRET_PASSWORD_FOR_KEYGEN)  $(SRC)/app-normal-record-release-unsigned.apk debug;

packeges-sign-release:
	jarsigner -verbose -keystore $(DEVELOPMENT_KEYS_DIR)/release.keystore -storepass:file  $(SECRET_PASSWORD_FOR_KEYGEN) $(SRC)/app-embed-noRecord-release-unsigned.apk release;
	jarsigner -verbose -keystore $(DEVELOPMENT_KEYS_DIR)/release.keystore -storepass:file  $(SECRET_PASSWORD_FOR_KEYGEN) $(SRC)/app-embed-noRecord-release.aab release;
	jarsigner -verbose -keystore $(DEVELOPMENT_KEYS_DIR)/release.keystore -storepass:file  $(SECRET_PASSWORD_FOR_KEYGEN) $(SRC)/app-normal-record-release-unsigned.apk release;

packeges-sign-verify:
	jarsigner -verify $(SRC)/app-embed-noRecord-release-unsigned.apk release;
	jarsigner -verify $(SRC)/app-embed-noRecord-release.aab release;
	jarsigner -verify $(SRC)/app-normal-record-release-unsigned.apk release;
.SILENT:all

 , , , ,

LINUX-ORG-RU
()

Можно ли слепо полагаться на Last-Modified?

Форум — Development

Суть, есть велосипедик очередной, проверяет изменения на веб страничках, это я так узнаю про новости, проверяет сейчас в лоб, скачивает страницу и с горем пополам извлекает текст и делается нечто вроде diff от предыдущей сохранённой страницы, совпадающий текст заменяется * звёздочками так я вижу что изменилось на странице, типа вот так

Всё бы ладно, но в большинстве случаев, обновлений на страницах нет. Вот все кнопки что коричневые это значит что на странице ничего не изменилось. Но узнаёт программа про это, только целиком скачав страницу и сравнив с предыдущей копией в лоб. Подумалось что правильнее будет узнавать какой Last-Modified и на основании его уже качать или не качать новые данные со страницы. Вопрос, Last-Modified может врать? Если да, то как часто это делает. Может там неписаные правила какие настройки вебсерверов.

И сразу второй вопрос, нужно ли сначала делать запрос If-Modified-Since для явного указания что я хочу узнать изменилась ли страница с предыдущей даты конкретной или просто можно читать Last-Modified и сравнивать даты чисто на своей стороне, в смысле может ли быть что Last-Modified вернётся новым тогда и только тогда когда был запрос с If-Modified-Since?

Хочется просто пропускать загрузку страниц для проверки, если они реально без обновлений. В правильную ли я сторону думаю?

Я параллельно сам читаю всякое, но думаю спросить лишним не будет =)

 , , lua-sec, ,

LINUX-ORG-RU
()

Бекапилка вашего ПекА

Форум — Talks
- АХТУНГ, ВАХТУНГ, БАХТУНГ, ДРЯХТУНГ, АЛЯРМ, ВНИМАНИЕ
- ЭТОТ СКРИПТ ПРЕДОСТАВЛЯЕТСЯ КАК ЕСТЬ, Я НЕ НЕСУ НИКАКОЙ
- ОТВЕТСТВЕННОСТИ ЯВНОЙ ИЛИ КОСВЕННОЙ, ВАМ ЛИБО КОМУ ТО ЕЩЁ
- ПО ПРИЧИНЕ, ПОРЧИ И/ИЛИ УНИЧТОЖЕНИИ ВАШИХ ДАННЫХ 
- ЕСЛИ ВЫ НЕСОГЛАСНЫ, Я ЗАПРЕЩАЮ ВАМ ЗАПУСКАТЬ ЭТОТ КОД
- ДЕЙСТВУЙТЕ НА СВОЙ СТРАХ И РИСК, ВАШИ ЛЮБЫЕ СОМНЕНИЯ 
- КАСАТЕЛЬНО СКРИПТА ДОЛЖНЫ ВЕСТИ К ОТКАЗУ ОТ ЕГО ИСПОЛЬЗОВАНИЯ

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

Простой скрипт ручного бекапа наиболее важных файлов, использует
rsync и zip Настройка производится внутри стрипта. Описание
настройки бекапа тоже распологается внутри скрипта.
Типичное использование это ручной запуск в конце дня или по
завершению какой либо важной работы или перед сном :)
Используйте для бекапа, отдельный диск где нет ничего кроме бекапа!
Просто иначе, в бекапе нет никакого смысла.
Лично я просто на панельку кнопочку сделал, и перед отплытием в люльку тыкаю кнопочку.
Комп я не выключаю обычно, а если выключаю до явно дожидаюсь окончания бекапа, не проверял что будет если увести в сон во время бекапа, так что, лучше дождаться окончания.

Зависимости

mkdir zip rsync notify-send lua

Использование

Настроить скрипт, после чего просто его запускать. Так как вам удобно.

  • Обязательные параметры
    • source каталог или файл источник
    • dest каталог приёмник
  • Дополнительные параметры
    • secure применить пароль (требуется zip=true)
    • zip сжать в архив
    • mirror сделать точную копию и обновить им текущий каталог бекапа( или архив если zip=true)
    • versions сделать точную копию и пометить датой в отдельном каталоге (или архиве если zip=true)

Пример поля конфигурации:

  • {secure=true, zip=true, versions=true, source='~/.ssh/', dest='ssh'};

Значит что нужно сделать рекурсивный бекап каталога с ключами ssh и расположить бекап в каталоге с именем ssh. secure=true означает что данные будут запаролены, а zip что будут сжаты, versions=true означает что каждый новый бекап будет создавать новую копию ~/.ssh помеченную датой. Описание того какие есть ключи и как они работают друг с другом есть внутри скрипта, вместе с примерами уже готовой настройки.

Ключи можно опускать (не указывать) обязательными являются только source и dest Не указанные ключи считаются заданными как false так что вот такая запись валидна

  • {source='~/Изображения/', dest='картинки'}

И означает сделать копию каталога ~/Изображения в каталоге картинки внутри общего каталога бекапа backup_path=... при этом если это второй или последующий бекап и в каталоге ~/Изображения/ была удалена картинка, она не будет удалена в каталоге картинки это сделано специально, дабы максимизировать сохраняемые данные. Если нужна точная копия с удалением того чего уже нет в каталоге источнике то нужно дополнительно явно указать опцию mirror=true это же правило работает если включить флаг zip=true будет всё тоже самое, но уже в виде архива.

На заметку

В каталоге относительно котрого исполняется скрипт будет лог бекапа, на рабочий стол будут приходить уведомление о текущем статусе выполнения, в случае ошибки будет показaн код возврата В лог попадает информация о том какая именно команда потерпела неудачу, пароль присутцвующий в команде заменяется на звёздочки. В зависимости от типа настроенного бекапа(первый бекап будет всегда долгим), а вот последующие при отсуцтвии изменений будут быстрыми исключение это mirror и versions оба создают точную копию с нуля, а не пытаются обновить текущее содержимое бекапа. В случае если любая программа вернёт код возврата отличный от 0 выполнение всего бекапа прерывается, и повторно он не запустится пока не будет удалён lock файл, это сделано специально, чтобы выяснить причину, исправить её и в ручном режиме удалить lock файл название котрого есть в срипте, в логах и уведомлении на рабочий стол.:

Если вы бекапите например ~./vimrc в dest каталог myvimrc то он будет там лежать как есть, с точкой. Так что так же чтобы его увидеть в каталоге бекапа нужно нажимать ctrl+h. Ну, я так. На всякий случай.

Удалять из бекапов лишнее, и вообще контролировать влезет ли всё, это ваша забота =)
Нет опции отключения уведомлений. Предпочитаю явно знать что всё прошло гладко.
Логи бекапа тоже руками удалять.

Дадада, пароль на zip для ssh и gpg ключей хахахаха, но хоть что-то.
Сам внешний накопитель держите шифрованным если туда важное сохраняется.

Сам сриптик. Взял его как есть.

#!/usr/env lua
local backup_rule =
{
    --каталог куда будут размещаться все бекапы
    backup_path = '/media/$USER/STORAGE_SSD/backup';
    --пароль если используется secure=true, лучше использовать файл с паролем
    backup_pass = nil;
    --пароль для важныйх файлов, можно записать в файл дабы не светить
    backup_pass_file = "/home/$USER/Документы/backup_pass";
    ---------------------------------------------------------------------------
    backup_list =
    {
    -- backup critical --
    {secure=true, zip=true, versions=true, source='~/.ssh/',     dest='ssh'    };
    {secure=true, zip=true, versions=true, source='~/.gnupg/',   dest='gpg'    };
    {secure=true, zip=true, mirror=true,   source='~/.config/',  dest='cfg'    };
    {secure=true, zip=true, mirror=true,   source='~/.mozilla/', dest='firefox'};
    {secure=true, zip=true, mirror=true,   source='~/Документы/',dest='doc'    };

    -- diffrerent media --
    {secure=false,zip=true, mirror=true, source='~/Книги/', dest='book'};
    {secure=false,zip=true, mirror=true, source='~/Музыка/',dest='music'};

    -- backup multimedia --
    {secure=false, zip=false, versions=false, source='~/FAMILY_ARCHIVE/', dest='family'};

    -- backup dev stuff  --
    {secure=false, zip=false, versions=true, source='~/.gitconfig',dest='gitconfig'};
    {secure=false, zip=false, versions=true, source='~/.vimrc',    dest='vimrc'};
    {secure=false, zip=false, versions=true, source='~/.bashrc',   dest='bashrc'};
    {secure=false, zip=true,  versions=true, source='~/.vim/',     dest='vim'};

    -- backups source code --
    {secure=false, zip=true, mirror=true, source='/mnt/STORAGE/', dest='code'};

    };
};
-------------------------------------------------------------------------------
-- :secure=true   - шифровать ли сжатый бекап с паролем.
--                  создать архив и запаролить, zip должен быть zip=true
--                  явное указание zip=false или отсуцтвие ключа создаст ошибку
-- ----------------------------------------------------------------------------
-- :secure=false  - тоже самое поведение что и при zip=false
-- ----------------------------------------------------------------------------
-- :zip=true      - создать архив и добавить в него файлы если их нет в архиве
--                  если version=false то обновлять архив, добавляя новые
--                  файлы и обновля существующие, не удаляет отсуцтвующие
-------------------------------------------------------------------------------
-- :zip=false     - создавать бекап в каталоге dest если versions=false
--                  то клонировать source в dest, при повторном бекапе добавлять
--                  новые файлы и обновлять текущие, не удалять отсуцтвующие
--                  если versions=true создавать отдельный бекап в новом
--                  каталоге с датой бекапа, полностью отельный бекап.
-------------------------------------------------------------------------------
-- :versions=true - если zip=false то создать новый каталог с датой бекапа
--                  если zip=true  то создать новый архив с датой бекапа
-------------------------------------------------------------------------------
-- :mirror=true   - если zip=true то создаётся точная сжатая копия источника
--                  если при этом versions=false то текущий архив удаляет из
--                  себя файлы которых больше нет в источнике
--                  если zip=false то вместо архива создаётся точная копия
--                  источника с удалением файлов из каталога бекапа если их
--                  нет в источнике, тоже самое зеркалирование/клонирование
-------------------------------------------------------------------------------
-- :source        - файл или каталог который нужно бекапить, каталоги
--                  обрабатываются рекурсивно бекапя всё что есть
-------------------------------------------------------------------------------
-- :dest          - каталог внутри носителя для бекапа, оно же префикс
--                  добавляющийся к каталогам и/или архивам при versions=true
--                  отражает то бекап чего делается ОБЯЗАН БЫТЬ УНИКАЛЬНЫМ!!!!
-------------------------------------------------------------------------------
--        Если какого либо ключа нет то он считается равным false
-------------------------------------------------------------------------------
-- В случае если zip mkdir или rsync завершаться с кодом отличным от нуля
-- бекап тут же прекращается с уведомлением на рабочий стол и в лог ошибок
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------
---- исполняет все команды, в случае ошибки прерывает всю работу
-------------------------------------------------------------------------------
local function execute(rule,command,exit_if_fail)
    local success, meta, code = os.execute(command..' >> backup-info.log');
    if (not success) then
       -- hide pass from output
       -- info log and messages
       if rule.backup_pass then
          command = command:gsub(rule.backup_pass,"********");
       end
       local msg = string.format('BACKUP ERROR %s %s %s',command,meta,code);
       io.stderr:write(msg..'\n');
       io.open('backup-error.log','a+'):write(msg);
       execute({},'notify-send  "'..msg..'" -u critical',false);
       if (exit_if_fail == true) then
          os.exit(127);
       end
       return success;
    end
    return success;
end
-------------------------------------------------------------------------------
--- информирует об ошибке и прерывает всю работу
-------------------------------------------------------------------------------
local function backup_fail(msg)
      io.stderr:write(msg..'\n');
      io.open('backup-error.log','a+'):write('\n'..msg..'\n');
      execute({},'notify-send -u critical "'..msg..'"',false);
      os.exit(127);
end
-------------------------------------------------------------------------------
--- просто информирует
-------------------------------------------------------------------------------
local function backup_info(msg)
      io.stdout:write(msg..'\n');
      local log = io.open('backup-info.log','a+');
      log:write('\n'..msg..'\n');
      log:close();
      execute({},'notify-send -u normal "'..msg..'"',false);
end
-------------------------------------------------------------------------------
--- проверяет используется ли в конфигурации пароль
-------------------------------------------------------------------------------
function check_need_pass(rule)
    for name,val in pairs(rule.backup_list) do
        if val.secure and val.secure == true then
           return true;
        end
    end
    return false;
end
-------------------------------------------------------------------------------
--- делает бекап K.O. :)
-------------------------------------------------------------------------------
local function make_backup(rule)
    if (rule.backup_path) then
       -- replaice $USER in path if we have ite
       rule.backup_path = rule.backup_path:gsub('$USER',os.getenv('USER'));
    else
       backup_fail('BACKUP ERROR backup_path not set, no place to save backup',true);
    end
    -- we use password?
    if (check_need_pass(rule) == true) then
        -- check pass contained in configuration
        if not rule.backup_pass and not rule.backup_pass_file then
           backup_fail('BACKUP ERROR you use secure option but backup_pass or backup_pass_file no set!',true);
        end
        -- if pass_file set read the password
        -- and override/write rule.backup_pass
        if (rule.backup_pass_file) then
           -- replaice $USER in path if we have ite
           rule.backup_pass_file = rule.backup_pass_file:gsub('$USER',os.getenv('USER'));
           local pass_file = io.open(rule.backup_pass_file);
           if (pass_file) then
              if rule.backup_pass then
                 backup_info('BACKUP WARNING rewrite backup_pass from backup_pass_file');
              end
              rule.backup_pass = pass_file:read('*l');
           else
              backup_fail('BACKUP ERROR '..rule.backup_pass_file..' no readable!',true);
           end
           pass_file:close();
        end
        -- maybe pass use only numbers force to string
        rule.backup_pass = tostring(rule.backup_pass);
        -- check small pass, no need but jut be
        if not rule.backup_pass or #rule.backup_pass <= 4 then -- kek cheburek 
           backup_info('BACKUP WARNING you backup_pass small, less or equal 4 symbols');
        end
    end
    -- info about starting
    backup_info("BACKUP START "..os.date());
    --first check, we have storage and access?
    execute(rule,'mkdir -p '..rule.backup_path, true);
    -- lockfile + uuid for exclude collisions
    local lockfile = '.lock-938448fc-1742-43e9-bff8-e2acf6d29710';
    -- check/create lock file
    -- if backup failed you self
    -- delete lock file your hands
    if not io.open(rule.backup_path..'/'..lockfile,'r') then
       io.open(rule.backup_path..'/'..lockfile,'w'):write('lock');
    else
       backup_fail("OTHER BACKUP PROCESS IS WORKING, SKIPPING THIS PROCESS\n"..
                   "IF YOU SURE DELETE LOCK FILE "..rule.backup_path..'/'..lockfile..'\n',true);
       ----------------------------------------------------------------------
       -- uncomment if after backup error no need locked backup for fix error
       ----------------------------------------------------------------------
       -- os.remove(rule.backup_path..'/'..lockfile);
    end
    for id,item in pairs(rule.backup_list) do
        local date = os.date("%Y-%m-%d_%X");
        local path = rule.backup_path;
        local pass = rule.backup_pass;
        local dest = item.dest;
        local source = item.source;

        if (not source) then
           backup_fail('BACKUP ERROR source path not set, what files backup?',true)
        end

        if (not dest) then
           backup_fail('BACKUP ERROR dest path not set, wheare dir name to save?',true)
        end

        execute(rule,'mkdir -p '..path..'/'..dest,true);
        backup_info('BACKUP: '..source);

        ------------------- secure: versionize backup data ---------------------
        ------------------------------------------------------------------------
        if (item.secure and item.zip and item.versions) then
            local target = string.format('%s/%s/%s-%s.zip',path,dest,dest,date);
            execute(rule,'zip -r9P '..pass..' '..target..' '..source,true);
            goto next_item;
        end
        ---------------- secure: update and append backup data -----------------
        ------------------------------------------------------------------------
        if (item.secure and item.zip and not item.versions) then
            local target = string.format('%s/%s/%s.zip',path,dest,dest);
            local opts = item.mirror and ' -rFS9P ' or ' -ru9P ';
            execute(rule,'zip '..opts..' '..pass..' '..target..' '..source,true);
            goto next_item;
        end
        ----------- no secure: zipped update and append backup data ------------
        ------------------------------------------------------------------------
        if (not item.secure and item.zip and not item.versions) then
           local target = string.format('%s/%s/%s.zip',path,dest,dest);
           local opts = item.mirror and ' -rFS9 ' or ' -ru9 ';
           execute(rule,'zip '..opts..' '..target..' '..source,true);
           goto next_item;
        end
        ----- no secure: zipped, versionize update and append backup data ------
        ------------------------------------------------------------------------
        if (not item.secure and item.zip and item.versions ) then
           local target = string.format('%s/%s/%s-%s.zip',path,dest,dest,date);
           execute(rule,'zip -r9 '..target..' '..source,true);
           goto next_item;
        end
        --------- no secure: versionize update and append, no delete -----------
        ------------------------------------------------------------------------
        if (not item.secure and not item.zip and item.versions ) then
           local target = string.format('%s/%s/%s-%s/',path,dest,dest,date);
           execute(rule,'mkdir -p '..target);
           execute(rule,'rsync -arv '..source..' '..target,true);
           goto next_item;
        end
        ----------------- no secure: update and append, no delete --------------
        ------------------------------------------------------------------------
        if (not item.secure and not item.zip and not item.versions) then
           local target = string.format('%s/%s/',path,dest);
           execute(rule,'mkdir -p '..target);
           local opts = item.mirror and ' -arv --delete ' or ' -arv ';
           execute(rule,'rsync '..opts..' '..source..' '..target,true);
           goto next_item;
        end
        ------------------------------------------------------------------------
        if (item.secure and not item.zip) then
            backup_fail('BACKUP ERROR UNSUPPORT SECURE WITCHOUT ZIP ARCHIVE :(');
        end
        ::next_item::
    end
    backup_info("BACKUP END "..os.date());
    os.remove(rule.backup_path..'/'..lockfile);
end

-- check depends
execute({},"zip --version",true);
execute({},"rsync --version",true);
--execute({},"mkdir --version",true);
execute({},"notify-send --version",true);
-- start backup now
make_backup(backup_rule)

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

 , , , ,

LINUX-ORG-RU
()

Вопрос по пул-реквестам

Форум — Development

Допустим есть репа, в ней два файла po я перевёл первый и сделал пул-реквест. Я перевёл второй и хотел сделать пул-реквест, но он будет уже включать два коммита изменений, предыдущий и текущий.

Это значит что для отдельных пул-реквестов нужно делать отдельные бранчи/ветки, ну типа «transtale A to B», «translate C to B» и делать пул-реквесты от них? Дожил, никогда подряд пулл-реквесты не присылал, а если и присылал то после того как предыдущий примут, поэтому с таким не сталкивался =)

Только так можно разделять коммиты по отдельным пул-реквестам? Типа «ФичаА», «ФичаБ».

Не бейте сильна :)

Ответ: Да

 , pull-request, запрос на слиятние, ,

LINUX-ORG-RU
()

Эталонные наборы данных для оценки сжатия

Форум — Development

Есть ли такие? Краткий поиск через поиск ничего не показал или я не знаю как искать. Суть проста, очередной дурак (я) играется с очередным своим (а на деле велосипедным наверное) «алгоритмом» сжатия, без цели, а просто за интерес. Ну, понятно что вся суть итоговая с какими данными то или иное работает лучше, универсальных алгоритмов сжатия не существует. Но чисто для своего удобства хотелось бы иметь некий набор, например данные состоящие только из уникальных значений, данные с последовательным повторением, оно же но с разной частотой повторений или размерами этих самых повторений, смешанные данные в разных пропорциях и так далее и так далее, как набор искусственных данных для синтетической оценки коэффициентов сжатия так и реальных наборов и их комбинаций так сказать типичных в повседневной практике.

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

Можно самому напридумывать конечно, но лень и возможно будет некорректно, может есть что? А то просто совать что под руку попадётся в целом прикидывая что внутри такое себе.

Да и даже в отрыве от личных экспериментов можно будет например выбирать тот или иной архиватор зная какой на каких данных лучше себя проявляет и использовать именно его для своих нужд в конкретных случаях когда твои файлики по составу наиболее приближены к одному или группе наилучших результатов одного из архиваторов. Даааа… это всё можно просто взять и на живых данных проверить, но да ладно.

В целом не критично, но просто если такое есть было бы удобно.
Как-то так. Может кто знает? Где и куда копать.
Если такого не существует, то надо будет заняться.

Перемещено hobbit из general

 , , датасет, палата мер и весов, сжатие данных

LINUX-ORG-RU
()

RSS подписка на новые темы