msvcdeps: use ant_glob() to get correct case of include paths

When using msvcdeps, header dependencies are not detected reliably for
generated source files. The root cause is a bug in versions of MSVC
prior to VS2019 16.0 in which it emits lower-case path prefixes when
resolving include paths relative to the containing file. Absolute paths
and paths relative to include directories passed in the MSVC command
line are, in contrast, case-correct.

Such a file-relative include directive with an incorrect lower-case
prefix derails waf's node hash signature handling and fails silently.

This change uses ant_glob() with the ignorecase keyword argument to
find the file on the filesystem with the correct case. The prior
case-correction code has been superseded and was removed.

See the following Visual Studio bug report for details on the issue:
https://developercommunity.visualstudio.com/content/problem/233871/showincludes-lowercases-some-path-segments.html
This commit is contained in:
Michael Vincent 2019-04-29 17:09:54 -05:00
parent 7c362340af
commit 9caad8c3ba
1 changed files with 9 additions and 23 deletions

View File

@ -50,17 +50,19 @@ def apply_msvcdeps_flags(taskgen):
if taskgen.env.get_flat(flag).find(PREPROCESSOR_FLAG) < 0: if taskgen.env.get_flat(flag).find(PREPROCESSOR_FLAG) < 0:
taskgen.env.append_value(flag, PREPROCESSOR_FLAG) taskgen.env.append_value(flag, PREPROCESSOR_FLAG)
# Figure out what casing conventions the user's shell used when
# launching Waf
(drive, _) = os.path.splitdrive(taskgen.bld.srcnode.abspath())
taskgen.msvcdeps_drive_lowercase = drive == drive.lower()
def path_to_node(base_node, path, cached_nodes): def path_to_node(base_node, path, cached_nodes):
''' '''
Take the base node and the path and return a node Take the base node and the path and return a node
Results are cached because searching the node tree is expensive Results are cached because searching the node tree is expensive
The following code is executed by threads, it is not safe, so a lock is needed... The following code is executed by threads, it is not safe, so a lock is needed...
''' '''
# normalize the path because ant_glob() does not understand
# parent path components (..)
path = os.path.normpath(path)
# normalize the path case to increase likelihood of a cache hit
path = os.path.normcase(path)
node_lookup_key = (base_node, path) node_lookup_key = (base_node, path)
try: try:
@ -71,7 +73,8 @@ def path_to_node(base_node, path, cached_nodes):
try: try:
node = cached_nodes[node_lookup_key] node = cached_nodes[node_lookup_key]
except KeyError: except KeyError:
node = cached_nodes[node_lookup_key] = base_node.find_resource(path) node_list = base_node.ant_glob([path], ignorecase=True, remove=False, quiet=True)
node = cached_nodes[node_lookup_key] = node_list[0] if node_list else None
return node return node
@ -87,11 +90,6 @@ def post_run(self):
unresolved_names = [] unresolved_names = []
resolved_nodes = [] resolved_nodes = []
lowercase = self.generator.msvcdeps_drive_lowercase
correct_case_path = bld.path.abspath()
correct_case_path_len = len(correct_case_path)
correct_case_path_norm = os.path.normcase(correct_case_path)
# Dynamically bind to the cache # Dynamically bind to the cache
try: try:
cached_nodes = bld.cached_nodes cached_nodes = bld.cached_nodes
@ -101,18 +99,6 @@ def post_run(self):
for path in self.msvcdeps_paths: for path in self.msvcdeps_paths:
node = None node = None
if os.path.isabs(path): if os.path.isabs(path):
# Force drive letter to match conventions of main source tree
drive, tail = os.path.splitdrive(path)
if os.path.normcase(path[:correct_case_path_len]) == correct_case_path_norm:
# Path is in the sandbox, force it to be correct. MSVC sometimes returns a lowercase path.
path = correct_case_path + path[correct_case_path_len:]
else:
# Check the drive letter
if lowercase and (drive != drive.lower()):
path = drive.lower() + tail
elif (not lowercase) and (drive != drive.upper()):
path = drive.upper() + tail
node = path_to_node(bld.root, path, cached_nodes) node = path_to_node(bld.root, path, cached_nodes)
else: else:
# when calling find_resource, make sure the path does not begin with '..' # when calling find_resource, make sure the path does not begin with '..'