Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/*******************************************************************************
* Copyright 2026 Espressif Systems (Shanghai) PTE LTD. All rights reserved.
* Use is subject to license terms.
*******************************************************************************/
package com.espressif.idf.core.util;

import java.io.BufferedReader;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import com.espressif.idf.core.logging.Logger;

public class OpenOcdVersionManager
{
private OpenOcdVersionManager()
{
/* This utility class should not be instantiated */
}

private static final Map<String, OpenOcdVersion> versionCache = new ConcurrentHashMap<>();

private static final Pattern VERSION_PATTERN = Pattern.compile(
"(?i)(?:Open On-Chip Debugger\\s+|v)(?<major>\\d+)\\.(?<minor>\\d+)(?:\\.(?<patch>\\d+))?(?:-[a-z0-9]+-(?<build>\\d+))?"); //$NON-NLS-1$

public static class OpenOcdVersion
{
public final int major;
public final int minor;
public final int patch;
public final int buildDate;

public OpenOcdVersion(int major, int minor, int patch, int buildDate)
{
this.major = major;
this.minor = minor;
this.patch = patch;
this.buildDate = buildDate;
}

public boolean isAtLeast(int targetMajor, int targetMinor)
{
return isAtLeast(targetMajor, targetMinor, 0);
}

public boolean isAtLeast(int targetMajor, int targetMinor, int targetPatch)
{

if (this.major > targetMajor)
return true;
if (this.major == targetMajor && this.minor > targetMinor)
return true;
return (this.major == targetMajor && this.minor == targetMinor && this.patch >= targetPatch);
}

public boolean isBuildDateAtLeast(int targetMajor, int targetMinor, int targetBuildDate)
{
// If strictly newer major/minor, we assume the feature exists
if (this.major > targetMajor)
return true;
if (this.major == targetMajor && this.minor > targetMinor)
return true;

// If exactly the same major/minor, check the build date
if (this.major == targetMajor && this.minor == targetMinor)
{
return this.buildDate >= targetBuildDate;
}
return false;
}

@Override
public String toString()
{
return major + "." + minor + "." + patch + " (Build: " + buildDate + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
}
}

public static OpenOcdVersion getVersion(String executablePath)
{
if (executablePath == null || executablePath.isEmpty())
{
return new OpenOcdVersion(0, 0, 0, 0);
}
return versionCache.computeIfAbsent(executablePath, OpenOcdVersionManager::fetchVersionFromProcess);
}

private static OpenOcdVersion fetchVersionFromProcess(String executablePath)
{
try
{
ProcessBuilder pb = new ProcessBuilder(executablePath, "--version"); //$NON-NLS-1$
pb.redirectErrorStream(true);
Process process = pb.start();

try
{
if (!process.waitFor(2, TimeUnit.SECONDS))
{
return new OpenOcdVersion(0, 0, 0, 0);
}

try (BufferedReader reader = process.inputReader())
{
String output = reader.lines().collect(Collectors.joining("\n")); //$NON-NLS-1$
return parseVersionString(output);
}
} finally
{
if (process.isAlive())
{
process.destroyForcibly();
}
}
}
catch (IOException e)
{
Logger.log("Failed to execute or parse OpenOCD version fallback for path: " + executablePath, e); //$NON-NLS-1$
}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();
Logger.log("Thread was interrupted while waiting for OpenOCD process to exit.", e); //$NON-NLS-1$
}

return new OpenOcdVersion(0, 0, 0, 0);
}

