[Scons-users] Question about scons

Gary Granger granger at ucar.edu
Thu Feb 11 16:48:30 EST 2016


Hi Guillaume,

I have attached a modified version of your tool and SConstruct file. 
Below is some explanation of my changes and my answers to your questions.

I used env.SetDefault() to set default tool variables rather than the
'if <var> not in env' pattern.

I added FFTW_INCLUDE_DIR to CPPPATH rather than adding the -I flag
directly to CFLAGS and CXXFLAGS.  SCons will generate the correct
compiler flags from CPPPATH.

I changed the tool to raise SCons exceptions rather than just exiting.

You can run this file to test it with 'scons -f SConsFFTW
--config=force'.  I suggest the config=force option in case you change
the FFTW_LIBRARY_WISH or FFTW_VERSION variables.  There is probably a
way to trigger rebuilds of the configure targets when those variables
change, but I didn't look up how to do that.

I believe the SCons convention is that once you have added a tool to the
Environment, then the Environment is ready to build software which
requires that tool without requiring an extra Configure step.  Thus I
commented that part out.  If you want to build software with different
tools, then you can create different Environments for each set of
tools.  At least, that is the approach we've used, and I think it works
well.  As an example, I create two Environments in the SConstruct file,
one for each version.

In testing on Fedora 22, I found that fftw2 requires libm, and so I add
it directly to the Environment before creating the Configure instance. 
I didn't think it was necessary to loop over all the libraries.  Instead
I add them directly to the Environment and just test for the header. 
The link check will still fail if any of the libraries cannot be found. 
I think the tool is the right place to run the Configure check, since
it's up to the tool to find the libraries and headers and customize the
Environment appropriately.

The fftw tool can explicitly apply the 'default' tool using the Tool()
method.  However, I do not think that is the usual approach.  I think
typically the 'default' tool is loaded explicitly when the Environment
is created, but others may know better.  I do frequently load
non-default tools inside other tools.  For example, we have a logging
library which has its own tool, but that tool in turn loads a log4cpp tool.

If you really want to defer adding the fftw libraries to the
Environment, then you can use the tool to cache the settings in the
Environment and then add a method which will apply the settings.  eg,
use env.AddMethod(FFTW) in the tool, and then call env.FFTW() in the
SConstruct.  I don't recommend that, though, if you can get the same
effect with Environment(tools=['default',fftw]).

Finally, we already had a simple fftw tool, but it was "hardcoded" for
version 3 with threads.  If you really need to support all the different
combinations, you might consider providing different tools rather than
one more complicated tool which has to be configured: eg, fftw3_threads,
fftw3, and fftw2.  The tool paradigm is really very powerful and
flexible.  It is even possible to define a tool which in turn defines
other tools.  For Qt4 we use a tool to define more tools for all the
various Qt4 modules.  I have been maintaining a custom library of tools
for our particular set of software for a long time, so maybe my own
experience will be helpful to you.

Thanks,
gary

