123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464 |
- #!/usr/bin/env python
- # encoding: utf-8
- # Thomas Nagy, 2006-2018 (ita)
- """
- Java support
- Javac is one of the few compilers that behaves very badly:
- #. it outputs files where it wants to (-d is only for the package root)
- #. it recompiles files silently behind your back
- #. it outputs an undefined amount of files (inner classes)
- Remember that the compilation can be performed using Jython[1] rather than regular Python. Instead of
- running one of the following commands::
- ./waf configure
- python waf configure
- You would have to run::
- java -jar /path/to/jython.jar waf configure
- [1] http://www.jython.org/
- """
- import os, shutil
- from waflib import Task, Utils, Errors, Node
- from waflib.Configure import conf
- from waflib.TaskGen import feature, before_method, after_method
- from waflib.Tools import ccroot
- ccroot.USELIB_VARS['javac'] = set(['CLASSPATH', 'JAVACFLAGS'])
- SOURCE_RE = '**/*.java'
- JAR_RE = '**/*'
- class_check_source = '''
- public class Test {
- public static void main(String[] argv) {
- Class lib;
- if (argv.length < 1) {
- System.err.println("Missing argument");
- System.exit(77);
- }
- try {
- lib = Class.forName(argv[0]);
- } catch (ClassNotFoundException e) {
- System.err.println("ClassNotFoundException");
- System.exit(1);
- }
- lib = null;
- System.exit(0);
- }
- }
- '''
- @feature('javac')
- @before_method('process_source')
- def apply_java(self):
- """
- Create a javac task for compiling *.java files*. There can be
- only one javac task by task generator.
- """
- Utils.def_attrs(self, jarname='', classpath='',
- sourcepath='.', srcdir='.',
- jar_mf_attributes={}, jar_mf_classpath=[])
- outdir = getattr(self, 'outdir', None)
- if outdir:
- if not isinstance(outdir, Node.Node):
- outdir = self.path.get_bld().make_node(self.outdir)
- else:
- outdir = self.path.get_bld()
- outdir.mkdir()
- self.outdir = outdir
- self.env.OUTDIR = outdir.abspath()
- self.javac_task = tsk = self.create_task('javac')
- tmp = []
- srcdir = getattr(self, 'srcdir', '')
- if isinstance(srcdir, Node.Node):
- srcdir = [srcdir]
- for x in Utils.to_list(srcdir):
- if isinstance(x, Node.Node):
- y = x
- else:
- y = self.path.find_dir(x)
- if not y:
- self.bld.fatal('Could not find the folder %s from %s' % (x, self.path))
- tmp.append(y)
- tsk.srcdir = tmp
- if getattr(self, 'compat', None):
- tsk.env.append_value('JAVACFLAGS', ['-source', str(self.compat)])
- if hasattr(self, 'sourcepath'):
- fold = [isinstance(x, Node.Node) and x or self.path.find_dir(x) for x in self.to_list(self.sourcepath)]
- names = os.pathsep.join([x.srcpath() for x in fold])
- else:
- names = [x.srcpath() for x in tsk.srcdir]
- if names:
- tsk.env.append_value('JAVACFLAGS', ['-sourcepath', names])
- @feature('javac')
- @before_method('propagate_uselib_vars')
- @after_method('apply_java')
- def use_javac_files(self):
- """
- Processes the *use* attribute referring to other java compilations
- """
- lst = []
- self.uselib = self.to_list(getattr(self, 'uselib', []))
- names = self.to_list(getattr(self, 'use', []))
- get = self.bld.get_tgen_by_name
- for x in names:
- try:
- y = get(x)
- except Errors.WafError:
- self.uselib.append(x)
- else:
- y.post()
- if hasattr(y, 'jar_task'):
- lst.append(y.jar_task.outputs[0].abspath())
- self.javac_task.set_run_after(y.jar_task)
- else:
- for tsk in y.tasks:
- self.javac_task.set_run_after(tsk)
- self.env.append_value('CLASSPATH', lst)
- @feature('javac')
- @after_method('apply_java', 'propagate_uselib_vars', 'use_javac_files')
- def set_classpath(self):
- """
- Sets the CLASSPATH value on the *javac* task previously created.
- """
- if getattr(self, 'classpath', None):
- self.env.append_unique('CLASSPATH', getattr(self, 'classpath', []))
- for x in self.tasks:
- x.env.CLASSPATH = os.pathsep.join(self.env.CLASSPATH) + os.pathsep
- @feature('jar')
- @after_method('apply_java', 'use_javac_files')
- @before_method('process_source')
- def jar_files(self):
- """
- Creates a jar task (one maximum per task generator)
- """
- destfile = getattr(self, 'destfile', 'test.jar')
- jaropts = getattr(self, 'jaropts', [])
- manifest = getattr(self, 'manifest', None)
- basedir = getattr(self, 'basedir', None)
- if basedir:
- if not isinstance(self.basedir, Node.Node):
- basedir = self.path.get_bld().make_node(basedir)
- else:
- basedir = self.path.get_bld()
- if not basedir:
- self.bld.fatal('Could not find the basedir %r for %r' % (self.basedir, self))
- self.jar_task = tsk = self.create_task('jar_create')
- if manifest:
- jarcreate = getattr(self, 'jarcreate', 'cfm')
- if not isinstance(manifest,Node.Node):
- node = self.path.find_resource(manifest)
- else:
- node = manifest
- if not node:
- self.bld.fatal('invalid manifest file %r for %r' % (manifest, self))
- tsk.dep_nodes.append(node)
- jaropts.insert(0, node.abspath())
- else:
- jarcreate = getattr(self, 'jarcreate', 'cf')
- if not isinstance(destfile, Node.Node):
- destfile = self.path.find_or_declare(destfile)
- if not destfile:
- self.bld.fatal('invalid destfile %r for %r' % (destfile, self))
- tsk.set_outputs(destfile)
- tsk.basedir = basedir
- jaropts.append('-C')
- jaropts.append(basedir.bldpath())
- jaropts.append('.')
- tsk.env.JAROPTS = jaropts
- tsk.env.JARCREATE = jarcreate
- if getattr(self, 'javac_task', None):
- tsk.set_run_after(self.javac_task)
- @feature('jar')
- @after_method('jar_files')
- def use_jar_files(self):
- """
- Processes the *use* attribute to set the build order on the
- tasks created by another task generator.
- """
- self.uselib = self.to_list(getattr(self, 'uselib', []))
- names = self.to_list(getattr(self, 'use', []))
- get = self.bld.get_tgen_by_name
- for x in names:
- try:
- y = get(x)
- except Errors.WafError:
- self.uselib.append(x)
- else:
- y.post()
- self.jar_task.run_after.update(y.tasks)
- class JTask(Task.Task):
- """
- Base class for java and jar tasks; provides functionality to run long commands
- """
- def split_argfile(self, cmd):
- inline = [cmd[0]]
- infile = []
- for x in cmd[1:]:
- # jar and javac do not want -J flags in @file
- if x.startswith('-J'):
- inline.append(x)
- else:
- infile.append(self.quote_flag(x))
- return (inline, infile)
- class jar_create(JTask):
- """
- Creates a jar file
- """
- color = 'GREEN'
- run_str = '${JAR} ${JARCREATE} ${TGT} ${JAROPTS}'
- def runnable_status(self):
- """
- Wait for dependent tasks to be executed, then read the
- files to update the list of inputs.
- """
- for t in self.run_after:
- if not t.hasrun:
- return Task.ASK_LATER
- if not self.inputs:
- try:
- self.inputs = [x for x in self.basedir.ant_glob(JAR_RE, remove=False) if id(x) != id(self.outputs[0])]
- except Exception:
- raise Errors.WafError('Could not find the basedir %r for %r' % (self.basedir, self))
- return super(jar_create, self).runnable_status()
- class javac(JTask):
- """
- Compiles java files
- """
- color = 'BLUE'
- run_str = '${JAVAC} -classpath ${CLASSPATH} -d ${OUTDIR} ${JAVACFLAGS} ${SRC}'
- vars = ['CLASSPATH', 'JAVACFLAGS', 'JAVAC', 'OUTDIR']
- """
- The javac task will be executed again if the variables CLASSPATH, JAVACFLAGS, JAVAC or OUTDIR change.
- """
- def uid(self):
- """Identify java tasks by input&output folder"""
- lst = [self.__class__.__name__, self.generator.outdir.abspath()]
- for x in self.srcdir:
- lst.append(x.abspath())
- return Utils.h_list(lst)
- def runnable_status(self):
- """
- Waits for dependent tasks to be complete, then read the file system to find the input nodes.
- """
- for t in self.run_after:
- if not t.hasrun:
- return Task.ASK_LATER
- if not self.inputs:
- self.inputs = []
- for x in self.srcdir:
- if x.exists():
- self.inputs.extend(x.ant_glob(SOURCE_RE, remove=False))
- return super(javac, self).runnable_status()
- def post_run(self):
- """
- List class files created
- """
- for node in self.generator.outdir.ant_glob('**/*.class'):
- self.generator.bld.node_sigs[node] = self.uid()
- self.generator.bld.task_sigs[self.uid()] = self.cache_sig
- @feature('javadoc')
- @after_method('process_rule')
- def create_javadoc(self):
- """
- Creates a javadoc task (feature 'javadoc')
- """
- tsk = self.create_task('javadoc')
- tsk.classpath = getattr(self, 'classpath', [])
- self.javadoc_package = Utils.to_list(self.javadoc_package)
- if not isinstance(self.javadoc_output, Node.Node):
- self.javadoc_output = self.bld.path.find_or_declare(self.javadoc_output)
- class javadoc(Task.Task):
- """
- Builds java documentation
- """
- color = 'BLUE'
- def __str__(self):
- return '%s: %s -> %s\n' % (self.__class__.__name__, self.generator.srcdir, self.generator.javadoc_output)
- def run(self):
- env = self.env
- bld = self.generator.bld
- wd = bld.bldnode
- #add src node + bld node (for generated java code)
- srcpath = self.generator.path.abspath() + os.sep + self.generator.srcdir
- srcpath += os.pathsep
- srcpath += self.generator.path.get_bld().abspath() + os.sep + self.generator.srcdir
- classpath = env.CLASSPATH
- classpath += os.pathsep
- classpath += os.pathsep.join(self.classpath)
- classpath = "".join(classpath)
- self.last_cmd = lst = []
- lst.extend(Utils.to_list(env.JAVADOC))
- lst.extend(['-d', self.generator.javadoc_output.abspath()])
- lst.extend(['-sourcepath', srcpath])
- lst.extend(['-classpath', classpath])
- lst.extend(['-subpackages'])
- lst.extend(self.generator.javadoc_package)
- lst = [x for x in lst if x]
- self.generator.bld.cmd_and_log(lst, cwd=wd, env=env.env or None, quiet=0)
- def post_run(self):
- nodes = self.generator.javadoc_output.ant_glob('**')
- for node in nodes:
- self.generator.bld.node_sigs[node] = self.uid()
- self.generator.bld.task_sigs[self.uid()] = self.cache_sig
- def configure(self):
- """
- Detects the javac, java and jar programs
- """
- # If JAVA_PATH is set, we prepend it to the path list
- java_path = self.environ['PATH'].split(os.pathsep)
- v = self.env
- if 'JAVA_HOME' in self.environ:
- java_path = [os.path.join(self.environ['JAVA_HOME'], 'bin')] + java_path
- self.env.JAVA_HOME = [self.environ['JAVA_HOME']]
- for x in 'javac java jar javadoc'.split():
- self.find_program(x, var=x.upper(), path_list=java_path)
- if 'CLASSPATH' in self.environ:
- v.CLASSPATH = self.environ['CLASSPATH']
- if not v.JAR:
- self.fatal('jar is required for making java packages')
- if not v.JAVAC:
- self.fatal('javac is required for compiling java classes')
- v.JARCREATE = 'cf' # can use cvf
- v.JAVACFLAGS = []
- @conf
- def check_java_class(self, classname, with_classpath=None):
- """
- Checks if the specified java class exists
- :param classname: class to check, like java.util.HashMap
- :type classname: string
- :param with_classpath: additional classpath to give
- :type with_classpath: string
- """
- javatestdir = '.waf-javatest'
- classpath = javatestdir
- if self.env.CLASSPATH:
- classpath += os.pathsep + self.env.CLASSPATH
- if isinstance(with_classpath, str):
- classpath += os.pathsep + with_classpath
- shutil.rmtree(javatestdir, True)
- os.mkdir(javatestdir)
- Utils.writef(os.path.join(javatestdir, 'Test.java'), class_check_source)
- # Compile the source
- self.exec_command(self.env.JAVAC + [os.path.join(javatestdir, 'Test.java')], shell=False)
- # Try to run the app
- cmd = self.env.JAVA + ['-cp', classpath, 'Test', classname]
- self.to_log("%s\n" % str(cmd))
- found = self.exec_command(cmd, shell=False)
- self.msg('Checking for java class %s' % classname, not found)
- shutil.rmtree(javatestdir, True)
- return found
- @conf
- def check_jni_headers(conf):
- """
- Checks for jni headers and libraries. On success the conf.env variables xxx_JAVA are added for use in C/C++ targets::
- def options(opt):
- opt.load('compiler_c')
- def configure(conf):
- conf.load('compiler_c java')
- conf.check_jni_headers()
- def build(bld):
- bld.shlib(source='a.c', target='app', use='JAVA')
- """
- if not conf.env.CC_NAME and not conf.env.CXX_NAME:
- conf.fatal('load a compiler first (gcc, g++, ..)')
- if not conf.env.JAVA_HOME:
- conf.fatal('set JAVA_HOME in the system environment')
- # jni requires the jvm
- javaHome = conf.env.JAVA_HOME[0]
- dir = conf.root.find_dir(conf.env.JAVA_HOME[0] + '/include')
- if dir is None:
- dir = conf.root.find_dir(conf.env.JAVA_HOME[0] + '/../Headers') # think different?!
- if dir is None:
- conf.fatal('JAVA_HOME does not seem to be set properly')
- f = dir.ant_glob('**/(jni|jni_md).h')
- incDirs = [x.parent.abspath() for x in f]
- dir = conf.root.find_dir(conf.env.JAVA_HOME[0])
- f = dir.ant_glob('**/*jvm.(so|dll|dylib)')
- libDirs = [x.parent.abspath() for x in f] or [javaHome]
- # On windows, we need both the .dll and .lib to link. On my JDK, they are
- # in different directories...
- f = dir.ant_glob('**/*jvm.(lib)')
- if f:
- libDirs = [[x, y.parent.abspath()] for x in libDirs for y in f]
- if conf.env.DEST_OS == 'freebsd':
- conf.env.append_unique('LINKFLAGS_JAVA', '-pthread')
- for d in libDirs:
- try:
- conf.check(header_name='jni.h', define_name='HAVE_JNI_H', lib='jvm',
- libpath=d, includes=incDirs, uselib_store='JAVA', uselib='JAVA')
- except Exception:
- pass
- else:
- break
- else:
- conf.fatal('could not find lib jvm in %r (see config.log)' % libDirs)
|