1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
3 #include "cmDocumentation.h"
10 #include "cmsys/FStream.hxx"
11 #include "cmsys/Glob.hxx"
13 #include "cmDocumentationEntry.h"
14 #include "cmDocumentationSection.h"
16 #include "cmSystemTools.h"
17 #include "cmVersion.h"
19 static const char* cmDocumentationStandardOptions[][2] = {
20 { "--help,-help,-usage,-h,-H,/?", "Print usage information and exit." },
21 { "--version,-version,/V [<f>]", "Print version number and exit." },
22 { "--help-full [<f>]", "Print all help manuals and exit." },
23 { "--help-manual <man> [<f>]", "Print one help manual and exit." },
24 { "--help-manual-list [<f>]", "List help manuals available and exit." },
25 { "--help-command <cmd> [<f>]", "Print help for one command and exit." },
26 { "--help-command-list [<f>]",
27 "List commands with help available and exit." },
28 { "--help-commands [<f>]", "Print cmake-commands manual and exit." },
29 { "--help-module <mod> [<f>]", "Print help for one module and exit." },
30 { "--help-module-list [<f>]", "List modules with help available and exit." },
31 { "--help-modules [<f>]", "Print cmake-modules manual and exit." },
32 { "--help-policy <cmp> [<f>]", "Print help for one policy and exit." },
33 { "--help-policy-list [<f>]",
34 "List policies with help available and exit." },
35 { "--help-policies [<f>]", "Print cmake-policies manual and exit." },
36 { "--help-property <prop> [<f>]", "Print help for one property and exit." },
37 { "--help-property-list [<f>]",
38 "List properties with help available and exit." },
39 { "--help-properties [<f>]", "Print cmake-properties manual and exit." },
40 { "--help-variable var [<f>]", "Print help for one variable and exit." },
41 { "--help-variable-list [<f>]",
42 "List variables with help available and exit." },
43 { "--help-variables [<f>]", "Print cmake-variables manual and exit." },
47 static const char* cmDocumentationCPackGeneratorsHeader[][2] = {
48 { nullptr, "The following generators are available on this platform:" },
52 static const char* cmDocumentationCMakeGeneratorsHeader[][2] = {
54 "The following generators are available on this platform (* marks "
59 cmDocumentation::cmDocumentation()
61 this->addCommonStandardDocSections();
62 this->ShowGenerators = true;
65 bool cmDocumentation::PrintVersion(std::ostream& os)
67 /* clang-format off */
69 this->GetNameString() <<
70 " version " << cmVersion::GetCMakeVersion() << "\n"
72 "CMake suite maintained and supported by Kitware (kitware.com/cmake).\n"
78 bool cmDocumentation::PrintDocumentation(Type ht, std::ostream& os)
81 case cmDocumentation::Usage:
82 return this->PrintUsage(os);
83 case cmDocumentation::Help:
84 return this->PrintHelp(os);
85 case cmDocumentation::Full:
86 return this->PrintHelpFull(os);
87 case cmDocumentation::OneManual:
88 return this->PrintHelpOneManual(os);
89 case cmDocumentation::OneCommand:
90 return this->PrintHelpOneCommand(os);
91 case cmDocumentation::OneModule:
92 return this->PrintHelpOneModule(os);
93 case cmDocumentation::OnePolicy:
94 return this->PrintHelpOnePolicy(os);
95 case cmDocumentation::OneProperty:
96 return this->PrintHelpOneProperty(os);
97 case cmDocumentation::OneVariable:
98 return this->PrintHelpOneVariable(os);
99 case cmDocumentation::ListManuals:
100 return this->PrintHelpListManuals(os);
101 case cmDocumentation::ListCommands:
102 return this->PrintHelpListCommands(os);
103 case cmDocumentation::ListModules:
104 return this->PrintHelpListModules(os);
105 case cmDocumentation::ListProperties:
106 return this->PrintHelpListProperties(os);
107 case cmDocumentation::ListVariables:
108 return this->PrintHelpListVariables(os);
109 case cmDocumentation::ListPolicies:
110 return this->PrintHelpListPolicies(os);
111 case cmDocumentation::ListGenerators:
112 return this->PrintHelpListGenerators(os);
113 case cmDocumentation::Version:
114 return this->PrintVersion(os);
115 case cmDocumentation::OldCustomModules:
116 return this->PrintOldCustomModules(os);
122 bool cmDocumentation::PrintRequestedDocumentation(std::ostream& os)
127 // Loop over requested documentation types.
128 for (RequestedHelpItem const& rhi : this->RequestedHelpItems) {
129 this->CurrentArgument = rhi.Argument;
130 // If a file name was given, use it. Otherwise, default to the
132 cmsys::ofstream fout;
133 std::ostream* s = &os;
134 if (!rhi.Filename.empty()) {
135 fout.open(rhi.Filename.c_str());
137 } else if (++count > 1) {
141 // Print this documentation type to the stream.
142 if (!this->PrintDocumentation(rhi.HelpType, *s) || s->fail()) {
149 #define GET_OPT_ARGUMENT(target) \
151 if ((i + 1 < argc) && !this->IsOption(argv[i + 1])) { \
152 (target) = argv[i + 1]; \
157 void cmDocumentation::WarnFormFromFilename(
158 cmDocumentation::RequestedHelpItem& request, bool& result)
160 std::string ext = cmSystemTools::GetFilenameLastExtension(request.Filename);
161 ext = cmSystemTools::UpperCase(ext);
162 if ((ext == ".HTM") || (ext == ".HTML")) {
163 request.HelpType = cmDocumentation::None;
165 cmSystemTools::Message("Warning: HTML help format no longer supported");
166 } else if (ext == ".DOCBOOK") {
167 request.HelpType = cmDocumentation::None;
169 cmSystemTools::Message("Warning: Docbook help format no longer supported");
171 // ".1" to ".9" should be manpages
172 else if ((ext.length() == 2) && (ext[1] >= '1') && (ext[1] <= '9')) {
173 request.HelpType = cmDocumentation::None;
175 cmSystemTools::Message("Warning: Man help format no longer supported");
179 void cmDocumentation::addCommonStandardDocSections()
181 cmDocumentationSection sec{ "Options" };
182 sec.Append(cmDocumentationStandardOptions);
183 this->AllSections.emplace("Options", std::move(sec));
186 void cmDocumentation::addCMakeStandardDocSections()
188 cmDocumentationSection sec{ "Generators" };
189 sec.Append(cmDocumentationCMakeGeneratorsHeader);
190 this->AllSections.emplace("Generators", std::move(sec));
193 void cmDocumentation::addCTestStandardDocSections()
195 // This is currently done for backward compatibility reason
196 // We may suppress some of these.
197 this->addCMakeStandardDocSections();
200 void cmDocumentation::addCPackStandardDocSections()
202 cmDocumentationSection sec{ "Generators" };
203 sec.Append(cmDocumentationCPackGeneratorsHeader);
204 this->AllSections.emplace("Generators", std::move(sec));
207 bool cmDocumentation::CheckOptions(int argc, const char* const* argv,
210 // Providing zero arguments gives usage information.
212 RequestedHelpItem help;
213 help.HelpType = cmDocumentation::Usage;
214 this->RequestedHelpItems.push_back(std::move(help));
218 // Search for supported help options.
221 for (int i = 1; i < argc; ++i) {
222 if (exitOpt && strcmp(argv[i], exitOpt) == 0) {
225 RequestedHelpItem help;
226 // Check if this is a supported help option.
227 if ((strcmp(argv[i], "-help") == 0) || (strcmp(argv[i], "--help") == 0) ||
228 (strcmp(argv[i], "/?") == 0) || (strcmp(argv[i], "-usage") == 0) ||
229 (strcmp(argv[i], "-h") == 0) || (strcmp(argv[i], "-H") == 0)) {
230 help.HelpType = cmDocumentation::Help;
231 GET_OPT_ARGUMENT(help.Argument);
232 help.Argument = cmSystemTools::LowerCase(help.Argument);
233 // special case for single command
234 if (!help.Argument.empty()) {
235 help.HelpType = cmDocumentation::OneCommand;
237 } else if (strcmp(argv[i], "--help-properties") == 0) {
238 help.HelpType = cmDocumentation::OneManual;
239 help.Argument = "cmake-properties.7";
240 GET_OPT_ARGUMENT(help.Filename);
241 this->WarnFormFromFilename(help, result);
242 } else if (strcmp(argv[i], "--help-policies") == 0) {
243 help.HelpType = cmDocumentation::OneManual;
244 help.Argument = "cmake-policies.7";
245 GET_OPT_ARGUMENT(help.Filename);
246 this->WarnFormFromFilename(help, result);
247 } else if (strcmp(argv[i], "--help-variables") == 0) {
248 help.HelpType = cmDocumentation::OneManual;
249 help.Argument = "cmake-variables.7";
250 GET_OPT_ARGUMENT(help.Filename);
251 this->WarnFormFromFilename(help, result);
252 } else if (strcmp(argv[i], "--help-modules") == 0) {
253 help.HelpType = cmDocumentation::OneManual;
254 help.Argument = "cmake-modules.7";
255 GET_OPT_ARGUMENT(help.Filename);
256 this->WarnFormFromFilename(help, result);
257 } else if (strcmp(argv[i], "--help-custom-modules") == 0) {
258 GET_OPT_ARGUMENT(help.Filename);
259 cmSystemTools::Message(
260 "Warning: --help-custom-modules no longer supported");
261 if (help.Filename.empty()) {
264 // Avoid breaking old project builds completely by at least generating
265 // the output file. Abuse help.Argument to give the file name to
266 // PrintOldCustomModules without disrupting our internal API.
267 help.HelpType = cmDocumentation::OldCustomModules;
268 help.Argument = cmSystemTools::GetFilenameName(help.Filename);
269 } else if (strcmp(argv[i], "--help-commands") == 0) {
270 help.HelpType = cmDocumentation::OneManual;
271 help.Argument = "cmake-commands.7";
272 GET_OPT_ARGUMENT(help.Filename);
273 this->WarnFormFromFilename(help, result);
274 } else if (strcmp(argv[i], "--help-compatcommands") == 0) {
275 GET_OPT_ARGUMENT(help.Filename);
276 cmSystemTools::Message(
277 "Warning: --help-compatcommands no longer supported");
279 } else if (strcmp(argv[i], "--help-full") == 0) {
280 help.HelpType = cmDocumentation::Full;
281 GET_OPT_ARGUMENT(help.Filename);
282 this->WarnFormFromFilename(help, result);
283 } else if (strcmp(argv[i], "--help-html") == 0) {
284 GET_OPT_ARGUMENT(help.Filename);
285 cmSystemTools::Message("Warning: --help-html no longer supported");
287 } else if (strcmp(argv[i], "--help-man") == 0) {
288 GET_OPT_ARGUMENT(help.Filename);
289 cmSystemTools::Message("Warning: --help-man no longer supported");
291 } else if (strcmp(argv[i], "--help-command") == 0) {
292 help.HelpType = cmDocumentation::OneCommand;
293 GET_OPT_ARGUMENT(help.Argument);
294 GET_OPT_ARGUMENT(help.Filename);
295 help.Argument = cmSystemTools::LowerCase(help.Argument);
296 this->WarnFormFromFilename(help, result);
297 } else if (strcmp(argv[i], "--help-module") == 0) {
298 help.HelpType = cmDocumentation::OneModule;
299 GET_OPT_ARGUMENT(help.Argument);
300 GET_OPT_ARGUMENT(help.Filename);
301 this->WarnFormFromFilename(help, result);
302 } else if (strcmp(argv[i], "--help-property") == 0) {
303 help.HelpType = cmDocumentation::OneProperty;
304 GET_OPT_ARGUMENT(help.Argument);
305 GET_OPT_ARGUMENT(help.Filename);
306 this->WarnFormFromFilename(help, result);
307 } else if (strcmp(argv[i], "--help-policy") == 0) {
308 help.HelpType = cmDocumentation::OnePolicy;
309 GET_OPT_ARGUMENT(help.Argument);
310 GET_OPT_ARGUMENT(help.Filename);
311 this->WarnFormFromFilename(help, result);
312 } else if (strcmp(argv[i], "--help-variable") == 0) {
313 help.HelpType = cmDocumentation::OneVariable;
314 GET_OPT_ARGUMENT(help.Argument);
315 GET_OPT_ARGUMENT(help.Filename);
316 this->WarnFormFromFilename(help, result);
317 } else if (strcmp(argv[i], "--help-manual") == 0) {
318 help.HelpType = cmDocumentation::OneManual;
319 GET_OPT_ARGUMENT(help.Argument);
320 GET_OPT_ARGUMENT(help.Filename);
321 this->WarnFormFromFilename(help, result);
322 } else if (strcmp(argv[i], "--help-command-list") == 0) {
323 help.HelpType = cmDocumentation::ListCommands;
324 GET_OPT_ARGUMENT(help.Filename);
325 } else if (strcmp(argv[i], "--help-module-list") == 0) {
326 help.HelpType = cmDocumentation::ListModules;
327 GET_OPT_ARGUMENT(help.Filename);
328 } else if (strcmp(argv[i], "--help-property-list") == 0) {
329 help.HelpType = cmDocumentation::ListProperties;
330 GET_OPT_ARGUMENT(help.Filename);
331 } else if (strcmp(argv[i], "--help-variable-list") == 0) {
332 help.HelpType = cmDocumentation::ListVariables;
333 GET_OPT_ARGUMENT(help.Filename);
334 } else if (strcmp(argv[i], "--help-policy-list") == 0) {
335 help.HelpType = cmDocumentation::ListPolicies;
336 GET_OPT_ARGUMENT(help.Filename);
337 } else if (strcmp(argv[i], "--help-manual-list") == 0) {
338 help.HelpType = cmDocumentation::ListManuals;
339 GET_OPT_ARGUMENT(help.Filename);
340 } else if (strcmp(argv[i], "--copyright") == 0) {
341 GET_OPT_ARGUMENT(help.Filename);
342 cmSystemTools::Message("Warning: --copyright no longer supported");
344 } else if ((strcmp(argv[i], "--version") == 0) ||
345 (strcmp(argv[i], "-version") == 0) ||
346 (strcmp(argv[i], "/V") == 0)) {
347 help.HelpType = cmDocumentation::Version;
348 GET_OPT_ARGUMENT(help.Filename);
350 if (help.HelpType != None) {
351 // This is a help option. See if there is a file name given.
353 this->RequestedHelpItems.push_back(std::move(help));
359 void cmDocumentation::SetName(const std::string& name)
361 this->NameString = name;
364 void cmDocumentation::SetSection(const char* name,
365 cmDocumentationSection section)
367 this->SectionAtName(name) = std::move(section);
370 void cmDocumentation::SetSection(const char* name,
371 std::vector<cmDocumentationEntry>& docs)
373 cmDocumentationSection sec{ name };
375 this->SetSection(name, std::move(sec));
378 void cmDocumentation::SetSection(const char* name, const char* docs[][2])
380 cmDocumentationSection sec{ name };
382 this->SetSection(name, std::move(sec));
385 void cmDocumentation::SetSections(
386 std::map<std::string, cmDocumentationSection> sections)
388 for (auto& s : sections) {
389 this->SetSection(s.first.c_str(), std::move(s.second));
392 cmDocumentationSection& cmDocumentation::SectionAtName(const char* name)
394 return this->AllSections.emplace(name, cmDocumentationSection{ name })
398 void cmDocumentation::PrependSection(const char* name, const char* docs[][2])
400 this->SectionAtName(name).Prepend(docs);
403 void cmDocumentation::PrependSection(const char* name,
404 std::vector<cmDocumentationEntry>& docs)
406 this->SectionAtName(name).Prepend(docs);
409 void cmDocumentation::AppendSection(const char* name, const char* docs[][2])
411 this->SectionAtName(name).Append(docs);
414 void cmDocumentation::AppendSection(const char* name,
415 std::vector<cmDocumentationEntry>& docs)
417 this->SectionAtName(name).Append(docs);
420 void cmDocumentation::AppendSection(const char* name,
421 cmDocumentationEntry& docs)
424 std::vector<cmDocumentationEntry> docsVec;
425 docsVec.push_back(docs);
426 this->AppendSection(name, docsVec);
429 void cmDocumentation::PrependSection(const char* name,
430 cmDocumentationEntry& docs)
433 std::vector<cmDocumentationEntry> docsVec;
434 docsVec.push_back(docs);
435 this->PrependSection(name, docsVec);
438 void cmDocumentation::GlobHelp(std::vector<std::string>& files,
439 std::string const& pattern)
442 std::string findExpr =
443 cmSystemTools::GetCMakeRoot() + "/Help/" + pattern + ".rst";
444 if (gl.FindFiles(findExpr)) {
445 files = gl.GetFiles();
449 void cmDocumentation::PrintNames(std::ostream& os, std::string const& pattern)
451 std::vector<std::string> files;
452 this->GlobHelp(files, pattern);
453 std::vector<std::string> names;
454 for (std::string const& f : files) {
456 cmsys::ifstream fin(f.c_str());
457 while (fin && cmSystemTools::GetLineFromStream(fin, line)) {
458 if (!line.empty() && (isalnum(line[0]) || line[0] == '<')) {
459 names.push_back(line);
464 std::sort(names.begin(), names.end());
465 for (std::string const& n : names) {
470 bool cmDocumentation::PrintFiles(std::ostream& os, std::string const& pattern)
473 std::vector<std::string> files;
474 this->GlobHelp(files, pattern);
475 std::sort(files.begin(), files.end());
476 cmRST r(os, cmSystemTools::GetCMakeRoot() + "/Help");
477 for (std::string const& f : files) {
478 found = r.ProcessFile(f) || found;
483 bool cmDocumentation::PrintHelpFull(std::ostream& os)
485 return this->PrintFiles(os, "index");
488 bool cmDocumentation::PrintHelpOneManual(std::ostream& os)
490 std::string mname = this->CurrentArgument;
491 std::string::size_type mlen = mname.length();
492 if (mlen > 3 && mname[mlen - 3] == '(' && mname[mlen - 1] == ')') {
493 mname = mname.substr(0, mlen - 3) + "." + mname[mlen - 2];
495 if (this->PrintFiles(os, "manual/" + mname) ||
496 this->PrintFiles(os, "manual/" + mname + ".[0-9]")) {
499 // Argument was not a manual. Complain.
500 os << "Argument \"" << this->CurrentArgument
501 << "\" to --help-manual is not an available manual. "
502 << "Use --help-manual-list to see all available manuals.\n";
506 bool cmDocumentation::PrintHelpListManuals(std::ostream& os)
508 this->PrintNames(os, "manual/*");
512 bool cmDocumentation::PrintHelpOneCommand(std::ostream& os)
514 std::string cname = cmSystemTools::LowerCase(this->CurrentArgument);
515 if (this->PrintFiles(os, "command/" + cname)) {
518 // Argument was not a command. Complain.
519 os << "Argument \"" << this->CurrentArgument
520 << "\" to --help-command is not a CMake command. "
521 << "Use --help-command-list to see all commands.\n";
525 bool cmDocumentation::PrintHelpListCommands(std::ostream& os)
527 this->PrintNames(os, "command/*");
531 bool cmDocumentation::PrintHelpOneModule(std::ostream& os)
533 std::string mname = this->CurrentArgument;
534 if (this->PrintFiles(os, "module/" + mname)) {
537 // Argument was not a module. Complain.
538 os << "Argument \"" << this->CurrentArgument
539 << "\" to --help-module is not a CMake module.\n";
543 bool cmDocumentation::PrintHelpListModules(std::ostream& os)
545 std::vector<std::string> files;
546 this->GlobHelp(files, "module/*");
547 std::vector<std::string> modules;
548 for (std::string const& f : files) {
549 std::string module = cmSystemTools::GetFilenameName(f);
550 modules.push_back(module.substr(0, module.size() - 4));
552 std::sort(modules.begin(), modules.end());
553 for (std::string const& m : modules) {
559 bool cmDocumentation::PrintHelpOneProperty(std::ostream& os)
561 std::string pname = cmSystemTools::HelpFileName(this->CurrentArgument);
562 if (this->PrintFiles(os, "prop_*/" + pname)) {
565 // Argument was not a property. Complain.
566 os << "Argument \"" << this->CurrentArgument
567 << "\" to --help-property is not a CMake property. "
568 << "Use --help-property-list to see all properties.\n";
572 bool cmDocumentation::PrintHelpListProperties(std::ostream& os)
574 this->PrintNames(os, "prop_*/*");
578 bool cmDocumentation::PrintHelpOnePolicy(std::ostream& os)
580 std::string pname = this->CurrentArgument;
581 std::vector<std::string> files;
582 if (this->PrintFiles(os, "policy/" + pname)) {
586 // Argument was not a policy. Complain.
587 os << "Argument \"" << this->CurrentArgument
588 << "\" to --help-policy is not a CMake policy.\n";
592 bool cmDocumentation::PrintHelpListPolicies(std::ostream& os)
594 this->PrintNames(os, "policy/*");
598 bool cmDocumentation::PrintHelpListGenerators(std::ostream& os)
600 const auto si = this->AllSections.find("Generators");
601 if (si != this->AllSections.end()) {
602 this->Formatter.SetIndent(" ");
603 this->Formatter.PrintSection(os, si->second);
608 bool cmDocumentation::PrintHelpOneVariable(std::ostream& os)
610 std::string vname = cmSystemTools::HelpFileName(this->CurrentArgument);
611 if (this->PrintFiles(os, "variable/" + vname)) {
614 // Argument was not a variable. Complain.
615 os << "Argument \"" << this->CurrentArgument
616 << "\" to --help-variable is not a defined variable. "
617 << "Use --help-variable-list to see all defined variables.\n";
621 bool cmDocumentation::PrintHelpListVariables(std::ostream& os)
623 this->PrintNames(os, "variable/*");
627 bool cmDocumentation::PrintUsage(std::ostream& os)
629 const auto si = this->AllSections.find("Usage");
630 if (si != this->AllSections.end()) {
631 this->Formatter.PrintSection(os, si->second);
636 bool cmDocumentation::PrintHelp(std::ostream& os)
638 auto si = this->AllSections.find("Usage");
639 if (si != this->AllSections.end()) {
640 this->Formatter.PrintSection(os, si->second);
642 si = this->AllSections.find("Options");
643 if (si != this->AllSections.end()) {
644 this->Formatter.PrintSection(os, si->second);
646 if (this->ShowGenerators) {
647 si = this->AllSections.find("Generators");
648 if (si != this->AllSections.end()) {
649 this->Formatter.PrintSection(os, si->second);
655 const char* cmDocumentation::GetNameString() const
657 if (!this->NameString.empty()) {
658 return this->NameString.c_str();
663 bool cmDocumentation::IsOption(const char* arg) const
665 return ((arg[0] == '-') || (strcmp(arg, "/V") == 0) ||
666 (strcmp(arg, "/?") == 0));
669 bool cmDocumentation::PrintOldCustomModules(std::ostream& os)
671 // CheckOptions abuses the Argument field to give us the file name.
672 std::string filename = this->CurrentArgument;
673 std::string ext = cmSystemTools::UpperCase(
674 cmSystemTools::GetFilenameLastExtension(filename));
675 std::string name = cmSystemTools::GetFilenameWithoutLastExtension(filename);
677 const char* summary = "cmake --help-custom-modules no longer supported\n";
679 "CMake versions prior to 3.0 exposed their internal module help page\n"
680 "generation functionality through the --help-custom-modules option.\n"
681 "CMake versions 3.0 and above use other means to generate their module\n"
682 "help pages so this functionality is no longer available to be exposed.\n"
684 "This file was generated as a placeholder to provide this information.\n";
685 if ((ext == ".HTM") || (ext == ".HTML")) {
686 os << "<html><title>" << name << "</title><body>\n"
687 << summary << "<p/>\n"
688 << detail << "</body></html>\n";
689 } else if ((ext.length() == 2) && (ext[1] >= '1') && (ext[1] <= '9')) {
690 /* clang-format off */
692 ".TH " << name << " " << ext[1] << " \"" <<
693 cmSystemTools::GetCurrentDateTime("%B %d, %Y") <<
694 "\" \"cmake " << cmVersion::GetCMakeVersion() << "\"\n"
697 name << " \\- " << summary <<
703 /* clang-format on */
705 os << name << "\n\n" << summary << "\n" << detail;