Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8256308: Send arguments to javac server in a config file #1195

Closed
wants to merge 6 commits into from
@@ -177,6 +177,12 @@ define SetupJavaCompilationBody
$$(error Must specify BIN (in $1))
endif

ifneq ($$($1_MODULE), )
$1_MODULE_SUBDIR := /$$($1_MODULE)
endif

$1_SAFE_NAME := $$(strip $$(subst /,_, $1))

ifeq ($$($1_SMALL_JAVA), )
# If unspecified, default to true
$1_SMALL_JAVA := true
@@ -215,18 +221,35 @@ define SetupJavaCompilationBody
ifeq ($$(ENABLE_JAVAC_SERVER)+$$($1_CLASSPATH), true+)
$1_JAVAC := $$(INTERIM_LANGTOOLS_ARGS) -m jdk.compiler.interim/com.sun.tools.sjavac.Main

# How to launch the server. This must use JAVA_DETACH, which is the "big" java
# with an ability to detach from fixpath (on Windows)
# This will be executed by the client, if needed.
$1_JAVAC_SERVER_CMD := $$(JAVA_DETACH) $$($1_JAVA_FLAGS) $$($1_JAVAC)
$1_ESCAPED_CMD := $$(subst $$(SPACE),%20,$$(subst $$(COMMA),%2C,$$(strip $$($1_JAVAC_SERVER_CMD))))
# Create a configuration file with the needed information for the javac
# server to function properly.
$1_JAVAC_SERVER_CONFIG := $$($1_BIN)$$($1_MODULE_SUBDIR)/_the.$$($1_SAFE_NAME)-server.conf

# The port file contains the tcp/ip on which the server listens
# The portfile contains the tcp/ip on which the server listens
# and the cookie necessary to talk to the server.
$1_JAVA_SERVER_FLAGS := --server:portfile=$$(JAVAC_SERVER_DIR)/server.port,sjavac=$$($1_ESCAPED_CMD)
$1_JAVAC_PORT_FILE := $$(JAVAC_SERVER_DIR)/server.port

# The servercmd specified how to launch the server. This must use
# JAVA_DETACH, which is the "big" java with an ability to detach from
# fixpath (on Windows) This will be executed by the client, if needed.
$1_JAVAC_SERVER_CMD := $$(JAVA_DETACH) $$($1_JAVA_FLAGS) $$($1_JAVAC)

$1_CONFIG_VARDEPS := $$($1_JAVAC_PORT_FILE) $$($1_JAVAC_SERVER_CMD)
$1_CONFIG_VARDEPS_FILE := $$(call DependOnVariable, $1_CONFIG_VARDEPS, \
$$($1_BIN)$$($1_MODULE_SUBDIR)/_the.$1.config_vardeps)

# Always use small to launch client
$1_JAVAC_CMD := $$(JAVA_SMALL) $$($1_JAVA_FLAGS) $$($1_JAVAC) $$($1_JAVA_SERVER_FLAGS)
ifeq ($(call isBuildOs, windows), true)
$1_ECHO_COMMAND := $(FIXPATH) cmd /c echo
else
$1_ECHO_COMMAND := $(ECHO)
endif
$$($1_JAVAC_SERVER_CONFIG): $$($1_CONFIG_VARDEPS_FILE)
$$($1_ECHO_COMMAND) portfile=$$($1_JAVAC_PORT_FILE) > $$@
$$($1_ECHO_COMMAND) servercmd=$$($1_JAVAC_SERVER_CMD) >> $$@

# Always use small java to launch client
$1_JAVAC_CMD := $$(JAVA_SMALL) $$($1_JAVA_FLAGS) $$($1_JAVAC) \
--server:conf=$$($1_JAVAC_SERVER_CONFIG)
else
# No javac server
$1_JAVAC := $$(INTERIM_LANGTOOLS_ARGS) -m jdk.compiler.interim/com.sun.tools.javac.Main
@@ -263,10 +286,6 @@ define SetupJavaCompilationBody
$1_FLAGS += -cp $$(call PathList, $$($1_CLASSPATH))
endif

ifneq ($$($1_MODULE), )
$1_MODULE_SUBDIR := /$$($1_MODULE)
endif

