1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package vogar.android;
18
19import com.google.common.collect.Iterables;
20
21import java.io.File;
22import java.util.ArrayList;
23import java.util.Collections;
24import java.util.HashSet;
25import java.util.List;
26import java.util.Set;
27
28import vogar.Action;
29import vogar.Classpath;
30import vogar.Mode;
31import vogar.ModeId;
32import vogar.Run;
33import vogar.Variant;
34import vogar.commands.VmCommandBuilder;
35import vogar.tasks.MkdirTask;
36import vogar.tasks.RunActionTask;
37import vogar.tasks.Task;
38
39/**
40 * Executes actions on a Dalvik or ART runtime on a Linux desktop.
41 */
42public final class HostRuntime implements Mode {
43    private final Run run;
44    private final ModeId modeId;
45    private final Variant variant;
46
47    public HostRuntime(Run run, ModeId modeId, Variant variant) {
48        if (!modeId.isHost() || !modeId.supportsVariant(variant)) {
49            throw new IllegalArgumentException("Unsupported mode:" + modeId +
50                    " or variant: " + variant);
51        }
52        this.run = run;
53        this.modeId = modeId;
54        this.variant = variant;
55    }
56
57    @Override public Task executeActionTask(Action action, boolean useLargeTimeout) {
58        return new RunActionTask(run, action, useLargeTimeout);
59    }
60
61    private File dalvikCache() {
62        return run.localFile("android-data", run.dalvikCache);
63    }
64
65    @Override public Set<Task> installTasks() {
66        Set<Task> result = new HashSet<Task>();
67        for (File classpathElement : run.classpath.getElements()) {
68            // Libraries need to be dex'ed and put in the temporary directory.
69            String name = run.basenameOfJar(classpathElement);
70            File localDex = run.localDexFile(name);
71            result.add(createCreateDexJarTask(run.classpath, classpathElement, name,
72                    null /* action */, localDex));
73        }
74        result.add(new MkdirTask(run.mkdir, dalvikCache()));
75        return result;
76    }
77
78    @Override public Set<Task> cleanupTasks(Action action) {
79        return Collections.emptySet();
80    }
81
82    @Override public Set<Task> installActionTasks(Action action, File jar) {
83        File localDexFile = run.localDexFile(action.getName());
84        Task createDexJarTask = createCreateDexJarTask(Classpath.of(jar), jar, action.getName(),
85                action, localDexFile);
86        return Collections.singleton(createDexJarTask);
87    }
88
89    @Override public VmCommandBuilder newVmCommandBuilder(Action action, File workingDirectory) {
90        String hostOut = System.getenv("ANDROID_HOST_OUT");
91        if (hostOut == null || hostOut.length() == 0) {
92          hostOut = System.getenv("ANDROID_BUILD_TOP");
93          if (hostOut == null) {
94            hostOut = "";
95          } else {
96            hostOut += "/";
97          }
98          hostOut += "out/host/linux-x86";
99        }
100
101        List<File> jars = new ArrayList<File>();
102        for (String jar : modeId.getJarNames()) {
103            jars.add(new File(hostOut, "framework/" + jar + ".jar"));
104        }
105        Classpath bootClasspath = Classpath.of(jars);
106
107        String libDir = hostOut;
108        if (variant == Variant.X32) {
109            libDir += "/lib";
110        } else if (variant == Variant.X64) {
111            libDir += "/lib64";
112        } else {
113            throw new AssertionError("Unsupported variant:" + variant);
114        }
115
116        List<String> vmCommand = new ArrayList<String>();
117        Iterables.addAll(vmCommand, run.invokeWith());
118        vmCommand.add(hostOut + "/bin/" + run.vmCommand);
119
120        // If you edit this, see also DeviceRuntime...
121        VmCommandBuilder builder = new VmCommandBuilder(run.log)
122                .env("ANDROID_PRINTF_LOG", "tag")
123                .env("ANDROID_LOG_TAGS", "*:i")
124                .env("ANDROID_DATA", dalvikCache().getParent())
125                .env("ANDROID_ROOT", hostOut)
126                .env("LD_LIBRARY_PATH", libDir)
127                .env("DYLD_LIBRARY_PATH", libDir)
128                // This is needed on the host so that the linker loads core.oat at the necessary
129                // address.
130                .env("LD_USE_LOAD_BIAS", "1")
131                .vmCommand(vmCommand)
132                .vmArgs("-Xbootclasspath:" + bootClasspath.toString())
133                .vmArgs("-Duser.language=en")
134                .vmArgs("-Duser.region=US");
135        if (run.debugPort != null) {
136            builder.vmArgs("-Xcompiler-option", "--debuggable");
137        }
138        if (!run.benchmark && run.checkJni) {
139            builder.vmArgs("-Xcheck:jni");
140        }
141        // dalvikvm defaults to no limit, but the framework sets the limit at 2000.
142        builder.vmArgs("-Xjnigreflimit:2000");
143        return builder;
144    }
145
146    @Override public Classpath getRuntimeClasspath(Action action) {
147        Classpath result = new Classpath();
148        result.addAll(run.localDexFile(action.getName()));
149        for (File classpathElement : run.classpath.getElements()) {
150            result.addAll(run.localDexFile(run.basenameOfJar(classpathElement)));
151        }
152        result.addAll(run.resourceClasspath);
153        return result;
154    }
155
156    private Task createCreateDexJarTask(Classpath classpath, File classpathElement, String name,
157            Action action, File localDex) {
158        Task dex;
159        if (run.useJack) {
160            dex = new JackDexTask(run, classpath, run.benchmark, name, classpathElement, action,
161                    localDex);
162        } else {
163            dex = new DexTask(run.androidSdk, classpath, run.benchmark, name, classpathElement,
164                    action, localDex);
165        }
166        return dex;
167    }
168}
169