ENH: new encapsulation: dynamicCode, dynamicCodeContext

- improve loading/unloading characteristics for codedFixedValue
  but still needs work
This commit is contained in:
Mark Olesen
2011-02-24 17:14:59 +01:00
parent 51399bbbd1
commit 74bc658fe9
11 changed files with 946 additions and 731 deletions

View File

@ -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

View File

@ -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
// << 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);
}

View File

@ -1,369 +0,0 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2011 OpenCFD Ltd.
\\/ M anipulation |
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
OpenFOAM 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
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM; if not, write to the Free Software Foundation,
Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
\*---------------------------------------------------------------------------*/
#include "codeStreamTools.H"
#include "stringOps.H"
#include "IFstream.H"
#include "OFstream.H"
#include "OSspecific.H"
#include "dictionary.H"
#include "dlLibraryTable.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
int Foam::codeStreamTools::allowSystemOperations
(
Foam::debug::infoSwitch("allowSystemOperations", 0)
);
const Foam::word Foam::codeStreamTools::codeTemplateEnvName
= "FOAM_CODE_TEMPLATES";
const Foam::fileName Foam::codeStreamTools::codeTemplateDirName
= "codeTemplates/dynamicCode";
// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
void Foam::codeStreamTools::checkSecurity
(
const char* title,
const dictionary& context
)
{
if (isAdministrator())
{
FatalIOErrorIn
(
title,
context
) << "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 "
<< "using dlopen)"
<< exit(FatalIOError);
}
}
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
(
ISstream& is,
OSstream& os,
const HashTable<string>& mapping
) const
{
if (!is.good())
{
FatalErrorIn
(
"codeStreamTools::copyAndExpand()"
" const"
) << "Failed opening for reading " << is.name()
<< exit(FatalError);
}
if (!os.good())
{
FatalErrorIn
(
"codeStreamTools::copyAndExpand()"
" const"
) << "Failed writing " << os.name()
<< exit(FatalError);
}
// Copy file while rewriting $VARS and ${VARS}
string line;
do
{
is.getLine(line);
// expand according to mapping
// expanding according to env variables might cause too many
// surprises
stringOps::inplaceExpand(line, mapping);
os << line.c_str() << nl;
}
while (is.good());
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::codeStreamTools::codeStreamTools()
{}
Foam::codeStreamTools::codeStreamTools
(
const word& name,
const dictionary& dict
)
:
name_(name)
{
read(dict);
}
Foam::codeStreamTools::codeStreamTools
(
const word& name,
const List<fileAndVars>& copyFiles,
const List<fileAndContent>& filesContents
)
:
name_(name),
copyFiles_(copyFiles),
filesContents_(filesContents)
{}
Foam::codeStreamTools::codeStreamTools(const codeStreamTools& tools)
:
name_(tools.name_),
copyFiles_(tools.copyFiles_),
filesContents_(tools.filesContents_)
{}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
bool Foam::codeStreamTools::copyFilesContents(const fileName& dir) const
{
if (!allowSystemOperations)
{
FatalErrorIn
(
"codeStreamTools::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"
<< " enable this" << nl
<< "facility be adding to the InfoSwitches setting in the system"
<< " controlDict:" << nl << nl
<< " allowSystemOperations 1" << nl << nl
<< "The system controlDict is either" << nl << nl
<< " ~/.OpenFOAM/$WM_PROJECT_VERSION/controlDict" << nl << nl
<< "or" << nl << nl
<< " $WM_PROJECT_DIR/etc/controlDict" << nl
<< endl
<< exit(FatalError);
}
// Create dir
mkDir(dir);
// Info<< "set mapping typeName=" << name_ << endl;
// Copy any template files
forAll(copyFiles_, i)
{
const fileName sourceFile(fileName(copyFiles_[i].file()).expand());
const fileName destFile(dir/sourceFile.name());
IFstream is(sourceFile);
//Info<< "Reading from " << is.name() << endl;
if (!is.good())
{
FatalErrorIn
(
"codeStreamTools::copyFilesContents(const fileName&)"
" const"
) << "Failed opening " << sourceFile << exit(FatalError);
}
OFstream os(destFile);
//Info<< "Writing to " << destFile.name() << endl;
if (!os.good())
{
FatalErrorIn
(
"codeStreamTools::copyFilesContents(const fileName&)"
" const"
) << "Failed writing " << destFile << exit(FatalError);
}
// variables mapping
HashTable<string> mapping(copyFiles_[i]);
mapping.set("typeName", name_);
// provide a zero digest if not otherwise specified
if (!mapping.found("SHA1sum"))
{
mapping.insert("SHA1sum", SHA1Digest().str());
}
copyAndExpand(is, os, mapping);
}
// Files that are always written:
forAll(filesContents_, i)
{
const fileName dstFile
(
fileName(dir/filesContents_[i].first()).expand()
);
mkDir(dstFile.path());
OFstream os(dstFile);
//Info<< "Writing to " << filesContents_[i].first() << endl;
if (!os.good())
{
FatalErrorIn
(
"codeStreamTools::copyFilesContents()"
" const"
) << "Failed writing " << dstFile << exit(FatalError);
}
os << filesContents_[i].second().c_str() << endl;
}
return true;
}
bool Foam::codeStreamTools::writeDigest
(
const fileName& dir,
const SHA1Digest& sha1
)
{
OFstream os(dir/"SHA1Digest");
os << sha1;
return os.good();
}
Foam::SHA1Digest Foam::codeStreamTools::readDigest(const fileName& dir)
{
IFstream is(dir/"SHA1Digest");
return SHA1Digest(is);
}
bool Foam::codeStreamTools::upToDate
(
const fileName& dir,
const SHA1Digest& sha1
)
{
if (!exists(dir/"SHA1Digest") || readDigest(dir) != sha1)
{
writeDigest(dir, sha1);
return false;
}
else
{
return true;
}
}
bool Foam::codeStreamTools::read(const dictionary& dict)
{
dict.lookup("copyFiles") >> copyFiles_;
dict.lookup("filesContents") >> filesContents_;
return true;
}
void Foam::codeStreamTools::writeDict(Ostream& os) const
{
os.writeKeyword("copyFiles") << copyFiles_ << token::END_STATEMENT << nl;
os.writeKeyword("filesContents") << filesContents_ << token::END_STATEMENT
<< nl;
}
// ************************************************************************* //

View File

@ -0,0 +1,479 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2011 OpenCFD Ltd.
\\/ M anipulation |
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
OpenFOAM 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
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM; if not, write to the Free Software Foundation,
Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
\*---------------------------------------------------------------------------*/
#include "dynamicCode.H"
#include "dynamicCodeContext.H"
#include "stringOps.H"
#include "IFstream.H"
#include "OFstream.H"
#include "OSspecific.H"
#include "dictionary.H"
#include "dlLibraryTable.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
int Foam::dynamicCode::allowSystemOperations
(
Foam::debug::infoSwitch("allowSystemOperations", 0)
);
const Foam::word Foam::dynamicCode::codeTemplateEnvName
= "FOAM_CODE_TEMPLATES";
const Foam::fileName Foam::dynamicCode::codeTemplateDirName
= "codeTemplates/dynamicCode";
// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
void Foam::dynamicCode::checkSecurity
(
const char* title,
const dictionary& dict
)
{
if (isAdministrator())
{
FatalIOErrorIn
(
title,
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 "
<< "using dlopen)"
<< exit(FatalIOError);
}
}
// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
void Foam::dynamicCode::copyAndFilter
(
ISstream& is,
OSstream& os,
const HashTable<string>& mapping
) const
{
if (!is.good())
{
FatalErrorIn
(
"dynamicCode::copyAndFilter()"
" const"
) << "Failed opening for reading " << is.name()
<< exit(FatalError);
}
if (!os.good())
{
FatalErrorIn
(
"dynamicCode::copyAndFilter()"
" const"
) << "Failed writing " << os.name()
<< exit(FatalError);
}
// Copy file while rewriting $VARS and ${VARS}
string line;
do
{
is.getLine(line);
// expand according to mapping
// expanding according to env variables might cause too many
// surprises
stringOps::inplaceExpand(line, mapping);
os << line.c_str() << nl;
}
while (is.good());
}
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::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());
}
void Foam::dynamicCode::addCreateFile
(
const fileName& name,
const string& contents
)
{
createFiles_.append(fileAndContent(name, contents));
}
void Foam::dynamicCode::addFilterFile
(
const fileName& name
)
{
filterFiles_.append(name);
}
void Foam::dynamicCode::setFilterContext
(
const dynamicCodeContext& context
)
{
filterVars_.set("code", context.code());
filterVars_.set("codeInclude", context.include());
filterVars_.set("SHA1sum", context.sha1().str());
}
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_);
}
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
(
"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"
<< " enable this" << nl
<< "facility be adding to the InfoSwitches setting in the system"
<< " controlDict:" << nl << nl
<< " allowSystemOperations 1" << nl << nl
<< "The system controlDict is either" << nl << nl
<< " ~/.OpenFOAM/$WM_PROJECT_VERSION/controlDict" << nl << nl
<< "or" << nl << nl
<< " $WM_PROJECT_DIR/etc/controlDict" << nl
<< endl
<< exit(FatalError);
}
List<fileName> resolvedFiles = resolveTemplates(filterFiles_);
// Create dir
const fileName outputDir = this->codePath();
// Create dir
mkDir(outputDir);
// Copy/filter files
forAll(resolvedFiles, fileI)
{
const fileName& srcFile = resolvedFiles[fileI];
const fileName dstFile(outputDir/srcFile.name());
IFstream is(srcFile);
//Info<< "Reading from " << is.name() << endl;
if (!is.good())
{
FatalErrorIn
(
"dynamicCode::copyFilesContents(const fileName&)"
" const"
) << "Failed opening " << srcFile
<< exit(FatalError);
}
OFstream os(dstFile);
//Info<< "Writing to " << dstFile.name() << endl;
if (!os.good())
{
FatalErrorIn
(
"dynamicCode::copyFilesContents(const fileName&)"
" const"
) << "Failed writing " << dstFile
<< exit(FatalError);
}
// variables mapping
copyAndFilter(is, os, filterVars_);
}
// Create files:
forAll(createFiles_, fileI)
{
const fileName dstFile
(
outputDir/stringOps::expand(createFiles_[fileI].first())
);
mkDir(dstFile.path());
OFstream os(dstFile);
//Info<< "Writing to " << createFiles_[fileI].first() << endl;
if (!os.good())
{
FatalErrorIn
(
"dynamicCode::copyFilesContents()"
" const"
) << "Failed writing " << dstFile
<< exit(FatalError);
}
os << createFiles_[fileI].second().c_str() << endl;
}
return true;
}
bool Foam::dynamicCode::wmakeLibso() const
{
const Foam::string wmakeCmd("wmake libso " + this->codePath());
Info<< "Invoking " << wmakeCmd << endl;
if (Foam::system(wmakeCmd))
{
return false;
}
else
{
return true;
}
}
bool Foam::dynamicCode::writeDigest
(
const fileName& dirName,
const SHA1Digest& sha1
) const
{
mkDir(dirName);
OFstream os(dirName/"SHA1Digest");
os << sha1;
return os.good();
}
Foam::SHA1Digest Foam::dynamicCode::readDigest(const fileName& dirName) const
{
IFstream is(dirName/"SHA1Digest");
return SHA1Digest(is);
}
bool Foam::dynamicCode::upToDate(const SHA1Digest& sha1) const
{
const fileName dirName = this->codePath();
if (!exists(dirName/"SHA1Digest") || readDigest(dirName) != sha1)
{
writeDigest(dirName, sha1);
return false;
}
else
{
return true;
}
}
bool Foam::dynamicCode::upToDate(const dynamicCodeContext& context) const
{
return upToDate(context.sha1());
}
// bool Foam::dynamicCode::openLibrary() const
// {
// return dlLibraryTable::openLibrary(this->libPath(), false);
// }
//
//
// bool Foam::dynamicCode::closeLibrary() const
// {
// return dlLibraryTable::closeLibrary(this->libPath(), false);
// }
//
//
// void* Foam::dynamicCode::findLibrary() const
// {
// return dlLibraryTable::findLibrary(this->libPath());
// }
// bool Foam::dynamicCode::read(const dictionary& dict)
// {
// dict.lookup("createFiles") >> createFiles_;
// dict.lookup("filterFiles") >> filterFiles_;
// dict.lookup("filterVariables") >> filterVariables_;
//
// return true;
// }
//
//
// void Foam::dynamicCode::writeDict(Ostream& os) const
// {
// os.writeKeyword("createFiles") << createFiles_
// << token::END_STATEMENT << nl;
//
// os.writeKeyword("filterFiles") << filterFiles_
// << token::END_STATEMENT << nl;
//
// os.writeKeyword("filterVariables") << filterVariables_
// << token::END_STATEMENT << nl;
// }
// ************************************************************************* //

