Skip to content

Commit 44441da

Browse files
authored
[wpilib] Use reflection to load main class, remove main from templates (#8627)
#8626 needs to switch to using reflection to load the robot class. Do that with this PR so it's separate. Also, remove the duplicated main files from the template, and instead fixup vscode to handle this properly.
1 parent 793b0a3 commit 44441da

File tree

93 files changed

+167
-2140
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

93 files changed

+167
-2140
lines changed

developerRobot/src/main/java/wpilib/robot/Main.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@ private Main() {}
1515
* <p>If you change your main robot class, change the parameter type.
1616
*/
1717
public static void main(String... args) {
18-
RobotBase.startRobot(Robot::new);
18+
RobotBase.startRobot(Robot.class);
1919
}
2020
}

shared/examplecheck.gradle

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ task checkTemplates(type: Task) {
8282
assert it.gradlebase != null
8383
assert it.commandversion != null
8484
if (it.gradlebase == 'java') {
85-
assert it.mainclass != null
85+
assert it.robotclass != null
8686
}
8787
}
8888
}
@@ -101,7 +101,7 @@ task checkExamples(type: Task) {
101101
assert it.gradlebase != null
102102
assert it.commandversion != null
103103
if (it.gradlebase == 'java') {
104-
assert it.mainclass != null
104+
assert it.robotclass != null
105105
}
106106
}
107107
}
@@ -119,7 +119,7 @@ task checkSnippets(type: Task) {
119119
assert it.foldername != null
120120
assert it.gradlebase != null
121121
if (it.gradlebase == 'java') {
122-
assert it.mainclass != null
122+
assert it.robotclass != null
123123
}
124124
}
125125
}

