Skip to content

Commit

Permalink
[Windows] Add version info migration (#123414)
Browse files Browse the repository at this point in the history
[Windows] Add version info migration
  • Loading branch information
loic-sharma committed Mar 29, 2023
1 parent f440649 commit 9f2ac97
Show file tree
Hide file tree
Showing 4 changed files with 485 additions and 0 deletions.
12 changes: 12 additions & 0 deletions packages/flutter_tools/lib/src/cmake_project.dart
Expand Up @@ -61,6 +61,13 @@ class WindowsProject extends FlutterProjectPlatform implements CmakeBasedProject
@override
File get generatedPluginCmakeFile => managedDirectory.childFile('generated_plugins.cmake');

/// The native entrypoint's CMake specification.
File get runnerCmakeFile => runnerDirectory.childFile('CMakeLists.txt');

/// The native entrypoint's resource file. Used to configure things
/// like the application icon, name, and version.
File get runnerResourceFile => runnerDirectory.childFile('Runner.rc');

@override
Directory get pluginSymlinkDirectory => ephemeralDirectory.childDirectory('.plugin_symlinks');

Expand All @@ -76,6 +83,11 @@ class WindowsProject extends FlutterProjectPlatform implements CmakeBasedProject
/// checked in should live here.
Directory get ephemeralDirectory => managedDirectory.childDirectory('ephemeral');

/// The directory in the project that is owned by the app. As much as
/// possible, Flutter tooling should not edit files in this directory after
/// initial project creation.
Directory get runnerDirectory => _editableDirectory.childDirectory('runner');

Future<void> ensureReadyForPlatformSpecificTooling() async {}
}

Expand Down
2 changes: 2 additions & 0 deletions packages/flutter_tools/lib/src/windows/build_windows.dart
Expand Up @@ -18,6 +18,7 @@ import '../convert.dart';
import '../flutter_plugins.dart';
import '../globals.dart' as globals;
import '../migrations/cmake_custom_command_migration.dart';
import 'migrations/version_migration.dart';
import 'visual_studio.dart';

// These characters appear to be fine: @%()-+_{}[]`~
Expand Down Expand Up @@ -52,6 +53,7 @@ Future<void> buildWindows(WindowsProject windowsProject, BuildInfo buildInfo, {

final List<ProjectMigrator> migrators = <ProjectMigrator>[
CmakeCustomCommandMigration(windowsProject, globals.logger),
VersionMigration(windowsProject, globals.logger),
];

final ProjectMigration migration = ProjectMigration(migrators);
Expand Down
@@ -0,0 +1,147 @@
// 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/file_system.dart';
import '../../base/project_migrator.dart';
import '../../cmake_project.dart';

const String _cmakeFileBefore = r'''
# Apply the standard set of build settings. This can be removed for applications
# that need different build settings.
apply_standard_settings(${BINARY_NAME})
# Disable Windows macros that collide with C++ standard library functions.
target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")
''';
const String _cmakeFileAfter = r'''
# Apply the standard set of build settings. This can be removed for applications
# that need different build settings.
apply_standard_settings(${BINARY_NAME})
# Add preprocessor definitions for the build version.
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"")
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}")
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}")
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}")
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}")
# Disable Windows macros that collide with C++ standard library functions.
target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")
''';

const String _resourceFileBefore = '''
#ifdef FLUTTER_BUILD_NUMBER
#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER
#else
#define VERSION_AS_NUMBER 1,0,0
#endif
#ifdef FLUTTER_BUILD_NAME
#define VERSION_AS_STRING #FLUTTER_BUILD_NAME
#else
#define VERSION_AS_STRING "1.0.0"
#endif
''';
const String _resourceFileAfter = '''
#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD)
#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD
#else
#define VERSION_AS_NUMBER 1,0,0,0
#endif
#if defined(FLUTTER_VERSION)
#define VERSION_AS_STRING FLUTTER_VERSION
#else
#define VERSION_AS_STRING "1.0.0"
#endif
''';

/// Migrates Windows apps to set Flutter's version information.
///
/// See https://github.com/flutter/flutter/issues/73652.
class VersionMigration extends ProjectMigrator {
VersionMigration(WindowsProject project, super.logger)
: _cmakeFile = project.runnerCmakeFile, _resourceFile = project.runnerResourceFile;

final File _cmakeFile;
final File _resourceFile;

@override
void migrate() {
// Skip this migration if the affected files do not exist. This indicates
// the app has done non-trivial changes to its runner and this migration
// might not work as expected if applied.
if (!_cmakeFile.existsSync()) {
logger.printTrace('''
windows/runner/CMakeLists.txt file not found, skipping version migration.
This indicates non-trivial changes have been made to the Windows runner in the
"windows" folder. If needed, you can reset the Windows runner by deleting the
"windows" folder and then using the "flutter create --platforms=windows ." command.
''');
return;
}

if (!_resourceFile.existsSync()) {
logger.printTrace('''
windows/runner/Runner.rc file not found, skipping version migration.
This indicates non-trivial changes have been made to the Windows runner in the
"windows" folder. If needed, you can reset the Windows runner by deleting the
"windows" folder and then using the "flutter create --platforms=windows ." command.
''');
return;
}

// Migrate the windows/runner/CMakeLists.txt file.
final String originalCmakeContents = _cmakeFile.readAsStringSync();
final String newCmakeContents = _replaceFirst(
originalCmakeContents,
_cmakeFileBefore,
_cmakeFileAfter,
);
if (originalCmakeContents != newCmakeContents) {
logger.printStatus('windows/runner/CMakeLists.txt does not define version information, updating.');
_cmakeFile.writeAsStringSync(newCmakeContents);
}

// Migrate the windows/runner/Runner.rc file.
final String originalResourceFileContents = _resourceFile.readAsStringSync();
final String newResourceFileContents = _replaceFirst(
originalResourceFileContents,
_resourceFileBefore,
_resourceFileAfter,
);
if (originalResourceFileContents != newResourceFileContents) {
logger.printStatus(
'windows/runner/Runner.rc does not define use Flutter version information, updating.',
);
_resourceFile.writeAsStringSync(newResourceFileContents);
}
}
}

/// Creates a new string with the first occurrence of [before] replaced by
/// [after].
///
/// If the [originalContents] uses CRLF line endings, the [before] and [after]
/// will be converted to CRLF line endings before the replacement is made.
/// This is necessary for users that have git autocrlf enabled.
///
/// Example:
/// ```dart
/// 'a\n'.replaceFirst('a\n', 'b\n'); // 'b\n'
/// 'a\r\n'.replaceFirst('a\n', 'b\n'); // 'b\r\n'
/// ```
String _replaceFirst(String originalContents, String before, String after) {
final String result = originalContents.replaceFirst(before, after);
if (result != originalContents) {
return result;
}

final String beforeCrlf = before.replaceAll('\n', '\r\n');
final String afterCrlf = after.replaceAll('\n', '\r\n');

return originalContents.replaceFirst(beforeCrlf, afterCrlf);
}

0 comments on commit 9f2ac97

Please sign in to comment.