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()
31 from __future__
import annotations
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."""
53 jarchdir
= env
.subst('$JARCHDIR', target
=target
, source
=source
)
55 jarchdir
= env
.fs
.Dir(jarchdir
)
58 contents
= src
.get_text_contents()
59 if contents
.startswith("Manifest-Version"):
65 _chdir
= src
.attributes
.java_classdir
66 except AttributeError:
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
))
77 def jarManifest(target
, source
, env
, for_signature
):
78 """Look in sources for a manifest file, if any."""
80 contents
= src
.get_text_contents()
81 if contents
.startswith("Manifest-Version"):
85 def jarFlags(target
, source
, env
, for_signature
) -> str:
86 """If we have a manifest, make sure that the 'm'
88 jarflags
= env
.subst('$JARFLAGS', target
=target
, source
=source
)
90 contents
= src
.get_text_contents()
91 if contents
.startswith("Manifest-Version"):
92 if 'm' not in jarflags
:
97 def Jar(env
, target
=None, source
=[], *args
, **kw
):
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
== []:
111 SCons
.Warnings
.SConsWarning
,
112 "Making implicit target jar file, and treating the list as sources"
117 # mutiple targets passed so build each target the same from the
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
):
123 for single_target
in target
:
124 jars
+= env
.Jar(target
=single_target
, source
=source
, *args
, **kw
)
127 # they passed no target so make a target implicitly
130 # make target from the first source file
131 target
= os
.path
.splitext(str(source
[0]))[0] + env
.subst('$JARSUFFIX')
133 # TODO: W0702: No exception type(s) specified
134 # something strange is happening but attempt anyways
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
):
144 if not SCons
.Util
.is_List(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')
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
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
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
)]
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
):
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
))
188 # found a dir so get the class files out of it
189 target_nodes
.extend(dir_to_class(src
))
192 # source is string try to convert it to file
193 target_nodes
.extend(file_to_class(env
.fs
.File(src
)))
196 # TODO: W0702: No exception type(s) specified
200 # source is string try to covnert it to dir
201 target_nodes
.extend(dir_to_class(env
.fs
.Dir(src
)))
204 # TODO: W0702: No exception type(s) specified
208 SCons
.Warnings
.SConsWarning
,
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
)
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
)
232 jar_bin_dir
= os
.path
.dirname(jar
)
233 env
.AppendENVPath('PATH', jar_bin_dir
)
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
250 # TODO: Come up with a proper detect() routine...and enable it.
255 # indent-tabs-mode:nil
257 # vim: set expandtab tabstop=4 shiftwidth=4: