Skip to content
Open
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
Expand Up @@ -6,9 +6,15 @@

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.core.runtime.Platform;

Expand All @@ -25,12 +31,96 @@
*/
public class ToolsUtility
{
private static final Pattern IDF_VERSION_MAJOR_PATTERN = Pattern.compile("IDF_VERSION_MAJOR\\s+(\\d+)"); //$NON-NLS-1$
private static final Pattern IDF_VERSION_MINOR_PATTERN = Pattern.compile("IDF_VERSION_MINOR\\s+(\\d+)"); //$NON-NLS-1$
private static final Pattern IDF_VERSION_PATCH_PATTERN = Pattern.compile("IDF_VERSION_PATCH\\s+(\\d+)"); //$NON-NLS-1$

/**
* Detects the ESP-IDF version for the given installation.
* <p>
* Prefers the full {@code MAJOR.MINOR.PATCH} read directly from {@code tools/cmake/version.cmake}
* under the installation's {@code IDF_PATH}. This is intentionally not derived from the
* {@code ESP_IDF_VERSION} environment variable: ESP-IDF and EIM define that variable as
* {@code MAJOR.MINOR} only (components consume it in Kconfig, e.g. {@code Kconfig.idf_v5.5.in}), so
* it never carries the patch component. When {@code version.cmake} cannot be read, falls back to the
* {@code ESP_IDF_VERSION} value printed by the activation script.
*/
public static String getIdfVersion(EimInstallationModel installation)
{
String versionFromCMake = readIdfVersionFromCMake(installation.getPath());
if (!StringUtil.isEmpty(versionFromCMake))
{
return versionFromCMake;
}

return installation.getActivationScript().map(ToolsUtility::readIdfVersionFromScript)
.orElse(StringUtil.EMPTY);
}

private static String readIdfVersionFromCMake(String idfPath)
{
if (StringUtil.isEmpty(idfPath))
{
return StringUtil.EMPTY;
}

try
{
Path versionCMakeFile = Path.of(idfPath, "tools", "cmake", "version.cmake"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
return parseVersionCMake(versionCMakeFile);

}
catch (InvalidPathException e)
{
Logger.log(e);
return StringUtil.EMPTY;
}

}
Comment thread
sigmaaa marked this conversation as resolved.

/**
* Parses {@code version.cmake} and returns {@code MAJOR.MINOR.PATCH} (or {@code MAJOR.MINOR} when
* the patch entry is absent). Returns an empty string when the file is missing or the required
* major/minor entries cannot be found.
*/
public static String parseVersionCMake(Path versionCMakeFile)
{
if (versionCMakeFile == null || !Files.isRegularFile(versionCMakeFile))
{
return StringUtil.EMPTY;
}

try
{
String content = Files.readString(versionCMakeFile);
String major = firstGroup(IDF_VERSION_MAJOR_PATTERN, content);
String minor = firstGroup(IDF_VERSION_MINOR_PATTERN, content);
if (major == null || minor == null)
{
return StringUtil.EMPTY;
}

String patch = firstGroup(IDF_VERSION_PATCH_PATTERN, content);
if (patch == null)
{
return major + "." + minor; //$NON-NLS-1$
}

return major + "." + minor + "." + patch; //$NON-NLS-1$ //$NON-NLS-2$
}
catch (IOException e)
{
Logger.log(e);
return StringUtil.EMPTY;
}
}

private static String firstGroup(Pattern pattern, String content)
{
Matcher matcher = pattern.matcher(content);
return matcher.find() ? matcher.group(1) : null;
}

private static String readIdfVersionFromScript(String activationScript)
{
String espIdfVersion = StringUtil.EMPTY;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*******************************************************************************
* Copyright 2026 Espressif Systems (Shanghai) PTE LTD. All rights reserved.
* Use is subject to license terms.
*******************************************************************************/
package com.espressif.idf.core.tools.test;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import com.espressif.idf.core.tools.util.ToolsUtility;

/**
* Unit tests for {@link ToolsUtility#parseVersionCMake(Path)}.
*/
public class ToolsUtilityTest
{
@Test
void full_version_includes_patch(@TempDir Path tempDir) throws IOException
{
Path file = writeVersionCMake(tempDir, """
set(IDF_VERSION_MAJOR 6)
set(IDF_VERSION_MINOR 0)
set(IDF_VERSION_PATCH 1)
""");
Assertions.assertEquals("6.0.1", ToolsUtility.parseVersionCMake(file));
}

@Test
void missing_patch_falls_back_to_major_minor(@TempDir Path tempDir) throws IOException
{
Path file = writeVersionCMake(tempDir, """
set(IDF_VERSION_MAJOR 5)
set(IDF_VERSION_MINOR 3)
""");
Assertions.assertEquals("5.3", ToolsUtility.parseVersionCMake(file));
}

@Test
void missing_minor_returns_empty(@TempDir Path tempDir) throws IOException
{
Path file = writeVersionCMake(tempDir, "set(IDF_VERSION_MAJOR 6)\n");
Assertions.assertEquals("", ToolsUtility.parseVersionCMake(file));
}

@Test
void nonexistent_file_returns_empty(@TempDir Path tempDir)
{
Assertions.assertEquals("", ToolsUtility.parseVersionCMake(tempDir.resolve("version.cmake")));
}

@Test
void null_file_returns_empty()
{
Assertions.assertEquals("", ToolsUtility.parseVersionCMake(null));
}

private static Path writeVersionCMake(Path dir, String content) throws IOException
{
Path file = dir.resolve("version.cmake");
Files.writeString(file, content);
return file;
}
}
Loading