diff --git a/.clang-format b/.clang-format index e0e6dfcaf..34d1b2fd1 100644 --- a/.clang-format +++ b/.clang-format @@ -1,183 +1,4 @@ -# This file is generated by the following command -# clang-format(v11.0.1, in vscode v1.55.0) -style=Google -dump-config > .clang-format +# DON'T delete this file as it helps developers format C++ code in +# IDEs such as VSCode. ---- -Language: Cpp -# BasedOnStyle: Google -AccessModifierOffset: -1 -AlignAfterOpenBracket: Align -AlignConsecutiveMacros: false -AlignConsecutiveAssignments: false -AlignConsecutiveBitFields: false -AlignConsecutiveDeclarations: false -AlignEscapedNewlines: Left -AlignOperands: Align -AlignTrailingComments: true -AllowAllArgumentsOnNextLine: true -AllowAllConstructorInitializersOnNextLine: true -AllowAllParametersOfDeclarationOnNextLine: true -AllowShortEnumsOnASingleLine: true -AllowShortBlocksOnASingleLine: Never -AllowShortCaseLabelsOnASingleLine: false -AllowShortFunctionsOnASingleLine: All -AllowShortLambdasOnASingleLine: All -AllowShortIfStatementsOnASingleLine: WithoutElse -AllowShortLoopsOnASingleLine: true -AlwaysBreakAfterDefinitionReturnType: None -AlwaysBreakAfterReturnType: None -AlwaysBreakBeforeMultilineStrings: true -AlwaysBreakTemplateDeclarations: Yes -BinPackArguments: true -BinPackParameters: true -BraceWrapping: - AfterCaseLabel: false - AfterClass: false - AfterControlStatement: Never - AfterEnum: false - AfterFunction: false - AfterNamespace: false - AfterObjCDeclaration: false - AfterStruct: false - AfterUnion: false - AfterExternBlock: false - BeforeCatch: false - BeforeElse: false - BeforeLambdaBody: false - BeforeWhile: false - IndentBraces: false - SplitEmptyFunction: true - SplitEmptyRecord: true - SplitEmptyNamespace: true -BreakBeforeBinaryOperators: None -BreakBeforeBraces: Attach -BreakBeforeInheritanceComma: false -BreakInheritanceList: BeforeColon -BreakBeforeTernaryOperators: true -BreakConstructorInitializersBeforeComma: false -BreakConstructorInitializers: BeforeColon -BreakAfterJavaFieldAnnotations: false -BreakStringLiterals: true -ColumnLimit: 80 -CommentPragmas: "^ IWYU pragma:" -CompactNamespaces: false -ConstructorInitializerAllOnOneLineOrOnePerLine: true -ConstructorInitializerIndentWidth: 4 -ContinuationIndentWidth: 4 -Cpp11BracedListStyle: true -DeriveLineEnding: true -DerivePointerAlignment: true -DisableFormat: false -ExperimentalAutoDetectBinPacking: false -FixNamespaceComments: true -ForEachMacros: - - foreach - - Q_FOREACH - - BOOST_FOREACH -IncludeBlocks: Regroup -IncludeCategories: - - Regex: '^' - Priority: 2 - SortPriority: 0 - - Regex: '^<.*\.h>' - Priority: 1 - SortPriority: 0 - - Regex: "^<.*" - Priority: 2 - SortPriority: 0 - - Regex: ".*" - Priority: 3 - SortPriority: 0 -IncludeIsMainRegex: "([-_](test|unittest))?$" -IncludeIsMainSourceRegex: "" -IndentCaseLabels: true -IndentCaseBlocks: false -IndentGotoLabels: true -IndentPPDirectives: None -IndentExternBlock: AfterExternBlock -IndentWidth: 2 -IndentWrappedFunctionNames: false -InsertTrailingCommas: None -JavaScriptQuotes: Leave -JavaScriptWrapImports: true -KeepEmptyLinesAtTheStartOfBlocks: false -MacroBlockBegin: "" -MacroBlockEnd: "" -MaxEmptyLinesToKeep: 1 -NamespaceIndentation: None -ObjCBinPackProtocolList: Never -ObjCBlockIndentWidth: 2 -ObjCBreakBeforeNestedBlockParam: true -ObjCSpaceAfterProperty: false -ObjCSpaceBeforeProtocolList: true -PenaltyBreakAssignment: 2 -PenaltyBreakBeforeFirstCallParameter: 1 -PenaltyBreakComment: 300 -PenaltyBreakFirstLessLess: 120 -PenaltyBreakString: 1000 -PenaltyBreakTemplateDeclaration: 10 -PenaltyExcessCharacter: 1000000 -PenaltyReturnTypeOnItsOwnLine: 200 -PointerAlignment: Left -RawStringFormats: - - Language: Cpp - Delimiters: - - cc - - CC - - cpp - - Cpp - - CPP - - "c++" - - "C++" - CanonicalDelimiter: "" - BasedOnStyle: google - - Language: TextProto - Delimiters: - - pb - - PB - - proto - - PROTO - EnclosingFunctions: - - EqualsProto - - EquivToProto - - PARSE_PARTIAL_TEXT_PROTO - - PARSE_TEST_PROTO - - PARSE_TEXT_PROTO - - ParseTextOrDie - - ParseTextProtoOrDie - - ParseTestProto - - ParsePartialTestProto - CanonicalDelimiter: "" - BasedOnStyle: google -ReflowComments: true -SortIncludes: true -SortUsingDeclarations: true -SpaceAfterCStyleCast: false -SpaceAfterLogicalNot: false -SpaceAfterTemplateKeyword: true -SpaceBeforeAssignmentOperators: true -SpaceBeforeCpp11BracedList: false -SpaceBeforeCtorInitializerColon: true -SpaceBeforeInheritanceColon: true -SpaceBeforeParens: ControlStatements -SpaceBeforeRangeBasedForLoopColon: true -SpaceInEmptyBlock: false -SpaceInEmptyParentheses: false -SpacesBeforeTrailingComments: 2 -SpacesInAngles: false -SpacesInConditionalStatement: false -SpacesInContainerLiterals: true -SpacesInCStyleCastParentheses: false -SpacesInParentheses: false -SpacesInSquareBrackets: false -SpaceBeforeSquareBrackets: false -Standard: Auto -StatementMacros: - - Q_UNUSED - - QT_REQUIRE_VERSION -TabWidth: 8 -UseCRLF: false -UseTab: Never -WhitespaceSensitiveMacros: - - STRINGIZE - - PP_STRINGIZE - - BOOST_PP_STRINGIZE +BasedOnStyle: Google diff --git a/.github/workflows/analyze.yml b/.github/workflows/analyze.yml index 25aff9b36..f77259b73 100644 --- a/.github/workflows/analyze.yml +++ b/.github/workflows/analyze.yml @@ -3,7 +3,7 @@ name: Analyze on: [push, pull_request] jobs: - dart: + dart_analyze: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -16,11 +16,9 @@ jobs: cd $d flutter pub get done - - name: Verify formatting - run: dart format --output=none --set-exit-if-changed packages - name: Analyze source code run: dart analyze --fatal-infos packages - clang: + format: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -31,5 +29,5 @@ jobs: run: | sudo apt-get update sudo apt-get install clang-format-11 - - name: Check tidy - run: ./tools/tools_runner.sh tidy --dir packages + - name: Check format + run: ./tools/tools_runner.sh format --fail-on-change --clang-format=clang-format-11 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5a4d6cc75..5699a99cb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,7 +14,7 @@ jobs: channel: stable - name: Find changed packages run: | - CHANGED_PACKAGES=$(./tools/tools_runner.sh plugins \ + CHANGED_PACKAGES=$(./tools/tools_runner.sh list \ --run-on-changed-packages \ --base-sha=$(git rev-parse HEAD^)) if [[ ! -z $CHANGED_PACKAGES ]]; then @@ -82,6 +82,6 @@ jobs: if: ${{ env.HAS_CHANGED_PACKAGES == 'true' }} run: | export PATH=`pwd`/flutter-tizen/bin:$PATH - ./tools/tools_runner.sh build \ + ./tools/tools_runner.sh build-examples \ --run-on-changed-packages \ --base-sha=$(git rev-parse HEAD^) diff --git a/.github/workflows/integration_test.yml b/.github/workflows/integration_test.yml index 07d34abba..8460786cd 100644 --- a/.github/workflows/integration_test.yml +++ b/.github/workflows/integration_test.yml @@ -26,7 +26,7 @@ jobs: if: ${{ github.event_name == 'pull_request' }} run: | export PATH=`pwd`/flutter-tizen/bin:$PATH - ./tools/tools_runner.sh test \ + ./tools/tools_runner.sh integration-test \ --recipe ./.github/recipe.yaml \ --generate-emulators \ --run-on-changed-packages \ @@ -35,6 +35,6 @@ jobs: if: ${{ github.event_name == 'push' }} run: | export PATH=`pwd`/flutter-tizen/bin:$PATH - ./tools/tools_runner.sh test \ + ./tools/tools_runner.sh integration-test \ --recipe ./.github/recipe.yaml \ --generate-emulators diff --git a/.gitignore b/.gitignore index d7560505f..3582a15fa 100644 --- a/.gitignore +++ b/.gitignore @@ -46,3 +46,6 @@ build/ .project .classpath .settings + +# Downloaded by the plugin tools. +google-java-format-1.3-all-deps.jar diff --git a/tools/analysis_options.yaml b/tools/analysis_options.yaml new file mode 100644 index 000000000..94e5a7bc9 --- /dev/null +++ b/tools/analysis_options.yaml @@ -0,0 +1,5 @@ +include: ../analysis_options.yaml + +linter: + rules: + implementation_imports: false diff --git a/tools/lib/src/build_examples_command.dart b/tools/lib/src/build_examples_command.dart new file mode 100644 index 000000000..b573ad0e8 --- /dev/null +++ b/tools/lib/src/build_examples_command.dart @@ -0,0 +1,77 @@ +// Copyright 2022 Samsung Electronics Co., Ltd. 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:flutter_plugin_tools/src/common/package_looping_command.dart'; +import 'package:flutter_plugin_tools/src/common/repository_package.dart'; + +/// A command to build the example applications for packages. +class BuildExamplesCommand extends PackageLoopingCommand { + /// Creates an instance of the build command. + BuildExamplesCommand(Directory packagesDir) : super(packagesDir); + + @override + String get description => 'Builds all example apps.\n\n' + 'This command requires "flutter-tizen" to be in your path.'; + + @override + String get name => 'build-examples'; + + @override + Future runForPackage(RepositoryPackage package) async { + final List errors = []; + + int exitCode = await processRunner.runAndStream( + 'flutter-tizen', + ['pub', 'get'], + workingDir: package.directory, + ); + if (exitCode != 0) { + errors.add('${package.displayName} (pub get failed)'); + return PackageResult.fail(errors); + } + + bool builtSomething = false; + for (final RepositoryPackage example in package.getExamples()) { + final String packageName = getRelativePosixPath( + example.directory, + from: packagesDir, + ); + + builtSomething = true; + // TODO(HakkyuKim): Support different profiles. + final int exitCode = await processRunner.runAndStream( + 'flutter-tizen', + [ + 'build', + 'tpk', + '--device-profile', + 'wearable', + '-v', + ], + workingDir: example.directory, + ); + if (exitCode != 0) { + errors.add(packageName); + } + } + + if (!builtSomething) { + errors.add('No examples found'); + } + + exitCode = await processRunner.runAndStream( + 'flutter-tizen', + ['clean'], + workingDir: package.directory, + ); + if (exitCode != 0) { + logWarning('Failed to clean ${package.displayName} after build.'); + } + + return errors.isEmpty + ? PackageResult.success() + : PackageResult.fail(errors); + } +} diff --git a/tools/lib/src/integration_test_command.dart b/tools/lib/src/integration_test_command.dart new file mode 100644 index 000000000..0430343b4 --- /dev/null +++ b/tools/lib/src/integration_test_command.dart @@ -0,0 +1,94 @@ +// Copyright 2022 Samsung Electronics Co., Ltd. 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:flutter_plugin_tools/src/common/core.dart'; +import 'package:flutter_plugin_tools/src/common/plugin_command.dart'; + +/// A command that runs integration test of plugin examples. +class IntegrationTestCommand extends PluginCommand { + /// Creates an instance of the integration-test command, which runs + /// integration tests of each plugin example on physical or emulator devices. + IntegrationTestCommand(Directory packagesDir) : super(packagesDir) { + argParser.addFlag( + _generateEmulatorsArg, + help: 'Create and destroy emulators during test.\n' + 'Must provide either $_platformsArg or $_recipeArg option to specify ' + 'which platforms to create.', + ); + argParser.addOption( + _platformsArg, + help: 'Run integration test on all connected devices that satisfy ' + 'profile-version (ex: wearable-5.5, tv-6.0).\n' + 'Selected devices will be used to test all plugins. If you wish to ' + 'run different devices for each plugin, use $_recipeArg instead.', + valueHelp: 'profile-version', + ); + argParser.addOption( + _recipeArg, + help: + 'The recipe file path. A recipe refers to a yaml file that defines ' + 'a list of target platforms to test for each plugin.\n' + 'Pass this file if you want to select specific target platforms ' + 'for different plugins. Every package listed in the recipe file ' + 'will be recognized by the tool(same as $_packagesArg option) ' + 'and those that specify an empty list will be explicitly excluded' + '(same as $_excludeArg option). If $_recipeArg is used, ' + '$_packagesArg and $_excludeArg options will be ignored.\n\n' + 'plugins:\n' + ' a: [wearable-5.5, tv-6.0]\n' + ' b: [mobile-6.0]\n' + ' c: [wearable-4.0]\n' + ' d: [] # explicitly excluded\n', + valueHelp: 'recipe.yaml', + ); + argParser.addOption( + _timeoutArg, + help: 'Timeout limit of each integration test in seconds.', + valueHelp: 'seconds', + defaultsTo: '120', + ); + } + + /// Copied from [PluginCommand]. + static const String _excludeArg = 'exclude'; + + /// Copied from [PluginCommand]. + static const String _packagesArg = 'packages'; + + static const String _generateEmulatorsArg = 'generate-emulators'; + static const String _platformsArg = 'platforms'; + static const String _recipeArg = 'recipe'; + static const String _timeoutArg = 'timeout'; + + @override + String get description => + 'Runs integration tests for plugin example apps.\n\n' + 'This command requires "flutter-tizen" to be in your path.'; + + @override + String get name => 'integration-test'; + + File get _pythonTool => packagesDir.parent + .childDirectory('tools') + .childDirectory('tools') + .childFile('run_command.py'); + + @override + Future run() async { + if (!_pythonTool.existsSync()) { + print('Error: Cannot find ${_pythonTool.path}.'); + throw ToolExit(1); + } + + // TODO(HakkyuKim): Migrate python tool logic to dart. + final int exitCode = await processRunner.runAndStream( + _pythonTool.path, + ['test', ...argResults!.arguments], + ); + if (exitCode != 0) { + throw ToolExit(exitCode); + } + } +} diff --git a/tools/lib/src/main.dart b/tools/lib/src/main.dart index ff108204c..4937c29b6 100644 --- a/tools/lib/src/main.dart +++ b/tools/lib/src/main.dart @@ -3,15 +3,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// ignore_for_file: implementation_imports - import 'dart:io' as io; +import 'package:args/command_runner.dart'; import 'package:file/file.dart'; import 'package:file/local.dart'; - import 'package:flutter_plugin_tools/src/common/core.dart'; -import 'package:flutter_plugin_tools/src/common/process_runner.dart'; +import 'package:flutter_plugin_tools/src/format_command.dart'; +import 'package:flutter_plugin_tools/src/list_command.dart'; + +import 'build_examples_command.dart'; +import 'integration_test_command.dart'; void main(List args) { const FileSystem fileSystem = LocalFileSystem(); @@ -28,19 +30,15 @@ void main(List args) { } } - const ProcessRunner processRunner = ProcessRunner(); - final Directory sourceTreeRoot = packagesDir.parent; - final File pythonTool = sourceTreeRoot - .childDirectory('tools') - .childDirectory('tools') - .childFile('run_command.py'); - - if (!pythonTool.existsSync()) { - print('Error: Cannot find ${pythonTool.path}.'); - io.exit(1); - } + final CommandRunner commandRunner = CommandRunner( + './tools/tools_runner.sh', + 'Productivity utils for hosting multiple plugins within one repository.') + ..addCommand(BuildExamplesCommand(packagesDir)) + ..addCommand(FormatCommand(packagesDir)) + ..addCommand(IntegrationTestCommand(packagesDir)) + ..addCommand(ListCommand(packagesDir)); - processRunner.runAndStream(pythonTool.path, args).catchError((Object e) { + commandRunner.run(args).catchError((Object e) { final ToolExit toolExit = e as ToolExit; int exitCode = toolExit.exitCode; // This should never happen; this check is here to guarantee that a ToolExit diff --git a/tools/pubspec.yaml b/tools/pubspec.yaml index ef389736e..83ee28522 100644 --- a/tools/pubspec.yaml +++ b/tools/pubspec.yaml @@ -7,5 +7,6 @@ environment: sdk: '>=2.15.0 <3.0.0' dependencies: + args: ^2.1.0 file: ^6.1.0 flutter_plugin_tools: ^0.7.3