chip_codegen.gni 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. # Copyright (c) 2022 Project CHIP Authors
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. import("//build_overrides/build.gni")
  15. import("//build_overrides/chip.gni")
  16. import("//build_overrides/pigweed.gni")
  17. import("$dir_pw_build/python.gni")
  18. import("${chip_root}/scripts/py_matter_idl/files.gni")
  19. declare_args() {
  20. # Location where code has been pre-generated
  21. chip_code_pre_generated_directory = ""
  22. }
  23. # Code generation that will happen at build time.
  24. #
  25. # uses `scripts/codegen.py` for code generation.
  26. template("_chip_build_time_codegen") {
  27. _name = target_name
  28. _generator = invoker.generator
  29. config("${_name}_config") {
  30. include_dirs = [ target_gen_dir ]
  31. }
  32. pw_python_action("${_name}_codegen") {
  33. script = "${chip_root}/scripts/codegen.py"
  34. # TODO: this seems to touch internals. Is this ok? speeds up builds!
  35. _pw_internal_run_in_venv = false
  36. _idl_file = invoker.input
  37. _expected_outputs = "${target_gen_dir}/${_name}.expected.outputs"
  38. write_file(_expected_outputs, invoker.outputs, "list lines")
  39. args = [
  40. "--generator",
  41. _generator,
  42. "--output-dir",
  43. rebase_path(target_gen_dir, root_build_dir),
  44. "--expected-outputs",
  45. rebase_path(_expected_outputs, root_build_dir),
  46. ]
  47. if (defined(invoker.options)) {
  48. foreach(option, invoker.options) {
  49. args += [
  50. "--option",
  51. option,
  52. ]
  53. }
  54. }
  55. args += [ rebase_path(_idl_file, root_build_dir) ]
  56. inputs = [
  57. _idl_file,
  58. _expected_outputs,
  59. ]
  60. # ensure any change in codegen files will result in a rebuild
  61. inputs += matter_idl_generator_files
  62. sources = [ _idl_file ]
  63. outputs = []
  64. foreach(name, invoker.outputs) {
  65. outputs += [ "${target_gen_dir}/${name}" ]
  66. }
  67. }
  68. source_set(_name) {
  69. sources = []
  70. foreach(name, invoker.outputs) {
  71. sources += [ "${target_gen_dir}/${name}" ]
  72. }
  73. public_configs = [ ":${_name}_config" ]
  74. if (defined(invoker.public_configs)) {
  75. public_configs += invoker.public_configs
  76. }
  77. forward_variables_from(invoker, [ "deps" ])
  78. if (!defined(deps)) {
  79. deps = []
  80. }
  81. deps += [ ":${_name}_codegen" ]
  82. }
  83. }
  84. # Code generation that will happen at build time.
  85. #
  86. # variables:
  87. # input
  88. # The ".zap" file to use to start the code generation
  89. #
  90. # generator
  91. # Name of the generator to use. Supported variants:
  92. # - "app-templates"
  93. #
  94. #
  95. #
  96. # deps, public_configs
  97. # Forwarded to the resulting source set
  98. #
  99. #
  100. # uses `zap` for code generation.
  101. template("_chip_build_time_zapgen") {
  102. _name = target_name
  103. _generator = invoker.generator
  104. config("${_name}_config") {
  105. include_dirs = [ "${target_gen_dir}/zapgen/" ]
  106. }
  107. assert(_generator == "app-templates")
  108. if (_generator == "app-templates") {
  109. _template_path =
  110. rebase_path("${chip_root}/src/app/zap-templates/app-templates.json")
  111. _partials_dir = "${chip_root}/src/app/zap-templates/partials"
  112. _template_dir = "${chip_root}/src/app/zap-templates/templates/app"
  113. # TODO: unclear how to maintain these: there is no parser that can figure
  114. # out links of template files and zap files and such
  115. _extra_dependencies = [
  116. "${_partials_dir}/header.zapt",
  117. "${_partials_dir}/im_command_handler_cluster_commands.zapt",
  118. # Application templates, actually generating files
  119. "${_template_dir}/access.zapt",
  120. "${_template_dir}/CHIPClusters.zapt",
  121. "${_template_dir}/endpoint_config.zapt",
  122. "${_template_dir}/gen_config.zapt",
  123. "${_template_dir}/im-cluster-command-handler.zapt",
  124. ]
  125. _output_subdir = "zap-generated"
  126. }
  127. pw_python_action("${_name}_zap_pregen") {
  128. script = "${chip_root}/scripts/tools/zap/generate.py"
  129. # TODO: this seems to touch internals. Is this ok? speeds up builds!
  130. _pw_internal_run_in_venv = false
  131. _idl_file = invoker.input
  132. args = [
  133. "--no-prettify-output",
  134. "--templates",
  135. _template_path,
  136. "--output-dir",
  137. rebase_path(target_gen_dir) + "/zap_pregen/" + _output_subdir,
  138. # TODO: lock file support should be removed as this serializes zap
  139. # (slower), however this is currently done because on Darwin zap startup
  140. # may conflict and error out with:
  141. # Error: EEXIST: file already exists, mkdir '/var/folders/24/8k48jl6d249_n_qfxwsl6xvm0000gn/T/pkg/465fcc8a6282e28dc7a166859d5814d34e2fb94249a72fa9229033b5b32dff1a'
  142. "--lock-file",
  143. rebase_path("${root_out_dir}/zap_gen.lock"),
  144. "--parallel",
  145. _idl_file,
  146. ]
  147. inputs = [
  148. _idl_file,
  149. _template_path,
  150. ]
  151. inputs += _extra_dependencies
  152. # ensure any change in codegen files will result in a rebuild
  153. inputs += matter_idl_generator_files
  154. sources = [ _idl_file ]
  155. outputs = []
  156. foreach(name, invoker.outputs) {
  157. outputs += [ "${target_gen_dir}/zap_pregen/${name}" ]
  158. }
  159. forward_variables_from(invoker, [ "prune_outputs" ])
  160. if (defined(prune_outputs)) {
  161. foreach(name, prune_outputs) {
  162. outputs += [ "${target_gen_dir}/zap_pregen/${name}" ]
  163. }
  164. }
  165. }
  166. # This action ensures that any "extra" files generated by zap codegen
  167. # are actually deleted.
  168. #
  169. # This is to avoid double-codegen of configurations like endpoint config
  170. # or access credentials being generated for both "controller client" and
  171. # application-specific
  172. pw_python_action("${_name}_files") {
  173. # TODO: this seems to touch internals. Is this ok? speeds up builds!
  174. _pw_internal_run_in_venv = false
  175. script = "${chip_root}/scripts/tools/zap/prune_outputs.py"
  176. _keep_file = rebase_path("${target_gen_dir}/${_name}.keep.outputs")
  177. write_file(_keep_file, invoker.outputs, "list lines")
  178. args = [
  179. "--keep",
  180. _keep_file,
  181. "--input-dir",
  182. rebase_path("${target_gen_dir}/zap_pregen/"),
  183. "--output-dir",
  184. rebase_path("${target_gen_dir}/zapgen/"),
  185. ]
  186. inputs = []
  187. foreach(name, invoker.outputs) {
  188. inputs += [ "${target_gen_dir}/zap_pregen/${name}" ]
  189. }
  190. outputs = []
  191. foreach(name, invoker.outputs) {
  192. outputs += [ "${target_gen_dir}/zapgen/${name}" ]
  193. }
  194. deps = [ ":${_name}_zap_pregen" ]
  195. }
  196. source_set(_name) {
  197. sources = []
  198. foreach(name, invoker.outputs) {
  199. sources += [ "${target_gen_dir}/zapgen/${name}" ]
  200. }
  201. public_configs = [ ":${_name}_config" ]
  202. if (defined(invoker.public_configs)) {
  203. public_configs += invoker.public_configs
  204. }
  205. forward_variables_from(invoker, [ "deps" ])
  206. if (!defined(public_deps)) {
  207. public_deps = []
  208. }
  209. public_deps += [
  210. ":${_name}_files",
  211. ":${_name}_zap_pregen",
  212. ]
  213. }
  214. }
  215. # Defines a target that runs code generation based on
  216. # scripts/codegen.py
  217. #
  218. # Arguments:
  219. # input
  220. # The ".matter" file to use to start the code generation
  221. #
  222. # generator
  223. # Name of the generator to use (e.g. java-jni, java-class, cpp-app)
  224. #
  225. # outputs
  226. # Explicit names of the expected outputs. Enforced to validate that
  227. # expected outputs are generated when processing input files.
  228. #
  229. # deps, public_configs
  230. # Forwarded to the resulting source set
  231. #
  232. # Command line parameters:
  233. #
  234. # chip_code_pre_generated_directory:
  235. # - If this is set, generation will NOT happen at compile time but rather
  236. # the code generation is assumed to have already happened and reside in
  237. # the given location.
  238. # - The TOP LEVEL directory is assumed to be given. Actual location for
  239. # individual generators is expected to be of the form
  240. # <top_dir>/<matter_path>/<generator>
  241. #
  242. # NOTE: content of "outputs" is verified to match the output of codegen.py
  243. # exactly. It is not inferred on purpose, to make build-rules explicit
  244. # and verifiable (even though codegen.py can at runtime report its outputs)
  245. #
  246. # To find the list of generated files, you can run codegen.py with the
  247. # "--name-only" argument
  248. #
  249. # NOTE:
  250. # the result of the target_name WILL BE a `source_set`. Treat it as such.
  251. #
  252. # Example usage:
  253. #
  254. # chip_codegen("java-jni-generate") {
  255. # input = "controller-clusters.matter"
  256. # generator = "java-jni"
  257. #
  258. # outputs = [
  259. # "jni/IdentifyClient-ReadImpl.cpp",
  260. # "jni/IdentifyClient-InvokeSubscribeImpl.cpp",
  261. # # ... more to follow
  262. # ]
  263. # }
  264. #
  265. template("chip_codegen") {
  266. if (chip_code_pre_generated_directory == "") {
  267. _chip_build_time_codegen(target_name) {
  268. forward_variables_from(invoker,
  269. [
  270. "deps",
  271. "generator",
  272. "input",
  273. "outputs",
  274. "options",
  275. "public_configs",
  276. ])
  277. }
  278. } else {
  279. _name = target_name
  280. not_needed(invoker, [ "options" ])
  281. # This constructs a path like:
  282. # FROM all-clusters-app.matter (inside examples/all-clusters-app/all-clusters-common/)
  283. # USING "cpp-app" for generator:
  284. # => ${pregen_dir}/examples/all-clusters-app/all-clusters-common/all-clusters-app/codegen/cpp-app
  285. _generation_dir =
  286. chip_code_pre_generated_directory + "/" +
  287. string_replace(rebase_path(invoker.input, chip_root), ".matter", "") +
  288. "/codegen/" + invoker.generator
  289. config("${_name}_config") {
  290. include_dirs = [ "${_generation_dir}" ]
  291. }
  292. source_set(_name) {
  293. public_configs = [ ":${_name}_config" ]
  294. if (defined(invoker.public_configs)) {
  295. public_configs += invoker.public_configs
  296. }
  297. forward_variables_from(invoker, [ "deps" ])
  298. sources = []
  299. foreach(name, invoker.outputs) {
  300. sources += [ "${_generation_dir}/${name}" ]
  301. }
  302. }
  303. }
  304. }
  305. # Defines a target that runs code generation based on
  306. # scripts/codegen.py
  307. #
  308. # Arguments:
  309. # input
  310. # The ".matter" file to use to start the code generation
  311. #
  312. # generator
  313. # Name of the generator to use (e.g. java-jni, java-class, cpp-app)
  314. #
  315. # outputs
  316. # Explicit names of the expected outputs. Enforced to validate that
  317. # expected outputs are generated when processing input files.
  318. #
  319. # deps, public_configs
  320. # Forwarded to the resulting source set
  321. #
  322. # Command line parameters:
  323. #
  324. # chip_code_pre_generated_directory:
  325. # - If this is set, generation will NOT happen at compile time but rather
  326. # the code generation is assumed to have already happened and reside in
  327. # the given location.
  328. # - The TOP LEVEL directory is assumed to be given. Actual location for
  329. # individual generators is expected to be of the form
  330. # <top_dir>/<matter_path>/<generator>
  331. #
  332. # NOTE: content of "outputs" is verified to match the output of codegen.py
  333. # exactly. It is not inferred on purpose, to make build-rules explicit
  334. # and verifiable (even though codegen.py can at runtime report its outputs)
  335. #
  336. # To find the list of generated files, you can run codegen.py with the
  337. # "--name-only" argument
  338. #
  339. # NOTE:
  340. # the result of the target_name WILL BE a `source_set`. Treat it as such.
  341. #
  342. # Example usage:
  343. #
  344. # chip_codegen("java-jni-generate") {
  345. # input = "controller-clusters.matter"
  346. # generator = "java-jni"
  347. #
  348. # outputs = [
  349. # "jni/IdentifyClient-ReadImpl.cpp",
  350. # "jni/IdentifyClient-InvokeSubscribeImpl.cpp",
  351. # # ... more to follow
  352. # ]
  353. # }
  354. #
  355. template("chip_zapgen") {
  356. if (chip_code_pre_generated_directory == "") {
  357. _chip_build_time_zapgen(target_name) {
  358. forward_variables_from(invoker,
  359. [
  360. "deps",
  361. "generator",
  362. "input",
  363. "outputs",
  364. "public_configs",
  365. "prune_outputs",
  366. ])
  367. }
  368. } else {
  369. _name = target_name
  370. # This contstructs a path like:
  371. # FROM all-clusters-app.zap (inside examples/all-clusters-app/all-clusters-common/)
  372. # USING "cpp-app" for generator:
  373. # => ${pregen_dir}/examples/all-clusters-app/all-clusters-common/all-clusters-app/codegen/cpp-app
  374. _generation_dir =
  375. chip_code_pre_generated_directory + "/" +
  376. string_replace(rebase_path(invoker.input, chip_root), ".zap", "") +
  377. "/zap/" + invoker.generator
  378. config("${_name}_config") {
  379. include_dirs = [ "${_generation_dir}" ]
  380. }
  381. # Pick up only the headers and mark them available to use
  382. # Specifically controller seems to require header files but NOT cpp (does)
  383. # not want to include cpp compilation of IM command handler data
  384. source_set("${_name}_headers") {
  385. sources = []
  386. foreach(name, invoker.outputs) {
  387. if (get_path_info(name, "extension") == "h") {
  388. sources += [ "${_generation_dir}/${name}" ]
  389. }
  390. }
  391. # Ugly, but references WILL reference back into main code.
  392. check_includes = false
  393. }
  394. # need to have consistent naming. Make sure "files" exists
  395. action("${_name}_files") {
  396. script = "${chip_root}/scripts/tools/zap/check_file_existence.py"
  397. _output_name = "${target_gen_dir}/${_name}_files_checked.stamp"
  398. args = [
  399. "--touch",
  400. rebase_path(_output_name),
  401. ]
  402. outputs = [ _output_name ]
  403. foreach(name, invoker.outputs) {
  404. args += [
  405. "--exists",
  406. rebase_path("${_generation_dir}/${name}"),
  407. ]
  408. }
  409. # Depending on the files gets access to the headers
  410. public_deps = [ ":${_name}_headers" ]
  411. }
  412. source_set(_name) {
  413. forward_variables_from(invoker,
  414. [
  415. "deps",
  416. "public_configs",
  417. "prune_outputs",
  418. ])
  419. if (!defined(public_configs)) {
  420. public_configs = []
  421. }
  422. public_configs += [ ":${_name}_config" ]
  423. sources = []
  424. foreach(name, invoker.outputs) {
  425. sources += [ "${_generation_dir}/${name}" ]
  426. }
  427. # Ugly, but references WILL reference back into main code.
  428. check_includes = false
  429. }
  430. }
  431. }