пятница, октября 28, 2011

Введение в прикладное программирование под GNU/Linux

Это конспект, который я готовил для доклада на конференции, проводившейся местным университетом совместно с нашей LUG. Доклад «для самых маленьких», так что профессионалам просьба не жаловаться на поверхностность и обзорность.

Аудитория

Эта статья расчитана на два вида читателей. Во-первых, это люди, имеющие опыт программирования под MS Windows, но не имеющие такого опыта под GNU/Linux. Во-вторых, это люди, не имеющие опыта программирования вовсе. Однако, я предполагаю, что читатель в общем знаком с общепринятой в программировании терминологией, и ему не нужно объяснять, например, что такое «программа», «функция», «компилятор» или «отладка».

Средства разработки

Я буду рассматривать разработку с использованием тех средств, которые являются наиболее «родными» для GNU/Linux. К ним относятся:

  • Язык программирования C

  • Командная оболочка bash

  • Текстовые редакторы Vim и Emacs

  • Компилятор GCC

  • Отладчик GDB

  • Утилита для сборки проекта GNU make

  • Система управления версиями Git

  • Оконная система X11

Выбор именно этих средств не является догмой. Каждое из выше перечисленных средств может быть при желании заменено на другое. Однако, обычно под фразами наподобие «среда разработки Linux» понимается именно этот набор инструментов.

Языки программирования

Наиболее «родным» языком программирования для GNU/Linux является C. Это обусловлено следующими факторами:

  • GNU/Linux заимствует многие идеи (практически, идеологию) операционной системы UNIX;

  • Операционная система UNIX была написана на языке C (собственно, этот язык создавался именно для написания этой ОС);

  • Соответственно, ядро Linux и системное окружение GNU написаны тоже на C.

Ниже я буду рассматривать разработку с использованием языка C. Однако, этот выбор не является догмой. Другими популярными при разработке под GNU/Linux языками являются C++, Python, Perl. Конечно, могут использоваться и любые другие языки.

Среда разработки

В течение последних двух десятилетий очень широкое распространение получили т.н. IDE — интегрированные среды разработки. Такая среда включает в себя текстовый редактор, компилятор, отладчик, средства сборки проекта и мн.др. Такие среды есть и под GNU/Linux (наиболее популярны Eclipse, NetBeans, IDEA, KDevelop, Anjuta). Однако, история разработки под UNIX-подобные системы показывает, что IDE не являются не только единственным, но и наиболее эффективным средством разработки. Практически, правильный ответ на вопрос «какая самая лучшая IDE под GNU/Linux» — это «GNU/Linux это и есть IDE».

Часто можно встретить мнение, что большой проект без IDE разрабатывать невозможно. Это мнение легко опровергается. Первые версии UNIX писались даже не в Vim (его тогда ещё не было), а в Ed. Это так называемый «построчный» текстовый редактор, в котором вы можете редактировать за раз только одну строку текста. Весь файл на экране не отображается. В случае с UNIX по-другому и быть не могло — у разработчиков не было никаких экранов, а общение с системой осуществлялось при помощи телетайпов. Современное ядро Linux пишется в основном в редакторах Emacs и Vim.

Многие утилиты UNIX вызывают «текстовый редактор по умолчанию». Команда, запускающая текстовый редактор по умолчанию, берётся из переменной окружения $EDITOR. Некоторые утилиты смотрят сначала в переменную $VISUAL, и, лишь если она не установлена, в переменную $EDITOR. Это исторически сложившееся поведение: к старым компьютерам зачастую не было подключено никакого дисплея, а только телетайп, поэтому запускать экранный (визуальный) редактор смысла не было. В современных дистрибутивах обычно по умолчанию оказывается EDITOR=vi или EDITOR=nano. Указать использование другого редактора для одной команды можно так:

EDITOR=emacs some-command

Чтобы использовать нужный редактор по умолчанию всегда, нужно добавить в файл ~/.profile строчку типа

export EDITOR=emacs

Исторически сложилось так, что «настоящими» текстовыми редакторами для программистов являются только Vim и Emacs (просто из-за того, что у них самая долгая история развития именно в качестве текстовых редакторов для программистов). Остальные редакторы находятся в положении догоняющих.

Командная оболочка

Командная оболочка (или командный интерпретатор) — это программа, принимающая команды от пользователя на некотором достаточно простом языке программирования и выполняющая их. Большинство команд запускают одноимённые программы. Отдельные команды представляют собой конструкции языка программирования оболочки.

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

ОС семейств DOS и Windows заимствовали некоторые функции командной оболочки из UNIX, однако их авторы пошли на существенные упрощения, из-за чего функционал COMMAND.COM и cmd.exe получился сильно урезанным. PowerShell вполне на уровне, но работает существенно по-другому.

В рамках этой статьи я ограничусь использованием командной оболочки bash (как наиболее распространённой и используемой по умолчанию в большинстве дистрибутивов) для запуска компилятора и других средств разработки. Хороший обзор использования командной оболочки можно найти, например, в известной книге [kernigan_pike].

Документация

Все средства разработки и библиотеки в GNU/Linux обычно довольно хорошо документированы. Традиционно для документации используется специальный формат и утилита для его просмотра — man. Документация в системе делится на несколько разделов:

  1. Команды пользователя (например, ls, gcc или man)

  2. Системные вызовы — API ядра ОС

  3. Библиотечные функции

  4. Драйвера и т.п

  5. Форматы файлов

  6. Игры и т.п

  7. Различные обзоры подсистем

  8. Команды, используемые для системного администрирования

Для вызова раздела документации по имени нужно указать это имя при вызове команды man (например, man ls). Иногда разделы с одинаковым названием есть сразу в нескольких разделах документации документации. Указать конкретный раздел можно при вызове man (например, man 3 printf).

Более подробную информацию о справочной системе man см. в man man.

Утилиты системного окружения GNU часто используют для документации формат info. См., например, info Coreutils.

Компилятор

Сейчас существует много компиляторов языка C, более-менее совместимых с различными стандартами. Тем не менее, пока что в среде GNU/Linux наиболее применимым остаётся компилятор C, входящий в комплект GNU Compilers Collection (GCC). Этот компилятор, кроме стандарта C, поддерживает некоторое количество расширений стандарта. Эти расширения, в частности, широко используются в исходных текстах ядра Linux. В последнее время появляются компиляторы, способные скомпилировать ядро Linux (например, llvm-clang, или EKO).

Компилятор GCC запускается из командной оболочки командой вида

gcc [OPTIONS] program.c

где program.c — имя входного файла. Кроме того, по стандарту POSIX, компилятор может быть запущен командой cc program.c (cc — от "C compiler").

