mirror of
https://github.com/MarlinFirmware/Marlin.git
synced 2025-12-28 10:20:36 -07:00
198 lines
6.4 KiB
Python
Executable file
198 lines
6.4 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
"""
|
|
mc-apply.py
|
|
|
|
Apply firmware configuration from a JSON file (marlin_config.json).
|
|
|
|
usage: mc-apply.py [-h] [--opt] [--verbose] [config_file]
|
|
|
|
Process Marlin firmware configuration.
|
|
|
|
positional arguments:
|
|
config_file Path to the configuration file.
|
|
|
|
optional arguments:
|
|
-h, --help Show this help message and exit
|
|
--opt Output as an option setting script.
|
|
--verbose Enable verbose logging (0-2)
|
|
"""
|
|
|
|
import json, sys, os, configuration
|
|
import config
|
|
import argparse
|
|
|
|
verbose = 0
|
|
def blab(msg, level=1):
|
|
if verbose >= level: print(f"[mc-apply] {msg}")
|
|
|
|
def normalize_value(v):
|
|
"""
|
|
Normalize configuration values to consistent format.
|
|
Returns tuple: (action, value) where action is 'enable', 'disable', or 'set'
|
|
|
|
- "on", "true", True, "" -> ('enable', '') - Enable without value
|
|
- "off", "false", False -> ('disable', '') - Disable/comment out
|
|
- Any other value -> ('set', value) - Enable with value
|
|
"""
|
|
# Convert to string for comparison, handle JSON booleans
|
|
if isinstance(v, bool):
|
|
v_str = 'true' if v else 'false'
|
|
else:
|
|
v_str = str(v).strip().lower()
|
|
|
|
# Check for enable values
|
|
if v_str in ('on', 'true', ''):
|
|
return ('enable', '')
|
|
|
|
# Check for disable values
|
|
elif v_str in ('off', 'false'):
|
|
return ('disable', '')
|
|
|
|
# Everything else is a value to set
|
|
else:
|
|
return ('set', v if not isinstance(v, bool) else v_str)
|
|
|
|
def report_version(conf):
|
|
if 'VERSION' in conf:
|
|
blab("Configuration version information:")
|
|
for k, v in sorted(conf['VERSION'].items()):
|
|
print(k + ': ' + v)
|
|
|
|
def write_opt_file(conf, outpath='Marlin/apply_config.sh'):
|
|
blab(f"Writing configuration script to {outpath}")
|
|
with open(outpath, 'w', encoding='utf-8') as outfile:
|
|
for key, val in conf.items():
|
|
if key in ('__INITIAL_HASH', '__directives__', 'VERSION'): continue
|
|
|
|
# Other keys are assumed to be configs
|
|
if not isinstance(val, dict):
|
|
continue
|
|
|
|
# Write config commands to the script file
|
|
lines = []
|
|
for k, v in sorted(val.items()):
|
|
action, norm_val = normalize_value(v)
|
|
|
|
if action == 'enable':
|
|
lines += [f'opt_enable {k}']
|
|
blab(f" opt_enable {k}", 2)
|
|
elif action == 'disable':
|
|
lines += [f'opt_disable {k}']
|
|
blab(f" opt_disable {k}", 2)
|
|
else: # action == 'set'
|
|
norm_val = str(norm_val).replace('"', '\\"').replace("'", "\\'").replace(' ', '\\ ')
|
|
lines += [f'opt_set {k} {norm_val}']
|
|
blab(f" opt_set {k} {norm_val}", 2)
|
|
|
|
outfile.write('\n'.join(lines))
|
|
|
|
print('Config script written to: ' + outpath)
|
|
|
|
def back_up_config(name):
|
|
# Back up the existing file before modifying it
|
|
conf_path = 'Marlin/' + name
|
|
if not os.path.exists(conf_path):
|
|
blab(f"Config file not found: {conf_path}", 0)
|
|
return
|
|
|
|
with open(conf_path, 'r', encoding='utf-8') as f:
|
|
# Write a filename.bak#.ext retaining the original extension
|
|
parts = conf_path.split('.')
|
|
nr = ''
|
|
while True:
|
|
bak_path = '.'.join(parts[:-1]) + f'.bak{nr}.' + parts[-1]
|
|
if os.path.exists(bak_path):
|
|
nr = 1 if nr == '' else nr + 1
|
|
continue
|
|
|
|
with open(bak_path, 'w', encoding='utf-8', newline='') as b:
|
|
b.writelines(f.readlines())
|
|
blab(f"Backed up {conf_path} to {bak_path}", 2)
|
|
break
|
|
|
|
def process_directives(directives):
|
|
"""Process special directives before applying config options"""
|
|
if not isinstance(directives, list):
|
|
directives = [directives]
|
|
|
|
for directive in directives:
|
|
directive = directive.strip()
|
|
blab(f"Processing directive: {directive}")
|
|
|
|
# Handle [disable] directive
|
|
if directive == "[disable]":
|
|
configuration.disable_all_options()
|
|
|
|
# Handle example fetching (examples/path or example/path)
|
|
elif directive.startswith('examples/') or directive.startswith('example/'):
|
|
if directive.startswith('example/'):
|
|
directive = 'examples' + directive[7:]
|
|
configuration.fetch_example(directive)
|
|
|
|
# Handle direct URLs
|
|
elif directive.startswith('http://') or directive.startswith('https://'):
|
|
configuration.fetch_example(directive)
|
|
|
|
else:
|
|
blab(f"Unknown directive: {directive}", 0)
|
|
|
|
def apply_config(conf):
|
|
# Process directives first if they exist
|
|
if '__directives__' in conf:
|
|
blab("=" * 20 + " Processing directives...")
|
|
process_directives(conf['__directives__'])
|
|
|
|
# Apply configuration options
|
|
blab("=" * 20 + " Applying configuration options...")
|
|
for key in conf:
|
|
if key in ('__INITIAL_HASH', '__directives__', 'VERSION'): continue
|
|
|
|
# Skip non-dict values
|
|
if not isinstance(conf[key], dict):
|
|
continue
|
|
|
|
back_up_config(key)
|
|
|
|
for k, v in conf[key].items():
|
|
action, norm_val = normalize_value(v)
|
|
conf_file = 'Marlin/' + key
|
|
|
|
if action == 'enable':
|
|
blab(f"Enabling {k}", 2)
|
|
config.enable(conf_file, k, True)
|
|
elif action == 'disable':
|
|
blab(f"Disabling {k}", 2)
|
|
config.enable(conf_file, k, False)
|
|
else: # action == 'set'
|
|
blab(f"Setting {k} = {norm_val}", 2)
|
|
config.set(conf_file, k, norm_val)
|
|
|
|
def main():
|
|
global verbose
|
|
|
|
parser = argparse.ArgumentParser(description='Process Marlin firmware configuration.')
|
|
parser.add_argument('--opt', action='store_true', help='Output as an option setting script.')
|
|
parser.add_argument('--verbose', '-v', type=int, default=0, help='Verbose logging level (0-2, default: 0)')
|
|
parser.add_argument('config_file', nargs='?', default='marlin_config.json', help='Path to the configuration file.')
|
|
|
|
args = parser.parse_args()
|
|
|
|
# Set verbose level
|
|
verbose = args.verbose
|
|
|
|
try:
|
|
infile = open(args.config_file, 'r', encoding='utf-8')
|
|
except:
|
|
print(f'No {args.config_file} found.')
|
|
sys.exit(1)
|
|
|
|
conf = json.load(infile)
|
|
report_version(conf)
|
|
|
|
if args.opt:
|
|
write_opt_file(conf)
|
|
else:
|
|
apply_config(conf)
|
|
|
|
if __name__ == '__main__':
|
|
main()
|