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

megadep.main.cpp: Split algorithm up into steps which can be specialized in a policy

parent b126d137
......@@ -29,163 +29,250 @@ bool contains_suffix(std::string_view file_name) {
return false;
}
template <typename ScanDirectories, typename IncludeDirectories>
int build_dependencies(const ScanDirectories &scan_directories,
const IncludeDirectories &include_directories) {
template <typename T> class bidirectional_index_map {
std::vector<T> m_values;
std::cout << "Scanning:\n";
for (const auto &directory : scan_directories) {
std::cout << " - " << directory << '\n';
public:
bidirectional_index_map(std::vector<T> value_list)
: m_values{std::move(value_list)} {
// Sort the files so we can refer to them by index, and look them up
// quickly by index.
// Also, erase any duplicates using a unique / erase.
std::sort(m_values.begin(), m_values.end());
m_values.erase(std::unique(m_values.begin(), m_values.end()),
m_values.end());
}
std::cout << '\n';
std::cout << "Including:\n";
for (const auto &directory : include_directories) {
std::cout << " - " << directory << '\n';
const std::string &operator[](size_t index) const {
assert(index < m_values.size());
return m_values[index];
}
std::cout << '\n';
std::vector<std::string> file_names;
for (const auto &directory : scan_directories) {
const std::optional<uint64_t> operator[](const T &value) const {
auto found_ptr = std::lower_bound(m_values.begin(), m_values.end(), value);
if (*found_ptr == value) {
return std::distance(m_values.begin(), found_ptr);
} else {
return std::nullopt;
}
}
auto directory_file_names =
megadep::list_files(std::string{directory.data(), directory.size()});
size_t size() const { return m_values.size(); }
};
for (const auto &file_name : directory_file_names) {
if (contains_suffix(file_name)) {
file_names.emplace_back(file_name);
class serial_megadep_policy {
public:
template <typename ScanDirectories>
static bidirectional_index_map<std::string>
build_file_map(const ScanDirectories &scan_directories) {
std::vector<std::string> file_names_list;
for (const auto &directory : scan_directories) {
auto directory_file_names =
megadep::list_files(std::string{directory.data(), directory.size()});
for (const auto &file_name : directory_file_names) {
if (contains_suffix(file_name)) {
file_names_list.emplace_back(file_name);
}
}
}
return bidirectional_index_map<std::string>(std::move(file_names_list));
}
// Sort the files so we can refer to them by index, and look them up
// quickly by index.
// Also, erase any duplicates using a unique / erase.
std::sort(file_names.begin(), file_names.end());
file_names.erase(std::unique(file_names.begin(), file_names.end()),
file_names.end());
template <typename FileNamesMap, typename IncludeDirectories>
auto
build_direct_includes_map(const FileNamesMap &file_names_map,
const IncludeDirectories &include_directories) {
return build_direct_includes_map(0, file_names_map.size(), file_names_map,
include_directories);
}
//
// Forward direct include map
//
template <typename FileNamesMap, typename IncludeDirectories>
std::vector<std::vector<uint64_t>>
build_direct_includes_map(size_t file_index_begin, size_t file_index_end,
const FileNamesMap &file_names_map,
const IncludeDirectories &include_directories) {
// Map from a file index to the file indices it #includes.
std::vector<std::vector<uint64_t>> file_to_includes(file_names.size());
for (size_t file_index = 0; file_index < file_names.size(); file_index++) {
const auto &file_name = file_names[file_index];
// Map from a file index to the file indices it #includes.
std::vector<std::vector<uint64_t>> file_to_includes(file_names_map.size());
for (size_t file_index = file_index_begin; file_index < file_index_end;
file_index++) {
const auto &file_name = file_names_map[file_index];
const auto include_strings = megadep::read_includes(file_name);
const auto include_strings = megadep::read_includes(file_name);
for (const auto &include : include_strings) {
auto resolved_include =
megadep::resolve_include(include, file_name, include_directories);
for (const auto &include : include_strings) {
auto resolved_include =
megadep::resolve_include(include, file_name, include_directories);
// If the resolved include is in the form <filename>, it's not in
// the includes, so it's an external depenency.
if (resolved_include.front() == '<' && resolved_include.back() == '>') {
continue;
}
// If the resolved include is in the form <filename>, it's not in
// the includes, so it's an external depenency.
if (resolved_include.front() == '<' && resolved_include.back() == '>') {
continue;
}
auto file_name_ptr = std::lower_bound(file_names.begin(),
file_names.end(), resolved_include);
auto included_file_index = file_names_map[resolved_include];
if (included_file_index) {
// If we found the exact value in file_names, add it to the includes
if (*file_name_ptr == resolved_include) {
// If we found the exact value in file_names, add it to the includes
file_to_includes[file_index].emplace_back(*included_file_index);
auto included_file_index =
std::distance(file_names.begin(), file_name_ptr);
} else {
// If we haven't found the exact value in file_names, it's in our
// includes, but it's not in the tracked list.
std::cerr << "Error: unresolved include!\n";
std::cerr << " Include: " << include << '\n';
std::cerr << " Attempt: " << resolved_include << '\n';
std::cerr << " Source : " << file_name << '\n';
file_to_includes[file_index].emplace_back(included_file_index);
std::exit(EXIT_FAILURE);
}
}
}
return file_to_includes;
}
} else {
// If we haven't found the exact value in file_names, it's in our
// includes, but it's not in the tracked list.
std::cerr << "Error: unresolved include!\n";
std::cerr << " Include: " << include << '\n';
std::cerr << " Attempt: " << resolved_include << '\n';
std::cerr << " Source : " << file_name << '\n';
template <typename FileToIncludes>
static std::vector<std::vector<uint64_t>>
invert_direct_includes_map(const FileToIncludes &file_to_includes) {
std::exit(EXIT_FAILURE);
// Invert the map
std::vector<std::vector<uint64_t>> file_to_includers(
file_to_includes.size());
for (size_t includer = 0; includer < file_to_includes.size(); includer++) {
for (const auto &included : file_to_includes[includer]) {
file_to_includers[included].push_back(includer);
}
}
return file_to_includers;
}
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';
template <typename FileToIncluders>
std::vector<std::vector<uint64_t>>
build_indirect_includes_map(const FileToIncluders &file_to_includers) {
std::vector<std::vector<uint64_t>> file_to_indirect_includes(
file_to_includers.size());
for (size_t file_index = 0; file_index < file_to_includers.size();
file_index++) {
std::vector<bool> processed(file_to_includers.size(), false);
// For `includer`, add `file_index` as an indirect dependency of
// every file it includes. The recurse on each of those files.
const auto recursive_add_to_includers =
[&](auto recursive_add_to_includers, auto includer) -> void {
for (const auto &sub_includer : file_to_includers[includer]) {
if (processed[sub_includer]) {
continue;
}
processed[sub_includer] = true;
file_to_indirect_includes[sub_includer].push_back(file_index);
recursive_add_to_includers(recursive_add_to_includers, sub_includer);
}
};
recursive_add_to_includers(recursive_add_to_includers, file_index);
}
return file_to_indirect_includes;
}
direct_includes_file.close();
//
// Inverse direct include map
//
template <typename FileToIncludes, typename FileNamesMap>
static void write_includes(const FileToIncludes &file_to_includes,
const FileNamesMap &file_names,
std::string output_file_name) {
// Invert the map
std::vector<std::vector<uint64_t>> file_to_includers(file_to_includes.size());
for (size_t includer = 0; includer < file_to_includes.size(); includer++) {
for (const auto &included : file_to_includes[includer]) {
file_to_includers[included].push_back(includer);
std::ofstream direct_includes_file(output_file_name, 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();
}
//
// Forward recursive include map
//
std::vector<std::vector<uint64_t>> file_to_indirect_includes(
file_to_includers.size());
for (size_t file_index = 0; file_index < file_to_includers.size();
file_index++) {
std::vector<bool> processed(file_to_includers.size(), false);
// For `includer`, add `file_index` as an indirect dependency of
// every file it includes. The recurse on each of those files.
const auto recursive_add_to_includers = [&](auto recursive_add_to_includers,
auto includer) -> void {
for (const auto &sub_includer : file_to_includers[includer]) {
if (processed[sub_includer]) {
continue;
}
processed[sub_includer] = true;
template<typename String, typename... Strings>
static void print(String&& string, Strings&&... strings) {
std::cout << string;
print(std::forward<Strings>(strings)...);
}
file_to_indirect_includes[sub_includer].push_back(file_index);
static void print() {}
};
recursive_add_to_includers(recursive_add_to_includers, sub_includer);
}
};
template <typename ScanDirectories, typename IncludeDirectories,
typename MegadepPolicy>
int run_megadep(const ScanDirectories &scan_directories,
const IncludeDirectories &include_directories,
MegadepPolicy &&megadep_policy) {
recursive_add_to_includers(recursive_add_to_includers, file_index);
megadep_policy.print("Scanning:\n");
for (const auto &directory : scan_directories) {
megadep_policy.print(" - ", directory, '\n');
}
megadep_policy.print('\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';
}
megadep_policy.print("Including:\n");
for (const auto &directory : include_directories) {
megadep_policy.print(" - ", directory, '\n');
}
indirect_includes_file.close();
megadep_policy.print('\n');
auto file_names = megadep_policy.build_file_map(scan_directories);
//
// Forward direct include map
//
auto file_to_includes =
megadep_policy.build_direct_includes_map(file_names, include_directories);
megadep_policy.write_includes(file_to_includes, file_names,
"direct-includes.yml");
//
// Inverse direct include map
//
auto file_to_includers =
megadep_policy.invert_direct_includes_map(file_to_includes);
//
// Forward recursive include map
//
auto file_to_indirect_includes =
megadep_policy.build_indirect_includes_map(file_to_includers);
megadep_policy.write_includes(file_to_indirect_includes, file_names,
"indirect-includes.yml");
return EXIT_SUCCESS;
}
struct CLIOptions {
template <typename ScanDirectories, typename IncludeDirectories>
int run_megadep(const ScanDirectories &scan_directories,
const IncludeDirectories &include_directories) {
return run_megadep(scan_directories, include_directories,
serial_megadep_policy{});
}
struct cli_options {
std::vector<std::string> scan_directories;
std::vector<std::string> include_directories;
};
class CLIParser {
class cli_parser {
CLI::App app;
std::vector<std::string> m_scan_directories;
......@@ -193,7 +280,7 @@ class CLIParser {
std::vector<std::string> m_system_include_directories;
public:
CLIParser() : app{"megadep: distributed dependency scanner"} {
cli_parser() : 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");
......@@ -201,13 +288,10 @@ public:
"System include directory");
}
std::variant<CLIOptions, int> parse(int argc, char **argv) {
std::variant<cli_options, 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) {
......@@ -225,7 +309,7 @@ public:
m_system_include_directories.end(),
std::back_inserter(combined_includes));
return CLIOptions{m_scan_directories, combined_includes};
return cli_options{m_scan_directories, combined_includes};
}
// Fix any -include or -isystem options to --include and --isystem
......@@ -284,17 +368,16 @@ template <class... Ts> overloaded(Ts...)->overloaded<Ts...>;
int main(int argc, char *argv[]) {
try {
auto parser = CLIParser();
auto parser = cli_parser();
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);
return std::visit(overloaded{[](int exit_value) { return exit_value; },
[](const cli_options &options) {
return run_megadep(
options.scan_directories,
options.include_directories);
}},
parse_result);
} catch (const std::bad_variant_access &) {
std::cerr << "Unknown error parsing command line!\n";
return EXIT_FAILURE;
......
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