При обычном запуске компилятор пытается создать исполняемый файл. По умолчанию, выходной файл называется a.out (такое название осталось от древних версий UNIX). Другое название можно задать с помощью опции компилятора -o, например,

gcc -o program program.c

При сборке программы из нескольких модулей компилятору можно подавать на вход несколько исходных файлов или файлов объектного кода, например,

gcc -o program main.c module1.o module2.o …

Чтобы только скомпилировать один исходный файл в объектный код (не пытаясь собрать исполняемый файл), нужно дать команду вида

gcc -c module.c

(имя выходного файла по умолчанию будет module.o).

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

Статическая библиотека в UNIX-подобных системах представляет собой архив (старинного формата ar), включающий набор объектных файлов. Такой архив создаётся командой вида

ar r libsomething.a module1.o module2.o …

Имена файлов библиотек традиционно начинаются с префикса lib.

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

gcc -shared -o libsomething.so module1.c module2.c …

Для использования библиотеки при сборке программы её нужно указать компилятору при помощи опции -l, например

gcc -o program -lm program.c

(здесь будет использоваться файл библиотеки libm.so, префикс lib компилятор подставляет по умолчанию). По умолчанию компилятор собирает программу, использующую динамические библиотеки. Если нужно использовать статические версии библиотек, компилятору нужно указать опцию -static.

Подробную информацию об опциях gcc см. в man gcc.

Hello, world!

Считается, что традиция начинать изучение языка программирования с написания программы, выводящей строку "Hello, world!", пошла с книги Кернигана и Ричи "Язык C" [kernigan_richie]. В случае с языком C эта программа выглядит следующим образом:

#include <stdio.h>

int main(int argc, char* argv[]) {
printf("Hello world!\n");
return 0;
}

Чтобы запустить эту программу, этот текст нужно записать в файл с именем, скажем, hello.c, и из директории, в которой расположен этот файл, дать команду вида

gcc -o hello hello.c

Впрочем, в случае такой простой программы достаточно дать команду

make hello

(я поясню ниже, почему эти две команды работают одинаково). В результате в той же директории появится исполняемый файл с именем hello. Запустить его можно командой

./hello

Порядок сборки

Остановимся несколько подробнее на том, что именно делает компилятор. Порядок действий компилятора C традиционен, и применяется компиляторами некоторых других языков.




На входе компилятор имеет в общем случае набор файлов с исходными текстами. Перед началом собственно компиляции эти файлы обрабатываются т.н. препроцессором (программа cpp). Главная функция этой программы — выполнение директив вида #include . Встретив такую директиву, препроцессор вставляет содержимое указанного файла (в данном случае, stdio.h) на место этой директивы. Препроцессор понимает ещё некоторые директивы, но сейчас на них останавливаться я не буду.

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

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

Собственно программа gcc представляет собой так называемый драйвер (driver). Она запускает упомянутые выше программы (или только некоторые из них, в зависимости от опций), чтобы получить исполняемый файл.

Второй пример: решение квадратных уравнений

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

#include <stdio.h>
#include <math.h>

/* solve: calculate roots of square equation.
* a, b, c are coefficients in equation.
* Roots would be stored at x1, x2.
* Return value: count of real roots.
*/
int solve(double a, double b, double c,
double* x1, double* x2) {
double D = b*b - 4*a*c;
double sqrtD;

if (D > 0) {
sqrtD = sqrt(D);
*x1 = (-b - sqrtD)/(2.0 * a);
*x2 = (-b + sqrtD)/(2.0 * a);
return 2;
} else if (D < 0)
return 0;
else {
*x1 = -b/(2.0*a);
return 1;
}
}

int main (int argc, char* argv[]) {
double a,b,c;
double x1, x2;
int roots_count;

// Input coefficients
printf("A: ");
scanf("%lf", &a);
printf("B: ");
scanf("%lf", &b);
printf("C: ");
scanf("%lf", &c);

// Solve the equation
roots_count = solve(a,b,c, &x1, &x2);

// Output results
switch (roots_count) {
case 0:
printf("No (real) roots.\n");
break;
case 1:
printf("One root: %0.4lf\n", x1);
break;
case 2:
printf("Two roots: %0.4lf and %0.4lf\n",
x1, x2);
break;
}

return 0;
}

По аналогии с предыдущим примером, запишем этот текст в файл square.c и попытаемся скомпилировать его командой

gcc -o square square.c

Но на этот раз мы получим ошибку примерно такого вида:

/tmp/cc6RNFIi.o: In function `solve': square.c:(.text+0x6d): undefined reference to `sqrt' collect2: ld returned 1 exit status

В чём здесь дело? Ясно, что компилятору почему-то не понравился вызов функции sqrt(). Причём, он жалуется уже не на файл исходного кода, а на объектный файл (вот этот cc6RNFIi.o). Это означает, что исходный файл благополучно скомпилировался, а проблемы возникли на стадии компоновки (что можно видеть и по упоминанию в тексте ошибки программы ld — это стандартный в GNU/Linux компоновщик). Компоновщик не смог найти функцию sqrt(). В данном случае, это произошло из-за того, что эта функция содержится в библиотеке libm, а мы не просили компилятор использовать её. Чтобы избавиться от этой ошибки, нам нужно изменить команду компиляции на следующую:

gcc -o square -lm square.c

Такая команда должна отработать без ошибок и создать исполняемый файл square.

При сборке любой достаточно сложной программы нам придётся использовать несколько библиотек, и, возможно, понадобится указывать ещё какие-то опции компилятору. Команда может получиться довольно длинная. Что же, каждый раз набирать её вручную? Нет. Один из принципов философии UNIX гласит: «Всё, что может быть автоматизировано, должно быть автоматизировано». Здесь нам пригодится одна из древнейших UNIX-утилит — программа make. Чтобы воспользоваться ею, нужно написать файл с именем Makefile (в той же директории, что и наш исходный файл) со следующим содержимым:

square: square.c         $(CC) -o $@ -lm $<

Теперь собрать исполняемый файл можно просто дав команду make. Как это работает?

Make

Утилита make предназначена для сборки программ (хотя может использоваться для автоматизации многих других похожих задач). Она читает файл с именем Makefile и видит в нём набор правил. Каждое правило определяет три вещи: цель (goal, т.е. то, что нужно собрать), список исходных файлов и набор команд, которые нужно выполнить, чтобы собрать цель из исходных файлов. В примере выше, square — это имя цели, square.c — единственный в данном случае исходный файл (если их несколько, они перечисляются через пробел), а вторая строчка — команда. В команде могут использоваться переменные. Некоторые из переменных имеют специальное значение. В частности, в любом правиле $@ обозначает имя цели, а $< — первый исходный файл. Переменная $(CC) указывает на компилятор C, используемый в системе по умолчанию (в большинстве случаев это gcc, но бывает и что-нибудь другое).

