Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 20 additions & 13 deletions src/org/rascalmpl/compiler/lang/rascalcore/check/Checker.rsc
Original file line number Diff line number Diff line change
Expand Up @@ -269,15 +269,18 @@ ModuleStatus rascalTModelForLocs(
} else {
for(m <- component){
m_compatible = false;
<found, tm, ms> = getTModelForModule(m, ms);
if(found && !tplOutdated(m, pcfg)){
imports_extends_m = imports_and_extends[m];
try {
<found, tm, ms> = getTModelForModule(m, ms);
if(found && !tplOutdated(m, pcfg)){
imports_extends_m = imports_and_extends[m];

<m_compatible, ms> = importsAndExtendsAreBinaryCompatible(tm, imports_extends_m, ms);
if(m_compatible){
ms.status[m] += {tpl_uptodate(), checked(), bom_update_needed()};
<m_compatible, ms> = 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;
}
}
Expand Down Expand Up @@ -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){
<found, tm, ms> = getTModelForModule(candidate, ms);
if(found){ // TODO: needed?
imports_and_extends = ms.paths<0,2>[candidate];
<compatible, ms> = importsAndExtendsAreBinaryCompatible(tm, imports_and_extends, ms);
if(!compatible){
try {
<found, tm, ms> = getTModelForModule(candidate, ms);
if(found){ // TODO: needed?
imports_and_extends = ms.paths<0,2>[candidate];
<compatible, ms> = importsAndExtendsAreBinaryCompatible(tm, imports_and_extends, ms);
if(!compatible){
return <false, ms>;
}
} else {
return <false, ms>;
}
}
} catch _: return <false, ms>;
}
return <true, ms>;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -424,9 +424,11 @@ tuple[bool, TModel, ModuleStatus] getTModelForModule(MODID moduleId, ModuleStatu
<found, tplLoc> = getTPLReadLoc(moduleId, pcfg);
if(found){
if(traceTPL) println("*** reading tmodel <tplLoc>");
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)){
Expand All @@ -441,7 +443,7 @@ tuple[bool, TModel, ModuleStatus] getTModelForModule(MODID moduleId, ModuleStatu
qualifiedModuleName = moduleId2moduleName(moduleId);
return <false, tmodel(modelName=moduleId2moduleName(moduleId), messages=[error("Cannot read TPL for <qualifiedModuleName>: <e>", tplLoc)]), ms>;
}
msg = "<tplLoc> has outdated or missing Rascal TPL version (required: <getCurrentRascalTplVersion()>)";
msg = "<tplLoc> has outdated Rascal TPL version <tmVersion != "0.0.0" ? tmVersion + " " : "">(required: <getCurrentRascalTplVersion()>)";
println("INFO: <msg>)");
throw rascalTplVersionError(msg);
}
Expand Down
9 changes: 6 additions & 3 deletions src/org/rascalmpl/compiler/lang/rascalcore/check/Import.rsc
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,9 @@ ModuleStatus getImportAndExtendGraph(MODID moduleId, ModuleStatus ms){
}
ms.status[moduleId] += module_dependencies_extracted();

<found, tm, ms> = getTModelForModule(moduleId, ms);
if(found){
try {
<found, tm, ms> = getTModelForModule(moduleId, ms);
if(found){
allImportsAndExtendsValid = true;
rel[loc, PathRole] localImportsAndExtends = {};

Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 ----------------------------------------------
Expand Down Expand Up @@ -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: <e>";
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: <e>";
return true;
}

bool expectNoErrors(list[ModuleMessages] modMsgs){
present = (/error(_,_) := modMsgs);
if(present){
Expand Down Expand Up @@ -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 | | |
Expand Down Expand Up @@ -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){
Expand Down
Loading