From bd65e3eec9c6873533cc39f2cb745c67d380c532 Mon Sep 17 00:00:00 2001 From: Tomas Dirvanauskas Date: Wed, 23 Mar 2022 09:43:58 +0200 Subject: [PATCH] switch end lines --- JniBridge.bee.cs | 1220 +++++++++++++++++++++++----------------------- build.pl | 650 ++++++++++++------------ 2 files changed, 935 insertions(+), 935 deletions(-) diff --git a/JniBridge.bee.cs b/JniBridge.bee.cs index c58e831..93bff62 100644 --- a/JniBridge.bee.cs +++ b/JniBridge.bee.cs @@ -1,610 +1,610 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Bee.BuildTools; -using Bee.Core; -using Bee.Core.Stevedore; -using Bee.NativeProgramSupport; -using Bee.Toolchain.Android; -using Bee.Toolchain.GNU; -using Bee.Tools; -using NiceIO; -using Bee.ProjectGeneration.VisualStudio; -using Bee.VisualStudioSolution; -using System.Linq; - -/** - * Required environment variables: - * - ANDROID_SDK_ROOT pointing to directory with Android SDK - * - JAVA_HOME (optional) pointing to Java install; will be downloaded from Stevedore otherwise - * Build targets to pass bee: - * - apigenerator - build the API generator - * - jnibridge - build the jnibridge.jar file - * - generate:jnibridge:android - generate C++ code for Android - * - generate:jnibridge:osx - generate C++ code for MaxOS (used by test program) - * - build:android:armeabi-v7a - * - build:android:arm64-v8a - * - build:android:x86 - * - build:android:x86_64 - * - build:android:zip - builds all 4 android archs above and zips them (THIS IS THE MAIN ONE) - * - build:osx:x86_64 - * - build:osx:test - builds the one above and the test program (run ./build/osx/test afterwards to run tests) - * - build:windows:x86_64 - * - build:windows:test - builds the one above and the test program (run build\windows\runtests.cmd afterwards to run tests) - * - projectfiles - generates IDE projects - */ - - -class JniBridge -{ - const string kAndroidApi = "android-31"; - - static class Platform - { - public const string OSX = "osx"; - public const string Windows = "windows"; - } - - static readonly string[] kAndroidApiClasses = new[] - { - "::android::Manifest_permission", - "::android::R_attr", - "::android::app::Activity", - "::android::app::AlertDialog_Builder", - "::android::app::NotificationManager", - "::android::app::Presentation", - "::android::content::Context", - "::android::graphics::Color", - "::android::graphics::ImageFormat", - "::android::graphics::drawable::ColorDrawable", - "::android::hardware::display::DisplayManager", - "::android::hardware::Camera", - "::android::hardware::input::InputManager", - "::android::hardware::GeomagneticField", - "::android::location::LocationManager", - "::android::media::AudioAttributes_Builder", - "::android::media::AudioFocusRequest_Builder", - "::android::media::AudioManager", - "::android::media::MediaCodec", - "::android::media::MediaCodec::BufferInfo", - "::android::media::MediaExtractor", - "::android::media::MediaFormat", - "::android::media::MediaRouter", - "::android::net::ConnectivityManager", - "::android::net::wifi::WifiManager", - "::android::os::Build", - "::android::os::Build_VERSION", - "::android::os::HandlerThread", - "::android::os::Environment", - "::android::os::PowerManager", - "::android::os::Process", - "::android::os::Vibrator", - "::android::provider::Settings_Secure", - "::android::provider::Settings_System", - "::android::telephony::TelephonyManager", - "::android::telephony::SubscriptionManager", - "::android::telephony::SubscriptionInfo", - "::android::view::Choreographer", - "::android::view::Display", - "::android::view::Gravity", - "::android::view::SurfaceView", - "::android::view::WindowManager", - "::android::webkit::MimeTypeMap", - "::android::widget::CheckBox", - "::android::widget::CompoundButton_OnCheckedChangeListener", - "::android::widget::ProgressBar", - "::java::lang::Character", - "::java::lang::System", - "::java::lang::SecurityException", - "::java::lang::NoSuchMethodError", - "::java::lang::ClassCastException", - "::java::lang::UnsatisfiedLinkError", - "::java::io::FileNotFoundException", - "::java::net::HttpURLConnection", - "::java::nio::channels::Channels", - "::java::util::HashSet", - "::java::util::Map_Entry", - "::java::util::NoSuchElementException", - "::java::util::Scanner", - "::java::util::zip::ZipFile", - "::javax::net::ssl::X509TrustManager", - "::javax::net::ssl::TrustManagerFactory", - "::java::security::KeyStore", - "::com::google::android::gms::ads::identifier::AdvertisingIdClient", - "::com::google::android::gms::common::GooglePlayServicesAvailabilityException", - "::com::google::android::gms::common::GooglePlayServicesNotAvailableException", - }; - - private static readonly string[] kDesktopClasses = new[] - { - "::java::lang::System", - "::java::lang::UnsupportedOperationException", - }; - - private static NPath[] HeaderFiles => NPath.CurrentDirectory.Files("*.h"); - private static NPath[] CppFiles => NPath.CurrentDirectory.Files("*.cpp"); - private static NPath[] JavaFiles => NPath.CurrentDirectory.Combine("jnibridge/bitter/jnibridge").Files("*.java"); - - static void Main() - { - using var _ = new BuildProgramContext(); - var codegen = CodeGen.Release; - - var jdk = SetupJava(); - var sdk = SetupAndroidSdk(); - - var apiGenerator = SetupApiGenerator(jdk); - var jnibridgeJar = SetupJniBridgeJar(jdk); - var generatedFilesAndroid = SetupSourceGeneration(jdk, apiGenerator, jnibridgeJar, GetAndroidSourceGenerationParams(sdk)); - var generatedFilesMacOS = SetupSourceGeneration(jdk, apiGenerator, jnibridgeJar, GetMacOSSourceGenerationParams(jdk)); - var generatedFilesWindows = SetupSourceGeneration(jdk, apiGenerator, jnibridgeJar, GetWindowsSourceGenerationParams(jdk)); - - var androidZip = new ZipArchiveContents(); - var versionFile = VersionControl.SetupWriteRevisionInfoFile(new NPath("artifacts").Combine("build.txt")); - var androidToolchains = GetAndroidToolchains(); - var nativePrograms = new List(); - foreach (var toolchain in androidToolchains) - { - var androidConfig = new NativeProgramConfiguration(codegen, toolchain, false); - var np = SetupJniBridgeStaticLib(generatedFilesAndroid, androidConfig, GetAndroidStaticLibParams(toolchain), androidZip); - nativePrograms.Add(np); - } - var headers = SetupJniBridgeHeaders(generatedFilesAndroid, "android"); - foreach (var header in headers) - { - androidZip.AddFileToArchive(header, new NPath("include").Combine(header.FileName)); - } - androidZip.AddFileToArchive(jnibridgeJar); - androidZip.AddFileToArchive(versionFile); - - - var codegenForTests = CodeGen.Debug; - var osxToolchain = ToolChain.Store.Mac().Sdk_10_13().x64(); - var osxConfig = new NativeProgramConfiguration(codegenForTests, osxToolchain, false); - var osxStaticLib = SetupJniBridgeStaticLib(generatedFilesMacOS, osxConfig, GetMacOSStaticLibParams(osxToolchain, jdk)); - SetupTestProgramOsx(osxToolchain, osxStaticLib, codegenForTests, generatedFilesMacOS, jdk); - - var windowsToolchain = ToolChain.Store.Windows().VS2019().Sdk_18362().x64(); - var windowsConfig = new NativeProgramConfiguration(codegenForTests, windowsToolchain, false); - var windowsStaticLib = SetupJniBridgeStaticLib(generatedFilesWindows, windowsConfig, GetWindowsStaticLibParams(windowsToolchain, jdk)); - var windowsTestProgram = SetupTestProgramWindows(windowsToolchain, windowsStaticLib, codegenForTests, generatedFilesWindows, jdk, out var targetExecutable); - - var androidZipPath = "build/builds.zip"; - ZipTool.SetupPack(androidZipPath, androidZip); - Backend.Current.AddAliasDependency("build:android:zip", androidZipPath); - - SetupGeneratedProjects(nativePrograms, windowsTestProgram, generatedFilesWindows, targetExecutable); - } - - static Jdk SetupJava() - { - var jdk = Jdk.UserDefault; - if (jdk != null) - return jdk; - var openJdk = StevedoreArtifact.UnityInternal(HostPlatform.Pick( - linux: "open-jdk-linux-x64/jdk8u172-b11_4be8440cc514099cfe1b50cbc74128f6955cd90fd5afe15ea7be60f832de67b4.zip", - mac: "open-jdk-mac-x64/jdk8u172-b11_01c8518f34d083b6adf57f1a3a7cff76e78dc0a2523b6332d10a0f30fcb8d308.zip", - windows: "open-jdk-win-x64/jdk8u172-b11_43d8964b5d97b95a87b54ed5b08b70dcb0aa4f6521a1dad4caba958e44c205b9.zip" - )); - return new Jdk(openJdk.Path.ResolveWithFileSystem()); - } - - static NPath SetupAndroidSdk() - { - return GetEnv("ANDROID_SDK_ROOT"); - } - - static NPath GetEnv(string variable) - { - var path = Environment.GetEnvironmentVariable(variable); - if (string.IsNullOrEmpty(path)) - throw new Exception($"Environment variable {variable} not set"); - return new NPath(path); - } - - static AndroidNdkToolchain[] GetAndroidToolchains() - { - var ndk = ToolChain.Store.Android().r21d(); - return new[] - { - ndk.Armv7(), - ndk.Arm64(), - ndk.x86(), - ndk.x64(), - }; - } - - static NPath SetupApiGenerator(Jdk jdk) - { - var apiGeneratorDir = new NPath("apigenerator"); - return SetupJarForDirectory(jdk, apiGeneratorDir); - } - - static NPath SetupJniBridgeJar(Jdk jdk) - { - var jnibridgeDir = new NPath("jnibridge"); - return SetupJarForDirectory(jdk, jnibridgeDir); - } - - static NPath SetupJarForDirectory(Jdk jdk, NPath jarDir) - { - var jarname = jarDir.FileName; - var apiGeneratorFiles = jarDir.Files("*.java", true); - var classFileDir = new NPath($"artifacts/{jarname}"); - var apiGeneratorJar = new NPath($"build/{jarname}.jar"); - var classFiles = jdk.SetupCompilation(classFileDir, apiGeneratorFiles, new NPath[] { jarDir }, new NPath[0]); - jdk.SetupJar(new NPath[] { classFileDir }, classFiles, apiGeneratorJar); - Backend.Current.AddAliasDependency(jarname, apiGeneratorJar); - return apiGeneratorJar; - } - - struct SourceGenerationParams - { - public string platformName; - public NPath[] inputJars; - public string[] classes; - } - - static SourceGenerationParams GetAndroidSourceGenerationParams(NPath sdk) - { - var androidJar = sdk.Combine("platforms", kAndroidApi, "android.jar"); - var googlePlayServicesJar = sdk.Combine("extras", "google", "google_play_services_froyo", "libproject", "google-play-services_lib", "libs", "google-play-services.jar"); - - return new SourceGenerationParams() - { - platformName = "android", - inputJars = new[] {androidJar, googlePlayServicesJar}, - classes = kAndroidApiClasses, - }; - } - - static SourceGenerationParams GetDesktopSourceGenerationParams(Jdk jdk, string platformName) - { - var rtJar = jdk.JavaHome.Combine("jre", "lib", "rt.jar"); - - return new SourceGenerationParams() - { - platformName = platformName, - inputJars = new [] {rtJar}, - classes = kDesktopClasses, - }; - } - - static SourceGenerationParams GetMacOSSourceGenerationParams(Jdk jdk) - { - return GetDesktopSourceGenerationParams(jdk, Platform.OSX); - } - - static SourceGenerationParams GetWindowsSourceGenerationParams(Jdk jdk) - { - return GetDesktopSourceGenerationParams(jdk, Platform.Windows); - } - - static NPath SetupSourceGeneration(Jdk jdk, NPath apiGenerator, NPath jnibridgeJar, SourceGenerationParams genParams) - { - var destDir = new NPath("artifacts").Combine("generated", genParams.platformName); - var actionName = "generate:jnibridge:" + genParams.platformName; - var generatedFiles = new NPath[] { destDir.Combine("API.h") }; - - var templateFiles = new NPath("templates").Files(new [] { "cpp", "h" }); - var inputs = new List(); - inputs.AddRange(genParams.inputJars); - inputs.Add(jnibridgeJar); - inputs.Add(apiGenerator); - inputs.AddRange(templateFiles); - - string inputJars = string.Empty; - if (genParams.inputJars.Length > 0) - { - var builder = new StringBuilder(); - builder.Append('"').Append(genParams.inputJars[0]); - for (int i = 1; i < genParams.inputJars.Length; ++i) - builder.Append(';').Append(genParams.inputJars[i]); - inputJars = builder.Append('"').ToString(); - } - var apiClassString = string.Join(" ", genParams.classes); - - Backend.Current.AddAction( - actionName, - generatedFiles, - inputs.ToArray(), - jdk.Java.InQuotes(SlashMode.Native), - new[] - { - "-cp", - apiGenerator.InQuotes(), - "APIGenerator", - destDir.InQuotes(), - inputJars, - apiClassString - }, - targetDirectories:new []{destDir} - ); - - Backend.Current.AddAliasDependency(actionName, generatedFiles); - return destDir; - } - - struct StaticLibParams - { - public string libName; - public string platformName; - public string archName; - public Action specialConfiguration; - } - - static StaticLibParams GetAndroidStaticLibParams(ToolChain toolchain) - { - Action specConfig = (toolchain.Architecture is ARMv7Architecture) - ? (np) => - { - np.CompilerSettingsForAndroid().Add(c => c.WithThumb(true)); - np.CompilerSettingsForAndroid().Add(c => c.WithDebugMode(DebugMode.None)); - } - : (np) => - { - np.CompilerSettingsForAndroid().Add(c => c.WithDebugMode(DebugMode.None)); - }; - return new StaticLibParams() - { - libName = kAndroidApi, - platformName = "android", - archName = AndroidNdk.AbiFromArch(toolchain.Architecture), - specialConfiguration = specConfig, - }; - } - - static StaticLibParams GetMacOSStaticLibParams(ToolChain toolchain, Jdk jdk) - { - return new StaticLibParams() - { - libName = "testlib", - platformName = Platform.OSX, - archName = toolchain.Architecture.Name, - specialConfiguration = (np) => - { - np.IncludeDirectories.Add(jdk.JavaHome.Combine("include")); - np.IncludeDirectories.Add(jdk.JavaHome.Combine("include", "darwin")); - }, - }; - } - - static StaticLibParams GetWindowsStaticLibParams(ToolChain toolchain, Jdk jdk) - { - return new StaticLibParams() - { - libName = "testlib", - platformName = Platform.Windows, - archName = toolchain.Architecture.Name, - specialConfiguration = (np) => - { - np.IncludeDirectories.Add(jdk.JavaHome.Combine("include")); - np.IncludeDirectories.Add(jdk.JavaHome.Combine("include", "win32")); - }, - }; - } - - static NativeProgram SetupJniBridgeStaticLib(NPath generatedFilesDir, NativeProgramConfiguration config, StaticLibParams libParams, ZipArchiveContents targetArchive = null) - { - NPath[] generatedSources; - if (!generatedFilesDir.DirectoryExists()) - generatedSources = new[] {generatedFilesDir.Combine("API.h")}; - else - generatedSources = generatedFilesDir.Files("*.cpp"); - - var buildDest = new NPath("build").Combine(libParams.platformName); - - var np = new NativeProgram(libParams.libName); - np.Sources.Add(generatedSources); - np.Sources.Add(CppFiles); - np.IncludeDirectories.Add(generatedFilesDir); - np.ExtraDependenciesForAllObjectFiles.Add(generatedFilesDir); - if (libParams.specialConfiguration != null) - libParams.specialConfiguration(np); - - var destDir = buildDest.Combine(libParams.archName); - var target = np.SetupSpecificConfiguration(config, config.ToolChain.StaticLibraryFormat).DeployTo(destDir); - - var alias = $"build:{libParams.platformName}:{libParams.archName}"; - Backend.Current.AddAliasDependency(alias, target.Paths); - if (targetArchive != null) - { - var destInArchive = destDir.RelativeTo("build"); - foreach (var file in target.Paths) - targetArchive.AddFileToArchive(file, destInArchive.Combine(file.FileName)); - } - - return np; - } - - static NPath[] SetupJniBridgeHeaders(NPath generatedFilesDir, string platformName) - { - var includeFiles = new List(); - var includes = new NPath("build").Combine(platformName, "include"); - - includeFiles.Add(Backend.Current.SetupCopyFile(includes.Combine("API.h"), generatedFilesDir.Combine("API.h"))); - foreach (var file in HeaderFiles) - includeFiles.Add(Backend.Current.SetupCopyFile(includes.Combine(file.FileName), file)); - - var incs = includeFiles.ToArray(); - Backend.Current.AddAliasDependency($"includes:{platformName}", incs); - return incs; - } - - static void SetupTestProgramOsx(ToolChain toolchain, NativeProgram staticLib, CodeGen codegen, NPath generatedFilesDir, Jdk jdk) - { - var np = new NativeProgram("JNIBridgeTests"); - np.Sources.Add(new NPath("test").Files("*.cpp")); - np.IncludeDirectories.Add(generatedFilesDir); - np.IncludeDirectories.Add(jdk.JavaHome.Combine("include")); - np.IncludeDirectories.Add(jdk.JavaHome.Combine("include", "darwin")); - np.Libraries.Add(staticLib); - var javaLibDir = jdk.JavaHome.Combine("jre", "lib", "server"); - np.Libraries.Add(new DynamicLibrary(javaLibDir.Combine("libjvm.dylib"))); - np.CompilerSettings().Add(c => c.WithCustomFlags(new [] {$"-Wl,-rpath,{javaLibDir}"})); - - var destDir = new NPath("build").Combine(Platform.OSX); - var config = new NativeProgramConfiguration(codegen, toolchain, false); - var target = np.SetupSpecificConfiguration(config, config.ToolChain.ExecutableFormat).DeployTo(destDir); - - Backend.Current.AddAliasDependency($"build:{Platform.OSX}:test", target.Paths); - } - - static NativeProgram SetupTestProgramWindows(ToolChain toolchain, NativeProgram staticLib, CodeGen codegen, NPath generatedFilesDir, Jdk jdk, out NPath targetExecutable) - { - var np = new NativeProgram("JNIBridgeTests"); - np.Sources.Add(new NPath("test").Files("*.cpp")); - np.IncludeDirectories.Add(generatedFilesDir); - np.IncludeDirectories.Add(jdk.JavaHome.Combine("include")); - np.IncludeDirectories.Add(jdk.JavaHome.Combine("include", "win32")); - np.Libraries.Add(staticLib); - var javaLibDir = jdk.JavaHome.Combine("lib"); - np.Libraries.Add(new StaticLibrary(javaLibDir.Combine("jvm.lib"))); - - var destDir = new NPath("build").Combine(Platform.Windows); - var config = new NativeProgramConfiguration(codegen, toolchain, false); - var target = np.SetupSpecificConfiguration(config, config.ToolChain.ExecutableFormat).DeployTo(destDir); - - var exeName = np.Name + ".exe"; - var pdbName = np.Name + ".pdb"; - var server = jdk.JavaHome.Combine("jre/bin/server"); - targetExecutable = Backend.Current.SetupCopyFile(server.Combine(exeName), destDir.Combine(exeName)); - var targetPdb = Backend.Current.SetupCopyFile(server.Combine(pdbName), destDir.Combine(pdbName)); - var targetJar = Backend.Current.SetupCopyFile(server.Combine("jnibridge.jar"), "build/jnibridge.jar"); - var script = destDir.Combine("runtests.cmd"); - Backend.Current.AddWriteTextAction(script, $@"echo off -echo Launching tests -{targetExecutable.MakeAbsolute().InQuotes()} -echo Exited with code %ERRORLEVEL% -exit %ERRORLEVEL% -"); - - var targetPaths = new List(target.Paths); - targetPaths.Add(targetExecutable); - targetPaths.Add(targetPdb); - targetPaths.Add(targetJar); - targetPaths.Add(script); - Backend.Current.AddAliasDependency($"build:{Platform.Windows}:test", targetPaths.ToArray()); - - return np; - } - - static string GetABI(Architecture architecture) - { - if (architecture == Architecture.Armv7) - return "armeabi-v7a"; - if (architecture == Architecture.Arm64) - return "arm64-v8a"; - if (architecture == Architecture.x86) - return "x86"; - if (architecture == Architecture.x64) - return "x86_64"; - throw new NotImplementedException(architecture.ToString()); - } - - static void SetupGeneratedProjects(IReadOnlyList programs, NativeProgram testingProgram, NPath generatedFilesDirectory, NPath executableForTests) - { - // Not entirely sure, but I think there's should have been only NativeProgram with different architectures. - // Yet we have 4 - // For now pick the first one, since it will be enough for editing purposes - - var nativeProgram = programs[0]; - var ndkToolchain = (AndroidNdkToolchain)nativeProgram.SetupConfigurations.FirstOrDefault().ToolChain; - var ndkIncludePath = ndkToolchain.Sdk.Path.ResolveWithFileSystem().Combine("sysroot/usr/include"); - - var jniBridgeNP = new NativeProgram("JNIBridge"); - nativeProgram.Sources.CopyTo(jniBridgeNP.Sources); - nativeProgram.IncludeDirectories.CopyTo(jniBridgeNP.IncludeDirectories); - nativeProgram.Defines.CopyTo(jniBridgeNP.Defines); - - jniBridgeNP.IncludeDirectories.Add(ndkIncludePath); - - var configs = new List(); - foreach (var p in programs) - { - configs.AddRange(p.SetupConfigurations.ToArray()); - } - - jniBridgeNP.ValidConfigurations = configs; - jniBridgeNP.OutputName.Set(string.Empty); - - var setAndroidSDKRoot = $"set ANDROID_SDK_ROOT={SetupAndroidSdk()}"; - jniBridgeNP.CommandToBuild.Set(c => - { - var architecture = ((AndroidNdkToolchain)c.ToolChain).Architecture; - return $@"{setAndroidSDKRoot} -bee build:android:{GetABI(architecture)}"; - }); - - var jniBridgeSln = new VisualStudioSolution(); - foreach (var config in configs) - { - var architecture = ((AndroidNdkToolchain)config.ToolChain).Architecture; - - jniBridgeSln.Configurations.Add(new SolutionConfiguration(GetABI(architecture), (configurations, file) => - { - var firstOrDefault = configurations.FirstOrDefault(c => c == config); - return new Tuple( - firstOrDefault ?? configurations.First(), - true); - })); - } - var extraFiles = new List(); - extraFiles.AddRange(HeaderFiles); - extraFiles.AddRange(CppFiles); - extraFiles.AddRange(JavaFiles); - - var builder = new VisualStudioNativeProjectFileBuilder(jniBridgeNP, extraFiles); - builder = jniBridgeNP.ValidConfigurations.Aggregate( - builder, - (current, c) => current.AddProjectConfiguration(c) - ); - - - jniBridgeSln.Projects.Add(builder.DeployTo("JNIBridge.vcxproj")); - - testingProgram.CommandToBuild.Set(c => - { - return @$"{setAndroidSDKRoot} -bee build:windows:test"; - }); - - var extraFilesForTests = new List(); - extraFilesForTests.AddRange(HeaderFiles); - extraFilesForTests.AddRange(CppFiles); - extraFilesForTests.AddRange(JavaFiles); - // Includes generated files for Windows, we don't want those in JNIBridge project since it builds for Android - if (generatedFilesDirectory.DirectoryExists()) - extraFilesForTests.AddRange(generatedFilesDirectory.Files(true)); - - builder = new VisualStudioNativeProjectFileBuilder(testingProgram, extraFilesForTests); - builder = testingProgram.SetupConfigurations.Aggregate( - builder, - (current, c) => current.AddProjectConfiguration(c) - ); - - var jniBridgeTestsSln = new VisualStudioSolution(); - var vsProject = builder.DeployTo("JNIBridgeTests.vcxproj"); - jniBridgeTestsSln.Projects.Add(vsProject); - - var vsProjectUsersSettings = (vsProject.Path + ".user").ToNPath(); - GenerateVSUserSettings(vsProjectUsersSettings, executableForTests); - Backend.Current.AddAliasDependency("projectfiles", jniBridgeSln.Setup()); - Backend.Current.AddAliasDependency("projectfiles", jniBridgeTestsSln.Setup()); - Backend.Current.AddAliasDependency("projectfiles", vsProjectUsersSettings); - Backend.Current.AddAliasDependency("projectfiles", generatedFilesDirectory); - } - - static void GenerateVSUserSettings(NPath path, NPath executablePath) - { - Backend.Current.AddWriteTextAction(path, -$@" - - - {executablePath.MakeAbsolute()} - WindowsLocalDebugger - - -"); - } -} +using System; +using System.Collections.Generic; +using System.Text; +using Bee.BuildTools; +using Bee.Core; +using Bee.Core.Stevedore; +using Bee.NativeProgramSupport; +using Bee.Toolchain.Android; +using Bee.Toolchain.GNU; +using Bee.Tools; +using NiceIO; +using Bee.ProjectGeneration.VisualStudio; +using Bee.VisualStudioSolution; +using System.Linq; + +/** + * Required environment variables: + * - ANDROID_SDK_ROOT pointing to directory with Android SDK + * - JAVA_HOME (optional) pointing to Java install; will be downloaded from Stevedore otherwise + * Build targets to pass bee: + * - apigenerator - build the API generator + * - jnibridge - build the jnibridge.jar file + * - generate:jnibridge:android - generate C++ code for Android + * - generate:jnibridge:osx - generate C++ code for MaxOS (used by test program) + * - build:android:armeabi-v7a + * - build:android:arm64-v8a + * - build:android:x86 + * - build:android:x86_64 + * - build:android:zip - builds all 4 android archs above and zips them (THIS IS THE MAIN ONE) + * - build:osx:x86_64 + * - build:osx:test - builds the one above and the test program (run ./build/osx/test afterwards to run tests) + * - build:windows:x86_64 + * - build:windows:test - builds the one above and the test program (run build\windows\runtests.cmd afterwards to run tests) + * - projectfiles - generates IDE projects + */ + + +class JniBridge +{ + const string kAndroidApi = "android-31"; + + static class Platform + { + public const string OSX = "osx"; + public const string Windows = "windows"; + } + + static readonly string[] kAndroidApiClasses = new[] + { + "::android::Manifest_permission", + "::android::R_attr", + "::android::app::Activity", + "::android::app::AlertDialog_Builder", + "::android::app::NotificationManager", + "::android::app::Presentation", + "::android::content::Context", + "::android::graphics::Color", + "::android::graphics::ImageFormat", + "::android::graphics::drawable::ColorDrawable", + "::android::hardware::display::DisplayManager", + "::android::hardware::Camera", + "::android::hardware::input::InputManager", + "::android::hardware::GeomagneticField", + "::android::location::LocationManager", + "::android::media::AudioAttributes_Builder", + "::android::media::AudioFocusRequest_Builder", + "::android::media::AudioManager", + "::android::media::MediaCodec", + "::android::media::MediaCodec::BufferInfo", + "::android::media::MediaExtractor", + "::android::media::MediaFormat", + "::android::media::MediaRouter", + "::android::net::ConnectivityManager", + "::android::net::wifi::WifiManager", + "::android::os::Build", + "::android::os::Build_VERSION", + "::android::os::HandlerThread", + "::android::os::Environment", + "::android::os::PowerManager", + "::android::os::Process", + "::android::os::Vibrator", + "::android::provider::Settings_Secure", + "::android::provider::Settings_System", + "::android::telephony::TelephonyManager", + "::android::telephony::SubscriptionManager", + "::android::telephony::SubscriptionInfo", + "::android::view::Choreographer", + "::android::view::Display", + "::android::view::Gravity", + "::android::view::SurfaceView", + "::android::view::WindowManager", + "::android::webkit::MimeTypeMap", + "::android::widget::CheckBox", + "::android::widget::CompoundButton_OnCheckedChangeListener", + "::android::widget::ProgressBar", + "::java::lang::Character", + "::java::lang::System", + "::java::lang::SecurityException", + "::java::lang::NoSuchMethodError", + "::java::lang::ClassCastException", + "::java::lang::UnsatisfiedLinkError", + "::java::io::FileNotFoundException", + "::java::net::HttpURLConnection", + "::java::nio::channels::Channels", + "::java::util::HashSet", + "::java::util::Map_Entry", + "::java::util::NoSuchElementException", + "::java::util::Scanner", + "::java::util::zip::ZipFile", + "::javax::net::ssl::X509TrustManager", + "::javax::net::ssl::TrustManagerFactory", + "::java::security::KeyStore", + "::com::google::android::gms::ads::identifier::AdvertisingIdClient", + "::com::google::android::gms::common::GooglePlayServicesAvailabilityException", + "::com::google::android::gms::common::GooglePlayServicesNotAvailableException", + }; + + private static readonly string[] kDesktopClasses = new[] + { + "::java::lang::System", + "::java::lang::UnsupportedOperationException", + }; + + private static NPath[] HeaderFiles => NPath.CurrentDirectory.Files("*.h"); + private static NPath[] CppFiles => NPath.CurrentDirectory.Files("*.cpp"); + private static NPath[] JavaFiles => NPath.CurrentDirectory.Combine("jnibridge/bitter/jnibridge").Files("*.java"); + + static void Main() + { + using var _ = new BuildProgramContext(); + var codegen = CodeGen.Release; + + var jdk = SetupJava(); + var sdk = SetupAndroidSdk(); + + var apiGenerator = SetupApiGenerator(jdk); + var jnibridgeJar = SetupJniBridgeJar(jdk); + var generatedFilesAndroid = SetupSourceGeneration(jdk, apiGenerator, jnibridgeJar, GetAndroidSourceGenerationParams(sdk)); + var generatedFilesMacOS = SetupSourceGeneration(jdk, apiGenerator, jnibridgeJar, GetMacOSSourceGenerationParams(jdk)); + var generatedFilesWindows = SetupSourceGeneration(jdk, apiGenerator, jnibridgeJar, GetWindowsSourceGenerationParams(jdk)); + + var androidZip = new ZipArchiveContents(); + var versionFile = VersionControl.SetupWriteRevisionInfoFile(new NPath("artifacts").Combine("build.txt")); + var androidToolchains = GetAndroidToolchains(); + var nativePrograms = new List(); + foreach (var toolchain in androidToolchains) + { + var androidConfig = new NativeProgramConfiguration(codegen, toolchain, false); + var np = SetupJniBridgeStaticLib(generatedFilesAndroid, androidConfig, GetAndroidStaticLibParams(toolchain), androidZip); + nativePrograms.Add(np); + } + var headers = SetupJniBridgeHeaders(generatedFilesAndroid, "android"); + foreach (var header in headers) + { + androidZip.AddFileToArchive(header, new NPath("include").Combine(header.FileName)); + } + androidZip.AddFileToArchive(jnibridgeJar); + androidZip.AddFileToArchive(versionFile); + + + var codegenForTests = CodeGen.Debug; + var osxToolchain = ToolChain.Store.Mac().Sdk_10_13().x64(); + var osxConfig = new NativeProgramConfiguration(codegenForTests, osxToolchain, false); + var osxStaticLib = SetupJniBridgeStaticLib(generatedFilesMacOS, osxConfig, GetMacOSStaticLibParams(osxToolchain, jdk)); + SetupTestProgramOsx(osxToolchain, osxStaticLib, codegenForTests, generatedFilesMacOS, jdk); + + var windowsToolchain = ToolChain.Store.Windows().VS2019().Sdk_18362().x64(); + var windowsConfig = new NativeProgramConfiguration(codegenForTests, windowsToolchain, false); + var windowsStaticLib = SetupJniBridgeStaticLib(generatedFilesWindows, windowsConfig, GetWindowsStaticLibParams(windowsToolchain, jdk)); + var windowsTestProgram = SetupTestProgramWindows(windowsToolchain, windowsStaticLib, codegenForTests, generatedFilesWindows, jdk, out var targetExecutable); + + var androidZipPath = "build/builds.zip"; + ZipTool.SetupPack(androidZipPath, androidZip); + Backend.Current.AddAliasDependency("build:android:zip", androidZipPath); + + SetupGeneratedProjects(nativePrograms, windowsTestProgram, generatedFilesWindows, targetExecutable); + } + + static Jdk SetupJava() + { + var jdk = Jdk.UserDefault; + if (jdk != null) + return jdk; + var openJdk = StevedoreArtifact.UnityInternal(HostPlatform.Pick( + linux: "open-jdk-linux-x64/jdk8u172-b11_4be8440cc514099cfe1b50cbc74128f6955cd90fd5afe15ea7be60f832de67b4.zip", + mac: "open-jdk-mac-x64/jdk8u172-b11_01c8518f34d083b6adf57f1a3a7cff76e78dc0a2523b6332d10a0f30fcb8d308.zip", + windows: "open-jdk-win-x64/jdk8u172-b11_43d8964b5d97b95a87b54ed5b08b70dcb0aa4f6521a1dad4caba958e44c205b9.zip" + )); + return new Jdk(openJdk.Path.ResolveWithFileSystem()); + } + + static NPath SetupAndroidSdk() + { + return GetEnv("ANDROID_SDK_ROOT"); + } + + static NPath GetEnv(string variable) + { + var path = Environment.GetEnvironmentVariable(variable); + if (string.IsNullOrEmpty(path)) + throw new Exception($"Environment variable {variable} not set"); + return new NPath(path); + } + + static AndroidNdkToolchain[] GetAndroidToolchains() + { + var ndk = ToolChain.Store.Android().r21d(); + return new[] + { + ndk.Armv7(), + ndk.Arm64(), + ndk.x86(), + ndk.x64(), + }; + } + + static NPath SetupApiGenerator(Jdk jdk) + { + var apiGeneratorDir = new NPath("apigenerator"); + return SetupJarForDirectory(jdk, apiGeneratorDir); + } + + static NPath SetupJniBridgeJar(Jdk jdk) + { + var jnibridgeDir = new NPath("jnibridge"); + return SetupJarForDirectory(jdk, jnibridgeDir); + } + + static NPath SetupJarForDirectory(Jdk jdk, NPath jarDir) + { + var jarname = jarDir.FileName; + var apiGeneratorFiles = jarDir.Files("*.java", true); + var classFileDir = new NPath($"artifacts/{jarname}"); + var apiGeneratorJar = new NPath($"build/{jarname}.jar"); + var classFiles = jdk.SetupCompilation(classFileDir, apiGeneratorFiles, new NPath[] { jarDir }, new NPath[0]); + jdk.SetupJar(new NPath[] { classFileDir }, classFiles, apiGeneratorJar); + Backend.Current.AddAliasDependency(jarname, apiGeneratorJar); + return apiGeneratorJar; + } + + struct SourceGenerationParams + { + public string platformName; + public NPath[] inputJars; + public string[] classes; + } + + static SourceGenerationParams GetAndroidSourceGenerationParams(NPath sdk) + { + var androidJar = sdk.Combine("platforms", kAndroidApi, "android.jar"); + var googlePlayServicesJar = sdk.Combine("extras", "google", "google_play_services_froyo", "libproject", "google-play-services_lib", "libs", "google-play-services.jar"); + + return new SourceGenerationParams() + { + platformName = "android", + inputJars = new[] {androidJar, googlePlayServicesJar}, + classes = kAndroidApiClasses, + }; + } + + static SourceGenerationParams GetDesktopSourceGenerationParams(Jdk jdk, string platformName) + { + var rtJar = jdk.JavaHome.Combine("jre", "lib", "rt.jar"); + + return new SourceGenerationParams() + { + platformName = platformName, + inputJars = new [] {rtJar}, + classes = kDesktopClasses, + }; + } + + static SourceGenerationParams GetMacOSSourceGenerationParams(Jdk jdk) + { + return GetDesktopSourceGenerationParams(jdk, Platform.OSX); + } + + static SourceGenerationParams GetWindowsSourceGenerationParams(Jdk jdk) + { + return GetDesktopSourceGenerationParams(jdk, Platform.Windows); + } + + static NPath SetupSourceGeneration(Jdk jdk, NPath apiGenerator, NPath jnibridgeJar, SourceGenerationParams genParams) + { + var destDir = new NPath("artifacts").Combine("generated", genParams.platformName); + var actionName = "generate:jnibridge:" + genParams.platformName; + var generatedFiles = new NPath[] { destDir.Combine("API.h") }; + + var templateFiles = new NPath("templates").Files(new [] { "cpp", "h" }); + var inputs = new List(); + inputs.AddRange(genParams.inputJars); + inputs.Add(jnibridgeJar); + inputs.Add(apiGenerator); + inputs.AddRange(templateFiles); + + string inputJars = string.Empty; + if (genParams.inputJars.Length > 0) + { + var builder = new StringBuilder(); + builder.Append('"').Append(genParams.inputJars[0]); + for (int i = 1; i < genParams.inputJars.Length; ++i) + builder.Append(';').Append(genParams.inputJars[i]); + inputJars = builder.Append('"').ToString(); + } + var apiClassString = string.Join(" ", genParams.classes); + + Backend.Current.AddAction( + actionName, + generatedFiles, + inputs.ToArray(), + jdk.Java.InQuotes(SlashMode.Native), + new[] + { + "-cp", + apiGenerator.InQuotes(), + "APIGenerator", + destDir.InQuotes(), + inputJars, + apiClassString + }, + targetDirectories:new []{destDir} + ); + + Backend.Current.AddAliasDependency(actionName, generatedFiles); + return destDir; + } + + struct StaticLibParams + { + public string libName; + public string platformName; + public string archName; + public Action specialConfiguration; + } + + static StaticLibParams GetAndroidStaticLibParams(ToolChain toolchain) + { + Action specConfig = (toolchain.Architecture is ARMv7Architecture) + ? (np) => + { + np.CompilerSettingsForAndroid().Add(c => c.WithThumb(true)); + np.CompilerSettingsForAndroid().Add(c => c.WithDebugMode(DebugMode.None)); + } + : (np) => + { + np.CompilerSettingsForAndroid().Add(c => c.WithDebugMode(DebugMode.None)); + }; + return new StaticLibParams() + { + libName = kAndroidApi, + platformName = "android", + archName = AndroidNdk.AbiFromArch(toolchain.Architecture), + specialConfiguration = specConfig, + }; + } + + static StaticLibParams GetMacOSStaticLibParams(ToolChain toolchain, Jdk jdk) + { + return new StaticLibParams() + { + libName = "testlib", + platformName = Platform.OSX, + archName = toolchain.Architecture.Name, + specialConfiguration = (np) => + { + np.IncludeDirectories.Add(jdk.JavaHome.Combine("include")); + np.IncludeDirectories.Add(jdk.JavaHome.Combine("include", "darwin")); + }, + }; + } + + static StaticLibParams GetWindowsStaticLibParams(ToolChain toolchain, Jdk jdk) + { + return new StaticLibParams() + { + libName = "testlib", + platformName = Platform.Windows, + archName = toolchain.Architecture.Name, + specialConfiguration = (np) => + { + np.IncludeDirectories.Add(jdk.JavaHome.Combine("include")); + np.IncludeDirectories.Add(jdk.JavaHome.Combine("include", "win32")); + }, + }; + } + + static NativeProgram SetupJniBridgeStaticLib(NPath generatedFilesDir, NativeProgramConfiguration config, StaticLibParams libParams, ZipArchiveContents targetArchive = null) + { + NPath[] generatedSources; + if (!generatedFilesDir.DirectoryExists()) + generatedSources = new[] {generatedFilesDir.Combine("API.h")}; + else + generatedSources = generatedFilesDir.Files("*.cpp"); + + var buildDest = new NPath("build").Combine(libParams.platformName); + + var np = new NativeProgram(libParams.libName); + np.Sources.Add(generatedSources); + np.Sources.Add(CppFiles); + np.IncludeDirectories.Add(generatedFilesDir); + np.ExtraDependenciesForAllObjectFiles.Add(generatedFilesDir); + if (libParams.specialConfiguration != null) + libParams.specialConfiguration(np); + + var destDir = buildDest.Combine(libParams.archName); + var target = np.SetupSpecificConfiguration(config, config.ToolChain.StaticLibraryFormat).DeployTo(destDir); + + var alias = $"build:{libParams.platformName}:{libParams.archName}"; + Backend.Current.AddAliasDependency(alias, target.Paths); + if (targetArchive != null) + { + var destInArchive = destDir.RelativeTo("build"); + foreach (var file in target.Paths) + targetArchive.AddFileToArchive(file, destInArchive.Combine(file.FileName)); + } + + return np; + } + + static NPath[] SetupJniBridgeHeaders(NPath generatedFilesDir, string platformName) + { + var includeFiles = new List(); + var includes = new NPath("build").Combine(platformName, "include"); + + includeFiles.Add(Backend.Current.SetupCopyFile(includes.Combine("API.h"), generatedFilesDir.Combine("API.h"))); + foreach (var file in HeaderFiles) + includeFiles.Add(Backend.Current.SetupCopyFile(includes.Combine(file.FileName), file)); + + var incs = includeFiles.ToArray(); + Backend.Current.AddAliasDependency($"includes:{platformName}", incs); + return incs; + } + + static void SetupTestProgramOsx(ToolChain toolchain, NativeProgram staticLib, CodeGen codegen, NPath generatedFilesDir, Jdk jdk) + { + var np = new NativeProgram("JNIBridgeTests"); + np.Sources.Add(new NPath("test").Files("*.cpp")); + np.IncludeDirectories.Add(generatedFilesDir); + np.IncludeDirectories.Add(jdk.JavaHome.Combine("include")); + np.IncludeDirectories.Add(jdk.JavaHome.Combine("include", "darwin")); + np.Libraries.Add(staticLib); + var javaLibDir = jdk.JavaHome.Combine("jre", "lib", "server"); + np.Libraries.Add(new DynamicLibrary(javaLibDir.Combine("libjvm.dylib"))); + np.CompilerSettings().Add(c => c.WithCustomFlags(new [] {$"-Wl,-rpath,{javaLibDir}"})); + + var destDir = new NPath("build").Combine(Platform.OSX); + var config = new NativeProgramConfiguration(codegen, toolchain, false); + var target = np.SetupSpecificConfiguration(config, config.ToolChain.ExecutableFormat).DeployTo(destDir); + + Backend.Current.AddAliasDependency($"build:{Platform.OSX}:test", target.Paths); + } + + static NativeProgram SetupTestProgramWindows(ToolChain toolchain, NativeProgram staticLib, CodeGen codegen, NPath generatedFilesDir, Jdk jdk, out NPath targetExecutable) + { + var np = new NativeProgram("JNIBridgeTests"); + np.Sources.Add(new NPath("test").Files("*.cpp")); + np.IncludeDirectories.Add(generatedFilesDir); + np.IncludeDirectories.Add(jdk.JavaHome.Combine("include")); + np.IncludeDirectories.Add(jdk.JavaHome.Combine("include", "win32")); + np.Libraries.Add(staticLib); + var javaLibDir = jdk.JavaHome.Combine("lib"); + np.Libraries.Add(new StaticLibrary(javaLibDir.Combine("jvm.lib"))); + + var destDir = new NPath("build").Combine(Platform.Windows); + var config = new NativeProgramConfiguration(codegen, toolchain, false); + var target = np.SetupSpecificConfiguration(config, config.ToolChain.ExecutableFormat).DeployTo(destDir); + + var exeName = np.Name + ".exe"; + var pdbName = np.Name + ".pdb"; + var server = jdk.JavaHome.Combine("jre/bin/server"); + targetExecutable = Backend.Current.SetupCopyFile(server.Combine(exeName), destDir.Combine(exeName)); + var targetPdb = Backend.Current.SetupCopyFile(server.Combine(pdbName), destDir.Combine(pdbName)); + var targetJar = Backend.Current.SetupCopyFile(server.Combine("jnibridge.jar"), "build/jnibridge.jar"); + var script = destDir.Combine("runtests.cmd"); + Backend.Current.AddWriteTextAction(script, $@"echo off +echo Launching tests +{targetExecutable.MakeAbsolute().InQuotes()} +echo Exited with code %ERRORLEVEL% +exit %ERRORLEVEL% +"); + + var targetPaths = new List(target.Paths); + targetPaths.Add(targetExecutable); + targetPaths.Add(targetPdb); + targetPaths.Add(targetJar); + targetPaths.Add(script); + Backend.Current.AddAliasDependency($"build:{Platform.Windows}:test", targetPaths.ToArray()); + + return np; + } + + static string GetABI(Architecture architecture) + { + if (architecture == Architecture.Armv7) + return "armeabi-v7a"; + if (architecture == Architecture.Arm64) + return "arm64-v8a"; + if (architecture == Architecture.x86) + return "x86"; + if (architecture == Architecture.x64) + return "x86_64"; + throw new NotImplementedException(architecture.ToString()); + } + + static void SetupGeneratedProjects(IReadOnlyList programs, NativeProgram testingProgram, NPath generatedFilesDirectory, NPath executableForTests) + { + // Not entirely sure, but I think there's should have been only NativeProgram with different architectures. + // Yet we have 4 + // For now pick the first one, since it will be enough for editing purposes + + var nativeProgram = programs[0]; + var ndkToolchain = (AndroidNdkToolchain)nativeProgram.SetupConfigurations.FirstOrDefault().ToolChain; + var ndkIncludePath = ndkToolchain.Sdk.Path.ResolveWithFileSystem().Combine("sysroot/usr/include"); + + var jniBridgeNP = new NativeProgram("JNIBridge"); + nativeProgram.Sources.CopyTo(jniBridgeNP.Sources); + nativeProgram.IncludeDirectories.CopyTo(jniBridgeNP.IncludeDirectories); + nativeProgram.Defines.CopyTo(jniBridgeNP.Defines); + + jniBridgeNP.IncludeDirectories.Add(ndkIncludePath); + + var configs = new List(); + foreach (var p in programs) + { + configs.AddRange(p.SetupConfigurations.ToArray()); + } + + jniBridgeNP.ValidConfigurations = configs; + jniBridgeNP.OutputName.Set(string.Empty); + + var setAndroidSDKRoot = $"set ANDROID_SDK_ROOT={SetupAndroidSdk()}"; + jniBridgeNP.CommandToBuild.Set(c => + { + var architecture = ((AndroidNdkToolchain)c.ToolChain).Architecture; + return $@"{setAndroidSDKRoot} +bee build:android:{GetABI(architecture)}"; + }); + + var jniBridgeSln = new VisualStudioSolution(); + foreach (var config in configs) + { + var architecture = ((AndroidNdkToolchain)config.ToolChain).Architecture; + + jniBridgeSln.Configurations.Add(new SolutionConfiguration(GetABI(architecture), (configurations, file) => + { + var firstOrDefault = configurations.FirstOrDefault(c => c == config); + return new Tuple( + firstOrDefault ?? configurations.First(), + true); + })); + } + var extraFiles = new List(); + extraFiles.AddRange(HeaderFiles); + extraFiles.AddRange(CppFiles); + extraFiles.AddRange(JavaFiles); + + var builder = new VisualStudioNativeProjectFileBuilder(jniBridgeNP, extraFiles); + builder = jniBridgeNP.ValidConfigurations.Aggregate( + builder, + (current, c) => current.AddProjectConfiguration(c) + ); + + + jniBridgeSln.Projects.Add(builder.DeployTo("JNIBridge.vcxproj")); + + testingProgram.CommandToBuild.Set(c => + { + return @$"{setAndroidSDKRoot} +bee build:windows:test"; + }); + + var extraFilesForTests = new List(); + extraFilesForTests.AddRange(HeaderFiles); + extraFilesForTests.AddRange(CppFiles); + extraFilesForTests.AddRange(JavaFiles); + // Includes generated files for Windows, we don't want those in JNIBridge project since it builds for Android + if (generatedFilesDirectory.DirectoryExists()) + extraFilesForTests.AddRange(generatedFilesDirectory.Files(true)); + + builder = new VisualStudioNativeProjectFileBuilder(testingProgram, extraFilesForTests); + builder = testingProgram.SetupConfigurations.Aggregate( + builder, + (current, c) => current.AddProjectConfiguration(c) + ); + + var jniBridgeTestsSln = new VisualStudioSolution(); + var vsProject = builder.DeployTo("JNIBridgeTests.vcxproj"); + jniBridgeTestsSln.Projects.Add(vsProject); + + var vsProjectUsersSettings = (vsProject.Path + ".user").ToNPath(); + GenerateVSUserSettings(vsProjectUsersSettings, executableForTests); + Backend.Current.AddAliasDependency("projectfiles", jniBridgeSln.Setup()); + Backend.Current.AddAliasDependency("projectfiles", jniBridgeTestsSln.Setup()); + Backend.Current.AddAliasDependency("projectfiles", vsProjectUsersSettings); + Backend.Current.AddAliasDependency("projectfiles", generatedFilesDirectory); + } + + static void GenerateVSUserSettings(NPath path, NPath executablePath) + { + Backend.Current.AddWriteTextAction(path, +$@" + + + {executablePath.MakeAbsolute()} + WindowsLocalDebugger + + +"); + } +} diff --git a/build.pl b/build.pl index c15d7eb..92c2787 100755 --- a/build.pl +++ b/build.pl @@ -1,325 +1,325 @@ -#!/usr/bin/env perl -w -use Cwd qw( abs_path ); -use File::Basename qw( dirname ); -use lib dirname(abs_path($0)); - -use PrepareAndroidSDK; -use File::Path; -use strict; -use warnings; - -my $api = "android-31"; - -my @classes = ( - '::android::Manifest_permission', - '::android::R_attr', - '::android::app::Activity', - '::android::app::AlertDialog_Builder', - '::android::app::NotificationManager', - '::android::app::Presentation', - '::android::content::Context', - '::android::graphics::Color', - '::android::graphics::ImageFormat', - '::android::graphics::drawable::ColorDrawable', - '::android::hardware::display::DisplayManager', - '::android::hardware::Camera', - '::android::hardware::input::InputManager', - '::android::hardware::GeomagneticField', - '::android::location::LocationManager', - '::android::media::AudioAttributes_Builder', - '::android::media::AudioFocusRequest_Builder', - '::android::media::AudioManager', - '::android::media::MediaCodec', - '::android::media::MediaCodec::BufferInfo', - '::android::media::MediaExtractor', - '::android::media::MediaFormat', - '::android::media::MediaRouter', - '::android::net::ConnectivityManager', - '::android::net::wifi::WifiManager', - '::android::os::Build', - '::android::os::Build_VERSION', - '::android::os::HandlerThread', - '::android::os::Environment', - '::android::os::PowerManager', - '::android::os::Process', - '::android::os::Vibrator', - '::android::provider::Settings_Secure', - '::android::provider::Settings_System', - '::android::telephony::TelephonyManager', - '::android::telephony::SubscriptionManager', - '::android::telephony::SubscriptionInfo', - '::android::view::Choreographer', - '::android::view::Display', - '::android::view::Gravity', - '::android::view::SurfaceView', - '::android::view::WindowManager', - '::android::webkit::MimeTypeMap', - '::android::widget::CheckBox', - '::android::widget::CompoundButton_OnCheckedChangeListener', - '::android::widget::ProgressBar', - '::java::lang::Character', - '::java::lang::System', - '::java::lang::SecurityException', - '::java::lang::NoSuchMethodError', - '::java::lang::ClassCastException', - '::java::lang::UnsatisfiedLinkError', - '::java::io::FileNotFoundException', - '::java::net::HttpURLConnection', - '::java::nio::channels::Channels', - '::java::util::HashSet', - '::java::util::Map_Entry', - '::java::util::NoSuchElementException', - '::java::util::Scanner', - '::java::util::zip::ZipFile', - '::javax::net::ssl::X509TrustManager', - '::javax::net::ssl::TrustManagerFactory', - '::java::security::KeyStore', - - '::com::google::android::gms::ads::identifier::AdvertisingIdClient', - '::com::google::android::gms::common::GooglePlayServicesAvailabilityException', - '::com::google::android::gms::common::GooglePlayServicesNotAvailableException', -); - -# Set ANDROID_SDK_ROOT -sub Prepare -{ - my $class_names = join(' ', @classes); - my $threads = 8; - - PrepareAndroidSDK::GetAndroidSDK("$api", "21", "froyo", "r21", "24"); - - #system("make clean") && die("Clean failed"); - #system("make api-source PLATFORM=android APINAME=\"$api\" APICLASSES=\"$class_names\"") && die("Failed to make API source"); - #system("make api-module PLATFORM=android APINAME=\"$api\" APICLASSES=\"$class_names\"") && die("Failed to make API module"); - #system("make compile-static-apilib -j$threads PLATFORM=android ABI=armeabi-v7a APINAME=\"$api\" APICLASSES=\"$class_names\"") && die("Failed to make android armv7 library"); - #system("make compile-static-apilib -j$threads PLATFORM=android ABI=arm64-v8a APINAME=\"$api\" APICLASSES=\"$class_names\"") && die("Failed to make android arm64 library"); - #system("make compile-static-apilib -j$threads PLATFORM=android ABI=x86 APINAME=\"$api\" APICLASSES=\"$class_names\"") && die("Failed to make android x86 library"); - #system("make compile-static-apilib -j$threads PLATFORM=android ABI=x86_64 APINAME=\"$api\" APICLASSES=\"$class_names\"") && die("Failed to make android x86_64 library"); -} - -sub ZipIt -{ - system("mkdir -p build/temp/include") && die("Failed to create temp directory."); - - # write build info - my $git_info = "$ENV{GIT_BRANCH}\n$ENV{GIT_REVISION}\n$ENV{GIT_REPOSITORY_URL}"; - open(BUILD_INFO_FILE, '>', "build/temp/build.txt") or die("Unable to write build information to build/temp/build.txt"); - print BUILD_INFO_FILE "$git_info"; - close(BUILD_INFO_FILE); - - # create zip - system("cp build/$api/source/*.h build/temp/include") && die("Failed to copy headers."); - system("cd build && jar cf temp/jnibridge.jar bitter") && die("Failed to create java class archive."); - system("cd build/$api && zip ../builds.zip -r android/*/*.a") && die("Failed to package libraries into zip file."); - system("cd build/temp && zip ../builds.zip -r build.txt jnibridge.jar include") && die("Failed to package zip file."); - system("rm -r build/temp") && die("Unable to remove temp directory."); -} - -my $stringBuildJniBridgeAndZipIt = "Build JNIBridge and Zip It"; -my $stringBuildJniBridge = "Build JNIBridge"; -my $stringGenerateVSProjectFiles = "Generate Visual Studio project files"; -my $stringTestOnOSX = "Test JNIBridge"; -my $stringHelp = "Show command line arguments"; -my @abis = ("armeabi-v7a", "arm64-v8a", "x86", "x86_64"); - -sub ShowMenu -{ - print("(b) ${stringBuildJniBridgeAndZipIt}\n"); - foreach my $abi ( @abis ) - { - print("(b $abi) ${stringBuildJniBridge} $abi\n"); - } - - print("(p) ${stringGenerateVSProjectFiles}\n"); - print("(t) ${stringTestOnOSX}\n"); - print("(h) ${stringHelp}\n"); - print("(q) Exit\n"); - print("\n"); -} - -sub ShowCommandLineArgs -{ - print("build.pl jnibridge\n ${stringBuildJniBridgeAndZipIt}\n\n"); - foreach my $abi ( @abis ) - { - print("build.pl jnibridge $abi\n ${stringBuildJniBridge} $abi\n\n"); - } - - print("build.pl projectfiles\n ${stringGenerateVSProjectFiles}\n\n"); - print("build.pl test\n ${stringTestOnOSX}\n\n"); - print("build.pl help\n ${stringHelp}\n\n"); -} - -sub GetBeeExecutable -{ - if (lc $^O eq 'darwin') - { - return "./bee"; - } - elsif (lc $^O eq 'linux') - { - return "./bee"; - } - elsif (lc $^O eq 'mswin32') - { - return "bee"; - } - else - { - die "Coudln't get Bee executable for " . $^O; - } -} - -my $Bee = GetBeeExecutable(); -my ($arg1, $arg2) = @ARGV; -my $quitAfterCommand = 0; - -while (1) -{ - if (not defined $arg1) - { - ShowMenu(); - my $pick = ; - - if ($pick) - { - chomp($pick); - } - else - { - # null stdin probably means ctrl-c - print("Ctrl + C detected, quitting\n"); - $pick = "q"; - } - - if ($pick eq "b") - { - $arg1 = "jnibridge"; - } - - foreach my $abi ( @abis ) - { - if ($pick eq "b $abi") - { - $arg1 = "jnibridge"; - $arg2 = $abi; - } - } - - - if ($pick eq "p") - { - $arg1 = "projectfiles"; - } - if ($pick eq "t") - { - $arg1 = "test"; - } - if ($pick eq "h") - { - $arg1 = "help"; - } - if ($pick eq "q") - { - $arg1 = "quit"; - } - - if (not defined $arg1) - { - $arg1 = "unknownshortcut"; - $arg2 = "Unknown command '${pick}'"; - } - - $quitAfterCommand = 0; - } - else - { - $quitAfterCommand = 1; - } - - - print("\nArguments:\n ${arg1}"); - if (defined $arg2) - { - print(" $arg2"); - } - print("\n\n"); - - if ($arg1 eq "jnibridge") - { - print("Building JNIBridge\n"); - Prepare(); - if (not defined $arg2) - { - system("${Bee} build:android:zip") && die("Couldn't build JNIBridge"); - } - else - { - my $foundCorrectABI = 0; - foreach my $abi ( @abis ) - { - if ($arg2 eq $abi) - { - $foundCorrectABI = 1; - system("${Bee} build:android:${arg2}") && die("Couldn't build JNIBridge ${arg2}"); - } - } - - if (not $foundCorrectABI) - { - print("Unknown jnibridge ABI '${arg2}'"); - } - } - - } - elsif ($arg1 eq "projectfiles") - { - print("Generating Visual Studio Projects JNIBridge\n"); - Prepare(); - system("${Bee} projectfiles") && die("Couldn't generate Visual Studio project files"); - } - elsif ($arg1 eq "test") - { - print("Building and testing JNIBridge\n"); - Prepare(); - - if (lc $^O eq 'darwin') - { - system("${Bee} build:osx:test") && die("Couldn't build JNIBridge for testing"); - system("./build/osx/JNIBridgeTests") && die("Test failed"); - } - elsif (lc $^O eq 'mswin32') - { - system("${Bee} build:windows:test") && die("Couldn't build JNIBridge for testing"); - system("build\\windows\\runtests.cmd") && die("Test failed"); - } - } - elsif ($arg1 eq "help") - { - ShowCommandLineArgs(); - } - elsif ($arg1 eq "quit") - { - exit(); - } - elsif ($arg1 eq "unknownshortcut") - { - print($arg2); - } - else - { - die("Unknown command ${arg1}\n"); - } - - - print("\n\nCommand finished.\n\n"); - - if ($quitAfterCommand) - { - exit(); - } - - $arg1 = undef(); - $arg2 = undef(); -} - +#!/usr/bin/env perl -w +use Cwd qw( abs_path ); +use File::Basename qw( dirname ); +use lib dirname(abs_path($0)); + +use PrepareAndroidSDK; +use File::Path; +use strict; +use warnings; + +my $api = "android-31"; + +my @classes = ( + '::android::Manifest_permission', + '::android::R_attr', + '::android::app::Activity', + '::android::app::AlertDialog_Builder', + '::android::app::NotificationManager', + '::android::app::Presentation', + '::android::content::Context', + '::android::graphics::Color', + '::android::graphics::ImageFormat', + '::android::graphics::drawable::ColorDrawable', + '::android::hardware::display::DisplayManager', + '::android::hardware::Camera', + '::android::hardware::input::InputManager', + '::android::hardware::GeomagneticField', + '::android::location::LocationManager', + '::android::media::AudioAttributes_Builder', + '::android::media::AudioFocusRequest_Builder', + '::android::media::AudioManager', + '::android::media::MediaCodec', + '::android::media::MediaCodec::BufferInfo', + '::android::media::MediaExtractor', + '::android::media::MediaFormat', + '::android::media::MediaRouter', + '::android::net::ConnectivityManager', + '::android::net::wifi::WifiManager', + '::android::os::Build', + '::android::os::Build_VERSION', + '::android::os::HandlerThread', + '::android::os::Environment', + '::android::os::PowerManager', + '::android::os::Process', + '::android::os::Vibrator', + '::android::provider::Settings_Secure', + '::android::provider::Settings_System', + '::android::telephony::TelephonyManager', + '::android::telephony::SubscriptionManager', + '::android::telephony::SubscriptionInfo', + '::android::view::Choreographer', + '::android::view::Display', + '::android::view::Gravity', + '::android::view::SurfaceView', + '::android::view::WindowManager', + '::android::webkit::MimeTypeMap', + '::android::widget::CheckBox', + '::android::widget::CompoundButton_OnCheckedChangeListener', + '::android::widget::ProgressBar', + '::java::lang::Character', + '::java::lang::System', + '::java::lang::SecurityException', + '::java::lang::NoSuchMethodError', + '::java::lang::ClassCastException', + '::java::lang::UnsatisfiedLinkError', + '::java::io::FileNotFoundException', + '::java::net::HttpURLConnection', + '::java::nio::channels::Channels', + '::java::util::HashSet', + '::java::util::Map_Entry', + '::java::util::NoSuchElementException', + '::java::util::Scanner', + '::java::util::zip::ZipFile', + '::javax::net::ssl::X509TrustManager', + '::javax::net::ssl::TrustManagerFactory', + '::java::security::KeyStore', + + '::com::google::android::gms::ads::identifier::AdvertisingIdClient', + '::com::google::android::gms::common::GooglePlayServicesAvailabilityException', + '::com::google::android::gms::common::GooglePlayServicesNotAvailableException', +); + +# Set ANDROID_SDK_ROOT +sub Prepare +{ + my $class_names = join(' ', @classes); + my $threads = 8; + + PrepareAndroidSDK::GetAndroidSDK("$api", "21", "froyo", "r21", "24"); + + #system("make clean") && die("Clean failed"); + #system("make api-source PLATFORM=android APINAME=\"$api\" APICLASSES=\"$class_names\"") && die("Failed to make API source"); + #system("make api-module PLATFORM=android APINAME=\"$api\" APICLASSES=\"$class_names\"") && die("Failed to make API module"); + #system("make compile-static-apilib -j$threads PLATFORM=android ABI=armeabi-v7a APINAME=\"$api\" APICLASSES=\"$class_names\"") && die("Failed to make android armv7 library"); + #system("make compile-static-apilib -j$threads PLATFORM=android ABI=arm64-v8a APINAME=\"$api\" APICLASSES=\"$class_names\"") && die("Failed to make android arm64 library"); + #system("make compile-static-apilib -j$threads PLATFORM=android ABI=x86 APINAME=\"$api\" APICLASSES=\"$class_names\"") && die("Failed to make android x86 library"); + #system("make compile-static-apilib -j$threads PLATFORM=android ABI=x86_64 APINAME=\"$api\" APICLASSES=\"$class_names\"") && die("Failed to make android x86_64 library"); +} + +sub ZipIt +{ + system("mkdir -p build/temp/include") && die("Failed to create temp directory."); + + # write build info + my $git_info = "$ENV{GIT_BRANCH}\n$ENV{GIT_REVISION}\n$ENV{GIT_REPOSITORY_URL}"; + open(BUILD_INFO_FILE, '>', "build/temp/build.txt") or die("Unable to write build information to build/temp/build.txt"); + print BUILD_INFO_FILE "$git_info"; + close(BUILD_INFO_FILE); + + # create zip + system("cp build/$api/source/*.h build/temp/include") && die("Failed to copy headers."); + system("cd build && jar cf temp/jnibridge.jar bitter") && die("Failed to create java class archive."); + system("cd build/$api && zip ../builds.zip -r android/*/*.a") && die("Failed to package libraries into zip file."); + system("cd build/temp && zip ../builds.zip -r build.txt jnibridge.jar include") && die("Failed to package zip file."); + system("rm -r build/temp") && die("Unable to remove temp directory."); +} + +my $stringBuildJniBridgeAndZipIt = "Build JNIBridge and Zip It"; +my $stringBuildJniBridge = "Build JNIBridge"; +my $stringGenerateVSProjectFiles = "Generate Visual Studio project files"; +my $stringTestOnOSX = "Test JNIBridge"; +my $stringHelp = "Show command line arguments"; +my @abis = ("armeabi-v7a", "arm64-v8a", "x86", "x86_64"); + +sub ShowMenu +{ + print("(b) ${stringBuildJniBridgeAndZipIt}\n"); + foreach my $abi ( @abis ) + { + print("(b $abi) ${stringBuildJniBridge} $abi\n"); + } + + print("(p) ${stringGenerateVSProjectFiles}\n"); + print("(t) ${stringTestOnOSX}\n"); + print("(h) ${stringHelp}\n"); + print("(q) Exit\n"); + print("\n"); +} + +sub ShowCommandLineArgs +{ + print("build.pl jnibridge\n ${stringBuildJniBridgeAndZipIt}\n\n"); + foreach my $abi ( @abis ) + { + print("build.pl jnibridge $abi\n ${stringBuildJniBridge} $abi\n\n"); + } + + print("build.pl projectfiles\n ${stringGenerateVSProjectFiles}\n\n"); + print("build.pl test\n ${stringTestOnOSX}\n\n"); + print("build.pl help\n ${stringHelp}\n\n"); +} + +sub GetBeeExecutable +{ + if (lc $^O eq 'darwin') + { + return "./bee"; + } + elsif (lc $^O eq 'linux') + { + return "./bee"; + } + elsif (lc $^O eq 'mswin32') + { + return "bee"; + } + else + { + die "Coudln't get Bee executable for " . $^O; + } +} + +my $Bee = GetBeeExecutable(); +my ($arg1, $arg2) = @ARGV; +my $quitAfterCommand = 0; + +while (1) +{ + if (not defined $arg1) + { + ShowMenu(); + my $pick = ; + + if ($pick) + { + chomp($pick); + } + else + { + # null stdin probably means ctrl-c + print("Ctrl + C detected, quitting\n"); + $pick = "q"; + } + + if ($pick eq "b") + { + $arg1 = "jnibridge"; + } + + foreach my $abi ( @abis ) + { + if ($pick eq "b $abi") + { + $arg1 = "jnibridge"; + $arg2 = $abi; + } + } + + + if ($pick eq "p") + { + $arg1 = "projectfiles"; + } + if ($pick eq "t") + { + $arg1 = "test"; + } + if ($pick eq "h") + { + $arg1 = "help"; + } + if ($pick eq "q") + { + $arg1 = "quit"; + } + + if (not defined $arg1) + { + $arg1 = "unknownshortcut"; + $arg2 = "Unknown command '${pick}'"; + } + + $quitAfterCommand = 0; + } + else + { + $quitAfterCommand = 1; + } + + + print("\nArguments:\n ${arg1}"); + if (defined $arg2) + { + print(" $arg2"); + } + print("\n\n"); + + if ($arg1 eq "jnibridge") + { + print("Building JNIBridge\n"); + Prepare(); + if (not defined $arg2) + { + system("${Bee} build:android:zip") && die("Couldn't build JNIBridge"); + } + else + { + my $foundCorrectABI = 0; + foreach my $abi ( @abis ) + { + if ($arg2 eq $abi) + { + $foundCorrectABI = 1; + system("${Bee} build:android:${arg2}") && die("Couldn't build JNIBridge ${arg2}"); + } + } + + if (not $foundCorrectABI) + { + print("Unknown jnibridge ABI '${arg2}'"); + } + } + + } + elsif ($arg1 eq "projectfiles") + { + print("Generating Visual Studio Projects JNIBridge\n"); + Prepare(); + system("${Bee} projectfiles") && die("Couldn't generate Visual Studio project files"); + } + elsif ($arg1 eq "test") + { + print("Building and testing JNIBridge\n"); + Prepare(); + + if (lc $^O eq 'darwin') + { + system("${Bee} build:osx:test") && die("Couldn't build JNIBridge for testing"); + system("./build/osx/JNIBridgeTests") && die("Test failed"); + } + elsif (lc $^O eq 'mswin32') + { + system("${Bee} build:windows:test") && die("Couldn't build JNIBridge for testing"); + system("build\\windows\\runtests.cmd") && die("Test failed"); + } + } + elsif ($arg1 eq "help") + { + ShowCommandLineArgs(); + } + elsif ($arg1 eq "quit") + { + exit(); + } + elsif ($arg1 eq "unknownshortcut") + { + print($arg2); + } + else + { + die("Unknown command ${arg1}\n"); + } + + + print("\n\nCommand finished.\n\n"); + + if ($quitAfterCommand) + { + exit(); + } + + $arg1 = undef(); + $arg2 = undef(); +} +