Delphi programming blog
Источник: http://teran.karelia.pro/articles/item_4498.html
 

Условная компиляция модулей

Опубликовано 05.12.2011 г. 16:23

Столкнулся с немного странным поведением Delphi 2010. Исходная проблема такова: допустим проект в зависимости от настроек может иметь некоторый модуль а может и нет. Под модулем будем понимать дата-модуль. Т.е. хочется в некотором конфиге при компиляции определить некоторую константу - использовать или не использовать указанный модуль. И вот в результате столкнулся с проблемой...

Вобще тут можно сказать, что линкер не будет собирать код, который не используется. Отчасти это так, но мой случай немного отличается. Для описания сути проблемы создаем новый проект - приложение VCL Forms. Помимо главной формы добавляем в него дата модуль - TestDM. В папке с проектом создаем файлик с описание директив - common.inc и подключаем его к проекту. В итоге дерево проектов выглядит следующим образом: Теперь в common.inc добавляем условную директиву:
{$DEFINE USE_MODULE}
Она будет определять, используется модуль или нет. Сам дата модуль будет использован в главной форме, например, при создании. Поэтому, если константа определена, то используем его:
procedure TMainForm.FormCreate(Sender: TObject);
begin
    {$IFDEF USE_MODULE}
        with TTestDataModule.Create(nil) do begin
            try
                ShowMessage('using DM!');
            finally
                Free();
            end;
        end;
    {$ENDIF}
end;
конечно же нам нужно подключить файл модуля в uses. в общем то подключать его надо только при условии использования:
{$INCLUDE 'common.inc'}

{$IFDEF USE_MODULE}
  uses testDM;
{$ENDIF}
Ок. все работает исправно. Константа определена - видим сообщение. не определена - не видим сообщения. В общем случае этого достаточно. Но в моем тесте проблема немного обширнее. Я хотел исключить модуль в самом dpr файле. Т.е модифицируем сам код проекта таким образом, чтобы файл модуля либо включался вообще в проект либо нет. Т.е отключаем его не только при компиляции, а вообще убираем из проекта если он не нужен:
program test;

{$INCLUDE 'common.inc'}
uses
  Forms,
  main in 'main.pas' {MainForm}
  {$IFDEF USE_MODULE}
  , testDM in 'testDM.pas' {TestDataModule: TDataModule}
  {$ENDIF}
  ;

{$R *.res}


begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TMainForm, MainForm);
  Application.Run;
end.
Получается, что если USE_MODULE определна, то файл участвует в проекте, если нет, то не участвует. Сохраняем проект, перезапускаем среду и с небольшим удивлением замечаем, что в дереве проекта остался только main.pas. Common.inc и TestDM.pas отсутствуют. В этом и заключается проблема с которой я столкнулся. Теперь к тому, почему мне требуется исключить файл из самого проекта. Дело в том, что в проекте в качестве главной единицы для хранения данных используется структура (запись). И допустим в некоторых случаях (если используем модуль) в записи этой должны быть доп. поля, если модуль не используется, то поля не нужны. т.е выглядит это опять таки следующим образом:
type TTestData = record
   field1 : integer;
   field2 : double;
   {$IFDEF USE_MODULE}
   FModuleField : TTypeDefinedInModule; 
   {$ENDIF}
end;
т.е получается, что при простом отключении USE_MODULE поле из записи убирается. Но код, который обращается к этому полю в дата модуле никуда не исчез. И если модуль включен в проект, этот код пытается скомпилироваться, что конечно же завершается ошибкой. Получается, что если не исключить модуль из проекта, то прийдется все обращения к полю TTestData.FModuleField также обрамлять в {$IFDEF}, что попахивает идиотизмом. Так что остается вопрос, почему при добавлении {IFDEF} в сам файла проекта .dpr, файлы TestDM & common.inc исключаются из дерева проекта. С одной стороны конечно dproj файл, описывающий проект, в частности дерево, строится по dpr файлу. И тут я в принципе допускаю что TestDM может исключаться оттуда. Но куда пропадает common.inc остается загадкой. Что же делать, как же быть? кто сталкивался с подобным поведением, или если у вас были подобные ситуации, то как вы их разрешали? Поделитесь опытом, товарищи!

