// 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:pub_semver/pub_semver.dart'; import 'build_info.dart'; import 'cmake_project.dart'; import 'globals.dart' as globals; /// Extracts the `BINARY_NAME` from a project's CMake file. /// /// Returns `null` if it cannot be found. String? getCmakeExecutableName(CmakeBasedProject project) { if (!project.cmakeFile.existsSync()) { return null; } final RegExp nameSetPattern = RegExp(r'^\s*set\(BINARY_NAME\s*"(.*)"\s*\)\s*$'); for (final String line in project.cmakeFile.readAsLinesSync()) { final RegExpMatch? match = nameSetPattern.firstMatch(line); if (match != null) { return match.group(1); } } return null; } String _escapeBackslashes(String s) { return s.replaceAll(r'\', r'\\'); } String _determineVersionString(CmakeBasedProject project, BuildInfo buildInfo) { // Prefer the build arguments for version information. final String buildName = buildInfo.buildName ?? project.parent.manifest.buildName ?? '1.0.0'; final String? buildNumber = buildInfo.buildName != null ? buildInfo.buildNumber : (buildInfo.buildNumber ?? project.parent.manifest.buildNumber); return buildNumber != null ? '$buildName+$buildNumber' : buildName; } Version _determineVersion(CmakeBasedProject project, BuildInfo buildInfo) { final String version = _determineVersionString(project, buildInfo); try { return Version.parse(version); } on FormatException { globals.printWarning('Warning: could not parse version $version, defaulting to 1.0.0.'); return Version(1, 0, 0); } } /// Attempts to map a Dart version's build identifier (the part after a +) into /// a single integer. Returns null for complex build identifiers like `foo` or `1.2`. int? _tryDetermineBuildVersion(Version version) { if (version.build.isEmpty) { return 0; } if (version.build.length != 1) { return null; } final Object buildIdentifier = version.build.first as Object; return buildIdentifier is int ? buildIdentifier : null; } /// Writes a generated CMake configuration file for [project], including /// variables expected by the build template and an environment variable list /// for calling back into Flutter. void writeGeneratedCmakeConfig( String flutterRoot, CmakeBasedProject project, BuildInfo buildInfo, Map<String, String> environment) { // Only a limited set of variables are needed by the CMake files themselves, // the rest are put into a list to pass to the re-entrant build step. final String escapedFlutterRoot = _escapeBackslashes(flutterRoot); final String escapedProjectDir = _escapeBackslashes(project.parent.directory.path); final Version version = _determineVersion(project, buildInfo); final int? buildVersion = _tryDetermineBuildVersion(version); // Since complex Dart build identifiers cannot be converted into integers, // different Dart versions may be converted into the same Windows numeric version. // Warn the user as some Windows installers, like MSI, don't update files if their versions are equal. if (buildVersion == null && project is WindowsProject) { final String buildIdentifier = version.build.join('.'); globals.printWarning( 'Warning: build identifier $buildIdentifier in version $version is not numeric ' 'and cannot be converted into a Windows build version number. Defaulting to 0.\n' 'This may cause issues with Windows installers.' ); } final StringBuffer buffer = StringBuffer(''' # Generated code do not commit. file(TO_CMAKE_PATH "$escapedFlutterRoot" FLUTTER_ROOT) file(TO_CMAKE_PATH "$escapedProjectDir" PROJECT_DIR) set(FLUTTER_VERSION "$version" PARENT_SCOPE) set(FLUTTER_VERSION_MAJOR ${version.major} PARENT_SCOPE) set(FLUTTER_VERSION_MINOR ${version.minor} PARENT_SCOPE) set(FLUTTER_VERSION_PATCH ${version.patch} PARENT_SCOPE) set(FLUTTER_VERSION_BUILD ${buildVersion ?? 0} PARENT_SCOPE) # Environment variables to pass to tool_backend.sh list(APPEND FLUTTER_TOOL_ENVIRONMENT "FLUTTER_ROOT=$escapedFlutterRoot" "PROJECT_DIR=$escapedProjectDir" '''); environment.forEach((String key, String value) { final String configValue = _escapeBackslashes(value); buffer.writeln(' "$key=$configValue"'); }); buffer.writeln(')'); project.generatedCmakeConfigFile ..createSync(recursive: true) ..writeAsStringSync(buffer.toString()); }