third_party: Update waf to verison 2.0.23
[Samba.git] / third_party / waf / waflib / extras / eclipse.py
blob49ca9686b7b6a5db671db6f5c676d347f69e3ffe
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)
7 """
8 Usage:
10 def options(opt):
11 opt.load('eclipse')
13 To add additional targets beside standard ones (configure, dist, install, check)
14 the environment ECLIPSE_EXTRA_TARGETS can be set (ie. to ['test', 'lint', 'docs'])
16 $ waf configure eclipse
17 """
19 import sys, os
20 from waflib import Utils, Logs, Context, Build, TaskGen, Scripting, Errors, Node
21 from xml.dom.minidom import Document
23 STANDARD_INCLUDES = [ '/usr/local/include', '/usr/include' ]
25 oe_cdt = 'org.eclipse.cdt'
26 cdt_mk = oe_cdt + '.make.core'
27 cdt_core = oe_cdt + '.core'
28 cdt_bld = oe_cdt + '.build.core'
29 extbuilder_dir = '.externalToolBuilders'
30 extbuilder_name = 'Waf_Builder.launch'
31 settings_dir = '.settings'
32 settings_name = 'language.settings.xml'
34 class eclipse(Build.BuildContext):
35 cmd = 'eclipse'
36 fun = Scripting.default_cmd
38 def execute(self):
39 """
40 Entry point
41 """
42 self.restore()
43 if not self.all_envs:
44 self.load_envs()
45 self.recurse([self.run_dir])
47 appname = getattr(Context.g_module, Context.APPNAME, os.path.basename(self.srcnode.abspath()))
48 self.create_cproject(appname, pythonpath=self.env['ECLIPSE_PYTHON_PATH'])
50 # Helper to dump the XML document content to XML with UTF-8 encoding
51 def write_conf_to_xml(self, filename, document):
52 self.srcnode.make_node(filename).write(document.toprettyxml(encoding='UTF-8'), flags='wb')
54 def create_cproject(self, appname, workspace_includes=[], pythonpath=[]):
55 """
56 Create the Eclipse CDT .project and .cproject files
57 @param appname The name that will appear in the Project Explorer
58 @param build The BuildContext object to extract includes from
59 @param workspace_includes Optional project includes to prevent
60 "Unresolved Inclusion" errors in the Eclipse editor
61 @param pythonpath Optional project specific python paths
62 """
63 hasc = hasjava = haspython = False
64 source_dirs = []
65 cpppath = self.env['CPPPATH']
66 javasrcpath = []
67 javalibpath = []
68 includes = STANDARD_INCLUDES
69 if sys.platform != 'win32':
70 cc = self.env.CC or self.env.CXX
71 if cc:
72 cmd = cc + ['-xc++', '-E', '-Wp,-v', '-']
73 try:
74 gccout = self.cmd_and_log(cmd, output=Context.STDERR, quiet=Context.BOTH, input='\n'.encode()).splitlines()
75 except Errors.WafError:
76 pass
77 else:
78 includes = []
79 for ipath in gccout:
80 if ipath.startswith(' /'):
81 includes.append(ipath[1:])
82 cpppath += includes
83 Logs.warn('Generating Eclipse CDT project files')
85 for g in self.groups:
86 for tg in g:
87 if not isinstance(tg, TaskGen.task_gen):
88 continue
90 tg.post()
92 # Add local Python modules paths to configuration so object resolving will work in IDE
93 # This may also contain generated files (ie. pyqt5 or protoc) that get picked from build
94 if 'py' in tg.features:
95 pypath = tg.path.relpath()
96 py_installfrom = getattr(tg, 'install_from', None)
97 if isinstance(py_installfrom, Node.Node):
98 pypath = py_installfrom.path_from(self.root.make_node(self.top_dir))
99 if pypath not in pythonpath:
100 pythonpath.append(pypath)
101 haspython = True
103 # Add Java source directories so object resolving works in IDE
104 # This may also contain generated files (ie. protoc) that get picked from build
105 if 'javac' in tg.features:
106 java_src = tg.path.relpath()
107 java_srcdir = getattr(tg.javac_task, 'srcdir', None)
108 if java_srcdir:
109 if isinstance(java_srcdir, Node.Node):
110 java_srcdir = [java_srcdir]
111 for x in Utils.to_list(java_srcdir):
112 x = x.path_from(self.root.make_node(self.top_dir))
113 if x not in javasrcpath:
114 javasrcpath.append(x)
115 else:
116 if java_src not in javasrcpath:
117 javasrcpath.append(java_src)
118 hasjava = True
120 # Check if there are external dependencies and add them as external jar so they will be resolved by Eclipse
121 usedlibs=getattr(tg, 'use', [])
122 for x in Utils.to_list(usedlibs):
123 for cl in Utils.to_list(tg.env['CLASSPATH_'+x]):
124 if cl not in javalibpath:
125 javalibpath.append(cl)
127 if not getattr(tg, 'link_task', None):
128 continue
130 features = Utils.to_list(getattr(tg, 'features', ''))
132 is_cc = 'c' in features or 'cxx' in features
134 incnodes = tg.to_incnodes(tg.to_list(getattr(tg, 'includes', [])) + tg.env['INCLUDES'])
135 for p in incnodes:
136 path = p.path_from(self.srcnode)
138 if (path.startswith("/")):
139 if path not in cpppath:
140 cpppath.append(path)
141 else:
142 if path not in workspace_includes:
143 workspace_includes.append(path)
145 if is_cc and path not in source_dirs:
146 source_dirs.append(path)
148 hasc = True
150 waf_executable = os.path.abspath(sys.argv[0])
151 project = self.impl_create_project(sys.executable, appname, hasc, hasjava, haspython, waf_executable)
152 self.write_conf_to_xml('.project', project)
154 if hasc:
155 project = self.impl_create_cproject(sys.executable, waf_executable, appname, workspace_includes, cpppath, source_dirs)
156 self.write_conf_to_xml('.cproject', project)
158 if haspython:
159 project = self.impl_create_pydevproject(sys.path, pythonpath)
160 self.write_conf_to_xml('.pydevproject', project)
162 if hasjava:
163 project = self.impl_create_javaproject(javasrcpath, javalibpath)
164 self.write_conf_to_xml('.classpath', project)
166 # Create editor language settings to have correct standards applied in IDE, as per project configuration
167 try:
168 os.mkdir(settings_dir)
169 except OSError:
170 pass # Ignore if dir already exists
172 lang_settings = Document()
173 project = lang_settings.createElement('project')
175 # Language configurations for C and C++ via cdt
176 if hasc:
177 configuration = self.add(lang_settings, project, 'configuration',
178 {'id' : 'org.eclipse.cdt.core.default.config.1', 'name': 'Default'})
180 extension = self.add(lang_settings, configuration, 'extension', {'point': 'org.eclipse.cdt.core.LanguageSettingsProvider'})
182 provider = self.add(lang_settings, extension, 'provider',
183 { 'copy-of': 'extension',
184 'id': 'org.eclipse.cdt.ui.UserLanguageSettingsProvider'})
186 provider = self.add(lang_settings, extension, 'provider-reference',
187 { 'id': 'org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider',
188 'ref': 'shared-provider'})
190 provider = self.add(lang_settings, extension, 'provider-reference',
191 { 'id': 'org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider',
192 'ref': 'shared-provider'})
194 # C and C++ are kept as separated providers so appropriate flags are used also in mixed projects
195 if self.env.CC:
196 provider = self.add(lang_settings, extension, 'provider',
197 { 'class': 'org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuiltinSpecsDetector',
198 'console': 'false',
199 'id': 'org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuiltinSpecsDetector.1',
200 'keep-relative-paths' : 'false',
201 'name': 'CDT GCC Built-in Compiler Settings',
202 'parameter': '%s %s ${FLAGS} -E -P -v -dD "${INPUTS}"'%(self.env.CC[0],' '.join(self.env['CFLAGS'])),
203 'prefer-non-shared': 'true' })
205 self.add(lang_settings, provider, 'language-scope', { 'id': 'org.eclipse.cdt.core.gcc'})
207 if self.env.CXX:
208 provider = self.add(lang_settings, extension, 'provider',
209 { 'class': 'org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuiltinSpecsDetector',
210 'console': 'false',
211 'id': 'org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuiltinSpecsDetector.2',
212 'keep-relative-paths' : 'false',
213 'name': 'CDT GCC Built-in Compiler Settings',
214 'parameter': '%s %s ${FLAGS} -E -P -v -dD "${INPUTS}"'%(self.env.CXX[0],' '.join(self.env['CXXFLAGS'])),
215 'prefer-non-shared': 'true' })
216 self.add(lang_settings, provider, 'language-scope', { 'id': 'org.eclipse.cdt.core.g++'})
218 lang_settings.appendChild(project)
219 self.write_conf_to_xml('%s%s%s'%(settings_dir, os.path.sep, settings_name), lang_settings)
221 def impl_create_project(self, executable, appname, hasc, hasjava, haspython, waf_executable):
222 doc = Document()
223 projectDescription = doc.createElement('projectDescription')
224 self.add(doc, projectDescription, 'name', appname)
225 self.add(doc, projectDescription, 'comment')
226 self.add(doc, projectDescription, 'projects')
227 buildSpec = self.add(doc, projectDescription, 'buildSpec')
228 buildCommand = self.add(doc, buildSpec, 'buildCommand')
229 self.add(doc, buildCommand, 'triggers', 'clean,full,incremental,')
230 arguments = self.add(doc, buildCommand, 'arguments')
231 dictionaries = {}
233 # If CDT is present, instruct this one to call waf as it is more flexible (separate build/clean ...)
234 if hasc:
235 self.add(doc, buildCommand, 'name', oe_cdt + '.managedbuilder.core.genmakebuilder')
236 # the default make-style targets are overwritten by the .cproject values
237 dictionaries = {
238 cdt_mk + '.contents': cdt_mk + '.activeConfigSettings',
239 cdt_mk + '.enableAutoBuild': 'false',
240 cdt_mk + '.enableCleanBuild': 'true',
241 cdt_mk + '.enableFullBuild': 'true',
243 else:
244 # Otherwise for Java/Python an external builder tool is created that will call waf build
245 self.add(doc, buildCommand, 'name', 'org.eclipse.ui.externaltools.ExternalToolBuilder')
246 dictionaries = {
247 'LaunchConfigHandle': '<project>/%s/%s'%(extbuilder_dir, extbuilder_name),
249 # The definition is in a separate directory XML file
250 try:
251 os.mkdir(extbuilder_dir)
252 except OSError:
253 pass # Ignore error if already exists
255 # Populate here the external builder XML calling waf
256 builder = Document()
257 launchConfiguration = doc.createElement('launchConfiguration')
258 launchConfiguration.setAttribute('type', 'org.eclipse.ui.externaltools.ProgramBuilderLaunchConfigurationType')
259 self.add(doc, launchConfiguration, 'booleanAttribute', {'key': 'org.eclipse.debug.ui.ATTR_LAUNCH_IN_BACKGROUND', 'value': 'false'})
260 self.add(doc, launchConfiguration, 'booleanAttribute', {'key': 'org.eclipse.ui.externaltools.ATTR_TRIGGERS_CONFIGURED', 'value': 'true'})
261 self.add(doc, launchConfiguration, 'stringAttribute', {'key': 'org.eclipse.ui.externaltools.ATTR_LOCATION', 'value': waf_executable})
262 self.add(doc, launchConfiguration, 'stringAttribute', {'key': 'org.eclipse.ui.externaltools.ATTR_RUN_BUILD_KINDS', 'value': 'full,incremental,'})
263 self.add(doc, launchConfiguration, 'stringAttribute', {'key': 'org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS', 'value': 'build'})
264 self.add(doc, launchConfiguration, 'stringAttribute', {'key': 'org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY', 'value': '${project_loc}'})
265 builder.appendChild(launchConfiguration)
266 # And write the XML to the file references before
267 self.write_conf_to_xml('%s%s%s'%(extbuilder_dir, os.path.sep, extbuilder_name), builder)
270 for k, v in dictionaries.items():
271 self.addDictionary(doc, arguments, k, v)
273 natures = self.add(doc, projectDescription, 'natures')
275 if hasc:
276 nature_list = """
277 core.ccnature
278 managedbuilder.core.ScannerConfigNature
279 managedbuilder.core.managedBuildNature
280 core.cnature
281 """.split()
282 for n in nature_list:
283 self.add(doc, natures, 'nature', oe_cdt + '.' + n)
285 if haspython:
286 self.add(doc, natures, 'nature', 'org.python.pydev.pythonNature')
287 if hasjava:
288 self.add(doc, natures, 'nature', 'org.eclipse.jdt.core.javanature')
290 doc.appendChild(projectDescription)
291 return doc
293 def impl_create_cproject(self, executable, waf_executable, appname, workspace_includes, cpppath, source_dirs=[]):
294 doc = Document()
295 doc.appendChild(doc.createProcessingInstruction('fileVersion', '4.0.0'))
296 cconf_id = cdt_core + '.default.config.1'
297 cproject = doc.createElement('cproject')
298 storageModule = self.add(doc, cproject, 'storageModule',
299 {'moduleId': cdt_core + '.settings'})
300 cconf = self.add(doc, storageModule, 'cconfiguration', {'id':cconf_id})
302 storageModule = self.add(doc, cconf, 'storageModule',
303 {'buildSystemId': oe_cdt + '.managedbuilder.core.configurationDataProvider',
304 'id': cconf_id,
305 'moduleId': cdt_core + '.settings',
306 'name': 'Default'})
308 self.add(doc, storageModule, 'externalSettings')
310 extensions = self.add(doc, storageModule, 'extensions')
311 extension_list = """
312 VCErrorParser
313 MakeErrorParser
314 GCCErrorParser
315 GASErrorParser
316 GLDErrorParser
317 """.split()
318 self.add(doc, extensions, 'extension', {'id': cdt_core + '.ELF', 'point':cdt_core + '.BinaryParser'})
319 for e in extension_list:
320 self.add(doc, extensions, 'extension', {'id': cdt_core + '.' + e, 'point':cdt_core + '.ErrorParser'})
322 storageModule = self.add(doc, cconf, 'storageModule',
323 {'moduleId': 'cdtBuildSystem', 'version': '4.0.0'})
324 config = self.add(doc, storageModule, 'configuration',
325 {'artifactName': appname,
326 'id': cconf_id,
327 'name': 'Default',
328 'parent': cdt_bld + '.prefbase.cfg'})
329 folderInfo = self.add(doc, config, 'folderInfo',
330 {'id': cconf_id+'.', 'name': '/', 'resourcePath': ''})
332 toolChain = self.add(doc, folderInfo, 'toolChain',
333 {'id': cdt_bld + '.prefbase.toolchain.1',
334 'name': 'No ToolChain',
335 'resourceTypeBasedDiscovery': 'false',
336 'superClass': cdt_bld + '.prefbase.toolchain'})
338 self.add(doc, toolChain, 'targetPlatform', {'binaryParser': 'org.eclipse.cdt.core.ELF', 'id': cdt_bld + '.prefbase.toolchain.1', 'name': ''})
340 waf_build = '"%s" %s'%(waf_executable, eclipse.fun)
341 waf_clean = '"%s" clean'%(waf_executable)
342 self.add(doc, toolChain, 'builder',
343 {'autoBuildTarget': waf_build,
344 'command': executable,
345 'enableAutoBuild': 'false',
346 'cleanBuildTarget': waf_clean,
347 'enableIncrementalBuild': 'true',
348 'id': cdt_bld + '.settings.default.builder.1',
349 'incrementalBuildTarget': waf_build,
350 'managedBuildOn': 'false',
351 'name': 'Gnu Make Builder',
352 'superClass': cdt_bld + '.settings.default.builder'})
354 tool_index = 1;
355 for tool_name in ("Assembly", "GNU C++", "GNU C"):
356 tool = self.add(doc, toolChain, 'tool',
357 {'id': cdt_bld + '.settings.holder.' + str(tool_index),
358 'name': tool_name,
359 'superClass': cdt_bld + '.settings.holder'})
360 if cpppath or workspace_includes:
361 incpaths = cdt_bld + '.settings.holder.incpaths'
362 option = self.add(doc, tool, 'option',
363 {'id': incpaths + '.' + str(tool_index),
364 'name': 'Include Paths',
365 'superClass': incpaths,
366 'valueType': 'includePath'})
367 for i in workspace_includes:
368 self.add(doc, option, 'listOptionValue',
369 {'builtIn': 'false',
370 'value': '"${workspace_loc:/%s/%s}"'%(appname, i)})
371 for i in cpppath:
372 self.add(doc, option, 'listOptionValue',
373 {'builtIn': 'false',
374 'value': '"%s"'%(i)})
375 if tool_name == "GNU C++" or tool_name == "GNU C":
376 self.add(doc,tool,'inputType',{ 'id':'org.eclipse.cdt.build.core.settings.holder.inType.' + str(tool_index), \
377 'languageId':'org.eclipse.cdt.core.gcc' if tool_name == "GNU C" else 'org.eclipse.cdt.core.g++','languageName':tool_name, \
378 'sourceContentType':'org.eclipse.cdt.core.cSource,org.eclipse.cdt.core.cHeader', \
379 'superClass':'org.eclipse.cdt.build.core.settings.holder.inType' })
380 tool_index += 1
382 if source_dirs:
383 sourceEntries = self.add(doc, config, 'sourceEntries')
384 for i in source_dirs:
385 self.add(doc, sourceEntries, 'entry',
386 {'excluding': i,
387 'flags': 'VALUE_WORKSPACE_PATH|RESOLVED',
388 'kind': 'sourcePath',
389 'name': ''})
390 self.add(doc, sourceEntries, 'entry',
392 'flags': 'VALUE_WORKSPACE_PATH|RESOLVED',
393 'kind': 'sourcePath',
394 'name': i})
396 storageModule = self.add(doc, cconf, 'storageModule',
397 {'moduleId': cdt_mk + '.buildtargets'})
398 buildTargets = self.add(doc, storageModule, 'buildTargets')
399 def addTargetWrap(name, runAll):
400 return self.addTarget(doc, buildTargets, executable, name,
401 '"%s" %s'%(waf_executable, name), runAll)
402 addTargetWrap('configure', True)
403 addTargetWrap('dist', False)
404 addTargetWrap('install', False)
405 addTargetWrap('check', False)
406 for addTgt in self.env.ECLIPSE_EXTRA_TARGETS or []:
407 addTargetWrap(addTgt, False)
409 storageModule = self.add(doc, cproject, 'storageModule',
410 {'moduleId': 'cdtBuildSystem',
411 'version': '4.0.0'})
413 self.add(doc, storageModule, 'project', {'id': '%s.null.1'%appname, 'name': appname})
415 storageModule = self.add(doc, cproject, 'storageModule',
416 {'moduleId': 'org.eclipse.cdt.core.LanguageSettingsProviders'})
418 storageModule = self.add(doc, cproject, 'storageModule',
419 {'moduleId': 'scannerConfiguration'})
421 doc.appendChild(cproject)
422 return doc
424 def impl_create_pydevproject(self, system_path, user_path):
425 # create a pydevproject file
426 doc = Document()
427 doc.appendChild(doc.createProcessingInstruction('eclipse-pydev', 'version="1.0"'))
428 pydevproject = doc.createElement('pydev_project')
429 prop = self.add(doc, pydevproject,
430 'pydev_property',
431 'python %d.%d'%(sys.version_info[0], sys.version_info[1]))
432 prop.setAttribute('name', 'org.python.pydev.PYTHON_PROJECT_VERSION')
433 prop = self.add(doc, pydevproject, 'pydev_property', 'Default')
434 prop.setAttribute('name', 'org.python.pydev.PYTHON_PROJECT_INTERPRETER')
435 # add waf's paths
436 wafadmin = [p for p in system_path if p.find('wafadmin') != -1]
437 if wafadmin:
438 prop = self.add(doc, pydevproject, 'pydev_pathproperty',
439 {'name':'org.python.pydev.PROJECT_EXTERNAL_SOURCE_PATH'})
440 for i in wafadmin:
441 self.add(doc, prop, 'path', i)
442 if user_path:
443 prop = self.add(doc, pydevproject, 'pydev_pathproperty',
444 {'name':'org.python.pydev.PROJECT_SOURCE_PATH'})
445 for i in user_path:
446 self.add(doc, prop, 'path', '/${PROJECT_DIR_NAME}/'+i)
448 doc.appendChild(pydevproject)
449 return doc
451 def impl_create_javaproject(self, javasrcpath, javalibpath):
452 # create a .classpath file for java usage
453 doc = Document()
454 javaproject = doc.createElement('classpath')
455 if javasrcpath:
456 for i in javasrcpath:
457 self.add(doc, javaproject, 'classpathentry',
458 {'kind': 'src', 'path': i})
460 if javalibpath:
461 for i in javalibpath:
462 self.add(doc, javaproject, 'classpathentry',
463 {'kind': 'lib', 'path': i})
465 self.add(doc, javaproject, 'classpathentry', {'kind': 'con', 'path': 'org.eclipse.jdt.launching.JRE_CONTAINER'})
466 self.add(doc, javaproject, 'classpathentry', {'kind': 'output', 'path': self.bldnode.name })
467 doc.appendChild(javaproject)
468 return doc
470 def addDictionary(self, doc, parent, k, v):
471 dictionary = self.add(doc, parent, 'dictionary')
472 self.add(doc, dictionary, 'key', k)
473 self.add(doc, dictionary, 'value', v)
474 return dictionary
476 def addTarget(self, doc, buildTargets, executable, name, buildTarget, runAllBuilders=True):
477 target = self.add(doc, buildTargets, 'target',
478 {'name': name,
479 'path': '',
480 'targetID': oe_cdt + '.build.MakeTargetBuilder'})
481 self.add(doc, target, 'buildCommand', executable)
482 self.add(doc, target, 'buildArguments', None)
483 self.add(doc, target, 'buildTarget', buildTarget)
484 self.add(doc, target, 'stopOnError', 'true')
485 self.add(doc, target, 'useDefaultCommand', 'false')
486 self.add(doc, target, 'runAllBuilders', str(runAllBuilders).lower())
488 def add(self, doc, parent, tag, value = None):
489 el = doc.createElement(tag)
490 if (value):
491 if type(value) == type(str()):
492 el.appendChild(doc.createTextNode(value))
493 elif type(value) == type(dict()):
494 self.setAttributes(el, value)
495 parent.appendChild(el)
496 return el
498 def setAttributes(self, node, attrs):
499 for k, v in attrs.items():
500 node.setAttribute(k, v)