Глава 4
Основная идея

Основная идея Mxx_ru заключается в том, что каждый проект определяет target (цель), которая должна быть построена. Каждая цель описывается экземпляром класса, производного от Mxx_ru::Abstract_target. Каждая цель описывается в одном проектном файле. Каждая цель должна иметь уникальный псевдоним (prj_alias). В Mxx_ru псевдонимом цели является имя проектного файла, в котором описывается цель.

Задачей проектного файла является создание экземпляра объекта-цели и передача этого объекта в Mxx_ru с помощью функции Mxx_ru::setup_target. Поэтому проекты в Mxx_ru, как правило, имеют вид:


1  require ’mxx_ru/<что-то>’
2  
3  Mxx_ru::setup_target(
4    <какой-то класс>.new( <имя проектного файла> ) {
5      <настройка параметров проекта>
6    }
7  )

4.1 Подробнее о Mxx_ru::Abstract_target

Класс Mxx_ru::Abstract_target является базовым классом для всех целей. Он определяет два самых важных метода: build для построения цели и clean для очистки цели. В самом классе Mxx_ru::Abstract_target эти методы считаются абстрактными (чистыми виртуальными, в терминологии C++), т.е. должны быть переопределены в производных классах. Но, поскольку в Ruby нет понятия чистого виртуального метода, то попытка вызова методов build, clean из класса Mxx_ru::Abstract_target приведет к порождению исключения Mxx_ru::Abstract_method_ex.

Получить псевдоним цели можно с помощью метода prj_alias.

4.1.1 Имена производимых целью файлов

Как правило, цель определяется для того, чтобы получить один результирующий файл. Например, исполнимый файл для C++ проектов, DVI-файл для LaTeX проектов или jar-файл для Java проектов. Но, в общем случае, цель может приводить к построению нескольких файлов — это зависит от типа проекта.

Сколько бы файлов не производила цель, все их полные имена должны быть переданы в базовый класс Mxx_ru::Abstract_target посредством метода mxx_add_full_target_name. Получить имена всех производимых целью файлов можно с помощью метода mxx_full_target_names.

Важно, чтобы в имена производимых файлов не включались имена промежуточных результатов построения цели. Например, для C/C++ проектов такими промежуточными результатами являются объектные файлы. Их имена не должны быть доступны через mxx_full_target_names.

4.1.2 Необходимые подпроекты

Часто бывает, что цель нуждается в файлах, производимых другой целью из другого проектного файла. Например, для линкования exe-файла могут потребоваться lib-файлы, создаваемые другими подпроектами. В этих случая проект (цель) нуждается в подпроектах (подчиненных проектах, подчиненных целях).

Подпроектом должен быть проектный Mxx_ru-файл, созданный по обычным правилам формирования проектных Mxx_ru-файлов. Подпроекты указываются при помощи метода required_prj:


1  require ’mxx_ru/cpp’
2  
3  Mxx_ru::setup_target(
4   Mxx_ru::Cpp::Composite_target.new( Mxx_ru::BUILD_ROOT ) {
5  
6   global_include_path( "." )
7  
8   required_prj( "oess_1/stdsn/prj.rb" )
9   required_prj( "oess_1/scheme/prj.rb" )
10   required_prj( "oess_1/util_cpp_serializer/prj.rb" )
11   required_prj( "oess_1/file/prj.rb" )
12   required_prj( "oess_1/db/prj.rb" )
13   required_prj( "oess_1/util_ent_enum/prj.rb" )
14   required_prj( "oess_1/util_slice_create/prj.rb" )
15   required_prj( "oess_1/tlv/prj.rb" )
16   }
17  )

Метод required_prj не только сохраняет в описании проекта имя его подпроекта — в нем происходит загрузка и обработка файла подпроекта.

Получить имена всех подпроектов можно с помощью метода mxx_required_prjs.

Обработка подпроектов может зависить от типа проекта, но исходя из здравого смысла, можно ожидать, что в методе build сначала вызываются методы build для всех подпроектов. Например, в C++ проектах так и происходит.

4.1.3 Генераторы исходных текстов

