6605077: (jrepo) add "dependencies" subcommand
authorbristor
Fri Jul 18 12:47:04 2008 -0700 (16 months ago)
changeset 42092ba7c4f13c6
parent 4192e35584326dc
child 421ffa6ecfd4108
child 4589fc03df46a6e
6605077: (jrepo) add "dependencies" subcommand
Summary: "jrepo dependencies" prints dependencies of one or more modules in a repository
Reviewed-by: mchung
src/share/classes/sun/module/repository/URLModuleInfo.java
src/share/classes/sun/module/tools/ImportTraverser.java
src/share/classes/sun/module/tools/JRepo.java
test/java/module/modinit/JRepoDepGold.txt
test/java/module/modinit/JRepoDependenciesTest.java
test/java/module/modinit/RunMTest.java
test/java/module/tools/jrepo/JRepoTest.java
--- a/src/share/classes/sun/module/repository/URLModuleInfo.java Fri Jul 11 08:29:29 2008 -0700
+++ b/src/share/classes/sun/module/repository/URLModuleInfo.java Fri Jul 18 12:47:04 2008 -0700
@@ -60,7 +60,7 @@ public class URLModuleInfo {
URLModuleInfo(String name, Version version, String platform, String arch, String path) {
if ((platform == null ^ arch == null)) {
throw new IllegalArgumentException(
- "module platform and name must be either both provided, or neither provided");
+ "module platform and arch must be either both provided, or neither provided");
}
if (name == null) {
--- a/src/share/classes/sun/module/tools/JRepo.java Fri Jul 11 08:29:29 2008 -0700
+++ b/src/share/classes/sun/module/tools/JRepo.java Fri Jul 18 12:47:04 2008 -0700
@@ -30,6 +30,7 @@ import java.io.FileNotFoundException;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
+import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;
@@ -38,16 +39,20 @@ import java.net.URL;
import java.net.URL;
import java.net.URISyntaxException;
import java.module.*;
+import java.module.annotation.ImportPolicyClass;
import java.security.AccessController;
+import java.security.PrivilegedAction;
import java.text.DateFormat;
import java.util.Arrays;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import sun.module.JamUtils;
+import java.util.Set;
import sun.module.repository.RepositoryConfig;
import sun.security.action.GetPropertyAction;
import sun.tools.jar.CommandLine;
@@ -79,11 +84,17 @@ public class JRepo {
/** Usage message created from each command's usage. */
private static String usage = null;
- /** Format for module name & version. */
+ /** Format for ModuleArchiveInfo name & version. */
private static final String MAIFormat = "%-20s %-20s";
- /** Format for additional/verbose module information. */
+ /** Format for additional/verbose ModuleArchiveInfo details. */
private static final String MAIFormatVerbose = " %-9s %-7s %-17s %s";
+
+ /** Format for ModuleDefinition name & version. */
+ private static final String MDFormat = "%s-%s";
+
+ /** Format for additional/verbose ModuleDefinition details. */
+ private static final String MDFormatVerbose = " %s";
/** String containing column headings for name & version. */
private static final String maiHeading;
@@ -99,6 +110,12 @@ public class JRepo {
/** Location of repository; if not given uses system repository. */
private static final RepositoryFlag repositoryFlag = new RepositoryFlag();
+
+ /** Indicates dependencies command should display info on core modules. */
+ private static final Flag javaseFlag = new Flag('j');
+
+ /** Provides way to specify platform binding fo dependencies command. */
+ private static final BindingFlag bindingFlag = new BindingFlag();
/** Contains the flags that are common to all commands. */
private static final HashMap<Character, Flag> commonFlags = new HashMap<Character, Flag>();
@@ -117,6 +134,10 @@ public class JRepo {
pw.printf(MAIFormat, "Name", "Version"); // XXX i18n
pw.printf(MAIFormatVerbose, "Platform", "Arch", "Modified", "Filename"); // XXX i18n
maiHeadingVerbose = sw.toString();
+ }
+
+ public JRepo(OutputStream out, OutputStream err, BufferedReader reader) {
+ this(new PrintStream(out), new PrintStream(err), reader);
}
public JRepo(PrintStream out, PrintStream err, BufferedReader reader) {
@@ -126,6 +147,7 @@ public class JRepo {
new ListCommand().register(commands);
new InstallCommand().register(commands);
new UninstallCommand().register(commands);
+ new DependenciesCommand().register(commands);
}
}
reset();
@@ -189,7 +211,8 @@ public class JRepo {
f.getCanonicalFile(), null,
RepositoryConfig.getSystemRepository());
} else {
- throw new IOException("Cannot access repository at " + repositoryLocation);
+ throw new IOException("Cannot access repository at " // XXX i18n
+ + repositoryLocation);
}
}
}
@@ -262,7 +285,7 @@ public class JRepo {
/** Returns a user-grokkable description of the repository. */
- private String getRepositoryText(Repository repo) {
+ private static String getRepositoryText(Repository repo) {
String rc;
URI u = repo.getSourceLocation();
if (u == null) {
@@ -277,7 +300,7 @@ public class JRepo {
return rc;
}
- private String getMAIText(ModuleArchiveInfo mai) {
+ private static String getMAIText(ModuleArchiveInfo mai) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
pw.printf(MAIFormat, mai.getName(), mai.getVersion());
@@ -297,6 +320,23 @@ public class JRepo {
return sw.toString();
}
+ private static String getMText(Module m) {
+ return getMDText(m.getModuleDefinition());
+ }
+
+ private static String getMDText(ModuleDefinition md) {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ pw.printf(MDFormat, md.getName(), md.getVersion());
+ if (verboseFlag.isEnabled()) {
+ pw.printf(MDFormatVerbose,
+ md.getRepository() == Repository.getBootstrapRepository()
+ ? "bootstrap"
+ : md.getRepository().getSourceLocation().toString());
+ }
+ return sw.toString();
+ }
+
void usageError() {
usageError(msg);
}
@@ -304,7 +344,7 @@ public class JRepo {
static void usageError(Messenger msg) {
if (usage == null) {
StringBuilder ub = new StringBuilder(
- "Usage: jrepo <command>\nwhere <command> includes:");
+ "Usage: jrepo <command>\nwhere <command> includes:"); // XXX i18n
for (Command c : commands.values()) {
String u = c.usage();
if (u != null) {
@@ -319,7 +359,7 @@ public class JRepo {
/**
* Represents a flag given on the command line. Flags always are 2
* characters long, and start with a '-'. They can have additional
- * associated arguments (cf RepositoryFlag).
+ * associated arguments (cf {@link #RepositoryFlag}).
*/
private static class Flag {
private final char name;
@@ -341,8 +381,11 @@ public class JRepo {
return name;
}
- /** @return the number of arguments consumed by this Flag. */
- int set(String[] args, int pos) {
+ /**
+ * @return the number of arguments consumed by this Flag
+ * @throws IllegalArgumentException if invalid args are given
+ */
+ int set(String[] args, int pos) throws IllegalArgumentException {
enabled = true;
return 1;
}
@@ -367,7 +410,8 @@ public class JRepo {
super('r');
}
- int set(String[] args, int pos) {
+ @Override
+ int set(String[] args, int pos) throws IllegalArgumentException {
int rc = super.set(args, pos);
location = args[pos + 1];
return rc + 1;
@@ -377,9 +421,47 @@ public class JRepo {
return location;
}
+ @Override
void reset() {
super.reset();
location = null;
+ }
+ }
+
+ private static class BindingFlag extends Flag {
+ String platform;
+ String arch;
+
+ BindingFlag() {
+ super('b');
+ }
+
+ @Override
+ int set(String[] args, int pos) throws IllegalArgumentException {
+ int rc = super.set(args, pos);
+ String[] binding = args[pos + 1].split("-");
+ if (binding.length != 2) {
+ throw new IllegalArgumentException(
+ "Must 2 and only 2 elements for platform-arch");
+ }
+ platform = binding[0];
+ arch = binding[1];
+ return rc + 1;
+ }
+
+ String getPlatform() {
+ return platform;
+ }
+
+ String getArch() {
+ return arch;
+ }
+
+ @Override
+ void reset() {
+ super.reset();
+ platform = null;
+ arch = null;
}
}
@@ -430,7 +512,8 @@ public class JRepo {
i += numConsumed; // Increases at each iteration.
rc += numConsumed; // Increases only when the arg is a flag.
} else {
- throw new IllegalArgumentException("unrecognized flag: " + args[i]);
+ throw new IllegalArgumentException("unrecognized flag: " // XXX i18n
+ + args[i]);
}
} else {
i++;
@@ -447,6 +530,7 @@ public class JRepo {
*/
abstract String usage();
+ @Override
public String toString() { return name; }
/**
@@ -459,9 +543,9 @@ public class JRepo {
abstract class RepositoryVisitor {
abstract void doit(Repository repo, Messenger msg);
- void doBefore(Messenger msg) { }
-
- void doAfter(Messenger msg) { }
+ void preVisit(Messenger msg) { }
+
+ void postVisit(Messenger msg) { }
final void run(Repository repo, Messenger msg) {
visit(repo, msg);
@@ -472,10 +556,179 @@ public class JRepo {
if (parent != null) {
visit(parent, msg);
}
- doBefore(msg);
+ preVisit(msg);
doit(repo, msg);
- doAfter(msg);
- }
+ postVisit(msg);
+ }
+ }
+ }
+
+
+ /** Lists dependencies of a module. */
+ private class DependenciesCommand extends Command {
+ @SuppressWarnings("unchecked")
+ private final Map<Character, Flag> myFlags = (Map<Character, Flag>) commonFlags.clone();
+
+ private String name;
+ private Version version;
+
+ // For printing module name, version.
+ private String moduleString;
+
+ DependenciesCommand() {
+ super("dependencies");
+ javaseFlag.register(myFlags);
+ bindingFlag.register(myFlags);
+ }
+
+ @Override
+ void reset() {
+ javaseFlag.reset();
+ bindingFlag.reset();
+ }
+
+ @Override
+ int parseFlags(String[] args, Map<Character, Flag> flags) {
+ return super.parseFlags(args, myFlags);
+ }
+
+ boolean parseArgs(String[] args, Messenger msg) {
+ if (args.length > 0) {
+ name = args[0];
+ }
+ if (args.length > 1) {
+ try {
+ version = Version.valueOf(args[1]);
+ } catch (IllegalArgumentException ex) {
+ return false;
+ }
+ }
+ if (args.length > 2) {
+ return false;
+ }
+
+ if (DEBUG) debug("name: " + name + " ver: " + version
+ + " plat: " + bindingFlag.getPlatform()
+ + " arch: " + bindingFlag.getArch());
+ return true;
+ }
+
+ boolean run(Repository repo, Messenger msg) {
+ boolean rc = true;
+ if (name != null) {
+ VersionConstraint vc = (version == null
+ ? VersionConstraint.DEFAULT
+ : version.toVersionConstraint());
+ printHeader(name, vc);
+ rc = depend(repo, name, vc,
+ bindingFlag.getPlatform(), bindingFlag.getArch());
+ printTrailer(null);
+ } else {
+ for (ModuleArchiveInfo mai : repo.list()) {
+ reset();
+ String maiName = mai.getName();
+ VersionConstraint vc = mai.getVersion().toVersionConstraint();
+ printHeader(maiName, vc);
+ rc &= depend(repo, maiName, vc,
+ mai.getPlatform(), mai.getArch());
+ printTrailer("\n");
+ }
+ }
+ return rc;
+ }
+
+ boolean depend(Repository repo, String name, VersionConstraint constraint,
+ String platform, String arch) {
+ ImportTraverser traverser = new ImportTraverser();
+ ImportTraverser.Visitor visitor = new ImportVisitor(traverser, msg);
+ try {
+ traverser.traverse(visitor, repo, name, constraint, platform, arch);
+ if (traverser.traversedAny()) {
+ return true;
+ }
+ if (verboseFlag.isEnabled()) {
+ msg.error("Cannot find module " + name + " in " // XXX i18n
+ + getRepositoryText(repo));
+ }
+ return false;
+ } catch (ModuleInitializationException ex) {
+ msg.error("Cannot instantiate module for " + name // XXX i18n
+ + ": " + ex);
+ return false;
+ }
+ }
+
+
+ void printHeader(String name, VersionConstraint vc) {
+ if (verboseFlag.isEnabled()) {
+ msg.println("Dependencies for " // XXXi18n
+ + name + "-" + vc + ":");
+ }
+ }
+
+ void printTrailer(String s) {
+ if (verboseFlag.isEnabled()) {
+ msg.print(s);
+ }
+ }
+
+ String usage() {
+ return "dependencies [-v] [-r repositoryLocation] [-b platform-arch]"// XXX i18n
+ + " [moduleName [moduleVersion] ]\n"
+ + " Lists all modules on which identified modules depend.\n"
+ + " If no moduleName is given, lists dependencies of all"
+ + " module archives in the repository\n"
+ + " which are instantiable on the current platform.";
+ }
+ }
+
+
+ private static class ImportVisitor extends ImportTraverser.Visitor {
+ private final Messenger msg;
+
+ private static final String INDENT = " ";
+ private static final int INDENT_LENGTH = INDENT.length();
+
+ private String indent = "";
+
+ ImportVisitor(ImportTraverser traverser, Messenger msg) {
+ super(traverser);
+ this.msg = msg;
+ }
+
+ @Override
+ protected void init(Module m) {
+ printModule(m);
+ }
+
+ @Override
+ protected boolean preVisit(Module m) {
+ if (javaseFlag.isEnabled() == false
+ && m.getModuleDefinition().getName().startsWith("java.se")) {
+ return false;
+ } else {
+ indent += INDENT;
+ return true;
+ }
+ }
+
+ @Override
+ protected void visit(Module m) {
+ printModule(m);
+ }
+
+ @Override
+ protected void postVisit(Module m) {
+ if (javaseFlag.isEnabled() == false
+ && m.getModuleDefinition().getName().startsWith("java.se")) {
+ // empty
+ } else {
+ indent = indent.substring(INDENT_LENGTH);
+ }
+ }
+
+ void printModule(Module m) {
+ msg.println(indent + getMText(m));
}
}
@@ -536,7 +789,7 @@ public class JRepo {
}
String usage() {
- return "install [-v] -r repositoryLocation jamFile | jamURL\n"
+ return "install [-v] -r repositoryLocation jamFile | jamURL\n" // XXX i18n
+ " installs a module into a repository";
}
}
@@ -562,6 +815,7 @@ public class JRepo {
interactiveFlag.register(myFlags);
}
+ @Override
void reset() {
version = null;
platformBinding = null;
@@ -569,6 +823,7 @@ public class JRepo {
interactiveFlag.reset();
}
+ @Override
int parseFlags(String[] args, Map<Character, Flag> flags) {
return super.parseFlags(args, myFlags);
}
@@ -655,7 +910,8 @@ public class JRepo {
}
String usage() {
- return "uninstall [-v] [-f | -i] -r repositoryLocation moduleName [moduleVersion] [modulePlatformBinding]\n"
+ return "uninstall [-v] [-f | -i] -r repositoryLocation moduleName"// XXX i18n
+ + " [moduleVersion] [modulePlatformBinding]\n"
+ " removes a module from a repository, along with associated files.";
}
@@ -667,9 +923,9 @@ public class JRepo {
rc = repo.uninstall(mai);
if (verboseFlag.isEnabled()) {
if (rc) {
- msg.println("Uninstalled " + getMAIText(mai));
+ msg.println("Uninstalled " + getMAIText(mai)); // XXX i18n
} else {
- msg.error("Failed to uninstall " + getMAIText(mai));
+ msg.error("Failed to uninstall " + getMAIText(mai)); // XXX i18n
}
}
} catch (Exception ex) {
@@ -777,10 +1033,12 @@ public class JRepo {
parentFlag.register(myFlags);
}
+ @Override
void reset() {
parentFlag.reset();
}
+ @Override
int parseFlags(String[] args, Map<Character, Flag> flags) {
return super.parseFlags(args, myFlags);
}
@@ -803,16 +1061,17 @@ public class JRepo {
boolean found = visitor.wasFound();
if (verboseFlag.isEnabled() && !found) {
if (moduleName != null) {
- msg.error("Could not find module name starting with '" + moduleName + "'");
+ msg.error("Could not find module name starting with '"// XXX i18n
+ + moduleName + "'");
} else {
- msg.error("Could not find any modules");
+ msg.error("Could not find any modules"); // XXX i18n
}
}
return found;
}
String usage() {
- return "list [-v] [-p] [-r repositoryLocation] moduleName\n"
+ return "list [-v] [-p] [-r repositoryLocation] moduleName\n"// XXX i18n
+ " lists the modules in the repository";
}
@@ -823,15 +1082,21 @@ public class JRepo {
void doit(Repository repo, Messenger msg) {
boolean printedHeader = false;
- for (ModuleArchiveInfo mai : repo.list()) {
- if (moduleName == null || mai.getName().startsWith(moduleName)) {
- if (!printedHeader) {
- msg.println(getRepositoryText(repo));
- msg.println(verboseFlag.isEnabled() ? maiHeadingVerbose : maiHeading);
- printedHeader = true;
+ List<ModuleArchiveInfo> maiList = repo.list();
+ if (maiList.size() == 0 && verboseFlag.isEnabled()) {
+ msg.println(getRepositoryText(repo));
+ msg.println(" empty");
+ } else {
+ for (ModuleArchiveInfo mai : repo.list()) {
+ if (moduleName == null || mai.getName().startsWith(moduleName)) {
+ if (!printedHeader) {
+ msg.println(getRepositoryText(repo));
+ msg.println(verboseFlag.isEnabled() ? maiHeadingVerbose : maiHeading);
+ printedHeader = true;
+ }
+ msg.println(getMAIText(mai));
+ found = true;
}
- msg.println(getMAIText(mai));
- found = true;
}
}
}
--- a/test/java/module/modinit/RunMTest.java Fri Jul 11 08:29:29 2008 -0700
+++ b/test/java/module/modinit/RunMTest.java Fri Jul 18 12:47:04 2008 -0700
@@ -61,8 +61,8 @@ public class RunMTest {
defaultProperties.put("header", WARNING_HEADER);
}
- private final File file;
- private final File outputDirectory;
+ protected final File file;
+ protected final File outputDirectory;
private final List<ModuleDescription> modules;
private final List<TestDescription> tests;
@@ -77,7 +77,7 @@ public class RunMTest {
} else {
subdir = cPath.substring(k + mString.length());
}
- System.out.println(">>> Test " + subdir);
+ System.out.println(">>>Test " + subdir);
outputDirectory = new File(baseDirectory, subdir);
if (outputDirectory.exists()) {
recursiveDelete(outputDirectory);
@@ -87,7 +87,7 @@ public class RunMTest {
parse();
}
- private String getName() {
+ protected String getName() {
return file.getName();
}
@@ -310,16 +310,49 @@ public class RunMTest {
}
}
- private static class TestDescription {
-
- private final String name;
- private String result;
-
- private TestDescription(String name) {
+
+ // Abstracting the creation of TestDescription instances to a factory
+ // allows other tests to override TestDescription.runTest.
+ //
+ public static class TestDescriptionFactory {
+ private static TestDescriptionFactory instance;
+
+ static TestDescription create(String name) {
+ return getInstance().doCreate(name);
+ }
+
+ private static TestDescriptionFactory getInstance() {
+ if (instance == null) {
+ String factName = System.getProperty("TestDescriptionFactory.classname");
+ if (factName != null) {
+ try {
+ Class<?> clazz = Class.forName(factName);
+ instance = (TestDescriptionFactory) clazz.newInstance();
+ } catch (Throwable t) {
+ throw new RuntimeException(t);
+ }
+ } else {
+ instance = new TestDescriptionFactory();
+ }
+ }
+ return instance;
+ }
+
+ protected TestDescription doCreate(String name) {
+ return new TestDescription(name);
+ }
+ }
+
+ public static class TestDescription {
+
+ protected final String name;
+ protected String result;
+
+ TestDescription(String name) {
this.name = name;
}
- private void runTest(RunMTest mTest) throws Exception {
+ protected void runTest(RunMTest mTest) throws Exception {
System.out.println("> Running test " + name + "...");
Repository parent = sun.module.repository.RepositoryConfig.getSystemRepository();
Repository repository = Modules.newLocalRepository(mTest.getName(), mTest.outputDirectory, null, parent);
@@ -360,7 +393,7 @@ public class RunMTest {
private TestDescription parseTest(String header, BufferedReader reader) throws IOException {
String[] s = header.split(" ");
String name = s[s.length - 1];
- TestDescription test = new TestDescription(name);
+ TestDescription test = TestDescriptionFactory.create(name);
while (true) {
String line = getLine(reader);
if (line == null) {
--- a/test/java/module/tools/jrepo/JRepoTest.java Fri Jul 11 08:29:29 2008 -0700
+++ b/test/java/module/tools/jrepo/JRepoTest.java Fri Jul 18 12:47:04 2008 -0700
@@ -36,7 +36,7 @@ import sun.module.tools.JRepo;
* @run main JRepoTest
*/
public class JRepoTest {
- private static final boolean debug = Boolean.getBoolean("module.tools.debug");
+ private static final boolean debug = true;/*Boolean.getBoolean("module.tools.debug"); ZZZ */
static final ByteArrayOutputStream bout = new ByteArrayOutputStream();
static final ByteArrayOutputStream berr = new ByteArrayOutputStream();
@@ -56,7 +56,6 @@ public class JRepoTest {
* as a sanity check.
*/
-
// Create a temporary directory for JAM files and repositories.
File tmp = new File(
System.getProperty("test.scratch", "."), "JRepoTestDir").getCanonicalFile();
@@ -73,7 +72,7 @@ public class JRepoTest {
check(!jr.run(getArgs("install")) && usageOK(0));
check(!jr.run(getArgs("install repo module")) && usageOK(0));
check(!jr.run(getArgs("install -r repoDoesNotExist module")) && errorOK(1));
- check(!jr.run(getArgs("install -p -r repoDoesNotExist module")) && errorOK(9));
+ check(!jr.run(getArgs("install -p -r repoDoesNotExist module")) && errorOK(13));
// Create a directory for a local repository.
File localRepoDir = new File(tmp, "JRepoTestLocalRepoDir");
@@ -123,13 +122,13 @@ public class JRepoTest {
check(!jr.run(getArgs("list")) && errorOK(0));
check(jr.run(getArgs("list -p")) && outputOK(14));
check(!jr.run(getArgs("list -v")) && errorOK(1));
- check(jr.run(getArgs("list -p -v")) && outputOK(14));
+ check(jr.run(getArgs("list -p -v")) && outputOK(22));
// Common prefixes of "list"
check(jr.run(getArgs("lis -r " + repo)) && outputOK(6));
check(jr.run(getArgs("li -p -r " + repo)) && outputOK(20));
check(jr.run(getArgs("l -v -r " + repo)) && outputOK(6));
- check(jr.run(getArgs("list -p -v -r " + repo)) && outputOK(20));
+ check(jr.run(getArgs("list -p -v -r " + repo)) && outputOK(28));
// Nonexist things are not there
check(!jr.run(getArgs("list ThisWillNotBeFound")) && outputOK(0));
@@ -139,26 +138,26 @@ public class JRepoTest {
check(!jr.run(getArgs("list java.se.core")) && errorOK(0));
check(jr.run(getArgs("list -p java.se.core")) && outputOK(3));
check(!jr.run(getArgs("list -v java.se.core")) && errorOK(1));
- check(jr.run(getArgs("list -p -v java.se.core")) && outputOK(3));
+ check(jr.run(getArgs("list -p -v java.se.core")) && outputOK(11));
// Various options work
check(jr.run(getArgs("list -r " + repo + " JRepoModuleA")) && outputOK(3));
check(jr.run(getArgs("list -p -r " + repo + " JRepoModuleA")) && outputOK(3));
check(jr.run(getArgs("list -v -r " + repo + " JRepoModuleA")) && outputOK(3));
- check(jr.run(getArgs("list -p -v -r " + repo + " JRepoModuleA")) && outputOK(3));
+ check(jr.run(getArgs("list -p -v -r " + repo + " JRepoModuleA")) && outputOK(11));
// Given module name is treated as substring of full module names
check(jr.run(getArgs("list -r " + repo + " JRepoModule")) && outputOK(6));
check(jr.run(getArgs("list -p -r " + repo + " JRepoModu")) && outputOK(6));
check(jr.run(getArgs("list -v -r " + repo + " JRepo")) && outputOK(6));
- check(jr.run(getArgs("list -p -v -r " + repo + " JR")) && outputOK(6));
+ check(jr.run(getArgs("list -p -v -r " + repo + " JR")) && outputOK(14));
/* Check uninstall command */
check(!jr.run(getArgs("uninstall")) && usageOK(0));
check(!jr.run(getArgs("uninstall repo MODULE")) && usageOK(0));
check(!jr.run(getArgs("uninstall -r repoDoesNotExist module")) && errorOK(1));
- check(!jr.run(getArgs("uninstall -p -r repoDoesNotExist module")) && errorOK(9));
+ check(!jr.run(getArgs("uninstall -p -r repoDoesNotExist module")) && errorOK(13));
check(!jr.run(getArgs("uninstall -r " + repo + " Fred")) && errorOK(0));
check(!jr.run(getArgs("uninstall -v -r " + repo + " Fred")) && errorOK(1));
@@ -314,7 +313,7 @@ public class JRepoTest {
/** Check that usage is provided as expected. */
static boolean usageOK(int len) throws Throwable {
// Add number of default lines of usage output to given value.
- return checkOutput(8 + len, berr);
+ return checkOutput(12 + len, berr);
}
//--------------------- Infrastructure ---------------------------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/module/tools/ImportTraverser.java Fri Jul 18 12:47:04 2008 -0700
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.module.tools;
+
+import java.io.File;
+import java.io.IOException;
+import java.module.Module;
+import java.module.ModuleArchiveInfo;
+import java.module.ModuleDefinition;
+import java.module.ModuleInitializationException;
+import java.module.Modules;
+import java.module.Query;
+import java.module.Repository;
+import java.module.VersionConstraint;
+import java.util.LinkedHashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import sun.module.JamUtils;
+
+/**
+ * Provides a means to visit each of a module's imports.
+ * @since 1.7
+ */
+public class ImportTraverser implements Iterable<Module> {
+ private Set<Module> visited = new LinkedHashSet<Module>();
+
+ /**
+ * Invokes a visitor on each of the imports of the module identified by
+ * the parameters. The imports are visited in depth-first order with respect to
+ * the graph of interconnected modules. Note that the module identified
+ * by parameters is the first one visited.
+ *
+ * @param repo repository in which to find a named module
+ * @param name name of the module to find
+ * @param constraint version constraint on the module; may be null
+ * @throws ModuleInitializationException if the identified module cannot be
+ * instantiated
+ */
+ public void traverse(Visitor visitor, Repository repo,
+ String name,
+ VersionConstraint constraint)
+ throws ModuleInitializationException{
+
+ traverse(visitor, repo, name, constraint, null, null);
+ }
+
+ /**
+ * Invokes a visitor on each of the imports of the module identified by
+ * the parameters. The imports are visited in depth-first order with respect to
+ * the graph of interconnected modules. Note that the module identified
+ * by parameters is the first one visited.
+ *
+ * @param jamName name of a JAM file for which dependencies are returned
+ * @throws java.io.IOException if a Repository for the named JAM file cannot
+ * be created
+ * @throws ModuleInitializationException if the identified module cannot be
+ * instantiated
+ */
+/* public void traverse(Visitor, visitor, String jamName)
+ throws IOException, ModuleInitializationException {
+
+ File repoDir = JamUtils.createTempDir();
+ Repository repo = Modules.newLocalRepository("importvisitor", repoDir);
+ ModuleArchiveInfo mai = repo.install(new File(jamName).toURI().toURL());
+
+ visit(visitor, repo, mai.getName(), mai.getVersion().toVersionConstraint(),
+ mai.getPlatform(), mai.getArch());
+ }
+*/
+ /**
+ * Invokes a visitor on each of the imports of the module identified by
+ * the parameters. The imports are visited in depth-first order with respect to
+ * the graph of interconnected modules. Note that the module identified
+ * by parameters is the first one visited.
+ *
+ * @param repo repository in which to find a named module
+ * @param name name of the module to find
+ * @param constraint version constraint on the module; may be null
+ * @param platform platform of the module to find; may be null
+ * @param arch architecture of the module to find; may be null
+ * @throws ModuleInitializationException if the identified module cannot be
+ * instantiated
+ * @throws IllegalArgumentException if one of {@code platform, arch} is
+ * null and the other is not, or if the {@code repository} finds more than
+ * one module matching the given parameters.
+ */
+ public void traverse(Visitor visitor, Repository repo,
+ String name, VersionConstraint constraint,
+ String platform, String arch)
+ throws ModuleInitializationException {
+
+ if (repo == null || name == null) {
+ throw new NullPointerException();
+ }
+
+ if (platform == null ^ arch == null) {
+ throw new IllegalArgumentException(
+ "module platform and arch must be either both null or both non-null");
+ }
+
+ Query q = Query.module(
+ name, constraint == null ? VersionConstraint.DEFAULT : constraint);
+ if (platform != null) {
+ q = Query.and(
+ q, Query.annotation(java.module.annotation.PlatformBinding.class));
+ }
+
+ List<ModuleDefinition> mdList = repo.find(q);
+ if (mdList.size() > 1) {
+ throw new IllegalArgumentException(
+ "more than one matching module found for name=" + name
+ + " version=" + constraint
+ + " platform=" + platform + " arch=" + arch);
+ } else if (mdList.size() == 1) {
+ Module m = mdList.get(0).getModuleInstance();
+ visitor.init(m);
+ traverse(visitor, m);
+ }
+ }
+
+ /**
+ * Invokes a visitor on each of the imports of the {@code module} The
+ * imports are visited in depth-first order with respect to the graph of
+ * interconnected modules. Note that the given module is the first one
+ * visited.
+ */
+ public void traverse(Visitor visitor, Module module) {
+ if (visited.contains(module)) {
+ return;
+ }
+ visited.add(module);
+ if (visitor.preVisit(module)) {
+ List<Module> imported = module.getImportedModules();
+ for (Module m : imported) {
+ visitor.visit(m);
+ traverse(visitor, m);
+ }
+ }
+ visitor.postVisit(module);
+ }
+
+ /**
+ * @return true if any modules were traversed, false otherwise
+ */
+ public boolean traversedAny() {
+ return visited.size() > 0;
+ }
+
+ /**
+ * @return an {@code Iterator} over the {@code Module} instances which have
+ * been visited; note that the list is ordered by the depth-first
+ * traversal of module imports.
+ */
+ public Iterator<Module> iterator() {
+ return visited.iterator();
+ }
+
+ abstract static class Visitor {
+ private final ImportTraverser traverser;
+
+ protected Visitor(ImportTraverser traverser) {
+ this.traverser = traverser;
+ }
+
+ /**
+ * @param m {@code Module} possibly visited by the traverser
+ * @return true if the given {@code Module} has been visited, false
+ * otherwise
+ */
+ protected boolean visited(Module m) {
+ return traverser.visited.contains(m);
+ }
+
+ /**
+ * Invoked with the {@code Module} identified by the client-called {@code
+ * visit} method before any other {@code Module} instances are visited.
+ * @param m First {@code Module} visited by the traverser
+ */
+ protected void init(Module m) { }
+
+ /**
+ * Invoked on a {@code Module} instance before its imports are visited.
+ * @param m {@code Module} which imports the module about to be visited by
+ * {@link #visit(Module)}
+ * @return true if the {@code Module} should be visited, false otherwise.
+ */
+ protected boolean preVisit(Module m) {
+ return true;
+ }
+
+ /**
+ * Invoked on a {@code Module} instance as it is visited
+ * @param m {@code Module} being visited
+ */
+ protected void visit(Module m) { }
+
+ /**
+ * Invoked on a {@code Module} instance after its imports are visited.
+ * @param m {@code Module} which imports the module that was just visited by
+ * {@link #visit(Module)}
+ */
+ protected void postVisit(Module m) { }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/module/modinit/JRepoDepGold.txt Fri Jul 18 12:47:04 2008 -0700
@@ -0,0 +1,114 @@
+>>>Test basic/import.mtest
+m1-1.0
+ java.se-1.7
+ m2-1.0
+ java.se-1.7
+>>>Test basic/import.mtest
+m2-1.0
+ java.se-1.7
+>>>Test circular/circular2.mtest
+m1-1.0
+ java.se-1.7
+ m2-1.0
+ java.se-1.7
+ m3-1.0
+ java.se-1.7
+ m1-1.0
+>>>Test circular/circular2.mtest
+m2-1.0
+ java.se-1.7
+ m3-1.0
+ java.se-1.7
+ m1-1.0
+ java.se-1.7
+ m2-1.0
+>>>Test circular/circular2.mtest
+m3-1.0
+ java.se-1.7
+ m1-1.0
+ java.se-1.7
+ m2-1.0
+ java.se-1.7
+ m3-1.0
+>>>Test importpolicy/import1.mtest
+m1-0.0-default
+ java.se-1.7
+ m2-0.0-default
+ java.se-1.7
+>>>Test importpolicy/optional1.mtest
+m1-0.0-default
+ java.se-1.7
+ m2-0.0-default
+ java.se-1.7
+ m3-0.0-default
+ java.se-1.7
+>>>Test importpolicy/optional2.mtest
+m1-0.0-default
+ java.se-1.7
+ m3-0.0-default
+ java.se-1.7
+>>>Test importpolicy/recurse1.mtest
+m1-0.0-default
+ java.se-1.7
+ m2-0.0-default
+ java.se-1.7
+>>>Test importpolicy/version1.mtest
+m1-0.0-default
+ java.se-1.7
+ m2-1.2
+ java.se-1.7
+>>>Test importpolicy/version3.mtest
+m1-0.0-default
+ java.se-1.7
+ m2-1.2
+ java.se-1.7
+>>>Test optional/basic.mtest
+m1-0.0-default
+ java.se-1.7
+ m2-0.0-default
+ java.se-1.7
+ m3-0.0-default
+ java.se-1.7
+ m4-0.0-default
+ java.se-1.7
+>>>Test optional/indirect.mtest
+m1-0.0-default
+ java.se-1.7
+ m2-0.0-default
+ java.se-1.7
+ m3-0.0-default
+ java.se-1.7
+ m4-0.0-default
+ java.se-1.7
+>>>Test optional/missing.mtest
+m1-0.0-default
+ java.se-1.7
+ m2-0.0-default
+ java.se-1.7
+ m4-0.0-default
+ java.se-1.7
+>>>Test validation/shadow4.mtest
+m1-0.0-default
+ java.se-1.7
+ m2-0.0-default
+ java.se-1.7
+ m3-0.0-default
+ java.se-1.7
+>>>Test validation/shadow4.mtest
+m2-0.0-default
+ java.se-1.7
+ m3-0.0-default
+ java.se-1.7
+>>>Test validation/shadow4.mtest
+m3-0.0-default
+ java.se-1.7
+>>>Test version/version3.mtest
+m1-1.1
+ java.se-1.7
+ m2-0.0-default
+ java.se-1.7
+ m4-1.6
+ java.se-1.7
+ m3-2.0
+ java.se-1.7
+ m4-1.6
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/module/modinit/JRepoDependenciesTest.java Fri Jul 18 12:47:04 2008 -0700
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+import java.io.*;
+import java.module.*;
+import java.util.*;
+import sun.module.tools.JRepo;
+
+/**
+ * @test
+ * @library ../tools ../tools/jrepo
+ * @compile -XDignore.symbol.file
+ * RunMTest.java
+ * classp/MainX.java
+ * JRepoDependenciesTest.java
+ * @run main/othervm
+ * -DTestDescriptionFactory.classname=JRepoDependenciesTest$MyFactory
+ * JRepoDependenciesTest
+ */
+public class JRepoDependenciesTest {
+ private static final boolean debug = Boolean.getBoolean("module.tools.debug");
+
+ private static PrintStream outStream = null;
+
+ private static final String[] tests = {
+ "basic/import.mtest",
+ "circular/circular2.mtest",
+ "importpolicy/import1.mtest",
+ "importpolicy/optional1.mtest",
+ "importpolicy/optional2.mtest",
+ "importpolicy/recurse1.mtest",
+ "importpolicy/version1.mtest",
+ "importpolicy/version3.mtest",
+ "optional/basic.mtest",
+ "optional/indirect.mtest",
+ "optional/missing.mtest",
+ "validation/shadow4.mtest",
+ "version/version3.mtest"
+ };
+
+ private static PrintStream resultStream;
+
+ public static void realMain(String args[]) throws Throwable {
+ if (args.length == 0) {
+ args = tests;
+ }
+ File resultFile = new File(
+ System.getProperty("test.scratch", "."), "JRepoDepOut.txt");
+ resultFile.delete();
+ resultStream = new PrintStream(new FileOutputStream(resultFile));
+ RunMTest.main(args);
+ resultStream.close();
+ compare(resultFile);
+ }
+
+ static void compare(File resultFile) throws Throwable {
+ File goldFile = new File(
+ System.getProperty("test.src", "."), "JRepoDepGold.txt");
+ if (goldFile.exists()) {
+ BufferedReader goldReader = new BufferedReader(
+ new FileReader(goldFile));
+ BufferedReader resultReader = new BufferedReader(
+ new FileReader(resultFile));
+ String goldLine = null;
+ String resultLine = null;
+ while ((goldLine = goldReader.readLine()) != null) {
+ goldLine = goldLine.replace('/', File.separatorChar);
+ resultLine = resultReader.readLine();
+ if (!goldLine.equals(resultLine)) {
+ System.err.println(
+ "MISMATCH: expected\n\t" + goldLine
+ + "but got\n\t" + resultLine);
+ fail();
+ break;
+ }
+ }
+ goldReader.close();
+ resultReader.close();
+ pass();
+ } else {
+ System.err.println(
+ "Error: JRepoDepGold.txt does not exist: "
+ + "not comparing actual v. expected results");
+ fail();
+ }
+ }
+
+ /** Return an array of Strings from the given String. */
+ static String[] getArgs(String s) {
+ List<String> args = new ArrayList<String>();
+ StringTokenizer st = new StringTokenizer(s);
+ while (st.hasMoreTokens()) {
+ String token = st.nextToken();
+ if (debug) System.err.println("adding arg " + token);
+ args.add(token);
+ }
+ if (debug) System.err.println("args length is " + args.size());
+ return args.toArray(new String[0]);
+ }
+
+ public static class MyFactory extends RunMTest.TestDescriptionFactory {
+
+ protected RunMTest.TestDescription doCreate(String name) {
+ return new MyTestDescription(name);
+ }
+ }
+
+ public static class MyTestDescription extends RunMTest.TestDescription {
+ MyTestDescription(String name) {
+ super(name);
+ }
+
+ protected void runTest(RunMTest mTest) throws Exception {
+ Repository parent = sun.module.repository.RepositoryConfig.getSystemRepository();
+ Repository repository = Modules.newLocalRepository(
+ mTest.getName(), mTest.outputDirectory, null, parent);
+
+ File file = mTest.file;
+ char SEP = File.separatorChar;
+ String mString = SEP + "mtest" + SEP;
+ String cPath = file.getCanonicalPath();
+ int k = cPath.lastIndexOf(mString);
+ String subdir;
+ if (k == -1) {
+ subdir = file.getName();
+ } else {
+ subdir = cPath.substring(k + mString.length());
+ }
+ resultStream.println(">>>Test " + subdir);
+
+ JRepo jr = new JRepo(resultStream, new PrintStream(System.err), null);
+
+ try {
+ check(jr.run(getArgs(
+ "dependencies -r "
+ + mTest.outputDirectory.getAbsolutePath() + " "
+ + name)));
+ } catch (Exception ex) {
+ if (ex instanceof ModuleInitializationException) {
+ // Ignore: Assume this is expected or that it is a new error
+ // that will be diagnosed when by examining the modinit tests
+ // themselves.
+ } else {
+ unexpected(ex);
+ }
+ }
+ }
+ }
+
+ //--------------------- Infrastructure ---------------------------
+ static volatile int passed = 0, failed = 0;
+ static boolean pass() {passed++; return true;}
+ static boolean fail() {failed++; Thread.dumpStack(); return false;}
+ static boolean fail(String msg) {System.err.println(msg); return fail();}
+ static void unexpected(Throwable t) {failed++; t.printStackTrace();}
+ static boolean check(boolean cond) {if (cond) pass(); else fail(); return cond;}
+ static boolean equal(Object x, Object y) {
+ if (x == null ? y == null : x.equals(y)) return pass();
+ else return fail(x + " not equal to " + y);}
+ public static void main(String[] args) throws Throwable {
+ try {realMain(args);} catch (Throwable t) {unexpected(t);}
+ System.out.println("\nPassed = " + passed + " failed = " + failed);
+ if (failed > 0) throw new AssertionError("Some tests failed");}
+}