2025-06-28 18:16:25 +08:00
|
|
|
|
import os
|
|
|
|
|
import sys
|
|
|
|
|
import time
|
|
|
|
|
import shutil
|
|
|
|
|
import dataclasses
|
|
|
|
|
import subprocess
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
from multiprocessing import Process,Queue,Value,cpu_count
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
os.environ["PATH"]+=";D:/Program Files/arm-gnu-toolchain/bin"
|
2025-07-06 15:22:15 +08:00
|
|
|
|
os.environ["LANG"]="zh_CN.GBK"
|
2025-06-28 18:16:25 +08:00
|
|
|
|
|
|
|
|
|
CC="arm-none-eabi-gcc"
|
|
|
|
|
AS = CC + ' -x assembler-with-cpp'
|
|
|
|
|
OBJCPY="arm-none-eabi-objcopy"
|
|
|
|
|
OBJDUMP="arm-none-eabi-objdump"
|
|
|
|
|
SIZE="arm-none-eabi-size"
|
|
|
|
|
|
|
|
|
|
CFLAG=[
|
|
|
|
|
'-mcpu=cortex-m4',
|
|
|
|
|
'-mthumb',
|
|
|
|
|
'-mfpu=fpv4-sp-d16',
|
|
|
|
|
'-mfloat-abi=hard',
|
2025-07-10 00:08:56 +08:00
|
|
|
|
'-O3',
|
2025-07-05 19:05:35 +08:00
|
|
|
|
'-Wall',
|
|
|
|
|
'-fdata-sections',
|
2025-06-28 18:16:25 +08:00
|
|
|
|
'-ffunction-sections',
|
2025-07-06 15:22:15 +08:00
|
|
|
|
# '-u _printf_float', # 使用这个选项会导致调用exit
|
|
|
|
|
'-specs=nano.specs', # 使用nano-newlib
|
2025-06-28 18:16:25 +08:00
|
|
|
|
# debug
|
2025-06-29 11:20:46 +08:00
|
|
|
|
'-g -gdwarf-2'
|
2025-06-28 18:16:25 +08:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
DEF=[
|
|
|
|
|
'-DUSE_STDPERIPH_DRIVER',
|
|
|
|
|
'-DSTM32F429_439xx',
|
|
|
|
|
'-DARM_MATH_CM4',
|
|
|
|
|
'-D__FPU_PRESENT=1',
|
|
|
|
|
# '-D__GNUC__',
|
|
|
|
|
'-D__packed=__attribute__((__packed__))',
|
|
|
|
|
'-D__weak=__attribute__((weak))'
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
INC=[
|
2025-06-28 22:15:49 +08:00
|
|
|
|
'-ISrc/MJPEG',
|
|
|
|
|
'-ISrc/MJPEG/JPEG',
|
2025-06-28 18:16:25 +08:00
|
|
|
|
'-ISrc/STM32/CMSIS/Device/ST/STM32F4xx/Include',
|
|
|
|
|
'-ISrc/STM32/CMSIS/Include',
|
|
|
|
|
'-ISrc/STM32/DSP/Include',
|
|
|
|
|
'-ISrc/STM32/STM32F4xx_StdPeriph_Driver/inc',
|
|
|
|
|
'-ISrc/Drive/Include',
|
|
|
|
|
'-ISrc/FATS',
|
|
|
|
|
'-ISrc/FreeType',
|
|
|
|
|
# '-ISrc/FreeType/devel',
|
|
|
|
|
'-ISrc/FreeType/include',
|
|
|
|
|
'-ISrc/JPEG',
|
|
|
|
|
'-ISrc/lib',
|
|
|
|
|
'-ISrc/lpng1637',
|
|
|
|
|
'-ISrc/lua-5.4.2',
|
|
|
|
|
'-ISrc/lua-5.4.2/src',
|
|
|
|
|
'-ISrc/MP3',
|
|
|
|
|
'-ISrc/MP3/helix',
|
|
|
|
|
'-ISrc/MY',
|
|
|
|
|
'-ISrc/MyApp',
|
|
|
|
|
'-ISrc/MyWin',
|
|
|
|
|
'-ISrc/MyWin/MyWinCore',
|
|
|
|
|
'-ISrc/MyWin/Window',
|
|
|
|
|
'-ISrc/MyWinApp',
|
|
|
|
|
'-ISrc/rt-thread',
|
|
|
|
|
'-ISrc/rt-thread/include',
|
|
|
|
|
# '-ISrc/sqlite3',
|
2025-07-08 19:13:35 +08:00
|
|
|
|
'-ISrc/zlib',
|
|
|
|
|
'-ISrc/NES'
|
2025-06-28 18:16:25 +08:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
SRC_DIR=[
|
2025-06-28 22:15:49 +08:00
|
|
|
|
'Src/MJPEG',
|
2025-06-28 18:16:25 +08:00
|
|
|
|
'Src/STM32/STM32F4xx_StdPeriph_Driver/src',
|
|
|
|
|
'Src/Drive/Source',
|
|
|
|
|
'Src/FreeType/src',
|
|
|
|
|
'Src/JPEG',
|
|
|
|
|
'Src/lpng1637',
|
2025-06-28 22:15:49 +08:00
|
|
|
|
# 'Src/lua-5.4.2/src',
|
2025-06-28 18:16:25 +08:00
|
|
|
|
'Src/MP3',
|
|
|
|
|
'Src/MyApp',
|
|
|
|
|
'Src/MyWin',
|
|
|
|
|
'Src/MyWinApp',
|
|
|
|
|
'Src/rt-thread/src',
|
|
|
|
|
# 'Src/sqlite3',
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
SRC=[
|
2025-06-29 11:20:46 +08:00
|
|
|
|
# 'Src/MY/startup_stm32f429_439xx.s',
|
|
|
|
|
'Src/MY/startup_stm32f429xx.s',
|
2025-06-28 22:15:49 +08:00
|
|
|
|
'Src/STM32/CMSIS/Device/ST/STM32F4xx/Source/Templates/system_stm32f4xx.c',
|
2025-06-28 18:16:25 +08:00
|
|
|
|
"Src/MY/bsp_init.c",
|
|
|
|
|
"Src/MY/main_my.c",
|
|
|
|
|
"Src/MY/stm32f4xx_it.c",
|
|
|
|
|
"Src/MY/sys_api.c",
|
|
|
|
|
"Src/MY/test.c",
|
|
|
|
|
'Src/FATS/diskio.c',
|
|
|
|
|
'Src/FATS/ff.c',
|
|
|
|
|
'Src/FATS/ffsystem.c',
|
|
|
|
|
'Src/FATS/mycc936.c',
|
2025-06-28 22:15:49 +08:00
|
|
|
|
"Src/lib/buff.c",
|
2025-06-28 18:16:25 +08:00
|
|
|
|
'Src/rt-thread/board.c',
|
|
|
|
|
# 'Src/rt-thread/core_delay.c',
|
2025-06-29 11:53:35 +08:00
|
|
|
|
'Src/rt-thread/ports/context_gcc.S',
|
2025-06-28 18:16:25 +08:00
|
|
|
|
'Src/rt-thread/ports/cpuport.c',
|
2025-06-28 22:15:49 +08:00
|
|
|
|
'Src/FreeType/ftdebug.c',
|
|
|
|
|
'Src/FreeType/ftfile.c',
|
|
|
|
|
|
|
|
|
|
'Src/zlib/adler32.c',
|
|
|
|
|
'Src/zlib/compress.c',
|
|
|
|
|
'Src/zlib/crc32.c',
|
|
|
|
|
'Src/zlib/deflate.c',
|
|
|
|
|
# 'Src/zlib/gzclose.c',
|
|
|
|
|
# 'Src/zlib/gzlib.c',
|
|
|
|
|
# 'Src/zlib/gzread.c',
|
|
|
|
|
# 'Src/zlib/gzwrite.c',
|
|
|
|
|
'Src/zlib/infback.c',
|
|
|
|
|
'Src/zlib/inffast.c',
|
|
|
|
|
'Src/zlib/inflate.c',
|
|
|
|
|
'Src/zlib/inftrees.c',
|
|
|
|
|
'Src/zlib/trees.c',
|
|
|
|
|
'Src/zlib/uncompr.c',
|
|
|
|
|
'Src/zlib/zutil.c',
|
|
|
|
|
'Src/MP3/helix/arm/asmmisc_gcc.s',
|
|
|
|
|
'Src/MP3/helix/arm/asmpoly_thumb2_gcc.s',
|
|
|
|
|
'Src/MY/syscalls.c',
|
2025-07-08 19:13:35 +08:00
|
|
|
|
|
|
|
|
|
'Src/NES/6502_gcc.S',
|
|
|
|
|
'Src/NES/6502cart_gcc.S',
|
|
|
|
|
'Src/NES/nes_apu.c',
|
|
|
|
|
'Src/NES/nes_main.c',
|
|
|
|
|
'Src/NES/nes_ppu.c',
|
|
|
|
|
'Src/NES/nes_mapper.c',
|
2025-06-28 18:16:25 +08:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
LD_FILE="stm32f429ighx_flash.ld"
|
|
|
|
|
|
|
|
|
|
TARGET="stm32"
|
|
|
|
|
|
|
|
|
|
OUTPUT="output"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 找到目录下的所有指定类型的文件
|
|
|
|
|
def find_type(path: str, fix: str):
|
|
|
|
|
root = Path(path)
|
|
|
|
|
file_list=[]
|
|
|
|
|
for file in root.rglob(f"*{fix}"):
|
|
|
|
|
if file.is_file():
|
|
|
|
|
file_list.append(f"{file}")
|
|
|
|
|
return file_list
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def tran_path(path:str):
|
|
|
|
|
path=os.path.normpath(os.path.join(OUTPUT,path))
|
|
|
|
|
base_path=os.path.dirname(path)
|
|
|
|
|
if not os.path.exists(base_path):
|
|
|
|
|
os.makedirs(base_path)
|
|
|
|
|
return path
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 判断是否需要重新生成
|
|
|
|
|
def check_rebuild(dst:str,src:list):
|
|
|
|
|
if(not os.path.exists(dst)):
|
|
|
|
|
return True
|
|
|
|
|
dst_time=os.path.getmtime(dst)
|
|
|
|
|
src_time=[]
|
|
|
|
|
for i in src:
|
|
|
|
|
src_time.append(os.path.getmtime(i))
|
|
|
|
|
src_time.sort()
|
|
|
|
|
if(src_time[-1]>dst_time):
|
|
|
|
|
return True
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 读取.d文件,返回依赖文件列表
|
|
|
|
|
def read_depend_files(name:str):
|
|
|
|
|
with open(name) as f:
|
|
|
|
|
lines=f.readlines()
|
|
|
|
|
t=''
|
|
|
|
|
for line in lines:
|
|
|
|
|
line=line.strip()
|
|
|
|
|
if(line[-1]=='\\'):
|
|
|
|
|
t+=line[:-1]
|
|
|
|
|
else:
|
|
|
|
|
t+=line
|
|
|
|
|
t=t.split(':')[-1].strip()
|
|
|
|
|
t=t.split(' ')
|
|
|
|
|
return t
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclasses.dataclass
|
|
|
|
|
class cmd_item_t:
|
|
|
|
|
cmd:str
|
|
|
|
|
info:str
|
|
|
|
|
|
|
|
|
|
def run_cmd(cmd_queue:Queue,cpu_index:int,return_list:Queue,failed_num):
|
|
|
|
|
ack=True
|
|
|
|
|
while(not cmd_queue.empty()):
|
|
|
|
|
if(failed_num.value>0):
|
|
|
|
|
break
|
|
|
|
|
try:
|
|
|
|
|
cmd=cmd_queue.get_nowait()
|
|
|
|
|
except Exception:
|
|
|
|
|
break
|
|
|
|
|
print(f"[{cpu_index}] {cmd.info}")
|
|
|
|
|
# ret=os.system(cmd.cmd)
|
|
|
|
|
try:
|
2025-06-28 22:15:49 +08:00
|
|
|
|
ret = subprocess.run(cmd.cmd, shell=True, timeout=60).returncode
|
2025-06-28 18:16:25 +08:00
|
|
|
|
except subprocess.TimeoutExpired:
|
|
|
|
|
print("命令执行超时!")
|
|
|
|
|
failed_num.value += 1
|
|
|
|
|
ret = -1
|
|
|
|
|
if(ret!=0):
|
|
|
|
|
print(f"[{cpu_index}] ret={ret}")
|
|
|
|
|
ack=False
|
|
|
|
|
failed_num.value+=1
|
|
|
|
|
break
|
|
|
|
|
return_list.put((cpu_index,ack))
|
|
|
|
|
|
|
|
|
|
|
2025-06-28 22:15:49 +08:00
|
|
|
|
def run_cmd_queue(cmd_queue:Queue,cpu_num:int=8):
|
2025-06-28 18:16:25 +08:00
|
|
|
|
if(cmd_queue.empty()):
|
|
|
|
|
return True
|
|
|
|
|
process_list = []
|
|
|
|
|
return_list=Queue()
|
|
|
|
|
failed_num=Value('i',0)
|
|
|
|
|
for i in range(cpu_num):
|
|
|
|
|
p = Process(target=run_cmd,args=(cmd_queue,i,return_list,failed_num,))
|
|
|
|
|
p.daemon=True
|
|
|
|
|
p.start()
|
|
|
|
|
process_list.append(p)
|
|
|
|
|
for i in process_list:
|
|
|
|
|
i.join()
|
|
|
|
|
ret=True
|
|
|
|
|
while not return_list.empty():
|
|
|
|
|
i=return_list.get()
|
|
|
|
|
if(not i[1]):
|
|
|
|
|
ret=False
|
|
|
|
|
print(f"子进程 [{i[0]}] 运行失败")
|
|
|
|
|
return_list.cancel_join_thread()
|
|
|
|
|
# 消耗掉所有数据防止进程无法退出
|
|
|
|
|
while not cmd_queue.empty():
|
|
|
|
|
cmd_queue.get()
|
|
|
|
|
cmd_queue.cancel_join_thread()
|
|
|
|
|
return ret
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 保证目标都存在
|
|
|
|
|
def check_exists(src:list):
|
|
|
|
|
for item in src:
|
|
|
|
|
for i in range(10):
|
|
|
|
|
if(os.path.exists(item)):
|
|
|
|
|
break
|
|
|
|
|
time.sleep(0.1)
|
|
|
|
|
# 生成依赖关系
|
|
|
|
|
def build_depend(src:list):
|
|
|
|
|
CmdQueue=Queue()
|
|
|
|
|
dst_list=[]
|
|
|
|
|
flags=f"{' '.join(INC)} {' '.join(DEF)} {' '.join(CFLAG)}"
|
|
|
|
|
for i in src:
|
|
|
|
|
name_t=os.path.splitext(tran_path(i))
|
|
|
|
|
name=name_t[0]
|
|
|
|
|
file_type=name_t[-1]
|
|
|
|
|
if(not file_type in ['.c','.C','.cpp']):
|
|
|
|
|
continue
|
|
|
|
|
dst='.'.join([name,'d'])
|
|
|
|
|
if(check_rebuild(dst,[i])):
|
|
|
|
|
cmd=f"{CC} -MM {i} -o {dst} {flags}"
|
|
|
|
|
CmdQueue.put(cmd_item_t(cmd,f"更新 {dst}"))
|
|
|
|
|
dst_list.append(dst)
|
|
|
|
|
return run_cmd_queue(CmdQueue)
|
|
|
|
|
|
|
|
|
|
# 生成中间文件
|
|
|
|
|
def build_object(src:list):
|
|
|
|
|
CmdQueue=Queue()
|
|
|
|
|
dst_list=[]
|
|
|
|
|
flags=f"{' '.join(INC)} {' '.join(DEF)} {' '.join(CFLAG)}"
|
|
|
|
|
for i in src:
|
|
|
|
|
name_t=os.path.splitext(tran_path(i))
|
|
|
|
|
name=name_t[0]
|
|
|
|
|
file_type=name_t[-1]
|
|
|
|
|
dst='.'.join([name,'o'])
|
|
|
|
|
cd='.'.join([name,'d'])
|
|
|
|
|
cmd = ''
|
|
|
|
|
if(file_type in ['.c','.C']):
|
|
|
|
|
if(check_rebuild(dst,read_depend_files(cd))):
|
|
|
|
|
cmd=f"{CC} -c {i} -o {dst} {flags}"
|
|
|
|
|
elif(file_type in ['.s','.S','.asm','.ASM']):
|
|
|
|
|
if(check_rebuild(dst,[i])):
|
|
|
|
|
cmd=f"{AS} -c {i} -o {dst} {flags}"
|
|
|
|
|
if(len(cmd)>0):
|
|
|
|
|
CmdQueue.put(cmd_item_t(cmd,f"编译 {dst}"))
|
|
|
|
|
dst_list.append(dst)
|
|
|
|
|
return run_cmd_queue(CmdQueue)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 生成可执行文件
|
|
|
|
|
def build_target(src:list):
|
|
|
|
|
flags=f"{' '.join(INC)} {' '.join(DEF)} {' '.join(CFLAG)}"
|
|
|
|
|
obj_list=[]
|
|
|
|
|
for i in src:
|
|
|
|
|
name=os.path.splitext(tran_path(i))[0]
|
|
|
|
|
obj_list.append('.'.join([name,'o']))
|
|
|
|
|
dst=os.path.join(OUTPUT,TARGET)+".elf"
|
|
|
|
|
if(check_rebuild(dst,obj_list)):
|
2025-07-06 15:22:15 +08:00
|
|
|
|
rsp=f"{' '.join(obj_list)} -o {dst} {flags} \
|
2025-06-28 18:16:25 +08:00
|
|
|
|
-T{LD_FILE} -lc -lm -lnosys -Wl,-Map={OUTPUT}/{TARGET}.map,--cref -Wl,--gc-sections \
|
2025-07-08 19:13:35 +08:00
|
|
|
|
-Wl,-print-memory-usage"
|
2025-06-28 18:16:25 +08:00
|
|
|
|
print(f"链接 {dst}")
|
2025-06-28 22:15:49 +08:00
|
|
|
|
with open(f"{OUTPUT}/{TARGET}.rsp",'w+') as f:
|
|
|
|
|
f.write(rsp.replace('\\','/'))
|
|
|
|
|
ret=os.system(f"{CC} @{OUTPUT}/{TARGET}.rsp")
|
2025-06-28 18:16:25 +08:00
|
|
|
|
return ret==0
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
|
global SRC
|
|
|
|
|
|
|
|
|
|
if not os.path.exists(OUTPUT):
|
|
|
|
|
os.mkdir(OUTPUT)
|
|
|
|
|
|
|
|
|
|
for item in SRC_DIR:
|
|
|
|
|
SRC += find_type(item,'.c')
|
|
|
|
|
|
2025-07-05 23:48:54 +08:00
|
|
|
|
if len(sys.argv) > 1:
|
|
|
|
|
l=[]
|
|
|
|
|
if sys.argv[1] == 'show_inc':
|
|
|
|
|
l=INC
|
|
|
|
|
elif sys.argv[1] == 'show_src':
|
|
|
|
|
l=SRC
|
|
|
|
|
l.sort()
|
|
|
|
|
for item in l:
|
|
|
|
|
t=item.replace('\\','/')
|
|
|
|
|
print(f"\"Project/{t}\",")
|
|
|
|
|
return
|
|
|
|
|
|
2025-06-28 18:16:25 +08:00
|
|
|
|
if build_depend(SRC):
|
|
|
|
|
if build_object(SRC):
|
|
|
|
|
if build_target(SRC):
|
|
|
|
|
os.system(f"{OBJCPY} -O binary -S {OUTPUT}/{TARGET}.elf {OUTPUT}/{TARGET}.bin")
|
|
|
|
|
os.system(f"{OBJCPY} -O ihex {OUTPUT}/{TARGET}.elf {OUTPUT}/{TARGET}.hex")
|
2025-07-10 00:08:56 +08:00
|
|
|
|
os.system(f"{OBJDUMP} -D {OUTPUT}/{TARGET}.elf > {OUTPUT}/{TARGET}.lst")
|
2025-06-28 18:16:25 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
tick_start=time.time()
|
|
|
|
|
main()
|
|
|
|
|
tick_end=time.time()
|
|
|
|
|
print(f"cost: {tick_end-tick_start}")
|
2025-06-28 22:15:49 +08:00
|
|
|
|
# for item in find_type('Src/zlib',".c"):
|
|
|
|
|
# print(f"'{item}',".replace('\\','/'))
|