mirror of
https://github.com/PhotonVision/photonvision
synced 2026-06-28 02:11:40 +00:00
Create photon-targeting-JNI framework (#1428)
Initial framework for adding JNI libraries. Auto generated JNI headers and sticks native libraries into the JAR (and adds to class path for testing)
This commit is contained in:
6
.github/workflows/build.yml
vendored
6
.github/workflows/build.yml
vendored
@@ -160,14 +160,14 @@ jobs:
|
||||
- run: git fetch --tags --force
|
||||
- run: |
|
||||
chmod +x gradlew
|
||||
./gradlew photon-targeting:build photon-lib:build -Pbuildalldesktop -i
|
||||
- run: ./gradlew photon-lib:publish photon-targeting:publish -Pbuildalldesktop
|
||||
./gradlew photon-targeting:build photon-lib:build -i
|
||||
- run: ./gradlew photon-lib:publish photon-targeting:publish
|
||||
name: Publish
|
||||
env:
|
||||
ARTIFACTORY_API_KEY: ${{ secrets.ARTIFACTORY_API_KEY }}
|
||||
if: github.event_name == 'push' && github.repository_owner == 'photonvision'
|
||||
# Copy artifacts to build/outputs/maven
|
||||
- run: ./gradlew photon-lib:publish photon-targeting:publish -PcopyOfflineArtifacts -Pbuildalldesktop
|
||||
- run: ./gradlew photon-lib:publish photon-targeting:publish -PcopyOfflineArtifacts
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: maven-${{ matrix.artifact-name }}
|
||||
|
||||
@@ -40,6 +40,8 @@ Note that these are case sensitive!
|
||||
- `-PtgtIP`: Specifies where `./gradlew deploy` should try to copy the fat JAR to
|
||||
- `-Pprofile`: enables JVM profiling
|
||||
|
||||
If you're cross-compiling, you'll need the wpilib toolchain installed. This can be done via Gradle: for example `./gradlew installArm64Toolchain` or `./gradlew installRoboRioToolchain`
|
||||
|
||||
## Out-of-Source Dependencies
|
||||
|
||||
PhotonVision uses the following additonal out-of-source repositories for building code.
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import edu.wpi.first.toolchain.*
|
||||
|
||||
plugins {
|
||||
id "java"
|
||||
id "cpp"
|
||||
id "com.diffplug.spotless" version "6.24.0"
|
||||
id "edu.wpi.first.NativeUtils" version "2024.6.1" apply false
|
||||
id "edu.wpi.first.wpilib.repositories.WPILibRepositoriesPlugin" version "2020.2"
|
||||
id "edu.wpi.first.GradleRIO" version "2024.3.2"
|
||||
id 'edu.wpi.first.WpilibTools' version '1.3.0'
|
||||
id 'com.google.protobuf' version '0.9.4' apply false
|
||||
id 'edu.wpi.first.GradleJni' version '1.1.0'
|
||||
}
|
||||
|
||||
allprojects {
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) Photon Vision.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.photonvision.common.hardware;
|
||||
|
||||
import java.io.IOException;
|
||||
import org.photonvision.common.util.ShellExec;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class PlatformUtils {
|
||||
private static final ShellExec shell = new ShellExec(true, false);
|
||||
private static final boolean isRoot = checkForRoot();
|
||||
|
||||
@SuppressWarnings("StatementWithEmptyBody")
|
||||
private static boolean checkForRoot() {
|
||||
if (Platform.isLinux()) {
|
||||
try {
|
||||
shell.executeBashCommand("id -u");
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
while (!shell.isOutputCompleted()) {
|
||||
// TODO: add timeout
|
||||
}
|
||||
|
||||
if (shell.getExitCode() == 0) {
|
||||
return shell.getOutput().split("\n")[0].equals("0");
|
||||
}
|
||||
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isRoot() {
|
||||
return isRoot;
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import org.photonvision.common.dataflow.DataChangeService;
|
||||
import org.photonvision.common.dataflow.DataChangeSource;
|
||||
import org.photonvision.common.dataflow.events.DataChangeEvent;
|
||||
import org.photonvision.common.hardware.Platform;
|
||||
import org.photonvision.common.hardware.PlatformUtils;
|
||||
import org.photonvision.common.logging.LogGroup;
|
||||
import org.photonvision.common.logging.Logger;
|
||||
import org.photonvision.common.util.ShellExec;
|
||||
@@ -54,7 +55,7 @@ public class NetworkManager {
|
||||
var config = ConfigManager.getInstance().getConfig().getNetworkConfig();
|
||||
logger.info("Setting " + config.connectionType + " with team " + config.ntServerAddress);
|
||||
if (Platform.isLinux()) {
|
||||
if (!Platform.isRoot()) {
|
||||
if (!PlatformUtils.isRoot()) {
|
||||
logger.error("Cannot manage hostname without root!");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
plugins {
|
||||
id 'edu.wpi.first.WpilibTools' version '1.3.0'
|
||||
}
|
||||
|
||||
import java.nio.file.Path
|
||||
|
||||
ext {
|
||||
@@ -314,3 +318,24 @@ publishing {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// setup wpilib bundled native libs
|
||||
wpilibTools.deps.wpilibVersion = wpi.versions.wpilibVersion.get()
|
||||
|
||||
def nativeConfigName = 'wpilibNatives'
|
||||
def nativeConfig = configurations.create(nativeConfigName)
|
||||
|
||||
def nativeTasks = wpilibTools.createExtractionTasks {
|
||||
configurationName = nativeConfigName
|
||||
}
|
||||
|
||||
nativeTasks.addToSourceSetResources(sourceSets.test)
|
||||
|
||||
nativeConfig.dependencies.add wpilibTools.deps.wpilib("wpimath")
|
||||
nativeConfig.dependencies.add wpilibTools.deps.wpilib("wpinet")
|
||||
nativeConfig.dependencies.add wpilibTools.deps.wpilib("wpiutil")
|
||||
nativeConfig.dependencies.add wpilibTools.deps.wpilib("ntcore")
|
||||
nativeConfig.dependencies.add wpilibTools.deps.wpilib("cscore")
|
||||
nativeConfig.dependencies.add wpilibTools.deps.wpilib("apriltag")
|
||||
nativeConfig.dependencies.add wpilibTools.deps.wpilib("hal")
|
||||
nativeConfig.dependencies.add wpilibTools.deps.wpilibOpenCv("frc" + wpi.frcYear.get(), wpi.versions.opencvVersion.get())
|
||||
|
||||
@@ -47,9 +47,7 @@ tasks.register("buildAndCopyUI") {
|
||||
|
||||
run {
|
||||
environment "PATH_PREFIX", "../"
|
||||
}
|
||||
|
||||
run {
|
||||
if (project.hasProperty("profile")) {
|
||||
jvmArgs=[
|
||||
"-Dcom.sun.management.jmxremote=true",
|
||||
@@ -105,7 +103,7 @@ task findDeployTarget {
|
||||
|
||||
task deploy {
|
||||
dependsOn findDeployTarget
|
||||
dependsOn assemble
|
||||
dependsOn 'shadowJar'
|
||||
|
||||
doLast {
|
||||
println 'Starting deployment to ' + findDeployTarget.rmt.host
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
plugins {
|
||||
id 'edu.wpi.first.WpilibTools' version '1.3.0'
|
||||
}
|
||||
|
||||
ext {
|
||||
nativeName = "photontargeting"
|
||||
}
|
||||
@@ -5,6 +9,7 @@ ext {
|
||||
apply plugin: 'cpp'
|
||||
apply plugin: 'google-test-test-suite'
|
||||
apply plugin: 'edu.wpi.first.NativeUtils'
|
||||
apply plugin: 'edu.wpi.first.GradleJni'
|
||||
|
||||
apply from: "${rootDir}/shared/config.gradle"
|
||||
apply from: "${rootDir}/shared/javacommon.gradle"
|
||||
@@ -19,6 +24,20 @@ nativeUtils {
|
||||
|
||||
sourceSets.main.java.srcDir "${projectDir}/src/generated/main/java"
|
||||
|
||||
// Folder whose contents will be included in the final jar
|
||||
def outputsFolder = file("$buildDir/extra_resources")
|
||||
|
||||
// Sync task: like the copy task, but all files that exist in the destination directory will be deleted before copying files
|
||||
task syncOutputsFolder(type: Sync) {
|
||||
into outputsFolder
|
||||
}
|
||||
|
||||
// And package our outputs folder into the final jar
|
||||
jar {
|
||||
from outputsFolder
|
||||
dependsOn syncOutputsFolder
|
||||
}
|
||||
|
||||
model {
|
||||
components {
|
||||
"${nativeName}"(NativeLibrarySpec) {
|
||||
@@ -42,14 +61,59 @@ model {
|
||||
it.tasks.withType(CppCompile) {
|
||||
it.dependsOn generateProto
|
||||
}
|
||||
if(project.hasProperty('includePhotonTargeting')) {
|
||||
lib project: ':photon-targeting', library: 'photontargeting', linkage: 'shared'
|
||||
}
|
||||
|
||||
nativeUtils.useRequiredLibrary(it, "wpiutil_shared")
|
||||
nativeUtils.useRequiredLibrary(it, "wpimath_shared")
|
||||
nativeUtils.useRequiredLibrary(it, "wpinet_shared")
|
||||
nativeUtils.useRequiredLibrary(it, "wpilibc_shared")
|
||||
nativeUtils.useRequiredLibrary(it, "ntcore_shared")
|
||||
}
|
||||
"${nativeName}JNI"(JniNativeLibrarySpec) {
|
||||
|
||||
enableCheckTask project.hasProperty('doJniCheck')
|
||||
javaCompileTasks << compileJava
|
||||
jniCrossCompileOptions << JniCrossCompileOptions(nativeUtils.wpi.platforms.roborio)
|
||||
jniCrossCompileOptions << JniCrossCompileOptions(nativeUtils.wpi.platforms.linuxarm32)
|
||||
jniCrossCompileOptions << JniCrossCompileOptions(nativeUtils.wpi.platforms.linuxarm64)
|
||||
|
||||
sources {
|
||||
cpp {
|
||||
source {
|
||||
srcDirs 'src/main/native/jni'
|
||||
include '**/*.cpp', '**/*.cc'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nativeUtils.useRequiredLibrary(it, "wpilib_shared")
|
||||
nativeUtils.useRequiredLibrary(it, "apriltag_shared")
|
||||
nativeUtils.useRequiredLibrary(it, "opencv_shared")
|
||||
binaries.all {
|
||||
lib library: nativeName, linkage: 'shared'
|
||||
}
|
||||
|
||||
nativeUtils.useRequiredLibrary(it, "wpiutil_shared")
|
||||
nativeUtils.useRequiredLibrary(it, "wpinet_shared")
|
||||
nativeUtils.useRequiredLibrary(it, "ntcore_shared")
|
||||
}
|
||||
|
||||
all {
|
||||
binaries.withType(SharedLibraryBinarySpec) { binary ->
|
||||
// check that we're building for the platform (per PArchOverride/wpilib plat detection)
|
||||
if (binary.targetPlatform.name == jniPlatform) {
|
||||
|
||||
// only include release binaries (hard coded for now)
|
||||
def isDebug = binary.buildType.name.contains('debug')
|
||||
if (!isDebug) {
|
||||
syncOutputsFolder {
|
||||
// Just shove the shared library into the root of the jar output by photon-targeting:jar
|
||||
from(binary.sharedLibraryFile) {
|
||||
into "nativelibraries/${wpilibNativeName}/"
|
||||
}
|
||||
// And (not sure if this is a hack) make the jar task depend on the build task
|
||||
dependsOn binary.identifier.projectScopedName
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
testSuites {
|
||||
@@ -76,17 +140,11 @@ model {
|
||||
it.tasks.withType(CppCompile) {
|
||||
it.dependsOn generateProto
|
||||
}
|
||||
if(project.hasProperty('includePhotonTargeting')) {
|
||||
lib project: ':photon-targeting', library: 'photontargeting', linkage: 'shared'
|
||||
}
|
||||
}
|
||||
|
||||
nativeUtils.useRequiredLibrary(it, "cscore_shared")
|
||||
nativeUtils.useRequiredLibrary(it, "cameraserver_shared")
|
||||
nativeUtils.useRequiredLibrary(it, "wpilib_executable_shared")
|
||||
nativeUtils.useRequiredLibrary(it, "googletest_static")
|
||||
nativeUtils.useRequiredLibrary(it, "apriltag_shared")
|
||||
nativeUtils.useRequiredLibrary(it, "opencv_shared")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,3 +187,27 @@ cppHeadersZip {
|
||||
into '/'
|
||||
}
|
||||
}
|
||||
|
||||
// make sure native libraries can be loaded in tests
|
||||
test {
|
||||
classpath += files(outputsFolder)
|
||||
dependsOn syncOutputsFolder
|
||||
}
|
||||
|
||||
// setup wpilib bundled native libs
|
||||
wpilibTools.deps.wpilibVersion = wpi.versions.wpilibVersion.get()
|
||||
|
||||
def nativeConfigName = 'wpilibNatives'
|
||||
def nativeConfig = configurations.create(nativeConfigName)
|
||||
|
||||
def nativeTasks = wpilibTools.createExtractionTasks {
|
||||
configurationName = nativeConfigName
|
||||
}
|
||||
|
||||
nativeTasks.addToSourceSetResources(sourceSets.test)
|
||||
|
||||
nativeConfig.dependencies.add wpilibTools.deps.wpilib("wpiutil")
|
||||
nativeConfig.dependencies.add wpilibTools.deps.wpilib("wpimath")
|
||||
nativeConfig.dependencies.add wpilibTools.deps.wpilib("wpinet")
|
||||
nativeConfig.dependencies.add wpilibTools.deps.wpilib("ntcore")
|
||||
nativeConfig.dependencies.add wpilibTools.deps.wpilib("hal")
|
||||
|
||||
@@ -17,13 +17,11 @@
|
||||
|
||||
package org.photonvision.common.hardware;
|
||||
|
||||
import com.jogamp.common.os.Platform.OSType;
|
||||
import edu.wpi.first.util.RuntimeDetector;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import org.photonvision.common.util.ShellExec;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public enum Platform {
|
||||
@@ -63,7 +61,6 @@ public enum Platform {
|
||||
UNKNOWN
|
||||
}
|
||||
|
||||
private static final ShellExec shell = new ShellExec(true, false);
|
||||
public final String description;
|
||||
public final String nativeLibraryFolderName;
|
||||
public final boolean isPi;
|
||||
@@ -72,7 +69,6 @@ public enum Platform {
|
||||
|
||||
// Set once at init, shouldn't be needed after.
|
||||
private static final Platform currentPlatform = getCurrentPlatform();
|
||||
private static final boolean isRoot = checkForRoot();
|
||||
|
||||
Platform(
|
||||
String description,
|
||||
@@ -115,10 +111,6 @@ public enum Platform {
|
||||
return currentPlatform.nativeLibraryFolderName;
|
||||
}
|
||||
|
||||
public static boolean isRoot() {
|
||||
return isRoot;
|
||||
}
|
||||
|
||||
public static boolean isSupported() {
|
||||
return currentPlatform.isSupported;
|
||||
}
|
||||
@@ -131,29 +123,6 @@ public enum Platform {
|
||||
private static final String UnknownPlatformString =
|
||||
String.format("Unknown Platform. OS: %s, Architecture: %s", OS_NAME, OS_ARCH);
|
||||
|
||||
@SuppressWarnings("StatementWithEmptyBody")
|
||||
private static boolean checkForRoot() {
|
||||
if (isLinux()) {
|
||||
try {
|
||||
shell.executeBashCommand("id -u");
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
while (!shell.isOutputCompleted()) {
|
||||
// TODO: add timeout
|
||||
}
|
||||
|
||||
if (shell.getExitCode() == 0) {
|
||||
return shell.getOutput().split("\n")[0].equals("0");
|
||||
}
|
||||
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static Platform getCurrentPlatform() {
|
||||
if (RuntimeDetector.isWindows()) {
|
||||
if (RuntimeDetector.is32BitIntel()) {
|
||||
Reference in New Issue
Block a user