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 { "-h,-H,--help,-help,-usage,/?", "Print usage information and exit." },
21 { "--version,-version,/V [<file>]", "Print version number and exit." },
22 { "--help-full [<file>]", "Print all help manuals and exit." },
23 { "--help-manual <man> [<file>]", "Print one help manual and exit." },
24 { "--help-manual-list [<file>]", "List help manuals available and exit." },
25 { "--help-command <cmd> [<file>]", "Print help for one command and exit." },
26 { "--help-command-list [<file>]",
27 "List commands with help available and exit." },
28 { "--help-commands [<file>]", "Print cmake-commands manual and exit." },
29 { "--help-module <mod> [<file>]", "Print help for one module and exit." },
30 { "--help-module-list [<file>]",
31 "List modules with help available and exit." },
32 { "--help-modules [<file>]", "Print cmake-modules manual and exit." },
33 { "--help-policy <cmp> [<file>]", "Print help for one policy and exit." },
34 { "--help-policy-list [<file>]",
35 "List policies with help available and exit." },
36 { "--help-policies [<file>]", "Print cmake-policies manual and exit." },
37 { "--help-property <prop> [<file>]",
38 "Print help for one property and exit." },
39 { "--help-property-list [<file>]",
40 "List properties with help available and exit." },
41 { "--help-properties [<file>]", "Print cmake-properties manual and exit." },
42 { "--help-variable var [<file>]", "Print help for one variable and exit." },
43 { "--help-variable-list [<file>]",
44 "List variables with help available and exit." },
45 { "--help-variables [<file>]", "Print cmake-variables manual and exit." },
49 static const char* cmDocumentationCPackGeneratorsHeader[][2] = {
50 { nullptr, "The following generators are available on this platform:" },
54 static const char* cmDocumentationCMakeGeneratorsHeader[][2] = {
56 "The following generators are available on this platform (* marks "
61 cmDocumentation::cmDocumentation()
63 this->addCommonStandardDocSections();
64 this->ShowGenerators = true;
67 bool cmDocumentation::PrintVersion(std::ostream& os)
69 /* clang-format off */
71 this->GetNameString() <<
72 " version " << cmVersion::GetCMakeVersion() << "\n"
74 "CMake suite maintained and supported by Kitware (kitware.com/cmake).\n"
80 bool cmDocumentation::PrintDocumentation(Type ht, std::ostream& os)
83 case cmDocumentation::Usage:
84 return this->PrintUsage(os);
85 case cmDocumentation::Help:
86 return this->PrintHelp(os);
87 case cmDocumentation::Full:
88 return this->PrintHelpFull(os);
89 case cmDocumentation::OneManual:
90 return this->PrintHelpOneManual(os);
91 case cmDocumentation::OneCommand:
92 return this->PrintHelpOneCommand(os);
93 case cmDocumentation::OneModule:
94 return this->PrintHelpOneModule(os);
95 case cmDocumentation::OnePolicy:
96 return this->PrintHelpOnePolicy(os);
97 case cmDocumentation::OneProperty:
98 return this->PrintHelpOneProperty(os);
99 case cmDocumentation::OneVariable:
100 return this->PrintHelpOneVariable(os);
101 case cmDocumentation::ListManuals:
102 return this->PrintHelpListManuals(os);
103 case cmDocumentation::ListCommands:
104 return this->PrintHelpListCommands(os);
105 case cmDocumentation::ListModules:
106 return this->PrintHelpListModules(os);
107 case cmDocumentation::ListProperties:
108 return this->PrintHelpListProperties(os);
109 case cmDocumentation::ListVariables:
110 return this->PrintHelpListVariables(os);
111 case cmDocumentation::ListPolicies:
112 return this->PrintHelpListPolicies(os);
113 case cmDocumentation::ListGenerators:
114 return this->PrintHelpListGenerators(os);
115 case cmDocumentation::Version:
116 return this->PrintVersion(os);
117 case cmDocumentation::OldCustomModules:
118 return this->PrintOldCustomModules(os);
124 bool cmDocumentation::PrintRequestedDocumentation(std::ostream& os)
129 // Loop over requested documentation types.
130 for (RequestedHelpItem const& rhi : this->RequestedHelpItems) {
131 this->CurrentArgument = rhi.Argument;
132 // If a file name was given, use it. Otherwise, default to the
134 cmsys::ofstream fout;
135 std::ostream* s = &os;
136 if (!rhi.Filename.empty()) {
137 fout.open(rhi.Filename.c_str());
139 } else if (++count > 1) {
143 // Print this documentation type to the stream.
144 if (!this->PrintDocumentation(rhi.HelpType, *s) || s->fail()) {
151 #define GET_OPT_ARGUMENT(target) \
153 if ((i + 1 < argc) && !this->IsOption(argv[i + 1])) { \
154 (target) = argv[i + 1]; \
159 void cmDocumentation::WarnFormFromFilename(
160 cmDocumentation::RequestedHelpItem& request, bool& result)
162 std::string ext = cmSystemTools::GetFilenameLastExtension(request.Filename);
163 ext = cmSystemTools::UpperCase(ext);
164 if ((ext == ".HTM") || (ext == ".HTML")) {
165 request.HelpType = cmDocumentation::None;
167 cmSystemTools::Message("Warning: HTML help format no longer supported");
168 } else if (ext == ".DOCBOOK") {
169 request.HelpType = cmDocumentation::None;
171 cmSystemTools::Message("Warning: Docbook help format no longer supported");
173 // ".1" to ".9" should be manpages
174 else if ((ext.length() == 2) && (ext[1] >= '1') && (ext[1] <= '9')) {
175 request.HelpType = cmDocumentation::None;
177 cmSystemTools::Message("Warning: Man help format no longer supported");
181 void cmDocumentation::addCommonStandardDocSections()
183 cmDocumentationSection sec{ "Options" };
184 sec.Append(cmDocumentationStandardOptions);
185 this->AllSections.emplace("Options", std::move(sec));
188 void cmDocumentation::addCMakeStandardDocSections()
190 cmDocumentationSection sec{ "Generators" };
191 sec.Append(cmDocumentationCMakeGeneratorsHeader);
192 this->AllSections.emplace("Generators", std::move(sec));
195 void cmDocumentation::addCTestStandardDocSections()
197 // This is currently done for backward compatibility reason
198 // We may suppress some of these.
199 this->addCMakeStandardDocSections();
202 void cmDocumentation::addCPackStandardDocSections()
204 cmDocumentationSection sec{ "Generators" };
205 sec.Append(cmDocumentationCPackGeneratorsHeader);
206 this->AllSections.emplace("Generators", std::move(sec));
209 bool cmDocumentation::CheckOptions(int argc, const char* const* argv,
212 // Providing zero arguments gives usage information.
214 RequestedHelpItem help;
215 help.HelpType = cmDocumentation::Usage;
216 this->RequestedHelpItems.push_back(std::move(help));
220 // Search for supported help options.
223 for (int i = 1; i < argc; ++i) {
224 if (exitOpt && strcmp(argv[i], exitOpt) == 0) {
227 RequestedHelpItem help;
228 // Check if this is a supported help option.
229 if ((strcmp(argv[i], "-help") == 0) || (strcmp(argv[i], "--help") == 0) ||
230 (strcmp(argv[i], "/?") == 0) || (strcmp(argv[i], "-usage") == 0) ||
231 (strcmp(argv[i], "-h") == 0) || (strcmp(argv[i], "-H") == 0)) {
232 help.HelpType = cmDocumentation::Help;
233 GET_OPT_ARGUMENT(help.Argument);
234 help.Argument = cmSystemTools::LowerCase(help.Argument);
235 // special case for single command
236 if (!help.Argument.empty()) {
237 help.HelpType = cmDocumentation::OneCommand;
239 } else if (strcmp(argv[i], "--help-properties") == 0) {
240 help.HelpType = cmDocumentation::OneManual;
241 help.Argument = "cmake-properties.7";
242 GET_OPT_ARGUMENT(help.Filename);
243 this->WarnFormFromFilename(help, result);
244 } else if (strcmp(argv[i], "--help-policies") == 0) {
245 help.HelpType = cmDocumentation::OneManual;
246 help.Argument = "cmake-policies.7";
247 GET_OPT_ARGUMENT(help.Filename);
248 this->WarnFormFromFilename(help, result);
249 } else if (strcmp(argv[i], "--help-variables") == 0) {
250 help.HelpType = cmDocumentation::OneManual;
251 help.Argument = "cmake-variables.7";
252 GET_OPT_ARGUMENT(help.Filename);
253 this->WarnFormFromFilename(help, result);
254 } else if (strcmp(argv[i], "--help-modules") == 0) {
255 help.HelpType = cmDocumentation::OneManual;
256 help.Argument = "cmake-modules.7";
257 GET_OPT_ARGUMENT(help.Filename);
258 this->WarnFormFromFilename(help, result);
259 } else if (strcmp(argv[i], "--help-custom-modules") == 0) {
260 GET_OPT_ARGUMENT(help.Filename);
261 cmSystemTools::Message(
262 "Warning: --help-custom-modules no longer supported");
263 if (help.Filename.empty()) {
266 // Avoid breaking old project builds completely by at least generating
267 // the output file. Abuse help.Argument to give the file name to
268 // PrintOldCustomModules without disrupting our internal API.
269 help.HelpType = cmDocumentation::OldCustomModules;
270 help.Argument = cmSystemTools::GetFilenameName(help.Filename);
271 } else if (strcmp(argv[i], "--help-commands") == 0) {
272 help.HelpType = cmDocumentation::OneManual;
273 help.Argument = "cmake-commands.7";
274 GET_OPT_ARGUMENT(help.Filename);
275 this->WarnFormFromFilename(help, result);
276 } else if (strcmp(argv[i], "--help-compatcommands") == 0) {
277 GET_OPT_ARGUMENT(help.Filename);
278 cmSystemTools::Message(
279 "Warning: --help-compatcommands no longer supported");
281 } else if (strcmp(argv[i], "--help-full") == 0) {
282 help.HelpType = cmDocumentation::Full;
283 GET_OPT_ARGUMENT(help.Filename);
284 this->WarnFormFromFilename(help, result);
285 } else if (strcmp(argv[i], "--help-html") == 0) {
286 GET_OPT_ARGUMENT(help.Filename);
287 cmSystemTools::Message("Warning: --help-html no longer supported");
289 } else if (strcmp(argv[i], "--help-man") == 0) {
290 GET_OPT_ARGUMENT(help.Filename);
291 cmSystemTools::Message("Warning: --help-man no longer supported");
293 } else if (strcmp(argv[i], "--help-command") == 0) {
294 help.HelpType = cmDocumentation::OneCommand;
295 GET_OPT_ARGUMENT(help.Argument);
296 GET_OPT_ARGUMENT(help.Filename);
297 help.Argument = cmSystemTools::LowerCase(help.Argument);
298 this->WarnFormFromFilename(help, result);
299 } else if (strcmp(argv[i], "--help-module") == 0) {
300 help.HelpType = cmDocumentation::OneModule;
301 GET_OPT_ARGUMENT(help.Argument);
302 GET_OPT_ARGUMENT(help.Filename);
303 this->WarnFormFromFilename(help, result);
304 } else if (strcmp(argv[i], "--help-property") == 0) {
305 help.HelpType = cmDocumentation::OneProperty;
306 GET_OPT_ARGUMENT(help.Argument);
307 GET_OPT_ARGUMENT(help.Filename);
308 this->WarnFormFromFilename(help, result);
309 } else if (strcmp(argv[i], "--help-policy") == 0) {
310 help.HelpType = cmDocumentation::OnePolicy;
311 GET_OPT_ARGUMENT(help.Argument);
312 GET_OPT_ARGUMENT(help.Filename);
313 this->WarnFormFromFilename(help, result);
314 } else if (strcmp(argv[i], "--help-variable") == 0) {
315 help.HelpType = cmDocumentation::OneVariable;
316 GET_OPT_ARGUMENT(help.Argument);
317 GET_OPT_ARGUMENT(help.Filename);
318 this->WarnFormFromFilename(help, result);
319 } else if (strcmp(argv[i], "--help-manual") == 0) {
320 help.HelpType = cmDocumentation::OneManual;
321 GET_OPT_ARGUMENT(help.Argument);
322 GET_OPT_ARGUMENT(help.Filename);
323 this->WarnFormFromFilename(help, result);
324 } else if (strcmp(argv[i], "--help-command-list") == 0) {
325 help.HelpType = cmDocumentation::ListCommands;
326 GET_OPT_ARGUMENT(help.Filename);
327 } else if (strcmp(argv[i], "--help-module-list") == 0) {
328 help.HelpType = cmDocumentation::ListModules;
329 GET_OPT_ARGUMENT(help.Filename);
330 } else if (strcmp(argv[i], "--help-property-list") == 0) {
331 help.HelpType = cmDocumentation::ListProperties;
332 GET_OPT_ARGUMENT(help.Filename);
333 } else if (strcmp(argv[i], "--help-variable-list") == 0) {
334 help.HelpType = cmDocumentation::ListVariables;
335 GET_OPT_ARGUMENT(help.Filename);
336 } else if (strcmp(argv[i], "--help-policy-list") == 0) {
337 help.HelpType = cmDocumentation::ListPolicies;
338 GET_OPT_ARGUMENT(help.Filename);
339 } else if (strcmp(argv[i], "--help-manual-list") == 0) {
340 help.HelpType = cmDocumentation::ListManuals;
341 GET_OPT_ARGUMENT(help.Filename);
342 } else if (strcmp(argv[i], "--copyright") == 0) {
343 GET_OPT_ARGUMENT(help.Filename);
344 cmSystemTools::Message("Warning: --copyright no longer supported");
346 } else if ((strcmp(argv[i], "--version") == 0) ||
347 (strcmp(argv[i], "-version") == 0) ||
348 (strcmp(argv[i], "/V") == 0)) {
349 help.HelpType = cmDocumentation::Version;
350 GET_OPT_ARGUMENT(help.Filename);
352 if (help.HelpType != None) {
353 // This is a help option. See if there is a file name given.
355 this->RequestedHelpItems.push_back(std::move(help));
361 void cmDocumentation::SetName(const std::string& name)
363 this->NameString = name;
366 void cmDocumentation::SetSection(const char* name,
367 cmDocumentationSection section)
369 this->SectionAtName(name) = std::move(section);
372 void cmDocumentation::SetSection(const char* name,
373 std::vector<cmDocumentationEntry>& docs)
375 cmDocumentationSection sec{ name };
377 this->SetSection(name, std::move(sec));
380 void cmDocumentation::SetSection(const char* name, const char* docs[][2])
382 cmDocumentationSection sec{ name };
384 this->SetSection(name, std::move(sec));
387 void cmDocumentation::SetSections(
388 std::map<std::string, cmDocumentationSection> sections)
390 for (auto& s : sections) {
391 this->SetSection(s.first.c_str(), std::move(s.second));
394 cmDocumentationSection& cmDocumentation::SectionAtName(const char* name)
396 return this->AllSections.emplace(name, cmDocumentationSection{ name })
400 void cmDocumentation::PrependSection(const char* name, const char* docs[][2])
402 this->SectionAtName(name).Prepend(docs);
405 void cmDocumentation::PrependSection(const char* name,
406 std::vector<cmDocumentationEntry>& docs)
408 this->SectionAtName(name).Prepend(docs);
411 void cmDocumentation::AppendSection(const char* name, const char* docs[][2])
413 this->SectionAtName(name).Append(docs);
416 void cmDocumentation::AppendSection(const char* name,
417 std::vector<cmDocumentationEntry>& docs)
419 this->SectionAtName(name).Append(docs);
422 void cmDocumentation::AppendSection(const char* name,
423 cmDocumentationEntry& docs)
426 std::vector<cmDocumentationEntry> docsVec;
427 docsVec.push_back(docs);
428 this->AppendSection(name, docsVec);
431 void cmDocumentation::PrependSection(const char* name,
432 cmDocumentationEntry& docs)
435 std::vector<cmDocumentationEntry> docsVec;
436 docsVec.push_back(docs);
437 this->PrependSection(name, docsVec);
440 void cmDocumentation::GlobHelp(std::vector<std::string>& files,
441 std::string const& pattern)
444 std::string findExpr =
445 cmSystemTools::GetCMakeRoot() + "/Help/" + pattern + ".rst";
446 if (gl.FindFiles(findExpr)) {
447 files = gl.GetFiles();
451 void cmDocumentation::PrintNames(std::ostream& os, std::string const& pattern)
453 std::vector<std::string> files;
454 this->GlobHelp(files, pattern);
455 std::vector<std::string> names;
456 for (std::string const& f : files) {
458 cmsys::ifstream fin(f.c_str());
459 while (fin && cmSystemTools::GetLineFromStream(fin, line)) {
460 if (!line.empty() && (isalnum(line[0]) || line[0] == '<')) {
461 names.push_back(line);
466 std::sort(names.begin(), names.end());
467 for (std::string const& n : names) {
472 bool cmDocumentation::PrintFiles(std::ostream& os, std::string const& pattern)
475 std::vector<std::string> files;
476 this->GlobHelp(files, pattern);
477 std::sort(files.begin(), files.end());
478 cmRST r(os, cmSystemTools::GetCMakeRoot() + "/Help");
479 for (std::string const& f : files) {
480 found = r.ProcessFile(f) || found;
485 bool cmDocumentation::PrintHelpFull(std::ostream& os)
487 return this->PrintFiles(os, "index");
490 bool cmDocumentation::PrintHelpOneManual(std::ostream& os)
492 std::string mname = this->CurrentArgument;
493 std::string::size_type mlen = mname.length();
494 if (mlen > 3 && mname[mlen - 3] == '(' && mname[mlen - 1] == ')') {
495 mname = mname.substr(0, mlen - 3) + "." + mname[mlen - 2];
497 if (this->PrintFiles(os, "manual/" + mname) ||
498 this->PrintFiles(os, "manual/" + mname + ".[0-9]")) {
501 // Argument was not a manual. Complain.
502 os << "Argument \"" << this->CurrentArgument
503 << "\" to --help-manual is not an available manual. "
504 << "Use --help-manual-list to see all available manuals.\n";
508 bool cmDocumentation::PrintHelpListManuals(std::ostream& os)
510 this->PrintNames(os, "manual/*");
514 bool cmDocumentation::PrintHelpOneCommand(std::ostream& os)
516 std::string cname = cmSystemTools::LowerCase(this->CurrentArgument);
517 if (this->PrintFiles(os, "command/" + cname)) {
520 // Argument was not a command. Complain.
521 os << "Argument \"" << this->CurrentArgument
522 << "\" to --help-command is not a CMake command. "
523 << "Use --help-command-list to see all commands.\n";
527 bool cmDocumentation::PrintHelpListCommands(std::ostream& os)
529 this->PrintNames(os, "command/*");
533 bool cmDocumentation::PrintHelpOneModule(std::ostream& os)
535 std::string mname = this->CurrentArgument;
536 if (this->PrintFiles(os, "module/" + mname)) {
539 // Argument was not a module. Complain.
540 os << "Argument \"" << this->CurrentArgument
541 << "\" to --help-module is not a CMake module.\n";
545 bool cmDocumentation::PrintHelpListModules(std::ostream& os)
547 std::vector<std::string> files;
548 this->GlobHelp(files, "module/*");
549 std::vector<std::string> modules;
550 for (std::string const& f : files) {
551 std::string module = cmSystemTools::GetFilenameName(f);
552 modules.push_back(module.substr(0, module.size() - 4));
554 std::sort(modules.begin(), modules.end());
555 for (std::string const& m : modules) {
561 bool cmDocumentation::PrintHelpOneProperty(std::ostream& os)
563 std::string pname = cmSystemTools::HelpFileName(this->CurrentArgument);
564 if (this->PrintFiles(os, "prop_*/" + pname)) {
567 // Argument was not a property. Complain.
568 os << "Argument \"" << this->CurrentArgument
569 << "\" to --help-property is not a CMake property. "
570 << "Use --help-property-list to see all properties.\n";
574 bool cmDocumentation::PrintHelpListProperties(std::ostream& os)
576 this->PrintNames(os, "prop_*/*");
580 bool cmDocumentation::PrintHelpOnePolicy(std::ostream& os)
582 std::string pname = this->CurrentArgument;
583 std::vector<std::string> files;
584 if (this->PrintFiles(os, "policy/" + pname)) {
588 // Argument was not a policy. Complain.
589 os << "Argument \"" << this->CurrentArgument
590 << "\" to --help-policy is not a CMake policy.\n";
594 bool cmDocumentation::PrintHelpListPolicies(std::ostream& os)
596 this->PrintNames(os, "policy/*");
600 bool cmDocumentation::PrintHelpListGenerators(std::ostream& os)
602 const auto si = this->AllSections.find("Generators");
603 if (si != this->AllSections.end()) {
604 this->Formatter.SetIndent(" ");
605 this->Formatter.PrintSection(os, si->second);
610 bool cmDocumentation::PrintHelpOneVariable(std::ostream& os)
612 std::string vname = cmSystemTools::HelpFileName(this->CurrentArgument);
613 if (this->PrintFiles(os, "variable/" + vname)) {
616 // Argument was not a variable. Complain.
617 os << "Argument \"" << this->CurrentArgument
618 << "\" to --help-variable is not a defined variable. "
619 << "Use --help-variable-list to see all defined variables.\n";
623 bool cmDocumentation::PrintHelpListVariables(std::ostream& os)
625 this->PrintNames(os, "variable/*");
629 bool cmDocumentation::PrintUsage(std::ostream& os)
631 const auto si = this->AllSections.find("Usage");
632 if (si != this->AllSections.end()) {
633 this->Formatter.PrintSection(os, si->second);
638 bool cmDocumentation::PrintHelp(std::ostream& os)
640 auto si = this->AllSections.find("Usage");
641 if (si != this->AllSections.end()) {
642 this->Formatter.PrintSection(os, si->second);
644 si = this->AllSections.find("Options");
645 if (si != this->AllSections.end()) {
646 this->Formatter.PrintSection(os, si->second);
648 if (this->ShowGenerators) {
649 si = this->AllSections.find("Generators");
650 if (si != this->AllSections.end()) {
651 this->Formatter.PrintSection(os, si->second);
657 const char* cmDocumentation::GetNameString() const
659 if (!this->NameString.empty()) {
660 return this->NameString.c_str();
665 bool cmDocumentation::IsOption(const char* arg) const
667 return ((arg[0] == '-') || (strcmp(arg, "/V") == 0) ||
668 (strcmp(arg, "/?") == 0));
671 bool cmDocumentation::PrintOldCustomModules(std::ostream& os)
673 // CheckOptions abuses the Argument field to give us the file name.
674 std::string filename = this->CurrentArgument;
675 std::string ext = cmSystemTools::UpperCase(
676 cmSystemTools::GetFilenameLastExtension(filename));
677 std::string name = cmSystemTools::GetFilenameWithoutLastExtension(filename);
679 const char* summary = "cmake --help-custom-modules no longer supported\n";
681 "CMake versions prior to 3.0 exposed their internal module help page\n"
682 "generation functionality through the --help-custom-modules option.\n"
683 "CMake versions 3.0 and above use other means to generate their module\n"
684 "help pages so this functionality is no longer available to be exposed.\n"
686 "This file was generated as a placeholder to provide this information.\n";
687 if ((ext == ".HTM") || (ext == ".HTML")) {
688 os << "<html><title>" << name << "</title><body>\n"
689 << summary << "<p/>\n"
690 << detail << "</body></html>\n";
691 } else if ((ext.length() == 2) && (ext[1] >= '1') && (ext[1] <= '9')) {
692 /* clang-format off */
694 ".TH " << name << " " << ext[1] << " \"" <<
695 cmSystemTools::GetCurrentDateTime("%B %d, %Y") <<
696 "\" \"cmake " << cmVersion::GetCMakeVersion() << "\"\n"
699 name << " \\- " << summary <<
705 /* clang-format on */
707 os << name << "\n\n" << summary << "\n" << detail;