Skip to content

Commit a633f3d

Browse files
authored
Add ability for PrebuiltWindowsApp to accept a zip archive. (#103918)
1 parent d54cdf9 commit a633f3d

File tree

3 files changed

+176
-6
lines changed

3 files changed

+176
-6
lines changed

packages/flutter_tools/lib/src/windows/application_package.dart

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5+
import 'package:archive/archive.dart';
6+
57
import '../application_package.dart';
68
import '../base/file_system.dart';
79
import '../base/utils.dart';
@@ -20,12 +22,56 @@ abstract class WindowsApp extends ApplicationPackage {
2022
);
2123
}
2224

23-
/// Creates a new [WindowsApp] from an existing executable.
25+
/// Creates a new [WindowsApp] from an existing executable or a zip archive.
2426
///
25-
/// `applicationBinary` is the path to the executable.
26-
factory WindowsApp.fromPrebuiltApp(FileSystemEntity applicationBinary) {
27+
/// `applicationBinary` is the path to the executable or the zipped archive.
28+
static WindowsApp? fromPrebuiltApp(FileSystemEntity applicationBinary) {
29+
if (!applicationBinary.existsSync()) {
30+
globals.printError('File "${applicationBinary.path}" does not exist.');
31+
return null;
32+
}
33+
34+
if (applicationBinary.path.endsWith('.exe')) {
35+
return PrebuiltWindowsApp(
36+
executable: applicationBinary.path,
37+
applicationPackage: applicationBinary,
38+
);
39+
}
40+
41+
if (!applicationBinary.path.endsWith('.zip')) {
42+
// Unknown file type
43+
globals.printError('Unknown windows application type.');
44+
return null;
45+
}
46+
47+
// Try to unpack as a zip.
48+
final Directory tempDir = globals.fs.systemTempDirectory.createTempSync('flutter_app.');
49+
try {
50+
globals.os.unzip(globals.fs.file(applicationBinary), tempDir);
51+
} on ArchiveException {
52+
globals.printError('Invalid prebuilt Windows app. Unable to extract from archive.');
53+
return null;
54+
}
55+
final List<FileSystemEntity> exeFilesFound = <FileSystemEntity>[];
56+
for (final FileSystemEntity file in tempDir.listSync()) {
57+
if (file.basename.endsWith('.exe')) {
58+
exeFilesFound.add(file);
59+
}
60+
}
61+
62+
if (exeFilesFound.isEmpty) {
63+
globals.printError('Cannot find .exe files in the zip archive.');
64+
return null;
65+
}
66+
67+
if (exeFilesFound.length > 1) {
68+
globals.printError('Archive "${applicationBinary.path}" contains more than one .exe files.');
69+
return null;
70+
}
71+
2772
return PrebuiltWindowsApp(
28-
executable: applicationBinary.path,
73+
executable: exeFilesFound.single.path,
74+
applicationPackage: applicationBinary,
2975
);
3076
}
3177

@@ -35,9 +81,10 @@ abstract class WindowsApp extends ApplicationPackage {
3581
String executable(BuildMode buildMode);
3682
}
3783

38-
class PrebuiltWindowsApp extends WindowsApp {
84+
class PrebuiltWindowsApp extends WindowsApp implements PrebuiltApplicationPackage {
3985
PrebuiltWindowsApp({
4086
required String executable,
87+
required this.applicationPackage,
4188
}) : _executable = executable,
4289
super(projectBundleId: executable);
4390

@@ -48,6 +95,9 @@ class PrebuiltWindowsApp extends WindowsApp {
4895

4996
@override
5097
String get name => _executable;
98+
99+
@override
100+
final FileSystemEntity applicationPackage;
51101
}
52102

53103
class BuildableWindowsApp extends WindowsApp {
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
// @dart = 2.8
6+
7+
import 'package:file/file.dart';
8+
import 'package:file/memory.dart';
9+
import 'package:flutter_tools/src/base/file_system.dart';
10+
import 'package:flutter_tools/src/base/logger.dart';
11+
import 'package:flutter_tools/src/base/os.dart';
12+
import 'package:flutter_tools/src/windows/application_package.dart';
13+
import 'package:test/fake.dart';
14+
15+
import '../../src/common.dart';
16+
import '../../src/context.dart';
17+
18+
void main() {
19+
group('PrebuiltWindowsApp', () {
20+
FakeOperatingSystemUtils os;
21+
FileSystem fileSystem;
22+
BufferLogger logger;
23+
24+
final Map<Type, Generator> overrides = <Type, Generator>{
25+
FileSystem: () => fileSystem,
26+
ProcessManager: () => FakeProcessManager.any(),
27+
OperatingSystemUtils: () => os,
28+
Logger: () => logger,
29+
};
30+
31+
setUp(() {
32+
fileSystem = MemoryFileSystem.test();
33+
os = FakeOperatingSystemUtils();
34+
logger = BufferLogger.test();
35+
});
36+
37+
testUsingContext('Error on non-existing exe file', () {
38+
final PrebuiltWindowsApp windowsApp = WindowsApp.fromPrebuiltApp(fileSystem.file('not_existing.exe')) as PrebuiltWindowsApp;
39+
40+
expect(windowsApp, isNull);
41+
expect(logger.errorText, contains('File "not_existing.exe" does not exist.'));
42+
}, overrides: overrides);
43+
44+
testUsingContext('Success on exe file', () {
45+
fileSystem.file('file.exe').createSync();
46+
final PrebuiltWindowsApp windowsApp = WindowsApp.fromPrebuiltApp(fileSystem.file('file.exe')) as PrebuiltWindowsApp;
47+
48+
expect(windowsApp.name, 'file.exe');
49+
}, overrides: overrides);
50+
51+
testUsingContext('Error on non-existing zip file', () {
52+
final PrebuiltWindowsApp windowsApp = WindowsApp.fromPrebuiltApp(fileSystem.file('not_existing.zip')) as PrebuiltWindowsApp;
53+
54+
expect(windowsApp, isNull);
55+
expect(logger.errorText, contains('File "not_existing.zip" does not exist.'));
56+
}, overrides: overrides);
57+
58+
testUsingContext('Bad zipped app, no payload dir', () {
59+
fileSystem.file('app.zip').createSync();
60+
final PrebuiltWindowsApp windowsApp = WindowsApp.fromPrebuiltApp(fileSystem.file('app.zip')) as PrebuiltWindowsApp;
61+
62+
expect(windowsApp, isNull);
63+
expect(logger.errorText, contains('Cannot find .exe files in the zip archive.'));
64+
}, overrides: overrides);
65+
66+
testUsingContext('Bad zipped app, two .exe files', () {
67+
fileSystem.file('app.zip').createSync();
68+
os.unzipOverride = (File zipFile, Directory targetDirectory) {
69+
if (zipFile.path != 'app.zip') {
70+
return;
71+
}
72+
final String exePath1 = fileSystem.path.join(targetDirectory.path, 'app1.exe');
73+
final String exePath2 = fileSystem.path.join(targetDirectory.path, 'app2.exe');
74+
fileSystem.directory(exePath1).createSync(recursive: true);
75+
fileSystem.directory(exePath2).createSync(recursive: true);
76+
};
77+
final PrebuiltWindowsApp windowsApp = WindowsApp.fromPrebuiltApp(fileSystem.file('app.zip')) as PrebuiltWindowsApp;
78+
79+
expect(windowsApp, isNull);
80+
expect(logger.errorText, contains('Archive "app.zip" contains more than one .exe files.'));
81+
}, overrides: overrides);
82+
83+
testUsingContext('Success with zipped app', () {
84+
fileSystem.file('app.zip').createSync();
85+
String exePath;
86+
os.unzipOverride = (File zipFile, Directory targetDirectory) {
87+
if (zipFile.path != 'app.zip') {
88+
return;
89+
}
90+
exePath = fileSystem.path.join(targetDirectory.path, 'app.exe');
91+
fileSystem.directory(exePath).createSync(recursive: true);
92+
};
93+
final PrebuiltWindowsApp windowsApp = WindowsApp.fromPrebuiltApp(fileSystem.file('app.zip')) as PrebuiltWindowsApp;
94+
95+
expect(logger.errorText, isEmpty);
96+
expect(windowsApp.name, exePath);
97+
expect(windowsApp.applicationPackage.path, 'app.zip');
98+
}, overrides: overrides);
99+
100+
testUsingContext('Error on unknown file type', () {
101+
fileSystem.file('not_existing.app').createSync();
102+
final PrebuiltWindowsApp windowsApp = WindowsApp.fromPrebuiltApp(fileSystem.file('not_existing.app')) as PrebuiltWindowsApp;
103+
104+
expect(windowsApp, isNull);
105+
expect(logger.errorText, contains('Unknown windows application type.'));
106+
}, overrides: overrides);
107+
});
108+
}
109+
110+
class FakeOperatingSystemUtils extends Fake implements OperatingSystemUtils {
111+
FakeOperatingSystemUtils();
112+
113+
void Function(File, Directory) unzipOverride;
114+
115+
@override
116+
void unzip(File file, Directory targetDirectory) {
117+
unzipOverride?.call(file, targetDirectory);
118+
}
119+
}

packages/flutter_tools/test/general.shard/windows/windows_device_test.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ import '../../src/fakes.dart';
2323
void main() {
2424
testWithoutContext('WindowsDevice defaults', () async {
2525
final WindowsDevice windowsDevice = setUpWindowsDevice();
26-
final PrebuiltWindowsApp windowsApp = PrebuiltWindowsApp(executable: 'foo');
26+
final File dummyFile = MemoryFileSystem.test().file('dummy');
27+
final PrebuiltWindowsApp windowsApp = PrebuiltWindowsApp(executable: 'foo', applicationPackage: dummyFile);
2728

2829
expect(await windowsDevice.targetPlatform, TargetPlatform.windows_x64);
2930
expect(windowsDevice.name, 'Windows');

0 commit comments

Comments
 (0)