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