xref: /petsc/systems/Apple/iOS/bin/iosbuilder.py (revision 607e733f3db3ee7f6f605a13295c517df8dbb9c9)
1#!/usr/bin/env python3
2#
3#   Builds a iOS Framework of PETSc
4#
5#   export PETSC_ARCH=arch-ios-simulator
6#
7#   ./systems/Apple/iOS/bin/arch-ios-simulator.py [use --with-debugging=0 to get iPhone/iPad version, otherwise creates simulator version]
8#      this sets up the appropriate configuration file
9#
10#   ./systems/Apple/iOS/bin/iosbuilder.py
11#      this creates the PETSc iPhone/iPad library
12#      this will open Xcode and give you directions to follow
13#
14#   open systems/Apple/iOS/examples/examples.xcodeproj
15#       Project -> Edit Project Setting  -> Configuration (make sure it is Release or Debug depending on if you used --with-debugging=0)
16#       Build -> Build and Debug
17#
18from __future__ import print_function
19import os, sys
20
21sys.path.insert(0, os.path.join(os.environ['PETSC_DIR'], 'config'))
22sys.path.insert(0, os.path.join(os.environ['PETSC_DIR'], 'config', 'BuildSystem'))
23
24import script
25
26class PETScMaker(script.Script):
27 def __init__(self):
28   import RDict
29   import os
30
31   argDB = RDict.RDict(None, None, 0, 0, readonly = True)
32   argDB.saveFilename = os.path.join(os.environ['PETSC_DIR'], os.environ['PETSC_ARCH'], 'lib','petsc','conf', 'RDict.db')
33   argDB.load()
34   script.Script.__init__(self, argDB = argDB)
35   self.log = sys.stdout
36   return
37
38 def setupModules(self):
39   self.mpi           = self.framework.require('config.packages.MPI',         None)
40   self.base          = self.framework.require('config.base',                 None)
41   self.setCompilers  = self.framework.require('config.setCompilers',         None)
42   self.arch          = self.framework.require('PETSc.options.arch',        None)
43   self.petscdir      = self.framework.require('PETSc.options.petscdir',    None)
44   self.languages     = self.framework.require('PETSc.options.languages',   None)
45   self.debugging     = self.framework.require('PETSc.options.debugging',   None)
46   self.opengles      = self.framework.require('config.packages.opengles',     None)
47   self.make          = self.framework.require('config.programs',             None)
48   self.compilers     = self.framework.require('config.compilers',            None)
49   self.types         = self.framework.require('config.types',                None)
50   self.headers       = self.framework.require('config.headers',              None)
51   self.functions     = self.framework.require('config.functions',            None)
52   self.libraries     = self.framework.require('config.libraries',            None)
53   self.scalarType    = self.framework.require('PETSc.options.scalarTypes', None)
54   self.memAlign      = self.framework.require('PETSc.options.memAlign',    None)
55   self.libraryOptions= self.framework.require('PETSc.options.libraryOptions', None)
56   self.compilerFlags = self.framework.require('config.compilerFlags', self)
57   return
58
59 def setupHelp(self, help):
60   import nargs
61
62   help = script.Script.setupHelp(self, help)
63   help.addArgument('RepManager', '-rootDir', nargs.ArgDir(None, os.environ['PETSC_DIR'], 'The root directory for this build', isTemporary = 1))
64   help.addArgument('RepManager', '-dryRun',  nargs.ArgBool(None, False, 'Only output what would be run', isTemporary = 1))
65   help.addArgument('RepManager', '-skipXCode',  nargs.ArgBool(None, False, 'Do not run XCode application/files have not been added/removed', isTemporary = 1))
66   help.addArgument('RepManager', '-verbose', nargs.ArgInt(None, 0, 'The verbosity level', min = 0, isTemporary = 1))
67   return help
68
69 def setup(self):
70   script.Script.setup(self)
71   self.framework = self.loadConfigure()
72   self.setupModules()
73   return
74
75 @property
76 def verbose(self):
77   '''The verbosity level'''
78   return self.argDB['verbose']
79
80 @property
81 def skipXCode(self):
82   '''Skip XCode application'''
83   return self.argDB['skipXCode']
84
85 @property
86 def dryRun(self):
87   '''Flag for only output of what would be run'''
88   return self.argDB['dryRun']
89
90 def getPackageInfo(self):
91   packageIncludes = []
92   packageLibs     = []
93   for p in self.framework.packages:
94     # Could put on compile line, self.addDefine('HAVE_'+i.PACKAGE, 1)
95     if hasattr(p, 'lib'):
96       if not isinstance(p.lib, list):
97         packageLibs.append(p.lib)
98       else:
99         packageLibs.extend(p.lib)
100     if hasattr(p, 'include'):
101       if not isinstance(p.include, list):
102         packageIncludes.append(p.include)
103       else:
104         packageIncludes.extend(p.include)
105   packageLibs     = self.libraries.toStringNoDupes(packageLibs+self.libraries.math)
106   packageIncludes = self.headers.toStringNoDupes(packageIncludes)
107   return packageIncludes, packageLibs
108
109 def buildDir(self, dirname):
110   ''' This is run in a PETSc source directory'''
111   if self.verbose: print('Entering '+dirname)
112   os.chdir(dirname)
113   l = len(os.environ['PETSC_DIR'])
114   basedir = os.path.join(os.environ['PETSC_DIR'],os.environ['PETSC_ARCH'],'xcode-links')
115   #newdirname = os.path.join(basedir,dirname[l+1:])
116   #os.mkdir(newdirname)
117
118
119   # Get list of source files in the directory
120   cnames = []
121   onames = []
122   fnames = []
123   hnames = []
124   for f in os.listdir(dirname):
125     ext = os.path.splitext(f)[1]
126     if ext == '.c':
127       cnames.append(f)
128       onames.append(f.replace('.c', '.o'))
129     if ext == '.h':
130       hnames.append(f)
131   if cnames:
132     if self.verbose: print('Linking C files',cnames)
133     for i in cnames:
134       j = i[l+1:]
135       if not os.path.islink(os.path.join(basedir,i)) and not i.startswith('.') and i.find(".BACKUP") == -1 and i.find(".LOCAL") == -1 and i.find(".BASE") == -1:
136         if i.endswith('openglops.c') and not os.path.islink(os.path.join(basedir,'openglops.m')):
137           os.symlink(os.path.join(dirname,i),os.path.join(basedir,'openglops.m'))
138         else:
139           os.symlink(os.path.join(dirname,i),os.path.join(basedir,i))
140   # do not need to link these because xcode project points to original source code directory
141   #if hnames:
142   #  if self.verbose: print 'Linking h files',hnames
143   #  for i in hnames:
144   #    if not os.path.islink(os.path.join(basedir,i)):
145   #      os.symlink(os.path.join(dirname,i),os.path.join(basedir,i))
146   return
147
148 def checkDir(self, dirname):
149   '''Checks whether we should recurse into this directory
150   - Excludes projects directory
151   - Excludes examples directory
152   - Excludes contrib directory
153   - Excludes tutorials directory
154   - Excludes benchmarks directory
155   - Checks makefile to see if compiler is allowed to visit this directory for this configuration'''
156#   print self.functions.functions
157#   print self.base.defines
158   base = os.path.basename(dirname)
159
160   if base == 'examples': return False
161   if base == 'projects': return False
162   if base.startswith('ftn-'): return False
163   if base == 'contrib':  return False
164   if base == 'tutorials':  return False
165   if base == 'benchmarks':  return False
166   if base == 'systems':  return False
167# for some reason agrmes is in the repository but not used!
168   if base == 'agmres':  return False
169   if base == 'test-dir':  return False
170   if base.startswith('arch-'):  return False
171
172   import re
173   reg   = re.compile(' [ ]*')
174   fname = os.path.join(dirname, 'makefile')
175   if not os.path.isfile(fname):
176     if os.path.isfile(os.path.join(dirname, 'Makefile')): print('ERROR: Change Makefile to makefile in',dirname)
177     return False
178   fd = open(fname)
179   text = fd.readline()
180   while text:
181     if text.startswith('#requires'):
182       text = text[9:-1].strip()
183       text = reg.sub(' ',text)
184       rtype = text.split(' ')[0]
185       rvalue = text.split(' ')[1]
186
187       if rvalue == "'"+'PETSC_USE_FORTRAN'+"'":
188         if not hasattr(self.compilers, 'FC'):
189           if self.verbose: print('Rejecting',dirname,'because fortran is not being used')
190           return 0
191       elif rvalue == "'"+'PETSC_USE_LOG'+"'":
192         if not self.libraryOptions.useLog:
193           if self.verbose: print('Rejecting',dirname,'because logging is turned off')
194           return 0
195       elif rvalue == "'"+'PETSC_USE_FORTRAN_KERNELS'+"'":
196         if not self.libraryOptions.useFortranKernels:
197           if self.verbose: print('Rejecting',dirname,'because fortran kernels are turned off')
198           return 0
199       elif rtype == 'scalar' and not self.scalarType.scalartype == rvalue:
200         if self.verbose: print('Rejecting',dirname,'because scalar type '+self.scalarType.scalartype+' is not '+rvalue)
201         return 0
202       elif rtype == 'language':
203         if rvalue == 'CXXONLY' and self.languages.clanguage == 'C':
204           if self.verbose: print('Rejecting',dirname,'because language is '+self.languages.clanguage+' is not C++')
205           return 0
206       elif rtype == 'precision' and not rvalue == self.scalarType.precision:
207         if self.verbose: print('Rejecting',dirname,'because precision '+self.scalarType.precision+' is not '+rvalue)
208         return 0
209       elif rtype == 'package':
210         found = 0
211         if self.mpi.usingMPIUni:
212           pname = 'PETSC_HAVE_MPIUNI'
213           pname = "'"+pname+"'"
214           if pname == rvalue: found = 1
215         for i in self.framework.packages:
216           pname = 'PETSC_HAVE_'+i.PACKAGE
217           pname = "'"+pname+"'"
218           if pname == rvalue: found = 1
219         if not found:
220           for i in self.base.defines:
221             pname = 'PETSC_'+i.upper()
222             pname = "'"+pname+"'"
223             if pname == rvalue: found = 1
224           for i in self.opengles.defines:
225             pname = 'PETSC_'+i.upper()
226             pname = "'"+pname+"'"
227             if pname == rvalue: found = 1
228           if not found:
229             if self.verbose: print('Rejecting',dirname,'because package '+rvalue+' does not exist')
230             return 0
231       elif rtype == 'define':
232         found = 0
233         for i in self.base.defines:
234           pname = 'PETSC_'+i.upper()
235           pname = "'"+pname+"'"
236           if pname == rvalue: found = 1
237         if not found:
238           if self.verbose: print('Rejecting',dirname,'because define '+rvalue+' does not exist')
239           return 0
240       elif rtype == 'function':
241         found = 0
242         for i in self.functions.functions:
243           pname = 'PETSC_HAVE_'+i.upper()
244           pname = "'"+pname+"'"
245#           print pname
246#           print rvalue
247           if pname == rvalue: found = 1
248         if not found:
249           if self.verbose: print('Rejecting',dirname,'because function '+rvalue+' does not exist')
250           return 0
251
252     text = fd.readline()
253   fd.close()
254   return True
255
256 def buildAll(self, rootDir = None):
257   import shutil
258   self.setup()
259   if rootDir is None:
260     rootDir = self.argDB['rootDir']
261   if not self.checkDir(rootDir):
262     print('Nothing to be done')
263   if rootDir == os.environ['PETSC_DIR']:
264     basedir = os.path.join(self.petscdir.dir, self.arch.arch, 'xcode-links')
265     if os.path.isdir(basedir):
266       if self.verbose: print('Removing '+basedir)
267       shutil.rmtree(basedir)
268   os.mkdir(basedir)
269   for root, dirs, files in os.walk(rootDir):
270     self.buildDir(root)
271     for badDir in [d for d in dirs if not self.checkDir(os.path.join(root, d))]:
272       dirs.remove(badDir)
273
274   if not self.skipXCode:
275
276     print('In Xcode mouse click on Other Sources then xcode-links and the delete key, then')
277     print('control mouse click on "Other Sources" and select "Add files to PETSc ...", then')
278     print('in the finder window locate ${PETSC_DIR}/arch-ios/xcode-links and select it. Now')
279     print('exit Xcode')
280
281     try:
282       import subprocess
283       subprocess.call('cd '+os.path.join(os.environ['PETSC_DIR'],'systems','Apple','iOS','PETSc')+';open -W PETSc.xcodeproj', shell=True)
284     except RuntimeError as e:
285       raise RuntimeError('Error opening xcode project '+str(e))
286
287
288   sdk         = ' -sdk iphonesimulator '
289   destination = 'iphonesimulator'
290   debug       = 'Debug'
291   debugdir    = 'Debug-'+destination
292   if not self.compilerFlags.debugging:
293     debug = 'Release'
294     debugdir = 'Release-'+destination
295   try:
296     output,err,ret  = self.executeShellCommand('cd '+os.path.join(os.environ['PETSC_DIR'],'systems','Apple','iOS','PETSc')+';xcodebuild -arch x86_64 -configuration '+debug+sdk, timeout=3000, log = self.log)
297   except RuntimeError as e:
298     raise RuntimeError('Error making iPhone/iPad version of PETSc libraries: '+str(e))
299
300   liblocation = os.path.join(os.environ['PETSC_DIR'],'systems','Apple','iOS','PETSc','build','Debug-iphonesimulator','PETSc.framework','PETSc')
301   if not os.path.exists(liblocation):
302     raise RuntimeError('Error library '+liblocation+' not created')
303   try:
304     output,err,ret  = self.executeShellCommand('cp -f '+liblocation+' '+os.path.join(os.environ['PETSC_DIR'],os.environ['PETSC_ARCH'],'lib','PETSc_framework'), timeout=30, log = self.log)
305   except RuntimeError as e:
306     raise RuntimeError('Error copying iPhone/iPad version of PETSc libraries: '+str(e))
307
308   return
309
310def noCheckCommand(command, status, output, error):
311  ''' Do no check result'''
312  return
313  noCheckCommand = staticmethod(noCheckCommand)
314
315if __name__ == '__main__':
316  PETScMaker().buildAll()
317