subsys-build-gn-coding-style-and-best-practice.md 14.4 KB
Newer Older
E
add doc  
ester.zhou 已提交
1 2 3 4
# Build System Coding Specifications and Best Practices

## Overview

A
Annie_wang 已提交
5
Generate 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.
E
add doc  
ester.zhou 已提交
6

A
Annie_wang 已提交
7
### GN
E
add doc  
ester.zhou 已提交
8

A
Annie_wang 已提交
9 10 11
- GN is used in large software systems such as Chromium, Fuchsia, and OpenHarmony.
- However, the GN syntax has limitations rooted in its [design philosophy](https://gn.googlesource.com/gn/+/main/docs/language.md#Design-philosophy). For example, it 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, visit https://gn.googlesource.com/gn/+/main/docs/.
E
add doc  
ester.zhou 已提交
12

A
Annie_wang 已提交
13
### Intended Audience and Purpose
E
add doc  
ester.zhou 已提交
14

A
Annie_wang 已提交
15
This document is intended for OpenHarmony developers. This document describes the GN coding style and practices. It does not cover the GN syntax. For details about the GN basics, see [GN Reference](https://gn.googlesource.com/gn/+/main/docs/reference.md).
E
add doc  
ester.zhou 已提交
16 17 18

### General Principles

A
Annie_wang 已提交
19
Scripts must be easy to read and maintain, and have good scalability and performance while functioning well.
E
add doc  
ester.zhou 已提交
20 21 22 23 24

## Coding Style

### Naming

A
Annie_wang 已提交
25
Follow the Linux kernel naming style, that is, lowercase letters + underscores (_).
E
add doc  
ester.zhou 已提交
26 27 28

#### Local Variables

A
Annie_wang 已提交
29
A local variable is a variable restricted to use in a certain scope and cannot be passed down.
E
add doc  
ester.zhou 已提交
30

A
Annie_wang 已提交
31
Different from global variables, local variables start with an underscore (_).
E
add doc  
ester.zhou 已提交
32 33

```
A
Annie_wang 已提交
34
# Example 1:
E
add doc  
ester.zhou 已提交
35 36
action("some_action") {
  ...
A
Annie_wang 已提交
37
  # _output is a local variable.
E
add doc  
ester.zhou 已提交
38 39 40 41
  _output = "${target_out_dir}/${target_name}.out"
  outputs = [ _output ]
  args = [
    ...
A
Annie_wang 已提交
42 43 44
      "--output",
      rebase_path(_output, root_build_dir),
      ...
E
add doc  
ester.zhou 已提交
45 46 47 48 49 50 51
  ]
  ...
}
```

#### Global Variables

A
Annie_wang 已提交
52
A global variable starts with a lowercase letter.
E
add doc  
ester.zhou 已提交
53

A
Annie_wang 已提交
54
Use **declare_args** to declare the variable value only if the variable value can be modified by **gn args**.
E
add doc  
ester.zhou 已提交
55 56 57 58

```
# Example 2
declare_args() {
A
Annie_wang 已提交
59
  # The value of some_feature can be changed by gn args.
E
add doc  
ester.zhou 已提交
60 61 62 63 64 65
  some_feature = false
}
```

#### Targets

A
Annie_wang 已提交
66
Name the targets in the lowercase letters + underscores (_) format.
E
add doc  
ester.zhou 已提交
67

A
Annie_wang 已提交
68
Name the subtargets in templates in the ${target_name}+double underscores (__)+suffix format. This naming convention has the following advantages:
E
add doc  
ester.zhou 已提交
69

A
Annie_wang 已提交
70
- ${target_name} prevents duplicate subtarget names.
E
add doc  
ester.zhou 已提交
71

A
Annie_wang 已提交
72 73
- The double underscores (__) help locate the module to which a subtarget belongs.
  
E
add doc  
ester.zhou 已提交
74 75 76
  ```
  # Example 3
  template("ohos_shared_library") {
A
Annie_wang 已提交
77
    # "{target_name}" (Target name) + "__" (double underscores) + "notice" (suffix)
E
add doc  
ester.zhou 已提交
78 79 80 81 82 83 84 85 86 87 88 89
    _notice_target = "${target_name}__notice"
    collect_notice(_notice_target) {
      ...
    }
    shared_library(target_name) {
      ...
    }
  }
  ```

#### Custom Templates

A
Annie_wang 已提交
90
Name templates in the verb+object format.
E
add doc  
ester.zhou 已提交
91 92 93 94 95 96 97 98 99 100 101

```
# Example 4
# Good
template("compile_resources") {
  ...
}
```

### Formatting

A
Annie_wang 已提交
102
GN 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: 
E
add doc  
ester.zhou 已提交
103 104 105 106 107

```shell
$ gn format path-to-BUILD.gn
```

A
Annie_wang 已提交
108
**gn format** sorts the imported files in alphabetical order. To maintain the original sequence, add an empty comment line.
E
add doc  
ester.zhou 已提交
109

A
Annie_wang 已提交
110
For example, the original import sequence is as follows:
E
add doc  
ester.zhou 已提交
111 112 113 114 115 116 117

```
# Example 5
import("//b.gni")
import("//a.gni")
```

A
Annie_wang 已提交
118
**gn format** sorts the files as follows:
E
add doc  
ester.zhou 已提交
119 120 121 122 123 124

```
import("//a.gni")
import("//b.gni")
```

A
Annie_wang 已提交
125
To maintain the original sequence, add an empty comment line.
E
add doc  
ester.zhou 已提交
126 127 128

```
import("//b.gni")
A
Annie_wang 已提交
129
# Comment to keep the original sequence
E
add doc  
ester.zhou 已提交
130 131 132
import("//a.gni")
```

A
Annie_wang 已提交
133
## Coding Practices
E
add doc  
ester.zhou 已提交
134

A
Annie_wang 已提交
135
### Guidelines
E
add doc  
ester.zhou 已提交
136

A
Annie_wang 已提交
137
The build script completes the following tasks:
E
add doc  
ester.zhou 已提交
138

A
Annie_wang 已提交
139 140 141
1. Describes the dependency (deps) between modules.
   
   In practice, the most common problem is lack of dependency.
E
add doc  
ester.zhou 已提交
142

A
Annie_wang 已提交
143 144 145
2. Defines the module build rules (rule).
   
   In practice, unclear input and output are common problems.
E
add doc  
ester.zhou 已提交
146

A
Annie_wang 已提交
147
Lack of dependency leads to the following problems:
E
add doc  
ester.zhou 已提交
148

A
Annie_wang 已提交
149 150
- Unexpected compilation error
  
E
add doc  
ester.zhou 已提交
151 152
  ```
  # Example 6
A
Annie_wang 已提交
153
  # Lack of dependency poses a possibility of compilation errors.
E
add doc  
ester.zhou 已提交
154 155 156 157 158 159 160 161 162 163 164 165 166
  shared_library("a") {
    ...
  }
  shared_library("b") {
    ...
    ldflags = [ "-la" ]
    deps = []
    ...
  }
  group("images") {
    deps = [ ":b" ]
  }
  ```
A
Annie_wang 已提交
167 168 169 170
  
  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.
  
  If **liba.so** is available, the compilation is successful. Therefore, lack of dependency poses a possibility of compilation errors.
E
add doc  
ester.zhou 已提交
171

A
Annie_wang 已提交
172 173 174
- Missing compilation of modules
  
  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.
E
add doc  
ester.zhou 已提交
175

A
Annie_wang 已提交
176
Another problem is unnecessary dependencies. Unnecessary dependencies reduce concurrency and slow down compilation. The following is an example:
E
add doc  
ester.zhou 已提交
177

A
Annie_wang 已提交
178
**_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.
E
add doc  
ester.zhou 已提交
179 180

```
A
Annie_wang 已提交
181 182
# Example 7:
# Unnecessary dependencies slow down compilation.
E
add doc  
ester.zhou 已提交
183 184 185 186 187 188
template("too_much_deps") {
  ...
  _gen_resource_target = "${target_name}__res"
  action(_gen_resource_target) {
    ...
  }
A
Annie_wang 已提交
189

190
  _compile_resource_target = "${target_name}__compile_res"
E
add doc  
ester.zhou 已提交
191 192 193 194
  action(_compile_resource_target) {
    deps = [":$_gen_resource_target"]
    ...
  }
A
Annie_wang 已提交
195

E
add doc  
ester.zhou 已提交
196 197 198 199 200 201 202 203
  _compile_js_target = "${target_name}__js"
  action(_compile_js_target) {
    # This deps is not required.
    deps = [":$_compile_resource_target"]
  }
}
```

A
Annie_wang 已提交
204
Unclear input leads to the following problems:
E
add doc  
ester.zhou 已提交
205

A
Annie_wang 已提交
206 207
- Modified code is not compiled during incremental compilation.
- The cache being used is still hit after the code is changed.
E
add doc  
ester.zhou 已提交
208

A
Annie_wang 已提交
209
In 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.
E
add doc  
ester.zhou 已提交
210 211

```
A
Annie_wang 已提交
212
# Example 8:
E
add doc  
ester.zhou 已提交
213 214 215 216 217 218 219 220
action("implict_input_action") {
  script = "//path-to-foo.py"
  ...
}
```

```
#!/usr/bin/env
A
Annie_wang 已提交
221
# Content of foo.py
E
add doc  
ester.zhou 已提交
222 223 224 225 226 227
import bar
...
bar.some_function()
...
```

A
Annie_wang 已提交
228
Unclear output leads to the following problems:
E
add doc  
ester.zhou 已提交
229

A
Annie_wang 已提交
230 231
- Implicit output
- A failure to obtain the implicit output from the cache
E
add doc  
ester.zhou 已提交
232

A
Annie_wang 已提交
233
In 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.
E
add doc  
ester.zhou 已提交
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254

```
# Example 9
action("implict_output_action") {
  outputs = ["${target_out_dir}/a.out"]
  script = "//path-to-foo.py"
  ...
}
```

```
#!/usr/bin/env
# Contents of foo.py
...
write_file("b.out")
write_file("a.out")
...
```

### Templates

A
Annie_wang 已提交
255
**Do not use GN native templates. Use the templates provided by the build system.**
E
add doc  
ester.zhou 已提交
256

A
Annie_wang 已提交
257
The GN native templates include **source_set**, **shared_library**, **static_library**, **action**, **executable** and **group**.
E
add doc  
ester.zhou 已提交
258

A
Annie_wang 已提交
259
The native templates are not recommended due to the following reasons:
E
add doc  
ester.zhou 已提交
260

A
Annie_wang 已提交
261
- The native templates provide only the minimal build configuration. They cannot provide functions, such as parsing **external_deps**, collecting notice, and generating installation information.
E
add doc  
ester.zhou 已提交
262

A
Annie_wang 已提交
263 264 265 266 267 268 269 270 271 272 273 274
- The native **action** template cannot automatically detect the changes in the dependencies of the input file, and cannot start recompilation. See Example 8.

  The table below lists the mapping between the GN native templates and templates provided by the compilation system.

| Template Provided by the Compilation System          | GN Native Template          |
|:------------------- | -------------- |
| ohos_shared_library | shared_library |
| ohos_source_set     | source_set     |
| ohos_executable     | executable     |
| ohos_static_library | static_library |
| action_with_pydeps  | action         |
| ohos_group          | group          |
E
add doc  
ester.zhou 已提交
275 276 277

### Using Python Scripts

A
Annie_wang 已提交
278
You are advised to use Python scripts instead of shell scripts in **action**. Compared with shell scripts, Python scripts feature:
E
add doc  
ester.zhou 已提交
279

A
Annie_wang 已提交
280 281 282 283
- More user-friendly syntax, which eliminates errors caused by lack of a space
- Easier to read
- Easier to maintain and debug
- Faster compilation due to caching of Python tasks
E
add doc  
ester.zhou 已提交
284 285 286

### rebase_path

A
Annie_wang 已提交
287 288
- Call **rebase_path** only in **args** of **action**.
  
E
add doc  
ester.zhou 已提交
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
  ```
  # Example 10
  template("foo") {
    action(target_name) {
      ...
      args = [
        # Call rebase_path only in args.
        "--bar=" + rebase_path(invoker.bar, root_build_dir),
        ...
      ]
      ...
    }
  }
  
  foo("good") {
    bar = something
    ...
  }
  ```

A
Annie_wang 已提交
309 310
- If rebase_path is called twice for the same variable, unexpected results occur.
  
E
add doc  
ester.zhou 已提交
311 312 313 314 315 316
  ```
  # Example 11
  template("foo") {
    action(target_name) {
      ...
      args = [
A
Annie_wang 已提交
317
        # After rebase_path is called twice for bar, the bar value passed is incorrect.
E
add doc  
ester.zhou 已提交
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
        "--bar=" + rebase_path(invoker.bar, root_build_dir),
        ...
      ]
      ...
    }
  }
  
  foo("bad") {
    # Do not call rebase_path here.
    bar = rebase_path(some_value, root_build_dir)
    ...
  }
  ```

### Sharing Data Between Modules

A
Annie_wang 已提交
334
It is common to share data between modules. For example, module A wants to know the output and **deps** of module B.
E
add doc  
ester.zhou 已提交
335

A
Annie_wang 已提交
336 337 338 339 340 341
- Data sharing within the same **BUILD.gn**
  
  Data in the same **BUILD.gn** can be shared by defining global variables.
  
  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.
  
E
add doc  
ester.zhou 已提交
342 343 344 345 346 347 348 349 350 351 352 353 354
  ```
  # Example 12
  _output_a = get_label_info(":a", "out_dir") + "/a.out"
  action("a") {
    outputs = _output_a
    ...
  }
  action("b") {
    inputs = [_output_a]
    ...
  }
  ```

A
Annie_wang 已提交
355 356 357
- Data sharing between different **BUILD.gn**s
  
  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.
E
add doc  
ester.zhou 已提交
358 359 360

### forward_variable_from

A
Annie_wang 已提交
361 362
- To customize a template, pass (**forward**) **testonly** first because the **testonly** target may depend on the template target.
  
E
add doc  
ester.zhou 已提交
363 364
  ```
  # Example 13
A
Annie_wang 已提交
365
  # For a customized template, pass testonly first.
E
add doc  
ester.zhou 已提交
366 367 368 369 370 371
  template("foo") {
    forward_variable_from(invoker, ["testonly"])
    ...
  }
  ```

A
Annie_wang 已提交
372 373
- Do not use asterisks (*) to **forward** variables. Required variables must be explicitly forwarded one by one.
  
E
add doc  
ester.zhou 已提交
374 375 376 377 378 379 380 381
  ```
  # Example 14
  # Bad. The asterisk (*) is used to forward the variable.
  template("foo") {
    forward_variable_from(invoker, "*")
    ...
  }
  
A
Annie_wang 已提交
382
  # Good. Variables are explicitly forwarded one by one.
E
add doc  
ester.zhou 已提交
383 384 385
  template("bar") {
    # 
    forward_variable_from(invoker, [
A
Annie_wang 已提交
386 387 388 389
                                       "testonly",
                                       "deps",
                                       ...
                                     ])
E
add doc  
ester.zhou 已提交
390 391 392 393 394 395
    ...
  }
  ```

### target_name

A
Annie_wang 已提交
396
The value of **target_name** varies with the scope.
E
add doc  
ester.zhou 已提交
397 398 399

```
# Example 15
A
Annie_wang 已提交
400
# The value of target_name varies with the scope.
E
add doc  
ester.zhou 已提交
401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425
template("foo") {
  # The displayed target_name is "${target_name}".
  print(target_name)
  _code_gen_target = "${target_name}__gen"
  code_gen(_code_gen_target) {
    # The displayed target_name is "${target_name}__gen".
    print(target_name)
    ...
  }
  _compile_gen_target = "${target_name}__compile"
  compile(_compile_gen_target) {
    # The displayed target_name is "${target_name}__compile".
    print(target_name)
    ...
  }
  ...
}
```

### public_configs

To export header files from a module, use **public_configs**.

```
# Example 16
A
Annie_wang 已提交
426
# b depends on a and inherits from the headers of a.
E
add doc  
ester.zhou 已提交
427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442
config("headers") {
  include_dirs = ["//path-to-headers"]
  ...
}
shared_library("a") {
  public_configs = [":headers"]
  ...
}
executable("b") {
  deps = [":a"]
  ...
}
```

### template

A
Annie_wang 已提交
443
A 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.
E
add doc  
ester.zhou 已提交
444 445 446

```
# Example 17
A
Annie_wang 已提交
447
# A custom template must have a subtarget named target_name.
E
add doc  
ester.zhou 已提交
448 449 450 451 452 453 454 455 456 457 458 459 460 461
template("foo") {
  _code_gen_target = "${target_name}__gen"
  code_gen(_code_gen_target) {
    ...
  }
  _compile_gen_target = "${target_name}__compile"
  compile(_compile_gen_target) {
    # The displayed target_name is "${target_name}__compile".
    print(target_name)
    ...
  }
  ...
  group(target_name) {
    deps = [
A
Annie_wang 已提交
462
    # _compile_gen_target depends on _code_gen_target. Therefore, target_name only needs to depend on _compile_gen_target.
E
add doc  
ester.zhou 已提交
463 464 465 466 467 468 469 470
      ":$_compile_gen_target"
    ]
  }
}
```

### set_source_assignment_filter

A
Annie_wang 已提交
471
In addition to **sources**, **set_source_assignment_filter** can be used to filter other variables. After the filtering is complete, clear the filter and **sources**.
E
add doc  
ester.zhou 已提交
472 473 474

```
# Example 18
A
Annie_wang 已提交
475
# Use set_source_assignment_filter to filter dependencies and add the dependencies with labels matching *:*_res to the dependency list.
E
add doc  
ester.zhou 已提交
476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491
_deps = []
foreach(_possible_dep, invoker.deps) {
  set_source_assignment_filter(["*:*_res"])
  _label = get_label_info(_possible_dep, "label_no_toolchain")
  sources = []
  sources = [ _label ]
  if (sources = []) {
    _deps += _sources
  }
}
sources = []
set_source_assignment_filter([])
```

In the latest version, **set_source_assignment_filter** is replaced by **filter_include** and **filter_exclude**.

A
Annie_wang 已提交
492 493 494
### Using deps and external_deps

- An OpenHarmony component is a group of modules that can provide a capability.
E
add doc  
ester.zhou 已提交
495

A
Annie_wang 已提交
496
- When defining a module, you must specify **part_name** to indicate the component to which the module belongs.
E
add doc  
ester.zhou 已提交
497

A
Annie_wang 已提交
498
- You must also declare **inner-kit** of a component for other components to call. For details about the declaration of component **innerkit**, see **bundle.json** in the source code.
E
add doc  
ester.zhou 已提交
499

A
Annie_wang 已提交
500
- **inner-kit** applies only to dependent modules in different components.
E
add doc  
ester.zhou 已提交
501

A
Annie_wang 已提交
502
- If 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**.
E
add doc  
ester.zhou 已提交
503

A
Annie_wang 已提交
504 505
- If 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.
  
E
add doc  
ester.zhou 已提交
506 507 508 509 510 511 512 513
  ```
  # Example 19
  shared_library("a") {
    ...
    external_deps = ["part_name_of_b:b"]
    ...
  }
  ```