Во-первых, необходимо начать с установки некоторых зависимостей. Все это руководство было сделано на основе Ubuntu 14.04, но подходит для большинства операционных систем на основе Debian. Выполните следующую команду, чтобы приступить к работе.
sudo apt-get install build-essential dh-make
Далее нам нужно будет создать пакет, в котором мы будем размещать все компоненты. В данном случае пакет будет называться “mylittledeb”. Для установки пакета используются следующие команды:
mkdir mylittledeb
touch mylittledeb/Makefile
touch mylittledeb/hello.c
Содержание hello.c должно содержать классический “Hello, World”.
#include <stdio.h>
int main() {
printf(“Hello, World\n”);
return 0;
}
Make-файл должен иметь следующее содержание. Информация для того, кто не знаком с Make-файлами — нужно применять «_», а не пробел или позже при выполнении некоторых команд Вы рискуете получить ошибки. Кроме того, обратите внимание, что очистка целевого элемента установлена
||true
после удаления бинарного файла. Указанное рассогласование и другие элементы Debian мы будем использовать в дальнейшем запуская make clean
перед созданием. all:
gcc hello.c -o hello
clean:
rm hello || true
В этой точке у Вас есть созданный пакет Debian, который позволит пользователям печатать «Hello, World». Это не самая интересная часть, но дальше будет больше. На этом этапе убедитесь, что запуск
make
производит бинарный вызов hello и запускает двоичные выводы «Hello, World». Если такого не произошло — значит, что-то пошло не так и это надо исправить, для того, чтобы двигаться дальше. Наконец, мы можем приступить к фактическому созданию пакета! Чтобы инициализировать пакет Debian, мы будем использовать удобную
dh_make
программу, которую мы устанавливали раньше. При выполнении следующей команды с вводом тех же настроек, которые указаны ниже, Вы должны получить ошибку, но это плановая ошибка. В данном случае важно понять, что такое dh_make и как решить другие проблемы, с которыми Вы, вероятно, столкнетесь позже при работе с более усовершенствованными пакетами. dh_make -p mylittledeb_0.0.1
Type of package: single binary, indep binary, multiple binary, library, kernel module, kernel patch?
[s/i/m/l/k/n] s
Maintainer name : root
Email-Address : root@unknown
Date : Sun, 10 Apr 2016 14:38:32 -0400
Package Name : mylittledeb
Version : 0.0.1
License : blank
Type of Package : Single
Hit <enter> to confirm:
Could not find mylittledeb_0.0.1.orig.tar.xz
Either specify an alternate file to use with -f,
or add --createorig to create one.
Мы получили сообщение об ошибке. Теперь рассмотрим несколько вещей, которые касаются этой ошибки. Во-первых, что такое orig.tar.xz файл. Во-вторых, почему использовался флаг -p? Давайте начнем с простого вопроса. Флаг-p используется, потому что dh_make смотрит на имя директории, в которой Вы находитесь в настоящее время, чтобы выяснить имя пакета и версию. Многие могут согласится, что глупо вызывать каталог таким путем, поэтому флаг -p при первом выполнении dh_make передает данные в виде <имя пакета> _ <версия>.
Теперь давайте выясним, что за файл orig.tar.xz. Официальная документация говорит, что это — просто tarball исходного кода, который в нашем случае является просто текущим состоянием каталога. Однако, возможно, что-то особенное в этом orig.tar.xz файле есть. Поэтому давайте посмотрим, как он создается. Выполнение следующей команды позволит Вам снести исходный код для dh-make. Вы можете сделать это в tmp, так как потребуется очистить все файлы, если вы делаете это в вашем пакете mylittledeb.
apt-get source dh_make
Теперь, когда есть исходный код, давайте посмотрим, что происходит. Открывая сценарий dh_make, можно найти внутри файл Perl. При поиске orig.tar.xz с Vim приходим к следующей строке.
system(‘tar’, ‘cfJ’, “../$package_name\_$version.orig.tar.xz”, “.”);
Это просто старый архивный файл tar. Однако у Вас должно быть некоторое понимание того, что все эти волшебные Debian сценарии делаются на случай, если все пойдет не так. Теперь давайте вернемся в нашу папку mylittledeb и запустим следующее:
dh_make -p mylittledeb_0.0.1 --createorig
Теперь вы должны увидеть папку DEBiAN в вашей папке mylittledeb со следующим содержимым:
changelog
compat
control
copyright
docs
init.d.ex
manpage.1.ex
manpage.sgml.ex
manpage.xml.ex
menu.ex
mylittledeb.cron.d.ex
mylittledeb.default.ex
mylittledeb.doc-base.EX
postinst.ex
postrm.ex
preinst.ex
prerm.ex
README.Debian
README.source
rules
source
watch.ex
Файлы .ex и .EX являются примерами файлов. В большинстве из них нет никакой необходимости.
changelog — этот файл управляет Вашей версией пакета, а также кратко приводит объяснение о том, что изменилось, начиная с последнего обновления. Вот то, на что должен быть похож основной файл.
mylittledeb (0.0.1–1) unstable; urgency=low
* Initial release (Closes: #nnnn) <nnnn - число ошибки Вашего ITP>
— root <root@unknown> Sun, 10 Apr 2016 15:00:11 -0400
'mylittledeb' будет содержать в названии '0.0.1' это версия пакета и '1' в конце это — версия Debian. Нестабильный дистрибутив, для которого пакет Debian является целевым и переноса на различные дистрибутивы, сделан за пределами этого процесса. В данном случае мы просто будем использовать trusty, так как все это строится на Ubuntu 14.04. После того, как Вы все сделали это выглядит примерно так:
mylittledeb (0.0.1–1) trusty; urgency=low
* Intial package release
— root <root@unknown> Sun, 10 Apr 2016 15:00:11 -0400
Последняя строка должна содержать имя, связанное с ключом GPG, если вы хотите подписать свои пакеты. Но мы еще дойдем до подписи пакета. На данный момент Вы можете проигнорировать этот пункт.
compat — это волшебный файл, и Вы должны всегда использовать цифру 9. Это — примерно единственная информация, которую Вы можете найти на нем. Почему именно 9? Ну, она используется всеми инструментами в пакете debhelper, это будет гарантировать, что ваш файл Debian совместим.
control — файл управления содержит версию, независимую информацию о Вашем пакете, которую увидят люди при выполнении 'apt-cache show mylittledeb'. Это все довольно хорошо объяснено в вики Debian. Ваш пакет должен выглядеть следующим образом.
Source: mylittledeb
Section: devel
Priority: optional
Maintainer: root <root@unknown>
Build-Depends: debhelper (>= 8.0.0)
Standards-Version: 3.9.4
#Vcs-Git: git://git.debian.org/collab-maint/mylittledeb.git
#Vcs-Browser: http://git.debian.org/?p=collab-maint/mylittledeb.git;a=summary
Package: mylittledeb
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}
Description: An example debian package that says “Hello, World”
Нужно понимать, что сборка зависимостей должна быть построена правильно, так как включает в себя все зависимости для вашего пакета. Они не должны быть добавлены к
Depends
, который включает только зависимости, которые запускают Вашу программу. Здесь важно отметить, что {shlibs:Depends} и {misc:Depends} — это две волшебные строки, вызываемые командой dh_shlibdeps. Эта команда полезна для определения зависимостей, Ваших двоичных потребностей, которые не сразу очевидны.copyright?— это самый очевидный из файлов, и если Вы заинтересованы выпустить пакет в общий доступ, он просто обязан содержать данный файл.
docs — этот файл может перечислить имена всех файлов, которые вы хотели бы видеть включенными в комплект.
rules — файл правил Debian, содержит всю информацию о создании вашего пакет и является специальной версией Makefile. В файле указываются дополнительные цели, которые используются в создании файлов Debian. По умолчанию, это очень простой файл, который просто запускает основные команды. Тем не менее, если вам нужно что-то переопределить, например, как ваш каталог будет очищаться до сборки, Вам стоит сделать что-то вроде этого.
#!/usr/bin/make -f
# -*- makefile -*-
# Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1
override_dh_auto_clean:
rm /tmp/random.file
%:
dh $@
Все доступные переопределения перечислены в этой вики статье, а также более детальное описания файла правил. По умолчанию все инструменты debhelper используют этот файл таким образом, например, если мы хотим очистить директорию, это должно быть сделано перед созданием нового пакета, то выполняется следующая команда 'dh clean'. Эта команда в свою очередь вызывает dh_testdir, dh_auto_clean и dh_clean, что в свою очередь вызывает некоторые perl скрипты.
На данный момент править этот файл не нужно, но помните, что вы можете настроить функциональные возможности позже, если вам это нужно, когда вы делаете свой собственный пакет Debian.
source/format — этот файл кажется довольно простым. На данный момент Вы можете оставить его как есть. Просто знайте, что это формат компилирования, специфический способ применения патчей к восходящему тарболу который мы создали ранее.
*.install- этот файл не создается автоматически, так что вам нужно будет сделать файл с именем mylittledeb.install. Любой файл, который был добавлен с установкой, будет использоваться, чтобы установить двоичный файл. Файл или директория, расположенные в системе, используется при установке пакета. Так как у нас есть «hello world» двоичный файл, мы должны будем поместить это в пользовательскую систему. Следующий файл поместит наш «hello world» двоичный файл в пользовательскую директорию /usr/bin.
hello /usr/bin
Остальная часть файлов, заканчивающихся на .ex и.EX, содержит описания того, что делают различные файлы. Их лучше же конечно сохранить, но и от удаления ничего страшного не произойдет.
Теперь простая часть. Выполните следующую команду.
dpkg-buildpackage
Если раскомментировать
export DH_VERBOSE=1
вы можете увидеть все команды, которые выполняются в данный момент. Все эти команды Вы можете переопределить, по этому в случае возникновения проблемы с своим собственным пакетом Вы сможете определить ее причину. Вот вывод приведенной выше команды:
dpkg-buildpackage: source package mylittledeb
dpkg-buildpackage: source version 0.0.1-1
dpkg-buildpackage: source distribution trusty
dpkg-buildpackage: source changed by root <root@unknown>
dpkg-buildpackage: host architecture amd64
dpkg-source --before-build mylittledeb
debian/rules clean
dh clean
dh_testdir
dh_auto_clean
make -j1 clean
make[1]: Entering directory `/root/mylittledeb'
rm hello || true
rm: cannot remove ‘hello’: No such file or directory
make[1]: Leaving directory `/root/mylittledeb'
dh_clean
rm -f debian/mylittledeb.substvars
rm -f debian/mylittledeb.*.debhelper
rm -rf debian/mylittledeb/
rm -f debian/*.debhelper.log
rm -f debian/files
find . \( \( -type f -a \( -name '#*#' -o -name '.*~' -o -name '*~' -o -name DEADJOE -o -name '*.orig' -o -name '*.rej' -o -name '*.bak' -o -name '.*.orig' -o -name .*.rej -o -name '.SUMS' -o -name TAGS -o \( -path '*/.deps/*' -a -name '*.P' \) \) -exec rm -f {} + \) -o \( -type d -a -name autom4te.cache -prune -exec rm -rf {} + \) \)
rm -f *-stamp
dpkg-source -b mylittledeb
dpkg-source: info: using source format `3.0 (quilt)'
dpkg-source: info: building mylittledeb using existing ./mylittledeb_0.0.1.orig.tar.xz
dpkg-source: info: building mylittledeb in mylittledeb_0.0.1-1.debian.tar.gz
dpkg-source: info: building mylittledeb in mylittledeb_0.0.1-1.dsc
debian/rules build
dh build
dh_testdir
dh_auto_configure
dh_auto_build
make -j1
make[1]: Entering directory `/root/mylittledeb'
gcc hello.c -o hello
make[1]: Leaving directory `/root/mylittledeb'
dh_auto_test
debian/rules binary
dh binary
dh_testroot
dh_prep
rm -f debian/mylittledeb.substvars
rm -f debian/mylittledeb.*.debhelper
rm -rf debian/mylittledeb/
dh_auto_install
install -d debian/mylittledeb
dh_install
install -d debian/mylittledeb//usr/bin
cp -a ./hello debian/mylittledeb//usr/bin/
dh_installdocs
install -g 0 -o 0 -d debian/mylittledeb/usr/share/doc/mylittledeb
chown -R 0:0 debian/mylittledeb/usr/share/doc
chmod -R go=rX debian/mylittledeb/usr/share/doc
chmod -R u\+rw debian/mylittledeb/usr/share/doc
install -g 0 -o 0 -m 644 -p debian/README.Debian debian/mylittledeb/usr/share/doc/mylittledeb/README.Debian
install -g 0 -o 0 -m 644 -p debian/copyright debian/mylittledeb/usr/share/doc/mylittledeb/copyright
dh_installchangelogs
install -o 0 -g 0 -p -m644 debian/changelog debian/mylittledeb/usr/share/doc/mylittledeb/changelog.Debian
dh_perl
dh_link
dh_compress
cd debian/mylittledeb
chmod a-x usr/share/doc/mylittledeb/changelog.Debian
gzip -9nf usr/share/doc/mylittledeb/changelog.Debian
cd '/root/mylittledeb'
dh_fixperms
find debian/mylittledeb -print0 2>/dev/null | xargs -0r chown --no-dereference 0:0
find debian/mylittledeb ! -type l -print0 2>/dev/null | xargs -0r chmod go=rX,u+rw,a-s
find debian/mylittledeb/usr/share/doc -type f ! -regex 'debian/mylittledeb/usr/share/doc/[^/]*/examples/.*' -print0 2>/dev/null | xargs -0r chmod 644
find debian/mylittledeb/usr/share/doc -type d -print0 2>/dev/null | xargs -0r chmod 755
find debian/mylittledeb/usr/share/man debian/mylittledeb/usr/man/ debian/mylittledeb/usr/X11*/man/ -type f -print0 2>/dev/null | xargs -0r chmod 644
find debian/mylittledeb -perm -5 -type f \( -name '*.so.*' -or -name '*.so' -or -name '*.la' -or -name '*.a' \) -print0 2>/dev/null | xargs -0r chmod 644
find debian/mylittledeb/usr/include -type f -print0 2>/dev/null | xargs -0r chmod 644
find debian/mylittledeb/usr/share/applications -type f -print0 2>/dev/null | xargs -0r chmod 644
find debian/mylittledeb -perm -5 -type f \( -name '*.cmxs' \) -print0 2>/dev/null | xargs -0r chmod 644
find debian/mylittledeb/usr/lib/perl5 debian/mylittledeb/usr/share/perl5 -type f -perm -5 -name '*.pm' -print0 2>/dev/null | xargs -0r chmod a-X
find debian/mylittledeb/usr/bin -type f -print0 2>/dev/null | xargs -0r chmod a+x
find debian/mylittledeb/usr/lib -type f -name '*.ali' -print0 2>/dev/null | xargs -0r chmod uga-w
dh_strip
strip --remove-section=.comment --remove-section=.note debian/mylittledeb/usr/bin/hello
dh_makeshlibs
rm -f debian/mylittledeb/DEBIAN/shlibs
dh_shlibdeps
install -o 0 -g 0 -d debian/mylittledeb/DEBIAN
dpkg-shlibdeps -Tdebian/mylittledeb.substvars debian/mylittledeb/usr/bin/hello
dh_installdeb
dh_gencontrol
echo misc:Depends= >> debian/mylittledeb.substvars
dpkg-gencontrol -ldebian/changelog -Tdebian/mylittledeb.substvars -Pdebian/mylittledeb
chmod 644 debian/mylittledeb/DEBIAN/control
chown 0:0 debian/mylittledeb/DEBIAN/control
dh_md5sums
(cd debian/mylittledeb >/dev/null ; find . -type f ! -regex './DEBIAN/.*' -printf '%P\0' | LC_ALL=C sort -z | xargs -r0 md5sum > DEBIAN/md5sums) >/dev/null
chmod 644 debian/mylittledeb/DEBIAN/md5sums
chown 0:0 debian/mylittledeb/DEBIAN/md5sums
dh_builddeb
dpkg-deb --build debian/mylittledeb ..
dpkg-deb: building package `mylittledeb' in `../mylittledeb_0.0.1-1_amd64.deb'.
signfile mylittledeb_0.0.1-1.dsc
gpg: skipped "root <root@unknown>": secret key not available
gpg: [stdin]: clearsign failed: secret key not available
dpkg-genchanges >../mylittledeb_0.0.1-1_amd64.changes
dpkg-genchanges: including full source code in upload
dpkg-source --after-build mylittledeb
dpkg-buildpackage: full upload (original source is included)
dpkg-buildpackage: warning: failed to sign .dsc and .changes file
Обратите внимание, что подписать изменения не удалось, потому что, как было сказано выше, ключ GPG не был создан. Это не страшно, даже не смотря на то, что получили такую ошибку пакет фактически был создан успешно.
Теперь Вы должны увидеть набор новых файлов в том же каталоге, где размещена Ваша папка mylittledeb. Нас интересует файл с названием «mylittledeb_0.0.1-1_amd64.deb», который может быть установлен с помощью команды:
sudo dpkg -i mylittledeb_0.0.1–1_amd64.deb
Теперь вы должны иметь возможность запускать «Hello» из любого места на вашем компьютере, и получить текст «Hello, World».
Комментарии (27)
amarao
20.04.2016 12:24+8Ужасно. Просто ужасно. Очередной пересказ «подфигачить dh_make'ом». Ноль объяснений как оно работает и зачем.
Sild
20.04.2016 15:15+1А на каком уровень необходимо объяснять создание деб-пакета? Сам год назад какое-то время потратил на изучение вопроса по работе, остались неплохие заметки «для себя» — как по deb, так и по rpm. Думал, гайдов уже хватает. Но если сообществу всё мало — c удовольствием поделюсь.
fshp
20.04.2016 15:54+1На уровне "Имеем vim и ar"
Sild
20.04.2016 16:05+1К моему удивлению, с помощью ar собралось гораздо быстрее, чем через dpkg-build, как делалось раньше — при этом требуя минимум изменений. Поэтому спасибо за совет.
А про описание на этом уровне — почему бы и нет. Был бы спрос.
monah_tuk
21.04.2016 10:12извиняюсь, но разве ar, это не архиватор, который, в основном, используют для сборки кучки объектников в статическую библиотеку?
amarao
20.04.2016 16:26+1Отлично, я рад, что хоть кто-то может про него рассказать подробно.
Дан код на питоне. Самописный, т.е. менять можно «под себя». Он предоставляет несколько модулей с main() внутри, т.е. может быть исполнен как программа. Нормальной практикой является использование setup.py с entry_points, которые задают console_script, который генерируется при установке пакета. setup.py использует stdeb, который настраивается pybuild'ом, который вызывается dh_python2(3) (с опцией --buildsystem=pybuild, который вызывается dpkg-buildpackage, который вызывается git-buildpackage, который вызывается build-and-provide скриптом debian-jenkins-glue, который вызывается jenkins'ом.
Задача: генерировать console_scripts не в /usr/bin, а в /usr/lib/other_app/plugins.
Как? Я эту проблему решил, причём довольноразумнымтерпимым методом (как мне кажется), но мне интересно было бы послушать других людей. Как сделать так, чтобы сгенерированный console_script после установки пакета был бы не в /usr/bin, а в /usr/lib/other_app/plugins? Желательно без костылей в виде «mv» где-то в потрошках, и без прямого перечисления всего и вся, т.е. «одной строчкой для всех console_scripts, которые генерирует setup.py». Они, если что, генерируются автоматически с помощью интроспекции модулей в коде setup.py.Sild
20.04.2016 16:58Я плохо понимаю что происходит с питоном, в частности спефицику работы pybuild и dh_python. По религиозным соображениям мне приходится работать только с sh. Но в ближайшие пару дней найду время познакомиться поближе и попробую что-то подсказать.
amarao
20.04.2016 17:02Это касается практически любой другой системы. Адекватного метода сказать dh_install'у, что нужно не «copy», а «move» просто нет. Даже с использованием dh_exec'а.
Sild
20.04.2016 15:00+2Штойта, старый-добрый dpkg-build с {pre,post}inst и {pre,port}rm уже не в моде? Зачем мне ваш dh_make?
amarao
20.04.2016 16:29+1dh_make не собирает пакет, а создаёт debian-часть для пакета, т.е. генерирует скелет с прототипом rules. Речь не про бинарные пакеты (в которых есть набор скриптов), а про source-пакет, который пересобирается командой dpkg-buildpackage.
Cryvage
20.04.2016 19:01Объяснения получились на уровне «Открываем гримуар на странице 666 и зачитываем заклинание вслух, громко и четко». Или может это я такой тугой. Я так понял этот способ только для пакетов с исходниками. А куда копать если у меня есть исполняемый файл и «сошки», от которых он зависит? По сути нужно просто раскопировать это все по нужным системным папкам. Или не париться с пакетами и написать обычный скрипт? Хочется не просто сделать, чтобы работало, но и, по возможности, сделать так, как это принято.
debsaw
20.04.2016 19:49есть 2 типа зависимостей Depends и Build-Depends. Первые нужны для установки (ваш вариант), вторые для компиляции. И те и другие описываются при необходимости в файле debian/control. dh_make, про который говорилось в статье, создаст bolier plate для control файла. Его синтаксис описан тут:
https://www.debian.org/doc/debian-policy/ch-controlfields.html
То есть просто нужно дописать необходимые пакеты в правильные секции «заготовки» контрол файла.
amarao
20.04.2016 20:04+1«Как принято» для бинарного пакета сделать невозможно. Модель debian (и deb) подразумевает, что у вас есть исходный текст.
Если нет — попробуйте удовлетворить dh_install с помощью файла «имя пакета».install (в каталоге debian).
Т.е. кладёте дерево нужных файлов (т.е. usr/bin/foo, usr/lib/libfoo-1.so), делаете debian/control, а в debian/foo.install пишите usr/bin/foo\nusr/lib/libfoo-1.so.
debian/rules при этом выглядит так:
%:
dh $@
debian/changelog можно заполнить с помощью dch -i.
19as
20.04.2016 19:49А есть у кого инфа по созданию ярлыка (shortcut или desktop) на рабочем столе пользователя?
Интересует для deb пакета, чтобы ярлык был уже у существующих пользователей и у вновь регистрируемых (в скелетоне).
Как это правильно организовать...amarao
20.04.2016 20:09Сам не пробовал, интуиция говорит, что dh-triggers, а так же http://sources.debian.net/src/dpkg/1.18.4/doc/triggers.txt/
fshp
21.04.2016 02:31уже у существующих пользователей и у вновь регистрируемых
Т.е. у всех? Тогда почему бы не положить его в
/usr/share/applications
?
monah_tuk
21.04.2016 10:10Вообще, когда система описаний (спеков) для сборки требует таких вот статей, что-то не так (у самого несколько PPA). Самые адекватные спеки, по функциональности и возможностями, имхо, у ArchLinux, AlpineLinux (они позаимствовали идеи у Arch, но спеки не на 100% одинаковые). Прямо подмывает сделать прокси-makepkg для deb-based систем, который будет брать спек в формате
PKGCONFIG
, генерировать инфраструктуру для dpkg-buildpackage и производить отстройку. Ну и возможность экспорта, что бы иметь возможность залить в тот же PPA.
Кстати, а никто не знает, вдруг подобное уже есть?
fshp
21.04.2016 11:27ArchLinux-пакеты не покрывают полностью функционал deb. reconfigure и update-alternatives как в Арче сделать?
monah_tuk
21.04.2016 12:09А не про сами пакеты и пакетную систему речь.
Данные действия вызываются из maintainer скриптов (аналоги самих скриптов — есть). Кроме того, они никакого отношения к описанию процесса сборки через control/rules/changelog не имеют. Я именно про это, про спек, а не про сам пакетный менеджер или формат пакета писал.
Т.е. что бы сделать описание сборки под Арч, достаточно одного файла, с простой шапкой, двумя функциями (build и install) и интуитивными командами (всё таки это шелл). А в debian:
- сделать "дебианизацию" пакета исходного кода
- распаковать исходники
- создать директорию debian с rules, control, changelog, format/version
Далее заполнить вышеозначенные файлы. А у каждого из них свой формат, со своим хитрым набором целей (rules) или специальными утилитами для обновления (changelog + dch). Если для примитивного опакечивания в арчике нужно минут 5-10 на создание правил, причём информации в "рыбе" файла хватает с избытком, то для debian — у меня меньше получаса не получается.
Добавить сюда невозможность (хотя фича спорная) при пакетировании автоматического версифицирования для пакетов сборке которых происходит из исходников из VCS; плюс то что сборку можно делать разными способами: debian/rules, dpkg-buildpackage, debuild, то в части описания сборки PKGBUILD и makepkg всё же удобнее.
Про подписи не говорю, оно сейчас и там и там есть в разном виде.
PS и да, я не пользователь Арча, лет несколько как.
iSage
>запуск make производит бинарный вызов hello и запускает двоичные выходы «Hello, World»
PROMPT approved.
iSage
Надо же, обидчивые какие. Банально вычитать перевод перед отправкой ума не хватило, зато на кнопочки тыкать много ума не надо.
fshp
Расстегните молнию. Бегите. Получайте удовольствие!
(Just unzip. Run. Enjoy!)