wpilibcExamples/src/main/cpp/examples/examples.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -520,7 +520,6 @@
520520
],
521521
"foldername": "Xrptimed",
522522
"gradlebase": "cppxrp",
523-
"mainclass": "Main",
524523
"commandversion": 2,
525524
"extravendordeps": [
526525
"xrp"

wpilibj/src/main/java/org/wpilib/framework/RobotBase.java

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
package org.wpilib.framework;
66

7+
import java.lang.reflect.Constructor;
78
import java.util.concurrent.locks.ReentrantLock;
8-
import java.util.function.Supplier;
99
import org.wpilib.driverstation.DriverStation;
1010
import org.wpilib.hardware.hal.HAL;
1111
import org.wpilib.hardware.hal.HALUtil;
@@ -285,13 +285,39 @@ public static String getOpMode() {
285285
private static RobotBase m_robotCopy;
286286
private static boolean m_suppressExitWarning;
287287

288+
private static <T extends RobotBase> T constructRobot(Class<T> robotClass) throws Throwable {
289+
Constructor<?>[] constructors = robotClass.getConstructors();
290+
Constructor<?> defaultConstructor = null;
291+
for (Constructor<?> constructor : constructors) {
292+
Class<?>[] paramTypes = constructor.getParameterTypes();
293+
if (paramTypes.length == 0) {
294+
if (defaultConstructor != null) {
295+
throw new IllegalArgumentException(
296+
"Multiple default constructors found in robot class " + robotClass.getName());
297+
}
298+
defaultConstructor = constructor;
299+
}
300+
}
301+
302+
T robot;
303+
304+
if (defaultConstructor != null) {
305+
robot = robotClass.cast(defaultConstructor.newInstance());
306+
} else {
307+
throw new IllegalArgumentException(
308+
"No valid constructor found in robot class " + robotClass.getName());
309+
}
310+
311+
return robot;
312+
}
313+
288314
/** Run the robot main loop. */
289-
private static <T extends RobotBase> void runRobot(Supplier<T> robotSupplier) {
315+
private static <T extends RobotBase> void runRobot(Class<T> robotClass) {
290316
System.out.println("********** Robot program starting **********");
291317

292318
T robot;
293319
try {
294-
robot = robotSupplier.get();
320+
robot = constructRobot(robotClass);
295321
} catch (Throwable throwable) {
296322
Throwable cause = throwable.getCause();
297323
if (cause != null) {
@@ -366,10 +392,9 @@ public static void suppressExitWarning(boolean value) {
366392
/**
367393
* Starting point for the applications.
368394
*
369-
* @param <T> Robot subclass.
370-
* @param robotSupplier Function that returns an instance of the robot subclass.
395+
* @param robotClass Robot subclass type.
371396
*/
372-
public static <T extends RobotBase> void startRobot(Supplier<T> robotSupplier) {
397+
public static void startRobot(Class<? extends RobotBase> robotClass) {
373398
// Check that the MSVC runtime is valid.
374399
WPIUtilJNI.checkMsvcRuntime();
375400

@@ -391,7 +416,7 @@ public static <T extends RobotBase> void startRobot(Supplier<T> robotSupplier) {
391416
Thread thread =
392417
new Thread(
393418
() -> {
394-
runRobot(robotSupplier);
419+
runRobot(robotClass);
395420
HAL.exitMain();
396421
},
397422
"robot main");
@@ -411,7 +436,7 @@ public static <T extends RobotBase> void startRobot(Supplier<T> robotSupplier) {
411436
Thread.currentThread().interrupt();
412437
}
413438
} else {
414-
runRobot(robotSupplier);
439+
runRobot(robotClass);
415440
}
416441

417442
// On RIO, this will just terminate rather than shutting down cleanly (it's a no-op in sim).

wpilibjExamples/build.gradle

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,10 @@ model {
177177
new groovy.json.JsonSlurper().parseText(exampleFile.text).each { entry ->
178178
project.tasks.create("run${entry.foldername}", JavaExec) { run ->
179179
run.group = "run examples"
180-
run.mainClass = "org.wpilib.examples." + entry.foldername + "." + entry.mainclass
180+
run.mainClass = "org.wpilib.Executor"
181+
run.args = [
182+
"org.wpilib.examples." + entry.foldername + "." + entry.robotclass
183+
]
181184
run.classpath = sourceSets.main.runtimeClasspath
182185
run.dependsOn it.tasks.install
183186
run.systemProperty 'java.library.path', filePath
@@ -222,7 +225,10 @@ model {
222225
new groovy.json.JsonSlurper().parseText(snippetsFile.text).each { entry ->
223226
project.tasks.create("runSnippet${entry.foldername}", JavaExec) { run ->
224227
run.group = "run snippets"
225-
run.mainClass = "org.wpilib.snippets." + entry.foldername + "." + entry.mainclass
228+
run.mainClass = "org.wpilib.Executor"
229+
run.args = [
230+
"org.wpilib.snippets." + entry.foldername + "." + entry.robotclass
231+
]
226232
run.classpath = sourceSets.main.runtimeClasspath
227233
run.dependsOn it.tasks.install
228234
run.systemProperty 'java.library.path', filePath

wpilibjExamples/publish.gradle

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ def commandsZipBaseName = '_GROUP_org_wpilib_wpilibj_ID_commands_CLS'
1111

1212
def outputsFolder = file("$project.buildDir/outputs")
1313

14+
def mainFile = file("$projectDir/src/main/java/org/wpilib/Main.java")
15+
1416
task javaExamplesZip(type: Zip) {
1517
destinationDirectory = outputsFolder
1618
archiveBaseName = examplesZipBaseName
@@ -19,6 +21,10 @@ task javaExamplesZip(type: Zip) {
1921
into '/'
2022
}
2123

24+
from(mainFile) {
25+
into '/'
26+
}
27+
2228
from('src/main/java/org/wpilib/examples') {
2329
into 'examples'
2430
}
@@ -36,6 +42,10 @@ task javaTemplatesZip(type: Zip) {
3642
into '/'
3743
}
3844

45+
from(mainFile) {
46+
into '/'
47+
}
48+
3949
from('src/main/java/org/wpilib/templates') {
4050
into 'templates'
4151
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright (c) FIRST and other WPILib contributors.
2+
// Open Source Software; you can modify and/or share it under the terms of
3+
// the WPILib BSD license file in the root directory of this project.
4+
5+
package org.wpilib;
6+
7+
import org.wpilib.framework.RobotBase;
8+
9+
/** This is the executor to launch template projects. */
10+
public final class Executor {
11+
private Executor() {}
12+
13+
/** Main initialization function. */
14+
public static void main(String... args) throws Throwable {
15+
// Load the class file for the robot.
16+
String packagePath = args[0];
17+
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
18+
19+
System.out.println("Loading robot class: " + packagePath);
20+
21+
Class<?> robotClass = classLoader.loadClass(packagePath);
22+
23+
System.out.println("Starting robot: " + robotClass.getName());
24+
25+
RobotBase.startRobot(robotClass.asSubclass(RobotBase.class));
26+
}
27+
}

wpilibjExamples/src/main/java/org/wpilib/examples/addressableled/Main.java renamed to wpilibjExamples/src/main/java/org/wpilib/Main.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Open Source Software; you can modify and/or share it under the terms of
33
// the WPILib BSD license file in the root directory of this project.
44

5-
package org.wpilib.examples.addressableled;
5+
package org.wpilib;
66

77
import org.wpilib.framework.RobotBase;
88

@@ -20,6 +20,6 @@ private Main() {}
2020
* <p>If you change your main robot class, change the parameter type.
2121
*/
2222
public static void main(String... args) {
23-
RobotBase.startRobot(Robot::new);
23+
RobotBase.startRobot(org.wpilib.templates.timed.Robot.class);
2424
}
2525
}

wpilibjExamples/src/main/java/org/wpilib/examples/apriltagsvision/Main.java

Lines changed: 0 additions & 25 deletions
This file was deleted.

wpilibjExamples/src/main/java/org/wpilib/examples/arcadedrive/Main.java

Lines changed: 0 additions & 25 deletions
This file was deleted.

0 commit comments

Comments
 (0)