@@ -4,6 +4,8 @@ import 'dart:io';
44import 'package:archive/archive_io.dart' ;
55import 'package:args/command_runner.dart' ;
66import 'package:crypto/crypto.dart' ;
7+ import 'package:glob/glob.dart' ;
8+ import 'package:glob/list_local_fs.dart' ;
79import 'package:http/http.dart' as http;
810import 'package:path/path.dart' as path;
911import 'package:shelf/shelf.dart' ;
@@ -58,11 +60,24 @@ const platforms = {
5860 }
5961};
6062
61- const junkFileExtensionsDesktop = [".c" , ".h" , ".hpp" , ".typed" , ".a" , ".pdb" ];
62- const junkFileExtensionsMobile = [...junkFileExtensionsDesktop, ".exe" , ".dll" ];
63-
64- const junkFilesDesktop = ["__pycache__" ];
65- const junkFilesMobile = [...junkFilesDesktop, "bin" ];
63+ final dartFile = Glob ("**.dart" );
64+
65+ const junkFilesDesktop = [
66+ "**.c" ,
67+ "**.h" ,
68+ "**.cpp" ,
69+ "**.hpp" ,
70+ "**.typed" ,
71+ "**.a" ,
72+ "**.pdb" ,
73+ "**.pyi" ,
74+ "**.pxd" ,
75+ "**.pyx" ,
76+ "**/LICENSE" ,
77+ "**.dist-info" ,
78+ "**/__pycache__" ,
79+ ];
80+ const junkFilesMobile = [...junkFilesDesktop, "**.exe" , "**.dll" , "**/bin" ];
6681
6782class PackageCommand extends Command {
6883 bool _verbose = false ;
@@ -104,6 +119,16 @@ class PackageCommand extends Command {
104119 help:
105120 "Cleanup app and packages from unneccessary files and directories." ,
106121 negatable: false );
122+ argParser.addFlag ("cleanup-app" ,
123+ help: "Cleanup app from unneccessary files and directories." ,
124+ negatable: false );
125+ argParser.addMultiOption ('cleanup-app-files' ,
126+ help: "List of globs to delete extra app files and directories." );
127+ argParser.addFlag ("cleanup-packages" ,
128+ help: "Cleanup packages from unneccessary files and directories." ,
129+ negatable: false );
130+ argParser.addMultiOption ('cleanup-packages-files' ,
131+ help: "List of globs to delete extra packages files and directories." );
107132 argParser.addFlag ("verbose" , help: "Verbose output." , negatable: false );
108133 }
109134
@@ -135,6 +160,10 @@ class PackageCommand extends Command {
135160 bool compileApp = argResults? ["compile-app" ];
136161 bool compilePackages = argResults? ["compile-packages" ];
137162 bool cleanup = argResults? ["cleanup" ];
163+ bool cleanupApp = argResults? ["cleanup-app" ];
164+ List <String > cleanupAppFiles = argResults? ['cleanup-app-files' ];
165+ bool cleanupPackages = argResults? ["cleanup-packages" ];
166+ List <String > cleanupPackagesFiles = argResults? ['cleanup-packages-files' ];
138167 _verbose = argResults? ["verbose" ];
139168
140169 if (path.isRelative (sourceDirPath)) {
@@ -162,8 +191,6 @@ class PackageCommand extends Command {
162191 bool isMobile = (platform == "iOS" || platform == "Android" );
163192 bool isWeb = platform == "Pyodide" ;
164193
165- var junkFileExtensions =
166- isMobile ? junkFileExtensionsMobile : junkFileExtensionsDesktop;
167194 var junkFiles = isMobile ? junkFilesMobile : junkFilesDesktop;
168195
169196 // Extra indexs
@@ -212,19 +239,19 @@ class PackageCommand extends Command {
212239 await runPython (['-m' , 'compileall' , '-b' , tempDir.path]);
213240
214241 verbose ("Deleting original .py files" );
215- await cleanupPyPackages (tempDir, [".py" ], [ ]);
242+ await cleanupDir (tempDir, ["** .py" ]);
216243 }
217244
218245 // cleanup
219- if (cleanup) {
246+ if (cleanupApp || cleanup) {
247+ var allJunkFiles = [...junkFiles, ...cleanupAppFiles];
220248 if (_verbose) {
221249 verbose (
222- "Delete unnecessary app files with extensions: $junkFileExtensions " );
223- verbose ("Delete unnecessary app files and directories: $junkFiles " );
250+ "Delete unnecessary app files and directories: $allJunkFiles " );
224251 } else {
225252 stdout.writeln (("Cleanup app" ));
226253 }
227- await cleanupPyPackages (tempDir, junkFileExtensions, junkFiles );
254+ await cleanupDir (tempDir, allJunkFiles );
228255 }
229256
230257 // install requirements
@@ -358,21 +385,19 @@ class PackageCommand extends Command {
358385 await runPython (['-m' , 'compileall' , '-b' , sitePackagesDir]);
359386
360387 verbose ("Deleting original .py files" );
361- await cleanupPyPackages (Directory (sitePackagesDir), [".py" ], [ ]);
388+ await cleanupDir (Directory (sitePackagesDir), ["** .py" ]);
362389 }
363390
364391 // cleanup packages
365- if (cleanup) {
392+ if (cleanupPackages || cleanup) {
393+ var allJunkFiles = [...junkFiles, ...cleanupPackagesFiles];
366394 if (_verbose) {
367395 verbose (
368- "Delete unnecessary package files with extensions: $junkFileExtensions " );
369- verbose (
370- "Delete unnecessary package files and directories: $junkFiles " );
396+ "Delete unnecessary package files and directories: $allJunkFiles " );
371397 } else {
372398 stdout.writeln (("Cleanup installed packages" ));
373399 }
374- await cleanupPyPackages (
375- Directory (sitePackagesDir), junkFileExtensions, junkFiles);
400+ await cleanupDir (Directory (sitePackagesDir), allJunkFiles);
376401 }
377402 } finally {
378403 if (sitecustomizeDir != null && await sitecustomizeDir.exists ()) {
@@ -435,26 +460,20 @@ class PackageCommand extends Command {
435460 }
436461 }
437462
438- Future <void > cleanupPyPackages (Directory directory,
439- List <String > fileExtensions, List <String > filesAndDirectories) async {
440- await for (var entity in directory.list ()) {
441- if (entity is Directory ) {
442- await cleanupPyPackages (entity, fileExtensions, filesAndDirectories);
443- } else if (entity is File &&
444- (fileExtensions.contains (path.extension (entity.path)) ||
445- filesAndDirectories.contains (path.basename (entity.path)))) {
446- verbose ("Deleting ${entity .path }" );
447-
448- await entity.delete ();
449- }
450- }
463+ Future <void > cleanupDir (Directory directory, List <String > filesGlobs) async {
464+ return cleanupDirRecursive (
465+ directory, filesGlobs.map ((g) => Glob (g.replaceAll ("\\ " , "/" ))));
466+ }
451467
452- await for (var entity in directory.list ()) {
453- if (entity is Directory &&
454- filesAndDirectories.contains (path.basename (entity.path))) {
468+ Future <void > cleanupDirRecursive (
469+ Directory directory, Iterable <Glob > globs) async {
470+ for (var entity in directory.listSync ()) {
471+ if (globs.any ((g) => g.matches (entity.path.replaceAll ("\\ " , "/" ))) &&
472+ await entity.exists ()) {
455473 verbose ("Deleting ${entity .path }" );
456-
457474 await entity.delete (recursive: true );
475+ } else if (entity is Directory ) {
476+ await cleanupDirRecursive (entity, globs);
458477 }
459478 }
460479 }
@@ -533,7 +552,7 @@ class PackageCommand extends Command {
533552 'tar' , ['-xzf' , pythonArchivePath, '-C' , _pythonDir! .path]);
534553
535554 if (Platform .isMacOS) {
536- copySysconfigFiles (_pythonDir! .path);
555+ duplicateSysconfigFile (_pythonDir! .path);
537556 }
538557 }
539558 }
@@ -547,53 +566,23 @@ class PackageCommand extends Command {
547566 return await runExec (pythonExePath, args, environment: environment);
548567 }
549568
550- void copySysconfigFiles (String pythonDir) {
551- final libPath = Directory (path.join (pythonDir, "python" , "lib" ));
552-
553- // Find the Python version dynamically (e.g., python3.10, python3.11)
554- if (! libPath.existsSync ()) {
555- stderr.writeln ('Python lib directory not found: $libPath ' );
556- exit (1 );
557- }
558-
559- // Find the actual Python 3.x subdirectory
560- final pythonSubDir = libPath
561- .listSync ()
562- .whereType <Directory >()
563- .firstWhere ((dir) => RegExp (r'python3\.\d+' ).hasMatch (dir.path),
564- orElse: () => throw Exception ('No Python 3.x directory found' ))
565- .path;
566-
567- final targetDir = Directory (pythonSubDir);
568-
569- // Search for `_sysconfigdata__*.py` files
570- final files = targetDir
571- .listSync ()
572- .whereType <File >()
573- .where ((file) => RegExp (r'_sysconfigdata__.*\.py$' ).hasMatch (file.path))
574- .toList ();
575-
576- if (files.isEmpty) {
577- stderr.writeln ('No matching _sysconfigdata__ files found in $targetDir ' );
578- exit (1 );
579- }
580-
581- for (final file in files) {
582- final dir = file.parent.path;
583-
584- // Define the new filenames
585- final targets = [
586- '_sysconfigdata__darwin_arm64_iphoneos.py' ,
587- '_sysconfigdata__darwin_arm64_iphonesimulator.py' ,
588- '_sysconfigdata__darwin_x86_64_iphonesimulator.py' ,
589- ];
590-
591- for (final target in targets) {
592- final targetPath = '$dir /$target ' ;
593- file.copySync (targetPath);
594- if (_verbose) {
595- verbose ('Copied ${file .path } -> $targetPath ' );
569+ void duplicateSysconfigFile (String pythonDir) {
570+ final sysConfigGlob = Glob ("python/lib/python3.*/_sysconfigdata__*.py" );
571+ for (var sysConfig in sysConfigGlob.listSync (root: pythonDir)) {
572+ // copy the first found sys config and exit
573+ if (sysConfig is File ) {
574+ for (final target in [
575+ '_sysconfigdata__darwin_arm64_iphoneos.py' ,
576+ '_sysconfigdata__darwin_arm64_iphonesimulator.py' ,
577+ '_sysconfigdata__darwin_x86_64_iphonesimulator.py' ,
578+ ]) {
579+ var targetPath = path.join (sysConfig.parent.path, target);
580+ (sysConfig as File ).copySync (targetPath);
581+ if (_verbose) {
582+ verbose ('Copied ${sysConfig .path } -> $targetPath ' );
583+ }
596584 }
585+ break ;
597586 }
598587 }
599588 }
0 commit comments