Mxx_ru предоставляет набор готовых средств для поддержки компиляции C/C++ проектов. Эти средства описаны в файле mxx_ru/cpp, который необходимо загружать директивой require:
Для поддержки C/C++ в Mxx_ru выделены важные понятия: toolset и obj_placement.
Mxx_ru предоставляет готовые классы для таких типов целей, как exe-файл, dll-файл (с поддержкой библиотек импорта), lib-файл, композитный проект.
Toolset — это набор инструментов, применяемых для компиляции проекта. Проще говоря, toolset определяет тип, версию и другие особенности конкретного компилятора на конкретной платформе.
Mxx_ru уже адаптирован к ряду компиляторов. В частности поддерживаются компиляторы Borland C++ (версий 5.* на платформе mswin); Visual C++ (версий 7.* и 6.* на платформе mswin); GNU C++ на платформах unix, mswin (через порты mingw и cygwin); с89 для HP NonStop (native-версия и кросс-компилятор из eToolkit для mswin).
Для корректной работы Mxx_ru тип toolset должен быть указан Mxx_ru во время настройки (см. 5.2 на стр. 59).
Toolset представляется объектом класса, производного от Mxx_ru::Cpp::Toolset. Объект-toolset создается Mxx_ru и используется для всего проекта (т.е. для проекта, имя файла которого указано интерпретатору Ruby и для всех его подпроектов). Объект-toolset нельзя заменить. Получить доступ к объекту-toolset можно через метод toolset().
Каждый toolset имеет собственное имя, доступное через метод name класса Mxx_ru::Cpp::Toolset. Это имя может использоваться для настройки проекта на конкретный компилятор. Например:
Определенные на данный момент имена компиляторов перечислены в таблице 5.1.
|
Т.к. toolset адаптирован к конкретному компилятору на конкретной платформе, то в проекте можно использовать toolset для настройки проекта на данную платформу. Для этих целей в toolset есть понятие тегов. Тег — это уникальны текстовый ключ, которому сопоставлено текстовое значение. Теги устанавливаются самими toolset, а так же их можно указать при настройке Mxx_ru.
Для получения значения тега предназначен метод tag(a_name, a_default=nil). Если в toolset установлен тег с таким именем, то возвращается его значение. В противном случае возвращается значение аргумента a_default. Если тег не установлен и a_default==nil, то порождается исключение Mxx_ru::Cpp::Toolset::Unknown_tag_ex.
Ряд тегов считаются обязательными, они должны быть определены для всех toolset на всех платформах (см. таблицу 5.2).
|
Значения тегов host_os и target_os могут различаться в случае использования кросс-компилятора, например, c89 для HP NonStop.
Операционные системы идентифицируются перечисленными в таблице 5.3 именами.
|
На платформе unix должен быть определен обязательный тег unix_port, который определяет конкретный тип unix. Рекомендуется использовать перечисленные в таблице 5.4 значения1.
|
При настройке Mxx_ru могут быть указаны и другие теги, которые необходимы проекту.
Пример работы с тегами:
Mxx_ru позволяет управлять размещением результатов компиляции и линковки C/C++ проекта. Для этих целей используется понятие obj_placement — объекта производного от Mxx_ru::Cpp::Obj_placement класса. Этот объект указывает Mxx_ru куда помещать объектные файлы, куда библиотеки, куда скомпилированные ресурсные файлы и т.д.
Проект может сам указать необходимый ему тип obj_placement. Для этого предназначен метод obj_placement. Например:
Проект может назначить глобальный obj_placement при помощи функции global_obj_placement. В этом случае действие нового obj_placement будет распространяться на все подпроекты.
Попытка установить одновременно и локальный и глобальный obj_placement является ошибкой и приведет к исключению. Рекомендуется устанавливать только глобальный obj_placement в самом верхнем, обычно композитном, проекте.
В состав Mxx_ru входят два штатных класса для obj_placement:
Например, если есть дерево каталогов:
и используется Source_subdir_obj_placement, то после компиляции дерево каталогов примет вид:
Если же используется Runtime_subdir_obj_placement с подкаталогом output в качестве корневого и с режимом компиляции debug, то получится следующе дерево каталогов:
При этом все результаты компиляции, включая exe, dll, lib и т.д., будут помещены в output/debug.
Если obj_placement не задан явно, то используется Source_subdir_obj_placement.
Для C/C++ проектов в Mxx_ru существуют следующие классы целей:
Для C/C++ проекта в проектном файле необходимо создать и передать в функцию Mxx_ru::setup_target объект одного из этих классов.
Все классы имеют конструкторы, которые могут получать блок кода (специфическая особенность языка Ruby). Поэтому для описания проекта не обязательно создавать свой класс, производный от одного из указанных выше классов. Достаточно создать объект одного из существующих классов и передать в его конструктор блок, в котором производится настройка проекта:
Примечание. Для того, чтобы Ruby считал блок в фигурных скобках параметром конструктора необходимо, чтобы открывающая фигурная скобка этого блока располагалась на той же строке, что и закрывающая круглая скобка вызова конструктора. Например:
При построении C/C++ целей выполняются следующие действия:
При очистке C/C++ целей выполняются следующие действия:
Примечание. Класс Composite_target выполняет построение и очистку только подпроектов.
Mxx_ru позволяет строить C/C++ проекты в трех режимах runtime:
Указать режим runtime можно двумя способами:
Описание C/C++ проекта в Mxx_ru состоит в перечислении таких параметров, как имя результирующего файла, имена исходных файлов и необходимых библиотек, необходимых define-символов, необходимых опций компилятора, линкера и т.д. Часть этих параметров могут относиться только к самому проекту. Например, имена исходных файлов. Но часть параметров, т.к. define-символы и опции компилятора, разделяются на три типа:
Пример установления локального define-символа:
Установленный таким образом символ MY_DLL_PRJ будет доступен только при компиляции my_dll/impl.cpp.
Глобальные параметры задаются с помощью функций с именами вида global_<имя>, где <имя> — это имя функции для установки локального параметра того же назначения. Например: global_define, global_include_path, global_compiler_option и т.д.
Пример установки глобального пути поиска заголовочных файлов для композитного проекта:
В этом случае при компиляции всех файлов из проектов engine/prj.rb и interface/prj.rb в качестве пути поиска заголовочных файлов будет использован текущий каталог.
Пример установки глобальной опции компилятора.
В этом случае при компиляции всех файлов всех проектов, которые оказались в одном общем проекте с packer/prj.rb компилятору Visual C++ будет передаваться опция -Zp4.
Upspread-параметры устанавливаются теми же функциями, что и локальные параметры, но с указанием в качестве второго аргумента константы Mxx_ru::Cpp::Target::OPT_UPSPREAD.
Пример установки upspread define-символа и upspread пути поиска заголовочных файлов:
В этом случае при компиляции всех проектов, которые прямо или косвенно используют проект pcre/prj.rb в качестве подпроекта (т.е. указывают его имя, либо имя использующего его проекта в required_prj) в качестве define-символов будут определены PCRE_STATIC и SUPPORT_UTF8, а в качестве пути для поиска заголовочных файлов будет использоваться подкаталог pcre.
Если в командной строке интерпретатору Ruby указан аргумент --mxx-cpp-1, то операции build и clean не распространяются на подпроекты.
Аргумент --mxx-cpp-1 удобно применять, когда выполняются работы над одним подпроектом в составе большого композитного проекта. Запуск построения всего композита может занимать много времени, что расточительно, если вносимые в подпроект изменения не требуют перекомпиляции и перелинковки остальных подпроектов. В этом случае интерпретатору Ruby в командной строке указывается не build.rb, а имя проектного файла и аргумент --mxx-cpp-1:
Аргумент --mxx-cpp-1 может использоваться не только для операции build, но и для операции clean — в этом случае будут удалены только файлы, относящиеся непосредственно к указанного проекту.
Аргумент --mxx-cpp-no-depends-analyzer2 указывает Mxx_ru не проводить поиск C/C++ зависимостей путем анализа исходных и заголовочных файлов. По умолчанию такой анализ производится, что может значительно замедлять процесс компиляции. Иногда (например, при полной перекомпиляции проекта) поиском зависимостей можно пренебречь. В таких случаях интерпретатору Ruby можно указать агрумент --mxx-cpp-no-depends-analyzer:
Для использования возможностей Mxx_ru для работы с C/C++ проектами необходимо определить переменную среды MXX_RU_CPP_TOOLSET. Значение этой переменной среды должно иметь вид:
где <file> – это имя .rb-файла из состава Mxx_ru, который отвечает за создания объекта-toolset. Поддерживаемые toolset перечислены в таблице 5.5. Пары значений tag, value будут установлены в качестве тегов выбранного toolset.
|
Примеры:
Получить объект-toolset из проекта можно обратившись к методу toolset класса Mxx_ru::Cpp::Target. Например:
Примечание. Т.к. toolset является не статическим методом, его можно вызывать только для созданного объекта-цели. Например, как показано выше, в блоке кода, переданного в конструктор в качестве аргумента.
Установить режим runtime в проектном файле можно при помощи метода runtime_mode. Значения режима runtime задаются константами: Mxx_ru::Cpp::RUNTIME_DEBUG, Mxx_ru::Cpp::RUNTIME_DEFAULT, Mxx_ru::Cpp::RUNTIME_RELEASE. По-умолчанию используется режим default.
Режим runtime является глобальным параметром. Он распространяется на все проекты сразу. Попытка двух проектов установить разные значения runtime приведет к порождению исключения.
Получить текущее значение режима runtime можно с помощью метода mxx_runtime_mode.
На платформах, которые поддерживают динамически-загружаемые библиотеки, некоторые toolset позволяют выбрать один из двух типов runtime library (rtl): static (код rtl влинковывается в само приложение) и shared (код rtl находится в отдельной dll, к которой линкуется приложение). Если приложение использует собственные dll и передает C++ объекты созданные в одной dll для уничтожения в другую dll, то такое приложение должно использовать shared вариант rtl (в этом случае все dll имеют общий heap).
Установить тип rtl можно при помощи метода rtl_mode. Тип rtl задается с помощью констант: Mxx_ru::Cpp::RTL_DEFAULT (проекту все равно, какой тип rtl будет использоваться), Mxx_ru::Cpp::RTL_STATIC (требуется использовать статический вариант rtl), Mxx_ru::Cpp::RTL_SHARED (требуется использовать разделяемый вариант rtl). По-умолчанию значение rtl_mode установлено в Mxx_ru::Cpp::RTL_DEFAULT (т.н. default режим).
Тип rtl является глобальным параметром. Он распространяется на все проекты сразу. Попытка двух проектов установить разные значения типа rtl приведет к порождению исключения.
Если тип rtl не был установлен (т.е. остался default-режим), то тип используемой rtl определяется toolset. Как правило, используется rtl, который компилятор подставляет по-умолчанию, если ему не дано никаких явных указаний на этот счет.
Пример:
Получить текущее значение типа rtl можно с помощью метода mxx_rtl_mode.
На некоторых платформах, например, mswin, требуется явно указывать, нуждается ли приложение в поддержке многопоточности или нет. Для установления режима многопоточности в Mxx_ru используется метод threading_mode. Режим многопоточности определяется с помощью констант Mxx_ru::Cpp::THREADING_DEFAULT (проект не предъявляет никаких требований к режиму многопоточности, является однопоточным, но может работать и в многопоточном приложении), Mxx_ru::Cpp::THREADING_MULTI (проект требует поддержки многопоточности), Mxx_ru::Cpp::THREADING_SINGLE (проект расчитан только на однопоточность, не может использоваться совместно с многопоточными проектами). По-умолчанию режим многопоточности выставлен в Mxx_ru::Cpp::THREADING_DEFAULT.
Если режим многопоточности отличается от Mxx_ru::Cpp::THREADING_MULTI и компилятор нуждается в явном указании режима многопоточности, то используется однопоточный режим.
Режим многопоточности является глобальным параметром. Он распространяется на все проекты сразу. Попытка двух проектов установить разные значения режима многопоточности приведет к порождению исключения.
Получить текущее значение режима многопоточности можно с помощью метода mxx_threading_mode.
Примечание. Для некоторых компиляторов, например, Visual C++, имена используемых rtl-библиотек выбираются исходя из типа rtl и режима многопоточности. И не для всех сочетаний возможных типов rtl и режима многопоточности компилятор может иметь библиотеки. Так, Visual C++ не имеет библиотек для однопоточной shared rtl.
В некоторых компиляторах режим RTTI (Run-Time Type Identification) по-умолчанию отключен (например, Visual C++) или может быть отключен (например, GNU C++). Если проект нуждается в RTTI, например, для безопасного использования dynamic_cast , то проект должен явно включить режим RTTI. Напротив, если проект не нуждается в RTTI и для проекта критично время исполнения и/или объем результирующего кода, то режим RTTI может быть отключен.
Для включения/выключения режима RTTI предназначен метод rtti_mode, который принимает в качестве значений константы Mxx_ru::Cpp::RTTI_DEFAULT (проекту все равно, используется ли RTTI или нет), Mxx_ru::Cpp::RTTI_ENABLED (проекту необходим режим RTTI) и Mxx_ru::Cpp::RTTI_DISABLED (проекту необходимо отсутствие RTTI).
Режим RTTI является глобальным параметром. Он распространяется на все проекты сразу. Попытка двух проектов установить разные значения режима RTTI приведет к порождению исключения.
Получить текущее значение режима RTTI можно с помощью метода mxx_rtti_mode.
Примечание. Для проектов, которые описываются с помощью Mxx_ru::Cpp::Composite_target нет результирующего файла, поэтому обращения к методам target_root, target, implib_path игнорируются.
Метод target_root позволяет задать имя каталога, в который должен быть помещен результирущий файл (exe, dll или lib). Размещение результата контролируется с помощью obj_placement (см. 5.1.2 на стр. 51). Если не задан target_root, то результат будет помещен в каталог, который выберет obj_placement. Если же установлен target_root, то результат будет помещен в подкаталог с именем, указанным в target_root, в каталоге, который выберет obj_placement.
Например, пусть obj_placement располагает exe-файлы в текущий каталог. Тогда, если установить в target_root значение out32, то obj_placement поместит результирующий файл в подкаталог out32 текущего каталога.
Важно. Желательно, чтобы метод target_root был вызван перед методом target. Тогда, при обращении к target имя результирующего файла будет автоматически создано с использованием значения target_root. Если же метод target_root вызывается после target, то модифицируется уже назначенное имя результирующего файла. В общем случае, если target_root и target вызываются по одному разу, то результат получается один и тот же. Но, если target_root вызвать два раза (до и после обращения к target, то результат может отличаться от ожидаемого.
Метод target позволяет задать базовое имя для результирующего файла. Реальное имя результата будет создано с учетом особенностей целевой платформы. Например, для dll с базовым именем threads_1.4.0 на платформе mswin будет создано реально имя threads_1.4.0.dll, а на платформе unix — libthreads_1.4.0.so.
Указываемое методу target имя не должно содержать имен каталогов. Если требуется дать указания относительно размещения результирующего файла, то следует использовать метод target_root перед обращением к target.
Метод implib_path, во-первых, предписывает Mxx_ru построить библиотеку импорта для dll (на тех платформах, на которых такое понятие актуально) и, во-вторых, определяет расположение библиотеки импорта.
В настоящий момент implib_path используется только для dll. Если implib_path не вызывается, то библиотека импорта не строится.
Простое указание имени для exe-файла.
Размещение exe-файла в подкаталоге utest.
На платформе mswin результирующая dll помещается в текущий каталог, а библиотека импорта в каталог lib. На платформе unix результирующая библиотека помещается в каталог lib.
Метод screen_mode предназначен для указания типа приложения: консольное или оконное. По-умолчанию, Mxx_ru расчитывает на создание консольного приложения — компилятору и линкеру выставляются специальные опции. Вызвав метод screen_mode можно перевести проект в режим создания оконного (GUI) приложения.
В метод screen_mode необходимо передавать константу Mxx_ru::Cpp::SCREEN_WINDOW для оконного приложения или константу Mxx_ru::Cpp::SCREEN_CONSOLE для консольного приложения.
Параметр screen_mode является локальным для проекта, он не распространяется ни на подчиненные проекты, ни на проекты, которые используют данный проект.
Получить текущий тип приложения можно посредством метода mxx_screen_mode.
По-умолчанию, Mxx_ru считает, что имена исходных файлов задаются относительно того каталога, в котором находится файл-проект. Т.е., если в качестве пседвдонима проекта указано имя interface/prj.rb, то конструкция:
приведет к тому, что Mxx_ru будет искать файлы interface/main.c и interface/io.cpp. Такой подход позволяет легко перечислять в проектном файле содержимое небольшого проекта. Но может быть два случая, когда требуется указать Mxx_ru иное место для поиска исходных файлов:
Оба подхода можно комбинировать. Например, сначала вызвать sources_root с одним параметром, а затем использовать sources_root с двумя параметрами:
Методы c_source и cpp_source предназначены для указания имен C и C++ файлов, входящих в проект. Важно, чтобы файлы, которые содержат только C-код (как правило, имеющие расширение .c) перечислялись с помощью метода c_source, а файлы с C++-кодом (которые на разных платформах могут иметь расширения .C, .cc, .cp, .cpp, .cxx, .c++ и т.д.) перечислялись с помощью метода cpp_source. Дело в том, что на некоторых платформах от типа файла зависит имя используемого компилятра (например, gcc и g++ для GNU C++). На некоторых платформах есть возможность выставлять специальные ключи компилятору для того, чтобы компилятор воспринимал файл как C- или C++-код вне зависимости от расширения файла. Mxx_ru учитывает все эти особенности и активно их использует.
Методы c_source, cpp_source получают два аргумента: первый, обязательный, указывает имя исходного файла. Второй, не обязательный, может содержать опции компилятора, которые будут добавлены к выбранным Mxx_ru опциям при компиляции только этого файла.
Как правило, используется вариант с одним параметром:
Вариант с двумя параметрами может использоваться, если для какого-то файла требуется установить специфические опции компилятора. Например, отдельный символ препроцессора:
в этом случае символ препроцессора LOG_LEVEL будет определен только при компиляции interface.cpp, но не будет определен для engine.cpp.
Применять методы c_source, cpp_source следует с осторожностью, учитывая следующие факторы:
Метод mswin_rc_file позволяет установить имя ресурсного файла на платформе mswin. Если имя ресурсного файла установлено, то при компиляции проекта Mxx_ru запускает компилятор ресурсов для получения .res файла, а при линковке результирующего exe- или dll-файла использует соответствующее средства toolset-а для включения скомпилированных ресурсов в результирующий файл.
Метод mswin_rc_file получает два параметра: первый, обязательный, указывает имя .rc-файла. Второй, необязательный, может содержать имена файлов, от которых зависит ресурсный файл. Например, имена файлов .ico, .bmp и т.д.
При использовании метода mswin_rc_file необходимо помнить, что значение первого параметра (имени .rc-файла) вычисляется с учетом текущего значения sources_root. Но имена зависимостей, которые передаются во втором параметре, должны быть указаны полностью.
Пример:
Примечание. Значение, установленное методом mswin_rc_file учитывается только, если целевой платформой является mswin.
Метод obj_file позволяет указать проекту дополнительные объектные файлы для линковки результирующего файла. По-умолчанию, Mxx_ru формирует имена объектных файлов, получаемых в результате компиляции исходных файлов, и использует их имена при линковании. Если проекту требуется включить в проект уже готовый объектный файл (например, получаемый путем компиляции исходного файла на другом языке программирования), то имя этого объектного файла нужно включить с помощью метода obj_file. Например:
Сам Mxx_ru использует метод obj_file в реализации toolset для сохранения в проекте имен объектных файлов, получаемых в результате компиляции исходных файлов.
Метод lib позволяет указать проекту дополнительные библиотеки для линкования результирующего файла. По-умолчанию, Mxx_ru извлекает имена необходимых для линкования библиотек из подпроектов. Но часто бывает необходимо указать проекту имя конкретной, как правило, платформо-зависимой библиотеки. В этом случае следует использовать метод lib:
Метод lib получает два аргумента. Первый задает имя библиотеки в том виде, в каком это имя следует указать линкеру в командной строке. Т.е., на платформе mswin это должно быть имя с расширением. А на платформах unix — имя без расширения и префикса lib.
Второй аргумент метода lib задает путь, в котором линкеру следует искать библиотеку. По-умолчанию, этот аргумент равен nil, т.е. линкеру не дается никаких указаний. В этом случае линкер будет искать библиотеку в стандартных для конкретной платформы путях. Если же второй параметр отличен он nil, то указанное значение будет передано линкеру в качестве пути для поиска библиотек. Например, если на платформе unix требуется указать линкеру использовать библиотеку /usr/local/share/mysec/lib/libtdes.3.4.a, то следует вызвать метод lib следующим образом:
Метод optimization позволяет установить необходимый режим оптимизации. Mxx_ru учитывает режим оптимизации при формировании опций компилятора в режиме release (см. 5.1.5 на стр. 55). По-умолчанию, используется оптимизации по скорости выполнения сгенерированного кода.
Режим оптимизации задается константами: Mxx_ru::Cpp::OPTIM_SIZE (оптимизация по размеру генерируемого кода) и Mxx_ru::Cpp::OPTIM_SPEED (оптимизации по скорости генерируемого кода).
Режим оптимизации является локальным для проекта параметром. Установленное в проекте значение режима оптимизации не оказывает никакого воздействия ни на подчиненные проекты, ни на проекты, которые используют в качестве подчиненного данный проект.
Как показано в 5.1.6 на стр. 56, для установки локальных, распространяемых и глобальных параметров проекта используются функции вида:
где <параметр> — это название параметра. Функция первого вида используется для установки локального или распространяемого параметра. Значение параметра будет локальным, если параметр a_type будет иметь значение Mxx_ru::Cpp::Target::OPT_LOCAL, либо будет опущен. Если параметр a_type будет иметь значение Mxx_ru::Cpp::Target::OPT_UPSPREAD, то значение параметра будет автоматически распространяться и на все использующие данный проект проекты. Функция с префиксом global_ используется для установки глобального значения параметра.
В данном разделе описываются функции, которые позволяют задавать локальные, распространяемые и глобальные значения параметров проекта.
Функции include_path, global_include_path позволяют установить пути, в которых компилятор будет искать заголовочные файлы.
Функции define, global_define позволяют установить символы препроцессора, которые будут определены при компиляции исходных C- и C++-файлов.
Функции compiler_option, global_compiler_option позволяют задать дополнительные опции компилятора, которые будут использоваться как для компиляции C, так и для компиляции C++-файлов.
Функции c_compiler_option, global_c_compiler_option позволяют задать дополнительные опции компилятора, которые будут использоваться для компиляции только C-файлов.
Функции cpp_compiler_option, global_cpp_compiler_option позволяют задать дополнительные опции компилятора, которые будут использоваться для компиляции только C++-файлов.
Функции linker_option, global_linker_option позволяют задать дополнительные опции линкера, которые будут использоваться для линковки exe- и dll-файлов.
Функции librarian_option, global_librarian_option позволяют задать дополнительные опции библиотекаря, которые будут использоваться для создания статических библиотек.
Функции mswin_rc_include_path, global_mswin_rc_include_path позволяют задать пути поиска заголовочных файлов для компилятора ресурсов.
Функции mswin_rc_define, global_mswin_rc_define позволяют задать символы препроцессора, которые будут определены при компиляции ресурсов.
Функции mswin_rc_option, global_mswin_rc_option позволяют задать дополнительные опции компилятору ресурсов.
Функции mswin_rlink_option, global_mswin_rlink_option позволяют задать дополнительные опции линкеру ресурсов.