Skip to content

Commit da6f23a

Browse files
committed
support linux
1 parent 4db3af1 commit da6f23a

13 files changed

+1062
-52
lines changed

CMakeLists.txt

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
cmake_minimum_required(VERSION 3.15)
1+
cmake_minimum_required(VERSION 3.10)
22

33
if (NOT DEFINED APP_NAME)
44
message(FATAL_ERROR "Application name has not been defined")
@@ -57,7 +57,7 @@ endif ()
5757
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/CMake)
5858

5959
project(launcher
60-
VERSION 2.1
60+
VERSION 2.2
6161
LANGUAGES C)
6262

6363
include(VersionParser)
@@ -81,13 +81,20 @@ if (WIN32 AND MSVC)
8181
${CMAKE_CURRENT_BINARY_DIR}/launcher.rc
8282
@ONLY
8383
)
84-
add_executable(${APP_EXE_NAME} launcher_win_main.c ${CMAKE_CURRENT_BINARY_DIR}/launcher.rc ${CMAKE_CURRENT_BINARY_DIR}/launcher_config.h launcher.h launcher_win.h launcher_win.c launcher.c)
84+
add_executable(${APP_EXE_NAME} launcher_win_main.c ${CMAKE_CURRENT_BINARY_DIR}/launcher.rc ${CMAKE_CURRENT_BINARY_DIR}/launcher_config.h launcher.h launcher_win.c launcher.c)
8585
target_link_libraries(${APP_EXE_NAME} pathcch)
8686
if (NOT DEFINED APP_SHOW_CONSOLE)
8787
set_property(TARGET ${APP_EXE_NAME} PROPERTY WIN32_EXECUTABLE true)
8888
else ()
8989
add_compile_definitions("APPLICATION_SHOW_CONSOLE")
9090
endif ()
91-
elseif ()
91+
elseif (UNIX)
92+
add_executable(${APP_EXE_NAME}
93+
${CMAKE_CURRENT_BINARY_DIR}/launcher_config.h
94+
launcher.h launcher.c launcher_linux.c launcher_linux_main.c
95+
whereami/whereami.h whereami/whereami.c
96+
)
97+
target_link_libraries(${APP_EXE_NAME} dl)
98+
else ()
9299
message(FATAL_ERROR "Unsupported platform")
93100
endif ()

LICENSE

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright 2020 Glavo
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is
8+
furnished to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all
11+
copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19+
SOFTWARE.

