Уменьшение накладных расходов для утилит на golang

Цель работы — сократить накладные расходы на хранение большого количества утилит, написанных на golang.Один из побочных эффектов статической компиляции golang — относительно большие накладные расходы на хранение рантайма и всех используемых библиотек внутри каждого исполняемого файла. Например небольшая утилитка, которая только и делает что обращается через сеть к серверу и выполняет простые полученные команды — весит 5.5Мб.Когда такая утилитка одна — это в современных условиях это еще не страшно. Когда утилиты накапливаются и их становится уже несколько десятков или сотен — чисто по-человечески становится жалко сотен мегабайтов, утекающих «вникуда».

Для решения этой проблемы я написал библиотеку multiex, которой и делюсь с сообществом. С его помощью можно объединить несколько программ в один исполняемый файл с минимальными изменениями внутри кода программ и без изменения внешнего поведения. Мысль была взята у busybox — все программы компилируются в один файл, а выполняемый код выбирается при запуске, исходя из имени запускаемого файла.

Для подключения программ нужно вызвать функцию-регистратор, например вот так:

func f1(){ if os.Args[1] == «asdf» { println («ok») } }

multiex.Register (multiex.ExecutorDescribe{Name: «test», Function: f1}) В структуре передается имя программы — если исполняемый файл вызывается с именем test, то будет вызвана функция f1. Дальше идет обычная работа функции без изменений, в т.ч. можно обычным образом разбираться переданные аргументы командной строки…/test asdf

./test может быть именем исполняемого файла, жесткой или символьной ссылкой.

Кроме этого можно вызвать функцию f1 если первым параметром исполняемого файла будет --multiex-command=test. При этом параметр --multiex-command=test будет удален и функция f1 так же сможет выполнять разбор своих аргументов как обычно…/any --multiex-command=test asdf

В библиотеке уже реализована одноименная программа с названием multiex, на данный момент она умеет:1. Выводить краткую справку о том что это за файл и какие программы в него включены — если файл вызван с непредусмотренным именем2. Создавать символьные ссылки на все программы, входящие в исполняемый файл. Ссылки создаются в той же папке, где лежит сам исполняемый файл…/any --multiex-command=multiex install

При этом ссылка на сам multiex не создается.

В github.com/rekby/multiex-example показан пример подключения программ:1. Регистрация функций непосредственно в основном компилируемом модуле. При этом способе главный сборочный модуль знает о конкретных включаемых функциях и при добавлении новой функции — нужно добавлять её перечисление в список регистрации. В сами включаемые программы при этом нет необходимости добавлять зависимость от multiex — достаточно иметь экспортируемую функцию Main.2. В сборочном модуле импортируются подключаемые модули, а каждый модуль сам регистрирует свои функции в процессе инициализации. Тут сборочный модуль знает только список включаемых модулей, а не каждую функцию. Каждый модуль сам определяет какие функции он будет экспортировать и для экспорта новой функции достаточно поправить только модуль с этой функцией.3. Естественно оба способа можно комбинировать (в примере как раз показан комбинированный вариант).

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

github.com/rekby/multiex

© Habrahabr.ru