xref: /petsc/systems/Apple/iOS/bin/iosbuilder.py (revision 9b88ac225e01f016352a5f4cd90e158abe5f5675)
11b37a2a7SPierre Jolivet#!/usr/bin/env python3
2525d6f2eSBarry Smith#
3f66eea08SBarry Smith#   Builds a iOS Framework of PETSc
4525d6f2eSBarry Smith#
5f66eea08SBarry Smith#   export PETSC_ARCH=arch-ios-simulator
6a95d84e0SBarry Smith#
7e611a964SBarry Smith#   ./systems/Apple/iOS/bin/arch-ios-simulator.py [use --with-debugging=0 to get iPhone/iPad version, otherwise creates simulator version]
8525d6f2eSBarry Smith#      this sets up the appropriate configuration file
9525d6f2eSBarry Smith#
10525d6f2eSBarry Smith#   ./systems/Apple/iOS/bin/iosbuilder.py
11525d6f2eSBarry Smith#      this creates the PETSc iPhone/iPad library
12525d6f2eSBarry Smith#      this will open Xcode and give you directions to follow
13525d6f2eSBarry Smith#
14a95d84e0SBarry Smith#   open systems/Apple/iOS/examples/examples.xcodeproj
15525d6f2eSBarry Smith#       Project -> Edit Project Setting  -> Configuration (make sure it is Release or Debug depending on if you used --with-debugging=0)
16525d6f2eSBarry Smith#       Build -> Build and Debug
17525d6f2eSBarry Smith#
185b6bfdb9SJed Brownfrom __future__ import print_function
19525d6f2eSBarry Smithimport os, sys
20525d6f2eSBarry Smith
21525d6f2eSBarry Smithsys.path.insert(0, os.path.join(os.environ['PETSC_DIR'], 'config'))
22525d6f2eSBarry Smithsys.path.insert(0, os.path.join(os.environ['PETSC_DIR'], 'config', 'BuildSystem'))
23525d6f2eSBarry Smith
24525d6f2eSBarry Smithimport script
25525d6f2eSBarry Smith
26525d6f2eSBarry Smithclass PETScMaker(script.Script):
27525d6f2eSBarry Smith def __init__(self):
28525d6f2eSBarry Smith   import RDict
29525d6f2eSBarry Smith   import os
30525d6f2eSBarry Smith
31525d6f2eSBarry Smith   argDB = RDict.RDict(None, None, 0, 0, readonly = True)
32575a0592SBarry Smith   argDB.saveFilename = os.path.join(os.environ['PETSC_DIR'], os.environ['PETSC_ARCH'], 'lib','petsc','conf', 'RDict.db')
33525d6f2eSBarry Smith   argDB.load()
34525d6f2eSBarry Smith   script.Script.__init__(self, argDB = argDB)
35525d6f2eSBarry Smith   self.log = sys.stdout
36525d6f2eSBarry Smith   return
37525d6f2eSBarry Smith
38525d6f2eSBarry Smith def setupModules(self):
39525d6f2eSBarry Smith   self.mpi           = self.framework.require('config.packages.MPI',         None)
40525d6f2eSBarry Smith   self.base          = self.framework.require('config.base',                 None)
41525d6f2eSBarry Smith   self.setCompilers  = self.framework.require('config.setCompilers',         None)
4244b85a23SBarry Smith   self.arch          = self.framework.require('PETSc.options.arch',        None)
4344b85a23SBarry Smith   self.petscdir      = self.framework.require('PETSc.options.petscdir',    None)
4444b85a23SBarry Smith   self.languages     = self.framework.require('PETSc.options.languages',   None)
4544b85a23SBarry Smith   self.debugging     = self.framework.require('PETSc.options.debugging',   None)
4644b85a23SBarry Smith   self.opengles      = self.framework.require('config.packages.opengles',     None)
47525d6f2eSBarry Smith   self.make          = self.framework.require('config.programs',             None)
48525d6f2eSBarry Smith   self.compilers     = self.framework.require('config.compilers',            None)
49525d6f2eSBarry Smith   self.types         = self.framework.require('config.types',                None)
50525d6f2eSBarry Smith   self.headers       = self.framework.require('config.headers',              None)
51525d6f2eSBarry Smith   self.functions     = self.framework.require('config.functions',            None)
52525d6f2eSBarry Smith   self.libraries     = self.framework.require('config.libraries',            None)
5344b85a23SBarry Smith   self.scalarType    = self.framework.require('PETSc.options.scalarTypes', None)
5444b85a23SBarry Smith   self.memAlign      = self.framework.require('PETSc.options.memAlign',    None)
5544b85a23SBarry Smith   self.libraryOptions= self.framework.require('PETSc.options.libraryOptions', None)
56525d6f2eSBarry Smith   self.compilerFlags = self.framework.require('config.compilerFlags', self)
57525d6f2eSBarry Smith   return
58525d6f2eSBarry Smith
59525d6f2eSBarry Smith def setupHelp(self, help):
60525d6f2eSBarry Smith   import nargs
61525d6f2eSBarry Smith
62525d6f2eSBarry Smith   help = script.Script.setupHelp(self, help)
63525d6f2eSBarry Smith   help.addArgument('RepManager', '-rootDir', nargs.ArgDir(None, os.environ['PETSC_DIR'], 'The root directory for this build', isTemporary = 1))
64525d6f2eSBarry Smith   help.addArgument('RepManager', '-dryRun',  nargs.ArgBool(None, False, 'Only output what would be run', isTemporary = 1))
6533be3048SBarry Smith   help.addArgument('RepManager', '-skipXCode',  nargs.ArgBool(None, False, 'Do not run XCode application/files have not been added/removed', isTemporary = 1))
66525d6f2eSBarry Smith   help.addArgument('RepManager', '-verbose', nargs.ArgInt(None, 0, 'The verbosity level', min = 0, isTemporary = 1))
67525d6f2eSBarry Smith   return help
68525d6f2eSBarry Smith
69525d6f2eSBarry Smith def setup(self):
70525d6f2eSBarry Smith   script.Script.setup(self)
71525d6f2eSBarry Smith   self.framework = self.loadConfigure()
72525d6f2eSBarry Smith   self.setupModules()
73525d6f2eSBarry Smith   return
74525d6f2eSBarry Smith
75525d6f2eSBarry Smith @property
76525d6f2eSBarry Smith def verbose(self):
77525d6f2eSBarry Smith   '''The verbosity level'''
78525d6f2eSBarry Smith   return self.argDB['verbose']
79525d6f2eSBarry Smith
80525d6f2eSBarry Smith @property
8133be3048SBarry Smith def skipXCode(self):
8233be3048SBarry Smith   '''Skip XCode application'''
8333be3048SBarry Smith   return self.argDB['skipXCode']
8433be3048SBarry Smith
8533be3048SBarry Smith @property
86525d6f2eSBarry Smith def dryRun(self):
87525d6f2eSBarry Smith   '''Flag for only output of what would be run'''
88525d6f2eSBarry Smith   return self.argDB['dryRun']
89525d6f2eSBarry Smith
90525d6f2eSBarry Smith def getPackageInfo(self):
91525d6f2eSBarry Smith   packageIncludes = []
92525d6f2eSBarry Smith   packageLibs     = []
93525d6f2eSBarry Smith   for p in self.framework.packages:
94525d6f2eSBarry Smith     # Could put on compile line, self.addDefine('HAVE_'+i.PACKAGE, 1)
95525d6f2eSBarry Smith     if hasattr(p, 'lib'):
96525d6f2eSBarry Smith       if not isinstance(p.lib, list):
97525d6f2eSBarry Smith         packageLibs.append(p.lib)
98525d6f2eSBarry Smith       else:
99525d6f2eSBarry Smith         packageLibs.extend(p.lib)
100525d6f2eSBarry Smith     if hasattr(p, 'include'):
101525d6f2eSBarry Smith       if not isinstance(p.include, list):
102525d6f2eSBarry Smith         packageIncludes.append(p.include)
103525d6f2eSBarry Smith       else:
104525d6f2eSBarry Smith         packageIncludes.extend(p.include)
105525d6f2eSBarry Smith   packageLibs     = self.libraries.toStringNoDupes(packageLibs+self.libraries.math)
106525d6f2eSBarry Smith   packageIncludes = self.headers.toStringNoDupes(packageIncludes)
107525d6f2eSBarry Smith   return packageIncludes, packageLibs
108525d6f2eSBarry Smith
109525d6f2eSBarry Smith def buildDir(self, dirname):
110525d6f2eSBarry Smith   ''' This is run in a PETSc source directory'''
1115b6bfdb9SJed Brown   if self.verbose: print('Entering '+dirname)
112525d6f2eSBarry Smith   os.chdir(dirname)
113525d6f2eSBarry Smith   l = len(os.environ['PETSC_DIR'])
114525d6f2eSBarry Smith   basedir = os.path.join(os.environ['PETSC_DIR'],os.environ['PETSC_ARCH'],'xcode-links')
115525d6f2eSBarry Smith   #newdirname = os.path.join(basedir,dirname[l+1:])
116525d6f2eSBarry Smith   #os.mkdir(newdirname)
117525d6f2eSBarry Smith
118525d6f2eSBarry Smith
119525d6f2eSBarry Smith   # Get list of source files in the directory
120525d6f2eSBarry Smith   cnames = []
121525d6f2eSBarry Smith   onames = []
122525d6f2eSBarry Smith   fnames = []
123525d6f2eSBarry Smith   hnames = []
124525d6f2eSBarry Smith   for f in os.listdir(dirname):
125525d6f2eSBarry Smith     ext = os.path.splitext(f)[1]
126525d6f2eSBarry Smith     if ext == '.c':
127525d6f2eSBarry Smith       cnames.append(f)
128525d6f2eSBarry Smith       onames.append(f.replace('.c', '.o'))
129525d6f2eSBarry Smith     if ext == '.h':
130525d6f2eSBarry Smith       hnames.append(f)
131525d6f2eSBarry Smith   if cnames:
1325b6bfdb9SJed Brown     if self.verbose: print('Linking C files',cnames)
133525d6f2eSBarry Smith     for i in cnames:
134525d6f2eSBarry Smith       j = i[l+1:]
135a95d84e0SBarry Smith       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:
13633be3048SBarry Smith         if i.endswith('openglops.c') and not os.path.islink(os.path.join(basedir,'openglops.m')):
137f72c6c23SBarry Smith           os.symlink(os.path.join(dirname,i),os.path.join(basedir,'openglops.m'))
138f72c6c23SBarry Smith         else:
139525d6f2eSBarry Smith           os.symlink(os.path.join(dirname,i),os.path.join(basedir,i))
140525d6f2eSBarry Smith   # do not need to link these because xcode project points to original source code directory
141525d6f2eSBarry Smith   #if hnames:
142525d6f2eSBarry Smith   #  if self.verbose: print 'Linking h files',hnames
143525d6f2eSBarry Smith   #  for i in hnames:
144525d6f2eSBarry Smith   #    if not os.path.islink(os.path.join(basedir,i)):
145525d6f2eSBarry Smith   #      os.symlink(os.path.join(dirname,i),os.path.join(basedir,i))
146525d6f2eSBarry Smith   return
147525d6f2eSBarry Smith
148525d6f2eSBarry Smith def checkDir(self, dirname):
149525d6f2eSBarry Smith   '''Checks whether we should recurse into this directory
150525d6f2eSBarry Smith   - Excludes projects directory
151525d6f2eSBarry Smith   - Excludes examples directory
152525d6f2eSBarry Smith   - Excludes contrib directory
153525d6f2eSBarry Smith   - Excludes tutorials directory
154525d6f2eSBarry Smith   - Excludes benchmarks directory
155525d6f2eSBarry Smith   - Checks makefile to see if compiler is allowed to visit this directory for this configuration'''
156525d6f2eSBarry Smith#   print self.functions.functions
157525d6f2eSBarry Smith#   print self.base.defines
158525d6f2eSBarry Smith   base = os.path.basename(dirname)
159525d6f2eSBarry Smith
160525d6f2eSBarry Smith   if base == 'examples': return False
161525d6f2eSBarry Smith   if base == 'projects': return False
1626dd63270SBarry Smith   if base.startswith('ftn-'): return False
163525d6f2eSBarry Smith   if base == 'contrib':  return False
164525d6f2eSBarry Smith   if base == 'tutorials':  return False
165525d6f2eSBarry Smith   if base == 'benchmarks':  return False
166f72c6c23SBarry Smith   if base == 'systems':  return False
167a95d84e0SBarry Smith# for some reason agrmes is in the repository but not used!
168a95d84e0SBarry Smith   if base == 'agmres':  return False
169a95d84e0SBarry Smith   if base == 'test-dir':  return False
170525d6f2eSBarry Smith   if base.startswith('arch-'):  return False
171525d6f2eSBarry Smith
172525d6f2eSBarry Smith   import re
173525d6f2eSBarry Smith   reg   = re.compile(' [ ]*')
174525d6f2eSBarry Smith   fname = os.path.join(dirname, 'makefile')
175525d6f2eSBarry Smith   if not os.path.isfile(fname):
1765b6bfdb9SJed Brown     if os.path.isfile(os.path.join(dirname, 'Makefile')): print('ERROR: Change Makefile to makefile in',dirname)
177525d6f2eSBarry Smith     return False
178525d6f2eSBarry Smith   fd = open(fname)
179525d6f2eSBarry Smith   text = fd.readline()
180525d6f2eSBarry Smith   while text:
181525d6f2eSBarry Smith     if text.startswith('#requires'):
182525d6f2eSBarry Smith       text = text[9:-1].strip()
183525d6f2eSBarry Smith       text = reg.sub(' ',text)
184525d6f2eSBarry Smith       rtype = text.split(' ')[0]
185525d6f2eSBarry Smith       rvalue = text.split(' ')[1]
186525d6f2eSBarry Smith
187*ccfb0f9fSMartin Diehl       if rvalue == "'"+'PETSC_USE_FORTRAN'+"'":
188525d6f2eSBarry Smith         if not hasattr(self.compilers, 'FC'):
1895b6bfdb9SJed Brown           if self.verbose: print('Rejecting',dirname,'because fortran is not being used')
190525d6f2eSBarry Smith           return 0
191525d6f2eSBarry Smith       elif rvalue == "'"+'PETSC_USE_LOG'+"'":
192525d6f2eSBarry Smith         if not self.libraryOptions.useLog:
1935b6bfdb9SJed Brown           if self.verbose: print('Rejecting',dirname,'because logging is turned off')
194525d6f2eSBarry Smith           return 0
195525d6f2eSBarry Smith       elif rvalue == "'"+'PETSC_USE_FORTRAN_KERNELS'+"'":
196525d6f2eSBarry Smith         if not self.libraryOptions.useFortranKernels:
1975b6bfdb9SJed Brown           if self.verbose: print('Rejecting',dirname,'because fortran kernels are turned off')
198525d6f2eSBarry Smith           return 0
199525d6f2eSBarry Smith       elif rtype == 'scalar' and not self.scalarType.scalartype == rvalue:
2005b6bfdb9SJed Brown         if self.verbose: print('Rejecting',dirname,'because scalar type '+self.scalarType.scalartype+' is not '+rvalue)
201525d6f2eSBarry Smith         return 0
202525d6f2eSBarry Smith       elif rtype == 'language':
203525d6f2eSBarry Smith         if rvalue == 'CXXONLY' and self.languages.clanguage == 'C':
2045b6bfdb9SJed Brown           if self.verbose: print('Rejecting',dirname,'because language is '+self.languages.clanguage+' is not C++')
205525d6f2eSBarry Smith           return 0
206525d6f2eSBarry Smith       elif rtype == 'precision' and not rvalue == self.scalarType.precision:
2075b6bfdb9SJed Brown         if self.verbose: print('Rejecting',dirname,'because precision '+self.scalarType.precision+' is not '+rvalue)
208525d6f2eSBarry Smith         return 0
209525d6f2eSBarry Smith       elif rtype == 'package':
210525d6f2eSBarry Smith         found = 0
211525d6f2eSBarry Smith         if self.mpi.usingMPIUni:
212525d6f2eSBarry Smith           pname = 'PETSC_HAVE_MPIUNI'
213525d6f2eSBarry Smith           pname = "'"+pname+"'"
214525d6f2eSBarry Smith           if pname == rvalue: found = 1
215525d6f2eSBarry Smith         for i in self.framework.packages:
216525d6f2eSBarry Smith           pname = 'PETSC_HAVE_'+i.PACKAGE
217525d6f2eSBarry Smith           pname = "'"+pname+"'"
218525d6f2eSBarry Smith           if pname == rvalue: found = 1
219525d6f2eSBarry Smith         if not found:
220a95d84e0SBarry Smith           for i in self.base.defines:
221a95d84e0SBarry Smith             pname = 'PETSC_'+i.upper()
222a95d84e0SBarry Smith             pname = "'"+pname+"'"
223a95d84e0SBarry Smith             if pname == rvalue: found = 1
224bcd1c4acSBarry Smith           for i in self.opengles.defines:
225bcd1c4acSBarry Smith             pname = 'PETSC_'+i.upper()
226bcd1c4acSBarry Smith             pname = "'"+pname+"'"
227bcd1c4acSBarry Smith             if pname == rvalue: found = 1
228a95d84e0SBarry Smith           if not found:
2295b6bfdb9SJed Brown             if self.verbose: print('Rejecting',dirname,'because package '+rvalue+' does not exist')
230525d6f2eSBarry Smith             return 0
231525d6f2eSBarry Smith       elif rtype == 'define':
232525d6f2eSBarry Smith         found = 0
233525d6f2eSBarry Smith         for i in self.base.defines:
234525d6f2eSBarry Smith           pname = 'PETSC_'+i.upper()
235525d6f2eSBarry Smith           pname = "'"+pname+"'"
236525d6f2eSBarry Smith           if pname == rvalue: found = 1
237525d6f2eSBarry Smith         if not found:
2385b6bfdb9SJed Brown           if self.verbose: print('Rejecting',dirname,'because define '+rvalue+' does not exist')
239525d6f2eSBarry Smith           return 0
240525d6f2eSBarry Smith       elif rtype == 'function':
241525d6f2eSBarry Smith         found = 0
242525d6f2eSBarry Smith         for i in self.functions.functions:
243525d6f2eSBarry Smith           pname = 'PETSC_HAVE_'+i.upper()
244525d6f2eSBarry Smith           pname = "'"+pname+"'"
245525d6f2eSBarry Smith#           print pname
246525d6f2eSBarry Smith#           print rvalue
247525d6f2eSBarry Smith           if pname == rvalue: found = 1
248525d6f2eSBarry Smith         if not found:
2495b6bfdb9SJed Brown           if self.verbose: print('Rejecting',dirname,'because function '+rvalue+' does not exist')
250525d6f2eSBarry Smith           return 0
251525d6f2eSBarry Smith
252525d6f2eSBarry Smith     text = fd.readline()
253525d6f2eSBarry Smith   fd.close()
254525d6f2eSBarry Smith   return True
255525d6f2eSBarry Smith
256525d6f2eSBarry Smith def buildAll(self, rootDir = None):
257525d6f2eSBarry Smith   import shutil
258525d6f2eSBarry Smith   self.setup()
259525d6f2eSBarry Smith   if rootDir is None:
260525d6f2eSBarry Smith     rootDir = self.argDB['rootDir']
261525d6f2eSBarry Smith   if not self.checkDir(rootDir):
2625b6bfdb9SJed Brown     print('Nothing to be done')
263525d6f2eSBarry Smith   if rootDir == os.environ['PETSC_DIR']:
264525d6f2eSBarry Smith     basedir = os.path.join(self.petscdir.dir, self.arch.arch, 'xcode-links')
265525d6f2eSBarry Smith     if os.path.isdir(basedir):
2665b6bfdb9SJed Brown       if self.verbose: print('Removing '+basedir)
267525d6f2eSBarry Smith       shutil.rmtree(basedir)
268525d6f2eSBarry Smith   os.mkdir(basedir)
269525d6f2eSBarry Smith   for root, dirs, files in os.walk(rootDir):
270525d6f2eSBarry Smith     self.buildDir(root)
271525d6f2eSBarry Smith     for badDir in [d for d in dirs if not self.checkDir(os.path.join(root, d))]:
272525d6f2eSBarry Smith       dirs.remove(badDir)
273525d6f2eSBarry Smith
27433be3048SBarry Smith   if not self.skipXCode:
27533be3048SBarry Smith
2765b6bfdb9SJed Brown     print('In Xcode mouse click on Other Sources then xcode-links and the delete key, then')
2775b6bfdb9SJed Brown     print('control mouse click on "Other Sources" and select "Add files to PETSc ...", then')
2785b6bfdb9SJed Brown     print('in the finder window locate ${PETSC_DIR}/arch-ios/xcode-links and select it. Now')
2795b6bfdb9SJed Brown     print('exit Xcode')
280525d6f2eSBarry Smith
281525d6f2eSBarry Smith     try:
282525d6f2eSBarry Smith       import subprocess
283525d6f2eSBarry Smith       subprocess.call('cd '+os.path.join(os.environ['PETSC_DIR'],'systems','Apple','iOS','PETSc')+';open -W PETSc.xcodeproj', shell=True)
2845b6bfdb9SJed Brown     except RuntimeError as e:
285525d6f2eSBarry Smith       raise RuntimeError('Error opening xcode project '+str(e))
286525d6f2eSBarry Smith
287525d6f2eSBarry Smith
288f66eea08SBarry Smith   sdk         = ' -sdk iphonesimulator '
289525d6f2eSBarry Smith   destination = 'iphonesimulator'
290525d6f2eSBarry Smith   debug       = 'Debug'
291525d6f2eSBarry Smith   debugdir    = 'Debug-'+destination
292525d6f2eSBarry Smith   if not self.compilerFlags.debugging:
293525d6f2eSBarry Smith     debug = 'Release'
294525d6f2eSBarry Smith     debugdir = 'Release-'+destination
295525d6f2eSBarry Smith   try:
2968506f42dSBarry Smith     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)
2975b6bfdb9SJed Brown   except RuntimeError as e:
298525d6f2eSBarry Smith     raise RuntimeError('Error making iPhone/iPad version of PETSc libraries: '+str(e))
299525d6f2eSBarry Smith
300b59f628eSBarry Smith   liblocation = os.path.join(os.environ['PETSC_DIR'],'systems','Apple','iOS','PETSc','build','Debug-iphonesimulator','PETSc.framework','PETSc')
301525d6f2eSBarry Smith   if not os.path.exists(liblocation):
302525d6f2eSBarry Smith     raise RuntimeError('Error library '+liblocation+' not created')
303525d6f2eSBarry Smith   try:
304575a0592SBarry Smith     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)
3055b6bfdb9SJed Brown   except RuntimeError as e:
306525d6f2eSBarry Smith     raise RuntimeError('Error copying iPhone/iPad version of PETSc libraries: '+str(e))
307525d6f2eSBarry Smith
308525d6f2eSBarry Smith   return
309525d6f2eSBarry Smith
310525d6f2eSBarry Smithdef noCheckCommand(command, status, output, error):
311525d6f2eSBarry Smith  ''' Do no check result'''
312525d6f2eSBarry Smith  return
313525d6f2eSBarry Smith  noCheckCommand = staticmethod(noCheckCommand)
314525d6f2eSBarry Smith
315525d6f2eSBarry Smithif __name__ == '__main__':
316525d6f2eSBarry Smith  PETScMaker().buildAll()
317