383 lines
9.0 KiB
Python
383 lines
9.0 KiB
Python
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"
|
||
os.environ["LANG"]="zh_CN.GBK"
|
||
|
||
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',
|
||
'-O3',
|
||
'-Wall',
|
||
'-fdata-sections',
|
||
'-ffunction-sections',
|
||
# '-u _printf_float', # 使用这个选项会导致调用exit
|
||
'-specs=nano.specs', # 使用nano-newlib
|
||
# debug
|
||
'-g -gdwarf-2'
|
||
]
|
||
|
||
DEF=[
|
||
'-DUSE_STDPERIPH_DRIVER',
|
||
'-DSTM32F429_439xx',
|
||
'-DARM_MATH_CM4',
|
||
'-D__FPU_PRESENT=1',
|
||
# '-D__GNUC__',
|
||
'-D__packed=__attribute__((__packed__))',
|
||
'-D__weak=__attribute__((weak))',
|
||
'-D__RTTHREAD__',
|
||
'-DSOC_SERIES_STM32F4',
|
||
'-DPKG_TINYUSB_DEVICE_ENABLE',
|
||
]
|
||
|
||
INC=[
|
||
'-ISrc/MJPEG',
|
||
'-ISrc/MJPEG/JPEG',
|
||
'-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',
|
||
'-ISrc/zlib',
|
||
'-ISrc/NES',
|
||
# '-ISrc/TinyUSB/lib/rt-thread',
|
||
'-ISrc/TinyUSB/examples/device/cdc_dual_ports/src',
|
||
'-ISrc/TinyUSB/src/class/cdc',
|
||
'-ISrc/TinyUSB/src/common',
|
||
'-ISrc/TinyUSB/src/device',
|
||
'-ISrc/TinyUSB/src/portable/synopsys/dwc2',
|
||
'-ISrc/TinyUSB/src',
|
||
'-ISrc/TinyUSB/src/osal',
|
||
'-ISrc/TinyUSB/hw',
|
||
]
|
||
|
||
SRC_DIR=[
|
||
'Src/MJPEG',
|
||
'Src/STM32/STM32F4xx_StdPeriph_Driver/src',
|
||
'Src/Drive/Source',
|
||
'Src/FreeType/src',
|
||
'Src/JPEG',
|
||
'Src/lpng1637',
|
||
# 'Src/lua-5.4.2/src',
|
||
'Src/MP3',
|
||
'Src/MyApp',
|
||
'Src/MyWin',
|
||
'Src/MyWinApp',
|
||
'Src/rt-thread/src',
|
||
# 'Src/sqlite3',
|
||
'Src/TinyUSB/src/class/cdc',
|
||
'Src/TinyUSB/src/common',
|
||
'Src/TinyUSB/src/device',
|
||
'Src/TinyUSB/src/portable/synopsys/dwc2',
|
||
# 'Src/TinyUSB/src',
|
||
]
|
||
|
||
SRC=[
|
||
# 'Src/MY/startup_stm32f429_439xx.s',
|
||
'Src/MY/startup_stm32f429xx.s',
|
||
'Src/STM32/CMSIS/Device/ST/STM32F4xx/Source/Templates/system_stm32f4xx.c',
|
||
"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',
|
||
"Src/lib/buff.c",
|
||
'Src/rt-thread/board.c',
|
||
# 'Src/rt-thread/core_delay.c',
|
||
'Src/rt-thread/ports/context_gcc.S',
|
||
'Src/rt-thread/ports/cpuport.c',
|
||
'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',
|
||
|
||
'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',
|
||
|
||
'Src/TinyUSB/src/tusb.c',
|
||
'Src/TinyUSB/lib/rt-thread/tusb_rt_thread_port.c',
|
||
'Src/TinyUSB/examples/device/cdc_dual_ports/src/usb_descriptors.c',
|
||
'Src/TinyUSB/hw/bsp/board.c'
|
||
]
|
||
|
||
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:
|
||
ret = subprocess.run(cmd.cmd, shell=True, timeout=60).returncode
|
||
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))
|
||
|
||
|
||
def run_cmd_queue(cmd_queue:Queue,cpu_num:int=8):
|
||
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)):
|
||
rsp=f"{' '.join(obj_list)} -o {dst} {flags} \
|
||
-T{LD_FILE} -lc -lm -lnosys -Wl,-Map={OUTPUT}/{TARGET}.map,--cref -Wl,--gc-sections \
|
||
-Wl,-print-memory-usage"
|
||
print(f"链接 {dst}")
|
||
with open(f"{OUTPUT}/{TARGET}.rsp",'w+') as f:
|
||
f.write(rsp.replace('\\','/'))
|
||
ret=os.system(f"{CC} @{OUTPUT}/{TARGET}.rsp")
|
||
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')
|
||
|
||
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
|
||
|
||
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")
|
||
os.system(f"{OBJDUMP} -D {OUTPUT}/{TARGET}.elf > {OUTPUT}/{TARGET}.lst")
|
||
|
||
|
||
|
||
if __name__ == "__main__":
|
||
tick_start=time.time()
|
||
main()
|
||
tick_end=time.time()
|
||
print(f"cost: {tick_end-tick_start}")
|
||
# for item in find_type('Src/zlib',".c"):
|
||
# print(f"'{item}',".replace('\\','/'))
|