Skip to content

Commit e36f493

Browse files
committed
[GR-54225] NPE if package tag in graalpy-maven-plugin config is empty.
PullRequest: graalpython/3342
2 parents 4597883 + 4558921 commit e36f493

File tree

5 files changed

+229
-25
lines changed

5 files changed

+229
-25
lines changed

graalpython/com.oracle.graal.python.test/src/tests/standalone/check_home_pom.xml

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -73,14 +73,7 @@ SOFTWARE.
7373
<executions>
7474
<execution>
7575
<configuration>
76-
<pythonHome>
77-
<includes>
78-
<include>.*__init__\.py</include>
79-
</includes>
80-
<excludes>
81-
<exclude>.*html/__init__\.py</exclude>
82-
</excludes>
83-
</pythonHome>
76+
8477
</configuration>
8578
<goals>
8679
<goal>process-graalpy-resources</goal>
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
Copyright (c) 2023, 2023, Oracle and/or its affiliates. All rights reserved.
4+
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5+
6+
The Universal Permissive License (UPL), Version 1.0
7+
8+
Subject to the condition set forth below, permission is hereby granted to any
9+
person obtaining a copy of this software, associated documentation and/or
10+
data (collectively the "Software"), free of charge and under any and all
11+
copyright rights in the Software, and any and all patent rights owned or
12+
freely licensable by each licensor hereunder covering either (i) the
13+
unmodified Software as contributed to or provided by such licensor, or (ii)
14+
the Larger Works (as defined below), to deal in both
15+
16+
(a) the Software, and
17+
18+
(b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
19+
one is included with the Software each a "Larger Work" to which the Software
20+
is contributed by such licensors),
21+
22+
without restriction, including without limitation the rights to copy, create
23+
derivative works of, display, perform, and distribute the Software and make,
24+
use, sell, offer for sale, import, export, have made, and have sold the
25+
Software and the Larger Work(s), and to sublicense the foregoing rights on
26+
either these or other terms.
27+
28+
This license is subject to the following condition:
29+
30+
The above copyright notice and either this complete permission notice or at a
31+
minimum a reference to the UPL must be included in all copies or substantial
32+
portions of the Software.
33+
34+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
37+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
38+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
39+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
40+
SOFTWARE.
41+
-->
42+
<project xmlns="http://maven.apache.org/POM/4.0.0"
43+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
44+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
45+
<modelVersion>4.0.0</modelVersion>
46+
47+
<groupId>org.apache.maven.plugin.my.unit</groupId>
48+
<artifactId>project-check-packages</artifactId>
49+
<version>1.0-SNAPSHOT</version>
50+
<packaging>jar</packaging>
51+
<name>Test MyMojo</name>
52+
53+
<dependencies>
54+
<dependency>
55+
<groupId>org.graalvm.polyglot</groupId>
56+
<artifactId>python-community</artifactId>
57+
<version>${env.GRAALPY_VERSION}</version>
58+
<type>pom</type>
59+
</dependency>
60+
<dependency>
61+
<groupId>org.graalvm.python</groupId>
62+
<artifactId>python-launcher</artifactId>
63+
<version>${env.GRAALPY_VERSION}</version>
64+
</dependency>
65+
</dependencies>
66+
67+
<build>
68+
<plugins>
69+
<plugin>
70+
<groupId>org.graalvm.python</groupId>
71+
<artifactId>graalpy-maven-plugin</artifactId>
72+
<version>${env.GRAALPY_VERSION}</version>
73+
<executions>
74+
<execution>
75+
<configuration>
76+
<packages>
77+
78+
</packages>
79+
</configuration>
80+
<goals>
81+
<goal>process-graalpy-resources</goal>
82+
</goals>
83+
</execution>
84+
</executions>
85+
</plugin>
86+
</plugins>
87+
</build>
88+
</project>

graalpython/com.oracle.graal.python.test/src/tests/standalone/test_standalone.py

Lines changed: 105 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,12 @@ def get_gp():
7575

7676
return graalpy
7777