В некоторых случаях требуется из файлов определенного типа при помощи специальных инструментов сгенерировать исходные файлы. Например, из .y-файлов с помощью yacc генерируется исходный код синтаксических анализаторов.

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

Поэтому в Mxx_ru используется обобщенное понятие генератора. Есть базовый класс Mxx_ru::Abstract_generator, который определяет два метода: build и clean. Все генераторы должны быть производными от этого класса.

Поскольку различные генераторы требуют различных способов работы с ними, то Mxx_ru никак не специфицирует других интерфейсов генераторов. Единственное требование: генераторы должны быть переданы в Mxx_ru::Abstract_target с помощью метода generator.

За запуск генераторов отвечает конкретный класс, производный от Mxx_ru::Abstract_target. Например, классы целей для C++ проектов запускают генераторы после того, как обработают все подпроекты и перед тем, как начнется компиляция самого проекта.

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


1  require ’mxx_ru/<что-то>’
2  
3  require ’<файл с описанием генератора>’
4  
5  Mxx_ru::setup_target(
6   Mxx_ru::<какой-то класс цели>.new( <псевдоним> ) {
7   ...
8   gen = generator( <класс генератора>.new( <параметры> ) )
9   gen.<какой-то метод>( <параметры> )
10   ...
11   }
12  )

Например:


1  require ’mxx_ru/cpp’
2  
3  require ’oess_1/version’
4  require ’oess_1/util_cpp_serializer/gen’
5  
6  Mxx_ru::setup_target(
7   Mxx_ru::Cpp::Dll_target.new( "oess_1/db/prj.rb" ) {
8  
9   ...
10   ddl_cpp_generator = generator(
11   Oess_1::Util_cpp_serializer::Gen.new( self ) )
12  
13   sources_root( "impl" ) {
14  
15   sources_root( "db_struct" ) {
16   cpp_source( "unit.cpp" )
17   ddl_cpp_generator.ddl_file( "unit.ddl" )
18  
19   cpp_source( "slice_unit.cpp" )
20   ddl_cpp_generator.ddl_file( "slice_unit.ddl" )
21  
22   cpp_source( "slice_stream_item.cpp" )
23   ddl_cpp_generator.ddl_file( "slice_stream_item.ddl" )
24  
25   cpp_source( "db_slicemap.cpp" )
26   cpp_source( "std_db_slicemap.cpp" )
27  
28   cpp_source( "db_struct.cpp" )
29   cpp_source( "std_db_struct.cpp" )
30   }
31   }
32   ...
33   }
34  )
35  

Либо, учитывая, что метод generator возвращает переданный ему объект-генератор, можно вызывать методы генератора не сохраняя ссылку на него в отдельной переменной:


1  require ’mxx_ru/cpp’
2  
3  require ’oess_1/util_cpp_serializer/gen’
4  
5  Mxx_ru::setup_target(
6   Mxx_ru::Cpp::Exe_target.new( "test/stdsn/shptr/prj.rb" ) {
7  
8   target( "test.stdsn.shptr" )
9  
10   required_prj( "oess_1/defs/prj.rb" )
11   required_prj( "oess_1/io/prj.rb" )
12  
13   generator( Oess_1::Util_cpp_serializer::Gen.new( self ) ).
14   ddl_file( "main.ddl" )
15  
16   cpp_source( "main.cpp" )
17   }
18  )

Получить список всех установленных генераторов можно с помощью метода mxx_generators.

Еще один способ применения генераторов описан в 6.6 на стр.  80

4.2 Подробнее о Mxx_ru::setup_target

Mxx_ru поддерживает карту целей — псевдонимам (т.е. именам проектных файлов) сопоставлены объекты-цели. Функция Mxx_ru::setup_target предназначена для занесения очередной цели в эту карту.

Кроме того, функция Mxx_ru::setup_target автоматически запускает построение или очистку проекта, если переданная в Mxx_ru::setup_target цель является самой верхней (т.е. цель создается в проектном файле, имя которого передано интерпретатору Ruby)1.

При обработке самой верхней цели Функция Mxx_ru::setup_target проверяет наличие аргумента --mxx-clean. Если этот аргумент указан, то у цели вызывается метод clean. В противном случае у цели вызывается метод build.

Hosted by uCoz