среда, 21 декабря 2011 г.

Кроссплатформенный парсинг конца строки


Лирическое вступление. Мое погружение в SNMP.


Компания, в которой я имел счастье работать до начала 2011 года занималась, ни много ни мало, разработкой, изготовлением, внедрением и поддержкой собственного телекоммуникационного оборудования. Занимаясь разработкой программного комплекса призванного мониторить и управлять всем этим хозяйством из тысяч разнотипных устройств вполне логично было столкнуться с использованием протокола SNMP.

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

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

У каждого творца есть свой порог детализации используемых им объектов. Все чаще я встречаю разработчиков, которые стараются мыслить категориями наиболее верхних порядков. Меня, наоборот, более привлекают низкоуровневые категории. Отчасти поэтому, я решил подойти к SNMP на максимально низком уровне, т.е. с нуля, без использования какого-либо готового кода. Такой подход не только удовлетворил моё любопытство (за счёт компании, разумеется), но и дал максимальную свободу во внедрении протокола в готовую систему на обоих её концах (клиенте и сервере).

Благо, что сама сущность протокола в системе была выделена на уровне готовой спецификации. Любой новый протокол мог быть вставлен в систему обычной динамической библиотекой, удовлетворяющей сформулированной спецификации. Протокольная сущность должна была не только разрешать проблемы кодирования и декодирования согласно особенностям самого протокола, но и конвертировать логику интерфейса верхних слоев системы в систему запросов конкретного протокола. На момент начала внедрения SNMP система уже работала с двумя разными протоколами (родным K095 и неким msk-json, используемом на одном из типов серверов компании).

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

Прошло около недели изучения разного рода документации, пока, наконец в моей голове появилось понимание таких важных для программирования протокола терминов, как SNMP, MIB, ASN.1, BER в едином контексте поставленной задачи.

Наконец я понял как от терминов верхнего уровня протокола SNMP (запросы get, get-next, set и пр.) перейти к последовательности байт, которые отражают эти запросы в сетевом трафике. С этого момента стало особенно интересно. Началось непосредственное кодирование.

Сначала написал код синтеза запросов и отправил его на готовый сервер. К радости, получил с сервера ответ. Написал парсер ответов. Подошёл вплотную к парсингу MIB-файлов. За два дня удалось выполнить синтаксический разбор MIB-а нашей компании. В общем, получилось некое тестовое приложение, написанное полностью с нуля (относительно категорий протокола SNMP), парсящее MIB-файл параметров устройств компании и выполняющих по нему систему запросов. В общем (творцы меня поймут) радости не было предела.

Кроссплатформенный парсинг конца строки


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

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

Напомню, что конец строки, в разных операционных системах представляется по разному и используется для это манипуляция с кодами 0×0A (line feed) и 0×0D (carret return). Такая ситуация усложняет реализацию счетчика строк при потоковом парсинге файла. Так как речь идет о кроссплатформенной системе, то вопрос особенно актуален. Хотя, даже если речь идёт о какой-то конкретной платформе, не факт, что вы будете иметь дело с файлом подготовленным на такой же платформе, в редакторе, который формирует окончание строки в удобном вам виде.

Детализирую проблему.
1. Мы не знаем, один или два кода используются для разделения строк.
2. Мы не знаем, является ли первым (а может и единственным) кодом, код 0×0A или код 0×0D.

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

// m_ucNL - флаг переноса
            // m_iRow - счетчик строк
            // m_iCol - счетчик колонок (позиция символа в строке) 

            // берем очередной символ из потока
            unsigned char ch = pchData[i++];

            if (ch == 0x0a || ch == 0x0d) {
                if (m_ucNL == 0) m_ucNL = ch;
                /* такое разделение условий позволит переключатся только по первому коду!!! */
                if (m_ucNL == ch) {
                    ++m_iRow; // инкрементируем счетчик строк
                }
                m_iCol = 0;
            } else {
                m_ucNL = 0;
                ++m_iCol;
            }

Дополнение от 14 февраля 2012 года

