Added --query-driver option to enable automatic detection of system include paths from selected compiler (#109)

This commit is contained in:
Bartek Kryza
2023-05-06 15:58:45 +02:00
parent 023fac07f9
commit 9b2adc5d2f
12 changed files with 329 additions and 26 deletions

View File

@@ -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,10 @@ cli_flow_t cli_handler::handle_post_config_options()
config.remove_compile_flags.has_value = true;
}
if (query_driver) {
config.query_driver.set(*query_driver);
}
return cli_flow_t::kContinue;
}

View File

@@ -123,6 +123,9 @@ public:
bool initialize{false};
std::optional<std::vector<std::string>> add_compile_flag;
std::optional<std::vector<std::string>> remove_compile_flag;
#if !defined(_WIN32)
std::optional<std::string> query_driver;
#endif
std::optional<std::string> add_class_diagram;
std::optional<std::string> add_sequence_diagram;
std::optional<std::string> add_package_diagram;

View File

@@ -18,6 +18,8 @@
#include "compilation_database.h"
#include "util/query_driver_output_extractor.h"
namespace clanguml::common {
std::unique_ptr<compilation_database>
@@ -77,9 +79,40 @@ 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<clang::tooling::CompileCommand> &commands) const
{
#if !defined(_WIN32)
if (config().query_driver && !config().query_driver().empty()) {
for (auto &compile_command : commands) {
util::query_driver_output_extractor extractor{
config().query_driver(),
guess_language_from_filename(compile_command.Filename)};
extractor.execute();
std::vector<std::string> 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());
}
}
#endif
if (config().add_compile_flags && !config().add_compile_flags().empty()) {
for (auto &compile_command : commands) {
compile_command.CommandLine.insert(

View File

@@ -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<clang::tooling::CompileCommand> &commands) const;

View File

@@ -228,6 +228,7 @@ struct config : public inheritable_diagram_options {
option<std::vector<std::string>> add_compile_flags{"add_compile_flags"};
option<std::vector<std::string>> remove_compile_flags{
"remove_compile_flags"};
option<std::string> query_driver{"query_driver"};
option<std::string> output_directory{"output_directory"};
option<std::map<std::string, diagram_template>> diagram_templates{

View File

@@ -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;
}

View File

@@ -0,0 +1,84 @@
/**
* src/util/query_driver_include_extractor.cc
*
* Copyright (c) 2021-2023 Bartek Kryza <bkryza@gmail.com>
*
* 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"
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);
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_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<std::string> &
query_driver_output_extractor::system_include_paths() const
{
return system_include_paths_;
}
} // namespace clanguml::util

View File

@@ -0,0 +1,47 @@
/**
* src/util/query_driver_include_extractor.h
*
* Copyright (c) 2021-2023 Bartek Kryza <bkryza@gmail.com>
*
* 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 <stdexcept>
#include <string>
#include <vector>
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_system_include_paths(const std::string &output);
const std::vector<std::string> &system_include_paths() const;
private:
const std::string command_;
const std::string language_;
std::vector<std::string> system_include_paths_;
};
} // namespace clanguml::util