blob: ddcf3065bfd9a3bb6fad67b4cb3ad7ce81af5b1c [file] [log] [blame]
/* Copyright (C) 2011 The Android Open Source Project
**
** This software is licensed under the terms of the GNU General Public
** License version 2, as published by the Free Software Foundation, and
** may be copied, distributed, and modified under those terms.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
*/
/* This is the source code to the tiny "emulator" launcher program
* that is in charge of starting the target-specific emulator binary
* for a given AVD, i.e. either 'emulator-arm' or 'emulator-x86'
*
* This program will be replaced in the future by what is currently
* known as 'emulator-ui', but is a good placeholder until this
* migration is completed.
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <android/utils/compiler.h>
#include <android/utils/host_bitness.h>
#include <android/utils/panic.h>
#include <android/utils/path.h>
#include <android/utils/bufprint.h>
#include <android/utils/win32_cmdline_quote.h>
#include <android/opengl/emugl_config.h>
#include <android/qt/qt_setup.h>
#include <android/avd/scanner.h>
#include <android/avd/util.h>
/* Required by android/utils/debug.h */
int android_verbose;
bool ranchu = false;
#define DEBUG 1
#if DEBUG
# define D(...) do { if (android_verbose) printf("emulator:" __VA_ARGS__); } while (0)
#else
# define D(...) do{}while(0)
#endif
/* The extension used by dynamic libraries on the host platform */
#ifdef _WIN32
# define DLL_EXTENSION ".dll"
#elif defined(__APPLE__)
# define DLL_EXTENSION ".dylib"
#else
# define DLL_EXTENSION ".so"
#endif
// Name of GPU emulation main library for (32-bit and 64-bit versions)
#define GLES_EMULATION_LIB "libOpenglRender" DLL_EXTENSION
#define GLES_EMULATION_LIB64 "lib64OpenglRender" DLL_EXTENSION
/* Forward declarations */
static char* getTargetEmulatorPath(const char* progDir,
bool tryCurrentPath,
const char* avdArch,
const int force_32bit,
bool* is_64bit);
/* The execv() definition in older mingw is slightly bogus.
* It takes a second argument of type 'const char* const*'
* while POSIX mandates char** instead.
*
* To avoid compiler warnings, define the safe_execv macro
* to perform an explicit cast with mingw.
*/
#if defined(_WIN32) && !ANDROID_GCC_PREREQ(4,4)
# define safe_execv(_filepath,_argv) execv((_filepath),(const char* const*)(_argv))
#else
# define safe_execv(_filepath,_argv) execv((_filepath),(_argv))
#endif
/* Main routine */
int main(int argc, char** argv)
{
const char* avdName = NULL;
char* avdArch = NULL;
const char* gpu = NULL;
char* emulatorPath;
int force_32bit = 0;
bool no_window = false;
/* Define ANDROID_EMULATOR_DEBUG to 1 in your environment if you want to
* see the debug messages from this launcher program.
*/
const char* debug = getenv("ANDROID_EMULATOR_DEBUG");
if (debug != NULL && *debug && *debug != '0')
android_verbose = 1;
/* Parse command-line and look for
* 1) an avd name either in the form or '-avd <name>' or '@<name>'
* 2) '-force-32bit' which always use 32-bit emulator on 64-bit platforms
* 3) '-verbose', or '-debug-all' or '-debug all' to enable verbose mode.
*/
int nn;
for (nn = 1; nn < argc; nn++) {
const char* opt = argv[nn];
if (!strcmp(opt,"-qemu"))
break;
if (!strcmp(opt,"-verbose") || !strcmp(opt,"-debug-all")) {
android_verbose = 1;
}
if (!strcmp(opt,"-debug") && nn + 1 < argc &&
!strcmp(argv[nn + 1], "all")) {
android_verbose = 1;
}
if (!strcmp(opt,"-gpu") && nn + 1 < argc) {
gpu = argv[nn + 1];
nn++;
}
if (!strcmp(opt,"-ranchu")) {
ranchu = true;
continue;
}
if (!strcmp(opt,"-force-32bit")) {
force_32bit = 1;
continue;
}
if (!strcmp(opt,"-no-window")) {
no_window = true;
continue;
}
if (!strcmp(opt,"-list-avds")) {
AvdScanner* scanner = avdScanner_new(NULL);
for (;;) {
const char* name = avdScanner_next(scanner);
if (!name) {
break;
}
printf("%s\n", name);
}
avdScanner_free(scanner);
exit(0);
}
if (!avdName) {
if (!strcmp(opt,"-avd") && nn+1 < argc) {
avdName = argv[nn+1];
}
else if (opt[0] == '@' && opt[1] != '\0') {
avdName = opt+1;
}
}
}
/* If ANDROID_EMULATOR_FORCE_32BIT is set to 'true' or '1' in the
* environment, set -force-32bit automatically.
*/
{
const char kEnvVar[] = "ANDROID_EMULATOR_FORCE_32BIT";
const char* val = getenv(kEnvVar);
if (val && (!strcmp(val, "true") || !strcmp(val, "1"))) {
if (!force_32bit) {
D("Auto-config: -force-32bit (%s=%s)\n", kEnvVar, val);
force_32bit = 1;
}
}
}
#if defined(__linux__)
if (!force_32bit && android_getHostBitness() == 32) {
fprintf(stderr,
"ERROR: 32-bit Linux Android emulator binaries are DEPRECATED, to use them\n"
" you will have to do at least one of the following:\n"
"\n"
" - Use the '-force-32bit' option when invoking 'emulator'.\n"
" - Set ANDROID_EMULATOR_FORCE_32BIT to 'true' in your environment.\n"
"\n"
" Either one will allow you to use the 32-bit binaries, but please be\n"
" aware that these will disappear in a future Android SDK release.\n"
" Consider moving to a 64-bit Linux system before that happens.\n"
"\n"
);
exit(1);
}
#endif
/* If there is an AVD name, we're going to extract its target architecture
* by looking at its config.ini
*/
if (avdName != NULL) {
D("Found AVD name '%s'\n", avdName);
avdArch = path_getAvdTargetArch(avdName);
D("Found AVD target architecture: %s\n", avdArch);
} else {
/* Otherwise, using the ANDROID_PRODUCT_OUT directory */
const char* androidOut = getenv("ANDROID_PRODUCT_OUT");
if (androidOut != NULL) {
D("Found ANDROID_PRODUCT_OUT: %s\n", androidOut);
avdArch = path_getBuildTargetArch(androidOut);
D("Found build target architecture: %s\n",
avdArch ? avdArch : "<NULL>");
}
}
if (avdArch == NULL) {
avdArch = "arm";
D("Can't determine target AVD architecture: defaulting to %s\n", avdArch);
}
/* Find program directory. */
char* progDir = NULL;
path_split(argv[0], &progDir, NULL);
/* Only search in current path if there is no directory separator
* in |progName|. */
#ifdef _WIN32
bool tryCurrentPath = (!strchr(argv[0], '/') && !strchr(argv[0], '\\'));
#else
bool tryCurrentPath = !strchr(argv[0], '/');
#endif
/* Find the architecture-specific program in the same directory */
bool is_64bit = false;
emulatorPath = getTargetEmulatorPath(progDir,
tryCurrentPath,
avdArch,
force_32bit,
&is_64bit);
D("Found target-specific emulator binary: %s\n", emulatorPath);
/* Replace it in our command-line */
argv[0] = emulatorPath;
/* We need to find the location of the GLES emulation shared libraries
* and modify either LD_LIBRARY_PATH or PATH accordingly
*/
bool gpuEnabled = false;
const char* gpuMode = NULL;
if (avdName) {
gpuMode = path_getAvdGpuMode(avdName);
gpuEnabled = (gpuMode != NULL);
}
EmuglConfig config;
int bitness = is_64bit ? 64 : 32;
if (!emuglConfig_init(
&config, gpuEnabled, gpuMode, gpu, bitness, no_window)) {
fprintf(stderr, "ERROR: %s\n", config.status);
exit(1);
}
D("%s\n", config.status);
emuglConfig_setupEnv(&config);
/* For Qt-based UI backends, add <lib>/qt/ to the library search path. */
androidQtSetupEnv(is_64bit);
#ifdef _WIN32
// Take care of quoting all parameters before sending them to execv().
// See the "Eveyone quotes command line arguments the wrong way" on
// MSDN.
int n;
for (n = 0; n < argc; ++n) {
// Technically, this leaks the quoted strings, but we don't care
// since this process will terminate after the execv() anyway.
argv[n] = win32_cmdline_quote(argv[n]);
D("Quoted param: [%s]\n", argv[n]);
}
#endif
if (android_verbose) {
int i;
printf("emulator: Running :%s\n", emulatorPath);
for(i = 0; i < argc; i++) {
printf("emulator: qemu backend: argv[%02d] = \"%s\"\n", i, argv[i]);
}
/* Dump final command-line parameters to make debugging easier */
printf("emulator: Concatenated backend parameters:\n");
for (i = 0; i < argc; i++) {
/* To make it easier to copy-paste the output to a command-line,
* quote anything that contains spaces.
*/
if (strchr(argv[i], ' ') != NULL) {
printf(" '%s'", argv[i]);
} else {
printf(" %s", argv[i]);
}
}
printf("\n");
}
// Launch it with the same set of options !
// Note that on Windows, the first argument must _not_ be quoted or
// Windows will fail to find the program.
safe_execv(emulatorPath, argv);
/* We could not launch the program ! */
fprintf(stderr, "Could not launch '%s': %s\n", emulatorPath, strerror(errno));
return errno;
}
static char* bufprint_emulatorName(char* p,
char* end,
const char* progDir,
const char* prefix,
const char* variant,
const char* archSuffix,
const char* exeExtension) {
if (progDir) {
p = bufprint(p, end, "%s/", progDir);
}
p = bufprint(p, end, "%s", prefix);
if (variant) {
p = bufprint(p, end, "%s-", variant);
}
p = bufprint(p, end, "%s%s", archSuffix, exeExtension);
return p;
}
/* Probe the filesystem to check if an emulator executable named like
* <progDir>/<prefix><arch> exists.
*
* |progDir| is an optional program directory. If NULL, the executable
* will be searched in the current directory.
* |variant| is an optional variant. If not NULL, then a name like
* 'emulator-<variant>-<archSuffix>' will be searched, instead of
* 'emulator-<archSuffix>'.
* |archSuffix| is an architecture-specific suffix, like "arm", or 'x86"
* If |search_for_64bit_emulator| is true, lookup for 64-bit emulator first,
* then the 32-bit version.
* If |try_current_path|, try to look into the current path if no
* executable was found under |progDir|.
* On success, returns the path of the executable (string must be freed by
* the caller) and sets |*is_64bit| to indicate whether the binary is a
* 64-bit executable. On failure, return NULL.
*/
static char*
probeTargetEmulatorPath(const char* progDir,
const char* variant,
const char* archSuffix,
bool search_for_64bit_emulator,
bool try_current_path,
bool* is_64bit)
{
char path[PATH_MAX], *pathEnd = path + sizeof(path), *p;
static const char kEmulatorPrefix[] = "emulator-";
static const char kEmulator64Prefix[] = "emulator64-";
#ifdef _WIN32
const char kExeExtension[] = ".exe";
#else
const char kExeExtension[] = "";
#endif
// First search for the 64-bit emulator binary.
if (search_for_64bit_emulator) {
p = bufprint_emulatorName(path,
pathEnd,
progDir,
kEmulator64Prefix,
variant,
archSuffix,
kExeExtension);
D("Probing program: %s\n", path);
if (p < pathEnd && path_exists(path)) {
*is_64bit = true;
return strdup(path);
}
}
// Then for the 32-bit one.
p = bufprint_emulatorName(path,
pathEnd,
progDir,
kEmulatorPrefix,
variant,
archSuffix,
kExeExtension);
D("Probing program: %s\n", path);
if (p < pathEnd && path_exists(path)) {
*is_64bit = false;
return strdup(path);
}
// Not found, try in the current path then
if (try_current_path) {
char* result;
if (search_for_64bit_emulator) {
p = bufprint_emulatorName(path,
pathEnd,
NULL,
kEmulator64Prefix,
variant,
archSuffix,
kExeExtension);
if (p < pathEnd) {
D("Probing path for: %s\n", path);
result = path_search_exec(path);
if (result) {
*is_64bit = true;
return result;
}
}
}
p = bufprint_emulatorName(path,
pathEnd,
NULL,
kEmulatorPrefix,
variant,
archSuffix,
kExeExtension);
if (p < pathEnd) {
D("Probing path for: %s\n", path);
result = path_search_exec(path);
if (result) {
*is_64bit = false;
return result;
}
}
}
return NULL;
}
/*
* emulator engine selection process is
* emulator ->
* arm:
* 32-bit: emulator(64)-arm
* 64-bit: emulator(64)-arm64(NA) ->
emulator(64)-ranchu-arm64 -> qemu/<host>/qemu-system-aarch64
* mips:
* 32-bit: emulator(64)-mips
* 64-bit: emulator(64)-mips64(NA) ->
emulator(64)-ranchu-mips64 -> qemu/<host>/qemu-system-mips64
* x86:
* 32-bit: (if '-ranchu')
* emulator(64)-ranchu-x86 -> qemu/<host>/qemu-system-x86
* else
* emulator(64)-x86
* 64-bit: (if '-ranchu')
* emulator(64)-ranchu-x86_64 -> qemu/<host>/qemu-system-x86_64
* else
* emulator(64)-x86
*/
static char*
getTargetEmulatorPath(const char* progDir,
bool tryCurrentPath,
const char* avdArch,
const int force_32bit,
bool* is_64bit)
{
char* result;
char* ranchu_result;
bool search_for_64bit_emulator =
!force_32bit && android_getHostBitness() == 64;
const char* emulatorSuffix;
/* Try look for classic emulator first */
emulatorSuffix = emulator_getBackendSuffix(avdArch);
if (!emulatorSuffix) {
APANIC("This emulator cannot emulate %s CPUs!\n", avdArch);
}
D("Looking for emulator-%s to emulate '%s' CPU\n", emulatorSuffix,
avdArch);
result = probeTargetEmulatorPath(progDir,
NULL,
emulatorSuffix,
search_for_64bit_emulator,
tryCurrentPath,
is_64bit);
if (result && !ranchu) {
/* found and not ranchu */
D("return result: %s\n", result);
return result;
} else {
/* no classic emulator or prefer ranchu */
D("Looking for ranchu emulator backend for %s CPU\n", avdArch);
ranchu_result = probeTargetEmulatorPath(progDir,
"ranchu",
avdArch,
#ifdef _WIN32
true, // 32 bit ranchu does not work on windows
#else
search_for_64bit_emulator,
#endif
tryCurrentPath,
is_64bit);
if (ranchu_result) {
D("return ranchu: %s\n", ranchu_result);
return ranchu_result;
} else {
if (result) {
/* ranchu not found, fallback to classic
should NOT happen in current scenario
just go ahead still and leave a message
*/
fprintf(stderr, "ERROR: requested 'ranchu' not available\n"
"classic backend is used\n");
return result;
}
}
}
/* Otherwise, the program is missing */
APANIC("Missing emulator engine program for '%s' CPUS.\n", avdArch);
return NULL;
}