Один из моих знакомых, читателей данного блога, недавно сообщил мне, что нашел ошибку в данном счетчике строк. По его мнению, последовательности типа "\x0A\x0A" (*nix) или "\x0D\x0A\x0D\x0A" (DOS/Windows) будут восприняты таким счетчиком как одна строка.

На случай, если кто-то еще опасается за такую ошибку счетчика, сообщаю, что все нормально. Ошибки нет. Обратите внимание, что флаг m_ucNL приобретает значение только если прежде его значение было равно нулю. Следовательно в последовательности символов разделителей флаг получит значение первого символа. Далее, обратите внимание на то, что инкремент счетчика строк возникает только при условии равенства флага m_ucNL текущему символу разделителя, т.е. счетчик строк будет инкрементироваться только по каждой новой строке.

понедельник, 28 ноября 2011 г.

Немного об ООП в контексте ObjectiveC, Qt и C++




Одновременно с ростом популярности поделок от компании Apple, растет интерес к языку программирования ObjectiveC, который, наряду с библиотекой ObjectiveC-классов Cocoa, широко применяется для программирования под современными платформами Mac OS X (персональные компьютеры от Apple) и iOS (портативные устройства от Apple).

Случилось так, что недавно мне подвернулась удача познакомиться с этим вплотную и, даже, неплохо подзаработать именно на программировании в ObjectiveC (Cocoa), используя популярную в Mac OS X бесплатную среду разработки Xcode.

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

Итак, по порядку. Начнем с общих вопросов объектно-ориентированного программирования (ООП).

Современный стереотип ООП


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

Таково, на мой взгляд, типичное, и, я бы добавил, - стереотипное, представление об ООП сегодня. Более того, в большинстве книг, посвященных языкам Java, Си++ или современным вариантам Pascal, при обсуждении вопросов ООП, согласно моему опыту, не касаются других вопросов. Я немного сомневаюсь насчет языка C#, так как недостаточно хорошо его знаю, но, видимо, там все тоже самое. Отсюда и стереотип относительно ООП, утверждённый в наших головах изучением этих популярных сегодня языков программирования.

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

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

А взаимодействие объектов, как говорит наука, может быть в стиле function-oriented и в стиле message-oriented. Последний вариант взаимодействия объектов, по мнению этой науки, более кошерный, но он не нашел применения в языках Java, C++ и современных объектно-ориентированных диалектах Pascal. Однако, именно message-oriented стиль общения объектов используется в ObjectiveC и является вариантом общения в Qt.

Рассмотрим эти стили взаимодействия объектов более подробно.

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

class A {
public:
  void fun_1();
private:
  void fun_2();
};

A *a = new A();

a->fun_1(); // Напрямую обратились к public-методу fun_1() объекта a, 
            // созданного по образу и подобию класса A.
a->fun_2(); // Так делать нельзя!!! Метод fun_2() закрыт правилами инкапсуляции
            // для внешнего использования.

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

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

Альтернативным способом взаимодействия объектов является способ, ориентированный на сообщения - message-oriented. Этот случай можно представить в виде аналогии отправки почтой некоторого письма предназначенного некоторому адресату. Если почта не найдет адресата, то письмо вернется к вам со специальным уведомлением о том, что адресат не найден.

Способ совершенно безопасный, но требует поддержки в виде реализации некоторого почтового диспетчера в виде специального run-time объекта. Недостатком этого способа можно сразу назвать потери времени, требуемые на работу почтового диспетчера при каждом обращении к объектам.

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

Теперь, пришло время рассмотреть две известные реализации message-oriented взаимодействия, которые были построены для расширения языков C и C++. Речь, идет об ObjectiveC и Qt, соответственно.

Сообщения в ObjectiveC


ObjectiveC был создан в начале 80-х годов 20-го столетия Бредом Коксом (Brad Cox) в стиле минимальных расширений к языку C, целью которых была реализация поддержки ООП в варианте, навеянном языком Smalltalk.

Тут сразу можно заметить ещё одну существенную разницу между языками C++ и ObjectiveC. На этот раз, не с точки зрения взаимодействия объектов, а с точки зрения совместимости с самим C. Для ObjectiveC справедливо правило: "Любая программа на языке C является программой на языке ObjectiveC.". Для C++ последнее несправедливо.

