## @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" " \n" " TestLib|TestPkg/TestLib.inf\n" " \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" " \n" " !include PrivateLibrary.inc.dsc\n" " \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()