1#!/usr/bin/env python
2
3"""
4Implements the "make clean" target
5
6(C) 2017 Jack Lloyd
7
8Botan is released under the Simplified BSD License (see license.txt)
9"""
10
11import os
12import sys
13import stat
14import re
15import optparse  # pylint: disable=deprecated-module
16import logging
17import json
18import shutil
19import errno
20
21def remove_dir(d):
22    try:
23        if os.access(d, os.X_OK):
24            logging.debug('Removing directory "%s"', d)
25            shutil.rmtree(d)
26        else:
27            logging.debug('Directory %s was missing', d)
28    except Exception as e: # pylint: disable=broad-except
29        logging.error('Failed removing directory "%s": %s', d, e)
30
31def remove_file(f):
32    try:
33        logging.debug('Removing file "%s"', f)
34        os.unlink(f)
35    except OSError as e:
36        if e.errno != errno.ENOENT:
37            logging.error('Failed removing file "%s": %s', f, e)
38
39def remove_all_in_dir(d):
40    if os.access(d, os.X_OK):
41        logging.debug('Removing all files in directory "%s"', d)
42
43        for f in os.listdir(d):
44            full_path = os.path.join(d, f)
45            mode = os.lstat(full_path).st_mode
46
47            if stat.S_ISDIR(mode):
48                remove_dir(full_path)
49            else:
50                remove_file(full_path)
51
52def parse_options(args):
53    parser = optparse.OptionParser()
54    parser.add_option('--build-dir', default='build', metavar='DIR',
55                      help='specify build dir to clean (default %default)')
56
57    parser.add_option('--distclean', action='store_true', default=False,
58                      help='clean everything')
59    parser.add_option('--verbose', action='store_true', default=False,
60                      help='noisy logging')
61
62    (options, args) = parser.parse_args(args)
63
64    if len(args) > 1:
65        raise Exception("Unknown arguments")
66
67    return options
68
69def main(args=None):
70    if args is None:
71        args = sys.argv
72
73    options = parse_options(args)
74
75    logging.basicConfig(stream=sys.stderr,
76                        format='%(levelname) 7s: %(message)s',
77                        level=logging.DEBUG if options.verbose else logging.INFO)
78
79    build_dir = options.build_dir
80
81    if os.access(build_dir, os.X_OK) != True:
82        logging.debug('No build directory found')
83        # No build dir: clean enough!
84        return 0
85
86    build_config_path = os.path.join(build_dir, 'build_config.json')
87    build_config_str = None
88
89    try:
90        build_config_file = open(build_config_path)
91        build_config_str = build_config_file.read()
92        build_config_file.close()
93    except Exception: # pylint: disable=broad-except
94        # Ugh have to do generic catch as different exception type thrown in Python2
95        logging.error("Unable to access build_config.json in build dir")
96        return 1
97
98    build_config = json.loads(build_config_str)
99
100    if options.distclean:
101        build_dir = build_config['build_dir']
102        remove_file(build_config['makefile_path'])
103        remove_dir(build_dir)
104    else:
105        for dir_type in ['libobj_dir', 'cliobj_dir', 'testobj_dir', 'depsobj_dir']:
106            dir_path = build_config[dir_type]
107            if dir_path:
108                remove_all_in_dir(dir_path)
109
110    remove_file(build_config['cli_exe'])
111    remove_file(build_config['test_exe'])
112
113    if options.distclean:
114        if 'generated_files' in build_config:
115            for f in build_config['generated_files'].split(' '):
116                remove_file(f)
117
118    return 0
119
120if __name__ == '__main__':
121    sys.exit(main())
122