Разумеется, если мы, по прошествии более 30 лет с момента появления ObjectiveC, говорим о нем как о языке, который является фактически основным языком программирования на популярных платформах от Apple, то понятно, что у него была относительно успешная история. Сегодня мне известны две реализации ObjectiveC: одна из них используется в Apple вместе с важнейшей для "яблочного" программотворчества библиотекой классов Cocoa, а вторая входит в состав коллекции компиляторов GNU и может быть установлена для работы в любом *nix. Вариант ObjectiveC от Apple можно тоже попробовать бесплатно, если использовать популярную для Mac OS X среду разработки Xcode.

Если вам интересно узнать историю языка и поверхностные подробности его синтаксиса и семантики, то, я бы посоветовал отличную статью в википедии по адресу http://ru.wikipedia.org/wiki/Objective-C. Здесь мы не будем повторяться и поговорим лишь о взаимодействии объектов.

В языке ObjectiveC, если у нас есть объект a, то мы можем послать ему сообщение message, например, таким образом:

[a message];

Для простоты даже не будем говорить о передаче параметров в сообщение, а поговорим только о том, как это работает. В составе средств ObjectiveC имеется специальная система run-time, в которой есть диспетчер, обрабатывающий все сообщения направляющиеся объектам. В рамках правил языка определяются жизненные циклы объектов, которые во всех подробностях известны этому диспетчеру. Таким образом диспетчер точно знает, существует ли в данный момент объект, которому назначается сообщение или нет. Кроме того, диспетчер знает о том, какие сообщения и с какими параметрами принимает каждый объект учитывая всю иерархию классов объекта. Таким образом, диспетчер знает, может ли данный объект обработать данное сообщение. В случае, если что-то не так, то сообщение переадресуется обратно отправителю. Если все нормально, то объект получает предназначенное ему сообщение. Теоретически, вы можете отправлять любую строку соответствующую синтаксису сообщения - диспетчер разберётся что с ней делать.

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

Мы уже обсуждали безопасность такого способа передачи сообщений. Остается лишь добавить, что его реализация в ObjectiveC очень удачна и производительна, и многолетний опыт применения ObjectiveC в компании Apple является отличным тому подтверждением.

Сообщения в Qt


На вопрос о том, что такое Qt, ответить непросто. Верно лишь то, что неправильно рассматривать Qt просто как обширную систему классов языка C++ для создания кроссплатформенных графических интерфейсов для разных операционных систем и устройств (сегодня это более 5000 классов и 9000 функций).

Неправильно это по двум серьезным причинам.

Во-первых, код Qt написан не на языке С++, а на специальном расширении этого языка, поддерживающим предварительную обработку кода специальным метакомпилятором (утилита moc - Meta Object Compiler). Правильным будет еще упомянуть утилиту uic (User Interface Compiler), которая преобразует некое XML-описание форм графического интерфейса, созданное графическим редактором форм, в код на Qt. Утилитой uic я не пользуюсь, предпочитая описывать интерфейс пользователя самостоятельно в коде программы.

Во-вторых, для поддержки системы взаимодействия объектов через систему так называемых сигналов и слотов, предлагаемых синтаксисом и семантикой moc, каждая программа, написанная с использованием Qt включает в себя некий runtime-диспетчер, управляющий этим взаимодействием.

История Qt, для меня, тесно пересекается с историей Java (0ak). Обе платформы были задуманы примерно в одинаковое время - в начале 90-х годов 20-го столетия. Наверное, годом рождения обоих идей и начальной реализацией можно назвать 1991 год. Кроме того, библиотеки классов, относящиеся к Qt и Java SE, на мой взгляд, многим похожи. Одна идея кроссплатформенна (Qt, попытка расширить ООП концепции языка C++), а вторая межплатформенна (Java, возможность реализовать ООП без наследия неудач C++).

Основателями Qt были программисты Хаавард Норд и Эрик Чамбенг. Первый, вскоре стал главным управляющим, а второй - президентом организованной ими компании Trolltech, которая и занималась развитием и продвижением Qt до января 2008 года, когда компания была выкуплена Nokia и была переименована в "Qt Software". Значение Qt в современном мире программирования трудно переоценить, о чем можно легко найти в Интернет много информации по запросам типа "история Qt".

