mirror of
https://github.com/tianocore/edk2
synced 2026-08-27 00:23:19 -04:00
Merge cd1d45873c into fb2ac429de
This commit is contained in:
commit
8a12cb91a1
3 changed files with 420 additions and 11 deletions
|
|
@ -1377,6 +1377,7 @@ class DscParser(MetaFileParser):
|
|||
}
|
||||
|
||||
self._Table = MetaFileStorage(self._RawTable.DB, self.MetaFile, MODEL_FILE_DSC, True)
|
||||
self._IdMapping = {-1:-1}
|
||||
self._DirectiveStack = []
|
||||
self._DirectiveEvalStack = []
|
||||
self._FileWithError = self.MetaFile
|
||||
|
|
@ -1400,17 +1401,6 @@ class DscParser(MetaFileParser):
|
|||
|
||||
self._Scope = [[S1, S2, S3]]
|
||||
#
|
||||
# Expand macros in arch field for all records so that per-module
|
||||
# sub-items (e.g. <LibraryClasses> under [Components.$(PEI_ARCH)])
|
||||
# resolve correctly, not just the Component record itself.
|
||||
#
|
||||
self._Scope[0][0] = ReplaceMacro(self._Scope[0][0], self._Macros)
|
||||
if '$(' in self._Scope[0][0] and S1 != TAB_ARCH_COMMON:
|
||||
EdkLogger.warn("Parser",
|
||||
"Macro in arch field was not resolved. "
|
||||
"'%s' used in section header is not defined." % S1,
|
||||
File=self._FileWithError, Line=LineStart)
|
||||
#
|
||||
# For !include directive, handle it specially,
|
||||
# merge arch and module type in case of duplicate items
|
||||
#
|
||||
|
|
@ -1469,6 +1459,8 @@ class DscParser(MetaFileParser):
|
|||
continue
|
||||
|
||||
NewOwner = self._IdMapping.get(Owner, -1)
|
||||
if NewOwner > 0:
|
||||
self.__ExpandArchitectureScope()
|
||||
self._Enabled = int((not self._DirectiveEvalStack) or (False not in self._DirectiveEvalStack))
|
||||
self._LastItem = self._Store(
|
||||
self._ItemType,
|
||||
|
|
@ -1723,6 +1715,16 @@ class DscParser(MetaFileParser):
|
|||
|
||||
def __ProcessComponent(self):
|
||||
self._ValueList[0] = ReplaceMacro(self._ValueList[0], self._Macros)
|
||||
self.__ExpandArchitectureScope()
|
||||
|
||||
def __ExpandArchitectureScope(self):
|
||||
OriginalArch = self._Scope[0][0]
|
||||
self._Scope[0][0] = ReplaceMacro(OriginalArch, self._Macros)
|
||||
if '$(' in self._Scope[0][0] and OriginalArch != TAB_ARCH_COMMON:
|
||||
EdkLogger.warn("Parser",
|
||||
"Macro in arch field was not resolved. "
|
||||
"'%s' used in section header is not defined." % OriginalArch,
|
||||
File=self._FileWithError, Line=self._LineIndex + 1)
|
||||
|
||||
def __ProcessBuildOption(self):
|
||||
self._ValueList = [ReplaceMacro(Value, self._Macros, RaiseError=False)
|
||||
|
|
|
|||
405
BaseTools/Tests/MetaFileParserTests.py
Normal file
405
BaseTools/Tests/MetaFileParserTests.py
Normal file
|
|
@ -0,0 +1,405 @@
|
|||
## @file
|
||||
# Unit tests for DSC parser component scope handling.
|
||||
#
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
#
|
||||
##
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
from Common import GlobalData
|
||||
from CommonDataClass.DataClass import MODEL_EFI_LIBRARY_CLASS
|
||||
from CommonDataClass.DataClass import MODEL_FILE_DSC
|
||||
from CommonDataClass.DataClass import MODEL_META_DATA_COMPONENT
|
||||
from CommonDataClass.DataClass import MODEL_PCD_FIXED_AT_BUILD
|
||||
from Common.Misc import PathClass
|
||||
from Workspace.MetaFileParser import DscParser
|
||||
from Workspace.MetaFileParser import MetaFileParser
|
||||
from Workspace.MetaFileTable import MetaFileStorage
|
||||
from Workspace.WorkspaceDatabase import WorkspaceDatabase
|
||||
|
||||
|
||||
class DscParserComponentScopeTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self._saved_workspace = GlobalData.gWorkspace
|
||||
self._saved_global_defines = GlobalData.gGlobalDefines
|
||||
self._saved_platform_defines = GlobalData.gPlatformDefines
|
||||
self._saved_command_line_defines = GlobalData.gCommandLineDefines
|
||||
self._saved_edk_global = GlobalData.gEdkGlobal
|
||||
self._saved_platform_pcds = GlobalData.gPlatformPcds
|
||||
self._saved_platform_other_pcds = GlobalData.gPlatformOtherPcds
|
||||
self._saved_meta_files = MetaFileParser.MetaFiles.copy()
|
||||
self._saved_storage_cache = MetaFileStorage._ObjectCache.copy()
|
||||
self._saved_included_files = DscParser.IncludedFiles.copy()
|
||||
|
||||
self._temporary_directory = tempfile.TemporaryDirectory()
|
||||
workspace = self._temporary_directory.name
|
||||
GlobalData.gWorkspace = workspace
|
||||
GlobalData.gGlobalDefines = {"WORKSPACE": workspace}
|
||||
GlobalData.gPlatformDefines = {}
|
||||
GlobalData.gCommandLineDefines = {}
|
||||
GlobalData.gEdkGlobal = {}
|
||||
GlobalData.gPlatformPcds = {}
|
||||
GlobalData.gPlatformOtherPcds = {}
|
||||
|
||||
def _write_file(self, relative_path, content):
|
||||
path = os.path.join(self._temporary_directory.name, relative_path)
|
||||
directory = os.path.dirname(path)
|
||||
if directory:
|
||||
os.makedirs(directory, exist_ok=True)
|
||||
with open(path, "w", newline="\n") as output_file:
|
||||
output_file.write(content)
|
||||
return path
|
||||
|
||||
def _create_parser(
|
||||
self,
|
||||
platform_name,
|
||||
platform_content,
|
||||
included_files,
|
||||
active_architecture="IA32",
|
||||
):
|
||||
for relative_path, content in included_files.items():
|
||||
self._write_file(relative_path, content)
|
||||
|
||||
platform_path = self._write_file(platform_name, platform_content)
|
||||
path = PathClass(platform_path)
|
||||
database = WorkspaceDatabase()
|
||||
table = MetaFileStorage(database, path, MODEL_FILE_DSC)
|
||||
return DscParser(path, MODEL_FILE_DSC, active_architecture, table)
|
||||
|
||||
@staticmethod
|
||||
def _platform_content(
|
||||
include_path,
|
||||
define_architecture=True,
|
||||
macro_architecture="IA32",
|
||||
):
|
||||
architecture_definition = (
|
||||
f" DEFINE PEI_ARCH = {macro_architecture}\n"
|
||||
if define_architecture
|
||||
else ""
|
||||
)
|
||||
return (
|
||||
"[Defines]\n"
|
||||
" PLATFORM_NAME = ParserTest\n"
|
||||
" PLATFORM_GUID = 11111111-2222-3333-4444-555555555555\n"
|
||||
" PLATFORM_VERSION = 0.1\n"
|
||||
" DSC_SPECIFICATION = 0x00010005\n"
|
||||
" OUTPUT_DIRECTORY = Build/ParserTest\n"
|
||||
" SUPPORTED_ARCHITECTURES = IA32|X64\n"
|
||||
" BUILD_TARGETS = DEBUG\n"
|
||||
" SKUID_IDENTIFIER = DEFAULT\n"
|
||||
f"{architecture_definition}"
|
||||
"\n"
|
||||
f"!include {include_path}\n"
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _component_content(section):
|
||||
return (
|
||||
f"[{section}]\n"
|
||||
" TestPkg/TestPeim.inf {\n"
|
||||
" <LibraryClasses>\n"
|
||||
" TestLib|TestPkg/TestLib.inf\n"
|
||||
" <PcdsFixedAtBuild>\n"
|
||||
" gTestPkgTokenSpaceGuid.PcdTest|TRUE\n"
|
||||
" }\n"
|
||||
)
|
||||
|
||||
def _assert_component_private_records(self, parser, architecture):
|
||||
components = parser[MODEL_META_DATA_COMPONENT, architecture]
|
||||
self.assertEqual(1, len(components))
|
||||
self.assertEqual(architecture, components[0][3])
|
||||
component_id = components[0][6]
|
||||
|
||||
library_classes = parser[
|
||||
MODEL_EFI_LIBRARY_CLASS, architecture, None, component_id
|
||||
]
|
||||
self.assertEqual(1, len(library_classes))
|
||||
self.assertEqual(architecture, library_classes[0][3])
|
||||
|
||||
fixed_pcds = parser[
|
||||
MODEL_PCD_FIXED_AT_BUILD, architecture, None, component_id
|
||||
]
|
||||
self.assertEqual(1, len(fixed_pcds))
|
||||
self.assertEqual(architecture, fixed_pcds[0][3])
|
||||
|
||||
self.assertEqual(
|
||||
[],
|
||||
parser[MODEL_EFI_LIBRARY_CLASS, architecture],
|
||||
)
|
||||
self.assertEqual(
|
||||
[],
|
||||
parser[MODEL_PCD_FIXED_AT_BUILD, architecture],
|
||||
)
|
||||
|
||||
def tearDown(self):
|
||||
MetaFileParser.MetaFiles.clear()
|
||||
MetaFileParser.MetaFiles.update(self._saved_meta_files)
|
||||
MetaFileStorage._ObjectCache.clear()
|
||||
MetaFileStorage._ObjectCache.update(self._saved_storage_cache)
|
||||
DscParser.IncludedFiles.clear()
|
||||
DscParser.IncludedFiles.update(self._saved_included_files)
|
||||
GlobalData.gWorkspace = self._saved_workspace
|
||||
GlobalData.gGlobalDefines = self._saved_global_defines
|
||||
GlobalData.gPlatformDefines = self._saved_platform_defines
|
||||
GlobalData.gCommandLineDefines = self._saved_command_line_defines
|
||||
GlobalData.gEdkGlobal = self._saved_edk_global
|
||||
GlobalData.gPlatformPcds = self._saved_platform_pcds
|
||||
GlobalData.gPlatformOtherPcds = self._saved_platform_other_pcds
|
||||
self._temporary_directory.cleanup()
|
||||
|
||||
def test_private_records_use_expanded_component_architecture(self):
|
||||
parser = self._create_parser(
|
||||
"Platform.dsc",
|
||||
self._platform_content("Child.inc.dsc"),
|
||||
{
|
||||
"Child.inc.dsc": self._component_content(
|
||||
"Components.$(PEI_ARCH)"
|
||||
)
|
||||
},
|
||||
)
|
||||
self._assert_component_private_records(parser, "IA32")
|
||||
|
||||
def test_macro_architecture_is_independent_of_active_architecture(self):
|
||||
parser = self._create_parser(
|
||||
"DifferentArch.dsc",
|
||||
self._platform_content(
|
||||
"DifferentArch.inc.dsc",
|
||||
macro_architecture="X64",
|
||||
),
|
||||
{
|
||||
"DifferentArch.inc.dsc": self._component_content(
|
||||
"Components.$(PEI_ARCH)"
|
||||
)
|
||||
},
|
||||
active_architecture="IA32",
|
||||
)
|
||||
self._assert_component_private_records(parser, "X64")
|
||||
|
||||
self.assertEqual(
|
||||
[],
|
||||
parser[MODEL_META_DATA_COMPONENT, "IA32"],
|
||||
)
|
||||
|
||||
def test_aarch64_private_records_use_expanded_component_architecture(self):
|
||||
platform_content = self._platform_content(
|
||||
"Aarch64.inc.dsc",
|
||||
macro_architecture="AARCH64",
|
||||
).replace(
|
||||
"SUPPORTED_ARCHITECTURES = IA32|X64",
|
||||
"SUPPORTED_ARCHITECTURES = IA32|X64|AARCH64",
|
||||
)
|
||||
parser = self._create_parser(
|
||||
"Aarch64.dsc",
|
||||
platform_content,
|
||||
{
|
||||
"Aarch64.inc.dsc": self._component_content(
|
||||
"Components.$(PEI_ARCH)"
|
||||
)
|
||||
},
|
||||
active_architecture="AARCH64",
|
||||
)
|
||||
self._assert_component_private_records(parser, "AARCH64")
|
||||
|
||||
def test_literal_component_architecture_is_unchanged(self):
|
||||
parser = self._create_parser(
|
||||
"Literal.dsc",
|
||||
self._platform_content("Literal.inc.dsc", False),
|
||||
{
|
||||
"Literal.inc.dsc": self._component_content(
|
||||
"Components.IA32"
|
||||
)
|
||||
},
|
||||
)
|
||||
self._assert_component_private_records(parser, "IA32")
|
||||
|
||||
def test_unresolved_component_architecture_macro_is_unchanged(self):
|
||||
parser = self._create_parser(
|
||||
"Unresolved.dsc",
|
||||
self._platform_content("Unresolved.inc.dsc", False),
|
||||
{
|
||||
"Unresolved.inc.dsc": self._component_content(
|
||||
"Components.$(UNKNOWN_ARCH)"
|
||||
)
|
||||
},
|
||||
)
|
||||
self._assert_component_private_records(parser, "$(UNKNOWN_ARCH)")
|
||||
|
||||
def test_nested_include_private_records_use_expanded_architecture(self):
|
||||
parser = self._create_parser(
|
||||
"Nested.dsc",
|
||||
self._platform_content("Intermediate.inc.dsc"),
|
||||
{
|
||||
"Intermediate.inc.dsc": "!include Leaf.inc.dsc\n",
|
||||
"Leaf.inc.dsc": self._component_content(
|
||||
"Components.$(PEI_ARCH)"
|
||||
),
|
||||
},
|
||||
)
|
||||
self._assert_component_private_records(parser, "IA32")
|
||||
|
||||
def test_top_level_include_scope_is_not_expanded(self):
|
||||
parser = self._create_parser(
|
||||
"TopLevelInclude.dsc",
|
||||
self._platform_content("TopLevelInclude.inc.dsc"),
|
||||
{
|
||||
"TopLevelInclude.inc.dsc": (
|
||||
"[LibraryClasses.$(PEI_ARCH)]\n"
|
||||
" TestLib|TestPkg/TestLib.inf\n"
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
library_classes = parser[
|
||||
MODEL_EFI_LIBRARY_CLASS,
|
||||
"$(PEI_ARCH)",
|
||||
]
|
||||
self.assertEqual(1, len(library_classes))
|
||||
self.assertEqual("$(PEI_ARCH)", library_classes[0][3])
|
||||
self.assertEqual(
|
||||
[],
|
||||
parser[MODEL_EFI_LIBRARY_CLASS, "IA32"],
|
||||
)
|
||||
|
||||
def test_reprocessing_does_not_reuse_component_owner_mapping(self):
|
||||
GlobalData.gCommandLineDefines["ENABLE_INCLUDE"] = "FALSE"
|
||||
platform_content = self._platform_content("Reprocess.inc.dsc").replace(
|
||||
"!include Reprocess.inc.dsc\n",
|
||||
(
|
||||
"!if $(ENABLE_INCLUDE) == TRUE\n"
|
||||
"!include Reprocess.inc.dsc\n"
|
||||
"!endif\n"
|
||||
),
|
||||
)
|
||||
parser = self._create_parser(
|
||||
"Reprocess.dsc",
|
||||
platform_content,
|
||||
{
|
||||
"Reprocess.inc.dsc": (
|
||||
"[LibraryClasses.$(PEI_ARCH)]\n"
|
||||
" TestLib|TestPkg/TestLib.inf\n"
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
[],
|
||||
parser[MODEL_EFI_LIBRARY_CLASS, "$(PEI_ARCH)"],
|
||||
)
|
||||
|
||||
GlobalData.gCommandLineDefines["ENABLE_INCLUDE"] = "TRUE"
|
||||
parser.DoPostProcess()
|
||||
|
||||
library_classes = parser[
|
||||
MODEL_EFI_LIBRARY_CLASS,
|
||||
"$(PEI_ARCH)",
|
||||
]
|
||||
self.assertEqual(1, len(library_classes))
|
||||
self.assertEqual("$(PEI_ARCH)", library_classes[0][3])
|
||||
self.assertEqual(
|
||||
[],
|
||||
parser[MODEL_EFI_LIBRARY_CLASS, "IA32"],
|
||||
)
|
||||
|
||||
def test_private_subsection_include_keeps_expanded_architecture(self):
|
||||
parser = self._create_parser(
|
||||
"SubsectionInclude.dsc",
|
||||
self._platform_content("SubsectionInclude.inc.dsc"),
|
||||
{
|
||||
"SubsectionInclude.inc.dsc": (
|
||||
"[Components.$(PEI_ARCH), Components.X64]\n"
|
||||
" TestPkg/TestPeim.inf {\n"
|
||||
" <LibraryClasses>\n"
|
||||
" !include PrivateLibrary.inc.dsc\n"
|
||||
" <PcdsFixedAtBuild>\n"
|
||||
" gTestPkgTokenSpaceGuid.PcdTest|TRUE\n"
|
||||
" }\n"
|
||||
),
|
||||
"PrivateLibrary.inc.dsc": (
|
||||
"TestLib|TestPkg/TestLib.inf\n"
|
||||
),
|
||||
},
|
||||
)
|
||||
self._assert_component_private_records(parser, "IA32")
|
||||
self._assert_component_private_records(parser, "X64")
|
||||
|
||||
def test_multi_architecture_private_records_keep_matching_owners(self):
|
||||
parser = self._create_parser(
|
||||
"MultiArch.dsc",
|
||||
self._platform_content("MultiArch.inc.dsc"),
|
||||
{
|
||||
"MultiArch.inc.dsc": self._component_content(
|
||||
"Components.$(PEI_ARCH), Components.X64"
|
||||
)
|
||||
},
|
||||
)
|
||||
self._assert_component_private_records(parser, "IA32")
|
||||
self._assert_component_private_records(parser, "X64")
|
||||
|
||||
def test_repeated_macro_sections_keep_private_records_with_their_owner(self):
|
||||
parser = self._create_parser(
|
||||
"RepeatedSections.dsc",
|
||||
self._platform_content("RepeatedSections.inc.dsc"),
|
||||
{
|
||||
"RepeatedSections.inc.dsc": (
|
||||
"[Components.$(PEI_ARCH)]\n"
|
||||
" TestPkg/FirstPeim.inf\n"
|
||||
"\n"
|
||||
+ self._component_content("Components.$(PEI_ARCH)")
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
components = parser[MODEL_META_DATA_COMPONENT, "IA32"]
|
||||
self.assertEqual(
|
||||
["TestPkg/FirstPeim.inf", "TestPkg/TestPeim.inf"],
|
||||
[component[0] for component in components],
|
||||
)
|
||||
first_component_id = components[0][6]
|
||||
private_component_id = components[1][6]
|
||||
|
||||
self.assertEqual(
|
||||
[],
|
||||
parser[
|
||||
MODEL_EFI_LIBRARY_CLASS,
|
||||
"IA32",
|
||||
None,
|
||||
first_component_id,
|
||||
],
|
||||
)
|
||||
self.assertEqual(
|
||||
1,
|
||||
len(
|
||||
parser[
|
||||
MODEL_EFI_LIBRARY_CLASS,
|
||||
"IA32",
|
||||
None,
|
||||
private_component_id,
|
||||
]
|
||||
),
|
||||
)
|
||||
self.assertEqual(
|
||||
1,
|
||||
len(
|
||||
parser[
|
||||
MODEL_PCD_FIXED_AT_BUILD,
|
||||
"IA32",
|
||||
None,
|
||||
private_component_id,
|
||||
]
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def TheTestSuite():
|
||||
return unittest.defaultTestLoader.loadTestsFromTestCase(
|
||||
DscParserComponentScopeTests
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
@ -20,6 +20,8 @@ def TheTestSuite():
|
|||
suites.append(CheckPythonSyntax.TheTestSuite())
|
||||
import CheckUnicodeSourceFiles
|
||||
suites.append(CheckUnicodeSourceFiles.TheTestSuite())
|
||||
import MetaFileParserTests
|
||||
suites.append(MetaFileParserTests.TheTestSuite())
|
||||
return unittest.TestSuite(suites)
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue