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)
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
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
):
36 fun
= Scripting
.default_cmd
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
=[]):
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
63 hasc
= hasjava
= haspython
= False
65 cpppath
= self
.env
['CPPPATH']
68 includes
= STANDARD_INCLUDES
69 if sys
.platform
!= 'win32':
70 cc
= self
.env
.CC
or self
.env
.CXX
72 cmd
= cc
+ ['-xc++', '-E', '-Wp,-v', '-']
74 gccout
= self
.cmd_and_log(cmd
, output
=Context
.STDERR
, quiet
=Context
.BOTH
, input='\n'.encode()).splitlines()
75 except Errors
.WafError
:
80 if ipath
.startswith(' /'):
81 includes
.append(ipath
[1:])
83 Logs
.warn('Generating Eclipse CDT project files')
87 if not isinstance(tg
, TaskGen
.task_gen
):
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
)
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)
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
)
116 if java_src
not in javasrcpath
:
117 javasrcpath
.append(java_src
)
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):
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'])
136 path
= p
.path_from(self
.srcnode
)
138 if (path
.startswith("/")):
139 if path
not in cpppath
:
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
)
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
)
155 project
= self
.impl_create_cproject(sys
.executable
, waf_executable
, appname
, workspace_includes
, cpppath
, source_dirs
)
156 self
.write_conf_to_xml('.cproject', project
)
159 project
= self
.impl_create_pydevproject(sys
.path
, pythonpath
)
160 self
.write_conf_to_xml('.pydevproject', project
)
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
168 os
.mkdir(settings_dir
)
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
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
196 provider
= self
.add(lang_settings
, extension
, 'provider',
197 { 'class': 'org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuiltinSpecsDetector',
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'})
208 provider
= self
.add(lang_settings
, extension
, 'provider',
209 { 'class': 'org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuiltinSpecsDetector',
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
):
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')
233 # If CDT is present, instruct this one to call waf as it is more flexible (separate build/clean ...)
235 self
.add(doc
, buildCommand
, 'name', oe_cdt
+ '.managedbuilder.core.genmakebuilder')
236 # the default make-style targets are overwritten by the .cproject values
238 cdt_mk
+ '.contents': cdt_mk
+ '.activeConfigSettings',
239 cdt_mk
+ '.enableAutoBuild': 'false',
240 cdt_mk
+ '.enableCleanBuild': 'true',
241 cdt_mk
+ '.enableFullBuild': 'true',
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')
247 'LaunchConfigHandle': '<project>/%s/%s'%(extbuilder_dir
, extbuilder_name
),
249 # The definition is in a separate directory XML file
251 os
.mkdir(extbuilder_dir
)
253 pass # Ignore error if already exists
255 # Populate here the external builder XML calling waf
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')
278 managedbuilder.core.ScannerConfigNature
279 managedbuilder.core.managedBuildNature
282 for n
in nature_list
:
283 self
.add(doc
, natures
, 'nature', oe_cdt
+ '.' + n
)
286 self
.add(doc
, natures
, 'nature', 'org.python.pydev.pythonNature')
288 self
.add(doc
, natures
, 'nature', 'org.eclipse.jdt.core.javanature')
290 doc
.appendChild(projectDescription
)
293 def impl_create_cproject(self
, executable
, waf_executable
, appname
, workspace_includes
, cpppath
, source_dirs
=[]):
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',
305 'moduleId': cdt_core
+ '.settings',
308 self
.add(doc
, storageModule
, 'externalSettings')
310 extensions
= self
.add(doc
, storageModule
, 'extensions')
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
,
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'})
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
),
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',
370 'value': '"${workspace_loc:/%s/%s}"'%(appname
, i
)})
372 self
.add(doc
, option
, 'listOptionValue',
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' })
383 sourceEntries
= self
.add(doc
, config
, 'sourceEntries')
384 for i
in source_dirs
:
385 self
.add(doc
, sourceEntries
, 'entry',
387 'flags': 'VALUE_WORKSPACE_PATH|RESOLVED',
388 'kind': 'sourcePath',
390 self
.add(doc
, sourceEntries
, 'entry',
392 'flags': 'VALUE_WORKSPACE_PATH|RESOLVED',
393 'kind': 'sourcePath',
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',
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
)
424 def impl_create_pydevproject(self
, system_path
, user_path
):
425 # create a pydevproject file
427 doc
.appendChild(doc
.createProcessingInstruction('eclipse-pydev', 'version="1.0"'))
428 pydevproject
= doc
.createElement('pydev_project')
429 prop
= self
.add(doc
, pydevproject
,
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')
436 wafadmin
= [p
for p
in system_path
if p
.find('wafadmin') != -1]
438 prop
= self
.add(doc
, pydevproject
, 'pydev_pathproperty',
439 {'name':'org.python.pydev.PROJECT_EXTERNAL_SOURCE_PATH'})
441 self
.add(doc
, prop
, 'path', i
)
443 prop
= self
.add(doc
, pydevproject
, 'pydev_pathproperty',
444 {'name':'org.python.pydev.PROJECT_SOURCE_PATH'})
446 self
.add(doc
, prop
, 'path', '/${PROJECT_DIR_NAME}/'+i
)
448 doc
.appendChild(pydevproject
)
451 def impl_create_javaproject(self
, javasrcpath
, javalibpath
):
452 # create a .classpath file for java usage
454 javaproject
= doc
.createElement('classpath')
456 for i
in javasrcpath
:
457 self
.add(doc
, javaproject
, 'classpathentry',
458 {'kind': 'src', 'path': i
})
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
)
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
)
476 def addTarget(self
, doc
, buildTargets
, executable
, name
, buildTarget
, runAllBuilders
=True):
477 target
= self
.add(doc
, buildTargets
, 'target',
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
)
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
)
498 def setAttributes(self
, node
, attrs
):
499 for k
, v
in attrs
.items():
500 node
.setAttribute(k
, v
)