Skip to content

Commit f709352

Browse files
authored
Merge pull request #3761 from djh82/feature/minor_java_improvements
Minor java improvements
2 parents 1f66961 + 245b627 commit f709352

File tree

8 files changed

+277
-13
lines changed

8 files changed

+277
-13
lines changed

CHANGES.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,9 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
7070
arguments would yield an exception. This issue was found via qt4 and qt5 tools in
7171
scons-contrib https://github.com/SCons/scons-contrib/issues/45
7272

73-
73+
From David H:
74+
- Add JavaScanner to include JAVACLASSPATH as a dependency when using the Java tool.
75+
- Fix incorrect Java classpath generation when a NodeList is used as part of any JAVA*PATH variables.
7476

7577
From Daniel Moody:
7678
- Add cache-debug messages for push failures.
@@ -296,7 +298,7 @@ RELEASE 4.2.0 - Sat, 31 Jul 2021 18:12:46 -0700
296298
- Fix Issue #3906 - `IMPLICIT_COMMAND_DEPENDENCIES` was not properly disabled when
297299
set to any string value (For example ['none','false','no','off'])
298300
Also previously 'All' wouldn't have the desired affect.
299-
301+
300302
From Ivan Kravets:
301303
- Provide a custom argument escape function for `TempFileMunge` using a new
302304
`TEMPFILEARGESCFUNC` variable. Useful if you need to apply extra operations on

