This repo is FluxEngine, a USB floppy-disk drive tool. The existing codebase is C++, and there is an active, incremental migration of components to Java. The Java side is the current focus of development. This document describes the Java build structure and the coding conventions used. Follow it when making changes.
Bazel with bzlmod. There is no WORKSPACE file — all dependency declarations live in
MODULE.bazel (rules_java, rules_jvm_external for Maven deps, rules_proto).
- Java sources:
java/(standard Bazel layout,comis a direct child ofjava) - Java tests:
javatests/ - Packages (Java):
com.cowlark.fluxengine(Main, FluxEngineComponent),com.cowlark.fluxengine.cli,com.cowlark.fluxengine.core,com.cowlark.fluxengine.core.flags,com.cowlark.fluxengine.data,com.cowlark.fluxengine.usb,com.cowlark.fluxengine.wiring - Each package directory has its own
BUILD.bazel.
Useful commands:
bazel build //java/...bazel test //javatests/...bazel run //java/com/cowlark/fluxengine:fluxengine -- <args>(JVM binary)bazel build //:fluxengine_deb //:fluxengine_rpm(jpackage .deb/.rpm installers; root aliases//:fluxengine,//:fluxengine_deb, and//:fluxengine_rpmexist)bazel build //:fluxengine_app_image(jpackage app-image, produced as a tar file)bazel build //:fluxengine_msi //:fluxengine_dmg(Windows MSI / macOS DMG installers, only buildable on their native platforms)
- Because there is no WORKSPACE, Java rules are not autoloaded. Every BUILD file must
explicitly load what it uses, e.g.
load("@rules_java//java:defs.bzl", "java_library", "java_binary", "java_plugin", "java_test"). - The
.deband.rpminstallers are built with jpackage via thejpackagerule injpackage.bzl(which uses the configured Java toolchain'sjpackage). Becauserpmbuildwrites to/var/tmpand read-only sandbox paths by default, the rule stages everything under a writableworkdir/and, for rpm, points rpmbuild's_tmppath/_builddiretc. at it via a~/.rpmmacrosfile. Thejpackage_app_imagerule produces the raw app-image directory as a tar file. - The MSI (
//:fluxengine_msi) and DMG (//:fluxengine_dmg) targets useselect()to set the jpackagepackage_typeper platform (@platforms//os:windows→msi,@platforms//os:osx→dmg); jpackage can't cross-compile, so on any other platform the type isunsupported, which makes the rule produce an empty target (sobazel build //java/...still works everywhere).
- Commands live in
com.cowlark.fluxengine.cliand implement theCommandinterface (String getHelp(),void run(ImmutableList<String> args)), receiving the tail of the argv array after the command name (modelled onsrc/fluxengine.cc'scommand_cb). Main.mainholds the command/subcommand tables asImmutableMap<String, Supplier<? extends Command>>:COMMANDS(top level),ANALYSABLES,FLUXFILEABLES,TESTABLES. The tables mirrorsrc/fluxengine.cc; unported commands map toStubCommand(name, help), which prints "not implemented yet".- Each command carries its own help text, returned by
getHelp();Main.helpprints the table by instantiating each command and callinggetHelp(). Main.dispatch(commands, args)consumes arguments until it reaches a real command, instantiates it via the supplier (TestDevicesCommand::new), and callsrun()with the tail. Group commands (analyse,fluxfile,test) areCommandGroup(subcommands, help)instances, which dispatch again on their sub-table and print extended help if nothing matches. Add new commands by updating the relevant table.
UsbFinder(java/com/cowlark/fluxengine/usb/) is the Java port oflib/usb/usbfinder.{cc,h}. It usesorg.usb4javadirectly (Device/DeviceDescriptor/DeviceHandle/LibUsb); enumeration goes through a singletonUsbContext(explicitContextviaLibUsb.init/exitwith shutdown hook) andLibUsb.getDeviceList/freeDeviceListwithrefDevice/unrefDevicesoCandidateDevice.deviceis a retainedDevice(not an open handle).HackyUsbSerialNumberResolverstill provides the Windows SetupAPI fallback.FluxEngineUsbDeviceopens the retainedDeviceto aDeviceHandle, claims interface 0, and usesLibUsb.interruptTransferforCMD_*andLibUsb.bulkTransferforDATA_*.DeviceTypeis an enum carrying its display name as a property (getDeviceName()).- jSerialComm is available for serial-port access (used by Greaseweazle/Applesauce via
SerialandfindSerialPort).
- Explicit types, no
var. - Prefer Guava utilities over hand-rolled checks:
Strings.nullToEmpty(...)instead of explicit null checks; useImmutableListfor returned collections. - Prefer
System.out.printf(...)overSystem.out.println(String.format(...)). - Tests use JUnit 4 (
@RunWith(JUnit4.class),org.junit.Test). - Follow existing patterns in the package you are editing; keep new functionality localized to the relevant package.
- Comments use block style
/* text\n * more text\n * trailing text */with leading/*and trailing*/on the same lines as the text. Wrap text to ~100 columns, merge adjacent comments into a single block, use grammatically correct English, and prefer block comments over//(convert//to block style when touching a file).
- Verify changes with
bazel build //java/...andbazel test //javatests/...(andbazel runfor CLI-visible behaviour) before finishing. - Do not commit anything to the VCS; the user will handle commits manually.