17169aa30eeaac89c49984bb9d061ca152d43391aBill Napier/*
27169aa30eeaac89c49984bb9d061ca152d43391aBill Napier * Copyright (C) 2010 The Android Open Source Project
37169aa30eeaac89c49984bb9d061ca152d43391aBill Napier *
47169aa30eeaac89c49984bb9d061ca152d43391aBill Napier * Licensed under the Apache License, Version 2.0 (the "License");
57169aa30eeaac89c49984bb9d061ca152d43391aBill Napier * you may not use this file except in compliance with the License.
67169aa30eeaac89c49984bb9d061ca152d43391aBill Napier * You may obtain a copy of the License at
77169aa30eeaac89c49984bb9d061ca152d43391aBill Napier *
87169aa30eeaac89c49984bb9d061ca152d43391aBill Napier *      http://www.apache.org/licenses/LICENSE-2.0
97169aa30eeaac89c49984bb9d061ca152d43391aBill Napier *
107169aa30eeaac89c49984bb9d061ca152d43391aBill Napier * Unless required by applicable law or agreed to in writing, software
117169aa30eeaac89c49984bb9d061ca152d43391aBill Napier * distributed under the License is distributed on an "AS IS" BASIS,
127169aa30eeaac89c49984bb9d061ca152d43391aBill Napier * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
137169aa30eeaac89c49984bb9d061ca152d43391aBill Napier * See the License for the specific language governing permissions and
147169aa30eeaac89c49984bb9d061ca152d43391aBill Napier * limitations under the License.
157169aa30eeaac89c49984bb9d061ca152d43391aBill Napier */
167169aa30eeaac89c49984bb9d061ca152d43391aBill Napierpackage com.android.monkeyrunner;
177169aa30eeaac89c49984bb9d061ca152d43391aBill Napier
187169aa30eeaac89c49984bb9d061ca152d43391aBill Napierimport com.google.common.base.Predicate;
197169aa30eeaac89c49984bb9d061ca152d43391aBill Napierimport com.google.common.collect.ImmutableMap;
207169aa30eeaac89c49984bb9d061ca152d43391aBill Napierimport com.google.common.collect.ImmutableMap.Builder;
21d1efb3282762072d219285643792ff0119e3ed96Bill Napierimport com.google.common.collect.Lists;
227169aa30eeaac89c49984bb9d061ca152d43391aBill Napier
237169aa30eeaac89c49984bb9d061ca152d43391aBill Napierimport org.python.core.Py;
247169aa30eeaac89c49984bb9d061ca152d43391aBill Napierimport org.python.core.PyException;
25d1efb3282762072d219285643792ff0119e3ed96Bill Napierimport org.python.core.PyJavaPackage;
26d1efb3282762072d219285643792ff0119e3ed96Bill Napierimport org.python.core.PyList;
277169aa30eeaac89c49984bb9d061ca152d43391aBill Napierimport org.python.core.PyObject;
28d1efb3282762072d219285643792ff0119e3ed96Bill Napierimport org.python.core.PyString;
29d1efb3282762072d219285643792ff0119e3ed96Bill Napierimport org.python.core.PySystemState;
307169aa30eeaac89c49984bb9d061ca152d43391aBill Napierimport org.python.util.InteractiveConsole;
317169aa30eeaac89c49984bb9d061ca152d43391aBill Napierimport org.python.util.JLineConsole;
327169aa30eeaac89c49984bb9d061ca152d43391aBill Napierimport org.python.util.PythonInterpreter;
337169aa30eeaac89c49984bb9d061ca152d43391aBill Napier
347169aa30eeaac89c49984bb9d061ca152d43391aBill Napierimport java.io.File;
357169aa30eeaac89c49984bb9d061ca152d43391aBill Napierimport java.util.Arrays;
367169aa30eeaac89c49984bb9d061ca152d43391aBill Napierimport java.util.Collection;
377169aa30eeaac89c49984bb9d061ca152d43391aBill Napierimport java.util.Collections;
387169aa30eeaac89c49984bb9d061ca152d43391aBill Napierimport java.util.List;
397169aa30eeaac89c49984bb9d061ca152d43391aBill Napierimport java.util.Map;
407169aa30eeaac89c49984bb9d061ca152d43391aBill Napierimport java.util.Properties;
417169aa30eeaac89c49984bb9d061ca152d43391aBill Napierimport java.util.logging.Level;
427169aa30eeaac89c49984bb9d061ca152d43391aBill Napierimport java.util.logging.Logger;
437169aa30eeaac89c49984bb9d061ca152d43391aBill Napier
447169aa30eeaac89c49984bb9d061ca152d43391aBill Napier
457169aa30eeaac89c49984bb9d061ca152d43391aBill Napier/**
467169aa30eeaac89c49984bb9d061ca152d43391aBill Napier * Runs Jython based scripts.
477169aa30eeaac89c49984bb9d061ca152d43391aBill Napier */
487169aa30eeaac89c49984bb9d061ca152d43391aBill Napierpublic class ScriptRunner {
497169aa30eeaac89c49984bb9d061ca152d43391aBill Napier    private static final Logger LOG = Logger.getLogger(MonkeyRunnerOptions.class.getName());
507169aa30eeaac89c49984bb9d061ca152d43391aBill Napier
517169aa30eeaac89c49984bb9d061ca152d43391aBill Napier    /** The "this" scope object for scripts. */
527169aa30eeaac89c49984bb9d061ca152d43391aBill Napier    private final Object scope;
537169aa30eeaac89c49984bb9d061ca152d43391aBill Napier    private final String variable;
547169aa30eeaac89c49984bb9d061ca152d43391aBill Napier
557169aa30eeaac89c49984bb9d061ca152d43391aBill Napier    /** Private constructor. */
567169aa30eeaac89c49984bb9d061ca152d43391aBill Napier    private ScriptRunner(Object scope, String variable) {
577169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        this.scope = scope;
587169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        this.variable = variable;
597169aa30eeaac89c49984bb9d061ca152d43391aBill Napier    }
607169aa30eeaac89c49984bb9d061ca152d43391aBill Napier
617169aa30eeaac89c49984bb9d061ca152d43391aBill Napier    /** Creates a new instance for the given scope object. */
627169aa30eeaac89c49984bb9d061ca152d43391aBill Napier    public static ScriptRunner newInstance(Object scope, String variable) {
637169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        return new ScriptRunner(scope, variable);
647169aa30eeaac89c49984bb9d061ca152d43391aBill Napier    }
657169aa30eeaac89c49984bb9d061ca152d43391aBill Napier
667169aa30eeaac89c49984bb9d061ca152d43391aBill Napier    /**
677169aa30eeaac89c49984bb9d061ca152d43391aBill Napier     * Runs the specified Jython script. First runs the initialization script to
687169aa30eeaac89c49984bb9d061ca152d43391aBill Napier     * preload the appropriate client library version.
697169aa30eeaac89c49984bb9d061ca152d43391aBill Napier     *
707169aa30eeaac89c49984bb9d061ca152d43391aBill Napier     * @param scriptfilename the name of the file to run.
717169aa30eeaac89c49984bb9d061ca152d43391aBill Napier     * @param args the arguments passed in (excluding the filename).
727169aa30eeaac89c49984bb9d061ca152d43391aBill Napier     * @param plugins a list of plugins to load.
737169aa30eeaac89c49984bb9d061ca152d43391aBill Napier     * @return the error code from running the script.
747169aa30eeaac89c49984bb9d061ca152d43391aBill Napier     */
755026cf75831b905d5002709abbd4b5a592628ddaBill Napier    public static int run(String executablePath, String scriptfilename,
765026cf75831b905d5002709abbd4b5a592628ddaBill Napier            Collection<String> args, Map<String,
775026cf75831b905d5002709abbd4b5a592628ddaBill Napier            Predicate<PythonInterpreter>> plugins) {
787169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        // Add the current directory of the script to the python.path search path.
797169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        File f = new File(scriptfilename);
807169aa30eeaac89c49984bb9d061ca152d43391aBill Napier
817169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        // Adjust the classpath so jython can access the classes in the specified classpath.
827169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        Collection<String> classpath = Lists.newArrayList(f.getParent());
837169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        classpath.addAll(plugins.keySet());
847169aa30eeaac89c49984bb9d061ca152d43391aBill Napier
857169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        String[] argv = new String[args.size() + 1];
867169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        argv[0] = f.getAbsolutePath();
877169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        int x = 1;
887169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        for (String arg : args) {
897169aa30eeaac89c49984bb9d061ca152d43391aBill Napier            argv[x++] = arg;
907169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        }
917169aa30eeaac89c49984bb9d061ca152d43391aBill Napier
925026cf75831b905d5002709abbd4b5a592628ddaBill Napier        initPython(executablePath, classpath, argv);
937169aa30eeaac89c49984bb9d061ca152d43391aBill Napier
947169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        PythonInterpreter python = new PythonInterpreter();
957169aa30eeaac89c49984bb9d061ca152d43391aBill Napier
967169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        // Now let the mains run.
977169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        for (Map.Entry<String, Predicate<PythonInterpreter>> entry : plugins.entrySet()) {
987169aa30eeaac89c49984bb9d061ca152d43391aBill Napier            boolean success;
997169aa30eeaac89c49984bb9d061ca152d43391aBill Napier            try {
1007169aa30eeaac89c49984bb9d061ca152d43391aBill Napier                success = entry.getValue().apply(python);
1017169aa30eeaac89c49984bb9d061ca152d43391aBill Napier            } catch (Exception e) {
1027169aa30eeaac89c49984bb9d061ca152d43391aBill Napier                LOG.log(Level.SEVERE, "Plugin Main through an exception.", e);
1037169aa30eeaac89c49984bb9d061ca152d43391aBill Napier                continue;
1047169aa30eeaac89c49984bb9d061ca152d43391aBill Napier            }
1057169aa30eeaac89c49984bb9d061ca152d43391aBill Napier            if (!success) {
1067169aa30eeaac89c49984bb9d061ca152d43391aBill Napier                LOG.severe("Plugin Main returned error for: " + entry.getKey());
1077169aa30eeaac89c49984bb9d061ca152d43391aBill Napier            }
1087169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        }
1097169aa30eeaac89c49984bb9d061ca152d43391aBill Napier
1107169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        // Bind __name__ to __main__ so mains will run
1117169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        python.set("__name__", "__main__");
112d1efb3282762072d219285643792ff0119e3ed96Bill Napier        // Also find __file__
113d1efb3282762072d219285643792ff0119e3ed96Bill Napier        python.set("__file__", scriptfilename);
1147169aa30eeaac89c49984bb9d061ca152d43391aBill Napier
1157169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        try {
1167169aa30eeaac89c49984bb9d061ca152d43391aBill Napier          python.execfile(scriptfilename);
1177169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        } catch (PyException e) {
1187169aa30eeaac89c49984bb9d061ca152d43391aBill Napier          if (Py.SystemExit.equals(e.type)) {
1197169aa30eeaac89c49984bb9d061ca152d43391aBill Napier            // Then recover the error code so we can pass it on
1207169aa30eeaac89c49984bb9d061ca152d43391aBill Napier            return (Integer) e.value.__tojava__(Integer.class);
1217169aa30eeaac89c49984bb9d061ca152d43391aBill Napier          }
1227169aa30eeaac89c49984bb9d061ca152d43391aBill Napier          // Then some other kind of exception was thrown.  Log it and return error;
1237169aa30eeaac89c49984bb9d061ca152d43391aBill Napier          LOG.log(Level.SEVERE, "Script terminated due to an exception", e);
1247169aa30eeaac89c49984bb9d061ca152d43391aBill Napier          return 1;
1257169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        }
1267169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        return 0;
1277169aa30eeaac89c49984bb9d061ca152d43391aBill Napier    }
1287169aa30eeaac89c49984bb9d061ca152d43391aBill Napier
1295026cf75831b905d5002709abbd4b5a592628ddaBill Napier    public static void runString(String executablePath, String script) {
1305026cf75831b905d5002709abbd4b5a592628ddaBill Napier        initPython(executablePath);
1317169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        PythonInterpreter python = new PythonInterpreter();
1327169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        python.exec(script);
1337169aa30eeaac89c49984bb9d061ca152d43391aBill Napier    }
1347169aa30eeaac89c49984bb9d061ca152d43391aBill Napier
1355026cf75831b905d5002709abbd4b5a592628ddaBill Napier    public static Map<String, PyObject> runStringAndGet(String executablePath,
1365026cf75831b905d5002709abbd4b5a592628ddaBill Napier            String script, String... names) {
1375026cf75831b905d5002709abbd4b5a592628ddaBill Napier        return runStringAndGet(executablePath, script, Arrays.asList(names));
1387169aa30eeaac89c49984bb9d061ca152d43391aBill Napier    }
1397169aa30eeaac89c49984bb9d061ca152d43391aBill Napier
1405026cf75831b905d5002709abbd4b5a592628ddaBill Napier    public static Map<String, PyObject> runStringAndGet(String executablePath,
1415026cf75831b905d5002709abbd4b5a592628ddaBill Napier            String script, Collection<String> names) {
1425026cf75831b905d5002709abbd4b5a592628ddaBill Napier        initPython(executablePath);
1437169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        final PythonInterpreter python = new PythonInterpreter();
1447169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        python.exec(script);
1457169aa30eeaac89c49984bb9d061ca152d43391aBill Napier
1467169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        Builder<String, PyObject> builder = ImmutableMap.builder();
1477169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        for (String name : names) {
1487169aa30eeaac89c49984bb9d061ca152d43391aBill Napier            builder.put(name, python.get(name));
1497169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        }
1507169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        return builder.build();
1517169aa30eeaac89c49984bb9d061ca152d43391aBill Napier    }
1527169aa30eeaac89c49984bb9d061ca152d43391aBill Napier
1535026cf75831b905d5002709abbd4b5a592628ddaBill Napier    private static void initPython(String executablePath) {
1547169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        List<String> arg = Collections.emptyList();
1555026cf75831b905d5002709abbd4b5a592628ddaBill Napier        initPython(executablePath, arg, new String[] {""});
1567169aa30eeaac89c49984bb9d061ca152d43391aBill Napier    }
1577169aa30eeaac89c49984bb9d061ca152d43391aBill Napier
1585026cf75831b905d5002709abbd4b5a592628ddaBill Napier    private static void initPython(String executablePath,
1595026cf75831b905d5002709abbd4b5a592628ddaBill Napier            Collection<String> pythonPath, String[] argv) {
1607169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        Properties props = new Properties();
1617169aa30eeaac89c49984bb9d061ca152d43391aBill Napier
1627169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        // Build up the python.path
1637169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        StringBuilder sb = new StringBuilder();
1647169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        sb.append(System.getProperty("java.class.path"));
1657169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        for (String p : pythonPath) {
1667169aa30eeaac89c49984bb9d061ca152d43391aBill Napier            sb.append(":").append(p);
1677169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        }
1687169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        props.setProperty("python.path", sb.toString());
1697169aa30eeaac89c49984bb9d061ca152d43391aBill Napier
1707169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        /** Initialize the python interpreter. */
1717169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        // Default is 'message' which displays sys-package-mgr bloat
1727169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        // Choose one of error,warning,message,comment,debug
1737169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        props.setProperty("python.verbose", "error");
1747169aa30eeaac89c49984bb9d061ca152d43391aBill Napier
1755026cf75831b905d5002709abbd4b5a592628ddaBill Napier        // This needs to be set for sys.executable to function properly
1765026cf75831b905d5002709abbd4b5a592628ddaBill Napier        props.setProperty("python.executable", executablePath);
1775026cf75831b905d5002709abbd4b5a592628ddaBill Napier
1787169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        PythonInterpreter.initialize(System.getProperties(), props, argv);
179d1efb3282762072d219285643792ff0119e3ed96Bill Napier
180d1efb3282762072d219285643792ff0119e3ed96Bill Napier        String frameworkDir = System.getProperty("java.ext.dirs");
181d1efb3282762072d219285643792ff0119e3ed96Bill Napier        File monkeyRunnerJar = new File(frameworkDir, "monkeyrunner.jar");
182d1efb3282762072d219285643792ff0119e3ed96Bill Napier        if (monkeyRunnerJar.canRead()) {
183d1efb3282762072d219285643792ff0119e3ed96Bill Napier            PySystemState.packageManager.addJar(monkeyRunnerJar.getAbsolutePath(), false);
184d1efb3282762072d219285643792ff0119e3ed96Bill Napier        }
1857169aa30eeaac89c49984bb9d061ca152d43391aBill Napier    }
1867169aa30eeaac89c49984bb9d061ca152d43391aBill Napier
1877169aa30eeaac89c49984bb9d061ca152d43391aBill Napier    /**
1887169aa30eeaac89c49984bb9d061ca152d43391aBill Napier     * Start an interactive python interpreter.
1897169aa30eeaac89c49984bb9d061ca152d43391aBill Napier     */
1905026cf75831b905d5002709abbd4b5a592628ddaBill Napier    public static void console(String executablePath) {
1915026cf75831b905d5002709abbd4b5a592628ddaBill Napier        initPython(executablePath);
1927169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        InteractiveConsole python = new JLineConsole();
1937169aa30eeaac89c49984bb9d061ca152d43391aBill Napier        python.interact();
1947169aa30eeaac89c49984bb9d061ca152d43391aBill Napier    }
1957169aa30eeaac89c49984bb9d061ca152d43391aBill Napier}
196