diff --git a/buildroot/share/PlatformIO/scripts/configuration.py b/buildroot/share/PlatformIO/scripts/configuration.py index 85fe8ec0bf..c184aab869 100755 --- a/buildroot/share/PlatformIO/scripts/configuration.py +++ b/buildroot/share/PlatformIO/scripts/configuration.py @@ -110,7 +110,7 @@ def disable_all_options(): match = regex.match(line) if match: name = match[3].upper() - if name in ('CONFIGURATION_H_VERSION', 'CONFIGURATION_ADV_H_VERSION'): continue + if name in ('CONFIGURATION_H_VERSION', 'CONFIGURATION_ADV_H_VERSION', 'CONFIG_EXAMPLES_DIR'): continue if name.startswith('_'): continue found = True # Comment out the define diff --git a/buildroot/share/PlatformIO/scripts/schema.py b/buildroot/share/PlatformIO/scripts/schema.py index 381ee57a95..381a7865c9 100755 --- a/buildroot/share/PlatformIO/scripts/schema.py +++ b/buildroot/share/PlatformIO/scripts/schema.py @@ -80,7 +80,7 @@ def load_boards(): return '' # -# Extract the current configuration files in the form of a structured schema. +# Extract the specified configuration files in the form of a structured schema. # Contains the full schema for the configuration files, not just the enabled options, # Contains the current values of the options, not just data structure, so "schema" is a slight misnomer. # @@ -99,9 +99,9 @@ def load_boards(): # - requires = The conditions that must be met for the define to be enabled # - comment = The comment for the define, if it has one # - units = The units for the define, if it has one -# - options = The options for the define, if it has one +# - options = The options for the define, if it has any # -def extract(): +def extract_files(filekey): # Load board names from boards.h boards = load_boards() @@ -114,10 +114,8 @@ def extract(): GET_SENSORS = 4 # Gathering temperature sensor options ERROR = 9 # Syntax error - # List of files to process, with shorthand - filekey = { 'Configuration.h':'basic', 'Configuration_adv.h':'advanced' } # A JSON object to store the data - sch_out = { 'basic':{}, 'advanced':{} } + sch_out = { key:{} for key in filekey.values() } # Regex for #define NAME [VALUE] [COMMENT] with sanitized line defgrep = re.compile(r'^(//)?\s*(#define)\s+([A-Za-z0-9_]+)\s*(.*?)\s*(//.+)?$') # Pattern to match a float value @@ -180,6 +178,12 @@ def extract(): cfield = 'notes' if 'comment' in last_added_ref else 'comment' last_added_ref[cfield] = cline + # + # Add the given comment line to the comment buffer, unless: + # - The line starts with ':' and JSON values to assign to 'opt'. + # - The line starts with '@section' so a new section needs to be returned. + # - The line starts with '======' so just skip it. + # def use_comment(c, opt, sec, bufref): if c.startswith(':'): # If the comment starts with : then it has magic JSON d = c[1:].strip() # Strip the leading : @@ -199,7 +203,7 @@ def extract(): # The comment will be applied to the next #define. if state == Parse.SLASH_COMMENT: if not defmatch and the_line.startswith('//'): - use_comment(the_line[2:].strip(), options_json, section, comment_buff) + options_json, section = use_comment(the_line[2:].strip(), options_json, section, comment_buff) continue else: state = Parse.NORMAL @@ -219,7 +223,7 @@ def extract(): state = Parse.NORMAL - # Strip the leading '*' from block comments + # Strip the leading '* ' from block comments cline = re.sub(r'^\* ?', '', cline) # Collect temperature sensors @@ -412,6 +416,13 @@ def extract(): return sch_out +# +# Extract the current configuration files in the form of a structured schema. +# +def extract(): + # List of files to process, with shorthand + return extract_files({ 'Configuration.h':'basic', 'Configuration_adv.h':'advanced' }) + def dump_json(schema:dict, jpath:Path): with jpath.open('w') as jfile: json.dump(schema, jfile, ensure_ascii=False, indent=2) diff --git a/buildroot/share/PlatformIO/scripts/signature.py b/buildroot/share/PlatformIO/scripts/signature.py index 8723969c00..2df4e099f8 100755 --- a/buildroot/share/PlatformIO/scripts/signature.py +++ b/buildroot/share/PlatformIO/scripts/signature.py @@ -35,18 +35,27 @@ def enabled_defines(filepath): ''' outdict = {} section = "user" - spatt = re.compile(r".*@section +([-a-zA-Z0-9_\s]+)$") # must match @section ... + spatt = re.compile(r".*@section +([-a-zA-Z0-9_\s]+)$") # @section ... f = open(filepath, encoding="utf8").read().split("\n") - # Get the full contents of the file and remove all block comments. - # This will avoid false positives from #defines in comments - f = re.sub(r'/\*.*?\*/', '', '\n'.join(f), flags=re.DOTALL).split("\n") - + incomment = False for line in f: sline = line.strip() + m = re.match(spatt, sline) # @section ... if m: section = m.group(1).strip() ; continue + + if incomment: + if '*/' in sline: + incomment = False + continue + else: + mpos, spos = sline.find('/*'), sline.find('//') + if mpos >= 0 and (spos < 0 or spos > mpos): + incomment = True + continue + if sline[:7] == "#define": # Extract the key here (we don't care about the value) kv = sline[8:].strip().split() @@ -70,6 +79,11 @@ def compress_file(filepath, storedname, outpath): with zipfile.ZipFile(outpath, 'w', compression=zipfile.ZIP_BZIP2, compresslevel=9) as zipf: zipf.write(filepath, arcname=storedname, compress_type=zipfile.ZIP_BZIP2, compresslevel=9) +ignore = ('CONFIGURATION_H_VERSION', 'CONFIGURATION_ADV_H_VERSION', 'CONFIG_EXAMPLES_DIR', 'CONFIG_EXPORT') + +# +# Compute a build signature and/or export the configuration +# def compute_build_signature(env): ''' Compute the build signature by extracting all configuration settings and @@ -81,11 +95,17 @@ def compute_build_signature(env): env.Append(BUILD_SIGNATURE=1) build_path = Path(env['PROJECT_BUILD_DIR'], env['PIOENV']) - marlin_json = build_path / 'marlin_config.json' + json_name = 'marlin_config.json' + marlin_json = build_path / json_name marlin_zip = build_path / 'mc.zip' + # ANSI colors + green = "\u001b[32m" + yellow = "\u001b[33m" + red = "\u001b[31m" + # Definitions from these files will be kept - header_paths = [ 'Marlin/Configuration.h', 'Marlin/Configuration_adv.h' ] + header_paths = ('Marlin/Configuration.h', 'Marlin/Configuration_adv.h') # Check if we can skip processing hashes = '' @@ -100,7 +120,7 @@ def compute_build_signature(env): conf = json.load(infile) same_hash = conf['__INITIAL_HASH'] == hashes if same_hash: - compress_file(marlin_json, 'marlin_config.json', marlin_zip) + compress_file(marlin_json, json_name, marlin_zip) except: pass @@ -179,25 +199,28 @@ def compute_build_signature(env): extended_dump = config_dump > 100 if extended_dump: config_dump -= 100 + # Get the schema class for exports that require it + if config_dump in (3, 4) or (extended_dump and config_dump in (2, 5)): + try: + conf_schema = schema.extract() + except Exception as exc: + print(red + "Error: " + str(exc)) + conf_schema = None + # # Produce an INI file if CONFIG_EXPORT == 2 # if config_dump == 2: - print("Generating config.ini ...") + print(yellow + "Generating config.ini ...") ini_fmt = '{0:40} = {1}' ext_fmt = '{0:40} {1}' - ignore = ('CONFIGURATION_H_VERSION', 'CONFIGURATION_ADV_H_VERSION', 'CONFIG_EXAMPLES_DIR', 'CONFIG_EXPORT') if extended_dump: # Extended export will dump config options by section # We'll use Schema class to get the sections - try: - conf_schema = schema.extract() - except Exception as exc: - print("Error: " + str(exc)) - exit(1) + if not conf_schema: exit(1) # Then group options by schema @section sections = {} @@ -305,27 +328,22 @@ f'''# for header in real_config: outfile.write(f'\n[{filegrp[header]}]\n') for name in sorted(real_config[header]): - if name not in ignore: - val = real_config[header][name]['value'] - if val == '': val = 'on' - outfile.write(ini_fmt.format(name.lower(), val) + '\n') + if name in ignore: continue + val = real_config[header][name]['value'] + if val == '': val = 'on' + outfile.write(ini_fmt.format(name.lower(), val) + '\n') # # CONFIG_EXPORT 3 = schema.json, 4 = schema.yml # - if config_dump >= 3: - try: - conf_schema = schema.extract() - except Exception as exc: - print("Error: " + str(exc)) - conf_schema = None + if config_dump in (3, 4): if conf_schema: # # 3 = schema.json # if config_dump in (3, 13): - print("Generating schema.json ...") + print(yellow + "Generating schema.json ...") schema.dump_json(conf_schema, build_path / 'schema.json') if config_dump == 13: schema.group_options(conf_schema) @@ -335,7 +353,7 @@ f'''# # 4 = schema.yml # elif config_dump == 4: - print("Generating schema.yml ...") + print(yellow + "Generating schema.yml ...") try: import yaml except ImportError: @@ -355,7 +373,7 @@ f'''# json_data = {} if extended_dump: - print("Extended dump ...") + print(yellow + "Extended dump ...") for header in real_config: confs = real_config[header] json_data[header] = {} @@ -395,7 +413,7 @@ f'''# # Compress the JSON file as much as we can if not same_hash: - compress_file(marlin_json, 'marlin_config.json', marlin_zip) + compress_file(marlin_json, json_name, marlin_zip) # Generate a C source file containing the entire ZIP file as an array with open('Marlin/src/mczip.h','wb') as result_file: diff --git a/buildroot/share/extras/file_header.h b/buildroot/share/extras/file_header.h index 0c868aebb3..79c9c363e2 100644 --- a/buildroot/share/extras/file_header.h +++ b/buildroot/share/extras/file_header.h @@ -1,6 +1,6 @@ /** * Marlin 3D Printer Firmware - * Copyright (c) 2023 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] * * Based on Sprinter and grbl. * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm