renamed SCons.Tool.ninja -> SCons.Tool.ninja_tool and added alias in tool loading...
[scons.git] / SCons / Tool / jar.py
blob13bdca051878d1b0902058eda14f9ab4c320b8a7
1 # MIT License
3 # Copyright The SCons Foundation
5 # Permission is hereby granted, free of charge, to any person obtaining
6 # a copy of this software and associated documentation files (the
7 # "Software"), to deal in the Software without restriction, including
8 # without limitation the rights to use, copy, modify, merge, publish,
9 # distribute, sublicense, and/or sell copies of the Software, and to
10 # permit persons to whom the Software is furnished to do so, subject to
11 # the following conditions:
13 # The above copyright notice and this permission notice shall be included
14 # in all copies or substantial portions of the Software.
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
17 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
18 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 """Tool-specific initialization for jar.
26 There normally shouldn't be any need to import this module directly.
27 It will usually be imported through the generic SCons.Tool.Tool()
28 selection method.
29 """
31 from __future__ import annotations
33 import os
35 import SCons.Node
36 import SCons.Node.FS
37 import SCons.Subst
38 import SCons.Tool
39 import SCons.Util
40 import SCons.Warnings
41 from SCons.Node.FS import _my_normcase
42 from SCons.Tool.JavaCommon import get_java_install_dirs
45 def jarSources(target, source, env, for_signature) -> list[str]:
46 """Only include sources that are not a manifest file."""
47 try:
48 env['JARCHDIR']
49 except KeyError:
50 jarchdir_set = False
51 else:
52 jarchdir_set = True
53 jarchdir = env.subst('$JARCHDIR', target=target, source=source)
54 if jarchdir:
55 jarchdir = env.fs.Dir(jarchdir)
56 result = []
57 for src in source:
58 contents = src.get_text_contents()
59 if contents.startswith("Manifest-Version"):
60 continue
61 if jarchdir_set:
62 _chdir = jarchdir
63 else:
64 try:
65 _chdir = src.attributes.java_classdir
66 except AttributeError:
67 _chdir = None
68 if _chdir:
69 # If we are changing the dir with -C, then sources should
70 # be relative to that directory.
71 src = SCons.Subst.Literal(src.get_path(_chdir))
72 result.append('-C')
73 result.append(_chdir)
74 result.append(src)
75 return result
77 def jarManifest(target, source, env, for_signature):
78 """Look in sources for a manifest file, if any."""
79 for src in source:
80 contents = src.get_text_contents()
81 if contents.startswith("Manifest-Version"):
82 return src
83 return ''
85 def jarFlags(target, source, env, for_signature) -> str:
86 """If we have a manifest, make sure that the 'm'
87 flag is specified."""
88 jarflags = env.subst('$JARFLAGS', target=target, source=source)
89 for src in source:
90 contents = src.get_text_contents()
91 if contents.startswith("Manifest-Version"):
92 if 'm' not in jarflags:
93 return jarflags + 'm'
94 break
95 return jarflags
97 def Jar(env, target=None, source=[], *args, **kw):
98 """The Jar Builder.
100 This is a pseudo-Builder wrapper around the separate jar builders
101 depending on whether the sources are a file list or a directory.
103 # TODO: W1113: Keyword argument before variable positional arguments list in the definition of Jar function
104 # TODO: W0102: Dangerous default value [] as argument
106 # jar target should not be a list so assume they passed
107 # no target and want implicit target to be made and the arg
108 # was actaully the list of sources
109 if SCons.Util.is_List(target) and source == []:
110 SCons.Warnings.warn(
111 SCons.Warnings.SConsWarning,
112 "Making implicit target jar file, and treating the list as sources"
114 source = target
115 target = None
117 # mutiple targets passed so build each target the same from the
118 # same source
119 #TODO Maybe this should only be done once, and the result copied
120 # for each target since it should result in the same?
121 if SCons.Util.is_List(target) and SCons.Util.is_List(source):
122 jars = []
123 for single_target in target:
124 jars += env.Jar(target=single_target, source=source, *args, **kw)
125 return jars
127 # they passed no target so make a target implicitly
128 if target is None:
129 try:
130 # make target from the first source file
131 target = os.path.splitext(str(source[0]))[0] + env.subst('$JARSUFFIX')
132 except:
133 # TODO: W0702: No exception type(s) specified
134 # something strange is happening but attempt anyways
135 SCons.Warnings.warn(
136 SCons.Warnings.SConsWarning,
137 "Could not make implicit target from sources, using directory"
139 target = os.path.basename(str(env.Dir('.'))) + env.subst('$JARSUFFIX')
141 # make lists out of our target and sources
142 if not SCons.Util.is_List(target):
143 target = [target]
144 if not SCons.Util.is_List(source):
145 source = [source]
147 # setup for checking through all the sources and handle accordingly
148 java_class_suffix = env.subst('$JAVACLASSSUFFIX')
149 java_suffix = env.subst('$JAVASUFFIX')
150 target_nodes = []
152 # function for determining what to do with a file and not a directory
153 # if its already a class file then it can be used as a
154 # source for jar, otherwise turn it into a class file then
155 # return the source
156 def file_to_class(src):
157 if _my_normcase(str(src)).endswith(java_suffix):
158 return env.JavaClassFile(source=src, *args, **kw)
159 return [env.fs.File(src)]
161 # function for calling the JavaClassDir builder if a directory is
162 # passed as a source to Jar builder. The JavaClassDir builder will
163 # return an empty list if there were no target classes built from
164 # the directory, in this case assume the user wanted the directory
165 # copied into the jar as is (it contains other files such as
166 # resources or class files compiled from prior commands)
167 # TODO: investigate the expexcted behavior for directories that
168 # have mixed content, such as Java files along side other files
169 # files.
170 def dir_to_class(src):
171 dir_targets = env.JavaClassDir(source=src, *args, **kw)
172 if dir_targets == []:
173 # no classes files could be built from the source dir
174 # so pass the dir as is.
175 return [env.fs.Dir(src)]
176 return dir_targets
178 # loop through the sources and handle each accordingly
179 # the goal here is to get all the source files into a class
180 # file or a directory that contains class files
181 for src in SCons.Util.flatten(source):
182 src = env.subst(src)
183 if isinstance(src, SCons.Node.FS.Base):
184 if isinstance(src, SCons.Node.FS.File):
185 # found a file so make sure its a class file
186 target_nodes.extend(file_to_class(src))
187 else:
188 # found a dir so get the class files out of it
189 target_nodes.extend(dir_to_class(src))
190 else:
191 try:
192 # source is string try to convert it to file
193 target_nodes.extend(file_to_class(env.fs.File(src)))
194 continue
195 except:
196 # TODO: W0702: No exception type(s) specified
197 pass
199 try:
200 # source is string try to covnert it to dir
201 target_nodes.extend(dir_to_class(env.fs.Dir(src)))
202 continue
203 except:
204 # TODO: W0702: No exception type(s) specified
205 pass
207 SCons.Warnings.warn(
208 SCons.Warnings.SConsWarning,
209 ("File: " + str(src)
210 + " could not be identified as File or Directory, skipping.")
213 # at this point all our sources have been converted to classes or
214 # directories of class so pass it to the Jar builder
215 return env.JarFile(target=target, source=target_nodes, *args, **kw)
217 def generate(env) -> None:
218 """Add Builders and construction variables for jar to an Environment."""
219 SCons.Tool.CreateJarBuilder(env)
221 SCons.Tool.CreateJavaFileBuilder(env)
222 SCons.Tool.CreateJavaClassFileBuilder(env)
223 SCons.Tool.CreateJavaClassDirBuilder(env)
225 env.AddMethod(Jar)
227 if env['PLATFORM'] == 'win32':
228 # Ensure that we have a proper path for jar
229 paths = get_java_install_dirs('win32')
230 jar = SCons.Tool.find_program_path(env, 'jar', default_paths=paths)
231 if jar:
232 jar_bin_dir = os.path.dirname(jar)
233 env.AppendENVPath('PATH', jar_bin_dir)
235 env['JAR'] = 'jar'
236 env['JARFLAGS'] = SCons.Util.CLVar('cf')
237 env['_JARFLAGS'] = jarFlags
238 env['_JARMANIFEST'] = jarManifest
239 env['_JARSOURCES'] = jarSources
240 env['_JARCOM'] = '$JAR $_JARFLAGS $TARGET $_JARMANIFEST $_JARSOURCES'
241 env['JARCOM'] = "${TEMPFILE('$_JARCOM','$JARCOMSTR')}"
242 env['JARSUFFIX'] = '.jar'
244 def exists(env) -> bool:
245 # As reported by Jan Nijtmans in issue #2730, the simple
246 # return env.Detect('jar')
247 # doesn't always work during initialization. For now, we
248 # stop trying to detect an executable (analogous to the
249 # javac Builder).
250 # TODO: Come up with a proper detect() routine...and enable it.
251 return True
253 # Local Variables:
254 # tab-width:4
255 # indent-tabs-mode:nil
256 # End:
257 # vim: set expandtab tabstop=4 shiftwidth=4: