BaseTools/Plugin/CodeQL: Add CodeQL build plugin
Adds a CodeQL plugin that supports CodeQL in the build system. 1. CodeQlBuildPlugin - Generates a CodeQL database for a given build. 2. CodeQlAnalyzePlugin - Analyzes a CodeQL database and interprets results. 3. External dependencies - Assist with downloading the CodeQL CLI and making it available to the CodeQL plugins. 4. CodeQlQueries.qls - A C/C++ CodeQL query set run against the code. 5. Readme.md - A comprehensive readme file to help: - Platform integrators understand how to configure the plugin - Developers understand how to modify the plugin - Users understand how to use the plugin Read Readme.md for additional details. Cc: Bob Feng <bob.c.feng@intel.com> Cc: Liming Gao <gaoliming@byosoft.com.cn> Cc: Michael D Kinney <michael.d.kinney@intel.com> Cc: Rebecca Cran <rebecca@bsdio.com> Cc: Sean Brogan <sean.brogan@microsoft.com> Cc: Yuwei Chen <yuwei.chen@intel.com> Signed-off-by: Michael Kubacki <michael.kubacki@microsoft.com> Reviewed-by: Yuwei Chen <yuwei.chen@intel.com> Reviewed-by: Sean Brogan <sean.brogan@microsoft.com> Acked-by: Laszlo Ersek <lersek@redhat.com> Acked-by: Michael D Kinney <michael.d.kinney@intel.com>
This commit is contained in:
committed by
mergify[bot]
parent
c1393bd486
commit
5464d0bed6
169
BaseTools/Plugin/CodeQL/CodeQlBuildPlugin.py
Normal file
169
BaseTools/Plugin/CodeQL/CodeQlBuildPlugin.py
Normal file
@ -0,0 +1,169 @@
|
||||
# @file CodeQlBuildPlugin.py
|
||||
#
|
||||
# A build plugin that produces CodeQL results for the present build.
|
||||
#
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
##
|
||||
|
||||
import glob
|
||||
import logging
|
||||
import os
|
||||
import stat
|
||||
from common import codeql_plugin
|
||||
from pathlib import Path
|
||||
|
||||
from edk2toolext import edk2_logging
|
||||
from edk2toolext.environment.plugintypes.uefi_build_plugin import \
|
||||
IUefiBuildPlugin
|
||||
from edk2toolext.environment.uefi_build import UefiBuilder
|
||||
from edk2toollib.uefi.edk2.path_utilities import Edk2Path
|
||||
from edk2toollib.utility_functions import GetHostInfo, RemoveTree
|
||||
|
||||
|
||||
class CodeQlBuildPlugin(IUefiBuildPlugin):
|
||||
|
||||
def do_pre_build(self, builder: UefiBuilder) -> int:
|
||||
"""CodeQL pre-build functionality.
|
||||
|
||||
Args:
|
||||
builder (UefiBuilder): A UEFI builder object for this build.
|
||||
|
||||
Returns:
|
||||
int: The plugin return code. Zero indicates the plugin ran
|
||||
successfully. A non-zero value indicates an unexpected error
|
||||
occurred during plugin execution.
|
||||
"""
|
||||
|
||||
if not builder.SkipBuild:
|
||||
self.builder = builder
|
||||
self.package = builder.edk2path.GetContainingPackage(
|
||||
builder.edk2path.GetAbsolutePathOnThisSystemFromEdk2RelativePath(
|
||||
builder.env.GetValue("ACTIVE_PLATFORM")
|
||||
)
|
||||
)
|
||||
|
||||
self.target = builder.env.GetValue("TARGET")
|
||||
|
||||
self.build_output_dir = builder.env.GetValue("BUILD_OUTPUT_BASE")
|
||||
|
||||
self.codeql_db_path = codeql_plugin.get_codeql_db_path(
|
||||
builder.ws, self.package, self.target)
|
||||
|
||||
edk2_logging.log_progress(f"{self.package} will be built for CodeQL")
|
||||
edk2_logging.log_progress(f" CodeQL database will be written to "
|
||||
f"{self.codeql_db_path}")
|
||||
|
||||
self.codeql_path = codeql_plugin.get_codeql_cli_path()
|
||||
if not self.codeql_path:
|
||||
logging.critical("CodeQL build enabled but CodeQL CLI application "
|
||||
"not found.")
|
||||
return -1
|
||||
|
||||
# CodeQL can only generate a database on clean build
|
||||
#
|
||||
# Note: builder.CleanTree() cannot be used here as some platforms
|
||||
# have build steps that run before this plugin that store
|
||||
# files in the build output directory.
|
||||
#
|
||||
# CodeQL does not care about with those files or many others such
|
||||
# as the FV directory, build logs, etc. so instead focus on
|
||||
# removing only the directories with compilation/linker output
|
||||
# for the architectures being built (that need clean runs for
|
||||
# CodeQL to work).
|
||||
targets = self.builder.env.GetValue("TARGET_ARCH").split(" ")
|
||||
for target in targets:
|
||||
directory_to_delete = Path(self.build_output_dir, target)
|
||||
|
||||
if directory_to_delete.is_dir():
|
||||
logging.debug(f"Removing {str(directory_to_delete)} to have a "
|
||||
f"clean build for CodeQL.")
|
||||
RemoveTree(str(directory_to_delete))
|
||||
|
||||
# CodeQL CLI does not handle spaces passed in CLI commands well
|
||||
# (perhaps at all) as discussed here:
|
||||
# 1. https://github.com/github/codeql-cli-binaries/issues/73
|
||||
# 2. https://github.com/github/codeql/issues/4910
|
||||
#
|
||||
# Since it's unclear how quotes are handled and may change in the
|
||||
# future, this code is going to use the workaround to place the
|
||||
# command in an executable file that is instead passed to CodeQL.
|
||||
self.codeql_cmd_path = Path(self.build_output_dir, "codeql_build_command")
|
||||
|
||||
build_params = self._get_build_params()
|
||||
|
||||
codeql_build_cmd = ""
|
||||
if GetHostInfo().os == "Windows":
|
||||
self.codeql_cmd_path = self.codeql_cmd_path.parent / (
|
||||
self.codeql_cmd_path.name + '.bat')
|
||||
elif GetHostInfo().os == "Linux":
|
||||
self.codeql_cmd_path = self.codeql_cmd_path.parent / (
|
||||
self.codeql_cmd_path.name + '.sh')
|
||||
codeql_build_cmd += f"#!/bin/bash{os.linesep * 2}"
|
||||
codeql_build_cmd += "build " + build_params
|
||||
|
||||
self.codeql_cmd_path.parent.mkdir(exist_ok=True, parents=True)
|
||||
self.codeql_cmd_path.write_text(encoding='utf8', data=codeql_build_cmd)
|
||||
|
||||
if GetHostInfo().os == "Linux":
|
||||
os.chmod(self.codeql_cmd_path,
|
||||
os.stat(self.codeql_cmd_path).st_mode | stat.S_IEXEC)
|
||||
for f in glob.glob(os.path.join(
|
||||
os.path.dirname(self.codeql_path), '**/*'), recursive=True):
|
||||
os.chmod(f, os.stat(f).st_mode | stat.S_IEXEC)
|
||||
|
||||
codeql_params = (f'database create {self.codeql_db_path} '
|
||||
f'--language=cpp '
|
||||
f'--source-root={builder.ws} '
|
||||
f'--command={self.codeql_cmd_path}')
|
||||
|
||||
# Set environment variables so the CodeQL build command is picked up
|
||||
# as the active build command.
|
||||
#
|
||||
# Note: Requires recent changes in edk2-pytool-extensions (0.20.0)
|
||||
# to support reading these variables.
|
||||
builder.env.SetValue(
|
||||
"EDK_BUILD_CMD", self.codeql_path, "Set in CodeQL Build Plugin")
|
||||
builder.env.SetValue(
|
||||
"EDK_BUILD_PARAMS", codeql_params, "Set in CodeQL Build Plugin")
|
||||
|
||||
return 0
|
||||
|
||||
def _get_build_params(self) -> str:
|
||||
"""Returns the build command parameters for this build.
|
||||
|
||||
Based on the well-defined `build` command-line parameters.
|
||||
|
||||
Returns:
|
||||
str: A string representing the parameters for the build command.
|
||||
"""
|
||||
build_params = f"-p {self.builder.env.GetValue('ACTIVE_PLATFORM')}"
|
||||
build_params += f" -b {self.target}"
|
||||
build_params += f" -t {self.builder.env.GetValue('TOOL_CHAIN_TAG')}"
|
||||
|
||||
max_threads = self.builder.env.GetValue('MAX_CONCURRENT_THREAD_NUMBER')
|
||||
if max_threads is not None:
|
||||
build_params += f" -n {max_threads}"
|
||||
|
||||
rt = self.builder.env.GetValue("TARGET_ARCH").split(" ")
|
||||
for t in rt:
|
||||
build_params += " -a " + t
|
||||
|
||||
if (self.builder.env.GetValue("BUILDREPORTING") == "TRUE"):
|
||||
build_params += (" -y " +
|
||||
self.builder.env.GetValue("BUILDREPORT_FILE"))
|
||||
rt = self.builder.env.GetValue("BUILDREPORT_TYPES").split(" ")
|
||||
for t in rt:
|
||||
build_params += " -Y " + t
|
||||
|
||||
# add special processing to handle building a single module
|
||||
mod = self.builder.env.GetValue("BUILDMODULE")
|
||||
if (mod is not None and len(mod.strip()) > 0):
|
||||
build_params += " -m " + mod
|
||||
edk2_logging.log_progress("Single Module Build: " + mod)
|
||||
|
||||
build_vars = self.builder.env.GetAllBuildKeyValues(self.target)
|
||||
for key, value in build_vars.items():
|
||||
build_params += " -D " + key + "=" + value
|
||||
|
||||
return build_params
|
Reference in New Issue
Block a user