| /******************************************************************************* |
| * Copyright (c) 2015, 2016 QNX Software Systems and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| *******************************************************************************/ |
| package org.eclipse.cdt.core.build; |
| |
| import java.io.BufferedReader; |
| import java.io.File; |
| import java.io.FileReader; |
| import java.io.FileWriter; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.io.OutputStream; |
| import java.io.PrintStream; |
| import java.lang.reflect.Type; |
| import java.net.URI; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| import java.nio.file.Paths; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| |
| import org.eclipse.cdt.core.CCorePlugin; |
| import org.eclipse.cdt.core.IConsoleParser; |
| import org.eclipse.cdt.core.IMarkerGenerator; |
| import org.eclipse.cdt.core.ProblemMarkerInfo; |
| import org.eclipse.cdt.core.envvar.IEnvironmentVariable; |
| import org.eclipse.cdt.core.model.CoreModel; |
| import org.eclipse.cdt.core.model.IBinary; |
| import org.eclipse.cdt.core.model.IBinaryContainer; |
| import org.eclipse.cdt.core.model.ICElement; |
| import org.eclipse.cdt.core.model.ICModelMarker; |
| import org.eclipse.cdt.core.model.ICProject; |
| import org.eclipse.cdt.core.model.ITranslationUnit; |
| import org.eclipse.cdt.core.parser.ExtendedScannerInfo; |
| import org.eclipse.cdt.core.parser.IExtendedScannerInfo; |
| import org.eclipse.cdt.core.parser.IScannerInfo; |
| import org.eclipse.cdt.core.parser.IScannerInfoChangeListener; |
| import org.eclipse.cdt.core.parser.IncludeExportPatterns; |
| import org.eclipse.cdt.core.resources.IConsole; |
| import org.eclipse.cdt.internal.core.parser.ParserSettings2; |
| import org.eclipse.core.filesystem.URIUtil; |
| import org.eclipse.core.resources.IBuildConfiguration; |
| import org.eclipse.core.resources.IContainer; |
| import org.eclipse.core.resources.IFolder; |
| import org.eclipse.core.resources.IMarker; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IProjectDescription; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.core.runtime.PlatformObject; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.preferences.InstanceScope; |
| import org.eclipse.osgi.util.NLS; |
| import org.osgi.service.prefs.BackingStoreException; |
| import org.osgi.service.prefs.Preferences; |
| |
| import com.google.gson.Gson; |
| import com.google.gson.GsonBuilder; |
| import com.google.gson.JsonArray; |
| import com.google.gson.JsonDeserializationContext; |
| import com.google.gson.JsonDeserializer; |
| import com.google.gson.JsonElement; |
| import com.google.gson.JsonObject; |
| import com.google.gson.JsonParseException; |
| |
| /** |
| * Root class for CDT build configurations. Provides access to the build |
| * settings for subclasses. |
| * |
| * @since 6.0 |
| * @noextend This class is provisional and should be subclassed with caution. |
| */ |
| public abstract class CBuildConfiguration extends PlatformObject |
| implements ICBuildConfiguration, IMarkerGenerator, IConsoleParser { |
| |
| private static final String TOOLCHAIN_TYPE = "cdt.toolChain.type"; //$NON-NLS-1$ |
| private static final String TOOLCHAIN_ID = "cdt.toolChain.id"; //$NON-NLS-1$ |
| private static final String TOOLCHAIN_VERSION = "cdt.toolChain.version"; //$NON-NLS-1$ |
| |
| private static final List<String> DEFAULT_COMMAND = new ArrayList<>(0); |
| |
| private final String name; |
| private final IBuildConfiguration config; |
| private final IToolChain toolChain; |
| |
| private final Map<IResource, List<IScannerInfoChangeListener>> scannerInfoListeners = new HashMap<>(); |
| private ScannerInfoCache scannerInfoCache; |
| |
| private Map<String, String> properties; |
| |
| protected CBuildConfiguration(IBuildConfiguration config, String name) throws CoreException { |
| this.config = config; |
| this.name = name; |
| |
| Preferences settings = getSettings(); |
| String typeId = settings.get(TOOLCHAIN_TYPE, ""); //$NON-NLS-1$ |
| String id = settings.get(TOOLCHAIN_ID, ""); //$NON-NLS-1$ |
| String version = settings.get(TOOLCHAIN_VERSION, ""); //$NON-NLS-1$ |
| IToolChainManager toolChainManager = CCorePlugin.getService(IToolChainManager.class); |
| IToolChain tc = toolChainManager.getToolChain(typeId, id, version); |
| |
| if (tc == null) { |
| // check for other versions |
| Collection<IToolChain> tcs = toolChainManager.getToolChains(typeId, id); |
| if (!tcs.isEmpty()) { |
| // TODO grab the newest version |
| tc = tcs.iterator().next(); |
| } else { |
| throw new CoreException(new Status(IStatus.ERROR, CCorePlugin.PLUGIN_ID, |
| String.format("Toolchain missing for config: %s", config.getName()))); |
| } |
| } |
| toolChain = tc; |
| } |
| |
| protected CBuildConfiguration(IBuildConfiguration config, String name, IToolChain toolChain) { |
| this.config = config; |
| this.name = name; |
| this.toolChain = toolChain; |
| |
| Preferences settings = getSettings(); |
| settings.put(TOOLCHAIN_TYPE, toolChain.getProvider().getId()); |
| settings.put(TOOLCHAIN_ID, toolChain.getId()); |
| settings.put(TOOLCHAIN_VERSION, toolChain.getVersion()); |
| try { |
| settings.flush(); |
| } catch (BackingStoreException e) { |
| CCorePlugin.log(e); |
| } |
| } |
| |
| protected CBuildConfiguration(IBuildConfiguration config, IToolChain toolChain) { |
| this(config, DEFAULT_NAME, toolChain); |
| } |
| |
| @Override |
| public IBuildConfiguration getBuildConfiguration() { |
| return config; |
| } |
| |
| public String getName() { |
| return name; |
| } |
| |
| public IProject getProject() { |
| return config.getProject(); |
| } |
| |
| @Override |
| public String getBinaryParserId() throws CoreException { |
| return toolChain != null ? toolChain.getBinaryParserId() : CCorePlugin.DEFAULT_BINARY_PARSER_UNIQ_ID; |
| } |
| |
| public IContainer getBuildContainer() throws CoreException { |
| // TODO should really be passing a monitor in here or create this in |
| // a better spot. should also throw the core exception |
| // TODO make the name of this folder a project property |
| IProgressMonitor monitor = new NullProgressMonitor(); |
| IProject project = getProject(); |
| IFolder buildRootFolder = project.getFolder("build"); //$NON-NLS-1$ |
| if (!buildRootFolder.exists()) { |
| buildRootFolder.create(IResource.FORCE | IResource.DERIVED, true, monitor); |
| } |
| IFolder buildFolder = buildRootFolder.getFolder(name); |
| if (!buildFolder.exists()) { |
| buildFolder.create(IResource.FORCE | IResource.DERIVED, true, monitor); |
| } |
| |
| return buildFolder; |
| } |
| |
| public URI getBuildDirectoryURI() throws CoreException { |
| return getBuildContainer().getLocationURI(); |
| } |
| |
| public Path getBuildDirectory() throws CoreException { |
| return Paths.get(getBuildDirectoryURI()); |
| } |
| |
| @Override |
| public void setBuildEnvironment(Map<String, String> env) { |
| CCorePlugin.getDefault().getBuildEnvironmentManager().setEnvironment(env, config, true); |
| } |
| |
| /** |
| * @since 6.1 |
| */ |
| @Override |
| public IBinary[] getBuildOutput() throws CoreException { |
| ICProject cproject = CoreModel.getDefault().create(config.getProject()); |
| IBinaryContainer binaries = cproject.getBinaryContainer(); |
| IPath outputPath = getBuildContainer().getFullPath(); |
| List<IBinary> outputs = new ArrayList<>(); |
| for (IBinary binary : binaries.getBinaries()) { |
| if (binary.isExecutable() && outputPath.isPrefixOf(binary.getPath())) { |
| outputs.add(binary); |
| } |
| } |
| return outputs.toArray(new IBinary[outputs.size()]); |
| } |
| |
| public void setActive(IProgressMonitor monitor) throws CoreException { |
| IProject project = config.getProject(); |
| if (config.equals(project.getActiveBuildConfig())) { |
| // already set |
| return; |
| } |
| |
| IProjectDescription projectDesc = project.getDescription(); |
| projectDesc.setActiveBuildConfig(config.getName()); |
| project.setDescription(projectDesc, monitor); |
| } |
| |
| protected Preferences getSettings() { |
| return InstanceScope.INSTANCE.getNode(CCorePlugin.PLUGIN_ID).node("config") //$NON-NLS-1$ |
| .node(getProject().getName()).node(config.getName()); |
| } |
| |
| @Override |
| public IToolChain getToolChain() throws CoreException { |
| return toolChain; |
| } |
| |
| @Override |
| public IEnvironmentVariable getVariable(String name) { |
| // By default, none |
| return null; |
| } |
| |
| @Override |
| public IEnvironmentVariable[] getVariables() { |
| // by default, none |
| return null; |
| } |
| |
| @Override |
| public void addMarker(IResource file, int lineNumber, String errorDesc, int severity, String errorVar) { |
| addMarker(new ProblemMarkerInfo(file, lineNumber, errorDesc, severity, errorVar, null)); |
| } |
| |
| @Override |
| public void addMarker(ProblemMarkerInfo problemMarkerInfo) { |
| try { |
| IProject project = config.getProject(); |
| IResource markerResource = problemMarkerInfo.file; |
| if (markerResource == null) { |
| markerResource = project; |
| } |
| String externalLocation = null; |
| if (problemMarkerInfo.externalPath != null && !problemMarkerInfo.externalPath.isEmpty()) { |
| externalLocation = problemMarkerInfo.externalPath.toOSString(); |
| } |
| |
| // Try to find matching markers and don't put in duplicates |
| IMarker[] markers = markerResource.findMarkers(ICModelMarker.C_MODEL_PROBLEM_MARKER, true, |
| IResource.DEPTH_ONE); |
| for (IMarker m : markers) { |
| int line = m.getAttribute(IMarker.LINE_NUMBER, -1); |
| int sev = m.getAttribute(IMarker.SEVERITY, -1); |
| String msg = (String) m.getAttribute(IMarker.MESSAGE); |
| if (line == problemMarkerInfo.lineNumber |
| && sev == mapMarkerSeverity(problemMarkerInfo.severity) |
| && msg.equals(problemMarkerInfo.description)) { |
| String extloc = (String) m.getAttribute(ICModelMarker.C_MODEL_MARKER_EXTERNAL_LOCATION); |
| if (extloc == externalLocation || (extloc != null && extloc.equals(externalLocation))) { |
| if (project == null || project.equals(markerResource.getProject())) { |
| return; |
| } |
| String source = (String) m.getAttribute(IMarker.SOURCE_ID); |
| if (project.getName().equals(source)) { |
| return; |
| } |
| } |
| } |
| } |
| |
| String type = problemMarkerInfo.getType(); |
| if (type == null) { |
| type = ICModelMarker.C_MODEL_PROBLEM_MARKER; |
| } |
| |
| IMarker marker = markerResource.createMarker(type); |
| marker.setAttribute(IMarker.MESSAGE, problemMarkerInfo.description); |
| marker.setAttribute(IMarker.SEVERITY, mapMarkerSeverity(problemMarkerInfo.severity)); |
| marker.setAttribute(IMarker.LINE_NUMBER, problemMarkerInfo.lineNumber); |
| marker.setAttribute(IMarker.CHAR_START, problemMarkerInfo.startChar); |
| marker.setAttribute(IMarker.CHAR_END, problemMarkerInfo.endChar); |
| if (problemMarkerInfo.variableName != null) { |
| marker.setAttribute(ICModelMarker.C_MODEL_MARKER_VARIABLE, problemMarkerInfo.variableName); |
| } |
| if (externalLocation != null) { |
| URI uri = URIUtil.toURI(externalLocation); |
| if (uri.getScheme() != null) { |
| marker.setAttribute(ICModelMarker.C_MODEL_MARKER_EXTERNAL_LOCATION, externalLocation); |
| String locationText = NLS.bind( |
| CCorePlugin.getResourceString("ACBuilder.ProblemsView.Location"), //$NON-NLS-1$ |
| problemMarkerInfo.lineNumber, externalLocation); |
| marker.setAttribute(IMarker.LOCATION, locationText); |
| } |
| } else if (problemMarkerInfo.lineNumber == 0) { |
| marker.setAttribute(IMarker.LOCATION, " "); //$NON-NLS-1$ |
| } |
| // Set source attribute only if the marker is being set to a file |
| // from different project |
| if (project != null && !project.equals(markerResource.getProject())) { |
| marker.setAttribute(IMarker.SOURCE_ID, project.getName()); |
| } |
| |
| // Add all other client defined attributes. |
| Map<String, String> attributes = problemMarkerInfo.getAttributes(); |
| if (attributes != null) { |
| for (Entry<String, String> entry : attributes.entrySet()) { |
| marker.setAttribute(entry.getKey(), entry.getValue()); |
| } |
| } |
| } catch (CoreException e) { |
| CCorePlugin.log(e.getStatus()); |
| } |
| } |
| |
| private int mapMarkerSeverity(int severity) { |
| switch (severity) { |
| case SEVERITY_ERROR_BUILD: |
| case SEVERITY_ERROR_RESOURCE: |
| return IMarker.SEVERITY_ERROR; |
| case SEVERITY_INFO: |
| return IMarker.SEVERITY_INFO; |
| case SEVERITY_WARNING: |
| return IMarker.SEVERITY_WARNING; |
| } |
| return IMarker.SEVERITY_ERROR; |
| } |
| |
| protected Path findCommand(String command) { |
| if (Platform.getOS().equals(Platform.OS_WIN32) && !command.endsWith(".exe")) { //$NON-NLS-1$ |
| command += ".exe"; //$NON-NLS-1$ |
| } |
| |
| Path cmdPath = Paths.get(command); |
| if (cmdPath.isAbsolute()) { |
| return cmdPath; |
| } |
| |
| Map<String, String> env = new HashMap<>(System.getenv()); |
| setBuildEnvironment(env); |
| |
| String[] path = env.get("PATH").split(File.pathSeparator); //$NON-NLS-1$ |
| for (String dir : path) { |
| Path commandPath = Paths.get(dir, command); |
| if (Files.exists(commandPath)) { |
| return commandPath; |
| } |
| } |
| return null; |
| } |
| |
| protected int watchProcess(Process process, IConsoleParser[] consoleParsers, IConsole console) |
| throws CoreException { |
| new ReaderThread(process.getInputStream(), consoleParsers, console.getOutputStream()).start(); |
| new ReaderThread(process.getErrorStream(), consoleParsers, console.getErrorStream()).start(); |
| try { |
| return process.waitFor(); |
| } catch (InterruptedException e) { |
| CCorePlugin.log(e); |
| return -1; |
| } |
| } |
| |
| private static class ReaderThread extends Thread { |
| |
| private final BufferedReader in; |
| private final PrintStream out; |
| private final IConsoleParser[] consoleParsers; |
| |
| public ReaderThread(InputStream in, IConsoleParser[] consoleParsers, OutputStream out) { |
| this.in = new BufferedReader(new InputStreamReader(in)); |
| this.consoleParsers = consoleParsers; |
| this.out = new PrintStream(out); |
| } |
| |
| @Override |
| public void run() { |
| try { |
| for (String line = in.readLine(); line != null; line = in.readLine()) { |
| for (IConsoleParser consoleParser : consoleParsers) { |
| // Synchronize to avoid interleaving of lines |
| synchronized (consoleParser) { |
| consoleParser.processLine(line); |
| } |
| } |
| out.println(line); |
| } |
| } catch (IOException e) { |
| CCorePlugin.log(e); |
| } |
| } |
| |
| } |
| |
| private File getScannerInfoCacheFile() { |
| return CCorePlugin.getDefault().getStateLocation().append("infoCache") //$NON-NLS-1$ |
| .append(getProject().getName()).append(name + ".json").toFile(); //$NON-NLS-1$ |
| } |
| |
| private static class IExtendedScannerInfoCreator implements JsonDeserializer<IExtendedScannerInfo> { |
| @Override |
| public IExtendedScannerInfo deserialize(JsonElement element, Type arg1, |
| JsonDeserializationContext arg2) throws JsonParseException { |
| JsonObject infoObj = element.getAsJsonObject(); |
| |
| Map<String, String> definedSymbols = null; |
| if (infoObj.has("definedSymbols")) { //$NON-NLS-1$ |
| JsonObject definedSymbolsObj = infoObj.get("definedSymbols").getAsJsonObject(); //$NON-NLS-1$ |
| definedSymbols = new HashMap<>(); |
| for (Entry<String, JsonElement> entry : definedSymbolsObj.entrySet()) { |
| definedSymbols.put(entry.getKey(), entry.getValue().getAsString()); |
| } |
| } |
| |
| String[] includePaths = null; |
| if (infoObj.has("includePaths")) { //$NON-NLS-1$ |
| JsonArray includePathsArray = infoObj.get("includePaths").getAsJsonArray(); //$NON-NLS-1$ |
| List<String> includePathsList = new ArrayList<>(includePathsArray.size()); |
| for (Iterator<JsonElement> i = includePathsArray.iterator(); i.hasNext();) { |
| includePathsList.add(i.next().getAsString()); |
| } |
| includePaths = includePathsList.toArray(new String[includePathsList.size()]); |
| } |
| |
| IncludeExportPatterns includeExportPatterns = null; |
| if (infoObj.has("includeExportPatterns")) { //$NON-NLS-1$ |
| JsonObject includeExportPatternsObj = infoObj.get("includeExportPatterns").getAsJsonObject(); //$NON-NLS-1$ |
| String exportPattern = null; |
| if (includeExportPatternsObj.has("includeExportPattern")) { //$NON-NLS-1$ |
| exportPattern = includeExportPatternsObj.get("includeExportPattern") //$NON-NLS-1$ |
| .getAsJsonObject().get("pattern").getAsString(); //$NON-NLS-1$ |
| } |
| |
| String beginExportsPattern = null; |
| if (includeExportPatternsObj.has("includeBeginExportPattern")) { //$NON-NLS-1$ |
| beginExportsPattern = includeExportPatternsObj.get("includeBeginExportPattern") //$NON-NLS-1$ |
| .getAsJsonObject().get("pattern").getAsString(); //$NON-NLS-1$ |
| } |
| |
| String endExportsPattern = null; |
| if (includeExportPatternsObj.has("includeEndExportPattern")) { //$NON-NLS-1$ |
| endExportsPattern = includeExportPatternsObj.get("includeEndExportPattern") //$NON-NLS-1$ |
| .getAsJsonObject().get("pattern").getAsString(); //$NON-NLS-1$ |
| } |
| |
| includeExportPatterns = new IncludeExportPatterns(exportPattern, beginExportsPattern, |
| endExportsPattern); |
| } |
| |
| ExtendedScannerInfo info = new ExtendedScannerInfo(definedSymbols, includePaths); |
| info.setIncludeExportPatterns(includeExportPatterns); |
| info.setParserSettings(new ParserSettings2()); |
| return info; |
| } |
| } |
| |
| /** |
| * @since 6.1 |
| */ |
| protected void loadScannerInfoCache() { |
| if (scannerInfoCache == null) { |
| File cacheFile = getScannerInfoCacheFile(); |
| if (cacheFile.exists()) { |
| try (FileReader reader = new FileReader(cacheFile)) { |
| GsonBuilder gsonBuilder = new GsonBuilder(); |
| gsonBuilder.registerTypeAdapter(IExtendedScannerInfo.class, |
| new IExtendedScannerInfoCreator()); |
| Gson gson = gsonBuilder.create(); |
| scannerInfoCache = gson.fromJson(reader, ScannerInfoCache.class); |
| } catch (IOException e) { |
| CCorePlugin.log(e); |
| scannerInfoCache = new ScannerInfoCache(); |
| } |
| } else { |
| scannerInfoCache = new ScannerInfoCache(); |
| } |
| scannerInfoCache.initCache(); |
| } |
| } |
| |
| /** |
| * @since 6.1 |
| */ |
| protected void saveScannerInfoCache() { |
| File cacheFile = getScannerInfoCacheFile(); |
| if (!cacheFile.getParentFile().exists()) { |
| try { |
| Files.createDirectories(cacheFile.getParentFile().toPath()); |
| } catch (IOException e) { |
| CCorePlugin.log(e); |
| return; |
| } |
| } |
| |
| try (FileWriter writer = new FileWriter(getScannerInfoCacheFile())) { |
| Gson gson = new Gson(); |
| gson.toJson(scannerInfoCache, writer); |
| } catch (IOException e) { |
| CCorePlugin.log(e); |
| } |
| } |
| |
| /** |
| * @since 6.1 |
| */ |
| protected ScannerInfoCache getScannerInfoCache() { |
| return scannerInfoCache; |
| } |
| |
| @Override |
| public IScannerInfo getScannerInformation(IResource resource) { |
| loadScannerInfoCache(); |
| IExtendedScannerInfo info = scannerInfoCache.getScannerInfo(resource); |
| if (info == null) { |
| ICElement celement = CCorePlugin.getDefault().getCoreModel().create(resource); |
| if (celement instanceof ITranslationUnit) { |
| ITranslationUnit tu = (ITranslationUnit) celement; |
| try { |
| info = getToolChain().getDefaultScannerInfo(getBuildConfiguration(), null, |
| tu.getLanguage(), getBuildDirectoryURI()); |
| scannerInfoCache.addScannerInfo(DEFAULT_COMMAND, info, resource); |
| saveScannerInfoCache(); |
| } catch (CoreException e) { |
| CCorePlugin.log(e.getStatus()); |
| } |
| } |
| } |
| return info; |
| } |
| |
| private boolean infoChanged = false; |
| |
| @Override |
| public boolean processLine(String line) { |
| // TODO smarter line parsing to deal with quoted arguments |
| List<String> command = Arrays.asList(line.split("\\s+")); //$NON-NLS-1$ |
| |
| // Make sure it's a compile command |
| String[] compileCommands = toolChain.getCompileCommands(); |
| loop: |
| for (String arg : command) { |
| if (arg.startsWith("-")) { //$NON-NLS-1$ |
| // option found, missed our command |
| return false; |
| } |
| |
| for (String cc : compileCommands) { |
| if (arg.endsWith(cc) |
| && (arg.equals(cc) || arg.endsWith("/" + cc) || arg.endsWith("\\" + cc))) { //$NON-NLS-1$ //$NON-NLS-2$ |
| break loop; |
| } |
| } |
| } |
| |
| try { |
| IResource[] resources = toolChain.getResourcesFromCommand(command, getBuildDirectoryURI()); |
| if (resources != null) { |
| List<String> commandStrings = toolChain.stripCommand(command, resources); |
| |
| for (IResource resource : resources) { |
| loadScannerInfoCache(); |
| if (scannerInfoCache.hasCommand(commandStrings)) { |
| scannerInfoCache.addResource(commandStrings, resource); |
| } else { |
| Path commandPath = findCommand(command.get(0)); |
| command.set(0, commandPath.toString()); |
| IExtendedScannerInfo info = getToolChain().getScannerInfo(getBuildConfiguration(), |
| command, null, resource, getBuildDirectoryURI()); |
| scannerInfoCache.addScannerInfo(commandStrings, info, resource); |
| } |
| infoChanged = true; |
| } |
| return true; |
| } else { |
| return false; |
| } |
| } catch (CoreException e) { |
| CCorePlugin.log(e); |
| return false; |
| } |
| } |
| |
| @Override |
| public void shutdown() { |
| // TODO persist changes |
| |
| // Trigger a reindex if anything changed |
| // TODO be more surgical |
| if (infoChanged) { |
| saveScannerInfoCache(); |
| CCorePlugin.getIndexManager().reindex(CoreModel.getDefault().create(getProject())); |
| infoChanged = false; |
| } |
| } |
| |
| @Override |
| public void subscribe(IResource resource, IScannerInfoChangeListener listener) { |
| List<IScannerInfoChangeListener> listeners = scannerInfoListeners.get(resource); |
| if (listeners == null) { |
| listeners = new ArrayList<>(); |
| scannerInfoListeners.put(resource, listeners); |
| } |
| listeners.add(listener); |
| } |
| |
| @Override |
| public void unsubscribe(IResource resource, IScannerInfoChangeListener listener) { |
| List<IScannerInfoChangeListener> listeners = scannerInfoListeners.get(resource); |
| if (listeners != null) { |
| listeners.remove(listener); |
| if (listeners.isEmpty()) { |
| scannerInfoListeners.remove(resource); |
| } |
| } |
| } |
| |
| /** |
| * Takes a command path and returns either the command path itself if it is |
| * absolute or the path to the command as it appears in the PATH environment |
| * variable. Also adjusts the command for Windows's .exe extension. |
| * |
| * @since 6.1 |
| */ |
| public static Path getCommandFromPath(Path command) { |
| if (command.isAbsolute()) { |
| return command; |
| } |
| |
| if (Platform.getOS().equals(Platform.OS_WIN32)) { |
| if (!command.toString().endsWith(".exe")) { //$NON-NLS-1$ |
| command = Paths.get(command.toString() + ".exe"); //$NON-NLS-1$ |
| } |
| } |
| |
| // Look for it in the path environment var |
| String path = System.getenv("PATH"); //$NON-NLS-1$ |
| for (String entry : path.split(File.pathSeparator)) { |
| Path entryPath = Paths.get(entry); |
| Path cmdPath = entryPath.resolve(command); |
| if (Files.isExecutable(cmdPath)) { |
| return cmdPath; |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * @since 6.2 |
| */ |
| @Override |
| public void setProperties(Map<String, String> properties) { |
| this.properties = properties; |
| } |
| |
| /** |
| * @since 6.2 |
| */ |
| @Override |
| public Map<String, String> getProperties() { |
| return properties != null ? properties : new HashMap<>(); |
| } |
| |
| } |