1#!/usr/bin/python
2
3# Copyright 2017 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""Command line tool to pack audio related modules into a zip file."""
8
9import argparse
10import logging
11import os
12import subprocess
13
14
15MODULES = ['audio_quality_measurement.py', 'audio_data.py', 'audio_analysis.py']
16ENTRY = '__main__.py'
17ENTRY_TARGET = 'check_quality.py'
18
19def add_args(parser):
20    """Adds command line arguments."""
21    parser.add_argument('-d', '--debug', action='store_true', default=False,
22                        help='Show debug message.')
23    parser.add_argument('-o', '--out', type=str, default='audio_quality.zip',
24                        help='Output file name. Default is audio_quality.zip.')
25    parser.add_argument('-s', '--skip-cleanup', action='store_true', default=False,
26                        help='Skip cleaning up temporary files. Default is False')
27
28
29def parse_args(parser):
30    """Parses args.
31
32    @param parser: An argparse.ArgumentParser.
33
34    @returns: The namespace parsed from command line arguments.
35
36    """
37    args = parser.parse_args()
38    return args
39
40
41def create_link():
42    """Creates a symbolic link from ENTRY to ENTRY_TARGET.
43
44    With this symlink, python can execute the zip file directly to execute
45    ENTRY_TARGET.
46
47    """
48    command = ['ln', '-sf', ENTRY_TARGET, ENTRY]
49    logging.debug('Link command: %s', command)
50    subprocess.check_call(command)
51
52
53def pack_files(out_file):
54    """Packs audio related modules into a zip file.
55
56    Packs audio related modules in MODULES into a zip file.
57    Packs the symlink pointing to ENTRY_TARGET.
58
59    @param out_file: Zip file name.
60
61    """
62    command = ['zip']
63    command.append(out_file)
64    command += MODULES
65    command.append(ENTRY)
66    command.append(ENTRY_TARGET)
67    logging.debug('Zip command: %s', command)
68    subprocess.check_call(command)
69
70
71def check_packed_file(out_file):
72    """Checks the packed file can be executed by python.
73
74    @param out_file: Zip file name.
75
76    """
77    command = ['python', out_file, '--help']
78    logging.debug('Check command: %s', command)
79    output = subprocess.check_output(command)
80    logging.debug('output: %s', output)
81
82
83def cleanup():
84    """Cleans up the symobolic link."""
85    if os.path.exists(ENTRY):
86        os.unlink(ENTRY)
87
88
89def repo_is_dirty():
90    """Checks if a repo is dirty by git diff command.
91
92    @returns: True if there are uncommitted changes. False otherwise.
93
94    """
95    try:
96        subprocess.check_call(['git', 'diff', '--quiet'])
97        subprocess.check_call(['git', 'diff', '--cached', '--quiet'])
98    except subprocess.CalledProcessError:
99        return True
100    return False
101
102
103def get_git_sha1():
104    """Returns git SHA-1 hash of HEAD.
105
106    @returns: git SHA-1 has of HEAD with minimum length 9.
107
108    """
109    return subprocess.check_output(
110            ['git', 'rev-parse', '--short', 'HEAD']).strip()
111
112
113def append_name_with_git_hash(out_file):
114    """Append the file with git SHA-1 hash.
115
116    For out_file like ABC.xyz, append the name ABC with git SHA-1 of HEAD, like
117    ABC_f4610bdd3.xyz.
118    If current repo contains uncommitted changes, it will be
119    ABC_f4610bdd3_dirty.xyz.
120
121    """
122    basename, ext = os.path.splitext(out_file)
123    basename += '_'
124    basename += get_git_sha1()
125    if repo_is_dirty():
126        basename += '_dirty'
127    return basename + ext
128
129
130if __name__ == '__main__':
131    parser = argparse.ArgumentParser(
132        description='Pack audio related modules into a zip file.')
133
134    add_args(parser)
135    args = parse_args(parser)
136
137    level = logging.DEBUG if args.debug else logging.INFO
138    format = '%(asctime)-15s:%(levelname)s:%(pathname)s:%(lineno)d: %(message)s'
139    logging.basicConfig(format=format, level=level)
140
141    out_file = append_name_with_git_hash(args.out)
142
143    try:
144        create_link()
145        pack_files(out_file)
146        check_packed_file(out_file)
147    finally:
148        if not args.skip_cleanup:
149            cleanup()
150
151    logging.info('Packed file: %s', out_file)
152