Fork me on GitHub
17/11/2008

Рабочее окружение: LaTeX+Mercurial+SCons

Думаю ни для кого не является секретом, что LaTeX - стандарт формата исходного текста статей по физико-математическим дисциплинам. При написании статей я использую некоторые технические приемы, которые позволяют немного упорядочить процесс работы над статьей. Я расскажу о них в порядке их "внедрения" в своё рабочее окружение. Что характерно, используемые инструменты написаны на Python ;-)

Расположение файлов

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

  • defs.tex - файл с новыми командами, переопределениями, подключениями пакетов
  • _draft.tex - файл-обертка для черновика
  • final.tex - файл-обертка для конечного варианта
  • article.tex - содержимое

Зачем нужны _draft и final: бывает так, что статьи пишутся для конкретного журнала и тогда стилевой файл известен изначально; бывает так что журнал определяется в процессе дописывания статьи и стилевой файл или требования по оформлению становятся известны в последний момент. Разделение контента и стилей/служебных конструкций позволяет, имея одно и то же содержимое, на первых этапах не беспокоиться за оформление, а на поздних этапах - без особых затрат видеть как материал будет выглядеть в журнале.

Для пояснения я приведу пример с содержимым.

defs.tex - файл со служебными данными:

%% defs.tex --- файл со служебными данными
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage[russian,english]{babel}
\usepackage{floatflt}
\usepackage{longtable}
\usepackage{amsmath}
\usepackage{amsfonts}
\usepackage{cite}
\usepackage{graphicx}
\usepackage{eucal}

\renewcommand{\thesection}{\arabic{section}}
\renewcommand{\thefigure}{\thesection.\arabic{figure}}
\renewcommand{\theequation}{\arabic{section}.\arabic{equation}}

\renewcommand{\d}{\partial}

_draft.tex - файл-обертка для черновика:

%% _draft.tex --- файл-обертка для черновика
\documentclass[a4paper]{article}

\include{defs}  % вставляем содержимое служебных инструкций из defs.tex
\usepackage{fancyhdr}
\pagestyle{fancy}
\rhead{}
\lhead{}
\rfoot{\scriptsize{Черновик}}   % в нижнем колонтитуле слева "Черновик"
\begin{document}

\include{article}   % вставляем содержимое статьи
\end{document}

final.tex - файл-обертка для конечного варианта:

%% final.tex --- файл-обертка для конечного варианта
\documentclass[12pt,a4paper]{article}

\include{defs}  % вставляем содержимое служебных инструкций из defs.tex

\textwidth=165mm
\textheight=240mm
\oddsidemargin=3mm
\evensidemargin=3mm
\topmargin=0cm
\headheight=0mm
\headsep=0mm

\renewcommand{\baselinestretch}{1.2}    % интерлиньяж
\frenchspacing

\begin{document}

\include{article}   % вставляем содержимое статьи
\end{document}

И содержимое статьи:

%% article.tex --- статья
% Сгенерирована Яндекс.Рефератами
\section{Изобарический определитель системы линейных уравнений: основные моменты}

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

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

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

При таком расположении файлов, для "компиляции" нужно запускать _draft.tex либо final.tex, с article.tex ничего не получится - нет необходимых LaTeX'у инструкций о типе документа.

Mercurial

Тут всё легко и просто: всё лежит в hg-репозитории. До Mercurial я использовал Subversion, это не принципиально. В общем, всё равно в какой именно VCS вы будете хранить содержимое, главное чтобы хранили :-). Это не раз меня спасало в различных ситуациях. Про Mercurial на русском очень хорошо написал Александр Соловьев, рекомендую.

SCons

SCons я стал использовать недавно, но результат мне нравится. SCons - это инструмент сборки, по типу Make. Что мне понравилось в SCons: он из коробки умеет правильно работать с .tex (знает как их "компилировать" и знает, что .aux, .log и т.д. - мусор), и у него более вменяемый синтаксис, чем у Make.

Для нашей текущей статьи Sconstruct выглядит примерно так:

DEFAULT_TARGET = '_draft.dvi'
DRAFT = '_draft'
FINAL = 'final'

env = Environment()
env.Alias('draft', DRAFT+'.dvi')
env.Alias('final', FINAL+'.dvi')

common_texs = ['defs.tex', 'article.tex']

draft_dvi = env.DVI(DRAFT+'.dvi', DRAFT+'.tex')
Depends(draft_dvi, common_texs)

final_dvi = env.DVI(FINAL+'.dvi', FINAL+'.tex')
Depends(final_dvi, common_texs)

Default(DEFAULT_TARGET)

Сам SCons достаточно навороченный инструмент, но я его использую по-простому, без изысков. Здесь объявление альясов (чтобы использовать scons draft вместо scons _draft.dvi), указание двух целей: draft_dvi, final_dvi - и их зависимости, определение цели по умолчанию.

Для частных случаев нюансы могут различаться (к примеру, в _draft и final подключены разные наборы исходных файлов), но в целом, я использую такой Sconstruct как шаблон.

Теперь "компиляция" черновика выглядит так: scons, а конечного варианта scons final. Очистка директории от мусора, соответственно scons -c и scons -c final. Рабочий пример можете посмотреть в sandbox.

Бонус: tiprev keyword

В добавок расскажу про совсем свежую штуку. Давно я хотел, чтобы вместо "Черновик" в нижнем колонтитуле писалось "Черновик, ревизия N", тогда разбираться в ворохе распечатанных версий было бы проще. Большинство VCS (по крайней мере, Subversion и Mercurial это могут, CVS тоже) умеют вставлять в файл номер последней ревизии, в которой файл был изменен. Делается это при помощи ключевых слов (keuwords), что-то вроде $Id$ или $Rev$. Но тут поджидает небольшое разочарование: основной контент находится в article.tex, а конструкция, задающая колонтитул - в _draft.tex. Естественно, в этом случае $Rev$ будет показывать ревизию последнего изменения _draft.tex, а нам нужно, чтобы он показывал последнюю ревизию репозитория. И вот для такого нет встроенных средств. Так что мне пришлось манкипатчить соответствующий плагин Keywords, чтобы вместо $tiprev$ подставлялась последняя ревизия репо. Сам манкипатч можно посмотреть в sandbox. Использование банально -- нужно в hgrc перед включением плагина keywords включить мой "плагин":

[extensions]
tiprev_keyword = /path/to/tiprev_keyword.py
hgext.keyword =

[keyword]
**.tex =

[keywordmaps]
tiprev = {tiprev}
rev = {rev}

После этого можно переписывать в _draft.tex колонтитул на

\rfoot{\scriptsize{Черновик, $tiprev$}}

Последний бонус немного "не доточен", в том плане что он реагирует на название ключевого слова, а не на его значение (в плагине keywords каждое ключевое слово раскрывается стандартными шаблонами, например $Revision$ -- это {node|short}), но связываться с шаблонами мне не особо хотелось, тем более ключевые слова в шаблонах ({node}, {author} и т.д.) захардкодены так, что не подберешься. В общем, не особо изящно, зато работает.

В качестве заключения пара ссылок по теме:

Комментарии

Все статьи