1#!/usr/bin/python
2import common
3import sys, os, shutil, optparse, logging
4from autotest_lib.client.common_lib import error, utils
5from autotest_lib.client.common_lib import logging_config, logging_manager
6"""
7Compile All Autotest GWT Clients Living in autotest/frontend/client/src
8"""
9
10_AUTOTEST_DIR = common.autotest_dir
11_DEFAULT_GWT_DIRS = ['/usr/local/lib/gwt', '/opt/google-web-toolkit']
12_DEFAULT_APP_DIR = os.path.join(_AUTOTEST_DIR, 'frontend/client')
13_DEFAULT_INSTALL_DIR = os.path.join(_DEFAULT_APP_DIR, 'www')
14_TMP_COMPILE_DIR = _DEFAULT_INSTALL_DIR + '.new'
15
16_COMPILE_LINE = ('java  -Xmx512M %(extra_args)s '
17                 '-cp "%(app_dir)s/src:%(app_dir)s/bin:%(gwt_dir)s/gwt-user.jar'
18                 ':%(gwt_dir)s/gwt-dev.jar" -Djava.awt.headless=true '
19                 'com.google.gwt.dev.Compiler -war "%(compile_dir)s" '
20                 '%(project_client)s')
21
22class CompileClientsLoggingConfig(logging_config.LoggingConfig):
23    def configure_logging(self, results_dir=None, verbose=False):
24        super(CompileClientsLoggingConfig, self).configure_logging(
25                                                               use_console=True,
26                                                               verbose=verbose)
27
28def enumerate_projects():
29    """List projects in _DEFAULT_APP_DIR."""
30    src_path = os.path.join(_DEFAULT_APP_DIR, 'src')
31    projects = {}
32    for project in os.listdir(src_path):
33        projects[project] = []
34        project_path = os.path.join(src_path, project)
35        for file in os.listdir(project_path):
36            if file.endswith('.gwt.xml'):
37                projects[project].append(file[:-8])
38    return projects
39
40
41def find_gwt_dir():
42    """See if GWT is installed in site-packages or in the system,
43       site-packages is favored over a system install or a /usr/local install.
44    """
45    site_gwt = os.path.join(_AUTOTEST_DIR, 'site-packages', 'gwt')
46    gwt_dirs = [site_gwt] + _DEFAULT_GWT_DIRS
47
48    for gwt_dir in gwt_dirs:
49        if os.path.isdir(gwt_dir):
50            return gwt_dir
51    logging.error('Unable to find GWT. '
52                  'You can use utils/build_externals.py to install it.')
53    sys.exit(1)
54
55
56def install_completed_client(compiled_dir, project_client):
57    """Remove old client directory if it exists,  move installed client to the
58       old directory and move newly compield client to the installed client
59       dir.
60       @param compiled_dir: Where the new client was compiled
61       @param project_client: project.client pair e.g. autotest.AfeClient
62       @returns True if installation was successful or False if it failed
63    """
64    tmp_client_dir = os.path.join(_TMP_COMPILE_DIR, project_client)
65    install_dir = os.path.join(_DEFAULT_INSTALL_DIR, project_client)
66    old_install_dir = os.path.join(_DEFAULT_INSTALL_DIR,
67                                   project_client + '.old')
68    if not os.path.exists(_DEFAULT_INSTALL_DIR):
69        os.mkdir(_DEFAULT_INSTALL_DIR)
70
71    if os.path.isdir(tmp_client_dir):
72        if os.path.isdir(old_install_dir):
73            shutil.rmtree(old_install_dir)
74        if os.path.isdir(install_dir):
75            os.rename(install_dir, old_install_dir)
76        try:
77            os.rename(tmp_client_dir, install_dir)
78            return True
79        except Exception, err:
80            # If we can't rename the client raise an exception
81            # and put the old client back
82            shutil.rmtree(install_dir)
83            shutil.copytree(old_install_dir, install_dir)
84            logging.error('Copying old client: %s', err)
85    else:
86        logging.error('Compiled directory is gone, something went wrong')
87
88    return False
89
90
91def compile_and_install_client(project_client, extra_args='',
92                               install_client=True):
93    """Compile the client into a temporary directory, if successful
94       call install_completed_client to install the new client.
95       @param project_client: project.client pair e.g. autotest.AfeClient
96       @param install_client: Boolean, if True install the clients
97       @return True if install and compile was successful False if it failed
98    """
99    java_args = {}
100    java_args['compile_dir'] = _TMP_COMPILE_DIR
101    java_args['app_dir'] = _DEFAULT_APP_DIR
102    java_args['gwt_dir'] = find_gwt_dir()
103    java_args['extra_args'] = extra_args
104    java_args['project_client'] = project_client
105    cmd = _COMPILE_LINE % java_args
106
107    logging.info('Compiling client %s', project_client)
108    try:
109        utils.run(cmd, verbose=True)
110        if install_client:
111            return install_completed_client(java_args['compile_dir'],
112                                            project_client)
113        return True
114    except error.CmdError:
115        logging.error('Error compiling %s, leaving old client', project_client)
116
117    return False
118
119
120def compile_all_projects(extra_args=''):
121    """Compile all projects available as defined by enumerate_projects.
122       @returns list of failed client installations
123    """
124    failed_clients = []
125    for project,clients in enumerate_projects().iteritems():
126        for client in clients:
127            project_client = '%s.%s' % (project, client)
128            if not compile_and_install_client(project_client, extra_args):
129                failed_clients.append(project_client)
130
131    return failed_clients
132
133
134def print_projects():
135    logging.info('Projects that can be compiled:')
136    for project,clients in enumerate_projects().iteritems():
137        for client in clients:
138            logging.info('%s.%s', project, client)
139
140
141def main():
142    logging_manager.configure_logging(CompileClientsLoggingConfig(),
143                                      verbose=True)
144    parser = optparse.OptionParser()
145    parser.add_option('-l', '--list-projects',
146                      action='store_true', dest='list_projects',
147                      default=False,
148                      help='List all projects and clients that can be compiled')
149    parser.add_option('-a', '--compile-all',
150                      action='store_true', dest='compile_all',
151                     default=False,
152                     help='Compile all available projects and clients')
153    parser.add_option('-c', '--compile',
154                      dest='compile_list', action='store',
155                      help='List of clients to compiled (e.g. -c "x.X c.C")')
156    parser.add_option('-e', '--extra-args',
157                      dest='extra_args', action='store',
158                      default='',
159                      help='Extra arguments to pass to java')
160    parser.add_option('-d', '--no-install', dest='install_client',
161                      action='store_false', default=True,
162                      help='Do not install the clients just compile them')
163    options, args = parser.parse_args()
164
165    if len(sys.argv) < 2:
166        parser.print_help()
167        sys.exit(0)
168    elif options.list_projects:
169        print_projects()
170        sys.exit(0)
171    elif options.compile_all and options.compile_list:
172        logging.error('Options -c and -a are mutually exclusive')
173        parser.print_help()
174        sys.exit(1)
175
176    failed_clients = []
177    if options.compile_all:
178        failed_clients = compile_all_projects(options.extra_args)
179    elif options.compile_list:
180        for client in options.compile_list.split():
181            if not compile_and_install_client(client, options.extra_args,
182                                              options.install_client):
183                failed_clients.append(client)
184
185    if os.path.exists(_TMP_COMPILE_DIR):
186        shutil.rmtree(_TMP_COMPILE_DIR)
187
188    if failed_clients:
189        logging.error('The following clients failed: %s',
190                      '\n'.join(failed_clients))
191        sys.exit(1)
192
193
194if __name__ == '__main__':
195    main()
196