• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import os
2import re
3import meson_impl as impl
4
5
6class Compiler(impl.Compiler):
7
8  def is_symbol_supported(self, header: str, symbol: str):
9    return super().is_symbol_supported(header, symbol)
10
11  def is_function_supported(self, function):
12    return super().is_function_supported(function)
13
14  def is_link_supported(self, name):
15    return super().is_link_supported(name)
16
17  def has_header_symbol(
18      self,
19      header: str,
20      symbol: str,
21      args=[],
22      dependencies=[],
23      include_directories=[],
24      no_builtin_args=False,
25      prefix=[],
26      required=False,
27  ):
28    # Exclude what is currently not working.
29    result = self.is_symbol_supported(header, symbol)
30    print("has_header_symbol '%s', '%s': %s" % (header, symbol, str(result)))
31    return result
32
33  def check_header(self, header, prefix=''):
34    result = self.is_header_supported(header)
35    print("check_header '%s': %s" % (header, str(result)))
36    return result
37
38  def has_function(self, function, args=[], prefix='', dependencies=''):
39    # Exclude what is currently not working.
40    result = self.is_function_supported(function)
41    print("has_function '%s': %s" % (function, str(result)))
42    return result
43
44  def links(self, snippet, name, args=[], dependencies=[]):
45    # Exclude what is currently not working.
46    result = self.is_link_supported(name)
47    print("links '%s': %s" % (name, str(result)))
48    return result
49
50
51class PkgconfigModule:
52
53  def generate(self, lib, name='', description='', extra_cflags=[]):
54    return
55
56
57# These globals exposed by the meson API
58meson = impl.Meson(Compiler())
59host_machine = impl.Machine('android')
60build_machine = impl.Machine('linux')
61
62
63def open_output_file():
64  impl.open_output_file(r'Android.bp')
65
66
67def close_output_file():
68  impl.close_output_file()
69
70
71def load_config_file():
72  impl.load_config_file('python-build/generate_android_build.config')
73
74
75def include_directories(*paths, is_system=False):
76  return impl.IncludeDirectories('', impl.get_include_dirs(paths))
77
78
79def module_import(name: str):
80  if name == 'python':
81    return impl.PythonModule()
82  if name == 'pkgconfig':
83    return PkgconfigModule()
84  exit('Unhandled module: ' + name)
85
86
87def dependency(
88    *names,
89    required=True,
90    version='',
91    allow_fallback=False,
92    method='',
93    modules=[],
94    optional_modules=[],
95    static=True,
96    fallback=[],
97    include_type='',
98    native=False
99):
100  for name in names:
101    if name == 'zlib':
102      return impl.Dependency(
103          name,
104          targets=[
105              impl.DependencyTarget(
106                  'libz', impl.DependencyTargetType.STATIC_LIBRARY
107              )
108          ],
109          version=version,
110          found=True,
111      )
112
113    if name == 'hardware':
114      return impl.Dependency(
115          name,
116          targets=[
117              impl.DependencyTarget(
118                  'libhardware', impl.DependencyTargetType.SHARED_LIBRARY
119              ),
120              impl.DependencyTarget(
121                  'hwvulkan_headers', impl.DependencyTargetType.HEADER_LIBRARY
122              ),
123          ],
124          version=version,
125          found=True,
126      )
127
128    if name == 'cutils' or name == 'log' or name == 'nativewindow':
129      return impl.Dependency(
130          name,
131          targets=[
132              impl.DependencyTarget(
133                  'lib' + name, impl.DependencyTargetType.SHARED_LIBRARY
134              )
135          ],
136          version=version,
137          found=True,
138      )
139
140    if name == 'sync':
141      return impl.Dependency(
142          name,
143          targets=[
144              impl.DependencyTarget(
145                  'lib' + name, impl.DependencyTargetType.STATIC_LIBRARY
146              )
147          ],
148          version=version,
149          found=True,
150      )
151
152    if name == 'android.hardware.graphics.mapper':
153      assert version == '>= 4.0'
154      return impl.Dependency(
155          name,
156          targets=[
157              impl.DependencyTarget(
158                  'libgralloctypes', impl.DependencyTargetType.STATIC_LIBRARY
159              ),
160              impl.DependencyTarget(
161                  'android.hardware.graphics.mapper@4.0',
162                  impl.DependencyTargetType.STATIC_LIBRARY,
163              ),
164              impl.DependencyTarget(
165                  'libhidlbase', impl.DependencyTargetType.SHARED_LIBRARY
166              ),
167              impl.DependencyTarget(
168                  'libutils', impl.DependencyTargetType.SHARED_LIBRARY
169              ),
170          ],
171          version=version,
172          found=True,
173      )
174
175  return impl.dependency(*names, required=required, version=version)
176
177
178def static_library(
179    target_name,
180    *source_files,
181    c_args=[],
182    cpp_args=[],
183    c_pch='',
184    build_by_default=False,
185    build_rpath='',
186    d_debug=[],
187    d_import_dirs=[],
188    d_module_versions=[],
189    d_unittest=False,
190    dependencies=[],
191    extra_files='',
192    gnu_symbol_visibility='',
193    gui_app=False,
194    implicit_include_directories=False,
195    include_directories=[],
196    install=False,
197    install_dir='',
198    install_mode=[],
199    install_rpath='',
200    install_tag='',
201    link_args=[],
202    link_depends='',
203    link_language='',
204    link_whole=[],
205    link_with=[],
206    name_prefix='',
207    name_suffix='',
208    native=False,
209    objects=[],
210    override_options=[],
211    pic=False,
212    prelink=False,
213    rust_abi='',
214    rust_crate_type='',
215    rust_dependency_map={},
216    sources='',
217    vala_args=[],
218    win_subsystem=''
219):
220  print('static_library: ' + target_name)
221
222  link_with = impl.get_linear_list(link_with)
223  link_whole = impl.get_linear_list(link_whole)
224
225  # Emitting link_with/link_whole into a static library isn't useful for building,
226  # shared libraries must specify the whole chain of dependencies.
227  # Leaving them here for traceability though maybe it's less confusing to remove them?
228  _emit_builtin_target(
229      'cc_library_static',
230      target_name,
231      *source_files,
232      c_args=c_args,
233      cpp_args=cpp_args,
234      dependencies=dependencies,
235      include_directories=include_directories,
236      link_with=link_with,
237      link_whole=link_whole
238  )
239
240  return impl.StaticLibrary(
241      target_name, link_with=link_with, link_whole=link_whole
242  )
243
244
245def _get_sources(input_sources, sources, generated_sources, generated_headers):
246  for source in input_sources:
247    if type(source) is list:
248      _get_sources(source, sources, generated_sources, generated_headers)
249    elif type(source) is impl.CustomTarget:
250      if source.generates_h():
251        generated_headers.add(source.target_name_h())
252      if source.generates_c():
253        generated_sources.add(source.target_name_c())
254    elif type(source) is impl.CustomTargetItem:
255      source = source.target
256      if source.generates_h():
257        generated_headers.add(source.target_name_h())
258      if source.generates_c():
259        generated_sources.add(source.target_name_c())
260    elif type(source) is impl.File:
261      sources.add(source.name)
262    elif type(source) is str:
263      sources.add(impl.get_relative_dir(source))
264    else:
265      exit('source type not handled: ' + str(type(source)))
266
267
268def _emit_builtin_target(
269    builtin_type_name,
270    target_name,
271    *source,
272    c_args=[],
273    cpp_args=[],
274    dependencies=[],
275    include_directories=[],
276    link_with=[],
277    link_whole=[]
278):
279  impl.fprint('%s {' % builtin_type_name)
280  impl.fprint('  name: "%s",' % target_name)
281
282  srcs = set()
283  generated_sources = set()
284  generated_headers = set()
285  for source_arg in source:
286    assert type(source_arg) is list
287    _get_sources(source_arg, srcs, generated_sources, generated_headers)
288
289  deps = impl.get_set_of_deps(dependencies)
290  include_directories = [impl.get_relative_dir()] + impl.get_include_dirs(
291      include_directories
292  )
293  static_libs = []
294  whole_static_libs = []
295  shared_libs = []
296  header_libs = []
297  for dep in deps:
298    print('  dep: ' + dep.name)
299    for src in impl.get_linear_list([dep.sources]):
300      if type(src) == impl.CustomTargetItem:
301        generated_headers.add(src.target.target_name_h())
302      elif type(src) == impl.CustomTarget:
303        generated_headers.add(src.target_name_h())
304      else:
305        exit('Unhandled source dependency: ' + str(type(src)))
306    include_directories.extend(impl.get_include_dirs(dep.include_directories))
307    for target in impl.get_static_libs([dep.link_with]):
308      assert type(target) == impl.StaticLibrary
309      static_libs.append(target.target_name)
310    for target in impl.get_linear_list([dep.link_whole]):
311      assert type(target) == impl.StaticLibrary
312      whole_static_libs.append(target.target_name)
313    for target in dep.targets:
314      if target.target_type == impl.DependencyTargetType.SHARED_LIBRARY:
315        shared_libs.append(target.target_name)
316      elif target.target_type == impl.DependencyTargetType.STATIC_LIBRARY:
317        static_libs.append(target.target_name)
318      elif target.target_type == impl.DependencyTargetType.HEADER_LIBRARY:
319        header_libs.append(target.target_name)
320
321  for target in impl.get_static_libs(link_with):
322    if type(target) == impl.StaticLibrary:
323      static_libs.append(target.target_name)
324    else:
325      exit('Unhandled link_with type: ' + str(type(target)))
326
327  for target in impl.get_whole_static_libs(link_whole):
328    if type(target) == impl.StaticLibrary:
329      whole_static_libs.append(target.target_name())
330    else:
331      exit('Unhandled link_whole type: ' + str(type(target)))
332
333  # Android turns all warnings into errors but thirdparty projects typically can't handle that
334  cflags = ['-Wno-error'] + impl.get_linear_list(
335      impl.get_project_cflags() + c_args
336  )
337  cppflags = ['-Wno-error'] + impl.get_linear_list(
338      impl.get_project_cppflags() + cpp_args
339  )
340
341  impl.fprint('  srcs: [')
342  for src in srcs:
343    # Filter out header files
344    if not src.endswith('.h'):
345      impl.fprint('    "%s",' % src)
346  impl.fprint('  ],')
347
348  impl.fprint('  generated_headers: [')
349  for generated in generated_headers:
350    impl.fprint('    "%s",' % generated)
351  impl.fprint('  ],')
352
353  impl.fprint('  generated_sources: [')
354  for generated in generated_sources:
355    impl.fprint('    "%s",' % generated)
356  impl.fprint('  ],')
357
358  for arg in impl.get_project_options():
359    if arg.name == 'c_std':
360      impl.fprint('  c_std: "%s",' % arg.value)
361    elif arg.name == 'cpp_std':
362      impl.fprint('  cpp_std: "%s",' % arg.value)
363
364  impl.fprint('  conlyflags: [')
365  for arg in cflags:
366    # Escape double quotations
367    arg = re.sub(r'"', '\\"', arg)
368    impl.fprint('    "%s",' % arg)
369  impl.fprint('  ],')
370  impl.fprint('  cppflags: [')
371  for arg in cppflags:
372    # Escape double quotations
373    arg = re.sub(r'"', '\\"', arg)
374    impl.fprint('    "%s",' % arg)
375  impl.fprint('  ],')
376
377  impl.fprint('  local_include_dirs: [')
378  for inc in include_directories:
379    impl.fprint('    "%s",' % inc)
380  impl.fprint('  ],')
381
382  impl.fprint('  static_libs: [')
383  for lib in static_libs:
384    impl.fprint('    "%s",' % lib)
385  impl.fprint('  ],')
386
387  impl.fprint('  whole_static_libs: [')
388  for lib in whole_static_libs:
389    impl.fprint('    "%s",' % lib)
390  impl.fprint('  ],')
391
392  impl.fprint('  shared_libs: [')
393  for lib in shared_libs:
394    impl.fprint('    "%s",' % lib)
395  impl.fprint('  ],')
396
397  impl.fprint('  header_libs: [')
398  for lib in header_libs:
399    impl.fprint('    "%s",' % lib)
400  impl.fprint('  ],')
401
402  impl.fprint('}')
403
404
405def shared_library(
406    target_name,
407    *source,
408    c_args=[],
409    cpp_args=[],
410    c_pch='',
411    build_by_default=False,
412    build_rpath='',
413    d_debug=[],
414    d_import_dirs=[],
415    d_module_versions=[],
416    d_unittest=False,
417    darwin_versions='',
418    dependencies=[],
419    extra_files='',
420    gnu_symbol_visibility='',
421    gui_app=False,
422    implicit_include_directories=False,
423    include_directories=[],
424    install=False,
425    install_dir='',
426    install_mode=[],
427    install_rpath='',
428    install_tag='',
429    link_args=[],
430    link_depends=[],
431    link_language='',
432    link_whole=[],
433    link_with=[],
434    name_prefix='',
435    name_suffix='',
436    native=False,
437    objects=[],
438    override_options=[],
439    rust_abi='',
440    rust_crate_type='',
441    rust_dependency_map={},
442    sources=[],
443    soversion='',
444    vala_args=[],
445    version='',
446    vs_module_defs='',
447    win_subsystem=''
448):
449  print('shared_library: ' + target_name)
450
451  link_with = impl.get_linear_list([link_with])
452  link_whole = impl.get_linear_list([link_whole])
453
454  _emit_builtin_target(
455      'cc_library_shared',
456      target_name,
457      *source,
458      c_args=c_args,
459      cpp_args=cpp_args,
460      dependencies=dependencies,
461      include_directories=include_directories,
462      link_with=link_with,
463      link_whole=link_whole
464  )
465  return impl.SharedLibrary(target_name)
466
467
468def _process_target_name(name):
469  name = re.sub(r'[\[\]]', '', name)
470  return name
471
472
473# Returns string or list of string
474def _location_wrapper(name_or_list):
475  if isinstance(name_or_list, list):
476    ret = []
477    for i in name_or_list:
478      ret.append('$(location %s)' % i)
479    return ret
480
481  assert isinstance(name_or_list, str)
482  return '$(location %s)' % name_or_list
483
484
485def _is_header(name):
486  return re.search(r'\.h[xx|pp]?$', name) != None
487
488
489def _is_source(name):
490  return re.search(r'\.c[c|xx|pp]?$', name) != None
491
492
493def _get_command_args(
494    command,
495    input,
496    output,
497    deps,
498    location_wrap=False,
499    obfuscate_output_c=False,
500    obfuscate_output_h=False,
501    obfuscate_suffix='',
502):
503  args = []
504  for command_item in command[1:]:
505    if isinstance(command_item, list):
506      for item in command_item:
507        assert type(item) == impl.File
508        args.append(
509            _location_wrapper(item.name) if location_wrap else item.name
510        )
511      continue
512
513    assert type(command_item) is str
514    match = re.match(r'@INPUT([0-9])?@', command_item)
515    if match != None:
516      if match.group(1) != None:
517        input_index = int(match.group(1))
518        input_list = impl.get_list_of_relative_inputs(input[input_index])
519      else:
520        input_list = impl.get_list_of_relative_inputs(input)
521      args.extend(
522          _location_wrapper(input_list) if location_wrap else input_list
523      )
524      continue
525
526    match = re.match(r'(.*?)@OUTPUT([0-9])?@', command_item)
527    if match != None:
528      output_list = []
529      if match.group(2) != None:
530        output_index = int(match.group(2))
531        selected_output = (
532            output[output_index] if isinstance(output, list) else output
533        )
534        output_list.append(impl.get_relative_gen_dir(selected_output))
535      elif isinstance(output, list):
536        for out in output:
537          output_list.append(impl.get_relative_gen_dir(out))
538      else:
539        output_list.append(impl.get_relative_gen_dir(output))
540      for out in output_list:
541        if _is_header(out) and obfuscate_output_h:
542          args.append(
543              match.group(1) + '$(genDir)/%s' % out if location_wrap else out
544          )
545        else:
546          if _is_source(out) and obfuscate_output_c:
547            out += obfuscate_suffix
548          args.append(
549              match.group(1) + _location_wrapper(out) if location_wrap else out
550          )
551      continue
552
553    # Assume used to locate generated outputs
554    match = re.match(r'(.*?)@CURRENT_BUILD_DIR@', command_item)
555    if match != None:
556      args.append('$(genDir)' + '/' + impl.get_relative_dir())
557      continue
558
559    # A plain arg
560    if ' ' in command_item:
561      args.append("'%s'" % command_item)
562    else:
563      args.append(command_item)
564
565  return args
566
567
568# Assume dependencies of custom targets are custom targets that are generating
569# python scripts; build a python path of their locations.
570def _get_python_path(deps):
571  python_path = ''
572  for index, dep in enumerate(deps):
573    assert type(dep) == impl.CustomTarget
574    if index > 0:
575      python_path += ':'
576    python_path += '`dirname %s`' % _location_wrapper(':%s' % dep.target_name())
577  return python_path
578
579
580def _get_export_include_dirs():
581  dirs = [impl.get_relative_dir()]
582  # HACK for source files that expect that include generated files like:
583  # include "vulkan/runtime/...h"
584  if impl.get_relative_dir().startswith('src'):
585    dirs.append('src')
586  return dirs
587
588
589def _process_wrapped_args_for_python(
590    wrapped_args, python_script, python_script_target_name, deps
591):
592  # The python script arg should be replaced with the python binary target name
593  args = impl.replace_wrapped_input_with_target(
594      wrapped_args, python_script, python_script_target_name
595  )
596  python_path = 'PYTHONPATH='
597  # Python scripts expect to be able to import other scripts from the same directory, but this
598  # doesn't work in the soong execution environment, so we have to explicitly add the script dir.
599  # We can't use $(location python_binary) because that is missing the relative path;
600  # instead we can use $(location python_script), which happens to work, and we're careful to ensure
601  # the script is in the list of sources even when it's used as the command directly.
602  python_path += '`dirname $(location %s)`' % python_script
603  # Also ensure that scripts generated by dependent custom targets can be imported.
604  if len(deps) > 0:
605    python_path += ':' + _get_python_path(deps)
606  args.insert(0, python_path)
607  return args
608
609
610# Create a custom top level build target
611# Issues:
612# - python scripts that call python modules from other directories:
613#   may pass an absolute path to the module directory which violates the sandbox.
614def custom_target(
615    target_name: str,
616    build_always=False,
617    build_always_stale=False,
618    build_by_default=False,
619    capture=False,
620    command=[],
621    console=False,
622    depend_files=[],
623    depends=[],
624    depfile='',
625    env=[],
626    feed=False,
627    input=[],
628    install=False,
629    install_dir='',
630    install_mode=[],
631    install_tag=[],
632    output=[],
633):
634  target_name = _process_target_name(target_name)
635  print('Custom target: ' + target_name)
636  assert type(command) is list
637  program = command[0]
638  program_args = []
639
640  # The program can be an array that includes arguments
641  if isinstance(program, list):
642    for arg in program[1:]:
643      assert type(arg) is str
644      program_args.append(arg)
645    program = program[0]
646
647  assert isinstance(program, impl.Program)
648  assert program.found()
649
650  args = program_args + _get_command_args(command, input, output, depends)
651
652  # Python scripts need special handling to find mako library
653  python_script = ''
654  python_script_target_name = ''
655  if program.command.endswith('.py'):
656    python_script = program.command
657  else:
658    for index, arg in enumerate(args):
659      if arg.endswith('.py'):
660        python_script = arg
661        break
662
663  if python_script != '':
664    python_script_target_name = (
665        target_name + '_' + os.path.basename(python_script)
666    )
667    srcs = [python_script] + impl.get_list_of_relative_inputs(depend_files)
668    impl.fprint('python_binary_host {')
669    impl.fprint('  name: "%s",' % python_script_target_name)
670    impl.fprint('  main: "%s",' % python_script)
671    impl.fprint('  srcs: [')
672    for src in srcs:
673      if src.endswith('.py'):
674        impl.fprint('    "%s",' % src)
675    impl.fprint('  ],')
676    impl.fprint('  libs: ["mako"],')
677    # For the PYTHONPATH to work in our genrules command, we need the python interpreter,
678    # not a compiled binary, so disable embedded_launcher.
679    impl.fprint('  version: {')
680    impl.fprint('    py3: {')
681    impl.fprint('      embedded_launcher: false,')
682    impl.fprint('    },')
683    impl.fprint('  },')
684    impl.fprint('}')
685
686  relative_inputs = impl.get_list_of_relative_inputs(input)
687  # We use python_host_binary instead of calling python scripts directly;
688  # however there's an issue with python locating modules in the same directory
689  # as the script; to workaround that (see _process_wrapped_args_for_python) we
690  # ensure the script is listed in the genrule targets.
691  if python_script != '' and not python_script in relative_inputs:
692    relative_inputs.append(python_script)
693  relative_inputs.extend(impl.get_list_of_relative_inputs(depend_files))
694
695  relative_outputs = []
696  if isinstance(output, list):
697    for file in output:
698      relative_outputs.append(impl.get_relative_gen_dir(file))
699  else:
700    assert type(output) == str
701    relative_outputs.append(impl.get_relative_gen_dir(output))
702
703  # Soong requires genrule to generate only headers OR non-headers
704  generates_h = False
705  generates_c = False
706  for out in relative_outputs:
707    if _is_header(out):
708      generates_h = True
709    if _is_source(out):
710      generates_c = True
711
712  custom_target = impl.CustomTarget(
713      target_name, relative_outputs, generates_h, generates_c
714  )
715
716  program_command = program.command
717  if program_command == 'bison':
718    program_command_arg = 'M4=$(location m4) $(location bison)'
719  elif program_command == 'flex':
720    program_command_arg = 'M4=$(location m4) $(location flex)'
721  elif program_command.endswith('.py'):
722    program_command_arg = _location_wrapper(program_command)
723  else:
724    program_command_arg = program_command
725
726  program_args = [program_command_arg] + program_args
727
728  if custom_target.generates_h() and custom_target.generates_c():
729    # Make a rule for only the headers
730    obfuscate_suffix = '.dummy.h'
731    wrapped_args = program_args + _get_command_args(
732        command,
733        input,
734        output,
735        depends,
736        location_wrap=True,
737        obfuscate_output_c=True,
738        obfuscate_suffix=obfuscate_suffix,
739    )
740    if python_script:
741      wrapped_args = _process_wrapped_args_for_python(
742          wrapped_args, python_script, python_script_target_name, depends
743      )
744
745    command_line = impl.get_command_line_from_args(wrapped_args)
746    if capture:
747      command_line += ' > %s' % _location_wrapper(
748          impl.get_relative_gen_dir(output)
749      )
750
751    impl.fprint('genrule {')
752    impl.fprint('  name: "%s",' % custom_target.target_name_h())
753    impl.fprint('  srcs: [')
754    for src in relative_inputs:
755      impl.fprint('    "%s",' % src)
756    for dep in depends:
757      assert type(dep) is CustomTarget
758      impl.fprint('    ":%s",' % dep.target_name())
759    impl.fprint('  ],')
760    impl.fprint('  out: [')
761    for out in relative_outputs:
762      if _is_source(out):
763        out += obfuscate_suffix
764        impl.fprint('    "%s",' % out)
765        # The scripts may still write to the assumed .c file, ensure the obfuscated file exists
766        command_line += "; echo '//nothing to see here' > " + _location_wrapper(
767            out
768        )
769      else:
770        impl.fprint('    "%s",' % out)
771    impl.fprint('  ],')
772    impl.fprint('  tools: [')
773    if python_script_target_name != '':
774      impl.fprint('    "%s",' % python_script_target_name)
775    if program_command == 'bison' or program_command == 'flex':
776      impl.fprint('    "%s",' % 'm4')
777      impl.fprint('    "%s",' % program_command)
778    impl.fprint('  ],')
779    impl.fprint('  export_include_dirs: [')
780    for dir in _get_export_include_dirs():
781      impl.fprint('    "%s",' % dir)
782    impl.fprint('  ],')
783    impl.fprint('  cmd: "%s"' % command_line)
784    impl.fprint('}')
785
786    # Make a rule for only the sources
787    obfuscate_suffix = '.dummy.c'
788    wrapped_args = program_args + _get_command_args(
789        command,
790        input,
791        output,
792        depends,
793        location_wrap=True,
794        obfuscate_output_h=True,
795        obfuscate_suffix=obfuscate_suffix,
796    )
797    if python_script:
798      wrapped_args = _process_wrapped_args_for_python(
799          wrapped_args, python_script, python_script_target_name, depends
800      )
801
802    command_line = impl.get_command_line_from_args(wrapped_args)
803    if capture:
804      command_line += ' > %s' % _location_wrapper(get_relative_gen_dir(output))
805
806    # We keep the header as an output with an obfuscated name because some scripts insist on having
807    # --out-h (like vk_entrypoints_gen.py).
808    # When Soong depends on this genrule it'll insist on compiling all the outputs, so we replace the
809    # content of all header outputs.
810    impl.fprint('genrule {')
811    impl.fprint('  name: "%s",' % custom_target.target_name_c())
812    impl.fprint('  srcs: [')
813    for src in relative_inputs:
814      impl.fprint('    "%s",' % src)
815    for dep in depends:
816      assert type(dep) is CustomTarget
817      impl.fprint('    ":%s",' % dep.target_name())
818    impl.fprint('  ],')
819    impl.fprint('  out: [')
820    for out in relative_outputs:
821      if _is_header(out):
822        out += obfuscate_suffix
823        impl.fprint('    "%s",' % out)
824        # Remove the content because Soong will compile this dummy source file
825        command_line += "; echo '//nothing to see here' > " + _location_wrapper(
826            out
827        )
828      else:
829        impl.fprint('    "%s",' % out)
830    impl.fprint('  ],')
831    impl.fprint('  tools: [')
832    if python_script_target_name != '':
833      impl.fprint('    "%s",' % python_script_target_name)
834    if program_command == 'bison' or program_command == 'flex':
835      impl.fprint('    "%s",' % 'm4')
836      impl.fprint('    "%s",' % program_command)
837    impl.fprint('  ],')
838    impl.fprint('  cmd: "%s"' % command_line)
839    impl.fprint('}')
840
841    return custom_target
842  else:
843    wrapped_args = program_args + _get_command_args(
844        command, input, output, depends, location_wrap=True
845    )
846    if python_script:
847      wrapped_args = _process_wrapped_args_for_python(
848          wrapped_args, python_script, python_script_target_name, depends
849      )
850
851    command_line = impl.get_command_line_from_args(wrapped_args)
852    if capture:
853      command_line += ' > %s' % _location_wrapper(
854          impl.get_relative_gen_dir(output)
855      )
856
857    impl.fprint('genrule {')
858    impl.fprint('  name: "%s",' % custom_target.target_name())
859    impl.fprint('  srcs: [')
860    for src in relative_inputs:
861      impl.fprint('    "%s",' % src)
862    for dep in depends:
863      assert type(dep) is impl.CustomTarget
864      impl.fprint('    ":%s",' % dep.target_name())
865    impl.fprint('  ],')
866    impl.fprint('  out: [')
867    for out in relative_outputs:
868      impl.fprint('    "%s",' % out)
869    impl.fprint('  ],')
870    impl.fprint('  tools: [')
871    if python_script_target_name != '':
872      impl.fprint('    "%s",' % python_script_target_name)
873    if program_command == 'bison' or program_command == 'flex':
874      impl.fprint('    "%s",' % 'm4')
875      impl.fprint('    "%s",' % program_command)
876    impl.fprint('  ],')
877    impl.fprint('  export_include_dirs: [')
878    for dir in _get_export_include_dirs():
879      impl.fprint('    "%s",' % dir)
880    impl.fprint('  ],')
881    impl.fprint('  cmd: "%s"' % command_line)
882    impl.fprint('}')
883
884  return custom_target
885