config_c.py :  » Build » Waf » waf-1.5.17 » wafadmin » Tools » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Build » Waf 
Waf » waf 1.5.17 » wafadmin » Tools » config_c.py
#!/usr/bin/env python
# encoding: utf-8
# Thomas Nagy, 2005-2008 (ita)

"""
c/c++ configuration routines
"""

import os, imp, sys, shlex, shutil
from Utils import md5
import Build, Utils, Configure, Task, Options, Logs, TaskGen
from Constants import *
from Configure import conf,conftest

cfg_ver = {
  'atleast-version': '>=',
  'exact-version': '==',
  'max-version': '<=',
}

SNIP1 = '''
  int main() {
  void *p;
  p=(void*)(%s);
  return 0;
}
'''

SNIP2 = '''
int main() {
  if ((%(type_name)s *) 0) return 0;
  if (sizeof (%(type_name)s)) return 0;
}
'''

SNIP3 = '''
int main() {
  return 0;
}
'''

def parse_flags(line, uselib, env):
  """pkg-config still has bugs on some platforms, and there are many -config programs, parsing flags is necessary :-/"""

  lst = shlex.split(line)
  while lst:
    x = lst.pop(0)
    st = x[:2]
    ot = x[2:]

    if st == '-I' or st == '/I':
      if not ot: ot = lst.pop(0)
      env.append_unique('CPPPATH_' + uselib, ot)
    elif st == '-D':
      if not ot: ot = lst.pop(0)
      env.append_unique('CXXDEFINES_' + uselib, ot)
      env.append_unique('CCDEFINES_' + uselib, ot)
    elif st == '-l':
      if not ot: ot = lst.pop(0)
      env.append_unique('LIB_' + uselib, ot)
    elif st == '-L':
      if not ot: ot = lst.pop(0)
      env.append_unique('LIBPATH_' + uselib, ot)
    elif x == '-pthread' or x.startswith('+'):
      env.append_unique('CCFLAGS_' + uselib, x)
      env.append_unique('CXXFLAGS_' + uselib, x)
      env.append_unique('LINKFLAGS_' + uselib, x)
    elif x == '-framework':
      env.append_unique('FRAMEWORK_' + uselib, lst.pop(0))
    elif x.startswith('-F'):
      env.append_unique('FRAMEWORKPATH_' + uselib, x[2:])
    elif x.startswith('-std'):
      env.append_unique('CCFLAGS_' + uselib, x)
      env.append_unique('LINKFLAGS_' + uselib, x)
    elif x.startswith('-Wl'):
      env.append_unique('LINKFLAGS_' + uselib, x)
    elif x.startswith('-m') or x.startswith('-f'):
      env.append_unique('CCFLAGS_' + uselib, x)
      env.append_unique('CXXFLAGS_' + uselib, x)

@conf
def ret_msg(self, f, kw):
  """execute a function, when provided"""
  if isinstance(f, str):
    return f
  return f(kw)

@conf
def validate_cfg(self, kw):
  if not 'path' in kw:
    kw['path'] = 'pkg-config --errors-to-stdout --print-errors'

  # pkg-config version
  if 'atleast_pkgconfig_version' in kw:
    if not 'msg' in kw:
      kw['msg'] = 'Checking for pkg-config version >= %s' % kw['atleast_pkgconfig_version']
    return

  # pkg-config --modversion
  if 'modversion' in kw:
    return

  if 'variables' in kw:
    if not 'msg' in kw:
      kw['msg'] = 'Checking for %s variables' % kw['package']
    return

  # checking for the version of a module, for the moment, one thing at a time
  for x in cfg_ver.keys():
    y = x.replace('-', '_')
    if y in kw:
      if not 'package' in kw:
        raise ValueError('%s requires a package' % x)

      if not 'msg' in kw:
        kw['msg'] = 'Checking for %s %s %s' % (kw['package'], cfg_ver[x], kw[y])
      return

  if not 'msg' in kw:
    kw['msg'] = 'Checking for %s' % (kw['package'] or kw['path'])
  if not 'okmsg' in kw:
    kw['okmsg'] = 'yes'
  if not 'errmsg' in kw:
    kw['errmsg'] = 'not found'