Мы же перейдем к тому, что нас интересует сейчас и поговорим подробнее и слотах и сигналах.

Если какой-то объект, написанный в стиле Qt, хочет что-то безопасно сообщить другому объекту Qt, то он излучает сигнал, синтаксис которого описывается при создании объекта. Объект может излучать только те типы сигналов, которые он зафиксировал при своём описании. Сигнал определяется своей сигнатурой, которая определяется типами и последовательностью его параметров. Т.е. разные объекты могут эмитировать одинаковые сигналы - сигналы с одной и той же сигнатурой.

Приведем пример описания сигналов.

signals:
  void clicked();
  void itemChanged(int index);

Здесь описаны сигналы с разными сигнатурами. Сигнал clicked() не передает в себе параметров, а сигнал itemChanged(int index) передает целое значение.

Чтобы выполнить эмиссию сигнала в коде объекта, в котором сигнал описан, надо написать так:
emit clicked();
  ...
  int x = ...
  emit itemChanged(x);

Теперь, если некий объект, написанный в стиле Qt, хочет получить возможность реагировать на сигналы определённой сигнатуры, то он должен описать слот указанной сигнатуры.

Приведем пример того, как можно оформить слоты для описанных выше сигналов.

public slots:
  void first();
  void second(int index);

С точки зрения языка Си++, слоты являются обычными методами, т.е. где-то должна быть написана реализация этих методов. Более того, эти методы могут быть вызваны обычным образом, как и любой другой метод класса, учитывая правила инкапсуляции. Т.е. слоты, также как и другие члены класса, могут быть описаны с квалификаторами области видимости public, protected или private, только к ним добавляется ключевое слово slots, обрабатываемое метакомпилятором.

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

connect(pObjectA, SIGNAL(clicked()), pObjectB, SLOT(first()));
connect(pObjectA, SIGNAL(itemChanged(int)), pObjectB, SLOT(second(int)));

Связь сигнал-слот является связью многие ко многим. Наверное, это то, что существенно отличает его от электронного аналога, где подключение нескольких выходов на один вход является задачей нетривиальной.

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

Сравнение ObjectiveC и Qt


Я бы выделил три существенных различия в реализации message-oriented взаимодействия в ObjectiveC и Qt.

1. Для ObjectiveC передача сообщения является единственным способом обращения к методу объекта. Обращаться к полям объекта можно безо всяких фокусов, но такое обращение, в общем случае, некошерно, учитывая отсутствие надежных правил инкапсуляции данных в объектах ObjectiveC. Для объектов Qt общение через систему сигналов и слотов является не более чем расширением возможностей function-oriented возможностей взаимодействия объектов C++.

2. Сообщения ObjectiveC могут быть динамически созданы и переданы строкой, которая потом будет разобрана диспетчером сообщений, в то время как для Qt и сигналы и слоты должны быть описаны до этапа метакомпиляции.

3. Qt не имеет средств ускорения взаимодействия сигнал-слот за счет возможности запоминания хешированной сигнатуры сообщения.

Отдельно хочется заметить общие особенности между ObjectiveC и Ot. Оба эти артефакта расширяют существующие популярные языки программирования. ObjectiveC расширяет язык C, а Qt расширяет язык C++. В обоих случаях это расширение, фактически представлено предварительной метаобработкой кода и runtime поддержкой полученного кода при его исполнении специальными диспетчерами. Так же как "код на языке C является кодом для ObjectiveC", так же "код на языке C++ является кодом для Qt". Последнее свойство Qt я последнее время активно использую в разработке на языке C++. Мне настолько нравится среда разработки QtCreator, которая используется для кроссплатформенной разработки в Qt, что я также использую её и для обычных кроссплатформенных разработок на языке C++, используя в качестве кроссплатформенных средств сборки qmake и cmake.

четверг, 24 ноября 2011 г.

Как начать работать с Ant




Введение


За последнюю неделю у меня два раза попросили рассказать об Ant. Решил, по этому поводу, написать несколько строчек для начинающих пользователей Ant и для тех, кто не знает, что это такое, но интересуется возможностями применения.

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

