From 106b87a4d2459ae881ab89b987cc9347a843a4ea Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sun, 13 May 2018 16:03:39 +0200 Subject: [PATCH] Add cross-compilation scripts for Windows Build the Windows binary from mingw on Linux, using the official prebuilt binaries for ffmpeg, SDL2 and adb. MSYS2 and all its packaged dll are not necessary anymore. --- Makefile | 65 ----------------------- Makefile.CrossWindows | 106 ++++++++++++++++++++++++++++++++++++++ app/meson.build | 54 ++++++++++++++++--- cross_win32.txt | 20 +++++++ cross_win64.txt | 20 +++++++ prebuilt-deps/.gitignore | 4 ++ prebuilt-deps/Makefile | 40 ++++++++++++++ prebuilt-deps/prepare-dep | 58 +++++++++++++++++++++ 8 files changed, 296 insertions(+), 71 deletions(-) delete mode 100644 Makefile create mode 100644 Makefile.CrossWindows create mode 100644 cross_win32.txt create mode 100644 cross_win64.txt create mode 100644 prebuilt-deps/.gitignore create mode 100644 prebuilt-deps/Makefile create mode 100755 prebuilt-deps/prepare-dep diff --git a/Makefile b/Makefile deleted file mode 100644 index 79439f3d..00000000 --- a/Makefile +++ /dev/null @@ -1,65 +0,0 @@ -# This makefile provides recipes to build a "portable" version of scrcpy. -# -# Here, "portable" means that the client and server binaries are expected to be -# anywhere, but in the same directory, instead of well-defined separate -# locations (e.g. /usr/bin/scrcpy and /usr/share/scrcpy/scrcpy-server.jar). -# -# In particular, this implies to change the location from where the client push -# the server to the device. -# -# "make release-portable" builds a zip containing the client and the server. -# -# On Windows with MSYS2/mingw64, execute: -# GRADLE="$PWD/gradlew" mingw32-make release-portable -# -# This is a simple Makefile because Meson is not flexible enough to execute some -# arbitrary commands. - -.PHONY: default clean build-portable release-portable dist-portable dist-portable-zip sums test check - -GRADLE ?= ./gradlew - -PORTABLE_BUILD_DIR := build-portable -DIST := dist -TARGET_DIR := scrcpy - -VERSION := $(shell git describe --tags --always) -TARGET := $(TARGET_DIR)-$(VERSION).zip - -default: - @echo 'You must specify a target. Try: make release-portable' - -clean: - $(GRADLE) clean - rm -rf "$(PORTABLE_BUILD_DIR)" "$(DIST)" - -build-portable: - [ -d "$(PORTABLE_BUILD_DIR)" ] || ( mkdir "$(PORTABLE_BUILD_DIR)" && \ - meson "$(PORTABLE_BUILD_DIR)" \ - --buildtype release --strip -Db_lto=true \ - -Doverride_server_path=scrcpy-server.jar ) - ninja -C "$(PORTABLE_BUILD_DIR)" - -release-portable: clean dist-portable-zip sums - @echo "Release created in $(DIST)/." - -dist-portable: build-portable - mkdir -p "$(DIST)/$(TARGET_DIR)" - cp "$(PORTABLE_BUILD_DIR)"/server/scrcpy-server.jar "$(DIST)/$(TARGET_DIR)/" - cp "$(PORTABLE_BUILD_DIR)"/app/scrcpy "$(DIST)/$(TARGET_DIR)/" - -dist-portable-zip: dist-portable - cd "$(DIST)"; \ - zip -r "$(TARGET)" "$(TARGET_DIR)" - -sums: - cd "$(DIST)"; \ - sha256sum *.zip > SHA256SUM.txt - -test: build-portable - $(GRADLE) test - ninja -C "$(PORTABLE_BUILD_DIR)" test - -check: build-portable - $(GRADLE) check - ninja -C "$(PORTABLE_BUILD_DIR)" test diff --git a/Makefile.CrossWindows b/Makefile.CrossWindows new file mode 100644 index 00000000..3d6bc59e --- /dev/null +++ b/Makefile.CrossWindows @@ -0,0 +1,106 @@ +# This makefile provides recipes to build a "portable" version of scrcpy for +# Windows. +# +# Here, "portable" means that the client and server binaries are expected to be +# anywhere, but in the same directory, instead of well-defined separate +# locations (e.g. /usr/bin/scrcpy and /usr/share/scrcpy/scrcpy-server.jar). +# +# In particular, this implies to change the location from where the client push +# the server to the device. + +.PHONY: default clean \ + build-server \ + prepare-deps-win32 prepare-deps-win64 \ + build-win32 build-win64 \ + dist-win32 dist-win64 \ + zip-win32 zip-win64 \ + sums release + +GRADLE ?= ./gradlew + +SERVER_BUILD_DIR := build-server +WIN32_BUILD_DIR := build-win32 +WIN64_BUILD_DIR := build-win64 + +DIST := dist +WIN32_TARGET_DIR := scrcpy-win32 +WIN64_TARGET_DIR := scrcpy-win64 + +VERSION := $(shell git describe --tags --always) +WIN32_TARGET := $(WIN32_TARGET_DIR)-$(VERSION).zip +WIN64_TARGET := $(WIN64_TARGET_DIR)-$(VERSION).zip + +release: clean zip-win32 zip-win64 sums + @echo "Release created in $(DIST)/." + +clean: + $(GRADLE) clean + rm -rf "$(SERVER_BUILD_DIR)" "$(WIN32_BUILD_DIR)" "$(WIN64_BUILD_DIR)" "$(DIST)" + +build-server: + [ -d "$(SERVER_BUILD_DIR)" ] || ( mkdir "$(SERVER_BUILD_DIR)" && \ + meson "$(SERVER_BUILD_DIR)" \ + --buildtype release -Dbuild_app=false ) + ninja -C "$(SERVER_BUILD_DIR)" + +prepare-deps-win32: + -$(MAKE) -C prebuilt-deps prepare-win32 + +build-win32: prepare-deps-win32 + [ -d "$(WIN32_BUILD_DIR)" ] || ( mkdir "$(WIN32_BUILD_DIR)" && \ + meson "$(WIN32_BUILD_DIR)" \ + --cross-file cross_win32.txt \ + --buildtype release --strip -Db_lto=true \ + -Dbuild_server=false \ + -Doverride_server_path=scrcpy-server.jar ) + ninja -C "$(WIN32_BUILD_DIR)" + +prepare-deps-win64: + -$(MAKE) -C prebuilt-deps prepare-win64 + +build-win64: prepare-deps-win64 + [ -d "$(WIN64_BUILD_DIR)" ] || ( mkdir "$(WIN64_BUILD_DIR)" && \ + meson "$(WIN64_BUILD_DIR)" \ + --cross-file cross_win64.txt \ + --buildtype release --strip -Db_lto=true \ + -Dbuild_server=false \ + -Doverride_server_path=scrcpy-server.jar ) + ninja -C "$(WIN64_BUILD_DIR)" + +dist-win32: build-server build-win32 + mkdir -p "$(DIST)/$(WIN32_TARGET_DIR)" + cp "$(SERVER_BUILD_DIR)"/server/scrcpy-server.jar "$(DIST)/$(WIN32_TARGET_DIR)/" + cp "$(WIN32_BUILD_DIR)"/app/scrcpy.exe "$(DIST)/$(WIN32_TARGET_DIR)/" + cp prebuilt-deps/ffmpeg-4.0-win32-shared/bin/avutil-56.dll "$(DIST)/$(WIN32_TARGET_DIR)/" + cp prebuilt-deps/ffmpeg-4.0-win32-shared/bin/avcodec-58.dll "$(DIST)/$(WIN32_TARGET_DIR)/" + cp prebuilt-deps/ffmpeg-4.0-win32-shared/bin/avformat-58.dll "$(DIST)/$(WIN32_TARGET_DIR)/" + cp prebuilt-deps/ffmpeg-4.0-win32-shared/bin/swresample-3.dll "$(DIST)/$(WIN32_TARGET_DIR)/" + cp prebuilt-deps/platform-tools/adb.exe "$(DIST)/$(WIN32_TARGET_DIR)/" + cp prebuilt-deps/platform-tools/AdbWinApi.dll "$(DIST)/$(WIN32_TARGET_DIR)/" + cp prebuilt-deps/platform-tools/AdbWinUsbApi.dll "$(DIST)/$(WIN32_TARGET_DIR)/" + cp prebuilt-deps/SDL2-2.0.8/i686-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN32_TARGET_DIR)/" + +dist-win64: build-server build-win64 + mkdir -p "$(DIST)/$(WIN64_TARGET_DIR)" + cp "$(SERVER_BUILD_DIR)"/server/scrcpy-server.jar "$(DIST)/$(WIN64_TARGET_DIR)/" + cp "$(WIN64_BUILD_DIR)"/app/scrcpy.exe "$(DIST)/$(WIN64_TARGET_DIR)/" + cp prebuilt-deps/ffmpeg-4.0-win64-shared/bin/avutil-56.dll "$(DIST)/$(WIN64_TARGET_DIR)/" + cp prebuilt-deps/ffmpeg-4.0-win64-shared/bin/avcodec-58.dll "$(DIST)/$(WIN64_TARGET_DIR)/" + cp prebuilt-deps/ffmpeg-4.0-win64-shared/bin/avformat-58.dll "$(DIST)/$(WIN64_TARGET_DIR)/" + cp prebuilt-deps/ffmpeg-4.0-win64-shared/bin/swresample-3.dll "$(DIST)/$(WIN64_TARGET_DIR)/" + cp prebuilt-deps/platform-tools/adb.exe "$(DIST)/$(WIN64_TARGET_DIR)/" + cp prebuilt-deps/platform-tools/AdbWinApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/" + cp prebuilt-deps/platform-tools/AdbWinUsbApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/" + cp prebuilt-deps/SDL2-2.0.8/x86_64-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN64_TARGET_DIR)/" + +zip-win32: dist-win32 + cd "$(DIST)"; \ + zip -r "$(WIN32_TARGET)" "$(WIN32_TARGET_DIR)" + +zip-win64: dist-win64 + cd "$(DIST)"; \ + zip -r "$(WIN64_TARGET)" "$(WIN64_TARGET_DIR)" + +sums: + cd "$(DIST)"; \ + sha256sum *.zip > SHA256SUM.txt diff --git a/app/meson.build b/app/meson.build index 88c26db7..ab3cbd0d 100644 --- a/app/meson.build +++ b/app/meson.build @@ -19,12 +19,54 @@ src = [ 'src/tinyxpm.c', ] -dependencies = [ - dependency('libavformat'), - dependency('libavcodec'), - dependency('libavutil'), - dependency('sdl2'), -] +if not meson.is_cross_build() + + # native build + dependencies = [ + dependency('libavformat'), + dependency('libavcodec'), + dependency('libavutil'), + dependency('sdl2'), + ] + +else + + # cross-compile mingw32 build (from Linux to Windows) + cc = meson.get_compiler('c') + + prebuilt_sdl2 = meson.get_cross_property('prebuilt_sdl2') + sdl2_bin_dir = meson.current_source_dir() + '/../prebuilt-deps/' + prebuilt_sdl2 + '/bin' + sdl2_lib_dir = meson.current_source_dir() + '/../prebuilt-deps/' + prebuilt_sdl2 + '/lib' + sdl2_include_dir = '../prebuilt-deps/' + prebuilt_sdl2 + '/include' + + sdl2 = declare_dependency( + dependencies: [ + cc.find_library('SDL2', dirs: sdl2_bin_dir), + cc.find_library('SDL2main', dirs: sdl2_lib_dir), + ], + include_directories: include_directories(sdl2_include_dir) + ) + + prebuilt_ffmpeg_shared = meson.get_cross_property('prebuilt_ffmpeg_shared') + prebuilt_ffmpeg_dev = meson.get_cross_property('prebuilt_ffmpeg_dev') + ffmpeg_bin_dir = meson.current_source_dir() + '/../prebuilt-deps/' + prebuilt_ffmpeg_shared + '/bin' + ffmpeg_include_dir = '../prebuilt-deps/' + prebuilt_ffmpeg_dev + '/include' + ffmpeg = declare_dependency( + dependencies: [ + cc.find_library('avcodec-58', dirs: ffmpeg_bin_dir), + cc.find_library('avformat-58', dirs: ffmpeg_bin_dir), + cc.find_library('avutil-56', dirs: ffmpeg_bin_dir), + ], + include_directories: include_directories(ffmpeg_include_dir) + ) + + dependencies = [ + ffmpeg, + sdl2, + cc.find_library('mingw32') + ] + +endif cc = meson.get_compiler('c') diff --git a/cross_win32.txt b/cross_win32.txt new file mode 100644 index 00000000..8cb650aa --- /dev/null +++ b/cross_win32.txt @@ -0,0 +1,20 @@ +# apt install mingw-w64 mingw-w64-tools + +[binaries] +name = 'mingw' +c = '/usr/bin/i686-w64-mingw32-gcc' +cpp = '/usr/bin/i686-w64-mingw32-g++' +ar = '/usr/bin/i686-w64-mingw32-ar' +strip = '/usr/bin/i686-w64-mingw32-strip' +pkgconfig = '/usr/bin/i686-w64-mingw32-pkg-config' + +[host_machine] +system = 'windows' +cpu_family = 'x86' +cpu = 'i686' +endian = 'little' + +[properties] +prebuilt_ffmpeg_shared = 'ffmpeg-4.0-win32-shared' +prebuilt_ffmpeg_dev = 'ffmpeg-4.0-win32-dev' +prebuilt_sdl2 = 'SDL2-2.0.8/i686-w64-mingw32' diff --git a/cross_win64.txt b/cross_win64.txt new file mode 100644 index 00000000..0110d5bb --- /dev/null +++ b/cross_win64.txt @@ -0,0 +1,20 @@ +# apt install mingw-w64 mingw-w64-tools + +[binaries] +name = 'mingw' +c = '/usr/bin/x86_64-w64-mingw32-gcc' +cpp = '/usr/bin/x86_64-w64-mingw32-g++' +ar = '/usr/bin/x86_64-w64-mingw32-ar' +strip = '/usr/bin/x86_64-w64-mingw32-strip' +pkgconfig = '/usr/bin/x86_64-w64-mingw32-pkg-config' + +[host_machine] +system = 'windows' +cpu_family = 'x86' +cpu = 'x86_64' +endian = 'little' + +[properties] +prebuilt_ffmpeg_shared = 'ffmpeg-4.0-win64-shared' +prebuilt_ffmpeg_dev = 'ffmpeg-4.0-win64-dev' +prebuilt_sdl2 = 'SDL2-2.0.8/x86_64-w64-mingw32' diff --git a/prebuilt-deps/.gitignore b/prebuilt-deps/.gitignore new file mode 100644 index 00000000..934bc04c --- /dev/null +++ b/prebuilt-deps/.gitignore @@ -0,0 +1,4 @@ +* +!/.gitignore +!/Makefile +!/prepare-dep diff --git a/prebuilt-deps/Makefile b/prebuilt-deps/Makefile new file mode 100644 index 00000000..557cc675 --- /dev/null +++ b/prebuilt-deps/Makefile @@ -0,0 +1,40 @@ +.PHONY: prepare-win32 prepare-win64 \ + prepare-ffmpeg-shared-win32 \ + prepare-ffmpeg-dev-win32 \ + prepare-ffmpeg-shared-win64 \ + prepare-ffmpeg-dev-win64 \ + prepare-sdl2 \ + prepare-adb + +prepare-win32: prepare-sdl2 prepare-ffmpeg-shared-win32 prepare-ffmpeg-dev-win32 prepare-adb +prepare-win64: prepare-sdl2 prepare-ffmpeg-shared-win64 prepare-ffmpeg-dev-win64 prepare-adb + +prepare-ffmpeg-shared-win32: + @./prepare-dep https://ffmpeg.zeranoe.com/builds/win32/shared/ffmpeg-4.0-win32-shared.zip \ + 530c92df0ca14c35901b4b681847d62da3c50a0cc9b7ced37b04968f6b5c243d \ + ffmpeg-4.0-win32-shared + +prepare-ffmpeg-dev-win32: + @./prepare-dep https://ffmpeg.zeranoe.com/builds/win32/dev/ffmpeg-4.0-win32-dev.zip \ + e2f5200b5e73c4d0abb9b89c4ffc0438f92a0aadc54c81cf57e18c81a9f11c6b \ + ffmpeg-4.0-win32-dev + +prepare-ffmpeg-shared-win64: + @./prepare-dep https://ffmpeg.zeranoe.com/builds/win64/shared/ffmpeg-4.0-win64-shared.zip \ + 8fe2d344463dbefc2db4239a4203a55ed0324faceaae57276a40c4fabda84c37 \ + ffmpeg-4.0-win64-shared + +prepare-ffmpeg-dev-win64: + @./prepare-dep https://ffmpeg.zeranoe.com/builds/win64/dev/ffmpeg-4.0-win64-dev.zip \ + facced738eabfc53fa92834dea8b24426f64db61298688fed480145945be07fa \ + ffmpeg-4.0-win64-dev + +prepare-sdl2: + @./prepare-dep https://libsdl.org/release/SDL2-devel-2.0.8-mingw.tar.gz \ + ffff7305d634aff5e1df5b7bb935435c3a02c8b03ad94a1a2be9169a558a7961 \ + SDL2-2.0.8 + +prepare-adb: + @./prepare-dep https://dl.google.com/android/repository/platform-tools_r27.0.1-windows.zip \ + 880662adfb0d6911ff250b9e13930ae1a4110fc36d5866afd4f8f56d935f7939 \ + platform-tools diff --git a/prebuilt-deps/prepare-dep b/prebuilt-deps/prepare-dep new file mode 100755 index 00000000..7da37811 --- /dev/null +++ b/prebuilt-deps/prepare-dep @@ -0,0 +1,58 @@ +#!/bin/bash +set -e +url="$1" +sum="$2" +dir="$3" + +checksum() { + local file="$1" + local sum="$2" + echo "$file: verifying checksum..." + echo "$sum $file" | sha256sum -c +} + +get_file() { + local url="$1" + local file="$2" + local sum="$3" + if [[ -f "$file" ]] + then + echo "$file: found" + else + echo "$file: not found, downloading..." + wget "$url" -O "$file" + fi + checksum "$file" "$sum" +} + +extract() { + local file="$1" + echo "Extracting $file..." + if [[ "$file" == *.zip ]] + then + unzip -q "$file" + elif [[ "$file" == *.tar.gz ]] + then + tar xf "$file" + else + echo "Unsupported file: $file" + return 1 + fi +} + +get_dep() { + local url="$1" + local sum="$2" + local dir="$3" + local file="${url##*/}" + if [[ -d "$dir" ]] + then + echo "$dir: found" + else + echo "$dir: not found" + get_file "$url" "$file" "$sum" + extract "$file" + fi +} + +get_dep "$1" "$2" "$3"