@@ -928,14 +928,14 @@ public function apiViewBuildError(): JsonResponse
928928 $ resolvedBuildErrors = $ this ->build ->GetResolvedBuildErrors ($ type );
929929 if ($ resolvedBuildErrors !== false ) {
930930 while ($ resolvedBuildError = $ resolvedBuildErrors ->fetch ()) {
931- $ this ->addErrorResponse (BuildError:: marshal ($ resolvedBuildError , $ this ->project , $ revision ), $ response );
931+ $ this ->addErrorResponse (self :: marshalBuildError ($ resolvedBuildError , $ this ->project , $ revision ), $ response );
932932 }
933933 }
934934
935935 // Build failure table
936936 $ resolvedBuildFailures = $ this ->build ->GetResolvedBuildFailures ($ type );
937937 foreach ($ resolvedBuildFailures as $ resolvedBuildFailure ) {
938- $ marshaledResolvedBuildFailure = BuildFailure:: marshal ($ resolvedBuildFailure , $ this ->project , $ revision , false , $ buildfailure );
938+ $ marshaledResolvedBuildFailure = self :: marshalBuildFailure ($ resolvedBuildFailure , $ this ->project , $ revision , false , $ buildfailure );
939939
940940 if ($ this ->project ->DisplayLabels ) {
941941 get_labels_JSON_from_query_results ('
@@ -968,14 +968,14 @@ public function apiViewBuildError(): JsonResponse
968968 $ buildErrors = $ this ->build ->GetErrors ($ filter_error_properties );
969969
970970 foreach ($ buildErrors as $ error ) {
971- $ this ->addErrorResponse (BuildError:: marshal ($ error , $ this ->project , $ revision ), $ response );
971+ $ this ->addErrorResponse (self :: marshalBuildError ($ error , $ this ->project , $ revision ), $ response );
972972 }
973973
974974 // Build failure table
975975 $ buildFailures = $ this ->build ->GetFailures (['type ' => $ type ]);
976976
977977 foreach ($ buildFailures as $ fail ) {
978- $ failure = BuildFailure:: marshal ($ fail , $ this ->project , $ revision , true , $ buildfailure );
978+ $ failure = self :: marshalBuildFailure ($ fail , $ this ->project , $ revision , true , $ buildfailure );
979979
980980 if ($ this ->project ->DisplayLabels ) {
981981 $ labels = RichBuildAlert::findOrFail ((int ) $ fail ['id ' ])->labels ()->pluck ('text ' );
@@ -995,6 +995,121 @@ public function apiViewBuildError(): JsonResponse
995995 return response ()->json (cast_data_for_JSON ($ response ));
996996 }
997997
998+ /**
999+ * @return array{
1000+ * 'file': string,
1001+ * 'directory': string,
1002+ * }
1003+ */
1004+ private static function GetSourceFileFromBuildError (array $ data ): array
1005+ {
1006+ $ sourceFile = [];
1007+
1008+ // Detect if the source directory has already been replaced by CTest
1009+ // with /.../. If so, sourcefile is already a relative path from the
1010+ // root of the source tree.
1011+ if (str_contains ($ data ['stdoutput ' ], '/.../ ' )) {
1012+ $ parts = explode ('/ ' , $ data ['sourcefile ' ]);
1013+ $ sourceFile ['file ' ] = array_pop ($ parts );
1014+ $ sourceFile ['directory ' ] = implode ('/ ' , $ parts );
1015+ } else {
1016+ $ sourceFile ['file ' ] = basename ($ data ['sourcefile ' ]);
1017+ $ sourceFile ['directory ' ] = dirname ($ data ['sourcefile ' ]);
1018+ }
1019+
1020+ return $ sourceFile ;
1021+ }
1022+
1023+ // Ideally $data would be loaded into $this
1024+ // need an id field?
1025+ /**
1026+ * Marshals the data of a particular build error into a serializable
1027+ * friendly format.
1028+ *
1029+ * Requires the $data of a build error, the $project, and the buildupdate.revision.
1030+ *
1031+ * @return array<string,mixed>
1032+ **/
1033+ public static function marshalBuildError (array $ data , \CDash \Model \Project $ project , $ revision ): array
1034+ {
1035+ deepEncodeHTMLEntities ($ data );
1036+
1037+ $ sourceFile = self ::GetSourceFileFromBuildError ($ data );
1038+
1039+ // When building without launchers, CTest truncates the source dir to
1040+ // /.../<project-name>/. Use this pattern to linkify compiler output.
1041+ $ source_dir = "/\.\.\./[^/]+ " ;
1042+
1043+ $ marshaled = [
1044+ 'new ' => (int ) ($ data ['newstatus ' ] ?? -1 ),
1045+ 'logline ' => (int ) $ data ['logline ' ],
1046+ 'cvsurl ' => RepositoryUtils::get_diff_url ($ project ->Id , $ project ->CvsUrl ?? '' , $ sourceFile ['directory ' ], $ sourceFile ['file ' ], $ revision ),
1047+ 'precontext ' => '' ,
1048+ 'text ' => RepositoryUtils::linkify_compiler_output ($ project ->CvsUrl ?? '' , $ source_dir , $ revision , $ data ['stdoutput ' ]),
1049+ 'postcontext ' => '' ,
1050+ 'sourcefile ' => $ data ['sourcefile ' ],
1051+ 'sourceline ' => $ data ['sourceline ' ],
1052+ ];
1053+
1054+ if (isset ($ data ['subprojectid ' ])) {
1055+ $ marshaled ['subprojectid ' ] = $ data ['subprojectid ' ];
1056+ $ marshaled ['subprojectname ' ] = $ data ['subprojectname ' ];
1057+ }
1058+
1059+ return $ marshaled ;
1060+ }
1061+
1062+ /**
1063+ * Marshal a build failure, this includes the build failure arguments.
1064+ **/
1065+ public static function marshalBuildFailure ($ data , \CDash \Model \Project $ project , $ revision , $ linkifyOutput , $ buildfailure ): array
1066+ {
1067+ deepEncodeHTMLEntities ($ data );
1068+
1069+ $ marshaled = array_merge ([
1070+ 'language ' => $ data ['language ' ],
1071+ 'sourcefile ' => $ data ['sourcefile ' ],
1072+ 'targetname ' => $ data ['targetname ' ],
1073+ 'outputfile ' => $ data ['outputfile ' ],
1074+ 'outputtype ' => $ data ['outputtype ' ],
1075+ 'workingdirectory ' => $ data ['workingdirectory ' ],
1076+ 'exitcondition ' => $ data ['exitcondition ' ],
1077+ ], $ buildfailure ->GetBuildFailureArguments ((int ) $ data ['id ' ]));
1078+
1079+ $ marshaled ['stderror ' ] = $ data ['stderror ' ];
1080+ $ marshaled ['stdoutput ' ] = $ data ['stdoutput ' ];
1081+
1082+ if (isset ($ data ['sourcefile ' ])) {
1083+ $ file = basename ($ data ['sourcefile ' ]);
1084+ $ directory = dirname ($ data ['sourcefile ' ]);
1085+
1086+ $ source_dir = RepositoryUtils::get_source_dir ($ project ->Id , $ project ->CvsUrl ?? '' , $ directory );
1087+ if (str_starts_with ($ directory , $ source_dir )) {
1088+ $ directory = substr ($ directory , strlen ($ source_dir ));
1089+ }
1090+
1091+ $ marshaled ['cvsurl ' ] = RepositoryUtils::get_diff_url ($ project ->Id ,
1092+ $ project ->CvsUrl ?? '' ,
1093+ $ directory ,
1094+ $ file ,
1095+ $ revision );
1096+
1097+ if ($ source_dir !== null && $ linkifyOutput ) {
1098+ $ marshaled ['stderror ' ] = RepositoryUtils::linkify_compiler_output ($ project ->CvsUrl ?? '' , $ source_dir ,
1099+ $ revision , $ data ['stderror ' ]);
1100+ $ marshaled ['stdoutput ' ] = RepositoryUtils::linkify_compiler_output ($ project ->CvsUrl ?? '' , $ source_dir ,
1101+ $ revision , $ data ['stdoutput ' ]);
1102+ }
1103+ }
1104+
1105+ if (isset ($ data ['subprojectid ' ])) {
1106+ $ marshaled ['subprojectid ' ] = $ data ['subprojectid ' ];
1107+ $ marshaled ['subprojectname ' ] = $ data ['subprojectname ' ];
1108+ }
1109+
1110+ return $ marshaled ;
1111+ }
1112+
9981113 public function manageBuildGroup (): View
9991114 {
10001115 $ this ->setProjectById (request ()->integer ('projectid ' ));
0 commit comments