319 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			319 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
| print("running extra...")
 | |
| import gzip
 | |
| import shutil
 | |
| import os
 | |
| import sys
 | |
| import inspect
 | |
| import json
 | |
| import glob
 | |
| from datetime import datetime
 | |
| Import("env")
 | |
| #print(env.Dump())
 | |
| OWN_FILE="extra_script.py"
 | |
| GEN_DIR='lib/generated'
 | |
| CFG_FILE='web/config.json'
 | |
| XDR_FILE='web/xdrconfig.json'
 | |
| CFG_INCLUDE='GwConfigDefinitions.h'
 | |
| CFG_INCLUDE_IMPL='GwConfigDefImpl.h'
 | |
| XDR_INCLUDE='GwXdrTypeMappings.h'
 | |
| TASK_INCLUDE='GwUserTasks.h'
 | |
| EMBEDDED_INCLUDE="GwEmbeddedFiles.h"
 | |
| 
 | |
| def getEmbeddedFiles(env):
 | |
|     rt=[]
 | |
|     efiles=env.GetProjectOption("board_build.embed_files")
 | |
|     for f in efiles.split("\n"):
 | |
|         if f == '':
 | |
|             continue
 | |
|         rt.append(f)
 | |
|     return rt  
 | |
| 
 | |
| def basePath():
 | |
|     #see: https://stackoverflow.com/questions/16771894/python-nameerror-global-name-file-is-not-defined
 | |
|     return os.path.dirname(inspect.getfile(lambda: None))
 | |
| 
 | |
| def outPath():
 | |
|     return os.path.join(basePath(),GEN_DIR)
 | |
| def checkDir():
 | |
|     dn=outPath()
 | |
|     if not os.path.exists(dn):
 | |
|         os.makedirs(dn)
 | |
|     if not os.path.isdir(dn):
 | |
|         print("unable to create %s"%dn)
 | |
|         return False
 | |
|     return True    
 | |
| 
 | |
| def isCurrent(infile,outfile):
 | |
|     if os.path.exists(outfile):
 | |
|         otime=os.path.getmtime(outfile)
 | |
|         itime=os.path.getmtime(infile)
 | |
|         if (otime >= itime):
 | |
|             own=os.path.join(basePath(),OWN_FILE)
 | |
|             if os.path.exists(own):
 | |
|                 owntime=os.path.getmtime(own)
 | |
|                 if owntime > otime:
 | |
|                     return False
 | |
|             print("%s is newer then %s, no need to recreate"%(outfile,infile))
 | |
|             return True
 | |
|     return False        
 | |
| def compressFile(inFile,outfile):
 | |
|     if isCurrent(inFile,outfile):
 | |
|         return
 | |
|     with open(inFile, 'rb') as f_in:
 | |
|         with gzip.open(outfile, 'wb') as f_out:
 | |
|             shutil.copyfileobj(f_in, f_out)
 | |
| 
 | |
| def generateFile(infile,outfile,callback,inMode='rb',outMode='w'):
 | |
|     if isCurrent(infile,outfile):
 | |
|         return
 | |
|     print("creating %s"%outfile)
 | |
|     oh=None
 | |
|     with open(infile,inMode) as ch:
 | |
|         with open(outfile,'w') as oh:
 | |
|             try:
 | |
|                 callback(ch,oh,inFile=infile)
 | |
|                 oh.close()
 | |
|             except Exception as e:
 | |
|                 try:
 | |
|                     oh.close()
 | |
|                 except:
 | |
|                     pass
 | |
|                 os.unlink(outfile)
 | |
|                 raise
 | |
| 
 | |
| def writeFileIfChanged(fileName,data):
 | |
|     if os.path.exists(fileName):
 | |
|         with open(fileName,"r") as ih:
 | |
|             old=ih.read()
 | |
|             ih.close()
 | |
|             if old == data:
 | |
|                 return False
 | |
|     print("#generating %s"%fileName)
 | |
|     with open(fileName,"w") as oh:
 | |
|         oh.write(data)
 | |
|     return True    
 | |
| 
 | |
| def mergeConfig(base,other):
 | |
|     for bdir in other:
 | |
|         cname=os.path.join(bdir,"config.json")
 | |
|         if os.path.exists(cname):
 | |
|             print("merge config %s"%cname)
 | |
|             with open(cname,'rb') as ah:
 | |
|                 merge=json.load(ah)
 | |
|                 base=base+merge
 | |
|     return base
 | |
| 
 | |
| def generateMergedConfig(inFile,outFile,addDirs=[]):
 | |
|     if not os.path.exists(inFile):
 | |
|         raise Exception("unable to read cfg file %s"%inFile)
 | |
|     data=""
 | |
|     with open(inFile,'rb') as ch:    
 | |
|         config=json.load(ch)
 | |
|         config=mergeConfig(config,addDirs)
 | |
|         data=json.dumps(config,indent=2)
 | |
|         writeFileIfChanged(outFile,data)
 | |
| 
 | |
| def generateCfg(inFile,outFile,impl):
 | |
|     if not os.path.exists(inFile):
 | |
