// 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/terminal.dart'; import 'package:flutter_tools/src/cmake_project.dart'; import 'package:flutter_tools/src/migrations/cmake_custom_command_migration.dart'; import 'package:flutter_tools/src/migrations/cmake_native_assets_migration.dart'; import 'package:test/fake.dart'; import '../../src/common.dart'; void main() { group('CMake project migration', () { group('migrate add_custom_command() to use VERBATIM', () { late MemoryFileSystem memoryFileSystem; late BufferLogger testLogger; late FakeCmakeProject mockCmakeProject; late File managedCmakeFile; setUp(() { memoryFileSystem = MemoryFileSystem.test(); managedCmakeFile = memoryFileSystem.file('CMakeLists.txtx'); testLogger = BufferLogger( terminal: Terminal.test(), outputPreferences: OutputPreferences.test(), ); mockCmakeProject = FakeCmakeProject(managedCmakeFile); }); testWithoutContext('skipped if files are missing', () async { final cmakeProjectMigration = CmakeCustomCommandMigration(mockCmakeProject, testLogger); await cmakeProjectMigration.migrate(); 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', () async { const contents = 'Nothing to migrate'; managedCmakeFile.writeAsStringSync(contents); final DateTime projectLastModified = managedCmakeFile.lastModifiedSync(); final cmakeProjectMigration = CmakeCustomCommandMigration(mockCmakeProject, testLogger); await cmakeProjectMigration.migrate(); expect(managedCmakeFile.lastModifiedSync(), projectLastModified); expect(managedCmakeFile.readAsStringSync(), contents); expect(testLogger.statusText, isEmpty); }); testWithoutContext('skipped if already migrated', () async { const 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" ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} VERBATIM ) '''; managedCmakeFile.writeAsStringSync(contents); final DateTime projectLastModified = managedCmakeFile.lastModifiedSync(); final cmakeProjectMigration = CmakeCustomCommandMigration(mockCmakeProject, testLogger); await cmakeProjectMigration.migrate(); expect(managedCmakeFile.lastModifiedSync(), projectLastModified); expect(managedCmakeFile.readAsStringSync(), contents); expect(testLogger.statusText, isEmpty); }); testWithoutContext('is migrated to use VERBATIM', () async { 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" ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} ) '''); final cmakeProjectMigration = CmakeCustomCommandMigration(mockCmakeProject, testLogger); await cmakeProjectMigration.migrate(); 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" ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} VERBATIM ) '''); expect( testLogger.statusText, contains('add_custom_command() missing VERBATIM or FLUTTER_TARGET_PLATFORM, updating.'), ); }); testWithoutContext('is migrated to use FLUTTER_TARGET_PLATFORM', () async { 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} VERBATIM ) '''); final cmakeProjectMigration = CmakeCustomCommandMigration(mockCmakeProject, testLogger); await cmakeProjectMigration.migrate(); 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" ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} VERBATIM ) '''); expect( testLogger.statusText, contains('add_custom_command() missing VERBATIM or FLUTTER_TARGET_PLATFORM, updating.'), ); }); }); group('migrate add install() NATIVE_ASSETS_DIR command', () { late MemoryFileSystem memoryFileSystem; late BufferLogger testLogger; late FakeCmakeProject mockCmakeProject; late File managedCmakeFile; setUp(() { memoryFileSystem = MemoryFileSystem.test(); managedCmakeFile = memoryFileSystem.file('CMakeLists.txtx'); testLogger = BufferLogger( terminal: Terminal.test(), outputPreferences: OutputPreferences.test(), ); mockCmakeProject = FakeCmakeProject(managedCmakeFile); }); testWithoutContext('skipped if files are missing', () async { final cmakeProjectMigration = CmakeNativeAssetsMigration( mockCmakeProject, 'linux', testLogger, ); await cmakeProjectMigration.migrate(); expect(managedCmakeFile.existsSync(), isFalse); expect( testLogger.traceText, contains('CMake project not found, skipping install() NATIVE_ASSETS_DIR migration.'), ); expect(testLogger.statusText, isEmpty); }); testWithoutContext('skipped if nothing to migrate', () async { const contents = 'Nothing to migrate'; managedCmakeFile.writeAsStringSync(contents); final DateTime projectLastModified = managedCmakeFile.lastModifiedSync(); final cmakeProjectMigration = CmakeNativeAssetsMigration( mockCmakeProject, 'linux', testLogger, ); await cmakeProjectMigration.migrate(); expect(managedCmakeFile.lastModifiedSync(), projectLastModified); expect(managedCmakeFile.readAsStringSync(), contents); expect(testLogger.statusText, isEmpty); }); testWithoutContext('skipped if already migrated', () async { const contents = r''' # Copy the native assets provided by the build.dart from all packages. set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/") install(DIRECTORY "${NATIVE_ASSETS_DIR}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" COMPONENT Runtime) '''; managedCmakeFile.writeAsStringSync(contents); final DateTime projectLastModified = managedCmakeFile.lastModifiedSync(); final cmakeProjectMigration = CmakeNativeAssetsMigration( mockCmakeProject, 'linux', testLogger, ); await cmakeProjectMigration.migrate(); expect(managedCmakeFile.lastModifiedSync(), projectLastModified); expect(managedCmakeFile.readAsStringSync(), contents); expect(testLogger.statusText, isEmpty); }); for (final os in ['linux', 'windows']) { testWithoutContext('is migrated to copy native assets', () async { managedCmakeFile.writeAsStringSync(r''' foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) install(FILES "${bundled_library}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" COMPONENT Runtime) endforeach(bundled_library) # Fully re-copy the assets directory on each build to avoid having stale files # from a previous install. set(FLUTTER_ASSET_DIR_NAME "flutter_assets") install(CODE " file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") " COMPONENT Runtime) install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) '''); final cmakeProjectMigration = CmakeNativeAssetsMigration( mockCmakeProject, os, testLogger, ); await cmakeProjectMigration.migrate(); expect(managedCmakeFile.readAsStringSync(), ''' foreach(bundled_library \${PLUGIN_BUNDLED_LIBRARIES}) install(FILES "\${bundled_library}" DESTINATION "\${INSTALL_BUNDLE_LIB_DIR}" COMPONENT Runtime) endforeach(bundled_library) # Copy the native assets provided by the build.dart from all packages. set(NATIVE_ASSETS_DIR "\${PROJECT_BUILD_DIR}native_assets/$os/") install(DIRECTORY "\${NATIVE_ASSETS_DIR}" DESTINATION "\${INSTALL_BUNDLE_LIB_DIR}" COMPONENT Runtime) # Fully re-copy the assets directory on each build to avoid having stale files # from a previous install. set(FLUTTER_ASSET_DIR_NAME "flutter_assets") install(CODE " file(REMOVE_RECURSE \\"\${INSTALL_BUNDLE_DATA_DIR}/\${FLUTTER_ASSET_DIR_NAME}\\") " COMPONENT Runtime) install(DIRECTORY "\${PROJECT_BUILD_DIR}/\${FLUTTER_ASSET_DIR_NAME}" DESTINATION "\${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) '''); expect( testLogger.statusText, contains('CMake missing install() NATIVE_ASSETS_DIR command, updating.'), ); }); } }); }); } class FakeCmakeProject extends Fake implements CmakeBasedProject { FakeCmakeProject(this.managedCmakeFile); @override final File managedCmakeFile; }