78+
def replace_in_file(file, str, replace_str):
79+
with open(file, "r") as f:
80+
contents = f.read()
81+
with open(file, "w") as f:
82+
f.write(contents.replace(str, replace_str))
83+
7884
class PolyglotAppTest(unittest.TestCase):
7985

8086
def setUpClass(self):
@@ -259,11 +265,7 @@ def test_gen_launcher_and_venv(self):
259265
util.check_ouput("termcolor", out, False)
260266

261267
# remove ujson pkg from plugin config and check if unistalled
262-
with open(os.path.join(target_dir, "pom.xml"), "r") as f:
263-
contents = f.read()
264-
265-
with open(os.path.join(target_dir, "pom.xml"), "w") as f:
266-
f.write(contents.replace("<package>ujson</package>", ""))
268+
replace_in_file(os.path.join(target_dir, "pom.xml"), "<package>ujson</package>", "")
267269

268270
cmd = mvnw_cmd + ["process-resources"]
269271
out, return_code = util.run_cmd(cmd, self.env, cwd=target_dir)
@@ -272,31 +274,124 @@ def test_gen_launcher_and_venv(self):
272274
util.check_ouput("Uninstalling ujson", out)
273275
util.check_ouput("termcolor", out, False)
274276

277+
def check_tagfile(self, target_dir, expected):
278+
with open(os.path.join(target_dir, "target", "classes", VFS_PREFIX, "home", "tagfile")) as f:
279+
lines = f.readlines()
280+
assert lines == expected, "expected tagfile " + str(expected) + ", but got " + str(lines)
281+
275282
@unittest.skipUnless(is_enabled, "ENABLE_STANDALONE_UNITTESTS is not true")
276283
def test_check_home(self):
277-
with tempfile.TemporaryDirectory() as tmpdir:
284+
with tempfile.TemporaryDirectory() as tmpdir:
278285
target_name = "check_home_test"
279286
target_dir = os.path.join(str(tmpdir), target_name)
280287
pom_template = os.path.join(os.path.dirname(__file__), "check_home_pom.xml")
281288
self.generate_app(tmpdir, target_dir, target_name, pom_template)
282289

283290
mvnw_cmd = util.get_mvn_wrapper(target_dir, self.env)
284291

285-
cmd = mvnw_cmd + ["process-resources"]
286-
out, return_code = util.run_cmd(cmd, self.env, cwd=target_dir)
292+
# 1. process-resources with no pythonHome config
293+
process_resources_cmd = mvnw_cmd + ["process-resources"]
294+
out, return_code = util.run_cmd(process_resources_cmd, self.env, cwd=target_dir)
295+
util.check_ouput("BUILD SUCCESS", out)
296+
util.check_ouput("Copying std lib to ", out)
297+
298+
home_dir = os.path.join(target_dir, "target", "classes", VFS_PREFIX, "home")
299+
assert os.path.exists(home_dir)
300+
assert os.path.exists(os.path.join(home_dir, "lib-graalpython"))
301+
assert os.path.isdir(os.path.join(home_dir, "lib-graalpython"))
302+
assert os.path.exists(os.path.join(home_dir, "lib-python"))
303+
assert os.path.isdir(os.path.join(home_dir, "lib-python"))
304+
assert os.path.exists(os.path.join(home_dir, "tagfile"))
305+
assert os.path.isfile(os.path.join(home_dir, "tagfile"))
306+
self.check_tagfile(target_dir, [f'{self.graalvmVersion}\n', 'include:.*\n'])
307+
308+
# 2. process-resources with empty pythonHome includes and excludes
309+
shutil.copyfile(pom_template, os.path.join(target_dir, "pom.xml"))
310+
util.patch_pom_repositories(os.path.join(target_dir, "pom.xml"))
311+
replace_in_file(os.path.join(target_dir, "pom.xml"), "</configuration>", "<pythonHome></pythonHome></configuration>")
312+
out, return_code = util.run_cmd(process_resources_cmd, self.env, cwd=target_dir)
313+
util.check_ouput("BUILD SUCCESS", out)
314+
util.check_ouput("Copying std lib to ", out, False)
315+
self.check_tagfile(target_dir, [f'{self.graalvmVersion}\n', 'include:.*\n'])
287316

