PageRenderTime 52ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/waflib/Tools/javaw.py

https://code.google.com/
Python | 474 lines | 442 code | 6 blank | 26 comment | 6 complexity | 41780f5790d888d2480a4c978198e6e8 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. #!/usr/bin/env python
  2. # encoding: utf-8
  3. # Thomas Nagy, 2006-2010 (ita)
  4. """
  5. Java support
  6. Javac is one of the few compilers that behaves very badly:
  7. #. it outputs files where it wants to (-d is only for the package root)
  8. #. it recompiles files silently behind your back
  9. #. it outputs an undefined amount of files (inner classes)
  10. Remember that the compilation can be performed using Jython[1] rather than regular Python. Instead of
  11. running one of the following commands::
  12. ./waf configure
  13. python waf configure
  14. You would have to run::
  15. java -jar /path/to/jython.jar waf configure
  16. [1] http://www.jython.org/
  17. """
  18. import os, re, tempfile, shutil
  19. from waflib import TaskGen, Task, Utils, Options, Build, Errors, Node, Logs
  20. from waflib.Configure import conf
  21. from waflib.TaskGen import feature, before_method, after_method
  22. from waflib.Tools import ccroot
  23. ccroot.USELIB_VARS['javac'] = set(['CLASSPATH', 'JAVACFLAGS'])
  24. SOURCE_RE = '**/*.java'
  25. JAR_RE = '**/*'
  26. class_check_source = '''
  27. public class Test {
  28. public static void main(String[] argv) {
  29. Class lib;
  30. if (argv.length < 1) {
  31. System.err.println("Missing argument");
  32. System.exit(77);
  33. }
  34. try {
  35. lib = Class.forName(argv[0]);
  36. } catch (ClassNotFoundException e) {
  37. System.err.println("ClassNotFoundException");
  38. System.exit(1);
  39. }
  40. lib = null;
  41. System.exit(0);
  42. }
  43. }
  44. '''
  45. @feature('javac')
  46. @before_method('process_source')
  47. def apply_java(self):
  48. """
  49. Create a javac task for compiling *.java files*. There can be
  50. only one javac task by task generator.
  51. """
  52. Utils.def_attrs(self, jarname='', classpath='',
  53. sourcepath='.', srcdir='.',
  54. jar_mf_attributes={}, jar_mf_classpath=[])
  55. nodes_lst = []
  56. outdir = getattr(self, 'outdir', None)
  57. if outdir:
  58. if not isinstance(outdir, Node.Node):
  59. outdir = self.path.get_bld().make_node(self.outdir)
  60. else:
  61. outdir = self.path.get_bld()
  62. outdir.mkdir()
  63. self.outdir = outdir
  64. self.env['OUTDIR'] = outdir.abspath()
  65. self.javac_task = tsk = self.create_task('javac')
  66. tmp = []
  67. srcdir = getattr(self, 'srcdir', '')
  68. if isinstance(srcdir, Node.Node):
  69. srcdir = [srcdir]
  70. for x in Utils.to_list(srcdir):
  71. if isinstance(x, Node.Node):
  72. y = x
  73. else:
  74. y = self.path.find_dir(x)
  75. if not y:
  76. self.bld.fatal('Could not find the folder %s from %s' % (x, self.path))
  77. tmp.append(y)
  78. tsk.srcdir = tmp
  79. if getattr(self, 'compat', None):
  80. tsk.env.append_value('JAVACFLAGS', ['-source', self.compat])
  81. if hasattr(self, 'sourcepath'):
  82. fold = [isinstance(x, Node.Node) and x or self.path.find_dir(x) for x in self.to_list(self.sourcepath)]
  83. names = os.pathsep.join([x.srcpath() for x in fold])
  84. else:
  85. names = [x.srcpath() for x in tsk.srcdir]
  86. if names:
  87. tsk.env.append_value('JAVACFLAGS', ['-sourcepath', names])
  88. @feature('javac')
  89. @after_method('apply_java')
  90. def use_javac_files(self):
  91. """
  92. Process the *use* attribute referring to other java compilations
  93. """
  94. lst = []
  95. self.uselib = self.to_list(getattr(self, 'uselib', []))
  96. names = self.to_list(getattr(self, 'use', []))
  97. get = self.bld.get_tgen_by_name
  98. for x in names:
  99. try:
  100. y = get(x)
  101. except Exception:
  102. self.uselib.append(x)
  103. else:
  104. y.post()
  105. lst.append(y.jar_task.outputs[0].abspath())
  106. self.javac_task.set_run_after(y.jar_task)
  107. if lst:
  108. self.env.append_value('CLASSPATH', lst)
  109. @feature('javac')
  110. @after_method('apply_java', 'propagate_uselib_vars', 'use_javac_files')
  111. def set_classpath(self):
  112. """
  113. Set the CLASSPATH value on the *javac* task previously created.
  114. """
  115. self.env.append_value('CLASSPATH', getattr(self, 'classpath', []))
  116. for x in self.tasks:
  117. x.env.CLASSPATH = os.pathsep.join(self.env.CLASSPATH) + os.pathsep
  118. @feature('jar')
  119. @after_method('apply_java', 'use_javac_files')
  120. @before_method('process_source')
  121. def jar_files(self):
  122. """
  123. Create a jar task. There can be only one jar task by task generator.
  124. """
  125. destfile = getattr(self, 'destfile', 'test.jar')
  126. jaropts = getattr(self, 'jaropts', [])
  127. manifest = getattr(self, 'manifest', None)
  128. basedir = getattr(self, 'basedir', None)
  129. if basedir:
  130. if not isinstance(self.basedir, Node.Node):
  131. basedir = self.path.get_bld().make_node(basedir)
  132. else:
  133. basedir = self.path.get_bld()
  134. if not basedir:
  135. self.bld.fatal('Could not find the basedir %r for %r' % (self.basedir, self))
  136. self.jar_task = tsk = self.create_task('jar_create')
  137. if manifest:
  138. jarcreate = getattr(self, 'jarcreate', 'cfm')
  139. node = self.path.find_node(manifest)
  140. tsk.dep_nodes.append(node)
  141. jaropts.insert(0, node.abspath())
  142. else:
  143. jarcreate = getattr(self, 'jarcreate', 'cf')
  144. if not isinstance(destfile, Node.Node):
  145. destfile = self.path.find_or_declare(destfile)
  146. if not destfile:
  147. self.bld.fatal('invalid destfile %r for %r' % (destfile, self))
  148. tsk.set_outputs(destfile)
  149. tsk.basedir = basedir
  150. jaropts.append('-C')
  151. jaropts.append(basedir.bldpath())
  152. jaropts.append('.')
  153. tsk.env['JAROPTS'] = jaropts
  154. tsk.env['JARCREATE'] = jarcreate
  155. if getattr(self, 'javac_task', None):
  156. tsk.set_run_after(self.javac_task)
  157. @feature('jar')
  158. @after_method('jar_files')
  159. def use_jar_files(self):
  160. """
  161. Process the *use* attribute to set the build order on the
  162. tasks created by another task generator.
  163. """
  164. lst = []
  165. self.uselib = self.to_list(getattr(self, 'uselib', []))
  166. names = self.to_list(getattr(self, 'use', []))
  167. get = self.bld.get_tgen_by_name
  168. for x in names:
  169. try:
  170. y = get(x)
  171. except Exception:
  172. self.uselib.append(x)
  173. else:
  174. y.post()
  175. self.jar_task.run_after.update(y.tasks)
  176. class jar_create(Task.Task):
  177. """
  178. Create a jar file
  179. """
  180. color = 'GREEN'
  181. run_str = '${JAR} ${JARCREATE} ${TGT} ${JAROPTS}'
  182. def runnable_status(self):
  183. """
  184. Wait for dependent tasks to be executed, then read the
  185. files to update the list of inputs.
  186. """
  187. for t in self.run_after:
  188. if not t.hasrun:
  189. return Task.ASK_LATER
  190. if not self.inputs:
  191. global JAR_RE
  192. try:
  193. self.inputs = [x for x in self.basedir.ant_glob(JAR_RE, remove=False) if id(x) != id(self.outputs[0])]
  194. except Exception:
  195. raise Errors.WafError('Could not find the basedir %r for %r' % (self.basedir, self))
  196. return super(jar_create, self).runnable_status()
  197. class javac(Task.Task):
  198. """
  199. Compile java files
  200. """
  201. color = 'BLUE'
  202. vars = ['CLASSPATH', 'JAVACFLAGS', 'JAVAC', 'OUTDIR']
  203. """
  204. The javac task will be executed again if the variables CLASSPATH, JAVACFLAGS, JAVAC or OUTDIR change.
  205. """
  206. def runnable_status(self):
  207. """
  208. Wait for dependent tasks to be complete, then read the file system to find the input nodes.
  209. """
  210. for t in self.run_after:
  211. if not t.hasrun:
  212. return Task.ASK_LATER
  213. if not self.inputs:
  214. global SOURCE_RE
  215. self.inputs = []
  216. for x in self.srcdir:
  217. self.inputs.extend(x.ant_glob(SOURCE_RE, remove=False))
  218. return super(javac, self).runnable_status()
  219. def run(self):
  220. """
  221. Execute the javac compiler
  222. """
  223. env = self.env
  224. gen = self.generator
  225. bld = gen.bld
  226. wd = bld.bldnode.abspath()
  227. def to_list(xx):
  228. if isinstance(xx, str): return [xx]
  229. return xx
  230. cmd = []
  231. cmd.extend(to_list(env['JAVAC']))
  232. cmd.extend(['-classpath'])
  233. cmd.extend(to_list(env['CLASSPATH']))
  234. cmd.extend(['-d'])
  235. cmd.extend(to_list(env['OUTDIR']))
  236. cmd.extend(to_list(env['JAVACFLAGS']))
  237. files = [a.path_from(bld.bldnode) for a in self.inputs]
  238. # workaround for command line length limit:
  239. # http://support.microsoft.com/kb/830473
  240. tmp = None
  241. try:
  242. if len(str(files)) + len(str(cmd)) > 8192:
  243. (fd, tmp) = tempfile.mkstemp(dir=bld.bldnode.abspath())
  244. try:
  245. os.write(fd, '\n'.join(files).encode())
  246. finally:
  247. if tmp:
  248. os.close(fd)
  249. if Logs.verbose:
  250. Logs.debug('runner: %r' % (cmd + files))
  251. cmd.append('@' + tmp)
  252. else:
  253. cmd += files
  254. ret = self.exec_command(cmd, cwd=wd, env=env.env or None)
  255. finally:
  256. if tmp:
  257. os.remove(tmp)
  258. return ret
  259. def post_run(self):
  260. """
  261. """
  262. for n in self.generator.outdir.ant_glob('**/*.class'):
  263. n.sig = Utils.h_file(n.abspath()) # careful with this
  264. self.generator.bld.task_sigs[self.uid()] = self.cache_sig
  265. @feature('javadoc')
  266. @after_method('process_rule')
  267. def create_javadoc(self):
  268. tsk = self.create_task('javadoc')
  269. tsk.classpath = getattr(self, 'classpath', [])
  270. self.javadoc_package = Utils.to_list(self.javadoc_package)
  271. if not isinstance(self.javadoc_output, Node.Node):
  272. self.javadoc_output = self.bld.path.find_or_declare(self.javadoc_output)
  273. class javadoc(Task.Task):
  274. color = 'BLUE'
  275. def __str__(self):
  276. return '%s: %s -> %s\n' % (self.__class__.__name__, self.generator.srcdir, self.generator.javadoc_output)
  277. def run(self):
  278. env = self.env
  279. bld = self.generator.bld
  280. wd = bld.bldnode.abspath()
  281. #add src node + bld node (for generated java code)
  282. srcpath = self.generator.path.abspath() + os.sep + self.generator.srcdir
  283. srcpath += os.pathsep
  284. srcpath += self.generator.path.get_bld().abspath() + os.sep + self.generator.srcdir
  285. classpath = env.CLASSPATH
  286. classpath += os.pathsep
  287. classpath += os.pathsep.join(self.classpath)
  288. classpath = "".join(classpath)
  289. self.last_cmd = lst = []
  290. lst.extend(Utils.to_list(env['JAVADOC']))
  291. lst.extend(['-d', self.generator.javadoc_output.abspath()])
  292. lst.extend(['-sourcepath', srcpath])
  293. lst.extend(['-classpath', classpath])
  294. lst.extend(['-subpackages'])
  295. lst.extend(self.generator.javadoc_package)
  296. lst = [x for x in lst if x]
  297. self.generator.bld.cmd_and_log(lst, cwd=wd, env=env.env or None, quiet=0)
  298. def post_run(self):
  299. nodes = self.generator.javadoc_output.ant_glob('**')
  300. for x in nodes:
  301. x.sig = Utils.h_file(x.abspath())
  302. self.generator.bld.task_sigs[self.uid()] = self.cache_sig
  303. def configure(self):
  304. """
  305. Detect the javac, java and jar programs
  306. """
  307. # If JAVA_PATH is set, we prepend it to the path list
  308. java_path = self.environ['PATH'].split(os.pathsep)
  309. v = self.env
  310. if 'JAVA_HOME' in self.environ:
  311. java_path = [os.path.join(self.environ['JAVA_HOME'], 'bin')] + java_path
  312. self.env['JAVA_HOME'] = [self.environ['JAVA_HOME']]
  313. for x in 'javac java jar javadoc'.split():
  314. self.find_program(x, var=x.upper(), path_list=java_path)
  315. if 'CLASSPATH' in self.environ:
  316. v['CLASSPATH'] = self.environ['CLASSPATH']
  317. if not v['JAR']: self.fatal('jar is required for making java packages')
  318. if not v['JAVAC']: self.fatal('javac is required for compiling java classes')
  319. v['JARCREATE'] = 'cf' # can use cvf
  320. v['JAVACFLAGS'] = []
  321. @conf
  322. def check_java_class(self, classname, with_classpath=None):
  323. """
  324. Check if the specified java class exists
  325. :param classname: class to check, like java.util.HashMap
  326. :type classname: string
  327. :param with_classpath: additional classpath to give
  328. :type with_classpath: string
  329. """
  330. javatestdir = '.waf-javatest'
  331. classpath = javatestdir
  332. if self.env['CLASSPATH']:
  333. classpath += os.pathsep + self.env['CLASSPATH']
  334. if isinstance(with_classpath, str):
  335. classpath += os.pathsep + with_classpath
  336. shutil.rmtree(javatestdir, True)
  337. os.mkdir(javatestdir)
  338. Utils.writef(os.path.join(javatestdir, 'Test.java'), class_check_source)
  339. # Compile the source
  340. self.exec_command(self.env['JAVAC'] + [os.path.join(javatestdir, 'Test.java')], shell=False)
  341. # Try to run the app
  342. cmd = self.env['JAVA'] + ['-cp', classpath, 'Test', classname]
  343. self.to_log("%s\n" % str(cmd))
  344. found = self.exec_command(cmd, shell=False)
  345. self.msg('Checking for java class %s' % classname, not found)
  346. shutil.rmtree(javatestdir, True)
  347. return found
  348. @conf
  349. def check_jni_headers(conf):
  350. """
  351. Check for jni headers and libraries. On success the conf.env variables xxx_JAVA are added for use in C/C++ targets::
  352. def options(opt):
  353. opt.load('compiler_c')
  354. def configure(conf):
  355. conf.load('compiler_c java')
  356. conf.check_jni_headers()
  357. def build(bld):
  358. bld.shlib(source='a.c', target='app', use='JAVA')
  359. """
  360. if not conf.env.CC_NAME and not conf.env.CXX_NAME:
  361. conf.fatal('load a compiler first (gcc, g++, ..)')
  362. if not conf.env.JAVA_HOME:
  363. conf.fatal('set JAVA_HOME in the system environment')
  364. # jni requires the jvm
  365. javaHome = conf.env['JAVA_HOME'][0]
  366. dir = conf.root.find_dir(conf.env.JAVA_HOME[0] + '/include')
  367. if dir is None:
  368. dir = conf.root.find_dir(conf.env.JAVA_HOME[0] + '/../Headers') # think different?!
  369. if dir is None:
  370. conf.fatal('JAVA_HOME does not seem to be set properly')
  371. f = dir.ant_glob('**/(jni|jni_md).h')
  372. incDirs = [x.parent.abspath() for x in f]
  373. dir = conf.root.find_dir(conf.env.JAVA_HOME[0])
  374. f = dir.ant_glob('**/*jvm.(so|dll|dylib)')
  375. libDirs = [x.parent.abspath() for x in f] or [javaHome]
  376. # On windows, we need both the .dll and .lib to link. On my JDK, they are
  377. # in different directories...
  378. f = dir.ant_glob('**/*jvm.(lib)')
  379. if f:
  380. libDirs = [[x, y.parent.abspath()] for x in libDirs for y in f]
  381. for d in libDirs:
  382. try:
  383. conf.check(header_name='jni.h', define_name='HAVE_JNI_H', lib='jvm',
  384. libpath=d, includes=incDirs, uselib_store='JAVA', uselib='JAVA')
  385. except Exception:
  386. pass
  387. else:
  388. break
  389. else:
  390. conf.fatal('could not find lib jvm in %r (see config.log)' % libDirs)