From f5b09df9c88d49c95fd0c0ef7b3670b40634ca5c Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Fri, 27 May 2022 09:50:35 +0200 Subject: WIP: automatic externals --- ctor.cc | 1 + src/configure.cc | 86 ++++++++++++++++++++++++++++++++++++- src/libctor.h | 14 +++++- src/search.cc | 120 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/search.h | 37 ++++++++++++++++ test/ctor.cc | 20 ++++++++- test/search_test.cc | 106 ++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 380 insertions(+), 4 deletions(-) create mode 100644 src/search.cc create mode 100644 src/search.h create mode 100644 test/search_test.cc diff --git a/ctor.cc b/ctor.cc index d795ff9..bdae7b8 100644 --- a/ctor.cc +++ b/ctor.cc @@ -19,6 +19,7 @@ BuildConfigurations ctorConfigs(const Settings& settings) "src/externals_manual.cc", "src/libctor.cc", "src/rebuild.cc", + "src/search.cc", "src/task.cc", "src/task_ar.cc", "src/task_fn.cc", diff --git a/src/configure.cc b/src/configure.cc index bc7d3e8..a7e47eb 100644 --- a/src/configure.cc +++ b/src/configure.cc @@ -14,6 +14,7 @@ #include "libctor.h" #include "tasks.h" #include "rebuild.h" +#include "search.h" #include "externals.h" std::filesystem::path configurationFile("configuration.cc"); @@ -146,6 +147,58 @@ public: } }; +int resolv(const Settings& settings, const std::string& name, + const ExternalAutomatic& ext, Flags& flags) +{ + using namespace std::string_literals; + + flags = ext.flags; + + for(const auto& header : ext.headers) + { + std::vector paths; + auto it = external_includedir.find(name); + if(it != external_includedir.end()) + { + paths.push_back(it->second); + } + + auto res = findHeader(settings, header, paths); + if(res.empty()) + { + std::cout << "Header " << header << " required by " << name << + " not found.\n"; + return 1; + } + auto flag = "-I"s + res.string(); + flags.cxxflags.push_back(flag); + } + + for(const auto& lib : ext.libs) + { + std::vector paths; + auto it = external_libdir.find(name); + if(it != external_libdir.end()) + { + paths.push_back(it->second); + } + + auto res = findLibrary(settings, lib, paths); + if(res.empty()) + { + std::cout << "Library " << lib << " required by " << name << + " not found.\n"; + return 1; + } + auto flag = "-L"s + res[0].parent_path().string(); + flags.ldflags.push_back(flag); + flag = "-l"s + lib; + flags.ldflags.push_back(flag); + } + + return 0; +} + // helper constant for the visitor template inline constexpr bool always_false_v = false; @@ -288,7 +341,11 @@ int regenerateCache(const Settings& default_settings, std::visit([&](auto&& arg) { using T = std::decay_t; - if constexpr (std::is_same_v) + if constexpr (std::is_same_v) + { + add_path_args(ext.name); + } + else if constexpr (std::is_same_v) { add_path_args(ext.name); } @@ -298,6 +355,24 @@ int regenerateCache(const Settings& default_settings, } }, ext.external); + /* + if(std::holds_alternative(ext.external)) + { + opt.add(ext.name + "-includedir", required_argument, key++, + "Set path to " + ext.name + " header file.", + [&]() { + //X_includedir = optarg; + return 0; + }); + + opt.add(ext.name + "-libdir", required_argument, key++, + "Set path to " + ext.name + " libraries.", + [&]() { + //X_libdir = optarg; + return 0; + }); + } + */ } opt.add("help", no_argument, 'h', @@ -434,6 +509,15 @@ int regenerateCache(const Settings& default_settings, return ret; } } + else if(std::holds_alternative(ext.external)) + { + if(auto ret = resolv(settings, ext.name, + std::get(ext.external), + resolved_flags)) + { + return ret; + } + } else { std::cout << "Unknown external type\n"; diff --git a/src/libctor.h b/src/libctor.h index 14fdf1d..9775085 100644 --- a/src/libctor.h +++ b/src/libctor.h @@ -69,7 +69,8 @@ struct Settings std::string builddir{"build"}; std::size_t parallel_processes{1}; int verbose{0}; // -1: completely silent, 0: normal, 1: verbose, ... -}; + std::vector include_paths{"/usr/include", "/usr/local/include"}; + std::vector library_paths{"/lib", "/usr/lib", "/usr/local/lib"};}; struct BuildConfiguration; using GeneratorCb = std::function headers; + std::vector libs; + Flags flags; +}; struct ExternalConfiguration { std::string name; // Name for configuration - std::variant external; + std::variant external; }; using ExternalConfigurations = std::vector; diff --git a/src/search.cc b/src/search.cc new file mode 100644 index 0000000..e33fc04 --- /dev/null +++ b/src/search.cc @@ -0,0 +1,120 @@ +#include "search.h" + +#include + +#include "libctor.h" + +// https://stackoverflow.com/questions/17939930/finding-out-what-the-gcc-include-path-is + +// /usr/include/ +// /usr/local/include +std::filesystem::path findFile(const Settings& settings, + const std::string& needle, + const std::vector& haystack) +{ + if(settings.verbose > 0) + { + std::cout << "Looking for '" << needle << "'\n"; + } + for(const auto& path : haystack) + { + std::filesystem::path candidate = std::filesystem::path(path) / needle; + if(settings.verbose > 1) + { + std::cout << " in '" << candidate.string() << "' ... "; + } + if(std::filesystem::exists(candidate)) + { + if(settings.verbose > 1) + { + std::cout << "yes\n"; + } + return path; + } + else + { + if(settings.verbose > 1) + { + std::cout << "no\n"; + } + } + } + + if(settings.verbose > 0) + { + std::cout << "Not found.\n"; + } + + return ""; // not found +} + +std::filesystem::path findHeader(const Settings& settings, + const std::string& needle, + const std::vector& haystack) +{ + auto paths = haystack; + paths.insert(paths.end(), + settings.include_paths.begin(), + settings.include_paths.end()); + return findFile(settings, needle, paths); +} + +std::vector findBasename(const Settings& settings, + const std::string& needle, + const std::vector& haystack) +{ + std::vector matches; + if(settings.verbose > 0) + { + std::cout << "Looking for basename '" << needle << "'\n"; + } + + for(const auto& path : haystack) + { + if(settings.verbose > 1) + { + std::cout << "Looking in '" << path << "\n"; + } + + for(const auto& dir_entry : std::filesystem::directory_iterator{path}) + { + if(std::filesystem::is_regular_file(dir_entry)) + { + if(dir_entry.path().stem() == needle) + { + if(settings.verbose > 1) + { + std::cout << "Found " << path / dir_entry.path() << "\n"; + } + matches.push_back(path / dir_entry.path()); + } + } + } + } + + if(matches.empty() && settings.verbose > 0) + { + std::cout << "Not found.\n"; + } + + return matches; +} + +std::vector findLibrary(const Settings& settings, + const std::string& needle, + const std::vector& haystack) +{ + using namespace std::string_literals; + + std::string lib = needle; + if(!lib.starts_with("lib")) + { + lib = "lib"s + needle; + } + auto paths = haystack; + paths.insert(paths.end(), + settings.library_paths.begin(), + settings.library_paths.end()); + return findBasename(settings, lib, paths); +} + diff --git a/src/search.h b/src/search.h new file mode 100644 index 0000000..1841978 --- /dev/null +++ b/src/search.h @@ -0,0 +1,37 @@ +// -*- c++ -*- +// Distributed under the BSD 2-Clause License. +// See accompanying file LICENSE for details. +#pragma once + +#include +#include +#include + +struct Settings; + +//! Locate file (needle) in the provided search path list (haystack) +//! The provided settings is used to control verbosity during the search. +//! \returns a std::filesystem::path pointing to path in which file was found +//! - empty if not found. +std::filesystem::path findFile(const Settings& settings, + const std::string& needle, + const std::vector& haystack); + +//! Locate header (needle) in the provided search path list (haystack) +//! The provided settings is used to control verbosity during the search as well +//! as providing the search paths defaults for headers. +//! The argument provided haystack is prepended to the one supplied by settings. +//! \returns a std::filesystem::path pointing to path where the file was found +//! - empty if not found. +std::filesystem::path findHeader(const Settings& settings, + const std::string& needle, + const std::vector& haystack); + + +std::vector findBasename(const Settings& settings, + const std::string& needle, + const std::vector& haystack); + +std::vector findLibrary(const Settings& settings, + const std::string& needle, + const std::vector& haystack); diff --git a/test/ctor.cc b/test/ctor.cc index c24ded7..fd97c80 100644 --- a/test/ctor.cc +++ b/test/ctor.cc @@ -67,7 +67,9 @@ BuildConfigurations ctorTestConfigs(const Settings& settings) "../src/build.cc", "../src/configure.cc", "../src/execute.cc", + "../src/externals_manual.cc", "../src/rebuild.cc", + "../src/search.cc", "../src/tasks.cc", "../src/task.cc", "../src/task_ar.cc", @@ -76,7 +78,6 @@ BuildConfigurations ctorTestConfigs(const Settings& settings) "../src/task_ld.cc", "../src/task_so.cc", "../src/util.cc", - "../src/externals_manual.cc", }, .flags = { .cxxflags = { @@ -86,6 +87,23 @@ BuildConfigurations ctorTestConfigs(const Settings& settings) .ldflags = { "-pthread" }, }, }, + { + .type = TargetType::UnitTest, + .target = "search_test", + .sources = { + "../src/search.cc", + "search_test.cc", + "testmain.cc", + }, + .flags = { + .cxxflags = { + "-std=c++20", "-O3", "-s", "-Wall", "-Werror", + "-I../src", "-Iuunit", + "-DOUTPUT=\"search\"", + }, + .ldflags = { "-pthread" }, + }, + }, }; } } diff --git a/test/search_test.cc b/test/search_test.cc new file mode 100644 index 0000000..179fb0e --- /dev/null +++ b/test/search_test.cc @@ -0,0 +1,106 @@ +#include + +#include +#include + +#include +#include + +#include "paths.h" + +class SearchTest + : public uUnit +{ +public: + SearchTest() + { + uTEST(SearchTest::test_include); + uTEST(SearchTest::test_lib); + } + + void test_include() + { + using namespace std::string_literals; + + constexpr auto verb = -1; + + auto src = paths::top_srcdir / "src"; + auto tmp = std::filesystem::temp_directory_path(); + + { // system + Settings settings{.verbose = verb}; // instantiate with default paths set + auto result = findHeader(settings, "stdio.h", {}); + uASSERT_EQUAL("/usr/include"s, result.string()); + } + + { // libctor.h in src folder + Settings settings{.verbose = verb}; // instantiate with default paths set + auto result = findHeader(settings, "libctor.h", { src.string() }); + uASSERT_EQUAL(src, result); + } + + { // src/libctor.h in topsrc folder (ie. path with '/') + Settings settings{.verbose = verb}; // instantiate with default paths set + auto result = findHeader(settings, "src/libctor.h", + { paths::top_srcdir.string() }); + uASSERT_EQUAL(paths::top_srcdir, result); + } + + { // libctor.h in src folder over one in temp folder (system) + Settings settings{.verbose = verb}; // instantiate with default paths set + settings.include_paths = { tmp.string() }; + auto tmpfile = tmp / "libctor.h"; + { // create /tmp/libctor.h file + std::ofstream of(tmpfile.string()); + of << "/* nop */"; + } + auto result = findHeader(settings, "libctor.h", { src.string() }); + std::filesystem::remove(tmpfile); + uASSERT_EQUAL(src, result); + } + + { // libctor.h in temp folder over one in src folder (local prio) + Settings settings{.verbose = verb}; // instantiate with default paths set + settings.include_paths = {}; // remove system search paths + auto tmpfile = tmp / "libctor.h"; + { // create /tmp/libctor.h file + std::ofstream of(tmpfile.string()); + of << "/* nop */"; + } + auto result = findHeader(settings, "libctor.h", + { tmp.string(), src.string() }); + std::filesystem::remove(tmpfile); + uASSERT_EQUAL(tmp, result); + } + + { // not found + Settings settings{.verbose = verb}; + settings.include_paths = {}; // remove system search paths + auto result = findHeader(settings, "no_such_file.h", {}); + uASSERT(result.empty()); + } + } + + void test_lib() + { + using namespace std::string_literals; + + constexpr auto verb = 2; + + auto src = paths::top_srcdir / "src"; + auto tmp = std::filesystem::temp_directory_path(); + + { // system + Settings settings{.verbose = verb}; // instantiate with default paths set + auto result = findLibrary(settings, "ffi", {}); + for(auto f : result) + { + std::cout << f << "\n"; + } + uASSERT_EQUAL("/usr/lib/libffi.so"s, result[0].string()); + } + } +}; + +// Registers the fixture into the 'registry' +static SearchTest test; -- cgit v1.2.3