Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2024-04-06 12:12:51

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