Unverified Commit f2536da1 authored by J-P Nurmi's avatar J-P Nurmi Committed by GitHub

[flutter_tools] Let CMake handle escaping (#70026)

parent de448c81
......@@ -40,12 +40,12 @@ file(TO_CMAKE_PATH "$escapedProjectDir" PROJECT_DIR)
# Environment variables to pass to tool_backend.sh
list(APPEND FLUTTER_TOOL_ENVIRONMENT
"FLUTTER_ROOT=\\"$escapedFlutterRoot\\""
"PROJECT_DIR=\\"$escapedProjectDir\\""
"FLUTTER_ROOT=$escapedFlutterRoot"
"PROJECT_DIR=$escapedProjectDir"
''');
for (final String key in environment.keys) {
final String value = _escapeBackslashes(environment[key]);
buffer.writeln(' "$key=\\"$value\\""');
buffer.writeln(' "$key=$value"');
}
buffer.writeln(')');
......
......@@ -7,12 +7,14 @@ import '../base/analyze_size.dart';
import '../base/common.dart';
import '../base/file_system.dart';
import '../base/logger.dart';
import '../base/project_migrator.dart';
import '../base/utils.dart';
import '../build_info.dart';
import '../cache.dart';
import '../cmake.dart';
import '../convert.dart';
import '../globals.dart' as globals;
import '../migrations/cmake_custom_command_migration.dart';
import '../plugins.dart';
import '../project.dart';
......@@ -35,6 +37,15 @@ Future<void> buildLinux(
'to learn about adding Linux support to a project.');
}
final List<ProjectMigrator> migrators = <ProjectMigrator>[
CmakeCustomCommandMigration(linuxProject, globals.logger),
];
final ProjectMigration migration = ProjectMigration(migrators);
if (!migration.run()) {
throwToolExit('Unable to migrate project files');
}
// Build the environment that needs to be set for the re-entrant flutter build
// step.
final Map<String, String> environmentConfig = buildInfo.toEnvironmentConfig();
......
// 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 '../base/file_system.dart';
import '../base/logger.dart';
import '../base/project_migrator.dart';
import '../project.dart';
// CMake's add_custom_command() should use VERBATIM to handle escaping of spaces
// and special characters correctly.
// See https://github.com/flutter/flutter/issues/67270.
class CmakeCustomCommandMigration extends ProjectMigrator {
CmakeCustomCommandMigration(CmakeBasedProject project, Logger logger)
: _cmakeFile = project.managedCmakeFile,
super(logger);
final File _cmakeFile;
@override
bool migrate() {
if (!_cmakeFile.existsSync()) {
logger.printTrace('CMake project not found, skipping add_custom_command() VERBATIM migration');
return true;
}
final String originalProjectContents = _cmakeFile.readAsStringSync();
// Example:
//
// add_custom_command(
// OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
// ${CMAKE_CURRENT_BINARY_DIR}/_phony_
// COMMAND ${CMAKE_COMMAND} -E env
// ${FLUTTER_TOOL_ENVIRONMENT}
// "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
// linux-x64 ${CMAKE_BUILD_TYPE}
// )
// Match the whole add_custom_command() and append VERBATIM unless it
// already exists.
final RegExp addCustomCommand = RegExp(
r'add_custom_command\(\s*(.*?)\s*\)',
multiLine: true,
dotAll: true,
);
String newProjectContents = originalProjectContents;
final Iterable<RegExpMatch> matches = addCustomCommand.allMatches(originalProjectContents);
for (final RegExpMatch match in matches) {
final String addCustomCommandOriginal = match.group(1);
if (addCustomCommandOriginal?.contains('VERBATIM') == false) {
final String addCustomCommandReplacement = '$addCustomCommandOriginal\n VERBATIM';
newProjectContents = newProjectContents.replaceAll(addCustomCommandOriginal, addCustomCommandReplacement);
}
}
if (originalProjectContents != newProjectContents) {
logger.printStatus('add_custom_command() missing VERBATIM, updating.');
_cmakeFile.writeAsStringSync(newProjectContents.toString());
}
return true;
}
}
......@@ -364,6 +364,9 @@ abstract class CmakeBasedProject {
/// The native project CMake specification.
File get cmakeFile;
/// Contains definitions for the Flutter library and the tool.
File get managedCmakeFile;
/// Contains definitions for FLUTTER_ROOT, LOCAL_ENGINE, and more flags for
/// the build.
File get generatedCmakeConfigFile;
......@@ -1099,6 +1102,9 @@ class WindowsProject extends FlutterProjectPlatform implements CmakeBasedProject
@override
File get cmakeFile => _editableDirectory.childFile('CMakeLists.txt');
@override
File get managedCmakeFile => managedDirectory.childFile('CMakeLists.txt');
@override
File get generatedCmakeConfigFile => ephemeralDirectory.childFile('generated_config.cmake');
......@@ -1153,6 +1159,9 @@ class LinuxProject extends FlutterProjectPlatform implements CmakeBasedProject {
@override
File get cmakeFile => _editableDirectory.childFile('CMakeLists.txt');
@override
File get managedCmakeFile => managedDirectory.childFile('CMakeLists.txt');
@override
File get generatedCmakeConfigFile => ephemeralDirectory.childFile('generated_config.cmake');
......
......@@ -7,12 +7,14 @@ import '../base/analyze_size.dart';
import '../base/common.dart';
import '../base/file_system.dart';
import '../base/logger.dart';
import '../base/project_migrator.dart';
import '../base/utils.dart';
import '../build_info.dart';
import '../cache.dart';
import '../cmake.dart';
import '../convert.dart';
import '../globals.dart' as globals;
import '../migrations/cmake_custom_command_migration.dart';
import '../plugins.dart';
import '../project.dart';
import 'visual_studio.dart';
......@@ -35,6 +37,15 @@ Future<void> buildWindows(WindowsProject windowsProject, BuildInfo buildInfo, {
'to learn about adding Windows support to a project.');
}
final List<ProjectMigrator> migrators = <ProjectMigrator>[
CmakeCustomCommandMigration(windowsProject, globals.logger),
];
final ProjectMigration migration = ProjectMigration(migrators);
if (!migration.run()) {
throwToolExit('Unable to migrate project files');
}
// Ensure that necessary ephemeral files are generated and up to date.
_writeGeneratedFlutterConfig(windowsProject, buildInfo, target);
createPluginSymlinks(windowsProject.parent);
......
......@@ -83,6 +83,7 @@ add_custom_command(
${FLUTTER_TOOL_ENVIRONMENT}
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
linux-x64 ${CMAKE_BUILD_TYPE}
VERBATIM
)
add_custom_target(flutter_assemble DEPENDS
"${FLUTTER_LIBRARY}"
......
......@@ -91,6 +91,7 @@ add_custom_command(
${FLUTTER_TOOL_ENVIRONMENT}
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
windows-x64 $<CONFIG>
VERBATIM
)
add_custom_target(flutter_assemble DEPENDS
"${FLUTTER_LIBRARY}"
......
......@@ -364,17 +364,17 @@ clang: error: linker command failed with exit code 1 (use -v to see invocation)
expect(configLines, containsAll(<String>[
'file(TO_CMAKE_PATH "$_kTestFlutterRoot" FLUTTER_ROOT)',
'file(TO_CMAKE_PATH "${fileSystem.currentDirectory.path}" PROJECT_DIR)',
r' "DART_DEFINES=\"foo.bar%3D2,fizz.far%3D3\""',
r' "DART_OBFUSCATION=\"true\""',
r' "EXTRA_FRONT_END_OPTIONS=\"--enable-experiment%3Dnon-nullable\""',
r' "EXTRA_GEN_SNAPSHOT_OPTIONS=\"--enable-experiment%3Dnon-nullable\""',
r' "SPLIT_DEBUG_INFO=\"foo/\""',
r' "TRACK_WIDGET_CREATION=\"true\""',
r' "TREE_SHAKE_ICONS=\"true\""',
' "FLUTTER_ROOT=\\"$_kTestFlutterRoot\\""',
' "PROJECT_DIR=\\"${fileSystem.currentDirectory.path}\\""',
r' "FLUTTER_TARGET=\"lib/other.dart\""',
r' "BUNDLE_SKSL_PATH=\"foo/bar.sksl.json\""',
' "DART_DEFINES=foo.bar%3D2,fizz.far%3D3"',
' "DART_OBFUSCATION=true"',
' "EXTRA_FRONT_END_OPTIONS=--enable-experiment%3Dnon-nullable"',
' "EXTRA_GEN_SNAPSHOT_OPTIONS=--enable-experiment%3Dnon-nullable"',
' "SPLIT_DEBUG_INFO=foo/"',
' "TRACK_WIDGET_CREATION=true"',
' "TREE_SHAKE_ICONS=true"',
' "FLUTTER_ROOT=$_kTestFlutterRoot"',
' "PROJECT_DIR=${fileSystem.currentDirectory.path}"',
' "FLUTTER_TARGET=lib/other.dart"',
' "BUNDLE_SKSL_PATH=foo/bar.sksl.json"',
]));
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
......
......@@ -315,17 +315,17 @@ C:\foo\windows\runner\main.cpp(17,1): error C2065: 'Baz': undeclared identifier
expect(configLines, containsAll(<String>[
r'file(TO_CMAKE_PATH "C:\\flutter" FLUTTER_ROOT)',
r'file(TO_CMAKE_PATH "C:\\" PROJECT_DIR)',
r' "DART_DEFINES=\"foo%3Da,bar%3Db\""',
r' "DART_OBFUSCATION=\"true\""',
r' "EXTRA_FRONT_END_OPTIONS=\"--enable-experiment%3Dnon-nullable\""',
r' "EXTRA_GEN_SNAPSHOT_OPTIONS=\"--enable-experiment%3Dnon-nullable\""',
r' "SPLIT_DEBUG_INFO=\"C:\\foo\\\""',
r' "TRACK_WIDGET_CREATION=\"true\""',
r' "TREE_SHAKE_ICONS=\"true\""',
r' "FLUTTER_ROOT=\"C:\\flutter\""',
r' "PROJECT_DIR=\"C:\\\""',
r' "FLUTTER_TARGET=\"lib\\other.dart\""',
r' "BUNDLE_SKSL_PATH=\"foo\\bar.sksl.json\""',
r' "DART_DEFINES=foo%3Da,bar%3Db"',
r' "DART_OBFUSCATION=true"',
r' "EXTRA_FRONT_END_OPTIONS=--enable-experiment%3Dnon-nullable"',
r' "EXTRA_GEN_SNAPSHOT_OPTIONS=--enable-experiment%3Dnon-nullable"',
r' "SPLIT_DEBUG_INFO=C:\\foo\\"',
r' "TRACK_WIDGET_CREATION=true"',
r' "TREE_SHAKE_ICONS=true"',
r' "FLUTTER_ROOT=C:\\flutter"',
r' "PROJECT_DIR=C:\\"',
r' "FLUTTER_TARGET=lib\\other.dart"',
r' "BUNDLE_SKSL_PATH=foo\\bar.sksl.json"',
]));
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
......
// 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:file/file.dart';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/base/project_migrator.dart';
import 'package:flutter_tools/src/base/terminal.dart';
import 'package:flutter_tools/src/migrations/cmake_custom_command_migration.dart';
import 'package:flutter_tools/src/project.dart';
import 'package:meta/meta.dart';
import 'package:mockito/mockito.dart';
import '../../src/common.dart';
void main () {
group('CMake project migration', () {
testWithoutContext('migrators succeed', () {
final FakeCmakeMigrator fakeCmakeMigrator = FakeCmakeMigrator(succeeds: true);
final ProjectMigration migration = ProjectMigration(<ProjectMigrator>[fakeCmakeMigrator]);
expect(migration.run(), isTrue);
});
testWithoutContext('migrators fail', () {
final FakeCmakeMigrator fakeCmakeMigrator = FakeCmakeMigrator(succeeds: false);
final ProjectMigration migration = ProjectMigration(<ProjectMigrator>[fakeCmakeMigrator]);
expect(migration.run(), isFalse);
});
group('migrate add_custom_command() to use VERBATIM', () {
MemoryFileSystem memoryFileSystem;
BufferLogger testLogger;
MockCmakeProject mockCmakeProject;
File managedCmakeFile;
setUp(() {
memoryFileSystem = MemoryFileSystem.test();
managedCmakeFile = memoryFileSystem.file('CMakeLists.txtx');
testLogger = BufferLogger(
terminal: AnsiTerminal(
stdio: null,
platform: const LocalPlatform(),
),
outputPreferences: OutputPreferences.test(),
);
mockCmakeProject = MockCmakeProject();
when(mockCmakeProject.managedCmakeFile).thenReturn(managedCmakeFile);
});
testWithoutContext('skipped if files are missing', () {
final CmakeCustomCommandMigration cmakeProjectMigration = CmakeCustomCommandMigration(
mockCmakeProject,
testLogger,
);
expect(cmakeProjectMigration.migrate(), isTrue);
expect(managedCmakeFile.existsSync(), isFalse);
expect(testLogger.traceText, contains('CMake project not found, skipping add_custom_command() VERBATIM migration'));
expect(testLogger.statusText, isEmpty);
});
testWithoutContext('skipped if nothing to migrate', () {
const String contents = 'Nothing to migrate';
managedCmakeFile.writeAsStringSync(contents);
final DateTime projectLastModified = managedCmakeFile.lastModifiedSync();
final CmakeCustomCommandMigration cmakeProjectMigration = CmakeCustomCommandMigration(
mockCmakeProject,
testLogger,
);
expect(cmakeProjectMigration.migrate(), isTrue);
expect(managedCmakeFile.lastModifiedSync(), projectLastModified);
expect(managedCmakeFile.readAsStringSync(), contents);
expect(testLogger.statusText, isEmpty);
});
testWithoutContext('skipped if already migrated', () {
const String contents = r'''
add_custom_command(
OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
${CMAKE_CURRENT_BINARY_DIR}/_phony_
COMMAND ${CMAKE_COMMAND} -E env
${FLUTTER_TOOL_ENVIRONMENT}
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
linux-x64 ${CMAKE_BUILD_TYPE}
VERBATIM
)
''';
managedCmakeFile.writeAsStringSync(contents);
final DateTime projectLastModified = managedCmakeFile.lastModifiedSync();
final CmakeCustomCommandMigration cmakeProjectMigration = CmakeCustomCommandMigration(
mockCmakeProject,
testLogger,
);
expect(cmakeProjectMigration.migrate(), isTrue);
expect(managedCmakeFile.lastModifiedSync(), projectLastModified);
expect(managedCmakeFile.readAsStringSync(), contents);
expect(testLogger.statusText, isEmpty);
});
testWithoutContext('is migrated to use VERBATIM', () {
managedCmakeFile.writeAsStringSync(r'''
add_custom_command(
OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
${CMAKE_CURRENT_BINARY_DIR}/_phony_
COMMAND ${CMAKE_COMMAND} -E env
${FLUTTER_TOOL_ENVIRONMENT}
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
linux-x64 ${CMAKE_BUILD_TYPE}
)
''');
final CmakeCustomCommandMigration cmakeProjectMigration = CmakeCustomCommandMigration(
mockCmakeProject,
testLogger,
);
expect(cmakeProjectMigration.migrate(), isTrue);
expect(managedCmakeFile.readAsStringSync(), r'''
add_custom_command(
OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
${CMAKE_CURRENT_BINARY_DIR}/_phony_
COMMAND ${CMAKE_COMMAND} -E env
${FLUTTER_TOOL_ENVIRONMENT}
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
linux-x64 ${CMAKE_BUILD_TYPE}
VERBATIM
)
''');
expect(testLogger.statusText, contains('add_custom_command() missing VERBATIM, updating.'));
});
});
});
}
class MockCmakeProject extends Mock implements CmakeBasedProject {}
class FakeCmakeMigrator extends ProjectMigrator {
FakeCmakeMigrator({@required this.succeeds})
: super(null);
final bool succeeds;
@override
bool migrate() {
return succeeds;
}
@override
String migrateLine(String line) {
return line;
}
}
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