# -*- coding: utf-8 -*-




import os
import sys
import time
import dataclasses
from multiprocessing import  Process,Queue
from create_lambda_fun import search_lambda
from create_signal_fun import moc_file_create


'''

修改编译脚本之后需要全编译一次

'''


CC = 'gcc'

CSRC = ["main.c"]

CINC = ['-Isoft',"-Iriscv_cpu", "-I./","-Itest"]

CDEF = ["-DTEST","-DLINUX"]

ASRC = []

BUILD_DIR = 'build'

TARGET = 'hello'

# CFLAG = ["-Wall -pedantic -specs=nano.specs -mcpu=cortex-m3 -mthumb -lc -lm -lnosys -Og -Tstm32_boot.ld",
#          f"-Wl,-Map={BUILD_DIR}/{TARGET}.map,--cref -Wl,--gc-sections"]
# -pedantic 这一项是ISO语法检验
CFLAG = ["-Wall -g","-pthread"]


# 找到指定后缀的文件
def find_type(path:str,fix:list):
  dlist=os.listdir(path)
  file_list=[]
  for i in dlist:
    ps=os.path.join(path, i)
    if os.path.isdir(ps):
      file_list+=find_type(ps,fix)
    else:
      suf=ps.split('.')[-1]
      if(suf in fix):
        file_list.append(ps)
  return file_list



'''
将.c编译为.o文件，
编译脚本需要实现 如果.c文件有修改 则重新编译对应文件的.o文件
gcc -c test.c -o test.o

输出依赖关系到.d文件
gcc -MM main.c -o build/main.d





'''


def tran_path(path:str):
  path=os.path.normpath(os.path.join(BUILD_DIR,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):
  while(not cmd_queue.empty()):
    try:
      cmd=cmd_queue.get_nowait()
    except Exception:
      return_list.put((cpu_index,True))
      return
    print(f"[{cpu_index}] {cmd.info}")
    ret=os.system(cmd.cmd)
    if(ret):
      return_list.put((cpu_index,False))
      return
  return_list.put((cpu_index,True))


def run_cmd_queue(cmd_queue:Queue,cpu_num:int=4):
  if(cmd_queue.empty()):
    return
  process_list = []
  return_list=Queue()
  for i in range(cpu_num):
    p = Process(target=run_cmd,args=(cmd_queue,i,return_list,))
    p.start()
    process_list.append(p)
  for i in process_list:
    p.join()
  while not return_list.empty():
    i=return_list.get()
    if(not i[1]):
      print(f"子进程 [{i[0]}] 运行失败")
      exit(-1)


# 保证目标都存在
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(CINC)} {' '.join(CDEF)} {' '.join(CFLAG)}"
  for i in src:
    name=os.path.splitext(tran_path(i))[0]
    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)
  run_cmd_queue(CmdQueue)
  check_exists(dst_list)


# 生成中间文件
def build_object(src:list):
  CmdQueue=Queue()
  dst_list=[]
  flags=f"{' '.join(CINC)} {' '.join(CDEF)} {' '.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'])
    if(file_type in ['.c','.C']):
      if(check_rebuild(dst,read_depend_files(cd))):
        cmd=f"{CC} -c {i} -o {dst} {flags}"
        CmdQueue.put(cmd_item_t(cmd,f"编译 {dst}"))
        dst_list.append(dst)
  run_cmd_queue(CmdQueue)
  check_exists(dst_list)


# 生成可执行文件
def build_target(src:list):
  flags=f"{' '.join(CINC)} {' '.join(CDEF)} {' '.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(BUILD_DIR,TARGET)
  if(check_rebuild(dst,obj_list)):
    cmd=f"{CC} {' '.join(obj_list)} -o {dst} {flags}"
    print(f"链接 {dst}")
    ret=os.system(cmd)
    if(ret):
      exit()


def main():
  global CSRC
  global ASRC
  if(not os.path.exists(BUILD_DIR)):
    os.makedirs(BUILD_DIR)
  # 在命令行中传入的源文件
  CSRC+=sys.argv[1:]
  CSRC+=find_type('soft',['c','C'])
  CSRC+=find_type('riscv_cpu',['c','C'])
  CSRC=search_lambda(CSRC)
  moc_file_create(f"{BUILD_DIR}/moc_tmp.c",list(item[2:] for item in CINC))
  CSRC.append(f"{BUILD_DIR}/moc_tmp.c")


  build_depend(CSRC)
  build_object(CSRC)
  build_target(CSRC+ASRC)

if __name__ == "__main__":
  tick_start=time.time()
  main()
  tick_end=time.time()
  print(f"cost: {tick_end-tick_start}")



