Make Flutter tooling work on Android without Xcode being installed (#15161)
diff --git a/packages/flutter_tools/test/ios/cocoapods_test.dart b/packages/flutter_tools/test/ios/cocoapods_test.dart index 4eccc62..825bbe4 100644 --- a/packages/flutter_tools/test/ios/cocoapods_test.dart +++ b/packages/flutter_tools/test/ios/cocoapods_test.dart
@@ -70,7 +70,7 @@ }); testUsingContext('creates swift Podfile if swift', () { - when(mockXcodeProjectInterpreter.canInterpretXcodeProjects).thenReturn(true); + when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true); when(mockXcodeProjectInterpreter.getBuildSettings(any, any)).thenReturn(<String, String>{ 'SWIFT_VERSION': '4.0', }); @@ -94,7 +94,7 @@ }); testUsingContext('does not create Podfile when we cannot interpret Xcode projects', () { - when(mockXcodeProjectInterpreter.canInterpretXcodeProjects).thenReturn(false); + when(mockXcodeProjectInterpreter.isInstalled).thenReturn(false); cocoaPodsUnderTest.setupPodfile('project');
diff --git a/packages/flutter_tools/test/ios/ios_workflow_test.dart b/packages/flutter_tools/test/ios/ios_workflow_test.dart index 1b222f3..401947a 100644 --- a/packages/flutter_tools/test/ios/ios_workflow_test.dart +++ b/packages/flutter_tools/test/ios/ios_workflow_test.dart
@@ -79,7 +79,7 @@ testUsingContext('Emits partial status when Xcode version too low', () async { when(xcode.isInstalled).thenReturn(true); - when(xcode.xcodeVersionText) + when(xcode.versionText) .thenReturn('Xcode 7.0.1\nBuild version 7C1002\n'); when(xcode.isInstalledAndMeetsVersionCheck).thenReturn(false); when(xcode.eulaSigned).thenReturn(true); @@ -94,7 +94,7 @@ testUsingContext('Emits partial status when Xcode EULA not signed', () async { when(xcode.isInstalled).thenReturn(true); - when(xcode.xcodeVersionText) + when(xcode.versionText) .thenReturn('Xcode 8.2.1\nBuild version 8C1002\n'); when(xcode.isInstalledAndMeetsVersionCheck).thenReturn(true); when(xcode.eulaSigned).thenReturn(false); @@ -109,7 +109,7 @@ testUsingContext('Emits partial status when Mac dev mode was never enabled', () async { when(xcode.isInstalled).thenReturn(true); - when(xcode.xcodeVersionText) + when(xcode.versionText) .thenReturn('Xcode 8.2.1\nBuild version 8C1002\n'); when(xcode.isInstalledAndMeetsVersionCheck).thenReturn(true); when(xcode.eulaSigned).thenReturn(true); @@ -124,7 +124,7 @@ testUsingContext('Emits partial status when python six not installed', () async { when(xcode.isInstalled).thenReturn(true); - when(xcode.xcodeVersionText) + when(xcode.versionText) .thenReturn('Xcode 8.2.1\nBuild version 8C1002\n'); when(xcode.isInstalledAndMeetsVersionCheck).thenReturn(true); when(xcode.eulaSigned).thenReturn(true); @@ -139,7 +139,7 @@ testUsingContext('Emits partial status when homebrew not installed', () async { when(xcode.isInstalled).thenReturn(true); - when(xcode.xcodeVersionText) + when(xcode.versionText) .thenReturn('Xcode 8.2.1\nBuild version 8C1002\n'); when(xcode.isInstalledAndMeetsVersionCheck).thenReturn(true); when(xcode.eulaSigned).thenReturn(true); @@ -154,7 +154,7 @@ testUsingContext('Emits partial status when libimobiledevice is not installed', () async { when(xcode.isInstalled).thenReturn(true); - when(xcode.xcodeVersionText) + when(xcode.versionText) .thenReturn('Xcode 8.2.1\nBuild version 8C1002\n'); when(xcode.isInstalledAndMeetsVersionCheck).thenReturn(true); when(xcode.eulaSigned).thenReturn(true); @@ -169,7 +169,7 @@ testUsingContext('Emits partial status when libimobiledevice is installed but not working', () async { when(xcode.isInstalled).thenReturn(true); - when(xcode.xcodeVersionText) + when(xcode.versionText) .thenReturn('Xcode 8.2.1\nBuild version 8C1002\n'); when(xcode.isInstalledAndMeetsVersionCheck).thenReturn(true); when(xcode.eulaSigned).thenReturn(true); @@ -184,7 +184,7 @@ testUsingContext('Emits partial status when ios-deploy is not installed', () async { when(xcode.isInstalled).thenReturn(true); - when(xcode.xcodeVersionText) + when(xcode.versionText) .thenReturn('Xcode 8.2.1\nBuild version 8C1002\n'); when(xcode.isInstalledAndMeetsVersionCheck).thenReturn(true); when(xcode.eulaSigned).thenReturn(true); @@ -199,7 +199,7 @@ testUsingContext('Emits partial status when ios-deploy version is too low', () async { when(xcode.isInstalled).thenReturn(true); - when(xcode.xcodeVersionText) + when(xcode.versionText) .thenReturn('Xcode 8.2.1\nBuild version 8C1002\n'); when(xcode.isInstalledAndMeetsVersionCheck).thenReturn(true); when(xcode.eulaSigned).thenReturn(true); @@ -214,7 +214,7 @@ testUsingContext('Emits partial status when CocoaPods is not installed', () async { when(xcode.isInstalled).thenReturn(true); - when(xcode.xcodeVersionText) + when(xcode.versionText) .thenReturn('Xcode 8.2.1\nBuild version 8C1002\n'); when(xcode.isInstalledAndMeetsVersionCheck).thenReturn(true); when(xcode.eulaSigned).thenReturn(true); @@ -231,7 +231,7 @@ testUsingContext('Emits partial status when CocoaPods version is too low', () async { when(xcode.isInstalled).thenReturn(true); - when(xcode.xcodeVersionText) + when(xcode.versionText) .thenReturn('Xcode 8.2.1\nBuild version 8C1002\n'); when(xcode.isInstalledAndMeetsVersionCheck).thenReturn(true); when(xcode.eulaSigned).thenReturn(true); @@ -249,7 +249,7 @@ testUsingContext('Emits partial status when CocoaPods is not initialized', () async { when(xcode.isInstalled).thenReturn(true); - when(xcode.xcodeVersionText) + when(xcode.versionText) .thenReturn('Xcode 8.2.1\nBuild version 8C1002\n'); when(xcode.isInstalledAndMeetsVersionCheck).thenReturn(true); when(xcode.eulaSigned).thenReturn(true); @@ -269,7 +269,7 @@ testUsingContext('Succeeds when all checks pass', () async { when(xcode.isInstalled).thenReturn(true); - when(xcode.xcodeVersionText) + when(xcode.versionText) .thenReturn('Xcode 8.2.1\nBuild version 8C1002\n'); when(xcode.isInstalledAndMeetsVersionCheck).thenReturn(true); when(xcode.eulaSigned).thenReturn(true);
diff --git a/packages/flutter_tools/test/ios/mac_test.dart b/packages/flutter_tools/test/ios/mac_test.dart index 7110f4e..fc7b5a5 100644 --- a/packages/flutter_tools/test/ios/mac_test.dart +++ b/packages/flutter_tools/test/ios/mac_test.dart
@@ -8,6 +8,7 @@ import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/io.dart' show ProcessException, ProcessResult; import 'package:flutter_tools/src/ios/mac.dart'; +import 'package:flutter_tools/src/ios/xcodeproj.dart'; import 'package:mockito/mockito.dart'; import 'package:platform/platform.dart'; import 'package:process/process.dart'; @@ -18,6 +19,7 @@ class MockProcessManager extends Mock implements ProcessManager {} class MockFile extends Mock implements File {} +class MockXcodeProjectInterpreter extends Mock implements XcodeProjectInterpreter {} void main() { group('IMobileDevice', () { @@ -97,9 +99,11 @@ group('Xcode', () { MockProcessManager mockProcessManager; Xcode xcode; + MockXcodeProjectInterpreter mockXcodeProjectInterpreter; setUp(() { mockProcessManager = new MockProcessManager(); + mockXcodeProjectInterpreter = new MockXcodeProjectInterpreter(); xcode = new Xcode(); }); @@ -120,100 +124,47 @@ ProcessManager: () => mockProcessManager, }); - testUsingContext('xcodeVersionText returns null when xcodebuild is not installed', () { - when(mockProcessManager.runSync(<String>['/usr/bin/xcodebuild', '-version'])) - .thenThrow(const ProcessException('/usr/bin/xcodebuild', const <String>['-version'])); - expect(xcode.xcodeVersionText, isNull); - }, overrides: <Type, Generator>{ - ProcessManager: () => mockProcessManager, - }); - - testUsingContext('xcodeVersionText returns formatted version text', () { - when(mockProcessManager.runSync(<String>['/usr/bin/xcodebuild', '-version'])) - .thenReturn(new ProcessResult(1, 0, 'Xcode 8.3.3\nBuild version 8E3004b', '')); - expect(xcode.xcodeVersionText, 'Xcode 8.3.3, Build version 8E3004b'); - }, overrides: <Type, Generator>{ - ProcessManager: () => mockProcessManager, - }); - - testUsingContext('xcodeVersionText handles Xcode version string with unexpected format', () { - when(mockProcessManager.runSync(<String>['/usr/bin/xcodebuild', '-version'])) - .thenReturn(new ProcessResult(1, 0, 'Xcode Ultra5000\nBuild version 8E3004b', '')); - expect(xcode.xcodeVersionText, 'Xcode Ultra5000, Build version 8E3004b'); - }, overrides: <Type, Generator>{ - ProcessManager: () => mockProcessManager, - }); - - testUsingContext('xcodeMajorVersion returns major version', () { - when(mockProcessManager.runSync(<String>['/usr/bin/xcodebuild', '-version'])) - .thenReturn(new ProcessResult(1, 0, 'Xcode 8.3.3\nBuild version 8E3004b', '')); - expect(xcode.xcodeMajorVersion, 8); - }, overrides: <Type, Generator>{ - ProcessManager: () => mockProcessManager, - }); - - testUsingContext('xcodeMajorVersion is null when version has unexpected format', () { - when(mockProcessManager.runSync(<String>['/usr/bin/xcodebuild', '-version'])) - .thenReturn(new ProcessResult(1, 0, 'Xcode Ultra5000\nBuild version 8E3004b', '')); - expect(xcode.xcodeMajorVersion, isNull); - }, overrides: <Type, Generator>{ - ProcessManager: () => mockProcessManager, - }); - - testUsingContext('xcodeMinorVersion returns minor version', () { - when(mockProcessManager.runSync(<String>['/usr/bin/xcodebuild', '-version'])) - .thenReturn(new ProcessResult(1, 0, 'Xcode 8.3.3\nBuild version 8E3004b', '')); - expect(xcode.xcodeMinorVersion, 3); - }, overrides: <Type, Generator>{ - ProcessManager: () => mockProcessManager, - }); - - testUsingContext('xcodeMinorVersion returns 0 when minor version is unspecified', () { - when(mockProcessManager.runSync(<String>['/usr/bin/xcodebuild', '-version'])) - .thenReturn(new ProcessResult(1, 0, 'Xcode 8\nBuild version 8E3004b', '')); - expect(xcode.xcodeMinorVersion, 0); - }, overrides: <Type, Generator>{ - ProcessManager: () => mockProcessManager, - }); - - testUsingContext('xcodeMinorVersion is null when version has unexpected format', () { - when(mockProcessManager.runSync(<String>['/usr/bin/xcodebuild', '-version'])) - .thenReturn(new ProcessResult(1, 0, 'Xcode Ultra5000\nBuild version 8E3004b', '')); - expect(xcode.xcodeMinorVersion, isNull); - }, overrides: <Type, Generator>{ - ProcessManager: () => mockProcessManager, - }); - testUsingContext('xcodeVersionSatisfactory is false when version is less than minimum', () { - when(mockProcessManager.runSync(<String>['/usr/bin/xcodebuild', '-version'])) - .thenReturn(new ProcessResult(1, 0, 'Xcode 8.3.3\nBuild version 8E3004b', '')); - expect(xcode.xcodeVersionSatisfactory, isFalse); + when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true); + when(mockXcodeProjectInterpreter.majorVersion).thenReturn(8); + when(mockXcodeProjectInterpreter.minorVersion).thenReturn(17); + expect(xcode.isVersionSatisfactory, isFalse); }, overrides: <Type, Generator>{ - ProcessManager: () => mockProcessManager, + XcodeProjectInterpreter: () => mockXcodeProjectInterpreter, }); - testUsingContext('xcodeVersionSatisfactory is false when version in unknown format', () { - when(mockProcessManager.runSync(<String>['/usr/bin/xcodebuild', '-version'])) - .thenReturn(new ProcessResult(1, 0, 'Xcode SuperHD\nBuild version 7A1001', '')); - expect(xcode.xcodeVersionSatisfactory, isFalse); + testUsingContext('xcodeVersionSatisfactory is false when xcodebuild tools are not installed', () { + when(mockXcodeProjectInterpreter.isInstalled).thenReturn(false); + expect(xcode.isVersionSatisfactory, isFalse); }, overrides: <Type, Generator>{ - ProcessManager: () => mockProcessManager, + XcodeProjectInterpreter: () => mockXcodeProjectInterpreter, }); testUsingContext('xcodeVersionSatisfactory is true when version meets minimum', () { - when(mockProcessManager.runSync(<String>['/usr/bin/xcodebuild', '-version'])) - .thenReturn(new ProcessResult(1, 0, 'Xcode 9.0\nBuild version 9A235', '')); - expect(xcode.xcodeVersionSatisfactory, isTrue); + when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true); + when(mockXcodeProjectInterpreter.majorVersion).thenReturn(9); + when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0); + expect(xcode.isVersionSatisfactory, isTrue); }, overrides: <Type, Generator>{ - ProcessManager: () => mockProcessManager, + XcodeProjectInterpreter: () => mockXcodeProjectInterpreter, }); - testUsingContext('xcodeVersionSatisfactory is true when version exceeds minimum', () { - when(mockProcessManager.runSync(<String>['/usr/bin/xcodebuild', '-version'])) - .thenReturn(new ProcessResult(1, 0, 'Xcode 10.0\nBuild version 10A123', '')); - expect(xcode.xcodeVersionSatisfactory, isTrue); + testUsingContext('xcodeVersionSatisfactory is true when major version exceeds minimum', () { + when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true); + when(mockXcodeProjectInterpreter.majorVersion).thenReturn(10); + when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0); + expect(xcode.isVersionSatisfactory, isTrue); }, overrides: <Type, Generator>{ - ProcessManager: () => mockProcessManager, + XcodeProjectInterpreter: () => mockXcodeProjectInterpreter, + }); + + testUsingContext('xcodeVersionSatisfactory is true when minor version exceeds minimum', () { + when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true); + when(mockXcodeProjectInterpreter.majorVersion).thenReturn(9); + when(mockXcodeProjectInterpreter.minorVersion).thenReturn(1); + expect(xcode.isVersionSatisfactory, isTrue); + }, overrides: <Type, Generator>{ + XcodeProjectInterpreter: () => mockXcodeProjectInterpreter, }); testUsingContext('eulaSigned is false when clang is not installed', () {
diff --git a/packages/flutter_tools/test/ios/simulators_test.dart b/packages/flutter_tools/test/ios/simulators_test.dart index 53deb2e..9ef1cf4 100644 --- a/packages/flutter_tools/test/ios/simulators_test.dart +++ b/packages/flutter_tools/test/ios/simulators_test.dart
@@ -165,8 +165,8 @@ testUsingContext( 'old Xcode doesn\'t support screenshot', () { - when(mockXcode.xcodeMajorVersion).thenReturn(7); - when(mockXcode.xcodeMinorVersion).thenReturn(1); + when(mockXcode.majorVersion).thenReturn(7); + when(mockXcode.minorVersion).thenReturn(1); expect(deviceUnderTest.supportsScreenshot, false); }, overrides: <Type, Generator>{Xcode: () => mockXcode} @@ -175,8 +175,8 @@ testUsingContext( 'Xcode 8.2+ supports screenshots', () async { - when(mockXcode.xcodeMajorVersion).thenReturn(8); - when(mockXcode.xcodeMinorVersion).thenReturn(2); + when(mockXcode.majorVersion).thenReturn(8); + when(mockXcode.minorVersion).thenReturn(2); expect(deviceUnderTest.supportsScreenshot, true); final MockFile mockFile = new MockFile(); when(mockFile.path).thenReturn(fs.path.join('some', 'path', 'to', 'screenshot.png'));
diff --git a/packages/flutter_tools/test/ios/xcodeproj_test.dart b/packages/flutter_tools/test/ios/xcodeproj_test.dart index 75cec8a..426c9c4 100644 --- a/packages/flutter_tools/test/ios/xcodeproj_test.dart +++ b/packages/flutter_tools/test/ios/xcodeproj_test.dart
@@ -1,9 +1,144 @@ -import 'package:test/test.dart'; +// Copyright 2018 The Chromium 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 'package:file/memory.dart'; +import 'package:flutter_tools/src/base/file_system.dart'; +import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/ios/xcodeproj.dart'; +import 'package:mockito/mockito.dart'; +import 'package:platform/platform.dart'; +import 'package:process/process.dart'; +import 'package:test/test.dart'; + +import '../src/context.dart'; + +const String xcodebuild = '/usr/bin/xcodebuild'; void main() { + group('xcodebuild versioning', () { + MockProcessManager mockProcessManager; + XcodeProjectInterpreter xcodeProjectInterpreter; + FakePlatform macOS; + FileSystem fs; + + setUp(() { + mockProcessManager = new MockProcessManager(); + xcodeProjectInterpreter = new XcodeProjectInterpreter(); + macOS = fakePlatform('macos'); + fs = new MemoryFileSystem(); + fs.file(xcodebuild).createSync(recursive: true); + }); + + void testUsingOsxContext(String description, dynamic testMethod()) { + testUsingContext(description, testMethod, overrides: <Type, Generator>{ + ProcessManager: () => mockProcessManager, + Platform: () => macOS, + FileSystem: () => fs, + }); + } + + testUsingOsxContext('versionText returns null when xcodebuild is not installed', () { + when(mockProcessManager.runSync(<String>[xcodebuild, '-version'])) + .thenThrow(const ProcessException(xcodebuild, const <String>['-version'])); + expect(xcodeProjectInterpreter.versionText, isNull); + }); + + testUsingOsxContext('versionText returns null when xcodebuild is not fully installed', () { + when(mockProcessManager.runSync(<String>[xcodebuild, '-version'])).thenReturn( + new ProcessResult( + 0, + 1, + "xcode-select: error: tool 'xcodebuild' requires Xcode, " + "but active developer directory '/Library/Developer/CommandLineTools' " + 'is a command line tools instance', + '', + ), + ); + expect(xcodeProjectInterpreter.versionText, isNull); + }); + + testUsingOsxContext('versionText returns formatted version text', () { + when(mockProcessManager.runSync(<String>[xcodebuild, '-version'])) + .thenReturn(new ProcessResult(1, 0, 'Xcode 8.3.3\nBuild version 8E3004b', '')); + expect(xcodeProjectInterpreter.versionText, 'Xcode 8.3.3, Build version 8E3004b'); + }); + + testUsingOsxContext('versionText handles Xcode version string with unexpected format', () { + when(mockProcessManager.runSync(<String>[xcodebuild, '-version'])) + .thenReturn(new ProcessResult(1, 0, 'Xcode Ultra5000\nBuild version 8E3004b', '')); + expect(xcodeProjectInterpreter.versionText, 'Xcode Ultra5000, Build version 8E3004b'); + }); + + testUsingOsxContext('majorVersion returns major version', () { + when(mockProcessManager.runSync(<String>[xcodebuild, '-version'])) + .thenReturn(new ProcessResult(1, 0, 'Xcode 8.3.3\nBuild version 8E3004b', '')); + expect(xcodeProjectInterpreter.majorVersion, 8); + }); + + testUsingOsxContext('majorVersion is null when version has unexpected format', () { + when(mockProcessManager.runSync(<String>[xcodebuild, '-version'])) + .thenReturn(new ProcessResult(1, 0, 'Xcode Ultra5000\nBuild version 8E3004b', '')); + expect(xcodeProjectInterpreter.majorVersion, isNull); + }); + + testUsingOsxContext('minorVersion returns minor version', () { + when(mockProcessManager.runSync(<String>[xcodebuild, '-version'])) + .thenReturn(new ProcessResult(1, 0, 'Xcode 8.3.3\nBuild version 8E3004b', '')); + expect(xcodeProjectInterpreter.minorVersion, 3); + }); + + testUsingOsxContext('minorVersion returns 0 when minor version is unspecified', () { + when(mockProcessManager.runSync(<String>[xcodebuild, '-version'])) + .thenReturn(new ProcessResult(1, 0, 'Xcode 8\nBuild version 8E3004b', '')); + expect(xcodeProjectInterpreter.minorVersion, 0); + }); + + testUsingOsxContext('minorVersion is null when version has unexpected format', () { + when(mockProcessManager.runSync(<String>[xcodebuild, '-version'])) + .thenReturn(new ProcessResult(1, 0, 'Xcode Ultra5000\nBuild version 8E3004b', '')); + expect(xcodeProjectInterpreter.minorVersion, isNull); + }); + + testUsingContext('isInstalled is false when not on MacOS', () { + fs.file(xcodebuild).deleteSync(); + expect(xcodeProjectInterpreter.isInstalled, isFalse); + }, overrides: <Type, Generator>{ + Platform: () => fakePlatform('notMacOS') + }); + + testUsingOsxContext('isInstalled is false when xcodebuild does not exist', () { + fs.file(xcodebuild).deleteSync(); + expect(xcodeProjectInterpreter.isInstalled, isFalse); + }); + + testUsingOsxContext('isInstalled is false when Xcode is not fully installed', () { + when(mockProcessManager.runSync(<String>[xcodebuild, '-version'])).thenReturn( + new ProcessResult( + 0, + 1, + "xcode-select: error: tool 'xcodebuild' requires Xcode, " + "but active developer directory '/Library/Developer/CommandLineTools' " + 'is a command line tools instance', + '', + ), + ); + expect(xcodeProjectInterpreter.isInstalled, isFalse); + }); + + testUsingOsxContext('isInstalled is false when version has unexpected format', () { + when(mockProcessManager.runSync(<String>[xcodebuild, '-version'])) + .thenReturn(new ProcessResult(1, 0, 'Xcode Ultra5000\nBuild version 8E3004b', '')); + expect(xcodeProjectInterpreter.isInstalled, isFalse); + }); + + testUsingOsxContext('isInstalled is true when version has expected format', () { + when(mockProcessManager.runSync(<String>[xcodebuild, '-version'])) + .thenReturn(new ProcessResult(1, 0, 'Xcode 8.3.3\nBuild version 8E3004b', '')); + expect(xcodeProjectInterpreter.isInstalled, isTrue); + }); + }); group('Xcode project properties', () { test('properties from default project can be parsed', () { const String output = ''' @@ -118,3 +253,10 @@ }); }); } + +Platform fakePlatform(String name) { + return new FakePlatform.fromPlatform(const LocalPlatform())..operatingSystem = name; +} + +class MockProcessManager extends Mock implements ProcessManager {} +class MockXcodeProjectInterpreter extends Mock implements XcodeProjectInterpreter { }
diff --git a/packages/flutter_tools/test/src/context.dart b/packages/flutter_tools/test/src/context.dart index 0a9f64d..01931b9 100644 --- a/packages/flutter_tools/test/src/context.dart +++ b/packages/flutter_tools/test/src/context.dart
@@ -291,7 +291,16 @@ class MockXcodeProjectInterpreter implements XcodeProjectInterpreter { @override - bool get canInterpretXcodeProjects => true; + bool get isInstalled => true; + + @override + String get versionText => 'Xcode 9.2'; + + @override + int get majorVersion => 9; + + @override + int get minorVersion => 2; @override Map<String, String> getBuildSettings(String projectPath, String target) {