My Marlin configs for Fabrikator Mini and CTC i3 Pro B
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

signature.py 5.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. #
  2. # signature.py
  3. #
  4. import os,subprocess,re,json,hashlib
  5. #
  6. # The dumbest preprocessor in the world
  7. # Extract macro name from an header file and store them in an array
  8. # No processing is done here, so they are raw values here and it does not match what actually enabled
  9. # in the file (since you can have #if SOMETHING_UNDEFINED / #define BOB / #endif)
  10. # But it's useful to filter the useful macro spit out by the preprocessor from noise from the system
  11. # headers.
  12. #
  13. def extract_defines(filepath):
  14. f = open(filepath, encoding="utf8").read().split("\n")
  15. a = []
  16. for line in f:
  17. sline = line.strip(" \t\n\r")
  18. if sline[:7] == "#define":
  19. # Extract the key here (we don't care about the value)
  20. kv = sline[8:].strip().split(' ')
  21. a.append(kv[0])
  22. return a
  23. # Compute the SHA256 hash of a file
  24. def get_file_sha256sum(filepath):
  25. sha256_hash = hashlib.sha256()
  26. with open(filepath,"rb") as f:
  27. # Read and update hash string value in blocks of 4K
  28. for byte_block in iter(lambda: f.read(4096),b""):
  29. sha256_hash.update(byte_block)
  30. return sha256_hash.hexdigest()
  31. #
  32. # Compress a JSON file into a zip file
  33. #
  34. import zipfile
  35. def compress_file(filepath, outputbase):
  36. with zipfile.ZipFile(outputbase + '.zip', 'w', compression=zipfile.ZIP_BZIP2, compresslevel=9) as zipf:
  37. zipf.write(filepath, compress_type=zipfile.ZIP_BZIP2, compresslevel=9)
  38. #
  39. # Compute the build signature. The idea is to extract all defines in the configuration headers
  40. # to build a unique reversible signature from this build so it can be included in the binary
  41. # We can reverse the signature to get a 1:1 equivalent configuration file
  42. #
  43. def compute_build_signature(env):
  44. if 'BUILD_SIGNATURE' in env:
  45. return
  46. # Definitions from these files will be kept
  47. files_to_keep = [ 'Marlin/Configuration.h', 'Marlin/Configuration_adv.h' ]
  48. build_dir=os.path.join(env['PROJECT_BUILD_DIR'], env['PIOENV'])
  49. # Check if we can skip processing
  50. hashes = ''
  51. for header in files_to_keep:
  52. hashes += get_file_sha256sum(header)[0:10]
  53. marlin_json = os.path.join(build_dir, 'marlin_config.json')
  54. marlin_zip = os.path.join(build_dir, 'mc')
  55. # Read existing config file
  56. try:
  57. with open(marlin_json, 'r') as infile:
  58. conf = json.load(infile)
  59. if conf['__INITIAL_HASH'] == hashes:
  60. # Same configuration, skip recomputing the building signature
  61. compress_file(marlin_json, marlin_zip)
  62. return
  63. except:
  64. pass
  65. # Get enabled config options based on preprocessor
  66. from preprocessor import run_preprocessor
  67. complete_cfg = run_preprocessor(env)
  68. # Dumb #define extraction from the configuration files
  69. real_defines = {}
  70. all_defines = []
  71. for header in files_to_keep:
  72. defines = extract_defines(header)
  73. # To filter only the define we want
  74. all_defines = all_defines + defines
  75. # To remember from which file it cames from
  76. real_defines[header.split('/')[-1]] = defines
  77. r = re.compile(r"\(+(\s*-*\s*_.*)\)+")
  78. # First step is to collect all valid macros
  79. defines = {}
  80. for line in complete_cfg:
  81. # Split the define from the value
  82. key_val = line[8:].strip().decode().split(' ')
  83. key, value = key_val[0], ' '.join(key_val[1:])
  84. # Ignore values starting with two underscore, since it's low level
  85. if len(key) > 2 and key[0:2] == "__" :
  86. continue
  87. # Ignore values containing a parenthesis (likely a function macro)
  88. if '(' in key and ')' in key:
  89. continue
  90. # Then filter dumb values
  91. if r.match(value):
  92. continue
  93. defines[key] = value if len(value) else ""
  94. if not 'CONFIGURATION_EMBEDDING' in defines:
  95. return
  96. # Second step is to filter useless macro
  97. resolved_defines = {}
  98. for key in defines:
  99. # Remove all boards now
  100. if key[0:6] == "BOARD_" and key != "BOARD_INFO_NAME":
  101. continue
  102. # Remove all keys ending by "_NAME" as it does not make a difference to the configuration
  103. if key[-5:] == "_NAME" and key != "CUSTOM_MACHINE_NAME":
  104. continue
  105. # Remove all keys ending by "_T_DECLARED" as it's a copy of not important system stuff
  106. if key[-11:] == "_T_DECLARED":
  107. continue
  108. # Remove keys that are not in the #define list in the Configuration list
  109. if not (key in all_defines) and key != "DETAILED_BUILD_VERSION" and key != "STRING_DISTRIBUTION_DATE":
  110. continue
  111. # Don't be that smart guy here
  112. resolved_defines[key] = defines[key]
  113. # Generate a build signature now
  114. # We are making an object that's a bit more complex than a basic dictionary here
  115. data = {}
  116. data['__INITIAL_HASH'] = hashes
  117. # First create a key for each header here
  118. for header in real_defines:
  119. data[header] = {}
  120. # Then populate the object where each key is going to (that's a O(N^2) algorithm here...)
  121. for key in resolved_defines:
  122. for header in real_defines:
  123. if key in real_defines[header]:
  124. data[header][key] = resolved_defines[key]
  125. # Append the source code version and date
  126. data['VERSION'] = {}
  127. data['VERSION']['DETAILED_BUILD_VERSION'] = resolved_defines['DETAILED_BUILD_VERSION']
  128. data['VERSION']['STRING_DISTRIBUTION_DATE'] = resolved_defines['STRING_DISTRIBUTION_DATE']
  129. try:
  130. curver = subprocess.check_output(["git", "describe", "--match=NeVeRmAtCh", "--always"]).strip()
  131. data['VERSION']['GIT_REF'] = curver.decode()
  132. except:
  133. pass
  134. with open(marlin_json, 'w') as outfile:
  135. json.dump(data, outfile, separators=(',', ':'))
  136. # Compress the JSON file as much as we can
  137. compress_file(marlin_json, marlin_zip)
  138. # Generate a C source file for storing this array
  139. with open('Marlin/src/mczip.h','wb') as result_file:
  140. result_file.write(b'#ifndef NO_CONFIGURATION_EMBEDDING_WARNING\n')
  141. result_file.write(b' #warning "Generated file \'mc.zip\' is embedded (Define NO_CONFIGURATION_EMBEDDING_WARNING to suppress this warning.)"\n')
  142. result_file.write(b'#endif\n')
  143. result_file.write(b'const unsigned char mc_zip[] PROGMEM = {\n ')
  144. count = 0
  145. for b in open(os.path.join(build_dir, 'mc.zip'), 'rb').read():
  146. result_file.write(b' 0x%02X,' % b)
  147. count += 1
  148. if (count % 16 == 0):
  149. result_file.write(b'\n ')
  150. if (count % 16):
  151. result_file.write(b'\n')
  152. result_file.write(b'};\n')