5454import subprocess
5555import sys
5656import tempfile
57+ import pathlib
5758import platform
5859import socket
5960import urllib
6061import urllib .request
6162import tarfile
6263import zipfile
63- import glob
64+ import glob
6465
6566assert sys .pycache_prefix is None
6667
68+ # Prefix and filelist match the defaults in org.graalvm.python.embedding.utils.VirtualFileSystem
6769VFS_PREFIX = "vfs"
70+ FILES_LIST_NAME = "fileslist.txt"
71+ FILES_LIST_PATH = VFS_PREFIX + "/" + FILES_LIST_NAME
72+
73+ # Global configuration
6874VFS_HOME = "home"
6975VFS_HOME_PREFIX = f"{ VFS_PREFIX } /{ VFS_HOME } "
7076VFS_VENV_PREFIX = VFS_PREFIX + "/venv"
7177VFS_PROJ_PREFIX = VFS_PREFIX + "/proj"
7278
73- VFS_JAVA_FILE = "VirtualFileSystem.java"
74- VFS_JAVA_FILE_TEMPLATE = f"resources/{ VFS_JAVA_FILE } "
75-
7679NATIVE_EXEC_LAUNCHER = "Py2BinLauncher"
7780NATIVE_EXEC_LAUNCHER_FILE = f"{ NATIVE_EXEC_LAUNCHER } .java"
7881NATIVE_EXEC_LAUNCHER_TEMPLATE_PATH = f"resources/{ NATIVE_EXEC_LAUNCHER_FILE } "
8386GRAALVM_URL_BASE = "https://download.oracle.com/graalvm/"
8487
8588MVN_REPOSITORY = os .getenv ("MVN_REPOSITORY" )
86- MVN_GRAALPY_ARTEFACT_ID = "python-community"
8789MVN_GRAALPY_VERSION = os .getenv ("MVN_GRAALPY_VERSION" ) if os .getenv ("MVN_GRAALPY_VERSION" ) else __graalpython__ .get_graalvm_version ()
88- PYTHON_LANGUAGE_JAR = f"org.graalvm.python-python-language-{ MVN_GRAALPY_VERSION } .jar"
8990
90- FILES_LIST_NAME = "fileslist.txt"
91- FILES_LIST_PATH = VFS_PREFIX + "/" + FILES_LIST_NAME
92-
9391CMD_NATIVE_EXECUTABLE = "native"
9492CMD_JAVA_PYTHON_APP = "polyglot_app"
9593ATTR_STANDALONE_CMD = "command"
9694
95+
9796def get_file (* paths ):
9897 return os .path .join (os .path .dirname (__file__ ), * paths )
9998
99+
100100def get_executable (file ):
101101 if os .path .isfile (file ):
102102 return file
@@ -108,6 +108,7 @@ def get_executable(file):
108108 return exe
109109 return None
110110
111+
111112def create_polyglot_app (parsed_args ):
112113 if hasattr (parsed_args , "module" ) and os .path .abspath (parsed_args .output_directory ).startswith (os .path .abspath (parsed_args .module )):
113114 print (
@@ -145,23 +146,39 @@ def create_polyglot_app(parsed_args):
145146 exit (1 )
146147
147148
148- def get_modules_path (target_dir ):
149- mp = os .path .join (__graalpython__ .home , "graalpy_downloaded_modules" )
149+ def get_download_dir (parsed_args ):
150+ subdir = "downloaded_standalone_resources"
151+ mp = os .path .join (__graalpython__ .home , subdir )
150152 try :
151153 if not os .path .exists (mp ):
152154 os .mkdir (mp )
155+ Pathlib (mp ).touch () # Ensure if we can write to that location
156+ parsed_args .keep_temp = True # Keep this location
153157 return mp
154158 except Exception as e :
155- pass
156- return os .path .join (target_dir , "modules" )
159+ if parsed_args .verbose :
160+ print ("Cannot store native standalone dependencies permanently, storing to tmpdir" )
161+ import traceback
162+ traceback .print_exception (e )
163+ mp = os .path .join (tempfile .mkdtemp (), subdir )
164+ os .mkdir (mp )
165+ return mp
166+
157167
158168def create_native_exec (parsed_args ):
159- target_dir = tempfile .mkdtemp ()
169+ artifacts = ["org.graalvm.python.python-embedding" ]
170+ if parsed_args .ce :
171+ artifacts .append ("org.graalvm.polyglot.python-community" )
172+ else :
173+ artifacts .append ("org.graalvm.polyglot.python" )
174+
175+ target_dir = get_download_dir (parsed_args )
160176 try :
161177 ni , jc = get_tools (target_dir , parsed_args )
162178
163- modules_path = get_modules_path (target_dir )
164- download_python (modules_path , parsed_args )
179+ modules_path = target_dir
180+ for artifact in artifacts :
181+ download_maven_artifact (modules_path , artifact , parsed_args )
165182
166183 launcher_file = os .path .join (target_dir , NATIVE_EXEC_LAUNCHER_FILE )
167184 create_target_directory (target_dir , launcher_file , parsed_args )
@@ -171,7 +188,8 @@ def create_native_exec(parsed_args):
171188 finally :
172189 if not parsed_args .keep_temp :
173190 shutil .rmtree (target_dir )
174-
191+
192+
175193def index_vfs (target_dir ):
176194 files_list_path = os .path .join (target_dir , FILES_LIST_PATH )
177195 dir_to_list = os .path .join (target_dir , VFS_PREFIX )
@@ -186,16 +204,7 @@ def f(dir_path, names, line_end):
186204 for (dir_path , dir_names , file_names ) in w :
187205 f (dir_path , dir_names , "/\n " )
188206 f (dir_path , file_names , "\n " )
189-
190- def create_virtual_filesystem_file (vfs_file ):
191- lines = open (get_file (VFS_JAVA_FILE_TEMPLATE ), 'r' ).readlines ()
192- with open (vfs_file , 'w' ) as f :
193- for line in lines :
194- if "{vfs-prefix}" in line :
195- line = line .replace ("{vfs-prefix}" , VFS_PREFIX )
196- if "{files-list-name}" in line :
197- line = line .replace ("{files-list-name}" , FILES_LIST_NAME )
198- f .write (line )
207+
199208
200209def create_launcher_file (template , launcher ):
201210 lines = open (template , 'r' ).readlines ()
@@ -209,6 +218,7 @@ def create_launcher_file(template, launcher):
209218 line = line .replace ("{vfs-proj-prefix}" , VFS_PROJ_PREFIX )
210219 f .write (line )
211220
221+
212222def create_target_directory (target_dir , launcher_file , parsed_args ):
213223 if parsed_args .verbose :
214224 print (f"Bundling Python resources into { target_dir } " )
@@ -222,11 +232,9 @@ def create_target_directory(target_dir, launcher_file, parsed_args):
222232 os .makedirs (os .path .dirname (launcher_file ), exist_ok = True )
223233 create_launcher_file (get_file (NATIVE_EXEC_LAUNCHER_TEMPLATE_PATH ), launcher_file )
224234
225- virtual_filesystem_java_file = os .path .join (target_dir , VFS_JAVA_FILE )
226- create_virtual_filesystem_file (virtual_filesystem_java_file )
227-
228235 shutil .copy (get_file (NATIVE_IMAGE_RESOURCES_PATH ), os .path .join (target_dir , NATIVE_IMAGE_RESOURCES_FILE ))
229236
237+
230238def bundle_python_resources (target_dir , project , venv = None ):
231239 """
232240 Copy the Python core, stdlib, venv, and module into one folder.
@@ -276,6 +284,7 @@ def bundle_python_resources(target_dir, project, venv=None):
276284 copy_folder_to_target (target_dir , tmpdir , VFS_PROJ_PREFIX )
277285 os .unlink (name )
278286
287+
279288def copy_folder_to_target (resource_root , folder , prefix , path_filter = lambda file = None , dir = None : False ):
280289 """
281290 Store a folder with Python modules.
@@ -292,6 +301,7 @@ def copy_folder_to_target(resource_root, folder, prefix, path_filter=lambda file
292301 os .makedirs (resource_parent_path , exist_ok = True )
293302 shutil .copy (fullname , os .path .join (resource_root , arcname ))
294303
304+
295305def get_graalvm_url ():
296306 jdk_version = __graalpython__ .get_jdk_version ()
297307 if "." in jdk_version :
@@ -321,6 +331,7 @@ def get_graalvm_url():
321331
322332 return f"{ GRAALVM_URL_BASE } { major_version } /archive/graalvm-jdk-{ jdk_version } _{ system } -{ machine } _bin.{ sufix } "
323333
334+
324335def get_tools (target_dir , parsed_args ):
325336 if os .getenv ("JAVA_HOME" ):
326337 graalvm_home = os .getenv ("JAVA_HOME" )
@@ -372,16 +383,15 @@ def get_tools(target_dir, parsed_args):
372383
373384 return ni , jc
374385
375- def download_python (modules_path , parsed_args ):
376- if os .path .exists ((os .path .join (modules_path , PYTHON_LANGUAGE_JAR ))):
377- return
378-
386+
387+ def download_maven_artifact (modules_path , artifact , parsed_args ):
379388 mvnd = get_executable (os .path .join (__graalpython__ .home , "libexec" , "graalpy-polyglot-get" ))
380389 cmd = [mvnd ]
381390
382391 if MVN_REPOSITORY :
383392 cmd += ["-r" , MVN_REPOSITORY ]
384- cmd += ["-a" , MVN_GRAALPY_ARTEFACT_ID ]
393+ cmd += ["-a" , artifact .rsplit ("." , 1 )[1 ]]
394+ cmd += ["-g" , artifact .rsplit ("." , 1 )[0 ]]
385395 cmd += ["-v" , MVN_GRAALPY_VERSION ]
386396 cmd += ["-o" , modules_path ]
387397 if parsed_args .verbose :
@@ -398,13 +408,14 @@ def download_python(modules_path, parsed_args):
398408 print (p .stderr .decode ())
399409 exit (1 )
400410
411+
401412def build_binary (target_dir , ni , jc , modules_path , launcher_file , parsed_args ):
402413 cwd = os .getcwd ()
403414 output = os .path .abspath (parsed_args .output )
404415 os .chdir (target_dir )
405416
406417 try :
407- cmd = [jc , "-cp" , f"{ modules_path } /*" , os . path . join ( target_dir , "VirtualFileSystem.java" ), launcher_file ]
418+ cmd = [jc , "-cp" , f"{ modules_path } /*" , launcher_file ]
408419 if parsed_args .verbose :
409420 print (f"Compiling code for Python standalone entry point: { ' ' .join (cmd )} " )
410421 p = subprocess .run (cmd , cwd = target_dir , stdout = subprocess .PIPE , stderr = subprocess .PIPE )
@@ -439,6 +450,7 @@ def build_binary(target_dir, ni, jc, modules_path, launcher_file, parsed_args):
439450 finally :
440451 os .chdir (cwd )
441452
453+
442454def main (args ):
443455 parser = argparse .ArgumentParser (prog = f"{ sys .executable } -m standalone" )
444456 parser .add_argument (
@@ -471,6 +483,9 @@ def main(args):
471483 metavar = "<arg>" ,
472484 default = [],
473485 )
486+ parser_bin .add_argument (
487+ "-ce" , action = "store_true" , help = "Use GraalPy Community Edition instead of Oracle GraalPy"
488+ )
474489
475490 parser_app = subparsers .add_parser (
476491 CMD_JAVA_PYTHON_APP ,
@@ -510,5 +525,6 @@ def main(args):
510525 else :
511526 create_native_exec (parsed_args )
512527
528+
513529if __name__ == "__main__" :
514530 main (sys .argv [1 :])
0 commit comments