@conf
def cmd_and_log(self, cmd, kw):
  Logs.debug('runner: %s\n' % cmd)
  if self.log:
    self.log.write('%s\n' % cmd)

  try:
    p = Utils.pproc.Popen(cmd, stdout=Utils.pproc.PIPE, stderr=Utils.pproc.PIPE, shell=True)
    (out, err) = p.communicate()
  except OSError, e:
    self.log.write('error %r' % e)
    self.fatal(str(e))

  # placeholder, don't touch
  out = str(out)
  err = str(err)

  if self.log:
    self.log.write(out)
    self.log.write(err)

  if p.returncode:
    if not kw.get('errmsg', ''):
      if kw.get('mandatory', False):
        kw['errmsg'] = out.strip()
      else:
        kw['errmsg'] = 'no'
    self.fatal('fail')
  return out

@conf
def exec_cfg(self, kw):

  # pkg-config version
  if 'atleast_pkgconfig_version' in kw:
    cmd = '%s --atleast-pkgconfig-version=%s' % (kw['path'], kw['atleast_pkgconfig_version'])
    self.cmd_and_log(cmd, kw)
    if not 'okmsg' in kw:
      kw['okmsg'] = 'yes'
    return

  # checking for the version of a module
  for x in cfg_ver:
    y = x.replace('-', '_')
    if y in kw:
      self.cmd_and_log('%s --%s=%s %s' % (kw['path'], x, kw[y], kw['package']), kw)
      if not 'okmsg' in kw:
        kw['okmsg'] = 'yes'
      self.define(self.have_define(kw.get('uselib_store', kw['package'])), 1, 0)
      break

  # retrieving the version of a module
  if 'modversion' in kw:
    version = self.cmd_and_log('%s --modversion %s' % (kw['path'], kw['modversion']), kw).strip()
    self.define('%s_VERSION' % Utils.quote_define_name(kw.get('uselib_store', kw['modversion'])), version)
    return version

  # retrieving variables of a module
  if 'variables' in kw:
    env = kw.get('env', self.env)
    uselib = kw.get('uselib_store', kw['package'].upper())
    vars = Utils.to_list(kw['variables'])
    for v in vars:
      val = self.cmd_and_log('%s --variable=%s %s' % (kw['path'], v, kw['package']), kw).strip()
      var = '%s_%s' % (uselib, v)
      env[var] = val
    if not 'okmsg' in kw:
      kw['okmsg'] = 'yes'
    return

  lst = [kw['path']]


  defi = kw.get('define_variable', None)
  if not defi:
    defi = self.env.PKG_CONFIG_DEFINES or {}
  for key, val in defi.iteritems():
    lst.append('--define-variable=%s=%s' % (key, val))

  lst.append(kw.get('args', ''))
  lst.append(kw['package'])

  # so we assume the command-line will output flags to be parsed afterwards
  cmd = ' '.join(lst)
  ret = self.cmd_and_log(cmd, kw)
  if not 'okmsg' in kw:
    kw['okmsg'] = 'yes'

  self.define(self.have_define(kw.get('uselib_store', kw['package'])), 1, 0)
  parse_flags(ret, kw.get('uselib_store', kw['package'].upper()), kw.get('env', self.env))
  return ret

@conf
def check_cfg(self, *k, **kw):
  """
  for pkg-config mostly, but also all the -config tools
  conf.check_cfg(path='mpicc', args='--showme:compile --showme:link', package='', uselib_store='OPEN_MPI')
  conf.check_cfg(package='dbus-1', variables='system_bus_default_address session_bus_services_dir')
  """

  self.validate_cfg(kw)
  if 'msg' in kw:
    self.check_message_1(kw['msg'])
  ret = None
  try:
    ret = self.exec_cfg(kw)
  except Configure.ConfigurationError, e:
    if 'errmsg' in kw:
      self.check_message_2(kw['errmsg'], 'YELLOW')
    if 'mandatory' in kw and kw['mandatory']:
      if Logs.verbose > 1:
        raise
      else:
        self.fatal('the configuration failed (see %r)' % self.log.name)
  else:
    kw['success'] = ret
    if 'okmsg' in kw:
      self.check_message_2(self.ret_msg(kw['okmsg'], kw))

  return ret

