process_whole_archive_option.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. import os
  2. import process_command_files as pcf
  3. class ProcessWholeArchiveOption:
  4. def __init__(self, arch, peers=None, libs=None):
  5. self.arch = arch.upper()
  6. self.peers = {x: 0 for x in peers} if peers else None
  7. self.libs = {x: 0 for x in libs} if libs else None
  8. self.start_wa_marker = '--start-wa'
  9. self.end_wa_marker = '--end-wa'
  10. def _match_peer_lib(self, arg, ext):
  11. key = None
  12. if arg.endswith(ext):
  13. key = os.path.dirname(arg)
  14. return key if key and self.peers and key in self.peers else None
  15. def _match_lib(self, arg):
  16. return arg if self.libs and arg in self.libs else None
  17. def _process_arg(self, arg, ext='.a'):
  18. peer_key = self._match_peer_lib(arg, ext)
  19. lib_key = self._match_lib(arg)
  20. if peer_key:
  21. self.peers[peer_key] += 1
  22. if lib_key:
  23. self.libs[lib_key] += 1
  24. return peer_key if peer_key else lib_key
  25. def _check_peers(self):
  26. if self.peers:
  27. for key, value in self.peers.items():
  28. assert value > 0, '"{}" specified in WHOLE_ARCHIVE() macro is not used on link command'.format(key)
  29. def _construct_cmd_apple(self, args):
  30. force_load_flag = '-Wl,-force_load,'
  31. is_inside_wa_markers = False
  32. cmd = []
  33. for arg in args:
  34. if arg.startswith(force_load_flag):
  35. cmd.append(arg)
  36. elif arg == self.start_wa_marker:
  37. is_inside_wa_markers = True
  38. elif arg == self.end_wa_marker:
  39. is_inside_wa_markers = False
  40. elif is_inside_wa_markers:
  41. cmd.append(force_load_flag + arg)
  42. else:
  43. key = self._process_arg(arg)
  44. cmd.append(force_load_flag + arg if key else arg)
  45. self._check_peers()
  46. return cmd
  47. def _construct_cmd_win(self, args):
  48. whole_archive_prefix = '/WHOLEARCHIVE:'
  49. is_inside_wa_markers = False
  50. def add_prefix(arg, need_check_peers_and_libs):
  51. key = self._process_arg(arg, '.lib') if need_check_peers_and_libs else arg
  52. return whole_archive_prefix + arg if key else arg
  53. def add_whole_archive_prefix(arg, need_check_peers_and_libs):
  54. if not pcf.is_cmdfile_arg(arg):
  55. return add_prefix(arg, need_check_peers_and_libs)
  56. cmd_file_path = pcf.cmdfile_path(arg)
  57. cf_args = pcf.read_from_command_file(cmd_file_path)
  58. with open(cmd_file_path, 'w') as afile:
  59. for cf_arg in cf_args:
  60. afile.write(add_prefix(cf_arg, need_check_peers_and_libs) + "\n")
  61. return arg
  62. cmd = []
  63. for arg in args:
  64. if arg == self.start_wa_marker:
  65. is_inside_wa_markers = True
  66. elif arg == self.end_wa_marker:
  67. is_inside_wa_markers = False
  68. elif is_inside_wa_markers:
  69. cmd.append(add_whole_archive_prefix(arg, False))
  70. continue
  71. elif self.peers or self.libs:
  72. cmd.append(add_whole_archive_prefix(arg, True))
  73. else:
  74. cmd.append(arg)
  75. self._check_peers()
  76. return cmd
  77. def _construct_cmd_linux(self, args):
  78. whole_archive_flag = '-Wl,--whole-archive'
  79. no_whole_archive_flag = '-Wl,--no-whole-archive'
  80. def replace_markers(arg):
  81. if arg == self.start_wa_marker:
  82. return whole_archive_flag
  83. if arg == self.end_wa_marker:
  84. return no_whole_archive_flag
  85. return arg
  86. args = [replace_markers(arg) for arg in args]
  87. cmd = []
  88. is_inside_whole_archive = False
  89. is_whole_archive = False
  90. # We are trying not to create excessive sequences of consecutive flags
  91. # -Wl,--no-whole-archive -Wl,--whole-archive ('externally' specified
  92. # flags -Wl,--[no-]whole-archive are not taken for consideration in this
  93. # optimization intentionally)
  94. for arg in args:
  95. if arg == whole_archive_flag:
  96. is_inside_whole_archive = True
  97. is_whole_archive = False
  98. elif arg == no_whole_archive_flag:
  99. is_inside_whole_archive = False
  100. is_whole_archive = False
  101. else:
  102. key = self._process_arg(arg)
  103. if not is_inside_whole_archive:
  104. if key:
  105. if not is_whole_archive:
  106. cmd.append(whole_archive_flag)
  107. is_whole_archive = True
  108. elif is_whole_archive:
  109. cmd.append(no_whole_archive_flag)
  110. is_whole_archive = False
  111. cmd.append(arg)
  112. if is_whole_archive:
  113. cmd.append(no_whole_archive_flag)
  114. # There can be an empty sequence of archive files between
  115. # -Wl, --whole-archive and -Wl, --no-whole-archive flags.
  116. # As a result an unknown option error may occur, therefore to
  117. # prevent this case we need to remove both flags from cmd.
  118. # These flags affects only on subsequent archive files.
  119. if len(cmd) == 2:
  120. return []
  121. self._check_peers()
  122. return cmd
  123. def construct_cmd(self, args):
  124. if self.arch in ('DARWIN', 'IOS', 'IOSSIM'):
  125. return self._construct_cmd_apple(args)
  126. if self.arch == 'WINDOWS':
  127. return self._construct_cmd_win(args)
  128. return self._construct_cmd_linux(args)
  129. def get_whole_archive_peers_and_libs(args):
  130. remaining_args = []
  131. peers = []
  132. libs = []
  133. peers_flag = '--whole-archive-peers'
  134. libs_flag = '--whole-archive-libs'
  135. next_is_peer = False
  136. next_is_lib = False
  137. for arg in args:
  138. if arg == peers_flag:
  139. next_is_peer = True
  140. elif arg == libs_flag:
  141. next_is_lib = True
  142. elif next_is_peer:
  143. peers.append(arg)
  144. next_is_peer = False
  145. elif next_is_lib:
  146. libs.append(arg)
  147. next_is_lib = False
  148. else:
  149. remaining_args.append(arg)
  150. return remaining_args, peers, libs