File: Dependencies.py

package info (click to toggle)
codequery 0.26.0%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 9,332 kB
  • sloc: cpp: 106,043; xml: 16,576; python: 4,187; perl: 244; makefile: 11
file content (153 lines) | stat: -rw-r--r-- 5,381 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#!/usr/bin/env python3
# Dependencies.py - discover, read, and write dependencies file for make.
# The format like the output from "g++ -MM" which produces a
# list of header (.h) files used by source files (.cxx).
# As a module, provides
#	FindPathToHeader(header, includePath) -> path
#	FindHeadersInFile(filePath) -> [headers]
#	FindHeadersInFileRecursive(filePath, includePath, renames) -> [paths]
#	FindDependencies(sourceGlobs, includePath, objExt, startDirectory, renames) -> [dependencies]
#	ExtractDependencies(input) -> [dependencies]
#	TextFromDependencies(dependencies)
#	WriteDependencies(output, dependencies)
#	UpdateDependencies(filepath, dependencies)
#	PathStem(p) -> stem
#	InsertSynonym(dependencies, current, additional) -> [dependencies]
# If run as a script reads from stdin and writes to stdout.
# Only tested with ASCII file names.
# Copyright 2019 by Neil Hodgson <neilh@scintilla.org>
# The License.txt file describes the conditions under which this software may be distributed.
# Requires Python 2.7 or later

import codecs, glob, os, sys

if __name__ == "__main__":
	import FileGenerator
else:
	from . import FileGenerator

continuationLineEnd = " \\"

def FindPathToHeader(header, includePath):
	for incDir in includePath:
		relPath = os.path.join(incDir, header)
		if os.path.exists(relPath):
			return relPath
	return ""

fhifCache = {}	# Remember the includes in each file. ~5x speed up.
def FindHeadersInFile(filePath):
	if filePath not in fhifCache:
		headers = []
		with codecs.open(filePath, "r", "utf-8") as f:
			for line in f:
				if line.strip().startswith("#include"):
					parts = line.split()
					if len(parts) > 1:
						header = parts[1]
						if header[0] != '<':	# No system headers
							headers.append(header.strip('"'))
		fhifCache[filePath] = headers
	return fhifCache[filePath]

def FindHeadersInFileRecursive(filePath, includePath, renames):
	headerPaths = []
	for header in FindHeadersInFile(filePath):
		if header in renames:
			header = renames[header]
		relPath = FindPathToHeader(header, includePath)
		if relPath and relPath not in headerPaths:
				headerPaths.append(relPath)
				subHeaders = FindHeadersInFileRecursive(relPath, includePath, renames)
				headerPaths.extend(sh for sh in subHeaders if sh not in headerPaths)
	return headerPaths

def RemoveStart(relPath, start):
	if relPath.startswith(start):
		return relPath[len(start):]
	return relPath

def ciKey(f):
	return f.lower()

def FindDependencies(sourceGlobs, includePath, objExt, startDirectory, renames={}):
	deps = []
	for sourceGlob in sourceGlobs:
		sourceFiles = glob.glob(sourceGlob)
		# Sorting the files minimizes deltas as order returned by OS may be arbitrary
		sourceFiles.sort(key=ciKey)
		for sourceName in sourceFiles:
			objName = os.path.splitext(os.path.basename(sourceName))[0]+objExt
			headerPaths = FindHeadersInFileRecursive(sourceName, includePath, renames)
			depsForSource = [sourceName] + headerPaths
			depsToAppend = [RemoveStart(fn.replace("\\", "/"), startDirectory) for
				fn in depsForSource]
			deps.append([objName, depsToAppend])
	return deps

def PathStem(p):
	""" Return the stem of a filename: "CallTip.o" -> "CallTip" """
	return os.path.splitext(os.path.basename(p))[0]

def InsertSynonym(dependencies, current, additional):
	""" Insert a copy of one object file with dependencies under a different name.
	Used when one source file is used to create two object files with different
	preprocessor definitions. """
	result = []
	for dep in dependencies:
		result.append(dep)
		if (dep[0] == current):
			depAdd = [additional, dep[1]]
			result.append(depAdd)
	return result

def ExtractDependencies(input):
	""" Create a list of dependencies from input list of lines
	Each element contains the name of the object and a list of
	files that it depends on.
	Dependencies that contain "/usr/" are removed as they are system headers. """

	deps = []
	for line in input:
		headersLine = line.startswith(" ") or line.startswith("\t")
		line = line.strip()
		isContinued = line.endswith("\\")
		line = line.rstrip("\\ ")
		fileNames = line.strip().split(" ")
		if not headersLine:
			# its a source file line, there may be headers too
			sourceLine = fileNames[0].rstrip(":")
			fileNames = fileNames[1:]
			deps.append([sourceLine, []])
		deps[-1][1].extend(header for header in fileNames if "/usr/" not in header)
	return deps

def TextFromDependencies(dependencies):
	""" Convert a list of dependencies to text. """
	text = ""
	indentHeaders = "\t"
	joinHeaders = continuationLineEnd + os.linesep + indentHeaders
	for dep in dependencies:
		object, headers = dep
		text += object + ":"
		for header in headers:
			text += joinHeaders
			text += header
		if headers:
			text += os.linesep
	return text

def UpdateDependencies(filepath, dependencies, comment=""):
	""" Write a dependencies file if different from dependencies. """
	FileGenerator.UpdateFile(os.path.abspath(filepath), comment.rstrip() + os.linesep +
		TextFromDependencies(dependencies))

def WriteDependencies(output, dependencies):
	""" Write a list of dependencies out to a stream. """
	output.write(TextFromDependencies(dependencies))

if __name__ == "__main__":
	""" Act as a filter that reformats input dependencies to one per line. """
	inputLines = sys.stdin.readlines()
	deps = ExtractDependencies(inputLines)
	WriteDependencies(sys.stdout, deps)