diff --git a/docs/common_options.md b/docs/common_options.md index d46f6d44..e1e8f0a2 100644 --- a/docs/common_options.md +++ b/docs/common_options.md @@ -6,6 +6,10 @@ * [Translation unit glob patterns](#translation-unit-glob-patterns) * [PlantUML custom directives](#plantuml-custom-directives) * [Adding debug information in the generated diagrams](#adding-debug-information-in-the-generated-diagrams) +* [Resolving include path and compiler flags issues](#resolving-include-path-and-compiler-flags-issues) + * [Use `--query-driver` command line option](#use---query-driver-command-line-option) + * [Manually add and remove compile flags from the compilation database](#manually-add-and-remove-compile-flags-from-the-compilation-database) + * [Using `CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES`](#using-cmake_cxx_implicit_include_directories) @@ -85,4 +89,99 @@ debug_mode: true ``` the generated PlantUML diagram will contain comments before each line containing the source location of the -specific diagram element. \ No newline at end of file +specific diagram element. + +## Resolving include path and compiler flags issues +Due to the fact, that your project can be compiled with different compilers +and toolchains than the Clang version, which `clang-uml` uses on your platform, +include paths specified in the generated `compile_commands.json` can be incorrect. + +> This is often an issue on macOS, when `clang-uml` uses Homebrew version of LLVM +> and your project was built using system Apple Clang + +Typically, this results in ugly error messages on the screen during diagram +generation, such as: + +``` +... fatal: 'stddef.h' file not found +``` + +or + +``` +... warning: implicit conversion from 'int' to 'float' changes value from 2147483647 to 2147483648 [-Wimplicit-const-int-float-conversion] +``` + +These errors can be overcome, by ensuring that the Clang parser has the correct +include paths to analyse your code base on the given platform. `clang-uml` +provides several mechanisms to resolve this issue: + +### Use `--query-driver` command line option + +> This option is not available on Windows. + +Providing this option on the `clang-uml` command line will result in `clang-uml` +executing the specified compiler with the following command, e.g.: + +```bash +/usr/bin/c++ -E -v -x c /dev/null 2>&1 +``` + +and extracting from the output the target and system include paths, which are +then injected to each entry of the compilation database. For instance, on my +system, when generating diagrams for an embedded project and providing +`arm-none-eabi-gcc` as driver: + +```bash +$ clang-uml --query-driver arm-none-eabi-gcc +``` + +the following options are appended to each command line after `argv[0]` of the +command: + +```bash +--target=arm-none-eabi -isystem /usr/lib/gcc/arm-none-eabi/10.3.1/include -isystem /usr/lib/gcc/arm-none-eabi/10.3.1/include-fixed -isystem /usr/lib/gcc/arm-none-eabi/10.3.1/../../../arm-none-eabi/include +``` + +If you want to include the system headers reported by the compiler specified +already as `argv[0]` in your `compile_commands.json`, you can simply invoke +`clang-uml` as: + +```bash +$ clang-uml --query-driver . +``` + +however please make sure that the `compile_commands.json` contain a command, +which is safe to execute. + +### Manually add and remove compile flags from the compilation database +If the system paths extracted from the compiler are not sufficient to resolve +include paths issues, it is possible to manually adjust the compilation +flags providing `add_compile_flags` and `remove_compile_flags` in the +configuration file, or providing `--add-compile-flag` and `--remove-compile-flag` +in the `clang-uml` command line. + +For instance: + +```yaml +add_compile_flags: + - -I/opt/my_toolchain/include +remove_compile_flags: + - -I/usr/include +``` + +These options can be also passed on the command line, for instance: + +```bash +$ clang-uml --add-compile-flag -I/opt/my_toolchain/include \ + --remove-compile-flag -I/usr/include ... +``` + +### Using `CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES` +Yet another option, for CMake based projects, is to use the following CMake option: + +```cmake +set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}) +``` + + diff --git a/docs/configuration_file.md b/docs/configuration_file.md index 25493ff1..646f6eaf 100644 --- a/docs/configuration_file.md +++ b/docs/configuration_file.md @@ -18,6 +18,7 @@ * `debug_mode` - add inline debug information in the generated diagrams * `add_compile_flags` - add compile flags to all compilation database entries * `remove_compile_flags` - remove compile flags from all compilation database entries +* `query_driver` - name or path to compiler driver, which should be queried for system include paths (e.g. arm-none-eabi-g++) ### Diagram options * `type` - type of diagram, one of [`class`, `sequence`, `package`, `include`] @@ -61,6 +62,9 @@ add_compile_flags: # Remove specified compile flags from all compilation database entries remove_compile_flags: - '-Wshadow' +# Compiler driver command to query for system include paths +query_driver: + - arm-none-eabi-g++ # The directory where *.puml files will be generated output_directory: docs/diagrams # Set this as default for all diagrams diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 23a4c3af..d5f18baf 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -15,17 +15,24 @@ ## General issues + ### Diagram generated with PlantUML is cropped -When generating diagrams with PlantUML without specifying an output file format, the default is PNG. -Unfortunately PlantUML will not check if the diagram will fit in the default PNG size, and often the diagram -will be incomplete in the picture. A better option is to specify SVG as output format and then convert + +When generating diagrams with PlantUML without specifying an output file format, +the default is PNG. +Unfortunately PlantUML will not check if the diagram will fit in the default PNG +size, and often the diagram +will be incomplete in the picture. A better option is to specify SVG as output +format and then convert to PNG, e.g.: + ```bash $ plantuml -tsvg mydiagram.puml $ convert +antialias mydiagram.svg mydiagram.png ``` ### `clang` produces several warnings during diagram generation + During the generation of the diagram `clang` may report a lot of warnings, which do not occur during the compilation with other compiler (e.g. GCC). This can be fixed easily by using the `add_compile_flags` config option. For instance, @@ -63,9 +70,10 @@ remove_compile_flags: ``` ### YAML anchors and aliases are not fully supported + `clang-uml` uses [yaml-cpp](https://github.com/jbeder/yaml-cpp) library, which currently does not support -[merging YAML anchor dictionaries](https://github.com/jbeder/yaml-cpp/issues/41), +[merging YAML anchor dictionaries](https://github.com/jbeder/yaml-cpp/issues/41), e.g. in the following configuration file the `main_sequence_diagram` will work, but the `foo_sequence_diagram` will fail with parse error: @@ -98,25 +106,44 @@ yq 'explode(.)' .clang-uml | clang-uml --config - ## Class diagrams ### "fatal error: 'stddef.h' file not found" -This error means that Clang cannot find some standard headers in the include paths -specified in the `compile_commands.json`. This typically happens on macOS and sometimes on Linux, when -the code was compiled with different Clang version than `clang-uml` itself. -One solution to this issue is to add the following line to your `CMakeLists.txt` file: +This error means that Clang cannot find some standard headers in the include +paths specified in the `compile_commands.json`. This typically happens on macOS +and sometimes on Linux, when the code was compiled with different Clang version +than `clang-uml` itself. + +One solution to this issue is to add the following line to your `CMakeLists.txt` +file: ```cmake set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}) ``` -Another option is to make sure that the Clang is installed on the system (even if not used for building your +Another option is to provide an option (on command line or in configuration +file) called `query_driver` (inspired by the [clangd](https://clangd.llvm.org/) +language server - although much less flexible), which will invoke the +provider compiler command and query it for its default system paths, which then +will be added to each compile command in the database. This is especially useful +on macOS as well as for embedded toolchains, example usage: + +```bash +$ clang-uml --query-driver arm-none-eabi-g++ +``` + +Another option is to make sure that the Clang is installed on the system (even +if not used for building your project), e.g.: + ```bash apt install clang ``` -If this doesn't help the include paths can be customized using config options: - * `add_compile_flags` - which adds a list of compile flags such as include paths to each entry of the compilation database - * `remove_compile_flags` - which removes existing compile flags from each entry of the compilation database +If this doesn't help to include paths can be customized using config options: + +* `add_compile_flags` - which adds a list of compile flags such as include paths + to each entry of the compilation database +* `remove_compile_flags` - which removes existing compile flags from each entry + of the compilation database For instance: @@ -133,28 +160,43 @@ These options can be also passed on the command line, for instance: $ clang-uml --add-compile-flag -I/opt/my_toolchain/include \ --remove-compile-flag -I/usr/include ... ``` + ## Sequence diagrams ### Generated diagram is empty -In order to generate sequence diagram the `start_from` configuration option must have a valid starting point -for the diagram (e.g. `function`), which must match exactly the function signature in the `clang-uml` model. + +In order to generate sequence diagram the `start_from` configuration option must +have a valid starting point +for the diagram (e.g. `function`), which must match exactly the function +signature in the `clang-uml` model. Look for error in the console output such as: + ```bash Failed to find participant mynamespace::foo(int) for start_from condition ``` -which means that either you have a typo in the function signature in the configuration file, or that the function -was not defined in the translation units you specified in the `glob` patterns for this diagram. Run again the -`clang-uml` tool with `-vvv` option and look in the console output for any mentions of the function from -which the diagram should start and copy the exact signature into the configuration file. + +which means that either you have a typo in the function signature in the +configuration file, or that the function +was not defined in the translation units you specified in the `glob` patterns +for this diagram. Run again the +`clang-uml` tool with `-vvv` option and look in the console output for any +mentions of the function from +which the diagram should start and copy the exact signature into the +configuration file. ### Generated diagram contains several empty control blocks or calls which should not be there -Currently the filtering of call expressions and purging empty control blocks (e.g. loops or conditional statements), -within which no interesting calls were included in the diagram is not perfect. In case the regular `namespaces` filter -is not enough, it is useful to add also a `paths` filter, which will only include participants and call expressions + +Currently the filtering of call expressions and purging empty control blocks ( +e.g. loops or conditional statements), +within which no interesting calls were included in the diagram is not perfect. +In case the regular `namespaces` filter +is not enough, it is useful to add also a `paths` filter, which will only +include participants and call expressions from files in a subdirectory of your project, e.g.: + ```yaml include: - namespaces: - - myproject - paths: - - src + namespaces: + - myproject + paths: + - src ``` diff --git a/src/cli/cli_handler.cc b/src/cli/cli_handler.cc index 08220881..bebb0759 100644 --- a/src/cli/cli_handler.cc +++ b/src/cli/cli_handler.cc @@ -88,6 +88,11 @@ cli_flow_t cli_handler::parse(int argc, const char **argv) app.add_option("--remove-compile-flag", remove_compile_flag, "Remove a compilation flag from each entry in the compilation " "database"); +#if !defined(_WIN32) + app.add_option("--query-driver", query_driver, + "Query the specific compiler driver to extract system paths and add to " + "compile commands (e.g. arm-none-eabi-g++)"); +#endif app.add_option( "--add-class-diagram", add_class_diagram, "Add class diagram config"); app.add_option("--add-sequence-diagram", add_sequence_diagram, @@ -289,6 +294,12 @@ cli_flow_t cli_handler::handle_post_config_options() config.remove_compile_flags.has_value = true; } +#if !defined(_WIN32) + if (query_driver) { + config.query_driver.set(*query_driver); + } +#endif + return cli_flow_t::kContinue; } diff --git a/src/cli/cli_handler.h b/src/cli/cli_handler.h index a2dce4d1..6e4f097b 100644 --- a/src/cli/cli_handler.h +++ b/src/cli/cli_handler.h @@ -123,6 +123,9 @@ public: bool initialize{false}; std::optional> add_compile_flag; std::optional> remove_compile_flag; +#if !defined(_WIN32) + std::optional query_driver; +#endif std::optional add_class_diagram; std::optional add_sequence_diagram; std::optional add_package_diagram; diff --git a/src/common/compilation_database.cc b/src/common/compilation_database.cc index 83772bae..3e762e89 100644 --- a/src/common/compilation_database.cc +++ b/src/common/compilation_database.cc @@ -18,6 +18,8 @@ #include "compilation_database.h" +#include "util/query_driver_output_extractor.h" + namespace clanguml::common { std::unique_ptr @@ -77,9 +79,49 @@ compilation_database::getAllCompileCommands() const return commands; } +std::string compilation_database::guess_language_from_filename( + const std::string &filename) const +{ + if (util::ends_with(filename, std::string{".c"})) + return "c"; + + return "c++"; +} + void compilation_database::adjust_compilation_database( std::vector &commands) const { +#if !defined(_WIN32) + if (config().query_driver && !config().query_driver().empty()) { + for (auto &compile_command : commands) { + auto argv0 = config().query_driver() == "." + ? compile_command.CommandLine.at(0) + : config().query_driver(); + + util::query_driver_output_extractor extractor{ + argv0, guess_language_from_filename(compile_command.Filename)}; + + extractor.execute(); + + std::vector system_header_args; + for (const auto &path : extractor.system_include_paths()) { + system_header_args.emplace_back("-isystem"); + system_header_args.emplace_back(path); + } + + compile_command.CommandLine.insert( + compile_command.CommandLine.begin() + 1, + system_header_args.begin(), system_header_args.end()); + + if (!extractor.target().empty()) { + compile_command.CommandLine.insert( + compile_command.CommandLine.begin() + 1, + fmt::format("--target={}", extractor.target())); + } + } + } +#endif + if (config().add_compile_flags && !config().add_compile_flags().empty()) { for (auto &compile_command : commands) { compile_command.CommandLine.insert( diff --git a/src/common/compilation_database.h b/src/common/compilation_database.h index 7a98f27c..e2e277a7 100644 --- a/src/common/compilation_database.h +++ b/src/common/compilation_database.h @@ -61,6 +61,8 @@ public: const clang::tooling::CompilationDatabase &base() const; + std::string guess_language_from_filename(const std::string &filename) const; + private: void adjust_compilation_database( std::vector &commands) const; diff --git a/src/config/config.h b/src/config/config.h index 807d80ef..f9f7f80e 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -228,6 +228,7 @@ struct config : public inheritable_diagram_options { option> add_compile_flags{"add_compile_flags"}; option> remove_compile_flags{ "remove_compile_flags"}; + option query_driver{"query_driver"}; option output_directory{"output_directory"}; option> diagram_templates{ diff --git a/src/main.cc b/src/main.cc index 825911a7..5ffb49e8 100644 --- a/src/main.cc +++ b/src/main.cc @@ -20,6 +20,7 @@ #include "common/compilation_database.h" #include "common/generators/generators.h" #include "include_diagram/generators/plantuml/include_diagram_generator.h" +#include "util/query_driver_output_extractor.h" #include "util/util.h" #ifdef ENABLE_BACKWARD_CPP @@ -81,6 +82,13 @@ int main(int argc, const char *argv[]) cli.config.compilation_database_dir(), e.what()); return 1; } + catch (clanguml::util::query_driver_no_paths &e) { + LOG_ERROR("Quering provided compiler driver {} did not provide any " + "paths, please make sure the path is correct and that your " + "compiler is GCC-compatible: {}", + cli.config.query_driver(), e.what()); + return 1; + } return 0; } diff --git a/src/util/query_driver_output_extractor.cc b/src/util/query_driver_output_extractor.cc new file mode 100644 index 00000000..73245817 --- /dev/null +++ b/src/util/query_driver_output_extractor.cc @@ -0,0 +1,106 @@ +/** + * src/util/query_driver_include_extractor.cc + * + * Copyright (c) 2021-2023 Bartek Kryza + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "query_driver_output_extractor.h" + +#include "util.h" + +#include +#include + +namespace clanguml::util { + +query_driver_output_extractor::query_driver_output_extractor( + std::string command, std::string language) + : command_{std::move(command)} + , language_{std::move(language)} +{ +} + +void query_driver_output_extractor::execute() +{ + auto cmd = + fmt::format("{} -E -v -x {} /dev/null 2>&1", command_, language_); + + LOG_DBG("Executing query driver command: {}", cmd); + + auto driver_output = get_process_output(cmd); + + system_include_paths_.clear(); + extract_system_include_paths(driver_output); + extract_target(driver_output); + + if (system_include_paths_.empty()) { + throw query_driver_no_paths(fmt::format( + "Compiler driver {} did not report any system include paths " + "in its output: {}", + command_, driver_output)); + } + + LOG_DBG("Extracted the following paths from compiler driver: {}", + fmt::join(system_include_paths_, ",")); +} + +void query_driver_output_extractor::extract_target(const std::string &output) +{ + std::istringstream f(output); + std::string line; + + while (std::getline(f, line)) { + line = trim(line); + if (util::starts_with(line, std::string{"Target: "})) { + target_ = line.substr(strlen("Target: ")); + break; + } + } +} + +void query_driver_output_extractor::extract_system_include_paths( + const std::string &output) +{ + std::istringstream f(output); + std::string line; + + bool in_include_paths_range{false}; + while (std::getline(f, line)) { + line = trim(line); + if (line == "#include <...> search starts here:") { + in_include_paths_range = true; + continue; + } + if (line == "End of search list.") { + break; + } + + if (in_include_paths_range) { + system_include_paths_.emplace_back(line); + } + } +} + +const std::vector & +query_driver_output_extractor::system_include_paths() const +{ + return system_include_paths_; +} + +const std::string &query_driver_output_extractor::target() const +{ + return target_; +} +} // namespace clanguml::util \ No newline at end of file diff --git a/src/util/query_driver_output_extractor.h b/src/util/query_driver_output_extractor.h new file mode 100644 index 00000000..5b08df87 --- /dev/null +++ b/src/util/query_driver_output_extractor.h @@ -0,0 +1,52 @@ +/** + * src/util/query_driver_include_extractor.h + * + * Copyright (c) 2021-2023 Bartek Kryza + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include + +namespace clanguml::util { + +class query_driver_no_paths : public std::runtime_error { + using std::runtime_error::runtime_error; +}; + +class query_driver_output_extractor { +public: + query_driver_output_extractor(std::string command, std::string language); + + ~query_driver_output_extractor() = default; + + void execute(); + + void extract_target(const std::string &output); + + void extract_system_include_paths(const std::string &output); + + const std::vector &system_include_paths() const; + + const std::string &target() const; + +private: + const std::string command_; + const std::string language_; + std::string target_; + std::vector system_include_paths_; +}; +} // namespace clanguml::util \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index bd41026d..9265d456 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -43,7 +43,8 @@ set(TEST_CASES test_config test_cli_handler test_filters - test_thread_pool_executor) + test_thread_pool_executor + test_query_driver_output_extractor) foreach(TEST_NAME ${TEST_CASES}) add_executable(${TEST_NAME} diff --git a/tests/test_compilation_database.cc b/tests/test_compilation_database.cc index b1ed87a9..aba0085e 100644 --- a/tests/test_compilation_database.cc +++ b/tests/test_compilation_database.cc @@ -39,6 +39,7 @@ TEST_CASE("Test compilation_database should work", "[unit-test]") using clanguml::common::model::access_t; using clanguml::common::model::relationship_t; using clanguml::util::contains; + using std::filesystem::path; auto cfg = clanguml::config::load("./test_compilation_database_data/config.yml"); @@ -51,12 +52,19 @@ TEST_CASE("Test compilation_database should work", "[unit-test]") auto all_files = db->getAllFiles(); REQUIRE(all_files.size() == 3); - REQUIRE(all_files.at(0) == - "src/class_diagram/generators/json/class_diagram_generator.cc"); - REQUIRE(all_files.at(1) == - "src/class_diagram/generators/plantuml/" - "class_diagram_generator.cc"); - REQUIRE(all_files.at(2) == "src/class_diagram/model/class.cc"); + REQUIRE(contains(all_files, + path("src/class_diagram/generators/json/class_diagram_generator.cc") + .make_preferred() + .string())); + REQUIRE(contains(all_files, + path("src/class_diagram/generators/plantuml/" + "class_diagram_generator.cc") + .make_preferred() + .string())); + REQUIRE(contains(all_files, + path("src/class_diagram/model/class.cc") + .make_preferred() + .string())); auto ccs = db->getAllCompileCommands(); diff --git a/tests/test_query_driver_output_extractor.cc b/tests/test_query_driver_output_extractor.cc new file mode 100644 index 00000000..361b27f5 --- /dev/null +++ b/tests/test_query_driver_output_extractor.cc @@ -0,0 +1,73 @@ +/** + * tests/test_query_driver_output_extractor.cc + * + * Copyright (c) 2021-2023 Bartek Kryza + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define CATCH_CONFIG_MAIN + +#include "util/query_driver_output_extractor.h" + +#include "catch.h" + +TEST_CASE("Test extract system include paths", "[unit-test]") +{ + + std::string output = R"(### +Using built-in specs. +COLLECT_GCC=g++ +OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa +OFFLOAD_TARGET_DEFAULT=1 +Target: x86_64-linux-gnu +Configured with: ../src/configure -v +Thread model: posix +Supported LTO compression algorithms: zlib zstd +gcc version 11.3.0 (Ubuntu 11.3.0-1ubuntu1~22.04) +COLLECT_GCC_OPTIONS='-E' '-v' '-shared-libgcc' '-mtune=generic' '-march=x86-64' + /usr/lib/gcc/x86_64-linux-gnu/11/cc1 -E -quiet -v -imultiarch x86_64-linux-gnu /dev/null -mtune=generic -march=x86-64 -fasynchronous-unwind-tables -fstack-protector-strong -Wformat -Wformat-security -fstack-clash-protection -fcf-protection -dumpbase null +ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu" +ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/11/include-fixed" +ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/11/../../../../x86_64-linux-gnu/include" +#include "..." search starts here: +#include <...> search starts here: + /usr/lib/gcc/x86_64-linux-gnu/11/include + /usr/local/include + /usr/include/x86_64-linux-gnu + /usr/include +End of search list. +# 0 "/dev/null" +# 0 "" +# 0 "" +# 1 "/usr/include/stdc-predef.h" 1 3 4 +# 0 "" 2 +# 1 "/dev/null" +COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/11/:/usr/lib/gcc/x86_64-linux-gnu/11/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/11/:/usr/lib/gcc/x86_64-linux-gnu/ +LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/11/:/usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/11/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/11/../../../:/lib/:/usr/lib/ +COLLECT_GCC_OPTIONS='-E' '-v' '-shared-libgcc' '-mtune=generic' '-march=x86-64' +###)"; + + clanguml::util::query_driver_output_extractor extractor("g++", "c++"); + + extractor.extract_system_include_paths(output); + + extractor.extract_target(output); + + std::vector expected = { + "/usr/lib/gcc/x86_64-linux-gnu/11/include", "/usr/local/include", + "/usr/include/x86_64-linux-gnu", "/usr/include"}; + + REQUIRE(extractor.system_include_paths() == expected); + + REQUIRE(extractor.target() == "x86_64-linux-gnu"); +} \ No newline at end of file