mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-07-20 15:35:24 +02:00
Bug 566385: integrate cmake build-output parser
Change-Id: Iaf6b6f6e653571f666474e096aec4cb29893088c Signed-off-by: Martin Weber <fifteenknots505@gmail.com>
This commit is contained in:
parent
b73f02b4d1
commit
a6baf50a47
3 changed files with 153 additions and 15 deletions
|
@ -38,14 +38,18 @@ import org.eclipse.cdt.core.build.CBuildConfiguration;
|
||||||
import org.eclipse.cdt.core.build.IToolChain;
|
import org.eclipse.cdt.core.build.IToolChain;
|
||||||
import org.eclipse.cdt.core.envvar.EnvironmentVariable;
|
import org.eclipse.cdt.core.envvar.EnvironmentVariable;
|
||||||
import org.eclipse.cdt.core.envvar.IEnvironmentVariable;
|
import org.eclipse.cdt.core.envvar.IEnvironmentVariable;
|
||||||
|
import org.eclipse.cdt.core.model.ElementChangedEvent;
|
||||||
|
import org.eclipse.cdt.core.model.ICElementDelta;
|
||||||
import org.eclipse.cdt.core.model.ICModelMarker;
|
import org.eclipse.cdt.core.model.ICModelMarker;
|
||||||
import org.eclipse.cdt.core.parser.ExtendedScannerInfo;
|
import org.eclipse.cdt.core.parser.ExtendedScannerInfo;
|
||||||
import org.eclipse.cdt.core.parser.IScannerInfo;
|
import org.eclipse.cdt.core.parser.IScannerInfo;
|
||||||
import org.eclipse.cdt.core.resources.IConsole;
|
import org.eclipse.cdt.core.resources.IConsole;
|
||||||
import org.eclipse.core.resources.IBuildConfiguration;
|
import org.eclipse.core.resources.IBuildConfiguration;
|
||||||
|
import org.eclipse.core.resources.IContainer;
|
||||||
import org.eclipse.core.resources.IFile;
|
import org.eclipse.core.resources.IFile;
|
||||||
import org.eclipse.core.resources.IProject;
|
import org.eclipse.core.resources.IProject;
|
||||||
import org.eclipse.core.resources.IResource;
|
import org.eclipse.core.resources.IResource;
|
||||||
|
import org.eclipse.core.resources.IResourceDelta;
|
||||||
import org.eclipse.core.resources.ResourcesPlugin;
|
import org.eclipse.core.resources.ResourcesPlugin;
|
||||||
import org.eclipse.core.runtime.CoreException;
|
import org.eclipse.core.runtime.CoreException;
|
||||||
import org.eclipse.core.runtime.IProgressMonitor;
|
import org.eclipse.core.runtime.IProgressMonitor;
|
||||||
|
@ -63,6 +67,15 @@ public class CMakeBuildConfiguration extends CBuildConfiguration {
|
||||||
|
|
||||||
private ICMakeToolChainFile toolChainFile;
|
private ICMakeToolChainFile toolChainFile;
|
||||||
private Map<IResource, IScannerInfo> infoPerResource;
|
private Map<IResource, IScannerInfo> infoPerResource;
|
||||||
|
/** whether one of the CMakeLists.txt files in the project has been
|
||||||
|
* modified and saved by the user since the last build.<br>
|
||||||
|
* Cmake-generated build scripts re-run cmake if one of the CMakeLists.txt files was modified,
|
||||||
|
* but that output goes through ErrorParserManager and is impossible to parse because cmake
|
||||||
|
* outputs to both stderr and stdout and ErrorParserManager intermixes these streams making it
|
||||||
|
* impossible to parse for errors.<br>
|
||||||
|
* To work around that, we run cmake in advance with its dedicated working error parser.
|
||||||
|
*/
|
||||||
|
private boolean cmakeListsModified;
|
||||||
|
|
||||||
public CMakeBuildConfiguration(IBuildConfiguration config, String name) throws CoreException {
|
public CMakeBuildConfiguration(IBuildConfiguration config, String name) throws CoreException {
|
||||||
super(config, name);
|
super(config, name);
|
||||||
|
@ -110,6 +123,7 @@ public class CMakeBuildConfiguration extends CBuildConfiguration {
|
||||||
|
|
||||||
Path buildDir = getBuildDirectory();
|
Path buildDir = getBuildDirectory();
|
||||||
|
|
||||||
|
// TODO print to info stream here
|
||||||
outStream.write(String.format(Messages.CMakeBuildConfiguration_BuildingIn, buildDir.toString()));
|
outStream.write(String.format(Messages.CMakeBuildConfiguration_BuildingIn, buildDir.toString()));
|
||||||
|
|
||||||
// Make sure we have a toolchain file if cross
|
// Make sure we have a toolchain file if cross
|
||||||
|
@ -124,21 +138,24 @@ public class CMakeBuildConfiguration extends CBuildConfiguration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean runCMake;
|
boolean runCMake = cmakeListsModified;
|
||||||
switch (generator) {
|
if (!runCMake) {
|
||||||
case "Ninja": //$NON-NLS-1$
|
switch (generator) {
|
||||||
runCMake = !Files.exists(buildDir.resolve("build.ninja")); //$NON-NLS-1$
|
case "Ninja": //$NON-NLS-1$
|
||||||
break;
|
runCMake = !Files.exists(buildDir.resolve("build.ninja")); //$NON-NLS-1$
|
||||||
case "Unix Makefiles": //$NON-NLS-1$
|
break;
|
||||||
runCMake = !Files.exists(buildDir.resolve("Makefile")); //$NON-NLS-1$
|
case "Unix Makefiles": //$NON-NLS-1$
|
||||||
break;
|
runCMake = !Files.exists(buildDir.resolve("Makefile")); //$NON-NLS-1$
|
||||||
default:
|
break;
|
||||||
runCMake = !Files.exists(buildDir.resolve("CMakeFiles")); //$NON-NLS-1$
|
default:
|
||||||
|
runCMake = !Files.exists(buildDir.resolve("CMakeFiles")); //$NON-NLS-1$
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (runCMake) {
|
if (runCMake) {
|
||||||
CMakeBuildConfiguration.deleteCMakeErrorMarkers(project);
|
CMakeBuildConfiguration.deleteCMakeErrorMarkers(project);
|
||||||
|
|
||||||
|
// TODO print to info stream here
|
||||||
console.getOutputStream().write(String.format(Messages.CMakeBuildConfiguration_Configuring, buildDir));
|
console.getOutputStream().write(String.format(Messages.CMakeBuildConfiguration_Configuring, buildDir));
|
||||||
// clean output to make sure there is no content
|
// clean output to make sure there is no content
|
||||||
// incompatible with current settings (cmake config would fail)
|
// incompatible with current settings (cmake config would fail)
|
||||||
|
@ -170,20 +187,23 @@ public class CMakeBuildConfiguration extends CBuildConfiguration {
|
||||||
command.addAll(Arrays.asList(userArgs.trim().split("\\s+"))); //$NON-NLS-1$
|
command.addAll(Arrays.asList(userArgs.trim().split("\\s+"))); //$NON-NLS-1$
|
||||||
}
|
}
|
||||||
|
|
||||||
command.add(new File(project.getLocationURI()).getAbsolutePath());
|
IContainer srcFolder = project;
|
||||||
|
command.add(new File(srcFolder.getLocationURI()).getAbsolutePath());
|
||||||
|
|
||||||
outStream.write(String.join(" ", command) + '\n'); //$NON-NLS-1$
|
outStream.write(String.join(" ", command) + '\n'); //$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());
|
||||||
// TODO hook in cmake error parsing here
|
// hook in cmake error parsing
|
||||||
Process p = startBuildProcess(command, new IEnvironmentVariable[0], workingDir, console, monitor);
|
IConsole errConsole = new CMakeConsoleWrapper(srcFolder, console);
|
||||||
|
Process p = startBuildProcess(command, new IEnvironmentVariable[0], workingDir, errConsole, monitor);
|
||||||
if (p == null) {
|
if (p == null) {
|
||||||
console.getErrorStream().write(String.format(Messages.CMakeBuildConfiguration_Failure, "")); //$NON-NLS-1$
|
console.getErrorStream().write(String.format(Messages.CMakeBuildConfiguration_Failure, "")); //$NON-NLS-1$
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
watchProcess(p, console);
|
watchProcess(p, errConsole);
|
||||||
|
cmakeListsModified = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
try (ErrorParserManager epm = new ErrorParserManager(project, getBuildDirectoryURI(), this,
|
try (ErrorParserManager epm = new ErrorParserManager(project, getBuildDirectoryURI(), this,
|
||||||
|
@ -239,6 +259,7 @@ public class CMakeBuildConfiguration extends CBuildConfiguration {
|
||||||
// output
|
// output
|
||||||
processCompileCommandsFile(console, monitor);
|
processCompileCommandsFile(console, monitor);
|
||||||
|
|
||||||
|
// TODO print to info stream here
|
||||||
outStream.write(String.format(Messages.CMakeBuildConfiguration_BuildingComplete, epm.getErrorCount(),
|
outStream.write(String.format(Messages.CMakeBuildConfiguration_BuildingComplete, epm.getErrorCount(),
|
||||||
epm.getWarningCount(), buildDir.toString()));
|
epm.getWarningCount(), buildDir.toString()));
|
||||||
}
|
}
|
||||||
|
@ -435,6 +456,56 @@ public class CMakeBuildConfiguration extends CBuildConfiguration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Overwritten to detect whether one of the CMakeLists.txt files in the project was modified since the last build.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void elementChanged(ElementChangedEvent event) {
|
||||||
|
super.elementChanged(event);
|
||||||
|
// Only respond to post change events
|
||||||
|
if (event.getType() != ElementChangedEvent.POST_CHANGE)
|
||||||
|
return;
|
||||||
|
if (!cmakeListsModified) {
|
||||||
|
processElementDelta(event.getDelta());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Processes the delta in order to detect whether one of the CMakeLists.txt files in the project has been
|
||||||
|
* modified and saved by the user since the last build.
|
||||||
|
* @return <code>true</code> to continue with delta processing, otherwise <code>false</code>
|
||||||
|
*/
|
||||||
|
private boolean processElementDelta(ICElementDelta delta) {
|
||||||
|
if (delta == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (delta.getKind() == ICElementDelta.CHANGED) {
|
||||||
|
// check for modified CMakeLists.txt file
|
||||||
|
if (0 != (delta.getFlags() & ICElementDelta.F_CONTENT)) {
|
||||||
|
IResourceDelta[] resourceDeltas = delta.getResourceDeltas();
|
||||||
|
if (resourceDeltas != null) {
|
||||||
|
for (IResourceDelta resourceDelta : resourceDeltas) {
|
||||||
|
IResource resource = resourceDelta.getResource();
|
||||||
|
if (resource.getType() == IResource.FILE) {
|
||||||
|
String name = resource.getName();
|
||||||
|
if (name.equals("CMakeLists.txt") || name.endsWith(".cmake")) { //$NON-NLS-1$ //$NON-NLS-2$
|
||||||
|
cmakeListsModified = true;
|
||||||
|
return false; // stop processing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// recurse...
|
||||||
|
for (ICElementDelta child : delta.getAffectedChildren()) {
|
||||||
|
if (!processElementDelta(child)) {
|
||||||
|
return false; // stop processing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/** Overwritten since we do not parse console output to get scanner information.
|
/** Overwritten since we do not parse console output to get scanner information.
|
||||||
*/
|
*/
|
||||||
// interface IConsoleParser2
|
// interface IConsoleParser2
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2020 Martin Weber.
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
package org.eclipse.cdt.cmake.core.internal;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import org.eclipse.cdt.core.ConsoleOutputStream;
|
||||||
|
import org.eclipse.cdt.core.resources.IConsole;
|
||||||
|
import org.eclipse.core.resources.IContainer;
|
||||||
|
import org.eclipse.core.resources.IProject;
|
||||||
|
import org.eclipse.core.runtime.CoreException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Martin Weber
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class CMakeConsoleWrapper implements IConsole {
|
||||||
|
private final IConsole delegate;
|
||||||
|
private final ConsoleOutputStream out;
|
||||||
|
private final ConsoleOutputStream err;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param srcFolder
|
||||||
|
* the source root of the project being built
|
||||||
|
* @param delegate
|
||||||
|
* the console to wrap
|
||||||
|
*/
|
||||||
|
public CMakeConsoleWrapper(IContainer srcFolder, IConsole delegate) throws CoreException {
|
||||||
|
Objects.requireNonNull(srcFolder);
|
||||||
|
this.delegate = Objects.requireNonNull(delegate);
|
||||||
|
// NOTE: we need one parser for each stream, since the output streams are not synchronized
|
||||||
|
// when the process is started via o.e.c.core.CommandLauncher, causing loss of
|
||||||
|
// the internal parser state
|
||||||
|
out = new CMakeErrorParser(srcFolder, delegate.getOutputStream());
|
||||||
|
err = new CMakeErrorParser(srcFolder, delegate.getErrorStream());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start(IProject project) {
|
||||||
|
delegate.start(project);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConsoleOutputStream getInfoStream() throws CoreException {
|
||||||
|
return delegate.getInfoStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConsoleOutputStream getOutputStream() throws CoreException {
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConsoleOutputStream getErrorStream() throws CoreException {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,6 +14,7 @@ import java.util.Objects;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import org.eclipse.cdt.core.ConsoleOutputStream;
|
||||||
import org.eclipse.core.resources.IContainer;
|
import org.eclipse.core.resources.IContainer;
|
||||||
import org.eclipse.core.resources.IMarker;
|
import org.eclipse.core.resources.IMarker;
|
||||||
import org.eclipse.core.runtime.CoreException;
|
import org.eclipse.core.runtime.CoreException;
|
||||||
|
@ -26,7 +27,7 @@ import org.eclipse.core.runtime.Status;
|
||||||
*
|
*
|
||||||
* @author Martin Weber
|
* @author Martin Weber
|
||||||
*/
|
*/
|
||||||
/* package */ class CMakeErrorParser extends OutputStream {
|
/* package */ class CMakeErrorParser extends ConsoleOutputStream {
|
||||||
|
|
||||||
public static final String CMAKE_PROBLEM_MARKER_ID = Activator.getId() + ".cmakeproblem"; //$NON-NLS-1$
|
public static final String CMAKE_PROBLEM_MARKER_ID = Activator.getId() + ".cmakeproblem"; //$NON-NLS-1$
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue