From 9302d514a1437e5d1977da06dfbbe4e7054f33d5 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Fri, 7 Jun 2019 10:58:59 -0700 Subject: [PATCH] [build] Xamarin.Android build preparation utility (#3051) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: https://github.com/xamarin/xamarin-android/issues/2562 The `make prepare` **make** target and `msbuild /t:Prepare` MSBuild target are used to "prepare" a `xamarin-android` checkout so that the subsequent `make all` or `msbuild /t:Build` invocation will work. The preparation step involves ensuring that all known required dependencies exist or are provisioned (54a7a029), such as the build utilities (`autoconf`/etc.), cross-compilers (MinGW), the Android SDK and NDK, and building "enough" to allow `Xamarin.Android.sln` to build (see also 7343965a0). Unfortunately, the Glorious Idea™ of using MSBuild for significant portions of this process is "baroque", making it difficult to understand and maintain, and the requirement on `msbuild` means that things break in really bizarre and obscure ways on Linux and macOS if the installed `mono` version is "too old"...and the determination of "too old" was being done via `mono` & `msbuild`! Furthermore, there was duplication and repetition of various bits of build configuration on Linux and macOS between Makefile and the MSBuild project system, which made it easier to make mistakes. (Then there's the occasional bizarro failures that would result from attempting to evaluate `$(PRODUCT_VERSION)` within `make`; it would fail when `MONO_LOG_LEVEL=info` was set. Then there's...) Instead of having `make prepare` be implemented as an unholy mess of `make` and `msbuild`, introduce the new `build-tools/xaprepare` app. `xaprepare` will be built and executed as part of `make prepare`, and takes over responsibility for repo build preparation. See also: build-tools/xaprepare/README.md TODO: Windows support --- CODEOWNERS | 1 + Configuration.props | 12 +- Makefile | 268 +++--- ThirdPartyNotices.txt | 25 +- Xamarin.Android.sln | 39 - .../result-packaging.targets | 2 + .../android-toolchain.csproj | 39 - .../android-toolchain.projitems | 169 ---- .../android-toolchain.targets | 111 --- build-tools/android-toolchain/packages.config | 3 - build-tools/automation/azure-pipelines.yaml | 53 +- build-tools/automation/build.groovy | 7 +- build-tools/automation/build.linux.groovy | 2 +- build-tools/create-bundle/bundle-path.targets | 38 - .../create-bundle/create-bundle.csproj | 52 -- .../create-bundle/create-bundle.targets | 28 - build-tools/dependencies/dependencies.csproj | 30 - .../dependencies/dependencies.projitems | 45 - build-tools/dependencies/dependencies.targets | 5 - .../download-bundle/download-bundle.targets | 5 +- .../fetch-windows-assemblers.cs | 738 --------------- .../fetch-windows-assemblers.csproj | 40 - .../installers/create-installers.targets | 2 +- build-tools/license-data/Mono-MIT.txt | 21 + .../mingw-dependencies.csproj | 28 - .../mingw-dependencies.projitems | 56 -- .../mingw-dependencies.targets | 89 -- build-tools/scripts/BuildEverything.mk | 134 +-- build-tools/scripts/Ndk.projitems.in | 36 + build-tools/scripts/Ndk.targets | 37 +- build-tools/scripts/Packaging.mk | 8 - build-tools/scripts/PrepareWindows.targets | 97 +- .../bundle-path.targets.in} | 3 +- .../scripts/dependencies/debian-common.sh | 46 - .../dependencies/linux-prepare-Arch.sh | 57 -- .../dependencies/linux-prepare-Debian.sh | 8 - .../dependencies/linux-prepare-LinuxMint.sh | 16 - .../dependencies/linux-prepare-Ubuntu.sh | 17 - .../scripts/dependencies/ubuntu-common.sh | 11 - build-tools/scripts/runtime-helpers.mk | 1 - .../ReplaceFileContents.cs | 14 +- build-tools/xaprepare/.gitignore | 405 ++++++++ build-tools/xaprepare/README.md | 173 ++++ build-tools/xaprepare/xaprepare.sln | 17 + build-tools/xaprepare/xaprepare/App.config | 6 + .../xaprepare/Application/Abi.Bitness.cs | 12 + .../xaprepare/xaprepare/Application/Abi.OS.cs | 12 + .../xaprepare/xaprepare/Application/Abi.cs | 119 +++ .../xaprepare/Application/AbiType.cs | 11 + .../xaprepare/Application/AndroidPlatform.cs | 41 + .../Application/AndroidToolchainComponent.cs | 37 + .../xaprepare/Application/AppObject.cs | 17 + .../xaprepare/Application/BclFile.cs | 65 ++ .../xaprepare/Application/BclFileTarget.cs | 23 + .../xaprepare/Application/BclFileType.cs | 8 + .../xaprepare/Application/BuildInfo.cs | 262 ++++++ .../xaprepare/Application/BundleItem.cs | 32 + .../xaprepare/Application/Characters.cs | 61 ++ .../Application/CompressionFormat.cs | 27 + .../xaprepare/Application/Context.Linux.cs | 10 + .../xaprepare/Application/Context.MacOS.cs | 10 + .../xaprepare/Application/Context.Windows.cs | 16 + .../xaprepare/Application/Context.cs | 845 +++++++++++++++++ .../DetermineWindowsVersion.Windows.cs | 160 ++++ .../xaprepare/Application/DownloadStatus.cs | 77 ++ .../Application/EssentialTools.Linux.cs | 12 + .../Application/EssentialTools.MacOS.cs | 25 + .../Application/EssentialTools.Unix.cs | 8 + .../xaprepare/Application/EssentialTools.cs | 31 + .../xaprepare/Application/ExecutionMode.cs | 9 + ...nsions.DictionaryOfProgramVersionParser.cs | 22 + .../Extensions.ListOfBundleItem.cs | 124 +++ .../Application/Extensions.Runtime.cs | 10 + .../Application/ExternalGitDependency.cs | 64 ++ .../xaprepare/Application/GeneratedFile.cs | 37 + .../GeneratedMakeRulesFile.MacOS.cs | 13 + .../Application/GeneratedMakeRulesFile.cs | 229 +++++ .../Application/GeneratedPlaceholdersFile.cs | 46 + ...GeneratedProfileAssembliesProjitemsFile.cs | 183 ++++ .../Application/HomebrewProgram.MacOS.cs | 153 +++ .../xaprepare/Application/KnownConditions.cs | 22 + .../xaprepare/Application/KnownProperties.cs | 41 + .../Application/LlvmRuntime.Linux.cs | 9 + .../Application/LlvmRuntime.MacOS.cs | 9 + .../Application/LlvmRuntime.Windows.cs | 9 + .../xaprepare/Application/LlvmRuntime.cs | 24 + .../xaprepare/Application/Log.Unix.cs | 24 + .../xaprepare/Application/Log.Windows.cs | 65 ++ .../xaprepare/xaprepare/Application/Log.cs | 354 +++++++ .../xaprepare/Application/LoggingVerbosity.cs | 11 + .../xaprepare/Application/MonoCrossRuntime.cs | 37 + .../Application/MonoHostRuntime.Linux.cs | 11 + .../xaprepare/Application/MonoHostRuntime.cs | 56 ++ .../xaprepare/Application/MonoJitRuntime.cs | 25 + .../Application/MonoPkgProgram.MacOS.cs | 48 + .../xaprepare/Application/MonoRuntime.cs | 37 + .../xaprepare/Application/MonoUtilityFile.cs | 27 + .../xaprepare/Application/PkgProgram.MacOS.cs | 99 ++ .../xaprepare/Application/ProcessRunner.cs | 318 +++++++ .../ProcessStandardStreamWrapper.cs | 114 +++ .../Application/Program.ArchLinux.cs | 29 + .../Application/Program.DebianLinux.cs | 68 ++ .../xaprepare/Application/Program.Linux.cs | 33 + .../xaprepare/Application/Program.cs | 155 ++++ .../Application/ProgramVersionParser.cs | 69 ++ .../Application/Properties.Defaults.cs.in | 54 ++ .../xaprepare/Application/Properties.cs | 77 ++ .../Application/PropertiesChangedEventArgs.cs | 21 + .../Application/RegexProgramVersionParser.cs | 89 ++ .../Application/RuleGeneratorDelegate.cs | 6 + .../xaprepare/Application/Runtime.cs | 52 ++ .../xaprepare/Application/RuntimeFile.cs | 56 ++ .../xaprepare/Application/RuntimeFileType.cs | 9 + .../xaprepare/Application/Scenario.cs | 74 ++ .../Application/ScenarioAttribute.cs | 15 + .../Application/ScenarioNoStandardEndSteps.cs | 20 + .../xaprepare/Application/SimpleActionStep.cs | 23 + .../xaprepare/Application/SizeFormatter.cs | 44 + .../xaprepare/xaprepare/Application/Step.cs | 71 ++ .../Application/StepWithDownloadProgress.cs | 55 ++ .../xaprepare/Application/TPNAttribute.cs | 11 + .../xaprepare/Application/TestAssembly.cs | 21 + .../xaprepare/Application/TestAssemblyType.cs | 11 + .../Application/ThirdPartyLicenseType.cs | 9 + .../xaprepare/Application/ThirdPartyNotice.cs | 32 + .../Application/ThirdPartyNoticeGroup.cs | 20 + .../Application/ThumbTwiddler.Unix.cs | 15 + .../Application/ThumbTwiddler.Windows.cs | 32 + .../xaprepare/Application/ThumbTwiddler.cs | 144 +++ .../xaprepare/Application/Utilities.Unix.cs | 45 + .../Application/Utilities.Windows.cs | 130 +++ .../xaprepare/Application/Utilities.cs | 835 +++++++++++++++++ .../xaprepare/Application/VersionFetchers.cs | 42 + .../xaprepare/xaprepare/BuildInfo.cs.in | 13 + .../xaprepare/ConfigAndData/AbiNames.cs | 153 +++ .../ConfigAndData/BuildAndroidPlatforms.cs | 49 + .../xaprepare/ConfigAndData/CommonLicenses.cs | 14 + .../ConfigAndData/Configurables.Linux.cs | 22 + .../ConfigAndData/Configurables.MacOS.cs | 28 + .../ConfigAndData/Configurables.Unix.cs | 22 + .../ConfigAndData/Configurables.Windows.cs | 55 ++ .../xaprepare/ConfigAndData/Configurables.cs | 414 +++++++++ .../Dependencies/AndroidToolchain.Linux.cs | 12 + .../Dependencies/AndroidToolchain.MacOS.cs | 8 + .../Dependencies/AndroidToolchain.Windows.cs | 8 + .../Dependencies/AndroidToolchain.cs | 62 ++ .../ConfigAndData/Dependencies/Linux.Arch.cs | 60 ++ .../Dependencies/Linux.Debian.cs | 67 ++ .../Dependencies/Linux.DebianCommon.cs | 51 + .../ConfigAndData/Dependencies/Linux.Mint.cs | 10 + .../Dependencies/Linux.Ubuntu.cs | 52 ++ .../Dependencies/Linux.UbuntuCommon.cs | 42 + .../ConfigAndData/Dependencies/MacOS.cs | 47 + .../ConfigAndData/Dependencies/Windows.cs | 10 + .../xaprepare/ConfigAndData/Runtimes.MacOS.cs | 15 + .../xaprepare/ConfigAndData/Runtimes.Unix.cs | 15 + .../xaprepare/ConfigAndData/Runtimes.cs | 869 ++++++++++++++++++ build-tools/xaprepare/xaprepare/Main.cs | 302 ++++++ .../OperatingSystems/Linux.UbuntuCommon.cs | 26 + .../xaprepare/OperatingSystems/Linux.cs | 149 +++ .../xaprepare/OperatingSystems/MacOS.cs | 107 +++ .../xaprepare/OperatingSystems/OS.cs | 529 +++++++++++ .../xaprepare/OperatingSystems/Unix.cs | 142 +++ .../xaprepare/OperatingSystems/Windows.cs | 129 +++ .../xaprepare/Properties/AssemblyInfo.cs | 26 + .../Configuration.OperatingSystem.props.in | 24 + .../Resources/JdkInfo.Windows.props.in | 19 + .../Scenarios/Scenario_AndroidToolchain.cs | 24 + ...Scenario_PrepareExternalGitDependencies.cs | 26 + .../Scenario_PrepareImageDependencies.cs | 23 + .../xaprepare/Scenarios/Scenario_Required.cs | 12 + .../Scenarios/Scenario_Standard.Unix.cs | 28 + .../Scenarios/Scenario_Standard.Windows.cs | 15 + .../xaprepare/Scenarios/Scenario_Standard.cs | 36 + .../Scenarios/Scenario_UpdateMono.Unix.cs | 33 + .../xaprepare/Steps/MonoRuntimesHelpers.cs | 184 ++++ .../xaprepare/Steps/Step_Android_SDK_NDK.cs | 268 ++++++ .../xaprepare/Steps/Step_BuildLibZip.MacOS.cs | 62 ++ .../Steps/Step_BuildLibZipForWindows.MacOS.cs | 10 + .../Steps/Step_BuildLibZipForWindows.Unix.cs | 147 +++ .../Steps/Step_BuildMingwDependencies.cs | 144 +++ .../Steps/Step_BuildMonoRuntimes.Unix.cs | 639 +++++++++++++ .../xaprepare/Steps/Step_CreateBundle.Unix.cs | 52 ++ .../xaprepare/Steps/Step_DownloadNuGet.cs | 51 + .../Steps/Step_GenerateFiles.Unix.cs | 13 + .../Steps/Step_GenerateFiles.Windows.cs | 37 + .../xaprepare/Steps/Step_GenerateFiles.cs | 195 ++++ .../Steps/Step_Get_Windows_GAS.Unix.cs | 740 +++++++++++++++ .../Steps/Step_InstallAnt.Windows.cs | 70 ++ .../Step_InstallCorrettoOpenJDK.Linux.cs | 14 + .../Step_InstallCorrettoOpenJDK.MacOS.cs | 13 + .../Step_InstallCorrettoOpenJDK.Windows.cs | 17 + .../Steps/Step_InstallCorrettoOpenJDK.cs | 160 ++++ .../Steps/Step_PrepareBundle.MacOS.cs | 10 + .../Steps/Step_PrepareBundle.Unix.cs | 25 + .../Steps/Step_PrepareBundle.Windows.cs | 12 + .../xaprepare/Steps/Step_PrepareBundle.cs | 93 ++ .../Steps/Step_PrepareExternal.Unix.cs | 54 ++ .../Steps/Step_PrepareExternal.Windows.cs | 32 + .../xaprepare/Steps/Step_PrepareExternal.cs | 63 ++ .../Step_PrepareExternalGitDependencies.cs | 86 ++ .../Step_PrepareImageDependencies.MacOS.cs | 27 + .../Step_PrepareImageDependencies.Unix.cs | 33 + .../Steps/Step_PrepareImageDependencies.cs | 73 ++ .../xaprepare/Steps/Step_PrepareLocal.cs | 41 + .../xaprepare/Steps/Step_PrepareMSBuild.cs | 21 + .../xaprepare/Steps/Step_PrepareProps.cs | 51 + .../xaprepare/Steps/Step_ThirdPartyNotices.cs | 203 ++++ .../ThirdPartyNotices/Java.Interop.cs | 141 +++ .../ThirdPartyNotices/LibZipSharp.cs | 20 + .../Xamarin.Android.Build.Tasks.cs | 179 ++++ .../Xamarin.Android.NunitLite.cs | 42 + .../Xamarin.Android.Tools.Aidl.cs | 39 + .../Xamarin.Android.Tools.JavadocImporter.cs | 21 + .../xaprepare/ThirdPartyNotices/aapt2.cs | 19 + .../xaprepare/ThirdPartyNotices/bundletool.cs | 19 + .../xaprepare/ThirdPartyNotices/libzip.cs | 20 + .../xaprepare/ThirdPartyNotices/mono.cs | 265 ++++++ .../xaprepare/ThirdPartyNotices/monodroid.cs | 37 + .../xaprepare/ThirdPartyNotices/opentk.cs | 20 + .../xaprepare/ThirdPartyNotices/proguard.cs | 19 + .../xaprepare/ThirdPartyNotices/r8.cs | 57 ++ .../xaprepare/ThirdPartyNotices/sqlite.cs | 33 + .../xaprepare/ToolRunners/BrewRunner.MacOS.cs | 133 +++ .../ToolRunners/CMakeRunner.OutputSink.cs | 19 + .../xaprepare/ToolRunners/CMakeRunner.cs | 53 ++ .../GitRunner.BlamePorcelainEntry.cs | 157 ++++ .../ToolRunners/GitRunner.OutputSink.cs | 23 + .../xaprepare/ToolRunners/GitRunner.cs | 265 ++++++ .../ToolRunners/MSBuildRunner.OutputSink.cs | 23 + .../xaprepare/ToolRunners/MSBuildRunner.cs | 67 ++ .../xaprepare/ToolRunners/MakeRunner.Linux.cs | 7 + .../xaprepare/ToolRunners/MakeRunner.MacOS.cs | 30 + .../ToolRunners/MakeRunner.OutputSink.Unix.cs | 19 + .../xaprepare/ToolRunners/MakeRunner.Unix.cs | 111 +++ .../ToolRunners/NinjaRunner.OutputSink.cs | 33 + .../xaprepare/ToolRunners/NinjaRunner.cs | 70 ++ .../ToolRunners/NuGetRunner.OutputSink.cs | 47 + .../xaprepare/ToolRunners/NuGetRunner.cs | 51 + .../ToolRunners/PkgutilRunner.MacOS.cs | 84 ++ .../ToolRunners/SevenZipRunner.OutputSink.cs | 28 + .../xaprepare/ToolRunners/SevenZipRunner.cs | 148 +++ .../ToolRunners/TarRunner.OutputSink.cs | 26 + .../xaprepare/ToolRunners/TarRunner.cs | 51 + .../ToolRunners/ToolRunner.OutputSink.cs | 40 + .../xaprepare/ToolRunners/ToolRunner.cs | 167 ++++ build-tools/xaprepare/xaprepare/app.manifest | 76 ++ .../xaprepare/xaprepare/xaprepare.csproj | 292 ++++++ .../xaprepare/xaprepare/xaprepare.targets | 84 ++ external/LibZipSharp | 2 +- src/Mono.Android/Mono.Android.csproj | 5 - .../Xamarin.Android.Build.Tasks.targets | 18 +- src/aapt2/aapt2.csproj | 7 - src/bundletool/bundletool.csproj | 7 - src/libzip-windows/libzip-windows.csproj | 7 - src/mono-runtimes/ProfileAssemblies.projitems | 318 ------- src/mono-runtimes/mono-runtimes.csproj | 41 - src/mono-runtimes/mono-runtimes.projitems | 208 ----- src/mono-runtimes/mono-runtimes.targets | 736 --------------- src/monodroid/monodroid.csproj | 20 - src/proguard/proguard.csproj | 7 - src/r8/r8.csproj | 7 - src/sqlite-xamarin/sqlite-xamarin.csproj | 7 - .../Xamarin.Android.Bcl-Tests.targets | 2 +- tests/api-compatibility/api-compatibility.mk | 3 +- 265 files changed, 17833 insertions(+), 3497 deletions(-) delete mode 100644 build-tools/android-toolchain/android-toolchain.csproj delete mode 100644 build-tools/android-toolchain/android-toolchain.projitems delete mode 100644 build-tools/android-toolchain/android-toolchain.targets delete mode 100644 build-tools/android-toolchain/packages.config delete mode 100644 build-tools/create-bundle/bundle-path.targets delete mode 100644 build-tools/create-bundle/create-bundle.csproj delete mode 100644 build-tools/create-bundle/create-bundle.targets delete mode 100644 build-tools/dependencies/dependencies.csproj delete mode 100644 build-tools/dependencies/dependencies.projitems delete mode 100644 build-tools/dependencies/dependencies.targets delete mode 100644 build-tools/fetch-windows-assemblers/fetch-windows-assemblers.cs delete mode 100644 build-tools/fetch-windows-assemblers/fetch-windows-assemblers.csproj create mode 100644 build-tools/license-data/Mono-MIT.txt delete mode 100644 build-tools/mingw-dependencies/mingw-dependencies.csproj delete mode 100644 build-tools/mingw-dependencies/mingw-dependencies.projitems delete mode 100644 build-tools/mingw-dependencies/mingw-dependencies.targets create mode 100644 build-tools/scripts/Ndk.projitems.in rename build-tools/{mingw-dependencies/mingw-dependencies.props => scripts/bundle-path.targets.in} (54%) delete mode 100644 build-tools/scripts/dependencies/debian-common.sh delete mode 100644 build-tools/scripts/dependencies/linux-prepare-Arch.sh delete mode 100644 build-tools/scripts/dependencies/linux-prepare-Debian.sh delete mode 100644 build-tools/scripts/dependencies/linux-prepare-LinuxMint.sh delete mode 100644 build-tools/scripts/dependencies/linux-prepare-Ubuntu.sh delete mode 100644 build-tools/scripts/dependencies/ubuntu-common.sh create mode 100644 build-tools/xaprepare/.gitignore create mode 100644 build-tools/xaprepare/README.md create mode 100644 build-tools/xaprepare/xaprepare.sln create mode 100644 build-tools/xaprepare/xaprepare/App.config create mode 100644 build-tools/xaprepare/xaprepare/Application/Abi.Bitness.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/Abi.OS.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/Abi.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/AbiType.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/AndroidPlatform.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/AndroidToolchainComponent.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/AppObject.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/BclFile.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/BclFileTarget.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/BclFileType.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/BuildInfo.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/BundleItem.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/Characters.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/CompressionFormat.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/Context.Linux.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/Context.MacOS.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/Context.Windows.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/Context.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/DetermineWindowsVersion.Windows.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/DownloadStatus.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/EssentialTools.Linux.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/EssentialTools.MacOS.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/EssentialTools.Unix.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/EssentialTools.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/ExecutionMode.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/Extensions.DictionaryOfProgramVersionParser.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/Extensions.ListOfBundleItem.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/Extensions.Runtime.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/ExternalGitDependency.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/GeneratedFile.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/GeneratedMakeRulesFile.MacOS.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/GeneratedMakeRulesFile.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/GeneratedPlaceholdersFile.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/GeneratedProfileAssembliesProjitemsFile.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/HomebrewProgram.MacOS.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/KnownConditions.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/KnownProperties.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/LlvmRuntime.Linux.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/LlvmRuntime.MacOS.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/LlvmRuntime.Windows.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/LlvmRuntime.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/Log.Unix.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/Log.Windows.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/Log.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/LoggingVerbosity.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/MonoCrossRuntime.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/MonoHostRuntime.Linux.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/MonoHostRuntime.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/MonoJitRuntime.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/MonoPkgProgram.MacOS.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/MonoRuntime.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/MonoUtilityFile.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/PkgProgram.MacOS.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/ProcessRunner.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/ProcessStandardStreamWrapper.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/Program.ArchLinux.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/Program.DebianLinux.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/Program.Linux.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/Program.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/ProgramVersionParser.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/Properties.Defaults.cs.in create mode 100644 build-tools/xaprepare/xaprepare/Application/Properties.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/PropertiesChangedEventArgs.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/RegexProgramVersionParser.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/RuleGeneratorDelegate.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/Runtime.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/RuntimeFile.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/RuntimeFileType.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/Scenario.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/ScenarioAttribute.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/ScenarioNoStandardEndSteps.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/SimpleActionStep.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/SizeFormatter.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/Step.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/StepWithDownloadProgress.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/TPNAttribute.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/TestAssembly.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/TestAssemblyType.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/ThirdPartyLicenseType.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/ThirdPartyNotice.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/ThirdPartyNoticeGroup.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/ThumbTwiddler.Unix.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/ThumbTwiddler.Windows.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/ThumbTwiddler.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/Utilities.Unix.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/Utilities.Windows.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/Utilities.cs create mode 100644 build-tools/xaprepare/xaprepare/Application/VersionFetchers.cs create mode 100644 build-tools/xaprepare/xaprepare/BuildInfo.cs.in create mode 100644 build-tools/xaprepare/xaprepare/ConfigAndData/AbiNames.cs create mode 100644 build-tools/xaprepare/xaprepare/ConfigAndData/BuildAndroidPlatforms.cs create mode 100644 build-tools/xaprepare/xaprepare/ConfigAndData/CommonLicenses.cs create mode 100644 build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.Linux.cs create mode 100644 build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.MacOS.cs create mode 100644 build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.Unix.cs create mode 100644 build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.Windows.cs create mode 100644 build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.cs create mode 100644 build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/AndroidToolchain.Linux.cs create mode 100644 build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/AndroidToolchain.MacOS.cs create mode 100644 build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/AndroidToolchain.Windows.cs create mode 100644 build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/AndroidToolchain.cs create mode 100644 build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/Linux.Arch.cs create mode 100644 build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/Linux.Debian.cs create mode 100644 build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/Linux.DebianCommon.cs create mode 100644 build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/Linux.Mint.cs create mode 100644 build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/Linux.Ubuntu.cs create mode 100644 build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/Linux.UbuntuCommon.cs create mode 100644 build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/MacOS.cs create mode 100644 build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/Windows.cs create mode 100644 build-tools/xaprepare/xaprepare/ConfigAndData/Runtimes.MacOS.cs create mode 100644 build-tools/xaprepare/xaprepare/ConfigAndData/Runtimes.Unix.cs create mode 100644 build-tools/xaprepare/xaprepare/ConfigAndData/Runtimes.cs create mode 100644 build-tools/xaprepare/xaprepare/Main.cs create mode 100644 build-tools/xaprepare/xaprepare/OperatingSystems/Linux.UbuntuCommon.cs create mode 100644 build-tools/xaprepare/xaprepare/OperatingSystems/Linux.cs create mode 100644 build-tools/xaprepare/xaprepare/OperatingSystems/MacOS.cs create mode 100644 build-tools/xaprepare/xaprepare/OperatingSystems/OS.cs create mode 100644 build-tools/xaprepare/xaprepare/OperatingSystems/Unix.cs create mode 100644 build-tools/xaprepare/xaprepare/OperatingSystems/Windows.cs create mode 100644 build-tools/xaprepare/xaprepare/Properties/AssemblyInfo.cs create mode 100644 build-tools/xaprepare/xaprepare/Resources/Configuration.OperatingSystem.props.in create mode 100644 build-tools/xaprepare/xaprepare/Resources/JdkInfo.Windows.props.in create mode 100644 build-tools/xaprepare/xaprepare/Scenarios/Scenario_AndroidToolchain.cs create mode 100644 build-tools/xaprepare/xaprepare/Scenarios/Scenario_PrepareExternalGitDependencies.cs create mode 100644 build-tools/xaprepare/xaprepare/Scenarios/Scenario_PrepareImageDependencies.cs create mode 100644 build-tools/xaprepare/xaprepare/Scenarios/Scenario_Required.cs create mode 100644 build-tools/xaprepare/xaprepare/Scenarios/Scenario_Standard.Unix.cs create mode 100644 build-tools/xaprepare/xaprepare/Scenarios/Scenario_Standard.Windows.cs create mode 100644 build-tools/xaprepare/xaprepare/Scenarios/Scenario_Standard.cs create mode 100644 build-tools/xaprepare/xaprepare/Scenarios/Scenario_UpdateMono.Unix.cs create mode 100644 build-tools/xaprepare/xaprepare/Steps/MonoRuntimesHelpers.cs create mode 100644 build-tools/xaprepare/xaprepare/Steps/Step_Android_SDK_NDK.cs create mode 100644 build-tools/xaprepare/xaprepare/Steps/Step_BuildLibZip.MacOS.cs create mode 100644 build-tools/xaprepare/xaprepare/Steps/Step_BuildLibZipForWindows.MacOS.cs create mode 100644 build-tools/xaprepare/xaprepare/Steps/Step_BuildLibZipForWindows.Unix.cs create mode 100644 build-tools/xaprepare/xaprepare/Steps/Step_BuildMingwDependencies.cs create mode 100644 build-tools/xaprepare/xaprepare/Steps/Step_BuildMonoRuntimes.Unix.cs create mode 100644 build-tools/xaprepare/xaprepare/Steps/Step_CreateBundle.Unix.cs create mode 100644 build-tools/xaprepare/xaprepare/Steps/Step_DownloadNuGet.cs create mode 100644 build-tools/xaprepare/xaprepare/Steps/Step_GenerateFiles.Unix.cs create mode 100644 build-tools/xaprepare/xaprepare/Steps/Step_GenerateFiles.Windows.cs create mode 100644 build-tools/xaprepare/xaprepare/Steps/Step_GenerateFiles.cs create mode 100644 build-tools/xaprepare/xaprepare/Steps/Step_Get_Windows_GAS.Unix.cs create mode 100644 build-tools/xaprepare/xaprepare/Steps/Step_InstallAnt.Windows.cs create mode 100644 build-tools/xaprepare/xaprepare/Steps/Step_InstallCorrettoOpenJDK.Linux.cs create mode 100644 build-tools/xaprepare/xaprepare/Steps/Step_InstallCorrettoOpenJDK.MacOS.cs create mode 100644 build-tools/xaprepare/xaprepare/Steps/Step_InstallCorrettoOpenJDK.Windows.cs create mode 100644 build-tools/xaprepare/xaprepare/Steps/Step_InstallCorrettoOpenJDK.cs create mode 100644 build-tools/xaprepare/xaprepare/Steps/Step_PrepareBundle.MacOS.cs create mode 100644 build-tools/xaprepare/xaprepare/Steps/Step_PrepareBundle.Unix.cs create mode 100644 build-tools/xaprepare/xaprepare/Steps/Step_PrepareBundle.Windows.cs create mode 100644 build-tools/xaprepare/xaprepare/Steps/Step_PrepareBundle.cs create mode 100644 build-tools/xaprepare/xaprepare/Steps/Step_PrepareExternal.Unix.cs create mode 100644 build-tools/xaprepare/xaprepare/Steps/Step_PrepareExternal.Windows.cs create mode 100644 build-tools/xaprepare/xaprepare/Steps/Step_PrepareExternal.cs create mode 100644 build-tools/xaprepare/xaprepare/Steps/Step_PrepareExternalGitDependencies.cs create mode 100644 build-tools/xaprepare/xaprepare/Steps/Step_PrepareImageDependencies.MacOS.cs create mode 100644 build-tools/xaprepare/xaprepare/Steps/Step_PrepareImageDependencies.Unix.cs create mode 100644 build-tools/xaprepare/xaprepare/Steps/Step_PrepareImageDependencies.cs create mode 100644 build-tools/xaprepare/xaprepare/Steps/Step_PrepareLocal.cs create mode 100644 build-tools/xaprepare/xaprepare/Steps/Step_PrepareMSBuild.cs create mode 100644 build-tools/xaprepare/xaprepare/Steps/Step_PrepareProps.cs create mode 100644 build-tools/xaprepare/xaprepare/Steps/Step_ThirdPartyNotices.cs create mode 100644 build-tools/xaprepare/xaprepare/ThirdPartyNotices/Java.Interop.cs create mode 100644 build-tools/xaprepare/xaprepare/ThirdPartyNotices/LibZipSharp.cs create mode 100644 build-tools/xaprepare/xaprepare/ThirdPartyNotices/Xamarin.Android.Build.Tasks.cs create mode 100644 build-tools/xaprepare/xaprepare/ThirdPartyNotices/Xamarin.Android.NunitLite.cs create mode 100644 build-tools/xaprepare/xaprepare/ThirdPartyNotices/Xamarin.Android.Tools.Aidl.cs create mode 100644 build-tools/xaprepare/xaprepare/ThirdPartyNotices/Xamarin.Android.Tools.JavadocImporter.cs create mode 100644 build-tools/xaprepare/xaprepare/ThirdPartyNotices/aapt2.cs create mode 100644 build-tools/xaprepare/xaprepare/ThirdPartyNotices/bundletool.cs create mode 100644 build-tools/xaprepare/xaprepare/ThirdPartyNotices/libzip.cs create mode 100644 build-tools/xaprepare/xaprepare/ThirdPartyNotices/mono.cs create mode 100644 build-tools/xaprepare/xaprepare/ThirdPartyNotices/monodroid.cs create mode 100644 build-tools/xaprepare/xaprepare/ThirdPartyNotices/opentk.cs create mode 100644 build-tools/xaprepare/xaprepare/ThirdPartyNotices/proguard.cs create mode 100644 build-tools/xaprepare/xaprepare/ThirdPartyNotices/r8.cs create mode 100644 build-tools/xaprepare/xaprepare/ThirdPartyNotices/sqlite.cs create mode 100644 build-tools/xaprepare/xaprepare/ToolRunners/BrewRunner.MacOS.cs create mode 100644 build-tools/xaprepare/xaprepare/ToolRunners/CMakeRunner.OutputSink.cs create mode 100644 build-tools/xaprepare/xaprepare/ToolRunners/CMakeRunner.cs create mode 100644 build-tools/xaprepare/xaprepare/ToolRunners/GitRunner.BlamePorcelainEntry.cs create mode 100644 build-tools/xaprepare/xaprepare/ToolRunners/GitRunner.OutputSink.cs create mode 100644 build-tools/xaprepare/xaprepare/ToolRunners/GitRunner.cs create mode 100644 build-tools/xaprepare/xaprepare/ToolRunners/MSBuildRunner.OutputSink.cs create mode 100644 build-tools/xaprepare/xaprepare/ToolRunners/MSBuildRunner.cs create mode 100644 build-tools/xaprepare/xaprepare/ToolRunners/MakeRunner.Linux.cs create mode 100644 build-tools/xaprepare/xaprepare/ToolRunners/MakeRunner.MacOS.cs create mode 100644 build-tools/xaprepare/xaprepare/ToolRunners/MakeRunner.OutputSink.Unix.cs create mode 100644 build-tools/xaprepare/xaprepare/ToolRunners/MakeRunner.Unix.cs create mode 100644 build-tools/xaprepare/xaprepare/ToolRunners/NinjaRunner.OutputSink.cs create mode 100644 build-tools/xaprepare/xaprepare/ToolRunners/NinjaRunner.cs create mode 100644 build-tools/xaprepare/xaprepare/ToolRunners/NuGetRunner.OutputSink.cs create mode 100644 build-tools/xaprepare/xaprepare/ToolRunners/NuGetRunner.cs create mode 100644 build-tools/xaprepare/xaprepare/ToolRunners/PkgutilRunner.MacOS.cs create mode 100644 build-tools/xaprepare/xaprepare/ToolRunners/SevenZipRunner.OutputSink.cs create mode 100644 build-tools/xaprepare/xaprepare/ToolRunners/SevenZipRunner.cs create mode 100644 build-tools/xaprepare/xaprepare/ToolRunners/TarRunner.OutputSink.cs create mode 100644 build-tools/xaprepare/xaprepare/ToolRunners/TarRunner.cs create mode 100644 build-tools/xaprepare/xaprepare/ToolRunners/ToolRunner.OutputSink.cs create mode 100644 build-tools/xaprepare/xaprepare/ToolRunners/ToolRunner.cs create mode 100644 build-tools/xaprepare/xaprepare/app.manifest create mode 100644 build-tools/xaprepare/xaprepare/xaprepare.csproj create mode 100644 build-tools/xaprepare/xaprepare/xaprepare.targets delete mode 100644 src/mono-runtimes/ProfileAssemblies.projitems delete mode 100644 src/mono-runtimes/mono-runtimes.csproj delete mode 100644 src/mono-runtimes/mono-runtimes.projitems delete mode 100644 src/mono-runtimes/mono-runtimes.targets diff --git a/CODEOWNERS b/CODEOWNERS index 1dde72621..20bd0be87 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -20,6 +20,7 @@ /build-tools/manifest-attribute-codegen @jonpryor /build-tools/scripts/PrepareWindows.targets @jonathanpeppers /build-tools/timing @jonathanpeppers @radekdoulik +/build-tools/xaprepare @grendello /src/OpenTK-1.0 @radekdoulik @jonpryor /src/Mono.Android.Export @jonpryor diff --git a/Configuration.props b/Configuration.props index b87ddb837..b6770bcf9 100644 --- a/Configuration.props +++ b/Configuration.props @@ -38,12 +38,17 @@ True portable + + Windows + Linux + Darwin + False False - $(MSBuildThisFileDirectory)bin\$(Configuration)\lib\xamarin.android\ + <_XABinRelativeInstallPrefix>lib\xamarin.android + $(MSBuildThisFileDirectory)bin\$(Configuration)\$(_XABinRelativeInstallPrefix)\ $(MSBuildThisFileDirectory)\bin\Build$(Configuration)\mingw-deps - Windows $(HostCc64) $(HostCxx64) $(HostCc32) @@ -171,7 +176,8 @@ $(AndroidSupportedTargetAotAbis.Split(':')) - $(ManagedRuntime) $(ManagedRuntimeArgs) "$(MSBuildThisFileDirectory)bin\Build$(Configuration)\remap-assembly-ref.exe" + $(MSBuildThisFileDirectory)bin\Build$(Configuration)\remap-assembly-ref.exe + $(ManagedRuntime) $(ManagedRuntimeArgs) "$(RemapAssemblyRefToolExecutable)" diff --git a/Makefile b/Makefile index 43a160ba3..3ff0452f0 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,3 @@ -export OS_NAME := $(shell uname) -export OS_ARCH := $(shell uname -m) -export NO_SUDO ?= false V ?= 0 prefix = /usr/local CONFIGURATION = Debug @@ -8,23 +5,74 @@ RUNTIME := $(shell which mono64 2> /dev/null && echo mono64 || echo mono) SOLUTION = Xamarin.Android.sln TEST_TARGETS = build-tools/scripts/RunTests.targets API_LEVEL ?= +PREPARE_ARGS = +PREPARE_BUILD_LOG = bin/Build$(CONFIGURATION)/bootstrap-build.binlog +PREPARE_RESTORE_LOG = bin/Build$(CONFIGURATION)/bootstrap-restore.binlog +PREPARE_SOURCE_DIR = build-tools/xaprepare +PREPARE_SOLUTION = $(PREPARE_SOURCE_DIR)/xaprepare.sln +PREPARE_EXE = $(PREPARE_SOURCE_DIR)/xaprepare/bin/$(CONFIGURATION)/xaprepare.exe +PREPARE_COMMON_MSBUILD_FLAGS = /p:Configuration=$(CONFIGURATION) $(PREPARE_MSBUILD_ARGS) $(MSBUILD_ARGS) +PREPARE_MSBUILD_FLAGS = /binaryLogger:"$(PREPARE_BUILD_LOG)" $(PREPARE_COMMON_MSBUILD_FLAGS) +PREPARE_RESTORE_FLAGS = /binaryLogger:"$(PREPARE_RESTORE_LOG)" $(PREPARE_COMMON_MSBUILD_FLAGS) +PREPARE_SCENARIO = +PREPARE_CI ?= 0 +PREPARE_AUTOPROVISION ?= 0 +PREPARE_IGNORE_MONO_VERSION ?= 1 -ifeq ($(OS_NAME),Darwin) -export MACOSX_DEPLOYMENT_TARGET := 10.11 -HOMEBREW_PREFIX ?= $(shell brew --prefix) -else -HOMEBREW_PREFIX := $prefix +_PREPARE_CI_MODE_ARGS = --no-emoji --run-mode=CI -a +_PREPARE_ARGS = + +all: + $(call MSBUILD_BINLOG,all,$(_SLN_BUILD)) $(MSBUILD_FLAGS) $(SOLUTION) + +-include bin/Build$(CONFIGURATION)/rules.mk + +ifeq ($(OS_NAME),) +export OS_NAME := $(shell uname) +endif + +ifeq ($(OS_ARCH),) +export OS_ARCH := $(shell uname -m) +endif + +export NO_SUDO ?= false + +ifneq ($(NO_SUDO),false) +_PREPARE_ARGS += --auto-provisioning-uses-sudo=false endif ifneq ($(V),0) MONO_OPTIONS += --debug NUGET_VERBOSITY = -Verbosity Detailed +_PREPARE_ARGS += -v:d endif -ifneq ($(MONO_OPTIONS),) -export MONO_OPTIONS +ifneq ($(PREPARE_CI),0) +_PREPARE_ARGS += $(_PREPARE_CI_MODE_ARGS) endif +ifneq ($(PREPARE_AUTOPROVISION),0) +_PREPARE_ARGS += --auto-provision=yes --auto-provision-uses-sudo=yes +endif + +ifeq ($(OS_NAME),Darwin) +ifeq ($(HOMEBREW_PREFIX),) +HOMEBREW_PREFIX ?= $(shell brew --prefix) +endif +else +HOMEBREW_PREFIX := $prefix +endif + +ifeq ($(wildcard Configuration.OperatingSystem.props),) +PREPARE_MSBUILD_FLAGS += "/p:HostHomebrewPrefix=$(HOMEBREW_PREFIX)" +endif + +ifneq ($(PREPARE_SCENARIO),) +_PREPARE_ARGS += -s:"$(PREPARE_SCENARIO)" +endif + +_PREPARE_ARGS += $(PREPARE_ARGS) + include build-tools/scripts/msbuild.mk ifeq ($(USE_MSBUILD),1) @@ -34,12 +82,9 @@ _SLN_BUILD = MSBUILD="$(MSBUILD)" tools/scripts/xabuild endif # $(USE_MSBUILD) == 1 ifneq ($(API_LEVEL),) -MSBUILD_FLAGS += /p:AndroidApiLevel=$(API_LEVEL) /p:AndroidFrameworkVersion=$(word $(API_LEVEL), $(ALL_FRAMEWORKS)) +MSBUILD_FLAGS += /p:AndroidApiLevel=$(API_LEVEL) /p:AndroidFrameworkVersion=$(word $(API_LEVEL), $(ALL_FRAMEWORKS)) /p:AndroidPlatformId=$(word $(a), $(ALL_PLATFORM_IDS)) endif -all:: - $(call MSBUILD_BINLOG,all,$(_SLN_BUILD)) $(MSBUILD_FLAGS) $(SOLUTION) - all-tests:: MSBUILD="$(MSBUILD)" $(call MSBUILD_BINLOG,all-tests,tools/scripts/xabuild) $(MSBUILD_FLAGS) Xamarin.Android-Tests.sln @@ -70,87 +115,6 @@ uninstall:: rm -rf "$(prefix)/lib/mono/xbuild/Xamarin/Android" rm -rf "$(prefix)/lib/mono/xbuild-frameworks/MonoAndroid" -ifeq ($(OS_NAME),Linux) -export LINUX_DISTRO := $(shell lsb_release -i -s || true) -export LINUX_DISTRO_RELEASE := $(shell lsb_release -r -s || true) -prepare:: linux-prepare -endif # $(OS_NAME)=Linux - -prepare:: prepare-paths prepare-msbuild - -linux-prepare:: - BINFMT_MISC_TROUBLE="cli win" \ - BINFMT_WARN=no ; \ - for m in ${BINFMT_MISC_TROUBLE}; do \ - if [ -f "/proc/sys/fs/binfmt_misc/$$m" ]; then \ - BINFMT_WARN=yes ; \ - fi ; \ - done ; \ - if [ "x${BINFMT_WARN}" = "xyes" ]; then \ - cat Documentation/binfmt_misc-warning-Linux.txt ; \ - fi; \ - if [ -f build-tools/scripts/dependencies/linux-prepare-$(LINUX_DISTRO)-$(LINUX_DISTRO_RELEASE).sh ]; then \ - sh build-tools/scripts/dependencies/linux-prepare-$(LINUX_DISTRO)-$(LINUX_DISTRO_RELEASE).sh $(LINUX_DISTRO_RELEASE); \ - elif [ -f build-tools/scripts/dependencies/linux-prepare-$(LINUX_DISTRO).sh ]; then \ - sh build-tools/scripts/dependencies/linux-prepare-$(LINUX_DISTRO).sh $(LINUX_DISTRO_RELEASE); \ - fi - -# $(call GetPath,path) -GetPath = $(shell $(MSBUILD) $(MSBUILD_FLAGS) /p:DoNotLoadOSProperties=True /nologo /v:minimal /t:Get$(1)FullPath build-tools/scripts/Paths.targets | tr -d '[[:space:]]' ) - -MSBUILD_PREPARE_PROJS = \ - src/mono-runtimes/mono-runtimes.csproj \ - src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj - -prepare-deps: prepare-cmake-mingw-toolchain - git submodule update --init --recursive - ./build-tools/scripts/generate-os-info Configuration.OperatingSystem.props - mkdir -p bin/Build$(CONFIGURATION) - $(call MSBUILD_BINLOG,prepare-deps) build-tools/dependencies/dependencies.csproj - -# -# $(1): output file name -# -define create_cmake_toolchain -prepare-cmake-mingw-toolchain:: bin/Build$(CONFIGURATION)/$(1) - -bin/Build$(CONFIGURATION)/$(1): build-tools/scripts/$(1).in - mkdir -p $$(dir $$@) - sed -e 's;@HOMEBREW_PREFIX@;$$(HOMEBREW_PREFIX);g' < $$< > $$@ -endef - -$(eval $(call create_cmake_toolchain,mingw-32.cmake)) -$(eval $(call create_cmake_toolchain,mingw-64.cmake)) - -prepare-external: prepare-deps - nuget restore $(NUGET_VERBOSITY) $(SOLUTION) - nuget restore $(NUGET_VERBOSITY) Xamarin.Android-Tests.sln - (cd external/xamarin-android-tools && make prepare CONFIGURATION=$(CONFIGURATION)) - (cd $(call GetPath,JavaInterop) && make prepare CONFIGURATION=$(CONFIGURATION) JI_MAX_JDK=8) - (cd $(call GetPath,JavaInterop) && make bin/Build$(CONFIGURATION)/JdkInfo.props CONFIGURATION=$(CONFIGURATION) JI_MAX_JDK=8) - -prepare-bundle: prepare-external - $(call MSBUILD_BINLOG,prepare-bundle) build-tools/download-bundle/download-bundle.csproj - $(call MSBUILD_BINLOG,prepare-restore) $(MSBUILD_FLAGS) tests/Xamarin.Forms-Performance-Integration/Xamarin.Forms.Performance.Integration.csproj /t:Restore - -prepare-props: prepare-bundle - cp $(call GetPath,JavaInterop)/external/Mono.Cecil* "$(call GetPath,MonoSource)/external" - cp "$(call GetPath,JavaInterop)/product.snk" "$(call GetPath,MonoSource)" - cp build-tools/scripts/Configuration.Java.Interop.Override.props external/Java.Interop/Configuration.Override.props - cp $(call GetPath,MonoSource)/mcs/class/msfinal.pub . - -prepare-msbuild: prepare-props -ifeq ($(USE_MSBUILD),1) - for proj in $(MSBUILD_PREPARE_PROJS); do \ - $(call MSBUILD_BINLOG,prepare-msbuild) "$$proj" || exit 1; \ - done -endif # msbuild - -prepare-image-dependencies: - $(call MSBUILD_BINLOG,prepare-image-deps) build-tools/scripts/PrepareImageDependencies.targets /t:PrepareImageDependencies \ - /p:AndroidSupportedHostJitAbis=mxe-Win32:mxe-Win64 - cat bin/Build$(CONFIGURATION)/prepare-image-dependencies.sh | tr -d '\r' > prepare-image-dependencies.sh - include build-tools/scripts/BuildEverything.mk # Must be after BuildEverything.mk - it uses variables defined there @@ -159,59 +123,10 @@ include tests/api-compatibility/api-compatibility.mk topdir := $(shell pwd) - -XA_BUILD_PATHS_OUT = bin/Test$(CONFIGURATION)/XABuildPaths.cs - -prepare-paths: $(XA_BUILD_PATHS_OUT) - -$(XA_BUILD_PATHS_OUT): bin/Test%/XABuildPaths.cs: build-tools/scripts/XABuildPaths.cs.in - mkdir -p $(shell dirname $@) - sed -e 's;@CONFIGURATION@;$*;g' \ - -e 's;@TOP_DIRECTORY@;$(topdir);g' < $< > $@ - cat $@ - - -# Usage: $(call CALL_CREATE_THIRD_PARTY_NOTICES,path,licenseType,includeExternalDeps,includeBuildDeps) -define CREATE_THIRD_PARTY_NOTICES - $(call MSBUILD_BINLOG,create-tpn,$(MSBUILD)) $(_MSBUILD_ARGS) \ - $(topdir)/build-tools/ThirdPartyNotices/ThirdPartyNotices.csproj \ - /p:Configuration=$(CONFIGURATION) \ - /p:ThirdPartyNoticeFile=$(topdir)/$(1) \ - /p:ThirdPartyNoticeLicenseType=$(2) \ - /p:TpnIncludeExternalDependencies=$(3) \ - /p:TpnIncludeBuildDependencies=$(4) -endef # CREATE_THIRD_PARTY_NOTICES - -prepare:: prepare-tpn - -TPN_LICENSE_FILES = $(shell grep -h '' external/*.tpnitems src/*.tpnitems \ - | sed -E 's,(.*),\1,g;s,.\(MSBuildThisFileDirectory\),$(topdir)/external/,g' \ - | tr \\ / ) - -# Usage: $(call CREATE_THIRD_PARTY_NOTICES_RULE,path,licenseType,includeExternalDeps,includeBuildDeps) -define CREATE_THIRD_PARTY_NOTICES_RULE -prepare-tpn:: $(1) - -$(1) $(topdir)/$(1): build-tools/ThirdPartyNotices/ThirdPartyNotices.csproj \ - $(wildcard external/*.tpnitems src/*.tpnitems build-tools/*.tpnitems) \ - $(TPN_LICENSE_FILES) - $(call CREATE_THIRD_PARTY_NOTICES,$(1),$(2),$(3),$(4)) -endef # CREATE_THIRD_PARTY_NOTICES_RULE - -THIRD_PARTY_NOTICE_LICENSE_TYPE = microsoft-oss - -$(eval $(call CREATE_THIRD_PARTY_NOTICES_RULE,ThirdPartyNotices.txt,foundation,False,False)) -$(eval $(call CREATE_THIRD_PARTY_NOTICES_RULE,bin/$(CONFIGURATION)/lib/xamarin.android/ThirdPartyNotices.txt,$(THIRD_PARTY_NOTICE_LICENSE_TYPE),True,False)) - # Used by External XA Build EXTERNAL_XA_PATH=$(topdir) EXTERNAL_GIT_PATH=$(topdir)/external -prepare-external-git-dependencies: - msbuild build-tools/xa-prep-tasks/xa-prep-tasks.csproj /p:Configuration=$(CONFIGURATION) - msbuild build-tools/xa-prep-tasks/xa-prep-tasks.csproj /p:Configuration=$(CONFIGURATION) \ - /t:CheckoutExternalGitSources /p:ExternalSourceDependencyDirectory='$(EXTERNAL_GIT_PATH)' - -include $(EXTERNAL_GIT_PATH)/monodroid/xa-integration.mk run-all-tests: @@ -257,3 +172,68 @@ list-nunit-tests: $(MSBUILD) $(MSBUILD_FLAGS) $(TEST_TARGETS) /t:ListNUnitTests include build-tools/scripts/runtime-helpers.mk + +.PHONY: prepare-build-init +prepare-build-init: + mkdir -p $(dir $(PREPARE_BUILD_LOG)) + msbuild $(PREPARE_RESTORE_FLAGS) $(PREPARE_SOLUTION) /t:Restore + +.PHONY: prepare-build +prepare-build: prepare-build-init + msbuild $(PREPARE_MSBUILD_FLAGS) $(PREPARE_SOLUTION) + +.PHONY: prepare-build-ci +prepare-build-ci: prepare-build-init + msbuild $(PREPARE_MSBUILD_FLAGS) $(PREPARE_SOLUTION) $(_MSBUILD_ARGS) + +.PHONY: prepare +prepare:: prepare-build + mono --debug $(PREPARE_EXE) $(_PREPARE_ARGS) + +.PHONY: prepare-help +prepare-help: prepare-build + mono --debug $(PREPARE_EXE) -h + +# Hack: The current commercial pipeline doesn't pass all the required arguments when preparing the build, in particular it doesn't override the +# ABI targets to build and so the prepare step configures only for the default set (armeabi-v7a, arm64-v8a, x86, $HOST_OS) which is not enough. +# The `jenkins` rule in `BuildEverything.mk`, invoked by the commercial pipeline, now calls the rule below in which we rebuild the bootstrapper +# with all the required properties set to include all the ABIs - it should fix the build. After the PR is merged, the commercial pipeline should +# be modified to do the right thing instead. +# +# Commercial pipeline should also set PREPARE_CI=1 when calling targets. Since this is currently not done, we have to pass $(_PREPARE_CI_MODE_ARGS) +# directly below +# +.PHONY: prepare-jenkins +prepare-jenkins: prepare-build-ci prepare-commercial + @echo preparing jenkins build + mono --debug $(PREPARE_EXE) $(_PREPARE_ARGS) $(_PREPARE_CI_MODE_ARGS) + +# This should go away once we can modify the commercial pipeline for the bootstrapper +.PHONY: prepare-commercial +ifeq ($(USE_COMMERCIAL_INSTALLER_NAME),true) +prepare-commercial: + cd $(TOP) && ./configure --with-xamarin-android='$(XAMARIN_ANDROID_PATH)' + mkdir -p $(XA_MSBUILD_DIR) + +else +prepare-commercial: +endif + +.PHONY: prepare-update-mono + +prepare-update-mono: prepare-build-ci + mono --debug $(PREPARE_EXE) $(_PREPARE_ARGS) $(_PREPARE_CI_MODE_ARGS) /s:UpdateMono + +# These targets exist only temporarily to satisfy requirements of the commercial build (since we can't modify the pipeline script in this PR) +.PHONY: prepare-deps + +# Commercial pipeline installs an older version of Mono and in effect we fail. `prepare-deps` is called after provisionator is ran and so we +# can, temporarily, re-update Mono here. After the PR is merged and commercial pipeline updated, this step should be removed. +prepare-deps: prepare-update-mono + @echo prepare-deps is no-op, prepare-jenkins or prepare do the work instead + +prepare-image-dependencies: prepare-build-ci + mono --debug $(PREPARE_EXE) $(_PREPARE_ARGS) $(_PREPARE_CI_MODE_ARGS) -s:PrepareImageDependencies + +prepare-external-git-dependencies: prepare-build-ci prepare-update-mono + mono --debug $(PREPARE_EXE) $(_PREPARE_ARGS) $(_PREPARE_CI_MODE_ARGS) -s:PrepareExternalGitDependencies diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt index 8d6110bd2..19cbdd565 100644 --- a/ThirdPartyNotices.txt +++ b/ThirdPartyNotices.txt @@ -16,9 +16,8 @@ The attached notices are provided for information only. 3. nunit/nunitlite (https://github.com/nunit/nunitlite/) 4. zLibDll/minizip (http://www.winimage.com/zLibDll/minizip.html) - %% bazelbuild/bazel NOTICES AND INFORMATION BEGIN HERE -========================================= +====================================================== Apache License Version 2.0, January 2004 @@ -221,11 +220,12 @@ The attached notices are provided for information only. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -========================================= + +====================================================== END OF bazelbuild/bazel NOTICES AND INFORMATION %% google/desugar NOTICES AND INFORMATION BEGIN HERE -========================================= +==================================================== Apache License Version 2.0, January 2004 @@ -428,12 +428,12 @@ END OF bazelbuild/bazel NOTICES AND INFORMATION WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -========================================= + +==================================================== END OF google/desugar NOTICES AND INFORMATION %% nunit/nunitlite NOTICES AND INFORMATION BEGIN HERE -========================================= - +===================================================== Copyright (c) 2004-2013 Charlie Poole Permission is hereby granted, free of charge, to any person obtaining a copy @@ -454,12 +454,13 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -========================================= + + +===================================================== END OF nunit/nunitlite NOTICES AND INFORMATION %% zLibDll/minizip NOTICES AND INFORMATION BEGIN HERE -========================================= - +===================================================== Copyright (C) 1998-2005 Gilles Vollant This software is provided 'as-is', without any express or implied @@ -478,5 +479,7 @@ appreciated but is not required. misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. -========================================= + +===================================================== END OF zLibDll/minizip NOTICES AND INFORMATION + diff --git a/Xamarin.Android.sln b/Xamarin.Android.sln index 317b3512d..17a65ef59 100644 --- a/Xamarin.Android.sln +++ b/Xamarin.Android.sln @@ -5,12 +5,8 @@ VisualStudioVersion = 15.0.27428.2037 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build-Tools", "Build-Tools", "{E351F97D-EA4F-4E7F-AAA0-8EBB1F2A4A62}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "android-toolchain", "build-tools\android-toolchain\android-toolchain.csproj", "{8FF78EB6-6FC8-46A7-8A15-EBBA9045C5FA}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Android.Tools.BootstrapTasks", "build-tools\Xamarin.Android.Tools.BootstrapTasks\Xamarin.Android.Tools.BootstrapTasks.csproj", "{E8492EFB-D14A-4F32-AA28-88848322ECEA}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mono-runtimes", "src\mono-runtimes\mono-runtimes.csproj", "{C03E6CF1-7460-4CDC-A4AB-292BBC0F61F2}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "java-runtime", "src\java-runtime\java-runtime.csproj", "{1D4FC8F1-0DA4-4F38-BE68-11AEBA9A0EA4}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "libzip", "src\libzip\libzip.csproj", "{900A0F71-BAAD-417A-8D1A-8D330297CDD0}" @@ -21,8 +17,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "api-merge", "build-tools\ap EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "remap-assembly-ref", "build-tools\remap-assembly-ref\remap-assembly-ref.csproj", "{C876DA71-8573-4CEF-9149-716D72455ED4}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "fetch-windows-assemblers", "build-tools\fetch-windows-assemblers\fetch-windows-assemblers.csproj", "{09518DF2-C7B1-4CDB-849A-9F91F4BEA925}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{04E3E11E-B47D-4599-8AFC-50515A95E715}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Java.Interop", "external\Java.Interop\src\Java.Interop\Java.Interop.csproj", "{94BD81F7-B06F-4295-9636-F8A3B6BDC762}" @@ -83,8 +77,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "libZipSharp", "external\Lib EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "libzip-windows", "src\libzip-windows\libzip-windows.csproj", "{0DE278D6-000F-4001-BB98-187C0AF58A61}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mingw-dependencies", "build-tools\mingw-dependencies\mingw-dependencies.csproj", "{2C1C68CD-CFED-4DEB-A2D3-61D6932F3E8E}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "create-bundle", "build-tools\create-bundle\create-bundle.csproj", "{1640725C-4DB8-4D8D-BC96-74E688A06EEF}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "xa-prep-tasks", "build-tools\xa-prep-tasks\xa-prep-tasks.csproj", "{7CE69551-BD73-4726-ACAA-AAF89C84BAF8}" @@ -93,8 +85,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Android.Cecil", "ex EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Android.Cecil.Mdb", "external\Java.Interop\src\Xamarin.Android.Cecil\Xamarin.Android.Cecil.Mdb.csproj", "{C0487169-8F81-497F-919E-EB42B1D0243F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dependencies", "build-tools\dependencies\dependencies.csproj", "{C845ECC0-2ED3-498E-8EA8-02EF7AC6E9AD}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "proguard", "src\proguard\proguard.csproj", "{4B9D96BB-95AB-44E8-9F87-13B12C8BCED1}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "api-xml-adjuster", "build-tools\api-xml-adjuster\api-xml-adjuster.csproj", "{8A6CB07C-E493-4A4F-AB94-038645A27118}" @@ -167,18 +157,10 @@ Global Release|AnyCPU = Release|AnyCPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {8FF78EB6-6FC8-46A7-8A15-EBBA9045C5FA}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU - {8FF78EB6-6FC8-46A7-8A15-EBBA9045C5FA}.Debug|AnyCPU.Build.0 = Debug|Any CPU - {8FF78EB6-6FC8-46A7-8A15-EBBA9045C5FA}.Release|AnyCPU.ActiveCfg = Release|Any CPU - {8FF78EB6-6FC8-46A7-8A15-EBBA9045C5FA}.Release|AnyCPU.Build.0 = Release|Any CPU {E8492EFB-D14A-4F32-AA28-88848322ECEA}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU {E8492EFB-D14A-4F32-AA28-88848322ECEA}.Debug|AnyCPU.Build.0 = Debug|Any CPU {E8492EFB-D14A-4F32-AA28-88848322ECEA}.Release|AnyCPU.ActiveCfg = Release|Any CPU {E8492EFB-D14A-4F32-AA28-88848322ECEA}.Release|AnyCPU.Build.0 = Release|Any CPU - {C03E6CF1-7460-4CDC-A4AB-292BBC0F61F2}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU - {C03E6CF1-7460-4CDC-A4AB-292BBC0F61F2}.Debug|AnyCPU.Build.0 = Debug|Any CPU - {C03E6CF1-7460-4CDC-A4AB-292BBC0F61F2}.Release|AnyCPU.ActiveCfg = Release|Any CPU - {C03E6CF1-7460-4CDC-A4AB-292BBC0F61F2}.Release|AnyCPU.Build.0 = Release|Any CPU {1D4FC8F1-0DA4-4F38-BE68-11AEBA9A0EA4}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU {1D4FC8F1-0DA4-4F38-BE68-11AEBA9A0EA4}.Debug|AnyCPU.Build.0 = Debug|Any CPU {1D4FC8F1-0DA4-4F38-BE68-11AEBA9A0EA4}.Release|AnyCPU.ActiveCfg = Release|Any CPU @@ -199,10 +181,6 @@ Global {C876DA71-8573-4CEF-9149-716D72455ED4}.Debug|AnyCPU.Build.0 = Debug|Any CPU {C876DA71-8573-4CEF-9149-716D72455ED4}.Release|AnyCPU.ActiveCfg = Release|Any CPU {C876DA71-8573-4CEF-9149-716D72455ED4}.Release|AnyCPU.Build.0 = Release|Any CPU - {09518DF2-C7B1-4CDB-849A-9F91F4BEA925}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU - {09518DF2-C7B1-4CDB-849A-9F91F4BEA925}.Debug|AnyCPU.Build.0 = Debug|Any CPU - {09518DF2-C7B1-4CDB-849A-9F91F4BEA925}.Release|AnyCPU.ActiveCfg = Release|Any CPU - {09518DF2-C7B1-4CDB-849A-9F91F4BEA925}.Release|AnyCPU.Build.0 = Release|Any CPU {94BD81F7-B06F-4295-9636-F8A3B6BDC762}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU {94BD81F7-B06F-4295-9636-F8A3B6BDC762}.Debug|AnyCPU.Build.0 = Debug|Any CPU {94BD81F7-B06F-4295-9636-F8A3B6BDC762}.Release|AnyCPU.ActiveCfg = Release|Any CPU @@ -303,14 +281,6 @@ Global {0DE278D6-000F-4001-BB98-187C0AF58A61}.Debug|AnyCPU.Build.0 = Debug|Any CPU {0DE278D6-000F-4001-BB98-187C0AF58A61}.Release|AnyCPU.ActiveCfg = Release|Any CPU {0DE278D6-000F-4001-BB98-187C0AF58A61}.Release|AnyCPU.Build.0 = Release|Any CPU - {2C1C68CD-CFED-4DEB-A2D3-61D6932F3E8E}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU - {2C1C68CD-CFED-4DEB-A2D3-61D6932F3E8E}.Debug|AnyCPU.Build.0 = Debug|Any CPU - {2C1C68CD-CFED-4DEB-A2D3-61D6932F3E8E}.Release|AnyCPU.ActiveCfg = Release|Any CPU - {2C1C68CD-CFED-4DEB-A2D3-61D6932F3E8E}.Release|AnyCPU.Build.0 = Release|Any CPU - {1640725C-4DB8-4D8D-BC96-74E688A06EEF}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU - {1640725C-4DB8-4D8D-BC96-74E688A06EEF}.Debug|AnyCPU.Build.0 = Debug|Any CPU - {1640725C-4DB8-4D8D-BC96-74E688A06EEF}.Release|AnyCPU.ActiveCfg = Release|Any CPU - {1640725C-4DB8-4D8D-BC96-74E688A06EEF}.Release|AnyCPU.Build.0 = Release|Any CPU {7CE69551-BD73-4726-ACAA-AAF89C84BAF8}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU {7CE69551-BD73-4726-ACAA-AAF89C84BAF8}.Debug|AnyCPU.Build.0 = Debug|Any CPU {7CE69551-BD73-4726-ACAA-AAF89C84BAF8}.Release|AnyCPU.ActiveCfg = Release|Any CPU @@ -323,10 +293,6 @@ Global {C0487169-8F81-497F-919E-EB42B1D0243F}.Debug|AnyCPU.Build.0 = Debug|Any CPU {C0487169-8F81-497F-919E-EB42B1D0243F}.Release|AnyCPU.ActiveCfg = Release|Any CPU {C0487169-8F81-497F-919E-EB42B1D0243F}.Release|AnyCPU.Build.0 = Release|Any CPU - {C845ECC0-2ED3-498E-8EA8-02EF7AC6E9AD}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU - {C845ECC0-2ED3-498E-8EA8-02EF7AC6E9AD}.Debug|AnyCPU.Build.0 = Debug|Any CPU - {C845ECC0-2ED3-498E-8EA8-02EF7AC6E9AD}.Release|AnyCPU.ActiveCfg = Release|Any CPU - {C845ECC0-2ED3-498E-8EA8-02EF7AC6E9AD}.Release|AnyCPU.Build.0 = Release|Any CPU {4B9D96BB-95AB-44E8-9F87-13B12C8BCED1}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU {4B9D96BB-95AB-44E8-9F87-13B12C8BCED1}.Debug|AnyCPU.Build.0 = Debug|Any CPU {4B9D96BB-95AB-44E8-9F87-13B12C8BCED1}.Release|AnyCPU.ActiveCfg = Release|Any CPU @@ -432,9 +398,7 @@ Global HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {8FF78EB6-6FC8-46A7-8A15-EBBA9045C5FA} = {E351F97D-EA4F-4E7F-AAA0-8EBB1F2A4A62} {E8492EFB-D14A-4F32-AA28-88848322ECEA} = {E351F97D-EA4F-4E7F-AAA0-8EBB1F2A4A62} - {C03E6CF1-7460-4CDC-A4AB-292BBC0F61F2} = {04E3E11E-B47D-4599-8AFC-50515A95E715} {1D4FC8F1-0DA4-4F38-BE68-11AEBA9A0EA4} = {04E3E11E-B47D-4599-8AFC-50515A95E715} {900A0F71-BAAD-417A-8D1A-8D330297CDD0} = {04E3E11E-B47D-4599-8AFC-50515A95E715} {AFB8F6D1-6EA9-42C3-950B-98F34C669AD2} = {E351F97D-EA4F-4E7F-AAA0-8EBB1F2A4A62} @@ -466,12 +430,9 @@ Global {5EB9E888-E357-417E-9F39-DDEC195CE47F} = {04E3E11E-B47D-4599-8AFC-50515A95E715} {E248B2CA-303B-4645-ADDC-9D4459D550FD} = {04E3E11E-B47D-4599-8AFC-50515A95E715} {0DE278D6-000F-4001-BB98-187C0AF58A61} = {04E3E11E-B47D-4599-8AFC-50515A95E715} - {2C1C68CD-CFED-4DEB-A2D3-61D6932F3E8E} = {E351F97D-EA4F-4E7F-AAA0-8EBB1F2A4A62} - {1640725C-4DB8-4D8D-BC96-74E688A06EEF} = {E351F97D-EA4F-4E7F-AAA0-8EBB1F2A4A62} {7CE69551-BD73-4726-ACAA-AAF89C84BAF8} = {E351F97D-EA4F-4E7F-AAA0-8EBB1F2A4A62} {15945D4B-FF56-4BCC-B598-2718D199DD08} = {864062D3-A415-4A6F-9324-5820237BA058} {C0487169-8F81-497F-919E-EB42B1D0243F} = {864062D3-A415-4A6F-9324-5820237BA058} - {C845ECC0-2ED3-498E-8EA8-02EF7AC6E9AD} = {E351F97D-EA4F-4E7F-AAA0-8EBB1F2A4A62} {4B9D96BB-95AB-44E8-9F87-13B12C8BCED1} = {04E3E11E-B47D-4599-8AFC-50515A95E715} {8A6CB07C-E493-4A4F-AB94-038645A27118} = {E351F97D-EA4F-4E7F-AAA0-8EBB1F2A4A62} {E0890301-F75F-40E7-B008-54C28B3BA542} = {864062D3-A415-4A6F-9324-5820237BA058} diff --git a/build-tools/Xamarin.Android.Tools.BootstrapTasks/result-packaging.targets b/build-tools/Xamarin.Android.Tools.BootstrapTasks/result-packaging.targets index 3acd5acbc..4a283bb73 100644 --- a/build-tools/Xamarin.Android.Tools.BootstrapTasks/result-packaging.targets +++ b/build-tools/Xamarin.Android.Tools.BootstrapTasks/result-packaging.targets @@ -7,6 +7,8 @@ <_BuildStatusFiles Include="$(XamarinAndroidSourcePath)Configuration.Override.props" Condition="Exists('$(XamarinAndroidSourcePath)Configuration.Override.props')" /> <_BuildStatusFiles Include="$(XamarinAndroidSourcePath)bin\Build$(Configuration)\XABuildConfig.cs" Condition="Exists('$(XamarinAndroidSourcePath)bin\Build$(Configuration)\XABuildConfig.cs')" /> <_BuildStatusFiles Include="$(XamarinAndroidSourcePath)bin\Build$(Configuration)\msbuild*.binlog" /> + <_BuildStatusFiles Include="$(XamarinAndroidSourcePath)bin\Build$(Configuration)\prepare*.log" /> + <_BuildStatusFiles Include="$(XamarinAndroidSourcePath)**\ThirdPartyNotices.txt" /> <_BuildStatusFiles Include="$(XamarinAndroidSourcePath)**\config.log" /> <_BuildStatusFiles Include="$(XamarinAndroidSourcePath)**\config.status" /> <_BuildStatusFiles Include="$(XamarinAndroidSourcePath)**\config.h" /> diff --git a/build-tools/android-toolchain/android-toolchain.csproj b/build-tools/android-toolchain/android-toolchain.csproj deleted file mode 100644 index 499fb1b8b..000000000 --- a/build-tools/android-toolchain/android-toolchain.csproj +++ /dev/null @@ -1,39 +0,0 @@ - - - - Debug - AnyCPU - {8FF78EB6-6FC8-46A7-8A15-EBBA9045C5FA} - - - - $(AndroidToolchainDirectory) - - - $(AndroidToolchainDirectory) - - - - - - - - {7CE69551-BD73-4726-ACAA-AAF89C84BAF8} - xa-prep-tasks - False - - - {C845ECC0-2ED3-498E-8EA8-02EF7AC6E9AD} - dependencies - False - - - {E8492EFB-D14A-4F32-AA28-88848322ECEA} - Xamarin.Android.Tools.BootstrapTasks - False - - - - - - diff --git a/build-tools/android-toolchain/android-toolchain.projitems b/build-tools/android-toolchain/android-toolchain.projitems deleted file mode 100644 index ec16cb9f4..000000000 --- a/build-tools/android-toolchain/android-toolchain.projitems +++ /dev/null @@ -1,169 +0,0 @@ - - - - https://dl.google.com/android/repository - https://archive.apache.org/dist/ant/binaries - - - - Darwin - - - Linux - - - Windows - - - - Linux - build-tools\$(XABuildToolsFolder) - - - Linux - platform-tools - - - Linux - tools - - - Linux - emulator - - - Darwin - build-tools\$(XABuildToolsFolder) - - - Darwin - platform-tools - - - Darwin - tools - - - Darwin - emulator - - - Windows - build-tools\$(XABuildToolsFolder) - - - Windows - platform-tools - - - Windows - tools - - - Windows - emulator - - - - - Linux - cmake\$(AndroidCmakeVersionPath) - True - - - Darwin - cmake\$(AndroidCmakeVersionPath) - True - - - Windows - cmake\$(AndroidCmakeVersionPath) - True - - - - - platforms\android-10 - - - - platforms\android-15 - - - - platforms\android-16 - - - - platforms\android-17 - - - - platforms\android-18 - - - - platforms\android-19 - - - - platforms\android-20 - - - - platforms\android-21 - - - - platforms\android-22 - - - - platforms\android-23 - - - - platforms\android-24 - - - - platforms\android-25 - - - - platforms\android-26 - - - - platforms\android-27 - - - - platforms\android-28 - - - - platforms\android-Q - - - - docs - - - - extras\android\m2repository - - - - sys-img/android/ - system-images\android-28\default\x86 - - - - - - - - diff --git a/build-tools/android-toolchain/android-toolchain.targets b/build-tools/android-toolchain/android-toolchain.targets deleted file mode 100644 index 09290235d..000000000 --- a/build-tools/android-toolchain/android-toolchain.targets +++ /dev/null @@ -1,111 +0,0 @@ - - - - - - - - - ResolveReferences; - _DownloadItems; - _UnzipFiles; - - - - - - - - - - - - - - - - - <_SdkStampFiles Include="@(_PlatformAndroidSdkItem->'$(AndroidToolchainDirectory)\sdk\.stamp-%(Identity)')" /> - - - <_SdkStampFiles Include="@(_PlatformAntItem->'$(AntDirectory)\.stamp-%(Identity)')" /> - - - - - - - - - - - - - - - - - <_OriginalPath>$(PATH) - - - - - - - - - - - - - - - - - - - - - - - diff --git a/build-tools/android-toolchain/packages.config b/build-tools/android-toolchain/packages.config deleted file mode 100644 index 6b8deb9c9..000000000 --- a/build-tools/android-toolchain/packages.config +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/build-tools/automation/azure-pipelines.yaml b/build-tools/automation/azure-pipelines.yaml index 1b8e50a91..d3ad5e75a 100644 --- a/build-tools/automation/azure-pipelines.yaml +++ b/build-tools/automation/azure-pipelines.yaml @@ -8,16 +8,16 @@ trigger: variables: BundleArtifactName: bundle AutoProvisionArgs: /p:AutoProvision=True /p:AutoProvisionUsesSudo=True /p:IgnoreMaxMonoVersion=False + AndroidTargetAbiArgs: >- + /p:AndroidSupportedTargetJitAbis=armeabi-v7a:arm64-v8a:x86:x86_64 + /p:AndroidSupportedTargetAotAbis=armeabi-v7a:arm64:x86:x86_64:win-armeabi-v7a:win-arm64:win-x86:win-x86_64 # Stage and Job "display names" are shortened because they are combined to form the name of the corresponding GitHub check. stages: - stage: prepare displayName: Prepare variables: - MSBuildAbiArgs: >- - /p:AndroidSupportedTargetJitAbis=armeabi-v7a:arm64-v8a:x86:x86_64 - /p:AndroidSupportedHostJitAbis=Darwin:mxe-Win32:mxe-Win64 - /p:AndroidSupportedTargetAotAbis=armeabi-v7a:arm64:x86:x86_64:win-armeabi-v7a:win-arm64:win-x86:win-x86_64 + MSBuildAbiArgs: $(AndroidTargetAbiArgs) /p:AndroidSupportedHostJitAbis=Darwin:mxe-Win32:mxe-Win64 jobs: - job: create_bundle displayName: Bundle @@ -25,20 +25,17 @@ stages: timeoutInMinutes: 60 cancelTimeoutInMinutes: 5 steps: + - checkout: self clean: true submodules: recursive - # Ensure the correct Mono.Cecil reference overrides are set before creating the bundle. - - script: make prepare-props V=1 CONFIGURATION=$(XA.Build.Configuration) MSBUILD_ARGS="$(AutoProvisionArgs) $(MSBuildAbiArgs)" - displayName: make prepare-props - - - task: MSBuild@1 + # Update Mono in a separate step since xaprepare uses it as well and it will crash when Mono it runs with is updated + # The 'prepare' step creates the bundle + - script: | + make prepare-update-mono PREPARE_CI=1 V=1 CONFIGURATION=$(XA.Build.Configuration) MSBUILD_ARGS="$(AutoProvisionArgs) $(MSBuildAbiArgs)" + make prepare PREPARE_CI=1 V=1 CONFIGURATION=$(XA.Build.Configuration) MSBUILD_ARGS="$(AutoProvisionArgs) $(MSBuildAbiArgs)" displayName: create bundle - inputs: - solution: build-tools/create-bundle/create-bundle.csproj - configuration: $(XA.Build.Configuration) - msbuildArguments: /bl:$(System.DefaultWorkingDirectory)/bin/Build$(XA.Build.Configuration)/msbuild-createbundle.binlog $(MSBuildAbiArgs) - task: CopyFiles@2 displayName: copy bundle @@ -58,7 +55,7 @@ stages: inputs: solution: build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks.csproj configuration: $(XA.Build.Configuration) - msbuildArguments: /t:ZipBuildStatus /p:BuildStatusZipOutputPath=$(Build.ArtifactStagingDirectory)/results + msbuildArguments: /t:Build,ZipBuildStatus /p:BuildStatusZipOutputPath=$(Build.ArtifactStagingDirectory)/results condition: always() - task: PublishPipelineArtifact@0 @@ -89,7 +86,7 @@ stages: artifactName: $(BundleArtifactName) downloadPath: $(System.DefaultWorkingDirectory) - - script: make prepare-external-git-dependencies CONFIGURATION=$(XA.Build.Configuration) + - script: make prepare-external-git-dependencies PREPARE_CI=1 CONFIGURATION=$(XA.Build.Configuration) displayName: make prepare-commercial condition: eq(variables['XA.Commercial.Build'], 'true') env: @@ -103,11 +100,12 @@ stages: provisioning_script: $(System.DefaultWorkingDirectory)/external/monodroid/build-tools/provisionator/profile.csx provisioning_extra_args: -vv - - script: make prepare-deps V=1 CONFIGURATION=$(XA.Build.Configuration) MSBUILD_ARGS="$(AutoProvisionArgs)" - displayName: make prepare-deps + - script: make prepare-update-mono V=1 CONFIGURATION=$(XA.Build.Configuration) PREPARE_CI=1 PREPARE_ARGS="--bundle-path=$(System.DefaultWorkingDirectory)" MSBUILD_ARGS="$(AutoProvisionArgs) /p:BundleRootPath=$(System.DefaultWorkingDirectory)" + displayName: make prepare-update-mono - - script: make prepare jenkins V=1 CONFIGURATION=$(XA.Build.Configuration) MSBUILD_ARGS="$(AutoProvisionArgs) /p:BundleRootPath=$(System.DefaultWorkingDirectory)" - displayName: make prepare jenkins + # No need to add `prepare` to the command line, the `jenkins` rule depends on `prepare-jenkins` which will run the bootstrapper + - script: make jenkins V=1 CONFIGURATION=$(XA.Build.Configuration) PREPARE_CI=1 PREPARE_ARGS="--bundle-path=$(System.DefaultWorkingDirectory)" MSBUILD_ARGS="$(AutoProvisionArgs) /p:BundleRootPath=$(System.DefaultWorkingDirectory)" + displayName: make jenkins - script: make create-installers V=1 CONFIGURATION=$(XA.Build.Configuration) displayName: make create-installers @@ -149,6 +147,8 @@ stages: pool: $(XA.Build.Win.Pool) timeoutInMinutes: 360 cancelTimeoutInMinutes: 5 + variables: + JAVA_HOME: '%HOMEDRIVE%%HOMEPATH%\android-toolchain\jdk' steps: - checkout: self clean: true @@ -160,31 +160,34 @@ stages: downloadPath: $(System.DefaultWorkingDirectory) - task: MSBuild@1 - displayName: msbuild Xamarin.Android /t:Prepare + displayName: build xaprepare inputs: - solution: Xamarin.Android.sln + solution: build-tools\xaprepare\xaprepare.sln configuration: $(XA.Build.Configuration) - msbuildArguments: /t:Prepare /bl:$(System.DefaultWorkingDirectory)\bin\Build$(XA.Build.Configuration)\msbuild-prepare.binlog /p:BundleRootPath=$(System.DefaultWorkingDirectory) + msbuildArguments: $(AutoProvisionArgs) $(AndroidTargetAbiArgs) /t:"Restore;Build" + + - script: build-tools\xaprepare\xaprepare\bin\$(XA.Build.Configuration)\xaprepare.exe -v:d --no-emoji --run-mode=CI -a --bundle-path="$(System.DefaultWorkingDirectory)" + displayName: run xaprepare - task: MSBuild@1 displayName: msbuild Xamarin.Android /t:Build inputs: solution: Xamarin.Android.sln configuration: $(XA.Build.Configuration) - msbuildArguments: /t:Build /bl:$(System.DefaultWorkingDirectory)\bin\Build$(XA.Build.Configuration)\msbuild-build.binlog /p:BundleRootPath=$(System.DefaultWorkingDirectory) + msbuildArguments: /t:Build /bl:$(System.DefaultWorkingDirectory)\bin\Build$(XA.Build.Configuration)\msbuild-build.binlog /p:BundleRootPath=$(System.DefaultWorkingDirectory) $(AndroidTargetAbiArgs) - task: MSBuild@1 displayName: msbuild create-vsix inputs: solution: build-tools\create-vsix\create-vsix.csproj configuration: $(XA.Build.Configuration) - msbuildArguments: /p:CreateVsixContainer=True /p:ZipPackageCompressionLevel=Normal /bl:$(System.DefaultWorkingDirectory)\bin\Build$(XA.Build.Configuration)\msbuild-create-vsix.binlog + msbuildArguments: /p:CreateVsixContainer=True /p:ZipPackageCompressionLevel=Normal /bl:$(System.DefaultWorkingDirectory)\bin\Build$(XA.Build.Configuration)\msbuild-create-vsix.binlog $(AndroidTargetAbiArgs) - task: CmdLine@1 displayName: xabuild Xamarin.Android-Tests inputs: filename: bin\$(XA.Build.Configuration)\bin\xabuild.exe - arguments: Xamarin.Android-Tests.sln /p:Configuration=$(XA.Build.Configuration) /p:XAIntegratedTests=False /bl:$(System.DefaultWorkingDirectory)\bin\Test$(XA.Build.Configuration)\msbuild-build-tests.binlog + arguments: Xamarin.Android-Tests.sln /p:Configuration=$(XA.Build.Configuration) /p:XAIntegratedTests=False /bl:$(System.DefaultWorkingDirectory)\bin\Test$(XA.Build.Configuration)\msbuild-build-tests.binlog $(AndroidTargetAbiArgs) - task: MSBuild@1 displayName: nunit Xamarin.Android.Build.Tests diff --git a/build-tools/automation/build.groovy b/build-tools/automation/build.groovy index 7bda909f4..326e8ab32 100644 --- a/build-tools/automation/build.groovy +++ b/build-tools/automation/build.groovy @@ -149,7 +149,7 @@ timestamps { utils.stageWithTimeout('prepare deps', 30, 'MINUTES', XADir, true) { // Typically takes less than 2 minutes, but can take longer if any prereqs need to be provisioned if (isCommercial) { - sh "make prepare-external-git-dependencies" + sh "make prepare-external-git-dependencies PREPARE_CI=1 V=1 " utils.stageWithTimeout('provisionator', 30, 'MINUTES', "${commercialPath}/build-tools/provisionator", true) { sh('./provisionator.sh profile.csx -v') @@ -166,12 +166,11 @@ timestamps { } } } - - sh "make prepare-deps CONFIGURATION=${env.BuildFlavor} V=1 MSBUILD_ARGS='$EXTRA_MSBUILD_ARGS'" } utils.stageWithTimeout('build', 6, 'HOURS', XADir, true) { // Typically takes less than one hour except a build on a new bot to populate local caches can take several hours - sh "make prepare ${buildTarget} CONFIGURATION=${env.BuildFlavor} V=1 MSBUILD_ARGS='$EXTRA_MSBUILD_ARGS'" + sh "make prepare-update-mono CONFIGURATION=${env.BuildFlavor} V=1 PREPARE_CI=1 MSBUILD_ARGS='$EXTRA_MSBUILD_ARGS'" + sh "make prepare ${buildTarget} CONFIGURATION=${env.BuildFlavor} V=1 PREPARE_CI=1 MSBUILD_ARGS='$EXTRA_MSBUILD_ARGS'" if (isCommercial) { sh "cp bin/${env.BuildFlavor}/bundle-*.zip ${packagePath}" diff --git a/build-tools/automation/build.linux.groovy b/build-tools/automation/build.linux.groovy index f47980ca1..4b84e68c6 100644 --- a/build-tools/automation/build.linux.groovy +++ b/build-tools/automation/build.linux.groovy @@ -167,7 +167,7 @@ timestamps { fi echo 'STAGE: build' - make prepare ${buildTarget} CONFIGURATION=${env.BuildFlavor} V=1 NO_SUDO=true MSBUILD_ARGS='/p:MonoRequiredMinimumVersion=5.12' + make prepare ${buildTarget} CONFIGURATION=${env.BuildFlavor} V=1 NO_SUDO=true MSBUILD_ARGS='/p:MonoRequiredMinimumVersion=5.12' PREPARE_CI=1 if [[ "${isPr}" != "true" ]]; then echo 'STAGE: package deb' diff --git a/build-tools/create-bundle/bundle-path.targets b/build-tools/create-bundle/bundle-path.targets deleted file mode 100644 index 6e3fe71d0..000000000 --- a/build-tools/create-bundle/bundle-path.targets +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - v21 - bundle-$(XABundleVersion)-h$(_VersionHash)-$(Configuration)-$(HostOS)-libzip=$(_LibZipHash),mono=$(_MonoHash).zip - bundle-$(XABundleVersion)-h$(_VersionHash)-$(Configuration)-Darwin-libzip=$(_LibZipHash),mono=$(_MonoHash).zip - - - diff --git a/build-tools/create-bundle/create-bundle.csproj b/build-tools/create-bundle/create-bundle.csproj deleted file mode 100644 index 76c62904b..000000000 --- a/build-tools/create-bundle/create-bundle.csproj +++ /dev/null @@ -1,52 +0,0 @@ - - - - Debug - AnyCPU - {1640725C-4DB8-4D8D-BC96-74E688A06EEF} - - - - ..\..\bin\Debug - - - ..\..\bin\Release - - - - - ResolveReferences; - CreateBundle - - - - - - {7CE69551-BD73-4726-ACAA-AAF89C84BAF8} - xa-prep-tasks - False - - - {8FF78EB6-6FC8-46A7-8A15-EBBA9045C5FA} - android-toolchain - False - - - {C03E6CF1-7460-4CDC-A4AB-292BBC0F61F2} - mono-runtimes - False - - - {900A0F71-BAAD-417A-8D1A-8D330297CDD0} - libzip - False - Never - - - {0DE278D6-000F-4001-BB98-187C0AF58A61} - libzip-windows - False - Never - - - diff --git a/build-tools/create-bundle/create-bundle.targets b/build-tools/create-bundle/create-bundle.targets deleted file mode 100644 index ad46eb813..000000000 --- a/build-tools/create-bundle/create-bundle.targets +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/build-tools/dependencies/dependencies.csproj b/build-tools/dependencies/dependencies.csproj deleted file mode 100644 index 245a56df8..000000000 --- a/build-tools/dependencies/dependencies.csproj +++ /dev/null @@ -1,30 +0,0 @@ - - - - Debug - AnyCPU - {C845ECC0-2ED3-498E-8EA8-02EF7AC6E9AD} - - - - - .\ - - - .\ - - - - - ResolveReferences; - CheckForRequiredPrograms; - - - - - {7CE69551-BD73-4726-ACAA-AAF89C84BAF8} - xa-prep-tasks - False - - - \ No newline at end of file diff --git a/build-tools/dependencies/dependencies.projitems b/build-tools/dependencies/dependencies.projitems deleted file mode 100644 index 7d9718e74..000000000 --- a/build-tools/dependencies/dependencies.projitems +++ /dev/null @@ -1,45 +0,0 @@ - - - - <_DarwinMonoFramework>MonoFramework-MDK-6.0.0.6.macos10.xamarin.universal.pkg - <_AptGetInstall>apt-get -f -u install - - - - - - mingw-w64 - - - autoconf - - - automake - - - cmake - - - ninja - - - xamarin/xamarin-android-windeps/mingw-zlib - xamarin/xamarin-android-windeps - - - 1.8 - $(MSBuildThisFileDirectory)..\scripts\javac-version - "$(JavaCPath)" -version 2>&1 - $(_AptGetInstall) openjdk-8-jdk - - - - $(MonoRequiredMinimumVersion) - $(MonoRequiredMaximumVersion) - $(MonoRequiredDarwinMinimumVersion) - $(MSBuildThisFileDirectory)..\scripts\mono-version - https://xamjenkinsartifact.azureedge.net/build-package-osx-mono/2019-02/7/12cadb2b21da7f1ad8f5999fd2c5ec8a127c5795/$(_DarwinMonoFramework) - installer -pkg "$(AndroidToolchainCacheDirectory)\$(_DarwinMonoFramework)" -target / - - - diff --git a/build-tools/dependencies/dependencies.targets b/build-tools/dependencies/dependencies.targets deleted file mode 100644 index 52813b1b3..000000000 --- a/build-tools/dependencies/dependencies.targets +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/build-tools/download-bundle/download-bundle.targets b/build-tools/download-bundle/download-bundle.targets index 13fb89451..507f6e800 100644 --- a/build-tools/download-bundle/download-bundle.targets +++ b/build-tools/download-bundle/download-bundle.targets @@ -2,14 +2,13 @@ - + <_AzureBaseUri>https://xamjenkinsartifact.azureedge.net/mono-jenkins/ <_NuGetUri>https://dist.nuget.org/win-x86-commandline/v4.7.1/nuget.exe <_NuGetPath>$(MSBuildThisFileDirectory)\..\..\.nuget - + $(AndroidToolchainCacheDirectory) <_BundlePath>$(BundleRootPath)\$(XABundleFileName) diff --git a/build-tools/fetch-windows-assemblers/fetch-windows-assemblers.cs b/build-tools/fetch-windows-assemblers/fetch-windows-assemblers.cs deleted file mode 100644 index 54fe4f0a7..000000000 --- a/build-tools/fetch-windows-assemblers/fetch-windows-assemblers.cs +++ /dev/null @@ -1,738 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.IO.Compression; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; - -// -// Taken from: -// https://github.com/force-net/Crc32.NET/blob/fbc1061b0cb53df2322d5aed33167a2e6335970b/Crc32.NET/SafeProxy.cs -// -// License: MIT -// https://github.com/force-net/Crc32.NET/blob/fbc1061b0cb53df2322d5aed33167a2e6335970b/LICENSE -// -class CRC32 -{ - const uint Poly = 0xedb88320u; - - readonly uint[] _table = new uint[16 * 256]; - - internal CRC32 () - { - Init (Poly); - } - - protected void Init (uint poly) - { - var table = _table; - for (uint i = 0; i < 256; i++) { - uint res = i; - for (int t = 0; t < 16; t++) { - for (int k = 0; k < 8; k++) res = (res & 1) == 1 ? poly ^ (res >> 1) : (res >> 1); - table[(t * 256) + i] = res; - } - } - } - - public uint Append (uint crc, byte[] input, int offset, int length) - { - uint crcLocal = uint.MaxValue ^ crc; - - uint[] table = _table; - while (length >= 16) { - var a = table[(3 * 256) + input[offset + 12]] - ^ table[(2 * 256) + input[offset + 13]] - ^ table[(1 * 256) + input[offset + 14]] - ^ table[(0 * 256) + input[offset + 15]]; - - var b = table[(7 * 256) + input[offset + 8]] - ^ table[(6 * 256) + input[offset + 9]] - ^ table[(5 * 256) + input[offset + 10]] - ^ table[(4 * 256) + input[offset + 11]]; - - var c = table[(11 * 256) + input[offset + 4]] - ^ table[(10 * 256) + input[offset + 5]] - ^ table[(9 * 256) + input[offset + 6]] - ^ table[(8 * 256) + input[offset + 7]]; - - var d = table[(15 * 256) + ((byte)crcLocal ^ input[offset])] - ^ table[(14 * 256) + ((byte)(crcLocal >> 8) ^ input[offset + 1])] - ^ table[(13 * 256) + ((byte)(crcLocal >> 16) ^ input[offset + 2])] - ^ table[(12 * 256) + ((crcLocal >> 24) ^ input[offset + 3])]; - - crcLocal = d ^ c ^ b ^ a; - offset += 16; - length -= 16; - } - - while (--length >= 0) - crcLocal = table[(byte)(crcLocal ^ input[offset++])] ^ crcLocal >> 8; - - return crcLocal ^ uint.MaxValue; - } -} - -class app -{ - const int EndFileChunkSize = 65535 + 22; // Maximum comment size + EOCD size - const uint EOCDSignature = 0x06054b50; - const uint CDHeaderSignature = 0x02014b50; - const uint LFHeaderSignature = 0x04034b50; - - class EOCD - { - public uint Signature; // Signature (0x06054b50) - public ushort DiskNumber; // number of this disk - public ushort CDStartDisk; // number of the disk with the start of the central directory - public ushort TotalEntriesThisDisk; // total number of entries in the central directory on this disk - public ushort TotalEntries; // total number of entries in the central directory - public uint CDSize; // size of the central directory - public uint CDOffset; // offset of start of central directory with respect to the starting disk number - public ushort CommentLength; // .ZIP file comment length - }; - - class CDHeader - { - public uint Signature; // 0x02014b50 - public ushort VersionMadeBy; - public ushort VersionNeededToExtract; - public ushort GeneralPurposeBitFlag; - public ushort CompressionMethod; - public ushort LastModFileTime; - public ushort LastModFileDate; - public uint CRC32; - public uint CompressedSize; - public uint UncompressedSize; - public ushort FileNameLength; - public ushort ExtraFieldLength; - public ushort FileCommentLength; - public ushort DiskNumberStart; - public ushort InternalFileAttributes; - public uint ExternalFileAttributes; - public uint RelativeOffsetOfLocalHeader; - public string FileName; - public byte[] ExtraField; - public string FileComment; - }; - - class LFHeader - { - public uint Signature; // 0x04034b50 - public ushort VersionNeededToExtract; - public ushort GeneralPurposeBitFlag; - public ushort CompressionMethod; - public ushort LastModFileTime; - public ushort LastModFileDate; - public uint CRC32; - public uint CompressedSize; - public uint UncompressedSize; - public ushort FileNameLength; - public ushort ExtraFieldLength; - public string FileName; - public byte[] ExtraField; - }; - - static int Main (string[] args) - { - if (args.Length < 3) { - Console.WriteLine ("Usage: fetch-windows-assemblers NDK_VERSION DESTINATION_DIRECTORY NDK_URL"); - return 1; - } - - Task fetcher = FetchFiles ( - args [0], - args [1], - new Uri (args [2]) - ); - - fetcher.Wait (); - - return fetcher.Result ? 0 : 1; - } - - static async Task FetchFiles (string ndkVersion, string destinationDirectory, Uri url) - { - var neededFiles = new HashSet (StringComparer.OrdinalIgnoreCase) { - $"android-ndk-r{ndkVersion}/toolchains/llvm/prebuilt/windows-x86_64/bin/i686-linux-android-as.exe", - $"android-ndk-r{ndkVersion}/toolchains/llvm/prebuilt/windows-x86_64/bin/arm-linux-androideabi-as.exe", - $"android-ndk-r{ndkVersion}/toolchains/llvm/prebuilt/windows-x86_64/bin/x86_64-linux-android-as.exe", - $"android-ndk-r{ndkVersion}/toolchains/llvm/prebuilt/windows-x86_64/bin/aarch64-linux-android-as.exe", - }; - - using (var httpClient = new HttpClient ()) { - bool success; - long size; - - Console.WriteLine ($"Retrieving {url}"); - (success, size) = await GetFileSize (httpClient, url); - if (!success) - return false; - - Console.WriteLine ($" File size: {size}"); - - EOCD eocd; - (success, eocd) = await GetEOCD (httpClient, url, size); - if (!success) { - Console.Error.WriteLine ("Failed to find the End of Central Directory record"); - return false; - } - - if (eocd.DiskNumber != 0) - throw new NotSupportedException ("Multi-disk ZIP archives not supported"); - - Console.WriteLine ($" Central Directory offset: {eocd.CDOffset} (0x{eocd.CDOffset:x})"); - Console.WriteLine ($" Central Directory size: {eocd.CDSize} (0x{eocd.CDSize})"); - Console.WriteLine ($" Total Entries: {eocd.TotalEntries}"); - - Stream cd; - (success, cd) = await ReadCD (httpClient, url, eocd.CDOffset, eocd.CDSize, size); - if (!success) { - Console.Error.WriteLine ("Failed to read the Central Directory"); - return false; - } - - Console.WriteLine (); - Console.WriteLine ("Entries:"); - if (!await ProcessEntries (httpClient, url, eocd, cd, neededFiles, destinationDirectory)) - return false; - } - - return true; - } - - static async Task ProcessEntries (HttpClient httpClient, Uri url, EOCD eocd, Stream centralDirectory, HashSet neededFiles, string destinationDirectory) - { - long foundEntries = 0; - - using (var br = new BinaryReader (centralDirectory)) { - long nread = 0; - long nentries = 1; - - while (nread < centralDirectory.Length && nentries <= eocd.TotalEntries) { - (bool success, CDHeader cdh) = ReadCDHeader (br, centralDirectory.Length, ref nread); - nentries++; - if (!success) { - Console.Error.WriteLine ($"Failed to read a Central Directory file header for entry {nentries}"); - return false; - } - - if (!neededFiles.Contains (cdh.FileName)) - continue; - - if (!await ReadEntry (httpClient, url, cdh, br, destinationDirectory)) - return false; - foundEntries++; - } - } - - if (foundEntries < neededFiles.Count) { - Console.WriteLine ($"Could not find all required binaries. Found {foundEntries} out of {neededFiles.Count}"); - return false; - } - - return true; - } - - static async Task ReadEntry (HttpClient httpClient, Uri url, CDHeader cdh, BinaryReader br, string destinationDirectory) - { - string compressedFilePath = Path.Combine (destinationDirectory, $"{Path.GetFileName (cdh.FileName)}.deflated"); - Console.WriteLine ($" {cdh.FileName} (offset: {cdh.RelativeOffsetOfLocalHeader})"); - - (bool success, Stream contentStream) = await ReadFileData (httpClient, url, cdh); - if (!success) { - Console.Error.WriteLine ("Failed to read file data"); - return false; - } - - using (var destFile = new BinaryWriter (File.OpenWrite (compressedFilePath))) { - using (var fbr = new BinaryReader (contentStream)) { - if (!await DownloadAndExtract (fbr, contentStream, destFile, compressedFilePath)) - return CleanupAndReturn (false); - } - } - - return CleanupAndReturn (true); - - bool CleanupAndReturn (bool retval) - { - if (File.Exists (compressedFilePath)) - File.Delete (compressedFilePath); - - return retval; - } - } - - static async Task DownloadAndExtract (BinaryReader fbr, Stream contentStream, BinaryWriter destFile, string destFileName) - { - long fread = 0; - (bool success, LFHeader lfh) = ReadLFHeader (fbr, contentStream.Length, ref fread); - if (!success) { - Console.Error.WriteLine ("Failed to read local file header"); - return false; - } - - uint dread = 0; - var buffer = new byte [8192]; - while (fread <= contentStream.Length && dread < lfh.CompressedSize) { - uint toRead; - if (lfh.CompressedSize - dread < buffer.Length) - toRead = lfh.CompressedSize - dread; - else - toRead = (uint)buffer.Length; - - int bread = await contentStream.ReadAsync (buffer, 0, (int)toRead); - if (bread == 0) - break; - destFile.Write (buffer, 0, bread); - fread += bread; - dread += (uint)bread; - } - - destFile.Flush (); - destFile.Close (); - destFile.Dispose (); - Extract (destFileName, lfh.CRC32); - - if (dread != lfh.CompressedSize) - Console.Error.WriteLine ($" Invalid data size: expected {lfh.CompressedSize} bytes, read {dread} bytes"); - - return true; - } - - static void Extract (string compressedFilePath, uint crc32FromHeader) - { - string outputFile = Path.GetFileNameWithoutExtension (compressedFilePath); - using (var fs = File.OpenRead (compressedFilePath)) { - using (var dfs = File.OpenWrite (outputFile)) { - Extract (fs, dfs, crc32FromHeader); - } - } - } - - static void Extract (Stream src, Stream dest, uint crc32FromHeader) - { - uint fileCRC = 0; - int fread = 0; - var crc32 = new CRC32 (); - var buffer = new byte [8192]; - - using (var iis = new DeflateStream (src, CompressionMode.Decompress)) { - while (true) { - fread = iis.Read(buffer, 0, buffer.Length); - if (fread <= 0) - break; - - fileCRC = crc32.Append (fileCRC, buffer, 0, fread); - dest.Write (buffer, 0, fread); - } - dest.Flush (); - } - - if (fileCRC != crc32FromHeader) - Console.Error.WriteLine ($" Invalid CRC32: expected 0x{crc32FromHeader:x}, got 0x{fileCRC:x}"); - } - - static async Task<(bool success, Stream data)> ReadFileData (HttpClient httpClient, Uri url, CDHeader cdh) - { - long fileOffset = cdh.RelativeOffsetOfLocalHeader; - long dataSize = - cdh.CompressedSize + - 30 + // local file header size, the static portion - cdh.FileName.Length + // They're the same in both haders - cdh.ExtraFieldLength + // This may differ between headers... - 16384; // ...so we add some extra padding - - var req = new HttpRequestMessage (HttpMethod.Get, url); - req.Headers.ConnectionClose = true; - req.Headers.Range = new RangeHeaderValue (fileOffset, fileOffset + dataSize); - - HttpResponseMessage resp = await httpClient.SendAsync (req).ConfigureAwait (false); - - if (!resp.IsSuccessStatusCode) { - Console.Error.WriteLine ($"Failed to read file data: HTTP error {resp.StatusCode}"); - return (false, null); - } - - Stream s = await resp.Content.ReadAsStreamAsync ().ConfigureAwait (false); - if (s.Length < dataSize) { - Console.Error.WriteLine ($"Failed to read file data: invalid data length ({s.Length} < {dataSize})"); - s.Dispose (); - return (false, null); - } - - return (true, s); - } - - static (bool success, LFHeader lfh) ReadLFHeader (BinaryReader cdr, long dataLength, ref long nread) - { - var lfh = new LFHeader (); - - bool worked; - string whatFailed = null; - lfh.Signature = ReadUInt (cdr, dataLength, ref nread, out worked); - if (!worked || lfh.Signature != LFHeaderSignature) { - whatFailed = $"Signature ({lfh.Signature:x} != {LFHeaderSignature:x})"; - goto failed; - } - - lfh.VersionNeededToExtract = ReadUShort (cdr, dataLength, ref nread, out worked); - if (!worked) { - whatFailed = "VersionNeededToExtract"; - goto failed; - } - - lfh.GeneralPurposeBitFlag = ReadUShort (cdr, dataLength, ref nread, out worked); - if (!worked) { - whatFailed = "GeneralPurposeBitFlag"; - goto failed; - } - - lfh.CompressionMethod = ReadUShort (cdr, dataLength, ref nread, out worked); - if (!worked) { - whatFailed = "CompressionMethod"; - goto failed; - } - - lfh.LastModFileTime = ReadUShort (cdr, dataLength, ref nread, out worked); - if (!worked) { - whatFailed = "LastModFileTime"; - goto failed; - } - - lfh.LastModFileDate = ReadUShort (cdr, dataLength, ref nread, out worked); - if (!worked) { - whatFailed = "LastModFileDate"; - goto failed; - } - - lfh.CRC32 = ReadUInt (cdr, dataLength, ref nread, out worked); - if (!worked) { - whatFailed = "CRC32"; - goto failed; - } - - lfh.CompressedSize = ReadUInt (cdr, dataLength, ref nread, out worked); - if (!worked) { - whatFailed = "CompressedSize"; - goto failed; - } - - lfh.UncompressedSize = ReadUInt (cdr, dataLength, ref nread, out worked); - if (!worked) { - whatFailed = "UncompressedSize"; - goto failed; - } - - lfh.FileNameLength = ReadUShort (cdr, dataLength, ref nread, out worked); - if (!worked) { - whatFailed = "FileNameLength"; - goto failed; - } - - lfh.ExtraFieldLength = ReadUShort (cdr, dataLength, ref nread, out worked); - if (!worked) { - whatFailed = "ExtraFieldLength"; - goto failed; - } - - byte[] bytes = ReadBytes (cdr, lfh.FileNameLength, dataLength, ref nread, out worked); - if (!worked) { - whatFailed = "FileName (bytes)"; - goto failed; - } - - lfh.FileName = Encoding.ASCII.GetString (bytes); - if (!worked) { - whatFailed = "FileName (ASCII decode)"; - goto failed; - } - - if (lfh.ExtraFieldLength > 0) { - lfh.ExtraField = ReadBytes (cdr, lfh.ExtraFieldLength, dataLength, ref nread, out worked); - if (!worked) { - whatFailed = "ExtraField"; - goto failed; - } - } - - return (true, lfh); - - failed: - if (!String.IsNullOrEmpty (whatFailed)) - Console.Error.WriteLine ($"Failed to read a local file header field: {whatFailed}"); - - return (false, null); - } - - static (bool success, CDHeader cdh) ReadCDHeader (BinaryReader cdr, long dataLength, ref long nread) - { - var cdh = new CDHeader (); - - bool worked; - string whatFailed = null; - cdh.Signature = ReadUInt (cdr, dataLength, ref nread, out worked); - if (!worked || cdh.Signature != CDHeaderSignature) { - whatFailed = "Signature ({cdh.Signature:x} != {CDHeaderSignature:x})"; - goto failed; - } - - cdh.VersionMadeBy = ReadUShort (cdr, dataLength, ref nread, out worked); - if (!worked) { - whatFailed = "VersionMadeBy"; - goto failed; - } - - cdh.VersionNeededToExtract = ReadUShort (cdr, dataLength, ref nread, out worked); - if (!worked) { - whatFailed = "VersionNeededToExtract"; - goto failed; - } - - cdh.GeneralPurposeBitFlag = ReadUShort (cdr, dataLength, ref nread, out worked); - if (!worked) { - whatFailed = "GeneralPurposeBitFlag"; - goto failed; - } - - cdh.CompressionMethod = ReadUShort (cdr, dataLength, ref nread, out worked); - if (!worked) { - whatFailed = "CompressionMethod"; - goto failed; - } - - cdh.LastModFileTime = ReadUShort (cdr, dataLength, ref nread, out worked); - if (!worked) { - whatFailed = "LastModFileTime"; - goto failed; - } - - cdh.LastModFileDate = ReadUShort (cdr, dataLength, ref nread, out worked); - if (!worked) { - whatFailed = "LastModFileDate"; - goto failed; - } - - cdh.CRC32 = ReadUInt (cdr, dataLength, ref nread, out worked); - if (!worked) { - whatFailed = "CRC32"; - goto failed; - } - - cdh.CompressedSize = ReadUInt (cdr, dataLength, ref nread, out worked); - if (!worked) { - whatFailed = "CompressedSize"; - goto failed; - } - - cdh.UncompressedSize = ReadUInt (cdr, dataLength, ref nread, out worked); - if (!worked) { - whatFailed = "UncompressedSize"; - goto failed; - } - - cdh.FileNameLength = ReadUShort (cdr, dataLength, ref nread, out worked); - if (!worked) { - whatFailed = "FileNameLength"; - goto failed; - } - - cdh.ExtraFieldLength = ReadUShort (cdr, dataLength, ref nread, out worked); - if (!worked) { - whatFailed = "ExtraFieldLength"; - goto failed; - } - - cdh.FileCommentLength = ReadUShort (cdr, dataLength, ref nread, out worked); - if (!worked) { - whatFailed = "FileCommentLength"; - goto failed; - } - - cdh.DiskNumberStart = ReadUShort (cdr, dataLength, ref nread, out worked); - if (!worked) { - whatFailed = "DiskNumberStart"; - goto failed; - } - - cdh.InternalFileAttributes = ReadUShort (cdr, dataLength, ref nread, out worked); - if (!worked) { - whatFailed = "InternalFileAttributes"; - goto failed; - } - - cdh.ExternalFileAttributes = ReadUInt (cdr, dataLength, ref nread, out worked); - if (!worked) { - whatFailed = "ExternalFileAttributes"; - goto failed; - } - - cdh.RelativeOffsetOfLocalHeader = ReadUInt (cdr, dataLength, ref nread, out worked); - if (!worked) { - whatFailed = "RelativeOffsetOfLocalHeader"; - goto failed; - } - - byte[] bytes = ReadBytes (cdr, cdh.FileNameLength, dataLength, ref nread, out worked); - if (!worked) { - whatFailed = "FileName (bytes)"; - goto failed; - } - - cdh.FileName = Encoding.ASCII.GetString (bytes); - if (!worked) { - whatFailed = "FileName (ASCII decode)"; - goto failed; - } - - if (cdh.ExtraFieldLength > 0) { - cdh.ExtraField = ReadBytes (cdr, cdh.ExtraFieldLength, dataLength, ref nread, out worked); - if (!worked) { - whatFailed = "ExtraField"; - goto failed; - } - } - - if (cdh.FileCommentLength > 0) { - bytes = ReadBytes (cdr, cdh.FileCommentLength, dataLength, ref nread, out worked); - if (!worked) { - whatFailed = "FileComment (bytes)"; - goto failed; - } - cdh.FileComment = Encoding.ASCII.GetString (bytes); - } - - return (true, cdh); - - failed: - if (!String.IsNullOrEmpty (whatFailed)) - Console.Error.WriteLine ($"Failed to read a central directory header field: {whatFailed}"); - - return (false, null); - } - - static ushort ReadUShort (BinaryReader br, long dataLength, ref long nread, out bool success) - { - success = false; - if (dataLength - nread < 2) - return 0; - - ushort ret = br.ReadUInt16 (); - nread += 2; - - success = true; - return ret; - } - - static uint ReadUInt (BinaryReader br, long dataLength, ref long nread, out bool success) - { - success = false; - if (dataLength - nread < 4) - return 0; - - uint ret = br.ReadUInt32 (); - nread += 4; - - success = true; - return ret; - } - - static byte[] ReadBytes (BinaryReader br, int neededBytes, long dataLength, ref long nread, out bool success) - { - success = false; - if (dataLength - nread < neededBytes) - return null; - - byte[] ret = br.ReadBytes (neededBytes); - nread += neededBytes; - - success = true; - return ret; - } - - static async Task<(bool success, Stream cd)> ReadCD (HttpClient httpClient, Uri url, uint cdOffset, uint cdSize, long fileSize) - { - long fileOffset = cdOffset; - var req = new HttpRequestMessage (HttpMethod.Get, url); - req.Headers.ConnectionClose = true; - req.Headers.Range = new RangeHeaderValue (fileOffset, fileOffset + cdSize); - - HttpResponseMessage resp = await httpClient.SendAsync (req).ConfigureAwait (false); - if (!resp.IsSuccessStatusCode) { - Console.Error.WriteLine ($"Failed to read Central Directory: HTTP error {resp.StatusCode}"); - return (false, null); - } - - Stream s = await resp.Content.ReadAsStreamAsync ().ConfigureAwait (false); - if (s.Length < cdSize) { - Console.Error.WriteLine ($"Failed to read Central Directory: invalid data length ({s.Length} < {cdSize})"); - s.Dispose (); - return (false, null); - } - - return (true, s); - } - - static async Task<(bool success, EOCD eocd)> GetEOCD (HttpClient httpClient, Uri url, long fileSize) - { - long fileOffset = fileSize - EndFileChunkSize; - var req = new HttpRequestMessage (HttpMethod.Get, url); - req.Headers.ConnectionClose = true; - req.Headers.Range = new RangeHeaderValue (fileOffset, fileSize); - - HttpResponseMessage resp = await httpClient.SendAsync (req).ConfigureAwait (false); - if (!resp.IsSuccessStatusCode) - return (false, null); - - using (var eocdStream = await resp.Content.ReadAsStreamAsync ().ConfigureAwait (false)) { - using (var sr = new BinaryReader (eocdStream)) { - byte[] expected = {0x50, 0x4b, 0x05, 0x06}; - int expectedPos = 0; - - for (int i = 0; i < eocdStream.Length; i++) { - byte b = sr.ReadByte (); - if (b != expected [expectedPos]) { - expectedPos = 0; - continue; - } - - if (expectedPos == expected.Length - 1) { - // We've found the signature - var eocd = new EOCD (); - eocd.Signature = 0x06054b50; - eocd.DiskNumber = sr.ReadUInt16 (); - eocd.CDStartDisk = sr.ReadUInt16 (); - eocd.TotalEntriesThisDisk = sr.ReadUInt16 (); - eocd.TotalEntries = sr.ReadUInt16 (); - eocd.CDSize = sr.ReadUInt32 (); - eocd.CDOffset = sr.ReadUInt32 (); - eocd.CommentLength = sr.ReadUInt16 (); - - return (true, eocd); - } - - expectedPos++; - if (expectedPos >= expected.Length) - expectedPos = 0; - } - } - } - - return (false, null); - } - - static async Task<(bool success, long size)> GetFileSize (HttpClient httpClient, Uri url) - { - var req = new HttpRequestMessage (HttpMethod.Head, url); - req.Headers.ConnectionClose = true; - - HttpResponseMessage resp = await httpClient.SendAsync (req).ConfigureAwait (false); - if (!resp.IsSuccessStatusCode || !resp.Content.Headers.ContentLength.HasValue) - return (false, 0); - - return (true, resp.Content.Headers.ContentLength.Value); - } -} diff --git a/build-tools/fetch-windows-assemblers/fetch-windows-assemblers.csproj b/build-tools/fetch-windows-assemblers/fetch-windows-assemblers.csproj deleted file mode 100644 index 60add5192..000000000 --- a/build-tools/fetch-windows-assemblers/fetch-windows-assemblers.csproj +++ /dev/null @@ -1,40 +0,0 @@ - - - - Debug - x86 - {09518DF2-C7B1-4CDB-849A-9F91F4BEA925} - Exe - fetchwindowsassemblers - fetch-windows-assemblers - v4.7 - - - - true - full - false - ..\..\bin\BuildDebug - DEBUG; - prompt - 4 - true - x86 - - - true - ..\..\bin\BuildRelease - prompt - 4 - true - x86 - - - - - - - - - - diff --git a/build-tools/installers/create-installers.targets b/build-tools/installers/create-installers.targets index 339cd7553..0174beaaf 100644 --- a/build-tools/installers/create-installers.targets +++ b/build-tools/installers/create-installers.targets @@ -1,7 +1,7 @@ - + diff --git a/build-tools/license-data/Mono-MIT.txt b/build-tools/license-data/Mono-MIT.txt new file mode 100644 index 000000000..b0a178006 --- /dev/null +++ b/build-tools/license-data/Mono-MIT.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Mono Project + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the ""Software""), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/build-tools/mingw-dependencies/mingw-dependencies.csproj b/build-tools/mingw-dependencies/mingw-dependencies.csproj deleted file mode 100644 index 142b219b3..000000000 --- a/build-tools/mingw-dependencies/mingw-dependencies.csproj +++ /dev/null @@ -1,28 +0,0 @@ - - - - Debug - AnyCPU - {2C1C68CD-CFED-4DEB-A2D3-61D6932F3E8E} - $(MSBuildThisFileFullPath) - - - - $(MingwDependenciesRootDirectory) - - - $(MingwDependenciesRootDirectory) - - - - - ResolveReferences; - _BuildUnlessCached - - - - - - - - diff --git a/build-tools/mingw-dependencies/mingw-dependencies.projitems b/build-tools/mingw-dependencies/mingw-dependencies.projitems deleted file mode 100644 index 7cba418df..000000000 --- a/build-tools/mingw-dependencies/mingw-dependencies.projitems +++ /dev/null @@ -1,56 +0,0 @@ - - - - cmake - ninja - <_OutputSubdir32>x86 - <_OutputSubdir64>x86_64 - <_CommonCmakeProjectFlags>-DCMAKE_MAKE_PROGRAM=$(Ninja) -GNinja -DBUILD_TESTS=OFF - <_CmakeNoSharedLibsFlags>-DBUILD_SHARED_LIBS=OFF - <_CmakeWithSharedLibsFlags>-DBUILD_SHARED_LIBS=ON - <_CMakeFlags32>-DCMAKE_INSTALL_PREFIX=$(MingwDependenciesRootDirectory)\$(_OutputSubdir32) - <_CMakeFlags64>-DCMAKE_INSTALL_PREFIX=$(MingwDependenciesRootDirectory)\$(_OutputSubdir64) - - - <_CmakeMingwDependency Include="dlfcn-win32-64" Condition="$(AndroidSupportedHostJitAbisForConditionalChecks.Contains (':mxe-Win64:'))"> - $(CMake) - $(Ninja) - dlfcn-win32 - ..\..\bin\Build$(Configuration)\mingw-64.cmake - $(_CommonCmakeProjectFlags) $(_CMakeFlags64) $(_CmakeNoSharedLibsFlags) - $(MingwDependenciesRootDirectory)\$(_OutputSubdir64)\ - lib\libdl.a - - - <_CmakeMingwDependency Include="dlfcn-win32-32" Condition="$(AndroidSupportedHostJitAbisForConditionalChecks.Contains (':mxe-Win32:'))"> - $(CMake) - $(Ninja) - dlfcn-win32 - ..\..\bin\Build$(Configuration)\mingw-32.cmake - $(_CommonCmakeProjectFlags) $(_CMakeFlags32) $(_CmakeNoSharedLibsFlags) - $(MingwDependenciesRootDirectory)\$(_OutputSubdir32)\ - lib\libdl.a - - - <_CmakeMingwDependency Include="mman-win32-64" Condition="$(AndroidSupportedHostJitAbisForConditionalChecks.Contains (':mxe-Win64:'))"> - $(CMake) - $(Ninja) - mman-win32 - ..\..\bin\Build$(Configuration)\mingw-64.cmake - $(_CommonCmakeProjectFlags) $(_CMakeFlags64) $(_CmakeNoSharedLibsFlags) - $(MingwDependenciesRootDirectory)\$(_OutputSubdir64)\ - lib\libmman.a - - - <_CmakeMingwDependency Include="mman-win32-32" Condition="$(AndroidSupportedHostJitAbisForConditionalChecks.Contains (':mxe-Win32:'))"> - $(CMake) - $(Ninja) - mman-win32 - ..\..\bin\Build$(Configuration)\mingw-32.cmake - $(_CommonCmakeProjectFlags) $(_CMakeFlags32) $(_CmakeNoSharedLibsFlags) - $(MingwDependenciesRootDirectory)\$(_OutputSubdir32)\ - lib\libmman.a - - - - diff --git a/build-tools/mingw-dependencies/mingw-dependencies.targets b/build-tools/mingw-dependencies/mingw-dependencies.targets deleted file mode 100644 index 9e27a89ca..000000000 --- a/build-tools/mingw-dependencies/mingw-dependencies.targets +++ /dev/null @@ -1,89 +0,0 @@ - - - - - - - CheckForRequiredPrograms; - _Configure; - _Make - - <_LibZipOutputPath>$(XAInstallPrefix)xbuild\Xamarin\Android\ - - - - - - - - - - - - - - - - - - - - - - - - - - %(_CmakeMingwDependency.Submodule) - - - - - - - - - - <_Now>$([System.DateTime]::Now.Ticks) - - - - - - - - - - - diff --git a/build-tools/scripts/BuildEverything.mk b/build-tools/scripts/BuildEverything.mk index b97ce25e7..8005b1192 100644 --- a/build-tools/scripts/BuildEverything.mk +++ b/build-tools/scripts/BuildEverything.mk @@ -1,130 +1,26 @@ -PRODUCT_VERSION = $(shell $(MSBUILD) $(MSBUILD_FLAGS) /p:DoNotLoadOSProperties=True /nologo /v:minimal /t:GetProductVersion build-tools/scripts/Info.targets | tr -d '[[:space:]]') - -GIT_BRANCH = $(shell LANG=C build-tools/scripts/get-git-branch.sh | tr -d '[[:space:]]' | tr -C a-zA-Z0-9- _) -GIT_COMMIT = $(shell LANG=C git log --no-color --first-parent -n1 --pretty=format:%h) - -# In which commit did $(PRODUCT_VERSION) change? 00000000 if uncommitted --commit-of-last-version-change = $(shell LANG=C git blame Configuration.props | grep '' | grep -v grep | sed 's/ .*//') - -# How many commits have passed since $(-commit-of-last-version-change)? -# "0" when commit hash is invalid (e.g. 00000000) --num-commits-since-version-change = $(shell LANG=C git log $(-commit-of-last-version-change)..HEAD --oneline 2>/dev/null | wc -l | sed 's/ //g') - -ifeq ($(OS_NAME),Linux) -ZIP_EXTENSION = tar.bz2 -else -ZIP_EXTENSION = zip -endif - -ZIP_OUTPUT_BASENAME = xamarin.android-oss_v$(PRODUCT_VERSION).$(-num-commits-since-version-change)_$(OS_NAME)-$(OS_ARCH)_$(GIT_BRANCH)_$(GIT_COMMIT)-$(CONFIGURATION) -ZIP_OUTPUT = $(ZIP_OUTPUT_BASENAME).$(ZIP_EXTENSION) - - -## The following values *must* use SPACE, **not** TAB, to separate values. - -# $(ALL_API_LEVELS) and $(ALL_FRAMEWORKS) must be kept in sync w/ each other -ALL_API_LEVELS = 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 -# this was different from ALL_API_LEVELS when API Level 26 was "O". Same could happen in the future. -ALL_PLATFORM_IDS = 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 Q -# supported api levels -ALL_FRAMEWORKS = _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ v4.4 v4.4.87 v5.0 v5.1 v6.0 v7.0 v7.1 v8.0 v8.1 v9.0 v9.0.99 -API_LEVELS = 19 20 21 22 23 24 25 26 27 28 29 -STABLE_API_LEVELS = 19 20 21 22 23 24 25 26 27 28 - -## The preceding values *must* use SPACE, **not** TAB, to separate values. - - -FRAMEWORKS = $(foreach a, $(API_LEVELS), $(word $(a),$(ALL_FRAMEWORKS))) -STABLE_FRAMEWORKS = $(foreach a, $(STABLE_API_LEVELS), $(word $(a),$(ALL_FRAMEWORKS))) -PLATFORM_IDS = $(foreach a, $(API_LEVELS), $(word $(a),$(ALL_PLATFORM_IDS))) - -ALL_JIT_ABIS = \ - armeabi-v7a \ - arm64-v8a \ - x86 \ - x86_64 - -ALL_HOST_ABIS = \ - $(shell uname) - -ALL_AOT_ABIS = \ - armeabi-v7a \ - arm64 \ - x86 \ - x86_64 \ - win-armeabi-v7a \ - win-arm64 \ - win-x86 \ - win-x86_64 - -ifneq ($(OS_NAME),Linux) -ALL_HOST_ABIS += \ - mxe-Win32 \ - mxe-Win64 -endif - -ifneq ($(OS_NAME),Linux) -MONO_OPTIONS += --arch=64 -endif - -_space := -_space += - -# usage: $(call join-with,SEPARATOR,LIST) -# Joins elements of LISt with SEPARATOR. -join-with = $(subst $(_space),$(1),$(strip $(2))) - - -_MSBUILD_ARGS = \ - /p:AndroidSupportedTargetJitAbis=$(call join-with,:,$(ALL_JIT_ABIS)) \ - /p:AndroidSupportedHostJitAbis=$(call join-with,:,$(ALL_HOST_ABIS)) \ - /p:AndroidSupportedTargetAotAbis=$(call join-with,:,$(ALL_AOT_ABIS)) - -.PHONY: leeroy jenkins leeroy-all opentk-jcw framework-assemblies +.PHONY: leeroy jenkins leeroy-all opentk-jcw .PHONY: create-vsix -jenkins:: prepare leeroy $(ZIP_OUTPUT) +# +# framework-assemblies lives in bin/Build$(CONFIGURATION)/rules.mk generated by `make prepare` +# +# It has to be invoked with $(MAKE) because otherwise rules.mk would not be included and we'd +# get a build failure. +# +# The other targets depended upon by leeroy also require rules.mk to be present and thus they +# are invoked in the same way framework-assemblies is +# +jenkins:: prepare-jenkins + $(MAKE) leeroy $(ZIP_OUTPUT) leeroy: leeroy-all framework-assemblies opentk-jcw leeroy-all: - $(call MSBUILD_BINLOG,leeroy-all,$(_SLN_BUILD)) $(SOLUTION) /p:Configuration=$(CONFIGURATION) $(_MSBUILD_ARGS) && \ - $(call CREATE_THIRD_PARTY_NOTICES,bin/$(CONFIGURATION)/lib/xamarin.android/ThirdPartyNotices.txt,$(THIRD_PARTY_NOTICE_LICENSE_TYPE),True,False) - -framework-assemblies: - PREV_VERSION="v1.0"; \ - $(foreach a, $(API_LEVELS), \ - CUR_VERSION=`echo "$(ALL_FRAMEWORKS)"|tr -s " "|cut -d " " -s -f $(a)`; \ - REDIST_FILE=bin/$(CONFIGURATION)/lib/xamarin.android/xbuild-frameworks/MonoAndroid/$${CUR_VERSION}/RedistList/FrameworkList.xml; \ - grep -q $${PREV_VERSION} $${REDIST_FILE}; \ - if [ $$? -ne 0 ] ; then \ - rm -f bin/$(CONFIGURATION)/lib/xamarin.android/xbuild-frameworks/MonoAndroid/$${CUR_VERSION}/RedistList/FrameworkList.xml; \ - fi; \ - $(call MSBUILD_BINLOG,Mono.Android,$(_SLN_BUILD)) src/Mono.Android/Mono.Android.csproj \ - /p:Configuration=$(CONFIGURATION) $(_MSBUILD_ARGS) \ - /p:AndroidApiLevel=$(a) /p:AndroidPlatformId=$(word $(a), $(ALL_PLATFORM_IDS)) /p:AndroidFrameworkVersion=$${CUR_VERSION} \ - /p:AndroidPreviousFrameworkVersion=$${PREV_VERSION} || exit 1; \ - PREV_VERSION=$${CUR_VERSION}; ) - rm -f bin/$(CONFIGURATION)/lib/xamarin.android/xbuild-frameworks/MonoAndroid/v1.0/Xamarin.Android.NUnitLite.dll; \ - $(call MSBUILD_BINLOG,NUnitLite,$(_SLN_BUILD)) $(MSBUILD_FLAGS) src/Xamarin.Android.NUnitLite/Xamarin.Android.NUnitLite.csproj \ - /p:Configuration=$(CONFIGURATION) $(_MSBUILD_ARGS) \ - /p:AndroidApiLevel=$(firstword $(API_LEVELS)) /p:AndroidPlatformId=$(word $(firstword $(API_LEVELS)), $(ALL_PLATFORM_IDS)) \ - /p:AndroidFrameworkVersion=$(firstword $(FRAMEWORKS)) || exit 1; - _latest_stable_framework=$$($(MSBUILD) /p:DoNotLoadOSProperties=True /nologo /v:minimal /t:GetAndroidLatestStableFrameworkVersion build-tools/scripts/Info.targets | tr -d '[[:space:]]') ; \ - rm -f "bin/$(CONFIGURATION)/lib/xamarin.android/xbuild-frameworks/MonoAndroid/$$_latest_stable_framework"/Mono.Android.Export.* ; \ - $(call MSBUILD_BINLOG,Mono.Android.Export,$(_SLN_BUILD)) $(MSBUILD_FLAGS) src/Mono.Android.Export/Mono.Android.Export.csproj \ - /p:Configuration=$(CONFIGURATION) $(_MSBUILD_ARGS) \ - /p:AndroidApiLevel=$(firstword $(API_LEVELS)) /p:AndroidPlatformId=$(word $(firstword $(API_LEVELS)), $(ALL_PLATFORM_IDS)) \ - /p:AndroidFrameworkVersion=$(firstword $(FRAMEWORKS)) || exit 1; \ - rm -f "bin/$(CONFIGURATION)/lib/xamarin.android/xbuild-frameworks/MonoAndroid/$$_latest_stable_framework"/OpenTK-1.0.* ; \ - $(call MSBUILD_BINLOG,OpenTK,$(_SLN_BUILD)) $(MSBUILD_FLAGS) src/OpenTK-1.0/OpenTK.csproj \ - /p:Configuration=$(CONFIGURATION) $(_MSBUILD_ARGS) \ - /p:AndroidApiLevel=$(firstword $(API_LEVELS)) /p:AndroidPlatformId=$(word $(firstword $(API_LEVELS)), $(ALL_PLATFORM_IDS)) \ - /p:AndroidFrameworkVersion=$(firstword $(FRAMEWORKS)) || exit 1; + $(call MSBUILD_BINLOG,leeroy-all,$(_SLN_BUILD)) $(SOLUTION) /p:Configuration=$(CONFIGURATION) $(_MSBUILD_ARGS) opentk-jcw: - $(foreach a, $(API_LEVELS), \ + $(foreach api_level, $(API_LEVELS), \ touch bin/$(CONFIGURATION)/lib/xamarin.android/xbuild-frameworks/MonoAndroid/*/OpenTK-1.0.dll; \ $(call MSBUILD_BINLOG,OpenTK-JCW,$(_SLN_BUILD)) $(MSBUILD_FLAGS) src/OpenTK-1.0/OpenTK.csproj \ /t:GenerateJavaCallableWrappers /p:Configuration=$(CONFIGURATION) $(_MSBUILD_ARGS) \ - /p:AndroidApiLevel=$(a) /p:AndroidPlatformId=$(word $(a), $(ALL_PLATFORM_IDS)) /p:AndroidFrameworkVersion=$(word $(a), $(ALL_FRAMEWORKS)) || exit 1; ) + /p:AndroidApiLevel=$(api_level) /p:AndroidPlatformId=$(word $(api_level), $(ALL_PLATFORM_IDS)) /p:AndroidFrameworkVersion=$(word $(api_level), $(ALL_FRAMEWORKS)) || exit 1; ) diff --git a/build-tools/scripts/Ndk.projitems.in b/build-tools/scripts/Ndk.projitems.in new file mode 100644 index 000000000..14a9400db --- /dev/null +++ b/build-tools/scripts/Ndk.projitems.in @@ -0,0 +1,36 @@ + + + + @NDK_RELEASE@ + @NDK_ARMEABI_V7_API@ + @NDK_ARM64_V8A_API@ + @NDK_X86_API@ + @NDK_X86_64_API@ + + + + + $(AndroidNdkApiLevel_ArmV7a) + + + + $(AndroidNdkApiLevel_ArmV8a) + + + + $(AndroidNdkApiLevel_X86) + + + + $(AndroidNdkApiLevel_X86_64) + + + diff --git a/build-tools/scripts/Ndk.targets b/build-tools/scripts/Ndk.targets index d2b424103..9972e4b3a 100644 --- a/build-tools/scripts/Ndk.targets +++ b/build-tools/scripts/Ndk.targets @@ -1,38 +1,9 @@ - 19c - 16 - 21 - 16 - 21 + $(MSBuildThisFileDirectory)..\..\bin\Build$(Configuration)\Ndk.projitems - - - - - $(AndroidNdkApiLevel_ArmV7a) - - - - $(AndroidNdkApiLevel_ArmV8a) - - - - $(AndroidNdkApiLevel_X86) - - - - $(AndroidNdkApiLevel_X86_64) - - + diff --git a/build-tools/scripts/Packaging.mk b/build-tools/scripts/Packaging.mk index 3cc7f2cd4..7f244a2c9 100644 --- a/build-tools/scripts/Packaging.mk +++ b/build-tools/scripts/Packaging.mk @@ -1,11 +1,3 @@ -_BUNDLE_ZIPS_INCLUDE = \ - $(ZIP_OUTPUT_BASENAME)/ThirdPartyNotices.txt \ - $(ZIP_OUTPUT_BASENAME)/bin/Debug \ - $(ZIP_OUTPUT_BASENAME)/bin/Release - -_BUNDLE_ZIPS_EXCLUDE = \ - $(ZIP_OUTPUT_BASENAME)/bin/*/bundle-*.zip - create-installers: create-pkg create-vsix create-pkg: diff --git a/build-tools/scripts/PrepareWindows.targets b/build-tools/scripts/PrepareWindows.targets index ebb39ca61..acb2fc648 100644 --- a/build-tools/scripts/PrepareWindows.targets +++ b/build-tools/scripts/PrepareWindows.targets @@ -3,97 +3,24 @@ Debug <_TopDir>$(MSBuildThisFileDirectory)..\.. - <_NuGet>.nuget\NuGet.exe - <_NuGetVerbosity>-Verbosity Detailed + <_XAPrepareExe>$(MSBuildThisFileDirectory)..\xaprepare\xaprepare\bin\$(Configuration)\xaprepare.exe + <_XAPrepareStandardArgs>--no-emoji --run-mode=CI - - - - - + + + - + + + + - - - - - - - - - - - - - - - - - - - <_CecilFiles Include="$(_TopDir)\external\Java.Interop\external\Mono.Cecil*" /> - - - - - - - + diff --git a/build-tools/mingw-dependencies/mingw-dependencies.props b/build-tools/scripts/bundle-path.targets.in similarity index 54% rename from build-tools/mingw-dependencies/mingw-dependencies.props rename to build-tools/scripts/bundle-path.targets.in index 8ef41c0ce..8d2172dd6 100644 --- a/build-tools/mingw-dependencies/mingw-dependencies.props +++ b/build-tools/scripts/bundle-path.targets.in @@ -1,6 +1,7 @@ - <_SubmoduleTopDir>$(XamarinAndroidSourcePath)\external + @XA_BUNDLE_VERSION@ + @XA_BUNDLE_FILE_NAME@ diff --git a/build-tools/scripts/dependencies/debian-common.sh b/build-tools/scripts/dependencies/debian-common.sh deleted file mode 100644 index ca38a024b..000000000 --- a/build-tools/scripts/dependencies/debian-common.sh +++ /dev/null @@ -1,46 +0,0 @@ -DEBIAN_COMMON_DEPS="autoconf - autotools-dev - automake - curl - g++-mingw-w64 - gcc-mingw-w64 - git - libtool - libncurses-dev - libz-mingw-w64-dev - libzip-dev - linux-libc-dev - make - unzip - vim-common - sqlite3 - zlib1g-dev - " - -if [ "$OS_ARCH" = "x86_64" ]; then -DEBIAN_COMMON_DEPS="$DEBIAN_COMMON_DEPS - lib32stdc++6 - lib32z1 - " -fi - -debian_install() -{ - if [ "$NO_SUDO" = "true" ]; then - for p in $DISTRO_DEPS; do - if dpkg -l $p > /dev/null 2>&1 ; then - echo "[INSTALLED] $p" - else - echo "[ MISSING ] $p" - PACKAGES_MISSING=yes - fi - done - if [ "x$PACKAGES_MISSING" = "xyes" ]; then - echo Some packages are missing, cannot continue - echo - exit 1 - fi - else - sudo apt-get -f -u -y install $DISTRO_DEPS - fi -} diff --git a/build-tools/scripts/dependencies/linux-prepare-Arch.sh b/build-tools/scripts/dependencies/linux-prepare-Arch.sh deleted file mode 100644 index ca7d63a61..000000000 --- a/build-tools/scripts/dependencies/linux-prepare-Arch.sh +++ /dev/null @@ -1,57 +0,0 @@ -ARCH_DEPS="autoconf - automake - binutils - bison - curl - fakeroot - file - findutils - flex - gawk - gcc - gettext - git - grep - groff - gtk-sharp-2 - gzip - jdk8-openjdk - libtool - libzip - m4 - make - nuget - patch - pkg-config - pkg-config - referenceassemblies-pcl - sed - texinfo - unzip - which - zip - " -all_installed=yes -for pkg in $ARCH_DEPS -do - if ! pacman -Qq "$pkg" > /dev/null 2>&1 - then - all_installed=no - missing_pkgs+=("$pkg") - fi -done -if [ "$NO_SUDO" = "false" ] -then - [ "$all_installed" = "yes" ] && exit - if ! sudo pacman -S --noconfirm --needed $ARCH_DEPS - then - echo "Failed to install required packages" - exit 1 - fi -else - if [ "$all_installed" = "no" ] - then - echo "Missing package(s): '${missing_pkgs[@]}'" - exit 1 - fi -fi diff --git a/build-tools/scripts/dependencies/linux-prepare-Debian.sh b/build-tools/scripts/dependencies/linux-prepare-Debian.sh deleted file mode 100644 index 251448ab4..000000000 --- a/build-tools/scripts/dependencies/linux-prepare-Debian.sh +++ /dev/null @@ -1,8 +0,0 @@ -. "`dirname $0`"/debian-common.sh - -DISTRO_DEPS="$DEBIAN_COMMON_DEPS $DISTRO_DEPS \ - zulu-8 - zlib1g-dev -" - -debian_install diff --git a/build-tools/scripts/dependencies/linux-prepare-LinuxMint.sh b/build-tools/scripts/dependencies/linux-prepare-LinuxMint.sh deleted file mode 100644 index 440917f83..000000000 --- a/build-tools/scripts/dependencies/linux-prepare-LinuxMint.sh +++ /dev/null @@ -1,16 +0,0 @@ -. "`dirname $0`"/debian-common.sh - -DISTRO_DEPS="$DEBIAN_COMMON_DEPS" - -MAJOR=$(echo $1 | cut -d '.' -f 1) -MINOR=$(echo $1 | cut -d '.' -f 2) - -if [ $MAJOR -ge 19 ]; then - NEED_LIBTOOL=yes -else - NEED_LIBTOOL=no -fi - -. "`dirname $0`"/ubuntu-common.sh - -debian_install diff --git a/build-tools/scripts/dependencies/linux-prepare-Ubuntu.sh b/build-tools/scripts/dependencies/linux-prepare-Ubuntu.sh deleted file mode 100644 index dc07e99ed..000000000 --- a/build-tools/scripts/dependencies/linux-prepare-Ubuntu.sh +++ /dev/null @@ -1,17 +0,0 @@ -. "`dirname $0`"/debian-common.sh - -MAJOR=$(echo $1 | cut -d '.' -f 1) -MINOR=$(echo $1 | cut -d '.' -f 2) - -if [ $MAJOR -eq 17 -a $MINOR -eq 10 ] || [ $MAJOR -ge 18 ]; then - NEED_LIBTOOL=yes - if [ $MAJOR -lt 19 ]; then - DISTRO_DEPS="$DEBIAN_COMMON_DEPS openjdk-8-jdk" - fi -else - NEED_LIBTOOL=no -fi - -. "`dirname $0`"/ubuntu-common.sh - -debian_install diff --git a/build-tools/scripts/dependencies/ubuntu-common.sh b/build-tools/scripts/dependencies/ubuntu-common.sh deleted file mode 100644 index f271badd8..000000000 --- a/build-tools/scripts/dependencies/ubuntu-common.sh +++ /dev/null @@ -1,11 +0,0 @@ -if [ "$OS_ARCH" = "x86_64" ]; then - DISTRO_DEPS="$DISTRO_DEPS - libx32tinfo-dev - linux-libc-dev:i386 - zlib1g-dev:i386 - " -fi - -if [ "$NEED_LIBTOOL" = "yes" ]; then - DISTRO_DEPS="$DISTRO_DEPS libtool-bin" -fi diff --git a/build-tools/scripts/runtime-helpers.mk b/build-tools/scripts/runtime-helpers.mk index 823f77afd..bb294467c 100644 --- a/build-tools/scripts/runtime-helpers.mk +++ b/build-tools/scripts/runtime-helpers.mk @@ -2,7 +2,6 @@ # Various helper/shortcut targets # MONO_RUNTIMES_DIR = src/mono-runtimes -MONO_RUNTIMES_PROJECT = $(MONO_RUNTIMES_DIR)/mono-runtimes.csproj MONO_RUNTIMES_BUILD_DIR = $(MONO_RUNTIMES_DIR)/obj/$(CONFIGURATION) # $(1) - architecture name diff --git a/build-tools/xa-prep-tasks/Xamarin.Android.BuildTools.PrepTasks/ReplaceFileContents.cs b/build-tools/xa-prep-tasks/Xamarin.Android.BuildTools.PrepTasks/ReplaceFileContents.cs index 8372a17a6..a4c828a51 100644 --- a/build-tools/xa-prep-tasks/Xamarin.Android.BuildTools.PrepTasks/ReplaceFileContents.cs +++ b/build-tools/xa-prep-tasks/Xamarin.Android.BuildTools.PrepTasks/ReplaceFileContents.cs @@ -18,6 +18,7 @@ namespace Xamarin.Android.BuildTools.PrepTasks public ITaskItem DestinationFile { get; set; } public string[] Replacements { get; set; } + public string ReplacementFilePath { get; set; } public override bool Execute () { @@ -25,13 +26,20 @@ namespace Xamarin.Android.BuildTools.PrepTasks Log.LogMessage (MessageImportance.Low, $" {nameof (SourceFile)}: {SourceFile.ItemSpec}"); Log.LogMessage (MessageImportance.Low, $" {nameof (DestinationFile)}: {DestinationFile.ItemSpec}"); Log.LogMessage (MessageImportance.Low, $" {nameof (Replacements)}:"); - foreach (var replacement in Replacements) { - Log.LogMessage (MessageImportance.Low, $" {replacement}"); + if (Replacements != null) { + foreach (var replacement in Replacements) { + Log.LogMessage (MessageImportance.Low, $" {replacement}"); + } } File.Delete (DestinationFile.ItemSpec); - var r = GetReplacementInfo (Replacements); + string[] replacements; + if (!String.IsNullOrEmpty (ReplacementFilePath)) + replacements = File.ReadAllLines (ReplacementFilePath); + else + replacements = Replacements; + var r = GetReplacementInfo (replacements); using (var i = File.OpenText (SourceFile.ItemSpec)) using (var o = File.CreateText (DestinationFile.ItemSpec)) { string line; diff --git a/build-tools/xaprepare/.gitignore b/build-tools/xaprepare/.gitignore new file mode 100644 index 000000000..ed9dbcb21 --- /dev/null +++ b/build-tools/xaprepare/.gitignore @@ -0,0 +1,405 @@ +# globs +Makefile.in +*.userprefs +*.usertasks +config.make +config.status +aclocal.m4 +install-sh +autom4te.cache/ +*.tar.gz +tarballs/ +test-results/ + +# Mac bundle stuff +*.dmg +*.app + +# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# content below from: https://github.com/github/gitignore/blob/master/VisualStudio.gitignore +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ \ No newline at end of file diff --git a/build-tools/xaprepare/README.md b/build-tools/xaprepare/README.md new file mode 100644 index 000000000..f7b14c526 --- /dev/null +++ b/build-tools/xaprepare/README.md @@ -0,0 +1,173 @@ + +**Table of Contents** + +- [Xamarin.Android build preparation utility](#xamarinandroid-build-preparation-utility) + - [Introduction](#introduction) + - [Why?](#why) + - [Supported operating systems](#supported-operating-systems) + - [Prerequisites](#prerequisites) + - [All systems](#all-systems) + - [macOS and Linux](#macos-and-linux) + - [macOS](#macos) + - [Build configuration](#build-configuration) + - [File naming convention](#file-naming-convention) + - [Configuration directory](#configuration-directory) + - [Running](#running) + - [Scenarios](#scenarios) + - [Invocation](#invocation) + - [Log files](#log-files) + + +# Xamarin.Android build preparation utility + +## Introduction + +The task and purpose of this utility is to prepare the `Xamarin.Android` source tree for build by +performing a number of steps which need to be done only once (or very few and far between) mostly after +the repository is freshly cloned. + +The utility is written in C# as a .NET 4.7 console app and does not depend on any other code within the +`xamarin-android` repository. + +## Why? + +The utility replaces older system which was implemented using a vast collection of MSBuild projects, +target files, property files distributed around the `Xamarin.Android` source tree which caused the following, +but not limited to, problems: + + - MSBuild doesn't very well interface with 3rd party build systems, which caused the need to employ a number + of workarounds/hacks in order to make things work. The workarounds made the code confusing and not very + friendly to occasional contributors. + - Settings, configurations etc were often duplicated between makefiles and msbuild (and even between different + MSBuild projects) which made it easy to make mistakes. It also made the whole setup hard to navigate. + - Due to the way MSBuild works, we had to build some projects twice in order to get things working - once during + preparation phase, second time during "normal" builds + - Retrieval of settings/property values from makefiles used MSBuild to invoke its targets which then printed + values to standard output to be captured by shell code inside the makefile. While it worked **most** of the time, + it caused mysterious and hard to track errors when it did not work (e.g. the shell invoking msbuild to get some + value would interpret "garbage" output and produce nonsensical errors) + +All of the above problems (and a few more) are addressed by this utility by putting all the configuration and code in +one place as well as the entire process using a real programming language which allows for more expressive and readable +implementation. + +## Supported operating systems + +Currently the utility supports `macOS`, ``Linux`` (``Debian`` and derivatives, `Arch` is to be implemented) +with `Windows` support in the works. + +## Prerequisites + +The utility requires that the following is present on the host system: + +### All systems + - [NuGet](https://www.nuget.org/downloads) + +### macOS and Linux + - [Mono runtime](https://www.mono-project.com/download/stable/) + +### macOS + - [Homebrew](https://brew.sh/) + - Xcode with command line utilities (for GNU Make as well as the compilers) + +## Build configuration + +The utility is designed to make it easy(-ier) to change all aspects of `Xamarin.Android` build preparation process including, +but not limited to: + + - Android API levels + - Mono runtimes/compilers/cross compilers + - Location of output files/logs/etc + - Contents of various "bundles" created by the build + - URLs to download Mono archive, installers etc from + - Build system dependencies and required program versions + +The idea here is to put all of the above, and more, in a single location (in a small number of C# source files) which contain +everyting that is required by other parts of the code, so that anyone (even with limited knowledge of the source tree or build +process) can make any necessary changes. The files are laid out in a way that the information found in them, albeit sometimes +voluminous, is self-explanatory and should not require much effort to understand and modify. + +### File naming convention + +Some files have the operating system embeded before their `.cs` extension. Such files are built and used only when the preparation +utility is built on that particular operating system. + +### Configuration directory + +The files mentioned above are found in the [ConfigAndData](xaprepare/ConfigAndData) directory and are briefly described below. + + - [AbiNames.cs](xaprepare/ConfigAndData/AbiNames.cs) + Rarely modified, contains all the target ABI names as used throughout the `Xamarin.Android` source as well as a number of + helper methods used throughout the preparation utility code. **Be very careful** when modifying the names there as it may + break the build! + - [BuildAndroidPlatforms.cs](xaprepare/ConfigAndData/BuildAndroidPlatforms.cs) + Modified whenever a new Android platform is added, this file names all of the Android API levels along with platform/API + identifiers and `Xamarin.Android` framework names corresponding to specific API levels. The file also contains specification + of minimum NDK API levels used for all the Android device targets. + - [CommonLicenses.cs](xaprepare/ConfigAndData/CommonLicenses.cs) + A file with constants containing paths to licenses commonly used by software `Xamarin.Android` uses. The licenses are used + when generating Third Party Notices. + - [Configurables](xaprepare/ConfigAndData/Configurables.cs) + The file (and its OS-specific companions) contain all of the tunable bits and pieces of configuration that affect various + aspects of the build. There are three subclasses with self-explanatory names: `Configurables.Paths`, `Configurables.Urls` and + `Configurables.Defaults` + - [Runtimes.cs](xaprepare/ConfigAndData/Runtimes.cs) + This file defines **all** of the Mono runtimes, BCL (Base Class Library) assemblies, utilities and components used by + `Xamarin.Android` in the build as well as generated as part of the build for inclusion in bundles and installers. + - [Dependencies/*.cs](xaprepare/ConfigAndData/Dependencies) + Files in this directory contain dependencies (program and package names + versions) for all the supported operating systems. + +## Running + +### Scenarios + +The utility employs the abstraction of "scenarios" which are collections of various steps to perform in order to achieve a +specific goal. The main scenario is named `Standard` and is the default one if no other scenario is named on the command line +(by using the `-s NAME` parameter). You can list all the scenarios by issuing the following command from the root of `Xamarin.Android` +source tree: + +``` +make PREPARE_ARGS=--ls +``` + +### Invocation + +In order to run the preparation utility on your development machine, all you need to do is to invoke the following command from +the root of the `Xamarin.Android` source tree: + +``` +make prepare +``` + +To get list of all command line parameters accepted by the utility, issue the following command: + +``` +make prepare-help +``` + +You can append the following parameters to the command line: + + - `PREPARE_ARGS=""` + All and any command line parameters accepted by the utility + - `PREPARE_SCENARIO=""` + Name of the "scenario" to run + - `PREPARE_AUTOPROVISION=0|1` + If set to `0` (the default), the utility will take notice of missing/outdated software the build depends on and exit with an error + should any such condition is detected. Setting the property to `1` will let the utility install the software (installation **may** + use `sudo` on Unix so you will need administrator/root credentials for it to work) + - `PREPARE_IGNORE_MONO_VERSION=0|1` + If set to `1` (the default), the utility will not enforce Mono version but rather will use any Mono version you have installed. + - `V=1` + Causes the run to output much more information (making output much more messy in the process) to the console. Normally this additional + information is placed only in the log files generated by the utility. + +### Log files + +Log files are generated in the `bin/Build*/` directory (where the part indicated by asterisk here is either `Debug` or `Release` depending +on the chosen configuration) in files named using the following format: `prepare-TIMESTAMP.TAGS.log` where the components in capital letters +have the following format: + + - `TIMESTAMP` + yyyymmddThhmmss (``yyyy`` - year, `mm` - month, `dd` - day, `hh` - hour, `mm` - minute, `ss` - second) + - `TAGS` + Arbitrary set of strings as set by various steps/tasks, usually self-explanatory diff --git a/build-tools/xaprepare/xaprepare.sln b/build-tools/xaprepare/xaprepare.sln new file mode 100644 index 000000000..a13876005 --- /dev/null +++ b/build-tools/xaprepare/xaprepare.sln @@ -0,0 +1,17 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "xaprepare", "xaprepare\xaprepare.csproj", "{965D4281-1668-498C-86C4-264D7A44DAE7}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x86 = Debug|x86 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {965D4281-1668-498C-86C4-264D7A44DAE7}.Debug|x86.ActiveCfg = Debug|x86 + {965D4281-1668-498C-86C4-264D7A44DAE7}.Debug|x86.Build.0 = Debug|x86 + {965D4281-1668-498C-86C4-264D7A44DAE7}.Release|x86.ActiveCfg = Release|x86 + {965D4281-1668-498C-86C4-264D7A44DAE7}.Release|x86.Build.0 = Release|x86 + EndGlobalSection +EndGlobal diff --git a/build-tools/xaprepare/xaprepare/App.config b/build-tools/xaprepare/xaprepare/App.config new file mode 100644 index 000000000..d740e8860 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/build-tools/xaprepare/xaprepare/Application/Abi.Bitness.cs b/build-tools/xaprepare/xaprepare/Application/Abi.Bitness.cs new file mode 100644 index 000000000..3e4f837b7 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/Abi.Bitness.cs @@ -0,0 +1,12 @@ +namespace Xamarin.Android.Prepare +{ + partial class Abi + { + public enum Bitness + { + Any, + ThirtyTwo, + SixtyFour, + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/Abi.OS.cs b/build-tools/xaprepare/xaprepare/Application/Abi.OS.cs new file mode 100644 index 000000000..54682c89e --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/Abi.OS.cs @@ -0,0 +1,12 @@ +namespace Xamarin.Android.Prepare +{ + partial class Abi + { + public enum OS + { + Any, + Windows, + NotWindows + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/Abi.cs b/build-tools/xaprepare/xaprepare/Application/Abi.cs new file mode 100644 index 000000000..a96b3b459 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/Abi.cs @@ -0,0 +1,119 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Xamarin.Android.Prepare +{ + partial class Abi + { + static readonly List KnownAbis = new List { + new Abi { Name = AbiNames.TargetJit.AndroidArmV7a, Type = AbiType.TargetJit, Is64Bit = false, IsCross = false, IsHost = false, IsLlvm = false, IsWindows = false }, + new Abi { Name = AbiNames.TargetJit.AndroidArmV8a, Type = AbiType.TargetJit, Is64Bit = true, IsCross = false, IsHost = false, IsLlvm = false, IsWindows = false }, + new Abi { Name = AbiNames.TargetJit.AndroidX86, Type = AbiType.TargetJit, Is64Bit = false, IsCross = false, IsHost = false, IsLlvm = false, IsWindows = false }, + new Abi { Name = AbiNames.TargetJit.AndroidX86_64, Type = AbiType.TargetJit, Is64Bit = true, IsCross = false, IsHost = false, IsLlvm = false, IsWindows = false }, + + new Abi { Name = AbiNames.TargetAot.ArmV7a, Type = AbiType.TargetAot, Is64Bit = false, IsCross = false, IsHost = false, IsLlvm = false, IsWindows = false }, + new Abi { Name = AbiNames.TargetAot.WinArmV7a, Type = AbiType.TargetAot, Is64Bit = false, IsCross = false, IsHost = false, IsLlvm = false, IsWindows = true }, + new Abi { Name = AbiNames.TargetAot.ArmV8a, Type = AbiType.TargetAot, Is64Bit = true, IsCross = false, IsHost = false, IsLlvm = false, IsWindows = false }, + new Abi { Name = AbiNames.TargetAot.WinArmV8a, Type = AbiType.TargetAot, Is64Bit = true, IsCross = false, IsHost = false, IsLlvm = false, IsWindows = true }, + new Abi { Name = AbiNames.TargetAot.X86, Type = AbiType.TargetAot, Is64Bit = false, IsCross = false, IsHost = false, IsLlvm = false, IsWindows = false }, + new Abi { Name = AbiNames.TargetAot.WinX86, Type = AbiType.TargetAot, Is64Bit = false, IsCross = false, IsHost = false, IsLlvm = false, IsWindows = true }, + new Abi { Name = AbiNames.TargetAot.X86_64, Type = AbiType.TargetAot, Is64Bit = true, IsCross = false, IsHost = false, IsLlvm = false, IsWindows = false }, + new Abi { Name = AbiNames.TargetAot.WinX86_64, Type = AbiType.TargetAot, Is64Bit = true, IsCross = false, IsHost = false, IsLlvm = false, IsWindows = true }, + + new Abi { Name = AbiNames.HostJit.Linux, Type = AbiType.HostJit, Is64Bit = false, IsCross = false, IsHost = true, IsLlvm = false, IsWindows = false }, + new Abi { Name = AbiNames.HostJit.Darwin, Type = AbiType.HostJit, Is64Bit = false, IsCross = false, IsHost = true, IsLlvm = false, IsWindows = false }, + new Abi { Name = AbiNames.HostJit.Win32, Type = AbiType.HostJit, Is64Bit = false, IsCross = false, IsHost = true, IsLlvm = false, IsWindows = true }, + new Abi { Name = AbiNames.HostJit.Win64, Type = AbiType.HostJit, Is64Bit = true, IsCross = false, IsHost = true, IsLlvm = false, IsWindows = true }, + + new Abi { Name = AbiNames.CrossAot.ArmV7a, Type = AbiType.CrossAot, Is64Bit = false, IsCross = true, IsHost = false, IsLlvm = false, IsWindows = false }, + new Abi { Name = AbiNames.CrossAot.WinArmV7a, Type = AbiType.CrossAot, Is64Bit = false, IsCross = true, IsHost = false, IsLlvm = false, IsWindows = true }, + new Abi { Name = AbiNames.CrossAot.ArmV8a, Type = AbiType.CrossAot, Is64Bit = true, IsCross = true, IsHost = false, IsLlvm = false, IsWindows = false }, + new Abi { Name = AbiNames.CrossAot.WinArmV8a, Type = AbiType.CrossAot, Is64Bit = true, IsCross = true, IsHost = false, IsLlvm = false, IsWindows = true }, + new Abi { Name = AbiNames.CrossAot.X86, Type = AbiType.CrossAot, Is64Bit = false, IsCross = true, IsHost = false, IsLlvm = false, IsWindows = false }, + new Abi { Name = AbiNames.CrossAot.WinX86, Type = AbiType.CrossAot, Is64Bit = false, IsCross = true, IsHost = false, IsLlvm = false, IsWindows = true }, + new Abi { Name = AbiNames.CrossAot.X86_64, Type = AbiType.CrossAot, Is64Bit = true, IsCross = true, IsHost = false, IsLlvm = false, IsWindows = false }, + new Abi { Name = AbiNames.CrossAot.WinX86_64, Type = AbiType.CrossAot, Is64Bit = true, IsCross = true, IsHost = false, IsLlvm = false, IsWindows = true }, + + new Abi { Name = AbiNames.Llvm.Host32Bit, Type = AbiType.Llvm, Is64Bit = false, IsCross = false, IsHost = false, IsLlvm = true, IsWindows = false }, + new Abi { Name = AbiNames.Llvm.Host64Bit, Type = AbiType.Llvm, Is64Bit = true, IsCross = false, IsHost = false, IsLlvm = true, IsWindows = false }, + new Abi { Name = AbiNames.Llvm.Windows32Bit, Type = AbiType.Llvm, Is64Bit = false, IsCross = false, IsHost = false, IsLlvm = true, IsWindows = true }, + new Abi { Name = AbiNames.Llvm.Windows64Bit, Type = AbiType.Llvm, Is64Bit = true, IsCross = false, IsHost = false, IsLlvm = true, IsWindows = true }, + }; + + public bool Is64Bit { get; private set; } + public bool IsCross { get; private set; } + public bool IsHost { get; private set; } + public bool IsLlvm { get; private set; } + public bool IsWindows { get; private set; } + public string Name { get; private set; } + public AbiType Type { get; private set; } + + public static HashSet GetHostAbis (OS osType = OS.Any, Bitness bitness = Bitness.Any, bool includeAllHostOSes = true) + { + IEnumerable abis = KnownAbis.Where + (a => + a.Type == AbiType.HostJit && + OSMatches(a, osType) && + BitnessMatches (a, bitness) && + (includeAllHostOSes || IsCurrentHostOS (a))).Select (a => a.Name); + + return MakeHashSet (abis); + } + + public static HashSet GetLlvmAbis (OS osType = OS.Any, Bitness bitness = Bitness.Any) + { + return MakeHashSet (KnownAbis.Where (a => a.Type == AbiType.Llvm && OSMatches (a, osType) && BitnessMatches (a, bitness)).Select (a => a.Name)); + } + + public static HashSet GetTargetJitAbis (Bitness bitness = Bitness.Any) + { + return MakeHashSet (KnownAbis.Where (a => a.Type == AbiType.TargetJit && BitnessMatches (a, bitness)).Select (a => a.Name)); + } + + public static HashSet GetHostAotAbis (OS osType = OS.Any, Bitness bitness = Bitness.Any) + { + return MakeHashSet (KnownAbis.Where (a => a.Type == AbiType.TargetAot && OSMatches (a, osType) && BitnessMatches (a, bitness)).Select (a => a.Name)); + } + + public static HashSet GetCrossAbis (OS osType = OS.Any, Bitness bitness = Bitness.Any) + { + return MakeHashSet (KnownAbis.Where (a => a.Type == AbiType.CrossAot && OSMatches (a, osType) && BitnessMatches (a, bitness)).Select (a => a.Name)); + } + + static bool IsCurrentHostOS (Abi abi) + { + if (abi.IsWindows) + return true; // Cross-build ABIs should be included in this case + + return String.Compare (abi.Name, Context.Instance.OS.Type, StringComparison.Ordinal) == 0; + } + + static bool OSMatches (Abi abi, OS osType) + { + if (osType == OS.Any) + return true; + + if (osType == OS.Windows) + return abi.IsWindows; + + return !abi.IsWindows; + } + + static bool BitnessMatches (Abi abi, Bitness bitness) + { + if (bitness == Bitness.Any) + return true; + + if (bitness == Bitness.ThirtyTwo) + return !abi.Is64Bit; + + return abi.Is64Bit; + } + + static HashSet MakeHashSet (IEnumerable entries) + { + return new HashSet (entries, StringComparer.Ordinal); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/AbiType.cs b/build-tools/xaprepare/xaprepare/Application/AbiType.cs new file mode 100644 index 000000000..ed07d082f --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/AbiType.cs @@ -0,0 +1,11 @@ +namespace Xamarin.Android.Prepare +{ + enum AbiType + { + CrossAot, + HostJit, + Llvm, + TargetJit, + TargetAot, + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/AndroidPlatform.cs b/build-tools/xaprepare/xaprepare/Application/AndroidPlatform.cs new file mode 100644 index 000000000..e7b415f3a --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/AndroidPlatform.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Xamarin.Android.Prepare +{ + class AndroidPlatform + { + public uint ApiLevel { get; } + public string PlatformID { get; } + public string Framework { get; } + public bool Stable { get; } + public bool Supported { get; } + + public AndroidPlatform (uint apiLevel, string platformID, string framework = null, bool stable = true) + { + if (String.IsNullOrEmpty (platformID)) + throw new ArgumentException ("must not be null or empty", nameof (platformID)); + + ApiLevel = apiLevel; + PlatformID = platformID; + Framework = framework ?? String.Empty; + Stable = stable; + Supported = !String.IsNullOrEmpty (framework); + } + } + + static class AndroidPlatformExtensions + { + public static void Add (this List list, uint apiLevel, string platformID, string framework, bool stable) + { + if (list == null) + throw new ArgumentNullException (nameof (list)); + + if (list.Any (p => p.ApiLevel == apiLevel)) + throw new InvalidOperationException ($"Duplicate Android platform, API level {apiLevel}"); + + list.Add (new AndroidPlatform (apiLevel, platformID, framework, stable)); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/AndroidToolchainComponent.cs b/build-tools/xaprepare/xaprepare/Application/AndroidToolchainComponent.cs new file mode 100644 index 000000000..276c5d3e4 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/AndroidToolchainComponent.cs @@ -0,0 +1,37 @@ +using System; +using System.IO; + +namespace Xamarin.Android.Prepare +{ + class AndroidToolchainComponent : AppObject + { + public string Name { get; } + public string DestDir { get; } + public Uri RelativeUrl { get; } + public bool IsMultiVersion { get; } + public bool NoSubdirectory { get; } + public string ExpectedPkgRevision { get; } + + public AndroidToolchainComponent (string name, string destDir, Uri relativeUrl = null, bool isMultiVersion = false, bool noSubdirectory = false, string expectedPkgRevision = null) + { + if (String.IsNullOrEmpty (name)) + throw new ArgumentException ("must not be null or empty", nameof (name)); + if (String.IsNullOrEmpty (destDir)) + throw new ArgumentException ("must not be null or empty", nameof (destDir)); + + Name = name; + DestDir = destDir; + RelativeUrl = relativeUrl; + IsMultiVersion = isMultiVersion; + NoSubdirectory = noSubdirectory; + ExpectedPkgRevision = expectedPkgRevision; + } + } + + class AndroidPlatformComponent : AndroidToolchainComponent + { + public AndroidPlatformComponent (string name, string apiLevel) + : base (name, Path.Combine ("platforms", $"android-{apiLevel}")) + {} + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/AppObject.cs b/build-tools/xaprepare/xaprepare/Application/AppObject.cs new file mode 100644 index 000000000..07330fe24 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/AppObject.cs @@ -0,0 +1,17 @@ +namespace Xamarin.Android.Prepare +{ + class AppObject + { + Log log; + + public Log Log { + get => log ?? Log.Instance; + protected set => log = value; + } + + protected AppObject (Log log = null) + { + this.log = log; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/BclFile.cs b/build-tools/xaprepare/xaprepare/Application/BclFile.cs new file mode 100644 index 000000000..88a26cc83 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/BclFile.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Xamarin.Android.Prepare +{ + sealed class BclFile + { + readonly Dictionary bclProfileSourceDirs = new Dictionary () { + { BclFileTarget.Android, Configurables.Paths.BCLAssembliesSourceDir }, + { BclFileTarget.DesignerHost, Configurables.Paths.BCLHostAssembliesSourceDir }, + { BclFileTarget.DesignerWindows, Configurables.Paths.BCLWindowsAssembliesSourceDir }, + }; + + readonly Dictionary bclFacadeSourceDirs = new Dictionary () { + { BclFileTarget.Android, Configurables.Paths.BCLFacadeAssembliesSourceDir }, + { BclFileTarget.DesignerHost, Configurables.Paths.BCLHostFacadeAssembliesSourceDir }, + { BclFileTarget.DesignerWindows, Configurables.Paths.BCLWindowsFacadeAssembliesSourceDir }, + }; + + public string Name { get; } + public BclFileType Type { get; } + public BclFileTarget Target { get; } + public bool ExcludeDebugSymbols { get; } + public string SourcePath { get; } + public string Version { get; } + public string DebugSymbolsPath { + get { + if (ExcludeDebugSymbols) + return null; + + return Utilities.GetDebugSymbolsPath (SourcePath); + } + } + + public BclFile (string name, BclFileType type, bool excludeDebugSymbols = false, string version = null, BclFileTarget target = BclFileTarget.Android) + { + name = name?.Trim (); + if (String.IsNullOrEmpty (name)) + throw new ArgumentException ("must not be null or empty", nameof (name)); + + Name = name; + Type = type; + ExcludeDebugSymbols = excludeDebugSymbols; + Version = version; + Target = target; + + string sourceDir; + switch (type) { + case BclFileType.ProfileAssembly: + sourceDir = bclProfileSourceDirs [target]; + break; + + case BclFileType.FacadeAssembly: + sourceDir = bclFacadeSourceDirs [target]; + break; + + default: + throw new InvalidOperationException ($"Unsupported BCL file type {Type} for file {Name}"); + } + + SourcePath = Path.GetFullPath (Path.Combine (sourceDir, name)); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/BclFileTarget.cs b/build-tools/xaprepare/xaprepare/Application/BclFileTarget.cs new file mode 100644 index 000000000..9e3505512 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/BclFileTarget.cs @@ -0,0 +1,23 @@ +namespace Xamarin.Android.Prepare +{ + /// + /// Installation target of the BCL file. + /// + enum BclFileTarget + { + /// + /// Install for Android + /// + Android, + + /// + /// Install for Android Designer on the current host operating system + /// + DesignerHost, + + /// + /// Install for Android Designer on Windows + /// + DesignerWindows, + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/BclFileType.cs b/build-tools/xaprepare/xaprepare/Application/BclFileType.cs new file mode 100644 index 000000000..c6816e12b --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/BclFileType.cs @@ -0,0 +1,8 @@ +namespace Xamarin.Android.Prepare +{ + enum BclFileType + { + FacadeAssembly, + ProfileAssembly, + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/BuildInfo.cs b/build-tools/xaprepare/xaprepare/Application/BuildInfo.cs new file mode 100644 index 000000000..f4e8b9f9f --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/BuildInfo.cs @@ -0,0 +1,262 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + partial class BuildInfo : AppObject + { + static readonly char[] NDKPropertySeparator = new [] { '=' }; + static readonly char[] NDKPlatformDirectorySeparator = new [] { '-' }; + + public string CommitOfLastVersionChange { get; private set; } + + // Not available from the start, only after NDK is installed + public string NDKRevision { get; private set; } = String.Empty; + public string NDKVersionMajor { get; private set; } = String.Empty; + public string NDKVersionMinor { get; private set; } = String.Empty; + public string NDKVersionMicro { get; private set; } = String.Empty; + public string NDKMinimumApiAvailable { get; private set; } = String.Empty; + + public string VersionHash { get; private set; } = String.Empty; + public string LibZipHash { get; private set; } = String.Empty; + public string FullLibZipHash { get; private set; } = String.Empty; + public string MonoHash { get; private set; } = String.Empty; + public string FullMonoHash { get; private set; } = String.Empty; + + public async Task GatherGitInfo (Context context) + { + if (context == null) + throw new ArgumentNullException (nameof (context)); + + Log.StatusLine (); + Log.StatusLine ("Determining basic build information", ConsoleColor.DarkGreen); + await DetermineLastVersionChangeCommit (context); + Log.StatusLine (); + DetermineBundleHashes (context); + Log.StatusLine (); + } + + public bool GatherNDKInfo (Context context, string ndkRoot) + { + string props = Path.Combine (ndkRoot, "source.properties"); + if (!File.Exists (props)) { + Log.ErrorLine ("NDK properties file does not exist: ", props, tailColor: Log.DestinationColor); + return false; + } + + string[] lines = File.ReadAllLines (props); + foreach (string l in lines) { + string line = l.Trim (); + string[] parts = line.Split (NDKPropertySeparator, 2); + if (parts.Length != 2) + continue; + + if (String.Compare ("Pkg.Revision", parts [0].Trim (), StringComparison.Ordinal) != 0) + continue; + + string rev = parts [1].Trim (); + NDKRevision = rev; + + Version ver; + if (!Version.TryParse (rev, out ver)) { + Log.ErrorLine ($"Unable to parse NDK revision '{rev}' as a valid version string"); + return false; + } + + NDKVersionMajor = ver.Major.ToString (); + NDKVersionMinor = ver.Minor.ToString (); + NDKVersionMicro = ver.Build.ToString (); + break; + } + + int minimumApi = Int32.MaxValue; + string platforms = Path.Combine (ndkRoot, "platforms"); + foreach (string p in Directory.EnumerateDirectories (platforms, "android-*", SearchOption.TopDirectoryOnly)) { + string pdir = Path.GetFileName (p); + string[] parts = pdir.Split (NDKPlatformDirectorySeparator, 2); + if (parts.Length != 2) + continue; + + int api; + if (!Int32.TryParse (parts [1].Trim (), out api)) + continue; + + if (api >= minimumApi) + continue; + + minimumApi = api; + } + + NDKMinimumApiAvailable = minimumApi.ToString (); + return true; + } + + void DetermineBundleHashes (Context context) + { + GitRunner git = CreateGitRunner (context); + + Log.StatusLine ($" {context.Characters.Bullet} LibZip commit hash", ConsoleColor.Gray); + FullLibZipHash = git.GetTopCommitHash (context.Properties.GetRequiredValue (KnownProperties.LibZipSourceFullPath), shortHash: false); + LibZipHash = EnsureHash ("LibZip", Utilities.ShortenGitHash (FullLibZipHash)); + + Log.StatusLine ($" {context.Characters.Bullet} Mono commit hash", ConsoleColor.Gray); + FullMonoHash = git.GetTopCommitHash (context.Properties.GetRequiredValue (KnownProperties.MonoSourceFullPath), shortHash: false); + MonoHash = EnsureHash ("Mono", Utilities.ShortenGitHash (FullMonoHash)); + + if (Configurables.Paths.BundleVersionHashFiles == null || Configurables.Paths.BundleVersionHashFiles.Count == 0) { + Log.WarningLine ("Bundle version hash files not specified"); + return; + } + + Log.StatusLine ($" {context.Characters.Bullet} Generating bundle version hash", ConsoleColor.Gray); + using (var ha = HashAlgorithm.Create (context.HashAlgorithm)) { + HashFiles (ha, Configurables.Paths.BundleVersionHashFiles); + VersionHash = FormatHash (ha.Hash).Substring (0, (int)Configurables.Defaults.AbbreviatedHashLength); + Log.StatusLine (" Hash: ", VersionHash, tailColor: ConsoleColor.Cyan); + } + + string EnsureHash (string name, string hash) + { + if (String.IsNullOrEmpty (hash)) + throw new InvalidOperationException ($"Unable to determine {name} commit hash"); + Log.StatusLine (" Commit: ", hash, tailColor: ConsoleColor.Cyan); + Log.StatusLine (); + + return hash; + } + } + + void HashFiles (HashAlgorithm ha, List globPatterns) + { + var block = new byte [4096]; + foreach (string glob in globPatterns) { + string pattern = glob?.Trim (); + if (String.IsNullOrEmpty (pattern)) + continue; + + foreach (string file in Directory.EnumerateFiles (Path.GetDirectoryName (pattern), Path.GetFileName (pattern))) { + Log.StatusLine (" file: ", Utilities.GetRelativePath (BuildPaths.XamarinAndroidSourceRoot, file), tailColor: ConsoleColor.Cyan); + HashFile (ha, file, block); + } + } + ha.TransformFinalBlock (block, 0, 0); + } + + void HashFile (HashAlgorithm ha, string filePath, byte[] block) + { + using (var memoryStream = new MemoryStream ()) { + //Read the file into a MemoryStream, ignoring newlines + using (var file = File.OpenRead (filePath)) { + int readByte; + while ((readByte = file.ReadByte ()) != -1) { + byte b = (byte)readByte; + if (b != '\r' && b != '\n') { + memoryStream.WriteByte (b); + } + } + } + memoryStream.Seek (0, SeekOrigin.Begin); + + int read; + while ((read = memoryStream.Read (block, 0, block.Length)) > 0) { + ha.TransformBlock (block, 0, read, block, 0); + } + } + } + + string FormatHash (byte[] hash) + { + return string.Join (String.Empty, hash.Select (b => b.ToString ("x2"))); + } + + async Task DetermineLastVersionChangeCommit (Context context) + { + Log.StatusLine ($" {context.Characters.Bullet} Commit of last version change", ConsoleColor.Gray); + GitRunner git = CreateGitRunner (context); + IList blameEntries; + + blameEntries = await git.Blame ("Configuration.props"); + if (blameEntries == null || blameEntries.Count == 0) + throw new InvalidOperationException ("Unable to determine the last version change commit"); + + foreach (GitRunner.BlamePorcelainEntry be in blameEntries) { + if (be == null || String.IsNullOrEmpty (be.Line)) + continue; + + if (be.Line.IndexOf ("") >= 0) { + Log.DebugLine ($"Last version change on {GetCommitDate (be)} by {be.Author}"); + CommitOfLastVersionChange = be.Commit; + Log.StatusLine (" Commit: ", be.Commit, tailColor: ConsoleColor.Cyan); + break; + } + } + } + + string GetCommitDate (GitRunner.BlamePorcelainEntry be) + { + int tzOffset = GetTZOffset (be.CommitterTZ, be); + int committerTimeUTC = (int)be.CommitterTime + tzOffset; + var unixEpoch = new DateTime (1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + DateTime dt = unixEpoch.AddSeconds (committerTimeUTC); + + return dt.ToString ("R", DateTimeFormatInfo.InvariantInfo); + } + + int GetTZOffset (string tz, GitRunner.BlamePorcelainEntry be) + { + if (String.IsNullOrEmpty (tz)) { + Log.DebugLine ($"No timezone information from `git blame` for commit {be.Commit}"); + return 0; + } + + if (tz.Length != 5) { + LogUnexpectedFormat (); + return 0; + } + + int tzSign; + switch (tz [0]) { + case '-': + tzSign = 1; + break; + + case '+': + tzSign = -1; + break; + + default: + LogUnexpectedFormat (); + return 0; + } + + if (!Int32.TryParse (tz.Substring (1, 2), out int hours)) { + LogUnexpectedFormat (); + return 0; + } + + if (!Int32.TryParse (tz.Substring (3, 2), out int minutes)) { + LogUnexpectedFormat (); + return 0; + } + + return tzSign * ((hours * 3600) + (minutes * 60)); + + void LogUnexpectedFormat () + { + Log.DebugLine ($"Unexpected timezone format from `git blame` for commit {be.Commit}: {tz}"); + } + } + + GitRunner CreateGitRunner (Context context) + { + return new GitRunner (context, Log) { + LogMessageIndent = " Running: " + }; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/BundleItem.cs b/build-tools/xaprepare/xaprepare/Application/BundleItem.cs new file mode 100644 index 000000000..aeacd7712 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/BundleItem.cs @@ -0,0 +1,32 @@ +using System; + +namespace Xamarin.Android.Prepare +{ + class BundleItem + { + /// + /// Optional path of the item in the destination archive + /// + public string ArchivePath { get; } + + /// + /// An optional functor to determine whether or not to include the item in the archive. + /// + public Func ShouldInclude { get; } + + /// + /// Required source path of the item. + /// + public string SourcePath { get; } + + public BundleItem (string sourcePath, string archivePath = null, Func shouldInclude = null) + { + if (String.IsNullOrEmpty (sourcePath)) + throw new ArgumentNullException (nameof (sourcePath)); + + ArchivePath = archivePath; + ShouldInclude = shouldInclude; + SourcePath = sourcePath; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/Characters.cs b/build-tools/xaprepare/xaprepare/Application/Characters.cs new file mode 100644 index 000000000..2e3a0c335 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/Characters.cs @@ -0,0 +1,61 @@ +using System; + +namespace Xamarin.Android.Prepare +{ + abstract class Characters + { + public abstract string Bullet { get; } + public abstract string Link { get; } + public abstract string Package { get; } + public abstract string LeftArrow { get; } + public abstract string RightArrow { get; } + public abstract string[] Twiddler { get; } + + protected Characters () + {} + + public static Characters Create (Context context) + { + if (context == null) + throw new ArgumentNullException (nameof (context)); + + if (!context.NoEmoji && !context.DullMode && context.CanConsoleUseUnicode) + return new UnicodeChars (); + + return new PlainChars (); + } + } + + sealed class PlainChars : Characters + { + static readonly string[] twiddler = new [] {"-", "\\", "|", "/", "-", "\\", "|", "/"}; + + public override string Bullet => "*"; + public override string Link => "->"; + public override string Package => "#"; + public override string LeftArrow => "<-"; + public override string RightArrow => "->"; + public override string[] Twiddler => twiddler; + } + + sealed class UnicodeChars : Characters + { + readonly string[] twiddler; + + public override string Bullet => "•"; + public override string Link => Char.ConvertFromUtf32 (0x1f517); // 🔗 + public override string Package => Char.ConvertFromUtf32 (0x1f4e6); // 📦 + public override string LeftArrow => "←"; + public override string RightArrow => "→"; + public override string[] Twiddler => twiddler; + + public UnicodeChars () + { + twiddler = new string [12]; + + for (int i = 0 ; i < 12; i++) { + twiddler [i] = Char.ConvertFromUtf32 (0x1F550 + i); // 🕐🕑🕒🕓🕔🕕🕖🕗🕘🕙🕚🕛 + } + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/CompressionFormat.cs b/build-tools/xaprepare/xaprepare/Application/CompressionFormat.cs new file mode 100644 index 000000000..d826ace99 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/CompressionFormat.cs @@ -0,0 +1,27 @@ +using System; + +namespace Xamarin.Android.Prepare +{ + class CompressionFormat + { + public string Description { get; } + public string Extension { get; } + public string Name { get; } + + public CompressionFormat (string name, string description, string extension) + { + if (String.IsNullOrEmpty (name)) + throw new ArgumentNullException (nameof (name)); + + if (String.IsNullOrEmpty (description)) + throw new ArgumentNullException (nameof (description)); + + if (String.IsNullOrEmpty (extension)) + throw new ArgumentNullException (nameof (extension)); + + Description = description; + Extension = extension; + Name = name; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/Context.Linux.cs b/build-tools/xaprepare/xaprepare/Application/Context.Linux.cs new file mode 100644 index 000000000..d0ff2cbaa --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/Context.Linux.cs @@ -0,0 +1,10 @@ +namespace Xamarin.Android.Prepare +{ + partial class Context + { + void InitOS () + { + OS = Linux.DetectAndCreate (this); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/Context.MacOS.cs b/build-tools/xaprepare/xaprepare/Application/Context.MacOS.cs new file mode 100644 index 000000000..ddc5de9ac --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/Context.MacOS.cs @@ -0,0 +1,10 @@ +namespace Xamarin.Android.Prepare +{ + partial class Context + { + void InitOS () + { + OS = new MacOS (this); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/Context.Windows.cs b/build-tools/xaprepare/xaprepare/Application/Context.Windows.cs new file mode 100644 index 000000000..d936d0db3 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/Context.Windows.cs @@ -0,0 +1,16 @@ +namespace Xamarin.Android.Prepare +{ + partial class Context + { + void InitOS () + { + OS = new Windows (Context.Instance); + + // Windows console (cmd.exe) is rather... antiquated and doesn't behave well with our output so turn off all + // the nice stuff until Windows Terminal is in and we can enjoy the full console goodness. + UseColor = false; + NoEmoji = true; + DullMode = true; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/Context.cs b/build-tools/xaprepare/xaprepare/Application/Context.cs new file mode 100644 index 000000000..332e28a58 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/Context.cs @@ -0,0 +1,845 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + /// + /// Shared application contet. + /// + partial class Context : AppObject + { + const ConsoleColor BannerColor = ConsoleColor.DarkGreen; + + public const ConsoleColor SuccessColor = ConsoleColor.Green; + public const ConsoleColor FailureColor = ConsoleColor.Red; + public const ConsoleColor WarningColor = ConsoleColor.Yellow; + + static readonly string XASolutionFilePath = Path.Combine (BuildPaths.XamarinAndroidSourceRoot, "Xamarin.Android.sln"); + static readonly string XATestsSolutionFilePath = Path.Combine (BuildPaths.XamarinAndroidSourceRoot, "Xamarin.Android-Tests.sln"); + + string logDirectory; + string mainLogFilePath; + string xaInstallPrefix; + string overridenLogDirectory; + string configuration; + string productVersion; + string androidLatestStableFrameworkVersion; + string hashAlgorithm; + bool canOutputColor; + bool canConsoleUseUnicode; + Characters characters; + bool? useColor; + bool? dullMode; + Scenario defaultScenario; + HashSet hostJitAbis; + HashSet targetAotAbis; + HashSet targetJitAbis; + List ruleGenerators; + string debugFileExtension; + CompressionFormat compressionFormat; + Dictionary conditions = new Dictionary (); + + /// + /// Access the only instance of the Context class + /// + public static Context Instance { get; } + + /// + /// Information about the operating system we're currently running on. See + /// + public OS OS { get; private set; } + + /// + /// A shortcut to access a small set of essential tools used by the bootstrapper. See + /// + public EssentialTools Tools { get; private set; } + + /// + /// Information about the current build. + /// + public BuildInfo BuildInfo { get; private set; } + + /// + /// All the scenarios known to the bootstrapper + /// + public IDictionary Scenarios { get; } = new SortedDictionary (StringComparer.OrdinalIgnoreCase); + + /// + /// Default scenario to execute if none was specified by the user on the command line + /// + public Scenario DefaultScenario => defaultScenario; + + /// + /// Scenario selected for the current session + /// + public Scenario SelectedScenario { get; private set; } + + /// + /// Whether the current run of the bootstrapper can interact with the user or not + /// + public bool InteractiveSession { get; } + + /// + /// Set of properties available in this instance of the bootstrapper. See and + /// + public Properties Properties { get; } = new Properties (); + + /// + /// Time stamp of the current build + /// + public string BuildTimeStamp { get; } + + /// + /// A collection of all the methods to obtain version numbers from programs. See + /// + public VersionFetchers VersionFetchers { get; private set; } + + + /// + /// Logging verbosity/level of the current session. + /// + public LoggingVerbosity LoggingVerbosity { get; set; } = Configurables.Defaults.LoggingVerbosity; + + /// + /// How many make/ninja jobs to run when building software + /// + public uint MakeConcurrency { get; set; } = Configurables.Defaults.MakeConcurrency; + + /// + /// Do not use emoji characters + /// + public bool NoEmoji { get; set; } = !Configurables.Defaults.UseEmoji; + + /// + /// Force a rebuild of the Mono runtimes + /// + public bool ForceRuntimesBuild { get; set; } + + /// + /// Automatically provision all the missing programs + /// + public bool AutoProvision { get; set; } + + /// + /// If a program being provisioned automatically requires administrative rights to install, use sudo + /// + public bool AutoProvisionUsesSudo { get; set; } + + /// + /// Do not terminate session when Mono is newer than specified in the dependencies + /// + public bool IgnoreMaxMonoVersion { get; set; } = true; + + /// + /// Current session execution mode. See + /// + public ExecutionMode ExecutionMode { get; set; } = Configurables.Defaults.ExecutionMode; + + /// + /// Set of Mono command line options to be placed in the `MONO_OPTIOS` environment variable + /// + public List MonoOptions { get; set; } + + /// + /// Enable all supported targets, runtimes etc. Takes effect only if set before is called + /// + public bool EnableAllTargets { get; set; } + + /// + /// Path to the current session's main log file + /// + public string MainLogFilePath => mainLogFilePath; + + /// + /// Path to the Xamarin.Android solution file + /// + public string XASolutionFile => XASolutionFilePath; + + /// + /// Path to the Xamarin.Android tests solution file + /// + public string XATestsSolutionFile => XATestsSolutionFilePath; + + /// + /// If true, the current console is capable of displayig UTF-8 characters + /// + public bool CanConsoleUseUnicode => canConsoleUseUnicode; + + /// + /// A set of various special characters used in progress messages. See + /// + public Characters Characters => characters; + + /// + /// Xamarin.Android version + /// + public string ProductVersion => productVersion; + + /// + /// If true make messages logged to console not use colors, do not use "fancy" progress indicators etc + /// + public bool DullMode { + get => dullMode.HasValue ? dullMode.Value : ExecutionMode == ExecutionMode.CI; + set => dullMode = value; + } + + /// + /// Compression format to use for the archives we create + /// + public CompressionFormat CompressionFormat { + get => compressionFormat ?? Configurables.Defaults.DefaultCompressionFormat; + set => compressionFormat = value; + } + + /// + /// Current session buuld configuration + /// + public string Configuration { + get => configuration ?? Properties.GetRequiredValue (KnownProperties.Configuration); + set { + if (String.IsNullOrEmpty (value)) + throw new ArgumentException ("must not be null or empty", nameof (value)); + if (!String.IsNullOrEmpty (configuration)) + throw new InvalidOperationException ("Configuration can be set only once"); + + logDirectory = null; + configuration = value; + } + } + + /// + /// Whether or not current build is a debug one. + /// + public bool IsDebugBuild => String.Compare (Configuration, "Debug", StringComparison.OrdinalIgnoreCase) == 0; + + /// + /// Hash algorithm to use when calculating various hashes. + /// + public string HashAlgorithm { + get => String.IsNullOrEmpty (hashAlgorithm) ? Configurables.Defaults.HashAlgorithm : HashAlgorithm; + set { + value = value?.Trim (); + if (String.IsNullOrEmpty (value)) + throw new ArgumentException ("must not be null or empty", "value"); + hashAlgorithm = value; + } + } + + /// + /// Directoruy containing all the session logs + /// + public string LogDirectory { + get => GetLogDirectory (); + set { + if (String.IsNullOrEmpty (value)) + throw new ArgumentException ("must not be null or empty", nameof (value)); + + overridenLogDirectory = value; + } + } + + /// + /// Whether or not log messages should use color + /// + public bool UseColor { + get => canOutputColor && (!useColor.HasValue || useColor.Value); + set => useColor = value; + } + + /// + /// Prefix where Xamarin.Android is installed + /// + public string XAInstallPrefix { + get { + if (String.IsNullOrEmpty (xaInstallPrefix)) + xaInstallPrefix = Properties.GetRequiredValue (KnownProperties.XAInstallPrefix); + return xaInstallPrefix; + } + } + + /// + /// true if any Windows ABI targets are enabled + /// + public bool WindowsJitAbisEnabled { + get => IsHostJitAbiEnabled (AbiNames.HostJit.Win32) || IsHostJitAbiEnabled (AbiNames.HostJit.Win64); + } + + /// + /// true if any Android device AOT targets are enabled + /// + public bool TargetAotAbisEnabled { + get => AbiNames.AllTargetAotAbis.Any (abi => IsTargetAotAbiEnabled (abi)); + } + + /// + /// A collection of delegates which can add rules to the `rules.mk` file generated at the end of + /// bootstrapper's run + /// + public List RuleGenerators { + get { + if (ruleGenerators == null) + ruleGenerators = new List (); + return ruleGenerators; + } + } + + /// + /// Extensions of files with debug information + /// + public string DebugFileExtension { + get => debugFileExtension ?? Configurables.Defaults.DebugFileExtension; + set { + value = value?.Trim (); + if (String.IsNullOrEmpty (value)) + throw new ArgumentException ("must not be null or empty", nameof (value)); + if (value [0] != '.') + debugFileExtension = $".{value}"; + else + debugFileExtension = value; + } + } + + /// + /// Full filesystem path to the Xamarin.Android bundle *if* defined on the command line by the user, otherwise + /// null + /// + public string XABundlePath { get; set; } + + static Context () + { + Instance = new Context (); + } + + Context () + { + try { + // This may throw on Windows + Console.CursorVisible = false; + } catch (IOException) { + // Ignore + } + + // Standard Console class offers no way to detect if the terminal can use color, so we use this rather poor + // way to detect it + canOutputColor = true; + try { + ConsoleColor color = Console.ForegroundColor; + } catch (IOException) { + canOutputColor = false; + } + + Properties.PropertiesChanged += PropertiesChanged; + canConsoleUseUnicode = + Console.OutputEncoding is UTF7Encoding || + Console.OutputEncoding is UTF8Encoding || + Console.OutputEncoding is UTF32Encoding || + Console.OutputEncoding is UnicodeEncoding; + + Log.Todo ("better checks for interactive session (isatty?)"); + InteractiveSession = !Console.IsOutputRedirected; + + var now = DateTime.Now; + BuildTimeStamp = $"{now.Year}{now.Month:00}{now.Day:00}T{now.Hour:00}{now.Minute:00}{now.Second:00}"; + mainLogFilePath = GetLogFilePath (null, true); + Log.Instance.SetLogFile (mainLogFilePath); + + productVersion = Properties.GetRequiredValue (KnownProperties.ProductVersion); + androidLatestStableFrameworkVersion = Properties.GetRequiredValue (KnownProperties.AndroidLatestStableFrameworkVersion); + + Log.Instance.DebugLine ("All defined properties:"); + foreach (KeyValuePair prop in Properties) { + Log.Instance.DebugLine ($" {prop.Key} = {prop.Value}"); + } + } + + void PropertiesChanged (object sender, PropertiesChangedEventArgs args) + { + if (String.Compare (KnownProperties.AndroidSupportedTargetJitAbis, args.Name, StringComparison.Ordinal) == 0) { + targetJitAbis = null; + return; + } + + if (String.Compare (KnownProperties.AndroidSupportedHostJitAbis, args.Name, StringComparison.Ordinal) == 0) { + hostJitAbis = null; + return; + } + + if (String.Compare (KnownProperties.AndroidSupportedTargetAotAbis, args.Name, StringComparison.Ordinal) == 0) { + targetAotAbis = null; + } + } + + /// + /// Checks whether names an enabled Android device ABI target. + /// + /// + /// + public bool IsTargetJitAbiEnabled (string abiName) + { + PopulateTargetJitAbis (); + return IsAbiEnabled (abiName, targetJitAbis); + } + + /// + /// Checks whether names an enabled host OS ABI target. + /// + /// + /// + public bool IsHostJitAbiEnabled (string abiName) + { + PopulateHostJitAbis (); + return IsAbiEnabled (abiName, hostJitAbis); + } + + /// + /// Checks whether names an enabled AOT cross-compieler target. + /// + /// + /// + public bool IsTargetAotAbiEnabled (string abiName) + { + PopulateTargetAotAbis (); + return IsAbiEnabled (abiName, targetAotAbis); + } + + /// + /// Checks whether refers to a host OS AOT cross compiler ABI + /// + /// + /// + public bool IsHostAotAbi (string abiName) + { + return AbiNames.AllHostAotAbis.Contains (abiName); + } + + /// + /// Checks whether refers to a Windows AOT cross compiler ABI + /// + /// + /// + public bool IsWindowsAotAbi (string abiName) + { + return AbiNames.AllWindowsAotAbis.Contains (abiName); + } + + /// + /// Checks whether refers to a 64-bit Android JIT ABI target + /// + /// + /// + public bool Is64BitTargetJitAbi (string abiName) + { + return AbiNames.All64BitTargetJitAbis.Contains (abiName); + } + + /// + /// Checks whether refers to a 32-bit Android JIT ABI target + /// + /// + /// + public bool Is32BitTargetJitAbi (string abiName) + { + return AbiNames.All32BitTargetJitAbis.Contains (abiName); + } + + /// + /// Checks whether refers to a 64-bit AOT cross-compiler target + /// + /// + /// + public bool Is64BitTargetAotAbi (string abiName) + { + return AbiNames.All64BitTargetAotAbis.Contains (abiName); + } + + /// + /// Checks whether refers to a 32-bit AOT cross-compiler target + /// + /// + /// + public bool Is32BitTargetAotAbi (string abiName) + { + return AbiNames.All32BitTargetAotAbis.Contains (abiName); + } + + /// + /// Checks whether refers to a host OS Windows cross-compiler target + /// + /// + /// + public bool IsMingwHostAbi (string abiName) + { + return AbiNames.AllMingwHostAbis.Contains (abiName); + } + + /// + /// Checks whether refers to a host OS Windows 32-bit cross-compiler target + /// + /// + /// + public bool Is32BitMingwHostAbi (string abiName) + { + return AbiNames.All32BitMingwHostAbis.Contains (abiName); + } + + /// + /// Checks whether refers to a host OS Windows 64-bit cross-compiler target + /// + /// + /// + public bool Is64BitMingwHostAbi (string abiName) + { + return AbiNames.All64BitMingwHostAbis.Contains (abiName); + } + + /// + /// Checks whether refers to a host OS Windows cross-compiler target + /// + /// + /// + public bool IsNativeHostAbi (string abiName) + { + return AbiNames.AllNativeHostAbis.Contains (abiName); + } + + /// + /// Checks whether refers to a host OS Windows AOT cross-compiler target + /// + /// + /// + public bool IsCrossAotWindowsAbi (string abiName) + { + return AbiNames.AllCrossWindowsAotAbis.Contains (abiName); + } + + /// + /// Checks whether refers to a host OS AOT 64-bit cross-compiler target + /// + /// + /// + public bool Is64BitCrossAbi (string abiName) + { + return AbiNames.All64BitCrossAotAbis.Contains (abiName); + } + + /// + /// Checks whether refers to a host OS Windows 32-bit cross-compiler target + /// + /// + /// + public bool Is32BitCrossAbi (string abiName) + { + return AbiNames.All32BitCrossAotAbis.Contains (abiName); + } + + /// + /// Checks whether refers to a host OS AOT cross-compiler target + /// + /// + /// + public bool IsHostCrossAotAbi (string abiName) + { + return AbiNames.AllCrossHostAotAbis.Contains (abiName); + } + + /// + /// Checks whether refers to a host OS Windows AOT cross-compiler target + /// + /// + /// + public bool IsWindowsCrossAotAbi (string abiName) + { + return AbiNames.AllCrossWindowsAotAbis.Contains (abiName); + } + + /// + /// Checks whether refers to a Windows LLVM target + /// + /// + /// + public bool IsLlvmWindowsAbi (string abiName) + { + return AbiNames.AllLlvmWindowsAbis.Contains (abiName); + } + + /// + /// Checks whether refers to a host OS LLVM target + /// + /// + /// + public bool IsLlvmHostAbi (string abiName) + { + return AbiNames.AllLlvmHostAbis.Contains (abiName); + } + + /// + /// Checks whether refers to a 32-bit LLVM target + /// + /// + /// + public bool Is32BitLlvmAbi (string abiName) + { + return AbiNames.All32BitLlvmAbis.Contains (abiName); + } + + /// + /// Checks whether refers to a 64-bit LLVM target + /// + /// + /// + public bool Is64BitLlvmAbi (string abiName) + { + return AbiNames.All64BitLlvmAbis.Contains (abiName); + } + + bool IsAbiEnabled (string abiName, HashSet collection) + { + if (String.IsNullOrEmpty (abiName)) + throw new ArgumentException ("must not be null or empty", nameof (abiName)); + + return collection.Contains (abiName); + } + + void PopulateTargetJitAbis () + { + if (targetJitAbis != null) + return; + + Utilities.AddAbis (Properties.GetRequiredValue (KnownProperties.AndroidSupportedTargetJitAbis).Trim (), ref targetJitAbis); + } + + void PopulateTargetAotAbis () + { + if (targetAotAbis != null) + return; + + Utilities.AddAbis (Properties.GetRequiredValue (KnownProperties.AndroidSupportedTargetAotAbis).Trim (), ref targetAotAbis); + } + + void PopulateHostJitAbis () + { + if (hostJitAbis != null) { + return; + } + Utilities.AddAbis (Properties.GetRequiredValue (KnownProperties.AndroidSupportedHostJitAbis).Trim (), ref hostJitAbis); + } + + void OnPropertiesChanged (object sender, EventArgs args) + { + hostJitAbis = null; + } + + /// + /// Construct and return path to a log file other than the main log file. The parameter + /// is a string appended to the log name - it MUST consist only of characters valid for file/path names. + /// + public string GetLogFilePath (string tags) + { + return GetLogFilePath (tags, false); + } + + string GetLogFilePath (string tags, bool mainLogFile) + { + string logFileName; + if (String.IsNullOrEmpty (tags)) { + if (!mainLogFile) + throw new ArgumentException ("must not be null or empty", nameof (tags)); + logFileName = $"{Configurables.Defaults.LogFilePrefix}-{BuildTimeStamp}.log"; + } else { + logFileName = $"{Configurables.Defaults.LogFilePrefix}-{BuildTimeStamp}.{tags}.log"; + } + + return Path.Combine (LogDirectory, logFileName); + } + + /// + /// Check value of a condition flag. If the flag was never set, it will return false + /// + public bool CheckCondition (KnownConditions knownCondition) + { + if (!conditions.TryGetValue (knownCondition, out bool v)) + return false; + + return v; + } + + /// + /// Set a condition flag that can be used by any part of the program for its own purposes. + /// + public void SetCondition (KnownConditions knownCondition, bool v) + { + Log.DebugLine ($"Setting condition {knownCondition} to '{v}'"); + conditions [knownCondition] = v; + } + + /// + /// Initialize the execution context. Called only once from Main. + /// + public async Task Init (string scenarioName = null) + { + SetCondition (KnownConditions.AllowProgramInstallation, true); + + characters = Characters.Create (this); + + Log.StatusLine ("Main log file: ", MainLogFilePath, ConsoleColor.Gray, Log.DestinationColor); + + MonoOptions = new List { + "--debug", // Doesn't hurt to have line numbers in stack traces... + }; + + Banner ("Detecting operating system"); + InitOS (); + + Log.StatusLine (); + Log.StatusLine (" OS type: ", OS.Type, tailColor: Log.InfoColor); + Log.StatusLine (" OS name: ", OS.Name, tailColor: Log.InfoColor); + Log.StatusLine ("OS release: ", OS.Release, tailColor: Log.InfoColor); + Log.StatusLine (" OS bits: ", OS.Architecture, tailColor: Log.InfoColor); + Log.StatusLine (" CPU count: ", OS.CPUCount.ToString (), tailColor: Log.InfoColor); + Log.StatusLine (); + + if (EnableAllTargets) { + Properties.Set (KnownProperties.AndroidSupportedTargetJitAbis, Utilities.ToXamarinAndroidPropertyValue (AbiNames.AllJitAbis)); + Properties.Set (KnownProperties.AndroidSupportedHostJitAbis, Utilities.ToXamarinAndroidPropertyValue (AbiNames.AllHostAbis)); + Properties.Set (KnownProperties.AndroidSupportedTargetAotAbis, Utilities.ToXamarinAndroidPropertyValue (AbiNames.AllAotAbis)); + } + + VersionFetchers = new VersionFetchers (); + Tools = new EssentialTools (); + DiscoverScenarios (scenarioName); + + if (!await OS.Init ()) { + Log.ErrorLine ("Failed to initialize OS support"); + return false; + } + + Tools.Init (this); + + Banner ("Updating Git submodules"); + + var git = new GitRunner (this); + if (!await git.SubmoduleUpdate ()) + Log.WarningLine ("Failed to update Git submodules"); + + BuildInfo = new BuildInfo (); + await BuildInfo.GatherGitInfo (this); + AbiNames.LogAllNames (this); + + if (MakeConcurrency == 0) + MakeConcurrency = OS.CPUCount + 1; + + return true; + } + + /// + /// Execute the selected scenario (either the default one or one chosen by using the -s command line parameter) + /// + public async Task Execute () + { + Scenario scenario = SelectedScenario; + Banner ($"Running scenario: {scenario.Description}"); + + string logFilePath = scenario.LogFilePath ?? mainLogFilePath; + Log scenarioLog = null; + + if (!String.IsNullOrEmpty (scenario.LogFilePath)) { + Log.StatusLine ("Log file: ", scenario.LogFilePath, ConsoleColor.Gray, Log.DestinationColor); + scenarioLog = new Log (logFilePath); + } else { + Log.StatusLine ("Logging to main log file"); + } + + try { + await scenario.Run (this, scenarioLog); + } finally { + scenarioLog?.Dispose (); + } + + return true; + } + + void DiscoverScenarios (string scenarioName) + { + List types = Utilities.GetTypesWithCustomAttribute (); + + bool haveScenarioName = !String.IsNullOrEmpty (scenarioName); + SelectedScenario = null; + defaultScenario = null; + foreach (Type type in types) { + var scenario = Activator.CreateInstance (type) as Scenario; + Scenarios.Add (scenario.Name, scenario); + if (IsDefaultScenario (type)) { + if (defaultScenario != null) + throw new InvalidOperationException ($"Only one default scenario is allowed. {defaultScenario} was previously declared as one, {type} is also marked as deafult"); + defaultScenario = scenario; + } + + if (!haveScenarioName || SelectedScenario != null) + continue; + + if (String.Compare (scenarioName, scenario.Name, StringComparison.OrdinalIgnoreCase) != 0) + continue; + + SelectedScenario = scenario; + } + + if (haveScenarioName && SelectedScenario == null) + throw new InvalidOperationException ($"Unknown scenario '{scenarioName}'"); + + if (SelectedScenario == null) + SelectedScenario = defaultScenario; + + if (SelectedScenario == null) + throw new InvalidOperationException ("No specific scenario named and no default scenario found"); + + Log.DebugLine ($"Initializing scenario {SelectedScenario.Name}"); + SelectedScenario.Init (this); + } + + bool IsDefaultScenario (Type type) + { + foreach (ScenarioAttribute attr in type.GetCustomAttributes (typeof(ScenarioAttribute), true)) { + if (attr.IsDefault) + return true; + } + + return false; + } + + /// + /// Print a "banner" to the output stream - will not show anything only if logging verbosity is set to + /// + public void Banner (string text) + { + if (LoggingVerbosity <= LoggingVerbosity.Quiet) + return; + + Log.StatusLine (); + Log.StatusLine ("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=", BannerColor); + Log.StatusLine (text, BannerColor); + Log.StatusLine ("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=", BannerColor); + Log.StatusLine (); + } + + string GetLogDirectory () + { + if (!String.IsNullOrEmpty (overridenLogDirectory)) + return overridenLogDirectory; + + if (!String.IsNullOrEmpty (logDirectory)) + return logDirectory; + + logDirectory = Path.Combine (BuildPaths.XamarinAndroidSourceRoot, "bin", $"Build{Configuration}"); + if (!Directory.Exists (logDirectory)) + Directory.CreateDirectory (logDirectory); + + return logDirectory; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/DetermineWindowsVersion.Windows.cs b/build-tools/xaprepare/xaprepare/Application/DetermineWindowsVersion.Windows.cs new file mode 100644 index 000000000..cd207b871 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/DetermineWindowsVersion.Windows.cs @@ -0,0 +1,160 @@ +using System; + +// Based on code from https://code.msdn.microsoft.com/windowsapps/How-to-determine-the-263b1850 +namespace Xamarin.Android.Prepare +{ + class OSVersionInfo + { + public string Name { get; private set; } + public int Minor { get; private set; } + public int Major { get; private set; } + public int Build { get; private set; } + + public string FullName => $"Microsoft {Name} [Version {Major}.{Minor}.{Build}]"; + + OSVersionInfo () + {} + + /// + /// Init OSVersionInfo object by current windows environment + /// + /// + public static OSVersionInfo GetOSVersionInfo () + { + OperatingSystem osVersionObj = Environment.OSVersion; + OSVersionInfo osVersionInfo = new OSVersionInfo () { + Name = GetOSName (osVersionObj), + Major = osVersionObj.Version.Major, + Minor = osVersionObj.Version.Minor, + Build = osVersionObj.Version.Build + }; + + return osVersionInfo; + } + + /// + /// Get current windows name + /// + /// + /// + static string GetOSName (OperatingSystem osInfo) + { + string osName = "unknown"; + switch (osInfo.Platform) { + //for old windows kernel + case PlatformID.Win32Windows: + osName = ForWin32Windows (osInfo); + break; + + //fow NT kernel + case PlatformID.Win32NT: + osName = ForWin32NT (osInfo); + break; + } + + return osName; + } + + /// + /// for old windows kernel + /// this function is the child function for method GetOSName + /// + /// + /// + static string ForWin32Windows (OperatingSystem osInfo) + { + string osVersion = "Unknown"; + + //Code to determine specific version of Windows 95, + //Windows 98, Windows 98 Second Edition, or Windows Me. + switch (osInfo.Version.Minor) { + case 0: + osVersion = "Windows 95"; + break; + + case 10: + switch (osInfo.Version.Revision.ToString ()) { + case "2222A": + osVersion = "Windows 98 Second Edition"; + break; + + default: + osVersion = "Windows 98"; + break; + } + break; + + case 90: + osVersion = "Windows Me"; + break; + } + + return osVersion; + } + + /// + /// fow NT kernel + /// this function is the child function for method GetOSName + /// + /// + /// + static string ForWin32NT (OperatingSystem osInfo) + { + string osVersion = "Unknown"; + + //Code to determine specific version of Windows NT 3.51, + //Windows NT 4.0, Windows 2000, or Windows XP. + switch (osInfo.Version.Major) { + case 3: + osVersion = "Windows NT 3.51"; + break; + + case 4: + osVersion = "Windows NT 4.0"; + break; + + case 5: + switch (osInfo.Version.Minor) { + case 0: + osVersion = "Windows 2000"; + break; + + case 1: + osVersion = "Windows XP"; + break; + + case 2: + osVersion = "Windows 2003"; + break; + } + break; + + case 6: + switch (osInfo.Version.Minor) { + case 0: + osVersion = "Windows Vista"; + break; + + case 1: + osVersion = "Windows 7"; + break; + + case 2: + osVersion = "Windows 8"; + break; + + case 3: + osVersion = "Windows 8.1"; + break; + } + break; + + case 10: + osVersion = "Windows 10"; + break; + } + + return osVersion; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/DownloadStatus.cs b/build-tools/xaprepare/xaprepare/Application/DownloadStatus.cs new file mode 100644 index 000000000..1107e8239 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/DownloadStatus.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Concurrent; +using System.Diagnostics; +using System.Linq; + +namespace Xamarin.Android.Prepare +{ + class DownloadStatus + { + const uint DefaultUpdateInterval = 1000; + readonly object updateLock = new object (); + + ConcurrentQueue byteSnapshots; + Stopwatch watch; + Action updater; + + public ulong TotalSize { get; } + public ulong DownloadedSoFar { get; set; } + public ulong BytesPerSecond { get; set; } + public uint UpdateIntervalMS { get; set; } = DefaultUpdateInterval; + + public DownloadStatus (ulong totalSize, Action updaterCallback) + { + if (updaterCallback == null) + throw new ArgumentNullException (nameof (updaterCallback)); + + TotalSize = totalSize; + DownloadedSoFar = 0; + BytesPerSecond = 0; + byteSnapshots = new ConcurrentQueue (); + watch = new Stopwatch (); + updater = updaterCallback; + } + + public void Start () + { + watch.Start (); + } + + public void Update (ulong bytesRead) + { + if (bytesRead == 0) + return; + + bool timeForUpdate = watch.ElapsedMilliseconds >= UpdateIntervalMS; + if (timeForUpdate) { + lock (updateLock) { + StoreAndUpdate (bytesRead, timeForUpdate); + } + } else { + StoreAndUpdate (bytesRead, timeForUpdate); + } + } + + void StoreAndUpdate (ulong bytesRead, bool timeForUpdate) + { + byteSnapshots.Enqueue (bytesRead); + if (!timeForUpdate) + return; + + ulong[] snapshots = byteSnapshots.ToArray (); + while (!byteSnapshots.IsEmpty) + byteSnapshots.TryDequeue (out ulong _); + + // LINQ has no overloads for UInt64 (!?), so we do it by hand... + ulong bytesPerSecond = 0; + foreach (ulong u in snapshots) { + DownloadedSoFar += u; + bytesPerSecond += u; + } + BytesPerSecond = bytesPerSecond; + + updater (this); + watch.Restart (); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/EssentialTools.Linux.cs b/build-tools/xaprepare/xaprepare/Application/EssentialTools.Linux.cs new file mode 100644 index 000000000..13755291a --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/EssentialTools.Linux.cs @@ -0,0 +1,12 @@ +using System; + +namespace Xamarin.Android.Prepare +{ + partial class EssentialTools : AppObject + { + partial void InitOS (Context context) + { + InitSharedUnixOS (context); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/EssentialTools.MacOS.cs b/build-tools/xaprepare/xaprepare/Application/EssentialTools.MacOS.cs new file mode 100644 index 000000000..1f43e7b5f --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/EssentialTools.MacOS.cs @@ -0,0 +1,25 @@ +using System; + +namespace Xamarin.Android.Prepare +{ + partial class EssentialTools : AppObject + { + public string BrewPath { get; set; } + public string PkgutilPath { get; set; } + + partial void InitOS (Context context) + { + Log.StatusLine ($" {context.Characters.Bullet} homebrew", ConsoleColor.White); + if (String.IsNullOrEmpty (BrewPath)) + BrewPath = context.OS.Which ("brew", required: true); + Log.StatusLine (" Found: ", BrewPath, tailColor: Log.DestinationColor); + + Log.StatusLine ($" {context.Characters.Bullet} pkgutil", ConsoleColor.White); + if (String.IsNullOrEmpty (PkgutilPath)) + PkgutilPath = context.OS.Which ("/usr/sbin/pkgutil", required: true); + Log.StatusLine ($" Found: ", PkgutilPath, tailColor: Log.DestinationColor); + + InitSharedUnixOS (context); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/EssentialTools.Unix.cs b/build-tools/xaprepare/xaprepare/Application/EssentialTools.Unix.cs new file mode 100644 index 000000000..f69609e3b --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/EssentialTools.Unix.cs @@ -0,0 +1,8 @@ +namespace Xamarin.Android.Prepare +{ + partial class EssentialTools + { + void InitSharedUnixOS (Context context) + {} + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/EssentialTools.cs b/build-tools/xaprepare/xaprepare/Application/EssentialTools.cs new file mode 100644 index 000000000..6d7fb4e75 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/EssentialTools.cs @@ -0,0 +1,31 @@ +using System; + +namespace Xamarin.Android.Prepare +{ + partial class EssentialTools : AppObject + { + public string GitPath { get; set; } + public string SevenZipPath { get; set; } + + public EssentialTools () + {} + + public void Init (Context context) + { + Log.StatusLine (); + Log.StatusLine ("Locating essential tool binaries", ConsoleColor.DarkGreen); + + Log.StatusLine ($" {context.Characters.Bullet} git", ConsoleColor.White); + GitPath = context.OS.Which ("git", required: true); + Log.StatusLine (" Found: ", GitPath, tailColor: Log.DestinationColor); + + Log.StatusLine ($" {context.Characters.Bullet} 7za", ConsoleColor.White); + SevenZipPath = context.OS.Which ("7za", required: true); + Log.StatusLine (" Found: ", SevenZipPath, tailColor: Log.DestinationColor); + + InitOS (context); + } + + partial void InitOS (Context context); + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/ExecutionMode.cs b/build-tools/xaprepare/xaprepare/Application/ExecutionMode.cs new file mode 100644 index 000000000..282920794 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/ExecutionMode.cs @@ -0,0 +1,9 @@ +namespace Xamarin.Android.Prepare +{ + enum ExecutionMode + { + CI, + Standard, + Interactive + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/Extensions.DictionaryOfProgramVersionParser.cs b/build-tools/xaprepare/xaprepare/Application/Extensions.DictionaryOfProgramVersionParser.cs new file mode 100644 index 000000000..bd15fe93b --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/Extensions.DictionaryOfProgramVersionParser.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; + +namespace Xamarin.Android.Prepare +{ + static class DictionaryOfProgramVersionParser_Extensions + { + public static void Add (this Dictionary dict, string programName, string versionArguments, Regex regex, uint versionOutputLine = 0, Log log = null) + { + if (dict == null) + throw new ArgumentNullException (nameof (dict)); + + if (dict.ContainsKey (programName)) { + Log.Instance.WarningLine ($"Entry for {programName} version matcher already defined. Ignoring the new entry ({regex})"); + return; + } + + dict [programName] = new RegexProgramVersionParser (programName, versionArguments, regex, versionOutputLine, log); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/Extensions.ListOfBundleItem.cs b/build-tools/xaprepare/xaprepare/Application/Extensions.ListOfBundleItem.cs new file mode 100644 index 000000000..d0683442e --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/Extensions.ListOfBundleItem.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Xamarin.Android.Prepare +{ + static class BundleItem_List_Extensions + { + public static void Add (this List list, string sourcePath) + { + list.Add (sourcePath, archivePath: null, shouldInclude: null); + } + + public static void Add (this List list, string sourcePath, string archivePath) + { + list.Add (sourcePath, archivePath, shouldInclude: null); + } + + public static void Add (this List list, string sourcePath, Func shouldInclude) + { + list.Add (sourcePath, archivePath: null, shouldInclude: shouldInclude); + } + + public static void Add (this List list, string sourcePath, string archivePath, Func shouldInclude) + { + if (list == null) + throw new ArgumentNullException (nameof (list)); + + list.Add (new BundleItem (sourcePath, archivePath, shouldInclude)); + } + + public static void AddRange (this List list, List bclFiles) + { + if (list == null) + throw new ArgumentNullException (nameof (list)); + + if (bclFiles == null) + throw new ArgumentNullException (nameof (bclFiles)); + + foreach (BclFile bf in bclFiles) { + if (bf == null) + continue; + + // BCL file's *destination* location is our *source* path + (string destFilePath, string debugSymbolsDestPath) = MonoRuntimesHelpers.GetDestinationPaths (bf); + list.Add (destFilePath); + if (bf.ExcludeDebugSymbols || !File.Exists (debugSymbolsDestPath)) + continue; + list.Add (debugSymbolsDestPath); + } + } + + public static void AddRange (this List list, List muFiles) + { + if (list == null) + throw new ArgumentNullException (nameof (list)); + + if (muFiles == null) + throw new ArgumentNullException (nameof (muFiles)); + + foreach (MonoUtilityFile muf in muFiles) { + if (muf == null) + continue; + + // MU file's *destination* location is our *source* path + (string destFilePath, string debugSymbolsDestPath) = MonoRuntimesHelpers.GetDestinationPaths (muf); + list.Add (destFilePath); + if (muf.IgnoreDebugInfo || String.IsNullOrEmpty (debugSymbolsDestPath)) + continue; + list.Add (debugSymbolsDestPath); + } + } + + public static void AddRange (this List list, List runtimeFiles) + { + if (list == null) + throw new ArgumentNullException (nameof (list)); + + if (runtimeFiles == null) + throw new ArgumentNullException (nameof (runtimeFiles)); + + var sharedFiles = new HashSet (StringComparer.Ordinal); + foreach (Runtime runtime in MonoRuntimesHelpers.GetEnabledRuntimes (new Runtimes (), false)) { + foreach (RuntimeFile rtf in runtimeFiles) { + if (rtf == null) + continue; + + // Runtime file's *destination* location is our *source* path + (bool skipFile, string _, string destFilePath) = MonoRuntimesHelpers.GetRuntimeFilePaths (runtime, rtf); + if (rtf.Shared && sharedFiles.Contains (destFilePath)) + continue; + + if (skipFile) + continue; + list.Add (destFilePath); + + if (rtf.Shared) + sharedFiles.Add (destFilePath); + } + } + } + + public static void AddRange (this List list, List testAssemblies) + { + if (list == null) + throw new ArgumentNullException (nameof (list)); + + if (testAssemblies == null) + throw new ArgumentNullException (nameof (testAssemblies)); + + foreach (TestAssembly tasm in testAssemblies) { + if (tasm == null) + continue; + + // Test assembly's *destination* location is our *source* path + (string destFilePath, string debugSymbolsDestPath) = MonoRuntimesHelpers.GetDestinationPaths (tasm); + list.Add (destFilePath); + if (String.IsNullOrEmpty (debugSymbolsDestPath)) + continue; + list.Add (debugSymbolsDestPath); + } + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/Extensions.Runtime.cs b/build-tools/xaprepare/xaprepare/Application/Extensions.Runtime.cs new file mode 100644 index 000000000..2e6f3aaaf --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/Extensions.Runtime.cs @@ -0,0 +1,10 @@ +namespace Xamarin.Android.Prepare +{ + static class Runtime_Extensions + { + public static T As (this Runtime runtime) where T: Runtime + { + return runtime as T; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/ExternalGitDependency.cs b/build-tools/xaprepare/xaprepare/Application/ExternalGitDependency.cs new file mode 100644 index 000000000..a129a90bd --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/ExternalGitDependency.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; + +namespace Xamarin.Android.Prepare +{ + sealed class ExternalGitDependency : AppObject + { + const string GitHubServer = "github.com"; + + static readonly Regex externalRegex = new Regex (@" +^ +\s* +(?\#.*) +| +( + \s* + (?[^/]+) + / + (?[^:]+) + : + (?[^@]+) + @ + (?.*) +) +$ +", RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace); + + public string Branch { get; private set; } + public string Commit { get; private set; } + public string Name { get; private set; } + public string Owner { get; private set; } + + public static List GetDependencies (Context context, string externalFilePath) + { + Log.Instance.StatusLine ($" {context.Characters.Bullet} Reading external dependencies from {Utilities.GetRelativePath (BuildPaths.XamarinAndroidSourceRoot, externalFilePath)}"); + string[] unparsedExternals = File.ReadAllLines (externalFilePath); + var externals = new List (unparsedExternals.Length); + + foreach (string external in unparsedExternals) { + Match match = externalRegex.Match (external); + if (match != null && match.Success) { + if (match.Groups["comment"].Success) { + // Ignore matching lines which start with '#'. + continue; + } + + var e = new ExternalGitDependency { + Branch = match.Groups["branch"].Value, + Commit = match.Groups["commit"].Value, + Name = match.Groups["repo"].Value, + Owner = match.Groups["owner"].Value, + }; + externals.Add (e); + Log.Instance.StatusLine ($" {context.Characters.Bullet} {e.Owner}/{e.Name} ({e.Commit})"); + } + } + + return externals; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/GeneratedFile.cs b/build-tools/xaprepare/xaprepare/Application/GeneratedFile.cs new file mode 100644 index 000000000..dee7711f8 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/GeneratedFile.cs @@ -0,0 +1,37 @@ +using System; +using System.IO; + +namespace Xamarin.Android.Prepare +{ + abstract class GeneratedFile : AppObject + { + public string InputPath { get; } + public string OutputPath { get; } + public bool EchoOutput { get; set; } + + protected GeneratedFile (string outputPath) + { + OutputPath = outputPath?.Trim (); + if (String.IsNullOrEmpty (OutputPath)) + throw new ArgumentException ("must not be null or empty", nameof (outputPath)); + } + + protected GeneratedFile (string inputPath, string outputPath) + : this (outputPath) + { + InputPath = inputPath?.Trim (); + if (String.IsNullOrEmpty (InputPath)) + throw new ArgumentException ("must not be null or empty", nameof (inputPath)); + + if (!File.Exists (InputPath)) + throw new InvalidOperationException ($"Input file {InputPath} must exist"); + } + + public abstract void Generate (Context context); + + protected void EnsureOutputDir () + { + Utilities.CreateDirectory (Path.GetDirectoryName (OutputPath)); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/GeneratedMakeRulesFile.MacOS.cs b/build-tools/xaprepare/xaprepare/Application/GeneratedMakeRulesFile.MacOS.cs new file mode 100644 index 000000000..188ba9948 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/GeneratedMakeRulesFile.MacOS.cs @@ -0,0 +1,13 @@ +using System.IO; + +namespace Xamarin.Android.Prepare +{ + partial class GeneratedMakeRulesFile + { + partial void OutputOSVariables (Context context, StreamWriter sw) + { + sw.WriteLine ($"export MACOSX_DEPLOYMENT_TARGET := {Configurables.Defaults.MacOSDeploymentTarget}"); + sw.WriteLine ($"HOMEBREW_PREFIX := {context.OS.HomebrewPrefix}"); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/GeneratedMakeRulesFile.cs b/build-tools/xaprepare/xaprepare/Application/GeneratedMakeRulesFile.cs new file mode 100644 index 000000000..75917716c --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/GeneratedMakeRulesFile.cs @@ -0,0 +1,229 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Xamarin.Android.Prepare +{ + partial class GeneratedMakeRulesFile : GeneratedFile + { + public GeneratedMakeRulesFile (string outputPath) + : base (outputPath) + {} + + public override void Generate (Context context) + { + Log.Todo ("Generate some help for `make help`"); + + if (context == null) + throw new ArgumentNullException (nameof (context)); + + using (StreamWriter sw = Utilities.OpenStreamWriter (OutputPath)) { + Generate (context, sw); + sw.Flush (); + } + } + + string GetOutputFileName (Context context, string namePrefix) + { + return $"{namePrefix}-v{BuildInfo.XAVersion}.$(-num-commits-since-version-change)_{context.OS.Type}-{context.OS.Architecture}_$(GIT_BRANCH)_$(GIT_COMMIT)-$(CONFIGURATION)"; + } + + void Generate (Context context, StreamWriter sw) + { + string myPath = Path.Combine (BuildPaths.XAPrepareSourceDir, "Application", "GeneratedMakeRulesFile.cs"); + sw.WriteLine ( "#"); + sw.WriteLine ($"# Generated by {myPath}"); + sw.WriteLine ( "#"); + sw.WriteLine (); + + WriteVariable ("export OS_NAME", context.OS.Type); + WriteVariable ("export OS_ARCH", context.OS.Architecture); + WriteVariable ("PRODUCT_VERSION", context.ProductVersion); + WriteVariable ("MONO_SOURCE_FULL_PATH", Configurables.Paths.MonoSourceFullPath); + + // These must remain dynamic since the developer may change branches without re-running `prepare` + string getGitBranchScript = Utilities.GetRelativePath (BuildPaths.XamarinAndroidSourceRoot, Path.Combine (Configurables.Paths.BuildToolsScriptsDir, "get-git-branch.sh")); + WriteVariable ("GIT_BRANCH", $"$(shell LANG=C \"{getGitBranchScript}\" | tr -d '[[:space:]]' | tr -C a-zA-Z0-9- _)"); + WriteVariable ("GIT_COMMIT", $"$(shell LANG=C git log --no-color --first-parent -n1 --pretty=format:%h)"); + WriteVariable ("-num-commits-since-version-change", $"$(shell LANG=C git log {context.BuildInfo.CommitOfLastVersionChange}..HEAD --oneline 2>/dev/null | wc -l | sed 's/ //g')"); + + WriteVariable ("ZIP_EXTENSION", context.OS.ZipExtension); + WriteVariable ("ZIP_OUTPUT_BASENAME", GetOutputFileName (context, "xamarin.android-oss")); + WriteVariable ("_TEST_RESULTS_BASENAME", GetOutputFileName (context, "xa-test-results")); + WriteVariable ("_BUILD_STATUS_BASENAME", GetOutputFileName (context, "xa-build-status")); + + WriteVariable ("ZIP_OUTPUT", "$(ZIP_OUTPUT_BASENAME).$(ZIP_EXTENSION)"); + WriteVariable ("_BUILD_STATUS_ZIP_OUTPUT", "$(_BUILD_STATUS_BASENAME).$(ZIP_EXTENSION)"); + WriteVariable ("_TEST_RESULTS_ZIP_OUTPUT", "$(_TEST_RESULTS_BASENAME).$(ZIP_EXTENSION)"); + + var allApiLevels = new List (); + var allPlatformIDs = new List (); + var allFrameworks = new List (); + var apiLevels = new List (); + var stableApiLevels = new List (); + var frameworks = new List (); + var stableFrameworks = new List (); + var platformIds = new List (); + + foreach (AndroidPlatform ap in BuildAndroidPlatforms.AllPlatforms) { + string api = ap.ApiLevel.ToString (); + + allApiLevels.Add (api); + allPlatformIDs.Add (ap.PlatformID); + if (!String.IsNullOrEmpty (ap.Framework)) { + allFrameworks.Add (ap.Framework); + frameworks.Add (ap.Framework); + if (ap.Stable) + stableFrameworks.Add (ap.Framework); + } else + allFrameworks.Add ("-"); + + if (!ap.Supported) + continue; + + apiLevels.Add (api); + platformIds.Add (ap.PlatformID); + if (ap.Stable) + stableApiLevels.Add (api); + } + + WriteVariable ("ALL_API_LEVELS", ToValue (allApiLevels)); + WriteVariable ("ALL_PLATFORM_IDS", ToValue (allPlatformIDs)); + WriteVariable ("ALL_FRAMEWORKS", ToValue (allFrameworks)); + WriteVariable ("API_LEVELS", ToValue (apiLevels)); + WriteVariable ("STABLE_API_LEVELS", ToValue (stableApiLevels)); + WriteVariable ("FRAMEWORKS", ToValue (frameworks)); + WriteVariable ("STABLE_FRAMEWORKS", ToValue (stableFrameworks)); + WriteVariable ("ALL_JIT_ABIS", ToValue (AbiNames.AllJitAbis)); + WriteVariable ("ALL_HOST_ABIS", ToValue (AbiNames.AllHostAbis)); + WriteVariable ("ALL_AOT_ABIS", ToValue (AbiNames.AllAotAbis)); + if (context.MonoOptions != null && context.MonoOptions.Count > 0) { + WriteVariable ("MONO_OPTIONS", ToValue (context.MonoOptions)); + sw.WriteLine ("export MONO_OPTIONS"); + } + + sw.WriteLine ("_MSBUILD_ARGS = \\"); + sw.WriteLine ($"\t/p:{KnownProperties.AndroidSupportedTargetJitAbis}={Utilities.ToXamarinAndroidPropertyValue (AbiNames.AllJitAbis)} \\"); + sw.WriteLine ($"\t/p:{KnownProperties.AndroidSupportedHostJitAbis}={Utilities.ToXamarinAndroidPropertyValue (AbiNames.AllHostAbis)} \\"); + sw.WriteLine ($"\t/p:{KnownProperties.AndroidSupportedTargetAotAbis}={Utilities.ToXamarinAndroidPropertyValue (AbiNames.AllAotAbis)}"); + + OutputOSVariables (context, sw); + + WriteListVariable ("_BUNDLE_ZIPS_INCLUDE", Configurables.Defaults.BundleZipsInclude); + WriteListVariable ("_BUNDLE_ZIPS_EXCLUDE", Configurables.Defaults.BundleZipsExclude); + WriteListVariable ("_TEST_RESULTS_BUNDLE_INCLUDE", Configurables.Defaults.TestResultsBundleInclude); + WriteListVariable ("_TEST_RESULTS_BUNDLE_EXCLUDE", Configurables.Defaults.TestResultsBundleExclude); + WriteListVariable ("_BUILD_STATUS_BUNDLE_INCLUDE", Configurables.Defaults.BuildStatusBundleInclude); + WriteListVariable ("_BUILD_STATUS_BUNDLE_INCLUDE", Configurables.Defaults.BuildStatusBundleIncludeConditional, true); + WriteListVariable ("_BUILD_STATUS_BUNDLE_EXCLUDE", Configurables.Defaults.BuildStatusBundleExclude); + + sw.WriteLine (); + sw.WriteLine (".PHONY: framework-assemblies"); + sw.WriteLine ("framework-assemblies:"); + + string prevVersion = "v1.0"; + string monoFrameworksRoot = Path.Combine ("bin", "$(CONFIGURATION)", context.Properties.GetRequiredValue (KnownProperties.XABinRelativeInstallPrefix), Configurables.Paths.MonoAndroidFrameworksSubDir); + for (int i = 0; i < apiLevels.Count; i++) { + string curVersion = frameworks [i]; + string apiLevel = apiLevels [i]; + string platformId = platformIds [i]; + string redistFile = Path.Combine (monoFrameworksRoot, curVersion, "RedistList", "FrameworkList.xml"); + WriteRuleLine ($"grep -q {prevVersion} {redistFile}; \\"); + WriteRuleLine ( "if [ $$? -ne 0 ] ; then \\"); + WriteRuleLine ($"\trm -f {redistFile}; \\"); + WriteRuleLine ( "fi; \\"); + WriteRuleLine ( "$(call MSBUILD_BINLOG,Mono.Android,$(_SLN_BUILD)) src/Mono.Android/Mono.Android.csproj \\"); + WriteRuleLine ( "\t/p:Configuration=$(CONFIGURATION) $(_MSBUILD_ARGS) \\"); + WriteRuleLine ($"\t/p:AndroidApiLevel={apiLevel} /p:AndroidPlatformId={platformId} /p:AndroidFrameworkVersion={curVersion} \\"); + WriteRuleLine ($"\t/p:AndroidPreviousFrameworkVersion={prevVersion} || exit 1;"); + + prevVersion = curVersion; + } + + string firstApiLevel = apiLevels [0]; + string firstPlatformId = platformIds [0]; + string firstFramework = frameworks [0]; + string latestStableFramework = stableFrameworks [stableFrameworks.Count - 1]; + + WriteMSBuildCall ( + fileToRemovePath: Path.Combine (monoFrameworksRoot, "v1.0", "Xamarin.Android.NUnitLite.dll"), + projectPath: "src/Xamarin.Android.NUnitLite/Xamarin.Android.NUnitLite.csproj" + ); + + WriteMSBuildCall ( + fileToRemovePath: $"{monoFrameworksRoot}/{latestStableFramework}/Mono.Android.Export.*", + projectPath: "src/Mono.Android.Export/Mono.Android.Export.csproj" + ); + + WriteMSBuildCall ( + fileToRemovePath: $"{monoFrameworksRoot}/{latestStableFramework}/OpenTK-1.0.*", + projectPath: "src/OpenTK-1.0/OpenTK.csproj" + ); + sw.WriteLine (); + + if (context.RuleGenerators == null || context.RuleGenerators.Count == 0) + return; + + foreach (RuleGenerator rg in context.RuleGenerators) { + if (rg == null) + continue; + rg (this, sw); + } + + void WriteMSBuildCall (string fileToRemovePath, string projectPath) + { + WriteRuleLine ($"rm -f {fileToRemovePath}"); + WriteRuleLine ($"$(call MSBUILD_BINLOG,NUnitLite,$(_SLN_BUILD)) $(MSBUILD_FLAGS) {projectPath} \\"); + WriteRuleLine ( "\t/p:Configuration=$(CONFIGURATION) $(_MSBUILD_ARGS) \\"); + WriteRuleLine ($"\t/p:AndroidApiLevel={firstApiLevel} /p:AndroidPlatformId={firstPlatformId} \\"); + WriteRuleLine ($"\t/p:AndroidFrameworkVersion={firstFramework} || exit 1;"); + } + + string ToValue (ICollection list, string separator = null) + { + return String.Join (separator ?? " ", list); + } + + void WriteRuleLine (string line = null) + { + sw.Write ('\t'); + sw.WriteLine (line); + } + + void WriteVariable (string name, string value) + { + sw.WriteLine ($"{name} = {value}"); + } + + void WriteListVariable (string name, ICollection list, bool conditional = false) + { + if (list.Count == 0) + return; + + if (!conditional) + sw.Write ($"{name} ="); + + foreach (string i in list) { + string item = i?.Trim (); + if (String.IsNullOrEmpty (item)) + continue; + + if (conditional) { + sw.WriteLine ($"ifneq ($(wildcard {item}),)"); + sw.WriteLine ($"{name} += {item}"); + sw.WriteLine ("endif"); + continue; + } + + sw.WriteLine (" \\"); + sw.Write ($"\t{item}"); + } + + if (!conditional) + sw.WriteLine (); + } + } + + partial void OutputOSVariables (Context context, StreamWriter sw); + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/GeneratedPlaceholdersFile.cs b/build-tools/xaprepare/xaprepare/Application/GeneratedPlaceholdersFile.cs new file mode 100644 index 000000000..681bdf209 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/GeneratedPlaceholdersFile.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Xamarin.Android.Prepare +{ + class GeneratedPlaceholdersFile : GeneratedFile + { + IDictionary replacements; + + public GeneratedPlaceholdersFile (IDictionary replacements, string inputPath, string outputPath) + : base (inputPath, outputPath) + { + if (replacements == null) + throw new ArgumentNullException (nameof (replacements)); + this.replacements = replacements; + } + + public override void Generate (Context context) + { + var inputData = new StringBuilder (File.ReadAllText (InputPath, Encoding.UTF8)); + + foreach (var kvp in replacements) { + string placeholder = kvp.Key?.Trim (); + if (String.IsNullOrEmpty (placeholder)) + continue; + + inputData.Replace (placeholder, kvp.Value ?? String.Empty); + } + + EnsureOutputDir (); + string outputData = inputData.ToString (); + File.WriteAllText (OutputPath, outputData, Encoding.UTF8); + + if (!EchoOutput) + return; + + Log.DebugLine (); + Log.DebugLine ("--------------------------------------------"); + Log.DebugLine (outputData); + Log.DebugLine ("--------------------------------------------"); + Log.DebugLine (); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/GeneratedProfileAssembliesProjitemsFile.cs b/build-tools/xaprepare/xaprepare/Application/GeneratedProfileAssembliesProjitemsFile.cs new file mode 100644 index 000000000..4ab12fc0a --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/GeneratedProfileAssembliesProjitemsFile.cs @@ -0,0 +1,183 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace Xamarin.Android.Prepare +{ + class GeneratedProfileAssembliesProjitemsFile : GeneratedFile + { + const string FileTop = @" + + + +"; + + const string FileBottom = @" + +"; + + public GeneratedProfileAssembliesProjitemsFile (string outputPath) + : base (outputPath) + {} + + public override void Generate (Context context) + { + var runtimes = new Runtimes (); + + IEnumerable facadeAssemblies = runtimes.BclFilesToInstall.Where (f => f.Type == BclFileType.FacadeAssembly); + IEnumerable profileAssemblies = runtimes.BclFilesToInstall.Where (f => f.Type == BclFileType.ProfileAssembly); + IEnumerable testAssemblies = Runtimes.TestAssemblies; + + EnsureNoDiscrepancies (facadeAssemblies, profileAssemblies, testAssemblies.Where (ta => ta.TestType != TestAssemblyType.Reference && ta.TestType != TestAssemblyType.TestRunner)); + + using (var fs = File.Open (OutputPath, FileMode.Create)) { + using (var sw = new StreamWriter (fs)) { + GenerateFile (sw, facadeAssemblies, profileAssemblies, testAssemblies); + } + } + } + + void EnsureNoDiscrepancies (IEnumerable facadeAssemblies, IEnumerable profileAssemblies, IEnumerable testAssemblies) + { + bool failed = false; + + // We compare against the *installed* locations since we will not always need to download and/or build the + // Mono Archive (when the XA bundle is present) so we can't rely on the *source* locations of those + // assemblies to be present. + failed |= FileSetsDiffer (facadeAssemblies, Configurables.Paths.InstallBCLFrameworkFacadesDir, "Façade", new HashSet (StringComparer.OrdinalIgnoreCase) { "nunitlite.dll" }); + failed |= FileSetsDiffer (profileAssemblies, Configurables.Paths.InstallBCLFrameworkDir, "Profile"); + failed |= FileSetsDiffer (testAssemblies, Configurables.Paths.BCLTestsDestDir, "Test"); + + if (failed) + throw new InvalidOperationException ("Profile assembly discrepancies found. Please examine 'build-tools/xaprepare/xaprepare/ConfigAndData/Runtimes.cs' to make sure all assemblies listed above are included"); + } + + bool FileSetsDiffer (IEnumerable assemblies, string directoryPath, string batchName, HashSet ignoreFiles = null) + { + List tests = FilesFromDir (directoryPath, ignoreFiles).ToList (); + tests.AddRange ( + FilesFromDir ( + directoryPath, + globPattern: "*.resources.dll", + stripPath: false, + searchSubdirs: true + ).Select (f => Utilities.GetRelativePath (directoryPath, f)) + ); + + return FileSetsDiffer (ToStringSet (assemblies), tests, batchName); + } + + bool FileSetsDiffer (IEnumerable assemblies, string directoryPath, string batchName, HashSet ignoreFiles = null) + { + return FileSetsDiffer (ToStringSet (assemblies), FilesFromDir (directoryPath, ignoreFiles), batchName); + } + + bool FileSetsDiffer (IEnumerable set1, IEnumerable set2, string batchName) + { + List diff = set1.Except (set2).ToList (); + + if (diff.Count == 0) + return false; + + Log.ErrorLine ($"{batchName} assemblies found on disk but missing from xaprepare:"); + foreach (string asm in diff) { + Log.StatusLine ($" {Context.Instance.Characters.Bullet} {asm}", ConsoleColor.Cyan); + } + + return true; + } + + IEnumerable FilesFromDir (string directoryPath, HashSet ignoreFiles = null, string globPattern = "*.dll", bool stripPath = true, bool searchSubdirs = false) + { + IEnumerable files = Directory.EnumerateFiles ( + directoryPath, + globPattern, + searchSubdirs ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly + ).Select (f => stripPath ? Path.GetFileName (f) : f); + + if (ignoreFiles == null || ignoreFiles.Count == 0) + return files; + + return files.Where (f => !ignoreFiles.Contains (f)); + } + + IEnumerable ToStringSet (IEnumerable files) + { + return files.Select (bcf => bcf.Name); + } + + IEnumerable ToStringSet (IEnumerable files) + { + return files.Select (ta => ta.Name); + } + + void GenerateFile (StreamWriter sw, IEnumerable facadeAssemblies, IEnumerable profileAssemblies, IEnumerable testAssemblies) + { + sw.Write (FileTop); + + WriteGroup (sw, "MonoFacadeAssembly", facadeAssemblies); + WriteGroup (sw, "MonoProfileAssembly", profileAssemblies); + WriteGroup (sw, testAssemblies); + + sw.Write (FileBottom); + } + + void WriteGroup (StreamWriter sw, string itemName, IEnumerable files) + { + StartGroup (sw); + foreach (BclFile bcf in files) { + sw.WriteLine ($" <{itemName} Include=\"{bcf.Name}\" />"); + } + EndGroup (sw); + } + + void WriteGroup (StreamWriter sw, IEnumerable files) + { + sw.WriteLine (""); + StartGroup (sw); + foreach (TestAssembly taf in files) { + string itemName = "MonoTestAssembly"; + string testType = null; + + switch (taf.TestType) { + case TestAssemblyType.Satellite: + itemName = "MonoTestSatelliteAssembly"; + break; + + case TestAssemblyType.XUnit: + testType = "xunit"; + break; + + case TestAssemblyType.Reference: + testType = "reference"; + break; + + case TestAssemblyType.TestRunner: + itemName = "MonoTestRunner"; + break; + } + + sw.Write ($" <{itemName} Include=\"{taf.Name}\""); + if (String.IsNullOrEmpty (testType)) + sw.WriteLine (" />"); + else { + sw.WriteLine (" >"); + sw.WriteLine ($" {testType}"); + sw.WriteLine ($" "); + } + } + EndGroup (sw); + } + + void StartGroup (StreamWriter sw) + { + sw.WriteLine (" "); + } + + void EndGroup (StreamWriter sw) + { + sw.WriteLine (" "); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/HomebrewProgram.MacOS.cs b/build-tools/xaprepare/xaprepare/Application/HomebrewProgram.MacOS.cs new file mode 100644 index 000000000..f91d4737e --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/HomebrewProgram.MacOS.cs @@ -0,0 +1,153 @@ +using System; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + class HomebrewProgram : Program + { + string cachedVersionOutput; + bool brewNeedsSudo; + + public override bool NeedsSudoToInstall => brewNeedsSudo; + public string HomebrewTapName { get; } + public Uri HomebrewFormulaUrl { get; } + public bool Pin { get; set; } + + public HomebrewProgram (string homebrewPackageName, string executableName = null) + : this (homebrewPackageName, homebrewTap: null, executableName: executableName) + {} + + public HomebrewProgram (string homebrewPackageName, Uri homebrewFormulaUrl, string executableName = null) + : this (homebrewPackageName, homebrewTap: null, executableName: executableName) + { + HomebrewFormulaUrl = homebrewFormulaUrl ?? throw new ArgumentNullException (nameof (homebrewFormulaUrl)); + } + + public HomebrewProgram (string homebrewPackageName, string homebrewTap, string executableName) + { + if (String.IsNullOrEmpty (homebrewPackageName)) + throw new ArgumentException ("must not be null or empty", nameof (homebrewPackageName)); + Name = homebrewPackageName; + HomebrewTapName = homebrewTap?.Trim (); + ExecutableName = executableName?.Trim (); + } + + public override async Task Install () + { + var runner = new BrewRunner (Context.Instance); + if (!String.IsNullOrEmpty (HomebrewTapName)) { + if (!await runner.Tap (HomebrewTapName)) + return false; + } + + bool success; + if (InstalledButWrongVersion) { + if (HomebrewFormulaUrl != null) + success = await runner.Upgrade (HomebrewFormulaUrl.ToString ()); + else + success = await runner.Upgrade (Name); + } else + success = await runner.Install (Name); + + if (!success || !Pin) + return success; + + return await runner.Pin (Name); + } + + protected override bool CheckWhetherInstalled () + { + if (String.IsNullOrEmpty (Name)) { + Log.DebugLine ("Homebrew package name not specified, unable to check installation state"); + return false; + } + + cachedVersionOutput = GetPackageVersion (); + return !String.IsNullOrEmpty (cachedVersionOutput); + } + + protected override bool ParseVersion (string version, out Version ver) + { + if (base.ParseVersion (version, out ver)) + return true; + + ver = null; + if (String.IsNullOrEmpty (version)) + return false; + + // Some brew packages (e.g. mingw-w64) have "weird" version formats, we'll handle them here on the + // case-by-case basis. First we should try some general rules to handle the weird versions, checking package + // name should be the very last resort. + int pos = version.IndexOf ('_'); + if (pos > 0) { + // e.g. 6.0.0_1 + string v = version.Replace ('_', '.'); + if (Version.TryParse (v, out ver)) + return true; + + Log.DebugLine ($"Failed to parse {Name} version {version} as {v}"); + } + + return false; + } + + protected override async Task DetermineCurrentVersion () + { + bool result = await base.DetermineCurrentVersion (); + if (result) + return true; + + if (String.IsNullOrEmpty (cachedVersionOutput)) { + cachedVersionOutput = GetPackageVersion (); + if (String.IsNullOrEmpty (cachedVersionOutput)) + return false; + } + + string[] parts = cachedVersionOutput.Split (new [] { ' ' }, 2, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length != 2) { + Log.DebugLine ($"Unable to parse {Name} version from Homebrew output: '{cachedVersionOutput}'"); + return false; + } + + string currentVersion = parts [1]; + if (String.IsNullOrEmpty (currentVersion)) { + Log.DebugLine ($"Missing Homebrew version info for package {Name}"); + return false; + } + + CurrentVersion = currentVersion; + + return true; + } + + protected override async Task AfterDetect (bool installed) + { + if (!installed) + return; + + var runner = new BrewRunner (Context.Instance); + if (InstalledButWrongVersion) { + Log.DebugLine ($"Unpinning {Name} as wrong version installed (may show warnings if package isn't pinned)"); + await runner.UnPin (Name); + return; + } + + if (!Pin) + return; + + Log.DebugLine ($"Pinning {Name} to version {CurrentVersion}"); + await runner.Pin (Name); + } + + string GetPackageVersion () + { + return Utilities.GetStringFromStdout ( + Context.Instance.Tools.BrewPath, + false, // throwOnErrors + true, // trimTrailingWhitespace + true, // quietErrors + "ls", "--versions", "-1", Name + ); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/KnownConditions.cs b/build-tools/xaprepare/xaprepare/Application/KnownConditions.cs new file mode 100644 index 000000000..2240d487a --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/KnownConditions.cs @@ -0,0 +1,22 @@ +namespace Xamarin.Android.Prepare +{ + public enum KnownConditions + { + /// + /// If this condition is set, then Mono upgrade will be performed. It is unset by default because Mono upgrade + /// requires application restart or we may crash. Default: unset. + /// + AllowMonoUpdate, + + /// + /// If set, the outdated or missing programs will be installed. Default: set. + /// + AllowProgramInstallation, + + /// + /// Ignore missing programs and do not signal an error. This is useful in scenarios when we want to update + /// only a single program (e.g. the UpdateMono scenario) but not the rest. Default: unset. + /// + IgnoreMissingPrograms, + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/KnownProperties.cs b/build-tools/xaprepare/xaprepare/Application/KnownProperties.cs new file mode 100644 index 000000000..a7a30a82e --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/KnownProperties.cs @@ -0,0 +1,41 @@ +namespace Xamarin.Android.Prepare +{ + static class KnownProperties + { + public const string AndroidCmakeVersion = "AndroidCmakeVersion"; + public const string AndroidCmakeVersionPath = "AndroidCmakeVersionPath"; + public const string AndroidLatestStableFrameworkVersion = "AndroidLatestStableFrameworkVersion"; + public const string AndroidMxeFullPath = "AndroidMxeFullPath"; + public const string AndroidNdkDirectory = "AndroidNdkDirectory"; + public const string AndroidSdkDirectory = "AndroidSdkDirectory"; + public const string AndroidSupportedHostJitAbis = "AndroidSupportedHostJitAbis"; + public const string AndroidSupportedTargetAotAbis = "AndroidSupportedTargetAotAbis"; + public const string AndroidSupportedTargetJitAbis = "AndroidSupportedTargetJitAbis"; + public const string AndroidToolchainCacheDirectory = "AndroidToolchainCacheDirectory"; + public const string AndroidToolchainDirectory = "AndroidToolchainDirectory"; + public const string AutoProvision = "AutoProvision"; + public const string AutoProvisionUsesSudo = "AutoProvisionUsesSudo"; + public const string Configuration = "Configuration"; + public const string EmulatorVersion = "EmulatorVersion"; + public const string IgnoreMaxMonoVersion = "IgnoreMaxMonoVersion"; + public const string JavaInteropFullPath = "JavaInteropFullPath"; + public const string JavaSdkDirectory = "JavaSdkDirectory"; + public const string LibZipSourceFullPath = "LibZipSourceFullPath"; + public const string ManagedRuntime = "ManagedRuntime"; + public const string MingwCommandPrefix32 = "MingwCommandPrefix32"; + public const string MingwCommandPrefix64 = "MingwCommandPrefix64"; + public const string MingwDependenciesRootDirectory = "MingwDependenciesRootDirectory"; + public const string MingwZlibLibraryName = "MingwZlibLibraryName"; + public const string MingwZlibRootDirectory32 = "MingwZlibRootDirectory32"; + public const string MingwZlibRootDirectory64 = "MingwZlibRootDirectory64"; + public const string MonoRequiredMinimumVersion = "MonoRequiredMinimumVersion"; + public const string MonoSourceFullPath = "MonoSourceFullPath"; + public const string ProductVersion = "ProductVersion"; + public const string RemapAssemblyRefToolExecutable = "RemapAssemblyRefToolExecutable"; + public const string XABuildToolsFolder = "XABuildToolsFolder"; + public const string XABuildToolsVersion = "XABuildToolsVersion"; + public const string XABinRelativeInstallPrefix = "XABinRelativeInstallPrefix"; + public const string XAInstallPrefix = "XAInstallPrefix"; + public const string XAPlatformToolsVersion = "XAPlatformToolsVersion"; + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/LlvmRuntime.Linux.cs b/build-tools/xaprepare/xaprepare/Application/LlvmRuntime.Linux.cs new file mode 100644 index 000000000..1552e3a5a --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/LlvmRuntime.Linux.cs @@ -0,0 +1,9 @@ +using System; + +namespace Xamarin.Android.Prepare +{ + partial class LlvmRuntime + { + const string OSInstallPath = "Linux"; + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/LlvmRuntime.MacOS.cs b/build-tools/xaprepare/xaprepare/Application/LlvmRuntime.MacOS.cs new file mode 100644 index 000000000..23a819c76 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/LlvmRuntime.MacOS.cs @@ -0,0 +1,9 @@ +using System; + +namespace Xamarin.Android.Prepare +{ + partial class LlvmRuntime + { + const string OSInstallPath = "Darwin"; + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/LlvmRuntime.Windows.cs b/build-tools/xaprepare/xaprepare/Application/LlvmRuntime.Windows.cs new file mode 100644 index 000000000..c24fad5cd --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/LlvmRuntime.Windows.cs @@ -0,0 +1,9 @@ +using System; + +namespace Xamarin.Android.Prepare +{ + partial class LlvmRuntime + { + const string OSInstallPath = ""; + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/LlvmRuntime.cs b/build-tools/xaprepare/xaprepare/Application/LlvmRuntime.cs new file mode 100644 index 000000000..44bab0bdc --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/LlvmRuntime.cs @@ -0,0 +1,24 @@ +using System; + +namespace Xamarin.Android.Prepare +{ + partial class LlvmRuntime : Runtime + { + public override string Flavor => "LLVM"; + public bool InstallBinaries { get; protected set; } + + public LlvmRuntime (string name, Func enabledCheck) + : base (name, enabledCheck) + {} + + public override void Init (Context context) + { + InstallBinaries = String.Compare (Name, AbiNames.Llvm.Windows64Bit, StringComparison.Ordinal) != 0; + if (Context.IsLlvmWindowsAbi (Name)) { + ExeSuffix = Configurables.Defaults.WindowsExecutableSuffix; + InstallPath = Configurables.Paths.InstallMSBuildDir; + } else + InstallPath = OSInstallPath; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/Log.Unix.cs b/build-tools/xaprepare/xaprepare/Application/Log.Unix.cs new file mode 100644 index 000000000..1505de2e6 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/Log.Unix.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Concurrent; + +namespace Xamarin.Android.Prepare +{ + partial class Log + { + void InitOS () + {} + + void ShutdownOS () + {} + + void DoConsoleWrite (string message) + { + Console.Write (message); + } + + void DoConsoleWriteLine (string message) + { + Console.WriteLine (message); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/Log.Windows.cs b/build-tools/xaprepare/xaprepare/Application/Log.Windows.cs new file mode 100644 index 000000000..18ef28695 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/Log.Windows.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Concurrent; +using System.Threading; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + partial class Log + { + sealed class LogMessage + { + public bool IsLine; + public string Message; + } + + BlockingCollection lineQueue = new BlockingCollection (new ConcurrentQueue ()); + Task loggerTask; + CancellationTokenSource cts; + CancellationToken token; + + void InitOS () + { + cts = new CancellationTokenSource (); + token = cts.Token; + loggerTask = Task.Run (() => LogWorker (), token); + } + + void ShutdownOS () + { + try { + cts.Cancel (); + loggerTask.Wait (500); + } catch (AggregateException ex) { + if (ex.InnerException is TaskCanceledException) + return; + throw; + } + } + + void DoConsoleWrite (string message) + { + lineQueue.Add (new LogMessage { IsLine = false, Message = message }); + } + + void DoConsoleWriteLine (string message) + { + lineQueue.Add (new LogMessage { IsLine = true, Message = message }); + } + + void LogWorker () + { + while (!token.IsCancellationRequested) { + LogMessage message = lineQueue.Take (); + if (message == null) + continue; + + if (message.IsLine) { + Console.WriteLine (message.Message); + } else { + Console.Write (message.Message); + } + } + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/Log.cs b/build-tools/xaprepare/xaprepare/Application/Log.cs new file mode 100644 index 000000000..24c9c88c9 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/Log.cs @@ -0,0 +1,354 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Text; +using System.Reflection; + +namespace Xamarin.Android.Prepare +{ + partial class Log : IDisposable + { + static readonly char[] lineSplit = new [] { '\n' }; + + static Log instance; + static readonly object writeLock = new object (); + + public const ConsoleColor ErrorColor = ConsoleColor.Red; + public const ConsoleColor ErrorLeadColor = ErrorColor; + public const ConsoleColor ErrorTailColor = ErrorColor; + public const ConsoleColor WarningColor = ConsoleColor.Yellow; + public const ConsoleColor InfoColor = ConsoleColor.Green; + public const ConsoleColor InfoLeadColor = ConsoleColor.White; + public const ConsoleColor InfoTailColor = InfoColor; + public const ConsoleColor MessageColor = ConsoleColor.Gray; + public const ConsoleColor DebugColor = ConsoleColor.DarkGray; + public const ConsoleColor DestinationColor = ConsoleColor.Cyan; + public const ConsoleColor StatusColor = ConsoleColor.Gray; + public const ConsoleColor StatusLeadColor = StatusColor; + public const ConsoleColor StatusTailColor = ConsoleColor.White; + + public const bool DefaultErrorShowSeverity = true; + public const bool DefaultWarningShowSeverity = true; + public const bool DefaultInfoShowSeverity = false; + public const bool DefaultMessageShowSeverity = false; + public const bool DefaultDebugShowSeverity = false; + + const LoggingVerbosity ErrorMinimumVerbosity = LoggingVerbosity.Quiet; + const LoggingVerbosity WarningMinimumVerbosity = LoggingVerbosity.Quiet; + const LoggingVerbosity InfoMinimumVerbosity = LoggingVerbosity.Quiet; + const LoggingVerbosity MessageMinimumVerbosity = LoggingVerbosity.Normal; + const LoggingVerbosity DebugMinimumVerbosity = LoggingVerbosity.Verbose; + + const string ErrorSeverity = " Error: "; + const string WarningSeverity = "Warning: "; + const string InfoSeverity = " Info: "; + const string MessageSeverity = "Message: "; + const string DebugSeverity = " Debug: "; + + static Context ctx; + static LoggingVerbosity Verbosity => ctx != null ? ctx.LoggingVerbosity : Configurables.Defaults.LoggingVerbosity; + + public static Log Instance => instance; + + static bool UseColor => ctx?.UseColor ?? true; + + TextWriter logFileWriter; + bool alreadyDisposed; + Stopwatch watch; + + static Log () + { + instance = new Log (); + } + + public Log (string logFilePath = null) + { + InitOS (); + SetLogFile (logFilePath); + watch = new Stopwatch (); + watch.Start (); + } + + public static void SetContext (Context context) + { + if (context == null) + throw new ArgumentNullException (nameof (context)); + ctx = context; + } + + public void SetLogFile (string logFilePath) + { + CloseLogFile (); + if (String.IsNullOrEmpty (logFilePath)) + return; + + logFileWriter = new StreamWriter (File.Open (logFilePath, FileMode.Create)); + } + + public void Todo (string text) + { + var caller = new StackFrame (1, true); + MethodBase method = caller.GetMethod (); + + var sb = new StringBuilder (); + sb.Append (method.DeclaringType.FullName); + sb.Append ('.'); + sb.Append (method.Name); + + if (!String.IsNullOrEmpty (caller.GetFileName ())) { + int lineNumber = caller.GetFileLineNumber (); + int columnNumber = caller.GetFileColumnNumber (); + bool haveLocation = false; + + if (lineNumber > 0 || columnNumber > 0) { + haveLocation = true; + sb.Append (" at "); + } else + sb.Append (" in "); + + sb.Append (caller.GetFileName ()); + + if (haveLocation) + sb.Append ($"({lineNumber},{columnNumber})"); + } + DoWrite (WriteToConsole, ErrorMinimumVerbosity, String.Empty, "TODO:", ConsoleColor.Red); + + string message = $" {text}\n From: {sb.ToString()}"; + DoWrite (WriteLineToConsole, ErrorMinimumVerbosity, String.Empty, message, ConsoleColor.Blue); + } + + public void Status (string text, ConsoleColor color = StatusColor, bool skipLogFile = false) + { + DoWrite (WriteToConsole, ErrorMinimumVerbosity, String.Empty, text, color, skipLogFile); + } + + public void Status (string lead, string tail, ConsoleColor leadColor = StatusLeadColor, ConsoleColor tailColor = StatusTailColor, bool skipLogFile = false) + { + DoWrite (WriteToConsole, ErrorMinimumVerbosity, String.Empty, lead, leadColor, skipLogFile); + DoWrite (WriteToConsole, ErrorMinimumVerbosity, String.Empty, tail, tailColor, skipLogFile); + } + + public void StatusLine (string line = null, ConsoleColor color = StatusColor, bool skipLogFile = false) + { + DoWrite (WriteLineToConsole, ErrorMinimumVerbosity, String.Empty, line, color, skipLogFile); + } + + public void StatusLine (string lead, string tail, ConsoleColor leadColor = StatusLeadColor, ConsoleColor tailColor = StatusTailColor, bool skipLogFile = false) + { + DoWrite (WriteToConsole, ErrorMinimumVerbosity, String.Empty, lead, leadColor, skipLogFile); + DoWrite (WriteLineToConsole, ErrorMinimumVerbosity, String.Empty, tail, tailColor, skipLogFile); + } + + public void Error (string text, ConsoleColor color = ErrorColor, bool showSeverity = DefaultErrorShowSeverity, string customSeverityName = null) + { + string severity = showSeverity ? (customSeverityName ?? ErrorSeverity) : String.Empty; + DoWrite (WriteToConsole, ErrorMinimumVerbosity, severity, text, color); + } + + public void Error (string lead, string tail, ConsoleColor leadColor = ErrorLeadColor, ConsoleColor tailColor = ErrorTailColor, bool showSeverity = DefaultErrorShowSeverity, string customSeverityName = null) + { + string severity = showSeverity ? (customSeverityName ?? ErrorSeverity) : String.Empty; + DoWrite (WriteToConsole, ErrorMinimumVerbosity, severity, lead, leadColor); + DoWrite (WriteToConsole, ErrorMinimumVerbosity, String.Empty, tail, tailColor); + } + + public void ErrorLine (string line = null, ConsoleColor color = ErrorColor, bool showSeverity = DefaultErrorShowSeverity, string customSeverityName = null) + { + string severity = showSeverity ? (customSeverityName ?? ErrorSeverity) : String.Empty; + DoWrite (WriteLineToConsole, ErrorMinimumVerbosity, severity, line, color); + } + + public void ErrorLine (string lead, string tail, ConsoleColor leadColor = ErrorLeadColor, ConsoleColor tailColor = ErrorTailColor, bool showSeverity = DefaultErrorShowSeverity, string customSeverityName = null) + { + string severity = showSeverity ? (customSeverityName ?? ErrorSeverity) : String.Empty; + DoWrite (WriteToConsole, ErrorMinimumVerbosity, severity, lead, leadColor); + DoWrite (WriteLineToConsole, ErrorMinimumVerbosity, String.Empty, tail, tailColor); + } + + public void Warning (string text, ConsoleColor color = WarningColor, bool showSeverity = DefaultWarningShowSeverity, string customSeverityName = null) + { + string severity = showSeverity ? (customSeverityName ?? WarningSeverity) : String.Empty; + DoWrite (WriteToConsole, WarningMinimumVerbosity, severity, text, color); + } + + public void WarningLine (string line = null, ConsoleColor color = WarningColor, bool showSeverity = DefaultWarningShowSeverity, string customSeverityName = null) + { + string severity = showSeverity ? (customSeverityName ?? WarningSeverity) : String.Empty; + DoWrite (WriteLineToConsole, WarningMinimumVerbosity, severity, line, color); + } + + public void Info (string text, ConsoleColor color = InfoColor, bool showSeverity = DefaultInfoShowSeverity, string customSeverityName = null) + { + string severity = showSeverity ? (customSeverityName ?? InfoSeverity) : String.Empty; + DoWrite (WriteToConsole, InfoMinimumVerbosity, severity, text, color); + } + + public void Info (string lead, string tail, ConsoleColor leadColor = InfoLeadColor, ConsoleColor tailColor = InfoTailColor, bool showSeverity = DefaultInfoShowSeverity, string customSeverityName = null) + { + string severity = showSeverity ? (customSeverityName ?? InfoSeverity) : String.Empty; + DoWrite (WriteToConsole, InfoMinimumVerbosity, severity, lead, leadColor); + DoWrite (WriteToConsole, InfoMinimumVerbosity, String.Empty, tail, tailColor); + } + + public void InfoLine (string line = null, ConsoleColor color = InfoColor, bool showSeverity = DefaultInfoShowSeverity, string customSeverityName = null) + { + string severity = showSeverity ? (customSeverityName ?? InfoSeverity) : String.Empty; + DoWrite (WriteLineToConsole, InfoMinimumVerbosity, severity, line, color); + } + + public void InfoLine (string lead, string tail, ConsoleColor leadColor = InfoLeadColor, ConsoleColor tailColor = InfoTailColor, bool showSeverity = DefaultInfoShowSeverity, string customSeverityName = null) + { + string severity = showSeverity ? (customSeverityName ?? InfoSeverity) : String.Empty; + DoWrite (WriteToConsole, InfoMinimumVerbosity, severity, lead, leadColor); + DoWrite (WriteLineToConsole, InfoMinimumVerbosity, String.Empty, tail, tailColor); + } + + public void Message (string text, ConsoleColor color = MessageColor, bool showSeverity = DefaultMessageShowSeverity, string customSeverityName = null) + { + string severity = showSeverity ? (customSeverityName ?? MessageSeverity) : String.Empty; + DoWrite (WriteToConsole, MessageMinimumVerbosity, severity, text, color); + } + + public void MessageLine (string line = null, ConsoleColor color = MessageColor, bool showSeverity = DefaultMessageShowSeverity, string customSeverityName = null) + { + string severity = showSeverity ? (customSeverityName ?? MessageSeverity) : String.Empty; + DoWrite (WriteLineToConsole, MessageMinimumVerbosity, showSeverity ? MessageSeverity : String.Empty, line, color); + } + + public void Debug (string text, ConsoleColor color = DebugColor, bool showSeverity = DefaultDebugShowSeverity, string customSeverityName = null) + { + string severity = showSeverity ? (customSeverityName ?? DebugSeverity) : String.Empty; + DoWrite (WriteToConsole, DebugMinimumVerbosity, severity, text, color); + } + + public void DebugLine (string line = null, ConsoleColor color = DebugColor, bool showSeverity = DefaultDebugShowSeverity, string customSeverityName = null) + { + string severity = showSeverity ? (customSeverityName ?? DebugSeverity) : String.Empty; + DoWrite (WriteLineToConsole, DebugMinimumVerbosity, severity, line, color); + } + + void DoWrite (Action writer, LoggingVerbosity minimumVerbosity, string prefix, string message, ConsoleColor color, bool skipLogFile = false) + { + writer (minimumVerbosity, $"{prefix}{message}", color, skipLogFile); + } + + void WriteLineToConsole (LoggingVerbosity minimumVerbosity, string message, ConsoleColor color, bool skipLogFile = false) + { + ConsoleColor fg = ConsoleColor.Gray; + try { + lock (writeLock) { + if (UseColor && Verbosity >= minimumVerbosity) { + fg = Console.ForegroundColor; + Console.ForegroundColor = color; + } + + WriteLineToConsole (minimumVerbosity, message, skipLogFile); + } + } finally { + if (UseColor && Verbosity >= minimumVerbosity) { + Console.ForegroundColor = fg; + } + } + } + + void WriteLineToConsole (LoggingVerbosity minimumVerbosity, string message = null, bool skipLogFile = false) + { + lock (writeLock) { + if (Verbosity >= minimumVerbosity) { + Console.WriteLine (message ?? String.Empty); + } + + if (skipLogFile) + return; + LogToFile (message ?? String.Empty, addNewLine: true); + } + } + + void WriteToConsole (LoggingVerbosity minimumVerbosity, string message, ConsoleColor color, bool skipLogFile = false) + { + ConsoleColor fg = ConsoleColor.Gray; + try { + lock (writeLock) { + if (UseColor && Verbosity >= minimumVerbosity) { + fg = Console.ForegroundColor; + Console.ForegroundColor = color; + } + + WriteToConsole (minimumVerbosity, message, skipLogFile); + } + } finally { + if (UseColor && Verbosity >= minimumVerbosity) { + Console.ForegroundColor = fg; + } + } + } + + void WriteToConsole (LoggingVerbosity minimumVerbosity, string message = null, bool skipLogFile = false) + { + if (message == null) + return; + + lock (writeLock) { + if (Verbosity >= minimumVerbosity) { + Console.Write (message); + } + + if (skipLogFile) + return; + LogToFile (message, addNewLine: false); + } + } + + void LogToFile (string message, bool addNewLine) + { + if (logFileWriter == null) + return; + + string stamp = watch.Elapsed.ToString (); + string[] lines = message.Split (lineSplit); + if (lines.Length > 1) { + for (int i = 0; i < lines.Length - 1; i++) { + logFileWriter.WriteLine (FormatMessage (lines [i])); + } + } + logFileWriter.Write (FormatMessage (lines [lines.Length - 1])); + + if (addNewLine) + logFileWriter.WriteLine (); + + string FormatMessage (string msg) + { + return $"[{stamp}] {msg}"; + } + } + + void CloseLogFile () + { + if (logFileWriter == null) + return; + + logFileWriter.Flush (); + logFileWriter.Dispose (); + logFileWriter = null; + } + +#region IDisposable Support + protected void Dispose (bool disposing) + { + if (!alreadyDisposed) { + if (disposing) { + CloseLogFile (); + ShutdownOS (); + } + + alreadyDisposed = true; + } + } + + public void Dispose () + { + Dispose (true); + } +#endregion + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/LoggingVerbosity.cs b/build-tools/xaprepare/xaprepare/Application/LoggingVerbosity.cs new file mode 100644 index 000000000..f1596679c --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/LoggingVerbosity.cs @@ -0,0 +1,11 @@ +namespace Xamarin.Android.Prepare +{ + enum LoggingVerbosity + { + Silent, + Quiet, + Normal, + Verbose, + Diagnostic, + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/MonoCrossRuntime.cs b/build-tools/xaprepare/xaprepare/Application/MonoCrossRuntime.cs new file mode 100644 index 000000000..43f165e81 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/MonoCrossRuntime.cs @@ -0,0 +1,37 @@ +using System; +using System.IO; + +namespace Xamarin.Android.Prepare +{ + class MonoCrossRuntime : MonoRuntime + { + public override string Flavor => "cross compilation"; + + public MonoCrossRuntime (string name, Func enabledCheck) + : base (name, enabledCheck) + {} + + public override void Init (Context context) + { + if (context.IsHostCrossAotAbi (Name)) { + InstallPath = context.OS.Type; // Linux | Darwin | Windows + Strip = "strip"; + StripFlags = "-S"; + } else if (Context.IsWindowsCrossAotAbi (Name)) { + string mingwPrefix; + + if (Context.Is32BitCrossAbi (Name)) + mingwPrefix = Path.Combine (Configurables.Paths.MingwBinDir, Context.Properties.GetRequiredValue (KnownProperties.MingwCommandPrefix32)); + else + mingwPrefix = Path.Combine (Configurables.Paths.MingwBinDir, Context.Properties.GetRequiredValue (KnownProperties.MingwCommandPrefix64)); + + Strip = $"{mingwPrefix}-strip"; + ExeSuffix = Configurables.Defaults.WindowsExecutableSuffix; + } else + throw new InvalidOperationException ($"Unsupported cross compiler abi {Name}"); + + CrossMonoName = Configurables.Defaults.CrossRuntimeNames [Name]; + ExePrefix = Configurables.Defaults.CrossRuntimeExePrefixes [Name]; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/MonoHostRuntime.Linux.cs b/build-tools/xaprepare/xaprepare/Application/MonoHostRuntime.Linux.cs new file mode 100644 index 000000000..3cf408315 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/MonoHostRuntime.Linux.cs @@ -0,0 +1,11 @@ +namespace Xamarin.Android.Prepare +{ + partial class MonoHostRuntime + { + partial void InitOS () + { + if (Context.Is32BitMingwHostAbi (Name)) + CanStripNativeLibrary = false; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/MonoHostRuntime.cs b/build-tools/xaprepare/xaprepare/Application/MonoHostRuntime.cs new file mode 100644 index 000000000..9aa8b8c5e --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/MonoHostRuntime.cs @@ -0,0 +1,56 @@ +using System; +using System.IO; + +namespace Xamarin.Android.Prepare +{ + partial class MonoHostRuntime : MonoRuntime + { + public override string Flavor => "host"; + public bool MinGW { get; } + + public MonoHostRuntime (string name, bool mingw, Func enabledCheck) + : base (name, enabledCheck) + { + MinGW = mingw; + CanStripNativeLibrary = true; + MonoSdksPrefix = "host-"; + } + + public override void Init (Context context) + { + if (MinGW) { + NativeLibraryExtension = Configurables.Defaults.MonoHostMingwRuntimeNativeLibraryExtension; + NativeLibraryDirPrefix = Configurables.Paths.MonoRuntimeHostMingwNativeLibraryPrefix; + } else + NativeLibraryExtension = Configurables.Defaults.NativeLibraryExtension; + + if (Context.IsNativeHostAbi (Name)) { + OutputAotProfilerFilename = Configurables.Defaults.MonoRuntimeOutputAotProfilerFilename; + OutputProfilerFilename = Configurables.Defaults.MonoRuntimeOutputProfilerFilename; + } else { + OutputAotProfilerFilename = String.Empty; + OutputProfilerFilename = String.Empty; + } + OutputMonoBtlsFilename = String.Empty; + OutputMonoPosixHelperFilename = Configurables.Defaults.MonoRuntimeOutputMonoPosixHelperFilename; + + if (Context.IsMingwHostAbi (Name)) { + string prefix; + if (Context.Is32BitMingwHostAbi (Name)) { + prefix = Context.Properties.GetRequiredValue (KnownProperties.MingwCommandPrefix32); + } else if (Context.Is64BitMingwHostAbi (Name)) { + prefix = Context.Properties.GetRequiredValue (KnownProperties.MingwCommandPrefix64); + } else + throw new InvalidOperationException($"MinGW host ABI {Name} is neither 32 nor 64-bit (?!)"); + Strip = Path.Combine (Configurables.Paths.MingwBinDir, $"{prefix}-strip"); + } else { + Strip = "strip"; + StripFlags = "-S"; + } + + InitOS (); + } + + partial void InitOS (); + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/MonoJitRuntime.cs b/build-tools/xaprepare/xaprepare/Application/MonoJitRuntime.cs new file mode 100644 index 000000000..e503bde19 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/MonoJitRuntime.cs @@ -0,0 +1,25 @@ +using System; +using System.IO; + +namespace Xamarin.Android.Prepare +{ + class MonoJitRuntime : MonoRuntime + { + public override string Flavor => "Android JIT"; + + public MonoJitRuntime (string abiName, Func enabledCheck) + : base (abiName, enabledCheck) + {} + + public override void Init (Context context) + { + InstallPath = "lib"; + NativeLibraryExtension = Configurables.Defaults.MonoJitRuntimeNativeLibraryExtension; + OutputAotProfilerFilename = Configurables.Defaults.MonoRuntimeOutputAotProfilerFilename; + OutputMonoBtlsFilename = Configurables.Defaults.MonoRuntimeOutputMonoBtlsFilename; + OutputMonoPosixHelperFilename = Configurables.Defaults.MonoRuntimeOutputMonoPosixHelperFilename; + OutputProfilerFilename = Configurables.Defaults.MonoRuntimeOutputProfilerFilename; + Strip = Path.Combine (Configurables.Paths.AndroidToolchainBinDirectory, $"{Configurables.Defaults.AndroidToolchainPrefixes [Name]}-strip"); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/MonoPkgProgram.MacOS.cs b/build-tools/xaprepare/xaprepare/Application/MonoPkgProgram.MacOS.cs new file mode 100644 index 000000000..7331578a9 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/MonoPkgProgram.MacOS.cs @@ -0,0 +1,48 @@ +using System; +using System.IO; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + class MonoPkgProgram : PkgProgram + { + public MonoPkgProgram (string name, string packageId, Uri packageUrl = null) + : base (name, packageId, packageUrl) + { + ExecutableName = "mono"; + } + + public override bool CanInstall () + { + // We do not want to return `false` here if Mono updates are disallowed - we want Install to be called to + // show the error message as returning `false` here might prevent other programs from updating, and there's + // no good reason for this. + if (!Context.Instance.CheckCondition (KnownConditions.AllowMonoUpdate)) + return base.CanInstall (); + return true; + } + + public override async Task Install () + { + if (Context.Instance.CheckCondition (KnownConditions.AllowMonoUpdate)) + return await base.Install (); + + Log.ErrorLine ($"Mono needs to be updated but updates are disallowed in this scenario. Please run prepare with the '/s:{Scenario_UpdateMono.MyName}' parameter to update Mono."); + return false; + } + + protected override bool CheckWhetherInstalled () + { + IgnoreMaximumVersion = Context.Instance.IgnoreMaxMonoVersion; + return base.CheckWhetherInstalled (); + } + + protected override async Task DetermineCurrentVersion () + { + // Mono is special in that its package does not contain the full version, so we have to get it from the + // --version output instead. + SkipPkgUtilVersionCheck = true; + return await base.DetermineCurrentVersion (); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/MonoRuntime.cs b/build-tools/xaprepare/xaprepare/Application/MonoRuntime.cs new file mode 100644 index 000000000..dce805f70 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/MonoRuntime.cs @@ -0,0 +1,37 @@ +using System; + +namespace Xamarin.Android.Prepare +{ + abstract class MonoRuntime : Runtime + { + string nativeLibraryDirPrefix; + + public bool CanStripNativeLibrary { get; set; } = true; + public string CrossMonoName { get; set; } + public string ExePrefix { get; set; } + public bool IsCrossRuntime { get; set; } + public string NativeLibraryExtension { get; set; } + + /// + /// Optional directory prefix for native library source. This should be a path relative to runtime's library + /// output dir and it exists because MinGW builds will put the runtime .dll in the bin directory instead of in + /// the lib one. + /// + public string NativeLibraryDirPrefix { + get => nativeLibraryDirPrefix ?? String.Empty; + set => nativeLibraryDirPrefix = value; + } + + public string OutputAotProfilerFilename { get; set; } + public string OutputMonoBtlsFilename { get; set; } + public string OutputMonoPosixHelperFilename { get; set; } + public string OutputProfilerFilename { get; set; } + public string OutputRuntimeFilename { get; set; } = Configurables.Defaults.MonoRuntimeOutputFileName; + public string Strip { get; set; } + public string StripFlags { get; set; } + + protected MonoRuntime (string name, Func enabledCheck) + : base (name, enabledCheck) + {} + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/MonoUtilityFile.cs b/build-tools/xaprepare/xaprepare/Application/MonoUtilityFile.cs new file mode 100644 index 000000000..9c49cc387 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/MonoUtilityFile.cs @@ -0,0 +1,27 @@ +using System; +using System.IO; + +namespace Xamarin.Android.Prepare +{ + sealed class MonoUtilityFile + { + public string SourcePath { get; } + public string TargetName { get; } + public bool RemapCecil { get; } + public bool IgnoreDebugInfo { get; } + + public string DebugSymbolsPath => Utilities.GetDebugSymbolsPath (SourcePath); + + public MonoUtilityFile (string name, bool remap = false, string targetName = null, bool ignoreDebugInfo = false) + { + name = name?.Trim (); + if (String.IsNullOrEmpty (name)) + throw new ArgumentException ("must not be null or empty", nameof (name)); + + SourcePath = Path.GetFullPath (Path.Combine (Configurables.Paths.MonoProfileToolsDir, name)); + RemapCecil = remap; + TargetName = targetName; + IgnoreDebugInfo = ignoreDebugInfo; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/PkgProgram.MacOS.cs b/build-tools/xaprepare/xaprepare/Application/PkgProgram.MacOS.cs new file mode 100644 index 000000000..3985420fa --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/PkgProgram.MacOS.cs @@ -0,0 +1,99 @@ +using System; +using System.IO; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + class PkgProgram : Program + { + public override bool NeedsSudoToInstall => false; + public string PackageId { get; } + public Uri PackageUrl { get; set; } + protected bool SkipPkgUtilVersionCheck { get; set; } + + public PkgProgram (string name, string packageId, Uri packageUrl = null) + { + if (String.IsNullOrEmpty (name)) + throw new ArgumentException ("must not be null or empty", nameof (name)); + + if (String.IsNullOrEmpty(packageId)) + throw new ArgumentException ("must not be null or empty", nameof (packageId)); + + Name = name; + PackageId = packageId; + PackageUrl = packageUrl; + } + + public override async Task Install () + { + Context context = Context.Instance; + + if (!context.AutoProvisionUsesSudo) { + Log.ErrorLine ("Installation of macOS packages requires sudo to be enabled (pass `--auto-provision-uses-sudo=yes` to the bootstrapper)"); + return false; + } + + if (PackageUrl == null) { + Log.ErrorLine ($"{Name} is not installed but no URL is provided to download it from. Please make sure to install it before continuing"); + return false; + } + + (bool success, ulong size) = await Utilities.GetDownloadSize (PackageUrl); + if (!success) { + Log.ErrorLine ($"Failed to get download size of {PackageUrl}"); + return false; + } + + DownloadStatus downloadStatus = Utilities.SetupDownloadStatus (context, size, context.InteractiveSession); + Log.StatusLine ($" {context.Characters.Link} {PackageUrl}", ConsoleColor.White); + + string localPath = Path.Combine (context.Properties.GetRequiredValue (KnownProperties.AndroidToolchainCacheDirectory), Path.GetFileName (PackageUrl.LocalPath)); + success = await Utilities.Download (PackageUrl, localPath, downloadStatus); + if (!success) { + Log.ErrorLine ($"Failed to download {PackageUrl}"); + return false; + } + + var runner = new ProcessRunner ("sudo") { + EchoStandardError = true, + EchoStandardOutput = true, + ProcessTimeout = TimeSpan.FromMinutes (10) + }; + + runner.AddArgument ("/usr/sbin/installer"); + runner.AddArgument ("-verbose"); + runner.AddArgument ("-pkg"); + runner.AddQuotedArgument (localPath); + runner.AddArgument ("-target"); + runner.AddArgument ("/"); + + return await Task.Run (() => runner.Run ()); + } + + protected override bool CheckWhetherInstalled () + { + return GetVersion (echoError: false).Result != null; + } + + protected override async Task DetermineCurrentVersion () + { + if (SkipPkgUtilVersionCheck) + return await base.DetermineCurrentVersion (); + + var runner = new PkgutilRunner (Context.Instance); + + Version ver = await GetVersion (echoError: true); + if (ver == null) + return false; + + CurrentVersion = ver.ToString (); + return true; + } + + async Task GetVersion (bool echoError) + { + var runner = new PkgutilRunner (Context.Instance); + return await runner.GetPackageVersion (PackageId, echoError: echoError); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/ProcessRunner.cs b/build-tools/xaprepare/xaprepare/Application/ProcessRunner.cs new file mode 100644 index 000000000..21971a61c --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/ProcessRunner.cs @@ -0,0 +1,318 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Text; +using System.Threading; + +namespace Xamarin.Android.Prepare +{ + class ProcessRunner : AppObject + { + public enum ErrorReasonCode + { + NotExecutedYet, + NoError, + CommandNotFound, + ExecutionTimedOut, + ExitCodeNotZero, + }; + + static readonly TimeSpan DefaultProcessTimeout = TimeSpan.FromMinutes (5); + static readonly TimeSpan DefaultOutputTimeout = TimeSpan.FromSeconds (10); + + sealed class WriterGuard + { + public readonly object WriteLock = new object (); + public readonly TextWriter Writer; + + public WriterGuard (TextWriter writer) + { + Writer = writer; + } + } + + string command; + List arguments; + List stderrSinks; + List stdoutSinks; + Dictionary guardCache; + bool defaultStdoutEchoWrapperAdded; + ProcessStandardStreamWrapper defaultStderrEchoWrapper; + + public string Command => command; + + public string Arguments { + get { + if (arguments == null) + return String.Empty; + return String.Join (" ", arguments); + } + } + + public string FullCommandLine { + get { + string args = Arguments; + if (String.IsNullOrEmpty (args)) + return command; + return $"{command} {args}"; + } + } + + public Dictionary Environment { get; } = new Dictionary (StringComparer.Ordinal); + public int ExitCode { get; private set; } = -1; + public ErrorReasonCode ErrorReason { get; private set; } = ErrorReasonCode.NotExecutedYet; + public bool EchoCmdAndArguments { get; set; } = true; + public bool EchoStandardOutput { get; set; } + public ProcessStandardStreamWrapper.LogLevel EchoStandardOutputLevel { get; set; } = ProcessStandardStreamWrapper.LogLevel.Message; + public bool EchoStandardError { get; set; } + public ProcessStandardStreamWrapper.LogLevel EchoStandardErrorLevel { get; set; } = ProcessStandardStreamWrapper.LogLevel.Error; + public ProcessStandardStreamWrapper StandardOutputEchoWrapper { get; set; } + public ProcessStandardStreamWrapper StandardErrorEchoWrapper { get; set; } + public Encoding StandardOutputEncoding { get; set; } = Encoding.Default; + public Encoding StandardErrorEncoding { get; set; } = Encoding.Default; + public TimeSpan StandardOutputTimeout { get; set; } = DefaultOutputTimeout; + public TimeSpan StandardErrorTimeout { get; set; } = DefaultOutputTimeout; + public TimeSpan ProcessTimeout { get; set; } = DefaultProcessTimeout; + public string WorkingDirectory { get; set; } + public Action StartInfoCallback { get; set; } + + public ProcessRunner (string command, params string[] arguments) + : this (command, false, arguments) + {} + + public ProcessRunner (string command, bool ignoreEmptyArguments, params string[] arguments) + { + if (String.IsNullOrEmpty (command)) + throw new ArgumentException ("must not be null or empty", nameof (command)); + + this.command = command; + if (arguments == null) + return; + + for (int i = 0; i < arguments.Length; i++) { + string argument = arguments [i]?.Trim (); + if (String.IsNullOrEmpty (argument)) { + if (ignoreEmptyArguments) + continue; + throw new InvalidOperationException ($"Argument {i} is null or empty"); + } + + AddQuotedArgument (argument); + } + } + + public ProcessRunner AddArgument (string argument) + { + if (String.IsNullOrEmpty (argument)) + throw new ArgumentException ("must not be null or empty", nameof (argument)); + + AddToList (argument, ref arguments); + return this; + } + + public ProcessRunner AddQuotedArgument (string argument) + { + if (String.IsNullOrEmpty (argument)) + throw new ArgumentException ("must not be null or empty", nameof (argument)); + + return AddArgument (QuoteArgument (argument)); + } + + public static string QuoteArgument (string argument) + { + if (String.IsNullOrEmpty (argument)) + return String.Empty; + + if (argument.IndexOf ('"') >= 0) + argument = argument.Replace ("\"", "\\\""); + + return $"\"{argument}\""; + } + + public ProcessRunner AddStandardErrorSink (TextWriter writer) + { + if (writer == null) + throw new ArgumentNullException (nameof (writer)); + + AddToList (GetGuard (writer), ref stderrSinks); + return this; + } + + public ProcessRunner AddStandardOutputSink (TextWriter writer) + { + if (writer == null) + throw new ArgumentNullException (nameof (writer)); + + AddToList (GetGuard (writer), ref stdoutSinks); + return this; + } + + /// + /// Some programs will not write errors to stderr but we might want to "redirect" them to the error stream for + /// console reporting, this is the purpose of this method. Not that this method bypassess stderr sinks other + /// than the default one (created when is set to true) or the one + /// specified by the caller by setting the . + /// + public void WriteStderrLine (string line) + { + if (StandardErrorEchoWrapper != null) { + StandardErrorEchoWrapper.WriteLine (line); + return; + } + + defaultStderrEchoWrapper?.WriteLine (line ?? String.Empty, stderrSinks); + } + + WriterGuard GetGuard (TextWriter writer) + { + if (guardCache == null) + guardCache = new Dictionary (); + + if (guardCache.TryGetValue (writer, out WriterGuard ret) && ret != null) + return ret; + + ret = new WriterGuard (writer); + guardCache.Add (writer, ret); + return ret; + } + + public bool Run () + { + if (EchoStandardOutput) { + if (StandardOutputEchoWrapper != null) { + AddStandardOutputSink (StandardOutputEchoWrapper); + } else if (!defaultStdoutEchoWrapperAdded) { + AddStandardOutputSink (new ProcessStandardStreamWrapper { LoggingLevel = EchoStandardOutputLevel, CustomSeverityName = "stdout" }); + defaultStdoutEchoWrapperAdded = true; + } + } + + if (EchoStandardError) { + if (StandardErrorEchoWrapper != null) { + AddStandardErrorSink (StandardErrorEchoWrapper); + } else if (defaultStderrEchoWrapper == null) { + defaultStderrEchoWrapper = new ProcessStandardStreamWrapper { LoggingLevel = EchoStandardErrorLevel, CustomSeverityName = "stderr" }; + AddStandardErrorSink (defaultStderrEchoWrapper); + } + } + + ManualResetEventSlim stdout_done = null; + ManualResetEventSlim stderr_done = null; + + if (stderrSinks != null && stderrSinks.Count > 0) + stderr_done = new ManualResetEventSlim (false); + + if (stdoutSinks != null && stdoutSinks.Count > 0) + stdout_done = new ManualResetEventSlim (false); + + var psi = new ProcessStartInfo (command) { + UseShellExecute = false, + CreateNoWindow = true, + WindowStyle = ProcessWindowStyle.Hidden, + RedirectStandardError = stderr_done != null, + RedirectStandardOutput = stdout_done != null, + }; + + if (arguments != null) + psi.Arguments = String.Join (" ", arguments); + + if (!String.IsNullOrEmpty (WorkingDirectory)) + psi.WorkingDirectory = WorkingDirectory; + + if (psi.RedirectStandardError) + StandardErrorEncoding = StandardErrorEncoding; + + if (psi.RedirectStandardOutput) + StandardOutputEncoding = StandardOutputEncoding; + + if (StartInfoCallback != null) + StartInfoCallback (psi); + + var process = new Process { + StartInfo = psi + }; + + if (EchoCmdAndArguments) + Log.DebugLine ($"Running: {FullCommandLine}"); + + try { + process.Start (); + } catch (System.ComponentModel.Win32Exception ex) { + Log.ErrorLine ($"Process failed to start: {ex.Message}"); + Log.DebugLine (ex.ToString ()); + + Log.Todo ("need to check NativeErrorCode to make sure it's command not found"); + ErrorReason = ErrorReasonCode.CommandNotFound; + return false; + } + + if (psi.RedirectStandardError) { + process.ErrorDataReceived += (object sender, DataReceivedEventArgs e) => { + if (e.Data != null) + WriteOutput (e.Data, stderrSinks); + else + stderr_done.Set (); + }; + process.BeginErrorReadLine (); + } + + if (psi.RedirectStandardOutput) { + process.OutputDataReceived += (object sender, DataReceivedEventArgs e) => { + if (e.Data != null) + WriteOutput (e.Data, stdoutSinks); + else + stdout_done.Set (); + }; + process.BeginOutputReadLine (); + } + + bool exited = process.WaitForExit ((int)ProcessTimeout.TotalMilliseconds); + if (!exited) { + Log.ErrorLine ($"Process '{FullCommandLine}' timed out after {ProcessTimeout}"); + ErrorReason = ErrorReasonCode.ExecutionTimedOut; + process.Kill (); + } + + // See: https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.process.waitforexit?view=netframework-4.7.2#System_Diagnostics_Process_WaitForExit) + if (psi.RedirectStandardError || psi.RedirectStandardOutput) + process.WaitForExit (); + + if (stderr_done != null) + stderr_done.Wait (StandardErrorTimeout); + + if (stdout_done != null) + stdout_done.Wait (StandardOutputTimeout); + + ExitCode = process.ExitCode; + if (ExitCode != 0 && ErrorReason == ErrorReasonCode.NotExecutedYet) { + ErrorReason = ErrorReasonCode.ExitCodeNotZero; + return false; + } + + if (exited) + ErrorReason = ErrorReasonCode.NoError; + + return exited; + } + + void WriteOutput (string data, List sinks) + { + foreach (WriterGuard wg in sinks) { + if (wg == null || wg.Writer == null) + continue; + + lock (wg.WriteLock) { + wg.Writer.WriteLine (data); + } + } + } + + void AddToList (T item, ref List list) + { + if (list == null) + list = new List (); + list.Add (item); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/ProcessStandardStreamWrapper.cs b/build-tools/xaprepare/xaprepare/Application/ProcessStandardStreamWrapper.cs new file mode 100644 index 000000000..339da2b94 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/ProcessStandardStreamWrapper.cs @@ -0,0 +1,114 @@ +using System; +using System.IO; +using System.Text; + +namespace Xamarin.Android.Prepare +{ + class ProcessStandardStreamWrapper : TextWriter + { + public enum LogLevel + { + Error, + Warning, + Info, + Message, + Debug, + } + + Log Log = Log.Instance; + string indentString = " | "; + + public bool IndentOutput { get; set; } = true; + public LogLevel LoggingLevel { get; set; } = LogLevel.Debug; + public string CustomSeverityName { get; set; } + + public string IndentString { + get => indentString; + set { + indentString = value ?? String.Empty; + } + } + + public override Encoding Encoding => Encoding.Default; + + public ProcessStandardStreamWrapper () + {} + + public ProcessStandardStreamWrapper (IFormatProvider formatProvider) + : base (formatProvider) + {} + + public override void WriteLine (string value) + { + DoWrite (value, true); + } + + protected virtual string PreprocessMessage (string message, ref bool writeLine) + { + return message; + } + + void DoWrite (string message, bool writeLine) + { + Action writer; + ConsoleColor color; + bool showSeverity; + + message = PreprocessMessage (message, ref writeLine) ?? String.Empty; + switch (LoggingLevel) { + case LogLevel.Error: + color = Log.ErrorColor; + showSeverity = Log.DefaultErrorShowSeverity; + if (writeLine) + writer = Log.ErrorLine; + else + writer = Log.Error; + break; + + case LogLevel.Warning: + color = Log.WarningColor; + showSeverity = Log.DefaultWarningShowSeverity; + if (writeLine) + writer = Log.WarningLine; + else + writer = Log.Warning; + break; + + case LogLevel.Info: + color = Log.InfoColor; + showSeverity = Log.DefaultInfoShowSeverity; + if (writeLine) + writer = Log.InfoLine; + else + writer = Log.Info; + break; + + case LogLevel.Message: + color = Log.MessageColor; + showSeverity = Log.DefaultMessageShowSeverity; + if (writeLine) + writer = Log.MessageLine; + else + writer = Log.Message; + break; + + case LogLevel.Debug: + color = Log.DebugColor; + showSeverity = Log.DefaultDebugShowSeverity; + if (writeLine) + writer = Log.DebugLine; + else + writer = Log.Debug; + break; + + default: + throw new InvalidOperationException ($"Unsupported log level {LoggingLevel}"); + } + + if (IndentOutput) + writer ($"{IndentString}{message}", color, showSeverity, CustomSeverityName); + else + writer (message, color, showSeverity, CustomSeverityName); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/Program.ArchLinux.cs b/build-tools/xaprepare/xaprepare/Application/Program.ArchLinux.cs new file mode 100644 index 000000000..444a50756 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/Program.ArchLinux.cs @@ -0,0 +1,29 @@ +using System; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + class ArchLinuxProgram : LinuxProgram + { + public ArchLinuxProgram (string packageName, string executableName = null) + : base (packageName, executableName) + {} + + protected override bool CheckWhetherInstalled () + { + throw new NotImplementedException (); + } + +#pragma warning disable CS1998 + public override async Task Install () + { + throw new NotImplementedException (); + } +#pragma warning restore CS1998 + + protected override bool DeterminePackageVersion() + { + throw new NotImplementedException(); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/Program.DebianLinux.cs b/build-tools/xaprepare/xaprepare/Application/Program.DebianLinux.cs new file mode 100644 index 000000000..fe0e5e708 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/Program.DebianLinux.cs @@ -0,0 +1,68 @@ +using System; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + class DebianLinuxProgram : LinuxProgram + { + class AptGetStandardStreamWrapper : ProcessStandardStreamWrapper + { + bool interactive = Context.Instance.InteractiveSession; + + public AptGetStandardStreamWrapper () + { + LoggingLevel = ProcessStandardStreamWrapper.LogLevel.Message; + } + + protected override string PreprocessMessage (string message, ref bool writeLine) + { + // apt-get calls `dpkg` which can't be persuaded to not show any progress and it shows the progress by + // writing a line which ends with `0x0D` that is supposed to move the caret to the beginning of the line + // which doesn't work with System.Diagnostics.Process because it strips 0x0D and 0x0A before passing the + // line to us... So in order to keep the display straight we need to reset the cursor position blindly + // here. + Console.CursorLeft = 1; + return message?.TrimEnd (); + } + } + + public DebianLinuxProgram (string packageName, string executableName = null) + : base (packageName, executableName) + {} + + protected override bool CheckWhetherInstalled () + { + string status = Utilities.GetStringFromStdout ("dpkg-query", "-f", "${db:Status-Abbrev}", "-W", PackageName); + return !String.IsNullOrEmpty (status) && status.Length >= 2 && status[1] == 'i'; + } + + public override async Task Install () + { + var runner = new ProcessRunner ("sudo", "apt-get", "-f", "-u", "install", PackageName) { + EchoStandardOutput = true, + EchoStandardError = true, + ProcessTimeout = TimeSpan.FromMinutes (30), + StandardOutputEchoWrapper = new AptGetStandardStreamWrapper (), + }; + + bool failed = await Task.Run (() => !runner.Run ()); + if (failed) { + Log.Error ($"Installation of {PackageName} timed out"); + failed = true; + } + + if (runner.ExitCode != 0) { + Log.Error ($"Installation failed with error code {runner.ExitCode}"); + failed = true; + } + + return !failed; + } + + protected override bool DeterminePackageVersion () + { + CurrentVersion = Utilities.GetStringFromStdout ("dpkg-query", "-f", "${Version}", "-W", PackageName); + return !String.IsNullOrEmpty (CurrentVersion); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/Program.Linux.cs b/build-tools/xaprepare/xaprepare/Application/Program.Linux.cs new file mode 100644 index 000000000..a41bc4cae --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/Program.Linux.cs @@ -0,0 +1,33 @@ +using System; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + abstract class LinuxProgram : Program + { + public override bool NeedsSudoToInstall => true; + + public string PackageName { get; } + + public LinuxProgram (string packageName, string executableName) + { + if (String.IsNullOrEmpty (packageName)) + throw new ArgumentException ("must not be null or empty", nameof (packageName)); + PackageName = packageName; + Name = packageName; + ExecutableName = executableName; + } + + protected override async Task DetermineCurrentVersion () + { + bool ret = await base.DetermineCurrentVersion (); + if (ret) + return true; + + Log.DebugLine ($"Getting {Name} version from package manager"); + return DeterminePackageVersion (); + } + + protected abstract bool DeterminePackageVersion (); + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/Program.cs b/build-tools/xaprepare/xaprepare/Application/Program.cs new file mode 100644 index 000000000..77dde794e --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/Program.cs @@ -0,0 +1,155 @@ +using System; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + abstract class Program : AppObject + { + const string DefaultCurrentVersion = "0.0.0"; + + bool? installed; + + /// + /// Whether the package/program needs sudo to install. + /// + public abstract bool NeedsSudoToInstall { get; } + + /// + /// Program/package name - this may be an actual program name or a system package name. What it is affects + /// version checking, + /// + public string Name { get; set; } + + /// + /// Name of an executable/binary inside a package () which will be used to query the program + /// version. If refers to an executable name (e.g. it coincides with the package name), + /// this property may be left unset. + /// + public string ExecutableName { get; set; } + + /// + /// Minimum supported version of the package/program + /// + public string MinimumVersion { get; set; } + + /// + /// Maximum supported version of the package/program + /// + public string MaximumVersion { get; set; } + + /// + /// Version of the currently installed package/program, if any. If the program isn't detected, this property + /// will be set to the default value of 0.0.0 + /// + public string CurrentVersion { get; protected set; } = DefaultCurrentVersion; + + public bool IgnoreMinimumVersion { get; set; } + public bool IgnoreMaximumVersion { get; set; } + public bool InstalledButWrongVersion { get; private set; } + + public abstract Task Install (); + + public async Task IsInstalled () + { + if (!installed.HasValue) + installed = await Detect (); + return installed.Value; + } + + public virtual bool CanInstall () + { + bool installationAllowed = Context.Instance.CheckCondition (KnownConditions.AllowProgramInstallation); + if (!installationAllowed) + Log.DebugLine ($"{Name} cannot be installed because program installation is disabled"); + + return installationAllowed; + } + + async Task Detect () + { + if (!CheckWhetherInstalled ()) { + await AfterDetect (false); + return false; + } + + bool success = await DetermineCurrentVersion (); + if (!success) { + Log.WarningLine ($"Unable to determine the current version of program '{Name}'"); + } else { + InstalledButWrongVersion = !IsValidVersion (); + } + + if (!success || String.IsNullOrEmpty (CurrentVersion)) { + Log.DebugLine ($"Undetermined current version of program '{Name}', will default to {DefaultCurrentVersion}"); + CurrentVersion = DefaultCurrentVersion; + } + + await AfterDetect (true); + return true; + } + + bool IsValidVersion () + { + if (!ParseVersion (CurrentVersion, out Version curVer)) { + Log.DebugLine ($"Unable to parse {Name} version from the string: {CurrentVersion}"); + Log.DebugLine ($"Version checks disabled for {Name}"); + return true; + } + + if (!ParseVersion (MinimumVersion, out Version minVer)) + minVer = null; + + if (!ParseVersion (MaximumVersion, out Version maxVer)) + maxVer = null; + + if (minVer == null && maxVer == null) + return true; + + if (!IgnoreMinimumVersion && minVer != null && curVer < minVer) { + Log.DebugLine ($"{Name} is too old. Minimum version: {minVer}; Installed version: {curVer}"); + return false; + } + + if (!IgnoreMaximumVersion && maxVer != null && curVer > maxVer) { + Log.DebugLine ($"{Name} is too new. Maximum version: {maxVer}; Installed version: {curVer}"); + return false; + } + + return true; + } + +#pragma warning disable CS1998 + protected virtual async Task AfterDetect (bool installed) + {} +#pragma warning restore CS1998 + + protected virtual bool ParseVersion (string version, out Version ver) + { + return Version.TryParse (version, out ver); + } + +#pragma warning disable CS1998 + protected virtual async Task DetermineCurrentVersion () + { + if (String.IsNullOrEmpty (Name)) { + Log.DebugLine ("Program name not specified, unable to check version"); + return false; + } + + bool success; + string version; + string programName = ExecutableName?.Trim (); + if (String.IsNullOrEmpty (programName)) + programName = Name; + + (success, version) = Utilities.GetProgramVersion (programName); + if (success) + CurrentVersion = version; + + return success; + } +#pragma warning restore CS1998 + + protected abstract bool CheckWhetherInstalled (); + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/ProgramVersionParser.cs b/build-tools/xaprepare/xaprepare/Application/ProgramVersionParser.cs new file mode 100644 index 000000000..70e2ac459 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/ProgramVersionParser.cs @@ -0,0 +1,69 @@ +using System; +using System.IO; + +namespace Xamarin.Android.Prepare +{ + abstract class ProgramVersionParser : AppObject + { + public const string DefaultVersionString = "0.0.0"; + + /// + /// Either a full path or just the name of the program to get the version of. In the latter case, is used to find the program. + /// + public string ProgramName { get; } + + /// + /// Number of the line in the version output on which the version string is found. If 0 (the default) all + /// lines are checked. + /// + public uint VersionOutputLine { get; } + + /// + /// Arguments to pass to in order to obtain the version. Defaults to null + /// since some programs show the version when there are no arguments passed to them. + /// + public string VersionArguments { get; } + + protected ProgramVersionParser (string programName, string versionArguments = null, uint versionOutputLine = 0, Log log = null) + : base (log) + { + if (String.IsNullOrEmpty (programName)) + throw new ArgumentException ("must not be null or empty", nameof (programName)); + ProgramName = programName; + VersionArguments = versionArguments; + VersionOutputLine = versionOutputLine; + } + + public virtual string GetVersion (Context context, string fullProgramPath) + { + if (context == null) + throw new ArgumentNullException (nameof (context)); + + string programPath = String.IsNullOrEmpty (fullProgramPath) ? ProgramName : fullProgramPath; + if (Path.IsPathRooted (ProgramName)) + programPath = ProgramName; + else if (ProgramName.IndexOf (Path.DirectorySeparatorChar) >= 0) { + if (ProgramName [0] == Path.DirectorySeparatorChar) { // Might be the case on Windows + programPath = Path.GetFullPath (ProgramName); + } else { + programPath = Path.Combine (BuildPaths.XamarinAndroidSourceRoot, ProgramName); + } + } else { + programPath = context.OS.Which (ProgramName, required: false); + } + + if (!Utilities.FileExists (programPath)) { + Log.DebugLine ("File {fullProgramPath} does not exist, unable to obtain version"); + return DefaultVersionString; + } + + string versionOutput = Utilities.GetStringFromStdout (programPath, VersionArguments); + Log.DebugLine ($"{programPath} {VersionArguments} returned: {versionOutput}"); + + return ParseVersion (versionOutput); + } + + protected abstract string ParseVersion (string programOutput); + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/Properties.Defaults.cs.in b/build-tools/xaprepare/xaprepare/Application/Properties.Defaults.cs.in new file mode 100644 index 000000000..91d0566c7 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/Properties.Defaults.cs.in @@ -0,0 +1,54 @@ +using System; + +namespace Xamarin.Android.Prepare +{ + partial class Properties + { + void InitDefaults () + { + properties.Add (KnownProperties.AndroidCmakeVersion, StripQuotes ("@AndroidCmakeVersion@")); + properties.Add (KnownProperties.AndroidCmakeVersionPath, StripQuotes (@"@AndroidCmakeVersionPath@")); + properties.Add (KnownProperties.AndroidLatestStableFrameworkVersion, StripQuotes ("@AndroidLatestStableFrameworkVersion@")); + properties.Add (KnownProperties.AndroidMxeFullPath, StripQuotes (@"@AndroidMxeFullPath@")); + properties.Add (KnownProperties.AndroidNdkDirectory, StripQuotes (@"@AndroidNdkDirectory@")); + properties.Add (KnownProperties.AndroidSdkDirectory, StripQuotes (@"@AndroidSdkDirectory@")); + properties.Add (KnownProperties.AndroidSupportedHostJitAbis, StripQuotes ("@AndroidSupportedHostJitAbis@")); + properties.Add (KnownProperties.AndroidSupportedTargetAotAbis, StripQuotes ("@AndroidSupportedTargetAotAbis@")); + properties.Add (KnownProperties.AndroidSupportedTargetJitAbis, StripQuotes ("@AndroidSupportedTargetJitAbis@")); + properties.Add (KnownProperties.AndroidToolchainCacheDirectory, StripQuotes (@"@AndroidToolchainCacheDirectory@")); + properties.Add (KnownProperties.AndroidToolchainDirectory, StripQuotes (@"@AndroidToolchainDirectory@")); + properties.Add (KnownProperties.AutoProvision, StripQuotes ("@AutoProvision@")); + properties.Add (KnownProperties.AutoProvisionUsesSudo, StripQuotes ("@AutoProvisionUsesSudo@")); + properties.Add (KnownProperties.Configuration, StripQuotes ("@Configuration@")); + properties.Add (KnownProperties.EmulatorVersion, StripQuotes ("@EmulatorVersion@")); + properties.Add (KnownProperties.IgnoreMaxMonoVersion, StripQuotes ("@IgnoreMaxMonoVersion@")); + properties.Add (KnownProperties.JavaInteropFullPath, StripQuotes (@"@JavaInteropFullPath@")); + properties.Add (KnownProperties.JavaSdkDirectory, StripQuotes (@"@JavaSdkDirectory@")); + properties.Add (KnownProperties.LibZipSourceFullPath, StripQuotes (@"@LibZipSourceFullPath@")); + properties.Add (KnownProperties.ManagedRuntime, StripQuotes (@"@ManagedRuntime@")); + properties.Add (KnownProperties.MingwCommandPrefix32, StripQuotes ("@MingwCommandPrefix32@")); + properties.Add (KnownProperties.MingwCommandPrefix64, StripQuotes ("@MingwCommandPrefix64@")); + properties.Add (KnownProperties.MingwDependenciesRootDirectory, StripQuotes (@"@MingwDependenciesRootDirectory@")); + properties.Add (KnownProperties.MingwZlibLibraryName, StripQuotes ("@MingwZlibLibraryName@")); + properties.Add (KnownProperties.MingwZlibRootDirectory32, StripQuotes (@"@MingwZlibRootDirectory32@")); + properties.Add (KnownProperties.MingwZlibRootDirectory64, StripQuotes (@"@MingwZlibRootDirectory64@")); + properties.Add (KnownProperties.MonoRequiredMinimumVersion, StripQuotes ("@MonoRequiredMinimumVersion@")); + properties.Add (KnownProperties.MonoSourceFullPath, StripQuotes (@"@MonoSourceFullPath@")); + properties.Add (KnownProperties.ProductVersion, StripQuotes ("@ProductVersion@")); + properties.Add (KnownProperties.RemapAssemblyRefToolExecutable, StripQuotes (@"@RemapAssemblyRefToolExecutable@")); + properties.Add (KnownProperties.XABuildToolsFolder, StripQuotes (@"@XABuildToolsFolder@")); + properties.Add (KnownProperties.XABuildToolsVersion, StripQuotes ("@XABuildToolsVersion@")); + properties.Add (KnownProperties.XABinRelativeInstallPrefix, StripQuotes (@"@XABinRelativeInstallPrefix@")); + properties.Add (KnownProperties.XAInstallPrefix, StripQuotes (@"@XAInstallPrefix@")); + properties.Add (KnownProperties.XAPlatformToolsVersion, StripQuotes ("@XAPlatformToolsVersion@")); + } + + string StripQuotes (string input) + { + if (String.IsNullOrEmpty (input)) + return String.Empty; + + return input.Trim ('"'); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/Properties.cs b/build-tools/xaprepare/xaprepare/Application/Properties.cs new file mode 100644 index 000000000..407f12dc1 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/Properties.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Xamarin.Android.Prepare +{ + partial class Properties : AppObject, IEnumerable > + { + SortedDictionary properties = new SortedDictionary (StringComparer.Ordinal); + + public event EventHandler PropertiesChanged; + + public int Count => properties.Count; + public bool IsDefined (string propertyName) => properties.ContainsKey (EnsureName (propertyName)); + public bool IsEmpty (string propertyName) => IsDefined (propertyName) && properties.TryGetValue (propertyName, out string v) && v != null; + public string this [string name] => GetValue (name); + + public Properties () + { + InitDefaults (); + } + + public string GetValue (string propertyName, string defaultValue = null) + { + if (!properties.TryGetValue (EnsureName (propertyName), out string value)) + return defaultValue; + + return value; + } + + public string GetRequiredValue (string propertyName) + { + string value = GetValue (propertyName); + if (value == null) + throw new InvalidOperationException ($"Required property '{propertyName}' has no defined value"); + return value; + } + + public void Set (string name, string value) + { + if (String.IsNullOrEmpty (name)) + throw new ArgumentException ("must not be null or empty", nameof (name)); + + if (!properties.TryGetValue (name, out string oldValue)) + oldValue = null; + + string newValue = value ?? String.Empty; + properties [name] = newValue; + OnPropertiesChanged (name, newValue, oldValue); + } + + string EnsureName (string propertyName) + { + if (String.IsNullOrEmpty (propertyName)) + throw new InvalidOperationException ("Property name is required"); + return propertyName; + } + + public IEnumerator> GetEnumerator () + { + return properties.GetEnumerator (); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return (properties as IEnumerable).GetEnumerator (); + } + + void OnPropertiesChanged (string name, string newValue, string oldValue) + { + if (PropertiesChanged == null) + return; + + PropertiesChanged (this, new PropertiesChangedEventArgs (name, newValue, oldValue)); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/PropertiesChangedEventArgs.cs b/build-tools/xaprepare/xaprepare/Application/PropertiesChangedEventArgs.cs new file mode 100644 index 000000000..bb5ab245b --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/PropertiesChangedEventArgs.cs @@ -0,0 +1,21 @@ +using System; + +namespace Xamarin.Android.Prepare +{ + class PropertiesChangedEventArgs : EventArgs + { + public string Name { get; } + public string NewValue { get; } + public string OldValue { get; } + + public PropertiesChangedEventArgs (string name, string newValue, string oldValue) + { + if (String.IsNullOrEmpty (name)) + throw new ArgumentException ("must not be null or empty", nameof (name)); + + Name = name; + NewValue = newValue; + OldValue = oldValue; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/RegexProgramVersionParser.cs b/build-tools/xaprepare/xaprepare/Application/RegexProgramVersionParser.cs new file mode 100644 index 000000000..a6a5e8a59 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/RegexProgramVersionParser.cs @@ -0,0 +1,89 @@ +using System; +using System.Text.RegularExpressions; + +namespace Xamarin.Android.Prepare +{ + /// + /// Parses program output looking for a version string. The regular expression must produce a single match which + /// contains a group called Version. The group's value is returned verbatim as the program version. + /// + /// Input from the program is broken up into separate lines and the regular expression is applied to each of them + /// separately. By default (when is 0) all lines are processed, but when + /// is set to any other value only that line is taken into consideration. This is + /// done to make processing less ambiguous and faster. + /// + class RegexProgramVersionParser : ProgramVersionParser + { + const string VersionGroupName = "Version"; + static readonly char[] LineSeparator = new [] { '\n' }; + + Regex rx; + + public RegexProgramVersionParser (string programName, string versionArguments, Regex regex, uint versionOutputLine = 0, Log log = null) + : base (programName, versionArguments, versionOutputLine, log) + { + if (regex == null) + throw new ArgumentNullException (nameof (regex)); + rx = regex; + } + + public RegexProgramVersionParser (string programName, string versionArguments, string regex, uint versionOutputLine = 0, Log log = null) + : base (programName, versionArguments, versionOutputLine, log) + { + if (String.IsNullOrEmpty (regex)) + throw new ArgumentException ("must not be null or empty", nameof (regex)); + + rx = new Regex (regex, RegexOptions.Compiled); + } + + protected override string ParseVersion (string programOutput) + { + string output = programOutput?.Trim (); + if (String.IsNullOrEmpty (output)) { + Log.WarningLine ($"Unable to parse version of {ProgramName} because version output was empty"); + return DefaultVersionString; + } + + string ret = null; + string[] lines = programOutput.Split (LineSeparator); + if (VersionOutputLine > 0) { + if (lines.Length < VersionOutputLine) { + Log.WarningLine ($"Not enough lines in version output of {ProgramName}: version number was supposed to be found on line {VersionOutputLine} but there are only {lines.Length} lines"); + return DefaultVersionString; + } + + if (TryMatch (lines [VersionOutputLine - 1], out ret) && ret != null) { + return ret; + } + + return DefaultVersionString; + } + + foreach (string line in lines) { + if (TryMatch (line, out ret)) + break; + } + + return ret ?? DefaultVersionString; + } + + bool TryMatch (string line, out string version) + { + version = null; + + Match match = rx.Match (line); + if (!match.Success || match.Groups.Count <= 0) { + return false; + } + + foreach (Group group in match.Groups) { + if (String.Compare (group.Name, VersionGroupName, StringComparison.OrdinalIgnoreCase) == 0) { + version = group.Value; + return true; + } + } + + return false; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/RuleGeneratorDelegate.cs b/build-tools/xaprepare/xaprepare/Application/RuleGeneratorDelegate.cs new file mode 100644 index 000000000..5db042e39 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/RuleGeneratorDelegate.cs @@ -0,0 +1,6 @@ +using System.IO; + +namespace Xamarin.Android.Prepare +{ + delegate void RuleGenerator (GeneratedMakeRulesFile file, StreamWriter rulesWriter); +} diff --git a/build-tools/xaprepare/xaprepare/Application/Runtime.cs b/build-tools/xaprepare/xaprepare/Application/Runtime.cs new file mode 100644 index 000000000..a73633896 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/Runtime.cs @@ -0,0 +1,52 @@ +using System; + +namespace Xamarin.Android.Prepare +{ + abstract class Runtime : AppObject + { + Func enabledCheck; + + protected Context Context => Context.Instance; + public bool Enabled => enabledCheck (Context); + public string ExeSuffix { get; protected set; } + + /// + /// Path relative to where the runtime will be placed or + /// null to install the runtime directly under . In + /// each case the runtime will be appended to create full path to the destination + /// directory. + /// + public string InstallPath { get; protected set; } + public string Name { get; protected set; } + + /// + /// Purely cosmetic thing - the kind of runtime (LLVM, JIT etc), for progress reporting. + /// + public abstract string Flavor { get; } + + /// + /// Prefix to used by MonoSDKs to construct the runtime output directory. + /// + protected string MonoSdksPrefix { get; set; } + + /// + /// Host runtimes need a prefix in order to match Mono SDKs output directory name for them. This property is + /// defined in the base class to make code using the runtime definitions simpler. + /// + public string PrefixedName => String.IsNullOrEmpty (MonoSdksPrefix) ? Name : $"{MonoSdksPrefix}{Name}"; + + public Runtime (string name, Func enabledCheck) + { + if (String.IsNullOrEmpty (name)) + throw new ArgumentException ("must not be null or empty", nameof (name)); + + if (enabledCheck == null) + throw new ArgumentNullException (nameof (enabledCheck)); + + this.enabledCheck = enabledCheck; + Name = name; + } + + public abstract void Init (Context context); + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/RuntimeFile.cs b/build-tools/xaprepare/xaprepare/Application/RuntimeFile.cs new file mode 100644 index 000000000..3fa3fedf7 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/RuntimeFile.cs @@ -0,0 +1,56 @@ +using System; + +namespace Xamarin.Android.Prepare +{ + class RuntimeFile + { + /// + /// Path of the source file (one in the Mono SDK output location). Either relative to Mono SDK output path or + /// absolute. + /// + public Func Source { get; } + + /// + /// Destination of the file. Either relative to or absolute. + /// + public Func Destination { get; } + + /// + /// An optional check on whether or not the file should be installed for the particular runtime. + /// + public Func ShouldSkip { get; } + + /// + /// Whether or not to strip the binary of debugging symbols after installation. + /// + public bool Strip { get; } = true; + + /// + /// Type of the file. It's needed in order to determine what tools, if any, we can run on the file once it is + /// installed, if any. + /// + public RuntimeFileType Type { get; } = RuntimeFileType.Other; + + /// + /// If set to true then the file will be copied only once, not per runtime + /// + public bool Shared { get; } + + public bool AlreadyCopied { get; set; } + + public RuntimeFile (Func sourceCreator, Func destinationCreator, Func shouldSkip = null, bool strip = true, RuntimeFileType type = RuntimeFileType.Other, bool shared = false) + { + if (sourceCreator == null) + throw new ArgumentNullException (nameof (sourceCreator)); + if (destinationCreator == null) + throw new ArgumentNullException (nameof (destinationCreator)); + + Source = sourceCreator; + Destination = destinationCreator; + ShouldSkip = shouldSkip; + Strip = strip; + Type = type; + Shared = shared; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/RuntimeFileType.cs b/build-tools/xaprepare/xaprepare/Application/RuntimeFileType.cs new file mode 100644 index 000000000..82b1f63b0 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/RuntimeFileType.cs @@ -0,0 +1,9 @@ +namespace Xamarin.Android.Prepare +{ + enum RuntimeFileType + { + Other, + Binary, + StrippableBinary, + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/Scenario.cs b/build-tools/xaprepare/xaprepare/Application/Scenario.cs new file mode 100644 index 000000000..49c1e257b --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/Scenario.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + abstract partial class Scenario : AppObject + { + public string Name { get; } + public string Description { get; } + public string LogFilePath { get; protected set; } + public List Steps { get; } = new List (); + + protected Scenario (string name, string description, Context context) + { + if (String.IsNullOrEmpty (name)) + throw new ArgumentException ("must not be null or empty", nameof (name)); + if (String.IsNullOrEmpty (description)) + throw new ArgumentException ("must not be null or empty", nameof (description)); + Name = name; + Description = description; + } + + public async Task Run (Context context, Log log = null) + { + Log = log; + foreach (Step step in Steps) { + context.Banner (step.Description ?? step.GetType ().FullName); + + bool success; + Exception stepEx = null; + try { + success = await step.Run (context); + } catch (Exception ex) { + stepEx = ex; + success = false; + } + + if (success) + continue; + + string message = $"Step {step.FailedStep ?? step} failed"; + if (stepEx != null) + throw new InvalidOperationException ($"{message}: {stepEx.Message}", stepEx); + else + throw new InvalidOperationException (message); + } + } + + public void Init (Context context) + { + AddStartSteps (context); + AddSteps (context); + AddEndSteps (context); + } + + protected virtual void AddStartSteps (Context context) + { + // These are steps that have to be executed by all the scenarios + Steps.Add (new Step_DownloadNuGet ()); + } + + protected virtual void AddEndSteps (Context context) + {} + + protected virtual void AddSteps (Context context) + {} + + protected void AddSimpleStep (string description, Func runner) + { + Steps.Add (new SimpleActionStep (description, runner)); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/ScenarioAttribute.cs b/build-tools/xaprepare/xaprepare/Application/ScenarioAttribute.cs new file mode 100644 index 000000000..a23b6720a --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/ScenarioAttribute.cs @@ -0,0 +1,15 @@ +using System; + +namespace Xamarin.Android.Prepare +{ + [AttributeUsage (AttributeTargets.Class)] + class ScenarioAttribute : Attribute + { + public bool IsDefault { get; } + + public ScenarioAttribute (bool isDefault) + { + IsDefault = isDefault; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/ScenarioNoStandardEndSteps.cs b/build-tools/xaprepare/xaprepare/Application/ScenarioNoStandardEndSteps.cs new file mode 100644 index 000000000..4c7e5faef --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/ScenarioNoStandardEndSteps.cs @@ -0,0 +1,20 @@ +namespace Xamarin.Android.Prepare +{ + /// + /// Abstract base class for all scenarios which don't perform the full set of steps and thus should not generate + /// the ProfileAssemblies.projitems file which step requires Mono runtime to be installed first. + /// + abstract class ScenarioNoStandardEndSteps : Scenario + { + protected ScenarioNoStandardEndSteps (string name, string description, Context context) + : base (name, description, context) + {} + + protected override void AddEndSteps (Context context) + { + // We don't want to call the end steps here because they require Mono runtime and assemblies to be installed + // (they generate ProfileAssemblies.projitems which step verifies that the sets of assemblies on disk and in + // xaprepare are identical) + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/SimpleActionStep.cs b/build-tools/xaprepare/xaprepare/Application/SimpleActionStep.cs new file mode 100644 index 000000000..f0b810163 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/SimpleActionStep.cs @@ -0,0 +1,23 @@ +using System; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + class SimpleActionStep : Step + { + Func runner; + +#pragma warning disable CS1998 + protected override async Task Execute (Context context) => runner (context); +#pragma warning restore CS1998 + + public SimpleActionStep (string description, Func stepRunner) + : base (description) + { + if (stepRunner == null) + throw new ArgumentNullException (nameof (stepRunner)); + + runner = stepRunner; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/SizeFormatter.cs b/build-tools/xaprepare/xaprepare/Application/SizeFormatter.cs new file mode 100644 index 000000000..22f2c836e --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/SizeFormatter.cs @@ -0,0 +1,44 @@ +namespace Xamarin.Android.Prepare +{ + class SizeFormatter + { + const ulong UL_KILOBYTE = 1024UL; + const decimal FL_KILOBYTE = 1024.0M; + const ulong UL_MEGABYTE = 1024UL * 1024UL; + const decimal FL_MEGABYTE = 1024.0M * 1024.0M; + const ulong UL_GIGABYTE = 1024UL * 1024UL * 1024UL; + const decimal FL_GIGABYTE = 1024.0M * 1024.0M * 1024.0M; + const ulong UL_TERABYTE = 1024UL * 1024UL * 1024UL * 1024UL; + const decimal FL_TERABYTE = 1024.0M * 1024.0M * 1024.0M * 1024.0M; + const ulong UL_PETABYTE = 1024UL * 1024UL * 1024UL * 1024UL * 1024UL; + const decimal FL_PETABYTE = 1024.0M * 1024.0M * 1024.0M * 1024.0M * 1024.0M; + const ulong UL_EXABYTE = 1024UL * 1024UL * 1024UL * 1024UL * 1024UL * 1024UL; + const decimal FL_EXABYTE = 1024.0M * 1024.0M * 1024.0M * 1024.0M * 1024.0M * 1024.0M; + + public static void FormatBytes (ulong bytes, out decimal value, out string unit) + { + if (bytes < UL_KILOBYTE) { + unit = "B"; + value = (decimal)bytes; + } else if (bytes >= UL_KILOBYTE && bytes < UL_MEGABYTE) { + unit = "KB"; + value = (decimal)bytes / FL_KILOBYTE; + } else if (bytes >= UL_MEGABYTE && bytes < UL_GIGABYTE) { + unit = "MB"; + value = (decimal)bytes / FL_MEGABYTE; + } else if (bytes >= UL_GIGABYTE && bytes < UL_TERABYTE) { + unit = "GB"; + value = (decimal)bytes / FL_GIGABYTE; + } else if (bytes >= UL_TERABYTE && bytes < UL_PETABYTE) { + unit = "TB"; + value = (decimal)bytes / FL_TERABYTE; + } else if (bytes >= UL_PETABYTE && bytes < UL_EXABYTE) { + unit = "PB"; + value = (decimal)bytes / FL_PETABYTE; + } else { + unit = "EB"; + value = (decimal)bytes / FL_EXABYTE; + } + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/Step.cs b/build-tools/xaprepare/xaprepare/Application/Step.cs new file mode 100644 index 000000000..80461bfcd --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/Step.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + abstract class Step : AppObject + { + List failureSteps; + + bool HasFailureSteps => failureSteps != null && failureSteps.Any (s => s != null); + + public string Description { get; } + public Step FailedStep { get; private set; } + public bool ExecutedFailureSteps { get; private set; } + + protected Step (string description) + { + if (String.IsNullOrEmpty (description)) + throw new ArgumentException ("must not be null or empty", nameof (description)); + Description = description; + } + + public void AddFailureStep (Step step) + { + if (step == null) + throw new ArgumentNullException (nameof (step)); + + if (failureSteps == null) + failureSteps = new List (); + + failureSteps.Add (step); + } + + protected abstract Task Execute (Context context); + + public async Task Run (Context context) + { + FailedStep = null; + bool success = await Execute (context); + if (success) + return true; + + if (!HasFailureSteps) + return false; + + foreach (Step step in failureSteps) { + ExecutedFailureSteps = true; + context.Banner (step.Description); + try { + if (!await step.Run (context)) { + FailedStep = step; + return false; + } + } catch { + FailedStep = step; + throw; + } + } + + return true; + } + + protected void TouchStampFile (string filePath) + { + File.WriteAllText (filePath, DateTime.UtcNow.ToString ()); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/StepWithDownloadProgress.cs b/build-tools/xaprepare/xaprepare/Application/StepWithDownloadProgress.cs new file mode 100644 index 000000000..06b09545c --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/StepWithDownloadProgress.cs @@ -0,0 +1,55 @@ +using System; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + abstract class StepWithDownloadProgress : Step + { + protected StepWithDownloadProgress (string description) + : base (description) + {} + + protected async Task Download (Context context, Uri url, string destinationFilePath, string descriptiveName, string fileName, DownloadStatus downloadStatus) + { + bool fancyLogging = context.InteractiveSession; + Log.DebugLine ($"{descriptiveName} URL: {url}"); + if (!context.InteractiveSession) + LogStatus ($"downloading {fileName} ", 4, ConsoleColor.Gray); + + bool success; + Exception downloadEx = null; + try { + Log.DebugLine ("About to start downloading"); + success = await Utilities.Download (url, destinationFilePath, downloadStatus); + } catch (Exception ex) { + Log.DebugLine ($"Caught exception: {ex}"); + downloadEx = ex; + success = false; + } + + Log.Debug ($"success == {success}"); + if (success) + return; + + string message = $"Failed to download {url}"; + + if (downloadEx != null) + throw new InvalidOperationException ($"{message}: {downloadEx.Message}", downloadEx); + throw new InvalidOperationException (message); + } + + protected void LogStatus (string status, int padLeft, ConsoleColor color, bool logLine = true) + { + string message = PadStatus (status, padLeft); + if (logLine) + Log.StatusLine (message, color); + else + Log.Status (message, color); + } + + protected string PadStatus (string status, int padLeft) + { + return status.PadLeft (status.Length + padLeft); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/TPNAttribute.cs b/build-tools/xaprepare/xaprepare/Application/TPNAttribute.cs new file mode 100644 index 000000000..8d67c9484 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/TPNAttribute.cs @@ -0,0 +1,11 @@ +using System; + +namespace Xamarin.Android.Prepare +{ + /// + /// Attribute to decorate (T)hird (P)arty (N)otice classes + /// + [AttributeUsage (AttributeTargets.Class)] + class TPNAttribute : Attribute + {} +} diff --git a/build-tools/xaprepare/xaprepare/Application/TestAssembly.cs b/build-tools/xaprepare/xaprepare/Application/TestAssembly.cs new file mode 100644 index 000000000..3c7db1f58 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/TestAssembly.cs @@ -0,0 +1,21 @@ +using System; + +namespace Xamarin.Android.Prepare +{ + sealed class TestAssembly + { + public string Name { get; } + public TestAssemblyType TestType { get; } + public bool ExcludeDebugSymbols { get; } + + public TestAssembly (string name, TestAssemblyType type, bool excludeDebugSymbols = false) + { + if (String.IsNullOrEmpty (name)) + throw new ArgumentException ("must not be null or empty", nameof (name)); + + Name = name; + TestType = type; + ExcludeDebugSymbols = excludeDebugSymbols; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/TestAssemblyType.cs b/build-tools/xaprepare/xaprepare/Application/TestAssemblyType.cs new file mode 100644 index 000000000..9d23902e4 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/TestAssemblyType.cs @@ -0,0 +1,11 @@ +namespace Xamarin.Android.Prepare +{ + enum TestAssemblyType + { + NUnit, + XUnit, + Reference, + TestRunner, + Satellite, + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/ThirdPartyLicenseType.cs b/build-tools/xaprepare/xaprepare/Application/ThirdPartyLicenseType.cs new file mode 100644 index 000000000..dbd75013e --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/ThirdPartyLicenseType.cs @@ -0,0 +1,9 @@ +namespace Xamarin.Android.Prepare +{ + enum ThirdPartyLicenseType + { + Foundation, + MicrosoftOSS, + Commercial, + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/ThirdPartyNotice.cs b/build-tools/xaprepare/xaprepare/Application/ThirdPartyNotice.cs new file mode 100644 index 000000000..ceb40ae04 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/ThirdPartyNotice.cs @@ -0,0 +1,32 @@ +using System; + +namespace Xamarin.Android.Prepare +{ + abstract class ThirdPartyNotice : AppObject + { + public abstract string LicenseText { get; } + public abstract string LicenseFile { get; } + public abstract string Name { get; } + public abstract Uri SourceUrl { get; } + + public virtual bool Include (bool includeExternalDeps, bool includeBuildDeps) => true; + + public void EnsureValid () + { + bool haveText = !String.IsNullOrEmpty (LicenseText); + bool haveFile = !String.IsNullOrEmpty (LicenseFile); + + if (haveText && haveFile) + throw new InvalidOperationException ($"Only one of LicenseText or LicenseFile properties can be set ({this})"); + + if (!haveText && !haveFile) + throw new InvalidOperationException ($"One of LicenseText or LicenseFile properties must be set ({this})"); + + if (String.IsNullOrEmpty (Name)) + throw new InvalidOperationException ($"The Name property must be set ({this})"); + + if (SourceUrl == null) + throw new InvalidOperationException ($"The SourceUrl property must be set ({this})"); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/ThirdPartyNoticeGroup.cs b/build-tools/xaprepare/xaprepare/Application/ThirdPartyNoticeGroup.cs new file mode 100644 index 000000000..bcceccf3f --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/ThirdPartyNoticeGroup.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; + +namespace Xamarin.Android.Prepare +{ + abstract class ThirdPartyNoticeGroup : ThirdPartyNotice + { + static readonly Uri url = new Uri ("http://not.an/item"); + + public override string LicenseText => "not a license item"; + public override string LicenseFile => null; + public override string Name => "license item group"; + public override Uri SourceUrl => url; + + public override bool Include (bool includeExternalDeps, bool includeBuildDeps) => ShouldInclude (includeExternalDeps, includeBuildDeps); + + public abstract List Notices { get; } + protected abstract bool ShouldInclude (bool includeExternalDeps, bool includeBuildDeps); + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/ThumbTwiddler.Unix.cs b/build-tools/xaprepare/xaprepare/Application/ThumbTwiddler.Unix.cs new file mode 100644 index 000000000..39ee7475c --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/ThumbTwiddler.Unix.cs @@ -0,0 +1,15 @@ +using System; + +namespace Xamarin.Android.Prepare +{ + partial class ThumbTwiddler + { + int ConsoleCursorTop => Console.CursorTop; + int ConsoleCursorLeft => Console.CursorLeft; + + void ConsoleSetCursorPosition (int left, int top) + { + Console.SetCursorPosition (left, top); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/ThumbTwiddler.Windows.cs b/build-tools/xaprepare/xaprepare/Application/ThumbTwiddler.Windows.cs new file mode 100644 index 000000000..97b1b6bae --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/ThumbTwiddler.Windows.cs @@ -0,0 +1,32 @@ +using System; +using System.IO; + +namespace Xamarin.Android.Prepare +{ + partial class ThumbTwiddler + { + int ConsoleCursorTop => SafeConsoleAccess (() => Console.CursorTop); + int ConsoleCursorLeft => SafeConsoleAccess (() => Console.CursorLeft); + + void ConsoleSetCursorPosition (int left, int top) + { + SafeConsoleAccess (() => { + Console.SetCursorPosition (left, top); + return 0; + } + ); + } + + int SafeConsoleAccess (Func code) + { + // Accessing the console may throw an exception of Windows (e.g. when xaprepare runs from within msbuild) + try { + return code (); + } catch (IOException) { + // Ignore + } + + return 0; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/ThumbTwiddler.cs b/build-tools/xaprepare/xaprepare/Application/ThumbTwiddler.cs new file mode 100644 index 000000000..b9c7b40db --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/ThumbTwiddler.cs @@ -0,0 +1,144 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; + +namespace Xamarin.Android.Prepare +{ + partial class ThumbTwiddler : AppObject + { + // yeah, I know, I know :P + static readonly List alternateTwiddlerMessages = new List { + "Working", + "Twiddle de dum, twiddle de de", + "Busy", + "So much stuff to do, so little time", + "'Round and 'round I go", + "Row, row, row your boat", + "1 2 3, left, left", + "And the time slowly passes", + "Tick, tock", + "Yawn... not finished yet", + "The paint dries slowly", + "Slowly rotate the wheels of time", + "What's these few minutes compared to eternity", + "Patience, my friend", + "To wait, or not to wait", + "\"Wait\" is my second name", + "And wait I shall", + "Still busy", + "Working relentlessly", + "Busy, as always", + "Busy, twiddling", + "Still not done", + }; + + readonly Context context; + readonly string[] twiddler; + readonly long twiddleInterval; + readonly int twiddleSteps; + readonly bool dullMode; + readonly bool showElapsedTime; + + int twiddleIndex; + Timer twiddleTimer; + Stopwatch watch; + Random random; + + public ThumbTwiddler (Context context, bool dullMode, bool showElapsedTime) + { + this.context = context ?? throw new ArgumentNullException (nameof (context)); + if (!context.InteractiveSession) + dullMode = true; + + twiddler = context.Characters.Twiddler; + if (twiddler == null || twiddler.Length == 0) { + dullMode = true; + twiddleSteps = 0; + } else { + twiddleSteps = twiddler.Length; + } + + if (!dullMode) + twiddleInterval = 1000 / twiddler.Length; + else { + random = new Random (); + twiddleInterval = 10000; + } + + this.dullMode = dullMode; + this.showElapsedTime = showElapsedTime; + twiddleIndex = 0; + } + + public void Start () + { + if (twiddleTimer != null) + return; + + if (showElapsedTime) { + watch = new Stopwatch (); + watch.Start (); + } + + twiddleTimer = new Timer (Twiddle); + twiddleTimer.Change (250, twiddleInterval); + } + + public void Stop () + { + if (twiddleTimer == null) + return; + + twiddleTimer.Dispose (); + twiddleTimer = null; + if (watch != null) { + WriteTwiddler ($"Elapsed: {GetElapsedTime ()}", showElapsed: false, skipLogFile: false); + } else + WriteTwiddler (" ", showElapsed: false); + } + + void Twiddle (object state) + { + if (dullMode) { + string wittyMessage = alternateTwiddlerMessages[random.Next (0, alternateTwiddlerMessages.Count - 1)]; + Log.StatusLine ($"{wittyMessage}... {GetElapsedTime ()}", ConsoleColor.Gray, skipLogFile: true); + return; + } + + if (twiddleIndex >= twiddleSteps) + twiddleIndex = 0; + + WriteTwiddler (twiddler [twiddleIndex++], watch != null); + } + + void WriteTwiddler (string s, bool showElapsed, bool skipLogFile = true) + { + int curRow = ConsoleCursorTop; + int curColumn = 0; // Console.CursorLeft is curiously unreliable on Mono, the cursor jumps around + + try { + ConsoleSetCursorPosition (0, Console.WindowHeight - 1); + string message; + if (!showElapsed) + message = s; + else + message = $"{s} {GetElapsedTime ()}"; + + Log.Status (message, ConsoleColor.White, skipLogFile: skipLogFile); + } catch { + // ignore + } finally { + ConsoleSetCursorPosition (curColumn, curRow); + } + } + + string GetElapsedTime () + { + if (watch == null) + return String.Empty; + + return watch.Elapsed.ToString (); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/Utilities.Unix.cs b/build-tools/xaprepare/xaprepare/Application/Utilities.Unix.cs new file mode 100644 index 000000000..95caf3be7 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/Utilities.Unix.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.IO; + +using Mono.Unix.Native; + +namespace Xamarin.Android.Prepare +{ + static partial class Utilities + { + public static bool FileExists (string path) + { + if (!File.Exists (path)) + return false; + + Stat sbuf; + int ret = Syscall.stat (path, out sbuf); + if (ret < 0) { + if (Stdlib.GetLastError () == Errno.ENOENT) + Log.Instance.WarningLine ($"File {path} is a dangling symlink. Treating as NOT existing."); + + return false; + } + + return true; + } + + public static IEnumerable FindExecutable (string executable) + { + yield return executable; + } + + // Adapted from CoreFX sources + static bool IsDirectorySeparator (char c) + { + return c == Path.DirectorySeparatorChar; + } + + // Adapted from CoreFX sources + static int GetRootLength(string path) + { + return path.Length > 0 && IsDirectorySeparator (path[0]) ? 1 : 0; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/Utilities.Windows.cs b/build-tools/xaprepare/xaprepare/Application/Utilities.Windows.cs new file mode 100644 index 000000000..4a020b806 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/Utilities.Windows.cs @@ -0,0 +1,130 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Xamarin.Android.Prepare +{ + static partial class Utilities + { + const int DevicePrefixLength = 4; + const int UncPrefixLength = 2; + const int UncExtendedPrefixLength = 8; + + internal const char VolumeSeparatorChar = ':'; + + public static bool FileExists (string filePath) + { + return File.Exists (filePath); + } + + public static IEnumerable FindExecutable (string executable) + { + var pathExt = Environment.GetEnvironmentVariable ("PATHEXT"); + var pathExts = pathExt?.Split (new char [] { Path.PathSeparator }, StringSplitOptions.RemoveEmptyEntries); + + if (pathExts != null) { + foreach (var ext in pathExts) + yield return Path.ChangeExtension (executable, ext); + } + yield return executable; + } + + // Adapted from CoreFX sources + internal static bool IsValidDriveChar(char value) + { + return ((value >= 'A' && value <= 'Z') || (value >= 'a' && value <= 'z')); + } + + // Adapted from CoreFX sources + static bool IsDirectorySeparator (char c) + { + return c == Path.DirectorySeparatorChar || c == Path.AltDirectorySeparatorChar; + } + + // Adapted from CoreFX sources + static bool IsExtended (string path) + { + // While paths like "//?/C:/" will work, they're treated the same as "\\.\" paths. + // Skipping of normalization will *only* occur if back slashes ('\') are used. + return path.Length >= DevicePrefixLength + && path[0] == '\\' + && (path[1] == '\\' || path[1] == '?') + && path[2] == '?' + && path[3] == '\\'; + } + + // Adapted from CoreFX sources + static bool IsDevice (string path) + { + // If the path begins with any two separators is will be recognized and normalized and prepped with + // "\??\" for internal usage correctly. "\??\" is recognized and handled, "/??/" is not. + return IsExtended (path) || + ( + path.Length >= DevicePrefixLength + && IsDirectorySeparator (path[0]) + && IsDirectorySeparator (path[1]) + && (path[2] == '.' || path[2] == '?') + && IsDirectorySeparator (path[3]) + ); + } + + // Adapted from CoreFX sources + static bool IsDeviceUNC (string path) + { + return path.Length >= UncExtendedPrefixLength + && IsDevice (path) + && IsDirectorySeparator (path[7]) + && path[4] == 'U' + && path[5] == 'N' + && path[6] == 'C'; + } + + // Adapted from CoreFX sources + static int GetRootLength (string path) + { + int pathLength = path.Length; + int i = 0; + + bool deviceSyntax = IsDevice (path); + bool deviceUnc = deviceSyntax && IsDeviceUNC(path); + + if ((!deviceSyntax || deviceUnc) && pathLength > 0 && IsDirectorySeparator (path[0])) { + // UNC or simple rooted path (e.g. "\foo", NOT "\\?\C:\foo") + if (deviceUnc || (pathLength > 1 && IsDirectorySeparator (path[1]))) { + // UNC (\\?\UNC\ or \\), scan past server\share + + // Start past the prefix ("\\" or "\\?\UNC\") + i = deviceUnc ? UncExtendedPrefixLength : UncPrefixLength; + + // Skip two separators at most + int n = 2; + while (i < pathLength && (!IsDirectorySeparator (path[i]) || --n > 0)) + i++; + } else { + // Current drive rooted (e.g. "\foo") + i = 1; + } + } else if (deviceSyntax) { + // Device path (e.g. "\\?\.", "\\.\") + // Skip any characters following the prefix that aren't a separator + i = DevicePrefixLength; + while (i < pathLength && !IsDirectorySeparator(path[i])) + i++; + + // If there is another separator take it, as long as we have had at least one + // non-separator after the prefix (e.g. don't take "\\?\\", but take "\\?\a\") + if (i < pathLength && i > DevicePrefixLength && IsDirectorySeparator(path[i])) + i++; + } else if (pathLength >= 2 && path[1] == VolumeSeparatorChar && IsValidDriveChar (path[0])) { + // Valid drive specified path ("C:", "D:", etc.) + i = 2; + + // If the colon is followed by a directory separator, move past it (e.g "C:\") + if (pathLength > 2 && IsDirectorySeparator(path[2])) + i++; + } + + return i; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/Utilities.cs b/build-tools/xaprepare/xaprepare/Application/Utilities.cs new file mode 100644 index 000000000..2b24b4980 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/Utilities.cs @@ -0,0 +1,835 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Net.Http; +using System.Reflection; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + static partial class Utilities + { + static readonly TimeSpan IOExceptionRetryInitialDelay = TimeSpan.FromMilliseconds (250); + static readonly int IOExceptionRetries = 5; + + const string MSBuildPropertyListSeparator = ":"; + + static readonly List tarArchiveExtensions = new List { + ".tar.gz", + ".tar.bz2", + ".tar", + }; + + static readonly Log Log = Log.Instance; + static readonly Dictionary versionCache = new Dictionary (StringComparer.Ordinal); + + public static readonly Encoding UTF8NoBOM = new UTF8Encoding (false); + + public static string ToXamarinAndroidPropertyValue (ICollection coll) + { + if (coll == null) + return String.Empty; + + return String.Join (MSBuildPropertyListSeparator, coll); + } + + public static DownloadStatus SetupDownloadStatus (Context context, ulong totalDownloadSize, bool fancyLogging) + { + var downloadStatus = new DownloadStatus (totalDownloadSize, (DownloadStatus status) => Utilities.LogDownloadProgress (status, fancyLogging)); + + if (!fancyLogging) + downloadStatus.UpdateIntervalMS = 60000; // No need to spam the log too much + return downloadStatus; + } + + public static void LogDownloadProgress (DownloadStatus status, bool fancyLogging) + { + int curRow = Console.CursorTop; + try { + double percentComplete = Math.Round ((status.DownloadedSoFar * 100.0) / status.TotalSize, 2); + + Utilities.FormatSize (status.DownloadedSoFar, out decimal sizeValue, out string sizeUnit); + Utilities.FormatSpeed (status.BytesPerSecond, out decimal speedValue, out string speedUnit); + + string progress = $"{percentComplete}% ({sizeValue} {sizeUnit} at {speedValue} {speedUnit})"; + if (!fancyLogging) { + Log.StatusLine ($"Download progress: {progress}", ConsoleColor.White); + return; + } + Console.SetCursorPosition (0, Console.WindowHeight - 1); + Log.Status ($"{progress} ", ConsoleColor.White); + } catch (Exception ex) { + Log.DebugLine ($"Failed to report progress: {ex.Message}"); + Log.DebugLine (ex.ToString ()); + } finally { + Console.SetCursorPosition (0, curRow); + } + } + + public static void AddAbis (string abis, ref HashSet collection, bool warnAboutDuplicates = true) + { + if (collection == null) + collection = new HashSet (StringComparer.OrdinalIgnoreCase); + + if (abis.Length == 0) + return; + + foreach (string abi in abis.Split (Configurables.Defaults.PropertyListSeparator, StringSplitOptions.RemoveEmptyEntries)) { + if (collection.Contains (abi)) { + if (warnAboutDuplicates) + Log.DebugLine ($"Duplicate ABI name: {abi}"); + continue; + } + + collection.Add (abi); + } + } + + public static async Task VerifyArchive (string fullArchivePath) + { + if (String.IsNullOrEmpty (fullArchivePath)) + throw new ArgumentNullException ("must not be null or empty", nameof (fullArchivePath)); + + if (!FileExists (fullArchivePath)) + return false; + + string sevenZip = Context.Instance.Tools.SevenZipPath; + Log.DebugLine ($"Verifying archive {fullArchivePath}"); + var runner = new SevenZipRunner (Context.Instance); + return await runner.VerifyArchive (fullArchivePath); + } + + public static async Task Unpack (string fullArchivePath, string destinationDirectory, bool cleanDestinatioBeforeUnpacking = false) + { + if (String.IsNullOrEmpty (fullArchivePath)) + throw new ArgumentNullException ("must not be null or empty", nameof (fullArchivePath)); + if (String.IsNullOrEmpty (destinationDirectory)) + throw new ArgumentNullException ("must not be null or empty", nameof (destinationDirectory)); + + if (cleanDestinatioBeforeUnpacking) + DeleteDirectorySilent (destinationDirectory); + + CreateDirectory (destinationDirectory); + + Log.DebugLine ($"Unpacking {fullArchivePath} to directory: {destinationDirectory}"); + bool useTar = false; + foreach (string ext in tarArchiveExtensions) { + if (fullArchivePath.EndsWith (ext, StringComparison.OrdinalIgnoreCase)) { + useTar = true; + break; + } + } + + if (useTar) { + var tar = new TarRunner (Context.Instance); + return await tar.Extract (fullArchivePath, destinationDirectory); + } + + var sevenZip = new SevenZipRunner (Context.Instance); + return await sevenZip.Extract (fullArchivePath, destinationDirectory); + } + + public static string ShortenGitHash (string fullHash) + { + if (String.IsNullOrEmpty (fullHash)) + return String.Empty; + + if (fullHash.Length <= Configurables.Defaults.AbbreviatedHashLength) + return fullHash; + + return fullHash.Substring (0, (int)Configurables.Defaults.AbbreviatedHashLength); + } + + public static string GetDebugSymbolsPath (string executablePath) + { + if (String.IsNullOrEmpty (executablePath)) + throw new ArgumentException ("must not be null or empty", nameof (executablePath)); + + string extension = Context.Instance.DebugFileExtension; + if (String.Compare (".mdb", extension, StringComparison.Ordinal) == 0) + return Path.Combine (executablePath, extension); + + return Path.Combine (Path.GetDirectoryName (executablePath), $"{Path.GetFileNameWithoutExtension (executablePath)}{extension}"); + } + + public static (bool success, string version) GetProgramVersion (string programPath) + { + if (String.IsNullOrEmpty (programPath)) + throw new ArgumentException ("must not be null or empty", nameof (programPath)); + + string version; + if (versionCache.TryGetValue (programPath, out version)) + return (true, version); + + bool fetcherPresent; + + (fetcherPresent, version) = RunVersionFetcher (programPath, programPath); + if (fetcherPresent) + return (IsValidVersion (), version); + + if (programPath.IndexOf (Path.DirectorySeparatorChar) >= 0) { + (fetcherPresent, version) = RunVersionFetcher (Path.GetFileName (programPath), programPath); + if (fetcherPresent) + return (IsValidVersion (), version); + } + + return (false, null); + + bool IsValidVersion () + { + bool valid = !String.IsNullOrEmpty (version); + if (!valid) + return false; + + versionCache [programPath] = version; + return valid; + } + } + + static (bool fetcherPresent, string version) RunVersionFetcher (string program, string programPath) + { + Log.DebugLine ($"Attempting to find version fetcher for: {program} ({programPath})"); + ProgramVersionParser vp; + if (Context.Instance.VersionFetchers.Fetchers.TryGetValue (program, out vp)) { + Log.DebugLine ("Fetcher found"); + string version = vp.GetVersion (Context.Instance, programPath); + Log.DebugLine ($"{program} version: {version}"); + return (true, version); + } + + Log.DebugLine ("Fetcher not found"); + return (false, null); + } + + public static void DeleteDirectory (string directoryPath, bool ignoreErrors = false, bool recurse = true) + { + if (String.IsNullOrEmpty (directoryPath)) + throw new ArgumentException ("must not be null or empty", nameof (directoryPath)); + + if (!Directory.Exists (directoryPath)) + return; + + try { + Log.DebugLine ($"Deleting directory recursively: {directoryPath}"); + DeleteDirectoryWithRetry (directoryPath, recurse); + } catch (Exception ex) { + if (ignoreErrors) { + Log.DebugLine ($"Failed to delete directory: {directoryPath}"); + Log.DebugLine (ex.ToString ()); + return; + } + + throw; + } + } + + public static void DeleteDirectorySilent (string directoryPath, bool recurse = true) + { + if (String.IsNullOrEmpty (directoryPath)) + return; + + DeleteDirectory (directoryPath, ignoreErrors: true, recurse: true); + } + + public static void DeleteFile (string filePath, bool ignoreErrors = false) + { + if (String.IsNullOrEmpty (filePath)) + throw new ArgumentException ("must not be null or empty", nameof (filePath)); + + // In this case we don't care if the file is a dangling symlink or not (which FileExists checks for) - we + // want to remove whatever exists. + if (!File.Exists (filePath)) + return; + + try { + Log.DebugLine ($"Deleting file: {filePath}"); + File.Delete (filePath); + } catch (Exception ex) { + if (ignoreErrors) { + Log.DebugLine ($"Failed to delete file: {filePath}"); + Log.DebugLine (ex.ToString ()); + return; + } + + throw; + } + } + + public static void DeleteFileSilent (string filePath) + { + if (String.IsNullOrEmpty (filePath)) + return; + + DeleteFile (filePath, true); + } + + public static void CreateDirectory (string directoryPath) + { + if (String.IsNullOrEmpty (directoryPath)) + throw new ArgumentException ("must not be null or empty", nameof (directoryPath)); + + if (Directory.Exists (directoryPath)) + return; + + Log.DebugLine ($"Creating directory: {directoryPath}"); + Directory.CreateDirectory (directoryPath); + } + + public static void CopyFilesSimple (IEnumerable sourceFiles, string destinationDirectory, bool overwriteDestinationFile = true) + { + if (sourceFiles == null) + throw new ArgumentNullException (nameof (sourceFiles)); + if (String.IsNullOrEmpty (destinationDirectory)) + throw new ArgumentException ("must not be null or empty", nameof (destinationDirectory)); + + CreateDirectory (destinationDirectory); + foreach (string src in sourceFiles) { + CopyFileInternal (src, destinationDirectory, destinationFileName: null, overwriteDestinationFile: overwriteDestinationFile); + } + } + + public static void CopyFile (string sourceFilePath, string destinationFilePath, bool overwriteDestinationFile = true) + { + if (String.IsNullOrEmpty (sourceFilePath)) + throw new ArgumentException ("must not be null or empty", nameof (sourceFilePath)); + if (String.IsNullOrEmpty (destinationFilePath)) + throw new ArgumentException ("must not be null or empty", nameof (destinationFilePath)); + + CopyFileInternal (sourceFilePath, Path.GetDirectoryName (destinationFilePath), Path.GetFileName (destinationFilePath), overwriteDestinationFile); + } + + public static void CopyFileToDir (string sourceFilePath, string destinationDirectory, string destinationFileName = null, bool overwriteDestinationFile = true) + { + if (String.IsNullOrEmpty (sourceFilePath)) + throw new ArgumentException ("must not be null or empty", nameof (sourceFilePath)); + if (String.IsNullOrEmpty (destinationDirectory)) + throw new ArgumentException ("must not be null or empty", nameof (destinationDirectory)); + + CreateDirectory (destinationDirectory); + CopyFileInternal (sourceFilePath, destinationDirectory, destinationFileName, overwriteDestinationFile); + } + + static void CopyFileInternal (string sourceFilePath, string destinationDirectory, string destinationFileName, bool overwriteDestinationFile) + { + string targetFileName; + if (String.IsNullOrEmpty (destinationFileName)) + targetFileName = Path.GetFileName (sourceFilePath); + else + targetFileName = destinationFileName; + + if (!File.Exists (sourceFilePath)) + throw new InvalidOperationException ($"Location '{sourceFilePath}' does not point to a file"); + + CreateDirectory (destinationDirectory); + string destFile = Path.Combine (destinationDirectory, targetFileName); + Log.DebugLine ($"Copying file '{sourceFilePath}' to '{destFile}' (overwrite destination: {overwriteDestinationFile})"); + File.Copy (sourceFilePath, destFile, overwriteDestinationFile); + } + + public static void FormatSpeed (ulong bytesPerSecond, out decimal value, out string unit) + { + SizeFormatter.FormatBytes (bytesPerSecond, out value, out unit); + value = SignificantDigits (value, 3); + unit += "/s"; + } + + public static void FormatSize (ulong dSize, out decimal value, out string unit) + { + SizeFormatter.FormatBytes (dSize, out value, out unit); + value = SignificantDigits (value, 3); + } + + // Creates numbers with maxDigitCount significant digits or less + static decimal SignificantDigits (decimal number, int maxDigitCount) + { + decimal n = number; + + while (n > 1 && maxDigitCount > 0) { + maxDigitCount--; + n = n / 10; + } + + return Math.Round (number, maxDigitCount); + } + + public static async Task<(bool success, ulong size)> GetDownloadSize (Uri url) + { + (bool success, ulong size, HttpStatusCode _) = await GetDownloadSizeWithStatus (url); + return (success, size); + } + + public static async Task<(bool success, ulong size, HttpStatusCode status)> GetDownloadSizeWithStatus (Uri url) + { + using (var httpClient = new HttpClient ()) { + var req = new HttpRequestMessage (HttpMethod.Head, url); + req.Headers.ConnectionClose = true; + + HttpResponseMessage resp = await httpClient.SendAsync (req, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait (true); + if (!resp.IsSuccessStatusCode || !resp.Content.Headers.ContentLength.HasValue) + return (false, 0, resp.StatusCode); + + return (true, (ulong)resp.Content.Headers.ContentLength.Value, resp.StatusCode); + } + } + + public static async Task Download (Uri url, string targetFile, DownloadStatus status) + { + if (url == null) + throw new ArgumentNullException (nameof (url)); + if (String.IsNullOrEmpty (targetFile)) + throw new ArgumentException ("must not be null or empty", nameof (targetFile)); + if (status == null) + throw new ArgumentNullException (nameof (status)); + + using (var httpClient = new HttpClient ()) { + HttpResponseMessage resp; + try { + Log.DebugLine ("Calling GetAsync"); + resp = await httpClient.GetAsync (url, HttpCompletionOption.ResponseHeadersRead); + Log.DebugLine ("GetAsync finished"); + } catch (Exception ex) { + Log.DebugLine ($"Exception: {ex}"); + throw; + } + + resp.EnsureSuccessStatusCode (); + string dir = Path.GetDirectoryName (targetFile); + CreateDirectory (dir); + using (var fs = File.Open (targetFile, FileMode.Create, FileAccess.Write)) { + using (var webStream = await resp.Content.ReadAsStreamAsync ()) { + status.Start (); + await DoDownload (fs, webStream, status); + } + } + } + + return true; + } + +#pragma warning disable CS1998 + static async Task DoDownload (FileStream fs, Stream webStream, DownloadStatus status) + { + var buf = new byte [16384]; + int nread; + + Log.DebugLine ("Downloading..."); + while ((nread = webStream.Read (buf, 0, buf.Length)) > 0) { + status.Update ((ulong)nread); + fs.Write (buf, 0, nread); + } + + fs.Flush (); + } +#pragma warning restore CS1998 + + public static void MoveDirectoryContentsRecursively (string sourceDir, string destinationDir, bool cleanDestinationDir = true, bool resetFileTimestamp = false) + { + if (String.IsNullOrEmpty (sourceDir)) + throw new ArgumentException ("must not be null or empty", nameof (sourceDir)); + if (String.IsNullOrEmpty (destinationDir)) + throw new ArgumentException ("must not be null or empty", nameof (destinationDir)); + + if (!Path.IsPathRooted (sourceDir)) + sourceDir = Path.GetFullPath (sourceDir); + + if (!Path.IsPathRooted (destinationDir)) + destinationDir = Path.Combine (sourceDir, destinationDir); + + if (sourceDir.Equals (destinationDir, Context.Instance.OS.DefaultStringComparison)) + return; + + if (!Directory.Exists (sourceDir)) + throw new InvalidOperationException ($"Source directory {sourceDir} does not exist"); + + if (cleanDestinationDir && Directory.Exists (destinationDir)) + DeleteDirectoryWithRetry (destinationDir, true); + + Log.DebugLine ($"Moving directory {sourceDir} to {destinationDir}"); + MoveDirectory (sourceDir, destinationDir, resetFileTimestamp); + DeleteDirectoryWithRetry (sourceDir, true); + } + + static void MoveDirectory (string sourceDir, string destinationDir, bool resetFileTimestamp) + { + foreach (string entry in Directory.EnumerateFileSystemEntries (sourceDir)) { + if (entry.Equals (destinationDir, Context.Instance.OS.DefaultStringComparison)) + continue; + + string destination; + if (Directory.Exists (entry)) { + destination = Path.Combine (destinationDir, Path.GetFileName (entry)); + if (File.Exists (destination)) + throw new InvalidOperationException ($"Destination '{destination}' exists and is not a directory"); + Log.DebugLine ($"Creating directory: {destination}"); + CreateDirectory (destination); + MoveDirectory (entry, destination, resetFileTimestamp); + continue; + } + + destination = Path.Combine (destinationDir, Path.GetFileName (entry)); + string dir = Path.GetDirectoryName (destination); + CreateDirectory (dir); + MoveFileWithRetry (entry, destination, resetFileTimestamp); + } + } + + public static void TouchFileWithRetry (string filePath) + { + TouchFileWithRetry (filePath, DateTime.UtcNow); + } + + public static void TouchFileWithRetry (string filePath, DateTime stamp) + { + if (!FileExists (filePath)) + return; + + Log.DebugLine ($"Resetting timestamp of {filePath}"); + TimeSpan delay = IOExceptionRetryInitialDelay; + Exception ex = null; + + for (int i = 0; i < IOExceptionRetries; i++) { + try { + // Don't attempt to set write/access time on linked files. + var destFileInfo = new FileInfo (filePath); + if (!destFileInfo.Attributes.HasFlag (FileAttributes.ReparsePoint)) { + File.SetLastWriteTimeUtc (filePath, stamp); + File.SetLastAccessTimeUtc (filePath, stamp); + } + break; + } catch (Exception e) { + ex = e; + } + WaitAWhile ($"Reset timestamp for {filePath}", i, ref ex, ref delay); + } + + if (ex != null) { + // No need to throw, timestamp reset is not critical + Log.WarningLine ($"Failed to reset timestamp for file {filePath}"); + Log.WarningLine ($"Exception {ex.GetType()} thrown: {ex.Message}"); + } + } + + public static void MoveFileWithRetry (string source, string destination, bool resetFileTimestamp = false) + { + TimeSpan delay = IOExceptionRetryInitialDelay; + Exception ex = null; + + Log.DebugLine ($"Moving '{source}' to '{destination}'"); + for (int i = 0; i < IOExceptionRetries; i++) { + try { + File.Move (source, destination); + break; + } catch (Exception e) { + ex = e; + } + WaitAWhile ($"File move ({source} -> {destination})", i, ref ex, ref delay); + } + + if (ex != null) + throw ex; + + if (!resetFileTimestamp) + return; + + TouchFileWithRetry (destination); + } + + public static void DeleteDirectoryWithRetry (string directoryPath, bool recursive) + { + TimeSpan delay = IOExceptionRetryInitialDelay; + Exception ex = null; + + for (int i = 0; i < IOExceptionRetries; i++) { + try { + Log.DebugLine ($"Deleting directory {directoryPath} (recursively? {recursive})"); + Directory.Delete (directoryPath, recursive); + return; + } catch (IOException e) { + ex = e; + } + WaitAWhile($"Directory {directoryPath} deletion", i, ref ex, ref delay); + } + + if (ex != null) + throw ex; + } + + static void WaitAWhile (string what, int which, ref Exception ex, ref TimeSpan delay) + { + Log.DebugLine ($"{what} attempt no. {which + 1} failed, retrying after delay of {delay}"); + if (ex != null) + Log.DebugLine ($"Failure cause: {ex.Message}"); + Thread.Sleep (delay); + delay = TimeSpan.FromMilliseconds (delay.TotalMilliseconds * 2); + } + + public static List GetTypesWithCustomAttribute (Assembly assembly = null) where T: System.Attribute + { + Assembly asm = assembly ?? typeof (Utilities).Assembly; + + var types = new List (); + foreach (Type type in asm.GetTypes ()) { + if (type.GetCustomAttribute (true) == null) + continue; + + types.Add (type); + } + + return types; + } + + public static StreamWriter OpenStreamWriter (string outputPath, bool append = false, Encoding encoding = null) + { + return new StreamWriter (OpenFileForWrite (outputPath, append), encoding ?? UTF8NoBOM); + } + + public static FileStream OpenFileForWrite (string outputPath, bool append = true) + { + if (String.IsNullOrEmpty (outputPath)) + throw new ArgumentException ("must not be null or empty", nameof (outputPath)); + + string dir = Path.GetDirectoryName (outputPath); + CreateDirectory (dir); + + if (File.Exists (outputPath)) + return File.Open (outputPath, append ? FileMode.Append : FileMode.Truncate, FileAccess.Write); + + return File.Open (outputPath, FileMode.Create, FileAccess.Write); + } + + public static bool RunCommand (string command, params string[] arguments) + { + return RunCommand (command, workingDirectory: null, echoStderr: true, ignoreEmptyArguments: false, arguments: arguments); + } + + public static bool RunCommand (string command, bool ignoreEmptyArguments, params string[] arguments) + { + return RunCommand (command, workingDirectory: null, echoStderr: true, ignoreEmptyArguments: ignoreEmptyArguments, arguments: arguments); + } + + public static bool RunCommand (string command, string workingDirectory, bool ignoreEmptyArguments, params string[] arguments) + { + return RunCommand (command, workingDirectory, echoStderr: true, ignoreEmptyArguments: ignoreEmptyArguments, arguments: arguments); + } + + public static bool RunCommand (string command, string workingDirectory, bool echoStderr, bool ignoreEmptyArguments, params string[] arguments) + { + if (String.IsNullOrEmpty (command)) + throw new ArgumentException ("must not be null or empty", nameof (command)); + + var runner = new ProcessRunner (command, ignoreEmptyArguments, arguments) { + EchoStandardError = echoStderr, + WorkingDirectory = workingDirectory, + }; + + return runner.Run (); + } + + public static string GetStringFromStdout (string command, params string[] arguments) + { + return GetStringFromStdout (command, throwOnErrors: false, trimTrailingWhitespace: true, arguments: arguments); + } + + public static string GetStringFromStdout (string command, bool throwOnErrors, params string[] arguments) + { + return GetStringFromStdout (command, throwOnErrors, trimTrailingWhitespace: true, arguments: arguments); + } + + public static string GetStringFromStdout (string command, bool throwOnErrors, bool trimTrailingWhitespace, params string[] arguments) + { + return GetStringFromStdout (new ProcessRunner (command, arguments), throwOnErrors, trimTrailingWhitespace); + } + + public static string GetStringFromStdout (string command, bool throwOnErrors, bool trimTrailingWhitespace, bool quietErrors, params string[] arguments) + { + return GetStringFromStdout (new ProcessRunner (command, arguments), throwOnErrors, trimTrailingWhitespace, quietErrors); + } + + public static string GetStringFromStdout (ProcessRunner runner, bool throwOnErrors = false, bool trimTrailingWhitespace = true, bool quietErrors = false) + { + using (var sw = new StringWriter ()) { + runner.AddStandardOutputSink (sw); + if (!runner.Run ()) { + LogError ("did not exit cleanly"); + return null; + } + + if (runner.ExitCode != 0) { + LogError ($"failed with exit code {runner.ExitCode}"); + return null; + } + + string ret = sw.ToString (); + if (trimTrailingWhitespace) + return ret.TrimEnd (); + return ret; + } + + void LogError (string message) + { + string msg = $"{runner.FullCommandLine}: {message}"; + if (throwOnErrors) + throw new InvalidOperationException (msg); + if (quietErrors) + Log.DebugLine (msg); + else + Log.ErrorLine (msg); + } + } + + public static string GetRelativePath (string relativeTo, string path) + { + return GetRelativePath (relativeTo, path, Context.Instance.OS.DefaultStringComparison); + } + + // Adapted from CoreFX sources + public static string GetRelativePath (string relativeTo, string path, StringComparison comparisonType) + { + if (String.IsNullOrEmpty (relativeTo)) + throw new ArgumentException ("must not be null or empty", nameof (relativeTo)); + + if (String.IsNullOrEmpty (path)) + throw new ArgumentException ("must not be null or empty", nameof (path)); + + relativeTo = Path.GetFullPath (relativeTo); + path = Path.GetFullPath (path); + + // Need to check if the roots are different- if they are we need to return the "to" path. + if (!AreRootsEqual (relativeTo, path, comparisonType)) + return path; + + int commonLength = GetCommonPathLength (relativeTo, path, ignoreCase: comparisonType == StringComparison.OrdinalIgnoreCase); + + // If there is nothing in common they can't share the same root, return the "to" path as is. + if (commonLength == 0) + return path; + + // Trailing separators aren't significant for comparison + int relativeToLength = relativeTo.Length; + if (EndsInDirectorySeparator (relativeTo)) + relativeToLength--; + + bool pathEndsInSeparator = EndsInDirectorySeparator (path); + int pathLength = path.Length; + if (pathEndsInSeparator) + pathLength--; + + // If we have effectively the same path, return "." + if (relativeToLength == pathLength && commonLength >= relativeToLength) + return "."; + + // We have the same root, we need to calculate the difference now using the + // common Length and Segment count past the length. + // + // Some examples: + // + // C:\Foo C:\Bar L3, S1 -> ..\Bar + // C:\Foo C:\Foo\Bar L6, S0 -> Bar + // C:\Foo\Bar C:\Bar\Bar L3, S2 -> ..\..\Bar\Bar + // C:\Foo\Foo C:\Foo\Bar L7, S1 -> ..\Bar + + var sb = new StringBuilder (Math.Max (relativeTo.Length, path.Length)); + + // Add parent segments for segments past the common on the "from" path + if (commonLength < relativeToLength) { + sb.Append (".."); + + for (int i = commonLength + 1; i < relativeToLength; i++) { + if (IsDirectorySeparator (relativeTo[i])) { + sb.Append (Path.DirectorySeparatorChar); + sb.Append (".."); + } + } + } else if (IsDirectorySeparator (path[commonLength])) { + // No parent segments and we need to eat the initial separator + // (C:\Foo C:\Foo\Bar case) + commonLength++; + } + + // Now add the rest of the "to" path, adding back the trailing separator + int differenceLength = pathLength - commonLength; + if (pathEndsInSeparator) + differenceLength++; + + if (differenceLength > 0) { + if (sb.Length > 0) { + sb.Append (Path.DirectorySeparatorChar); + } + + sb.Append(path, commonLength, differenceLength); + } + + return sb.ToString (); + } + + // Adapted from CoreFX sources + static bool AreRootsEqual (string first, string second, StringComparison comparisonType) + { + int firstRootLength = GetRootLength (first); + int secondRootLength = GetRootLength (second); + + return firstRootLength == secondRootLength + && String.Compare ( + strA: first, + indexA: 0, + strB: second, + indexB: 0, + length: firstRootLength, + comparisonType: comparisonType) == 0; + } + + // Adapted from CoreFX sources + static int GetCommonPathLength (string first, string second, bool ignoreCase) + { + int commonChars = EqualStartingCharacterCount (first, second, ignoreCase: ignoreCase); + + // If nothing matches + if (commonChars == 0) + return commonChars; + + // Or we're a full string and equal length or match to a separator + if (commonChars == first.Length && (commonChars == second.Length || IsDirectorySeparator (second[commonChars]))) + return commonChars; + + if (commonChars == second.Length && IsDirectorySeparator (first[commonChars])) + return commonChars; + + // It's possible we matched somewhere in the middle of a segment e.g. C:\Foodie and C:\Foobar. + while (commonChars > 0 && !IsDirectorySeparator (first[commonChars - 1])) + commonChars--; + + return commonChars; + } + + // Adapted from CoreFX sources + static unsafe int EqualStartingCharacterCount (string first, string second, bool ignoreCase) + { + if (String.IsNullOrEmpty (first) || string.IsNullOrEmpty (second)) + return 0; + + int commonChars = 0; + fixed (char* f = first) { + fixed (char* s = second) { + char* l = f; + char* r = s; + char* leftEnd = l + first.Length; + char* rightEnd = r + second.Length; + + while (l != leftEnd && r != rightEnd && (*l == *r || (ignoreCase && char.ToUpperInvariant ((*l)) == char.ToUpperInvariant ((*r))))) { + commonChars++; + l++; + r++; + } + } + } + + return commonChars; + } + + // Adapted from CoreFX sources + static bool EndsInDirectorySeparator (string path) => path.Length > 0 && IsDirectorySeparator (path[path.Length - 1]); + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/VersionFetchers.cs b/build-tools/xaprepare/xaprepare/Application/VersionFetchers.cs new file mode 100644 index 000000000..9cbb14969 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/VersionFetchers.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; + +namespace Xamarin.Android.Prepare +{ + class VersionFetchers + { + const string StandardVersionRegex = "(?(\\d+)\\.(\\d+\\.?)(\\d+\\.)?(\\d+)?)"; + const string JavaVersionRegex = "(?(\\d+)\\.(\\d+)\\.(\\d+)_(\\d+))"; + + static readonly Regex StandardVersionAtEOL = MakeRegex ($" {StandardVersionRegex}"); // $ isn't needed, we match + // line by line + static readonly Regex StandardVersionAtBOL = MakeRegex ($"^{StandardVersionRegex}"); + + public readonly Dictionary Fetchers = new Dictionary (Context.Instance.OS.DefaultStringComparer) { + {"7z", "--help", MakeRegex ($"Version {StandardVersionRegex}"), 3}, + {"ant", "-version", MakeRegex ($"version {StandardVersionRegex}"), 1}, + {"autoconf", "--version", StandardVersionAtEOL, 1}, + {"automake", "--version", StandardVersionAtEOL, 1}, + {"brew", "--version", MakeRegex ($"^Homebrew {StandardVersionRegex}"), 1}, + {"cmake", "--version", StandardVersionAtEOL, 1}, + {"curl", "--version", MakeRegex ($"^curl {StandardVersionRegex}"), 1}, + {"g++", "--version", StandardVersionAtEOL, 1}, + {"gcc", "--version", StandardVersionAtEOL, 1}, + {"git", "--version", StandardVersionAtEOL, 1}, + {"gmake", "--version", StandardVersionAtEOL, 1}, + {"java", "-version", MakeRegex ($" \"{JavaVersionRegex}\"$"), 1}, + {"javac", "-version", MakeRegex ($"{JavaVersionRegex}$"), 1}, + {"libtool", "--version", StandardVersionAtEOL, 1}, + {"make", "--version", StandardVersionAtEOL, 1}, + {"mono", "--version", MakeRegex ($"version {StandardVersionRegex}"), 1}, + {"ninja", "--version", MakeRegex (StandardVersionRegex), 1}, + {"sqlite3", "--version", StandardVersionAtBOL, 1}, + }; + + static Regex MakeRegex (string regex) + { + return new Regex (regex, RegexOptions.Compiled | RegexOptions.Singleline); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/BuildInfo.cs.in b/build-tools/xaprepare/xaprepare/BuildInfo.cs.in new file mode 100644 index 000000000..9b62d6b91 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/BuildInfo.cs.in @@ -0,0 +1,13 @@ +namespace Xamarin.Android.Prepare +{ + static class BuildPaths + { + public const string XamarinAndroidSourceRoot = @"@XA_SOURCE_ROOT@"; + public const string XAPrepareSourceDir = @"@XA_PREPARE_SOURCE@"; + } + + partial class BuildInfo + { + public const string XAVersion = "@XA_PRODUCT_VERSION@"; + } +} diff --git a/build-tools/xaprepare/xaprepare/ConfigAndData/AbiNames.cs b/build-tools/xaprepare/xaprepare/ConfigAndData/AbiNames.cs new file mode 100644 index 000000000..21ecfde62 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ConfigAndData/AbiNames.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections.Generic; + +namespace Xamarin.Android.Prepare +{ + static class AbiNames + { + static HashSet allHostAbis; + + public static class TargetJit + { + public const string AndroidArmV7a = "armeabi-v7a"; + public const string AndroidArmV8a = "arm64-v8a"; + public const string AndroidArm64 = AndroidArmV8a; + public const string AndroidX86 = "x86"; + public const string AndroidX86_64 = "x86_64"; + } + + public static class TargetAot + { + public const string ArmV7a = "armeabi-v7a"; + public const string WinArmV7a = "win-armeabi-v7a"; + public const string ArmV8a = "arm64"; + public const string Arm64 = ArmV8a; + public const string WinArmV8a = "win-arm64"; + public const string WinArm64 = WinArmV8a; + public const string X86 = "x86"; + public const string WinX86 = "win-x86"; + public const string X86_64 = "x86_64"; + public const string WinX86_64 = "win-x86_64"; + } + + public static class HostJit + { + public const string Linux = "Linux"; + public const string Darwin = "Darwin"; + public const string Win32 = "mxe-Win32"; + public const string Win64 = "mxe-Win64"; + } + + public static class CrossAot + { + public static readonly string ArmV7a = "cross-arm"; + public static readonly string WinArmV7a = $"{ArmV7a}-win"; + public static readonly string ArmV8a = "cross-arm64"; + public static readonly string Arm64 = ArmV8a; + public static readonly string WinArmV8a = $"{ArmV8a}-win"; + public static readonly string WinArm64 = WinArmV8a; + public static readonly string X86 = "cross-x86"; + public static readonly string WinX86 = $"{X86}-win"; + public static readonly string X86_64 = "cross-x86_64"; + public static readonly string WinX86_64 = $"{X86_64}-win"; + } + + public static class Llvm + { + public const string Host32Bit = "llvm32"; + public const string Host64Bit = "llvm64"; + public const string Windows32Bit = "llvmwin32"; + public const string Windows64Bit = "llvmwin64"; + } + + public static HashSet AllHostAbis { + get { + if (allHostAbis == null) + allHostAbis = Abi.GetHostAbis (includeAllHostOSes: false); + return allHostAbis; + } + } + + public static readonly HashSet AllNativeHostAbis = Abi.GetHostAbis (osType: Abi.OS.NotWindows); + + public static readonly HashSet AllAotAbis = Abi.GetHostAotAbis (); + + public static readonly HashSet AllLlvmHostAbis = Abi.GetLlvmAbis (); + public static readonly HashSet AllLlvmWindowsAbis = Abi.GetLlvmAbis (osType: Abi.OS.Windows); + public static readonly HashSet All32BitLlvmAbis = Abi.GetLlvmAbis (bitness: Abi.Bitness.ThirtyTwo); + public static readonly HashSet All64BitLlvmAbis = Abi.GetLlvmAbis (bitness: Abi.Bitness.SixtyFour); + + public static readonly HashSet AllJitAbis = Abi.GetTargetJitAbis (); + public static readonly HashSet All32BitTargetJitAbis = Abi.GetTargetJitAbis (Abi.Bitness.ThirtyTwo); + public static readonly HashSet All64BitTargetJitAbis = Abi.GetTargetJitAbis (Abi.Bitness.SixtyFour); + + public static readonly HashSet AllHostAotAbis = Abi.GetHostAotAbis (osType: Abi.OS.NotWindows); + public static readonly HashSet All32BitHostAotAbis = Abi.GetHostAotAbis (osType: Abi.OS.NotWindows, bitness: Abi.Bitness.ThirtyTwo); + public static readonly HashSet All64BitHostAotAbis = Abi.GetHostAotAbis (osType: Abi.OS.NotWindows, bitness: Abi.Bitness.SixtyFour); + + public static readonly HashSet AllWindowsAotAbis = Abi.GetHostAotAbis (osType: Abi.OS.Windows); + public static readonly HashSet All32BitWindowsAotAbis = Abi.GetHostAotAbis (osType: Abi.OS.Windows, bitness: Abi.Bitness.ThirtyTwo); + public static readonly HashSet All64BitWindowsAotAbis = Abi.GetHostAotAbis (osType: Abi.OS.Windows, bitness: Abi.Bitness.SixtyFour); + + public static readonly HashSet AllTargetAotAbis = Abi.GetHostAotAbis (); + public static readonly HashSet All64BitTargetAotAbis = Abi.GetHostAotAbis (osType: Abi.OS.Any, bitness: Abi.Bitness.SixtyFour); + public static readonly HashSet All32BitTargetAotAbis = Abi.GetHostAotAbis (osType: Abi.OS.Any, bitness: Abi.Bitness.ThirtyTwo); + + public static readonly HashSet AllCrossHostAotAbis = Abi.GetCrossAbis (osType: Abi.OS.NotWindows); + public static readonly HashSet AllCrossWindowsAotAbis = Abi.GetCrossAbis (osType: Abi.OS.Windows); + public static readonly HashSet All64BitCrossAotAbis = Abi.GetCrossAbis (bitness: Abi.Bitness.SixtyFour); + public static readonly HashSet All32BitCrossAotAbis = Abi.GetCrossAbis (bitness: Abi.Bitness.ThirtyTwo); + + public static readonly HashSet AllMingwHostAbis = Abi.GetHostAbis (osType: Abi.OS.Windows); + public static readonly HashSet All32BitMingwHostAbis = Abi.GetHostAbis (osType: Abi.OS.Windows, bitness: Abi.Bitness.ThirtyTwo); + public static readonly HashSet All64BitMingwHostAbis = Abi.GetHostAbis (osType: Abi.OS.Windows, bitness: Abi.Bitness.SixtyFour); + + public static void LogAllNames (Context context) + { + if (context.LoggingVerbosity < LoggingVerbosity.Verbose) + return; + + Log.Instance.DebugLine ("All defined ABI names:"); + + LogAbis (context, "AllHostAbis", AllHostAbis); + LogAbis (context, "AllNativeHostAbis", AllNativeHostAbis); + + LogAbis (context, "AllLlvmHostAbis", AllLlvmHostAbis); + LogAbis (context, "AllLlvmWindowsAbis", AllLlvmWindowsAbis); + LogAbis (context, "All32BitLlvmAbis", All32BitLlvmAbis); + LogAbis (context, "All64BitLlvmAbis", All64BitLlvmAbis); + + LogAbis (context, "AllJitAbis", AllJitAbis); + LogAbis (context, "All32BitTargetJitAbis", All32BitTargetJitAbis); + LogAbis (context, "All64BitTargetJitAbis", All64BitTargetJitAbis); + + LogAbis (context, "AllHostAotAbis", AllHostAotAbis); + LogAbis (context, "All32BitHostAotAbis", All32BitHostAotAbis); + LogAbis (context, "All64BitHostAotAbis", All64BitHostAotAbis); + + LogAbis (context, "AllWindowsAotAbis", AllWindowsAotAbis); + LogAbis (context, "All32BitWindowsAotAbis", All32BitWindowsAotAbis); + LogAbis (context, "All64BitWindowsAotAbis", All64BitWindowsAotAbis); + LogAbis (context, "All64BitTargetAotAbis", All64BitTargetAotAbis); + LogAbis (context, "All32BitTargetAotAbis", All32BitTargetAotAbis); + + LogAbis (context, "AllCrossHostAotAbis", AllCrossHostAotAbis); + LogAbis (context, "AllCrossWindowsAotAbis", AllCrossWindowsAotAbis); + LogAbis (context, "All64BitCrossAotAbis", All64BitCrossAotAbis); + LogAbis (context, "All32BitCrossAotAbis", All32BitCrossAotAbis); + + LogAbis (context, "AllMingwHostAbis", AllMingwHostAbis); + LogAbis (context, "All32BitMingwHostAbis", All32BitMingwHostAbis); + LogAbis (context, "All64BitMingwHostAbis", All64BitMingwHostAbis); + } + + static void LogAbis (Context context, string name, HashSet abis) + { + Log.Instance.DebugLine ($" {context.Characters.Bullet} {name}"); + foreach (string abi in abis) { + Log.Instance.DebugLine ($" {abi}"); + } + Log.Instance.DebugLine (); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/ConfigAndData/BuildAndroidPlatforms.cs b/build-tools/xaprepare/xaprepare/ConfigAndData/BuildAndroidPlatforms.cs new file mode 100644 index 000000000..b7701e643 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ConfigAndData/BuildAndroidPlatforms.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; + +namespace Xamarin.Android.Prepare +{ + class BuildAndroidPlatforms + { + public const string AndroidNdkVersion = "19c"; + + public static readonly List AllPlatforms = new List { + new AndroidPlatform (apiLevel: 1, platformID: "1"), + new AndroidPlatform (apiLevel: 2, platformID: "2"), + new AndroidPlatform (apiLevel: 3, platformID: "3"), + new AndroidPlatform (apiLevel: 4, platformID: "4"), + new AndroidPlatform (apiLevel: 5, platformID: "5"), + new AndroidPlatform (apiLevel: 6, platformID: "6"), + new AndroidPlatform (apiLevel: 7, platformID: "7"), + new AndroidPlatform (apiLevel: 8, platformID: "8"), + new AndroidPlatform (apiLevel: 9, platformID: "9"), + new AndroidPlatform (apiLevel: 10, platformID: "10"), + new AndroidPlatform (apiLevel: 11, platformID: "11"), + new AndroidPlatform (apiLevel: 12, platformID: "12"), + new AndroidPlatform (apiLevel: 13, platformID: "13"), + new AndroidPlatform (apiLevel: 14, platformID: "14"), + new AndroidPlatform (apiLevel: 15, platformID: "15"), + new AndroidPlatform (apiLevel: 16, platformID: "16"), + new AndroidPlatform (apiLevel: 17, platformID: "17"), + new AndroidPlatform (apiLevel: 18, platformID: "18"), + new AndroidPlatform (apiLevel: 19, platformID: "19", framework: "v4.4"), + new AndroidPlatform (apiLevel: 20, platformID: "20", framework: "v4.4.87"), + new AndroidPlatform (apiLevel: 21, platformID: "21", framework: "v5.0"), + new AndroidPlatform (apiLevel: 22, platformID: "22", framework: "v5.1"), + new AndroidPlatform (apiLevel: 23, platformID: "23", framework: "v6.0"), + new AndroidPlatform (apiLevel: 24, platformID: "24", framework: "v7.0"), + new AndroidPlatform (apiLevel: 25, platformID: "25", framework: "v7.1"), + new AndroidPlatform (apiLevel: 26, platformID: "26", framework: "v8.0"), + new AndroidPlatform (apiLevel: 27, platformID: "27", framework: "v8.1"), + new AndroidPlatform (apiLevel: 28, platformID: "28", framework: "v9.0"), + new AndroidPlatform (apiLevel: 29, platformID: "Q", framework: "v9.0.99", stable: false), + }; + + public static readonly Dictionary NdkMinimumAPI = new Dictionary { + { AbiNames.TargetJit.AndroidArmV7a, 16 }, + { AbiNames.TargetJit.AndroidArmV8a, 21 }, + { AbiNames.TargetJit.AndroidX86, 16 }, + { AbiNames.TargetJit.AndroidX86_64, 21 }, + }; + } +} diff --git a/build-tools/xaprepare/xaprepare/ConfigAndData/CommonLicenses.cs b/build-tools/xaprepare/xaprepare/ConfigAndData/CommonLicenses.cs new file mode 100644 index 000000000..f14e1ead9 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ConfigAndData/CommonLicenses.cs @@ -0,0 +1,14 @@ +using System; +using System.IO; + +namespace Xamarin.Android.Prepare +{ + static class CommonLicenses + { + static readonly string LicenseDataDir = Path.Combine ("build-tools", "license-data"); + + public static readonly string Apache20Path = Path.Combine (LicenseDataDir, "Apache-2.0.txt"); + public static readonly string GPLv2Path = Path.Combine (LicenseDataDir, "GPLv2.txt"); + public static readonly string MonoMITPath = Path.Combine (LicenseDataDir, "Mono-MIT.txt"); + } +} diff --git a/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.Linux.cs b/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.Linux.cs new file mode 100644 index 000000000..531979047 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.Linux.cs @@ -0,0 +1,22 @@ +using System; + +namespace Xamarin.Android.Prepare +{ + partial class Configurables + { + partial class Urls + { + public static readonly Uri Corretto = new Uri ("https://d3pxv6yz143wms.cloudfront.net/8.212.04.2/amazon-corretto-8.212.04.2-linux-x64.tar.gz"); + } + + partial class Defaults + { + public const string NativeLibraryExtension = ".so"; + } + + partial class Paths + { + public const string NdkToolchainOSTag = "linux-x86_64"; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.MacOS.cs b/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.MacOS.cs new file mode 100644 index 000000000..6e8be5377 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.MacOS.cs @@ -0,0 +1,28 @@ +using System; + +namespace Xamarin.Android.Prepare +{ + partial class Configurables + { + partial class Urls + { + public static readonly Uri Corretto = new Uri ("https://d3pxv6yz143wms.cloudfront.net/8.212.04.2/amazon-corretto-8.212.04.2-macosx-x64.tar.gz"); + + // 6.0: https://download.mono-project.com/archive/6.0.0/macos-10-universal/MonoFramework-MDK-6.0.0.241.macos10.xamarin.universal.pkg + // Currently breaks the build, thus we use the latest stable below + public static readonly Uri MonoPackage = new Uri ("https://download.mono-project.com/archive/5.20.1/macos-10-universal/MonoFramework-MDK-5.20.1.19.macos10.xamarin.universal.pkg"); + } + + partial class Defaults + { + public const string MacOSDeploymentTarget = "10.11"; + public const string NativeLibraryExtension = ".dylib"; + } + + partial class Paths + { + public const string MonoCrossRuntimeInstallPath = "Darwin"; + public const string NdkToolchainOSTag = "darwin-x86_64"; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.Unix.cs b/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.Unix.cs new file mode 100644 index 000000000..8a031b753 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.Unix.cs @@ -0,0 +1,22 @@ +using System.IO; + +namespace Xamarin.Android.Prepare +{ + partial class Configurables + { + partial class Defaults + { + public const string DefaultCompiler = "cc"; + } + + partial class Paths + { + static string BundleOSType => Context.Instance.OS.Type; + + public static string BCLTestsSourceDir => GetCachedPath (ref bclTestsSourceDir, () => Path.Combine (MonoProfileDir, "tests")); + public static string BCLAssembliesSourceDir => MonoProfileDir; + + public static readonly string MonoRuntimeHostMingwNativeLibraryPrefix = Path.Combine ("..", "bin"); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.Windows.cs b/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.Windows.cs new file mode 100644 index 000000000..078a88a52 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.Windows.cs @@ -0,0 +1,55 @@ +using System; +using System.IO; + +namespace Xamarin.Android.Prepare +{ + partial class Configurables + { + partial class Urls + { + public static Uri Corretto => GetWindowsCorrettoUrl (); + + public static readonly Uri Corretto64 = new Uri ("https://d3pxv6yz143wms.cloudfront.net/8.212.04.2/amazon-corretto-8.212.04.2-windows-x64-jdk.zip"); + public static readonly Uri Corretto32 = new Uri ("https://d3pxv6yz143wms.cloudfront.net/8.212.04.2/amazon-corretto-8.212.04.2-windows-x86-jdk.zip"); + + /// + /// Base URL for the Ant tool download. Used in + /// + public static readonly Uri AntBaseUri = new Uri ("https://archive.apache.org/dist/ant/binaries/"); + + static Uri GetWindowsCorrettoUrl () + { + if (Context.Instance.OS == null) + return Corretto64; + + return Context.Instance.OS.Is64Bit ? Corretto64 : Corretto32; + } + } + + partial class Defaults + { + public const string NativeLibraryExtension = ".dll"; + } + + partial class Paths + { + static string BundleOSType => "Darwin"; // Windows doesn't build the bundle + + // Windows doesn't build the bundle so we need to look in the XA framework dir as installed + public static string BCLAssembliesSourceDir => InstallBCLFrameworkDir; + + // Likewise here, there's no "source" dir for test assemblies - we need to look at the destination dir + public static string BCLTestsSourceDir => BCLTestsDestDir; + + public const string MonoCrossRuntimeInstallPath = "Windows"; + public static readonly string MonoRuntimeHostMingwNativeLibraryPrefix = Path.Combine ("..", "bin"); + public const string NdkToolchainOSTag = "windows"; + public const string AntArchiveName = "apache-ant-1.10.6-bin.zip"; + public static string AntArchivePath => GetCachedPath (ref antArchivePath, () => Path.Combine (ctx.Properties.GetRequiredValue (KnownProperties.AndroidToolchainCacheDirectory), AntArchiveName)); + public static string AntInstallDir => GetCachedPath (ref antInstallDir, () => Path.Combine (ctx.Properties.GetRequiredValue (KnownProperties.AndroidToolchainDirectory), "ant")); + } + + static string antArchivePath; + static string antInstallDir; + } +} diff --git a/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.cs b/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.cs new file mode 100644 index 000000000..491e70c3d --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.cs @@ -0,0 +1,414 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Xamarin.Android.Prepare +{ + // This class exists so that nobody has to jump around the source in order to find an URL to modify, a setting to + // tweak etc. Code that uses those configurables is all over the place, but the source of the data is here and only + // here it has to be changed if need be. The entries should be something that might have to be changed by somebody + // not familiar with the entirety of Xamarin.Android or a developer not usually involved in build system + // maintenance/development. + // + // When adding entries here, try to make their names clear and unambiguous. Don't hesitate using comments (doc + // comments work fine). + // + partial class Configurables + { + static Context ctx => Context.Instance; + + public static partial class Urls + { + /// + /// Base URL for all Android SDK and NDK downloads. Used in + /// + public static readonly Uri AndroidToolchain_AndroidUri = new Uri ("https://dl.google.com/android/repository/"); + + /// + /// Base URL to download the XA binary bundle from + /// + public static Uri Bundle_XABundleDownloadPrefix => new Uri (Bundle_AzureBaseUri, $"{Bundle_AzureJobUri}/xamarin-android/bin/{Context.Instance.Configuration}"); + static readonly Uri Bundle_AzureBaseUri = new Uri ("https://xamjenkinsartifact.azureedge.net/mono-jenkins/"); + const string Bundle_AzureJobUri_Debug = "xamarin-android-debug"; + const string Bundle_AzureJobUri_Release = "xamarin-android"; + static string Bundle_AzureJobUri => ctx.IsDebugBuild ? Bundle_AzureJobUri_Debug : Bundle_AzureJobUri_Release; + + public static readonly Uri NugetUri = new Uri ("https://dist.nuget.org/win-x86-commandline/v4.9.4/nuget.exe"); + + public static Uri MonoArchive_BaseUri = new Uri ("https://xamjenkinsartifact.azureedge.net/mono-sdks/"); + } + + public static partial class Defaults + { + public static readonly char[] PropertyListSeparator = new [] { ':' }; + + // Mono runtimes + public const string DebugFileExtension = ".pdb"; + public const string MonoHostMingwRuntimeNativeLibraryExtension = WindowsDLLSuffix; + public const string MonoJitRuntimeNativeLibraryExtension = ".so"; + public const string MonoRuntimeOutputMonoBtlsFilename = "libmono-btls-shared"; + public const string MonoRuntimeOutputMonoPosixHelperFilename = "libMonoPosixHelper"; + public const string MonoRuntimeOutputAotProfilerFilename = "libmono-profiler-aot"; + public const string MonoRuntimeOutputFileName = "libmonosgen-2.0"; + public const string MonoRuntimeOutputProfilerFilename = "libmono-profiler-log"; + + public const string WindowsExecutableSuffix = ".exe"; + public const string WindowsDLLSuffix = ".dll"; + public const string DebugBinaryInfix = ".d"; + + public const bool UseEmoji = true; + public const bool DullMode = false; + + public static string MonoSdksConfiguration => Context.Instance.Configuration.ToLowerInvariant (); + + public const string ZipCompressionFormatName = "zip"; + public const string SevenZipCompressionFormatName = "7z"; + + public static readonly Dictionary CompressionFormats = new Dictionary (StringComparer.OrdinalIgnoreCase) { + {ZipCompressionFormatName, new CompressionFormat (ZipCompressionFormatName, "ZIP", "zip")}, + {SevenZipCompressionFormatName, new CompressionFormat (SevenZipCompressionFormatName, "7Zip", "7z")}, + }; + + public static CompressionFormat DefaultCompressionFormat => CompressionFormats [ZipCompressionFormatName]; + + /// + /// Default execution mode. One of: + /// + /// * CI: continuous integration (a.k.a. bot, a.k.a. dull, a.k.a. sad) mode in which no color, no fancy + /// progress indicators are used. At the end of the run the application exits. + /// + /// * Standard: default mode if running with a terminal attached (i.e. when we're not redirected) and + /// when the console supports colors, cursor movement etc. If no such capabilities are detected this + /// mode degrades to CI. At the end of the run the application exits. + /// + /// * Interactive: like Standard but at the end of the run the application does not exit. This mode + /// presents the user with a full TUI app. Degrades in the same manner as Standard. + /// + /// + public static readonly ExecutionMode ExecutionMode = ExecutionMode.Standard; + + /// + /// Default make/ninja/etc concurrency. If set to 0 then the actual concurrency level is determined + /// by looking at the number of CPUs and equals that value + 1. + /// + public const uint MakeConcurrency = 0; + + /// + /// Default maximum number of parallel tasks to start, for instance when downloading. + /// + public const int DefaultMaximumParallelTasks = 5; + + /// + /// The maximum JDK version we support. Note: this will probably go away with Corretto + /// + public const int MaxJDKVersion = 8; + public static readonly Version CorrettoVersion = Version.Parse ("8.212.04.2"); + + /// + /// Prefix for all the log files created by the bootstrapper. + /// + public const string LogFilePrefix = "prepare"; + + /// + /// Default logging verbosity for the entire program. + /// + public static readonly LoggingVerbosity LoggingVerbosity = LoggingVerbosity.Normal; + + /// + /// Version of the XA binary bundle downloaded/created by this tool. + /// + public const string XABundleVersion = "v21"; + + /// + /// Length to truncate the git commit hash to. + /// + public const uint AbbreviatedHashLength = 7; + + /// + /// Default hash algorithm to compute file hashes + /// + public const string HashAlgorithm = "SHA1"; + + public static readonly Dictionary AndroidToolchainPrefixes = new Dictionary (StringComparer.Ordinal) { + { AbiNames.TargetJit.AndroidArmV7a, "arm-linux-androideabi" }, + { AbiNames.TargetJit.AndroidArmV8a, "aarch64-linux-android" }, + { AbiNames.TargetJit.AndroidX86, "i686-linux-android" }, + { AbiNames.TargetJit.AndroidX86_64, "x86_64-linux-android" }, + }; + + const string CrossArmV7aName = "cross-arm"; + const string CrossArmV8aName = "cross-arm64"; + const string CrossX86Name = "cross-x86"; + const string CrossX86_64Name = "cross-x86_64"; + + public static readonly Dictionary CrossRuntimeNames = new Dictionary (StringComparer.Ordinal) { + { AbiNames.CrossAot.ArmV7a, CrossArmV7aName }, + { AbiNames.CrossAot.ArmV8a, CrossArmV8aName }, + { AbiNames.CrossAot.X86, CrossX86Name }, + { AbiNames.CrossAot.X86_64, CrossX86_64Name }, + { AbiNames.CrossAot.WinArmV7a, CrossArmV7aName }, + { AbiNames.CrossAot.WinArmV8a, CrossArmV8aName }, + { AbiNames.CrossAot.WinX86, CrossX86Name }, + { AbiNames.CrossAot.WinX86_64, CrossX86_64Name }, + }; + + const string ArmV7aPrefix = "armv7-linux-android-"; + const string ArmV8aPrefix = "aarch64-v8a-linux-android-"; + const string X86Prefix = "i686-linux-android-"; + const string X86_64Prefix = "x86_64-linux-android-"; + + public static readonly Dictionary CrossRuntimeExePrefixes = new Dictionary (StringComparer.Ordinal) { + { AbiNames.CrossAot.ArmV7a, ArmV7aPrefix}, + { AbiNames.CrossAot.ArmV8a, ArmV8aPrefix }, + { AbiNames.CrossAot.X86, X86Prefix }, + { AbiNames.CrossAot.X86_64, X86_64Prefix }, + { AbiNames.CrossAot.WinArmV7a, ArmV7aPrefix }, + { AbiNames.CrossAot.WinArmV8a, ArmV8aPrefix }, + { AbiNames.CrossAot.WinX86, X86Prefix }, + { AbiNames.CrossAot.WinX86_64, X86_64Prefix }, + }; + + /// + /// Used in rules.mk generator. Files to include in the XA bundle archives. + /// + public static readonly List BundleZipsInclude = new List { + "$(ZIP_OUTPUT_BASENAME)/ThirdPartyNotices.txt", + "$(ZIP_OUTPUT_BASENAME)/bin/Debug", + "$(ZIP_OUTPUT_BASENAME)/bin/Release", + }; + + /// + /// Used in rules.mk generator. Files to exclude from the XA bundle archives. Must be syntactically + /// correct for GNU Make. + /// + public static readonly List BundleZipsExclude = new List { + "$(ZIP_OUTPUT_BASENAME)/bin/*/bundle-*.zip" + }; + + /// + /// Used in rules.mk generator. Files to include in test results bundle. Must be syntactically + /// correct for GNU Make. + /// + public static readonly List TestResultsBundleInclude = new List { + "$(wildcard TestResult-*.xml)", + "$(wildcard bin/Test$(CONFIGURATION)/compatibility)", + "$(wildcard bin/Test$(CONFIGURATION)/logcat*)", + "$(wildcard bin/Test$(CONFIGURATION)/msbuild*.binlog*)", + "$(wildcard bin/Test$(CONFIGURATION)/temp)", + "$(wildcard bin/Test$(CONFIGURATION)/EmbeddedDSO)", + "$(wildcard bin/Test$(CONFIGURATION)/CodeBehind)", + "$(wildcard bin/Test$(CONFIGURATION)/TestOutput-*.txt)", + "$(wildcard bin/Test$(CONFIGURATION)/Timing_*)", + "$(wildcard *.csv)", + }; + + /// + /// Used in rules.mk generator. Files to exclude from the test results bundle. Must be syntactically + /// correct for GNU Make. + /// + public static readonly List TestResultsBundleExclude = new List { + }; + + /// + /// Used in rules.mk generator. Files to include in build status bundle archive. Must be syntactically + /// correct for GNU Make. + /// + public static readonly List BuildStatusBundleInclude = new List { + "Configuration.OperatingSystem.props", + "$(wildcard bin/Build$(CONFIGURATION)/msbuild*.binlog)", + "$(shell find . -name 'config.log')", + "$(shell find . -name 'config.status')", + "$(shell find . -name 'config.h')", + "$(shell find . -name 'CMakeCache.txt')", + "$(shell find . -name 'config.h')", + "$(shell find . -name '.ninja_log')", + "$(shell find . -name 'android-*.config.cache')", + "bin/Build$(CONFIGURATION)/XABuildConfig.cs", + }; + + /// + /// Used in rules.mk generator. Optional files to include in the build status bundle (included only if + /// they exist). Must be syntactically correct for GNU Make. + /// + public static readonly List BuildStatusBundleIncludeConditional = new List { + "Configuration.Override.props", + }; + + /// + /// Used in rules.mk generator. Files to exclude from the build status bundle. Must be syntactically + /// correct for GNU Make. + /// + public static readonly List BuildStatusBundleExclude = new List { + }; + } + + public static partial class Paths + { + // Global, compile-time locations + public static readonly string HomeDir = Environment.GetFolderPath (Environment.SpecialFolder.Personal); + public static readonly string BootstrapResourcesDir = Path.Combine (BuildPaths.XAPrepareSourceDir, "Resources"); + public static readonly string BuildToolsDir = Path.Combine (BuildPaths.XamarinAndroidSourceRoot, "build-tools"); + public static readonly string BuildToolsScriptsDir = Path.Combine (BuildToolsDir, "scripts"); + public static readonly string BinDirRoot = Path.Combine (BuildPaths.XamarinAndroidSourceRoot, "bin"); + public static readonly string ExternalDir = Path.Combine (BuildPaths.XamarinAndroidSourceRoot, "external"); + public static readonly string LocalNugetPath = Path.Combine (BuildPaths.XamarinAndroidSourceRoot, ".nuget", "NuGet.exe"); + public static readonly string ExternalGitDepsFilePath = Path.Combine (BuildPaths.XamarinAndroidSourceRoot, ".external"); + public static readonly string ExternalGitDepsDestDir = ExternalDir; + public static readonly string ExternalXamarinAndroidToolsSln = Path.Combine (ExternalDir, "xamarin-android-tools", "Xamarin.Android.Tools.sln"); + public static readonly string MxeSourceDir = Path.Combine (ExternalDir, "mxe"); + public static readonly string MonoSDKSRelativeOutputDir = Path.Combine ("sdks", "out"); + public static readonly string RuntimeInstallRelativeLibDir = "lib"; + public static readonly string PackageImageDependenciesTemplate = Path.Combine (BuildToolsScriptsDir, "prepare-image-dependencies.sh.in"); + public static readonly string PackageImageDependenciesOutput = Path.Combine (BuildPaths.XamarinAndroidSourceRoot, "prepare-image-dependencies.sh"); + public static readonly string BundlePathTemplate = Path.Combine (BuildToolsScriptsDir, "bundle-path.targets.in"); + + // Dynamic locations used throughout the code + public static string ExternalJavaInteropDir => GetCachedPath (ref externalJavaInteropDir, () => ctx.Properties.GetRequiredValue (KnownProperties.JavaInteropFullPath)); + public static string BundlePathOutput => GetCachedPath (ref bundlePathOutput, () => Path.Combine (BuildBinDir, "bundle-path.targets")); + public static string MonoSDKSOutputDir => GetCachedPath (ref monoSDKsOutputDir, () => Path.Combine (MonoSourceFullPath, MonoSDKSRelativeOutputDir)); + public static string MonoProfileDir => GetCachedPath (ref monoProfileDir, () => Path.Combine (MonoSDKSOutputDir, "android-bcl", "monodroid")); + public static string MonoProfileToolsDir => GetCachedPath (ref monoProfileToolsDir, () => Path.Combine (MonoSDKSOutputDir, "android-bcl", "monodroid_tools")); + + public static string BCLFacadeAssembliesSourceDir => GetCachedPath (ref bclFacadeAssembliesSourceDir, () => Path.Combine (BCLAssembliesSourceDir, "Facades")); + public static string BCLHostAssembliesSourceDir => BCLAssembliesSourceDir; + public static string BCLHostFacadeAssembliesSourceDir => BCLFacadeAssembliesSourceDir; + + public static string BCLWindowsOutputDir => GetCachedPath (ref bclWindowsOutputDir, () => Path.Combine (BuildBinDir, "windows-bcl")); + public static string BCLWindowsAssembliesSourceDir => GetCachedPath (ref bclWindowsAssembliesSourceDir, () => Path.Combine (BCLWindowsOutputDir, "android-bcl", "monodroid")); + public static string BCLWindowsFacadeAssembliesSourceDir => GetCachedPath (ref bclWindowsFacadeAssembliesSourceDir, () => Path.Combine (BCLWindowsAssembliesSourceDir, "Facades")); + + public static string BCLTestsDestDir => GetCachedPath (ref bclTestsDestDir, () => Path.Combine (XAInstallPrefix, "..", "..", "bcl-tests")); + public static string BCLTestsArchivePath => GetCachedPath (ref bclTestsArchivePath, () => Path.Combine (BCLTestsDestDir, BCLTestsArchiveName)); + + public static string TestBinDir => GetCachedPath (ref testBinDir, () => Path.Combine (Configurables.Paths.BinDirRoot, $"Test{ctx.Configuration}")); + public static string BinDir => GetCachedPath (ref binDir, () => Path.Combine (Configurables.Paths.BinDirRoot, ctx.Configuration)); + public static string BuildBinDir => GetCachedPath (ref buildBinDir, () => Path.Combine (Configurables.Paths.BinDirRoot, $"Build{ctx.Configuration}")); + public static string MingwBinDir => GetCachedPath (ref mingwBinDir, () => Path.Combine (ctx.Properties.GetRequiredValue (KnownProperties.AndroidMxeFullPath), "bin")); + public static string ProfileAssembliesProjitemsPath => GetCachedPath (ref profileAssembliesProjitemsPath, () => Path.Combine (BuildBinDir, "ProfileAssemblies.projitems")); + + public static string BundleArchivePath => GetCachedPath (ref bundleArchivePath, () => Path.Combine (ctx.XABundlePath ?? ctx.Properties.GetRequiredValue (KnownProperties.AndroidToolchainCacheDirectory), XABundleFileName)); + public static string BundleInstallDir => BinDir; + + // Mono Runtimes + public static string MonoAndroidFrameworksSubDir = Path.Combine ("xbuild-frameworks", "MonoAndroid"); + public static string MonoAndroidFrameworksRootDir => GetCachedPath (ref monoAndroidFrameworksRootDir, () => Path.Combine (XAInstallPrefix, MonoAndroidFrameworksSubDir)); + public static string InstallBCLFrameworkDir => GetCachedPath (ref installBCLFrameworkDir, () => Path.Combine (MonoAndroidFrameworksRootDir, "v1.0")); + public static string InstallBCLFrameworkFacadesDir => GetCachedPath (ref installBCLFrameworkFacadesDir, () => Path.Combine (InstallBCLFrameworkDir, "Facades")); + public static string InstallBCLFrameworkRedistListDir => GetCachedPath (ref installBCLFrameworkRedistListDir, () => Path.Combine (InstallBCLFrameworkDir, "RedistList")); + + public static string InstallBCLDesignerDir => GetCachedPath (ref installBCLDesignerDir, () => Path.Combine (XAInstallPrefix, "xbuild", "Xamarin", "Android")); + public static string InstallHostBCLDir => GetCachedPath (ref installHostBCLDir, () => Path.Combine (InstallBCLDesignerDir, ctx.OS.Type, "bcl")); + public static string InstallHostBCLFacadesDir => GetCachedPath (ref installHostBCLFacadesDir, () => Path.Combine (InstallBCLDesignerDir, ctx.OS.Type, "bcl", "Facades")); + public static string InstallWindowsBCLDir => GetCachedPath (ref installWindowsBCLDir, () => Path.Combine (InstallBCLDesignerDir, "bcl")); + public static string InstallWindowsBCLFacadesDir => GetCachedPath (ref installWindowsBCLFacadesDir, () => Path.Combine (InstallBCLDesignerDir, "bcl", "Facades")); + + public static string InstallMSBuildDir => GetCachedPath (ref installMSBuildDir, () => Path.Combine (XAInstallPrefix, "xbuild", "Xamarin", "Android")); + public static string OutputIncludeDir => GetCachedPath (ref outputIncludeDir, () => Path.Combine (BinDirRoot, ctx.Configuration, "include")); + public static string MonoRuntimesEnabledAbisCachePath => GetCachedPath (ref monoRuntimesEnabledAbisCachePath, () => Path.Combine (BuildBinDir, "mono-runtimes-abi.cache")); + public static string FrameworkListInstallPath => GetCachedPath (ref frameworkListInstallPath, () => Path.Combine (InstallBCLFrameworkRedistListDir, "FrameworkList.xml")); + + // Cmake + MinGW + public static string Mingw32CmakeTemplatePath => GetCachedPath (ref mingw32CmakeTemplatePath, () => Path.Combine (BuildToolsScriptsDir, "mingw-32.cmake.in")); + public static string Mingw64CmakeTemplatePath => GetCachedPath (ref mingw64CmakeTemplatePath, () => Path.Combine (BuildToolsScriptsDir, "mingw-64.cmake.in")); + public static string Mingw32CmakePath => GetCachedPath (ref mingw32CmakePath, () => Path.Combine (BuildBinDir, "mingw-32.cmake")); + public static string Mingw64CmakePath => GetCachedPath (ref mingw64CmakePath, () => Path.Combine (BuildBinDir, "mingw-64.cmake")); + + // Corretto OpenJDK + public static string CorrettoCacheDir => GetCachedPath (ref correttoCacheDir, () => ctx.Properties.GetRequiredValue (KnownProperties.AndroidToolchainCacheDirectory)); + public static string CorrettoInstallDir => GetCachedPath (ref correttoInstallDir, () => Path.Combine (ctx.Properties.GetRequiredValue (KnownProperties.AndroidToolchainDirectory), "jdk")); + + // LibZip + public static string LibZipOutputPath => InstallMSBuildDir; + + // bundle + public static string XABundleFileName => $"bundle-{Defaults.XABundleVersion}-h{ctx.BuildInfo.VersionHash}-{ctx.Configuration}-{BundleOSType}-libzip={ctx.BuildInfo.LibZipHash},mono={ctx.BuildInfo.MonoHash}.{ctx.CompressionFormat.Extension}"; + public static string BCLTestsArchiveName = "bcl-tests.zip"; + + // Mono Archive + public static string MonoArchiveMonoHash => ctx.BuildInfo.FullMonoHash; + public static string MonoArchiveBaseFileName => $"android-{Defaults.MonoSdksConfiguration}-{ctx.OS.Type}-{MonoArchiveMonoHash}"; + public static string MonoArchiveWindowsBaseFileName => $"android-release-Windows-{MonoArchiveMonoHash}"; + public static string MonoArchiveFileName => $"{MonoArchiveBaseFileName}.zip"; + public static string MonoArchiveWindowsFileName => $"{MonoArchiveWindowsBaseFileName}.zip"; + public static string MonoArchiveLocalPath => Path.Combine (ctx.Properties.GetRequiredValue (KnownProperties.AndroidToolchainCacheDirectory), MonoArchiveFileName); + public static string MonoArchiveWindowsLocalPath => Path.Combine (ctx.Properties.GetRequiredValue (KnownProperties.AndroidToolchainCacheDirectory), MonoArchiveWindowsFileName); + + // Other + + // All the entries are treated as glob patterns and processed as follows: + // + // * Base path (i.e. the result of Path.GetDirectoryName) is treated as the target directory + // * The "file" part is treated as the glob patter passed to Directory.EnumerateFiles + // * The "file" part must always be a valid glob pattern + // + public static readonly List BundleVersionHashFiles = new List { + Path.Combine (BuildToolsScriptsDir, "BuildEverything.mk"), + Path.Combine (BuildPaths.XAPrepareSourceDir, "ConfigAndData", "BuildAndroidPlatforms.cs"), + }; + + public static string AndroidToolchainBinDirectory => EnsureAndroidToolchainBinDirectories (); + + // not really configurables, merely convenience aliases for more frequently used paths that come from properties + public static string XAInstallPrefix => ctx.Properties.GetRequiredValue (KnownProperties.XAInstallPrefix); + public static string MonoSourceFullPath => ctx.Properties.GetRequiredValue (KnownProperties.MonoSourceFullPath); + public static string MonoExternalFullPath => Path.Combine (MonoSourceFullPath, "external"); + + static string EnsureAndroidToolchainBinDirectories () + { + if (androidToolchainBinDirectory != null) + return androidToolchainBinDirectory; + + androidToolchainBinDirectory = Path.Combine (ctx.Properties.GetRequiredValue (KnownProperties.AndroidNdkDirectory), "toolchains", "llvm", "prebuilt", NdkToolchainOSTag, "bin"); + return androidToolchainBinDirectory; + } + + static string GetCachedPath (ref string variable, Func creator) + { + if (!String.IsNullOrEmpty (variable)) + return variable; + + variable = Path.GetFullPath (creator ()); + return variable; + } + + static string testBinDir; + static string buildBinDir; + static string mingwBinDir; + static string binDir; + static string monoSDKsOutputDir; + static string androidToolchainBinDirectory; + static string monoProfileDir; + static string monoProfileToolsDir; + static string bundleArchivePath; + static string bclTestsDestDir; + static string bclTestsArchivePath; + static string bclFacadeAssembliesSourceDir; + static string bclWindowsOutputDir; + static string bclWindowsAssembliesSourceDir; + static string bclWindowsFacadeAssembliesSourceDir; + static string installBCLFrameworkDir; + static string installBCLFrameworkFacadesDir; + static string installBCLFrameworkRedistListDir; + static string installMSBuildDir; + static string outputIncludeDir; + static string mingw32CmakePath; + static string mingw64CmakePath; + static string mingw32CmakeTemplatePath; + static string mingw64CmakeTemplatePath; + static string monoRuntimesEnabledAbisCachePath; + static string frameworkListInstallPath; + static string correttoCacheDir; + static string correttoInstallDir; + static string profileAssembliesProjitemsPath; + static string bundlePathOutput; + static string bclTestsSourceDir; + static string installHostBCLDir; + static string installHostBCLFacadesDir; + static string installWindowsBCLDir; + static string installWindowsBCLFacadesDir; + static string installBCLDesignerDir; + static string monoAndroidFrameworksRootDir; + static string externalJavaInteropDir; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/AndroidToolchain.Linux.cs b/build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/AndroidToolchain.Linux.cs new file mode 100644 index 000000000..74e2c5355 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/AndroidToolchain.Linux.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Xamarin.Android.Prepare +{ + partial class AndroidToolchain + { + static readonly string osTag = "linux"; + static readonly string altOsTag = osTag; + } +} diff --git a/build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/AndroidToolchain.MacOS.cs b/build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/AndroidToolchain.MacOS.cs new file mode 100644 index 000000000..e39118768 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/AndroidToolchain.MacOS.cs @@ -0,0 +1,8 @@ +namespace Xamarin.Android.Prepare +{ + partial class AndroidToolchain + { + static readonly string osTag = "darwin"; + static readonly string altOsTag = "macosx"; + } +} diff --git a/build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/AndroidToolchain.Windows.cs b/build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/AndroidToolchain.Windows.cs new file mode 100644 index 000000000..cb362d006 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/AndroidToolchain.Windows.cs @@ -0,0 +1,8 @@ +namespace Xamarin.Android.Prepare +{ + partial class AndroidToolchain + { + static readonly string osTag = "windows"; + static readonly string altOsTag = osTag; + } +} diff --git a/build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/AndroidToolchain.cs b/build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/AndroidToolchain.cs new file mode 100644 index 000000000..08c79a922 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/AndroidToolchain.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Xamarin.Android.Prepare +{ + partial class AndroidToolchain : AppObject + { + public static readonly Uri AndroidUri = Configurables.Urls.AndroidToolchain_AndroidUri; + + public List Components { get; } + + public AndroidToolchain () + { + string AndroidNdkVersion = BuildAndroidPlatforms.AndroidNdkVersion; + string AndroidNdkDirectory = GetRequiredProperty (KnownProperties.AndroidNdkDirectory); + string AndroidCmakeVersion = GetRequiredProperty (KnownProperties.AndroidCmakeVersion); + string AndroidCmakeVersionPath = GetRequiredProperty (KnownProperties.AndroidCmakeVersionPath); + string EmulatorVersion = GetRequiredProperty (KnownProperties.EmulatorVersion); + string XABuildToolsFolder = GetRequiredProperty (KnownProperties.XABuildToolsFolder); + string XABuildToolsVersion = GetRequiredProperty (KnownProperties.XABuildToolsVersion); + string XAPlatformToolsVersion = GetRequiredProperty (KnownProperties.XAPlatformToolsVersion); + + Components = new List { + new AndroidPlatformComponent ("android-2.3.3_r02-linux", "10"), + new AndroidPlatformComponent ("android-15_r03", "15"), + new AndroidPlatformComponent ("android-16_r04", "16"), + new AndroidPlatformComponent ("android-17_r02", "17"), + new AndroidPlatformComponent ("android-18_r02", "18"), + new AndroidPlatformComponent ("android-19_r03", "19"), + new AndroidPlatformComponent ("android-20_r02", "20"), + new AndroidPlatformComponent ("android-21_r02", "21"), + new AndroidPlatformComponent ("android-22_r02", "22"), + new AndroidPlatformComponent ("platform-23_r03", "23"), + new AndroidPlatformComponent ("platform-24_r02", "24"), + new AndroidPlatformComponent ("platform-25_r03", "25"), + new AndroidPlatformComponent ("platform-26_r02", "26"), + new AndroidPlatformComponent ("platform-27_r03", "27"), + new AndroidPlatformComponent ("platform-28_r04", "28"), + new AndroidPlatformComponent ("platform-Q_r02", "Q"), + + new AndroidToolchainComponent ("docs-24_r01", destDir: "docs"), + new AndroidToolchainComponent ("android_m2repository_r16", destDir: Path.Combine ("extras", "android", "m2repository")), + new AndroidToolchainComponent ("x86-28_r04", destDir: Path.Combine ("system-images", "android-28", "default", "x86"), relativeUrl: new Uri ("sys-img/android/", UriKind.Relative)), + new AndroidToolchainComponent ($"android-ndk-r{AndroidNdkVersion}-{osTag}-x86_64", destDir: AndroidNdkDirectory, expectedPkgRevision: "19.2.5345600"), + new AndroidToolchainComponent ($"build-tools_r{XABuildToolsVersion}-{altOsTag}", destDir: Path.Combine ("build-tools", XABuildToolsFolder), isMultiVersion: true), + new AndroidToolchainComponent ($"platform-tools_r{XAPlatformToolsVersion}-{osTag}", destDir: "platform-tools"), + new AndroidToolchainComponent ($"sdk-tools-{osTag}-4333796", destDir: "tools"), + new AndroidToolchainComponent ($"emulator-{osTag}-{EmulatorVersion}", destDir: "emulator"), + new AndroidToolchainComponent ($"cmake-{AndroidCmakeVersion}-{osTag}-x86_64", destDir: Path.Combine ("cmake", AndroidCmakeVersionPath), isMultiVersion: true, noSubdirectory: true, expectedPkgRevision: "3.10.2"), + }; + } + + static string GetRequiredProperty (string propertyName) + { + string value = Context.Instance.Properties [propertyName]; + if (String.IsNullOrEmpty (value)) + throw new InvalidOperationException ($"Required property '{propertyName}' not defined"); + return value; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/Linux.Arch.cs b/build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/Linux.Arch.cs new file mode 100644 index 000000000..7faf87234 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/Linux.Arch.cs @@ -0,0 +1,60 @@ +using System.Collections.Generic; + +namespace Xamarin.Android.Prepare +{ + class LinuxArch : Linux + { + static readonly List packages = new List { + new ArchLinuxProgram ("autoconf"), + new ArchLinuxProgram ("automake"), + new ArchLinuxProgram ("binutils"), + new ArchLinuxProgram ("bison"), + new ArchLinuxProgram ("curl"), + new ArchLinuxProgram ("fakeroot"), + new ArchLinuxProgram ("file"), + new ArchLinuxProgram ("findutils"), + new ArchLinuxProgram ("flex"), + new ArchLinuxProgram ("gawk"), + new ArchLinuxProgram ("gcc"), + new ArchLinuxProgram ("gettext"), + new ArchLinuxProgram ("git"), + new ArchLinuxProgram ("grep"), + new ArchLinuxProgram ("groff"), + new ArchLinuxProgram ("gtk-sharp-2"), + new ArchLinuxProgram ("gzip"), + new ArchLinuxProgram ("jdk8-openjdk"), + new ArchLinuxProgram ("libtool"), + new ArchLinuxProgram ("libzip"), + new ArchLinuxProgram ("m4"), + new ArchLinuxProgram ("make"), + new ArchLinuxProgram ("nuget"), + new ArchLinuxProgram ("patch"), + new ArchLinuxProgram ("pkg-config"), + new ArchLinuxProgram ("referenceassemblies-pcl"), + new ArchLinuxProgram ("sed"), + new ArchLinuxProgram ("texinfo"), + new ArchLinuxProgram ("unzip"), + new ArchLinuxProgram ("which"), + new ArchLinuxProgram ("zip"), + new ArchLinuxProgram ("p7zip"), + }; + + public LinuxArch (Context context) + : base (context) + { + Dependencies.AddRange (packages); + } + + protected override void InitializeDependencies () + {} + + protected override bool InitOS () + { + if (!base.InitOS ()) + return false; + + Log.Todo ("Implement"); + return false; + } + }; +} diff --git a/build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/Linux.Debian.cs b/build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/Linux.Debian.cs new file mode 100644 index 000000000..c21e9c880 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/Linux.Debian.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; + +namespace Xamarin.Android.Prepare +{ + class LinuxDebian : LinuxDebianCommon + { + static readonly List packages = new List { + new DebianLinuxProgram ("zlib1g-dev"), + new DebianLinuxProgram ("libtool-bin", "libtool"), + }; + + static readonly List packagesPre10 = new List { + new DebianLinuxProgram ("openjdk-8-jdk"), + }; + + // zulu-8 does NOT exist as official Debian package! We need it for our bots, but we have to figure out what to + // do with Debian 10+ in general, as it does not contain OpenJDK 8 anymore and we require it to work. + static readonly List packages10AndNewer = new List { + new DebianLinuxProgram ("zulu-8"), + }; + + protected Version DebianRelease { get; private set; } = new Version (0, 0); + protected bool IsTesting { get; private set; } + + public LinuxDebian (Context context) + : base (context) + { + Dependencies.AddRange (packages); + } + + protected override void InitializeDependencies () + { + base.InitializeDependencies (); + + if (DebianRelease.Major >= 10 || (IsTesting && String.Compare ("buster", CodeName, StringComparison.OrdinalIgnoreCase) == 0)) + Dependencies.AddRange (packages10AndNewer); + else + Dependencies.AddRange (packagesPre10); + } + + protected override bool InitOS () + { + if (!base.InitOS ()) + return false; + + Version debianRelease; + if (!Version.TryParse (Release, out debianRelease)) { + if (Int32.TryParse (Release, out int singleNumberVersion)) { + debianRelease = new Version (singleNumberVersion, 0); + } else { + if (String.Compare ("testing", Release, StringComparison.OrdinalIgnoreCase) != 0) { + Log.ErrorLine ($"Unable to parse string '{Release}' as a valid Debian release version"); + return false; + } + + IsTesting = true; + return true; + } + } + + DebianRelease = debianRelease; + + return true; + } + }; +} diff --git a/build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/Linux.DebianCommon.cs b/build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/Linux.DebianCommon.cs new file mode 100644 index 000000000..aaa756f5c --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/Linux.DebianCommon.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; + +namespace Xamarin.Android.Prepare +{ + abstract class LinuxDebianCommon : Linux + { + static readonly List commonPackages = new List { + new DebianLinuxProgram ("ant"), + new DebianLinuxProgram ("autoconf"), + new DebianLinuxProgram ("autotools-dev"), + new DebianLinuxProgram ("automake"), + new DebianLinuxProgram ("cmake"), + new DebianLinuxProgram ("build-essential"), + new DebianLinuxProgram ("curl"), + new DebianLinuxProgram ("gcc"), + new DebianLinuxProgram ("g++"), + new DebianLinuxProgram ("g++-mingw-w64"), + new DebianLinuxProgram ("gcc-mingw-w64"), + new DebianLinuxProgram ("git"), + new DebianLinuxProgram ("libncurses-dev"), + new DebianLinuxProgram ("libtool"), + new DebianLinuxProgram ("libz-mingw-w64-dev"), + new DebianLinuxProgram ("libzip-dev"), + new DebianLinuxProgram ("linux-libc-dev"), + new DebianLinuxProgram ("make"), + new DebianLinuxProgram ("ninja-build", "ninja"), + new DebianLinuxProgram ("p7zip-full", "7z"), + new DebianLinuxProgram ("sqlite3"), + new DebianLinuxProgram ("vim-common"), + new DebianLinuxProgram ("zlib1g-dev"), + }; + + static readonly List commonPackages64bit = new List { + new DebianLinuxProgram ("lib32stdc++6"), + new DebianLinuxProgram ("lib32z1"), + }; + + protected override void InitializeDependencies () + { + Dependencies.AddRange (commonPackages); + if (!Is64Bit) + Dependencies.AddRange (commonPackages64bit); + } + + protected LinuxDebianCommon (Context context) + : base (context) + { + Flavor = "Debian"; + } + }; +} diff --git a/build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/Linux.Mint.cs b/build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/Linux.Mint.cs new file mode 100644 index 000000000..8614a5883 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/Linux.Mint.cs @@ -0,0 +1,10 @@ +namespace Xamarin.Android.Prepare +{ + class LinuxMint : LinuxUbuntu + { + protected override bool NeedLibtool => UbuntuRelease.Major == 19; + + public LinuxMint (Context context) : base (context) + {} + }; +} diff --git a/build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/Linux.Ubuntu.cs b/build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/Linux.Ubuntu.cs new file mode 100644 index 000000000..18a2d8196 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/Linux.Ubuntu.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; + +namespace Xamarin.Android.Prepare +{ + class LinuxUbuntu : LinuxUbuntuCommon + { + static readonly List preCosmicPackages = new List { + new DebianLinuxProgram ("libx32tinfo-dev"), + }; + + static readonly List cosmicPackages = new List { + new DebianLinuxProgram ("libx32ncurses6-dev"), + }; + + static readonly List preDiscoPackages = new List { + new DebianLinuxProgram ("openjdk-8-jdk"), + }; + + protected Version UbuntuRelease { get; private set; } = new Version (0, 0); + + protected override bool NeedLibtool => (UbuntuRelease.Major == 17 && UbuntuRelease.Minor == 10) | UbuntuRelease.Major >= 18; + + public LinuxUbuntu (Context context) : base (context) + {} + + protected override void InitializeDependencies () + { + base.InitializeDependencies (); + + if (UbuntuRelease.Major < 18 || (UbuntuRelease.Major == 18 && UbuntuRelease.Minor < 10)) + Dependencies.AddRange (preCosmicPackages); + else { + Dependencies.AddRange (cosmicPackages); + if (UbuntuRelease.Major < 19) + Dependencies.AddRange (preDiscoPackages); + } + } + + protected override bool InitOS () + { + Version ubuntuRelease; + if (!Version.TryParse (Release, out ubuntuRelease)) { + Log.ErrorLine ($"Unable to parse string '{Release}' as a valid Ubuntu release version"); + return false; + } + UbuntuRelease = ubuntuRelease; + + return base.InitOS (); + } + }; +} diff --git a/build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/Linux.UbuntuCommon.cs b/build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/Linux.UbuntuCommon.cs new file mode 100644 index 000000000..fcdd32b38 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/Linux.UbuntuCommon.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; + +namespace Xamarin.Android.Prepare +{ + abstract partial class LinuxUbuntuCommon : LinuxDebianCommon + { + static readonly List commonPackages64bit = new List { + new DebianLinuxProgram ("linux-libc-dev:i386"), + new DebianLinuxProgram ("zlib1g-dev:i386"), + }; + + static readonly List libtoolPackages = new List { + new DebianLinuxProgram ("libtool-bin", "libtool"), + }; + + protected virtual bool NeedLibtool { get; } = false; + + protected LinuxUbuntuCommon (Context context) + : base (context) + {} + + protected override void InitializeDependencies () + { + base.InitializeDependencies (); + + if (Is64Bit) + Dependencies.AddRange (commonPackages64bit); + } + + protected override bool InitOS () + { + if (!base.InitOS ()) + return false; + + if (NeedLibtool) + Dependencies.AddRange (libtoolPackages); + + return true; + } + }; +} diff --git a/build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/MacOS.cs b/build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/MacOS.cs new file mode 100644 index 000000000..eace2d105 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/MacOS.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; + +namespace Xamarin.Android.Prepare +{ + partial class MacOS + { + static readonly List programs = new List { + new HomebrewProgram ("ant"), + new HomebrewProgram ("autoconf"), + new HomebrewProgram ("automake"), + new HomebrewProgram ("cmake"), + + new HomebrewProgram ("git") { + MinimumVersion = "2.20.0", + }, + + new HomebrewProgram ("libzip", new Uri ("https://raw.githubusercontent.com/Homebrew/homebrew-core/3d9a3eb3a62ed9586cd8f6b3bf506845159f39a4/Formula/libzip.rb")) { + MinimumVersion = "1.5.2", + Pin = true, + }, + + new HomebrewProgram ("make"), + + new HomebrewProgram ("mingw-w64", new Uri ("https://raw.githubusercontent.com/Homebrew/homebrew-core/a6542037a48a55061a4c319e6bb174b3715f7cbe/Formula/mingw-w64.rb")) { + MinimumVersion = "6.0.0_1", + MaximumVersion = "6.0.0_1", + Pin = true, + }, + + new HomebrewProgram ("ninja"), + new HomebrewProgram ("p7zip", "7za"), + new HomebrewProgram ("xamarin/xamarin-android-windeps/mingw-zlib", "xamarin/xamarin-android-windeps", null), + + // If you change the minimum Mono version here, please change the URL as well + new MonoPkgProgram ("Mono", "com.xamarin.mono-MDK.pkg", Configurables.Urls.MonoPackage) { + MinimumVersion = "5.20.1.19", + MaximumVersion = "5.99.0.0", + }, + }; + + protected override void InitializeDependencies () + { + Dependencies.AddRange (programs); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/Windows.cs b/build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/Windows.cs new file mode 100644 index 000000000..8a3670a15 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/Windows.cs @@ -0,0 +1,10 @@ +namespace Xamarin.Android.Prepare +{ + partial class Windows + { + protected override void InitializeDependencies () + { + // No-op for now + } + } +} diff --git a/build-tools/xaprepare/xaprepare/ConfigAndData/Runtimes.MacOS.cs b/build-tools/xaprepare/xaprepare/ConfigAndData/Runtimes.MacOS.cs new file mode 100644 index 000000000..85ab6d8bb --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ConfigAndData/Runtimes.MacOS.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace Xamarin.Android.Prepare +{ + partial class Runtimes + { + partial void AddMacOSBundleItems (List bundleItems) + { + bundleItems.AddRange (MacOSBundleItems); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/ConfigAndData/Runtimes.Unix.cs b/build-tools/xaprepare/xaprepare/ConfigAndData/Runtimes.Unix.cs new file mode 100644 index 000000000..d7df1650c --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ConfigAndData/Runtimes.Unix.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace Xamarin.Android.Prepare +{ + partial class Runtimes + { + partial void AddUnixBundleItems (List bundleItems) + { + bundleItems.AddRange (UnixBundleItems); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/ConfigAndData/Runtimes.cs b/build-tools/xaprepare/xaprepare/ConfigAndData/Runtimes.cs new file mode 100644 index 000000000..51b102196 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ConfigAndData/Runtimes.cs @@ -0,0 +1,869 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace Xamarin.Android.Prepare +{ + partial class Runtimes + { + static Context ctx => Context.Instance; + + public readonly List Items = new List { + new MonoJitRuntime ( + abiName: AbiNames.TargetJit.AndroidArmV7a, + enabledCheck: (Context ctx) => ctx.IsTargetJitAbiEnabled (AbiNames.TargetJit.AndroidArmV7a) + ), + + new MonoJitRuntime ( + abiName: AbiNames.TargetJit.AndroidArmV8a, + enabledCheck: (Context ctx) => ctx.IsTargetJitAbiEnabled (AbiNames.TargetJit.AndroidArmV8a) + ), + + new MonoJitRuntime ( + abiName: AbiNames.TargetJit.AndroidX86, + enabledCheck: (Context ctx) => ctx.IsTargetJitAbiEnabled (AbiNames.TargetJit.AndroidX86) + ), + + new MonoJitRuntime ( + abiName: AbiNames.TargetJit.AndroidX86_64, + enabledCheck: (Context ctx) => ctx.IsTargetJitAbiEnabled (AbiNames.TargetJit.AndroidX86_64) + ), + + new MonoHostRuntime ( + name: AbiNames.HostJit.Linux, + mingw: false, + enabledCheck: (Context ctx) => ctx.IsHostJitAbiEnabled (AbiNames.HostJit.Linux) + ), + + new MonoHostRuntime ( + name: AbiNames.HostJit.Darwin, + mingw: false, + enabledCheck: (Context ctx) => ctx.IsHostJitAbiEnabled (AbiNames.HostJit.Darwin) + ), + + new MonoHostRuntime ( + name: AbiNames.HostJit.Win32, + mingw: true, + enabledCheck: (Context ctx) => ctx.IsHostJitAbiEnabled (AbiNames.HostJit.Win32) + ), + + new MonoHostRuntime ( + name: AbiNames.HostJit.Win64, + mingw: true, + enabledCheck: (Context ctx) => ctx.IsHostJitAbiEnabled (AbiNames.HostJit.Win64) + ), + + new MonoCrossRuntime ( + name: AbiNames.CrossAot.ArmV7a, + enabledCheck: (Context ctx) => ctx.IsTargetAotAbiEnabled (AbiNames.TargetAot.ArmV7a) + ), + + new MonoCrossRuntime ( + name: AbiNames.CrossAot.ArmV8a, + enabledCheck: (Context ctx) => ctx.IsTargetAotAbiEnabled (AbiNames.TargetAot.ArmV8a) + ), + + new MonoCrossRuntime ( + name: AbiNames.CrossAot.X86, + enabledCheck: (Context ctx) => ctx.IsTargetAotAbiEnabled (AbiNames.TargetAot.X86) + ), + + new MonoCrossRuntime ( + name: AbiNames.CrossAot.X86_64, + enabledCheck: (Context ctx) => ctx.IsTargetAotAbiEnabled (AbiNames.TargetAot.X86_64) + ), + + new MonoCrossRuntime ( + name: AbiNames.CrossAot.WinArmV7a, + enabledCheck: (Context ctx) => ctx.IsTargetAotAbiEnabled (AbiNames.TargetAot.WinArmV7a) + ), + + new MonoCrossRuntime ( + name: AbiNames.CrossAot.WinArmV8a, + enabledCheck: (Context ctx) => ctx.IsTargetAotAbiEnabled (AbiNames.TargetAot.WinArmV8a) + ), + + new MonoCrossRuntime ( + name: AbiNames.CrossAot.WinX86, + enabledCheck: (Context ctx) => ctx.IsTargetAotAbiEnabled (AbiNames.TargetAot.WinX86) + ), + + new MonoCrossRuntime ( + name: AbiNames.CrossAot.WinX86_64, + enabledCheck: (Context ctx) => ctx.IsTargetAotAbiEnabled (AbiNames.TargetAot.WinX86_64) + ), + + new LlvmRuntime ( + name: AbiNames.Llvm.Host32Bit, + enabledCheck: (Context ctx) => IsLlvmRuntimeEnabled (ctx, AbiNames.Llvm.Host32Bit) + ), + + new LlvmRuntime ( + name: AbiNames.Llvm.Host64Bit, + enabledCheck: (Context ctx) => IsLlvmRuntimeEnabled (ctx, AbiNames.Llvm.Host64Bit) + ), + + new LlvmRuntime ( + name: AbiNames.Llvm.Windows32Bit, + enabledCheck: (Context ctx) => IsLlvmRuntimeEnabled (ctx, AbiNames.Llvm.Windows32Bit) + ), + + new LlvmRuntime ( + name: AbiNames.Llvm.Windows64Bit, + enabledCheck: (Context) => IsLlvmRuntimeEnabled (ctx, AbiNames.Llvm.Windows64Bit) + ), + }; + + public readonly List BclFilesToInstall = new List { + new BclFile ("I18N.dll", BclFileType.ProfileAssembly), + new BclFile ("I18N.CJK.dll", BclFileType.ProfileAssembly), + new BclFile ("I18N.MidEast.dll", BclFileType.ProfileAssembly), + new BclFile ("I18N.Other.dll", BclFileType.ProfileAssembly), + new BclFile ("I18N.Rare.dll", BclFileType.ProfileAssembly), + new BclFile ("I18N.West.dll", BclFileType.ProfileAssembly), + new BclFile ("Microsoft.CSharp.dll", BclFileType.ProfileAssembly), + new BclFile ("Mono.Btls.Interface.dll", BclFileType.ProfileAssembly), + new BclFile ("Mono.CompilerServices.SymbolWriter.dll", BclFileType.ProfileAssembly), + new BclFile ("Mono.CSharp.dll", BclFileType.ProfileAssembly), + new BclFile ("Mono.Data.Sqlite.dll", BclFileType.ProfileAssembly), + new BclFile ("Mono.Data.Tds.dll", BclFileType.ProfileAssembly), + new BclFile ("Mono.Posix.dll", BclFileType.ProfileAssembly), + new BclFile ("Mono.Security.dll", BclFileType.ProfileAssembly), + new BclFile ("mscorlib.dll", BclFileType.ProfileAssembly), + new BclFile ("System.dll", BclFileType.ProfileAssembly), + new BclFile ("System.ComponentModel.Composition.dll", BclFileType.ProfileAssembly), + new BclFile ("System.ComponentModel.DataAnnotations.dll", BclFileType.ProfileAssembly), + new BclFile ("System.Core.dll", BclFileType.ProfileAssembly), + new BclFile ("System.Data.dll", BclFileType.ProfileAssembly), + new BclFile ("System.Data.Services.Client.dll", BclFileType.ProfileAssembly), + new BclFile ("System.IdentityModel.dll", BclFileType.ProfileAssembly), + new BclFile ("System.IO.Compression.dll", BclFileType.ProfileAssembly), + new BclFile ("System.IO.Compression.FileSystem.dll", BclFileType.ProfileAssembly), + new BclFile ("System.Json.dll", BclFileType.ProfileAssembly), + new BclFile ("System.Net.dll", BclFileType.ProfileAssembly), + new BclFile ("System.Net.Http.dll", BclFileType.ProfileAssembly), + new BclFile ("System.Net.Http.WinHttpHandler.dll", BclFileType.ProfileAssembly), + new BclFile ("System.Numerics.dll", BclFileType.ProfileAssembly), + new BclFile ("System.Numerics.Vectors.dll", BclFileType.ProfileAssembly), + new BclFile ("System.Reflection.Context.dll", BclFileType.ProfileAssembly), + new BclFile ("System.Runtime.CompilerServices.Unsafe.dll", BclFileType.ProfileAssembly, excludeDebugSymbols: true), + new BclFile ("System.Runtime.Serialization.dll", BclFileType.ProfileAssembly), + new BclFile ("System.Security.dll", BclFileType.ProfileAssembly), + new BclFile ("System.ServiceModel.dll", BclFileType.ProfileAssembly), + new BclFile ("System.ServiceModel.Internals.dll", BclFileType.ProfileAssembly), + new BclFile ("System.ServiceModel.Web.dll", BclFileType.ProfileAssembly), + new BclFile ("System.Transactions.dll", BclFileType.ProfileAssembly), + new BclFile ("System.Web.Services.dll", BclFileType.ProfileAssembly), + new BclFile ("System.Windows.dll", BclFileType.ProfileAssembly, excludeDebugSymbols: true), + new BclFile ("System.Xml.dll", BclFileType.ProfileAssembly), + new BclFile ("System.Xml.Linq.dll", BclFileType.ProfileAssembly), + new BclFile ("System.Xml.Serialization.dll", BclFileType.ProfileAssembly, excludeDebugSymbols: true), + + new BclFile ("Microsoft.Win32.Primitives.dll", BclFileType.FacadeAssembly), + new BclFile ("Microsoft.Win32.Registry.dll", BclFileType.FacadeAssembly), + new BclFile ("Microsoft.Win32.Registry.AccessControl.dll", BclFileType.FacadeAssembly), + new BclFile ("netstandard.dll", BclFileType.FacadeAssembly), + new BclFile ("System.AppContext.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Buffers.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Collections.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Collections.Concurrent.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Collections.NonGeneric.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Collections.Specialized.dll", BclFileType.FacadeAssembly), + new BclFile ("System.ComponentModel.dll", BclFileType.FacadeAssembly), + new BclFile ("System.ComponentModel.Annotations.dll", BclFileType.FacadeAssembly), + new BclFile ("System.ComponentModel.EventBasedAsync.dll", BclFileType.FacadeAssembly), + new BclFile ("System.ComponentModel.Primitives.dll", BclFileType.FacadeAssembly), + new BclFile ("System.ComponentModel.TypeConverter.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Console.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Data.Common.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Data.SqlClient.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Diagnostics.Contracts.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Diagnostics.Debug.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Diagnostics.FileVersionInfo.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Diagnostics.Process.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Diagnostics.StackTrace.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Diagnostics.TextWriterTraceListener.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Diagnostics.Tools.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Diagnostics.TraceEvent.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Diagnostics.TraceSource.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Diagnostics.Tracing.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Drawing.Common.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Drawing.Primitives.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Dynamic.Runtime.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Globalization.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Globalization.Calendars.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Globalization.Extensions.dll", BclFileType.FacadeAssembly), + new BclFile ("System.IO.dll", BclFileType.FacadeAssembly), + new BclFile ("System.IO.Compression.ZipFile.dll", BclFileType.FacadeAssembly), + new BclFile ("System.IO.FileSystem.dll", BclFileType.FacadeAssembly), + new BclFile ("System.IO.FileSystem.AccessControl.dll", BclFileType.FacadeAssembly), + new BclFile ("System.IO.FileSystem.DriveInfo.dll", BclFileType.FacadeAssembly), + new BclFile ("System.IO.FileSystem.Primitives.dll", BclFileType.FacadeAssembly), + new BclFile ("System.IO.FileSystem.Watcher.dll", BclFileType.FacadeAssembly), + new BclFile ("System.IO.IsolatedStorage.dll", BclFileType.FacadeAssembly), + new BclFile ("System.IO.MemoryMappedFiles.dll", BclFileType.FacadeAssembly), + new BclFile ("System.IO.Pipes.dll", BclFileType.FacadeAssembly), + new BclFile ("System.IO.UnmanagedMemoryStream.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Linq.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Linq.Expressions.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Linq.Parallel.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Linq.Queryable.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Memory.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Net.AuthenticationManager.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Net.Cache.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Net.HttpListener.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Net.Mail.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Net.NameResolution.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Net.NetworkInformation.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Net.Ping.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Net.Primitives.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Net.Requests.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Net.Security.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Net.ServicePoint.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Net.Sockets.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Net.Utilities.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Net.WebHeaderCollection.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Net.WebSockets.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Net.WebSockets.Client.dll", BclFileType.FacadeAssembly), + new BclFile ("System.ObjectModel.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Reflection.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Reflection.DispatchProxy.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Reflection.Emit.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Reflection.Emit.ILGeneration.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Reflection.Emit.Lightweight.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Reflection.Extensions.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Reflection.Primitives.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Reflection.TypeExtensions.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Resources.Reader.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Resources.ReaderWriter.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Resources.ResourceManager.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Resources.Writer.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Runtime.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Runtime.CompilerServices.VisualC.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Runtime.Extensions.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Runtime.Handles.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Runtime.InteropServices.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Runtime.InteropServices.RuntimeInformation.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Runtime.InteropServices.WindowsRuntime.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Runtime.Loader.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Runtime.Numerics.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Runtime.Serialization.Formatters.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Runtime.Serialization.Json.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Runtime.Serialization.Primitives.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Runtime.Serialization.Xml.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Security.AccessControl.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Security.Claims.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Security.Cryptography.Algorithms.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Security.Cryptography.Cng.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Security.Cryptography.Csp.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Security.Cryptography.DeriveBytes.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Security.Cryptography.Encoding.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Security.Cryptography.Encryption.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Security.Cryptography.Encryption.Aes.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Security.Cryptography.Encryption.ECDiffieHellman.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Security.Cryptography.Encryption.ECDsa.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Security.Cryptography.Hashing.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Security.Cryptography.Hashing.Algorithms.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Security.Cryptography.OpenSsl.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Security.Cryptography.Pkcs.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Security.Cryptography.Primitives.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Security.Cryptography.ProtectedData.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Security.Cryptography.RandomNumberGenerator.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Security.Cryptography.RSA.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Security.Cryptography.X509Certificates.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Security.Principal.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Security.Principal.Windows.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Security.SecureString.dll", BclFileType.FacadeAssembly), + new BclFile ("System.ServiceModel.Duplex.dll", BclFileType.FacadeAssembly), + new BclFile ("System.ServiceModel.Http.dll", BclFileType.FacadeAssembly), + new BclFile ("System.ServiceModel.NetTcp.dll", BclFileType.FacadeAssembly), + new BclFile ("System.ServiceModel.Primitives.dll", BclFileType.FacadeAssembly), + new BclFile ("System.ServiceModel.Security.dll", BclFileType.FacadeAssembly), + new BclFile ("System.ServiceProcess.ServiceController.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Text.Encoding.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Text.Encoding.CodePages.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Text.Encoding.Extensions.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Text.RegularExpressions.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Threading.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Threading.AccessControl.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Threading.Overlapped.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Threading.Tasks.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Threading.Tasks.Extensions.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Threading.Tasks.Parallel.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Threading.Thread.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Threading.ThreadPool.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Threading.Timer.dll", BclFileType.FacadeAssembly), + new BclFile ("System.ValueTuple.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Xml.ReaderWriter.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Xml.XDocument.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Xml.XmlDocument.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Xml.XmlSerializer.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Xml.XPath.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Xml.XPath.XDocument.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Xml.XPath.XmlDocument.dll", BclFileType.FacadeAssembly), + new BclFile ("System.Xml.Xsl.Primitives.dll", BclFileType.FacadeAssembly), + }; + + // These two are populated from BclFilesToInstall in the constructor + public readonly List DesignerHostBclFilesToInstall; + public readonly List DesignerWindowsBclFilesToInstall; + + public static readonly List TestAssemblies = new List { + new TestAssembly ("BinarySerializationOverVersionsTest.dll", TestAssemblyType.NUnit), + new TestAssembly ("monodroid_corlib_test.dll", TestAssemblyType.NUnit), + new TestAssembly ("monodroid_corlib_xunit-test.dll", TestAssemblyType.XUnit), + new TestAssembly ("monodroid_I18N.CJK_test.dll", TestAssemblyType.NUnit), + new TestAssembly ("monodroid_I18N.MidEast_test.dll", TestAssemblyType.NUnit), + new TestAssembly ("monodroid_I18N.Other_test.dll", TestAssemblyType.NUnit), + new TestAssembly ("monodroid_I18N.Rare_test.dll", TestAssemblyType.NUnit), + new TestAssembly ("monodroid_I18N.West_test.dll", TestAssemblyType.NUnit), + new TestAssembly ("monodroid_Microsoft.CSharp_xunit-test.dll", TestAssemblyType.NUnit), + new TestAssembly ("monodroid_Mono.CSharp_test.dll", TestAssemblyType.NUnit), + new TestAssembly ("monodroid_Mono.Data.Sqlite_test.dll", TestAssemblyType.NUnit), + new TestAssembly ("monodroid_Mono.Data.Tds_test.dll", TestAssemblyType.NUnit), + new TestAssembly ("monodroid_Mono.Posix_test.dll", TestAssemblyType.NUnit), + new TestAssembly ("monodroid_Mono.Security_test.dll", TestAssemblyType.NUnit), + new TestAssembly ("monodroid_System.ComponentModel.Composition_xunit-test.dll", TestAssemblyType.XUnit), + new TestAssembly ("monodroid_System.ComponentModel.DataAnnotations_test.dll", TestAssemblyType.NUnit), + new TestAssembly ("monodroid_System.Core_test.dll", TestAssemblyType.NUnit), + new TestAssembly ("monodroid_System.Core_xunit-test.dll", TestAssemblyType.XUnit), + new TestAssembly ("monodroid_System.Data_test.dll", TestAssemblyType.NUnit), + new TestAssembly ("monodroid_System.Data_xunit-test.dll", TestAssemblyType.XUnit), + new TestAssembly ("monodroid_System.IO.Compression.FileSystem_test.dll", TestAssemblyType.NUnit), + new TestAssembly ("monodroid_System.IO.Compression_test.dll", TestAssemblyType.NUnit), + new TestAssembly ("monodroid_System.Json_test.dll", TestAssemblyType.NUnit), + new TestAssembly ("monodroid_System.Json_xunit-test.dll", TestAssemblyType.XUnit), + new TestAssembly ("monodroid_System.Net.Http_test.dll", TestAssemblyType.NUnit), + new TestAssembly ("monodroid_System.Net.Http.FunctionalTests_xunit-test.dll", TestAssemblyType.XUnit), + new TestAssembly ("monodroid_System.Net.Http.UnitTests_xunit-test.dll", TestAssemblyType.XUnit), + new TestAssembly ("monodroid_System.Numerics_test.dll", TestAssemblyType.NUnit), + new TestAssembly ("monodroid_System.Numerics_xunit-test.dll", TestAssemblyType.XUnit), + new TestAssembly ("monodroid_System.Runtime.CompilerServices.Unsafe_xunit-test.dll", TestAssemblyType.XUnit), + new TestAssembly ("monodroid_System.Runtime.Serialization_test.dll", TestAssemblyType.NUnit), + new TestAssembly ("monodroid_System.Runtime.Serialization_xunit-test.dll", TestAssemblyType.XUnit), + new TestAssembly ("monodroid_System.Security_test.dll", TestAssemblyType.NUnit), + new TestAssembly ("monodroid_System.Security_xunit-test.dll", TestAssemblyType.XUnit), + new TestAssembly ("monodroid_System.ServiceModel.Web_test.dll", TestAssemblyType.NUnit), + new TestAssembly ("monodroid_System.ServiceModel_test.dll", TestAssemblyType.NUnit), + new TestAssembly ("monodroid_System.Transactions_test.dll", TestAssemblyType.NUnit), + new TestAssembly ("monodroid_System.Web.Services_test.dll", TestAssemblyType.NUnit), + new TestAssembly ("monodroid_System.Xml.Linq_test.dll", TestAssemblyType.NUnit), + new TestAssembly ("monodroid_System.Xml.Linq_xunit-test.dll", TestAssemblyType.XUnit), + new TestAssembly ("monodroid_System.Xml_test.dll", TestAssemblyType.NUnit), + new TestAssembly ("monodroid_System.Xml_xunit-test.dll", TestAssemblyType.XUnit), + new TestAssembly ("monodroid_System_test.dll", TestAssemblyType.NUnit), + new TestAssembly ("monodroid_System_xunit-test.dll", TestAssemblyType.XUnit), + new TestAssembly ("System.Reflection.TestModule.dll", TestAssemblyType.NUnit, excludeDebugSymbols: true), + new TestAssembly ("TestLoadAssembly.dll", TestAssemblyType.NUnit), + + // Mono.CSharp testsuite dynamically loads Microsoft.CSharp + new TestAssembly ("Microsoft.CSharp.dll", TestAssemblyType.Reference), + + // This is referenced by monodroid_corlib_xunit-test.dll + new TestAssembly ("System.Runtime.CompilerServices.Unsafe.dll", TestAssemblyType.Reference, excludeDebugSymbols: true), + + // Satellite assemblies + new TestAssembly (Path.Combine ("es-ES", "monodroid_corlib_test.resources.dll"), TestAssemblyType.Satellite), + new TestAssembly (Path.Combine ("nn-NO", "monodroid_corlib_test.resources.dll"), TestAssemblyType.Satellite), + + // Other + new TestAssembly ("nunitlite.dll", TestAssemblyType.TestRunner), + }; + + public static readonly Dictionary FrameworkListVersionOverrides = new Dictionary (StringComparer.Ordinal) { + { "System.Buffers", "4.0.99.0" }, + { "System.Memory", "4.0.99.0" } + }; + + public static readonly string FrameworkListRedist = "MonoAndroid"; + public static readonly string FrameworkListName = "Xamarin.Android Base Class Libraries"; + + public readonly List UtilityFilesToInstall = new List { + new MonoUtilityFile ("mono-cil-strip.exe", targetName: "cil-strip.exe"), + new MonoUtilityFile ("illinkanalyzer.exe", remap: true), + new MonoUtilityFile ("mdoc.exe", remap: true), + new MonoUtilityFile ("mono-symbolicate.exe", remap: true), + new MonoUtilityFile ("mkbundle.exe", remap: true), + new MonoUtilityFile ("monodoc.dll"), + new MonoUtilityFile ("monodoc.dll.config", ignoreDebugInfo: true), + new MonoUtilityFile ("mono-api-html.exe", remap: true), + new MonoUtilityFile ("mono-api-info.exe", remap: true), + }; + + /// + /// These are source:destination relative paths for source files we are to install. Sources are relative to + /// the mono submodule directory or absolute, while destinations are relative to or absolute. Note that the destination must include the + /// file name. + /// + public readonly List RuntimeFilesToInstall = new List { + new RuntimeFile ( + sourceCreator: (Runtime runtime) => Path.Combine (GetAndroidInputRootDir (runtime), "share", "mono-2.0", "mono", "eglib", "eglib-config.h"), + destinationCreator:(Runtime runtime) => Path.Combine (Configurables.Paths.OutputIncludeDir, runtime.PrefixedName, "eglib", "eglib-config.h"), + shouldSkip: (Runtime runtime) => !IsHostOrTargetRuntime (runtime) + ), + + new RuntimeFile ( + sourceCreator: (Runtime runtime) => Path.Combine (Configurables.Paths.MonoProfileDir, "Consts.cs"), + destinationCreator: (Runtime runtime) => Path.Combine (Configurables.Paths.OutputIncludeDir, "Consts.cs"), + strip: false, + shared: true + ), + + // Stripped runtime + new RuntimeFile ( + sourceCreator: (Runtime runtime) => GetRuntimeOutputSourcePath (runtime), + destinationCreator: (Runtime runtime) => GetRuntimeOutputDestinationPath (runtime, debug: false), + shouldSkip: (Runtime runtime) => !IsHostOrTargetRuntime (runtime), + type: RuntimeFileType.StrippableBinary + ), + + // Unstripped runtime + new RuntimeFile ( + sourceCreator: (Runtime runtime) => GetRuntimeOutputSourcePath (runtime), + destinationCreator: (Runtime runtime) => GetRuntimeOutputDestinationPath (runtime, debug: true), + shouldSkip: (Runtime runtime) => !IsHostOrTargetRuntime (runtime), + type: RuntimeFileType.StrippableBinary, + strip: false + ), + + // Stripped libmono-native for Mac, copied from libmono-native-compat + new RuntimeFile ( + sourceCreator: (Runtime runtime) => GetMonoNativeOutputSourcePath (runtime), + destinationCreator: (Runtime runtime) => GetMonoNativeOutputDestinationPath (runtime, debug: false), + shouldSkip: (Runtime runtime) => !IsHostOrTargetRuntime (runtime) || !IsAbi (runtime, AbiNames.HostJit.Darwin), + type: RuntimeFileType.StrippableBinary + ), + + // Unstripped libmono-native for Mac, copied from libmono-native-compat + new RuntimeFile ( + sourceCreator: (Runtime runtime) => GetMonoNativeOutputSourcePath (runtime), + destinationCreator: (Runtime runtime) => GetMonoNativeOutputDestinationPath (runtime, debug: true), + shouldSkip: (Runtime runtime) => !IsHostOrTargetRuntime (runtime) || !IsAbi (runtime, AbiNames.HostJit.Darwin), + type: RuntimeFileType.StrippableBinary, + strip: false + ), + + // Stripped libmono-native for everything except: Mac, Win32, Win64, cross runtimes + new RuntimeFile ( + sourceCreator: (Runtime runtime) => GetMonoNativeOutputSourcePath (runtime), + destinationCreator: (Runtime runtime) => GetMonoNativeOutputDestinationPath (runtime, debug: false), + shouldSkip: (Runtime runtime) => !IsHostOrTargetRuntime (runtime) || IsAbi (runtime, AbiNames.HostJit.Darwin, AbiNames.HostJit.Win32, AbiNames.HostJit.Win64), + type: RuntimeFileType.StrippableBinary + ), + + // Untripped libmono-native for everything except: Mac, Win32, Win64, cross runtimes + new RuntimeFile ( + sourceCreator: (Runtime runtime) => GetMonoNativeOutputSourcePath (runtime), + destinationCreator: (Runtime runtime) => GetMonoNativeOutputDestinationPath (runtime, debug: true), + shouldSkip: (Runtime runtime) => !IsHostOrTargetRuntime (runtime) || IsAbi (runtime, AbiNames.HostJit.Darwin, AbiNames.HostJit.Win32, AbiNames.HostJit.Win64), + type: RuntimeFileType.StrippableBinary, + strip: false + ), + + // Unstripped host mono binary + new RuntimeFile ( + sourceCreator: (Runtime runtime) => Path.Combine (GetAndroidInputRootDir (runtime), "bin", "mono"), + destinationCreator: (Runtime runtime) => Path.Combine (runtime.Name, "mono"), + shouldSkip: (Runtime runtime) => !IsRuntimeType (runtime) || IsAbi (runtime, AbiNames.HostJit.Win32, AbiNames.HostJit.Win64), + type: RuntimeFileType.StrippableBinary, + strip: false + ), + + // Stripped cross runtime + new RuntimeFile ( + sourceCreator: (Runtime runtime) => GetCrossRuntimeOutputSourcePath (runtime), + destinationCreator: (Runtime runtime) => GetCrossRuntimeOutputDestinationPath (runtime, debug: false), + shouldSkip: (Runtime runtime) => !IsRuntimeType (runtime), + type: RuntimeFileType.StrippableBinary + ), + + // Untripped cross runtime + new RuntimeFile ( + sourceCreator: (Runtime runtime) => GetCrossRuntimeOutputSourcePath (runtime), + destinationCreator: (Runtime runtime) => GetCrossRuntimeOutputDestinationPath (runtime, debug: true), + shouldSkip: (Runtime runtime) => !IsRuntimeType (runtime), + type: RuntimeFileType.StrippableBinary, + strip: false + ), + + // Stripped profiler + new RuntimeFile ( + sourceCreator: (Runtime runtime) => GetProfilerOutputSourcePath (runtime), + destinationCreator: (Runtime runtime) => GetProfilerOutputDestinationPath (runtime, debug: false), + shouldSkip: (Runtime runtime) => !IsHostOrTargetRuntime (runtime) || String.IsNullOrEmpty (runtime.As().OutputProfilerFilename), + type: RuntimeFileType.StrippableBinary + ), + + // Unstripped profiler + new RuntimeFile ( + sourceCreator: (Runtime runtime) => GetProfilerOutputSourcePath (runtime), + destinationCreator: (Runtime runtime) => GetProfilerOutputDestinationPath (runtime, debug: true), + shouldSkip: (Runtime runtime) => !IsHostOrTargetRuntime (runtime) || String.IsNullOrEmpty (runtime.As().OutputProfilerFilename), + type: RuntimeFileType.StrippableBinary, + strip: false + ), + + // Stripped AOT profiler + new RuntimeFile ( + sourceCreator: (Runtime runtime) => GetAotProfilerOutputSourcePath (runtime), + destinationCreator: (Runtime runtime) => GetAotProfilerOutputDestinationPath (runtime, debug: false), + shouldSkip: (Runtime runtime) => !IsHostOrTargetRuntime (runtime) || String.IsNullOrEmpty (runtime.As().OutputAotProfilerFilename), + type: RuntimeFileType.StrippableBinary + ), + + // Unstripped AOT profiler + new RuntimeFile ( + sourceCreator: (Runtime runtime) => GetAotProfilerOutputSourcePath (runtime), + destinationCreator: (Runtime runtime) => GetAotProfilerOutputDestinationPath (runtime, debug: true), + shouldSkip: (Runtime runtime) => !IsHostOrTargetRuntime (runtime) || String.IsNullOrEmpty (runtime.As().OutputAotProfilerFilename), + type: RuntimeFileType.StrippableBinary, + strip: false + ), + + // Stripped BTLS + new RuntimeFile ( + sourceCreator: (Runtime runtime) => GetMonoBtlsOutputSourcePath (runtime), + destinationCreator: (Runtime runtime) => GetMonoBtlsOutputDestinationPath (runtime, debug: false), + shouldSkip: (Runtime runtime) => !IsHostOrTargetRuntime (runtime) || String.IsNullOrEmpty (runtime.As().OutputMonoBtlsFilename), + type: RuntimeFileType.StrippableBinary + ), + + // Unstripped BTLS + new RuntimeFile ( + sourceCreator: (Runtime runtime) => GetMonoBtlsOutputSourcePath (runtime), + destinationCreator: (Runtime runtime) => GetMonoBtlsOutputDestinationPath (runtime, debug: true), + shouldSkip: (Runtime runtime) => !IsHostOrTargetRuntime (runtime) || String.IsNullOrEmpty (runtime.As().OutputMonoBtlsFilename), + type: RuntimeFileType.StrippableBinary, + strip: false + ), + + // Stripped MonoPosixHelper + new RuntimeFile ( + sourceCreator: (Runtime runtime) => GetMonoPosixHelperOutputSourcePath (runtime), + destinationCreator: (Runtime runtime) => GetMonoPosixHelperOutputDestinationPath (runtime, debug: false), + shouldSkip: (Runtime runtime) => !IsHostOrTargetRuntime (runtime) || String.IsNullOrEmpty (runtime.As().OutputMonoPosixHelperFilename), + type: RuntimeFileType.StrippableBinary + ), + + // Unstripped MonoPosixHelper + new RuntimeFile ( + sourceCreator: (Runtime runtime) => GetMonoPosixHelperOutputSourcePath (runtime), + destinationCreator: (Runtime runtime) => GetMonoPosixHelperOutputDestinationPath (runtime, debug: true), + shouldSkip: (Runtime runtime) => !IsHostOrTargetRuntime (runtime) || String.IsNullOrEmpty (runtime.As().OutputMonoPosixHelperFilename), + type: RuntimeFileType.StrippableBinary, + strip: false + ), + + // LLVM opt + new RuntimeFile ( + sourceCreator: (Runtime runtime) => Path.Combine (GetLlvmOutputSourcePath(runtime), $"opt{runtime.As().ExeSuffix}"), + destinationCreator: (Runtime runtime) => Path.Combine (GetLlvmOutputDestinationPath (runtime), $"opt{runtime.As().ExeSuffix}"), + shouldSkip: (Runtime runtime) => !IsRuntimeType (runtime) || !runtime.As().InstallBinaries, + type: RuntimeFileType.StrippableBinary + ), + + // LLVM llc + new RuntimeFile ( + sourceCreator: (Runtime runtime) => Path.Combine (GetLlvmOutputSourcePath(runtime), $"llc{runtime.As().ExeSuffix}"), + destinationCreator: (Runtime runtime) => Path.Combine (GetLlvmOutputDestinationPath (runtime), $"llc{runtime.As().ExeSuffix}"), + shouldSkip: (Runtime runtime) => !IsRuntimeType (runtime) || !runtime.As().InstallBinaries, + type: RuntimeFileType.StrippableBinary + ) + }; + + // If some assemblies don't exist in the Designer BCL set, list them here + static readonly Dictionary DesignerIgnoreFiles = new Dictionary { + { "Mono.Btls.Interface.dll", (Type: BclFileType.ProfileAssembly, Target: BclFileTarget.DesignerWindows) }, + }; + + static readonly List UnixBundleItems = new List { + new BundleItem ( + sourcePath: Path.Combine (Configurables.Paths.InstallMSBuildDir, "libzip.dll"), + shouldInclude: (Context ctx) => ctx.WindowsJitAbisEnabled + ), + + new BundleItem ( + sourcePath: Path.Combine (Configurables.Paths.InstallMSBuildDir, "x64", "libzip.dll"), + shouldInclude: (Context ctx) => ctx.WindowsJitAbisEnabled + ), + }; + + static readonly List MacOSBundleItems = new List { + new BundleItem ( + sourcePath: Path.Combine (Configurables.Paths.InstallMSBuildDir, "libzip.5.0.dylib") + ), + }; + + public List BundleItems { + get { + if (bundleItems != null) + return bundleItems; + + bundleItems = new List (); + bundleItems.AddRange (BclFilesToInstall); + bundleItems.AddRange (DesignerHostBclFilesToInstall); + bundleItems.AddRange (DesignerWindowsBclFilesToInstall); + bundleItems.AddRange (TestAssemblies); + bundleItems.AddRange (RuntimeFilesToInstall); + bundleItems.AddRange (UtilityFilesToInstall); + bundleItems.Add (Configurables.Paths.FrameworkListInstallPath); + bundleItems.Add (Configurables.Paths.BCLTestsArchivePath); + + AddUnixBundleItems (bundleItems); + AddMacOSBundleItems (bundleItems); + + return bundleItems; + } + } + + partial void AddUnixBundleItems (List bundleItems); + partial void AddMacOSBundleItems (List bundleItems); + + /// + /// List of directories we'll be installing to. All the directories will be removed recursively before + /// installation starts. This is to ensure that no artifacts from previous builds remain. + /// + public readonly List OutputDirectories = new List { + Configurables.Paths.BCLTestsDestDir, + Configurables.Paths.InstallMSBuildDir, + Configurables.Paths.InstallBCLFrameworkDir, + Configurables.Paths.OutputIncludeDir, + }; + + static string GetMonoUtilitySourcePath (string utilityName) + { + return Path.Combine (Configurables.Paths.MonoProfileToolsDir, utilityName); + } + + static string GetLlvmOutputSourcePath (Runtime runtime) + { + var llvmRuntime = EnsureRuntimeType (runtime, "LLVM"); + return Path.Combine (GetLlvmInputDir (runtime), "bin"); + } + + static string GetLlvmOutputDestinationPath (Runtime runtime) + { + var llvmRuntime = EnsureRuntimeType (runtime, "LLVM"); + return llvmRuntime.InstallPath; + } + + static string GetMonoPosixHelperOutputSourcePath (Runtime runtime) + { + var monoRuntime = EnsureRuntimeType (runtime, "Mono"); + return Path.Combine (GetAndroidInputLibDir (runtime), monoRuntime.NativeLibraryDirPrefix, $"{monoRuntime.OutputMonoPosixHelperFilename}{monoRuntime.NativeLibraryExtension}"); + } + + static string GetMonoPosixHelperOutputDestinationPath (Runtime runtime, bool debug) + { + var monoRuntime = EnsureRuntimeType (runtime, "Mono"); + return Path.Combine (GetRuntimeOutputDir (runtime), $"{monoRuntime.OutputMonoPosixHelperFilename}{GetDebugInfix (debug)}{monoRuntime.NativeLibraryExtension}"); + } + + static string GetMonoBtlsOutputSourcePath (Runtime runtime) + { + var monoRuntime = EnsureRuntimeType (runtime, "Mono"); + return Path.Combine (GetAndroidInputLibDir (runtime), monoRuntime.NativeLibraryDirPrefix, $"{monoRuntime.OutputMonoBtlsFilename}{monoRuntime.NativeLibraryExtension}"); + } + + static string GetMonoBtlsOutputDestinationPath (Runtime runtime, bool debug) + { + var monoRuntime = EnsureRuntimeType (runtime, "Mono"); + return Path.Combine (GetRuntimeOutputDir (runtime), $"{monoRuntime.OutputMonoBtlsFilename}{GetDebugInfix (debug)}{monoRuntime.NativeLibraryExtension}"); + } + + static string GetAotProfilerOutputSourcePath (Runtime runtime) + { + var monoRuntime = EnsureRuntimeType (runtime, "Mono"); + return Path.Combine (GetAndroidInputLibDir (runtime), monoRuntime.NativeLibraryDirPrefix, $"{monoRuntime.OutputAotProfilerFilename}{monoRuntime.NativeLibraryExtension}"); + } + + static string GetAotProfilerOutputDestinationPath (Runtime runtime, bool debug) + { + var monoRuntime = EnsureRuntimeType (runtime, "Mono"); + return Path.Combine (GetRuntimeOutputDir (runtime), $"{monoRuntime.OutputAotProfilerFilename}{GetDebugInfix (debug)}{monoRuntime.NativeLibraryExtension}"); + } + + static string GetProfilerOutputSourcePath (Runtime runtime) + { + var monoRuntime = EnsureRuntimeType (runtime, "Mono"); + return Path.Combine (GetAndroidInputLibDir (runtime), monoRuntime.NativeLibraryDirPrefix, $"{monoRuntime.OutputProfilerFilename}{monoRuntime.NativeLibraryExtension}"); + } + + static string GetProfilerOutputDestinationPath (Runtime runtime, bool debug) + { + var monoRuntime = EnsureRuntimeType (runtime, "Mono"); + return Path.Combine (GetRuntimeOutputDir (runtime), $"{monoRuntime.OutputProfilerFilename}{GetDebugInfix (debug)}{monoRuntime.NativeLibraryExtension}"); + } + + static string GetCrossRuntimeOutputSourcePath (Runtime runtime) + { + var crossRuntime = EnsureRuntimeType (runtime, "cross compilation"); + return Path.Combine (GetAndroidInputRootDir (runtime), "bin", $"{crossRuntime.ExePrefix}mono-sgen{crossRuntime.ExeSuffix}"); + } + + static string GetCrossRuntimeOutputDestinationPath (Runtime runtime, bool debug) + { + var crossRuntime = EnsureRuntimeType (runtime, "cross compilation"); + string runtimeName = $"{crossRuntime.CrossMonoName}{GetDebugInfix (debug)}{crossRuntime.ExeSuffix}"; + if (String.IsNullOrEmpty (crossRuntime.InstallPath)) + return runtimeName; + + return Path.Combine (crossRuntime.InstallPath, runtimeName); + } + + static string GetRuntimeOutputSourcePath (Runtime runtime) + { + var monoRuntime = EnsureRuntimeType (runtime, "Mono"); + return Path.Combine (GetAndroidInputLibDir (runtime), monoRuntime.NativeLibraryDirPrefix, $"{monoRuntime.OutputRuntimeFilename}{monoRuntime.NativeLibraryExtension}"); + } + + static string GetRuntimeOutputDestinationPath (Runtime runtime, bool debug) + { + var monoRuntime = EnsureRuntimeType (runtime, "Mono"); + return Path.Combine (GetRuntimeOutputDir (runtime), $"{monoRuntime.OutputRuntimeFilename}{GetDebugInfix (debug)}{monoRuntime.NativeLibraryExtension}"); + } + + static string GetMonoNativeOutputSourcePath (Runtime runtime) + { + var monoRuntime = EnsureRuntimeType (runtime, "Mono"); + if (IsAbi (runtime, AbiNames.HostJit.Darwin)) + return Path.Combine (GetAndroidInputLibDir (runtime), monoRuntime.NativeLibraryDirPrefix, $"libmono-native-compat{monoRuntime.NativeLibraryExtension}"); + + return Path.Combine (GetAndroidInputLibDir (runtime), monoRuntime.NativeLibraryDirPrefix, $"libmono-native{monoRuntime.NativeLibraryExtension}"); + } + + static string GetMonoNativeOutputDestinationPath (Runtime runtime, bool debug) + { + var monoRuntime = EnsureRuntimeType (runtime, "Mono"); + return Path.Combine (GetRuntimeOutputDir (runtime), $"libmono-native{GetDebugInfix (debug)}{monoRuntime.NativeLibraryExtension}"); + } + + static string GetDebugInfix (bool debug) + { + return debug ? Configurables.Defaults.DebugBinaryInfix : String.Empty; + } + + static bool IsHostOrTargetRuntime (Runtime runtime) + { + return IsRuntimeType (runtime) || IsRuntimeType (runtime); + } + + static T EnsureRuntimeType (Runtime runtime, string typeName) where T: Runtime + { + var ret = runtime.As (); + if (ret == null) + throw new InvalidOperationException ($"Runtime {runtime.Name} is not a {typeName} runtime"); + + return ret; + } + + static bool IsRuntimeType (Runtime runtime) where T: Runtime + { + return runtime.As() != null; + } + + static bool IsAbi (Runtime runtime, string abiName, params string[] furtherAbiNames) + { + if (ExpectedAbi (abiName)) + return true; + + if (furtherAbiNames == null) + return false; + + foreach (string a in furtherAbiNames) { + if (ExpectedAbi (a)) + return true; + } + + return false; + + bool ExpectedAbi (string abi) + { + if (String.IsNullOrEmpty (abi)) + return false; + + return String.Compare (abi, runtime.Name ?? String.Empty, StringComparison.Ordinal) == 0; + } + } + + static string GetLlvmInputDir (Runtime runtime) + { + return GetLlvmInputRootDir (runtime); + } + + static string GetLlvmInputRootDir (Runtime runtime) + { + return Path.Combine (Configurables.Paths.MonoSDKSRelativeOutputDir, $"llvm-{runtime.PrefixedName}"); + } + + static string GetAndroidInputLibDir (Runtime runtime) + { + return Path.Combine (GetAndroidInputRootDir (runtime), "lib"); + } + + static string GetAndroidInputRootDir (Runtime runtime) + { + return Path.Combine (Configurables.Paths.MonoSDKSRelativeOutputDir, $"android-{runtime.PrefixedName}-{Configurables.Defaults.MonoSdksConfiguration}"); + } + + static string GetRuntimeOutputDir (Runtime runtime) + { + return Path.Combine (Configurables.Paths.RuntimeInstallRelativeLibDir, runtime.PrefixedName); + } + + static bool IsLlvmRuntimeEnabled (Context ctx, string llvmAbi) + { + bool enabled = false; + bool windows = ctx.IsLlvmWindowsAbi (llvmAbi); + bool is64Bit = ctx.Is64BitLlvmAbi (llvmAbi); + + HashSet targets; + if (windows) + targets = is64Bit ? AbiNames.All64BitWindowsAotAbis : AbiNames.All32BitWindowsAotAbis; + else + targets = is64Bit ? AbiNames.All64BitHostAotAbis : AbiNames.All32BitHostAotAbis; + + foreach (string target in targets) { + if (Context.Instance.IsTargetAotAbiEnabled (target)) { + enabled = true; + break; + } + } + + return enabled && (!is64Bit || Context.Instance.OS.Is64Bit); + } + + public Runtimes () + { + Context c = ctx; + foreach (Runtime runtime in Items) { + runtime.Init (c); + } + + DesignerHostBclFilesToInstall = BclToDesigner (BclFileTarget.DesignerHost); + DesignerWindowsBclFilesToInstall = BclToDesigner (BclFileTarget.DesignerWindows); + + List BclToDesigner (BclFileTarget ignoreForTarget) + { + return BclFilesToInstall.Where (bf => ShouldInclude (bf, ignoreForTarget)).Select (bf => new BclFile (bf.Name, bf.Type, excludeDebugSymbols: true, version: bf.Version, target: ignoreForTarget)).ToList (); + } + + bool ShouldInclude (BclFile bf, BclFileTarget ignoreForTarget) + { + if (DesignerIgnoreFiles == null || !DesignerIgnoreFiles.TryGetValue (bf.Name, out (BclFileType Type, BclFileTarget Target) bft)) { + return true; + } + + if (bf.Type != bft.Type || bft.Target != ignoreForTarget) + return true; + + Log.Instance.DebugLine ($"BCL file {bf.Name} will NOT be included in the installed Designer BCL files ({ignoreForTarget})"); + return false; + } + } + + List bundleItems; + } +} diff --git a/build-tools/xaprepare/xaprepare/Main.cs b/build-tools/xaprepare/xaprepare/Main.cs new file mode 100644 index 000000000..55625fd00 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Main.cs @@ -0,0 +1,302 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +using Mono.Options; + +namespace Xamarin.Android.Prepare +{ + class App + { + sealed class ParsedOptions + { + public bool ShowHelp { get; set; } = false; + public bool DumpProps { get; set; } = false; + public bool NoEmoji { get; set; } = !Configurables.Defaults.UseEmoji; + public bool ForceRuntimesBuild { get; set; } = false; + public string HashAlgorithm { get; set; } = null; + public uint MakeConcurrency { get; set; } = 0; + public ExecutionMode ExecutionMode { get; set; } = Configurables.Defaults.ExecutionMode; + public LoggingVerbosity Verbosity { get; set; } = Configurables.Defaults.LoggingVerbosity; + public string DebugFileExtension { get; set; } = Configurables.Defaults.DebugFileExtension; + public string ScenarioName { get; set; } = null; + public bool ListScenarios { get; set; } = false; + public string CompressionFormat { get; set; } = Configurables.Defaults.DefaultCompressionFormat.Name; + public string Configuration { get; set; } + public bool AutoProvision { get; set; } + public bool AutoProvisionUsesSudo { get; set; } + public bool IgnoreMaxMonoVersion { get; set; } + public bool RefreshPrograms { get; set; } + public bool EnableAll { get; set; } + public string XABundlePath { get; set; } + } + + public static int Main (string[] args) + { + try { + return Run (args).Result; + } catch (AggregateException aex) { + foreach (Exception ex in aex.InnerExceptions) { + PrintException (ex); + } + } catch (Exception ex) { + PrintException (ex); + } finally { + Log.Instance.Dispose (); + ResetConsoleColors (); + } + + return 1; + + void PrintException (Exception ex) + { + Log.Instance.ErrorLine (showSeverity: false); + Log.Instance.ErrorLine (ex.Message, showSeverity: false); + Log.Instance.ErrorLine (ex.ToString (), showSeverity: false); + Log.Instance.ErrorLine (showSeverity: false); + } + } + + static void ResetConsoleColors () + { + try { + Console.CursorVisible = true; + Console.ResetColor (); + } catch { + // Ignore + } + } + + static async Task Run (string[] args) + { + Log.SetContext (Context.Instance); + + var optionErrors = new List (); + ParsedOptions parsedOptions = new ParsedOptions { + AutoProvision = ParseBoolean (Context.Instance.Properties.GetValue (KnownProperties.AutoProvision)), + AutoProvisionUsesSudo = ParseBoolean (Context.Instance.Properties.GetValue (KnownProperties.AutoProvisionUsesSudo)), + IgnoreMaxMonoVersion = ParseBoolean (Context.Instance.Properties.GetValue (KnownProperties.IgnoreMaxMonoVersion)), + }; + + var opts = new OptionSet { + "Usage: xaprepare [OPTIONS]", + $"Xamarin.Android v{BuildInfo.XAVersion} preparation utility", + "", + {"p|property={=}", "Set a {PROPERTY} to a {VALUE}", (string p, string v) => Context.Instance.Properties.Set (p, v) }, + {"d|dump-properties", "Dump values of all the defined properties to the screen", v => parsedOptions.DumpProps = true }, + {"j|make-concurrency=", "Number of concurrent jobs for make to run. A positive integer or 0 for the default. Defaults to the number of CPUs/cores", v => parsedOptions.MakeConcurrency = EnsureUInt (v, "Invalid Make concurrency value") }, + {"no-emoji", "Do not use any emoji characters in the output", v => parsedOptions.NoEmoji = true }, + {"r|run-mode=", $"Specify the execution mode: {GetExecutionModes()}. See documentation for mode descriptions. Default: {Configurables.Defaults.ExecutionMode}", v => parsedOptions.ExecutionMode = ParseExecutionMode (v)}, + {"f|build-runtimes", $"Build runtimes even if the bundle/archives are available.", v => parsedOptions.ForceRuntimesBuild = true }, + {"H|hash-algorithm=", "Use the specified hash algorithm instead of the default {Configurables.Defaults.HashAlgorithm}", v => parsedOptions.HashAlgorithm = v?.Trim () }, + {"D|debug-ext=", $"Extension of files with debug information for managed DLLs and executables. Default: {parsedOptions.DebugFileExtension}", v => parsedOptions.DebugFileExtension = v?.Trim () }, + {"v|verbosity=", $"Set console log verbosity to {{LEVEL}}. Level name may be abbreviated to the smallest unique part (one of: {GetVerbosityLevels ()}). Default: {Context.Instance.LoggingVerbosity.ToString().ToLowerInvariant ()}", v => parsedOptions.Verbosity = ParseLogVerbosity (v) }, + {"s|scenario=", "Run the specified scenario (use --ls to list all known scenarios) instead of the default one", v => parsedOptions.ScenarioName = v }, + {"ls", "List names of all known scenarios", v => parsedOptions.ListScenarios = true }, + {"cf=", $"{{NAME}} of the compression format to use for some archives (e.g. the XA bundle). One of: {GetCompressionFormatNames ()}; Default: {parsedOptions.CompressionFormat}", v => parsedOptions.CompressionFormat = v?.Trim ()}, + {"c|configuration=", $"Build {{CONFIGURATION}}. Default: {Context.Instance.Configuration}", v => parsedOptions.Configuration = v?.Trim ()}, + {"a|enable-all", "Enable preparation of all the supported targets, ABIs etc", v => parsedOptions.EnableAll = true}, + {"b|bundle-path=", "Full path to the directory where Xamarin.Android bundle can be found (excluding the file name)", v => parsedOptions.XABundlePath = v?.Trim ()}, + "", + {"auto-provision=", $"Automatically install software required by Xamarin.Android", v => parsedOptions.AutoProvision = ParseBoolean (v)}, + {"auto-provision-uses-sudo=", $"Allow use of sudo(1) when provisioning", v => parsedOptions.AutoProvisionUsesSudo = ParseBoolean (v)}, + {"ignore-max-mono-version=", $"Ignore the maximum supported Mono version restriction", v => parsedOptions.IgnoreMaxMonoVersion = ParseBoolean (v)}, + "", + {"h|help", "Show this help message", v => parsedOptions.ShowHelp = true }, + }; + + opts.Parse (args); + if (parsedOptions.ShowHelp) { + opts.WriteOptionDescriptions (Console.Out); + return 0; + } + + if (optionErrors.Count > 0) { + Log.Instance.ErrorLine ("Invalid arguments passed, please run with --help to see option documentation:"); + Log.Instance.ErrorLine (); + foreach (string errorLine in optionErrors) { + Log.Instance.ErrorLine ($" {errorLine}"); + } + } + + // If we're running without a terminal or the output is redirected we must enforce the dull mode. + if (!Context.Instance.InteractiveSession) + parsedOptions.ExecutionMode = ExecutionMode.CI; + + Context.Instance.MakeConcurrency = parsedOptions.MakeConcurrency; + Context.Instance.NoEmoji = parsedOptions.NoEmoji; + Context.Instance.ExecutionMode = parsedOptions.ExecutionMode; + Context.Instance.ForceRuntimesBuild = parsedOptions.ForceRuntimesBuild; + Context.Instance.LoggingVerbosity = parsedOptions.Verbosity; + Context.Instance.DebugFileExtension = parsedOptions.DebugFileExtension; + Context.Instance.AutoProvision = parsedOptions.AutoProvision; + Context.Instance.AutoProvisionUsesSudo = parsedOptions.AutoProvisionUsesSudo; + Context.Instance.IgnoreMaxMonoVersion = parsedOptions.IgnoreMaxMonoVersion; + Context.Instance.EnableAllTargets = parsedOptions.EnableAll; + + if (!String.IsNullOrEmpty (parsedOptions.XABundlePath)) + Context.Instance.XABundlePath = parsedOptions.XABundlePath; + + if (!String.IsNullOrEmpty (parsedOptions.Configuration)) + Context.Instance.Configuration = parsedOptions.Configuration; + + if (!String.IsNullOrEmpty (parsedOptions.HashAlgorithm)) + Context.Instance.HashAlgorithm = parsedOptions.HashAlgorithm; + + SetCompressionFormat (parsedOptions.CompressionFormat); + + if (!await Context.Instance.Init (parsedOptions.ScenarioName)) { + return 1; + } + + if (parsedOptions.DumpProps) + DumpProperties (Context.Instance); + if (parsedOptions.ListScenarios) { + ListScenarios (Context.Instance); + return 0; + } + + return await Context.Instance.Execute () ? 0 : 1; + + uint EnsureUInt (string v, string errorText) + { + if (UInt32.TryParse (v, out uint ret)) + return ret; + + optionErrors.Add (errorText); + return 0; + } + } + + static bool SetCompressionFormat (string cfName) + { + if (String.IsNullOrEmpty (cfName)) { + Log.Instance.ErrorLine ("Compression format name must be specified"); + return false; + } + + if (String.Compare (cfName, Configurables.Defaults.DefaultCompressionFormat.Name, StringComparison.OrdinalIgnoreCase) == 0) + return true; + + if (!Configurables.Defaults.CompressionFormats.TryGetValue (cfName, out CompressionFormat cf)) { + Log.Instance.ErrorLine ($"Unknown compression format name: {cfName}"); + return false; + } + + if (cf == null) + throw new InvalidOperationException ($"Valid compression format name ({cfName}) but compression format is null!"); + + Log.Instance.DebugLine ($"Setting compression format to: {cf.Description}; File extension: {cf.Extension}"); + Context.Instance.CompressionFormat = cf; + + return true; + } + + static string GetCompressionFormatNames () + { + return String.Join (", ", Configurables.Defaults.CompressionFormats.Keys); + } + + static void ListScenarios (Context context) + { + Log.Instance.StatusLine ("Known scenarios:"); + foreach (var kvp in context.Scenarios) { + Scenario scenario = kvp.Value; + if (scenario == null) + continue; + + Log.Instance.Status ($" {context.Characters.Bullet} {scenario.Name}"); + Log.Instance.StatusLine (scenario == context.DefaultScenario ? " (default)" : String.Empty, ConsoleColor.Green); + } + } + + static void DumpProperties (Context context) + { + if (context.Properties.Count == 0) { + Log.Instance.InfoLine ("No properties defined"); + return; + } + + context.Banner ("Defined properties"); + foreach (var kvp in context.Properties) { + Log.Instance.InfoLine ($"{kvp.Key} = ", kvp.Value ?? "", ConsoleColor.White, ConsoleColor.White); + } + } + + static string GetVerbosityLevels () + { + return EnumNamesToCommaSeparatedList (); + } + + static string GetExecutionModes () + { + return EnumNamesToCommaSeparatedList (); + } + + static string EnumNamesToCommaSeparatedList () + { + return String.Join (", ", GetEnumNames ()); + } + + static IEnumerable GetEnumNames () + { + return Enum.GetNames (typeof (T)).Select (v => v.ToLowerInvariant ()); + } + + static LoggingVerbosity ParseLogVerbosity (string name) + { + switch (Char.ToLowerInvariant (name [0])) { + case 's': + return LoggingVerbosity.Silent; + + case 'q': + return LoggingVerbosity.Quiet; + + case 'n': + return LoggingVerbosity.Normal; + + case 'v': + return LoggingVerbosity.Verbose; + + case 'd': + return LoggingVerbosity.Diagnostic; + + default: + throw new InvalidOperationException ($"Unknown logging verbosity level '{name}'"); + } + } + + static ExecutionMode ParseExecutionMode (string name) + { + switch (Char.ToLowerInvariant (name [0])) { + case 'c': + return ExecutionMode.CI; + + case 's': + return ExecutionMode.Standard; + + case 'i': + return ExecutionMode.Interactive; + + default: + throw new InvalidOperationException ($"Unknown execution mode '{name}'"); + } + } + + static bool ParseBoolean (string value) + { + string v = value?.Trim (); + if (String.IsNullOrEmpty (v)) + throw new ArgumentException ("must not be null or empty", nameof (v)); + + if (String.Compare ("yes", v, StringComparison.OrdinalIgnoreCase) == 0 || String.Compare ("true", v, StringComparison.OrdinalIgnoreCase) == 0) + return true; + + if (String.Compare ("no", v, StringComparison.OrdinalIgnoreCase) == 0 || String.Compare ("false", v, StringComparison.OrdinalIgnoreCase) == 0) + return false; + + throw new InvalidOperationException ($"Unknown boolean value: {value}"); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/OperatingSystems/Linux.UbuntuCommon.cs b/build-tools/xaprepare/xaprepare/OperatingSystems/Linux.UbuntuCommon.cs new file mode 100644 index 000000000..7ed70d37f --- /dev/null +++ b/build-tools/xaprepare/xaprepare/OperatingSystems/Linux.UbuntuCommon.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; + +namespace Xamarin.Android.Prepare +{ + partial class LinuxUbuntuCommon : LinuxDebianCommon + { + const string BinfmtWarningUbuntu = @" +Since you are running Ubuntu then you can disable just the CLI (Mono) and Wine interpreters +by issuing the following commands as root (or by prepending the commands below with `sudo`): + + update-binfmts --disable cli + update-binfmts --disable wine + +"; + + public override void ShowFinalNotices () + { + base.ShowFinalNotices (); + if (!WarnBinFmt) + return; + + Log.WarningLine (BinfmtWarningUbuntu, ConsoleColor.White, showSeverity: false); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/OperatingSystems/Linux.cs b/build-tools/xaprepare/xaprepare/OperatingSystems/Linux.cs new file mode 100644 index 000000000..b0dec2013 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/OperatingSystems/Linux.cs @@ -0,0 +1,149 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Xamarin.Android.Prepare +{ + abstract partial class Linux : Unix + { + const string BinfmtBaseWarning = @" +Your Linux appears to have support for binfmt_misc kernel module enabled. +The module makes it possible to execute non-Linux binaries if the appropriate +interpreter for the given format is available. +Your machine is configured to handle Windows PE executables either via Mono or +Wine. It will make the Xamarin.Android build fail IF you choose to build the +Windows cross-compilers by enabling the 'mxe-Win32' or 'mxe-Win64' host targets. + +You can disable the binfmt_misc module by issuing the following command as root +before building Xamarin.Android: + + echo 0 > /proc/sys/fs/binfmt_misc/status + +and re-enable it after building with the following command: + + echo 1 > /proc/sys/fs/binfmt_misc/status +"; + + const string FlatpakInfoPath = "/.flatpak-info"; + const string FlatpakDefaultRelease = "0.0.0"; + const string DefaultLsbReleasePath = "/usr/bin/lsb_release"; + + static readonly Dictionary> distroMap = new Dictionary> (StringComparer.OrdinalIgnoreCase) { + {"Debian", (ctx) => new LinuxDebian (ctx)}, + {"Ubuntu", (ctx) => new LinuxUbuntu (ctx)}, + {"LinuxMint", (ctx) => new LinuxMint (ctx)}, + {"Arch", (ctx) => new LinuxArch (ctx)}, + }; + + bool warnBinFmt; + string codeName; + + public override string Type { get; } = "Linux"; + public override List Dependencies { get; } + public override StringComparison DefaultStringComparison => StringComparison.Ordinal; + public override StringComparer DefaultStringComparer => StringComparer.Ordinal; + + protected bool WarnBinFmt => warnBinFmt; + protected string CodeName => codeName; + + protected Linux (Context context) + : base (context) + { + Flavor = "Generic"; + ZipExtension = "tar.bz2"; + Dependencies = new List (); + } + + public override void ShowFinalNotices () + { + if (!warnBinFmt) + return; + + Log.WarningLine (showSeverity: false); + Log.WarningLine ("*************** WARNING ***************", showSeverity: false); + Log.WarningLine (BinfmtBaseWarning, ConsoleColor.White, showSeverity: false); + } + + public static Linux DetectAndCreate (Context context) + { + string name; + string release; + string codeName; + string progPath; + + if (Directory.Exists ("/app")) { + name = "Flatpak"; + release = GetFlatpakRelease (); + codeName = String.Empty; + } else { + if (File.Exists (DefaultLsbReleasePath)) + progPath = DefaultLsbReleasePath; + else + progPath = FindProgram ("lsb_release", GetPathDirectories ()); + + if (String.IsNullOrEmpty (progPath) || !IsExecutable (progPath, true)) + throw new InvalidOperationException ("Your Linux distribution lacks a working `lsb_release` command"); + + name = Utilities.GetStringFromStdout (progPath, "-is"); + release = Utilities.GetStringFromStdout (progPath, "-rs"); + codeName = Utilities.GetStringFromStdout (progPath, "-cs"); + } + + Func creator; + if (!distroMap.TryGetValue (name, out creator)) + throw new InvalidOperationException ($"Your Linux distribution ({name} {release}) is not supported at this time."); + + Linux linux = creator (context); + linux.Name = name; + linux.Release = release; + linux.warnBinFmt = ShouldWarnAboutBinfmt (); + linux.codeName = codeName; + + Log.Instance.Todo ("Check Mono version and error out if not the required minimum version"); + return linux; + } + + static bool ShouldWarnAboutBinfmt () + { + const string procDir = "/proc/sys/fs/binfmt_misc"; + if (!Directory.Exists (procDir)) + return false; + + foreach (string trouble in new [] { "cli", "win" }) { + if (File.Exists ($"{procDir}/{trouble}")) + return true; + } + + return false; + } + + static string GetFlatpakRelease () + { + if (!File.Exists (FlatpakInfoPath)) { + Log.Instance.WarningLine ($"Unable to determine Flatpak release ({FlatpakInfoPath} does not exist)"); + return FlatpakDefaultRelease; + } + + string[] lines = File.ReadAllLines (FlatpakInfoPath); + foreach (string l in lines) { + string line = l?.Trim (); + if (String.IsNullOrEmpty (line)) + continue; + + if (!line.StartsWith ("flatpak-version", StringComparison.Ordinal)) + continue; + + string[] parts = line.Split (new [] { '=' }, 2); + if (parts.Length != 2) { + Log.Instance.WarningLine ($"Invalid version format in {FlatpakInfoPath}"); + return FlatpakDefaultRelease; + } + + return parts [1]; + } + + Log.Instance.WarningLine ($"Unable to find Flatpak version information in {FlatpakInfoPath}"); + return FlatpakDefaultRelease; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/OperatingSystems/MacOS.cs b/build-tools/xaprepare/xaprepare/OperatingSystems/MacOS.cs new file mode 100644 index 000000000..b3b207d28 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/OperatingSystems/MacOS.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Generic; + +namespace Xamarin.Android.Prepare +{ + partial class MacOS : Unix + { + const string HomebrewErrorsAdvice = @" +Some errors occurred while running one or more Homebrew commands (please check logs for details). +It may mean that there are some issues with your instance of Homebrew. In order to make sure that +this is not the case, please run the following command and follow any instructions given by it: + + brew doctor + +After all the issues are fixed, please re-run the bootstrapper. +"; + + public override string Type { get; } = "Darwin"; + public override List Dependencies { get; } + + public override StringComparison DefaultStringComparison => StringComparison.OrdinalIgnoreCase; + public override StringComparer DefaultStringComparer => StringComparer.OrdinalIgnoreCase; + + public Version Version { get; } + public string Build { get; } + public Version HomebrewVersion { get; private set; } + public bool HomebrewErrors { get; set; } + + public MacOS (Context context) + : base (context) + { + Flavor = "macOS"; + + string progPath = FindProgram ("sw_vers", GetPathDirectories ()); + if (!String.IsNullOrEmpty (progPath)) { + Name = Utilities.GetStringFromStdout (progPath, "-productName"); + + string release = Utilities.GetStringFromStdout (progPath, "-productVersion"); + string build = Utilities.GetStringFromStdout (progPath, "-buildVersion"); + Build = build; + Release = $"{release} ({build})"; + + if (!Version.TryParse (release, out Version ver)) { + Log.WarningLine ($"Unable to parse macOS version: {release}"); + Version = new Version (0, 0, 0); + } else + Version = ver; + } else { + Name = "macOS"; + Release = "0.0.0"; + Build = "Unknown"; + Version = new Version (0, 0, 0); + } + + Dependencies = new List (); + } + + protected override void PopulateEnvironmentVariables () + { + base.PopulateEnvironmentVariables (); + EnvironmentVariables ["MACOSX_DEPLOYMENT_TARGET"] = Configurables.Defaults.MacOSDeploymentTarget; + } + + protected override bool InitOS () + { + if (!base.InitOS ()) + return false; + + string brewPath = Which ("brew", false); + if (String.IsNullOrEmpty (brewPath)) { + Log.ErrorLine ("Could not find Homebrew on this system, please install it from https://brew.sh/"); + return false; + } + + Context.MonoOptions.Add ("--arch=64"); + Context.Instance.Tools.BrewPath = brewPath; + HomebrewPrefix = Utilities.GetStringFromStdout (brewPath, "--prefix"); + + (bool success, string bv) = Utilities.GetProgramVersion (brewPath); + if (!success || !Version.TryParse (bv, out Version brewVersion)) { + Log.ErrorLine ("Failed to obtain Homebrew version"); + return false; + } + + HomebrewVersion = brewVersion; + + // This is a hack since we have a chicken-and-egg problem. On mac, Configuration.props uses the + // `HostHomebrewPrefix` property which is defined in `Configuration.OperatingSystem.props` but we're here to + // *generate* the latter file, so when the bootstrapper is built `HostHomebrewPrefix` is empty and we can't + // access mingw utilities. So, we need to cheat here. + string brewPrefix = Context.Instance.Properties.GetValue (KnownProperties.AndroidMxeFullPath); + if (String.IsNullOrEmpty (brewPrefix)) + Context.Instance.Properties.Set (KnownProperties.AndroidMxeFullPath, HomebrewPrefix); + + return true; + } + + public override void ShowFinalNotices () + { + base.ShowFinalNotices (); + if (!HomebrewErrors) + return; + + Log.WarningLine (HomebrewErrorsAdvice, ConsoleColor.White, showSeverity: false); + } + }; +} diff --git a/build-tools/xaprepare/xaprepare/OperatingSystems/OS.cs b/build-tools/xaprepare/xaprepare/OperatingSystems/OS.cs new file mode 100644 index 000000000..64b4ac981 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/OperatingSystems/OS.cs @@ -0,0 +1,529 @@ +using System; +using System.IO; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + /// + /// Base class for all supported operating systems. + /// + abstract class OS : AppObject + { + /// + /// Type of the operating system (Linux, Darwin or Windows currently) + /// + public abstract string Type { get; } + + /// + /// List of programs Xamarin.Android depends on when running on the particular OS + /// + public abstract List Dependencies { get; } + + /// + /// A convenience shortcut for + /// + public Context Context { get; } + + /// + /// true if the OS is 64-bit + /// + public bool Is64Bit => Environment.Is64BitOperatingSystem; + + /// + /// Number of processors found in the host machine + /// + public uint CPUCount => (uint)Environment.ProcessorCount; + + /// + /// A dictionary of variables to export in environment of all the executed programs, scripts etc. + /// + public Dictionary EnvironmentVariables { get; } + + /// + /// Path to javac (full or relative) + /// + public string JavaCPath { get; set; } = "javac"; + + /// + /// Path to jar (full or relative) + /// + public string JarPath { get; set; } = "jar"; + + /// + /// Path to java (full or relative) + /// + public string JavaPath { get; set; } = "java"; + + /// + /// Full path to Java home, defaults to contents of the JAVA_HOME environment variable or an empty + /// string if the variable isn't present. + /// + public string JavaHome { get; set; } + + /// + /// Name of the operating system (e.g. Ubuntu, Debian, Arch etc for Linux-based operating systems, Mac OS X, Windows) + /// + public string Name { get; protected set; } + + /// + /// OS "flavor" name (i.e. a variation of the OS named ), may be equal to + /// + public string Flavor { get; protected set; } + + /// + /// Operating system release version/name + /// + public string Release { get; protected set; } + + /// + /// Host system architecture (e.g. x86, x86_64) + /// + public string Architecture { get; protected set; } + + /// + /// Path to the C compiler to build binaries with the native OS bitness + /// + public string CC { get; set; } + + /// + /// Path to the C compiler to build binaries with 32-bit bitness + /// + public string CC32 { get; set; } + + /// + /// Path to the C compiler to build binaries with 64-bit bitness + /// + public string CC64 { get; set; } + + /// + /// Path to the C++ compiler to build binaries with the native OS bitness + /// + public string CXX { get; set; } + + /// + /// Path to the C+ compiler to build binaries with 32-bit bitness + /// + public string CXX32 { get; set; } + + /// + /// Path to the C compiler to build binaries with 64-bit bitness + /// + public string CXX64 { get; set; } + + /// + /// Default compiler target triple + /// + public string Triple { get; set; } + + /// + /// 32-bit compiler target triple + /// + public string Triple32 { get; set; } + + /// + /// 64-bit compiler target triple + /// + public string Triple64 { get; set; } + + /// + /// Prefix where Homebrew is installed (relevant only on macOS, but present in all operating system for + /// compatibility reasons) + /// + public string HomebrewPrefix { get; set; } = String.Empty; + + /// + /// File extension used for ZIP archives + /// + public string ZipExtension { get; protected set; } = "zip"; + + /// + /// Return full path to the user's home directory + /// + public abstract string HomeDirectory { get; } + + /// + /// Extensions of executable files as supported by the host OS or null if executable extensions aren't + /// used/needed + /// + protected abstract List ExecutableExtensions { get; } + + /// + /// Default string comparison for path comparison + /// + public abstract StringComparison DefaultStringComparison { get; } + + /// + /// Default string comparer for path comparison + /// + public abstract StringComparer DefaultStringComparer { get; } + + /// + /// true if we are running on Windows + /// + public abstract bool IsWindows { get; } + + /// + /// true if we are running on Unix + /// + public abstract bool IsUnix { get; } + + /// + /// Returns path to the managed program "runner" (most commonly a .NET runtime) used by the host OS or null if + /// no such runner is needed. + /// + public abstract string GetManagedProgramRunner (string programPath); + + /// + /// Initialize base OS support properties, variables etc. Implementation should perform as little detection of + /// programs etc as possible + /// + protected abstract bool InitOS (); + + /// + /// Initialize for the host OS + /// + protected abstract void InitializeDependencies (); + + /// + /// Assert that the program passed in is in fact executable and throw an exception if + /// it is not. + /// + protected abstract string AssertIsExecutable (string fullPath); + + /// + /// Detect the required C and C++ compilers on the system. + /// + protected abstract void DetectCompilers (); + + protected OS (Context context) + { + if (context == null) + throw new ArgumentNullException (nameof (context)); + Context = context; + EnvironmentVariables = new Dictionary (StringComparer.Ordinal); + } + + /// + /// Override to print notices/information/etc at the very end of bootstrapper run. + /// + public virtual void ShowFinalNotices () + {} + + /// + /// + /// Initialize OS support. Initializes basic OS properties (by calling ), dependencies (by + /// calling ) as well as makes sure that all the dependencies are + /// installed and initializes the environment. + /// + /// + /// Missing dependencies are installed only if + /// condition is set and and is true. If the two conditions aren't + /// met and missing programs are found, the initialization fails unless the is set to true in which case only a warning is + /// printed regarding the missing dependencies. + /// + /// + public async Task Init () + { + if (!InitOS ()) + throw new InvalidOperationException ("Failed to initialize operating system support"); + + InitializeDependencies (); + + Context.Banner ("Ensuring all required programs are installed"); + if (!await EnsureDependencies ()) + return false; + + Context.Banner ("Configuring environment"); + ConfigureEnvironment (); + return true; + } + + async Task EnsureDependencies () + { + if (Dependencies == null) + throw new InvalidOperationException ("Dependencies not set"); + + Log.Todo ("Implement 'package refresh' mode where we reinstall packages/programs forcibly"); + + int maxNameLength = GetMaxNameLength (Dependencies); + var missing = new List (); + foreach (Program p in Dependencies) { + if (p == null) + continue; + + Log.Status ($"Checking ", $"{p.Name}".PadRight (maxNameLength), tailColor: ConsoleColor.White); + bool installed = await p.IsInstalled (); + if (installed) { + if (!p.InstalledButWrongVersion) { + Log.StatusLine ($" [FOUND {p.CurrentVersion}]", Context.SuccessColor); + } else { + Log.StatusLine ($" [WRONG VERSION {p.CurrentVersion}]", Context.WarningColor); + missing.Add (p); + } + } else { + Log.StatusLine (" [MISSING]", Context.FailureColor); + missing.Add (p); + } + } + + if (missing.Count == 0) + return true; + + bool ignoreMissing = Context.Instance.CheckCondition (KnownConditions.IgnoreMissingPrograms); + if (!Context.Instance.AutoProvision) { + string message = "Some programs are missing or have invalid versions, but automatic provisioning is disabled"; + if (ignoreMissing) { + Log.WarningLine ($"{message}. Ignoring missing programs."); + return true; + } + + Log.ErrorLine (message); + return false; + } + + maxNameLength = GetMaxNameLength (missing); + Context.Banner ("Installing programs"); + if (missing.Any (p => p.NeedsSudoToInstall)) + Log.StatusLine ("You might be prompted for your sudo password"); + + bool someFailed = false; + foreach (Program p in missing) { + if (p.NeedsSudoToInstall && !Context.AutoProvisionUsesSudo) { + Log.ErrorLine ($"Program '{p.Name}' requires sudo to install but sudo is disabled"); + someFailed = true; + continue; + } + + if (!p.CanInstall ()) { + if (!ignoreMissing) + someFailed = true; + Log.Status ("Installation disabled for "); + Log.StatusLine (p.Name.PadRight (maxNameLength), ConsoleColor.Cyan); + continue; + } + + Log.Status ("Installing "); + Log.StatusLine (p.Name.PadRight (maxNameLength), ConsoleColor.White); + bool success = await p.Install (); + Log.StatusLine (); + if (success) + continue; + + someFailed = true; + Log.ErrorLine ($"Installation of {p.Name} failed"); + } + + if (someFailed) + throw new InvalidOperationException ("Failed to install some required programs."); + + return true; + + int GetMaxNameLength (List list) + { + int ret = 0; + list.ForEach (p => { + int len = p.Name.Length; + if (len < ret) + return; + ret = len; + } + ); + ret++; + + return ret; + } + } + + void ConfigureEnvironment () + { + PopulateEnvironmentVariables (); + + foreach (var kvp in EnvironmentVariables) { + string name = kvp.Key?.Trim (); + if (String.IsNullOrEmpty (name)) + continue; + + string value = kvp.Value ?? String.Empty; + Log.DebugLine ($"Setting environment variable: {name} = {value}"); + Environment.SetEnvironmentVariable (name, value); + } + + DetectCompilers (); + } + + /// + /// Verifies whether the configured compilers can in fact be found, throws an exception if they aren't. + /// + protected void VerifyCompilersExist () + { + Log.DebugLine ($"Verifying {CC} compiler name is set"); + AssertCompilerSet (CC, "C"); + + Log.DebugLine ($"Verifying {CXX} compiler name is set"); + AssertCompilerSet (CXX, "C++"); + + Log.DebugLine ($"Verifying {CC} compiler exists"); + AssertCompilerFound (Which (CC), CC, "C"); + + Log.DebugLine ($"Verifying {CXX} compiler exists"); + AssertCompilerFound (Which (CXX), CXX, "C++"); + + void AssertCompilerFound (string path, string variable, string name) + { + if (String.IsNullOrEmpty (path)) + throw new InvalidOperationException ($"{name} compiler not found. Tried to find: {variable}"); + } + + void AssertCompilerSet (string variable, string name) + { + if (String.IsNullOrEmpty (variable)) + throw new InvalidOperationException ($"{name} compiler not set"); + } + } + + protected void LogCompilerDetails () + { + const ConsoleColor prefixColor = ConsoleColor.White; + const ConsoleColor languageColor = ConsoleColor.Yellow; + const ConsoleColor unsupportedColor = ConsoleColor.Red; + + LogCompiler (" Default", "C", CC); + LogCompiler (" 32-bit", "C", CC32); + LogCompiler (" 64-bit", "C", CC64); + + LogCompiler ("Default", "C++", CXX); + LogCompiler (" 32-bit", "C++", CXX32); + LogCompiler (" 64-bit", "C++", CXX64); + + Log.Status (" Default triple: ", prefixColor); + LogCompilerInfo (Triple); + Log.Status (" 32-bit triple: ", prefixColor); + LogCompilerInfo (Triple32); + Log.Status (" 64-bit triple: ", prefixColor); + LogCompilerInfo (Triple64); + + void LogCompiler (string variationName, string languageName, string compiler) + { + Log.Status ($"{variationName} ", prefixColor); + Log.Status (languageName, languageColor); + Log.Status (" compiler: ", prefixColor); + LogCompilerInfo (compiler); + } + + void LogCompilerInfo (string compiler) + { + if (String.IsNullOrEmpty (compiler)) + Log.StatusLine ("unsupported", unsupportedColor); + else + Log.StatusLine (compiler); + } + } + + /// + /// Populate with variables to be exported in the environment of all the + /// programs executed by the bootstrapper. + /// + protected virtual void PopulateEnvironmentVariables () + { + EnvironmentVariables ["OS_NAME"] = Name; + EnvironmentVariables ["OS_ARCH"] = Architecture; + } + + /// + /// Locate the program indicated in which can be just the base program name + /// (without the executable extension for cross-OS compatibility) or a full path to a program (however also + /// without the executable extension). In both cases the method tries to find the program at the indicated + /// location (if a path is used in ) or in the directories found in the + /// PATH environment variable, trying all executable extensions () + /// until the program file is found. If the file is indeed found, a check is made whether it is executable (by + /// calling ) and the path is returned. If, however, the program is not found + /// and is true then an exception is throw. If + /// is false, however, null is returned. + /// + public virtual string Which (string programPath, bool required = true) + { + if (String.IsNullOrEmpty (programPath)) { + goto doneAndOut; + } + + string match; + // If it's any form of path, just return it as-is, possibly with executable extension added + if (programPath.IndexOf (Path.DirectorySeparatorChar) >= 0) { + match = GetExecutableWithExtension (programPath, (string ext) => { + string fp = $"{programPath}{ext}"; + if (Utilities.FileExists (fp)) + return fp; + return null; + } + ); + + if (match == null && Utilities.FileExists (programPath)) + match = programPath; + + if (match != null) + return match; + else if (required) { + goto doneAndOut; + } + + return programPath; + } + + List extensions = ExecutableExtensions; + List directories = GetPathDirectories (); + match = GetExecutableWithExtension (programPath, (string ext) => FindProgram ($"{programPath}{ext}", directories)); + if (match != null) + return AssertIsExecutable (match); + + match = FindProgram ($"{programPath}", directories); + if (match != null) + return AssertIsExecutable (match); + + doneAndOut: + if (required) + throw new InvalidOperationException ($"Required program '{programPath}' could not be found"); + + return null; + } + + string GetExecutableWithExtension (string programPath, Func finder) + { + List extensions = ExecutableExtensions; + if (extensions == null || extensions.Count == 0) + return null; + + foreach (string extension in extensions) { + string match = finder (extension); + if (match != null) + return match; + } + + return null; + } + + protected static string FindProgram (string programName, List directories) + { + foreach (string dir in directories) { + string path = Path.Combine (dir, programName); + if (Utilities.FileExists (path)) + return path; + } + + return null; + } + + protected static List GetPathDirectories () + { + var ret = new List (); + string path = Environment.GetEnvironmentVariable ("PATH")?.Trim (); + if (String.IsNullOrEmpty (path)) + return ret; + + ret.AddRange (path.Split (new []{ Path.PathSeparator }, StringSplitOptions.RemoveEmptyEntries)); + return ret; + } + }; +} diff --git a/build-tools/xaprepare/xaprepare/OperatingSystems/Unix.cs b/build-tools/xaprepare/xaprepare/OperatingSystems/Unix.cs new file mode 100644 index 000000000..8e600a0b1 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/OperatingSystems/Unix.cs @@ -0,0 +1,142 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +using Mono.Unix.Native; + +namespace Xamarin.Android.Prepare +{ + abstract partial class Unix : OS + { + const FilePermissions ExecutableBits = FilePermissions.S_IXUSR | FilePermissions.S_IXGRP | FilePermissions.S_IXOTH; + + protected override List ExecutableExtensions => null; + public override bool IsWindows => false; + public override bool IsUnix => true; + public override string HomeDirectory => GetHomeDir (); + + protected Unix (Context context) : base (context) + { + Architecture = Utilities.GetStringFromStdout ("uname", "-m")?.Trim (); + } + + protected override bool InitOS () + { + JavaHome = Context.Instance.Properties.GetValue (KnownProperties.JavaSdkDirectory); + if (String.IsNullOrEmpty (JavaHome)) + JavaHome = Environment.GetEnvironmentVariable ("JAVA_HOME") ?? String.Empty; + + return true; + } + + protected override void DetectCompilers () + { + string ccVersion = Utilities.GetStringFromStdout (Configurables.Defaults.DefaultCompiler, "--version"); + if (String.IsNullOrEmpty (ccVersion)) + throw new InvalidOperationException ($"Failed to obtain version information from the {Configurables.Defaults.DefaultCompiler} compiler"); + + Log.DebugLine ($"Default compiler {Configurables.Defaults.DefaultCompiler} identifies as:"); + Log.DebugLine (ccVersion); + + bool clang = false; + if (ccVersion.IndexOf ("Free Software Foundation", StringComparison.OrdinalIgnoreCase) >= 0) { + CC = "gcc"; + CXX = "g++"; + } else if (ccVersion.IndexOf ("clang", StringComparison.OrdinalIgnoreCase) >= 0) { + CC = "clang"; + CXX = "clang++"; + clang = true; + } else { + CC = "cc"; + CXX = "c++"; + } + + VerifyCompilersExist (); + + Triple = Utilities.GetStringFromStdout (CC, "-dumpmachine"); + if (String.IsNullOrEmpty (Triple)) + throw new InvalidOperationException ($"Failed to determine default target triple for compiler {CC}"); + + if (Is64Bit) { + CC64 = CC; + CXX64 = CXX; + CC32 = $"{CC} -m32"; + CXX32 = $"{CXX} -m32"; + + if (clang) + Triple32 = Utilities.GetStringFromStdout(CC, "-m32", "-dumpmachine"); + else { + if (!Triple.StartsWith ("x86_64-", StringComparison.OrdinalIgnoreCase)) + throw new InvalidOperationException ($"Unexpected 64-bit triple '{Triple}'"); + + Triple32 = Triple.Replace ("x86_64-", "i686-"); + } + + Triple64 = Triple; + } else { + CC64 = null; + CXX64 = null; + CC32 = CC; + CXX32 =CXX; + Triple32 = Triple; + Triple64 = null; + } + + LogCompilerDetails (); + } + + protected override string AssertIsExecutable (string fullPath) + { + IsExecutable (fullPath, true); + return fullPath; + } + + public override string GetManagedProgramRunner (string programPath) + { + if (String.IsNullOrEmpty (programPath)) + return null; + + if (programPath.EndsWith (".exe", StringComparison.OrdinalIgnoreCase) || programPath.EndsWith (".dll", StringComparison.OrdinalIgnoreCase)) + return "mono"; // Caller will find the exact mono executable, we just provide a name + + return null; + } + + protected static bool IsExecutable (string fullPath, bool throwOnErrors = false) + { + Stat sbuf; + int ret = Syscall.stat (fullPath, out sbuf); + + if (ret < 0) { + if (throwOnErrors) + throw new InvalidOperationException ($"Failed to stat file '{fullPath}': {Stdlib.strerror (Stdlib.GetLastError ())}"); + return false; + } + + if ((sbuf.st_mode & ExecutableBits) == 0) { + if (throwOnErrors) + throw new InvalidOperationException ($"File '{fullPath}' is not executable"); + return false; + } + + return true; + } + + protected override void PopulateEnvironmentVariables () + { + base.PopulateEnvironmentVariables (); + EnvironmentVariables ["NO_SUDO"] = Context.AutoProvisionUsesSudo ? "false" : "true"; + + List monoOptions = Context.MonoOptions; + if (monoOptions != null && monoOptions.Count > 0) + EnvironmentVariables ["MONO_OPTIONS" ] = String.Join (" ", monoOptions); + } + + [MethodImplAttribute (MethodImplOptions.InternalCall)] + internal extern static string internalGetHome (); + string GetHomeDir () + { + return internalGetHome (); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/OperatingSystems/Windows.cs b/build-tools/xaprepare/xaprepare/OperatingSystems/Windows.cs new file mode 100644 index 000000000..0be1960f1 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/OperatingSystems/Windows.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using System.IO; + +using Xamarin.Android.Tools.VSWhere; + +namespace Xamarin.Android.Prepare +{ + partial class Windows : OS + { + readonly List executableExtensions; + readonly VisualStudioInstance vsInstance; + + public override string Type { get; } = "Windows"; + public override List Dependencies { get; } = new List (); + public override StringComparison DefaultStringComparison => StringComparison.OrdinalIgnoreCase; + public override StringComparer DefaultStringComparer => StringComparer.OrdinalIgnoreCase; + + protected override List ExecutableExtensions => executableExtensions; + public override bool IsWindows => true; + public override bool IsUnix => false; + public override string HomeDirectory => GetHomeDir (); + + public Windows (Context context) : base (context) + { + string[] pathext = Environment.GetEnvironmentVariable ("PATHEXT")?.Split (new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); + if (pathext == null || pathext.Length == 0) { + executableExtensions = new List { + ".exe", + ".cmd", + ".bat" + }; + } else { + executableExtensions = new List (); + foreach (string ext in pathext) { + executableExtensions.Add (ext.ToLowerInvariant ()); + } + } + + vsInstance = MSBuildLocator.QueryLatest (); + Log.DebugLine ($"Visual Studio detected in {vsInstance.VisualStudioRootPath}"); + Log.DebugLine ($"MSBuild detected at {vsInstance.MSBuildPath}"); + + OSVersionInfo osinfo = OSVersionInfo.GetOSVersionInfo (); + Flavor = osinfo.Name; + Name = osinfo.FullName; + Architecture = Is64Bit ? "x86" : "x86_64"; // Not good enough! (ARM) + Release = $"{osinfo.Major}.{osinfo.Minor}.{osinfo.Build}"; + } + + public override string Which (string programPath, bool required = true) + { + if (String.Compare ("7za", programPath, StringComparison.OrdinalIgnoreCase) == 0) { + string homeDir = Context.Instance?.OS?.HomeDirectory; + if (String.IsNullOrEmpty (homeDir)) { + Log.WarningLine ("User's home directory not known (yet?), cannot return path to 7za"); + return base.Which (programPath, required); + } + + string packagePath = Path.Combine (homeDir, ".nuget", "packages", "7-zip.commandline", "18.1.0", "tools"); + if (Is64Bit) + packagePath = Path.Combine (packagePath, "x64"); + return Path.Combine (packagePath, "7za.exe"); + } + + if (String.Compare ("nuget", programPath, StringComparison.OrdinalIgnoreCase) == 0) { + // NuGet is downloaded at the very beginning, before anything uses it + return Path.Combine (BuildPaths.XamarinAndroidSourceRoot, ".nuget", "NuGet.exe"); + } + + if (String.Compare ("msbuild", programPath, StringComparison.OrdinalIgnoreCase) == 0) { + return vsInstance.MSBuildPath; + } + + return base.Which (programPath, required); + } + + protected override string AssertIsExecutable (string fullPath) + { + return fullPath; + } + + public override string GetManagedProgramRunner (string programPath) + { + return null; + } + + protected override bool InitOS () + { + Log.Todo ("gather dependencies here"); + Log.Todo ("Implement JdkInfo"); + + JavaHome = Context.Instance.Properties.GetValue ("JavaSdkDirectory")?.Trim (); + if (String.IsNullOrEmpty (JavaHome)) + JavaHome = Path.Combine (HomeDirectory, "android-toolchain", "jdk"); + + JavaCPath = Path.Combine (JavaHome, "bin", "javac.exe"); + JavaPath = Path.Combine (JavaHome, "bin", "java.exe"); + JarPath = Path.Combine (JavaHome, "bin", "jar.exe"); + + // This is required by Android SDK which uses a utility to locate Java on Windows + // ($SDK_ROOT/tools/lib/find_java.bat) and that utility, in turn, looks at JAVA_HOME + EnvironmentVariables ["JAVA_HOME"] = JavaHome; + return true; + } + + protected override void DetectCompilers () + { + Log.Todo ("Implement compiler detection"); + } + + string GetHomeDir () + { + string homeDir = Environment.GetEnvironmentVariable ("USERPROFILE"); + if (!String.IsNullOrEmpty (homeDir)) + return homeDir; + + string homeDrive = Environment.GetEnvironmentVariable ("HOMEDRIVE"); + if (String.IsNullOrEmpty (homeDrive)) + throw new InvalidOperationException ("Unable to determine user's home directory, missing HOMEDRIVE environment variable"); + + homeDir = Environment.GetEnvironmentVariable ("HOMEPATH"); + if (String.IsNullOrEmpty (homeDir)) + throw new InvalidOperationException ("Unable to determine user's home directory, missing HOMEPATH environment variable"); + + return $"{homeDrive}{homeDir}"; + } + }; +} diff --git a/build-tools/xaprepare/xaprepare/Properties/AssemblyInfo.cs b/build-tools/xaprepare/xaprepare/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..b23626a38 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Properties/AssemblyInfo.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle ("xabootstrap")] +[assembly: AssemblyDescription ("")] +[assembly: AssemblyConfiguration ("")] +[assembly: AssemblyCompany ("")] +[assembly: AssemblyProduct ("")] +[assembly: AssemblyCopyright ("${AuthorCopyright}")] +[assembly: AssemblyTrademark ("")] +[assembly: AssemblyCulture ("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion ("1.0.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] diff --git a/build-tools/xaprepare/xaprepare/Resources/Configuration.OperatingSystem.props.in b/build-tools/xaprepare/xaprepare/Resources/Configuration.OperatingSystem.props.in new file mode 100644 index 000000000..ab31f0785 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Resources/Configuration.OperatingSystem.props.in @@ -0,0 +1,24 @@ + + + + @OS_NAME@ + @HOST_OS_FLAVOR@ + @OS_RELEASE@ + @HOST_TRIPLE@ + @HOST_TRIPLE32@ + @HOST_TRIPLE64@ + @HOST_CPUS@ + @ARCHITECTURE_BITS@ + @HOST_CC@ + @HOST_CXX@ + @HOST_CC32@ + @HOST_CC64@ + @HOST_CXX32@ + @HOST_CXX64@ + @JavaSdkDirectory@ + @javac@ + @jar@ + @java@ + @HOST_HOMEBREW_PREFIX@ + + diff --git a/build-tools/xaprepare/xaprepare/Resources/JdkInfo.Windows.props.in b/build-tools/xaprepare/xaprepare/Resources/JdkInfo.Windows.props.in new file mode 100644 index 000000000..a25069e53 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Resources/JdkInfo.Windows.props.in @@ -0,0 +1,19 @@ + + + + + + @JdkJvmPath@ + + + + + + + + + @java@ + @javac@ + @jar@ + + diff --git a/build-tools/xaprepare/xaprepare/Scenarios/Scenario_AndroidToolchain.cs b/build-tools/xaprepare/xaprepare/Scenarios/Scenario_AndroidToolchain.cs new file mode 100644 index 000000000..5af6db633 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Scenarios/Scenario_AndroidToolchain.cs @@ -0,0 +1,24 @@ +using System; + +namespace Xamarin.Android.Prepare +{ + [Scenario (isDefault: false)] + partial class Scenario_AndroidToolchain : ScenarioNoStandardEndSteps + { + public Scenario_AndroidToolchain () + : base ("AndroidToolchain", "Install Android SDK, NDK and Corretto JDK.", Context.Instance) + {} + + protected override void AddSteps (Context context) + { + Steps.Add (new Step_Android_SDK_NDK ()); + Steps.Add (new Step_InstallCorrettoOpenJDK ()); + + // disable installation of missing programs... + context.SetCondition (KnownConditions.AllowProgramInstallation, false); + + // ...but do not signal an error when any are missing + context.SetCondition (KnownConditions.IgnoreMissingPrograms, true); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Scenarios/Scenario_PrepareExternalGitDependencies.cs b/build-tools/xaprepare/xaprepare/Scenarios/Scenario_PrepareExternalGitDependencies.cs new file mode 100644 index 000000000..bc0237bb7 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Scenarios/Scenario_PrepareExternalGitDependencies.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; + +namespace Xamarin.Android.Prepare +{ + [Scenario (isDefault: false)] + class Scenario_PrepareExternalGitDependencies : ScenarioNoStandardEndSteps + { + public Scenario_PrepareExternalGitDependencies () + : base ("PrepareExternalGitDependencies", "Prepare external GIT dependencies", Context.Instance) + { + LogFilePath = Context.Instance.GetLogFilePath ("external-git-deps"); + } + + protected override void AddSteps (Context context) + { + Steps.Add (new Step_PrepareExternalGitDependencies ()); + + // disable installation of missing programs... + context.SetCondition (KnownConditions.AllowProgramInstallation, false); + + // ...but do not signal an error when any are missing + context.SetCondition (KnownConditions.IgnoreMissingPrograms, true); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Scenarios/Scenario_PrepareImageDependencies.cs b/build-tools/xaprepare/xaprepare/Scenarios/Scenario_PrepareImageDependencies.cs new file mode 100644 index 000000000..cb649b256 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Scenarios/Scenario_PrepareImageDependencies.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; + +namespace Xamarin.Android.Prepare +{ + [Scenario (isDefault: false)] + class Scenario_PrepareImageDependencies : ScenarioNoStandardEndSteps + { + public Scenario_PrepareImageDependencies () : base ("PrepareImageDependencies", "Prepare provisioning dependencies", Context.Instance) + {} + + protected override void AddSteps (Context context) + { + Steps.Add (new Step_PrepareImageDependencies ()); + + // disable installation of missing programs... + context.SetCondition (KnownConditions.AllowProgramInstallation, false); + + // ...but do not signal an error when any are missing + context.SetCondition (KnownConditions.IgnoreMissingPrograms, true); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Scenarios/Scenario_Required.cs b/build-tools/xaprepare/xaprepare/Scenarios/Scenario_Required.cs new file mode 100644 index 000000000..b3a4026d5 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Scenarios/Scenario_Required.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; + +namespace Xamarin.Android.Prepare +{ + [Scenario (isDefault: false)] + class Scenario_Required : Scenario + { + public Scenario_Required () : base ("Required", "Just the basic steps to quickly refresh NDK, SDK and generate build files.", Context.Instance) + {} + } +} diff --git a/build-tools/xaprepare/xaprepare/Scenarios/Scenario_Standard.Unix.cs b/build-tools/xaprepare/xaprepare/Scenarios/Scenario_Standard.Unix.cs new file mode 100644 index 000000000..5d9fe386c --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Scenarios/Scenario_Standard.Unix.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; + +namespace Xamarin.Android.Prepare +{ + partial class Scenario_Standard + { + partial void AddRequiredOSSpecificSteps (bool beforeBundle) + { + if (!beforeBundle) { + // It has to go after the bundle step because bundle unpacking or creation *always* cleans its + // destination directory and this is where we download the GAS binaries. They are not part of the bundle + // (because they're not useful for every day work with XA) so they must be downloaded after the bundle + // is unpacked. + Log.DebugLine ("Adding Windows GAS download step (AFTER bundle)"); + Steps.Add (new Step_Get_Windows_GAS ()); + return; + } + + if (Context.Instance.WindowsJitAbisEnabled) { + Log.DebugLine ("Windows JIT ABIs ENABLED, ADDING MinGW dependencies build step (BEFORE bundle)"); + Steps.Add (new Step_BuildMingwDependencies ()); + } else { + Log.DebugLine ("Windows JIT ABis DISABLED, SKIPPING MinGW dependencies build step"); + } + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Scenarios/Scenario_Standard.Windows.cs b/build-tools/xaprepare/xaprepare/Scenarios/Scenario_Standard.Windows.cs new file mode 100644 index 000000000..68d7de57d --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Scenarios/Scenario_Standard.Windows.cs @@ -0,0 +1,15 @@ +using System; + +namespace Xamarin.Android.Prepare +{ + partial class Scenario_Standard + { + partial void AddRequiredOSSpecificSteps (bool beforeBundle) + { + if (!beforeBundle) + return; + + Steps.Add (new Step_InstallAnt ()); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Scenarios/Scenario_Standard.cs b/build-tools/xaprepare/xaprepare/Scenarios/Scenario_Standard.cs new file mode 100644 index 000000000..22180cd23 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Scenarios/Scenario_Standard.cs @@ -0,0 +1,36 @@ +using System; + +namespace Xamarin.Android.Prepare +{ + [Scenario (isDefault: true)] + partial class Scenario_Standard : Scenario + { + public Scenario_Standard () + : base ("Standard", "Standard init", Context.Instance) + {} + + protected override void AddSteps (Context context) + { + if (context == null) + throw new ArgumentNullException (nameof (context)); + + Steps.Add (new Step_Android_SDK_NDK ()); + Steps.Add (new Step_GenerateFiles (atBuildStart: true)); + Steps.Add (new Step_InstallCorrettoOpenJDK ()); + Steps.Add (new Step_PrepareProps ()); + Steps.Add (new Step_PrepareExternal ()); + Steps.Add (new Step_PrepareLocal ()); + AddRequiredOSSpecificSteps (true); + Steps.Add (new Step_PrepareBundle ()); + AddRequiredOSSpecificSteps (false); + } + + protected override void AddEndSteps (Context context) + { + Steps.Add (new Step_ThirdPartyNotices ()); + Steps.Add (new Step_GenerateFiles (atBuildStart: false)); + } + + partial void AddRequiredOSSpecificSteps (bool beforeBundle); + } +} diff --git a/build-tools/xaprepare/xaprepare/Scenarios/Scenario_UpdateMono.Unix.cs b/build-tools/xaprepare/xaprepare/Scenarios/Scenario_UpdateMono.Unix.cs new file mode 100644 index 000000000..3ff75fbf5 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Scenarios/Scenario_UpdateMono.Unix.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; + +namespace Xamarin.Android.Prepare +{ + [Scenario (isDefault: false)] + class Scenario_UpdateMono : ScenarioNoStandardEndSteps + { + public const string MyName = "UpdateMono"; + + public Scenario_UpdateMono () + : base (MyName, "Perform basic detection steps AND update Mono if necessary", Context.Instance) + {} + + protected override void AddSteps (Context context) + { + // Allow automatic provisioning... + context.AutoProvision = true; + + // ...and let it use sudo, because without it it's useless... + context.AutoProvisionUsesSudo = true; + + // ...no new steps here, just enable Mono updates... + context.SetCondition (KnownConditions.AllowMonoUpdate, true); + + // ...and disable installation of other programs... + context.SetCondition (KnownConditions.AllowProgramInstallation, false); + + // ...but do not signal an error when any are missing + context.SetCondition (KnownConditions.IgnoreMissingPrograms, true); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Steps/MonoRuntimesHelpers.cs b/build-tools/xaprepare/xaprepare/Steps/MonoRuntimesHelpers.cs new file mode 100644 index 000000000..517602aab --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Steps/MonoRuntimesHelpers.cs @@ -0,0 +1,184 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace Xamarin.Android.Prepare +{ + class MonoRuntimesHelpers + { + public static string UtilitiesDestinationDir => Configurables.Paths.InstallMSBuildDir; + public static string BCLRedistListDestinationDir => Configurables.Paths.InstallBCLFrameworkRedistListDir; + public static string BCLTestsDestinationDir => Configurables.Paths.BCLTestsDestDir; + public static string BCLTestsArchivePath => Configurables.Paths.BCLTestsArchivePath; + public static string RuntimeDestinationDir => Configurables.Paths.InstallMSBuildDir; + public static string FrameworkListPath => Configurables.Paths.FrameworkListInstallPath; + + public static readonly Dictionary BCLDestinationDirs = new Dictionary () { + { BclFileTarget.Android, Configurables.Paths.InstallBCLFrameworkDir }, + { BclFileTarget.DesignerHost, Configurables.Paths.InstallHostBCLDir }, + { BclFileTarget.DesignerWindows, Configurables.Paths.InstallWindowsBCLDir }, + }; + + public static readonly Dictionary BCLFacadesDestinationDirs = new Dictionary () { + { BclFileTarget.Android, Configurables.Paths.InstallBCLFrameworkFacadesDir }, + { BclFileTarget.DesignerHost, Configurables.Paths.InstallHostBCLFacadesDir }, + { BclFileTarget.DesignerWindows, Configurables.Paths.InstallWindowsBCLFacadesDir }, + }; + + public static List GetEnabledRuntimes (Runtimes allRuntimes, bool enableLogging) + { + if (allRuntimes == null) + throw new ArgumentNullException (nameof (allRuntimes)); + + var context = Context.Instance; + var enabledRuntimes = new List (); + + if (enableLogging) + Log.Instance.StatusLine ("Enabled Mono Android runtime ABIs:", ConsoleColor.White); + + foreach (Runtime runtime in allRuntimes.Items.Where (r => r is MonoJitRuntime && r.Enabled)) { + enabledRuntimes.Add (runtime); + if (enableLogging) + Log.Instance.StatusLine ($" {context.Characters.Bullet} {runtime.Name}"); + } + + if (enableLogging) { + Log.Instance.StatusLine (); + Log.Instance.StatusLine ("Enabled Mono host runtimes:", ConsoleColor.White); + } + foreach (Runtime runtime in allRuntimes.Items.Where (r => r is MonoHostRuntime && r.Enabled)) { + enabledRuntimes.Add (runtime); + if (enableLogging) + Log.Instance.StatusLine ($" {context.Characters.Bullet} {runtime.Name}"); + } + + bool anyCrossEnabled = false; + if (enableLogging) { + Log.Instance.StatusLine (); + Log.Instance.StatusLine ("Enabled Mono cross compilers:", ConsoleColor.White); + } + foreach (Runtime runtime in allRuntimes.Items.Where (r => r is MonoCrossRuntime && r.Enabled)) { + anyCrossEnabled = true; + enabledRuntimes.Add (runtime); + if (enableLogging) + Log.Instance.StatusLine ($" {context.Characters.Bullet} {runtime.Name}"); + } + + if (enableLogging && !anyCrossEnabled) + Log.Instance.StatusLine ($" NONE", ConsoleColor.DarkCyan); + + anyCrossEnabled = false; + if (enableLogging) { + Log.Instance.StatusLine (); + Log.Instance.StatusLine ("Enabled LLVM cross compilers:", ConsoleColor.White); + } + foreach (Runtime runtime in allRuntimes.Items.Where (r => r is LlvmRuntime && r.Enabled)) { + anyCrossEnabled = true; + enabledRuntimes.Add (runtime); + if (enableLogging) + Log.Instance.StatusLine ($" {context.Characters.Bullet} {runtime.Name}"); + } + if (enableLogging && !anyCrossEnabled) + Log.Instance.StatusLine ($" NONE", ConsoleColor.DarkCyan); + + return enabledRuntimes; + } + + public static (string executable, string debugSymbols) GetDestinationPaths (MonoUtilityFile muf) + { + if (muf == null) + throw new ArgumentNullException (nameof (muf)); + + string destDir = UtilitiesDestinationDir; + string targetFileName; + + if (!String.IsNullOrEmpty (muf.TargetName)) + targetFileName = muf.TargetName; + else + targetFileName = Path.GetFileName (muf.SourcePath); + + string destFilePath = Path.Combine (destDir, targetFileName); + if (String.IsNullOrEmpty (muf.DebugSymbolsPath)) + return (destFilePath, null); + + return (destFilePath, Path.Combine (destDir, Utilities.GetDebugSymbolsPath (targetFileName))); + } + + public static (string assembly, string debugSymbols) GetDestinationPaths (BclFile bf) + { + if (bf == null) + throw new ArgumentNullException (nameof (bf)); + + string destDir; + switch (bf.Type) { + case BclFileType.ProfileAssembly: + destDir = BCLDestinationDirs [bf.Target]; + break; + + case BclFileType.FacadeAssembly: + destDir = BCLFacadesDestinationDirs [bf.Target]; + break; + + default: + throw new InvalidOperationException ($"Unsupported BCL file type {bf.Type} for file {bf.Name}"); + } + + string destFile = Path.Combine (destDir, bf.Name); + if (bf.ExcludeDebugSymbols || String.IsNullOrEmpty (bf.DebugSymbolsPath)) + return (destFile, null); + + return (destFile, Path.Combine (destDir, Path.GetFileName (bf.DebugSymbolsPath))); + } + + public static (string assembly, string debugSymbols) GetDestinationPaths (TestAssembly tasm) + { + if (tasm == null) + throw new ArgumentNullException (nameof (tasm)); + + bool pdbRequired; + switch (tasm.TestType) { + case TestAssemblyType.Reference: + case TestAssemblyType.TestRunner: + case TestAssemblyType.XUnit: + case TestAssemblyType.NUnit: + case TestAssemblyType.Satellite: + pdbRequired = tasm.TestType != TestAssemblyType.Satellite && !tasm.ExcludeDebugSymbols; + break; + + default: + throw new InvalidOperationException ($"Unsupported test assembly type: {tasm.TestType}"); + } + + string destDir; + if (tasm.Name.IndexOf (Path.DirectorySeparatorChar) >= 0) + destDir = Path.Combine (BCLTestsDestinationDir, Path.GetDirectoryName (tasm.Name)); + else + destDir = BCLTestsDestinationDir; + + string destFile = Path.Combine (destDir, Path.GetFileName (tasm.Name)); + return (destFile, pdbRequired ? Utilities.GetDebugSymbolsPath (destFile) : null); + } + + public static (bool skip, string src, string dst) GetRuntimeFilePaths (Runtime runtime, RuntimeFile rtf) + { + if (rtf.ShouldSkip != null && rtf.ShouldSkip (runtime)) + return (true, null, null); + + return ( + false, + GetPath ("source", rtf.Source (runtime), Configurables.Paths.MonoSourceFullPath), + GetPath ("destination", rtf.Destination (runtime), RuntimeDestinationDir) + ); + + string GetPath (string name, string path, string defaultRoot) + { + if (String.IsNullOrEmpty (path)) + throw new InvalidOperationException ($"Empty {name} file path"); + if (!Path.IsPathRooted (path)) + path = Path.Combine (defaultRoot, path); + return Path.GetFullPath (path); + } + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_Android_SDK_NDK.cs b/build-tools/xaprepare/xaprepare/Steps/Step_Android_SDK_NDK.cs new file mode 100644 index 000000000..75512af14 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Steps/Step_Android_SDK_NDK.cs @@ -0,0 +1,268 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Kajabity.Tools.Java; + +namespace Xamarin.Android.Prepare +{ + class Step_Android_SDK_NDK : StepWithDownloadProgress + { + class AndroidPackage + { + public AndroidToolchainComponent Component; + public string PackageName; + public Uri Url; + public string LocalPackagePath; + public string DestinationDir; + } + + public Step_Android_SDK_NDK () + : base ("Preparing Android SDK and NDK") + {} + + protected override async Task Execute (Context context) + { + string sdkRoot = context.Properties.GetRequiredValue (KnownProperties.AndroidSdkDirectory); + string ndkRoot = context.Properties.GetRequiredValue (KnownProperties.AndroidNdkDirectory); + string packageCacheDir = context.Properties.GetRequiredValue (KnownProperties.AndroidToolchainCacheDirectory); + + Log.StatusLine ("Android SDK location: ", sdkRoot, tailColor: Log.DestinationColor); + Log.StatusLine ("Android NDK location: ", ndkRoot, tailColor: Log.DestinationColor); + Log.DebugLine ($"Toolchain cache directory: {packageCacheDir}"); + + var toolchain = new AndroidToolchain (); + var toInstall = new List (); + + toolchain.Components.ForEach (c => Check (context, packageCacheDir, sdkRoot, c, toInstall, 4)); + if (toInstall.Count == 0) + return GatherNDKInfo (context, ndkRoot); + + Log.MessageLine (); + toInstall.ForEach (p => Log.DebugLine ($"Missing Android component: {p.Component.Name}")); + + string tempDir = Path.Combine (context.Properties.GetRequiredValue (KnownProperties.AndroidToolchainDirectory), "temp"); + Log.DebugLine ($"Toolchain temporary directory: {tempDir}"); + + if (Directory.Exists (tempDir)) { + Log.DebugLine ("Temporary directory exists, cleaning it up"); + Utilities.DeleteDirectorySilent (tempDir); + } + Directory.CreateDirectory (tempDir); + + Log.MessageLine ("Installing missing components"); + var toDownload = new List (); + toInstall.ForEach (c => CheckPackageStatus (context, packageCacheDir, c, toDownload)); + + if (toDownload.Count > 0) { + ulong totalDownloadSize = 0; + foreach (AndroidPackage pkg in toDownload) { + Log.DebugLine ($"Android component '{pkg.Component.Name}' will be downloaded from {pkg.Url}"); + (bool success, ulong size) = await Utilities.GetDownloadSize (pkg.Url); + if (!success) + continue; + totalDownloadSize += size; + }; + + toDownload.ForEach (p => Log.StatusLine ($" {context.Characters.Link} {p.Url}", ConsoleColor.White)); + + DownloadStatus downloadStatus = Utilities.SetupDownloadStatus (context, totalDownloadSize, context.InteractiveSession); + await Task.WhenAll (toDownload.Select (p => Download (context, p.Url, p.LocalPackagePath, p.Component.Name, p.PackageName, downloadStatus))); + } + + foreach (AndroidPackage p in toInstall) { + await Unpack (context, tempDir, p); + } + + if (!AcceptLicenses (context, sdkRoot)) { + Log.ErrorLine ("Failed to accept Android SDK licenses"); + return false; + } + + return GatherNDKInfo (context, ndkRoot); + } + + bool AcceptLicenses (Context context, string sdkRoot) + { + string sdkManager = context.OS.Which (Path.Combine (sdkRoot, "tools", "bin", "sdkmanager")); + string jdkDir = context.OS.JavaHome; + + Log.Todo ("Modify ProcessRunner to allow standard input writing and switch to it here"); + // var runner = new ProcessRunner (sdkManager, "--licenses"); + // runner.StartInfoCallback = (ProcessStartInfo psi) => { + // if (String.IsNullOrEmpty (jdkDir)) + // psi.EnvironmentVariables.Add ("JAVA_HOME", jdkDir); + // psi.RedirectStandardInput = true; + // }; + + var psi = new ProcessStartInfo (sdkManager, "--licenses") { + UseShellExecute = false, + RedirectStandardInput = true + }; + if (String.IsNullOrEmpty (jdkDir) && !psi.EnvironmentVariables.ContainsKey ("JAVA_HOME")) + psi.EnvironmentVariables.Add ("JAVA_HOME", jdkDir); + + Log.DebugLine ($"Starting {psi.FileName} {psi.Arguments}"); + var proc = Process.Start (psi); + for (int i = 0; i < 10; i++) + proc.StandardInput.WriteLine ('y'); + proc.WaitForExit (); + + return true; + } + + bool GatherNDKInfo (Context context, string ndkRoot) + { + return context.BuildInfo.GatherNDKInfo (context, ndkRoot); + } + + void CheckPackageStatus (Context context, string packageCacheDir, AndroidPackage pkg, List toDownload) + { + Log.StatusLine ($" {context.Characters.Bullet} Installing ", pkg.Component.Name, tailColor: ConsoleColor.White); + + if (File.Exists (pkg.LocalPackagePath)) { + Log.DebugLine ($"Component '{pkg.Component.Name}' package exists: {pkg.LocalPackagePath}"); + LogStatus ("already downloaded", 4, Log.InfoColor); + } else { + Log.DebugLine ($"Component '{pkg.Component.Name}' package not downloaded yet: {pkg.LocalPackagePath}"); + LogStatus ("not downloaded yet", 4, ConsoleColor.Magenta); + toDownload.Add (pkg); + } + } + + async Task Unpack (Context context, string tempDirRoot, AndroidPackage pkg) + { + Log.StatusLine (PadStatus ($"unpacking {pkg.PackageName} to ", 4), pkg.DestinationDir, tailColor: Log.DestinationColor); + + string sevenZip = context.Tools.SevenZipPath; + Log.DebugLine ($"7z binary path: {sevenZip}"); + + string tempDir = Path.Combine (tempDirRoot, Path.GetRandomFileName ()); + + if (!await Utilities.Unpack (pkg.LocalPackagePath, tempDir)) + throw new InvalidOperationException ($"Failed to unpack {pkg.LocalPackagePath}"); + + if (pkg.Component.NoSubdirectory) { + Utilities.MoveDirectoryContentsRecursively (tempDir, pkg.DestinationDir); + return; + } + + // There should be just a single subdirectory + List subdirs = Directory.EnumerateDirectories (tempDir).ToList (); + if (subdirs.Count > 1) + throw new InvalidOperationException ($"Unexpected contents layout of Android component '{pkg.Component.Name}' - expected a single subdirectory, instead found {subdirs.Count}"); + + Utilities.MoveDirectoryContentsRecursively (subdirs [0], pkg.DestinationDir); + } + + Uri GetPackageUrl (AndroidToolchainComponent component, string packageName) + { + Uri packageUrl; + + if (component.RelativeUrl != null) + packageUrl = new Uri (AndroidToolchain.AndroidUri, component.RelativeUrl); + else + packageUrl = AndroidToolchain.AndroidUri; + + return new Uri (packageUrl, packageName); + } + + string GetDestinationDir (AndroidToolchainComponent component, string sdkRoot) + { + string path = component.DestDir; + if (!Path.IsPathRooted (path)) + return Path.Combine (sdkRoot, path); + return path; + } + + void Check (Context context, string packageCacheDir, string sdkRoot, AndroidToolchainComponent component, List toInstall, int padLeft) + { + Log.StatusLine ($" {context.Characters.Bullet} Checking ", component.Name, tailColor: ConsoleColor.White); + + const string statusMissing = "missing"; + const string statusOutdated = "outdated"; + const string statusInstalled = "installed"; + + string path = GetDestinationDir (component, sdkRoot); + + Log.DebugLine ($"Checking if {component.Name} exists in {path}"); + bool missing; + if (IsInstalled (component, path, out missing)) { + LogStatus (statusInstalled, padLeft, Log.InfoColor); + return; + } + + if (missing) + LogStatus (statusMissing, padLeft, ConsoleColor.Magenta); + else + LogStatus (statusOutdated, padLeft, ConsoleColor.DarkYellow); + + string packageName = $"{component.Name}.zip"; + var pkg = new AndroidPackage { + Component = component, + PackageName = packageName, + Url = GetPackageUrl (component, packageName), + LocalPackagePath = Path.Combine (packageCacheDir, packageName), + DestinationDir = GetDestinationDir (component, sdkRoot), + }; + + toInstall.Add (pkg); + } + + bool IsInstalled (AndroidToolchainComponent component, string path, out bool missing) + { + missing = true; + if (!Directory.Exists (path)) { + Log.DebugLine ($"Component '{component.Name}' directory does not exist: {path}"); + return false; + } + + // This is just a cursory check, we might want to check versions + string propsFile = Path.Combine (path, "source.properties"); + if (!File.Exists (propsFile)) { + Log.DebugLine ($"Component '{component.Name}' properties file does not exist: {propsFile}"); + return false; + } + + missing = false; + if (String.IsNullOrEmpty (component.ExpectedPkgRevision)) { + Log.DebugLine ($"Component '{component.Name}' does not specify required Pkg.Revision, assuming it's valid"); + return true; + } + + Log.DebugLine ($"Component '{component.Name}' requires Pkg.Revision to be '{component.ExpectedPkgRevision}', verifying"); + var props = new JavaProperties (); + try { + using (var fs = File.OpenRead (propsFile)) { + props.Load (fs); + } + } catch (Exception ex) { + Log.DebugLine ($"Failed to read '{component.Name}' source.properties. Assuming invalid version, component will be reinstalled."); + Log.DebugLine (ex.ToString ()); + return false; + } + + string pkgRevision = props.GetProperty ("Pkg.Revision", String.Empty); + if (String.IsNullOrEmpty (pkgRevision)) { + Log.DebugLine ($"Component '{component.Name}' does not have Pkg.Revision in its source.properties file, it will be reinstalled."); + return false; + } + + if (!Version.TryParse (pkgRevision, out Version pkgVer)) { + Log.DebugLine ($"Failed to parse a valid version from Pkg.Revision ({pkgRevision}) for component '{component.Name}'. Component will be reinstalled."); + return false; + } + + if (!Version.TryParse (component.ExpectedPkgRevision, out Version expectedPkgVer)) + throw new InvalidOperationException ($"Invalid expected package version for component '{component.Name}': {component.ExpectedPkgRevision}"); + + bool equal = pkgVer == expectedPkgVer; + if (!equal) + Log.DebugLine ($"Installed version of '{component.Name}' ({pkgVer}) is different than the required one ({expectedPkgVer})"); + + return equal; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_BuildLibZip.MacOS.cs b/build-tools/xaprepare/xaprepare/Steps/Step_BuildLibZip.MacOS.cs new file mode 100644 index 000000000..b5e214577 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Steps/Step_BuildLibZip.MacOS.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + class Step_BuildLibZip : Step + { + static readonly Regex libZipDylib = new Regex ("^.*/libzip.\\d+\\.\\d+\\.dylib$"); + + public Step_BuildLibZip () + : base ("Installing the LibZip library") + {} + +#pragma warning disable CS1998 + protected override async Task Execute (Context context) + { + var runner = new BrewRunner (context); + if (!runner.List ("libzip", out List lines) || lines == null || lines.Count == 0) { + Log.ErrorLine ("Failed to retrieve libzip package contents"); + return false; + } + + string libZipPath = null; + foreach (string line in lines) { + Match match = libZipDylib.Match (line); + if (!match.Success) + continue; + libZipPath = line; + break; + } + + if (String.IsNullOrEmpty (libZipPath)) { + Log.ErrorLine ("`libzip` package does not contain the dynamic library"); + return false; + } + + if (!File.Exists (libZipPath)) { + Log.ErrorLine ($"`libzip` package lists the dynamic library at {libZipPath} but the file does not exist"); + return false; + } + + Log.DebugLine ($"`libzip` library found at {libZipPath}"); + string destFile = Path.Combine (Configurables.Paths.InstallMSBuildDir, Path.GetFileName (libZipPath)); + Log.Status ("Installing "); + Log.Status (Utilities.GetRelativePath (BuildPaths.XamarinAndroidSourceRoot, destFile), ConsoleColor.White); + Log.StatusLine ($" {context.Characters.LeftArrow} ", libZipPath, leadColor: ConsoleColor.Cyan, tailColor: ConsoleColor.White); + + Utilities.CopyFile (libZipPath, destFile); + + if (!File.Exists (destFile)) { + Log.ErrorLine ("Failed to copy the libzip dynamic library."); + return false; + } + + return true; + } +#pragma warning restore CS1998 + } +} diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_BuildLibZipForWindows.MacOS.cs b/build-tools/xaprepare/xaprepare/Steps/Step_BuildLibZipForWindows.MacOS.cs new file mode 100644 index 000000000..4e8da8378 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Steps/Step_BuildLibZipForWindows.MacOS.cs @@ -0,0 +1,10 @@ +namespace Xamarin.Android.Prepare +{ + partial class Step_BuildLibZipForWindows + { + partial void InitOS () + { + zlibRootPrefix = Context.Instance.OS.HomebrewPrefix; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_BuildLibZipForWindows.Unix.cs b/build-tools/xaprepare/xaprepare/Steps/Step_BuildLibZipForWindows.Unix.cs new file mode 100644 index 000000000..5d2a84603 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Steps/Step_BuildLibZipForWindows.Unix.cs @@ -0,0 +1,147 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + partial class Step_BuildLibZipForWindows : Step + { + const string LibZipName = "libzip.dll"; + + // This is for homebrew on mac. We need to do it this way because Configuration.props will not have the + // `HostHomebrewPrefix` property set yet - it's *our* task to detect it - and thus the + // `MingwZlibRootDirectory{32,64}` properties will *not* have the right path and the build will fail on macOS. + string zlibRootPrefix = String.Empty; + + public Step_BuildLibZipForWindows () + : base ("Cross-building the LibZip library for Windows") + { + InitOS (); + } + + partial void InitOS (); + + protected override async Task Execute (Context context) + { + if (context == null) + throw new ArgumentNullException (nameof (context)); + + if (!context.WindowsJitAbisEnabled) + throw new InvalidOperationException ("No Windows targets enabled, this step should not be called"); + + bool disabled, success; + (disabled, success) = await ConfigureBuildAndInstall (context, AbiNames.HostJit.Win32); + if (!disabled && !success) + return false; + + (disabled, success) = await ConfigureBuildAndInstall (context, AbiNames.HostJit.Win64); + if (!disabled && !success) + return false; + + return true; + } + + async Task<(bool disabled, bool success)> ConfigureBuildAndInstall (Context context, string abiName) + { + if (!context.IsHostJitAbiEnabled (abiName)) { + Log.DebugLine ($"Windows target {abiName} disabled, not building libzip"); + return (true, true); + } + + bool sixtyFourBit = context.Is64BitMingwHostAbi (abiName); + string sourceDir = context.Properties.GetRequiredValue (KnownProperties.LibZipSourceFullPath); + string buildDir = Path.Combine (Configurables.Paths.BuildBinDir, $"libzip-windows-{abiName}"); + string stampFile = Path.Combine (buildDir, $".build-{context.BuildInfo.FullLibZipHash}"); + string outputPath; + + if (sixtyFourBit) + outputPath = Path.Combine ("x64", LibZipName); + else + outputPath = LibZipName; + + string sourceFile = Path.Combine (buildDir, "lib", LibZipName); + string destFile = Path.Combine (Configurables.Paths.LibZipOutputPath, outputPath); + bool needBuild; + + if (Utilities.FileExists (stampFile) && Utilities.FileExists (sourceFile)) { + Log.DebugLine ($"LibZip-Windows build stamp file exists: {stampFile}"); + Log.StatusLine ($"LibZip for {abiName} already built, skipping compilation"); + needBuild = false; + } else { + needBuild = true; + } + + if (needBuild) { + Utilities.DeleteDirectorySilent (buildDir); + Utilities.CreateDirectory (buildDir); + List arguments = GetCmakeArguments (context, buildDir, sixtyFourBit); + + string logTag = $"libzip-windows-{abiName}"; + var cmake = new CMakeRunner (context); + bool result = await cmake.Run ( + logTag: logTag, + sourceDirectory: sourceDir, + workingDirectory: buildDir, + arguments: arguments + ); + + if (!result) + return (false, false); + + var ninja = new NinjaRunner (context); + result = await ninja.Run ( + logTag: logTag, + workingDirectory: buildDir + ); + + if (!result) + return (false, false); + } + + Utilities.CopyFile (sourceFile, destFile); + + if (!File.Exists (destFile)) { + Log.ErrorLine ($"Failed to copy {sourceFile} to {destFile}"); + return (false, false); + } + + TouchStampFile (stampFile); + return (false, true); + } + + List GetCmakeArguments (Context context, string workingDirectory, bool sixtyFourBit) + { + string cmakeToolchainFile; + string zlibRoot; + + if (sixtyFourBit) { + cmakeToolchainFile = Configurables.Paths.Mingw64CmakePath; + zlibRoot = context.Properties.GetRequiredValue (KnownProperties.MingwZlibRootDirectory64); + } else { + cmakeToolchainFile = Configurables.Paths.Mingw32CmakePath; + zlibRoot = context.Properties.GetRequiredValue (KnownProperties.MingwZlibRootDirectory32); + } + + if (!String.IsNullOrEmpty (zlibRootPrefix)) + zlibRoot = Path.Combine (zlibRootPrefix, zlibRoot); + + string zlibLibrary = Path.Combine (zlibRoot, "lib", context.Properties.GetRequiredValue (KnownProperties.MingwZlibLibraryName)); + string zlibIncludeDir = Path.Combine (zlibRoot, "include"); + + return new List { + "-GNinja", + "-DCMAKE_MAKE_PROGRAM=ninja", + "-DCMAKE_POLICY_DEFAULT_CMP0074=NEW", + "-DENABLE_GNUTLS=OFF", + "-DENABLE_OPENSSL=OFF", + "-DENABLE_COMMONCRYPTO=OFF", + "-Wno-dev", // Hushes some warnings that are useless for us + $"-DCMAKE_TOOLCHAIN_FILE={Utilities.GetRelativePath (workingDirectory, cmakeToolchainFile)}", + $"-DZLIB_ROOT={zlibRoot}", + $"-DZLIB_LIBRARY={zlibLibrary}", + $"-DZLIB_INCLUDE_DIR={zlibIncludeDir}" + }; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_BuildMingwDependencies.cs b/build-tools/xaprepare/xaprepare/Steps/Step_BuildMingwDependencies.cs new file mode 100644 index 000000000..44b3e92b6 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Steps/Step_BuildMingwDependencies.cs @@ -0,0 +1,144 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + class Step_BuildMingwDependencies : Step + { + static readonly SortedDictionary dependencies = new SortedDictionary (StringComparer.Ordinal) { + { "dlfcn-win32", (description: "libdl for Windows", libraryName: "libdl.a") }, + { "mman-win32", (description: "mmap for Windows", libraryName: "libmman.a") }, + }; + + public Step_BuildMingwDependencies () : + base ("Build MinGW dependencies") + {} + + protected override async Task Execute (Context context) + { + if (context == null) + throw new ArgumentNullException (nameof (context)); + + if (!context.WindowsJitAbisEnabled) + throw new InvalidOperationException ("No Windows targets enabled, this step should not be called"); + + Log.StatusLine ("Dependencies:"); + foreach (var kvp in dependencies) { + string dependencyDir = kvp.Key; + (string dependencyDescription, string libraryName) = kvp.Value; + bool disabled, success; + + (disabled, success) = await ConfigureBuildAndInstall (context, AbiNames.HostJit.Win32, dependencyDir, dependencyDescription, libraryName); + if (!disabled && !success) + return false; + + (disabled, success) = await ConfigureBuildAndInstall (context, AbiNames.HostJit.Win64, dependencyDir, dependencyDescription, libraryName); + if (!disabled && !success) + return false; + } + + return true; + } + + async Task<(bool disabled, bool success)> ConfigureBuildAndInstall (Context context, string abiName, string dependencyDir, string dependencyDescription, string libraryName) + { + if (!context.IsHostJitAbiEnabled (abiName)) { + Log.DebugLine ($"Windows target {abiName} disabled, not building ${dependencyDescription}"); + return (true, true); + } + + Log.StatusLine ($" {context.Characters.Bullet} {dependencyDescription}"); + bool sixtyFourBit = context.Is64BitMingwHostAbi (abiName); + string sourceDir = Path.Combine (Configurables.Paths.ExternalDir, dependencyDir); + string buildDir = Path.Combine (Configurables.Paths.BuildBinDir, $"{dependencyDir}-{abiName}"); + + Log.DebugLine ($"{dependencyDir} source directory: {sourceDir}"); + Log.DebugLine ($"{dependencyDir} build directory: {buildDir}"); + + string outputDir; + if (sixtyFourBit) + outputDir = "x86_64"; + else + outputDir = "x86"; + outputDir = Path.Combine (context.Properties.GetRequiredValue (KnownProperties.MingwDependenciesRootDirectory), outputDir); + Log.DebugLine ($"{dependencyDir} output directory: {outputDir}"); + + string outputLibraryPath = Path.Combine (outputDir, "lib", libraryName); + Log.DebugLine ($"{dependencyDir} output file path: {outputLibraryPath}"); + + if (Utilities.FileExists (outputLibraryPath)) { + Log.StatusLine (" already built", Context.SuccessColor); + return (false, true); + } + + Utilities.DeleteDirectorySilent (buildDir); + Utilities.CreateDirectory (buildDir); + + Log.StatusLine (" configuring..."); + List arguments = GetCmakeArguments (context, buildDir, outputDir, sixtyFourBit); + string logTag = $"{dependencyDir}-{abiName}-configure"; + var cmake = new CMakeRunner (context); + bool result = await cmake.Run ( + logTag: logTag, + sourceDirectory: sourceDir, + workingDirectory: buildDir, + arguments: arguments + ); + + if (!result) + return (false, false); + + logTag = $"{dependencyDir}-{abiName}-build"; + Log.StatusLine (" building..."); + var ninja = new NinjaRunner (context); + result = await ninja.Run ( + logTag: logTag, + workingDirectory: buildDir + ); + + if (!result) + return (false, false); + + logTag = $"{dependencyDir}-{abiName}-install"; + Log.StatusLine (" installing..."); + result = await ninja.Run ( + logTag: logTag, + workingDirectory: buildDir, + arguments: new List { "install", "-v" } + ); + + if (!File.Exists (outputLibraryPath)) { + Log.ErrorLine ($"Installation of {dependencyDescription} failed"); + return (false, false); + } + + if (!result) + return (false, false); + + return (false, true); + } + + List GetCmakeArguments (Context context, string workingDirectory, string outputDirectory, bool sixtyFourBit) + { + string cmakeToolchainFile; + + if (sixtyFourBit) { + cmakeToolchainFile = Configurables.Paths.Mingw64CmakePath; + } else { + cmakeToolchainFile = Configurables.Paths.Mingw32CmakePath; + } + + return new List { + "-GNinja", + "-DCMAKE_MAKE_PROGRAM=ninja", + "-DBUILD_TESTS=OFF", + "-DBUILD_SHARED_LIBS=OFF", + $"-DCMAKE_INSTALL_PREFIX={outputDirectory}", + "-Wno-dev", // Hushes some warnings that are useless for us + $"-DCMAKE_TOOLCHAIN_FILE={Utilities.GetRelativePath (workingDirectory, cmakeToolchainFile)}", + }; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_BuildMonoRuntimes.Unix.cs b/build-tools/xaprepare/xaprepare/Steps/Step_BuildMonoRuntimes.Unix.cs new file mode 100644 index 000000000..64035a918 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Steps/Step_BuildMonoRuntimes.Unix.cs @@ -0,0 +1,639 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Reflection; +using System.Threading.Tasks; +using System.Xml.Linq; + +namespace Xamarin.Android.Prepare +{ + class Step_BuildMonoRuntimes : StepWithDownloadProgress + { + const string StatusIndent = " "; + const string SubStatusIndent = " "; + + List runtimeBuildMakeOptions; + List runtimeBuildMakeTargets; + Runtimes allRuntimes; + + public Step_BuildMonoRuntimes () + : base ("Preparing Mono runtimes") + { + Context.Instance.RuleGenerators.Add (MonoRuntime_RuleGenerator); + } + + protected override async Task Execute (Context context) + { + List enabledRuntimes = GetEnabledRuntimes (enableLogging: true); + if (enabledRuntimes.Count == 0) { + Log.StatusLine ("No runtimes to build/install"); + return true; + } + + bool built = await DownloadMonoArchive (context); + + if (!built) { + List makeArguments = GetMakeArguments (context, enabledRuntimes); + if (!await BuildRuntimes (context, makeArguments)) { + Log.ErrorLine ("Mono runtime build failed"); + return false; + } + } + + CleanupBeforeInstall (); + Log.StatusLine (); + if (!await InstallRuntimes (context, enabledRuntimes)) + return false; + + if (!InstallBCL (context)) + return false; + + if (!InstallUtilities (context)) + return false; + + return true; + } + + void CleanupBeforeInstall () + { + foreach (string dir in allRuntimes.OutputDirectories) { + Utilities.DeleteDirectorySilent (dir); + } + } + + bool AbiChoiceChanged (Context context) + { + string cacheFile = Configurables.Paths.MonoRuntimesEnabledAbisCachePath; + if (!File.Exists (cacheFile)) { + Log.DebugLine ($"Enabled ABI cache file not found at {cacheFile}"); + return true; + } + + var oldAbis = new HashSet (StringComparer.Ordinal); + foreach (string l in File.ReadAllLines (cacheFile)) { + string line = l?.Trim (); + if (String.IsNullOrEmpty (line) || oldAbis.Contains (line)) + continue; + oldAbis.Add (line); + } + + HashSet currentAbis = null; + FillCurrentAbis (context, ref currentAbis); + + if (oldAbis.Count != currentAbis.Count) + return true; + + foreach (string abi in oldAbis) { + if (!currentAbis.Contains (abi)) + return true; + } + + return false; + } + + void SaveAbiChoice (Context context) + { + HashSet currentAbis = null; + FillCurrentAbis (context, ref currentAbis); + + string cacheFile = Configurables.Paths.MonoRuntimesEnabledAbisCachePath; + Log.DebugLine ($"Writing ABI cache file {cacheFile}"); + File.WriteAllLines (cacheFile, currentAbis); + } + + void FillCurrentAbis (Context context, ref HashSet currentAbis) + { + Utilities.AddAbis (context.Properties.GetRequiredValue (KnownProperties.AndroidSupportedTargetJitAbis).Trim (), ref currentAbis); + Utilities.AddAbis (context.Properties.GetRequiredValue (KnownProperties.AndroidSupportedTargetAotAbis).Trim (), ref currentAbis); + Utilities.AddAbis (context.Properties.GetRequiredValue (KnownProperties.AndroidSupportedHostJitAbis).Trim (), ref currentAbis); + } + + async Task DownloadMonoArchive (Context context) + { + if (context.ForceRuntimesBuild) { + Log.StatusLine ("Mono runtime rebuild forced, Mono Archive download skipped"); + return false; + } + + Log.StatusLine ("Checking if all runtime files are present"); + allRuntimes = new Runtimes (); + bool runtimesFoundAndComplete = true; + foreach (BundleItem item in allRuntimes.BundleItems) { + if (item == null) + continue; + + // BundleItem.SourcePath is the path *after* the file is installed into our tree + if (File.Exists (item.SourcePath)) + continue; + + runtimesFoundAndComplete = false; + Log.DebugLine ($"{item.SourcePath} missing, skipping the rest of file scan"); + Log.StatusLine ($" {context.Characters.Bullet} some Mono Runtime files are missing, download/rebuild forced"); + break; + } + + if (runtimesFoundAndComplete) { + // User might have changed the set of ABIs to build, we need to check and rebuild if necessary + if (!AbiChoiceChanged (context)) { + Log.StatusLine ("Mono runtimes already present and complete. No need to download or build."); + return true; + } + + Log.StatusLine ("Mono already present, but the choice of ABIs changed since previous build, runtime refresh is necessary"); + } + + bool result = await DownloadAndUpackIfNeeded ( + context, + "Mono", + Configurables.Paths.MonoArchiveLocalPath, + Configurables.Paths.MonoArchiveFileName, + Configurables.Paths.MonoSDKSOutputDir + ); + + if (!result) + return false; + + return await DownloadAndUpackIfNeeded ( + context, + "Windows Mono", + Configurables.Paths.MonoArchiveWindowsLocalPath, + Configurables.Paths.MonoArchiveWindowsFileName, + Configurables.Paths.BCLWindowsOutputDir + ); + } + + async Task DownloadAndUpackIfNeeded (Context context, string name, string localPath, string archiveFileName, string destinationDirectory) + { + if (await Utilities.VerifyArchive (localPath)) { + Log.StatusLine ($"{name} archive already downloaded and valid"); + } else { + Utilities.DeleteFileSilent (localPath); + + var url = new Uri (Configurables.Urls.MonoArchive_BaseUri, archiveFileName); + Log.StatusLine ($"Downloading {name} archive"); + + (bool success, ulong size, HttpStatusCode status) = await Utilities.GetDownloadSizeWithStatus (url); + if (!success) { + if (status == HttpStatusCode.NotFound) + Log.Info ($"{name} archive URL not found"); + else + Log.Info ($"Failed to obtain {name} archive size. HTTP status code: {status} ({(int)status})"); + Log.InfoLine (". Mono runtimes will be rebuilt"); + return false; + } + + DownloadStatus downloadStatus = Utilities.SetupDownloadStatus (context, size, context.InteractiveSession); + Log.StatusLine ($" {context.Characters.Link} {url}", ConsoleColor.White); + await Download (context, url, localPath, $"{name} Archive", archiveFileName, downloadStatus); + + if (!File.Exists (localPath)) { + Log.InfoLine ($"Download of {name} archive from {url} failed, Mono will be rebuilt"); + return false; + } + } + + string tempDir = $"{destinationDirectory}.tmp"; + if (!await Utilities.Unpack (localPath, tempDir, cleanDestinatioBeforeUnpacking: true)) { + Utilities.DeleteDirectorySilent (destinationDirectory); + Log.WarningLine ($"Failed to unpack {name} archive {localPath}, Mono will be rebuilt"); + return false; + } + + Log.DebugLine ("Moving unpacked Mono archive from {tempDir} to {destinationDirectory}"); + try { + Utilities.MoveDirectoryContentsRecursively (tempDir, destinationDirectory, resetFileTimestamp: true); + } finally { + Utilities.DeleteDirectorySilent (tempDir); + } + + return true; + } + + bool InstallUtilities (Context context) + { + string destDir = MonoRuntimesHelpers.UtilitiesDestinationDir; + + Utilities.CreateDirectory (destDir); + + string managedRuntime = context.Properties.GetRequiredValue (KnownProperties.ManagedRuntime); + bool haveManagedRuntime = !String.IsNullOrEmpty (managedRuntime); + string remapper = Utilities.GetRelativePath (BuildPaths.XamarinAndroidSourceRoot, context.Properties.GetRequiredValue (KnownProperties.RemapAssemblyRefToolExecutable)); + string targetCecil = Utilities.GetRelativePath (BuildPaths.XamarinAndroidSourceRoot, Path.Combine (Configurables.Paths.BuildBinDir, "Xamarin.Android.Cecil.dll")); + + StatusStep (context, "Installing runtime utilities"); + foreach (MonoUtilityFile muf in allRuntimes.UtilityFilesToInstall) { + (string destFilePath, string debugSymbolsDestPath) = MonoRuntimesHelpers.GetDestinationPaths (muf); + Utilities.CopyFile (muf.SourcePath, destFilePath); + if (!muf.IgnoreDebugInfo) { + if (!String.IsNullOrEmpty (debugSymbolsDestPath)) { + Utilities.CopyFile (muf.DebugSymbolsPath, debugSymbolsDestPath); + } else { + Log.DebugLine ($"Debug symbols not found for utility file {Path.GetFileName (muf.SourcePath)}"); + } + } + + if (!muf.RemapCecil) + continue; + + string relDestFilePath = Utilities.GetRelativePath (BuildPaths.XamarinAndroidSourceRoot, destFilePath); + StatusSubStep (context, $"Remapping Cecil references for {relDestFilePath}"); + bool result = Utilities.RunCommand ( + haveManagedRuntime ? managedRuntime : remapper, // command + BuildPaths.XamarinAndroidSourceRoot, // workingDirectory + true, // ignoreEmptyArguments + + // arguments + haveManagedRuntime ? remapper : String.Empty, + Utilities.GetRelativePath (BuildPaths.XamarinAndroidSourceRoot, muf.SourcePath), + relDestFilePath, + "Mono.Cecil", + targetCecil); + + if (result) + continue; + + Log.ErrorLine ($"Failed to remap cecil reference for {destFilePath}"); + return false; + } + + return true; + } + + bool GenerateFrameworkList (Context contex, string filePath, string bclDir, string facadesDir) + { + Log.DebugLine ($"Generating {filePath}"); + + var contents = new XElement ( + "FileList", + new XAttribute ("Redist", Runtimes.FrameworkListRedist), + new XAttribute ("Name", Runtimes.FrameworkListName), + allRuntimes.BclFilesToInstall.Where (f => f.Type == BclFileType.FacadeAssembly || f.Type == BclFileType.ProfileAssembly).Select (f => ToFileElement (f)) + ); + contents.Save (filePath); + return true; + + XElement ToFileElement (BclFile bcf) + { + Log.Debug ("Writing "); + string fullFilePath; + + switch (bcf.Type) { + case BclFileType.ProfileAssembly: + fullFilePath = Path.Combine (bclDir, bcf.Name); + Log.Debug ("profile"); + break; + + case BclFileType.FacadeAssembly: + Log.Debug ("facade"); + fullFilePath = Path.Combine (facadesDir, bcf.Name); + break; + + default: + Log.Debug ("unsupported"); + fullFilePath = null; + break; + } + + Log.DebugLine ($" BCL assembly {bcf.Name}"); + if (String.IsNullOrEmpty (fullFilePath)) + throw new InvalidOperationException ($"Unsupported BCL file type {bcf.Type}"); + + AssemblyName aname = AssemblyName.GetAssemblyName (fullFilePath); + string version = bcf.Version; + if (String.IsNullOrEmpty (version) && !Runtimes.FrameworkListVersionOverrides.TryGetValue (bcf.Name, out version)) + version = aname.Version.ToString (); + + return new XElement ( + "File", + new XAttribute ("AssemblyName", aname.Name), + new XAttribute ("Version", version), + new XAttribute ("PublicKeyToken", String.Join (String.Empty, aname.GetPublicKeyToken ().Select (b => b.ToString ("x2")))), + new XAttribute ("ProcessorArchitecture", aname.ProcessorArchitecture.ToString ()) + ); + } + } + + bool InstallBCL (Context context) + { + string redistListDir = MonoRuntimesHelpers.BCLRedistListDestinationDir; + + foreach (KeyValuePair kvp in MonoRuntimesHelpers.BCLDestinationDirs) { + Utilities.CreateDirectory (kvp.Value); + } + + foreach (KeyValuePair kvp in MonoRuntimesHelpers.BCLFacadesDestinationDirs) { + Utilities.CreateDirectory (kvp.Value); + } + + Utilities.CreateDirectory (redistListDir); + + StatusStep (context, "Installing Android BCL assemblies"); + InstallBCLFiles (allRuntimes.BclFilesToInstall); + + StatusStep (context, "Installing Designer Host BCL assemblies"); + InstallBCLFiles (allRuntimes.DesignerHostBclFilesToInstall); + + StatusStep (context, "Installing Designer Windows BCL assemblies"); + InstallBCLFiles (allRuntimes.DesignerWindowsBclFilesToInstall); + + Utilities.DeleteDirectorySilent (Configurables.Paths.BCLWindowsOutputDir); + + return GenerateFrameworkList ( + context, + MonoRuntimesHelpers.FrameworkListPath, + MonoRuntimesHelpers.BCLDestinationDirs [BclFileTarget.Android], + MonoRuntimesHelpers.BCLFacadesDestinationDirs [BclFileTarget.Android] + ); + } + + void InstallBCLFiles (List files) + { + foreach (BclFile bf in files) { + (string destFilePath, string debugSymbolsDestPath) = MonoRuntimesHelpers.GetDestinationPaths (bf); + + Utilities.CopyFile (bf.SourcePath, destFilePath); + if (bf.ExcludeDebugSymbols) + continue; + if (debugSymbolsDestPath == null) { + Log.DebugLine ($"Debug symbols not found for BCL file {bf.Name} ({bf.Type})"); + continue; + } + + if (!File.Exists (bf.DebugSymbolsPath)) { + Log.DebugLine ($"Debug symbols file does not exist: {bf.DebugSymbolsPath}"); + continue; + } + + Utilities.CopyFile (bf.DebugSymbolsPath, debugSymbolsDestPath); + } + } + + async Task InstallRuntimes (Context context, List enabledRuntimes) + { + StatusStep (context, "Installing tests"); + foreach (TestAssembly tasm in Runtimes.TestAssemblies) { + string sourceBasePath; + + switch (tasm.TestType) { + case TestAssemblyType.Reference: + case TestAssemblyType.TestRunner: + sourceBasePath = Path.Combine (Configurables.Paths.MonoProfileDir); + break; + + case TestAssemblyType.XUnit: + case TestAssemblyType.NUnit: + case TestAssemblyType.Satellite: + sourceBasePath = Configurables.Paths.BCLTestsSourceDir; + break; + + default: + throw new InvalidOperationException ($"Unsupported test assembly type: {tasm.TestType}"); + } + + (string destFilePath, string debugSymbolsDestPath) = MonoRuntimesHelpers.GetDestinationPaths (tasm); + CopyFile (Path.Combine (sourceBasePath, tasm.Name), destFilePath); + if (debugSymbolsDestPath != null) + CopyFile (Path.Combine (sourceBasePath, Utilities.GetDebugSymbolsPath (tasm.Name)), debugSymbolsDestPath); + } + + StatusSubStep (context, "Creating BCL tests archive"); + Utilities.DeleteFileSilent (MonoRuntimesHelpers.BCLTestsArchivePath); + var sevenZip = new SevenZipRunner (context); + if (!await sevenZip.Zip (MonoRuntimesHelpers.BCLTestsArchivePath, MonoRuntimesHelpers.BCLTestsDestinationDir, new List { "." })) { + Log.ErrorLine ("BCL tests archive creation failed, see the log files for details."); + return false; + } + + StatusStep (context, "Installing runtimes"); + foreach (Runtime runtime in enabledRuntimes) { + StatusSubStep (context, $"Installing {runtime.Flavor} runtime {runtime.Name}"); + + string src, dst; + bool skipFile; + foreach (RuntimeFile rtf in allRuntimes.RuntimeFilesToInstall) { + if (rtf.Shared && rtf.AlreadyCopied) + continue; + + (skipFile, src, dst) = MonoRuntimesHelpers.GetRuntimeFilePaths (runtime, rtf); + if (skipFile) + continue; + + CopyFile (src, dst); + if (!StripFile (runtime, rtf, dst)) + return false; + + if (rtf.Shared) + rtf.AlreadyCopied = true; + } + } + + return true; + + bool StripFile (Runtime runtime, RuntimeFile rtf, string filePath) + { + if (rtf.Type != RuntimeFileType.StrippableBinary) + return true; + + var monoRuntime = runtime.As (); + if (monoRuntime == null || !monoRuntime.CanStripNativeLibrary || !rtf.Strip) + return true; + + if (String.IsNullOrEmpty (monoRuntime.Strip)) { + Log.WarningLine ($"Binary stripping impossible, runtime {monoRuntime.Name} doesn't define the strip command"); + return true; + } + + bool result; + if (!String.IsNullOrEmpty (monoRuntime.StripFlags)) + result = Utilities.RunCommand (monoRuntime.Strip, monoRuntime.StripFlags, filePath); + else + result = Utilities.RunCommand (monoRuntime.Strip, filePath); + + if (result) + return true; + + Log.ErrorLine ($"Failed to strip the binary file {filePath}, see logs for error details"); + return false; + } + + void CopyFile (string src, string dest) + { + if (!CheckFileExists (src, true)) + return; + + Utilities.CopyFile (src, dest); + } + } + + async Task BuildRuntimes (Context context, List makeArguments) + { + var make = new MakeRunner (context); + + bool result = await make.Run ( + logTag: "mono-runtimes", + workingDirectory: GetWorkingDirectory (context), + arguments: makeArguments + ); + + if (!result) + return false; + + SaveAbiChoice (context); + + return true; + } + + string GetWorkingDirectory (Context context) + { + return Path.Combine (context.Properties.GetRequiredValue (KnownProperties.MonoSourceFullPath), "sdks", "builds"); + } + + List GetMakeArguments (Context context, List enabledRuntimes) + { + string workingDirectory = GetWorkingDirectory (context); + return PrepareMakeArguments (context, workingDirectory, enabledRuntimes); + } + + List GetEnabledRuntimes (bool enableLogging) + { + var enabledRuntimes = new List (); + + if (allRuntimes == null) + allRuntimes = new Runtimes (); + return MonoRuntimesHelpers.GetEnabledRuntimes (allRuntimes, enableLogging); + } + + List PrepareMakeArguments (Context context, string workingDirectory, List runtimes) + { + string toolchainsPrefix = Path.Combine (GetProperty (KnownProperties.AndroidToolchainDirectory), "toolchains"); + + var ret = new List { + "DISABLE_IOS=1", + "DISABLE_MAC=1", + $"CONFIGURATION={Configurables.Defaults.MonoSdksConfiguration}", + "IGNORE_PROVISION_MXE=false", + "IGNORE_PROVISION_ANDROID=true", + $"ANDROID_CMAKE_VERSION={GetProperty (KnownProperties.AndroidCmakeVersionPath)}", + $"ANDROID_NDK_VERSION=r{BuildAndroidPlatforms.AndroidNdkVersion}", + $"ANDROID_SDK_VERSION_armeabi-v7a={GetMinimumApi (AbiNames.TargetJit.AndroidArmV7a)}", + $"ANDROID_SDK_VERSION_arm64-v8a={GetMinimumApi (AbiNames.TargetJit.AndroidArmV8a)}", + $"ANDROID_SDK_VERSION_x86={GetMinimumApi (AbiNames.TargetJit.AndroidX86)}", + $"ANDROID_SDK_VERSION_x86_64={GetMinimumApi (AbiNames.TargetJit.AndroidX86_64)}", + $"ANDROID_TOOLCHAIN_DIR={GetProperty (KnownProperties.AndroidToolchainDirectory)}", + $"ANDROID_TOOLCHAIN_CACHE_DIR={GetProperty (KnownProperties.AndroidToolchainCacheDirectory)}", + $"ANDROID_TOOLCHAIN_PREFIX={toolchainsPrefix}", + $"MXE_PREFIX_DIR={GetProperty (KnownProperties.AndroidToolchainDirectory)}", + $"MXE_SRC={Configurables.Paths.MxeSourceDir}" + }; + + runtimeBuildMakeOptions = new List (ret); + runtimeBuildMakeTargets = new List (); + + List standardArgs = null; + var make = new MakeRunner (context); + make.GetStandardArguments (ref standardArgs, workingDirectory); + if (standardArgs != null && standardArgs.Count > 0) { + runtimeBuildMakeOptions.AddRange (standardArgs); + } + + AddHostTargets (runtimes.Where (r => r is MonoHostRuntime)); + AddPackageTargets (runtimes.Where (r => r is MonoJitRuntime)); + AddPackageTargets (runtimes.Where (r => r is MonoCrossRuntime)); + ret.Add ("package-android-bcl"); + AddTargets ("provision-llvm", runtimes.Where (r => r is LlvmRuntime)); + + return ret; + + void AddHostTargets (IEnumerable items) + { + AddTargets ("package-android-host", items); + } + + void AddPackageTargets (IEnumerable items) + { + AddTargets ("package-android", items); + } + + void AddTargets (string prefix, IEnumerable items) + { + foreach (Runtime runtime in items) { + string target = $"{prefix}-{runtime.Name}"; + ret.Add (target); + runtimeBuildMakeTargets.Add (target); + } + } + + string GetProperty (string name) + { + return context.Properties.GetRequiredValue (name); + } + + string GetMinimumApi (string name) + { + return BuildAndroidPlatforms.NdkMinimumAPI [name].ToString (); + } + } + + void MonoRuntime_RuleGenerator (GeneratedMakeRulesFile file, StreamWriter ruleWriter) + { + const string OptionsVariableName = "MONO_RUNTIME_SDKS_MAKE_OPTIONS"; + + if (runtimeBuildMakeOptions == null || runtimeBuildMakeTargets == null) { + List enabledRuntimes = GetEnabledRuntimes (false); + GetMakeArguments (Context.Instance, enabledRuntimes); + + if (runtimeBuildMakeOptions == null || runtimeBuildMakeTargets == null) { + Log.DebugLine ("No rules to generate for Mono SDKs build"); + return; + } + } + + ruleWriter.Write ($"{OptionsVariableName} ="); + foreach (string opt in runtimeBuildMakeOptions) { + ruleWriter.WriteLine (" \\"); + ruleWriter.Write ($"\t{opt}"); + } + ruleWriter.WriteLine (); + + foreach (string target in runtimeBuildMakeTargets) { + ruleWriter.WriteLine (); + ruleWriter.WriteLine ($"sdks-{target}:"); + ruleWriter.WriteLine ($"\t$(MAKE) $({OptionsVariableName}) {target}"); + } + + ruleWriter.WriteLine (); + ruleWriter.WriteLine ("sdks-all:"); + + string allTargets = String.Join (" ", runtimeBuildMakeTargets); + ruleWriter.WriteLine ($"\t$(MAKE) $({OptionsVariableName}) {allTargets}"); + } + + void StatusMessage (Context context, string indent, string message) + { + Log.StatusLine ($"{indent}{context.Characters.Bullet} {message}"); + } + + void StatusStep (Context context, string name) + { + StatusMessage (context, StatusIndent, name);; + } + + void StatusSubStep (Context context, string name) + { + StatusMessage (context, SubStatusIndent, name);; + } + + bool CheckFileExists (string filePath, bool required) + { + if (File.Exists (filePath)) + return true; + + if (required) + throw new InvalidOperationException ($"Required file not found: {filePath}"); + + return false; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_CreateBundle.Unix.cs b/build-tools/xaprepare/xaprepare/Steps/Step_CreateBundle.Unix.cs new file mode 100644 index 000000000..5eff8261f --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Steps/Step_CreateBundle.Unix.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + class Step_CreateBundle : Step + { + public Step_CreateBundle () + : base ("Creating binary bundle") + {} + + protected override async Task Execute (Context context) + { + var allRuntimes = new Runtimes (); + string binRoot = Configurables.Paths.BinDir; + string bundlePath = Path.Combine (binRoot, Configurables.Paths.XABundleFileName); + Log.StatusLine ("Generating bundle archive: ", Utilities.GetRelativePath (BuildPaths.XamarinAndroidSourceRoot, bundlePath), tailColor: ConsoleColor.White); + Utilities.DeleteFileSilent (bundlePath); + + var sevenZip = new SevenZipRunner (context); + CompressionFormat cf = context.CompressionFormat; + Func, Task> compressor; + + if (String.Compare (cf.Name, Configurables.Defaults.ZipCompressionFormatName, StringComparison.OrdinalIgnoreCase) == 0) { + compressor = sevenZip.Zip; + } else if (String.Compare (cf.Name, Configurables.Defaults.SevenZipCompressionFormatName, StringComparison.OrdinalIgnoreCase) == 0) { + compressor = sevenZip.SevenZip; + } else { + throw new InvalidOperationException ($"Unsupported compression type: {cf.Description}"); + } + + List items = allRuntimes.BundleItems.Select ( + item => { + string relPath = Utilities.GetRelativePath (binRoot, item.SourcePath); + Log.DebugLine ($"Bundle item: {item.SourcePath} (archive path: {relPath})"); + return relPath; + } + ).Distinct ().ToList (); + items.Sort (); + + if (!await compressor (bundlePath, binRoot, items)) { + Log.ErrorLine ("Bundle archive creation failed, see the log files for details."); + return false; + } + + return true; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_DownloadNuGet.cs b/build-tools/xaprepare/xaprepare/Steps/Step_DownloadNuGet.cs new file mode 100644 index 000000000..a2bdbbf28 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Steps/Step_DownloadNuGet.cs @@ -0,0 +1,51 @@ +using System; +using System.IO; +using System.Net; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + class Step_DownloadNuGet : StepWithDownloadProgress + { + public Step_DownloadNuGet () + : base ("Download NuGet") + {} + + protected override async Task Execute (Context context) + { + if (context == null) + throw new ArgumentNullException (nameof (context)); + + Uri nugetUrl = Configurables.Urls.NugetUri; + string localNugetPath = Configurables.Paths.LocalNugetPath; + + if (Utilities.FileExists (localNugetPath)) { + Log.StatusLine ($"NuGet already downloaded ({localNugetPath})"); + return true; + } + + Utilities.CreateDirectory (Path.GetDirectoryName (localNugetPath)); + Log.StatusLine ("Downloading NuGet"); + + (bool success, ulong size, HttpStatusCode status) = await Utilities.GetDownloadSizeWithStatus (nugetUrl); + if (!success) { + if (status == HttpStatusCode.NotFound) + Log.ErrorLine ("NuGet URL not found"); + else + Log.ErrorLine ("Failed to obtain NuGet size. HTTP status code: {status} ({(int)status})"); + return false; + } + + DownloadStatus downloadStatus = Utilities.SetupDownloadStatus (context, size, context.InteractiveSession); + Log.StatusLine ($" {context.Characters.Link} {nugetUrl}", ConsoleColor.White); + await Download (context, nugetUrl, localNugetPath, "NuGet", Path.GetFileName (localNugetPath), downloadStatus); + + if (!File.Exists (localNugetPath)) { + Log.ErrorLine ($"Download of NuGet from {nugetUrl} failed"); + return false; + } + + return true; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_GenerateFiles.Unix.cs b/build-tools/xaprepare/xaprepare/Steps/Step_GenerateFiles.Unix.cs new file mode 100644 index 000000000..a82a22fd4 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Steps/Step_GenerateFiles.Unix.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using System.IO; + +namespace Xamarin.Android.Prepare +{ + partial class Step_GenerateFiles + { + partial void AddUnixPostBuildSteps (Context context, List steps) + { + steps.Add (new GeneratedMakeRulesFile (Path.Combine (Configurables.Paths.BuildBinDir, "rules.mk"))); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_GenerateFiles.Windows.cs b/build-tools/xaprepare/xaprepare/Steps/Step_GenerateFiles.Windows.cs new file mode 100644 index 000000000..0a430a02c --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Steps/Step_GenerateFiles.Windows.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Xamarin.Android.Prepare +{ + partial class Step_GenerateFiles + { + partial void AddOSSpecificSteps (Context context, List steps) + { + string javaSdkDirectory = context.Properties.GetValue ("JavaSdkDirectory"); + if (String.IsNullOrEmpty (javaSdkDirectory)) + javaSdkDirectory = context.OS.JavaHome; + + string jdkJvmPath = Path.Combine (javaSdkDirectory, "jre", "bin", "server", "jvm.dll"); + string jdkIncludePathShared = Path.Combine (javaSdkDirectory, "include"); + string jdkIncludePathOS = Path.Combine (jdkIncludePathShared, "win32"); + + var replacements = new Dictionary (StringComparer.Ordinal) { + { "@JdkJvmPath@", jdkJvmPath }, + { "@JdkIncludePathShared@", jdkIncludePathShared }, + { "@JdkIncludePathOS@", jdkIncludePathOS }, + { "@javac@", context.OS.JavaCPath }, + { "@java@", context.OS.JavaPath }, + { "@jar@", context.OS.JarPath }, + }; + + var step = new GeneratedPlaceholdersFile ( + replacements, + Path.Combine (Configurables.Paths.BootstrapResourcesDir, "JdkInfo.Windows.props.in"), + Path.Combine (Configurables.Paths.ExternalJavaInteropDir, "bin", $"Build{context.Configuration}", "JdkInfo.props") + ); + + steps.Add (step); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_GenerateFiles.cs b/build-tools/xaprepare/xaprepare/Steps/Step_GenerateFiles.cs new file mode 100644 index 000000000..1c0b3a11d --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Steps/Step_GenerateFiles.cs @@ -0,0 +1,195 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + partial class Step_GenerateFiles : Step + { + bool atBuildStart; + + public Step_GenerateFiles (bool atBuildStart) + : base ("Generating files required by the build") + { + this.atBuildStart = atBuildStart; + } + +#pragma warning disable CS1998 + protected override async Task Execute (Context context) + { + List filesToGenerate = GetFilesToGenerate (context); + if (filesToGenerate != null && filesToGenerate.Count > 0) { + foreach (GeneratedFile gf in filesToGenerate) { + if (gf == null) + continue; + + Log.Status ("Generating "); + Log.Status (Utilities.GetRelativePath (BuildPaths.XamarinAndroidSourceRoot, gf.OutputPath), ConsoleColor.White); + if (!String.IsNullOrEmpty (gf.InputPath)) + Log.StatusLine ($" {context.Characters.LeftArrow} ", Utilities.GetRelativePath (BuildPaths.XamarinAndroidSourceRoot, gf.InputPath), leadColor: ConsoleColor.Cyan, tailColor: ConsoleColor.White); + else + Log.StatusLine (); + + gf.Generate (context); + } + } + + return true; + } +#pragma warning restore CS1998 + + List GetFilesToGenerate (Context context) + { + if (atBuildStart) { + return new List { + Get_Configuration_OperatingSystem_props (context), + Get_Ndk_projitems (context), + Get_XABuildPaths_cs (context), + Get_XABuildConfig_cs (context), + Get_mingw_32_cmake (context), + Get_mingw_64_cmake (context), + Get_bundle_path_targets (context), + }; + } + + var steps = new List { + new GeneratedProfileAssembliesProjitemsFile (Configurables.Paths.ProfileAssembliesProjitemsPath), + }; + + AddOSSpecificSteps (context, steps); + AddUnixPostBuildSteps (context, steps); + + return steps; + } + + partial void AddUnixPostBuildSteps (Context context, List steps); + partial void AddOSSpecificSteps (Context context, List steps); + + GeneratedFile Get_Configuration_OperatingSystem_props (Context context) + { + const string OutputFileName = "Configuration.OperatingSystem.props"; + + string javaSdkDirectory = context.Properties.GetValue ("JavaSdkDirectory"); + if (String.IsNullOrEmpty (javaSdkDirectory)) + javaSdkDirectory = context.OS.JavaHome; + + var replacements = new Dictionary (StringComparer.Ordinal) { + { "@OS_NAME@", context.OS.Name ?? String.Empty }, + { "@HOST_OS_FLAVOR@", context.OS.Flavor ?? String.Empty }, + { "@OS_RELEASE@", context.OS.Release ?? String.Empty }, + { "@HOST_TRIPLE@", context.OS.Triple ?? String.Empty }, + { "@HOST_TRIPLE32@", context.OS.Triple32 ?? String.Empty }, + { "@HOST_TRIPLE64@", context.OS.Triple64 ?? String.Empty }, + { "@HOST_CPUS@", context.OS.CPUCount.ToString () }, + { "@ARCHITECTURE_BITS@", context.OS.Is64Bit ? "64" : "32" }, + { "@HOST_CC@", context.OS.CC ?? String.Empty }, + { "@HOST_CXX@", context.OS.CXX ?? String.Empty }, + { "@HOST_CC32@", context.OS.CC32 ?? String.Empty }, + { "@HOST_CC64@", context.OS.CC64 ?? String.Empty }, + { "@HOST_CXX32@", context.OS.CXX32 ?? String.Empty }, + { "@HOST_CXX64@", context.OS.CXX64 ?? String.Empty }, + { "@HOST_HOMEBREW_PREFIX@", context.OS.HomebrewPrefix ?? String.Empty }, + { "@JavaSdkDirectory@", javaSdkDirectory ?? String.Empty }, + { "@javac@", context.OS.JavaCPath }, + { "@java@", context.OS.JavaPath }, + { "@jar@", context.OS.JarPath }, + }; + + return new GeneratedPlaceholdersFile ( + replacements, + Path.Combine (Configurables.Paths.BootstrapResourcesDir, $"{OutputFileName}.in"), + Path.Combine (BuildPaths.XamarinAndroidSourceRoot, OutputFileName) + ); + } + + GeneratedFile Get_XABuildPaths_cs (Context context) + { + const string OutputFileName = "XABuildPaths.cs"; + + var replacements = new Dictionary (StringComparer.Ordinal) { + { "@CONFIGURATION@", context.Configuration }, + { "@TOP_DIRECTORY@", BuildPaths.XamarinAndroidSourceRoot }, + }; + + return new GeneratedPlaceholdersFile ( + replacements, + Path.Combine (Configurables.Paths.BuildToolsScriptsDir, $"{OutputFileName}.in"), + Path.Combine (Configurables.Paths.TestBinDir, OutputFileName) + ); + } + + GeneratedFile Get_XABuildConfig_cs (Context context) + { + const string OutputFileName = "XABuildConfig.cs"; + + var replacements = new Dictionary (StringComparer.Ordinal) { + { "@NDK_REVISION@", context.BuildInfo.NDKRevision }, + { "@NDK_RELEASE@", BuildAndroidPlatforms.AndroidNdkVersion }, + { "@NDK_MINIMUM_API_AVAILABLE@", context.BuildInfo.NDKMinimumApiAvailable }, + { "@NDK_VERSION_MAJOR@", context.BuildInfo.NDKVersionMajor }, + { "@NDK_VERSION_MINOR@", context.BuildInfo.NDKVersionMinor }, + { "@NDK_VERSION_MICRO@", context.BuildInfo.NDKVersionMicro }, + { "@NDK_ARMEABI_V7_API@", BuildAndroidPlatforms.NdkMinimumAPI [AbiNames.TargetJit.AndroidArmV7a].ToString () }, + { "@NDK_ARM64_V8A_API@", BuildAndroidPlatforms.NdkMinimumAPI [AbiNames.TargetJit.AndroidArmV8a].ToString () }, + { "@NDK_X86_API@", BuildAndroidPlatforms.NdkMinimumAPI [AbiNames.TargetJit.AndroidX86].ToString () }, + { "@NDK_X86_64_API@", BuildAndroidPlatforms.NdkMinimumAPI [AbiNames.TargetJit.AndroidX86_64].ToString () }, + { "@XA_SUPPORTED_ABIS@", context.Properties.GetRequiredValue (KnownProperties.AndroidSupportedTargetJitAbis).Replace (':', ';') }, + }; + + return new GeneratedPlaceholdersFile ( + replacements, + Path.Combine (Configurables.Paths.BuildToolsScriptsDir, $"{OutputFileName}.in"), + Path.Combine (Configurables.Paths.BuildBinDir, OutputFileName) + ); + } + + GeneratedFile Get_Ndk_projitems (Context context) + { + const string OutputFileName = "Ndk.projitems"; + + var replacements = new Dictionary (StringComparer.Ordinal) { + { "@NDK_RELEASE@", BuildAndroidPlatforms.AndroidNdkVersion }, + { "@NDK_ARMEABI_V7_API@", BuildAndroidPlatforms.NdkMinimumAPI [AbiNames.TargetJit.AndroidArmV7a].ToString () }, + { "@NDK_ARM64_V8A_API@", BuildAndroidPlatforms.NdkMinimumAPI [AbiNames.TargetJit.AndroidArmV8a].ToString () }, + { "@NDK_X86_API@", BuildAndroidPlatforms.NdkMinimumAPI [AbiNames.TargetJit.AndroidX86].ToString () }, + { "@NDK_X86_64_API@", BuildAndroidPlatforms.NdkMinimumAPI [AbiNames.TargetJit.AndroidX86_64].ToString () }, + }; + + return new GeneratedPlaceholdersFile ( + replacements, + Path.Combine (Configurables.Paths.BuildToolsScriptsDir, $"{OutputFileName}.in"), + Path.Combine (Configurables.Paths.BuildBinDir, OutputFileName) + ); + } + + GeneratedFile Get_mingw_32_cmake (Context context) + { + return Get_mingw_cmake (context, Configurables.Paths.Mingw32CmakeTemplatePath, Configurables.Paths.Mingw32CmakePath); + } + + GeneratedFile Get_mingw_64_cmake (Context context) + { + return Get_mingw_cmake (context, Configurables.Paths.Mingw64CmakeTemplatePath, Configurables.Paths.Mingw64CmakePath); + } + + GeneratedFile Get_mingw_cmake (Context context, string input, string output) + { + var replacements = new Dictionary (StringComparer.Ordinal) { + { "@HOMEBREW_PREFIX@", context.OS.HomebrewPrefix ?? String.Empty }, + }; + + return new GeneratedPlaceholdersFile (replacements, Path.Combine (input), Path.Combine (output)); + } + + GeneratedFile Get_bundle_path_targets (Context context) + { + var replacements = new Dictionary (StringComparer.Ordinal) { + { "@XA_BUNDLE_VERSION@", Configurables.Defaults.XABundleVersion }, + { "@XA_BUNDLE_FILE_NAME@", Configurables.Paths.XABundleFileName }, + }; + + return new GeneratedPlaceholdersFile (replacements, Configurables.Paths.BundlePathTemplate, Configurables.Paths.BundlePathOutput); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_Get_Windows_GAS.Unix.cs b/build-tools/xaprepare/xaprepare/Steps/Step_Get_Windows_GAS.Unix.cs new file mode 100644 index 000000000..c0e294d15 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Steps/Step_Get_Windows_GAS.Unix.cs @@ -0,0 +1,740 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + class Step_Get_Windows_GAS : Step + { + const int EndFileChunkSize = 65535 + 22; // Maximum comment size + EOCD size + const uint EOCDSignature = 0x06054b50; + const uint CDHeaderSignature = 0x02014b50; + const uint LFHeaderSignature = 0x04034b50; + + class EOCD + { + public uint Signature; // Signature (0x06054b50) + public ushort DiskNumber; // number of this disk + public ushort CDStartDisk; // number of the disk with the start of the central directory + public ushort TotalEntriesThisDisk; // total number of entries in the central directory on this disk + public ushort TotalEntries; // total number of entries in the central directory + public uint CDSize; // size of the central directory + public uint CDOffset; // offset of start of central directory with respect to the starting disk number + public ushort CommentLength; // .ZIP file comment length + }; + + class CDHeader + { + public uint Signature; // 0x02014b50 + public ushort VersionMadeBy; + public ushort VersionNeededToExtract; + public ushort GeneralPurposeBitFlag; + public ushort CompressionMethod; + public ushort LastModFileTime; + public ushort LastModFileDate; + public uint CRC32; + public uint CompressedSize; + public uint UncompressedSize; + public ushort FileNameLength; + public ushort ExtraFieldLength; + public ushort FileCommentLength; + public ushort DiskNumberStart; + public ushort InternalFileAttributes; + public uint ExternalFileAttributes; + public uint RelativeOffsetOfLocalHeader; + public string FileName; + public byte[] ExtraField; + public string FileComment; + }; + + class LFHeader + { + public uint Signature; // 0x04034b50 + public ushort VersionNeededToExtract; + public ushort GeneralPurposeBitFlag; + public ushort CompressionMethod; + public ushort LastModFileTime; + public ushort LastModFileDate; + public uint CRC32; + public uint CompressedSize; + public uint UncompressedSize; + public ushort FileNameLength; + public ushort ExtraFieldLength; + public string FileName; + public byte[] ExtraField; + }; + + public Step_Get_Windows_GAS () + : base ("Downloading NDK tools for Windows") + {} + + protected override async Task Execute (Context context) + { + string ndkVersion = BuildAndroidPlatforms.AndroidNdkVersion; + + var neededFiles = new HashSet (StringComparer.OrdinalIgnoreCase) { + $"android-ndk-r{ndkVersion}/toolchains/llvm/prebuilt/windows-x86_64/bin/i686-linux-android-as.exe", + $"android-ndk-r{ndkVersion}/toolchains/llvm/prebuilt/windows-x86_64/bin/arm-linux-androideabi-as.exe", + $"android-ndk-r{ndkVersion}/toolchains/llvm/prebuilt/windows-x86_64/bin/x86_64-linux-android-as.exe", + $"android-ndk-r{ndkVersion}/toolchains/llvm/prebuilt/windows-x86_64/bin/aarch64-linux-android-as.exe", + }; + + string destinationDirectory = Path.Combine (Configurables.Paths.InstallMSBuildDir); + int existingFiles = 0; + foreach (string f in neededFiles) { + string file = Path.Combine (destinationDirectory, Path.GetFileName (f)); + string stampFile = GetStampFile (f, destinationDirectory, ndkVersion); + if (File.Exists (file)) { + Log.DebugLine ($"{file} exists"); + if (File.Exists (stampFile)) { + existingFiles++; + } else { + Log.DebugLine ($"Stamp file {stampFile} does not exist, will need to download the executable again"); + } + } + } + + if (existingFiles == neededFiles.Count) { + Log.StatusLine ("All Windows GAS binaries already downloaded."); + return true; + } + + bool result = await FetchFiles ( + neededFiles, + destinationDirectory, + new Uri (AndroidToolchain.AndroidUri, $"android-ndk-r{ndkVersion}-windows-x86_64.zip") + ); + + if (!result) + return false; + + StampFiles (neededFiles, destinationDirectory, ndkVersion); + return true; + } + + void StampFiles (HashSet neededFiles, string destinationDirectory, string ndkVersion) + { + var now = DateTime.UtcNow; + foreach (string file in neededFiles) { + File.WriteAllText (GetStampFile (file, destinationDirectory, ndkVersion), now.ToString ()); + } + } + + string GetStampFile (string file, string destinationDirectory, string ndkVersion) + { + return Path.Combine (destinationDirectory, $"{Path.GetFileName (file)}.{ndkVersion}"); + } + + async Task FetchFiles (HashSet neededFiles, string destinationDirectory, Uri url) + { + Utilities.CreateDirectory (destinationDirectory); + using (var httpClient = new HttpClient ()) { + bool success; + long size; + + Log.StatusLine ($"Accessing {url}"); + (success, size) = await GetFileSize (httpClient, url); + if (!success) + return false; + + Log.DebugLine ($" File size: {size}"); + + EOCD eocd; + (success, eocd) = await GetEOCD (httpClient, url, size); + if (!success) { + Log.ErrorLine ("Failed to find the End of Central Directory record"); + return false; + } + + if (eocd.DiskNumber != 0) + throw new InvalidOperationException ("Multi-disk ZIP archives not supported"); + + Log.DebugLine ($" Central Directory offset: {eocd.CDOffset} (0x{eocd.CDOffset:x})"); + Log.DebugLine ($" Central Directory size: {eocd.CDSize} (0x{eocd.CDSize})"); + Log.DebugLine ($" Total Entries: {eocd.TotalEntries}"); + + Stream cd; + (success, cd) = await ReadCD (httpClient, url, eocd.CDOffset, eocd.CDSize, size); + if (!success) { + Log.ErrorLine ("Failed to read the Central Directory"); + return false; + } + + Log.StatusLine ("Files:"); + if (!await ProcessEntries (httpClient, url, eocd, cd, neededFiles, destinationDirectory)) + return false; + } + + return true; + } + + async Task ProcessEntries (HttpClient httpClient, Uri url, EOCD eocd, Stream centralDirectory, HashSet neededFiles, string destinationDirectory) + { + long foundEntries = 0; + + using (var br = new BinaryReader (centralDirectory)) { + long nread = 0; + long nentries = 1; + + while (nread < centralDirectory.Length && nentries <= eocd.TotalEntries) { + (bool success, CDHeader cdh) = ReadCDHeader (br, centralDirectory.Length, ref nread); + nentries++; + if (!success) { + Log.ErrorLine ($"Failed to read a Central Directory file header for entry {nentries}"); + return false; + } + + if (!neededFiles.Contains (cdh.FileName)) + continue; + + if (!await ReadEntry (httpClient, url, cdh, br, destinationDirectory)) + return false; + foundEntries++; + } + } + + if (foundEntries < neededFiles.Count) { + Log.ErrorLine ($"Could not find all required binaries. Found {foundEntries} out of {neededFiles.Count}"); + return false; + } + + return true; + } + + async Task ReadEntry (HttpClient httpClient, Uri url, CDHeader cdh, BinaryReader br, string destinationDirectory) + { + Context context = Context.Instance; + string destFilePath = Path.Combine (destinationDirectory, Path.GetFileName (cdh.FileName)); + string compressedFilePath = Path.Combine (destinationDirectory, $"{destFilePath}.deflated"); + Log.Status ($" {context.Characters.Bullet} {Path.GetFileName (cdh.FileName)} "); + Log.Status ($"{context.Characters.RightArrow}", ConsoleColor.Cyan); + Log.StatusLine ($" {Utilities.GetRelativePath (BuildPaths.XamarinAndroidSourceRoot, destFilePath)}"); + Log.DebugLine ($" {cdh.FileName} (offset: {cdh.RelativeOffsetOfLocalHeader})"); + + (bool success, Stream contentStream) = await ReadFileData (httpClient, url, cdh); + if (!success) { + Log.ErrorLine ("Failed to read file data"); + return false; + } + + using (var destFile = new BinaryWriter (File.OpenWrite (compressedFilePath))) { + using (var fbr = new BinaryReader (contentStream)) { + if (!await DownloadAndExtract (fbr, contentStream, destFile, compressedFilePath)) + return CleanupAndReturn (false); + } + } + + return CleanupAndReturn (true); + + bool CleanupAndReturn (bool retval) + { + if (File.Exists (compressedFilePath)) + File.Delete (compressedFilePath); + + return retval; + } + } + + async Task DownloadAndExtract (BinaryReader fbr, Stream contentStream, BinaryWriter destFile, string destFileName) + { + long fread = 0; + (bool success, LFHeader lfh) = ReadLFHeader (fbr, contentStream.Length, ref fread); + if (!success) { + Log.ErrorLine ("Failed to read local file header"); + return false; + } + + uint dread = 0; + var buffer = new byte [8192]; + while (fread <= contentStream.Length && dread < lfh.CompressedSize) { + uint toRead; + if (lfh.CompressedSize - dread < buffer.Length) + toRead = lfh.CompressedSize - dread; + else + toRead = (uint)buffer.Length; + + int bread = await contentStream.ReadAsync (buffer, 0, (int)toRead); + if (bread == 0) + break; + destFile.Write (buffer, 0, bread); + fread += bread; + dread += (uint)bread; + } + + destFile.Flush (); + destFile.Close (); + destFile.Dispose (); + Extract (destFileName, lfh.CRC32); + + if (dread != lfh.CompressedSize) + Log.ErrorLine ($" Invalid data size: expected {lfh.CompressedSize} bytes, read {dread} bytes"); + + return true; + } + + void Extract (string compressedFilePath, uint crc32FromHeader) + { + string outputFile = Path.Combine (Path.GetDirectoryName (compressedFilePath), Path.GetFileNameWithoutExtension (compressedFilePath)); + using (var fs = File.OpenRead (compressedFilePath)) { + using (var dfs = File.OpenWrite (outputFile)) { + Extract (fs, dfs, crc32FromHeader); + } + } + } + + void Extract (Stream src, Stream dest, uint crc32FromHeader) + { + uint fileCRC = 0; + int fread = 0; + var crc32 = new CRC32 (); + var buffer = new byte [8192]; + + using (var iis = new DeflateStream (src, CompressionMode.Decompress)) { + while (true) { + fread = iis.Read (buffer, 0, buffer.Length); + if (fread <= 0) + break; + + fileCRC = crc32.Append (fileCRC, buffer, 0, fread); + dest.Write (buffer, 0, fread); + } + dest.Flush (); + } + + if (fileCRC != crc32FromHeader) + Log.ErrorLine ($" Invalid CRC32: expected 0x{crc32FromHeader:x}, got 0x{fileCRC:x}"); + } + + async Task<(bool success, Stream data)> ReadFileData (HttpClient httpClient, Uri url, CDHeader cdh) + { + long fileOffset = cdh.RelativeOffsetOfLocalHeader; + long dataSize = + cdh.CompressedSize + + 30 + // local file header size, the static portion + cdh.FileName.Length + // They're the same in both haders + cdh.ExtraFieldLength + // This may differ between headers... + 16384; // ...so we add some extra padding + + var req = new HttpRequestMessage (HttpMethod.Get, url); + req.Headers.ConnectionClose = true; + req.Headers.Range = new RangeHeaderValue (fileOffset, fileOffset + dataSize); + + HttpResponseMessage resp = await httpClient.SendAsync (req).ConfigureAwait (false); + + if (!resp.IsSuccessStatusCode) { + Log.ErrorLine ($"Failed to read file data: HTTP error {resp.StatusCode}"); + return (false, null); + } + + Stream s = await resp.Content.ReadAsStreamAsync ().ConfigureAwait (false); + if (s.Length < dataSize) { + Log.ErrorLine ($"Failed to read file data: invalid data length ({s.Length} < {dataSize})"); + s.Dispose (); + return (false, null); + } + + return (true, s); + } + + (bool success, LFHeader lfh) ReadLFHeader (BinaryReader cdr, long dataLength, ref long nread) + { + var lfh = new LFHeader (); + + bool worked; + lfh.Signature = ReadUInt (cdr, dataLength, ref nread, out worked); + if (!worked || lfh.Signature != LFHeaderSignature) { + Log.ErrorLine ($"Invalid signature ({lfh.Signature:x} != {LFHeaderSignature:x})"); + goto failed; + } + + lfh.VersionNeededToExtract = ReadUShort (cdr, dataLength, ref nread, out worked); + if (!worked) { + goto failed; + } + + lfh.GeneralPurposeBitFlag = ReadUShort (cdr, dataLength, ref nread, out worked); + if (!worked) { + goto failed; + } + + lfh.CompressionMethod = ReadUShort (cdr, dataLength, ref nread, out worked); + if (!worked) { + goto failed; + } + + lfh.LastModFileTime = ReadUShort (cdr, dataLength, ref nread, out worked); + if (!worked) { + goto failed; + } + + lfh.LastModFileDate = ReadUShort (cdr, dataLength, ref nread, out worked); + if (!worked) { + goto failed; + } + + lfh.CRC32 = ReadUInt (cdr, dataLength, ref nread, out worked); + if (!worked) { + goto failed; + } + + lfh.CompressedSize = ReadUInt (cdr, dataLength, ref nread, out worked); + if (!worked) { + goto failed; + } + + lfh.UncompressedSize = ReadUInt (cdr, dataLength, ref nread, out worked); + if (!worked) { + goto failed; + } + + lfh.FileNameLength = ReadUShort (cdr, dataLength, ref nread, out worked); + if (!worked) { + goto failed; + } + + lfh.ExtraFieldLength = ReadUShort (cdr, dataLength, ref nread, out worked); + if (!worked) { + goto failed; + } + + byte[] bytes = ReadBytes (cdr, lfh.FileNameLength, dataLength, ref nread, out worked); + if (!worked) { + goto failed; + } + + lfh.FileName = Encoding.ASCII.GetString (bytes); + if (!worked) { + goto failed; + } + + if (lfh.ExtraFieldLength > 0) { + lfh.ExtraField = ReadBytes (cdr, lfh.ExtraFieldLength, dataLength, ref nread, out worked); + if (!worked) { + goto failed; + } + } + + return (true, lfh); + + failed: + return (false, null); + } + + (bool success, CDHeader cdh) ReadCDHeader (BinaryReader cdr, long dataLength, ref long nread) + { + var cdh = new CDHeader (); + + bool worked; + cdh.Signature = ReadUInt (cdr, dataLength, ref nread, out worked); + if (!worked || cdh.Signature != CDHeaderSignature) { + Log.ErrorLine ($"Invalid signature ({cdh.Signature:x} != {CDHeaderSignature:x})"); + goto failed; + } + + cdh.VersionMadeBy = ReadUShort (cdr, dataLength, ref nread, out worked); + if (!worked) { + goto failed; + } + + cdh.VersionNeededToExtract = ReadUShort (cdr, dataLength, ref nread, out worked); + if (!worked) { + goto failed; + } + + cdh.GeneralPurposeBitFlag = ReadUShort (cdr, dataLength, ref nread, out worked); + if (!worked) { + goto failed; + } + + cdh.CompressionMethod = ReadUShort (cdr, dataLength, ref nread, out worked); + if (!worked) { + goto failed; + } + + cdh.LastModFileTime = ReadUShort (cdr, dataLength, ref nread, out worked); + if (!worked) { + goto failed; + } + + cdh.LastModFileDate = ReadUShort (cdr, dataLength, ref nread, out worked); + if (!worked) { + goto failed; + } + + cdh.CRC32 = ReadUInt (cdr, dataLength, ref nread, out worked); + if (!worked) { + goto failed; + } + + cdh.CompressedSize = ReadUInt (cdr, dataLength, ref nread, out worked); + if (!worked) { + goto failed; + } + + cdh.UncompressedSize = ReadUInt (cdr, dataLength, ref nread, out worked); + if (!worked) { + goto failed; + } + + cdh.FileNameLength = ReadUShort (cdr, dataLength, ref nread, out worked); + if (!worked) { + goto failed; + } + + cdh.ExtraFieldLength = ReadUShort (cdr, dataLength, ref nread, out worked); + if (!worked) { + goto failed; + } + + cdh.FileCommentLength = ReadUShort (cdr, dataLength, ref nread, out worked); + if (!worked) { + goto failed; + } + + cdh.DiskNumberStart = ReadUShort (cdr, dataLength, ref nread, out worked); + if (!worked) { + goto failed; + } + + cdh.InternalFileAttributes = ReadUShort (cdr, dataLength, ref nread, out worked); + if (!worked) { + goto failed; + } + + cdh.ExternalFileAttributes = ReadUInt (cdr, dataLength, ref nread, out worked); + if (!worked) { + goto failed; + } + + cdh.RelativeOffsetOfLocalHeader = ReadUInt (cdr, dataLength, ref nread, out worked); + if (!worked) { + goto failed; + } + + byte[] bytes = ReadBytes (cdr, cdh.FileNameLength, dataLength, ref nread, out worked); + if (!worked) { + goto failed; + } + + cdh.FileName = Encoding.ASCII.GetString (bytes); + if (!worked) { + goto failed; + } + + if (cdh.ExtraFieldLength > 0) { + cdh.ExtraField = ReadBytes (cdr, cdh.ExtraFieldLength, dataLength, ref nread, out worked); + if (!worked) { + goto failed; + } + } + + if (cdh.FileCommentLength > 0) { + bytes = ReadBytes (cdr, cdh.FileCommentLength, dataLength, ref nread, out worked); + if (!worked) { + goto failed; + } + cdh.FileComment = Encoding.ASCII.GetString (bytes); + } + + return (true, cdh); + + failed: + return (false, null); + } + + ushort ReadUShort (BinaryReader br, long dataLength, ref long nread, out bool success) + { + success = false; + if (dataLength - nread < 2) + return 0; + + ushort ret = br.ReadUInt16 (); + nread += 2; + + success = true; + return ret; + } + + uint ReadUInt (BinaryReader br, long dataLength, ref long nread, out bool success) + { + success = false; + if (dataLength - nread < 4) + return 0; + + uint ret = br.ReadUInt32 (); + nread += 4; + + success = true; + return ret; + } + + byte[] ReadBytes (BinaryReader br, int neededBytes, long dataLength, ref long nread, out bool success) + { + success = false; + if (dataLength - nread < neededBytes) + return null; + + byte[] ret = br.ReadBytes (neededBytes); + nread += neededBytes; + + success = true; + return ret; + } + + async Task<(bool success, Stream cd)> ReadCD (HttpClient httpClient, Uri url, uint cdOffset, uint cdSize, long fileSize) + { + long fileOffset = cdOffset; + var req = new HttpRequestMessage (HttpMethod.Get, url); + req.Headers.ConnectionClose = true; + req.Headers.Range = new RangeHeaderValue (fileOffset, fileOffset + cdSize); + + HttpResponseMessage resp = await httpClient.SendAsync (req).ConfigureAwait (false); + if (!resp.IsSuccessStatusCode) { + Log.ErrorLine ($"Failed to read Central Directory: HTTP error {resp.StatusCode}"); + return (false, null); + } + + Stream s = await resp.Content.ReadAsStreamAsync ().ConfigureAwait (false); + if (s.Length < cdSize) { + Log.ErrorLine ($"Failed to read Central Directory: invalid data length ({s.Length} < {cdSize})"); + s.Dispose (); + return (false, null); + } + + return (true, s); + } + + async Task<(bool success, EOCD eocd)> GetEOCD (HttpClient httpClient, Uri url, long fileSize) + { + long fileOffset = fileSize - EndFileChunkSize; + var req = new HttpRequestMessage (HttpMethod.Get, url); + req.Headers.ConnectionClose = true; + req.Headers.Range = new RangeHeaderValue (fileOffset, fileSize); + + HttpResponseMessage resp = await httpClient.SendAsync (req).ConfigureAwait (false); + if (!resp.IsSuccessStatusCode) + return (false, null); + + using (var eocdStream = await resp.Content.ReadAsStreamAsync ().ConfigureAwait (false)) { + using (var sr = new BinaryReader (eocdStream)) { + byte[] expected = {0x50, 0x4b, 0x05, 0x06}; + int expectedPos = 0; + + for (int i = 0; i < eocdStream.Length; i++) { + byte b = sr.ReadByte (); + if (b != expected [expectedPos]) { + expectedPos = 0; + continue; + } + + if (expectedPos == expected.Length - 1) { + // We've found the signature + var eocd = new EOCD (); + eocd.Signature = 0x06054b50; + eocd.DiskNumber = sr.ReadUInt16 (); + eocd.CDStartDisk = sr.ReadUInt16 (); + eocd.TotalEntriesThisDisk = sr.ReadUInt16 (); + eocd.TotalEntries = sr.ReadUInt16 (); + eocd.CDSize = sr.ReadUInt32 (); + eocd.CDOffset = sr.ReadUInt32 (); + eocd.CommentLength = sr.ReadUInt16 (); + + return (true, eocd); + } + + expectedPos++; + if (expectedPos >= expected.Length) + expectedPos = 0; + } + } + } + + return (false, null); + } + + async Task<(bool success, long size)> GetFileSize (HttpClient httpClient, Uri url) + { + var req = new HttpRequestMessage (HttpMethod.Head, url); + req.Headers.ConnectionClose = true; + + HttpResponseMessage resp = await httpClient.SendAsync (req).ConfigureAwait (false); + if (!resp.IsSuccessStatusCode || !resp.Content.Headers.ContentLength.HasValue) + return (false, 0); + + return (true, resp.Content.Headers.ContentLength.Value); + } + } + + // + // Taken from: + // https://github.com/force-net/Crc32.NET/blob/fbc1061b0cb53df2322d5aed33167a2e6335970b/Crc32.NET/SafeProxy.cs + // + // License: MIT + // https://github.com/force-net/Crc32.NET/blob/fbc1061b0cb53df2322d5aed33167a2e6335970b/LICENSE + // + class CRC32 + { + const uint Poly = 0xedb88320u; + + readonly uint[] _table = new uint[16 * 256]; + + internal CRC32 () + { + Init (Poly); + } + + protected void Init (uint poly) + { + var table = _table; + for (uint i = 0; i < 256; i++) { + uint res = i; + for (int t = 0; t < 16; t++) { + for (int k = 0; k < 8; k++) res = (res & 1) == 1 ? poly ^ (res >> 1) : (res >> 1); + table[(t * 256) + i] = res; + } + } + } + + public uint Append (uint crc, byte[] input, int offset, int length) + { + uint crcLocal = uint.MaxValue ^ crc; + + uint[] table = _table; + while (length >= 16) { + var a = table[(3 * 256) + input[offset + 12]] + ^ table[(2 * 256) + input[offset + 13]] + ^ table[(1 * 256) + input[offset + 14]] + ^ table[(0 * 256) + input[offset + 15]]; + + var b = table[(7 * 256) + input[offset + 8]] + ^ table[(6 * 256) + input[offset + 9]] + ^ table[(5 * 256) + input[offset + 10]] + ^ table[(4 * 256) + input[offset + 11]]; + + var c = table[(11 * 256) + input[offset + 4]] + ^ table[(10 * 256) + input[offset + 5]] + ^ table[(9 * 256) + input[offset + 6]] + ^ table[(8 * 256) + input[offset + 7]]; + + var d = table[(15 * 256) + ((byte)crcLocal ^ input[offset])] + ^ table[(14 * 256) + ((byte)(crcLocal >> 8) ^ input[offset + 1])] + ^ table[(13 * 256) + ((byte)(crcLocal >> 16) ^ input[offset + 2])] + ^ table[(12 * 256) + ((crcLocal >> 24) ^ input[offset + 3])]; + + crcLocal = d ^ c ^ b ^ a; + offset += 16; + length -= 16; + } + + while (--length >= 0) + crcLocal = table[(byte)(crcLocal ^ input[offset++])] ^ crcLocal >> 8; + + return crcLocal ^ uint.MaxValue; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_InstallAnt.Windows.cs b/build-tools/xaprepare/xaprepare/Steps/Step_InstallAnt.Windows.cs new file mode 100644 index 000000000..108b81f52 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Steps/Step_InstallAnt.Windows.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + class Step_InstallAnt : StepWithDownloadProgress + { + public Step_InstallAnt () + : base ("Install Ant") + {} + + protected override async Task Execute (Context context) + { + string localPackagePath = Configurables.Paths.AntArchivePath; + + if (await Utilities.VerifyArchive (localPackagePath)) { + Log.StatusLine ("Ant archive already downloaded and valid"); + } else { + Uri antUrl = new Uri (Configurables.Urls.AntBaseUri, Configurables.Paths.AntArchiveName); + + Log.StatusLine ("Ant URL: ", $"{antUrl}", tailColor: ConsoleColor.Cyan); + + HttpStatusCode status; + bool success; + ulong size; + + (success, size, status) = await Utilities.GetDownloadSizeWithStatus (antUrl); + if (!success) { + Log.ErrorLine ($"Failed to access Ant at {antUrl} (HTTP status: {status})"); + return false; + } + + DownloadStatus downloadStatus = Utilities.SetupDownloadStatus (context, size, context.InteractiveSession); + Log.StatusLine ($" {context.Characters.Link} {antUrl}", ConsoleColor.White); + await Download (context, antUrl, localPackagePath, "Apache Ant", Path.GetFileName (localPackagePath), downloadStatus); + + if (!File.Exists (localPackagePath)) { + Log.ErrorLine ($"Download of Xamarin.Android Bundle from {antUrl} failed."); + return false; + } + } + + Log.StatusLine ($"Unpacking Ant to {Configurables.Paths.AntInstallDir}"); + string tempDir = $"{Configurables.Paths.AntInstallDir}-ant.temp"; + try { + if (!await Utilities.Unpack (localPackagePath, tempDir, cleanDestinatioBeforeUnpacking: true)) { + Log.ErrorLine ("Failed to unpack Ant"); + return false; + } + + Log.DebugLine ("Moving unpacked Ant from {tempDir} to {Configurables.Paths.AntInstallDir}"); + + // There should be just a single subdirectory + List subdirs = Directory.EnumerateDirectories (tempDir).ToList (); + if (subdirs.Count > 1) + throw new InvalidOperationException ($"Unexpected contents layout of the Ant archive - expected a single subdirectory, instead found {subdirs.Count}"); + + Utilities.MoveDirectoryContentsRecursively (subdirs [0], Configurables.Paths.AntInstallDir); + } finally { + Utilities.DeleteDirectorySilent (tempDir); + } + + return true; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_InstallCorrettoOpenJDK.Linux.cs b/build-tools/xaprepare/xaprepare/Steps/Step_InstallCorrettoOpenJDK.Linux.cs new file mode 100644 index 000000000..1fc5813dc --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Steps/Step_InstallCorrettoOpenJDK.Linux.cs @@ -0,0 +1,14 @@ +using System; + +namespace Xamarin.Android.Prepare +{ + partial class Step_InstallCorrettoOpenJDK + { + string GetArchiveRootDirectoryName () + { + Version v = Configurables.Defaults.CorrettoVersion; + + return $"amazon-corretto-{v.Major}.{v.Minor}.{v.Build:00}.{v.Revision}-linux-x64"; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_InstallCorrettoOpenJDK.MacOS.cs b/build-tools/xaprepare/xaprepare/Steps/Step_InstallCorrettoOpenJDK.MacOS.cs new file mode 100644 index 000000000..733fff5f8 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Steps/Step_InstallCorrettoOpenJDK.MacOS.cs @@ -0,0 +1,13 @@ +using System; +using System.IO; + +namespace Xamarin.Android.Prepare +{ + partial class Step_InstallCorrettoOpenJDK + { + string GetArchiveRootDirectoryName () + { + return Path.Combine ("amazon-corretto-8.jdk", "Contents", "Home"); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_InstallCorrettoOpenJDK.Windows.cs b/build-tools/xaprepare/xaprepare/Steps/Step_InstallCorrettoOpenJDK.Windows.cs new file mode 100644 index 000000000..f54de803e --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Steps/Step_InstallCorrettoOpenJDK.Windows.cs @@ -0,0 +1,17 @@ +using System; +using System.Threading; + +namespace Xamarin.Android.Prepare +{ + partial class Step_InstallCorrettoOpenJDK + { + static readonly TimeSpan BeforeMoveSleepTime = TimeSpan.FromSeconds (30); + + string GetArchiveRootDirectoryName () + { + Version v = Configurables.Defaults.CorrettoVersion; + + return $"jdk1.8.0_{v.Minor}"; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_InstallCorrettoOpenJDK.cs b/build-tools/xaprepare/xaprepare/Steps/Step_InstallCorrettoOpenJDK.cs new file mode 100644 index 000000000..50fd503bb --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Steps/Step_InstallCorrettoOpenJDK.cs @@ -0,0 +1,160 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Reflection; +using System.Threading.Tasks; +using System.Xml.Linq; + +namespace Xamarin.Android.Prepare +{ + partial class Step_InstallCorrettoOpenJDK : StepWithDownloadProgress + { + // Paths relative to JDK installation root, just for a cursory check whether we have a sane JDK instance + // NOTE: file extensions are not necessary here + static readonly List jdkFiles = new List { + Path.Combine ("bin", "java"), + Path.Combine ("bin", "javac"), + Path.Combine ("include", "jni.h"), + }; + + public Step_InstallCorrettoOpenJDK () + : base ("Installing OpenJDK (Amazon Corretto 8)") + {} + + protected override async Task Execute (Context context) + { + string corettoInstallDir = Configurables.Paths.CorrettoInstallDir; + if (CorrettoExistsAndIsValid (corettoInstallDir, out string installedVersion)) { + Log.Status ("Corretto version "); + Log.Status (installedVersion, ConsoleColor.Yellow); + Log.StatusLine (" already installed in: ", corettoInstallDir, tailColor: ConsoleColor.Cyan); + return true; + } + + Log.StatusLine ($"Corretto JDK {Configurables.Defaults.CorrettoVersion} will be installed"); + Uri correttoURL = Configurables.Urls.Corretto; + if (correttoURL == null) + throw new InvalidOperationException ("Corretto URL must not be null"); + + string packageName = Path.GetFileName (correttoURL.LocalPath); + string localPackagePath = Path.Combine (Configurables.Paths.CorrettoCacheDir, packageName); + if (!await DownloadCorretto (context, localPackagePath, correttoURL)) + return false; + + string tempDir = $"{corettoInstallDir}.temp"; + try { + if (!await Utilities.Unpack (localPackagePath, tempDir, cleanDestinatioBeforeUnpacking: true)) { + Log.ErrorLine ("Failed to install Corretto"); + return false; + } + + string rootDirName = GetArchiveRootDirectoryName (); + string rootDir = Path.Combine (tempDir, rootDirName); + Log.DebugLine ($"{context.OS.Type} root directory name of Corretto OpenJDK package is: {rootDirName}"); + if (!Directory.Exists (rootDir)) { + Log.ErrorLine ($"Corretto root directory not found after unpacking: {rootDirName}"); + return false; + } + + Utilities.MoveDirectoryContentsRecursively (rootDir, corettoInstallDir); + } finally { + Utilities.DeleteDirectorySilent (tempDir); + } + + return true; + } + + async Task DownloadCorretto (Context context, string localPackagePath, Uri url) + { + if (File.Exists (localPackagePath)) { + if (await Utilities.VerifyArchive (localPackagePath)) { + Log.StatusLine ("Corretto archive already downloaded and valid"); + return true; + } + Utilities.DeleteFileSilent (localPackagePath); + } + + Log.StatusLine ("Downloading Corretto from ", url.ToString (), tailColor: ConsoleColor.White); + (bool success, ulong size, HttpStatusCode status) = await Utilities.GetDownloadSizeWithStatus (url); + if (!success) { + if (status == HttpStatusCode.NotFound) + Log.ErrorLine ("Corretto archive URL not found"); + else + Log.ErrorLine ($"Failed to obtain Corretto size. HTTP status code: {status} ({(int)status})"); + return false; + } + + DownloadStatus downloadStatus = Utilities.SetupDownloadStatus (context, size, context.InteractiveSession); + Log.StatusLine ($" {context.Characters.Link} {url}", ConsoleColor.White); + await Download (context, url, localPackagePath, "Corretto", Path.GetFileName (localPackagePath), downloadStatus); + + if (!File.Exists (localPackagePath)) { + Log.ErrorLine ($"Download of Corretto from {url} failed."); + return false; + } + + return true; + } + + bool CorrettoExistsAndIsValid (string installDir, out string installedVersion) + { + installedVersion = null; + if (!Directory.Exists (installDir)) { + Log.DebugLine ($"Corretto directory {installDir} does not exist"); + return false; + } + + string corettoVersionFile = Path.Combine (installDir, "version.txt"); + if (!File.Exists (corettoVersionFile)) { + Log.DebugLine ($"Corretto version file {corettoVersionFile} does not exist"); + return false; + } + + string[] lines = File.ReadAllLines (corettoVersionFile); + if (lines == null || lines.Length == 0) { + Log.DebugLine ($"Corretto version file {corettoVersionFile} is empty, cannot determine version"); + return false; + } + + string cv = lines [0].Trim (); + if (String.IsNullOrEmpty (cv)) { + Log.DebugLine ($"Corretto version is empty"); + return false; + } + installedVersion = cv; + + if (!Version.TryParse (cv, out Version cversion)) { + Log.DebugLine ($"Unable to parse Corretto version from: {cv}"); + return false; + } + + if (cversion != Configurables.Defaults.CorrettoVersion) { + Log.DebugLine ($"Invalid Corretto version. Need {Configurables.Defaults.CorrettoVersion}, found {cversion}"); + return false; + } + + foreach (string f in jdkFiles) { + string file = Path.Combine (installDir, f); + if (!File.Exists (file)) { + bool foundExe = false; + foreach (string exe in Utilities.FindExecutable (f)) { + file = Path.Combine (installDir, exe); + if (File.Exists (file)) { + foundExe = true; + break; + } + } + + if (!foundExe) { + Log.DebugLine ($"JDK file {file} missing from Corretto"); + return false; + } + } + } + + return true; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_PrepareBundle.MacOS.cs b/build-tools/xaprepare/xaprepare/Steps/Step_PrepareBundle.MacOS.cs new file mode 100644 index 000000000..218cc481e --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Steps/Step_PrepareBundle.MacOS.cs @@ -0,0 +1,10 @@ +namespace Xamarin.Android.Prepare +{ + partial class Step_PrepareBundle + { + partial void AddBuildLibZipFailureStep () + { + AddFailureStep (new Step_BuildLibZip ()); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_PrepareBundle.Unix.cs b/build-tools/xaprepare/xaprepare/Steps/Step_PrepareBundle.Unix.cs new file mode 100644 index 000000000..34a31f335 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Steps/Step_PrepareBundle.Unix.cs @@ -0,0 +1,25 @@ +namespace Xamarin.Android.Prepare +{ + partial class Step_PrepareBundle + { + const string bundle404Message = null; + + void InitOS () + { + osSupportsMonoBuild = true; + + AddFailureStep (new Step_BuildMonoRuntimes ()); + AddBuildLibZipFailureStep (); + if (Context.Instance.WindowsJitAbisEnabled) + AddFailureStep (new Step_BuildLibZipForWindows ()); + + // We need it here (even though Scenario_Standard runs the step, because if we failed to download the + // bundle, the Step_BuildMonoRuntimes above will clean the destination directory and the Windows GAS + // executables with it. + AddFailureStep (new Step_Get_Windows_GAS ()); + AddFailureStep (new Step_CreateBundle ()); + } + + partial void AddBuildLibZipFailureStep (); + } +} diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_PrepareBundle.Windows.cs b/build-tools/xaprepare/xaprepare/Steps/Step_PrepareBundle.Windows.cs new file mode 100644 index 000000000..249be5d9b --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Steps/Step_PrepareBundle.Windows.cs @@ -0,0 +1,12 @@ +namespace Xamarin.Android.Prepare +{ + partial class Step_PrepareBundle + { + const string bundle404Message = "The Windows build depends on a cached mono bundle, that may not be available yet. Try going back a few commits (with git reset) to download a different bundle."; + + void InitOS () + { + osSupportsMonoBuild = false; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_PrepareBundle.cs b/build-tools/xaprepare/xaprepare/Steps/Step_PrepareBundle.cs new file mode 100644 index 000000000..57c4efb01 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Steps/Step_PrepareBundle.cs @@ -0,0 +1,93 @@ +using System; +using System.IO; +using System.Net; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + partial class Step_PrepareBundle : StepWithDownloadProgress + { + static Uri BundleUriPrefix => Configurables.Urls.Bundle_XABundleDownloadPrefix; + static string BundleFileName => Configurables.Paths.XABundleFileName; + static bool osSupportsMonoBuild; + + public Step_PrepareBundle () + : base ("Preparing the binary bundle") + { + InitOS (); + } + + protected override async Task Execute (Context context) + { + if (context.ForceRuntimesBuild) { + if (osSupportsMonoBuild) { + Log.InfoLine ("Rebuilding Mono runtimes as requested"); + return false; + } + + Log.InfoLine ($"Forced Mono runtimes rebuild requested but rebuilding on {context.OS.Type} is currently not supported."); + } + + string localPackagePath = Path.Combine (Configurables.Paths.BundleArchivePath); + if (await Utilities.VerifyArchive (localPackagePath)) { + Log.StatusLine ("Xamarin.Android Bundle archive already downloaded and valid"); + } else { + if (!String.IsNullOrEmpty (context.XABundlePath)) { + // User indicated they wanted to use a specific bundle that's supposed to be on disk. It's not (or + // it's invalid) and that means we have no way of getting it - we can't download the default one + // since that was not the intention behind overriding the location. Thus, we error out. + throw new InvalidOperationException ($"Xamarin.Android bundle indicated on the command line does not exist ({context.XABundlePath})"); + } + + var bundleUrl = new Uri (BundleUriPrefix, BundleFileName); + + Log.StatusLine ("Bundle URL: ", $"{bundleUrl}", tailColor: ConsoleColor.Cyan); + + HttpStatusCode status; + bool success; + ulong size; + + (success, size, status) = await Utilities.GetDownloadSizeWithStatus (bundleUrl); + if (!success) { + if (status == HttpStatusCode.NotFound) { + if (osSupportsMonoBuild) + Log.StatusLine (" not found, will need to rebuild"); + else + Log.ErrorLine ($" not found, rebuilding on {context.OS.Type} is not currently supported"); + return false; + } + + if (String.IsNullOrEmpty (bundle404Message)) + throw new InvalidOperationException ($"Failed to access bundle at {bundleUrl} (HTTP status: {status})"); + else + throw new InvalidOperationException (bundle404Message); + } + + DownloadStatus downloadStatus = Utilities.SetupDownloadStatus (context, size, context.InteractiveSession); + Log.StatusLine ($" {context.Characters.Link} {bundleUrl}", ConsoleColor.White); + await Download (context, bundleUrl, localPackagePath, "Xamarin.Android Bundle", Path.GetFileName (localPackagePath), downloadStatus); + + if (!File.Exists (localPackagePath)) { + Log.ErrorLine ($"Download of Xamarin.Android Bundle from {bundleUrl} failed."); + return false; + } + } + + Log.StatusLine ($"Unpacking bundle to {Utilities.GetRelativePath (BuildPaths.XamarinAndroidSourceRoot, Configurables.Paths.BundleInstallDir)}"); + string tempDir = $"{Configurables.Paths.BundleInstallDir}-bundle.temp"; + try { + if (!await Utilities.Unpack (localPackagePath, tempDir, cleanDestinatioBeforeUnpacking: true)) { + Log.WarningLine ("Failed to unpack bundle, will need to rebuild"); + return false; + } + + Log.DebugLine ("Moving unpacked bundle from {tempDir} to {Configurables.Paths.Bundle_InstallDir}"); + Utilities.MoveDirectoryContentsRecursively (tempDir, Configurables.Paths.BundleInstallDir, resetFileTimestamp: true); + } finally { + Utilities.DeleteDirectorySilent (tempDir); + } + + return true; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_PrepareExternal.Unix.cs b/build-tools/xaprepare/xaprepare/Steps/Step_PrepareExternal.Unix.cs new file mode 100644 index 000000000..d649cd8ea --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Steps/Step_PrepareExternal.Unix.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + partial class Step_PrepareExternal + { + async Task ExecuteOSSpecific (Context context, NuGetRunner nuget) + { + Log.StatusLine (); + var make = new MakeRunner (context) { + NoParallelJobs = true + }; + + bool result = await make.Run ( + logTag: "xamarin-android-tools", + workingDirectory: Path.Combine (Configurables.Paths.ExternalDir, "xamarin-android-tools"), + arguments: new List { + "prepare", + $"CONFIGURATION={context.Configuration}", + } + ); + if (!result) + return false; + + string javaInteropDir = context.Properties.GetRequiredValue (KnownProperties.JavaInteropFullPath); + Log.StatusLine (); + result = await make.Run ( + logTag: "java-interop-prepare", + workingDirectory: javaInteropDir, + arguments: new List { + "prepare", + $"CONFIGURATION={context.Configuration}", + $"JI_MAX_JDK={Configurables.Defaults.MaxJDKVersion}", + } + ); + if (!result) + return false; + + Log.StatusLine (); + result = await make.Run ( + logTag: "java-interop-props", + workingDirectory: javaInteropDir, + arguments: new List { + $"bin/Build{context.Configuration}/JdkInfo.props", + $"CONFIGURATION={context.Configuration}", + $"JI_MAX_JDK={Configurables.Defaults.MaxJDKVersion}", + } + ); + return result; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_PrepareExternal.Windows.cs b/build-tools/xaprepare/xaprepare/Steps/Step_PrepareExternal.Windows.cs new file mode 100644 index 000000000..e35837a3d --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Steps/Step_PrepareExternal.Windows.cs @@ -0,0 +1,32 @@ +using System.IO; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + partial class Step_PrepareExternal + { + async Task ExecuteOSSpecific (Context context, NuGetRunner nuget) + { + var msbuild = new MSBuildRunner (context); + string javaInteropSolution = Path.Combine (Configurables.Paths.ExternalJavaInteropDir, "Java.Interop.sln"); + bool result = await msbuild.Run ( + projectPath: javaInteropSolution, + logTag: "java.interop-restore", + arguments: new List { + "/t:Restore" + }, + binlogName: "prepare-java.interop-restore" + ); + + if (!result) + return false; + + result = await NuGetRestore (nuget, javaInteropSolution); + if (!result) + return false; + + return await NuGetRestore (nuget, Configurables.Paths.ExternalXamarinAndroidToolsSln); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_PrepareExternal.cs b/build-tools/xaprepare/xaprepare/Steps/Step_PrepareExternal.cs new file mode 100644 index 000000000..9eeb21ec1 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Steps/Step_PrepareExternal.cs @@ -0,0 +1,63 @@ +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + partial class Step_PrepareExternal : Step + { + public Step_PrepareExternal () + : base ("Preparing external components") + {} + + protected override async Task Execute (Context context) + { + var nuget = new NuGetRunner (context); + + if (!await NuGetRestore (nuget, context.XASolutionFile)) { + return false; + } + + Log.StatusLine (); + if (!await NuGetRestore (nuget, context.XATestsSolutionFile)) { + return false; + } + + var msbuild = new MSBuildRunner (context); + string slnPath = Path.Combine (Configurables.Paths.ExternalDir, "LibZipSharp", "libZipSharp.sln"); + bool result = await msbuild.Run ( + projectPath: slnPath, + logTag: "libzipsharp-restore", + arguments: new List { + "/t:Restore" + }, + binlogName: "prepare-libzipsharp-restore" + ); + + slnPath = Path.Combine (Configurables.Paths.ExternalDir, "debugger-libs", "debugger-libs.sln"); + result = await msbuild.Run ( + projectPath: slnPath, + logTag: "debugger-libs-restore", + arguments: new List { + "/t:Restore" + }, + binlogName: "prepare-debugger-libs-restore" + ); + + if (!result) + return false; + + return await ExecuteOSSpecific (context, nuget); + } + + async Task NuGetRestore (NuGetRunner nuget, string solutionFilePath) + { + if (!await nuget.Restore (solutionFilePath)) { + Log.ErrorLine ($"NuGet restore for solution {solutionFilePath} failed"); + return false; + } + + return true; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_PrepareExternalGitDependencies.cs b/build-tools/xaprepare/xaprepare/Steps/Step_PrepareExternalGitDependencies.cs new file mode 100644 index 000000000..3d89e141a --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Steps/Step_PrepareExternalGitDependencies.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + class Step_PrepareExternalGitDependencies : Step + { + public Step_PrepareExternalGitDependencies () + : base ("Preparing external GIT dependencies") + {} + + protected override async Task Execute (Context context) + { + List externalDependencies = ExternalGitDependency.GetDependencies (context, Configurables.Paths.ExternalGitDepsFilePath); + + bool failed = false; + Log.StatusLine (); + Log.StatusLine ("Updating external repositories"); + var git = new GitRunner (context) { + EchoCmdAndArguments = false + }; + foreach (ExternalGitDependency egd in externalDependencies) { + Log.StatusLine ($" {context.Characters.Bullet} {egd.Name}"); + string destDir = Path.Combine (Configurables.Paths.ExternalGitDepsDestDir, egd.Name); + if (!Directory.Exists (destDir)) { + var egdUrl = await GetGitHubURL (egd, git); + Log.StatusLine ($" {context.Characters.Link} cloning from {egd.Owner}/{egd.Name}"); + if (!await git.Clone (egdUrl, destDir)) { + Log.ErrorLine ($"Failed to clone {egd.Name}"); + failed = true; + continue; + } + } + + Log.StatusLine ($" {context.Characters.Link} fetching changes from {egd.Owner}/{egd.Name}"); + if (!await git.Fetch (destDir)) { + Log.ErrorLine ($"Failed to fetch changes for {egd.Name}"); + failed = true; + continue; + } + + Log.StatusLine ($" {context.Characters.Bullet} checking out commit {egd.Commit}"); + if (!await git.CheckoutCommit (destDir, egd.Commit)) { + Log.ErrorLine ($"Failed to checkout commit {egd.Commit} for {egd.Name}"); + failed = true; + continue; + } + + // + // Commented out for now because we only have monodroid in .external and its submodules are updated + // elsewhere and there's no need to duplicate the (time-consuming) work. However, it might be a good + // idea to re-enable this code for the benefit of future .external additions (and, possibly, monodroid + // itself after its integration code is updated to not initialize submodules) + // + + // string gitModules = Path.Combine (destDir, ".gitmodules"); + // if (!Utilities.FileExists (gitModules)) + // continue; + + // Log.StatusLine ($" {context.Characters.Bullet} updating submodules"); + // if (!await git.SubmoduleUpdate (destDir)) { + // Log.ErrorLine ($"Failed to update submodules for {egd.Name}"); + // failed = true; + // } + } + + return !failed; + } + + async Task GetGitHubURL (ExternalGitDependency egd, GitRunner git) + { + string ghToken = Environment.GetEnvironmentVariable("GH_AUTH_SECRET"); + if (!String.IsNullOrEmpty (ghToken)) { + return $"https://{ghToken}@github.com:/{egd.Owner}/{egd.Name}"; + } else { + if (await git.IsRepoUrlHttps (BuildPaths.XamarinAndroidSourceRoot)) { + return $"https://github.com:/{egd.Owner}/{egd.Name}"; + } else { + return $"git@github.com:/{egd.Owner}/{egd.Name}"; + } + } + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_PrepareImageDependencies.MacOS.cs b/build-tools/xaprepare/xaprepare/Steps/Step_PrepareImageDependencies.MacOS.cs new file mode 100644 index 000000000..5c0f7dca6 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Steps/Step_PrepareImageDependencies.MacOS.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; + +namespace Xamarin.Android.Prepare +{ + partial class Step_PrepareImageDependencies + { + partial void GatherMacPackages (List brewTaps, List brewPackages, List pkgUrls) + { + foreach (Program p in Context.Instance.OS.Dependencies) { + var homebrewProgram = p as HomebrewProgram; + if (homebrewProgram == null) + continue; + + if (!String.IsNullOrEmpty (homebrewProgram.HomebrewTapName)) + brewTaps.Add (homebrewProgram.HomebrewTapName); + + if (homebrewProgram.HomebrewFormulaUrl != null) + brewPackages.Add (homebrewProgram.HomebrewFormulaUrl.ToString ()); + else + brewPackages.Add (homebrewProgram.Name); + } + + pkgUrls.Add (Configurables.Urls.MonoPackage.ToString ()); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_PrepareImageDependencies.Unix.cs b/build-tools/xaprepare/xaprepare/Steps/Step_PrepareImageDependencies.Unix.cs new file mode 100644 index 000000000..01cda47b0 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Steps/Step_PrepareImageDependencies.Unix.cs @@ -0,0 +1,33 @@ +using System; +using System.IO; + +using Mono.Unix.Native; + +namespace Xamarin.Android.Prepare +{ + partial class Step_PrepareImageDependencies + { + partial void MakeExecutable (string scriptPath) + { + if (String.IsNullOrEmpty (scriptPath)) + throw new ArgumentException ("must not be null or empty", nameof (scriptPath)); + + if (!File.Exists (scriptPath)) { + Log.WarningLine ($"Script {scriptPath} does not exist"); + return; + } + + int ret = Syscall.chmod (scriptPath, + FilePermissions.S_IRGRP | FilePermissions.S_IXGRP | + FilePermissions.S_IROTH | FilePermissions.S_IXOTH | + FilePermissions.S_IRUSR | FilePermissions.S_IXUSR | FilePermissions.S_IWUSR + ); + + if (ret == 0) + return; + + Log.ErrorLine ($"Failed to make {scriptPath} executable: {Stdlib.strerror (Stdlib.GetLastError ())}"); + throw new InvalidOperationException ("Failed"); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_PrepareImageDependencies.cs b/build-tools/xaprepare/xaprepare/Steps/Step_PrepareImageDependencies.cs new file mode 100644 index 000000000..c07926ab6 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Steps/Step_PrepareImageDependencies.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + partial class Step_PrepareImageDependencies : Step + { + public Step_PrepareImageDependencies () + : base ("Preparing image dependency script") + {} + +#pragma warning disable CS1998 + protected override async Task Execute (Context context) + { + var toolchainDirs = new List { + Path.GetFileName (context.Properties.GetRequiredValue (KnownProperties.AndroidSdkDirectory)), + Path.GetFileName (context.Properties.GetRequiredValue (KnownProperties.AndroidNdkDirectory)), + Path.GetFileName (Configurables.Paths.CorrettoInstallDir), + }; + + var androidToolchain = new AndroidToolchain (); + var androidPackages = new List (); + foreach (AndroidToolchainComponent component in androidToolchain.Components) { + if (component == null) + continue; + + Uri pkgUrl; + if (component.RelativeUrl != null) + pkgUrl = new Uri (AndroidToolchain.AndroidUri, component.RelativeUrl); + else + pkgUrl = AndroidToolchain.AndroidUri; + pkgUrl = new Uri (pkgUrl, $"{component.Name}.zip"); + androidPackages.Add ($"{pkgUrl} {component.DestDir}"); + } + + var brewTaps = new List (); + var brewPackages = new List (); + var pkgUrls = new List (); + + GatherMacPackages (brewTaps, brewPackages, pkgUrls); + + var sb = new StringBuilder (File.ReadAllText (Configurables.Paths.PackageImageDependenciesTemplate)); + sb.Replace ("@TOOLCHAIN_DIRS@", MakeLines (toolchainDirs)); + sb.Replace ("@PACKAGES@", MakeLines (androidPackages)); + sb.Replace ("@BREW_TAPS@", MakeLines (brewTaps)); + sb.Replace ("@BREWS@", MakeLines (brewPackages)); + sb.Replace ("@PKG_URLS@", MakeLines (pkgUrls)); + + string outputFile = Configurables.Paths.PackageImageDependenciesOutput; + Log.StatusLine ($"Generating ", outputFile, tailColor: ConsoleColor.White); + File.WriteAllText (outputFile, sb.ToString ()); + + try { + MakeExecutable (outputFile); + return true; + } catch (InvalidOperationException) { + return false; + } + + string MakeLines (List list) + { + return String.Join ("\n", list); + } + } +#pragma warning restore CS1998 + + partial void GatherMacPackages (List brewTaps, List brewPackages, List pkgUrls); + partial void MakeExecutable (string scriptPath); + } +} diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_PrepareLocal.cs b/build-tools/xaprepare/xaprepare/Steps/Step_PrepareLocal.cs new file mode 100644 index 000000000..4adba773d --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Steps/Step_PrepareLocal.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + class Step_PrepareLocal : Step + { + public Step_PrepareLocal () + : base ("Preparing local components") + {} + + protected override async Task Execute(Context context) + { + var msbuild = new MSBuildRunner (context); + + // This needs to be built *after* we copy Java.Interop props or we'll get the wrong Mono.Cecil assembly. + string remapAsmRefPath = Path.Combine (Configurables.Paths.BuildToolsDir, "remap-assembly-ref", "remap-assembly-ref.csproj"); + bool result = await msbuild.Run ( + projectPath: remapAsmRefPath, + logTag: "remap-assembly-ref", + binlogName: "build-remap-assembly-ref" + ); + + if (!result) { + Log.ErrorLine ("Failed to build remap-assembly-ref"); + return false; + } + + string xfTestPath = Path.Combine (BuildPaths.XamarinAndroidSourceRoot, "tests", "Xamarin.Forms-Performance-Integration", "Xamarin.Forms.Performance.Integration.csproj"); + return await msbuild.Run ( + projectPath: xfTestPath, + logTag: "xfperf", + arguments: new List { + "/t:Restore" + }, + binlogName: "prepare-restore" + ); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_PrepareMSBuild.cs b/build-tools/xaprepare/xaprepare/Steps/Step_PrepareMSBuild.cs new file mode 100644 index 000000000..73b077b3a --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Steps/Step_PrepareMSBuild.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + class Step_PrepareMSBuild : Step + { + public Step_PrepareMSBuild () + : base ("Preparing MSbuild") + {} + +#pragma warning disable CS1998 + protected override async Task Execute (Context context) + { + Log.StatusLine (".:! NOT IMPLEMENTED YET !:. (Possibly not needed?)"); + return true; + } +#pragma warning restore CS1998 + } +} diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_PrepareProps.cs b/build-tools/xaprepare/xaprepare/Steps/Step_PrepareProps.cs new file mode 100644 index 000000000..0ac0ad2dc --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Steps/Step_PrepareProps.cs @@ -0,0 +1,51 @@ +using System; +using System.IO; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + class Step_PrepareProps : Step + { + const ConsoleColor StepColor = ConsoleColor.White; + + public Step_PrepareProps () + : base ("Preparing property files") + {} + +#pragma warning disable CS1998 + protected override async Task Execute (Context context) + { + string monoSourceDir = Configurables.Paths.MonoSourceFullPath; + string javaInteropDir = Configurables.Paths.ExternalJavaInteropDir; + + LogStep (context, "Copying Mono.Cecil files"); + Utilities.CopyFilesSimple ( + Directory.EnumerateFiles (Path.Combine (javaInteropDir, "external"), "Mono.Cecil*"), + Path.Combine (monoSourceDir, "external") + ); + + LogStep (context, "Copying code signing keys"); + Utilities.CopyFileToDir (Path.Combine (javaInteropDir, "product.snk"), monoSourceDir); + Utilities.CopyFileToDir ( + Path.Combine (monoSourceDir, "mcs", "class", "msfinal.pub"), + BuildPaths.XamarinAndroidSourceRoot + ); + + LogStep (context, "Configuring Java.Interop property overrides"); + Utilities.CopyFileToDir ( + Path.Combine (Configurables.Paths.BuildToolsScriptsDir, "Configuration.Java.Interop.Override.props"), + javaInteropDir, + "Configuration.Override.props" + ); + + return true; + } + + void LogStep (Context context, string step) + { + Log.StatusLine ($" {context.Characters.Bullet} {step}", StepColor); + } + +#pragma warning restore CS1998 + } +} diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_ThirdPartyNotices.cs b/build-tools/xaprepare/xaprepare/Steps/Step_ThirdPartyNotices.cs new file mode 100644 index 000000000..274e34ce8 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Steps/Step_ThirdPartyNotices.cs @@ -0,0 +1,203 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + class Step_ThirdPartyNotices : Step + { + static readonly Dictionary tpnBlurbs = new Dictionary { + { ThirdPartyLicenseType.MicrosoftOSS, + @"xamarin-android + +THIRD - PARTY SOFTWARE NOTICES AND INFORMATION +Do Not Translate or Localize + +This project incorporates components from the projects listed below. +The original copyright notices and the licenses under which Microsoft +received such components are set forth below. +Microsoft reserves all rights not expressly granted herein, whether by +implication, estoppel or otherwise."}, + + { ThirdPartyLicenseType.Foundation, + @"xamarin-android uses third-party libraries or other resources that may be +distributed under licenses different than the xamarin-android software. + +Attributions and license notices for test cases originally authored by +third parties can be found in the respective test directories. + +In the event that we accidentally failed to list a required notice, please +bring it to our attention. Post an issue or email us: + + dotnet@microsoft.com + +The attached notices are provided for information only."}, + + { ThirdPartyLicenseType.Commercial, + @"xamarin-android + +THIRD - PARTY SOFTWARE NOTICES AND INFORMATION +Do Not Translate or Localize + +Xamarin-Android incorporates components from the projects listed below. +Microsoft licenses these components to you under Microsoft’s licensing +terms, except that components licensed under open source licenses +requiring that such components remain under their original license are +being made available to you under their original licensing terms. +The original copyright notices and the licenses under which Microsoft +received such components are set forth below for informational purposes. +Microsoft reserves all rights not expressly granted herein, whether by +implication, estoppel or otherwise." } + }; + + public Step_ThirdPartyNotices () + : base ("Preparing Third Party Notices") + {} + +#pragma warning disable CS1998 + protected override async Task Execute (Context context) + { + GenerateThirdPartyNotices (Path.Combine (BuildPaths.XamarinAndroidSourceRoot, "ThirdPartyNotices.txt"), + ThirdPartyLicenseType.Foundation, + includeExternalDeps: false, + includeBuildDeps: false); + Log.StatusLine (); + GenerateThirdPartyNotices (Path.Combine (context.XAInstallPrefix, "ThirdPartyNotices.txt"), + ThirdPartyLicenseType.MicrosoftOSS, + includeExternalDeps: true, + includeBuildDeps: false); + + return true; + } +#pragma warning restore CS1998 + + void GenerateThirdPartyNotices (string outputPath, ThirdPartyLicenseType licenseType, bool includeExternalDeps, bool includeBuildDeps) + { + List types = Utilities.GetTypesWithCustomAttribute (); + if (types.Count == 0) { + Log.StatusLine ("No Third Party Notice entries found", ConsoleColor.Gray); + return; + } + + var licenses = new SortedDictionary (StringComparer.OrdinalIgnoreCase); + foreach (Type type in types) { + EnsureValidTPNType (type); + + if (type.IsSubclassOf (typeof (ThirdPartyNoticeGroup))) { + ProcessTPN (licenses, Activator.CreateInstance (type) as ThirdPartyNoticeGroup, includeExternalDeps, includeBuildDeps); + continue; + } + + if (type.IsSubclassOf (typeof (ThirdPartyNotice))) { + ProcessTPN (licenses, Activator.CreateInstance (type) as ThirdPartyNotice, includeExternalDeps, includeBuildDeps); + continue; + } + + throw new NotSupportedException ($"ThirdPartyNotice type {type.FullName} not supported"); + } + + if (licenses.Count == 0) + return; + + string blurb; + if (!tpnBlurbs.TryGetValue (licenseType, out blurb)) + throw new InvalidOperationException ($"Unknown license type {licenseType}"); + + using (StreamWriter sw = Utilities.OpenStreamWriter (outputPath)) { + Log.StatusLine ($" Generating: {Utilities.GetRelativePath (BuildPaths.XamarinAndroidSourceRoot, outputPath)}", ConsoleColor.Gray); + Log.DebugLine ($"Full path: {outputPath}"); + + sw.WriteLine (blurb); + sw.WriteLine (); + + uint i = 0; + int pad = licenses.Count >= 10 ? 4 : 3; + foreach (var kvp in licenses) { + string name = kvp.Key; + ThirdPartyNotice tpn = kvp.Value; + + sw.Write ($"{++i}.".PadRight (pad)); + sw.WriteLine ($"{name} ({tpn.SourceUrl})"); + } + sw.WriteLine (); + + foreach (string key in licenses.Keys) { + ThirdPartyNotice tpn = licenses [key]; + + string heading = $"%% {tpn.Name} NOTICES AND INFORMATION BEGIN HERE"; + string underline = "=".PadRight (heading.Length, '='); + sw.WriteLine (heading); + sw.WriteLine (underline); + if (tpn.LicenseText != null) + sw.WriteLine (tpn.LicenseText.TrimStart ()); + else + sw.WriteLine (FetchTPNLicense (tpn.LicenseFile)); + sw.WriteLine (); + sw.WriteLine (underline); + sw.WriteLine ($"END OF {tpn.Name} NOTICES AND INFORMATION"); + sw.WriteLine (); + } + + sw.Flush (); + } + } + + string FetchTPNLicense (string relativeFilePath) + { + string path = Path.Combine (BuildPaths.XamarinAndroidSourceRoot, relativeFilePath); + if (!File.Exists (path)) + throw new InvalidOperationException ($"License file {path} does not exist"); + + return File.ReadAllText (path); + } + + void EnsureValidTPNType (Type type) + { + if (!type.IsClass || type.IsAbstract) + throw new InvalidOperationException ($"TPN type must be a non-abstract class ({type})"); + } + + void ProcessTPN (SortedDictionary licenses, ThirdPartyNotice tpn, bool includeExternalDeps, bool includeBuildDeps) + { + if (tpn == null) + throw new ArgumentNullException (nameof (tpn)); + + tpn.EnsureValid (); + if (!tpn.Include (includeExternalDeps, includeBuildDeps)) + return; + + Log.StatusLine ($" {Context.Instance.Characters.Bullet} Processing: ", tpn.Name, ConsoleColor.Gray, ConsoleColor.White); + + if (licenses.ContainsKey (tpn.Name)) { + Log.WarningLine ($"Duplicate Third Party Notice '{tpn.Name}' (old class: {licenses [tpn.Name]}; new class: {tpn})"); + return; + } + + licenses.Add (tpn.Name, tpn); + } + + void ProcessTPN (SortedDictionary licenses, ThirdPartyNoticeGroup tpng, bool includeExternalDeps, bool includeBuildDeps) + { + if (tpng == null) + throw new ArgumentNullException (nameof (tpng)); + + if (tpng.Notices == null || tpng.Notices.All (n => n == null)) + throw new InvalidOperationException ($"TPN group {tpng} without notices"); + + if (!tpng.Include (includeExternalDeps, includeBuildDeps)) + return; + + foreach (ThirdPartyNotice tpn in tpng.Notices) { + if (tpn == null) + throw new InvalidOperationException ($"TPN group {tpng} contains a null notice"); + if (tpn is ThirdPartyNoticeGroup tpg) + ProcessTPN (licenses, tpg, includeExternalDeps, includeBuildDeps); + else + ProcessTPN (licenses, tpn, includeExternalDeps, includeBuildDeps); + } + } + } +} diff --git a/build-tools/xaprepare/xaprepare/ThirdPartyNotices/Java.Interop.cs b/build-tools/xaprepare/xaprepare/ThirdPartyNotices/Java.Interop.cs new file mode 100644 index 000000000..ab99d789f --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ThirdPartyNotices/Java.Interop.cs @@ -0,0 +1,141 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Xamarin.Android.Prepare +{ + [TPN] + class JavaInterop_External_Dependencies_Group : ThirdPartyNoticeGroup + { + protected override bool ShouldInclude (bool includeExternalDeps, bool includeBuildDeps) => includeExternalDeps; + + public override List Notices => new List { + new JavaInterop_xamarin_Java_Interop_TPN (), + new JavaInterop_xamarin_mono_cecil_TPN (), + new JavaInterop_jonpryor_mono_linq_expressions_TPN (), + new JavaInterop_mono_csharp_TPN (), + new JavaInterop_mono_LineEditor_TPN (), + new JavaInterop_mono_Options_TPN (), + new JavaInterop_zzzprojects_HtmlAgilityPack_TPN (), + }; + } + + class JavaInterop_xamarin_Java_Interop_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://github.com/xamarin/Java.Interop/"); + static readonly string licenseFile = Path.Combine (Configurables.Paths.ExternalJavaInteropDir, "LICENSE"); + + public override string LicenseFile => licenseFile; + public override string Name => "xamarin/Java.Interop"; + public override Uri SourceUrl => url; + public override string LicenseText => null; + } + + // git submodules of Java.Interop + class JavaInterop_xamarin_mono_cecil_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://github.com/mono/cecil/"); + static readonly string licenseFile = Path.Combine (Configurables.Paths.ExternalJavaInteropDir, "external", "cecil", "LICENSE.txt"); + + public override string LicenseFile => licenseFile; + public override string Name => "mono/cecil"; + public override Uri SourceUrl => url; + public override string LicenseText => null; + } + + class JavaInterop_jonpryor_mono_linq_expressions_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://github.com/jonpryor/mono.linq.expressions/"); + + public override string LicenseFile => null; + public override string Name => "jonpryor/mono.linq.expressions"; + public override Uri SourceUrl => url; + + public override string LicenseText => @" + Copyright (C) 2012 Jb Evain + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + ""Software""), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +"; + + public override bool Include(bool includeExternalDeps, bool includeBuildDeps) => includeBuildDeps; + } + + // from various packages.config files in Java.Interop + class JavaInterop_mono_csharp_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://github.com/mono/mono/tree/master/mcs/class/Mono.CSharp"); + + public override string LicenseFile => CommonLicenses.MonoMITPath; + public override string Name => "mono/csharp"; + public override Uri SourceUrl => url; + + public override string LicenseText => null; + } + + class JavaInterop_mono_LineEditor_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://github.com/mono/LineEditor/blob/master/LICENSE"); + + public override string LicenseFile => CommonLicenses.MonoMITPath; + public override string Name => "mono/LineEditor"; + public override Uri SourceUrl => url; + public override string LicenseText => null; + } + + class JavaInterop_mono_Options_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://github.com/mono/mono/tree/master/mcs/class/Mono.Options"); + + public override string LicenseFile => CommonLicenses.MonoMITPath; + public override string Name => "mono/Options"; + public override Uri SourceUrl => url; + public override string LicenseText => null; + } + + class JavaInterop_zzzprojects_HtmlAgilityPack_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://github.com/zzzprojects/html-agility-pack"); + + public override string LicenseFile => null; + public override string Name => "zzzprojects/HtmlAgilityPack"; + public override Uri SourceUrl => url; + + public override string LicenseText => @" + The MIT License (MIT) + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the ""Software""), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +"; + } +} diff --git a/build-tools/xaprepare/xaprepare/ThirdPartyNotices/LibZipSharp.cs b/build-tools/xaprepare/xaprepare/ThirdPartyNotices/LibZipSharp.cs new file mode 100644 index 000000000..789e7ed58 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ThirdPartyNotices/LibZipSharp.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Xamarin.Android.Prepare +{ + [TPN] + class LibZipSharp_grendello_LibZipSharp_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://github.com/grendello/LibZipSharp/"); + static readonly string licenseFile = Path.Combine ("external", "LibZipSharp", "LICENSE"); + + public override string LicenseFile => licenseFile; + public override string Name => "grendello/LibZipSharp"; + public override Uri SourceUrl => url; + public override string LicenseText => null; + + public override bool Include (bool includeExternalDeps, bool includeBuildDeps) => includeExternalDeps; + } +} diff --git a/build-tools/xaprepare/xaprepare/ThirdPartyNotices/Xamarin.Android.Build.Tasks.cs b/build-tools/xaprepare/xaprepare/ThirdPartyNotices/Xamarin.Android.Build.Tasks.cs new file mode 100644 index 000000000..90e9d31f6 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ThirdPartyNotices/Xamarin.Android.Build.Tasks.cs @@ -0,0 +1,179 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Xamarin.Android.Prepare +{ + // The contents of the following files are originally from + // https://github.com/bazelbuild/bazel/tree/master/src/tools/android/java/com/google/devtools/build/android/incrementaldeployment + // * Xamarin.Android.Build.Tasks/Resources/IncrementalClassLoader.java + // * Xamarin.Android.Build.Tasks/Resources/Placeholder.java + // * Xamarin.Android.Build.Tasks/Resources/MonkeyPatcher.java + // * Xamarin.Android.Build.Tasks/Resources/ResourceLoader.java + + [TPN] + class XamarinAndroidBuildTasks_bazelbuild_bazel_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://github.com/bazelbuild/bazel/"); + + public override string LicenseText => null; + public override string LicenseFile => CommonLicenses.Apache20Path; + public override string Name => "bazelbuild/bazel"; + public override Uri SourceUrl => url; + } + + [TPN] + class XamarinAndroidBuildTasks_google_desugar_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://android.googlesource.com/platform/external/desugar/+/master/"); + + public override string LicenseText => null; + public override string LicenseFile => CommonLicenses.Apache20Path; + public override string Name => "google/desugar"; + public override Uri SourceUrl => url; + } + + [TPN] + class XamarinAndroidBuildTasks_External_Dependencies_Group : ThirdPartyNoticeGroup + { + protected override bool ShouldInclude (bool includeExternalDeps, bool includeBuildDeps) => includeExternalDeps; + + public override List Notices => new List { + new XamarinAndroidBuildTasks_fsharp_fsharp_TPN (), + new XamarinAndroidBuildTasks_fsprojects_FSharpCompilerCodeDom_TPN (), + new XamarinAndroidBuildTasks_IronyProject_Irony_TPN (), + new XamarinAndroidBuildTasks_JamesNK_NewtonsoftJson_TPN (), + new XamarinAndroidBuildTasks_NuGet_NuGetClient_TPN (), + }; + } + + class XamarinAndroidBuildTasks_fsharp_fsharp_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://github.com/fsharp/fsharp/"); + + public override string LicenseText => null; + public override string LicenseFile => CommonLicenses.Apache20Path; + public override string Name => "fsharp/fsharp"; + public override Uri SourceUrl => url; + } + + class XamarinAndroidBuildTasks_fsprojects_FSharpCompilerCodeDom_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://github.com/fsprojects/FSharp.Compiler.CodeDom/"); + + public override string LicenseFile => null; + public override string Name => "fsprojects/FSharp.Compiler.CodeDom"; + public override Uri SourceUrl => url; + + public override string LicenseText => @" + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. + + In jurisdictions that recognize copyright laws, the author or authors + of this software dedicate any and all copyright interest in the + software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of + relinquishment in perpetuity of all present and future rights to this + software under copyright law. + + THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + For more information, please refer to +"; + } + + class XamarinAndroidBuildTasks_IronyProject_Irony_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://github.com/IronyProject/Irony"); + + public override string LicenseFile => null; + public override string Name => "IronyProject/Irony"; + public override Uri SourceUrl => url; + + public override string LicenseText => @" + Copyright (c) 20012 Roman Ivantsov + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software + and associated documentation files (the ""Software""), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE + AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +"; + } + + class XamarinAndroidBuildTasks_JamesNK_NewtonsoftJson_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://github.com/JamesNK/Newtonsoft.Json"); + + public override string LicenseFile => null; + public override string Name => "JamesNK/Newtonsoft.Json"; + public override Uri SourceUrl => url; + + public override string LicenseText => @" + The MIT License (MIT) + + Copyright (c) 2007 James Newton-King + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + associated documentation files (the ""Software""), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the + following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial + portions of the Software. + + THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +"; + } + + class XamarinAndroidBuildTasks_NuGet_NuGetClient_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://github.com/NuGet/NuGet.Client"); + + public override string LicenseFile => null; + public override string Name => "NuGet/NuGet.Client"; + public override Uri SourceUrl => url; + + public override string LicenseText => @" + Copyright (c) .NET Foundation and Contributors. + + All rights reserved. + + Licensed under the Apache License, Version 2.0 (the ""License""); you may not use + these files except in compliance with the License. You may obtain a copy of the + License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software distributed + under the License is distributed on an ""AS IS"" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. See the License for the + specific language governing permissions and limitations under the License. +"; + } +} diff --git a/build-tools/xaprepare/xaprepare/ThirdPartyNotices/Xamarin.Android.NunitLite.cs b/build-tools/xaprepare/xaprepare/ThirdPartyNotices/Xamarin.Android.NunitLite.cs new file mode 100644 index 000000000..f75b31b29 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ThirdPartyNotices/Xamarin.Android.NunitLite.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Xamarin.Android.Prepare +{ + // The contents of the Xamarin.Android.NUnitLite/NUnitLite directory are + // from https://github.com/nunit/nunitlite/tree/master/src/framework + + [TPN] + class XamarinAndroidNunitLite_nunit_nunitlite_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://github.com/nunit/nunitlite/"); + + public override string LicenseFile => null; + public override string Name => "nunit/nunitlite"; + public override Uri SourceUrl => url; + + public override string LicenseText => @" +Copyright (c) 2004-2013 Charlie Poole + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the ""Software""), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +"; + } +} diff --git a/build-tools/xaprepare/xaprepare/ThirdPartyNotices/Xamarin.Android.Tools.Aidl.cs b/build-tools/xaprepare/xaprepare/ThirdPartyNotices/Xamarin.Android.Tools.Aidl.cs new file mode 100644 index 000000000..8111d3adc --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ThirdPartyNotices/Xamarin.Android.Tools.Aidl.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Xamarin.Android.Prepare +{ + // External dependencies from `Xamarin.Android.Tools.Aidl/packages.config` + + [TPN] + class XamarinAndroidToolsAidl_IronyProject_Irony_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://github.com/IronyProject/Irony"); + + public override string LicenseFile => null; + public override string Name => "IronyProject/Irony"; + public override Uri SourceUrl => url; + + public override string LicenseText => @" + Copyright (c) 20012 Roman Ivantsov + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software + and associated documentation files (the ""Software""), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE + AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +"; + + public override bool Include (bool includeExternalDeps, bool includeBuildDeps) => includeExternalDeps; + } +} diff --git a/build-tools/xaprepare/xaprepare/ThirdPartyNotices/Xamarin.Android.Tools.JavadocImporter.cs b/build-tools/xaprepare/xaprepare/ThirdPartyNotices/Xamarin.Android.Tools.JavadocImporter.cs new file mode 100644 index 000000000..11f66e1ef --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ThirdPartyNotices/Xamarin.Android.Tools.JavadocImporter.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Xamarin.Android.Prepare +{ + // External dependencies from `Xamarin.Android.Tools.JavadocImporter/packages.config` + + [TPN] + class XamarinAndroidToolsJavadocImporter_lovettchris_SgmlReader_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://github.com/lovettchris/SgmlReader/"); + + public override string LicenseFile => CommonLicenses.Apache20Path; + public override string Name => "lovettchris/SgmlReader"; + public override Uri SourceUrl => url; + public override string LicenseText => null; + + public override bool Include(bool includeExternalDeps, bool includeBuildDeps) => includeExternalDeps; + } +} diff --git a/build-tools/xaprepare/xaprepare/ThirdPartyNotices/aapt2.cs b/build-tools/xaprepare/xaprepare/ThirdPartyNotices/aapt2.cs new file mode 100644 index 000000000..e82f1302e --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ThirdPartyNotices/aapt2.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Xamarin.Android.Prepare +{ + [TPN] + class aapt2_google_aapt2_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://mvnrepository.com/artifact/com.android.tools.build/aapt2"); + + public override string LicenseFile => CommonLicenses.Apache20Path; + public override string Name => "google/aapt2"; + public override Uri SourceUrl => url; + public override string LicenseText => null; + + public override bool Include (bool includeExternalDeps, bool includeBuildDeps) => includeExternalDeps; + } +} diff --git a/build-tools/xaprepare/xaprepare/ThirdPartyNotices/bundletool.cs b/build-tools/xaprepare/xaprepare/ThirdPartyNotices/bundletool.cs new file mode 100644 index 000000000..0a7e5d225 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ThirdPartyNotices/bundletool.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Xamarin.Android.Prepare +{ + [TPN] + class bundletool_google_bundletool_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://github.com/google/bundletool"); + + public override string LicenseFile => CommonLicenses.Apache20Path; + public override string Name => "google/bundletool"; + public override Uri SourceUrl => url; + public override string LicenseText => null; + + public override bool Include (bool includeExternalDeps, bool includeBuildDeps) => includeExternalDeps; + } +} diff --git a/build-tools/xaprepare/xaprepare/ThirdPartyNotices/libzip.cs b/build-tools/xaprepare/xaprepare/ThirdPartyNotices/libzip.cs new file mode 100644 index 000000000..ea048909b --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ThirdPartyNotices/libzip.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Xamarin.Android.Prepare +{ + [TPN] + class libzip_nih_at_libzip_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://github.com/nih-at/libzip/"); + static readonly string licenseFile = Path.Combine ("external", "libzip", "LICENSE"); + + public override string LicenseFile => licenseFile; + public override string Name => "nih-at/libzip"; + public override Uri SourceUrl => url; + public override string LicenseText => null; + + public override bool Include (bool includeExternalDeps, bool includeBuildDeps) => includeExternalDeps; + } +} diff --git a/build-tools/xaprepare/xaprepare/ThirdPartyNotices/mono.cs b/build-tools/xaprepare/xaprepare/ThirdPartyNotices/mono.cs new file mode 100644 index 000000000..52c82fe78 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ThirdPartyNotices/mono.cs @@ -0,0 +1,265 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Xamarin.Android.Prepare +{ + [TPN] + class mono_External_Dependencies_Group : ThirdPartyNoticeGroup + { + protected override bool ShouldInclude (bool includeExternalDeps, bool includeBuildDeps) => includeExternalDeps; + + public override List Notices => new List { + new mono_mono_mono_TPN (), + new mono_mono_boringssl_TPN (), + new mono_mono_ikdasm_TPN (), + new mono_mono_ikvm_fork_TPN (), + new mono_mono_linker_TPN (), + new mono_mono_NuGet_BuildTasks_TPN (), + new mono_mono_NUnitLite_TPN (), + new mono_mono_rx_net_TPN (), + new mono_mono_Ix_net_TPN (), + new mono_llvm_Group (), + }; + } + + class mono_mono_mono_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://github.com/mono/"); + static readonly string licenseFile = Path.Combine (Configurables.Paths.MonoSourceFullPath, "LICENSE"); + + public override string LicenseFile => licenseFile; + public override string Name => "mono/mono"; + public override Uri SourceUrl => url; + public override string LicenseText => null; + } + + class mono_mono_aspnetwebstack_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://github.com/mono/aspnetwebstack/"); + static readonly string licenseFile = Path.Combine (Configurables.Paths.MonoSourceFullPath, "aspnetwebstack", "License.txt"); + + public override string LicenseFile => licenseFile; + public override string Name => "mono/aspnetwebstack"; + public override Uri SourceUrl => url; + public override string LicenseText => null; + + public override bool Include (bool includeExternalDeps, bool includeBuildDeps) => includeBuildDeps; + } + + class mono_mono_boringssl_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://github.com/mono/boringssl"); + static readonly string licenseFile = Path.Combine (Configurables.Paths.MonoExternalFullPath, "boringssl", "LICENSE"); + + public override string LicenseFile => licenseFile; + public override string Name => "mono/boringssl"; + public override Uri SourceUrl => url; + public override string LicenseText => null; + + public override bool Include (bool includeExternalDeps, bool includeBuildDeps) => includeBuildDeps; + } + + class mono_mono_ikdasm_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://github.com/mono/ikdasm"); + + public override string LicenseFile => null; + public override string Name => "mono/ikdasm"; + public override Uri SourceUrl => url; + public override string LicenseText => @" +Copyright (C) 2012 Jeroen Frijters + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + +Jeroen Frijters +jeroen@frijters.net +"; + + public override bool Include (bool includeExternalDeps, bool includeBuildDeps) => includeBuildDeps; + } + + class mono_mono_ikvm_fork_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://github.com/mono/ikvm-fork/"); + static readonly string licenseFile = Path.Combine (Configurables.Paths.MonoExternalFullPath, "ikvm", "LICENSE"); + + public override string LicenseFile => licenseFile; + public override string Name => "mono/ikvm-fork"; + public override Uri SourceUrl => url; + public override string LicenseText => null; + + public override bool Include (bool includeExternalDeps, bool includeBuildDeps) => includeBuildDeps; + } + + class mono_mono_linker_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://github.com/mono/linker/"); + static readonly string licenseFile = Path.Combine (Configurables.Paths.MonoExternalFullPath, "linker", "LICENSE"); + + public override string LicenseFile => licenseFile; + public override string Name => "mono/linker"; + public override Uri SourceUrl => url; + public override string LicenseText => null; + } + + class mono_mono_NuGet_BuildTasks_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://github.com/mono/NuGet.BuildTasks/"); + static readonly string licenseFile = Path.Combine (Configurables.Paths.MonoExternalFullPath, "nuget-buildtasks", "LICENSE.txt"); + + public override string LicenseFile => licenseFile; + public override string Name => "mono/NuGet.BuildTasks"; + public override Uri SourceUrl => url; + public override string LicenseText => null; + + public override bool Include (bool includeExternalDeps, bool includeBuildDeps) => includeBuildDeps; + } + + class mono_mono_NUnitLite_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://github.com/mono/NUnitLite/"); + static readonly string licenseFile = Path.Combine (Configurables.Paths.MonoExternalFullPath, "nunit-lite", "NUnitLite-1.0.0", "LICENSE.txt"); + + public override string LicenseFile => licenseFile; + public override string Name => "mono/NUnitLite"; + public override Uri SourceUrl => url; + public override string LicenseText => null; + + public override bool Include (bool includeExternalDeps, bool includeBuildDeps) => includeBuildDeps; + } + + class mono_mono_rx_net_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://github.com/mono/rx/"); + static readonly string licenseFile = Path.Combine (Configurables.Paths.MonoExternalFullPath, "rx", "Rx", "NET", "Source", "license.txt"); + + public override string LicenseFile => licenseFile; + public override string Name => "mono/rx.net"; + public override Uri SourceUrl => url; + public override string LicenseText => null; + + public override bool Include (bool includeExternalDeps, bool includeBuildDeps) => includeBuildDeps; + } + + class mono_mono_Ix_net_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://github.com/mono/rx/"); + static readonly string licenseFile = Path.Combine (Configurables.Paths.MonoExternalFullPath, "rx", "Ix", "NET", "license.txt"); + + public override string LicenseFile => licenseFile; + public override string Name => "mono/Ix.net"; + public override Uri SourceUrl => url; + public override string LicenseText => null; + + public override bool Include (bool includeExternalDeps, bool includeBuildDeps) => includeBuildDeps; + } + + class mono_llvm_Group : ThirdPartyNoticeGroup + { + protected override bool ShouldInclude (bool includeExternalDeps, bool includeBuildDeps) => includeExternalDeps && (includeBuildDeps || Context.Instance.TargetAotAbisEnabled); + + public override List Notices => new List { + new mono_llvm_llvm_TPN (), + new mono_llvm_google_test_TPN (), + new mono_llvm_openbsd_regex_TPN (), + new mono_llvm_pyyaml_tests_TPN (), + new mono_llvm_arm_contributions_TPN (), + new mono_llvm_md5_contributions_TPN (), + }; + } + + class mono_llvm_llvm_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://github.com/mono/llvm/"); + static readonly string licenseFile = Path.Combine (Configurables.Paths.MonoExternalFullPath, "llvm", "LICENSE.TXT"); + + public override string LicenseFile => licenseFile; + public override string Name => "mono/llvm"; + public override Uri SourceUrl => url; + public override string LicenseText => null; + } + + class mono_llvm_google_test_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://github.com/mono/llvm/tree/master/utils/unittest/googletest/"); + static readonly string licenseFile = Path.Combine (Configurables.Paths.MonoExternalFullPath, "llvm", "utils", "unittest", "googletest", "LICENSE.TXT"); + + public override string LicenseFile => licenseFile; + public override string Name => "mono/llvm Google Test"; + public override Uri SourceUrl => url; + public override string LicenseText => null; + } + + class mono_llvm_openbsd_regex_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://github.com/mono/llvm/tree/master/lib/Support/"); + static readonly string licenseFile = Path.Combine (Configurables.Paths.MonoExternalFullPath, "llvm", "lib", "Support", "COPYRIGHT.regex"); + + public override string LicenseFile => licenseFile; + public override string Name => "mono/llvm OpenBSD Regex"; + public override Uri SourceUrl => url; + public override string LicenseText => null; + } + + class mono_llvm_pyyaml_tests_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://github.com/mono/llvm/tree/master/test/YAMLParser/"); + static readonly string licenseFile = Path.Combine (Configurables.Paths.MonoExternalFullPath, "llvm", "test", "YAMLParser", "LICENSE.txt"); + + public override string LicenseFile => licenseFile; + public override string Name => "mono/llvm pyyaml tests"; + public override Uri SourceUrl => url; + public override string LicenseText => null; + } + + class mono_llvm_arm_contributions_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://github.com/mono/llvm/tree/master/lib/Target/ARM/"); + static readonly string licenseFile = Path.Combine (Configurables.Paths.MonoExternalFullPath, "llvm", "lib", "Target", "ARM", "LICENSE.TXT"); + + public override string LicenseFile => licenseFile; + public override string Name => "mono/llvm ARM contributions"; + public override Uri SourceUrl => url; + public override string LicenseText => null; + } + + class mono_llvm_md5_contributions_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://github.com/mono/llvm/blob/master/lib/Support/MD5.cpp"); + + public override string LicenseFile => null; + public override string Name => "mono/llvm md5 contributions"; + public override Uri SourceUrl => url; + + public override string LicenseText => @" + This software was written by Alexander Peslyak in 2001. No copyright is + claimed, and the software is hereby placed in the public domain. + In case this attempt to disclaim copyright and place the software in the + public domain is deemed null and void, then the software is + Copyright (c) 2001 Alexander Peslyak and it is hereby released to the + general public under the following terms: + + Redistribution and use in source and binary forms, with or without + modification, are permitted. + + There's ABSOLUTELY NO WARRANTY, express or implied. + + (This is a heavily cut-down ""BSD license"".) +"; + } +} diff --git a/build-tools/xaprepare/xaprepare/ThirdPartyNotices/monodroid.cs b/build-tools/xaprepare/xaprepare/ThirdPartyNotices/monodroid.cs new file mode 100644 index 000000000..776cbd17b --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ThirdPartyNotices/monodroid.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Xamarin.Android.Prepare +{ + // monodroid/jni/zip + [TPN] + class XamarinAndroidToolsAidl_zLibDll_minizip_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("http://www.winimage.com/zLibDll/minizip.html"); + + public override string LicenseFile => null; + public override string Name => "zLibDll/minizip"; + public override Uri SourceUrl => url; + + public override string LicenseText => @" +Copyright (C) 1998-2005 Gilles Vollant + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +"; + } +} diff --git a/build-tools/xaprepare/xaprepare/ThirdPartyNotices/opentk.cs b/build-tools/xaprepare/xaprepare/ThirdPartyNotices/opentk.cs new file mode 100644 index 000000000..5620d0da4 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ThirdPartyNotices/opentk.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Xamarin.Android.Prepare +{ + [TPN] + class opentk_mono_opentk_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://github.com/mono/opentk/"); + static readonly string licenseFile = Path.Combine ("external", "opentk", "Documentation", "License.txt"); + + public override string LicenseFile => licenseFile; + public override string Name => "mono/opentk"; + public override Uri SourceUrl => url; + public override string LicenseText => null; + + public override bool Include (bool includeExternalDeps, bool includeBuildDeps) => includeExternalDeps; + } +} diff --git a/build-tools/xaprepare/xaprepare/ThirdPartyNotices/proguard.cs b/build-tools/xaprepare/xaprepare/ThirdPartyNotices/proguard.cs new file mode 100644 index 000000000..9df5a6ab3 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ThirdPartyNotices/proguard.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Xamarin.Android.Prepare +{ + [TPN] + class proguard_xamarin_proguard_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://github.com/xamarin/proguard/"); + + public override string LicenseFile => CommonLicenses.GPLv2Path; + public override string Name => "xamarin/proguard"; + public override Uri SourceUrl => url; + public override string LicenseText => null; + + public override bool Include (bool includeExternalDeps, bool includeBuildDeps) => includeExternalDeps; + } +} diff --git a/build-tools/xaprepare/xaprepare/ThirdPartyNotices/r8.cs b/build-tools/xaprepare/xaprepare/ThirdPartyNotices/r8.cs new file mode 100644 index 000000000..cc8ddd9bd --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ThirdPartyNotices/r8.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Xamarin.Android.Prepare +{ + // monodroid/jni/zip + [TPN] + class XamarinAndroidToolsAidl_google_r8_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://r8.googlesource.com/r8/"); + + public override string LicenseFile => null; + public override string Name => "google/r8"; + public override Uri SourceUrl => url; + + public override string LicenseText => @" + https://opensource.org/licenses/BSD-3-Clause + + SPDX short identifier: BSD-3-Clause + + Note: This license has also been called the ""New BSD License"" or + ""Modified BSD License"". See also the 2-clause BSD License. + + Copyright (c) 2016, the R8 project authors. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ""AS IS"" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +"; + + public override bool Include (bool includeExternalDeps, bool includeBuildDeps) => includeExternalDeps; + } +} diff --git a/build-tools/xaprepare/xaprepare/ThirdPartyNotices/sqlite.cs b/build-tools/xaprepare/xaprepare/ThirdPartyNotices/sqlite.cs new file mode 100644 index 000000000..28216ead1 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ThirdPartyNotices/sqlite.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Xamarin.Android.Prepare +{ + [TPN] + class sqlite_xamarin_sqlite_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://github.com/xamarin/sqlite/"); + + public override string LicenseFile => CommonLicenses.Apache20Path; + public override string Name => "xamarin/sqlite"; + public override Uri SourceUrl => url; + public override string LicenseText => null; + + public override bool Include (bool includeExternalDeps, bool includeBuildDeps) => includeExternalDeps; + } + + [TPN] + class sqlite_sqlite_sqlite_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://github.com/xamarin/sqlite/tree/master/dist/"); + static readonly string licenseFile = Path.Combine ("external", "sqlite", "dist", "NOTICE"); + + public override string LicenseFile => licenseFile; + public override string Name => "sqlite/sqlite"; + public override Uri SourceUrl => url; + public override string LicenseText => null; + + public override bool Include (bool includeExternalDeps, bool includeBuildDeps) => includeExternalDeps; + } +} diff --git a/build-tools/xaprepare/xaprepare/ToolRunners/BrewRunner.MacOS.cs b/build-tools/xaprepare/xaprepare/ToolRunners/BrewRunner.MacOS.cs new file mode 100644 index 000000000..cda555700 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ToolRunners/BrewRunner.MacOS.cs @@ -0,0 +1,133 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + partial class BrewRunner : ToolRunner + { + static readonly Version sudoVersion = new Version (1, 1); + static readonly char[] lineSplit = new [] { '\n' }; + + bool? needSudo; + + protected override string DefaultToolExecutableName => GetDefaultExecutableName (); + protected override string ToolName => "Homebrew"; + + string BrewPath => Context.Instance?.Tools?.BrewPath ?? "brew"; + + public BrewRunner (Context context, Log log = null, string toolPath = null) + : base (context, log, toolPath) + { + ProcessTimeout = TimeSpan.FromMinutes (30); + } + + string GetDefaultExecutableName () + { + if (!needSudo.HasValue) { + // MUST use Context.Instance as the `Context` property might be null here + var os = Context.Instance.OS as MacOS; + if (os == null) + throw new InvalidOperationException ($"BrewRunner does not suppport {Context.Instance.OS.Name}"); + + needSudo = os.HomebrewVersion != null && os.HomebrewVersion < sudoVersion; + } + + return needSudo.Value ? "sudo" : BrewPath; + } + + public async Task Tap (string tapName, bool echoOutput = true, bool echoError = true) + { + if (String.IsNullOrEmpty (tapName)) + throw new ArgumentException ("must not be null or empty", nameof (tapName)); + + return await RunBrew (echoOutput, echoError, "tap", tapName); + } + + public async Task Install (string packageName, bool echoOutput = true, bool echoError = true) + { + if (String.IsNullOrEmpty (packageName)) + throw new ArgumentException ("must not be null or empty", nameof (packageName)); + + return await RunBrew (echoOutput, echoError, "install", packageName); + } + + public async Task Upgrade (string packageName, bool echoOutput = true, bool echoError = true) + { + if (String.IsNullOrEmpty (packageName)) + throw new ArgumentException ("must not be null or empty", nameof (packageName)); + + return await RunBrew (echoOutput, echoError, "upgrade", packageName); + } + + public async Task Pin (string packageName, bool echoOutput = false, bool echoError = false) + { + if (String.IsNullOrEmpty (packageName)) + throw new ArgumentException ("must not be null or empty", nameof (packageName)); + + return await RunBrew (echoOutput, echoError, "pin", packageName); + } + + public async Task UnPin (string packageName, bool echoOutput = false, bool echoError = false) + { + if (String.IsNullOrEmpty (packageName)) + throw new ArgumentException ("must not be null or empty", nameof (packageName)); + + return await RunBrew (echoOutput, echoError, "unpin", packageName);; + } + + public bool List (string packageName, out List lines) + { + if (String.IsNullOrEmpty (packageName)) + throw new ArgumentException ("must not be null or empty", nameof (packageName)); + + lines = null; + string listing = CaptureBrewOutput ("ls", packageName)?.Trim (); + if (String.IsNullOrEmpty (listing)) + return false; + + lines = new List (listing.Split (lineSplit, StringSplitOptions.RemoveEmptyEntries)); + return true; + } + + string CaptureBrewOutput (params string[] parameters) + { + return Utilities.GetStringFromStdout (GetBrewRunner (false, true, parameters)); + } + + async Task RunBrew (bool echoOutput, bool echoError, params string[] parameters) + { + ProcessRunner runner = GetBrewRunner (echoOutput, echoError, parameters); + bool success = await RunTool (() => runner.Run ()); + if (!success) { + var os = Context.Instance.OS as MacOS; + os.HomebrewErrors = true; + } + + return success; + } + + ProcessRunner GetBrewRunner (bool echoOutput, bool echoError, params string[] parameters) + { + ProcessRunner runner = CreateProcessRunner (); + + if (needSudo.HasValue && needSudo.Value) { + runner.AddArgument (BrewPath); + } + + AddArguments (runner, parameters); + + if (!echoOutput) { + runner.EchoStandardOutputLevel = ProcessStandardStreamWrapper.LogLevel.Debug; + } + + if (!echoError) { + runner.EchoStandardErrorLevel = ProcessStandardStreamWrapper.LogLevel.Debug; + } + + return runner; + } + + } +} diff --git a/build-tools/xaprepare/xaprepare/ToolRunners/CMakeRunner.OutputSink.cs b/build-tools/xaprepare/xaprepare/ToolRunners/CMakeRunner.OutputSink.cs new file mode 100644 index 000000000..51170589c --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ToolRunners/CMakeRunner.OutputSink.cs @@ -0,0 +1,19 @@ +using System; + +namespace Xamarin.Android.Prepare +{ + partial class CMakeRunner + { + class OutputSink : ToolRunner.ToolOutputSink + { + public OutputSink (Log log, string logFilePath) + : base (log, logFilePath) + {} + + public override void WriteLine (string value) + { + base.WriteLine (value); + } + } + } +} diff --git a/build-tools/xaprepare/xaprepare/ToolRunners/CMakeRunner.cs b/build-tools/xaprepare/xaprepare/ToolRunners/CMakeRunner.cs new file mode 100644 index 000000000..6e1e5ad61 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ToolRunners/CMakeRunner.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + partial class CMakeRunner : ToolRunner + { + protected override string ToolName => "CMake"; + protected override string DefaultToolExecutableName => "cmake"; + + public CMakeRunner (Context context, Log log = null, string toolPath = null) + : base (context, log, toolPath) + { + ProcessTimeout = TimeSpan.FromMinutes (60); + } + + public async Task Run (string logTag, string sourceDirectory, string workingDirectory, List arguments) + { + if (String.IsNullOrEmpty (logTag)) + throw new ArgumentException ("must not be null or empty", nameof (logTag)); + + if (String.IsNullOrEmpty (sourceDirectory)) + throw new ArgumentException ("must not be null or empty", nameof (sourceDirectory)); + + if (String.IsNullOrEmpty (workingDirectory)) + throw new ArgumentException ("must not be null or empty", nameof (workingDirectory)); + + var runner = CreateProcessRunner (); + AddArguments (runner, arguments); + runner.AddQuotedArgument (Utilities.GetRelativePath (workingDirectory,sourceDirectory)); + + try { + return await RunTool (() => { + using (var outputSink = (OutputSink)SetupOutputSink (runner, $"cmake.{logTag}")) { + runner.WorkingDirectory = workingDirectory; + StartTwiddler (); + return runner.Run (); + } + } + ); + } finally { + StopTwiddler (); + } + } + + protected override TextWriter CreateLogSink (string logFilePath) + { + return new OutputSink (Log, logFilePath); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/ToolRunners/GitRunner.BlamePorcelainEntry.cs b/build-tools/xaprepare/xaprepare/ToolRunners/GitRunner.BlamePorcelainEntry.cs new file mode 100644 index 000000000..01d06a5a6 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ToolRunners/GitRunner.BlamePorcelainEntry.cs @@ -0,0 +1,157 @@ +using System; +using System.Collections.Generic; + +namespace Xamarin.Android.Prepare +{ + partial class GitRunner + { + sealed class BlameParserState + { + public BlamePorcelainEntry CurrentEntry; + public List Entries; + } + + // Encompasses the `git blame -p` output as documented in https://www.git-scm.com/docs/git-blame#_the_porcelain_format + public class BlamePorcelainEntry + { + static readonly char[] FieldSeparator = new [] { ' ' }; + + // Standard/known headers + public string Author { get; private set; } + public string AuthorMail { get; private set; } + public uint AuthorTime { get; private set; } + public string AuthorTZ { get; private set; } + + public string Committer { get; private set; } + public string CommitterMail { get; private set; } + public uint CommitterTime { get; private set; } + public string CommitterTZ { get; private set; } + + public string Summary { get; private set; } + public string PreviousCommit { get; private set; } + public string PreviousCommitFile { get; private set; } + public string Filename { get; private set; } + + // Unknown headers + public IDictionary OtherHeaders { get; private set; } + + public string Commit { get; private set; } + public int OriginalFileLine { get; private set; } + public int FinalFileLine { get; private set; } + public int NumberOfLinesInGroup { get; private set; } + + // Contents of the changed line + public string Line { get; private set; } + + public bool ProcessLine (string line) + { + if (String.IsNullOrEmpty (line)) + return false; + + if (String.IsNullOrEmpty (Commit)) { + ParseCommit (line); + return false; + } + + if (line [0] == '\t') { + Line = line.Substring (1); + // Means we've parsed the last line in the record and our job is done + return true; + } + + StoreHeader (line); + return false; + } + + void StoreHeader (string line) + { + string[] parts = line.Split (FieldSeparator, 2, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length != 2) + throw new InvalidOperationException ($"Unexpected commit header format (wrong number of fields): {line}"); + + if (IsHeaderName ("author")) { + Author = parts [1]; + return; + } + + if (IsHeaderName ("author-mail")) { + AuthorMail = parts [1]; + return; + } + + if (IsHeaderName ("author-time")) { + AuthorTime = UInt32.Parse (parts [1]); + return; + } + + if (IsHeaderName ("author-tz")) { + AuthorTZ = parts [1]; + return; + } + + if (IsHeaderName ("committer")) { + Committer = parts [1]; + return; + } + + if (IsHeaderName ("committer-mail")) { + CommitterMail = parts [1]; + return; + } + + if (IsHeaderName ("committer-time")) { + CommitterTime = UInt32.Parse (parts [1]); + return; + } + + if (IsHeaderName ("committer-tz")) { + CommitterTZ = parts [1]; + return; + } + + if (IsHeaderName ("summary")) { + Summary = parts [1]; + return; + } + + if (IsHeaderName ("previous")) { + string[] previous_parts = parts [1].Split (FieldSeparator, 2, StringSplitOptions.RemoveEmptyEntries); + if (previous_parts.Length != 2) + throw new InvalidOperationException ($"Unexpected previous commmit header format (not enough fields): {parts[1]}"); + PreviousCommit = previous_parts [0]; + PreviousCommitFile = previous_parts [1]; + return; + } + + if (IsHeaderName ("filename")) { + Filename = parts [1]; + return; + } + + if (OtherHeaders == null) + OtherHeaders = new SortedDictionary (StringComparer.Ordinal); + OtherHeaders [parts [0]] = parts [1]; + + bool IsHeaderName (string name) + { + return String.Compare (name, parts [0], StringComparison.Ordinal) == 0; + } + } + + void ParseCommit (string line) + { + string[] parts = line.Split (FieldSeparator, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length < 3) + throw new InvalidOperationException ($"Unexpected commit line format (not enough fields): {line}"); + + Commit = parts [0]; + OriginalFileLine = Int32.Parse (parts [1]); + FinalFileLine = Int32.Parse (parts [2]); + if (parts.Length > 3) + NumberOfLinesInGroup = Int32.Parse (parts [3]); + else + NumberOfLinesInGroup = 0; + } + } + } +} diff --git a/build-tools/xaprepare/xaprepare/ToolRunners/GitRunner.OutputSink.cs b/build-tools/xaprepare/xaprepare/ToolRunners/GitRunner.OutputSink.cs new file mode 100644 index 000000000..1bd12ba5b --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ToolRunners/GitRunner.OutputSink.cs @@ -0,0 +1,23 @@ +using System; + +namespace Xamarin.Android.Prepare +{ + partial class GitRunner + { + class OutputSink : ToolRunner.ToolOutputSink + { + public Action LineCallback { get; set; } + + public OutputSink (Log log, string logFilePath = null) + : base (log, logFilePath) + { + } + + public override void WriteLine (string value) + { + base.WriteLine (value); + LineCallback?.Invoke (value); + } + } + } +} diff --git a/build-tools/xaprepare/xaprepare/ToolRunners/GitRunner.cs b/build-tools/xaprepare/xaprepare/ToolRunners/GitRunner.cs new file mode 100644 index 000000000..678931417 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ToolRunners/GitRunner.cs @@ -0,0 +1,265 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + partial class GitRunner : ToolRunner + { + // These are passed to `git` itself *before* the command + static readonly List standardGlobalOptions = new List { + "--no-pager" + }; + + protected override string DefaultToolExecutableName => "git"; + protected override string ToolName => "Git"; + + public GitRunner (Context context, Log log = null, string gitPath = null) + : base (context, log, gitPath ?? context?.Tools?.GitPath) + { + ProcessTimeout = TimeSpan.FromMinutes (30); + } + + public async Task CheckoutCommit (string repositoryPath, string commitRef, bool force = true) + { + if (String.IsNullOrEmpty (repositoryPath)) + throw new ArgumentException ("must not be null or empty", nameof (repositoryPath)); + if (String.IsNullOrEmpty (commitRef)) + throw new ArgumentException ("must not be null or empty", nameof (commitRef)); + + var runner = CreateGitRunner (repositoryPath); + runner.AddArgument ("checkout"); + if (force) + runner.AddArgument ("--force"); + runner.AddArgument ("--progress"); + runner.AddQuotedArgument (commitRef); + + return await RunGit (runner, $"checkout-{Path.GetFileName (repositoryPath)}-{commitRef}"); + } + + public async Task Fetch (string repositoryPath) + { + if (String.IsNullOrEmpty (repositoryPath)) + throw new ArgumentException ("must not be null or empty", nameof (repositoryPath)); + + if (!Directory.Exists (repositoryPath)) { + Log.ErrorLine ($"Repository {repositoryPath} does not exist"); + return false; + } + + var runner = CreateGitRunner (repositoryPath); + runner.AddArgument ("fetch"); + runner.AddArgument ("--all"); + runner.AddArgument ("--no-recurse-submodules"); + runner.AddArgument ("--progress"); + + return await RunGit (runner, $"fetch-{Path.GetFileName (repositoryPath)}"); + } + + public async Task Clone (string url, string destinationDirectoryPath) + { + if (String.IsNullOrEmpty (url)) + throw new ArgumentException ("must not be null or empty", nameof (url)); + if (String.IsNullOrEmpty (destinationDirectoryPath)) + throw new ArgumentException ("must not be null or empty", nameof (destinationDirectoryPath)); + + string parentDir = Path.GetDirectoryName (destinationDirectoryPath); + string dirName = Path.GetFileName (destinationDirectoryPath); + Utilities.CreateDirectory (parentDir); + var runner = CreateGitRunner (parentDir);; + runner.AddArgument ("clone"); + runner.AddArgument ("--progress"); + runner.AddQuotedArgument (url); + + return await RunGit (runner, $"clone-{dirName}"); + } + + public async Task SubmoduleUpdate (string workingDirectory = null, bool init = true, bool recursive = true) + { + string runnerWorkingDirectory = DetermineRunnerWorkingDirectory (workingDirectory); + + var runner = CreateGitRunner (runnerWorkingDirectory);; + runner.AddArgument ("submodule"); + runner.AddArgument ("update"); + if (init) + runner.AddArgument ("--init"); + if (recursive) + runner.AddArgument ("--recursive"); + + return await RunGit (runner, "submodule-update"); + } + + public string GetTopCommitHash (string workingDirectory = null, bool shortHash = true) + { + string runnerWorkingDirectory = DetermineRunnerWorkingDirectory (workingDirectory); + + var runner = CreateGitRunner (runnerWorkingDirectory); + runner.AddArgument ("rev-parse"); + runner.AddArgument ("HEAD"); + + Log.StatusLine (GetLogMessage (runner), CommandMessageColor); + + string hash = null; + using (var outputSink = (OutputSink)SetupOutputSink (runner)) { + outputSink.LineCallback = (string line) => { + if (!String.IsNullOrEmpty (hash)) + return; + hash = line?.Trim (); + }; + + if (!runner.Run ()) + return null; + + if (shortHash) + return Utilities.ShortenGitHash (hash); + + return hash; + } + } + + public async Task> Blame (string filePath) + { + return await Blame (filePath, gitArguments: null, blameArguments: null, workingDirectory: null); + } + + public async Task> Blame (string filePath, List blameArguments) + { + return await Blame (filePath, gitArguments: null, blameArguments: blameArguments, workingDirectory: null); + } + + public async Task> Blame (string filePath, List gitArguments, List blameArguments, string workingDirectory = null) + { + if (String.IsNullOrEmpty(filePath)) + throw new ArgumentException ("must not be null or empty", nameof (filePath)); + + var runner = CreateGitRunner (workingDirectory, gitArguments); + SetCommandArguments (runner, "blame", blameArguments); + runner.AddArgument ("-p"); + runner.AddQuotedArgument (filePath); + + var parserState = new BlameParserState { + CurrentEntry = null, + Entries = new List () + }; + + Log.StatusLine (GetLogMessage (runner), CommandMessageColor); + bool success = await RunTool ( + () => { + using (var outputSink = (OutputSink)SetupOutputSink (runner)) { + outputSink.LineCallback = (string line) => ParseBlameLine (line, parserState); + runner.WorkingDirectory = DetermineRunnerWorkingDirectory (workingDirectory); + return runner.Run (); + } + } + ); + + if (!success) + return null; + + return parserState.Entries; + } + + public async Task IsRepoUrlHttps (string workingDirectory) + { + if (!Directory.Exists (workingDirectory)) + throw new ArgumentException ("must exist", nameof (workingDirectory)); + + var runner = CreateGitRunner (workingDirectory); + runner.AddArgument ("config"); + runner.AddArgument ("--get"); + runner.AddArgument ("remote.origin.url"); + + bool containsHttps = false; + + bool success = await RunTool ( + () => { + using (var outputSink = (OutputSink)SetupOutputSink (runner)) { + outputSink.LineCallback = (string line) => { + containsHttps = !string.IsNullOrEmpty (line) && line.Contains ("https://"); + }; + runner.WorkingDirectory = DetermineRunnerWorkingDirectory (workingDirectory); + return runner.Run (); + } + } + ); + + if (!success) + return false; + + return containsHttps; + } + + ProcessRunner CreateGitRunner (string workingDirectory, List arguments = null) + { + var runner = CreateProcessRunner (); + runner.WorkingDirectory = workingDirectory; + SetGitArguments (runner, workingDirectory, arguments); + + return runner; + } + + async Task RunGit (ProcessRunner runner, string logTag) + { + try { + return await RunTool ( + () => { + using (var outputSink = (OutputSink)SetupOutputSink (runner)) { + StartTwiddler (); + return runner.Run (); + } + } + ); + } finally { + StopTwiddler (); + } + } + + void ParseBlameLine (string line, BlameParserState state) + { + if (state.CurrentEntry == null) + state.CurrentEntry = new BlamePorcelainEntry (); + + if (!state.CurrentEntry.ProcessLine (line)) + return; + + state.Entries.Add (state.CurrentEntry); + state.CurrentEntry = null; + } + + protected override TextWriter CreateLogSink (string logFilePath) + { + return new OutputSink (Log, logFilePath); + } + + void SetCommandArguments (ProcessRunner runner, string command, List commandArguments) + { + runner.AddArgument (command); + if (commandArguments == null || commandArguments.Count == 0) + return; + AddArguments (runner, commandArguments); + } + + string DetermineRunnerWorkingDirectory (string workingDirectory) + { + if (!String.IsNullOrEmpty (workingDirectory)) + return workingDirectory; + + return BuildPaths.XamarinAndroidSourceRoot; + } + + void SetGitArguments (ProcessRunner runner, string workingDirectory, List gitArguments) + { + foreach (string arg in standardGlobalOptions) { + runner.AddArgument (arg); + } + + if (!String.IsNullOrEmpty (workingDirectory)) { + runner.AddArgument ("-C"); + runner.AddQuotedArgument (workingDirectory); + } + + AddArguments (runner, gitArguments); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/ToolRunners/MSBuildRunner.OutputSink.cs b/build-tools/xaprepare/xaprepare/ToolRunners/MSBuildRunner.OutputSink.cs new file mode 100644 index 000000000..b5f9310c5 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ToolRunners/MSBuildRunner.OutputSink.cs @@ -0,0 +1,23 @@ +using System; +using System.Text; + +namespace Xamarin.Android.Prepare +{ + partial class MSBuildRunner + { + class OutputSink : ToolRunner.ToolOutputSink + { + public override Encoding Encoding => Encoding.Default; + + public OutputSink (Log log, string logFilePath) + : base (log, logFilePath) + { + } + + public override void WriteLine (string value) + { + base.WriteLine (value); + } + } + } +} diff --git a/build-tools/xaprepare/xaprepare/ToolRunners/MSBuildRunner.cs b/build-tools/xaprepare/xaprepare/ToolRunners/MSBuildRunner.cs new file mode 100644 index 000000000..05bce64a2 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ToolRunners/MSBuildRunner.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + partial class MSBuildRunner : ToolRunner + { + protected override string DefaultToolExecutableName => "msbuild"; + protected override string ToolName => "MSBuild"; + + public List StandardArguments { get; } + + public MSBuildRunner (Context context, Log log = null, string msbuildPath = null) + : base (context, log, msbuildPath) + { + ProcessTimeout = TimeSpan.FromMinutes (30); + + StandardArguments = new List { + $"/p:Configuration={context.Configuration}", + }; + } + + public async Task Run (string projectPath, string logTag, List arguments = null, string binlogName = null, string workingDirectory = null) + { + if (String.IsNullOrEmpty (logTag)) + throw new ArgumentException ("must not be null or empty", nameof (logTag)); + + if (String.IsNullOrEmpty (workingDirectory)) + workingDirectory = BuildPaths.XamarinAndroidSourceRoot; + + ProcessRunner runner = CreateProcessRunner (); + AddArguments (runner, StandardArguments); + if (!String.IsNullOrEmpty (binlogName)) { + string logPath = Utilities.GetRelativePath (workingDirectory, Path.Combine (Configurables.Paths.BuildBinDir, $"msbuild-{Context.BuildTimeStamp}-{binlogName}.binlog")); + runner.AddArgument ("/v:normal"); + runner.AddQuotedArgument ($"/bl:{logPath}"); + } + AddArguments (runner, arguments); + runner.AddQuotedArgument (Utilities.GetRelativePath (workingDirectory, projectPath)); + + string message = GetLogMessage (runner); + Log.Info (message, CommandMessageColor); + Log.StatusLine (); + + try { + return await RunTool ( + () => { + using (var outputSink = (OutputSink)SetupOutputSink (runner, $"msbuild.{logTag}")) { + runner.WorkingDirectory = workingDirectory; + StartTwiddler (); + return runner.Run (); + } + } + ); + } finally { + StopTwiddler (); + } + } + + protected override TextWriter CreateLogSink (string logFilePath) + { + return new OutputSink (Log, logFilePath); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/ToolRunners/MakeRunner.Linux.cs b/build-tools/xaprepare/xaprepare/ToolRunners/MakeRunner.Linux.cs new file mode 100644 index 000000000..ee4f6ddb3 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ToolRunners/MakeRunner.Linux.cs @@ -0,0 +1,7 @@ +namespace Xamarin.Android.Prepare +{ + partial class MakeRunner + { + protected override string DefaultToolExecutableName => "make"; + } +} diff --git a/build-tools/xaprepare/xaprepare/ToolRunners/MakeRunner.MacOS.cs b/build-tools/xaprepare/xaprepare/ToolRunners/MakeRunner.MacOS.cs new file mode 100644 index 000000000..4730ca00a --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ToolRunners/MakeRunner.MacOS.cs @@ -0,0 +1,30 @@ +using System; + +namespace Xamarin.Android.Prepare +{ + partial class MakeRunner + { + static string executableName; + + protected override string DefaultToolExecutableName => EnsureExecutableName (); + + static string EnsureExecutableName () + { + if (!String.IsNullOrEmpty (executableName)) + return executableName; + + // gmake is preferred since it comes from HomeBrew and is a much newer version than the one provided by + // Apple with Xcode. The reason we care is the `--output-sync` option which allows to generate sane output + // when running with `-j`. The option is supported since Make v4, however Apple provide version 3 while + // HomeBrew has v4 installed as `gmake` + string gmake = Context.Instance.OS.Which ("gmake", false); + if (!String.IsNullOrEmpty (gmake)) { + Log.Instance.DebugLine ($"Found gmake at {gmake}, using it instead of the Apple provided make"); + executableName = "gmake"; + } else + executableName = "make"; + + return executableName; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/ToolRunners/MakeRunner.OutputSink.Unix.cs b/build-tools/xaprepare/xaprepare/ToolRunners/MakeRunner.OutputSink.Unix.cs new file mode 100644 index 000000000..74dafc27f --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ToolRunners/MakeRunner.OutputSink.Unix.cs @@ -0,0 +1,19 @@ +using System; + +namespace Xamarin.Android.Prepare +{ + partial class MakeRunner + { + class OutputSink : ToolRunner.ToolOutputSink + { + public OutputSink (Log log, string logFilePath) + : base (log, logFilePath) + {} + + public override void WriteLine (string value) + { + base.WriteLine (value); + } + } + } +} diff --git a/build-tools/xaprepare/xaprepare/ToolRunners/MakeRunner.Unix.cs b/build-tools/xaprepare/xaprepare/ToolRunners/MakeRunner.Unix.cs new file mode 100644 index 000000000..2d705d00c --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ToolRunners/MakeRunner.Unix.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + partial class MakeRunner : ToolRunner + { + Version version; + + protected override string ToolName => "Make"; + public Version Version => version; + public bool NoParallelJobs { get; set; } + + public MakeRunner (Context context, Log log = null, string makePath = null) + : base (context, log, makePath) + { + ProcessTimeout = TimeSpan.FromMinutes (60); + string vs = VersionString?.Trim (); + if (String.IsNullOrEmpty (vs) || !Version.TryParse (vs, out version)) + version = new Version (0, 0); + } + + public async Task Run (string logTag, string workingDirectory = null, List arguments = null) + { + if (String.IsNullOrEmpty (logTag)) + throw new ArgumentException ("must not be null or empty", nameof (logTag)); + + bool haveChangeDirArg = false; + bool haveConcurrency = false; + ProcessRunner runner = CreateProcessRunner (); + if (arguments != null && arguments.Count > 0) { + foreach (string a in arguments) { + string arg = a?.Trim (); + if (String.IsNullOrEmpty (arg)) + continue; + if (arg.StartsWith ("-C", StringComparison.Ordinal) || arg.StartsWith ("--directory", StringComparison.Ordinal)) + haveChangeDirArg = true; + if (arg.StartsWith ("-j", StringComparison.Ordinal)) + haveConcurrency = true; + runner.AddQuotedArgument (arg); + } + } + + SetStandardArguments (workingDirectory, haveChangeDirArg, haveConcurrency, runner); + + string message = GetLogMessage (runner); + Log.Info (message, CommandMessageColor); + Log.StatusLine (); + + try { + return await RunTool ( + () => { + using (var outputSink = (OutputSink)SetupOutputSink (runner, $"make.{logTag}")) { + StartTwiddler (); + runner.WorkingDirectory = workingDirectory; + return runner.Run (); + } + } + ); + } finally { + StopTwiddler (); + } + } + + public void GetStandardArguments (ref List arguments, string workingDirectory = null) + { + var args = new List (); + SetStandardArguments (workingDirectory, false, false, arg => args.Add (arg)); + + if (args.Count == 0) + return; + + if (arguments == null) + arguments = args; + else + arguments.AddRange (args); + } + + void SetStandardArguments (string workingDirectory, bool haveChangeDirArg, bool haveConcurrency, ProcessRunner runner) + { + SetStandardArguments (workingDirectory, haveChangeDirArg, haveConcurrency, arg => runner.AddArgument (arg)); + } + + void SetStandardArguments (string workingDirectory, bool haveChangeDirArg, bool haveConcurrency, Action argumentSetter) + { + if (!haveChangeDirArg && !String.IsNullOrEmpty (workingDirectory)) { + argumentSetter ("-C"); + argumentSetter (ProcessRunner.QuoteArgument (workingDirectory)); + } + + bool concurrencyUsed; + if (!haveConcurrency && !NoParallelJobs && Context.MakeConcurrency > 1) { + argumentSetter ($"-j{Context.MakeConcurrency}"); + concurrencyUsed = true; + } else { + concurrencyUsed = false; + } + + if (concurrencyUsed && version.Major >= 4) { + argumentSetter ("--output-sync=target"); + } + } + + protected override TextWriter CreateLogSink (string logFilePath) + { + return new OutputSink (Log, logFilePath); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/ToolRunners/NinjaRunner.OutputSink.cs b/build-tools/xaprepare/xaprepare/ToolRunners/NinjaRunner.OutputSink.cs new file mode 100644 index 000000000..ecf0d0434 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ToolRunners/NinjaRunner.OutputSink.cs @@ -0,0 +1,33 @@ +using System; + +namespace Xamarin.Android.Prepare +{ + partial class NinjaRunner + { + class OutputSink : ToolRunner.ToolOutputSink + { + bool writeToStderr; + + public ProcessRunner Runner { get; set; } + + public OutputSink (Log log, string logFilePath) + : base (log, logFilePath) + {} + + public override void WriteLine (string value) + { + base.WriteLine (value); + + if (!writeToStderr && value.StartsWith ("FAILED:", StringComparison.Ordinal)) { + writeToStderr = true; + } + + if (!writeToStderr) { + return; + } + + Runner?.WriteStderrLine (value); + } + } + } +} diff --git a/build-tools/xaprepare/xaprepare/ToolRunners/NinjaRunner.cs b/build-tools/xaprepare/xaprepare/ToolRunners/NinjaRunner.cs new file mode 100644 index 000000000..95391774e --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ToolRunners/NinjaRunner.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + partial class NinjaRunner : ToolRunner + { + protected override string ToolName => "Ninja"; + protected override string DefaultToolExecutableName => "ninja"; + + public NinjaRunner (Context context, Log log = null, string toolPath = null) + : base (context, log, toolPath) + { + ProcessTimeout = TimeSpan.FromMinutes (60); + } + + public async Task Run (string logTag, string workingDirectory, List arguments = null) + { + if (String.IsNullOrEmpty (logTag)) + throw new ArgumentException ("must not be null or empty", nameof (logTag)); + if (String.IsNullOrEmpty (workingDirectory)) + throw new ArgumentException ("must not be null or empty", nameof (workingDirectory)); + + ProcessRunner runner = CreateProcessRunner (); + AddArguments (runner, arguments); + + bool haveConcurrency = false; + bool haveChangeDir = false; + if (arguments != null) { + foreach (string a in arguments) { + if (String.IsNullOrEmpty (a)) + continue; + + if (a.StartsWith ("-j", StringComparison.Ordinal)) + haveConcurrency = true; + if (a.StartsWith ("-C", StringComparison.Ordinal)) + haveChangeDir = true; + } + } + + if (!haveChangeDir) { + runner.AddQuotedArgument ($"-C{workingDirectory}"); + } + + if (!haveConcurrency && Context.MakeConcurrency > 1) { + runner.AddArgument ($"-j{Context.MakeConcurrency}"); + } + + try { + return await RunTool (() => { + using (var outputSink = (OutputSink)SetupOutputSink (runner, $"ninja.{logTag}")) { + outputSink.Runner = runner; + runner.WorkingDirectory = workingDirectory; + StartTwiddler (); + return runner.Run (); + } + }); + } finally { + StopTwiddler (); + } + } + + protected override TextWriter CreateLogSink (string logFilePath) + { + return new OutputSink (Log, logFilePath); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/ToolRunners/NuGetRunner.OutputSink.cs b/build-tools/xaprepare/xaprepare/ToolRunners/NuGetRunner.OutputSink.cs new file mode 100644 index 000000000..b38abdf15 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ToolRunners/NuGetRunner.OutputSink.cs @@ -0,0 +1,47 @@ +using System; +using System.Text; + +namespace Xamarin.Android.Prepare +{ + partial class NuGetRunner + { + class OutputSink : ToolRunner.ToolOutputSink + { + const string DefaultIndent = " "; + + string indent; + string packageBullet; + + public override Encoding Encoding => Encoding.Default; + + public OutputSink (Log log, string logFilePath, string indent = null) + : base (log, logFilePath) + { + this.indent = indent ?? DefaultIndent; + packageBullet = Context.Instance.Characters.Package; + } + + public override void WriteLine (string value) + { + const string RestoringPackagePrefix = "Restoring NuGet package"; + const string AllPackagesRestoredPrefix = "All packages listed in"; + + base.WriteLine (value); + if (String.IsNullOrEmpty (value)) + return; + + string consoleMessage = null; + if (value.StartsWith (RestoringPackagePrefix, StringComparison.OrdinalIgnoreCase)) { + consoleMessage = $"{packageBullet} {value.Substring (RestoringPackagePrefix.Length).Trim ().TrimEnd ('.')}"; + } else if (value.StartsWith (AllPackagesRestoredPrefix, StringComparison.OrdinalIgnoreCase)) { + consoleMessage = value.Trim (); + } + + if (consoleMessage == null) + return; + + Log.StatusLine ($"{indent}{consoleMessage}", ConsoleColor.White); + } + } + } +} diff --git a/build-tools/xaprepare/xaprepare/ToolRunners/NuGetRunner.cs b/build-tools/xaprepare/xaprepare/ToolRunners/NuGetRunner.cs new file mode 100644 index 000000000..c5794397a --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ToolRunners/NuGetRunner.cs @@ -0,0 +1,51 @@ +using System; +using System.IO; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + partial class NuGetRunner : ToolRunner + { + protected override string DefaultToolExecutableName => "nuget"; + protected override string ToolName => "NuGet"; + + public NuGetRunner (Context context, Log log = null, string nugetPath = null) + : base (context, log, nugetPath) + {} + + public async Task Restore (string solutionFilePath) + { + if (String.IsNullOrEmpty (solutionFilePath)) + throw new ArgumentException ("must not be null or empty", nameof (solutionFilePath)); + + if (!File.Exists (solutionFilePath)) + throw new InvalidOperationException ($"Solution file '{solutionFilePath}' does not exist"); + + ProcessRunner runner = CreateProcessRunner ("restore"); + + runner.AddArgument ("-Verbosity").AddArgument ("detailed"); + runner.AddArgument ("-NonInteractive"); + runner.AddArgument ("-ForceEnglishOutput"); + runner.AddQuotedArgument (solutionFilePath); + + try { + return await RunTool (() => { + using (TextWriter outputSink = SetupOutputSink (runner, $"nuget-restore.{Path.GetFileName (solutionFilePath)}", "restoring NuGet packages")) { + Log.StatusLine ($"Solution file: {Utilities.GetRelativePath (BuildPaths.XamarinAndroidSourceRoot, solutionFilePath)}", ConsoleColor.White); + runner.WorkingDirectory = Path.GetDirectoryName (solutionFilePath); + StartTwiddler (); + return runner.Run (); + } + } + ); + } finally { + StopTwiddler (); + } + } + + protected override TextWriter CreateLogSink (string logFilePath) + { + return new OutputSink (Log, logFilePath); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/ToolRunners/PkgutilRunner.MacOS.cs b/build-tools/xaprepare/xaprepare/ToolRunners/PkgutilRunner.MacOS.cs new file mode 100644 index 000000000..5543d0d0f --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ToolRunners/PkgutilRunner.MacOS.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + partial class PkgutilRunner : ToolRunner + { + protected override string DefaultToolExecutableName => Context.Instance?.Tools?.PkgutilPath ?? "pkgutil"; + protected override string ToolName => "PkgUtil"; + + public PkgutilRunner (Context context, Log log = null, string toolPath = null) + : base (context, log, toolPath) + {} + +#pragma warning disable CS1998 + public async Task LogPackagesInstalled () + { + Log.StatusLine ("Installed packages:"); + Log.StatusLine (Utilities.GetStringFromStdout (GetRunner (true, true, "--pkgs"))); + return true; + } + + public async Task GetPackageVersion (string packageId, bool echoOutput = false, bool echoError = true) + { + if (String.IsNullOrEmpty (packageId)) + throw new ArgumentException ("must not be null or empty", nameof (packageId)); + + string output = Utilities.GetStringFromStdout (GetRunner (echoOutput, echoError, "--pkg-info", packageId))?.Trim (); + if (String.IsNullOrEmpty (output)) + return null; + + string v = null; + foreach (string l in output.Split ('\n')) { + if (GetFieldValue (l.Trim (), "version:", out v)) + break; + } + + if (String.IsNullOrEmpty (v)) { + Log.WarningLine ($"Package info for {packageId} is empty"); + return null; + } + + if (!Version.TryParse (v, out Version pkgVer)) { + Log.ErrorLine ($"Failed to parse package {packageId} version from '{v}'"); + return null; + } + + return pkgVer; + } +#pragma warning restore CS1998 + + bool GetFieldValue (string line, string name, out string v) + { + v = null; + if (String.IsNullOrEmpty (line)) + return false; + + if (!line.StartsWith (name, StringComparison.Ordinal)) + return false; + + v = line.Substring (name.Length).Trim (); + return true; + } + + ProcessRunner GetRunner (bool echoOutput, bool echoError, params string[] parameters) + { + ProcessRunner runner = CreateProcessRunner (); + + AddArguments (runner, parameters); + + if (!echoOutput) { + runner.EchoStandardOutputLevel = ProcessStandardStreamWrapper.LogLevel.Debug; + } + + if (!echoError) { + runner.EchoStandardErrorLevel = ProcessStandardStreamWrapper.LogLevel.Debug; + } + + return runner; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/ToolRunners/SevenZipRunner.OutputSink.cs b/build-tools/xaprepare/xaprepare/ToolRunners/SevenZipRunner.OutputSink.cs new file mode 100644 index 000000000..3b7fb7f71 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ToolRunners/SevenZipRunner.OutputSink.cs @@ -0,0 +1,28 @@ +using System; +using System.Text; + +namespace Xamarin.Android.Prepare +{ + partial class SevenZipRunner + { + class OutputSink : ToolRunner.ToolOutputSink + { + public override Encoding Encoding => Encoding.Default; + + public OutputSink (Log log, string logFilePath, string indent = null) + : base (log, logFilePath) + {} + + public override void WriteLine (string value) + { + base.WriteLine (value); + if (String.IsNullOrEmpty (value)) + return; + + // 7zip poses a small problem - its progress indicator is a single line which is rewritten repeatedly by + // writing enough backspace ASCII characters to remove previous text and replace it with the updated + // progress message. This means we will NOT see the updates until the process is done... + } + } + } +} diff --git a/build-tools/xaprepare/xaprepare/ToolRunners/SevenZipRunner.cs b/build-tools/xaprepare/xaprepare/ToolRunners/SevenZipRunner.cs new file mode 100644 index 000000000..5acad46f4 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ToolRunners/SevenZipRunner.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + partial class SevenZipRunner : ToolRunner + { + const double DefaultTimeout = 30; // minutes + + protected override string DefaultToolExecutableName => "7za"; + protected override string ToolName => "7zip"; + + public SevenZipRunner (Context context, Log log = null, string toolPath = null) + : base (context, log, toolPath ?? Context.Instance.Tools.SevenZipPath) + { + ProcessTimeout = TimeSpan.FromMinutes (DefaultTimeout); + } + + public async Task Extract (string archivePath, string outputDirectory, List extraArguments = null) + { + if (String.IsNullOrEmpty (archivePath)) + throw new ArgumentException ("must not be null or empty", nameof (archivePath)); + if (String.IsNullOrEmpty (outputDirectory)) + throw new ArgumentException ("must not be null or empty", nameof (outputDirectory)); + + ProcessRunner runner = CreateProcessRunner ("x"); + AddStandardArguments (runner); + AddArguments (runner, extraArguments); + runner.AddQuotedArgument ($"-o{outputDirectory}"); + runner.AddQuotedArgument (archivePath); + + try { + Log.StatusLine ($"Archive path: {archivePath}", ConsoleColor.White); + return await RunTool ( + () => { + using (TextWriter outputSink = SetupOutputSink (runner, $"7zip-extract.{Path.GetFileName (archivePath)}", "extracting archive")) { + runner.WorkingDirectory = Path.GetDirectoryName (archivePath); + StartTwiddler (); + return runner.Run (); + } + } + ); + } finally { + StopTwiddler (); + } + } + + public async Task VerifyArchive (string archivePath) + { + if (String.IsNullOrEmpty (archivePath)) + throw new ArgumentException ("must not be null or empty", nameof (archivePath)); + + ProcessRunner runner = CreateProcessRunner ("t"); + AddStandardArguments (runner); + runner.AddQuotedArgument (archivePath); + + try { + Log.StatusLine ($"Archive path: {archivePath}"); + return await RunTool ( + () => { + using (TextWriter outputSink = SetupOutputSink (runner, $"7zip-verify-archive.{Path.GetFileName (archivePath)}", "verifying archive integrity")) { + StartTwiddler (); + return runner.Run (); + } + } + ); + } finally { + StopTwiddler (); + } + } + + public async Task SevenZip (string outputArchivePath, string workingDirectory, List inputFiles) + { + return await DoZip (outputArchivePath, workingDirectory, inputFiles, "7z", 9); + } + + public async Task Zip (string outputArchivePath, string workingDirectory, List inputFiles) + { + return await DoZip (outputArchivePath, workingDirectory, inputFiles, "zip", 9); + } + + async Task DoZip (string outputArchivePath, string workingDirectory, List inputFiles, string archiveFormat, uint compressionLevel) + { + if (String.IsNullOrEmpty (outputArchivePath)) + throw new ArgumentException ("must not be null or empty", nameof (outputArchivePath)); + if (String.IsNullOrEmpty (workingDirectory)) + throw new ArgumentException ("must not be null or empty", nameof (workingDirectory)); + if (inputFiles == null) + throw new ArgumentNullException (nameof (inputFiles)); + + var files = inputFiles.Where (f => !String.IsNullOrEmpty (f)).ToList (); + if (files.Count == 0) + throw new ArgumentException ("must not be an empty list", nameof (inputFiles)); + + ProcessRunner runner = CreateProcessRunner ("a"); + AddStandardArguments (runner); + runner.AddArgument ($"-t{archiveFormat}"); + runner.AddArgument ($"-mx={compressionLevel}"); // maximum compression (range: 0-9) + runner.AddQuotedArgument (outputArchivePath); + + string responseFilePath = Path.GetTempFileName (); + File.WriteAllLines (responseFilePath, files); + runner.AddQuotedArgument ($"@{responseFilePath}"); + + try { + Log.StatusLine ($"Archive path: {outputArchivePath}", ConsoleColor.White); + return await RunTool ( + () => { + using (TextWriter outputSink = SetupOutputSink (runner, $"7zip-create-{archiveFormat}.{Path.GetFileName (outputArchivePath)}", $"creating {archiveFormat} archive")) { + runner.WorkingDirectory = workingDirectory; + StartTwiddler (); + return runner.Run (); + } + } + ); + } finally { + StopTwiddler (); + Utilities.DeleteFileSilent (responseFilePath); + } + } + + protected override TextWriter CreateLogSink (string logFilePath) + { + return new OutputSink (Log, logFilePath); + } + + void AddStandardArguments (ProcessRunner runner) + { + // Disable progress indicator (doesn't appear to have any effect with some versions of 7z) + runner.AddArgument ("-bd"); + + // Write standard messages to stdout + runner.AddArgument ("-bso1"); + + // Write progress updates to stdout + runner.AddArgument ("-bsp1"); + + // Write errors to stderr + runner.AddArgument ("-bse2"); + + // Answer 'yes' to all questions + runner.AddArgument ("-y"); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/ToolRunners/TarRunner.OutputSink.cs b/build-tools/xaprepare/xaprepare/ToolRunners/TarRunner.OutputSink.cs new file mode 100644 index 000000000..59262b68a --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ToolRunners/TarRunner.OutputSink.cs @@ -0,0 +1,26 @@ +using System; +using System.Text; + +namespace Xamarin.Android.Prepare +{ + partial class TarRunner + { + class OutputSink : ToolRunner.ToolOutputSink + { + public override Encoding Encoding => Encoding.Default; + + public OutputSink (Log log, string logFilePath, string indent = null) + : base (log, logFilePath) + { + Log.Todo ("Implement parsing, if necessary"); + } + + public override void WriteLine (string value) + { + base.WriteLine (value); + if (String.IsNullOrEmpty (value)) + return; + } + } + } +} diff --git a/build-tools/xaprepare/xaprepare/ToolRunners/TarRunner.cs b/build-tools/xaprepare/xaprepare/ToolRunners/TarRunner.cs new file mode 100644 index 000000000..6df57782c --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ToolRunners/TarRunner.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + partial class TarRunner : ToolRunner + { + protected override string DefaultToolExecutableName => "tar"; + protected override string ToolName => "Tar"; + + public TarRunner (Context context, Log log = null, string toolPath = null) + : base (context, log, toolPath) + {} + + public async Task Extract (string fullArchivePath, string destinationDirectory) + { + if (String.IsNullOrEmpty (fullArchivePath)) + throw new ArgumentException ("must not be null or empty", nameof (fullArchivePath)); + if (String.IsNullOrEmpty (destinationDirectory)) + throw new ArgumentException ("must not be null or empty", nameof (destinationDirectory)); + + ProcessRunner runner = CreateProcessRunner (); + runner.AddArgument ("-x"); + runner.AddArgument ("-f"); + runner.AddQuotedArgument (fullArchivePath); + + try { + Log.StatusLine ($"Archive path: {fullArchivePath}"); + return await RunTool ( + () => { + using (TextWriter outputSink = SetupOutputSink (runner, $"tar-extract-archive.{Path.GetFileName (fullArchivePath)}", "extracting archive")) { + StartTwiddler (); + runner.WorkingDirectory = destinationDirectory; + return runner.Run (); + } + } + ); + } finally { + StopTwiddler (); + } + } + + protected override TextWriter CreateLogSink (string logFilePath) + { + return new OutputSink (Log, logFilePath); + } + } +} diff --git a/build-tools/xaprepare/xaprepare/ToolRunners/ToolRunner.OutputSink.cs b/build-tools/xaprepare/xaprepare/ToolRunners/ToolRunner.OutputSink.cs new file mode 100644 index 000000000..af6a09a8b --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ToolRunners/ToolRunner.OutputSink.cs @@ -0,0 +1,40 @@ +using System; +using System.IO; +using System.Text; + +namespace Xamarin.Android.Prepare +{ + abstract partial class ToolRunner + { + protected abstract class ToolOutputSink : TextWriter + { + protected Log Log { get; } + protected StreamWriter Writer { get; private set; } + + public override Encoding Encoding => Encoding.Default; + + protected ToolOutputSink (Log log, string logFilePath) + { + Log = log ?? throw new ArgumentNullException (nameof (log)); + if (!String.IsNullOrEmpty (logFilePath)) + Writer = new StreamWriter (File.Open (logFilePath, FileMode.Create, FileAccess.Write), Utilities.UTF8NoBOM); + } + + public override void WriteLine (string value) + { + Writer?.WriteLine (value ?? String.Empty); + } + + protected override void Dispose (bool disposing) + { + if (disposing && Writer != null) { + Writer.Flush (); + Writer.Dispose (); + Writer = null; + } + + base.Dispose (disposing); + } + } + } +} diff --git a/build-tools/xaprepare/xaprepare/ToolRunners/ToolRunner.cs b/build-tools/xaprepare/xaprepare/ToolRunners/ToolRunner.cs new file mode 100644 index 000000000..3c84cf465 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/ToolRunners/ToolRunner.cs @@ -0,0 +1,167 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + abstract partial class ToolRunner : AppObject + { + static readonly TimeSpan DefaultProcessTimeout = TimeSpan.FromMinutes (15); + + ThumbTwiddler twiddler; + string programVersion; + + protected const ConsoleColor CommandMessageColor = ConsoleColor.White; + + protected abstract string DefaultToolExecutableName { get; } + protected abstract string ToolName { get; } + + protected Context Context { get; } + public string FullToolPath { get; } + protected string VersionString => GetVersion (); + + public bool EchoCmdAndArguments { get; set; } = true; + public bool EchoStandardError { get; set; } = true; + public bool EchoStandardOutput { get; set; } + public string LogMessageIndent { get; set; } = String.Empty; + public virtual TimeSpan ProcessTimeout { get; set; } = DefaultProcessTimeout; + + protected ToolRunner (Context context, Log log = null, string toolPath = null) + : base (log) + { + Context = context ?? throw new ArgumentNullException (nameof (context)); + + if (String.IsNullOrEmpty (toolPath)) { + Log.DebugLine ($"Locating {ToolName} executable '{DefaultToolExecutableName}'"); + FullToolPath = context.OS.Which (DefaultToolExecutableName); + } else { + Log.DebugLine ($"Custom NuGet path: {toolPath}"); + if (toolPath.IndexOf (Path.DirectorySeparatorChar) < 0) { + Log.DebugLine ($"Locating custom {ToolName} executable '{toolPath}'"); + FullToolPath = context.OS.Which (toolPath); + } else if (Path.IsPathRooted (toolPath)) { + Log.DebugLine ($"{ToolName} executable path is rooted, using verbatim"); + FullToolPath = toolPath; + } else { + Log.DebugLine ($"{ToolName} executable path is relative to Xamarin.Android repository root"); + FullToolPath = Path.Combine (BuildPaths.XamarinAndroidSourceRoot, toolPath); + } + } + + if (String.IsNullOrEmpty (FullToolPath)) + throw new InvalidOperationException ($"{ToolName} executable path must be specified"); + + Log.DebugLine ($"Full {ToolName} executable path value: {FullToolPath}"); + if (!File.Exists (FullToolPath)) + throw new InvalidOperationException ($"{ToolName} executable '{FullToolPath}' not found"); + } + + protected virtual string GetLogMessage (ProcessRunner runner) + { + string message = $"{LogMessageIndent}{Path.GetFileName (FullToolPath)}"; + string formattedArguments = runner.Arguments; + if (!String.IsNullOrEmpty (formattedArguments)) + message = $"{message} {runner.Arguments}"; + + return $"{message} "; + } + + protected void AddArguments (ProcessRunner runner, IEnumerable arguments) + { + if (arguments == null) + return; + + foreach (string a in arguments) { + string arg = a?.Trim (); + if (String.IsNullOrEmpty (arg)) + continue; + + runner.AddQuotedArgument (arg); + } + } + + protected virtual ProcessRunner CreateProcessRunner (params string[] initialParams) + { + string managedRunner = Context.OS.GetManagedProgramRunner (FullToolPath); + if (managedRunner != null) + managedRunner = Context.OS.Which (managedRunner); + + var runner = new ProcessRunner (managedRunner ?? FullToolPath, initialParams) { + ProcessTimeout = ProcessTimeout, + EchoCmdAndArguments = EchoCmdAndArguments, + EchoStandardError = EchoStandardError, + EchoStandardOutput = EchoStandardOutput, + }; + + if (Context.Instance.OS.EnvironmentVariables != null) { + foreach (var kvp in Context.Instance.OS.EnvironmentVariables) { + runner.Environment [kvp.Key] = kvp.Value; + } + } + + if (managedRunner != null) + runner.AddQuotedArgument (FullToolPath); + + return runner; + } + + protected virtual async Task RunTool (Func runner) + { + return await Task.Run (runner); + } + + protected TextWriter SetupOutputSink (ProcessRunner runner, string tags = null, string messagePrefix = null) + { + string logFilePath = null; + + if (!String.IsNullOrEmpty (tags)) { + logFilePath = Context.GetLogFilePath (tags ?? String.Empty); + if (String.IsNullOrEmpty (messagePrefix)) + messagePrefix = "running"; + Log.StatusLine ($"{LogMessageIndent}[{ToolName}] {messagePrefix}"); + Log.StatusLine ($"[{ToolName}] log file: ", $"{Utilities.GetRelativePath (BuildPaths.XamarinAndroidSourceRoot, logFilePath)}", tailColor: Log.DestinationColor); + } + + TextWriter ret = CreateLogSink (logFilePath); + + runner.AddStandardErrorSink (ret); + runner.AddStandardOutputSink (ret); + + return ret; + } + + protected virtual TextWriter CreateLogSink (string logFilePath) + { + throw new NotSupportedException ("Child class must implement this method if it uses GetLogFileSink"); + } + + protected void StartTwiddler (bool showElapsedTime = true) + { + twiddler = new ThumbTwiddler (Context.Instance, Context.Instance.DullMode, showElapsedTime); + twiddler.Start (); + } + + protected void StopTwiddler () + { + if (twiddler == null) + return; + + twiddler.Stop (); + Log.StatusLine (); + } + + string GetVersion () + { + if (!String.IsNullOrEmpty (programVersion)) + return programVersion; + + (bool success, string version) = Utilities.GetProgramVersion (FullToolPath); + if (!success) + return null; + + programVersion = version; + return version; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/app.manifest b/build-tools/xaprepare/xaprepare/app.manifest new file mode 100644 index 000000000..cd575f456 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/app.manifest @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build-tools/xaprepare/xaprepare/xaprepare.csproj b/build-tools/xaprepare/xaprepare/xaprepare.csproj new file mode 100644 index 000000000..1e090f8d7 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/xaprepare.csproj @@ -0,0 +1,292 @@ + + + + Debug + x86 + {965D4281-1668-498C-86C4-264D7A44DAE7} + Exe + Xamarin.Android.Prepare + xaprepare + true + + + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + true + x86 + 7.1 + bin\Debug\xaprepare.xml + + + true + bin\Release + prompt + 4 + true + x86 + 7.1 + bin\Release\xaprepare.xml + + + app.manifest + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Always + + + + + 18.1.0 + + + 0.2.6862.30334 + + + 5.3.0.1 + + + + + + + + + + + + + + diff --git a/build-tools/xaprepare/xaprepare/xaprepare.targets b/build-tools/xaprepare/xaprepare/xaprepare.targets new file mode 100644 index 000000000..5614572a7 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/xaprepare.targets @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + BuildInfo.cs.in + $(IntermediateOutputPath)\BuildInfo.Generated.cs + Application/Properties.Defaults.cs.in + $(IntermediateOutputPath)\Properties.Defaults.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/external/LibZipSharp b/external/LibZipSharp index caa0c7497..da94d3df8 160000 --- a/external/LibZipSharp +++ b/external/LibZipSharp @@ -1 +1 @@ -Subproject commit caa0c7497c01f92921062b252dafa3531957e02f +Subproject commit da94d3df843857a86b7a88319b52064102a27eaf diff --git a/src/Mono.Android/Mono.Android.csproj b/src/Mono.Android/Mono.Android.csproj index 9ae5889e8..62103105d 100644 --- a/src/Mono.Android/Mono.Android.csproj +++ b/src/Mono.Android/Mono.Android.csproj @@ -361,11 +361,6 @@ jcw-gen False - - {C03E6CF1-7460-4CDC-A4AB-292BBC0F61F2} - mono-runtimes - False - {1D4FC8F1-0DA4-4F38-BE68-11AEBA9A0EA4} java-runtime diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.targets index 4aee97fbc..9f7e36938 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.targets @@ -5,7 +5,7 @@ - + <_SharedRuntimeBuildPath Condition=" '$(_SharedRuntimeBuildPath)' == '' ">$(XAInstallPrefix)xbuild-frameworks\MonoAndroid\ @@ -164,7 +164,6 @@ <_NDKToolFileName>$([System.IO.Path]::GetFileName ('$(_NDKToolLocation)')) <_NDKToolDestinationBase>$(XAInstallPrefix)xbuild\Xamarin\Android - <_WinAsmDestDir>$(XAInstallPrefix)xbuild\Xamarin\Android @@ -174,21 +173,6 @@ - - - - - - - - {8FF78EB6-6FC8-46A7-8A15-EBBA9045C5FA} - android-toolchain - False - - \ No newline at end of file diff --git a/src/bundletool/bundletool.csproj b/src/bundletool/bundletool.csproj index fe3a3a689..a7ba6c387 100644 --- a/src/bundletool/bundletool.csproj +++ b/src/bundletool/bundletool.csproj @@ -11,11 +11,4 @@ - - - {8FF78EB6-6FC8-46A7-8A15-EBBA9045C5FA} - android-toolchain - False - - \ No newline at end of file diff --git a/src/libzip-windows/libzip-windows.csproj b/src/libzip-windows/libzip-windows.csproj index 649d8462a..a9009cf3e 100644 --- a/src/libzip-windows/libzip-windows.csproj +++ b/src/libzip-windows/libzip-windows.csproj @@ -26,11 +26,4 @@ - - - {2C1C68CD-CFED-4DEB-A2D3-61D6932F3E8E} - mingw-dependencies - False - - diff --git a/src/mono-runtimes/ProfileAssemblies.projitems b/src/mono-runtimes/ProfileAssemblies.projitems deleted file mode 100644 index 340fb0dea..000000000 --- a/src/mono-runtimes/ProfileAssemblies.projitems +++ /dev/null @@ -1,318 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - xunit - - - - - - - - - - - - - xunit - - - - - - - - - - - - - xunit - - - - - - - xunit - - - - - xunit - - - - - - - - - xunit - - - xunit - - - xunit - - - - - - - xunit - - - xunit - - - - - xunit - - - - - xunit - - - - - - - - - - - - - xunit - - - - - xunit - - - - - xunit - - - - - - - - - - - reference - - - - reference - - - - diff --git a/src/mono-runtimes/mono-runtimes.csproj b/src/mono-runtimes/mono-runtimes.csproj deleted file mode 100644 index 3001aed83..000000000 --- a/src/mono-runtimes/mono-runtimes.csproj +++ /dev/null @@ -1,41 +0,0 @@ - - - - Debug - AnyCPU - {C03E6CF1-7460-4CDC-A4AB-292BBC0F61F2} - v4.5 - - - - $(XAInstallPrefix) - - - $(XAInstallPrefix) - - - - - ResolveReferences; - _BuildUnlessCached - - - - - - {7CE69551-BD73-4726-ACAA-AAF89C84BAF8} - xa-prep-tasks - False - - - {8FF78EB6-6FC8-46A7-8A15-EBBA9045C5FA} - android-toolchain - False - - - {C876DA71-8573-4CEF-9149-716D72455ED4} - remap-assembly-ref - False - - - diff --git a/src/mono-runtimes/mono-runtimes.projitems b/src/mono-runtimes/mono-runtimes.projitems deleted file mode 100644 index bb8380f5e..000000000 --- a/src/mono-runtimes/mono-runtimes.projitems +++ /dev/null @@ -1,208 +0,0 @@ - - - - <_MonoBcl Include="bcl" /> - - - - <_NDKToolchainArch Condition=" '$(HostOS)' == 'Linux' ">linux-x86_64 - <_NDKToolchainArch Condition=" '$(HostOS)' == 'Darwin' ">darwin-x86_64 - <_NDKToolchainArch Condition=" '$(HostOS)' == 'Windows' ">windows - <_NDKToolchainPrefix>$(AndroidNdkFullPath)\toolchains\llvm\prebuilt\$(_NDKToolchainArch) - - - - <_MonoRuntime Include="armeabi-v7a" Condition=" $(AndroidSupportedTargetJitAbisForConditionalChecks.Contains (':armeabi-v7a:')) "> - $(_NDKToolchainPrefix)\bin\arm-linux-androideabi-strip - libmonosgen-2.0 - so - True - libmono-profiler-log - libmono-profiler-aot - libmono-btls-shared - libMonoPosixHelper - - <_MonoRuntime Include="arm64-v8a" Condition=" $(AndroidSupportedTargetJitAbisForConditionalChecks.Contains (':arm64-v8a:')) "> - $(_NDKToolchainPrefix)\bin\aarch64-linux-android-strip - libmonosgen-2.0 - so - True - libmono-profiler-log - libmono-profiler-aot - libmono-btls-shared - libMonoPosixHelper - - <_MonoRuntime Include="x86" Condition=" $(AndroidSupportedTargetJitAbisForConditionalChecks.Contains (':x86:')) "> - $(_NDKToolchainPrefix)\bin\i686-linux-android-strip - libmonosgen-2.0 - so - True - libmono-btls-shared - libmono-profiler-log - libmono-profiler-aot - libMonoPosixHelper - - <_MonoRuntime Include="x86_64" Condition=" $(AndroidSupportedTargetJitAbisForConditionalChecks.Contains (':x86_64:')) "> - $(_NDKToolchainPrefix)\bin\x86_64-linux-android-strip - libmonosgen-2.0 - so - True - libmono-profiler-log - libmono-profiler-aot - libmono-btls-shared - libMonoPosixHelper - - - - - False - - - True - - - - <_MonoRuntime Include="host-mxe-Win64" Condition="$(AndroidSupportedHostJitAbisForConditionalChecks.Contains (':mxe-Win64:')) Or $(AndroidSupportedHostJitAbisForConditionalChecks.Contains (':linux-Win64:'))"> - $(AndroidMxeFullPath)\bin\$(MingwCommandPrefix64)-strip - ..\bin\ - dll - True - libmonosgen-2.0 - - - - libMonoPosixHelper - - <_MonoRuntime Include="host-mxe-Win32" Condition="$(AndroidSupportedHostJitAbisForConditionalChecks.Contains (':mxe-Win32:')) Or $(AndroidSupportedHostJitAbisForConditionalChecks.Contains (':linux-Win32:'))"> - $(AndroidMxeFullPath)\bin\$(MingwCommandPrefix32)-strip - ..\bin\ - dll - $(CanStripWin32DLL) - libmonosgen-2.0 - - - - libMonoPosixHelper - - <_MonoRuntime Include="host-$(HostOS)" Condition="$(AndroidSupportedHostJitAbisForConditionalChecks.Contains (':$(HostOS):')) And ( '$(HostOS)' == 'Darwin' Or '$(HostOS)' == 'Linux' )"> - strip - -S - libmonosgen-2.0 - dylib - so - True - libmono-profiler-log - libmono-profiler-aot - - libMonoPosixHelper - - - - - - <_LlvmRuntime Include="llvm32" Condition=" ($(AndroidSupportedTargetAotAbisForConditionalChecks.Contains (':armeabi-v7a:')) Or $(AndroidSupportedTargetAotAbisForConditionalChecks.Contains (':x86:'))) "> - - true - Darwin/ - Linux/ - - - <_LlvmRuntime Include="llvm64" Condition=" ($(AndroidSupportedTargetAotAbisForConditionalChecks.Contains (':arm64:')) Or $(AndroidSupportedTargetAotAbisForConditionalChecks.Contains (':x86_64:'))) And '$(HostBits)' == '64' "> - - true - Darwin/ - Linux/ - - - <_LlvmRuntime Include="llvmwin32" Condition=" ($(AndroidSupportedTargetAotAbisForConditionalChecks.Contains (':win-armeabi-v7a:')) Or $(AndroidSupportedTargetAotAbisForConditionalChecks.Contains (':win-x86:'))) "> - .exe - true - - - - <_LlvmRuntime Include="llvmwin64" Condition=" ($(AndroidSupportedTargetAotAbisForConditionalChecks.Contains (':win-arm64:')) Or $(AndroidSupportedTargetAotAbisForConditionalChecks.Contains (':win-x86_64:'))) And '$(HostBits)' == '64' "> - .exe - false - - - - - - - <_MonoCrossRuntime Include="cross-arm" Condition="$(AndroidSupportedTargetAotAbisForConditionalChecks.Contains (':armeabi-v7a:'))"> - strip - -S - armv7-linux-android- - - Darwin/ - Linux/ - cross-arm - - - <_MonoCrossRuntime Include="cross-arm64" Condition="$(AndroidSupportedTargetAotAbisForConditionalChecks.Contains (':arm64:'))"> - strip - -S - aarch64-v8a-linux-android- - - Darwin/ - Linux/ - cross-arm64 - - - <_MonoCrossRuntime Include="cross-x86" Condition="$(AndroidSupportedTargetAotAbisForConditionalChecks.Contains (':x86:'))"> - strip - -S - i686-linux-android- - - Darwin/ - Linux/ - cross-x86 - - - <_MonoCrossRuntime Include="cross-x86_64" Condition="$(AndroidSupportedTargetAotAbisForConditionalChecks.Contains (':x86_64:'))"> - strip - -S - x86_64-linux-android- - - Darwin/ - Linux/ - cross-x86_64 - - - <_MonoCrossRuntime Include="cross-arm-win" Condition="$(AndroidSupportedTargetAotAbisForConditionalChecks.Contains (':win-armeabi-v7a:'))"> - $(AndroidMxeFullPath)\bin\$(MingwCommandPrefix32)-strip - - armv7-linux-android- - .exe - - cross-arm - - - <_MonoCrossRuntime Include="cross-arm64-win" Condition="$(AndroidSupportedTargetAotAbisForConditionalChecks.Contains (':win-arm64:'))"> - $(AndroidMxeFullPath)\bin\$(MingwCommandPrefix64)-strip - - aarch64-v8a-linux-android- - .exe - - cross-arm64 - - - <_MonoCrossRuntime Include="cross-x86-win" Condition="$(AndroidSupportedTargetAotAbisForConditionalChecks.Contains (':win-x86:'))"> - $(AndroidMxeFullPath)\bin\$(MingwCommandPrefix32)-strip - - i686-linux-android- - .exe - - cross-x86 - - - <_MonoCrossRuntime Include="cross-x86_64-win" Condition="$(AndroidSupportedTargetAotAbisForConditionalChecks.Contains (':win-x86_64:'))"> - $(AndroidMxeFullPath)\bin\$(MingwCommandPrefix64)-strip - - x86_64-linux-android- - .exe - - cross-x86_64 - - - diff --git a/src/mono-runtimes/mono-runtimes.targets b/src/mono-runtimes/mono-runtimes.targets deleted file mode 100644 index 0d3c69877..000000000 --- a/src/mono-runtimes/mono-runtimes.targets +++ /dev/null @@ -1,736 +0,0 @@ - - - - - - - - - - - <_SourceTopDir>..\.. - <_BclFrameworkDir>$(XAInstallPrefix)xbuild-frameworks\MonoAndroid\v1.0\ - <_MSBuildDir>$(XAInstallPrefix)xbuild\Xamarin\Android - <_OutputIncludeDir>..\..\bin\$(Configuration)\include\ - <_DebugFileExt Condition=" '$(_DebugFileExt)' == '' ">.pdb - - - - - - - - - - <_MonoDocCopyItems Include="@(MonoDocCopyItem->'$(_MonoProfileToolsDir)\%(Identity)')" /> - - - <_MonoDocInstalledItems Include="@(MonoDocCopyItem->'$(_MSBuildDir)\%(Identity)')" /> - <_MonoDocInstalledItems Include="$(_MSBuildDir)\mdoc.exe" /> - - - <_MonoProfileDir>$(MonoSourceFullPath)\sdks\out\android-bcl\monodroid - <_MonoProfileToolsDir>$(MonoSourceFullPath)\sdks\out\android-bcl\monodroid_tools - - - <_MonoSdksConfiguration Condition=" '$(Configuration)' == 'Release' ">release - <_MonoSdksConfiguration Condition=" '$(Configuration)' != 'Release' ">debug - - <_MonoSdksAndroidToolchainDir>$(AndroidToolchainDirectory) - <_MonoSdksAndroidToolchainCacheDir>$(AndroidToolchainCacheDirectory) - <_MonoSdksAndroidToolchainPrefix>$(AndroidToolchainDirectory)\toolchains - <_MonoSdksMxePrefixDir>$(AndroidToolchainDirectory) - <_MonoSdksMxeSrc>$(MSBuildThisFileDirectory)..\..\external\mxe - <_MonoSdksParameters>CONFIGURATION=$(_MonoSdksConfiguration) IGNORE_PROVISION_MXE=false IGNORE_PROVISION_ANDROID=true ANDROID_CMAKE_VERSION=$(AndroidCmakeVersionPath) ANDROID_NDK_VERSION=r$(AndroidNdkVersion) ANDROID_SDK_VERSION_armeabi-v7a=$(AndroidNdkApiLevel_ArmV7a) ANDROID_SDK_VERSION_arm64-v8a=$(AndroidNdkApiLevel_ArmV8a) ANDROID_SDK_VERSION_x86=$(AndroidNdkApiLevel_X86) ANDROID_SDK_VERSION_x86_64=$(AndroidNdkApiLevel_X86_64) ANDROID_TOOLCHAIN_DIR="$(_MonoSdksAndroidToolchainDir)" ANDROID_TOOLCHAIN_CACHE_DIR="$(_MonoSdksAndroidToolchainCacheDir)" ANDROID_TOOLCHAIN_PREFIX="$(_MonoSdksAndroidToolchainPrefix)" MXE_PREFIX_DIR="$(_MonoSdksMxePrefixDir)" MXE_SRC="$(_MonoSdksMxeSrc)" - - - - _DownloadArchive; - _Build; - _InstallRuntimes; - _InstallLlvm; - _InstallBcl; - _InstallCilStrip; - _InstallMonoDoc; - _InstallMonoUtilities; - - - - - - <_BclAssembly Include="@(MonoProfileAssembly)" /> - <_BclExcludeDebugSymbols Include="System.Reflection.TestModule.dll" /> - <_BclExcludeDebugSymbols Include="System.Runtime.CompilerServices.Unsafe.dll" /> - <_BclExcludeDebugSymbols Include="System.Windows.dll" /> - <_BclExcludeDebugSymbols Include="System.Xml.Serialization.dll" /> - <_BclTestAssemblyDestination Include="@(MonoTestAssembly->'$(XAInstallPrefix)\..\..\bcl-tests\%(Identity)')" /> - <_BclTestAssemblyDestination Include="@(MonoTestSatelliteAssembly->'$(XAInstallPrefix)\..\..\bcl-tests\%(Identity)')" /> - <_BclTestAssemblyDestination - Include="@(MonoTestAssembly->'$(XAInstallPrefix)\..\..\bcl-tests\%(Filename).pdb')" - Exclude="@(_BclExcludeDebugSymbols->'$(XAInstallPrefix)\..\..\bcl-tests\%(Filename).pdb')" - /> - <_BclTestAssemblyDestination Include="@(MonoTestRunner->'$(XAInstallPrefix)\..\..\bcl-tests\%(Identity)')" /> - <_BclTestAssemblyDestination Include="@(MonoTestRunner->'$(XAInstallPrefix)\..\..\bcl-tests\%(Filename).pdb')" /> - - - <_BclTestOutput Include="@(_BclTestAssemblyDestination)" /> - <_BclTestOutput Include="$(XAInstallPrefix)\..\..\bcl-tests\bcl-tests.zip" /> - - - <_BclProfileItems Include="@(_BclAssembly->'$(_MonoProfileDir)\%(Identity)')" /> - <_BclProfileItems - Condition=" '$(_DebugFileExt)' == '.pdb' " - Include="@(_BclAssembly->'$(_MonoProfileDir)\%(Filename).pdb')" - Exclude="@(_BclExcludeDebugSymbols->'$(_MonoProfileDir)\%(Filename).pdb')" - /> - - - <_BclInstalledItem Include="@(_BclAssembly->'$(_BclFrameworkDir)%(Identity)')" /> - <_BclInstalledItem - Condition=" '$(_DebugFileExt)' == '.mdb' " - Include="@(_BclAssembly->'$(_BclFrameworkDir)%(Identity).mdb')" - Exclude="@(_BclExcludeDebugSymbols->'$(_BclFrameworkDir)%(Identity).mdb')" - /> - <_BclInstalledItem - Condition=" '$(_DebugFileExt)' == '.pdb' " - Include="@(_BclAssembly->'$(_BclFrameworkDir)%(Filename).pdb')" - Exclude="@(_BclExcludeDebugSymbols->'$(_BclFrameworkDir)%(Filename).pdb')" - /> - - - <_MonoUtility Include="illinkanalyzer.exe" /> - <_MonoUtility Include="mkbundle.exe" /> - <_MonoUtility Include="mono-symbolicate.exe" /> - <_MonoUtility Include="mono-api-html.exe" /> - <_MonoUtility Include="mono-api-info.exe" /> - - - <_MonoUtilitySource Include="@(_MonoUtility->'$(_MonoProfileToolsDir)\%(Identity)')" /> - <_MonoUtilityDest Include="@(_MonoUtility->'$(_MSBuildDir)\%(Identity)')" /> - <_MonoUtilitySource - Condition=" '$(_DebugFileExt)' == '.mdb'" - Include="@(_MonoUtility->'$(_MonoProfileToolsDir)\%(Identity).mdb')" - /> - <_MonoUtilityDest - Condition=" '$(_DebugFileExt)' == '.mdb'" - Include="@(_MonoUtility->'$(_MSBuildDir)\%(Identity).mdb')" - /> - <_MonoUtilitySource - Condition=" '$(_DebugFileExt)' == '.pdb'" - Include="@(_MonoUtility->'$(_MonoProfileToolsDir)\%(Filename).pdb')" - /> - <_MonoUtilityDest - Condition=" '$(_DebugFileExt)' == '.pdb'" - Include="@(_MonoUtility->'$(_MSBuildDir)\%(Filename).pdb')" - /> - - - - - - - - - - - <_LlvmSourceBinary Include="@(_LlvmRuntime->'$(MonoSourceFullPath)\sdks\out\llvm-%(Identity)\bin\opt%(ExeSuffix)')" Condition=" '%(_LlvmRuntime.InstallBinaries)' == 'true' " /> - <_LlvmTargetBinary Include="@(_LlvmRuntime->'$(_MSBuildDir)\%(InstallPath)opt%(ExeSuffix)')" Condition=" '%(_LlvmRuntime.InstallBinaries)' == 'true' " /> - - <_LlvmSourceBinary Include="@(_LlvmRuntime->'$(MonoSourceFullPath)\sdks\out\llvm-%(Identity)\bin\llc%(ExeSuffix)')" Condition=" '%(_LlvmRuntime.InstallBinaries)' == 'true' " /> - <_LlvmTargetBinary Include="@(_LlvmRuntime->'$(_MSBuildDir)\%(InstallPath)llc%(ExeSuffix)')" Condition=" '%(_LlvmRuntime.InstallBinaries)' == 'true' " /> - - - - - - - - - - - - - - <_MonoArchiveUriBase>https://xamjenkinsartifact.azureedge.net/mono-sdks - <_MonoArchiveName>android-$(_MonoSdksConfiguration)-$(HostOS)-$(_MonoGitCommitHash) - <_WindowsMonoArchiveName>android-release-Windows-$(_MonoGitCommitHash) - <_MonoArchiveBclDir>$(_MSBuildDir)\$(HostOS)\bcl - <_WindowsMonoArchiveBclDir>$(_MSBuildDir)\bcl - - - - - - <_RuntimeSource - Include="@(_MonoRuntime->'$(MonoSourceFullPath)\sdks\out\android-%(Identity)-$(_MonoSdksConfiguration)\lib\%(NativeLibraryPrefix)%(OutputRuntimeFilename).%(NativeLibraryExtension)')" - /> - <_RuntimeSource - Condition=" 'host-Darwin' == '%(Identity)' " - Include="@(_MonoRuntime->'$(MonoSourceFullPath)\sdks\out\android-%(Identity)-$(_MonoSdksConfiguration)\lib\%(NativeLibraryPrefix)libmono-native-compat.%(NativeLibraryExtension)')" - /> - <_RuntimeSource - Condition=" 'host-Darwin' != '%(Identity)' And 'host-mxe-Win32' != '%(Identity)' And 'host-mxe-Win64' != '%(Identity)' " - Include="@(_MonoRuntime->'$(MonoSourceFullPath)\sdks\out\android-%(Identity)-$(_MonoSdksConfiguration)\lib\%(NativeLibraryPrefix)libmono-native.%(NativeLibraryExtension)')" - /> - <_InstallRuntimeOutput - Include="@(_MonoRuntime->'$(_MSBuildDir)\lib\%(Identity)\%(OutputRuntimeFilename).%(NativeLibraryExtension)')" - /> - <_InstallRuntimeOutput - Condition=" 'host-Darwin' == '%(Identity)' " - Include="@(_MonoRuntime->'$(_MSBuildDir)\lib\%(Identity)\libmono-native.%(NativeLibraryExtension)')" - /> - <_InstallRuntimeOutput - Condition=" 'host-Darwin' != '%(Identity)' And 'host-mxe-Win32' != '%(Identity)' And 'host-mxe-Win64' != '%(Identity)'" - Include="@(_MonoRuntime->'$(_MSBuildDir)\lib\%(Identity)\libmono-native.%(NativeLibraryExtension)')" - /> - <_InstallUnstrippedRuntimeOutput - Include="@(_MonoRuntime->'$(_MSBuildDir)\lib\%(Identity)\%(OutputRuntimeFilename).d.%(NativeLibraryExtension)')" - /> - <_InstallUnstrippedRuntimeOutput - Condition=" 'host-Darwin' == '%(Identity)' " - Include="@(_MonoRuntime->'$(_MSBuildDir)\lib\%(Identity)\libmono-native.d.%(NativeLibraryExtension)')" - /> - <_InstallUnstrippedRuntimeOutput - Condition=" 'host-Darwin' != '%(Identity)' And 'host-mxe-Win32' != '%(Identity)' And 'host-mxe-Win64' != '%(Identity)'" - Include="@(_MonoRuntime->'$(_MSBuildDir)\lib\%(Identity)\libmono-native.d.%(NativeLibraryExtension)')" - /> - <_RuntimeBinarySource - Condition=" 'host-$(HostOS)' == '%(Identity)' " - Include="@(_MonoRuntime->'$(MonoSourceFullPath)\sdks\out\android-%(Identity)-$(_MonoSdksConfiguration)\bin\mono')" - /> - <_InstallRuntimeBinaryOutput - Condition=" 'host-$(HostOS)' == '%(Identity)' " - Include="@(_MonoRuntime->'$(_MSBuildDir)\$(HostOS)\mono')" - /> - <_CrossRuntimeBinarySource - Include="@(_MonoCrossRuntime->'$(MonoSourceFullPath)\sdks\out\android-%(Identity)-$(_MonoSdksConfiguration)\bin\%(ExePrefix)mono-sgen%(ExeSuffix)')" - /> - <_InstallCrossRuntimeBinaryOutput - Include="@(_MonoCrossRuntime->'$(_MSBuildDir)\%(InstallPath)%(CrossMonoName)%(ExeSuffix)')" - /> - <_InstallUnstrippedCrossRuntimeBinaryOutput - Include="@(_MonoCrossRuntime->'$(_MSBuildDir)\%(InstallPath)%(CrossMonoName).d%(ExeSuffix)')" - /> - <_ProfilerSource - Condition=" '%(_MonoRuntime.OutputProfilerFilename)' != '' " - Include="@(_MonoRuntime->'$(MonoSourceFullPath)\sdks\out\android-%(Identity)-$(_MonoSdksConfiguration)\lib\%(NativeLibraryPrefix)%(OutputProfilerFilename).%(NativeLibraryExtension)')" - /> - <_ProfilerSource - Condition=" '%(_MonoRuntime.OutputAotProfilerFilename)' != '' " - Include="@(_MonoRuntime->'$(MonoSourceFullPath)\sdks\out\android-%(Identity)-$(_MonoSdksConfiguration)\lib\%(NativeLibraryPrefix)%(OutputAotProfilerFilename).%(NativeLibraryExtension)')" - /> - <_InstallProfilerOutput - Condition=" '%(_MonoRuntime.OutputProfilerFilename)' != '' " - Include="@(_MonoRuntime->'$(_MSBuildDir)\lib\%(Identity)\%(OutputProfilerFilename).%(NativeLibraryExtension)')" - /> - <_InstallProfilerOutput - Condition=" '%(_MonoRuntime.OutputAotProfilerFilename)' != '' " - Include="@(_MonoRuntime->'$(_MSBuildDir)\lib\%(Identity)\%(OutputAotProfilerFilename).%(NativeLibraryExtension)')" - /> - <_InstallUnstrippedProfilerOutput - Condition=" '%(_MonoRuntime.OutputProfilerFilename)' != '' " - Include="@(_MonoRuntime->'$(_MSBuildDir)\lib\%(Identity)\%(OutputProfilerFilename).d.%(NativeLibraryExtension)')" - /> - <_InstallUnstrippedProfilerOutput - Condition=" '%(_MonoRuntime.OutputAotProfilerFilename)' != '' " - Include="@(_MonoRuntime->'$(_MSBuildDir)\lib\%(Identity)\%(OutputAotProfilerFilename).d.%(NativeLibraryExtension)')" - /> - <_MonoBtlsSource - Condition=" '%(_MonoRuntime.OutputMonoBtlsFilename)' != '' " - Include="@(_MonoRuntime->'$(MonoSourceFullPath)\sdks\out\android-%(Identity)-$(_MonoSdksConfiguration)\lib\%(NativeLibraryPrefix)%(OutputMonoBtlsFilename).%(NativeLibraryExtension)')" - /> - <_InstallMonoBtlsOutput - Condition=" '%(_MonoRuntime.OutputMonoBtlsFilename)' != '' " - Include="@(_MonoRuntime->'$(_MSBuildDir)\lib\%(Identity)\%(OutputMonoBtlsFilename).%(NativeLibraryExtension)')" - /> - <_InstallUnstrippedMonoBtlsOutput - Condition=" '%(_MonoRuntime.OutputMonoBtlsFilename)' != '' " - Include="@(_MonoRuntime->'$(_MSBuildDir)\lib\%(Identity)\%(OutputMonoBtlsFilename).d.%(NativeLibraryExtension)')" - /> - <_MonoPosixHelperSource - Condition=" '%(_MonoRuntime.OutputMonoPosixHelperFilename)' != '' " - Include="@(_MonoRuntime->'$(MonoSourceFullPath)\sdks\out\android-%(Identity)-$(_MonoSdksConfiguration)\lib\%(NativeLibraryPrefix)%(OutputMonoPosixHelperFilename).%(NativeLibraryExtension)')" - /> - <_InstallMonoPosixHelperOutput - Condition=" '%(_MonoRuntime.OutputMonoPosixHelperFilename)' != '' " - Include="@(_MonoRuntime->'$(_MSBuildDir)\lib\%(Identity)\%(OutputMonoPosixHelperFilename).%(NativeLibraryExtension)')" - /> - <_InstallUnstrippedMonoPosixHelperOutput - Condition=" '%(_MonoRuntime.OutputMonoPosixHelperFilename)' != '' " - Include="@(_MonoRuntime->'$(_MSBuildDir)\lib\%(Identity)\%(OutputMonoPosixHelperFilename).d.%(NativeLibraryExtension)')" - /> - <_RuntimeEglibHeaderSource - Include="@(_MonoRuntime->'$(MonoSourceFullPath)\sdks\out\android-%(Identity)-$(_MonoSdksConfiguration)\share\mono-2.0\mono\eglib\eglib-config.h')" - /> - <_RuntimeEglibHeaderOutput - Include="@(_MonoRuntime->'$(_OutputIncludeDir)%(Identity)\eglib\eglib-config.h')" - /> - <_MonoConstsSource - Include="$(_MonoProfileDir)\Consts.cs" - /> - <_MonoConstsOutput - Include="$(_OutputIncludeDir)Consts.cs" - /> - - - <_BclTestAssemblyReference Include="@(MonoTestAssembly)" Condition="'%(MonoTestAssembly.TestType)' == 'reference'" /> - <_BclTestAssembly Include="@(MonoTestAssembly)" Condition="'%(MonoTestAssembly.TestType)' == '' Or '%(MonoTestAssembly.TestType)' == 'xunit' " /> - <_BclTestAssemblySource Include="@(_BclTestAssemblyReference->'$(_MonoProfileDir)\%(Identity)')" /> - <_BclTestAssemblySource Include="@(_BclTestAssembly->'$(_MonoProfileDir)\tests\%(Identity)')" /> - <_BclTestAssemblySymbols - Include="@(_BclTestAssemblyReference->'$(_MonoProfileDir)\%(Filename).pdb')" - Exclude="@(_BclExcludeDebugSymbols->'$(_MonoProfileDir)\%(Filename).pdb')" - /> - <_BclTestAssemblySymbols - Include="@(_BclTestAssembly->'$(_MonoProfileDir)\tests\%(Filename).pdb')" - Exclude="@(_BclExcludeDebugSymbols->'$(_MonoProfileDir)\tests\%(Filename).pdb')" - /> - <_BclTestAssemblySource Include="@(_BclTestAssemblySymbols)" /> - <_BclTestAssemblySource Include="@(MonoTestRunner->'$(_MonoProfileDir)\%(Identity)')" /> - <_BclTestAssemblySource Include="@(MonoTestRunner->'$(_MonoProfileDir)\%(Filename).pdb')" /> - <_BclTestSatelliteAssemblySource Include="@(MonoTestSatelliteAssembly->'$(_MonoProfileDir)\tests\%(Identity)')" /> - <_BclTestSatelliteAssemblyDest Include="@(MonoTestSatelliteAssembly->'$(XAInstallPrefix)\..\..\bcl-tests\%(Identity)')" /> - - - - - - - - <_UnzipTempPath>$(MonoSourceFullPath)\sdks\.out.$([System.IO.Path]::GetRandomFileName()) - <_HostBclFilesDir>$(MonoSourceFullPath)\sdks\out\android-bcl\monodroid - <_WindowsBclFilesDir>$(_WindowsMonoArchiveBclDir)\android-bcl\monodroid - - - - - - - - <_ExtractedAndroidMainBclFiles Include="$(_HostBclFilesDir)\*" Exclude="$(_HostBclFilesDir)\*.pdb;$(_HostBclFilesDir)\*.unsafe.dll.tmp;$(_HostBclFilesDir)\Consts.cs" /> - <_ExtractedAndroidFacadeBclFiles Include="$(_HostBclFilesDir)\Facades\*" Exclude="$(_HostBclFilesDir)\Facades\*.pdb;$(_HostBclFilesDir)\Facades\.stamp" /> - - - - - - - - <_UnzipTempPath>$(_MSBuildDir)\.windows-bcl.$([System.IO.Path]::GetRandomFileName()) - - - - - <_ExtractedWindowsAndroidMainBclFiles Include="$(_WindowsBclFilesDir)\*" Exclude="$(_WindowsBclFilesDir)\*.pdb;$(_WindowsBclFilesDir)\*.unsafe.dll.tmp;$(_WindowsBclFilesDir)\Consts.cs" /> - <_ExtractedWindowsAndroidFacadeBclFiles Include="$(_WindowsBclFilesDir)\Facades\*" Exclude="$(_WindowsBclFilesDir)\Facades\*.pdb;$(_WindowsBclFilesDir)\Facades\.stamp" /> - - - - - - - - - - - - - - - - - - - <_ZipTestContent Include="$(MonoSourceFullPath)\mcs\class\System.IO.Compression.FileSystem\foo\**\*.*" /> - <_ZipTestContentDest Include="@(_ZipTestContent->'$(XAInstallPrefix)\..\..\bcl-tests\foo\%(RecursiveDir)%(Filename)%(Extension)')" /> - <_BclTestContent Include="@(_ZipTestContent)" /> - <_BclTestContentDest Include="@(_ZipTestContentDest)" /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <_MonoCilStripSource Include="$(_MonoProfileToolsDir)\mono-cil-strip.exe" /> - <_MonoCilStripDest Include="$(_MSBuildDir)\cil-strip.exe" /> - <_MonoCilStripSource - Condition=" '$(_DebugFileExt)' == '.mdb' " - Include="$(_MonoProfileToolsDir)\mono-cil-strip.exe.mdb" - /> - <_MonoCilStripDest - Condition=" '$(_DebugFileExt)' == '.mdb' " - Include="$(_MonoProfileToolsDir)\cil-strip.exe.mdb" - /> - <_MonoCilStripSource - Condition=" '$(_DebugFileExt)' == '.pdb' " - Include="$(_MonoProfileToolsDir)\mono-cil-strip.pdb" - /> - <_MonoCilStripDest - Condition=" '$(_DebugFileExt)' == '.pdb' " - Include="$(_MSBuildDir)\cil-strip.pdb" - /> - - - - - - - - - - - - - - - - <_MonoUtilityExe Include="@(_MonoUtility)"> - $(_MonoProfileToolsDir)\%(Identity) - $(_MSBuildDir)\%(Identity) - - - - - - - - - - <_PackageConfigFiles Include="$(_SourceTopDir)\src\Xamarin.Android.Build.Tasks\packages.config" /> - - - <_Facades Include="$(_MonoProfileDir)\Facades\*.dll" /> - - - - - - <_VersionOverride Include="System.Buffers.dll"> - 4.0.99.0 - - <_VersionOverride Include="System.Memory.dll"> - 4.0.99.0 - - - - - - - - <_BundleHostOS Condition=" '$(HostOS)' == 'Windows' ">Darwin - <_BundleHostOS Condition=" '$(_BundleHostOS)' == '' ">$(HostOS) - - - <_WinExcludeAssembly Include="Mono.Btls.Interface.dll" /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <_Now>$([System.DateTime]::Now.Ticks) - - - - - - - - - <_StampFiles Include="$(MonoSourceFullPath)\sdks\out\.stamp-android-$(_MonoSdksConfiguration)-*-$(HostOS)-build"/> - <_StampFiles Include="$(MonoSourceFullPath)\sdks\out\.stamp-android-$(_MonoSdksConfiguration)-*-$(HostOS)-download"/> - - - - - diff --git a/src/monodroid/monodroid.csproj b/src/monodroid/monodroid.csproj index 6f7f01cb1..7f566dd5e 100644 --- a/src/monodroid/monodroid.csproj +++ b/src/monodroid/monodroid.csproj @@ -21,16 +21,6 @@ - - {8FF78EB6-6FC8-46A7-8A15-EBBA9045C5FA} - android-toolchain - False - - - {C845ECC0-2ED3-498E-8EA8-02EF7AC6E9AD} - dependencies - False -