В имени цели и списке исходных файлов может использоваться подстановочный символ %. Например, такое правило:

%.o: %.c   $(CC) -c $<

обозначает, что файлы с именем, заканчивающимся на .o, нужно собирать из соответствующих файлов с суффиксом .c.

Кроме того, make заранее знает некоторое количество правил по умолчанию. Среди них есть упомянутое в последнем примере, а также правило

%: %.c   $(CC) -o $@ $<

Благодаря этому правилу, в примере с «Hello, world!» просто команда make hello запускала cc -o hello hello.c.

По набору правил make составляет граф зависимостей целей друг от друга и от исходных файлов, и выполняет только те команды, которые нужны для сборки цели, указанной в командной строке. Если не указано никаких целей, то собирается первая цель, описанная в Makefile.

Более подробную информацию об этой утилите см., например, в man make.

Управление версиями

Для управления версиями исходного кода может использоваться любая VCS. Однако, раз уж мы говорим о GNU/Linux, рассмотрим вкратце систему, используемую для разработки ядра Linux: git. По git существует довольно обширная документация, в т.ч. и на русском языке. См. например, мою статью [vcs_git] или известную серию статей [los-t_git].

Для начала использования git нужно создать репозиторий — хранилище для версий файлов. Это делается командой

git init

Теперь можно добавлять файлы в репозиторий. Но нам не нужно отслеживать версии некоторых файлов, а именно: объектных файлов и исполняемых файлов. Чтобы сразу исключить их из рассмотрения git, напишем файл .gitignore следующего содержания:

*.o square hello

Теперь команда

git add .

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

git commit

По этой команде откроется текстовый редактор по умолчанию. Тут нужно будет написать комментарий к коммиту. В данном случае достаточно строчки типа «Initial commit».

Отладка

Для отладки в Linux используется отладчик gdb. Но сначала, для того, чтобы программу было удобно отлаживать, её нужно скомпилировать с опцией -g. Сейчас нам достаточно изменить Makefile, приведя его к виду

square: square.c         $(CC) -o $@ -lm -g $<

и пересобрать программу.

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

Отладка запускается командой вида

gdb ./square

Если отлаживаемой программе нужно передать какие-то опции командной строки, их можно указать здесь же:

gdb ./some-program -a -b

При запуске отладчика появляется приглашение командной строки вида:

GNU gdb (GDB) 7.2-ubuntu Copyright (C) 2010 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later  This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.  Type "show copying" and "show warranty" for details. This GDB was configured as "i686-linux-gnu". For bug reporting instructions, please see: ... Reading symbols from /home/portnov/LUG/src/square...done. (gdb)

Работа с отладчиком, в общих чертах, напоминает работу с командной оболочкой. Вы вводите команды, отладчик их исполняет. Как и в командной оболочке, работает автодополнение команд по клавише Tab. Кроме того, для краткости можно сокращать команды до первых нескольких букв — лишь бы избежать неоднозначности.

К наиболее часто используемым командам относятся:

list

Напечатать очередной кусок исходника (печатается 10 строк). Можно указать конкретные номера строк после имени команды, например l 10,15.

run

Запустить программу на выполнение под отладчиком. Программа будет выполняться до ближайшей точки останова, или до конца.

break

Установить точку останова. Номер строки, на которой нужно установить точку останова, указывается после имени команды.

next

Выполнить одну строку программы.

print

Вычислить и напечатать выражение. Выражение указывается после команды. Таким образом можно, например, однократно посмотреть значение какой-нибудь переменной.

display

Добавить выражение к списку постоянно отображаемых. Значения этих выражений будут показываться после исполнения каждой команды. Рядом с каждым выражением печатается его номер в списке. Удалить выражение из списка можно командой undisplay с номером выражения.

quit

Выход из отладчика.

Более подробную информацию по GDB см. в man gdb.

Оконная система X11

Исторически в UNIX не было и не могло быть никакой графической среды, потому что не было графических дисплеев. Графическая среда для UNIX появилась примерно тогда, когда появились распространённые графические дисплеи: в 1984. Сначала она называлась W (от Window), затем её усовершенствовали и назвали следующей буквой алфавита — X, следующая версия называлась X2… Сейчас имеем X11.

X11 представляет собой, прежде всего, сетевой протокол поверх TCP/IP и UDP/IP. У протокола есть клиент и есть сервер. Клиент посылает последовательность запросов вида «нарисуй мне окошко», «нарисуй на нём кнопочку», а сервер их исполняет. Один из главных принципов X11 — «определять механизмы, а не политики». Протокол предоставляет возможность, скажем, рисовать окошки, а как именно они будут отображаться — не определяет.

