/// <summary> /// Runs the "flutter --version" command to retrieve the current version of Flutter. /// </summary> public static FlutterVersion GetVersion(bool verbose = false) { CommandResult result = FlutnetShell.RunCommand("flutter --version --no-version-check", Environment.CurrentDirectory, verbose); FlutterVersion version = new FlutterVersion(); int index; using (StringReader reader = new StringReader(result.StandardOutput)) { string versionLine = reader.ReadLine(); string[] parts = versionLine .Replace("Flutter", string.Empty, StringComparison.InvariantCultureIgnoreCase) .Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); if (parts.Length > 0) { version.Version = parts[0].Trim(); } string frameworkRevLine = reader.ReadLine(); index = frameworkRevLine.IndexOf("revision ", StringComparison.InvariantCultureIgnoreCase); if (index != -1) { version.FrameworkRev = frameworkRevLine.Substring(index + 9, 10).Trim(); } string engineRevLine = reader.ReadLine(); index = engineRevLine.IndexOf("revision ", StringComparison.InvariantCultureIgnoreCase); if (index != -1) { version.EngineRev = engineRevLine.Substring(index + 9).Trim(); } } return(version); }
public static int Go(Options options) { if (options.NoAndroid && options.NoIos) { Console.WriteLine("Nothing to do."); return(ReturnCodes.Success); } string targetFolder = !string.IsNullOrEmpty(options.TargetDirectory) ? Path.GetFullPath(options.TargetDirectory) : DefaultTargetDirectory; string targetFolderAndroid = Path.Combine(targetFolder, "Android"); string targetFolderAndroidDebug = Path.Combine(targetFolderAndroid, "debug"); string targetFolderAndroidRelease = Path.Combine(targetFolderAndroid, "release"); string tmpTargetAndroidOfficial = Path.Combine(targetFolderAndroid, "official_repo"); string targetFolderIos = Path.Combine(targetFolder, "iOS"); string targetFolderIosDebug = Path.Combine(targetFolderIos, "debug"); string targetFolderIosRelease = Path.Combine(targetFolderIos, "release"); string gradleCacheFolder = !string.IsNullOrEmpty(options.GradleCacheDirectory) ? Path.GetFullPath(options.GradleCacheDirectory) : DefaultGradleCacheDirectory; string tmpFolder = Path.GetTempPath(); string tmpModuleName = $"module_{Guid.NewGuid().ToString("N").Substring(0, 10)}"; string tmpModulePath = Path.Combine(tmpFolder, tmpModuleName); try { Console.WriteLine("Detecting Flutter version..."); FlutterVersion version = FlutterTools.GetVersion(); Console.WriteLine("Done (current version is {0}).", version.Version); Console.WriteLine("Creating temporary Flutter module..."); DartProject project = FlutterTools.CreateModule(tmpFolder, tmpModuleName); Console.WriteLine("Done."); if (!options.NoAndroid) { Console.WriteLine("Building Android archives..."); FlutterTools.BuildAndroidArchive(tmpModulePath, FlutterModuleBuildConfig.Debug | FlutterModuleBuildConfig.Release); Console.WriteLine("Done."); if (Directory.Exists(targetFolderAndroid)) { Directory.Delete(targetFolderAndroid, true); } Directory.CreateDirectory(targetFolderAndroidDebug); Directory.CreateDirectory(targetFolderAndroidRelease); // NOTE: The JARs / AARs found in the Gradle cache folder must be manipulated // in order for them to be embedded into a Xamarin bindings library Console.WriteLine("Copying Android archives into destination folder..."); Directory.CreateDirectory(tmpTargetAndroidOfficial); DirectoryInfo gradleCacheDir = new DirectoryInfo(gradleCacheFolder); foreach (var gradleCacheArchDir in gradleCacheDir.EnumerateDirectories() .Where(di => !di.Name.Contains("profile", StringComparison.OrdinalIgnoreCase))) { var dir = gradleCacheArchDir.GetDirectories($"*-{version.EngineRev}*")[0]; foreach (var subdir in dir.EnumerateDirectories()) { var files = subdir.GetFiles(); if (files.Length == 0) { continue; } var file = files[0]; if (!string.Equals(file.Extension, ".jar", StringComparison.InvariantCultureIgnoreCase) && !string.Equals(file.Extension, ".aar", StringComparison.InvariantCultureIgnoreCase)) { continue; } file.CopyTo(Path.Combine(tmpTargetAndroidOfficial, file.Name)); } } Console.WriteLine("Done."); Console.WriteLine("Configuring Android archives for Xamarin bindings..."); foreach (string filename in Directory.EnumerateFiles(tmpTargetAndroidOfficial, "*_debug-*.jar")) { FileInfo fi = new FileInfo(filename); if (fi.Name.StartsWith("flutter_embedding")) { fi.CopyTo(Path.Combine(targetFolderAndroidDebug, "flutter_embedding.jar")); } else { int index = fi.Name.IndexOf("_debug-", StringComparison.InvariantCultureIgnoreCase); // https://docs.microsoft.com/en-US/xamarin/android/app-fundamentals/cpu-architectures?tabs=windows // arm64_v8a must become arm64-v8a // armeabi_v7a must become armeabi-v7a // x86_64 must not change string arch = fi.Name.Substring(0, index).Replace("_v", "-v"); string libFolder = Path.Combine(targetFolderAndroidDebug, "lib", arch); Directory.CreateDirectory(libFolder); using (ZipInputStream stream = new ZipInputStream(fi.OpenRead())) { ZipEntry entry; while ((entry = stream.GetNextEntry()) != null) { string entryFilename = Path.GetFileName(entry.Name); if (string.IsNullOrEmpty(entryFilename) || !string.Equals(Path.GetExtension(entryFilename), ".so", StringComparison.InvariantCultureIgnoreCase)) { continue; } using (FileStream streamWriter = File.Create(Path.Combine(libFolder, entryFilename))) { int size; byte[] data = new byte[2048]; while (true) { size = stream.Read(data, 0, data.Length); if (size > 0) { streamWriter.Write(data, 0, size); } else { break; } } } } } } } foreach (string filename in Directory.EnumerateFiles(tmpTargetAndroidOfficial, "*_release-*.jar")) { FileInfo fi = new FileInfo(filename); if (fi.Name.StartsWith("flutter_embedding")) { fi.CopyTo(Path.Combine(targetFolderAndroidRelease, "flutter_embedding.jar")); } else { int index = fi.Name.IndexOf("_release-", StringComparison.InvariantCultureIgnoreCase); // https://docs.microsoft.com/en-US/xamarin/android/app-fundamentals/cpu-architectures?tabs=windows // arm64_v8a must become arm64-v8a // armeabi_v7a must become armeabi-v7a // x86_64 must not change string arch = fi.Name.Substring(0, index).Replace("_v", "-v"); string libFolder = Path.Combine(targetFolderAndroidRelease, "lib", arch); Directory.CreateDirectory(libFolder); using (ZipInputStream stream = new ZipInputStream(fi.OpenRead())) { ZipEntry entry; while ((entry = stream.GetNextEntry()) != null) { string entryFilename = Path.GetFileName(entry.Name); if (string.IsNullOrEmpty(entryFilename) || !string.Equals(Path.GetExtension(entryFilename), ".so", StringComparison.InvariantCultureIgnoreCase)) { continue; } using (FileStream streamWriter = File.Create(Path.Combine(libFolder, entryFilename))) { int size; byte[] data = new byte[2048]; while (true) { size = stream.Read(data, 0, data.Length); if (size > 0) { streamWriter.Write(data, 0, size); } else { break; } } } } } } } Console.WriteLine("Done."); } if (!options.NoIos && OperatingSystem.IsMacOS()) { Console.WriteLine("Building iOS frameworks..."); FlutterTools.BuildIosFramework(tmpModulePath, FlutterModuleBuildConfig.Debug | FlutterModuleBuildConfig.Release); Console.WriteLine("Done."); if (Directory.Exists(targetFolderIos)) { Directory.Delete(targetFolderIos, true); } Directory.CreateDirectory(targetFolderIosDebug); Directory.CreateDirectory(targetFolderIosRelease); Console.WriteLine("Copying iOS frameworks into destination folder..."); bool xcframework = !version.Version.StartsWith("1"); string appFrameworkName = xcframework ? "App.xcframework" : "App.framework"; string flutterFrameworkName = xcframework ? "Flutter.xcframework" : "Flutter.framework"; string appFrameworkDebug = xcframework ? project.GetIosXCFrameworkPath(FlutterModuleBuildConfig.Debug) : project.GetIosFrameworkPath(FlutterModuleBuildConfig.Debug); string flutterFrameworkDebug = appFrameworkDebug.Replace(appFrameworkName, flutterFrameworkName, StringComparison.InvariantCultureIgnoreCase); DirectoryInfo flutterFrameworkDebugDir = new DirectoryInfo(flutterFrameworkDebug); DirectoryInfo flutterFrameworkOutputDebugDir = new DirectoryInfo(Path.Combine(targetFolderIosDebug, flutterFrameworkName)); flutterFrameworkOutputDebugDir.Create(); CopyAll(flutterFrameworkDebugDir, flutterFrameworkOutputDebugDir); string appFrameworkRelease = xcframework ? project.GetIosXCFrameworkPath(FlutterModuleBuildConfig.Release) : project.GetIosFrameworkPath(FlutterModuleBuildConfig.Release); string flutterFrameworkRelease = appFrameworkRelease.Replace(appFrameworkName, flutterFrameworkName, StringComparison.InvariantCultureIgnoreCase); DirectoryInfo flutterFrameworkReleaseDir = new DirectoryInfo(flutterFrameworkRelease); DirectoryInfo flutterFrameworkOutputReleaseDir = new DirectoryInfo(Path.Combine(targetFolderIosRelease, flutterFrameworkName)); flutterFrameworkOutputReleaseDir.Create(); CopyAll(flutterFrameworkReleaseDir, flutterFrameworkOutputReleaseDir); Console.WriteLine("Done."); } return(ReturnCodes.Success); } catch (Exception ex) { Console.WriteLine(ex.Message); return(ReturnCodes.CommandExecutionError); } finally { Console.WriteLine("Clearing temporary files..."); if (Directory.Exists(tmpModulePath)) { Directory.Delete(tmpModulePath, true); } if (Directory.Exists(tmpTargetAndroidOfficial)) { Directory.Delete(tmpTargetAndroidOfficial, true); } Console.WriteLine("Done."); } }