RELEASE.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY
6565
output format written to stdout to include more information about the source for each
6666
message of MSVC initialization debugging output. A single space was added before the
6767
message for all debugging output records written to stdout and to files.
68+
- Add JavaScanner to include JAVACLASSPATH as a dependency when using the Java tool.
6869
- The build argument (i.e., x86) is no longer passed to the MSVC 6.0 to 7.1 batch
6970
files. This may improve the effectiveness of the internal msvc cache when using
7071
MSVC detection and when bypassing MSVC detection as the MSVC 6.0 to 7.1 batch files
@@ -121,6 +122,7 @@ FIXES
121122
that link has been modified (issue #3880)
122123
- Fix typo in ninja scons daemon startup which causes ConnectionRefusedError to not retry
123124
to connect to the server during start up.
125+
- Fix incorrect Java classpath generation when a NodeList is used as part of any JAVA*PATH variables.
124126
- The system environment variable names imported for MSVC 7.0 and 6.0 were updated to be
125127
consistent with the variables names defined by their respective installers. This fixes an
126128
error caused when bypassing MSVC detection by specifying the MSVC 7.0 batch file directly.

SCons/Scanner/Java.py

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# MIT License
2+
#
3+
# Copyright The SCons Foundation
4+
#
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:
12+
#
13+
# The above copyright notice and this permission notice shall be included
14+
# in all copies or substantial portions of the Software.
15+
#
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.
23+
24+
import os
25+
26+
import SCons.Node
27+
import SCons.Node.FS
28+
import SCons.Scanner
29+
import SCons.Util
30+
31+
32+
def _subst_libs(env, libs):
33+
"""
34+
Substitute environment variables and split into list.
35+
"""
36+
if SCons.Util.is_String(libs):
37+
libs = env.subst(libs)
38+
if SCons.Util.is_String(libs):
39+
libs = libs.split()
40+
elif SCons.Util.is_Sequence(libs):
41+
_libs = []
42+
for lib in libs:
43+
_libs += _subst_libs(env, lib)
44+
libs = _libs
45+
else:
46+
# libs is an object (Node, for example)
47+
libs = [libs]
48+
return libs
49+
50+
51+
def _collect_classes(list, dirname, files):
52+
for fname in files:
53+
if os.path.splitext(fname)[1] == ".class":
54+
list.append(os.path.join(str(dirname), fname))
55+
56+
57+
def scan(node, env, libpath=()):
58+
"""Scan for files on the JAVACLASSPATH.
59+
60+
The classpath can contain:
61+
- Explicit paths to JAR/Zip files
62+
- Wildcards (*)
63+
- Directories which contain classes in an unnamed package
64+
- Parent directories of the root package for classes in a named package
65+
66+
Class path entries that are neither directories nor archives (.zip or JAR files) nor the asterisk (*) wildcard character are ignored.
67+
"""
68+
classpath = env.get('JAVACLASSPATH', [])
69+
classpath = _subst_libs(env, classpath)
70+
71+
result = []
72+
for path in classpath:
73+
if SCons.Util.is_String(path) and "*" in path:
74+
libs = env.Glob(path)
75+
else:
76+
libs = [path]
77+
78+
for lib in libs:
79+
if os.path.isdir(str(lib)):
80+
# grab the in-memory nodes
81+
env.Dir(lib).walk(_collect_classes, result)
82+
# now the on-disk ones
83+
for root, dirs, files in os.walk(str(lib)):
84+
_collect_classes(result, root, files)
85+
else:
86+
result.append(lib)
87+
88+
return list(filter(lambda x: os.path.splitext(str(x))[1] in [".class", ".zip", ".jar"], result))
89+
90+
91+
def JavaScanner():
92+
return SCons.Scanner.Base(scan, 'JavaScanner',
93+
skeys=['.java'])
94+
95+
# Local Variables:
96+
# tab-width:4
97+
# indent-tabs-mode:nil
98+
# End:
99+
# vim: set expandtab tabstop=4 shiftwidth=4:

SCons/Scanner/JavaTests.py

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
# MIT License
2+
#
3+
# Copyright The SCons Foundation
4+
#
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:
12+
#
13+
# The above copyright notice and this permission notice shall be included
14+
# in all copies or substantial portions of the Software.
15+
#
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.
23+
24+
import unittest
25+
import collections
26+
import os
27+
28+
import TestCmd
29+
30+
import SCons.Scanner.Java
31+
import SCons.Node.FS
32+
import SCons.Warnings
33+
34+
35+
test = TestCmd.TestCmd(workdir = '')
36+
test.subdir('com')
37+
38+
files = [
39+
'bootclasspath.jar',
40+
'classpath.jar',
41+
'Test.class',
42+
'com/Test.class'
43+
]
44+
45+
for fname in files:
46+
test.write(fname, "\n")
47+
48+
49+
class DummyEnvironment(collections.UserDict):
50+
def __init__(self,**kw):
51+
collections.UserDict.__init__(self)
52+
self.data.update(kw)
53+
self.fs = SCons.Node.FS.FS(test.workpath(''))
54+
self['ENV'] = {}
55+
56+
def Dictionary(self, *args):
57+
return self.data
58+
59+
def subst(self, strSubst, target=None, source=None, conv=None):
60+
if strSubst[0] == '$':
61+
return self.data[strSubst[1:]]
62+
return strSubst
63+
64+
def subst_path(self, path, target=None, source=None, conv=None):
65+
if not isinstance(path, list):
66+
path = [path]
67+
return list(map(self.subst, path))
68+
69+
def has_key(self, key):
70+
return key in self.Dictionary()
71+
72+
def get_calculator(self):
73+
return None
74+
75+
def get_factory(self, factory):
76+
return factory or self.fs.File
77+
78+
def Dir(self, filename):
79+
return self.fs.Dir(filename)
80+
81+
def File(self, filename):
82+
return self.fs.File(filename)
83+
84+
def Glob(self, path):
85+
return self.fs.Glob(path)
86+
87+
88+
class DummyNode:
89+
def __init__(self, name):
90+
self.name = name
91+
92+
def rexists(self):
93+
return 1
94+
95+
def __str__(self):
96+
return self.name
97+
98+
99+
global my_normpath
100+
my_normpath = os.path.normpath
101+
102+
if os.path.normcase('foo') == os.path.normcase('FOO'):
103+
my_normpath = os.path.normcase
104+
105+
def deps_match(self, deps, headers):
106+
scanned = sorted(map(my_normpath, list(map(str, deps))))
107+
expect = sorted(map(my_normpath, headers))
108+
self.assertTrue(scanned == expect, "expect %s != scanned %s" % (expect, scanned))
109+
110+
111+
class JavaScannerEmptyClasspath(unittest.TestCase):
112+
def runTest(self):
113+
path = []
114+
env = DummyEnvironment(JAVASUFFIXES=['.java'],
115+
JAVACLASSPATH=path)
116+
s = SCons.Scanner.Java.JavaScanner()
117+
deps = s(DummyNode('dummy'), env)
118+
expected = []
119+
deps_match(self, deps, expected)
120+
121+
122+
class JavaScannerClasspath(unittest.TestCase):
123+
def runTest(self):
124+
env = DummyEnvironment(JAVASUFFIXES=['.java'],
125+
JAVACLASSPATH=[test.workpath('classpath.jar')])
126+
s = SCons.Scanner.Java.JavaScanner()
127+
deps = s(DummyNode('dummy'), env)
128+
expected = ['classpath.jar']
129+
deps_match(self, deps, expected)
130+
131+
132+
class JavaScannerWildcardClasspath(unittest.TestCase):
133+
def runTest(self):
134+
env = DummyEnvironment(JAVASUFFIXES=['.java'],
135+
JAVACLASSPATH=[test.workpath('*')])
136+
s = SCons.Scanner.Java.JavaScanner()
137+
deps = s(DummyNode('dummy'), env)
138+
expected = ['bootclasspath.jar', 'classpath.jar', 'Test.class']
139+
deps_match(self, deps, expected)
140+
141+
142+
class JavaScannerDirClasspath(unittest.TestCase):
143+
def runTest(self):
144+
env = DummyEnvironment(JAVASUFFIXES=['.java'],
145+
JAVACLASSPATH=[test.workpath()])
146+
s = SCons.Scanner.Java.JavaScanner()
147+
deps = s(DummyNode('dummy'), env)
148+
expected = ['Test.class', 'com/Test.class']
149+
deps_match(self, deps, expected)
150+
151+
152+
153+
if __name__ == "__main__":
154+
unittest.main()
155+
156+
# Local Variables:
157+
# tab-width:4
158+
# indent-tabs-mode:nil
159+
# End:
160+
# vim: set expandtab tabstop=4 shiftwidth=4:

SCons/Tool/__init__.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
import SCons.Scanner
4949
import SCons.Scanner.C
5050
import SCons.Scanner.D
51+
import SCons.Scanner.Java
5152
import SCons.Scanner.LaTeX
5253
import SCons.Scanner.Prog
5354
import SCons.Scanner.SWIG
@@ -57,6 +58,7 @@
5758

5859
CScanner = SCons.Scanner.C.CScanner()
5960
DScanner = SCons.Scanner.D.DScanner()
61+
JavaScanner = SCons.Scanner.Java.JavaScanner()
6062
LaTeXScanner = SCons.Scanner.LaTeX.LaTeXScanner()
6163
PDFLaTeXScanner = SCons.Scanner.LaTeX.PDFLaTeXScanner()
6264
ProgramScanner = SCons.Scanner.Prog.ProgramScanner()
@@ -521,7 +523,8 @@ def CreateJavaClassFileBuilder(env):
521523
src_suffix='$JAVASUFFIX',
522524
src_builder=['JavaFile'],
523525
target_factory=fs.Entry,
524-
source_factory=fs.File)
526+
source_factory=fs.File,
527+
target_scanner=JavaScanner)
525528
env['BUILDERS']['JavaClassFile'] = java_class_file
526529
return java_class_file
527530

