Unverified Commit 378387b1 authored by Victoria Ashworth's avatar Victoria Ashworth Committed by GitHub

when getting xcworkspace, exclude hidden files (#114099)

* exclude xcworkspace that begins with a period

* fix if spacing, add comment

* add unit test for when no xcworkspace found

* update to use xcodeWorkspace, make it nullable and refactor

* check if hostAppRoot exists before trying to get xcworkspace

* use local variables to take advantage of type promotion

* only check if not null, don't need to check if exists

* readd exist check for migrate

* readd missing line at end of file
parent 77c06c2c
...@@ -71,7 +71,8 @@ class CleanCommand extends FlutterCommand { ...@@ -71,7 +71,8 @@ class CleanCommand extends FlutterCommand {
} }
Future<void> _cleanXcode(XcodeBasedProject xcodeProject) async { Future<void> _cleanXcode(XcodeBasedProject xcodeProject) async {
if (!xcodeProject.existsSync()) { final Directory? xcodeWorkspace = xcodeProject.xcodeWorkspace;
if (xcodeWorkspace == null) {
return; return;
} }
final Status xcodeStatus = globals.logger.startProgress( final Status xcodeStatus = globals.logger.startProgress(
...@@ -79,7 +80,6 @@ class CleanCommand extends FlutterCommand { ...@@ -79,7 +80,6 @@ class CleanCommand extends FlutterCommand {
); );
try { try {
final XcodeProjectInterpreter xcodeProjectInterpreter = globals.xcodeProjectInterpreter!; final XcodeProjectInterpreter xcodeProjectInterpreter = globals.xcodeProjectInterpreter!;
final Directory xcodeWorkspace = xcodeProject.xcodeWorkspace;
final XcodeProjectInfo projectInfo = (await xcodeProjectInterpreter.getInfo(xcodeWorkspace.parent.path))!; final XcodeProjectInfo projectInfo = (await xcodeProjectInterpreter.getInfo(xcodeWorkspace.parent.path))!;
for (final String scheme in projectInfo.schemes) { for (final String scheme in projectInfo.schemes) {
await xcodeProjectInterpreter.cleanWorkspace(xcodeWorkspace.path, scheme, verbose: _verbose); await xcodeProjectInterpreter.cleanWorkspace(xcodeWorkspace.path, scheme, verbose: _verbose);
......
...@@ -250,17 +250,14 @@ Future<XcodeBuildResult> buildXcodeProject({ ...@@ -250,17 +250,14 @@ Future<XcodeBuildResult> buildXcodeProject({
buildCommands.add('-allowProvisioningDeviceRegistration'); buildCommands.add('-allowProvisioningDeviceRegistration');
} }
final List<FileSystemEntity> contents = app.project.hostAppRoot.listSync(); final Directory? workspacePath = app.project.xcodeWorkspace;
for (final FileSystemEntity entity in contents) { if (workspacePath != null) {
if (globals.fs.path.extension(entity.path) == '.xcworkspace') { buildCommands.addAll(<String>[
buildCommands.addAll(<String>[ '-workspace', workspacePath.basename,
'-workspace', globals.fs.path.basename(entity.path), '-scheme', scheme,
'-scheme', scheme, if (buildAction != XcodeBuildAction.archive) // dSYM files aren't copied to the archive if BUILD_DIR is set.
if (buildAction != XcodeBuildAction.archive) // dSYM files aren't copied to the archive if BUILD_DIR is set. 'BUILD_DIR=${globals.fs.path.absolute(getIosBuildDirectory())}',
'BUILD_DIR=${globals.fs.path.absolute(getIosBuildDirectory())}', ]);
]);
break;
}
} }
// Check if the project contains a watchOS companion app. // Check if the project contains a watchOS companion app.
......
...@@ -15,16 +15,17 @@ class XcodeBuildSystemMigration extends ProjectMigrator { ...@@ -15,16 +15,17 @@ class XcodeBuildSystemMigration extends ProjectMigrator {
super.logger, super.logger,
) : _xcodeWorkspaceSharedSettings = project.xcodeWorkspaceSharedSettings; ) : _xcodeWorkspaceSharedSettings = project.xcodeWorkspaceSharedSettings;
final File _xcodeWorkspaceSharedSettings; final File? _xcodeWorkspaceSharedSettings;
@override @override
void migrate() { void migrate() {
if (!_xcodeWorkspaceSharedSettings.existsSync()) { final File? xcodeWorkspaceSharedSettings = _xcodeWorkspaceSharedSettings;
if (xcodeWorkspaceSharedSettings == null || !xcodeWorkspaceSharedSettings.existsSync()) {
logger.printTrace('Xcode workspace settings not found, skipping build system migration'); logger.printTrace('Xcode workspace settings not found, skipping build system migration');
return; return;
} }
final String contents = _xcodeWorkspaceSharedSettings.readAsStringSync(); final String contents = xcodeWorkspaceSharedSettings.readAsStringSync();
// Only delete this file when it is pointing to the legacy build system. // Only delete this file when it is pointing to the legacy build system.
const String legacyBuildSettingsWorkspace = ''' const String legacyBuildSettingsWorkspace = '''
...@@ -33,8 +34,8 @@ class XcodeBuildSystemMigration extends ProjectMigrator { ...@@ -33,8 +34,8 @@ class XcodeBuildSystemMigration extends ProjectMigrator {
// contains instead of equals to ignore newline file ending variance. // contains instead of equals to ignore newline file ending variance.
if (contents.contains(legacyBuildSettingsWorkspace)) { if (contents.contains(legacyBuildSettingsWorkspace)) {
logger.printStatus('Legacy build system detected, removing ${_xcodeWorkspaceSharedSettings.path}'); logger.printStatus('Legacy build system detected, removing ${xcodeWorkspaceSharedSettings.path}');
_xcodeWorkspaceSharedSettings.deleteSync(); xcodeWorkspaceSharedSettings.deleteSync();
} }
} }
} }
...@@ -36,7 +36,8 @@ Future<void> buildMacOS({ ...@@ -36,7 +36,8 @@ Future<void> buildMacOS({
required bool verboseLogging, required bool verboseLogging,
SizeAnalyzer? sizeAnalyzer, SizeAnalyzer? sizeAnalyzer,
}) async { }) async {
if (!flutterProject.macos.xcodeWorkspace.existsSync()) { final Directory? xcodeWorkspace = flutterProject.macos.xcodeWorkspace;
if (xcodeWorkspace == null) {
throwToolExit('No macOS desktop project configured. ' throwToolExit('No macOS desktop project configured. '
'See https://docs.flutter.dev/desktop#add-desktop-support-to-an-existing-flutter-app ' 'See https://docs.flutter.dev/desktop#add-desktop-support-to-an-existing-flutter-app '
'to learn about adding macOS support to a project.'); 'to learn about adding macOS support to a project.');
...@@ -106,7 +107,7 @@ Future<void> buildMacOS({ ...@@ -106,7 +107,7 @@ Future<void> buildMacOS({
'/usr/bin/env', '/usr/bin/env',
'xcrun', 'xcrun',
'xcodebuild', 'xcodebuild',
'-workspace', flutterProject.macos.xcodeWorkspace.path, '-workspace', xcodeWorkspace.path,
'-configuration', configuration, '-configuration', configuration,
'-scheme', 'Runner', '-scheme', 'Runner',
'-derivedDataPath', flutterBuildDir.absolute.path, '-derivedDataPath', flutterBuildDir.absolute.path,
......
...@@ -47,13 +47,26 @@ abstract class XcodeBasedProject extends FlutterProjectPlatform { ...@@ -47,13 +47,26 @@ abstract class XcodeBasedProject extends FlutterProjectPlatform {
.childFile('contents.xcworkspacedata'); .childFile('contents.xcworkspacedata');
/// The Xcode workspace (.xcworkspace directory) of the host app. /// The Xcode workspace (.xcworkspace directory) of the host app.
Directory get xcodeWorkspace => hostAppRoot.childDirectory('$_hostAppProjectName.xcworkspace'); Directory? get xcodeWorkspace {
if (!hostAppRoot.existsSync()) {
return null;
}
final List<FileSystemEntity> contents = hostAppRoot.listSync();
for (final FileSystemEntity entity in contents) {
// On certain volume types, there is sometimes a stray `._Runner.xcworkspace` file.
// Find the first non-hidden xcworkspace and return the directory.
if (globals.fs.path.extension(entity.path) == '.xcworkspace' && !globals.fs.path.basename(entity.path).startsWith('.')) {
return hostAppRoot.childDirectory(entity.basename);
}
}
return null;
}
/// Xcode workspace shared data directory for the host app. /// Xcode workspace shared data directory for the host app.
Directory get xcodeWorkspaceSharedData => xcodeWorkspace.childDirectory('xcshareddata'); Directory? get xcodeWorkspaceSharedData => xcodeWorkspace?.childDirectory('xcshareddata');
/// Xcode workspace shared workspace settings file for the host app. /// Xcode workspace shared workspace settings file for the host app.
File get xcodeWorkspaceSharedSettings => xcodeWorkspaceSharedData.childFile('WorkspaceSettings.xcsettings'); File? get xcodeWorkspaceSharedSettings => xcodeWorkspaceSharedData?.childFile('WorkspaceSettings.xcsettings');
/// Contains definitions for FLUTTER_ROOT, LOCAL_ENGINE, and more flags for /// Contains definitions for FLUTTER_ROOT, LOCAL_ENGINE, and more flags for
/// the Xcode build. /// the Xcode build.
......
...@@ -99,7 +99,7 @@ void main() { ...@@ -99,7 +99,7 @@ void main() {
'/usr/bin/env', '/usr/bin/env',
'xcrun', 'xcrun',
'xcodebuild', 'xcodebuild',
'-workspace', flutterProject.macos.xcodeWorkspace.path, '-workspace', flutterProject.macos.xcodeWorkspace!.path,
'-configuration', configuration, '-configuration', configuration,
'-scheme', 'Runner', '-scheme', 'Runner',
'-derivedDataPath', flutterBuildDir.absolute.path, '-derivedDataPath', flutterBuildDir.absolute.path,
...@@ -337,6 +337,7 @@ STDERR STUFF ...@@ -337,6 +337,7 @@ STDERR STUFF
final FlutterProject flutterProject = FlutterProject.fromDirectory(fileSystem.currentDirectory); final FlutterProject flutterProject = FlutterProject.fromDirectory(fileSystem.currentDirectory);
final Directory flutterBuildDir = fileSystem.directory(getMacOSBuildDirectory()); final Directory flutterBuildDir = fileSystem.directory(getMacOSBuildDirectory());
createMinimalMockProjectFiles();
fakeProcessManager.addCommands(<FakeCommand>[ fakeProcessManager.addCommands(<FakeCommand>[
FakeCommand( FakeCommand(
...@@ -344,7 +345,7 @@ STDERR STUFF ...@@ -344,7 +345,7 @@ STDERR STUFF
'/usr/bin/env', '/usr/bin/env',
'xcrun', 'xcrun',
'xcodebuild', 'xcodebuild',
'-workspace', flutterProject.macos.xcodeWorkspace.path, '-workspace', flutterProject.macos.xcodeWorkspace!.path,
'-configuration', 'Debug', '-configuration', 'Debug',
'-scheme', 'Runner', '-scheme', 'Runner',
'-derivedDataPath', flutterBuildDir.absolute.path, '-derivedDataPath', flutterBuildDir.absolute.path,
...@@ -359,7 +360,6 @@ STDERR STUFF ...@@ -359,7 +360,6 @@ STDERR STUFF
]); ]);
final BuildCommand command = BuildCommand(); final BuildCommand command = BuildCommand();
createMinimalMockProjectFiles();
await createTestCommandRunner(command).run( await createTestCommandRunner(command).run(
const <String>['build', 'macos', '--debug', '--no-pub'] const <String>['build', 'macos', '--debug', '--no-pub']
......
...@@ -44,7 +44,7 @@ void main() { ...@@ -44,7 +44,7 @@ void main() {
}); });
testUsingContext('$CleanCommand removes build and .dart_tool and ephemeral directories, cleans Xcode for iOS and macOS', () async { testUsingContext('$CleanCommand removes build and .dart_tool and ephemeral directories, cleans Xcode for iOS and macOS', () async {
final FlutterProject projectUnderTest = setupProjectUnderTest(fs.currentDirectory); final FlutterProject projectUnderTest = setupProjectUnderTest(fs.currentDirectory, true);
// Xcode is installed and version satisfactory. // Xcode is installed and version satisfactory.
xcodeProjectInterpreter.isInstalled = true; xcodeProjectInterpreter.isInstalled = true;
xcodeProjectInterpreter.version = Version(1000, 0, 0); xcodeProjectInterpreter.version = Version(1000, 0, 0);
...@@ -70,7 +70,7 @@ void main() { ...@@ -70,7 +70,7 @@ void main() {
expect(projectUnderTest.flutterPluginsDependenciesFile, isNot(exists)); expect(projectUnderTest.flutterPluginsDependenciesFile, isNot(exists));
expect(projectUnderTest.packagesFile, isNot(exists)); expect(projectUnderTest.packagesFile, isNot(exists));
expect(xcodeProjectInterpreter.workspaces, const <CleanWorkspaceCall>[ expect(xcodeProjectInterpreter.workspaces, const <CleanWorkspaceCall>[
CleanWorkspaceCall('/ios/Runner.xcworkspace', 'Runner', false), CleanWorkspaceCall('/ios/Runner.xcworkspace', 'Runner', false),
CleanWorkspaceCall('/macos/Runner.xcworkspace', 'Runner', false), CleanWorkspaceCall('/macos/Runner.xcworkspace', 'Runner', false),
]); ]);
...@@ -81,8 +81,23 @@ void main() { ...@@ -81,8 +81,23 @@ void main() {
XcodeProjectInterpreter: () => xcodeProjectInterpreter, XcodeProjectInterpreter: () => xcodeProjectInterpreter,
}); });
testUsingContext('$CleanCommand does not run when there is no xcworkspace', () async {
setupProjectUnderTest(fs.currentDirectory, false);
// Xcode is installed and version satisfactory.
xcodeProjectInterpreter.isInstalled = true;
xcodeProjectInterpreter.version = Version(1000, 0, 0);
await CleanCommand().runCommand();
expect(xcodeProjectInterpreter.workspaces, const <CleanWorkspaceCall>[]);
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
Xcode: () => xcode,
XcodeProjectInterpreter: () => xcodeProjectInterpreter,
});
testUsingContext('$CleanCommand cleans Xcode verbosely for iOS and macOS', () async { testUsingContext('$CleanCommand cleans Xcode verbosely for iOS and macOS', () async {
setupProjectUnderTest(fs.currentDirectory); setupProjectUnderTest(fs.currentDirectory, true);
// Xcode is installed and version satisfactory. // Xcode is installed and version satisfactory.
xcodeProjectInterpreter.isInstalled = true; xcodeProjectInterpreter.isInstalled = true;
xcodeProjectInterpreter.version = Version(1000, 0, 0); xcodeProjectInterpreter.version = Version(1000, 0, 0);
...@@ -154,12 +169,13 @@ void main() { ...@@ -154,12 +169,13 @@ void main() {
}); });
} }
FlutterProject setupProjectUnderTest(Directory currentDirectory) { FlutterProject setupProjectUnderTest(Directory currentDirectory, bool setupXcodeWorkspace) {
// This needs to be run within testWithoutContext and not setUp since FlutterProject uses context. // This needs to be run within testWithoutContext and not setUp since FlutterProject uses context.
final FlutterProject projectUnderTest = FlutterProject.fromDirectory(currentDirectory); final FlutterProject projectUnderTest = FlutterProject.fromDirectory(currentDirectory);
projectUnderTest.ios.xcodeWorkspace.createSync(recursive: true); if (setupXcodeWorkspace == true) {
projectUnderTest.macos.xcodeWorkspace.createSync(recursive: true); projectUnderTest.ios.hostAppRoot.childDirectory('Runner.xcworkspace').createSync(recursive: true);
projectUnderTest.macos.hostAppRoot.childDirectory('Runner.xcworkspace').createSync(recursive: true);
}
projectUnderTest.dartTool.createSync(recursive: true); projectUnderTest.dartTool.createSync(recursive: true);
projectUnderTest.packagesFile.createSync(recursive: true); projectUnderTest.packagesFile.createSync(recursive: true);
projectUnderTest.android.ephemeralDirectory.createSync(recursive: true); projectUnderTest.android.ephemeralDirectory.createSync(recursive: true);
......
...@@ -208,6 +208,20 @@ keep this 2 ...@@ -208,6 +208,20 @@ keep this 2
expect(testLogger.statusText, isEmpty); expect(testLogger.statusText, isEmpty);
}); });
testWithoutContext('skipped if _xcodeWorkspaceSharedSettings is null', () {
final XcodeBuildSystemMigration iosProjectMigration = XcodeBuildSystemMigration(
project,
testLogger,
);
project.xcodeWorkspaceSharedSettings = null;
iosProjectMigration.migrate();
expect(xcodeWorkspaceSharedSettings.existsSync(), isFalse);
expect(testLogger.traceText, contains('Xcode workspace settings not found, skipping build system migration'));
expect(testLogger.statusText, isEmpty);
});
testWithoutContext('skipped if nothing to upgrade', () { testWithoutContext('skipped if nothing to upgrade', () {
const String contents = ''' const String contents = '''
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
...@@ -995,7 +1009,7 @@ class FakeIosProject extends Fake implements IosProject { ...@@ -995,7 +1009,7 @@ class FakeIosProject extends Fake implements IosProject {
File xcodeProjectWorkspaceData = MemoryFileSystem.test().file('xcodeProjectWorkspaceData'); File xcodeProjectWorkspaceData = MemoryFileSystem.test().file('xcodeProjectWorkspaceData');
@override @override
File xcodeWorkspaceSharedSettings = MemoryFileSystem.test().file('xcodeWorkspaceSharedSettings'); File? xcodeWorkspaceSharedSettings = MemoryFileSystem.test().file('xcodeWorkspaceSharedSettings');
@override @override
File xcodeProjectInfoFile = MemoryFileSystem.test().file('xcodeProjectInfoFile'); File xcodeProjectInfoFile = MemoryFileSystem.test().file('xcodeProjectInfoFile');
......
...@@ -350,6 +350,19 @@ void main() { ...@@ -350,6 +350,19 @@ void main() {
expect(versionInfo['build_number'],'3'); expect(versionInfo['build_number'],'3');
expect(versionInfo['package_name'],'test'); expect(versionInfo['package_name'],'test');
}); });
_testInMemory('gets xcworkspace directory', () async {
final FlutterProject project = await someProject();
project.ios.xcodeProject.createSync();
project.ios.hostAppRoot.childFile('._Runner.xcworkspace').createSync(recursive: true);
project.ios.hostAppRoot.childFile('Runner.xcworkspace').createSync(recursive: true);
expect(project.ios.xcodeWorkspace?.basename, 'Runner.xcworkspace');
});
_testInMemory('no xcworkspace directory found', () async {
final FlutterProject project = await someProject();
project.ios.xcodeProject.createSync();
expect(project.ios.xcodeWorkspace?.basename, null);
});
}); });
group('module status', () { group('module status', () {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment