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