Skip to content

Commit 35ea1cd

Browse files
authored
Merge pull request cms-sw#42650 from kpedro88/cmsArgParse
Improve command-line argument support for Python configs in cmsRun and edm tools
2 parents 0afcc4f + 9d49116 commit 35ea1cd

File tree

144 files changed

+1303
-656
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

144 files changed

+1303
-656
lines changed

FWCore/Framework/BuildFile.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<use name="boost"/>
2+
<use name="boost_program_options"/>
23
<use name="rootcore"/>
34
<use name="tbb"/>
45
<use name="DataFormats/Common"/>

FWCore/Framework/bin/cmsRun.cpp

Lines changed: 59 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ This is a generic main that can be used with any plugin and a
33
PSet script. See notes in EventProcessor.cpp for details about it.
44
----------------------------------------------------------------------*/
55

6+
#include "FWCore/Framework/interface/CmsRunParser.h"
67
#include "FWCore/Framework/interface/EventProcessor.h"
78
#include "FWCore/Framework/interface/defaultCmsRunServices.h"
89
#include "FWCore/MessageLogger/interface/ExceptionMessages.h"
@@ -30,7 +31,6 @@ PSet script. See notes in EventProcessor.cpp for details about it.
3031

3132
#include "TError.h"
3233

33-
#include "boost/program_options.hpp"
3434
#include "oneapi/tbb/task_arena.h"
3535

3636
#include <cstring>
@@ -41,24 +41,6 @@ PSet script. See notes in EventProcessor.cpp for details about it.
4141
#include <string>
4242
#include <vector>
4343

