diff --git a/src/engine/SCons/Tool/__init__.py b/src/engine/SCons/Tool/__init__.py index a4e44a0f16..42f84e118b 100644 --- a/src/engine/SCons/Tool/__init__.py +++ b/src/engine/SCons/Tool/__init__.py @@ -913,15 +913,25 @@ def createCFileBuilders(env): # Create common Java builders def CreateJarBuilder(env): + """The Jar builder expects a list of class files + which it can package into a jar file. + + The jar tool provides an interface for passing other types + of java files such as .java, directories or swig interfaces + and will build them to class files in which it can package + into the jar. + """ try: - java_jar = env['BUILDERS']['Jar'] + java_jar = env['BUILDERS']['JarFile'] except KeyError: fs = SCons.Node.FS.get_default_fs() jar_com = SCons.Action.Action('$JARCOM', '$JARCOMSTR') java_jar = SCons.Builder.Builder(action = jar_com, suffix = '$JARSUFFIX', + src_suffix = '$JAVACLASSSUFFIX', + src_builder = 'JavaClassFile', source_factory = fs.Entry) - env['BUILDERS']['Jar'] = java_jar + env['BUILDERS']['JarFile'] = java_jar return java_jar def CreateJavaHBuilder(env): diff --git a/src/engine/SCons/Tool/jar.py b/src/engine/SCons/Tool/jar.py index 830892702e..62cd86a267 100644 --- a/src/engine/SCons/Tool/jar.py +++ b/src/engine/SCons/Tool/jar.py @@ -35,6 +35,8 @@ import SCons.Subst import SCons.Util +from SCons.Node.FS import _my_normcase +import os def jarSources(target, source, env, for_signature): """Only include sources that are not a manifest file.""" @@ -87,10 +89,115 @@ def jarFlags(target, source, env, for_signature): break return jarflags +def Jar(env, target = None, source = [], *args, **kw): + """ + A pseudo-Builder wrapper around the separate Jar sources{File,Dir} + Builders. + """ + + # jar target should not be a list so assume they passed + # no target and want implicit target to be made and the arg + # was actaully the list of sources + if SCons.Util.is_List(target): + source = target + target = None + + # they passed no target so make a target implicitly + if target == None: + try: + # make target from the first source file + target = os.path.splitext(str(source[0]))[0] + env.subst('$JARSUFFIX') + except: + # something strange is happening but attempt anyways + SCons.Warning.Warning("Could not make implicit target from sources, using directory") + target = os.path.basename(str(env.Dir('.'))) + env.subst('$JARSUFFIX') + + # make lists out of our target and sources + if not SCons.Util.is_List(target): + target = [target] + if not SCons.Util.is_List(source): + source = [source] + + # Pad the target list with repetitions of the last element in the + # list so we have a target for every source element. + target = target + ([target[-1]] * (len(source) - len(target))) + + # setup for checking through all the sources and handle accordingly + java_class_suffix = env.subst('$JAVACLASSSUFFIX') + java_suffix = env.subst('$JAVASUFFIX') + target_classes = [] + + # function for determining what to do with a file and not a directory + # if its already a class file then it can be used as a + # source for jar, otherwise turn it into a class file then + # return the source + def file_to_class(s): + if(str(_my_normcase(s)).endswith(java_suffix)): + return env.JavaClassFile(source = s, *args, **kw) + else: + return [env.fs.File(s)] + + # In the case that we are passed just string to a node which is directory + # but does not exist, we need to check all the current targets to see if + # that directory is going to exist so we can add it as a source to Jar builder + def get_all_targets(env, node='.'): + def get_all_targets_iter(env, node): + if node.has_builder(): + yield node + for kid in node.all_children(): + for kid in get_all_targets(env, kid): + yield kid + node = env.arg2nodes(node, env.fs.Entry)[0] + return list(get_all_targets_iter(env, node)) + + # loop through the sources and handle each accordingly + # the goal here is to get all the source files into a class + # file or a directory that contains class files + for s in source: + s = env.subst(s) + if isinstance(s, SCons.Node.FS.Base): + if isinstance(s, SCons.Node.FS.File): + # found a file so make sure its a class file + target_classes.extend(file_to_class(s)) + else: + # found a dir so make sure its a dir of class files + target_classes.extend(env.JavaClassDir(source = env.fs.Dir(s), *args, **kw)) + else: + if os.path.isfile(s): + # found a file that exists on the FS, make sure its a class file + target_classes.extend(file_to_class(s)) + elif os.path.isdir(s): + # found a dir on the FS, add it as a dir of class files + target_classes.append(env.fs.Dir(s)) + elif s[-len(java_suffix):] == java_suffix or s[-len(java_class_suffix):] == java_class_suffix: + # found a file that may not exists and is only a string + # so add it after converting it to a class file + target_classes.extend(file_to_class(s)) + else: + # found a swig file so add it after converting it to class files + if(os.path.splitext(str(s))[1] == ".i"): + target_classes.extend(env.JavaClassFile(source = s, *args, **kw)) + else: + # found a directory that does not yet exist, but can exist as a node + # check the target nodes to make sure it will be built, then add + # it as a source + for node in get_all_targets(env): + if(s in str(node) and os.path.splitext(str(node))[1] == ""): + target_classes.append(node) + # at this point all our sources have been converted to classes or directories of class + # so pass it to the Jar builder + return env.JarFile(target = target, source = target_classes, *args, **kw) + def generate(env): """Add Builders and construction variables for jar to an Environment.""" SCons.Tool.CreateJarBuilder(env) + SCons.Tool.CreateJavaFileBuilder(env) + SCons.Tool.CreateJavaClassFileBuilder(env) + SCons.Tool.CreateJavaClassDirBuilder(env) + + env.AddMethod(Jar) + env['JAR'] = 'jar' env['JARFLAGS'] = SCons.Util.CLVar('cf') env['_JARFLAGS'] = jarFlags