2025-08-25 12:03:17 +08:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
# tools/process_config.py
|
|
|
|
|
#
|
|
|
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
#
|
|
|
|
|
# Licensed to the Apache Software Foundation (ASF) under one or more
|
|
|
|
|
# contributor license agreements. See the NOTICE file distributed with
|
|
|
|
|
# this work for additional information regarding copyright ownership. The
|
|
|
|
|
# ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
|
|
|
# "License"); you may not use this file except in compliance with the
|
|
|
|
|
# License. You may obtain a copy of the License at
|
|
|
|
|
#
|
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
#
|
|
|
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
|
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
|
|
|
# License for the specific language governing permissions and limitations
|
|
|
|
|
# under the License.
|
|
|
|
|
#
|
|
|
|
|
|
2025-09-09 22:05:33 +08:00
|
|
|
import json
|
|
|
|
|
import os
|
2025-08-25 12:03:17 +08:00
|
|
|
import re
|
2025-09-09 22:05:33 +08:00
|
|
|
import shutil
|
2025-08-25 12:03:17 +08:00
|
|
|
import sys
|
2025-09-09 22:05:33 +08:00
|
|
|
from collections import OrderedDict
|
2025-08-25 12:03:17 +08:00
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
|
2025-09-09 22:05:33 +08:00
|
|
|
def parse_config_line(line):
|
2025-08-25 12:03:17 +08:00
|
|
|
"""
|
2025-09-09 22:05:33 +08:00
|
|
|
Parse a configuration line and return the key and value.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
line (str): A line from a configuration file
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
tuple: (key, value) if the line contains a configuration, (None, None) otherwise
|
|
|
|
|
|
|
|
|
|
Handles two formats:
|
|
|
|
|
1. "# CONFIG_XXX is not set" -> returns (CONFIG_XXX, 'n')
|
|
|
|
|
2. "CONFIG_XXX=value" -> returns (CONFIG_XXX, value)
|
|
|
|
|
"""
|
|
|
|
|
line = line.strip()
|
|
|
|
|
if not line:
|
|
|
|
|
return None, None
|
|
|
|
|
|
|
|
|
|
# Handle "# CONFIG_XXX is not set" format
|
|
|
|
|
if line.startswith("# ") and line.endswith(" is not set"):
|
|
|
|
|
config_name = line.split()[1]
|
|
|
|
|
return config_name, "n"
|
|
|
|
|
|
|
|
|
|
# Handle "CONFIG_XXX=value" format
|
|
|
|
|
if "=" in line:
|
|
|
|
|
key, value = line.split("=", 1)
|
|
|
|
|
return key, value
|
|
|
|
|
|
|
|
|
|
return None, None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def opposite(value):
|
|
|
|
|
if value == "n":
|
|
|
|
|
return "y"
|
|
|
|
|
else:
|
|
|
|
|
return "n"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def expand_file(input_path, include_paths, processed=None, tree_node=None):
|
|
|
|
|
"""
|
|
|
|
|
Recursively expand a configuration file with #include directives.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
input_path (str): Path to the input configuration file
|
|
|
|
|
include_paths (list): List of directories to search for included files
|
|
|
|
|
processed (set, optional): Set of already processed files to avoid circular includes
|
|
|
|
|
tree_node (dict, optional): Node in the configuration tree being built
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
tuple: (list of expanded lines, tree structure node)
|
|
|
|
|
|
|
|
|
|
This function:
|
|
|
|
|
1. Reads the input file line by line
|
|
|
|
|
2. Processes #include directives by recursively expanding included files
|
|
|
|
|
3. Parses configuration lines to build a configuration dictionary
|
|
|
|
|
4. Builds a tree structure representing the file inclusion hierarchy
|
|
|
|
|
5. Returns the expanded content and the tree structure
|
2025-08-25 12:03:17 +08:00
|
|
|
"""
|
|
|
|
|
if processed is None:
|
|
|
|
|
processed = set()
|
2025-09-09 22:05:33 +08:00
|
|
|
if tree_node is None:
|
|
|
|
|
tree_node = {
|
|
|
|
|
"file": str(input_path),
|
|
|
|
|
"includes": [],
|
|
|
|
|
"configs": OrderedDict(),
|
|
|
|
|
"include_lines": [],
|
|
|
|
|
"raw_content": [], # Store original content for postprocessing
|
|
|
|
|
}
|
2025-08-25 12:03:17 +08:00
|
|
|
|
|
|
|
|
input_path = Path(input_path).resolve()
|
|
|
|
|
if input_path in processed:
|
2025-09-09 22:05:33 +08:00
|
|
|
return [], tree_node
|
2025-08-25 12:03:17 +08:00
|
|
|
processed.add(input_path)
|
|
|
|
|
|
|
|
|
|
expanded_lines = []
|
2025-09-09 22:05:33 +08:00
|
|
|
current_configs = OrderedDict()
|
2025-08-25 12:03:17 +08:00
|
|
|
|
|
|
|
|
with input_path.open("r", encoding="utf-8") as f:
|
|
|
|
|
lines = f.readlines()
|
|
|
|
|
|
2025-09-09 22:05:33 +08:00
|
|
|
# Save original content for postprocessing
|
|
|
|
|
tree_node["raw_content"] = [line.rstrip("\n") for line in lines]
|
|
|
|
|
|
2025-08-25 12:03:17 +08:00
|
|
|
for line in lines:
|
|
|
|
|
line_strip = line.strip()
|
2025-09-09 22:05:33 +08:00
|
|
|
match = re.match(r"#include\s*[<\"]([^\">]+)[\">]", line_strip)
|
2025-08-25 12:03:17 +08:00
|
|
|
if match:
|
|
|
|
|
include_file = match.group(1)
|
|
|
|
|
found = False
|
|
|
|
|
|
2025-09-09 22:05:33 +08:00
|
|
|
# Record original include line for postprocessing
|
|
|
|
|
tree_node["include_lines"].append(line.rstrip("\n"))
|
2025-08-25 12:03:17 +08:00
|
|
|
|
2025-09-09 22:05:33 +08:00
|
|
|
# Check current directory first
|
2025-08-25 12:03:17 +08:00
|
|
|
direct_path = input_path.parent / include_file
|
|
|
|
|
if direct_path.exists():
|
2025-09-09 22:05:33 +08:00
|
|
|
include_node = {
|
|
|
|
|
"file": str(direct_path),
|
|
|
|
|
"includes": [],
|
|
|
|
|
"configs": OrderedDict(),
|
|
|
|
|
"include_lines": [],
|
|
|
|
|
"raw_content": [],
|
|
|
|
|
}
|
|
|
|
|
tree_node["includes"].append(include_node)
|
|
|
|
|
|
|
|
|
|
# Recursively expand the included file
|
|
|
|
|
included_lines, include_node = expand_file(
|
|
|
|
|
direct_path, include_paths, processed, include_node
|
2025-08-25 12:03:17 +08:00
|
|
|
)
|
2025-09-09 22:05:33 +08:00
|
|
|
expanded_lines.extend(included_lines)
|
|
|
|
|
|
|
|
|
|
# Merge configurations (later configurations override earlier ones)
|
|
|
|
|
for key, value in include_node["configs"].items():
|
|
|
|
|
current_configs[key] = value
|
|
|
|
|
tree_node["configs"][key] = value
|
|
|
|
|
|
2025-08-25 12:03:17 +08:00
|
|
|
found = True
|
|
|
|
|
else:
|
2025-09-09 22:05:33 +08:00
|
|
|
# Check include paths
|
2025-08-25 12:03:17 +08:00
|
|
|
for path in include_paths:
|
|
|
|
|
candidate = Path(path) / include_file
|
|
|
|
|
if candidate.exists():
|
2025-09-09 22:05:33 +08:00
|
|
|
include_node = {
|
|
|
|
|
"file": str(candidate),
|
|
|
|
|
"includes": [],
|
|
|
|
|
"configs": OrderedDict(),
|
|
|
|
|
"include_lines": [],
|
|
|
|
|
"raw_content": [],
|
|
|
|
|
}
|
|
|
|
|
tree_node["includes"].append(include_node)
|
|
|
|
|
|
|
|
|
|
# Recursively expand the included file
|
|
|
|
|
included_lines, include_node = expand_file(
|
|
|
|
|
candidate, include_paths, processed, include_node
|
2025-08-25 12:03:17 +08:00
|
|
|
)
|
2025-09-09 22:05:33 +08:00
|
|
|
expanded_lines.extend(included_lines)
|
|
|
|
|
|
|
|
|
|
# Merge configurations
|
|
|
|
|
for key, value in include_node["configs"].items():
|
|
|
|
|
current_configs[key] = value
|
|
|
|
|
tree_node["configs"][key] = value
|
|
|
|
|
|
2025-08-25 12:03:17 +08:00
|
|
|
found = True
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
if not found:
|
|
|
|
|
print(
|
|
|
|
|
f'ERROR: Cannot find "{include_file}" from {input_path}',
|
|
|
|
|
file=sys.stderr,
|
|
|
|
|
)
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
else:
|
2025-09-09 22:05:33 +08:00
|
|
|
# Parse configuration line
|
|
|
|
|
key, value = parse_config_line(line)
|
|
|
|
|
if key is not None:
|
|
|
|
|
current_configs[key] = value
|
|
|
|
|
tree_node["configs"][key] = value
|
2025-08-25 12:03:17 +08:00
|
|
|
expanded_lines.append(line)
|
|
|
|
|
|
2025-09-09 22:05:33 +08:00
|
|
|
expanded_lines.append("\n") # Maintain separation between files
|
|
|
|
|
return expanded_lines, tree_node
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def preprocess(output_path, input_path, include_paths, tree_output_path=None):
|
|
|
|
|
"""
|
|
|
|
|
Process a configuration file with #include directives.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
output_path (str): Path to write the expanded configuration
|
|
|
|
|
input_path (str): Path to the input configuration file
|
|
|
|
|
include_paths (list): List of directories to search for included files
|
|
|
|
|
tree_output_path (str, optional): Path to write the tree structure
|
2025-08-25 12:03:17 +08:00
|
|
|
|
2025-09-09 22:05:33 +08:00
|
|
|
This function:
|
|
|
|
|
1. Expands the input file by processing #include directives
|
|
|
|
|
2. Writes the expanded configuration to output_path
|
|
|
|
|
3. Optionally writes the tree structure to tree_output_path for postprocessing
|
|
|
|
|
"""
|
|
|
|
|
lines, tree = expand_file(input_path, include_paths)
|
2025-08-25 12:03:17 +08:00
|
|
|
|
2025-09-09 22:05:33 +08:00
|
|
|
# Write expanded configuration
|
2025-08-25 12:03:17 +08:00
|
|
|
with open(output_path, "w", encoding="utf-8") as out:
|
|
|
|
|
out.writelines(lines)
|
|
|
|
|
|
2025-09-09 22:05:33 +08:00
|
|
|
# Write tree structure if requested
|
|
|
|
|
if tree_output_path and tree["includes"]:
|
|
|
|
|
with open(tree_output_path, "w", encoding="utf-8") as f:
|
|
|
|
|
json.dump(tree, f, indent=2, ensure_ascii=False)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_all_included_configs(tree):
|
|
|
|
|
"""
|
|
|
|
|
Extract all configuration options from included files.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
tree (dict): The configuration tree structure
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
OrderedDict: Dictionary of configuration options from all included files
|
|
|
|
|
|
|
|
|
|
This function recursively traverses the tree to collect all configurations
|
|
|
|
|
from files included via #include directives.
|
|
|
|
|
"""
|
|
|
|
|
included_configs = OrderedDict()
|
|
|
|
|
|
|
|
|
|
def collect_configs(node):
|
|
|
|
|
for include in node.get("includes", []):
|
|
|
|
|
collect_configs(include)
|
|
|
|
|
for key, value in node.get("configs", {}).items():
|
|
|
|
|
included_configs[key] = value
|
|
|
|
|
|
|
|
|
|
# Collect configurations from included files only (not the main file)
|
|
|
|
|
for include in tree.get("includes", []):
|
|
|
|
|
collect_configs(include)
|
|
|
|
|
|
|
|
|
|
return included_configs
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_main_configs(tree):
|
|
|
|
|
"""
|
|
|
|
|
Extract configuration options from the main file (excluding #include directives).
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
tree (dict): The configuration tree structure
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
OrderedDict: Dictionary of configuration options from the main file
|
|
|
|
|
"""
|
|
|
|
|
main_configs = OrderedDict()
|
|
|
|
|
for line in tree["raw_content"]:
|
|
|
|
|
key, value = parse_config_line(line)
|
|
|
|
|
if key is not None:
|
|
|
|
|
main_configs[key] = value
|
|
|
|
|
return main_configs
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_current_configs(config_path):
|
|
|
|
|
"""
|
|
|
|
|
Parse the current full configuration file.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
config_path (str): Path to the current configuration file
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
OrderedDict: Dictionary of configuration options from the current file
|
|
|
|
|
"""
|
|
|
|
|
configs = OrderedDict()
|
|
|
|
|
with open(config_path, "r", encoding="utf-8") as f:
|
|
|
|
|
for line in f:
|
|
|
|
|
key, value = parse_config_line(line)
|
|
|
|
|
if key is not None:
|
|
|
|
|
configs[key] = value
|
|
|
|
|
return configs
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def postprocess_inner(tree_path, added, changed, removed, output_path):
|
|
|
|
|
"""
|
|
|
|
|
Postprocess configuration changes to generate a defconfig with #include directives.
|
|
|
|
|
|
|
|
|
|
This function takes the specific changes (added, changed, removed) calculated
|
|
|
|
|
by postprocess and applies them to the original defconfig structure
|
|
|
|
|
represented by the tree, producing a new defconfig file.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
tree_path (str): Path to the config_tree.json generated during preprocessing of the ORIGINAL defconfig.
|
|
|
|
|
added (dict): {key: value} - Configurations added by the user.
|
|
|
|
|
changed (dict): {key: (old_value, new_value)} - Configurations changed by the user.
|
|
|
|
|
removed (dict): {key: old_value} - Configurations removed by the user.
|
|
|
|
|
output_path (str): Path where the new defconfig should be written.
|
|
|
|
|
"""
|
|
|
|
|
# 1. Load the original tree structure (this represents the structure of the ORIGINAL defconfig)
|
|
|
|
|
with open(tree_path, "r", encoding="utf-8") as f:
|
|
|
|
|
original_tree = json.load(f, object_pairs_hook=OrderedDict)
|
|
|
|
|
|
|
|
|
|
# 2. Get the original configuration sets from the tree
|
|
|
|
|
original_included_configs = get_all_included_configs(original_tree)
|
|
|
|
|
original_main_configs = get_main_configs(original_tree)
|
|
|
|
|
|
|
|
|
|
# 3. Dictionary to store the final configurations that will go into the main defconfig file
|
|
|
|
|
final_main_configs = OrderedDict()
|
|
|
|
|
|
|
|
|
|
# --- Logic to determine final content of the main defconfig file ---
|
|
|
|
|
|
|
|
|
|
# a. Handle configurations that were originally in included files
|
|
|
|
|
# We only place them in the main defconfig if they were explicitly added/changed/removed.
|
|
|
|
|
# If untouched, they remain in their included files implicitly.
|
|
|
|
|
for key in original_included_configs:
|
|
|
|
|
if key in added:
|
|
|
|
|
# User added/changed a config that was originally in an included file.
|
|
|
|
|
# It must now be explicitly set in the main defconfig to override the included value.
|
|
|
|
|
final_main_configs[key] = added[key]
|
|
|
|
|
elif key in changed:
|
|
|
|
|
# User changed a config that was originally in an included file.
|
|
|
|
|
final_main_configs[key] = changed[key][1] # Use the new value
|
|
|
|
|
elif key in removed:
|
|
|
|
|
# User removed a config that was originally in an included file.
|
|
|
|
|
# To "remove" it, we explicitly set it to opposite orig value in the main defconfig.
|
|
|
|
|
# This overrides the value from the included file.
|
|
|
|
|
final_main_configs[key] = opposite(removed[key])
|
|
|
|
|
|
|
|
|
|
# b. Handle configurations that were originally in the main file
|
|
|
|
|
# They should generally stay represented in the main file output.
|
|
|
|
|
for key in original_main_configs:
|
|
|
|
|
if key in added:
|
|
|
|
|
# User added/changed a config that was already in the main file.
|
|
|
|
|
final_main_configs[key] = added[key]
|
|
|
|
|
elif key in changed:
|
|
|
|
|
# User changed a config that was in the main file.
|
|
|
|
|
final_main_configs[key] = changed[key][1] # Use the new value
|
|
|
|
|
elif key in removed:
|
|
|
|
|
# User removed a config that was in the main file.
|
|
|
|
|
# Explicitly set to opposite orig value to override its previous state.
|
|
|
|
|
final_main_configs[key] = opposite(removed[key])
|
|
|
|
|
else:
|
|
|
|
|
# Config was in the original main file and user did NOT touch it.
|
|
|
|
|
# According to the new logic, we should PRESERVE these in the output main defconfig
|
|
|
|
|
# to maintain the structure and non-default values from the original main file.
|
|
|
|
|
# This prevents the output from becoming sparse if the user only made minor changes.
|
|
|
|
|
final_main_configs[key] = original_main_configs[key]
|
|
|
|
|
|
|
|
|
|
# c. Handle configurations that are entirely new (not present in original main or included)
|
|
|
|
|
# These must go into the main defconfig file.
|
|
|
|
|
for key, value in added.items():
|
|
|
|
|
if key not in original_main_configs and key not in original_included_configs:
|
|
|
|
|
final_main_configs[key] = value
|
|
|
|
|
|
|
|
|
|
# 4. Write the final output defconfig file
|
|
|
|
|
with open(output_path, "w", encoding="utf-8") as f:
|
|
|
|
|
# Write the original #include directives to preserve the structure
|
|
|
|
|
for include_line in original_tree.get("include_lines", []):
|
|
|
|
|
f.write(include_line + "\n")
|
|
|
|
|
|
|
|
|
|
# Add a newline for separation if there were includes
|
|
|
|
|
if original_tree.get("include_lines"):
|
|
|
|
|
f.write("\n")
|
|
|
|
|
|
|
|
|
|
# Write the final configurations for the main file in sorted order
|
|
|
|
|
final_write_list = []
|
|
|
|
|
for key, value in final_main_configs.items():
|
|
|
|
|
if value == "n":
|
|
|
|
|
final_write_list.append(f"# {key} is not set\n")
|
|
|
|
|
else:
|
|
|
|
|
final_write_list.append(f"{key}={value}\n")
|
|
|
|
|
|
|
|
|
|
# Sort configurations for consistent and readable output
|
|
|
|
|
final_write_list.sort()
|
|
|
|
|
for write_line in final_write_list:
|
|
|
|
|
f.write(write_line)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_config_diff(old_config, new_config):
|
|
|
|
|
"""
|
|
|
|
|
Compare two config dictionaries and return the differences.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
old_config (dict): The original configuration.
|
|
|
|
|
new_config (dict): The modified configuration.
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
tuple: (added, changed, removed)
|
|
|
|
|
added (dict): Items in new_config but not in old_config.
|
|
|
|
|
changed (dict): Items with different values. {key: (old_value, new_value)}.
|
|
|
|
|
removed (dict): Items in old_config but not in new_config.
|
|
|
|
|
"""
|
|
|
|
|
added = {}
|
|
|
|
|
changed = {}
|
|
|
|
|
removed = {}
|
|
|
|
|
|
|
|
|
|
# Find added and changed items
|
|
|
|
|
for key, new_value in new_config.items():
|
|
|
|
|
if key not in old_config:
|
|
|
|
|
added[key] = new_value
|
|
|
|
|
elif old_config[key] != new_value:
|
|
|
|
|
changed[key] = (old_config[key], new_value) # (old_value, new_value)
|
|
|
|
|
|
|
|
|
|
# Find removed items
|
|
|
|
|
for key in old_config:
|
|
|
|
|
if key not in new_config:
|
|
|
|
|
removed[key] = old_config[key]
|
|
|
|
|
|
|
|
|
|
return added, changed, removed
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def load_config_file(filepath):
|
|
|
|
|
"""
|
|
|
|
|
Load a .config or defconfig file into an OrderedDict.
|
|
|
|
|
"""
|
|
|
|
|
config = OrderedDict()
|
|
|
|
|
try:
|
|
|
|
|
with open(filepath, "r") as f:
|
|
|
|
|
for line in f:
|
|
|
|
|
key, value = parse_config_line(line)
|
|
|
|
|
if key is not None:
|
|
|
|
|
config[key] = value
|
|
|
|
|
except FileNotFoundError:
|
|
|
|
|
print(
|
|
|
|
|
f"Warning: Config file {filepath} not found. Treating as empty.",
|
|
|
|
|
file=sys.stderr,
|
|
|
|
|
)
|
|
|
|
|
return config
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def postprocess(
|
|
|
|
|
tree_path, original_defconfig_path, modified_defconfig_path, output_defconfig_path
|
|
|
|
|
):
|
|
|
|
|
"""
|
|
|
|
|
An improved postprocess that compares defconfig files before and after modification.
|
|
|
|
|
|
|
|
|
|
This function addresses the issue where Kconfig's savedefconfig omits default values,
|
|
|
|
|
making it hard to distinguish user deletions from optimizations.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
tree_path (str): Path to the config_tree.json generated during preprocessing.
|
|
|
|
|
original_defconfig_path (str): Path to the defconfig file BEFORE user modification.
|
|
|
|
|
modified_defconfig_path (str): Path to the defconfig file AFTER user modification.
|
|
|
|
|
output_defconfig_path (str): Path where the updated defconfig should be written.
|
|
|
|
|
"""
|
|
|
|
|
# 1. Load the defconfig files
|
|
|
|
|
defconfig_original = load_config_file(original_defconfig_path)
|
|
|
|
|
defconfig_modified = load_config_file(modified_defconfig_path)
|
|
|
|
|
|
|
|
|
|
# 2. Compare the defconfig files to find the actual user changes
|
|
|
|
|
added, changed, removed = get_config_diff(defconfig_original, defconfig_modified)
|
|
|
|
|
|
|
|
|
|
# 3. Use the new postprocess_inner function to generate the final defconfig
|
|
|
|
|
# Pass the calculated differences (added, changed, removed) and the original tree.
|
|
|
|
|
postprocess_inner(tree_path, added, changed, removed, output_defconfig_path)
|
|
|
|
|
|
2025-08-25 12:03:17 +08:00
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
if len(sys.argv) < 3:
|
|
|
|
|
print(
|
2025-09-09 22:05:33 +08:00
|
|
|
"Usage: process_config.py <mode> [options]",
|
|
|
|
|
file=sys.stderr,
|
|
|
|
|
)
|
|
|
|
|
print("Modes:", file=sys.stderr)
|
|
|
|
|
print(
|
|
|
|
|
" preprocess <output_file> <input_file> [include_paths...] [--tree <tree_file>]",
|
|
|
|
|
file=sys.stderr,
|
|
|
|
|
)
|
|
|
|
|
print(
|
|
|
|
|
" postprocess <tree_file> <original_defconfig> <modified_defconfig> <output_defconfig>",
|
2025-08-25 12:03:17 +08:00
|
|
|
file=sys.stderr,
|
|
|
|
|
)
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
2025-09-09 22:05:33 +08:00
|
|
|
mode = sys.argv[1]
|
2025-08-25 12:03:17 +08:00
|
|
|
|
2025-09-09 22:05:33 +08:00
|
|
|
if mode == "preprocess":
|
|
|
|
|
if len(sys.argv) < 4:
|
|
|
|
|
print(
|
|
|
|
|
"Usage: preprocess <output_file> <input_file> [include_paths...] [--tree <tree_file>]",
|
|
|
|
|
file=sys.stderr,
|
|
|
|
|
)
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
output_file = Path(sys.argv[2])
|
|
|
|
|
input_file = sys.argv[3]
|
|
|
|
|
include_dirs = []
|
|
|
|
|
tree_file = None
|
|
|
|
|
|
|
|
|
|
# Parse arguments
|
|
|
|
|
i = 4
|
|
|
|
|
while i < len(sys.argv):
|
|
|
|
|
if sys.argv[i] == "--tree" and i + 1 < len(sys.argv):
|
|
|
|
|
tree_file = sys.argv[i + 1]
|
|
|
|
|
i += 2
|
|
|
|
|
else:
|
|
|
|
|
include_dirs.append(sys.argv[i])
|
|
|
|
|
i += 1
|
2025-08-25 12:03:17 +08:00
|
|
|
|
2025-09-09 22:05:33 +08:00
|
|
|
if output_file.exists():
|
|
|
|
|
output_file.unlink()
|
|
|
|
|
|
|
|
|
|
preprocess(output_file, input_file, include_dirs, tree_file)
|
|
|
|
|
|
|
|
|
|
elif mode == "postprocess":
|
|
|
|
|
if len(sys.argv) < 6:
|
|
|
|
|
print(
|
|
|
|
|
"Usage: postprocess <tree_file> <original_defconfig> <modified_defconfig> <output_defconfig>",
|
|
|
|
|
file=sys.stderr,
|
|
|
|
|
)
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
tree_file = sys.argv[2]
|
|
|
|
|
original_defconfig = sys.argv[3]
|
|
|
|
|
modified_defconfig = sys.argv[4]
|
|
|
|
|
output_defconfig = sys.argv[5]
|
|
|
|
|
if Path(tree_file).is_file():
|
|
|
|
|
post_defconfig = output_defconfig + "tmp"
|
|
|
|
|
postprocess(
|
|
|
|
|
tree_file, original_defconfig, modified_defconfig, post_defconfig
|
|
|
|
|
)
|
|
|
|
|
shutil.copy2(post_defconfig, output_defconfig)
|
|
|
|
|
os.remove(post_defconfig)
|
|
|
|
|
else:
|
|
|
|
|
shutil.copy2(modified_defconfig, output_defconfig)
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
print(f"Unknown mode: {mode}", file=sys.stderr)
|
|
|
|
|
sys.exit(1)
|