1*0ee81e68SLisandro Dalcin#!/usr/bin/env python 2*0ee81e68SLisandro Dalcin 3*0ee81e68SLisandro Dalcinimport os 4*0ee81e68SLisandro Dalcinfrom distutils.sysconfig import parse_makefile 5*0ee81e68SLisandro Dalcinimport sys 6*0ee81e68SLisandro Dalcinimport logging 7*0ee81e68SLisandro Dalcinsys.path.insert(0, os.path.abspath(os.path.dirname(__file__))) 8*0ee81e68SLisandro Dalcinfrom cmakegen import Mistakes, stripsplit, AUTODIRS, SKIPDIRS 9*0ee81e68SLisandro Dalcinfrom cmakegen import defaultdict # collections.defaultdict, with fallback for python-2.4 10*0ee81e68SLisandro Dalcin 11*0ee81e68SLisandro DalcinPKGS = 'sys vec mat dm ksp snes ts tao'.split() 12*0ee81e68SLisandro DalcinLANGS = dict(c='C', cxx='CXX', cu='CU', F='F') 13*0ee81e68SLisandro Dalcin 14*0ee81e68SLisandro Dalcintry: 15*0ee81e68SLisandro Dalcin all([True, True]) 16*0ee81e68SLisandro Dalcinexcept NameError: # needs python-2.5 17*0ee81e68SLisandro Dalcin def all(iterable): 18*0ee81e68SLisandro Dalcin for i in iterable: 19*0ee81e68SLisandro Dalcin if not i: 20*0ee81e68SLisandro Dalcin return False 21*0ee81e68SLisandro Dalcin return True 22*0ee81e68SLisandro Dalcin 23*0ee81e68SLisandro Dalcintry: 24*0ee81e68SLisandro Dalcin os.path.relpath # needs python-2.6 25*0ee81e68SLisandro Dalcinexcept AttributeError: 26*0ee81e68SLisandro Dalcin def _relpath(path, start=os.path.curdir): 27*0ee81e68SLisandro Dalcin """Return a relative version of a path""" 28*0ee81e68SLisandro Dalcin 29*0ee81e68SLisandro Dalcin from os.path import curdir, abspath, commonprefix, sep, pardir, join 30*0ee81e68SLisandro Dalcin if not path: 31*0ee81e68SLisandro Dalcin raise ValueError("no path specified") 32*0ee81e68SLisandro Dalcin 33*0ee81e68SLisandro Dalcin start_list = [x for x in abspath(start).split(sep) if x] 34*0ee81e68SLisandro Dalcin path_list = [x for x in abspath(path).split(sep) if x] 35*0ee81e68SLisandro Dalcin 36*0ee81e68SLisandro Dalcin # Work out how much of the filepath is shared by start and path. 37*0ee81e68SLisandro Dalcin i = len(commonprefix([start_list, path_list])) 38*0ee81e68SLisandro Dalcin 39*0ee81e68SLisandro Dalcin rel_list = [pardir] * (len(start_list)-i) + path_list[i:] 40*0ee81e68SLisandro Dalcin if not rel_list: 41*0ee81e68SLisandro Dalcin return curdir 42*0ee81e68SLisandro Dalcin return join(*rel_list) 43*0ee81e68SLisandro Dalcin os.path.relpath = _relpath 44*0ee81e68SLisandro Dalcin 45*0ee81e68SLisandro Dalcinclass debuglogger(object): 46*0ee81e68SLisandro Dalcin def __init__(self, log): 47*0ee81e68SLisandro Dalcin self._log = log 48*0ee81e68SLisandro Dalcin 49*0ee81e68SLisandro Dalcin def write(self, string): 50*0ee81e68SLisandro Dalcin self._log.debug(string) 51*0ee81e68SLisandro Dalcin 52*0ee81e68SLisandro Dalcinclass Petsc(object): 53*0ee81e68SLisandro Dalcin def __init__(self, petsc_dir=None, petsc_arch=None, verbose=False): 54*0ee81e68SLisandro Dalcin if petsc_dir is None: 55*0ee81e68SLisandro Dalcin petsc_dir = os.environ.get('PETSC_DIR') 56*0ee81e68SLisandro Dalcin if petsc_dir is None: 57*0ee81e68SLisandro Dalcin try: 58*0ee81e68SLisandro Dalcin petsc_dir = parse_makefile(os.path.join('lib','petsc-conf', 'petscvariables')).get('PETSC_DIR') 59*0ee81e68SLisandro Dalcin finally: 60*0ee81e68SLisandro Dalcin if petsc_dir is None: 61*0ee81e68SLisandro Dalcin raise RuntimeError('Could not determine PETSC_DIR, please set in environment') 62*0ee81e68SLisandro Dalcin if petsc_arch is None: 63*0ee81e68SLisandro Dalcin petsc_arch = os.environ.get('PETSC_ARCH') 64*0ee81e68SLisandro Dalcin if petsc_arch is None: 65*0ee81e68SLisandro Dalcin try: 66*0ee81e68SLisandro Dalcin petsc_arch = parse_makefile(os.path.join(petsc_dir, 'lib','petsc-conf', 'petscvariables')).get('PETSC_ARCH') 67*0ee81e68SLisandro Dalcin finally: 68*0ee81e68SLisandro Dalcin if petsc_arch is None: 69*0ee81e68SLisandro Dalcin raise RuntimeError('Could not determine PETSC_ARCH, please set in environment') 70*0ee81e68SLisandro Dalcin self.petsc_dir = petsc_dir 71*0ee81e68SLisandro Dalcin self.petsc_arch = petsc_arch 72*0ee81e68SLisandro Dalcin self.read_conf() 73*0ee81e68SLisandro Dalcin logging.basicConfig(filename=self.arch_path('lib','petsc-conf', 'gmake.log'), level=logging.DEBUG) 74*0ee81e68SLisandro Dalcin self.log = logging.getLogger('gmakegen') 75*0ee81e68SLisandro Dalcin self.mistakes = Mistakes(debuglogger(self.log), verbose=verbose) 76*0ee81e68SLisandro Dalcin self.gendeps = [] 77*0ee81e68SLisandro Dalcin 78*0ee81e68SLisandro Dalcin def arch_path(self, *args): 79*0ee81e68SLisandro Dalcin return os.path.join(self.petsc_dir, self.petsc_arch, *args) 80*0ee81e68SLisandro Dalcin 81*0ee81e68SLisandro Dalcin def read_conf(self): 82*0ee81e68SLisandro Dalcin self.conf = dict() 83*0ee81e68SLisandro Dalcin for line in open(self.arch_path('include', 'petscconf.h')): 84*0ee81e68SLisandro Dalcin if line.startswith('#define '): 85*0ee81e68SLisandro Dalcin define = line[len('#define '):] 86*0ee81e68SLisandro Dalcin space = define.find(' ') 87*0ee81e68SLisandro Dalcin key = define[:space] 88*0ee81e68SLisandro Dalcin val = define[space+1:] 89*0ee81e68SLisandro Dalcin self.conf[key] = val 90*0ee81e68SLisandro Dalcin self.conf.update(parse_makefile(self.arch_path('lib','petsc-conf', 'petscvariables'))) 91*0ee81e68SLisandro Dalcin self.have_fortran = int(self.conf.get('PETSC_HAVE_FORTRAN', '0')) 92*0ee81e68SLisandro Dalcin 93*0ee81e68SLisandro Dalcin def inconf(self, key, val): 94*0ee81e68SLisandro Dalcin if key in ['package', 'function', 'define']: 95*0ee81e68SLisandro Dalcin return self.conf.get(val) 96*0ee81e68SLisandro Dalcin elif key == 'precision': 97*0ee81e68SLisandro Dalcin return val == self.conf['PETSC_PRECISION'] 98*0ee81e68SLisandro Dalcin elif key == 'scalar': 99*0ee81e68SLisandro Dalcin return val == self.conf['PETSC_SCALAR'] 100*0ee81e68SLisandro Dalcin elif key == 'language': 101*0ee81e68SLisandro Dalcin return val == self.conf['PETSC_LANGUAGE'] 102*0ee81e68SLisandro Dalcin raise RuntimeError('Unknown conf check: %s %s' % (key, val)) 103*0ee81e68SLisandro Dalcin 104*0ee81e68SLisandro Dalcin def relpath(self, root, src): 105*0ee81e68SLisandro Dalcin return os.path.relpath(os.path.join(root, src), self.petsc_dir) 106*0ee81e68SLisandro Dalcin 107*0ee81e68SLisandro Dalcin def get_sources(self, makevars): 108*0ee81e68SLisandro Dalcin """Return dict {lang: list_of_source_files}""" 109*0ee81e68SLisandro Dalcin source = dict() 110*0ee81e68SLisandro Dalcin for lang, sourcelang in LANGS.items(): 111*0ee81e68SLisandro Dalcin source[lang] = [f for f in makevars.get('SOURCE'+sourcelang,'').split() if f.endswith(lang)] 112*0ee81e68SLisandro Dalcin return source 113*0ee81e68SLisandro Dalcin 114*0ee81e68SLisandro Dalcin def gen_pkg(self, pkg): 115*0ee81e68SLisandro Dalcin pkgsrcs = dict() 116*0ee81e68SLisandro Dalcin for lang in LANGS: 117*0ee81e68SLisandro Dalcin pkgsrcs[lang] = [] 118*0ee81e68SLisandro Dalcin for root, dirs, files in os.walk(os.path.join(self.petsc_dir, 'src', pkg)): 119*0ee81e68SLisandro Dalcin makefile = os.path.join(root,'makefile') 120*0ee81e68SLisandro Dalcin if not os.path.exists(makefile): 121*0ee81e68SLisandro Dalcin dirs[:] = [] 122*0ee81e68SLisandro Dalcin continue 123*0ee81e68SLisandro Dalcin mklines = open(makefile) 124*0ee81e68SLisandro Dalcin conditions = set(tuple(stripsplit(line)) for line in mklines if line.startswith('#requires')) 125*0ee81e68SLisandro Dalcin mklines.close() 126*0ee81e68SLisandro Dalcin if not all(self.inconf(key, val) for key, val in conditions): 127*0ee81e68SLisandro Dalcin dirs[:] = [] 128*0ee81e68SLisandro Dalcin continue 129*0ee81e68SLisandro Dalcin makevars = parse_makefile(makefile) 130*0ee81e68SLisandro Dalcin mdirs = makevars.get('DIRS','').split() # Directories specified in the makefile 131*0ee81e68SLisandro Dalcin self.mistakes.compareDirLists(root, mdirs, dirs) # diagnostic output to find unused directories 132*0ee81e68SLisandro Dalcin candidates = set(mdirs).union(AUTODIRS).difference(SKIPDIRS) 133*0ee81e68SLisandro Dalcin dirs[:] = list(candidates.intersection(dirs)) 134*0ee81e68SLisandro Dalcin allsource = [] 135*0ee81e68SLisandro Dalcin def mkrel(src): 136*0ee81e68SLisandro Dalcin return self.relpath(root, src) 137*0ee81e68SLisandro Dalcin source = self.get_sources(makevars) 138*0ee81e68SLisandro Dalcin for lang, s in source.items(): 139*0ee81e68SLisandro Dalcin pkgsrcs[lang] += map(mkrel, s) 140*0ee81e68SLisandro Dalcin allsource += s 141*0ee81e68SLisandro Dalcin self.mistakes.compareSourceLists(root, allsource, files) # Diagnostic output about unused source files 142*0ee81e68SLisandro Dalcin self.gendeps.append(self.relpath(root, 'makefile')) 143*0ee81e68SLisandro Dalcin return pkgsrcs 144*0ee81e68SLisandro Dalcin 145*0ee81e68SLisandro Dalcin def gen_gnumake(self, fd): 146*0ee81e68SLisandro Dalcin def write(stem, srcs): 147*0ee81e68SLisandro Dalcin fd.write('%s :=\n' % stem) 148*0ee81e68SLisandro Dalcin for lang in LANGS: 149*0ee81e68SLisandro Dalcin fd.write('%(stem)s.%(lang)s := %(srcs)s\n' % dict(stem=stem, lang=lang, srcs=' '.join(srcs[lang]))) 150*0ee81e68SLisandro Dalcin fd.write('%(stem)s += $(%(stem)s.%(lang)s)\n' % dict(stem=stem, lang=lang)) 151*0ee81e68SLisandro Dalcin for pkg in PKGS: 152*0ee81e68SLisandro Dalcin srcs = self.gen_pkg(pkg) 153*0ee81e68SLisandro Dalcin write('srcs-' + pkg, srcs) 154*0ee81e68SLisandro Dalcin return self.gendeps 155*0ee81e68SLisandro Dalcin 156*0ee81e68SLisandro Dalcin def gen_ninja(self, fd): 157*0ee81e68SLisandro Dalcin libobjs = [] 158*0ee81e68SLisandro Dalcin for pkg in PKGS: 159*0ee81e68SLisandro Dalcin srcs = self.gen_pkg(pkg) 160*0ee81e68SLisandro Dalcin for lang in LANGS: 161*0ee81e68SLisandro Dalcin for src in srcs[lang]: 162*0ee81e68SLisandro Dalcin obj = '$objdir/%s.o' % src 163*0ee81e68SLisandro Dalcin fd.write('build %(obj)s : %(lang)s_COMPILE %(src)s\n' % dict(obj=obj, lang=lang.upper(), src=os.path.join(self.petsc_dir,src))) 164*0ee81e68SLisandro Dalcin libobjs.append(obj) 165*0ee81e68SLisandro Dalcin fd.write('\n') 166*0ee81e68SLisandro Dalcin fd.write('build $libdir/libpetsc.so : %s_LINK_SHARED %s\n\n' % ('CF'[self.have_fortran], ' '.join(libobjs))) 167*0ee81e68SLisandro Dalcin fd.write('build petsc : phony || $libdir/libpetsc.so\n\n') 168*0ee81e68SLisandro Dalcin 169*0ee81e68SLisandro Dalcin def summary(self): 170*0ee81e68SLisandro Dalcin self.mistakes.summary() 171*0ee81e68SLisandro Dalcin 172*0ee81e68SLisandro Dalcindef WriteGnuMake(petsc): 173*0ee81e68SLisandro Dalcin arch_files = petsc.arch_path('lib','petsc-conf', 'files') 174*0ee81e68SLisandro Dalcin fd = open(arch_files, 'w') 175*0ee81e68SLisandro Dalcin gendeps = petsc.gen_gnumake(fd) 176*0ee81e68SLisandro Dalcin fd.write('\n') 177*0ee81e68SLisandro Dalcin fd.write('# Dependency to regenerate this file\n') 178*0ee81e68SLisandro Dalcin fd.write('%s : %s %s\n' % (os.path.relpath(arch_files, petsc.petsc_dir), 179*0ee81e68SLisandro Dalcin os.path.relpath(__file__, os.path.realpath(petsc.petsc_dir)), 180*0ee81e68SLisandro Dalcin ' '.join(gendeps))) 181*0ee81e68SLisandro Dalcin fd.write('\n') 182*0ee81e68SLisandro Dalcin fd.write('# Dummy dependencies in case makefiles are removed\n') 183*0ee81e68SLisandro Dalcin fd.write(''.join([dep + ':\n' for dep in gendeps])) 184*0ee81e68SLisandro Dalcin fd.close() 185*0ee81e68SLisandro Dalcin 186*0ee81e68SLisandro Dalcindef WriteNinja(petsc): 187*0ee81e68SLisandro Dalcin conf = dict() 188*0ee81e68SLisandro Dalcin parse_makefile(os.path.join(petsc.petsc_dir, 'lib', 'petsc-conf', 'variables'), conf) 189*0ee81e68SLisandro Dalcin parse_makefile(petsc.arch_path('lib','petsc-conf', 'petscvariables'), conf) 190*0ee81e68SLisandro Dalcin build_ninja = petsc.arch_path('build.ninja') 191*0ee81e68SLisandro Dalcin fd = open(build_ninja, 'w') 192*0ee81e68SLisandro Dalcin fd.write('objdir = obj-ninja\n') 193*0ee81e68SLisandro Dalcin fd.write('libdir = lib\n') 194*0ee81e68SLisandro Dalcin fd.write('c_compile = %(PCC)s\n' % conf) 195*0ee81e68SLisandro Dalcin fd.write('c_flags = %(PETSC_CC_INCLUDES)s %(PCC_FLAGS)s %(CCPPFLAGS)s\n' % conf) 196*0ee81e68SLisandro Dalcin fd.write('c_link = %(PCC_LINKER)s\n' % conf) 197*0ee81e68SLisandro Dalcin fd.write('c_link_flags = %(PCC_LINKER_FLAGS)s\n' % conf) 198*0ee81e68SLisandro Dalcin if petsc.have_fortran: 199*0ee81e68SLisandro Dalcin fd.write('f_compile = %(FC)s\n' % conf) 200*0ee81e68SLisandro Dalcin fd.write('f_flags = %(PETSC_FC_INCLUDES)s %(FC_FLAGS)s %(FCPPFLAGS)s\n' % conf) 201*0ee81e68SLisandro Dalcin fd.write('f_link = %(FC_LINKER)s\n' % conf) 202*0ee81e68SLisandro Dalcin fd.write('f_link_flags = %(FC_LINKER_FLAGS)s\n' % conf) 203*0ee81e68SLisandro Dalcin fd.write('petsc_external_lib = %(PETSC_EXTERNAL_LIB_BASIC)s\n' % conf) 204*0ee81e68SLisandro Dalcin fd.write('python = %(PYTHON)s\n' % conf) 205*0ee81e68SLisandro Dalcin fd.write('\n') 206*0ee81e68SLisandro Dalcin fd.write('rule C_COMPILE\n' 207*0ee81e68SLisandro Dalcin ' command = $c_compile -MMD -MF $out.d $c_flags -c $in -o $out\n' 208*0ee81e68SLisandro Dalcin ' description = CC $out\n' 209*0ee81e68SLisandro Dalcin ' depfile = $out.d\n' 210*0ee81e68SLisandro Dalcin # ' deps = gcc\n') # 'gcc' is default, 'msvc' only recognized by newer versions of ninja 211*0ee81e68SLisandro Dalcin '\n') 212*0ee81e68SLisandro Dalcin fd.write('rule C_LINK_SHARED\n' 213*0ee81e68SLisandro Dalcin ' command = $c_link $c_link_flags -shared -o $out $in $petsc_external_lib\n' 214*0ee81e68SLisandro Dalcin ' description = CLINK_SHARED $out\n' 215*0ee81e68SLisandro Dalcin '\n') 216*0ee81e68SLisandro Dalcin if petsc.have_fortran: 217*0ee81e68SLisandro Dalcin fd.write('rule F_COMPILE\n' 218*0ee81e68SLisandro Dalcin ' command = $f_compile -MMD -MF $out.d $f_flags -c $in -o $out\n' 219*0ee81e68SLisandro Dalcin ' description = FC $out\n' 220*0ee81e68SLisandro Dalcin ' depfile = $out.d\n' 221*0ee81e68SLisandro Dalcin '\n') 222*0ee81e68SLisandro Dalcin fd.write('rule F_LINK_SHARED\n' 223*0ee81e68SLisandro Dalcin ' command = $f_link $f_link_flags -shared -o $out $in $petsc_external_lib\n' 224*0ee81e68SLisandro Dalcin ' description = FLINK_SHARED $out\n' 225*0ee81e68SLisandro Dalcin '\n') 226*0ee81e68SLisandro Dalcin fd.write('rule GEN_NINJA\n' 227*0ee81e68SLisandro Dalcin ' command = $python $in --output=ninja\n' 228*0ee81e68SLisandro Dalcin ' generator = 1\n' 229*0ee81e68SLisandro Dalcin '\n') 230*0ee81e68SLisandro Dalcin petsc.gen_ninja(fd) 231*0ee81e68SLisandro Dalcin fd.write('\n') 232*0ee81e68SLisandro Dalcin fd.write('build %s : GEN_NINJA | %s %s %s %s\n' % (build_ninja, 233*0ee81e68SLisandro Dalcin os.path.abspath(__file__), 234*0ee81e68SLisandro Dalcin os.path.join(petsc.petsc_dir, 'lib','petsc-conf', 'variables'), 235*0ee81e68SLisandro Dalcin petsc.arch_path('lib','petsc-conf', 'petscvariables'), 236*0ee81e68SLisandro Dalcin ' '.join(os.path.join(petsc.petsc_dir, dep) for dep in petsc.gendeps))) 237*0ee81e68SLisandro Dalcin 238*0ee81e68SLisandro Dalcindef main(petsc_dir=None, petsc_arch=None, output=None, verbose=False): 239*0ee81e68SLisandro Dalcin if output is None: 240*0ee81e68SLisandro Dalcin output = 'gnumake' 241*0ee81e68SLisandro Dalcin writer = dict(gnumake=WriteGnuMake, ninja=WriteNinja) 242*0ee81e68SLisandro Dalcin petsc = Petsc(petsc_dir=petsc_dir, petsc_arch=petsc_arch, verbose=verbose) 243*0ee81e68SLisandro Dalcin writer[output](petsc) 244*0ee81e68SLisandro Dalcin petsc.summary() 245*0ee81e68SLisandro Dalcin 246*0ee81e68SLisandro Dalcinif __name__ == '__main__': 247*0ee81e68SLisandro Dalcin import optparse 248*0ee81e68SLisandro Dalcin parser = optparse.OptionParser() 249*0ee81e68SLisandro Dalcin parser.add_option('--verbose', help='Show mismatches between makefiles and the filesystem', action='store_true', default=False) 250*0ee81e68SLisandro Dalcin parser.add_option('--petsc-arch', help='Set PETSC_ARCH different from environment', default=os.environ.get('PETSC_ARCH')) 251*0ee81e68SLisandro Dalcin parser.add_option('--output', help='Location to write output file', default=None) 252*0ee81e68SLisandro Dalcin opts, extra_args = parser.parse_args() 253*0ee81e68SLisandro Dalcin if extra_args: 254*0ee81e68SLisandro Dalcin import sys 255*0ee81e68SLisandro Dalcin sys.stderr.write('Unknown arguments: %s\n' % ' '.join(extra_args)) 256*0ee81e68SLisandro Dalcin exit(1) 257*0ee81e68SLisandro Dalcin main(petsc_arch=opts.petsc_arch, output=opts.output, verbose=opts.verbose) 258