README.md

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,19 @@ A lightweight Java GUI application launcher, you can easily customize the launch
44
The purpose of this project is to work with jre generated by [jlink](https://docs.oracle.com/en/java/javase/11/tools/jlink.html).
55
This launcher starts the JVM using the JNI invocation api, does not depend on java.exe / javaw.exe, you can exclude them when using jlink by adding the `--strip-native-commands` option.
66

7-
This project only supports Windows at the moment. Launching applications with this launcher can avoid opening a console, and the task manager shows the application name instead of the OpenJDK process name (i.e. *OpenJDK Platform binary*).
7+
Launching applications with this launcher can avoid opening a console, and the task manager shows the application name instead of the OpenJDK process name (i.e. *OpenJDK Platform binary*).
88

99
![a launcher file details properties](Screenshot-001.png)
1010

1111
![Task Manager](Screenshot-002.png)
1212

1313
## Build your launcher
1414

15-
Before building the launcher, please make sure that CMake and MSVC are installed on your computer, and the console environment is configured using the vcvars script.
15+
Before building the launcher, please make sure that CMake is installed on your computer,
16+
and installed the necessary toolchains to build.
1617

17-
Build your launcher in the console (replace `<build-dir>` with where you want to build the launcher, replace `<sources-root>` with the source directory of the launcher, replace `<build-options>` with your [build options](#Build Options):
18+
Build your launcher in the console (replace `<build-dir>` with where you want to build the launcher,
19+
replace `<sources-root>` with the source directory of the launcher, replace `<build-options>` with your [build options](#Build Options):
1820

1921
```batch
2022
cd <build-dir>
@@ -41,13 +43,13 @@ You can customize the generated launcher by passing build options.
4143
| Option | Description |
4244
| -------- | ------------ |
4345
| -DAPP_NAME=\<application name\> | application name |
44-
| -DAPP_VERSION=[v]\<major\>[.\<minor\>[.\<patch\>[.\<tweak\>]]] | application version number|
45-
| -DAPP_ORG=\<organization name\> | company/organization name |
46-
| -DAPP_ICON=\<icon path\> | *(Optional)* application icon |
46+
| -DAPP_VERSION=[v]\<major\>[.\<minor\>[.\<patch\>[.\<tweak\>]]] | (Windows Only) application version number|
47+
| -DAPP_ORG=\<organization name\> | (Windows Only) company/organization name |
48+
| -DAPP_ICON=\<icon path\> | *(Optional, Windows Only)* application icon |
4749
| -DAPP_JVM_TYPE=\<server\|client\> | JVM type. Default is `server` |
4850
| -DAPP_MAIN_CLASS=\<main class name\> | full name of the main class, without the module name |
4951
| -DAPP_JRE_PATH=\<jre path\> | JRE path. The path can be relative to the launcher, or it can be an absolute path. Default is `..` (put launcher into the `bin` directory of JRE) |
50-
| -DAPP_COPYRIGHT=\<copyright notice\> | application copyright notice |
52+
| -DAPP_COPYRIGHT=\<copyright notice\> | (Windows Only) application copyright notice |
5153
| -DAPP_PREDEF_OPTIONS=\<predef vm options\> | predefined VM options, separate multiple options with `;;;`. See [JNI_CreateJavaVM](https://docs.oracle.com/en/java/javase/11/docs/specs/jni/invocation.html#jni_createjavavm), *Standard Options* table |
5254

5355
## Runtime Options

launcher.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,7 @@
66

77
JavaVM *vm = NULL;
88
JNIEnv *env = NULL;
9+
10+
CMD cmd;
11+
12+
LIBRARY_HANDLE libjvm = NULL;

launcher.h

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#define LAUNCHER_LAUNCHER_H
77

88
#include <jni.h>
9+
#include <launcher_config.h>
910

1011

1112
#ifdef WIN32
@@ -22,18 +23,37 @@ typedef jint (JNICALL *CreateJavaVM_t)(JavaVM **pvm, void **penv, void *args);
2223
extern JavaVM *vm;
2324
extern JNIEnv *env;
2425

25-
extern void parseCMD();
26+
typedef struct _CMD {
27+
int nOptions;
28+
JavaVMOption *options;
29+
int nArguments;
30+
#ifdef WIN32
31+
LPWSTR *arguments;
32+
#else
33+
const char **arguments;
34+
#endif
35+
} CMD;
2636

27-
extern void destructCMD();
37+
extern CMD cmd;
38+
39+
#ifdef WIN32
40+
extern void parseCMD(void);
41+
#else // WIN32
42+
extern void parseCMD(int argc, char *argv[]);
43+
#endif
44+
45+
extern void destructCMD(void);
2846

2947
static inline jclass findClass(const char *name) {
3048
return (*env)->FindClass(env, name);
3149
}
3250

33-
extern LIBRARY_HANDLE loadJVM();
51+
extern LIBRARY_HANDLE libjvm;
52+
53+
extern void loadJVM(void);
3454

35-
extern jint createJVM(LIBRARY_HANDLE);
55+
extern jint createJVM(void);
3656

37-
extern jobjectArray javaArguments();
57+
extern jobjectArray javaArguments(void);
3858

3959
#endif //LAUNCHER_LAUNCHER_H

launcher_linux.c

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
//
2+
// Created by Glavo on 2020.01.10.
3+
//
4+
5+
#define _POSIX_C_SOURCE 1
6+
7+
#include <string.h>
8+
#include <stdlib.h>
9+
#include <limits.h>
10+
#include <dlfcn.h>
11+
12+
#include "whereami/whereami.h"
13+
#include "launcher.h"
14+
15+
const char APPLICATION_PREDEF_VM_OPTIONS[] = APPLICATION_PREDEF_OPTIONS;
16+
17+
void parseCMD(int argc, char *argv[]) {
18+
int preOptionsSize = 0;
19+
for (int i = 0; i < sizeof(APPLICATION_PREDEF_VM_OPTIONS) / sizeof(char); ++i) {
20+
if (APPLICATION_PREDEF_VM_OPTIONS[i] == '\0') {
21+
++preOptionsSize;
22+
}
23+
}
24+
25+
int nOptions = 0;
26+
JavaVMOption *options = malloc((argc - 1 + preOptionsSize) * sizeof(JavaVMOption));
27+
int nArguments = 0;
28+
const char **arguments = malloc((argc - 1) * sizeof(char *));
29+
30+
const char *p = APPLICATION_PREDEF_VM_OPTIONS;
31+
while (p < APPLICATION_PREDEF_VM_OPTIONS + sizeof(APPLICATION_PREDEF_VM_OPTIONS)) {
32+
size_t len = strlen(p);
33+
if (len == 0) {
34+
++p;
35+
continue;
36+
}
37+
options[nOptions].optionString = (char *) p;
38+
options[nOptions].extraInfo = NULL;
39+
++nOptions;
40+
p += len + 1;
41+
}
42+
43+
for (int i = 1; i < argc; ++i) {
44+
char *str = argv[i];
45+
int strLen = (int) strlen(str);
46+
if (strLen > 2 && str[0] == L'-' && str[1] == L'J') {
47+
str += 2;
48+
options[nOptions].optionString = str;
49+
options[nOptions].extraInfo = NULL;
50+
++nOptions;
51+
} else {
52+
arguments[nArguments++] = str;
53+
}
54+
}
55+
56+
cmd.nOptions = nOptions;
57+
if (nOptions == 0) {
58+
free(options);
59+
cmd.options = NULL;
60+
} else {
61+
cmd.options = options;
62+
}
63+
64+
cmd.nArguments = nArguments;
65+
if (nArguments == 0) {
66+
free(arguments);
67+
cmd.arguments = NULL;
68+
} else {
69+
cmd.arguments = arguments;
70+
}
71+
}
72+
73+
void destructCMD(void) {
74+
75+
}
76+
77+
void loadJVM() {
78+
int pathLen;
79+
char jrePath[PATH_MAX + 1] = {0};
80+
if (APPLICATION_JRE_PATH[0] == '/') {
81+
strcpy(jrePath, APPLICATION_JRE_PATH);
82+
pathLen = strlen(APPLICATION_JRE_PATH);
83+
} else if (wai_getExecutablePath(jrePath, PATH_MAX + 1, &pathLen) != -1) {
84+
jrePath[pathLen] = '/';
85+
strcpy(jrePath + pathLen + 1, APPLICATION_JRE_PATH);
86+
pathLen = (int) strlen(jrePath);
87+
} else {
88+
fprintf(stderr, "Failed to get file path\n");
89+
return;
90+
}
91+
92+
if (jrePath[pathLen - 1] == '/') {
93+
--pathLen;
94+
}
95+
strcpy(jrePath + pathLen, "/lib/" APPLICATION_JVM_TYPE "/libjvm.so");
96+
void *jvm = dlopen(jrePath, RTLD_NOW);
97+
const char *err = dlerror();
98+
if (err) {
99+
fprintf(stderr, "Open libjvm.so failed: %s\n", err);
100+
return;
101+
}
102+
libjvm = jvm;
103+
}
104+
105+
jint createJVM() {
106+
CreateJavaVM_t createJavaVM = (CreateJavaVM_t) dlsym(libjvm, "JNI_CreateJavaVM");
107+
108+
if (createJavaVM == NULL) {
109+
return JNI_ERR;
110+
}
111+
112+
// options
113+
JavaVMInitArgs initArgs;
114+
initArgs.version = JNI_VERSION_1_6;
115+
initArgs.nOptions = cmd.nOptions;
116+
initArgs.options = cmd.options;
117+
118+
return createJavaVM(&vm, (void **) &env, &initArgs);
119+
}
120+
121+
jobjectArray javaArguments() {
122+
jclass stringCls = findClass("java/lang/String");
123+
if (stringCls == NULL) {
124+
return NULL;
125+
}
126+
127+
jmethodID vf = (*env)->GetMethodID(env, stringCls, "<init>", "([B)V");
128+
if (vf == NULL) {
129+
fprintf(stderr, "\n");
130+
return NULL;
131+
}
132+
133+
int nArguments = cmd.nArguments;
134+
const char **arguments = cmd.arguments;
135+
136+
jobjectArray arr = (*env)->NewObjectArray(env, nArguments, stringCls, NULL);
137+
for (int i = 0; i < nArguments; ++i) {
138+
const char *arg = arguments[i];
139+
size_t len = strlen(arg);
140+
141+
jbyteArray argChars = (*env)->NewByteArray(env, len);
142+
(*env)->SetByteArrayRegion(env, argChars, 0, len, (const jbyte *) arg);
143+
144+
jstring str = (*env)->NewObject(env, stringCls, vf, argChars);
145+
(*env)->SetObjectArrayElement(env, arr, i - 1, str);
146+
}
147+
return arr;
148+
}

launcher_linux_main.c

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
//
2+
// Created by Glavo on 2020.01.10.
3+
//
4+
#include <stdlib.h>
5+
#include "launcher.h"
6+
7+
int main(int argc, char *argv[]) {
8+
loadJVM();
9+
if (libjvm == NULL) {
10+
return EXIT_FAILURE;
11+
}
12+
13+
// jvm methods
14+
jint ans = createJVM();
15+
16+
if (ans != JNI_OK) {
17+
fprintf(stderr, "Create JVM failed");
18+
return ans;
19+
}
20+
21+
jthrowable ex;
22+
23+
jclass mainClass = findClass(APPLICATION_MAIN_CLASS);
24+
ex = (*env)->ExceptionOccurred(env);
25+
if (mainClass == NULL) {
26+
fprintf(stderr, "Main class '" APPLICATION_MAIN_CLASS"' not found");
27+
return EXIT_FAILURE;
28+
}
29+
if (ex != NULL) {
30+
(*env)->ExceptionDescribe(env);
31+
return EXIT_FAILURE;
32+
}
33+
34+
35+
jmethodID mainMethodID = (*env)->GetStaticMethodID(env, mainClass, "main", "([Ljava/lang/String;)V");
36+
ex = (*env)->ExceptionOccurred(env);
37+
if (mainMethodID == NULL) {
38+
fprintf(stderr, "Main method not found");
39+
return EXIT_FAILURE;
40+
}
41+
if (ex != NULL) {
42+
(*env)->ExceptionDescribe(env);
43+
return EXIT_FAILURE;
44+
}
45+
46+
parseCMD(argc, argv);
47+
jobjectArray args = javaArguments();
48+
ex = (*env)->ExceptionOccurred(env);
49+
if (args == NULL) {
50+
fprintf(stderr, "Failed to initialization arguments");
51+
return EXIT_FAILURE;
52+
}
53+
if (ex != NULL) {
54+
(*env)->ExceptionDescribe(env);
55+
return EXIT_FAILURE;
56+
}
57+
58+
destructCMD();
59+
60+
ex = (*env)->ExceptionOccurred(env);
61+
(*env)->CallStaticVoidMethod(env, mainClass, mainMethodID, args);
62+
if (ex != NULL) {
63+
(*env)->ExceptionDescribe(env);
64+
return EXIT_FAILURE;
65+
}
66+
67+
(*vm)->DestroyJavaVM(vm);
68+
return 0;
69+
}
70+
71+

0 commit comments

Comments
 (0)