1
+ #
2
+ # Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved.
3
+ # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
+ #
5
+ # The Universal Permissive License (UPL), Version 1.0
6
+ #
7
+ # Subject to the condition set forth below, permission is hereby granted to any
8
+ # person obtaining a copy of this software, associated documentation and/or
9
+ # data (collectively the "Software"), free of charge and under any and all
10
+ # copyright rights in the Software, and any and all patent rights owned or
11
+ # freely licensable by each licensor hereunder covering either (i) the
12
+ # unmodified Software as contributed to or provided by such licensor, or (ii)
13
+ # the Larger Works (as defined below), to deal in both
14
+ #
15
+ # (a) the Software, and
16
+ #
17
+ # (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
18
+ # one is included with the Software each a "Larger Work" to which the Software
19
+ # is contributed by such licensors),
20
+ #
21
+ # without restriction, including without limitation the rights to copy, create
22
+ # derivative works of, display, perform, and distribute the Software and make,
23
+ # use, sell, offer for sale, import, export, have made, and have sold the
24
+ # Software and the Larger Work(s), and to sublicense the foregoing rights on
25
+ # either these or other terms.
26
+ #
27
+ # This license is subject to the following condition:
28
+ #
29
+ # The above copyright notice and either this complete permission notice or at a
30
+ # minimum a reference to the UPL must be included in all copies or substantial
31
+ # portions of the Software.
32
+ #
33
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39
+ # SOFTWARE.
40
+ #
41
+ import json
42
+ import os
43
+ import subprocess
44
+ import sys
45
+ from pathlib import Path
46
+
47
+ def generate_matrix (path_to_data , libs_per_job , delimiter ):
48
+ '''
49
+ Generates a matrix in the format of GAV coordinate tuples (depending on the selected number of libraries per action job) for GitHub actions.
50
+ '''
51
+ try :
52
+ with open (os .path .join (path_to_data , 'popular-maven-libraries.json' ), 'r' ) as f :
53
+ data = json .load (f )
54
+ with open (os .path .join (path_to_data , 'excluded-popular-maven-libraries.json' ), 'r' ) as f :
55
+ exclude_data = json .load (f )
56
+ except (FileNotFoundError , json .JSONDecodeError ) as e :
57
+ print (f"Error loading files: { e } " )
58
+ sys .exit (1 )
59
+
60
+ matrix = {'coordinates' : []}
61
+ excluded_coordinates = {f'{ lib ['group_id' ]} :{ lib ['artifact_id' ]} :{ lib ['version' ]} ' for lib in exclude_data }
62
+ libs_in_job = []
63
+ for lib in data :
64
+ lib_coordinates = f'{ lib ['group_id' ]} :{ lib ['artifact_id' ]} :{ lib ['version' ]} '
65
+ if lib_coordinates in excluded_coordinates :
66
+ continue
67
+ libs_in_job .append (lib_coordinates )
68
+ if len (libs_in_job ) == libs_per_job :
69
+ matrix ['coordinates' ].append (delimiter .join (libs_in_job ))
70
+ libs_in_job = []
71
+
72
+ if len (libs_in_job ) > 0 :
73
+ matrix ['coordinates' ].append (delimiter .join (libs_in_job ))
74
+
75
+ try :
76
+ github_output = os .getenv ('GITHUB_OUTPUT' )
77
+ if github_output is None :
78
+ raise EnvironmentError ("GITHUB_OUTPUT environment variable not set" )
79
+ with open (github_output , 'a' ) as f :
80
+ f .write (f"matrix={ json .dumps (matrix )} \n " )
81
+ except (IOError , EnvironmentError ) as e :
82
+ print (f"Error writing to GITHUB_OUTPUT: { e } " )
83
+ sys .exit (1 )
84
+
85
+ def build_layers (native_image_path , coordinates , delimiter ):
86
+ '''
87
+ Builds native-image layers out of the given libraries, given their GAV coordinates and native-image path.
88
+
89
+ Firstly, the function invokes a maven command to download the library jar with all it's transitive dependencies, given its GAV coordinates.
90
+ After that, it invokes a maven command to get the full classpath of the given library.
91
+ Finally, it runs a native-image command to build the native-image layer and prints out the used command for local testing in case of issues.
92
+ '''
93
+ coordinates_list = coordinates .split (delimiter )
94
+
95
+ for gav in coordinates_list :
96
+ currDir = os .getcwd ()
97
+ group_id , artifact_id , version = gav .rstrip ().split (':' )
98
+
99
+ subprocess .run (['mvn' , 'dependency:get' , f'-Dartifact={ gav } ' , '-Dtransitive=true' ])
100
+
101
+ library_path = os .path .join (Path .home (), '.m2' , 'repository' , group_id .replace ('.' ,'/' ), artifact_id , version )
102
+ jar_path = os .path .join (library_path , f'{ artifact_id } -{ version } .jar' )
103
+ subprocess .run (['cp' , f'{ os .path .join (library_path , f'{ artifact_id } -{ version } .pom' )} ' , f'{ os .path .join (library_path , 'pom.xml' )} ' ])
104
+
105
+ if Path (library_path ).exists ():
106
+ subprocess .run (['mkdir' , gav ])
107
+ os .chdir (gav )
108
+ image_path = os .getcwd ()
109
+ os .chdir (library_path )
110
+ dependency_path = subprocess .check_output (['mvn' , '-q' , 'exec:exec' , '-Dexec.executable=echo' , '-Dexec.args=%classpath' ]).decode ('utf-8' ).rstrip ()
111
+ os .chdir (image_path )
112
+ command = [native_image_path , '-H:+UnlockExperimentalVMOptions' , '-cp' ,f'{ jar_path } :{ dependency_path } ' , f'-H:LayerCreate=layer.nil,package={ jar_path } ' , '-H:+ReportExceptionStackTraces' , '--no-fallback' , '-o' , f'{ artifact_id } -{ version } ' ] # Assertions currently excluded, see GR-57236
113
+ print (f'Command: { ' ' .join (command )} ' )
114
+ subprocess .run (command )
115
+ os .chdir ('..' )
116
+
117
+ os .chdir (currDir )
118
+
119
+ if __name__ == '__main__' :
120
+ delimiter = " && "
121
+ libs_per_job = 2
122
+ if len (sys .argv ) == 2 :
123
+ generate_matrix (sys .argv [1 ], libs_per_job , delimiter )
124
+ elif len (sys .argv ) == 3 :
125
+ build_layers (sys .argv [1 ], sys .argv [2 ], delimiter )
126
+ else :
127
+ print ("Error: Wrong number of arguments!" )
128
+ sys .exit (1 )
0 commit comments