source: trunk/fsource/SConstruct @ 3106

Last change on this file since 3106 was 3106, checked in by vondreele, 4 years ago

new fortran routine for processing spot masks
fix to SConstruct - remove -m64

File size: 14.1 KB
Line 
1# this is a build script that is intended to be run from scons (it will not work in python)
2# it compiles the Fortran files needed to be used as Python packages for GSAS-II
3#
4# if the default options need to be overridden for this to work on your system, please let me
5# know the details of what OS, compiler and python you are using as well as the command-line
6# options you need.
7import platform
8import sys
9import os
10import glob
11import subprocess
12#==========================================================================================
13def is_exe(fpath):
14    return os.path.exists(fpath) and os.access(fpath, os.X_OK)
15def which_path(program):
16    "emulates Unix which: finds a program in path, but returns the path"
17    import os, sys
18    if sys.platform == "win32" and os.path.splitext(program)[1].lower() != '.exe':
19        program = program + '.exe'
20    fpath, fname = os.path.split(program)
21    if fpath:
22        if is_exe(program):
23            return fpath
24    else:
25        for path in os.environ["PATH"].split(os.pathsep):
26            exe_file = os.path.join(path, program)
27            if is_exe(exe_file):
28                return path
29    return ""
30#==========================================================================================
31# misc initializations
32# need command-line options for fortran command and fortran options
33F2PYflags = '' # compiler options for f2py command
34F2PYpath = ARGUMENTS.get('F2PYpath', '')
35# find a default f2py relative to the scons location. Should be in the same place as f2py
36spath = os.path.normpath(os.path.split(sys.executable)[0])
37for pth in [F2PYpath,spath,os.path.normpath(os.path.join(spath,'..')),os.path.join(spath,'Scripts')]:
38    if not pth: continue
39    if sys.platform == "win32":
40        program = 'f2py.exe'
41    else:
42        program = 'f2py'
43    f2pyprogram = os.path.join(pth,program)
44    if is_exe(f2pyprogram):
45        F2PYpath,F2PYprog = os.path.split(f2pyprogram)
46        break
47    program = 'f2py.py'
48    f2pyprogram = os.path.join(pth,program)
49    if os.path.exists(f2pyprogram) and os.path.splitext(program)[1] == '.py':
50        F2PYpath,F2PYprog = os.path.split(f2pyprogram)
51        break
52else:
53    print 'Note: Using f2py from path (hope that works!)'
54    F2PYpath = which_path('f2py')       # default path to f2py
55    F2PYprog = 'f2py'
56# check if we have a working path to f2py:
57f2pyprogram = os.path.normpath(os.path.join(F2PYpath,F2PYprog))
58if os.path.exists(f2pyprogram) and os.path.splitext(program)[1] == '.py':
59    pass
60elif is_exe(f2pyprogram):
61    pass
62else:
63    print '''
64ERROR: The f2py program was not found. If this program is installed
65but not in your path, you should specify the path on the command line:
66   scons -Q F2PYpath=/Library/Frameworks/Python.framework/Versions/6.2/bin/
67   scons -Q F2PYpath=D:/Python27/Scripts
68'''
69    sys.exit()
70
71GFORTpath = which_path('gfortran')   # path to compiler
72FCompiler='gfortran'
73G77path = which_path('g77')     # path to compiler
74FORTpath = ""
75FORTflags = ""
76liblist = []
77LDFLAGS = ''
78#==========================================================================================
79# configure platform dependent options here:
80if sys.platform == "win32":
81    F2PYsuffix = '.pyd'
82    if G77path != "":
83      FCompiler='g77'
84    elif GFORTpath != "":
85      FCompiler='gfortran'
86#      LDFLAGS = '-static-libgfortran -static-libgcc'
87    else:
88      print 'No Fortran compiler in path'
89      sys.exit()
90elif sys.platform == "darwin":
91    LDFLAGS = '-undefined dynamic_lookup -bundle -static-libgfortran -static-libgcc'
92    F2PYsuffix = '.so'
93elif sys.platform == "linux2":
94    #LDFLAGS = '-Wall -shared -static-libgfortran -static-libgcc' # does not work with gfortran 4.4.4 20100726 (Red Hat 4.4.4-13)
95    F2PYsuffix = '.so'
96else:
97    print "Sorry, parameters for platform "+sys.platform+" are not yet defined"
98    sys.exit()
99#==========================================================================================
100# help
101if 'help' in COMMAND_LINE_TARGETS:
102    print """
103----------------------------------------------
104Building Fortran routines for use with GSAS-II
105----------------------------------------------
106
107To build the compiled modules files needed to run GSAS-II, invoke this script:
108    scons [options]
109where the following options are defined (all are optional):
110
111-Q      -- produces less output from scons
112
113-n      -- causes scons to show but not execute the commands it will perform
114
115-c      -- clean: causes scons to delete previously created files (don't use
116   with install)
117
118help    -- causes this message to be displayed (no compiling is done)
119
120install -- causes the module files to be placed in an installation directory
121   (../bin<X>NNv.v) rather than ../bin (<X> is mac, win or linux; NN is 64-
122   or blank; v.v is the python version (2.6 or 2.7,...). Normally used only
123   by Brian or Bob for distribution of compiled software.
124
125The following options override defaults set in the scons script:
126
127FCompiler=<name>  -- define the name of the fortran compiler, typically gnu
128   or gfortran; default is to use gnu on Windows and gfortran elsewhere. If
129   you use something other than these, you must also specify F2PYflags.
130
131FORTpath=<path>    -- define a path to the fortran program; default is to use
132   first gfortran (gnu for Windows) found in path
133
134FORTflags='string' -- string of options to be used for Fortran
135   during library build step
136
137F2PYpath=<path>    -- define a path to the f2py program; default is to use
138   first f2py found in path
139
140F2PYflags='string' -- defines optional flags to be supplied to f2py:
141   Typically these option define which fortran compiler to use.
142
143F2PYsuffix='.xxx'  -- extension for output module files (default: win: '.pyd',
144   mac/linux: '.so')
145
146LDFLAGS='string'   -- string of options to be used for f2py during link step
147
148Note that at present, this has been tested with 32-bit python on windows and
149Mac & 64 bit on linux. Python 3.x is not supported in GSAS-II yet.
150
151examples:
152    scons -Q
153       (builds into ../bin for current platform using default options)
154    scons -Q install
155       (builds into ../bin<platform> for module distribution)
156    scons -Q install F2PYpath=/Library/Frameworks/Python.framework/Versions/6.2/bin/
157       (builds with a non-default [e.g. older] version of python)
158    """
159    #sys.exit()
160#==========================================================================================
161# override from command-line options
162for var in ['F2PYflags','F2PYpath','F2PYsuffix','FCompiler','FORTpath','FORTflags','LDFLAGS']:
163    if ARGUMENTS.get(var, None) is not None:
164        print 'Setting',var,'to',ARGUMENTS.get(var),'based on command line'
165        exec(var + "= ARGUMENTS.get('" + var +"')")
166#==========================================================================================
167# get the python version number from the python image in the f2py directory
168# find the python location associated with the f2py in use. Note
169#   that on Windows it may be in the parent of the f2py location.
170# then run it to get info about the verision and the number of bits
171pythonpath = ''
172for program in ['python','../python']:
173    if sys.platform == "win32" and os.path.splitext(program)[1].lower() != '.exe':
174        program = program + '.exe'
175    pythonprogram = os.path.normpath(os.path.join(F2PYpath,program))
176    if is_exe(pythonprogram):
177        pythonpath = os.path.split(program)[0]
178        break
179else:
180    print 'python not found'
181    sys.exit()
182p = subprocess.Popen(pythonprogram, stdout=subprocess.PIPE, stdin=subprocess.PIPE)
183p.stdin.write("import sys,platform;print str(sys.version_info[0]);print str(sys.version_info[1]);print platform.architecture()[0];sys.exit()")
184p.stdin.close()
185p.wait()
186versions = p.stdout.readlines()
187version = str(int(versions[0])) + '.' + str(int(versions[1]))
188PlatformBits = versions[2][:-1]
189#
190
191# Install location
192InstallLoc = '../bin'
193if len(COMMAND_LINE_TARGETS) == 0:
194    Default(InstallLoc)
195elif 'install' in COMMAND_LINE_TARGETS:
196    if PlatformBits == '64bit':
197        bits = '64-'
198    else:
199        bits = ''
200    if sys.platform == "win32":
201        prefix = 'binwin'
202    elif sys.platform == "darwin":
203        prefix = 'binmac'
204    elif sys.platform == "linux2":
205        prefix = 'binlinux'
206    InstallLoc = '../' + prefix + bits + version
207    Alias('install',InstallLoc)
208#==========================================================================================
209# use the compiler choice to set compiler options, but don't change anything
210# specified on the command line
211if FCompiler == 'gfortran':
212    if FORTpath == "": FORTpath = GFORTpath
213    if sys.platform == "linux2" and PlatformBits == '64bit':
214        if FORTflags == "": FORTflags = ' -w -O2 -fPIC -m64'
215        if F2PYflags == "": F2PYflags = '--fcompiler=gnu95 --f77exec=gfortran --f77flags="-fno-range-check -m64"'# --arch="-arch x86_64"'
216    elif sys.platform == "linux2":
217        if FORTflags == "": FORTflags = ' -w -O2 -fPIC -m32'
218        if F2PYflags == "": F2PYflags = '--fcompiler=gnu95 --f77exec=gfortran --f77flags="-fno-range-check -m32"'
219    elif sys.platform == "darwin" and PlatformBits == '64bit':
220        LDFLAGS += " -arch x86_64 -m64"
221        if FORTflags == "": FORTflags = ' -w -O2 -m64'
222        if F2PYflags == "": F2PYflags = '--fcompiler=gnu95 --f77exec=gfortran --f77flags="-fno-range-check -m64"'
223    elif sys.platform == "darwin":
224        if F2PYflags == "": F2PYflags = '--fcompiler=gnu95 --f77exec=gfortran --f77flags="-fno-range-check -m32"'
225    elif sys.platform == "win32":
226         if F2PYflags == "": F2PYflags = '--compiler=mingw32 --fcompiler=gnu95 --f77flags="-fno-range-check"'
227elif FCompiler == 'g77':
228    if FORTpath == "": FORTpath = G77path
229    if sys.platform == "win32":
230        if F2PYflags == "": F2PYflags = '--compiler=mingw32 --fcompiler=gnu'
231        if FORTflags == "": FORTflags = ' -w -O2 -fno-automatic -finit-local-zero -malign-double'
232    else:
233        if F2PYflags == "": F2PYflags = '--fcompiler=gnu --f77exec=g77 --f77flags="-fno-range-check"'
234
235else:
236    if FORTpath == "": print 'Likely error, FORTpath is not specified'
237    if F2PYflags == "":
238        print 'Error: specify a F2PYflags value'
239        sys.exit()
240#==========================================================================================
241# Setup build Environment
242env = Environment()
243# Define a builder to run f2py
244def generate_f2py(source, target, env, for_signature):
245    module = os.path.splitext(str(source[0]))[0]
246    if len(liblist) > 0:
247        for lib in liblist:
248            module = module + ' ' + str(lib)
249    f2pyprogram = os.path.normpath(os.path.join(F2PYpath,F2PYprog))
250    if os.path.splitext(F2PYprog)[1] == '.py':
251        return pythonprogram + ' ' + f2pyprogram + ' -c $SOURCE ' + ' -m ' + module + ' ' + F2PYflags
252    else:
253        return f2pyprogram + ' -c $SOURCE' + ' -m ' + module + ' ' + F2PYflags
254f2py = Builder(generator = generate_f2py)
255env.Append(BUILDERS = {'f2py' : f2py},)
256# create a builder for the fortran compiler for library compilation so we can control how it is done
257def generate_obj(source, target, env, for_signature):
258    dir = os.path.split(str(source[0]))[0]
259    obj = os.path.splitext(str(source[0]))[0]+'.o'
260    return os.path.join(FORTpath,FCompiler)  + ' -c $SOURCE ' + FORTflags + ' -I' + dir + ' -o' + obj
261fort = Builder(generator = generate_obj, suffix = '.o', src_suffix = '.for')
262# create a library builder so we can control how it is done on windows
263def generate_lib(source, target, env, for_signature):
264    srclst = ""
265    for s in source:
266      srclst += str(s) + " "
267    return os.path.join(FORTpath,'ar.exe')  + ' -rs $TARGET ' + srclst
268lib = Builder(generator = generate_lib, suffix = '.a',
269               src_suffix = '.o')
270env.Append(BUILDERS = {'fort' : fort, 'lib' : lib},)
271
272#==========================================================================================
273# Setup build Environment
274#    add compiler, f2py & python to path
275if FORTpath != "":  env.PrependENVPath('PATH', FORTpath)
276if F2PYpath != "":  env.PrependENVPath('PATH', F2PYpath)
277if pythonpath != "" and pythonpath != F2PYpath: env.PrependENVPath('PATH', pythonpath)
278#   add other needed environment variables
279var = 'LDFLAGS'
280if eval(var) != "": env['ENV'][var] = eval(var)
281
282#==========================================================================================
283# finally ready to build something!
284# locate libraries to be built (subdirectories named *subs)
285for sub in glob.glob('*subs'):
286    filelist = []
287    for file in glob.glob(os.path.join(sub,'*.for')):
288        #target = os.path.splitext(file)[0]+'.o'
289        target = env.fort(file) # connect .o files to .for files
290        #print 'Compile: ',file, target
291        filelist.append(target)
292    #lib = Library(sub, Glob(os.path.join(sub,'*.for'))) # register library to be created
293    if sys.platform == "win32":
294         lib = env.lib(sub, filelist)
295    else:
296       lib = Library(sub, filelist) # register library to be created
297    liblist.append(lib[0].name)
298    filename = str(lib[0])
299    #Install(InstallLoc,filename)
300# find modules that need to be built
301modlist = []
302for src in glob.glob('*.for'):
303    target = os.path.splitext(src)[0] + F2PYsuffix
304    out = env.f2py(target,src)
305    Depends(target, liblist) # make sure libraries are rebuilt if old
306    modlist.append(out[0].name)
307    env.Install(InstallLoc, out[0].name)
308    #break # bail out early for testing
309#==========================================================================================
310# all done with setup, show the user the options and let scons do the work
311print 80*'='
312for var in ['FCompiler','FORTpath','FORTflags','F2PYflags','F2PYpath','F2PYsuffix','LDFLAGS']:
313    print 'Variable',var,'is',eval(var)
314print 'Using python at', pythonprogram
315print 'Python/f2py version =',version,PlatformBits
316print 'Install directory is',InstallLoc
317print 'Will build object libraries:',
318for lib in liblist: print " " + lib,
319print ""
320print 'f2py will build these modules:',
321for mod in modlist: print " " + mod,
322print ""
323print 80*'='
324#print env.Dump()
325if 'help' in COMMAND_LINE_TARGETS: sys.exit()
Note: See TracBrowser for help on using the repository browser.