|         raise Exception("unable to read cfg file %s"%inFile)
 | |
|     data=""
 | |
|     with open(inFile,'rb') as ch:    
 | |
|         config=json.load(ch)     
 | |
|         data+="//generated from %s\n"%inFile
 | |
|         l=len(config)
 | |
|         idx=0
 | |
|         if not impl:
 | |
|             data+='#include "GwConfigItem.h"\n'
 | |
|             data+='class GwConfigDefinitions{\n'
 | |
|             data+='  public:\n'
 | |
|             data+='  int getNumConfig() const{return %d;}\n'%(l)
 | |
|             for item in config:
 | |
|                 n=item.get('name')
 | |
|                 if n is None:
 | |
|                     continue
 | |
|                 if len(n) > 15:
 | |
|                     raise Exception("%s: config names must be max 15 caracters"%n)
 | |
|                 data+='  static constexpr const char* %s="%s";\n'%(n,n)
 | |
|             data+="};\n"
 | |
|         else:
 | |
|             data+='void GwConfigHandler::populateConfigs(GwConfigInterface **config){\n'
 | |
|             for item in config:
 | |
|                 name=item.get('name')
 | |
|                 if name is None:
 | |
|                     continue
 | |
|                 data+='  configs[%d]=\n'%(idx)
 | |
|                 idx+=1
 | |
|                 secret="false";
 | |
|                 if item.get('type') == 'password':
 | |
|                     secret="true"
 | |
|                 data+="    #undef __CFGMODE\n"
 | |
|                 data+="    #ifdef CFGMODE_%s\n"%(name)
 | |
|                 data+="      #define __CFGMODE CFGMODE_%s\n"%(name)
 | |
|                 data+="    #else\n"
 | |
|                 data+="      #define __CFGMODE GwConfigInterface::NORMAL\n"
 | |
|                 data+="    #endif\n"    
 | |
|                 data+="    #ifdef CFGDEFAULT_%s\n"%(name)
 | |
|                 data+="     new GwConfigInterface(%s,CFGDEFAULT_%s,%s,__CFGMODE)\n"%(name,name,secret)
 | |
|                 data+="    #else\n"
 | |
|                 data+="     new GwConfigInterface(%s,\"%s\",%s,__CFGMODE)\n"%(name,item.get('default'),secret)
 | |
|                 data+="    #endif\n"
 | |
|                 data+=";\n"
 | |
|             data+='}\n'  
 | |
|             for item in config:
 | |
|                 name=item.get('name')
 | |
|                 if name is None:
 | |
|                     continue
 | |
|                 data+="#ifdef CFGMODE_%s\n"%(name)
 | |
|                 data+=" __MSG(\"CFGMODE_%s=\" __XSTR(CFGMODE_%s))\n"%(name,name)
 | |
|                 data+="#endif\n"
 | |
|                 data+="#ifdef CFGDEFAULT_%s\n"%(name)
 | |
|                 data+=" __MSG(\"CFGDEFAULT_%s=\" CFGDEFAULT_%s)\n"%(name,name)
 | |
|                 data+="#endif\n"
 | |
|     writeFileIfChanged(outFile,data)    
 | |
|                     
 | |
| 
 | |
| def generateXdrMappings(fp,oh,inFile=''):
 | |
|     jdoc=json.load(fp)
 | |
|     oh.write("static GwXDRTypeMapping* typeMappings[]={\n")
 | |
|     first=True
 | |
|     for cat in jdoc:
 | |
|         item=jdoc[cat]
 | |
|         cid=item.get('id')
 | |
|         if cid is None:
 | |
|             continue
 | |
|         tc=item.get('type')
 | |
|         if tc is not None:
 | |
|             if first:
 | |
|                 first=False
 | |
|             else:
 | |
|                 oh.write(",\n")
 | |
|             oh.write("   new GwXDRTypeMapping(%d,0,%d) /*%s*/"%(cid,tc,cat))
 | |
|         fields=item.get('fields')
 | |
|         if fields is None:
 | |
|             continue
 | |
|         idx=0
 | |
|         for fe in fields:
 | |
|             if not isinstance(fe,dict):
 | |
|                 continue
 | |
|             tc=fe.get('t')
 | |
|             id=fe.get('v')
 | |
|             if id is None:
 | |
|                 id=idx
 | |
|             idx+=1
 | |
|             l=fe.get('l') or ''
 | |
|             if tc is None or id is None:
 | |
|                 continue
 | |
|             if first:
 | |
|                 first=False
 | |
|             else:
 | |
|                 oh.write(",\n")
 | |
|             oh.write("   new GwXDRTypeMapping(%d,%d,%d) /*%s:%s*/"%(cid,id,tc,cat,l))
 | |
|     oh.write("\n")
 | |
|     oh.write("};\n")
 | |
| 
 | |
| userTaskDirs=[]
 | |
| 
 | |
| def getUserTaskDirs():
 | |
|     rt=[]
 | |
|     taskdirs=glob.glob(os.path.join('lib','*task*'))
 | |
|     for task in taskdirs:
 | |
|         rt.append(task)
 | |
