Skip to content

Commit a3a9951

Browse files
committed
fix(pm): tighten dependency namespace compatibility
1 parent 9e8ed16 commit a3a9951

10 files changed

Lines changed: 363 additions & 120 deletions

File tree

src/cli.cppm

Lines changed: 10 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1368,17 +1368,17 @@ prepare_build(bool print_fingerprint,
13681368
}
13691369

13701370
// Set up project-level .mcpp/ directory for custom indices.
1371-
// This creates .mcpp/.xlings.json with non-builtin, non-local index
1371+
// This creates .mcpp/.xlings.json with custom non-builtin index
13721372
// entries so xlings can clone them into the project-scoped data dir.
13731373
if (!m->indices.empty()) {
13741374
auto cfg2 = get_cfg();
13751375
if (cfg2) {
13761376
mcpp::config::ensure_project_index_dir(**cfg2, *root, m->indices);
13771377

1378-
// On first build, .mcpp/data/ may be empty because
1378+
// On first build, the project xlings data root may be empty because
13791379
// ensure_project_index_dir only writes .xlings.json but doesn't
13801380
// trigger clone/link creation. Check whether there are any custom
1381-
// non-builtin indices and whether .mcpp/data/ has index content.
1381+
// non-builtin indices and whether the project data roots have index content.
13821382
// If not, run xlings update before dependency resolution.
13831383
bool hasCustomIndices = false;
13841384
for (auto& [idxName, spec] : m->indices) {
@@ -1388,22 +1388,7 @@ prepare_build(bool print_fingerprint,
13881388
}
13891389
}
13901390
if (hasCustomIndices) {
1391-
auto dataDir = *root / ".mcpp" / "data";
1392-
bool needsClone = !std::filesystem::exists(dataDir);
1393-
if (!needsClone) {
1394-
// Check if data/ has any index directories (dirs with pkgs/ subdir)
1395-
std::error_code ec;
1396-
bool hasIndexRepo = false;
1397-
if (std::filesystem::is_directory(dataDir, ec)) {
1398-
for (auto& entry : std::filesystem::directory_iterator(dataDir, ec)) {
1399-
if (entry.is_directory() && std::filesystem::exists(entry.path() / "pkgs")) {
1400-
hasIndexRepo = true;
1401-
break;
1402-
}
1403-
}
1404-
}
1405-
needsClone = !hasIndexRepo;
1406-
}
1391+
bool needsClone = !mcpp::config::project_index_data_initialized(*root);
14071392
if (needsClone) {
14081393
mcpp::ui::status("Fetching", "custom index repos (first use)");
14091394
auto projEnv = mcpp::config::make_project_xlings_env(**cfg2, *root);
@@ -1513,17 +1498,18 @@ prepare_build(bool print_fingerprint,
15131498
// validate the lua exists, then fall through to the normal install
15141499
// flow below.
15151500
if (idxSpec && idxSpec->is_local()) {
1501+
auto indexPath = mcpp::config::resolve_project_index_path(*root, *idxSpec);
15161502
auto luaCheck = mcpp::fetcher::Fetcher::read_xpkg_lua_from_path(
1517-
idxSpec->path, shortName);
1503+
indexPath, shortName);
15181504
if (!luaCheck) return std::unexpected(std::format(
15191505
"dependency '{}': not found in local index at '{}'",
1520-
depName, idxSpec->path.string()));
1506+
depName, indexPath.string()));
15211507
// lua found — fall through to normal install path resolution.
15221508
}
15231509

15241510
const bool useProjectEnv = idxSpec && !idxSpec->is_builtin();
15251511

1526-
// For custom indices, try project-level .mcpp/data/ first.
1512+
// For custom indices, try project-level xlings data roots first.
15271513
std::optional<std::filesystem::path> installed;
15281514
if (useProjectEnv) {
15291515
installed = mcpp::fetcher::Fetcher::install_path_from_project_data(
@@ -1798,7 +1784,7 @@ prepare_build(bool print_fingerprint,
17981784
auto& spec = item.spec;
17991785

18001786
mcpp::pm::compat::normalize_nested_namespace(
1801-
spec.namespace_, spec.shortName);
1787+
spec.namespace_, spec.shortName, spec.legacyDottedKey);
18021788

18031789
// Pin SemVer constraint before dedup/fetch.
18041790
if (auto r = resolveSemver(spec, name); !r) {
@@ -2965,7 +2951,7 @@ int cmd_index_update(const mcpplibs::cmdline::ParsedArgs& parsed) {
29652951
} else {
29662952
mcpp::ui::status("Updated", std::format("project index `{}`", idxName));
29672953
}
2968-
break; // ensure_project_index_dir handles all non-local indices at once
2954+
break; // ensure_project_index_dir handles all custom indices at once
29692955
}
29702956
}
29712957
}

src/config.cppm

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,51 @@ mcpp::xlings::Env make_project_xlings_env(const GlobalConfig& cfg,
104104
return { cfg.xlingsBinary, cfg.xlingsHome(), projectDir / ".mcpp" };
105105
}
106106

107+
std::vector<std::filesystem::path>
108+
project_xlings_data_roots(const std::filesystem::path& projectDir)
109+
{
110+
auto dotMcpp = projectDir / ".mcpp";
111+
return {
112+
dotMcpp / "data",
113+
dotMcpp / ".xlings" / "data",
114+
};
115+
}
116+
117+
std::filesystem::path
118+
resolve_project_index_path(const std::filesystem::path& projectDir,
119+
const mcpp::pm::IndexSpec& spec)
120+
{
121+
auto source = spec.path;
122+
if (source.is_relative()) source = projectDir / source;
123+
return source.lexically_normal();
124+
}
125+
126+
bool project_index_data_initialized(const std::filesystem::path& projectDir)
127+
{
128+
std::error_code ec;
129+
for (auto const& data : project_xlings_data_roots(projectDir)) {
130+
auto reposDir = data / "xim-index-repos";
131+
if (std::filesystem::exists(reposDir / "xim-indexrepos.json", ec)) {
132+
return true;
133+
}
134+
if (std::filesystem::is_directory(reposDir, ec)) {
135+
for (auto const& entry : std::filesystem::directory_iterator(reposDir, ec)) {
136+
if (entry.is_directory(ec) && std::filesystem::exists(entry.path() / "pkgs", ec)) {
137+
return true;
138+
}
139+
}
140+
}
141+
if (std::filesystem::is_directory(data, ec)) {
142+
for (auto const& entry : std::filesystem::directory_iterator(data, ec)) {
143+
if (entry.is_directory(ec) && std::filesystem::exists(entry.path() / "pkgs", ec)) {
144+
return true;
145+
}
146+
}
147+
}
148+
}
149+
return false;
150+
}
151+
107152
// Check that the sandbox bootstrap completed successfully.
108153
// Returns empty string if ok, or a description of what's missing.
109154
std::string check_base_init(const GlobalConfig& cfg) {
@@ -160,7 +205,7 @@ void reset_registry(const GlobalConfig& cfg) {
160205
}
161206

162207
// Ensure the project-level .mcpp/ directory exists and contains a
163-
// .xlings.json seeded with the custom (non-builtin, non-local) index
208+
// .xlings.json seeded with custom non-builtin index entries
164209
// entries. Returns true if a .mcpp/ directory was created/updated.
165210
bool ensure_project_index_dir(
166211
const GlobalConfig& cfg,
@@ -620,9 +665,8 @@ bool ensure_project_index_dir(
620665
for (auto& [name, spec] : indices) {
621666
if (spec.is_builtin()) continue;
622667
if (spec.is_local()) {
623-
auto source = spec.path;
624-
if (source.is_relative()) source = projectDir / source;
625-
customRepos.emplace_back(name, source.lexically_normal().string());
668+
auto source = resolve_project_index_path(projectDir, spec);
669+
customRepos.emplace_back(name, source.string());
626670
continue;
627671
}
628672
customRepos.emplace_back(name, spec.url);

src/libs/toml.cppm

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ private:
6969

7070
class Document {
7171
public:
72-
explicit Document(Table root) : root_(std::move(root)) {}
72+
explicit Document(Table root, std::set<std::string, std::less<>> explicitTables = {})
73+
: root_(std::move(root)), explicitTables_(std::move(explicitTables)) {}
7374

7475
const Table& root() const { return root_; }
7576
Table& root() { return root_; }
@@ -83,9 +84,13 @@ public:
8384
std::optional<bool> get_bool(std::string_view path) const;
8485
std::optional<std::vector<std::string>> get_string_array(std::string_view path) const;
8586
const Table* get_table(std::string_view path) const;
87+
bool has_explicit_table(std::string_view path) const {
88+
return explicitTables_.contains(path);
89+
}
8690

8791
private:
8892
Table root_;
93+
std::set<std::string, std::less<>> explicitTables_;
8994
};
9095

9196
std::expected<Document, ParseError> parse(std::string_view src);
@@ -328,6 +333,13 @@ inline Table* dive(Table& root, const std::vector<std::string>& path, bool creat
328333
return cur;
329334
}
330335

336+
inline std::string join_path(const std::vector<std::string>& path) {
337+
return std::accumulate(path.begin(), path.end(), std::string{},
338+
[](std::string a, const std::string& b){
339+
return a.empty() ? b : a + "." + b;
340+
});
341+
}
342+
331343
} // namespace detail
332344

333345
const Value* Document::get(std::string_view dotted_path) const {
@@ -390,6 +402,7 @@ std::expected<Document, ParseError> parse(std::string_view src) {
390402
using namespace detail;
391403
Lexer L { src };
392404
Table root;
405+
std::set<std::string, std::less<>> explicitTables;
393406
Table* current_table = &root;
394407

395408
while (true) {
@@ -412,11 +425,9 @@ std::expected<Document, ParseError> parse(std::string_view src) {
412425
auto* t = dive(root, *path);
413426
if (!t) return std::unexpected(ParseError{
414427
std::format("table path '{}' conflicts with non-table value",
415-
std::accumulate(path->begin(), path->end(), std::string{},
416-
[](std::string a, const std::string& b){
417-
return a.empty() ? b : a + "." + b;
418-
})),
428+
join_path(*path)),
419429
L.position()});
430+
explicitTables.insert(join_path(*path));
420431
current_table = t;
421432
} else {
422433
// key = value
@@ -441,7 +452,7 @@ std::expected<Document, ParseError> parse(std::string_view src) {
441452
}
442453
}
443454

444-
return Document{std::move(root)};
455+
return Document{std::move(root), std::move(explicitTables)};
445456
}
446457

447458
std::expected<Document, ParseError> parse_file(const std::filesystem::path& p) {

0 commit comments

Comments
 (0)