# Make sure the dirs exist, or that one of the EXTRA_FILES, that may not
# exist yet, is in it.
$$(foreach d, $$($1_SRC), \
@@ -331,9 +350,6 @@ define SetupJavaCompilationBody
$$(error No source files found for $1)
endif
else

$1_SAFE_NAME := $$(strip $$(subst /,_, $1))

# All files below META-INF are always copied.
$1_ALL_COPIES := $$(filter $$(addsuffix /META-INF%,$$($1_SRC)),$$($1_ALL_SRCS))
# Find all files to be copied from source to bin.
@@ -442,7 +458,7 @@ define SetupJavaCompilationBody

# Do the actual compilation
$$($1_COMPILE_TARGET): $$($1_SRCS) $$($1_FILELIST) $$($1_DEPENDS) \
$$($1_VARDEPS_FILE) $$($1_EXTRA_DEPS)
$$($1_VARDEPS_FILE) $$($1_EXTRA_DEPS) $$($1_JAVAC_SERVER_CONFIG)
$$(call MakeDir, $$(@D))
$$(call ExecuteWithLog, $$($1_BIN)$$($1_MODULE_SUBDIR)/_the.$$($1_SAFE_NAME)_batch, \
$$($1_JAVAC_CMD) $$($1_FLAGS) \
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -79,15 +79,23 @@ public static String extractStringOption(String opName, String s) {
return extractStringOption(opName, s, null);
}

public static String extractStringOption(String opName, String s, String deflt) {
private static String extractStringOptionWithDelimiter(String opName, String s, String deflt, char delimiter) {
int p = s.indexOf(opName+"=");
if (p == -1) return deflt;
p+=opName.length()+1;
int pe = s.indexOf(',', p);
int pe = s.indexOf(delimiter, p);
if (pe == -1) pe = s.length();
return s.substring(p, pe);
}

public static String extractStringOption(String opName, String s, String deflt) {
return extractStringOptionWithDelimiter(opName, s, deflt, ',');
}

public static String extractStringOptionLine(String opName, String s, String deflt) {
return extractStringOptionWithDelimiter(opName, s, deflt, '\n').strip();

This comment has been minimized.

@jbf

jbf Nov 24, 2020
Member

Is '\n' going to be problematic due to differences in line endings on various platforms?

This comment has been minimized.

@magicus

magicus Nov 24, 2020
Author Member

The JDK project is limited to unix-style line endings only. No other styles are supported, and any attempt to use such will break the build (often early on).

}

public static boolean extractBooleanOption(String opName, String s, boolean deflt) {
String str = extractStringOption(opName, s);
return "true".equals(str) ? true
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -34,6 +34,9 @@
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -42,15 +45,11 @@
import com.sun.tools.javac.main.Main.Result;
import com.sun.tools.sjavac.Log;
import com.sun.tools.sjavac.Util;
import com.sun.tools.sjavac.options.OptionHelper;
import com.sun.tools.sjavac.options.Options;
import com.sun.tools.sjavac.server.CompilationSubResult;
import com.sun.tools.sjavac.server.PortFile;
import com.sun.tools.sjavac.server.Sjavac;
import com.sun.tools.sjavac.server.SjavacServer;

import static java.util.stream.Collectors.joining;

/**
* Sjavac implementation that delegates requests to a SjavacServer.
*
@@ -61,59 +60,58 @@
*/
public class SjavacClient implements Sjavac {

// The id can perhaps be used in the future by the javac server to reuse the
// JavaCompiler instance for several compiles using the same id.
private final String id;
private final PortFile portFile;

// Default keepalive for server is 120 seconds.
// I.e. it will accept 120 seconds of inactivity before quitting.
private final int keepalive;
private final int poolsize;
private PortFile portFile;

// The sjavac option specifies how the server part of sjavac is spawned.
// If you have the experimental sjavac in your path, you are done. If not, you have
// to point to a com.sun.tools.sjavac.Main that supports --startserver
// for example by setting: sjavac=java%20-jar%20...javac.jar%com.sun.tools.sjavac.Main
private final String sjavacForkCmd;
// The servercmd option specifies how the server part of sjavac is spawned.
// It should point to a com.sun.tools.sjavac.Main that supports --startserver
private String serverCommand;

// Accept 120 seconds of inactivity before quitting.
private static final int KEEPALIVE = 120;
private static final int POOLSIZE = Runtime.getRuntime().availableProcessors();
// Wait 2 seconds for response, before giving up on javac server.
static int CONNECTION_TIMEOUT = 2000;
static int MAX_CONNECT_ATTEMPTS = 3;
static int WAIT_BETWEEN_CONNECT_ATTEMPTS = 2000;

// Store the server conf settings here.
private final String settings;
private static final int CONNECTION_TIMEOUT = 2000;
private static final int MAX_CONNECT_ATTEMPTS = 3;
private static final int WAIT_BETWEEN_CONNECT_ATTEMPTS = 2000;

public SjavacClient(Options options) {
String tmpServerConf = options.getServerConf();
String serverConf = (tmpServerConf!=null)? tmpServerConf : "";
String tmpId = Util.extractStringOption("id", serverConf);
id = (tmpId!=null) ? tmpId : "id"+(((new java.util.Random()).nextLong())&Long.MAX_VALUE);
String defaultPortfile = options.getDestDir()
.resolve("javac_server")
.toAbsolutePath()
.toString();
String portfileName = Util.extractStringOption("portfile", serverConf, defaultPortfile);
portFile = SjavacServer.getPortFile(portfileName);
sjavacForkCmd = Util.extractStringOption("sjavac", serverConf, "sjavac");
int poolsize = Util.extractIntOption("poolsize", serverConf);
keepalive = Util.extractIntOption("keepalive", serverConf, 120);
String serverConf = options.getServerConf();
String configFile = Util.extractStringOption("conf", serverConf, "");

this.poolsize = poolsize > 0 ? poolsize : Runtime.getRuntime().availableProcessors();
settings = (serverConf.equals("")) ? "id="+id+",portfile="+portfileName : serverConf;
}
try {
List<String> configFileLines = Files.readAllLines(Path.of(configFile), StandardCharsets.UTF_8);
String configFileContent = String.join("\n", configFileLines);

/**
* Hand out the server settings.
* @return The server settings, possibly a default value.
*/
public String serverSettings() {
return settings;
String portfileName = Util.extractStringOptionLine("portfile", configFileContent, "");
if (portfileName.isEmpty()) {
Log.error("Configuration file missing value for 'portfile'");
portFile = null;
} else {
portFile = SjavacServer.getPortFile(portfileName);
}

String serverCommandString = Util.extractStringOptionLine("servercmd", configFileContent, "");
if (serverCommandString.isEmpty()) {
Log.error("Configuration file missing value for 'servercmd'");
serverCommand = null;
} else {
serverCommand = serverCommandString;
}
} catch (IOException e) {
Log.error("Cannot read configuration file " + configFile);
Log.debug(e);
portFile = null;
serverCommand = null;
}
}

@Override
public Result compile(String[] args) {
if (portFile == null || serverCommand == null) {
Log.error("Incorrect configuration, portfile and/or servercmd missing");
return Result.ERROR;
}

Result result = null;
try (Socket socket = tryConnect()) {
PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
@@ -176,7 +174,7 @@ public Result compile(String[] args) {
* Makes MAX_CONNECT_ATTEMPTS attempts to connect to server.
*/
private Socket tryConnect() throws IOException, InterruptedException {
makeSureServerIsRunning(portFile);
makeSureServerIsRunning();
int attempt = 0;
while (true) {
Log.debug("Trying to connect. Attempt " + (++attempt) + " of " + MAX_CONNECT_ATTEMPTS);
@@ -206,7 +204,7 @@ private Socket makeConnectionAttempt() throws IOException {
* Will return immediately if a server already seems to be running,
* otherwise fork a new server and block until it seems to be running.
*/
private void makeSureServerIsRunning(PortFile portFile)
private void makeSureServerIsRunning()
throws IOException, InterruptedException {

if (portFile.exists()) {
@@ -221,10 +219,7 @@ private void makeSureServerIsRunning(PortFile portFile)
}

// Fork a new server and wait for it to start
SjavacClient.fork(sjavacForkCmd,
portFile,
poolsize,
keepalive);
startNewServer();
}

@Override
@@ -235,14 +230,14 @@ public void shutdown() {
/*
* Fork a server process process and wait for server to come around
*/
public static void fork(String sjavacCmd, PortFile portFile, int poolsize, int keepalive)
public void startNewServer()
throws IOException, InterruptedException {
List<String> cmd = new ArrayList<>();
cmd.addAll(Arrays.asList(OptionHelper.unescapeCmdArg(sjavacCmd).split(" ")));
cmd.addAll(Arrays.asList(serverCommand.split(" ")));
cmd.add("--startserver:"
+ "portfile=" + portFile.getFilename()
+ ",poolsize=" + poolsize
+ ",keepalive="+ keepalive);
+ ",poolsize=" + POOLSIZE
+ ",keepalive="+ KEEPALIVE);

Process serverProcess;
Log.debug("Starting server. Command: " + String.join(" ", cmd));
ProTip! Use n and p to navigate between commits in a pull request.