mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 15:12:06 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			125 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			125 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| import json
 | |
| from pathlib import Path
 | |
| import re
 | |
| import subprocess
 | |
| 
 | |
| 
 | |
| def load_idedata(environment, temp_folder, platformio_ini):
 | |
|     build_environment = environment.replace("-tidy", "")
 | |
|     build_dir = Path(temp_folder) / f"build-{build_environment}"
 | |
|     Path(build_dir).mkdir(exist_ok=True)
 | |
|     Path(build_dir / "platformio.ini").write_text(
 | |
|         Path(platformio_ini).read_text(encoding="utf-8"), encoding="utf-8"
 | |
|     )
 | |
|     esphome_dir = Path(build_dir / "esphome")
 | |
|     esphome_dir.mkdir(exist_ok=True)
 | |
|     Path(esphome_dir / "main.cpp").write_text(
 | |
|         """
 | |
| #include <zephyr/kernel.h>
 | |
| int main() { return 0;}
 | |
| """,
 | |
|         encoding="utf-8",
 | |
|     )
 | |
|     zephyr_dir = Path(build_dir / "zephyr")
 | |
|     zephyr_dir.mkdir(exist_ok=True)
 | |
|     Path(zephyr_dir / "prj.conf").write_text(
 | |
|         """
 | |
| CONFIG_NEWLIB_LIBC=y
 | |
| """,
 | |
|         encoding="utf-8",
 | |
|     )
 | |
|     subprocess.run(["pio", "run", "-e", build_environment, "-d", build_dir], check=True)
 | |
| 
 | |
|     def extract_include_paths(command):
 | |
|         include_paths = []
 | |
|         include_pattern = re.compile(r'("-I\s*[^"]+)|(-isystem\s*[^\s]+)|(-I\s*[^\s]+)')
 | |
|         for match in include_pattern.findall(command):
 | |
|             split_strings = re.split(
 | |
|                 r"\s*-\s*(?:I|isystem)", list(filter(lambda x: x, match))[0]
 | |
|             )
 | |
|             include_paths.append(split_strings[1])
 | |
|         return include_paths
 | |
| 
 | |
|     def extract_defines(command):
 | |
|         defines = []
 | |
|         define_pattern = re.compile(r"-D\s*([^\s]+)")
 | |
|         for match in define_pattern.findall(command):
 | |
|             if match not in ("_ASMLANGUAGE"):
 | |
|                 defines.append(match)
 | |
|         return defines
 | |
| 
 | |
|     def find_cxx_path(commands):
 | |
|         for entry in commands:
 | |
|             command = entry["command"]
 | |
|             cxx_path = command.split()[0]
 | |
|             if not cxx_path.endswith("++"):
 | |
|                 continue
 | |
|             return cxx_path
 | |
| 
 | |
|     def get_builtin_include_paths(compiler):
 | |
|         result = subprocess.run(
 | |
|             [compiler, "-E", "-x", "c++", "-", "-v"],
 | |
|             input="",
 | |
|             text=True,
 | |
|             stderr=subprocess.PIPE,
 | |
|             stdout=subprocess.DEVNULL,
 | |
|             check=True,
 | |
|         )
 | |
|         include_paths = []
 | |
|         start_collecting = False
 | |
|         for line in result.stderr.splitlines():
 | |
|             if start_collecting:
 | |
|                 if line.startswith(" "):
 | |
|                     include_paths.append(line.strip())
 | |
|                 else:
 | |
|                     break
 | |
|             if "#include <...> search starts here:" in line:
 | |
|                 start_collecting = True
 | |
|         return include_paths
 | |
| 
 | |
|     def extract_cxx_flags(command):
 | |
|         flags = []
 | |
|         # Extracts CXXFLAGS from the command string, excluding includes and defines.
 | |
|         flag_pattern = re.compile(
 | |
|             r"(-O[0-3s]|-g|-std=[^\s]+|-Wall|-Wextra|-Werror|--[^\s]+|-f[^\s]+|-m[^\s]+|-imacros\s*[^\s]+)"
 | |
|         )
 | |
|         for match in flag_pattern.findall(command):
 | |
|             flags.append(match.replace("-imacros ", "-imacros"))
 | |
|         return flags
 | |
| 
 | |
|     def transform_to_idedata_format(compile_commands):
 | |
|         cxx_path = find_cxx_path(compile_commands)
 | |
|         idedata = {
 | |
|             "includes": {
 | |
|                 "toolchain": get_builtin_include_paths(cxx_path),
 | |
|                 "build": set(),
 | |
|             },
 | |
|             "defines": set(),
 | |
|             "cxx_path": cxx_path,
 | |
|             "cxx_flags": set(),
 | |
|         }
 | |
| 
 | |
|         for entry in compile_commands:
 | |
|             command = entry["command"]
 | |
|             exec = command.split()[0]
 | |
|             if exec != cxx_path:
 | |
|                 continue
 | |
| 
 | |
|             idedata["includes"]["build"].update(extract_include_paths(command))
 | |
|             idedata["defines"].update(extract_defines(command))
 | |
|             idedata["cxx_flags"].update(extract_cxx_flags(command))
 | |
| 
 | |
|         # Convert sets to lists for JSON serialization
 | |
|         idedata["includes"]["build"] = list(idedata["includes"]["build"])
 | |
|         idedata["defines"] = list(idedata["defines"])
 | |
|         idedata["cxx_flags"] = list(idedata["cxx_flags"])
 | |
| 
 | |
|         return idedata
 | |
| 
 | |
|     compile_commands = json.loads(
 | |
|         Path(
 | |
|             build_dir / ".pio" / "build" / build_environment / "compile_commands.json"
 | |
|         ).read_text(encoding="utf-8")
 | |
|     )
 | |
|     return transform_to_idedata_format(compile_commands)
 |