public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: artem.khakimov@intel.com
To: devel@edk2.groups.io
Cc: Artem Khakimov <artem.khakimov@intel.com>,
	Bob Feng <bob.c.feng@intel.com>,
	Olga Artemeva <olga.artemeva@intel.com>,
	Vadim Mikheev <vadim.mikheev@intel.com>,
	Alexander Larionov <alexander.larionov@intel.com>
Subject: [PATCH] BaseTools: Add python version of GenCrc32
Date: Tue,  5 Apr 2022 12:59:31 -0700	[thread overview]
Message-ID: <86c94d6a11c45be6173bfd185d87158e08569746.1649188308.git.artem.khakimov@intel.com> (raw)

From: Artem Khakimov <artem.khakimov@intel.com>

Add GenCrc32 python tool, add tests, switch
BinWrappers and BinPipWrappers to GenCrc32.py

Cc: Bob Feng <bob.c.feng@intel.com>
Cc: Olga Artemeva <olga.artemeva@intel.com>
Cc: Vadim Mikheev <vadim.mikheev@intel.com>
Cc: Alexander Larionov <alexander.larionov@intel.com>
Signed-off-by: Artem Khakimov <artem.khakimov@intel.com>
---
 BaseTools/BinPipWrappers/PosixLike/GenCrc32   |  41 ++--
 .../BinPipWrappers/WindowsLike/GenCrc32.bat   |   3 +
 BaseTools/BinWrappers/PosixLike/GenCrc32      |  43 ++--
 .../BinWrappers/WindowsLike/GenCrc32.bat      |   4 +
 BaseTools/Source/Python/GenCrc32/GenCrc32.py  | 185 ++++++++++++++++++
 BaseTools/Tests/PythonToolsTests.py           |   2 +
 BaseTools/Tests/TestGenCrc32.py               |  64 ++++++
 7 files changed, 284 insertions(+), 58 deletions(-)
 create mode 100644 BaseTools/BinPipWrappers/WindowsLike/GenCrc32.bat
 create mode 100644 BaseTools/BinWrappers/WindowsLike/GenCrc32.bat
 create mode 100644 BaseTools/Source/Python/GenCrc32/GenCrc32.py
 create mode 100644 BaseTools/Tests/TestGenCrc32.py

