From 2755bfc255f28db0727e29749d456eebe82ba776 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Mon, 10 Jun 2019 15:14:10 +0200 Subject: [PATCH] Improve portable builds In portable builds, scrcpy-server.jar was supposed to be present in the current directory, so in practice it worked only if scrcpy was launched from its own directory. Instead, find the absolute path of the executable and build a suitable path to use scrcpy-server.jar from the same directory. --- app/meson.build | 4 +-- app/src/command.h | 7 +++++ app/src/server.c | 53 +++++++++++++++++++++++++++++++------- app/src/sys/unix/command.c | 26 +++++++++++++++++++ app/src/sys/win/command.c | 15 +++++++++++ meson_options.txt | 2 +- 6 files changed, 95 insertions(+), 12 deletions(-) diff --git a/app/meson.build b/app/meson.build index 673892cd..02d24a34 100644 --- a/app/meson.build +++ b/app/meson.build @@ -93,8 +93,8 @@ conf.set_quoted('SCRCPY_VERSION', meson.project_version()) # the prefix used during configuration (meson --prefix=PREFIX) conf.set_quoted('PREFIX', get_option('prefix')) -# build a "portable" version (with scrcpy-server.jar accessible from the -# current directory) +# build a "portable" version (with scrcpy-server.jar accessible from the same +# directory as the executable) conf.set('PORTABLE', get_option('portable')) # the default client TCP port for the "adb reverse" tunnel diff --git a/app/src/command.h b/app/src/command.h index 3453ca10..db6358da 100644 --- a/app/src/command.h +++ b/app/src/command.h @@ -9,6 +9,7 @@ // not needed here, but winsock2.h must never be included AFTER windows.h # include # include +# define PATH_SEPARATOR '\\' # define PRIexitcode "lu" // # ifdef _WIN64 @@ -23,6 +24,7 @@ #else # include +# define PATH_SEPARATOR '/' # define PRIsizet "zu" # define PRIexitcode "d" # define PROCESS_NONE -1 @@ -76,4 +78,9 @@ adb_install(const char *serial, const char *local); bool process_check_success(process_t proc, const char *name); +// return the absolute path of the executable (the scrcpy binary) +// may be NULL on error; to be freed by SDL_free +char * +get_executable_path(void); + #endif diff --git a/app/src/server.c b/app/src/server.c index e7c8712e..d0599bef 100644 --- a/app/src/server.c +++ b/app/src/server.c @@ -2,32 +2,67 @@ #include #include +#include #include #include #include #include "config.h" +#include "command.h" #include "log.h" #include "net.h" #define SOCKET_NAME "scrcpy" #define SERVER_FILENAME "scrcpy-server.jar" -#ifdef PORTABLE -# define DEFAULT_SERVER_PATH SERVER_FILENAME -#else -# define DEFAULT_SERVER_PATH PREFIX "/share/scrcpy/" SERVER_FILENAME -#endif - +#define DEFAULT_SERVER_PATH PREFIX "/share/scrcpy/" SERVER_FLENAME #define DEVICE_SERVER_PATH "/data/local/tmp/" SERVER_FILENAME static const char * get_server_path(void) { - const char *server_path = getenv("SCRCPY_SERVER_PATH"); - if (!server_path) { - server_path = DEFAULT_SERVER_PATH; + const char *server_path_env = getenv("SCRCPY_SERVER_PATH"); + if (server_path_env) { + LOGD("Using SCRCPY_SERVER_PATH: %s", server_path_env); + // if the envvar is set, use it + return server_path_env; } + +#ifndef PORTABLE + LOGD("Using server: " DEFAULT_SERVER_PATH); + // the absolute path is hardcoded + return DEFAULT_SERVER_PATH; +#else + // use scrcpy-server.jar in the same directory as the executable + char *executable_path = get_executable_path(); + if (!executable_path) { + LOGE("Cannot get executable path, " + "using " SERVER_FILENAME " from current directory"); + // not found, use current directory + return SERVER_FILENAME; + } + char *dir = dirname(executable_path); + size_t dirlen = strlen(dir); + + // sizeof(SERVER_FILENAME) gives statically the size including the null byte + size_t len = dirlen + 1 + sizeof(SERVER_FILENAME); + char *server_path = SDL_malloc(len); + if (!server_path) { + LOGE("Cannot alloc server path string, " + "using " SERVER_FILENAME " from current directory"); + SDL_free(executable_path); + return SERVER_FILENAME; + } + + memcpy(server_path, dir, dirlen); + server_path[dirlen] = PATH_SEPARATOR; + memcpy(&server_path[dirlen + 1], SERVER_FILENAME, sizeof(SERVER_FILENAME)); + // the final null byte has been copied with SERVER_FILENAME + + SDL_free(executable_path); + + LOGD("Using server (portable): %s", server_path); return server_path; +#endif } static bool diff --git a/app/src/sys/unix/command.c b/app/src/sys/unix/command.c index fa41571e..55aea5e8 100644 --- a/app/src/sys/unix/command.c +++ b/app/src/sys/unix/command.c @@ -1,9 +1,15 @@ +// for portability #define _POSIX_SOURCE // for kill() +#define _BSD_SOURCE // for readlink() + +// modern glibc will complain without this +#define _DEFAULT_SOURCE #include "command.h" #include #include +#include #include #include #include @@ -98,3 +104,23 @@ cmd_simple_wait(pid_t pid, int *exit_code) { } return !code; } + +char * +get_executable_path(void) { +// +#ifdef __linux__ + char buf[PATH_MAX + 1]; // +1 for the null byte + ssize_t len = readlink("/proc/self/exe", buf, PATH_MAX); + if (len == -1) { + perror("readlink"); + return NULL; + } + buf[len] = '\0'; + return SDL_strdup(buf); +#else + // in practice, we only need this feature for portable builds, only used on + // Windows, so we don't care implementing it for every platform + // (it's useful to have a working version on Linux for debugging though) + return NULL; +#endif +} diff --git a/app/src/sys/win/command.c b/app/src/sys/win/command.c index 8434dc98..484ce9f0 100644 --- a/app/src/sys/win/command.c +++ b/app/src/sys/win/command.c @@ -75,3 +75,18 @@ cmd_simple_wait(HANDLE handle, DWORD *exit_code) { } return !code; } + +char * +get_executable_path(void) { + HMODULE hModule = GetModuleHandleW(NULL); + if (!hModule) { + return NULL; + } + WCHAR buf[MAX_PATH + 1]; // +1 for the null byte + int len = GetModuleFileNameW(hModule, buf, MAX_PATH); + if (!len) { + return NULL; + } + buf[len] = '\0'; + return utf8_from_wide_char(buf); +} diff --git a/meson_options.txt b/meson_options.txt index 90b4293c..a443ccb2 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -3,6 +3,6 @@ option('build_server', type: 'boolean', value: true, description: 'Build the ser option('crossbuild_windows', type: 'boolean', value: false, description: 'Build for Windows from Linux') option('windows_noconsole', type: 'boolean', value: false, description: 'Disable console on Windows (pass -mwindows flag)') option('prebuilt_server', type: 'string', description: 'Path of the prebuilt server') -option('portable', type: 'boolean', description: 'Use scrcpy-server.jar from the current directory') +option('portable', type: 'boolean', description: 'Use scrcpy-server.jar from the same directory as the scrcpy executable') option('skip_frames', type: 'boolean', value: true, description: 'Always display the most recent frame') option('hidpi_support', type: 'boolean', value: true, description: 'Enable High DPI support')