| /******************************************************************************* |
| * Copyright (c) 2020 Gunnar Wagenknecht and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Gunnar Wagenknecht - copied from ClasspathShortener and simplified to shorten all arguments |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.launching; |
| |
| import static org.eclipse.jdt.internal.launching.LaunchingPlugin.LAUNCH_TEMP_FILE_PREFIX; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.nio.file.Files; |
| import java.nio.file.Paths; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| |
| import org.eclipse.core.runtime.Assert; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.debug.core.DebugPlugin; |
| import org.eclipse.debug.core.ILaunch; |
| import org.eclipse.debug.core.ILaunchConfiguration; |
| import org.eclipse.jdt.core.JavaCore; |
| import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants; |
| import org.eclipse.jdt.launching.IVMInstall; |
| import org.eclipse.jdt.launching.IVMInstall2; |
| import org.eclipse.osgi.service.environment.Constants; |
| |
| /** |
| * Shortens the command line by writing all commands into an arguments file. |
| */ |
| public class CommandLineShortener { |
| public static String getJavaVersion(IVMInstall vmInstall) { |
| if (vmInstall instanceof IVMInstall2) { |
| IVMInstall2 install = (IVMInstall2) vmInstall; |
| return install.getJavaVersion(); |
| } |
| return null; |
| } |
| |
| private final String javaVersion; |
| private final ILaunch launch; |
| private final String[] cmdLine; |
| private File processTempFilesDir; |
| |
| private final List<File> processTempFiles = new ArrayList<>(); |
| |
| /** |
| * |
| * @param vmInstall |
| * the vm installation |
| * @param launch |
| * the launch |
| * @param cmdLine |
| * the command line (java executable + VM arguments + program arguments) |
| * @param lastJavaArgumentIndex |
| * the index of the last java argument in cmdLine (next arguments if any are program arguments) |
| * @param workingDir |
| * the working dir to use for the launched VM or null |
| * @param envp |
| * array of strings, each element of which has environment variable settings in the format name=value, or null if the subprocess should |
| * inherit the environment of the current process. |
| */ |
| public CommandLineShortener(IVMInstall vmInstall, ILaunch launch, String[] cmdLine, File workingDir) { |
| this(getJavaVersion(vmInstall), launch, cmdLine, workingDir); |
| } |
| |
| protected CommandLineShortener(String javaVersion, ILaunch launch, String[] cmdLine, File workingDir) { |
| Assert.isNotNull(javaVersion); |
| Assert.isNotNull(launch); |
| Assert.isNotNull(cmdLine); |
| this.javaVersion = javaVersion; |
| this.launch = launch; |
| this.cmdLine = cmdLine; |
| this.processTempFilesDir = workingDir != null ? workingDir : Paths.get(".").toAbsolutePath().normalize().toFile(); //$NON-NLS-1$ |
| } |
| |
| protected void addProcessTempFile(File file) { |
| processTempFiles.add(file); |
| } |
| |
| protected File createArgumentFile(String[] cmdLine) throws CoreException { |
| try { |
| String timeStamp = getLaunchTimeStamp(); |
| File argumentsFile = new File(processTempFilesDir, String.format(LAUNCH_TEMP_FILE_PREFIX |
| + "%s-args-%s.txt", getLaunchConfigurationName(), timeStamp)); //$NON-NLS-1$ |
| |
| cmdLine = quoteForArgfile(cmdLine); |
| |
| Files.write(argumentsFile.toPath(), Arrays.asList(cmdLine)); |
| return argumentsFile; |
| } catch (IOException e) { |
| throw new CoreException(new Status(IStatus.ERROR, LaunchingPlugin.getUniqueIdentifier(), IStatus.ERROR, "Cannot create argument file", e)); //$NON-NLS-1$ |
| } |
| } |
| |
| String[] quoteForArgfile(String[] cmdLine) { |
| if (Platform.getOS().equals(Constants.OS_WIN32)) { |
| return CommandLineQuoting.quoteWindowsArgs(cmdLine); |
| } |
| |
| String[] quotedCmdLine = new String[cmdLine.length]; |
| for (int i = 0; i < cmdLine.length; i++) { |
| String arg = cmdLine[i]; |
| if (CommandLineQuoting.needsQuoting(arg)) { |
| StringBuilder escapedArg = new StringBuilder(); |
| for (int j = 0; j < arg.length(); j++) { |
| char c = arg.charAt(j); |
| if (c == '\\') { |
| escapedArg.append('\\'); |
| } else if (c == '\"') { |
| escapedArg.append('\\'); |
| } |
| escapedArg.append(c); |
| } |
| arg = "\"" + escapedArg.toString() + "\""; //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| quotedCmdLine[i] = arg; |
| } |
| return quotedCmdLine; |
| } |
| |
| protected String getLaunchConfigurationName() { |
| return launch.getLaunchConfiguration().getName(); |
| } |
| |
| protected String getLaunchTimeStamp() { |
| String timeStamp = launch.getAttribute(DebugPlugin.ATTR_LAUNCH_TIMESTAMP); |
| if (timeStamp == null) { |
| timeStamp = Long.toString(System.currentTimeMillis()); |
| } |
| return timeStamp; |
| } |
| |
| /** |
| * The files that were created while shortening the path. They can be deleted once the process is terminated |
| * |
| * @return created files |
| */ |
| public List<File> getProcessTempFiles() { |
| return new ArrayList<>(processTempFiles); |
| } |
| |
| public File getProcessTempFilesDir() { |
| return processTempFilesDir; |
| } |
| |
| /** |
| * @return the original, unshortened command line |
| */ |
| public String[] getOriginalCmdLine() { |
| return cmdLine; |
| } |
| |
| /** |
| * @return <code>true</code> if the JVM supports launching with argument files, <code>false</code> otherwise |
| */ |
| protected boolean isArgumentFileSupported() { |
| return JavaCore.compareJavaVersions(javaVersion, JavaCore.VERSION_9) >= 0; |
| } |
| |
| /** |
| * The directory to use to create temp files needed when shortening the classpath. By default, the working directory is used |
| * |
| * The java.io.tmpdir should not be used on MacOs (does not work for classpath-only jars) |
| * |
| * @param processTempFilesDir |
| */ |
| public void setProcessTempFilesDir(File processTempFilesDir) { |
| this.processTempFilesDir = processTempFilesDir; |
| } |
| |
| /** |
| * Writes the command line into an arguments file and returns the shortened command line. |
| * |
| * @return a shortened command line |
| * @throws CoreException |
| */ |
| public String[] shortenCommandLine() throws CoreException { |
| List<String> fullCommandLine = new ArrayList<>(Arrays.asList(cmdLine)); |
| List<String> shortCommandLine = new ArrayList<>(); |
| |
| shortCommandLine.add(fullCommandLine.remove(0)); |
| |
| File argumentFile = createArgumentFile(fullCommandLine.toArray(new String[fullCommandLine.size()])); |
| addProcessTempFile(argumentFile); |
| shortCommandLine.add("@" + argumentFile.getAbsolutePath());//$NON-NLS-1$ |
| |
| return shortCommandLine.toArray(new String[shortCommandLine.size()]); |
| } |
| |
| /** |
| * Indicates if the command line {@link #isArgumentFileSupported() can} and should be shortened. |
| * <p> |
| * The command line should only be shortened if at least Java 9 is used and the launch is configured to do so. |
| * </p> |
| * |
| * @return <code>true</code> if {@link #isArgumentFileSupported()} returns <code>true</code> and command line should be shortened, |
| * <code>false</code> otherwise |
| * @throws CoreException |
| */ |
| public boolean shouldShortenCommandLine() throws CoreException { |
| if (!isArgumentFileSupported()) { |
| return false; |
| } |
| |
| if (cmdLine.length < 2) { |
| // no need to shorten if it's just the program argument |
| return false; |
| } |
| |
| ILaunchConfiguration configuration = launch.getLaunchConfiguration(); |
| if (configuration != null) { |
| return configuration.getAttribute(IJavaLaunchConfigurationConstants.ATTR_USE_ARGFILE, true); |
| } |
| |
| return false; |
| } |
| } |