Commit b126d137 authored by Pádraig Ó Conbhuí's avatar Pádraig Ó Conbhuí
Browse files

Add CLI11 parser to magadep.main.cpp. Add target_link_system_libraries function to CMake.

parent a06c730f
......@@ -7,6 +7,12 @@ project(
LANGUAGES CXX
)
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
include(target_link_system_libraries)
add_subdirectory(external/cli11)
add_library(
libmegadep
......@@ -26,6 +32,7 @@ add_executable(
src/megadep.main.cpp
)
target_link_libraries(megadep PRIVATE libmegadep)
target_link_system_libraries(megadep PRIVATE CLI11::CLI11)
option(MEGADEP_ENABLE_TESTING "Enable testing for Megadep" ON)
......
#
# target_link_system_libraries(<target>
# <PRIVATE|PUBLIC|INTERFACE> <item>...
# [<PRIVATE|PUBLIC|INTERFACE> <item>...]...)
#
# This has the same behaviour as `target_link_libraries`, except any
# INTERFACE_INCLUDE_DIRECTORIES are re-included as
# INTERFACE_SYSTEM_INCLUDE_DIRECTORIES and any INTERFACE_LINK_LIBRARIES are
# explicitly linked to the target using this function, which will recursively
# add any INTERFACE_INCLUDE_DIRECTORIES as INTERFACE_SYSTEM_INCLUDE_DIRECTORIES.
#
function(target_link_system_libraries target visibility item)
set(options)
set(one_value_keywords)
set(multi_value_keywords PUBLIC PRIVATE INTERFACE)
cmake_parse_arguments(
PARSE_ARGV 1 "_link"
"${options}" "${one_value_keywords}" "${multi_value_keywords}"
)
# Pass arguments to target_link_libraries as usual
target_link_libraries(${ARGV})
# For each library, get the INTERFACE_INCLUDE_DIRECTORIES property,
# and re-include them as target_include_directories(... SYSTEM ...)
foreach(linkage PUBLIC;PRIVATE;INTERFACE)
foreach(lib ${_link_${linkage}})
message("lib: ${lib}")
get_target_property(
_interface_includes ${lib} INTERFACE_INCLUDE_DIRECTORIES
)
get_target_property(
_interface_system_includes ${lib} INTERFACE_SYSTEM_INCLUDE_DIRECTORIES
)
get_target_property(
_interface_links ${lib} INTERFACE_LINK_LIBRARIES
)
if(_interface_includes)
target_include_directories(
${target} SYSTEM ${linkage} ${_interface_includes}
)
endif()
if(_interface_links)
target_link_system_libraries(
${target} ${linkage} ${_interface_links}
)
endif()
endforeach()
endforeach()
endfunction(target_link_system_libraries)
#include <megadep/filesystem.hpp>
#include <CLI/CLI.hpp>
#include <algorithm>
#include <array>
#include <fstream>
......@@ -7,6 +9,7 @@
#include <iterator>
#include <string>
#include <string_view>
#include <variant>
#include <vector>
// List of file suffixes the program will consider
......@@ -26,26 +29,12 @@ bool contains_suffix(std::string_view file_name) {
return false;
}
int main(int argc, char *argv[]) {
std::vector<std::string> project_directories;
std::vector<std::string> include_directories;
for (int i = 1; i < argc; i++) {
std::string_view argv_str = argv[i]; // NOLINT
if (argv_str.compare(0, 2, "-I") == 0) {
// -I for include directory
argv_str.remove_prefix(2);
include_directories.emplace_back(argv_str);
} else {
// Regular positional argument for project directory
project_directories.emplace_back(argv_str);
}
}
template <typename ScanDirectories, typename IncludeDirectories>
int build_dependencies(const ScanDirectories &scan_directories,
const IncludeDirectories &include_directories) {
std::cout << "Scanning:\n";
for (const auto &directory : project_directories) {
for (const auto &directory : scan_directories) {
std::cout << " - " << directory << '\n';
}
std::cout << '\n';
......@@ -57,7 +46,7 @@ int main(int argc, char *argv[]) {
std::cout << '\n';
std::vector<std::string> file_names;
for (const auto &directory : project_directories) {
for (const auto &directory : scan_directories) {
auto directory_file_names =
megadep::list_files(std::string{directory.data(), directory.size()});
......@@ -121,22 +110,18 @@ int main(int argc, char *argv[]) {
}
}
try {
std::ofstream direct_includes_file("direct-includes.yml",
std::ofstream::out);
for (size_t file_index = 0; file_index < file_to_includes.size();
file_index++) {
if (file_index != 0) {
direct_includes_file << '\n';
}
direct_includes_file << file_names[file_index] << ":\n";
for (const auto &include : file_to_includes[file_index]) {
direct_includes_file << " - " << file_names[include] << '\n';
}
std::ofstream direct_includes_file("direct-includes.yml", std::ofstream::out);
for (size_t file_index = 0; file_index < file_to_includes.size();
file_index++) {
if (file_index != 0) {
direct_includes_file << '\n';
}
direct_includes_file << file_names[file_index] << ":\n";
for (const auto &include : file_to_includes[file_index]) {
direct_includes_file << " - " << file_names[include] << '\n';
}
direct_includes_file.close();
} catch (...) {
}
direct_includes_file.close();
//
// Inverse direct include map
......@@ -178,20 +163,143 @@ int main(int argc, char *argv[]) {
recursive_add_to_includers(recursive_add_to_includers, file_index);
}
try {
std::ofstream indirect_includes_file("indirect-includes.yml",
std::ofstream::out);
for (size_t file_index = 0; file_index < file_to_includes.size();
file_index++) {
if (file_index != 0) {
indirect_includes_file << '\n';
std::ofstream indirect_includes_file("indirect-includes.yml",
std::ofstream::out);
for (size_t file_index = 0; file_index < file_to_includes.size();
file_index++) {
if (file_index != 0) {
indirect_includes_file << '\n';
}
indirect_includes_file << file_names[file_index] << ":\n";
for (const auto &include : file_to_indirect_includes[file_index]) {
indirect_includes_file << " - " << file_names[include] << '\n';
}
}
indirect_includes_file.close();
return EXIT_SUCCESS;
}
struct CLIOptions {
std::vector<std::string> scan_directories;
std::vector<std::string> include_directories;
};
class CLIParser {
CLI::App app;
std::vector<std::string> m_scan_directories;
std::vector<std::string> m_include_directories;
std::vector<std::string> m_system_include_directories;
public:
CLIParser() : app{"megadep: distributed dependency scanner"} {
app.add_option("scan_directory", m_scan_directories, "Scan directory");
app.add_option("--include,-I", m_include_directories, "Include directory");
app.add_option("--isystem", m_system_include_directories,
"System include directory");
}
std::variant<CLIOptions, int> parse(int argc, char **argv) {
auto fixed_args = fixup_args(app, argc, argv);
auto fixed_argv = arg_ptrs(fixed_args);
for(const auto& arg: fixed_args) {
std::cout << "arg: " << arg << '\n';
}
try {
app.parse(argc, fixed_argv.data());
} catch (const CLI::ParseError &e) {
return app.exit(e);
}
// Combine include_directories and system_include_directories
std::vector<std::string> combined_includes;
combined_includes.reserve(m_include_directories.size() +
m_system_include_directories.size());
std::copy(m_include_directories.begin(), m_include_directories.end(),
std::back_inserter(combined_includes));
std::copy(m_system_include_directories.begin(),
m_system_include_directories.end(),
std::back_inserter(combined_includes));
return CLIOptions{m_scan_directories, combined_includes};
}
// Fix any -include or -isystem options to --include and --isystem
static std::vector<std::string> fixup_args(const CLI::App &app, int argc,
char **argv) {
std::vector<std::string> args{argv, std::next(argv, argc)};
const auto options = app.get_options();
for (auto &arg : args) {
// We have at least -- or -x
if (arg.size() < 2) {
continue;
}
// We're starting with a '-'
if (arg[0] != '-') {
continue;
}
indirect_includes_file << file_names[file_index] << ":\n";
for (const auto &include : file_to_indirect_includes[file_index]) {
indirect_includes_file << " - " << file_names[include] << '\n';
// We've got -X and not --X
if (arg[1] == '-') {
continue;
}
const std::string_view arg_view = arg;
for (const auto &option : options) {
const auto lnames = option->get_lnames();
for (const auto &lname : lnames) {
// if we have -lname, turn it into --lname
if (arg_view.substr(1, lname.size()) == lname) {
arg.insert(0, 1, '-');
}
}
}
}
return args;
}
static std::vector<const char *>
arg_ptrs(const std::vector<std::string> &argv) {
std::vector<const char *> result;
result.reserve(argv.size());
for (const auto &arg : argv) {
result.push_back(arg.c_str());
}
indirect_includes_file.close();
return result;
}
};
// NOLINTNEXTLINE(fuchsia-multiple-inheritance)
template <class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template <class... Ts> overloaded(Ts...)->overloaded<Ts...>;
int main(int argc, char *argv[]) {
try {
auto parser = CLIParser();
auto parse_result = parser.parse(argc, argv);
return std::visit(
overloaded{
[](int exit_value) { return exit_value; },
[](const CLIOptions &cli) {
return build_dependencies(cli.scan_directories,
cli.include_directories);
}},
parse_result);
} catch (const std::bad_variant_access &) {
std::cerr << "Unknown error parsing command line!\n";
return EXIT_FAILURE;
} catch (...) {
std::cerr << "Unexpected exception!\n";
return EXIT_FAILURE;
}
}
......@@ -5,7 +5,7 @@ add_library(
catch2_runner.main.cpp
)
target_link_libraries(catch2_runner PUBLIC Catch2::Catch2)
target_link_system_libraries(catch2_runner PUBLIC Catch2::Catch2)
target_compile_features(catch2_runner PUBLIC cxx_std_17)
add_library(medagep::catch2_runner ALIAS catch2_runner)
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment