@@ -4,19 +4,25 @@ pub mod policy;
44
55use super :: { error:: JavaError , plugin:: JavaPlugin , CompileResult , TestRun , SEPARATOR } ;
66
7+ use flate2:: read:: GzDecoder ;
78use j4rs:: Jvm ;
89use policy:: MavenStudentFilePolicy ;
9- use std:: fs;
10- use std:: path:: Path ;
10+ use std:: ffi:: OsString ;
11+ use std:: fs:: { self , File } ;
12+ use std:: io:: { Cursor , Write } ;
13+ use std:: path:: { Path , PathBuf } ;
1114use std:: process:: Command ;
1215use std:: time:: Duration ;
16+ use tar:: Archive ;
1317use tmc_langs_framework:: {
1418 domain:: { ExerciseDesc , RunResult } ,
1519 plugin:: { Language , LanguagePlugin , ValidationResult } ,
1620 policy:: StudentFilePolicy ,
1721 Error ,
1822} ;
1923
24+ const MVN_ARCHIVE : & [ u8 ] = include_bytes ! ( "../apache-maven-3.6.3-bin.tar.gz" ) ;
25+
2026pub struct MavenPlugin {
2127 jvm : Jvm ,
2228}
@@ -26,6 +32,30 @@ impl MavenPlugin {
2632 let jvm = crate :: instantiate_jvm ( ) ?;
2733 Ok ( Self { jvm } )
2834 }
35+
36+ // check if mvn is in PATH, if yes return mvn
37+ // if not, check if the bundled maven has been extracted already,
38+ // if not, extract
39+ // finally, return the path to the extracted executable
40+ fn get_mvn_command ( & self ) -> Result < OsString , JavaError > {
41+ // check if mvn is in PATH
42+ if let Ok ( status) = Command :: new ( "mvn" ) . arg ( "--version" ) . status ( ) {
43+ if status. success ( ) {
44+ return Ok ( OsString :: from ( "mvn" ) ) ;
45+ }
46+ }
47+ log:: debug!( "could not execute mvn" ) ;
48+ let tmc_path = dirs:: cache_dir ( ) . ok_or ( JavaError :: CacheDir ) ?. join ( "tmc" ) ;
49+ let mvn_exec = tmc_path. join ( "apache-maven-3.6.3" ) . join ( "bin" ) . join ( "mvn" ) ;
50+ if !mvn_exec. exists ( ) {
51+ log:: debug!( "extracting bundled tar" ) ;
52+ let tar = GzDecoder :: new ( Cursor :: new ( MVN_ARCHIVE ) ) ;
53+ let mut tar = Archive :: new ( tar) ;
54+ tar. unpack ( & tmc_path)
55+ . map_err ( |e| JavaError :: MvnUnpack ( tmc_path, e) ) ?;
56+ }
57+ Ok ( mvn_exec. as_os_str ( ) . to_os_string ( ) )
58+ }
2959}
3060
3161impl LanguagePlugin for MavenPlugin {
@@ -95,18 +125,24 @@ impl JavaPlugin for MavenPlugin {
95125 let class_path_file = temp. path ( ) . join ( "cp.txt" ) ;
96126
97127 let output_arg = format ! ( "-Dmdep.outputFile={}" , class_path_file. display( ) ) ;
98- let output = Command :: new ( "mvn" )
128+ let mvn_path = self . get_mvn_command ( ) ?;
129+ let output = Command :: new ( & mvn_path)
99130 . current_dir ( path)
100131 . arg ( "dependency:build-classpath" )
101132 . arg ( output_arg)
102133 . output ( )
103- . map_err ( |e| JavaError :: FailedToRun ( "mvn" , e) ) ?;
134+ . map_err ( |e| {
135+ JavaError :: FailedToRun ( mvn_path. as_os_str ( ) . to_string_lossy ( ) . to_string ( ) , e)
136+ } ) ?;
104137
105138 log:: trace!( "stdout: {}" , String :: from_utf8_lossy( & output. stdout) ) ;
106139 log:: debug!( "stderr: {}" , String :: from_utf8_lossy( & output. stderr) ) ;
107140
108141 if !output. status . success ( ) {
109- return Err ( JavaError :: FailedCommand ( "mvn" , output. stderr ) ) ;
142+ return Err ( JavaError :: FailedCommand (
143+ mvn_path. as_os_str ( ) . to_string_lossy ( ) . to_string ( ) ,
144+ output. stderr ,
145+ ) ) ;
110146 }
111147
112148 let class_path = fs:: read_to_string ( & class_path_file)
@@ -129,19 +165,25 @@ impl JavaPlugin for MavenPlugin {
129165 fn build ( & self , project_root_path : & Path ) -> Result < CompileResult , JavaError > {
130166 log:: info!( "Building maven project at {}" , project_root_path. display( ) ) ;
131167
132- let output = Command :: new ( "mvn" )
168+ let mvn_path = self . get_mvn_command ( ) ?;
169+ let output = Command :: new ( & mvn_path)
133170 . current_dir ( project_root_path)
134171 . arg ( "clean" )
135172 . arg ( "compile" )
136173 . arg ( "test-compile" )
137174 . output ( )
138- . map_err ( |e| JavaError :: FailedToRun ( "mvn" , e) ) ?;
175+ . map_err ( |e| {
176+ JavaError :: FailedToRun ( mvn_path. as_os_str ( ) . to_string_lossy ( ) . to_string ( ) , e)
177+ } ) ?;
139178
140179 log:: trace!( "stdout: {}" , String :: from_utf8_lossy( & output. stdout) ) ;
141180 log:: debug!( "stderr: {}" , String :: from_utf8_lossy( & output. stderr) ) ;
142181
143182 if !output. status . success ( ) {
144- return Err ( JavaError :: FailedCommand ( "mvn" , output. stderr ) ) ;
183+ return Err ( JavaError :: FailedCommand (
184+ mvn_path. as_os_str ( ) . to_string_lossy ( ) . to_string ( ) ,
185+ output. stderr ,
186+ ) ) ;
145187 }
146188
147189 Ok ( CompileResult {
@@ -158,17 +200,23 @@ impl JavaPlugin for MavenPlugin {
158200 ) -> Result < TestRun , JavaError > {
159201 log:: info!( "Running tests for maven project at {}" , path. display( ) ) ;
160202
161- let output = Command :: new ( "mvn" )
203+ let mvn_path = self . get_mvn_command ( ) ?;
204+ let output = Command :: new ( & mvn_path)
162205 . current_dir ( path)
163206 . arg ( "fi.helsinki.cs.tmc:tmc-maven-plugin:1.12:test" )
164207 . output ( )
165- . map_err ( |e| JavaError :: FailedToRun ( "mvn" , e) ) ?;
208+ . map_err ( |e| {
209+ JavaError :: FailedToRun ( mvn_path. as_os_str ( ) . to_string_lossy ( ) . to_string ( ) , e)
210+ } ) ?;
166211
167212 log:: trace!( "stdout: {}" , String :: from_utf8_lossy( & output. stdout) ) ;
168213 log:: debug!( "stderr: {}" , String :: from_utf8_lossy( & output. stderr) ) ;
169214
170215 if !output. status . success ( ) {
171- return Err ( JavaError :: FailedCommand ( "mvn" , output. stderr ) ) ;
216+ return Err ( JavaError :: FailedCommand (
217+ mvn_path. as_os_str ( ) . to_string_lossy ( ) . to_string ( ) ,
218+ output. stderr ,
219+ ) ) ;
172220 }
173221
174222 Ok ( TestRun {
@@ -314,4 +362,17 @@ mod test {
314362 "com.puppycrawl.tools.checkstyle.checks.indentation.IndentationCheck"
315363 ) ;
316364 }
365+
366+ // TODO: currently will extract maven to your cache directory
367+ #[ test]
368+ fn unpack_bundled_mvn ( ) {
369+ let plugin = MavenPlugin :: new ( ) . unwrap ( ) ;
370+ std:: env:: set_var ( "PATH" , "" ) ;
371+ let cmd = plugin. get_mvn_command ( ) . unwrap ( ) ;
372+ let expected = format ! (
373+ "tmc{0}apache-maven-3.6.3{0}bin{0}mvn" ,
374+ std:: path:: MAIN_SEPARATOR
375+ ) ;
376+ assert ! ( cmd. to_string_lossy( ) . ends_with( & expected) )
377+ }
317378}
0 commit comments