Skip to content

Smart grouping in updated file list #22

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
build/
.python-version
*.pyc
_pmm/
_pmm/
.vscode/
.vs/
7 changes: 5 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"C_Cpp.default.configurationProvider": "vector-of-bool.cmake-tools",
"files.associations": {
"*.cmake.in": "cmake",
"*.lix": "elixir",
"array": "cpp",
"string_view": "cpp",
Expand Down Expand Up @@ -53,6 +54,8 @@
"condition_variable": "cpp",
"new": "cpp",
"thread": "cpp",
"algorithm": "cpp"
"algorithm": "cpp",
"xutility": "cpp",
"xlocale": "cpp"
}
}
}
6 changes: 4 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ include(pmm.cmake)
pmm(CONAN SETTINGS ${conan_std})
conan_set_find_paths()

# Find-package the Boost that Conan gave us
find_package(Boost 1.68.0 REQUIRED)
# Find-package the dependencies that Conan gave us
find_package(Boost 1.70.0 REQUIRED)
find_package(range-v3 0.5.0 REQUIRED)

# Other deps not controlled by Conan
find_package(Filesystem REQUIRED)
Expand Down Expand Up @@ -75,6 +76,7 @@ pf_auto(
PRIVATE_LINK
kainjow::mustache
pf::templates
range-v3
EXE_LINK
taywee::args
)
Expand Down
7 changes: 4 additions & 3 deletions conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ class PitchforkConanFile(CMakeConanFile):
name = 'pf'
version = '0.1.0'
requires = (
'catch2/2.3.0@bincrafters/stable',
'spdlog/1.1.0@bincrafters/stable',
'boost/1.68.0@conan/stable',
'Catch2/2.8.0@catchorg/stable',
'spdlog/1.3.1@bincrafters/stable',
'boost/1.70.0@conan/stable',
'range-v3/0.5.0@ericniebler/stable'
)
build_args = ['-DBUILD_SPEC=NO']

Expand Down
4 changes: 3 additions & 1 deletion external/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
include(FetchContent)

add_subdirectory(args)
add_subdirectory(mustache)
add_subdirectory(mustache)
21 changes: 19 additions & 2 deletions src/pf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,18 @@ class cmd_update {
{'b', "build-system"},
_bs_map};

std::unordered_map<std::string, pf::update_grouping> _grouping_map{
{"none", pf::update_grouping::none},
{"smart", pf::update_grouping::smart},
};
args::MapFlag<std::string, pf::update_grouping> _grouping{
_cmd,
"none|smart",
"The generated list of files will follow the specified grouping. ",
{"grouping"},
_grouping_map,
};

public:
explicit cmd_update(cli_common& gl)
: _cli{gl} {}
Expand All @@ -329,6 +341,11 @@ class cmd_update {
bs = get_map_value("Build system whose files to update", _bs_map, "cmake");
}

auto grouping = _grouping.Get();
if (grouping == pf::update_grouping::unspecified) {
grouping = get_map_value("How to group the generated files", _grouping_map, "smart");
}

