Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes15kdownloads
UserDocumentation.md966 linesDownload Raw Back to docs
1# User Documentation
2
3## Introduction
4
5This document is intended to provide a user-level guide to GYP.  The
6emphasis here is on how to use GYP to accomplish specific tasks, not on
7the complete technical language specification.  (For that, see the
8[LanguageSpecification](LanguageSpecification.md).)
9
10The document below starts with some overviews to provide context: an
11overview of the structure of a `.gyp` file itself, an overview of a
12typical executable-program target in a `.gyp` file, an an overview of a
13typical library target in a `.gyp` file.
14
15After the overviews, there are examples of `gyp` patterns for different
16common use cases.
17
18## Skeleton of a typical Chromium .gyp file
19
20Here is the skeleton of a typical `.gyp` file in the Chromium tree:
21
22```
23  {
24    'variables': {
25      .
26      .
27      .
28    },
29    'includes': [
30      '../build/common.gypi',
31    ],
32    'target_defaults': {
33      .
34      .
35      .
36    },
37    'targets': [
38      {
39        'target_name': 'target_1',
40          .
41          .
42          .
43      },
44      {
45        'target_name': 'target_2',
46          .
47          .
48          .
49      },
50    ],
51    'conditions': [
52      ['OS=="linux"', {
53        'targets': [
54          {
55            'target_name': 'linux_target_3',
56              .
57              .
58              .
59          },
60        ],
61      }],
62      ['OS=="win"', {
63        'targets': [
64          {
65            'target_name': 'windows_target_4',
66              .
67              .
68              .
69          },
70        ],
71      }, { # OS != "win"
72        'targets': [
73          {
74            'target_name': 'non_windows_target_5',
75              .
76              .
77              .
78          },
79      }],
80    ],
81  }
82```
83
84The entire file just contains a Python dictionary.  (It's actually JSON,
85with two small Pythonic deviations: comments are introduced with `#`,
86and a `,` (comma)) is legal after the last element in a list or
87dictionary.)
88
89The top-level pieces in the `.gyp` file are as follows:
90
91`'variables'`:  Definitions of variables that can be interpolated and
92used in various other parts of the file.
93
94`'includes'`:  A list of of other files that will be included in this
95file.  By convention, included files have the suffix `.gypi` (gyp
96include).
97
98`'target_defaults'`:  Settings that will apply to _all_ of the targets
99defined in this `.gyp` file.
100
101`'targets'`:  The list of targets for which this `.gyp` file can
102generate builds.  Each target is a dictionary that contains settings
103describing all the information necessary to build the target.
104
105`'conditions'`:  A list of condition specifications that can modify the
106contents of the items in the global dictionary defined by this `.gyp`
107file based on the values of different variables.  As implied by the
108above example, the most common use of a `conditions` section in the
109top-level dictionary is to add platform-specific targets to the
110`targets` list.
111
112## Skeleton of a typical executable target in a .gyp file
113
114The most straightforward target is probably a simple executable program.
115Here is an example `executable` target that demonstrates the features
116that should cover most simple uses of gyp:
117
118```
119  {
120    'targets': [
121      {
122        'target_name': 'foo',
123        'type': 'executable',
124        'msvs_guid': '5ECEC9E5-8F23-47B6-93E0-C3B328B3BE65',
125        'dependencies': [
126          'xyzzy',
127          '../bar/bar.gyp:bar',
128        ],
129        'defines': [
130          'DEFINE_FOO',
131          'DEFINE_A_VALUE=value',
132        ],
133        'include_dirs': [
134          '..',
135        ],
136        'sources': [
137          'file1.cc',
138          'file2.cc',
139        ],
140        'conditions': [
141          ['OS=="linux"', {
142            'defines': [
143              'LINUX_DEFINE',
144            ],
145            'include_dirs': [
146              'include/linux',
147            ],
148          }],
149          ['OS=="win"', {
150            'defines': [
151              'WINDOWS_SPECIFIC_DEFINE',
152            ],
153          }, { # OS != "win",
154            'defines': [
155              'NON_WINDOWS_DEFINE',
156            ],
157          }]
158        ],
159      },
160    ],
161  }
162```
163
164The top-level settings in the target include:
165
166`'target_name'`: The name by which the target should be known, which
167should be unique across all `.gyp` files.  This name will be used as the
168project name in the generated Visual Studio solution, as the target name
169in the generated XCode configuration, and as the alias for building this
170target from the command line of the generated SCons configuration.
171
172`'type'`: Set to `executable`, logically enough.
173
174`'msvs_guid'`: THIS IS ONLY TRANSITIONAL.  This is a hard-coded GUID
175values that will be used in the generated Visual Studio solution
176file(s).  This allows us to check in a `chrome.sln` file that
177interoperates with gyp-generated project files.  Once everything in
178Chromium is being generated by gyp, it will no longer be important that
179the GUIDs stay constant across invocations, and we'll likely get rid of
180these settings,
181
182`'dependencies'`: This lists other targets that this target depends on.
183The gyp-generated files will guarantee that the other targets are built
184before this target.  Any library targets in the `dependencies` list will
185be linked with this target.  The various settings (`defines`,
186`include_dirs`, etc.) listed in the `direct_dependent_settings` sections
187of the targets in this list will be applied to how _this_ target is
188built and linked.  See the more complete discussion of
189`direct_dependent_settings`, below.
190
191`'defines'`: The C preprocessor definitions that will be passed in on
192compilation command lines (using `-D` or `/D` options).
193
194`'include_dirs'`: The directories in which included header files live.
195These will be passed in on compilation command lines (using `-I` or `/I`
196options).
197
198`'sources'`: The source files for this target.
199
200`'conditions'`: A block of conditions that will be evaluated to update
201the different settings in the target dictionary.
202
203## Skeleton of a typical library target in a .gyp file
204
205The vast majority of targets are libraries.  Here is an example of a
206library target including the additional features that should cover most
207needs of libraries:
208
209```
210  {
211    'targets': [
212      {
213        'target_name': 'foo',
214        'type': '<(library)'
215        'msvs_guid': '5ECEC9E5-8F23-47B6-93E0-C3B328B3BE65',
216        'dependencies': [
217          'xyzzy',
218          '../bar/bar.gyp:bar',
219        ],
220        'defines': [
221          'DEFINE_FOO',
222          'DEFINE_A_VALUE=value',
223        ],
224        'include_dirs': [
225          '..',
226        ],
227        'direct_dependent_settings': {
228          'defines': [
229            'DEFINE_FOO',
230            'DEFINE_ADDITIONAL',
231          ],
232          'linkflags': [
233          ],
234        },
235        'export_dependent_settings': [
236          '../bar/bar.gyp:bar',
237        ],
238        'sources': [
239          'file1.cc',
240          'file2.cc',
241        ],
242        'conditions': [
243          ['OS=="linux"', {
244            'defines': [
245              'LINUX_DEFINE',
246            ],
247            'include_dirs': [
248              'include/linux',
249            ],
250          ],
251          ['OS=="win"', {
252            'defines': [
253              'WINDOWS_SPECIFIC_DEFINE',
254            ],
255          }, { # OS != "win",
256            'defines': [
257              'NON_WINDOWS_DEFINE',
258            ],
259          }]
260        ],
261    ],
262  }
263```
264
265The possible entries in a library target are largely the same as those
266that can be specified for an executable target (`defines`,
267`include_dirs`, etc.).  The differences include:
268
269`'type'`: This should almost always be set to '<(library)', which allows
270the user to define at gyp time whether libraries are to be built static
271or shared.  (On Linux, at least, linking with shared libraries saves
272significant link time.) If it's necessary to pin down the type of
273library to be built, the `type` can be set explicitly to
274`static_library` or `shared_library`.
275
276`'direct_dependent_settings'`: This defines the settings that will be
277applied to other targets that _directly depend_ on this target--that is,
278that list _this_ target in their `'dependencies'` setting.  This is
279where you list the `defines`, `include_dirs`, `cflags` and `linkflags`
280that other targets that compile or link against this target need to
281build consistently.
282
283`'export_dependent_settings'`: This lists the targets whose
284`direct_dependent_settings` should be "passed on" to other targets that
285use (depend on) this target.  `TODO:  expand on this description.`
286
287## Use Cases
288
289These use cases are intended to cover the most common actions performed
290by developers using GYP.
291
292Note that these examples are _not_ fully-functioning, self-contained
293examples (or else they'd be way too long).  Each example mostly contains
294just the keywords and settings relevant to the example, with perhaps a
295few extra keywords for context.  The intent is to try to show the
296specific pieces you need to pay attention to when doing something.
297[NOTE:  if practical use shows that these examples are confusing without
298additional context, please add what's necessary to clarify things.]
299
300### Add new source files
301
302There are similar but slightly different patterns for adding a
303platform-independent source file vs. adding a source file that only
304builds on some of the supported platforms.
305
306#### Add a source file that builds on all platforms
307
308**Simplest possible case**: You are adding a file(s) that builds on all
309platforms.
310
311Just add the file(s) to the `sources` list of the appropriate dictionary
312in the `targets` list:
313
314```
315  {
316    'targets': [
317      {
318        'target_name': 'my_target',
319        'type': 'executable',
320        'sources': [
321          '../other/file_1.cc',
322          'new_file.cc',
323          'subdir/file3.cc',
324        ],
325      },
326    ],
327  },
328```
329
330File path names are relative to the directory in which the `.gyp` file lives.
331
332Keep the list sorted alphabetically (unless there's a really, really,
333_really_ good reason not to).
334
335#### Add a platform-specific source file
336
337##### Your platform-specific file is named `*_linux.{ext}`, `*_mac.{ext}`, `*_posix.{ext}` or `*_win.{ext}`
338
339The simplest way to add a platform-specific source file, assuming you're
340adding a completely new file and get to name it, is to use one of the
341following standard suffixes:
342
343  * `_linux`  (e.g. `foo_linux.cc`)
344  * `_mac`    (e.g. `foo_mac.cc`)
345  * `_posix`  (e.g. `foo_posix.cc`)
346  * `_win`    (e.g. `foo_win.cc`)
347
348Simply add the file to the `sources` list of the appropriate dict within
349the `targets` list, like you would any other source file.
350
351```
352  {
353    'targets': [
354      {
355        'target_name': 'foo',
356        'type': 'executable',
357        'sources': [
358          'independent.cc',
359          'specific_win.cc',
360        ],
361      },
362    ],
363  },
364```
365
366The Chromium `.gyp` files all have appropriate `conditions` entries to
367filter out the files that aren't appropriate for the current platform.
368In the above example, the `specific_win.cc` file will be removed
369automatically from the source-list on non-Windows builds.
370
371##### Your platform-specific file does not use an already-defined pattern
372
373If your platform-specific file does not contain a
374`*_{linux,mac,posix,win}` substring (or some other pattern that's
375already in the `conditions` for the target), and you can't change the
376file name, there are two patterns that can be used.
377
378**Preferred**:  Add the file to the `sources` list of the appropriate
379dictionary within the `targets` list.  Add an appropriate `conditions`
380section to exclude the specific files name:
381
382```
383  {
384    'targets': [
385      {
386        'target_name': 'foo',
387        'type': 'executable',
388        'sources': [
389          'linux_specific.cc',
390        ],
391        'conditions': [
392          ['OS != "linux"', {
393            'sources!': [
394              # Linux-only; exclude on other platforms.
395              'linux_specific.cc',
396            ]
397          }[,
398        ],
399      },
400    ],
401  },
402```
403
404Despite the duplicate listing, the above is generally preferred because
405the `sources` list contains a useful global list of all sources on all
406platforms with consistent sorting on all platforms.
407
408**Non-preferred**: In some situations, however, it might make sense to
409list a platform-specific file only in a `conditions` section that
410specifically _includes_ it in the `sources` list:
411
412```
413  {
414    'targets': [
415      {
416        'target_name': 'foo',
417        'type': 'executable',
418        'sources': [],
419        ['OS == "linux"', {
420          'sources': [
421            # Only add to sources list on Linux.
422            'linux_specific.cc',
423          ]
424        }],
425      },
426    ],
427  },
428```
429
430The above two examples end up generating equivalent builds, with the
431small exception that the `sources` lists will list the files in
432different orders.  (The first example defines explicitly where
433`linux_specific.cc` appears in the list--perhaps in in the
434middle--whereas the second example will always tack it on to the end of
435the list.)
436
437**Including or excluding files using patterns**: There are more
438complicated ways to construct a `sources` list based on patterns.  See
439`TODO` below.
440
441### Add a new executable
442
443An executable program is probably the most straightforward type of
444target, since all it typically needs is a list of source files, some
445compiler/linker settings (probably varied by platform), and some library
446targets on which it depends and which must be used in the final link.
447
448#### Add an executable that builds on all platforms
449
450Add a dictionary defining the new executable target to the `targets`
451list in the appropriate `.gyp` file.  Example:
452
453```
454  {
455    'targets': [
456      {
457        'target_name': 'new_unit_tests',
458        'type': 'executable',
459        'defines': [
460          'FOO',
461        ],
462        'include_dirs': [
463          '..',
464        ],
465        'dependencies': [
466          'other_target_in_this_file',
467          'other_gyp2:target_in_other_gyp2',
468        ],
469        'sources': [
470          'new_additional_source.cc',
471          'new_unit_tests.cc',
472        ],
473      },
474    ],
475  }
476```
477
478#### Add a platform-specific executable
479
480Add a dictionary defining the new executable target to the `targets`
481list within an appropriate `conditions` block for the platform.  The
482`conditions` block should be a sibling to the top-level `targets` list:
483
484```
485  {
486    'targets': [
487    ],
488    'conditions': [
489      ['OS=="win"', {
490        'targets': [
491          {
492            'target_name': 'new_unit_tests',
493            'type': 'executable',
494            'defines': [
495              'FOO',
496            ],
497            'include_dirs': [
498              '..',
499            ],
500            'dependencies': [
501              'other_target_in_this_file',
502              'other_gyp2:target_in_other_gyp2',
503            ],
504            'sources': [
505              'new_additional_source.cc',
506              'new_unit_tests.cc',
507            ],
508          },
509        ],
510      }],
511    ],
512  }
513```
514
515### Add settings to a target
516
517There are several different types of settings that can be defined for
518any given target.
519
520#### Add new preprocessor definitions (`-D` or `/D` flags)
521
522New preprocessor definitions are added by the `defines` setting:
523
524```
525  {
526    'targets': [
527      {
528        'target_name': 'existing_target',
529        'defines': [
530          'FOO',
531          'BAR=some_value',
532        ],
533      },
534    ],
535  },
536```
537
538These may be specified directly in a target's settings, as in the above
539example, or in a `conditions` section.
540
541#### Add a new include directory (`-I` or `/I` flags)
542
543New include directories are added by the `include_dirs` setting:
544
545```
546  {
547    'targets': [
548      {
549        'target_name': 'existing_target',
550        'include_dirs': [
551          '..',
552          'include',
553        ],
554      },
555    ],
556  },
557```
558
559These may be specified directly in a target's settings, as in the above
560example, or in a `conditions` section.
561
562#### Add new compiler flags
563
564Specific compiler flags can be added with the `cflags` setting:
565
566```
567  {
568    'targets': [
569      {
570        'target_name': 'existing_target',
571        'conditions': [
572          ['OS=="win"', {
573            'cflags': [
574              '/WX',
575            ],
576          }, { # OS != "win"
577            'cflags': [
578              '-Werror',
579            ],
580          }],
581        ],
582      },
583    ],
584  },
585```
586
587Because these flags will be specific to the actual compiler involved,
588they will almost always be only set within a `conditions` section.
589
590#### Add new linker flags
591
592Setting linker flags is OS-specific. On linux and most non-mac posix
593systems, they can be added with the `ldflags` setting:
594
595```
596  {
597    'targets': [
598      {
599        'target_name': 'existing_target',
600        'conditions': [
601          ['OS=="linux"', {
602            'ldflags': [
603              '-pthread',
604            ],
605          }],
606        ],
607      },
608    ],
609  },
610```
611
612Because these flags will be specific to the actual linker involved,
613they will almost always be only set within a `conditions` section.
614
615On OS X, linker settings are set via `xcode_settings`, on Windows via
616`msvs_settings`.
617
618#### Exclude settings on a platform
619
620Any given settings keyword (`defines`, `include_dirs`, etc.) has a
621corresponding form with a trailing `!` (exclamation point) to remove
622values from a setting.  One useful example of this is to remove the
623Linux `-Werror` flag from the global settings defined in
624`build/common.gypi`:
625
626```
627  {
628    'targets': [
629      {
630        'target_name': 'third_party_target',
631        'conditions': [
632          ['OS=="linux"', {
633            'cflags!': [
634              '-Werror',
635            ],
636          }],
637        ],
638      },
639    ],
640  },
641```
642
643### Cross-compiling
644
645GYP has some (relatively limited) support for cross-compiling.
646
647If the variable `GYP_CROSSCOMPILE` or one of the toolchain-related
648variables (like `CC_host` or `CC_target`) is set, GYP will think that
649you wish to do a cross-compile.
650
651When cross-compiling, each target can be part of a "host" build, a
652"target" build, or both. By default, the target is assumed to be (only)
653part of the "target" build. The 'toolsets' property can be set on a
654target to change the default.
655
656A target's dependencies are assumed to match the build type (so, if A
657depends on B, by default that means that a target build of A depends on
658a target build of B). You can explicitly depend on targets across
659toolchains by specifying "#host" or "#target" in the dependencies list.
660If GYP is not doing a cross-compile, the "#host" and "#target" will be
661stripped as needed, so nothing breaks.
662
663### Add a new library
664
665TODO:  write intro
666
667#### Add a library that builds on all platforms
668
669Add the a dictionary defining the new library target to the `targets`
670list in the appropriate `.gyp` file.  Example:
671
672```
673  {
674    'targets': [
675      {
676        'target_name': 'new_library',
677        'type': '<(library)',
678        'defines': [
679          'FOO',
680          'BAR=some_value',
681        ],
682        'include_dirs': [
683          '..',
684        ],
685        'dependencies': [
686          'other_target_in_this_file',
687          'other_gyp2:target_in_other_gyp2',
688        ],
689        'direct_dependent_settings': {
690          'include_dirs': '.',
691        },
692        'export_dependent_settings': [
693          'other_target_in_this_file',
694        ],
695        'sources': [
696          'new_additional_source.cc',
697          'new_library.cc',
698        ],
699      },
700    ],
701  }
702```
703
704The use of the `<(library)` variable above should be the default `type`
705setting for most library targets, as it allows the developer to choose,
706at `gyp` time, whether to build with static or shared libraries.
707(Building with shared libraries saves a _lot_ of link time on Linux.)
708
709It may be necessary to build a specific library as a fixed type.  Is so,
710the `type` field can be hard-wired appropriately.  For a static library:
711
712```
713        'type': 'static_library',
714```
715
716For a shared library:
717
718```
719        'type': 'shared_library',
720```
721
722#### Add a platform-specific library
723
724Add a dictionary defining the new library target to the `targets` list
725within a `conditions` block that's a sibling to the top-level `targets`
726list:
727
728```
729  {
730    'targets': [
731    ],
732    'conditions': [
733      ['OS=="win"', {
734        'targets': [
735          {
736            'target_name': 'new_library',
737            'type': '<(library)',
738            'defines': [
739              'FOO',
740              'BAR=some_value',
741            ],
742            'include_dirs': [
743              '..',
744            ],
745            'dependencies': [
746              'other_target_in_this_file',
747              'other_gyp2:target_in_other_gyp2',
748            ],
749            'direct_dependent_settings': {
750              'include_dirs': '.',
751            },
752            'export_dependent_settings': [
753              'other_target_in_this_file',
754            ],
755            'sources': [
756              'new_additional_source.cc',
757              'new_library.cc',
758            ],
759          },
760        ],
761      }],
762    ],
763  }
764```
765
766### Dependencies between targets
767
768GYP provides useful primitives for establishing dependencies between
769targets, which need to be configured in the following situations.
770
771#### Linking with another library target
772
773```
774  {
775    'targets': [
776      {
777        'target_name': 'foo',
778        'dependencies': [
779          'libbar',
780        ],
781      },
782      {
783        'target_name': 'libbar',
784        'type': '<(library)',
785        'sources': [
786        ],
787      },
788    ],
789  }
790```
791
792Note that if the library target is in a different `.gyp` file, you have
793to specify the path to other `.gyp` file, relative to this `.gyp` file's
794directory:
795
796```
797  {
798    'targets': [
799      {
800        'target_name': 'foo',
801        'dependencies': [
802          '../bar/bar.gyp:libbar',
803        ],
804      },
805    ],
806  }
807```
808
809Adding a library often involves updating multiple `.gyp` files, adding
810the target to the appropriate `.gyp` file (possibly a newly-added `.gyp`
811file), and updating targets in the other `.gyp` files that depend on
812(link with) the new library.
813
814#### Compiling with necessary flags for a library target dependency
815
816We need to build a library (often a third-party library) with specific
817preprocessor definitions or command-line flags, and need to ensure that
818targets that depend on the library build with the same settings.  This
819situation is handled by a `direct_dependent_settings` block:
820
821```
822  {
823    'targets': [
824      {
825        'target_name': 'foo',
826        'type': 'executable',
827        'dependencies': [
828          'libbar',
829        ],
830      },
831      {
832        'target_name': 'libbar',
833        'type': '<(library)',
834        'defines': [
835          'LOCAL_DEFINE_FOR_LIBBAR',
836          'DEFINE_TO_USE_LIBBAR',
837        ],
838        'include_dirs': [
839          '..',
840          'include/libbar',
841        ],
842        'direct_dependent_settings': {
843          'defines': [
844            'DEFINE_TO_USE_LIBBAR',
845          ],
846          'include_dirs': [
847            'include/libbar',
848          ],
849        },
850      },
851    ],
852  }
853```
854
855In the above example, the sources of the `foo` executable will be
856compiled with the options `-DDEFINE_TO_USE_LIBBAR -Iinclude/libbar`,
857because of those settings' being listed in the
858`direct_dependent_settings` block.
859
860Note that these settings will likely need to be replicated in the
861settings for the library target itself, so that the library will build
862with the same options.  This does not prevent the target from defining
863additional options for its "internal" use when compiling its own source
864files.  (In the above example, these are the `LOCAL_DEFINE_FOR_LIBBAR`
865define, and the `..` entry in the `include_dirs` list.)
866
867#### When a library depends on an additional library at final link time
868
869```
870  {
871    'targets': [
872      {
873        'target_name': 'foo',
874        'type': 'executable',
875        'dependencies': [
876          'libbar',
877        ],
878      },
879      {
880        'target_name': 'libbar',
881        'type': '<(library)',
882        'dependencies': [
883          'libother'
884        ],
885        'export_dependent_settings': [
886          'libother'
887        ],
888      },
889      {
890        'target_name': 'libother',
891        'type': '<(library)',
892        'direct_dependent_settings': {
893          'defines': [
894            'DEFINE_FOR_LIBOTHER',
895          ],
896          'include_dirs': [
897            'include/libother',
898          ],
899        },
900      },
901    ],
902  }
903```
904
905### Support for Mac OS X bundles
906
907gyp supports building bundles on OS X (.app, .framework, .bundle, etc).
908Here is an example of this:
909
910```
911    {
912      'target_name': 'test_app',
913      'product_name': 'Test App Gyp',
914      'type': 'executable',
915      'mac_bundle': 1,
916      'sources': [
917        'main.m',
918        'TestAppAppDelegate.h',
919        'TestAppAppDelegate.m',
920      ],
921      'mac_bundle_resources': [
922        'TestApp/English.lproj/InfoPlist.strings',
923        'TestApp/English.lproj/MainMenu.xib',
924      ],
925      'link_settings': {
926        'libraries': [
927          '$(SDKROOT)/System/Library/Frameworks/Cocoa.framework',
928        ],
929      },
930      'xcode_settings': {
931        'INFOPLIST_FILE': 'TestApp/TestApp-Info.plist',
932      },
933    },
934```
935
936The `mac_bundle` key tells gyp that this target should be a bundle.
937`executable` targets get extension `.app` by default, `shared_library`
938targets get `.framework` – but you can change the bundle extensions by
939setting `product_extension` if you want. Files listed in
940`mac_bundle_resources` will be copied to the bundle's `Resource` folder
941of the bundle. You can also set
942`process_outputs_as_mac_bundle_resources` to 1 in actions and rules to
943let the output of actions and rules be added to that folder (similar to
944`process_outputs_as_sources`). If `product_name` is not set, the bundle
945will be named after `target_name`as usual.
946
947### Move files (refactoring)
948
949TODO
950
951### Custom build steps
952
953TODO
954
955#### Adding an explicit build step to generate specific files
956
957TODO
958
959#### Adding a rule to handle files with a new suffix
960
961TODO
962
963### Build flavors
964
965TODO
966 
codekingpro/portable-devtools · Team Ai