diff --git a/dev/tools/localization/bin/gen_l10n.dart b/dev/tools/localization/bin/gen_l10n.dart index 195b860c80..8b21cfabd0 100644 --- a/dev/tools/localization/bin/gen_l10n.dart +++ b/dev/tools/localization/bin/gen_l10n.dart @@ -120,6 +120,14 @@ void main(List arguments) { '\n\n' 'When null, the JSON file will not be generated.' ); + parser.addOption( + 'project-dir', + valueHelp: 'absolute/path/to/flutter/project', + help: 'When specified, the tool uses the path passed into this option ' + 'as the directory of the root Flutter project.' + '\n\n' + 'When null, the relative path to the present working directory will be used.' + ); final argslib.ArgResults results = parser.parse(arguments); if (results['help'] == true) { @@ -140,6 +148,7 @@ void main(List arguments) { final String headerFile = results['header-file'] as String; final bool useDeferredLoading = results['use-deferred-loading'] as bool; final String inputsAndOutputsListPath = results['gen-inputs-and-outputs-list'] as String; + final String projectPathString = results['project-dir'] as String; const local.LocalFileSystem fs = local.LocalFileSystem(); final LocalizationsGenerator localizationsGenerator = LocalizationsGenerator(fs); @@ -157,6 +166,7 @@ void main(List arguments) { headerFile: headerFile, useDeferredLoading: useDeferredLoading, inputsAndOutputsListPath: inputsAndOutputsListPath, + projectPathString: projectPathString, ) ..loadResources() ..writeOutputFiles() diff --git a/dev/tools/localization/gen_l10n.dart b/dev/tools/localization/gen_l10n.dart index dfab847ec7..b4373e8411 100644 --- a/dev/tools/localization/gen_l10n.dart +++ b/dev/tools/localization/gen_l10n.dart @@ -403,6 +403,11 @@ class LocalizationsGenerator { /// This directory is specified with the [initialize] method. Directory inputDirectory; + /// The Flutter project's root directory. + /// + /// This directory is specified with the [initialize] method. + Directory projectDirectory; + /// The directory to generate the project's localizations files in. /// /// It is assumed that all output files (e.g. The localizations @@ -518,7 +523,9 @@ class LocalizationsGenerator { String headerFile, bool useDeferredLoading = false, String inputsAndOutputsListPath, + String projectPathString, }) { + setProjectDir(projectPathString); setInputDirectory(inputPathString); setOutputDirectory(outputPathString ?? inputPathString); setTemplateArbFile(templateArbFileName); @@ -544,22 +551,44 @@ class LocalizationsGenerator { return !(statString[1] == 'w' || statString[4] == 'w' || statString[7] == 'w'); } + @visibleForTesting + void setProjectDir(String projectPathString) { + if (projectPathString == null) { + return; + } + + final Directory directory = _fs.directory(projectPathString); + if (!directory.existsSync()) { + throw L10nException( + 'Directory does not exist: $directory.\n' + 'Please select a directory that contains the project\'s localizations ' + 'resource files.' + ); + } + projectDirectory = directory; + } + /// Sets the reference [Directory] for [inputDirectory]. @visibleForTesting void setInputDirectory(String inputPathString) { if (inputPathString == null) throw L10nException('inputPathString argument cannot be null'); - inputDirectory = _fs.directory(inputPathString); + inputDirectory = _fs.directory( + projectDirectory != null + ? _getAbsoluteProjectPath(inputPathString) + : inputPathString + ); + if (!inputDirectory.existsSync()) throw FileSystemException( - "The 'input-dir' directory, '$inputDirectory', does not exist.\n" + "The 'arb-dir' directory, '$inputDirectory', does not exist.\n" 'Make sure that the correct path was provided.' ); final FileStat fileStat = inputDirectory.statSync(); if (_isNotReadable(fileStat) || _isNotWritable(fileStat)) throw FileSystemException( - "The 'input-dir' directory, '$inputDirectory', doesn't allow reading and writing.\n" + "The 'arb-dir' directory, '$inputDirectory', doesn't allow reading and writing.\n" 'Please ensure that the user has read and write permissions.' ); } @@ -569,7 +598,11 @@ class LocalizationsGenerator { void setOutputDirectory(String outputPathString) { if (outputPathString == null) throw L10nException('outputPathString argument cannot be null'); - outputDirectory = _fs.directory(outputPathString); + outputDirectory = _fs.directory( + projectDirectory != null + ? _getAbsoluteProjectPath(outputPathString) + : outputPathString + ); } /// Sets the reference [File] for [templateArbFile]. @@ -667,6 +700,8 @@ class LocalizationsGenerator { } } + String _getAbsoluteProjectPath(String relativePath) => _fs.path.join(projectDirectory.path, relativePath); + void _setUseDeferredLoading(bool useDeferredLoading) { if (useDeferredLoading == null) { throw L10nException('useDeferredLoading argument cannot be null.'); diff --git a/dev/tools/test/localization/gen_l10n_test.dart b/dev/tools/test/localization/gen_l10n_test.dart index 5105e66372..4e97537f72 100644 --- a/dev/tools/test/localization/gen_l10n_test.dart +++ b/dev/tools/test/localization/gen_l10n_test.dart @@ -193,6 +193,95 @@ void main() { ); }); + test('sets absolute path of the target Flutter project', () { + // Set up project directory. + final Directory l10nDirectory = fs.currentDirectory + .childDirectory('absolute') + .childDirectory('path') + .childDirectory('to') + .childDirectory('flutter_project') + .childDirectory('lib') + .childDirectory('l10n') + ..createSync(recursive: true); + l10nDirectory.childFile(defaultTemplateArbFileName) + .writeAsStringSync(singleMessageArbFileString); + l10nDirectory.childFile(esArbFileName) + .writeAsStringSync(singleEsMessageArbFileString); + + // Run localizations generator in specified absolute path. + final LocalizationsGenerator generator = LocalizationsGenerator(fs); + final String flutterProjectPath = path.join('absolute', 'path', 'to', 'flutter_project'); + try { + generator.initialize( + projectPathString: flutterProjectPath, + inputPathString: defaultL10nPathString, + outputPathString: defaultL10nPathString, + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + ); + generator.loadResources(); + generator.writeOutputFiles(); + } on L10nException catch (e) { + throw TestFailure('Unexpected failure during test setup: ${e.message}'); + } on Exception catch (e) { + throw TestFailure('Unexpected failure during test setup: $e'); + } + + // Output files should be generated in the provided absolute path. + expect( + fs.isFileSync(path.join( + flutterProjectPath, + 'lib', + 'l10n', + 'output-localization-file_en.dart', + )), + true, + ); + expect( + fs.isFileSync(path.join( + flutterProjectPath, + 'lib', + 'l10n', + 'output-localization-file_es.dart', + )), + true, + ); + }); + + test('throws error when directory at absolute path does not exist', () { + // Set up project directory. + final Directory l10nDirectory = fs.currentDirectory + .childDirectory('lib') + .childDirectory('l10n') + ..createSync(recursive: true); + l10nDirectory.childFile(defaultTemplateArbFileName) + .writeAsStringSync(singleMessageArbFileString); + l10nDirectory.childFile(esArbFileName) + .writeAsStringSync(singleEsMessageArbFileString); + + // Project path should be intentionally a directory that does not exist. + final LocalizationsGenerator generator = LocalizationsGenerator(fs); + try { + generator.initialize( + projectPathString: 'absolute/path/to/flutter_project', + inputPathString: defaultL10nPathString, + outputPathString: defaultL10nPathString, + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + ); + } on L10nException catch (e) { + expect(e.message, contains('Directory does not exist')); + return; + } + + fail( + 'An exception should be thrown when the directory ' + 'specified in projectPathString does not exist.' + ); + }); + group('className should only take valid Dart class names', () { LocalizationsGenerator generator; setUp(() { diff --git a/packages/flutter_tools/lib/src/build_system/targets/localizations.dart b/packages/flutter_tools/lib/src/build_system/targets/localizations.dart index fba14943ab..166766e313 100644 --- a/packages/flutter_tools/lib/src/build_system/targets/localizations.dart +++ b/packages/flutter_tools/lib/src/build_system/targets/localizations.dart @@ -36,11 +36,13 @@ Future generateLocalizations({ 'bin', 'gen_l10n.dart', ); + final ProcessResult result = await processManager.run([ dartBinaryPath, '--disable-dart-dev', genL10nPath, '--gen-inputs-and-outputs-list=${dependenciesDir.path}', + '--project-dir=${projectDir.path}', if (options.arbDirectory != null) '--arb-dir=${options.arbDirectory.toFilePath()}', if (options.templateArbFile != null) diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/localizations_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/localizations_test.dart index e224089a1e..2482b96571 100644 --- a/packages/flutter_tools/test/general.shard/build_system/targets/localizations_test.dart +++ b/packages/flutter_tools/test/general.shard/build_system/targets/localizations_test.dart @@ -17,13 +17,15 @@ void main() { testWithoutContext('generateLocalizations forwards arguments correctly', () async { final FileSystem fileSystem = MemoryFileSystem.test(); final Logger logger = BufferLogger.test(); + final String projectDir = fileSystem.path.join('path', 'to', 'flutter_project'); final FakeProcessManager processManager = FakeProcessManager.list([ - const FakeCommand( + FakeCommand( command: [ 'dart', '--disable-dart-dev', 'dev/tools/localization/bin/gen_l10n.dart', '--gen-inputs-and-outputs-list=/', + '--project-dir=$projectDir', '--arb-dir=arb', '--template-arb-file=example.arb', '--output-localization-file=bar', @@ -36,7 +38,11 @@ void main() { ], ), ]); - final Directory arbDirectory = fileSystem.directory('arb') + final Directory flutterProjectDirectory = fileSystem + .directory(fileSystem.path.join('path', 'to', 'flutter_project')) + ..createSync(recursive: true); + final Directory arbDirectory = flutterProjectDirectory + .childDirectory('arb') ..createSync(); arbDirectory.childFile('foo.arb').createSync(); arbDirectory.childFile('bar.arb').createSync(); @@ -57,7 +63,7 @@ void main() { logger: logger, fileSystem: fileSystem, processManager: processManager, - projectDir: fileSystem.currentDirectory, + projectDir: flutterProjectDirectory, dartBinaryPath: 'dart', flutterRoot: '', dependenciesDir: fileSystem.currentDirectory, diff --git a/packages/flutter_tools/test/general.shard/resident_runner_test.dart b/packages/flutter_tools/test/general.shard/resident_runner_test.dart index b6194d02df..2726bf8624 100644 --- a/packages/flutter_tools/test/general.shard/resident_runner_test.dart +++ b/packages/flutter_tools/test/general.shard/resident_runner_test.dart @@ -1189,6 +1189,7 @@ void main() { '--disable-dart-dev', globals.fs.path.join(Cache.flutterRoot, 'dev', 'tools', 'localization', 'bin', 'gen_l10n.dart'), '--gen-inputs-and-outputs-list=${dependencies.absolute.path}', + '--project-dir=${globals.fs.currentDirectory.path}', ], onRun: () { dependencies @@ -1218,6 +1219,7 @@ void main() { '--disable-dart-dev', globals.fs.path.join(Cache.flutterRoot, 'dev', 'tools', 'localization', 'bin', 'gen_l10n.dart'), '--gen-inputs-and-outputs-list=${dependencies.absolute.path}', + '--project-dir=${globals.fs.currentDirectory.path}', ], exitCode: 1, stderr: 'stderr'