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', '-O0', '-Wall', '-fdata-sections', '-ffunction-sections', '-Wno-unused-but-set-variable', '-Wno-unused-variable', '-Wno-unused-function', # '-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__', # 使用usb时打开这个宏 系统时钟会被设置为168MHz USB时钟为48MHz # 如果不打开这个宏 系统时钟会被设置为180MHz USB会通信异常 '-D__USB_USB__', ] 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/net_lwip_webserver/src', '-ISrc/TinyUSB/src/class/net', '-ISrc/TinyUSB/src/common', '-ISrc/TinyUSB/src/device', '-ISrc/TinyUSB/src/portable/synopsys/dwc2', '-ISrc/TinyUSB/src', '-ISrc/TinyUSB/src/osal', '-ISrc/TinyUSB/hw', '-ISrc/TinyUSB/lib/networking', '-ISrc/rt-thread/components/net/lwip-2.1.0/src/include', '-ISrc/rt-thread/components/net/lwip-2.1.0/src/arch/include' ] 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/class/net', 'Src/TinyUSB/src/common', 'Src/TinyUSB/src/device', 'Src/TinyUSB/src/portable/synopsys/dwc2', 'Src/TinyUSB/lib/networking', # 'Src/TinyUSB/src', 'Src/rt-thread/components/net/lwip-2.1.0/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/net_lwip_webserver/src/main.c', 'Src/TinyUSB/examples/device/net_lwip_webserver/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('\\','/'))