diff --git a/BaseTools/BinPipWrappers/PosixLike/GenCrc32 b/BaseTools/BinPipWrappers/PosixLike/GenCrc32
index 0945d86d92..14e8d8af71 100755
--- a/BaseTools/BinPipWrappers/PosixLike/GenCrc32
+++ b/BaseTools/BinPipWrappers/PosixLike/GenCrc32
@@ -1,29 +1,12 @@
-#!/usr/bin/env bash
-
-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")
-cmd=${full_cmd##*/}
-
-if [ -n "$WORKSPACE" ] && [ -e "$WORKSPACE/Conf/BaseToolsCBinaries" ]
-then
-  exec "$WORKSPACE/Conf/BaseToolsCBinaries/$cmd"
-elif [ -n "$WORKSPACE" ] && [ -e "$EDK_TOOLS_PATH/Source/C" ]
-then
-  if [ ! -e "$EDK_TOOLS_PATH/Source/C/bin/$cmd" ]
-  then
-    echo "BaseTools C Tool binary was not found ($cmd)"
-    echo "You may need to run:"
-    echo "  make -C $EDK_TOOLS_PATH/Source/C"
-  else
-    exec "$EDK_TOOLS_PATH/Source/C/bin/$cmd" "$@"
-  fi
-elif [ -e "$dir/../../Source/C/bin/$cmd" ]
-then
-  exec "$dir/../../Source/C/bin/$cmd" "$@"
-else
-  echo "Unable to find the real '$cmd' to run"
-  echo "This message was printed by"
-  echo "  $0"
-  exit 127
-fi
-
+#!/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
+cmd=${full_cmd##*/}
+
+exec "${python_exe:-python}" -m edk2basetools.$cmd "$@"
diff --git a/BaseTools/BinPipWrappers/WindowsLike/GenCrc32.bat b/BaseTools/BinPipWrappers/WindowsLike/GenCrc32.bat
new file mode 100644
index 0000000000..d347d64844
--- /dev/null
+++ b/BaseTools/BinPipWrappers/WindowsLike/GenCrc32.bat
@@ -0,0 +1,3 @@
+@setlocal
+@set ToolName=%~n0%
+@%PYTHON_COMMAND% -m edk2basetools.%ToolName%.%ToolName% %*
diff --git a/BaseTools/BinWrappers/PosixLike/GenCrc32 b/BaseTools/BinWrappers/PosixLike/GenCrc32
index 0945d86d92..39483a9869 100755
--- a/BaseTools/BinWrappers/PosixLike/GenCrc32
+++ b/BaseTools/BinWrappers/PosixLike/GenCrc32
@@ -1,29 +1,14 @@
-#!/usr/bin/env bash
-
-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")
-cmd=${full_cmd##*/}
-
-if [ -n "$WORKSPACE" ] && [ -e "$WORKSPACE/Conf/BaseToolsCBinaries" ]
-then
-  exec "$WORKSPACE/Conf/BaseToolsCBinaries/$cmd"
-elif [ -n "$WORKSPACE" ] && [ -e "$EDK_TOOLS_PATH/Source/C" ]
-then
-  if [ ! -e "$EDK_TOOLS_PATH/Source/C/bin/$cmd" ]
-  then
-    echo "BaseTools C Tool binary was not found ($cmd)"
-    echo "You may need to run:"
-    echo "  make -C $EDK_TOOLS_PATH/Source/C"
-  else
-    exec "$EDK_TOOLS_PATH/Source/C/bin/$cmd" "$@"
-  fi
-elif [ -e "$dir/../../Source/C/bin/$cmd" ]
-then
-  exec "$dir/../../Source/C/bin/$cmd" "$@"
-else
-  echo "Unable to find the real '$cmd' to run"
-  echo "This message was printed by"
-  echo "  $0"
-  exit 127
-fi
-
+#!/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")
+cmd=${full_cmd##*/}
+
+export PYTHONPATH="$dir/../../Source/Python${PYTHONPATH:+:"$PYTHONPATH"}"
+exec "${python_exe:-python}" -m $cmd.$cmd "$@"
diff --git a/BaseTools/BinWrappers/WindowsLike/GenCrc32.bat b/BaseTools/BinWrappers/WindowsLike/GenCrc32.bat
new file mode 100644
index 0000000000..f43dba81f1
--- /dev/null
+++ b/BaseTools/BinWrappers/WindowsLike/GenCrc32.bat
@@ -0,0 +1,4 @@
+@setlocal
+@set ToolName=%~n0%
+@set PYTHONPATH=%PYTHONPATH%;%BASE_TOOLS_PATH%\Source\Python
+@%PYTHON_COMMAND% -m %ToolName%.%ToolName% %*
diff --git a/BaseTools/Source/Python/GenCrc32/GenCrc32.py b/BaseTools/Source/Python/GenCrc32/GenCrc32.py
new file mode 100644
index 0000000000..ef1f2a5b0a
--- /dev/null
+++ b/BaseTools/Source/Python/GenCrc32/GenCrc32.py
@@ -0,0 +1,185 @@
+#
+# @file
+# Calculate Crc32 value and Verify Crc32 value for input data.
+#
+# Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+
+import sys
+import argparse
+import zlib
+
+__prog__ = 'GenCrc32'
+__description__ = 'Tool has two modes of operation: Encode and Decode. \
+                  Encode calculates CRC32 of a file and appends checksum \
+                  to the beginning of file. Decode checks stored CRC32 \
+                  and removes it from file.'
+__version__ = '0.3'
+
+MSG_INFO = 0
+MSG_ERROR = 1
+MSG_DEBUG = 2
+
+STATUS_SUCCESS = 0
+STATUS_ERROR = 2
+
+
+def log(args, message_type, message):
+    if (((args.verbose and
+        (message_type == MSG_INFO or
+         message_type == MSG_ERROR)) or
+        (args.debug and
+        (message_type == MSG_DEBUG or
+         message_type == MSG_INFO or
+         message_type == MSG_ERROR))) and
+            not args.quiet):
+        print("%s: %s" % (__prog__, message))
+
+
+# replacement for int.from_bytes()
+# for compatability with python 2
+def uint32_from_bytes(data):
+    barray = bytearray(data)
+    return (
+        (barray[3] << 24) |
+        (barray[2] << 16) |
+        (barray[1] << 8) |
+        (barray[0] << 0))
+
+
+# replacement for int.to_bytes()
+# for compatability with python 2
+def uint32_to_bytes(uint):
+    return bytearray([
+            (uint >> 0) & 0xff,
+            (uint >> 8) & 0xff,
+            (uint >> 16) & 0xff,
+            (uint >> 24) & 0xff])
+
+
+def parse_args():
+    parser = argparse.ArgumentParser(prog=__prog__,
+                                     description=__description__,
+                                     conflict_handler='resolve')
+    parser.add_argument('--version',
+                        action='version',
+                        version=__version__,
+                        help='Show version number and exit')
+    parser.add_argument('--debug',
+                        type=int,
+                        metavar='[0-9]',
+                        choices=range(0, 10),
+                        default=0,
+                        help='Output DEBUG statements')
+    logging = parser.add_mutually_exclusive_group(required=False)
+    logging.add_argument('-v',
+                         '--verbose',
+                         action='store_true',
+                         help='Print informational statements')
+    logging.add_argument('-q',
+                         '--quiet',
+                         action='store_true',
+                         help='Returns the exit code, \
+                         error messages will be displayed')
+    operation = parser.add_mutually_exclusive_group(required=True)
+    operation.add_argument('-e',
+                           '--encode',
+                           action='store_true',
+                           help='Calculate CRC32 value for the input file')
+    operation.add_argument('-d',
+                           '--decode',
+                           action='store_true',
+                           help='Verify CRC32 value for the input file')
+    parser.add_argument('-o',
+                        '--output',
+                        dest='output_file',
+                        type=str,
+                        help='Output file name',
+                        required=True)
+    parser.add_argument(dest='input_file',
+                        type=argparse.FileType('rb'),
+                        help='Input file name')
+    return parser.parse_args()
+
+
+def main():
+    args = parse_args()
+    log(args, MSG_DEBUG, "Input file name: %s" % args.input_file.name)
+    log(args, MSG_DEBUG, "Output file name: %s" % args.output_file)
+    if args.encode:
+        log(args, MSG_INFO, "Operation: Encode")
+    if args.decode:
+        log(args, MSG_INFO, "Operation: Decode")
+
+    # Reading input file to buffer
+    data = bytearray()
+    header = bytearray()
+    log(args, MSG_INFO, "Reading input file")
+    try:
+        if args.decode:
+            header = args.input_file.read(4)
+        data = args.input_file.read()
+        args.input_file.close()
+    except MemoryError as e:
+        log(args, MSG_ERROR, ("Error reading input file, "
+            "cannot allocate memory"))
+        sys.exit(STATUS_ERROR)
+    log(args, MSG_INFO, ("Input file length: %d bytes "
+        "(%d bytes crc32 header and %d bytes data)") %
+        ((len(header) + len(data)), len(header), len(data)))
+
+    # Calculating CRC32
+    log(args, MSG_INFO, "Calculating CRC32")
+    data_crc32 = zlib.crc32(data) & 0xffffffff
+    if args.decode:
+        if len(header) >= 4:
+            header_crc32 = uint32_from_bytes(header)
+        else:
+            log(args, MSG_ERROR, ("Input file size is smaller "
+                "than 4 bytes, invalid file format"))
+            sys.exit(STATUS_ERROR)
+        log(args, MSG_INFO, "CRC32 in input file header is %s" %
+            hex(header_crc32))
+    log(args, MSG_INFO, "CRC32 of input file data is %s" %
+        hex(data_crc32))
+
+    # Perform selected operation by modifying header/data
+
+    # Encoding
+    if args.encode:
+        log(args, MSG_DEBUG, "Encoding header")
+        header = uint32_to_bytes(data_crc32)
+
+    # Verification and decoding
+    if args.decode:
+        log(args, MSG_INFO, "Verifying checksum")
+        header = bytearray()
+        if data_crc32 == header_crc32:
+            log(args, MSG_INFO, "Checksum verification succeeded")
+        else:
+            log(args, MSG_ERROR, ("Checksum verification failed, "
+                "CRC32 mismatch"))
+            sys.exit(STATUS_ERROR)
+
+    # Write output file
+    log(args, MSG_INFO, "Writing output file")
+    try:
+        with open(args.output_file, 'wb') as output_file:
+            output_file.write(header)
+            output_file.write(data)
+    except OSError as e:
+        log(args, MSG_ERROR, "Error writing output file: %s",
+            format(e.errno, e.strerror))
+        sys.exit(STATUS_ERROR)
+    log(args, MSG_INFO, ("Output file length: %d bytes "
+        "(%d bytes crc32 header and %d bytes data)") %
+        ((len(header) + len(data)), len(header), len(data)))
+
+    # Finish
+    log(args, MSG_INFO, "Success")
+    sys.exit(STATUS_SUCCESS)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/BaseTools/Tests/PythonToolsTests.py b/BaseTools/Tests/PythonToolsTests.py
index 05b27ab033..4d0ae89508 100644
--- a/BaseTools/Tests/PythonToolsTests.py
+++ b/BaseTools/Tests/PythonToolsTests.py
@@ -20,6 +20,8 @@ def TheTestSuite():
     suites.append(CheckPythonSyntax.TheTestSuite())
     import CheckUnicodeSourceFiles
     suites.append(CheckUnicodeSourceFiles.TheTestSuite())
+    import TestGenCrc32
+    suites.append(TestGenCrc32.TheTestSuite())
     return unittest.TestSuite(suites)
 
 if __name__ == '__main__':
diff --git a/BaseTools/Tests/TestGenCrc32.py b/BaseTools/Tests/TestGenCrc32.py
new file mode 100644
index 0000000000..937d0b14d5
--- /dev/null
+++ b/BaseTools/Tests/TestGenCrc32.py
@@ -0,0 +1,64 @@
+# @file
+# Unit tests for GenCrc32 utility
+#
+#  Copyright (c) 2008, Intel Corporation. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+
+import unittest
+import TestTools
+
+
+class Tests(TestTools.BaseToolsTest):
+
+    def setUp(self):
+        TestTools.BaseToolsTest.setUp(self)
+        self.toolName = 'GenCrc32'
+
+    def test_display_help(self):
+        result = self.RunTool(
+            '--help',
+            logFile='help'
+            )
+        self.assertTrue(result == 0)
+
+    def do_encode_decode(self, data):
+        path = self.GetTmpFilePath('input')
+        self.WriteTmpFile('input', data)
+        result = self.RunTool(
+            '--verbose',
+            '-e',
+            '-o', self.GetTmpFilePath('output1'),
+            self.GetTmpFilePath('input'),
+            )
+        self.assertTrue(result == 0)
+        result = self.RunTool(
+            '-d',
+            '-o', self.GetTmpFilePath('output2'),
+            self.GetTmpFilePath('output1')
+            )
+        self.assertTrue(result == 0)
+        start = self.ReadTmpFile('input')
+        finish = self.ReadTmpFile('output2')
+        start_equals_finish = start == finish
+        if not start_equals_finish:
+            print('Original data did not match decode(encode(data))')
+            self.DisplayBinaryData('original data', start)
+            self.DisplayBinaryData('after encoding',
+                                   self.ReadTmpFile('output1'))
+            self.DisplayBinaryData('after decoding', finish)
+        self.assertTrue(start_equals_finish)
+
+    def test_encode_decode_various_sizes(self):
+        for i in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 1000]:
+            data = self.GetRandomString(i, i)
+            self.do_encode_decode(data)
+            self.CleanUpTmpDir()
+
+
+TheTestSuite = TestTools.MakeTheTestSuite(locals())
+
+if __name__ == '__main__':
+    allTests = TheTestSuite()
+    unittest.TextTestRunner().run(allTests)
-- 
2.25.1


--------------------------------------------------------------------
Joint Stock Company Intel A/O
Registered legal address: Krylatsky Hills Business Park,
17 Krylatskaya Str., Bldg 4, Moscow 121614,
Russian Federation

This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.


                 reply	other threads:[~2022-04-05 20:00 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=86c94d6a11c45be6173bfd185d87158e08569746.1649188308.git.artem.khakimov@intel.com \
    --to=devel@edk2.groups.io \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox