Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2021-02-14 13:28:55

0001 // -*- C++ -*-
0002 //
0003 // Package:     ParameterSet
0004 // Class  :     ConfigurationDescriptions
0005 //
0006 // Implementation:
0007 //     <Notes on implementation>
0008 //
0009 // Original Author:  W. David Dagenhart
0010 //         Created:  17 December 2008
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 }  // namespace
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     // To minimize the number of copies involved create an empty description first
0078     // and push it into the vector.  Then perform the copy.
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     // If there is a matching label
0128     if (psetDesc != nullptr) {
0129       psetDesc->validate(pset);
0130     }
0131     // Is there an explicit description to be used for a non standard label
0132     else if (defaultDescDefined_) {
0133       defaultDesc_.validate(pset);
0134     }
0135     // Otherwise use the first one.
0136     else if (!descriptions_.empty()) {
0137       descriptions_[0].second.validate(pset);
0138     }
0139     // It is possible for no descriptions to be defined and no validation occurs
0140     // for this module ever.
0141   }
0142 
0143   void ConfigurationDescriptions::writeCfis(std::set<std::string>& usedCfiFileNames) const {
0144     for_all(descriptions_,
0145             std::bind(&ConfigurationDescriptions::writeCfiForLabel,
0146                       std::placeholders::_1,
0147                       std::cref(baseType_),
0148                       std::cref(pluginName_),
0149                       std::ref(usedCfiFileNames)));
0150   }
0151 
0152   void ConfigurationDescriptions::writeCfiForLabel(std::pair<std::string, ParameterSetDescription> const& labelAndDesc,
0153                                                    std::string const& baseType,
0154                                                    std::string const& pluginName,
0155                                                    std::set<std::string>& usedCfiFileNames) {
0156     if (0 == strcmp(baseType.c_str(), kService) && labelAndDesc.first != pluginName) {
0157       throw edm::Exception(edm::errors::LogicError,
0158                            "ConfigurationDescriptions::writeCfiForLabel\nFor a service the label and the plugin name "
0159                            "must be the same.\n")
0160           << "This error is probably caused by an incorrect label being passed\nto the ConfigurationDescriptions::add "
0161              "function earlier.\n"
0162           << "plugin name = \"" << pluginName << "\"  label name = \"" << labelAndDesc.first << "\"\n";
0163     }
0164 
0165     std::string cfi_filename;
0166     if (0 == strcmp(baseType.c_str(), kSource)) {
0167       cfi_filename = pluginName + "_cfi.py";
0168     } else {
0169       cfi_filename = labelAndDesc.first + "_cfi.py";
0170     }
0171     if (!usedCfiFileNames.insert(cfi_filename).second) {
0172       edm::Exception ex(edm::errors::LogicError,
0173                         "Two cfi files are being generated with the same name in the same directory.\n");
0174       ex << "The cfi file name is '" << cfi_filename << "' and\n"
0175          << "the module label is \'" << labelAndDesc.first << "\'.\n"
0176          << "This error is probably caused by an error in one or more fillDescriptions functions\n"
0177          << "where duplicate module labels are being passed to the ConfigurationDescriptions::add\n"
0178          << "function. All such module labels must be unique within a package.\n"
0179          << "If you do not want the generated cfi file and do not need more than one\n"
0180          << "description for a plugin, then a way to fix this is to use the addDefault\n"
0181          << "function instead of the add function.\n"
0182          << "There are 3 common ways this problem can happen.\n"
0183          << "1. This can happen when a module label is explicitly duplicated in one or more\n"
0184          << "fillDescriptions functions. Fix these by changing the module labels to be unique.\n"
0185          << "2. This can also happen when a module class is a template class and plugins are\n"
0186          << "defined by instantiations with differing template parameters and these plugins\n"
0187          << "share the same fillDescriptions function. Fix these by specializing the fillDescriptions\n"
0188          << "function for each template instantiation.\n"
0189          << "3. This can also happen when there is an inheritance heirarchy and multiple plugin modules\n"
0190          << "are defined using derived classes and the base class which share the same fillDescriptions\n"
0191          << "function. Fix these by redefining the fillDescriptions function in each derived class.\n";
0192       ex.addContext("Executing function ConfigurationDescriptions::writeCfiForLabel");
0193       throw ex;
0194     }
0195     std::ofstream outFile(cfi_filename.c_str());
0196     if (outFile.fail()) {
0197       edm::Exception ex(edm::errors::LogicError, "Creating cfi file failed.\n");
0198       ex << "Opening a file '" << cfi_filename << "' for module '" << labelAndDesc.first << "' failed.\n";
0199       ex << "Error code from errno " << errno << ": " << std::strerror(errno) << "\n";
0200 
0201       ex.addContext("Executing function ConfigurationDescriptions::writeCfiForLabel");
0202       throw ex;
0203     }
0204 
0205     outFile << "import FWCore.ParameterSet.Config as cms\n\n";
0206     outFile << labelAndDesc.first << " = cms." << baseType << "('" << pluginName << "'";
0207 
0208     bool startWithComma = true;
0209     int indentation = 2;
0210     labelAndDesc.second.writeCfi(outFile, startWithComma, indentation);
0211 
0212     outFile << ")\n";
0213 
0214     outFile.close();
0215 
0216     if (0 == strcmp(baseType.c_str(), kSource)) {
0217       std::cout << pluginName << "\n";
0218     } else {
0219       std::cout << labelAndDesc.first << "\n";
0220     }
0221   }
0222 
0223   void ConfigurationDescriptions::print(std::ostream& os,
0224                                         std::string const& moduleLabel,
0225                                         bool brief,
0226                                         bool printOnlyLabels,
0227                                         size_t lineWidth,
0228                                         int indentation,
0229                                         int iPlugin) const {
0230     if (!brief) {
0231       if (!comment().empty()) {
0232         DocFormatHelper::wrapAndPrintText(os, comment(), indentation, lineWidth);
0233       }
0234       os << "\n";
0235     }
0236 
0237     if (descriptions_.empty() && !defaultDescDefined_) {
0238       char oldFill = os.fill();
0239       indentation += DocFormatHelper::offsetModuleLabel();
0240       os << std::setfill(' ') << std::setw(indentation) << "";
0241       os << "There are no PSet descriptions defined for this plugin.\n";
0242       os << std::setfill(' ') << std::setw(indentation) << "";
0243       os << "PSets will not be validated and no cfi files will be generated.\n";
0244       os << std::setfill(oldFill);
0245       if (!brief)
0246         os << "\n";
0247       return;
0248     }
0249 
0250     if (descriptions_.empty() && defaultDescDefined_ && defaultDesc_.isUnknown()) {
0251       indentation += DocFormatHelper::offsetModuleLabel();
0252       char oldFill = os.fill();
0253       os << std::setfill(' ') << std::setw(indentation) << "";
0254       os << "This plugin has not implemented the function which defines its\n";
0255       os << std::setfill(' ') << std::setw(indentation) << "";
0256       os << "configuration descriptions yet. No descriptions are available.\n";
0257       os << std::setfill(' ') << std::setw(indentation) << "";
0258       os << "Its PSets will not be validated, and no cfi files will be generated.\n";
0259       os << std::setfill(oldFill);
0260       if (!brief)
0261         os << "\n";
0262       return;
0263     }
0264 
0265     if (!brief) {
0266       std::stringstream ss;
0267       if (defaultDescDefined_) {
0268         if (descriptions_.empty()) {
0269           ss << "This plugin has only one PSet description. "
0270              << "This description is always used to validate configurations. "
0271              << "Because this configuration has no label, no cfi files will be generated.";
0272         } else {
0273           ss << "This plugin has " << (descriptions_.size() + 1U) << " PSet descriptions. "
0274              << "The description used to validate a configuration is selected by "
0275              << "matching the module labels. If none match, then the last description, "
0276              << "which has no label, is selected. "
0277              << "A cfi file will be generated for each configuration with a module label.";
0278         }
0279       } else {
0280         if (descriptions_.size() == 1U) {
0281           ss << "This plugin has " << descriptions_.size() << " PSet description. "
0282              << "This description is always used to validate configurations. "
0283              << "The label below is used when generating the cfi file.";
0284         } else {
0285           ss << "This plugin has " << descriptions_.size() << " PSet descriptions. "
0286              << "The description used to validate a configuration is selected by "
0287              << "matching the module labels. If none match the first description below is used. "
0288              << "The module labels below are also used when generating the cfi files.";
0289         }
0290       }
0291       DocFormatHelper::wrapAndPrintText(os, ss.str(), indentation, lineWidth);
0292       os << "\n";
0293     }
0294 
0295     indentation += DocFormatHelper::offsetModuleLabel();
0296 
0297     DescriptionCounter counter;
0298     counter.iPlugin = iPlugin;
0299     counter.iSelectedModule = 0;
0300     counter.iModule = 0;
0301 
0302     for (auto const& d : descriptions_) {
0303       printForLabel(d, os, moduleLabel, brief, printOnlyLabels, lineWidth, indentation, counter);
0304     }
0305 
0306     if (defaultDescDefined_) {
0307       printForLabel(os,
0308                     std::string("@default"),
0309                     defaultDesc_,
0310                     moduleLabel,
0311                     brief,
0312                     printOnlyLabels,
0313                     lineWidth,
0314                     indentation,
0315                     counter);
0316     }
0317   }
0318 
0319   void ConfigurationDescriptions::printForLabel(std::pair<std::string, ParameterSetDescription> const& labelAndDesc,
0320                                                 std::ostream& os,
0321                                                 std::string const& moduleLabel,
0322                                                 bool brief,
0323                                                 bool printOnlyLabels,
0324                                                 size_t lineWidth,
0325                                                 int indentation,
0326                                                 DescriptionCounter& counter) const {
0327     printForLabel(os,
0328                   labelAndDesc.first,
0329                   labelAndDesc.second,
0330                   moduleLabel,
0331                   brief,
0332                   printOnlyLabels,
0333                   lineWidth,
0334                   indentation,
0335                   counter);
0336   }
0337 
0338   void ConfigurationDescriptions::printForLabel(std::ostream& os,
0339                                                 std::string const& label,
0340                                                 ParameterSetDescription const& description,
0341                                                 std::string const& moduleLabel,
0342                                                 bool brief,
0343                                                 bool printOnlyLabels,
0344                                                 size_t lineWidth,
0345                                                 int indentation,
0346                                                 DescriptionCounter& counter) const {
0347     ++counter.iModule;
0348     if (!moduleLabel.empty() && label != moduleLabel)
0349       return;
0350     ++counter.iSelectedModule;
0351 
0352     std::stringstream ss;
0353     ss << counter.iPlugin << "." << counter.iSelectedModule;
0354     std::string section = ss.str();
0355 
0356     char oldFill = os.fill();
0357     os << std::setfill(' ') << std::setw(indentation) << "" << std::setfill(oldFill);
0358     os << section << " ";
0359     if (label == std::string("@default")) {
0360       os << "description without a module label\n";
0361     } else {
0362       if (!brief) {
0363         if (0 == strcmp(baseType_.c_str(), kSource) || 0 == strcmp(baseType_.c_str(), kService)) {
0364           os << "label: ";
0365         } else {
0366           os << "module label: ";
0367         }
0368       }
0369       os << label << "\n";
0370     }
0371 
0372     if (!brief) {
0373       if (!description.comment().empty()) {
0374         DocFormatHelper::wrapAndPrintText(os, description.comment(), indentation, lineWidth - indentation);
0375       }
0376       os << "\n";
0377     }
0378     if (printOnlyLabels)
0379       return;
0380 
0381     DocFormatHelper dfh;
0382     dfh.setBrief(brief);
0383     dfh.setLineWidth(lineWidth);
0384     dfh.setIndentation(indentation + DocFormatHelper::offsetTopLevelPSet());
0385     dfh.setSection(section);
0386     dfh.setParent(DocFormatHelper::TOP);
0387 
0388     description.print(os, dfh);
0389   }
0390 }  // namespace edm