| // Copyright 2014 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'base/context.dart'; |
| import 'base/utils.dart'; |
| import 'globals.dart' as globals; |
| |
| /// Information about a build to be performed or used. |
| class BuildInfo { |
| const BuildInfo( |
| this.mode, |
| this.flavor, { |
| this.trackWidgetCreation = false, |
| this.extraFrontEndOptions, |
| this.extraGenSnapshotOptions, |
| this.fileSystemRoots, |
| this.fileSystemScheme, |
| this.buildNumber, |
| this.buildName, |
| }); |
| |
| final BuildMode mode; |
| |
| /// Represents a custom Android product flavor or an Xcode scheme, null for |
| /// using the default. |
| /// |
| /// If not null, the Gradle build task will be `assembleFlavorMode` (e.g. |
| /// `assemblePaidRelease`), and the Xcode build configuration will be |
| /// Mode-Flavor (e.g. Release-Paid). |
| final String flavor; |
| |
| final List<String> fileSystemRoots; |
| final String fileSystemScheme; |
| |
| /// Whether the build should track widget creation locations. |
| final bool trackWidgetCreation; |
| |
| /// Extra command-line options for front-end. |
| final String extraFrontEndOptions; |
| |
| /// Extra command-line options for gen_snapshot. |
| final String extraGenSnapshotOptions; |
| |
| /// Internal version number (not displayed to users). |
| /// Each build must have a unique number to differentiate it from previous builds. |
| /// It is used to determine whether one build is more recent than another, with higher numbers indicating more recent build. |
| /// On Android it is used as versionCode. |
| /// On Xcode builds it is used as CFBundleVersion. |
| final String buildNumber; |
| |
| /// A "x.y.z" string used as the version number shown to users. |
| /// For each new version of your app, you will provide a version number to differentiate it from previous versions. |
| /// On Android it is used as versionName. |
| /// On Xcode builds it is used as CFBundleShortVersionString, |
| final String buildName; |
| |
| static const BuildInfo debug = BuildInfo(BuildMode.debug, null); |
| static const BuildInfo profile = BuildInfo(BuildMode.profile, null); |
| static const BuildInfo jitRelease = BuildInfo(BuildMode.jitRelease, null); |
| static const BuildInfo release = BuildInfo(BuildMode.release, null); |
| |
| /// Returns whether a debug build is requested. |
| /// |
| /// Exactly one of [isDebug], [isProfile], or [isRelease] is true. |
| bool get isDebug => mode == BuildMode.debug; |
| |
| /// Returns whether a profile build is requested. |
| /// |
| /// Exactly one of [isDebug], [isProfile], [isJitRelease], |
| /// or [isRelease] is true. |
| bool get isProfile => mode == BuildMode.profile; |
| |
| /// Returns whether a release build is requested. |
| /// |
| /// Exactly one of [isDebug], [isProfile], [isJitRelease], |
| /// or [isRelease] is true. |
| bool get isRelease => mode == BuildMode.release; |
| |
| /// Returns whether a JIT release build is requested. |
| /// |
| /// Exactly one of [isDebug], [isProfile], [isJitRelease], |
| /// or [isRelease] is true. |
| bool get isJitRelease => mode == BuildMode.jitRelease; |
| |
| bool get usesAot => isAotBuildMode(mode); |
| bool get supportsEmulator => isEmulatorBuildMode(mode); |
| bool get supportsSimulator => isEmulatorBuildMode(mode); |
| String get modeName => getModeName(mode); |
| String get friendlyModeName => getFriendlyModeName(mode); |
| } |
| |
| /// Information about an Android build to be performed or used. |
| class AndroidBuildInfo { |
| const AndroidBuildInfo( |
| this.buildInfo, { |
| this.targetArchs = const <AndroidArch>[ |
| AndroidArch.armeabi_v7a, |
| AndroidArch.arm64_v8a, |
| AndroidArch.x86_64, |
| ], |
| this.splitPerAbi = false, |
| this.shrink = false, |
| this.fastStart = false, |
| }); |
| |
| // The build info containing the mode and flavor. |
| final BuildInfo buildInfo; |
| |
| /// Whether to split the shared library per ABI. |
| /// |
| /// When this is false, multiple ABIs will be contained within one primary |
| /// build artifact. When this is true, multiple build artifacts (one per ABI) |
| /// will be produced. |
| final bool splitPerAbi; |
| |
| /// Whether to enable code shrinking on release mode. |
| final bool shrink; |
| |
| /// The target platforms for the build. |
| final Iterable<AndroidArch> targetArchs; |
| |
| /// Whether to bootstrap an empty application. |
| final bool fastStart; |
| } |
| |
| /// A summary of the compilation strategy used for Dart. |
| class BuildMode { |
| const BuildMode._(this.name); |
| |
| factory BuildMode.fromName(String value) { |
| switch (value) { |
| case 'debug': |
| return BuildMode.debug; |
| case 'profile': |
| return BuildMode.profile; |
| case 'release': |
| return BuildMode.release; |
| case 'jit_release': |
| return BuildMode.jitRelease; |
| } |
| throw ArgumentError('$value is not a supported build mode'); |
| } |
| |
| /// Built in JIT mode with no optimizations, enabled asserts, and an observatory. |
| static const BuildMode debug = BuildMode._('debug'); |
| |
| /// Built in AOT mode with some optimizations and an observatory. |
| static const BuildMode profile = BuildMode._('profile'); |
| |
| /// Built in AOT mode with all optimizations and no observatory. |
| static const BuildMode release = BuildMode._('release'); |
| |
| /// Built in JIT mode with all optimizations and no observatory. |
| static const BuildMode jitRelease = BuildMode._('jit_release'); |
| |
| static const List<BuildMode> values = <BuildMode>[ |
| debug, |
| profile, |
| release, |
| jitRelease, |
| ]; |
| static const Set<BuildMode> releaseModes = <BuildMode>{ |
| release, |
| jitRelease, |
| }; |
| static const Set<BuildMode> jitModes = <BuildMode>{ |
| debug, |
| jitRelease, |
| }; |
| |
| /// Whether this mode is considered release. |
| /// |
| /// Useful for determining whether we should enable/disable asserts or |
| /// other development features. |
| bool get isRelease => releaseModes.contains(this); |
| |
| /// Whether this mode is using the JIT runtime. |
| bool get isJit => jitModes.contains(this); |
| |
| /// Whether this mode is using the precompiled runtime. |
| bool get isPrecompiled => !isJit; |
| |
| /// The name for this build mode. |
| final String name; |
| |
| @override |
| String toString() => name; |
| } |
| |
| /// Return the name for the build mode, or "any" if null. |
| String getNameForBuildMode(BuildMode buildMode) { |
| return buildMode.name; |
| } |
| |
| /// Returns the [BuildMode] for a particular `name`. |
| BuildMode getBuildModeForName(String name) { |
| return BuildMode.fromName(name); |
| } |
| |
| String validatedBuildNumberForPlatform(TargetPlatform targetPlatform, String buildNumber) { |
| if (buildNumber == null) { |
| return null; |
| } |
| if (targetPlatform == TargetPlatform.ios || |
| targetPlatform == TargetPlatform.darwin_x64) { |
| // See CFBundleVersion at https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html |
| final RegExp disallowed = RegExp(r'[^\d\.]'); |
| String tmpBuildNumber = buildNumber.replaceAll(disallowed, ''); |
| if (tmpBuildNumber.isEmpty) { |
| return null; |
| } |
| final List<String> segments = tmpBuildNumber |
| .split('.') |
| .where((String segment) => segment.isNotEmpty) |
| .toList(); |
| if (segments.isEmpty) { |
| segments.add('0'); |
| } |
| tmpBuildNumber = segments.join('.'); |
| if (tmpBuildNumber != buildNumber) { |
| globals.printTrace('Invalid build-number: $buildNumber for iOS/macOS, overridden by $tmpBuildNumber.\n' |
| 'See CFBundleVersion at https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html'); |
| } |
| return tmpBuildNumber; |
| } |
| if (targetPlatform == TargetPlatform.android_arm || |
| targetPlatform == TargetPlatform.android_arm64 || |
| targetPlatform == TargetPlatform.android_x64 || |
| targetPlatform == TargetPlatform.android_x86) { |
| // See versionCode at https://developer.android.com/studio/publish/versioning |
| final RegExp disallowed = RegExp(r'[^\d]'); |
| String tmpBuildNumberStr = buildNumber.replaceAll(disallowed, ''); |
| int tmpBuildNumberInt = int.tryParse(tmpBuildNumberStr) ?? 0; |
| if (tmpBuildNumberInt < 1) { |
| tmpBuildNumberInt = 1; |
| } |
| tmpBuildNumberStr = tmpBuildNumberInt.toString(); |
| if (tmpBuildNumberStr != buildNumber) { |
| globals.printTrace('Invalid build-number: $buildNumber for Android, overridden by $tmpBuildNumberStr.\n' |
| 'See versionCode at https://developer.android.com/studio/publish/versioning'); |
| } |
| return tmpBuildNumberStr; |
| } |
| return buildNumber; |
| } |
| |
| String validatedBuildNameForPlatform(TargetPlatform targetPlatform, String buildName) { |
| if (buildName == null) { |
| return null; |
| } |
| if (targetPlatform == TargetPlatform.ios || |
| targetPlatform == TargetPlatform.darwin_x64) { |
| // See CFBundleShortVersionString at https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html |
| final RegExp disallowed = RegExp(r'[^\d\.]'); |
| String tmpBuildName = buildName.replaceAll(disallowed, ''); |
| if (tmpBuildName.isEmpty) { |
| return null; |
| } |
| final List<String> segments = tmpBuildName |
| .split('.') |
| .where((String segment) => segment.isNotEmpty) |
| .toList(); |
| while (segments.length < 3) { |
| segments.add('0'); |
| } |
| tmpBuildName = segments.join('.'); |
| if (tmpBuildName != buildName) { |
| globals.printTrace('Invalid build-name: $buildName for iOS/macOS, overridden by $tmpBuildName.\n' |
| 'See CFBundleShortVersionString at https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html'); |
| } |
| return tmpBuildName; |
| } |
| if (targetPlatform == TargetPlatform.android || |
| targetPlatform == TargetPlatform.android_arm || |
| targetPlatform == TargetPlatform.android_arm64 || |
| targetPlatform == TargetPlatform.android_x64 || |
| targetPlatform == TargetPlatform.android_x86) { |
| // See versionName at https://developer.android.com/studio/publish/versioning |
| return buildName; |
| } |
| return buildName; |
| } |
| |
| String getModeName(BuildMode mode) => getEnumName(mode); |
| |
| String getFriendlyModeName(BuildMode mode) { |
| return snakeCase(getModeName(mode)).replaceAll('_', ' '); |
| } |
| |
| // Returns true if the selected build mode uses ahead-of-time compilation. |
| bool isAotBuildMode(BuildMode mode) { |
| return mode == BuildMode.profile || mode == BuildMode.release; |
| } |
| |
| // Returns true if the given build mode can be used on emulators / simulators. |
| bool isEmulatorBuildMode(BuildMode mode) { |
| return mode == BuildMode.debug; |
| } |
| |
| enum HostPlatform { |
| darwin_x64, |
| linux_x64, |
| windows_x64, |
| } |
| |
| String getNameForHostPlatform(HostPlatform platform) { |
| switch (platform) { |
| case HostPlatform.darwin_x64: |
| return 'darwin-x64'; |
| case HostPlatform.linux_x64: |
| return 'linux-x64'; |
| case HostPlatform.windows_x64: |
| return 'windows-x64'; |
| } |
| assert(false); |
| return null; |
| } |
| |
| enum TargetPlatform { |
| android, |
| ios, |
| darwin_x64, |
| linux_x64, |
| windows_x64, |
| fuchsia_arm64, |
| fuchsia_x64, |
| tester, |
| web_javascript, |
| // The arch specific android target platforms are soft-depreacted. |
| // Instead of using TargetPlatform as a combination arch + platform |
| // the code will be updated to carry arch information in [DarwinArch] |
| // and [AndroidArch]. |
| android_arm, |
| android_arm64, |
| android_x64, |
| android_x86, |
| } |
| |
| /// iOS and macOS target device architecture. |
| // |
| // TODO(cbracken): split TargetPlatform.ios into ios_armv7, ios_arm64. |
| enum DarwinArch { |
| armv7, |
| arm64, |
| x86_64, |
| } |
| |
| // TODO(jonahwilliams): replace all android TargetPlatform usage with AndroidArch. |
| enum AndroidArch { |
| armeabi_v7a, |
| arm64_v8a, |
| x86, |
| x86_64, |
| } |
| |
| /// The default set of iOS device architectures to build for. |
| const List<DarwinArch> defaultIOSArchs = <DarwinArch>[ |
| DarwinArch.arm64, |
| ]; |
| |
| String getNameForDarwinArch(DarwinArch arch) { |
| switch (arch) { |
| case DarwinArch.armv7: |
| return 'armv7'; |
| case DarwinArch.arm64: |
| return 'arm64'; |
| case DarwinArch.x86_64: |
| return 'x86_64'; |
| } |
| assert(false); |
| return null; |
| } |
| |
| DarwinArch getIOSArchForName(String arch) { |
| switch (arch) { |
| case 'armv7': |
| return DarwinArch.armv7; |
| case 'arm64': |
| return DarwinArch.arm64; |
| } |
| assert(false); |
| return null; |
| } |
| |
| String getNameForTargetPlatform(TargetPlatform platform) { |
| switch (platform) { |
| case TargetPlatform.android_arm: |
| return 'android-arm'; |
| case TargetPlatform.android_arm64: |
| return 'android-arm64'; |
| case TargetPlatform.android_x64: |
| return 'android-x64'; |
| case TargetPlatform.android_x86: |
| return 'android-x86'; |
| case TargetPlatform.ios: |
| return 'ios'; |
| case TargetPlatform.darwin_x64: |
| return 'darwin-x64'; |
| case TargetPlatform.linux_x64: |
| return 'linux-x64'; |
| case TargetPlatform.windows_x64: |
| return 'windows-x64'; |
| case TargetPlatform.fuchsia_arm64: |
| return 'fuchsia-arm64'; |
| case TargetPlatform.fuchsia_x64: |
| return 'fuchsia-x64'; |
| case TargetPlatform.tester: |
| return 'flutter-tester'; |
| case TargetPlatform.web_javascript: |
| return 'web-javascript'; |
| case TargetPlatform.android: |
| return 'android'; |
| } |
| assert(false); |
| return null; |
| } |
| |
| TargetPlatform getTargetPlatformForName(String platform) { |
| switch (platform) { |
| case 'android': |
| return TargetPlatform.android; |
| case 'android-arm': |
| return TargetPlatform.android_arm; |
| case 'android-arm64': |
| return TargetPlatform.android_arm64; |
| case 'android-x64': |
| return TargetPlatform.android_x64; |
| case 'android-x86': |
| return TargetPlatform.android_x86; |
| case 'fuchsia-arm64': |
| return TargetPlatform.fuchsia_arm64; |
| case 'fuchsia-x64': |
| return TargetPlatform.fuchsia_x64; |
| case 'ios': |
| return TargetPlatform.ios; |
| case 'darwin-x64': |
| return TargetPlatform.darwin_x64; |
| case 'linux-x64': |
| return TargetPlatform.linux_x64; |
| case 'windows-x64': |
| return TargetPlatform.windows_x64; |
| case 'web-javascript': |
| return TargetPlatform.web_javascript; |
| } |
| assert(platform != null); |
| return null; |
| } |
| |
| AndroidArch getAndroidArchForName(String platform) { |
| switch (platform) { |
| case 'android-arm': |
| return AndroidArch.armeabi_v7a; |
| case 'android-arm64': |
| return AndroidArch.arm64_v8a; |
| case 'android-x64': |
| return AndroidArch.x86_64; |
| case 'android-x86': |
| return AndroidArch.x86; |
| } |
| assert(false); |
| return null; |
| } |
| |
| String getNameForAndroidArch(AndroidArch arch) { |
| switch (arch) { |
| case AndroidArch.armeabi_v7a: |
| return 'armeabi-v7a'; |
| case AndroidArch.arm64_v8a: |
| return 'arm64-v8a'; |
| case AndroidArch.x86_64: |
| return 'x86_64'; |
| case AndroidArch.x86: |
| return 'x86'; |
| } |
| assert(false); |
| return null; |
| } |
| |
| String getPlatformNameForAndroidArch(AndroidArch arch) { |
| switch (arch) { |
| case AndroidArch.armeabi_v7a: |
| return 'android-arm'; |
| case AndroidArch.arm64_v8a: |
| return 'android-arm64'; |
| case AndroidArch.x86_64: |
| return 'android-x64'; |
| case AndroidArch.x86: |
| return 'android-x86'; |
| } |
| assert(false); |
| return null; |
| } |
| |
| String fuchsiaArchForTargetPlatform(TargetPlatform targetPlatform) { |
| switch (targetPlatform) { |
| case TargetPlatform.fuchsia_arm64: |
| return 'arm64'; |
| case TargetPlatform.fuchsia_x64: |
| return 'x64'; |
| default: |
| assert(false); |
| return null; |
| } |
| } |
| |
| HostPlatform getCurrentHostPlatform() { |
| if (globals.platform.isMacOS) { |
| return HostPlatform.darwin_x64; |
| } |
| if (globals.platform.isLinux) { |
| return HostPlatform.linux_x64; |
| } |
| if (globals.platform.isWindows) { |
| return HostPlatform.windows_x64; |
| } |
| |
| globals.printError('Unsupported host platform, defaulting to Linux'); |
| |
| return HostPlatform.linux_x64; |
| } |
| |
| /// Returns the top-level build output directory. |
| String getBuildDirectory() { |
| // TODO(johnmccutchan): Stop calling this function as part of setting |
| // up command line argument processing. |
| if (context == null || globals.config == null) { |
| return 'build'; |
| } |
| |
| final String buildDir = globals.config.getValue('build-dir') as String ?? 'build'; |
| if (globals.fs.path.isAbsolute(buildDir)) { |
| throw Exception( |
| 'build-dir config setting in ${globals.config.configPath} must be relative'); |
| } |
| return buildDir; |
| } |
| |
| /// Returns the Android build output directory. |
| String getAndroidBuildDirectory() { |
| // TODO(cbracken): move to android subdir. |
| return getBuildDirectory(); |
| } |
| |
| /// Returns the AOT build output directory. |
| String getAotBuildDirectory() { |
| return globals.fs.path.join(getBuildDirectory(), 'aot'); |
| } |
| |
| /// Returns the asset build output directory. |
| String getAssetBuildDirectory() { |
| return globals.fs.path.join(getBuildDirectory(), 'flutter_assets'); |
| } |
| |
| /// Returns the iOS build output directory. |
| String getIosBuildDirectory() { |
| return globals.fs.path.join(getBuildDirectory(), 'ios'); |
| } |
| |
| /// Returns the macOS build output directory. |
| String getMacOSBuildDirectory() { |
| return globals.fs.path.join(getBuildDirectory(), 'macos'); |
| } |
| |
| /// Returns the web build output directory. |
| String getWebBuildDirectory() { |
| return globals.fs.path.join(getBuildDirectory(), 'web'); |
| } |
| |
| /// Returns the Linux build output directory. |
| String getLinuxBuildDirectory() { |
| return globals.fs.path.join(getBuildDirectory(), 'linux'); |
| } |
| |
| /// Returns the Windows build output directory. |
| String getWindowsBuildDirectory() { |
| return globals.fs.path.join(getBuildDirectory(), 'windows'); |
| } |
| |
| /// Returns the Fuchsia build output directory. |
| String getFuchsiaBuildDirectory() { |
| return globals.fs.path.join(getBuildDirectory(), 'fuchsia'); |
| } |