Комментарии

smile
05.12.2011 в 17:12
Это во всех версиях так.
D5,D6,D7,D2005,D2006,D2007,D2010,DXE

Не выявил закономерностей.
Регулярно проверяю *.DPR
Держу в большом коментарии шаблон, как должно быть.
После обширных правок проверяю.
Дмитрий
14.12.2011 в 09:13
Согласен.

Поступаю именно так же. Ну и конечно, при коммите в систему контроля версий всегда проверяю dpr-файл проекта.
Andreas
05.12.2011 в 18:58
common.inc "выпадает" из проекта, т.к не перечислен в USES в dpr-файле (не подключен к проекту; и наверное, вообще, не модуль - unit), он просто там включается (include), что совсем разные вещи.

TestDM "выпадает" от того, что директива компилятора не в настройках проекта задана, а в include-файле. Попробуйте вписать USE_MODULE в настройки проекта, вдруг получится.

Без пахнущего идиотизмом IFDEF при каждом обращении к полю записи тоже вряд ли удасться обойтись.
sw
05.12.2011 в 18:41
Как сказано выше - это проблема любой версии Delphi.
Беда в том, что раздел uses drp-файла среда полностью создаёт заново при добалвении/исключении модулей проекта.

Я эту проблему решил следующим образом: создал юнит, например под таким названием:
MyAdditions.pas
В котором директивами включаю (или не включаю) доп. модули в uses этого файла (у меня там FastMM/FastCode/EurekaLog etc.)

А в dpr-файле в разделе uses модуль MyAdditions идёт первым.
IL
06.12.2011 в 00:23
Тоже наткнулись на это в D7 проекте из нескольких dpr на одной базе кода. В опциях dpr прописаны специфические и общие DEFINE. При любом сохранении .dpr все обрамления IFDEF убираются и остаются те команды, которые были внутри включенного DEFINE и отметаются все команды, DEFINE которых был отключен. Теперь проект переделывается под плагины на интерфейсах в D2010/XE и отпала надобность в DEFINE на уровне dpr.
jack128
06.12.2011 в 14:39
>> Что же делать, как же быть? кто сталкивался с подобным поведением, >> или если у вас были подобные ситуации, то как вы их разрешали?
>> Поделитесь опытом, товарищи!

Просто поставь в ifdef содержимое модуля testDM.pas
ter
06.12.2011 в 16:32
мысль интересная конечно (:
попробую (:
ter
07.12.2011 в 12:45
попробовал (: вроде результат успешен.
модули теперь выглядят как-то так:

unit xyz;

interface

{$IFDEF USE_MODULE}
uses ...

  type 
    TForm1 = ...
{$ENDUF}

implementation

{$IFDEF USE_MODULE}
uses ...

{$R *.res}
... 

{$ENDIF}

end.
ter
16.12.2011 в 13:36
что то с формами плохо выходит такой фокус. редактор не адекватен становится =/
Алексей Тимохин
07.12.2011 в 18:43
Да, dpr не умеет дружить с директивами компиляции вообще и с IFDEF-ами в частности. Delphi автоматом переписывает содержимое блока uses после каждого изменения опций проекта.

Так как у меня все проекты под надзором системы контроля версий, то обычно при изменении dpr-файла вручную восстанавливаю выброшенные строки.
tartuff22
18.10.2013 в 10:42
Интересное решение описанной проблемы http://www.deltics.co.nz/blog/posts/1954
- Имя
- e-mail*
- Сайт
вы можете использовать теги [i],[b],[code],[quote]
Дополнительно