[starter] Add a process starter for use by the installer for launching tools (#4931)

This commit is contained in:
Thad House
2023-01-19 20:09:35 -08:00
committed by GitHub
parent a60ca9d71c
commit 657951f6dd
6 changed files with 403 additions and 0 deletions

View File

@@ -0,0 +1,84 @@
import org.gradle.internal.os.OperatingSystem
if (!project.hasProperty('onlylinuxathena')) {
description = "Process Starter"
apply plugin: 'cpp'
apply plugin: 'objective-cpp'
apply plugin: 'visual-studio'
apply plugin: 'edu.wpi.first.NativeUtils'
ext {
nativeName = 'processstarter'
}
apply from: "${rootDir}/shared/config.gradle"
// Replace shared crt with static crt.
// Note this means no wpilib binaries can be dependencies
nativeUtils.platformConfigs.named(nativeUtils.wpi.platforms.windowsx64).configure {
cppCompiler.debugArgs.remove('/MDd')
cppCompiler.debugArgs.add('/MTd')
cppCompiler.releaseArgs.remove('/MD')
cppCompiler.releaseArgs.add('/MT')
}
project(':').libraryBuild.dependsOn build
model {
components {
"${nativeName}"(NativeExecutableSpec) {
baseName = 'processstarter'
binaries.all {
if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
it.buildable = false
return
}
if (it.targetPlatform.operatingSystem.isMacOsX()) {
it.sources {
macObjCpp(ObjectiveCppSourceSet) {
source {
srcDirs 'src/main/native/osx'
include '**/*.mm'
}
exportedHeaders {
srcDirs 'src/main/native/include'
include '**/*.h'
}
}
}
} else if (it.targetPlatform.operatingSystem.isLinux()) {
it.sources {
linuxCpp(CppSourceSet) {
source {
srcDirs 'src/main/native/linux'
include '**/*.cpp'
}
exportedHeaders {
srcDirs 'src/main/native/include'
include '**/*.h'
}
}
}
} else if (it.targetPlatform.operatingSystem.isWindows()) {
it.sources {
windowsCpp(CppSourceSet) {
source {
srcDirs 'src/main/native/windows'
include '**/*.cpp'
}
exportedHeaders {
srcDirs 'src/main/native/include'
include '**/*.h'
}
}
}
}
}
}
}
}
apply from: 'publish.gradle'
}

View File

@@ -0,0 +1,67 @@
apply plugin: 'maven-publish'
def baseArtifactId = 'processstarter'
def artifactGroupId = 'edu.wpi.first.tools'
def zipBaseName = '_GROUP_edu_wpi_first_tools_ID_processstarter_CLS'
def outputsFolder = file("$project.buildDir/outputs")
model {
tasks {
// Create the run task.
$.components.processstarter.binaries.each { bin ->
if (bin.buildable && bin.name.toLowerCase().contains("debug") && nativeUtils.isNativeDesktopPlatform(bin.targetPlatform)) {
Task run = project.tasks.create("run", Exec) {
commandLine bin.tasks.install.runScriptFile.get().asFile.toString()
}
run.dependsOn bin.tasks.install
}
}
}
publishing {
def processstarterTaskList = []
$.components.each { component ->
component.binaries.each { binary ->
if (binary in NativeExecutableBinarySpec && binary.component.name.contains("processstarter")) {
if (binary.buildable && (binary.name.contains('Release') || binary.name.contains('release'))) {
// We are now in the binary that we want.
// This is the default application path for the ZIP task.
def applicationPath = binary.executable.file
// Create the ZIP.
def task = project.tasks.create("copyprocessstarterExecutable" + binary.targetPlatform.architecture.name, Zip) {
description("Copies the processstarter executable to the outputs directory.")
destinationDirectory = outputsFolder
archiveBaseName = '_M_' + zipBaseName
duplicatesStrategy = 'exclude'
classifier = nativeUtils.getPublishClassifier(binary)
from(licenseFile) {
into '/'
}
from(applicationPath)
}
task.dependsOn binary.tasks.link
processstarterTaskList.add(task)
project.build.dependsOn task
project.artifacts { task }
addTaskToCopyAllOutputs(task)
}
}
}
}
publications {
processstarter(MavenPublication) {
processstarterTaskList.each { artifact it }
artifactId = baseArtifactId
groupId = artifactGroupId
version wpilibVersioning.version.get()
}
}
}
}