// Only CMake supported at this time
if (bs != pf::build_system::cmake) {
_cli.console->error(
Expand All @@ -342,12 +359,12 @@ class cmd_update {

auto const src_dir = base_dir / "src";
std::vector<fs::path> sources = pf::glob_sources(src_dir);
pf::update_source_files(src_dir / "CMakeLists.txt", sources);
pf::update_source_files(src_dir / "CMakeLists.txt", sources, grouping);

auto const tests_dir = base_dir / "tests";
if (fs::exists(tests_dir)) {
std::vector<fs::path> test_sources = pf::glob_sources(tests_dir);
pf::update_source_files(tests_dir / "CMakeLists.txt", test_sources);
pf::update_source_files(tests_dir / "CMakeLists.txt", test_sources, grouping);
}
} catch (const std::system_error& e) {
_cli.console->error("Failed to update project in {}: {}",
Expand Down
33 changes: 14 additions & 19 deletions src/pf/existing/detect_base_dir.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#include <string>
#include <utility>

#include <boost/range/iterator_range.hpp>
#include <range/v3/algorithm/find_if.hpp>

namespace fs = pf::fs;

Expand All @@ -30,25 +30,20 @@ std::optional<fs::path> parse_cmakecache_homedir(fs::path const& cmakecache) {
} // namespace

std::optional<fs::path> pf::detect_base_dir(fs::path from_dir) {
auto cur_dir = std::find_if(pf::ascending_iterator{from_dir},
pf::ascending_iterator{},
[](auto const& dir) {
return fs::exists(dir / "CMakeLists.txt")
|| fs::exists(dir / "CMakeCache.txt");
});

if (cur_dir == pf::ascending_iterator{}) {
return std::nullopt;
}
auto ascending_from = pf::ascending_paths(from_dir);
auto cur_dir = ranges::find_if(ascending_from, [](auto const& dir) {
return fs::exists(dir / "CMakeLists.txt") || fs::exists(dir / "CMakeCache.txt");
});

// Iterator always valid, but may be root dir `/`.
if (fs::exists(*cur_dir / "CMakeLists.txt")) {
return *std::find_if(pf::ascending_iterator{*cur_dir},
pf::ascending_iterator{},
[](auto const& path) {
return !fs::exists(path.parent_path() / "CMakeLists.txt");
});
auto ascending_cur = pf::ascending_paths(*cur_dir);
return *ranges::find_if(ascending_cur, [](auto const& path) {
return !fs::exists(path.parent_path() / "CMakeLists.txt");
});
} else if (fs::exists(*cur_dir / "CMakeCache.txt")) {
return ::parse_cmakecache_homedir(*cur_dir / "CMakeCache.txt");
} else {
return std::nullopt;
}

// There's a CMakeCache
return ::parse_cmakecache_homedir(*cur_dir / "CMakeCache.txt");
}
72 changes: 50 additions & 22 deletions src/pf/existing/update_source_files.cpp
Original file line number Diff line number Diff line change
@@ -1,37 +1,62 @@
#include "./update_source_files.hpp"

#include <algorithm>
#include <cassert>
#include <cctype>
#include <fstream>
#include <iterator>
#include <sstream>
#include <tuple>
#include <utility>

#include <range/v3/view/group_by.hpp>
#include <range/v3/view/join.hpp>
#include <range/v3/view/transform.hpp>
#include <spdlog/fmt/ostr.h>
#include <spdlog/spdlog.h>

namespace fs = pf::fs;
namespace view = ranges::view;
namespace fs = pf::fs;

using namespace std::literals;

namespace {
auto transform_relative(fs::path const& base_dir) {
return view::transform([&base_dir](auto const& it) {
auto result = fs::relative(it, base_dir).string();
// TODO: evaluate if replacing `\` in filenames might cause a problem
std::replace(result.begin(), result.end(), '\\', '/');
return result;
});
}

constexpr bool smart_grouping = true;

std::vector<std::string> relative_source_strings(std::vector<fs::path> const& source_files,
fs::path const& base_dir) {
std::vector<std::string> source_strings;
source_strings.reserve(source_files.size());

std::transform(source_files.begin(),
source_files.end(),
std::back_inserter(source_strings),
[&base_dir](auto const& path) {
auto result = fs::relative(path, base_dir).string();
// TODO: evaluate if replacing `\` in filenames might cause a problem
std::replace(result.begin(), result.end(), '\\', '/');

return result;
});

return source_strings;
fs::path const& base_dir,
pf::update_grouping grouping) {
assert(grouping == pf::update_grouping::smart || grouping == pf::update_grouping::none);

auto result = source_files | ::transform_relative(base_dir);

if (grouping == pf::update_grouping::smart) {
return result | view::group_by([](std::string const& lhs, std::string const& rhs) {
// Note: computes each length at least twice rather than just once.
// Much simpler than combining with view::zip_with, though.
auto const lhs_dir_len = lhs.rfind('/');
auto const rhs_dir_len = rhs.rfind('/');

if (lhs_dir_len == std::string::npos) {
return lhs_dir_len == rhs_dir_len;
}

return std::string_view{lhs.c_str(), lhs_dir_len}
== std::string_view{rhs.c_str(), rhs_dir_len};
})
| view::join(""s);
}

return result;
}

constexpr std::string_view SourcesComment = "# sources\n";
Expand Down Expand Up @@ -116,9 +141,11 @@ auto insert_sources(std::string& cmakelists,
first_iteration = false;
}

insertion_point
= std::next(cmakelists.insert(insertion_point, indent.begin(), indent.end()),
indent.size());
if (!source.empty()) {
insertion_point
= std::next(cmakelists.insert(insertion_point, indent.begin(), indent.end()),
indent.size());
}
insertion_point
= std::next(cmakelists.insert(insertion_point, source.begin(), source.end()),
source.size());
Expand Down Expand Up @@ -148,7 +175,8 @@ write_sources(std::string& cmakelists,
} // namespace

void pf::update_source_files(fs::path const& cmakelists_file,
std::vector<fs::path> const& source_files) {
std::vector<fs::path> const& source_files,
pf::update_grouping grouping) {
if (!fs::exists(cmakelists_file)) {
throw std::system_error{
std::make_error_code(std::errc::no_such_file_or_directory),
Expand All @@ -159,7 +187,7 @@ void pf::update_source_files(fs::path const& cmakelists_file,
std::string cmakelists = pf::slurp_file(cmakelists_file);

std::vector<std::string> const source_strings
= ::relative_source_strings(source_files, cmakelists_file.parent_path());
= ::relative_source_strings(source_files, cmakelists_file.parent_path(), grouping);

std::string const cmakelists_cpy = cmakelists;

Expand Down
10 changes: 9 additions & 1 deletion src/pf/existing/update_source_files.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,15 @@

namespace pf {

void update_source_files(fs::path const& cmakelists_file, std::vector<fs::path> const& sources);
enum class update_grouping {
unspecified,
none,
smart,
};

void update_source_files(fs::path const& cmakelists_file,
std::vector<fs::path> const& sources,
update_grouping grouping = update_grouping::smart);

} // namespace pf

Expand Down
2 changes: 1 addition & 1 deletion src/pf/fs.hpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#ifndef PF_FS_HPP_INCLUDED
#define PF_FS_HPP_INCLUDED

#include <pf/fs/ascending_iterator.hpp>
#include <pf/fs/ascending_paths.hpp>
#include <pf/fs/core.hpp>
#include <pf/fs/glob.hpp>

Expand Down
1 change: 0 additions & 1 deletion src/pf/fs/ascending_iterator.cpp

This file was deleted.

53 changes: 0 additions & 53 deletions src/pf/fs/ascending_iterator.hpp

This file was deleted.

5 changes: 5 additions & 0 deletions src/pf/fs/ascending_paths.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#include "./ascending_paths.hpp"

#include <range/v3/range_concepts.hpp>

CONCEPT_ASSERT(ranges::InputRange<pf::ascending_paths>());
Loading