Можно ли заставить компилятор Java 8 создавать воспроизводимые файлы классов? - PullRequest
7 голосов
/ 13 марта 2019

У моего работодателя есть бизнес-необходимость сделать воспроизводимые побайтные сборки Java.Я знаю о трудностях создания воспроизводимых файлов JAR (из-за порядка и меток архивирования), но сейчас я говорю о файлах классов.

У меня есть сборки того же кода с использованием Java 8u65,как на Mac, так и на Linux.Файлы классов отличаются друг от друга.Оба класса декомпилируются обратно в один и тот же источник;чтобы увидеть разницу, требуется дизассемблер javap.

Исходный код выглядит следующим образом:

final TrustStrategy acceptingTrustStrategy =
              (X509Certificate[] chain, String authType) -> true;

В одной сборке результат:

private static boolean lambda$restTemplate$38(java.security.cert.X509Certificate[], java.lang.String) throws java.security.cert.CertificateException;
        Code:
           0: iconst_1
           1: ireturn
     

Надругое:

private static boolean lambda$restTemplate$15(java.security.cert.X509Certificate[], java.lang.String) throws java.security.cert.CertificateException;
        Code:
           0: iconst_1
           1: ireturn

Анонимные лямбды получают имена с разными номерами (lambda$restTemplate$15 против lambda$restTemplate$38).

Похоже, что когда я перестраиваю на том жехост, я получаю те же байты.Когда хост отличается, номера меняются;два хоста Linux выдавали разные байты.

Что определяет эти числа?Есть ли способ заставить каждую компиляцию использовать одни и те же числа в этом месте и, таким образом, создавать одинаковые файлы классов?Или компиляция файла класса Java 8 является неопределенной?

Ответы [ 3 ]

3 голосов
/ 15 марта 2019

Подсчет лямбда-выражений выполняется компилятором и увеличивается при обнаружении других лямбда-выражений.

Если файлы читаются компилятором в том же порядке, он должен давать одинаковые скомпилированные классы.

В любом случае, поскольку вы сами создаете код, вы можете просто изменить лямбда-выражение на анонимные объявления классов.

РЕДАКТИРОВАТЬ : Я только что заметил, что вы указали, что классы построены на двух разных ОС. Это может внести разницу в фазе компиляции вашего кода. Чтобы иметь воспроизводимую сборку, она должна быть выполнена на той же архитектуре. Есть ли причина, по которой вы не можете развернуть артефакты как построенные на одной архитектуре (MacOS или Linux)?

3 голосов
/ 15 марта 2019

Я не слишком разбирался в этом, но эта статья рассказывает о воспроизводимых сборках в Java, а reproducible-builds имеет некоторые инструменты, помогающие создавать сборки (и классы) воспроизводимые.

Ссылка, которую вы, вероятно, ищете, - это Reproducible Build Maven Plugin , созданный специально для Java, чтобы попытаться "удалить невоспроизводимые данные из сгенерированных артефактов".

1 голос
/ 21 марта 2019

Как упоминалось в статье DZone, связанной с ответом Майора, для gradle это все, что вам нужно:

tasks.withType(AbstractArchiveTask) {
    preserveFileTimestamps = false
    reproducibleFileOrder = true
}

После добавления этого значения в build.gradle сумма md5 файла .jar была стабильной.между сборками в одной системе.Я не смог протестировать с другими системами, потому что у всех, кого я спрашивал, были разные версии компилятора, и это отличало сборку.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...