CMS 3D CMS Logo

ConfigurationDescriptions.cc
Go to the documentation of this file.
1 // -*- C++ -*-
2 //
3 // Package: ParameterSet
4 // Class : ConfigurationDescriptions
5 //
6 // Implementation:
7 // <Notes on implementation>
8 //
9 // Original Author: W. David Dagenhart
10 // Created: 17 December 2008
11 //
12 
18 
19 #include <fstream>
20 #include <iostream>
21 #include <iomanip>
22 #include <sstream>
23 #include <cstring>
24 #include <cerrno>
25 #include <cstring>
26 
27 namespace {
28  void matchLabel(std::pair<std::string, edm::ParameterSetDescription> const& thePair,
29  std::string const& moduleLabel,
30  edm::ParameterSetDescription const*& psetDesc) {
31  if (thePair.first == moduleLabel) {
32  psetDesc = &thePair.second;
33  }
34  }
35 } // namespace
36 
37 static const char* const kSource = "Source";
38 static const char* const kService = "Service";
39 static const char* const k_source = "source";
40 
41 namespace edm {
42 
44  : baseType_(baseType), pluginName_(pluginName), defaultDescDefined_(false) {}
45 
47 
49 
51 
52  void ConfigurationDescriptions::add(char const* label, ParameterSetDescription const& psetDescription) {
53  std::string labelString(label);
54  add(labelString, psetDescription);
55  }
56 
58  if (0 == strcmp(baseType_.c_str(), kSource)) {
59  if (0 != strcmp(label.c_str(), k_source)) {
61  "ConfigurationDescriptions::add, when adding a ParameterSetDescription for a source the "
62  "label must be \"source\"\n");
63  }
64  if (!descriptions_.empty() || defaultDescDefined_ == true) {
65  throw edm::Exception(
67  "ConfigurationDescriptions::add, for a source only 1 ParameterSetDescription may be added\n");
68  }
69  } else if (0 == strcmp(baseType_.c_str(), kService)) {
70  if (!descriptions_.empty() || defaultDescDefined_ == true) {
71  throw edm::Exception(
73  "ConfigurationDescriptions::add, for a service only 1 ParameterSetDescription may be added\n");
74  }
75  }
76 
77  // To minimize the number of copies involved create an empty description first
78  // and push it into the vector. Then perform the copy.
79  std::pair<std::string, ParameterSetDescription> pairWithEmptyDescription;
80  descriptions_.push_back(pairWithEmptyDescription);
81  std::pair<std::string, ParameterSetDescription>& pair = descriptions_.back();
82 
83  pair.first = label;
84  pair.second = psetDescription;
85  }
86 
89  if (kService == baseType_) {
91  } else if (kSource == baseType_) {
92  label = "source";
93  } else {
95  }
96  add(label, psetDescription);
97  }
98 
100  if (0 == strcmp(baseType_.c_str(), kSource) || 0 == strcmp(baseType_.c_str(), kService)) {
101  if (!descriptions_.empty() || defaultDescDefined_ == true) {
103  "ConfigurationDescriptions::addDefault, for a source or service only 1 "
104  "ParameterSetDescription may be added\n");
105  }
106  }
107 
108  defaultDescDefined_ = true;
109  defaultDesc_ = psetDescription;
110  }
111 
113  if (defaultDescDefined_) {
114  return &defaultDesc_;
115  }
116  return nullptr;
117  }
118 
120 
122 
124  ParameterSetDescription const* psetDesc = nullptr;
125  for_all(descriptions_, std::bind(&matchLabel, std::placeholders::_1, std::cref(moduleLabel), std::ref(psetDesc)));
126 
127  // If there is a matching label
128  if (psetDesc != nullptr) {
129  psetDesc->validate(pset);
130  }
131  // Is there an explicit description to be used for a non standard label
132  else if (defaultDescDefined_) {
134  }
135  // Otherwise use the first one.
136  else if (!descriptions_.empty()) {
137  descriptions_[0].second.validate(pset);
138  }
139  // It is possible for no descriptions to be defined and no validation occurs
140  // for this module ever.
141  }
142 
143  void ConfigurationDescriptions::writeCfis(std::set<std::string>& usedCfiFileNames) const {
144  bool wroteClassFile = false;
146  if (defaultDescDefined_) {
148  wroteClassFile = true;
149  } else if (descriptions_.size() == 1) {
150  paths = writeClassFile(descriptions_.begin()->second, true);
151  wroteClassFile = true;
152  }
153  CfiOptions ops = wroteClassFile ? CfiOptions{cfi::Untyped{paths}} : CfiOptions{cfi::Typed{}};
154  for (auto& d : descriptions_) {
156  d, baseType_, pluginName_, (not defaultDescDefined_) and (1 == descriptions_.size()), ops, usedCfiFileNames);
157  }
158  }
159 
160  namespace {
161  std::string modifyPluginName(std::string iName) {
162  auto found = iName.find("::");
163  while (found != std::string::npos) {
164  iName.replace(found, 2, "_");
165  found = iName.find("::");
166  }
167  //Symbols that can appear in our plugin names but can't in python function names
168  const std::string toReplace("@<>,");
169  found = iName.find_first_of(toReplace);
170  while (found != std::string::npos) {
171  iName.replace(found, 1, "_");
172  found = iName.find_first_of(toReplace, found);
173  }
174  return iName;
175  }
176  } // namespace
177 
179  bool willUseWithCfis) const {
180  std::string pluginName = modifyPluginName(pluginName_);
181 
182  std::string fileName = pluginName + ".py";
183  std::ofstream outFile(fileName.c_str());
184  if (outFile.fail()) {
185  edm::Exception ex(edm::errors::LogicError, "Creating class file failed.\n");
186  ex << "Opening a file '" << fileName << "' failed.\n";
187  ex << "Error code from errno " << errno << ": " << std::strerror(errno) << "\n";
188 
189  ex.addContext("Executing function ConfigurationDescriptions::writeDefault");
190  throw ex;
191  }
192  outFile << "import FWCore.ParameterSet.Config as cms\n\n";
193  outFile << "def " << pluginName
194  << "(*args, **kwargs):\n"
195  " mod = cms."
196  << baseType_ << "('" << pluginName_ << "'";
197 
198  bool startWithComma = true;
199  int indentation = 4;
200  CfiOptions ops = willUseWithCfis ? CfiOptions{cfi::ClassFile{}} : CfiOptions{cfi::Typed{}};
201  iDesc.writeCfi(outFile, startWithComma, indentation, ops);
202 
203  outFile << ")\n"
204  " for a in args:\n"
205  " mod.update_(a)\n"
206  " mod.update_(kwargs)\n"
207  " return mod\n";
208 
209  outFile.close();
210  return std::holds_alternative<cfi::ClassFile>(ops) ? std::get<cfi::ClassFile>(ops).releasePaths() : cfi::Paths{};
211  }
212 
213  void ConfigurationDescriptions::writeCfiForLabel(std::pair<std::string, ParameterSetDescription> const& labelAndDesc,
214  std::string const& baseType,
215  std::string const& pluginName,
216  bool isSameAsDefault,
218  std::set<std::string>& usedCfiFileNames) {
219  if (0 == strcmp(baseType.c_str(), kService) && labelAndDesc.first != pluginName) {
221  "ConfigurationDescriptions::writeCfiForLabel\nFor a service the label and the plugin name "
222  "must be the same.\n")
223  << "This error is probably caused by an incorrect label being passed\nto the ConfigurationDescriptions::add "
224  "function earlier.\n"
225  << "plugin name = \"" << pluginName << "\" label name = \"" << labelAndDesc.first << "\"\n";
226  }
227 
228  std::string cfi_filename;
229  if (0 == strcmp(baseType.c_str(), kSource)) {
230  cfi_filename = pluginName + "_cfi.py";
231  } else {
232  cfi_filename = labelAndDesc.first + "_cfi.py";
233  }
234  if (!usedCfiFileNames.insert(cfi_filename).second) {
236  "Two cfi files are being generated with the same name in the same directory.\n");
237  ex << "The cfi file name is '" << cfi_filename << "' and\n"
238  << "the module label is \'" << labelAndDesc.first << "\'.\n"
239  << "This error is probably caused by an error in one or more fillDescriptions functions\n"
240  << "where duplicate module labels are being passed to the ConfigurationDescriptions::add\n"
241  << "function. All such module labels must be unique within a package.\n"
242  << "If you do not want the generated cfi file and do not need more than one\n"
243  << "description for a plugin, then a way to fix this is to use the addDefault\n"
244  << "function instead of the add function.\n"
245  << "There are 3 common ways this problem can happen.\n"
246  << "1. This can happen when a module label is explicitly duplicated in one or more\n"
247  << "fillDescriptions functions. Fix these by changing the module labels to be unique.\n"
248  << "2. This can also happen when a module class is a template class and plugins are\n"
249  << "defined by instantiations with differing template parameters and these plugins\n"
250  << "share the same fillDescriptions function. Fix these by specializing the fillDescriptions\n"
251  << "function for each template instantiation.\n"
252  << "3. This can also happen when there is an inheritance heirarchy and multiple plugin modules\n"
253  << "are defined using derived classes and the base class which share the same fillDescriptions\n"
254  << "function. Fix these by redefining the fillDescriptions function in each derived class.\n";
255  ex.addContext("Executing function ConfigurationDescriptions::writeCfiForLabel");
256  throw ex;
257  }
258  std::ofstream outFile(cfi_filename.c_str());
259  if (outFile.fail()) {
260  edm::Exception ex(edm::errors::LogicError, "Creating cfi file failed.\n");
261  ex << "Opening a file '" << cfi_filename << "' for module '" << labelAndDesc.first << "' failed.\n";
262  ex << "Error code from errno " << errno << ": " << std::strerror(errno) << "\n";
263 
264  ex.addContext("Executing function ConfigurationDescriptions::writeCfiForLabel");
265  throw ex;
266  }
267 
268  bool startWithComma = true;
269  if (not shouldWriteUntyped(options)) {
270  outFile << "import FWCore.ParameterSet.Config as cms\n\n";
271  outFile << labelAndDesc.first << " = cms." << baseType << "('" << pluginName << "'";
272  } else {
273  outFile << "import FWCore.ParameterSet.Config as cms\n\n";
274 
275  auto pythonName = modifyPluginName(pluginName);
276  outFile << "from ." << pythonName << " import " << pythonName << "\n\n";
277  outFile << labelAndDesc.first << " = " << pythonName << "(";
278  startWithComma = false;
279  }
280  if (not isSameAsDefault) {
281  int indentation = 2;
282  labelAndDesc.second.writeCfi(outFile, startWithComma, indentation, options);
283  }
284  outFile << ")\n";
285 
286  outFile.close();
287 
288  if (0 == strcmp(baseType.c_str(), kSource)) {
289  std::cout << pluginName << "\n";
290  } else {
291  std::cout << labelAndDesc.first << "\n";
292  }
293  }
294 
296  std::string const& moduleLabel,
297  bool brief,
298  bool printOnlyLabels,
299  size_t lineWidth,
300  int indentation,
301  int iPlugin) const {
302  if (!brief) {
303  if (!comment().empty()) {
305  }
306  os << "\n";
307  }
308 
309  if (descriptions_.empty() && !defaultDescDefined_) {
310  char oldFill = os.fill();
312  os << std::setfill(' ') << std::setw(indentation) << "";
313  os << "There are no PSet descriptions defined for this plugin.\n";
314  os << std::setfill(' ') << std::setw(indentation) << "";
315  os << "PSets will not be validated and no cfi files will be generated.\n";
316  os << std::setfill(oldFill);
317  if (!brief)
318  os << "\n";
319  return;
320  }
321 
324  char oldFill = os.fill();
325  os << std::setfill(' ') << std::setw(indentation) << "";
326  os << "This plugin has not implemented the function which defines its\n";
327  os << std::setfill(' ') << std::setw(indentation) << "";
328  os << "configuration descriptions yet. No descriptions are available.\n";
329  os << std::setfill(' ') << std::setw(indentation) << "";
330  os << "Its PSets will not be validated, and no cfi files will be generated.\n";
331  os << std::setfill(oldFill);
332  if (!brief)
333  os << "\n";
334  return;
335  }
336 
337  if (!brief) {
338  std::stringstream ss;
339  if (defaultDescDefined_) {
340  if (descriptions_.empty()) {
341  ss << "This plugin has only one PSet description. "
342  << "This description is always used to validate configurations. "
343  << "Because this configuration has no label, no cfi files will be generated.";
344  } else {
345  ss << "This plugin has " << (descriptions_.size() + 1U) << " PSet descriptions. "
346  << "The description used to validate a configuration is selected by "
347  << "matching the module labels. If none match, then the last description, "
348  << "which has no label, is selected. "
349  << "A cfi file will be generated for each configuration with a module label.";
350  }
351  } else {
352  if (descriptions_.size() == 1U) {
353  ss << "This plugin has " << descriptions_.size() << " PSet description. "
354  << "This description is always used to validate configurations. "
355  << "The label below is used when generating the cfi file.";
356  } else {
357  ss << "This plugin has " << descriptions_.size() << " PSet descriptions. "
358  << "The description used to validate a configuration is selected by "
359  << "matching the module labels. If none match the first description below is used. "
360  << "The module labels below are also used when generating the cfi files.";
361  }
362  }
364  os << "\n";
365  }
366 
368 
370  counter.iPlugin = iPlugin;
371  counter.iSelectedModule = 0;
372  counter.iModule = 0;
373 
374  for (auto const& d : descriptions_) {
375  printForLabel(d, os, moduleLabel, brief, printOnlyLabels, lineWidth, indentation, counter);
376  }
377 
378  if (defaultDescDefined_) {
380  std::string("@default"),
381  defaultDesc_,
382  moduleLabel,
383  brief,
384  printOnlyLabels,
385  lineWidth,
386  indentation,
387  counter);
388  }
389  }
390 
391  void ConfigurationDescriptions::printForLabel(std::pair<std::string, ParameterSetDescription> const& labelAndDesc,
392  std::ostream& os,
393  std::string const& moduleLabel,
394  bool brief,
395  bool printOnlyLabels,
396  size_t lineWidth,
397  int indentation,
398  DescriptionCounter& counter) const {
400  labelAndDesc.first,
401  labelAndDesc.second,
402  moduleLabel,
403  brief,
404  printOnlyLabels,
405  lineWidth,
406  indentation,
407  counter);
408  }
409 
411  std::string const& label,
413  std::string const& moduleLabel,
414  bool brief,
415  bool printOnlyLabels,
416  size_t lineWidth,
417  int indentation,
418  DescriptionCounter& counter) const {
419  ++counter.iModule;
420  if (!moduleLabel.empty() && label != moduleLabel)
421  return;
422  ++counter.iSelectedModule;
423 
424  std::stringstream ss;
425  ss << counter.iPlugin << "." << counter.iSelectedModule;
426  std::string section = ss.str();
427 
428  char oldFill = os.fill();
429  os << std::setfill(' ') << std::setw(indentation) << "" << std::setfill(oldFill);
430  os << section << " ";
431  if (label == std::string("@default")) {
432  os << "description without a module label\n";
433  } else {
434  if (!brief) {
435  if (0 == strcmp(baseType_.c_str(), kSource) || 0 == strcmp(baseType_.c_str(), kService)) {
436  os << "label: ";
437  } else {
438  os << "module label: ";
439  }
440  }
441  os << label << "\n";
442  }
443 
444  if (!brief) {
445  if (!description.comment().empty()) {
447  }
448  os << "\n";
449  }
450  if (printOnlyLabels)
451  return;
452 
453  DocFormatHelper dfh;
454  dfh.setBrief(brief);
455  dfh.setLineWidth(lineWidth);
457  dfh.setSection(section);
459 
460  description.print(os, dfh);
461  }
462 } // namespace edm
bool shouldWriteUntyped(CfiOptions const &iOps) noexcept
ConfigurationDescriptions(std::string const &baseType, std::string const &pluginName)
void addWithDefaultLabel(ParameterSetDescription const &psetDescription)
void validate(ParameterSet &pset, std::string const &moduleLabel) const
static const char *const kService
void print(std::ostream &os, std::string const &moduleLabel, bool brief, bool printOnlyLabels, size_t lineWidth, int indentation, int iPlugin) const
static void wrapAndPrintText(std::ostream &os, std::string const &text, size_t indent, size_t suggestedWidth)
void printForLabel(std::pair< std::string, ParameterSetDescription > const &labelAndDesc, std::ostream &os, std::string const &moduleLabel, bool brief, bool printOnlyLabels, size_t lineWidth, int indentationn, DescriptionCounter &counter) const
cfi::Paths writeClassFile(ParameterSetDescription const &, bool willUseWithCfis) const
ParameterSetDescription * defaultDescription()
Returns 0 if no default has been assigned.
static const char *const kSource
void writeCfis(std::set< std::string > &usedCfiFileNames) const
Func for_all(ForwardSequence &s, Func f)
wrapper for std::for_each
Definition: Algorithms.h:14
static int offsetModuleLabel()
char const * label
void addDefault(ParameterSetDescription const &psetDescription)
void setBrief(bool value)
std::vector< std::pair< std::string, ParameterSetDescription > > descriptions_
std::string defaultModuleLabel(std::string label)
Definition: value.py:1
d
Definition: ztail.py:151
void setComment(std::string const &value)
static void writeCfiForLabel(std::pair< std::string, ParameterSetDescription > const &labelAndDesc, std::string const &baseType, std::string const &pluginName, bool isSameAsDefault, CfiOptions &options, std::set< std::string > &usedCfiFileNames)
void add(std::string const &label, ParameterSetDescription const &psetDescription)
void addContext(std::string const &context)
Definition: Exception.cc:169
void validate(ParameterSet &pset) const
HLT enums.
static std::atomic< unsigned int > counter
static const char *const k_source
static int offsetTopLevelPSet()
void setSection(std::string const &value)
std::vector< std::pair< std::string, ParameterSetDescription > >::iterator iterator
void writeCfi(std::ostream &os, bool startWithComma, int indentation, CfiOptions &) const
std::string const & comment() const
void setParent(DescriptionParent value)
cfi::CfiOptions CfiOptions
void setIndentation(int value)
void setLineWidth(size_t value)