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