Commit 74bc658f authored by Mark Olesen's avatar Mark Olesen
Browse files

ENH: new encapsulation: dynamicCode, dynamicCodeContext

- improve loading/unloading characteristics for codedFixedValue
  but still needs work
parent 51399bbb
......@@ -189,8 +189,9 @@ db/objectRegistry/objectRegistry.C
db/CallbackRegistry/CallbackRegistryName.C
dll = db/dynamicLibrary
$(dll)/codeStream/codeStreamTools.C
$(dll)/dlLibraryTable/dlLibraryTable.C
$(dll)/dynamicCode/dynamicCode.C
$(dll)/dynamicCode/dynamicCodeContext.C
db/functionObjects/functionObject/functionObject.C
db/functionObjects/functionObjectList/functionObjectList.C
......
......@@ -28,10 +28,9 @@ License
#include "IStringStream.H"
#include "OStringStream.H"
#include "IOstreams.H"
#include "SHA1Digest.H"
#include "OSHA1stream.H"
#include "codeStreamTools.H"
#include "stringOps.H"
#include "dynamicCode.H"
#include "dynamicCodeContext.H"
#include "dlLibraryTable.H"
#include "OSspecific.H"
#include "Time.H"
......@@ -71,7 +70,7 @@ bool Foam::functionEntries::codeStream::execute
Istream& is
)
{
codeStreamTools::checkSecurity
dynamicCode::checkSecurity
(
"functionEntries::codeStream::execute(..)",
parentDict
......@@ -81,54 +80,23 @@ bool Foam::functionEntries::codeStream::execute
// must reference parent for stringOps::expand to work nicely
dictionary codeDict("#codeStream", parentDict, is);
// get code, codeInclude, codeOptions
dynamicCodeContext context(codeDict);
// Read three sections of code.
// Remove any leading whitespace - necessary for compilation options,
// convenience for includes and body.
// "codeInclude" is optional
string codeInclude;
if (codeDict.found("codeInclude"))
{
codeInclude = stringOps::trim(codeDict["codeInclude"]);
stringOps::inplaceExpand(codeInclude, codeDict);
}
// "codeOptions" is optional
string codeOptions;
if (codeDict.found("codeOptions"))
{
codeOptions = stringOps::trim(codeDict["codeOptions"]);
stringOps::inplaceExpand(codeOptions, codeDict);
}
// "code" is mandatory
string code = stringOps::trim(codeDict["code"]);
stringOps::inplaceExpand(code, codeDict);
// Create SHA1 digest from the contents
SHA1Digest sha;
{
OSHA1stream os;
os << codeInclude << codeOptions << code;
sha = os.digest();
}
// codeName = prefix + sha1
const fileName codeName = "codeStream_" + sha.str();
// write code into _SHA1 subdir
const fileName codePath = codeStreamTools::codePath("_" + sha.str());
// write library into platforms/$WM_OPTIONS/lib subdir
const fileName libPath = codeStreamTools::libPath(codeName);
// codeName: prefix_ + sha1
// codeDir : _<sha1>
dynamicCode dynCode
(
"codeStream_" + context.sha1().str(),
"_" + context.sha1().str()
);
// Load library if not already loaded
// Version information is encoded in the libPath (encoded with the SHA1)
const fileName libPath = dynCode.libPath();
void* lib = dlLibraryTable::findLibrary(libPath);
// try to load if not already loaded
if (!lib && dlLibraryTable::open(libPath, false))
{
lib = dlLibraryTable::findLibrary(libPath);
......@@ -139,76 +107,54 @@ bool Foam::functionEntries::codeStream::execute
{
if (Pstream::master())
{
if (!codeStreamTools::upToDate(codePath, sha))
if (!dynCode.upToDate(context))
{
Info<< "Creating new library in " << libPath << endl;
Info<< "Creating new library in "
<< dynCode.libPath() << endl;
const fileName fileCsrc
(
codeStreamTools::findTemplate
(
codeTemplateC
)
);
// filter C template
dynCode.addFilterFile(codeTemplateC);
// not found!
if (fileCsrc.empty())
{
FatalIOErrorIn
(
"functionEntries::codeStream::execute(..)",
parentDict
) << "Could not find the code template: "
<< codeTemplateC << nl
<< codeStreamTools::searchedLocations()
<< exit(FatalIOError);
}
List<codeStreamTools::fileAndVars> copyFiles(1);
copyFiles[0].file() = fileCsrc;
copyFiles[0].set("codeInclude", codeInclude);
copyFiles[0].set("code", code);
copyFiles[0].set("SHA1sum", sha.str());
List<codeStreamTools::fileAndContent> filesContents(2);
// filter with this context
dynCode.setFilterContext(context);
// Write Make/files
filesContents[0].first() = "Make/files";
filesContents[0].second() =
dynCode.addCreateFile
(
"Make/files",
codeTemplateC + "\n\n"
+ codeStreamTools::libTarget(codeName);
+ dynCode.libTarget()
);
// Write Make/options
filesContents[1].first() = "Make/options";
filesContents[1].second() =
dynCode.addCreateFile
(
"Make/options",
"EXE_INC = -g \\\n"
+ codeOptions
+ "\n\nLIB_LIBS =";
+ context.options()
+ "\n\nLIB_LIBS ="
);
codeStreamTools writer(codeName, copyFiles, filesContents);
if (!writer.copyFilesContents(codePath))
if (!dynCode.copyFilesContents())
{
FatalIOErrorIn
(
"functionEntries::codeStream::execute(..)",
parentDict
) << "Failed writing " <<nl
<< copyFiles << endl
<< filesContents
) << "Failed writing " << nl
// << copyFiles << endl
// << filesContents
<< exit(FatalIOError);
}
}
const Foam::string wmakeCmd("wmake libso " + codePath);
Info<< "Invoking " << wmakeCmd << endl;
if (Foam::system(wmakeCmd))
if (!dynCode.wmakeLibso())
{
FatalIOErrorIn
(
"functionEntries::codeStream::execute(..)",
parentDict
) << "Failed " << wmakeCmd
) << "Failed wmake " << libPath
<< exit(FatalIOError);
}
}
......@@ -239,16 +185,17 @@ bool Foam::functionEntries::codeStream::execute
void (*function)(const dictionary&, Ostream&);
function = reinterpret_cast<void(*)(const dictionary&, Ostream&)>
(
dlSym(lib, codeName)
dlSym(lib, dynCode.codeName())
);
if (!function)
{
FatalIOErrorIn
(
"functionEntries::codeStream::execute(..)",
parentDict
) << "Failed looking up symbol " << codeName
) << "Failed looking up symbol " << dynCode.codeName()
<< " in library " << lib << exit(FatalIOError);
}
......
......@@ -24,7 +24,8 @@ License
\*---------------------------------------------------------------------------*/
#include "codeStreamTools.H"
#include "dynamicCode.H"
#include "dynamicCodeContext.H"
#include "stringOps.H"
#include "IFstream.H"
#include "OFstream.H"
......@@ -34,25 +35,25 @@ License
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
int Foam::codeStreamTools::allowSystemOperations
int Foam::dynamicCode::allowSystemOperations
(
Foam::debug::infoSwitch("allowSystemOperations", 0)
);
const Foam::word Foam::codeStreamTools::codeTemplateEnvName
const Foam::word Foam::dynamicCode::codeTemplateEnvName
= "FOAM_CODE_TEMPLATES";
const Foam::fileName Foam::codeStreamTools::codeTemplateDirName
const Foam::fileName Foam::dynamicCode::codeTemplateDirName
= "codeTemplates/dynamicCode";
// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
void Foam::codeStreamTools::checkSecurity
void Foam::dynamicCode::checkSecurity
(
const char* title,
const dictionary& context
const dictionary& dict
)
{
if (isAdministrator())
......@@ -60,7 +61,7 @@ void Foam::codeStreamTools::checkSecurity
FatalIOErrorIn
(
title,
context
dict
) << "This code should not be executed by someone with administrator"
<< " rights due to security reasons." << nl
<< "(it writes a shared library which then gets loaded "
......@@ -70,70 +71,9 @@ void Foam::codeStreamTools::checkSecurity
}
// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
Foam::fileName Foam::codeStreamTools::codePath(const word& subDirName)
{
return stringOps::expand("$FOAM_CASE/dynamicCode/" + subDirName);
}
Foam::fileName Foam::codeStreamTools::libPath(const word& codeName)
{
return stringOps::expand
(
"$FOAM_CASE/dynamicCode/platforms/$WM_OPTIONS/lib/lib"
+ codeName + ".so"
);
}
Foam::string Foam::codeStreamTools::libTarget(const word& codeName)
{
return "LIB = $(PWD)/../platforms/$(WM_OPTIONS)/lib/lib" + codeName;
}
Foam::fileName Foam::codeStreamTools::findTemplate(const word& templateFile)
{
// try to get template from FOAM_CODE_TEMPLATES
fileName templateDir(Foam::getEnv(codeTemplateEnvName));
fileName file;
if (!templateDir.empty() && isDir(templateDir))
{
file = templateDir/templateFile;
if (!isFile(file, false))
{
file.clear();
}
}
// not found - fallback to ~OpenFOAM expansion
if (file.empty())
{
file = findEtcFile(codeTemplateDirName/templateFile);
}
return file;
}
Foam::string Foam::codeStreamTools::searchedLocations()
{
return
(
"Under the $"
+ codeTemplateDirName
+ " directory or via via the ~OpenFOAM/"
+ codeTemplateDirName
+ " expansion"
);
}
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
void Foam::codeStreamTools::copyAndExpand
void Foam::dynamicCode::copyAndFilter
(
ISstream& is,
OSstream& os,
......@@ -144,7 +84,7 @@ void Foam::codeStreamTools::copyAndExpand
{
FatalErrorIn
(
"codeStreamTools::copyAndExpand()"
"dynamicCode::copyAndFilter()"
" const"
) << "Failed opening for reading " << is.name()
<< exit(FatalError);
......@@ -154,7 +94,7 @@ void Foam::codeStreamTools::copyAndExpand
{
FatalErrorIn
(
"codeStreamTools::copyAndExpand()"
"dynamicCode::copyAndFilter()"
" const"
) << "Failed writing " << os.name()
<< exit(FatalError);
......@@ -177,54 +117,181 @@ void Foam::codeStreamTools::copyAndExpand
}
Foam::List<Foam::fileName>
Foam::dynamicCode::resolveTemplates(const UList<fileName>& names)
{
// try to get template from FOAM_CODESTREAM_TEMPLATES
const fileName templateDir(Foam::getEnv(codeTemplateEnvName));
DynamicList<fileName> badFiles(names.size());
List<fileName> resolved(names.size());
label nResolved = 0;
forAll(names, fileI)
{
const fileName& templateName = names[fileI];
fileName file;
if (!templateDir.empty() && isDir(templateDir))
{
file = templateDir/templateName;
if (!isFile(file, false))
{
file.clear();
}
}
// not found - fallback to ~OpenFOAM expansion
if (file.empty())
{
file = findEtcFile(codeTemplateDirName/templateName);
}
if (file.empty())
{
badFiles.append(templateName);
}
else
{
resolved[nResolved++] = file;
}
}
resolved.setSize(nResolved);
if (!badFiles.empty())
{
FatalErrorIn
(
"dynamicCode::resolveTemplates(..)"
) << "Could not find the code template(s): "
<< badFiles << nl
<< "Under the $" << codeTemplateDirName
<< " directory or via via the ~OpenFOAM/"
<< codeTemplateDirName << " expansion"
<< exit(FatalError);
}
return resolved;
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::codeStreamTools::codeStreamTools()
{}
Foam::dynamicCode::dynamicCode(const word& codeName)
:
codeName_(codeName),
codeDirName_(codeName)
{
filterVars_.set("typeName", codeName_);
filterVars_.set("SHA1sum", SHA1Digest().str());
}
Foam::dynamicCode::dynamicCode(const word& codeName, const word& codeDirName)
:
codeName_(codeName),
codeDirName_(codeDirName)
{
filterVars_.set("typeName", codeName_);
filterVars_.set("SHA1sum", SHA1Digest().str());
}
// Foam::dynamicCode::dynamicCode(const dynamicCode& dc)
// :
// codeName_(dc.codeName_),
// copyFiles_(dc.copyFiles_),
// filesContents_(dc.filesContents_)
// {}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
void Foam::dynamicCode::clear()
{
filterVars_.clear();
filterFiles_.clear();
createFiles_.clear();
filterVars_.set("typeName", codeName_);
filterVars_.set("SHA1sum", SHA1Digest().str());
}
Foam::codeStreamTools::codeStreamTools
void Foam::dynamicCode::addCreateFile
(
const word& name,
const dictionary& dict
const fileName& name,
const string& contents
)
:
name_(name)
{
read(dict);
createFiles_.append(fileAndContent(name, contents));
}
Foam::codeStreamTools::codeStreamTools
void Foam::dynamicCode::addFilterFile
(
const word& name,
const List<fileAndVars>& copyFiles,
const List<fileAndContent>& filesContents
const fileName& name
)
:
name_(name),
copyFiles_(copyFiles),
filesContents_(filesContents)
{}
{
filterFiles_.append(name);
}
Foam::codeStreamTools::codeStreamTools(const codeStreamTools& tools)
:
name_(tools.name_),
copyFiles_(tools.copyFiles_),
filesContents_(tools.filesContents_)
{}
void Foam::dynamicCode::setFilterContext
(
const dynamicCodeContext& context
)
{
filterVars_.set("code", context.code());
filterVars_.set("codeInclude", context.include());
filterVars_.set("SHA1sum", context.sha1().str());
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
void Foam::dynamicCode::setFilterVariable
(
const word& key,
const string& value
)
{
filterVars_.set(key, value);
}
Foam::fileName Foam::dynamicCode::codePath() const
{
return stringOps::expand("$FOAM_CASE/dynamicCode/" + codeDirName_);
}
bool Foam::codeStreamTools::copyFilesContents(const fileName& dir) const
Foam::fileName Foam::dynamicCode::libPath() const
{
return
(
stringOps::expand
(
"$FOAM_CASE/dynamicCode/platforms/$WM_OPTIONS/lib/lib"
)
+ codeName_ + ".so"
);
}
Foam::string Foam::dynamicCode::libTarget() const
{
return "LIB = $(PWD)/../platforms/$(WM_OPTIONS)/lib/lib" + codeName_;
}
bool Foam::dynamicCode::copyFilesContents() const
{
if (!allowSystemOperations)
{
FatalErrorIn
(
"codeStreamTools::copyFilesContents(const fileName&) const"
"dynamicCode::copyFilesContents(const fileName&) const"
) << "Loading a shared library using case-supplied code is not"
<< " enabled by default" << nl
<< "because of security issues. If you trust the code you can"
......@@ -240,106 +307,118 @@ bool Foam::codeStreamTools::copyFilesContents(const fileName& dir) const
<< exit(FatalError);
}
List<fileName> resolvedFiles = resolveTemplates(filterFiles_);
// Create dir
const fileName outputDir = this->codePath();
// Create dir
mkDir(dir);
mkDir(outputDir);
// Info<< "set mapping typeName=" << name_ << endl;
// Copy any template files
forAll(copyFiles_, i)
// Copy/filter files
forAll(resolvedFiles, fileI)
{
const fileName sourceFile(fileName(copyFiles_[i].file()).expand());
const fileName destFile(dir/sourceFile.name());
const fileName& srcFile = resolvedFiles[fileI];
const fileName dstFile(outputDir/srcFile.name());
IFstream is(sourceFile);
IFstream is(srcFile);
//Info<< "Reading from " << is.name() << endl;
if (!is.good())
{
FatalErrorIn
(
"codeStreamTools::copyFilesContents(const fileName&)"
"dynamicCode::copyFilesContents(const fileName&)"
" const"
) << "Failed opening " << sourceFile << exit(FatalError);
) << "Failed opening " << srcFile