По сути своей работы Ant аналогичен известному средству обработки сценариев make. Обычно, многие программисты, работающие с Си/Си++ знакомы с make не понаслышке. Особенно это относится к программистам, которые работают в *nix. Однако make исполняет сценарии зависимые от платформы, или, если быть более точным, от командного процессора для которого они написаны.


Как работает Ant


Ant исполняет сценарии, которые, по умолчанию, располагаются в файле с названием build.xml. Как видно из расширения файла, его содержимое представлено в формате XML.

Корневым тегом документа является тег project. Проект состоит из целей - теги target, а цели состоят из задач. Задач для Ant определено несколько десятков. Каждая задача представлена своим тегом.

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

Рассмотрим это на примере простого сценария. Приведем возможный вариант файла build.xml.

<project name='test' default='release' >

    <target name='spec' >
        <echo message='Target: spec' >
    </target> 

    <target name='release' >
        <echo message='Target: release' >
    </target> 

    <target name='debug' depends='spec' >
        <echo message='Target: debug' >
    </target> 

</project>  

Прокомментируем представленный вариант сценария.

Описывается проект с именем test, в котором целью по умолчанию объявляется цель release. Т.е., если запустить Ant без специального указания исполняемой цели, то, согласно этому сценарию, будет выполнена цель release.

Кроме цели release представлены еще цели debug и spec, причем, исполнение цели debug зависит от исполнения цели spec. Значит, если заказать исполнение цели debug, то сначала выполнится цель spec, и только в случае ее успешного исполнения будет выполнена цель debug.

Во всех трех представленных целях выполняется одна простейшая задача echo, которая выводит в стандартный выходной поток (в нашем случае, в консоль) сообщение заданное атрибутом message.

Теперь остановимся на исполнении этого сценария. В системе Ant, наверное, главной утилитой является утилита ant, которая и призвана исполнять сценарии. Давайте зайдем в командную строку и в директории, где расположен нужный нам файл build.xml выполним следующую команду.

$ ant

(символ $ означает стандартное приглашение пользователя в системах *nix, поэтому его вводить не надо. Для DOS/Windows это будет символ > )

Такой вызов ant приведет к исполнению стандартной цели сценария, которой, в нашем случае, является цель release. Следовательно, при исполнении нашего сценария, мы увидим следующее сообщение в консоли исполнения.

$ ant
Target: release

Чтобы выполнить цель debug нашего сценария, необходимо выполнить ant с аргументом debug.

$ ant debug
Target: spec
Target: debug

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

Для ленивых я предложу несколько готовых ссылок (которые, разумеется, могут в любой момент устареть).
http://ant.apache.org/ - Официальный сайт системы Ant.
http://ant.apache.org/manual/index.html - Главная страница официальной документации по системе Ant. Для просмотра всего списка задач выберите раздел Ant Tasks -> List of Tasks.


Установка Ant


Если вы работаете в Linux, то проблемы с установкой Ant у вас не будет. Разумеется, при условии, что вы умеете пользоваться своим Linux. Обычно, все что нужно сделать это обратиться к своему установщику с просьбой установки Ant, и, после завершения его работы, можно будет сразу приступать к использованию Ant. Здесь проблем возникнуть не должно. Хотя, конечно могут быть и кривые дистрибутивы и кривые руки и даже кривые ноги, но стандартной рекомендации в этом случае дать не получится - разбирайтесь с /dev/head.

Я хочу остановиться подробнее на установке Ant под Windows, так как именно там не все так просто для новичков.

Сложность в том, что разработчики Ant не предоставляют к системе Windows специального native-инсталлятора, который бы решил все проблемы.

Скачайте нужную вам версию Ant с cайта разработчика. Если ссылка не устареет, то попробуйте сделать это со страницы http://ant.apache.org/bindownload.cgi. Скачайте более привычный для пользователей DOS/Windows архив с компрессией zip. Скачайте и распакуйте его средствами, которые вам доступны в вашей системе. Для эксперимента с Ant под Windows я скачал архив apache-ant-1.8.2-bin.zip.

