- 
                Notifications
    You must be signed in to change notification settings 
- Fork 489
Description
This is a prerequisite to making the gradle build cache useful across machines (#280). Here is why:
- 
spotless formats by applying a series of steps - every step is capable of serializing its entire state, including config files, for the purpose of up-to-date checks 
- 
spotless/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessTask.java Lines 143 to 146 in eb783cd @Input public List<FormatterStep> getSteps() { return Collections.unmodifiableList(steps); } 
- 
spotless/lib/src/main/java/com/diffplug/spotless/FormatterStep.java Lines 58 to 72 in eb783cd /** * Implements a FormatterStep in a strict way which guarantees correct and lazy implementation * of up-to-date checks. This maximizes performance for cases where the FormatterStep is not * actually needed (e.g. don't load eclipse setting file unless this step is actually running) * while also ensuring that gradle can detect changes in a step's settings to determine that * it needs to rerun a format. */ abstract class Strict<State extends Serializable> extends LazyForwardingEquality<State> implements FormatterStep { private static final long serialVersionUID = 1L; /** * Implements the formatting function strictly in terms * of the input data and the result of {@link #calculateState()}. */ protected abstract String format(State state, String rawUnix, File file) throws Exception; 
- 
spotless/lib/src/main/java/com/diffplug/spotless/LazyForwardingEquality.java Lines 41 to 75 in eb783cd /** * This function is guaranteed to be called at most once. * If the state is never required, then it will never be called at all. * * Throws exception because it's likely that there will be some IO going on. */ protected abstract T calculateState() throws Exception; /** Returns the underlying state, possibly triggering a call to {{@link #calculateState()}. */ protected final T state() { // double-checked locking for lazy evaluation of calculateState if (state == null) { synchronized (this) { if (state == null) { try { state = calculateState(); } catch (Exception e) { throw ThrowingEx.asRuntime(e); } } } } return state; // will always be nonnull at this point } // override serialize output private void writeObject(ObjectOutputStream out) throws IOException { out.writeObject(state()); } // override serialize input @SuppressWarnings("unchecked") private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { state = (T) Objects.requireNonNull(in.readObject()); } 
- 
so far so good, this means that most steps will work with the gradle build cache. The problem is for some formatters that have config files. - Some formatters capture the state of these config files by loading their content into a String, these will relocate okay
- e.g. LicenseHeaderStepspotless/lib/src/main/java/com/diffplug/spotless/generic/LicenseHeaderStep.java Lines 142 to 145 in eb783cd /** Reads the license file from the given file. */ private LicenseHeaderStep(File licenseFile, Charset encoding, String delimiter, String yearSeparator) throws IOException { this(new String(Files.readAllBytes(licenseFile.toPath()), encoding), delimiter, yearSeparator); } 
- But most of them use FileSignature, which will not relocate.
- e.g. ScalaFmtStepstatic final class State implements Serializable { private static final long serialVersionUID = 1L; final JarState jarState; final FileSignature configSignature; State(String version, Provisioner provisioner, @Nullable File configFile) throws IOException { String mavenCoordinate; Matcher versionMatcher = VERSION_PRE_2_0.matcher(version); if (versionMatcher.matches()) { mavenCoordinate = MAVEN_COORDINATE_PRE_2_0; } else { mavenCoordinate = MAVEN_COORDINATE; } this.jarState = JarState.from(mavenCoordinate + version, provisioner); this.configSignature = FileSignature.signAsList(configFile == null ? Collections.emptySet() : Collections.singleton(configFile)); } 
 
- 
FileSignatureuses filesystem absolute paths and lastModified timestampts- spotless/lib/src/main/java/com/diffplug/spotless/FileSignature.java - Lines 43 to 45 in eb783cd - private final String[] filenames; - private final long[] filesizes; - private final long[] lastModified; 
 
- 
Worst of all, JarStateis used by almost every single FormatterStep, and it has aFileSignatureinside it- spotless/lib/src/main/java/com/diffplug/spotless/JarState.java - Lines 42 to 47 in eb783cd - public final class JarState implements Serializable { - private static final long serialVersionUID = 1L; - private final Set<String> mavenCoordinates; - @SuppressWarnings("unused") - private final FileSignature fileSignature;