Наиболее распространённым X-сервером сейчас является Xorg (http://x.org); всё ещё жив XFree86; под Windows актуален Xming; выпускаются аппаратные X-серверы — комплекты «монитор + клавиатура + мышка», в которых поддержка серверной функциональности X11 реализована аппаратно — такие комплекты используются в качестве графических терминалов.

Протокол X11, в отличие от, скажем, HTTP, является бинарным, а не текстовым — это сделано из соображений экономии пропускной способности сетевого соединения и простоты разбора запросов сервером. Но это усложняет создание клиентов этого протокола: собирать замысловатые бинарные X11-запросы заведомо сложнее, чем, например, текстовые HTTP-запросы. Поэтому для написания X-клиентов используются специальные библиотеки функций, формирующих и отправляющих серверу X-запросы. Наиболее распространена библиотека libX11. Более современным вариантом является libxcb.

Запросы X11 весьма низкоуровневые. Например, чтобы реализовать функциональность кнопки, нужно нарисовать в окне прямоугольник, написать в нём текст, ждать в цикле нажатия кнопки мыши, и при каждом нажатии проверять, где щёлкнули — внутри прямоугольника или вне него. Поэтому стали появляться так называемые тулкиты — библиотеки, являющиеся высокоуровневыми обёртками над libX11.

Исторически первым тулкитом был Athena3D. Потом были Motif и Tk. Сейчас наиболее распространены GTK+ и Qt (Qt, строго говоря, представляет собой не X11-тулкит, а многоцелевой кроссплатформенный набор библиотек, который может использоваться в качестве X11-тулкита).

Hello, world на GTK+

В качестве примера рассмотрим следующую программу. Она показывает окно с одной кнопкой. При нажатии на эту кнопку появляется сообщение «Hello, world».

#include <gtk/gtk.h>

// This function displays message dialog.
// main_window parameter should be set to parent window of the dialog.
void message_box (GtkWindow* main_window, gchar *message) {
GtkWidget *dialog, *label, *content_area;

// Create a dialog
dialog = gtk_dialog_new_with_buttons ("Message",
main_window,
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_STOCK_OK,
GTK_RESPONSE_NONE,
NULL);
// Create a label
content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
label = gtk_label_new (message);

// On "response" signal (it's called when user clicks a button in
// the dialog), destroy the dialog.
g_signal_connect_swapped (dialog,
"response",
G_CALLBACK (gtk_widget_destroy),
dialog);

// Add a label
gtk_container_add (GTK_CONTAINER (content_area), label);
// Show the dialog
gtk_widget_show_all (dialog);
}

// Callback for delete-event signal
static gboolean delete_event( GtkWidget *widget,
GdkEvent *event,
gpointer data )
{
// If return TRUE, window will not be closed.
// This may be used to preven closing window in some situations.
return FALSE;
}

// Callback for destroy signal
static void destroy( GtkWidget *widget,
gpointer data )
{
// End main GTK+ event loop
gtk_main_quit ();
}

// Callback for button click
static void hello ( GtkWidget *widget,
gpointer data )
{
// "data" parameter represents main window here
message_box(GTK_WINDOW(data), "Hello, world!");
}

int main( int argc,
char *argv[] )
{
GtkWidget *window;
GtkWidget *button;

// Init GTK+
gtk_init (&argc, &argv);

// Create main window
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

// Set up callbacks for some signals
g_signal_connect (window, "delete-event",
G_CALLBACK (delete_event), NULL);

g_signal_connect (window, "destroy",
G_CALLBACK (destroy), NULL);

// Set window borders width
gtk_container_set_border_width (GTK_CONTAINER (window), 10);

// Create labeled button
button = gtk_button_new_with_label ("Hello World");

// Set up callback for "clicked" signal of the button.
// Pass main window as second parameter.
g_signal_connect (button, "clicked", G_CALLBACK (hello), (gpointer)window);

// Pack the button into window
gtk_container_add (GTK_CONTAINER (window), button);

// Show the button
gtk_widget_show (button);

// Show the window
gtk_widget_show (window);

// Run main GTK+ event loop.
gtk_main ();

return 0;
}

Собирается эта программа командой вида

gcc -o gtk-hello $(pkg-config --cflags gtk+-2.0) $(pkg-config --libs gtk+-2.0) gtk-hello.c

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

Дополнительная литература

[raymond] Реймонд, Эрик С. Искусство программирования для UNIX. — Пер. с англ. — М.: Издательский дом «Вильямс», 2005. — 544с., ил.

[kernigan_pike] Керниган Б., Пайк Р. UNIX. Программное окружене. — Пер с англ. — СПб: Символ-Плюс, 2003. — 416с., ил.

[kernigan_richie] Керниган Б., Ритчи Д. Язык программирования C. — Пер. с англ. — Москва: Вильямс, 2006. — 304 с.

[vcs_git] Портнов И. Системы контроля версий. Git. URL: http://iportnov.blogspot.com/2008/06/git.html

[los-t_git] Los-t. Git guts (серия статей в ЖЖ). URL: http://los-t.livejournal.com/tags/git+guts


вторник, марта 29, 2011

Изготовление презентаций из Asciidoc с помощью Beamer

В одном из недавних постов я говорил, что из Asciidoc-разметки можно сделать много всяких разных выходных форматов. Среди всего прочего, можно делать и презентации. Не слишком «навороченные», правда, но для многих случаев этого хватает.

Схема такая:



Сначала из Asciidoc делаем DocBook. Это можно сделать командой вида asciidoc -b docbook input.txt. Только вот по такой команде получится файл в формате DocBook 4.5, а моя XSL-таблица понимает только DocBook 5.0+. Поэтому берём конфигурационные файлы asciidoc для изготовления DocBook 5.0, например, отсюда, и даём команду вида

asciidoc -b topic input.txt

Получается файл input.xml. Теперь берём XSL-таблицу, преобразующую DocBook 5.0+ в TeX-овский исходник, например, здесь (эта таблица заведомо не полная, но для моих задач хватает; кому не хватит — добавляйте туда свои шаблоны :)), и даём команду вида

xsltproc beamer.xsl input.xml > presentation.tex

Ну а теперь компилируем этот исходник командой вида

xelatex presentation.tex

(может понадобиться запустить эту команду несколько раз, чтобы проставились все ссылки и т.п).

Таким образом сделана, в частности, упомянутая в одном из предыдущих постов презентация (из вот такого исходника).

воскресенье, марта 27, 2011

Об изготовлении EPUB из DocBook

Тут не так давно я уже упоминал, что из DocBook можно делать, в том числе, и EPUB. Однако процесс не вполне тривиальный; мне кажется, его стоит расписать несколько подробнее.

Вот тут в IBM DeveloperWorks описан такой процесс. Однако изготовленный по этому рецепту файл будет обладать парой недостатков:

  • Это будет файл устаревшего формата «Open Epub»;

  • В нём не будет внедрённых шрифтов.

Такой файл благополучно открывается чем-нибудь типа Okular. Однако AdobeViewer на читалках типа PocketBook 301+ в таких файлах показывает русские буквы вопросиками. Это происходит из-за того, что встроенного шрифта в файле нет, AdobeViewer пытается использовать шрифт по умолчанию, а в нём нет русских букв.

Так что будем делать «более правильный» EPUB. «Нормальный» EPUB-файл — это zip-архив, в котором содержатся следующие вещи:

  • Файл с именем «mimetype»; этот файл обязательно должен идти первым в архиве, не должен быть сжат, и его содержимым должна быть строка «application/epub+zip» (без символа новой строки в конце).

  • Директория с именем «META-INF»; в ней — файл container.xml. Примеры содержимого этого файла можно нагуглить, главное что в нём должно быть прописано — ссылка на файл contents.opf (OPS/contents.opf).

  • Директория с именем «OPS»; В ней — HTML-файлы с собственно содержимым книги, картинки к ним, шрифты (файлы *.ttf), и файл contents.opf и таблица стилей CSS.