View File

@ -23,23 +23,24 @@ License
Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Class
Foam::codeStreamTools
Foam::dynamicCode
Description
Base for all things on-the-fly from dictionary
Tools for handling dynamic code compilation
SourceFiles
codeStreamTools.C
dynamicCode.C
\*---------------------------------------------------------------------------*/
#ifndef codeStreamTools_H
#define codeStreamTools_H
#ifndef dynamicCode_H
#define dynamicCode_H
#include "Tuple2.H"
#include "Pair.H"
#include "SHA1Digest.H"
#include "HashTable.H"
#include "DynamicList.H"
#include "dlLibraryTable.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@ -47,66 +48,58 @@ namespace Foam
{
// Forward declaration of classes
class dynamicCodeContext;
class ISstream;
class OSstream;
/*---------------------------------------------------------------------------*\
Class codeStreamTools Declaration
Class dynamicCode Declaration
\*---------------------------------------------------------------------------*/
class codeStreamTools
class dynamicCode
{
public:
typedef Tuple2<fileName, string> fileAndContent;
//- Helper class for managing file and variables
class fileAndVars
:
public HashTable<string>
{
// Private data
fileName file_;
public:
//- Construct null
fileAndVars()
{}
//- Return the file name
const fileName& file() const
{
return file_;
}
//- Return the file name
fileName& file()
{
return file_;
}
};
private:
// Private data
//- Name for underlying set
word name_;
//- Name for code
word codeName_;
//- Files to copy
List<codeStreamTools::fileAndVars> copyFiles_;
//- Name for code subdirectory
mutable word codeDirName_;
//- Variables to use during filtering
HashTable<string> filterVars_;
//- Direct contents for files
List<fileAndContent> filesContents_;
DynamicList<fileAndContent> createFiles_;
//- Files to copy and filter
DynamicList<fileName> filterFiles_;
protected:
void copyAndExpand
void copyAndFilter
(
ISstream&,
OSstream&,
const HashTable<string>& mapping
) const;
//- Resolve code-templates via the codeTemplateEnvName
// alternatively in the codeTemplateDirName via Foam::findEtcFile
static List<fileName> resolveTemplates
(
const UList<fileName>& names
);
bool writeDigest(const fileName& dir, const SHA1Digest& sha1) const;
SHA1Digest readDigest(const fileName& dir) const;
public:
// Static data members
@ -119,87 +112,94 @@ public:
// Used when locating the codeTemplateName via Foam::findEtcFile
static const fileName codeTemplateDirName;
static int allowSystemOperations;
// Constructors
//- Construct null
codeStreamTools();
//- Construct from dictionary
codeStreamTools(const word& name, const dictionary& dict);
//- Copy from components
codeStreamTools
(
const word& name,
const List<fileAndVars>&,
const List<fileAndContent>&
);
//- Construct copy
codeStreamTools(const codeStreamTools&);
// Member functions
// Static Member functions
//- Check security for creating dynamic code
static void checkSecurity
(
const char* title,
const dictionary& context
const dictionary& dict
);
// Constructors
//- Construct for a specified code name
dynamicCode(const word& codeName);
//- Construct for a specified code name and directory name
dynamicCode(const word& codeName, const word& dirName);
// Member functions
//- Return the code-name
const word& codeName() const
{
return codeName_;
}
//- Return the code-dirname
const word& codeDirName() const
{
return codeDirName_;
}
//- Clear variables and files
void clear();
//- Add a file to create with its contents. Will not be filtered
void addCreateFile(const fileName& name, const string& contents);
//- Add a file template name, which will be found and filtered
void addFilterFile(const fileName& name);
//- Define filter variables for code, codeInclude, SHA1sum
void setFilterContext(const dynamicCodeContext&);
//- Define a filter variable
void setFilterVariable(const word& key, const string& value);
//- Local path for specified code name
// Expanded from \$FOAM_CASE/codeStream
static fileName codePath(const word& subDirName);
// Expanded from \$FOAM_CASE/dynamicCode/codeDirName
fileName codePath() const;
//- Local library path for specified code name
// Expanded from \$FOAM_CASE/platforms/\$WM_OPTIONS/lib
static fileName libPath(const word& codeName);
// Expanded from \$FOAM_CASE/dynamicCode/platforms/\$WM_OPTIONS/lib
fileName libPath() const;
//- The library target path for Make/files
static string libTarget(const word& codeName);
string libTarget() const;
//- Find a code-template via the codeTemplateEnvName
// alternatively in the codeTemplateDirName via Foam::findEtcFile
static fileName findTemplate(const word& templateName);
//- Verify if the copied code is up-to-date
bool upToDate(const dynamicCodeContext& context) const;
//- List searched locations in a format suitable for display an error
static string searchedLocations();
//- Verify if the copied code is up-to-date
bool upToDate(const SHA1Digest& sha1) const;
//- Copy/create files prior to compilation
bool copyFilesContents() const;
//- Compile a libso
bool wmakeLibso() const;
const word& name() const
{
return name_;
}
// //- Open the libPath() library
// bool openLibrary() const;
//
// //- Close the libPath() library
// bool closeLibrary() const;
//
// //- Find the handle of the libPath() library
// void* findLibrary() const;
const List<fileAndVars>& copyFiles() const
{
return copyFiles_;
}
const List<Tuple2<fileName, string> >& filesContents() const
{
return filesContents_;
}
bool copyFilesContents(const fileName& dir) const;
static void* findLibrary(const fileName& libPath);
static bool writeDigest(const fileName& dir, const SHA1Digest& sha1);
static SHA1Digest readDigest(const fileName& dir);
static bool upToDate(const fileName& dir, const SHA1Digest& sha1);
bool read(const dictionary&);
void writeDict(Ostream&) const;
// bool read(const dictionary&);
// void writeDict(Ostream&) const;
};

View File

@ -0,0 +1,69 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2011 OpenCFD Ltd.
\\/ M anipulation |
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
OpenFOAM 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
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM; if not, write to the Free Software Foundation,
Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
\*---------------------------------------------------------------------------*/
#include "dynamicCodeContext.H"
#include "stringOps.H"
#include "OSHA1stream.H"
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::dynamicCodeContext::dynamicCodeContext(const dictionary& dict)
:
dict_(dict),
code_(stringOps::trim(dict["code"])),
include_(),
options_()
{
// expand dictionary entries
stringOps::inplaceExpand(code_, dict);
// note: removes any leading/trailing whitespace
// - necessary for compilation options, convenient for includes
// and body.
// optional
if (dict.found("codeInclude"))
{
include_ = stringOps::trim(dict["codeInclude"]);
stringOps::inplaceExpand(include_, dict);
}
// optional
if (dict.found("codeOptions"))
{
options_ = stringOps::trim(dict["codeOptions"]);
stringOps::inplaceExpand(options_, dict);
}
// calculate SHA1 digest from include, options, code
OSHA1stream os;
os << include_ << options_ << code_;
sha1_ = os.digest();
}
// ************************************************************************* //

View File

@ -0,0 +1,120 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2011 OpenCFD Ltd.
\\/ M anipulation |
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
OpenFOAM 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
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM; if not, write to the Free Software Foundation,
Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Class
Foam::dynamicCodeContext
Description
Encapsulation of dynamic code dictionaries
SourceFiles
dynamicCodeContext.C
\*---------------------------------------------------------------------------*/
#ifndef dynamicCodeContext_H
#define dynamicCodeContext_H
#include "dictionary.H"
#include "SHA1Digest.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
/*---------------------------------------------------------------------------*\
Class dynamicCodeContext Declaration
\*---------------------------------------------------------------------------*/
class dynamicCodeContext
{
// Private data
//- The parent dictionary context
const dictionary& dict_;
//- Mandatory "code" entry
string code_;
//- Optional "codeInclude" entry
string include_;
//- Optional "codeOptions" entry
string options_;
//- Calculated SHA1Digest
SHA1Digest sha1_;
public:
// Constructors
//- Construct from a dictionary
dynamicCodeContext(const dictionary&);
// Member functions
//- Return the parent dictionary context
const dictionary& dict() const
{
return dict_;
}
//- Return the code-includes
const string& include() const
{
return include_;
}
//- Return the code-options
const string& options() const
{
return options_;
}
//- Return the code
const string& code() const
{
return code_;
}
//- Return SHA1 digest calculated from include, options, code
const SHA1Digest& sha1() const
{
return sha1_;
}
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -32,7 +32,7 @@ License
#include "JobInfo.H"
#include "labelList.H"
#include "regIOobject.H"
#include "codeStreamTools.H"
#include "dynamicCode.H"
#include <cctype>
@ -790,7 +790,7 @@ Foam::argList::argList
<< endl;
Info<< "allowSystemOperations : ";
if (codeStreamTools::allowSystemOperations)
if (dynamicCode::allowSystemOperations)
{
Info<< "Allowing user-supplied system call operations" << endl;
}

View File

@ -33,8 +33,8 @@ License
#include "IFstream.H"
#include "OFstream.H"
#include "SHA1Digest.H"
#include "OSHA1stream.H"
#include "codeStreamTools.H"
#include "dynamicCode.H"
#include "dynamicCodeContext.H"
#include "codeProperties.H"
#include "stringOps.H"
@ -78,113 +78,52 @@ Foam::codedFixedValueFvPatchScalarField::dict() const
}
void Foam::codedFixedValueFvPatchScalarField::writeLibrary
void Foam::codedFixedValueFvPatchScalarField::createLibrary
(
const fileName& codePath,
const fileName& libPath,
const dictionary& dict
dynamicCode& dynCode,
const dynamicCodeContext& context
)
{
// Write files for new library
if (!Pstream::master())
if (Pstream::master() && !dynCode.upToDate(context))
{
return;
}
Info<< "Creating new library in " << dynCode.libPath() << endl;
dynCode.clear();
// "codeInclude" is optional
string codeInclude;
if (dict.found("codeInclude"))
{
codeInclude = stringOps::trim(dict["codeInclude"]);
stringOps::inplaceExpand(codeInclude, dict);
}
// filter with this context
dynCode.setFilterContext(context);
// "codeOptions" is optional
string codeOptions;
if (dict.found("codeOptions"))
{
codeOptions = stringOps::trim(dict["codeOptions"]);
stringOps::inplaceExpand(codeOptions, dict);
}
// filter C/H template
dynCode.addFilterFile(codeTemplateC);
dynCode.addFilterFile(codeTemplateH);
// "code" is mandatory
string code = stringOps::trim(dict["code"]);
stringOps::inplaceExpand(code, dict);
// Create SHA1 digest from the contents
SHA1Digest sha;
{
OSHA1stream os;
os << codeInclude << codeOptions << code;
sha = os.digest();
}
// Info<<"old SHA1: " << sha1_ << nl
// <<"new SHA1: " << sha << endl;
// only use side-effect of writing SHA1Digest for now
(void) codeStreamTools::upToDate(codePath, sha);
// TODO: compile on-demand
if (true)
{
Info<< "Creating new library in " << libPath << endl;
const fileName fileCsrc(codeStreamTools::findTemplate(codeTemplateC));
const fileName fileHsrc(codeStreamTools::findTemplate(codeTemplateH));
// not found!
if (fileCsrc.empty() || fileHsrc.empty())
{
FatalIOErrorIn
// Make/files
dynCode.addCreateFile
(
"codedFixedValueFvPatchScalarField::writeLibrary(..)",
dict
) << "Could not find one or both code templates: "
<< codeTemplateC << ", " << codeTemplateH << nl
<< codeStreamTools::searchedLocations()
<< exit(FatalIOError);
}
List<codeStreamTools::fileAndVars> copyFiles(2);
copyFiles[0].file() = fileCsrc;
copyFiles[0].set("codeInclude", codeInclude);
copyFiles[0].set("code", code);
copyFiles[0].set("SHA1sum", sha.str());
copyFiles[1].file() = fileHsrc;
List<codeStreamTools::fileAndContent> filesContents(2);
// Write Make/files
filesContents[0].first() = "Make/files";
filesContents[0].second() =
"Make/files",
codeTemplateC + "\n\n"
+ codeStreamTools::libTarget(redirectType_);
+ dynCode.libTarget()
);
// Write Make/options
filesContents[1].first() = "Make/options";
filesContents[1].second() =
// Make/options
dynCode.addCreateFile
(
"Make/options",
"EXE_INC = -g \\\n"
"-I$(LIB_SRC)/finiteVolume/lnInclude\\\n"
+ codeOptions
+ "\n\nLIB_LIBS = ";
+ context.options()
+ "\n\nLIB_LIBS = "
);
codeStreamTools writer(redirectType_, copyFiles, filesContents);
if (!writer.copyFilesContents(codePath))
if (!dynCode.copyFilesContents())
{
FatalIOErrorIn
(
"codedFixedValueFvPatchScalarField::writeLibrary(..)",
dict
context.dict()
) << "Failed writing " << nl
<< copyFiles << nl
<< filesContents
// << copyFiles << nl
// << filesContents
<< exit(FatalIOError);
}
}
@ -193,98 +132,113 @@ void Foam::codedFixedValueFvPatchScalarField::writeLibrary
void Foam::codedFixedValueFvPatchScalarField::updateLibrary()
{
codeStreamTools::checkSecurity
dynamicCode::checkSecurity
(
"codedFixedValueFvPatchScalarField::updateLibrary()",
dict_
);
// write code into redirectType_ subdir
const fileName codePath = codeStreamTools::codePath(redirectType_);
// use in-line or via codeProperties
const bool useInlineDict = dict_.found("code");
// const fileName oldLibPath = codeStreamTools::libPath
// (
// redirectType_ + "_" + sha1_
// );
// write library into platforms/$WM_OPTIONS/lib subdir
const fileName libPath = codeStreamTools::libPath(redirectType_);
// determine code context (code, codeInclude, codeOptions)
dynamicCodeContext context
(
useInlineDict
? dict_
: this->dict().subDict(redirectType_)
);
//Info<< "codePath:" << codePath << nl
// << "libPath:" << libPath << endl;
// NOTE: probably don't need codeProperties anymore
// since we use the sha1 directly
if (!useInlineDict)
{
this->dict().setUnmodified();
}
// write code into redirectType_ subdir as well
dynamicCode dynCode(redirectType_);
// The version function name - based on the SHA1
const string checkFuncName
(
dynCode.codeName() + "_" + context.sha1().str()
);
const fileName libPath = dynCode.libPath();
void* lib = dlLibraryTable::findLibrary(libPath);
// TODO:
// calculate old/new SHA1 for code
// check if the correct library version was already loaded.
// Find the library handle.
//
// string signatureName(redirectType_ + "_" + sha().str());
// void (*signatureFunction)();
// signatureFunction = reinterpret_cast<void(*)()>(dlSym(lib, signatureName));
if (dict_.found("code"))
// Load library if not already loaded
bool reusing = false;
if (!lib && dlLibraryTable::open(libPath, false))
{
if (!lib)
{
writeLibrary(codePath, libPath, dict_);
lib = dlLibraryTable::findLibrary(libPath);
reusing = true;
}
}
else
{
const codeProperties& onTheFlyDict = dict();
if (onTheFlyDict.modified())
{
onTheFlyDict.setUnmodified();
// Remove instantiation of fvPatchField provided by library
redirectPatchFieldPtr_.clear();
// Unload library
// library may have loaded, the version may not be correct
bool waiting = false;
if (lib)
{
if (!dlLibraryTable::close(libPath))
// Unload library if needed
if (!dlSym(lib, checkFuncName))
{
reusing = false;
waiting = true;
if (!dlLibraryTable::close(libPath, false))
{
FatalIOErrorIn
(
"codedFixedValueFvPatchScalarField::updateLibrary(..)",
onTheFlyDict
) << "Failed unloading library " << libPath
context.dict()
) << "Failed unloading library "
<< libPath
<< exit(FatalIOError);
}
lib = NULL;
lib = 0;
}
const dictionary& codeDict = onTheFlyDict.subDict(redirectType_);
writeLibrary(codePath, libPath, codeDict);
}
// unload from all processes
reduce(waiting, orOp<bool>());
}
// create library
if (!lib)
{
if (useInlineDict)
{
createLibrary(dynCode, context);
}
else
{
// Remove instantiation of fvPatchField provided by library
redirectPatchFieldPtr_.clear();
createLibrary(dynCode, context);
}
if (Pstream::master())
{
const Foam::string wmakeCmd("wmake libso " + codePath);
Info<< "Invoking " << wmakeCmd << endl;
if (Foam::system(wmakeCmd))
if (!dynCode.wmakeLibso())
{
FatalIOErrorIn
(
"codedFixedValueFvPatchScalarField::updateLibrary()",
dict_
) << "Failed " << wmakeCmd
) << "Failed wmake " << libPath
<< exit(FatalIOError);
}
}
// all processes must wait for compile
bool dummy = true;
reduce(dummy, orOp<bool>());
waiting = true;
reduce(waiting, orOp<bool>());
if (!dlLibraryTable::open(libPath))
if (!dlLibraryTable::open(libPath, false))
{
FatalIOErrorIn
(
@ -293,6 +247,29 @@ void Foam::codedFixedValueFvPatchScalarField::updateLibrary()
) << "Failed loading library " << libPath
<< exit(FatalIOError);
}
// paranoid - check that signature function is really there
lib = dlLibraryTable::findLibrary(libPath);
if (lib)
{
if (!dlSym(lib, checkFuncName))
{
FatalIOErrorIn
(
"codedFixedValueFvPatchScalarField::updateLibrary(..)",
dict_
) << "Library loaded - but wrong version!"
<< libPath
<< exit(FatalIOError);
}
lib = 0;
}
}
else if (reusing)
{
Info<< "Reusing library in " << libPath << nl
<< " with " << context.sha1().str() << nl;
}
}