View File

@@ -0,0 +1,86 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include <poll.h>
#include <spawn.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#include <climits>
#include <cstdio>
#include <cstring>
#include <filesystem>
int main(int argc, char* argv[]) {
char path[PATH_MAX];
char dest[PATH_MAX];
std::memset(dest, 0, sizeof(dest)); // readlink does not null terminate!
pid_t pid = getpid();
std::snprintf(path, PATH_MAX, "/proc/%d/exe", pid);
int readlink_len = readlink(path, dest, PATH_MAX);
if (readlink_len < 0) {
std::perror("readlink");
return 1;
} else if (readlink_len == PATH_MAX) {
std::printf("Truncation occured\n");
return 1;
}
std::filesystem::path exePath{dest};
if (exePath.empty()) {
return 1;
}
if (!exePath.has_stem()) {
return 1;
}
if (!exePath.has_parent_path()) {
return 1;
}
std::filesystem::path jarPath{exePath};
jarPath.replace_extension("jar");
std::filesystem::path parentPath{exePath.parent_path()};
if (!parentPath.has_parent_path()) {
return 1;
}
std::filesystem::path toolsFolder{parentPath.parent_path()};
std::filesystem::path Java = toolsFolder / "jdk" / "bin" / "java";
pid = 0;
std::string data = jarPath;
std::string jarArg = "-jar";
char* const arguments[] = {jarArg.data(), data.data(), nullptr};
int status =
posix_spawn(&pid, Java.c_str(), nullptr, nullptr, arguments, environ);
if (status != 0) {
char* home = std::getenv("JAVA_HOME");
std::string javaLocal = "java";
if (home != nullptr) {
std::filesystem::path javaHomePath{home};
javaHomePath /= "bin";
javaHomePath /= "java";
javaLocal = javaHomePath;
}
status = posix_spawn(&pid, javaLocal.c_str(), nullptr, nullptr, arguments,
environ);
if (status != 0) {
return 1;
}
}
int childPid = syscall(SYS_pidfd_open, pid, 0);
if (childPid <= 0) {
return 1;
}
struct pollfd pfd = {childPid, POLLIN, 0};
return poll(&pfd, 1, 3000);
}

View File

@@ -0,0 +1,80 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#import <Foundation/Foundation.h>
#include <string>
#include <filesystem>
int main(int argc, char* argv[]) {
(void)argc;
(void)argv;
NSString* exePathPlat = [[NSBundle mainBundle] bundlePath];
NSString* identifier = [[NSBundle mainBundle] bundleIdentifier];
if (identifier == nil) {
exePathPlat = [[NSBundle mainBundle] executablePath];
}
std::filesystem::path exePath{[exePathPlat UTF8String]};
if (exePath.empty()) {
return 1;
}
if (!exePath.has_stem()) {
return 1;
}
if (!exePath.has_parent_path()) {
return 1;
}
std::filesystem::path jarPath{exePath};
jarPath.replace_extension("jar");
std::filesystem::path parentPath{exePath.parent_path()};
if (!parentPath.has_parent_path()) {
return 1;
}
std::filesystem::path toolsFolder{parentPath.parent_path()};
std::filesystem::path java = toolsFolder / "jdk" / "bin" / "java";
NSArray<NSString*>* Arguments =
@[ @"-jar", [NSString stringWithFormat:@"%s", jarPath.c_str()] ];
NSTask* task = [[NSTask alloc] init];
task.launchPath = [NSString stringWithFormat:@"%s", java.c_str()];
task.arguments = Arguments;
task.terminationHandler = ^(NSTask* t) {
(void)t;
CFRunLoopStop(CFRunLoopGetMain());
};
if (![task launchAndReturnError:nil]) {
task.terminationHandler = nil;
NSString* javaHome =
[[[NSProcessInfo processInfo] environment] objectForKey:@"JAVA_HOME"];
task = [[NSTask alloc] init];
task.launchPath = @"java";
if (javaHome != nil) {
std::filesystem::path javaHomePath{[javaHome UTF8String]};
javaHomePath /= "bin";
javaHomePath /= "java";
task.launchPath = [NSString stringWithFormat:@"%s", javaHomePath.c_str()];
}
task.arguments = Arguments;
task.terminationHandler = ^(NSTask* t) {
(void)t;
CFRunLoopStop(CFRunLoopGetMain());
};
if (![task launchAndReturnError:nil]) {
return 1;
}
}
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 3, false);
return task.running ? 0 : 1;
}

