├── .gitattributes ├── .github └── workflows │ ├── ci.yml │ └── gradle-wrapper-validation.yml ├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── img └── tty.gif ├── settings.gradle ├── src ├── main │ ├── java │ │ └── edu │ │ │ └── wpi │ │ │ └── first │ │ │ └── gradlerio │ │ │ ├── GradleRIOPlugin.java │ │ │ ├── OneDriveException.java │ │ │ ├── PreemptiveDownloadTask.java │ │ │ ├── SingletonTask.java │ │ │ ├── caching │ │ │ └── WrapperInspector.java │ │ │ ├── deploy │ │ │ ├── DebugFileTask.java │ │ │ ├── DebugInfo.java │ │ │ ├── DebuggableArtifact.java │ │ │ ├── DebuggableJavaArtifact.java │ │ │ ├── DebuggableNativeArtifact.java │ │ │ ├── DeployStage.java │ │ │ ├── FRCDeployPlugin.java │ │ │ ├── FRCExtension.java │ │ │ ├── JavaTargetDebugInfo.java │ │ │ ├── NativeTargetDebugInfo.java │ │ │ ├── StageDeployTask.java │ │ │ ├── StagedDeployTarget.java │ │ │ ├── TargetDebugFileTask.java │ │ │ ├── TargetDebugInfo.java │ │ │ ├── TeamNumberNotFoundException.java │ │ │ ├── WPIRemoteTarget.java │ │ │ └── roborio │ │ │ │ ├── ByteArrayOutputStreamAccessor.java │ │ │ │ ├── DSDeployLocation.java │ │ │ │ ├── FMSConnectedException.java │ │ │ │ ├── FRCJNILibraryArtifact.java │ │ │ │ ├── FRCJREArtifact.java │ │ │ │ ├── FRCJavaArtifact.java │ │ │ │ ├── FRCNativeArtifact.java │ │ │ │ ├── FRCNativeLibraryArtifact.java │ │ │ │ ├── FRCProgramKillArtifact.java │ │ │ │ ├── FRCProgramStartArtifact.java │ │ │ │ ├── GarbageCollectorType.java │ │ │ │ ├── InvalidImageException.java │ │ │ │ ├── RoboRIO.java │ │ │ │ └── RobotCommandArtifact.java │ │ │ ├── simulation │ │ │ ├── HalSimPair.java │ │ │ ├── JavaExternalSimulationTask.java │ │ │ ├── JavaSimulationTask.java │ │ │ ├── NativeExternalSimulationTask.java │ │ │ └── NativeSimulationTask.java │ │ │ └── wpi │ │ │ ├── WPIExtension.java │ │ │ ├── WPIMavenExtension.java │ │ │ ├── WPIMavenRepo.java │ │ │ ├── WPIPlugin.java │ │ │ ├── WPIVersionsExtension.java │ │ │ ├── cpp │ │ │ ├── WPINativeCompileRules.java │ │ │ ├── WPINativeDepsExtension.java │ │ │ └── WPINativeExtension.java │ │ │ ├── dependencies │ │ │ ├── WPIDependenciesPlugin.java │ │ │ └── tools │ │ │ │ ├── CppToolRunTask.java │ │ │ │ ├── ToolInstallTask.java │ │ │ │ ├── ToolRunException.java │ │ │ │ ├── ToolRunTask.java │ │ │ │ ├── WPICppTool.java │ │ │ │ ├── WPITool.java │ │ │ │ └── WPIToolsPlugin.java │ │ │ ├── java │ │ │ ├── ExtractNativeJavaArtifacts.java │ │ │ ├── TestTaskDoFirstAction.java │ │ │ ├── WPIJavaDepsExtension.java │ │ │ └── WPIJavaExtension.java │ │ │ └── simulation │ │ │ ├── HalSimExtension.java │ │ │ ├── SimulationDependencySet.java │ │ │ └── SimulationExtension.java │ └── resources │ │ └── frcKillRobot.sh └── test │ └── groovy │ └── edu │ └── wpi │ └── first │ └── gradlerio │ ├── GradleRioInitializationTest.groovy │ └── wpi │ ├── WPIExtensionExtendableTest.groovy │ └── WPIMavenExtensionExtendableTest.groovy ├── testing ├── README.md ├── asm │ ├── .wpilib │ │ └── wpilib_preferences.json │ ├── build.gradle │ ├── settings.gradle │ └── src │ │ └── main │ │ └── asm │ │ └── Robot.s ├── cpp │ ├── .wpilib │ │ └── wpilib_preferences.json │ ├── README.md │ ├── build.gradle │ ├── settings.gradle │ └── src │ │ ├── main │ │ ├── cpp │ │ │ └── robot.cpp │ │ └── include │ │ │ └── Robot.h │ │ └── test │ │ └── cpp │ │ └── main.cpp ├── java │ ├── .wpilib │ │ └── wpilib_preferences.json │ ├── README.md │ ├── build.gradle │ ├── settings.gradle │ └── src │ │ ├── main │ │ └── java │ │ │ └── frc │ │ │ └── team0000 │ │ │ └── robot │ │ │ ├── Main.java │ │ │ └── Robot.java │ │ └── test │ │ └── java │ │ └── TestCode.java └── jni │ ├── .gitignore │ ├── .wpilib │ └── wpilib_preferences.json │ ├── build.gradle │ ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle │ └── src │ └── main │ ├── deploy │ └── example.txt │ ├── java │ └── frc │ │ └── robot │ │ ├── JNICode.java │ │ ├── Main.java │ │ └── Robot.java │ └── native │ └── cpp │ └── JNICode.cpp ├── versionupdates.gradle └── wsl_install_java.sh /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sh text eol=lf 2 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-22.04 8 | steps: 9 | - uses: actions/checkout@v4 10 | with: 11 | fetch-depth: 0 12 | - uses: actions/setup-java@v4 13 | with: 14 | java-version: 17 15 | distribution: temurin 16 | - name: Publish to Maven Local 17 | run: ./gradlew build publishToMavenLocal --stacktrace -PlocalPublish 18 | - name: Check output 19 | run: git --no-pager diff --exit-code HEAD 20 | - uses: actions/upload-artifact@v4 21 | with: 22 | name: Maven 23 | path: ~/.m2/repository/ 24 | 25 | test_examples: 26 | needs: build 27 | runs-on: ubuntu-22.04 28 | strategy: 29 | matrix: 30 | language: [cpp, java, asm] #, jni] 31 | steps: 32 | - uses: actions/checkout@v4 33 | - uses: actions/setup-java@v4 34 | with: 35 | java-version: 17 36 | distribution: temurin 37 | - uses: actions/download-artifact@v4 38 | with: 39 | name: Maven 40 | path: ~/.m2/repository/ 41 | 42 | - name: Setup RoboRIO Toolchain 43 | if: ${{ (matrix.language == 'cpp') || (matrix.language == 'asm') }} 44 | run: ../../gradlew installRoboRioToolchain 45 | working-directory: testing/${{ matrix.language }} 46 | - name: Test ${{ matrix.language }} Build 47 | run: ../../gradlew build 48 | working-directory: testing/${{ matrix.language }} 49 | 50 | publish: 51 | needs: [build, test_examples] 52 | runs-on: ubuntu-22.04 53 | steps: 54 | - uses: actions/checkout@v4 55 | with: 56 | fetch-depth: 0 57 | - uses: actions/setup-java@v4 58 | with: 59 | java-version: 17 60 | distribution: temurin 61 | - name: Publish 62 | if: ${{ github.repository_owner == 'wpilibsuite' && startsWith(github.ref, 'refs/tags/v') }} 63 | env: 64 | GRADLE_PUBLISH_KEY: ${{ secrets.GRADLE_PUBLISH_KEY }} 65 | GRADLE_PUBLISH_SECRET: ${{ secrets.GRADLE_PUBLISH_SECRET }} 66 | run: | 67 | ./gradlew publishPlugin \ 68 | -Pgradle.publish.key=$GRADLE_PUBLISH_KEY \ 69 | -Pgradle.publish.secret=$GRADLE_PUBLISH_SECRET 70 | -------------------------------------------------------------------------------- /.github/workflows/gradle-wrapper-validation.yml: -------------------------------------------------------------------------------- 1 | name: "Validate Gradle Wrapper" 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | validation: 6 | name: "Validation" 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v4 10 | - uses: gradle/actions/wrapper-validation@v4 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build 3 | .idea 4 | *.ipr 5 | *.iml 6 | *.iws 7 | *.sln 8 | *.vcxproj 9 | *.vcxproj.* 10 | .DS_STORE 11 | quickstart/gradle/libs/ 12 | server_script/maven/ 13 | server_script/tmp/ 14 | CMakeLists.txt 15 | .vs 16 | cmake-build-debug 17 | _localtest/libs 18 | bin/ 19 | .settings/ 20 | .project 21 | .classpath 22 | out/ 23 | hs_err_* 24 | .vscode/ 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Jaci R 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.gradle.plugin-publish" version "1.2.1" 3 | id 'java-gradle-plugin' 4 | id 'idea' 5 | id 'maven-publish' 6 | id 'java' 7 | id 'groovy' 8 | } 9 | 10 | repositories { 11 | maven { 12 | url "https://plugins.gradle.org/m2/" 13 | } 14 | mavenLocal() 15 | } 16 | 17 | configurations { 18 | processstarterDownload 19 | } 20 | 21 | def processstarterToolVersion = "2026.0.0-alpha-1" 22 | 23 | dependencies { 24 | 25 | api 'com.google.code.gson:gson:2.13.1' 26 | 27 | api 'edu.wpi.first:native-utils:2026.0.0' 28 | 29 | api 'de.undercouch:gradle-download-task:5.6.0' 30 | 31 | testImplementation('org.spockframework:spock-core:2.0-M4-groovy-3.0') { 32 | exclude group: 'org.codehaus.groovy' 33 | } 34 | testImplementation("org.junit.jupiter:junit-jupiter-api:5.13.4") 35 | testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") 36 | testImplementation gradleTestKit() 37 | 38 | processstarterDownload "edu.wpi.first.tools:processstarter:$processstarterToolVersion:windowsx86-64@zip" 39 | processstarterDownload "edu.wpi.first.tools:processstarter:$processstarterToolVersion:linuxx86-64@zip" 40 | processstarterDownload "edu.wpi.first.tools:processstarter:$processstarterToolVersion:linuxarm32@zip" 41 | processstarterDownload "edu.wpi.first.tools:processstarter:$processstarterToolVersion:linuxarm64@zip" 42 | processstarterDownload "edu.wpi.first.tools:processstarter:$processstarterToolVersion:osxuniversal@zip" 43 | processstarterDownload "edu.wpi.first.tools:processstarter:$processstarterToolVersion:windowsarm64@zip" 44 | } 45 | 46 | tasks.withType(Test).configureEach { 47 | useJUnitPlatform() 48 | } 49 | 50 | base { 51 | archivesName = "GradleRIO" 52 | } 53 | 54 | java { 55 | sourceCompatibility = 17 56 | targetCompatibility = 17 57 | } 58 | 59 | allprojects { 60 | group = "edu.wpi.first" 61 | 62 | if (project.hasProperty('publishVersion')) { 63 | version = project.publishVersion 64 | } 65 | 66 | tasks.withType(Javadoc) { 67 | options.addBooleanOption('Xdoclint:all,-missing', true) 68 | } 69 | 70 | tasks.withType(JavaCompile) { 71 | options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" 72 | } 73 | } 74 | 75 | gradlePlugin { 76 | website = 'https://github.com/wpilibsuite/GradleRIO' 77 | vcsUrl = 'https://github.com/wpilibsuite/GradleRIO' 78 | plugins { 79 | gradleRio { 80 | id = 'edu.wpi.first.GradleRIO' 81 | displayName = 'GradleRIO' 82 | implementationClass = 'edu.wpi.first.gradlerio.GradleRIOPlugin' 83 | description = 'Managing FRC projects, the Gradle way (2019+)' 84 | tags = ['frc', 'wpilib', 'gradlerio'] 85 | } 86 | } 87 | } 88 | 89 | apply from: 'versionupdates.gradle' 90 | 91 | def examplesFolder = file("$rootDir/testing") 92 | 93 | tasks.register('PatchExamples') { 94 | doLast { 95 | String regex = "(id\\s*?[\\\"|\\']edu\\.wpi\\.first\\.GradleRIO[\\\"|\\'].*?version\\s*?[\\\"|\\'])(.+?)([\\\"|\\'])"; 96 | 97 | examplesFolder.eachFile { File file -> 98 | if (file.isDirectory() && file.name != '_archived') { 99 | def buildGradleFile = new File(file, 'build.gradle') 100 | if (buildGradleFile.exists() && buildGradleFile.isFile()) { 101 | def text = buildGradleFile.text 102 | text = text.replaceAll(regex, "id \"edu.wpi.first.GradleRIO\" version \"${version}\"") 103 | buildGradleFile.text = text 104 | } 105 | } 106 | } 107 | } 108 | } 109 | 110 | task zipExamples(dependsOn: PatchExamples) 111 | 112 | examplesFolder.eachFile { File file -> 113 | if (file.isDirectory() && file.name != '_archived') { 114 | task "zipExample${file.name}"(type: Zip) { 115 | from(file) { 116 | archiveFileName = "${file.name}.zip" 117 | exclude 'build/' 118 | exclude '.gradle/' 119 | } 120 | 121 | zipExamples.dependsOn it 122 | it.dependsOn PatchExamples 123 | } 124 | } 125 | } 126 | 127 | jar.finalizedBy zipExamples 128 | 129 | task processprocessstarterDownload { 130 | inputs.files configurations.processstarterDownload 131 | doLast { 132 | configurations.processstarterDownload.each { downloadedFile -> 133 | def destinationDir = file("${buildDir}/resources/main") 134 | // Unzip and copy files 135 | copy { 136 | from(zipTree(downloadedFile)) { 137 | include 'processstarter*' 138 | } 139 | into destinationDir 140 | } 141 | 142 | // Rename the extracted file to include the classifier 143 | destinationDir.eachFile { extractedFile -> 144 | 145 | if (extractedFile.name.startsWith('processstarter') && !extractedFile.name.contains('-')) { 146 | def destinationFile = file("${buildDir}/resources/main/processstarter") 147 | if (downloadedFile.name.contains("linuxarm32")) { 148 | destinationFile = file("${buildDir}/resources/main/processstarter-linuxarm32") 149 | } else if (downloadedFile.name.contains("linuxarm64")) { 150 | destinationFile = file("${buildDir}/resources/main/processstarter-linuxarm64") 151 | } else if (downloadedFile.name.contains("linuxx86-64")) { 152 | destinationFile = file("${buildDir}/resources/main/processstarter-linuxx86-64") 153 | } else if (downloadedFile.name.contains("osxuniversal")) { 154 | destinationFile = file("${buildDir}/resources/main/processstarter-osxuniversal") 155 | } else if (downloadedFile.name.contains("windowsx86-64")) { 156 | destinationFile = file("${buildDir}/resources/main/processstarter-windowsx86-64.exe") 157 | } else if (downloadedFile.name.contains("windowsarm64")) { 158 | destinationFile = file("${buildDir}/resources/main/processstarter-windowsarm64.exe") 159 | } 160 | extractedFile.renameTo(destinationFile) 161 | } 162 | } 163 | } 164 | } 165 | } 166 | 167 | jar.dependsOn processprocessstarterDownload 168 | 169 | wrapper { 170 | gradleVersion = '8.11' 171 | distributionType = Wrapper.DistributionType.BIN 172 | } 173 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | version = 2026.0.0-alpha-1 2 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wpilibsuite/GradleRIO/4f6ab6d1b1e5b968f1d575b23c702bf34850e676/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /img/tty.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wpilibsuite/GradleRIO/4f6ab6d1b1e5b968f1d575b23c702bf34850e676/img/tty.gif -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wpilibsuite/GradleRIO/4f6ab6d1b1e5b968f1d575b23c702bf34850e676/settings.gradle -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/OneDriveException.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio; 2 | 3 | import org.gradle.api.Project; 4 | 5 | public class OneDriveException extends java.lang.RuntimeException { 6 | public OneDriveException(Project project) { 7 | super(String.format("Error cannot create project inside OneDrive. Project Directory = %S", project.getRootDir().toString())); 8 | System.out.println(String.format("Error cannot create project inside OneDrive. Project Directory = %S", project.getRootDir().toString())); 9 | System.out.println("To fix this error move your project out of the OneDrive folder."); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/PreemptiveDownloadTask.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio; 2 | 3 | import org.gradle.api.DefaultTask; 4 | import org.gradle.api.file.ConfigurableFileCollection; 5 | import org.gradle.api.tasks.InputFiles; 6 | import org.gradle.api.tasks.TaskAction; 7 | 8 | 9 | public abstract class PreemptiveDownloadTask extends DefaultTask { 10 | 11 | @InputFiles 12 | public abstract ConfigurableFileCollection getFiles(); 13 | 14 | @TaskAction 15 | public void execute() { 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/SingletonTask.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio; 2 | 3 | import org.gradle.api.provider.Provider; 4 | 5 | public interface SingletonTask { 6 | public Provider getSingletonName(); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/caching/WrapperInspector.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.caching; 2 | 3 | import edu.wpi.first.deployutils.log.ETLogger; 4 | 5 | import java.io.File; 6 | import java.io.FileReader; 7 | import java.io.IOException; 8 | import java.util.Properties; 9 | 10 | import org.gradle.api.Project; 11 | import org.gradle.api.tasks.wrapper.Wrapper; 12 | import org.gradle.wrapper.WrapperExecutor; 13 | 14 | public class WrapperInspector { 15 | 16 | public static final String NAME = "wrapper"; 17 | 18 | public static void run(Project project, ETLogger log) { 19 | File settingsFile = project.file("gradle/wrapper/gradle-wrapper.properties"); 20 | boolean wrapperTaskQueued = project.getGradle().getTaskGraph().getAllTasks().stream().filter(x -> x instanceof Wrapper).findAny().isPresent(); 21 | if (settingsFile.exists() && settingsFile.isFile() && !wrapperTaskQueued) { 22 | log.info("Found gradle-wrapper.properties on " + project.getPath()); 23 | Properties props = new Properties(); 24 | try (FileReader reader = new FileReader(settingsFile)) { 25 | props.load(reader); 26 | if (requiresUpdate(props)) { 27 | log.logErrorHead("Warning! Your wrapper zip / dist store is set to wrapper/dists!"); 28 | log.logError("This can cause issues when going to competition, since this directory is automatically purged once a month"); 29 | log.logError("Run ./gradlew wrapper to fix this, or use -Pskip-inspector-wrapper to squash this warning."); 30 | } 31 | reader.close(); 32 | } catch (IOException e) { 33 | log.info("Wrapper Inspector failed: " + e.getMessage()); 34 | } 35 | } 36 | } 37 | 38 | public static boolean requiresUpdate(Properties props) { 39 | String zipStorePath = props.getProperty(WrapperExecutor.ZIP_STORE_PATH_PROPERTY); 40 | String distributionPath = props.getProperty(WrapperExecutor.DISTRIBUTION_PATH_PROPERTY); 41 | boolean isZipStoreValid = zipStorePath != null && zipStorePath.equals("wrapper/dists"); 42 | boolean isDistributionPathValid = distributionPath != null && distributionPath.equals("wrapper/dists"); 43 | return isZipStoreValid || isDistributionPathValid; 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/deploy/DebugFileTask.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.deploy; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | import javax.inject.Inject; 9 | 10 | import com.google.gson.GsonBuilder; 11 | 12 | import org.codehaus.groovy.runtime.ResourceGroovyMethods; 13 | import org.gradle.api.DefaultTask; 14 | import org.gradle.api.file.RegularFileProperty; 15 | import org.gradle.api.model.ObjectFactory; 16 | import org.gradle.api.tasks.Internal; 17 | import org.gradle.api.tasks.OutputFile; 18 | import org.gradle.api.tasks.TaskAction; 19 | 20 | public abstract class DebugFileTask extends DefaultTask { 21 | private final List targets = new ArrayList<>(); 22 | 23 | @Internal 24 | public List getTargets() { 25 | return targets; 26 | } 27 | 28 | @OutputFile 29 | public abstract RegularFileProperty getDebugFile(); 30 | 31 | @Inject 32 | public DebugFileTask(ObjectFactory objects) { 33 | getOutputs().upToDateWhen(spec -> false); 34 | } 35 | 36 | @TaskAction 37 | public void execute() throws IOException { 38 | List debugInfo = new ArrayList<>(); 39 | for (WPIRemoteTarget wpiRemoteTarget : targets) { 40 | File debugFile = wpiRemoteTarget.debugFileTask.get().getDebugFile().get().getAsFile(); 41 | debugInfo.add(new DebugInfo(wpiRemoteTarget.getName(), debugFile.getAbsolutePath())); 42 | } 43 | GsonBuilder builder = new GsonBuilder(); 44 | builder.setPrettyPrinting(); 45 | File outputFile = getDebugFile().get().getAsFile(); 46 | outputFile.getParentFile().mkdirs(); 47 | ResourceGroovyMethods.setText(outputFile, builder.create().toJson(debugInfo)); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/deploy/DebugInfo.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.deploy; 2 | 3 | public class DebugInfo { 4 | public final String name; 5 | public final String path; 6 | 7 | public DebugInfo(String name, String path) { 8 | this.name = name; 9 | this.path = path; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/deploy/DebuggableArtifact.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.deploy; 2 | 3 | import edu.wpi.first.deployutils.deploy.artifact.Artifact; 4 | 5 | public interface DebuggableArtifact extends Artifact { 6 | TargetDebugInfo getTargetDebugInfo(); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/deploy/DebuggableJavaArtifact.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.deploy; 2 | 3 | import javax.inject.Inject; 4 | 5 | import edu.wpi.first.deployutils.deploy.artifact.JavaArtifact; 6 | import edu.wpi.first.deployutils.deploy.context.DeployContext; 7 | import edu.wpi.first.deployutils.deploy.sessions.IPSessionController; 8 | import edu.wpi.first.deployutils.deploy.target.RemoteTarget; 9 | 10 | public class DebuggableJavaArtifact extends JavaArtifact implements DebuggableArtifact { 11 | 12 | private int debugPort = 8349; 13 | 14 | public int getDebugPort() { 15 | return debugPort; 16 | } 17 | 18 | @Inject 19 | public DebuggableJavaArtifact(String name, RemoteTarget target) { 20 | super(name, target); 21 | } 22 | 23 | @Override 24 | public TargetDebugInfo getTargetDebugInfo() { 25 | DeployContext ctx = getTarget().getTargetDiscoveryTask().get().getActiveContext(); 26 | 27 | if (ctx.getController() instanceof IPSessionController) { 28 | IPSessionController session = (IPSessionController)ctx.getController(); 29 | return new JavaTargetDebugInfo(getName(), debugPort, session.getHost(), getTarget().getProject().getName()); 30 | } 31 | return null; 32 | } 33 | 34 | 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/deploy/DebuggableNativeArtifact.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.deploy; 2 | 3 | import java.io.File; 4 | import java.util.ArrayList; 5 | import java.util.Arrays; 6 | import java.util.List; 7 | 8 | import javax.inject.Inject; 9 | 10 | import org.gradle.language.nativeplatform.HeaderExportingSourceSet; 11 | import org.gradle.nativeplatform.NativeDependencySet; 12 | import org.gradle.nativeplatform.NativeExecutableBinarySpec; 13 | 14 | import edu.wpi.first.deployutils.deploy.artifact.NativeExecutableArtifact; 15 | import edu.wpi.first.deployutils.deploy.context.DeployContext; 16 | import edu.wpi.first.deployutils.deploy.sessions.IPSessionController; 17 | import edu.wpi.first.toolchain.ToolchainDiscoverer; 18 | import edu.wpi.first.toolchain.ToolchainExtension; 19 | import edu.wpi.first.toolchain.roborio.RoboRioToolchainPlugin; 20 | import edu.wpi.first.vscode.dependencies.SourceContainingNativeDependencySet; 21 | 22 | public class DebuggableNativeArtifact extends NativeExecutableArtifact implements DebuggableArtifact { 23 | 24 | private int debugPort = 8349; 25 | 26 | public int getDebugPort() { 27 | return debugPort; 28 | } 29 | 30 | @Inject 31 | public DebuggableNativeArtifact(String name, WPIRemoteTarget target) { 32 | super(name, target); 33 | } 34 | 35 | @Override 36 | public TargetDebugInfo getTargetDebugInfo() { 37 | DeployContext ctx = getTarget().getTargetDiscoveryTask().get().getActiveContext(); 38 | 39 | if (ctx.getController() instanceof IPSessionController) { 40 | IPSessionController session = (IPSessionController) ctx.getController(); 41 | 42 | // TODO get all this from the VS Code plugin without needing to enumerate EVERYTHING 43 | 44 | ToolchainDiscoverer toolchain = getTarget().getProject().getExtensions().getByType(ToolchainExtension.class) 45 | .getToolchainDescriptors().getByName(RoboRioToolchainPlugin.toolchainName).discover(); 46 | String gdbpath = toolchain.gdbFile().get().getAbsolutePath(); 47 | String sysroot = toolchain.sysroot().get().getAbsolutePath(); 48 | 49 | // TODO deduplicate this with NativeExternalSimulationTask 50 | List srcpaths = new ArrayList<>(); 51 | List headerpaths = new ArrayList<>(); 52 | List libsrcpaths = new ArrayList<>(); 53 | 54 | NativeExecutableBinarySpec bin = getBinary().get(); 55 | for (HeaderExportingSourceSet sourceSet : bin.getInputs().withType(HeaderExportingSourceSet.class)) { 56 | srcpaths.addAll(sourceSet.getSource().getSrcDirs()); 57 | srcpaths.addAll(sourceSet.getExportedHeaders().getSrcDirs()); 58 | } 59 | 60 | // Get all folders in install dir 61 | List libpaths = new ArrayList<>(Arrays.asList(getInstallTaskProvider().get().getInstallDirectory().get().getAsFile().listFiles(f -> f.isDirectory()))); 62 | libpaths.add(getInstallTaskProvider().get().getInstallDirectory().get().getAsFile()); 63 | 64 | for (NativeDependencySet ds : bin.getLibs()) { 65 | headerpaths.addAll(ds.getIncludeRoots().getFiles()); 66 | 67 | if (ds instanceof SourceContainingNativeDependencySet) { 68 | SourceContainingNativeDependencySet scnDs = (SourceContainingNativeDependencySet)ds; 69 | libsrcpaths.addAll(scnDs.getSourceRoots().getFiles()); 70 | } 71 | } 72 | 73 | return new NativeTargetDebugInfo(getName(), debugPort, session.getHost(), 74 | getDeployedFile().getAbsolutePath(), gdbpath, sysroot, srcpaths, headerpaths, libpaths, libsrcpaths); 75 | } 76 | return null; 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/deploy/DeployStage.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.deploy; 2 | 3 | public enum DeployStage { 4 | BeforeProgramKill, 5 | ProgramKill, 6 | AfterProgramKill, 7 | FileRetreival, 8 | FileDeploy, 9 | BeforeProgramStart, 10 | ProgramStart, 11 | AfterProgramStart 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/deploy/FRCDeployPlugin.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.deploy; 2 | 3 | import org.gradle.api.ExtensiblePolymorphicDomainObjectContainer; 4 | import org.gradle.api.Plugin; 5 | import org.gradle.api.Project; 6 | import org.gradle.api.model.ObjectFactory; 7 | 8 | import edu.wpi.first.deployutils.DeployUtils; 9 | import edu.wpi.first.deployutils.deploy.DeployExtension; 10 | import edu.wpi.first.deployutils.deploy.artifact.Artifact; 11 | import edu.wpi.first.deployutils.deploy.context.DeployContext; 12 | import edu.wpi.first.deployutils.deploy.target.location.DeployLocation; 13 | import edu.wpi.first.gradlerio.deploy.roborio.DSDeployLocation; 14 | import edu.wpi.first.gradlerio.deploy.roborio.FRCJNILibraryArtifact; 15 | import edu.wpi.first.gradlerio.deploy.roborio.FRCJREArtifact; 16 | import edu.wpi.first.gradlerio.deploy.roborio.FRCJavaArtifact; 17 | import edu.wpi.first.gradlerio.deploy.roborio.FRCNativeArtifact; 18 | import edu.wpi.first.gradlerio.deploy.roborio.FRCProgramStartArtifact; 19 | import edu.wpi.first.gradlerio.deploy.roborio.RoboRIO; 20 | import edu.wpi.first.gradlerio.deploy.roborio.RobotCommandArtifact; 21 | import edu.wpi.first.deployutils.deploy.NamedObjectFactory; 22 | 23 | public class FRCDeployPlugin implements Plugin { 24 | 25 | private Project project; 26 | 27 | public static final String LIB_DEPLOY_DIR = "/usr/local/frc/third-party/lib"; 28 | 29 | private void configureRoboRIOTypes(RoboRIO target) { 30 | ObjectFactory objects = target.getProject().getObjects(); 31 | ExtensiblePolymorphicDomainObjectContainer locations = target.getLocations(); 32 | ExtensiblePolymorphicDomainObjectContainer artifacts = target.getArtifacts(); 33 | 34 | NamedObjectFactory.registerType(FRCJavaArtifact.class, artifacts, target, objects); 35 | NamedObjectFactory.registerType(FRCNativeArtifact.class, artifacts, target, objects); 36 | NamedObjectFactory.registerType(FRCJREArtifact.class, artifacts, target, objects); 37 | NamedObjectFactory.registerType(FRCJNILibraryArtifact.class, artifacts, target, objects); 38 | NamedObjectFactory.registerType(FRCProgramStartArtifact.class, artifacts, target, objects); 39 | NamedObjectFactory.registerType(RobotCommandArtifact.class, artifacts, target, objects); 40 | 41 | NamedObjectFactory.registerType(DSDeployLocation.class, locations, target, objects); 42 | } 43 | 44 | @Override 45 | public void apply(Project project) { 46 | this.project = project; 47 | 48 | project.getPluginManager().apply(DeployUtils.class); 49 | // TODO 50 | //project.getPluginManager().apply(RioLogPlugin.class); 51 | 52 | DeployExtension deployExtension = project.getExtensions().getByType(DeployExtension.class); 53 | 54 | FRCExtension frcExtension = project.getExtensions().create("frc", FRCExtension.class, project, deployExtension); 55 | 56 | // Register the RoboRIO target 57 | deployExtension.getTargets().registerFactory(RoboRIO.class, name -> { 58 | RoboRIO target = project.getObjects().newInstance(RoboRIO.class, name, project, deployExtension, frcExtension); 59 | configureRoboRIOTypes(target); 60 | return target; 61 | }); 62 | } 63 | 64 | public Project getProject() { 65 | return project; 66 | } 67 | 68 | public static void ownDirectory(DeployContext ctx, String directory) { 69 | ctx.execute("chmod -R 777 \"" + directory + "\" || true; chown -R lvuser:ni \"" + directory + "\""); 70 | } 71 | 72 | public static DeployExtension deployExtension(Project project) { 73 | return project.getExtensions().getByType(DeployExtension.class); 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/deploy/FRCExtension.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.deploy; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | 6 | import javax.inject.Inject; 7 | 8 | import com.google.gson.Gson; 9 | import com.google.gson.JsonSyntaxException; 10 | 11 | import org.codehaus.groovy.runtime.ResourceGroovyMethods; 12 | import org.gradle.api.Project; 13 | import org.gradle.api.tasks.TaskProvider; 14 | 15 | import edu.wpi.first.deployutils.deploy.DeployExtension; 16 | 17 | public class FRCExtension { 18 | private final Project project; 19 | 20 | @Inject 21 | public FRCExtension(Project project, DeployExtension deployExtension) { 22 | this.project = project; 23 | 24 | debugFileTask = project.getTasks().register("writeDebugInfo", DebugFileTask.class, t -> { 25 | t.getDebugFile().set(project.getLayout().getBuildDirectory().file("debug/debug_info.json")); 26 | }); 27 | 28 | deployExtension.getDeployTask().configure(t -> { 29 | t.dependsOn(debugFileTask); 30 | }); 31 | } 32 | 33 | private final TaskProvider debugFileTask; 34 | 35 | public TaskProvider getDebugFileTask() { 36 | return debugFileTask; 37 | } 38 | 39 | public Project getProject() { 40 | return project; 41 | } 42 | 43 | public int getTeamOrDefault(int teamDefault) { 44 | if (project.hasProperty("teamNumber")) { 45 | return Integer.parseInt((String) project.findProperty("teamNumber")); 46 | } 47 | 48 | int number = getTeamNumberFromJSON(); 49 | if (number < 0) { 50 | return teamDefault; 51 | } 52 | return number; 53 | } 54 | 55 | public int getTeamNumber() { 56 | if (project.hasProperty("teamNumber")) { 57 | return Integer.parseInt((String) project.findProperty("teamNumber")); 58 | } 59 | 60 | int number = getTeamNumberFromJSON(); 61 | if (number < 0) { 62 | throw new TeamNumberNotFoundException(); 63 | } 64 | return number; 65 | } 66 | 67 | public boolean getDebugOrDefault(boolean debugDefault) { 68 | if (project.hasProperty("debugMode")) { 69 | return true; 70 | } 71 | return debugDefault; 72 | } 73 | 74 | private static class PreferencesJson { 75 | public int teamNumber; 76 | } 77 | 78 | private int getTeamNumberFromJSON() { 79 | File jsonFile = project.getRootProject().file(".wpilib/wpilib_preferences.json"); 80 | if (jsonFile.exists()) { 81 | Gson gson = new Gson(); 82 | try { 83 | PreferencesJson json = gson.fromJson(ResourceGroovyMethods.getText(jsonFile), PreferencesJson.class); 84 | return json.teamNumber; 85 | } catch (JsonSyntaxException | IOException e) { 86 | } 87 | } 88 | return -1; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/deploy/JavaTargetDebugInfo.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.deploy; 2 | 3 | public class JavaTargetDebugInfo extends TargetDebugInfo { 4 | public final String type = "java"; 5 | public String name; 6 | public int port; 7 | public String target; 8 | public String project; 9 | 10 | public JavaTargetDebugInfo(String name, int port, String target, String project) { 11 | this.name = name; 12 | this.port = port; 13 | this.target = target; 14 | this.project = project; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/deploy/NativeTargetDebugInfo.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.deploy; 2 | 3 | import java.io.File; 4 | import java.util.List; 5 | 6 | public class NativeTargetDebugInfo extends TargetDebugInfo { 7 | public final String type = "native"; 8 | public String name; 9 | public int port; 10 | public String target; 11 | public String launchfile; 12 | public String gdb; 13 | public String sysroot; 14 | public Object[] srcpaths; 15 | public Object[] headerpaths; 16 | public Object[] libpaths; 17 | public Object[] libsrcpaths; 18 | 19 | public NativeTargetDebugInfo(String name, int port, String target, String launchfile, String gdb, String sysroot, 20 | List srcpaths, List headerpaths, List libpaths, List libsrcpaths) { 21 | this.name = name; 22 | this.port = port; 23 | this.target = target; 24 | this.launchfile = launchfile; 25 | this.gdb = gdb; 26 | this.sysroot = sysroot; 27 | this.srcpaths = srcpaths.stream().map(x -> x.getAbsolutePath()).toArray(); 28 | this.headerpaths = headerpaths.stream().map(x -> x.getAbsolutePath()).toArray(); 29 | this.libpaths = libpaths.stream().map(x -> x.getAbsolutePath()).toArray(); 30 | this.libsrcpaths = libsrcpaths.stream().map(x -> x.getAbsolutePath()).toArray(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/deploy/StageDeployTask.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.deploy; 2 | 3 | import org.gradle.api.DefaultTask; 4 | import org.gradle.api.tasks.Internal; 5 | import org.gradle.api.tasks.TaskProvider; 6 | 7 | public abstract class StageDeployTask extends DefaultTask { 8 | private DeployStage stage; 9 | private TaskProvider previousStage; 10 | 11 | @Internal 12 | public DeployStage getStage() { 13 | return stage; 14 | } 15 | public void setStage(DeployStage stage) { 16 | this.stage = stage; 17 | } 18 | 19 | @Internal 20 | public TaskProvider getPreviousStage() { 21 | return previousStage; 22 | } 23 | 24 | public void setPreviousStage(TaskProvider previousStage) { 25 | this.previousStage = previousStage; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/deploy/StagedDeployTarget.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.deploy; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | import java.util.concurrent.Callable; 8 | 9 | import javax.inject.Inject; 10 | 11 | import org.gradle.api.Project; 12 | import org.gradle.api.plugins.ExtensionAware; 13 | import org.gradle.api.tasks.TaskProvider; 14 | 15 | import edu.wpi.first.deployutils.deploy.DeployExtension; 16 | import edu.wpi.first.deployutils.deploy.artifact.Artifact; 17 | import edu.wpi.first.deployutils.deploy.target.RemoteTarget; 18 | 19 | public abstract class StagedDeployTarget extends RemoteTarget { 20 | private final Map> previousStageMap = new HashMap<>(); 21 | 22 | @Inject 23 | public StagedDeployTarget(String name, Project project, DeployExtension de) { 24 | super(name, project, de); 25 | TaskProvider previousStage = null; 26 | for (DeployStage stage : DeployStage.values()) { 27 | String taskName = "deployStage" + name + stage.toString(); 28 | TaskProvider fixedPreviousStage = previousStage; 29 | TaskProvider stageTask = project.getTasks().register(taskName, StageDeployTask.class, task -> { 30 | task.setGroup("GradleRIO"); 31 | task.setDescription("Deploy stage " + stage + " for " + name); 32 | 33 | task.dependsOn(getTargetDiscoveryTask()); 34 | 35 | task.setStage(stage); 36 | task.setPreviousStage(fixedPreviousStage); 37 | 38 | Callable> cbl = () -> computeStageDependencies(task); 39 | task.dependsOn(cbl); 40 | 41 | if (fixedPreviousStage != null) { 42 | task.dependsOn(fixedPreviousStage); 43 | } 44 | }); 45 | previousStageMap.put(stage, previousStage); 46 | previousStage = stageTask; 47 | } 48 | TaskProvider lastStage = previousStage; 49 | getDeployTask().configure(x -> x.dependsOn(lastStage)); 50 | 51 | getArtifacts().all(this::configureArtifact); 52 | } 53 | 54 | private void configureArtifact(Artifact artifact) { 55 | Callable> cbl = () -> computeArtifactStageDep(artifact); 56 | artifact.dependsOnForDeployTask(cbl); 57 | } 58 | 59 | private final DeployStage defaultStage = DeployStage.FileDeploy; 60 | 61 | private TaskProvider computeArtifactStageDep(Artifact artifact) { 62 | // Artifact must depend on stage before 63 | DeployStage stage = defaultStage; 64 | if (artifact instanceof ExtensionAware) { 65 | ExtensionAware ext = (ExtensionAware)artifact; 66 | DeployStage innerStage = ext.getExtensions().findByType(DeployStage.class); 67 | if (innerStage != null) { 68 | stage = innerStage; 69 | } 70 | } 71 | TaskProvider prevStageTask = previousStageMap.get(stage); 72 | return prevStageTask; 73 | } 74 | 75 | private List computeStageDependencies(StageDeployTask stageTask) { 76 | List depTasks = new ArrayList<>(); 77 | for (Artifact artifact : getArtifacts()) { 78 | DeployStage stage = defaultStage; 79 | if (artifact instanceof ExtensionAware) { 80 | ExtensionAware ext = (ExtensionAware)artifact; 81 | DeployStage innerStage = ext.getExtensions().findByType(DeployStage.class); 82 | if (innerStage != null) { 83 | stage = innerStage; 84 | } 85 | } 86 | if (stage == stageTask.getStage()) { 87 | depTasks.add(artifact.getDeployTask()); 88 | } 89 | } 90 | return depTasks; 91 | } 92 | 93 | public void setDeployStage(Artifact artifact, DeployStage stage) { 94 | artifact.getExtensionContainer().add(DeployStage.class, "stage", stage); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/deploy/TargetDebugFileTask.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.deploy; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | import java.util.concurrent.Callable; 8 | 9 | import javax.inject.Inject; 10 | 11 | import com.google.gson.GsonBuilder; 12 | 13 | import org.codehaus.groovy.runtime.ResourceGroovyMethods; 14 | import org.gradle.api.DefaultTask; 15 | import org.gradle.api.file.RegularFileProperty; 16 | import org.gradle.api.model.ObjectFactory; 17 | import org.gradle.api.tasks.Internal; 18 | import org.gradle.api.tasks.OutputFile; 19 | import org.gradle.api.tasks.TaskAction; 20 | 21 | import edu.wpi.first.deployutils.deploy.target.RemoteTarget; 22 | 23 | public class TargetDebugFileTask extends DefaultTask { 24 | 25 | private RemoteTarget target; 26 | 27 | @Internal 28 | public RemoteTarget getTarget() { 29 | return target; 30 | } 31 | 32 | public void setTarget(RemoteTarget target) { 33 | this.target = target; 34 | } 35 | 36 | private final RegularFileProperty debugFile; 37 | 38 | @OutputFile 39 | public RegularFileProperty getDebugFile() { 40 | return debugFile; 41 | } 42 | 43 | @Inject 44 | public TargetDebugFileTask(ObjectFactory objects) { 45 | getOutputs().upToDateWhen((spec) -> false); 46 | Callable cbl = () -> target.getArtifacts().withType(DebuggableArtifact.class).stream().map(x -> x.getDeployTask()).toArray(); 47 | dependsOn(cbl); 48 | debugFile = objects.fileProperty(); 49 | } 50 | 51 | @TaskAction 52 | public void execute() throws IOException { 53 | List debugList = new ArrayList<>(); 54 | for (DebuggableArtifact artifact : target.getArtifacts().withType(DebuggableArtifact.class)) { 55 | debugList.add(artifact.getTargetDebugInfo()); 56 | } 57 | GsonBuilder builder = new GsonBuilder(); 58 | builder.setPrettyPrinting(); 59 | File outputFile = debugFile.get().getAsFile(); 60 | outputFile.getParentFile().mkdirs(); 61 | ResourceGroovyMethods.setText(outputFile, builder.create().toJson(debugList)); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/deploy/TargetDebugInfo.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.deploy; 2 | 3 | public abstract class TargetDebugInfo { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/deploy/TeamNumberNotFoundException.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.deploy; 2 | 3 | public class TeamNumberNotFoundException extends RuntimeException { 4 | private static final long serialVersionUID = -7735297736598850111L; 5 | 6 | public TeamNumberNotFoundException() { 7 | super("Could not find team number. Make sure either one is passed in, or the team number is set in the wpilib_preferences.json file. You can also use getTeamOrDefault(number) to pass in a default team number instead of getTeamValue"); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/deploy/WPIRemoteTarget.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.deploy; 2 | 3 | import javax.inject.Inject; 4 | 5 | import org.gradle.api.Project; 6 | import org.gradle.api.provider.Property; 7 | import org.gradle.api.provider.Provider; 8 | import org.gradle.api.tasks.TaskProvider; 9 | 10 | import edu.wpi.first.deployutils.deploy.DeployExtension; 11 | 12 | public abstract class WPIRemoteTarget extends StagedDeployTarget { 13 | 14 | private final Property debug; 15 | 16 | /** 17 | * Gets or Sets if debugging should be enabled for this target. 18 | * 19 | * Implies debug binaries in C++. For Java, use `wpi.java.debugJni = true` 20 | * to deploy debug JNI artifacts. 21 | * 22 | * @return Property for debug mode. 23 | */ 24 | public Property getDebug() { 25 | return debug; 26 | } 27 | 28 | private final Provider buildType; 29 | 30 | /** 31 | * Gets a mapping of getDebug() to the correct build type for C++. 32 | * 33 | * @return Build Type mapping for getDebug() 34 | */ 35 | public Provider getBuildType() { 36 | return buildType; 37 | } 38 | 39 | public final TaskProvider debugFileTask; 40 | 41 | public TaskProvider getDebugFileTask() { 42 | return debugFileTask; 43 | } 44 | 45 | @Inject 46 | public WPIRemoteTarget(String name, Project project, DeployExtension de, FRCExtension frcExtension) { 47 | super(name, project, de); 48 | 49 | debug = project.getObjects().property(Boolean.class); 50 | debug.set(false); 51 | 52 | buildType = debug.map(x -> x ? "debug" : "release"); 53 | 54 | debugFileTask = project.getTasks().register("writeTargetDebugInfo" + name, TargetDebugFileTask.class, t -> { 55 | t.getDebugFile().set(project.getLayout().getBuildDirectory().file("debug/" + name + ".json")); 56 | t.setTarget(this); 57 | t.dependsOn(getTargetDiscoveryTask()); 58 | }); 59 | frcExtension.getDebugFileTask().configure(t -> { 60 | t.getTargets().add(this); 61 | t.dependsOn(debugFileTask); 62 | }); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/deploy/roborio/ByteArrayOutputStreamAccessor.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.deploy.roborio; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | 5 | public class ByteArrayOutputStreamAccessor extends ByteArrayOutputStream { 6 | public byte[] getBackingArray() { 7 | return this.buf; 8 | } 9 | 10 | public int getBackingLength() { 11 | return this.count; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/deploy/roborio/DSDeployLocation.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.deploy.roborio; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStreamReader; 6 | import java.net.InetAddress; 7 | import java.net.InetSocketAddress; 8 | import java.net.Socket; 9 | import java.util.Optional; 10 | 11 | import javax.inject.Inject; 12 | 13 | import com.google.gson.Gson; 14 | import com.google.gson.annotations.SerializedName; 15 | 16 | import edu.wpi.first.deployutils.deploy.target.RemoteTarget; 17 | import edu.wpi.first.deployutils.deploy.target.discovery.action.DiscoveryAction; 18 | import edu.wpi.first.deployutils.deploy.target.discovery.action.SshDiscoveryAction; 19 | import edu.wpi.first.deployutils.deploy.target.location.SshDeployLocation; 20 | import edu.wpi.first.deployutils.log.ETLogger; 21 | import edu.wpi.first.deployutils.log.ETLoggerFactory; 22 | 23 | public class DSDeployLocation extends SshDeployLocation { 24 | 25 | @Inject 26 | public DSDeployLocation(String name, RemoteTarget target) { 27 | super(name, target); 28 | } 29 | 30 | private String dsAddress = "localhost"; 31 | private Optional cachedAddress = Optional.empty(); 32 | private ETLogger log = ETLoggerFactory.INSTANCE.create("DSDeployLocation"); 33 | private int timeout = 1000; 34 | private int port = 1742; 35 | 36 | public int getPort() { 37 | return port; 38 | } 39 | 40 | public void setPort(int port) { 41 | this.port = port; 42 | } 43 | 44 | public int getTimeout() { 45 | return timeout; 46 | } 47 | 48 | public void setTimeout(int timeout) { 49 | this.timeout = timeout; 50 | } 51 | 52 | @Override 53 | public String getAddress() { 54 | return cachedAddress.orElseGet(this::determineAddress); 55 | } 56 | 57 | @Override 58 | public void setAddress(String address) { 59 | this.dsAddress = address; 60 | } 61 | 62 | @Override 63 | public DiscoveryAction createAction() { 64 | return new SshDiscoveryAction(this); 65 | } 66 | 67 | private static class DsJsonData { 68 | @SerializedName("FMS Connected") public Boolean fmsConnected; 69 | public Long robotIP; 70 | } 71 | 72 | private String determineAddress() { 73 | try (Socket socket = new Socket()) { 74 | InetAddress addr = InetAddress.getByName(dsAddress); 75 | socket.connect(new InetSocketAddress(addr, port), timeout); 76 | 77 | String jsonText = new BufferedReader(new InputStreamReader(socket.getInputStream())).readLine(); 78 | Gson gson = new Gson(); 79 | DsJsonData data = gson.fromJson(jsonText, DsJsonData.class); 80 | 81 | if (data.fmsConnected) { 82 | String msg = "You can't deploy code while connected to the FMS! Ask the FTA to allow you to tether your robot."; 83 | log.logErrorHead(msg); 84 | throw new FMSConnectedException(msg); 85 | } 86 | 87 | if (data.robotIP != null) { 88 | long ipLong = data.robotIP; 89 | if (ipLong == 0) { 90 | log.debug("Driver Station isn't connected to robot."); 91 | return "ds_comms_failed.ds_not_connected_to_robot"; 92 | } 93 | 94 | InetAddress ip = InetAddress.getByAddress(new byte[] { 95 | (byte) ((ipLong >> 24) & 0xff), 96 | (byte) ((ipLong >> 16) & 0xff), 97 | (byte) ((ipLong >> 8) & 0xff), 98 | (byte) (ipLong & 0xff) 99 | }); 100 | 101 | log.log("Driver Station reported IP: " + ip.getHostAddress()); 102 | cachedAddress = Optional.of(ip.getHostAddress()); 103 | return ip.getHostAddress(); 104 | } else { 105 | log.debug("Driver Station didn't provide robotIP in JSON response"); 106 | } 107 | return "ds_comms_failed.no_address"; 108 | } catch (IOException e) { 109 | throw new RuntimeException(e); 110 | } 111 | } 112 | 113 | 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/deploy/roborio/FMSConnectedException.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.deploy.roborio; 2 | 3 | public class FMSConnectedException extends RuntimeException { 4 | private static final long serialVersionUID = 1L; 5 | 6 | public FMSConnectedException(String msg) { 7 | super(msg); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/deploy/roborio/FRCJNILibraryArtifact.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.deploy.roborio; 2 | 3 | import java.io.File; 4 | import java.util.Optional; 5 | import java.util.Set; 6 | 7 | import javax.inject.Inject; 8 | 9 | import org.gradle.api.artifacts.Configuration; 10 | import org.gradle.api.file.FileCollection; 11 | import org.gradle.api.file.FileTree; 12 | import org.gradle.api.provider.Property; 13 | import org.gradle.api.tasks.util.PatternFilterable; 14 | import org.gradle.api.tasks.util.PatternSet; 15 | 16 | import edu.wpi.first.deployutils.deploy.artifact.FileCollectionArtifact; 17 | import edu.wpi.first.deployutils.deploy.context.DeployContext; 18 | import edu.wpi.first.deployutils.deploy.target.RemoteTarget; 19 | import edu.wpi.first.gradlerio.deploy.FRCDeployPlugin; 20 | 21 | public class FRCJNILibraryArtifact extends FileCollectionArtifact { 22 | private Property configuration; 23 | private boolean zipped; 24 | private PatternFilterable filter; 25 | 26 | @Inject 27 | public FRCJNILibraryArtifact(String name, RemoteTarget target) { 28 | super(name, target); 29 | 30 | getDirectory().set(FRCDeployPlugin.LIB_DEPLOY_DIR); 31 | 32 | filter = new PatternSet(); 33 | 34 | configuration = target.getProject().getObjects().property(Configuration.class); 35 | 36 | setOnlyIf(ctx -> { 37 | return getFiles().isPresent() && !getFiles().get().isEmpty() && !getFiles().get().getFiles().isEmpty(); 38 | }); 39 | 40 | getPreWorkerThread().add(cfg -> { 41 | if (!configuration.isPresent()) { 42 | return; 43 | } 44 | getFiles().set(computeFiles()); 45 | 46 | }); 47 | 48 | getPostdeploy().add(ctx -> { 49 | FRCDeployPlugin.ownDirectory(ctx, FRCDeployPlugin.LIB_DEPLOY_DIR); 50 | ctx.execute("ldconfig"); 51 | }); 52 | } 53 | 54 | public Property getConfiguration() { 55 | return configuration; 56 | } 57 | 58 | public boolean isZipped() { 59 | return zipped; 60 | } 61 | 62 | public void setZipped(boolean zipped) { 63 | this.zipped = zipped; 64 | } 65 | 66 | public PatternFilterable getFilter() { 67 | return filter; 68 | } 69 | 70 | @Override 71 | public void deploy(DeployContext arg0) { 72 | 73 | super.deploy(arg0); 74 | } 75 | 76 | public FileCollection computeFiles() { 77 | Set configFileCaches = configuration.get().getIncoming().getFiles().getFiles(); 78 | if (zipped) { 79 | Optional allFiles = configFileCaches.stream().map(file -> getTarget().getProject().zipTree(file).matching(filter)).filter(x -> x != null).reduce((a, b) -> a.plus(b)); 80 | if (allFiles.isPresent()) { 81 | return allFiles.get(); 82 | } else { 83 | return getTarget().getProject().files(); 84 | } 85 | } else { 86 | return getTarget().getProject().files(configFileCaches); 87 | } 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/deploy/roborio/FRCJREArtifact.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.deploy.roborio; 2 | 3 | import javax.inject.Inject; 4 | 5 | import org.gradle.api.Project; 6 | 7 | import edu.wpi.first.deployutils.deploy.CommandDeployResult; 8 | import edu.wpi.first.deployutils.deploy.artifact.MavenArtifact; 9 | import edu.wpi.first.deployutils.deploy.context.DeployContext; 10 | import edu.wpi.first.gradlerio.deploy.DeployStage; 11 | import edu.wpi.first.gradlerio.deploy.StagedDeployTarget; 12 | import edu.wpi.first.gradlerio.wpi.WPIExtension; 13 | 14 | public class FRCJREArtifact extends MavenArtifact { 15 | private final String configName; 16 | 17 | public String getConfigName() { 18 | return configName; 19 | } 20 | 21 | public boolean isCheckJreVersion() { 22 | return checkJreVersion; 23 | } 24 | 25 | public void setCheckJreVersion(boolean checkJreVersion) { 26 | this.checkJreVersion = checkJreVersion; 27 | } 28 | 29 | private boolean checkJreVersion = true; 30 | 31 | 32 | @Inject 33 | public FRCJREArtifact(String name, StagedDeployTarget target) { 34 | super(name, target); 35 | String configName = name + "frcjre"; 36 | this.configName = configName; 37 | Project project = target.getProject(); 38 | WPIExtension wpiExt = project.getExtensions().getByType(WPIExtension.class); 39 | getConfiguration().set(project.getConfigurations().create(configName)); 40 | getDependency().set(project.getDependencies().add(configName, wpiExt.getJreArtifactLocation())); 41 | 42 | setOnlyIf(ctx -> { 43 | return jreMissing(ctx) || jreOutOfDate(ctx) || project.hasProperty("force-redeploy-jre"); 44 | }); 45 | 46 | getDirectory().set("/tmp"); 47 | getFilename().set("frcjre.ipk"); 48 | 49 | getPostdeploy().add(ctx -> { 50 | ctx.getLogger().log("Installing JRE..."); 51 | ctx.execute("opkg remove frc*-openjdk*; opkg install /tmp/frcjre.ipk; rm /tmp/frcjre.ipk"); 52 | ctx.getLogger().log("JRE Deployed!"); 53 | }); 54 | 55 | target.setDeployStage(this, DeployStage.FileDeploy); 56 | } 57 | 58 | private boolean jreMissing(DeployContext ctx) { 59 | return ctx.execute("if [[ -f \"/usr/local/frc/JRE/bin/java\" ]]; then echo OK; else echo MISSING; fi").getResult().contains("MISSING"); 60 | } 61 | 62 | private boolean jreOutOfDate(DeployContext ctx) { 63 | if (!checkJreVersion) { 64 | return false; 65 | } 66 | String version = getDependency().get().getVersion(); 67 | CommandDeployResult cmdResult = ctx.execute("opkg list-installed | grep openjdk"); 68 | if (cmdResult.getExitCode() != 0) { 69 | ctx.getLogger().log("JRE not found"); 70 | return false; 71 | } 72 | String result = cmdResult.getResult().trim(); 73 | ctx.getLogger().log("Searching for JRE " + version); 74 | ctx.getLogger().log("Found JRE " + result); 75 | boolean matches = result.contains(version); 76 | ctx.getLogger().log(matches ? "JRE Is Correct Version" : "JRE is mismatched. Reinstalling"); 77 | return !matches; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/deploy/roborio/FRCJavaArtifact.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.deploy.roborio; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import javax.inject.Inject; 7 | 8 | import org.gradle.api.tasks.TaskProvider; 9 | import org.gradle.api.tasks.bundling.Jar; 10 | 11 | import edu.wpi.first.deployutils.PathUtils; 12 | import edu.wpi.first.deployutils.deploy.context.DeployContext; 13 | import edu.wpi.first.gradlerio.deploy.DebuggableJavaArtifact; 14 | import edu.wpi.first.gradlerio.deploy.DeployStage; 15 | import edu.wpi.first.gradlerio.deploy.FRCDeployPlugin; 16 | import edu.wpi.first.gradlerio.wpi.WPIExtension; 17 | 18 | public class FRCJavaArtifact extends DebuggableJavaArtifact { 19 | 20 | private final FRCProgramStartArtifact programStartArtifact; 21 | private final RobotCommandArtifact robotCommandArtifact; 22 | private final FRCJREArtifact jreArtifact; 23 | private final FRCJNILibraryArtifact nativeZipArtifact; 24 | 25 | private final List jvmArgs = new ArrayList<>(); 26 | private final List arguments = new ArrayList<>(); 27 | 28 | private final RoboRIO roboRIO; 29 | 30 | private GarbageCollectorType gcType = GarbageCollectorType.Serial; 31 | 32 | private String javaCommand = "/usr/local/frc/JRE/bin/java"; 33 | 34 | @Inject 35 | public FRCJavaArtifact(String name, RoboRIO target) { 36 | super(name, target); 37 | roboRIO = target; 38 | 39 | jvmArgs.add("-Djava.lang.invoke.stringConcat=BC_SB"); 40 | jvmArgs.add("-Djava.library.path=" + FRCDeployPlugin.LIB_DEPLOY_DIR); 41 | 42 | var debugConfiguration = target.getProject().getConfigurations().create("roborioDebug"); 43 | var releaseConfiguration = target.getProject().getConfigurations().create("roborioRelease"); 44 | 45 | programStartArtifact = target.getArtifacts().create("programStart" + name, FRCProgramStartArtifact.class, art -> { 46 | }); 47 | 48 | jreArtifact = target.getArtifacts().create("jre" + name, FRCJREArtifact.class, art -> { 49 | }); 50 | 51 | robotCommandArtifact = target.getArtifacts().create("robotCommand" + name, RobotCommandArtifact.class, art -> { 52 | art.setStartCommandFunc(this::generateStartCommand); 53 | art.dependsOn(getJarProvider()); 54 | }); 55 | 56 | nativeZipArtifact = target.getArtifacts().create("nativeZips" + name, FRCJNILibraryArtifact.class, artifact -> { 57 | target.setDeployStage(artifact, DeployStage.FileDeploy); 58 | 59 | var cbl = target.getProject().getProviders().provider(() -> { 60 | boolean debug = target.getProject().getExtensions().getByType(WPIExtension.class).getJava().getDebugJni().get(); 61 | if (debug) { 62 | return debugConfiguration; 63 | } else { 64 | return releaseConfiguration; 65 | } 66 | }); 67 | 68 | artifact.getConfiguration().set(cbl); 69 | artifact.setZipped(true); 70 | artifact.getFilter().include("**/*.so*"); 71 | artifact.getFilter().include("**/*.so"); 72 | artifact.getFilter().getExcludes().add("**/*.so.debug"); 73 | artifact.getFilter().getExcludes().add("**/*.so.*.debug"); 74 | }); 75 | 76 | programStartArtifact.getPostdeploy().add(this::postStart); 77 | 78 | getPostdeploy().add(ctx -> { 79 | String binFile = getBinFile(ctx); 80 | ctx.execute("chmod +x \"" + binFile + "\"; chown lvuser \"" + binFile + "\""); 81 | }); 82 | 83 | target.setDeployStage(this, DeployStage.FileDeploy); 84 | } 85 | 86 | public String getJavaCommand() { 87 | return javaCommand; 88 | } 89 | 90 | public void setJavaCommand(String javaCommand) { 91 | this.javaCommand = javaCommand; 92 | } 93 | 94 | public GarbageCollectorType getGcType() { 95 | return gcType; 96 | } 97 | 98 | public void setGcType(GarbageCollectorType gcType) { 99 | this.gcType = gcType; 100 | } 101 | 102 | private String getBinFile(DeployContext ctx) { 103 | return PathUtils.combine(ctx.getWorkingDir(), getFilename().getOrElse(getFile().get().getName())); 104 | } 105 | 106 | @Override 107 | public void setJarTask(Jar jarTask) { 108 | robotCommandArtifact.getDeployTask().configure(x -> x.dependsOn(jarTask)); 109 | super.setJarTask(jarTask); 110 | } 111 | 112 | @Override 113 | public void setJarTask(TaskProvider jarTask) { 114 | robotCommandArtifact.getDeployTask().configure(x -> x.dependsOn(jarTask)); 115 | super.setJarTask(jarTask); 116 | } 117 | 118 | public FRCJREArtifact getJreArtifact() { 119 | return jreArtifact; 120 | } 121 | 122 | public FRCProgramStartArtifact getProgramStartArtifact() { 123 | return programStartArtifact; 124 | } 125 | 126 | public RobotCommandArtifact getRobotCommandArtifact() { 127 | return robotCommandArtifact; 128 | } 129 | 130 | public FRCJNILibraryArtifact getNativeZipArtifact() { 131 | return nativeZipArtifact; 132 | } 133 | 134 | public List getJvmArgs() { 135 | return jvmArgs; 136 | } 137 | 138 | public List getArguments() { 139 | return arguments; 140 | } 141 | 142 | private String generateStartCommand(DeployContext ctx) { 143 | StringBuilder builder = new StringBuilder(); 144 | builder.append(javaCommand); 145 | builder.append(" "); 146 | builder.append(String.join(" ", gcType.getGcArguments())); 147 | builder.append(" "); 148 | builder.append(String.join(" ", jvmArgs)); 149 | builder.append(" "); 150 | 151 | // Debug stuff 152 | boolean debug = roboRIO.getDebug().get(); 153 | if (debug) { 154 | builder.append("-XX:+UsePerfData -agentlib:jdwp=transport=dt_socket,address=0.0.0.0:"); 155 | builder.append(getDebugPort()); 156 | builder.append(",server=y,suspend=y "); 157 | } 158 | 159 | String binFile = getBinFile(ctx); 160 | 161 | builder.append("-jar \""); 162 | builder.append(binFile); 163 | builder.append("\" "); 164 | builder.append(String.join(" ", arguments)); 165 | 166 | return builder.toString(); 167 | } 168 | 169 | private void postStart(DeployContext ctx) { 170 | boolean debug = roboRIO.getDebug().get(); 171 | if (debug) { 172 | ctx.getLogger().withLock(x -> { 173 | x.log("===================================================================="); 174 | x.log("DEBUGGING ACTIVE ON PORT " + getDebugPort() + "!"); 175 | x.log("===================================================================="); 176 | }); 177 | } 178 | } 179 | 180 | } 181 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/deploy/roborio/FRCNativeArtifact.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.deploy.roborio; 2 | 3 | import java.io.File; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | import javax.inject.Inject; 8 | 9 | import org.gradle.api.provider.Property; 10 | import org.gradle.api.tasks.util.PatternFilterable; 11 | import org.gradle.nativeplatform.NativeExecutableBinarySpec; 12 | import org.gradle.nativeplatform.NativeExecutableSpec; 13 | 14 | import edu.wpi.first.deployutils.PathUtils; 15 | import edu.wpi.first.deployutils.deploy.context.DeployContext; 16 | import edu.wpi.first.gradlerio.deploy.DebuggableNativeArtifact; 17 | import edu.wpi.first.gradlerio.deploy.DeployStage; 18 | import edu.wpi.first.gradlerio.deploy.FRCDeployPlugin; 19 | 20 | public class FRCNativeArtifact extends DebuggableNativeArtifact { 21 | 22 | private final FRCProgramStartArtifact programStartArtifact; 23 | private final RobotCommandArtifact robotCommandArtifact; 24 | private final List arguments = new ArrayList<>(); 25 | private final RoboRIO roboRIO; 26 | 27 | private final Property componentSpec; 28 | 29 | public Property getComponent() { 30 | return componentSpec; 31 | } 32 | 33 | @Inject 34 | public FRCNativeArtifact(String name, RoboRIO target) { 35 | super(name, target); 36 | roboRIO = target; 37 | 38 | componentSpec = target.getProject().getObjects().property(NativeExecutableSpec.class); 39 | 40 | getBinary().set(componentSpec.map(x -> { 41 | for (NativeExecutableBinarySpec bin : x.getBinaries().withType(NativeExecutableBinarySpec.class)) { 42 | if (bin.getTargetPlatform().getName().equals(getTarget().getTargetPlatform().get()) && 43 | bin.getBuildType().getName().equals(target.getBuildType().get())) { 44 | return bin; 45 | } 46 | } 47 | return null; 48 | })); 49 | 50 | PatternFilterable filterable = getLibraryFilter(); 51 | filterable.getExcludes().add("**/*.so.debug"); 52 | filterable.getExcludes().add("**/*.so.*.debug"); 53 | 54 | getPostdeploy().add(ctx -> { 55 | FRCDeployPlugin.ownDirectory(ctx, getLibraryDirectory().get()); 56 | ctx.execute("ldconfig"); 57 | }); 58 | 59 | programStartArtifact = target.getArtifacts().create("programStart" + name, FRCProgramStartArtifact.class, art -> { 60 | }); 61 | 62 | robotCommandArtifact = target.getArtifacts().create("robotCommand" + name, RobotCommandArtifact.class, art -> { 63 | art.setStartCommandFunc(this::generateStartCommand); 64 | art.dependsOn(getInstallTaskProvider()); 65 | }); 66 | 67 | programStartArtifact.getPostdeploy().add(this::postStart); 68 | 69 | getPostdeploy().add(ctx -> { 70 | String binFile = getBinFile(ctx); 71 | ctx.execute("chmod +x \"" + binFile + "\"; chown lvuser \"" + binFile + "\""); 72 | // Let user program set RT thread priorities by making CAP_SYS_NICE 73 | // permitted, inheritable, and effective. See "man 7 capabilities" 74 | // for docs on capabilities and file capability sets. 75 | ctx.execute("setcap cap_sys_nice+eip \"" + binFile + "\""); 76 | }); 77 | 78 | this.getLibraryDirectory().set(FRCDeployPlugin.LIB_DEPLOY_DIR); 79 | 80 | target.setDeployStage(this, DeployStage.FileDeploy); 81 | } 82 | 83 | private String getBinFile(DeployContext ctx) { 84 | File exeFile = getDeployedFile(); 85 | return PathUtils.combine(ctx.getWorkingDir(), getFilename().getOrElse(exeFile.getName())); 86 | } 87 | 88 | public FRCProgramStartArtifact getProgramStartArtifact() { 89 | return programStartArtifact; 90 | } 91 | 92 | public RobotCommandArtifact getRobotCommandArtifact() { 93 | return robotCommandArtifact; 94 | } 95 | 96 | public List getArguments() { 97 | return arguments; 98 | } 99 | 100 | private String generateStartCommand(DeployContext ctx) { 101 | StringBuilder builder = new StringBuilder(); 102 | boolean debug = roboRIO.getDebug().get(); 103 | if (debug) { 104 | builder.append("gdbserver host:"); 105 | builder.append(getDebugPort()); 106 | builder.append(' '); 107 | } 108 | builder.append('\"'); 109 | String binFile = getBinFile(ctx); 110 | builder.append(binFile); 111 | builder.append("\" "); 112 | builder.append(String.join(" ", arguments)); 113 | 114 | return builder.toString(); 115 | } 116 | 117 | private void postStart(DeployContext ctx) { 118 | boolean debug = roboRIO.getDebug().get(); 119 | if (debug) { 120 | ctx.getLogger().withLock(x -> { 121 | x.log("===================================================================="); 122 | x.log("DEBUGGING ACTIVE ON PORT " + getDebugPort() + "!"); 123 | x.log("===================================================================="); 124 | }); 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/deploy/roborio/FRCNativeLibraryArtifact.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.deploy.roborio; 2 | 3 | import javax.inject.Inject; 4 | 5 | import org.gradle.api.Project; 6 | 7 | //import edu.wpi.first.deployutils.deploy.artifact.NativeArtifact; 8 | 9 | public class FRCNativeLibraryArtifact { // TODO Extend NativeLibraryArtifact 10 | 11 | // @Inject 12 | // public FRCNativeLibraryArtifact(String name, Project project) { 13 | // super(name, project); 14 | 15 | // getDirectory().set(FRCPlugin.LIB_DEPLOY_DIR); 16 | 17 | // getPostdeploy().add(ctx -> { 18 | // FRCPlugin.ownDirectory(ctx, FRCPlugin.LIB_DEPLOY_DIR); 19 | // ctx.execute("ldconfig"); 20 | // }); 21 | 22 | // getExtensionContainer().add(DeployStage.class, "stage", DeployStage.FileDeploy); 23 | 24 | // } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/deploy/roborio/FRCProgramKillArtifact.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.deploy.roborio; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.security.DigestInputStream; 7 | import java.security.MessageDigest; 8 | import java.security.NoSuchAlgorithmException; 9 | 10 | import javax.inject.Inject; 11 | 12 | import org.codehaus.groovy.runtime.EncodingGroovyMethods; 13 | 14 | import edu.wpi.first.deployutils.deploy.artifact.AbstractArtifact; 15 | import edu.wpi.first.deployutils.deploy.context.DeployContext; 16 | import edu.wpi.first.gradlerio.deploy.DeployStage; 17 | import edu.wpi.first.gradlerio.deploy.StagedDeployTarget; 18 | import edu.wpi.first.gradlerio.wpi.dependencies.tools.ToolInstallTask; 19 | 20 | public class FRCProgramKillArtifact extends AbstractArtifact { 21 | 22 | @Inject 23 | public FRCProgramKillArtifact(String name, StagedDeployTarget target) { 24 | super(name, target); 25 | 26 | target.setDeployStage(this, DeployStage.ProgramKill); 27 | } 28 | 29 | @Override 30 | public void deploy(DeployContext ctx) { 31 | 32 | 33 | MessageDigest md; 34 | try { 35 | md = MessageDigest.getInstance("MD5"); 36 | } catch (NoSuchAlgorithmException e1) { 37 | throw new RuntimeException(e1); 38 | } 39 | 40 | try (DigestInputStream it = new DigestInputStream(ToolInstallTask.class.getResourceAsStream("/frcKillRobot.sh"), md); 41 | ByteArrayOutputStreamAccessor dump = new ByteArrayOutputStreamAccessor()) { 42 | it.transferTo(dump); 43 | String local = EncodingGroovyMethods.encodeHex(md.digest()).toString(); 44 | 45 | String result = ctx.execute("md5sum /usr/local/frc/bin/frcKillRobot.sh").getResult(); 46 | if (result != null && result.toLowerCase().startsWith(local.toLowerCase())) { 47 | ctx.getLogger().log("Skipping redeploy of frcKillRobot script"); 48 | } else { 49 | ctx.getLogger().log("Redeploying frcKillRobot script"); 50 | try (InputStream out = new ByteArrayInputStream(dump.getBackingArray(), 0, dump.getBackingLength())) { 51 | ctx.put(out, "/usr/local/frc/bin/frcKillRobot.sh.tmp"); 52 | } 53 | ctx.execute("mv /usr/local/frc/bin/frcKillRobot.sh.tmp /usr/local/frc/bin/frcKillRobot.sh && chmod +x /usr/local/frc/bin/frcKillRobot.sh"); 54 | ctx.execute("sync"); 55 | } 56 | } catch (IOException e) { 57 | throw new RuntimeException(e); 58 | } 59 | ctx.execute(". /etc/profile.d/natinst-path.sh; /usr/local/frc/bin/frcKillRobot.sh -t 2> /dev/null"); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/deploy/roborio/FRCProgramStartArtifact.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.deploy.roborio; 2 | 3 | import javax.inject.Inject; 4 | 5 | import edu.wpi.first.deployutils.deploy.artifact.AbstractArtifact; 6 | import edu.wpi.first.deployutils.deploy.context.DeployContext; 7 | import edu.wpi.first.gradlerio.deploy.DeployStage; 8 | import edu.wpi.first.gradlerio.deploy.StagedDeployTarget; 9 | 10 | public class FRCProgramStartArtifact extends AbstractArtifact { 11 | 12 | @Inject 13 | public FRCProgramStartArtifact(String name, StagedDeployTarget target) { 14 | super(name, target); 15 | 16 | target.setDeployStage(this, DeployStage.ProgramStart); 17 | } 18 | 19 | @Override 20 | public void deploy(DeployContext ctx) { 21 | ctx.execute("sync"); 22 | String result = ctx.execute("/usr/local/natinst/bin/nirtcfg --file=/etc/natinst/share/ni-rt.ini --get section=systemsettings,token=NoApp.enabled,value=unknown").getResult(); 23 | if (result != null && result.trim().equalsIgnoreCase("true")) { 24 | ctx.getLogger().logError("NoApp is set on the device. Robot program cannot be started. Disable NoApp either with the imaging tool or by holding the User button for 5 seconds"); 25 | } else { 26 | ctx.execute(". /etc/profile.d/natinst-path.sh; /usr/local/frc/bin/frcKillRobot.sh -t -r 2> /dev/null"); 27 | } 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/deploy/roborio/GarbageCollectorType.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.deploy.roborio; 2 | 3 | import java.util.List; 4 | 5 | public enum GarbageCollectorType { 6 | G1("-XX:+UseG1GC", "-XX:MaxGCPauseMillis=1", "-XX:GCTimeRatio=1"), 7 | G1_LongPause("-XX:+UseG1GC", "-XX:MaxGCPauseMillis=5", "-XX:GCTimeRatio=1"), 8 | G1_Base("-XX:+UseG1GC"), 9 | Serial("-XX:+UseSerialGC"), 10 | Parallel("-XX:+UseParallelGC"), 11 | Serial_PauseGoal("-XX:+UseSerialGC", "-XX:MaxGCPauseMillis=5"), 12 | Parallel_PauseGoal("-XX:+UseParallelGC", "-XX:MaxGCPauseMillis=5"), 13 | Other(); 14 | 15 | private final List gcArguments; 16 | 17 | public List getGcArguments() { 18 | return gcArguments; 19 | } 20 | 21 | private GarbageCollectorType(String... arguments) { 22 | gcArguments = List.of(arguments); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/deploy/roborio/InvalidImageException.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.deploy.roborio; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class InvalidImageException extends RuntimeException { 7 | private static final long serialVersionUID = 1840472963312724922L; 8 | private final String imageVersion; 9 | private final List allowedImageVersions; 10 | 11 | public String getImageVersion() { 12 | return imageVersion; 13 | } 14 | 15 | public List getAllowedImageVersions() { 16 | return allowedImageVersions; 17 | } 18 | 19 | private static String parseMessage(String imageVersion, List allowedImageVersions) { 20 | return "Invalid RoboRIO Image Version!" + 21 | "\nRoboRIO image and GradleRIO versions are incompatible:" + 22 | "\n\tCurrent image version: " + imageVersion + 23 | "\n\tGradleRIO-compatible image versions: " + String.join(", ", allowedImageVersions) + 24 | "\nSee https://docs.wpilib.org/en/stable/docs/zero-to-robot/step-3/imaging-your-roborio.html" + 25 | "\nfor information about upgrading the RoboRIO image." + 26 | "\nSee https://docs.wpilib.org/en/stable/docs/zero-to-robot/step-2/wpilib-setup.html and" + 27 | "\nhttps://docs.wpilib.org/en/stable/docs/software/vscode-overview/importing-gradle-project.html" + 28 | "\nfor information about updating WPILib and GradleRIO."; 29 | } 30 | 31 | public InvalidImageException(String imageVersion, List allowedImageVersions) { 32 | super(parseMessage(imageVersion, allowedImageVersions)); 33 | this.imageVersion = imageVersion; 34 | this.allowedImageVersions = new ArrayList<>(allowedImageVersions); 35 | } 36 | 37 | public InvalidImageException() { 38 | super("Could not parse image version!"); 39 | allowedImageVersions = List.of(); 40 | this.imageVersion = ""; 41 | } 42 | 43 | public InvalidImageException(String imageFile) { 44 | super("Could not parse image version!\n/etc/natinst/share/scs_imagemetadata.ini contents:\n" + imageFile); 45 | allowedImageVersions = List.of(); 46 | this.imageVersion = ""; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/deploy/roborio/RoboRIO.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.deploy.roborio; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.regex.Matcher; 6 | import java.util.regex.Pattern; 7 | 8 | import javax.inject.Inject; 9 | 10 | import org.gradle.api.Project; 11 | import org.gradle.api.logging.Logger; 12 | import org.gradle.api.logging.Logging; 13 | 14 | import edu.wpi.first.deployutils.deploy.DeployExtension; 15 | import edu.wpi.first.deployutils.deploy.context.DeployContext; 16 | import edu.wpi.first.deployutils.deploy.target.location.SshDeployLocation; 17 | import edu.wpi.first.gradlerio.deploy.FRCExtension; 18 | import edu.wpi.first.gradlerio.deploy.WPIRemoteTarget; 19 | import edu.wpi.first.gradlerio.wpi.WPIExtension; 20 | import edu.wpi.first.toolchain.NativePlatforms; 21 | 22 | public class RoboRIO extends WPIRemoteTarget { 23 | 24 | private final Logger log; 25 | private int team; 26 | private boolean checkImage = true; 27 | private final List validImageVersions; 28 | 29 | private final FRCProgramKillArtifact programKillArtifact; 30 | 31 | @Inject 32 | public RoboRIO(String name, Project project, DeployExtension de, FRCExtension frcExtension) { 33 | super(name, project, de, frcExtension); 34 | log = Logging.getLogger(this.toString()); 35 | 36 | setDirectory("/home/lvuser"); 37 | 38 | setMaxChannels(4); 39 | 40 | // Increase timeout. The only time this is really used is if the host is resolved, 41 | // but takes forever to connect, which can happen if the CPU is loaded. 42 | setTimeout(7); 43 | 44 | setOnlyIf(ctx -> verifyOnlyIf(ctx)); 45 | 46 | // Make a copy of valid image versions so user defined cannot modify the global array 47 | validImageVersions = new ArrayList<>(WPIExtension.getValidImageVersions()); 48 | 49 | programKillArtifact = project.getObjects().newInstance(FRCProgramKillArtifact.class, "programKill" + name, this); 50 | 51 | getTargetPlatform().set(NativePlatforms.roborio); 52 | 53 | getArtifacts().add(programKillArtifact); 54 | } 55 | 56 | public FRCProgramKillArtifact getProgramKillArtifact() { 57 | return programKillArtifact; 58 | } 59 | 60 | public List getValidImageVersions() { 61 | return validImageVersions; 62 | } 63 | 64 | public boolean isCheckImage() { 65 | return checkImage; 66 | } 67 | 68 | public void setCheckImage(boolean checkImage) { 69 | this.checkImage = checkImage; 70 | } 71 | 72 | public int getTeam() { 73 | return team; 74 | } 75 | 76 | public void setTeam(int team) { 77 | this.team = team; 78 | setAddresses( 79 | "roborio-" + team + "-FRC.local", // Default mDNS 80 | "10." + (team / 100) + "." + (team % 100) + ".2", // 10.TE.AM.2 81 | "172.22.11.2", // USB 82 | 83 | // Weird Environments 84 | "roborio-" + team + "-FRC", // Default DNS 85 | "roborio-" + team + "-FRC.lan", // LAN mDNS/DNS 86 | "roborio-" + team + "-FRC.frc-field.local" /// Practice Field mDNS 87 | ); 88 | } 89 | 90 | public void setAddresses(String... addresses) { 91 | this.getLocations().clear(); 92 | 93 | for (String addr : addresses) { 94 | this.addAddress(addr); 95 | } 96 | 97 | getLocations().create("ds", DSDeployLocation.class, ds -> { 98 | ds.setUser("admin"); 99 | ds.setPassword(""); 100 | ds.setIpv6(false); 101 | }); 102 | } 103 | 104 | public void addAddress(String address) { 105 | getLocations().create(address, SshDeployLocation.class, loc -> { 106 | loc.setAddress(address); 107 | loc.setIpv6(false); 108 | loc.setUser("admin"); 109 | loc.setPassword(""); 110 | }); 111 | } 112 | 113 | private boolean verifyOnlyIf(DeployContext ctx) { 114 | ctx.getLogger().silent(true); 115 | try { 116 | if (checkImage) { 117 | log.info("Checking image..."); 118 | readAndVerifyImage(ctx); 119 | } 120 | } finally { 121 | ctx.getLogger().silent(false); 122 | } 123 | return true; 124 | } 125 | 126 | private void readAndVerifyImage(DeployContext context) { 127 | final String imageFile = "/etc/natinst/share/scs_imagemetadata.ini"; 128 | final Pattern pattern = Pattern.compile("^IMAGEVERSION\\s*=\\s*\\\"(FRC_)?roboRIO2?_(?\\d{4}_v\\d+(?:\\.\\d+)?)\\\""); 129 | 130 | String content = context.execute("cat " + imageFile).getResult(); 131 | log.info("Received Image File: "); 132 | log.info(content); 133 | 134 | boolean imageFound = false; 135 | for (String line : content.split("\n")) { 136 | log.info(line); 137 | Matcher matcher = pattern.matcher(line.trim()); 138 | if (matcher.matches()) { 139 | String imageGroup = matcher.group("version"); 140 | log.info("Matched version: " + imageGroup); 141 | verifyImageVersion(imageGroup); 142 | imageFound = true; 143 | break; 144 | } 145 | } 146 | 147 | if (!imageFound) { 148 | throw new InvalidImageException(content); 149 | } 150 | } 151 | 152 | private void verifyImageVersion(String image) { 153 | boolean foundMatch = validImageVersions.stream().filter(x -> { 154 | int index = x.indexOf("*"); 155 | if (index == -1) { 156 | // no wildcard, check if versions are equal 157 | return x.equals(image); 158 | } else if (index > image.length()) { 159 | return false; 160 | } else { 161 | return (x.substring(0, index).equals(image.substring(0, index))); 162 | } 163 | }).findAny().isPresent(); 164 | if (!foundMatch) { 165 | throw new InvalidImageException(image, validImageVersions); 166 | } 167 | } 168 | 169 | @Override 170 | public String toString() { 171 | return "RoboRIO[" + getName() + "]"; 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/deploy/roborio/RobotCommandArtifact.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.deploy.roborio; 2 | 3 | import java.util.function.Function; 4 | 5 | import javax.inject.Inject; 6 | 7 | import edu.wpi.first.deployutils.deploy.artifact.FileArtifact; 8 | import edu.wpi.first.deployutils.deploy.context.DeployContext; 9 | import edu.wpi.first.gradlerio.deploy.DeployStage; 10 | import edu.wpi.first.gradlerio.deploy.StagedDeployTarget; 11 | 12 | public class RobotCommandArtifact extends FileArtifact { 13 | 14 | private Function startCommandFunc; 15 | 16 | @Inject 17 | public RobotCommandArtifact(String name, StagedDeployTarget target) { 18 | super(name, target); 19 | 20 | target.setDeployStage(this, DeployStage.FileDeploy); 21 | } 22 | 23 | public Function getStartCommandFunc() { 24 | return startCommandFunc; 25 | } 26 | 27 | public void setStartCommandFunc(Function startCommandFunc) { 28 | this.startCommandFunc = startCommandFunc; 29 | } 30 | 31 | @Override 32 | public void deploy(DeployContext ctx) { 33 | String content = startCommandFunc.apply(ctx); 34 | 35 | ctx.execute("echo '" + content + "' > /home/lvuser/robotCommand"); 36 | ctx.execute("chmod +x /home/lvuser/robotCommand; chown lvuser /home/lvuser/robotCommand"); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/simulation/HalSimPair.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.simulation; 2 | 3 | public class HalSimPair { 4 | public final String name; 5 | public final String libName; 6 | public final boolean defaultEnabled; 7 | 8 | public HalSimPair(String name, String libName, boolean defaultEnabled) { 9 | this.name = name; 10 | this.libName = libName; 11 | this.defaultEnabled = defaultEnabled; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/simulation/JavaExternalSimulationTask.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.simulation; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | import javax.inject.Inject; 10 | 11 | import com.google.gson.GsonBuilder; 12 | 13 | import org.codehaus.groovy.runtime.ResourceGroovyMethods; 14 | import org.gradle.api.DefaultTask; 15 | import org.gradle.api.Project; 16 | import org.gradle.api.file.RegularFileProperty; 17 | import org.gradle.api.model.ObjectFactory; 18 | import org.gradle.api.provider.Provider; 19 | import org.gradle.api.tasks.Internal; 20 | import org.gradle.api.tasks.OutputFile; 21 | import org.gradle.api.tasks.TaskAction; 22 | import org.gradle.jvm.tasks.Jar; 23 | 24 | import edu.wpi.first.gradlerio.wpi.WPIExtension; 25 | import edu.wpi.first.gradlerio.wpi.java.ExtractNativeJavaArtifacts; 26 | import edu.wpi.first.gradlerio.wpi.simulation.SimulationExtension; 27 | 28 | public class JavaExternalSimulationTask extends DefaultTask { 29 | private final List jars = new ArrayList<>(); 30 | private Provider extractJni; 31 | private boolean isDebug; 32 | 33 | @Internal 34 | public List getJars() { 35 | return jars; 36 | } 37 | 38 | public void setDependencies(SimulationExtension sim, Provider extract, boolean debug, Project project) { 39 | this.extractJni = extract; 40 | isDebug = debug; 41 | this.dependsOn(extractJni); 42 | } 43 | 44 | @Inject 45 | public JavaExternalSimulationTask(ObjectFactory objects) { 46 | getOutputs().upToDateWhen(spec -> false); 47 | dependsOn(jars); 48 | simulationFile = objects.fileProperty(); 49 | } 50 | 51 | private final RegularFileProperty simulationFile; 52 | 53 | @OutputFile 54 | public RegularFileProperty getSimulationFile() { 55 | return simulationFile; 56 | } 57 | 58 | public static class SimInfo { 59 | public final String type = "java"; 60 | public final String name; 61 | public final List extensions; 62 | public final Map environment; 63 | public final String libraryDir; 64 | public final String mainClassName; 65 | 66 | public SimInfo(String name, List extensions, Map environment, String libraryDir, 67 | String mainClassName) { 68 | this.name = name; 69 | this.extensions = extensions; 70 | this.environment = environment; 71 | this.libraryDir = libraryDir; 72 | this.mainClassName = mainClassName; 73 | } 74 | } 75 | 76 | @TaskAction 77 | public void execute() throws IOException { 78 | var ext = getProject().getExtensions().getByType(WPIExtension.class); 79 | SimulationExtension sim = ext.getSim(); 80 | 81 | File ldpath = extractJni.get().getDestinationDirectory().get().getAsFile(); 82 | 83 | List simInfo = new ArrayList<>(); 84 | 85 | List extensions = sim.getHalSimLocations(List.of(ldpath), isDebug); 86 | 87 | Map env = sim.getEnvironment(); 88 | 89 | for (Jar jar : jars) { 90 | String name = jar.getName() + " (in project " + getProject().getName() + ")"; 91 | 92 | String mainClass = (String)jar.getManifest().getAttributes().get("Main-Class"); 93 | 94 | simInfo.add(new SimInfo(name, extensions, env, ldpath.getAbsolutePath(), mainClass)); 95 | } 96 | 97 | GsonBuilder builder = new GsonBuilder(); 98 | builder.setPrettyPrinting(); 99 | File outputFile = simulationFile.get().getAsFile(); 100 | outputFile.getParentFile().mkdirs(); 101 | ResourceGroovyMethods.setText(outputFile, builder.create().toJson(simInfo)); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/simulation/JavaSimulationTask.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.simulation; 2 | 3 | import org.gradle.api.tasks.JavaExec; 4 | 5 | public class JavaSimulationTask extends JavaExec { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/simulation/NativeExternalSimulationTask.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.simulation; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | import java.util.List; 8 | import java.util.Map; 9 | import java.util.concurrent.Callable; 10 | 11 | import javax.inject.Inject; 12 | 13 | import com.google.gson.GsonBuilder; 14 | 15 | import org.codehaus.groovy.runtime.ResourceGroovyMethods; 16 | import org.gradle.api.DefaultTask; 17 | import org.gradle.api.GradleException; 18 | import org.gradle.api.file.RegularFileProperty; 19 | import org.gradle.api.model.ObjectFactory; 20 | import org.gradle.api.tasks.Internal; 21 | import org.gradle.api.tasks.OutputFile; 22 | import org.gradle.api.tasks.TaskAction; 23 | import org.gradle.language.nativeplatform.HeaderExportingSourceSet; 24 | import org.gradle.nativeplatform.NativeBinarySpec; 25 | import org.gradle.nativeplatform.NativeDependencySet; 26 | import org.gradle.nativeplatform.NativeExecutableBinarySpec; 27 | import org.gradle.nativeplatform.tasks.InstallExecutable; 28 | import org.gradle.nativeplatform.test.NativeTestSuiteBinarySpec; 29 | import org.gradle.nativeplatform.toolchain.Clang; 30 | 31 | import edu.wpi.first.gradlerio.wpi.WPIExtension; 32 | import edu.wpi.first.gradlerio.wpi.simulation.SimulationExtension; 33 | import edu.wpi.first.vscode.dependencies.SourceContainingNativeDependencySet; 34 | 35 | public class NativeExternalSimulationTask extends DefaultTask { 36 | 37 | private final List binaries = new ArrayList<>(); 38 | 39 | @Internal 40 | public List getBinaries() { 41 | return binaries; 42 | } 43 | 44 | private InstallExecutable getInstallForBinary(NativeBinarySpec binary) { 45 | if (binary instanceof NativeExecutableBinarySpec) { 46 | return (InstallExecutable)((NativeExecutableBinarySpec)binary).getTasks().getInstall(); 47 | } else if (binary instanceof NativeTestSuiteBinarySpec) { 48 | return (InstallExecutable)((NativeTestSuiteBinarySpec)binary).getTasks().getInstall(); 49 | } else { 50 | throw new GradleException("Unknown binary type"); 51 | } 52 | } 53 | 54 | @Inject 55 | public NativeExternalSimulationTask(ObjectFactory objects) { 56 | getOutputs().upToDateWhen(spec -> false); 57 | Callable cbl = () -> binaries.stream().map(this::getInstallForBinary).toArray(); 58 | dependsOn(cbl); 59 | simulationFile = objects.fileProperty(); 60 | } 61 | 62 | private final RegularFileProperty simulationFile; 63 | 64 | @OutputFile 65 | public RegularFileProperty getSimulationFile() { 66 | return simulationFile; 67 | } 68 | 69 | public static class SimInfo { 70 | public final String type = "native"; 71 | public String name; 72 | public List extensions; 73 | public String launchfile; 74 | public boolean clang; 75 | public Map environment; 76 | 77 | public Object[] srcpaths; 78 | public Object[] headerpaths; 79 | public Object[] libpaths; 80 | public Object[] libsrcpaths; 81 | public SimInfo(String name, List extensions, String launchfile, boolean clang, 82 | Map environment, List srcpaths, List headerpaths, List libpaths, List libsrcpaths) { 83 | this.name = name; 84 | this.extensions = extensions; 85 | this.launchfile = launchfile; 86 | this.clang = clang; 87 | this.environment = environment; 88 | this.srcpaths = srcpaths.stream().map(x -> x.getAbsolutePath()).toArray(); 89 | this.headerpaths = headerpaths.stream().map(x -> x.getAbsolutePath()).toArray(); 90 | this.libpaths = libpaths.stream().map(x -> x.getAbsolutePath()).toArray(); 91 | this.libsrcpaths = libsrcpaths.stream().map(x -> x.getAbsolutePath()).toArray(); 92 | } 93 | } 94 | 95 | @TaskAction 96 | public void execute() throws IOException { 97 | SimulationExtension sim = getProject().getExtensions().getByType(WPIExtension.class).getSim(); 98 | 99 | List simInfo = new ArrayList<>(); 100 | 101 | 102 | Map env = sim.getEnvironment(); 103 | 104 | for (NativeBinarySpec binary : binaries) { 105 | InstallExecutable install = getInstallForBinary(binary); 106 | 107 | String name = binary.getComponent().getName() + " (in project " + getProject().getName() + ")"; 108 | 109 | String launchfile = install.getInstalledExecutable().get().getAsFile().getAbsolutePath(); 110 | boolean clang = binary.getToolChain() instanceof Clang; 111 | 112 | List srcpaths = new ArrayList<>(); 113 | List headerpaths = new ArrayList<>(); 114 | List libsrcpaths = new ArrayList<>(); 115 | 116 | for (HeaderExportingSourceSet sourceSet : binary.getInputs().withType(HeaderExportingSourceSet.class)) { 117 | srcpaths.addAll(sourceSet.getSource().getSrcDirs()); 118 | srcpaths.addAll(sourceSet.getExportedHeaders().getSrcDirs()); 119 | } 120 | 121 | // Get all folders in install dir 122 | List libpaths = new ArrayList<>(Arrays.asList(install.getInstallDirectory().get().getAsFile().listFiles(f -> f.isDirectory()))); 123 | libpaths.add(install.getInstallDirectory().get().getAsFile()); 124 | 125 | for (NativeDependencySet ds : binary.getLibs()) { 126 | headerpaths.addAll(ds.getIncludeRoots().getFiles()); 127 | 128 | if (ds instanceof SourceContainingNativeDependencySet) { 129 | SourceContainingNativeDependencySet scnDs = (SourceContainingNativeDependencySet)ds; 130 | libsrcpaths.addAll(scnDs.getSourceRoots().getFiles()); 131 | } 132 | } 133 | 134 | List extensions = sim.getHalSimLocations(libpaths, binary.getBuildType().getName().contains("debug")); 135 | 136 | simInfo.add(new SimInfo(name, extensions, launchfile, clang, env, srcpaths, headerpaths, libpaths, libsrcpaths)); 137 | } 138 | 139 | GsonBuilder builder = new GsonBuilder(); 140 | builder.setPrettyPrinting(); 141 | File outputFile = simulationFile.get().getAsFile(); 142 | outputFile.getParentFile().mkdirs(); 143 | ResourceGroovyMethods.setText(outputFile, builder.create().toJson(simInfo)); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/simulation/NativeSimulationTask.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.simulation; 2 | 3 | import java.io.File; 4 | import java.util.ArrayList; 5 | import java.util.Arrays; 6 | import java.util.List; 7 | import java.util.Optional; 8 | import java.util.concurrent.Callable; 9 | 10 | import javax.inject.Inject; 11 | 12 | import org.gradle.api.GradleException; 13 | import org.gradle.api.tasks.AbstractExecTask; 14 | import org.gradle.api.tasks.Internal; 15 | import org.gradle.api.tasks.TaskAction; 16 | import org.gradle.nativeplatform.NativeBinarySpec; 17 | import org.gradle.nativeplatform.NativeExecutableBinarySpec; 18 | import org.gradle.nativeplatform.tasks.InstallExecutable; 19 | 20 | import edu.wpi.first.gradlerio.wpi.WPIExtension; 21 | import edu.wpi.first.gradlerio.wpi.simulation.SimulationExtension; 22 | 23 | public class NativeSimulationTask extends AbstractExecTask { 24 | private final List binaries = new ArrayList<>(); 25 | 26 | @Internal 27 | public List getBinaries() { 28 | return binaries; 29 | } 30 | 31 | private InstallExecutable getInstallForBinary(NativeBinarySpec binary) { 32 | if (binary instanceof NativeExecutableBinarySpec) { 33 | return (InstallExecutable)((NativeExecutableBinarySpec)binary).getTasks().getInstall(); 34 | } else { 35 | throw new GradleException("Unknown binary type"); 36 | } 37 | } 38 | 39 | private final Callable cbl; 40 | 41 | @Inject 42 | public NativeSimulationTask() { 43 | super(NativeSimulationTask.class); 44 | getOutputs().upToDateWhen(spec -> false); 45 | cbl = () -> binaries.stream().map(this::getInstallForBinary).toArray(); 46 | dependsOn(cbl); 47 | } 48 | 49 | @TaskAction 50 | @Override 51 | protected void exec() { 52 | if (binaries.size() != 1) { 53 | throw new GradleException("Must have 1 and only 1 binary"); 54 | } 55 | NativeExecutableBinarySpec binary = (NativeExecutableBinarySpec)binaries.get(0); 56 | InstallExecutable install = (InstallExecutable)binary.getTasks().getInstall(); 57 | 58 | setExecutable(install.getRunScriptFile().get().getAsFile()); 59 | 60 | List libpaths = new ArrayList<>(Arrays.asList(install.getInstallDirectory().get().getAsFile().listFiles(f -> f.isDirectory()))); 61 | libpaths.add(install.getInstallDirectory().get().getAsFile()); 62 | 63 | SimulationExtension sim = getProject().getExtensions().getByType(WPIExtension.class).getSim(); 64 | 65 | List extensions = sim.getHalSimLocations(libpaths, binary.getBuildType().getName().contains("debug")); 66 | Optional extensionString = extensions.stream().filter(x -> x.defaultEnabled).map(x -> x.libName).reduce((a, b) -> a + File.pathSeparator + b); 67 | if (extensionString.isPresent()) { 68 | environment("HALSIM_EXTENSIONS", extensionString.get()); 69 | } 70 | 71 | super.exec(); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/wpi/WPIMavenExtension.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.wpi; 2 | 3 | import org.gradle.api.Action; 4 | import org.gradle.api.Project; 5 | import org.gradle.api.internal.DefaultNamedDomainObjectSet; 6 | import org.gradle.api.internal.CollectionCallbackActionDecorator; 7 | import org.gradle.internal.reflect.DirectInstantiator; 8 | 9 | import java.util.HashSet; 10 | import java.util.Set; 11 | 12 | import javax.inject.Inject; 13 | 14 | public class WPIMavenExtension extends DefaultNamedDomainObjectSet { 15 | 16 | private final Project project; 17 | 18 | private final Set vendorCacheGroupIds = new HashSet<>(); 19 | private boolean useDevelopment; 20 | private boolean useLocal; 21 | private boolean useFrcMavenLocalDevelopment; 22 | private boolean useFrcMavenLocalRelease; 23 | private boolean useMavenCentral; 24 | private boolean useFrcMavenVendorCache; 25 | private boolean enableRepositoryGroupLimits; 26 | 27 | @Inject 28 | public WPIMavenExtension(Project project) { 29 | super(WPIMavenRepo.class, DirectInstantiator.INSTANCE, CollectionCallbackActionDecorator.NOOP); 30 | this.project = project; 31 | 32 | this.useDevelopment = false; // Do not rename without changing versionupdates.gradle 33 | this.useLocal = true; 34 | this.useFrcMavenLocalDevelopment = false; 35 | this.useFrcMavenLocalRelease = false; 36 | this.useMavenCentral = true; 37 | this.useFrcMavenVendorCache = true; 38 | this.enableRepositoryGroupLimits = true; 39 | 40 | // mirror("AU") { WPIMavenRepo mirror -> 41 | // mirror.release = "http://wpimirror.imjac.in/m2/release" 42 | // mirror.development = "http://wpimirror.imjac.in/m2/development" 43 | // } 44 | } 45 | 46 | public boolean isEnableRepositoryGroupLimits() { 47 | return enableRepositoryGroupLimits; 48 | } 49 | 50 | public void setEnableRepositoryGroupLimits(boolean enableRepositoryGroupLimits) { 51 | this.enableRepositoryGroupLimits = enableRepositoryGroupLimits; 52 | } 53 | 54 | public Set getVendorCacheGroupIds() { 55 | return vendorCacheGroupIds; 56 | } 57 | 58 | public boolean isUseFrcMavenVendorCache() { 59 | return useFrcMavenVendorCache; 60 | } 61 | 62 | public void setUseFrcMavenVendorCache(boolean useFrcMavenVendorCache) { 63 | this.useFrcMavenVendorCache = useFrcMavenVendorCache; 64 | } 65 | 66 | public Project getProject() { 67 | return project; 68 | } 69 | 70 | public boolean isUseDevelopment() { 71 | return useDevelopment; 72 | } 73 | 74 | public boolean isUseLocal() { 75 | return useLocal; 76 | } 77 | 78 | public boolean isUseFrcMavenLocalDevelopment() { 79 | return useFrcMavenLocalDevelopment; 80 | } 81 | 82 | public boolean isUseFrcMavenLocalRelease() { 83 | return useFrcMavenLocalRelease; 84 | } 85 | 86 | public boolean isUseMavenCentral() { 87 | return useMavenCentral; 88 | } 89 | 90 | public void setUseDevelopment(boolean useDevelopment) { 91 | this.useDevelopment = useDevelopment; 92 | } 93 | 94 | public void setUseLocal(boolean useLocal) { 95 | this.useLocal = useLocal; 96 | } 97 | 98 | public void setUseFrcMavenLocalDevelopment(boolean useFrcMavenLocalDevelopment) { 99 | this.useFrcMavenLocalDevelopment = useFrcMavenLocalDevelopment; 100 | } 101 | 102 | public void setUseFrcMavenLocalRelease(boolean useFrcMavenLocalRelease) { 103 | this.useFrcMavenLocalRelease = useFrcMavenLocalRelease; 104 | } 105 | 106 | public void setUseMavenCentral(boolean useMavenCentral) { 107 | this.useMavenCentral = useMavenCentral; 108 | } 109 | 110 | // Mirror = source for WPILib artifacts 111 | // Repo = source for any artifacts 112 | 113 | // Repo should always take precedence over mirror in the case they want 114 | // to provide custom builds of WPILib artifacts. 115 | 116 | public WPIMavenRepo mirror(String name, final Action config) { 117 | WPIMavenRepo mirr = project.getObjects().newInstance(WPIMavenRepo.class, name); 118 | mirr.setPriority(WPIMavenRepo.PRIORITY_MIRROR); 119 | config.execute(mirr); 120 | this.add(mirr); 121 | return mirr; 122 | } 123 | 124 | public WPIMavenRepo repo(String name, final Action config) { 125 | WPIMavenRepo mirr = project.getObjects().newInstance(WPIMavenRepo.class, name); 126 | config.execute(mirr); 127 | this.add(mirr); 128 | return mirr; 129 | } 130 | 131 | public WPIMavenRepo vendor(String name, final Action config) { 132 | WPIMavenRepo mirr = project.getObjects().newInstance(WPIMavenRepo.class, name); 133 | mirr.setPriority(WPIMavenRepo.PRIORITY_VENDOR); 134 | config.execute(mirr); 135 | this.add(mirr); 136 | return mirr; 137 | } 138 | 139 | public void useMirror(String name) { 140 | this.all(m -> { 141 | if (m.getName().equals(name)) { 142 | m.setPriority(WPIMavenRepo.PRIORITY_MIRROR_INUSE); 143 | } 144 | }); 145 | } 146 | 147 | public void useRepo(String name) { 148 | this.all(m -> { 149 | if (m.getName().equals(name)) { 150 | m.setPriority(WPIMavenRepo.PRIORITY_REPO_INUSE); 151 | } 152 | }); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/wpi/WPIMavenRepo.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.wpi; 2 | 3 | import java.util.Set; 4 | 5 | import javax.inject.Inject; 6 | 7 | import org.gradle.api.Named; 8 | 9 | public class WPIMavenRepo implements Named { 10 | private String release = null; 11 | private String development = null; 12 | private int priority = PRIORITY_REPO; 13 | private Set allowedGroupIds; 14 | private Set allowedGroupIdsRegex; 15 | 16 | private String name; 17 | 18 | public static final int PRIORITY_REPO = 100; 19 | public static final int PRIORITY_REPO_INUSE = 50; 20 | 21 | public static final int PRIORITY_OFFICIAL = 150; 22 | public static final int PRIORITY_MIRROR = 250; 23 | public static final int PRIORITY_MIRROR_INUSE = 120; 24 | 25 | public static final int PRIORITY_WPILIB_VENDOR_CACHE = 200; 26 | public static final int PRIORITY_VENDOR = 225; 27 | 28 | @Inject 29 | public WPIMavenRepo(String name) { 30 | this.setName(name); 31 | } 32 | 33 | public Set getAllowedGroupIdsRegex() { 34 | return allowedGroupIdsRegex; 35 | } 36 | 37 | public void setAllowedGroupIdsRegex(Set allowedGroupIdsRegex) { 38 | this.allowedGroupIdsRegex = allowedGroupIdsRegex; 39 | } 40 | 41 | public Set getAllowedGroupIds() { 42 | return allowedGroupIds; 43 | } 44 | 45 | public void setAllowedGroupIds(Set allowedGroupIds) { 46 | this.allowedGroupIds = allowedGroupIds; 47 | } 48 | 49 | public String getRelease() { 50 | return release; 51 | } 52 | 53 | public void setRelease(String release) { 54 | this.release = release; 55 | } 56 | 57 | public String getDevelopment() { 58 | return development; 59 | } 60 | 61 | public void setDevelopment(String development) { 62 | this.development = development; 63 | } 64 | 65 | public int getPriority() { 66 | return priority; 67 | } 68 | 69 | public void setPriority(int priority) { 70 | this.priority = priority; 71 | } 72 | 73 | @Override 74 | public String getName() { 75 | return name; 76 | } 77 | 78 | public void setName(String name) { 79 | this.name = name; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/wpi/WPIVersionsExtension.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.wpi; 2 | 3 | import javax.inject.Inject; 4 | 5 | import org.gradle.api.provider.Property; 6 | 7 | public abstract class WPIVersionsExtension { 8 | 9 | private static final String wpilibVersion = "2026.0.0-alpha-1"; 10 | private static final String niLibrariesVersion = "2025.2.0"; 11 | private static final String opencvVersion = "4.10.0-3"; 12 | private static final String imguiVersion = "1.89.9-1"; 13 | private static final String ejmlVersion = "0.44.0"; 14 | private static final String jacksonVersion = "2.19.2"; 15 | private static final String quickbufVersion = "1.4"; 16 | private static final String wpimathVersion = "2026.0.0-alpha-1"; 17 | 18 | private static final String smartDashboardVersion = "2026.0.0-alpha-1"; 19 | private static final String shuffleboardVersion = "2026.0.0-alpha-1"; 20 | private static final String outlineViewerVersion = "2026.0.0-alpha-1"; 21 | private static final String robotBuilderVersion = "2026.0.0-alpha-1"; 22 | private static final String pathWeaverVersion = "2026.0.0-alpha-1"; 23 | private static final String glassVersion = "2026.0.0-alpha-1"; 24 | private static final String sysIdVersion = "2026.0.0-alpha-1"; 25 | private static final String roboRIOTeamNumberSetterVersion = "2026.0.0-alpha-1"; 26 | private static final String dataLogToolVersion = "2026.0.0-alpha-1"; 27 | private static final String wpicalToolVersion = "2026.0.0-alpha-1"; 28 | private static final String processstarterToolVersion = "2026.0.0-alpha-1"; 29 | 30 | 31 | public abstract Property getWpilibVersion(); 32 | public abstract Property getNiLibrariesVersion(); 33 | public abstract Property getOpencvVersion(); 34 | public abstract Property getImguiVersion(); 35 | public abstract Property getWpimathVersion(); 36 | 37 | public abstract Property getEjmlVersion(); 38 | public abstract Property getJacksonVersion(); 39 | public abstract Property getQuickbufVersion(); 40 | public abstract Property getSmartDashboardVersion(); 41 | public abstract Property getShuffleboardVersion(); 42 | public abstract Property getOutlineViewerVersion(); 43 | public abstract Property getRobotBuilderVersion(); 44 | public abstract Property getPathWeaverVersion(); 45 | public abstract Property getGlassVersion(); 46 | public abstract Property getSysIdVersion(); 47 | public abstract Property getRoboRIOTeamNumberSetterVersion(); 48 | public abstract Property getDataLogToolVersion(); 49 | public abstract Property getwpicalToolVersion(); 50 | public abstract Property getprocessstarterToolVersion(); 51 | 52 | @Inject 53 | public WPIVersionsExtension() { 54 | getWpilibVersion().convention(wpilibVersion); 55 | getNiLibrariesVersion().convention(niLibrariesVersion); 56 | getOpencvVersion().convention(opencvVersion); 57 | getImguiVersion().convention(imguiVersion); 58 | getWpimathVersion().convention(wpimathVersion); 59 | 60 | getEjmlVersion().convention(ejmlVersion); 61 | getJacksonVersion().convention(jacksonVersion); 62 | getQuickbufVersion().convention(quickbufVersion); 63 | getSmartDashboardVersion().convention(smartDashboardVersion); 64 | getShuffleboardVersion().convention(shuffleboardVersion); 65 | getOutlineViewerVersion().convention(outlineViewerVersion); 66 | getRobotBuilderVersion().convention(robotBuilderVersion); 67 | getPathWeaverVersion().convention(pathWeaverVersion); 68 | getGlassVersion().convention(glassVersion); 69 | getSysIdVersion().convention(sysIdVersion); 70 | getRoboRIOTeamNumberSetterVersion().convention(roboRIOTeamNumberSetterVersion); 71 | getDataLogToolVersion().convention(dataLogToolVersion); 72 | getwpicalToolVersion().convention(wpicalToolVersion); 73 | getprocessstarterToolVersion().convention(processstarterToolVersion); 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/wpi/cpp/WPINativeCompileRules.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.wpi.cpp; 2 | 3 | import java.io.File; 4 | import java.nio.file.Files; 5 | import java.util.Iterator; 6 | import java.util.stream.Stream; 7 | 8 | import org.gradle.api.Action; 9 | import org.gradle.api.Project; 10 | import org.gradle.api.Task; 11 | import org.gradle.api.logging.Logging; 12 | import org.gradle.api.plugins.ExtensionContainer; 13 | import org.gradle.language.base.internal.ProjectLayout; 14 | import org.gradle.language.nativeplatform.tasks.AbstractNativeSourceCompileTask; 15 | import org.gradle.model.ModelMap; 16 | import org.gradle.model.Mutate; 17 | import org.gradle.model.RuleSource; 18 | import org.gradle.model.Validate; 19 | import org.gradle.nativeplatform.BuildTypeContainer; 20 | import org.gradle.nativeplatform.NativeBinarySpec; 21 | import org.gradle.nativeplatform.TargetedNativeComponent; 22 | import org.gradle.nativeplatform.test.googletest.GoogleTestTestSuiteBinarySpec; 23 | import org.gradle.platform.base.BinaryContainer; 24 | import org.gradle.platform.base.BinarySpec; 25 | import org.gradle.platform.base.ComponentSpec; 26 | import org.gradle.platform.base.ComponentSpecContainer; 27 | import org.gradle.platform.base.internal.BinarySpecInternal; 28 | 29 | import edu.wpi.first.nativeutils.NativeUtilsExtension; 30 | import edu.wpi.first.toolchain.NativePlatforms; 31 | 32 | public class WPINativeCompileRules extends RuleSource { 33 | 34 | @Mutate 35 | public void addBuildTypes(BuildTypeContainer bts) { 36 | bts.maybeCreate("debug"); 37 | bts.maybeCreate("release"); 38 | } 39 | 40 | @Mutate 41 | public void addBinaryFlags(BinaryContainer binaries, ExtensionContainer extensions) { 42 | NativeUtilsExtension ntExt = extensions.getByType(NativeUtilsExtension.class); 43 | 44 | binaries.withType(NativeBinarySpec.class, bin -> { 45 | ntExt.usePlatformArguments(bin); 46 | }); 47 | } 48 | 49 | @Mutate 50 | public void addBinaryFlags(BinaryContainer binaries) { 51 | binaries.withType(GoogleTestTestSuiteBinarySpec.class, bin -> { 52 | if (!bin.getTargetPlatform().getName().equals(NativePlatforms.desktop)) { 53 | ((BinarySpecInternal) bin).setBuildable(false); 54 | } 55 | bin.getCppCompiler().define("RUNNING_FRC_TESTS"); 56 | bin.getcCompiler().define("RUNNING_FRC_TESTS"); 57 | }); 58 | } 59 | 60 | @Validate 61 | void setupCompilerWarningPrints(ModelMap tasks, ProjectLayout layout, ComponentSpecContainer components) { 62 | if (components == null) 63 | return; 64 | Project project = (Project) layout.getProjectIdentifier(); 65 | 66 | for (ComponentSpec c : components) { 67 | if (c instanceof TargetedNativeComponent) { 68 | for (BinarySpec bin : ((TargetedNativeComponent) c).getBinaries()) { 69 | bin.getTasks().withType(AbstractNativeSourceCompileTask.class, t -> { 70 | t.doLast(new Action() { 71 | @Override 72 | public void execute(Task arg0) { 73 | printWarningsForBinTask(t.getName().toString(), project); 74 | } 75 | }); 76 | }); 77 | } 78 | } 79 | } 80 | } 81 | 82 | // From 83 | // https://github.com/wpilibsuite/native-utils/blob/a8ea595670716c7b898878a37e36c2b52e8e3f42/src/main/groovy/edu/wpi/first/nativeutils/rules/BuildConfigRules.groovy#L450 84 | private static void printWarningsForBinTask(String taskName, Project project) { 85 | File file = project.getLayout().getBuildDirectory().get().file("/tmp/" + taskName + "/output.txt").getAsFile(); 86 | 87 | if (!file.exists()) 88 | return; 89 | 90 | String currentFile = ""; 91 | boolean hasFirstLine = false; 92 | boolean hasPrintedFileName = false; 93 | 94 | try (Stream rawIterator = Files.lines(file.toPath())) { 95 | Iterable fileIterator = new Iterable() { 96 | 97 | @Override 98 | public Iterator iterator() { 99 | return rawIterator.iterator(); 100 | } 101 | 102 | }; 103 | 104 | for (String line : fileIterator) { 105 | if (!hasFirstLine) { 106 | hasFirstLine = true; 107 | } else if (line.startsWith("compiling ")) { 108 | currentFile = line.substring(10, line.indexOf("successful.")); 109 | hasPrintedFileName = false; 110 | } else if (line.contains("Finished") && line.contains("see full log")) { 111 | // No op 112 | } else if (line.trim().equals(currentFile.trim())) { 113 | // No op 114 | } else if (!line.isEmpty()) { 115 | if (!hasPrintedFileName) { 116 | hasPrintedFileName = true; 117 | System.out.println("Warnings in file " + currentFile + "...."); 118 | } 119 | System.out.println(line); 120 | } 121 | } 122 | } catch (Exception e) { 123 | Logging.getLogger(WPINativeCompileRules.class).warn( 124 | "Failed to print warnings file. You might need to manually open it to find any compile warnings", 125 | e); 126 | } 127 | 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/wpi/cpp/WPINativeDepsExtension.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.wpi.cpp; 2 | 3 | import javax.inject.Inject; 4 | 5 | import org.gradle.nativeplatform.NativeBinarySpec; 6 | import org.gradle.platform.base.VariantComponentSpec; 7 | 8 | import edu.wpi.first.nativeutils.NativeUtilsExtension; 9 | 10 | public class WPINativeDepsExtension { 11 | private final NativeUtilsExtension nte; 12 | 13 | @Inject 14 | public WPINativeDepsExtension(NativeUtilsExtension nte) { 15 | this.nte = nte; 16 | } 17 | 18 | public void useLibrary(VariantComponentSpec component, String... libraries) { 19 | useRequiredLibrary(component, libraries); 20 | } 21 | 22 | public void useLibrary(NativeBinarySpec binary, String... libraries) { 23 | useRequiredLibrary(binary, libraries); 24 | } 25 | 26 | public void useRequiredLibrary(VariantComponentSpec component, String... libraries) { 27 | nte.useRequiredLibrary(component, libraries); 28 | } 29 | 30 | public void useRequiredLibrary(NativeBinarySpec binary, String... libraries) { 31 | nte.useRequiredLibrary(binary, libraries); 32 | } 33 | 34 | public void useOptionalLibrary(VariantComponentSpec component, String... libraries) { 35 | nte.useOptionalLibrary(component, libraries); 36 | } 37 | 38 | public void useOptionalLibrary(NativeBinarySpec binary, String... libraries) { 39 | nte.useOptionalLibrary(binary, libraries); 40 | } 41 | 42 | void wpilib(VariantComponentSpec component) { 43 | useLibrary(component, "wpilib_executable_shared", "vision_shared"); 44 | } 45 | 46 | void wpilib(NativeBinarySpec binary) { 47 | useLibrary(binary, "wpilib_executable_shared", "vision_shared"); 48 | } 49 | 50 | void wpilibStatic(VariantComponentSpec component) { 51 | useLibrary(component, "wpilib_executable_static", "vision_static"); 52 | } 53 | 54 | void wpilibStatic(NativeBinarySpec binary) { 55 | useLibrary(binary, "wpilib_executable_static", "vision_static"); 56 | } 57 | 58 | void googleTest(VariantComponentSpec component) { 59 | useLibrary(component, "googletest_static"); 60 | } 61 | 62 | void googleTest(NativeBinarySpec binary) { 63 | useLibrary(binary, "googletest_static"); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/wpi/dependencies/WPIDependenciesPlugin.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.wpi.dependencies; 2 | 3 | import java.io.File; 4 | import java.util.ArrayList; 5 | import java.util.HashSet; 6 | import java.util.List; 7 | import java.util.Set; 8 | 9 | import org.gradle.api.Plugin; 10 | import org.gradle.api.Project; 11 | import org.gradle.api.artifacts.Configuration; 12 | import org.gradle.api.provider.Provider; 13 | import org.gradle.api.tasks.TaskProvider; 14 | import org.gradle.jvm.tasks.Jar; 15 | 16 | import edu.wpi.first.deployutils.deploy.DeployExtension; 17 | import edu.wpi.first.deployutils.deploy.artifact.Artifact; 18 | import edu.wpi.first.deployutils.deploy.target.RemoteTarget; 19 | import edu.wpi.first.deployutils.log.ETLogger; 20 | import edu.wpi.first.deployutils.log.ETLoggerFactory; 21 | import edu.wpi.first.gradlerio.PreemptiveDownloadTask; 22 | import edu.wpi.first.gradlerio.deploy.roborio.FRCJREArtifact; 23 | import edu.wpi.first.gradlerio.deploy.roborio.FRCJavaArtifact; 24 | 25 | public class WPIDependenciesPlugin implements Plugin { 26 | 27 | private Project project; 28 | 29 | @Override 30 | public void apply(Project project) { 31 | this.project = project; 32 | TaskProvider lazyPreempt = project.getTasks().register("downloadDepsPreemptively", PreemptiveDownloadTask.class); 33 | 34 | lazyPreempt.configure(x -> { 35 | Provider> filesProvider = project.provider(() -> { 36 | return getFiles(); 37 | }); 38 | x.getFiles().from(filesProvider); 39 | }); 40 | 41 | project.getTasks().withType(Jar.class, jarTask -> { 42 | jarTask.dependsOn(lazyPreempt); 43 | }); 44 | } 45 | 46 | private Set getFiles() { 47 | ETLogger logger = ETLoggerFactory.INSTANCE.create("DownloadAll"); 48 | 49 | Set files = new HashSet<>(); 50 | 51 | List configs = new ArrayList<>(); 52 | 53 | for (RemoteTarget target : project.getExtensions().getByType(DeployExtension.class).getTargets()) { 54 | for (Artifact artifact : target.getArtifacts()) { 55 | if (artifact instanceof FRCJREArtifact) { 56 | Configuration cfg = ((FRCJREArtifact)artifact).getConfiguration().get(); 57 | logger.info("Found JRE Configuration: " + cfg.getName()); 58 | configs.add(cfg); 59 | } else if (artifact instanceof FRCJavaArtifact) { 60 | Configuration cfg = ((FRCJavaArtifact)artifact).getNativeZipArtifact().getConfiguration().get(); 61 | logger.info("Found Java Configuration: " + cfg.getName()); 62 | configs.add(cfg); 63 | } 64 | } 65 | } 66 | 67 | for (Configuration cfg : configs) { 68 | if (cfg.isCanBeResolved()) { 69 | logger.info("Resolving Deps Configuration: " + cfg.getName()); 70 | files.addAll(cfg.getIncoming().getFiles().getFiles()); 71 | } else { 72 | logger.info("Can't resolve: " + cfg.getName()); 73 | } 74 | } 75 | 76 | return files; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/wpi/dependencies/tools/CppToolRunTask.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.wpi.dependencies.tools; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | 6 | import javax.inject.Inject; 7 | 8 | import org.codehaus.groovy.runtime.IOGroovyMethods; 9 | import org.gradle.api.DefaultTask; 10 | import org.gradle.api.file.Directory; 11 | import org.gradle.api.file.DirectoryProperty; 12 | import org.gradle.api.model.ObjectFactory; 13 | import org.gradle.api.provider.Property; 14 | import org.gradle.api.provider.Provider; 15 | import org.gradle.api.tasks.Internal; 16 | import org.gradle.api.tasks.TaskAction; 17 | import org.gradle.internal.os.OperatingSystem; 18 | import org.gradle.process.ExecOperations; 19 | 20 | import edu.wpi.first.gradlerio.SingletonTask; 21 | 22 | public class CppToolRunTask extends DefaultTask implements SingletonTask { 23 | private final Property toolName; 24 | private final DirectoryProperty toolsFolder; 25 | private final ExecOperations operations; 26 | 27 | @Internal 28 | public Property getToolName() { 29 | return toolName; 30 | } 31 | 32 | @Internal 33 | public DirectoryProperty getToolsFolder() { 34 | return toolsFolder; 35 | } 36 | 37 | @Inject 38 | public CppToolRunTask(ObjectFactory objects, ExecOperations execOperations) { 39 | setGroup("GradleRIO"); 40 | 41 | this.toolName = objects.property(String.class); 42 | toolsFolder = objects.directoryProperty(); 43 | operations = execOperations; 44 | } 45 | 46 | @TaskAction 47 | public void runTool() { 48 | boolean isWindows = OperatingSystem.current().isWindows(); 49 | if (isWindows) { 50 | runToolWindows(); 51 | } else { 52 | runToolUnix(); 53 | } 54 | } 55 | 56 | private String getArgumentPath(String toolNameLower) { 57 | return new File(getProject().getProjectDir(), "." + toolNameLower).getAbsolutePath(); 58 | } 59 | 60 | private void runToolWindows() { 61 | Directory toolsFolder = this.toolsFolder.get(); 62 | String toolName = this.toolName.get(); 63 | File outputFile = toolsFolder.file(toolName + ".exe").getAsFile(); 64 | ProcessBuilder builder = new ProcessBuilder(outputFile.getAbsolutePath(), 65 | getArgumentPath(toolName.toLowerCase())); 66 | try { 67 | Process proc = builder.start(); 68 | int result = proc.waitFor(); 69 | if (result != 0) { 70 | String stdOut = IOGroovyMethods.getText(proc.getInputStream()); 71 | String stdErr = IOGroovyMethods.getText(proc.getErrorStream()); 72 | throw new ToolRunException(stdOut, stdErr); 73 | } 74 | } catch (IOException | InterruptedException e) { 75 | throw new RuntimeException(e); 76 | } 77 | 78 | } 79 | 80 | private void runToolUnix() { 81 | Directory toolsFolder = this.toolsFolder.get(); 82 | String toolName = this.toolName.get(); 83 | File outputFile = toolsFolder.file(toolName).getAsFile(); 84 | operations.exec(spec -> { 85 | spec.setExecutable(outputFile.getAbsolutePath()); 86 | spec.args(getArgumentPath(toolName.toLowerCase())); 87 | }); 88 | } 89 | 90 | @Override 91 | @Internal 92 | public Provider getSingletonName() { 93 | return toolName; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/wpi/dependencies/tools/ToolRunException.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.wpi.dependencies.tools; 2 | 3 | public class ToolRunException extends RuntimeException { 4 | private static final long serialVersionUID = 8095609598836161230L; 5 | 6 | public ToolRunException(String stdOut, String stdErr) { 7 | super("Tool failed to start:\n\nOutput: " + stdOut + "\n\nError: " + stdErr); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/wpi/dependencies/tools/ToolRunTask.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.wpi.dependencies.tools; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | 6 | import javax.inject.Inject; 7 | 8 | import org.codehaus.groovy.runtime.IOGroovyMethods; 9 | import org.gradle.api.DefaultTask; 10 | import org.gradle.api.file.Directory; 11 | import org.gradle.api.file.DirectoryProperty; 12 | import org.gradle.api.model.ObjectFactory; 13 | import org.gradle.api.provider.Property; 14 | import org.gradle.api.provider.Provider; 15 | import org.gradle.api.tasks.Internal; 16 | import org.gradle.api.tasks.TaskAction; 17 | import org.gradle.internal.os.OperatingSystem; 18 | import org.gradle.process.ExecOperations; 19 | 20 | import edu.wpi.first.gradlerio.SingletonTask; 21 | import groovy.transform.CompileStatic; 22 | 23 | @CompileStatic 24 | public class ToolRunTask extends DefaultTask implements SingletonTask { 25 | 26 | private final Property toolName; 27 | private final DirectoryProperty toolsFolder; 28 | private final ExecOperations operations; 29 | 30 | @Internal 31 | public Property getToolName() { 32 | return toolName; 33 | } 34 | 35 | @Internal 36 | public DirectoryProperty getToolsFolder() { 37 | return toolsFolder; 38 | } 39 | 40 | @Inject 41 | public ToolRunTask(ObjectFactory objects, ExecOperations execOperations) { 42 | setGroup("GradleRIO"); 43 | 44 | toolName = objects.property(String.class); 45 | toolsFolder = objects.directoryProperty(); 46 | operations = execOperations; 47 | } 48 | 49 | @TaskAction 50 | public void runTool() { 51 | boolean isWindows = OperatingSystem.current().isWindows(); 52 | if (isWindows) { 53 | runToolWindows(); 54 | } else { 55 | runToolUnix(); 56 | } 57 | } 58 | 59 | private void runToolWindows() { 60 | Directory toolsFolder = this.toolsFolder.get(); 61 | String toolName = this.toolName.get(); 62 | File outputFile = toolsFolder.file(toolName + ".exe").getAsFile(); 63 | ProcessBuilder builder = new ProcessBuilder(outputFile.getAbsolutePath()); 64 | Process proc; 65 | try { 66 | proc = builder.start(); 67 | int result = proc.waitFor(); 68 | if (result != 0) { 69 | String stdOut = IOGroovyMethods.getText(proc.getInputStream()); 70 | String stdErr = IOGroovyMethods.getText(proc.getErrorStream()); 71 | throw new ToolRunException(stdOut, stdErr); 72 | } 73 | } catch (IOException | InterruptedException e) { 74 | throw new RuntimeException(e); 75 | } 76 | 77 | } 78 | 79 | private void runToolUnix() { 80 | Directory toolsFolder = this.toolsFolder.get(); 81 | String toolName = this.toolName.get(); 82 | File outputFile = toolsFolder.file(toolName).getAsFile(); 83 | operations.exec(spec -> { 84 | spec.setExecutable(outputFile.getAbsolutePath()); 85 | }); 86 | } 87 | 88 | @Override 89 | @Internal 90 | public Provider getSingletonName() { 91 | return toolName; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/wpi/dependencies/tools/WPICppTool.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.wpi.dependencies.tools; 2 | 3 | import org.gradle.api.Named; 4 | import org.gradle.api.Project; 5 | import org.gradle.api.file.Directory; 6 | import org.gradle.api.provider.Provider; 7 | import org.gradle.api.tasks.TaskProvider; 8 | 9 | import edu.wpi.first.gradlerio.wpi.WPIExtension; 10 | 11 | public class WPICppTool implements Named { 12 | //TaskProvider toolInstallTask 13 | private final TaskProvider toolRunTask; 14 | 15 | private final String name; 16 | 17 | private final Provider version; 18 | 19 | public WPICppTool(Project project, String name, Provider version, String artifactId, Provider toolsFolder) { 20 | //Configuration config = project.getConfigurations().getByName("wpiCppTools"); 21 | String toolsClassifier = project.getExtensions().getByType(WPIExtension.class).getCppToolsClassifier(); 22 | Provider fullId = project.getProviders().provider(() -> { 23 | String id = artifactId; 24 | id += ":" + version.get(); 25 | id += ":" + toolsClassifier + "@zip"; 26 | return id; 27 | }); 28 | project.getDependencies().add("wpiCppTools", fullId); 29 | //toolInstallTask = project.tasks.register("${name}Install".toString(), CppToolInstallTask, name, config, dependency) 30 | toolRunTask = project.getTasks().register(name, CppToolRunTask.class); 31 | toolRunTask.configure(t -> { 32 | t.setDescription("Run the tool " + name); 33 | t.getToolsFolder().set(toolsFolder); 34 | t.getToolName().set(name); 35 | }); 36 | this.name = name; 37 | this.version = version; 38 | } 39 | 40 | public TaskProvider getToolRunTask() { 41 | return toolRunTask; 42 | } 43 | 44 | public String getName() { 45 | return name; 46 | } 47 | 48 | public Provider getVersion() { 49 | return version; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/wpi/dependencies/tools/WPITool.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.wpi.dependencies.tools; 2 | 3 | import edu.wpi.first.gradlerio.wpi.WPIExtension; 4 | 5 | import org.gradle.api.Named; 6 | import org.gradle.api.Project; 7 | import org.gradle.api.artifacts.Configuration; 8 | import org.gradle.api.file.Directory; 9 | import org.gradle.api.provider.Provider; 10 | import org.gradle.api.tasks.TaskProvider; 11 | 12 | public class WPITool implements Named { 13 | private final TaskProvider toolInstallTask; 14 | private final TaskProvider toolRunTask; 15 | 16 | private final String name; 17 | 18 | private final Provider version; 19 | 20 | public WPITool(Project project, String name, Provider version, String groupId, String artifactName, boolean platformJars, Provider toolsFolder) { 21 | Configuration config = project.getConfigurations().getByName("wpiTools"); 22 | String toolsClassifier = project.getExtensions().getByType(WPIExtension.class).getToolsClassifier(); 23 | Provider fullId = project.getProviders().provider(() -> { 24 | String id = groupId + ":" + artifactName; 25 | id += ":" + version.get(); 26 | if (platformJars) { 27 | id += ":" + toolsClassifier; 28 | } 29 | return id; 30 | }); 31 | project.getDependencies().add("wpiTools", fullId); 32 | toolInstallTask = project.getTasks().register(name + "Install".toString(), ToolInstallTask.class); 33 | toolInstallTask.configure(t -> { 34 | t.setDescription("Install the tool " + name); 35 | t.getToolsFolder().set(toolsFolder); 36 | t.getConfiguration().set(config); 37 | t.getToolName().set(name); 38 | t.getArtifactName().set(artifactName); 39 | }); 40 | toolRunTask = project.getTasks().register(name, ToolRunTask.class); 41 | toolRunTask.configure(t -> { 42 | t.dependsOn(toolInstallTask); 43 | t.setDescription("Run the tool " + name); 44 | t.getToolsFolder().set(toolsFolder); 45 | t.getToolName().set(name); 46 | }); 47 | this.name = name; 48 | this.version = version; 49 | } 50 | 51 | public TaskProvider getToolInstallTask() { 52 | return toolInstallTask; 53 | } 54 | 55 | public TaskProvider getToolRunTask() { 56 | return toolRunTask; 57 | } 58 | 59 | @Override 60 | public String getName() { 61 | return name; 62 | } 63 | 64 | public Provider getVersion() { 65 | return version; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/wpi/dependencies/tools/WPIToolsPlugin.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.wpi.dependencies.tools; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.gradle.api.Plugin; 7 | import org.gradle.api.Project; 8 | import org.gradle.api.file.Directory; 9 | import org.gradle.api.provider.Provider; 10 | 11 | import edu.wpi.first.gradlerio.wpi.WPIExtension; 12 | 13 | public class WPIToolsPlugin implements Plugin { 14 | 15 | @Override 16 | public void apply(Project project) { 17 | project.getConfigurations().maybeCreate("wpiTools"); 18 | project.getConfigurations().maybeCreate("wpiCppTools"); 19 | 20 | WPIExtension wpi = project.getExtensions().getByType(WPIExtension.class); 21 | List tools = new ArrayList<>(); 22 | List cppTools = new ArrayList<>(); 23 | 24 | Provider frcHome = wpi.getFrcHome(); 25 | Provider toolsFolder = project.provider(() -> frcHome.get().dir("tools")); 26 | 27 | String toolsClassifier = project.getExtensions().getByType(WPIExtension.class).getToolsClassifier(); 28 | if (!toolsClassifier.equals("macarm64") && !toolsClassifier.equals("linuxarm64")) { 29 | tools.add(new WPITool(project, "SmartDashboard", wpi.getVersions().getSmartDashboardVersion(), 30 | "edu.wpi.first.tools", "SmartDashboard", true, toolsFolder)); 31 | } 32 | 33 | tools.add(new WPITool(project, "ShuffleBoard", wpi.getVersions().getShuffleboardVersion(), 34 | "edu.wpi.first.tools", "Shuffleboard", true, toolsFolder)); 35 | tools.add(new WPITool(project, "RobotBuilder", wpi.getVersions().getRobotBuilderVersion(), 36 | "edu.wpi.first.tools", "RobotBuilder", false, toolsFolder)); 37 | tools.add(new WPITool(project, "PathWeaver", wpi.getVersions().getPathWeaverVersion(), "edu.wpi.first.tools", 38 | "PathWeaver", true, toolsFolder)); 39 | 40 | cppTools.add(new WPICppTool(project, "OutlineViewer", wpi.getVersions().getOutlineViewerVersion(), 41 | "edu.wpi.first.tools:OutlineViewer", toolsFolder)); 42 | cppTools.add( 43 | new WPICppTool(project, "Glass", wpi.getVersions().getGlassVersion(), "edu.wpi.first.tools:Glass", 44 | toolsFolder)); 45 | cppTools.add( 46 | new WPICppTool(project, "SysId", wpi.getVersions().getSysIdVersion(), "edu.wpi.first.tools:SysId", 47 | toolsFolder)); 48 | cppTools.add(new WPICppTool(project, "roboRIOTeamNumberSetter", 49 | wpi.getVersions().getRoboRIOTeamNumberSetterVersion(), "edu.wpi.first.tools:roboRIOTeamNumberSetter", 50 | toolsFolder)); 51 | cppTools.add(new WPICppTool(project, "DataLogTool", wpi.getVersions().getDataLogToolVersion(), 52 | "edu.wpi.first.tools:DataLogTool", toolsFolder)); 53 | 54 | if (!toolsClassifier.equals("linuxarm64")) { 55 | cppTools.add(new WPICppTool(project, "wpical", wpi.getVersions().getwpicalToolVersion(), 56 | "edu.wpi.first.tools:wpical", toolsFolder)); 57 | } 58 | 59 | cppTools.add(new WPICppTool(project, "processstarter", wpi.getVersions().getprocessstarterToolVersion(), 60 | "edu.wpi.first.tools:processstarter", toolsFolder)); 61 | 62 | project.getTasks().register("InstallAllTools", task -> { 63 | task.setGroup("GradleRIO"); 64 | task.setDescription("Install All Tools"); 65 | 66 | for (WPITool tool : tools) { 67 | task.dependsOn(tool.getToolInstallTask()); 68 | } 69 | }); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/wpi/java/ExtractNativeJavaArtifacts.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.wpi.java; 2 | 3 | import javax.inject.Inject; 4 | 5 | import org.gradle.api.DefaultTask; 6 | import org.gradle.api.file.ConfigurableFileCollection; 7 | import org.gradle.api.file.DirectoryProperty; 8 | import org.gradle.api.file.FileSystemOperations; 9 | import org.gradle.api.tasks.InputFiles; 10 | import org.gradle.api.tasks.OutputDirectory; 11 | import org.gradle.api.tasks.TaskAction; 12 | import org.gradle.workers.WorkAction; 13 | import org.gradle.workers.WorkParameters; 14 | import org.gradle.workers.WorkQueue; 15 | import org.gradle.workers.WorkerExecutor; 16 | 17 | public abstract class ExtractNativeJavaArtifacts extends DefaultTask { 18 | 19 | public static interface ExtractParameters extends WorkParameters { 20 | ConfigurableFileCollection getFiles(); 21 | DirectoryProperty getDestinationDirectory(); 22 | } 23 | 24 | public static abstract class ExtractFiles implements WorkAction { 25 | @Inject 26 | public abstract FileSystemOperations getFileSystemOperations(); 27 | 28 | @Override 29 | public void execute() { 30 | ExtractParameters parameters = getParameters(); 31 | getFileSystemOperations().sync(copySpec -> { 32 | copySpec.into(parameters.getDestinationDirectory()); 33 | copySpec.from(parameters.getFiles()); 34 | }); 35 | } 36 | } 37 | 38 | @InputFiles 39 | public abstract ConfigurableFileCollection getFiles(); 40 | 41 | @OutputDirectory 42 | public abstract DirectoryProperty getDestinationDirectory(); 43 | 44 | @Inject 45 | public abstract WorkerExecutor getWorkerExecuter(); 46 | 47 | @TaskAction 48 | public void execute() { 49 | WorkQueue workQueue = getWorkerExecuter().noIsolation(); 50 | 51 | workQueue.submit(ExtractFiles.class, parameters -> { 52 | parameters.getFiles().setFrom(getFiles()); 53 | parameters.getDestinationDirectory().set(getDestinationDirectory()); 54 | }); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/wpi/java/TestTaskDoFirstAction.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.wpi.java; 2 | 3 | import java.io.File; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | import org.gradle.api.Action; 8 | import org.gradle.api.Task; 9 | import org.gradle.api.file.DirectoryProperty; 10 | import org.gradle.api.provider.Provider; 11 | import org.gradle.internal.os.OperatingSystem; 12 | import org.gradle.process.JavaForkOptions; 13 | 14 | public class TestTaskDoFirstAction implements Action { 15 | 16 | private final JavaForkOptions t; 17 | private final Provider extract; 18 | 19 | public TestTaskDoFirstAction(JavaForkOptions t, Provider extract) { 20 | this.t = t; 21 | this.extract = extract; 22 | } 23 | 24 | @Override 25 | public void execute(Task arg0) { 26 | Map env = new HashMap<>(); 27 | 28 | String ldpath = extract.get().get().getAsFile().getAbsolutePath(); 29 | 30 | if (OperatingSystem.current().isUnix()) { 31 | env.put("LD_LIBRARY_PATH", ldpath); 32 | env.put("DYLD_FALLBACK_LIBRARY_PATH", ldpath); 33 | env.put("DYLD_LIBRARY_PATH", ldpath); 34 | } else if (OperatingSystem.current().isWindows()) { 35 | env.put("PATH", System.getenv("PATH") + File.pathSeparator + ldpath); 36 | } 37 | 38 | t.environment(env); 39 | 40 | String jlp = ldpath; 41 | 42 | if (t.getSystemProperties().containsKey("java.library.path")) { 43 | jlp = (String) t.getSystemProperties().get("java.library.path") + File.pathSeparator + ldpath; 44 | } 45 | t.getSystemProperties().put("java.library.path", jlp); 46 | 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/wpi/java/WPIJavaDepsExtension.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.wpi.java; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import javax.inject.Inject; 7 | 8 | import org.gradle.api.provider.Provider; 9 | import org.gradle.api.provider.ProviderFactory; 10 | 11 | import edu.wpi.first.gradlerio.wpi.WPIVersionsExtension; 12 | 13 | public class WPIJavaDepsExtension { 14 | 15 | private final ProviderFactory providers; 16 | 17 | private static String dependencyNotation(String groupId, String artifactId, Provider version) { 18 | return groupId + ":" + artifactId + ":" + version.get(); 19 | } 20 | 21 | private void createJavaDependencies(String groupdId, String artifactId, Provider version) { 22 | wpilibDeps.add(providers.provider(() -> { 23 | return dependencyNotation(groupdId, artifactId, version); 24 | })); 25 | 26 | wpilibSources.add(providers.provider(() -> { 27 | return dependencyNotation(groupdId, artifactId, version) + ":sources"; 28 | })); 29 | } 30 | 31 | private Provider createJniDependency(String groupdId, String artifactId, Provider version, boolean debug, String platform) { 32 | String debugString = debug ? "debug" : ""; 33 | return providers.provider(() -> { 34 | return dependencyNotation(groupdId, artifactId, version) + ":" + platform + debugString + "@zip"; 35 | }); 36 | } 37 | 38 | private final List> wpilibDeps = new ArrayList<>(); 39 | private final List> wpilibSources = new ArrayList<>(); 40 | private final WPIVersionsExtension versions; 41 | 42 | @Inject 43 | public WPIJavaDepsExtension(WPIVersionsExtension versions, ProviderFactory providers) { 44 | this.providers = providers; 45 | this.versions = versions; 46 | 47 | createJavaDependencies("edu.wpi.first.wpilibj", "wpilibj-java", versions.getWpilibVersion()); 48 | createJavaDependencies("edu.wpi.first.wpimath", "wpimath-java", versions.getWpimathVersion()); 49 | createJavaDependencies("edu.wpi.first.ntcore", "ntcore-java", versions.getWpilibVersion()); 50 | createJavaDependencies("edu.wpi.first.cscore", "cscore-java", versions.getWpilibVersion()); 51 | createJavaDependencies("edu.wpi.first.cameraserver", "cameraserver-java", versions.getWpilibVersion()); 52 | createJavaDependencies("edu.wpi.first.hal", "hal-java", versions.getWpilibVersion()); 53 | createJavaDependencies("edu.wpi.first.wpinet", "wpinet-java", versions.getWpilibVersion()); 54 | createJavaDependencies("edu.wpi.first.wpiutil", "wpiutil-java", versions.getWpilibVersion()); 55 | createJavaDependencies("edu.wpi.first.apriltag", "apriltag-java", versions.getWpilibVersion()); 56 | createJavaDependencies("edu.wpi.first.wpiunits", "wpiunits-java", versions.getWpilibVersion()); 57 | createJavaDependencies("edu.wpi.first.epilogue", "epilogue-runtime-java", versions.getWpilibVersion()); 58 | 59 | createJavaDependencies("edu.wpi.first.thirdparty.frc2025.opencv", "opencv-java", versions.getOpencvVersion()); 60 | createJavaDependencies("org.ejml", "ejml-simple", versions.getEjmlVersion()); 61 | 62 | createJavaDependencies("com.fasterxml.jackson.core", "jackson-annotations", versions.getJacksonVersion()); 63 | createJavaDependencies("com.fasterxml.jackson.core", "jackson-core", versions.getJacksonVersion()); 64 | createJavaDependencies("com.fasterxml.jackson.core", "jackson-databind", versions.getJacksonVersion()); 65 | 66 | createJavaDependencies("us.hebi.quickbuf", "quickbuf-runtime", versions.getQuickbufVersion()); 67 | } 68 | 69 | public List> wpilib() { 70 | return wpilibDeps; 71 | } 72 | 73 | /** Dependencies required for using WPILib's Java annotations during compilation. */ 74 | public List> wpilibAnnotations() { 75 | // epilogue-runtime is a dependency of epilogue-processor, and needs to be on the annotation processor 76 | // classpath at compile time for the processor to function 77 | return List.of( 78 | providers.provider(() -> dependencyNotation("edu.wpi.first.epilogue", "epilogue-processor-java", versions.getWpilibVersion())), 79 | providers.provider(() -> dependencyNotation("edu.wpi.first.epilogue", "epilogue-runtime-java", versions.getWpilibVersion())) 80 | ); 81 | } 82 | 83 | public List> wpilibSources() { 84 | return wpilibSources; 85 | } 86 | 87 | public List> wpilibJniDebug(String platform) { 88 | return getWpilibJniInternal(true, platform); 89 | } 90 | 91 | public List> wpilibJniRelease(String platform) { 92 | return getWpilibJniInternal(false, platform); 93 | } 94 | 95 | private List> getWpilibJniInternal(boolean debug, String platform) { 96 | return List.of( 97 | createJniDependency("edu.wpi.first.hal", "hal-cpp", versions.getWpilibVersion(), debug, platform), 98 | createJniDependency("edu.wpi.first.wpimath", "wpimath-cpp", versions.getWpimathVersion(), debug, platform), 99 | createJniDependency("edu.wpi.first.ntcore", "ntcore-cpp", versions.getWpilibVersion(), debug, platform), 100 | createJniDependency("edu.wpi.first.cscore", "cscore-cpp", versions.getWpilibVersion(), debug, platform), 101 | createJniDependency("edu.wpi.first.thirdparty.frc2025.opencv", "opencv-cpp", versions.getOpencvVersion(), debug, platform), 102 | createJniDependency("edu.wpi.first.wpinet", "wpinet-cpp", versions.getWpilibVersion(), debug, platform), 103 | createJniDependency("edu.wpi.first.wpiutil", "wpiutil-cpp", versions.getWpilibVersion(), debug, platform), 104 | createJniDependency("edu.wpi.first.apriltag", "apriltag-cpp", versions.getWpilibVersion(), debug, platform) 105 | ); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/wpi/simulation/HalSimExtension.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.wpi.simulation; 2 | 3 | import java.io.File; 4 | import java.util.Optional; 5 | 6 | import javax.inject.Inject; 7 | import org.gradle.api.Named; 8 | import org.gradle.api.Project; 9 | import org.gradle.api.artifacts.ArtifactView; 10 | import org.gradle.api.artifacts.result.ResolvedArtifactResult; 11 | import org.gradle.api.file.FileCollection; 12 | import org.gradle.api.model.ObjectFactory; 13 | import org.gradle.api.provider.Property; 14 | import org.gradle.api.provider.Provider; 15 | 16 | public class HalSimExtension implements Named { 17 | private final String name; 18 | private final Property groupId; 19 | private final Property artifactId; 20 | private final Property version; 21 | private final Property defaultEnabled; 22 | 23 | @Override 24 | public String getName() { 25 | return name; 26 | } 27 | 28 | public Property getGroupId() { 29 | return groupId; 30 | } 31 | 32 | public Property getArtifactId() { 33 | return artifactId; 34 | } 35 | 36 | public Property getVersion() { 37 | return version; 38 | } 39 | 40 | public Property getDefaultEnabled() { 41 | return defaultEnabled; 42 | } 43 | 44 | public Provider getReleaseDependency(Project project, SimulationExtension sim) { 45 | return project.getProviders().provider(() -> getIdString() + ":" + sim.desktopPlatform + "@zip"); 46 | } 47 | 48 | public Provider getDebugDependency(Project project, SimulationExtension sim) { 49 | return project.getProviders().provider(() -> getIdString() + ":" + sim.desktopPlatform + "debug@zip"); 50 | } 51 | 52 | private String getIdString() { 53 | return getGroupId().get() + ":" + getArtifactId().get() + ":" + getVersion().get(); 54 | } 55 | 56 | public Optional getFilenameForArtifact(ArtifactView view, FileCollection files) { 57 | // Find artifact matching 58 | String idString = getGroupId().get() + ":" + getArtifactId().get() + ":"; 59 | for (ResolvedArtifactResult artifact : view.getArtifacts()) { 60 | if (artifact.getId().getComponentIdentifier().getDisplayName().startsWith(idString)) { 61 | // Found artifact, now find binary 62 | for (File file : files) { 63 | if (file.getAbsolutePath().startsWith(artifact.getFile().getAbsolutePath())) { 64 | return Optional.of(file.getName()); 65 | } 66 | } 67 | } 68 | } 69 | return Optional.empty(); 70 | } 71 | 72 | @Inject 73 | public HalSimExtension(String name, ObjectFactory objects) { 74 | this.name = name; 75 | groupId = objects.property(String.class); 76 | artifactId = objects.property(String.class); 77 | version = objects.property(String.class); 78 | defaultEnabled = objects.property(Boolean.class); 79 | defaultEnabled.set(false); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/edu/wpi/first/gradlerio/wpi/simulation/SimulationDependencySet.java: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.wpi.simulation; 2 | 3 | import javax.inject.Inject; 4 | 5 | import org.gradle.api.file.FileCollection; 6 | import org.gradle.api.file.ProjectLayout; 7 | import org.gradle.nativeplatform.NativeDependencySet; 8 | 9 | public class SimulationDependencySet implements NativeDependencySet { 10 | 11 | private final FileCollection emptyCollection; 12 | private final FileCollection runtimeFiles; 13 | 14 | @Inject 15 | public SimulationDependencySet(ProjectLayout layout, FileCollection runtimeFiles) { 16 | emptyCollection = layout.files(); 17 | 18 | this.runtimeFiles = runtimeFiles; 19 | } 20 | 21 | @Override 22 | public FileCollection getIncludeRoots() { 23 | return emptyCollection; 24 | } 25 | 26 | @Override 27 | public FileCollection getLinkFiles() { 28 | return emptyCollection; 29 | } 30 | 31 | @Override 32 | public FileCollection getRuntimeFiles() { 33 | return runtimeFiles; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/resources/frcKillRobot.sh: -------------------------------------------------------------------------------- 1 | FRC_PID_FILE=/var/run/natinst/FRC_UserProgram.pid 2 | 3 | usage() 4 | { 5 | cat >&2 < 21 | outerName = name 22 | }) 23 | 24 | when: 25 | def test = wpi.extensions.getByName('testAdd') 26 | test('hello') 27 | then: 28 | outerName == 'hello' 29 | } 30 | 31 | @CompileStatic 32 | def "can extend wpi extension static"() { 33 | outerName = '' 34 | Project project = ProjectBuilder.builder().build() 35 | project.pluginManager.apply(WPIPlugin) 36 | def wpi = project.extensions.getByType(WPIExtension) as ExtensionAware 37 | wpi.extensions.add('testAdd', { String name -> 38 | outerName = name 39 | }) 40 | 41 | when: 42 | def test = wpi.extensions.getByName('testAdd') as Closure 43 | test('hello') 44 | then: 45 | outerName == 'hello' 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/test/groovy/edu/wpi/first/gradlerio/wpi/WPIMavenExtensionExtendableTest.groovy: -------------------------------------------------------------------------------- 1 | package edu.wpi.first.gradlerio.wpi 2 | 3 | import groovy.transform.CompileStatic 4 | import edu.wpi.first.gradlerio.wpi.WPIPlugin 5 | import edu.wpi.first.gradlerio.wpi.WPIExtension 6 | import edu.wpi.first.gradlerio.wpi.WPIMavenExtension 7 | import org.gradle.testfixtures.ProjectBuilder 8 | import spock.lang.Specification 9 | import org.gradle.api.plugins.ExtensionAware 10 | import org.gradle.api.Project 11 | 12 | class WPIMavenExtensionExtendableTest extends Specification { 13 | 14 | def outerName = '' 15 | 16 | def "can extend wpi maven extension"() { 17 | outerName = '' 18 | Project project = ProjectBuilder.builder().build() 19 | project.pluginManager.apply(WPIPlugin) 20 | def wpi = (WPIExtension)project.extensions.getByType(WPIExtension) 21 | wpi.maven.extensions.add('testAdd', { String name -> 22 | outerName = name 23 | }) 24 | 25 | when: 26 | def test = wpi.maven.extensions.getByName('testAdd') 27 | test('hello') 28 | then: 29 | outerName == 'hello' 30 | } 31 | 32 | @CompileStatic 33 | def "can extend wpi maven extension static"() { 34 | outerName = '' 35 | Project project = ProjectBuilder.builder().build() 36 | project.pluginManager.apply(WPIPlugin) 37 | def wpi = project.extensions.getByType(WPIExtension) 38 | def wpiMaven = wpi.maven as ExtensionAware 39 | wpiMaven.extensions.add('testAdd', { String name -> 40 | outerName = name 41 | }) 42 | 43 | when: 44 | def test = wpiMaven.extensions.getByName('testAdd') as Closure 45 | test('hello') 46 | then: 47 | outerName == 'hello' 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /testing/README.md: -------------------------------------------------------------------------------- 1 | GradleRIO Testing Project 2 | --- 3 | 4 | Do not use these for robot projects. They are not properly configured. The correct solution is to download the examples or templates zip from the latest VS Code release (https://github.com/wpilibsuite/vscode-wpilib/releases). Those are generated exactly as VS Code would generate projects. 5 | -------------------------------------------------------------------------------- /testing/asm/.wpilib/wpilib_preferences.json: -------------------------------------------------------------------------------- 1 | { 2 | "enableCppIntellisense": true, 3 | "currentLanguage": "cpp", 4 | "projectYear": "DoNotUse", 5 | "teamNumber": 9999 6 | } 7 | -------------------------------------------------------------------------------- /testing/asm/build.gradle: -------------------------------------------------------------------------------- 1 | 2 | plugins { 3 | id "assembler" 4 | id "edu.wpi.first.GradleRIO" version "2026.0.0-alpha-1" 5 | } 6 | 7 | def projectFolder = project.buildFile.parentFile 8 | def testingFolder = projectFolder.parentFile 9 | 10 | if (testingFolder.name != 'testing' || projectFolder.name != 'asm') { 11 | throw new GradleException("These projects are not to be used for robot projects. See README.md in the GradleRIO testing folder for the correct templates to use.") 12 | } 13 | 14 | // Define my targets (RoboRIO) and artifacts (deployable files) 15 | // This is added by GradleRIO's backing project DeployUtils. 16 | deploy { 17 | targets { 18 | roborio(getTargetTypeClass('RoboRIO')) { 19 | // Team number is loaded either from the .wpilib/wpilib_preferences.json 20 | // or from command line. If not found an exception will be thrown. 21 | // You can use getTeamOrDefault(team) instead of getTeamNumber if you 22 | // want to store a team number in this file. 23 | team = project.frc.getTeamNumber() 24 | debug = project.frc.getDebugOrDefault(false) 25 | 26 | artifacts { 27 | // First part is artifact name, 2nd is artifact type 28 | // getTargetTypeClass is a shortcut to get the class type using a string 29 | 30 | frcCpp(getArtifactTypeClass('FRCNativeArtifact')) { 31 | } 32 | 33 | // Static files artifact 34 | frcStaticFileDeploy(getArtifactTypeClass('FileTreeArtifact')) { 35 | files = project.fileTree('src/main/deploy') 36 | directory = '/home/lvuser/deploy' 37 | } 38 | } 39 | } 40 | } 41 | } 42 | 43 | def deployArtifact = deploy.targets.roborio.artifacts.frcCpp 44 | 45 | // Simulation configuration 46 | 47 | wpi.cpp.debugSimulation = false 48 | 49 | wpi.sim.addGui().defaultEnabled = true 50 | wpi.sim.addDriverstation() 51 | 52 | //Sets the websocket client remote host. 53 | wpi.sim.envVar("HALSIMWS_HOST", "10.0.0.2") 54 | wpi.sim.addWebsocketsServer().defaultEnabled = true 55 | wpi.sim.addWebsocketsClient().defaultEnabled = true 56 | 57 | model { 58 | components { 59 | frcUserProgram(NativeExecutableSpec) { 60 | targetPlatform wpi.platforms.roborio 61 | 62 | sources.asm { 63 | source { 64 | srcDir 'src/main/asm' 65 | } 66 | } 67 | 68 | deployArtifact.component = it 69 | wpi.cpp.enableExternalTasks(it) 70 | 71 | wpi.sim.enable(it) 72 | // Defining my dependencies. In this case, WPILib (+ friends), and vendor libraries. 73 | wpi.cpp.vendor.cpp(it) 74 | wpi.cpp.deps.wpilib(it) 75 | } 76 | } 77 | } 78 | 79 | wrapper { 80 | gradleVersion = '8.10.2' 81 | distributionType = Wrapper.DistributionType.BIN 82 | } 83 | -------------------------------------------------------------------------------- /testing/asm/settings.gradle: -------------------------------------------------------------------------------- 1 | import org.gradle.internal.os.OperatingSystem 2 | 3 | pluginManagement { 4 | repositories { 5 | mavenLocal() 6 | gradlePluginPortal() 7 | String frcYear = '2026beta' 8 | File frcHome 9 | if (OperatingSystem.current().isWindows()) { 10 | String publicFolder = System.getenv('PUBLIC') 11 | if (publicFolder == null) { 12 | publicFolder = "C:\\Users\\Public" 13 | } 14 | def homeRoot = new File(publicFolder, "wpilib") 15 | frcHome = new File(homeRoot, frcYear) 16 | } else { 17 | def userFolder = System.getProperty("user.home") 18 | def homeRoot = new File(userFolder, "wpilib") 19 | frcHome = new File(homeRoot, frcYear) 20 | } 21 | def frcHomeMaven = new File(frcHome, 'maven') 22 | maven { 23 | name 'frcHome' 24 | url frcHomeMaven 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /testing/asm/src/main/asm/Robot.s: -------------------------------------------------------------------------------- 1 | .set STACK_SIZE, 48 2 | .set PWM_LOCATION, 44 3 | .set DIO_LOCATION, 40 4 | .set EVENT_LOCATION, 36 5 | .set COUNTER_LOCATION, 32 6 | 7 | .global main 8 | 9 | .data 10 | 11 | .LC0: 12 | .ascii "Failed to initialize the HAL\000" 13 | 14 | .LC1: 15 | .ascii "Initialized the HAL\000" 16 | 17 | .LC2: 18 | .ascii "Looping Disabled\000" 19 | 20 | .LC3: 21 | .ascii "Looping Teleop\000" 22 | 23 | .LC4: 24 | .ascii "Looping Test\000" 25 | 26 | .LC5: 27 | .ascii "Looping Auton\000" 28 | 29 | .text 30 | 31 | printCurrentState: 32 | ldr r3, [r0] 33 | add r3, r3, #1 34 | cmp r3, #50 35 | str r3, [r0] 36 | bxne lr 37 | 38 | mov r3, #0 39 | str r3, [r0] 40 | 41 | cmp r1, #0 42 | beq .printCurrentStateDisabled 43 | cmp r1, #1 44 | beq .printCurrentStateTeleop 45 | cmp r1, #2 46 | beq .printCurrentStateTest 47 | 48 | movw r0, #:lower16:.LC5 49 | movt r0, #:upper16:.LC5 50 | b puts 51 | 52 | .printCurrentStateDisabled: 53 | movw r0, #:lower16:.LC2 54 | movt r0, #:upper16:.LC2 55 | b puts 56 | 57 | .printCurrentStateTeleop: 58 | movw r0, #:lower16:.LC3 59 | movt r0, #:upper16:.LC3 60 | b puts 61 | 62 | .printCurrentStateTest: 63 | movw r0, #:lower16:.LC4 64 | movt r0, #:upper16:.LC4 65 | b puts 66 | 67 | getDSMode: 68 | push {r4, lr} 69 | sub sp, sp, #8 70 | add r0, sp, #4 71 | bl HAL_GetControlWord 72 | ldrb r3, [sp, #4] @ zero_extendqisi2 73 | ands r4, r3, #1 74 | beq .getDSModeDisabled 75 | tst r3, #2 76 | bne .getDSModeAutonomous 77 | tst r3, #4 78 | bne .getDSModeTest 79 | bl HAL_ObserveUserProgramTeleop 80 | mov r0, #1 81 | .getDSModeEnd: 82 | add sp, sp, #8 83 | pop {r4, pc} 84 | .getDSModeTest: 85 | bl HAL_ObserveUserProgramTest 86 | mov r0, #2 87 | add sp, sp, #8 88 | pop {r4, pc} 89 | .getDSModeDisabled: 90 | bl HAL_ObserveUserProgramDisabled 91 | mov r0, r4 92 | add sp, sp, #8 93 | pop {r4, pc} 94 | .getDSModeAutonomous: 95 | bl HAL_ObserveUserProgramAutonomous 96 | mov r0, #3 97 | b .getDSModeEnd 98 | 99 | 100 | 101 | main: 102 | push {r4, lr} 103 | 104 | sub sp, sp, #STACK_SIZE 105 | mov r0, #0 106 | str r0, [sp, #COUNTER_LOCATION] 107 | 108 | mov r1, #0 109 | mov r0, #500 110 | bl HAL_Initialize 111 | cmp r0, #0 112 | beq .FAIL_HAL_INIT 113 | bl HAL_ObserveUserProgramStarting 114 | 115 | movw r0, #:lower16:.LC1 116 | movt r0, #:upper16:.LC1 117 | bl puts 118 | 119 | mov r0, #2 120 | bl HAL_GetPort // Result in r0 121 | mov r1, #0 122 | str r1, [sp, #4] 123 | add r2, sp, #4 124 | bl HAL_InitializePWMPort // (HAL_GetPort, NULL, &status) 125 | str r0, [sp, #PWM_LOCATION] 126 | ldr r0, [sp, #4] 127 | cmp r0, #0 128 | bne .FAIL_GET_LAST_ERROR 129 | 130 | ldr r0, [sp, #PWM_LOCATION] 131 | mov r1, #2000 132 | mov r2, #1501 133 | mov r3, #1500 134 | mov r4, #1499 135 | str r4, [sp] 136 | mov r4, #1000 137 | str r4, [sp, #4] 138 | add r4, sp, #12 139 | str r4, [sp, #8] 140 | mov r4, #0 141 | str r4, [sp, #12] 142 | bl HAL_SetPWMConfigMicroseconds 143 | 144 | mov r0, #2 145 | bl HAL_GetPort 146 | mov r1, #1 147 | mov r2, #0 148 | str r2, [sp, #4] 149 | add r3, sp, #4 150 | bl HAL_InitializeDIOPort 151 | str r0, [sp, #DIO_LOCATION] 152 | ldr r0, [sp, #4] 153 | cmp r0, #0 154 | bne .FAIL_GET_LAST_ERROR_CLEANUP_PWM 155 | 156 | mov r0, #0 157 | mov r1, #0 158 | bl WPI_CreateEvent 159 | str r0, [sp, #EVENT_LOCATION] 160 | 161 | bl HAL_ProvideNewDataEventHandle 162 | 163 | // Main loop 164 | .LOOP: 165 | 166 | ldr r0, [sp, #EVENT_LOCATION] 167 | // r1 is unused 168 | mov r2, #0 // r2 and r3 are the double 169 | mov r3, #0 170 | movt r3, 16368 171 | add r4, sp, #12 172 | str r4, [sp] // First stack variable is timed out 173 | mov r4, #0 174 | str r4, [sp, #12] 175 | bl WPI_WaitForObjectTimeout 176 | cmp r0, #0 177 | beq .LOOP 178 | 179 | bl HAL_RefreshDSData 180 | 181 | bl getDSMode 182 | mov r1, r0 183 | mov r4, r0 184 | add r0, sp, #COUNTER_LOCATION 185 | bl printCurrentState 186 | 187 | cmp r4, #1 188 | bne .LOOP 189 | 190 | ldr r0, [sp, #DIO_LOCATION] 191 | mov r4, #0 192 | str r4, [sp, #4] 193 | add r1, sp, #4 194 | bl HAL_GetDIO 195 | 196 | cmp r0, #0 197 | beq .STOP_MOTOR 198 | 199 | mov r2, #0 // r2 and r3 are the double 200 | mov r3, #0 201 | movt r3, #16368 202 | 203 | 204 | b .DO_MOTOR 205 | .STOP_MOTOR: 206 | 207 | mov r2, #0 // r2 and r3 are the double 208 | mov r3, #0 209 | 210 | .DO_MOTOR: 211 | ldr r0, [sp, #DIO_LOCATION] 212 | add r4, sp, #12 213 | str r4, [sp] // First stack variable is timed out 214 | mov r4, #0 215 | str r4, [sp, #12] 216 | bl HAL_SetPWMSpeed 217 | 218 | b .LOOP 219 | 220 | ldr r0, [sp, #EVENT_LOCATION] 221 | bl HAL_RemoveNewDataEventHandle 222 | 223 | ldr r0, [sp, #EVENT_LOCATION] 224 | bl WPI_DestroyEvent 225 | 226 | ldr r0, [sp, #DIO_LOCATION] 227 | bl HAL_FreeDIOPort 228 | 229 | ldr r0, [sp, #PWM_LOCATION] 230 | mov r1, #0 231 | str r1, [sp, #4] 232 | add r1, sp, #4 233 | 234 | bl HAL_FreePWMPort 235 | 236 | mov r0, #0 237 | 238 | .END_MAIN: 239 | add sp, sp, #STACK_SIZE 240 | pop {r4, pc} 241 | 242 | 243 | .FAIL_HAL_INIT: 244 | movw r0, #:lower16:.LC0 245 | movt r0, #:upper16:.LC0 246 | bl puts 247 | mov r0, #1 248 | b .END_MAIN 249 | 250 | 251 | .FAIL_GET_LAST_ERROR: 252 | str r0, [sp, #4] 253 | add r0, sp, #4 254 | bl HAL_GetLastError 255 | bl puts 256 | mov r0, #1 257 | b .END_MAIN 258 | 259 | .FAIL_GET_LAST_ERROR_CLEANUP_PWM: 260 | str r0, [sp, #4] 261 | add r0, sp, #4 262 | bl HAL_GetLastError 263 | bl puts 264 | 265 | ldr r0, [sp, #PWM_LOCATION] 266 | str r1, [sp, #4] 267 | add r1, sp, #4 268 | bl HAL_FreePWMPort 269 | 270 | mov r0, #1 271 | b .END_MAIN 272 | -------------------------------------------------------------------------------- /testing/cpp/.wpilib/wpilib_preferences.json: -------------------------------------------------------------------------------- 1 | { 2 | "enableCppIntellisense": true, 3 | "currentLanguage": "cpp", 4 | "projectYear": "DoNotUse", 5 | "teamNumber": 9999 6 | } 7 | -------------------------------------------------------------------------------- /testing/cpp/README.md: -------------------------------------------------------------------------------- 1 | C++ 2 | --- 3 | 4 | This example involves building and deploying C++ sources. This should be all you need to get started. -------------------------------------------------------------------------------- /testing/cpp/build.gradle: -------------------------------------------------------------------------------- 1 | 2 | plugins { 3 | id "cpp" 4 | id "google-test-test-suite" 5 | id "edu.wpi.first.GradleRIO" version "2026.0.0-alpha-1" 6 | } 7 | 8 | def projectFolder = project.buildFile.parentFile 9 | def testingFolder = projectFolder.parentFile 10 | 11 | if (testingFolder.name != 'testing' || projectFolder.name != 'cpp') { 12 | throw new GradleException("These projects are not to be used for robot projects. See README.md in the GradleRIO testing folder for the correct templates to use.") 13 | } 14 | 15 | // Define my targets (RoboRIO) and artifacts (deployable files) 16 | // This is added by GradleRIO's backing project DeployUtils. 17 | deploy { 18 | targets { 19 | roborio(getTargetTypeClass('RoboRIO')) { 20 | // Team number is loaded either from the .wpilib/wpilib_preferences.json 21 | // or from command line. If not found an exception will be thrown. 22 | // You can use getTeamOrDefault(team) instead of getTeamNumber if you 23 | // want to store a team number in this file. 24 | team = project.frc.getTeamNumber() 25 | debug = project.frc.getDebugOrDefault(false) 26 | 27 | artifacts { 28 | // First part is artifact name, 2nd is artifact type 29 | // getTargetTypeClass is a shortcut to get the class type using a string 30 | 31 | frcCpp(getArtifactTypeClass('FRCNativeArtifact')) { 32 | } 33 | 34 | // Static files artifact 35 | frcStaticFileDeploy(getArtifactTypeClass('FileTreeArtifact')) { 36 | files = project.fileTree('src/main/deploy') 37 | directory = '/home/lvuser/deploy' 38 | } 39 | } 40 | } 41 | } 42 | } 43 | 44 | def deployArtifact = deploy.targets.roborio.artifacts.frcCpp 45 | 46 | // Simulation configuration 47 | 48 | wpi.cpp.debugSimulation = false 49 | 50 | wpi.sim.addGui().defaultEnabled = true 51 | wpi.sim.addDriverstation() 52 | 53 | //Sets the websocket client remote host. 54 | wpi.sim.envVar("HALSIMWS_HOST", "10.0.0.2") 55 | wpi.sim.addWebsocketsServer().defaultEnabled = true 56 | wpi.sim.addWebsocketsClient().defaultEnabled = true 57 | 58 | model { 59 | components { 60 | frcUserProgram(NativeExecutableSpec) { 61 | targetPlatform wpi.platforms.roborio 62 | targetPlatform wpi.platforms.desktop 63 | 64 | sources.cpp { 65 | source { 66 | srcDir 'src/main/cpp' 67 | } 68 | exportedHeaders { 69 | srcDir 'src/main/include' 70 | } 71 | } 72 | 73 | deployArtifact.component = it 74 | wpi.cpp.enableExternalTasks(it) 75 | 76 | wpi.sim.enable(it) 77 | // Defining my dependencies. In this case, WPILib (+ friends), and vendor libraries. 78 | wpi.cpp.vendor.cpp(it) 79 | wpi.cpp.deps.wpilib(it) 80 | } 81 | } 82 | testSuites { 83 | frcUserProgramTest(GoogleTestTestSuiteSpec) { 84 | testing $.components.frcUserProgram 85 | 86 | sources.cpp { 87 | source { 88 | srcDir 'src/test/cpp' 89 | include '**/*.cpp' 90 | } 91 | } 92 | 93 | wpi.cpp.enableExternalTasks(it) 94 | 95 | wpi.cpp.vendor.cpp(it) 96 | wpi.cpp.deps.wpilib(it) 97 | wpi.cpp.deps.googleTest(it) 98 | } 99 | } 100 | } 101 | 102 | wrapper { 103 | gradleVersion = '8.10.2' 104 | distributionType = Wrapper.DistributionType.BIN 105 | } 106 | -------------------------------------------------------------------------------- /testing/cpp/settings.gradle: -------------------------------------------------------------------------------- 1 | import org.gradle.internal.os.OperatingSystem 2 | 3 | pluginManagement { 4 | repositories { 5 | mavenLocal() 6 | gradlePluginPortal() 7 | String frcYear = '2025' 8 | File frcHome 9 | if (OperatingSystem.current().isWindows()) { 10 | String publicFolder = System.getenv('PUBLIC') 11 | if (publicFolder == null) { 12 | publicFolder = "C:\\Users\\Public" 13 | } 14 | def homeRoot = new File(publicFolder, "wpilib") 15 | frcHome = new File(homeRoot, frcYear) 16 | } else { 17 | def userFolder = System.getProperty("user.home") 18 | def homeRoot = new File(userFolder, "wpilib") 19 | frcHome = new File(homeRoot, frcYear) 20 | } 21 | def frcHomeMaven = new File(frcHome, 'maven') 22 | maven { 23 | name 'frcHome' 24 | url frcHomeMaven 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /testing/cpp/src/main/cpp/robot.cpp: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------*/ 2 | /* Copyright (c) 2017-2018 FIRST. All Rights Reserved. */ 3 | /* Open Source Software - may be modified and shared by FRC teams. The code */ 4 | /* must be accompanied by the FIRST BSD license file in the root directory of */ 5 | /* the project. */ 6 | /*----------------------------------------------------------------------------*/ 7 | 8 | #include "Robot.h" 9 | 10 | #include 11 | 12 | #include 13 | 14 | constexpr bool testBool = false; 15 | 16 | void Robot::RobotInit() { 17 | m_chooser.SetDefaultOption(kAutoNameDefault, kAutoNameDefault); 18 | m_chooser.AddOption(kAutoNameCustom, kAutoNameCustom); 19 | frc::SmartDashboard::PutData("Auto Modes", &m_chooser); 20 | } 21 | 22 | /** 23 | * This function is called every robot packet, no matter the mode. Use 24 | * this for items like diagnostics that you want ran during disabled, 25 | * autonomous, teleoperated and test. 26 | * 27 | *

