Merge pull request #134 from ZhadowValker/fix/espidf-library-structure
fix: Preserve library directory structure in ESP-IDF component conversion
This commit is contained in:
commit
29a31d8e0a
|
|
@ -454,32 +454,42 @@ class ESPIDFCompiler:
|
||||||
found_any = True
|
found_any = True
|
||||||
header_to_comp[header] = 'user_libs_all'
|
header_to_comp[header] = 'user_libs_all'
|
||||||
|
|
||||||
# Copy all source files flat into the merged component directory.
|
# Preserve directory structure while merging libraries.
|
||||||
# First-writer wins for name conflicts (rare across Arduino libs).
|
# Skip non-buildable directories like examples, tests, docs.
|
||||||
for pattern in ('*.h', '*.cpp', '*.c', 'src/*.h', 'src/*.cpp', 'src/*.c'):
|
lib_root = src_root.parent if src_root.name == 'src' else src_root
|
||||||
glob_root = src_root.parent if pattern.startswith('src/') else src_root
|
has_src_layout = (lib_root / 'src').is_dir()
|
||||||
for f in glob_root.glob(pattern):
|
|
||||||
if not f.is_file():
|
|
||||||
continue
|
|
||||||
if f.name not in seen_names:
|
|
||||||
shutil.copy2(f, comp_dir / f.name)
|
|
||||||
seen_names.add(f.name)
|
|
||||||
if f.suffix in ('.cpp', '.c') and f.name not in cpp_files:
|
|
||||||
cpp_files.append(f.name)
|
|
||||||
|
|
||||||
# Also handle libraries that use an src/ subdirectory layout.
|
excluded_dirs = {
|
||||||
src_sub = (src_root / 'src') if (src_root / 'src').is_dir() else None
|
'.git', '.github', '.vscode', '__pycache__',
|
||||||
if src_sub is None and (src_root.parent / 'src').is_dir():
|
'docs', 'doc', 'example', 'examples', 'test', 'tests',
|
||||||
src_sub = src_root.parent / 'src'
|
'extras', 'ci', 'fuzz', 'fuzzing', 'benchmark', 'benchmarks',
|
||||||
if src_sub:
|
}
|
||||||
for f in src_sub.glob('**/*'):
|
|
||||||
if not f.is_file() or f.suffix not in ('.h', '.cpp', '.c'):
|
def _should_include(rel_path: Path) -> bool:
|
||||||
continue
|
parts = rel_path.parts
|
||||||
if f.name not in seen_names:
|
if any(part.lower() in excluded_dirs for part in parts[:-1]):
|
||||||
shutil.copy2(f, comp_dir / f.name)
|
return False
|
||||||
seen_names.add(f.name)
|
if rel_path.suffix not in ('.h', '.hpp', '.c', '.cpp'):
|
||||||
if f.suffix in ('.cpp', '.c') and f.name not in cpp_files:
|
return False
|
||||||
cpp_files.append(f.name)
|
if has_src_layout:
|
||||||
|
return parts[0] == 'src' or len(parts) == 1
|
||||||
|
return len(parts) == 1 or parts[0].lower() == 'utility'
|
||||||
|
|
||||||
|
for f in lib_root.rglob('*'):
|
||||||
|
if not f.is_file():
|
||||||
|
continue
|
||||||
|
rel_path = f.relative_to(lib_root)
|
||||||
|
if not _should_include(rel_path):
|
||||||
|
continue
|
||||||
|
# Track file by its relative path to preserve structure
|
||||||
|
file_key = str(rel_path).replace('\\', '/')
|
||||||
|
if file_key not in seen_names:
|
||||||
|
dest = comp_dir / rel_path
|
||||||
|
dest.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
shutil.copy2(f, dest)
|
||||||
|
seen_names.add(file_key)
|
||||||
|
if f.suffix in ('.cpp', '.c') and file_key not in cpp_files:
|
||||||
|
cpp_files.append(file_key)
|
||||||
|
|
||||||
# Scan newly copied headers for transitive includes.
|
# Scan newly copied headers for transitive includes.
|
||||||
for lib_file in comp_dir.glob('*.h'):
|
for lib_file in comp_dir.glob('*.h'):
|
||||||
|
|
@ -497,13 +507,22 @@ class ESPIDFCompiler:
|
||||||
return [], {}
|
return [], {}
|
||||||
|
|
||||||
srcs_line = 'SRCS ' + ' '.join(f'"{f}"' for f in sorted(cpp_files)) if cpp_files else ''
|
srcs_line = 'SRCS ' + ' '.join(f'"{f}"' for f in sorted(cpp_files)) if cpp_files else ''
|
||||||
|
|
||||||
|
# Generate INCLUDE_DIRS from the directory structure of copied files
|
||||||
|
include_dirs: set[str] = {'.'}
|
||||||
|
for file_key in seen_names:
|
||||||
|
parent = str(Path(file_key).parent)
|
||||||
|
if parent and parent != '.':
|
||||||
|
include_dirs.add(parent)
|
||||||
|
|
||||||
|
include_dirs_line = 'INCLUDE_DIRS ' + ' '.join(f'"{d}"' for d in sorted(include_dirs))
|
||||||
|
|
||||||
cmake_content = (
|
cmake_content = (
|
||||||
'# Auto-generated by Velxio — all user libraries merged into one component.\n'
|
'# Auto-generated by Velxio — all user libraries merged into one component.\n'
|
||||||
'# Single flat directory: every header sees every other header without\n'
|
'# Directory structure preserved for libraries like ArduinoJson with src/ layout.\n'
|
||||||
'# cross-component REQUIRES propagation.\n'
|
|
||||||
'idf_component_register(\n'
|
'idf_component_register(\n'
|
||||||
f' {srcs_line}\n'
|
f' {srcs_line}\n'
|
||||||
' INCLUDE_DIRS "."\n'
|
f' {include_dirs_line}\n'
|
||||||
f' REQUIRES {arduino_comp_name}\n'
|
f' REQUIRES {arduino_comp_name}\n'
|
||||||
')\n'
|
')\n'
|
||||||
)
|
)
|
||||||
|
|
@ -567,47 +586,80 @@ class ESPIDFCompiler:
|
||||||
comp_dir = user_libs_dir / safe_name
|
comp_dir = user_libs_dir / safe_name
|
||||||
comp_dir.mkdir(parents=True, exist_ok=True)
|
comp_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
# Collect all source files — preserve subdirectory structure via INCLUDE_DIRS
|
# Preserve the original library layout for actual buildable library code
|
||||||
# We copy files flat into the component root but add src/ as an include dir
|
# while skipping repo-only content such as examples, tests, and CI files.
|
||||||
cpp_files: list[str] = []
|
lib_root = src_root.parent if src_root.name == 'src' else src_root
|
||||||
seen_names: set[str] = set()
|
include_dirs: set[str] = {'.'}
|
||||||
|
cpp_files: set[str] = set()
|
||||||
|
copied_any = False
|
||||||
|
|
||||||
for pattern in ('*.h', '*.cpp', '*.c', 'src/*.h', 'src/*.cpp', 'src/*.c'):
|
has_src_layout = (lib_root / 'src').is_dir()
|
||||||
for f in src_root.parent.glob(pattern) if pattern.startswith('src/') else src_root.glob(pattern):
|
excluded_dirs = {
|
||||||
if not f.is_file():
|
'.git',
|
||||||
continue
|
'.github',
|
||||||
dest = comp_dir / f.name
|
'.vscode',
|
||||||
if f.name not in seen_names:
|
'__pycache__',
|
||||||
shutil.copy2(f, dest)
|
'docs',
|
||||||
seen_names.add(f.name)
|
'doc',
|
||||||
if f.suffix in ('.cpp', '.c') and f.name not in cpp_files:
|
'example',
|
||||||
cpp_files.append(f.name)
|
'examples',
|
||||||
|
'test',
|
||||||
|
'tests',
|
||||||
|
'extras',
|
||||||
|
'ci',
|
||||||
|
'fuzz',
|
||||||
|
'fuzzing',
|
||||||
|
'benchmark',
|
||||||
|
'benchmarks',
|
||||||
|
}
|
||||||
|
|
||||||
# Also copy from src/ subdirectory if present (e.g. Adafruit libraries)
|
def should_include(relative_path: Path) -> bool:
|
||||||
src_sub = src_root / 'src' if (src_root / 'src').is_dir() else None
|
parts = relative_path.parts
|
||||||
if src_sub is None and (src_root.parent / 'src').is_dir():
|
if any(part.lower() in excluded_dirs for part in parts[:-1]):
|
||||||
src_sub = src_root.parent / 'src'
|
return False
|
||||||
if src_sub:
|
if relative_path.suffix not in ('.h', '.hpp', '.c', '.cpp'):
|
||||||
for f in src_sub.glob('**/*'):
|
return False
|
||||||
if not f.is_file() or f.suffix not in ('.h', '.cpp', '.c'):
|
|
||||||
continue
|
if has_src_layout:
|
||||||
if f.name not in seen_names:
|
return parts[0] == 'src' or len(parts) == 1
|
||||||
shutil.copy2(f, comp_dir / f.name)
|
|
||||||
seen_names.add(f.name)
|
return len(parts) == 1 or parts[0].lower() == 'utility'
|
||||||
if f.suffix in ('.cpp', '.c') and f.name not in cpp_files:
|
|
||||||
cpp_files.append(f.name)
|
for f in lib_root.rglob('*'):
|
||||||
|
if not f.is_file():
|
||||||
|
continue
|
||||||
|
rel_path = f.relative_to(lib_root)
|
||||||
|
if not should_include(rel_path):
|
||||||
|
continue
|
||||||
|
dest = comp_dir / rel_path
|
||||||
|
dest.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
shutil.copy2(f, dest)
|
||||||
|
copied_any = True
|
||||||
|
include_dirs.add(str(rel_path.parent).replace('\\', '/'))
|
||||||
|
if f.suffix in ('.cpp', '.c'):
|
||||||
|
cpp_files.add(str(rel_path).replace('\\', '/'))
|
||||||
|
|
||||||
|
if not copied_any:
|
||||||
|
raise ValueError(f'No buildable source files found in library {lib_dir_name}')
|
||||||
|
|
||||||
# Generate CMakeLists.txt for this component
|
# Generate CMakeLists.txt for this component
|
||||||
|
include_dirs.discard('.')
|
||||||
|
ordered_include_dirs = ['.'] + sorted(d for d in include_dirs if d and d != '.')
|
||||||
|
|
||||||
if cpp_files:
|
if cpp_files:
|
||||||
srcs_line = 'SRCS ' + ' '.join(f'"{f}"' for f in sorted(cpp_files))
|
srcs_line = 'SRCS ' + ' '.join(f'"{f}"' for f in sorted(cpp_files))
|
||||||
else:
|
else:
|
||||||
srcs_line = '# header-only library'
|
srcs_line = '# header-only library'
|
||||||
|
|
||||||
|
include_dirs_line = 'INCLUDE_DIRS ' + ' '.join(
|
||||||
|
f'"{include_dir}"' for include_dir in ordered_include_dirs
|
||||||
|
)
|
||||||
|
|
||||||
cmake_content = (
|
cmake_content = (
|
||||||
f'# Auto-generated by Velxio for library: {lib_dir_name}\n'
|
f'# Auto-generated by Velxio for library: {lib_dir_name}\n'
|
||||||
f'idf_component_register(\n'
|
f'idf_component_register(\n'
|
||||||
f' {srcs_line}\n'
|
f' {srcs_line}\n'
|
||||||
f' INCLUDE_DIRS "."\n'
|
f' {include_dirs_line}\n'
|
||||||
f' REQUIRES {arduino_comp_name}\n'
|
f' REQUIRES {arduino_comp_name}\n'
|
||||||
f')\n'
|
f')\n'
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue