Skip to content

Commit

Permalink
Set up Spotless for Java, LF, etc.
Browse files Browse the repository at this point in the history
  • Loading branch information
petervdonovan committed Sep 20, 2022
1 parent dae5946 commit 9223786
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 24 deletions.
41 changes: 40 additions & 1 deletion build.gradle
@@ -1,3 +1,6 @@
import com.diffplug.gradle.spotless.SpotlessTask
import lfformat.LfFormatStep

buildscript {
repositories {
mavenCentral()
Expand All @@ -12,13 +15,14 @@ plugins {
id "com.github.johnrengelman.shadow" version "${shadowJarVersion}"
id 'java'
id 'jacoco'
id "com.diffplug.spotless" version "6.11.0"
}

subprojects {
repositories {
mavenCentral()
}

apply plugin: 'kotlin'
compileKotlin {
destinationDir = compileJava.destinationDir
Expand Down Expand Up @@ -74,3 +78,38 @@ gradle.projectsEvaluated {
}
}
}


spotless {
repositories {
mavenCentral()
}
// optional: limit format enforcement to just the files changed by this feature branch
ratchetFrom 'origin/master'

format 'misc', {
// define the files to apply `misc` to
target '*.gradle', '*.md', '.gitignore'

// define the steps to apply to those files
trimTrailingWhitespace()
indentWithSpaces() // or spaces. Takes an integer argument if you don't like 4
endWithNewline()
}

format 'linguaFranca', {
addStep(LfFormatStep.create())
target 'test/*/src/**/*.lf' // you have to set the target manually
targetExclude 'test/**/failing/**'
}

java {
target 'org.lflang*/src/**/*.java', 'buildSrc/**/*.java'
// The following is quoted from https://github.com/google/google-java-format
// "Note: There is no configurability as to the formatter's algorithm for formatting.
// This is a deliberate design decision to unify our code formatting on a single format."
googleJavaFormat('1.15.0').aosp().reflowLongStrings()
formatAnnotations()
}
}
tasks.withType(SpotlessTask) { it.dependsOn(":org.lflang.cli:jarLff") }
9 changes: 9 additions & 0 deletions buildSrc/build.gradle
@@ -0,0 +1,9 @@
repositories {
mavenCentral()
dependencies {
// https://mvnrepository.com/artifact/com.diffplug.spotless/spotless-lib
implementation group: 'com.diffplug.spotless', name: 'spotless-lib', version: '2.30.0'
// https://mvnrepository.com/artifact/com.diffplug.spotless/spotless-lib-extra
implementation group: 'com.diffplug.spotless', name: 'spotless-lib-extra', version: '2.30.0'
}
}
58 changes: 58 additions & 0 deletions buildSrc/src/main/java/lfformat/LfFormatStep.java
@@ -0,0 +1,58 @@
package lfformat;

import com.diffplug.spotless.FormatterStep;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Path;
import java.util.List;

public final class LfFormatStep {
private LfFormatStep() {}

public static FormatterStep create() {
return new Step();
}

private static final class Step implements FormatterStep {
@Override
public String format(@SuppressWarnings("NullableProblems") String rawUnix, File file)
throws IOException, InterruptedException {
// It looks stupid to invoke Java from Java, but it is necessary in
// order to break the circularity of needing the program to be built
// in order for it to be built.
Process p =
new ProcessBuilder(
List.of(
"java",
"-jar",
Path.of(
"org.lflang.cli",
"build",
"libs",
"org.lflang.cli-0.3.1-SNAPSHOT-lff.jar")
.toString(),
"--dry-run",
file.getAbsoluteFile().toString()))
.start();
StringBuilder output = new StringBuilder();
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
if (!output.isEmpty()) output.append("\n");
// Filtering by "lff: " is yet another string-processing hack that is not airtight!
if (!line.startsWith("lff: ")) output.append(line);
}
int returnCode = p.waitFor();
if (returnCode != 0) throw new RuntimeException("Failed to reformat file.");
return output.toString().stripTrailing() + "\n"; // Not sure why this is necessary
}

@SuppressWarnings("NullableProblems")
@Override
public String getName() {
return "Lingua Franca formatting step";
}
}
}
1 change: 1 addition & 0 deletions gradle.properties
Expand Up @@ -4,6 +4,7 @@ version=0.3.1-SNAPSHOT

[versions]
commonsCliVersion=1.4
googleJavaFormatVersion=1.8
guiceVersion=5.1.0
jacocoVersion=0.8.7
jupiterVersion=5.8.2
Expand Down
71 changes: 48 additions & 23 deletions org.lflang.cli/src/org/lflang/cli/Lff.java
Expand Up @@ -51,6 +51,13 @@ public class Lff extends CliBase {
*/
enum CLIOption {
HELP("h", "help", false, false, "Display this information."),
DRY_RUN(
"d",
"dry-run",
false,
false,
"Send the formatted file contents to stdout without writing to the file system."
),
LINE_WRAP(
"w",
"wrap",
Expand Down Expand Up @@ -199,6 +206,8 @@ private void runFormatter(List<Path> files) {
FormattingUtils.DEFAULT_LINE_LENGTH
: Integer.parseInt(cmd.getOptionValue(CLIOption.LINE_WRAP.option.getOpt()));

final boolean dryRun = cmd.hasOption(CLIOption.DRY_RUN.option.getOpt());

for (Path path : files) {
if (cmd.hasOption(CLIOption.VERBOSE.option.getOpt())) {
reporter.printInfo("Formatting " + path + ":");
Expand All @@ -207,12 +216,17 @@ private void runFormatter(List<Path> files) {
if (
Files.isDirectory(path)&& !cmd.hasOption(CLIOption.NO_RECURSE.option.getLongOpt())
) {
formatRecursive(Paths.get("."), path, outputRoot, lineLength);
formatRecursive(Paths.get("."), path, outputRoot, lineLength, dryRun);
} else {
if (outputRoot == null) {
formatSingleFile(path, path, lineLength);
formatSingleFile(path, path, lineLength, dryRun);
} else {
formatSingleFile(path, outputRoot.resolve(path.getFileName()), lineLength);
formatSingleFile(
path,
outputRoot.resolve(path.getFileName()),
lineLength,
dryRun
);
}
}
}
Expand All @@ -226,18 +240,24 @@ private void runFormatter(List<Path> files) {
* @param outputRoot Root output directory.
* @param lineLength The preferred maximum number of columns per line.
*/
private void formatRecursive(Path curPath, Path inputRoot, Path outputRoot, int lineLength) {
private void formatRecursive(
Path curPath,
Path inputRoot,
Path outputRoot,
int lineLength,
boolean dryRun
) {
Path curDir = inputRoot.resolve(curPath);
try (var dirStream = Files.newDirectoryStream(curDir)) {
for (Path path : dirStream) {
Path newPath = curPath.resolve(path.getFileName());
if (Files.isDirectory(path)) {
formatRecursive(newPath, inputRoot, outputRoot, lineLength);
formatRecursive(newPath, inputRoot, outputRoot, lineLength, dryRun);
} else {
if (outputRoot == null) {
formatSingleFile(path, path, lineLength);
formatSingleFile(path, path, lineLength, dryRun);
} else {
formatSingleFile(path, outputRoot.resolve(newPath), lineLength);
formatSingleFile(path, outputRoot.resolve(newPath), lineLength, dryRun);
}
}
}
Expand All @@ -249,7 +269,7 @@ private void formatRecursive(Path curPath, Path inputRoot, Path outputRoot, int
/**
* Load and validate a single file, then format it and output to the given outputPath.
*/
private void formatSingleFile(Path file, Path outputPath, int lineLength) {
private void formatSingleFile(Path file, Path outputPath, int lineLength, boolean dryRun) {
file = file.normalize();
outputPath = outputPath.normalize();
final Resource resource = getResource(file);
Expand All @@ -262,24 +282,29 @@ private void formatSingleFile(Path file, Path outputPath, int lineLength) {
validateResource(resource);

exitIfCollectedErrors();
final String formattedFileContents = FormattingUtils.render(
resource.getContents().get(0),
lineLength
);

try {
FileUtil.writeToFile(
FormattingUtils.render(resource.getContents().get(0), lineLength),
outputPath,
true
);
} catch (IOException e) {
if (e instanceof FileAlreadyExistsException) {
// only happens if a subdirectory is named with ".lf" at the end
if (dryRun) {
System.out.println(formattedFileContents);
} else {
try {
FileUtil.writeToFile(formattedFileContents, outputPath, true);
} catch (IOException e) {
if (e instanceof FileAlreadyExistsException) {
// only happens if a subdirectory is named with ".lf" at the end
reporter.printFatalErrorAndExit(
"Error writing to " + outputPath
+ ": file already exists. Make sure that no "
+ "file or directory within provided input paths have the same relative "
+ "paths.");
}
reporter.printFatalErrorAndExit(
"Error writing to " + outputPath + ": file already exists. Make sure that no "
+ "file or directory within provided input paths have the same relative "
+ "paths.");
"Error writing to " + outputPath + ": " + e.getMessage()
);
}
reporter.printFatalErrorAndExit(
"Error writing to " + outputPath + ": " + e.getMessage()
);
}

exitIfCollectedErrors();
Expand Down

0 comments on commit 9223786

Please sign in to comment.