|     return rt
 | |
| def genereateUserTasks(outfile):
 | |
|     includes=[]
 | |
|     for task in userTaskDirs:
 | |
|         #print("##taskdir=%s"%task)
 | |
|         base=os.path.basename(task)
 | |
|         includeNames=[base.lower()+".h",'gw'+base.lower()+'.h']
 | |
|         for f in os.listdir(task):
 | |
|             if not f.endswith('.h'):
 | |
|                 continue
 | |
|             match=False
 | |
|             for cmp in includeNames:
 | |
|                 #print("##check %s<->%s"%(f.lower(),cmp))
 | |
|                 if f.lower() == cmp:
 | |
|                     match=True
 | |
|             if not match:
 | |
|                 continue
 | |
|             includes.append(f)
 | |
|     includeData=""
 | |
|     for i in includes:
 | |
|         print("#task include %s"%i)
 | |
|         includeData+="#include <%s>\n"%i
 | |
|     writeFileIfChanged(outfile,includeData)            
 | |
| 
 | |
| def generateEmbedded(elist,outFile):
 | |
|     content=""
 | |
|     for entry in elist:
 | |
|         content+="EMBED_GZ_FILE(\"%s\",%s,\"%s\");\n"%entry
 | |
|     writeFileIfChanged(outFile,content)    
 | |
| 
 | |
| def getContentType(fn):
 | |
|     if (fn.endswith('.gz')):
 | |
|         fn=fn[0:-3]
 | |
|     if (fn.endswith('html')):
 | |
|         return "text/html"
 | |
|     if (fn.endswith('json')):
 | |
|         return "application/json"
 | |
|     if (fn.endswith('js')):
 | |
|         return "text/javascript"
 | |
|     if (fn.endswith('css')):    
 | |
|         return "text/css"
 | |
|     return "application/octet-stream"
 | |
| 
 | |
| def prebuild(env):
 | |
|     global userTaskDirs
 | |
|     print("#prebuild running")
 | |
|     if not checkDir():
 | |
|         sys.exit(1)
 | |
|     userTaskDirs=getUserTaskDirs()
 | |
|     mergedConfig=os.path.join(outPath(),os.path.basename(CFG_FILE))
 | |
|     generateMergedConfig(os.path.join(basePath(),CFG_FILE),mergedConfig,userTaskDirs)
 | |
|     compressFile(mergedConfig,mergedConfig+".gz")
 | |
|     generateCfg(mergedConfig,os.path.join(outPath(),CFG_INCLUDE),False)
 | |
|     generateCfg(mergedConfig,os.path.join(outPath(),CFG_INCLUDE_IMPL),True)
 | |
|     embedded=getEmbeddedFiles(env)
 | |
|     filedefs=[]
 | |
|     for ef in embedded:
 | |
|         print("#checking embedded file %s"%ef)
 | |
|         (dn,fn)=os.path.split(ef)
 | |
|         pureName=fn
 | |
|         if pureName.endswith('.gz'):
 | |
|             pureName=pureName[0:-3]
 | |
|         ct=getContentType(pureName)
 | |
|         usname=ef.replace('/','_').replace('.','_')
 | |
|         filedefs.append((pureName,usname,ct))
 | |
|         inFile=os.path.join(basePath(),"web",pureName)
 | |
|         if os.path.exists(inFile):
 | |
|             print("compressing %s"%inFile)
 | |
|             compressFile(inFile,ef)
 | |
|         else:
 | |
|             print("#WARNING: infile %s for %s not found"%(inFile,ef))
 | |
|     generateEmbedded(filedefs,os.path.join(outPath(),EMBEDDED_INCLUDE))
 | |
|     genereateUserTasks(os.path.join(outPath(), TASK_INCLUDE))
 | |
|     generateFile(os.path.join(basePath(),XDR_FILE),os.path.join(outPath(),XDR_INCLUDE),generateXdrMappings)
 | |
|     version="dev"+datetime.now().strftime("%Y%m%d")
 | |
|     env.Append(CPPDEFINES=[('GWDEVVERSION',version)])
 | |
| 
 | |
| def cleangenerated(source, target, env):
 | |
|     od=outPath()
 | |
|     if os.path.isdir(od):
 | |
|         print("#cleaning up %s"%od)
 | |
|         for f in os.listdir(od):
 | |
|             if f == "." or f == "..":
 | |
|                 continue
 | |
|             fn=os.path.join(od,f)
 | |
|             os.unlink(f)
 | |
| 
 | |
| print("#prescript...")
 | |
| prebuild(env)
 | |
| board="PLATFORM_BOARD_%s"%env["BOARD"].replace("-","_").upper()
 | |
| print("Board=#%s#"%board)
 | |
| print("BuildFlags=%s"%(" ".join(env["BUILD_FLAGS"])))
 | |
| env.Append(
 | |
|     LINKFLAGS=[ "-u", "custom_app_desc" ],
 | |
|     CPPDEFINES=[(board,"1")]
 | |
| )
 | |
| #script does not run on clean yet - maybe in the future
 | |
| env.AddPostAction("clean",cleangenerated)
 |