# the idea is the following: now that we are certain
# that all the code here is only for c or c++, it is
# easy to put all the logic in one function
#
# this should prevent code duplication (ita)

# env: an optional environment (modified -> provide a copy)
# compiler: cc or cxx - it tries to guess what is best
# type: cprogram, cshlib, cstaticlib
# code: a c code to execute
# uselib_store: where to add the variables
# uselib: parameters to use for building
# define: define to set, like FOO in #define FOO, if not set, add /* #undef FOO */
# execute: True or False - will return the result of the execution

@conf
def validate_c(self, kw):
  """validate the parameters for the test method"""

  if not 'env' in kw:
    kw['env'] = self.env.copy()

  env = kw['env']
  if not 'compiler' in kw:
    kw['compiler'] = 'cc'
    if env['CXX_NAME'] and Task.TaskBase.classes.get('cxx', None):
      kw['compiler'] = 'cxx'
      if not self.env['CXX']:
        self.fatal('a c++ compiler is required')
    else:
      if not self.env['CC']:
        self.fatal('a c compiler is required')

  if not 'type' in kw:
    kw['type'] = 'cprogram'

  assert not(kw['type'] != 'cprogram' and kw.get('execute', 0)), 'can only execute programs'


  #if kw['type'] != 'program' and kw.get('execute', 0):
  #  raise ValueError, 'can only execute programs'

  def to_header(dct):
    if 'header_name' in dct:
      dct = Utils.to_list(dct['header_name'])
      return ''.join(['#include <%s>\n' % x for x in dct])
    return ''

  # set the file name
  if not 'compile_mode' in kw:
    kw['compile_mode'] = (kw['compiler'] == 'cxx') and 'cxx' or 'cc'

  if not 'compile_filename' in kw:
    kw['compile_filename'] = 'test.c' + ((kw['compile_mode'] == 'cxx') and 'pp' or '')

  #OSX
  if 'framework_name' in kw:
    try: TaskGen.task_gen.create_task_macapp
    except AttributeError: self.fatal('frameworks require the osx tool')

    fwkname = kw['framework_name']
    if not 'uselib_store' in kw:
      kw['uselib_store'] = fwkname.upper()

    if not kw.get('no_header', False):
      if not 'header_name' in kw:
        kw['header_name'] = []
      fwk = '%s/%s.h' % (fwkname, fwkname)
      if kw.get('remove_dot_h', None):
        fwk = fwk[:-2]
      kw['header_name'] = Utils.to_list(kw['header_name']) + [fwk]

    kw['msg'] = 'Checking for framework %s' % fwkname
    kw['framework'] = fwkname
    #kw['frameworkpath'] = set it yourself

  if 'function_name' in kw:
    fu = kw['function_name']
    if not 'msg' in kw:
      kw['msg'] = 'Checking for function %s' % fu
    kw['code'] = to_header(kw) + SNIP1 % fu
    if not 'uselib_store' in kw:
      kw['uselib_store'] = fu.upper()
    if not 'define_name' in kw:
      kw['define_name'] = self.have_define(fu)

  elif 'type_name' in kw:
    tu = kw['type_name']
    if not 'msg' in kw:
      kw['msg'] = 'Checking for type %s' % tu
    if not 'header_name' in kw:
      kw['header_name'] = 'stdint.h'
    kw['code'] = to_header(kw) + SNIP2 % {'type_name' : tu}
    if not 'define_name' in kw:
      kw['define_name'] = self.have_define(tu.upper())

  elif 'header_name' in kw:
    if not 'msg' in kw:
      kw['msg'] = 'Checking for header %s' % kw['header_name']

    l = Utils.to_list(kw['header_name'])
    assert len(l)>0, 'list of headers in header_name is empty'

    kw['code'] = to_header(kw) + SNIP3

    if not 'uselib_store' in kw:
      kw['uselib_store'] = l[0].upper()

    if not 'define_name' in kw:
      kw['define_name'] = self.have_define(l[0])

  if 'lib' in kw:
    if not 'msg' in kw:
      kw['msg'] = 'Checking for library %s' % kw['lib']
    if not 'uselib_store' in kw:
      kw['uselib_store'] = kw['lib'].upper()

  if 'staticlib' in kw:
    if not 'msg' in kw:
      kw['msg'] = 'Checking for static library %s' % kw['staticlib']
    if not 'uselib_store' in kw:
      kw['uselib_store'] = kw['staticlib'].upper()

  if 'fragment' in kw:
    # an additional code fragment may be provided to replace the predefined code
    # in custom headers
    kw['code'] = kw['fragment']
    if not 'msg' in kw:
      kw['msg'] = 'Checking for custom code'
    if not 'errmsg' in kw:
      kw['errmsg'] = 'no'

  for (flagsname,flagstype) in [('cxxflags','compiler'), ('cflags','compiler'), ('linkflags','linker')]:
    if flagsname in kw:
      if not 'msg' in kw:
        kw['msg'] = 'Checking for %s flags %s' % (flagstype, kw[flagsname])
      if not 'errmsg' in kw:
        kw['errmsg'] = 'no'

  if not 'execute' in kw:
    kw['execute'] = False

  if not 'errmsg' in kw:
    kw['errmsg'] = 'not found'

  if not 'okmsg' in kw:
    kw['okmsg'] = 'yes'

  if not 'code' in kw:
    kw['code'] = SNIP3

  if not kw.get('success'): kw['success'] = None

  assert 'msg' in kw, 'invalid parameters, read http://freehackers.org/~tnagy/wafbook/single.html#config_helpers_c'

