Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/lint-format.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,5 @@ jobs:
distribution: temurin
java-version: '25'
- uses: gradle/actions/setup-gradle@v6
- run: ./gradlew spotlessCheck
- run: ./gradlew checkSharedFiles spotlessCheck
working-directory: examples
82 changes: 81 additions & 1 deletion examples/examples.gradle
Original file line number Diff line number Diff line change
@@ -1,3 +1,83 @@
import java.nio.file.Files
import java.nio.file.StandardCopyOption

// Each example project gets the contents of the shared/ directories listed here, so its build
// scripts and Gradle wrapper are identical everywhere. Edit the files in shared/ and run
// `./gradlew copySharedFiles`; `./gradlew checkSharedFiles` fails if a project is out of date.
def sharedProjects = [
'stage0/solutions' : ['wrapper', 'stage0'],
'stage0/templates' : ['wrapper', 'stage0'],
'stage0/snippets' : ['wrapper', 'stage0'],
'stage1/templates/ctre' : ['wrapper', 'stage1'],
'stage1/templates/rev' : ['wrapper', 'stage1'],
'stage1/stage1a/solutions/ctre' : ['wrapper', 'stage1'],
'stage1/stage1a/solutions/rev' : ['wrapper', 'stage1'],
'stage1/stage1b/solutions/ctre' : ['wrapper', 'stage1'],
'stage1/stage1b/solutions/rev' : ['wrapper', 'stage1'],
'stage1/stage1b/snippets' : ['wrapper', 'stage1'],
]

// [source path, destination path] for every shared file in every project.
def sharedFilePairs = []
def sharedRoot = file('shared').toPath()
sharedProjects.each { projectDir, sharedDirs ->
def target = file(projectDir).toPath()
sharedDirs.each { sharedDir ->
def source = sharedRoot.resolve(sharedDir)
source.toFile().eachFileRecurse(groovy.io.FileType.FILES) { sourceFile ->
// Skip Gradle's own cache directories
if (source.relativize(sourceFile.toPath()).startsWith('.gradle')) return
def dest = target.resolve(source.relativize(sourceFile.toPath()))
sharedFilePairs << [sourceFile.toString(), dest.toString()]
}
}
}

gradle.rootProject { root ->
root.tasks.register('copySharedFiles') {
group = 'build setup'
description = 'Copies shared/ build scripts and the Gradle wrapper into every example project.'
def pairs = sharedFilePairs
mustRunAfter 'checkSharedFiles'
inputs.files(pairs.collect { it[0] }).withPropertyName('sharedFiles')
outputs.files(pairs.collect { it[1] }).withPropertyName('projectCopies')
doLast {
pairs.each { sourcePath, destPath ->
def source = new File(sourcePath)
def dest = new File(destPath)
if (dest.exists() && dest.bytes == source.bytes) return
Files.createDirectories(dest.toPath().parent)
Files.copy(source.toPath(), dest.toPath(), StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES)
logger.lifecycle("Updated ${dest}")
}
}
}

root.tasks.register('checkSharedFiles') {
group = 'verification'
description = 'Fails if any example project differs from the shared/ files.'
def pairs = sharedFilePairs
def rootPath = root.projectDir.toPath()
inputs.files(pairs.collect { it[0] }).withPropertyName('sharedFiles')
inputs.files(pairs.collect { it[1] }).withPropertyName('projectCopies')
outputs.upToDateWhen { true }
doLast {
def stale = pairs.findAll { sourcePath, destPath ->
def dest = new File(destPath)
!dest.exists() || dest.bytes != new File(sourcePath).bytes
}
if (stale) {
def list = stale.collect { ' ' + rootPath.relativize(new File(it[1]).toPath()) }.join('\n')
throw new GradleException("Files differ from shared/. Run './gradlew copySharedFiles'.\n${list}")
}
}
}

root.tasks.matching { it.name in ['build', 'clean', 'spotlessCheck', 'spotlessApply'] }.configureEach {
dependsOn 'copySharedFiles'
}
}

// Auto-discover subprojects by scanning for build.gradle files
// Excludes build artifacts, hidden dirs, and the root itself
ext.exampleProjectDirs = []
Expand All @@ -11,4 +91,4 @@ def scanForProjects(File dir, ArrayList<String> excluded, boolean isInitial) {
dir.eachDir { subDir -> scanForProjects(subDir, excluded, false) }
}

