diff --git a/CMakeLists.txt b/CMakeLists.txt index a31af70c02..41b0d2216f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -401,7 +401,7 @@ if (MRDOCS_BUILD_TESTS) "--stdlib-includes=${LIBCXX_DIR}" "--stdlib-includes=${STDLIB_INCLUDE_DIR}" "--libc-includes=${CMAKE_SOURCE_DIR}/share/mrdocs/headers/libc-stubs" - --report=2 + --log-level=warn ) foreach (action IN ITEMS test create update) add_custom_target( @@ -416,7 +416,7 @@ if (MRDOCS_BUILD_TESTS) "--stdlib-includes=${LIBCXX_DIR}" "--stdlib-includes=${STDLIB_INCLUDE_DIR}" "--libc-includes=${CMAKE_SOURCE_DIR}/share/mrdocs/headers/libc-stubs" - --report=2 + --log-level=warn DEPENDS mrdocs-test ) endforeach () diff --git a/docs/extensions/config-options-reference.js b/docs/extensions/config-options-reference.js index 95d5603f9e..f520f729b6 100644 --- a/docs/extensions/config-options-reference.js +++ b/docs/extensions/config-options-reference.js @@ -90,6 +90,10 @@ function toDefaultValueStr(value) { } function pushOptionBlocks(options, block, parents = []) { + function makeOptionID(option) { + return [...parents, option.name].join('_') + "_option" + } + block.lines.push('') block.lines.push('') block.lines.push('') @@ -108,7 +112,8 @@ function pushOptionBlocks(options, block, parents = []) { let optionName = [...parents, option.name].join('.') block.lines.push('') block.lines.push(`
`) - block.lines.push(`${optionName}`) + const colorStr = option['deprecated'] ? 'red' : 'darkblue' + block.lines.push(`${optionName}`) block.lines.push(`
`) block.lines.push(`(${toTypeStr(option.type)})`) let observations = [] @@ -118,8 +123,11 @@ function pushOptionBlocks(options, block, parents = []) { if (option['command-line-only']) { observations.push('Command line only') } + if (option['deprecated']) { + observations.push(`Deprecated`) + } if (observations.length !== 0) { - block.lines.push(`

`) + block.lines.push(`
`) let observationsStr = observations.join(', ') block.lines.push(`(${observationsStr})`) } @@ -134,7 +142,9 @@ function pushOptionBlocks(options, block, parents = []) { // Option details for (let option of options) { let optionName = [...parents, option.name].join('.') - block.lines.push(`

${optionName}

`) + const optionID = optionName.replace(/\./g, '_') + const colorStr = option['deprecated'] ? 'red' : 'darkblue' + block.lines.push(`

${optionName}

`) block.lines.push(`

${option.brief}

`) if (option.details) { block.lines.push(`

${replaceCodeTags(escapeHtml(option.details))}

`) @@ -142,6 +152,9 @@ function pushOptionBlocks(options, block, parents = []) { block.lines.push(`

`) block.lines.push(`

`) block.lines.push(`
    `) + if (option['deprecated']) { + block.lines.push(`
  • Deprecated: ${replaceCodeTags(escapeHtml(option['deprecated']))}
  • `) + } if (option.type) { block.lines.push(`
  • Type: ${toTypeStr(option.type)}
  • `) } else { diff --git a/docs/modules/ROOT/pages/config-file.adoc b/docs/modules/ROOT/pages/config-file.adoc index 6456fe4849..8d5c669f1e 100644 --- a/docs/modules/ROOT/pages/config-file.adoc +++ b/docs/modules/ROOT/pages/config-file.adoc @@ -149,13 +149,13 @@ namespace my_library === Private Symbols -The `implementation-detail` and `see-below` options can be used to designate symbols as implementation details or "see below" in the documentation. +The `implementation-defined` and `see-below` options can be used to designate symbols as implementation details or "see below" in the documentation. [,yaml] ---- include-symbols: - 'my_library::**' -implementation-detail: +implementation-defined: - 'my_library::detail::**' see-below: - 'my_library::see_below::**' diff --git a/docs/modules/ROOT/pages/generators.adoc b/docs/modules/ROOT/pages/generators.adoc index 7d057bc98a..84bce290ae 100644 --- a/docs/modules/ROOT/pages/generators.adoc +++ b/docs/modules/ROOT/pages/generators.adoc @@ -3,12 +3,12 @@ MrDocs uses a generator to convert the extracted symbols into documentation. MrDocs supports multiple output formats that can be specified via the `generate` option: -The `generate` option can be used to specify the output format: +The `generator` option can be used to specify the output format: [source,yaml] ---- # ... -generate: adoc +generator: adoc # ... ---- diff --git a/docs/modules/ROOT/pages/usage.adoc b/docs/modules/ROOT/pages/usage.adoc index e0bc02b9a4..7784f82491 100644 --- a/docs/modules/ROOT/pages/usage.adoc +++ b/docs/modules/ROOT/pages/usage.adoc @@ -233,7 +233,7 @@ And the user can specify that symbols in the `impl` namespace are implementation [source,yaml] ---- # ... -implementation-detail: impl::** +implementation-defined: impl::** # ... ---- diff --git a/docs/modules/ROOT/partials/mrdocs-example.yml b/docs/modules/ROOT/partials/mrdocs-example.yml index 9d2e1ebce4..cec7993041 100644 --- a/docs/modules/ROOT/partials/mrdocs-example.yml +++ b/docs/modules/ROOT/partials/mrdocs-example.yml @@ -1,3 +1,3 @@ source-root: ../include multipage: false -generate: adoc +generator: adoc diff --git a/docs/modules/ROOT/partials/mrdocs-schema-example.yml b/docs/modules/ROOT/partials/mrdocs-schema-example.yml index affa277355..51ccf6c9d3 100644 --- a/docs/modules/ROOT/partials/mrdocs-schema-example.yml +++ b/docs/modules/ROOT/partials/mrdocs-schema-example.yml @@ -3,4 +3,4 @@ source-root: ../include multipage: false -generate: adoc +generator: adoc diff --git a/docs/mrdocs.schema.json b/docs/mrdocs.schema.json index f92abfd400..a2e97b37f5 100644 --- a/docs/mrdocs.schema.json +++ b/docs/mrdocs.schema.json @@ -190,6 +190,19 @@ "title": "Standard Library include paths", "type": "array" }, + "log-level": { + "default": "info", + "description": "The reporting level determines the amount of information displayed during the generation of the documentation.", + "enum": [ + "trace", + "debug", + "info", + "warn", + "error", + "fatal" + ], + "title": "The minimum reporting level" + }, "multipage": { "default": true, "description": "Generates a multipage documentation. The output directory must be a directory. This option acts as a hint to the generator to create a multipage documentation. Whether the hint is followed or not depends on the generator.", @@ -237,11 +250,11 @@ "type": "boolean" }, "report": { - "default": 1, - "description": "The reporting level determines the amount of information displayed during the generation of the documentation. The levels are: 0 - no output, 1 - errors only, 2 - errors and warnings, 3 - errors, warnings, and information, 4 - errors, warnings, information, and debug information.", - "maximum": 4, - "minimum": 0, - "title": "The minimum reporting level: 0 to 4", + "default": -1, + "description": "The reporting level determines the amount of information displayed during the generation of the documentation. The value `-1` delegates the decision to the `log-level` option.", + "maximum": 5, + "minimum": -1, + "title": "The minimum reporting level", "type": "integer" }, "see-below": { diff --git a/include/mrdocs/Support/Error.hpp b/include/mrdocs/Support/Error.hpp index 65450e4bae..475f1ea823 100644 --- a/include/mrdocs/Support/Error.hpp +++ b/include/mrdocs/Support/Error.hpp @@ -2665,7 +2665,8 @@ namespace report { */ enum class Level { - debug = 0, + trace = 0, + debug, info, warn, error, @@ -2676,6 +2677,7 @@ enum class Level */ struct Results { + std::size_t traceCount; std::size_t debugCount; std::size_t infoCount; std::size_t warnCount; @@ -2699,8 +2701,7 @@ results; */ MRDOCS_DECL void -setMinimumLevel( - Level level) noexcept; +setMinimumLevel(Level level) noexcept; MRDOCS_DECL Level @@ -2825,6 +2826,17 @@ log( std::forward(args)...); } +/** Report a message to the console. +*/ +template +void +trace( + Located format, + Args&&... args) +{ + return log(Level::trace, format, std::forward(args)...); +} + /** Report a message to the console. */ template diff --git a/include/mrdocs/Support/ScopeExit.hpp b/include/mrdocs/Support/ScopeExit.hpp index 8c2f5613ca..eb9092266d 100644 --- a/include/mrdocs/Support/ScopeExit.hpp +++ b/include/mrdocs/Support/ScopeExit.hpp @@ -35,6 +35,9 @@ class ScopeExit { } }; +template +ScopeExit(F) -> ScopeExit; + template class ScopeExitRestore { T prev_; @@ -66,9 +69,6 @@ class ScopeExitRestore { } }; -template -ScopeExit(F) -> ScopeExit; - template ScopeExitRestore(T&) -> ScopeExitRestore; diff --git a/src/lib/AST/ASTVisitor.cpp b/src/lib/AST/ASTVisitor.cpp index 150f1d2ea7..5b02b8a874 100644 --- a/src/lib/AST/ASTVisitor.cpp +++ b/src/lib/AST/ASTVisitor.cpp @@ -2594,7 +2594,7 @@ checkFileFilters(std::string_view const symbolPath) const ASTVisitor::ExtractionInfo ASTVisitor:: -checkSymbolFilters(Decl const* D, bool AllowParent) +checkSymbolFilters(Decl const* D, bool const AllowParent) { // Use the cache if (auto const it = extraction_.find(D); it != extraction_.end()) diff --git a/src/lib/AST/ClangHelpers.hpp b/src/lib/AST/ClangHelpers.hpp index b99c71b652..f48e3d71f2 100644 --- a/src/lib/AST/ClangHelpers.hpp +++ b/src/lib/AST/ClangHelpers.hpp @@ -972,7 +972,7 @@ namespace detail { #define MRDOCS_SYMBOL_TRACE(D, C) \ SmallString<256> MRDOCS_SYMBOL_TRACE_UNIQUE_NAME; \ detail::printTraceName(D, C, MRDOCS_SYMBOL_TRACE_UNIQUE_NAME); \ - report::debug("{}", std::string_view(MRDOCS_SYMBOL_TRACE_UNIQUE_NAME.str())) + report::trace("{}", std::string_view(MRDOCS_SYMBOL_TRACE_UNIQUE_NAME.str())) #endif } // clang::mrdocs diff --git a/src/lib/Lib/Config.cpp b/src/lib/Lib/Config.cpp index 035aa649e4..dca6e966af 100644 --- a/src/lib/Lib/Config.cpp +++ b/src/lib/Lib/Config.cpp @@ -100,7 +100,7 @@ struct PublicSettingsVisitor { } else if constexpr (std::same_as || std::same_as) { - return normalizeInteger(name, value, opts); + return normalizeInteger(self, name, value, opts); } else { @@ -328,15 +328,11 @@ struct PublicSettingsVisitor { template Expected normalizeInteger( + PublicSettings& self, std::string_view name, T& value, PublicSettings::OptionProperties const& opts) const { - if (name == "concurrency" && std::cmp_equal(value, 0)) - { - value = std::thread::hardware_concurrency(); - return {}; - } MRDOCS_CHECK( !opts.minValue || std::cmp_greater_equal(value, *opts.minValue), formatError( @@ -351,6 +347,30 @@ struct PublicSettingsVisitor { name, value, *opts.maxValue)); + + if (name == "concurrency" && std::cmp_equal(value, 0)) + { + value = std::thread::hardware_concurrency(); + return {}; + } + + if (name == "report" && std::cmp_not_equal(value, static_cast(-1))) + { + static_assert( + static_cast(PublicSettings::LogLevel::Trace) == + static_cast(report::Level::trace)); + static_assert( + static_cast(PublicSettings::LogLevel::Fatal) == + static_cast(report::Level::fatal)); + MRDOCS_ASSERT(opts.deprecated); + report::warn( + "`report` option is deprecated, use `log-level` instead"); + auto const logLevel = static_cast(value); + auto logLevelStr = PublicSettings::toString(logLevel); + report::warn("`report` option: setting `log-level` to \"{}\"", logLevelStr); + self.logLevel = logLevel; + return {}; + } return {}; } diff --git a/src/lib/Lib/ConfigOptions.json b/src/lib/Lib/ConfigOptions.json index 436499958b..52135a3093 100644 --- a/src/lib/Lib/ConfigOptions.json +++ b/src/lib/Lib/ConfigOptions.json @@ -350,9 +350,7 @@ "details": "The desired level of concurrency: 0 for hardware-suggested.", "type": "unsigned", "default": 0, - "value-mapping": { - "0": "std::thread::hardware_concurrency()" - } + "min-value": 0 }, { "name": "verbose", @@ -363,12 +361,28 @@ }, { "name": "report", - "brief": "The minimum reporting level: 0 to 4", - "details": "The reporting level determines the amount of information displayed during the generation of the documentation. The levels are: 0 - no output, 1 - errors only, 2 - errors and warnings, 3 - errors, warnings, and information, 4 - errors, warnings, information, and debug information.", + "brief": "The minimum reporting level", + "details": "The reporting level determines the amount of information displayed during the generation of the documentation. The value `-1` delegates the decision to the `log-level` option.", "type": "unsigned", - "default": 1, - "min-value": 0, - "max-value": 4 + "default": -1, + "min-value": -1, + "max-value": 5, + "deprecated": "Use `log-level` instead" + }, + { + "name": "log-level", + "brief": "The minimum reporting level", + "details": "The reporting level determines the amount of information displayed during the generation of the documentation.", + "type": "enum", + "values": [ + "trace", + "debug", + "info", + "warn", + "error", + "fatal" + ], + "default": "info" }, { "name": "ignore-map-errors", diff --git a/src/lib/Support/Error.cpp b/src/lib/Support/Error.cpp index 70a3fb2c3f..d9a497f16b 100644 --- a/src/lib/Support/Error.cpp +++ b/src/lib/Support/Error.cpp @@ -186,8 +186,7 @@ print_impl( } void -setMinimumLevel( - Level level) noexcept +setMinimumLevel(Level const level) noexcept { level_ = level; } @@ -253,11 +252,10 @@ call_impl( { llvm::raw_string_ostream os(s); f(os); + using LT = std::underlying_type_t; if(sourceLocationWarnings_ && - loc && ( - level == Level::warn || - level == Level::error || - level == Level::fatal)) + loc && + static_cast(level) >= static_cast(Level::error)) { os << "\n\n"; os << "An issue occurred during execution.\n"; @@ -288,6 +286,9 @@ call_impl( } switch(level) { + case Level::trace: + ++results.traceCount; + break; case Level::debug: ++results.debugCount; break; diff --git a/src/lib/Support/Error.hpp b/src/lib/Support/Error.hpp index 423e97d64b..8975582363 100644 --- a/src/lib/Support/Error.hpp +++ b/src/lib/Support/Error.hpp @@ -20,8 +20,7 @@ #include #include -namespace clang { -namespace mrdocs { +namespace clang::mrdocs { namespace report { @@ -129,8 +128,7 @@ call( } // report -} // mrdocs -} // clang +} // clang::mrdocs template<> struct fmt::formatter diff --git a/src/test/TestMain.cpp b/src/test/TestMain.cpp index 00cfabfb2b..f48e5aada0 100644 --- a/src/test/TestMain.cpp +++ b/src/test/TestMain.cpp @@ -94,10 +94,10 @@ int test_main(int argc, char const** argv) return EXIT_FAILURE; } - // Apply reportLevel - report::setMinimumLevel(report::getLevel( - testArgs.report.getValue())); - + // Apply log-level + auto ll = PublicSettings::LogLevel::Info; + PublicSettings::fromString(testArgs.logLevel.getValue(), ll); + report::setMinimumLevel(static_cast(ll)); report::setSourceLocationWarnings(false); if (!testArgs.cmdLineInputs.empty()) diff --git a/src/tool/GenerateAction.cpp b/src/tool/GenerateAction.cpp index 0724fdd1e7..f540e3dac6 100644 --- a/src/tool/GenerateAction.cpp +++ b/src/tool/GenerateAction.cpp @@ -5,6 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) // // Official repository: https://github.com/cppalliance/mrdocs // @@ -25,8 +26,7 @@ #include -namespace clang { -namespace mrdocs { +namespace clang::mrdocs { namespace { @@ -101,6 +101,7 @@ DoGenerateAction( MRDOCS_TRY(Config::Settings::load_file(publicSettings, configPath, dirs)); MRDOCS_TRY(toolArgs.apply(publicSettings, dirs, argv)); MRDOCS_TRY(publicSettings.normalize(dirs)); + report::setMinimumLevel(static_cast(publicSettings.logLevel)); ThreadPool threadPool(publicSettings.concurrency); MRDOCS_TRY( std::shared_ptr config, @@ -213,5 +214,4 @@ DoGenerateAction( return {}; } -} // mrdocs -} // clang +} // clang::mrdocs diff --git a/src/tool/ToolMain.cpp b/src/tool/ToolMain.cpp index 828407e2c9..9849ff508e 100644 --- a/src/tool/ToolMain.cpp +++ b/src/tool/ToolMain.cpp @@ -108,10 +108,6 @@ mrdocs_main(int argc, char const** argv) return EXIT_FAILURE; } - // Apply report level - report::setMinimumLevel(report::getLevel( - toolArgs.report.getValue())); - // Set up addons directory #ifdef __GNUC__ #pragma GCC diagnostic push @@ -124,6 +120,10 @@ mrdocs_main(int argc, char const** argv) #endif std::string execPath = llvm::sys::fs::getMainExecutable(argv[0], addressOfMain); + // Before `DoGenerateAction`, we use an error reporting level. + // DoGenerateAction will set the level to whatever is specified in + // the command line or the configuration file + report::setMinimumLevel(report::Level::error); auto res = getReferenceDirectories(execPath); if (!res) { @@ -141,8 +141,7 @@ mrdocs_main(int argc, char const** argv) auto configPath = *std::move(expConfigPath); // Generate - auto exp = DoGenerateAction(configPath, dirs, argv); - if (!exp) + if (auto exp = DoGenerateAction(configPath, dirs, argv); !exp) { report::error("Generating reference failed: {}", exp.error()); } diff --git a/util/generate-config-info.py b/util/generate-config-info.py index bc3122c2b2..4bc02a5f34 100644 --- a/util/generate-config-info.py +++ b/util/generate-config-info.py @@ -63,7 +63,8 @@ def get_flat_suboptions(option_name, options): def get_valid_enum_categories(): valid_enum_cats = { - 'generator': ["adoc", "html", "xml"] + 'generator': ["adoc", "html", "xml"], + 'log-level': ["trace", "debug", "info", "warn", "error", "fatal"] } return valid_enum_cats @@ -310,6 +311,7 @@ def generate_public_settings_hpp(config): contents += ' //--------------------------------------------\n' contents += ' // Enums\n' contents += ' //--------------------------------------------\n\n' + for [enum_name, enum_values] in get_valid_enum_categories().items(): options_that_use_it = [] for category in config: @@ -331,6 +333,31 @@ def generate_public_settings_hpp(config): contents += f' {to_pascal_case(enum_value)},\n' contents += ' };\n\n' + contents += f' static\n' + contents += f' constexpr\n' + contents += f' std::string_view\n' + contents += f' toString({to_pascal_case(enum_name)} const e) {{\n' + contents += f' switch (e) {{\n' + for enum_value in enum_values: + contents += f' case {to_pascal_case(enum_name)}::{to_pascal_case(enum_value)}:\n' + contents += f' return "{enum_value}";\n' + contents += f' }}\n' + contents += f' return "";\n' + contents += ' }\n\n' + + contents += f' static\n' + contents += f' constexpr\n' + contents += f' bool\n' + contents += f' fromString(std::string_view const str, {to_pascal_case(enum_name)}& e) {{\n' + for enum_value in enum_values: + contents += f' if (str == "{enum_value}")\n' + contents += f' {{\n' + contents += f' e = {to_pascal_case(enum_name)}::{to_pascal_case(enum_value)};\n' + contents += f' return true;\n' + contents += f' }}\n' + contents += f' return false;\n' + contents += ' }\n\n' + for category in config: category_name = category['category'] category_brief = category['brief'] @@ -393,6 +420,7 @@ def generate_public_settings_hpp(config): contents += f' {cpp_type}' contents += f'> {to_camel_case("default")}Value = std::monostate();\n' contents += f' std::string {to_camel_case("relative-to")} = {{}};\n' + contents += f' std::optional {to_camel_case("deprecated")} = std::nullopt;\n' contents += ' };\n\n' contents += ' /** Normalize the configuration values with a visitor\n' @@ -436,11 +464,13 @@ def generate_public_settings_hpp(config): # print the default in cpp if cpp_default_value is not None: cpp_type = to_cpp_type(option) - if cpp_type in ['bool', 'unsigned', 'int']: + if cpp_type in ['bool', 'unsigned', 'int', 'enum']: cpp_type = f'static_cast<{cpp_type}>' contents += f'{pad}.{to_camel_case("default")}Value = {cpp_type}({cpp_default_value}),\n' if 'relative-to' in option: contents += f'{pad}.{to_camel_case("relative-to")} = {escape_as_cpp_string(option["relative-to"])},\n' + if 'deprecated' in option: + contents += f'{pad}.{to_camel_case("deprecated")} = {escape_as_cpp_string(option["deprecated"])},\n' contents += f' }}));\n' contents += ' return {};\n' contents += ' }\n\n' @@ -907,8 +937,7 @@ def generate_public_toolargs_cpp(config): contents += f'#include {header}\n' contents += '\n' - contents += 'namespace clang {\n' - contents += 'namespace mrdocs {\n\n' + contents += 'namespace clang::mrdocs {\n\n' # Main constructor that initializes all options contents += 'PublicToolArgs::\n' @@ -1017,8 +1046,7 @@ def generate_public_toolargs_cpp(config): contents += ' return {};\n' contents += '}\n' - contents += '} // namespace mrdocs\n' - contents += '} // namespace clang\n\n' + contents += '} // namespace clang::mrdocs\n' return contents