Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
0f2af976
Unverified
Commit
0f2af976
authored
Sep 05, 2019
by
Zachary Anderson
Committed by
GitHub
Sep 05, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[flutter_tools] Add a timeout to another showBuildSettings command (#39579)
parent
d50d9c5e
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
227 additions
and
31 deletions
+227
-31
logger.dart
packages/flutter_tools/lib/src/base/logger.dart
+3
-0
context_runner.dart
packages/flutter_tools/lib/src/context_runner.dart
+2
-0
devices.dart
packages/flutter_tools/lib/src/ios/devices.dart
+4
-1
mac.dart
packages/flutter_tools/lib/src/ios/mac.dart
+35
-16
xcodeproj.dart
packages/flutter_tools/lib/src/ios/xcodeproj.dart
+16
-8
process_test.dart
...s/flutter_tools/test/general.shard/base/process_test.dart
+4
-1
devices_test.dart
...es/flutter_tools/test/general.shard/ios/devices_test.dart
+137
-0
xcodeproj_test.dart
.../flutter_tools/test/general.shard/ios/xcodeproj_test.dart
+4
-2
mocks.dart
packages/flutter_tools/test/src/mocks.dart
+22
-3
No files found.
packages/flutter_tools/lib/src/base/logger.dart
View file @
0f2af976
...
...
@@ -278,6 +278,9 @@ class BufferLogger extends Logger {
String
get
statusText
=>
_status
.
toString
();
String
get
traceText
=>
_trace
.
toString
();
@override
bool
get
hasTerminal
=>
false
;
@override
void
printError
(
String
message
,
{
...
...
packages/flutter_tools/lib/src/context_runner.dart
View file @
0f2af976
...
...
@@ -32,6 +32,7 @@ import 'features.dart';
import
'fuchsia/fuchsia_device.dart'
show
FuchsiaDeviceTools
;
import
'fuchsia/fuchsia_sdk.dart'
show
FuchsiaSdk
,
FuchsiaArtifacts
;
import
'fuchsia/fuchsia_workflow.dart'
show
FuchsiaWorkflow
;
import
'ios/devices.dart'
show
IOSDeploy
;
import
'ios/ios_workflow.dart'
;
import
'ios/mac.dart'
;
import
'ios/simulators.dart'
;
...
...
@@ -90,6 +91,7 @@ Future<T> runInContext<T>(
GenSnapshot:
()
=>
const
GenSnapshot
(),
HotRunnerConfig:
()
=>
HotRunnerConfig
(),
IMobileDevice:
()
=>
IMobileDevice
(),
IOSDeploy:
()
=>
const
IOSDeploy
(),
IOSSimulatorUtils:
()
=>
IOSSimulatorUtils
(),
IOSWorkflow:
()
=>
const
IOSWorkflow
(),
KernelCompilerFactory:
()
=>
const
KernelCompilerFactory
(),
...
...
packages/flutter_tools/lib/src/ios/devices.dart
View file @
0f2af976
...
...
@@ -8,6 +8,7 @@ import 'package:meta/meta.dart';
import
'../application_package.dart'
;
import
'../artifacts.dart'
;
import
'../base/context.dart'
;
import
'../base/file_system.dart'
;
import
'../base/io.dart'
;
import
'../base/logger.dart'
;
...
...
@@ -28,6 +29,8 @@ import 'mac.dart';
class
IOSDeploy
{
const
IOSDeploy
();
static
IOSDeploy
get
instance
=>
context
.
get
<
IOSDeploy
>();
/// Installs and runs the specified app bundle using ios-deploy, then returns
/// the exit code.
Future
<
int
>
runApp
({
...
...
@@ -365,7 +368,7 @@ class IOSDevice extends Device {
);
}
final
int
installationResult
=
await
const
IOSDeploy
()
.
runApp
(
final
int
installationResult
=
await
IOSDeploy
.
instance
.
runApp
(
deviceId:
id
,
bundlePath:
bundle
.
path
,
launchArguments:
launchArguments
,
...
...
packages/flutter_tools/lib/src/ios/mac.dart
View file @
0f2af976
...
...
@@ -462,22 +462,41 @@ Future<XcodeBuildResult> buildXcodeProject({
);
flutterUsage
.
sendTiming
(
'build'
,
'xcode-ios'
,
Duration
(
milliseconds:
sw
.
elapsedMilliseconds
));
// Run -showBuildSettings again but with the exact same parameters as the build.
final
Map
<
String
,
String
>
buildSettings
=
parseXcodeBuildSettings
(
runCheckedSync
(
(
List
<
String
>
.
from
(
buildCommands
)
..
add
(
'-showBuildSettings'
))
// Undocumented behavior: xcodebuild craps out if -showBuildSettings
// is used together with -allowProvisioningUpdates or
// -allowProvisioningDeviceRegistration and freezes forever.
.
where
((
String
buildCommand
)
{
return
!
const
<
String
>[
'-allowProvisioningUpdates'
,
'-allowProvisioningDeviceRegistration'
,
].
contains
(
buildCommand
);
}).
toList
(),
workingDirectory:
app
.
project
.
hostAppRoot
.
path
,
));
// Run -showBuildSettings again but with the exact same parameters as the
// build. showBuildSettings is reported to ocassionally timeout. Here, we give
// it a lot of wiggle room (locally on Flutter Gallery, this takes ~1s).
// When there is a timeout, we retry once. See issue #35988.
final
List
<
String
>
showBuildSettingsCommand
=
(
List
<
String
>
.
from
(
buildCommands
)
..
add
(
'-showBuildSettings'
))
// Undocumented behavior: xcodebuild craps out if -showBuildSettings
// is used together with -allowProvisioningUpdates or
// -allowProvisioningDeviceRegistration and freezes forever.
.
where
((
String
buildCommand
)
{
return
!
const
<
String
>[
'-allowProvisioningUpdates'
,
'-allowProvisioningDeviceRegistration'
,
].
contains
(
buildCommand
);
}).
toList
();
const
Duration
showBuildSettingsTimeout
=
Duration
(
minutes:
1
);
Map
<
String
,
String
>
buildSettings
;
try
{
final
RunResult
showBuildSettingsResult
=
await
runCheckedAsync
(
showBuildSettingsCommand
,
workingDirectory:
app
.
project
.
hostAppRoot
.
path
,
timeout:
showBuildSettingsTimeout
,
timeoutRetries:
1
,
);
final
String
showBuildSettings
=
showBuildSettingsResult
.
stdout
.
trim
();
buildSettings
=
parseXcodeBuildSettings
(
showBuildSettings
);
}
on
ProcessException
catch
(
e
)
{
if
(
e
.
toString
().
contains
(
'timed out'
))
{
BuildEvent
(
'xcode-show-build-settings-timeout'
,
command:
showBuildSettingsCommand
.
join
(
' '
),
).
send
();
}
rethrow
;
}
if
(
buildResult
.
exitCode
!=
0
)
{
printStatus
(
'Failed to build iOS app'
);
...
...
packages/flutter_tools/lib/src/ios/xcodeproj.dart
View file @
0f2af976
...
...
@@ -20,6 +20,7 @@ import '../build_info.dart';
import
'../cache.dart'
;
import
'../globals.dart'
;
import
'../project.dart'
;
import
'../reporting/reporting.dart'
;
final
RegExp
_settingExpr
=
RegExp
(
r'(\w+)\s*=\s*(.*)$'
);
final
RegExp
_varExpr
=
RegExp
(
r'\$\(([^)]*)\)'
);
...
...
@@ -278,18 +279,20 @@ class XcodeProjectInterpreter {
final
Status
status
=
Status
.
withSpinner
(
timeout:
timeoutConfiguration
.
fastOperation
,
);
final
List
<
String
>
showBuildSettingsCommand
=
<
String
>[
_executable
,
'-project'
,
fs
.
path
.
absolute
(
projectPath
),
'-target'
,
target
,
'-showBuildSettings'
,
];
try
{
// showBuildSettings is reported to ocassionally timeout. Here, we give it
// a lot of wiggle room (locally on Flutter Gallery, this takes ~1s).
// When there is a timeout, we retry once.
final
RunResult
result
=
await
runCheckedAsync
(<
String
>[
_executable
,
'-project'
,
fs
.
path
.
absolute
(
projectPath
),
'-target'
,
target
,
'-showBuildSettings'
,
],
final
RunResult
result
=
await
runCheckedAsync
(
showBuildSettingsCommand
,
workingDirectory:
projectPath
,
timeout:
timeout
,
timeoutRetries:
1
,
...
...
@@ -297,6 +300,11 @@ class XcodeProjectInterpreter {
final
String
out
=
result
.
stdout
.
trim
();
return
parseXcodeBuildSettings
(
out
);
}
catch
(
error
)
{
if
(
error
is
ProcessException
&&
error
.
toString
().
contains
(
'timed out'
))
{
BuildEvent
(
'xcode-show-build-settings-timeout'
,
command:
showBuildSettingsCommand
.
join
(
' '
),
).
send
();
}
printTrace
(
'Unexpected failure to get the build settings:
$error
.'
);
return
const
<
String
,
String
>{};
}
finally
{
...
...
packages/flutter_tools/test/general.shard/base/process_test.dart
View file @
0f2af976
...
...
@@ -103,7 +103,10 @@ void main() {
// MockProcessManager has an implementation of start() that returns the
// result of processFactory.
flakyProcessManager
=
MockProcessManager
();
flakyProcessManager
.
processFactory
=
flakyProcessFactory
(
1
,
delay:
delay
);
flakyProcessManager
.
processFactory
=
flakyProcessFactory
(
flakes:
1
,
delay:
delay
,
);
});
testUsingContext
(
'flaky process fails without retry'
,
()
async
{
...
...
packages/flutter_tools/test/general.shard/ios/devices_test.dart
View file @
0f2af976
...
...
@@ -3,7 +3,9 @@
// found in the LICENSE file.
import
'dart:async'
;
import
'dart:convert'
;
import
'package:args/command_runner.dart'
;
import
'package:file/file.dart'
;
import
'package:file/memory.dart'
;
import
'package:flutter_tools/src/application_package.dart'
;
...
...
@@ -12,14 +14,19 @@ 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/cache.dart'
;
import
'package:flutter_tools/src/commands/create.dart'
;
import
'package:flutter_tools/src/device.dart'
;
import
'package:flutter_tools/src/doctor.dart'
;
import
'package:flutter_tools/src/ios/devices.dart'
;
import
'package:flutter_tools/src/ios/mac.dart'
;
import
'package:flutter_tools/src/ios/ios_workflow.dart'
;
import
'package:flutter_tools/src/macos/xcode.dart'
;
import
'package:flutter_tools/src/project.dart'
;
import
'package:meta/meta.dart'
;
import
'package:mockito/mockito.dart'
;
import
'package:platform/platform.dart'
;
import
'package:process/process.dart'
;
import
'package:quiver/testing/async.dart'
;
import
'../../src/common.dart'
;
import
'../../src/context.dart'
;
...
...
@@ -31,6 +38,7 @@ class MockCache extends Mock implements Cache {}
class
MockDirectory
extends
Mock
implements
Directory
{}
class
MockFileSystem
extends
Mock
implements
FileSystem
{}
class
MockIMobileDevice
extends
Mock
implements
IMobileDevice
{}
class
MockIOSDeploy
extends
Mock
implements
IOSDeploy
{}
class
MockXcode
extends
Mock
implements
Xcode
{}
class
MockFile
extends
Mock
implements
File
{}
class
MockPortForwarder
extends
Mock
implements
DevicePortForwarder
{}
...
...
@@ -71,6 +79,11 @@ void main() {
MockProcessManager
mockProcessManager
;
MockDeviceLogReader
mockLogReader
;
MockPortForwarder
mockPortForwarder
;
MockIMobileDevice
mockIMobileDevice
;
MockIOSDeploy
mockIosDeploy
;
Directory
tempDir
;
Directory
projectDir
;
const
int
devicePort
=
499
;
const
int
hostPort
=
42
;
...
...
@@ -86,6 +99,8 @@ void main() {
);
setUp
(()
{
Cache
.
disableLocking
();
mockApp
=
MockIOSApp
();
mockArtifacts
=
MockArtifacts
();
mockCache
=
MockCache
();
...
...
@@ -94,6 +109,11 @@ void main() {
mockProcessManager
=
MockProcessManager
();
mockLogReader
=
MockDeviceLogReader
();
mockPortForwarder
=
MockPortForwarder
();
mockIMobileDevice
=
MockIMobileDevice
();
mockIosDeploy
=
MockIOSDeploy
();
tempDir
=
fs
.
systemTempDirectory
.
createTempSync
(
'flutter_tools_create_test.'
);
projectDir
=
tempDir
.
childDirectory
(
'flutter_project'
);
when
(
mockArtifacts
.
getArtifactPath
(
...
...
@@ -126,10 +146,16 @@ void main() {
.
thenAnswer
(
(
_
)
=>
Future
<
ProcessResult
>.
value
(
ProcessResult
(
1
,
0
,
''
,
''
))
);
when
(
mockIMobileDevice
.
getInfoForDevice
(
any
,
'CPUArchitecture'
))
.
thenAnswer
((
_
)
=>
Future
<
String
>.
value
(
'arm64'
));
});
tearDown
(()
{
mockLogReader
.
dispose
();
tryToDelete
(
tempDir
);
Cache
.
enableLocking
();
});
testUsingContext
(
' succeeds in debug mode'
,
()
async
{
...
...
@@ -202,6 +228,91 @@ void main() {
Platform:
()
=>
macPlatform
,
ProcessManager:
()
=>
mockProcessManager
,
});
void
testNonPrebuilt
({
@required
bool
showBuildSettingsFlakes
,
})
{
const
String
name
=
' non-prebuilt succeeds in debug mode'
;
testUsingContext
(
name
+
' flaky:
$showBuildSettingsFlakes
'
,
()
async
{
final
Directory
targetBuildDir
=
projectDir
.
childDirectory
(
'build/ios/iphoneos/Debug-arm64'
);
// The -showBuildSettings calls have a timeout and so go through
// processManager.start().
mockProcessManager
.
processFactory
=
flakyProcessFactory
(
flakes:
showBuildSettingsFlakes
?
1
:
0
,
delay:
const
Duration
(
seconds:
62
),
filter:
(
List
<
String
>
args
)
=>
args
.
contains
(
'-showBuildSettings'
),
stdout:
()
=>
Stream
<
String
>
.
fromIterable
(
<
String
>[
'TARGET_BUILD_DIR =
${targetBuildDir.path}
\n
'
])
.
transform
(
utf8
.
encoder
),
);
// Make all other subcommands succeed.
when
(
mockProcessManager
.
run
(
any
,
workingDirectory:
anyNamed
(
'workingDirectory'
),
environment:
anyNamed
(
'environment'
),
)).
thenAnswer
((
Invocation
inv
)
{
return
Future
<
ProcessResult
>.
value
(
ProcessResult
(
0
,
0
,
''
,
''
));
});
// Deploy works.
when
(
mockIosDeploy
.
runApp
(
deviceId:
anyNamed
(
'deviceId'
),
bundlePath:
anyNamed
(
'bundlePath'
),
launchArguments:
anyNamed
(
'launchArguments'
),
)).
thenAnswer
((
_
)
=>
Future
<
int
>.
value
(
0
));
// Create a dummy project to avoid mocking out the whole directory
// structure expected by device.startApp().
Cache
.
flutterRoot
=
'../..'
;
final
CreateCommand
command
=
CreateCommand
();
final
CommandRunner
<
void
>
runner
=
createTestCommandRunner
(
command
);
await
runner
.
run
(<
String
>[
'create'
,
'--no-pub'
,
projectDir
.
path
,
]);
final
IOSApp
app
=
AbsoluteBuildableIOSApp
(
FlutterProject
.
fromDirectory
(
projectDir
).
ios
);
final
IOSDevice
device
=
IOSDevice
(
'123'
);
// Pre-create the expected build products.
targetBuildDir
.
createSync
(
recursive:
true
);
projectDir
.
childDirectory
(
'build/ios/iphoneos/Runner.app'
).
createSync
(
recursive:
true
);
final
Completer
<
LaunchResult
>
completer
=
Completer
<
LaunchResult
>();
FakeAsync
().
run
((
FakeAsync
time
)
{
device
.
startApp
(
app
,
prebuiltApplication:
false
,
debuggingOptions:
DebuggingOptions
.
disabled
(
const
BuildInfo
(
BuildMode
.
debug
,
null
)),
platformArgs:
<
String
,
dynamic
>{},
).
then
((
LaunchResult
result
)
{
completer
.
complete
(
result
);
});
time
.
flushMicrotasks
();
time
.
elapse
(
const
Duration
(
seconds:
65
));
});
final
LaunchResult
launchResult
=
await
completer
.
future
;
expect
(
launchResult
.
started
,
isTrue
);
expect
(
launchResult
.
hasObservatory
,
isFalse
);
expect
(
await
device
.
stopApp
(
mockApp
),
isFalse
);
},
overrides:
<
Type
,
Generator
>{
DoctorValidatorsProvider:
()
=>
FakeIosDoctorProvider
(),
IMobileDevice:
()
=>
mockIMobileDevice
,
IOSDeploy:
()
=>
mockIosDeploy
,
Platform:
()
=>
macPlatform
,
ProcessManager:
()
=>
mockProcessManager
,
});
}
testNonPrebuilt
(
showBuildSettingsFlakes:
false
);
testNonPrebuilt
(
showBuildSettingsFlakes:
true
);
});
group
(
'Process calls'
,
()
{
...
...
@@ -499,3 +610,29 @@ flutter:
Platform:
()
=>
macPlatform
,
});
}
class
AbsoluteBuildableIOSApp
extends
BuildableIOSApp
{
AbsoluteBuildableIOSApp
(
IosProject
project
)
:
super
(
project
);
@override
String
get
deviceBundlePath
=>
fs
.
path
.
join
(
project
.
parent
.
directory
.
path
,
'build'
,
'ios'
,
'iphoneos'
,
name
);
}
class
FakeIosDoctorProvider
implements
DoctorValidatorsProvider
{
List
<
Workflow
>
_workflows
;
@override
List
<
DoctorValidator
>
get
validators
=>
<
DoctorValidator
>[];
@override
List
<
Workflow
>
get
workflows
{
if
(
_workflows
==
null
)
{
_workflows
=
<
Workflow
>[];
if
(
iosWorkflow
.
appliesToHostPlatform
)
_workflows
.
add
(
iosWorkflow
);
}
return
_workflows
;
}
}
packages/flutter_tools/test/general.shard/ios/xcodeproj_test.dart
View file @
0f2af976
...
...
@@ -153,8 +153,10 @@ void main() {
testUsingContext
(
'build settings flakes'
,
()
async
{
const
Duration
delay
=
Duration
(
seconds:
1
);
mockProcessManager
.
processFactory
=
mocks
.
flakyProcessFactory
(
1
,
delay:
delay
+
const
Duration
(
seconds:
1
));
mockProcessManager
.
processFactory
=
mocks
.
flakyProcessFactory
(
flakes:
1
,
delay:
delay
+
const
Duration
(
seconds:
1
),
);
expect
(
await
xcodeProjectInterpreter
.
getBuildSettingsAsync
(
''
,
''
,
timeout:
delay
),
const
<
String
,
String
>{});
...
...
packages/flutter_tools/test/src/mocks.dart
View file @
0f2af976
...
...
@@ -185,11 +185,26 @@ class MockProcessManager extends Mock implements ProcessManager {
/// A function that generates a process factory that gives processes that fail
/// a given number of times before succeeding. The returned processes will
/// fail after a delay if one is supplied.
ProcessFactory
flakyProcessFactory
(
int
flakes
,
{
Duration
delay
})
{
ProcessFactory
flakyProcessFactory
(
{
int
flakes
,
bool
Function
(
List
<
String
>
command
)
filter
,
Duration
delay
,
Stream
<
List
<
int
>>
Function
()
stdout
,
Stream
<
List
<
int
>>
Function
()
stderr
,
})
{
int
flakesLeft
=
flakes
;
stdout
??=
()
=>
const
Stream
<
List
<
int
>>.
empty
();
stderr
??=
()
=>
const
Stream
<
List
<
int
>>.
empty
();
return
(
List
<
String
>
command
)
{
if
(
filter
!=
null
&&
!
filter
(
command
))
{
return
MockProcess
();
}
if
(
flakesLeft
==
0
)
{
return
MockProcess
(
exitCode:
Future
<
int
>.
value
(
0
));
return
MockProcess
(
exitCode:
Future
<
int
>.
value
(
0
),
stdout:
stdout
(),
stderr:
stderr
(),
);
}
flakesLeft
=
flakesLeft
-
1
;
Future
<
int
>
exitFuture
;
...
...
@@ -198,7 +213,11 @@ ProcessFactory flakyProcessFactory(int flakes, {Duration delay}) {
}
else
{
exitFuture
=
Future
<
int
>.
delayed
(
delay
,
()
=>
Future
<
int
>.
value
(-
9
));
}
return
MockProcess
(
exitCode:
exitFuture
);
return
MockProcess
(
exitCode:
exitFuture
,
stdout:
stdout
(),
stderr:
stderr
(),
);
};
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment