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-2020 OpenCFD Ltd.
10 -------------------------------------------------------------------------------
11 License
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 "IOstreams.H"
36 #include "OSspecific.H"
37 #include "etcFiles.H"
38 #include "dictionary.H"
39 #include "foamVersion.H"
40 
41 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
42 
44 (
45  Foam::debug::infoSwitch("allowSystemOperations", 0)
46 );
47 
48 
50  = "FOAM_CODE_TEMPLATES";
51 
53  = "codeTemplates/dynamicCode";
54 
55 const char* const Foam::dynamicCode::targetLibDir
56  = "LIB = $(PWD)/../platforms/$(WM_OPTIONS)/lib";
57 
59  = "dynamicCode";
60 
61 
62 // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
63 
65 (
66  const char* title,
67  const dictionary& dict
68 )
69 {
70  if (isAdministrator())
71  {
73  << "This code should not be executed by someone"
74  << " with administrator rights for security reasons." << nl
75  << "It generates a shared library which is loaded using dlopen"
76  << nl << endl
77  << exit(FatalIOError);
78  }
79 
81  {
83  << "Loading shared libraries using case-supplied code may have"
84  << " been disabled" << nl
85  << "by default for security reasons." << nl
86  << "If you trust the code, you may enable this by adding"
87  << nl << nl
88  << " allowSystemOperations 1" << nl << nl
89  << "to the InfoSwitches setting in the system controlDict." << nl
90  << "The system controlDict is any of" << nl << nl
91  << " ~/.OpenFOAM/" << foamVersion::api << "/controlDict" << nl
92  << " ~/.OpenFOAM/controlDict" << nl
93  << " $WM_PROJECT_DIR/etc/controlDict" << nl << endl
94  << exit(FatalIOError);
95  }
96 }
97 
98 
99 // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
100 
102 (
103  ISstream& is,
104  OSstream& os,
105  const HashTable<string>& mapping
106 )
107 {
108  if (!is.good())
109  {
111  << "Failed opening for reading " << is.name()
112  << exit(FatalError);
113  }
114 
115  if (!os.good())
116  {
118  << "Failed writing " << os.name()
119  << exit(FatalError);
120  }
121 
122  // Copy file while rewriting $VARS and ${VARS}
123  string line;
124  do
125  {
126  is.getLine(line);
127 
128  // Expand according to HashTable mapping, not the environment.
129  // Expanding according to env variables might cause too many
130  // surprises
131  stringOps::inplaceExpand(line, mapping);
132  os.writeQuoted(line, false) << nl;
133  }
134  while (is.good());
135 }
136 
137 
139 (
140  const UList<fileName>& templateNames,
141  DynamicList<fileName>& resolvedFiles,
142  DynamicList<fileName>& badFiles
143 )
144 {
145  // Try to get template from FOAM_CODE_TEMPLATES
146  const fileName templateDir(Foam::getEnv(codeTemplateEnvName));
147 
148  bool allOkay = true;
149  for (const fileName& templateName : templateNames)
150  {
151  fileName file;
152  if (!templateDir.empty() && isDir(templateDir))
153  {
154  file = templateDir/templateName;
155  if (!isFile(file, false))
156  {
157  file.clear();
158  }
159  }
160 
161  // Not found - fallback to <etc> expansion
162  if (file.empty())
163  {
164  file = findEtcFile(codeTemplateDirName/templateName);
165  }
166 
167  if (file.empty())
168  {
169  badFiles.append(templateName);
170  allOkay = false;
171  }
172  else
173  {
174  resolvedFiles.append(file);
175  }
176  }
177 
178  return allOkay;
179 }
180 
181 
182 bool Foam::dynamicCode::writeCommentSHA1(Ostream& os) const
183 {
184  const auto fnd = filterVars_.cfind("SHA1sum");
185 
186  if (!fnd.found())
187  {
188  return false;
189  }
191  os << "/* dynamicCode:\n * SHA1 = ";
192  os.writeQuoted(*fnd, false) << "\n */\n";
193  return true;
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
228  << targetLibDir
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 
257  writeCommentSHA1(os);
258  os.writeQuoted(makeOptions_, false) << nl;
259 
260  return true;
261 }
262 
263 
264 bool Foam::dynamicCode::writeDigest(const SHA1Digest& sha1) const
265 {
266  const fileName file = digestFile();
267  mkDir(file.path());
268 
269  OFstream os(file);
270  sha1.write(os, true) << nl;
271 
272  return os.good();
273 }
274 
275 
276 bool 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 
291 Foam::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
346 )
347 {
348  clear();
349  setFilterContext(context);
350 }
351 
354 {
355  compileFiles_.append(name);
356 }
357 
358 
360 {
361  copyFiles_.append(name);
362 }
363 
364 
366 (
367  const fileName& name,
368  const string& contents
369 )
370 {
371  createFiles_.append(fileAndContent(name, contents));
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 
397 void Foam::dynamicCode::setMakeOptions(const std::string& content)
398 {
399  makeOptions_ = content;
400 }
401 
402 
403 bool Foam::dynamicCode::copyOrCreateFiles(const bool verbose) const
404 {
405  if (verbose)
406  {
407  DetailInfo
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 fileAndContent& 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 : Serr);
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 
517 bool 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 
530 bool Foam::dynamicCode::upToDate(const dynamicCodeContext& context) const
531 {
532  return upToDate(context.sha1());
533 }
534 
535 
536 // ************************************************************************* //
const string & include() const noexcept
The code includes.
dictionary dict
const string & localCode() const noexcept
The local (file-scope) code.
bool writeDigest(const SHA1Digest &) const
Write digest to Make/SHA1Digest.
Definition: dynamicCode.C:257
A line primitive.
Definition: line.H:52
A class for handling file names.
Definition: fileName.H:71
std::string str(const bool prefixed=false) const
The digest (40-byte) text representation, optionally with &#39;_&#39; prefix.
Definition: SHA1I.H:114
void reset(const dynamicCodeContext &)
Clear files and reset variables to specified context.
Definition: dynamicCode.C:337
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
error FatalError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL ERROR&#39; header text and sta...
A list of keyword definitions, which are a keyword followed by a number of values (eg...
Definition: dictionary.H:120
void setMakeOptions(const std::string &content)
Define contents for Make/options.
Definition: dynamicCode.C:390
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:578
A 2-tuple for storing two objects of dissimilar types. The container is similar in purpose to std::pa...
Definition: stringOps.H:55
Output to file stream, using an OSstream.
Definition: OFstream.H:49
int infoDetailLevel
Global for selective suppression of Info output.
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:49
static bool resolveTemplates(const UList< fileName > &templateNames, DynamicList< fileName > &resolvedFiles, DynamicList< fileName > &badFiles)
Resolve code-templates via the codeTemplateEnvName.
Definition: dynamicCode.C:132
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:487
The SHA1 message digest.
Definition: SHA1Digest.H:56
void setFilterContext(const dynamicCodeContext &)
Define filter variables for code, codeInclude, SHA1sum.
Definition: dynamicCode.C:369
static const word codeTemplateEnvName
Name of the code template environment variable.
Definition: dynamicCode.H:199
static const char *const topDirName
Top-level directory name for copy/compiling.
Definition: dynamicCode.H:136
static const char *const targetLibDir
Directory for library targets for Make/files.
Definition: dynamicCode.H:131
static std::string path(const std::string &str)
Return directory path name (part before last /)
Definition: fileNameI.H:169
void addCompileFile(const fileName &name)
Add a file template name, which will be found and filtered.
Definition: dynamicCode.C:346
string getEnv(const std::string &envName)
Get environment value for given envName.
Definition: POSIX.C:292
int infoSwitch(const char *name, const int deflt=0)
Lookup info switch or add default value.
Definition: debug.C:228
bool writeCommentSHA1(Ostream &) const
Write SHA1 value as C-comment.
Definition: dynamicCode.C:175
virtual Ostream & writeQuoted(const std::string &str, const bool quoted=true)
Write std::string surrounded by quotes.
Definition: OBJstream.C:101
bool createMakeOptions() const
Copy/create Make/options prior to compilation.
Definition: dynamicCode.C:228
Useful combination of include files which define Sin, Sout and Serr and the use of IO streams general...
fileName codeRelPath() const
Path for specified code name relative to <case>
Definition: dynamicCode.C:302
bool isDir(const fileName &name, const bool followLink=true)
Does the name exist as a DIRECTORY in the file system?
Definition: POSIX.C:813
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
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:567
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for INVALID.
Definition: exprTraits.C:52
fileName libRelPath() const
Library path for specified code name relative to <case>
Definition: dynamicCode.C:314
A class for handling words, derived from Foam::string.
Definition: word.H:63
Functions to search &#39;etc&#39; directories for configuration files etc.
static const fileName codeTemplateDirName
Name of the code template sub-directory.
Definition: dynamicCode.H:206
Extract command arguments and options from the supplied argc and argv parameters. ...
Definition: argList.H:118
static int allowSystemOperations
Flag if system operations are allowed.
Definition: dynamicCode.H:211
static void copyAndFilter(ISstream &, OSstream &, const HashTable< string > &mapping)
Copy lines while expanding variables.
Definition: dynamicCode.C:95
void addCreateFile(const fileName &name, const string &contents)
Add a file to create with its contents. Will not be filtered.
Definition: dynamicCode.C:359
static word fullname(word libName)
Library fullname, prefix with &#39;lib&#39;, suffix with &#39;.so&#39;.
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:788
patchWriters clear()
bool wmakeLibso() const
Compile a libso.
Definition: dynamicCode.C:488
#define DetailInfo
Definition: evalEntry.C:30
const int api
OpenFOAM api number (integer) corresponding to the value of OPENFOAM at the time of compilation...
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...
Definition: stringOps.C:718
bool upToDate(const dynamicCodeContext &context) const
Verify if the copied code is up-to-date, based on Make/SHA1Digest.
Definition: dynamicCode.C:523
bool copyOrCreateFiles(const bool verbose=false) const
Copy/create files prior to compilation.
Definition: dynamicCode.C:396
OBJstream os(runTime.globalPath()/outputName)
virtual const fileName & name() const
Read/write access to the name of the stream.
Definition: OSstream.H:128
const string & code() const noexcept
The code.
bool isAdministrator()
Is the current user the administrator (root)
Definition: POSIX.C:387
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:830
Encapsulation of dynamic code dictionaries.
Ostream & write(Ostream &os, const bool prefixed=false) const
Write (40-byte) text representation, optionally with &#39;_&#39; prefix.
Definition: SHA1Digest.C:179
void setFilterVariable(const word &key, const std::string &value)
Define a filter variable.
Definition: dynamicCode.C:381
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
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:607
auto key(const Type &t) -> typename std::enable_if< std::is_enum< Type >::value, typename std::underlying_type< Type >::type >::type
Definition: foamGltfBase.H:103
bool good() const noexcept
True if next operation might succeed.
Definition: IOstream.H:274
void addCopyFile(const fileName &name)
Add a file template name, which will be found and filtered.
Definition: dynamicCode.C:352
OSstream Serr
OSstream wrapped stderr (std::cerr)
fileName libPath() const
Library path for specified code name.
Definition: dynamicCode.C:308
messageStream Info
Information stream (stdout output on master, null elsewhere)
void clear()
Clear files and variables.
Definition: dynamicCode.C:320
int system(const std::string &command, const bool bg=false)
Execute the specified command via the shell.
Definition: POSIX.C:1655
static void checkSecurity(const char *title, const dictionary &)
Check security for creating dynamic code.
Definition: dynamicCode.C:58
bool createMakeFiles() const
Copy/create Make/files prior to compilation.
Definition: dynamicCode.C:190
const SHA1 & sha1() const noexcept
The SHA1 calculated from options, libs, include, code, etc.
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.
Definition: stringOps.C:705
IOerror FatalIOError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL IO ERROR&#39; header text and ...