Skip to content

Commit

Permalink
[OEMDATA] Add OEM Data build rule
Browse files Browse the repository at this point in the history
Adds a new build rule for OEM data files which are structured
compiled data. The rule provides support for MSFT, GCC, and
Clang toolchains.

Deletes the intermediate binary files in a module output directory
after the final binary is written using the files. This is
important to prevent intermediate files from potentially being
used in future incremental builds if the corresponding source
file is no longer present.

BaseTools/OemDataFormatter: Rename to ProductDataFormatter

Renames the tool to ProductDataFormatter to align with a change in
data name from "OEM data" to "product data".
  • Loading branch information
makubacki authored and kenlautner committed Jul 2, 2024
1 parent f22ba83 commit 419fc77
Show file tree
Hide file tree
Showing 4 changed files with 180 additions and 4 deletions.
14 changes: 14 additions & 0 deletions BaseTools/BinWrappers/PosixLike/ProductDataFormatter
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/usr/bin/env bash
#python `dirname $0`/RunToolFromSource.py `basename $0` $*

# If a ${PYTHON_COMMAND} command is available, use it in preference to python
if command -v ${PYTHON_COMMAND} >/dev/null 2>&1; then
python_exe=${PYTHON_COMMAND}
fi

full_cmd=${BASH_SOURCE:-$0} # see http://mywiki.wooledge.org/BashFAQ/028 for a discussion of why $0 is not a good choice here
dir=$(dirname "$full_cmd")
exe=$(basename "$full_cmd")

export PYTHONPATH="$dir/../../Source/Python${PYTHONPATH:+:"$PYTHONPATH"}"
exec "${python_exe:-python}" "$dir/../../Source/Python/$exe/$exe.py" "$@"
3 changes: 3 additions & 0 deletions BaseTools/BinWrappers/WindowsLike/ProductDataFormatter.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@setlocal
@set ToolName=%~n0%
@%PYTHON_COMMAND% %BASE_TOOLS_PATH%\Source\Python\%ToolName%\%ToolName%.py %*
47 changes: 43 additions & 4 deletions BaseTools/Conf/build_rule.template
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,10 @@
# 1.01 - Add MASM for Microsoft ARM assembly files. "MS-ARMx-Assembly-Code-File.COMMON.COMMON"
# 2.00 - Add .slib and .efi support in the sources file
# 2.01 - Add support for DEPS_FLAGS as well as
# 2.23 - Add OemData Definitions
# 2.24 - Rename OemData to ProductData.

#!VERSION=2.01
#!VERSION=2.24

[C-Code-File]
<InputFile>
Expand Down Expand Up @@ -372,7 +374,7 @@
<Command.XCODE>
"$(DLINK)" $(DLINK_FLAGS) -o ${dst} $(DLINK_SPATH) -filelist $(STATIC_LIBRARY_FILES_LIST) $(DLINK2_FLAGS)


[Static-Library-File.SEC.AARCH64, Static-Library-File.PEI_CORE.AARCH64, Static-Library-File.PEIM.AARCH64,Static-Library-File.SEC.ARM, Static-Library-File.PEI_CORE.ARM, Static-Library-File.PEIM.ARM]
<InputFile>
*.lib
Expand Down Expand Up @@ -407,7 +409,7 @@
<Command.XCODE>
"$(DLINK)" -o ${dst} $(DLINK_FLAGS) $(DLINK_SPATH) -filelist $(STATIC_LIBRARY_FILES_LIST) $(DLINK2_FLAGS)


[Dynamic-Library-File]
<InputFile>
?.dll
Expand Down Expand Up @@ -548,7 +550,44 @@
"$(MTOC)" -subsystem $(MODULE_TYPE) $(MTOC_FLAGS) $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.dll $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.pecoff
"$(GENFW)" -o ${dst} -c $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.pecoff $(GENFW_FLAGS)


# MU_CHANGE BEGIN
[C-Code-File-ProductDataIntermediate]
<InputFile>
?.productdatac

