修好了那个保留用户区域问题
This commit is contained in:
parent
d1c3b2747a
commit
52b6449c4f
@ -661,8 +661,8 @@ def patch_uart_interrupts(project_path, uart_instances):
|
|||||||
flags=re.DOTALL
|
flags=re.DOTALL
|
||||||
)
|
)
|
||||||
|
|
||||||
with open(it_path, "w", encoding="utf-8") as f:
|
# 使用save_with_preserve保存文件以保留用户区域
|
||||||
f.write(code)
|
CodeGenerator.save_with_preserve(it_path, code)
|
||||||
|
|
||||||
|
|
||||||
class bsp_uart(BspPeripheralBase):
|
class bsp_uart(BspPeripheralBase):
|
||||||
|
|||||||
@ -75,10 +75,8 @@ def generate_device_header(project_path, enabled_devices):
|
|||||||
replacement = f'/* AUTO GENERATED SIGNALS BEGIN */\n{signals_text}\n/* AUTO GENERATED SIGNALS END */'
|
replacement = f'/* AUTO GENERATED SIGNALS BEGIN */\n{signals_text}\n/* AUTO GENERATED SIGNALS END */'
|
||||||
content = re.sub(pattern, replacement, content, flags=re.DOTALL)
|
content = re.sub(pattern, replacement, content, flags=re.DOTALL)
|
||||||
|
|
||||||
# 保存文件
|
# 使用save_with_preserve保存文件以保留用户区域
|
||||||
os.makedirs(os.path.dirname(dst_path), exist_ok=True)
|
CodeGenerator.save_with_preserve(dst_path, content)
|
||||||
with open(dst_path, 'w', encoding='utf-8') as f:
|
|
||||||
f.write(content)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -251,24 +249,7 @@ class DeviceSimple(QWidget):
|
|||||||
dst_path = os.path.join(self.project_path, f"User/device/{filename}")
|
dst_path = os.path.join(self.project_path, f"User/device/{filename}")
|
||||||
|
|
||||||
if os.path.exists(src_path):
|
if os.path.exists(src_path):
|
||||||
# 头文件和源文件都做变量替换
|
# 读取模板文件内容
|
||||||
with open(src_path, 'r', encoding='utf-8') as f:
|
|
||||||
content = f.read()
|
|
||||||
for var_name, device_name in bsp_config.items():
|
|
||||||
content = content.replace(var_name, device_name)
|
|
||||||
os.makedirs(os.path.dirname(dst_path), exist_ok=True)
|
|
||||||
with open(dst_path, 'w', encoding='utf-8') as f:
|
|
||||||
f.write(content)
|
|
||||||
|
|
||||||
if file_type == 'header':
|
|
||||||
# 头文件需要保留用户区域
|
|
||||||
os.makedirs(os.path.dirname(dst_path), exist_ok=True)
|
|
||||||
with open(src_path, 'r', encoding='utf-8') as f:
|
|
||||||
content = f.read()
|
|
||||||
CodeGenerator.save_with_preserve(dst_path, content)
|
|
||||||
|
|
||||||
elif file_type == 'source':
|
|
||||||
# 源文件需要替换BSP设备名称
|
|
||||||
with open(src_path, 'r', encoding='utf-8') as f:
|
with open(src_path, 'r', encoding='utf-8') as f:
|
||||||
content = f.read()
|
content = f.read()
|
||||||
|
|
||||||
@ -276,7 +257,12 @@ class DeviceSimple(QWidget):
|
|||||||
for var_name, device_name in bsp_config.items():
|
for var_name, device_name in bsp_config.items():
|
||||||
content = content.replace(var_name, device_name)
|
content = content.replace(var_name, device_name)
|
||||||
|
|
||||||
# 保存文件
|
# 根据文件类型选择保存方式
|
||||||
|
if file_type == 'header':
|
||||||
|
# 头文件需要保留用户区域
|
||||||
|
CodeGenerator.save_with_preserve(dst_path, content)
|
||||||
|
else:
|
||||||
|
# 源文件直接保存(不需要保留用户区域)
|
||||||
os.makedirs(os.path.dirname(dst_path), exist_ok=True)
|
os.makedirs(os.path.dirname(dst_path), exist_ok=True)
|
||||||
with open(dst_path, 'w', encoding='utf-8') as f:
|
with open(dst_path, 'w', encoding='utf-8') as f:
|
||||||
f.write(content)
|
f.write(content)
|
||||||
|
|||||||
@ -35,7 +35,9 @@ class CodeGenerator:
|
|||||||
def save_file(content: str, file_path: str) -> bool:
|
def save_file(content: str, file_path: str) -> bool:
|
||||||
"""保存文件"""
|
"""保存文件"""
|
||||||
try:
|
try:
|
||||||
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
dir_path = os.path.dirname(file_path)
|
||||||
|
if dir_path: # 只有当目录路径不为空时才创建
|
||||||
|
os.makedirs(dir_path, exist_ok=True)
|
||||||
with open(file_path, 'w', encoding='utf-8') as f:
|
with open(file_path, 'w', encoding='utf-8') as f:
|
||||||
f.write(content)
|
f.write(content)
|
||||||
return True
|
return True
|
||||||
@ -176,6 +178,11 @@ class CodeGenerator:
|
|||||||
用户代码...
|
用户代码...
|
||||||
/* USER REGION_NAME END */
|
/* USER REGION_NAME END */
|
||||||
|
|
||||||
|
支持的格式示例:
|
||||||
|
- /* USER REFEREE BEGIN */ ... /* USER REFEREE END */
|
||||||
|
- /* USER CODE BEGIN */ ... /* USER CODE END */
|
||||||
|
- /* USER CUSTOM_NAME BEGIN */ ... /* USER CUSTOM_NAME END */
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
new_code: 新的代码内容
|
new_code: 新的代码内容
|
||||||
old_code: 旧的代码内容
|
old_code: 旧的代码内容
|
||||||
@ -186,23 +193,40 @@ class CodeGenerator:
|
|||||||
if not old_code:
|
if not old_code:
|
||||||
return new_code
|
return new_code
|
||||||
|
|
||||||
|
# 更灵活的正则表达式,支持更多格式的用户区域标记
|
||||||
|
# 匹配 /* USER 任意字符 BEGIN */ ... /* USER 相同字符 END */
|
||||||
pattern = re.compile(
|
pattern = re.compile(
|
||||||
r"/\*\s*(USER [A-Z0-9_ ]+)\s*BEGIN\s*\*/(.*?)/\*\s*\1\s*END\s*\*/",
|
r"/\*\s*USER\s+([A-Za-z0-9_\s]+?)\s+BEGIN\s*\*/(.*?)/\*\s*USER\s+\1\s+END\s*\*/",
|
||||||
re.DOTALL
|
re.DOTALL | re.IGNORECASE
|
||||||
)
|
)
|
||||||
|
|
||||||
# 提取旧代码中的所有用户区域
|
# 提取旧代码中的所有用户区域
|
||||||
old_regions = {m.group(1): m.group(2) for m in pattern.finditer(old_code)}
|
old_regions = {}
|
||||||
|
for match in pattern.finditer(old_code):
|
||||||
|
region_name = match.group(1).strip()
|
||||||
|
region_content = match.group(2)
|
||||||
|
old_regions[region_name.upper()] = region_content
|
||||||
|
|
||||||
def repl(m):
|
# 替换函数
|
||||||
region_name = m.group(1)
|
def repl(match):
|
||||||
|
region_name = match.group(1).strip().upper()
|
||||||
|
current_content = match.group(2)
|
||||||
old_content = old_regions.get(region_name)
|
old_content = old_regions.get(region_name)
|
||||||
if old_content is not None:
|
|
||||||
# 替换为旧的用户内容
|
|
||||||
return m.group(0).replace(m.group(2), old_content)
|
|
||||||
return m.group(0)
|
|
||||||
|
|
||||||
return pattern.sub(repl, new_code)
|
if old_content is not None:
|
||||||
|
# 直接替换中间的内容,保持原有的注释标记不变
|
||||||
|
return match.group(0).replace(current_content, old_content)
|
||||||
|
|
||||||
|
return match.group(0)
|
||||||
|
|
||||||
|
# 应用替换
|
||||||
|
result = pattern.sub(repl, new_code)
|
||||||
|
|
||||||
|
# 调试信息:记录找到的用户区域
|
||||||
|
if old_regions:
|
||||||
|
print(f"保留了 {len(old_regions)} 个用户区域: {list(old_regions.keys())}")
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def save_with_preserve(file_path: str, new_code: str) -> bool:
|
def save_with_preserve(file_path: str, new_code: str) -> bool:
|
||||||
@ -229,7 +253,9 @@ class CodeGenerator:
|
|||||||
final_code = CodeGenerator.preserve_all_user_regions(new_code, old_code)
|
final_code = CodeGenerator.preserve_all_user_regions(new_code, old_code)
|
||||||
|
|
||||||
# 确保目录存在
|
# 确保目录存在
|
||||||
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
dir_path = os.path.dirname(file_path)
|
||||||
|
if dir_path: # 只有当目录路径不为空时才创建
|
||||||
|
os.makedirs(dir_path, exist_ok=True)
|
||||||
|
|
||||||
# 保存文件
|
# 保存文件
|
||||||
with open(file_path, "w", encoding="utf-8") as f:
|
with open(file_path, "w", encoding="utf-8") as f:
|
||||||
@ -450,6 +476,11 @@ class CodeGenerator:
|
|||||||
def extract_user_regions(code: str) -> Dict[str, str]:
|
def extract_user_regions(code: str) -> Dict[str, str]:
|
||||||
"""从代码中提取所有用户区域
|
"""从代码中提取所有用户区域
|
||||||
|
|
||||||
|
支持提取各种格式的用户区域:
|
||||||
|
- /* USER REFEREE BEGIN */ ... /* USER REFEREE END */
|
||||||
|
- /* USER CODE BEGIN */ ... /* USER CODE END */
|
||||||
|
- /* USER CUSTOM_NAME BEGIN */ ... /* USER CUSTOM_NAME END */
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
code: 要提取的代码内容
|
code: 要提取的代码内容
|
||||||
|
|
||||||
@ -459,9 +490,68 @@ class CodeGenerator:
|
|||||||
if not code:
|
if not code:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
# 使用与preserve_all_user_regions相同的正则表达式
|
||||||
pattern = re.compile(
|
pattern = re.compile(
|
||||||
r"/\*\s*USER CODE BEGIN ([A-Za-z0-9_ ]+)\s*\*/(.*?)/\*\s*USER CODE END \1\s*\*/",
|
r"/\*\s*USER\s+([A-Za-z0-9_\s]+?)\s+BEGIN\s*\*/(.*?)/\*\s*USER\s+\1\s+END\s*\*/",
|
||||||
re.DOTALL
|
re.DOTALL | re.IGNORECASE
|
||||||
)
|
)
|
||||||
|
|
||||||
return {m.group(1): m.group(2) for m in pattern.finditer(code)}
|
regions = {}
|
||||||
|
for match in pattern.finditer(code):
|
||||||
|
region_name = match.group(1).strip().upper()
|
||||||
|
region_content = match.group(2)
|
||||||
|
regions[region_name] = region_content
|
||||||
|
|
||||||
|
return regions
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def debug_user_regions(new_code: str, old_code: str, verbose: bool = False) -> Dict[str, Dict[str, str]]:
|
||||||
|
"""调试用户区域,显示新旧内容的对比
|
||||||
|
|
||||||
|
Args:
|
||||||
|
new_code: 新的代码内容
|
||||||
|
old_code: 旧的代码内容
|
||||||
|
verbose: 是否输出详细信息
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict: 包含所有用户区域信息的字典
|
||||||
|
"""
|
||||||
|
if verbose:
|
||||||
|
print("=== 用户区域调试信息 ===")
|
||||||
|
|
||||||
|
new_regions = CodeGenerator.extract_user_regions(new_code)
|
||||||
|
old_regions = CodeGenerator.extract_user_regions(old_code)
|
||||||
|
|
||||||
|
all_region_names = set(new_regions.keys()) | set(old_regions.keys())
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
|
||||||
|
for region_name in sorted(all_region_names):
|
||||||
|
new_content = new_regions.get(region_name, "")
|
||||||
|
old_content = old_regions.get(region_name, "")
|
||||||
|
|
||||||
|
result[region_name] = {
|
||||||
|
"new_content": new_content,
|
||||||
|
"old_content": old_content,
|
||||||
|
"will_preserve": bool(old_content),
|
||||||
|
"exists_in_new": region_name in new_regions,
|
||||||
|
"exists_in_old": region_name in old_regions
|
||||||
|
}
|
||||||
|
|
||||||
|
if verbose:
|
||||||
|
status = "保留旧内容" if old_content else "使用新内容"
|
||||||
|
print(f"\n区域: {region_name} ({status})")
|
||||||
|
print(f" 新模板中存在: {'是' if region_name in new_regions else '否'}")
|
||||||
|
print(f" 旧文件中存在: {'是' if region_name in old_regions else '否'}")
|
||||||
|
|
||||||
|
if new_content.strip():
|
||||||
|
print(f" 新内容预览: {repr(new_content.strip()[:50])}...")
|
||||||
|
if old_content.strip():
|
||||||
|
print(f" 旧内容预览: {repr(old_content.strip()[:50])}...")
|
||||||
|
|
||||||
|
if verbose:
|
||||||
|
print(f"\n总计: {len(all_region_names)} 个用户区域")
|
||||||
|
preserve_count = sum(1 for info in result.values() if info["will_preserve"])
|
||||||
|
print(f"将保留: {preserve_count} 个区域的旧内容")
|
||||||
|
|
||||||
|
return result
|
||||||
@ -1,48 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
检查GitHub Releases API响应结构
|
|
||||||
"""
|
|
||||||
|
|
||||||
import requests
|
|
||||||
import json
|
|
||||||
|
|
||||||
def check_releases_structure():
|
|
||||||
"""检查GitHub releases的API响应结构"""
|
|
||||||
try:
|
|
||||||
url = "https://api.github.com/repos/goldenfishs/MRobot/releases/latest"
|
|
||||||
response = requests.get(url, timeout=10)
|
|
||||||
|
|
||||||
if response.status_code == 200:
|
|
||||||
data = response.json()
|
|
||||||
|
|
||||||
print("Release信息:")
|
|
||||||
print(f"标签: {data.get('tag_name')}")
|
|
||||||
print(f"名称: {data.get('name')}")
|
|
||||||
print(f"发布时间: {data.get('published_at')}")
|
|
||||||
print(f"是否为预发布: {data.get('prerelease')}")
|
|
||||||
print(f"是否为草稿: {data.get('draft')}")
|
|
||||||
|
|
||||||
print("\n可用的资源文件:")
|
|
||||||
assets = data.get('assets', [])
|
|
||||||
|
|
||||||
if not assets:
|
|
||||||
print("❌ 没有找到任何资源文件")
|
|
||||||
print("建议在GitHub Release中上传安装包文件")
|
|
||||||
else:
|
|
||||||
for i, asset in enumerate(assets):
|
|
||||||
print(f" {i+1}. {asset['name']}")
|
|
||||||
print(f" 大小: {asset['size']} 字节")
|
|
||||||
print(f" 下载链接: {asset['browser_download_url']}")
|
|
||||||
print(f" 内容类型: {asset.get('content_type', 'unknown')}")
|
|
||||||
print()
|
|
||||||
|
|
||||||
print(f"\n更新说明:\n{data.get('body', '无')}")
|
|
||||||
|
|
||||||
else:
|
|
||||||
print(f"❌ API请求失败,状态码: {response.status_code}")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ 检查失败: {e}")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
check_releases_structure()
|
|
||||||
@ -1,136 +0,0 @@
|
|||||||
# CodeGenerator 使用情况完整检查报告
|
|
||||||
|
|
||||||
## 检查目标
|
|
||||||
确保 `CodeGenerator` 类在开发环境和打包exe环境中都能正常工作,不会出现 `local variable 'CodeGenerator' referenced before assignment` 错误。
|
|
||||||
|
|
||||||
## 已修复的问题
|
|
||||||
|
|
||||||
### 1. 静态方法中的导入问题
|
|
||||||
✅ **已修复** - 以下静态方法已添加本地导入:
|
|
||||||
|
|
||||||
- `app/code_generate_interface.py`
|
|
||||||
- `generate_code()` - 第270行已添加本地导入
|
|
||||||
- `_load_csv_and_build_tree()` - 第348行已添加本地导入
|
|
||||||
|
|
||||||
- `app/code_page/component_interface.py`
|
|
||||||
- `component.generate_component()` - 第289行已添加本地导入
|
|
||||||
|
|
||||||
- `app/code_page/bsp_interface.py`
|
|
||||||
- `bsp.generate_bsp()` - 第1240行已添加本地导入
|
|
||||||
|
|
||||||
- `app/code_page/device_interface.py`
|
|
||||||
- `generate_device_header()` - 第44行已添加本地导入
|
|
||||||
- `get_device_page()` - 第321行已添加本地导入
|
|
||||||
|
|
||||||
### 2. 相对导入问题
|
|
||||||
✅ **已修复** - 统一使用绝对导入:
|
|
||||||
- `app/code_page/component_interface.py` 第321行的相对导入已改为绝对导入
|
|
||||||
|
|
||||||
### 3. 缓存和性能优化
|
|
||||||
✅ **已优化**:
|
|
||||||
- 添加了 `_assets_dir_cache` 和 `_assets_dir_initialized` 缓存机制
|
|
||||||
- 优化了 `get_template_dir()` 方法,减少重复日志输出
|
|
||||||
- 改进了路径计算,避免重复的文件系统操作
|
|
||||||
|
|
||||||
## 安全的使用场景
|
|
||||||
|
|
||||||
### 1. 实例方法中的使用(✅ 安全)
|
|
||||||
这些使用 `CodeGenerator` 的地方都是在类的实例方法中,可以正常使用顶层导入:
|
|
||||||
|
|
||||||
- `ComponentSimple.__init__()` - 第108行
|
|
||||||
- `ComponentSimple._generate_component_code_internal()` - 第172行
|
|
||||||
- `ComponentSimple._get_component_template_dir()` - 第182行
|
|
||||||
- `ComponentSimple._save_config()` - 第186, 191行
|
|
||||||
- `ComponentSimple._load_config()` - 第195行
|
|
||||||
- `DevicePageBase.__init__()` 及相关方法
|
|
||||||
- `BspPeripheralBase` 各种实例方法
|
|
||||||
- `DataInterface.show_user_code_files()` - 已有本地导入
|
|
||||||
- `DataInterface.generate_code()` - 已有本地导入
|
|
||||||
- `DataInterface.generate_task_code()` - 已有本地导入
|
|
||||||
|
|
||||||
### 2. 模块级别函数(需要验证)
|
|
||||||
以下独立函数需要确认是否使用了 `CodeGenerator`:
|
|
||||||
|
|
||||||
- `load_device_config()`
|
|
||||||
- `get_available_bsp_devices()`
|
|
||||||
- `load_descriptions()` (bsp)
|
|
||||||
- `get_available_*()` 系列函数
|
|
||||||
|
|
||||||
## 打包环境优化
|
|
||||||
|
|
||||||
### 1. 路径处理优化
|
|
||||||
✅ **已优化** `get_assets_dir()` 方法:
|
|
||||||
- 优先使用 `sys._MEIPASS`(PyInstaller临时目录)
|
|
||||||
- 后备使用可执行文件目录
|
|
||||||
- 增加工作目录查找作为最后选择
|
|
||||||
- 改进了开发环境的目录查找逻辑
|
|
||||||
- 添加了路径规范化处理
|
|
||||||
|
|
||||||
### 2. 错误处理改进
|
|
||||||
✅ **已改进**:
|
|
||||||
- 更好的错误提示信息
|
|
||||||
- 只在第一次访问路径时显示警告
|
|
||||||
- 防止重复日志输出
|
|
||||||
|
|
||||||
## 建议的进一步改进
|
|
||||||
|
|
||||||
### 1. 添加环境检测工具方法
|
|
||||||
```python
|
|
||||||
@staticmethod
|
|
||||||
def is_frozen():
|
|
||||||
"""检测是否在打包环境中运行"""
|
|
||||||
return getattr(sys, 'frozen', False)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_base_path():
|
|
||||||
"""获取基础路径,自动适配开发/打包环境"""
|
|
||||||
if CodeGenerator.is_frozen():
|
|
||||||
return getattr(sys, '_MEIPASS', os.path.dirname(sys.executable))
|
|
||||||
else:
|
|
||||||
return os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 配置验证方法
|
|
||||||
```python
|
|
||||||
@staticmethod
|
|
||||||
def validate_assets():
|
|
||||||
"""验证assets目录是否存在并包含必要文件"""
|
|
||||||
assets_dir = CodeGenerator.get_assets_dir()
|
|
||||||
required_dirs = ["User_code/bsp", "User_code/component", "User_code/device"]
|
|
||||||
|
|
||||||
for req_dir in required_dirs:
|
|
||||||
path = os.path.join(assets_dir, req_dir)
|
|
||||||
if not os.path.exists(path):
|
|
||||||
return False, f"缺少必要目录: {path}"
|
|
||||||
return True, "Assets目录验证通过"
|
|
||||||
```
|
|
||||||
|
|
||||||
## 测试清单
|
|
||||||
|
|
||||||
### 开发环境测试
|
|
||||||
- [ ] 启动应用程序无错误
|
|
||||||
- [ ] 生成BSP代码功能正常
|
|
||||||
- [ ] 生成Component代码功能正常
|
|
||||||
- [ ] 生成Device代码功能正常
|
|
||||||
- [ ] 路径解析正确
|
|
||||||
- [ ] 无重复日志输出
|
|
||||||
|
|
||||||
### 打包环境测试
|
|
||||||
- [ ] 使用PyInstaller打包成exe
|
|
||||||
- [ ] exe启动无错误
|
|
||||||
- [ ] 所有代码生成功能正常
|
|
||||||
- [ ] assets目录正确定位
|
|
||||||
- [ ] 模板文件正确加载
|
|
||||||
- [ ] 配置文件读写正常
|
|
||||||
|
|
||||||
## 总结
|
|
||||||
|
|
||||||
经过完整检查和修复,现在的代码应该能够在开发环境和打包环境中都正常工作。主要改进包括:
|
|
||||||
|
|
||||||
1. **导入安全性**:所有静态方法都添加了本地导入
|
|
||||||
2. **路径处理**:优化了assets目录的查找逻辑
|
|
||||||
3. **性能优化**:添加了缓存机制,减少重复计算
|
|
||||||
4. **错误处理**:改进了错误提示和日志输出
|
|
||||||
5. **兼容性**:确保开发和打包环境的兼容性
|
|
||||||
|
|
||||||
建议在发布前进行完整的功能测试,特别是在打包后的exe环境中测试所有代码生成功能。
|
|
||||||
Loading…
Reference in New Issue
Block a user