xref: /petsc/config/gmakegen.py (revision df3bd252dbc574245cbbb4edf6b407438c525c05)
1*df3bd252SSatish Balay#!/usr/bin/env python3
20ee81e68SLisandro Dalcin
30ee81e68SLisandro Dalcinimport os
4becf0a19SJed Brownfrom sysconfig import _parse_makefile as parse_makefile
50ee81e68SLisandro Dalcinimport sys
60ee81e68SLisandro Dalcinimport logging
70ee81e68SLisandro Dalcinsys.path.insert(0, os.path.abspath(os.path.dirname(__file__)))
87b8851e6SJed Brownfrom collections import defaultdict
90ee81e68SLisandro Dalcin
10b892d579SJed BrownAUTODIRS = set('ftn-auto ftn-custom f90-custom ftn-auto-interfaces'.split()) # Automatically recurse into these, if they exist
11f22bedf1SBarry SmithSKIPDIRS = set('benchmarks build'.split())               # Skip these during the build
12dc0529c6SBarry SmithNOWARNDIRS = set('tests tutorials'.split())              # Do not warn about mismatch in these
13dc0529c6SBarry Smith
14dc0529c6SBarry Smithdef pathsplit(path):
15dc0529c6SBarry Smith    """Recursively split a path, returns a tuple"""
16dc0529c6SBarry Smith    stem, basename = os.path.split(path)
17dc0529c6SBarry Smith    if stem == '':
18dc0529c6SBarry Smith        return (basename,)
19dc0529c6SBarry Smith    if stem == path:            # fixed point, likely '/'
20dc0529c6SBarry Smith        return (path,)
21dc0529c6SBarry Smith    return pathsplit(stem) + (basename,)
22dc0529c6SBarry Smith
23c0558f20SBarry Smithdef getlangext(name):
24c0558f20SBarry Smith    """Returns everything after the first . in the filename, including the ."""
25c0558f20SBarry Smith    file = os.path.basename(name)
26c0558f20SBarry Smith    loc = file.find('.')
27c0558f20SBarry Smith    if loc > -1: return file[loc:]
28c0558f20SBarry Smith    else: return ''
29c0558f20SBarry Smith
30c0558f20SBarry Smithdef getlangsplit(name):
31c0558f20SBarry Smith    """Returns everything before the first . in the filename, excluding the ."""
32c0558f20SBarry Smith    file = os.path.basename(name)
33c0558f20SBarry Smith    loc = file.find('.')
34c0558f20SBarry Smith    if loc > -1: return os.path.join(os.path.dirname(name),file[:loc])
35c0558f20SBarry Smith    raise RuntimeError("No . in filename")
36c0558f20SBarry Smith
37dc0529c6SBarry Smithclass Mistakes(object):
38dc0529c6SBarry Smith    def __init__(self, log, verbose=False):
39dc0529c6SBarry Smith        self.mistakes = []
40dc0529c6SBarry Smith        self.verbose = verbose
41dc0529c6SBarry Smith        self.log = log
42dc0529c6SBarry Smith
43dc0529c6SBarry Smith    def compareDirLists(self,root, mdirs, dirs):
44dc0529c6SBarry Smith        if NOWARNDIRS.intersection(pathsplit(root)):
45dc0529c6SBarry Smith            return
46dc0529c6SBarry Smith        smdirs = set(mdirs)
47dc0529c6SBarry Smith        sdirs  = set(dirs).difference(AUTODIRS)
48dc0529c6SBarry Smith        if not smdirs.issubset(sdirs):
49cca3327aSJed Brown            self.mistakes.append('%s/makefile contains a directory not on the filesystem: %r' % (root, sorted(smdirs - sdirs)))
50dc0529c6SBarry Smith        if not self.verbose: return
51dc0529c6SBarry Smith        if smdirs != sdirs:
52dc0529c6SBarry Smith            from sys import stderr
53dc0529c6SBarry Smith            stderr.write('Directory mismatch at %s:\n\t%s: %r\n\t%s: %r\n\t%s: %r\n'
54dc0529c6SBarry Smith                         % (root,
55dc0529c6SBarry Smith                            'in makefile   ',sorted(smdirs),
56dc0529c6SBarry Smith                            'on filesystem ',sorted(sdirs),
57dc0529c6SBarry Smith                            'symmetric diff',sorted(smdirs.symmetric_difference(sdirs))))
58dc0529c6SBarry Smith
59dc0529c6SBarry Smith    def compareSourceLists(self, root, msources, files):
60dc0529c6SBarry Smith        if NOWARNDIRS.intersection(pathsplit(root)):
61dc0529c6SBarry Smith            return
62dc0529c6SBarry Smith        smsources = set(msources)
63d23021a0SBarry Smith        ssources  = set(f for f in files if getlangext(f) in ['.c', '.kokkos.cxx','.cxx', '.cc', '.cu', '.cpp', '.F', '.F90', '.hip.cpp', '.sycl.cxx', 'raja.cxx'])
64dc0529c6SBarry Smith        if not smsources.issubset(ssources):
65a943032dSBarry Smith            self.mistakes.append('%s/makefile contains a file not on the filesystem: %r' % (root, sorted(smsources - ssources)))
66dc0529c6SBarry Smith        if not self.verbose: return
67dc0529c6SBarry Smith        if smsources != ssources:
68dc0529c6SBarry Smith            from sys import stderr
69dc0529c6SBarry Smith            stderr.write('Source mismatch at %s:\n\t%s: %r\n\t%s: %r\n\t%s: %r\n'
70dc0529c6SBarry Smith                         % (root,
71dc0529c6SBarry Smith                            'in makefile   ',sorted(smsources),
72dc0529c6SBarry Smith                            'on filesystem ',sorted(ssources),
73dc0529c6SBarry Smith                            'symmetric diff',sorted(smsources.symmetric_difference(ssources))))
74dc0529c6SBarry Smith
75dc0529c6SBarry Smith    def summary(self):
76dc0529c6SBarry Smith        for m in self.mistakes:
77dc0529c6SBarry Smith            self.log.write(m + '\n')
78dc0529c6SBarry Smith        if self.mistakes:
79a943032dSBarry Smith            raise RuntimeError('\n\nThe PETSc makefiles contain mistakes or files are missing on the filesystem.\n%s\nPossible reasons:\n\t1. Files were deleted locally, try "git checkout filename", where "filename" is the missing file.\n\t2. Files were deleted from the repository, but were not removed from the makefile. Send mail to petsc-maint@mcs.anl.gov.\n\t3. Someone forgot to "add" new files to the repository. Send mail to petsc-maint@mcs.anl.gov.\n\n' % ('\n'.join(self.mistakes)))
80dc0529c6SBarry Smith
81dc0529c6SBarry Smithdef stripsplit(line):
82dc0529c6SBarry Smith  return line[len('#requires'):].replace("'","").split()
83dc0529c6SBarry Smith
8447fd361eSStefano ZampiniPetscPKGS = 'sys vec mat dm ksp snes ts tao'.split()
85c0558f20SBarry Smith# the key is actually the language suffix, it won't work for suffixes such as 'kokkos.cxx' so use an _ and replace the _ as needed with .
8659af0bd3SScott KrugerLANGS = dict(kokkos_cxx='KOKKOS', c='C', cxx='CXX', cpp='CPP', cu='CU', F='F',
8750dcbc5aSJunchao Zhang             F90='F90', hip_cpp='HIP', sycl_cxx='SYCL', raja_cxx='RAJA')
880ee81e68SLisandro Dalcin
890ee81e68SLisandro Dalcinclass debuglogger(object):
900ee81e68SLisandro Dalcin    def __init__(self, log):
910ee81e68SLisandro Dalcin        self._log = log
920ee81e68SLisandro Dalcin
930ee81e68SLisandro Dalcin    def write(self, string):
940ee81e68SLisandro Dalcin        self._log.debug(string)
950ee81e68SLisandro Dalcin
960ee81e68SLisandro Dalcinclass Petsc(object):
9747fd361eSStefano Zampini    def __init__(self, petsc_dir=None, petsc_arch=None, pkg_dir=None, pkg_name=None, pkg_arch=None, pkg_pkgs=None, verbose=False):
980ee81e68SLisandro Dalcin        if petsc_dir is None:
990ee81e68SLisandro Dalcin            petsc_dir = os.environ.get('PETSC_DIR')
1000ee81e68SLisandro Dalcin            if petsc_dir is None:
1010ee81e68SLisandro Dalcin                try:
102af0996ceSBarry Smith                    petsc_dir = parse_makefile(os.path.join('lib','petsc','conf', 'petscvariables')).get('PETSC_DIR')
1030ee81e68SLisandro Dalcin                finally:
1040ee81e68SLisandro Dalcin                    if petsc_dir is None:
1050ee81e68SLisandro Dalcin                        raise RuntimeError('Could not determine PETSC_DIR, please set in environment')
1060ee81e68SLisandro Dalcin        if petsc_arch is None:
1070ee81e68SLisandro Dalcin            petsc_arch = os.environ.get('PETSC_ARCH')
1080ee81e68SLisandro Dalcin            if petsc_arch is None:
1090ee81e68SLisandro Dalcin                try:
110af0996ceSBarry Smith                    petsc_arch = parse_makefile(os.path.join(petsc_dir, 'lib','petsc','conf', 'petscvariables')).get('PETSC_ARCH')
1110ee81e68SLisandro Dalcin                finally:
1120ee81e68SLisandro Dalcin                    if petsc_arch is None:
1130ee81e68SLisandro Dalcin                        raise RuntimeError('Could not determine PETSC_ARCH, please set in environment')
114add6df95SStefano Zampini        self.petsc_dir = os.path.normpath(petsc_dir)
115add6df95SStefano Zampini        self.petsc_arch = petsc_arch.rstrip(os.sep)
116add6df95SStefano Zampini        self.pkg_dir = pkg_dir
117add6df95SStefano Zampini        self.pkg_name = pkg_name
118c4bccdb5SStefano Zampini        self.pkg_arch = pkg_arch
119add6df95SStefano Zampini        if self.pkg_dir is None:
120add6df95SStefano Zampini          self.pkg_dir = petsc_dir
121add6df95SStefano Zampini          self.pkg_name = 'petsc'
122c4bccdb5SStefano Zampini          self.pkg_arch = self.petsc_arch
123add6df95SStefano Zampini        if self.pkg_name is None:
124add6df95SStefano Zampini          self.pkg_name = os.path.basename(os.path.normpath(self.pkg_dir))
125c4bccdb5SStefano Zampini        if self.pkg_arch is None:
126c4bccdb5SStefano Zampini          self.pkg_arch = self.petsc_arch
12747fd361eSStefano Zampini        self.pkg_pkgs = PetscPKGS
12847fd361eSStefano Zampini        if pkg_pkgs is not None:
12947fd361eSStefano Zampini          self.pkg_pkgs += list(set(pkg_pkgs.split(','))-set(self.pkg_pkgs))
13047fd361eSStefano Zampini        self.read_conf()
1318e69c5ecSJed Brown        try:
132add6df95SStefano Zampini            logging.basicConfig(filename=self.pkg_arch_path('lib',self.pkg_name,'conf', 'gmake.log'), level=logging.DEBUG)
1338e69c5ecSJed Brown        except IOError:
1348e69c5ecSJed Brown            # Disable logging if path is not writeable (e.g., prefix install)
1358e69c5ecSJed Brown            logging.basicConfig(filename='/dev/null', level=logging.DEBUG)
1360ee81e68SLisandro Dalcin        self.log = logging.getLogger('gmakegen')
1370ee81e68SLisandro Dalcin        self.mistakes = Mistakes(debuglogger(self.log), verbose=verbose)
1380ee81e68SLisandro Dalcin        self.gendeps = []
1390ee81e68SLisandro Dalcin
1400ee81e68SLisandro Dalcin    def arch_path(self, *args):
1410ee81e68SLisandro Dalcin        return os.path.join(self.petsc_dir, self.petsc_arch, *args)
1420ee81e68SLisandro Dalcin
143add6df95SStefano Zampini    def pkg_arch_path(self, *args):
144c4bccdb5SStefano Zampini        return os.path.join(self.pkg_dir, self.pkg_arch, *args)
145add6df95SStefano Zampini
1460ee81e68SLisandro Dalcin    def read_conf(self):
1470ee81e68SLisandro Dalcin        self.conf = dict()
148021a2b48SJed Brown        with open(self.arch_path('include', 'petscconf.h')) as petscconf_h:
149021a2b48SJed Brown            for line in petscconf_h:
1500ee81e68SLisandro Dalcin                if line.startswith('#define '):
1510ee81e68SLisandro Dalcin                    define = line[len('#define '):]
1520ee81e68SLisandro Dalcin                    space = define.find(' ')
1530ee81e68SLisandro Dalcin                    key = define[:space]
1540ee81e68SLisandro Dalcin                    val = define[space+1:]
1550ee81e68SLisandro Dalcin                    self.conf[key] = val
156af0996ceSBarry Smith        self.conf.update(parse_makefile(self.arch_path('lib','petsc','conf', 'petscvariables')))
15747fd361eSStefano Zampini        # allow parsing package additional configurations (if any)
15847fd361eSStefano Zampini        if self.pkg_name != 'petsc' :
15947fd361eSStefano Zampini            f = self.pkg_arch_path('include', self.pkg_name + 'conf.h')
16047fd361eSStefano Zampini            if os.path.isfile(f):
161021a2b48SJed Brown                with open(self.pkg_arch_path('include', self.pkg_name + 'conf.h')) as pkg_conf_h:
162021a2b48SJed Brown                    for line in pkg_conf_h:
16347fd361eSStefano Zampini                        if line.startswith('#define '):
16447fd361eSStefano Zampini                            define = line[len('#define '):]
16547fd361eSStefano Zampini                            space = define.find(' ')
16647fd361eSStefano Zampini                            key = define[:space]
16747fd361eSStefano Zampini                            val = define[space+1:]
16847fd361eSStefano Zampini                            self.conf[key] = val
16947fd361eSStefano Zampini            f = self.pkg_arch_path('lib',self.pkg_name,'conf', self.pkg_name + 'variables')
17047fd361eSStefano Zampini            if os.path.isfile(f):
17147fd361eSStefano Zampini                self.conf.update(parse_makefile(self.pkg_arch_path('lib',self.pkg_name,'conf', self.pkg_name + 'variables')))
1720ee81e68SLisandro Dalcin        self.have_fortran = int(self.conf.get('PETSC_HAVE_FORTRAN', '0'))
1730ee81e68SLisandro Dalcin
1740ee81e68SLisandro Dalcin    def inconf(self, key, val):
1750ee81e68SLisandro Dalcin        if key in ['package', 'function', 'define']:
1760ee81e68SLisandro Dalcin            return self.conf.get(val)
1770ee81e68SLisandro Dalcin        elif key == 'precision':
1780ee81e68SLisandro Dalcin            return val == self.conf['PETSC_PRECISION']
1790ee81e68SLisandro Dalcin        elif key == 'scalar':
1800ee81e68SLisandro Dalcin            return val == self.conf['PETSC_SCALAR']
1810ee81e68SLisandro Dalcin        elif key == 'language':
1820ee81e68SLisandro Dalcin            return val == self.conf['PETSC_LANGUAGE']
1830ee81e68SLisandro Dalcin        raise RuntimeError('Unknown conf check: %s %s' % (key, val))
1840ee81e68SLisandro Dalcin
1850ee81e68SLisandro Dalcin    def relpath(self, root, src):
186add6df95SStefano Zampini        return os.path.relpath(os.path.join(root, src), self.pkg_dir)
1870ee81e68SLisandro Dalcin
1880ee81e68SLisandro Dalcin    def get_sources(self, makevars):
1890ee81e68SLisandro Dalcin        """Return dict {lang: list_of_source_files}"""
1900ee81e68SLisandro Dalcin        source = dict()
1910ee81e68SLisandro Dalcin        for lang, sourcelang in LANGS.items():
192c0558f20SBarry Smith            source[lang] = [f for f in makevars.get('SOURCE'+sourcelang,'').split() if f.endswith(lang.replace('_','.'))]
19359af0bd3SScott Kruger
19459af0bd3SScott Kruger            #source[lang] = [f for f in makevars.get('SOURCE'+sourcelang,'').split() if f.endswith(lang)]
19559af0bd3SScott Kruger#SEK            source[lang] = [f for f in
19659af0bd3SScott Kruger#SEK                            makevars.get('SOURCE'+sourcelang,'').split() if
19759af0bd3SScott Kruger#SEK                            f.endswith(LANGSEXT[lang])]
1980ee81e68SLisandro Dalcin        return source
1990ee81e68SLisandro Dalcin
2000ee81e68SLisandro Dalcin    def gen_pkg(self, pkg):
2010ee81e68SLisandro Dalcin        pkgsrcs = dict()
2020ee81e68SLisandro Dalcin        for lang in LANGS:
2030ee81e68SLisandro Dalcin            pkgsrcs[lang] = []
204add6df95SStefano Zampini        for root, dirs, files in os.walk(os.path.join(self.pkg_dir, 'src', pkg)):
20509a6cbfcSBernhard M. Wiedemann            dirs.sort()
20609a6cbfcSBernhard M. Wiedemann            files.sort()
2070ee81e68SLisandro Dalcin            makefile = os.path.join(root,'makefile')
2080ee81e68SLisandro Dalcin            if not os.path.exists(makefile):
2090ee81e68SLisandro Dalcin                dirs[:] = []
2100ee81e68SLisandro Dalcin                continue
211021a2b48SJed Brown            with open(makefile) as mklines:
2120ee81e68SLisandro Dalcin                conditions = set(tuple(stripsplit(line)) for line in mklines if line.startswith('#requires'))
2130ee81e68SLisandro Dalcin            if not all(self.inconf(key, val) for key, val in conditions):
2140ee81e68SLisandro Dalcin                dirs[:] = []
2150ee81e68SLisandro Dalcin                continue
2160ee81e68SLisandro Dalcin            makevars = parse_makefile(makefile)
2170ee81e68SLisandro Dalcin            mdirs = makevars.get('DIRS','').split() # Directories specified in the makefile
2180ee81e68SLisandro Dalcin            self.mistakes.compareDirLists(root, mdirs, dirs) # diagnostic output to find unused directories
2190ee81e68SLisandro Dalcin            candidates = set(mdirs).union(AUTODIRS).difference(SKIPDIRS)
2200ee81e68SLisandro Dalcin            dirs[:] = list(candidates.intersection(dirs))
2210ee81e68SLisandro Dalcin            allsource = []
2220ee81e68SLisandro Dalcin            def mkrel(src):
2230ee81e68SLisandro Dalcin                return self.relpath(root, src)
2240ee81e68SLisandro Dalcin            source = self.get_sources(makevars)
2250ee81e68SLisandro Dalcin            for lang, s in source.items():
2262b757757SJed Brown                pkgsrcs[lang] += [mkrel(t) for t in s]
2270ee81e68SLisandro Dalcin                allsource += s
2280ee81e68SLisandro Dalcin            self.mistakes.compareSourceLists(root, allsource, files) # Diagnostic output about unused source files
2290ee81e68SLisandro Dalcin            self.gendeps.append(self.relpath(root, 'makefile'))
2300ee81e68SLisandro Dalcin        return pkgsrcs
2310ee81e68SLisandro Dalcin
232b0790570SJed Brown    def gen_gnumake(self, fd):
2330ee81e68SLisandro Dalcin        def write(stem, srcs):
2340ee81e68SLisandro Dalcin            for lang in LANGS:
235c0558f20SBarry Smith                fd.write('%(stem)s.%(lang)s := %(srcs)s\n' % dict(stem=stem, lang=lang.replace('_','.'), srcs=' '.join(srcs[lang])))
23647fd361eSStefano Zampini        for pkg in self.pkg_pkgs:
2370ee81e68SLisandro Dalcin            srcs = self.gen_pkg(pkg)
238b0790570SJed Brown            write('srcs-' + pkg, srcs)
2390ee81e68SLisandro Dalcin        return self.gendeps
2400ee81e68SLisandro Dalcin
2410ee81e68SLisandro Dalcin    def gen_ninja(self, fd):
2420ee81e68SLisandro Dalcin        libobjs = []
24347fd361eSStefano Zampini        for pkg in self.pkg_pkgs:
2440ee81e68SLisandro Dalcin            srcs = self.gen_pkg(pkg)
2450ee81e68SLisandro Dalcin            for lang in LANGS:
2460ee81e68SLisandro Dalcin                for src in srcs[lang]:
2470ee81e68SLisandro Dalcin                    obj = '$objdir/%s.o' % src
248add6df95SStefano Zampini                    fd.write('build %(obj)s : %(lang)s_COMPILE %(src)s\n' % dict(obj=obj, lang=lang.upper(), src=os.path.join(self.pkg_dir,src)))
2490ee81e68SLisandro Dalcin                    libobjs.append(obj)
2500ee81e68SLisandro Dalcin        fd.write('\n')
2510ee81e68SLisandro Dalcin        fd.write('build $libdir/libpetsc.so : %s_LINK_SHARED %s\n\n' % ('CF'[self.have_fortran], ' '.join(libobjs)))
2520ee81e68SLisandro Dalcin        fd.write('build petsc : phony || $libdir/libpetsc.so\n\n')
2530ee81e68SLisandro Dalcin
2540ee81e68SLisandro Dalcin    def summary(self):
2550ee81e68SLisandro Dalcin        self.mistakes.summary()
2560ee81e68SLisandro Dalcin
2570ee81e68SLisandro Dalcindef WriteGnuMake(petsc):
258add6df95SStefano Zampini    arch_files = petsc.pkg_arch_path('lib',petsc.pkg_name,'conf', 'files')
259021a2b48SJed Brown    with open(arch_files, 'w') as fd:
2600ee81e68SLisandro Dalcin        gendeps = petsc.gen_gnumake(fd)
2610ee81e68SLisandro Dalcin        fd.write('\n')
2620ee81e68SLisandro Dalcin        fd.write('# Dependency to regenerate this file\n')
263add6df95SStefano Zampini        fd.write('%s : %s %s\n' % (os.path.relpath(arch_files, petsc.pkg_dir),
264add6df95SStefano Zampini                                   os.path.relpath(__file__, os.path.realpath(petsc.pkg_dir)),
2650ee81e68SLisandro Dalcin                                   ' '.join(gendeps)))
2660ee81e68SLisandro Dalcin        fd.write('\n')
2670ee81e68SLisandro Dalcin        fd.write('# Dummy dependencies in case makefiles are removed\n')
2680ee81e68SLisandro Dalcin        fd.write(''.join([dep + ':\n' for dep in gendeps]))
2690ee81e68SLisandro Dalcin
2700ee81e68SLisandro Dalcindef WriteNinja(petsc):
2710ee81e68SLisandro Dalcin    conf = dict()
272af0996ceSBarry Smith    parse_makefile(os.path.join(petsc.petsc_dir, 'lib', 'petsc','conf', 'variables'), conf)
273af0996ceSBarry Smith    parse_makefile(petsc.arch_path('lib','petsc','conf', 'petscvariables'), conf)
2740ee81e68SLisandro Dalcin    build_ninja = petsc.arch_path('build.ninja')
275021a2b48SJed Brown    with open(build_ninja, 'w') as fd:
2760ee81e68SLisandro Dalcin        fd.write('objdir = obj-ninja\n')
2770ee81e68SLisandro Dalcin        fd.write('libdir = lib\n')
2780ee81e68SLisandro Dalcin        fd.write('c_compile = %(PCC)s\n' % conf)
2790ee81e68SLisandro Dalcin        fd.write('c_flags = %(PETSC_CC_INCLUDES)s %(PCC_FLAGS)s %(CCPPFLAGS)s\n' % conf)
2800ee81e68SLisandro Dalcin        fd.write('c_link = %(PCC_LINKER)s\n' % conf)
2810ee81e68SLisandro Dalcin        fd.write('c_link_flags = %(PCC_LINKER_FLAGS)s\n' % conf)
2820ee81e68SLisandro Dalcin        if petsc.have_fortran:
2830ee81e68SLisandro Dalcin            fd.write('f_compile = %(FC)s\n' % conf)
2840ee81e68SLisandro Dalcin            fd.write('f_flags = %(PETSC_FC_INCLUDES)s %(FC_FLAGS)s %(FCPPFLAGS)s\n' % conf)
2850ee81e68SLisandro Dalcin            fd.write('f_link = %(FC_LINKER)s\n' % conf)
2860ee81e68SLisandro Dalcin            fd.write('f_link_flags = %(FC_LINKER_FLAGS)s\n' % conf)
2870ee81e68SLisandro Dalcin        fd.write('petsc_external_lib = %(PETSC_EXTERNAL_LIB_BASIC)s\n' % conf)
2880ee81e68SLisandro Dalcin        fd.write('python = %(PYTHON)s\n' % conf)
2890ee81e68SLisandro Dalcin        fd.write('\n')
2900ee81e68SLisandro Dalcin        fd.write('rule C_COMPILE\n'
2910ee81e68SLisandro Dalcin                 '  command = $c_compile -MMD -MF $out.d $c_flags -c $in -o $out\n'
2920ee81e68SLisandro Dalcin                 '  description = CC $out\n'
2930ee81e68SLisandro Dalcin                 '  depfile = $out.d\n'
2940ee81e68SLisandro Dalcin                 # '  deps = gcc\n') # 'gcc' is default, 'msvc' only recognized by newer versions of ninja
2950ee81e68SLisandro Dalcin                 '\n')
2960ee81e68SLisandro Dalcin        fd.write('rule C_LINK_SHARED\n'
2970ee81e68SLisandro Dalcin                 '  command = $c_link $c_link_flags -shared -o $out $in $petsc_external_lib\n'
2980ee81e68SLisandro Dalcin                 '  description = CLINK_SHARED $out\n'
2990ee81e68SLisandro Dalcin                 '\n')
3000ee81e68SLisandro Dalcin        if petsc.have_fortran:
3010ee81e68SLisandro Dalcin            fd.write('rule F_COMPILE\n'
3020ee81e68SLisandro Dalcin                     '  command = $f_compile -MMD -MF $out.d $f_flags -c $in -o $out\n'
3030ee81e68SLisandro Dalcin                     '  description = FC $out\n'
3040ee81e68SLisandro Dalcin                     '  depfile = $out.d\n'
3050ee81e68SLisandro Dalcin                     '\n')
3060ee81e68SLisandro Dalcin            fd.write('rule F_LINK_SHARED\n'
3070ee81e68SLisandro Dalcin                     '  command = $f_link $f_link_flags -shared -o $out $in $petsc_external_lib\n'
3080ee81e68SLisandro Dalcin                     '  description = FLINK_SHARED $out\n'
3090ee81e68SLisandro Dalcin                     '\n')
3100ee81e68SLisandro Dalcin        fd.write('rule GEN_NINJA\n'
3110ee81e68SLisandro Dalcin                 '  command = $python $in --output=ninja\n'
3120ee81e68SLisandro Dalcin                 '  generator = 1\n'
3130ee81e68SLisandro Dalcin                 '\n')
3140ee81e68SLisandro Dalcin        petsc.gen_ninja(fd)
3150ee81e68SLisandro Dalcin        fd.write('\n')
3160ee81e68SLisandro Dalcin        fd.write('build %s : GEN_NINJA | %s %s %s %s\n' % (build_ninja,
3170ee81e68SLisandro Dalcin                                                           os.path.abspath(__file__),
318af0996ceSBarry Smith                                                           os.path.join(petsc.petsc_dir, 'lib','petsc','conf', 'variables'),
319af0996ceSBarry Smith                                                           petsc.arch_path('lib','petsc','conf', 'petscvariables'),
320add6df95SStefano Zampini                                                       ' '.join(os.path.join(petsc.pkg_dir, dep) for dep in petsc.gendeps)))
3210ee81e68SLisandro Dalcin
32247fd361eSStefano Zampinidef main(petsc_dir=None, petsc_arch=None, pkg_dir=None, pkg_name=None, pkg_arch=None, pkg_pkgs=None, output=None, verbose=False):
3230ee81e68SLisandro Dalcin    if output is None:
3240ee81e68SLisandro Dalcin        output = 'gnumake'
3250ee81e68SLisandro Dalcin    writer = dict(gnumake=WriteGnuMake, ninja=WriteNinja)
32647fd361eSStefano Zampini    petsc = Petsc(petsc_dir=petsc_dir, petsc_arch=petsc_arch, pkg_dir=pkg_dir, pkg_name=pkg_name, pkg_arch=pkg_arch, pkg_pkgs=pkg_pkgs, verbose=verbose)
3270ee81e68SLisandro Dalcin    writer[output](petsc)
3280ee81e68SLisandro Dalcin    petsc.summary()
3290ee81e68SLisandro Dalcin
3300ee81e68SLisandro Dalcinif __name__ == '__main__':
3310ee81e68SLisandro Dalcin    import optparse
3320ee81e68SLisandro Dalcin    parser = optparse.OptionParser()
3330ee81e68SLisandro Dalcin    parser.add_option('--verbose', help='Show mismatches between makefiles and the filesystem', action='store_true', default=False)
3340ee81e68SLisandro Dalcin    parser.add_option('--petsc-arch', help='Set PETSC_ARCH different from environment', default=os.environ.get('PETSC_ARCH'))
335add6df95SStefano Zampini    parser.add_option('--pkg-dir', help='Set the directory of the package (different from PETSc) you want to generate the makefile rules for', default=None)
336add6df95SStefano Zampini    parser.add_option('--pkg-name', help='Set the name of the package you want to generate the makefile rules for', default=None)
337c4bccdb5SStefano Zampini    parser.add_option('--pkg-arch', help='Set the package arch name you want to generate the makefile rules for', default=None)
33847fd361eSStefano Zampini    parser.add_option('--pkg-pkgs', help='Set the package folders (comma separated list, different from the usual sys,vec,mat etc) you want to generate the makefile rules for', default=None)
3390ee81e68SLisandro Dalcin    parser.add_option('--output', help='Location to write output file', default=None)
3400ee81e68SLisandro Dalcin    opts, extra_args = parser.parse_args()
3410ee81e68SLisandro Dalcin    if extra_args:
3420ee81e68SLisandro Dalcin        import sys
3430ee81e68SLisandro Dalcin        sys.stderr.write('Unknown arguments: %s\n' % ' '.join(extra_args))
3440ee81e68SLisandro Dalcin        exit(1)
34547fd361eSStefano Zampini    main(petsc_arch=opts.petsc_arch, pkg_dir=opts.pkg_dir, pkg_name=opts.pkg_name, pkg_arch=opts.pkg_arch, pkg_pkgs=opts.pkg_pkgs, output=opts.output, verbose=opts.verbose)
346