process_whole_archive_option.py 6.3 KB

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