aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/tools/launcher/java_launcher.cc
diff options
context:
space:
mode:
authorGravatar Yun Peng <pcloudy@google.com>2017-08-24 17:40:22 +0200
committerGravatar Damien Martin-Guillerez <dmarting@google.com>2017-08-25 12:53:08 +0200
commit54c5c5cf41bd47d11ea309f37697d83ae65fe9e7 (patch)
tree181e37a31a457de9989f79199bd98d046e85830c /src/tools/launcher/java_launcher.cc
parent5371d134cfaf3c258f9ebb606a5cb061f419613e (diff)
Windows: Implement Java native launcher
Now Bazel build a Windows exe binary to launch JVM for java_binary and java_test. The Java native launcher is implemented with the same logic and functionalities as the original java shell stub script. Change-Id: Ida40579bce82425f3506f9376b7256aa3edc265e PiperOrigin-RevId: 166346445
Diffstat (limited to 'src/tools/launcher/java_launcher.cc')
-rw-r--r--src/tools/launcher/java_launcher.cc276
1 files changed, 274 insertions, 2 deletions
diff --git a/src/tools/launcher/java_launcher.cc b/src/tools/launcher/java_launcher.cc
index ce626d347e..b8c0bdbd43 100644
--- a/src/tools/launcher/java_launcher.cc
+++ b/src/tools/launcher/java_launcher.cc
@@ -12,14 +12,286 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <sstream>
+#include <string>
+#include <vector>
+
#include "src/tools/launcher/java_launcher.h"
+#include "src/tools/launcher/util/launcher_util.h"
namespace bazel {
namespace launcher {
+using std::getline;
+using std::ofstream;
+using std::ostringstream;
+using std::string;
+using std::stringstream;
+using std::vector;
+
+// The runfile path of java binary, eg. local_jdk/bin/java.exe
+static constexpr const char* JAVA_BIN_PATH = "java_bin_path";
+static constexpr const char* JAR_BIN_PATH = "jar_bin_path";
+static constexpr const char* CLASSPATH = "classpath";
+static constexpr const char* JAVA_START_CLASS = "java_start_class";
+static constexpr const char* JVM_FLAGS = "jvm_flags";
+
+// Check if a string start with a certain prefix.
+// If it's true, store the substring without the prefix in value.
+// If value is quoted, then remove the quotes.
+static bool GetFlagValue(const string& str, const string& prefix,
+ string* value_ptr) {
+ if (str.compare(0, prefix.length(), prefix)) {
+ return false;
+ }
+ string& value = *value_ptr;
+ value = str.substr(prefix.length());
+ int len = value.length();
+ if (len >= 2 && value[0] == '"' && value[len - 1] == '"') {
+ value = value.substr(1, len - 2);
+ }
+ return true;
+}
+
+// Parses one launcher flag and updates this object's state accordingly.
+//
+// Returns true if the flag is a valid launcher flag; false otherwise.
+bool JavaBinaryLauncher::ProcessWrapperArgument(const string& argument) {
+ string flag_value;
+ if (argument.compare("--debug") == 0) {
+ string default_jvm_debug_port;
+ if (GetEnv("DEFAULT_JVM_DEBUG_PORT", &default_jvm_debug_port)) {
+ this->jvm_debug_port = default_jvm_debug_port;
+ } else {
+ this->jvm_debug_port = "5005";
+ }
+ } else if (GetFlagValue(argument, "--debug=", &flag_value)) {
+ this->jvm_debug_port = flag_value;
+ } else if (GetFlagValue(argument, "--main_advice=", &flag_value)) {
+ this->main_advice = flag_value;
+ } else if (GetFlagValue(argument, "--main_advice_classpath=", &flag_value)) {
+ this->main_advice_classpath = flag_value;
+ } else if (GetFlagValue(argument, "--jvm_flag=", &flag_value)) {
+ this->jvm_flags_cmdline.push_back(flag_value);
+ } else if (GetFlagValue(argument, "--jvm_flags=", &flag_value)) {
+ stringstream flag_value_ss(flag_value);
+ string item;
+ while (getline(flag_value_ss, item, ' ')) {
+ this->jvm_flags_cmdline.push_back(item);
+ }
+ } else if (argument.compare("--singlejar") == 0) {
+ this->singlejar = true;
+ } else if (argument.compare("--print_javabin") == 0) {
+ this->print_javabin = true;
+ } else if (GetFlagValue(argument, "--classpath_limit=", &flag_value)) {
+ this->classpath_limit = std::stoi(flag_value);
+ } else {
+ return false;
+ }
+ return true;
+}
+
+vector<string> JavaBinaryLauncher::ProcessesCommandLine() {
+ vector<string> args;
+ bool first = 1;
+ for (const auto& arg : this->GetCommandlineArguments()) {
+ // Skip the first arugment.
+ if (first) {
+ first = 0;
+ continue;
+ }
+ string flag_value;
+ // TODO(pcloudy): Should rename this flag to --native_launcher_flag.
+ // But keep it as it is for now to be consistent with the shell script
+ // launcher.
+ if (GetFlagValue(arg, "--wrapper_script_flag=", &flag_value)) {
+ if (!ProcessWrapperArgument(flag_value)) {
+ die("invalid wrapper argument '%s'", arg);
+ }
+ } else if (!args.empty() || !ProcessWrapperArgument(arg)) {
+ args.push_back(arg);
+ }
+ }
+ return args;
+}
+
+string JavaBinaryLauncher::CreateClasspathJar(const string& classpath) {
+ static constexpr const char* URI_PREFIX = "file:/";
+ ostringstream manifest_classpath;
+ manifest_classpath << "Class-Path:";
+ stringstream classpath_ss(classpath);
+ string path;
+ while (getline(classpath_ss, path, ';')) {
+ manifest_classpath << ' ';
+ manifest_classpath << URI_PREFIX;
+ for (const auto& x : path) {
+ if (x == ' ') {
+ manifest_classpath << "%20";
+ } else {
+ manifest_classpath << x;
+ }
+ }
+ }
+
+ string binary_base_path =
+ GetBinaryPathWithoutExtension(this->GetCommandlineArguments()[0]);
+ string jar_manifest_file_path = binary_base_path + ".jar_manifest";
+ ofstream jar_manifest_file(jar_manifest_file_path);
+ jar_manifest_file << "Manifest-Version: 1.0\n";
+ // No line in the MANIFEST.MF file may be longer than 72 bytes.
+ // A space prefix indicates the line is still the content of the last
+ // attribute.
+ string manifest_classpath_str = manifest_classpath.str();
+ for (size_t i = 0; i < manifest_classpath_str.length(); i += 71) {
+ if (i > 0) {
+ jar_manifest_file << " ";
+ }
+ jar_manifest_file << manifest_classpath_str.substr(i, 71) << "\n";
+ }
+ jar_manifest_file.close();
+
+ // Create the command for generating classpath jar.
+ // We pass the command to cmd.exe to use redirection for suppressing output.
+ string manifest_jar_path = binary_base_path + "-classpath.jar";
+ string jar_bin = this->Rlocation(this->GetLaunchInfoByKey(JAR_BIN_PATH),
+ /*need_workspace_name =*/false);
+ vector<string> arguments;
+ arguments.push_back("/c");
+
+ ostringstream jar_command;
+ jar_command << jar_bin << " cvfm ";
+ jar_command << manifest_jar_path << " ";
+ jar_command << jar_manifest_file_path << " ";
+ jar_command << "> nul";
+ arguments.push_back(jar_command.str());
+
+ if (this->LaunchProcess("cmd.exe", arguments) != 0) {
+ die("Couldn't create classpath jar: %s", manifest_jar_path.c_str());
+ }
+
+ return manifest_jar_path;
+}
+
ExitCode JavaBinaryLauncher::Launch() {
- // TODO(pcloudy): Implement Java launcher
- return 0;
+ // Parse the original command line.
+ vector<string> remaining_args = this->ProcessesCommandLine();
+
+ // Set JAVA_RUNFILES
+ string java_runfiles;
+ if (!GetEnv("JAVA_RUNFILES", &java_runfiles)) {
+ java_runfiles = this->GetRunfilesPath();
+ }
+ SetEnv("JAVA_RUNFILES", java_runfiles);
+
+ // Print Java binary path if needed
+ string java_bin = this->Rlocation(this->GetLaunchInfoByKey(JAVA_BIN_PATH),
+ /*need_workspace_name =*/false);
+ if (this->print_javabin ||
+ this->GetLaunchInfoByKey(JAVA_START_CLASS) == "--print_javabin") {
+ printf("%s\n", java_bin.c_str());
+ return 0;
+ }
+
+ ostringstream classpath;
+
+ // Run deploy jar if needed, otherwise generate the CLASSPATH by rlocation.
+ if (this->singlejar) {
+ string deploy_jar =
+ GetBinaryPathWithoutExtension(this->GetCommandlineArguments()[0]) +
+ "_deploy.jar";
+ if (!DoesFilePathExist(deploy_jar.c_str())) {
+ die("Option --singlejar was passed, but %s does not exist.\n (You may "
+ "need to build it explicitly.)",
+ deploy_jar.c_str());
+ }
+ classpath << deploy_jar << ';';
+ } else {
+ // Add main advice classpath if exists
+ if (!this->main_advice_classpath.empty()) {
+ classpath << this->main_advice_classpath << ';';
+ }
+ string path;
+ stringstream classpath_ss(this->GetLaunchInfoByKey(CLASSPATH));
+ while (getline(classpath_ss, path, ';')) {
+ classpath << this->Rlocation(path) << ';';
+ }
+ }
+
+ // Set jvm debug options
+ ostringstream jvm_debug_flags;
+ if (!this->jvm_debug_port.empty()) {
+ string jvm_debug_suspend;
+ if (!GetEnv("DEFAULT_JVM_DEBUG_SUSPEND", &jvm_debug_suspend)) {
+ jvm_debug_suspend = "y";
+ }
+ jvm_debug_flags << "-agentlib:jdwp=transport=dt_socket,server=y";
+ jvm_debug_flags << ",suspend=" << jvm_debug_suspend;
+ jvm_debug_flags << ",address=" << jvm_debug_port;
+
+ string value;
+ if (GetEnv("PERSISTENT_TEST_RUNNER", &value) && value == "true") {
+ jvm_debug_flags << ",quiet=y";
+ }
+ }
+
+ // Get jvm flags from JVM_FLAGS environment variable and JVM_FLAGS launch info
+ vector<string> jvm_flags;
+ string jvm_flags_env;
+ GetEnv("JVM_FLAGS", &jvm_flags_env);
+ string flag;
+ stringstream jvm_flags_env_ss(jvm_flags_env);
+ while (getline(jvm_flags_env_ss, flag, ' ')) {
+ jvm_flags.push_back(flag);
+ }
+ stringstream jvm_flags_launch_info_ss(this->GetLaunchInfoByKey(JVM_FLAGS));
+ while (getline(jvm_flags_launch_info_ss, flag, ' ')) {
+ jvm_flags.push_back(flag);
+ }
+
+ // Check if TEST_TMPDIR is available to use for scratch.
+ string test_tmpdir;
+ if (GetEnv("TEST_TMPDIR", &test_tmpdir) &&
+ DoesDirectoryPathExist(test_tmpdir.c_str())) {
+ jvm_flags.push_back("-Djava.io.tmpdir=" + test_tmpdir);
+ }
+
+ // Construct the final command line arguments
+ vector<string> arguments;
+ // Add classpath flags
+ arguments.push_back("-classpath");
+ // Check if CLASSPATH is over classpath length limit.
+ // If it does, then we create a classpath jar to pass CLASSPATH value.
+ string classpath_str = classpath.str();
+ if (classpath_str.length() > this->classpath_limit) {
+ arguments.push_back(CreateClasspathJar(classpath_str));
+ } else {
+ arguments.push_back(classpath_str);
+ }
+ // Add JVM debug flags
+ string jvm_debug_flags_str = jvm_debug_flags.str();
+ if (!jvm_debug_flags_str.empty()) {
+ arguments.push_back(jvm_debug_flags_str);
+ }
+ // Add JVM flags parsed from env and launch info.
+ for (const auto& arg : jvm_flags) {
+ arguments.push_back(arg);
+ }
+ // Add JVM flags parsed from command line.
+ for (const auto& arg : this->jvm_flags_cmdline) {
+ arguments.push_back(arg);
+ }
+ // Add main advice class
+ if (!this->main_advice.empty()) {
+ arguments.push_back(this->main_advice);
+ }
+ // Add java start class
+ arguments.push_back(this->GetLaunchInfoByKey(JAVA_START_CLASS));
+ // Add the remaininng arguements, they will be passed to the program.
+ for (const auto& arg : remaining_args) {
+ arguments.push_back(arg);
+ }
+
+ return this->LaunchProcess(java_bin, arguments);
}
} // namespace launcher