Macros and Conditionals/ru

From Lazarus wiki

English (en) français (fr) русский (ru)

Обзор

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

Build macros (Макросы сборки) - это макросы, специфичные для проекта/пакета. Они не передаются компилятору. Ярким примером является LCLWidgetType, который определяется пакетом LCL и позволяет выбирать набор виджетов (carbon, gtk, qt, win32, wince, ...).


Conditionals (Условные определения) - это правила, позволяющие задавать макросы в зависимости от целевой платформы и/или других макросов. Например, вы можете использовать их для определения специальных параметров компоновщика при компиляции для Mac OS X. Они используют язык сценариев типа паскаль, позволяющий определять даже сложные правила.

Вы можете задать макросы сборки через Project / Project Options / Compiler Options / Additions and Overrides.

Вы можете задать условные определения через Project / Project Options / Compiler Options / Other / Conditionals (прим.перев.: в версиях Лазаруса 2.0.х и выше опция находится здесь Project / Project Options / Compiler Options / Custom Options).

conditional.png

Макросы сборки и условные определения существуют с Lazarus 0.9.29.

CompilerAdditionsAndOverrides1.png

Условные определения

В условных определениях используется простой язык сценариев.

Изменение параметров компилятора с помощью условных определений

Сценарий условных определений может задавать некоторые переменные, значения которых используются IDE для параметров компилятора:

  • UnitPath: добавляется к Other Source Files (-Fu). Символы / и \ автоматически преобразуются в разделитель текущего пути. После добавления пути поиска с точкой с запятой макросы заменяются, а относительные пути расширяются каталогом пакета/проекта.
  • IncPath: добавляется к Include Files (-Fi).
  • LibraryPath: добавляется к Libraries (-Fl).
  • SrcPath: добавляется к Other sources (не используются компилятором, только IDE).
  • DebugPath: добавляется к Debugger path addition (не используется ни компилятором, ни инструментами кода, используется только отладчиком).
  • LinkerOptions: добавляется к параметрам компоновщика (-k). Разделители путей не меняются. Относительные пути не расширяются. Макросы заменяются.
  • CustomOptions: добавляется к параметрам компилятора. Разделители путей не меняются. Относительные пути не расширяются. Макросы заменяются.
  • OutputDir: если задано, он заменит текущий каталог вывода. Разделители путей не меняются. Относительные пути не расширяются. Макросы заменяются.

Для пакетов существует больше переменных для параметров использования. Эти пути наследуются, то есть добавляются ко всем пакетам/проектам, использующим этот пакет, прямо или косвенно:

  • UsageUnitPath: добавляется к используемым путям Unit (-Fu). Символы / и \ автоматически конвертиуются в текущий разделитель путей. После добавления пути поиска с точкой с запятой макросы заменяются, а относительные пути расширяются каталогом пакета/проекта.
  • UsageIncPath: добавляется к используемым путям Include (-Fi).
  • UsageLibraryPath: добавляется к используемым путям Library (-Fl).
  • UsageSrcPath: добавляется к используемым путям Source (не используются компилятором, только IDE).
  • UsageDebugPath: добавляется к используемым путям Debugger (не используется ни компилятором, ни инструментами кода, используется только отладчиком).
  • UsageLinkerOptions: добавляется к используемым параметрам компоновщика (-k). Разделители путей не меняются. Относительные пути не расширяются. Макросы заменяются.
  • UsageCustomOptions: добавляется к используемым параметрам компилятора. Разделители путей не меняются. Относительные пути не расширяются.

Синтаксис условных определений

Поддерживаемые синтактические конструкции

  • If <условие> then <выражение>;
  • If <условие> then <выражение> else <выражение>;
  • begin <выражение>; ... <выражение>; end;
  • <Переменная>:=<условие>;
  • <Переменная>+=<условие>;
  • <Комментарий> //, { }, (* *)
  • Пробелы, табуляции и переводы строк то же самое. Как и в Паскале, они нужны только для разделения двух идентификаторов/ключевых слов.

Переменные

Переменные задаются просто путем присвоения значения. Есть несколько предопределенных переменных. См. список ниже.

Переменные могут быть определены или не определены:

if defined(Name) then ...
if undefined(Name) then ...
undefine(Name);

Если переменная задана, она имеет один из трех типов:

  • none
  • string
  • number (int64)

Переменная является false, если она имеет номер 0 или строку '0'. В противном случае это true. Это означает, что переменная без значения истинна и неопределенная переменная также истинна.

Вы можете изменить тип переменной с помощью:

a := '1';        // строка
a := integer(a); // преобразование в число с помощью StrToInt(), в случае неудачи 'a' остается строкой
a := int64(a);   // преобразование в число с помощью StrToInt6(), в случае неудачи 'a' остается строкой
a := string(a);  // преобразование в строку

Константы

a := 1234; // десятичная
a := $A1B; // шестнадцатеричная
a := &127; // восьмеричная
a := %101; // двоичная
a := '10'; // строковая
a := #123; // символьная

