1"""SCons.Tool.javac
2
3Tool-specific initialization for javac.
4
5There normally shouldn't be any need to import this module directly.
6It will usually be imported through the generic SCons.Tool.Tool()
7selection method.
8
9"""
10
11#
12# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
13#
14# Permission is hereby granted, free of charge, to any person obtaining
15# a copy of this software and associated documentation files (the
16# "Software"), to deal in the Software without restriction, including
17# without limitation the rights to use, copy, modify, merge, publish,
18# distribute, sublicense, and/or sell copies of the Software, and to
19# permit persons to whom the Software is furnished to do so, subject to
20# the following conditions:
21#
22# The above copyright notice and this permission notice shall be included
23# in all copies or substantial portions of the Software.
24#
25# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
26# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
27# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32#
33
34__revision__ = "src/engine/SCons/Tool/javac.py 4369 2009/09/19 15:58:29 scons"
35
36import os
37import os.path
38import string
39
40import SCons.Action
41import SCons.Builder
42from SCons.Node.FS import _my_normcase
43from SCons.Tool.JavaCommon import parse_java_file
44import SCons.Util
45
46def classname(path):
47    """Turn a string (path name) into a Java class name."""
48    return string.replace(os.path.normpath(path), os.sep, '.')
49
50def emit_java_classes(target, source, env):
51    """Create and return lists of source java files
52    and their corresponding target class files.
53    """
54    java_suffix = env.get('JAVASUFFIX', '.java')
55    class_suffix = env.get('JAVACLASSSUFFIX', '.class')
56
57    target[0].must_be_same(SCons.Node.FS.Dir)
58    classdir = target[0]
59
60    s = source[0].rentry().disambiguate()
61    if isinstance(s, SCons.Node.FS.File):
62        sourcedir = s.dir.rdir()
63    elif isinstance(s, SCons.Node.FS.Dir):
64        sourcedir = s.rdir()
65    else:
66        raise SCons.Errors.UserError("Java source must be File or Dir, not '%s'" % s.__class__)
67
68    slist = []
69    js = _my_normcase(java_suffix)
70    find_java = lambda n, js=js, ljs=len(js): _my_normcase(n[-ljs:]) == js
71    for entry in source:
72        entry = entry.rentry().disambiguate()
73        if isinstance(entry, SCons.Node.FS.File):
74            slist.append(entry)
75        elif isinstance(entry, SCons.Node.FS.Dir):
76            result = SCons.Util.OrderedDict()
77            def visit(arg, dirname, names, fj=find_java, dirnode=entry.rdir()):
78                java_files = sorted(filter(fj, names))
79                # The on-disk entries come back in arbitrary order.  Sort
80                # them so our target and source lists are determinate.
81                mydir = dirnode.Dir(dirname)
82                java_paths = map(lambda f, d=mydir: d.File(f), java_files)
83                for jp in java_paths:
84                     arg[jp] = True
85
86            os.path.walk(entry.rdir().get_abspath(), visit, result)
87            entry.walk(visit, result)
88
89            slist.extend(result.keys())
90        else:
91            raise SCons.Errors.UserError("Java source must be File or Dir, not '%s'" % entry.__class__)
92
93    version = env.get('JAVAVERSION', '1.4')
94    full_tlist = []
95    for f in slist:
96        tlist = []
97        source_file_based = True
98        pkg_dir = None
99        if not f.is_derived():
100            pkg_dir, classes = parse_java_file(f.rfile().get_abspath(), version)
101            if classes:
102                source_file_based = False
103                if pkg_dir:
104                    d = target[0].Dir(pkg_dir)
105                    p = pkg_dir + os.sep
106                else:
107                    d = target[0]
108                    p = ''
109                for c in classes:
110                    t = d.File(c + class_suffix)
111                    t.attributes.java_classdir = classdir
112                    t.attributes.java_sourcedir = sourcedir
113                    t.attributes.java_classname = classname(p + c)
114                    tlist.append(t)
115
116        if source_file_based:
117            base = f.name[:-len(java_suffix)]
118            if pkg_dir:
119                t = target[0].Dir(pkg_dir).File(base + class_suffix)
120            else:
121                t = target[0].File(base + class_suffix)
122            t.attributes.java_classdir = classdir
123            t.attributes.java_sourcedir = f.dir
124            t.attributes.java_classname = classname(base)
125            tlist.append(t)
126
127        for t in tlist:
128            t.set_specific_source([f])
129
130        full_tlist.extend(tlist)
131
132    return full_tlist, slist
133
134JavaAction = SCons.Action.Action('$JAVACCOM', '$JAVACCOMSTR')
135
136JavaBuilder = SCons.Builder.Builder(action = JavaAction,
137                    emitter = emit_java_classes,
138                    target_factory = SCons.Node.FS.Entry,
139                    source_factory = SCons.Node.FS.Entry)
140
141class pathopt:
142    """
143    Callable object for generating javac-style path options from
144    a construction variable (e.g. -classpath, -sourcepath).
145    """
146    def __init__(self, opt, var, default=None):
147        self.opt = opt
148        self.var = var
149        self.default = default
150
151    def __call__(self, target, source, env, for_signature):
152        path = env[self.var]
153        if path and not SCons.Util.is_List(path):
154            path = [path]
155        if self.default:
156            path = path + [ env[self.default] ]
157        if path:
158            return [self.opt, string.join(path, os.pathsep)]
159            #return self.opt + " " + string.join(path, os.pathsep)
160        else:
161            return []
162            #return ""
163
164def Java(env, target, source, *args, **kw):
165    """
166    A pseudo-Builder wrapper around the separate JavaClass{File,Dir}
167    Builders.
168    """
169    if not SCons.Util.is_List(target):
170        target = [target]
171    if not SCons.Util.is_List(source):
172        source = [source]
173
174    # Pad the target list with repetitions of the last element in the
175    # list so we have a target for every source element.
176    target = target + ([target[-1]] * (len(source) - len(target)))
177
178    java_suffix = env.subst('$JAVASUFFIX')
179    result = []
180
181    for t, s in zip(target, source):
182        if isinstance(s, SCons.Node.FS.Base):
183            if isinstance(s, SCons.Node.FS.File):
184                b = env.JavaClassFile
185            else:
186                b = env.JavaClassDir
187        else:
188            if os.path.isfile(s):
189                b = env.JavaClassFile
190            elif os.path.isdir(s):
191                b = env.JavaClassDir
192            elif s[-len(java_suffix):] == java_suffix:
193                b = env.JavaClassFile
194            else:
195                b = env.JavaClassDir
196        result.extend(b(*(t, s) + args, **kw))
197
198    return result
199
200def generate(env):
201    """Add Builders and construction variables for javac to an Environment."""
202    java_file = SCons.Tool.CreateJavaFileBuilder(env)
203    java_class = SCons.Tool.CreateJavaClassFileBuilder(env)
204    java_class_dir = SCons.Tool.CreateJavaClassDirBuilder(env)
205    java_class.add_emitter(None, emit_java_classes)
206    java_class.add_emitter(env.subst('$JAVASUFFIX'), emit_java_classes)
207    java_class_dir.emitter = emit_java_classes
208
209    env.AddMethod(Java)
210
211    env['JAVAC']                    = 'javac'
212    env['JAVACFLAGS']               = SCons.Util.CLVar('')
213    env['JAVABOOTCLASSPATH']        = []
214    env['JAVACLASSPATH']            = []
215    env['JAVASOURCEPATH']           = []
216    env['_javapathopt']             = pathopt
217    env['_JAVABOOTCLASSPATH']       = '${_javapathopt("-bootclasspath", "JAVABOOTCLASSPATH")} '
218    env['_JAVACLASSPATH']           = '${_javapathopt("-classpath", "JAVACLASSPATH")} '
219    env['_JAVASOURCEPATH']          = '${_javapathopt("-sourcepath", "JAVASOURCEPATH", "_JAVASOURCEPATHDEFAULT")} '
220    env['_JAVASOURCEPATHDEFAULT']   = '${TARGET.attributes.java_sourcedir}'
221    env['_JAVACCOM']                = '$JAVAC $JAVACFLAGS $_JAVABOOTCLASSPATH $_JAVACLASSPATH -d ${TARGET.attributes.java_classdir} $_JAVASOURCEPATH $SOURCES'
222    env['JAVACCOM']                 = "${TEMPFILE('$_JAVACCOM')}"
223    env['JAVACLASSSUFFIX']          = '.class'
224    env['JAVASUFFIX']               = '.java'
225
226def exists(env):
227    return 1
228
229# Local Variables:
230# tab-width:4
231# indent-tabs-mode:nil
232# End:
233# vim: set expandtab tabstop=4 shiftwidth=4:
234