瀏覽代碼

✨ Configurations embed and retrieve (#21321)

X-Ryl669 3 年之前
父節點
當前提交
b464a4b1a4
沒有連結到貢獻者的電子郵件帳戶。

+ 3
- 0
.gitignore 查看文件

22
 # Generated files
22
 # Generated files
23
 _Version.h
23
 _Version.h
24
 bdf2u8g
24
 bdf2u8g
25
+marlin_config.json
26
+mczip.h
27
+*.gen
25
 
28
 
26
 #
29
 #
27
 # OS
30
 # OS

+ 8
- 0
Marlin/Configuration_adv.h 查看文件

1593
     #define SD_FIRMWARE_UPDATE_INACTIVE_VALUE 0xFF
1593
     #define SD_FIRMWARE_UPDATE_INACTIVE_VALUE 0xFF
1594
   #endif
1594
   #endif
1595
 
1595
 
1596
+  /**
1597
+   * Enable this option if you have more than ~3K of unused flash space.
1598
+   * Marlin will embed all settings in the firmware binary as compressed data.
1599
+   * Use 'M503 C' to write the settings out to the SD Card as 'mc.zip'.
1600
+   * See docs/ConfigEmbedding.md for details on how to use 'mc-apply.py'.
1601
+   */
1602
+  //#define CONFIGURATION_EMBEDDING
1603
+
1596
   // Add an optimized binary file transfer mode, initiated with 'M28 B1'
1604
   // Add an optimized binary file transfer mode, initiated with 'M28 B1'
1597
   //#define BINARY_FILE_TRANSFER
1605
   //#define BINARY_FILE_TRANSFER
1598
 
1606
 

+ 18
- 0
Marlin/src/gcode/eeprom/M500-M504.cpp 查看文件

25
 #include "../../core/serial.h"
25
 #include "../../core/serial.h"
26
 #include "../../inc/MarlinConfig.h"
26
 #include "../../inc/MarlinConfig.h"
27
 
27
 
28
+#if ENABLED(CONFIGURATION_EMBEDDING)
29
+  #include "../../sd/SdBaseFile.h"
30
+  #include "../../mczip.h"
31
+#endif
32
+
28
 /**
33
 /**
29
  * M500: Store settings in EEPROM
34
  * M500: Store settings in EEPROM
30
  */
35
  */
50
 
55
 
51
   /**
56
   /**
52
    * M503: print settings currently in memory
57
    * M503: print settings currently in memory
58
+   *
59
+   * With CONFIGURATION_EMBEDDING:
60
+   *   C<flag> : Save the full Marlin configuration to SD Card as "mc.zip"
53
    */
61
    */
54
   void GcodeSuite::M503() {
62
   void GcodeSuite::M503() {
55
     (void)settings.report(!parser.boolval('S', true));
63
     (void)settings.report(!parser.boolval('S', true));
64
+
65
+    #if ENABLED(CONFIGURATION_EMBEDDING)
66
+      if (parser.seen_test('C')) {
67
+        SdBaseFile file;
68
+        const uint16_t size = sizeof(mc_zip);
69
+        // Need to create the config size on the SD card
70
+        if (file.open("mc.zip", O_WRITE|O_CREAT) && file.write(pgm_read_ptr(mc_zip), size) != -1 && file.close())
71
+          SERIAL_ECHO_MSG("Configuration saved as 'mc.zip'");
72
+      }
73
+    #endif
56
   }
74
   }
57
 
75
 
58
 #endif // !DISABLE_M503
76
 #endif // !DISABLE_M503

+ 21
- 7
Marlin/src/gcode/host/M115.cpp 查看文件

24
 #include "../../inc/MarlinConfig.h"
24
 #include "../../inc/MarlinConfig.h"
25
 #include "../queue.h"           // for getting the command port
25
 #include "../queue.h"           // for getting the command port
26
 
26
 
27
-
28
 #if ENABLED(M115_GEOMETRY_REPORT)
27
 #if ENABLED(M115_GEOMETRY_REPORT)
29
   #include "../../module/motion.h"
28
   #include "../../module/motion.h"
30
 #endif
29
 #endif
33
   #include "../../feature/caselight.h"
32
   #include "../../feature/caselight.h"
34
 #endif
33
 #endif
35
 
34
 
35
+//#define MINIMAL_CAP_LINES // Don't even mention the disabled capabilities
36
+
36
 #if ENABLED(EXTENDED_CAPABILITIES_REPORT)
37
 #if ENABLED(EXTENDED_CAPABILITIES_REPORT)
37
-  static void cap_line(FSTR_P const name, bool ena=false) {
38
-    SERIAL_ECHOPGM("Cap:");
39
-    SERIAL_ECHOF(name);
40
-    SERIAL_CHAR(':', '0' + ena);
41
-    SERIAL_EOL();
42
-  }
38
+  #if ENABLED(MINIMAL_CAP_LINES)
39
+    #define cap_line(S,C) if (C) _cap_line(S)
40
+    static void _cap_line(FSTR_P const name) {
41
+      SERIAL_ECHOPGM("Cap:");
42
+      SERIAL_ECHOF(name);
43
+      SERIAL_ECHOLNPGM(":1");
44
+    }
45
+  #else
46
+    #define cap_line(V...) _cap_line(V)
47
+    static void _cap_line(FSTR_P const name, bool ena=false) {
48
+      SERIAL_ECHOPGM("Cap:");
49
+      SERIAL_ECHOF(name);
50
+      SERIAL_CHAR(':', '0' + ena);
51
+      SERIAL_EOL();
52
+    }
53
+  #endif
43
 #endif
54
 #endif
44
 
55
 
45
 /**
56
 /**
167
     // MEATPACK Compression
178
     // MEATPACK Compression
168
     cap_line(F("MEATPACK"), SERIAL_IMPL.has_feature(port, SerialFeature::MeatPack));
179
     cap_line(F("MEATPACK"), SERIAL_IMPL.has_feature(port, SerialFeature::MeatPack));
169
 
180
 
181
+    // CONFIG_EXPORT
182
+    cap_line(F("CONFIG_EXPORT"), ENABLED(CONFIG_EMBED_AND_SAVE_TO_SD));
183
+
170
     // Machine Geometry
184
     // Machine Geometry
171
     #if ENABLED(M115_GEOMETRY_REPORT)
185
     #if ENABLED(M115_GEOMETRY_REPORT)
172
       const xyz_pos_t bmin = { 0, 0, 0 },
186
       const xyz_pos_t bmin = { 0, 0, 0 },

+ 6
- 0
Marlin/src/inc/Conditionals_adv.h 查看文件

1004
 #if EITHER(MEATPACK_ON_SERIAL_PORT_1, MEATPACK_ON_SERIAL_PORT_2)
1004
 #if EITHER(MEATPACK_ON_SERIAL_PORT_1, MEATPACK_ON_SERIAL_PORT_2)
1005
   #define HAS_MEATPACK 1
1005
   #define HAS_MEATPACK 1
1006
 #endif
1006
 #endif
1007
+
1008
+// AVR are (usually) too limited in resources to store the configuration into the binary
1009
+#if !defined(FORCE_CONFIG_EMBED) && (defined(__AVR__) || DISABLED(SDSUPPORT) || EITHER(SDCARD_READONLY, DISABLE_M503))
1010
+  #undef CONFIGURATION_EMBEDDING
1011
+  #define CANNOT_EMBED_CONFIGURATION defined(__AVR__)
1012
+#endif

+ 4
- 0
Marlin/src/inc/Warnings.cpp 查看文件

549
 #elif !USE_SENSORLESS && ENABLED(USES_DIAG_PINS)
549
 #elif !USE_SENSORLESS && ENABLED(USES_DIAG_PINS)
550
   #warning "Driver DIAG pins must be physically removed unless SENSORLESS_HOMING is enabled. (See https://bit.ly/2ZPRlt0)"
550
   #warning "Driver DIAG pins must be physically removed unless SENSORLESS_HOMING is enabled. (See https://bit.ly/2ZPRlt0)"
551
 #endif
551
 #endif
552
+
553
+#if CANNOT_EMBED_CONFIGURATION
554
+  #warning "Disabled CONFIGURATION_EMBEDDING because the target usually has less flash storage. Define FORCE_CONFIG_EMBED to override."
555
+#endif

+ 1
- 1
buildroot/bin/restore_configs 查看文件

2
 
2
 
3
 git checkout Marlin/Configuration*.h 2>/dev/null
3
 git checkout Marlin/Configuration*.h 2>/dev/null
4
 git checkout Marlin/src/pins/ramps/pins_RAMPS.h 2>/dev/null
4
 git checkout Marlin/src/pins/ramps/pins_RAMPS.h 2>/dev/null
5
-rm -f Marlin/_Bootscreen.h Marlin/_Statusscreen.h
5
+rm -f Marlin/_Bootscreen.h Marlin/_Statusscreen.h marlin_config.json .pio/build/mc.zip

+ 0
- 1
buildroot/share/PlatformIO/scripts/SAMD51_grandcentral_m4.py 查看文件

6
 if pioutil.is_pio_build():
6
 if pioutil.is_pio_build():
7
 	from os.path import join, isfile
7
 	from os.path import join, isfile
8
 	import shutil
8
 	import shutil
9
-	from pprint import pprint
10
 
9
 
11
 	Import("env")
10
 	Import("env")
12
 
11
 

+ 0
- 0
buildroot/share/PlatformIO/scripts/__init__.py 查看文件


+ 11
- 76
buildroot/share/PlatformIO/scripts/common-dependencies.py 查看文件

193
 				set_env_field('lib_ignore', lib_ignore)
193
 				set_env_field('lib_ignore', lib_ignore)
194
 
194
 
195
 	#
195
 	#
196
-	# Find a compiler, considering the OS
197
-	#
198
-	ENV_BUILD_PATH = os.path.join(env.Dictionary('PROJECT_BUILD_DIR'), env['PIOENV'])
199
-	GCC_PATH_CACHE = os.path.join(ENV_BUILD_PATH, ".gcc_path")
200
-	def search_compiler():
201
-		try:
202
-			filepath = env.GetProjectOption('custom_gcc')
203
-			blab("Getting compiler from env")
204
-			return filepath
205
-		except:
206
-			pass
207
-
208
-		if os.path.exists(GCC_PATH_CACHE):
209
-			with open(GCC_PATH_CACHE, 'r') as f:
210
-				return f.read()
211
-
212
-		# Find the current platform compiler by searching the $PATH
213
-		# which will be in a platformio toolchain bin folder
214
-		path_regex = re.escape(env['PROJECT_PACKAGES_DIR'])
215
-
216
-		# See if the environment provides a default compiler
217
-		try:
218
-			gcc = env.GetProjectOption('custom_deps_gcc')
219
-		except:
220
-			gcc = "g++"
221
-
222
-		if env['PLATFORM'] == 'win32':
223
-			path_separator = ';'
224
-			path_regex += r'.*\\bin'
225
-			gcc += ".exe"
226
-		else:
227
-			path_separator = ':'
228
-			path_regex += r'/.+/bin'
229
-
230
-		# Search for the compiler
231
-		for pathdir in env['ENV']['PATH'].split(path_separator):
232
-			if not re.search(path_regex, pathdir, re.IGNORECASE):
233
-				continue
234
-			for filepath in os.listdir(pathdir):
235
-				if not filepath.endswith(gcc):
236
-					continue
237
-				# Use entire path to not rely on env PATH
238
-				filepath = os.path.sep.join([pathdir, filepath])
239
-				# Cache the g++ path to no search always
240
-				if os.path.exists(ENV_BUILD_PATH):
241
-					with open(GCC_PATH_CACHE, 'w+') as f:
242
-						f.write(filepath)
243
-
244
-				return filepath
245
-
246
-		filepath = env.get('CXX')
247
-		if filepath == 'CC':
248
-			filepath = gcc
249
-		blab("Couldn't find a compiler! Fallback to %s" % filepath)
250
-		return filepath
251
-
252
-	#
253
 	# Use the compiler to get a list of all enabled features
196
 	# Use the compiler to get a list of all enabled features
254
 	#
197
 	#
255
 	def load_marlin_features():
198
 	def load_marlin_features():
257
 			return
200
 			return
258
 
201
 
259
 		# Process defines
202
 		# Process defines
260
-		build_flags = env.get('BUILD_FLAGS')
261
-		build_flags = env.ParseFlagsExtended(build_flags)
262
-
263
-		cxx = search_compiler()
264
-		cmd = ['"' + cxx + '"']
265
-
266
-		# Build flags from board.json
267
-		#if 'BOARD' in env:
268
-		#	cmd += [env.BoardConfig().get("build.extra_flags")]
269
-		for s in build_flags['CPPDEFINES']:
270
-			if isinstance(s, tuple):
271
-				cmd += ['-D' + s[0] + '=' + str(s[1])]
272
-			else:
273
-				cmd += ['-D' + s]
274
-
275
-		cmd += ['-D__MARLIN_DEPS__ -w -dM -E -x c++ buildroot/share/PlatformIO/scripts/common-dependencies.h']
276
-		cmd = ' '.join(cmd)
277
-		blab(cmd, 4)
278
-		define_list = subprocess.check_output(cmd, shell=True).splitlines()
203
+		from preprocessor import run_preprocessor
204
+		define_list = run_preprocessor(env)
279
 		marlin_features = {}
205
 		marlin_features = {}
280
 		for define in define_list:
206
 		for define in define_list:
281
 			feature = define[8:].strip().decode().split(' ')
207
 			feature = define[8:].strip().decode().split(' ')
310
 	except:
236
 	except:
311
 		pass
237
 		pass
312
 
238
 
239
+	#
313
 	# Add a method for other PIO scripts to query enabled features
240
 	# Add a method for other PIO scripts to query enabled features
241
+	#
314
 	env.AddMethod(MarlinFeatureIsEnabled)
242
 	env.AddMethod(MarlinFeatureIsEnabled)
315
 
243
 
244
+	#
316
 	# Add dependencies for enabled Marlin features
245
 	# Add dependencies for enabled Marlin features
246
+	#
317
 	apply_features_config()
247
 	apply_features_config()
318
 	force_ignore_unused_libs()
248
 	force_ignore_unused_libs()
249
+
250
+	#print(env.Dump())
251
+
252
+	from signature import compute_build_signature
253
+	compute_build_signature(env)

+ 69
- 0
buildroot/share/PlatformIO/scripts/mc-apply.py 查看文件

1
+#!/usr/bin/env python
2
+#
3
+# Create a Configuration from marlin_config.json
4
+#
5
+import json
6
+import sys
7
+import shutil
8
+import re
9
+
10
+opt_output = '--opt' in sys.argv
11
+output_suffix = '.sh' if opt_output else '' if '--bare-output' in sys.argv else '.gen'
12
+
13
+try:
14
+	with open('marlin_config.json', 'r') as infile:
15
+		conf = json.load(infile)
16
+		for key in conf:
17
+			# We don't care about the hash when restoring here
18
+			if key == '__INITIAL_HASH':
19
+				continue
20
+			if key == 'VERSION':
21
+				for k, v in sorted(conf[key].items()):
22
+					print(k + ': ' + v)
23
+				continue
24
+			# The key is the file name, so let's build it now
25
+			outfile = open('Marlin/' + key + output_suffix, 'w')
26
+			for k, v in sorted(conf[key].items()):
27
+				# Make define line now
28
+				if opt_output:
29
+					if v != '':
30
+						if '"' in v:
31
+							v = "'%s'" % v
32
+						elif ' ' in v:
33
+							v = '"%s"' % v
34
+						define = 'opt_set ' + k + ' ' + v + '\n'
35
+					else:
36
+						define = 'opt_enable ' + k + '\n'
37
+				else:
38
+					define = '#define ' + k + ' ' + v + '\n'
39
+				outfile.write(define)
40
+			outfile.close()
41
+
42
+			# Try to apply changes to the actual configuration file (in order to keep useful comments)
43
+			if output_suffix != '':
44
+				# Move the existing configuration so it doesn't interfere
45
+				shutil.move('Marlin/' + key, 'Marlin/' + key + '.orig')
46
+				infile_lines = open('Marlin/' + key + '.orig', 'r').read().split('\n')
47
+				outfile = open('Marlin/' + key, 'w')
48
+				for line in infile_lines:
49
+					sline = line.strip(" \t\n\r")
50
+					if sline[:7] == "#define":
51
+						# Extract the key here (we don't care about the value)
52
+						kv = sline[8:].strip().split(' ')
53
+						if kv[0] in conf[key]:
54
+							outfile.write('#define ' + kv[0] + ' ' + conf[key][kv[0]] + '\n')
55
+							# Remove the key from the dict, so we can still write all missing keys at the end of the file
56
+							del conf[key][kv[0]]
57
+						else:
58
+							outfile.write(line + '\n')
59
+					else:
60
+						outfile.write(line + '\n')
61
+				# Process any remaining defines here
62
+				for k, v in sorted(conf[key].items()):
63
+					define = '#define ' + k + ' ' + v + '\n'
64
+					outfile.write(define)
65
+				outfile.close()
66
+
67
+			print('Output configuration written to: ' + 'Marlin/' + key + output_suffix)
68
+except:
69
+	print('No marlin_config.json found.')

+ 99
- 0
buildroot/share/PlatformIO/scripts/preprocessor.py 查看文件

1
+#
2
+# preprocessor.py
3
+#
4
+import subprocess,os,re
5
+
6
+verbose = 0
7
+
8
+def blab(str):
9
+	if verbose:
10
+		print(str)
11
+
12
+################################################################################
13
+#
14
+# Invoke GCC to run the preprocessor and extract enabled features
15
+#
16
+preprocessor_cache = {}
17
+def run_preprocessor(env, fn=None):
18
+	filename = fn or 'buildroot/share/PlatformIO/scripts/common-dependencies.h'
19
+	if filename in preprocessor_cache:
20
+		return preprocessor_cache[filename]
21
+
22
+	# Process defines
23
+	build_flags = env.get('BUILD_FLAGS')
24
+	build_flags = env.ParseFlagsExtended(build_flags)
25
+
26
+	cxx = search_compiler(env)
27
+	cmd = ['"' + cxx + '"']
28
+
29
+	# Build flags from board.json
30
+	#if 'BOARD' in env:
31
+	#	cmd += [env.BoardConfig().get("build.extra_flags")]
32
+	for s in build_flags['CPPDEFINES']:
33
+		if isinstance(s, tuple):
34
+			cmd += ['-D' + s[0] + '=' + str(s[1])]
35
+		else:
36
+			cmd += ['-D' + s]
37
+
38
+	cmd += ['-D__MARLIN_DEPS__ -w -dM -E -x c++']
39
+	depcmd = cmd + [ filename ]
40
+	cmd = ' '.join(depcmd)
41
+	blab(cmd)
42
+	define_list = subprocess.check_output(cmd, shell=True).splitlines()
43
+	preprocessor_cache[filename] = define_list
44
+	return define_list
45
+
46
+
47
+################################################################################
48
+#
49
+# Find a compiler, considering the OS
50
+#
51
+def search_compiler(env):
52
+
53
+	ENV_BUILD_PATH = os.path.join(env.Dictionary('PROJECT_BUILD_DIR'), env['PIOENV'])
54
+	GCC_PATH_CACHE = os.path.join(ENV_BUILD_PATH, ".gcc_path")
55
+
56
+	try:
57
+		filepath = env.GetProjectOption('custom_gcc')
58
+		blab("Getting compiler from env")
59
+		return filepath
60
+	except:
61
+		pass
62
+
63
+	if os.path.exists(GCC_PATH_CACHE):
64
+		blab("Getting g++ path from cache")
65
+		with open(GCC_PATH_CACHE, 'r') as f:
66
+			return f.read()
67
+
68
+	# Find the current platform compiler by searching the $PATH
69
+	# which will be in a platformio toolchain bin folder
70
+	path_regex = re.escape(env['PROJECT_PACKAGES_DIR'])
71
+	gcc = "g++"
72
+	if env['PLATFORM'] == 'win32':
73
+		path_separator = ';'
74
+		path_regex += r'.*\\bin'
75
+		gcc += ".exe"
76
+	else:
77
+		path_separator = ':'
78
+		path_regex += r'/.+/bin'
79
+
80
+	# Search for the compiler
81
+	for pathdir in env['ENV']['PATH'].split(path_separator):
82
+		if not re.search(path_regex, pathdir, re.IGNORECASE):
83
+			continue
84
+		for filepath in os.listdir(pathdir):
85
+			if not filepath.endswith(gcc):
86
+				continue
87
+			# Use entire path to not rely on env PATH
88
+			filepath = os.path.sep.join([pathdir, filepath])
89
+			# Cache the g++ path to no search always
90
+			if os.path.exists(ENV_BUILD_PATH):
91
+				blab("Caching g++ for current env")
92
+				with open(GCC_PATH_CACHE, 'w+') as f:
93
+					f.write(filepath)
94
+
95
+			return filepath
96
+
97
+	filepath = env.get('CXX')
98
+	blab("Couldn't find a compiler! Fallback to %s" % filepath)
99
+	return filepath

+ 176
- 0
buildroot/share/PlatformIO/scripts/signature.py 查看文件

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

+ 19
- 0
docs/ConfigEmbedding.md 查看文件

1
+# Configuration Embedding
2
+
3
+Starting with version 2.0.9.3, Marlin automatically extracts the configuration used to generate the firmware and stores it in the firmware binary. This is enabled by defining `CONFIGURATION_EMBEDDING` in `Configuration_adv.h`.
4
+
5
+## How it's done
6
+To create the embedded configuration, we do a compiler pass to process the Configuration files and extract all active options. The active options are parsed into key/value pairs, serialized to JSON format, and stored in a file called `marlin_config.json`, which also includes specific build information (like the git revision, the build date, and some version information. The JSON file is then compressed in a ZIP archive called `.pio/build/mc.zip` which is converted into a C array and stored in a C++ file called `mc.h` which is included in the build.
7
+
8
+## Extracting configurations from a Marlin binary
9
+To get the configuration out of a binary firmware, you'll need a non-write-protected SD card inserted into the printer while running the firmware.
10
+Send the command `M503 C` to write the file `mc.zip` to the SD card. Copy the file to your computer, ideally in the same folder as the Marlin repository.
11
+
12
+Run the following commands to extract and apply the configuration:
13
+```
14
+$ git checkout -f
15
+$ unzip mc.zip 
16
+$ python buildroot/share/PlatformIO/scripts/mc-apply.py
17
+```
18
+
19
+This will attempt to update the configuration files to match the settings used for the original build. It will also dump the git reference used to build the code (which may be accessible if the firmware was built from the main repository. As a fallback it also includes the `STRING_DISTRIBUTION_DATE` which is unlikely to be modified in a fork).

Loading…
取消
儲存