1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
|
In this lesson we are going to look how EDKII configures its build tools.
As you might remember when you source `edksetup.sh` script for the first time, this script creates `Conf` folder from the contents inside the [https://github.com/tianocore/edk2/tree/master/BaseTools/Conf](https://github.com/tianocore/edk2/tree/master/BaseTools/Conf) directory.
In this lesson we are going to look at the content of the `Conf/tools_def.txt` file which is created from the [https://github.com/tianocore/edk2/blob/master/BaseTools/Conf/tools_def.template](https://github.com/tianocore/edk2/blob/master/BaseTools/Conf/tools_def.template).
This file defines configurations for the edk2 build tools.
Every configuration is representent in a format `TARGET_TOOLCHAIN_ARCH_COMMANDTYPE_ATTRIBUTE`, for example:
```
RELEASE_VS2013x86_X64_CC_FLAGS = /nologo /c /WX /GS- /W4 /Gs32768 /D UNICODE /O1b2s /GL /Gy /FIAutoGen.h /EHs-c- /GR- /GF /Gw
```
In this example:
```
TARGET = RELEASE
TOOLCHAIN = VS2013x86
ARCH = X64
COMMANDTYPE = CC
ATTRIBUTE = FLAGS
```
This means, when the project is build in `RELEASE` mode with a `VS2013x86` toolchain for `X64` architecture use the `FLAGS` flags for the compiler `CC`.
If we use `*` in place of some part of the format string, it means `for every`. For example:
```
*_GCC5_X64_NASM_FLAGS = -f elf64
```
This means, when the project is build with a `GCC5` toolchain for the `X64` architecture use the `FLAGS` flags for the nasm assembler `NASM`. And this configuration would be active for every build mode (RELEASE/DEBUG/NOOPT).
There can be many `*` in the configuration string. For example:
```
*_*_*_BROTLI_PATH = BrotliCompress
```
This means that the `PATH` attribute for the `BROTLI` command is `BrotliCompress`. And this is true for builds for any arch, toolchain or build mode.
Another example:
```
*_CLANG38_*_MAKE_PATH = make
```
I guess no explanation is needed at this point. I just wanted to point out, that the `*` could be in any part of the format string.
# `DEF` and `ENV`
The `tools_def.txt` file has several syntactic constructions to help format strings definition.
`DEFINE` statement is used to create a named data that can be later expanded via `DEF` statement. This is similar to the C language define statemnt. For example:
```
DEFINE IASL_OUTFLAGS = -p
...
*_GCC49_*_ASL_OUTFLAGS = DEF(IASL_OUTFLAGS)
```
There can be chains of defines:
```
DEFINE GCC_ASM_FLAGS = -c -x assembler -imacros AutoGen.h
DEFINE GCC48_ASM_FLAGS = DEF(GCC_ASM_FLAGS)
DEFINE GCC49_ASM_FLAGS = DEF(GCC48_ASM_FLAGS)
DEFINE GCC5_ASM_FLAGS = DEF(GCC49_ASM_FLAGS)
*_GCC5_X64_ASM_FLAGS = DEF(GCC5_ASM_FLAGS) -m64
```
Another syntactic construction is `ENV`. It is used when the value should be obtained from the environment:
```
*_GCC5_ARM_CC_PATH = ENV(GCC5_ARM_PREFIX)gcc
```
This way if we cross compile EDKII for the ARM architecture we can set gcc prefix:
```
export GCC5_ARM_PREFIX=<...>
```
# Precedence rules
The more defined statements override the more common ones. The `tools_def.txt` has a comment about the override rules:
```
TARGET_TOOLCHAIN_ARCH_COMMANDTYPE_ATTRIBUTE (Highest)
******_TOOLCHAIN_ARCH_COMMANDTYPE_ATTRIBUTE
TARGET_*********_ARCH_COMMANDTYPE_ATTRIBUTE
******_*********_ARCH_COMMANDTYPE_ATTRIBUTE
TARGET_TOOLCHAIN_****_COMMANDTYPE_ATTRIBUTE
******_TOOLCHAIN_****_COMMANDTYPE_ATTRIBUTE
TARGET_*********_****_COMMANDTYPE_ATTRIBUTE
******_*********_****_COMMANDTYPE_ATTRIBUTE
TARGET_TOOLCHAIN_ARCH_***********_ATTRIBUTE
******_TOOLCHAIN_ARCH_***********_ATTRIBUTE
TARGET_*********_ARCH_***********_ATTRIBUTE
******_*********_ARCH_***********_ATTRIBUTE
TARGET_TOOLCHAIN_****_***********_ATTRIBUTE
******_TOOLCHAIN_****_***********_ATTRIBUTE
TARGET_*********_****_***********_ATTRIBUTE
******_*********_****_***********_ATTRIBUTE (Lowest)
```
# Tools and flags review
Here is some peak on the parameters that currently can be defined for the commands:
```
TARGET =
RELEASE | DEBUG | NOOPT
```
```
TOOLCHAIN =
VS2008 | VS2008x86 | VS2010 | VS2010x86 | VS2012 | VS2012x86 | VS2013 | VS2013x86 | VS2015 | VS2015x86 | VS2017 | VS2019
GCC48 | GCC49 | GCC5
CLANG35 | CLANG38 | CLANGPDB | CLANGDWARF
XCODE5
RVCT | RVCTLINUX | RVCTCYGWIN # ARM RealView Tools Windows | ARM RealView Tools Linux | ARM RealView Tools - Cygwin
```
```
ARCH =
IA32 | X64 | EBC | AARCH64 | ARM | RISCV64
```
```
ATTRIBUTE =
PATH | FLAGS | OUTFLAGS | XIPFLAGS | GUID | DLL | FAMILY | BUILDRULEFAMILY
```
And here are descriptions for some of the common commands:
```
COMMANDTYPE =
APP # C compiler for applications
ASL # ACPI Compiler for generating ACPI tables
ASLCC # ACPI Table C compiler
ASLDLINK # ACPI Table C Dynamic linker
ASLPP # ASL C pre-processor
ASM # A Macro Assembler for assembly code in some libraries
ASMLINK # The Linker to use for assembly code generated by the ASM tool
CC # C compiler for PE32/PE32+/Coff images
DLINK # The C dynamic linker
MAKE # Required for tool chains. This identifies the utility used to process the Makefiles generated by the first phase of the build
PCH # The compiler for generating pre-compiled headers
PP # The C pre-processor command
SLINK # The C static linker
TIANO # This special keyword identifies a compression tool used to generate compression sections as well as the library needed to uncompress an image in the firmware volume
VFR # The VFR file compiler which creates IFR code
VFRPP # The C pre-processor used to process VFR files
```
# Setting `TARGET`/`TOOLCHAIN`/`ARCH` for the project
Configuration strings are represented in the form `TARGET_TOOLCHAIN_ARCH_COMMANDTYPE_ATTRIBUTE`.
The first three parameters `TARGET`/`TOOLCHAIN`/`ARCH` are usually constant for the build environment, so we've even fixed them in our `Conf/target.txt` file:
```
...
TARGET = RELEASE
TARGET_ARCH = X64
TOOL_CHAIN_TAG = GCC5
...
```
But as you remember we can always override them from command prompt to build different configurations:
```
build <...> --arch=X64 --buildtarget=RELEASE --tagname=GCC5
```
# Modify build tools attributes via INF files
It is possible to modify build tools options in the INF files.
Let's create new app:
```
./createNewApp.sh BuildOptionsApp
```
And add it to our package DSC file `UefiLessonsPkg/UefiLessonsPkg.dsc`:
```
[Components]
...
UefiLessonsPkg/BuildOptionsApp/BuildOptionsApp.inf
```
Add this code to the `UefiLessonsPkg/BuildOptionsApp/BuildOptionsApp.c`:
```cpp
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>
EFI_STATUS
EFIAPI
UefiMain (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
#ifdef MY_DEBUG
Print(L"MY_DEBUG is definedi\n");
#endif
#ifdef MY_RELEASE
Print(L"MY_RELEASE is defined\n");
#endif
#ifdef MY_ALL_TARGETS
Print(L"MY_ALL_TARGETS is defined\n");
#endif
return EFI_SUCCESS;
}
```
Build our application:
```
$ build --buildtarget=RELEASE
```
And copy result to the QEMU shared folder
```
$ cp Build/UefiLessonsPkg/RELEASE_GCC5/X64/BuildOptionsApp.efi ~/UEFI_disk/
```
If you execute our app under UEFI shell now, it wouldn't produce any output as we didn't define any of the defines.
Now let's add this `[BuildOptions]` section to the application INF file:
```
[BuildOptions]
RELEASE_GCC5_X64_CC_FLAGS = "-DMY_RELEASE"
DEBUG_GCC5_X64_CC_FLAGS = "-DMY_DEBUG"
*_GCC5_CC_X64_FLAGS = "-DMY_ALL_TARGETS"
```
Now build and test our application in the RELEASE mode:
```
$ build --buildtarget=RELEASE
$ cp Build/UefiLessonsPkg/RELEASE_GCC5/X64/BuildOptionsApp.efi ~/UEFI_disk/
```
This version would give you this output:
```
FS0:\> BuildOptionsApp.efi
MY_RELEASE is defined
MY_ALL_TARGETS is defined
```
And if you build with the DEBUG mode:
```
$ build --buildtarget=DEBUG
$ cp Build/UefiLessonsPkg/DEBUG_GCC5/X64/BuildOptionsApp.efi ~/UEFI_disk/
```
You would get:
```
FS0:\> BuildOptionsApp.efi
MY_DEBUG is defined
MY_ALL_TARGETS is defined
```
If you look closely to the build log you could even see our defines in the EDK2 build output. For example this is a sample from the RELEASE build:
```
...
"gcc" -MMD -MF /<...>/Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/BuildOptionsApp/BuildOptionsApp/OUTPUT/AutoGen.obj.deps -g -Os -fshort-wchar -fno-builtin -fno-strict-aliasing -Wall -Werror -Wno-array-bounds -include AutoGen.h -fno-common -ffunction-sections -fdata-sections -DSTRING_ARRAY_NAME=BuildOptionsAppStrings -m64 -fno-stack-protector "-DEFIAPI=__attribute__((ms_abi))" -maccumulate-outgoing-args -mno-red-zone -Wno-address -mcmodel=small -fpie -fno-asynchronous-unwind-tables -Wno-address -flto -DUSING_LTO -Os -Wno-unused-but-set-variable -Wno-unused-const-variable "-DMY_RELEASE" "-DMY_ALL_TARGETS" -c -o /<...>/Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/BuildOptionsApp/BuildOptionsApp/OUTPUT/./AutoGen.obj -I/<...>/UefiLessonsPkg/BuildOptionsApp -I/<...>/Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/BuildOptionsApp/BuildOptionsApp/DEBUG -I/<...>/MdePkg -I/<...>/MdePkg/Include -I/<...>/MdePkg/Test/UnitTest/Include -I/<...>/MdePkg/Include/X64 /<...>/Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/BuildOptionsApp/BuildOptionsApp/DEBUG/AutoGen.c
...
```
The `=` sign in the our statements actually means "append". This is why we have both defines in the end.
If you want to completely override some attribute, you should use "==" syntax. EDK2 has many necessary options for the `CC_FLAGS`, so you probably shouldn't override this variable, but you might need this functionality for some other attributes.
# `FAMILY` attribute
With the help of a `FAMILY` attribute, it is possible to define rules for several toolchains. For example if we have these strings in the `tools_def.txt` file:
```
*_GCC48_*_*_FAMILY = GCC
*_GCC49_*_*_FAMILY = GCC
*_GCC5_*_*_FAMILY = GCC
```
Then we can add append like this:
```
[BuildOptions]
GCC:*_*_*_CC_FLAGS = "-DMY_FAMILY"
```
And it would be active if our toolchain one of the GCC48/GCC49/GCC5
# Command substitution in the tools options
As we were adding defines to the gcc in our example, I want to point out one interesting usage of this feature.
Look at the [https://github.com/tianocore/edk2/blob/master/EmbeddedPkg/Library/VirtualRealTimeClockLib/VirtualRealTimeClockLib.inf](https://github.com/tianocore/edk2/blob/master/EmbeddedPkg/Library/VirtualRealTimeClockLib/VirtualRealTimeClockLib.inf) `BuildOptions` section:
```
[BuildOptions]
GCC:*_*_*_CC_FLAGS = -DBUILD_EPOCH=`date +%s`
```
Here you can see that it is even possible to use command substitution in the `BuildOptions`. Fun, right?
In case you don't know this command gives current date as Unix time (which is the number of (non-leap) seconds since 1970-01-01). Try to execute it in you Linux shell:
```
$ date +%s
1657034765
```
In the code [https://github.com/tianocore/edk2/blob/master/EmbeddedPkg/Library/VirtualRealTimeClockLib/VirtualRealTimeClockLib.c] this define is used like this:
```cpp
EFI_STATUS
EFIAPI
LibGetTime (
OUT EFI_TIME *Time,
OUT EFI_TIME_CAPABILITIES *Capabilities
)
{
UINTN EpochSeconds;
...
EpochSeconds = BUILD_EPOCH;
...
}
```
# Modify build tools attributes via DSC files
Besides the INF files it is also possible to modify build tool attributes via the DSC file.
You can do it globally for all the package modules via the `[BuildOptions]` section like we did in the INF file:
```
[BuildOptions]
RELEASE_GCC5_X64_CC_FLAGS = "-DMY_RELEASE_DSC"
```
Or only for individual modules:
```
[Components]
...
UefiLessonsPkg/BuildOptionsApp/BuildOptionsApp.inf {
<BuildOptions>
RELEASE_GCC5_X64_CC_FLAGS = "-DMY_RELEASE_DSC"
}
```
In the first case this would lead to recompilation of all the modules listed in the DSC. And in the second case build system will recompile only one module.
# Section tag
In the examples above we've always used `[BuildOptions]` name for our section. But this is just a short form. The most full form for the section is `[BuildOptions.$(arch).CodeBase.ModuleType]`.
Here are:
- `$(arch)` - target architecture. You can use `common` for any architecture or define particular rules just for particalar one like `X64`, `AARCH64`, ...
- `CodeBase` - this is equal to the `EDKII`
- `ModuleType` - with this option we can define options for different classes of modules, e.g. `DXE_RUNTIME_DRIVER`, `SMM_CORE`. This value corresponds to the `MODULE_TYPE` value in the INF files
So the section `[BuildOptions.X64.EDKII.DXE_RUNTIME_DRIVER]` would change build options for the modules of type `DXE_RUNTIME_DRIVER` if they are compiled for the `X64` arch.
The `[BuildOptions]` is the shortest form, but it is possible to have all intermidiate forms. For example `[BuildOptions.X64]` section name is allowed.
Also it is possible to list several names as a section name. Look at this as an example:
```
[BuildOptions.common.EDKII.DXE_RUNTIME_DRIVER, BuildOptions.common.EDKII.COMBINED_SMM_DXE, BuildOptions.common.EDKII.DXE_SMM_DRIVER, BuildOptions.common.EDKII.SMM_CORE, BuildOptions.Common.EDK.DXE_RUNTIME_DRIVER]
```
With this you could define the same modifiers for different situations.
# Override priority
Generally if we use override `==` syntax, the DSC override would take the precedence over the INF override. But if we use `[BuildOptions.$(arch).CodeBase.ModuleType]` section names, things get a little bit more complicated.
Here is complete override sequence from the EDKII docs:
```
HIGHEST PRIORITY
- DSC file's component scoped <BuildOptions> for individual INF files
- [BuildOptions.$(arch).CodeBase.ModuleType]
- [BuildOptions.$(arch).CodeBase]
- [BuildOptions.common.CodeBase]
- [BuildOptions.$(arch)]
- [BuildOptions.common]
- [BuildOptions]
- INF file's [BuildOptions] section
- tools_def.txt
LOWEST PRIORITY
```
# Links
Here are links to the EDKII docs about `BuildOptions` section:
- in the INF file: [https://edk2-docs.gitbook.io/edk-ii-inf-specification/3_edk_ii_inf_file_format/35_-buildoptions-_sections](https://edk2-docs.gitbook.io/edk-ii-inf-specification/3_edk_ii_inf_file_format/35_-buildoptions-_sections) and [https://github.com/tianocore-docs/edk2-InfSpecification/blob/master/2_inf_overview/26_%5Bbuildoptions%5D_section.md](https://github.com/tianocore-docs/edk2-InfSpecification/blob/master/2_inf_overview/26_%5Bbuildoptions%5D_section.md)
- in the DSC file: [https://edk2-docs.gitbook.io/edk-ii-dsc-specification/3_edk_ii_dsc_file_format/36_-buildoptions-_sections](https://edk2-docs.gitbook.io/edk-ii-dsc-specification/3_edk_ii_dsc_file_format/36_-buildoptions-_sections) and [https://github.com/tianocore-docs/edk2-DscSpecification/blob/master/2_dsc_overview/24_%5Bbuildoptions%5D_section.md](https://github.com/tianocore-docs/edk2-DscSpecification/blob/master/2_dsc_overview/24_%5Bbuildoptions%5D_section.md)
|