Loading...
Searching...
No Matches
dynamicCode.C
Go to the documentation of this file.
1/*---------------------------------------------------------------------------*\
2 ========= |
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4 \\ / O peration |
5 \\ / A nd | www.openfoam.com
6 \\/ M anipulation |
7-------------------------------------------------------------------------------
8 Copyright (C) 2011-2016 OpenFOAM Foundation
9 Copyright (C) 2016-2023 OpenCFD Ltd.
10-------------------------------------------------------------------------------
11License
12 This file is part of OpenFOAM.
13
14 OpenFOAM is free software: you can redistribute it and/or modify it
15 under the terms of the GNU General Public License as published by
16 the Free Software Foundation, either version 3 of the License, or
17 (at your option) any later version.
18
19 OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26
27\*---------------------------------------------------------------------------*/
28
29#include "dynamicCode.H"
30#include "dynamicCodeContext.H"
31#include "dlLibraryTable.H"
32#include "argList.H"
33#include "stringOps.H"
34#include "Fstream.H"
35#include "IOobject.H"
36#include "IOstreams.H"
37#include "OSspecific.H"
38#include "etcFiles.H"
39#include "dictionary.H"
40#include "foamVersion.H"
41
42// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
43
45(
46 Foam::debug::infoSwitch("allowSystemOperations", 0)
47);
48
49
51 = "FOAM_CODE_TEMPLATES";
52
54 = "codeTemplates/dynamicCode";
55
56const char* const Foam::dynamicCode::targetLibDir
57 = "LIB = $(PWD)/../platforms/$(WM_OPTIONS)/lib";
59const char* const Foam::dynamicCode::topDirName
60 = "dynamicCode";
61
62
63// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
64
66(
67 const char* title,
68 const dictionary& dict
69)
70{
71 if (isAdministrator())
72 {
74 << "This code should not be executed by someone"
75 << " with administrator rights for security reasons." << nl
76 << "It generates a shared library which is loaded using dlopen"
77 << nl << endl
79 }
80
82 {
84 << "Loading shared libraries using case-supplied code may have"
85 << " been disabled" << nl
86 << "by default for security reasons." << nl
87 << "If you trust the code, you may enable this by adding"
88 << nl << nl
89 << " allowSystemOperations 1" << nl << nl
90 << "to the InfoSwitches setting in the system controlDict." << nl
91 << "The system controlDict is any of" << nl << nl
92 << " ~/.OpenFOAM/" << foamVersion::api << "/controlDict" << nl
93 << " ~/.OpenFOAM/controlDict" << nl
94 << " $WM_PROJECT_DIR/etc/controlDict" << nl << endl
96 }
97}
98
99
100// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
101
103(
104 ISstream& is,
105 OSstream& os,
106 const HashTable<string>& mapping
107)
108{
109 if (!is.good())
110 {
112 << "Failed opening for reading " << is.name()
113 << exit(FatalError);
114 }
115
116 if (!os.good())
117 {
119 << "Failed writing " << os.name()
120 << exit(FatalError);
121 }
122
123 // Copy file while rewriting $VARS and ${VARS}
124 string line;
125 do
126 {
127 is.getLine(line);
128
129 // Expand according to HashTable mapping, not the environment.
130 // Expanding according to env variables might cause too many
131 // surprises
133 os.writeQuoted(line, false) << nl;
134 }
135 while (is.good());
136}
137
138
140(
141 const UList<fileName>& templateNames,
142 DynamicList<fileName>& resolvedFiles,
143 DynamicList<fileName>& badFiles
144)
145{
146 // Try to get template from FOAM_CODE_TEMPLATES
147 const fileName templateDir(Foam::getEnv(codeTemplateEnvName));
148
149 bool allOkay = true;
150 for (const fileName& templateName : templateNames)
151 {
152 fileName file;
153 if (!templateDir.empty() && isDir(templateDir))
154 {
155 file = templateDir/templateName;
156 if (!isFile(file, false))
157 {
158 file.clear();
159 }
160 }
161
162 // Not found - fallback to <etc> expansion
163 if (file.empty())
164 {
165 file = findEtcFile(codeTemplateDirName/templateName);
166 }
167
168 if (file.empty())
169 {
170 badFiles.push_back(templateName);
171 allOkay = false;
172 }
173 else
174 {
175 resolvedFiles.push_back(file);
177 }
178
179 return allOkay;
180}
181
182
184{
185 const auto iter = filterVars_.cfind("SHA1sum");
186
187 if (iter.good())
188 {
189 os << "/* dynamicCode:\n * SHA1 = ";
190 os.writeQuoted(iter.val(), false) << "\n */\n";
191 }
192
193 return iter.good();
194}
195
196
198{
199 // Create Make/files
200 if (compileFiles_.empty())
201 {
202 return false;
203 }
204
205 const fileName dstFile(this->codePath()/"Make/files");
206
207 // Create dir
208 mkDir(dstFile.path());
209
210 OFstream os(dstFile);
211 //Debug: Info<< "Writing to " << dstFile << endl;
212 if (!os.good())
213 {
215 << "Failed writing " << dstFile
216 << exit(FatalError);
217 }
218
219 writeCommentSHA1(os);
220
221 // Write compile files
222 for (const fileName& file : compileFiles_)
223 {
224 os.writeQuoted(file, false) << nl;
225 }
226
227 os << nl
229 << "/lib" << codeName_.c_str() << nl;
230
231 return true;
232}
233
234
236{
237 // Create Make/options
238 if (compileFiles_.empty() || makeOptions_.empty())
239 {
240 return false;
241 }
242
243 const fileName dstFile(this->codePath()/"Make/options");
244
245 // Create dir
246 mkDir(dstFile.path());
247
248 OFstream os(dstFile);
249 //Debug: Info<< "Writing to " << dstFile << endl;
250 if (!os.good())
251 {
253 << "Failed writing " << dstFile
254 << exit(FatalError);
255 }
256
258 os.writeQuoted(makeOptions_, false) << nl;
259
260 return true;
261}
262
263
264bool Foam::dynamicCode::writeDigest(const SHA1Digest& sha1) const
265{
266 const fileName file = digestFile();
267 mkDir(file.path());
268
270 sha1.write(os, true) << nl;
271
272 return os.good();
273}
274
275
276bool Foam::dynamicCode::writeDigest(const std::string& sha1) const
277{
278 const fileName file = digestFile();
279 mkDir(file.path());
280
281 OFstream os(file);
282 os << '_';
283 os.writeQuoted(sha1, false) << nl;
285 return os.good();
286}
287
288
289// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
290
291Foam::dynamicCode::dynamicCode(const word& codeName, const word& codeDirName)
292:
293 codeRoot_(argList::envGlobalPath()/topDirName),
294 libSubDir_(stringOps::expand("platforms/${WM_OPTIONS}/lib")),
295 codeName_(codeName),
296 codeDirName_(codeDirName)
297{
298 if (codeDirName_.empty())
299 {
300 codeDirName_ = codeName_;
301 }
303 clear();
304}
305
306
307// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
310{
311 return topDirName/codeDirName_;
312}
313
316{
317 return codeRoot_/libSubDir_/dlLibraryTable::fullname(codeName_);
318}
319
322{
323 return codeRelPath()/libSubDir_/dlLibraryTable::fullname(codeName_);
324}
325
326
328{
329 compileFiles_.clear();
330 copyFiles_.clear();
331 createFiles_.clear();
332 filterVars_.clear();
333 filterVars_.set("typeName", codeName_);
334 filterVars_.set("SHA1sum", SHA1Digest().str());
335
336 // Default Make/options
337 makeOptions_ =
338 "EXE_INC = -g\n"
339 "\n\nLIB_LIBS = ";
340}
341
342
344(
345 const dynamicCodeContext& context
347{
348 clear();
349 setFilterContext(context);
350}
351
354{
355 compileFiles_.push_back(name);
356}
357
360{
361 copyFiles_.push_back(name);
362}
363
364
366(
367 const fileName& name,
368 const std::string& fileContents
369)
370{
371 createFiles_.emplace_back(name, fileContents);
372}
373
374
376(
377 const dynamicCodeContext& context
378)
379{
380 filterVars_.set("localCode", context.localCode());
381 filterVars_.set("code", context.code());
382 filterVars_.set("codeInclude", context.include());
383 filterVars_.set("SHA1sum", context.sha1().str());
384}
385
386
388(
389 const word& key,
390 const std::string& value
391)
392{
393 filterVars_.set(key, value);
394}
395
397void Foam::dynamicCode::setMakeOptions(const std::string& content)
398{
399 makeOptions_ = content;
400}
401
402
403bool Foam::dynamicCode::copyOrCreateFiles(const bool verbose) const
404{
405 if (verbose)
406 {
408 << "Creating new library in " << this->libRelPath() << endl;
409 }
410
411 const label nFiles = compileFiles_.size() + copyFiles_.size();
412
413 DynamicList<fileName> resolvedFiles(nFiles);
414 DynamicList<fileName> badFiles(nFiles);
415
416 // Resolve template, or add to bad-files
417 resolveTemplates(compileFiles_, resolvedFiles, badFiles);
418 resolveTemplates(copyFiles_, resolvedFiles, badFiles);
419
420 if (!badFiles.empty())
421 {
423 << "Could not find code template(s): "
424 << badFiles << nl
425 << "Under the $" << codeTemplateEnvName
426 << " directory or via the <etc>/"
427 << codeTemplateDirName << " expansion"
428 << exit(FatalError);
429 }
430
431
432
433 // Create dir
434 const fileName outputDir = this->codePath();
435
436 // Create dir
437 mkDir(outputDir);
438
439 // Copy/filter files
440 for (const fileName& srcFile : resolvedFiles)
441 {
442 const fileName dstFile(outputDir/srcFile.name());
443
444 IFstream is(srcFile);
445 //Debug: Info<< "Reading from " << is.name() << endl;
446 if (!is.good())
447 {
449 << "Failed opening " << srcFile
450 << exit(FatalError);
451 }
452
453 OFstream os(dstFile);
454 //Debug: Info<< "Writing to " << dstFile.name() << endl;
455 if (!os.good())
456 {
458 << "Failed writing " << dstFile
459 << exit(FatalError);
460 }
461
462 // Copy lines while expanding variables
463 copyAndFilter(is, os, filterVars_);
464 }
465
466
467 // Create files:
468 for (const auto& content : createFiles_)
469 {
470 const fileName dstFile(outputDir/stringOps::expand(content.first()));
471
472 mkDir(dstFile.path());
473 OFstream os(dstFile);
474 //Debug: Info<< "Writing to " << content.first() << endl;
475 if (!os.good())
476 {
478 << "Failed writing " << dstFile
479 << exit(FatalError);
480 }
481 os.writeQuoted(content.second(), false) << nl;
482 }
483
484
485 // Create Make/files + Make/options
486 createMakeFiles();
487 createMakeOptions();
489 writeDigest(filterVars_["SHA1sum"]);
490
491 return true;
492}
493
494
496{
497 stringList cmd({"wmake", "-s", "libso", this->codePath()});
498
499 // NOTE: could also resolve wmake command explicitly
500 // cmd[0] = stringOps::expand("$WM_PROJECT_DIR/wmake/wmake");
501
502 // This can take a bit longer, so report that we are starting wmake
503 // Even with details turned off, we want some feedback
504
505 OSstream& os = (Foam::infoDetailLevel > 0 ? Info : InfoErr);
506 os << "Invoking wmake libso " << this->codePath().c_str() << endl;
507
508 if (Foam::system(cmd) == 0)
509 {
510 return true;
511 }
512
513 return false;
514}
515
516
517bool Foam::dynamicCode::upToDate(const SHA1Digest& sha1) const
518{
519 const fileName file = digestFile();
520
521 if (!exists(file, false) || SHA1Digest(IFstream(file)()) != sha1)
522 {
523 return false;
524 }
525
526 return true;
527}
528
529
530bool Foam::dynamicCode::upToDate(const dynamicCodeContext& context) const
532 return upToDate(context.sha1());
533}
534
535
536// * * * * * * * * * * * * * * * Synchronisation * * * * * * * * * * * * * * //
537
539(
540 const fileName& file,
541 const dictionary& contextDict
542)
543{
544 const int debug = 0;
545
546 if (!UPstream::parRun())
547 {
548 return;
549 }
550
551 // If library has just been compiled on the master, the other nodes
552 // need to pick this library up through NFS, which likely has delays
553 // in it.
554
555 // We do this by just polling a few times using the
556 // fileModificationSkew.
557
558 off_t localSize = Foam::fileSize(file);
559 off_t masterSize = localSize;
560 Pstream::broadcast(masterSize);
561
562 for
563 (
564 label iter = 0;
565 (
568 );
569 ++iter
570 )
571 {
573 << "Processor " << UPstream::myProcNo()
574 << " masterSize:" << masterSize
575 << " localSize:" << localSize << endl;
576
577 if (localSize == masterSize)
578 {
579 return;
580 }
581 if (localSize > masterSize)
582 {
583 FatalIOErrorInFunction(contextDict)
584 << "Excessive size when reading (NFS mounted) library "
585 << nl << file << nl
586 << "on processor " << UPstream::myProcNo()
587 << " detected size " << localSize
588 << " whereas master size is " << masterSize
589 << " bytes." << nl
590 << "If your case is NFS mounted increase"
591 << " fileModificationSkew or maxFileModificationPolls;"
592 << nl << "If your case is not NFS mounted"
593 << " (so distributed) set fileModificationSkew"
594 << " to 0"
595 << exit(FatalIOError);
596 }
597 else
598 {
600 << "Local file " << file
601 << " not of same size (" << localSize
602 << ") as master ("
603 << masterSize << "). Waiting for "
605 << " seconds." << endl;
606
608
609 // Recheck local size
610 localSize = Foam::fileSize(file);
611 }
612 }
613
614 // Finished doing iterations. Do final check
615 if (localSize != masterSize)
616 {
617 FatalIOErrorInFunction(contextDict)
618 << "Cannot read (NFS mounted) library:" << nl
619 << file << nl
620 << "on processor " << UPstream::myProcNo()
621 << " detected size " << localSize
622 << " whereas master size is " << masterSize
623 << " bytes." << nl
624 << "If your case is NFS mounted increase"
625 << " fileModificationSkew or maxFileModificationPolls;" << nl
626 << "If your case is not NFS mounted"
627 << " (so distributed) set fileModificationSkew"
628 << " to 0"
629 << exit(FatalIOError);
630 }
631
633 << "Processor " << UPstream::myProcNo()
634 << " masterSize:" << masterSize
635 << " localSize:" << localSize
636 << " ... after waiting" << endl;
637}
638
639
640// ************************************************************************* //
Useful combination of include files which define Sin, Sout and Serr and the use of IO streams general...
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
A 1D vector of objects of type <T> that resizes itself as necessary to accept the new objects.
Definition DynamicList.H:68
void push_back(const T &val)
Copy append an element to the end of this list.
A HashTable similar to std::unordered_map.
Definition HashTable.H:124
Input from file stream as an ISstream, normally using std::ifstream for the actual input.
Definition IFstream.H:55
static float fileModificationSkew
Time skew (seconds) for file modification checks.
Definition IOobject.H:363
static int maxFileModificationPolls
Max number of times to poll for file modification changes.
Definition IOobject.H:368
bool good() const noexcept
True if next operation might succeed.
Definition IOstream.H:281
Generic input stream using a standard (STL) stream.
Definition ISstream.H:54
virtual const fileName & name() const override
The name of the input serial stream. (eg, the name of the Fstream file name).
Definition ISstream.H:147
ISstream & getLine(std::string &str, char delim='\n')
Raw, low-level getline (until delimiter) into a string.
Definition ISstreamI.H:69
virtual Ostream & writeQuoted(const char *str, std::streamsize len, const bool quoted=true) override
Write character/string content, with/without surrounding quotes.
Definition OBJstream.C:78
Output to file stream as an OSstream, normally using std::ofstream for the actual output.
Definition OFstream.H:75
virtual const fileName & name() const override
Read/write access to the name of the stream.
Definition OSstream.H:134
Generic output stream using a standard (STL) stream.
Definition OSstream.H:53
An Ostream is an abstract base class for all output systems (streams, files, token lists,...
Definition Ostream.H:59
The SHA1 message digest.
Definition SHA1Digest.H:58
Ostream & write(Ostream &os, const bool prefixed=false) const
Write (40-byte) text representation, optionally with '_' prefix.
Definition SHA1Digest.C:247
std::string str(const bool prefixed=false) const
The digest (40-byte) text representation, optionally with '_' prefix.
Definition SHA1I.H:114
A 1D vector of objects of type <T>, where the size of the vector is known and can be used for subscri...
Definition UList.H:89
bool empty() const noexcept
True if List is empty (ie, size() is zero).
Definition UList.H:701
static int myProcNo(const label communicator=worldComm)
Rank of this process in the communicator (starting from masterNo()). Negative if the process is not a...
Definition UPstream.H:1706
static bool parRun(const bool on) noexcept
Set as parallel run on/off.
Definition UPstream.H:1669
@ broadcast
broadcast [MPI]
Definition UPstream.H:189
Extract command arguments and options from the supplied argc and argv parameters.
Definition argList.H:119
A list of keyword definitions, which are a keyword followed by a number of values (eg,...
Definition dictionary.H:133
static word fullname(word libName)
Library fullname, prefix with 'lib', suffix with '.so'.
Encapsulation of dynamic code dictionaries.
const string & code() const noexcept
The code.
const string & include() const noexcept
The code includes.
const SHA1 & sha1() const noexcept
The SHA1 calculated from options, libs, include, code, etc.
const string & localCode() const noexcept
The local (file-scope) code.
static int allowSystemOperations
Flag if system operations are allowed.
static void copyAndFilter(ISstream &, OSstream &, const HashTable< string > &mapping)
Copy lines while expanding variables.
Definition dynamicCode.C:96
fileName codePath() const
Path for specified code name.
bool copyOrCreateFiles(const bool verbose=false) const
Copy/create files prior to compilation.
dynamicCode(const dynamicCode &)=delete
No copy construct.
void addCopyFile(const fileName &name)
Add a file template name, which will be found and filtered.
void reset(const dynamicCodeContext &)
Clear files and reset variables to specified context.
static const char *const targetLibDir
Directory for library targets for Make/files.
fileName libRelPath() const
Library path for specified code name relative to <case>.
static bool resolveTemplates(const UList< fileName > &templateNames, DynamicList< fileName > &resolvedFiles, DynamicList< fileName > &badFiles)
Resolve code-templates via the codeTemplateEnvName.
static const word codeTemplateEnvName
Name of the code template environment variable.
static const char *const topDirName
Top-level directory name for copy/compiling.
void setFilterVariable(const word &key, const std::string &value)
Define a filter variable.
const word & codeDirName() const noexcept
Return the code-dirname.
static void waitForFile(const fileName &file, const dictionary &contextDict)
Wait for libPath() file to appear on sub-ranks.
void addCreateFile(const fileName &name, const std::string &contents)
Add a file to create with its contents. Will not be filtered.
void setFilterContext(const dynamicCodeContext &)
Define filter variables for code, codeInclude, SHA1sum.
static void checkSecurity(const char *title, const dictionary &)
Check security for creating dynamic code.
Definition dynamicCode.C:59
bool createMakeFiles() const
Copy/create Make/files prior to compilation.
fileName digestFile() const
Path for SHA1Digest.
bool writeDigest(const SHA1Digest &) const
Write digest to Make/SHA1Digest.
fileName codeRelPath() const
Path for specified code name relative to <case>.
void addCompileFile(const fileName &name)
Add a file template name, which will be found and filtered.
void clear()
Clear files and variables.
bool upToDate(const dynamicCodeContext &context) const
Verify if the copied code is up-to-date, based on Make/SHA1Digest.
fileName libPath() const
Library path for specified code name.
static const fileName codeTemplateDirName
Name of the code template sub-directory.
bool createMakeOptions() const
Copy/create Make/options prior to compilation.
bool wmakeLibso() const
Compile a libso.
bool writeCommentSHA1(Ostream &) const
Write SHA1 value as C-comment.
void setMakeOptions(const std::string &content)
Define contents for Make/options.
const word & codeName() const noexcept
Return the code-name.
A class for handling file names.
Definition fileName.H:75
static std::string path(const std::string &str)
Return directory path name (part before last /).
Definition fileNameI.H:169
static std::string name(const std::string &str)
Return basename (part beyond last /), including its extension.
Definition fileNameI.H:192
A line primitive.
Definition line.H:180
A class for handling words, derived from Foam::string.
Definition word.H:66
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition error.H:629
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition error.H:600
Functions to search 'etc' directories for configuration files etc.
#define DetailInfo
Definition evalEntry.C:30
OBJstream os(runTime.globalPath()/outputName)
surface1 clear()
#define DebugPout
Report an information message using Foam::Pout.
Namespace for handling debugging switches.
Definition debug.C:45
int infoSwitch(const char *name, const int deflt=0)
Lookup info switch or add default value.
Definition debug.C:228
const int api
OpenFOAM api number (integer) corresponding to the value of OPENFOAM at the time of compilation.
Collection of static functions for various string-related operations.
void inplaceExpand(std::string &s, const HashTable< string > &mapping, const char sigil='$')
Inplace expand occurrences of variables according to the mapping. Does not use environment values.
string expand(const std::string &s, const HashTable< string > &mapping, const char sigil='$')
Expand occurrences of variables according to the mapping and return the expanded string.
string getEnv(const std::string &envName)
Get environment value for given envName.
Definition POSIX.C:341
bool exists(const fileName &name, const bool checkGzip=true, const bool followLink=true)
Does the name exist (as DIRECTORY or FILE) in the file system?
Definition POSIX.C:837
int system(const std::string &command, const bool bg=false)
Execute the specified command via the shell.
Definition POSIX.C:1704
bool mkDir(const fileName &pathName, mode_t mode=0777)
Make a directory and return an error if it could not be created.
Definition POSIX.C:616
bool isAdministrator()
Is the current user the administrator (root).
Definition POSIX.C:436
messageStream Info
Information stream (stdout output on master, null elsewhere).
unsigned int sleep(const unsigned int sec)
Sleep for the specified number of seconds.
Definition POSIX.C:1549
fileName findEtcFile(const fileName &name, const bool mandatory=false, unsigned short location=0777)
Search for a single FILE within the etc directories.
Definition etcFiles.C:439
List< string > stringList
List of string.
Definition stringList.H:32
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition Ostream.H:519
off_t fileSize(const fileName &name, const bool followLink=true)
Return size of file or -1 on failure (normally follows symbolic links).
Definition POSIX.C:907
IOerror FatalIOError
Error stream (stdout output on all processes), with additional 'FOAM FATAL IO ERROR' header text and ...
int infoDetailLevel
Global for selective suppression of Info output.
bool isFile(const fileName &name, const bool checkGzip=true, const bool followLink=true)
Does the name exist as a FILE in the file system?
Definition POSIX.C:879
messageStream InfoErr
Information stream (stderr output on master, null elsewhere).
error FatalError
Error stream (stdout output on all processes), with additional 'FOAM FATAL ERROR' header text and sta...
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for expressions::valueTypeCode::INVALID.
Definition exprTraits.C:127
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition errorManip.H:125
bool isDir(const fileName &name, const bool followLink=true)
Does the name exist as a DIRECTORY in the file system?
Definition POSIX.C:862
constexpr char nl
The newline '\n' character (0x0a).
Definition Ostream.H:50
dictionary dict