Boot: Add version number from command line

- Add an optional command line argument that can be used to set an
image's version number at build-time (using
-DIMAGE_VERSION=maj.min.rev+build) so that the Cmake command within
bl2/ext/mcuboot/MCUBoot.cmake doesn't need to be edited each time the
specified version number needs to be changed
- If the version number is not defined in the command line, use a
default value and automatically increment it in the next build

Signed-off-by: Oliver Swede <oli.swede@arm.com>
Change-Id: I3ff44ffb8219d7ea1a441d918cceb525e7af60f2
diff --git a/bl2/ext/mcuboot/scripts/imgtool.py b/bl2/ext/mcuboot/scripts/imgtool.py
index 9420d2b..4c5b451 100644
--- a/bl2/ext/mcuboot/scripts/imgtool.py
+++ b/bl2/ext/mcuboot/scripts/imgtool.py
@@ -1,6 +1,7 @@
 #! /usr/bin/env python3
 #
 # Copyright 2017 Linaro Limited
+# Copyright (c) 2018, Arm Limited.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -14,12 +15,46 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import os
 import argparse
 from imgtool import keys
 from imgtool import image
 from imgtool import version
 import sys
 
+# Returns the last version number if present, or None if not
+def get_last_version(path):
+    if (os.path.isfile(path) == False): # Version file not present
+        return None
+    else: # Version file is present, check it has a valid number inside it
+        with open(path, "r") as oldFile:
+            fileContents = oldFile.read()
+            if version.version_re.match(fileContents): # number is valid
+                return version.decode_version(fileContents)
+            else:
+                return None
+
+def next_version_number(args, defaultVersion, path):
+    newVersion = None
+    if (version.compare(args.version, defaultVersion) == 0): # Default version
+        lastVersion = get_last_version(path)
+        if (lastVersion is not None):
+            newVersion = version.increment_build_num(lastVersion)
+        else:
+            newVersion = version.increment_build_num(defaultVersion)
+    else: # Version number has been explicitly provided (not using the default)
+        newVersion = args.version
+    versionString = "{a}.{b}.{c}+{d}".format(
+                    a=str(newVersion.major),
+                    b=str(newVersion.minor),
+                    c=str(newVersion.revision),
+                    d=str(newVersion.build)
+    )
+    with open(path, "w") as newFile:
+        newFile.write(versionString)
+    print("**[INFO]** Image version number set to " + versionString)
+    return newVersion
+
 def gen_rsa2048(args):
     keys.RSA2048.generate().export_private(args.key)
 
@@ -43,7 +78,10 @@
 def do_sign(args):
     if args.rsa_pkcs1_15:
         keys.sign_rsa_pss = False
-    img = image.Image.load(args.infile, version=args.version,
+    img = image.Image.load(args.infile,
+            version=next_version_number(args,
+                                        version.decode_version("0"),
+                                        "lastVerNum.txt"),
             header_size=args.header_size,
             included_header=args.included_header,
             pad=args.pad)
@@ -80,7 +118,7 @@
     keygenp = subs.add_parser('keygen', help='Generate pub/private keypair')
     keygenp.add_argument('-k', '--key', metavar='filename', required=True)
     keygenp.add_argument('-t', '--type', metavar='type',
-            choices=keygens.keys(), required=True)
+                         choices=keygens.keys(), required=True)
 
     getpub = subs.add_parser('getpub', help='Get public key from keypair')
     getpub.add_argument('-k', '--key', metavar='filename', required=True)
@@ -89,14 +127,16 @@
     sign = subs.add_parser('sign', help='Sign an image with a private key')
     sign.add_argument('-k', '--key', metavar='filename')
     sign.add_argument("--align", type=alignment_value, required=True)
-    sign.add_argument("-v", "--version", type=version.decode_version, required=True)
+    sign.add_argument("-v", "--version", type=version.decode_version,
+                      default="0.0.0+0")
     sign.add_argument("-H", "--header-size", type=intparse, required=True)
     sign.add_argument("--included-header", default=False, action='store_true',
-            help='Image has gap for header')
+                      help='Image has gap for header')
     sign.add_argument("--pad", type=intparse,
-            help='Pad image to this many bytes, adding trailer magic')
-    sign.add_argument("--rsa-pkcs1-15", help='Use old PKCS#1 v1.5 signature algorithm',
-            default=False, action='store_true')
+                      help='Pad image to this many bytes, adding trailer magic')
+    sign.add_argument("--rsa-pkcs1-15",
+                      help='Use old PKCS#1 v1.5 signature algorithm',
+                      default=False, action='store_true')
     sign.add_argument("infile")
     sign.add_argument("outfile")
 
@@ -108,4 +148,4 @@
     subcmds[args.subcmd](args)
 
 if __name__ == '__main__':
-    args()
+    args()
\ No newline at end of file
diff --git a/bl2/ext/mcuboot/scripts/imgtool/image.py b/bl2/ext/mcuboot/scripts/imgtool/image.py
index f8309b3..9731844 100644
--- a/bl2/ext/mcuboot/scripts/imgtool/image.py
+++ b/bl2/ext/mcuboot/scripts/imgtool/image.py
@@ -79,8 +79,8 @@
         obj.check()
         return obj
 
-    def __init__(self, version=None, header_size=IMAGE_HEADER_SIZE, pad=0):
-        self.version = version or versmod.decode_version("0")
+    def __init__(self, version, header_size=IMAGE_HEADER_SIZE, pad=0):
+        self.version = version
         self.header_size = header_size or IMAGE_HEADER_SIZE
         self.pad = pad
 
@@ -182,4 +182,4 @@
         pbytes  = b'\xff' * padding
         pbytes += b'\xff' * (tsize - len(boot_magic))
         pbytes += boot_magic
-        self.payload += pbytes
+        self.payload += pbytes
\ No newline at end of file
diff --git a/bl2/ext/mcuboot/scripts/imgtool/version.py b/bl2/ext/mcuboot/scripts/imgtool/version.py
index 64962e9..d1d45f0 100644
--- a/bl2/ext/mcuboot/scripts/imgtool/version.py
+++ b/bl2/ext/mcuboot/scripts/imgtool/version.py
@@ -1,4 +1,5 @@
 # Copyright 2017 Linaro Limited
+# Copyright (c) 2018, Arm Limited.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -24,11 +25,29 @@
 
 SemiSemVersion = namedtuple('SemiSemVersion', ['major', 'minor', 'revision', 'build'])
 
+def increment_build_num(lastVer):
+    newVer = SemiSemVersion(lastVer.major, lastVer.minor, lastVer.revision, lastVer.build + 1)
+    return newVer
+
+# -1 if a is older than b; 0 if they're the same version; 1 if a is newer than b
+def compare(a, b):
+    if (a.major > b.major): return 1
+    elif (a.major < b.major): return -1
+    else:
+        if (a.minor > b.minor): return 1
+        elif (a.minor < b.minor): return -1
+        else:
+            if (a.revision > b.revision): return 1
+            elif (a.revision < b.revision): return -1
+            else:
+                if (a.build > b.build): return 1
+                elif (a.build < b.build): return -1
+                else: return 0
+
 version_re = re.compile(r"""^([1-9]\d*|0)(\.([1-9]\d*|0)(\.([1-9]\d*|0)(\+([1-9]\d*|0))?)?)?$""")
 def decode_version(text):
     """Decode the version string, which should be of the form maj.min.rev+build"""
     m = version_re.match(text)
-    # print("decode:", text, m.groups())
     if m:
         result = SemiSemVersion(
                 int(m.group(1)) if m.group(1) else 0,