@@ -535,7 +538,8 @@ def CreateJavaClassDirBuilder(env):
535538
java_class_dir = SCons.Builder.Builder(action=javac_com,
536539
emitter={},
537540
target_factory=fs.Dir,
538-
source_factory=fs.Dir)
541+
source_factory=fs.Dir,
542+
target_scanner=JavaScanner)
539543
env['BUILDERS']['JavaClassDir'] = java_class_dir
540544
return java_class_dir
541545

SCons/Tool/javac.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ def __call__(self, target, source, env, for_signature):
157157
default = [default]
158158
path = path + default
159159
if path:
160+
path = SCons.Util.flatten(path)
160161
return [self.opt, os.pathsep.join(map(str, path))]
161162
else:
162163
return []

SCons/Tool/javac.xml

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -215,15 +215,6 @@ env = Environment(JAVACCOMSTR="Compiling class files $TARGETS from $SOURCES")
215215
<filename>;</filename>
216216
on Windows).
217217
</para>
218-
219-
<para>
220-
Note that this currently just adds the specified
221-
directory via the <option>-classpath</option> option.
222-
&SCons; does not currently search the
223-
&cv-JAVACLASSPATH; directories for dependency
224-
<filename>.class</filename>
225-
files.
226-
</para>
227218
</summary>
228219
</cvar>
229220

SCons/Tool/javacTests.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,11 @@ def test_default_unset(self):
9797
['-foopath', '/foo'],
9898
'/foo',
9999
'')
100+
101+
def test_list_within_list(self):
102+
self.assert_pathopt(['-foopath', os.pathsep.join(['/foo','/bar'])],
103+
['/foo', ['/bar']])
104+
100105

101106
if __name__ == "__main__":
102107
unittest.main()

0 commit comments

Comments
 (0)