dead simple dual build target POC

This commit is contained in:
Siyuan 2025-11-12 14:16:53 +00:00
parent bca9afd1d7
commit 2f868bc36d
7 changed files with 382 additions and 59 deletions

View File

@ -42,6 +42,8 @@ FetchContent_MakeAvailable(lvgl)
# Get source files, excluding CMake generated files # Get source files, excluding CMake generated files
file(GLOB_RECURSE sources CONFIGURE_DEPENDS "*.c" "ui/*.c") file(GLOB_RECURSE sources CONFIGURE_DEPENDS "*.c" "ui/*.c")
list(FILTER sources EXCLUDE REGEX "CMakeFiles.*CompilerId.*\\.c$") list(FILTER sources EXCLUDE REGEX "CMakeFiles.*CompilerId.*\\.c$")
# Exclude main.c from library sources (it's used for the binary target)
list(FILTER sources EXCLUDE REGEX "main\\.c$")
add_library(jknative STATIC ${sources} ${CMAKE_CURRENT_SOURCE_DIR}/ctrl.h) add_library(jknative STATIC ${sources} ${CMAKE_CURRENT_SOURCE_DIR}/ctrl.h)
@ -68,4 +70,14 @@ target_link_libraries(jknative PRIVATE
# libgpiod # libgpiod
) )
# Binary target using main.c as entry point
add_executable(jknative-bin ${CMAKE_CURRENT_SOURCE_DIR}/main.c)
# Link the binary to the library (if needed in the future)
target_link_libraries(jknative-bin PRIVATE
jknative
pthread
)
install(TARGETS jknative DESTINATION lib) install(TARGETS jknative DESTINATION lib)
install(TARGETS jknative-bin DESTINATION bin)

227
internal/native/cgo/main.c Normal file
View File

