1#!/usr/bin/python2 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