Для того, чтобы сделать такой файл из имеющегося DocBook-файла book.xml, нужно проделать следующее:

  1. Выполнить команду вида:

    xsltproc --stringparam epub.oebps.dir OPS/ \
    --stringparam epub.embedded.font Ubuntu-R.ttf \
    --stringparam html.stylesheet style.css /usr/share/xml/docbook/stylesheet/docbook-xsl/epub/docbook.xsl \
    book.xml

    Здесь вместо «Ubuntu-R.ttf» можете указать какое-нибудь другое имя шрифтового файла. Путь к epub/docbook.xsl на вашей системе может быть и другим, подставьте ваш. Эта команда создаст директори META-INF и OPS в текущей директории.

  2. Скопировать откуда-нибудь или написать таблицу стилей OPS/style.css. Стили можно настраивать на ваш вкус, но надо учитывать следующее:

    • Если вы предполагаете, что документ будут читать на E-Ink устройстве, то не делайте слишком сложную цветовую гамму; экран мало того что чёрно-белый, так ещё не очень хорошо показывает тонкие оттенки. Идеально было бы обойтись 4мя или 8ю оттенками серого (включая чёрный и белый).

    • В таблице должны быть правила, указывающие конкретные шрифты, наподобие следующих:

      @font-face {
      font-family: "Ubuntu";
      font-style: normal;
      font-weight: normal;
      src: url(Ubuntu-R.ttf)
      }

      @font-face {
      font-family: "Ubuntu";
      font-style: normal;
      font-weight: bold;
      src: url(Ubuntu-B.ttf)
      }

      @font-face {
      font-family: "Ubuntu";
      font-style: italic;
      font-weight: normal;
      src: url(Ubuntu-I.ttf)
      }

      @font-face {
      font-family: "Ubuntu";
      font-style: italic;
      font-weight: bold;
      src: url(Ubuntu-BI.ttf)
      }

      @font-face {
      font-family: "DroidSansMono", monospace;
      font-style: normal;
      font-weight: normal;
      src: url(DroidSansMono.ttf)
      }

      pre {
      white-space: pre-wrap;
      font-family: "DroidSansMono", monospace;
      font-size: 85%;
      }

      (имена шрифтов, конечно, укажите ваши).

  3. Скопировать откуда-нибудь файлы шрифтов (*.ttf) в директорию OPS/.

  4. Исправить файл OPS/content.opf, добавив туда ссылки на шрифты. Это делается строчками вида:



    <item id="font2" href="Ubuntu-I.ttf" type="application/x-font-ttf"/>

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

  5. Собрать собственно EPUB-файл командами вида

    $ echo "application/epub+zip" > mimetype
    $ zip -0Xq my-book.epub mimetype
    $ zip -Xr9D my-book.epub META-INF OPS
  6. (Опционально) Проверить получившийся файл при помощи программы epubcheck командой вида

    java -jar epubcheck.jar my-book.epub

Получившийся файл my-book.epub должен нормально читаться как на компьютере, так и на книгочиталках.

суббота, марта 19, 2011

Небольшое разъяснение по комментариям

В разных постах в комментариях товарищи меня корректируют — где-то я ошибся, где-то что-то перепутал. Я специально не вношу изменения в пост — чтобы было видно, что вот тут я ошибался, а вот тут меня исправили.

Подготовка технической документации с использованием asciidoc и DocBook

Это мой конспект для выступления на семинаре, проводившемся нашей LUG. См. также презентацию к докладу.



Глава 1. Актуальность, цели и задачи

Вобще говоря, разработка технической документации — это достаточно обширная область. Техническая документация, по идее, прилагается чуть ли не ко всему подряд — инструкция к чайнику, проектная документация к мосту, мануал к программе. Конечно, в разных случаях аудитория у документов разная, разные и цели подготовки документации, а потому используются разные подходы. Я буду говорить в основном о подготовке документации к программным продуктам, хотя практически всё это применимо и к разработке других видов документации (хотя, вероятно, предлагаемой методики недостаточно).

Целью разработки документации к программному продукту является предоставление пользователю достаточно полной информации о продукте в том виде, в котором это удобно пользователю. Отсюда вытекают следующие задачи:

  • Полная документация к большому продукту будет большой. Поэтому необходимо обеспечить автору документации простые и удобные средства для её подготовки, стараться не тратить время автора на посторонние вещи типа оформления этой документации или тонкостей xml-разметки;
  • Пользователи у продукта, возможно, будут разные, а потому документацию необходимо предоставлять в разных форматах; самые популярные форматы — PDF, HTML и CHM;
  • Так как пользователи разные, разной должна быть комплектация документации. В одних случаях нужно только руководство пользователя, в других — только руководство программиста, в третьих — и то, и другое.

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

Глава 2. Обзор технологий

Я начну с краткого обзора технологий, которые применяются для подготовки технической документации. Наиболее известны следующие технологии:

  • MS Word и подобные системы;
  • Системы «единого источника»:

    • Help&Manual и аналоги;
    • DocBook;
    • DITA;
    • и другие.

Рассмотрим некоторые из этих технологий несколько подробнее.

Глава 3. Help&Manual

Главным плюсом этой системы часто является то, что она уже внедрена и работает. Также несомненный плюс — это система единого источника, т.е. из одного текста можно получить документ в разных форматах. Особенность этой программы и аналогов: это WISIWYG-системы. Насчёт того, является ли эта особенность плюсом или минусом, по сей день не утихают споры. Плюс WISIWYG-технологий очевиден, он заложен в названии: можно сразу видеть, как будет выглядеть текст в результате. К минусам WISIWYG относят следующие пункты:

  • Есть такое высказывание: если система предоставляет возможность что-нибудь настраивать, она, фактически, заставляет вас это настраивать. В данном случае это относится к тому, что автор тратит время не только на написание текста, но и на его оформление, хотя это, по идее, не его задача.
  • Такие системы склонны порождать совершенно дикую и нечитаемую разметку при конвертировании в HTML, LaTeX и другие подобные форматы. Например, легко можно получить разметку наподобие: разметка. Это сказывается, как только появляется необходимость поправить такую разметку «руками».
  • При работе в таких системах люди склонны использовать не логическую, а физическую разметку (например, помечать кусок текста жирным шрифтом, вместо того чтобы отмечать этот текст как заголовок). Что интересно, большинство таких систем предоставляют возможность использования логической разметки, иногда даже удобную, но по каким-то психологическим соображениям люди не склонны её использовать. Это приводит к проблемам при изменении общего стиля оформления документа, а также зачастую при конвертировании в другие форматы.