288-
# check fileslist.txt
317+
shutil.copyfile(pom_template, os.path.join(target_dir, "pom.xml"))
318+
util.patch_pom_repositories(os.path.join(target_dir, "pom.xml"))
319+
replace_in_file(os.path.join(target_dir, "pom.xml"), "</configuration>", "<pythonHome><includes></includes><excludes></excludes></pythonHome></configuration>")
320+
out, return_code = util.run_cmd(process_resources_cmd, self.env, cwd=target_dir)
321+
util.check_ouput("BUILD SUCCESS", out)
322+
util.check_ouput("Copying std lib to ", out, False)
323+
self.check_tagfile(target_dir, [f'{self.graalvmVersion}\n', 'include:.*\n'])
324+
325+
shutil.copyfile(pom_template, os.path.join(target_dir, "pom.xml"))
326+
util.patch_pom_repositories(os.path.join(target_dir, "pom.xml"))
327+
home_tag = """
328+
<pythonHome>
329+
<includes>
330+
<include></include>
331+
<include> </include>
332+
</includes>
333+
<excludes>
334+
<exclude></exclude>
335+
<exclude> </exclude>
336+
</excludes>
337+
</pythonHome>
338+
"""
339+
replace_in_file(os.path.join(target_dir, "pom.xml"), "</configuration>", home_tag + "</configuration>")
340+
out, return_code = util.run_cmd(process_resources_cmd, self.env, cwd=target_dir)
341+
util.check_ouput("BUILD SUCCESS", out)
342+
util.check_ouput("Copying std lib to ", out, False)
343+
self.check_tagfile(target_dir, [f'{self.graalvmVersion}\n', 'include:.*\n'])
344+
345+
# 3. process-resources with pythonHome includes and excludes
346+
home_tag = """
347+
<pythonHome>
348+
<includes>
349+
<include>.*__init__\.py</include>
350+
</includes>
351+
<excludes>
352+
<exclude>.*html/__init__\.py</exclude>
353+
</excludes>
354+
</pythonHome>
355+
"""
356+
replace_in_file(os.path.join(target_dir, "pom.xml"), "</configuration>", home_tag + "</configuration>")
357+
out, return_code = util.run_cmd(process_resources_cmd, self.env, cwd=target_dir)
358+
util.check_ouput("BUILD SUCCESS", out)
359+
util.check_ouput("Deleting GraalPy home due to changed includes or excludes", out)
360+
util.check_ouput("Copying std lib to ", out)
361+
self.check_tagfile(target_dir, [f'{self.graalvmVersion}\n', 'include:.*__init__\\.py\n', 'exclude:.*html/__init__\\.py\n'])
362+
363+
# 4. check fileslist.txt
289364
fl_path = os.path.join(target_dir, "target", "classes", VFS_PREFIX, "fileslist.txt")
290365
with open(fl_path) as f:
291366
for line in f:
292367
line = f.readline()
293368
# string \n
294369
line = line[:len(line)-1]
295-
if line.endswith("/") or line == "/" + VFS_PREFIX + "/home/tagfile" or line == "/" + VFS_PREFIX + "/proj/hello.py":
370+
if not line.startswith("/" + VFS_PREFIX + "/home/") or line.endswith("/"):
296371
continue
297372
assert line.endswith("/__init__.py")
298373
assert not line.endswith("html/__init__.py")
299374

