1+ @Grab (group = ' org.yaml' , module = ' snakeyaml' , version = ' 1.25' )
2+ @Grab (group = ' org.codehaus.groovy' , module = ' groovy-cli-commons' , version = ' 2.5.7' )
3+
4+ import groovy.cli.commons.CliBuilder
5+ import groovy.transform.Field
6+ import org.yaml.snakeyaml.Yaml
7+
8+ import java.nio.file.Files
9+ import java.nio.file.Paths
10+ import java.util.logging.Level
11+ import java.util.logging.Logger
12+
13+ @Field static final String CONFIG_AGENT_ARGS = ' native-image.agent.args'
14+ @Field static final String CONFIG_BUILD_ADDITIONAL_ARGS = ' native-image.build.additional-args'
15+ @Field static final String CONFIG_BUILD_OVERRIDE_ARGS = ' native-image.build.override-args'
16+ @Field static final String ARG_GRAPE_DISABLE = ' -Dgroovy.grape.enable=false'
17+ @Field static final String REGEX_ENV_VARIABLE = ' \\ $\\ {([A-Z_]+)\\ }'
18+
19+ System . setProperty(' java.util.logging.SimpleFormatter.format' ,
20+ ' %1$tY-%1$tm-%1$tdT%1$tH:%1$tM:%1$tS.%1$tL%1$tz %4$s %5$s%6$s%n' )
21+ @Field Logger logger = Logger . getLogger(' CreateNativeImage.log' )
22+
23+ def cli = new CliBuilder (usage : ' groovy CreateNativeImage.groovy [options]' )
24+ cli. m(longOpt : ' main-class-name' , args : 1 , argName : ' main-class-name' , ' The name of the main class' )
25+ cli. j(longOpt : ' jar-name' , args : 1 , argName : ' jar-name' , ' The jar containing the main class' )
26+ cli. c(longOpt : ' config' , args : 1 , argName : ' config' , ' File containing configuration for the native image build' )
27+ cli. cp(longOpt : ' classpath' , args : 1 , argName : ' classpath' , ' Classpath for the native image build' )
28+ cli. rp(longOpt : ' reflection-config-path' , args : 1 , argName : ' reflection-config-path' , ' Path in which to generate the reflection config' )
29+ cli. d(longOpt : ' debug' , args : 0 , argName : ' debug' , ' Enable debug logs' )
30+
31+ def options = cli. parse(args)
32+ if (! (options. m || options. j)) {
33+ cli. usage()
34+ System . exit(1 )
35+ }
36+
37+ // Enable debug logs
38+ if (options. d) {
39+ Logger root = Logger . getLogger(' ' )
40+ root. setLevel(Level . FINE )
41+ root. getHandlers(). each { it. setLevel(Level . FINE ) }
42+ }
43+
44+ // Read config file
45+ String configFile = options. c
46+ def config = [:]
47+ if (configFile) {
48+ Yaml yaml = new Yaml ()
49+ config. putAll(getFlattenedMap(' ' , yaml. load(new File (configFile). text)))
50+ }
51+ logger. fine({ String . valueOf(config) })
52+
53+ // Generate reflection config
54+ String classPath = options. cp ?: ' build/native/libs/*:build/native/classes'
55+ String reflectConfigPath = options. rp ?: ' build/native/graal-config/'
56+ Files . createDirectories(Paths . get(reflectConfigPath))
57+
58+ def firstCommand = [' java' , " -agentlib:native-image-agent=config-output-dir=${ reflectConfigPath} " ,
59+ ' -cp' , classPath, ARG_GRAPE_DISABLE ]
60+ def baseCommand = [' java' , " -agentlib:native-image-agent=config-merge-dir=${ reflectConfigPath} " ,
61+ ' -cp' , classPath, ARG_GRAPE_DISABLE ]
62+ def executable = []
63+ if (options. m) {
64+ executable. add(options. m)
65+ } else {
66+ executable. add(' -jar' )
67+ executable. add(options. j)
68+ }
69+ firstCommand. addAll(executable)
70+ baseCommand. addAll(executable)
71+
72+ if (config[CONFIG_AGENT_ARGS ]) {
73+ boolean first = true
74+ def commandWithArgs = []
75+
76+ config[CONFIG_AGENT_ARGS ]. each { arguments ->
77+ commandWithArgs. clear()
78+
79+ if (first) {
80+ commandWithArgs. addAll(firstCommand)
81+ first = false
82+ } else {
83+ commandWithArgs. addAll(baseCommand)
84+ }
85+
86+ arguments. each { argument ->
87+ // If argument is an environment variable, replace it with its value if present
88+ if (argument =~ REGEX_ENV_VARIABLE ) {
89+ String envVariableName = argument. find(REGEX_ENV_VARIABLE ) { matchedText , group ->
90+ return group
91+ }
92+ String envVariableValue = System . getenv(envVariableName)
93+ commandWithArgs. add(envVariableValue ?: argument)
94+ } else {
95+ commandWithArgs. add(argument)
96+ }
97+ }
98+ executeCommand(commandWithArgs)
99+ }
100+ } else {
101+ executeCommand(firstCommand)
102+ }
103+
104+ /* * Build native image **/
105+ def nativeImageCommand = [' native-image' , ' -cp' , classPath]
106+
107+ // If override arguments are specified, add only those
108+ if (config[CONFIG_BUILD_OVERRIDE_ARGS ]) {
109+ nativeImageCommand. addAll(config[CONFIG_BUILD_OVERRIDE_ARGS ])
110+ } else {
111+ nativeImageCommand. addAll([ARG_GRAPE_DISABLE , ' --no-server' , ' --allow-incomplete-classpath' ,
112+ ' --report-unsupported-elements-at-runtime' , ' --initialize-at-build-time' ,
113+ ' --enable-url-protocols=http,https' , " -H:ConfigurationFileDirectories=${ reflectConfigPath} " ])
114+
115+ if (config[CONFIG_BUILD_ADDITIONAL_ARGS ]) {
116+ nativeImageCommand. addAll(config[CONFIG_BUILD_ADDITIONAL_ARGS ])
117+ }
118+ }
119+ nativeImageCommand. addAll(executable)
120+ executeCommand(nativeImageCommand)
121+
122+
123+ /**
124+ * Executes a command
125+ *
126+ * @param command
127+ */
128+ void executeCommand (def command ) {
129+ logger. fine({ String . valueOf(command) })
130+ Process process = command. execute()
131+ process. consumeProcessOutput(System . out, System . err)
132+ process. waitFor()
133+ }
134+
135+ /**
136+ * Converts the YAML configuration into a flat map
137+ *
138+ * @param prefix
139+ * @param map
140+ * @return
141+ */
142+ Map getFlattenedMap (String prefix , Map map ) {
143+ Map flatMap = [:]
144+
145+ map. each { key , value ->
146+ if (value instanceof Map ) {
147+ flatMap. putAll(getFlattenedMap(prefix + key + ' .' , value))
148+ } else {
149+ flatMap[(prefix + key)] = value
150+ }
151+ }
152+
153+ return flatMap
154+ }
0 commit comments