diff --git a/lib/src/main/java/com/team2813/lib2813/util/BuildConstants.java b/lib/src/main/java/com/team2813/lib2813/util/BuildConstants.java
new file mode 100644
index 00000000..fcd2a2c1
--- /dev/null
+++ b/lib/src/main/java/com/team2813/lib2813/util/BuildConstants.java
@@ -0,0 +1,16 @@
+package com.team2813.lib2813.util;
+
+import java.time.ZonedDateTime;
+
+/** Holder for data collected at build time about the robot code. */
+public interface BuildConstants {
+
+ /** The current git branch when the code was built. */
+ String gitBranch();
+
+ /** The time the most recent commit at HEAD was submitted. */
+ ZonedDateTime gitSubmitTime();
+
+ /** The time the code was built. */
+ ZonedDateTime buildTime();
+}
diff --git a/lib/src/main/java/com/team2813/lib2813/util/BuildConstantsPublisher.java b/lib/src/main/java/com/team2813/lib2813/util/BuildConstantsPublisher.java
index bde76526..7d59e1ca 100644
--- a/lib/src/main/java/com/team2813/lib2813/util/BuildConstantsPublisher.java
+++ b/lib/src/main/java/com/team2813/lib2813/util/BuildConstantsPublisher.java
@@ -2,6 +2,8 @@
import edu.wpi.first.networktables.NetworkTable;
import edu.wpi.first.networktables.NetworkTableInstance;
+import java.util.Optional;
+import java.util.function.Function;
/**
* Publishes build constants to NetworkTables.
@@ -22,60 +24,25 @@
*
*
To instantiate a BuildConstantsPublisher, a build constants class, {@code BuildConstants}, is
* needs to be generated for the robot library by enabling the `gversion` plugin in the gradle build
- * file.
- *
- *
{@code
- * plugins {
- * ...
- * // Plugin needed for Git Build Info
- * // (see https://docs.wpilib.org/en/stable/docs/software/advanced-gradlerio/deploy-git-data.html)
- * id 'com.peterabeles.gversion' version '1.10'
- * ...
- * }
- * ...
- * // Generates a BuildConstants file.
- * // https://docs.wpilib.org/en/stable/docs/software/advanced-gradlerio/deploy-git-data.html
- * project.compileJava.dependsOn(createVersionFile)
- * def BUILD_CONSTANTS_AUTOGEN_PATH = 'build/generated/sources/build_constants/'
- * gversion {
- * // Build inside build/ (so that it will be ignored by git due to .gitignore)
- * // and inside build/generated/ (so that it will be ignored by our Spotless
- * // rules).
- * srcDir = BUILD_CONSTANTS_AUTOGEN_PATH
- * classPackage = 'com.team2813'
- * className = 'BuildConstants'
- * dateFormat = 'yyyy-MM-dd HH:mm:ss z'
- * timeZone = 'America/Los_Angeles' // Use preferred time zone
- * indent = ' '
- * }
- * sourceSets.main.java.srcDirs += BUILD_CONSTANTS_AUTOGEN_PATH
- * ...
- * }
+ * file. Instructions can be found in
+ * the WPILib documentation.
*
* With the BuildConstants generation enabled, the publisher is initialized and used like this:
*
*
{@code
- * BuildConstantsPublisher buildConstantsPublisher(com.team2813.BuildConstants.class);
+ * BuildConstantsPublisher buildConstantsPublisher(frc.robot.BuildConstants.class);
* // Publish the build constants to "/Metadata" on the NetworkTables
* buildConstantsPublisher.publish(NetworkTableInstance.getDefault());
* // Log the build constants in the robot console as well.
* buildConstantsPublisher.log();
* }
*/
-public class BuildConstantsPublisher {
+public final class BuildConstantsPublisher {
/** The name of the NetworkTable under which the build constants are published. */
public static final String METADATA_TABLE_NAME = "Metadata";
- private String m_mavenName;
- // Don't resolve BuildConstants.MAVEN_GROUP because it is always empty
- private int m_gitRevision;
- // Don't resolve BuildConstants.VERSION because it is always "unspecified".
- private String m_gitSha;
- private String m_gitDate;
- private String m_gitBranch;
- private String m_buildDate;
- private long m_buildUnixTime;
- private int m_dirty;
+ private final Optional constants;
/**
* Constructs a BuildConstantsPublisher.
@@ -87,20 +54,12 @@ public class BuildConstantsPublisher {
* constants. See class description for instructions on how to generate the class.
*/
public BuildConstantsPublisher(Class> buildConstantsClass) {
- try {
- m_mavenName = (String) buildConstantsClass.getDeclaredField("MAVEN_NAME").get(null);
- m_gitRevision = (int) buildConstantsClass.getDeclaredField("GIT_REVISION").get(null);
- m_gitSha = (String) buildConstantsClass.getDeclaredField("GIT_SHA").get(null);
- m_gitDate = (String) buildConstantsClass.getDeclaredField("GIT_DATE").get(null);
- m_gitBranch = (String) buildConstantsClass.getDeclaredField("GIT_BRANCH").get(null);
- m_buildDate = (String) buildConstantsClass.getDeclaredField("BUILD_DATE").get(null);
- m_buildUnixTime = (long) buildConstantsClass.getDeclaredField("BUILD_UNIX_TIME").get(null);
- m_dirty = (int) buildConstantsClass.getDeclaredField("DIRTY").get(null);
- } catch (NoSuchFieldException | IllegalAccessException e) {
- // TODO(vdikov): Add a proper error logging here so that developers can catch it when it
- // happens
- e.printStackTrace();
- }
+ constants = BuildConstantsRecord.fromGeneratedClass(buildConstantsClass);
+ }
+
+ /** Gets the build constants extracted from the publisher. */
+ public Optional buildConstants() {
+ return constants.map(Function.identity());
}
/**
@@ -112,26 +71,32 @@ public BuildConstantsPublisher(Class> buildConstantsClass) {
* constants are published.
*/
public void publish(NetworkTableInstance ntInstance) {
- NetworkTable table = ntInstance.getTable(METADATA_TABLE_NAME);
- table.getStringTopic("MavenName").publish().set(m_mavenName);
- table.getIntegerTopic("GitRevision").publish().set(m_gitRevision);
- table.getStringTopic("GitSha").publish().set(m_gitSha);
- table.getStringTopic("GitDate").publish().set(m_gitDate);
- table.getStringTopic("GitBranch").publish().set(m_gitBranch);
- table.getStringTopic("BuildDate").publish().set(m_buildDate);
- table.getIntegerTopic("BuildUnixTime").publish().set(m_buildUnixTime);
- table.getIntegerTopic("Dirty").publish().set(m_dirty);
+ constants.ifPresent(
+ values -> {
+ NetworkTable table = ntInstance.getTable(METADATA_TABLE_NAME);
+ table.getStringTopic("MavenName").publish().set(values.mavenName());
+ table.getIntegerTopic("GitRevision").publish().set(values.gitRevision());
+ table.getStringTopic("GitSha").publish().set(values.gitSha());
+ table.getStringTopic("GitDate").publish().set(values.gitSubmitTimeString());
+ table.getStringTopic("GitBranch").publish().set(values.gitBranch());
+ table.getStringTopic("BuildDate").publish().set(values.buildTimeString());
+ table.getIntegerTopic("BuildUnixTime").publish().set(values.buildTimeMillis());
+ table.getIntegerTopic("Dirty").publish().set(values.dirty());
+ });
}
/** Logs the build constants to the console. */
public void log() {
- System.out.println("MavenName: " + m_mavenName);
- System.out.println("GitRevision: " + m_gitRevision);
- System.out.println("GitSha: " + m_gitSha);
- System.out.println("GitDate: " + m_gitDate);
- System.out.println("GitBranch: " + m_gitBranch);
- System.out.println("BuildDate: " + m_buildDate);
- System.out.println("BuildUnixTime: " + m_buildUnixTime);
- System.out.println("Dirty: " + m_dirty);
+ constants.ifPresent(
+ values -> {
+ System.out.println("MavenName: " + values.mavenName());
+ System.out.println("GitRevision: " + values.gitRevision());
+ System.out.println("GitSha: " + values.gitSha());
+ System.out.println("GitDate: " + values.gitSubmitTimeString());
+ System.out.println("GitBranch: " + values.gitBranch());
+ System.out.println("BuildDate: " + values.buildTimeString());
+ System.out.println("BuildUnixTime: " + values.buildTimeMillis());
+ System.out.println("Dirty: " + values.dirty());
+ });
}
}
diff --git a/lib/src/main/java/com/team2813/lib2813/util/BuildConstantsRecord.java b/lib/src/main/java/com/team2813/lib2813/util/BuildConstantsRecord.java
new file mode 100644
index 00000000..56b2a6d5
--- /dev/null
+++ b/lib/src/main/java/com/team2813/lib2813/util/BuildConstantsRecord.java
@@ -0,0 +1,95 @@
+package com.team2813.lib2813.util;
+
+import edu.wpi.first.wpilibj.DriverStation;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.util.Optional;
+
+record BuildConstantsRecord(
+ String mavenName,
+ int gitRevision,
+ String gitSha,
+ String gitBranch,
+ ZonedDateTime gitSubmitTime,
+ ZonedDateTime buildTime,
+ long buildTimeMillis,
+ int dirty)
+ implements BuildConstants {
+ private static final DateTimeFormatter DATE_FORMATTER =
+ DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss z");
+
+ /**
+ * Constructs an instance from a class generated by the gversion Gradle plugin.
+ *
+ * Instructions for using the gversion Gradle plugin can be found buildConstantsClass, String fieldName)
+ throws NoSuchFieldException, IllegalAccessException {
+ String value = (String) buildConstantsClass.getDeclaredField(fieldName).get(null);
+ try {
+ return ZonedDateTime.parse(value, DATE_FORMATTER);
+ } catch (DateTimeParseException e) {
+ String message =
+ "Could not extract build constants from "
+ + buildConstantsClass.getSimpleName()
+ + " due to unparsable date-time value for "
+ + fieldName
+ + ": "
+ + e.getMessage();
+ DriverStation.reportWarning(message, e.getStackTrace());
+ return null;
+ }
+ }
+}
diff --git a/lib/src/test/java/com/team2813/lib2813/util/BuildConstantsPublisherTest.java b/lib/src/test/java/com/team2813/lib2813/util/BuildConstantsPublisherTest.java
index a105e8b8..89ddce28 100644
--- a/lib/src/test/java/com/team2813/lib2813/util/BuildConstantsPublisherTest.java
+++ b/lib/src/test/java/com/team2813/lib2813/util/BuildConstantsPublisherTest.java
@@ -11,12 +11,19 @@
import edu.wpi.first.networktables.NetworkTableInstance;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
+import java.time.Instant;
import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import org.junit.jupiter.api.Test;
public class BuildConstantsPublisherTest {
+ // This format must be consistent with the `createVersionFile` settings in the build.gradle.
+ private static final DateTimeFormatter DATE_TIME_FORMATTER =
+ DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss z");
+
// Fake constants copied and adapted from this article
// https://docs.wpilib.org/en/stable/docs/software/advanced-gradlerio/deploy-git-data.html
public final class FakeBuildConstants {
@@ -29,7 +36,7 @@ public final class FakeBuildConstants {
public static final String GIT_BRANCH = "main";
public static final String BUILD_DATE = "2023-10-27 12:29:57 EDT";
public static final long BUILD_UNIX_TIME = 1698424197122L;
- public static final int DIRTY = 0;
+ public static final int DIRTY = 1;
private FakeBuildConstants() {}
}
@@ -42,9 +49,6 @@ private FakeBuildConstants() {}
*/
private class DateTimeStringSubject extends Subject {
private final String actual;
- // The format must be consistent with the `createVersionFile` settings in the build.gradle.
- private final DateTimeFormatter formatter =
- DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss z");
private DateTimeStringSubject(FailureMetadata metadata, String actual) {
super(metadata, actual);
@@ -58,10 +62,10 @@ public void parsesAsLocalDateTime() {
}
try {
- LocalDateTime.parse(actual, formatter);
+ LocalDateTime.parse(actual, DATE_TIME_FORMATTER);
} catch (DateTimeParseException e) {
failWithActual(
- fact("expected to parse as LocalDateTime with format", formatter),
+ fact("expected to parse as LocalDateTime with format", DATE_TIME_FORMATTER),
fact("but parsing failed with", e.getMessage()));
}
}
@@ -83,6 +87,36 @@ private Long getIntegerEntryOrDefault(NetworkTable table, String key, long defau
return table.getIntegerTopic(key).getEntry(defaultValue).get();
}
+ @Test
+ public void extractsBuildConstants() {
+ // Arrange.
+ BuildConstantsPublisher publisher = new BuildConstantsPublisher(FakeBuildConstants.class);
+
+ // Act.
+ var constants = publisher.buildConstants();
+
+ // Assert.
+ ZonedDateTime expectedBuildTime =
+ ZonedDateTime.ofInstant(
+ Instant.ofEpochMilli(FakeBuildConstants.BUILD_UNIX_TIME),
+ ZoneId.of("America/New_York"))
+ .withNano(0);
+ ZonedDateTime expectedGitCommitTime =
+ ZonedDateTime.parse(FakeBuildConstants.GIT_DATE, DATE_TIME_FORMATTER);
+ var expectedRecord =
+ new BuildConstantsRecord(
+ FakeBuildConstants.MAVEN_NAME,
+ FakeBuildConstants.GIT_REVISION,
+ FakeBuildConstants.GIT_SHA,
+ FakeBuildConstants.GIT_BRANCH,
+ expectedGitCommitTime,
+ expectedBuildTime,
+ FakeBuildConstants.BUILD_UNIX_TIME,
+ FakeBuildConstants.DIRTY);
+
+ assertThat(constants).hasValue(expectedRecord);
+ }
+
@Test
public void publishesBuildConstantsToNetworkTables() {
// Arrange.
@@ -117,7 +151,9 @@ public void publishesBuildConstantsToNetworkTables() {
assertAbout(DateTimeStringSubject::new)
.that(getStringEntryOrEmpty(table, "BuildDate"))
.parsesAsLocalDateTime();
- assertThat(getIntegerEntryOrDefault(table, "Dirty", -1)).isAnyOf(0l, 1l);
+ assertThat(getIntegerEntryOrDefault(table, "Dirty", -1)).isAnyOf(0L, 1L);
+
+ ntInstance.close();
}
@Test