Операторы

  • +: Два числа складываются, иначе они рассматриваются как строки и объединяются.
  • <, >, <=, >=, =, <>: Два числа сравниваются математически, в противном случае они рассматриваются как строки и сравниваются лексикографически (с учетом регистра).
  • not: Унарный (с изменением знака) логический оператор
  • and, or, xor: Логические операторы
  • (,): Групповые операторы
  • :=: Присвоение. Не допускается в выражениях.
  • +=: Добавление и назначение. Не допускается в выражениях.

Уровни приоритета:

  • 1 Not и унарный минус
  • 1 And
  • 2 Or
  • 2 XOr
  • 2 +
  • 4 =
  • 4 <>
  • 4 <
  • 4 <=
  • 4 >
  • 4 >=

Predefined Variables

Before the conditionals script is run the IDE initializes a few variables:

  • TargetOS: the project's target operating system as defined by fpc. e.g. 'linux', 'win32', 'darwin', 'freebsd', 'wince'.
  • TargetCPU: the project's target processor as defined by fpc. e.g. 'i386', 'x86_64', 'arm'
  • SrcOS: the value 'win' for all MS Windows platforms and 'unix' for all unix like platforms. The value depends on the TargetOS.
  • SrcOS2: the value 'bsd' for all BSD like platforms. The value depends on the TargetOS.
  • True: value 1
  • False: value 0
  • all build macros defined by the current project.
  • all build macros defined by used packages. That means if the project has not defined a value for a package macro the value of the conditionals of the used package will be used.

Predefined Functions

  • Defined(Variable): returns 1 (true) if the variable is defined otherwise 0 (false). Example if Defined(Macro1) then ;
  • Undefine(Variable): undefine the variable. Example: Undefine(A);
  • Int64(expression): Returns the int64 value of expression. Example: i:=int64('3');
  • Integer(expression): Returns the integer value of expression. Example: i:=integer('3');
  • String(expression): Returns the string value of expression. Example: s:=string(3);
  • GetIDEValue(string): See IDE macros
    • GetIDEValue('OS'): Returns the OS with which the IDE was compiled. since 1.0.
    • GetIDEValue('CPU'): Returns the CPU with which the IDE was compiled. since 1.0.
    • GetIDEValue('SrcOS'): SrcOS of IDE. since 1.0.
    • GetIDEValue('SrcOS2'): SrcOS2 of IDE. since 1.0.
    • GetIDEValue('LCLWidgeType'): LCL platform of the IDE, which might be different than project. lazbuild returns win32, carbon or gtk2 depending on its OS. since 1.0.
  • GetEnv(string): Returns the environment variable. For example GetEnv('HOME') returns under Linux the user's home directory. since 1.0.
  • GetProjValue(string): Returns a project value.
    • GetProjValue('FPC_FULLVERSION'): Returns the FPC_FULLVERSION fetched from the project compiler. The version is an integer e.g. 20701. since 1.3.

Order of macro computation

  1. the values of the current project are taken
  2. if the project does not define values for TargetOS, TargetCPU the IDE sets defaults
  3. SrcOS and SrcOS2 are computed from TargetOS
  4. the conditionals of used packages are computed. If package A uses package B, then the conditionals of B are computed before the conditionals of A.
  5. Every conditional script starts with the values of the project.
    • If the project does not define a value for a package macro, the result of the used package is used.
    • A script can alter any macro while it runs, but only the build macros and the built-in macros are used. For example you can set TargetOS to another value, but this has no effect on any other script, nor on any search path using $(TargetOS), not even the search path of the package itself.
    • If two packages define the same build macro, then both can alter the value (unless the project sets a value). The exact effect depends on the dependency order.
  6. the default IDE macros are used last.

Examples

Adding a compiler flag for target Linux

if TargetOS = 'linux' then 
  CustomOptions := '-dUseCThreads';

Note:

  • TargetOS is a predefined macro by the IDE.
  • CustomOptions is a result variable used by the IDE.
  • The identifiers are case insensitive, but = (equal) operator is not. The TargetOS macro uses the same case as the compiler does, which is currently all lowercase.
  • The IDE adds the first space automatically when adding the custom options.
  • When used in a package the above only will be applied to the options used for compiling the package, not to the project using the package.

Adding some linker options for target Mac OS X

The compiler uses for Mac OS X the value 'darwin' for TargetOS.

if TargetOS = 'darwin' then begin
  LinkerOptions += ' -framework Cocoa';
  if TargetCPU = 'i386' then 
    LinkerOptions += ' -framework OpenGL';
end;

Notes:

  • TargetOS and TargetCPU are predefined macros by the IDE.
  • The += operator appends a string or adds a number.
  • When adding several options you have to add a space between options.
  • The IDE adds the first space automatically when adding the linker options.
  • You can nest begin..end just like Pascal.
  • If both conditions hold LinkerOptions will contain the value ' -framework Cocoa -framework OpenGL'.
  • The IDE automatically prepends the -k option when passing linker options to the compiler.
  • The above only works for project's conditionals.

Adding some linker options for Mac OS X for all projects using a package

