// 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 'package:xml/xml.dart'; import 'base/common.dart'; import 'base/file_system.dart'; import 'base/utils.dart'; import 'cmake.dart'; import 'platform_plugins.dart'; import 'project.dart'; /// Represents a CMake-based sub-project. /// /// This defines interfaces common to Windows and Linux projects. abstract class CmakeBasedProject { /// The parent of this project. FlutterProject get parent; /// Whether the subproject (either Windows or Linux) exists in the Flutter project. bool existsSync(); /// 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; /// Included CMake with rules and variables for plugin builds. File get generatedPluginCmakeFile; /// The directory to write plugin symlinks. Directory get pluginSymlinkDirectory; } /// The Windows sub project. class WindowsProject extends FlutterProjectPlatform implements CmakeBasedProject { WindowsProject.fromFlutter(this.parent); @override final FlutterProject parent; @override String get pluginConfigKey => WindowsPlugin.kConfigKey; String get _childDirectory => 'windows'; @override bool existsSync() => _editableDirectory.existsSync() && cmakeFile.existsSync(); @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'); @override File get generatedPluginCmakeFile => managedDirectory.childFile('generated_plugins.cmake'); @override Directory get pluginSymlinkDirectory => ephemeralDirectory.childDirectory('.plugin_symlinks'); Directory get _editableDirectory => parent.directory.childDirectory(_childDirectory); /// The directory in the project that is managed by Flutter. As much as /// possible, files that are edited by Flutter tooling after initial project /// creation should live here. Directory get managedDirectory => _editableDirectory.childDirectory('flutter'); /// The subdirectory of [managedDirectory] that contains files that are /// generated on the fly. All generated files that are not intended to be /// checked in should live here. Directory get ephemeralDirectory => managedDirectory.childDirectory('ephemeral'); Future<void> ensureReadyForPlatformSpecificTooling() async {} } /// The Windows UWP version of the Windows project. class WindowsUwpProject extends WindowsProject { WindowsUwpProject.fromFlutter(FlutterProject parent) : super.fromFlutter(parent); @override String get _childDirectory => 'winuwp'; File get runnerCmakeFile => _editableDirectory.childDirectory('runner_uwp').childFile('CMakeLists.txt'); /// Eventually this will be used to check if the user's unstable project needs to be regenerated. int? get projectVersion => int.tryParse(_editableDirectory.childFile('project_version').readAsStringSync()); /// Retrieve the GUID of the UWP package. late final String? packageGuid = getCmakePackageGuid(runnerCmakeFile); File get appManifest => _editableDirectory.childDirectory('runner_uwp').childFile('appxmanifest.in'); late final String? packageVersion = parseAppVersion(this); } @visibleForTesting String? parseAppVersion(WindowsUwpProject project) { final File appManifestFile = project.appManifest; if (!appManifestFile.existsSync()) { return null; } XmlDocument document; try { document = XmlDocument.parse(appManifestFile.readAsStringSync()); } on XmlParserException { throwToolExit('Error parsing $appManifestFile. Please ensure that the appx manifest is a valid XML document and try again.'); } for (final XmlElement metaData in document.findAllElements('Identity')) { return metaData.getAttribute('Version'); } return null; } /// The Linux sub project. class LinuxProject extends FlutterProjectPlatform implements CmakeBasedProject { LinuxProject.fromFlutter(this.parent); @override final FlutterProject parent; @override String get pluginConfigKey => LinuxPlugin.kConfigKey; static final RegExp _applicationIdPattern = RegExp(r'''^\s*set\s*\(\s*APPLICATION_ID\s*"(.*)"\s*\)\s*$'''); Directory get _editableDirectory => parent.directory.childDirectory('linux'); /// The directory in the project that is managed by Flutter. As much as /// possible, files that are edited by Flutter tooling after initial project /// creation should live here. Directory get managedDirectory => _editableDirectory.childDirectory('flutter'); /// The subdirectory of [managedDirectory] that contains files that are /// generated on the fly. All generated files that are not intended to be /// checked in should live here. Directory get ephemeralDirectory => managedDirectory.childDirectory('ephemeral'); @override bool existsSync() => _editableDirectory.existsSync(); @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'); @override File get generatedPluginCmakeFile => managedDirectory.childFile('generated_plugins.cmake'); @override Directory get pluginSymlinkDirectory => ephemeralDirectory.childDirectory('.plugin_symlinks'); Future<void> ensureReadyForPlatformSpecificTooling() async {} String? get applicationId { return firstMatchInFile(cmakeFile, _applicationIdPattern)?.group(1); } }