3131import java .nio .charset .StandardCharsets ;
3232import java .nio .file .FileVisitResult ;
3333import java .nio .file .Files ;
34+ import java .nio .file .LinkOption ;
3435import java .nio .file .Path ;
3536import java .nio .file .Paths ;
3637import java .nio .file .SimpleFileVisitor ;
@@ -49,9 +50,9 @@ public final class QmakePlugin extends AbstractLanguagePlugin {
4950
5051 private static final Path TMC_TEST_RESULTS = Paths .get ("tmc_test_results.xml" );
5152
52- // Finds pattern 'POINT(exercise_name, 1)'
53+ // Finds pattern 'POINT(exercise_name, 1.1 )'
5354 private static final Pattern POINT_PATTERN
54- = Pattern .compile ("POINT\\ (\\ s*(\\ w+),\\ s*(\\ w +)\\ s*\\ )\\ s*;" );
55+ = Pattern .compile ("POINT\\ (\\ s*(\\ w+),\\ s*([^ \\ s| \\ )] +)\\ s*\\ )\\ s*;" );
5556 // Pattern to find comments
5657 private static final Pattern COMMENT_PATTERN
5758 = Pattern .compile ("(^[^\" \\ r\\ n]*\\ /\\ *{1,2}.*?\\ *\\ /"
@@ -79,8 +80,9 @@ public String getPluginName() {
7980 * Resolve the exercise .pro file from exercise directory. The file should
8081 * be named after the directory.
8182 */
82- private Path getProFile (Path basePath ) {
83- return Paths .get (basePath .toString () + "/" + basePath .getFileName () + ".pro" );
83+ private Path getProFile (Path basePath ) throws IOException {
84+ Path fullPath = basePath .toRealPath (LinkOption .NOFOLLOW_LINKS );
85+ return fullPath .resolve (fullPath .getFileName () + ".pro" );
8486 }
8587
8688 @ Override
@@ -101,7 +103,11 @@ public Optional<ExerciseDesc> scanExercise(Path path, String exerciseName) {
101103
102104 @ Override
103105 public boolean isExerciseTypeCorrect (Path path ) {
104- return Files .isRegularFile (getProFile (path ));
106+ try {
107+ return Files .isRegularFile (getProFile (path ));
108+ } catch (IOException e ) {
109+ return false ;
110+ }
105111 }
106112
107113 /**
@@ -121,31 +127,45 @@ protected StudentFilePolicy getStudentFilePolicy(Path projectPath) {
121127
122128 @ Override
123129 public RunResult runTests (Path path ) {
124- Path shadowDir = makeShadowBuildDir (path );
130+ Path fullPath ;
131+ try {
132+ fullPath = path .toRealPath (LinkOption .NOFOLLOW_LINKS );
133+ } catch (IOException e ) {
134+ log .error ("Exercise directory not found" , e );
135+ return filledFailure (Status .GENERIC_ERROR , "Exercise directory not found" );
136+ }
137+
138+ Path shadowDir ;
139+ try {
140+ shadowDir = makeShadowBuildDir (fullPath );
141+ } catch (IOException e ) {
142+ log .error ("Preparing exercise failed" , e );
143+ return filledFailure (Status .GENERIC_ERROR , "Could not create build directory" );
144+ }
125145
126146 try {
127147 ProcessResult qmakeBuild = buildWithQmake (shadowDir );
128148 if (qmakeBuild .statusCode != 0 ) {
129149 log .error ("Building project with qmake failed: {}" , qmakeBuild .errorOutput );
130- return filledFailure (qmakeBuild .errorOutput );
150+ return filledFailure (Status . COMPILE_FAILED , qmakeBuild .errorOutput );
131151 }
132152 } catch (IOException | InterruptedException e ) {
133153 log .error ("Building project with qmake failed" , e );
134- throw new QmakeBuildException ( e );
154+ return filledFailure ( Status . GENERIC_ERROR , "Building project with qmake failed" );
135155 }
136156
137157 try {
138158 ProcessResult makeBuild = buildWithMake (shadowDir );
139159 if (makeBuild .statusCode != 0 ) {
140160 log .error ("Building project with make failed: {}" , makeBuild .errorOutput );
141- return filledFailure (makeBuild .errorOutput );
161+ return filledFailure (Status . COMPILE_FAILED , makeBuild .errorOutput );
142162 }
143163 } catch (IOException | InterruptedException e ) {
144164 log .error ("Building project with make failed" , e );
145- throw new QmakeBuildException ( e );
165+ return filledFailure ( Status . GENERIC_ERROR , "Building project with make failed" );
146166 }
147167
148- Path testResults = path .resolve (TMC_TEST_RESULTS );
168+ Path testResults = shadowDir .resolve (TMC_TEST_RESULTS );
149169
150170 String target = "check" ;
151171 String config = "TESTARGS=-o " + testResults .toString () + ",xml" ;
@@ -158,11 +178,11 @@ public RunResult runTests(Path path) {
158178
159179 if (!Files .exists (testResults )) {
160180 log .error ("Failed to get test output at {}" , testResults );
161- return filledFailure (testRun .output );
181+ return filledFailure (Status . GENERIC_ERROR , testRun .output );
162182 }
163183 } catch (IOException | InterruptedException e ) {
164184 log .error ("Testing with make check failed" , e );
165- throw new QmakeBuildException ( e );
185+ return filledFailure ( Status . GENERIC_ERROR , "Testing with make check failed" );
166186 }
167187
168188 QTestResultParser parser = new QTestResultParser ();
@@ -185,11 +205,18 @@ public Map<File, List<ValidationError>> getValidationErrors() {
185205 };
186206 }
187207
188- private Path makeShadowBuildDir (Path dir ) {
189- File buildDir = dir .resolve ("build" ).toFile ();
208+ private Path makeShadowBuildDir (Path dir ) throws IOException {
209+ Path shadowPath = dir .resolve ("build" );
210+ if (Files .exists (shadowPath )) {
211+ log .info ("Shadow dir already exists at {}" , shadowPath );
212+ return shadowPath ;
213+ }
214+
215+ File buildDir = shadowPath .toFile ();
216+
190217 log .info ("Making shadow build dir to {}" , buildDir .toPath ());
191218 if (!buildDir .mkdirs ()) {
192- throw new RuntimeException (
219+ throw new IOException (
193220 "Unable to create shadow build directory: "
194221 + buildDir .toPath ());
195222 }
@@ -231,13 +258,13 @@ private ProcessResult run(String[] command, Path dir) throws IOException, Interr
231258 return runner .call ();
232259 }
233260
234- private RunResult filledFailure (String output ) {
261+ private RunResult filledFailure (Status status , String output ) {
235262 byte [] errorOutput = output .getBytes (StandardCharsets .UTF_8 );
236263 ImmutableMap <String , byte []> logs
237264 = new ImmutableMap .Builder ()
238265 .put (SpecialLogs .COMPILER_OUTPUT , errorOutput )
239266 .<String , byte []>build ();
240- return new RunResult (Status . COMPILE_FAILED , ImmutableList .<TestResult >of (), logs );
267+ return new RunResult (status , ImmutableList .<TestResult >of (), logs );
241268 }
242269
243270 /**
0 commit comments