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
3f1f0a81
Unverified
Commit
3f1f0a81
authored
Jun 03, 2022
by
Jenn Magder
Committed by
GitHub
Jun 03, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add flutter build macos-framework command (#105242)
parent
975e04ba
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
881 additions
and
42 deletions
+881
-42
build_ios_framework_module_test.dart
dev/devicelab/bin/tasks/build_ios_framework_module_test.dart
+306
-19
utils.dart
dev/devicelab/lib/framework/utils.dart
+7
-0
build.dart
packages/flutter_tools/lib/src/commands/build.dart
+5
-0
build_macos_framework.dart
...flutter_tools/lib/src/commands/build_macos_framework.dart
+308
-0
xcode_project.dart
packages/flutter_tools/lib/src/xcode_project.dart
+2
-0
build_darwin_framework_test.dart
.../commands.shard/hermetic/build_darwin_framework_test.dart
+252
-22
project_test.dart
packages/flutter_tools/test/general.shard/project_test.dart
+1
-1
No files found.
dev/devicelab/bin/tasks/build_ios_framework_module_test.dart
View file @
3f1f0a81
...
...
@@ -10,7 +10,7 @@ import 'package:flutter_devicelab/framework/task_result.dart';
import
'package:flutter_devicelab/framework/utils.dart'
;
import
'package:path/path.dart'
as
path
;
/// Tests that iOS .xcframeworks can be built.
/// Tests that iOS
and macOS
.xcframeworks can be built.
Future
<
void
>
main
()
async
{
await
task
(()
async
{
...
...
@@ -19,7 +19,7 @@ Future<void> main() async {
final
Directory
tempDir
=
Directory
.
systemTemp
.
createTempSync
(
'flutter_module_test.'
);
try
{
await
inDirectory
(
tempDir
,
()
async
{
section
(
'Test module template'
);
section
(
'Test
iOS
module template'
);
final
Directory
moduleProjectDir
=
Directory
(
path
.
join
(
tempDir
.
path
,
'hello_module'
));
...
...
@@ -34,6 +34,7 @@ Future<void> main() async {
],
);
await
_addPlugin
(
moduleProjectDir
);
await
_testBuildIosFramework
(
moduleProjectDir
,
isModule:
true
);
section
(
'Test app template'
);
...
...
@@ -45,7 +46,9 @@ Future<void> main() async {
options:
<
String
>[
'--org'
,
'io.flutter.devicelab'
,
'hello_project'
],
);
await
_addPlugin
(
projectDir
);
await
_testBuildIosFramework
(
projectDir
);
await
_testBuildMacOSFramework
(
projectDir
);
});
return
TaskResult
.
success
(
null
);
...
...
@@ -59,7 +62,7 @@ Future<void> main() async {
});
}
Future
<
void
>
_
testBuildIosFramework
(
Directory
projectDir
,
{
bool
isModule
=
false
}
)
async
{
Future
<
void
>
_
addPlugin
(
Directory
projectDir
)
async
{
section
(
'Add plugins'
);
final
File
pubspec
=
File
(
path
.
join
(
projectDir
.
path
,
'pubspec.yaml'
));
...
...
@@ -75,24 +78,11 @@ Future<void> _testBuildIosFramework(Directory projectDir, { bool isModule = fals
options:
<
String
>[
'get'
],
);
});
}
// First, build the module in Debug to copy the debug version of Flutter.xcframework.
// This proves "flutter build ios-framework" re-copies the relevant Flutter.xcframework,
// otherwise building plugins with bitcode will fail linking because the debug version
// of Flutter.xcframework does not contain bitcode.
await
inDirectory
(
projectDir
,
()
async
{
await
flutter
(
'build'
,
options:
<
String
>[
'ios'
,
'--debug'
,
'--no-codesign'
,
],
);
});
Future
<
void
>
_testBuildIosFramework
(
Directory
projectDir
,
{
bool
isModule
=
false
})
async
{
// This builds all build modes' frameworks by default
section
(
'Build
frameworks
'
);
section
(
'Build
iOS app
'
);
const
String
outputDirectoryName
=
'flutter-frameworks'
;
...
...
@@ -488,6 +478,293 @@ Future<void> _testBuildIosFramework(Directory projectDir, { bool isModule = fals
}
}
Future
<
void
>
_testBuildMacOSFramework
(
Directory
projectDir
)
async
{
// This builds all build modes' frameworks by default
section
(
'Build macOS frameworks'
);
const
String
outputDirectoryName
=
'flutter-frameworks'
;
await
inDirectory
(
projectDir
,
()
async
{
await
flutter
(
'build'
,
options:
<
String
>[
'macos-framework'
,
'--verbose'
,
'--output=
$outputDirectoryName
'
,
'--obfuscate'
,
'--split-debug-info=symbols'
,
],
);
});
final
String
outputPath
=
path
.
join
(
projectDir
.
path
,
outputDirectoryName
);
final
String
flutterFramework
=
path
.
join
(
outputPath
,
'Debug'
,
'FlutterMacOS.xcframework'
,
'macos-arm64_x86_64'
,
'FlutterMacOS.framework'
,
);
checkDirectoryExists
(
flutterFramework
);
final
String
debugAppFrameworkPath
=
path
.
join
(
outputPath
,
'Debug'
,
'App.xcframework'
,
'macos-arm64_x86_64'
,
'App.framework'
,
'App'
,
);
checkSymlinkExists
(
debugAppFrameworkPath
);
checkFileExists
(
path
.
join
(
outputPath
,
'Debug'
,
'App.xcframework'
,
'macos-arm64_x86_64'
,
'App.framework'
,
'Resources'
,
'Info.plist'
,
));
section
(
'Check debug build has Dart snapshot as asset'
);
checkFileExists
(
path
.
join
(
outputPath
,
'Debug'
,
'App.xcframework'
,
'macos-arm64_x86_64'
,
'App.framework'
,
'Resources'
,
'flutter_assets'
,
'vm_snapshot_data'
,
));
section
(
'Check obfuscation symbols'
);
checkFileExists
(
path
.
join
(
projectDir
.
path
,
'symbols'
,
'app.darwin-arm64.symbols'
,
));
checkFileExists
(
path
.
join
(
projectDir
.
path
,
'symbols'
,
'app.darwin-x86_64.symbols'
,
));
section
(
'Check debug build has no Dart AOT'
);
final
String
aotSymbols
=
await
_dylibSymbols
(
debugAppFrameworkPath
);
if
(
aotSymbols
.
contains
(
'architecture'
)
||
aotSymbols
.
contains
(
'_kDartVmSnapshot'
))
{
throw
TaskResult
.
failure
(
'Debug App.framework contains AOT'
);
}
section
(
'Check profile, release builds has Dart AOT dylib'
);
for
(
final
String
mode
in
<
String
>[
'Profile'
,
'Release'
])
{
final
String
appFrameworkPath
=
path
.
join
(
outputPath
,
mode
,
'App.xcframework'
,
'macos-arm64_x86_64'
,
'App.framework'
,
'App'
,
);
await
_checkDylib
(
appFrameworkPath
);
final
String
aotSymbols
=
await
_dylibSymbols
(
appFrameworkPath
);
if
(!
aotSymbols
.
contains
(
'_kDartVmSnapshot'
))
{
throw
TaskResult
.
failure
(
'
$mode
App.framework missing Dart AOT'
);
}
checkFileNotExists
(
path
.
join
(
outputPath
,
mode
,
'App.xcframework'
,
'macos-arm64_x86_64'
,
'App.framework'
,
'Resources'
,
'flutter_assets'
,
'vm_snapshot_data'
,
));
checkFileExists
(
path
.
join
(
outputPath
,
mode
,
'App.xcframework'
,
'macos-arm64_x86_64'
,
'App.framework'
,
'Resources'
,
'Info.plist'
,
));
}
section
(
"Check all modes' engine dylib"
);
for
(
final
String
mode
in
<
String
>[
'Debug'
,
'Profile'
,
'Release'
])
{
final
String
engineBinary
=
path
.
join
(
outputPath
,
mode
,
'FlutterMacOS.xcframework'
,
'macos-arm64_x86_64'
,
'FlutterMacOS.framework'
,
'FlutterMacOS'
,
);
checkSymlinkExists
(
engineBinary
);
checkFileExists
(
path
.
join
(
outputPath
,
mode
,
'FlutterMacOS.xcframework'
,
'macos-arm64_x86_64'
,
'FlutterMacOS.framework'
,
'Headers'
,
'FlutterMacOS.h'
,
));
}
section
(
'Check all modes have plugins'
);
for
(
final
String
mode
in
<
String
>[
'Debug'
,
'Profile'
,
'Release'
])
{
final
String
pluginFrameworkPath
=
path
.
join
(
outputPath
,
mode
,
'connectivity_macos.xcframework'
,
'macos-arm64_x86_64'
,
'connectivity_macos.framework'
,
'connectivity_macos'
,
);
await
_checkDylib
(
pluginFrameworkPath
);
if
(!
await
_linksOnFlutterMacOS
(
pluginFrameworkPath
))
{
throw
TaskResult
.
failure
(
'
$pluginFrameworkPath
does not link on Flutter'
);
}
final
String
transitiveDependencyFrameworkPath
=
path
.
join
(
outputPath
,
mode
,
'Reachability.xcframework'
,
'macos-arm64_x86_64'
,
'Reachability.framework'
,
'Reachability'
,
);
if
(
await
_linksOnFlutterMacOS
(
transitiveDependencyFrameworkPath
))
{
throw
TaskResult
.
failure
(
'Transitive dependency
$transitiveDependencyFrameworkPath
unexpectedly links on Flutter'
);
}
checkFileExists
(
path
.
join
(
outputPath
,
mode
,
'connectivity_macos.xcframework'
,
'macos-arm64_x86_64'
,
'connectivity_macos.framework'
,
'Headers'
,
'connectivity_macos-Swift.h'
,
));
checkDirectoryExists
(
path
.
join
(
outputPath
,
mode
,
'connectivity_macos.xcframework'
,
'macos-arm64_x86_64'
,
'connectivity_macos.framework'
,
'Modules'
,
'connectivity_macos.swiftmodule'
,
));
if
(
mode
!=
'Debug'
)
{
checkDirectoryExists
(
path
.
join
(
outputPath
,
mode
,
'connectivity_macos.xcframework'
,
'macos-arm64_x86_64'
,
'dSYMs'
,
'connectivity_macos.framework.dSYM'
,
));
}
checkSymlinkExists
(
path
.
join
(
outputPath
,
mode
,
'connectivity_macos.xcframework'
,
'macos-arm64_x86_64'
,
'connectivity_macos.framework'
,
'connectivity_macos'
,
));
}
// This builds all build modes' frameworks by default
section
(
'Build podspec and static plugins'
);
const
String
cocoapodsOutputDirectoryName
=
'flutter-frameworks-cocoapods'
;
await
inDirectory
(
projectDir
,
()
async
{
await
flutter
(
'build'
,
options:
<
String
>[
'macos-framework'
,
'--cocoapods'
,
'--force'
,
// Allow podspec creation on master.
'--output=
$cocoapodsOutputDirectoryName
'
,
'--static'
,
],
);
});
final
String
cocoapodsOutputPath
=
path
.
join
(
projectDir
.
path
,
cocoapodsOutputDirectoryName
);
for
(
final
String
mode
in
<
String
>[
'Debug'
,
'Profile'
,
'Release'
])
{
checkFileExists
(
path
.
join
(
cocoapodsOutputPath
,
mode
,
'FlutterMacOS.podspec'
,
));
await
_checkDylib
(
path
.
join
(
cocoapodsOutputPath
,
mode
,
'App.xcframework'
,
'macos-arm64_x86_64'
,
'App.framework'
,
'App'
,
));
await
_checkStatic
(
path
.
join
(
cocoapodsOutputPath
,
mode
,
'package_info.xcframework'
,
'macos-arm64_x86_64'
,
'package_info.framework'
,
'package_info'
,
));
await
_checkStatic
(
path
.
join
(
cocoapodsOutputPath
,
mode
,
'connectivity_macos.xcframework'
,
'macos-arm64_x86_64'
,
'connectivity_macos.framework'
,
'connectivity_macos'
,
));
checkDirectoryExists
(
path
.
join
(
cocoapodsOutputPath
,
mode
,
'Reachability.xcframework'
,
));
}
checkFileExists
(
path
.
join
(
outputPath
,
'GeneratedPluginRegistrant.swift'
,
));
}
Future
<
void
>
_checkDylib
(
String
pathToLibrary
)
async
{
final
String
binaryFileType
=
await
fileType
(
pathToLibrary
);
if
(!
binaryFileType
.
contains
(
'dynamically linked'
))
{
...
...
@@ -529,3 +806,13 @@ Future<bool> _linksOnFlutter(String pathToBinary) async {
]);
return
loadCommands
.
contains
(
'Flutter.framework'
);
}
Future
<
bool
>
_linksOnFlutterMacOS
(
String
pathToBinary
)
async
{
final
String
loadCommands
=
await
eval
(
'otool'
,
<
String
>[
'-l'
,
'-arch'
,
'arm64'
,
pathToBinary
,
]);
return
loadCommands
.
contains
(
'FlutterMacOS.framework'
);
}
dev/devicelab/lib/framework/utils.dart
View file @
3f1f0a81
...
...
@@ -753,6 +753,13 @@ void checkDirectoryNotExists(String directory) {
}
}
/// Checks that the symlink exists, otherwise throws a [FileSystemException].
void
checkSymlinkExists
(
String
file
)
{
if
(!
exists
(
Link
(
file
)))
{
throw
FileSystemException
(
'Expected symlink to exist.'
,
file
);
}
}
/// Check that `collection` contains all entries in `values`.
void
checkCollectionContains
<
T
>(
Iterable
<
T
>
values
,
Iterable
<
T
>
collection
)
{
for
(
final
T
value
in
values
)
{
...
...
packages/flutter_tools/lib/src/commands/build.dart
View file @
3f1f0a81
...
...
@@ -17,6 +17,7 @@ import 'build_bundle.dart';
import
'build_fuchsia.dart'
;
import
'build_ios.dart'
;
import
'build_ios_framework.dart'
;
import
'build_macos_framework.dart'
;
import
'build_web.dart'
;
class
BuildCommand
extends
FlutterCommand
{
...
...
@@ -29,6 +30,10 @@ class BuildCommand extends FlutterCommand {
buildSystem:
globals
.
buildSystem
,
verboseHelp:
verboseHelp
,
));
_addSubcommand
(
BuildMacOSFrameworkCommand
(
buildSystem:
globals
.
buildSystem
,
verboseHelp:
verboseHelp
,
));
_addSubcommand
(
BuildIOSArchiveCommand
(
verboseHelp:
verboseHelp
));
_addSubcommand
(
BuildBundleCommand
(
verboseHelp:
verboseHelp
));
_addSubcommand
(
BuildWebCommand
(
verboseHelp:
verboseHelp
));
...
...
packages/flutter_tools/lib/src/commands/build_macos_framework.dart
0 → 100644
View file @
3f1f0a81
// 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
'package:meta/meta.dart'
;
import
'../base/common.dart'
;
import
'../base/file_system.dart'
;
import
'../base/logger.dart'
;
import
'../base/process.dart'
;
import
'../base/utils.dart'
;
import
'../build_info.dart'
;
import
'../build_system/build_system.dart'
;
import
'../build_system/targets/macos.dart'
;
import
'../cache.dart'
;
import
'../flutter_plugins.dart'
;
import
'../globals.dart'
as
globals
;
import
'../macos/cocoapod_utils.dart'
;
import
'../runner/flutter_command.dart'
show
DevelopmentArtifact
,
FlutterCommandResult
;
import
'../version.dart'
;
import
'build_ios_framework.dart'
;
/// Produces a .framework for integration into a host macOS app. The .framework
/// contains the Flutter engine and framework code as well as plugins. It can
/// be integrated into plain Xcode projects without using or other package
/// managers.
class
BuildMacOSFrameworkCommand
extends
BuildFrameworkCommand
{
BuildMacOSFrameworkCommand
({
super
.
flutterVersion
,
required
super
.
buildSystem
,
required
super
.
verboseHelp
,
super
.
cache
,
super
.
platform
,
});
@override
final
String
name
=
'macos-framework'
;
@override
final
String
description
=
'Produces .xcframeworks for a Flutter project '
'and its plugins for integration into existing, plain macOS Xcode projects.
\n
'
'This can only be run on macOS hosts.'
;
@override
Future
<
Set
<
DevelopmentArtifact
>>
get
requiredArtifacts
async
=>
const
<
DevelopmentArtifact
>{
DevelopmentArtifact
.
macOS
,
};
@override
Future
<
FlutterCommandResult
>
runCommand
()
async
{
final
String
outputArgument
=
stringArg
(
'output'
)
??
globals
.
fs
.
path
.
join
(
globals
.
fs
.
currentDirectory
.
path
,
'build'
,
'macos'
,
'framework'
,
);
if
(
outputArgument
.
isEmpty
)
{
throwToolExit
(
'--output is required.'
);
}
if
(!
project
.
macos
.
existsSync
())
{
throwToolExit
(
'Project does not support macOS'
);
}
final
Directory
outputDirectory
=
globals
.
fs
.
directory
(
globals
.
fs
.
path
.
absolute
(
globals
.
fs
.
path
.
normalize
(
outputArgument
)));
final
List
<
BuildInfo
>
buildInfos
=
await
getBuildInfos
();
displayNullSafetyMode
(
buildInfos
.
first
);
for
(
final
BuildInfo
buildInfo
in
buildInfos
)
{
globals
.
printStatus
(
'Building macOS frameworks in
${getNameForBuildMode(buildInfo.mode)}
mode...'
);
final
String
xcodeBuildConfiguration
=
sentenceCase
(
getNameForBuildMode
(
buildInfo
.
mode
));
final
Directory
modeDirectory
=
outputDirectory
.
childDirectory
(
xcodeBuildConfiguration
);
if
(
modeDirectory
.
existsSync
())
{
modeDirectory
.
deleteSync
(
recursive:
true
);
}
if
(
boolArg
(
'cocoapods'
)
??
false
)
{
produceFlutterPodspec
(
buildInfo
.
mode
,
modeDirectory
,
force:
boolArg
(
'force'
)
??
false
);
}
// Build aot, create App.framework and copy FlutterMacOS.framework. Make XCFrameworks.
await
_produceAppFramework
(
buildInfo
,
modeDirectory
);
// Build and copy plugins.
final
Directory
buildOutput
=
modeDirectory
.
childDirectory
(
'macos'
);
await
processPodsIfNeeded
(
project
.
macos
,
getMacOSBuildDirectory
(),
buildInfo
.
mode
);
if
(
hasPlugins
(
project
))
{
await
_producePlugins
(
xcodeBuildConfiguration
,
buildOutput
,
modeDirectory
);
}
globals
.
logger
.
printStatus
(
' └─Moving to
${globals.fs.path.relative(modeDirectory.path)}
'
);
// Delete the intermediaries since they would have been copied into our
// output frameworks.
if
(
buildOutput
.
existsSync
())
{
buildOutput
.
deleteSync
(
recursive:
true
);
}
}
globals
.
printStatus
(
'Frameworks written to
${outputDirectory.path}
.'
);
if
(
hasPlugins
(
project
))
{
// Apps do not generate a FlutterPluginRegistrant.framework. Users will need
// to copy GeneratedPluginRegistrant.swift to their project manually.
final
File
pluginRegistrantImplementation
=
project
.
macos
.
pluginRegistrantImplementation
;
pluginRegistrantImplementation
.
copySync
(
outputDirectory
.
childFile
(
pluginRegistrantImplementation
.
basename
).
path
);
globals
.
printStatus
(
'
\n
Copy
${globals.fs.path.basename(pluginRegistrantImplementation.path)}
into your project.'
);
}
return
FlutterCommandResult
.
success
();
}
/// Create podspec that will download and unzip remote engine assets so host apps can leverage CocoaPods
/// vendored framework caching.
@visibleForTesting
void
produceFlutterPodspec
(
BuildMode
mode
,
Directory
modeDirectory
,
{
bool
force
=
false
})
{
final
Status
status
=
globals
.
logger
.
startProgress
(
' ├─Creating FlutterMacOS.podspec...'
);
try
{
final
GitTagVersion
gitTagVersion
=
flutterVersion
.
gitTagVersion
;
if
(!
force
&&
(
gitTagVersion
.
x
==
null
||
gitTagVersion
.
y
==
null
||
gitTagVersion
.
z
==
null
||
gitTagVersion
.
commits
!=
0
))
{
throwToolExit
(
'--cocoapods is only supported on the dev, beta, or stable channels. Detected version is
${flutterVersion.frameworkVersion}
'
);
}
// Podspecs use semantic versioning, which don't support hotfixes.
// Fake out a semantic version with major.minor.(patch * 100) + hotfix.
// A real increasing version is required to prompt CocoaPods to fetch
// new artifacts when the source URL changes.
final
int
minorHotfixVersion
=
(
gitTagVersion
.
z
??
0
)
*
100
+
(
gitTagVersion
.
hotfix
??
0
);
final
File
license
=
cache
.
getLicenseFile
();
if
(!
license
.
existsSync
())
{
throwToolExit
(
'Could not find license at
${license.path}
'
);
}
final
String
licenseSource
=
license
.
readAsStringSync
();
final
String
artifactsMode
=
mode
==
BuildMode
.
debug
?
'darwin-x64'
:
'darwin-x64-
${mode.name}
'
;
final
String
podspecContents
=
'''
Pod::Spec.new do |s|
s.name = '
FlutterMacOS
'
s.version = '
$
{
gitTagVersion
.
x
}.
$
{
gitTagVersion
.
y
}.
$minorHotfixVersion
' #
${flutterVersion.frameworkVersion}
s.summary = '
A
UI
toolkit
for
beautiful
and
fast
apps
.
'
s.description = <<-DESC
Flutter is Google'
s
UI
toolkit
for
building
beautiful
,
fast
apps
for
mobile
,
web
,
desktop
,
and
embedded
devices
from
a
single
codebase
.
This
pod
vends
the
macOS
Flutter
engine
framework
.
It
is
compatible
with
application
frameworks
created
with
this
version
of
the
engine
and
tools
.
The
pod
version
matches
Flutter
version
major
.
minor
.(
patch
*
100
)
+
hotfix
.
DESC
s
.
homepage
=
'https://flutter.dev'
s
.
license
=
{
:
type
=>
'BSD'
,
:
text
=>
<<-
LICENSE
$licenseSource
LICENSE
}
s
.
author
=
{
'Flutter Dev Team'
=>
'flutter-dev@googlegroups.com'
}
s
.
source
=
{
:
http
=>
'
${cache.storageBaseUrl}
/flutter_infra_release/flutter/
${cache.engineRevision}
/
$artifactsMode
/artifacts.zip'
}
s
.
documentation_url
=
'https://flutter.dev/docs'
s
.
osx
.
deployment_target
=
'10.11'
s
.
vendored_frameworks
=
'FlutterMacOS.framework'
s
.
prepare_command
=
'unzip FlutterMacOS.framework -d FlutterMacOS.framework'
end
''';
final File podspec = modeDirectory.childFile('
FlutterMacOS
.
podspec
')..createSync(recursive: true);
podspec.writeAsStringSync(podspecContents);
} finally {
status.stop();
}
}
Future<void> _produceAppFramework(
BuildInfo buildInfo,
Directory outputBuildDirectory,
) async {
final Status status = globals.logger.startProgress(
'
├─
Building
App
.
xcframework
and
FlutterMacOS
.
xcframework
...
',
);
try {
final Environment environment = Environment(
projectDir: globals.fs.currentDirectory,
outputDir: outputBuildDirectory,
buildDir: project.dartTool.childDirectory('
flutter_build
'),
cacheDir: globals.cache.getRoot(),
flutterRootDir: globals.fs.directory(Cache.flutterRoot),
defines: <String, String>{
kTargetFile: targetFile,
kTargetPlatform: getNameForTargetPlatform(TargetPlatform.darwin),
kDarwinArchs: <DarwinArch>[
DarwinArch.x86_64,
DarwinArch.arm64,
].map(getNameForDarwinArch).join('
'),
...buildInfo.toBuildSystemEnvironment(),
},
artifacts: globals.artifacts!,
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
platform: globals.platform,
engineVersion: globals.artifacts!.isLocalEngine ? null : globals.flutterVersion.engineRevision,
generateDartPluginRegistry: true,
);
Target target;
// Always build debug for simulator.
if (buildInfo.isDebug) {
target = const DebugMacOSBundleFlutterAssets();
} else if (buildInfo.isProfile) {
target = const ProfileMacOSBundleFlutterAssets();
} else {
target = const ReleaseMacOSBundleFlutterAssets();
}
final BuildResult result = await buildSystem.build(target, environment);
if (!result.success) {
for (final ExceptionMeasurement measurement in result.exceptions.values) {
globals.printError(measurement.exception.toString());
}
throwToolExit('
The
App
.
xcframework
build
failed
.
');
}
} finally {
status.stop();
}
final Directory appFramework = outputBuildDirectory.childDirectory('
App
.
framework
');
await BuildFrameworkCommand.produceXCFramework(
<Directory>[appFramework],
'
App
',
outputBuildDirectory,
globals.processManager,
);
appFramework.deleteSync(recursive: true);
final Directory flutterFramework = outputBuildDirectory.childDirectory('
FlutterMacOS
.
framework
');
// If FlutterMacOS.podspec was generated, do not generate XCFramework.
if (!(boolArg('
cocoapods
') ?? false)) {
await BuildFrameworkCommand.produceXCFramework(
<Directory>[flutterFramework],
'
FlutterMacOS
',
outputBuildDirectory,
globals.processManager,
);
}
flutterFramework.deleteSync(recursive: true);
}
Future<void> _producePlugins(
String xcodeBuildConfiguration,
Directory buildOutput,
Directory modeDirectory,
) async {
final Status status = globals.logger.startProgress('
├─
Building
plugins
...
');
try {
final List<String> pluginsBuildCommand = <String>[
...globals.xcode!.xcrunCommand(),
'
xcodebuild
',
'
-
alltargets
',
'
-
sdk
',
'
macosx
',
'
-
configuration
',
xcodeBuildConfiguration,
'
SYMROOT
=
$
{
buildOutput
.
path
}
',
'
ONLY_ACTIVE_ARCH
=
NO
', // No device targeted, so build all valid architectures.
'
BUILD_LIBRARY_FOR_DISTRIBUTION
=
YES
',
if (boolArg('
static
') ?? false) '
MACH_O_TYPE
=
staticlib
',
];
final RunResult buildPluginsResult = await globals.processUtils.run(
pluginsBuildCommand,
workingDirectory: project.macos.hostAppRoot.childDirectory('
Pods
').path,
);
if (buildPluginsResult.exitCode != 0) {
throwToolExit('
Unable
to
build
plugin
frameworks:
$
{
buildPluginsResult
.
stderr
}
');
}
final Directory buildConfiguration = buildOutput.childDirectory(xcodeBuildConfiguration);
final Iterable<Directory> products = buildConfiguration.listSync(followLinks: false).whereType<Directory>();
for (final Directory builtProduct in products) {
for (final FileSystemEntity podProduct in builtProduct.listSync(followLinks: false)) {
final String podFrameworkName = podProduct.basename;
if (globals.fs.path.extension(podFrameworkName) != '
.
framework
') {
continue;
}
final String binaryName = globals.fs.path.basenameWithoutExtension(podFrameworkName);
await BuildFrameworkCommand.produceXCFramework(
<Directory>[
podProduct as Directory,
],
binaryName,
modeDirectory,
globals.processManager,
);
}
}
} finally {
status.stop();
}
}
}
packages/flutter_tools/lib/src/xcode_project.dart
View file @
3f1f0a81
...
...
@@ -518,6 +518,8 @@ class MacOSProject extends XcodeBasedProject {
@override
File
get
generatedXcodePropertiesFile
=>
ephemeralDirectory
.
childFile
(
'Flutter-Generated.xcconfig'
);
File
get
pluginRegistrantImplementation
=>
managedDirectory
.
childFile
(
'GeneratedPluginRegistrant.swift'
);
@override
File
xcodeConfigFor
(
String
mode
)
=>
managedDirectory
.
childFile
(
'Flutter-
$mode
.xcconfig'
);
...
...
packages/flutter_tools/test/commands.shard/hermetic/build_darwin_framework_test.dart
View file @
3f1f0a81
...
...
@@ -11,6 +11,7 @@ import 'package:flutter_tools/src/build_info.dart';
import
'package:flutter_tools/src/build_system/build_system.dart'
;
import
'package:flutter_tools/src/cache.dart'
;
import
'package:flutter_tools/src/commands/build_ios_framework.dart'
;
import
'package:flutter_tools/src/commands/build_macos_framework.dart'
;
import
'package:flutter_tools/src/version.dart'
;
import
'../../src/common.dart'
;
...
...
@@ -19,31 +20,31 @@ import '../../src/fakes.dart';
import
'../../src/test_build_system.dart'
;
void
main
(
)
{
group
(
'build ios-framework'
,
()
{
MemoryFileSystem
memoryFileSystem
;
Directory
outputDirectory
;
FakePlatform
fakePlatform
;
setUpAll
(()
{
Cache
.
disableLocking
();
});
MemoryFileSystem
memoryFileSystem
;
Directory
outputDirectory
;
FakePlatform
fakePlatform
;
const
String
storageBaseUrl
=
'https://fake.googleapis.com'
;
setUp
(()
{
memoryFileSystem
=
MemoryFileSystem
.
test
();
fakePlatform
=
FakePlatform
(
operatingSystem:
'macos'
,
environment:
<
String
,
String
>{
'FLUTTER_STORAGE_BASE_URL'
:
storageBaseUrl
,
},
);
setUpAll
(()
{
Cache
.
disableLocking
();
});
outputDirectory
=
memoryFileSystem
.
systemTempDirectory
.
createTempSync
(
'flutter_build_ios_framework_test_output.'
)
.
childDirectory
(
'Debug'
)
..
createSync
();
});
const
String
storageBaseUrl
=
'https://fake.googleapis.com'
;
setUp
(()
{
memoryFileSystem
=
MemoryFileSystem
.
test
();
fakePlatform
=
FakePlatform
(
operatingSystem:
'macos'
,
environment:
<
String
,
String
>{
'FLUTTER_STORAGE_BASE_URL'
:
storageBaseUrl
,
},
);
outputDirectory
=
memoryFileSystem
.
systemTempDirectory
.
createTempSync
(
'flutter_build_framework_test_output.'
)
.
childDirectory
(
'Debug'
)
..
createSync
();
});
group
(
'build ios-framework'
,
()
{
group
(
'podspec'
,
()
{
const
String
engineRevision
=
'0123456789abcdef'
;
Cache
cache
;
...
...
@@ -272,6 +273,235 @@ void main() {
});
});
group
(
'build macos-framework'
,
()
{
group
(
'podspec'
,
()
{
const
String
engineRevision
=
'0123456789abcdef'
;
Cache
cache
;
setUp
(()
{
final
Directory
rootOverride
=
memoryFileSystem
.
directory
(
'cache'
);
cache
=
Cache
.
test
(
rootOverride:
rootOverride
,
platform:
fakePlatform
,
fileSystem:
memoryFileSystem
,
processManager:
FakeProcessManager
.
any
(),
);
rootOverride
.
childDirectory
(
'bin'
).
childDirectory
(
'internal'
).
childFile
(
'engine.version'
)
..
createSync
(
recursive:
true
)
..
writeAsStringSync
(
engineRevision
);
});
testUsingContext
(
'version unknown'
,
()
async
{
const
String
frameworkVersion
=
'0.0.0-unknown'
;
final
FakeFlutterVersion
fakeFlutterVersion
=
FakeFlutterVersion
(
frameworkVersion:
frameworkVersion
);
final
BuildMacOSFrameworkCommand
command
=
BuildMacOSFrameworkCommand
(
buildSystem:
TestBuildSystem
.
all
(
BuildResult
(
success:
true
)),
platform:
fakePlatform
,
flutterVersion:
fakeFlutterVersion
,
cache:
cache
,
verboseHelp:
false
,
);
expect
(()
=>
command
.
produceFlutterPodspec
(
BuildMode
.
debug
,
outputDirectory
),
throwsToolExit
(
message:
'Detected version is
$frameworkVersion
'
));
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
memoryFileSystem
,
ProcessManager:
()
=>
FakeProcessManager
.
any
(),
});
testUsingContext
(
'throws when not on a released version'
,
()
async
{
const
String
frameworkVersion
=
'v1.13.10+hotfix-pre.2'
;
const
GitTagVersion
gitTagVersion
=
GitTagVersion
(
x:
1
,
y:
13
,
z:
10
,
hotfix:
13
,
commits:
2
,
);
final
FakeFlutterVersion
fakeFlutterVersion
=
FakeFlutterVersion
(
gitTagVersion:
gitTagVersion
,
frameworkVersion:
frameworkVersion
,
);
final
BuildMacOSFrameworkCommand
command
=
BuildMacOSFrameworkCommand
(
buildSystem:
TestBuildSystem
.
all
(
BuildResult
(
success:
true
)),
platform:
fakePlatform
,
flutterVersion:
fakeFlutterVersion
,
cache:
cache
,
verboseHelp:
false
,
);
expect
(()
=>
command
.
produceFlutterPodspec
(
BuildMode
.
debug
,
outputDirectory
),
throwsToolExit
(
message:
'Detected version is
$frameworkVersion
'
));
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
memoryFileSystem
,
ProcessManager:
()
=>
FakeProcessManager
.
any
(),
});
testUsingContext
(
'throws when license not found'
,
()
async
{
final
FakeFlutterVersion
fakeFlutterVersion
=
FakeFlutterVersion
(
gitTagVersion:
const
GitTagVersion
(
x:
1
,
y:
13
,
z:
10
,
hotfix:
13
,
commits:
0
,
),
);
final
BuildMacOSFrameworkCommand
command
=
BuildMacOSFrameworkCommand
(
buildSystem:
TestBuildSystem
.
all
(
BuildResult
(
success:
true
)),
platform:
fakePlatform
,
flutterVersion:
fakeFlutterVersion
,
cache:
cache
,
verboseHelp:
false
,
);
expect
(()
=>
command
.
produceFlutterPodspec
(
BuildMode
.
debug
,
outputDirectory
),
throwsToolExit
(
message:
'Could not find license'
));
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
memoryFileSystem
,
ProcessManager:
()
=>
FakeProcessManager
.
any
(),
});
group
(
'is created'
,
()
{
const
String
frameworkVersion
=
'v1.13.11+hotfix.13'
;
const
String
licenseText
=
'This is the license!'
;
setUp
(()
{
// cache.getLicenseFile() relies on the flutter root being set.
Cache
.
flutterRoot
??=
getFlutterRoot
();
cache
.
getLicenseFile
()
..
createSync
(
recursive:
true
)
..
writeAsStringSync
(
licenseText
);
});
group
(
'on master channel'
,
()
{
testUsingContext
(
'created when forced'
,
()
async
{
const
GitTagVersion
gitTagVersion
=
GitTagVersion
(
x:
1
,
y:
13
,
z:
11
,
hotfix:
13
,
commits:
100
,
);
final
FakeFlutterVersion
fakeFlutterVersion
=
FakeFlutterVersion
(
gitTagVersion:
gitTagVersion
,
frameworkVersion:
frameworkVersion
,
);
final
BuildMacOSFrameworkCommand
command
=
BuildMacOSFrameworkCommand
(
buildSystem:
TestBuildSystem
.
all
(
BuildResult
(
success:
true
)),
platform:
fakePlatform
,
flutterVersion:
fakeFlutterVersion
,
cache:
cache
,
verboseHelp:
false
,
);
command
.
produceFlutterPodspec
(
BuildMode
.
debug
,
outputDirectory
,
force:
true
);
final
File
expectedPodspec
=
outputDirectory
.
childFile
(
'FlutterMacOS.podspec'
);
expect
(
expectedPodspec
.
existsSync
(),
isTrue
);
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
memoryFileSystem
,
ProcessManager:
()
=>
FakeProcessManager
.
any
(),
});
});
group
(
'not on master channel'
,
()
{
FakeFlutterVersion
fakeFlutterVersion
;
setUp
(()
{
const
GitTagVersion
gitTagVersion
=
GitTagVersion
(
x:
1
,
y:
13
,
z:
11
,
hotfix:
13
,
commits:
0
,
);
fakeFlutterVersion
=
FakeFlutterVersion
(
gitTagVersion:
gitTagVersion
,
frameworkVersion:
frameworkVersion
,
);
});
testUsingContext
(
'contains license and version'
,
()
async
{
final
BuildMacOSFrameworkCommand
command
=
BuildMacOSFrameworkCommand
(
buildSystem:
TestBuildSystem
.
all
(
BuildResult
(
success:
true
)),
platform:
fakePlatform
,
flutterVersion:
fakeFlutterVersion
,
cache:
cache
,
verboseHelp:
false
,
);
command
.
produceFlutterPodspec
(
BuildMode
.
debug
,
outputDirectory
);
final
File
expectedPodspec
=
outputDirectory
.
childFile
(
'FlutterMacOS.podspec'
);
final
String
podspecContents
=
expectedPodspec
.
readAsStringSync
();
expect
(
podspecContents
,
contains
(
"'1.13.1113'"
));
expect
(
podspecContents
,
contains
(
'#
$frameworkVersion
'
));
expect
(
podspecContents
,
contains
(
licenseText
));
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
memoryFileSystem
,
ProcessManager:
()
=>
FakeProcessManager
.
any
(),
});
testUsingContext
(
'debug URL'
,
()
async
{
final
BuildMacOSFrameworkCommand
command
=
BuildMacOSFrameworkCommand
(
buildSystem:
TestBuildSystem
.
all
(
BuildResult
(
success:
true
)),
platform:
fakePlatform
,
flutterVersion:
fakeFlutterVersion
,
cache:
cache
,
verboseHelp:
false
,
);
command
.
produceFlutterPodspec
(
BuildMode
.
debug
,
outputDirectory
);
final
File
expectedPodspec
=
outputDirectory
.
childFile
(
'FlutterMacOS.podspec'
);
final
String
podspecContents
=
expectedPodspec
.
readAsStringSync
();
expect
(
podspecContents
,
contains
(
"'
$storageBaseUrl
/flutter_infra_release/flutter/
$engineRevision
/darwin-x64/artifacts.zip'"
));
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
memoryFileSystem
,
ProcessManager:
()
=>
FakeProcessManager
.
any
(),
});
testUsingContext
(
'profile URL'
,
()
async
{
final
BuildMacOSFrameworkCommand
command
=
BuildMacOSFrameworkCommand
(
buildSystem:
TestBuildSystem
.
all
(
BuildResult
(
success:
true
)),
platform:
fakePlatform
,
flutterVersion:
fakeFlutterVersion
,
cache:
cache
,
verboseHelp:
false
,
);
command
.
produceFlutterPodspec
(
BuildMode
.
profile
,
outputDirectory
);
final
File
expectedPodspec
=
outputDirectory
.
childFile
(
'FlutterMacOS.podspec'
);
final
String
podspecContents
=
expectedPodspec
.
readAsStringSync
();
expect
(
podspecContents
,
contains
(
"'
$storageBaseUrl
/flutter_infra_release/flutter/
$engineRevision
/darwin-x64-profile/artifacts.zip'"
));
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
memoryFileSystem
,
ProcessManager:
()
=>
FakeProcessManager
.
any
(),
});
testUsingContext
(
'release URL'
,
()
async
{
final
BuildMacOSFrameworkCommand
command
=
BuildMacOSFrameworkCommand
(
buildSystem:
TestBuildSystem
.
all
(
BuildResult
(
success:
true
)),
platform:
fakePlatform
,
flutterVersion:
fakeFlutterVersion
,
cache:
cache
,
verboseHelp:
false
,
);
command
.
produceFlutterPodspec
(
BuildMode
.
release
,
outputDirectory
);
final
File
expectedPodspec
=
outputDirectory
.
childFile
(
'FlutterMacOS.podspec'
);
final
String
podspecContents
=
expectedPodspec
.
readAsStringSync
();
expect
(
podspecContents
,
contains
(
"'
$storageBaseUrl
/flutter_infra_release/flutter/
$engineRevision
/darwin-x64-release/artifacts.zip'"
));
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
memoryFileSystem
,
ProcessManager:
()
=>
FakeProcessManager
.
any
(),
});
});
});
});
});
group
(
'XCFrameworks'
,
()
{
MemoryFileSystem
fileSystem
;
FakeProcessManager
fakeProcessManager
;
...
...
packages/flutter_tools/test/general.shard/project_test.dart
View file @
3f1f0a81
...
...
@@ -251,7 +251,7 @@ void main() {
final
FlutterProject
project
=
await
someProject
();
project
.
macos
.
managedDirectory
.
createSync
(
recursive:
true
);
await
project
.
regeneratePlatformSpecificTooling
();
expectExists
(
project
.
macos
.
managedDirectory
.
childFile
(
'GeneratedPluginRegistrant.swift'
)
);
expectExists
(
project
.
macos
.
pluginRegistrantImplementation
);
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
MemoryFileSystem
.
test
(),
ProcessManager:
()
=>
FakeProcessManager
.
any
(),
...
...
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