#!/usr/bin/env python import os, sys, inspect from util import fabricate from util.fabricate import * # depending on root/gcc version, need to compile with "-std=c++11" # you can set this from stdcxx = "" # get cpp version.. #cppver = fabricate.shell("gcc","--version").splitlines()[0].split()[-1] #cppver = int( ( cppver.decode("utf-8") ).split(".")[0] ) # major version only if 'parallel' not in dir() : # because we remove 'dev' from argv, we should only set # the value of 'parallel' once. if 'dev' in sys.argv : parallel = False # sys.argv.remove('dev') else : parallel = True def interpolate(template, **kwargs): return template.format(**kwargs) def run( s ) : "a variant of fabricate.run that does string interpolation." #http://stackoverflow.com/questions/4450592/string-interpolation-in-python checkdeps() calling_frame = inspect.currentframe().f_back names = dict( calling_frame.f_locals, **calling_frame.f_globals ) ss = interpolate(s, **names ) return fabricate.run(*(ss.split())) def shell( *cmds ) : if ( len(cmds) == 1 ) and " " in cmds[0] : # allow one argument: i.e. "command arg1 arg2 etc" return shell( cmds[0].split() ) return fabricate.shell(*cmds).decode("utf-8").strip() def search_file( path , filename ): "search path (which a list of dirs) for filenname" for p in path : f = p +"/"+filename if os.path.exists( f ) : return f return None from contextlib import contextmanager import time rr = "" @contextmanager def subjob ( description ) : global rr t0 = time.time() print ("=== "+description+"...") yield after() # wait for all parallel fabricate jobs to end t1 = time.time() rr += "%-50s %3.2f s\n" % ( description , t1-t0 ) def subjobs_report() : print ("-------------------------------------------------------------") print (rr[:-1]) print ("-------------------------------------------------------------") def checkdeps() : ' check for corrupted .dpes file and remove it if needed' if os.path.exists(".deps") : import json with open(".deps") as f : try : json.load(f) #print(".deps seems fine") except json.decoder.JSONDecodeError : print("-- fabricate .deps file could not be read; deleting so it can be regenerated ") os.remove(".deps") class GccRunner(Runner): """ Runner subclass example that uses gcc's -M dependency generation if it can, otherwise defaults to the default "smart" dependency runner. """ def __init__(self, builder): self.smart_runner = SmartRunner(builder) # get runner instance self.parallel_ok = True def __call__(self, *args): """ Run command using gcc dependency generation if it looks possible, otherwise use the default smart runner to find dependencies. """ if self.is_gcc(args): return self.gcc_runner(args) else: return self.smart_runner(args) def is_gcc(self, args): """ Return True if command looks like a gcc command. """ variants = ['gcc','gcc.exe','g++','gfortran','rootcint',"rootcling" ] return any ( args[0].endswith( x ) for x in variants ) def gcc_runner(self, args): """ Run gcc command and return its dependencies and outputs. """ if args[0].strip() == 'rootcint' or args[0].strip() == 'rootcling' : return self.rootcint_compile_runner( args ) if args[0] == 'gfortran' : return self.gfortran_compile_runner( args ) if '-shared' in args : # crude heuristic return self.gcc_link_runner(args) return self.gcc_compile_runner(args) def run_args( self, args ) : deps_file = 'temp'+str(hash(tuple(args))%1000000)+".d" args = list(args)+[deps_file] # remove empty arguments args = [ a for a in args if a ] fabricate.shell( args, silent=False) f = open(deps_file) try: target, deps_data = f.read().split(':', 1) if " " in target : target = target.split()[-1] finally: f.close() os.remove(deps_file) deps = [] for line in deps_data.split('\n'): if line.endswith(' \\'): line = line[:-2] # temporarily replace spaces in filenames with a char that will # "never" occur line = line.replace('\\ ', '#') for dep in line.split(): dep = dep.replace('#', ' ') deps.append(os.path.abspath(dep)) outputs = [os.path.abspath(target)] return deps, outputs def gfortran_compile_runner( self, args ) : deps_file = 'temp'+str(hash(args)%1000000)+".d" a = list(args) + ['-cpp','-MD','-MF' ] # last one should be -MF as return self.run_args( a ) # ..run_args will add filename def gcc_compile_runner(self, args ): """ Run gcc compile command and return its list of dependencies and outputs, using gcc's -M options to determine dependencies. """ a = list(args) + ['-MMD', '-MF' ] return self.run_args( a ) def rootcint_compile_runner(self, args ): """ Run gcc compile command and return its list of dependencies and outputs, using gcc's -M options to determine dependencies. """ org_args = list(args) target = args[list(args).index('-f') + 1] linkef_file = None args = [] for i,a in enumerate(org_args) : if a == 'rootcint' or a == 'rootcling' : a = 'g++' # fake it to make it args.append(a) args.append( stdcxx ) continue # remove "-f target" if a == '-f' or (i>1 and org_args[i-1] =='-f' ) : continue # remove linkdef if 'linkdef' in a.lower() : linkef_file = a args.append( a ) # -M : only depedenceis, run_args will add filename # also hunt-down dependences inside the linkdef if stdcxx : args += [stdcxx] args += ['-M','-MT',target ] if linkef_file : args += ['-include', linkef_file ] args += ["-MF"] print ("getting dependencies for rootcling") print( args ) deps, outputs = self.run_args( args ) # we have the dependencies: run the orginal command -- this time for real print ("running rootcling") fabricate.shell(org_args, silent=False) return deps, outputs def gcc_link_runner(self, args): """ Run gcc link command and return its list of dependencies and outputs, using given gcc command to determine dependencies. """ target = args[list(args).index('-o') + 1] if sys.platform == 'win32' and not target.endswith('.exe'): target += '.exe' output = [os.path.abspath(target)] # -- simple parsing of linker directive -- path, deps = ["."], [] for a in args : if a.startswith("-L") : path.append( a[2:] ) if a.startswith("-l") : f = search_file( path, "lib"+a[2:]+".so" ) if f : deps.append( os.path.abspath( f ) ) if a.endswith('.o') or a.endswith('.so') : deps.append( os.path.abspath(a) ) # -- dependencies done; finally run the linker -- fabricate.shell(args, silent=False) return deps, output # set up fabricate to use GccRunner setup(runner=GccRunner)