summaryrefslogtreecommitdiffstats
path: root/qemu/roms/seabios/scripts/buildversion.py
blob: 46928984e4d4a519e5860951765bdc1fedad74d2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#!/usr/bin/env python
# Generate version information for a program
#
# Copyright (C) 2015  Kevin O'Connor <kevin@koconnor.net>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import sys, os, subprocess, shlex, time, socket, optparse, logging, traceback

VERSION_FORMAT = """
/* DO NOT EDIT!  This is an autogenerated file.  See scripts/buildversion.py. */
#define BUILD_VERSION "%s"
#define BUILD_TOOLS "%s"
"""

# Run program and return the specified output
def check_output(prog):
    logging.debug("Running %s" % (repr(prog),))
    try:
        process = subprocess.Popen(shlex.split(prog), stdout=subprocess.PIPE)
        output = process.communicate()[0]
        retcode = process.poll()
    except OSError:
        logging.debug("Exception on run: %s" % (traceback.format_exc(),))
        return ""
    logging.debug("Got (code=%s): %s" % (retcode, repr(output)))
    if retcode:
        return ""
    try:
        return output.decode()
    except UnicodeError:
        logging.debug("Exception on decode: %s" % (traceback.format_exc(),))
        return ""

# Obtain version info from "git" program
def git_version():
    if not os.path.exists('.git'):
        logging.debug("No '.git' file/directory found")
        return ""
    ver = check_output("git describe --tags --long --dirty").strip()
    logging.debug("Got git version: %s" % (repr(ver),))
    return ver

# Look for version in a ".version" file.  Official release tarballs
# have this file (see scripts/tarball.sh).
def file_version():
    if not os.path.isfile('.version'):
        logging.debug("No '.version' file found")
        return ""
    try:
        f = open('.version', 'r')
        ver = f.readline().strip()
        f.close()
    except OSError:
        logging.debug("Exception on read: %s" % (traceback.format_exc(),))
        return ""
    logging.debug("Got .version: %s" % (repr(ver),))
    return ver

# Generate an output file with the version information
def write_version(outfile, version, toolstr):
    logging.debug("Write file %s and %s" % (repr(version), repr(toolstr)))
    sys.stdout.write("Version: %s\n" % (version,))
    f = open(outfile, 'w')
    f.write(VERSION_FORMAT % (version, toolstr))
    f.close()

# Run "tool --version" for each specified tool and extract versions
def tool_versions(tools):
    tools = [t.strip() for t in tools.split(';')]
    versions = ['', '']
    success = 0
    for tool in tools:
        # Extract first line from "tool --version" output
        verstr = check_output("%s --version" % (tool,)).split('\n')[0]
        # Check if this tool looks like a binutils program
        isbinutils = 0
        if verstr.startswith('GNU '):
            isbinutils = 1
            verstr = verstr[4:]
        # Extract version information and exclude program name
        if ' ' not in verstr:
            continue
        prog, ver = verstr.split(' ', 1)
        if not prog or not ver:
            continue
        # Check for any version conflicts
        if versions[isbinutils] and versions[isbinutils] != ver:
            logging.debug("Mixed version %s vs %s" % (
                repr(versions[isbinutils]), repr(ver)))
            versions[isbinutils] = "mixed"
            continue
        versions[isbinutils] = ver
        success += 1
    cleanbuild = versions[0] and versions[1] and success == len(tools)
    return cleanbuild, "gcc: %s binutils: %s" % (versions[0], versions[1])

def main():
    usage = "%prog [options] <outputheader.h>"
    opts = optparse.OptionParser(usage)
    opts.add_option("-e", "--extra", dest="extra", default="",
                    help="extra version string to append to version")
    opts.add_option("-t", "--tools", dest="tools", default="",
                    help="list of build programs to extract version from")
    opts.add_option("-v", action="store_true", dest="verbose",
                    help="enable debug messages")

    options, args = opts.parse_args()
    if len(args) != 1:
        opts.error("Incorrect arguments")
    outfile = args[0]
    if options.verbose:
        logging.basicConfig(level=logging.DEBUG)

    cleanbuild, toolstr = tool_versions(options.tools)

    ver = git_version()
    cleanbuild = cleanbuild and 'dirty' not in ver
    if not ver:
        ver = file_version()
        # We expect the "extra version" to contain information on the
        # distributor and distribution package version (if
        # applicable).  It is a "clean" build if this is a build from
        # an official release tarball and the above info is present.
        cleanbuild = cleanbuild and ver and options.extra != ""
        if not ver:
            ver = "?"
    if not cleanbuild:
        btime = time.strftime("%Y%m%d_%H%M%S")
        hostname = socket.gethostname()
        ver = "%s-%s-%s" % (ver, btime, hostname)
    write_version(outfile, ver + options.extra, toolstr)

if __name__ == '__main__':
    main()