• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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```