diff --git a/pom.xml b/pom.xml index 661feb3bc04..31541df8499 100644 --- a/pom.xml +++ b/pom.xml @@ -56,6 +56,22 @@ **/*.class + + test/org/rascalmpl/benchmark + test/org/rascalmpl/benchmark + + **/*.java + **/*.class + + + + test/org/rascalmpl/test/data + test/org/rascalmpl/test/data + + **/*.java + **/*.class + + . false diff --git a/src/org/rascalmpl/interpreter/utils/RascalManifest.java b/src/org/rascalmpl/interpreter/utils/RascalManifest.java index 447bc72aba5..9367bf726ab 100644 --- a/src/org/rascalmpl/interpreter/utils/RascalManifest.java +++ b/src/org/rascalmpl/interpreter/utils/RascalManifest.java @@ -68,13 +68,16 @@ public static String getRascalVersionNumber() { * This looks into the META-INF/MANIFEST.MF file for a Name and Specification-Version */ public String getManifestVersionNumber(ISourceLocation project) throws IOException { - Manifest mf = new Manifest(javaManifest(project)); + InputStream is = javaManifest(project); + if (is != null) { + Manifest mf = new Manifest(is); - String bundleName = mf.getMainAttributes().getValue("Name"); - if (bundleName != null && bundleName.equals("rascal")) { - String result = mf.getMainAttributes().getValue("Specification-Version"); - if (result != null) { - return result; + String bundleName = mf.getMainAttributes().getValue("Name"); + if (bundleName != null && bundleName.equals("rascal")) { + String result = mf.getMainAttributes().getValue("Specification-Version"); + if (result != null) { + return result; + } } } diff --git a/src/org/rascalmpl/library/util/PathConfig.java b/src/org/rascalmpl/library/util/PathConfig.java index 56c205af84c..7765db1f725 100644 --- a/src/org/rascalmpl/library/util/PathConfig.java +++ b/src/org/rascalmpl/library/util/PathConfig.java @@ -18,6 +18,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.function.Consumer; +import java.util.function.Predicate; import java.util.jar.Manifest; import org.rascalmpl.interpreter.Configuration; @@ -470,16 +473,23 @@ public static PathConfig fromSourceProjectRascalManifest(ISourceLocation manifes IListWriter libsWriter = vf.listWriter(); IListWriter srcsWriter = vf.listWriter(); IListWriter messages = vf.listWriter(); - - if (!projectName.equals("rascal")) { - // always add the standard library but not for the project named "rascal" - // which contains the source of the standard library - try { - libsWriter.append(resolveCurrentRascalRuntimeJar()); - } - catch (IOException e) { - messages.append(Messages.error(e.getMessage(), manifestRoot)); - } + + // Approach: + // - Rascal modules of `rascal` inside `std` are loaded from the + // "current `rascal`" (as per `resolveCurrentRascalRuntimeJar`). + // - Rascal modules of `rascal` outside `std` are loaded from the + // "current project" of this path config (as per `manifestRoot`). + // - Java classes of `rascal`, regardless of inside/outside `std`, are + // loaded from the current `rascal`. + // + // Thus, Rascal modules and Java classes of `rascal` inside `std` are + // guaranteed to be consistently loaded from the same version. + try { + srcsWriter.append(URIUtil.rootLocation("std")); + libsWriter.append(resolveCurrentRascalRuntimeJar()); + } + catch (IOException e) { + messages.append(Messages.error(e.getMessage(), manifestRoot)); } ISourceLocation target = URIUtil.correctLocation("project", projectName, "target/classes"); @@ -535,7 +545,7 @@ public static PathConfig fromSourceProjectRascalManifest(ISourceLocation manifes } if (libProjectName != null) { - if (reg.exists(projectLoc) && dep != rascalProject) { + if (reg.exists(projectLoc)) { // The project we depend on is available in the current workspace. // so we configure for using the current state of that project. PathConfig childConfig = fromSourceProjectRascalManifest(projectLoc, mode); @@ -543,7 +553,9 @@ public static PathConfig fromSourceProjectRascalManifest(ISourceLocation manifes switch (mode) { case INTERPRETER: srcsWriter.appendAll(childConfig.getSrcs()); - libsWriter.append(childConfig.getBin()); + if (dep != rascalProject) { // Never load Java classes from a non-current `rascal` + libsWriter.append(childConfig.getBin()); + } break; case COMPILER: libsWriter.append(setTargetScheme(projectLoc)); @@ -556,9 +568,6 @@ public static PathConfig fromSourceProjectRascalManifest(ISourceLocation manifes // error messages are transitively collected messages.appendAll(childConfig.getMessages()); } - else if (dep == rascalProject) { - // not adding it again (we added rascal already above) - } else { // just a pre-installed dependency in the local maven repository if (!reg.exists(dep)) { @@ -570,7 +579,9 @@ else if (dep == rascalProject) { libsWriter.append(dep); break; case INTERPRETER: - libsWriter.append(dep); + if (dep != rascalProject) { // Never load Java classes from a non-current `rascal` + libsWriter.append(dep); + } addLibraryToSourcePath(reg, srcsWriter, messages, dep); break; default: @@ -586,36 +597,42 @@ else if (dep == rascalProject) { } try { - if (!projectName.equals("rascal") && rascalProject == null) { - // always add the standard library but not for the project named "rascal" - // which contains the (source of) the standard library, and if we already - // have a dependency on the rascal project we don't add it here either. - var rascalLib = resolveCurrentRascalRuntimeJar(); - messages.append(Messages.info("Effective rascal library: " + rascalLib, getPomXmlLocation(manifestRoot))); - libsWriter.append(rascalLib); - } - else if (projectName.equals("rascal")) { - messages.append(Messages.info("detected rascal self-application", getPomXmlLocation(manifestRoot))); + var currentRascal = resolveCurrentRascalRuntimeJar(); + var currentRascalPhysical = reg.logicalToPhysical(currentRascal); + var currentRascalVersion = RascalManifest.getRascalVersionNumber(); + + // Checks if location `otherRascal` represents the same version of + // the project named "rascal" as location `currentRascal` + Predicate isCurrentRascal = otherRascal -> { + try { + var otherRascalPhysical = reg.logicalToPhysical(otherRascal); + var otherRascalVersion = new RascalManifest().getManifestVersionNumber(otherRascalPhysical); + return Objects.equals(currentRascalPhysical, otherRascalPhysical) + || Objects.equals(currentRascalVersion, otherRascalVersion); + } catch (IOException e) { + return false; + } + }; + + Consumer report = s -> messages.append(Messages.info(s, getPomXmlLocation(manifestRoot))); + report.accept("Using Rascal standard library at " + currentRascal + ". Version: " + currentRascalVersion + "."); + + // When this path config's project is `rascal`... + if (projectName.equals("rascal") && !isCurrentRascal.test(target)) { + report.accept("Ignoring Rascal standard library at " + target + " (self-application)"); } - else if (rascalProject != null) { - // The Rascal interpreter can not escape its own classpath, whether + + // When a dependency of this path config's project is `rascal`... + if (rascalProject != null && !isCurrentRascal.test(rascalProject)) { + report.accept("Ignoring Rascal standard library at " + rascalProject + " (dependency in POM)"); + + // Note: The Rascal interpreter can not escape its own classpath, whether // or not we configure a different version in the current project's // pom.xml or not. So that pom dependency is always ignored! - // We check this also in COMPILED mode, for the sake of consistency, // but it is not strictly necessary since the compiler can check and compile // against any standard library on the libs path, even if it's running // itself against a different rascal runtime and standard library. - - RascalManifest rmf = new RascalManifest(); - var builtinVersion = RascalManifest.getRascalVersionNumber(); - var dependentRascalProject = reg.logicalToPhysical(rascalProject); - var dependentVersion = rmf.getManifestVersionNumber(dependentRascalProject); - - if (!builtinVersion.equals(dependentVersion)) { - messages.append(Messages.info("Effective rascal version: " + builtinVersion, getPomXmlLocation(manifestRoot))); - messages.append(Messages.warning("Different rascal dependency is not used: " + dependentVersion, getPomXmlLocation(manifestRoot))); - } } ISourceLocation projectLoc = URIUtil.correctLocation("project", projectName, ""); @@ -635,8 +652,11 @@ else if (rascalProject != null) { messages.append(Messages.error(e.getMessage(), getRascalMfLocation(manifestRoot))); } - for (String srcName : manifest.getSourceRoots(manifestRoot)) { + if (refersToStd(projectName, srcName)) { + continue; // Never load Rascal modules of `std` from a non-current `rascal` + } + var srcFolder = URIUtil.getChildLocation(manifestRoot, srcName); if (!reg.exists(srcFolder) || !reg.isDirectory(srcFolder)) { @@ -661,18 +681,16 @@ private static void addLibraryToSourcePath(URIResolverRegistry reg, IListWriter return; } - var manifest = new RascalManifest(); - - // the rascal dependency leads to a dependency on the std:/// location, somewhere _inside_ of the rascal jar - if (manifest.getProjectName(jar).equals("rascal")) { - srcsWriter.append(URIUtil.rootLocation("std")); - return; - } + var projectName = manifest.getProjectName(jar); boolean foundSrc = false; for (String src : manifest.getSourceRoots(jar)) { + if (refersToStd(projectName, src)) { + continue; // Never load Rascal modules of `std` from a non-current `rascal` + } + ISourceLocation srcLib = URIUtil.getChildLocation(jar, src); if (reg.exists(srcLib)) { srcsWriter.append(srcLib); @@ -683,12 +701,16 @@ private static void addLibraryToSourcePath(URIResolverRegistry reg, IListWriter } } - if (!foundSrc) { + if (!foundSrc && !projectName.equals("rascal")) { // if we could not find source roots, we default to the jar root srcsWriter.append(jar); } } + private static boolean refersToStd(String projectName, String srcName) { + return projectName.equals("rascal") && srcName.equals("src/org/rascalmpl/library"); + } + private static ISourceLocation setTargetScheme(ISourceLocation projectLoc) { try { return URIUtil.changeScheme(projectLoc, "target"); @@ -788,7 +810,19 @@ private static String computeMavenCommandName() { */ public void printInterpreterConfigurationStatus(PrintWriter out) { out.println("Module paths:"); - getSrcs().forEach((f) -> out.println(" ".repeat(4) + f)); + getSrcs().forEach((f) -> { + var s = f.toString(); + if (((ISourceLocation) f).getScheme().equals("std")) { + s += " at "; + try { + s += resolveCurrentRascalRuntimeJar(); + } + catch (IOException e) { + s += "unknown physical location"; + } + } + out.println(" ".repeat(4) + s); + }); out.println("JVM library classpath:"); getLibsAndTarget().forEach((l) -> out.println(" ".repeat(4) + l)); out.flush();