From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mga09.intel.com (mga09.intel.com []) by mx.groups.io with SMTP id smtpd.web11.1464.1573089234838775507 for ; Wed, 06 Nov 2019 17:13:57 -0800 Authentication-Results: mx.groups.io; dkim=missing; spf=fail (domain: intel.com, ip: , mailfrom: michael.d.kinney@intel.com) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga003.jf.intel.com ([10.7.209.27]) by orsmga102.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 06 Nov 2019 17:13:57 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.68,276,1569308400"; d="scan'208";a="205520633" Received: from unknown (HELO mdkinney-MOBL2.amr.corp.intel.com) ([10.241.98.74]) by orsmga003.jf.intel.com with ESMTP; 06 Nov 2019 17:13:57 -0800 From: "Michael D Kinney" To: devel@edk2.groups.io Cc: Sean Brogan , Bret Barkelew , Liming Gao Subject: [Patch v4 19/22] .pytool: Add CISettings.py and Readme.md Date: Wed, 6 Nov 2019 17:13:46 -0800 Message-Id: <20191107011349.16524-20-michael.d.kinney@intel.com> X-Mailer: git-send-email 2.21.0.windows.1 In-Reply-To: <20191107011349.16524-1-michael.d.kinney@intel.com> References: <20191107011349.16524-1-michael.d.kinney@intel.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit From: Sean Brogan Add main python script for EDK II Continuous Integration (CI) builds along with a Readme.md that provides a summary of the packages, platforms, and checks performs during a CI build. Cc: Sean Brogan Cc: Bret Barkelew Cc: Liming Gao Signed-off-by: Michael D Kinney --- .pytool/CISettings.py | 173 ++++++++++++++++++++++++++++++++++++++ .pytool/Readme.md | 191 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 364 insertions(+) create mode 100644 .pytool/CISettings.py create mode 100644 .pytool/Readme.md diff --git a/.pytool/CISettings.py b/.pytool/CISettings.py new file mode 100644 index 0000000000..a78e8b974c --- /dev/null +++ b/.pytool/CISettings.py @@ -0,0 +1,173 @@ +# @file +# +# Copyright (c) 2018, Microsoft Corporation +# SPDX-License-Identifier: BSD-2-Clause-Patent +## +import os +import logging +from edk2toolext.environment import shell_environment +from edk2toolext.invocables.edk2_ci_build import CiBuildSettingsManager +from edk2toolext.invocables.edk2_setup import SetupSettingsManager, RequiredSubmodule +from edk2toolext.invocables.edk2_update import UpdateSettingsManager +from edk2toolext.invocables.edk2_pr_eval import PrEvalSettingsManager +from edk2toollib.utility_functions import GetHostInfo + + +class Settings(CiBuildSettingsManager, UpdateSettingsManager, SetupSettingsManager, PrEvalSettingsManager): + + def __init__(self): + self.ActualPackages = [] + self.ActualTargets = [] + self.ActualArchitectures = [] + self.ActualToolChainTag = "" + + # ####################################################################################### # + # Extra CmdLine configuration # + # ####################################################################################### # + + def AddCommandLineOptions(self, parserObj): + pass + + def RetrieveCommandLineOptions(self, args): + pass + + # ####################################################################################### # + # Default Support for this Ci Build # + # ####################################################################################### # + + def GetPackagesSupported(self): + ''' return iterable of edk2 packages supported by this build. + These should be edk2 workspace relative paths ''' + + return ("MdePkg", + "MdeModulePkg", + "NetworkPkg", + "PcAtChipsetPkg", + "SecurityPkg", + "UefiCpuPkg", + "FmpDevicePkg", + "ShellPkg", + "FatPkg", + "CryptoPkg" + ) + + def GetArchitecturesSupported(self): + ''' return iterable of edk2 architectures supported by this build ''' + return ("IA32", + "X64", + "ARM", + "AARCH64") + + def GetTargetsSupported(self): + ''' return iterable of edk2 target tags supported by this build ''' + return ("DEBUG", "RELEASE", "NO-TARGET", "NOOPT") + + # ####################################################################################### # + # Verify and Save requested Ci Build Config # + # ####################################################################################### # + + def SetPackages(self, list_of_requested_packages): + ''' Confirm the requested package list is valid and configure SettingsManager + to build the requested packages. + + Raise UnsupportedException if a requested_package is not supported + ''' + unsupported = set(list_of_requested_packages) - \ + set(self.GetPackagesSupported()) + if(len(unsupported) > 0): + logging.critical( + "Unsupported Package Requested: " + " ".join(unsupported)) + raise Exception("Unsupported Package Requested: " + + " ".join(unsupported)) + self.ActualPackages = list_of_requested_packages + + def SetArchitectures(self, list_of_requested_architectures): + ''' Confirm the requests architecture list is valid and configure SettingsManager + to run only the requested architectures. + + Raise Exception if a list_of_requested_architectures is not supported + ''' + unsupported = set(list_of_requested_architectures) - \ + set(self.GetArchitecturesSupported()) + if(len(unsupported) > 0): + logging.critical( + "Unsupported Architecture Requested: " + " ".join(unsupported)) + raise Exception( + "Unsupported Architecture Requested: " + " ".join(unsupported)) + self.ActualArchitectures = list_of_requested_architectures + + def SetTargets(self, list_of_requested_target): + ''' Confirm the request target list is valid and configure SettingsManager + to run only the requested targets. + + Raise UnsupportedException if a requested_target is not supported + ''' + unsupported = set(list_of_requested_target) - \ + set(self.GetTargetsSupported()) + if(len(unsupported) > 0): + logging.critical( + "Unsupported Targets Requested: " + " ".join(unsupported)) + raise Exception("Unsupported Targets Requested: " + + " ".join(unsupported)) + self.ActualTargets = list_of_requested_target + + # ####################################################################################### # + # Actual Configuration for Ci Build # + # ####################################################################################### # + + def GetActiveScopes(self): + ''' return tuple containing scopes that should be active for this process ''' + scopes = ("cibuild","edk2-build") + + self.ActualToolChainTag = shell_environment.GetBuildVars().GetValue("TOOL_CHAIN_TAG", "") + + if GetHostInfo().os.upper() == "LINUX" and self.ActualToolChainTag.upper().startswith("GCC"): + if "AARCH64" in self.ActualArchitectures: + scopes += ("gcc_aarch64_linux",) + if "ARM" in self.ActualArchitectures: + scopes += ("gcc_arm_linux",) + + return scopes + + def GetRequiredSubmodules(self): + ''' return iterable containing RequiredSubmodule objects. + If no RequiredSubmodules return an empty iterable + ''' + rs=[] + rs.append(RequiredSubmodule( + "ArmPkg/Library/ArmSoftFloatLib/berkeley-softfloat-3", False)) + rs.append(RequiredSubmodule( + "CryptoPkg/Library/OpensslLib/openssl", False)) + return rs + + def GetName(self): + return "Edk2" + + def GetDependencies(self): + return [] + + def GetPackagesPath(self): + return () + + def GetWorkspaceRoot(self): + ''' get WorkspacePath ''' + return os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + def FilterPackagesToTest(self, changedFilesList: list, potentialPackagesList: list) -> list: + ''' Filter potential packages to test based on changed files. ''' + build_these_packages=[] + possible_packages=potentialPackagesList.copy() + for f in changedFilesList: + nodes=f.split("/") # split each part of path for comparison later + + # python file change in .pytool folder causes building all + if f.endswith(".py") and ".pytool" in nodes: + build_these_packages = possible_packages + break + + # BaseTools files that might change the build + if "BaseTools" in nodes: + if os.path.splitext(f) not in [".txt", ".md"]: + build_these_packages = possible_packages + break + return build_these_packages diff --git a/.pytool/Readme.md b/.pytool/Readme.md new file mode 100644 index 0000000000..fa10628fcf --- /dev/null +++ b/.pytool/Readme.md @@ -0,0 +1,191 @@ +# Edk2 Continuous Integration + +## Basic Status + +| Package | Windows VS2019 | Ubuntu GCC | Known Issues | +| :---- | :----- | :---- | :--- | +| ArmPkg | +| ArmPlatformPkg | +| ArmVirtPkg | +| CryptoPkg | :heavy_check_mark: | :heavy_check_mark: | New changes for NULL basecryptlib and tlslib, add IntrinsicLib.inf to dsc +| DynamicTablesPkg | +| EmbeddedPkg | +| EmulatorPkg | +| FatPkg | :heavy_check_mark: | :heavy_check_mark: | +| FmpDevicePkg | :heavy_check_mark: | :heavy_check_mark: | +| IntelFsp2Pkg | +| IntelFsp2WrapperPkg | +| MdeModulePkg | :heavy_check_mark: | :heavy_check_mark: | DxeIpl dependency on ArmPkg, Missing Visual Studio AARCH64/ARM support for EBC +| MdePkg | :heavy_check_mark: | :heavy_check_mark: | Update DSC to add UefiFileHandleLib, Update BaseIoLibIntrinsic to support MSFT ARM/AARCH64 +| NetworkPkg | :heavy_check_mark: | :heavy_check_mark: | Libraries missing from components section for package dsc +| OvmfPkg | +| PcAtChipsetPkg | :heavy_check_mark: | :heavy_check_mark: | +| SecurityPkg | :heavy_check_mark: | :heavy_check_mark: | Incorrect libraryclass in dec +| ShellPkg | :heavy_check_mark: | :heavy_check_mark: | +| SignedCapsulePkg | +| SourceLevelDebugPkg | +| StandaloneMmPkg | +| UefiCpuPkg | :heavy_check_mark: | :heavy_check_mark: | +| UefiPayloadPkg | + +For more detailed status look at the test results of the latest CI run on the +repo readme. + +## Background + +While a number of CI solutions exist, this proposal will focus on the usage of +Azure Dev Ops and Build Pipelines. For demonstration, a sample +[TianoCore repo](https://github.com/tianocore/edk2-staging.git) (branch master) +and [Dev Ops Pipeline](https://dev.azure.com/tianocore/edk2-ci-play/_build?definitionId=14) +have been set up. + +Furthermore, this proposal will leverage the TianoCore python tools PIP modules: +[library](https://pypi.org/project/edk2-pytool-library/) and +[extensions](https://pypi.org/project/edk2-pytool-extensions/) (with repos +located [here](https://github.com/tianocore/edk2-pytool-library) and +[here](https://github.com/tianocore/edk2-pytool-extensions)). + +The primary execution flows can be found in the +`ci/AzurePipelines/Windows-VS2019.yml` and `ci/AzurePipelines/Ubuntu-GCC5.yml` +files. These YAML files are consumed by the Azure Dev Ops Build Pipeline and +dictate what server resources should be used, how they should be configured, and +what processes should be run on them. An overview of this schema can be found +[here](https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=azure-devops&tabs=schema). + +Inspection of these files reveals the EDKII Tools commands that make up the +primary processes for the CI build: 'stuart_setup', 'stuart_update', and +'stuart_ci_build'. These commands come from the EDKII Tools PIP modules and are +configured as described below. More documentation on the stuart tools can be +found [here](https://github.com/tianocore/edk2-pytool-extensions/blob/master/docs/using.md) +and [here](https://github.com/tianocore/edk2-pytool-extensions/blob/master/docs/features/feature_invocables.md). + +## Configuration + +Configuration of the CI process consists of (in order of precedence): + +* command-line arguments passed in via the Pipeline YAML +* a per-package configuration file (e.g. `.ci.yaml`) that is + detected by the CI system in EDKII Tools. +* a global configuration Python module (e.g. `CISetting.py`) passed in via the + command-line + +The global configuration file is described in +[this readme](https://github.com/tianocore/edk2-pytool-extensions/blob/master/docs/usability/using_settings_manager.md) +from the EDKII Tools documentation. This configuration is written as a Python +module so that decisions can be made dynamically based on command line +parameters and codebase state. + +The per-package configuration file can override most settings in the global +configuration file, but is not dynamic. This file can be used to skip or +customize tests that may be incompatible with a specific package. By default, +the global configuration will try to run all tests on all packages. + +## Current PyTool Test Capabilities + +All CI tests are instances of EDKII Tools plugins. Documentation on the plugin +system can be found [here](https://github.com/tianocore/edk2-pytool-extensions/blob/master/docs/usability/using_plugin_manager.md) +and [here](https://github.com/tianocore/edk2-pytool-extensions/blob/master/docs/features/feature_plugin_manager.md). +Upon invocation, each plugin will be passed the path to the current package +under test and a dictionary containing its targeted configuration, as assembled +from the command line, per-package configuration, and global configuration. + +Note: CI plugins are considered unique from build plugins and helper plugins, +even though some CI plugins may execute steps of a build. + +In the example, these plugins live alongside the code under test (in the +`ci/Plugin` directory), but may be moved to the 'edk2-test' repo if that +location makes more sense for the community. + +### Module Inclusion Test - DscCompleteCheck + +This test scans all available modules (via INF files) and compares them to the +package-level DSC file for the package each module is contained within. The test +considers it an error if any module does not appear in the `Components` section +of at least one package-level DSC (indicating that it would not be built if the +package were built). + +### Code Compilation Test - CompilerPlugin + +Once the Module Inclusion Test has verified that all modules would be built if +all package-level DSCs were built, the Code Compilation Test simply runs through +and builds every package-level DSC on every toolchain and for every architecture +that is supported. Any module that fails to build is considered an error. + +### GUID Uniqueness Test - GuidCheck + +This test works on the collection of all packages rather than an individual +package. It looks at all FILE_GUIDs and GUIDs declared in DEC files and ensures +that they are unique for the codebase. This prevents, for example, accidental +duplication of GUIDs when using an existing INF as a template for a new module. + +### Cross-Package Dependency Test - DependencyCheck + +This test compares the list of all packages used in INFs files for a given +package against a list of "allowed dependencies" in plugin configuration for +that package. Any module that depends on a disallowed package will cause a test +failure. + +### Library Declaration Test - LibraryClassCheck + +This test scans at all library header files found in the `Library` folders in +all of the package's declared include directories and ensures that all files +have a matching LibraryClass declaration in the DEC file for the package. Any +missing declarations will cause a failure. + +### Invalid Character Test - CharEncodingCheck + +This test scans all files in a package to make sure that there are no invalid +Unicode characters that may cause build errors in some character +sets/localizations. + +### Spell Checking - cspell + +This test runs a spell checker on all files within the package. This is done +using the NodeJs cspell tool. For details check `ci/Plugin/SpellCheck`. For +this plugin to run during ci you must install nodejs and cspell and have both +available to the command line when running your CI. + +Install + +* Install nodejs from https://nodejs.org/en/ +* Install cspell + 1. Open cmd prompt with access to node and npm + 2. Run `npm install -g cspell` + + More cspell info: https://github.com/streetsidesoftware/cspell + +## Current Azure Pipeline Tests + +When adding a test it can be added as either a *PyTool* test or just added to +the CI build process. This should be a deliberate choice. Any change added as +a pipeline test is not as easily run on a private/local workspace. But there +are times where this is still the preferred method. + +## PyTool Scopes + +Scopes are how the PyTool ext_dep, path_env, and plugins are activated. Meaning +that if an invocable process has a scope active then those ext_dep and path_env +will be active. To allow easy integration of PyTools capabilities there are a +few standard scopes. + +| Scope | Invocable | Description | +| :---- | :----- | :---- | +| global | edk2_invocable++ - should be base_abstract_invocable | Running an invocables | +| global-win | edk2_invocable++ | Running on Microsoft Windows | +| global-nix | edk2_invocable++ | Running on Linux based OS | +| edk2-build | | This indicates that an invocable is building EDK2 based UEFI code | +| cibuild | set in .pytool/CISettings.py | Suggested target for edk2 continuous integration builds. Tools used for CiBuilds can use this scope. Example: asl compiler | + +## Future investments + +* PatchCheck tests as plugins +* MacOS/xcode support +* Clang/LLVM support +* Visual Studio AARCH64 and ARM support +* BaseTools C tools CI/PR and binary release process +* BaseTools Python tools CI/PR process +* Host based unit testing +* Extensible private/closed source platform reporting +* Platform builds, validation +* UEFI SCTs +* Other automation -- 2.21.0.windows.1