#!/usr/bin/python3 import os,sys,math import subprocess ##flist - list all files in a given directory pth ##optional arguments: # recurse - (T/F): Whether to recursively search for files in directory tree # exts - (list): A list of file extensions to filter on # normpath (T/F): whether to normalize path variables after #filelist = flist(pth,**kwargs): def flist(pth,**kwargs): flst = [] if(not('recurse' in kwargs)): recurse_ = False else: recurse_ = kwargs['recurse'] if(not('exts' in kwargs)): filterexts_ = False else: filterexts_ = True exts = kwargs['exts'] if(not('normpath' in kwargs)): normpath_ = True else: normpath_ = kwargs['normpath'] if(not('linuxpath' in kwargs)): linuxpath_ = False else: linuxpath_ = kwargs['linuxpath'] if(not('followlinks' in kwargs)): followlinks_ = False else: followlinks_ = kwargs['followlinks'] dirlist = [] rawlist = os.listdir(pth) for F in rawlist: F2 = os.path.join(pth,F) if(os.path.isdir(F2)): b = (followlinks_) or ((not followlinks_) and not(os.path.islink(F2))) if(b): if((F2!=".")&(F2!="..")): dirlist.append(F2) elif(os.path.isfile(F2)): flst.append(F2) #Recurse through directories if(recurse_): for D in dirlist: lst = flist(D,**kwargs) for L in lst: flst.append(L) #Postprocess: #Filter out all extensions except the selected ext list if(filterexts_): flst = filterexts(flst,exts) #Normalize filename path according to os if(normpath_): flst2 = list(flst) for I in range(0,len(flst2)): flst[I] = os.path.normpath(flst2[I]) #If linuxpath, convert all \\ to / #if(linuxpath_): # flst2 = list(flst) # for I in range(0,len(flst2)): # flst[I] = linuxpath(flst2[I]) return flst #Filters by extensions in a list of files #flst = def filterexts(flst,exts): def filterexts(flst,exts): flst2 = [] if(isinstance(exts,str)): exts = list([exts]) for F in flst: b = False for ext in exts: if(ext[0]!='.'): ext = '.'+ext F2 = os.path.splitext(F) if(len(F2)>=2): ex = F2[1] if(len(ex)>0): if(ex[0]!='.'): ex = '.'+ex if(ex==ext): b = True if(b): flst2.append(F) return flst2 #Find a file fname, starting in pth and recursing #Used for finding library files to link def findfile(fname,pth,**kwargs): fullfname = "" flst = flist(pth,recurse=True) for F in flst: F2 = os.path.split(F)[1] if(F2 == fname): fullfname = F return fullfname def replaceext(fname,ext): fname2 = "" if(len(ext)>0): if(ext[0]!='.'): ext = '.'+ext fname2 = os.path.splitext(fname)[0]+ext else: fname2 = os.path.splitext(fname)[0] return fname2 def replaceexts(fnamelist,ext): fname2list = [] for F in fnamelist: F2 = replaceext(F,ext) fname2list.append(F2) return fname2list #filenames must match def except_contains(lst1,exc): lst2 = [] for item in lst1: b = 1 for item2 in exc: fsplit = os.path.split(item) fn = fsplit[len(fsplit)-1] if(fn==item2): b = 0 break if(b==1): lst2.append(item) return lst2 ########################## ##System Call Procedures## ########################## def callproc(cmd, **kwargs): if(not('logfile' in kwargs)): use_lf = False else: logfile = kwargs['logfile'] if(logfile!=""): fp = open(kwargs['logfile'],'a+') use_lf = True else: use_lf = False if(not('echo' in kwargs)): echo = True else: echo = kwargs['echo'] if(echo): print(cmd) #encoding/deconding to/from bytes is necessary to use the subprocess command #in python3.7 #However, only do this in linux if(sys.platform!='win32'): cmd2 = cmd.encode(encoding='utf-8') else: cmd2 = cmd proc = subprocess.Popen(cmd2,stderr = subprocess.STDOUT, stdout=subprocess.PIPE, shell=True) (out, err) = proc.communicate() out = out.decode(encoding='utf-8') if(echo): print(out) #print(err); if(use_lf): fp.writelines(cmd+'\n') fp.writelines(out+'\n') if(use_lf): fp.close() #List to space-seperated-string def list_to_sss(lst): lout = "" for I in range(0,len(lst)-1): lout = lout + lst[I] + " " if(len(lst)>0): lout = lout + lst[len(lst)-1] return lout ##################################### ## Incremental Compilation Library ## ##################################### #silently read lines from a text file if exists def readtextlines(fname): txtlns = [] if(not os.path.isfile(fname)): return txtlns try: fp = open(fname,"r") except: return txtlns ln = " " while(ln!=""): ln = fp.readline() txtlns.append(ln) fp.close() return txtlns def getincludefnfrage(includeline): fnfrag = "" I1 = -1 I2 = -1 for I in range(0,len(includeline)): if(I1<0 and (includeline[I]=='<' or includeline[I]=='"')): I1 = I if(I1>=0 and (includeline[I]=='>' or includeline[I]=='"')): I2 = I break if(I1>=0 and I2>=0): fnfrag = includeline[I1+1:I2] return fnfrag #Returns the name of the source file fname (if it exists) #and all included filenames def getsrcandincludes(fname, incdirs): flist = [] if(os.path.isfile(fname)): flist.append(fname) Ilist = 0 while(Ilist=0): fnfrag = getincludefnfrage(lns[J]) for K in range(0,len(incdirs)): tfn = os.path.join(incdirs[K],fnfrag) if(os.path.isfile(tfn)): flist.append(tfn) break Ilist = Ilist + 1 return flist #Returns the name of the object file associated with the source file #within the object store folder (if it exists) def getobjfile(fname,objstore,objext = ".o"): fret = "" f1 = os.path.split(fname)[1] f2 = f1 while(os.path.splitext(f2)[1]!=""): f2 = os.path.splitext(f2)[0] objext = objext.strip('.') f3 = os.path.join(objstore,"{}.{}".format(f2,objext)) if(os.path.exists(f3)): fret = f3 return fret def getsrctimes(fname, incdirs): ftimes = [] flst = getsrcandincludes(fname, incdirs) for I in range(0,len(flst)): f = flst[I] mt = os.path.getmtime(f) ftimes.append(mt) return ftimes def getobjtime(fname,objstore,objext=".o"): ret = -1 fret = getobjfile(fname,objstore,objext) if(fret!=""): ret = os.path.getmtime(fret) return ret #Decide whether or not to compile source file def decidecompile(fname,**kwargs): ret = True if(not os.path.isfile(fname)): ret = False return ret ##unpack kwargs if("searchincdirs" in kwargs): incdirs = kwargs["searchincdirs"] else: incdirs = ["./include"] if("objext" in kwargs): objext = kwargs["objext"] else: objext = ".o" if("objstore" in kwargs): objstore = kwargs["objstore"] else: objstore = "./objstore" srclist = getsrcandincludes(fname,incdirs) srctlist = getsrctimes(fname,incdirs) obj = getobjfile(fname,objstore,objext) objt = getobjtime(fname,objstore,objext) if(obj!=""): ret = False for I in range(0,len(srctlist)): if(srctlist[I]>objt): ret = True break return ret def gs_incremental_compile(compiler,srcfile,**kwargs): if(not('include' in kwargs)): include = '' else: include = kwargs['include'] if(isinstance(include,list)): include = list_to_sss(include) if(not('flags' in kwargs)): flags = '' else: flags = kwargs['flags'] if(isinstance(flags,list)): flags = list_to_sss(flags) if(not('objext' in kwargs)): objext = '.o' else: objext = kwargs['objext'] if(not('srcfileflag' in kwargs)): srcfileflag = '-c' else: srcfileflag = kwargs['srcfileflag'] if(not('outfileflag' in kwargs)): outfileflag = '-o' else: outfileflag = kwargs['outfileflag'] if(not('logfile' in kwargs)): logfile = "" else: logfile = kwargs['logfile'] if(not('smartcompile' in kwargs)): _smartcompile = True else: _smartcompile = kwargs['smartcompile'] #incrementalcompile if("searchincdirs" in kwargs): incdirs = kwargs["searchincdirs"] else: incdirs = ["./include"] if("objext" in kwargs): objext = kwargs["objext"] else: objext = ".o" if("objstore" in kwargs): objstore = kwargs["objstore"] else: objstore = "./objstore" #Do I want to make this thing this general? docompile = decidecompile(srcfile,**kwargs) if(docompile): f1 = os.path.split(srcfile)[1] f2 = f1 while(os.path.splitext(f2)[1]!=""): f2 = os.path.splitext(f2)[0] outfile = os.path.join(objstore,"{}{}".format(f2,objext)) ln = compiler+" "+flags+" " + outfileflag+" "+outfile+" "+srcfileflag+" "+srcfile ln = ln + " " + include callproc(ln,echo=True,logfile=logfile) return def gs_incremental_compile_list(compiler,srclist,**kwargs): for s in srclist: gs_incremental_compile(compiler,s,**kwargs) return #MSVC compiler wrapper def msvc_incremental_compile(compilername, srcfile, **kwargs): if(not('include' in kwargs)): include = '' else: include = kwargs['include'] if(isinstance(include,list)): include = list_to_sss(include) if(not('flags' in kwargs)): flags = '' else: flags = kwargs['flags'] if(isinstance(flags,list)): flags = list_to_sss(flags) if(not('objext' in kwargs)): objext = '.obj' else: objext = kwargs['objext'] if(not('srcfileflag' in kwargs)): srcfileflag = '/c' else: srcfileflag = kwargs['srcfileflag'] if(not('outfileflag' in kwargs)): outfileflag = '/Fo:' else: outfileflag = kwargs['outfileflag'] if(not('logfile' in kwargs)): logfile = "" else: logfile = kwargs['logfile'] #incrementalcompile if("searchincdirs" in kwargs): incdirs = kwargs["searchincdirs"] else: incdirs = ["./include"] # if("objext" in kwargs): # objext = kwargs["objext"] # else: # objext = ".o" if("objstore" in kwargs): objstore = kwargs["objstore"] else: objstore = "./objstore" docompile = decidecompile(srcfile,**kwargs) if(docompile): f1 = os.path.split(srcfile)[1] f2 = f1 while(os.path.splitext(f2)[1]!=""): f2 = os.path.splitext(f2)[0] outfile = os.path.join(objstore,"{}{}".format(f2,objext)) ln = compilername+" "+flags+" "+srcfileflag+" "+srcfile+" "+outfileflag+" "+outfile ln = ln + " " + include callproc(ln,echo=True,logfile=logfile) # outfile = replaceext(srcfile,objext) # ln = compilername+" "+flags+" "+" "+srcfileflag+" "+srcfile+" "+outfileflag+outfile # ln = ln + " " + include callproc(ln,echo=True,logfile=logfile) return def msvc_incremental_compile_list(compiler,srclist,**kwargs): for S in srclist: msvc_incremental_compile(compiler,S,**kwargs) return ####################### ## Main Script Tests ## ####################### def testtimes(args): if(len(args)>=2): flist = getsrcandincludes(args[1],["./include"]) ftlist = getsrctimes(args[1],["./include"]) for I in range(0,len(flist)): print("{}\t\t{}".format(flist[I],ftlist[I])) print("associated obj file:") fobj = getobjfile(args[1],"./objstore") ftobj = getobjtime(args[1],"./objstore") if(fobj!=""): print("{}\t\t{}".format(fobj,ftobj)) else: print("none found") cflag = decidecompile(args[1]) print("compile? : {}".format(cflag)) return # if(__name__ == "__main__"): # args = sys.argv # testtimes(args)