import sys
import os
import json
MODULE_DEPS_MAP = {}
def load_all_specs(spec_dir):
global MODULE_DEPS_MAP
for root, dirs, files in os.walk(spec_dir):
for f in files:
if not f.endswith('.spec'):
continue
spec_path = os.path.join(root, f)
module_name = f.split('.')[0]
if module_name in MODULE_DEPS_MAP:
continue
_process_spec_file(spec_path, module_name)
def _process_spec_file(spec_path, module_name):
global MODULE_DEPS_MAP
try:
with open(spec_path, 'r', encoding='utf-8') as sf:
data = json.loads(sf.read())
depends_str = data.get("depends", "")
deps = [d.strip() for d in depends_str.split(',') if d.strip()] if depends_str else []
MODULE_DEPS_MAP[module_name] = deps
except (json.JSONDecodeError, IOError):
MODULE_DEPS_MAP[module_name] = []
def get_transitive_deps(module_name, visited=None):
if visited is None:
visited = set()
if module_name in visited:
return set()
visited.add(module_name)
direct_deps = MODULE_DEPS_MAP.get(module_name, [])
all_deps = set()
for dep in direct_deps:
if dep == '' or dep == module_name:
continue
all_deps.add(dep)
all_deps.update(get_transitive_deps(dep, visited))
return all_deps
def find_redundant_deps(module_name):
direct_deps = MODULE_DEPS_MAP.get(module_name, [])
if not direct_deps:
return []
redundant = []
for dep in direct_deps:
if dep == '' or dep == module_name:
continue
transitive_from_others = set()
for other_dep in direct_deps:
if other_dep != dep and other_dep != '':
transitive_from_others.update(get_transitive_deps(other_dep))
if dep in transitive_from_others:
redundant.append(dep)
return redundant
def get_minimal_deps(module_name):
direct_deps = MODULE_DEPS_MAP.get(module_name, [])
redundant = find_redundant_deps(module_name)
minimal = [d for d in direct_deps if d not in redundant and d != '']
return minimal, redundant
def set_spec(module_name, deps, cmake_file_path):
with open(cmake_file_path, 'w', encoding='utf-8') as out:
out.write('#auto generated file. DO NOT EDIT\n')
out.write('set(%s_depend_module "")\n' % module_name)
for depend in sorted(deps):
if depend == '' or depend == module_name:
continue
out.write('list(APPEND %s_depend_module %s)\n' % (module_name, depend))
def find_project_root(spec_file):
dir_path = os.path.dirname(os.path.abspath(spec_file))
while dir_path != os.path.dirname(dir_path):
cmake_path = os.path.join(dir_path, 'CMakeLists.txt')
if not os.path.exists(cmake_path):
dir_path = os.path.dirname(dir_path)
continue
if _is_project_root(cmake_path):
return dir_path
dir_path = os.path.dirname(dir_path)
return os.path.dirname(os.path.abspath(spec_file))
def _is_project_root(cmake_path):
try:
with open(cmake_path, 'r') as f:
cmake_content = f.read()
return 'project(K-NET' in cmake_content or 'project(DP' in cmake_content
except (IOError, FileNotFoundError):
return False
def main():
if len(sys.argv) < 3:
print("Usage: depends_parse.py <spec_file> <cmake_output_file>")
sys.exit(1)
spec_file = sys.argv[1]
cmake_file = sys.argv[2]
module = os.path.basename(spec_file).split('.')[0]
project_root = find_project_root(spec_file)
load_all_specs(project_root)
direct_deps = MODULE_DEPS_MAP.get(module, [])
all_deps = get_transitive_deps(module)
all_deps.discard(module)
minimal_deps, redundant_deps = get_minimal_deps(module)
print(f"Module: {module}")
print(f"Direct dependencies: {direct_deps}")
if redundant_deps:
print(f"Redundant dependencies (can be removed): {redundant_deps}")
print(f"Minimal dependencies: {minimal_deps}")
print(f"All dependencies (including transitive): {sorted(all_deps)}")
set_spec(module, all_deps, cmake_file)
print(f"Generated cmake file: {cmake_file}")
if __name__ == "__main__":
main()