Если вы хотите получить информацию об установке Ant во всех подробностях из первоисточника, то надо опять обратиться к соответствующему разделу сайта производителя Ant. На момент написания этих строк можно было воспользоваться адресом http://ant.apache.org/manual/index.html. На указанной странице нужно выбрать Installing Apache Ant -> Installing Ant.

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


Во-первых.

Назовём корневой директорией Ant директорию, в которой вы найдете поддиректории bin, lib, docs, etc и прочие объекты файловой системы, относящиеся к системе Ant.

Создайте системную переменную ANT_HOME и установите ее в значение корневой директории Ant.

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

Во-вторых.


Для работы Ant требуется Java. Причем, если мы хотим собирать средствами Ant проекты Java (а, как правило, именно для этого мы и ставим Ant), то нам нужен именно пакет JDK, содержащий средства компиляции, а не JRE, где содержатся только средства исполнения байт-кода Java.

Следовательно, в вашей системе, нужно иметь установленным JDK. Если у вас JDK не установлено, то скачайте и установите его. С этим, как правило, проблем не возникает.

Теперь нам следует сообщить системе Ant о расположении установленного в системе JDK. Для этого, следует установить системную переменную JAVA_HOME в значение директории, где лежит установленное JDK.

Также, имеет смысл добавить в список системных путей поддиректорию bin корневой директории JDK. Это упростит вам самим использование предоставляемых утилит JDK. 

В-третьих.

Нужно проследить, чтобы значение системной переменной CLASSPATH было пустым, либо не было определено вообще. Это требование Ant может расстроить тех, кто организует свое рабочее место определяя значение этой системной переменной.

ВАЖНО!!! При установке значений ANT_HOME и JAVA_HOME следите за тем, чтобы ни одно из значений не заканчивалось на символ разделителя директорий - "\" (DOS\Windows) или "/" (POSIX).

Для тех кто не знает как устанавливать системные переменные и расширять список системных путей (системная переменная PATH) подскажу, что в Windows их можно установить из панели управления. Для Windows XP, в классическом размещении инструментов панели, установку системных переменных надо искать в последовательности выбора "Панель управления -> Система -> Дополнительно -> Переменные среды -> Создать|Изменить Системные переменные".

Если верить докуменации Ant, то часть этой работы должен выполнить скрипт ant.bat из поддиректории bin корневой директории Ant. Я не пробовал.

Итак, в моем Windows XP были установлены следующие значения указанных переменных.

JAVA_HOME=C:\Program Files\Java\jdk1.7.0_01
ANT_HOME=C:\apache-ant-1.8.2
PATH=<прежний список путей>;C:\Program Files\Java\jdk1.7.0_01\bin;C:\apache-ant-1.8.2\bin

Чтобы проверить значение установленных переменных, следует открыть консоль Windows (Пуск->Выполнить->cmd) и исполнить команду echo передав ей значение системной переменной. Напомню, что если переменная называется xyz, то обратиться к ее значению в DOS\Windows можно взяв ее в знаки процента, т.е. написав %xyz% (В оригинальном *nix чуть проще - $xyz). Итак, посмотрим:

> echo %JAVA_HOME%
C:\Program Files\Java\jdk1.7.0_01

> echo %ANT_HOME%
C:\apache-ant-1.8.2

> echo %PATH%
<длинный список путей>;C:\Program Files\Java\jdk1.7.0_01\bin;C:\apache-ant-1.8.2\bin

> echo %CLASSPATH%
<должно быть пусто>

среда, 23 ноября 2011 г.

Boost и CMake для Windows (подробности)

Новичкам Boost и CMake посвящается ...

Для тех, кто только начинает осваивать кроссплатформенные средства о которых шел разговор в сообщении "Boost и CMake для Windows" этого блога, я решил добавить немного подробностей в обсуждаемую там тему.

Прежде всего, необходимо заметить, что операционная система Windows относится к сугубо проприентарным системам, и, видимо, как следствие этого, наиболее функциональной средой разработки кода под эту систему является Microsoft Visual Studio. Нравится это кому-нибудь или нет, можно обсуждать отдельно, но данный факт, видимо, не подлежит сомнению. Соответственно, нельзя обвинять софтверные компании в том, что предоставляя компилируемый продукт под Windows они предоставляют, прежде всего, средства компиляции, ориентированные именно на MSVS.

