Refactor build-number/build-name logic. (#27743)
This PR aims at several things:
1. Use pub_semver to check a version in pubspec.yaml meets the requirements specified in https://semver.org/.
2. Don't limit build-number/build-name as a fixed format. Instead, validate it according to the target(ios/android).
3. Make sure that build-number/build-name are always validated no matter it's specified by the `flutter command` or version in pubspec.yaml.
Fixes #27589
diff --git a/packages/flutter_tools/lib/src/build_info.dart b/packages/flutter_tools/lib/src/build_info.dart
index 0e63342..cd8ce05 100644
--- a/packages/flutter_tools/lib/src/build_info.dart
+++ b/packages/flutter_tools/lib/src/build_info.dart
@@ -83,7 +83,7 @@
/// 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 int buildNumber;
+ 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.
@@ -141,6 +141,83 @@
dynamicRelease
}
+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, '');
+ final List<String> segments = tmpBuildNumber
+ .split('.')
+ .where((String segment) => segment.isNotEmpty)
+ .toList();
+ if (segments.isEmpty) {
+ segments.add('0');
+ }
+ tmpBuildNumber = segments.join('.');
+ if (tmpBuildNumber != buildNumber) {
+ 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) {
+ 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, '');
+ 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) {
+ 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_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) {