diff --git a/CHANGELOG.md b/CHANGELOG.md index b98b225..914a1cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,12 @@ +## 0.0.3 + +* Using `package:args` for handling CLI arguments. +* Dependency versions increased. +* Corrected wrong cli command suggestions in case of un-locateable dylibs. +* Fixed throughput benchmark's `RangeError` in case of 0 result. + ## 0.0.2 + * Added support for MacOS. ## 0.0.1+1 diff --git a/README.md b/README.md index 8f41d7d..7d53f4b 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ dart run benchmark/throughput.dart # For parallel requests benchmark. dart run benchmark/run_all.dart # To run all the benchmarks and get reports. ``` -All the benchmarking scripts take test server url as a cli argument. `throughput.dart` and `run_all.dart` also take `N` where `2^N` is the maximum possible parallel requests and the max duration for each run to complete in seconds. +Use `-h` to see available cli arguments and usage informations. To know how to setup local test servers, read [benchmarking guide](benchmark/benchmarking.md). diff --git a/benchmark/latency.dart b/benchmark/latency.dart index 6c8777f..e21d175 100644 --- a/benchmark/latency.dart +++ b/benchmark/latency.dart @@ -4,6 +4,7 @@ import 'dart:io' as io; +import 'package:args/args.dart'; import 'package:cronet/cronet.dart'; abstract class LatencyBenchmark { @@ -119,11 +120,24 @@ class CronetLatencyBenchmark extends LatencyBenchmark { } void main(List args) async { - // Accepts test url as optional cli parameter. - var url = 'https://example.com'; - if (args.isNotEmpty) { - url = args[0]; + final parser = ArgParser(); + parser + ..addOption('url', + abbr: 'u', + help: 'The server to ping for running this benchmark.', + defaultsTo: 'https://example.com') + ..addFlag('help', + abbr: 'h', negatable: false, help: 'Print this usage information.'); + final arguments = parser.parse(args); + if (arguments.wasParsed('help')) { + print(parser.usage); + return; } + if (arguments.rest.isNotEmpty) { + print(parser.usage); + throw ArgumentError(); + } + final url = arguments['url'] as String; // TODO: https://github.com/google/cronet.dart/issues/11 await CronetLatencyBenchmark.main(url); // Used as an delemeter while parsing output in run_all script. diff --git a/benchmark/run_all.dart b/benchmark/run_all.dart index 0407793..3e319fa 100644 --- a/benchmark/run_all.dart +++ b/benchmark/run_all.dart @@ -6,6 +6,8 @@ import 'dart:convert'; import 'dart:io'; import 'dart:math'; +import 'package:args/args.dart'; + import 'latency.dart'; import 'throughput.dart'; @@ -24,18 +26,36 @@ List throughputParserHelper(String aotThroughputStdout) { } void main(List args) async { - var url = 'https://example.com'; - var throughputPrallelLimit = 10; - var duration = const Duration(seconds: 1); - if (args.isNotEmpty) { - url = args[0]; - } - if (args.length > 1) { - throughputPrallelLimit = int.parse(args[1]).toInt(); + final parser = ArgParser(); + parser + ..addOption('url', + abbr: 'u', + help: 'The server to ping for running this benchmark.', + defaultsTo: 'https://example.com') + ..addOption('limit', + abbr: 'l', + help: 'Limits the maximum number of parallel requests to 2^N where N' + ' is provided through this option.', + defaultsTo: '10') + ..addOption('time', + abbr: 't', + help: 'Maximum second(s) the benchmark should wait for each request.', + defaultsTo: '1') + ..addFlag('help', + abbr: 'h', negatable: false, help: 'Print this usage information.'); + final arguments = parser.parse(args); + if (arguments.wasParsed('help')) { + print(parser.usage); + return; } - if (args.length > 2) { - duration = Duration(seconds: int.parse(args[2])); + if (arguments.rest.isNotEmpty) { + print(parser.usage); + throw ArgumentError(); } + final url = arguments['url'] as String; + final throughputPrallelLimit = int.parse(arguments['limit'] as String); + final duration = Duration(seconds: int.parse(arguments['time'] as String)); + print('Latency Test against: $url'); print('JIT'); final jitCronetLatency = await CronetLatencyBenchmark.main(url); @@ -43,8 +63,9 @@ void main(List args) async { print('AOT'); print('Compiling...'); - Process.runSync('dart', ['compile', 'exe', 'benchmarks/latency.dart']); - final aotLantencyProc = await Process.start('benchmarks/latency.exe', [url]); + Process.runSync('dart', ['compile', 'exe', 'benchmark/latency.dart']); + final aotLantencyProc = + await Process.start('benchmark/latency.exe', ['-u', url]); stderr.addStream(aotLantencyProc.stderr); var latencyStdout = ''; await for (final chunk in aotLantencyProc.stdout.transform(utf8.decoder)) { @@ -67,9 +88,15 @@ void main(List args) async { print('AOT'); print('Compiling...'); - Process.runSync('dart', ['compile', 'exe', 'benchmarks/throughput.dart']); - final aotThroughputProc = await Process.start('benchmarks/throughput.exe', - [url, throughputPrallelLimit.toString(), duration.inSeconds.toString()]); + Process.runSync('dart', ['compile', 'exe', 'benchmark/throughput.dart']); + final aotThroughputProc = await Process.start('benchmark/throughput.exe', [ + '-u', + url, + '-l', + throughputPrallelLimit.toString(), + '-t', + duration.inSeconds.toString() + ]); stderr.addStream(aotThroughputProc.stderr); var throughputStdout = ''; await for (final chunk in aotThroughputProc.stdout.transform(utf8.decoder)) { @@ -90,7 +117,7 @@ void main(List args) async { ' ${jitDartIOLatency.toStringAsFixed(3)} ms |'); print('| AOT | ${aotCronetLatency.toStringAsFixed(3)} ms |' ' ${aotDartIOLatency.toStringAsFixed(3)} ms |'); - print('\n\nThroughput Test Results (Duration ${duration.inSeconds}'); + print('\nThroughput Test Results (Duration: ${duration.inSeconds}s)'); print('| Mode | package:cronet | dart:io |'); print('| :-----------: |:--------------: | :-----------: |'); print('| JIT | ${jitCronetThroughput[1]} out of' diff --git a/benchmark/throughput.dart b/benchmark/throughput.dart index 5c072bf..6aff3bb 100644 --- a/benchmark/throughput.dart +++ b/benchmark/throughput.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'dart:io' as io; import 'dart:math'; +import 'package:args/args.dart'; import 'package:cronet/cronet.dart'; abstract class ThroughputBenchmark { @@ -56,7 +57,7 @@ abstract class ThroughputBenchmark { Future> report() async { var maxReturn = 0; - var throughput = List.empty(); + var throughput = [0, 0]; // Run the benchmark for 1, 2, 4...spawnThreshold. for (int currentThreshold = 1; currentThreshold <= spawnThreshold; @@ -151,19 +152,36 @@ class CronetThroughputBenchmark extends ThroughputBenchmark { } void main(List args) async { - // Accepts test url & parallel request threshold as optional cli parameter. - var url = 'https://example.com'; - var spawnThreshold = 1024; - var duration = const Duration(seconds: 1); - if (args.isNotEmpty) { - url = args[0]; - } - if (args.length > 1) { - spawnThreshold = pow(2, int.parse(args[1])).toInt(); - } - if (args.length > 2) { - duration = Duration(seconds: int.parse(args[2])); - } + final parser = ArgParser(); + parser + ..addOption('url', + abbr: 'u', + help: 'The server to ping for running this benchmark.', + defaultsTo: 'https://example.com') + ..addOption('limit', + abbr: 'l', + help: 'Limits the maximum number of parallel requests to 2^N where N' + ' is provided through this option.', + defaultsTo: '10') + ..addOption('time', + abbr: 't', + help: 'Maximum second(s) the benchmark should wait for each request.', + defaultsTo: '1') + ..addFlag('help', + abbr: 'h', negatable: false, help: 'Print this usage information.'); + final arguments = parser.parse(args); + if (arguments.wasParsed('help')) { + print(parser.usage); + return; + } + if (arguments.rest.isNotEmpty) { + print(parser.usage); + throw ArgumentError(); + } + final url = arguments['url'] as String; + final spawnThreshold = + pow(2, int.parse(arguments['limit'] as String)).toInt(); + final duration = Duration(seconds: int.parse(arguments['time'] as String)); // TODO: https://github.com/google/cronet.dart/issues/11 await CronetThroughputBenchmark.main(url, spawnThreshold, duration); // Used as an delemeter while parsing output in run_all script. diff --git a/bin/setup.dart b/bin/setup.dart index 427fad9..ee70e9f 100644 --- a/bin/setup.dart +++ b/bin/setup.dart @@ -5,6 +5,7 @@ import 'dart:io'; import 'package:archive/archive.dart'; +import 'package:args/command_runner.dart'; import 'package:cli_util/cli_logging.dart' show Ansi, Logger; import 'package:cronet/src/constants.dart'; import 'package:cronet/src/third_party/ffigen/find_resource.dart'; @@ -183,32 +184,61 @@ void verifyCronetBinary() { sample.deleteSync(); } -Future main(List args) async { - const docStr = """ -dart run cronet:setup [option] -Downloads the cronet binaries.\n -clean\tClean downloaded or built binaries. -build\tBuilds the wrapper. Requires cmake. -verify\tVerifies the cronet binary. - """; - final logger = Logger.standard(); - if (args.length > 1) { - logger.stderr('Expected 1 argument only.'); - logger.stdout(docStr); - } else if (args.contains('-h')) { - logger.stdout(docStr); - } else if (args.contains('clean')) { - logger.stdout('cleaning...'); - Directory(binaryStorageDir).deleteSync(recursive: true); - logger.stdout('Done!'); - } else if (args.contains('build')) { +// Available Commands. + +class BuildCommand extends Command { + @override + String get description => 'Builds the wrapper binaries. Requires cmake.'; + + @override + String get name => 'build'; + + @override + void run() { buildWrapper(); - } else if (args.contains('verify')) { + } +} + +class CleanCommand extends Command { + @override + String get description => 'Cleans downloaded or built binaries.'; + + @override + String get name => 'clean'; + + @override + void run() { + print('cleaning...'); + Directory(binaryStorageDir).deleteSync(recursive: true); + } +} + +class VerifyCommand extends Command { + @override + String get description => 'Verifies the cronet binary.'; + + @override + String get name => 'verify'; + + @override + void run() { verifyCronetBinary(); - } else { + } +} + +Future main(List args) async { + final runner = + CommandRunner('setup', 'Downloads/Builds the cronet binaries.'); + runner + ..addCommand(BuildCommand()) + ..addCommand(CleanCommand()) + ..addCommand(VerifyCommand()); + if (args.isEmpty) { // Targeting only 64bit OS. (At least for the time being.) if (validPlatforms.contains('${Platform.operatingSystem}64')) { await downloadCronetBinaries('${Platform.operatingSystem}64'); } + } else { + await runner.run(args); } } diff --git a/example/README.md b/example/README.md new file mode 100644 index 0000000..b347cb2 --- /dev/null +++ b/example/README.md @@ -0,0 +1,4 @@ +## Examples + +- [Bare Minimum](https://github.com/google/cronet.dart#example) +- [Simple (Dart CLI)](https://github.com/google/cronet.dart/tree/main/example/) diff --git a/lib/src/dylib_handler.dart b/lib/src/dylib_handler.dart index edd230b..aea01b5 100644 --- a/lib/src/dylib_handler.dart +++ b/lib/src/dylib_handler.dart @@ -133,7 +133,7 @@ DynamicLibrary loadDylib(String name) { logger.stdout( 'To download the binaries, please run the following from the root of' ' your project:'); - logger.stdout('${ansi.yellow}dart run cronet ${ansi.none}'); + logger.stdout('${ansi.yellow}dart run cronet:setup${ansi.none}'); logger.stdout('${ansi.green}Valid platforms are:'); for (final platform in validPlatforms) { logger.stdout(platform); diff --git a/pubspec.yaml b/pubspec.yaml index f521afc..fc36e52 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,7 @@ # BSD-style license that can be found in the LICENSE file. name: cronet -version: 0.0.2 +version: 0.0.3 homepage: https://github.com/google/cronet.dart description: Experimental Cronet dart bindings. @@ -11,9 +11,10 @@ environment: sdk: '>=2.12.0 <3.0.0' dependencies: - ffi: ^1.0.0 + ffi: ^1.1.2 path: ^1.8.0 - cli_util: ^0.3.0 + args: ^2.1.1 + cli_util: ^0.3.3 archive: ^3.1.2 dev_dependencies: diff --git a/tool/update_bindings.dart b/tool/update_bindings.dart index 8fbed3e..086b275 100644 --- a/tool/update_bindings.dart +++ b/tool/update_bindings.dart @@ -4,7 +4,7 @@ import 'dart:io'; -void main(List args) async { +void main() async { final root = Directory.fromUri(Platform.script).parent.parent.uri.toFilePath();