aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2021-02-11 14:19:47 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2021-02-11 14:19:47 +0200
commitde5aee27885f5d9bd67e67b1333d119583fcac2e (patch)
tree8a1992c2e558f0537efc7c166c6e5cf2c858f6b2
parentfcda5635e81376494def70e7c6880655efac8b8e (diff)
Improve module name to file name matching logic
-rw-r--r--libbuild2/cc/compile-rule.cxx47
-rw-r--r--tests/cc/modules/modules.testscript3
2 files changed, 41 insertions, 9 deletions
diff --git a/libbuild2/cc/compile-rule.cxx b/libbuild2/cc/compile-rule.cxx
index 0c17421..116f67b 100644
--- a/libbuild2/cc/compile-rule.cxx
+++ b/libbuild2/cc/compile-rule.cxx
@@ -5248,7 +5248,7 @@ namespace build2
//
// In the above examples one common theme about all the file names is
// that they contain, in one form or another, the "tail" of the module
- // name ('core'). So what we are going to do is require that, within a
+ // name (`core`). So what we are going to do is require that, within a
// pool (library, executable), the interface file names contain enough
// of the module name tail to unambiguously resolve all the module
// imports. On our side we are going to implement a "fuzzy" module name
@@ -5267,7 +5267,7 @@ namespace build2
// abstract-window.mxx: which one is likely to define this module?
// Clearly the first, but in the above-described scheme they will get
// the same score. More generally, consider these "obvious" (to the
- // human) situations:
+ // human, that is) situations:
//
// window.mxx vs abstract-window.mxx
// details/window.mxx vs abstract-window.mxx
@@ -5276,13 +5276,17 @@ namespace build2
// To handle such cases we are going to combine the above primary score
// with the following secondary scores (in that order):
//
- // a) Strength of separation between matched and unmatched parts:
+ // A) Strength of separation between matched and unmatched parts:
//
// '\0' > directory separator > other separator > unseparated
//
// Here '\0' signifies nothing to separate (unmatched part is empty).
//
- // b) Shortness of the unmatched part.
+ // B) Shortness of the unmatched part.
+ //
+ // Finally, for the fuzzy match we require a complete match of the last
+ // module (or partition) component. Failed that, we will match `format`
+ // to `print` because the last character (`t`) is the same.
//
// For std.* modules we only accept non-fuzzy matches (think std.core vs
// some core.mxx). And if such a module is unresolved, then we assume it
@@ -5299,8 +5303,12 @@ namespace build2
//
// PPPPABBBB
//
- // We use decimal instead of binary packing to make it easier to
- // separate fields in the trace messages, during debugging, etc.
+ // Where PPPP is the primary score, A is the A) score, and BBBB is
+ // the B) scope described above. Zero signifies no match.
+ //
+ // We use decimal instead of binary packing to make it easier for the
+ // human to separate fields in the trace messages, during debugging,
+ // etc.
//
return m.size () * 100000 + 99999; // Maximum match score.
};
@@ -5323,6 +5331,8 @@ namespace build2
(ucase (c1) == c1) != (ucase (c2) == c2));
};
+ auto mod_sep = [] (char c) {return c == '.' || c == ':';};
+
size_t fn (f.size ()), fi (fn);
size_t mn (m.size ()), mi (mn);
@@ -5332,6 +5342,10 @@ namespace build2
bool fsep (false);
bool msep (false);
+ // We require complete match of at least last module component.
+ //
+ bool match (false);
+
// Scan backwards for as long as we match. Keep track of the previous
// character for case change detection.
//
@@ -5357,11 +5371,12 @@ namespace build2
// FOObar
//
bool fs (char_sep (fc));
- bool ms (mc == '_' || mc == '.' || mc == ':');
+ bool ms (mod_sep (mc) || mc == '_');
if (fs && ms)
{
fsep = msep = true;
+ match = match || mod_sep (mc);
continue;
}
@@ -5379,6 +5394,7 @@ namespace build2
if (fa) {++fi; msep = true;}
if (ma) {++mi; fsep = true;}
+ match = match || mod_sep (mc);
continue;
}
}
@@ -5386,6 +5402,14 @@ namespace build2
break; // No match.
}
+ // Deal with edge cases: complete module match and complete file
+ // match.
+ //
+ match = match || mi == 0 || (fi == 0 && mod_sep (m[mi - 1]));
+
+ if (!match)
+ return 0;
+
// "Uncount" real separators.
//
if (fsep) fi++;
@@ -5753,8 +5777,13 @@ namespace build2
//
// But at this stage this doesn't seem worth the trouble.
//
- fail (relative (src)) << "unable to resolve module "
- << imports[i].name;
+ fail (relative (src))
+ << "unable to resolve module " << imports[i].name <<
+ info << "verify module interface is listed as a prerequisite, "
+ << "otherwise" <<
+ info << "consider adjusting module interface file names or" <<
+ info << "consider specifying module name with " << x
+ << ".module_name";
}
}
}
diff --git a/tests/cc/modules/modules.testscript b/tests/cc/modules/modules.testscript
index a43cc57..eb4c122 100644
--- a/tests/cc/modules/modules.testscript
+++ b/tests/cc/modules/modules.testscript
@@ -173,6 +173,9 @@ $* test clean <<EOI
ln -s ../driver.cxx ./;
$* test &*.d <'exe{test}: cxx{driver}' 2>>EOE != 0
driver.cxx: error: unable to resolve module foo.core
+ info: verify module interface is listed as a prerequisite, otherwise
+ info: consider adjusting module interface file names or
+ info: consider specifying module name with cxx.module_name
EOE
: misguessed