1 Star 0 Fork 1

Mike_W / ProgramYield

forked from 刘子玄 / ProgramYield 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
cx.py 7.24 KB
一键复制 编辑 原始数据 按行查看 历史
刘子玄 提交于 2021-07-20 00:24 . 2021072000
#!/bin/python
# run helper of alone c++ file
# by clonne/2021
#
# * now support clang++ only
# * default use c++20
# * it's auto compile if file has update
# * it's auto analysis #include
# * it's auto find pre-compile-header and using
# * always compile if set optimize-level
#=
# before Usage:
# cp cx.py $PREFIX/bin/cx
# if no "exe" dir on current dir
# mkdir exe
# Usage:
# 1 run a.cc or a.cpp
# cx
# 2 run other file
# cx b
# 3 run use argument
# cx b.cc @ a b c
# 4 set optimize level
# cx .o3 b @ a b c
# 5 test compile options(show command only)
# cx .tco main
#
# Default Options(for clang++):
# -std=c++20
# -Wall -Werror -g
# -DDEBUG
# -O1
# -lreadline
#
import sys,os,subprocess
import re
from pathlib import Path
from functools import *
class const:
DIR_EXE = Path("./exe/")
CODE_INCLUDE_PATTERN = re.compile("[^#]*#include[^\"<]*(\"|\<)([^\">]+)")
TEST_COMPILE_OPTIONS = "//test-compile-options"
default_options = {
"compiler":"c++",
"target":"a",
"sources":["a"],
"std":"20",
"optimize":"unset",
"exflags":["-Wall","-Werror","-g","-ftemplate-backtrace-limit=2"],
"macros":["DEBUG"],
"libs":["readline"],
"source_suffixs":["",".cc",".cpp"],
"pch_suffixs":[".pch",".gch",".h.pch",".h.gch",".hpp.pch",".hpp.gch"],
"test-compile-options":False}
def AutoUsePCH(header_paths:[Path],pch_suffixs:[str]):
def CompileOptions(path_h:Path,path_pch:Path):
return ["-include",
path_h.relative_to('.'),
"-include-pch",
path_pch.relative_to('.')]
for path_h in header_paths:
for path_pch in [path_h.with_suffix(suffix) for suffix in pch_suffixs]:
if path_pch.is_file():
return CompileOptions(path_h,path_pch)
return []
def LinkerArgs(xs:{}):
def Optimize():
optimize = xs["optimize"]
if optimize == "unset":
optimize = "1"
if optimize > "0":
return ["-Xlinker",f"-O{optimize}"]
return []
def Libs():
args = []
for lib in xs["libs"]:
args.extend(["-Xlinker",f"-l{lib}"])
return args
return [*(Optimize()), *(Libs())]
# Ingnore #include <...>
def GetIncludePath(path_source:Path,include_target:str,symbol:str):
if symbol == '"':
for path_dir in [path_source.parent]:
path_include = Path(path_dir,include_target)
if path_include.is_file():
return path_include
return None
@cache
def Includes(path_source:Path):
headers = set()
with path_source.open("r") as f:
for line in f.readlines():
r = const.CODE_INCLUDE_PATTERN.match(line)
if r and len(r.groups()):
path_header = GetIncludePath(path_source,r.group(2),r.group(1))
if path_header:
headers.add(path_header)
return headers
def IncludesLevel1(source_paths:[Path]):
headers = set()
for path_source in source_paths:
headers |= Includes(path_source)
return headers
def IncludesHasUpdate(headers:set,time_target):
for path_header in headers:
if path_header.stat().st_mtime > time_target:
return True
next_headers = IncludesLevel1(headers)
if len(next_headers):
return IncludesHasUpdate(next_headers,time_target)
return False
# Order of Decide Target Path
# 1 <user-argument>
# 2 <user-argument>.exe
# 3 exe/<user-argument>.exe
# 4 exe/<user-argument>
# use 4 if no matched
def GetTargetPath(xs:{}):
path_user = xs["target"]
matchs = [path_user]
if len(path_user.suffix) < 1:
matchs.append(path_user.with_suffix(".exe"))
matchs.append(Path(const.DIR_EXE,matchs[-1]))
matchs.append(Path(const.DIR_EXE,path_user))
for p in matchs:
if p.is_file(): return p
return matchs[-1]
def Make(xs:{}):
path_target = GetTargetPath(xs)
target_mtime = path_target.stat().st_mtime if path_target.is_file() else 0
source_paths = xs["sources"]
headers_level1 = IncludesLevel1(source_paths)
def CompileOptions():
compiler = xs["compiler"]
sources = ' '.join(map(str,source_paths))
std = xs["std"]
pch = xs.get("pch", AutoUsePCH(headers_level1,xs["pch_suffixs"]))
exflags = xs["exflags"]
macros = xs["macros"]
linker_args = LinkerArgs(xs)
return [compiler,
f"-o{path_target}",
f"-std=c++{std}",
*([f"-D{m}" for m in macros]),
*exflags,
*pch,
*linker_args,
sources]
def Update():
subprocess.run(CompileOptions(),check=True)
return path_target
if xs["test-compile-options"]:
print(CompileOptions())
return const.TEST_COMPILE_OPTIONS
# Trigger Update if set optimize-level
if xs["optimize"] != "unset":
return Update()
# Trigger Update if source has modify
for path_source in source_paths:
if path_source.stat().st_mtime > target_mtime:
return Update()
# Teigger Update if headers has modify
if IncludesHasUpdate(headers_level1,target_mtime):
return Update()
return path_target
def Call(target:Path,args:[str]):
if target != const.TEST_COMPILE_OPTIONS:
try:
subprocess.run([target.resolve(),*args],check=True)
except subprocess.CalledProcessError: pass
def BeforeMake(xs:{}):
def Target(target:str):
path = Path(xs["sources"][0].stem)
return path
def Sources(sources:[]):
paths = sources.copy()
suffixs = xs["source_suffixs"]
def FindBySuffix(i:int):
for suffix in suffixs:
p = Path(paths[i]).with_suffix(suffix)
if p.is_file():
paths[i] = p
return True
return False
def FindByGlob(i:int):
for g in Path(".").glob(f"{paths[i]}*.cc"):
paths[i] = g
return True
return False
for i in range(len(paths)):
if not (FindBySuffix(i) or FindByGlob(i)):
raise RuntimeError(f"Source \"{paths[i]}\" Not Found.")
return paths
def Fix(name:str,f:callable):
xs[name] = f(xs[name])
Fix("sources",Sources)
Fix("target",Target)
return xs
def DoOptions(args:[]):
makexs = {**default_options}
sources = []
target_args = []
in_target_args_mode = False
def Argument(a:str):
nonlocal in_target_args_mode
if in_target_args_mode:
target_args.append(x)
elif "@" == a:
in_target_args_mode = True
elif ".o" == a or ".o2" == a:
makexs["optimize"] = "2"
elif ".o3" == a:
makexs["optimize"] = "3"
elif ".tco" == a:
makexs["test-compile-options"] = True
else:
sources.append(a)
if len(args):
for x in args:
Argument(x)
if len(sources):
makexs["sources"] = sources
return lambda:Call(Make(BeforeMake(makexs)),target_args)
def Main(args:[]):
DoOptions(args)()
try:
Main(sys.argv[1:])
except subprocess.SubprocessError as e:
print(e)
except RuntimeError as e:
print("Error:",e)
except:
print(sys.exc_info()[0])
raise
Python
1
https://gitee.com/Mike_W/ProgramYield.git
git@gitee.com:Mike_W/ProgramYield.git
Mike_W
ProgramYield
ProgramYield
main

搜索帮助