• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1from enum import Enum
2import os
3import re
4import subprocess
5
6# The file used to write output build definitions.
7_gOutputFile = ''
8
9# The relative directory that is currently being processed. When files are
10# referenced they are relative to this path.
11_gRelativeDir = ''
12
13# Global compiler flags
14_gProjectCflags = []
15_gProjectCppflags = []
16
17# Parameters set by config file
18_gCpuFamily = 'unknown'
19_gCpu = _gCpuFamily
20
21_gProjectVersion = 'unknown'
22_gProjectOptions = []
23
24
25class IncludeDirectories:
26
27  def __init__(self, name: str, dirs: []):
28    self.name = name
29    self.dirs = dirs
30
31  def __iter__(self):
32    return iter([self])
33
34
35class File:
36
37  def __init__(self, name: str):
38    self.name = name
39
40
41class Machine:
42  def __init__(self, system):
43    self._system = system
44
45  def system(self):
46    return self._system
47
48  def cpu_family(self):
49    return _gCpuFamily
50
51  def cpu(self):
52    return _gCpuFamily
53
54
55class DependencyTargetType(Enum):
56  SHARED_LIBRARY = 1
57  STATIC_LIBRARY = 2
58  HEADER_LIBRARY = 3
59
60
61class DependencyTarget:
62
63  def __init__(self, target_name: str, target_type: DependencyTargetType):
64    self.target_name = target_name
65    self.target_type = target_type
66
67
68class Dependency:
69  _id_generator = 1000
70
71  def __init__(
72      self,
73      name: str,
74      version='',
75      found=False,
76      targets=[],
77      compile_args=[],
78      include_directories=[],
79      dependencies=[],
80      sources=[],
81      link_with=[],
82      link_whole=[],
83  ):
84    self.name = name
85    self.targets = targets
86    self._version = version
87    self._found = found
88    self.compile_args = compile_args
89    self.include_directories = include_directories
90    self.dependencies = dependencies
91    self.sources = sources
92    self.link_with = link_with
93    self.link_whole = link_whole
94    Dependency._id_generator += 1
95    self.unique_id = Dependency._id_generator
96
97  def version(self):
98    return self._version
99
100  def found(self):
101    return self._found
102
103  def partial_dependency(self, compile_args=''):
104    return self
105
106  def __iter__(self):
107    return iter([self])
108
109  def __hash__(self):
110    return hash(self.unique_id)
111
112  def __eq__(self, other):
113    return self.unique_id == other._unique_id
114
115
116class CommandReturn:
117
118  def __init__(self, completed_process):
119    self.completed_process = completed_process
120
121  def returncode(self):
122    return self.completed_process.returncode
123
124  def stdout(self):
125    return self.completed_process.stdout
126
127
128class Program:
129
130  def __init__(self, command, found: bool):
131    self.command = command
132    self._found = found
133
134  # Running commands from the ambient system may give wrong/misleading results, since
135  # some build systems use hermetic installations of tools like python.
136  def run_command(self, *commands, capture_output=False):
137    command_line = [self.command]
138    for command in commands:
139      command_line += command
140
141    completed_process = subprocess.run(
142        command_line, check=False, capture_output=capture_output
143    )
144    return CommandReturn(completed_process)
145
146  def found(self):
147    return self._found
148
149  def full_path(self):
150    return 'full_path'
151
152
153class PythonModule:
154
155  def find_installation(self, name: str):
156    if name == 'python3':
157      return Program(name, found=True)
158    exit('Unhandled python installation: ' + name)
159
160
161class EnableState(Enum):
162  ENABLED = 1
163  DISABLED = 2
164  AUTO = 3
165
166
167class FeatureOption:
168
169  def __init__(self, name, state=EnableState.AUTO):
170    self.name = name
171    self.state = state
172
173  def allowed(self):
174    return self.state == EnableState.ENABLED or self.state == EnableState.AUTO
175
176  def enabled(self):
177    return self.state == EnableState.ENABLED
178
179  def disabled(self):
180    return self.state == EnableState.DISABLED
181
182  def disable_auto_if(self, value: bool):
183    if value and self.state == EnableState.AUTO:
184      self.state = EnableState.DISABLED
185    return self
186
187  def disable_if(self, value: bool, error_message: str):
188    if not value:
189      return self
190    if self.state == EnableState.ENABLED:
191      exit(error_message)
192    return FeatureOption(self.name, state=EnableState.DISABLED)
193
194  def require(self, value: bool, error_message: str):
195    if value:
196      return self
197    if self.state == EnableState.ENABLED:
198      exit(error_message)
199    return FeatureOption(self.name, state=EnableState.DISABLED)
200
201  def set(self, value: str):
202    value = value.lower()
203    if value == 'auto':
204      self.state = EnableState.AUTO
205    elif value == 'enabled':
206      self.state = EnableState.ENABLED
207    elif value == 'disabled':
208      self.state = EnableState.DISABLED
209    else:
210      exit('Unable to set feature to: %s' % value)
211
212
213class ArrayOption:
214
215  def __init__(self, name: str, value: []):
216    self.name = name
217    self.strings = value
218
219  def set(self, value: str):
220    if value == '':
221      self.strings = []
222    else:
223      self.strings = [value]
224
225
226class ComboOption:
227
228  def __init__(self, name: str, value: str):
229    self.name = name
230    self.value = value
231
232  def set(self, value: str):
233    self.value = value
234
235
236class BooleanOption:
237
238  def __init__(self, name, value: bool):
239    assert type(value) is bool
240    self.name = name
241    self.value = value
242
243  def set(self, value: str):
244    self.value = bool(value)
245
246
247# Value can be string or other type
248class SimpleOption:
249
250  def __init__(self, name, value):
251    self.name = name
252    self.value = value
253
254  def set(self, value: str):
255    if type(self.value) == int:
256      self.value = int(value)
257    else:
258      self.value = value
259
260
261class Environment:
262
263  def set(self, var, val):
264    return
265
266  def append(self, var, val):
267    return
268
269
270class StaticLibrary:
271
272  def __init__(self, target_name, link_with=[], link_whole=[]):
273    self.target_name = target_name
274    self.link_with = link_with
275    self.link_whole = link_whole
276
277
278class SharedLibrary:
279  name = ''
280
281  def __init__(self, name):
282    self.name = name
283
284
285class Executable:
286
287  def __init__(self, name):
288    self.name = name
289
290
291class CustomTargetItem:
292
293  def __init__(self, custom_target, index):
294    self.target = custom_target
295    self.index = index
296
297
298class CustomTarget:
299
300  def __init__(self, name, outputs=[], generates_h=False, generates_c=False):
301    self._name = name
302    self._outputs = outputs
303    self._generates_h = generates_h
304    self._generates_c = generates_c
305
306  def generates_h(self):
307    return self._generates_h
308
309  def generates_c(self):
310    return self._generates_c
311
312  def target_name(self):
313    return self._name
314
315  def target_name_h(self):
316    if self._generates_h and self._generates_c:
317      return self._name + '.h'
318    return self._name
319
320  def target_name_c(self):
321    if self._generates_h and self._generates_c:
322      return self._name + '.c'
323    return self._name
324
325  def __iter__(self):
326    return iter([self])
327
328  def __getitem__(self, index):
329    return CustomTargetItem(self, index)
330
331  def full_path(self):
332    return 'fullpath'
333
334
335class Meson:
336
337  def __init__(self, compiler):
338    self._compiler = compiler
339
340  def get_compiler(self, language_string, native=False):
341    return self._compiler
342
343  def project_version(self):
344    return _gProjectVersion
345
346  def project_source_root(self):
347    return os.getcwd()
348
349  def is_cross_build(self):
350    return True
351
352  def can_run_host_binaries(self):
353    return False
354
355  def current_source_dir(self):
356    return os.getcwd()
357
358  def current_build_dir(self):
359    return '@CURRENT_BUILD_DIR@'
360
361  def project_build_root(self):
362    return '@PROJECT_BUILD_ROOT@'
363
364  def add_devenv(self, env):
365    return
366
367
368class Compiler:
369
370  def get_id(self):
371    return 'clang'
372
373  def is_symbol_supported(self, header: str, symbol: str):
374    if header == 'sys/mkdev.h' or symbol == 'program_invocation_name':
375      return False
376    return True
377
378  def is_function_supported(self, function: str):
379    if (
380        function == 'qsort_s'
381        or function == 'pthread_setaffinity_np'
382        or function == 'secure_getenv'
383    ):
384      return False
385    return True
386
387  def is_link_supported(self, name):
388    if name == 'GNU qsort_r' or name == 'BSD qsort_r':
389      return False
390    return True
391
392  def is_header_supported(self, header):
393    if (
394        header == 'xlocale.h'
395        or header == 'pthread_np.h'
396        or header == 'renderdoc_app.h'
397    ):
398      return False
399    return True
400
401  def get_define(self, define, prefix):
402    if define == 'ETIME':
403      return 'ETIME'
404    exit('Unhandled define: ' + define)
405
406  def get_supported_function_attributes(self, attributes):
407    # Assume all are supported
408    return attributes
409
410  def has_function_attribute(self, attribute):
411    return True
412
413  def has_argument(self, name):
414    result = True
415    print("has_argument '%s': %s" % (name, str(result)))
416    return result
417
418  def has_link_argument(self, name):
419    result = True
420    print("has_link_argument '%s': %s" % (name, str(result)))
421    return result
422
423  def compiles(self, snippet, name):
424    # Exclude what is currently not working.
425    result = True
426    if name == '__uint128_t':
427      result = False
428    print("compiles '%s': %s" % (name, str(result)))
429    return result
430
431  def has_member(sef, struct, member, prefix):
432    # Assume it does
433    return True
434
435  def get_argument_syntax(self):
436    return 'gcc'
437
438  def get_supported_arguments(self, args):
439    supported_args = []
440    for arg in args:
441      if (
442          arg.startswith('-flifetime-dse')
443          or arg.startswith('-Wno-format-truncation')
444          or arg.startswith('-Wno-nonnull-compare')
445          or arg.startswith('-Wno-class-memaccess')
446          or arg.startswith('-Wno-format-truncation')
447      ):
448        continue
449      supported_args.append(arg)
450    return supported_args
451
452  def get_supported_link_arguments(self, args):
453    return args
454
455  def find_library(self, name, required=False):
456    if name == 'ws2_32' or name == 'elf' or name == 'm' or name == 'sensors':
457      return Dependency(name, found=required)
458    exit('Unhandled library: ' + name)
459
460  def sizeof(self, string):
461    table = _get_sizeof_table()
462
463    if not string in table:
464      exit('Unhandled compiler sizeof: ' + string)
465    return table[string]
466
467
468###################################################################################################
469
470
471def fprint(args):
472  print(args, file=_gOutputFile)
473
474
475def set_relative_dir(dir):
476  global _gRelativeDir
477  _gRelativeDir = dir
478
479
480def open_output_file(name):
481  global _gOutputFile
482  _gOutputFile = open(name, 'w')
483
484
485def close_output_file():
486  global _gOutputFile
487  _gOutputFile.close()
488
489
490def get_relative_dir(path_or_file=''):
491  if isinstance(path_or_file, File):
492    return path_or_file.name
493
494  assert isinstance(path_or_file, str)
495  if path_or_file == '':
496    return _gRelativeDir
497  return os.path.join(_gRelativeDir, path_or_file)
498
499
500def get_relative_gen_dir(path=''):
501  return os.path.join(_gRelativeDir, path)
502
503
504def project(
505    name, language_list, version, license, meson_version, default_options
506):
507  if type(version) == str:
508    _gProjectVersion = version
509  else:
510    assert type(version) == list
511    version_file = version[0]
512    assert type(version_file) == File
513    with open(version_file.name, 'r') as file:
514      for line in file:
515        _gProjectVersion = line.strip()
516        break
517
518  for option in default_options:
519    value_pair = option.split('=')
520    _gProjectOptions.append(SimpleOption(value_pair[0], value_pair[1]))
521
522
523def get_project_options():
524  return _gProjectOptions
525
526
527def load_config_file(filename):
528  with open(filename, 'r') as file:
529    for line in file:
530      key, value = line.strip().split('=')
531      if key == 'cpu_family':
532        global _gCpuFamily
533        _gCpuFamily = value
534        print('Config: cpu_family=%s' % _gCpuFamily)
535      elif key == 'cpu':
536        global _gCpu
537        _gCpu = value
538        print('Config: cpu=%s' % _gCpu)
539      else:
540        exit('Unhandled config key: %s' % key)
541
542
543def add_project_arguments(args, language=[], native=False):
544  global _gProjectCflags, _gProjectCppflags
545  if not type(args) is list:
546    args = [args]
547  for l in language:
548    for arg in args:
549      if isinstance(arg, list):
550        add_project_arguments(arg, language=language, native=native)
551        continue
552      assert isinstance(arg, str)
553      if l == 'c':
554        print('cflags: ' + arg)
555        _gProjectCflags.append(arg)
556      elif l == 'cpp':
557        print('cppflags: ' + arg)
558        _gProjectCppflags.append(arg)
559      else:
560        exit('Unhandle arguments language: ' + l)
561
562
563def get_project_cflags():
564  return _gProjectCflags
565
566
567def get_project_cppflags():
568  return _gProjectCppflags
569
570
571def _get_sizeof_table():
572  table_32 = {'void*': 4}
573  table_64 = {'void*': 8}
574  if _gCpuFamily == 'arm':
575    table = table_32
576  elif _gCpuFamily == 'aarch64':
577    table = table_64
578  else:
579    exit('sizeof unhandled cpu family: %s' % _gCpuFamily)
580  return table
581
582
583def get_linear_list(arg_list):
584  args = []
585  for arg in arg_list:
586    if type(arg) == list:
587      args.extend(get_linear_list(arg))
588    else:
589      args.append(arg)
590  return args
591
592
593def dependency(*names, required=True, version=''):
594  for name in names:
595    print('dependency: %s' % name)
596    if name == '':
597      return Dependency('null', version, found=False)
598
599    if (
600        name == 'backtrace'
601        or name == 'curses'
602        or name == 'expat'
603        or name == 'libconfig'
604        or name == 'libmagma_virt'
605        or name == 'libva'
606        or name == 'libzstd'
607        or name == 'libdrm'
608        or name == 'libglvnd'
609        or name == 'libudev'
610        or name == 'libunwind'
611        or name == 'llvm'
612        or name == 'libxml-2.0'
613        or name == 'lua54'
614        or name == 'valgrind'
615        or name == 'wayland-scanner'
616    ):
617      return Dependency(name, version, found=False)
618
619    if (
620        name == ''
621        or name == 'libarchive'
622        or name == 'libelf'
623        or name == 'threads'
624        or name == 'vdpau'
625    ):
626      return Dependency(name, version, found=required)
627
628    exit('Unhandled dependency: ' + name)
629
630
631def get_set_of_deps(deps, set_of_deps=set()):
632  for dep in deps:
633    if type(dep) == list:
634      set_of_deps = get_set_of_deps(dep, set_of_deps)
635    elif dep not in set_of_deps:
636      set_of_deps.add(dep)
637      set_of_deps = get_set_of_deps(dep.dependencies, set_of_deps)
638  return set_of_deps
639
640
641def get_include_dirs(paths) -> list[str]:
642  dir_list = []
643  for path in paths:
644    if type(path) is list:
645      dir_list.extend(get_include_dirs(p for p in path))
646    elif type(path) is IncludeDirectories:
647      dir_list.extend(path.dirs)
648    else:
649      assert type(path) is str
650      dir_list.append(get_relative_dir(path))
651  return dir_list
652
653
654def get_include_directories(includes) -> list[IncludeDirectories]:
655  dirs = []
656  if type(includes) == list:
657    for inc in includes:
658      dirs.extend(get_include_directories(inc))
659  elif type(includes) == IncludeDirectories:
660    dirs.extend(includes)
661  else:
662    assert type(includes) == str
663    exit('get_include_directories got string: %s' % includes)
664  return dirs
665
666
667def get_static_libs(arg_list):
668  libs = []
669  for arg in arg_list:
670    if type(arg) == list:
671      libs.extend(get_static_libs(arg))
672    else:
673      assert type(arg) == StaticLibrary
674      libs.extend(get_static_libs(arg.link_with))
675      libs.append(arg)
676  return libs
677
678
679def get_whole_static_libs(arg_list):
680  libs = []
681  for arg in arg_list:
682    if type(arg) == list:
683      libs.extend(get_whole_static_libs(arg))
684    else:
685      assert type(arg) == StaticLibrary
686      libs.extend(get_whole_static_libs(arg._link_whole))
687      libs.append(arg)
688  return libs
689
690
691def get_list_of_relative_inputs(list_or_string):
692  if isinstance(list_or_string, list):
693    ret = []
694    for item in list_or_string:
695      ret.extend(get_list_of_relative_inputs(item))
696    return ret
697
698  return [get_relative_dir(list_or_string)]
699
700
701def get_command_line_from_args(args: list):
702  command_line = ''
703  for arg in args:
704    command_line += ' ' + arg
705  # Escape angle brackets
706  command_line = re.sub(r'(<|>)', '\\\\\\\\\g<1>', command_line)
707  return command_line
708
709
710def replace_wrapped_input_with_target(
711    args, python_script, python_script_target_name
712):
713  outargs = []
714  for index, arg in enumerate(args):
715    pattern = '(.*?)(' + python_script + ')'
716    replace = '\g<1>' + python_script_target_name
717    outargs.append(re.sub(pattern, replace, arg))
718  return outargs
719