48
48
49
49
import java .io .File ;
50
50
import java .io .IOException ;
51
+ import java .nio .file .Path ;
51
52
import java .security .MessageDigest ;
52
53
import java .security .NoSuchAlgorithmException ;
53
- import java .util .ArrayList ;
54
- import java .util .Enumeration ;
55
- import java .util .List ;
56
- import java .util .Set ;
54
+ import java .util .*;
57
55
import java .util .jar .JarEntry ;
58
56
import java .util .jar .JarFile ;
57
+ import java .util .stream .Collectors ;
59
58
60
59
import static java .lang .Math .max ;
61
60
import static java .lang .String .format ;
@@ -392,6 +391,22 @@ abstract class AbstractProtocMojo extends AbstractMojo {
392
391
)
393
392
private boolean clearOutputDirectory ;
394
393
394
+ /**
395
+ * When {@code true}, the .proto files to be compiled will be sorted before providing their
396
+ * paths to the protoc compiler. Otherwise, the order depends on the order in which the
397
+ * files are returned from the file system, which is nondeterministic and differs between
398
+ * file systems (and thus between Windows and macOS/Linux, for example).
399
+ * <p>
400
+ * This can for example be relevant when using documentation generation plugins for protoc.
401
+ *
402
+ * @since 0.7.2
403
+ */
404
+ @ Parameter (
405
+ required = false ,
406
+ defaultValue = "false"
407
+ )
408
+ private boolean sortProtoFiles ;
409
+
395
410
/**
396
411
* Executes the mojo.
397
412
*/
@@ -886,7 +901,11 @@ protected List<File> findProtoFilesInDirectory(final File directory) {
886
901
} catch (IOException e ) {
887
902
throw new MojoInitializationException ("Unable to retrieve the list of files: " + e .getMessage (), e );
888
903
}
889
- return protoFilesInDirectory ;
904
+ if (sortProtoFiles ) {
905
+ return sortProtoFiles (protoFilesInDirectory );
906
+ } else {
907
+ return protoFilesInDirectory ;
908
+ }
890
909
}
891
910
892
911
protected List <File > findProtoFilesInDirectories (final Iterable <File > directories ) {
@@ -900,6 +919,41 @@ protected List<File> findProtoFilesInDirectories(final Iterable<File> directorie
900
919
return protoFiles ;
901
920
}
902
921
922
+ /**
923
+ * Sorts the given list of .proto files lexicographically.
924
+ * <p>
925
+ * This results in an order like
926
+ * <ol>
927
+ * <li>/top1/a.proto</li>
928
+ * <li>/top1/sub1/a.proto</li>
929
+ * <li>/top1/sub1/b.proto</li>
930
+ * <li>/top1/sub1/bot1/d.proto</li>
931
+ * <li>/top1/sub1/bot2/c.proto</li>
932
+ * <li>/top1/sub2/x.proto</li>
933
+ * <li>/top2/z.proto</li>
934
+ * <li>/top2/sub1/y.proto</li>
935
+ * </ol>
936
+ *
937
+ * @param unsortedFiles the unsorted list of files
938
+ * @return the sorted list
939
+ */
940
+ protected List <File > sortProtoFiles (final List <File > unsortedFiles ) {
941
+ return unsortedFiles .stream ().sorted ((aFirst , aSecond ) -> {
942
+ final Path first = aFirst .getAbsoluteFile ().toPath ();
943
+ final Path firstParent = first .getParent ();
944
+ final Path second = aSecond .getAbsoluteFile ().toPath ();
945
+ final Path secondParent = second .getParent ();
946
+
947
+ // In case of equal parent directories, compare the file names
948
+ if (firstParent .equals (secondParent )) {
949
+ return first .getFileName ().compareTo (second .getFileName ());
950
+ } else {
951
+ // For different parent directories, sort parents lexicographically
952
+ return firstParent .compareTo (secondParent );
953
+ }
954
+ }).collect (Collectors .toList ());
955
+ }
956
+
903
957
/**
904
958
* Truncates the path of jar files so that they are relative to the local repository.
905
959
*
0 commit comments