@@ -29,6 +29,7 @@ const junkFilesAndDirectories = ["__pycache__", "bin"];
2929
3030class PackageCommand extends Command {
3131 bool _verbose = false ;
32+ Directory ? _pythonDir;
3233
3334 @override
3435 final name = "package" ;
@@ -56,6 +57,9 @@ class PackageCommand extends Command {
5657 argParser.addOption ('platform' ,
5758 help:
5859 "Make pip to install dependencies for this platform, e.g. 'emscripten_3_1_45_wasm32'. An attempt to install native Python modules will raise an error." );
60+ argParser.addOption ('exclude' ,
61+ help:
62+ "List of relative paths to exclude from app package, e.g. \" assets,build\" ." );
5963 }
6064
6165 // [run] may also return a Future.
@@ -86,6 +90,7 @@ class PackageCommand extends Command {
8690 String ? reqDepsArg = argResults? ['req-deps' ];
8791 String ? findLinksArg = argResults? ['find-links' ];
8892 String ? platformArg = argResults? ['platform' ];
93+ String ? excludeArg = argResults? ['exclude' ];
8994 _verbose = argResults? ["verbose" ];
9095
9196 if (mobile && web) {
@@ -130,7 +135,13 @@ class PackageCommand extends Command {
130135 // copy app to a temp dir
131136 stdout.writeln (
132137 "Copying Python app from ${sourceDir .path } to ${tempDir .path }" );
133- await copyDirectory (sourceDir, tempDir);
138+ await copyDirectory (
139+ sourceDir,
140+ tempDir,
141+ sourceDir.path,
142+ excludeArg != null
143+ ? excludeArg.split ("," ).map ((s) => s.trim ()).toList ()
144+ : []);
134145
135146 // discover dependencies
136147 List <String > dependencies = [];
@@ -310,6 +321,10 @@ class PackageCommand extends Command {
310321 "Deleting sitecustomize directory ${sitecustomizeDir .path }" );
311322 await sitecustomizeDir.delete (recursive: true );
312323 }
324+ if (_pythonDir != null && await _pythonDir! .exists ()) {
325+ stdout.writeln ("Deleting Python directory ${_pythonDir !.path }" );
326+ await _pythonDir! .delete (recursive: true );
327+ }
313328 }
314329 }
315330
@@ -330,13 +345,18 @@ class PackageCommand extends Command {
330345 return null ;
331346 }
332347
333- Future <void > copyDirectory (Directory source, Directory destination) async {
348+ Future <void > copyDirectory (Directory source, Directory destination,
349+ String rootDir, List <String > excludeList) async {
334350 await for (var entity in source.list ()) {
351+ if (excludeList.contains (path.relative (entity.path, from: rootDir))) {
352+ continue ;
353+ }
335354 if (entity is Directory ) {
336355 final newDirectory =
337356 Directory (path.join (destination.path, path.basename (entity.path)));
338357 await newDirectory.create ();
339- await copyDirectory (entity.absolute, newDirectory);
358+ await copyDirectory (
359+ entity.absolute, newDirectory, rootDir, excludeList);
340360 } else if (entity is File ) {
341361 await entity
342362 .copy (path.join (destination.path, path.basename (entity.path)));
@@ -385,21 +405,8 @@ class PackageCommand extends Command {
385405
386406 Future <int > runPython (List <String > args,
387407 {Map <String , String >? environment}) async {
388- var pythonDir =
389- Directory (path.join (Directory .systemTemp.path, "hostpython3.11" ));
390-
391- var pythonExePath = Platform .isWindows
392- ? path.join (pythonDir.path, 'python' , 'python.exe' )
393- : path.join (pythonDir.path, 'python' , 'bin' , 'python3' );
394-
395- if (! await File (pythonExePath).exists ()) {
396- stdout
397- .writeln ("Downloading and extracting Python into ${pythonDir .path }" );
398-
399- if (await pythonDir.exists ()) {
400- await pythonDir.delete (recursive: true );
401- }
402- await pythonDir.create (recursive: true );
408+ if (_pythonDir == null ) {
409+ _pythonDir = await Directory .systemTemp.createTemp ('hostpython3.11_' );
403410
404411 var isArm64 = Platform .version.contains ("arm64" );
405412
@@ -416,20 +423,35 @@ class PackageCommand extends Command {
416423 arch = 'x86_64-pc-windows-msvc-shared' ;
417424 }
418425
419- final url =
420- "https://github.com/indygreg/python-build-standalone/releases/download/20231002/ cpython-3.11.6+20231002-$arch -install_only.tar.gz" ;
426+ var pythonArchiveFilename =
427+ "cpython-3.11.6+20231002-$arch -install_only.tar.gz" ;
421428
422- // Download the release asset
423- var response = await http.get (Uri .parse (url));
424- var archivePath = path.join (pythonDir.path, 'python.tar.gz' );
425- await File (archivePath).writeAsBytes (response.bodyBytes);
429+ var pythonArchivePath =
430+ path.join (Directory .systemTemp.path, pythonArchiveFilename);
426431
427- // Extract the archive
428- await Process .run ('tar' , ['-xzf' , archivePath, '-C' , pythonDir.path]);
429- } else {
430- verbose ("Python executable found at $pythonExePath " );
432+ if (! await File (pythonArchivePath).exists ()) {
433+ // download Python distr from GitHub
434+ final url =
435+ "https://github.com/indygreg/python-build-standalone/releases/download/20231002/$pythonArchiveFilename " ;
436+
437+ stdout.writeln (
438+ "Downloading Python distributive from $url to $pythonArchivePath " );
439+
440+ var response = await http.get (Uri .parse (url));
441+ await File (pythonArchivePath).writeAsBytes (response.bodyBytes);
442+ }
443+
444+ // extract Python from archive
445+ stdout.writeln (
446+ "Extracting Python distributive from $pythonArchivePath to ${_pythonDir !.path }" );
447+ await Process .run (
448+ 'tar' , ['-xzf' , pythonArchivePath, '-C' , _pythonDir! .path]);
431449 }
432450
451+ var pythonExePath = Platform .isWindows
452+ ? path.join (_pythonDir! .path, 'python' , 'python.exe' )
453+ : path.join (_pythonDir! .path, 'python' , 'bin' , 'python3' );
454+
433455 // Run the python executable
434456 verbose ([pythonExePath, ...args].join (" " ));
435457 return await runExec (pythonExePath, args, environment: environment);
0 commit comments