1
1
use std:: env;
2
2
use std:: fs;
3
- use std:: io:: { self , Write } ;
3
+ use std:: io:: { self , Write , Read } ;
4
4
use std:: path:: { Path , PathBuf } ;
5
5
use std:: process:: Command ;
6
6
use regex:: Regex ;
7
7
use zip:: write:: { SimpleFileOptions , ZipWriter } ;
8
- use zip:: CompressionMethod ;
8
+ use zip:: { CompressionMethod , ZipArchive } ;
9
9
use tempfile:: NamedTempFile ;
10
10
11
11
fn main ( ) -> Result < ( ) , i32 > {
@@ -129,6 +129,23 @@ fn find_main_classes(class_files: &[String]) -> Vec<String> {
129
129
main_classes
130
130
}
131
131
132
+ fn process_jar_file ( jar_path : & str ) -> io:: Result < Vec < ( String , Vec < u8 > ) > > {
133
+ let jar_file = fs:: File :: open ( jar_path) ?;
134
+ let mut archive = ZipArchive :: new ( jar_file) ?;
135
+ let mut class_files = Vec :: new ( ) ;
136
+
137
+ for i in 0 ..archive. len ( ) {
138
+ let mut file = archive. by_index ( i) ?;
139
+ if file. name ( ) . ends_with ( ".class" ) {
140
+ let mut contents = Vec :: new ( ) ;
141
+ file. read_to_end ( & mut contents) ?;
142
+ class_files. push ( ( file. name ( ) . to_string ( ) , contents) ) ;
143
+ }
144
+ }
145
+
146
+ Ok ( class_files)
147
+ }
148
+
132
149
fn create_jar (
133
150
input_files : & [ String ] ,
134
151
output_jar_path : & str ,
@@ -148,61 +165,100 @@ fn create_jar(
148
165
149
166
let re = Regex :: new ( r"^(.*?)-[0-9a-f]+(\.class)$" ) . unwrap ( ) ;
150
167
151
- for input_file_path_str in input_files {
152
- let input_path = Path :: new ( input_file_path_str) ;
153
- let original_file_name = input_path. file_name ( )
154
- . ok_or_else ( || io:: Error :: new ( io:: ErrorKind :: InvalidInput , format ! ( "Invalid input path: {}" , input_file_path_str) ) ) ?
155
- . to_str ( )
156
- . ok_or_else ( || io:: Error :: new ( io:: ErrorKind :: InvalidData , "Input filename is not valid UTF-8" ) ) ?;
168
+ // Track all class files we've added to avoid duplicates
169
+ let mut added_class_files = std:: collections:: HashSet :: new ( ) ;
157
170
158
- let jar_entry_name = if let Some ( caps) = re. captures ( original_file_name) {
159
- format ! ( "{}{}" , & caps[ 1 ] , & caps[ 2 ] )
160
- } else {
161
- original_file_name. to_string ( )
162
- } ;
171
+ for input_file_path_str in input_files {
172
+ if input_file_path_str. ends_with ( ".jar" ) {
173
+ println ! ( "Processing JAR file: {}" , input_file_path_str) ;
174
+ let class_files = process_jar_file ( input_file_path_str) ?;
175
+
176
+ for ( class_path, class_data) in class_files {
177
+ if !added_class_files. contains ( & class_path) {
178
+ let processed_data = if let Some ( processor_path) = processor_jar_path {
179
+ let temp_out_file = NamedTempFile :: new ( ) ?;
180
+ let temp_in_file = NamedTempFile :: new ( ) ?;
181
+ fs:: write ( & temp_in_file, & class_data) ?;
163
182
164
- let file_data_to_add : Vec < u8 > ;
165
- let mut _temp_file_handle : Option < NamedTempFile > = None ;
183
+ let temp_out_path = temp_out_file . path ( ) . to_path_buf ( ) ;
184
+ let temp_in_path = temp_in_file . path ( ) . to_path_buf ( ) ;
166
185
167
- if input_file_path_str. ends_with ( ".class" ) {
168
- println ! ( "Processing class file: {}" , input_file_path_str) ;
186
+ let mut cmd = Command :: new ( "java" ) ;
187
+ cmd. arg ( "-jar" )
188
+ . arg ( processor_path)
189
+ . arg ( & temp_in_path)
190
+ . arg ( & temp_out_path) ;
169
191
170
- let file_data = if let Some ( processor_path) = processor_jar_path {
171
- let temp_out_file = NamedTempFile :: new ( ) ?;
172
- let temp_out_path = temp_out_file. path ( ) . to_path_buf ( ) ;
192
+ println ! ( "Running processor on {}: {:?}" , class_path, cmd) ;
193
+ let output = cmd. output ( ) ?;
173
194
174
- let mut cmd = Command :: new ( "java" ) ;
175
- cmd. arg ( "-jar" )
176
- . arg ( processor_path)
177
- . arg ( input_file_path_str)
178
- . arg ( & temp_out_path) ;
195
+ if !output. status . success ( ) {
196
+ eprintln ! ( "Error processing file from JAR: {}" , class_path) ;
197
+ eprintln ! ( "Processor STDOUT:\n {}" , String :: from_utf8_lossy( & output. stdout) ) ;
198
+ eprintln ! ( "Processor STDERR:\n {}" , String :: from_utf8_lossy( & output. stderr) ) ;
199
+ return Err ( io:: Error :: new ( io:: ErrorKind :: Other , "ASM processor failed" ) ) ;
200
+ }
179
201
180
- println ! ( "Running processor: {:?}" , cmd) ;
181
- let output = cmd. output ( ) ?;
202
+ fs:: read ( & temp_out_path) ?
203
+ } else {
204
+ class_data
205
+ } ;
182
206
183
- if !output. status . success ( ) {
184
- eprintln ! ( "Error processing file: {}" , input_file_path_str) ;
185
- eprintln ! ( "Processor STDOUT:\n {}" , String :: from_utf8_lossy( & output. stdout) ) ;
186
- eprintln ! ( "Processor STDERR:\n {}" , String :: from_utf8_lossy( & output. stderr) ) ;
187
- return Err ( io:: Error :: new ( io:: ErrorKind :: Other , "ASM processor failed" ) ) ;
207
+ zip_writer. start_file ( & class_path, options) ?;
208
+ zip_writer. write_all ( & processed_data) ?;
209
+ added_class_files. insert ( class_path. clone ( ) ) ;
210
+ println ! ( "Added class from JAR: {}" , class_path) ;
188
211
}
212
+ }
213
+ } else if input_file_path_str. ends_with ( ".class" ) {
214
+ let input_path = Path :: new ( input_file_path_str) ;
215
+ let original_file_name = input_path. file_name ( )
216
+ . ok_or_else ( || io:: Error :: new ( io:: ErrorKind :: InvalidInput , format ! ( "Invalid input path: {}" , input_file_path_str) ) ) ?
217
+ . to_str ( )
218
+ . ok_or_else ( || io:: Error :: new ( io:: ErrorKind :: InvalidData , "Input filename is not valid UTF-8" ) ) ?;
189
219
190
- let processed_data = fs:: read ( & temp_out_path) ?;
191
- _temp_file_handle = Some ( temp_out_file) ;
192
- processed_data
220
+ let jar_entry_name = if let Some ( caps) = re. captures ( original_file_name) {
221
+ format ! ( "{}{}" , & caps[ 1 ] , & caps[ 2 ] )
193
222
} else {
194
- fs :: read ( input_file_path_str ) ?
223
+ original_file_name . to_string ( )
195
224
} ;
196
225
197
- file_data_to_add = file_data;
198
- println ! ( "Successfully processed: {}" , input_file_path_str) ;
226
+ if !added_class_files. contains ( & jar_entry_name) {
227
+ println ! ( "Processing class file: {}" , input_file_path_str) ;
228
+
229
+ let file_data = if let Some ( processor_path) = processor_jar_path {
230
+ let temp_out_file = NamedTempFile :: new ( ) ?;
231
+ let temp_out_path = temp_out_file. path ( ) . to_path_buf ( ) ;
232
+
233
+ let mut cmd = Command :: new ( "java" ) ;
234
+ cmd. arg ( "-jar" )
235
+ . arg ( processor_path)
236
+ . arg ( input_file_path_str)
237
+ . arg ( & temp_out_path) ;
238
+
239
+ println ! ( "Running processor: {:?}" , cmd) ;
240
+ let output = cmd. output ( ) ?;
241
+
242
+ if !output. status . success ( ) {
243
+ eprintln ! ( "Error processing file: {}" , input_file_path_str) ;
244
+ eprintln ! ( "Processor STDOUT:\n {}" , String :: from_utf8_lossy( & output. stdout) ) ;
245
+ eprintln ! ( "Processor STDERR:\n {}" , String :: from_utf8_lossy( & output. stderr) ) ;
246
+ return Err ( io:: Error :: new ( io:: ErrorKind :: Other , "ASM processor failed" ) ) ;
247
+ }
248
+
249
+ fs:: read ( & temp_out_path) ?
250
+ } else {
251
+ fs:: read ( input_file_path_str) ?
252
+ } ;
253
+
254
+ zip_writer. start_file ( & jar_entry_name, options) ?;
255
+ zip_writer. write_all ( & file_data) ?;
256
+ added_class_files. insert ( jar_entry_name) ;
257
+ println ! ( "Successfully processed: {}" , input_file_path_str) ;
258
+ }
199
259
} else {
200
- println ! ( "Skipping non-class file: {}" , input_file_path_str) ;
201
- continue ;
260
+ println ! ( "Skipping non-class/jar file: {}" , input_file_path_str) ;
202
261
}
203
-
204
- zip_writer. start_file ( & jar_entry_name, options) ?;
205
- zip_writer. write_all ( & file_data_to_add) ?;
206
262
}
207
263
208
264
zip_writer. finish ( ) ?;
0 commit comments