View File

@@ -0,0 +1,85 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include <filesystem>
#include "Windows.h"
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPTSTR pCmdLine, int nCmdShow) {
DWORD Status;
WCHAR ExePathRaw[1024];
Status = GetModuleFileNameW(NULL, ExePathRaw, 1024);
if (Status == 0) {
DWORD LastError = GetLastError();
return LastError;
}
std::filesystem::path ExePath{ExePathRaw};
if (ExePath.empty()) {
return 1;
}
if (!ExePath.has_stem()) {
return 1;
}
if (!ExePath.has_parent_path()) {
return 1;
}
std::filesystem::path JarPath{ExePath};
JarPath.replace_extension(L"jar");
std::filesystem::path ParentPath{ExePath.parent_path()};
if (!ParentPath.has_parent_path()) {
return 1;
}
std::filesystem::path ToolsFolder{ParentPath.parent_path()};
std::filesystem::path Javaw = ToolsFolder / L"jdk" / L"bin" / L"javaw.exe";
std::wstring ToRun = L" -jar \"";
ToRun += JarPath;
ToRun += L"\"";
STARTUPINFOW StartupInfo;
PROCESS_INFORMATION ProcessInfo;
ZeroMemory(&StartupInfo, sizeof(StartupInfo));
StartupInfo.cb = sizeof(StartupInfo);
ZeroMemory(&ProcessInfo, sizeof(ProcessInfo));
if (!CreateProcessW(Javaw.c_str(), ToRun.data(), NULL, NULL, FALSE, 0, NULL,
NULL, &StartupInfo, &ProcessInfo)) {
ZeroMemory(&StartupInfo, sizeof(StartupInfo));
StartupInfo.cb = sizeof(StartupInfo);
ZeroMemory(&ProcessInfo, sizeof(ProcessInfo));
ToRun = L" -jar \"";
ToRun += JarPath;
ToRun += L"\"";
Status = GetEnvironmentVariableW(L"JAVA_HOME", ExePathRaw, 1024);
std::wstring JavawLocal = L"javaw";
if (Status != 0 && Status < 1024) {
std::filesystem::path JavaHomePath{ExePathRaw};
JavaHomePath /= "bin";
JavaHomePath /= "javaw.exe";
JavawLocal = JavaHomePath;
}
if (!CreateProcessW(JavawLocal.c_str(), ToRun.data(), NULL, NULL, FALSE, 0,
NULL, NULL, &StartupInfo, &ProcessInfo)) {
return 1;
}
}
Status =
WaitForSingleObject(ProcessInfo.hProcess, 3000); // Wait for 3 seconds
CloseHandle(ProcessInfo.hProcess);
CloseHandle(ProcessInfo.hThread);
return Status == WAIT_TIMEOUT ? 0 : 1;
}

View File

@@ -52,6 +52,7 @@ include 'docs'
include 'msvcruntime'
include 'ntcoreffi'
include 'apriltag'
include 'processstarter'
buildCache {
def cred = {