This commit implements described in the #104573. The goal is to fix the confusion of the submodule hashes change, which are not ideal for any of the supported git-module configuration (they are either always visible causing confusion, or silently staged and committed, also causing confusion). This commit replaces submodules with a checkout of addons and addons_contrib, covered by the .gitignore, and locale and developer tools are moved to the main repository. This also changes the paths: - /release/scripts are moved to the /scripts - /source/tools are moved to the /tools - /release/datafiles/locale is moved to /locale This is done to avoid conflicts when using bisect, and also allow buildbot to automatically "recover" wgen building older or newer branches/patches. Running `make update` will initialize the local checkout to the changed repository configuration. Another aspect of the change is that the make update will support Github style of remote organization (origin remote pointing to thy fork, upstream remote pointing to the upstream blender/blender.git). Pull Request #104755
422 lines
14 KiB
Python
Executable File
422 lines
14 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
# Note: this code should be cleaned up / refactored.
|
|
|
|
import sys
|
|
if sys.version_info.major < 3:
|
|
print("\nPython3.x needed, found %s.\nAborting!\n" %
|
|
sys.version.partition(" ")[0])
|
|
sys.exit(1)
|
|
|
|
import os
|
|
from os.path import (
|
|
dirname,
|
|
join,
|
|
normpath,
|
|
splitext,
|
|
)
|
|
|
|
from check_cmake_consistency_config import (
|
|
IGNORE_SOURCE,
|
|
IGNORE_SOURCE_MISSING,
|
|
IGNORE_CMAKE,
|
|
UTF8_CHECK,
|
|
SOURCE_DIR,
|
|
BUILD_DIR,
|
|
)
|
|
|
|
from typing import (
|
|
Callable,
|
|
Dict,
|
|
Generator,
|
|
Iterator,
|
|
List,
|
|
Optional,
|
|
Tuple,
|
|
)
|
|
|
|
|
|
global_h = set()
|
|
global_c = set()
|
|
global_refs: Dict[str, List[Tuple[str, int]]] = {}
|
|
|
|
# Flatten `IGNORE_SOURCE_MISSING` to avoid nested looping.
|
|
IGNORE_SOURCE_MISSING_FLAT = [
|
|
(k, ignore_path) for k, ig_list in IGNORE_SOURCE_MISSING
|
|
for ignore_path in ig_list
|
|
]
|
|
|
|
# Ignore cmake file, path pairs.
|
|
global_ignore_source_missing: Dict[str, List[str]] = {}
|
|
for k, v in IGNORE_SOURCE_MISSING_FLAT:
|
|
global_ignore_source_missing.setdefault(k, []).append(v)
|
|
del IGNORE_SOURCE_MISSING_FLAT
|
|
|
|
|
|
def replace_line(f: str, i: int, text: str, keep_indent: bool = True) -> None:
|
|
file_handle = open(f, 'r')
|
|
data = file_handle.readlines()
|
|
file_handle.close()
|
|
|
|
l = data[i]
|
|
ws = l[:len(l) - len(l.lstrip())]
|
|
|
|
data[i] = "%s%s\n" % (ws, text)
|
|
|
|
file_handle = open(f, 'w')
|
|
file_handle.writelines(data)
|
|
file_handle.close()
|
|
|
|
|
|
def source_list(
|
|
path: str,
|
|
filename_check: Optional[Callable[[str], bool]] = None,
|
|
) -> Generator[str, None, None]:
|
|
for dirpath, dirnames, filenames in os.walk(path):
|
|
# skip '.git'
|
|
dirnames[:] = [d for d in dirnames if not d.startswith(".")]
|
|
|
|
for filename in filenames:
|
|
if filename_check is None or filename_check(filename):
|
|
yield os.path.join(dirpath, filename)
|
|
|
|
|
|
# extension checking
|
|
def is_cmake(filename: str) -> bool:
|
|
ext = splitext(filename)[1]
|
|
return (ext == ".cmake") or (filename == "CMakeLists.txt")
|
|
|
|
|
|
def is_c_header(filename: str) -> bool:
|
|
ext = splitext(filename)[1]
|
|
return (ext in {".h", ".hpp", ".hxx", ".hh"})
|
|
|
|
|
|
def is_c(filename: str) -> bool:
|
|
ext = splitext(filename)[1]
|
|
return (ext in {".c", ".cpp", ".cxx", ".m", ".mm", ".rc", ".cc", ".inl", ".metal", ".msl"})
|
|
|
|
|
|
def is_c_any(filename: str) -> bool:
|
|
return is_c(filename) or is_c_header(filename)
|
|
|
|
|
|
def cmake_get_src(f: str) -> None:
|
|
|
|
sources_h = []
|
|
sources_c = []
|
|
|
|
filen = open(f, "r", encoding="utf8")
|
|
it: Optional[Iterator[str]] = iter(filen)
|
|
found = False
|
|
i = 0
|
|
# print(f)
|
|
|
|
def is_definition(l: str, f: str, i: int, name: str) -> Tuple[bool, int]:
|
|
"""
|
|
Return (is_definition, single_line_offset).
|
|
"""
|
|
if l.startswith("unset("):
|
|
return False, -1
|
|
|
|
single_line_offset = -1
|
|
name_test = 'set(%s' % name
|
|
single_line_offset = l.find(name_test)
|
|
if (single_line_offset != -1) or ('set(' in l and l.endswith(name)):
|
|
if single_line_offset != -1:
|
|
single_line_offset += len(name_test)
|
|
# if len(l.split()) > 1:
|
|
# raise Exception("strict formatting not kept 'set(%s*' %s:%d" % (name, f, i))
|
|
if l.endswith(")"):
|
|
pass
|
|
while single_line_offset < len(l) and l[single_line_offset] != " ":
|
|
single_line_offset += 1
|
|
else:
|
|
single_line_offset = -1
|
|
return True, single_line_offset
|
|
|
|
name_test = "list(APPEND %s" % name
|
|
single_line_offset = l.find(name_test)
|
|
if (single_line_offset != -1) or ('list(APPEND ' in l and l.endswith(name)):
|
|
if single_line_offset != -1:
|
|
single_line_offset += len(name_test)
|
|
if l.endswith(")"):
|
|
# raise Exception("strict formatting not kept 'list(APPEND %s...)' on 1 line %s:%d" % (name, f, i))
|
|
pass
|
|
while single_line_offset < len(l) and l[single_line_offset] != " ":
|
|
single_line_offset += 1
|
|
else:
|
|
single_line_offset = -1
|
|
return True, single_line_offset
|
|
return False, -1
|
|
|
|
while it is not None:
|
|
context_name = ""
|
|
while it is not None:
|
|
i += 1
|
|
try:
|
|
l = next(it)
|
|
except StopIteration:
|
|
it = None
|
|
break
|
|
l = l.strip()
|
|
if not l.startswith("#"):
|
|
for var in ("SRC", "INC"):
|
|
found, single_line_offset = is_definition(l, f, i, var)
|
|
if found:
|
|
context_name = var
|
|
break
|
|
if found:
|
|
break
|
|
|
|
if found:
|
|
tokens = []
|
|
if single_line_offset != -1:
|
|
end = False
|
|
for w in l[single_line_offset:].split():
|
|
if w.startswith("#"):
|
|
break
|
|
if w.endswith(")"):
|
|
w = w[:-1].rstrip()
|
|
end = True
|
|
tokens.append((w, i))
|
|
if end:
|
|
break
|
|
del end
|
|
if len(tokens) > 1:
|
|
print("Expect multi-variable to be split across multiple lines! '%s' %s:%d" % (l, f, i))
|
|
else:
|
|
while it is not None:
|
|
i += 1
|
|
try:
|
|
l = next(it)
|
|
except StopIteration:
|
|
it = None
|
|
break
|
|
l = l.strip()
|
|
if not l.startswith("#"):
|
|
# Remove in-line comments.
|
|
l = l.split(" # ")[0].rstrip()
|
|
if ")" in l:
|
|
if l.strip() != ")":
|
|
raise Exception("strict formatting not kept '*)' %s:%d" % (f, i))
|
|
break
|
|
tokens.append((l, i))
|
|
|
|
cmake_base = dirname(f)
|
|
cmake_base_bin = os.path.join(BUILD_DIR, os.path.relpath(cmake_base, SOURCE_DIR))
|
|
|
|
# Find known missing sources list (if we have one).
|
|
f_rel = os.path.relpath(f, SOURCE_DIR)
|
|
f_rel_key = f_rel
|
|
if os.sep != "/":
|
|
f_rel_key = f_rel_key.replace(os.sep, "/")
|
|
local_ignore_source_missing = global_ignore_source_missing.get(f_rel_key, [])
|
|
|
|
for l, line_number in tokens:
|
|
# Replace directories.
|
|
l = l.replace("${CMAKE_SOURCE_DIR}", SOURCE_DIR)
|
|
l = l.replace("${CMAKE_CURRENT_SOURCE_DIR}", cmake_base)
|
|
l = l.replace("${CMAKE_CURRENT_BINARY_DIR}", cmake_base_bin)
|
|
l = l.strip('"')
|
|
|
|
if not l:
|
|
pass
|
|
elif l in local_ignore_source_missing:
|
|
local_ignore_source_missing.remove(l)
|
|
elif l.startswith("$"):
|
|
if context_name == "SRC":
|
|
# assume if it ends with context_name we know about it
|
|
if not l.split("}")[0].endswith(context_name):
|
|
print("Can't use var '%s' %s:%d" % (l, f, line_number))
|
|
elif len(l.split()) > 1:
|
|
raise Exception("Multi-line define '%s' %s:%d" % (l, f, line_number))
|
|
else:
|
|
new_file = normpath(join(cmake_base, l))
|
|
|
|
if context_name == "SRC":
|
|
if is_c_header(new_file):
|
|
sources_h.append(new_file)
|
|
global_refs.setdefault(new_file, []).append((f, line_number))
|
|
elif is_c(new_file):
|
|
sources_c.append(new_file)
|
|
global_refs.setdefault(new_file, []).append((f, line_number))
|
|
elif l in {"PARENT_SCOPE", }:
|
|
# cmake var, ignore
|
|
pass
|
|
elif new_file.endswith(".list"):
|
|
pass
|
|
elif new_file.endswith(".def"):
|
|
pass
|
|
elif new_file.endswith(".cl"): # OPENCL.
|
|
pass
|
|
elif new_file.endswith(".cu"): # CUDA.
|
|
pass
|
|
elif new_file.endswith(".osl"): # open shading language.
|
|
pass
|
|
elif new_file.endswith(".glsl"):
|
|
pass
|
|
else:
|
|
raise Exception("unknown file type - not c or h %s -> %s" % (f, new_file))
|
|
|
|
elif context_name == "INC":
|
|
if new_file.startswith(BUILD_DIR):
|
|
# assume generated path
|
|
pass
|
|
elif os.path.isdir(new_file):
|
|
new_path_rel = os.path.relpath(new_file, cmake_base)
|
|
|
|
if new_path_rel != l:
|
|
print("overly relative path:\n %s:%d\n %s\n %s" % (f, line_number, l, new_path_rel))
|
|
|
|
# # Save time. just replace the line
|
|
# replace_line(f, line_number - 1, new_path_rel)
|
|
|
|
else:
|
|
raise Exception("non existent include %s:%d -> %s" % (f, line_number, new_file))
|
|
|
|
# print(new_file)
|
|
|
|
global_h.update(set(sources_h))
|
|
global_c.update(set(sources_c))
|
|
'''
|
|
if not sources_h and not sources_c:
|
|
raise Exception("No sources %s" % f)
|
|
|
|
sources_h_fs = list(source_list(cmake_base, is_c_header))
|
|
sources_c_fs = list(source_list(cmake_base, is_c))
|
|
'''
|
|
# find missing C files:
|
|
'''
|
|
for ff in sources_c_fs:
|
|
if ff not in sources_c:
|
|
print(" missing: " + ff)
|
|
'''
|
|
|
|
# reset
|
|
del sources_h[:]
|
|
del sources_c[:]
|
|
|
|
filen.close()
|
|
|
|
|
|
def is_ignore_source(f: str, ignore_used: List[bool]) -> bool:
|
|
for index, ignore_path in enumerate(IGNORE_SOURCE):
|
|
if ignore_path in f:
|
|
ignore_used[index] = True
|
|
return True
|
|
return False
|
|
|
|
|
|
def is_ignore_cmake(f: str, ignore_used: List[bool]) -> bool:
|
|
for index, ignore_path in enumerate(IGNORE_CMAKE):
|
|
if ignore_path in f:
|
|
ignore_used[index] = True
|
|
return True
|
|
return False
|
|
|
|
|
|
def main() -> None:
|
|
|
|
print("Scanning:", SOURCE_DIR)
|
|
|
|
ignore_used_source = [False] * len(IGNORE_SOURCE)
|
|
ignore_used_cmake = [False] * len(IGNORE_CMAKE)
|
|
|
|
for cmake in source_list(SOURCE_DIR, is_cmake):
|
|
if not is_ignore_cmake(cmake, ignore_used_cmake):
|
|
cmake_get_src(cmake)
|
|
|
|
# First do stupid check, do these files exist?
|
|
print("\nChecking for missing references:")
|
|
is_err = False
|
|
errs = []
|
|
for f in (global_h | global_c):
|
|
if f.startswith(BUILD_DIR):
|
|
continue
|
|
|
|
if not os.path.exists(f):
|
|
refs = global_refs[f]
|
|
if refs:
|
|
for cf, i in refs:
|
|
errs.append((cf, i))
|
|
else:
|
|
raise Exception("CMake references missing, internal error, aborting!")
|
|
is_err = True
|
|
|
|
errs.sort()
|
|
errs.reverse()
|
|
for cf, i in errs:
|
|
print("%s:%d" % (cf, i))
|
|
# Write a 'sed' script, useful if we get a lot of these
|
|
# print("sed '%dd' '%s' > '%s.tmp' ; mv '%s.tmp' '%s'" % (i, cf, cf, cf, cf))
|
|
|
|
if is_err:
|
|
raise Exception("CMake references missing files, aborting!")
|
|
del is_err
|
|
del errs
|
|
|
|
# now check on files not accounted for.
|
|
print("\nC/C++ Files CMake does not know about...")
|
|
for cf in sorted(source_list(SOURCE_DIR, is_c)):
|
|
if not is_ignore_source(cf, ignore_used_source):
|
|
if cf not in global_c:
|
|
print("missing_c: ", cf)
|
|
|
|
# Check if automake builds a corresponding .o file.
|
|
'''
|
|
if cf in global_c:
|
|
out1 = os.path.splitext(cf)[0] + ".o"
|
|
out2 = os.path.splitext(cf)[0] + ".Po"
|
|
out2_dir, out2_file = out2 = os.path.split(out2)
|
|
out2 = os.path.join(out2_dir, ".deps", out2_file)
|
|
if not os.path.exists(out1) and not os.path.exists(out2):
|
|
print("bad_c: ", cf)
|
|
'''
|
|
|
|
print("\nC/C++ Headers CMake does not know about...")
|
|
for hf in sorted(source_list(SOURCE_DIR, is_c_header)):
|
|
if not is_ignore_source(hf, ignore_used_source):
|
|
if hf not in global_h:
|
|
print("missing_h: ", hf)
|
|
|
|
if UTF8_CHECK:
|
|
# test encoding
|
|
import traceback
|
|
for files in (global_c, global_h):
|
|
for f in sorted(files):
|
|
if os.path.exists(f):
|
|
# ignore outside of our source tree
|
|
if "extern" not in f:
|
|
i = 1
|
|
try:
|
|
for _ in open(f, "r", encoding="utf8"):
|
|
i += 1
|
|
except UnicodeDecodeError:
|
|
print("Non utf8: %s:%d" % (f, i))
|
|
if i > 1:
|
|
traceback.print_exc()
|
|
|
|
# Check ignores aren't stale
|
|
print("\nCheck for unused 'IGNORE_SOURCE' paths...")
|
|
for index, ignore_path in enumerate(IGNORE_SOURCE):
|
|
if not ignore_used_source[index]:
|
|
print("unused ignore: %r" % ignore_path)
|
|
|
|
# Check ignores aren't stale
|
|
print("\nCheck for unused 'IGNORE_SOURCE_MISSING' paths...")
|
|
for k, v in sorted(global_ignore_source_missing.items()):
|
|
for ignore_path in v:
|
|
print("unused ignore: %r -> %r" % (ignore_path, k))
|
|
|
|
# Check ignores aren't stale
|
|
print("\nCheck for unused 'IGNORE_CMAKE' paths...")
|
|
for index, ignore_path in enumerate(IGNORE_CMAKE):
|
|
if not ignore_used_cmake[index]:
|
|
print("unused ignore: %r" % ignore_path)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|