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