public static OpenOcdVersion parseVersionString(String output)
{
if (output == null || output.trim().isEmpty())
{
return new OpenOcdVersion(0, 0, 0, 0);
}

Matcher matcher = VERSION_PATTERN.matcher(output);
if (matcher.find())
{
int major = Integer.parseInt(matcher.group("major")); //$NON-NLS-1$
int minor = Integer.parseInt(matcher.group("minor")); //$NON-NLS-1$
int patch = matcher.group("patch") != null ? Integer.parseInt(matcher.group("patch")) : 0; //$NON-NLS-1$ //$NON-NLS-2$
int buildDate = matcher.group("build") != null ? Integer.parseInt(matcher.group("build")) : 0; //$NON-NLS-1$//$NON-NLS-2$

return new OpenOcdVersion(major, minor, patch, buildDate);
}

return new OpenOcdVersion(0, 0, 0, 0);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,8 @@

package com.espressif.idf.debug.gdbjtag.openocd;

import java.io.BufferedReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.cdt.core.settings.model.ICConfigurationDescription;
import org.eclipse.cdt.debug.gdbjtag.core.IGDBJtagConstants;
Expand All @@ -29,15 +24,17 @@
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
import org.eclipse.core.resources.IProject;
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.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.embedcdt.core.EclipseUtils;
import org.eclipse.embedcdt.core.StringUtils;
import org.eclipse.embedcdt.debug.gdbjtag.core.DebugUtils;
import org.eclipse.launchbar.core.ILaunchBarManager;
import org.eclipse.launchbar.core.target.ILaunchTarget;

import com.espressif.idf.core.build.IDFLaunchConstants;
import com.espressif.idf.core.util.OpenOcdVersionManager;
import com.espressif.idf.core.util.PortChecker;
import com.espressif.idf.debug.gdbjtag.openocd.preferences.DefaultPreferences;
import com.espressif.idf.launch.serial.util.ESPFlashUtil;
Expand Down Expand Up @@ -114,6 +111,19 @@ public static String[] getGdbServerCommandLineArray(ILaunchConfiguration configu
configurationWorkingCopy.setAttribute(IGDBJtagConstants.ATTR_PORT_NUMBER, port);
configurationWorkingCopy.doSave();

ILaunchTarget activeLaunchTarget = Activator.getService(ILaunchBarManager.class).getActiveLaunchTarget();
if (activeLaunchTarget != null)
{
String openocdLoc = activeLaunchTarget.getAttribute(IDFLaunchConstants.OPENOCD_USB_LOCATION,
(String) null);

if (openocdLoc != null && supportsAdapterUsbCommand(executable))
{
lst.add("-c");
lst.add(String.format("adapter usb location %s", openocdLoc));
}
}

lst.add("-c"); //$NON-NLS-1$
lst.add(String.format(fmtGdbPort, port));

Expand Down Expand Up @@ -336,55 +346,13 @@ public static boolean getDoStartGdbClient(ILaunchConfiguration config) throws Co

// ------------------------------------------------------------------------

private static boolean useModernPortSyntax(String executablePath)
private static boolean supportsAdapterUsbCommand(String executablePath)
{
if (executablePath == null || executablePath.isEmpty())
{
return false;
}
Pattern outputPattern = Pattern.compile("(?:Open On-Chip Debugger |v)(?<major>\\d+)\\.(?<minor>\\d+)"); //$NON-NLS-1$

try
{
ProcessBuilder pb = new ProcessBuilder(executablePath, "--version"); //$NON-NLS-1$
pb.redirectErrorStream(true);
Process process = pb.start();

try
{
if (!process.waitFor(2, TimeUnit.SECONDS))
{
return false;
}
return OpenOcdVersionManager.getVersion(executablePath).isBuildDateAtLeast(0, 12, 20260424);
}

try (BufferedReader reader = process.inputReader())
{
return reader.lines().map(outputPattern::matcher).filter(Matcher::find).findFirst().map(matcher -> {
int major = Integer.parseInt(matcher.group("major")); //$NON-NLS-1$
int minor = Integer.parseInt(matcher.group("minor")); //$NON-NLS-1$
return major > 0 || (major == 0 && minor >= 12);
}).orElse(false);
}
} finally
{
if (process.isAlive())
{
process.destroyForcibly();
}
}
}
catch (IOException e)
{
Activator.log(new CoreException(new Status(IStatus.WARNING, Activator.PLUGIN_ID,
"Failed to execute or parse OpenOCD version fallback for path: " + executablePath, e))); //$NON-NLS-1$
return false;
}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();
Activator.log(new CoreException(new Status(IStatus.WARNING, Activator.PLUGIN_ID,
"Thread was interrupted while waiting for OpenOCD process to exit.", e))); //$NON-NLS-1$
return false;
}
private static boolean useModernPortSyntax(String executablePath)
{
return OpenOcdVersionManager.getVersion(executablePath).isAtLeast(0, 12);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@

import com.espressif.idf.core.IDFCorePlugin;
import com.espressif.idf.core.build.IDFLaunchConstants;
import com.espressif.idf.core.util.OpenOcdVersionManager;
import com.espressif.idf.debug.gdbjtag.openocd.Activator;
import com.espressif.idf.debug.gdbjtag.openocd.Configuration;

Expand Down Expand Up @@ -226,9 +227,12 @@ protected Process launchGdbServerProcess(String[] commandLineArray) throws CoreE
if (activeLaunchTarget != null)
{
String openocdLoc = activeLaunchTarget.getAttribute(IDFLaunchConstants.OPENOCD_USB_LOCATION, (String) null);
if (openocdLoc != null) {
envMap.put(IDFLaunchConstants.OPENOCD_USB_LOCATION, openocdLoc);
}
if (openocdLoc != null && commandLineArray != null && commandLineArray.length > 0
&& !supportsAdapterUsbCommand(commandLineArray[0]))
{
envMap.put(IDFLaunchConstants.OPENOCD_USB_LOCATION, openocdLoc);
}
Comment thread
sigmaaa marked this conversation as resolved.

}

// Convert back to envp
Expand All @@ -239,4 +243,9 @@ protected Process launchGdbServerProcess(String[] commandLineArray) throws CoreE

return DebugUtils.exec(commandLineArray, envList.toArray(new String[0]), dir);
}

private boolean supportsAdapterUsbCommand(String executablePath)
{
return OpenOcdVersionManager.getVersion(executablePath).isBuildDateAtLeast(0, 12, 20260424);
}
}
Loading
Loading