]> git.proxmox.com Git - mirror_edk2.git/blob - BaseTools/Source/Python/AutoGen/IncludesAutoGen.py
bb6e883d84ca6decc97514f95a35bd6db8714eed
[mirror_edk2.git] / BaseTools / Source / Python / AutoGen / IncludesAutoGen.py
1 ## @file
2 # Build cache intermediate result and state
3 #
4 # Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
5 # SPDX-License-Identifier: BSD-2-Clause-Patent
6 #
7 from Common.caching import cached_property
8 import Common.EdkLogger as EdkLogger
9 import Common.LongFilePathOs as os
10 from Common.BuildToolError import *
11 from Common.Misc import SaveFileOnChange, PathClass
12 from Common.Misc import TemplateString
13 import sys
14 gIsFileMap = {}
15 if sys.platform == "win32":
16 _INCLUDE_DEPS_TEMPLATE = TemplateString('''
17 ${BEGIN}
18 !IF EXIST(${deps_file})
19 !INCLUDE ${deps_file}
20 !ENDIF
21 ${END}
22 ''')
23 else:
24 _INCLUDE_DEPS_TEMPLATE = TemplateString('''
25 ${BEGIN}
26 -include ${deps_file}
27 ${END}
28 ''')
29
30 DEP_FILE_TAIL = "# Updated \n"
31
32 class IncludesAutoGen():
33 """ This class is to manage the dependent files witch are used in Makefile to support incremental build.
34 1. C files:
35 1. MSVS.
36 cl.exe has a build option /showIncludes to display include files on stdout. Build tool captures
37 that messages and generate dependency files, .deps files.
38 2. CLANG and GCC
39 -MMD -MF build option are used to generate dependency files by compiler. Build tool updates the
40 .deps files.
41 2. ASL files:
42 1. Trim find out all the included files with asl specific include format and generate .trim.deps file.
43 2. ASL PP use c preprocessor to find out all included files with #include format and generate a .deps file
44 3. build tool updates the .deps file
45 3. ASM files (.asm, .s or .nasm):
46 1. Trim find out all the included files with asl specific include format and generate .trim.deps file.
47 2. ASM PP use c preprocessor to find out all included files with #include format and generate a deps file
48 3. build tool updates the .deps file
49 """
50 def __init__(self, makefile_folder, ModuleAuto):
51 self.d_folder = makefile_folder
52 self.makefile_folder = makefile_folder
53 self.module_autogen = ModuleAuto
54 self.ToolChainFamily = ModuleAuto.ToolChainFamily
55 self.workspace = ModuleAuto.WorkspaceDir
56
57 def CreateModuleDeps(self):
58 SaveFileOnChange(os.path.join(self.makefile_folder,"deps.txt"),"\n".join(self.DepsCollection),False)
59
60 def CreateDepsInclude(self):
61 deps_file = {'deps_file':self.deps_files}
62 try:
63 deps_include_str = _INCLUDE_DEPS_TEMPLATE.Replace(deps_file)
64 except Exception as e:
65 print(e)
66 SaveFileOnChange(os.path.join(self.makefile_folder,"dependency"),deps_include_str,False)
67
68 @cached_property
69 def deps_files(self):
70 """ Get all .deps file under module build folder. """
71 deps_files = []
72 for root, _, files in os.walk(self.d_folder, topdown=False):
73 for name in files:
74 if not name.endswith(".deps"):
75 continue
76 abspath = os.path.join(root, name)
77 deps_files.append(abspath)
78 return deps_files
79
80 @cached_property
81 def DepsCollection(self):
82 """ Collect all the dependency files list from all .deps files under a module's build folder """
83 includes = set()
84 targetname = [item[0].Name for item in self.TargetFileList.values()]
85 for abspath in self.deps_files:
86 try:
87 with open(abspath,"r") as fd:
88 lines = fd.readlines()
89
90 firstlineitems = lines[0].split(": ")
91 dependency_file = firstlineitems[1].strip(" \\\n")
92 dependency_file = dependency_file.strip('''"''')
93 if dependency_file:
94 if os.path.normpath(dependency_file +".deps") == abspath:
95 continue
96 filename = os.path.basename(dependency_file).strip()
97 if filename not in self.SourceFileList and filename not in targetname:
98 includes.add(dependency_file.strip())
99
100 for item in lines[1:]:
101 if item == DEP_FILE_TAIL:
102 continue
103 dependency_file = item.strip(" \\\n")
104 dependency_file = dependency_file.strip('''"''')
105 if os.path.normpath(dependency_file +".deps") == abspath:
106 continue
107 filename = os.path.basename(dependency_file).strip()
108 if filename in self.SourceFileList:
109 continue
110 if filename in targetname:
111 continue
112 includes.add(dependency_file.strip())
113 except Exception as e:
114 EdkLogger.error("build",FILE_NOT_FOUND, "%s doesn't exist" % abspath, ExtraData=str(e), RaiseError=False)
115 continue
116 rt = sorted(list(set([item.strip(' " \\\n') for item in includes])))
117 return rt
118
119 @cached_property
120 def SourceFileList(self):
121 """ Get a map of module's source files name to module's source files path """
122 source = {os.path.basename(item.File):item.Path for item in self.module_autogen.SourceFileList}
123 middle_file = {}
124 for afile in source:
125 if afile.upper().endswith(".VFR"):
126 middle_file.update({afile.split(".")[0]+".c":os.path.join(self.module_autogen.DebugDir,afile.split(".")[0]+".c")})
127 if afile.upper().endswith((".S","ASM")):
128 middle_file.update({afile.split(".")[0]+".i":os.path.join(self.module_autogen.OutputDir,afile.split(".")[0]+".i")})
129 if afile.upper().endswith(".ASL"):
130 middle_file.update({afile.split(".")[0]+".i":os.path.join(self.module_autogen.OutputDir,afile.split(".")[0]+".i")})
131 source.update({"AutoGen.c":os.path.join(self.module_autogen.OutputDir,"AutoGen.c")})
132 source.update(middle_file)
133 return source
134
135 @cached_property
136 def HasNamesakeSourceFile(self):
137 source_base_name = set([os.path.basename(item.File) for item in self.module_autogen.SourceFileList])
138 rt = len(source_base_name) != len(self.module_autogen.SourceFileList)
139 return rt
140 @cached_property
141 def CcPPCommandPathSet(self):
142 rt = set()
143 rt.add(self.module_autogen.BuildOption.get('CC',{}).get('PATH'))
144 rt.add(self.module_autogen.BuildOption.get('ASLCC',{}).get('PATH'))
145 rt.add(self.module_autogen.BuildOption.get('ASLPP',{}).get('PATH'))
146 rt.add(self.module_autogen.BuildOption.get('VFRPP',{}).get('PATH'))
147 rt.add(self.module_autogen.BuildOption.get('PP',{}).get('PATH'))
148 rt.add(self.module_autogen.BuildOption.get('APP',{}).get('PATH'))
149 rt.discard(None)
150 return rt
151 @cached_property
152 def TargetFileList(self):
153 """ Get a map of module's target name to a tuple of module's targets path and whose input file path """
154 targets = {}
155 targets["AutoGen.obj"] = (PathClass(os.path.join(self.module_autogen.OutputDir,"AutoGen.obj")),PathClass(os.path.join(self.module_autogen.DebugDir,"AutoGen.c")))
156 for item in self.module_autogen.Targets.values():
157 for block in item:
158 targets[block.Target.Path] = (block.Target,block.Inputs[0])
159 return targets
160
161 def GetRealTarget(self,source_file_abs):
162 """ Get the final target file based on source file abspath """
163 source_target_map = {item[1].Path:item[0].Path for item in self.TargetFileList.values()}
164 source_name_map = {item[1].File:item[0].Path for item in self.TargetFileList.values()}
165 target_abs = source_target_map.get(source_file_abs)
166 if target_abs is None:
167 if source_file_abs.strip().endswith(".i"):
168 sourcefilename = os.path.basename(source_file_abs.strip())
169 for sourcefile in source_name_map:
170 if sourcefilename.split(".")[0] == sourcefile.split(".")[0]:
171 target_abs = source_name_map[sourcefile]
172 break
173 else:
174 target_abs = source_file_abs
175 else:
176 target_abs = source_file_abs
177 return target_abs
178
179 def CreateDepsFileForMsvc(self, DepList):
180 """ Generate dependency files, .deps file from /showIncludes output message """
181 if not DepList:
182 return
183 ModuleDepDict = {}
184 current_source = ""
185 SourceFileAbsPathMap = self.SourceFileList
186 for line in DepList:
187 line = line.strip()
188 if self.HasNamesakeSourceFile:
189 for cc_cmd in self.CcPPCommandPathSet:
190 if cc_cmd in line:
191 if '''"'''+cc_cmd+'''"''' in line:
192 cc_options = line[len(cc_cmd)+2:].split()
193 else:
194 cc_options = line[len(cc_cmd):].split()
195 SourceFileAbsPathMap = {os.path.basename(item):item for item in cc_options if not item.startswith("/") and os.path.exists(item)}
196 if line in SourceFileAbsPathMap:
197 current_source = line
198 if current_source not in ModuleDepDict:
199 ModuleDepDict[SourceFileAbsPathMap[current_source]] = []
200 elif "Note: including file:" == line.lstrip()[:21]:
201 if not current_source:
202 EdkLogger.error("build",BUILD_ERROR, "Parse /showIncludes output failed. line: %s. \n" % line, RaiseError=False)
203 else:
204 ModuleDepDict[SourceFileAbsPathMap[current_source]].append(line.lstrip()[22:].strip())
205
206 for source_abs in ModuleDepDict:
207 if ModuleDepDict[source_abs]:
208 target_abs = self.GetRealTarget(source_abs)
209 dep_file_name = os.path.basename(source_abs) + ".deps"
210 SaveFileOnChange(os.path.join(os.path.dirname(target_abs),dep_file_name)," \\\n".join([target_abs+":"] + ['''"''' + item +'''"''' for item in ModuleDepDict[source_abs]]),False)
211
212 def UpdateDepsFileforNonMsvc(self):
213 """ Update .deps files.
214 1. Update target path to absolute path.
215 2. Update middle target to final target.
216 """
217
218 for abspath in self.deps_files:
219 if abspath.endswith(".trim.deps"):
220 continue
221 try:
222 newcontent = []
223 with open(abspath,"r") as fd:
224 lines = fd.readlines()
225 if lines[-1] == DEP_FILE_TAIL:
226 continue
227 firstlineitems = lines[0].strip().split(" ")
228
229 if len(firstlineitems) > 2:
230 sourceitem = firstlineitems[1]
231 else:
232 sourceitem = lines[1].strip().split(" ")[0]
233
234 source_abs = self.SourceFileList.get(sourceitem,sourceitem)
235 firstlineitems[0] = self.GetRealTarget(source_abs)
236 p_target = firstlineitems
237 if not p_target[0].strip().endswith(":"):
238 p_target[0] += ": "
239
240 if len(p_target) == 2:
241 p_target[0] += lines[1]
242 newcontent.append(p_target[0])
243 newcontent.extend(lines[2:])
244 else:
245 line1 = " ".join(p_target).strip()
246 line1 += "\n"
247 newcontent.append(line1)
248 newcontent.extend(lines[1:])
249
250 newcontent.append("\n")
251 newcontent.append(DEP_FILE_TAIL)
252 with open(abspath,"w") as fw:
253 fw.write("".join(newcontent))
254 except Exception as e:
255 EdkLogger.error("build",FILE_NOT_FOUND, "%s doesn't exist" % abspath, ExtraData=str(e), RaiseError=False)
256 continue
257
258 def UpdateDepsFileforTrim(self):
259 """ Update .deps file which generated by trim. """
260
261 for abspath in self.deps_files:
262 if not abspath.endswith(".trim.deps"):
263 continue
264 try:
265 newcontent = []
266 with open(abspath,"r") as fd:
267 lines = fd.readlines()
268 if lines[-1] == DEP_FILE_TAIL:
269 continue
270
271 source_abs = lines[0].strip().split(" ")[0]
272 targetitem = self.GetRealTarget(source_abs.strip(" :"))
273
274 targetitem += ": "
275 targetitem += lines[1]
276 newcontent.append(targetitem)
277 newcontent.extend(lines[2:])
278 newcontent.append("\n")
279 newcontent.append(DEP_FILE_TAIL)
280 with open(abspath,"w") as fw:
281 fw.write("".join(newcontent))
282 except Exception as e:
283 EdkLogger.error("build",FILE_NOT_FOUND, "%s doesn't exist" % abspath, ExtraData=str(e), RaiseError=False)
284 continue