Compare commits
2 Commits
599e186971
...
1890ba8bc3
| Author | SHA1 | Date | |
|---|---|---|---|
| 1890ba8bc3 | |||
| ce580c180e |
File diff suppressed because one or more lines are too long
@ -5,6 +5,9 @@ import sys
|
||||
project_root = os.path.dirname(os.path.abspath(__file__))
|
||||
sys.path.insert(0, project_root)
|
||||
|
||||
# 设置工作目录为项目根目录
|
||||
os.chdir(project_root)
|
||||
|
||||
# 添加 RenderPipeline 到路径(注意路径名称应与实际目录一致)
|
||||
render_pipeline_path = os.path.join(project_root, "RenderPipeline")
|
||||
sys.path.insert(0, render_pipeline_path)
|
||||
|
||||
95
tools/open_source_rate.py
Normal file
95
tools/open_source_rate.py
Normal file
@ -0,0 +1,95 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
THIRD_PARTY_DIR_NAMES = {
|
||||
'node_modules', 'vendor', 'third_party', 'third-party', 'extern', 'external', 'deps',
|
||||
'Cesium', 'Cesium-1.132', 'dist', 'build'
|
||||
}
|
||||
|
||||
CODE_EXTENSIONS = {
|
||||
'.py', '.js', '.ts', '.tsx', '.jsx', '.css', '.scss', '.html', '.c', '.h', '.cpp', '.hpp',
|
||||
'.cc', '.hh', '.m', '.mm', '.java', '.go', '.rs', '.cs', '.vue', '.svelte'
|
||||
}
|
||||
|
||||
SKIP_DIR_NAMES = {
|
||||
'.git', '.hg', '.svn', '__pycache__', '.mypy_cache', '.pytest_cache', '.idea', '.vscode',
|
||||
'.venv', 'venv', 'env', '.tox', '.cache', 'Resources', 'icons', 'tex', 'terminal'
|
||||
}
|
||||
|
||||
MAX_FILE_SIZE_BYTES = 2 * 1024 * 1024 # 2 MiB
|
||||
|
||||
|
||||
def is_third_party(path: Path) -> bool:
|
||||
for part in path.parts:
|
||||
# normalize case on Windows
|
||||
name = part.lower()
|
||||
if name in {n.lower() for n in THIRD_PARTY_DIR_NAMES}:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def should_skip_dir(dirname: str) -> bool:
|
||||
return dirname.lower() in {n.lower() for n in SKIP_DIR_NAMES}
|
||||
|
||||
|
||||
def is_code_file(path: Path) -> bool:
|
||||
return path.suffix.lower() in CODE_EXTENSIONS
|
||||
|
||||
|
||||
def count_lines(path: Path) -> int:
|
||||
try:
|
||||
if path.stat().st_size > MAX_FILE_SIZE_BYTES:
|
||||
return 0
|
||||
# Try text read with universal newlines, fallback to binary
|
||||
try:
|
||||
with path.open('r', encoding='utf-8', errors='ignore') as f:
|
||||
return sum(1 for _ in f)
|
||||
except Exception:
|
||||
with path.open('rb') as f:
|
||||
return f.read().count(b'\n')
|
||||
except Exception:
|
||||
return 0
|
||||
|
||||
|
||||
def main(root: Path) -> None:
|
||||
third_party_loc = 0
|
||||
first_party_loc = 0
|
||||
third_party_files = 0
|
||||
first_party_files = 0
|
||||
|
||||
for dirpath, dirnames, filenames in os.walk(root):
|
||||
# prune directories
|
||||
dirnames[:] = [d for d in dirnames if not should_skip_dir(d)]
|
||||
|
||||
current = Path(dirpath)
|
||||
current_is_third = is_third_party(current)
|
||||
|
||||
for fn in filenames:
|
||||
p = current / fn
|
||||
if not is_code_file(p):
|
||||
continue
|
||||
loc = count_lines(p)
|
||||
if current_is_third:
|
||||
third_party_loc += loc
|
||||
third_party_files += 1
|
||||
else:
|
||||
first_party_loc += loc
|
||||
first_party_files += 1
|
||||
|
||||
total_loc = first_party_loc + third_party_loc
|
||||
open_source_rate = (third_party_loc / total_loc) * 100 if total_loc else 0.0
|
||||
|
||||
print('Open-Source Rate (by LOC): {:.2f}%'.format(open_source_rate))
|
||||
print('\nBreakdown:')
|
||||
print(' First-party LOC : {} ({} files)'.format(first_party_loc, first_party_files))
|
||||
print(' Third-party LOC : {} ({} files)'.format(third_party_loc, third_party_files))
|
||||
print(' Total LOC : {}'.format(total_loc))
|
||||
print('\nNotes:')
|
||||
print(' - Third-party directories are heuristically detected by common names: {}'.format(', '.join(sorted(THIRD_PARTY_DIR_NAMES))))
|
||||
print(' - Static asset directories are skipped: {}'.format(', '.join(sorted(SKIP_DIR_NAMES))))
|
||||
print(' - Counted code extensions: {}'.format(', '.join(sorted(CODE_EXTENSIONS))))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(Path('.').resolve())
|
||||
|
||||
Loading…
Reference in New Issue
Block a user