eclipse.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. #! /usr/bin/env python
  2. # encoding: utf-8
  3. # Eclipse CDT 5.0 generator for Waf
  4. # Richard Quirk 2009-1011 (New BSD License)
  5. # Thomas Nagy 2011 (ported to Waf 1.6)
  6. """
  7. Usage:
  8. def options(opt):
  9. opt.load('eclipse')
  10. $ waf configure eclipse
  11. """
  12. import sys, os
  13. from waflib import Utils, Logs, Context, Build, TaskGen, Scripting, Errors, Node
  14. from xml.dom.minidom import Document
  15. STANDARD_INCLUDES = [ '/usr/local/include', '/usr/include' ]
  16. oe_cdt = 'org.eclipse.cdt'
  17. cdt_mk = oe_cdt + '.make.core'
  18. cdt_core = oe_cdt + '.core'
  19. cdt_bld = oe_cdt + '.build.core'
  20. extbuilder_dir = '.externalToolBuilders'
  21. extbuilder_name = 'Waf_Builder.launch'
  22. class eclipse(Build.BuildContext):
  23. cmd = 'eclipse'
  24. fun = Scripting.default_cmd
  25. def execute(self):
  26. """
  27. Entry point
  28. """
  29. self.restore()
  30. if not self.all_envs:
  31. self.load_envs()
  32. self.recurse([self.run_dir])
  33. appname = getattr(Context.g_module, Context.APPNAME, os.path.basename(self.srcnode.abspath()))
  34. self.create_cproject(appname, pythonpath=self.env['ECLIPSE_PYTHON_PATH'])
  35. # Helper to dump the XML document content to XML with UTF-8 encoding
  36. def write_conf_to_xml(self, filename, document):
  37. self.srcnode.make_node(filename).write(document.toprettyxml(encoding='UTF-8'), flags='wb')
  38. def create_cproject(self, appname, workspace_includes=[], pythonpath=[]):
  39. """
  40. Create the Eclipse CDT .project and .cproject files
  41. @param appname The name that will appear in the Project Explorer
  42. @param build The BuildContext object to extract includes from
  43. @param workspace_includes Optional project includes to prevent
  44. "Unresolved Inclusion" errors in the Eclipse editor
  45. @param pythonpath Optional project specific python paths
  46. """
  47. hasc = hasjava = haspython = False
  48. source_dirs = []
  49. cpppath = self.env['CPPPATH']
  50. javasrcpath = []
  51. javalibpath = []
  52. includes = STANDARD_INCLUDES
  53. if sys.platform != 'win32':
  54. cc = self.env.CC or self.env.CXX
  55. if cc:
  56. cmd = cc + ['-xc++', '-E', '-Wp,-v', '-']
  57. try:
  58. gccout = self.cmd_and_log(cmd, output=Context.STDERR, quiet=Context.BOTH, input='\n'.encode()).splitlines()
  59. except Errors.WafError:
  60. pass
  61. else:
  62. includes = []
  63. for ipath in gccout:
  64. if ipath.startswith(' /'):
  65. includes.append(ipath[1:])
  66. cpppath += includes
  67. Logs.warn('Generating Eclipse CDT project files')
  68. for g in self.groups:
  69. for tg in g:
  70. if not isinstance(tg, TaskGen.task_gen):
  71. continue
  72. tg.post()
  73. # Add local Python modules paths to configuration so object resolving will work in IDE
  74. # This may also contain generated files (ie. pyqt5 or protoc) that get picked from build
  75. if 'py' in tg.features:
  76. pypath = tg.path.relpath()
  77. py_installfrom = getattr(tg, 'install_from', None)
  78. if isinstance(py_installfrom, Node.Node):
  79. pypath = py_installfrom.path_from(self.root.make_node(self.top_dir))
  80. if pypath not in pythonpath:
  81. pythonpath.append(pypath)
  82. haspython = True
  83. # Add Java source directories so object resolving works in IDE
  84. # This may also contain generated files (ie. protoc) that get picked from build
  85. if 'javac' in tg.features:
  86. java_src = tg.path.relpath()
  87. java_srcdir = getattr(tg.javac_task, 'srcdir', None)
  88. if java_srcdir:
  89. if isinstance(java_srcdir, Node.Node):
  90. java_srcdir = [java_srcdir]
  91. for x in Utils.to_list(java_srcdir):
  92. x = x.path_from(self.root.make_node(self.top_dir))
  93. if x not in javasrcpath:
  94. javasrcpath.append(x)
  95. else:
  96. if java_src not in javasrcpath:
  97. javasrcpath.append(java_src)
  98. hasjava = True
  99. # Check if there are external dependencies and add them as external jar so they will be resolved by Eclipse
  100. usedlibs=getattr(tg, 'use', [])
  101. for x in Utils.to_list(usedlibs):
  102. for cl in Utils.to_list(tg.env['CLASSPATH_'+x]):
  103. if cl not in javalibpath:
  104. javalibpath.append(cl)
  105. if not getattr(tg, 'link_task', None):
  106. continue
  107. features = Utils.to_list(getattr(tg, 'features', ''))
  108. is_cc = 'c' in features or 'cxx' in features
  109. incnodes = tg.to_incnodes(tg.to_list(getattr(tg, 'includes', [])) + tg.env['INCLUDES'])
  110. for p in incnodes:
  111. path = p.path_from(self.srcnode)
  112. if (path.startswith("/")):
  113. cpppath.append(path)
  114. else:
  115. workspace_includes.append(path)
  116. if is_cc and path not in source_dirs:
  117. source_dirs.append(path)
  118. hasc = True
  119. waf_executable = os.path.abspath(sys.argv[0])
  120. project = self.impl_create_project(sys.executable, appname, hasc, hasjava, haspython, waf_executable)
  121. self.write_conf_to_xml('.project', project)
  122. if hasc:
  123. project = self.impl_create_cproject(sys.executable, waf_executable, appname, workspace_includes, cpppath, source_dirs)
  124. self.write_conf_to_xml('.cproject', project)
  125. if haspython:
  126. project = self.impl_create_pydevproject(sys.path, pythonpath)
  127. self.write_conf_to_xml('.pydevproject', project)
  128. if hasjava:
  129. project = self.impl_create_javaproject(javasrcpath, javalibpath)
  130. self.write_conf_to_xml('.classpath', project)
  131. def impl_create_project(self, executable, appname, hasc, hasjava, haspython, waf_executable):
  132. doc = Document()
  133. projectDescription = doc.createElement('projectDescription')
  134. self.add(doc, projectDescription, 'name', appname)
  135. self.add(doc, projectDescription, 'comment')
  136. self.add(doc, projectDescription, 'projects')
  137. buildSpec = self.add(doc, projectDescription, 'buildSpec')
  138. buildCommand = self.add(doc, buildSpec, 'buildCommand')
  139. self.add(doc, buildCommand, 'triggers', 'clean,full,incremental,')
  140. arguments = self.add(doc, buildCommand, 'arguments')
  141. dictionaries = {}
  142. # If CDT is present, instruct this one to call waf as it is more flexible (separate build/clean ...)
  143. if hasc:
  144. self.add(doc, buildCommand, 'name', oe_cdt + '.managedbuilder.core.genmakebuilder')
  145. # the default make-style targets are overwritten by the .cproject values
  146. dictionaries = {
  147. cdt_mk + '.contents': cdt_mk + '.activeConfigSettings',
  148. cdt_mk + '.enableAutoBuild': 'false',
  149. cdt_mk + '.enableCleanBuild': 'true',
  150. cdt_mk + '.enableFullBuild': 'true',
  151. }
  152. else:
  153. # Otherwise for Java/Python an external builder tool is created that will call waf build
  154. self.add(doc, buildCommand, 'name', 'org.eclipse.ui.externaltools.ExternalToolBuilder')
  155. dictionaries = {
  156. 'LaunchConfigHandle': '<project>/%s/%s'%(extbuilder_dir, extbuilder_name),
  157. }
  158. # The definition is in a separate directory XML file
  159. try:
  160. os.mkdir(extbuilder_dir)
  161. except OSError:
  162. pass # Ignore error if already exists
  163. # Populate here the external builder XML calling waf
  164. builder = Document()
  165. launchConfiguration = doc.createElement('launchConfiguration')
  166. launchConfiguration.setAttribute('type', 'org.eclipse.ui.externaltools.ProgramBuilderLaunchConfigurationType')
  167. self.add(doc, launchConfiguration, 'booleanAttribute', {'key': 'org.eclipse.debug.ui.ATTR_LAUNCH_IN_BACKGROUND', 'value': 'false'})
  168. self.add(doc, launchConfiguration, 'booleanAttribute', {'key': 'org.eclipse.ui.externaltools.ATTR_TRIGGERS_CONFIGURED', 'value': 'true'})
  169. self.add(doc, launchConfiguration, 'stringAttribute', {'key': 'org.eclipse.ui.externaltools.ATTR_LOCATION', 'value': waf_executable})
  170. self.add(doc, launchConfiguration, 'stringAttribute', {'key': 'org.eclipse.ui.externaltools.ATTR_RUN_BUILD_KINDS', 'value': 'full,incremental,'})
  171. self.add(doc, launchConfiguration, 'stringAttribute', {'key': 'org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS', 'value': 'build'})
  172. self.add(doc, launchConfiguration, 'stringAttribute', {'key': 'org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY', 'value': '${project_loc}'})
  173. builder.appendChild(launchConfiguration)
  174. # And write the XML to the file references before
  175. self.write_conf_to_xml('%s%s%s'%(extbuilder_dir, os.path.sep, extbuilder_name), builder)
  176. for k, v in dictionaries.items():
  177. self.addDictionary(doc, arguments, k, v)
  178. natures = self.add(doc, projectDescription, 'natures')
  179. if hasc:
  180. nature_list = """
  181. core.ccnature
  182. managedbuilder.core.ScannerConfigNature
  183. managedbuilder.core.managedBuildNature
  184. core.cnature
  185. """.split()
  186. for n in nature_list:
  187. self.add(doc, natures, 'nature', oe_cdt + '.' + n)
  188. if haspython:
  189. self.add(doc, natures, 'nature', 'org.python.pydev.pythonNature')
  190. if hasjava:
  191. self.add(doc, natures, 'nature', 'org.eclipse.jdt.core.javanature')
  192. doc.appendChild(projectDescription)
  193. return doc
  194. def impl_create_cproject(self, executable, waf_executable, appname, workspace_includes, cpppath, source_dirs=[]):
  195. doc = Document()
  196. doc.appendChild(doc.createProcessingInstruction('fileVersion', '4.0.0'))
  197. cconf_id = cdt_core + '.default.config.1'
  198. cproject = doc.createElement('cproject')
  199. storageModule = self.add(doc, cproject, 'storageModule',
  200. {'moduleId': cdt_core + '.settings'})
  201. cconf = self.add(doc, storageModule, 'cconfiguration', {'id':cconf_id})
  202. storageModule = self.add(doc, cconf, 'storageModule',
  203. {'buildSystemId': oe_cdt + '.managedbuilder.core.configurationDataProvider',
  204. 'id': cconf_id,
  205. 'moduleId': cdt_core + '.settings',
  206. 'name': 'Default'})
  207. self.add(doc, storageModule, 'externalSettings')
  208. extensions = self.add(doc, storageModule, 'extensions')
  209. extension_list = """
  210. VCErrorParser
  211. MakeErrorParser
  212. GCCErrorParser
  213. GASErrorParser
  214. GLDErrorParser
  215. """.split()
  216. self.add(doc, extensions, 'extension', {'id': cdt_core + '.ELF', 'point':cdt_core + '.BinaryParser'})
  217. for e in extension_list:
  218. self.add(doc, extensions, 'extension', {'id': cdt_core + '.' + e, 'point':cdt_core + '.ErrorParser'})
  219. storageModule = self.add(doc, cconf, 'storageModule',
  220. {'moduleId': 'cdtBuildSystem', 'version': '4.0.0'})
  221. config = self.add(doc, storageModule, 'configuration',
  222. {'artifactName': appname,
  223. 'id': cconf_id,
  224. 'name': 'Default',
  225. 'parent': cdt_bld + '.prefbase.cfg'})
  226. folderInfo = self.add(doc, config, 'folderInfo',
  227. {'id': cconf_id+'.', 'name': '/', 'resourcePath': ''})
  228. toolChain = self.add(doc, folderInfo, 'toolChain',
  229. {'id': cdt_bld + '.prefbase.toolchain.1',
  230. 'name': 'No ToolChain',
  231. 'resourceTypeBasedDiscovery': 'false',
  232. 'superClass': cdt_bld + '.prefbase.toolchain'})
  233. self.add(doc, toolChain, 'targetPlatform', {'binaryParser': 'org.eclipse.cdt.core.ELF', 'id': cdt_bld + '.prefbase.toolchain.1', 'name': ''})
  234. waf_build = '"%s" %s'%(waf_executable, eclipse.fun)
  235. waf_clean = '"%s" clean'%(waf_executable)
  236. self.add(doc, toolChain, 'builder',
  237. {'autoBuildTarget': waf_build,
  238. 'command': executable,
  239. 'enableAutoBuild': 'false',
  240. 'cleanBuildTarget': waf_clean,
  241. 'enableIncrementalBuild': 'true',
  242. 'id': cdt_bld + '.settings.default.builder.1',
  243. 'incrementalBuildTarget': waf_build,
  244. 'managedBuildOn': 'false',
  245. 'name': 'Gnu Make Builder',
  246. 'superClass': cdt_bld + '.settings.default.builder'})
  247. tool_index = 1;
  248. for tool_name in ("Assembly", "GNU C++", "GNU C"):
  249. tool = self.add(doc, toolChain, 'tool',
  250. {'id': cdt_bld + '.settings.holder.' + str(tool_index),
  251. 'name': tool_name,
  252. 'superClass': cdt_bld + '.settings.holder'})
  253. if cpppath or workspace_includes:
  254. incpaths = cdt_bld + '.settings.holder.incpaths'
  255. option = self.add(doc, tool, 'option',
  256. {'id': incpaths + '.' + str(tool_index),
  257. 'name': 'Include Paths',
  258. 'superClass': incpaths,
  259. 'valueType': 'includePath'})
  260. for i in workspace_includes:
  261. self.add(doc, option, 'listOptionValue',
  262. {'builtIn': 'false',
  263. 'value': '"${workspace_loc:/%s/%s}"'%(appname, i)})
  264. for i in cpppath:
  265. self.add(doc, option, 'listOptionValue',
  266. {'builtIn': 'false',
  267. 'value': '"%s"'%(i)})
  268. if tool_name == "GNU C++" or tool_name == "GNU C":
  269. self.add(doc,tool,'inputType',{ 'id':'org.eclipse.cdt.build.core.settings.holder.inType.' + str(tool_index), \
  270. 'languageId':'org.eclipse.cdt.core.gcc' if tool_name == "GNU C" else 'org.eclipse.cdt.core.g++','languageName':tool_name, \
  271. 'sourceContentType':'org.eclipse.cdt.core.cSource,org.eclipse.cdt.core.cHeader', \
  272. 'superClass':'org.eclipse.cdt.build.core.settings.holder.inType' })
  273. tool_index += 1
  274. if source_dirs:
  275. sourceEntries = self.add(doc, config, 'sourceEntries')
  276. for i in source_dirs:
  277. self.add(doc, sourceEntries, 'entry',
  278. {'excluding': i,
  279. 'flags': 'VALUE_WORKSPACE_PATH|RESOLVED',
  280. 'kind': 'sourcePath',
  281. 'name': ''})
  282. self.add(doc, sourceEntries, 'entry',
  283. {
  284. 'flags': 'VALUE_WORKSPACE_PATH|RESOLVED',
  285. 'kind': 'sourcePath',
  286. 'name': i})
  287. storageModule = self.add(doc, cconf, 'storageModule',
  288. {'moduleId': cdt_mk + '.buildtargets'})
  289. buildTargets = self.add(doc, storageModule, 'buildTargets')
  290. def addTargetWrap(name, runAll):
  291. return self.addTarget(doc, buildTargets, executable, name,
  292. '"%s" %s'%(waf_executable, name), runAll)
  293. addTargetWrap('configure', True)
  294. addTargetWrap('dist', False)
  295. addTargetWrap('install', False)
  296. addTargetWrap('check', False)
  297. storageModule = self.add(doc, cproject, 'storageModule',
  298. {'moduleId': 'cdtBuildSystem',
  299. 'version': '4.0.0'})
  300. self.add(doc, storageModule, 'project', {'id': '%s.null.1'%appname, 'name': appname})
  301. doc.appendChild(cproject)
  302. return doc
  303. def impl_create_pydevproject(self, system_path, user_path):
  304. # create a pydevproject file
  305. doc = Document()
  306. doc.appendChild(doc.createProcessingInstruction('eclipse-pydev', 'version="1.0"'))
  307. pydevproject = doc.createElement('pydev_project')
  308. prop = self.add(doc, pydevproject,
  309. 'pydev_property',
  310. 'python %d.%d'%(sys.version_info[0], sys.version_info[1]))
  311. prop.setAttribute('name', 'org.python.pydev.PYTHON_PROJECT_VERSION')
  312. prop = self.add(doc, pydevproject, 'pydev_property', 'Default')
  313. prop.setAttribute('name', 'org.python.pydev.PYTHON_PROJECT_INTERPRETER')
  314. # add waf's paths
  315. wafadmin = [p for p in system_path if p.find('wafadmin') != -1]
  316. if wafadmin:
  317. prop = self.add(doc, pydevproject, 'pydev_pathproperty',
  318. {'name':'org.python.pydev.PROJECT_EXTERNAL_SOURCE_PATH'})
  319. for i in wafadmin:
  320. self.add(doc, prop, 'path', i)
  321. if user_path:
  322. prop = self.add(doc, pydevproject, 'pydev_pathproperty',
  323. {'name':'org.python.pydev.PROJECT_SOURCE_PATH'})
  324. for i in user_path:
  325. self.add(doc, prop, 'path', '/${PROJECT_DIR_NAME}/'+i)
  326. doc.appendChild(pydevproject)
  327. return doc
  328. def impl_create_javaproject(self, javasrcpath, javalibpath):
  329. # create a .classpath file for java usage
  330. doc = Document()
  331. javaproject = doc.createElement('classpath')
  332. if javasrcpath:
  333. for i in javasrcpath:
  334. self.add(doc, javaproject, 'classpathentry',
  335. {'kind': 'src', 'path': i})
  336. if javalibpath:
  337. for i in javalibpath:
  338. self.add(doc, javaproject, 'classpathentry',
  339. {'kind': 'lib', 'path': i})
  340. self.add(doc, javaproject, 'classpathentry', {'kind': 'con', 'path': 'org.eclipse.jdt.launching.JRE_CONTAINER'})
  341. self.add(doc, javaproject, 'classpathentry', {'kind': 'output', 'path': self.bldnode.name })
  342. doc.appendChild(javaproject)
  343. return doc
  344. def addDictionary(self, doc, parent, k, v):
  345. dictionary = self.add(doc, parent, 'dictionary')
  346. self.add(doc, dictionary, 'key', k)
  347. self.add(doc, dictionary, 'value', v)
  348. return dictionary
  349. def addTarget(self, doc, buildTargets, executable, name, buildTarget, runAllBuilders=True):
  350. target = self.add(doc, buildTargets, 'target',
  351. {'name': name,
  352. 'path': '',
  353. 'targetID': oe_cdt + '.build.MakeTargetBuilder'})
  354. self.add(doc, target, 'buildCommand', executable)
  355. self.add(doc, target, 'buildArguments', None)
  356. self.add(doc, target, 'buildTarget', buildTarget)
  357. self.add(doc, target, 'stopOnError', 'true')
  358. self.add(doc, target, 'useDefaultCommand', 'false')
  359. self.add(doc, target, 'runAllBuilders', str(runAllBuilders).lower())
  360. def add(self, doc, parent, tag, value = None):
  361. el = doc.createElement(tag)
  362. if (value):
  363. if type(value) == type(str()):
  364. el.appendChild(doc.createTextNode(value))
  365. elif type(value) == type(dict()):
  366. self.setAttributes(el, value)
  367. parent.appendChild(el)
  368. return el
  369. def setAttributes(self, node, attrs):
  370. for k, v in attrs.items():
  371. node.setAttribute(k, v)