Skip to content
Permalink
Browse files

8229396: jdeps ignores multi-release when generate-module-info used o…

…n command line

Reviewed-by: alanb
  • Loading branch information
Mandy Chung
Mandy Chung committed Jan 10, 2020
1 parent 78df4d4 commit b7e74ef62f4797d30a6edc5c7963f309b11b067d
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -58,13 +58,6 @@
* a .class file, a directory, or a JAR file.
*/
public class ClassFileReader implements Closeable {
/**
* Returns a ClassFileReader instance of a given path.
*/
public static ClassFileReader newInstance(Path path) throws IOException {
return newInstance(path, null);
}

/**
* Returns a ClassFileReader instance of a given path.
*/
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -915,19 +915,24 @@ boolean checkOptions() {
option);
return false;
}
if (!options.rootModules.isEmpty()) {
reportError("err.invalid.options", "-m or --module",
option);
return false;
}
return true;
}

@Override
boolean run(JdepsConfiguration config) throws IOException {
// check if any JAR file contains unnamed package
for (String arg : inputArgs) {
try (ClassFileReader reader = ClassFileReader.newInstance(Paths.get(arg))) {
try (ClassFileReader reader = ClassFileReader.newInstance(Paths.get(arg), config.getVersion())) {
Optional<String> classInUnnamedPackage =
reader.entries().stream()
.filter(n -> n.endsWith(".class"))
.filter(cn -> toPackageName(cn).isEmpty())
.findFirst();
.filter(n -> n.endsWith(".class"))
.filter(cn -> toPackageName(cn).isEmpty())
.findFirst();

if (classInUnnamedPackage.isPresent()) {
if (classInUnnamedPackage.get().equals("module-info.class")) {
@@ -942,10 +947,10 @@ boolean run(JdepsConfiguration config) throws IOException {

ModuleInfoBuilder builder
= new ModuleInfoBuilder(config, inputArgs, dir, openModule);
boolean ok = builder.run();

if (!ok && !options.nowarning) {
boolean ok = builder.run(options.ignoreMissingDeps, log, options.nowarning);
if (!ok) {
reportError("err.missing.dependences");
log.println();
builder.visitMissingDeps(new SimpleDepVisitor());
}
return ok;
@@ -1041,7 +1046,7 @@ boolean run(JdepsConfiguration config) throws IOException {
separator);
boolean ok = analyzer.run(options.depth(), options.ignoreMissingDeps);
if (!ok) {
reportError("err.cant.list.module.deps");
reportError("err.missing.dependences");
log.println();
analyzer.visitMissingDeps(new SimpleDepVisitor());
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -98,7 +98,7 @@ public ModuleInfoBuilder(JdepsConfiguration configuration,
}
}

public boolean run() throws IOException {
public boolean run(boolean ignoreMissingDeps, PrintWriter log, boolean quiet) throws IOException {
try {
// pass 1: find API dependencies
Map<Archive, Set<Archive>> requiresTransitive = computeRequiresTransitive();
@@ -108,52 +108,65 @@ public boolean run() throws IOException {

analyzer.run(automaticModules(), dependencyFinder.locationToArchive());

boolean missingDeps = false;
for (Module m : automaticModules()) {
Set<Archive> apiDeps = requiresTransitive.containsKey(m)
? requiresTransitive.get(m)
: Collections.emptySet();

Path file = outputdir.resolve(m.name()).resolve("module-info.java");
// if this is a multi-release JAR, write to versions/$VERSION/module-info.java
Runtime.Version version = configuration.getVersion();
Path dir = version != null
? outputdir.resolve(m.name())
.resolve("versions")
.resolve(String.valueOf(version.feature()))
: outputdir.resolve(m.name());
Path file = dir.resolve("module-info.java");

// computes requires and requires transitive
Module normalModule = toNormalModule(m, apiDeps);
Module normalModule = toNormalModule(m, apiDeps, ignoreMissingDeps);
if (normalModule != null) {
automaticToNormalModule.put(m, normalModule);

// generate module-info.java
System.out.format("writing to %s%n", file);
if (!quiet) {
if (ignoreMissingDeps && analyzer.requires(m).anyMatch(Analyzer::notFound)) {
log.format("Warning: --ignore-missing-deps specified. Missing dependencies from %s are ignored%n",
m.name());
}
log.format("writing to %s%n", file);
}
writeModuleInfo(file, normalModule.descriptor());
} else {
// find missing dependences
System.out.format("Missing dependence: %s not generated%n", file);
missingDeps = true;
return false;
}
}

return !missingDeps;
} finally {
dependencyFinder.shutdown();
}
return true;
}

private Module toNormalModule(Module module, Set<Archive> requiresTransitive)
private Module toNormalModule(Module module, Set<Archive> requiresTransitive, boolean ignoreMissingDeps)
throws IOException
{
// done analysis
module.close();

if (analyzer.requires(module).anyMatch(Analyzer::notFound)) {
if (!ignoreMissingDeps && analyzer.requires(module).anyMatch(Analyzer::notFound)) {
// missing dependencies
return null;
}

Map<String, Boolean> requires = new HashMap<>();
requiresTransitive.stream()
.filter(a -> !(ignoreMissingDeps && Analyzer.notFound(a)))
.map(Archive::getModule)
.forEach(m -> requires.put(m.name(), Boolean.TRUE));

analyzer.requires(module)
.filter(a -> !(ignoreMissingDeps && Analyzer.notFound(a)))
.map(Archive::getModule)
.forEach(d -> requires.putIfAbsent(d.name(), Boolean.FALSE));

@@ -1,5 +1,5 @@
#
# Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@@ -229,7 +229,6 @@ main.opt.multi-release=\
err.command.set={0} and {1} options are specified.
err.unknown.option=unknown option: {0}
err.missing.arg=no value given for {0}
err.missing.dependences=missing dependencies
err.invalid.arg.for.option=invalid argument for option: {0}
err.option.after.class=option must be specified before classes: {0}
err.genmoduleinfo.not.jarfile={0} is a modular JAR file that cannot be specified with the --generate-module-info option
@@ -246,8 +245,8 @@ err.multirelease.option.exists={0} is not a multi-release jar file but --multi-r
err.multirelease.option.notfound={0} is a multi-release jar file but --multi-release option is not set
err.multirelease.version.associated=class {0} already associated with version {1}, trying to add version {2}
err.multirelease.jar.malformed=malformed multi-release jar, {0}, bad entry: {1}
err.cant.list.module.deps=\
Missing dependencies from the module path and classpath.\n\
err.missing.dependences=\
Missing dependencies: classes not found from the module path and classpath.\n\
To suppress this error, use --ignore-missing-deps to continue.

warn.invalid.arg=Path does not exist: {0}
@@ -0,0 +1,130 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

/*
* @test
* @summary Tests jdeps option on a MR jar with missing dependences
* @library ../lib
* @build CompilerUtils JdepsUtil
* @modules jdk.jdeps/com.sun.tools.jdeps
* @run testng MissingDepsTest
*/

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.spi.ToolProvider;
import java.util.stream.Stream;

import org.testng.annotations.BeforeTest;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

import static org.testng.Assert.assertTrue;
import static org.testng.Assert.assertEquals;


public class MissingDepsTest {
private static final String TEST_SRC = System.getProperty("test.src");
private static final Path SRC_DIR = Paths.get(TEST_SRC, "src");

private static final Path MODS_DIR = Paths.get("mods");
private static final Path CLASSES_DIR = Paths.get("classes");

private static final ToolProvider JAR_TOOL = ToolProvider.findFirst("jar").orElseThrow();
private static final String VERSION = "13";

private static final Set<String> modules = Set.of("m1", "m2");

/**
* Compiles classes used by the test
*/
@BeforeTest
public void compileAll() throws Exception {
CompilerUtils.cleanDir(MODS_DIR);
modules.forEach(mn ->
assertTrue(CompilerUtils.compileModule(SRC_DIR, MODS_DIR, mn)));

// compile a versioned class file
Path versionedFile = Paths.get(TEST_SRC, "p/internal/X.java");
assertTrue(CompilerUtils.compile(versionedFile, CLASSES_DIR, "-cp", MODS_DIR.resolve("m2").toString()));

// create a modular multi-release m1.jar
JAR_TOOL.run(System.out, System.err, "cf", "m1.jar",
"-C", MODS_DIR.resolve("m1").toString(), ".");
JAR_TOOL.run(System.out, System.err, "uf", "m1.jar",
"--release", VERSION, "-C", CLASSES_DIR.toString(), "p/internal/X.class");
// create a non-modular multi-release mr.jar
JAR_TOOL.run(System.out, System.err, "cf", "mr.jar",
"-C", MODS_DIR.resolve("m1").toString(), "p/Foo.class",
"--release", VERSION, "-C", CLASSES_DIR.toString(), "p/internal/X.class");
}

@Test
public void genModuleInfo() {
JdepsTest test = new JdepsTest();
test.options(List.of("--generate-module-info", ".", "--multi-release", VERSION, "mr.jar"));
test.checkMissingDeps();
Path file = Paths.get("mr", "versions", VERSION, "module-info.java");
test.ignoreMissingDeps(file.toString());
assertTrue(Files.exists(file));
}

@Test
public void listModuleDeps() {
JdepsTest test = new JdepsTest();
test.options(List.of("--list-deps", "--multi-release", VERSION, "mr.jar"));
test.checkMissingDeps();
test.ignoreMissingDeps("java.management");
}

class JdepsTest {
// set DEBUG to true to show the jdeps output
static final boolean DEBUG = false;
List<String> options;
JdepsTest options(List<String> options) {
this.options = options;
return this;
}

private void checkMissingDeps() {
JdepsRunner jdepsRunner = new JdepsRunner(options.toArray(new String[0]));
int rc = jdepsRunner.run(DEBUG);
assertTrue(rc != 0);
String regex = "\\s+13/p.internal.X\\s+->\\s+q.T\\s+not found";
assertTrue(Arrays.stream(jdepsRunner.output()).anyMatch(l -> l.matches(regex)));
}

public void ignoreMissingDeps(String expected) {
JdepsRunner jdepsRunner = new JdepsRunner(Stream.concat(Stream.of("--ignore-missing-deps"), options.stream())
.toArray(String[]::new));
int rc = jdepsRunner.run(DEBUG);
assertTrue(rc == 0);
System.out.println("Expected: " + expected);
assertTrue(Arrays.stream(jdepsRunner.output()).anyMatch(l -> l.contains(expected)));
}
}
}
@@ -0,0 +1,32 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

package p.internal;

import java.lang.management.*;

class X implements q.T {
public void upTime() {
ManagementFactory.getRuntimeMXBean().getUptime();
}
}
@@ -0,0 +1,27 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

module m1 {
requires static m2;
exports p;
}

0 comments on commit b7e74ef

Please sign in to comment.