Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2023-03-17 11:03:19

0001 // -*- C++ -*-
0002 //
0003 // Package:     FWCore/ParameterSet
0004 // Filename:    edmWriteConfigs.cpp
0005 //
0006 // This program takes as input the name of
0007 // a shared object library that is associated
0008 // with a plugin(s).  For each module that is
0009 // defined in that library it determines
0010 // which labels have predefined configurations
0011 // defined in the C++ code of the module.
0012 // For each of these, it fills a ParameterSetDescription
0013 // in memory.  Then it writes a file named
0014 // <module_label>_cfi.py containing the
0015 // default configuration for each of those module
0016 // labels.  It also prints to std::cout
0017 // the name of each of these module labels.
0018 //
0019 // It also does an analogous thing for sources
0020 // and services.  The only difference is that
0021 // each will have at most one description and
0022 // the output filename is <pluginName>_cfi.py.
0023 //
0024 // Original Author:  W. David Dagenhart
0025 //         Created:  10 December 2008
0026 
0027 #include "FWCore/Utilities/interface/ConvertException.h"
0028 #include "FWCore/Utilities/interface/Exception.h"
0029 #include "FWCore/Utilities/interface/TimeOfDay.h"
0030 #include "FWCore/PluginManager/interface/PluginManager.h"
0031 #include "FWCore/PluginManager/interface/standard.h"
0032 #include "FWCore/Utilities/interface/FileInPath.h"
0033 #include "FWCore/ParameterSet/interface/ParameterSetDescriptionFillerPluginFactory.h"
0034 #include "FWCore/Utilities/interface/Algorithms.h"
0035 #include "FWCore/PluginManager/interface/PluginInfo.h"
0036 #include "FWCore/ParameterSet/interface/ParameterSetDescriptionFillerBase.h"
0037 #include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h"
0038 #include "FWCore/PluginManager/interface/SharedLibrary.h"
0039 #include "FWCore/PluginManager/interface/PluginFactoryBase.h"
0040 #include "FWCore/PluginManager/interface/PluginFactoryManager.h"
0041 
0042 #include <boost/program_options.hpp>
0043 
0044 #include <set>
0045 #include <string>
0046 #include <iostream>
0047 #include <vector>
0048 #include <exception>
0049 #include <filesystem>
0050 #include <functional>
0051 #include <memory>
0052 #include <utility>
0053 #include "FWCore/Utilities/interface/Signal.h"
0054 #include <sstream>
0055 
0056 static char const* const kHelpOpt = "help";
0057 static char const* const kHelpCommandOpt = "help,h";
0058 static char const* const kLibraryOpt = "library";
0059 static char const* const kLibraryCommandOpt = "library,l";
0060 static char const* const kPathOpt = "path";
0061 static char const* const kPathCommandOpt = "path,p";
0062 static char const* const kPluginOpt = "plugin";
0063 static char const* const kPluginCommandOpt = "plugin,x";
0064 
0065 namespace {
0066   void getMatchingPluginNames(edmplugin::PluginInfo const& pluginInfo,
0067                               std::vector<std::string>& pluginNames,
0068                               std::string& previousName,
0069                               std::string const& library) {
0070     // We do not want duplicate plugin names and
0071     // can safely assume the calls to this function
0072     // occur with the pluginInfo argument sorted by
0073     // pluginName
0074     if (pluginInfo.name_ == previousName)
0075       return;
0076     previousName = pluginInfo.name_;
0077 
0078     // If the library matches save the name
0079     if (pluginInfo.loadable_.filename() == library) {
0080       pluginNames.push_back(pluginInfo.name_);
0081     }
0082   }
0083 
0084   void writeCfisForPlugin(std::string const& pluginName,
0085                           edm::ParameterSetDescriptionFillerPluginFactory* factory,
0086                           std::set<std::string>& usedCfiFileNames) {
0087     std::unique_ptr<edm::ParameterSetDescriptionFillerBase> filler(factory->create(pluginName));
0088 
0089     std::string baseType = filler->baseType();
0090 
0091     edm::ConfigurationDescriptions descriptions(filler->baseType(), pluginName);
0092 
0093     try {
0094       edm::convertException::wrap([&]() { filler->fill(descriptions); });
0095     } catch (cms::Exception& e) {
0096       std::ostringstream ost;
0097       ost << "Filling ParameterSetDescriptions for module of base type " << baseType << " with plugin name \'"
0098           << pluginName << "\'";
0099       e.addContext(ost.str());
0100       throw;
0101     }
0102 
0103     try {
0104       edm::convertException::wrap([&]() { descriptions.writeCfis(usedCfiFileNames); });
0105     } catch (cms::Exception& e) {
0106       std::ostringstream ost;
0107       ost << "Writing cfi files using ParameterSetDescriptions for module of base type " << baseType
0108           << " with plugin name \'" << pluginName << "\'";
0109       e.addContext(ost.str());
0110       throw;
0111     }
0112   }
0113 
0114   struct Listener {
0115     typedef std::pair<std::string, std::string> NameAndType;
0116     typedef std::vector<NameAndType> NameAndTypes;
0117 
0118     void newFactory(edmplugin::PluginFactoryBase const* iBase) {
0119       using std::placeholders::_1;
0120       using std::placeholders::_2;
0121       iBase->newPluginAdded_.connect(std::bind(std::mem_fn(&Listener::newPlugin), this, _1, _2));
0122     }
0123     void newPlugin(std::string const& iCategory, edmplugin::PluginInfo const& iInfo) {
0124       nameAndTypes_.push_back(NameAndType(iInfo.name_, iCategory));
0125     }
0126     NameAndTypes nameAndTypes_;
0127   };
0128 
0129   void getPluginsMatchingCategory(Listener::NameAndType const& nameAndType,
0130                                   std::vector<std::string>& pluginNames,
0131                                   std::string const& category) {
0132     if (category == nameAndType.second) {
0133       pluginNames.push_back(nameAndType.first);
0134     }
0135   }
0136 }  // namespace
0137 
0138 int main(int argc, char** argv) try {
0139   edm::FileInPath::disableFileLookup();
0140 
0141   using std::placeholders::_1;
0142   std::filesystem::path initialWorkingDirectory = std::filesystem::current_path();
0143 
0144   // Process the command line arguments
0145   std::string descString(argv[0]);
0146   descString += " [options] [[--";
0147   descString += kLibraryOpt;
0148   descString += "] library_filename]\n\n";
0149   descString += "Generates and writes configuration files that have the suffix _cfi.py.\n";
0150   descString += "One configuration file is written for each configuration defined with a\n";
0151   descString += "module label in the fillDescriptions functions of the plugins in the library.\n";
0152   descString += "Silently does nothing if the library is not in the edmplugincache, does not\n";
0153   descString += "exist at all, or the plugins in the library have not defined any configurations.\n";
0154   descString += "Instead of specifying a library, there is also an option to specify a plugin.\n\n";
0155   descString += "Allowed options";
0156   boost::program_options::options_description desc(descString);
0157 
0158   // clang-format off
0159   desc.add_options()(kHelpCommandOpt, "produce help message")(
0160       kLibraryCommandOpt, boost::program_options::value<std::string>(), "library filename")(
0161       kPathCommandOpt,
0162       "When this option is set, the library filename "
0163       "is interpreted as a relative or absolute path. If there are no directories in "
0164       "the library filename, then it looks for the library file in the current directory. "
0165       "Fails with an error message if the path does not lead to a library file that exists "
0166       "or can be loaded. "
0167       "If this option is not set, then the library filename should only be "
0168       "a filename without any directories.  In that case, it is assumed "
0169       "the build system has already put the library file in the "
0170       "appropriate place, built the edmplugincache, and the PluginManager "
0171       "is used to find and load the library.")(
0172       kPluginCommandOpt,
0173       boost::program_options::value<std::string>(),
0174       "plugin name. You must specify either a library or plugin, but not both.");
0175   // clang-format on
0176 
0177   boost::program_options::positional_options_description p;
0178   p.add(kLibraryOpt, -1);
0179 
0180   boost::program_options::variables_map vm;
0181   try {
0182     store(boost::program_options::command_line_parser(argc, argv).options(desc).positional(p).run(), vm);
0183     notify(vm);
0184   } catch (boost::program_options::error const& iException) {
0185     std::cerr << "Exception from command line processing: " << iException.what() << "\n";
0186     std::cerr << desc << std::endl;
0187     return 1;
0188   }
0189 
0190   if (vm.count(kHelpOpt)) {
0191     std::cout << desc << std::endl;
0192     return 0;
0193   }
0194 
0195   std::string library;
0196   std::string requestedPlugin;
0197 
0198   try {
0199     edm::convertException::wrap([&]() {
0200       if (vm.count(kLibraryOpt) && vm.count(kPluginOpt)) {
0201         throw cms::Exception("Command Line Arguments")
0202             << "Both library and plugin specified. You must specify one or the other, but not both.";
0203       }
0204       if (vm.count(kLibraryOpt)) {
0205         library = vm[kLibraryOpt].as<std::string>();
0206       } else if (vm.count(kPluginOpt)) {
0207         requestedPlugin = vm[kPluginOpt].as<std::string>();
0208       } else {
0209         throw cms::Exception("Command Line Arguments")
0210             << "No library or plugin specified. You must specify one or the other (but not both).";
0211       }
0212 
0213       edm::ParameterSetDescriptionFillerPluginFactory* factory;
0214       std::vector<std::string> pluginNames;
0215 
0216       if (vm.count(kPluginOpt)) {
0217         edmplugin::PluginManager::configure(edmplugin::standard::config());
0218         factory = edm::ParameterSetDescriptionFillerPluginFactory::get();
0219         pluginNames.push_back(requestedPlugin);
0220       }
0221       // If using the PluginManager to find the library
0222       else if (!vm.count(kPathOpt)) {
0223         // From the PluginManager get a reference to a
0224         // a vector of PlugInInfo's for plugins defining ParameterSetDescriptions.
0225         // Each PlugInInfo contains the plugin name and library name.
0226         edmplugin::PluginManager::configure(edmplugin::standard::config());
0227         typedef edmplugin::PluginManager::CategoryToInfos CatToInfos;
0228         CatToInfos const& catToInfos = edmplugin::PluginManager::get()->categoryToInfos();
0229         factory = edm::ParameterSetDescriptionFillerPluginFactory::get();
0230         CatToInfos::const_iterator itPlugins = catToInfos.find(factory->category());
0231 
0232         // No plugins in this category at all
0233         if (itPlugins == catToInfos.end())
0234           return;
0235 
0236         std::vector<edmplugin::PluginInfo> const& infos = itPlugins->second;
0237         std::string previousName;
0238 
0239         edm::for_all(
0240             infos,
0241             std::bind(&getMatchingPluginNames, _1, std::ref(pluginNames), std::ref(previousName), std::cref(library)));
0242 
0243       } else {
0244         // the library name is part of a path
0245 
0246         Listener listener;
0247         edmplugin::PluginFactoryManager* pfm = edmplugin::PluginFactoryManager::get();
0248         pfm->newFactory_.connect(std::bind(std::mem_fn(&Listener::newFactory), &listener, _1));
0249         edm::for_all(*pfm, std::bind(std::mem_fn(&Listener::newFactory), &listener, _1));
0250 
0251         std::filesystem::path loadableFile(library);
0252 
0253         // If it is just a filename without any directories,
0254         // then turn it into an absolute path using the current
0255         // directory when the program starts.  This prevents
0256         // the function that loads the library from using
0257         // LD_LIBRARY_PATH or some other location that it searches
0258         // to find some library that has the same name, but
0259         // was not the intended target.
0260         if (loadableFile.filename() == loadableFile.string()) {
0261           loadableFile = initialWorkingDirectory / loadableFile;
0262         }
0263 
0264         // This really loads the library into the program
0265         // The listener records the plugin names and categories as they are loaded
0266         try {
0267           edmplugin::SharedLibrary lib(loadableFile);
0268         } catch (cms::Exception const& iException) {
0269           if (iException.category() == "PluginLibraryLoadError") {
0270             std::cerr << "error: edmWriteConfigs caught an exception while loading a plugin library.\n"
0271                       << "The executable will return success (0) so scram will continue,\n"
0272                       << "but no cfi files will be written.\n"
0273                       << iException.what() << std::endl;
0274             return;
0275           } else {
0276             throw;
0277           }
0278         }
0279 
0280         // We do not care about PluginCapabilities category so do not bother to try to include them
0281 
0282         factory = edm::ParameterSetDescriptionFillerPluginFactory::get();
0283 
0284         edm::for_all(listener.nameAndTypes_,
0285                      std::bind(&getPluginsMatchingCategory, _1, std::ref(pluginNames), std::cref(factory->category())));
0286       }
0287 
0288       std::set<std::string> usedCfiFileNames;
0289       edm::for_all(pluginNames, std::bind(&writeCfisForPlugin, _1, factory, std::ref(usedCfiFileNames)));
0290     });
0291   } catch (cms::Exception& iException) {
0292     if (!library.empty()) {
0293       std::ostringstream ost;
0294       ost << "Processing library " << library;
0295       iException.addContext(ost.str());
0296     }
0297     iException.addContext("Running executable \"edmWriteConfigs\"");
0298     std::cerr << "----- Begin Fatal Exception " << std::setprecision(0) << edm::TimeOfDay()
0299               << "-----------------------\n"
0300               << iException.explainSelf()
0301               << "----- End Fatal Exception -------------------------------------------------" << std::endl;
0302     return 1;
0303   }
0304   return 0;
0305 } catch (cms::Exception const& e) {
0306   std::cerr << e.explainSelf() << std::endl;
0307   return 1;
0308 } catch (std::exception const& e) {
0309   std::cerr << e.what() << std::endl;
0310   return 1;
0311 }