On 02/11/2016 04:15 AM, Guillaume Anciaux wrote:
> Dear all,
>
> This is a follow-up on my progression.
>
> Before even working on the repo to place the tools (and other stuff) I
> needed to do a few tests for my knowledge and be sure I go in the right
> direction.
>
> What I did is I made a tool in a site_scons/site_init.py as a start.
> It aims at 'detecting' FFTW with a few options like the required version
> for instance. This is what I wrote:
>
> *******************************************************
> def fftw(env):
>    """A Tool to search for fftw headers and libraries"""
>
>    if env.GetOption('clean'): return
>    if 'FFTW_VERSION' not in env: env['FFTW_VERSION'] = "3"
>    env['FFTW_VERSION'] = str(env['FFTW_VERSION'])
>
>    if 'FFTW_INCLUDE_DIR' in env:
>       env['CXXFLAGS'].append('-I{0}'.format(env['FFTW_INCLUDE_DIR']))
>       env['CFLAGS'].append('-I{0}'.format(env['FFTW_INCLUDE_DIR']))
>
>    if 'FFTW_LIBRARY_WISH' not in env:
>       env['FFTW_LIBRARY_WISH'] = ['main']
>      
>    if 'FFTW_LIBRARY_DIR' in env:
>       if 'LIBPATH' not in env: env['LIBPATH'] = []
>       env['LIBPATH'] += [env['FFTW_LIBRARY_DIR']]
>   
>    version = env['FFTW_VERSION']
>    if version == "2":
>       lib_names = {'main':'fftw'}
>       inc_names = ['fftw.h']
>    else:
>       lib_names =
> {'main':'fftw3','thread':'fftw3_threads','omp':'fftw3_omp'}
>       inc_names = ['fftw3.h']
>
>    conf = Configure(env)
>
>    try:
>       lib_names = [lib_names[i] for i in env['FFTW_LIBRARY_WISH']+['main']]
>    except:
>       print 'Incompatible wishlist {0} from version
> {1}'.format(env['FFTW_WISH_LIST'],
>                                                                
> env['FFTW_VERSION'])
>       Exit(1)
>
>    for l in lib_names:
>       if not conf.CheckLibWithHeader(l, inc_names, 'c'):
>          print 'Did not find FFTW, exiting!'
>          Exit(1)
>       print env['LIBS']
>   
>         
>    env['FFTW_LIBRARIES'] = lib_names
>    env = conf.Finish()
> *******************************************************
>
> Thus I have some questions:
>
> - What I wrote also has a configure sequence. But I think this is not
> what a tool should do. Correct ?
>
> From what I read you can also make your own configure checks.
>
> - I think the environment extension, which defines a few variables based
> on some others, and the 'configure' stage which actually verify that
> everything can be accessed are close related pieces of code: can we make
> a tool that registers specific configure checks ?
>
> - The configure stage is adding directly what he found to the $LIBS
> option. However if I just want to know that everything is here and
> manually add to LIBS: can I do that ?
>
>
> I guess that in the end I would like to be able to write a sequence of
> code like:
> *********************************
> env = Environment(
>     tools=['defatult','fftw'],
>     FFTW_LIBRARY_WISH = ['thread','omp']
>     FFTW_VERSION = ['thread','omp']
> )
>
> conf = Configure(env)
> conf.CheckFFTW()
> conf.Finish()
> **********************************
>
>
> - how to make it explicit that the FFTW tool depends on the 'default'
> (since it needs a C/C++ compiler to be used) ?
>
> Thanks for your help.
>
> Best
>
> Guillaume
>

-------------- next part --------------
# -*- python -*-

import SCons

def fftw(env):
    """A Tool to search for fftw headers and libraries"""

    if env.GetOption('clean'): return
    env.Tool('default')
    env.SetDefault(FFTW_VERSION='3')
    env.SetDefault(FFTW_LIBRARY_WISH=[])
    print("FFTW_VERSION=%s" % (str(env['FFTW_VERSION'])))

    if 'FFTW_INCLUDE_DIR' in env:
        env.AppendUnique(CPPPATH=env['FFTW_INCLUDE_DIR'])

    if 'FFTW_LIBRARY_DIR' in env:
        env.AppendUnique(LIBPATH=env['FFTW_LIBRARY_DIR'])
  
    version = env['FFTW_VERSION']
    if version == "2":
        lib_names = {'main':'fftw'}
        inc_names = ['fftw.h']
    else:
        lib_names = {'main':'fftw3','thread':'fftw3_threads','omp':'fftw3_omp'}
        inc_names = ['fftw3.h']

    try:
        lib_names = [lib_names[i] for i in env['FFTW_LIBRARY_WISH']+['main']]
    except:
        raise SCons.Errors.StopError(
            'Incompatible wishlist {0} from version {1}'.format(
                env['FFTW_LIBRARY_WISH'], env['FFTW_VERSION']))
                                                               
    env.Append(LIBS=lib_names)
    if version == "2":
        env.Append(LIBS='m')
    conf = Configure(env)
    if not conf.CheckLibWithHeader(None, inc_names, 'c'):
            raise SCons.Errors.StopError(
                'Failed to find libraries {0} or '
                'headers {1}.'.format(str(lib_names), str(inc_names)))
    env = conf.Finish()


env = Environment(
    tools=[fftw],
    FFTW_LIBRARY_WISH = ['thread', 'omp'],
    FFTW_VERSION = '3'
)
print(env['LIBS'])

env2 = Environment(
    tools=[fftw],
    FFTW_VERSION = '2'
)
print(env2['LIBS'])

#conf = Configure(env)
#conf.CheckFFTW()
#conf.Finish()


More information about the Scons-users mailing list