1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-29 19:45:01 +02:00

Use CommandLineUtil to split and join command lines in CBS Makefile support

Without this the build and clean command entered in the CBS
settings could be parsed and re-assembled incorrectly.

A simple example is that an extra space (e.g. "make  clean") would
lead to an error when running make like:

```
make: *** empty string invalid as file name.  Stop.
```

This happened because the code used trivial split on " " and join
with " " to handle parsing that command line string into array of
arguments. This change fixes that by using the functionality already
available in CommandLineUtil

Fixes #1072
This commit is contained in:
Jonah Graham 2025-02-01 23:45:06 -05:00
parent c8e47b321d
commit c55db63bad
4 changed files with 70 additions and 40 deletions

View file

@ -24,6 +24,11 @@ The following classes have been removed or modified in API breaking ways:
- spelling corrected for methods with Uninitialized in the name - spelling corrected for methods with Uninitialized in the name
- setWarnUnused renamed to setWarnUnusedVars and isWarnUnused renamed to isWarnUnusedVars - setWarnUnused renamed to setWarnUnusedVars and isWarnUnused renamed to isWarnUnusedVars
### StandardBuildConfiguration.setBuildCommand(String[]) and StandardBuildConfiguration.setCleanCommand(String[]) removed
These methods (in `org.eclipse.cdt.core.build.StandardBuildConfiguration`) made it difficult to save and load users build and clean command without modifying it.
They have been replaced with methods that take only a `String` for consistent parsing of command lines.
See [#1072](https://github.com/eclipse-cdt/cdt/issues/1072) for more details on motivation for this change.
## API Changes in CDT 11.5. ## API Changes in CDT 11.5.

View file

@ -198,19 +198,8 @@ public class MakeBuildSettingsTab extends CommonBuildTab {
stdConfig.setBuildContainer(stdConfig.getProject()); stdConfig.setBuildContainer(stdConfig.getProject());
} }
String buildCommand = buildCmdText.getText().trim(); stdConfig.setBuildCommand(buildCmdText.getText());
if (!buildCommand.isEmpty()) { stdConfig.setCleanCommand(cleanCmdText.getText());
stdConfig.setBuildCommand(buildCommand.split(" ")); //$NON-NLS-1$
} else {
stdConfig.setBuildCommand(null);
}
String cleanCommand = cleanCmdText.getText().trim();
if (!cleanCommand.isEmpty()) {
stdConfig.setCleanCommand(cleanCommand.split(" ")); //$NON-NLS-1$
} else {
stdConfig.setCleanCommand(null);
}
} }
} catch (CoreException e) { } catch (CoreException e) {
MakeUIPlugin.log(e.getStatus()); MakeUIPlugin.log(e.getStatus());

View file

@ -29,6 +29,7 @@ import org.eclipse.cdt.core.envvar.IEnvironmentVariable;
import org.eclipse.cdt.core.model.ICModelMarker; import org.eclipse.cdt.core.model.ICModelMarker;
import org.eclipse.cdt.core.resources.IConsole; import org.eclipse.cdt.core.resources.IConsole;
import org.eclipse.cdt.internal.core.build.Messages; import org.eclipse.cdt.internal.core.build.Messages;
import org.eclipse.cdt.utils.CommandLineUtil;
import org.eclipse.core.resources.IBuildConfiguration; import org.eclipse.core.resources.IBuildConfiguration;
import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IProject;
@ -61,11 +62,11 @@ public class StandardBuildConfiguration extends CBuildConfiguration {
*/ */
public static final String CLEAN_COMMAND = "stdbuild.clean.command"; //$NON-NLS-1$ public static final String CLEAN_COMMAND = "stdbuild.clean.command"; //$NON-NLS-1$
private static final String[] DEFAULT_BUILD_COMMAND = new String[] { "make" }; //$NON-NLS-1$ private static final String DEFAULT_BUILD_COMMAND = "make"; //$NON-NLS-1$
private static final String[] DEFAULT_CLEAN_COMMAND = new String[] { "make", "clean" }; //$NON-NLS-1$ //$NON-NLS-2$ private static final String DEFAULT_CLEAN_COMMAND = "make clean"; //$NON-NLS-1$
private String[] buildCommand = DEFAULT_BUILD_COMMAND; private String buildCommand = DEFAULT_BUILD_COMMAND;
private String[] cleanCommand = DEFAULT_CLEAN_COMMAND; private String cleanCommand = DEFAULT_CLEAN_COMMAND;
private IContainer buildContainer; private IContainer buildContainer;
private IEnvironmentVariable[] envVars; private IEnvironmentVariable[] envVars;
@ -97,18 +98,18 @@ public class StandardBuildConfiguration extends CBuildConfiguration {
private void applyProperties() { private void applyProperties() {
setBuildContainer(getProperty(BUILD_CONTAINER)); setBuildContainer(getProperty(BUILD_CONTAINER));
String buildCmd = getProperty(BUILD_COMMAND); String buildCommand = getProperty(BUILD_COMMAND);
if (buildCmd != null && !buildCmd.trim().isEmpty()) { if (buildCommand != null && !buildCommand.isBlank()) {
buildCommand = buildCmd.split(" "); //$NON-NLS-1$ this.buildCommand = buildCommand;
} else { } else {
buildCommand = DEFAULT_BUILD_COMMAND; this.buildCommand = DEFAULT_BUILD_COMMAND;
} }
String cleanCmd = getProperty(CLEAN_COMMAND); String cleanCommand = getProperty(CLEAN_COMMAND);
if (cleanCmd != null && !cleanCmd.trim().isEmpty()) { if (cleanCommand != null && !cleanCommand.isBlank()) {
cleanCommand = cleanCmd.split(" "); //$NON-NLS-1$ this.cleanCommand = cleanCommand;
} else { } else {
cleanCommand = DEFAULT_CLEAN_COMMAND; this.cleanCommand = DEFAULT_CLEAN_COMMAND;
} }
} }
@ -161,20 +162,32 @@ public class StandardBuildConfiguration extends CBuildConfiguration {
} }
} }
public void setBuildCommand(String[] buildCommand) { /**
if (buildCommand != null) { * Set the build command to use. The string will be converted to
* command line arguments using {@link CommandLineUtil}
*
* @since 9.0
*/
public void setBuildCommand(String buildCommand) {
if (buildCommand != null && !buildCommand.isBlank()) {
this.buildCommand = buildCommand; this.buildCommand = buildCommand;
setProperty(BUILD_COMMAND, String.join(" ", buildCommand)); //$NON-NLS-1$ setProperty(BUILD_COMMAND, buildCommand);
} else { } else {
this.buildCommand = DEFAULT_BUILD_COMMAND; this.buildCommand = DEFAULT_BUILD_COMMAND;
removeProperty(BUILD_COMMAND); removeProperty(BUILD_COMMAND);
} }
} }
public void setCleanCommand(String[] cleanCommand) { /**
if (cleanCommand != null) { * Set the build command to use. The string will be converted to
* command line arguments using {@link CommandLineUtil}
*
* @since 9.0
*/
public void setCleanCommand(String cleanCommand) {
if (cleanCommand != null && !cleanCommand.isBlank()) {
this.cleanCommand = cleanCommand; this.cleanCommand = cleanCommand;
setProperty(CLEAN_COMMAND, String.join(" ", cleanCommand)); //$NON-NLS-1$ setProperty(CLEAN_COMMAND, cleanCommand);
} else { } else {
this.cleanCommand = DEFAULT_CLEAN_COMMAND; this.cleanCommand = DEFAULT_CLEAN_COMMAND;
removeProperty(CLEAN_COMMAND); removeProperty(CLEAN_COMMAND);
@ -212,9 +225,9 @@ public class StandardBuildConfiguration extends CBuildConfiguration {
return null; return null;
} }
case BUILD_COMMAND: case BUILD_COMMAND:
return String.join(" ", buildCommand); //$NON-NLS-1$ return buildCommand;
case CLEAN_COMMAND: case CLEAN_COMMAND:
return String.join(" ", cleanCommand); //$NON-NLS-1$ return cleanCommand;
} }
return null; return null;
@ -242,8 +255,9 @@ public class StandardBuildConfiguration extends CBuildConfiguration {
infoStream.write(String.format(Messages.StandardBuildConfiguration_0, buildDir.toString())); infoStream.write(String.format(Messages.StandardBuildConfiguration_0, buildDir.toString()));
String[] parsedBuildCommand = CommandLineUtil.argumentsToArray(buildCommand);
List<String> command = new ArrayList<>(); List<String> command = new ArrayList<>();
command.add(buildCommand[0]); command.add(parsedBuildCommand[0]);
if (!getBuildContainer().equals(getProject())) { if (!getBuildContainer().equals(getProject())) {
Path makefile = Paths.get(getProject().getFile("Makefile").getLocationURI()); //$NON-NLS-1$ Path makefile = Paths.get(getProject().getFile("Makefile").getLocationURI()); //$NON-NLS-1$
@ -252,15 +266,16 @@ public class StandardBuildConfiguration extends CBuildConfiguration {
command.add(relative.toString()); command.add(relative.toString());
} }
for (int i = 1; i < buildCommand.length; i++) { for (int i = 1; i < parsedBuildCommand.length; i++) {
command.add(buildCommand[i]); command.add(parsedBuildCommand[i]);
} }
try (ErrorParserManager epm = new ErrorParserManager(project, getProject().getLocationURI(), this, try (ErrorParserManager epm = new ErrorParserManager(project, getProject().getLocationURI(), this,
getToolChain().getErrorParserIds())) { getToolChain().getErrorParserIds())) {
epm.setOutputStream(console.getOutputStream()); epm.setOutputStream(console.getOutputStream());
// run make // run make
console.getOutputStream().write(String.format("%s\n", String.join(" ", command))); //$NON-NLS-1$ //$NON-NLS-2$ console.getOutputStream()
.write(String.format("%s\n", CommandLineUtil.argumentsToString(command, false))); //$NON-NLS-1$
org.eclipse.core.runtime.Path workingDir = new org.eclipse.core.runtime.Path( org.eclipse.core.runtime.Path workingDir = new org.eclipse.core.runtime.Path(
getBuildDirectory().toString()); getBuildDirectory().toString());
@ -301,9 +316,9 @@ public class StandardBuildConfiguration extends CBuildConfiguration {
List<String> command = new ArrayList<>(); List<String> command = new ArrayList<>();
List<String> buildCommand; List<String> buildCommand;
if (cleanCommand != null) { if (cleanCommand != null) {
buildCommand = Arrays.asList(cleanCommand); buildCommand = Arrays.asList(CommandLineUtil.argumentsToArray(cleanCommand));
} else { } else {
buildCommand = Arrays.asList(DEFAULT_CLEAN_COMMAND); buildCommand = Arrays.asList(CommandLineUtil.argumentsToArray(DEFAULT_CLEAN_COMMAND));
} }
command.add(buildCommand.get(0)); command.add(buildCommand.get(0));
@ -321,7 +336,7 @@ public class StandardBuildConfiguration extends CBuildConfiguration {
} }
// run make // run make
infoStream.write(String.format("%s\n", String.join(" ", command))); //$NON-NLS-1$ //$NON-NLS-2$ infoStream.write(String.format("%s\n", CommandLineUtil.argumentsToString(command, false))); //$NON-NLS-1$
org.eclipse.core.runtime.Path workingDir = new org.eclipse.core.runtime.Path( org.eclipse.core.runtime.Path workingDir = new org.eclipse.core.runtime.Path(
getBuildDirectory().toString()); getBuildDirectory().toString());

View file

@ -15,6 +15,7 @@
package org.eclipse.cdt.utils; package org.eclipse.cdt.utils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Platform;
import org.eclipse.osgi.service.environment.Constants; import org.eclipse.osgi.service.environment.Constants;
@ -269,6 +270,26 @@ public class CommandLineUtil {
return aList.toArray(new String[aList.size()]); return aList.toArray(new String[aList.size()]);
} }
/**
* Converts argument array to a string suitable for passing to Bash like:
*
* This process reverses {@link #argumentsToArray(String)}, but does not
* restore the exact same results.
*
* @param args
* the arguments to convert and escape
* @param encodeNewline
* <code>true</code> if newline (<code>\r</code> or
* <code>\n</code>) should be encoded
*
* @return args suitable for passing to some process that decodes the string
* into an argument array
* @since 9.0
*/
public static String argumentsToString(Collection<String> args, boolean encodeNewline) {
return argumentsToString(args.toArray(String[]::new), encodeNewline);
}
/** /**
* Converts argument array to a string suitable for passing to Bash like: * Converts argument array to a string suitable for passing to Bash like:
* *