375+
@unittest.skipUnless(is_enabled, "ENABLE_STANDALONE_UNITTESTS is not true")
376+
def test_empty_packages(self):
377+
with tempfile.TemporaryDirectory() as tmpdir:
378+
target_name = "empty_packages_test"
379+
target_dir = os.path.join(str(tmpdir), target_name)
380+
pom_template = os.path.join(os.path.dirname(__file__), "check_packages_pom.xml")
381+
self.generate_app(tmpdir, target_dir, target_name, pom_template)
382+
383+
mvnw_cmd = util.get_mvn_wrapper(target_dir, self.env)
384+
385+
cmd = mvnw_cmd + ["process-resources"]
386+
out, return_code = util.run_cmd(cmd, self.env, cwd=target_dir)
387+
util.check_ouput("BUILD SUCCESS", out)
388+
389+
replace_in_file(os.path.join(target_dir, "pom.xml"), "</packages>", "<package></package><package> </package></packages>")
390+
391+
cmd = mvnw_cmd + ["process-resources"]
392+
out, return_code = util.run_cmd(cmd, self.env, cwd=target_dir)
393+
util.check_ouput("BUILD SUCCESS", out)
394+
300395
@unittest.skipUnless(is_enabled, "ENABLE_STANDALONE_UNITTESTS is not true")
301396
def test_native_executable_one_file():
302397
graalpy = util.get_gp()

graalpython/graalpy-archetype-polyglot-app/src/main/resources/archetype-resources/pom.xml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,19 +46,20 @@
4646
<executions>
4747
<execution>
4848
<configuration>
49-
<!-- specify python packages as if used with pip -->
49+
<!-- specify python packages and their versions as if used with pip -->
5050
<packages>
5151
<package>termcolor==2.2</package>
5252
</packages>
5353
<pythonHome>
54-
<!-- java-like regular expression what file paths should be included into venv/home;
55-
default is all -->
5654
<includes>
55+
<!-- java-like regular expression what file paths should be included into venv/home;
56+
if empty than default is all -->
5757
<include>.*</include>
58-
</includes>
58+
</includes>
59+
<excludes>
5960
<!-- java-like regular expression what file paths should be excluded from venv/home;
6061
default is none -->
61-
<excludes>
62+
<!-- <exclude></exclude> -->
6263
</excludes>
6364
</pythonHome>
6465
</configuration>

graalpython/graalpy-maven-plugin/src/main/java/org/graalvm/python/maven/plugin/ManageResourcesMojo.java

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,16 @@ public void execute() throws MojoExecutionException {
142142
manageNativeImageConfig();
143143
}
144144

145+
private void trim(List<String> l) {
146+
Iterator<String> it = l.iterator();
147+
while(it.hasNext()) {
148+
String p = it.next();
149+
if(p == null || p.trim().isEmpty()) {
150+
it.remove();
151+
}
152+
}
153+
}
154+
145155
private void manageNativeImageConfig() throws MojoExecutionException {
146156
Path metaInf = getMetaInfDirectory(project);
147157
Path resourceConfig = metaInf.resolve("resource-config.json");
@@ -168,9 +178,23 @@ public static class PythonHome {
168178
private void manageHome() throws MojoExecutionException {
169179
var homeDirectory = getHomeDirectory(project);
170180
if (pythonHome == null) {
171-
delete(homeDirectory);
172-
return;
181+
pythonHome = new PythonHome();
182+
pythonHome.includes = Arrays.asList(".*");
183+
pythonHome.excludes = Collections.emptyList();
184+
} else {
185+
if (pythonHome.includes != null) {
186+
trim(pythonHome.includes);
187+
}
188+
if (pythonHome.includes == null || pythonHome.includes.isEmpty()) {
189+
pythonHome.includes = Arrays.asList(".*");
190+
}
191+
if (pythonHome.excludes == null) {
192+
pythonHome.excludes = Collections.emptyList();
193+
} else {
194+
trim(pythonHome.excludes);
195+
}
173196
}
197+
174198
var tag = homeDirectory.resolve("tagfile");
175199
var graalPyVersion = getGraalPyVersion(project);
176200

@@ -264,6 +288,9 @@ private void manageVenv() throws MojoExecutionException {
264288

265289
var venvDirectory = getVenvDirectory(project);
266290

291+
if(packages != null) {
292+
trim(packages);
293+
}
267294
if (packages == null || packages.isEmpty()) {
268295
getLog().info(String.format("No venv packages declared, deleting %s", venvDirectory));
269296
delete(venvDirectory);

0 commit comments

Comments
 (0)