3 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file
7 # Thomas Nagy, 2006-2018 (ita)
12 Javac is one of the few compilers that behaves very badly:
14 #. it outputs files where it wants to (-d is only for the package root)
16 #. it recompiles files silently behind your back
18 #. it outputs an undefined amount of files (inner classes)
20 Remember that the compilation can be performed using Jython[1] rather than regular Python. Instead of
21 running one of the following commands::
26 You would have to run::
28 java -jar /path/to/jython.jar waf configure
30 [1] http://www.jython.org/
34 from waflib import Task, Utils, Errors, Node
35 from waflib.Configure import conf
36 from waflib.TaskGen import feature, before_method, after_method
38 from waflib.Tools import ccroot
39 ccroot.USELIB_VARS['javac'] = set(['CLASSPATH', 'JAVACFLAGS'])
41 SOURCE_RE = '**/*.java'
44 class_check_source = '''
46 public static void main(String[] argv) {
48 if (argv.length < 1) {
49 System.err.println("Missing argument");
53 lib = Class.forName(argv[0]);
54 } catch (ClassNotFoundException e) {
55 System.err.println("ClassNotFoundException");
65 @before_method('process_source')
68 Create a javac task for compiling *.java files*. There can be
69 only one javac task by task generator.
71 Utils.def_attrs(self, jarname='', classpath='',
72 sourcepath='.', srcdir='.',
73 jar_mf_attributes={}, jar_mf_classpath=[])
75 outdir = getattr(self, 'outdir', None)
77 if not isinstance(outdir, Node.Node):
78 outdir = self.path.get_bld().make_node(self.outdir)
80 outdir = self.path.get_bld()
83 self.env.OUTDIR = outdir.abspath()
85 self.javac_task = tsk = self.create_task('javac')
88 srcdir = getattr(self, 'srcdir', '')
89 if isinstance(srcdir, Node.Node):
91 for x in Utils.to_list(srcdir):
92 if isinstance(x, Node.Node):
95 y = self.path.find_dir(x)
97 self.bld.fatal('Could not find the folder %s from %s' % (x, self.path))
102 if getattr(self, 'compat', None):
103 tsk.env.append_value('JAVACFLAGS', ['-source', str(self.compat)])
105 if hasattr(self, 'sourcepath'):
106 fold = [isinstance(x, Node.Node) and x or self.path.find_dir(x) for x in self.to_list(self.sourcepath)]
107 names = os.pathsep.join([x.srcpath() for x in fold])
109 names = [x.srcpath() for x in tsk.srcdir]
112 tsk.env.append_value('JAVACFLAGS', ['-sourcepath', names])
115 @before_method('propagate_uselib_vars')
116 @after_method('apply_java')
117 def use_javac_files(self):
119 Processes the *use* attribute referring to other java compilations
122 self.uselib = self.to_list(getattr(self, 'uselib', []))
123 names = self.to_list(getattr(self, 'use', []))
124 get = self.bld.get_tgen_by_name
128 except Errors.WafError:
129 self.uselib.append(x)
132 if hasattr(y, 'jar_task'):
133 lst.append(y.jar_task.outputs[0].abspath())
134 self.javac_task.set_run_after(y.jar_task)
137 self.javac_task.set_run_after(tsk)
138 self.env.append_value('CLASSPATH', lst)
141 @after_method('apply_java', 'propagate_uselib_vars', 'use_javac_files')
142 def set_classpath(self):
144 Sets the CLASSPATH value on the *javac* task previously created.
146 if getattr(self, 'classpath', None):
147 self.env.append_unique('CLASSPATH', getattr(self, 'classpath', []))
149 x.env.CLASSPATH = os.pathsep.join(self.env.CLASSPATH) + os.pathsep
152 @after_method('apply_java', 'use_javac_files')
153 @before_method('process_source')
156 Creates a jar task (one maximum per task generator)
158 destfile = getattr(self, 'destfile', 'test.jar')
159 jaropts = getattr(self, 'jaropts', [])
160 manifest = getattr(self, 'manifest', None)
162 basedir = getattr(self, 'basedir', None)
164 if not isinstance(self.basedir, Node.Node):
165 basedir = self.path.get_bld().make_node(basedir)
167 basedir = self.path.get_bld()
169 self.bld.fatal('Could not find the basedir %r for %r' % (self.basedir, self))
171 self.jar_task = tsk = self.create_task('jar_create')
173 jarcreate = getattr(self, 'jarcreate', 'cfm')
174 if not isinstance(manifest,Node.Node):
175 node = self.path.find_resource(manifest)
179 self.bld.fatal('invalid manifest file %r for %r' % (manifest, self))
180 tsk.dep_nodes.append(node)
181 jaropts.insert(0, node.abspath())
183 jarcreate = getattr(self, 'jarcreate', 'cf')
184 if not isinstance(destfile, Node.Node):
185 destfile = self.path.find_or_declare(destfile)
187 self.bld.fatal('invalid destfile %r for %r' % (destfile, self))
188 tsk.set_outputs(destfile)
189 tsk.basedir = basedir
192 jaropts.append(basedir.bldpath())
195 tsk.env.JAROPTS = jaropts
196 tsk.env.JARCREATE = jarcreate
198 if getattr(self, 'javac_task', None):
199 tsk.set_run_after(self.javac_task)
202 @after_method('jar_files')
203 def use_jar_files(self):
205 Processes the *use* attribute to set the build order on the
206 tasks created by another task generator.
208 self.uselib = self.to_list(getattr(self, 'uselib', []))
209 names = self.to_list(getattr(self, 'use', []))
210 get = self.bld.get_tgen_by_name
214 except Errors.WafError:
215 self.uselib.append(x)
218 self.jar_task.run_after.update(y.tasks)
220 class JTask(Task.Task):
222 Base class for java and jar tasks; provides functionality to run long commands
224 def split_argfile(self, cmd):
228 # jar and javac do not want -J flags in @file
229 if x.startswith('-J'):
232 infile.append(self.quote_flag(x))
233 return (inline, infile)
235 class jar_create(JTask):
240 run_str = '${JAR} ${JARCREATE} ${TGT} ${JAROPTS}'
242 def runnable_status(self):
244 Wait for dependent tasks to be executed, then read the
245 files to update the list of inputs.
247 for t in self.run_after:
249 return Task.ASK_LATER
252 self.inputs = [x for x in self.basedir.ant_glob(JAR_RE, remove=False) if id(x) != id(self.outputs[0])]
254 raise Errors.WafError('Could not find the basedir %r for %r' % (self.basedir, self))
255 return super(jar_create, self).runnable_status()
262 run_str = '${JAVAC} -classpath ${CLASSPATH} -d ${OUTDIR} ${JAVACFLAGS} ${SRC}'
263 vars = ['CLASSPATH', 'JAVACFLAGS', 'JAVAC', 'OUTDIR']
265 The javac task will be executed again if the variables CLASSPATH, JAVACFLAGS, JAVAC or OUTDIR change.
268 """Identify java tasks by input&output folder"""
269 lst = [self.__class__.__name__, self.generator.outdir.abspath()]
270 for x in self.srcdir:
271 lst.append(x.abspath())
272 return Utils.h_list(lst)
274 def runnable_status(self):
276 Waits for dependent tasks to be complete, then read the file system to find the input nodes.
278 for t in self.run_after:
280 return Task.ASK_LATER
284 for x in self.srcdir:
286 self.inputs.extend(x.ant_glob(SOURCE_RE, remove=False))
287 return super(javac, self).runnable_status()
291 List class files created
293 for node in self.generator.outdir.ant_glob('**/*.class'):
294 self.generator.bld.node_sigs[node] = self.uid()
295 self.generator.bld.task_sigs[self.uid()] = self.cache_sig
298 @after_method('process_rule')
299 def create_javadoc(self):
301 Creates a javadoc task (feature 'javadoc')
303 tsk = self.create_task('javadoc')
304 tsk.classpath = getattr(self, 'classpath', [])
305 self.javadoc_package = Utils.to_list(self.javadoc_package)
306 if not isinstance(self.javadoc_output, Node.Node):
307 self.javadoc_output = self.bld.path.find_or_declare(self.javadoc_output)
309 class javadoc(Task.Task):
311 Builds java documentation
316 return '%s: %s -> %s\n' % (self.__class__.__name__, self.generator.srcdir, self.generator.javadoc_output)
320 bld = self.generator.bld
323 #add src node + bld node (for generated java code)
324 srcpath = self.generator.path.abspath() + os.sep + self.generator.srcdir
325 srcpath += os.pathsep
326 srcpath += self.generator.path.get_bld().abspath() + os.sep + self.generator.srcdir
328 classpath = env.CLASSPATH
329 classpath += os.pathsep
330 classpath += os.pathsep.join(self.classpath)
331 classpath = "".join(classpath)
333 self.last_cmd = lst = []
334 lst.extend(Utils.to_list(env.JAVADOC))
335 lst.extend(['-d', self.generator.javadoc_output.abspath()])
336 lst.extend(['-sourcepath', srcpath])
337 lst.extend(['-classpath', classpath])
338 lst.extend(['-subpackages'])
339 lst.extend(self.generator.javadoc_package)
340 lst = [x for x in lst if x]
342 self.generator.bld.cmd_and_log(lst, cwd=wd, env=env.env or None, quiet=0)
345 nodes = self.generator.javadoc_output.ant_glob('**')
347 self.generator.bld.node_sigs[node] = self.uid()
348 self.generator.bld.task_sigs[self.uid()] = self.cache_sig
352 Detects the javac, java and jar programs
354 # If JAVA_PATH is set, we prepend it to the path list
355 java_path = self.environ['PATH'].split(os.pathsep)
358 if 'JAVA_HOME' in self.environ:
359 java_path = [os.path.join(self.environ['JAVA_HOME'], 'bin')] + java_path
360 self.env.JAVA_HOME = [self.environ['JAVA_HOME']]
362 for x in 'javac java jar javadoc'.split():
363 self.find_program(x, var=x.upper(), path_list=java_path)
365 if 'CLASSPATH' in self.environ:
366 v.CLASSPATH = self.environ['CLASSPATH']
369 self.fatal('jar is required for making java packages')
371 self.fatal('javac is required for compiling java classes')
373 v.JARCREATE = 'cf' # can use cvf
377 def check_java_class(self, classname, with_classpath=None):
379 Checks if the specified java class exists
381 :param classname: class to check, like java.util.HashMap
382 :type classname: string
383 :param with_classpath: additional classpath to give
384 :type with_classpath: string
386 javatestdir = '.waf-javatest'
388 classpath = javatestdir
389 if self.env.CLASSPATH:
390 classpath += os.pathsep + self.env.CLASSPATH
391 if isinstance(with_classpath, str):
392 classpath += os.pathsep + with_classpath
394 shutil.rmtree(javatestdir, True)
395 os.mkdir(javatestdir)
397 Utils.writef(os.path.join(javatestdir, 'Test.java'), class_check_source)
400 self.exec_command(self.env.JAVAC + [os.path.join(javatestdir, 'Test.java')], shell=False)
403 cmd = self.env.JAVA + ['-cp', classpath, 'Test', classname]
404 self.to_log("%s\n" % str(cmd))
405 found = self.exec_command(cmd, shell=False)
407 self.msg('Checking for java class %s' % classname, not found)
409 shutil.rmtree(javatestdir, True)
414 def check_jni_headers(conf):
416 Checks for jni headers and libraries. On success the conf.env variables xxx_JAVA are added for use in C/C++ targets::
419 opt.load('compiler_c')
422 conf.load('compiler_c java')
423 conf.check_jni_headers()
426 bld.shlib(source='a.c', target='app', use='JAVA')
428 if not conf.env.CC_NAME and not conf.env.CXX_NAME:
429 conf.fatal('load a compiler first (gcc, g++, ..)')
431 if not conf.env.JAVA_HOME:
432 conf.fatal('set JAVA_HOME in the system environment')
434 # jni requires the jvm
435 javaHome = conf.env.JAVA_HOME[0]
437 dir = conf.root.find_dir(conf.env.JAVA_HOME[0] + '/include')
439 dir = conf.root.find_dir(conf.env.JAVA_HOME[0] + '/../Headers') # think different?!
441 conf.fatal('JAVA_HOME does not seem to be set properly')
443 f = dir.ant_glob('**/(jni|jni_md).h')
444 incDirs = [x.parent.abspath() for x in f]
446 dir = conf.root.find_dir(conf.env.JAVA_HOME[0])
447 f = dir.ant_glob('**/*jvm.(so|dll|dylib)')
448 libDirs = [x.parent.abspath() for x in f] or [javaHome]
450 # On windows, we need both the .dll and .lib to link. On my JDK, they are
451 # in different directories...
452 f = dir.ant_glob('**/*jvm.(lib)')
454 libDirs = [[x, y.parent.abspath()] for x in libDirs for y in f]
456 if conf.env.DEST_OS == 'freebsd':
457 conf.env.append_unique('LINKFLAGS_JAVA', '-pthread')
460 conf.check(header_name='jni.h', define_name='HAVE_JNI_H', lib='jvm',
461 libpath=d, includes=incDirs, uselib_store='JAVA', uselib='JAVA')
467 conf.fatal('could not find lib jvm in %r (see config.log)' % libDirs)