diff --git a/bin/inno_build.dart b/bin/inno_build.dart index 4860574..9f979d3 100644 --- a/bin/inno_build.dart +++ b/bin/inno_build.dart @@ -1,3 +1,5 @@ +// bin/inno_build.dart + // Dart imports: import 'dart:io'; @@ -10,11 +12,11 @@ import 'package:inno_build/inno_build.dart'; import 'package:inno_build/models/build_mode.dart'; import 'package:inno_build/services/app_id_service.dart'; import 'package:inno_build/services/dependency_manager.dart'; -import 'package:inno_build/services/flutter_builder.dart'; import 'package:inno_build/services/inno_setup_manager.dart'; import 'package:inno_build/utils/constants.dart'; import 'package:inno_build/utils/pubspec_manager.dart'; +/// The main entry point for the Inno Build CLI. Future main(List arguments) async { final parser = ArgParser() ..addOption('app-id', abbr: 'a', help: 'Generate a new InnoSetup AppID.') @@ -25,22 +27,29 @@ Future main(List arguments) async { ..addFlag('install-inno', abbr: 'i', help: 'Install Inno Setup if not present.') ..addFlag('skip-flutter-build', help: 'Skip the Flutter build step.') - ..addFlag('verbose', abbr: 'v', help: 'Enable verbose output.') - ..addFlag('quiet', abbr: 'q', help: 'Suppress output (quiet mode).') ..addFlag('help', abbr: 'h', help: 'Show this help message.') - ..addFlag('version', help: 'Show version information.'); + ..addFlag('version', help: 'Show version information.') + ..addFlag('obfuscate', help: 'Obfuscate the Dart code during the build.') + ..addOption('split-debug-info', + help: 'Path to store split debug info files.') + ..addMultiOption('dart-define', + help: 'Pass additional key-value pairs to the Dart compiler.') + ..addOption('target', + abbr: 't', help: 'The main entry-point file of the application.') + ..addFlag('verbose', abbr: 'v', help: 'Enable verbose output.') + ..addFlag('quiet', abbr: 'q', help: 'Suppress output (quiet mode).'); final argResults = parser.parse(arguments); stdout.writeln(welcomeMessage); if (argResults['help']) { - print(helpMessage); + print(parser.usage); return; } if (argResults['version']) { - print('Inno Build CLI v1.0.0'); + print('inno_build v1.0.0'); return; } @@ -54,10 +63,10 @@ Future main(List arguments) async { final buildMode = _determineBuildMode(argResults); + // Servislerin oluşturulması final pubspecManager = PubspecManager(); final appIdService = AppIdService(pubspecManager); final dependencyManager = DependencyManager(verbose: verbose); - final flutterBuilder = FlutterBuilder(buildMode, verbose: verbose); final innoSetupManager = InnoSetupManager(buildMode, verbose: verbose, quiet: quiet); final spinner = CliSpin(isSilent: quiet); @@ -67,7 +76,6 @@ Future main(List arguments) async { pubspecManager: pubspecManager, appIdService: appIdService, dependencyManager: dependencyManager, - flutterBuilder: flutterBuilder, innoSetupManager: innoSetupManager, spinner: spinner, buildMode: buildMode, @@ -76,16 +84,17 @@ Future main(List arguments) async { try { await appLogic.run(); } catch (e) { - print(e); - exit(64); // Exit code for usage error + print('An error occurred: $e'); + exit(1); } } +/// Determines the [BuildMode] based on the provided command-line arguments. BuildMode _determineBuildMode(ArgResults argResults) { if (argResults['debug']) { if (argResults['release']) { print('Error: --release and --debug cannot be used together.'); - exit(64); // Exit code for usage error + exit(64); } return BuildMode.debug; } diff --git a/example/pubspec.lock b/example/pubspec.lock index 1708d83..95847ff 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -145,31 +145,31 @@ packages: path: ".." relative: true source: path - version: "0.3.0" + version: "0.4.0" leak_tracker: dependency: transitive description: name: leak_tracker - sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + sha256: "8dcda04c3fc16c14f48a7bb586d4be1f0d1572731b6d81d51772ef47c02081e0" url: "https://pub.dev" source: hosted - version: "10.0.9" + version: "11.0.1" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" url: "https://pub.dev" source: hosted - version: "3.0.9" + version: "3.0.10" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" lints: dependency: transitive description: @@ -275,10 +275,10 @@ packages: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.7.6" typed_data: dependency: transitive description: @@ -299,10 +299,10 @@ packages: dependency: transitive description: name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.2.0" vm_service: dependency: transitive description: @@ -328,5 +328,5 @@ packages: source: hosted version: "2.0.0" sdks: - dart: ">=3.7.0-0 <4.0.0" + dart: ">=3.8.0-0 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" diff --git a/lib/inno_build.dart b/lib/inno_build.dart index a950d22..e8c08f1 100644 --- a/lib/inno_build.dart +++ b/lib/inno_build.dart @@ -1,3 +1,5 @@ +// lib/inno_build.dart + // Dart imports: import 'dart:io'; @@ -15,98 +17,64 @@ import 'package:inno_build/utils/config.dart'; import 'package:inno_build/utils/constants.dart'; import 'package:inno_build/utils/pubspec_manager.dart'; +/// A list of command-line options that should be passed through to the +/// `flutter build` command. To add support for a new flutter build argument, +/// add it to this list and also to the ArgParser in your main executable file. +const List _flutterBuildOptions = [ + 'obfuscate', + 'split-debug-info', + 'dart-define', + 'target', + 'verbose', + 'quiet', + // Future Flutter build arguments can be added here. + // e.g., 'tree-shake-icons', 'build-name', 'build-number' +]; + /// This class is the main entry point of the `inno_build` command-line tool. /// /// It takes the parsed command-line arguments, and uses them to determine what /// actions to take. The actions are: /// 1. Generate a new App ID if the user specified the `--app-id` flag. -/// 2. Check if Inno Setup is installed, and download and install it if not. -/// 3. Check if the Visual C++ 2015-2022 Redistributable is downloaded, and -/// download it if not. -/// 4. Build the Flutter Windows application using the `flutter build` command. -/// 5. Build the Inno Setup script using the `inno_setup_manager` service. -/// 6. Compile the Inno Setup script using the `inno_setup_manager` service. +/// 2. Build the Flutter Windows application using the `flutter build` command. +/// 3. Build the Inno Setup script using the `inno_setup_manager` service. +/// 4. Compile the Inno Setup script using the `inno_setup_manager` service. class InnoBuild { /// The parsed command-line arguments. - /// - /// This is used to determine the actions to take when running the `inno_build` - /// command-line tool. final ArgResults argResults; /// An instance of the `PubspecManager` class. - /// - /// This is used to read and write the `pubspec.yaml` file. final PubspecManager pubspecManager; /// An instance of the `AppIdService` class. - /// - /// This is used to generate a new App ID if the user specified the - /// `--app-id` flag. final AppIdService appIdService; /// An instance of the `DependencyManager` class. - /// - /// This is used to download and install Inno Setup if it is not already - /// installed, and to download the Visual C++ 2015-2022 Redistributable if - /// it is not already downloaded. final DependencyManager dependencyManager; - /// An instance of the `FlutterBuilder` class. - /// - /// This is used to build the Flutter Windows application using the - /// `flutter build` command. - final FlutterBuilder flutterBuilder; - /// An instance of the `InnoSetupManager` class. - /// - /// This is used to build the Inno Setup script and compile it using the - /// `inno_setup_manager` service. final InnoSetupManager innoSetupManager; /// An instance of the `CliSpin` class. - /// - /// This is used to display a spinner while the `inno_build` command-line tool - /// is running. final CliSpin spinner; /// The build mode specified by the user. - /// - /// This is either `BuildMode.debug` or `BuildMode.release`. final BuildMode buildMode; /// Creates an instance of the `inno_build` command-line tool. /// /// The [argResults] parameter is the parsed command-line arguments. - /// - /// The [pubspecManager] parameter is an instance of the `PubspecManager` - /// class, which is used to read and write the `pubspec.yaml` file. - /// - /// The [appIdService] parameter is an instance of the `AppIdService` class, - /// which is used to generate a new App ID if the user specified the - /// `--app-id` flag. - /// - /// The [dependencyManager] parameter is an instance of the - /// `DependencyManager` class, which is used to check if Inno Setup is - /// installed, and download and install it if not. - /// - /// The [flutterBuilder] parameter is an instance of the `FlutterBuilder` - /// class, which is used to build the Flutter Windows application using - /// the `flutter build` command. - /// - /// The [innoSetupManager] parameter is an instance of the `InnoSetupManager` - /// class, which is used to build the Inno Setup script and compile it. - /// - /// The [spinner] parameter is an instance of the `CliSpin` class, which is - /// used to display a spinner on the console. - /// - /// The [buildMode] parameter is the build mode that the user specified using - /// the `--debug` or `--release` flag. + /// The [pubspecManager] parameter is an instance of the `PubspecManager`. + /// The [appIdService] parameter is an instance of the `AppIdService`. + /// The [dependencyManager] parameter is an instance of the `DependencyManager`. + /// The [innoSetupManager] parameter is an instance of the `InnoSetupManager`. + /// The [spinner] parameter is an instance of the `CliSpin` class. + /// The [buildMode] parameter is the build mode that the user specified. InnoBuild({ required this.argResults, required this.pubspecManager, required this.appIdService, required this.dependencyManager, - required this.flutterBuilder, required this.innoSetupManager, required this.spinner, required this.buildMode, @@ -123,8 +91,6 @@ class InnoBuild { exit(0); } await _handleAppId(); - // await _checkAndInstallInnoSetup(); - // await _checkAndDownloadVccRedist(); if (!argResults['skip-flutter-build']) { await _buildFlutterApp(); } @@ -144,10 +110,22 @@ class InnoBuild { 'Error: --release, --debug, and --install-inno cannot be used together.'); } if (argResults['verbose'] && argResults['quiet']) { - throw ArgumentError('Error: --verbose --quiet cannot be used together.'); + throw ArgumentError( + 'Error: --verbose and --quiet cannot be used together.'); } } + /// Collects all relevant arguments from argResults to be passed to Flutter. + Map _getFlutterBuildArgs() { + final args = {}; + for (final option in _flutterBuildOptions) { + if (argResults.wasParsed(option)) { + args[option] = argResults[option]; + } + } + return args; + } + /// Handles generating a new App ID if the user specified the `--app-id` flag. /// /// This method is called by the `run` method and checks if the user has @@ -175,43 +153,30 @@ class InnoBuild { } } - /// Checks if Inno Setup is installed, and downloads and installs it if not. + /// Builds the Flutter Windows application using the `flutter build` command. /// - /// This method is called by the `run` method and checks if Inno Setup is - /// installed. If not, it downloads and installs it. - // Future _checkAndInstallInnoSetup() async { - // spinner.start('Checking Inno Setup...'); - // if (File(innoCompilerPath).existsSync()) { - // spinner.success('Inno Setup is already installed.'); - // } else { - // await _installInnoSetup(); - // } - // } + /// This method is called by the `run` method and builds the Flutter Windows + /// application using the `flutter build` command. + Future _buildFlutterApp() async { + spinner.start('Building Flutter Windows application...'); - /// Checks if the Visual C++ 2015-2022 Redistributable is downloaded, and - /// downloads it if not. - /// - /// This method is called by the `run` method and checks if the Visual C++ - /// 2015-2022 Redistributable is downloaded. If not, it downloads it. - // Future _checkAndDownloadVccRedist() async { - // spinner.start('Checking Visual C++ 2015-2022 Redistributable...'); - // if (File(vcRedistPath).existsSync()) { - // spinner.success( - // 'Visual C++ 2015-2022 Redistributable is already donwloaded.'); - // } else { - // spinner.start('Downloading Visual C++ 2015-2022 Redistributable...'); - // if (argResults['verbose']) { - // spinner.stopAndPersist(); - // } - // final download = await dependencyManager.ensureVcredistDownloaded(); - // if (download == 0) { - // spinner.success( - // 'Downloaded Visual C++ 2015-2022 Redistributable successfully.'); - // } else { - // spinner.fail('Failed to download Visual C++ 2015-2022 Redistributable'); - // } - // } - // } + // Create a FlutterBuilder instance with the dynamic arguments. + final flutterBuilder = FlutterBuilder( + buildMode: buildMode, + flutterBuildArgs: _getFlutterBuildArgs(), + ); + + if (argResults['verbose']) spinner.stopAndPersist(); + + final exitCode = await flutterBuilder.buildApp(); + + if (exitCode == 0) { + spinner.success('Built ${buildMode.buildPath}\\${Config.execName}.'); + } else { + spinner.fail( + 'Failed to build Flutter application. Check the logs above for details.'); + } + } /// Downloads Inno Setup if it is not already installed. /// @@ -228,21 +193,6 @@ class InnoBuild { } } - /// Builds the Flutter Windows application using the `flutter build` command. - /// - /// This method is called by the `run` method and builds the Flutter Windows - /// application using the `flutter build` command. - Future _buildFlutterApp() async { - spinner.start('Building Flutter Windows application...'); - if (argResults['verbose']) spinner.stopAndPersist(); - final exitCode = await flutterBuilder.buildApp(); - if (exitCode == 0) { - spinner.success('Built ${buildMode.buildPath}\\${Config.execName}.'); - } else { - spinner.fail('Failed to build Flutter application'); - } - } - /// Installs Inno Setup if it is not already installed. /// /// This method is called by the `run` method and installs Inno Setup if it is diff --git a/lib/services/flutter_builder.dart b/lib/services/flutter_builder.dart index a7c57e7..b3a79d8 100644 --- a/lib/services/flutter_builder.dart +++ b/lib/services/flutter_builder.dart @@ -9,53 +9,70 @@ import 'package:inno_build/models/build_mode.dart'; /// Class that builds a Flutter application. /// /// This class abstracts the process of building a Flutter application -/// using the `flutter build` command. The application is built in the -/// mode specified by [buildMode]. -/// -/// The [verbose] parameter allows to control the verbosity of the -/// command. If `true`, the `flutter build` command will be executed with -/// the `--verbose` flag and the output will be printed to the console. -/// If `false`, the command will be executed with the `--quiet` flag and -/// the output will be silent. +/// using the `flutter build` command. It dynamically constructs the command +/// based on the provided build mode and additional Flutter arguments. class FlutterBuilder { - /// Whether the `flutter build` command should be executed with the - /// `--verbose` flag. - /// - /// If `true`, the output will be printed to the console. If `false`, the - /// output will be silent. - final bool verbose; - /// The build mode to use when building the Flutter application. - /// - /// The build mode determines where the application will be built. - /// The possible values are: - /// - [BuildMode.debug]: The application will be built in debug mode. - /// - [BuildMode.release]: The application will be built in release mode. final BuildMode buildMode; + /// A map of additional arguments to pass to the 'flutter build' command. + /// Keys are the argument names (e.g., 'obfuscate', 'split-debug-info') + /// and values are the argument values. + /// For flags, the value should be `true`. For options with values, + /// it should be the string value. For multi-options, it can be a list. + final Map flutterBuildArgs; + /// Creates a new instance of [FlutterBuilder]. /// - /// The [verbose] parameter allows to control the verbosity of the - /// command. If `true`, the `flutter build` command will be executed with - /// the `--verbose` flag and the output will be printed to the console. - /// If `false`, the command will be executed with the `--quiet` flag and - /// the output will be silent. - FlutterBuilder(this.buildMode, {this.verbose = false}); + /// [buildMode] determines the build configuration (e.g., debug, release). + /// [flutterBuildArgs] contains all additional parameters to be passed + /// to the flutter build command. + FlutterBuilder({ + required this.buildMode, + this.flutterBuildArgs = const {}, + }); /// Builds the Flutter application. /// /// This method executes the `flutter build` command with the options - /// specified by [buildMode] and [verbose]. + /// specified by [buildMode] and [flutterBuildArgs]. /// The method returns the exit code of the `flutter build` command. Future buildApp() async { final mode = buildMode.name; - List args = ['/c', 'flutter', 'build', 'windows', '--$mode']; - if (verbose) args.add('--verbose'); + final List args = ['/c', 'flutter', 'build', 'windows', '--$mode']; + + // Dynamically add arguments from the map + flutterBuildArgs.forEach((key, value) { + // For flags like --obfuscate or --verbose where value is true + if (value is bool && value == true) { + args.add('--$key'); + } + // For options with values like --split-debug-info=... + else if (value is String && value.isNotEmpty) { + args.add('--$key=$value'); + } + // For list values like --dart-define=KEY=VALUE + else if (value is List) { + for (final item in value) { + if (item is String) { + args.add('--$key=$item'); + } + } + } + }); + + final bool isVerbose = flutterBuildArgs['verbose'] == true; + + // For debugging purposes, print the command that will be executed. + if (isVerbose) { + print('Executing command: cmd.exe ${args.join(' ')}'); + } + final process = await Process.start( 'cmd', args, - mode: verbose ? ProcessStartMode.inheritStdio : ProcessStartMode.normal, + mode: isVerbose ? ProcessStartMode.inheritStdio : ProcessStartMode.normal, ); - return process.exitCode; + return await process.exitCode; } } diff --git a/pubspec.lock b/pubspec.lock index 9569c31..1a14819 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,23 +5,18 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab" + sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" url: "https://pub.dev" source: hosted - version: "76.0.0" - _macros: - dependency: transitive - description: dart - source: sdk - version: "0.3.3" + version: "67.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e" + sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" url: "https://pub.dev" source: hosted - version: "6.11.0" + version: "6.4.1" ansi_regex: dependency: transitive description: @@ -214,14 +209,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" - macros: - dependency: transitive - description: - name: macros - sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656" - url: "https://pub.dev" - source: hosted - version: "0.1.3-main.0" matcher: dependency: transitive description: @@ -503,4 +490,4 @@ packages: source: hosted version: "2.0.0" sdks: - dart: ">=3.5.0 <4.0.0" + dart: ">=3.4.4 <4.0.0"