@conf
def post_check(self, *k, **kw):
  "set the variables after a test was run successfully"

  is_success = False
  if kw['execute']:
    if kw['success'] is not None:
      is_success = True
  else:
    is_success = (kw['success'] == 0)

  if 'define_name' in kw:
    if 'header_name' in kw or 'function_name' in kw or 'type_name' in kw or 'fragment' in kw:
      if kw['execute']:
        key = kw['success']
        if isinstance(key, str):
          if key:
            self.define(kw['define_name'], key, quote=kw.get('quote', 1))
          else:
            self.define_cond(kw['define_name'], True)
        else:
          self.define_cond(kw['define_name'], False)
      else:
        self.define_cond(kw['define_name'], is_success)

  if is_success and 'uselib_store' in kw:
    import cc, cxx
    for k in set(cc.g_cc_flag_vars).union(cxx.g_cxx_flag_vars):
      lk = k.lower()
      # inconsistency: includes -> CPPPATH
      if k == 'CPPPATH': lk = 'includes'
      if k == 'CXXDEFINES': lk = 'defines'
      if k == 'CCDEFINES': lk = 'defines'
      if lk in kw:
        val = kw[lk]
        # remove trailing slash
        if isinstance(val, str):
          val = val.rstrip(os.path.sep)
        self.env.append_unique(k + '_' + kw['uselib_store'], val)

@conf
def check(self, *k, **kw):
  # so this will be the generic function
  # it will be safer to use check_cxx or check_cc
  self.validate_c(kw)
  self.check_message_1(kw['msg'])
  ret = None
  try:
    ret = self.run_c_code(*k, **kw)
  except Configure.ConfigurationError, e:
    self.check_message_2(kw['errmsg'], 'YELLOW')
    if 'mandatory' in kw and kw['mandatory']:
      if Logs.verbose > 1:
        raise
      else:
        self.fatal('the configuration failed (see %r)' % self.log.name)
  else:
    kw['success'] = ret
    self.check_message_2(self.ret_msg(kw['okmsg'], kw))

  self.post_check(*k, **kw)
  if not kw.get('execute', False):
    return ret == 0
  return ret

