File indexing completed on 2024-09-24 22:51:19
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013 #include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h"
0014 #include "FWCore/ParameterSet/interface/DocFormatHelper.h"
0015 #include "FWCore/ParameterSet/interface/defaultModuleLabel.h"
0016 #include "FWCore/Utilities/interface/Algorithms.h"
0017 #include "FWCore/Utilities/interface/EDMException.h"
0018
0019 #include <fstream>
0020 #include <iostream>
0021 #include <iomanip>
0022 #include <sstream>
0023 #include <cstring>
0024 #include <cerrno>
0025 #include <cstring>
0026
0027 namespace {
0028 void matchLabel(std::pair<std::string, edm::ParameterSetDescription> const& thePair,
0029 std::string const& moduleLabel,
0030 edm::ParameterSetDescription const*& psetDesc) {
0031 if (thePair.first == moduleLabel) {
0032 psetDesc = &thePair.second;
0033 }
0034 }
0035 }
0036
0037 static const char* const kSource = "Source";
0038 static const char* const kService = "Service";
0039 static const char* const k_source = "source";
0040
0041 namespace edm {
0042
0043 ConfigurationDescriptions::ConfigurationDescriptions(std::string const& baseType, std::string const& pluginName)
0044 : baseType_(baseType), pluginName_(pluginName), defaultDescDefined_(false) {}
0045
0046 ConfigurationDescriptions::~ConfigurationDescriptions() {}
0047
0048 void ConfigurationDescriptions::setComment(std::string const& value) { comment_ = value; }
0049
0050 void ConfigurationDescriptions::setComment(char const* value) { comment_ = value; }
0051
0052 void ConfigurationDescriptions::add(char const* label, ParameterSetDescription const& psetDescription) {
0053 std::string labelString(label);
0054 add(labelString, psetDescription);
0055 }
0056
0057 void ConfigurationDescriptions::add(std::string const& label, ParameterSetDescription const& psetDescription) {
0058 if (0 == strcmp(baseType_.c_str(), kSource)) {
0059 if (0 != strcmp(label.c_str(), k_source)) {
0060 throw edm::Exception(edm::errors::LogicError,
0061 "ConfigurationDescriptions::add, when adding a ParameterSetDescription for a source the "
0062 "label must be \"source\"\n");
0063 }
0064 if (!descriptions_.empty() || defaultDescDefined_ == true) {
0065 throw edm::Exception(
0066 edm::errors::LogicError,
0067 "ConfigurationDescriptions::add, for a source only 1 ParameterSetDescription may be added\n");
0068 }
0069 } else if (0 == strcmp(baseType_.c_str(), kService)) {
0070 if (!descriptions_.empty() || defaultDescDefined_ == true) {
0071 throw edm::Exception(
0072 edm::errors::LogicError,
0073 "ConfigurationDescriptions::add, for a service only 1 ParameterSetDescription may be added\n");
0074 }
0075 }
0076
0077
0078
0079 std::pair<std::string, ParameterSetDescription> pairWithEmptyDescription;
0080 descriptions_.push_back(pairWithEmptyDescription);
0081 std::pair<std::string, ParameterSetDescription>& pair = descriptions_.back();
0082
0083 pair.first = label;
0084 pair.second = psetDescription;
0085 }
0086
0087 void ConfigurationDescriptions::addWithDefaultLabel(ParameterSetDescription const& psetDescription) {
0088 std::string label;
0089 if (kService == baseType_) {
0090 label = pluginName_;
0091 } else if (kSource == baseType_) {
0092 label = "source";
0093 } else {
0094 label = defaultModuleLabel(pluginName_);
0095 }
0096 add(label, psetDescription);
0097 }
0098
0099 void ConfigurationDescriptions::addDefault(ParameterSetDescription const& psetDescription) {
0100 if (0 == strcmp(baseType_.c_str(), kSource) || 0 == strcmp(baseType_.c_str(), kService)) {
0101 if (!descriptions_.empty() || defaultDescDefined_ == true) {
0102 throw edm::Exception(edm::errors::LogicError,
0103 "ConfigurationDescriptions::addDefault, for a source or service only 1 "
0104 "ParameterSetDescription may be added\n");
0105 }
0106 }
0107
0108 defaultDescDefined_ = true;
0109 defaultDesc_ = psetDescription;
0110 }
0111
0112 ParameterSetDescription* ConfigurationDescriptions::defaultDescription() {
0113 if (defaultDescDefined_) {
0114 return &defaultDesc_;
0115 }
0116 return nullptr;
0117 }
0118
0119 ConfigurationDescriptions::iterator ConfigurationDescriptions::begin() { return descriptions_.begin(); }
0120
0121 ConfigurationDescriptions::iterator ConfigurationDescriptions::end() { return descriptions_.end(); }
0122
0123 void ConfigurationDescriptions::validate(ParameterSet& pset, std::string const& moduleLabel) const {
0124 ParameterSetDescription const* psetDesc = nullptr;
0125 for_all(descriptions_, std::bind(&matchLabel, std::placeholders::_1, std::cref(moduleLabel), std::ref(psetDesc)));
0126
0127
0128 if (psetDesc != nullptr) {
0129 psetDesc->validate(pset);
0130 }
0131
0132 else if (defaultDescDefined_) {
0133 defaultDesc_.validate(pset);
0134 }
0135
0136 else if (!descriptions_.empty()) {
0137 descriptions_[0].second.validate(pset);
0138 }
0139
0140
0141 }
0142
0143 void ConfigurationDescriptions::writeCfis(std::set<std::string>& usedCfiFileNames) const {
0144 bool wroteClassFile = false;
0145 cfi::Paths paths;
0146 if (defaultDescDefined_) {
0147 paths = writeClassFile(defaultDesc_, not descriptions_.empty());
0148 wroteClassFile = true;
0149 } else if (descriptions_.size() == 1) {
0150 paths = writeClassFile(descriptions_.begin()->second, true);
0151 wroteClassFile = true;
0152 }
0153 CfiOptions ops = wroteClassFile ? CfiOptions{cfi::Untyped{paths}} : CfiOptions{cfi::Typed{}};
0154 for (auto& d : descriptions_) {
0155 writeCfiForLabel(
0156 d, baseType_, pluginName_, (not defaultDescDefined_) and (1 == descriptions_.size()), ops, usedCfiFileNames);
0157 }
0158 }
0159
0160 namespace {
0161 std::string modifyPluginName(std::string iName) {
0162 auto found = iName.find("::");
0163 while (found != std::string::npos) {
0164 iName.replace(found, 2, "_");
0165 found = iName.find("::");
0166 }
0167
0168 const std::string toReplace("@<>,");
0169 found = iName.find_first_of(toReplace);
0170 while (found != std::string::npos) {
0171 iName.replace(found, 1, "_");
0172 found = iName.find_first_of(toReplace, found);
0173 }
0174 return iName;
0175 }
0176 }
0177
0178 cfi::Paths ConfigurationDescriptions::writeClassFile(ParameterSetDescription const& iDesc,
0179 bool willUseWithCfis) const {
0180 std::string pluginName = modifyPluginName(pluginName_);
0181
0182 std::string fileName = pluginName + ".py";
0183 std::ofstream outFile(fileName.c_str());
0184 if (outFile.fail()) {
0185 edm::Exception ex(edm::errors::LogicError, "Creating class file failed.\n");
0186 ex << "Opening a file '" << fileName << "' failed.\n";
0187 ex << "Error code from errno " << errno << ": " << std::strerror(errno) << "\n";
0188
0189 ex.addContext("Executing function ConfigurationDescriptions::writeDefault");
0190 throw ex;
0191 }
0192 outFile << "import FWCore.ParameterSet.Config as cms\n\n";
0193 outFile << "def " << pluginName
0194 << "(*args, **kwargs):\n"
0195 " mod = cms."
0196 << baseType_ << "('" << pluginName_ << "'";
0197
0198 bool startWithComma = true;
0199 int indentation = 4;
0200 CfiOptions ops = willUseWithCfis ? CfiOptions{cfi::ClassFile{}} : CfiOptions{cfi::Typed{}};
0201 iDesc.writeCfi(outFile, startWithComma, indentation, ops);
0202
0203 outFile << ")\n"
0204 " for a in args:\n"
0205 " mod.update_(a)\n"
0206 " mod.update_(kwargs)\n"
0207 " return mod\n";
0208
0209 outFile.close();
0210 return std::holds_alternative<cfi::ClassFile>(ops) ? std::get<cfi::ClassFile>(ops).releasePaths() : cfi::Paths{};
0211 }
0212
0213 void ConfigurationDescriptions::writeCfiForLabel(std::pair<std::string, ParameterSetDescription> const& labelAndDesc,
0214 std::string const& baseType,
0215 std::string const& pluginName,
0216 bool isSameAsDefault,
0217 CfiOptions& options,
0218 std::set<std::string>& usedCfiFileNames) {
0219 if (0 == strcmp(baseType.c_str(), kService) && labelAndDesc.first != pluginName) {
0220 throw edm::Exception(edm::errors::LogicError,
0221 "ConfigurationDescriptions::writeCfiForLabel\nFor a service the label and the plugin name "
0222 "must be the same.\n")
0223 << "This error is probably caused by an incorrect label being passed\nto the ConfigurationDescriptions::add "
0224 "function earlier.\n"
0225 << "plugin name = \"" << pluginName << "\" label name = \"" << labelAndDesc.first << "\"\n";
0226 }
0227
0228 std::string cfi_filename;
0229 if (0 == strcmp(baseType.c_str(), kSource)) {
0230 cfi_filename = pluginName + "_cfi.py";
0231 } else {
0232 cfi_filename = labelAndDesc.first + "_cfi.py";
0233 }
0234 if (!usedCfiFileNames.insert(cfi_filename).second) {
0235 edm::Exception ex(edm::errors::LogicError,
0236 "Two cfi files are being generated with the same name in the same directory.\n");
0237 ex << "The cfi file name is '" << cfi_filename << "' and\n"
0238 << "the module label is \'" << labelAndDesc.first << "\'.\n"
0239 << "This error is probably caused by an error in one or more fillDescriptions functions\n"
0240 << "where duplicate module labels are being passed to the ConfigurationDescriptions::add\n"
0241 << "function. All such module labels must be unique within a package.\n"
0242 << "If you do not want the generated cfi file and do not need more than one\n"
0243 << "description for a plugin, then a way to fix this is to use the addDefault\n"
0244 << "function instead of the add function.\n"
0245 << "There are 3 common ways this problem can happen.\n"
0246 << "1. This can happen when a module label is explicitly duplicated in one or more\n"
0247 << "fillDescriptions functions. Fix these by changing the module labels to be unique.\n"
0248 << "2. This can also happen when a module class is a template class and plugins are\n"
0249 << "defined by instantiations with differing template parameters and these plugins\n"
0250 << "share the same fillDescriptions function. Fix these by specializing the fillDescriptions\n"
0251 << "function for each template instantiation.\n"
0252 << "3. This can also happen when there is an inheritance heirarchy and multiple plugin modules\n"
0253 << "are defined using derived classes and the base class which share the same fillDescriptions\n"
0254 << "function. Fix these by redefining the fillDescriptions function in each derived class.\n";
0255 ex.addContext("Executing function ConfigurationDescriptions::writeCfiForLabel");
0256 throw ex;
0257 }
0258 std::ofstream outFile(cfi_filename.c_str());
0259 if (outFile.fail()) {
0260 edm::Exception ex(edm::errors::LogicError, "Creating cfi file failed.\n");
0261 ex << "Opening a file '" << cfi_filename << "' for module '" << labelAndDesc.first << "' failed.\n";
0262 ex << "Error code from errno " << errno << ": " << std::strerror(errno) << "\n";
0263
0264 ex.addContext("Executing function ConfigurationDescriptions::writeCfiForLabel");
0265 throw ex;
0266 }
0267
0268 bool startWithComma = true;
0269 if (not shouldWriteUntyped(options)) {
0270 outFile << "import FWCore.ParameterSet.Config as cms\n\n";
0271 outFile << labelAndDesc.first << " = cms." << baseType << "('" << pluginName << "'";
0272 } else {
0273 outFile << "import FWCore.ParameterSet.Config as cms\n\n";
0274
0275 auto pythonName = modifyPluginName(pluginName);
0276 outFile << "from ." << pythonName << " import " << pythonName << "\n\n";
0277 outFile << labelAndDesc.first << " = " << pythonName << "(";
0278 startWithComma = false;
0279 }
0280 if (not isSameAsDefault) {
0281 int indentation = 2;
0282 labelAndDesc.second.writeCfi(outFile, startWithComma, indentation, options);
0283 }
0284 outFile << ")\n";
0285
0286 outFile.close();
0287
0288 if (0 == strcmp(baseType.c_str(), kSource)) {
0289 std::cout << pluginName << "\n";
0290 } else {
0291 std::cout << labelAndDesc.first << "\n";
0292 }
0293 }
0294
0295 void ConfigurationDescriptions::print(std::ostream& os,
0296 std::string const& moduleLabel,
0297 bool brief,
0298 bool printOnlyLabels,
0299 size_t lineWidth,
0300 int indentation,
0301 int iPlugin) const {
0302 if (!brief) {
0303 if (!comment().empty()) {
0304 DocFormatHelper::wrapAndPrintText(os, comment(), indentation, lineWidth);
0305 }
0306 os << "\n";
0307 }
0308
0309 if (descriptions_.empty() && !defaultDescDefined_) {
0310 char oldFill = os.fill();
0311 indentation += DocFormatHelper::offsetModuleLabel();
0312 os << std::setfill(' ') << std::setw(indentation) << "";
0313 os << "There are no PSet descriptions defined for this plugin.\n";
0314 os << std::setfill(' ') << std::setw(indentation) << "";
0315 os << "PSets will not be validated and no cfi files will be generated.\n";
0316 os << std::setfill(oldFill);
0317 if (!brief)
0318 os << "\n";
0319 return;
0320 }
0321
0322 if (descriptions_.empty() && defaultDescDefined_ && defaultDesc_.isUnknown()) {
0323 indentation += DocFormatHelper::offsetModuleLabel();
0324 char oldFill = os.fill();
0325 os << std::setfill(' ') << std::setw(indentation) << "";
0326 os << "This plugin has not implemented the function which defines its\n";
0327 os << std::setfill(' ') << std::setw(indentation) << "";
0328 os << "configuration descriptions yet. No descriptions are available.\n";
0329 os << std::setfill(' ') << std::setw(indentation) << "";
0330 os << "Its PSets will not be validated, and no cfi files will be generated.\n";
0331 os << std::setfill(oldFill);
0332 if (!brief)
0333 os << "\n";
0334 return;
0335 }
0336
0337 if (!brief) {
0338 std::stringstream ss;
0339 if (defaultDescDefined_) {
0340 if (descriptions_.empty()) {
0341 ss << "This plugin has only one PSet description. "
0342 << "This description is always used to validate configurations. "
0343 << "Because this configuration has no label, no cfi files will be generated.";
0344 } else {
0345 ss << "This plugin has " << (descriptions_.size() + 1U) << " PSet descriptions. "
0346 << "The description used to validate a configuration is selected by "
0347 << "matching the module labels. If none match, then the last description, "
0348 << "which has no label, is selected. "
0349 << "A cfi file will be generated for each configuration with a module label.";
0350 }
0351 } else {
0352 if (descriptions_.size() == 1U) {
0353 ss << "This plugin has " << descriptions_.size() << " PSet description. "
0354 << "This description is always used to validate configurations. "
0355 << "The label below is used when generating the cfi file.";
0356 } else {
0357 ss << "This plugin has " << descriptions_.size() << " PSet descriptions. "
0358 << "The description used to validate a configuration is selected by "
0359 << "matching the module labels. If none match the first description below is used. "
0360 << "The module labels below are also used when generating the cfi files.";
0361 }
0362 }
0363 DocFormatHelper::wrapAndPrintText(os, ss.str(), indentation, lineWidth);
0364 os << "\n";
0365 }
0366
0367 indentation += DocFormatHelper::offsetModuleLabel();
0368
0369 DescriptionCounter counter;
0370 counter.iPlugin = iPlugin;
0371 counter.iSelectedModule = 0;
0372 counter.iModule = 0;
0373
0374 for (auto const& d : descriptions_) {
0375 printForLabel(d, os, moduleLabel, brief, printOnlyLabels, lineWidth, indentation, counter);
0376 }
0377
0378 if (defaultDescDefined_) {
0379 printForLabel(os,
0380 std::string("@default"),
0381 defaultDesc_,
0382 moduleLabel,
0383 brief,
0384 printOnlyLabels,
0385 lineWidth,
0386 indentation,
0387 counter);
0388 }
0389 }
0390
0391 void ConfigurationDescriptions::printForLabel(std::pair<std::string, ParameterSetDescription> const& labelAndDesc,
0392 std::ostream& os,
0393 std::string const& moduleLabel,
0394 bool brief,
0395 bool printOnlyLabels,
0396 size_t lineWidth,
0397 int indentation,
0398 DescriptionCounter& counter) const {
0399 printForLabel(os,
0400 labelAndDesc.first,
0401 labelAndDesc.second,
0402 moduleLabel,
0403 brief,
0404 printOnlyLabels,
0405 lineWidth,
0406 indentation,
0407 counter);
0408 }
0409
0410 void ConfigurationDescriptions::printForLabel(std::ostream& os,
0411 std::string const& label,
0412 ParameterSetDescription const& description,
0413 std::string const& moduleLabel,
0414 bool brief,
0415 bool printOnlyLabels,
0416 size_t lineWidth,
0417 int indentation,
0418 DescriptionCounter& counter) const {
0419 ++counter.iModule;
0420 if (!moduleLabel.empty() && label != moduleLabel)
0421 return;
0422 ++counter.iSelectedModule;
0423
0424 std::stringstream ss;
0425 ss << counter.iPlugin << "." << counter.iSelectedModule;
0426 std::string section = ss.str();
0427
0428 char oldFill = os.fill();
0429 os << std::setfill(' ') << std::setw(indentation) << "" << std::setfill(oldFill);
0430 os << section << " ";
0431 if (label == std::string("@default")) {
0432 os << "description without a module label\n";
0433 } else {
0434 if (!brief) {
0435 if (0 == strcmp(baseType_.c_str(), kSource) || 0 == strcmp(baseType_.c_str(), kService)) {
0436 os << "label: ";
0437 } else {
0438 os << "module label: ";
0439 }
0440 }
0441 os << label << "\n";
0442 }
0443
0444 if (!brief) {
0445 if (!description.comment().empty()) {
0446 DocFormatHelper::wrapAndPrintText(os, description.comment(), indentation, lineWidth - indentation);
0447 }
0448 os << "\n";
0449 }
0450 if (printOnlyLabels)
0451 return;
0452
0453 DocFormatHelper dfh;
0454 dfh.setBrief(brief);
0455 dfh.setLineWidth(lineWidth);
0456 dfh.setIndentation(indentation + DocFormatHelper::offsetTopLevelPSet());
0457 dfh.setSection(section);
0458 dfh.setParent(DocFormatHelper::TOP);
0459
0460 description.print(os, dfh);
0461 }
0462 }