eclipse.py (19719B)
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 """ 8 Usage: 9 10 def options(opt): 11 opt.load('eclipse') 12 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']) 15 16 $ waf configure eclipse 17 """ 18 19 import sys, os 20 from waflib import Utils, Logs, Context, Build, TaskGen, Scripting, Errors, Node 21 from xml.dom.minidom import Document 22 23 STANDARD_INCLUDES = [ '/usr/local/include', '/usr/include' ] 24 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' 33 34 class eclipse(Build.BuildContext): 35 cmd = 'eclipse' 36 fun = Scripting.default_cmd 37 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]) 46 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']) 49 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') 53 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') 84 85 for g in self.groups: 86 for tg in g: 87 if not isinstance(tg, TaskGen.task_gen): 88 continue 89 90 tg.post() 91 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 102 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 119 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) 126 127 if not getattr(tg, 'link_task', None): 128 continue 129 130 features = Utils.to_list(getattr(tg, 'features', '')) 131 132 is_cc = 'c' in features or 'cxx' in features 133 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) 137 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) 144 145 if is_cc and path not in source_dirs: 146 source_dirs.append(path) 147 148 hasc = True 149 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) 153 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) 157 158 if haspython: 159 project = self.impl_create_pydevproject(sys.path, pythonpath) 160 self.write_conf_to_xml('.pydevproject', project) 161 162 if hasjava: 163 project = self.impl_create_javaproject(javasrcpath, javalibpath) 164 self.write_conf_to_xml('.classpath', project) 165 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 171 172 lang_settings = Document() 173 project = lang_settings.createElement('project') 174 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'}) 179 180 extension = self.add(lang_settings, configuration, 'extension', {'point': 'org.eclipse.cdt.core.LanguageSettingsProvider'}) 181 182 provider = self.add(lang_settings, extension, 'provider', 183 { 'copy-of': 'extension', 184 'id': 'org.eclipse.cdt.ui.UserLanguageSettingsProvider'}) 185 186 provider = self.add(lang_settings, extension, 'provider-reference', 187 { 'id': 'org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider', 188 'ref': 'shared-provider'}) 189 190 provider = self.add(lang_settings, extension, 'provider-reference', 191 { 'id': 'org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider', 192 'ref': 'shared-provider'}) 193 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' }) 204 205 self.add(lang_settings, provider, 'language-scope', { 'id': 'org.eclipse.cdt.core.gcc'}) 206 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++'}) 217 218 lang_settings.appendChild(project) 219 self.write_conf_to_xml('%s%s%s'%(settings_dir, os.path.sep, settings_name), lang_settings) 220 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 = {} 232 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', 242 } 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), 248 } 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 254 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) 268 269 270 for k, v in dictionaries.items(): 271 self.addDictionary(doc, arguments, k, v) 272 273 natures = self.add(doc, projectDescription, 'natures') 274 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) 284 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') 289 290 doc.appendChild(projectDescription) 291 return doc 292 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}) 301 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'}) 307 308 self.add(doc, storageModule, 'externalSettings') 309 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'}) 321 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': ''}) 331 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'}) 337 338 self.add(doc, toolChain, 'targetPlatform', {'binaryParser': 'org.eclipse.cdt.core.ELF', 'id': cdt_bld + '.prefbase.toolchain.1', 'name': ''}) 339 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'}) 353 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 381 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', 391 { 392 'flags': 'VALUE_WORKSPACE_PATH|RESOLVED', 393 'kind': 'sourcePath', 394 'name': i}) 395 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) 408 409 storageModule = self.add(doc, cproject, 'storageModule', 410 {'moduleId': 'cdtBuildSystem', 411 'version': '4.0.0'}) 412 413 self.add(doc, storageModule, 'project', {'id': '%s.null.1'%appname, 'name': appname}) 414 415 storageModule = self.add(doc, cproject, 'storageModule', 416 {'moduleId': 'org.eclipse.cdt.core.LanguageSettingsProviders'}) 417 418 storageModule = self.add(doc, cproject, 'storageModule', 419 {'moduleId': 'scannerConfiguration'}) 420 421 doc.appendChild(cproject) 422 return doc 423 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) 447 448 doc.appendChild(pydevproject) 449 return doc 450 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}) 459 460 if javalibpath: 461 for i in javalibpath: 462 self.add(doc, javaproject, 'classpathentry', 463 {'kind': 'lib', 'path': i}) 464 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 469 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 475 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()) 487 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 497 498 def setAttributes(self, node, attrs): 499 for k, v in attrs.items(): 500 node.setAttribute(k, v) 501