View File

@ -29,8 +29,6 @@ Description
Constructs on-the-fly a new boundary condition (derived from
fixedValueFvPatchScalarField) which is then used to evaluate.
See also codeStream.
Example:
\verbatim
movingWall
@ -71,7 +69,7 @@ Description
\endverbatim
SeeAlso
Foam::codeStreamTools for constant paths used
Foam::dynamicCode and Foam::functionEntries::codeStream
SourceFiles
codedFixedValueFvPatchScalarField.C
@ -82,14 +80,16 @@ SourceFiles
#define codedFixedValueFvPatchScalarField_H
#include "fixedValueFvPatchFields.H"
#include "SHA1Digest.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
// Forward declaration of classes
class codeProperties;
class dynamicCode;
class dynamicCodeContext;
/*---------------------------------------------------------------------------*\
Class codedFixedValueFvPatchScalarField Declaration
@ -104,10 +104,6 @@ class codedFixedValueFvPatchScalarField
//- Dictionary contents for the boundary condition
mutable dictionary dict_;
//- SHA1Digest of the Dictionary contents
// Currently unused, but useful for reloading?
mutable SHA1Digest sha1_;
const word redirectType_;
mutable autoPtr<fvPatchScalarField> redirectPatchFieldPtr_;
@ -117,12 +113,7 @@ class codedFixedValueFvPatchScalarField
const codeProperties& dict() const;
void writeLibrary
(
const fileName& dir,
const fileName& libPath,
const dictionary& dict
);
void createLibrary(dynamicCode&, const dynamicCodeContext& dict);
void updateLibrary();

View File

@ -25,7 +25,7 @@ License
#include "systemCall.H"
#include "Time.H"
#include "codeStreamTools.H"
#include "dynamicCode.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
@ -71,7 +71,7 @@ void Foam::systemCall::read(const dictionary& dict)
<< "no executeCalls, endCalls or writeCalls defined."
<< endl;
}
else if (!codeStreamTools::allowSystemOperations)
else if (!dynamicCode::allowSystemOperations)
{
FatalErrorIn
(