44-
//Command line parameters
45-
static char const* const kParameterSetOpt = "parameter-set";
46-
static char const* const kPythonOpt = "pythonOptions";
47-
static char const* const kParameterSetCommandOpt = "parameter-set,p";
48-
static char const* const kJobreportCommandOpt = "jobreport,j";
49-
static char const* const kJobreportOpt = "jobreport";
50-
static char const* const kEnableJobreportCommandOpt = "enablejobreport,e";
51-
static const char* const kEnableJobreportOpt = "enablejobreport";
52-
static char const* const kJobModeCommandOpt = "mode,m";
53-
static char const* const kJobModeOpt = "mode";
54-
static char const* const kNumberOfThreadsCommandOpt = "numThreads,n";
55-
static char const* const kNumberOfThreadsOpt = "numThreads";
56-
static char const* const kSizeOfStackForThreadCommandOpt = "sizeOfStackForThreadsInKB,s";
57-
static char const* const kSizeOfStackForThreadOpt = "sizeOfStackForThreadsInKB";
58-
static char const* const kHelpOpt = "help";
59-
static char const* const kHelpCommandOpt = "help,h";
60-
static char const* const kStrictOpt = "strict";
61-
6244
// -----------------------------------------------
6345
namespace {
6446
class EventProcessorWithSentry {
@@ -101,10 +83,9 @@ namespace {
10183
private:
10284
edm::EventProcessor* ep_;
10385
};
104-
10586
} // namespace
10687

107-
int main(int argc, char* argv[]) {
88+
int main(int argc, const char* argv[]) {
10889
edm::TimingServiceBase::jobStarted();
10990

11091
int returnCode = 0;
@@ -116,7 +97,7 @@ int main(int argc, char* argv[]) {
11697
edm::s_defaultSizeOfStackForThreadsInKB * 1024);
11798
std::shared_ptr<edm::Presence> theMessageServicePresence;
11899
std::unique_ptr<std::ofstream> jobReportStreamPtr;
119-
std::shared_ptr<edm::serviceregistry::ServiceWrapper<edm::JobReport> > jobRep;
100+
std::shared_ptr<edm::serviceregistry::ServiceWrapper<edm::JobReport>> jobRep;
120101
EventProcessorWithSentry proc;
121102

122103
try {
@@ -145,74 +126,39 @@ int main(int argc, char* argv[]) {
145126
std::shared_ptr<edm::Presence>(edm::PresenceFactory::get()->makePresence("SingleThreadMSPresence").release());
146127

147128
context = "Processing command line arguments";
148-
std::string descString(argv[0]);
149-
descString += " [options] [--";
150-
descString += kParameterSetOpt;
151-
descString += "] config_file \nAllowed options";
152-
boost::program_options::options_description desc(descString);
153-
154-
// clang-format off
155-
desc.add_options()(kHelpCommandOpt, "produce help message")(
156-
kParameterSetCommandOpt, boost::program_options::value<std::string>(), "configuration file")(
157-
kJobreportCommandOpt,
158-
boost::program_options::value<std::string>(),
159-
"file name to use for a job report file: default extension is .xml")(
160-
kEnableJobreportCommandOpt, "enable job report files (if any) specified in configuration file")(
161-
kJobModeCommandOpt,
162-
boost::program_options::value<std::string>(),
163-
"Job Mode for MessageLogger defaults - default mode is grid")(
164-
kNumberOfThreadsCommandOpt,
165-
boost::program_options::value<unsigned int>(),
166-
"Number of threads to use in job (0 is use all CPUs)")(
167-
kSizeOfStackForThreadCommandOpt,
168-
boost::program_options::value<unsigned int>(),
169-
"Size of stack in KB to use for extra threads (0 is use system default size)")(kStrictOpt, "strict parsing");
170-
// clang-format on
171-
172-
// anything at the end will be ignored, and sent to python
173-
boost::program_options::positional_options_description p;
174-
p.add(kParameterSetOpt, 1).add(kPythonOpt, -1);
175-
176-
// This --fwk option is not used anymore, but I'm leaving it around as
177-
// it might be useful again in the future for code development
178-
// purposes. We originally used it when implementing the boost
179-
// state machine code.
180-
boost::program_options::options_description hidden("hidden options");
181-
hidden.add_options()("fwk", "For use only by Framework Developers")(
182-
kPythonOpt,
183-
boost::program_options::value<std::vector<std::string> >(),
184-
"options at the end to be passed to python");
185-
186-
boost::program_options::options_description all_options("All Options");
187-
all_options.add(desc).add(hidden);
188-
189-
boost::program_options::variables_map vm;
190-
try {
191-
store(boost::program_options::command_line_parser(argc, argv).options(all_options).positional(p).run(), vm);
192-
notify(vm);
193-
} catch (boost::program_options::error const& iException) {
194-
edm::LogAbsolute("CommandLineProcessing")
195-
<< "cmsRun: Error while trying to process command line arguments:\n"
196-
<< iException.what() << "\nFor usage and an options list, please do 'cmsRun --help'.";
197-
return edm::errors::CommandLineProcessing;
198-
}
199-
200-
if (vm.count(kHelpOpt)) {
201-
std::cout << desc << std::endl;
202-
if (!vm.count(kParameterSetOpt))
129+
edm::CmsRunParser parser(argv[0]);
130+
131+
const auto& parserOutput = parser.parse(argc, argv);
132+
//return with exit code from parser
133+
if (edm::CmsRunParser::hasExit(parserOutput))
134+
return edm::CmsRunParser::getExit(parserOutput);
135+
auto vm = edm::CmsRunParser::getVM(parserOutput);
136+
137+
std::string cmdString;
138+
std::string fileName;
139+
if (vm.count(edm::CmsRunParser::kCmdOpt)) {
140+
cmdString = vm[edm::CmsRunParser::kCmdOpt].as<std::string>();
141+
if (vm.count(edm::CmsRunParser::kParameterSetOpt)) {
142+
edm::LogAbsolute("CommandLineProcessing") << "cmsRun: Error while trying to process command line arguments:\n"
143+
<< "cannot use '-c [command line input]' with 'config_file'\n"
144+
<< "For usage and an options list, please do 'cmsRun --help'.";
203145
edm::HaltMessageLogging();
204-
return 0;
205-
}
206-
207-
if (!vm.count(kParameterSetOpt)) {
146+
return edm::errors::CommandLineProcessing;
147+
}
148+
} else if (!vm.count(edm::CmsRunParser::kParameterSetOpt)) {
208149
edm::LogAbsolute("ConfigFileNotFound") << "cmsRun: No configuration file given.\n"
209150
<< "For usage and an options list, please do 'cmsRun --help'.";
210151
edm::HaltMessageLogging();
211152
return edm::errors::ConfigFileNotFound;
153+
} else
154+
fileName = vm[edm::CmsRunParser::kParameterSetOpt].as<std::string>();
155+
std::vector<std::string> pythonOptValues;
156+
if (vm.count(edm::CmsRunParser::kPythonOpt)) {
157+
pythonOptValues = vm[edm::CmsRunParser::kPythonOpt].as<std::vector<std::string>>();
212158
}
213-
std::string fileName(vm[kParameterSetOpt].as<std::string>());
159+
pythonOptValues.insert(pythonOptValues.begin(), fileName);
214160

215-
if (vm.count(kStrictOpt)) {
161+
if (vm.count(edm::CmsRunParser::kStrictOpt)) {
216162
//edm::setStrictParsing(true);
217163
edm::LogSystem("CommandLineProcessing") << "Strict configuration processing is now done from python";
218164
}
@@ -221,9 +167,9 @@ int main(int argc, char* argv[]) {
221167
// Decide whether to enable creation of job report xml file
222168
// We do this first so any errors will be reported
223169
std::string jobReportFile;
224-
if (vm.count(kJobreportOpt)) {
225-
jobReportFile = vm[kJobreportOpt].as<std::string>();
226-
} else if (vm.count(kEnableJobreportOpt)) {
170+
if (vm.count(edm::CmsRunParser::kJobreportOpt)) {
171+
jobReportFile = vm[edm::CmsRunParser::kJobreportOpt].as<std::string>();
172+
} else if (vm.count(edm::CmsRunParser::kEnableJobreportOpt)) {
227173
jobReportFile = "FrameworkJobReport.xml";
228174
}
229175
jobReportStreamPtr = jobReportFile.empty() ? nullptr : std::make_unique<std::ofstream>(jobReportFile.c_str());
@@ -234,15 +180,32 @@ int main(int argc, char* argv[]) {
234180
jobRep.reset(new edm::serviceregistry::ServiceWrapper<edm::JobReport>(std::move(jobRepPtr)));
235181
edm::ServiceToken jobReportToken = edm::ServiceRegistry::createContaining(jobRep);
236182

237-
context = "Processing the python configuration file named ";
238-
context += fileName;
183+
if (!fileName.empty()) {
184+
context = "Processing the python configuration file named ";
185+
context += fileName;
186+
} else {
187+
context = "Processing the python configuration from command line ";
188+
context += cmdString;
189+
}
239190
std::shared_ptr<edm::ProcessDesc> processDesc;
240191
try {
241-
std::unique_ptr<edm::ParameterSet> parameterSet = edm::readConfig(fileName, argc, argv);
242-
processDesc.reset(new edm::ProcessDesc(std::move(parameterSet)));
192+
std::unique_ptr<edm::ParameterSet> parameterSet;
193+
if (!fileName.empty())
194+
parameterSet = edm::readConfig(fileName, pythonOptValues);
195+
else
196+
edm::makeParameterSets(cmdString, parameterSet);
197+
processDesc = std::make_shared<edm::ProcessDesc>(std::move(parameterSet));
243198
} catch (edm::Exception const&) {
244199
throw;
245200
} catch (cms::Exception& iException) {
201+
//check for "SystemExit: 0" on second line
202+
const std::string& sysexit0("SystemExit: 0");
203+
const auto& msg = iException.message();
204+
size_t pos2 = msg.find('\n');
205+
if (pos2 != std::string::npos and (msg.size() - (pos2 + 1)) > sysexit0.size() and
206+
msg.compare(pos2 + 1, sysexit0.size(), sysexit0) == 0)
207+
return 0;
208+
246209
edm::Exception e(edm::errors::ConfigFileReadError, "", iException);
247210
throw e;
248211
}
@@ -264,11 +227,11 @@ int main(int argc, char* argv[]) {
264227
auto threadsInfo = threadOptions(*pset);
265228

266229
// check the command line options
267-
if (vm.count(kNumberOfThreadsOpt)) {
268-
threadsInfo.nThreads_ = vm[kNumberOfThreadsOpt].as<unsigned int>();
230+
if (vm.count(edm::CmsRunParser::kNumberOfThreadsOpt)) {
231+
threadsInfo.nThreads_ = vm[edm::CmsRunParser::kNumberOfThreadsOpt].as<unsigned int>();
269232
}
270-
if (vm.count(kSizeOfStackForThreadOpt)) {
271-
threadsInfo.stackSize_ = vm[kSizeOfStackForThreadOpt].as<unsigned int>();
233+
if (vm.count(edm::CmsRunParser::kSizeOfStackForThreadOpt)) {
234+
threadsInfo.stackSize_ = vm[edm::CmsRunParser::kSizeOfStackForThreadOpt].as<unsigned int>();
272235
}
273236

274237
// if needed, re-initialise TBB
@@ -290,8 +253,8 @@ int main(int argc, char* argv[]) {
290253

291254
context = "Setting MessageLogger defaults";
292255
// Decide what mode of hardcoded MessageLogger defaults to use
293-
if (vm.count(kJobModeOpt)) {
294-
std::string jobMode = vm[kJobModeOpt].as<std::string>();
256+
if (vm.count(edm::CmsRunParser::kJobModeOpt)) {
257+
std::string jobMode = vm[edm::CmsRunParser::kJobModeOpt].as<std::string>();
295258
edm::MessageDrop::instance()->jobMode = jobMode;
296259
}
297260

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#ifndef FWCore_Framework_CmsRunParser_h
2+
#define FWCore_Framework_CmsRunParser_h
3+
4+
#include "boost/program_options.hpp"
5+
6+
#include <variant>
7+
8+
namespace edm {
9+
class CmsRunParser {
10+
public:
11+
using MapOrExit = std::variant<boost::program_options::variables_map, int>;
12+
CmsRunParser(const char* name);
13+
MapOrExit parse(int argc, const char* argv[]) const;
14+
//variant helpers
15+
static bool hasVM(const MapOrExit& output) {
16+
return std::holds_alternative<boost::program_options::variables_map>(output);
17+
}
18+
static boost::program_options::variables_map getVM(const MapOrExit& output) {
19+
return std::get<boost::program_options::variables_map>(output);
20+
}
21+
static bool hasExit(const MapOrExit& output) { return std::holds_alternative<int>(output); }
22+
static int getExit(const MapOrExit& output) { return std::get<int>(output); }
23+
24+
private:
25+
boost::program_options::options_description desc_;
26+
boost::program_options::options_description all_options_;
27+
boost::program_options::positional_options_description pos_options_;
28+
29+
public:
30+
//Command line parameters
31+
static inline const char* const kParameterSetOpt = "parameter-set";
32+
static inline const char* const kPythonOpt = "pythonOptions";
33+
static inline const char* const kPythonOptDefault = "CMSRUN_PYTHONOPT_DEFAULT";
34+
static inline const char* const kCmdCommandOpt = "command,c";
35+
static inline const char* const kCmdOpt = "command";
36+
static inline const char* const kJobreportCommandOpt = "jobreport,j";
37+
static inline const char* const kJobreportOpt = "jobreport";
38+
static inline const char* const kEnableJobreportCommandOpt = "enablejobreport,e";
39+
static inline const char* const kEnableJobreportOpt = "enablejobreport";
40+
static inline const char* const kJobModeCommandOpt = "mode,m";
41+
static inline const char* const kJobModeOpt = "mode";
42+
static inline const char* const kNumberOfThreadsCommandOpt = "numThreads,n";
43+
static inline const char* const kNumberOfThreadsOpt = "numThreads";
44+
static inline const char* const kSizeOfStackForThreadCommandOpt = "sizeOfStackForThreadsInKB,s";
45+
static inline const char* const kSizeOfStackForThreadOpt = "sizeOfStackForThreadsInKB";
46+
static inline const char* const kHelpOpt = "help";
47+
static inline const char* const kHelpCommandOpt = "help,h";
48+
static inline const char* const kStrictOpt = "strict";
49+
};
50+
} // namespace edm
51+
52+
#endif

0 commit comments

Comments
 (0)