You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

344 lines
13 KiB

6 years ago
  1. """SCons.Tool.qt
  2. Tool-specific initialization for Qt.
  3. There normally shouldn't be any need to import this module directly.
  4. It will usually be imported through the generic SCons.Tool.Tool()
  5. selection method.
  6. """
  7. #
  8. # Copyright (c) 2001 - 2017 The SCons Foundation
  9. #
  10. # Permission is hereby granted, free of charge, to any person obtaining
  11. # a copy of this software and associated documentation files (the
  12. # "Software"), to deal in the Software without restriction, including
  13. # without limitation the rights to use, copy, modify, merge, publish,
  14. # distribute, sublicense, and/or sell copies of the Software, and to
  15. # permit persons to whom the Software is furnished to do so, subject to
  16. # the following conditions:
  17. #
  18. # The above copyright notice and this permission notice shall be included
  19. # in all copies or substantial portions of the Software.
  20. #
  21. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
  22. # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  23. # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  24. # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  25. # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  26. # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  27. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  28. #
  29. from __future__ import print_function
  30. __revision__ = "src/engine/SCons/Tool/qt.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog"
  31. import os.path
  32. import re
  33. import SCons.Action
  34. import SCons.Builder
  35. import SCons.Defaults
  36. import SCons.Scanner
  37. import SCons.Tool
  38. import SCons.Util
  39. class ToolQtWarning(SCons.Warnings.Warning):
  40. pass
  41. class GeneratedMocFileNotIncluded(ToolQtWarning):
  42. pass
  43. class QtdirNotFound(ToolQtWarning):
  44. pass
  45. SCons.Warnings.enableWarningClass(ToolQtWarning)
  46. header_extensions = [".h", ".hxx", ".hpp", ".hh"]
  47. if SCons.Util.case_sensitive_suffixes('.h', '.H'):
  48. header_extensions.append('.H')
  49. import SCons.Tool.cxx
  50. cplusplus = SCons.Tool.cxx
  51. #cplusplus = __import__('cxx', globals(), locals(), [])
  52. cxx_suffixes = cplusplus.CXXSuffixes
  53. def checkMocIncluded(target, source, env):
  54. moc = target[0]
  55. cpp = source[0]
  56. # looks like cpp.includes is cleared before the build stage :-(
  57. # not really sure about the path transformations (moc.cwd? cpp.cwd?) :-/
  58. path = SCons.Defaults.CScan.path(env, moc.cwd)
  59. includes = SCons.Defaults.CScan(cpp, env, path)
  60. if not moc in includes:
  61. SCons.Warnings.warn(
  62. GeneratedMocFileNotIncluded,
  63. "Generated moc file '%s' is not included by '%s'" %
  64. (str(moc), str(cpp)))
  65. def find_file(filename, paths, node_factory):
  66. for dir in paths:
  67. node = node_factory(filename, dir)
  68. if node.rexists():
  69. return node
  70. return None
  71. class _Automoc(object):
  72. """
  73. Callable class, which works as an emitter for Programs, SharedLibraries and
  74. StaticLibraries.
  75. """
  76. def __init__(self, objBuilderName):
  77. self.objBuilderName = objBuilderName
  78. def __call__(self, target, source, env):
  79. """
  80. Smart autoscan function. Gets the list of objects for the Program
  81. or Lib. Adds objects and builders for the special qt files.
  82. """
  83. try:
  84. if int(env.subst('$QT_AUTOSCAN')) == 0:
  85. return target, source
  86. except ValueError:
  87. pass
  88. try:
  89. debug = int(env.subst('$QT_DEBUG'))
  90. except ValueError:
  91. debug = 0
  92. # some shortcuts used in the scanner
  93. splitext = SCons.Util.splitext
  94. objBuilder = getattr(env, self.objBuilderName)
  95. # some regular expressions:
  96. # Q_OBJECT detection
  97. q_object_search = re.compile(r'[^A-Za-z0-9]Q_OBJECT[^A-Za-z0-9]')
  98. # cxx and c comment 'eater'
  99. #comment = re.compile(r'(//.*)|(/\*(([^*])|(\*[^/]))*\*/)')
  100. # CW: something must be wrong with the regexp. See also bug #998222
  101. # CURRENTLY THERE IS NO TEST CASE FOR THAT
  102. # The following is kind of hacky to get builders working properly (FIXME)
  103. objBuilderEnv = objBuilder.env
  104. objBuilder.env = env
  105. mocBuilderEnv = env.Moc.env
  106. env.Moc.env = env
  107. # make a deep copy for the result; MocH objects will be appended
  108. out_sources = source[:]
  109. for obj in source:
  110. if not obj.has_builder():
  111. # binary obj file provided
  112. if debug:
  113. print("scons: qt: '%s' seems to be a binary. Discarded." % str(obj))
  114. continue
  115. cpp = obj.sources[0]
  116. if not splitext(str(cpp))[1] in cxx_suffixes:
  117. if debug:
  118. print("scons: qt: '%s' is no cxx file. Discarded." % str(cpp))
  119. # c or fortran source
  120. continue
  121. #cpp_contents = comment.sub('', cpp.get_text_contents())
  122. if debug:
  123. print("scons: qt: Getting contents of %s" % cpp)
  124. cpp_contents = cpp.get_text_contents()
  125. h=None
  126. for h_ext in header_extensions:
  127. # try to find the header file in the corresponding source
  128. # directory
  129. hname = splitext(cpp.name)[0] + h_ext
  130. h = find_file(hname, (cpp.get_dir(),), env.File)
  131. if h:
  132. if debug:
  133. print("scons: qt: Scanning '%s' (header of '%s')" % (str(h), str(cpp)))
  134. #h_contents = comment.sub('', h.get_text_contents())
  135. h_contents = h.get_text_contents()
  136. break
  137. if not h and debug:
  138. print("scons: qt: no header for '%s'." % (str(cpp)))
  139. if h and q_object_search.search(h_contents):
  140. # h file with the Q_OBJECT macro found -> add moc_cpp
  141. moc_cpp = env.Moc(h)
  142. moc_o = objBuilder(moc_cpp)
  143. out_sources.append(moc_o)
  144. #moc_cpp.target_scanner = SCons.Defaults.CScan
  145. if debug:
  146. print("scons: qt: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(h), str(moc_cpp)))
  147. if cpp and q_object_search.search(cpp_contents):
  148. # cpp file with Q_OBJECT macro found -> add moc
  149. # (to be included in cpp)
  150. moc = env.Moc(cpp)
  151. env.Ignore(moc, moc)
  152. if debug:
  153. print("scons: qt: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(cpp), str(moc)))
  154. #moc.source_scanner = SCons.Defaults.CScan
  155. # restore the original env attributes (FIXME)
  156. objBuilder.env = objBuilderEnv
  157. env.Moc.env = mocBuilderEnv
  158. return (target, out_sources)
  159. AutomocShared = _Automoc('SharedObject')
  160. AutomocStatic = _Automoc('StaticObject')
  161. def _detect(env):
  162. """Not really safe, but fast method to detect the QT library"""
  163. QTDIR = None
  164. if not QTDIR:
  165. QTDIR = env.get('QTDIR',None)
  166. if not QTDIR:
  167. QTDIR = os.environ.get('QTDIR',None)
  168. if not QTDIR:
  169. moc = env.WhereIs('moc')
  170. if moc:
  171. QTDIR = os.path.dirname(os.path.dirname(moc))
  172. SCons.Warnings.warn(
  173. QtdirNotFound,
  174. "Could not detect qt, using moc executable as a hint (QTDIR=%s)" % QTDIR)
  175. else:
  176. QTDIR = None
  177. SCons.Warnings.warn(
  178. QtdirNotFound,
  179. "Could not detect qt, using empty QTDIR")
  180. return QTDIR
  181. def uicEmitter(target, source, env):
  182. adjustixes = SCons.Util.adjustixes
  183. bs = SCons.Util.splitext(str(source[0].name))[0]
  184. bs = os.path.join(str(target[0].get_dir()),bs)
  185. # first target (header) is automatically added by builder
  186. if len(target) < 2:
  187. # second target is implementation
  188. target.append(adjustixes(bs,
  189. env.subst('$QT_UICIMPLPREFIX'),
  190. env.subst('$QT_UICIMPLSUFFIX')))
  191. if len(target) < 3:
  192. # third target is moc file
  193. target.append(adjustixes(bs,
  194. env.subst('$QT_MOCHPREFIX'),
  195. env.subst('$QT_MOCHSUFFIX')))
  196. return target, source
  197. def uicScannerFunc(node, env, path):
  198. lookout = []
  199. lookout.extend(env['CPPPATH'])
  200. lookout.append(str(node.rfile().dir))
  201. includes = re.findall("<include.*?>(.*?)</include>", node.get_text_contents())
  202. result = []
  203. for incFile in includes:
  204. dep = env.FindFile(incFile,lookout)
  205. if dep:
  206. result.append(dep)
  207. return result
  208. uicScanner = SCons.Scanner.Base(uicScannerFunc,
  209. name = "UicScanner",
  210. node_class = SCons.Node.FS.File,
  211. node_factory = SCons.Node.FS.File,
  212. recursive = 0)
  213. def generate(env):
  214. """Add Builders and construction variables for qt to an Environment."""
  215. CLVar = SCons.Util.CLVar
  216. Action = SCons.Action.Action
  217. Builder = SCons.Builder.Builder
  218. env.SetDefault(QTDIR = _detect(env),
  219. QT_BINPATH = os.path.join('$QTDIR', 'bin'),
  220. QT_CPPPATH = os.path.join('$QTDIR', 'include'),
  221. QT_LIBPATH = os.path.join('$QTDIR', 'lib'),
  222. QT_MOC = os.path.join('$QT_BINPATH','moc'),
  223. QT_UIC = os.path.join('$QT_BINPATH','uic'),
  224. QT_LIB = 'qt', # may be set to qt-mt
  225. QT_AUTOSCAN = 1, # scan for moc'able sources
  226. # Some QT specific flags. I don't expect someone wants to
  227. # manipulate those ...
  228. QT_UICIMPLFLAGS = CLVar(''),
  229. QT_UICDECLFLAGS = CLVar(''),
  230. QT_MOCFROMHFLAGS = CLVar(''),
  231. QT_MOCFROMCXXFLAGS = CLVar('-i'),
  232. # suffixes/prefixes for the headers / sources to generate
  233. QT_UICDECLPREFIX = '',
  234. QT_UICDECLSUFFIX = '.h',
  235. QT_UICIMPLPREFIX = 'uic_',
  236. QT_UICIMPLSUFFIX = '$CXXFILESUFFIX',
  237. QT_MOCHPREFIX = 'moc_',
  238. QT_MOCHSUFFIX = '$CXXFILESUFFIX',
  239. QT_MOCCXXPREFIX = '',
  240. QT_MOCCXXSUFFIX = '.moc',
  241. QT_UISUFFIX = '.ui',
  242. # Commands for the qt support ...
  243. # command to generate header, implementation and moc-file
  244. # from a .ui file
  245. QT_UICCOM = [
  246. CLVar('$QT_UIC $QT_UICDECLFLAGS -o ${TARGETS[0]} $SOURCE'),
  247. CLVar('$QT_UIC $QT_UICIMPLFLAGS -impl ${TARGETS[0].file} '
  248. '-o ${TARGETS[1]} $SOURCE'),
  249. CLVar('$QT_MOC $QT_MOCFROMHFLAGS -o ${TARGETS[2]} ${TARGETS[0]}')],
  250. # command to generate meta object information for a class
  251. # declarated in a header
  252. QT_MOCFROMHCOM = (
  253. '$QT_MOC $QT_MOCFROMHFLAGS -o ${TARGETS[0]} $SOURCE'),
  254. # command to generate meta object information for a class
  255. # declarated in a cpp file
  256. QT_MOCFROMCXXCOM = [
  257. CLVar('$QT_MOC $QT_MOCFROMCXXFLAGS -o ${TARGETS[0]} $SOURCE'),
  258. Action(checkMocIncluded,None)])
  259. # ... and the corresponding builders
  260. uicBld = Builder(action=SCons.Action.Action('$QT_UICCOM', '$QT_UICCOMSTR'),
  261. emitter=uicEmitter,
  262. src_suffix='$QT_UISUFFIX',
  263. suffix='$QT_UICDECLSUFFIX',
  264. prefix='$QT_UICDECLPREFIX',
  265. source_scanner=uicScanner)
  266. mocBld = Builder(action={}, prefix={}, suffix={})
  267. for h in header_extensions:
  268. act = SCons.Action.Action('$QT_MOCFROMHCOM', '$QT_MOCFROMHCOMSTR')
  269. mocBld.add_action(h, act)
  270. mocBld.prefix[h] = '$QT_MOCHPREFIX'
  271. mocBld.suffix[h] = '$QT_MOCHSUFFIX'
  272. for cxx in cxx_suffixes:
  273. act = SCons.Action.Action('$QT_MOCFROMCXXCOM', '$QT_MOCFROMCXXCOMSTR')
  274. mocBld.add_action(cxx, act)
  275. mocBld.prefix[cxx] = '$QT_MOCCXXPREFIX'
  276. mocBld.suffix[cxx] = '$QT_MOCCXXSUFFIX'
  277. # register the builders
  278. env['BUILDERS']['Uic'] = uicBld
  279. env['BUILDERS']['Moc'] = mocBld
  280. static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
  281. static_obj.add_src_builder('Uic')
  282. shared_obj.add_src_builder('Uic')
  283. # We use the emitters of Program / StaticLibrary / SharedLibrary
  284. # to scan for moc'able files
  285. # We can't refer to the builders directly, we have to fetch them
  286. # as Environment attributes because that sets them up to be called
  287. # correctly later by our emitter.
  288. env.AppendUnique(PROGEMITTER =[AutomocStatic],
  289. SHLIBEMITTER=[AutomocShared],
  290. LDMODULEEMITTER=[AutomocShared],
  291. LIBEMITTER =[AutomocStatic],
  292. # Of course, we need to link against the qt libraries
  293. CPPPATH=["$QT_CPPPATH"],
  294. LIBPATH=["$QT_LIBPATH"],
  295. LIBS=['$QT_LIB'])
  296. def exists(env):
  297. return _detect(env)
  298. # Local Variables:
  299. # tab-width:4
  300. # indent-tabs-mode:nil
  301. # End:
  302. # vim: set expandtab tabstop=4 shiftwidth=4: