Unverified Commit 55a1ba76 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

Use output dir instead of specific paths in assemble rules (#39274)

parent 1ba5f799
......@@ -72,4 +72,5 @@ RunCommand "${FLUTTER_ROOT}/bin/flutter" --suppress-analytics \
-dBuildMode="${build_mode}" \
--build-inputs="${build_inputs_path}" \
--build-outputs="${build_outputs_path}" \
--output="${ephemeral_dir}" \
"${build_mode}_macos_bundle_flutter_assets"
......@@ -349,6 +349,7 @@ class Environment {
/// defaults based on it.
factory Environment({
@required Directory projectDir,
@required Directory outputDir,
Directory buildDir,
Map<String, String> defines = const <String, String>{},
}) {
......@@ -371,6 +372,7 @@ class Environment {
final Directory rootBuildDir = buildDir ?? projectDir.childDirectory('build');
final Directory buildDirectory = rootBuildDir.childDirectory(buildPrefix);
return Environment._(
outputDir: outputDir,
projectDir: projectDir,
buildDir: buildDirectory,
rootBuildDir: rootBuildDir,
......@@ -381,6 +383,7 @@ class Environment {
}
Environment._({
@required this.outputDir,
@required this.projectDir,
@required this.buildDir,
@required this.rootBuildDir,
......@@ -401,6 +404,9 @@ class Environment {
/// The [Source] value which is substituted with a path to the flutter root.
static const String kFlutterRootDirectory = '{FLUTTER_ROOT}';
/// The [Source] value which is substituted with a path to [outputDir].
static const String kOutputDirectory = '{OUTPUT_DIR}';
/// The `PROJECT_DIR` environment variable.
///
/// This should be root of the flutter project where a pubspec and dart files
......@@ -424,6 +430,11 @@ class Environment {
/// Defaults to to the value of [Cache.flutterRoot].
final Directory flutterRootDir;
/// The `OUTPUT_DIR` environment variable.
///
/// Must be provided to configure the output location for the final artifacts.
final Directory outputDir;
/// Additional configuration passed to the build targets.
///
/// Setting values here forces a unique build directory to be chosen
......@@ -464,6 +475,7 @@ class BuildSystem {
{ BuildSystemConfig buildSystemConfig = const BuildSystemConfig() }
) async {
environment.buildDir.createSync(recursive: true);
environment.outputDir.createSync(recursive: true);
// Load file hash store from previous builds.
final FileHashStore fileCache = FileHashStore(environment)
......@@ -482,7 +494,7 @@ class BuildSystem {
}
// TODO(jonahwilliams): this is a bit of a hack, due to various parts of
// the flutter tool writing these files unconditionally. Since Xcode uses
// timestamps to track files, this leads to unecessary rebuilds if they
// timestamps to track files, this leads to unnecessary rebuilds if they
// are included. Once all the places that write these files have been
// tracked down and moved into assemble, these checks should be removable.
// We also remove files under .dart_tool, since these are intermediaries
......
......@@ -71,6 +71,10 @@ class SourceVisitor {
segments.addAll(
fs.path.split(environment.flutterRootDir.absolute.path));
break;
case Environment.kOutputDirectory:
segments.addAll(
fs.path.split(environment.outputDir.resolveSymbolicLinksSync()));
break;
default:
throw InvalidPatternException(pattern);
}
......
......@@ -15,12 +15,11 @@ import '../../build_info.dart';
import '../../devfs.dart';
import '../../globals.dart';
import '../../macos/xcode.dart';
import '../../project.dart';
import '../build_system.dart';
import '../exceptions.dart';
import 'dart.dart';
const String _kOutputPrefix = '{PROJECT_DIR}/macos/Flutter/ephemeral/FlutterMacOS.framework';
const String _kOutputPrefix = '{OUTPUT_DIR}/FlutterMacOS.framework';
/// The copying logic for flutter assets in macOS.
// TODO(jonahwilliams): remove once build planning lands.
......@@ -51,8 +50,7 @@ class MacOSAssetBehavior extends SourceBehavior {
manifestPath: environment.projectDir.childFile('pubspec.yaml').path,
packagesPath: environment.projectDir.childFile('.packages').path,
);
final FlutterProject flutterProject = FlutterProject.fromDirectory(environment.projectDir);
final String prefix = fs.path.join(flutterProject.macos.ephemeralDirectory.path,
final String prefix = fs.path.join(environment.outputDir.path,
'App.framework', 'Versions', 'A', 'Resources', 'flutter_assets');
final List<File> results = <File>[];
for (String key in assetBundle.entries.keys) {
......@@ -65,7 +63,7 @@ class MacOSAssetBehavior extends SourceBehavior {
/// Copy the macOS framework to the correct copy dir by invoking 'cp -R'.
///
/// This class is abstract to share logic between the three conrete
/// This class is abstract to share logic between the three concrete
/// implementations. The shelling out is done to avoid complications with
/// preserving special files (e.g., symbolic links) in the framework structure.
///
......@@ -121,9 +119,8 @@ abstract class UnpackMacOS extends Target {
}
final BuildMode buildMode = getBuildModeForName(environment.defines[kBuildMode]);
final String basePath = artifacts.getArtifactPath(Artifact.flutterMacOSFramework, mode: buildMode);
final FlutterProject flutterProject = FlutterProject.fromDirectory(environment.projectDir);
final Directory targetDirectory = flutterProject.macos
.ephemeralDirectory
final Directory targetDirectory = environment
.outputDir
.childDirectory('FlutterMacOS.framework');
if (targetDirectory.existsSync()) {
targetDirectory.deleteSync(recursive: true);
......@@ -247,7 +244,6 @@ class CompileMacOSFramework extends Target {
if (buildMode == BuildMode.debug) {
throw Exception('precompiled macOS framework only supported in release/profile builds.');
}
final FlutterProject flutterProject = FlutterProject.fromDirectory(environment.projectDir);
final int result = await AOTSnapshotter(reportTimings: false).build(
bitcode: false,
buildMode: buildMode,
......@@ -255,7 +251,7 @@ class CompileMacOSFramework extends Target {
outputPath: environment.buildDir.path,
platform: TargetPlatform.darwin_x64,
darwinArch: DarwinArch.x86_64,
packagesPath: flutterProject.packagesFile.path
packagesPath: environment.projectDir.childFile('.packages').path,
);
if (result != 0) {
throw Exception('gen shapshot failed.');
......@@ -298,11 +294,11 @@ abstract class MacOSBundleFlutterAssets extends Target {
@override
List<Source> get outputs => const <Source>[
Source.behavior(MacOSAssetBehavior()),
Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/Versions/A/App'),
Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/Versions/A/Resources/Info.plist'),
Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/Versions/A/Resources/flutter_assets/AssetManifest.json'),
Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/Versions/A/Resources/flutter_assets/FontManifest.json'),
Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/Versions/A/Resources/flutter_assets/LICENSE'),
Source.pattern('{OUTPUT_DIR}/App.framework/Versions/A/App'),
Source.pattern('{OUTPUT_DIR}/App.framework/Versions/A/Resources/Info.plist'),
Source.pattern('{OUTPUT_DIR}/App.framework/Versions/A/Resources/flutter_assets/AssetManifest.json'),
Source.pattern('{OUTPUT_DIR}/App.framework/Versions/A/Resources/flutter_assets/FontManifest.json'),
Source.pattern('{OUTPUT_DIR}/App.framework/Versions/A/Resources/flutter_assets/LICENSE'),
];
@override
......@@ -311,9 +307,8 @@ abstract class MacOSBundleFlutterAssets extends Target {
throw MissingDefineException(kBuildMode, 'compile_macos_framework');
}
final BuildMode buildMode = getBuildModeForName(environment.defines[kBuildMode]);
final FlutterProject flutterProject = FlutterProject.fromDirectory(environment.projectDir);
final Directory frameworkRootDirectory = flutterProject.macos
.ephemeralDirectory
final Directory frameworkRootDirectory = environment
.outputDir
.childDirectory('App.framework');
final Directory outputDirectory = frameworkRootDirectory
.childDirectory('Versions')
......@@ -420,7 +415,6 @@ abstract class MacOSBundleFlutterAssets extends Target {
if (!currentVersion.existsSync()) {
final String linkPath = fs.path.relative(outputDirectory.path,
from: outputDirectory.parent.path);
print(linkPath);
currentVersion.createSync('$linkPath${fs.path.separator}');
}
// Create symlink to current resources.
......@@ -429,7 +423,6 @@ abstract class MacOSBundleFlutterAssets extends Target {
if (!currentResources.existsSync()) {
final String linkPath = fs.path.relative(fs.path.join(currentVersion.path, 'Resources'),
from: frameworkRootDirectory.path);
print(linkPath);
currentResources.createSync(linkPath);
}
// Create symlink to current binary.
......@@ -438,12 +431,11 @@ abstract class MacOSBundleFlutterAssets extends Target {
if (!currentFramework.existsSync()) {
final String linkPath = fs.path.relative(fs.path.join(currentVersion.path, 'App'),
from: frameworkRootDirectory.path);
print(linkPath);
currentFramework.createSync(linkPath);
}
} on FileSystemException {
throw Exception('Failed to create symlinks for framework. try removing '
'the "${flutterProject.macos.ephemeralDirectory.path}" directory and rerunning');
'the "${environment.outputDir.path}" directory and rerunning');
}
}
}
......@@ -473,9 +465,9 @@ class DebugMacOSBundleFlutterAssets extends MacOSBundleFlutterAssets {
@override
List<Source> get outputs => <Source>[
...super.outputs,
const Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/Versions/A/Resources/flutter_assets/kernel_blob.bin'),
const Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/Versions/A/Resources/flutter_assets/vm_snapshot_data'),
const Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/Versions/A/Resources/flutter_assets/isolate_snapshot_data'),
const Source.pattern('{OUTPUT_DIR}/App.framework/Versions/A/Resources/flutter_assets/kernel_blob.bin'),
const Source.pattern('{OUTPUT_DIR}/App.framework/Versions/A/Resources/flutter_assets/vm_snapshot_data'),
const Source.pattern('{OUTPUT_DIR}/App.framework/Versions/A/Resources/flutter_assets/isolate_snapshot_data'),
];
}
......
......@@ -54,6 +54,10 @@ class AssembleCommand extends FlutterCommand {
'separated file containing all outputs used will be written after a build.'
' This file is not included as a build input or output. This file is not'
' written if the build fails for any reason.');
argParser.addOption('output', abbr: 'o', help: 'A directory where output '
'files will be written. Must be either absolute or relative from the '
'root of the current Flutter project.',
);
argParser.addOption(
'resource-pool-size',
help: 'The maximum number of concurrent tasks the build system will run.'
......@@ -83,7 +87,16 @@ class AssembleCommand extends FlutterCommand {
/// The environmental configuration for a build invocation.
Environment get environment {
final FlutterProject flutterProject = FlutterProject.current();
String output = argResults['output'];
if (output == null) {
throwToolExit('--output directory is required for assemble.');
}
// If path is relative, make it absolute from flutter project.
if (fs.path.isRelative(output)) {
output = fs.path.join(flutterProject.directory.path, output);
}
final Environment result = Environment(
outputDir: fs.directory(output),
buildDir: flutterProject.directory
.childDirectory('.dart_tool')
.childDirectory('flutter_build'),
......
......@@ -92,6 +92,7 @@ void main() {
testbed = Testbed(
setup: () {
environment = Environment(
outputDir: fs.currentDirectory,
projectDir: fs.currentDirectory,
);
fs.file('foo.dart')
......
......@@ -18,6 +18,7 @@ void main() {
testbed = Testbed(setup: () {
fs.directory('build').createSync();
environment = Environment(
outputDir: fs.currentDirectory,
projectDir: fs.currentDirectory,
);
environment.buildDir.createSync(recursive: true);
......
......@@ -21,7 +21,10 @@ void main() {
setUp(() {
testbed = Testbed(setup: () {
fs.directory('cache').createSync();
final Directory outputs = fs.directory('outputs')
..createSync();
environment = Environment(
outputDir: outputs,
projectDir: fs.currentDirectory,
buildDir: fs.directory('build'),
);
......@@ -45,6 +48,15 @@ void main() {
expect(visitor.sources.single.path, fs.path.absolute('foo'));
}));
test('can substitute {OUTPUT_DIR}/foo', () => testbed.run(() {
fs.file('foo').createSync();
const Source fooSource = Source.pattern('{OUTPUT_DIR}/foo');
fooSource.accept(visitor);
expect(visitor.sources.single.path, fs.path.absolute(fs.path.join('outputs', 'foo')));
}));
test('can substitute {BUILD_DIR}/bar', () => testbed.run(() {
final String path = fs.path.join(environment.buildDir.path, 'bar');
fs.file(path).createSync();
......
......@@ -17,6 +17,7 @@ void main() {
setUp(() {
testbed = Testbed(setup: () {
environment = Environment(
outputDir: fs.currentDirectory,
projectDir: fs.currentDirectory,
);
fs.file(fs.path.join('packages', 'flutter_tools', 'lib', 'src',
......@@ -71,6 +72,7 @@ flutter:
..writeAsStringSync('name: foo\ndependencies:\n foo: any\n');
await const FlutterPlugins().build(<File>[], Environment(
outputDir: fs.currentDirectory,
projectDir: fs.currentDirectory,
));
......
......@@ -39,6 +39,7 @@ void main() {
mockProcessManager = MockProcessManager();
testbed = Testbed(setup: () {
androidEnvironment = Environment(
outputDir: fs.currentDirectory,
projectDir: fs.currentDirectory,
defines: <String, String>{
kBuildMode: getNameForBuildMode(BuildMode.profile),
......@@ -46,6 +47,7 @@ void main() {
}
);
iosEnvironment = Environment(
outputDir: fs.currentDirectory,
projectDir: fs.currentDirectory,
defines: <String, String>{
kBuildMode: getNameForBuildMode(BuildMode.profile),
......@@ -158,6 +160,7 @@ flutter_tools:lib/''');
});
await const KernelSnapshot().build(<File>[], Environment(
outputDir: fs.currentDirectory,
projectDir: fs.currentDirectory,
defines: <String, String>{
kBuildMode: 'debug',
......
......@@ -31,6 +31,7 @@ void main() {
testbed = Testbed(setup: () {
Cache.flutterRoot = '';
environment = Environment(
outputDir: fs.currentDirectory,
projectDir: fs.currentDirectory,
);
fs.file('bin/cache/artifacts/engine/linux-x64/libflutter_linux_glfw.so').createSync(recursive: true);
......
......@@ -21,7 +21,7 @@ import '../../../src/common.dart';
import '../../../src/testbed.dart';
const String _kInputPrefix = 'bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework';
const String _kOutputPrefix = 'macos/Flutter/ephemeral/FlutterMacOS.framework';
const String _kOutputPrefix = 'FlutterMacOS.framework';
final List<File> inputs = <File>[
fs.file('$_kInputPrefix/FlutterMacOS'),
......@@ -68,6 +68,7 @@ void main() {
'vmservice_io.dart')).createSync(recursive: true);
environment = Environment(
outputDir: fs.currentDirectory,
projectDir: fs.currentDirectory,
defines: <String, String>{
kBuildMode: 'debug',
......@@ -126,13 +127,11 @@ void main() {
'isolate_snapshot.bin')).createSync(recursive: true);
fs.file(fs.path.join(environment.buildDir.path, 'App.framework', 'App'))
..createSync(recursive: true);
final String frameworkPath = fs.path.join(environment.projectDir.path,
'macos', 'Flutter', 'ephemeral', 'App.framework');
final String inputKernel = fs.path.join(environment.buildDir.path, 'app.dill');
fs.directory(frameworkPath).createSync(recursive: true);
final String outputKernel = fs.path.join(frameworkPath, 'Versions', 'A', 'Resources',
final String outputKernel = fs.path.join('App.framework', 'Versions', 'A', 'Resources',
'flutter_assets', 'kernel_blob.bin');
final String outputPlist = fs.path.join(frameworkPath, 'Versions', 'A', 'Resources',
final String outputPlist = fs.path.join('App.framework', 'Versions', 'A', 'Resources',
'Info.plist');
fs.file(inputKernel)
..createSync(recursive: true)
......@@ -151,14 +150,11 @@ void main() {
'isolate_snapshot.bin')).createSync(recursive: true);
fs.file(fs.path.join(environment.buildDir.path, 'App.framework', 'App'))
..createSync(recursive: true);
final String frameworkPath = fs.path.join(environment.projectDir.path,
'macos', 'Flutter', 'ephemeral', 'App.framework');
fs.directory(frameworkPath).createSync(recursive: true);
final String outputKernel = fs.path.join(frameworkPath, 'Resources',
final String outputKernel = fs.path.join('App.framework', 'Resources',
'flutter_assets', 'kernel_blob.bin');
final String precompiledVm = fs.path.join(frameworkPath, 'Resources',
final String precompiledVm = fs.path.join('App.framework', 'Resources',
'flutter_assets', 'vm_snapshot_data');
final String precompiledIsolate = fs.path.join(frameworkPath, 'Resources',
final String precompiledIsolate = fs.path.join('App.framework', 'Resources',
'flutter_assets', 'isolate_snapshot_data');
await const ProfileMacOSBundleFlutterAssets().build(<File>[], environment..defines[kBuildMode] = 'profile');
......
......@@ -32,6 +32,7 @@ void main() {
when(platform.pathSeparator).thenReturn(r'\');
testbed = Testbed(setup: () {
environment = Environment(
outputDir: fs.currentDirectory,
projectDir: fs.currentDirectory,
);
fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\flutter_export.h').createSync(recursive: true);
......
......@@ -36,12 +36,22 @@ void main() {
return BuildResult(success: true);
});
final CommandRunner<void> commandRunner = createTestCommandRunner(AssembleCommand());
await commandRunner.run(<String>['assemble', 'debug_macos_bundle_flutter_assets']);
await commandRunner.run(<String>['assemble', '-o Output', 'debug_macos_bundle_flutter_assets']);
final BufferLogger bufferLogger = logger;
expect(bufferLogger.statusText.trim(), 'build succeeded.');
}));
test('Throws ToolExit if not provided with output', () => testbed.run(() async {
when(mockBuildSystem.build(any, any, buildSystemConfig: anyNamed('buildSystemConfig')))
.thenAnswer((Invocation invocation) async {
return BuildResult(success: true);
});
final CommandRunner<void> commandRunner = createTestCommandRunner(AssembleCommand());
expect(commandRunner.run(<String>['assemble', 'debug_macos_bundle_flutter_assets']), throwsA(isInstanceOf<ToolExit>()));
}));
test('Throws ToolExit if called with non-existent rule', () => testbed.run(() async {
when(mockBuildSystem.build(any, any, buildSystemConfig: anyNamed('buildSystemConfig')))
.thenAnswer((Invocation invocation) async {
......@@ -49,8 +59,9 @@ void main() {
});
final CommandRunner<void> commandRunner = createTestCommandRunner(AssembleCommand());
expect(commandRunner.run(<String>['assemble', 'undefined']), throwsA(isInstanceOf<ToolExit>()));
expect(commandRunner.run(<String>['assemble', '-o Output', 'undefined']), throwsA(isInstanceOf<ToolExit>()));
}));
test('Only writes input and output files when the values change', () => testbed.run(() async {
when(mockBuildSystem.build(any, any, buildSystemConfig: anyNamed('buildSystemConfig')))
.thenAnswer((Invocation invocation) async {
......@@ -62,7 +73,7 @@ void main() {
});
final CommandRunner<void> commandRunner = createTestCommandRunner(AssembleCommand());
await commandRunner.run(<String>['assemble', '--build-outputs=outputs', '--build-inputs=inputs', 'debug_macos_bundle_flutter_assets']);
await commandRunner.run(<String>['assemble', '-o Output', '--build-outputs=outputs', '--build-inputs=inputs', 'debug_macos_bundle_flutter_assets']);
final File inputs = fs.file('inputs');
final File outputs = fs.file('outputs');
......@@ -72,7 +83,7 @@ void main() {
final DateTime theDistantPast = DateTime(1991, 8, 23);
inputs.setLastModifiedSync(theDistantPast);
outputs.setLastModifiedSync(theDistantPast);
await commandRunner.run(<String>['assemble', '--build-outputs=outputs', '--build-inputs=inputs', 'debug_macos_bundle_flutter_assets']);
await commandRunner.run(<String>['assemble', '-o Output', '--build-outputs=outputs', '--build-inputs=inputs', 'debug_macos_bundle_flutter_assets']);
expect(inputs.lastModifiedSync(), theDistantPast);
expect(outputs.lastModifiedSync(), theDistantPast);
......@@ -85,7 +96,7 @@ void main() {
inputFiles: <File>[fs.file('foo'), fs.file('fizz')..createSync()],
outputFiles: <File>[fs.file('bar'), fs.file(fs.path.join('.dart_tool', 'fizz2'))..createSync(recursive: true)]);
});
await commandRunner.run(<String>['assemble', '--build-outputs=outputs', '--build-inputs=inputs', 'debug_macos_bundle_flutter_assets']);
await commandRunner.run(<String>['assemble', '-o Output', '--build-outputs=outputs', '--build-inputs=inputs', 'debug_macos_bundle_flutter_assets']);
expect(inputs.readAsStringSync(), contains('foo'));
expect(inputs.readAsStringSync(), contains('fizz'));
......
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