Текущая реализация M++ шаблона для C++ позволяет строить EXE-файлы, статические библиотеки, динамические библиотеки для платформ Win32 и Linux (а так же UNIX платформ с компилятором GNU C++). Для платформы Win32 поддерживается построение модулей для оконного и консольного режимов.
Текущая реализация M++ шаблона для C++ поддерживает компиляторы:
Примечание.Текущая версия шаблона является адаптацией шаблона для C++ из предыдущей версии M++ (M++ v.3), которая кроме вышеозначеных компиляторов поддерживала так же Watcom C++ v.11 (OS/2), IBM Visual C++ (OS/2), GNU C++ (Solaris). Т.е. шаблон M++ для C++ работал на всех платформах, которые были доступны для меня в тот момент. Но сейчас мне доступны только платформы Win32 и Linux. Из-за этого список поддерживаемых компиляторов и операционных систем является таким коротким. Но, как показала практика, добавление нового компилятора к шаблону не вызывает трудностей. Да и сам перенос M++ v.4 на другие операционные системы не должен быть слишком трудоемким. Для этого необходимо только иметь доступ к новой платформе и средствам разработки для нее.
Главной задачей шаблона M++ для C++ является компиляция проектов на языке C++. Описание проекта оформляется в виде простой программы на языке M++ обязательным подключением двух стандартных файлов start.4xx и finish.4xx (которые, собственно и являются шаблоном). Имя проектного файла передается как параметр утилите mxxc. Утилита mxxc, интерпритируя программу-проектный файл, осуществляет компиляцию проекта практически так же, как это делает утилита make. Управлять процессом трансляции и сборки проекта можно передавая дополнительные параметры в командной строке утилите mxxc.
Шаблон M++ для C++ определяет набор переменных, часть из которых нужно заполнить в проектном файле для построения проекта. Наиболее важными для проекта переменными являются:
Создаваемые с использованием M++ шаблона проектные файлы (как для простых, так и для сложных проектов), содержат:
Проектный файл для примера "Hello, world" очень наглядно демонстрирует общую структуру проектных файлов:
// Загружаем стандартный файл start.4xx #include <start.4xx> // Присваиваем значения переменным шаблона: // Указываем тип результирующего модуля - // исполнимый модуль... appMode = "exe"; // ...который строится для консольного режима screenMode = "console"; // Указываем имя результирующего модуля target = "hello"; // В проект входит только один C++ файл cppSource += "hello.cpp"; // Загружаем стандартный файл finish.4xx #include <finish.4xx>
Шаблон M++ позволяет строить гораздо более сложные проектные файлы и даже системы проектных файлов. Но общая идея и структура проектных файлов остается точно такой же.
Шаблон M++ для C++ является программой на языке M++, которая преобразует значения переменных шаблона в набор make-правил и заставляет mxxc выполнить эти правила, как это сделала бы утилита make. Так, если в проектном файле указаны имена C++ файлов, то для каждого C++ файла будет создано make-правило компиляции исходного файла в объектный файл. В этом правиле будет содержатьcя имя компилятора, опции компилятора, имена исходного и объектного файлов.
Но при построении make-правил шаблон должен знать семейство компилятора (т.к. имена у компиляторов разные), нужно ли копилировать файлы с отладочной информацией или с оптимизаций и т.д. Для этого в шаблоне содержатся соответствующие фрагменты для всех семейств компиляторов и всех типов трансляции. Задача состоит в том, чтобы утилита mxxc задействовала только те фрагменты кода шаблона, которые необходимы для данного конкретного случая.
Это осуществляется посредством препроцессора языка M++. Все фрагменты шаблона обрамлены конструкциями #if/#else/#elif/#endif. Это позволяет путем определения специальных символьных констант препроцессора выбирать только те фрагменты из общего кода шаблона, которые соотвествуют данному конкретному случаю.
В большинстве случаев константы препроцессора (или т.н. символы препроцессора) указываются как параметры в командной строке mxxc. Но они могут задаваться напрямую в M++ коде посредством директивы #define или в переменной среды MXX4.
Символьные константы препроцессора, распознаваемые шаблоном можно условно разделить на три группы - указывающие семейство компилятора, указывающие режим построения проекта и вспомогательные. Наиболее важными являются:
Примеры:
Компиляция проекта при помощи Visual C++ в отладочном режиме: mxxc -d MSC -f hello.4xx -d DEBUG Компиляция проекта при помощи Visual C++ в режиме оптимизации: mxxc -d MSC -f hello.4xx -d RELEASE Удаление существующей версии проекта и перекомпиляция его при помощи Borland C++ в отладочном режиме: mxxc -d BORLAND -f hello.4xx -d REBUILD Удаление существующей версии проекта: mxxc -f hello.4xx -d CLEANUP Указание семейства компилятора в переменной среды MXX4 (одновременно указывается и путь доступа к стандартным файлам M++). После чего проект компилируется в режиме оптимизации. Платформа Win32: set MXX4=-i c:\bin\mxx-4\lib -d ICC mxxc -f hello.4xx -d DEBUG Указание семейства компилятора в переменной среды MXX4 (одновременно указывается и путь доступа к стандартным файлам M++). После чего проект компилируется в режиме оптимизации. Платформа Linux: export MXX4="-i ~/bin/mxx-4/lib -d GNU" mxxc -f hello.4xx -d DEBUG
По умолчанию шаблон M++ для C++ помещает объектные и .res-файлы в подкаталог с именем "o", который должен быть в каждом каталоге, где есть .cpp- и .rc-файлами. Например, если есть файловая структура:
calculator/ | calculator.4xx | calculator.rc | main.cpp |--parser/ | args.cpp | parser.cpp | parser.hpp |--compute/ | calculate.cpp | calculate.hpp | check.cpp `--output/ show_result.cpp show_result.hpp
А проектный файл calculator.4xx имеет вид:
#include <start.4xx> appMode = "exe"; screenMode = "window"; target = "calculator"; cppSource += "parser\\args.cpp"; cppSource += "parser\\parser.cpp"; cppSource += "compute\\check.cpp"; cppSource += "compute\\calculate.cpp"; cppSource += "output\\show_result.cpp"; cppSource += "main.cpp"; #include <finish.4xx>
То для успешной компиляции нужно в каталогах calculator, calculator/parser, calculator/compute, calculator/output создать подкаталог "o". Тогда, после компиляции файловая структура примет вид:
calculator/ | calculator.4xx | calculator.rc | main.cpp | calculator.exe |--o/ | calculator.res | main.obj |--parser/ | | args.cpp | | parser.cpp | | parser.hpp | `--o/ | args.obj | parser.obj |--compute/ | | calculate.cpp | | calculate.hpp | | check.cpp | `--o/ | calculate.obj | check.obj `--output/ | show_result.cpp | show_result.hpp `--o/ show_result.obj
Посредством значения переменной objPath (см. 4.3.12) можно указать шаблону имя каталога для объектных файлов (можно указать так же, что бы объектные файлы располагались в том же каталоге, что и исходные). Но в любом случае расположение объектного файла будет вычисляться относительно расположения исходного файла. Т.е. нельзя сделать так, что бы все объектные файлы располагались в одном общем каталоге, вне зависимости от того, где находятся исходные файлы.
В M++ реализован анализатор C++ зависимостей. Т.е. все файлы, имена которых указаны в переменной шаблона cppSource просматриваются с целью поиска директив #include. После этого анализатор C++ зависимостей пытается разыскать все файлы, имена которых указывались в директивах #include. После этого все найденые файлы так же просматриваются с целью поиска директив #include. После этого опять ищутся файлы, имена которых указывались в директивах #include и т.д. В результате, для каждого C++ файла строится список найденых заголовочных файлов, которые данный файл загружает (причем не только явно, но и опосредовано, через другие заголовочные файлы). Этот список добавляется в список зависимостей для C++ файла в make-правило для получения объектного файла. Т.о. при изменении заголовочного файла автоматически будет перекомпилированы все C++ файлы, которые так или иначе его загружают.
При этом, поиск файла, имя которого указано в директиве #include, ведется по обычным для C++ правилам. Т.е. если имя указано в кавычках, то файл ищется в каталоге с исходным файлом, а затем в каталогах для стандартных файлов. Если же имя указано в угловых скобках, то файл ищется только в каталогах для стандартных файлов.
Если в проекте используются собственные пути для стандартных файлов (т.е. все имена указываются в угловых скобках), то M++ нужно сообщить пути, в которых располагаются заголовочные файлы проекта. Для этого используется функция cpp_add_include_path. Например:
#include <start.4xx> appMode = "exe"; screenMode = "window"; target = "calculator"; cppSource += "parser\\args.cpp"; cppSource += "parser\\parser.cpp"; cppSource += "compute\\check.cpp"; cppSource += "compute\\calculate.cpp"; cppSource += "output\\show_result.cpp"; cppSource += "main.cpp"; // Указываем пути для заголовочных файлов cpp_add_include_path( [ "parser", "compute", "output" ] ); #include <finish.4xx>
Анализатор C++ зависимостей запускается автоматически и работает достаточно быстро. Например, поиск зависимостей для проектов с несколькими десятками файлов, общим объемом более 50000 строк, на машине класса Pentium-133 занимает 2-3 секунды.
Анализатор C++ зависимостей запускается еще до начала генерации make-правил. В некоторых случаях может оказаться, что не все C++ файлы существуют. Например, если C++ файлы генерируются утилитами yacc или lex. Для того, что бы отключить анализатор зависимостей достаточно определить символ препроцессора NOAUTODEP:
mxxc -d GNU -f calculator.4xx -d NOAUTODEP
Главной задачей при разработке M++4 и шаблона M++ для C++ являлось обеспечение переносимости проектных файлов между различными семействами (и версиями) компиляторов и различными семействами (и версиями) операционных систем. Идея шаблона служит в M++ для обеспечения, в первую очередь, независимости от семейства компилятора, поскольку шаблон скрывает от программиста имена конкретных утилит, аргументы командной строки и т.д.
Независимость проектного файла от операционной системы на сегодняшний момент включает в себя следующие вопросы:
Подобные несоответствия устраняются путем преобразования имени, указанного в переменной target (4.3.3) в соответствии с соглашениями, принятыми на конкретной платформе. (Именно по этому, в target нужно указывать имя без расширения, что бы не получить, например, libmymodule.so.so или mymodule.exe.exe).
Подобные несоответствия устраняются путем преобразования значений переменной libs (4.3.5)
Для устранения проблем с прямыми и обратными косыми шаблон M++ автоматически выполняет пребразование имен файлов так, что бы в них использовались только символы, принятые на данной платформе. Поэтому в проекте M++4 можно указывать как прямые, так и обратные косые в именах файлов - все равно, при передаче имени конкретному инструменту оно будет преобразовано. Подобные преобразования гарантированно выполняются для значений переменных target, cppSource, libs. Удостоверится в том, выполняются ли подобные преобразования с другими переменными (например, libPaths, stdIncludePath или impLibCopies) можно ознакомившись с исходными текстами второй части шаблона M++ (поскольку более поздние версии шаблона могут отличаться от ранних версий).
Так же M++ API содержит функцию str_correct_path_separators, которая выполняет преобразование имен файлов. Вы можете использовать эту функцию, что бы гарантировать правильность имени файла вне зависимости от версии шаблона M++ для C++.
Шаблон для C++ хранится в нескольких файлах, расположенных в подкаталоге lib. Главными файлами являются:
Остальные файлы каталога lib являются служебными, их состав и содержимое может изменяться в будущем. Они относятся к шаблону и не должны использоваться в проектных файлах напрямую.
Обычно не возникает необходимости в модификации каких-либо файлов шаблона. Но, может потребоваться изменение файлов с индивидуальными настройками конкретного компилятора (файлы *defs.4xx). Например, если нужно изменить опции, которые заложены в шаблоне для управления отладочной информацией.
Изменение файлов настройки конкретного компилятора не представляет сложности. Однако для этого желательно ознакомиться с полным списком переменных шаблона (документация может не отражать полного списка переменных, поэтому, при обнаружении переменной с не известным именем нужно будет более подробно ознакомиться с поведением шаблона, для чего может потребоваться просмотр файлов шаблона).
Далее описываются наиболее важные переменные шаблона для C++, их тип, назначение, правила и примеры использования. Переменные описываются в порядке их значимости для проекта.
При описании типа переменной используется синтаксис языка M++.
string appMode;
Допустимые значения: exe, lib, dll.
Определяет тип модуля:
exe - исполнимый модуль (EXE-файл). Результирующий модуль будет
иметь расширение .exe на платформе Win32 и не будет иметь
расширения на UNIX-платформах;
lib - статическая библиотека. Результирующий модуль будет иметь
расширение .lib на платформе Win32 и расширение .a на UNIX-платформах;
dll - динамически-загружаемая библиотека. Результирующий модуль
будет иметь расширение .dll на платформе Win32 и расширение .so на
UNIX-платформах.
Значение этой переменной обязательно должно быть указано.
// Построение статической библиотеки под Win32 и UNIX appMode = "lib"; target = "mylib";
string screenMode;
Допустимые значения: window, console.
Определяет режим работы модуля. Модуль будет построен для оконного режима, если указано значение window. Модуль будет построен для консольного режима, если указано значение console.
Значение этой переменной обязательно должно быть указано.
Примечание. Оконный режим поддерживается только для платформы Win32.
string target;
Содержит результирующее имя модуля без расширения. Расширение не указывается из-за того, что на разных платформах модули одного типа могут иметь различные расширения (например, динамически-загружаемая библиотека на платформах Win32 и OS/2 имеет расширение .DLL, а на UNIX-платформах - .so). В тоже время, имя модуля может содержать имя каталога, в котором нужно создавать модуль.
Значение этой переменной обязательно должно быть указано.
Примечание. Для статических и динамически загружаемых на платформе UNIX должен задаваться префикс lib. Т.е. библиотека mylib будет иметь настоящее имя mylib.lib на платформах Win32 и OS/2, и имя libmylib.a на платформах UNIX. Шаблон M++ берет на себя автоматическое добавление префикса lib к имени результирующего модуля.
// Построение EXE-модуля для консольного режима appMode = "exe"; screenMode = "console"; target = "my_module"; // Построение EXE-модуля для оконного режима. // Размещение модуля определяется типом компиляции appMode = "exe"; screenMode = "window"; #if defined( RELEASE ) target = "release\\my_module"; #else target = "debug\\my_module"; #endif // Построение статической библиотеки. // Для платформы Win32 будет создана библиотека // lib\my_module.lib. // Для UNIX-платформ будет создана библиотека // lib/libmy_module.a appMode = "lib"; screenMode = "console"; target = "lib/my_module"; // Построение динамически-загружаемой библиотеки. // Для платформы Win32 будет создана библиотека // bin\my_module.dll. // Для UNIX-платформ будет создана библиотека // bin/libmy_module.so appMode = "dll"; screenMode = "console"; target = "bin/my_module";
string[] cppSource;
Содержит имена исходных C++ файлов модуля. Каждый файл, перечисленный в cppSource будет скомпилирован, а получившийся объектный файл передан линкеру. Файлы будут компилироватья и передаваться линкеру в том порядке, в котором они были занесены в cppSource.
appMode = "exe"; screenMode = "console"; target = "cmd_line_calculator"; cppSource += "main.cpp"; cppSource += "cmd_line_args.cpp"; cppSource += "parser.cpp"; cppSource += "compute.cpp";
string[] libs; string[] sysLibs; string[] libPaths;
Переменные libs, sysLibs содержат имена библиотек, которые должны быть указаны линкеру для сборки результирующего модуля. Переменная libPaths содержит пути поиска библиотек.
Различия между libs и sysLibs:
Главным назначением переменной libs является перечисление библиотек, которые строятся в рамках той же разработки, что и текущий проектный файл. Например, если некоторая задача требует создания нескольких общих статических библиотек, которые используются остальными исполнительными модулями, то в проектных файлах для исполнительных модулей имена общих статических библиотек можно указывать в переменной libs.
Переменная sysLibs, напротив, предназначена для указания имен библиотек, не являющихся частью той же разработки, что и текущий проектный файл. Например, для указания стандартных библиотек компилятора или операционной системы.
На платформе Win32 шаблон поступает со значениями переменной libs следующим образом:
На UNIX-платформах шаблон поступает со значениями переменной libs следующим образом:
Т.е., если в проекте было задано:
libs += "module1/lib/module1"; libs += "common_lib/common_lib1"; libs += "module2/lib/module2"; libs += "common_lib/common_lib2";
То для платформы Win32 список библиотек будет иметь вид: module1\lib\module1.lib, common_lib\common_lib1.lib, module2\lib\module2.lib, common_lib\common_lib2.lib. Именно эти имена и в таком порядке будут переданы линкеру в качестве имен библиотек.
То для UNIX-платформ список библиотек будет иметь вид: module1, common_lib1, module2, common_lib2. А список путей для поиска библиотек будет иметь вид: module1, common_lib, module2.
Примечание. Над значениями переменной sysLibs не выполняется никаких преобразований. Это происходит из-за предположения, что в sysLibs указываются имена, зависящие от семейства (и версии) компилятора и/или семейства (и версии) операционной системы. Для обработки зависимостей от компилятора и операционной системы предназначен препроцессор M++.
// Проектный файл для создания общей библиотеки #include <start.4xx> appMode = "lib"; screenMode = "console"; target = "lib/common_lib"; cppSource += "common_lib1.cpp"; cppSource += "common_lib2.cpp"; ... #include <finish.4xx> // Проектный файл для создания исполнимого модуля, // использующего общую библиотеку #include <start.4xx> appMode = "exe"; screenMode = "console"; target = "my_module"; ... // Подключаем общую библиотеку libs += "lib/common_lib"; // Подключаем стандартные библиотеки #if defined( UNIX ) sysLibs += "socket"; sysLibs += "pthread"; #elif defined( NT ) sysLibs += "wsock32.lib"; #endif #include <finish.4xx>
string[] defines;
Содержит названия символов препроцессора, которые должны быть определены в командной строке транслятора. Порядок занесения значений в эту переменную определяет порядок передачи указания символов препроцессора в командной строке компилятора.
Примечание. Значения, находящиеся в данной переменной просто подставляются в соответствующие параметры транслятора командной строки. Поэтому, значениями переменной defines могут быть символы препроцессора со своими значениями (если это поддерживает C++ компилятор).
appMode = "dll"; screenMode = "window"; target = "my_dll"; defines += "MY_DLL"; defines += "MY_API_VER=0x0103"; #if defined( MSC ) // Определение символов, необходимых для Visual C++ defines += [ "_WINDOWS", "_MBCS" ]; #if defined( DEBUG ) defines += [ "_DEBUG" ]; #endif #endif
Примечание. Символ NDEBUG для release-компиляций заносится в переменную defines автоматически во второй части шаблона M++ для C++.
string[] stdIncludePath;
Содержит пути, которые должны быть переданы компилятору в качестве путей стандартных заголовочных файлов. (Т.е. в них должен осуществляться поиск файлов, указанных в директивах #include в угловых скобках). Порядок занесения значений в эту переменную определяет порядок указания путей в командной строке компилятора.
// Для компиляции M++ v.4 компилятору в качестве // каталога со стандартными заголовочными файлами // необходимо указать подкаталог h. // В шаблоне M++ для C++ это можно сделать так: appMode = "exe"; screenMode = "console"; target = "mxxc"; stdIncludePath += "h";
string[] dependOnPrjs;
Содержит имена проектных файлов M++, от которых зависит данный проектный файл. Перед построением make-правил для данного проектного файла для каждого из перечисленных в dependOnPrjs будет запущена утилита mxxc. При вызове mxxc будут передаваться те же самые аргументы командной строки, которые были указаны при запуске данного проектного файла.
Эта переменная предназначена для случаев, когда в какой-либо задаче требуется реализовать несколько модулей, зависящих друг от друга. Например, пусть есть статическая библиотека slib-1, которая используется для получения динамических библиотек dlib-1, dlib-2. Которые, в свою очередь, используются для получения исполнительных модулей exe-1, exe-2, exe-3.В таких ситуациях при внесении изменений в библиотеку slib-1 нужно выполнить, как минимум, компиляцию модулей dlib-1, dlib-2. Часто случается, что изменения нужно вносить при отладке более высокоуровневых модулей, например, exe-1. Без использования переменной dependOnPrjs потребовалось бы запустить компиляцию модуля slib-1, затем dlib-1, затем dlib-2, и только затем - exe-1. Но в проектном файле для модуля exe-1 можно указать, что он зависит от проектов dlib-1 и dlib-2. В проектах dlib-1 и dlib-2 можно указать, что они зависят от проекта slib-1. Тогда при запуске mxxc с проектом exe-1 будет запущен mxxc для проекта dlib-1. Из него будет запущен mxxc для проекта slib-1. Проект slib-1 будет перекомпилирован. После этого будет перекомпилирован проект dlib-1. Затем будет запущен mxxc для проекта dlib-2, а из него - для slib-1. Но slib-1 уже был перекомпилирован. Поэтому, перекомпилируется только dlib-2. Затем произойдет возврат в проект exe-1 и exe-1 так же будет перекомпилирован.
Примечание. Очевидно, что в больших разработках с большим числом подчиненных проектных файлов утилита mxxc для одного и того же проектного файла может запускаться несколько раз. Это нужно учитывать, если проектный файл кроме заполнения шаблона и/или формирования make-правил занимается еще какой-либо работой (например, копированием файлов или отсылкой уведомлений по электронной почте).
Проектный файл для модуля slib-1 (slib-1.4xx) ... appMode = "lib"; target = "slib-1"; ... Проектный файл для модуля dlib-1 (dlib-1.4xx) ... appMode = "dll"; target = "dlib-1"; impLib = "dlib-1.lib"; ... // Использует библиотеку slib-1 libs += "slib-1"; // И зависит от проекта slib-1 dependOnPrjs += "slib-1.4xx"; ... Проектный файл для модуля dlib-2 (dlib-2.4xx) ... appMode = "dll"; target = "dlib-2"; impLib = "dlib-2.lib"; ... // Использует библиотеку slib-1 libs += "slib-1"; // И зависит от проекта slib-1 dependOnPrjs += "slib-1.4xx"; ... Проектный файл для модуля exe-1 (exe-1.4xx) ... appMode = "exe"; target = "exe-1"; ... // Использует библиотеку импорта dlib-1 libs += "dlib-1"; // И зависит от проекта dlib-1 dependOnPrjs += "dlib-1.4xx"; // Использует библиотеку импорта dlib-2 libs += "dlib-2"; // И зависит от проекта dlib-2 dependOnPrjs += "dlib-2.4xx"; ...
string resourceSource;
Содержит имя файла-ресурсов.
Примечание. Имеет значение только на платформе Win32.
string[] resFileDepends;
Содержит имена файлов-зависимостей для файла-ресурсов. Значения данной переменной будут использованы как зависимости при формировании make-правила для компиляции ресурсов.
Примечание. Имеет значение только на платформе Win32.
appMode = "exe"; screenMode = "window"; target = "my_app"; resourceSource = "my_app.rc"; resFileDepends += "res/my_app.ico"; resFileDepends += "res/my_app.rc2"; resFileDepends += "res/Toolbar.bmp"; resFileDepends += "res/tree_state_1.bmp"; resFileDepends += "res/tree_state_2.bmp"; resFileDepends += "res/tree_fol.bmp";
string pchHeaderFile; string pchCppFile;
Содержат значения для управления прекомпилированными заголовочными файлами. Если в проекте используются прекомпилированные заголовочные файлы, то должны быть указаны значения для обеих переменных.
Подробное описание см. "ПРИЛОЖЕНИЕ A. Прекомпилированные заголовочные файлы"
pchHeaderFile = "stdafx.h"; pchCppFile = "stdafx.cpp";
string objPath;
Значение по умолчанию: o.
Содержит подкаталога, в который необходимо помещать объектные файлы (а так же .res-файл для файла-ресурсов).
Шаблон M++ для C++ построен таким образом, чтобы помещать объектные файлы не в один общий каталог, а в специальные подкаталоги каждого каталога, в котором располагаются исходные тексты. Т.е., если есть файловая структура:
dev/ | smodule.4xx | dmodule.4xx | emodule.4xx `--src/ |--smodule/ | ver.cpp | lib_code.cpp |--dmodule/ | ver.cpp | dll_code.cpp | module.rc `--emodule/ ver.cpp main.cpp module.rc
То для применения шаблона M++ необходимо в каждом каталоге с .cpp и .rc файлами создать подкаталог "o/". В него и будут помещаться объектные и .res-файлы. Т.е. файловая структура должна иметь вид (в ней так же показаны результаты компиляции на платформе Win32):
dev/ | smodule.4xx | dmodule.4xx | emodule.4xx | smodule.lib | dmodule.dll | emodule.exe `--src/ |--smodule/ | | ver.cpp | | lib_code.cpp | `--o/ | ver.obj | lib_code.obj |--dmodule/ | | ver.cpp | | dll_code.cpp | | module.rc | `--o/ | ver.obj | dll_code.obj | module.res `--emodule/ | ver.cpp | main.cpp | module.rc `--o/ ver.obj main.obj module.res
Вторая часть шаблона проверяет, назначено ли значение переменной objPath. Если не назначено, то используется значение по умолчанию. В противном случае используется значение переменной objPath. Поэтому в проектном файле есть возможность назначить подходящее размещение объектных файлов.
Например, так можно указать, что объектные файлы должны располагаться в том же каталоге, что и исходные:
objPath = "."; ... #include <finish.4xx>
Так можно указать, что объектные файлы для debug- и release-компиляции размещались в разных подкаталогах:
#if defined( RELEASE ) objPath = "o/r"; #else objPath = "o/d"; #endif ... #include <finish.4xx>
Последний случай применим, например, к следующей файловой структуре:
dev/ `--src/ |--smodule/ | `--o/ | |--r/ | `--d/ |--dmodule/ | `--o/ | |--r/ | `--d/ `--emodule/ `--o/ |--r/ `--d/
string impLib;
Содержит имя библиотеки импорта, которая должна быть создана после удачной сборки динамически-загружаемой библиотеки. Значение данной переменной учитывается только, если appMode содержит "dll" и только на платформах, на которых библиотека импорта имеет значение (Win32).
Присваивание значения данной переменной предписывает шаблону построить библиотеку импорта с указанным именем. Если данная переменная не будет иметь значения, то шаблон M++ не будет пытаться строить библиотеку импорта. Но, например, компилятор Visual C++ все равно построит библиотеку импорта, дав ей имя DLL-файла и поместив ее в тот же каталог, что и DLL-файл.
Используя переменную impLib можно указать имя библиотеки импорта и ее расположение.
#include <start.4xx> appMode = "dll"; screenMode = "console"; target = "dll\\module"; impLib = "lib\\module.lib"; cppSource += "module.cpp"; #include <finish.4xx>
string[] targetCopies;
Содержит имена, под которыми результирующий модуль должен быть скопирован после успешного построения.
Используя данную переменную можно предписать шаблону скопировать результирующий модуль в один или несколько каталогов. Это может быть полезно, например, если результирующий модуль используется в одном или нескольких других проектах. Например, допустим, что строится динамически-загружаемая библиотека, самая последняя версия которой должна всегда быть доступна для тестирования и для еще одного проекта.
#include <start.4xx> appMode = "dll"; screenMode = "console"; target = "dll\\module"; impLib = "lib\\module.lib"; // Копируем DLL в каталог с тестами... targetCopies += "..\\tests\\bin\\module.dll"; // ... и в другой проект targetCopies += "..\\exe_module\\module.dll"; cppSource += "module.cpp"; #include <finish.4xx>
Так же переменная targetCopies может использоваться, для того, чтобы сделать доступным результирующий модуль общедоступным. Например, в следующем примере результирующий модуль помещается в каталог, указанный в переменной среды PATH:
#include <start.4xx> appMode = "exe"; screenMode = "console"; target = "my_calculator"; #if defined( UNIX ) targetCopies += "~/bin/my_calculator"; #else targetCopies += "c:\\bin\\my_calculator.exe"; #endif #include <finish.4xx>
Примечание. Имена, указанные в targetCopies не контролируются. Это означает, что необходимо самостоятельно контролировать расширение имени модуля для конкретной платформы.
Примечание. В текущей версии шаблона для копирования результирующего модуля используется команда копированния операционной системы (copy для Win32, cp для UNIX). Это означает, что:
string[] impLibCopies;
Содержит имена, под которыми библиотека импорта должна быть скопирована после успешного построения результирующей динамически-загружаемой библиотеки и библиотеки импорта. Значение данной переменной учитывается только, если appMode содержит "dll" и только на платформах, на которых библиотека импорта имеет значение (Win32).
Используя данную переменную можно предписать шаблону скопировать библиотеку в один или несколько каталогов. Или создать копию библиотеки импорта под другим именем. Это может быть полезно, например, если результирующий модуль используется в одном или нескольких других проектах. Например, допустим, что строится динамически-загружаемая библиотека, самая последняя версия которой должна всегда быть доступна для тестирования и для еще одного проекта. Или, если имеется несколько версий DLL, а линковаться нужно только к последней версии.
#include <start.4xx> appMode = "dll"; screenMode = "console"; target = "dll\\my_lib-4-15-3"; impLib = "lib\\my_lib-4-15-3.lib"; // Копируем DLL в каталог с тестами... targetCopies += "..\\tests\\bin\\my_lib-4-15-3.dll"; // ... и в другой проект targetCopies += "..\\exe_module\\my_lib-4-15-3.dll"; // Копируем библиотеку для тестов и в другой проект. // Но там не нужно знать, что это за версия библиотеки. impLibCopies += "..\\test\\lib\\my_lib.lib"; impLibCopies += "..\\exe_module\\lib\\my_lib.lib"; // В текущем проекте так же может оказаться полезным // иметь библиотеку импорта без знания номера версии impLibCopies += "lib\\my_lib.lib"; cppSource += "my_lib.cpp"; #include <finish.4xx>
Примечание. Имена, указанные в impLibCopies не контролируются. Это означает, что необходимо самостоятельно контролировать расширение имени модуля для конкретной платформы.
Примечание. В текущей версии шаблона для копирования результирующего модуля используется команда копированния операционной системы (copy для Win32, cp для UNIX). Это означает, что:
string[] objs;
Содержит имена объектных файлов, которые должны быть переданы линкеру для сборки результирующего модуля.
Данная переменная предназначена для случаев, когда некий код предоставляется вам не в виде исходного текста или библиотеки, а в виде объектных файлов. А вам необходимо использовать их для сборки своих модулей.
4.3.17.
cppOptions, linkOptions, libOptions, resCompOptions, resBuildOptions,
impLibOptions
string[] cppOptions; string[] linkOptions; string[] libOptions; string[] resCompOptions; string[] resBuildOptions; string[] impLibOptions;
Данные переменные содержат параметры командной строки для различных инструментов, используемых для компиляции и сборки модуля:
Назначая значения данным переменным можно управлять процессом компиляции и линковки проекта. В качестве примера их использования лучше всего обратиться к файлам *defs.4xx, являющихся частью шаблона. Просмотр файлов *defs.4xx может оказаться необходимым для того, чтобы узнать, какие параметры указаны для построения проекта по умолчанию - возможно, вам не понадобиться ничего изменять. Или наоборот - вы захотите использовать собственные установки.
Шаблон M++ для C++ использует ряд символов препроцессора, которые оказывают влияние на выполняемые шаблоном действия. Эти символы препроцессора можно передавать утилите mxxc в командной строке, определять в переменно среды MXX4 или непосредственно в тексте проектного файла посредством директивы #define.
Далее описываются наиболее важные символы препроцессора, которые отслеживаются в шаблоне M++ для C++.
Указывает использовать компилятор семейства Borland C++. Должен определяться пользователем. Не должен использоваться совместно с другими символами препроцессора, указывающими семейство компилятора (GNU, IBM, ICC, MSC, WATCOM и т.д.).
Если вы используете несколько компиляторов и операционная система позволяет создавать несколько командных процессоров с уникальным наборо переменных среды, то это символ лучше всего определить в переменной среды MXX4.
Указывает удалить результирующий модуль и все существующие объектные и .res-файлы проекта, после чего обработка проектного файла завершаетcя. Должен определяться пользователем.
Указывает осуществлять компиляцию и линковку с включенной отладочной информацией (т.н. debug-build). Должен определяться пользователем. Не должен использоваться совместно с символом RELEASE.
Примечание.В стандартной реализации шаблона M++ для C++, если не определены символы DEBUG и RELEASE, то при компиляции и линковке не задейстованы ни ключи для создания отладочной информации, ни для оптимизации кода.
Указывает использовать компилятор семейства GNU C++. Должен определяться пользователем. Не должен использоваться совместно с другими символами препроцессора, указывающими семейство компилятора (BORLAND, IBM, ICC, MSC, WATCOM и т.д.).
Если вы используете несколько компиляторов и операционная система позволяет создавать несколько командных процессоров с уникальным наборо переменных среды, то это символ лучше всего определить в переменной среды MXX4.
Указывает использовать компилятор семейства IBM Visual Age C++. В текущей версии M++ данное семейство компиляторов не поддерживается.
Указывает использовать компилятор семейства Intel C++. Должен определяться пользователем. Не должен использоваться совместно с другими символами препроцессора, указывающими семейство компилятора (BORLAND, GNU, IBM, MSC, WATCOM и т.д.).
Если вы используете несколько компиляторов и операционная система позволяет создавать несколько командных процессоров с уникальным наборо переменных среды, то это символ лучше всего определить в переменной среды MXX4.
Указывает использовать компилятор семейства MinGW C++. Должен определяться пользователем. Не должен использоваться совместно с другими символами препроцессора, указывающими семейство компилятора (BORALND, GNU, IBM, ICC, WATCOM и т.д.).
Если вы используете несколько компиляторов и операционная система позволяет создавать несколько командных процессоров с уникальным наборо переменных среды, то это символ лучше всего определить в переменной среды MXX4.
Указывает использовать компилятор семейства Visual C++. Должен определяться пользователем. Не должен использоваться совместно с другими символами препроцессора, указывающими семейство компилятора (BORALND, GNU, IBM, ICC, WATCOM и т.д.).
Если вы используете несколько компиляторов и операционная система позволяет создавать несколько командных процессоров с уникальным наборо переменных среды, то это символ лучше всего определить в переменной среды MXX4.
Указывает отобразить на стандартный поток вывода сгенерированный набор make-правил. Должен определяться пользователем и использоваться совместно с символом NOBUILD.
Если определен этот символ, то шаблон отображает на стандартный поток вывода некоторую дополнительную информацию о шаблоне и выполняемым им действиям. Например, версия шаблона, моменты запуска и останова анализатора C++ зависимостей. Должен определяться пользователем.
Указывает шаблону не запускать анализатор C++ зависимостей. Должен определяться пользователем.
Указывает шаблону построить make-правила, но не запускать их исполнение. Может использоваться для отладки проектных файлов. Должен определяться пользователем.
Указывает шаблону не удалять временные файлы, которые строятся при генерации make-правил. Например, параметры для линкеров записывается во временный response-файл, имя которого передается как параметр линкеру. Если символ NODELTEMPFILES не определен, то шаблон перед завершением своей работы удаляет все созданные им временные файлы. Если символ NODELTEMPFILES определен, то временные файлы не удаляются и могут использоваться, например, для поиска причин ошибок линковки.
Должен определяться пользователем.
Указывает, что текущей платформой является Win32. Определяется автоматически.
Указывает, что текущей платформой является OS/2. Определяется автоматически. Текущая версия M++ не перенесена на OS/2.
Указывает удалить результирующий модуль и все существующие объектные и .res-файлы проекта, после чего запускается новая компиляция и сборка проекта. Должен определяться пользователем.
Указывает осуществлять компиляцию и линковку с максимальной оптимизацией (т.н. release-build). Должен определяться пользователем. Не должен использоваться совместно с символом DEBUG.
Примечание.В стандартной реализации шаблона M++ для C++, если не определены символы DEBUG и RELEASE, то при компиляции и линковке не задейстованы ни ключи для создания отладочной информации, ни для оптимизации кода.
Указывает, что текущей платформой является UNIX. Определяется автоматически.
Указывает использовать компилятор семейства Watcom C++. Должен определяться пользователем. Не должен использоваться совместно с другими символами препроцессора, указывающими семейство компилятора (BORLAND, GNU, IBM, ICC, MSC и т.д.).
Если вы используете несколько компиляторов и операционная система позволяет создавать несколько командных процессоров с уникальным наборо переменных среды, то это символ лучше всего определить в переменной среды MXX4.
На мой взгляд, одним из важнейших факторов удобства работы с каким-либо проектом является организация файловой структуры для исходных файлов проекта. Хотя понятие удобства является субъективным - как именование классов и переменных в программе, так и именование и расположение исходных файлов определяется личными пристрастиями каждого разработчика. Но шаблон M++ для C++ предъявляет некоторые требования к расположению объектных файлов. Поэтому имеет смысл не примерах объяснить откуда появились эти требования.
Довольно распространненой практикой сейчас является помещение одного проекта (модуля) в один каталог с небольшим количеством подкаталогов. Например, при использовании среды разработки Visual C++ разработчику предлагается приблизительно следующая структура каталогов:
module/ | *.hpp, *.cpp, *.rc, проектные файлы и т.д. |--Release/ | *.obj, *.res, *.dll, *.exe и другие результаты компиляции | в режиме оптимизации |--Debug/ | *.obj, *.res, *.dll, *.exe и другие результаты компиляции | в отладочном режиме `--Res/ *.rc1, *.bmp, *.ico и другие ресурсные файлы
Если разрабатывается проект, в котором содержится несколько модулей, то дерево каталогов может выглядеть приблизительно следующим образом:
project/ |--module-1/ | | *.hpp, *.cpp, *.rc, проектные файлы и т.д. | |--Release/ | | *.obj, *.res, *.dll, *.exe и другие результаты компиляции | | в режиме оптимизации | |--Debug/ | | *.obj, *.res, *.dll, *.exe и другие результаты компиляции | | в отладочном режиме | `--Res/ | *.rc1, *.bmp, *.ico и другие ресурсные файлы |--module-2/ | | *.hpp, *.cpp, *.rc, проектные файлы и т.д. | |--Release/ | | *.obj, *.res, *.dll, *.exe и другие результаты компиляции | | в режиме оптимизации | |--Debug/ | | *.obj, *.res, *.dll, *.exe и другие результаты компиляции | | в отладочном режиме | `--Res/ | *.rc1, *.bmp, *.ico и другие ресурсные файлы | | . . . | `--module-N/ | *.hpp, *.cpp, *.rc, проектные файлы и т.д. |--Release/ | *.obj, *.res, *.dll, *.exe и другие результаты компиляции | в режиме оптимизации |--Debug/ | *.obj, *.res, *.dll, *.exe и другие результаты компиляции | в отладочном режиме `--Res/ *.rc1, *.bmp, *.ico и другие ресурсные файлы
Для одного модуля подобный подход к организации структуры каталогов еще может считаться удобным. Хотя, если в модуль входят несколько десятков .cpp-файлов, для каждого из которых нужен .hpp-файл, то разбираться в этом множестве заголовочных и исходных файлов мне не удобно.
Если же проект состоит из нескольких модулей, для каждого из которых создается подобная структура, то возникают трудности в использовании файлов одного подпроекта в другом подпроекте. Это относится как к заголовочным, так и к .lib-, .dll- и .exe-файлам. И если для .hpp- и .lib-файлов можно определять пути поиска у компилятора и линкера, то для .dll- и .exe-файлов приходится что-то придумывать. Например, как быть если module-2, module-3 - это DLL-библиотеки, которые используются EXE-файлами модулей module-5, module-7, которые, используются EXE-файлом модуля module-N?
Иногда проекты оформляются так, чтобы был общий каталог для исходных текстов, который разбит на подкаталоги для отдельных модулей:
project/ | *.exe, *.dll, *.mak `--src/ |--module-1/ | *.hpp, *.cpp, *.obj, *.lib, *.rc, *.res, *.mak |--module-2/ | *.hpp, *.cpp, *.obj, *.lib, *.rc, *.res, *.mak | ... `--module-N/ *.hpp, *.cpp, *.obj, *.lib, *.rc, *.res, *.mak
Эта структура, на мой взгляд гораздо более удобна, но ее недостатком является то, что в одном каталоге располагаются и заголовочные, и исходные, и объектные файлы. Там же часто оказываются библиотеки и проектные файлы.
В свое время я опробовал различные способы организации файловой структуры для проектов. В результате я считаю наиболее удобными следующие способы организации файлов проекта:
project/ | *.exe (*.dll, *.lib), *.4xx, архивные файлы `--module/ | *.cpp |--h/ | *.hpp, *.h `--o/ *.obj
Если в проекте необходимы ресурсы, то .rc-файл располагается там же, где и .cpp-файлы. Для других ресурсов, например *.ico, *.bmp и т.д., можно создать отдельные подкаталоги (если таких файлов много).
project/ | архивные файлы `--dev/ | *.exe, *.dll |--4xx/ | *.4xx - для компиляции и настройки всего проекта. |--lib/ | *.lib |--module-1/ | | *.cpp, *.rc, *.4xx | |--h/ | | *.hpp, *.h | `--o/ | *.obj, *.res |--module-2/ | | *.cpp, *.rc, *.4xx | |--h/ | | *.hpp, *.h | `--o/ | *.obj, *.res | ... `--module-N/ | *.cpp, *.rc, *.4xx |--h/ | *.hpp, *.h `--o/ *.obj, *.res
Эту структуру можно дополнять, например, так:
project/ `--dev/ |--debug/ | *.exe, *.dll |--release/ | *.exe, *.dll |--4xx/ |--lib/ | |--debug/ | | *.lib | `--release/ | *.lib |--module-1/ | |--h/ | `--o/ | |--debug/ | `--release/ |--module-2/ | |--h/ | `--o/ | |--debug/ | `--release/ | ... `--module-N/ |--h/ `--o/ |--debug/ `--release/
Какие бы широкие возможности не предоставлял шаблон, всегда может потребоваться внесение собственных правил в построение проекта. Поскольку шаблон M++ для C++ является всего лишь программой для построения make-правил, то вполне допустимо внесение кода по генерации собственных make-правил непосредственно в проектный файл.
В следующем примере показывается, как в проектном файле описываются правила для создания файлов parser.cpp и parser.hpp утилитой fpg, если они зависят от файла parser.fpg:
#include <start.4xx> appMode = "exe"; screenMode = "console"; target = "jdgen"; cppSource += "fpgparse.cpp"; cppSource += "javadoc.cpp"; cppSource += "lex_buffer.cpp"; cppSource += "gen_info.cpp"; cppSource += "parser_data.cpp"; cppSource += "args.cpp"; cppSource += "main.cpp"; cppSource += "parser.cpp"; // Описываем собственное make-правило make_define_node( // что должно быть построено [ "parser.cpp", "parser.hpp" ], // от чего зависит [ "parser.fpg" ], // как должно быть построено [ "fpg -g parser.fpg -cpp parser.hpp parser.cpp" ] ); #if !defined( NOAUTODEP ) cpp_add_include_path( "." ); #endif #include <finish.4xx>
Для того, что бы включение собственных make-правил не вызывало сложностей необходимо:
Ниже приведены проектные файлы из реальной разработки по созданию библиотеки стандартных алгоритмов DES и DAC. Библиотека secalg состоит из собственно библиотеки (secalg) и двух тестовых программ (t1 - для тестирования DES, t1_dac_des - для тестирования DAC). Файловая структура разработки имеет вид:
secalg/ `--secalg/ |--des/ | |--h/ | | dac_des.hpp | | des.cpp | `--o/ | dac_des.cpp | des.cpp |--h/ | bitv.hpp | bitv_iostream.hpp | defs.hpp | permutation.hpp | static_bitv.hpp |--o/ `--t1/ `--o/ t1.4xx t1.cpp t1_dac_des.4xx t1_dac_des.cpp bitv.cpp bitv_iostream.cpp locals.4xx permutation.cpp secalg.4xx static_bitv.cpp default.4xx secalg.rar
Проектные файлы:
secalg/secalg.4xx #include <start.4xx> appMode = "lib"; screenMode = "console"; target = "secalg"; cppSource += "secalg/bitv.cpp"; cppSource += "secalg/bitv_iostream.cpp"; cppSource += "secalg/static_bitv.cpp"; cppSource += "secalg/permutation.cpp"; cppSource += "secalg/des/des.cpp"; cppSource += "secalg/des/dac_des.cpp"; #include "secalg/locals.4xx" #include <finish.4xx>
secalg/locals.4xx cpp_add_include_path( stdIncludePath += [ "." ] );
secalg/t1/t1.4xx #include <start.4xx> appMode = "exe"; screenMode = "console"; target = "t1"; cppSource += "secalg/t1/t1.cpp"; libs += "secalg"; dependOnPrjs += "secalg/secalg.4xx"; #include "secalg/locals.4xx" #include <finish.4xx>
default.4xx //#include "secalg/secalg.4xx" //#include "secalg/t1/t1.4xx" #include "secalg/t1/t1_dac_des.4xx"
Содержимое файла default.4xx нужно пояснить:
#include "secalg/secalg.4xx"Т.е. строилась только библиотека secalg.lib (libsecalg.a для UNIX).
//#include "secalg/secalg.4xx" #include "secalg/t1/t1.4xx"Т.е. не нужно компилировать secalg.lib, т.к. это делается из t1.4xx, но нужно запускать компиляцию t1.4xx.
//#include "secalg/secalg.4xx" //#include "secalg/t1/t1.4xx" #include "secalg/t1/t1_dac_des.4xx"Т.е. не нужно компилировать secalg.lib, т.к. это делается из t1_dac_des.4xx. Не нужно компилировать t1.4xx, т.к. сейчас отлаживается другой модуль. Нужно запускать компиляцию t1_dac_des.4xx.
M++4 и шаблон M++ для C++ могут использоваться и для создания приложений на основе MFC и Microsoft VisualStudio. Например, приводимые ниже проектные файлы взяты из реальной разработки, выполненой с использованием MFC, VisualStudio v.6 и M++4.
Для платформы Win32 среда разработки VisualStudio стала наиболее распространненой средой программирования на C++. Нужно признать, что это произошло не просто так - VisualStudio действительно является удобной средой. Но лично мне не нравится выбранный в VisualStudio подход организации проектов на основе одного Workspace и нескольких проектов в нем. Причем активным проектом может быть только один проект в Workspace. Это приводит к тому, что при необходимости внести какие-то изменения в какой-то общеиспользуемый проект нужно сделать его активным, внести изменения, перекомпилировать, поочередно сделать активными проекты, от него зависящие, перекомпилировать их и т.д.
Я не являюсь специалистом по среде VisualStudio и не знаю, есть ли в ней средства, которые позволяют это упростить. Но мне кажется, что если они есть и не лежат на поверхности, то не целесообразно их изучать и использовать, если есть более привычные лично мне инструменты (например, M++4 или make). Поэтому я нашел способ совместно использовать то, что я знаю в VisualStudio и то, что предоставляет M++4.
Для использования mxxc из VisualStudio я поступил следующим образом:
@echo off set path=%path%;e:\home\eao\bin set MXX4=-i e:\home\eao\prj\mxx-4\lib -d MSC call "f:\program files\microsoft visual studio\vc98\bin\vcvars32.bat" mxxc.exe %*Для платформ Win95/Win98 ему нужно дать расширение .bat. Но, если на машине установлен только Visual C++ и настройки Visual C++ и M++4 выполнены в глобальных переменных окружения, то в файле run_mxxc.cmd нет необходимости - VisualStudio можно настроить на mxxc.exe сразу.
Command: e:\home\eao\bin\run_mxxc.cmd Arguments: Initial directory: $(WkspDir) Use Output Window: On
Теперь, занимаясь какой-либо разработкой, я создаю проект, который находится и указывает на корневой каталог разработки. В него я помещаю все свои исходные, заголовочные и проектные файлы. Для компиляции проекта я активизирую инструмент "Mxx4 for VC++". При этом текущим каталогом становится каталог проекта (т.е. корневой каталог разработки). В этом каталоге запускается mxxc без параметров. Утилита mxxc находит default.4xx и использует его в качестве проектного файла. Все сообщения об ошибках направляются в output-окно среды VisualStudio, что позволяет манипулировать сообщениями об ошибках так же, как и после "родной" компиляции проекта.
Для применения MFC в разработке нужно создать проект приложения (или DLL) с использованием MFC. Это позволит обращаться ко всем wizard-ам VisualStudio для работы со средствами MFC. Но для компиляции проекта средствами M++ нужно указывать в проектных файлах M++ дополнительные настройки. Вот как это было сделано в реальной разработке:
simexpl.4xx Проектный файл для главного модуля разработки // Файл 4xx/start.4xx сам загрузит первую часть шаблона #include "4xx/start.4xx" appMode = "exe"; screenMode = "window"; target = resultPath + "simexpl"; // Исходные тексты cppSource += "log.cpp"; cppSource += "args.cpp"; cppSource += "long_op_flags.cpp"; cppSource += "card_representation.cpp"; cppSource += "reader_thread.cpp"; cppSource += "reader_thread_queue.cpp"; cppSource += "ddv_chv.cpp"; cppSource += "ext_tree_ctrl.cpp"; cppSource += "rich_edit_filler.cpp"; cppSource += "view_log_dlg.cpp"; cppSource += "yesno_dlg.cpp"; cppSource += "notify_dlg.cpp"; cppSource += "io_requests.cpp"; cppSource += "file_handler.cpp"; cppSource += "file_handler_monitor.cpp"; cppSource += "fh_card_login_ok.cpp"; cppSource += "req_verify_chv.cpp"; cppSource += "file_handler_verify_chv.cpp"; cppSource += "req_chv1.cpp"; cppSource += "enter_pin_dlg.cpp"; cppSource += "enter_new_pin_dlg.cpp"; cppSource += "enter_puk_dlg.cpp"; cppSource += "file_handler_chv1.cpp"; cppSource += "file_handler_chv2.cpp"; cppSource += "xdn_item_dlg.cpp"; cppSource += "alpha_tag_charset_dlg.cpp"; cppSource += "xdn_sort_prop_dlg.cpp"; cppSource += "file_handler_xdn.cpp"; cppSource += "file_handler_plmn.cpp"; cppSource += "file_handler_lp.cpp"; cppSource += "sms_item_dlg.cpp"; cppSource += "file_handler_sms.cpp"; cppSource += "smsp_item_dlg.cpp"; cppSource += "file_handler_smsp.cpp"; cppSource += "fh_card_copy_load1.cpp"; cppSource += "fh_card_copy_load2.cpp"; cppSource += "fh_card_copy_load_factory.cpp"; cppSource += "fh_card_copy_write1.cpp"; cppSource += "fh_card_copy_write2.cpp"; cppSource += "fh_card_copy_write_factory.cpp"; cppSource += "copy_helper_window.cpp"; cppSource += "shutdown_dlg.cpp"; cppSource += "main_frame.cpp"; cppSource += "messages.cpp"; cppSource += "show_error_dlg.cpp"; cppSource += "simexpl.cpp"; cppSource += "sim_expl_doc.cpp"; cppSource += "sim_expl_tree_view.cpp"; cppSource += "sim_expl_view.cpp"; cppSource += "state_thread.cpp"; // Стандартные библиотеки sysLibs += "winscard.lib"; sysLibs += "scarddlg.lib"; // Ресурсный файл и его зависимости resourceSource = "simexpl.rc"; resFileDepends += "res/simexpl.ico"; resFileDepends += "res/simexpl.rc2"; resFileDepends += "res/sim_expl_doc.ico"; resFileDepends += "res/Toolbar.bmp"; resFileDepends += "res/tree_state_1.bmp"; resFileDepends += "res/tree_state_2.bmp"; resFileDepends += "res/tree_fol.bmp"; resFileDepends += "res/1_2.bmp"; // Собственные библиотеки, созданные в рамках той же разработки libs += resultPath + "stdafx"; libs += resultPath + "scard_lib"; libs += resultPath + "threads"; libs += resultPath + "req_percents"; libs += resultPath + "parsers"; libs += resultPath + "chv_lib"; libs += resultPath + "xdn_ef"; libs += resultPath + "plmn_ef"; libs += resultPath + "lp_ef"; libs += resultPath + "sms_ef"; libs += resultPath + "smsp_ef"; libs += resultPath + "card_copy"; // Проектные файлы для собственных библиотек dependOnPrjs += "4xx/scard_lib.4xx"; dependOnPrjs += "4xx/threads.4xx"; dependOnPrjs += "4xx/req_percents.4xx"; dependOnPrjs += "4xx/parsers.4xx"; dependOnPrjs += "4xx/chv_lib.4xx"; dependOnPrjs += "4xx/xdn_ef.4xx"; dependOnPrjs += "4xx/plmn_ef.4xx"; dependOnPrjs += "4xx/lp_ef.4xx"; dependOnPrjs += "4xx/sms_ef.4xx"; dependOnPrjs += "4xx/smsp_ef.4xx"; dependOnPrjs += "4xx/card_copy.4xx"; // Файл 4xx/finish.4xx сам загрузит вторую часть шаблона #include "4xx/finish.4xx"
Файл 4xx/start.4xx Должен загружаться вместо стандартного файла start.4xx // Загружаем первую часть шаблона #include <start.4xx> string resultPath; string pchFile; #if !defined( RELEASE ) && !defined( DEBUG ) #define DEBUG #endif #if defined( DEBUG ) // Определяем расположение результатов компиляции // для debug-build resultPath = "debug/"; objPath = "o/d"; #elif defined( RELEASE ) // Определяем расположение результатов компиляции // для release-build resultPath = "release/"; objPath = "o/r"; #endif // Имя для precompiled header pchFile = resultPath + "simexpl.pch";
Файл 4xx/finish.4xx Должен загружаться вместо стандартного файла finish.4xx Файл 4xx/mfc.4xx определяет настройки для MFC #include "4xx/mfc.4xx" Загружаем вторую часть шаблона #include <finish.4xx>
Файл 4xx/mfc.4xx Определяет установки для MFC #if defined( DEBUG ) // Настройки для debug-build cppOptions += "/W3 /Z7 /Od /GZ"; linkOptions += "/pdb:none"; defines += "_DEBUG"; #else // Настройки для release-build cppOptions += "/W3"; #endif // Если определ это символ, то используется // DLL-версия MFC и runtime-библиотек. // Учитывается в стандартном файле шаблона msc-defs.4xx //#define SHARED_RTL // Используем precompiled header cppOptions += "/Fp" + pchFile; // Символ CREATE_PCH_FILE определяется // в проектном файле 4xx/stdafx.4xx #if !defined( CREATE_PCH_FILE ) // Указываем, что используется в качестве // precompiled header cppOptions += "/Yustdafx.h"; #endif defines += [ "WIN32", "_WINDOWS", "_MBCS" ]; #if defined( SHARED_RTL ) defines += "_AFXDLL"; #endif cpp_add_include_path( stdIncludePath += "." );
Файл 4xx/stdafx.4xx Отвечает за создание precompiled header #include "4xx/start.4xx" appMode = "lib"; screenMode = "window"; target = resultPath + "stdafx"; cppSource += "stdafx.cpp"; // Указываем создание precompiled header cppOptions += "/Ycstdafx.h"; // Определяем символ CREATE_PCH_FILE чтобы не // конфликтовали опции /Yc и /Yu #define CREATE_PCH_FILE #if defined( CLEANUP ) || defined( REBUILD ) // Если удаляются результаты компиляции, // то удаляем precompiled header sys_remove( pchFile ); #endif #include "4xx/finish.4xx"