В связи с последним пунктом можно упомянуть системы с похожим девизом — WISIWYM (What You See Is What You Mind). Таких систем значительно меньше. К ним относится, например, LyX (http://www.lyx.org). Такие системы вобще не позволяют использовать физическую разметку. LyX, например, не позволит вам поставить два пробела или два разрыва строки подряд.

Известные минусы H&M и аналогов:

  • Закрытый бинарный формат файлов; впрочем, это исправляется в новой версии H&M, где используется XML-формат;
  • Следствие: для просмотра и редактирования нужна сама программа H& M;
  • Все эти системы платные;
  • Нет средств для управления версиями документации;
  • Нет средств для обеспечения одновременной работы нескольких человек над одним документом.

Наш отдел документации, видимо, сравнительно небольшой. Важнее, на самом деле, что над одним документом у нас работает 1-2 человека - писатель и переводчик. Поэтому пока что отсутствие средств для управления версиями нам не слишком мешает.

Глава 4. DocBook

DocBook — это формат для ведения технической документации, основанный на XML. Разработка DocBook началась в 1991 году и, в разное время, развивался и поддерживался этот формат различными организациями:

  • 1991—1994: HaL Computer Systems и O’Reilly & Associates (+ большое влияние Novell и Digital);
  • 1994—1998: Davenport Group (+ большое влияние Novell и Sun);
  • 1998—2007: OASIS. DocBook XML v4.5;
  • 2 Aug 2008: OASIS. DocBook XML v5.0.

Раньше поддерживалась версия DocBook, основанная на SGML вместо XML (DocBook/SGML). SGML более «дружелюбен», более приспособлен к написанию человеком. Например, некоторые теги можно не закрывать, некоторые можно даже и не открывать. Это свойство досталось в наследство от SGML в HTML. Если в HTML-файле написать



Привет мир! <a href=http://site.com/>site.com</a>


(это полное содержимое файла), то браузер обязан интерпретировать это так же, как код



<html>
<header></header>
<body>
<p>Привет мир! <a href="http://site.com/">site.com</a></p>
</body>
</html>

Сразу видно, что это позволяет значительно упростить разметку. Однако это делает программы для обработки SGML гораздо более сложными и неустойчивыми в работе. До сих пор не существует программ, разбирающих любую разновидность SGML!

Так что сейчас DocBook/SGML не поддерживается, поддерживается только DocBook/XML.

К плюсам этой технологии можно отнести:

  • Не только открытый, но и стандартный формат, широко используемый как в OpenSource, так и в коммерческих компаниях;
  • Существующие инструменты позволяют преобразовывать DocBook во все распространённые форматы (HTML, CHM, PDF итп);
  • За счёт открытости и стандартности возможно реализовать преобразование в любой другой формат;
  • Формат очень полный; в спецификации предусмотрены, например, специальные теги для сочетаний клавиш, для пунктов меню, и т.п;
  • Формат можно расширять, дописывая собственные DTD;
  • Это XML, а для работы с XML существует огромное количество инструментов и технологий;
  • Т.к. исходные файлы текстовые, их можно держать под управлением системы контроля версий.

К минусам я бы отнёс:

  • Высокий порог вхождения; нужно знать XML и саму спецификацию DocBook;
  • Нетривиальная настройка вывода в HTML, PDF и т.п; Например, для вывода в PDF используется либо промежуточный вывод в TeX (так что для настройки нужно знать TeX), либо промежуточный вывод в XSL-FO (так что для настройки нужно знать XSL-FO);
  • Системы контроля версий не так уж хорошо приспособлены к хранению XML-файлов.

Менее очевидным минусом является малое количество WISYWIG-программ для редактирования DocBook (но они есть, из бесплантых — Serna, разрабатываемая нашими соотечественниками).

На мой личный взгляд, XML не приспособлен для написания и чтения человеком «вручную». Тем не менее, я знаю как минимум одного технического писателя, благополучно пишущего большие документы в DocBook именно «вручную».

Глава 5. DITA

Некоторыми (особенно в США) формат DocBook рассматривается как устаревающий. На смену ему приходит технология DITA. Это также формат, основанный на XML. Однако базовый формат определяет только основные теги, которые используются в любом документе. Все более специфичные теги определяются в так называемых специализациях DITA с помощью деклараций DTD. Существует несколько стандартных специализаций. Предполагается, что каждый пишет специализацию для своих нужд.

DITA содержит следующие нетривиальные идеи:

  1. Текст пишется во многих небольших файлах, называемых топиками (topics). Каждый топик — это логически завершённый кусок документа.
  2. Эти топики объединяются в выходной документ согласно содержимому других файлов — карт документа. Таким образом, получается, что текст отделён от структуры документации, текст и структуру можно менять независимо.
  3. Каждый топик имеет определённый тип (doctype в смысле DTD), и структура топика определяется его типом. Стандартные типы — Задача, Концепция и Справочник. Например, топик типа Задача описывает последовательность шагов, необходимых для выполнения задачи. Топик типа Справочник содержит перечень элементов и их описаний, и т.д. Благодаря специализации всегда можно добавить свои типы топиков.

Плюсы:

  • БОльшая, даже по сравнению с DocBook, гибкость формата;
  • И все плюсы DocBook.

Минусы:

  • По сравнению с DITA Docbook рассматривается как формат для чайников; Как минимум, здесь надо знать не только XML, а ещё и DTD;
  • Т.к. формат ещё относительно новый, он пока слабее поддерживается;
  • Тот же минус, что у DocBook: не лучшая поддержка XML со стороны систем контроля версий;
  • Тот же минус, что у DocBook: сложность настройки вывода в PDF, по тем же причинам.

Про бесплатные WISIWYG-редакторы для DITA я ещё вобще не слышал.

Глава 6. Wiki-подобные разметки

Чтобы не писать в XML, широко используются различные Wiki-подобные разметки. Их очень много. Практически каждый wiki-движок предлагает свой язык разметки. Наиболее известны разметки MediaWiki (используется википедией) и dokuwiki.

Не все wiki-разметки поддерживают все средства форматирования, многие ограничены. Разметка MediaWiki поддерживает практически любое форматирование, но выглядит такая разметка страшновато. Dokuwiki выглядит более читаемо, но не поддерживает некоторое сложное форматирование.

Среди таких разметок известна ещё разметка Asciidoc. asciidoc - это скрипт на python, преобразующий соответствующую разметку в несколько выходных форматов. Есть возможность определить «бэкенды» для вывода в какие-нибудь ещё форматы. Достаточно хорошо поддерживаются выходные форматы HTML и DocBook. DocBook, в свою очередь, можно преобразовать практически во что угодно.

Существует программа (и библиотека) pandoc, предназначенная для конвертации различных форматов разметки. Например, с помощью этой программы можно преобразовать RST в Markdown, HTML или MediaWiki, или HTML в Markdown, и т.п. Эта библиотека достаточно гибкая, к ней можно писать фильтры — readers и writers. Я поддерживаю reader и writer для Asciidoc.

Плюсы такой технологии:

  • Меньший порог вхождения, чем у XML-технологий;
  • Системы контроля версий лучше всего работают как раз с plain text файлами;
  • При использовании DocBook в качестве промежуточного формата получаем все плюсы технологии DocBook;
  • В принципе, можно использовать DITA вместо DocBook в качестве промежуточного формата;
  • За счёт наличия конвертеров всегда есть возможность уйти с этой технологии на какую-то другую.

Минусы:

  • Порог вхождения всё-таки есть, хоть и небольшой;
  • Из-за использования DocBook в качестве промежуточного формата получаем минус DocBook — сложность настройки вывода в PDF.

Глава 7. Применение для совместной работы

В основном здесь рассматривается использование web-интерфейса для работы с документами. Однако надо заметить, что это, не единственный вариант. Как минимум не стоит забывать про возможность использования систем контроля версий для совместной работы над документами.

Сначала я стал рассматривать возможность использования уже имеющейся веб-платформы — Confluence. Её, в принципе, можно использовать для разработки документации, но:

  • Своеобразная разметка; это тоже wiki-разметка, но уникальная, никто кроме confluence её не поддерживает;
  • Слабая поддержка импорта: практически есть возможность только импортировать doc-файлы;
  • Слабая поддержка экспорта: confluence может экспортировать pdf, но так, что этот вариант не многим лучше распечатки веб-страницы на pdf-принтере; другие форматы вывода не поддерживаются;
  • Сами тексты хранятся в БД, и в случае, если придётся переходить на другую технологию, тексты придётся как-то нетривиально добывать из БД.

Я давно использую Asciidoc, поэтому стал искать wiki-движки, которые бы поддерживали asciidoc. Как оказалось, среди огромного количества вики-движков только единицы поддерживают больше одного формата разметки. Единственный движок, поддерживающий Asciidoc — это Gitit, и как раз за счёт использования библиотеки pandoc. Кроме того, Gitit хранит тексты не в БД, а в текстовых файлах. Для контроля версий может использоваться одна из нескольких систем контроля версий (Git, Mercurial, Darcs). Так что если открыть доступ к этому репозиторию по сети, то получаем возможность редактирования содержимого вики с локального компьютера, любимым текстовым редактором, без веб-форм и вообще без соединения по сети (сеть нужна только чтобы взять содержимое и положить его обратно).

Gitit использует для обеспечения совместного редактирования достаточно простую модель. Предположим, один человек (A) начал редактировать страницу, затем (не дожидаясь пока A закончит) её начал редактировать Б. В таком случае, если изменения, внесённые двумя людьми, не пересекаются (например, они редактировали разные части страницы), будут применены все изменения. Иначе тому, кто закончит последним, будет предложено разрешить конфликты вручную.

Системы контроля версий используют примерно ту же модель, но могут обеспечивать бОльшую гибкость: см., например, git merge vs git rebase.

Глава 8. Об импорте и экспорте

Собственно, об экспорте я кратко уже упомянул. Программа pandoc позволяет конвертировать документы в разметке asciidoc в несколько распространённых форматов, включая docbook. В свою очередь, docbook можно сконвертировать во что угодно, даже в xml-формат последней версии H&M.

В случае необходимости можно преобразовать имеющиеся документы в формат asciidoc. В случае с исходными документами в формате H&M используемой, 4й версии, для этого придётся:

  • Конвертировать исходный hmx файл в xml-формат новой версии H&M, используя конвертер, поставляемый с этой новой версией;
  • С помощью небольшого скрипта и XSL-таблицы преобразовать полученный набор xml-файлов в один xml-файл docbook;
  • С помощью ещё одной XSL-таблицы преобразовать docbook в asciidoc.

При этом возникают мелкие недочёты, связанные с особенностями работы H&M. Например, в его xml-файлах встречается разметка вида:


слово слово<strong> выделено </strong>текст...

или даже


слово <strong></strong> дальше текст...

Такая разметка, конечно, превращается в не вполне корректную asciidoc-разметку. Но это, вообще говоря, мелочи, и исправляется "поиском и заменой" в текстовом редакторе.

Глава 9. Улучшение поддержки DocBook

Для обеспечения более полной поддержки DocBook я определил в своём конфигурационном файле Asciidoc дополнительные макросы:

  • icon:image.png[] для вставки иконок;
  • button:button.png[Текст кнопки] для упоминаний кнопок;
  • label:[Текст] для заголовков элементов управления;
  • screenshot::screen.png[] для больших скриншотов.

Глава 10. Настройка вывода в PDF

Существует несколько способов преобразовать DocBook в PDF. Наиболее распространены два:

  • С использованием XSL-FO в качестве промежуточного формата;
  • С использованием LaTeX в качестве промежуточного формата.

XSL-FO — это ещё один XML-формат для представления форматированного текста. В отличие от DocBook, этот формат ориентирован на низкоуровневую физическую разметку. Преобразовать DocBook в XSL-FO несложно, для этого есть свободно доступные XSL-таблицы. Преобразованием XSL-FO в PDF занимаются специальные программы, называемые XSL-FO Processors. Их существует несколько:

  • Apache FOP (бесплатный, но не поддерживает некоторых возможностей и имеет некоторые сложности с настройкой русскоязычного вывода);
  • JadeTeX (специальная версия TeX, настроенная так, что понимает xml на входе);
  • PassiveTeX (что-то из той же серии);
  • XEP (коммерческий продукт, полностью функциональный, но опять же есть сложности с русским языком).

Собственно, различные сложности с русским языком есть у всех этих вариантов (они всегда решаемы, но это требует времени). Разработка JadeTeX и PassiveTeX, к тому же, на данный момент заморожена.

Другой вариант преобразования — использовать в качестве промежуточного формата TeX. Для преобразования DocBook в TeX используется программа dblatex, представляющая собой скрипт на Python и набор XSL-таблиц. Полученный TeX-файл, в зависимости от настроек dblatex, должен быть затем обработан LaTeX, PDFLaTeX или XeLaTeX. Я использую XeLaTeX. Он имеет следующие преимущества:

  • Полная поддержка Unicode (те, кто работал с «традиционными» версиями TeX, знают, что поддержка Unicode в них реализована с помощью хитроумных хаков);
  • XeLaTeX использует шрифтовую подсистему операционной системы (GDI на Windows, Freetype на Linux) вместо специфической и местами устаревшей шрифтовой подсистемы TeX. В частности, доступны все установленные в системе шрифты;
  • Формат вывода по умолчанию — PDF.

Сгенерированные dblatex документы используют специальный стиль, поставляемый в комплекте dblatex. Его, конечно, можно модифицировать. XSL-таблицы из комплекта тоже можно настроить под свои задачи.

Глава 11. Вывод в CHM

Вывод в CHM поддерживается «стандартным» набором DocBook-XSL. Правда, для полноценной поддержки русского языка надо добавить следующие параметры к xsltproc:

xsltproc -stringparam chunker.output.encoding windows-1251 \
-stringparam htmlhelp.encoding windows-1251 -stringparam chunker.output.indent yes

и в получившемся файле htmlhelp.hhp указать русский язык:

sed -i -e 's/0x0409.*/0x0419 Russian (RUSSIA)/' chm/htmlhelp.hhp

Компилятор CHM (hhc.exe) из комплекта MS HTML Help Workshop благополучно запускается под Wine, достаточно «подсунуть» ему windows-библиотеки mfc40.dll и itss.dll.

Глава 12. Поддержка составных документов

Поддержку документов, состоящих из многих файлах, в рассматриваемой схеме можно реализовать, как минимум, двумя способами. Во-первых, asciidoc сам поддерживает директиву include, работающую подобно директиве #include препроцессора C. Устанавливая значение параметра leveloffset до и после директивы include, можно «сдвинуть» все заголовки во включаемом файле (например, все заголовки первого уровня во включаемом файле будут считаться заголовками второго уровня, и т.п.). Однако для документов с большой степенью вложенности это не очень удобно, легко запутаться в значениях параметра leveloffset (осбоенно в случае, если нужно делать несколько вариантов документации в разной комплектации).

Другой вариант — использовать XInclude уже после преобразования документов в DocBook/XML. Этот вариант более гибкий и удобный. Например, можно включать не весь файл целиком, а только нужные секции. Для этого пишется ещё один XML-файл, называемый мастер-документом, в котором указана мета-информация (заголовок и т.п.), а также структура документа и набор тегов , включающих нужные разделы других документов. Но такой XML содержит мало полезной информации и много служебной разметки. Чтобы не писать мастер-файл в XML руками, я использую небольшой скрипт, делающий этот файл из YAML-файла вида

title:  Заголовок составного документа
author: Ilya V. Portnov
date: September 2010
contents: # Содержание
- programmer: # Здесь будут включены разделы из файла programmer.xml
- used_abbrevs # \
- add_docs # }- Список разделов из programmer.xml
- sys_reqs # /
- part: # Здесь будет сформирована часть документа
Концепция: # с заголовком part
- concept: # Внутрь этой части будут помещены разделы из concept.xml
- concept_goals # ...
- concept_components
- technologies
- concept_features
# Здесь включаются все главы, являющиеся дочерними
# по отношению к элементу 'general_descr'
- part:
Руководство программиста. общее описание:
- programmer: [ xpointer(id('general_descr')/chapter) ]
- part:
...
Руководство администратора:
- admin:
- _radixware_starter
- _radixware_server
- _radixware_manager
- user: [ _radixware_explorer ]

Таким образом получается подход, близкий к подходу DITA. Текст хранится в отдельных топиках и объединяется согласно картам документов (в формате YAML). При этом получаем даже дополнительную гибкость по сравнению с DITA, т.к. в YAML-карте документа упоминаются не только имена страниц, а и идентификаторы конкретных секций. Благодаря этому можно, например, переставить разделы в выходном документе, не меняя страниц, а меняя только карту документа.

Глава 13. Профилирование

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

DocBook предусматривает атрибуты тегов для профилирования изначально. Их поддержка в asciidoc добавляется редактированием конфигурационного файла. Например, чтобы пометить абзац как относящийся только к ОС Windows, нужно перед абзацем поместить строку [os="windows"].

Обычно для каждой задачи требуется профилирование сразу по нескольким переменным. Например, у данного заказчика OS Linux, архитектура процессора x86_64, и т.п. Чтобы не указывать каждый раз все параметры вручную, я пишу файл profiles.yaml вида

me:
os: any
audience: author
review:
os: any
audience: reviewer
some_customer:
os: linux
arch: x86_64

Здесь каждой цели профилирования сопоставлен набор пар (имя, значение), по которым должно производиться профилирование. Скрипту нужно только указать цель профилирования в качестве аргумента командной строки.

Кроме того, поддерживаются «профили по умолчанию» для отдельных документов. Эти профили указываются в файле *.ymap. Параметры профилирования, заданные при сборке, перекрывают параметры, заданные для документа по умолчанию.

Глава 14. История изменений

Часто регламент требует, чтобы в начале документа присутствовала таблица, описывающая историю версий документа. Ведение такой таблицы автоматизируется благодаря тому, что для хранения документации используется git. Специальный python-скрипт анализирует вывод команды git log для каждой страницы и ищет сообщениях коммитов пометку [MAJOR] (таким образом, в сводную таблицу попадут только действительно важные изменения). Этот же скрипт пытается получить из таких сообщений версию продукта, к которой относится изменение (ищет сразу после пометки [MAJOR] строку вида PRODUCT: TXRBS-1.1.1.6). Затем другой скрипт сводит таблицы изменений для отдельных страниц в одну таблицу для всего документа и для каждого изменения указывает список изменившихся разделов.

Глава 15. Внешние ссылки

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

Эта задача у меня решается следующим образом. Во всех ссылках указываются только идентификаторы целей. При сборке xsl-стили автоматически делают внешними те ссылки, которые ссылаются на идентификаторы, определённые не в собираемом документе. В случае, когда все связанные документы описаны в одном ymap-файле, на этом этапе сборки все документы существуют в виде одного большого xml-документа (правда, он целиком никогда не формируется в виде файла, а только передаётся между скриптами через unix pipes). В такой ситуации не составляет труда с помощью xsl выяснить, какой идентификатор в каком документе определён.

В случаях же, когда некоторые из документов, на которые указывают ссылки из собираемого документа, описаны другими ymap-файлами, эти другие ymap-файлы перечисляются в собираемом ymap-файле в специальном разделе external-documents. Скрипт, преобразующий ymap в xml-документ, читает ymap-файлы, на которые ссылается данный, и в выходном xml-документе формирует тег вида

    <externs>
<document name="another_document">
<title>Руководство администратора</title>
<pointer target="target1"/>
<pointer target="target2"/>
...
</pointer>
<document name="another_document_2">
...
</document>
</externs>

Таким образом, в xml-документе опять же оказывается определено, какие идентификаторы в каких документах определены, и xsl-стиль может правильно расставить внешние ссылки.

Глава 16 Полная схема сборки