1# Build System Coding Specifications and Best Practices 2 3## Overview 4 5Generate Ninja (GN) is a meta-build system that generates build files for Ninja. It is the front end of Ninja. GN and Ninja together complete OpenHarmony build tasks. 6 7### GN 8 9GN is used in large software systems such as Chromium, Fuchsia, and OpenHarmony. However, the GN syntax has limitations rooted in its design philosophy. For details, see https://gn.googlesource.com/gn/+/main/docs/language.md#Design-philosophy. For example, GN does not support wildcards and cannot get the length of a list. If you find it complex to implement something with GN, stop and consider whether it is necessary to do it. For more details about GN, see https://gn.googlesource.com/gn/+/main/docs/. 10 11### Intended Audience and Purpose 12 13This document is intended for OpenHarmony developers. It describes the GN coding style and practices, but does not cover the GN syntax. For details about the GN basics, see https://gn.googlesource.com/gn/+/main/docs/reference.md. 14 15### General Principles 16 17Scripts must be easy to read and maintain, and have good scalability and performance while functioning well. 18 19## Coding Style 20 21### Naming 22 23Follow the Linux kernel naming style, that is, lowercase letters + underscores (_). 24 25#### Local Variables 26 27A local variable is a variable restricted to use in a certain scope and cannot be passed down. 28 29Different from global variables, local variables start with an underscore (_). 30 31```shell 32# Example 1: 33action("some_action") { 34 ... 35 # _output is a local variable. 36 _output = "${target_out_dir}/${target_name}.out" 37 outputs = [ _output ] 38 args = [ 39 ... 40 "--output", 41 rebase_path(_output, root_build_dir), 42 ... 43 ] 44 ... 45} 46``` 47 48#### Global Variables 49 50A global variable starts with a lowercase letter. 51 52Use **declare_args** to declare the variable value only if the variable value can be modified by **gn args**. 53 54```shell 55# Example 2 56declare_args() { 57 # The value of some_feature can be changed by gn args. 58 some_feature = false 59} 60``` 61 62#### Targets 63 64Name the targets in the lowercase letters + underscores (_) format. 65 66Name the subtargets in templates in the ${target_name}+double underscores (__)+suffix format. This naming convention has the following advantages: 67 68- ${target_name} prevents duplicate subtarget names. 69 70- The double underscores (__) help locate the module to which a subtarget belongs. 71 72 ```shell 73 # Example 3 74 template("ohos_shared_library") { 75 # "{target_name}" (Target name) + "__" (double underscores) + "notice" (suffix) 76 _notice_target = "${target_name}__notice" 77 collect_notice(_notice_target) { 78 ... 79 } 80 shared_library(target_name) { 81 ... 82 } 83 } 84 ``` 85 86#### Custom Templates 87 88Name templates in the verb+object format. 89 90```shell 91# Example 4 92# Good 93template("compile_resources") { 94 ... 95} 96``` 97 98### Formatting 99 100GN scripts must be formatted before being submitted. Formatting ensures consistency in style, such as code alignment and line feed. Use the format tool provided by GN to format your scripts. The command is as follows: 101 102```shell 103$ gn format path-to-BUILD.gn 104``` 105 106**gn format** sorts the imported files in alphabetical order. To maintain the original sequence, add an empty comment line. 107 108For example, the original import sequence is as follows: 109 110```shell 111# Example 5 112import("//b.gni") 113import("//a.gni") 114``` 115 116**gn format** sorts the files as follows: 117 118```shell 119import("//a.gni") 120import("//b.gni") 121``` 122 123To maintain the original sequence, add an empty comment line. 124 125```shell 126import("//b.gni") 127# Comment to keep the original sequence 128import("//a.gni") 129``` 130 131## Coding Practices 132 133### Guidelines 134 135The build script completes the following tasks: 136 1371. Describes the dependency (deps) between modules. 138 139 In practice, the most common problem is lack of dependency. 140 1412. Defines the module build rules (rule). 142 143 In practice, unclear input and output are common problems. 144 145Lack of dependency leads to the following problems: 146 147- Unexpected compilation error 148 149 ```shell 150 # Example 6 151 # Lack of dependency poses a possibility of compilation errors. 152 shared_library("a") { 153 ... 154 } 155 shared_library("b") { 156 ... 157 ldflags = [ "-la" ] 158 deps = [] 159 ... 160 } 161 group("images") { 162 deps = [ ":b" ] 163 } 164 ``` 165 166 In this example, **libb.so** is linked to **liba.so**, which means that **b** depends on **a**. However, the dependency of **b** on **a** is not declared in the dependency list (**deps**) of **b**. Compilation is performed concurrently. An error occurs if **liba.so** is not available when **libb.so** attempts to create a link to it. 167 168 If **liba.so** is available, the compilation is successful. Therefore, lack of dependency poses a possibility of compilation errors. 169 170- Missing compilation of modules 171 172 In example 6, images are the target to build. Since images depend only on **b**, **a** will not be compiled. However, as **b** depends on **a**, an error occurs when **b** is linked. 173 174Another problem is unnecessary dependencies. Unnecessary dependencies reduce concurrency and slow down compilation. The following is an example: 175 176**_compile_js_target** does not necessarily depend on **_compile_resource_target**. If this dependency is added, **_compile_js_target** can be compiled only after **_compile_resource_target** is compiled. 177 178```shell 179# Example 7: 180# Unnecessary dependencies slow down compilation. 181template("too_much_deps") { 182 ... 183 _gen_resource_target = "${target_name}__res" 184 action(_gen_resource_target) { 185 ... 186 } 187 188 _compile_resource_target = "${target_name}__compile_res" 189 action(_compile_resource_target) { 190 deps = [":$_gen_resource_target"] 191 ... 192 } 193 194 _compile_js_target = "${target_name}__js" 195 action(_compile_js_target) { 196 # This deps is not required. 197 deps = [":$_compile_resource_target"] 198 } 199} 200``` 201 202Unclear input leads to the following problems: 203 204- Modified code is not compiled during incremental compilation. 205- The cache being used is still hit after the code is changed. 206 207In the following example, **foo.py** references the functions in **bar.py**. This means **bar.py** is the input of **foo.py**. You need to add **bar.py** to **input** or **depfile** of **implict_input_action**. Otherwise, if **bar.py** is modified, **implict_input_action** will not be recompiled. 208 209```shell 210# Example 8: 211action("implict_input_action") { 212 script = "//path-to-foo.py" 213 ... 214} 215``` 216 217```shell 218#!/usr/bin/env 219# Content of foo.py 220import bar 221... 222bar.some_function() 223... 224``` 225 226Unclear output leads to the following problems: 227 228- Implicit output 229- A failure to obtain the implicit output from the cache 230 231In the following example, **foo.py** generates two files: **a.out** and **b.out**. However, the output of **implict_output_action** declares only **a.out**. In this case, **b.out** is an implicit output, and the cache stores only **a.out**. When the cache is hit, **b.out** cannot be compiled. 232 233```shell 234# Example 9 235action("implict_output_action") { 236 outputs = ["${target_out_dir}/a.out"] 237 script = "//path-to-foo.py" 238 ... 239} 240``` 241 242```shell 243#!/usr/bin/env 244# Content of foo.py 245... 246write_file("b.out") 247write_file("a.out") 248... 249``` 250 251### Templates 252 253**Do not use GN native templates. Use the templates provided by the build system.** 254 255The GN native templates include **source_set**, **shared_library**, **static_library**, **action**, **executable** and **group**. 256 257The native templates are not recommended due to the following reasons: 258 259- The native templates provide only the minimal build configuration. They cannot provide functions, such as parsing **external_deps**, collecting notice, and generating installation information. 260 261- The native **action** template cannot automatically detect the changes in the dependencies of the input file, and cannot start recompilation. See Example 8. 262 263 The table below lists the mapping between the GN native templates and templates provided by OpenHarmony Compilation and Build subsystem. 264 265| OpenHarmony Template | GN Native Template | 266| :------------------ | -------------- | 267| ohos_shared_library | shared_library | 268| ohos_source_set | source_set | 269| ohos_executable | executable | 270| ohos_static_library | static_library | 271| action_with_pydeps | action | 272| ohos_group | group | 273 274### Using Python Scripts 275 276You are advised to use Python scripts instead of shell scripts in **action**. Compared with shell scripts, Python scripts feature: 277 278- More user-friendly syntax, which eliminates errors caused by lack of a space 279- Easier to read 280- Easier to maintain and debug 281- Faster compilation due to caching of Python tasks 282 283### rebase_path 284 285- Call **rebase_path** only in **args** of **action**. 286 287 ```shell 288 # Example 10 289 template("foo") { 290 action(target_name) { 291 ... 292 args = [ 293 # Call rebase_path only in args. 294 "--bar=" + rebase_path(invoker.bar, root_build_dir), 295 ... 296 ] 297 ... 298 } 299 } 300 301 foo("good") { 302 bar = something 303 ... 304 } 305 ``` 306 307- If rebase_path is called twice for the same variable, unexpected results occur. 308 309 ```shell 310 # Example 11 311 template("foo") { 312 action(target_name) { 313 ... 314 args = [ 315 # After rebase_path is called twice for bar, the bar value passed is incorrect. 316 "--bar=" + rebase_path(invoker.bar, root_build_dir), 317 ... 318 ] 319 ... 320 } 321 } 322 323 foo("bad") { 324 # Do not call rebase_path here. 325 bar = rebase_path(some_value, root_build_dir) 326 ... 327 } 328 ``` 329 330### Sharing Data Between Modules 331 332It is common to share data between modules. For example, module A wants to know the output and **deps** of module B. 333 334- Data sharing within the same **BUILD.gn** 335 336 Data in the same **BUILD.gn** can be shared by defining global variables. 337 338 In the following example, the output of module **a** is the input of module **b**, and can be shared with module **b** via global variables. 339 340 ```shell 341 # Example 12 342 _output_a = get_label_info(":a", "out_dir") + "/a.out" 343 action("a") { 344 outputs = _output_a 345 ... 346 } 347 action("b") { 348 inputs = [_output_a] 349 ... 350 } 351 ``` 352 353- Data sharing between different **BUILD.gn**s 354 355 The best way to share data between different **BUILD.gn** is to save the data as files and transfer the files between modules. You can refer to **write_meta_data** in the OpenHarmony HAP build process. 356 357### forward_variable_from 358 359- To customize a template, pass (**forward**) **testonly** first because the **testonly** target may depend on the template target. 360 361 ```shell 362 # Example 13 363 # For a customized template, pass testonly first. 364 template("foo") { 365 forward_variable_from(invoker, ["testonly"]) 366 ... 367 } 368 ``` 369 370- Do not use asterisks (*) to **forward** variables. Required variables must be explicitly forwarded one by one. 371 372 ```shell 373 # Example 14 374 # Bad. The asterisk (*) is used to forward the variable. 375 template("foo") { 376 forward_variable_from(invoker, "*") 377 ... 378 } 379 380 # Good. Variables are explicitly forwarded one by one. 381 template("bar") { 382 # 383 forward_variable_from(invoker, [ 384 "testonly", 385 "deps", 386 ... 387 ]) 388 ... 389 } 390 ``` 391 392### target_name 393 394The value of **target_name** varies with the scope. 395 396```shell 397# Example 15 398# The value of target_name varies with the scope. 399template("foo") { 400 # The displayed target_name is "${target_name}". 401 print(target_name) 402 _code_gen_target = "${target_name}__gen" 403 code_gen(_code_gen_target) { 404 # The displayed target_name is "${target_name}__gen". 405 print(target_name) 406 ... 407 } 408 _compile_gen_target = "${target_name}__compile" 409 compile(_compile_gen_target) { 410 # The displayed target_name is "${target_name}__compile". 411 print(target_name) 412 ... 413 } 414 ... 415} 416``` 417 418### public_configs 419 420To export header files from a module, use **public_configs**. 421 422```shell 423# Example 16 424# b depends on a and inherits from the headers of a. 425config("headers") { 426 include_dirs = ["//path-to-headers"] 427 ... 428} 429shared_library("a") { 430 public_configs = [":headers"] 431 ... 432} 433executable("b") { 434 deps = [":a"] 435 ... 436} 437``` 438 439### template 440 441A custom template must contain a subtarget named **target_name**. This subtarget is used as the target of the template and depends on other subtargets. Otherwise, the subtargets will not be compiled. 442 443```shell 444# Example 17 445# A custom template must have a subtarget named target_name. 446template("foo") { 447 _code_gen_target = "${target_name}__gen" 448 code_gen(_code_gen_target) { 449 ... 450 } 451 _compile_gen_target = "${target_name}__compile" 452 compile(_compile_gen_target) { 453 # The displayed target_name is "${target_name}__compile". 454 print(target_name) 455 ... 456 } 457 ... 458 group(target_name) { 459 deps = [ 460 # _compile_gen_target depends on _code_gen_target. Therefore, target_name only needs to depend on _compile_gen_target. 461 ":$_compile_gen_target" 462 ] 463 } 464} 465``` 466 467### set_source_assignment_filter 468 469In addition to **sources**, **set_source_assignment_filter** can be used to filter other variables. After the filtering is complete, clear the filter and **sources**. 470 471```shell 472# Example 18 473# Use set_source_assignment_filter to filter dependencies and add the dependencies with labels matching *:*_res to the dependency list. 474_deps = [] 475foreach(_possible_dep, invoker.deps) { 476 set_source_assignment_filter(["*:*_res"]) 477 _label = get_label_info(_possible_dep, "label_no_toolchain") 478 sources = [] 479 sources = [ _label ] 480 if (sources = []) { 481 _deps += _sources 482 } 483} 484sources = [] 485set_source_assignment_filter([]) 486``` 487 488In the latest version, **set_source_assignment_filter** is replaced by **filter_include** and **filter_exclude**. 489 490### Setting deps and external_deps 491 492An OpenHarmony component is a group of modules that can provide a capability. When defining a module, set **part_name** to specify the component to which the module belongs. 493 494You must also declare **inner_kits** of a component for other components to call. For details, see **bundle.json** in the source code. **inner_kits** applies only to dependent modules in different components. 495 496If modules **a** and **b** has the same **part_name**, modules **a** and **b** belong to the same component. In this case, declare the dependency between them using **deps**. 497 498If modules **a** and **b** have different **part_name**, modules **a** and **b** belong to different components. In this case, declare the dependency between them using **external_deps** in the *Component name:Module name* format. See Example 19. 499 500```shell 501# Example 19 502shared_library("a") { 503 ... 504 external_deps = ["part_name_of_b:b"] 505 ... 506} 507```