@conf
def run_c_code(self, *k, **kw):
  test_f_name = kw['compile_filename']

  k = 0
  while k < 10000:
    # make certain to use a fresh folder - necessary for win32
    dir = os.path.join(self.blddir, '.conf_check_%d' % k)

    # if the folder already exists, remove it
    try:
      shutil.rmtree(dir)
    except OSError:
      pass

    try:
      os.stat(dir)
    except OSError:
      break

    k += 1

  try:
    os.makedirs(dir)
  except:
    self.fatal('cannot create a configuration test folder %r' % dir)

  try:
    os.stat(dir)
  except:
    self.fatal('cannot use the configuration test folder %r' % dir)

  bdir = os.path.join(dir, 'testbuild')

  if not os.path.exists(bdir):
    os.makedirs(bdir)

  env = kw['env']

  dest = open(os.path.join(dir, test_f_name), 'w')
  dest.write(kw['code'])
  dest.close()

  back = os.path.abspath('.')

  bld = Build.BuildContext()
  bld.log = self.log
  bld.all_envs.update(self.all_envs)
  bld.all_envs['default'] = env
  bld.lst_variants = bld.all_envs.keys()
  bld.load_dirs(dir, bdir)

  os.chdir(dir)

  bld.rescan(bld.srcnode)

  if not 'features' in kw:
    # conf.check(features='cc cprogram pyext', ...)
    kw['features'] = [kw['compile_mode'], kw['type']] # "cprogram cc"

  o = bld(features=kw['features'], source=test_f_name, target='testprog')

  for k, v in kw.iteritems():
    setattr(o, k, v)

  self.log.write("==>\n%s\n<==\n" % kw['code'])

  # compile the program
  try:
    bld.compile()
  except Utils.WafError:
    ret = Utils.ex_stack()
  else:
    ret = 0

  # chdir before returning
  os.chdir(back)

  if ret:
    self.log.write('command returned %r' % ret)
    self.fatal(str(ret))

  # if we need to run the program, try to get its result
  # keep the name of the program to execute
  if kw['execute']:
    lastprog = o.link_task.outputs[0].abspath(env)

    args = Utils.to_list(kw.get('exec_args', []))
    proc = Utils.pproc.Popen([lastprog] + args, stdout=Utils.pproc.PIPE, stderr=Utils.pproc.PIPE)
    (out, err) = proc.communicate()
    w = self.log.write
    w(str(out))
    w('\n')
    w(str(err))
    w('\n')
    w('returncode %r' % proc.returncode)
    w('\n')
    if proc.returncode:
      self.fatal(Utils.ex_stack())
    ret = out

  return ret

@conf
def check_cxx(self, *k, **kw):
  kw['compiler'] = 'cxx'
  return self.check(*k, **kw)

@conf
def check_cc(self, *k, **kw):
  kw['compiler'] = 'cc'
  return self.check(*k, **kw)

@conf
def define(self, define, value, quote=1):
  """store a single define and its state into an internal list for later
     writing to a config header file.  Value can only be
     a string or int; other types not supported.  String
     values will appear properly quoted in the generated
     header file."""
  assert define and isinstance(define, str)

  # ordered_dict is for writing the configuration header in order
  tbl = self.env[DEFINES] or Utils.ordered_dict()

  # the user forgot to tell if the value is quoted or not
  if isinstance(value, str):
    if quote:
      tbl[define] = '"%s"' % repr('"'+value)[2:-1].replace('"', '\\"')
    else:
      tbl[define] = value
  elif isinstance(value, int):
    tbl[define] = value
  else:
    raise TypeError('define %r -> %r must be a string or an int' % (define, value))

  # add later to make reconfiguring faster
  self.env[DEFINES] = tbl
  self.env[define] = value # <- not certain this is necessary