Packages have two different sets of options. The set used for compiling the package itself and the options added to all projects/packages using the package. You have to change the usage options of the package:

if TargetOS = 'darwin' then begin
  UsageLinkerOptions += ' -framework Cocoa';
  if TargetCPU = 'i386' then 
    UsageLinkerOptions += ' -framework OpenGL';
end;

Adding an option to a package

If your package provides some optional parts, for example the ability to use opengl you can define a build macro. The following explains how to set this up step by step:

Let's say your package is called Pkg1 and allows to switch on opengl support by compiling it with the flag -dEnableOpenGL. The package should provide a boolean option Pkg1_UseOpenGL with the values 'True' and 'False'.

Open the package editor of your package 'pkg1', click on compiler options, select the page IDE Macros.

Click on the left + button to add a new macro. It will be called 'Pkg1_Macro1'. Click on the tree node to rename it to 'Pkg1_UseOpenGL'.

Click on the middle + button to add a new macro value. It will be called 'Value1'. Click on the tree node to rename it to 'True'. Repeat this for the value 'False'.

Add the following code into the Conditionals (Compiler Options / Other / Conditionals) text box:

if Pkg1_UseOpenGL = 'True' then 
  CustomOptions := '-dEnableOpenGL';

Compileroptions buildmacro example1.png

Notes:

  • The value 'True' is case sensitive. Because the value is usually selected from the combobox, there is no chance of typos.
  • If the user has not set any value for the macro, the Variable 'Pkg1_UseOpenGL' is undefined and the expression results to false.

Add a release/debug mode

A release/debug mode for your project and all your packages

Note: This requires Lazarus 0.9.31 or higher.

In the project's compiler options (Project / Project Options / Compiler Options / Build modes) add a build mode Release. Adding this build mode will automatically activate it, so all changes to the compiler options are now only done in this build mode.

In "Set Macro Values" click on the left column on the last row "(none)". Set it to "MyPackageOptions". Note: It is not listed in the combo box.

Set its value to the options. For example "-O3".

Build mode release macro1.png

This macro is usable in the project compiler options and all packages.

For each package do: Open the compiler options of the package, go to the page Other. Add to Custom options the text $(MyPackageOptions).

A special release/debug mode for one package

Let's say the normal mode is the debug mode and you want another mode for the release.

Open the package editor of your package 'pkg1', click on compiler options, select the page Build Macros.

Under Build macros click on the left + button to add a new macro. It will be called 'Pkg1_Macro1'. Click on the tree node to rename it to 'Pkg1_Release'.

Click on the middle + button to add a new macro value. It will be called 'Value1'. Click on the tree node to rename it to 'True'. Repeat this for the value 'False'.

Add the following code into the Conditionals text box:

if Pkg1_Release = 'True' then 
  // release mode
  CustomOptions := '-O3'
else
  // debug mode
  CustomOptions := '-Sa Ctroi';

Notes:

  • The value 'True' is case sensitive. Because the value is usually selected from the combobox, there is no chance of typos.
  • If the user has not set any value for the macro, the Variable 'Pkg1_Release' is undefined and the expression results to false.

Adding a macro for search paths to handle different platforms

Let's say your package is called Pkg1 and uses the windows GDI on MS Windows and X11 on Linux and BSD. The platform independent units are in the package main directory and you have created two sub directories 'gdi' and 'x11' containing the platform dependent units:

/path/of/your/package/pkg1/
  pkg1.lpk
  pkg1.pas
  generalunit.pas
  gdi/
    backend.pas
  x11/
    backend.pas

The unit backend in gdi should be used on Win32, Win64 and WinCE, while under Linux and FreeBSD the backend unit in the x11 should be used. This means the Other unit files (-Fu) search path must be adapted depending on the target OS. The following describes how to set this up step by step:

Open the package editor of your package 'pkg1', click on compiler options, select the page Build Macros.

Click on the left + button to add a new macro. It will be called 'Pkg1_Macro1'. Click on the tree node to rename it to 'Pkg1_Backend'.

Click on the middle + button to add a new macro value. It will be called 'Value1'. Click on the tree node to rename it to 'gdi'. Repeat this for the value 'x11'.

Add the following code into the Conditionals text box:

// choose a value for Pkg1_Backend depending on the TargetOS
if undefined(Pkg1_Backend) then begin
  if SrcOS='win' then
    Pkg1_Backend:='gdi'
  else
    Pkg1_Backend:='x11';
end;

The macro $(Pkg1_Backend) will become usable once you click Ok. So, click 'Ok' to close the compiler options and open it again to use it in the search path Other unit files (-Fu).

Compileroptions buildmacro example2.png

Notes:

  • The user can override the value of Pkg1_Backend. Therefore it is good practice to enclose the setting in if undefined(Pkg1_Backend).
  • Instead of the SrcOS macro you could use if (TargetOS='win32') or (TargetOS='win64') or (TargetOS='wince') then
Directives, definitions and conditionals definitions
global compiler directives • local compiler directives

Conditional Compiler Options • Conditional compilation • Macros and Conditionals • Platform defines
$IF