Boost

Набор библиотек Boost, сегодня, является полезным артефактом, который пока не нашел полное свое отражение в стандарте C++, хотя часть из списка библиотек набора Boost уже вошло в стандарт в виде TR1 (Technical Report). Так, если быть конкретным, то сегодня туда входят 10 библиотек, а формируемый стандарт C++0x даже несколько расширил этот десяток в своем TR1. Остальные библиотеки набора Boost (может быть не все, но большая часть) должны войти в стандарт C++0x в виде TR2.

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

Вообще, новичкам небезынтересно будет знать, что многие библиотеки Boost не имеют бинарных файлов, содержащих наборы функций, специфичных для данной среды исполнения, а потому не требуют компиляции.  Т.е. некоторые части Boost можно использовать в виде "как есть", без компиляции. Такие элементы Boost содержат в себе только *.hpp файлы. Для того чтобы их использовать нужно просто выполнить #include <boost/some_boost_lib/some_file.hpp> и указать компилятору путь размещения заголовочных файлов boost, эти файлы уже не были размещены по путям, известным компилятору. Однако, если требуемые вам части Boost основаны не только на *.hpp, но и поддержаны бинарниками, то их придется компилировать для вашей среды исполнения.

Так вот, если вернуться к теме установки Boost под Windows, то следует заметить следующий факт. Сложности, с которыми я столкнулся видимо имеют две основные причины. Во-первых, мои корявые руки, а, во-вторых, то, что я не использовал установку MSVS под которую, собственно, и была заточена инсталляция. Другими словами, я пытался откушать супа деревянной вилкой, что не есть правильно.

Работая и проживая долгие годы в Linux, я привык смотреть на вещи несколько иначе, нежели того требует работа с Windows. Отсюда, мои "корявые руки". Да и первоначальные проблемы были связан с недопониманием того, что надо было что-то делать с файлами Boost после того, как я их извлек из архива. В Linux я просто сообщаю установщику пакетов о том, что мне нужен boost и после того, как он мне скажет примерно следующее - "все исполнено, мой господин", я начинаю использовать библиотеки Boost. Т.е. в Linux я не перехожу из образа программиста в образ администратора с шаманским бубном для выполнения необходимых танцев и заклинаний.

Итак, я уже говорил, что после безуспешных попыток "откушать супа деревянной вилкой" я обнаружил наличие порта установки Boost через систему CMake. Это было решение проблемы в той ситуации, которую я сам себе создал.

CMake

Итак, я скачал себе Boost для CMake, извлек из скаченного архива исходный материал для компиляции, зашёл в корневую директорию полученной сборки и увидел там заветный файл CMakeLists.txt. Этот файл представляет собой сценарий компиляции для кроссплатформенной системы сборки CMake.

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

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

Так как я собирался делать сборку средствами установленной ранее MinGW, то я, вызывая утилиту cmake, сообщил ей явно о необходимости использования генератора "MinGW Makefiles". В противном случае, cmake начал бы работать согласно своим стандартным настройкам для той операционной системы, которую он обнаружил бы в начале своей работы.

Итак, чтобы подготовить сборку Boost для компиляции средствами MinGW, я выполнил в командной строке, в корневом каталоге Boost, где размещен центральный файл CMakeLists.txt, следующую команду.

> cmake -G "MinGW Makefiles"

В результате я получил Makefile, который должен был исполнить утилитой make, входящей в состав установленного MinGW. В моей системе это получилось выполнением следующей команды относительно директории с лежащим в ней сценарием сборки Makefile.

> mingw32-make

После того, как компиляция была закончена, в текущей директории появилась поддиректория lib с бинарными файлами библиотек Boost.

MinGW

Установить MinGW не сложно. Нужно просто скачать в сети инсталлятор нужной версии и запустить его.
 
Я использовал MinGW входящий в состав комплексного инсталлятора для среды разработки QtCreator под Windows, куда входила сама среда разработки QtCreator, средства Qt SDK и система компиляции MinGW.

