🔨 Importable configuration.py for mc-apply.py (#28182)
Some checks are pending
CI - Build Tests / Build Test (push) Waiting to run
CI - Unit Tests / Unit Test (push) Waiting to run
CI - Validate Source Files / Validate Source Files (push) Waiting to run

This commit is contained in:
Andrew 2025-11-24 20:57:47 -05:00 committed by GitHub
parent 50297c65d4
commit e35bedab48
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 72 additions and 126 deletions

View file

@ -28,7 +28,7 @@ if pioutil.is_pio_build():
name = f"marlin_{name}",
dependencies = None,
actions = [
f"echo ====== Configuring for marlin_{name} ======",
f"@echo ====== Configuring for marlin_{name} ======",
"restore_configs",
f"cp -f {path} ./Marlin/config.ini",
"python ./buildroot/share/PlatformIO/scripts/configuration.py",

View file

@ -7,27 +7,29 @@ import re, os, shutil, configparser, datetime
from pathlib import Path
verbose = 0
def blab(str,level=1):
if verbose >= level: print(f"[config] {str}")
def blab(msg, level=1):
if verbose >= level: print(f"[config] {msg}")
def config_path(cpath):
return Path("Marlin", cpath)
# Apply a single name = on/off ; name = value ; etc.
# TODO: Limit to the given (optional) configuration
def apply_opt(name, val, conf=None):
def apply_opt(name, val):
if name == "lcd": name, val = val, "on"
# Create a regex to match the option and capture parts of the line
# 1: Indentation
# 2: Comment
# 3: #define and whitespace
# 4: Option name
# 5: First space after name
# 6: Remaining spaces between name and value
# 7: Option value
# 8: Whitespace after value
# 9: End comment
"""
Create a regex to match the option and capture parts of the line
1: Indentation
2: Comment
3: #define and whitespace
4: Option name
5: First space after name
6: Remaining spaces between name and value
7: Option value
8: Whitespace after value
9: End comment
"""
regex = re.compile(
rf"^(\s*)(//\s*)?(#define\s+)({name}\b)(\s?)(\s*)(.*?)(\s*)(//.*)?$",
re.IGNORECASE
@ -101,11 +103,16 @@ def apply_opt(name, val, conf=None):
# Everything in the named sections. Section hint for exceptions may be added.
def disable_all_options():
# Create a regex to match the option and capture parts of the line
blab("Disabling all configuration options...")
regex = re.compile(r'^(\s*)(#define\s+)([A-Z0-9_]+\b)(\s?)(\s*)(.*?)(\s*)(//.*)?$', re.IGNORECASE)
# Disable all enabled options in both Config files
for file in ("Configuration.h", "Configuration_adv.h"):
fullpath = config_path(file)
if not fullpath.exists():
blab(f"File not found: {fullpath}", 0)
continue
lines = fullpath.read_text(encoding='utf-8').split('\n')
found = False
for i in range(len(lines)):
@ -120,14 +127,18 @@ def disable_all_options():
# TODO: Comment more lines in a multi-line define with \ continuation
lines[i] = re.sub(r'^(\s*)(#define)(\s{1,3})?(\s*)', r'\1//\2 \4', line)
blab(f"Disable {name}")
#blab(f"Disable {name}", 2)
# TODO: Taken from mc-apply, not sure which
# If the option was found, write the modified lines
if found:
fullpath.write_text('\n'.join(lines), encoding='utf-8')
blab(f"Updated {file}")
# Fetch configuration files from GitHub given the path.
# Return True if any files were fetched.
def fetch_example(url):
blab(f"Fetching example configuration from: {url}")
if url.endswith("/"): url = url[:-1]
if not url.startswith('http'):
brch = "bugfix-2.1.x"
@ -143,9 +154,12 @@ def fetch_example(url):
fetch = "wget -q -O"
else:
blab("Couldn't find curl or wget", -1)
#blab("Couldn't find curl or wget", 0)
# TODO: Taken from mc-apply, not sure which
return False
# Reset configurations to default
blab("Resetting configurations to default...")
os.system("git checkout HEAD Marlin/*.h")
# Try to fetch the remote files
@ -154,9 +168,15 @@ def fetch_example(url):
if os.system(f"{fetch} wgot {url}/{fn} >/dev/null 2>&1") == 0:
shutil.move('wgot', config_path(fn))
gotfile = True
blab(f"Fetched {fn}", 2)
if Path('wgot').exists(): shutil.rmtree('wgot')
if gotfile:
blab("Example configuration fetched successfully")
else:
blab("Failed to fetch example configuration", 0)
return gotfile
def section_items(cp, sectkey):
@ -217,8 +237,6 @@ def apply_config_ini(cp):
# For each ini_use_config item perform an action
for ckey in config_keys:
addbase = False
# For a key ending in .ini load and parse another .ini file
if ckey.endswith('.ini'):
sect = 'base'
@ -278,12 +296,17 @@ else:
#
# From within PlatformIO use the loaded INI file
#
import pioutil
if pioutil.is_pio_build():
try:
verbose = int(pioutil.env.GetProjectOption('custom_verbose'))
except:
pass
try:
import pioutil
if pioutil.is_pio_build():
try:
verbose = int(pioutil.env.GetProjectOption('custom_verbose'))
except:
pass
from platformio.project.config import ProjectConfig
apply_config_ini(ProjectConfig())
from platformio.project.config import ProjectConfig
apply_config_ini(ProjectConfig())
except AttributeError:
# Handle the 'IsIntegrationDump' error here, or just continue if
# the build is not a PlatformIO build where pioutil would be unavailable.
pass

View file

@ -1,32 +1,29 @@
#!/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, re, shutil, datetime
"""
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
from pathlib import Path
verbose = 0
def blab(str, level=1):
if verbose >= level: print(f"[mc-apply] {str}")
def config_path(cpath):
return Path("Marlin", cpath)
def blab(msg, level=1):
if verbose >= level: print(f"[mc-apply] {msg}")
def normalize_value(v):
"""
@ -55,80 +52,6 @@ def normalize_value(v):
else:
return ('set', v if not isinstance(v, bool) else v_str)
# Disable all (most) defined options in the configuration files.
def disable_all_options():
blab("Disabling all configuration options...")
# Create a regex to match the option and capture parts of the line
regex = re.compile(r'^(\s*)(#define\s+)([A-Z0-9_]+\b)(\s?)(\s*)(.*?)(\s*)(//.*)?$', re.IGNORECASE)
# Disable all enabled options in both Config files
for file in ("Configuration.h", "Configuration_adv.h"):
fullpath = config_path(file)
if not fullpath.exists():
blab(f"File not found: {fullpath}", 0)
continue
lines = fullpath.read_text(encoding='utf-8').split('\n')
found = False
for i in range(len(lines)):
line = lines[i]
match = regex.match(line)
if match:
name = match[3].upper()
if name in ('CONFIGURATION_H_VERSION', 'CONFIGURATION_ADV_H_VERSION', 'CONFIG_EXAMPLES_DIR'): continue
if name.startswith('_'): continue
found = True
# Comment out the define
lines[i] = re.sub(r'^(\s*)(#define)(\s{1,3})?(\s*)', r'\1//\2 \4', line)
blab(f"Disable {name}", 2)
# If the option was found, write the modified lines
if found:
fullpath.write_text('\n'.join(lines), encoding='utf-8')
blab(f"Updated {file}")
# Fetch configuration files from GitHub given the path.
# Return True if any files were fetched.
def fetch_example(url):
blab(f"Fetching example configuration from: {url}")
if url.endswith("/"): url = url[:-1]
if not url.startswith('http'):
brch = "bugfix-2.1.x"
if '@' in url: url, brch = map(str.strip, url.split('@'))
if url == 'examples/default': url = 'default'
url = f"https://raw.githubusercontent.com/MarlinFirmware/Configurations/{brch}/config/{url}"
url = url.replace("%", "%25").replace(" ", "%20")
# Find a suitable fetch command
if shutil.which("curl") is not None:
fetch = "curl -L -s -S -f -o"
elif shutil.which("wget") is not None:
fetch = "wget -q -O"
else:
blab("Couldn't find curl or wget", 0)
return False
# Reset configurations to default
blab("Resetting configurations to default...")
os.system("git checkout HEAD Marlin/*.h")
# Try to fetch the remote files
gotfile = False
for fn in ("Configuration.h", "Configuration_adv.h", "_Bootscreen.h", "_Statusscreen.h"):
if os.system(f"{fetch} wgot {url}/{fn} >/dev/null 2>&1") == 0:
shutil.move('wgot', config_path(fn))
gotfile = True
blab(f"Fetched {fn}", 2)
if Path('wgot').exists(): shutil.rmtree('wgot')
if gotfile:
blab("Example configuration fetched successfully")
else:
blab("Failed to fetch example configuration", 0)
return gotfile
def report_version(conf):
if 'VERSION' in conf:
blab("Configuration version information:")
@ -142,7 +65,7 @@ def write_opt_file(conf, outpath='Marlin/apply_config.sh'):
if key in ('__INITIAL_HASH', '__directives__', 'VERSION'): continue
# Other keys are assumed to be configs
if not type(val) is dict:
if not isinstance(val, dict):
continue
# Write config commands to the script file
@ -198,17 +121,17 @@ def process_directives(directives):
# Handle [disable] directive
if directive == "[disable]":
disable_all_options()
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:]
fetch_example(directive)
configuration.fetch_example(directive)
# Handle direct URLs
elif directive.startswith('http://') or directive.startswith('https://'):
fetch_example(directive)
configuration.fetch_example(directive)
else:
blab(f"Unknown directive: {directive}", 0)