diff --git a/src/org/rascalmpl/compiler/lang/rascalcore/check/Checker.rsc b/src/org/rascalmpl/compiler/lang/rascalcore/check/Checker.rsc index d7e3dc4fc63..052543bd357 100644 --- a/src/org/rascalmpl/compiler/lang/rascalcore/check/Checker.rsc +++ b/src/org/rascalmpl/compiler/lang/rascalcore/check/Checker.rsc @@ -269,15 +269,18 @@ ModuleStatus rascalTModelForLocs( } else { for(m <- component){ m_compatible = false; - = getTModelForModule(m, ms); - if(found && !tplOutdated(m, pcfg)){ - imports_extends_m = imports_and_extends[m]; + try { + = getTModelForModule(m, ms); + if(found && !tplOutdated(m, pcfg)){ + imports_extends_m = imports_and_extends[m]; - = importsAndExtendsAreBinaryCompatible(tm, imports_extends_m, ms); - if(m_compatible){ - ms.status[m] += {tpl_uptodate(), checked(), bom_update_needed()}; + = importsAndExtendsAreBinaryCompatible(tm, imports_extends_m, ms); + if(m_compatible){ + ms.status[m] += {tpl_uptodate(), checked(), bom_update_needed()}; + } } - } + } catch rascalTplVersionError(e): ;// m_compatible remains false + compatible_with_all_imports = compatible_with_all_imports && m_compatible; } } @@ -550,14 +553,18 @@ bool uptodateTPls(list[loc] candidates, list[str] mnames, PathConfig pcfg){ tuple[bool, ModuleStatus] libraryDependenciesAreCompatible(list[MODID] candidates, ModuleStatus ms){ pcfg = ms.pathConfig; for(candidate <- candidates){ - = getTModelForModule(candidate, ms); - if(found){ // TODO: needed? - imports_and_extends = ms.paths<0,2>[candidate]; - = importsAndExtendsAreBinaryCompatible(tm, imports_and_extends, ms); - if(!compatible){ + try { + = getTModelForModule(candidate, ms); + if(found){ // TODO: needed? + imports_and_extends = ms.paths<0,2>[candidate]; + = importsAndExtendsAreBinaryCompatible(tm, imports_and_extends, ms); + if(!compatible){ + return ; + } + } else { return ; } - } + } catch _: return ; } return ; } diff --git a/src/org/rascalmpl/compiler/lang/rascalcore/check/CheckerCommon.rsc b/src/org/rascalmpl/compiler/lang/rascalcore/check/CheckerCommon.rsc index 9a447b9c82a..614945f3b79 100644 --- a/src/org/rascalmpl/compiler/lang/rascalcore/check/CheckerCommon.rsc +++ b/src/org/rascalmpl/compiler/lang/rascalcore/check/CheckerCommon.rsc @@ -424,9 +424,11 @@ tuple[bool, TModel, ModuleStatus] getTModelForModule(MODID moduleId, ModuleStatu = getTPLReadLoc(moduleId, pcfg); if(found){ if(traceTPL) println("*** reading tmodel "); + tmVersion = "0.0.0"; try { tm = readBinaryValueFile(ReifiedTModel, tplLoc); if(tm.rascalTplVersion? && isValidRascalTplVersion(tm.rascalTplVersion)){ + tmVersion = tm.rascalTplVersion; ms.tmodels[moduleId] = tm; mloc = getRascalModuleLocation(moduleId, ms); if(isModuleLocationInLibs(mloc, pcfg)){ @@ -441,7 +443,7 @@ tuple[bool, TModel, ModuleStatus] getTModelForModule(MODID moduleId, ModuleStatu qualifiedModuleName = moduleId2moduleName(moduleId); return : ", tplLoc)]), ms>; } - msg = " has outdated or missing Rascal TPL version (required: )"; + msg = " has outdated Rascal TPL version (required: )"; println("INFO: )"); throw rascalTplVersionError(msg); } diff --git a/src/org/rascalmpl/compiler/lang/rascalcore/check/Import.rsc b/src/org/rascalmpl/compiler/lang/rascalcore/check/Import.rsc index 3d0bdb6c426..0449b7b670e 100644 --- a/src/org/rascalmpl/compiler/lang/rascalcore/check/Import.rsc +++ b/src/org/rascalmpl/compiler/lang/rascalcore/check/Import.rsc @@ -147,8 +147,9 @@ ModuleStatus getImportAndExtendGraph(MODID moduleId, ModuleStatus ms){ } ms.status[moduleId] += module_dependencies_extracted(); - = getTModelForModule(moduleId, ms); - if(found){ + try { + = getTModelForModule(moduleId, ms); + if(found){ allImportsAndExtendsValid = true; rel[loc, PathRole] localImportsAndExtends = {}; @@ -239,7 +240,9 @@ ModuleStatus getImportAndExtendGraph(MODID moduleId, ModuleStatus ms){ } return completeModuleStatus(ms); } - } + } + } catch rascalTplVersionError(_): + ; // Need to recheck since TModel uses incompatible TPL version if(rsc_not_found() in ms.status[moduleId]){ return ms; diff --git a/src/org/rascalmpl/compiler/lang/rascalcore/check/ModuleLocations.rsc b/src/org/rascalmpl/compiler/lang/rascalcore/check/ModuleLocations.rsc index 803a960067f..bf72bb487bd 100644 --- a/src/org/rascalmpl/compiler/lang/rascalcore/check/ModuleLocations.rsc +++ b/src/org/rascalmpl/compiler/lang/rascalcore/check/ModuleLocations.rsc @@ -30,6 +30,7 @@ module lang::rascalcore::check::ModuleLocations import IO; import List; import String; +import Message; import util::Reflective; import util::FileSystem; import lang::rascalcore::check::BasicRascalConfig; diff --git a/src/org/rascalmpl/compiler/lang/rascalcore/check/tests/BinaryDependencyTests.rsc b/src/org/rascalmpl/compiler/lang/rascalcore/check/tests/BinaryDependencyTests.rsc index f8ec3b8d6f2..67188c0e718 100644 --- a/src/org/rascalmpl/compiler/lang/rascalcore/check/tests/BinaryDependencyTests.rsc +++ b/src/org/rascalmpl/compiler/lang/rascalcore/check/tests/BinaryDependencyTests.rsc @@ -37,12 +37,14 @@ import lang::rascalcore::check::ATypeBase; import lang::rascalcore::check::TestConfigs; import util::Reflective; import IO; +import ValueIO; import lang::rascalcore::check::Import; import Map; import lang::rascal::\syntax::Rascal; import ParseTree; import String; import lang::rascalcore::check::ModuleLocations; +import util::FileSystem; // ---- Utilities for test setup ---------------------------------------------- @@ -127,6 +129,29 @@ Project removeSourceOfModule(str mname, Project pd){ return pd; } +Project setTPLVersionOfProject(str v, Project pd){ + try { + for(loc f <- find(bin(pd.name), "tpl")){ + tm = readBinaryValueFile(#TModel, f); + tm.rascalTplVersion = v; + writeBinaryValueFile(f, tm); + } + } catch e: throw "setTPLVersionOfProject: "; + return pd; +} + +bool validateTPLVersionOfProject(Project pd){ + try { + for(loc f <- find(bin(pd.name), "tpl")){ + tm = readBinaryValueFile(#TModel, f); + if(!satisfiesVersion(tm.rascalTplVersion, "2.0.0-3.0.0")){ + return false; + } + } + } catch e: throw "setTPLVersionOfProject: "; + return true; +} + bool expectNoErrors(list[ModuleMessages] modMsgs){ present = (/error(_,_) := modMsgs); if(present){ @@ -343,6 +368,82 @@ test bool incompatibleWithBinaryLibraryAfterChange(){ return checkExpectErrors("M2", ["Expected 2 argument(s), found 1"], client.pcfg, remove = [lib, client]); } + +test bool incompatibleWithBinaryLibraryDueToTPLVersion(){ + // Create project "lib" and module "M1" and then compile "M1" + libName = "lib"; + lib = createProject(libName, + ("M1": "int f(int n) = n;"), + createPathConfig(libName) + ); + assert checkExpectNoErrors("M1", lib.pcfg); + + + lib = removeSourceOfModule("M1", lib); // remove source of M1 completely, to be sure + lib = setTPLVersionOfProject("0.0.0", lib); // set rascalTplVersion to incompatible version number + + // Create project "client" and module "M2" and then compile "M2" + // "client" uses "lib" as binary library + clientName = "client"; + client = createProject(clientName, + ("M2": "import M1; // binary import + 'int main() = f(42); // compatible call fo f + "), + createPathConfig(clientName) + [libs = [bin(libName)] ] + ); + return checkExpectErrors("M2", ["has outdated Rascal TPL version"], client.pcfg, remove = [lib, client]); +} + +// Scenarios with outdated TPL versions + +test bool recompileModulesInSameProjectWithIncompatibleTPLs(){ + // Create project "m" with modules "M1" and "M2" + // m's sources remain available while tpl version is downgraded and made incompatible + mName = "m"; + m = createProject(mName, + ("M1": "int f(int n) = n;", + "M2": "import M1; // import + 'int main() = f(42); // compatible call fo f + "), + createPathConfig(mName) + ); + assert checkExpectNoErrors("M2", m.pcfg); + m = setTPLVersionOfProject("0.0.0", m); // set rascalTplVersion to incompatible version number + // tpl is more recent than source + // expect seamless recompilation of M1 and M2 + assert checkExpectNoErrors("M2", m.pcfg, remove = [m]); + return validateTPLVersionOfProject(m); +} + +test bool recompileLibraryModulesWithIncompatibleTPLVersionWhenSourceIsAvailable(){ + // Create project "lib" and module "M1" and then compile "M1" + libName = "lib"; + lib = createProject(libName, + ("M1": "int f(int n) = n;"), + createPathConfig(libName) + ); + assert checkExpectNoErrors("M1", lib.pcfg); + + touch(getRascalModuleLocation("M1", lib.pcfg)); // keep src of M1 but change modification time + lib = setTPLVersionOfProject("0.0.0", lib); // set rascalTplVersion to incompatible version number + // tpl is more recent than source + + // Create project "client" and module "M2" and then compile "M2" + // "client" uses "lib" as library but lib's sources remain available + clientName = "client"; + client = createProject(clientName, + ("M2": "import M1; // binary import + 'int main() = f(42); // compatible call fo f + "), + createPathConfig(clientName) + [srcs = [src(clientName), src(libName)]] + [libs = [bin(libName)] ] + ); + assert checkExpectNoErrors("M2", client.pcfg, remove = [lib, client]); + return validateTPLVersionOfProject(lib) && validateTPLVersionOfProject(client); +} + /* * rascal: IO -+ rascal: IO' * extend | | | @@ -417,6 +518,7 @@ AGrammar getGrammar(TModel tm){ throw "`grammar` has incorrect format in store"; } } + // The binary compatibility test for TModels bool binaryCompatible(tuple[TModel old, TModel new] tms){