Единственное, что может потребоваться, это установка путей на директорию bin установленной системы MinGW.

Так, например, так как я использовал MinGW, который был инкапсулирован в среду разработки и компиляции программ написанных на Qt в среде разработки QtCreator, то информация об установленном MinGW не была объявлена операционной системе в виде путей. Это как раз тот случай, когда это понадобится сделать самому, иначе CMake не будет отрабатывать заданный генератор, и вызвать mingw32-make без указания полного пути не удастся.

В своей системе, мне понадобилось добавить в системные пути путь "C:\Qt\qtcreator-2.3.1\mingw\bin".

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

У новичков могут возникнуть вопросы по использованию скомпилированного Boost.

В зависимости от используемой вами среды разработки и системы сборки проекта это может выглядеть по разному, но суть всегда одинакова. Надо сделать три вещи.
1. Указать путь до включаемых файлов Boost там, где указываются пути до include-файлов.
2. Указать путь до скомпилированных файлов, помещенных в каталог lib, там, где указываются пути до файлов библиотек.
3. Указать имя библиотеки из Boost, которую вы собираетесь использовать, там, где указываются библиотеки для линковки с объектными файлами вашей программы.

вторник, 22 ноября 2011 г.

Boost и CMake для Windows

Случилось так, что занялся я одной кроссплатформенной задачкой на C++. Обычно, я такие задачи делаю на привычной мне связке C++/Qt, но это был случай, когда о Qt надо было забыть.

Очень скоро споткнулся на проблеме управления файловой системой. Меня удивили, что стандартные средства C/C++ не предоставляют в своих библиотеках средств для решения этой проблемы. В результате - серьезная кроссплатформенная проблема. Конечно же, решением, является boost. Хотя это очень не понравилось заказчику.

Надо заметить, что заказчик выполнял проверку системы под Windows, а программирование я выполнял в родном Linux.

Уже давно забыв про кошмары "народной операционной системы" я долго не понимал заказчика. Казалось бы, в чем проблема. Поставишь себе CMake для сборки представленного кода, установишь boost, подправишь каталог размещения boost и будет всем счастье.

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

С установкой CMake особых проблем не было. Была только наша взаимная нелюбовь с операционной системой, которая вылилась в проблемы, которые я сам себе создал.

Проблемы начались с установки библиотеки boost. Скачав архив библиотеки с центрального сайта я столкнулся с тем, что никак не мог выполнить сборку бинарной части библиотеки средствами MinGW. Ни заказчик, ни я не питали нежных чувств к Microsoft Visual Studio, поэтому о MSVC речи даже не шло. Потом, на факультативных началах я бы мог доиграть конфиг CMake под компиляцию нашего проекта и на MSVC, но на том этапе об этом речь не стояла.

К сожалению, в официальной документации к boost я не нашел рабочих решений для сборки библиотеки не средствами MSVC, поэтому я обратился к мантровым архивам Google в поисках необходимых заклинаний. Что-то начало получаться, но до конца дело так и не доходило. На каждом шагу меня ждали неожиданности. И, к сожалению, неожиданности неприятные.

Я уже готов был статься, когда заметил в официальной документации замечание о существовании порта boost для сборки средствами CMake.

https://svn.boost.org/trac/boost/wiki/CMake - вот спасительный ресурс. Его можно легко найти в google по запросу "boost cmake".

Я скачал архив нужной версии, распаковал его и в его главном каталоге, обнаружив центральный файл сборки CMakeLists.txt я выполнил

> cmake -G "MinGW Makefiles"

Родной cmake, пошуршав немного файлами сообщил об успешной генерации Makefile, который я выполнил средствами make из пакета MinGW, который поставил ранее. У меня это была команда

> mingw32-make

которую я исполнил в каталоге созданного Makefile.

Разумеется, для того, чтобы это сработало нужно установить MinGW и сообщить в путях системы о положении его каталога bin. Я использовал MinGW из сборки QtCreator.

Как и положено, сборка библиотеки выполнялась заметное время. Красочный цветной сценарий созданный cmake выполнялся красиво, сообщая о текущем выполненном проценте сборки. После компиляции я увидил заветный каталог lib с бинарными библиотеками boost.