This runs after the mode specific periodic functions, but before 28 | * LiveWindow and SmartDashboard integrated updating. 29 | */ 30 | void Robot::RobotPeriodic() {} 31 | 32 | /** 33 | * This autonomous (along with the chooser code above) shows how to select 34 | * between different autonomous modes using the dashboard. The sendable chooser 35 | * code works with the Java SmartDashboard. If you prefer the LabVIEW Dashboard, 36 | * remove all of the chooser code and uncomment the GetString line to get the 37 | * auto name from the text box below the Gyro. 38 | * 39 | * You can add additional auto modes by adding additional comparisons to the 40 | * if-else structure below with additional strings. If using the SendableChooser 41 | * make sure to add them to the chooser code above as well. 42 | */ 43 | void Robot::AutonomousInit() { 44 | m_autoSelected = m_chooser.GetSelected(); 45 | // m_autoSelected = SmartDashboard::GetString("Auto Selector", 46 | // kAutoNameDefault); 47 | std::cout << "Auto selected: " << m_autoSelected << std::endl; 48 | 49 | if (m_autoSelected == kAutoNameCustom) { 50 | // Custom Auto goes here 51 | } else { 52 | // Default Auto goes here 53 | } 54 | } 55 | 56 | void Robot::AutonomousPeriodic() { 57 | if (m_autoSelected == kAutoNameCustom) { 58 | // Custom Auto goes here 59 | } else { 60 | // Default Auto goes here 61 | } 62 | } 63 | 64 | void Robot::TeleopInit() {} 65 | 66 | void Robot::TeleopPeriodic() {} 67 | 68 | void Robot::TestPeriodic() {} 69 | 70 | #ifndef RUNNING_FRC_TESTS 71 | int main() { return frc::StartRobot(); } 72 | #endif 73 | -------------------------------------------------------------------------------- /testing/cpp/src/main/include/Robot.h: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------*/ 2 | /* Copyright (c) 2017-2018 FIRST. All Rights Reserved. */ 3 | /* Open Source Software - may be modified and shared by FRC teams. The code */ 4 | /* must be accompanied by the FIRST BSD license file in the root directory of */ 5 | /* the project. */ 6 | /*----------------------------------------------------------------------------*/ 7 | 8 | #pragma once 9 | 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | class Robot : public frc::TimedRobot { 16 | public: 17 | void RobotInit() override; 18 | void RobotPeriodic() override; 19 | void AutonomousInit() override; 20 | void AutonomousPeriodic() override; 21 | void TeleopInit() override; 22 | void TeleopPeriodic() override; 23 | void TestPeriodic() override; 24 | 25 | private: 26 | frc::SendableChooser m_chooser; 27 | const std::string kAutoNameDefault = "Default"; 28 | const std::string kAutoNameCustom = "My Auto"; 29 | std::string m_autoSelected; 30 | }; 31 | -------------------------------------------------------------------------------- /testing/cpp/src/test/cpp/main.cpp: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------*/ 2 | /* Copyright (c) 2015-2018 FIRST. All Rights Reserved. */ 3 | /* Open Source Software - may be modified and shared by FRC teams. The code */ 4 | /* must be accompanied by the FIRST BSD license file in the root directory of */ 5 | /* the project. */ 6 | /*----------------------------------------------------------------------------*/ 7 | 8 | #include 9 | 10 | #include "gtest/gtest.h" 11 | 12 | int main(int argc, char** argv) { 13 | HAL_Initialize(500, 0); 14 | ::testing::InitGoogleTest(&argc, argv); 15 | int ret = RUN_ALL_TESTS(); 16 | return ret; 17 | } 18 | -------------------------------------------------------------------------------- /testing/java/.wpilib/wpilib_preferences.json: -------------------------------------------------------------------------------- 1 | { 2 | "enableCppIntellisense": false, 3 | "currentLanguage": "java", 4 | "projectYear": "DoNotUse", 5 | "teamNumber": 9999 6 | } 7 | -------------------------------------------------------------------------------- /testing/java/README.md: -------------------------------------------------------------------------------- 1 | Java 2 | --- 3 | 4 | This example involves building and deploying Java sources. This should be all you need to get started, however there are plenty of resources online for building more advanced java projects should you require. -------------------------------------------------------------------------------- /testing/java/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "java" 3 | id "edu.wpi.first.GradleRIO" version "2026.0.0-alpha-1" 4 | } 5 | 6 | def projectFolder = project.buildFile.parentFile 7 | def testingFolder = projectFolder.parentFile 8 | 9 | if (testingFolder.name != 'testing' || projectFolder.name != 'java') { 10 | throw new GradleException("These projects are not to be used for robot projects. See README.md in the GradleRIO testing folder for the correct templates to use.") 11 | } 12 | 13 | java { 14 | sourceCompatibility = JavaVersion.VERSION_17 15 | targetCompatibility = JavaVersion.VERSION_17 16 | } 17 | 18 | def ROBOT_MAIN_CLASS = "frc.team0000.robot.Main" 19 | 20 | // Define my targets (RoboRIO) and artifacts (deployable files) 21 | // This is added by GradleRIO's backing project DeployUtils. 22 | deploy { 23 | targets { 24 | roborio(getTargetTypeClass('RoboRIO')) { 25 | // Team number is loaded either from the .wpilib/wpilib_preferences.json 26 | // or from command line. If not found an exception will be thrown. 27 | // You can use getTeamOrDefault(team) instead of getTeamNumber if you 28 | // want to store a team number in this file. 29 | team = project.frc.getTeamNumber() 30 | debug = project.frc.getDebugOrDefault(false) 31 | 32 | artifacts { 33 | // First part is artifact name, 2nd is artifact type 34 | // getTargetTypeClass is a shortcut to get the class type using a string 35 | 36 | frcJava(getArtifactTypeClass('FRCJavaArtifact')) { 37 | } 38 | 39 | // Static files artifact 40 | frcStaticFileDeploy(getArtifactTypeClass('FileTreeArtifact')) { 41 | files = project.fileTree('src/main/deploy') 42 | directory = '/home/lvuser/deploy' 43 | } 44 | } 45 | } 46 | } 47 | } 48 | 49 | def deployArtifact = deploy.targets.roborio.artifacts.frcJava 50 | 51 | wpi.java.debugJni = false 52 | 53 | // Defining my dependencies. In this case, WPILib (+ friends), and vendor libraries. 54 | // Also defines JUnit 4. 55 | dependencies { 56 | implementation wpi.java.deps.wpilib() 57 | implementation wpi.java.vendor.java() 58 | 59 | roborioDebug wpi.java.deps.wpilibJniDebug(wpi.platforms.roborio) 60 | roborioDebug wpi.java.vendor.jniDebug(wpi.platforms.roborio) 61 | 62 | roborioRelease wpi.java.deps.wpilibJniRelease(wpi.platforms.roborio) 63 | roborioRelease wpi.java.vendor.jniRelease(wpi.platforms.roborio) 64 | 65 | nativeDebug wpi.java.deps.wpilibJniDebug(wpi.platforms.desktop) 66 | nativeDebug wpi.java.vendor.jniDebug(wpi.platforms.desktop) 67 | simulationDebug wpi.sim.enableDebug() 68 | 69 | nativeRelease wpi.java.deps.wpilibJniRelease(wpi.platforms.desktop) 70 | nativeRelease wpi.java.vendor.jniRelease(wpi.platforms.desktop) 71 | simulationRelease wpi.sim.enableRelease() 72 | 73 | testImplementation 'junit:junit:4.12' 74 | } 75 | 76 | 77 | // Simulation configuration (e.g. environment variables). 78 | 79 | wpi.sim.addGui().defaultEnabled = true 80 | wpi.sim.addDriverstation() 81 | 82 | //Sets the websocket client remote host. 83 | wpi.sim.envVar("HALSIMWS_HOST", "10.0.0.2") 84 | wpi.sim.addWebsocketsServer().defaultEnabled = true 85 | wpi.sim.addWebsocketsClient().defaultEnabled = true 86 | 87 | // Setting up my Jar File. In this case, adding all libraries into the main jar ('fat jar') 88 | // in order to make them all available at runtime. Also adding the manifest so WPILib 89 | // knows where to look for our Robot Class. 90 | jar { 91 | from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } } 92 | 93 | manifest edu.wpi.first.gradlerio.GradleRIOPlugin.javaManifest(ROBOT_MAIN_CLASS) 94 | 95 | duplicatesStrategy = DuplicatesStrategy.INCLUDE 96 | } 97 | 98 | deployArtifact.jarTask = jar 99 | wpi.java.configureExecutableTasks(jar) 100 | wpi.java.configureTestTasks(test) 101 | 102 | wrapper { 103 | gradleVersion = '8.10.2' 104 | distributionType = Wrapper.DistributionType.BIN 105 | } 106 | -------------------------------------------------------------------------------- /testing/java/settings.gradle: -------------------------------------------------------------------------------- 1 | import org.gradle.internal.os.OperatingSystem 2 | 3 | pluginManagement { 4 | repositories { 5 | mavenLocal() 6 | gradlePluginPortal() 7 | String frcYear = '2026beta' 8 | File frcHome 9 | if (OperatingSystem.current().isWindows()) { 10 | String publicFolder = System.getenv('PUBLIC') 11 | if (publicFolder == null) { 12 | publicFolder = "C:\\Users\\Public" 13 | } 14 | def homeRoot = new File(publicFolder, "wpilib") 15 | frcHome = new File(homeRoot, frcYear) 16 | } else { 17 | def userFolder = System.getProperty("user.home") 18 | def homeRoot = new File(userFolder, "wpilib") 19 | frcHome = new File(homeRoot, frcYear) 20 | } 21 | def frcHomeMaven = new File(frcHome, 'maven') 22 | maven { 23 | name 'frcHome' 24 | url frcHomeMaven 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /testing/java/src/main/java/frc/team0000/robot/Main.java: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------*/ 2 | /* Copyright (c) 2018 FIRST. All Rights Reserved. */ 3 | /* Open Source Software - may be modified and shared by FRC teams. The code */ 4 | /* must be accompanied by the FIRST BSD license file in the root directory of */ 5 | /* the project. */ 6 | /*----------------------------------------------------------------------------*/ 7 | 8 | package frc.team0000.robot; 9 | 10 | import edu.wpi.first.wpilibj.RobotBase; 11 | 12 | /** 13 | * Do NOT add any static variables to this class, or any initialization at all. 14 | * Unless you know what you are doing, do not modify this file except to 15 | * change the parameter class to the startRobot call. 16 | */ 17 | public final class Main { 18 | private Main() { 19 | } 20 | 21 | /** 22 | * Main initialization function. Do not perform any initialization here. 23 | * 24 | *

If you change your main robot class, change the parameter type. 25 | */ 26 | public static void main(String... args) { 27 | RobotBase.startRobot(Robot::new); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /testing/java/src/main/java/frc/team0000/robot/Robot.java: -------------------------------------------------------------------------------- 1 | package frc.team0000.robot; 2 | 3 | import edu.wpi.first.wpilibj.TimedRobot; 4 | import edu.wpi.first.wpilibj.smartdashboard.SendableChooser; 5 | import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; 6 | 7 | /** 8 | * The VM is configured to automatically run this class, and to call the 9 | * functions corresponding to each mode, as described in the TimedRobot 10 | * documentation. If you change the name of this class or the package after 11 | * creating this project, you must also update the build.gradle file in the 12 | * project. 13 | */ 14 | public class Robot extends TimedRobot { 15 | private static final String kDefaultAuto = "Default"; 16 | private static final String kCustomAuto = "My Auto"; 17 | private String m_autoSelected; 18 | private final SendableChooser m_chooser = new SendableChooser<>(); 19 | 20 | /** 21 | * This function is run when the robot is first started up and should be 22 | * used for any initialization code. 23 | */ 24 | @Override 25 | public void robotInit() { 26 | m_chooser.setDefaultOption("Default Auto", kDefaultAuto); 27 | m_chooser.addOption("My Auto", kCustomAuto); 28 | SmartDashboard.putData("Auto choices", m_chooser); 29 | } 30 | 31 | /** 32 | * This function is called every robot packet, no matter the mode. Use 33 | * this for items like diagnostics that you want ran during disabled, 34 | * autonomous, teleoperated and test. 35 | * 36 | *

This runs after the mode specific periodic functions, but before 37 | * LiveWindow and SmartDashboard integrated updating. 38 | */ 39 | @Override 40 | public void robotPeriodic() { 41 | } 42 | 43 | /** 44 | * This autonomous (along with the chooser code above) shows how to select 45 | * between different autonomous modes using the dashboard. The sendable 46 | * chooser code works with the Java SmartDashboard. If you prefer the 47 | * LabVIEW Dashboard, remove all of the chooser code and uncomment the 48 | * getString line to get the auto name from the text box below the Gyro 49 | * 50 | *

You can add additional auto modes by adding additional comparisons to 51 | * the switch structure below with additional strings. If using the 52 | * SendableChooser make sure to add them to the chooser code above as well. 53 | */ 54 | @Override 55 | public void autonomousInit() { 56 | m_autoSelected = m_chooser.getSelected(); 57 | // m_autoSelected = SmartDashboard.getString("Auto Selector", kDefaultAuto); 58 | System.out.println("Auto selected: " + m_autoSelected); 59 | } 60 | 61 | /** 62 | * This function is called periodically during autonomous. 63 | */ 64 | @Override 65 | public void autonomousPeriodic() { 66 | switch (m_autoSelected) { 67 | case kCustomAuto: 68 | // Put custom auto code here 69 | break; 70 | case kDefaultAuto: 71 | default: 72 | // Put default auto code here 73 | break; 74 | } 75 | } 76 | 77 | /** 78 | * This function is called periodically during operator control. 79 | */ 80 | @Override 81 | public void teleopPeriodic() { 82 | } 83 | 84 | /** 85 | * This function is called periodically during test mode. 86 | */ 87 | @Override 88 | public void testPeriodic() { 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /testing/java/src/test/java/TestCode.java: -------------------------------------------------------------------------------- 1 | import org.junit.Test; 2 | import edu.wpi.first.cscore.*; 3 | 4 | public class TestCode { 5 | @Test 6 | public void jniLinkTest() { 7 | CameraServerJNI.getHostname(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /testing/jni/.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/c++,java,linux,macos,gradle,windows,visualstudiocode 2 | 3 | ### C++ ### 4 | # Prerequisites 5 | *.d 6 | 7 | # Compiled Object files 8 | *.slo 9 | *.lo 10 | *.o 11 | *.obj 12 | 13 | # Precompiled Headers 14 | *.gch 15 | *.pch 16 | 17 | # Compiled Dynamic libraries 18 | *.so 19 | *.dylib 20 | *.dll 21 | 22 | # Fortran module files 23 | *.mod 24 | *.smod 25 | 26 | # Compiled Static libraries 27 | *.lai 28 | *.la 29 | *.a 30 | *.lib 31 | 32 | # Executables 33 | *.exe 34 | *.out 35 | *.app 36 | 37 | ### Java ### 38 | # Compiled class file 39 | *.class 40 | 41 | # Log file 42 | *.log 43 | 44 | # BlueJ files 45 | *.ctxt 46 | 47 | # Mobile Tools for Java (J2ME) 48 | .mtj.tmp/ 49 | 50 | # Package Files # 51 | *.jar 52 | *.war 53 | *.nar 54 | *.ear 55 | *.zip 56 | *.tar.gz 57 | *.rar 58 | 59 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 60 | hs_err_pid* 61 | 62 | ### Linux ### 63 | *~ 64 | 65 | # temporary files which can be created if a process still has a handle open of a deleted file 66 | .fuse_hidden* 67 | 68 | # KDE directory preferences 69 | .directory 70 | 71 | # Linux trash folder which might appear on any partition or disk 72 | .Trash-* 73 | 74 | # .nfs files are created when an open file is removed but is still being accessed 75 | .nfs* 76 | 77 | ### macOS ### 78 | # General 79 | .DS_Store 80 | .AppleDouble 81 | .LSOverride 82 | 83 | # Icon must end with two \r 84 | Icon 85 | 86 | # Thumbnails 87 | ._* 88 | 89 | # Files that might appear in the root of a volume 90 | .DocumentRevisions-V100 91 | .fseventsd 92 | .Spotlight-V100 93 | .TemporaryItems 94 | .Trashes 95 | .VolumeIcon.icns 96 | .com.apple.timemachine.donotpresent 97 | 98 | # Directories potentially created on remote AFP share 99 | .AppleDB 100 | .AppleDesktop 101 | Network Trash Folder 102 | Temporary Items 103 | .apdisk 104 | 105 | ### VisualStudioCode ### 106 | .vscode/* 107 | !.vscode/settings.json 108 | !.vscode/tasks.json 109 | !.vscode/launch.json 110 | !.vscode/extensions.json 111 | 112 | ### Windows ### 113 | # Windows thumbnail cache files 114 | Thumbs.db 115 | ehthumbs.db 116 | ehthumbs_vista.db 117 | 118 | # Dump file 119 | *.stackdump 120 | 121 | # Folder config file 122 | [Dd]esktop.ini 123 | 124 | # Recycle Bin used on file shares 125 | $RECYCLE.BIN/ 126 | 127 | # Windows Installer files 128 | *.cab 129 | *.msi 130 | *.msix 131 | *.msm 132 | *.msp 133 | 134 | # Windows shortcuts 135 | *.lnk 136 | 137 | ### Gradle ### 138 | .gradle 139 | /build/ 140 | 141 | # Ignore Gradle GUI config 142 | gradle-app.setting 143 | 144 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 145 | !gradle-wrapper.jar 146 | 147 | # Cache of project 148 | .gradletasknamecache 149 | 150 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 151 | # gradle/wrapper/gradle-wrapper.properties 152 | 153 | # # VS Code Specific Java Settings 154 | .classpath 155 | .project 156 | .settings/ 157 | bin/ 158 | imgui.ini 159 | 160 | 161 | # End of https://www.gitignore.io/api/c++,java,linux,macos,gradle,windows,visualstudiocode 162 | -------------------------------------------------------------------------------- /testing/jni/.wpilib/wpilib_preferences.json: -------------------------------------------------------------------------------- 1 | { 2 | "enableCppIntellisense": true, 3 | "currentLanguage": "java", 4 | "projectYear": "2021", 5 | "teamNumber": 9999 6 | } 7 | -------------------------------------------------------------------------------- /testing/jni/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "java" 3 | id "cpp" 4 | id "edu.wpi.first.GradleRIO" version "2026.0.0-alpha-1" 5 | id 'edu.wpi.first.GradleJni' version '0.10.1' 6 | } 7 | 8 | sourceCompatibility = JavaVersion.VERSION_17 9 | targetCompatibility = JavaVersion.VERSION_17 10 | 11 | def ROBOT_MAIN_CLASS = "frc.robot.Main" 12 | 13 | // Define my targets (RoboRIO) and artifacts (deployable files) 14 | // This is added by GradleRIO's backing project DeployUtils. 15 | deploy { 16 | targets { 17 | roboRIO("roborio") { 18 | // Team number is loaded either from the .wpilib/wpilib_preferences.json 19 | // or from command line. If not found an exception will be thrown. 20 | // You can use getTeamOrDefault(team) instead of getTeamNumber if you 21 | // want to store a team number in this file. 22 | team = frc.getTeamNumber() 23 | } 24 | } 25 | artifacts { 26 | frcJavaArtifact('frcJava') { 27 | targets << "roborio" 28 | // Debug can be overridden by command line, for use with VSCode 29 | debug = frc.getDebugOrDefault(false) 30 | } 31 | // Built in artifact to deploy arbitrary files to the roboRIO. 32 | fileTreeArtifact('frcStaticFileDeploy') { 33 | // The directory below is the local directory to deploy 34 | files = fileTree(dir: 'src/main/deploy') 35 | // Deploy to RoboRIO target, into /home/lvuser/deploy 36 | targets << "roborio" 37 | directory = '/home/lvuser/deploy' 38 | } 39 | frcNativeLibraryArtifact('jniLibrary') { 40 | targets << "roborio" 41 | component = 'JNILibrary' 42 | buildType = 'release' 43 | } 44 | } 45 | } 46 | 47 | // Set this to true to enable desktop support. 48 | def includeDesktopSupport = true 49 | 50 | // Simulation configuration (e.g. environment variables). 51 | sim { 52 | // Sets the websocket client remote host. 53 | // envVar "HALSIMWS_HOST", "10.0.0.2" 54 | } 55 | 56 | model { 57 | components { 58 | JNILibrary(JniNativeLibrarySpec) { 59 | targetPlatform wpi.platforms.roborio 60 | if (includeDesktopSupport) { 61 | targetPlatform wpi.platforms.desktop 62 | } 63 | 64 | enableCheckTask true 65 | javaCompileTasks << compileJava 66 | jniCrossCompileOptions << JniCrossCompileOptions(nativeUtils.wpi.platforms.roborio) 67 | jniCrossCompileOptions << JniCrossCompileOptions(nativeUtils.wpi.platforms.raspbian) 68 | jniCrossCompileOptions << JniCrossCompileOptions(nativeUtils.wpi.platforms.aarch64bionic) 69 | 70 | sources { 71 | cpp { 72 | source { 73 | srcDirs 'src/main/native/cpp' 74 | include '**/*.cpp' 75 | } 76 | exportedHeaders { 77 | srcDir 'src/main/native/include' 78 | include '**/*.h' 79 | } 80 | 81 | } 82 | } 83 | 84 | wpi.useLibrary(it, 'driver_shared') 85 | } 86 | } 87 | } 88 | 89 | // Defining my dependencies. In this case, WPILib (+ friends), and vendor libraries. 90 | // Also defines JUnit 4. 91 | dependencies { 92 | implementation wpi.deps.wpilib() 93 | nativeZip wpi.deps.wpilibJni(wpi.platforms.roborio) 94 | nativeDesktopZip wpi.deps.wpilibJni(wpi.platforms.desktop) 95 | 96 | 97 | implementation wpi.deps.vendor.java() 98 | nativeZip wpi.deps.vendor.jni(wpi.platforms.roborio) 99 | nativeDesktopZip wpi.deps.vendor.jni(wpi.platforms.desktop) 100 | 101 | testImplementation 'junit:junit:4.12' 102 | 103 | // Enable simulation gui support. Must check the box in vscode to enable support 104 | // upon debugging 105 | simulation wpi.deps.sim.gui(wpi.platforms.desktop, false) 106 | } 107 | 108 | // Setting up my Jar File. In this case, adding all libraries into the main jar ('fat jar') 109 | // in order to make them all available at runtime. Also adding the manifest so WPILib 110 | // knows where to look for our Robot Class. 111 | jar { 112 | from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } } 113 | manifest edu.wpi.first.gradlerio.GradleRIOPlugin.javaManifest(ROBOT_MAIN_CLASS) 114 | } 115 | -------------------------------------------------------------------------------- /testing/jni/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wpilibsuite/GradleRIO/4f6ab6d1b1e5b968f1d575b23c702bf34850e676/testing/jni/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /testing/jni/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=permwrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=permwrapper/dists 6 | -------------------------------------------------------------------------------- /testing/jni/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | # Determine the Java command to use to start the JVM. 86 | if [ -n "$JAVA_HOME" ] ; then 87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 88 | # IBM's JDK on AIX uses strange locations for the executables 89 | JAVACMD="$JAVA_HOME/jre/sh/java" 90 | else 91 | JAVACMD="$JAVA_HOME/bin/java" 92 | fi 93 | if [ ! -x "$JAVACMD" ] ; then 94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 95 | 96 | Please set the JAVA_HOME variable in your environment to match the 97 | location of your Java installation." 98 | fi 99 | else 100 | JAVACMD="java" 101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 102 | 103 | Please set the JAVA_HOME variable in your environment to match the 104 | location of your Java installation." 105 | fi 106 | 107 | # Increase the maximum file descriptors if we can. 108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 109 | MAX_FD_LIMIT=`ulimit -H -n` 110 | if [ $? -eq 0 ] ; then 111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 112 | MAX_FD="$MAX_FD_LIMIT" 113 | fi 114 | ulimit -n $MAX_FD 115 | if [ $? -ne 0 ] ; then 116 | warn "Could not set maximum file descriptor limit: $MAX_FD" 117 | fi 118 | else 119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 120 | fi 121 | fi 122 | 123 | # For Darwin, add options to specify how the application appears in the dock 124 | if $darwin; then 125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 126 | fi 127 | 128 | # For Cygwin or MSYS, switch paths to Windows format before running java 129 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 132 | JAVACMD=`cygpath --unix "$JAVACMD"` 133 | 134 | # We build the pattern for arguments to be converted via cygpath 135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 136 | SEP="" 137 | for dir in $ROOTDIRSRAW ; do 138 | ROOTDIRS="$ROOTDIRS$SEP$dir" 139 | SEP="|" 140 | done 141 | OURCYGPATTERN="(^($ROOTDIRS))" 142 | # Add a user-defined pattern to the cygpath arguments 143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 145 | fi 146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 147 | i=0 148 | for arg in "$@" ; do 149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 151 | 152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 154 | else 155 | eval `echo args$i`="\"$arg\"" 156 | fi 157 | i=`expr $i + 1` 158 | done 159 | case $i in 160 | 0) set -- ;; 161 | 1) set -- "$args0" ;; 162 | 2) set -- "$args0" "$args1" ;; 163 | 3) set -- "$args0" "$args1" "$args2" ;; 164 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 165 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 166 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 167 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 168 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 169 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 170 | esac 171 | fi 172 | 173 | # Escape application args 174 | save () { 175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 176 | echo " " 177 | } 178 | APP_ARGS=`save "$@"` 179 | 180 | # Collect all arguments for the java command, following the shell quoting and substitution rules 181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 182 | 183 | exec "$JAVACMD" "$@" 184 | -------------------------------------------------------------------------------- /testing/jni/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 33 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 34 | 35 | @rem Find java.exe 36 | if defined JAVA_HOME goto findJavaFromJavaHome 37 | 38 | set JAVA_EXE=java.exe 39 | %JAVA_EXE% -version >NUL 2>&1 40 | if "%ERRORLEVEL%" == "0" goto init 41 | 42 | echo. 43 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 44 | echo. 45 | echo Please set the JAVA_HOME variable in your environment to match the 46 | echo location of your Java installation. 47 | 48 | goto fail 49 | 50 | :findJavaFromJavaHome 51 | set JAVA_HOME=%JAVA_HOME:"=% 52 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 53 | 54 | if exist "%JAVA_EXE%" goto init 55 | 56 | echo. 57 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 58 | echo. 59 | echo Please set the JAVA_HOME variable in your environment to match the 60 | echo location of your Java installation. 61 | 62 | goto fail 63 | 64 | :init 65 | @rem Get command-line arguments, handling Windows variants 66 | 67 | if not "%OS%" == "Windows_NT" goto win9xME_args 68 | 69 | :win9xME_args 70 | @rem Slurp the command line arguments. 71 | set CMD_LINE_ARGS= 72 | set _SKIP=2 73 | 74 | :win9xME_args_slurp 75 | if "x%~1" == "x" goto execute 76 | 77 | set CMD_LINE_ARGS=%* 78 | 79 | :execute 80 | @rem Setup the command line 81 | 82 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 83 | 84 | @rem Execute Gradle 85 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 86 | 87 | :end 88 | @rem End local scope for the variables with windows NT shell 89 | if "%ERRORLEVEL%"=="0" goto mainEnd 90 | 91 | :fail 92 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 93 | rem the _cmd.exe /c_ return code! 94 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 95 | exit /b 1 96 | 97 | :mainEnd 98 | if "%OS%"=="Windows_NT" endlocal 99 | 100 | :omega 101 | -------------------------------------------------------------------------------- /testing/jni/settings.gradle: -------------------------------------------------------------------------------- 1 | import org.gradle.internal.os.OperatingSystem 2 | 3 | pluginManagement { 4 | repositories { 5 | mavenLocal() 6 | gradlePluginPortal() 7 | String frcYear = '2026beta' 8 | File frcHome 9 | if (OperatingSystem.current().isWindows()) { 10 | String publicFolder = System.getenv('PUBLIC') 11 | if (publicFolder == null) { 12 | publicFolder = "C:\\Users\\Public" 13 | } 14 | def homeRoot = new File(publicFolder, "wpilib") 15 | frcHome = new File(homeRoot, frcYear) 16 | } else { 17 | def userFolder = System.getProperty("user.home") 18 | def homeRoot = new File(userFolder, "wpilib") 19 | frcHome = new File(homeRoot, frcYear) 20 | } 21 | def frcHomeMaven = new File(frcHome, 'maven') 22 | maven { 23 | name 'frcHome' 24 | url frcHomeMaven 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /testing/jni/src/main/deploy/example.txt: -------------------------------------------------------------------------------- 1 | Files placed in this directory will be deployed to the RoboRIO into the 2 | 'deploy' directory in the home folder. Use the 'Filesystem.getDeployDirectory' wpilib function 3 | to get a proper path relative to the deploy directory. -------------------------------------------------------------------------------- /testing/jni/src/main/java/frc/robot/JNICode.java: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------*/ 2 | /* Copyright (c) 2018 FIRST. All Rights Reserved. */ 3 | /* Open Source Software - may be modified and shared by FRC teams. The code */ 4 | /* must be accompanied by the FIRST BSD license file in the root directory of */ 5 | /* the project. */ 6 | /*----------------------------------------------------------------------------*/ 7 | 8 | package frc.robot; 9 | 10 | import java.io.IOException; 11 | 12 | import edu.wpi.first.util.RuntimeLoader; 13 | 14 | public class JNICode { 15 | static RuntimeLoader loader = null; 16 | 17 | static { 18 | try { 19 | // Change this name to match the library name of the jni library in build.gradle 20 | // For the last parameter, make it match this class 21 | loader = new RuntimeLoader<>("JNILibrary", RuntimeLoader.getDefaultExtractionRoot(), JNICode.class); 22 | loader.loadLibrary(); 23 | } catch (IOException ex) { 24 | ex.printStackTrace(); 25 | System.exit(1); 26 | } 27 | } 28 | 29 | public static native void jniFunction(); 30 | } 31 | -------------------------------------------------------------------------------- /testing/jni/src/main/java/frc/robot/Main.java: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------*/ 2 | /* Copyright (c) 2018 FIRST. All Rights Reserved. */ 3 | /* Open Source Software - may be modified and shared by FRC teams. The code */ 4 | /* must be accompanied by the FIRST BSD license file in the root directory of */ 5 | /* the project. */ 6 | /*----------------------------------------------------------------------------*/ 7 | 8 | package frc.robot; 9 | 10 | import edu.wpi.first.wpilibj.RobotBase; 11 | 12 | /** 13 | * Do NOT add any static variables to this class, or any initialization at all. 14 | * Unless you know what you are doing, do not modify this file except to 15 | * change the parameter class to the startRobot call. 16 | */ 17 | public final class Main { 18 | private Main() { 19 | } 20 | 21 | /** 22 | * Main initialization function. Do not perform any initialization here. 23 | * 24 | *

If you change your main robot class, change the parameter type. 25 | */ 26 | public static void main(String... args) { 27 | RobotBase.startRobot(Robot::new); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /testing/jni/src/main/java/frc/robot/Robot.java: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------*/ 2 | /* Copyright (c) 2018 FIRST. All Rights Reserved. */ 3 | /* Open Source Software - may be modified and shared by FRC teams. The code */ 4 | /* must be accompanied by the FIRST BSD license file in the root directory of */ 5 | /* the project. */ 6 | /*----------------------------------------------------------------------------*/ 7 | 8 | package frc.robot; 9 | 10 | import edu.wpi.first.wpilibj.TimedRobot; 11 | 12 | /** 13 | * The VM is configured to automatically run this class, and to call the 14 | * functions corresponding to each mode, as described in the TimedRobot 15 | * documentation. If you change the name of this class or the package after 16 | * creating this project, you must also update the build.gradle file in the 17 | * project. 18 | */ 19 | public class Robot extends TimedRobot { 20 | /** 21 | * This function is run when the robot is first started up and should be used 22 | * for any initialization code. 23 | */ 24 | @Override 25 | public void robotInit() { 26 | JNICode.jniFunction(); 27 | } 28 | 29 | @Override 30 | public void autonomousInit() { 31 | } 32 | 33 | @Override 34 | public void autonomousPeriodic() { 35 | } 36 | 37 | @Override 38 | public void teleopInit() { 39 | } 40 | 41 | @Override 42 | public void teleopPeriodic() { 43 | } 44 | 45 | @Override 46 | public void testInit() { 47 | } 48 | 49 | @Override 50 | public void testPeriodic() { 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /testing/jni/src/main/native/cpp/JNICode.cpp: -------------------------------------------------------------------------------- 1 | #include "frc_robot_JNICode.h" 2 | 3 | #include "wpi/jni_util.h" 4 | #include "wpi/raw_ostream.h" 5 | 6 | extern "C" { 7 | JNIEXPORT void JNICALL Java_frc_robot_JNICode_jniFunction 8 | (JNIEnv *, jclass) { 9 | wpi::outs() << "Hello from JNI!\n"; 10 | wpi::outs().flush(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /versionupdates.gradle: -------------------------------------------------------------------------------- 1 | 2 | def versionMap = [ 3 | wpilibVersion: 'edu.wpi.first.wpilibj:wpilibj-java:+', 4 | smartDashboardVersion: 'edu.wpi.first.tools:SmartDashboard:+:win64', 5 | outlineViewerVersion: 'edu.wpi.first.tools:OutlineViewer:+:windowsx86-64@zip', 6 | robotBuilderVersion: 'edu.wpi.first.tools:RobotBuilder:+', 7 | shuffleboardVersion: 'edu.wpi.first.tools:Shuffleboard:+:win64', 8 | pathWeaverVersion: 'edu.wpi.first.tools:PathWeaver:+:win64', 9 | glassVersion: 'edu.wpi.first.tools:Glass:+:windowsx86-64@zip', 10 | sysIdVersion: 'edu.wpi.first.tools:SysId:+:windowsx86-64@zip', 11 | roboRIOTeamNumberSetterVersion: 'edu.wpi.first.tools:roboRIOTeamNumberSetter:+:windowsx86-64@zip', 12 | dataLogToolVersion: 'edu.wpi.first.tools:DataLogTool:+:windowsx86-64@zip', 13 | opencvVersion: 'edu.wpi.first.thirdparty.frc2025.opencv:opencv-java:+', 14 | niLibrariesVersion: 'edu.wpi.first.ni-libraries:runtime:+:allowedimages@zip', 15 | imguiVersion: 'edu.wpi.first.thirdparty.frc2024:imgui:+:headers', 16 | wpimathVersion: 'edu.wpi.first.wpimath:wpimath-java:+', 17 | wpicalToolVersion: 'edu.wpi.first.tools:wpical:+:windowsx86-64@zip', 18 | processstarterToolVersion: 'edu.wpi.first.tools:processstarter:+:windowsx86-64@zip', 19 | ] 20 | 21 | configurations { 22 | gradleRioVersions 23 | } 24 | 25 | def useDevelopmentProperty = 'useDevelopment' 26 | 27 | project.repositories.maven { repo -> 28 | repo.name = "WPI" 29 | if (project.hasProperty(useDevelopmentProperty)) { 30 | repo.url = "https://frcmaven.wpi.edu/artifactory/development" 31 | } else { 32 | repo.url = "https://frcmaven.wpi.edu/artifactory/release" 33 | } 34 | } 35 | 36 | dependencies { 37 | versionMap.each { key, value -> 38 | gradleRioVersions value 39 | } 40 | } 41 | 42 | String regex = "String\\s+?placeholder\\s+?=\\s+?[\\\"|\\'].+?[\\\"|\\']" 43 | String buildRegex = "def\\s+?placeholder\\s+?=\\s+?[\\\"|\\'].+?[\\\"|\\']" 44 | String mavenDevRegex = "this\\.useDevelopment\\s*=\\s*(true|false)" 45 | String validVersionsRegex = "validImageVersions = List\\.of\\((.+)\\);" 46 | 47 | tasks.register('UpdateVersions') { 48 | doLast { 49 | def mavenExtFile = file('src/main/java/edu/wpi/first/gradlerio/wpi/WPIMavenExtension.java') 50 | def mavenExtText = mavenExtFile.text 51 | def toSet = "this.useDevelopment = false" 52 | if (project.hasProperty(useDevelopmentProperty)) { 53 | toSet = "this.useDevelopment = true" 54 | } 55 | 56 | mavenExtFile.text = mavenExtText.replaceAll(mavenDevRegex, toSet) 57 | 58 | 59 | def extFile = file('src/main/java/edu/wpi/first/gradlerio/wpi/WPIVersionsExtension.java') 60 | def extText = extFile.text 61 | configurations.gradleRioVersions.resolvedConfiguration.resolvedArtifacts.each { 62 | versionMap.each { key, value -> 63 | def id = it.moduleVersion.id 64 | if (value.startsWith("${id.group}:${it.name}:+".toString())) { 65 | def localRegex = regex.replace('placeholder', key) 66 | extText = extText.replaceAll(localRegex, "String ${key} = \"${id.version}\"".toString()) 67 | } 68 | } 69 | } 70 | extFile.text = extText 71 | 72 | def buildFile = file('build.gradle') 73 | def buildText = buildFile.text 74 | configurations.gradleRioVersions.resolvedConfiguration.resolvedArtifacts.each { 75 | versionMap.each { key, value -> 76 | def id = it.moduleVersion.id 77 | if (value.startsWith("${id.group}:${it.name}:+".toString())) { 78 | def localBuildRegex = buildRegex.replace('placeholder', key) 79 | buildText = buildText.replaceAll(localBuildRegex, "def ${key} = \"${id.version}\"".toString()) 80 | } 81 | } 82 | } 83 | buildFile.text = buildText 84 | 85 | def allowedVersions = "" 86 | def first = true 87 | configurations.gradleRioVersions.resolvedConfiguration.resolvedArtifacts.each { 88 | if (it.classifier == "allowedimages") { 89 | def f = project.zipTree(it.file) 90 | f.visit { 91 | if (it.name == "allowed_images.txt") { 92 | it.file.eachLine { 93 | if (!first) { 94 | allowedVersions += ', ' 95 | } 96 | first = false 97 | allowedVersions += "\"$it\"" 98 | } 99 | } 100 | } 101 | } 102 | } 103 | 104 | def rootExtFile = file('src/main/java/edu/wpi/first/gradlerio/wpi/WPIExtension.java') 105 | def rootExtText = rootExtFile.text 106 | 107 | rootExtFile.text = rootExtText.replaceAll(validVersionsRegex, "validImageVersions = List.of($allowedVersions);") 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /wsl_install_java.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | JAVA_REL=17 4 | JAVA_HOME="/usr/lib/jvm/java-$JAVA_REL-openjdk-amd64" 5 | 6 | set -ex 7 | sudo apt-get install -y --no-install-recommends \ 8 | openjdk-$JAVA_REL-jdk-headless 9 | sudo update-alternatives --set java "$JAVA_HOME/bin/java" 10 | sudo update-alternatives --set javac "$JAVA_HOME/bin/javac" 11 | (javac -version | grep -q "javac $JAVA_REL.*") || exit 12 | 13 | cat << EOF | sudo tee /etc/profile.d/wpi-openjdk.sh 14 | export JAVA_HOME=$JAVA_HOME 15 | EOF 16 | 17 | --------------------------------------------------------------------------------