@ -0,0 +1,227 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/select.h>
#include <fcntl.h>
#include <errno.h>
#include <pthread.h>
#include "ctrl.h"
#include "main.h"
#define SOCKET_PATH "/tmp/video.sock"
#define BUFFER_SIZE 4096
// Global state
static int client_fd = -1;
static pthread_mutex_t client_fd_mutex = PTHREAD_MUTEX_INITIALIZER;
void jetkvm_c_log_handler(int level, const char *filename, const char *funcname, int line, const char *message) {
// printf("[%s] %s:%d %s: %s\n", filename ? filename : "unknown", funcname ? funcname : "unknown", line, message ? message : "");
fprintf(stderr, "[%s] %s:%d %s: %s\n", filename ? filename : "unknown", funcname ? funcname : "unknown", line, message ? message : "");
}
// Video handler that pipes frames to the Unix socket
// This will be called by the video subsystem via video_send_frame -> jetkvm_set_video_handler's handler
void jetkvm_video_handler(const uint8_t *frame, ssize_t len) {
// pthread_mutex_lock(&client_fd_mutex);
// if (client_fd >= 0 && frame != NULL && len > 0) {
// ssize_t bytes_written = 0;
// while (bytes_written < len) {
// ssize_t n = write(client_fd, frame + bytes_written, len - bytes_written);
// if (n < 0) {
// if (errno == EPIPE || errno == ECONNRESET) {
// // Client disconnected
// close(client_fd);
// client_fd = -1;
// break;
// }
// perror("write");
// break;
// }
// bytes_written += n;
// }
// }
// pthread_mutex_unlock(&client_fd_mutex);
}
void jetkvm_video_state_handler(jetkvm_video_state_t *state) {
fprintf(stderr, "Video state: {\n"
"\"ready\": %d,\n"
"\"error\": \"%s\",\n"
"\"width\": %d,\n"
"\"height\": %d,\n"
"\"frame_per_second\": %f\n"
"}\n", state->ready, state->error, state->width, state->height, state->frame_per_second);
}
void jetkvm_indev_handler(int code) {
fprintf(stderr, "Video indev: %d\n", code);
}
void jetkvm_rpc_handler(const char *method, const char *params) {
fprintf(stderr, "Video rpc: %s %s\n", method, params);
}
// Note: jetkvm_set_video_handler, jetkvm_set_indev_handler, jetkvm_set_rpc_handler,
// jetkvm_call_rpc_handler, and jetkvm_set_video_state_handler are implemented in
// the library (ctrl.c) and will be used from there when linking.
int main(int argc, char *argv[]) {
const char *socket_path = SOCKET_PATH;
// Allow custom socket path via command line argument
if (argc > 1) {
socket_path = argv[1];
}
// Remove existing socket file if it exists
unlink(socket_path);
// Set handlers
jetkvm_set_log_handler(&jetkvm_c_log_handler);
jetkvm_set_video_handler(&jetkvm_video_handler);
jetkvm_set_video_state_handler(&jetkvm_video_state_handler);
jetkvm_set_indev_handler(&jetkvm_indev_handler);
jetkvm_set_rpc_handler(&jetkvm_rpc_handler);
// Initialize video first (before accepting connections)
fprintf(stderr, "Initializing video...\n");
if (jetkvm_video_init(1.0) != 0) {
fprintf(stderr, "Failed to initialize video\n");
return 1;
}
// Start video streaming - frames will be sent via video_send_frame
// which calls the video handler we set up
jetkvm_video_start();
fprintf(stderr, "Video streaming started.\n");
// Create Unix domain socket
int server_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (server_fd < 0) {
perror("socket");
jetkvm_video_stop();
jetkvm_video_shutdown();
return 1;
}
// Make socket non-blocking
int flags = fcntl(server_fd, F_GETFL, 0);
if (flags < 0 || fcntl(server_fd, F_SETFL, flags | O_NONBLOCK) < 0) {
perror("fcntl");
close(server_fd);
jetkvm_video_stop();
jetkvm_video_shutdown();
return 1;
}
// Bind socket to path
struct sockaddr_un addr;
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1);
if (bind(server_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
perror("bind");
close(server_fd);
jetkvm_video_stop();
jetkvm_video_shutdown();
return 1;
}
// Listen for connections
if (listen(server_fd, 1) < 0) {
perror("listen");
close(server_fd);
jetkvm_video_stop();
jetkvm_video_shutdown();
return 1;
}
fprintf(stderr, "Listening on Unix socket: %s (non-blocking)\n", socket_path);
fprintf(stderr, "Video frames will be sent to connected clients...\n");
// Main loop: check for new connections and handle client disconnections
fd_set read_fds;
struct timeval timeout;
while (1) {
FD_ZERO(&read_fds);
FD_SET(server_fd, &read_fds);
pthread_mutex_lock(&client_fd_mutex);
int current_client_fd = client_fd;
if (current_client_fd >= 0) {
FD_SET(current_client_fd, &read_fds);
}
int max_fd = (current_client_fd > server_fd) ? current_client_fd : server_fd;
pthread_mutex_unlock(&client_fd_mutex);
timeout.tv_sec = 1;
timeout.tv_usec = 0;
int result = select(max_fd + 1, &read_fds, NULL, NULL, &timeout);
if (result < 0) {
if (errno == EINTR) {
continue;
}
perror("select");
break;
}
// Check for new connection
if (FD_ISSET(server_fd, &read_fds)) {
int accepted_fd = accept(server_fd, NULL, NULL);
if (accepted_fd >= 0) {
fprintf(stderr, "Client connected\n");
pthread_mutex_lock(&client_fd_mutex);
if (client_fd >= 0) {
// Close previous client if any
close(client_fd);
}
client_fd = accepted_fd;
pthread_mutex_unlock(&client_fd_mutex);
} else if (errno != EAGAIN && errno != EWOULDBLOCK) {
perror("accept");
}
}
// Check if client disconnected
pthread_mutex_lock(&client_fd_mutex);
current_client_fd = client_fd;
pthread_mutex_unlock(&client_fd_mutex);
if (current_client_fd >= 0 && FD_ISSET(current_client_fd, &read_fds)) {
// Client sent data or closed connection
char buffer[1];
if (read(current_client_fd, buffer, 1) <= 0) {
fprintf(stderr, "Client disconnected\n");
pthread_mutex_lock(&client_fd_mutex);
close(client_fd);
client_fd = -1;
pthread_mutex_unlock(&client_fd_mutex);
}
}
}
// Stop video streaming
jetkvm_video_stop();
jetkvm_video_shutdown();
// Cleanup
pthread_mutex_lock(&client_fd_mutex);
if (client_fd >= 0) {
close(client_fd);
client_fd = -1;
}
pthread_mutex_unlock(&client_fd_mutex);
close(server_fd);
unlink(socket_path);
return 0;
}