<OutputFile>
$(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.productdatabin.i

<ExtraDependency>
$(MAKE_FILE)

<Command.MSFT>
"$(CC)" /Fo$(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.obj $(DEPS_FLAGS) /nologo /c /FIAutoGen.h /TC /Dmain=main $(INC) ${src}
"$(DLINK)" /OUT:$(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.dll /NODEFAULTLIB /ENTRY:main /SUBSYSTEM:CONSOLE $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.obj
"$(GENFW)" -o $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.productdatabin.i -b $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.dll $(GENFW_FLAGS)

<Command.GCC>
"$(CC)" $(DEPS_FLAGS) -c -o $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.obj $(CC_FLAGS) -fno-toplevel-reorder -x c $(DEPS_FLAGS) $(INC) ${src}
"$(DLINK)" -o $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.dll $(DLINK_COMMON) -u $(IMAGE_ENTRY_POINT) $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.obj $(CC_FLAGS)
"$(GENFW)" -o $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.productdatabin.i -b $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.dll $(GENFW_FLAGS)

<Command.CLANGPDB>
"$(CC)" $(DEPS_FLAGS) -c -o $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.obj -O0 -fkeep-static-consts --language=c $(DEPS_FLAGS) $(INC) ${src}
"$(DLINK)" /OUT:$(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.dll /NODEFAULTLIB /ENTRY:main /SUBSYSTEM:CONSOLE $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.obj
"$(GENFW)" -o $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.productdatabin.i -b $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.dll $(GENFW_FLAGS)

[ProductDataIntermediate-to-ProductDataBin]
<InputFile>
?.productdatabin.i

<OutputFile>
$(OUTPUT_DIR)(+)${s_dir}(+)$(MODULE_NAME).productdatabin

<Command>
ProductDataFormatter $(OUTPUT_DIR)(+)${s_dir} $(OUTPUT_DIR)(+)${s_dir}(+)$(MODULE_NAME).productdatabin

# MU_CHANGE END

[Masm16-Code-File]
<InputFile>
?.asm16, ?.Asm16, ?.ASM16, ?.s16, ?.S16
Expand Down
120 changes: 120 additions & 0 deletions BaseTools/Source/Python/ProductDataFormatter/ProductDataFormatter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# @file
#
# Formats a collection of intermediate product data binary files
# in a given directory into a single product binary file
#
# Copyright (C) Microsoft Corporation.
#
##

import glob
import io
import os
import struct
from collections import namedtuple


class ProductDataFormatter:
STRUCT_SIGNATURE = b"__SDSH__"
ITEM_SIGNATURE = b"_SDDATA_"
PRODUCT_ID = b"__MSFT__"

def __init__(self, directory_path):
if not os.path.isdir(directory_path):
raise NotADirectoryError(
f"{directory_path} is an invalid directory!")

self._directory_path = directory_path

def _get_bins(self):
return glob.glob(os.path.join(
self._directory_path, '*.productdatabin.i'))

def _get_signed_item(self, file_path):
with open(file_path, 'rb') as bin_file:
bin_data = bytearray(bin_file.read())

signed_item_pos = 0
while True:
signed_item_pos = bin_data.find(
self.ITEM_SIGNATURE, signed_item_pos)

if signed_item_pos == -1:
return

ItemHeader = namedtuple('ItemHeader', 'sig header_len id len')

item_header = ItemHeader._make(struct.unpack_from(
'<QHHI', bin_data, signed_item_pos))

print(f"Signed data item found")
print(f" Item ID: {item_header.id}")
print(f" Item Length: {item_header.len} bytes")

signed_item = bin_data[signed_item_pos:signed_item_pos +
item_header.header_len + item_header.len]

yield signed_item
signed_item_pos += (item_header.header_len + item_header.len)

def _get_signed_structure(self):
signed_items = [i for b in self._get_bins() for i in
self._get_signed_item(b)]

StructureHeader = namedtuple('StructureHeader',
'sig rev header_len product_id desc_count')

struct_header = StructureHeader(
self.STRUCT_SIGNATURE,
1,
26,
self.PRODUCT_ID,
len(signed_items))

struct_header_data = struct.pack('<8sHI8sI', *struct_header)

with io.BytesIO() as out_buffer:
out_buffer.write(struct_header_data)
for item in signed_items:
# Note: Just keep things packed tight for now
# aligned_boundary = (((~out_buffer.tell()) + 1) &
# self.ITEM_ALIGNMENT - 1)
# out_buffer.seek(out_buffer.tell() + aligned_boundary)
out_buffer.write(item)
return out_buffer.getvalue()

def get_file_data(self):
return {f: list(self._get_signed_item(f)) for f in self._get_bins()}

def write_data(self, out_file_path):
with open(out_file_path, 'wb') as out_bin_file:
out_bin_file.write(self._get_signed_structure())

def remove_intermediate_files(self):
for binary in self._get_bins():
try:
os.remove(binary)
except OSError as e:
print(e)
print(f"File path could not be removed.")


# Todo: Clean up arg parsing, documentation, logger, etc.
if __name__ == '__main__':
import argparse

parser = argparse.ArgumentParser(
description=(
'Formats intermediate product data files into '
'a single consolidated binary.'))
parser.add_argument(
'input_directory',
help='Directory of .productdatabin.i files')
parser.add_argument(
'output_file_path',
help='Name of of the final .productdatabin file.')
args = parser.parse_args()

product_data = ProductDataFormatter(args.input_directory)
product_data.write_data(args.output_file_path)
product_data.remove_intermediate_files()

0 comments on commit 419fc77

Please sign in to comment.