third_party:waf: update to upstream 2.0.4 release
[bbaumbach/samba-autobuild/.git] / third_party / waf / waflib / Tools / javaw.py
1 #! /usr/bin/env python
2 # encoding: utf-8
3 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file
4
5 #!/usr/bin/env python
6 # encoding: utf-8
7 # Thomas Nagy, 2006-2018 (ita)
8
9 """
10 Java support
11
12 Javac is one of the few compilers that behaves very badly:
13
14 #. it outputs files where it wants to (-d is only for the package root)
15
16 #. it recompiles files silently behind your back
17
18 #. it outputs an undefined amount of files (inner classes)
19
20 Remember that the compilation can be performed using Jython[1] rather than regular Python. Instead of
21 running one of the following commands::
22
23    ./waf configure
24    python waf configure
25
26 You would have to run::
27
28    java -jar /path/to/jython.jar waf configure
29
30 [1] http://www.jython.org/
31 """
32
33 import os, shutil
34 from waflib import Task, Utils, Errors, Node
35 from waflib.Configure import conf
36 from waflib.TaskGen import feature, before_method, after_method
37
38 from waflib.Tools import ccroot
39 ccroot.USELIB_VARS['javac'] = set(['CLASSPATH', 'JAVACFLAGS'])
40
41 SOURCE_RE = '**/*.java'
42 JAR_RE = '**/*'
43
44 class_check_source = '''
45 public class Test {
46         public static void main(String[] argv) {
47                 Class lib;
48                 if (argv.length < 1) {
49                         System.err.println("Missing argument");
50                         System.exit(77);
51                 }
52                 try {
53                         lib = Class.forName(argv[0]);
54                 } catch (ClassNotFoundException e) {
55                         System.err.println("ClassNotFoundException");
56                         System.exit(1);
57                 }
58                 lib = null;
59                 System.exit(0);
60         }
61 }
62 '''
63
64 @feature('javac')
65 @before_method('process_source')
66 def apply_java(self):
67         """
68         Create a javac task for compiling *.java files*. There can be
69         only one javac task by task generator.
70         """
71         Utils.def_attrs(self, jarname='', classpath='',
72                 sourcepath='.', srcdir='.',
73                 jar_mf_attributes={}, jar_mf_classpath=[])
74
75         outdir = getattr(self, 'outdir', None)
76         if outdir:
77                 if not isinstance(outdir, Node.Node):
78                         outdir = self.path.get_bld().make_node(self.outdir)
79         else:
80                 outdir = self.path.get_bld()
81         outdir.mkdir()
82         self.outdir = outdir
83         self.env.OUTDIR = outdir.abspath()
84
85         self.javac_task = tsk = self.create_task('javac')
86         tmp = []
87
88         srcdir = getattr(self, 'srcdir', '')
89         if isinstance(srcdir, Node.Node):
90                 srcdir = [srcdir]
91         for x in Utils.to_list(srcdir):
92                 if isinstance(x, Node.Node):
93                         y = x
94                 else:
95                         y = self.path.find_dir(x)
96                         if not y:
97                                 self.bld.fatal('Could not find the folder %s from %s' % (x, self.path))
98                 tmp.append(y)
99
100         tsk.srcdir = tmp
101
102         if getattr(self, 'compat', None):
103                 tsk.env.append_value('JAVACFLAGS', ['-source', str(self.compat)])
104
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])
108         else:
109                 names = [x.srcpath() for x in tsk.srcdir]
110
111         if names:
112                 tsk.env.append_value('JAVACFLAGS', ['-sourcepath', names])
113
114 @feature('javac')
115 @before_method('propagate_uselib_vars')
116 @after_method('apply_java')
117 def use_javac_files(self):
118         """
119         Processes the *use* attribute referring to other java compilations
120         """
121         lst = []
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
125         for x in names:
126                 try:
127                         y = get(x)
128                 except Errors.WafError:
129                         self.uselib.append(x)
130                 else:
131                         y.post()
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)
135                         else:
136                                 for tsk in y.tasks:
137                                         self.javac_task.set_run_after(tsk)
138         self.env.append_value('CLASSPATH', lst)
139
140 @feature('javac')
141 @after_method('apply_java', 'propagate_uselib_vars', 'use_javac_files')
142 def set_classpath(self):
143         """
144         Sets the CLASSPATH value on the *javac* task previously created.
145         """
146         if getattr(self, 'classpath', None):
147                 self.env.append_unique('CLASSPATH', getattr(self, 'classpath', []))
148         for x in self.tasks:
149                 x.env.CLASSPATH = os.pathsep.join(self.env.CLASSPATH) + os.pathsep
150
151 @feature('jar')
152 @after_method('apply_java', 'use_javac_files')
153 @before_method('process_source')
154 def jar_files(self):
155         """
156         Creates a jar task (one maximum per task generator)
157         """
158         destfile = getattr(self, 'destfile', 'test.jar')
159         jaropts = getattr(self, 'jaropts', [])
160         manifest = getattr(self, 'manifest', None)
161
162         basedir = getattr(self, 'basedir', None)
163         if basedir:
164                 if not isinstance(self.basedir, Node.Node):
165                         basedir = self.path.get_bld().make_node(basedir)
166         else:
167                 basedir = self.path.get_bld()
168         if not basedir:
169                 self.bld.fatal('Could not find the basedir %r for %r' % (self.basedir, self))
170
171         self.jar_task = tsk = self.create_task('jar_create')
172         if manifest:
173                 jarcreate = getattr(self, 'jarcreate', 'cfm')
174                 if not isinstance(manifest,Node.Node):
175                         node = self.path.find_resource(manifest)
176                 else:
177                         node = manifest
178                 if not node:
179                         self.bld.fatal('invalid manifest file %r for %r' % (manifest, self))
180                 tsk.dep_nodes.append(node)
181                 jaropts.insert(0, node.abspath())
182         else:
183                 jarcreate = getattr(self, 'jarcreate', 'cf')
184         if not isinstance(destfile, Node.Node):
185                 destfile = self.path.find_or_declare(destfile)
186         if not destfile:
187                 self.bld.fatal('invalid destfile %r for %r' % (destfile, self))
188         tsk.set_outputs(destfile)
189         tsk.basedir = basedir
190
191         jaropts.append('-C')
192         jaropts.append(basedir.bldpath())
193         jaropts.append('.')
194
195         tsk.env.JAROPTS = jaropts
196         tsk.env.JARCREATE = jarcreate
197
198         if getattr(self, 'javac_task', None):
199                 tsk.set_run_after(self.javac_task)
200
201 @feature('jar')
202 @after_method('jar_files')
203 def use_jar_files(self):
204         """
205         Processes the *use* attribute to set the build order on the
206         tasks created by another task generator.
207         """
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
211         for x in names:
212                 try:
213                         y = get(x)
214                 except Errors.WafError:
215                         self.uselib.append(x)
216                 else:
217                         y.post()
218                         self.jar_task.run_after.update(y.tasks)
219
220 class JTask(Task.Task):
221         """
222         Base class for java and jar tasks; provides functionality to run long commands
223         """
224         def split_argfile(self, cmd):
225                 inline = [cmd[0]]
226                 infile = []
227                 for x in cmd[1:]:
228                         # jar and javac do not want -J flags in @file
229                         if x.startswith('-J'):
230                                 inline.append(x)
231                         else:
232                                 infile.append(self.quote_flag(x))
233                 return (inline, infile)
234
235 class jar_create(JTask):
236         """
237         Creates a jar file
238         """
239         color   = 'GREEN'
240         run_str = '${JAR} ${JARCREATE} ${TGT} ${JAROPTS}'
241
242         def runnable_status(self):
243                 """
244                 Wait for dependent tasks to be executed, then read the
245                 files to update the list of inputs.
246                 """
247                 for t in self.run_after:
248                         if not t.hasrun:
249                                 return Task.ASK_LATER
250                 if not self.inputs:
251                         try:
252                                 self.inputs = [x for x in self.basedir.ant_glob(JAR_RE, remove=False) if id(x) != id(self.outputs[0])]
253                         except Exception:
254                                 raise Errors.WafError('Could not find the basedir %r for %r' % (self.basedir, self))
255                 return super(jar_create, self).runnable_status()
256
257 class javac(JTask):
258         """
259         Compiles java files
260         """
261         color   = 'BLUE'
262         run_str = '${JAVAC} -classpath ${CLASSPATH} -d ${OUTDIR} ${JAVACFLAGS} ${SRC}'
263         vars = ['CLASSPATH', 'JAVACFLAGS', 'JAVAC', 'OUTDIR']
264         """
265         The javac task will be executed again if the variables CLASSPATH, JAVACFLAGS, JAVAC or OUTDIR change.
266         """
267         def uid(self):
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)
273
274         def runnable_status(self):
275                 """
276                 Waits for dependent tasks to be complete, then read the file system to find the input nodes.
277                 """
278                 for t in self.run_after:
279                         if not t.hasrun:
280                                 return Task.ASK_LATER
281
282                 if not self.inputs:
283                         self.inputs  = []
284                         for x in self.srcdir:
285                                 if x.exists():
286                                         self.inputs.extend(x.ant_glob(SOURCE_RE, remove=False))
287                 return super(javac, self).runnable_status()
288
289         def post_run(self):
290                 """
291                 List class files created
292                 """
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
296
297 @feature('javadoc')
298 @after_method('process_rule')
299 def create_javadoc(self):
300         """
301         Creates a javadoc task (feature 'javadoc')
302         """
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)
308
309 class javadoc(Task.Task):
310         """
311         Builds java documentation
312         """
313         color = 'BLUE'
314
315         def __str__(self):
316                 return '%s: %s -> %s\n' % (self.__class__.__name__, self.generator.srcdir, self.generator.javadoc_output)
317
318         def run(self):
319                 env = self.env
320                 bld = self.generator.bld
321                 wd = bld.bldnode
322
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
327
328                 classpath = env.CLASSPATH
329                 classpath += os.pathsep
330                 classpath += os.pathsep.join(self.classpath)
331                 classpath = "".join(classpath)
332
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]
341
342                 self.generator.bld.cmd_and_log(lst, cwd=wd, env=env.env or None, quiet=0)
343
344         def post_run(self):
345                 nodes = self.generator.javadoc_output.ant_glob('**')
346                 for node in nodes:
347                         self.generator.bld.node_sigs[node] = self.uid()
348                 self.generator.bld.task_sigs[self.uid()] = self.cache_sig
349
350 def configure(self):
351         """
352         Detects the javac, java and jar programs
353         """
354         # If JAVA_PATH is set, we prepend it to the path list
355         java_path = self.environ['PATH'].split(os.pathsep)
356         v = self.env
357
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']]
361
362         for x in 'javac java jar javadoc'.split():
363                 self.find_program(x, var=x.upper(), path_list=java_path)
364
365         if 'CLASSPATH' in self.environ:
366                 v.CLASSPATH = self.environ['CLASSPATH']
367
368         if not v.JAR:
369                 self.fatal('jar is required for making java packages')
370         if not v.JAVAC:
371                 self.fatal('javac is required for compiling java classes')
372
373         v.JARCREATE = 'cf' # can use cvf
374         v.JAVACFLAGS = []
375
376 @conf
377 def check_java_class(self, classname, with_classpath=None):
378         """
379         Checks if the specified java class exists
380
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
385         """
386         javatestdir = '.waf-javatest'
387
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
393
394         shutil.rmtree(javatestdir, True)
395         os.mkdir(javatestdir)
396
397         Utils.writef(os.path.join(javatestdir, 'Test.java'), class_check_source)
398
399         # Compile the source
400         self.exec_command(self.env.JAVAC + [os.path.join(javatestdir, 'Test.java')], shell=False)
401
402         # Try to run the app
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)
406
407         self.msg('Checking for java class %s' % classname, not found)
408
409         shutil.rmtree(javatestdir, True)
410
411         return found
412
413 @conf
414 def check_jni_headers(conf):
415         """
416         Checks for jni headers and libraries. On success the conf.env variables xxx_JAVA are added for use in C/C++ targets::
417
418                 def options(opt):
419                         opt.load('compiler_c')
420
421                 def configure(conf):
422                         conf.load('compiler_c java')
423                         conf.check_jni_headers()
424
425                 def build(bld):
426                         bld.shlib(source='a.c', target='app', use='JAVA')
427         """
428         if not conf.env.CC_NAME and not conf.env.CXX_NAME:
429                 conf.fatal('load a compiler first (gcc, g++, ..)')
430
431         if not conf.env.JAVA_HOME:
432                 conf.fatal('set JAVA_HOME in the system environment')
433
434         # jni requires the jvm
435         javaHome = conf.env.JAVA_HOME[0]
436
437         dir = conf.root.find_dir(conf.env.JAVA_HOME[0] + '/include')
438         if dir is None:
439                 dir = conf.root.find_dir(conf.env.JAVA_HOME[0] + '/../Headers') # think different?!
440         if dir is None:
441                 conf.fatal('JAVA_HOME does not seem to be set properly')
442
443         f = dir.ant_glob('**/(jni|jni_md).h')
444         incDirs = [x.parent.abspath() for x in f]
445
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]
449
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)')
453         if f:
454                 libDirs = [[x, y.parent.abspath()] for x in libDirs for y in f]
455
456         if conf.env.DEST_OS == 'freebsd':
457                 conf.env.append_unique('LINKFLAGS_JAVA', '-pthread')
458         for d in libDirs:
459                 try:
460                         conf.check(header_name='jni.h', define_name='HAVE_JNI_H', lib='jvm',
461                                 libpath=d, includes=incDirs, uselib_store='JAVA', uselib='JAVA')
462                 except Exception:
463                         pass
464                 else:
465                         break
466         else:
467                 conf.fatal('could not find lib jvm in %r (see config.log)' % libDirs)
468