scanForProjects(file(rootDir), ['build', '.gradle', 'bin', 'gradle', 'node_modules'], true)
scanForProjects(file(rootDir), ['build', '.gradle', 'bin', 'gradle', 'node_modules', 'shared'], true)
87 changes: 87 additions & 0 deletions examples/shared/stage0/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
plugins {
id 'java'
id "com.diffplug.spotless" version "8.0.0"
}

repositories {
mavenCentral()
}

java {
sourceCompatibility = JavaVersion.VERSION_25
targetCompatibility = JavaVersion.VERSION_25
}

sourceSets {
main {
java {
setSrcDirs(['src'])
}
}
}

def sourceFiles = fileTree('src') { include '**/*.java' }.files
def runnableFiles = sourceFiles.findAll { it.text.contains('void main(') }
def runTasks = runnableFiles.collect { src ->
def className = src.name - '.java'
tasks.register("run${className}", JavaExec) {
dependsOn 'compileJava'
classpath = sourceSets.main.output
mainClass = className
}
}

tasks.register('runAll') {
dependsOn runTasks
}

def allJavaFiles = fileTree(".") {
include "**/*.java"
exclude "**/build/**", "**/build-*/**"
}

// Compact source files (JEP 512) declare members at the top level with no enclosing
// type, which google-java-format cannot parse. They only get whitespace cleanup.
def compactSourceFiles = allJavaFiles.filter { it.text =~ /(?m)^void main\(/ }

spotless {
java {
target allJavaFiles - compactSourceFiles
toggleOffOn()
googleJavaFormat()
trimTrailingWhitespace()
endWithNewline()
}
format "compactSources", {
target compactSourceFiles
trimTrailingWhitespace()
endWithNewline()
}
groovyGradle {
target fileTree(".") {
include "**/*.gradle"
exclude "**/build/**", "**/build-*/**"
}
greclipse()
leadingTabsToSpaces(4)
trimTrailingWhitespace()
endWithNewline()
}
format "misc", {
target fileTree(".") {
include "**/*.md", "**/.gitignore"
exclude "**/build/**", "**/build-*/**"
}
trimTrailingWhitespace()
leadingTabsToSpaces(2)
endWithNewline()
}
}

tasks.named('compileJava') {
dependsOn 'spotlessJavaCheck'
}

tasks.named('check') {
dependsOn 'runAll'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
1 change: 1 addition & 0 deletions examples/shared/stage0/settings.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rootProject.name = 'stage0'
152 changes: 152 additions & 0 deletions examples/shared/stage1/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
plugins {
id "java"
id "application"
id "com.diffplug.spotless" version "8.0.0"
id "org.wpilib.GradleRIO" version "2027.0.0-alpha-8"
}

java {
sourceCompatibility = JavaVersion.VERSION_25
targetCompatibility = JavaVersion.VERSION_25
}

def ROBOT_MAIN_CLASS = "first.Main"

// Define my targets (SystemCore) and artifacts (deployable files)
// This is added by GradleRIO's backing project DeployUtils.
deploy {
targets {
systemcore(getTargetTypeClass('SystemCore')) {
// Team number is loaded either from the .wpilib/wpilib_preferences.json
// or from command line. If not found an exception will be thrown.
// You can use getTeamOrDefault(team) instead of getTeamNumber if you
// want to store a team number in this file.
team = project.wpilib.getTeamNumber()
// Use the default systemcore host name. This must be called after setting team
// as happens on the line above
useDefaultSystemcoreHostName()
debug = project.wpilib.getDebugOrDefault(false)

artifacts {
// First part is artifact name, 2nd is artifact type
// getTargetTypeClass is a shortcut to get the class type using a string

wpilibJava(getArtifactTypeClass('WPILibJavaArtifact')) {
// Set to true to use debug including JNI, which will drastically impact performance.
debugJni = false
}

// Static files artifact
wpilibStaticFileDeploy(getArtifactTypeClass('FileTreeArtifact')) {
files = project.fileTree('src/main/deploy')
directory = '/home/systemcore/deploy'
deleteOldFiles = false // Change to true to delete files on systemcore that no
// longer exist in deploy directory of this project
}
}
}
}
}

def deployArtifact = deploy.targets.systemcore.artifacts.wpilibJava

// Set to true to use debug for simulation including JNI
wpi.java.runSimWithDebugJni = false

// Set this to true to enable desktop support.
def includeDesktopSupport = false

// Defining my dependencies. In this case, WPILib (+ friends), and vendor libraries.
// Also defines JUnit 5.
dependencies {
annotationProcessor wpi.java.deps.wpilibAnnotations()
implementation wpi.java.deps.wpilib()
implementation wpi.java.vendor.java()

systemcoreDebug wpi.java.deps.wpilibJniDebug(wpi.platforms.systemcore)
systemcoreDebug wpi.java.vendor.jniDebug(wpi.platforms.systemcore)

systemcoreRelease wpi.java.deps.wpilibJniRelease(wpi.platforms.systemcore)
systemcoreRelease wpi.java.vendor.jniRelease(wpi.platforms.systemcore)

nativeDebug wpi.java.deps.wpilibJniDebug(wpi.platforms.desktop)
nativeDebug wpi.java.vendor.jniDebug(wpi.platforms.desktop)
simulationDebug wpi.sim.enableDebug()

nativeRelease wpi.java.deps.wpilibJniRelease(wpi.platforms.desktop)
nativeRelease wpi.java.vendor.jniRelease(wpi.platforms.desktop)
simulationRelease wpi.sim.enableRelease()

testImplementation 'org.junit.jupiter:junit-jupiter:5.10.1'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

test {
useJUnitPlatform()
systemProperty 'junit.jupiter.extensions.autodetection.enabled', 'true'
}

// Simulation configuration (e.g. environment variables).
wpi.sim.addGui().defaultEnabled = true
wpi.sim.addDriverstation()

application.mainClass = ROBOT_MAIN_CLASS

deployArtifact.configureApplication(application)
wpi.java.configureApplication(application)
wpi.java.configureTestTasks(test)

jar {
from('src') { into 'backup/src' }
from('vendordeps') { into 'backup/vendordeps' }
from('build.gradle') { into 'backup' }
}

// Configure string concat to always inline compile
tasks.withType(JavaCompile) {
options.compilerArgs.add '-XDstringConcat=inline'
}

project.compileJava.dependsOn(spotlessApply)
spotless {
java {
target fileTree('.') {
include '**/*.java'
exclude '**/build/**', '**/build-*/**', '**/bin/**'
}
toggleOffOn()
googleJavaFormat()
removeUnusedImports()
trimTrailingWhitespace()
endWithNewline()
}
groovyGradle {
target fileTree('.') {
include '**/*.gradle'
exclude '**/build/**', '**/build-*/**'
}
greclipse()
leadingTabsToSpaces(4)
trimTrailingWhitespace()
endWithNewline()
}
format 'xml', {
target fileTree('.') {
include '**/*.xml'
exclude '**/build/**', '**/build-*/**'
}
eclipseWtp('xml')
trimTrailingWhitespace()
leadingTabsToSpaces(2)
endWithNewline()
}
format 'misc', {
target fileTree('.') {
include '**/*.md', '**/.gitignore'
exclude '**/build/**', '**/build-*/**'
}
trimTrailingWhitespace()
leadingTabsToSpaces(2)
endWithNewline()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=permwrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=permwrapper/dists
38 changes: 38 additions & 0 deletions examples/shared/stage1/settings.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import org.gradle.internal.os.OperatingSystem

pluginManagement {
repositories {
String wpilibYear = '2027_alpha7'
File wpilibHome
def os = OperatingSystem.current()
if (os.isWindows()) {
String publicFolder = System.getenv('PUBLIC')
if (publicFolder == null) {
publicFolder = "C:\\Users\\Public"
}
def homeRoot = new File(publicFolder, "wpilib")
wpilibHome = new File(homeRoot, wpilibYear)
} else if (os.isLinux()) {
String xdgDataHome = System.getenv('XDG_DATA_HOME')
if (xdgDataHome == null || xdgDataHome.trim().isEmpty() || !new File(xdgDataHome).isAbsolute()) {
xdgDataHome = new File(System.getProperty("user.home"), ".local/share").getPath()
}
def homeRoot = new File(xdgDataHome, "wpilib")
wpilibHome = new File(homeRoot, wpilibYear)
} else {
def userFolder = System.getProperty("user.home")
def homeRoot = new File(userFolder, ".wpilib")
wpilibHome = new File(homeRoot, wpilibYear)
}
def wpilibHomeMaven = new File(wpilibHome, 'maven')
maven {
name = 'wpilibHome'
url = wpilibHomeMaven
}
mavenLocal()
gradlePluginPortal()
}
}

Properties props = System.getProperties();
props.setProperty("org.gradle.internal.native.headers.unresolved.dependencies.ignore", "true");
Binary file not shown.
Loading
Loading