@conf
def undefine(self, define):
  """store a single define and its state into an internal list
     for later writing to a config header file"""
  assert define and isinstance(define, str)

  tbl = self.env[DEFINES] or Utils.ordered_dict()

  value = UNDEFINED
  tbl[define] = value

  # add later to make reconfiguring faster
  self.env[DEFINES] = tbl
  self.env[define] = value

@conf
def define_cond(self, name, value):
  """Conditionally define a name.
  Formally equivalent to: if value: define(name, 1) else: undefine(name)"""
  if value:
    self.define(name, 1)
  else:
    self.undefine(name)

@conf
def is_defined(self, key):
  defines = self.env[DEFINES]
  if not defines:
    return False
  try:
    value = defines[key]
  except KeyError:
    return False
  else:
    return value != UNDEFINED

@conf
def get_define(self, define):
  "get the value of a previously stored define"
  try: return self.env[DEFINES][define]
  except KeyError: return None

@conf
def have_define(self, name):
  "prefix the define with 'HAVE_' and make sure it has valid characters."
  return self.__dict__.get('HAVE_PAT', 'HAVE_%s') % Utils.quote_define_name(name)

@conf
def write_config_header(self, configfile='', env='', guard='', top=False):
  "save the defines into a file"
  if not configfile: configfile = WAF_CONFIG_H
  waf_guard = guard or '_%s_WAF' % Utils.quote_define_name(configfile)

  # configfile -> absolute path
  # there is a good reason to concatenate first and to split afterwards
  if not env: env = self.env
  if top:
    diff = ''
  else:
    diff = Utils.diff_path(self.srcdir, self.curdir)
  full = os.sep.join([self.blddir, env.variant(), diff, configfile])
  full = os.path.normpath(full)
  (dir, base) = os.path.split(full)

  try: os.makedirs(dir)
  except: pass

  dest = open(full, 'w')
  dest.write('/* Configuration header created by Waf - do not edit */\n')
  dest.write('#ifndef %s\n#define %s\n\n' % (waf_guard, waf_guard))

  dest.write(self.get_config_header())

  # config files are not removed on "waf clean"
  env.append_unique(CFG_FILES, os.path.join(diff, configfile))

  dest.write('\n#endif /* %s */\n' % waf_guard)
  dest.close()

@conf
def get_config_header(self):
  """Fill-in the contents of the config header. Override when you need to write your own config header."""
  config_header = []

  tbl = self.env[DEFINES] or Utils.ordered_dict()
  for key in tbl.allkeys:
    value = tbl[key]
    if value is None:
      config_header.append('#define %s' % key)
    elif value is UNDEFINED:
      config_header.append('/* #undef %s */' % key)
    else:
      config_header.append('#define %s %s' % (key, value))
  return "\n".join(config_header)

@conftest
def find_cpp(conf):
  v = conf.env
  cpp = None
  if v['CPP']: cpp = v['CPP']
  elif 'CPP' in conf.environ: cpp = conf.environ['CPP']
  if not cpp: cpp = conf.find_program('cpp', var='CPP')
  if not cpp: cpp = v['CC']
  if not cpp: cpp = v['CXX']
  v['CPP'] = cpp

@conftest
def cc_add_flags(conf):
  conf.add_os_flags('CFLAGS', 'CCFLAGS')
  conf.add_os_flags('CPPFLAGS')

@conftest
def cxx_add_flags(conf):
  conf.add_os_flags('CXXFLAGS')
  conf.add_os_flags('CPPFLAGS')

@conftest
def link_add_flags(conf):
  conf.add_os_flags('LINKFLAGS')
  conf.add_os_flags('LDFLAGS', 'LINKFLAGS')

@conftest
def cc_load_tools(conf):
  conf.check_tool('cc')

@conftest
def cxx_load_tools(conf):
  conf.check_tool('cxx')

www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.