3 # Thomas Nagy, 2006-2008 (ita)
8 Javac is one of the few compilers that behaves very badly:
9 * it outputs files where it wants to (-d is only for the package root)
10 * it recompiles files silently behind your back
11 * it outputs an undefined amount of files (inner classes)
13 Fortunately, the convention makes it possible to use the build dir without
14 too many problems for the moment
16 Inner classes must be located and cleaned when a problem arise,
17 for the moment waf does not track the production of inner classes.
19 Adding all the files to a task and executing it if any of the input files
20 change is only annoying for the compilation times
22 Compilation can be run using Jython[1] rather than regular Python. Instead of
23 running one of the following commands:
26 You would have to run:
27 java -jar /path/to/jython.jar waf configure
29 [1] http://www.jython.org/
33 from Configure
import conf
34 import TaskGen
, Task
, Utils
, Options
, Build
35 from TaskGen
import feature
, before
, taskgen
37 class_check_source
= '''
39 public static void main(String[] argv) {
41 if (argv.length < 1) {
42 System.err.println("Missing argument");
46 lib = Class.forName(argv[0]);
47 } catch (ClassNotFoundException e) {
48 System.err.println("ClassNotFoundException");
60 basedir
= getattr(self
, 'basedir', '.')
61 destfile
= getattr(self
, 'destfile', 'test.jar')
62 jaropts
= getattr(self
, 'jaropts', [])
63 jarcreate
= getattr(self
, 'jarcreate', 'cf')
65 dir = self
.path
.find_dir(basedir
)
69 jaropts
.append(dir.abspath(self
.env
))
72 out
= self
.path
.find_or_declare(destfile
)
74 tsk
= self
.create_task('jar_create')
76 tsk
.inputs
= [x
for x
in dir.find_iter(src
=0, bld
=1) if x
.id != out
.id]
77 tsk
.env
['JAROPTS'] = jaropts
78 tsk
.env
['JARCREATE'] = jarcreate
83 Utils
.def_attrs(self
, jarname
='', jaropts
='', classpath
='',
84 sourcepath
='.', srcdir
='.', source_re
='**/*.java',
85 jar_mf_attributes
={}, jar_mf_classpath
=[])
87 if getattr(self
, 'source_root', None):
89 self
.srcdir
= self
.source_root
94 if not self
.classpath
:
95 if not self
.env
['CLASSPATH']:
96 self
.env
['CLASSPATH'] = '..' + os
.pathsep
+ '.'
98 self
.env
['CLASSPATH'] = self
.classpath
100 srcdir_node
= self
.path
.find_dir(self
.srcdir
)
102 raise Utils
.WafError('could not find srcdir %r' % self
.srcdir
)
104 src_nodes
= [x
for x
in srcdir_node
.ant_glob(self
.source_re
, flat
=False)]
105 bld_nodes
= [x
.change_ext('.class') for x
in src_nodes
]
107 self
.env
['OUTDIR'] = [srcdir_node
.bldpath(self
.env
)]
109 tsk
= self
.create_task('javac')
110 tsk
.set_inputs(src_nodes
)
111 tsk
.set_outputs(bld_nodes
)
113 if getattr(self
, 'compat', None):
114 tsk
.env
.append_value('JAVACFLAGS', ['-source', self
.compat
])
116 if hasattr(self
, 'sourcepath'):
117 fold
= [self
.path
.find_dir(x
) for x
in self
.to_list(self
.sourcepath
)]
118 names
= os
.pathsep
.join([x
.srcpath() for x
in fold
])
120 names
= srcdir_node
.srcpath()
123 tsk
.env
.append_value('JAVACFLAGS', ['-sourcepath', names
])
126 jtsk
= self
.create_task('jar_create', bld_nodes
, self
.path
.find_or_declare(self
.jarname
))
127 jtsk
.set_run_after(tsk
)
129 if not self
.env
.JAROPTS
:
131 self
.env
.JAROPTS
= self
.jaropts
134 self
.env
.JAROPTS
= ['-C', ''.join(self
.env
['OUTDIR']), dirs
]
136 Task
.simple_task_type('jar_create', '${JAR} ${JARCREATE} ${TGT} ${JAROPTS}', color
='GREEN', shell
=False)
137 cls
= Task
.simple_task_type('javac', '${JAVAC} -classpath ${CLASSPATH} -d ${OUTDIR} ${JAVACFLAGS} ${SRC}', shell
=False)
139 def post_run_javac(self
):
140 """this is for cleaning the folder
141 javac creates single files for inner classes
142 but it is not possible to know which inner classes in advance"""
145 for x
in self
.inputs
:
146 par
[x
.parent
.id] = x
.parent
149 for k
in par
.values():
150 path
= k
.abspath(self
.env
)
151 lst
= os
.listdir(path
)
155 inner_class_node
= k
.find_or_declare(u
)
156 inner
[inner_class_node
.id] = inner_class_node
158 to_add
= set(inner
.keys()) - set([x
.id for x
in self
.outputs
])
160 self
.outputs
.append(inner
[x
])
162 self
.cached
= True # disable the cache here - inner classes are a problem
163 return Task
.Task
.post_run(self
)
164 cls
.post_run
= post_run_javac
167 # If JAVA_PATH is set, we prepend it to the path list
168 java_path
= conf
.environ
['PATH'].split(os
.pathsep
)
171 if 'JAVA_HOME' in conf
.environ
:
172 java_path
= [os
.path
.join(conf
.environ
['JAVA_HOME'], 'bin')] + java_path
173 conf
.env
['JAVA_HOME'] = [conf
.environ
['JAVA_HOME']]
175 for x
in 'javac java jar'.split():
176 conf
.find_program(x
, var
=x
.upper(), path_list
=java_path
)
177 conf
.env
[x
.upper()] = conf
.cmd_to_list(conf
.env
[x
.upper()])
178 v
['JAVA_EXT'] = ['.java']
180 if 'CLASSPATH' in conf
.environ
:
181 v
['CLASSPATH'] = conf
.environ
['CLASSPATH']
183 if not v
['JAR']: conf
.fatal('jar is required for making java packages')
184 if not v
['JAVAC']: conf
.fatal('javac is required for compiling java classes')
185 v
['JARCREATE'] = 'cf' # can use cvf
188 def check_java_class(self
, classname
, with_classpath
=None):
189 """Check if the specified java class is installed"""
193 javatestdir
= '.waf-javatest'
195 classpath
= javatestdir
196 if self
.env
['CLASSPATH']:
197 classpath
+= os
.pathsep
+ self
.env
['CLASSPATH']
198 if isinstance(with_classpath
, str):
199 classpath
+= os
.pathsep
+ with_classpath
201 shutil
.rmtree(javatestdir
, True)
202 os
.mkdir(javatestdir
)
204 java_file
= open(os
.path
.join(javatestdir
, 'Test.java'), 'w')
205 java_file
.write(class_check_source
)
209 Utils
.exec_command(self
.env
['JAVAC'] + [os
.path
.join(javatestdir
, 'Test.java')], shell
=False)
212 cmd
= self
.env
['JAVA'] + ['-cp', classpath
, 'Test', classname
]
213 self
.log
.write("%s\n" % str(cmd
))
214 found
= Utils
.exec_command(cmd
, shell
=False, log
=self
.log
)
216 self
.check_message('Java class %s' % classname
, "", not found
)
218 shutil
.rmtree(javatestdir
, True)
223 def check_jni_headers(conf
):
225 Check for jni headers and libraries
227 On success the environment variable xxx_JAVA is added for uselib
230 if not conf
.env
.CC_NAME
and not conf
.env
.CXX_NAME
:
231 conf
.fatal('load a compiler first (gcc, g++, ..)')
233 if not conf
.env
.JAVA_HOME
:
234 conf
.fatal('set JAVA_HOME in the system environment')
236 # jni requires the jvm
237 javaHome
= conf
.env
['JAVA_HOME'][0]
239 b
= Build
.BuildContext()
240 b
.load_dirs(conf
.srcdir
, conf
.blddir
)
241 dir = b
.root
.find_dir(conf
.env
.JAVA_HOME
[0] + '/include')
242 f
= dir.ant_glob('**/(jni|jni_md).h', flat
=False)
243 incDirs
= [x
.parent
.abspath() for x
in f
]
245 dir = b
.root
.find_dir(conf
.env
.JAVA_HOME
[0])
246 f
= dir.ant_glob('**/*jvm.(so|dll)', flat
=False)
247 libDirs
= [x
.parent
.abspath() for x
in f
] or [javaHome
]
249 for i
, d
in enumerate(libDirs
):
250 if conf
.check(header_name
='jni.h', define_name
='HAVE_JNI_H', lib
='jvm',
251 libpath
=d
, includes
=incDirs
, uselib_store
='JAVA', uselib
='JAVA'):
254 conf
.fatal('could not find lib jvm in %r (see config.log)' % libDirs
)