cmakelib.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. from __future__ import print_function
  2. import sys, subprocess, json
  3. termwidth = 150
  4. print_communication = True
  5. def ordered(obj):
  6. if isinstance(obj, dict):
  7. return sorted((k, ordered(v)) for k, v in obj.items())
  8. if isinstance(obj, list):
  9. return sorted(ordered(x) for x in obj)
  10. else:
  11. return obj
  12. def col_print(title, array):
  13. print()
  14. print()
  15. print(title)
  16. indentwidth = 4
  17. indent = " " * indentwidth
  18. if not array:
  19. print(indent + "<None>")
  20. return
  21. padwidth = 2
  22. maxitemwidth = len(max(array, key=len))
  23. numCols = max(1, int((termwidth - indentwidth + padwidth) / (maxitemwidth + padwidth)))
  24. numRows = len(array) // numCols + 1
  25. pad = " " * padwidth
  26. for index in range(numRows):
  27. print(indent + pad.join(item.ljust(maxitemwidth) for item in array[index::numRows]))
  28. def waitForRawMessage(cmakeCommand):
  29. stdoutdata = ""
  30. payload = ""
  31. while not cmakeCommand.poll():
  32. stdoutdataLine = cmakeCommand.stdout.readline()
  33. if stdoutdataLine:
  34. stdoutdata += stdoutdataLine.decode('utf-8')
  35. else:
  36. break
  37. begin = stdoutdata.find('[== "CMake Server" ==[\n')
  38. end = stdoutdata.find(']== "CMake Server" ==]')
  39. if (begin != -1 and end != -1):
  40. begin += len('[== "CMake Server" ==[\n')
  41. payload = stdoutdata[begin:end]
  42. if print_communication:
  43. print("\nSERVER>", json.loads(payload), "\n")
  44. return json.loads(payload)
  45. def writeRawData(cmakeCommand, content):
  46. writeRawData.counter += 1
  47. payload = """
  48. [== "CMake Server" ==[
  49. %s
  50. ]== "CMake Server" ==]
  51. """ % content
  52. rn = ( writeRawData.counter % 2 ) == 0
  53. if rn:
  54. payload = payload.replace('\n', '\r\n')
  55. if print_communication:
  56. print("\nCLIENT>", content, "(Use \\r\\n:", rn, ")\n")
  57. cmakeCommand.stdin.write(payload.encode('utf-8'))
  58. cmakeCommand.stdin.flush()
  59. writeRawData.counter = 0
  60. def writePayload(cmakeCommand, obj):
  61. writeRawData(cmakeCommand, json.dumps(obj))
  62. def initProc(cmakeCommand):
  63. cmakeCommand = subprocess.Popen([cmakeCommand, "-E", "server", "--experimental", "--debug"],
  64. stdin=subprocess.PIPE,
  65. stdout=subprocess.PIPE)
  66. packet = waitForRawMessage(cmakeCommand)
  67. if packet == None:
  68. print("Not in server mode")
  69. sys.exit(1)
  70. if packet['type'] != 'hello':
  71. print("No hello message")
  72. sys.exit(1)
  73. return cmakeCommand
  74. def exitProc(cmakeCommand):
  75. # Tell the server to exit.
  76. cmakeCommand.stdin.close()
  77. cmakeCommand.stdout.close()
  78. # Wait for the server to exit.
  79. # If this version of python supports it, terminate the server after a timeout.
  80. try:
  81. cmakeCommand.wait(timeout=5)
  82. except TypeError:
  83. cmakeCommand.wait()
  84. except:
  85. cmakeCommand.terminate()
  86. raise
  87. def waitForMessage(cmakeCommand, expected):
  88. data = ordered(expected)
  89. packet = ordered(waitForRawMessage(cmakeCommand))
  90. if packet != data:
  91. sys.exit(-1)
  92. return packet
  93. def waitForReply(cmakeCommand, originalType, cookie, skipProgress):
  94. gotResult = False
  95. while True:
  96. packet = waitForRawMessage(cmakeCommand)
  97. t = packet['type']
  98. if packet['cookie'] != cookie or packet['inReplyTo'] != originalType:
  99. sys.exit(1)
  100. if t == 'message' or t == 'progress':
  101. if skipProgress:
  102. continue
  103. if t == 'reply':
  104. break
  105. sys.exit(1)
  106. return packet
  107. def waitForError(cmakeCommand, originalType, cookie, message):
  108. packet = waitForRawMessage(cmakeCommand)
  109. if packet['cookie'] != cookie or packet['type'] != 'error' or packet['inReplyTo'] != originalType or packet['errorMessage'] != message:
  110. sys.exit(1)
  111. def waitForProgress(cmakeCommand, originalType, cookie, current, message):
  112. packet = waitForRawMessage(cmakeCommand)
  113. if packet['cookie'] != cookie or packet['type'] != 'progress' or packet['inReplyTo'] != originalType or packet['progressCurrent'] != current or packet['progressMessage'] != message:
  114. sys.exit(1)
  115. def handshake(cmakeCommand, major, minor, source, build, generator, extraGenerator):
  116. version = { 'major': major }
  117. if minor >= 0:
  118. version['minor'] = minor
  119. writePayload(cmakeCommand, { 'type': 'handshake', 'protocolVersion': version,
  120. 'cookie': 'TEST_HANDSHAKE', 'sourceDirectory': source, 'buildDirectory': build,
  121. 'generator': generator, 'extraGenerator': extraGenerator })
  122. waitForReply(cmakeCommand, 'handshake', 'TEST_HANDSHAKE', False)
  123. def validateGlobalSettings(cmakeCommand, cmakeCommandPath, data):
  124. packet = waitForReply(cmakeCommand, 'globalSettings', '', False)
  125. capabilities = packet['capabilities']
  126. # validate version:
  127. cmakeoutput = subprocess.check_output([ cmakeCommandPath, "--version" ], universal_newlines=True)
  128. cmakeVersion = cmakeoutput.splitlines()[0][14:]
  129. version = capabilities['version']
  130. versionString = version['string']
  131. vs = str(version['major']) + '.' + str(version['minor']) + '.' + str(version['patch'])
  132. if (versionString != vs and not versionString.startswith(vs + '-')):
  133. sys.exit(1)
  134. if (versionString != cmakeVersion):
  135. sys.exit(1)
  136. # validate generators:
  137. generatorObjects = capabilities['generators']
  138. cmakeoutput = subprocess.check_output([ cmakeCommandPath, "--help" ], universal_newlines=True)
  139. index = cmakeoutput.index('\nGenerators\n\n')
  140. cmakeGenerators = []
  141. for line in cmakeoutput[index + 12:].splitlines():
  142. if not line.startswith(' '):
  143. continue
  144. if line.startswith(' '):
  145. continue
  146. equalPos = line.find('=')
  147. tmp = ''
  148. if (equalPos > 0):
  149. tmp = line[2:equalPos].strip()
  150. else:
  151. tmp = line.strip()
  152. if tmp.endswith(" [arch]"):
  153. tmp = tmp[0:len(tmp) - 7]
  154. if (len(tmp) > 0) and (" - " not in tmp) and (tmp != 'KDevelop3'):
  155. cmakeGenerators.append(tmp)
  156. generators = []
  157. for genObj in generatorObjects:
  158. generators.append(genObj['name'])
  159. generators.sort()
  160. cmakeGenerators.sort()
  161. for gen in cmakeGenerators:
  162. if (not gen in generators):
  163. sys.exit(1)
  164. gen = packet['generator']
  165. if (gen != '' and not (gen in generators)):
  166. sys.exit(1)
  167. for i in data:
  168. print("Validating", i)
  169. if (packet[i] != data[i]):
  170. sys.exit(1)
  171. def validateCache(cmakeCommand, data):
  172. packet = waitForReply(cmakeCommand, 'cache', '', False)
  173. cache = packet['cache']
  174. if (data['isEmpty']):
  175. if (cache != []):
  176. print('Expected empty cache, but got data.\n')
  177. sys.exit(1)
  178. return;
  179. if (cache == []):
  180. print('Expected cache contents, but got none.\n')
  181. sys.exit(1)
  182. hadHomeDir = False
  183. for value in cache:
  184. if (value['key'] == 'CMAKE_HOME_DIRECTORY'):
  185. hadHomeDir = True
  186. if (not hadHomeDir):
  187. print('No CMAKE_HOME_DIRECTORY found in cache.')
  188. sys.exit(1)