View File

@ -0,0 +1,26 @@
#ifndef JETKVM_NATIVE_MAIN_H
#define JETKVM_NATIVE_MAIN_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include "ctrl.h"
void jetkvm_c_log_handler(int level, const char *filename, const char *funcname, int line, const char *message);
void jetkvm_video_handler(const uint8_t *frame, ssize_t len);
void jetkvm_video_state_handler(jetkvm_video_state_t *state);
void jetkvm_indev_handler(int code);
void jetkvm_rpc_handler(const char *method, const char *params);
// typedef void (jetkvm_video_state_handler_t)(jetkvm_video_state_t *state);
// typedef void (jetkvm_log_handler_t)(int level, const char *filename, const char *funcname, int line, const char *message);
// typedef void (jetkvm_rpc_handler_t)(const char *method, const char *params);
// typedef void (jetkvm_video_handler_t)(const uint8_t *frame, ssize_t len);
// typedef void (jetkvm_indev_handler_t)(int code);
#endif

View File

@ -28,11 +28,11 @@ type Native struct {
} }
type NativeOptions struct { type NativeOptions struct {
Disable bool `env:"JETKVM_NATIVE_DISABLE"` Disable bool
SystemVersion *semver.Version `env:"JETKVM_NATIVE_SYSTEM_VERSION"` SystemVersion *semver.Version
AppVersion *semver.Version `env:"JETKVM_NATIVE_APP_VERSION"` AppVersion *semver.Version
DisplayRotation uint16 `env:"JETKVM_NATIVE_DISPLAY_ROTATION"` DisplayRotation uint16
DefaultQualityFactor float64 `env:"JETKVM_NATIVE_DEFAULT_QUALITY_FACTOR"` DefaultQualityFactor float64
OnVideoStateChange func(state VideoState) OnVideoStateChange func(state VideoState)
OnVideoFrameReceived func(frame []byte, duration time.Duration) OnVideoFrameReceived func(frame []byte, duration time.Duration)
OnIndevEvent func(event string) OnIndevEvent func(event string)

View File

@ -9,7 +9,8 @@ import (
"syscall" "syscall"
"time" "time"
"github.com/jetkvm/kvm/internal/supervisor" "github.com/Masterminds/semver/v3"
"github.com/jetkvm/kvm/internal/utils"
"github.com/rs/zerolog" "github.com/rs/zerolog"
) )
@ -17,6 +18,48 @@ const (
maxFrameSize = 1920 * 1080 / 2 maxFrameSize = 1920 * 1080 / 2
) )
type nativeProxyOptions struct {
Disable bool `env:"JETKVM_NATIVE_DISABLE"`
SystemVersion *semver.Version `env:"JETKVM_NATIVE_SYSTEM_VERSION"`
AppVersion *semver.Version `env:"JETKVM_NATIVE_APP_VERSION"`
DisplayRotation uint16 `env:"JETKVM_NATIVE_DISPLAY_ROTATION"`
DefaultQualityFactor float64 `env:"JETKVM_NATIVE_DEFAULT_QUALITY_FACTOR"`
CtrlUnixSocket string `env:"JETKVM_NATIVE_CTRL_UNIX_SOCKET"`
VideoStreamUnixSocket string `env:"JETKVM_NATIVE_VIDEO_STREAM_UNIX_SOCKET"`
BinaryPath string `env:"JETKVM_NATIVE_BINARY_PATH"`
LoggerLevel zerolog.Level `env:"JETKVM_NATIVE_LOGGER_LEVEL"`
HandshakeMessage string `env:"JETKVM_NATIVE_HANDSHAKE_MESSAGE"`
OnVideoFrameReceived func(frame []byte, duration time.Duration)
OnIndevEvent func(event string)
OnRpcEvent func(event string)
OnVideoStateChange func(state VideoState)
}
func (n *NativeOptions) toProxyOptions() *nativeProxyOptions {
return &nativeProxyOptions{
Disable: n.Disable,
SystemVersion: n.SystemVersion,
AppVersion: n.AppVersion,
DisplayRotation: n.DisplayRotation,
DefaultQualityFactor: n.DefaultQualityFactor,
OnVideoFrameReceived: n.OnVideoFrameReceived,
OnIndevEvent: n.OnIndevEvent,
OnRpcEvent: n.OnRpcEvent,
OnVideoStateChange: n.OnVideoStateChange,
}
}
func (p *nativeProxyOptions) toNativeOptions() *NativeOptions {
return &NativeOptions{
Disable: p.Disable,
SystemVersion: p.SystemVersion,
AppVersion: p.AppVersion,
DisplayRotation: p.DisplayRotation,
DefaultQualityFactor: p.DefaultQualityFactor,
}
}
// cmdWrapper wraps exec.Cmd to implement processCmd interface // cmdWrapper wraps exec.Cmd to implement processCmd interface
type cmdWrapper struct { type cmdWrapper struct {
*exec.Cmd *exec.Cmd
@ -55,23 +98,17 @@ type NativeProxy struct {
cmd *cmdWrapper cmd *cmdWrapper
logger *zerolog.Logger logger *zerolog.Logger
ready chan struct{} ready chan struct{}
options *NativeOptions options *nativeProxyOptions
restartM sync.Mutex restartM sync.Mutex
stopped bool stopped bool
processWait chan error processWait chan error
} }
func ensureDirectoryExists(path string) error {
if _, err := os.Stat(path); os.IsNotExist(err) {
return os.MkdirAll(path, 0600)
}
return nil
}
// NewNativeProxy creates a new NativeProxy that spawns a separate process // NewNativeProxy creates a new NativeProxy that spawns a separate process
func NewNativeProxy(opts NativeOptions) (*NativeProxy, error) { func NewNativeProxy(opts NativeOptions) (*NativeProxy, error) {
nativeUnixSocket := "jetkvm-native-grpc" proxyOptions := opts.toProxyOptions()
videoStreamUnixSocket := "@jetkvm-native-video-stream" proxyOptions.CtrlUnixSocket = "jetkvm-native-grpc"
proxyOptions.VideoStreamUnixSocket = "@jetkvm-native-video-stream"
// Get the current executable path to spawn itself // Get the current executable path to spawn itself
exePath, err := os.Executable() exePath, err := os.Executable()
@ -80,12 +117,12 @@ func NewNativeProxy(opts NativeOptions) (*NativeProxy, error) {
} }
proxy := &NativeProxy{ proxy := &NativeProxy{
nativeUnixSocket: nativeUnixSocket, nativeUnixSocket: proxyOptions.CtrlUnixSocket,
videoStreamUnixSocket: videoStreamUnixSocket, videoStreamUnixSocket: proxyOptions.VideoStreamUnixSocket,
binaryPath: exePath, binaryPath: exePath,
logger: nativeLogger, logger: nativeLogger,
ready: make(chan struct{}), ready: make(chan struct{}),
options: &opts, options: proxyOptions,
processWait: make(chan error, 1), processWait: make(chan error, 1),
} }
proxy.cmd, err = proxy.spawnProcess() proxy.cmd, err = proxy.spawnProcess()
@ -95,7 +132,7 @@ func NewNativeProxy(opts NativeOptions) (*NativeProxy, error) {
} }
// create unix packet // create unix packet
listener, err := net.Listen("unixpacket", videoStreamUnixSocket) listener, err := net.Listen("unixpacket", proxyOptions.VideoStreamUnixSocket)
if err != nil { if err != nil {
nativeLogger.Warn().Err(err).Msg("failed to start server") nativeLogger.Warn().Err(err).Msg("failed to start server")
return nil, fmt.Errorf("failed to start server: %w", err) return nil, fmt.Errorf("failed to start server: %w", err)
@ -116,6 +153,11 @@ func NewNativeProxy(opts NativeOptions) (*NativeProxy, error) {
} }
func (p *NativeProxy) spawnProcess() (*cmdWrapper, error) { func (p *NativeProxy) spawnProcess() (*cmdWrapper, error) {
envArgs, err := utils.MarshalEnv(p.options)
if err != nil {
return nil, fmt.Errorf("failed to marshal environment variables: %w", err)
}
cmd := exec.Command( cmd := exec.Command(
p.binaryPath, p.binaryPath,
"-subcomponent=native", "-subcomponent=native",
@ -125,13 +167,7 @@ func (p *NativeProxy) spawnProcess() (*cmdWrapper, error) {
// Set environment variable to indicate native process mode // Set environment variable to indicate native process mode
cmd.Env = append( cmd.Env = append(
os.Environ(), os.Environ(),
fmt.Sprintf("%s=native", supervisor.EnvSubcomponent), envArgs...,
fmt.Sprintf("%s=%s", "JETKVM_NATIVE_SOCKET", p.nativeUnixSocket),
fmt.Sprintf("%s=%s", "JETKVM_VIDEO_STREAM_SOCKET", p.videoStreamUnixSocket),
fmt.Sprintf("%s=%s", "JETKVM_NATIVE_SYSTEM_VERSION", p.options.SystemVersion),
fmt.Sprintf("%s=%s", "JETKVM_NATIVE_APP_VERSION", p.options.AppVersion),
fmt.Sprintf("%s=%d", "JETKVM_NATIVE_DISPLAY_ROTATION", p.options.DisplayRotation),
fmt.Sprintf("%s=%f", "JETKVM_NATIVE_DEFAULT_QUALITY_FACTOR", p.options.DefaultQualityFactor),
) )
// Wrap cmd to implement processCmd interface // Wrap cmd to implement processCmd interface
wrappedCmd := &cmdWrapper{Cmd: cmd} wrappedCmd := &cmdWrapper{Cmd: cmd}

View File

@ -10,7 +10,6 @@ import (
"github.com/caarlos0/env/v11" "github.com/caarlos0/env/v11"
"github.com/erikdubbelboer/gspt" "github.com/erikdubbelboer/gspt"
"github.com/rs/zerolog"
) )
// Native Process // Native Process
@ -19,31 +18,24 @@ import (
// RunNativeProcess runs the native process mode // RunNativeProcess runs the native process mode
func RunNativeProcess(binaryName string) { func RunNativeProcess(binaryName string) {
logger := *nativeLogger
// Initialize logger // Initialize logger
logger := zerolog.New(os.Stderr).With().Timestamp().Logger()
gspt.SetProcTitle(binaryName + " [native]") gspt.SetProcTitle(binaryName + " [native]")
// Determine socket path var proxyOptions nativeProxyOptions
socketPath := os.Getenv("JETKVM_NATIVE_SOCKET") if err := env.Parse(&proxyOptions); err != nil {
videoStreamSocketPath := os.Getenv("JETKVM_VIDEO_STREAM_SOCKET")
if socketPath == "" || videoStreamSocketPath == "" {
logger.Fatal().Str("socket_path", socketPath).Str("video_stream_socket_path", videoStreamSocketPath).Msg("socket path or video stream socket path is not set")
}
// connect to video stream socket
conn, err := net.Dial("unixpacket", videoStreamSocketPath)
if err != nil {
logger.Fatal().Err(err).Msg("failed to connect to video stream socket")
}
logger.Info().Str("video_stream_socket_path", videoStreamSocketPath).Msg("connected to video stream socket")
var nativeOptions NativeOptions
if err := env.Parse(&nativeOptions); err != nil {
logger.Fatal().Err(err).Msg("failed to parse native options") logger.Fatal().Err(err).Msg("failed to parse native options")
} }
// connect to video stream socket
conn, err := net.Dial("unixpacket", proxyOptions.VideoStreamUnixSocket)
if err != nil {
logger.Fatal().Err(err).Msg("failed to connect to video stream socket")
}
logger.Info().Str("video_stream_socket_path", proxyOptions.VideoStreamUnixSocket).Msg("connected to video stream socket")
nativeOptions := proxyOptions.toNativeOptions()
nativeOptions.OnVideoFrameReceived = func(frame []byte, duration time.Duration) { nativeOptions.OnVideoFrameReceived = func(frame []byte, duration time.Duration) {
_, err := conn.Write(frame) _, err := conn.Write(frame)
if err != nil { if err != nil {
@ -52,7 +44,7 @@ func RunNativeProcess(binaryName string) {
} }
// Create native instance // Create native instance
nativeInstance := NewNative(nativeOptions) nativeInstance := NewNative(*nativeOptions)
// Start native instance // Start native instance
if err := nativeInstance.Start(); err != nil { if err := nativeInstance.Start(); err != nil {
@ -64,14 +56,14 @@ func RunNativeProcess(binaryName string) {
logger.Info().Msg("starting gRPC server") logger.Info().Msg("starting gRPC server")
// Start gRPC server // Start gRPC server
server, lis, err := StartGRPCServer(grpcServer, fmt.Sprintf("@%v", socketPath), &logger) server, lis, err := StartGRPCServer(grpcServer, fmt.Sprintf("@%v", proxyOptions.CtrlUnixSocket), &logger)
if err != nil { if err != nil {
logger.Fatal().Err(err).Msg("failed to start gRPC server") logger.Fatal().Err(err).Msg("failed to start gRPC server")
} }
gspt.SetProcTitle(binaryName + " [native] ready") gspt.SetProcTitle(binaryName + " [native] ready")
// Signal that we're ready by writing socket path to stdout (for parent to read) // Signal that we're ready by writing socket path to stdout (for parent to read)
fmt.Fprintf(os.Stdout, "%s\n", socketPath) fmt.Fprintf(os.Stdout, "%s\n", proxyOptions.CtrlUnixSocket)
defer os.Stdout.Close() defer os.Stdout.Close()
// Set up signal handling // Set up signal handling

View File

@ -18,6 +18,7 @@ show_help() {
echo " --skip-native-build Skip native build" echo " --skip-native-build Skip native build"
echo " --disable-docker Disable docker build" echo " --disable-docker Disable docker build"
echo " --enable-sync-trace Enable sync trace (do not use in release builds)" echo " --enable-sync-trace Enable sync trace (do not use in release builds)"
echo " --native-binary Build and deploy the native binary (FOR DEBUGGING ONLY)"
echo " -i, --install Build for release and install the app" echo " -i, --install Build for release and install the app"
echo " --help Display this help message" echo " --help Display this help message"
echo echo
@ -58,6 +59,7 @@ REMOTE_PATH="/userdata/jetkvm/bin"
SKIP_UI_BUILD=false SKIP_UI_BUILD=false
SKIP_UI_BUILD_RELEASE=0 SKIP_UI_BUILD_RELEASE=0
SKIP_NATIVE_BUILD=0 SKIP_NATIVE_BUILD=0
BUILD_NATIVE_BINARY=false
ENABLE_SYNC_TRACE=0 ENABLE_SYNC_TRACE=0
RESET_USB_HID_DEVICE=false RESET_USB_HID_DEVICE=false
LOG_TRACE_SCOPES="${LOG_TRACE_SCOPES:-jetkvm,cloud,websocket,native,jsonrpc}" LOG_TRACE_SCOPES="${LOG_TRACE_SCOPES:-jetkvm,cloud,websocket,native,jsonrpc}"
@ -113,6 +115,10 @@ while [[ $# -gt 0 ]]; do
RUN_GO_TESTS=true RUN_GO_TESTS=true
shift shift
;; ;;
--native-binary)
BUILD_NATIVE_BINARY=true
shift
;;
-i|--install) -i|--install)
INSTALL_APP=true INSTALL_APP=true
shift shift
@ -141,6 +147,9 @@ fi
# Check device connectivity before proceeding # Check device connectivity before proceeding
check_ping "${REMOTE_HOST}" check_ping "${REMOTE_HOST}"
check_ssh "${REMOTE_USER}" "${REMOTE_HOST}" check_ssh "${REMOTE_USER}" "${REMOTE_HOST}"
function sshdev() {
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${REMOTE_USER}@${REMOTE_HOST}" $@
}
# check if the current CPU architecture is x86_64 # check if the current CPU architecture is x86_64
if [ "$(uname -m)" != "x86_64" ]; then if [ "$(uname -m)" != "x86_64" ]; then
@ -152,6 +161,27 @@ if [ "$BUILD_IN_DOCKER" = true ]; then
build_docker_image build_docker_image
fi fi
if [ "$BUILD_NATIVE_BINARY" = true ]; then
msg_info "▶ Building native binary"
make build_native
sshdev "killall -9 jetkvm_app jetkvm_app_debug jetkvm_native_debug || true"
sshdev "cat > ${REMOTE_PATH}/jetkvm_native_debug" < internal/native/cgo/build/jknative-bin
sshdev ash << EOF
set -e
# Set the library path to include the directory where librockit.so is located
export LD_LIBRARY_PATH=/oem/usr/lib:\$LD_LIBRARY_PATH
cd ${REMOTE_PATH}
killall -9 jetkvm_app jetkvm_app_debug jetkvm_native_debug || true
sleep 5
echo 'V' > /dev/watchdog
chmod +x jetkvm_native_debug
./jetkvm_native_debug
EOF
exit 0
fi
# Build the development version on the host # Build the development version on the host
# When using `make build_release`, the frontend will be built regardless of the `SKIP_UI_BUILD` flag # When using `make build_release`, the frontend will be built regardless of the `SKIP_UI_BUILD` flag
# check if static/index.html exists # check if static/index.html exists
@ -176,10 +206,10 @@ if [ "$RUN_GO_TESTS" = true ]; then
make build_dev_test make build_dev_test
msg_info "▶ Copying device-tests.tar.gz to remote host" msg_info "▶ Copying device-tests.tar.gz to remote host"
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${REMOTE_USER}@${REMOTE_HOST}" "cat > /tmp/device-tests.tar.gz" < device-tests.tar.gz sshdev "cat > /tmp/device-tests.tar.gz" < device-tests.tar.gz
msg_info "▶ Running go tests" msg_info "▶ Running go tests"
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${REMOTE_USER}@${REMOTE_HOST}" ash << 'EOF' sshdev ash << 'EOF'
set -e set -e
TMP_DIR=$(mktemp -d) TMP_DIR=$(mktemp -d)
cd ${TMP_DIR} cd ${TMP_DIR}
@ -222,10 +252,10 @@ then
ENABLE_SYNC_TRACE=${ENABLE_SYNC_TRACE} ENABLE_SYNC_TRACE=${ENABLE_SYNC_TRACE}
# Copy the binary to the remote host as if we were the OTA updater. # Copy the binary to the remote host as if we were the OTA updater.
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${REMOTE_USER}@${REMOTE_HOST}" "cat > /userdata/jetkvm/jetkvm_app.update" < bin/jetkvm_app sshdev "cat > /userdata/jetkvm/jetkvm_app.update" < bin/jetkvm_app
# Reboot the device, the new app will be deployed by the startup process. # Reboot the device, the new app will be deployed by the startup process.
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${REMOTE_USER}@${REMOTE_HOST}" "reboot" sshdev "reboot"
else else
msg_info "▶ Building development binary" msg_info "▶ Building development binary"
do_make build_dev \ do_make build_dev \
@ -234,21 +264,21 @@ else
ENABLE_SYNC_TRACE=${ENABLE_SYNC_TRACE} ENABLE_SYNC_TRACE=${ENABLE_SYNC_TRACE}
# Kill any existing instances of the application # Kill any existing instances of the application
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${REMOTE_USER}@${REMOTE_HOST}" "killall jetkvm_app_debug || true" sshdev "killall jetkvm_app_debug || true"
# Copy the binary to the remote host # Copy the binary to the remote host
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${REMOTE_USER}@${REMOTE_HOST}" "cat > ${REMOTE_PATH}/jetkvm_app_debug" < bin/jetkvm_app sshdev "cat > ${REMOTE_PATH}/jetkvm_app_debug" < bin/jetkvm_app
if [ "$RESET_USB_HID_DEVICE" = true ]; then if [ "$RESET_USB_HID_DEVICE" = true ]; then
msg_info "▶ Resetting USB HID device" msg_info "▶ Resetting USB HID device"
msg_warn "The option has been deprecated and will be removed in a future version, as JetKVM will now reset USB gadget configuration when needed" msg_warn "The option has been deprecated and will be removed in a future version, as JetKVM will now reset USB gadget configuration when needed"
# Remove the old USB gadget configuration # Remove the old USB gadget configuration
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${REMOTE_USER}@${REMOTE_HOST}" "rm -rf /sys/kernel/config/usb_gadget/jetkvm/configs/c.1/hid.usb*" sshdev "rm -rf /sys/kernel/config/usb_gadget/jetkvm/configs/c.1/hid.usb*"
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${REMOTE_USER}@${REMOTE_HOST}" "ls /sys/class/udc > /sys/kernel/config/usb_gadget/jetkvm/UDC" sshdev "ls /sys/class/udc > /sys/kernel/config/usb_gadget/jetkvm/UDC"
fi fi
# Deploy and run the application on the remote host # Deploy and run the application on the remote host
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${REMOTE_USER}@${REMOTE_HOST}" ash << EOF sshdev ash << EOF
set -e set -e
# Set the library path to include the directory where librockit.so is located # Set the library path to include the directory where librockit.so is located