From 81814f713d2d5230525b32256bf4a8eb939a2d16 Mon Sep 17 00:00:00 2001 From: Denys Almazov Date: Mon, 14 Sep 2026 14:59:42 +0300 Subject: [PATCH 1/4] fix: fixing bug with custom build folder reset --- .../core/build/BuildDirectoryResolver.java | 122 ++++++++++++++ .../idf/core/build/IDFBuildConfiguration.java | 21 ++- .../core/util/ClangdConfigFileHandler.java | 6 +- .../com/espressif/idf/core/util/IDFUtil.java | 15 +- .../espressif/idf/core/util/LaunchUtil.java | 51 ++++-- .../core/util/ProjectDescriptionReader.java | 21 ++- .../idf/core/util/SDKConfigUtil.java | 28 ++-- .../ui/TabGroupLaunchConfiguration.java | 23 --- .../core/IDFCoreLaunchConfigProvider.java | 3 - .../idf/launch/serial/util/ESPFlashUtil.java | 15 +- .../sdk/config/core/KConfigMenuProcessor.java | 19 ++- .../core/server/ConfigServerManager.java | 88 +++++++--- .../config/core/server/JsonConfigServer.java | 32 +++- .../core/server/JsonConfigServerRunnable.java | 23 +-- .../sdk/config/ui/ConfigContentProvider.java | 21 +-- .../sdk/config/ui/LoadSdkConfigHandler.java | 3 +- .../sdk/config/ui/SDKConfigurationEditor.java | 88 ++++------ .../CoreDumpPostmortemDebuggerLauncher.java | 12 +- .../espressif/idf/ui/LaunchBarListener.java | 47 +++--- .../idf/ui/dialogs/CMakeBuildTab2.java | 1 + .../idf/ui/dialogs/SbomCommandDialog.java | 13 +- .../ui/handlers/HeapDumpAnalysisHandler.java | 10 +- .../espressif/idf/ui/handlers/Messages.java | 1 - .../handlers/RenameIdfProjectParticipant.java | 66 +------- .../idf/ui/handlers/messages.properties | 1 - .../idf/ui/size/IDFSizeMemoryHandler.java | 10 +- .../idf/ui/tracing/AppLvlTracingDialog.java | 4 +- .../idf/ui/tracing/TracingJsonParser.java | 55 ++++++- .../HeapTracingAnalysisEditor.java | 8 +- docs/en/buildproject.rst | 11 +- docs/en/faqs.rst | 10 +- docs/zh_CN/buildproject.rst | 11 +- docs/zh_CN/faqs.rst | 10 +- .../test/BuildDirectoryResolverTest.java | 150 ++++++++++++++++++ .../LaunchUtilBoundConfigurationTest.java | 105 ++++++++++++ .../idf/core/util/test/SDKConfigUtilTest.java | 40 +++++ .../LaunchBarCDTConfigurationsTest.java | 65 ++++++++ 37 files changed, 882 insertions(+), 327 deletions(-) create mode 100644 bundles/com.espressif.idf.core/src/com/espressif/idf/core/build/BuildDirectoryResolver.java create mode 100644 tests/com.espressif.idf.core.test/src/com/espressif/idf/core/build/test/BuildDirectoryResolverTest.java create mode 100644 tests/com.espressif.idf.core.test/src/com/espressif/idf/core/util/test/LaunchUtilBoundConfigurationTest.java create mode 100644 tests/com.espressif.idf.core.test/src/com/espressif/idf/core/util/test/SDKConfigUtilTest.java diff --git a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/build/BuildDirectoryResolver.java b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/build/BuildDirectoryResolver.java new file mode 100644 index 000000000..dbf959df9 --- /dev/null +++ b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/build/BuildDirectoryResolver.java @@ -0,0 +1,122 @@ +/******************************************************************************* + * Copyright 2026 Espressif Systems (Shanghai) PTE LTD. All rights reserved. + * Use is subject to license terms. + *******************************************************************************/ +package com.espressif.idf.core.build; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.QualifiedName; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.launchbar.core.ILaunchBarManager; + +import com.espressif.idf.core.IDFConstants; +import com.espressif.idf.core.IDFCorePlugin; +import com.espressif.idf.core.util.LaunchUtil; +import com.espressif.idf.core.util.StringUtil; + +/** + * Resolves the build directory represented by the active launch configuration. + */ +public final class BuildDirectoryResolver +{ + private BuildDirectoryResolver() + { + } + + /** + * Resolves the build directory for a project using its active launch configuration. + * + * @param project project whose build directory should be resolved + * @return absolute build directory path + * @throws CoreException if the launch configuration or project properties cannot be read + */ + public static IPath resolve(IProject project) throws CoreException + { + ILaunchBarManager launchBarManager = IDFCorePlugin.getService(ILaunchBarManager.class); + if (launchBarManager != null) + { + ILaunchConfiguration configuration = launchBarManager.getActiveLaunchConfiguration(); + if (configuration != null) + { + ILaunchConfiguration buildConfiguration = resolveBuildConfiguration(configuration); + if (belongsToProject(buildConfiguration, project)) + { + return resolve(project, buildConfiguration); + } + } + } + + return resolveLegacyOrDefault(project); + } + + /** + * Resolves the build directory from a specific launch configuration. + * + * @param project project used to resolve relative paths + * @param configuration launch configuration containing the build folder attribute + * @return absolute build directory path + * @throws CoreException if the configuration or project properties cannot be read + */ + public static IPath resolve(IProject project, ILaunchConfiguration configuration) throws CoreException + { + ILaunchConfiguration buildConfiguration = resolveBuildConfiguration(configuration); + if (!belongsToProject(buildConfiguration, project)) + { + return resolveLegacyOrDefault(project); + } + + String buildFolder = buildConfiguration.getAttribute(IDFLaunchConstants.BUILD_FOLDER_PATH, StringUtil.EMPTY); + return resolvePath(project, buildFolder); + } + + private static ILaunchConfiguration resolveBuildConfiguration(ILaunchConfiguration configuration) + throws CoreException + { + if (configuration.getType().getIdentifier().equals(IDFLaunchConstants.DEBUG_LAUNCH_CONFIG_TYPE)) + { + return new LaunchUtil(DebugPlugin.getDefault().getLaunchManager()).getBoundConfiguration(configuration); + } + return configuration; + } + + private static boolean belongsToProject(ILaunchConfiguration configuration, IProject project) throws CoreException + { + return project != null && project.equals(LaunchUtil.getMappedProject(configuration)); + } + + private static IPath resolvePath(IProject project, String buildFolder) + { + String normalizedBuildFolder = StringUtil.isEmpty(buildFolder) || buildFolder.isBlank() + ? IDFConstants.BUILD_FOLDER + : buildFolder.trim(); + IPath path = Path.fromOSString(normalizedBuildFolder); + return path.isAbsolute() ? path : project.getLocation().append(path); + } + + private static IPath resolveLegacyOrDefault(IProject project) throws CoreException + { + String legacyBuildDirectory = project + .getPersistentProperty(new QualifiedName(IDFCorePlugin.PLUGIN_ID, IDFConstants.BUILD_DIR_PROPERTY)); + if (StringUtil.isEmpty(legacyBuildDirectory)) + { + return resolvePath(project, IDFConstants.BUILD_FOLDER); + } + + IPath legacyPath = resolvePath(project, legacyBuildDirectory); + return isStaleAfterRename(project, legacyPath) ? resolvePath(project, IDFConstants.BUILD_FOLDER) : legacyPath; + } + + /** + * The legacy property stores an absolute path, so a value captured before a project rename still points inside + * the old project folder. Such a path must not win over the project's current default build folder (IEP-1521). + */ + private static boolean isStaleAfterRename(IProject project, IPath legacyPath) + { + IPath projectLocation = project.getLocation(); + return projectLocation != null && !projectLocation.isPrefixOf(legacyPath) && !legacyPath.toFile().exists(); + } +} diff --git a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/build/IDFBuildConfiguration.java b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/build/IDFBuildConfiguration.java index 1ca246b07..718eee220 100644 --- a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/build/IDFBuildConfiguration.java +++ b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/build/IDFBuildConfiguration.java @@ -53,14 +53,12 @@ import org.eclipse.cdt.internal.core.model.CModelManager; import org.eclipse.core.resources.IBuildConfiguration; import org.eclipse.core.resources.IContainer; -import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; 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.Status; import org.eclipse.core.runtime.jobs.Job; @@ -70,7 +68,6 @@ import org.eclipse.launchbar.core.ILaunchBarManager; import org.eclipse.launchbar.core.target.ILaunchTarget; -import com.espressif.idf.core.IDFConstants; import com.espressif.idf.core.IDFCorePlugin; import com.espressif.idf.core.IDFCorePreferenceConstants; import com.espressif.idf.core.IDFEnvironmentVariables; @@ -137,20 +134,19 @@ public Path getBuildDirectory() throws CoreException public IContainer getBuildContainer() throws CoreException { IProject project = getProject(); - IFolder buildRootFolder = project.getFolder(IDFConstants.BUILD_FOLDER); - - IProgressMonitor monitor = new NullProgressMonitor(); - if (!buildRootFolder.exists()) + IPath projectLocation = project.getLocation(); + IPath buildDirectory = getBuildContainerPath(); + if (projectLocation.isPrefixOf(buildDirectory)) { - buildRootFolder.create(IResource.FORCE | IResource.DERIVED, true, monitor); + IPath relativePath = buildDirectory.makeRelativeTo(projectLocation); + return relativePath.isEmpty() ? project : project.getFolder(relativePath); } - - return buildRootFolder; + return project; } public IPath getBuildContainerPath() throws CoreException { - org.eclipse.core.runtime.Path path = new org.eclipse.core.runtime.Path(IDFUtil.getBuildDir(getProject())); + IPath path = BuildDirectoryResolver.resolve(getProject()); if (!path.toFile().exists()) { path.toFile().mkdirs(); @@ -224,7 +220,8 @@ public String getProperty(String name) private IBinary[] getBuildOutput(final IBinaryContainer binaries, final IPath outputPath) throws CoreException { - return Arrays.stream(binaries.getBinaries()).filter(b -> b.isExecutable() && outputPath.isPrefixOf(b.getPath())) + return Arrays.stream(binaries.getBinaries()) + .filter(b -> b.isExecutable() && b.getLocation() != null && outputPath.isPrefixOf(b.getLocation())) .toArray(IBinary[]::new); } diff --git a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/ClangdConfigFileHandler.java b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/ClangdConfigFileHandler.java index 4a4b44c1f..9b43c88c8 100644 --- a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/ClangdConfigFileHandler.java +++ b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/ClangdConfigFileHandler.java @@ -20,11 +20,8 @@ import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.NullProgressMonitor; -import org.eclipse.core.runtime.QualifiedName; import org.yaml.snakeyaml.Yaml; -import com.espressif.idf.core.IDFConstants; -import com.espressif.idf.core.IDFCorePlugin; import com.espressif.idf.core.ILSPConstants; /** @@ -53,8 +50,7 @@ public void update(IProject project) throws CoreException, IOException compileFlags = new LinkedHashMap<>(); data.put("CompileFlags", compileFlags); //$NON-NLS-1$ } - updateCompileFlagsSection(compileFlags, project.getPersistentProperty( - new QualifiedName(IDFCorePlugin.PLUGIN_ID, IDFConstants.BUILD_DIR_PROPERTY))); + updateCompileFlagsSection(compileFlags, IDFUtil.getBuildDir(project)); // Write updated clangd back to file try (Writer writer = new FileWriter(file, StandardCharsets.UTF_8)) diff --git a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/IDFUtil.java b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/IDFUtil.java index da0efb8e6..0eba7d8d7 100644 --- a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/IDFUtil.java +++ b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/IDFUtil.java @@ -47,6 +47,7 @@ import com.espressif.idf.core.LaunchBarTargetConstants; import com.espressif.idf.core.ProcessBuilderFactory; import com.espressif.idf.core.SystemExecutableFinder; +import com.espressif.idf.core.build.BuildDirectoryResolver; import com.espressif.idf.core.build.IDFLaunchConstants; import com.espressif.idf.core.logging.Logger; import com.espressif.idf.core.toolchain.ESPToolChainManager; @@ -548,14 +549,7 @@ private static String runCommand(List arguments, Map env */ public static String getBuildDir(IProject project) throws CoreException { - String buildDirectory = project - .getPersistentProperty(new QualifiedName(IDFCorePlugin.PLUGIN_ID, IDFConstants.BUILD_DIR_PROPERTY)); - if (StringUtil.isEmpty(buildDirectory)) - { - buildDirectory = project.getFolder(IDFConstants.BUILD_FOLDER).getLocation().toOSString(); - } - - return buildDirectory; + return BuildDirectoryResolver.resolve(project).toOSString(); } /** @@ -564,7 +558,10 @@ public static String getBuildDir(IProject project) throws CoreException * @param project * @param pathToBuildDir * @throws CoreException + * @deprecated Build directories are configured per launch configuration using + * {@link IDFLaunchConstants#BUILD_FOLDER_PATH}. */ + @Deprecated(forRemoval = true) public static void setBuildDir(IProject project, String pathToBuildDir) throws CoreException { project.setPersistentProperty(new QualifiedName(IDFCorePlugin.PLUGIN_ID, IDFConstants.BUILD_DIR_PROPERTY), @@ -583,7 +580,9 @@ public static void setBuildDir(IProject project, String pathToBuildDir) throws C * parameter cannot be {@code null}. * @throws CoreException If there is an issue with accessing the project or updating the build folder. This * exception is logged, but not rethrown. + * @deprecated Runtime consumers resolve the build directory directly from the active launch configuration. */ + @Deprecated(forRemoval = true) public static void updateProjectBuildFolder(ILaunchConfigurationWorkingCopy configuration) { try diff --git a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/LaunchUtil.java b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/LaunchUtil.java index ddce63076..7c600c1cf 100644 --- a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/LaunchUtil.java +++ b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/LaunchUtil.java @@ -4,12 +4,9 @@ *******************************************************************************/ package com.espressif.idf.core.util; -import java.util.stream.Stream; - import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; -import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.launchbar.core.ILaunchDescriptor; @@ -31,8 +28,8 @@ public ILaunchConfiguration findAppropriateLaunchConfig(ILaunchDescriptor descri IProject project = descriptor.getAdapter(IProject.class); for (ILaunchConfiguration config : launchManager.getLaunchConfigurations()) { - IResource[] mappedResource = config.getMappedResources(); - if (mappedResource != null && mappedResource.length > 0 && mappedResource[0].getProject().equals(project) + IProject mappedProject = getMappedProject(config); + if (mappedProject != null && mappedProject.equals(project) && config.getType().getIdentifier().contentEquals(configIndentifier)) { return config; @@ -41,6 +38,22 @@ public ILaunchConfiguration findAppropriateLaunchConfig(ILaunchDescriptor descri return null; } + /** + * Returns the project a launch configuration is mapped to, or null when it carries no mapped + * resource. Unlike CDT's {@code CoreBuildLaunchConfigDelegate#getProject}, this never fails on such a + * configuration. + */ + public static IProject getMappedProject(ILaunchConfiguration configuration) throws CoreException + { + if (configuration == null) + { + return null; + } + + IResource[] mappedResources = configuration.getMappedResources(); + return mappedResources == null || mappedResources.length == 0 ? null : mappedResources[0].getProject(); + } + /* * In case when the active configuration is debugging, we are using bound launch configuration to build the project */ @@ -48,11 +61,29 @@ public ILaunchConfiguration getBoundConfiguration(ILaunchConfiguration configura { String bindedLaunchConfigName = configuration.getAttribute(IDFLaunchConstants.ATTR_LAUNCH_CONFIGURATION_NAME, StringUtil.EMPTY); - ILaunchConfiguration[] launchConfigurations = launchManager.getLaunchConfigurations(DebugPlugin.getDefault() - .getLaunchManager().getLaunchConfigurationType(IDFLaunchConstants.RUN_LAUNCH_CONFIG_TYPE)); - ILaunchConfiguration defaultConfiguration = launchConfigurations[0]; - return Stream.of(launchConfigurations).filter(config -> config.getName().contentEquals(bindedLaunchConfigName)) - .findFirst().orElse(defaultConfiguration); + ILaunchConfiguration[] launchConfigurations = launchManager.getLaunchConfigurations( + launchManager.getLaunchConfigurationType(IDFLaunchConstants.RUN_LAUNCH_CONFIG_TYPE)); + for (ILaunchConfiguration launchConfiguration : launchConfigurations) + { + if (launchConfiguration.getName().contentEquals(bindedLaunchConfigName)) + { + return launchConfiguration; + } + } + + IProject project = getMappedProject(configuration); + if (project != null) + { + for (ILaunchConfiguration launchConfiguration : launchConfigurations) + { + if (project.equals(getMappedProject(launchConfiguration))) + { + return launchConfiguration; + } + } + } + + return configuration; } diff --git a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/ProjectDescriptionReader.java b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/ProjectDescriptionReader.java index 947b36a81..46d54a592 100644 --- a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/ProjectDescriptionReader.java +++ b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/ProjectDescriptionReader.java @@ -6,7 +6,9 @@ import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.Path; import com.espressif.idf.core.IDFConstants; import com.espressif.idf.core.logging.Logger; @@ -20,14 +22,25 @@ public ProjectDescriptionReader(IProject project) this.project = project; } + /** + * @return workspace file for the application ELF, or {@code null} when the configured build directory is external + * @deprecated Use {@link #getAppElfFileLocation()} for custom directories outside the workspace. + */ + @Deprecated(forRemoval = true) public IFile getAppElfFile() { - IFile appElfFile = null; + File appElfFile = getAppElfFileLocation(); + return appElfFile == null ? null + : ResourcesPlugin.getWorkspace().getRoot().getFileForLocation(Path.fromOSString(appElfFile.getPath())); + } + + public File getAppElfFileLocation() + { + File appElfFile = null; try { String appElfFileName = getAppElfFileName(); - appElfFile = appElfFileName.isEmpty() ? appElfFile - : project.getFolder(IDFConstants.BUILD_FOLDER).getFile(appElfFileName); + appElfFile = appElfFileName.isEmpty() ? appElfFile : new File(IDFUtil.getBuildDir(project), appElfFileName); } catch (Exception e) { @@ -54,7 +67,7 @@ private String getAppElfFileName() return appElfFileName; } - + public String getIdfPath() { String idfPath = StringUtil.EMPTY; diff --git a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/SDKConfigUtil.java b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/SDKConfigUtil.java index 4c09377fe..b9c2ff911 100644 --- a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/SDKConfigUtil.java +++ b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/SDKConfigUtil.java @@ -7,7 +7,6 @@ import java.io.File; import org.eclipse.core.resources.IProject; -import org.eclipse.core.runtime.IPath; import com.espressif.idf.core.IDFConstants; @@ -18,20 +17,31 @@ public class SDKConfigUtil { + /** + * @param project project whose active build directory should be used + * @return path to kconfig_menus.json + * @throws Exception if the build directory does not exist + * @deprecated Pass the already resolved build directory to keep multi-config operations scoped. + */ + @Deprecated(forRemoval = true) + public String getConfigMenuFilePath(IProject project) throws Exception + { + return getConfigMenuFilePath(IDFUtil.getBuildDir(project)); + } + /** * @param buildDirectory * @return * @throws Exception */ - public String getConfigMenuFilePath(IProject project) throws Exception + public String getConfigMenuFilePath(String buildDirectory) throws Exception { - String buildDir = IDFUtil.getBuildDir(project); - if (!new File(buildDir).exists()) + if (!new File(buildDirectory).exists()) { - throw new Exception("Build directory is not found: "+ buildDir); //$NON-NLS-1$ + throw new Exception("Build directory is not found: " + buildDirectory); //$NON-NLS-1$ } - return new File(buildDir).getAbsolutePath() + IPath.SEPARATOR + IDFConstants.CONFIG_FOLDER - + IPath.SEPARATOR + IDFConstants.KCONFIG_MENUS_JSON; + return new File(new File(buildDirectory, IDFConstants.CONFIG_FOLDER), IDFConstants.KCONFIG_MENUS_JSON) + .getAbsolutePath(); } /** @@ -46,7 +56,7 @@ public String getSDKConfigJsonFilePath(IProject project) throws Exception { throw new Exception("Build directory is not found: "+ buildDir); //$NON-NLS-1$ } - return new File(buildDir).getAbsolutePath() + IPath.SEPARATOR + IDFConstants.CONFIG_FOLDER - + IPath.SEPARATOR + IDFConstants.SDKCONFIG_JSON_FILE_NAME; + return new File(new File(buildDir, IDFConstants.CONFIG_FOLDER), IDFConstants.SDKCONFIG_JSON_FILE_NAME) + .getAbsolutePath(); } } diff --git a/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/ui/TabGroupLaunchConfiguration.java b/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/ui/TabGroupLaunchConfiguration.java index 723225448..aff9656f1 100644 --- a/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/ui/TabGroupLaunchConfiguration.java +++ b/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/ui/TabGroupLaunchConfiguration.java @@ -14,16 +14,9 @@ package com.espressif.idf.debug.gdbjtag.openocd.ui; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.debug.core.DebugPlugin; -import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.debug.ui.AbstractLaunchConfigurationTabGroup; import org.eclipse.debug.ui.ILaunchConfigurationDialog; -import com.espressif.idf.core.logging.Logger; -import com.espressif.idf.core.util.IDFUtil; -import com.espressif.idf.core.util.LaunchUtil; - public class TabGroupLaunchConfiguration extends AbstractLaunchConfigurationTabGroup { @@ -33,20 +26,4 @@ public void createTabs(ILaunchConfigurationDialog dialog, String mode) setTabs(); } - @Override - public void performApply(ILaunchConfigurationWorkingCopy configuration) - { - super.performApply(configuration); - try - { - IDFUtil.updateProjectBuildFolder(new LaunchUtil(DebugPlugin.getDefault().getLaunchManager()) - .getBoundConfiguration(configuration).getWorkingCopy()); - } - catch (CoreException e) - { - Logger.log(e); - } - - } - } diff --git a/bundles/com.espressif.idf.launch.serial.core/src/com/espressif/idf/launch/serial/core/IDFCoreLaunchConfigProvider.java b/bundles/com.espressif.idf.launch.serial.core/src/com/espressif/idf/launch/serial/core/IDFCoreLaunchConfigProvider.java index 769c2bbbd..0817cd6ac 100644 --- a/bundles/com.espressif.idf.launch.serial.core/src/com/espressif/idf/launch/serial/core/IDFCoreLaunchConfigProvider.java +++ b/bundles/com.espressif.idf.launch.serial.core/src/com/espressif/idf/launch/serial/core/IDFCoreLaunchConfigProvider.java @@ -15,7 +15,6 @@ import org.eclipse.launchbar.core.target.ILaunchTarget; import com.espressif.idf.core.build.IDFLaunchConstants; -import com.espressif.idf.core.util.IDFUtil; import com.espressif.idf.core.util.LaunchUtil; public class IDFCoreLaunchConfigProvider extends CoreBuildGenericLaunchConfigProvider @@ -78,8 +77,6 @@ public boolean launchConfigurationAdded(ILaunchConfiguration configuration) thro @Override public boolean launchConfigurationChanged(ILaunchConfiguration configuration) throws CoreException { - IDFUtil.updateProjectBuildFolder(configuration.getWorkingCopy()); - return false; } diff --git a/bundles/com.espressif.idf.launch.serial.core/src/com/espressif/idf/launch/serial/util/ESPFlashUtil.java b/bundles/com.espressif.idf.launch.serial.core/src/com/espressif/idf/launch/serial/util/ESPFlashUtil.java index 92adbeea5..f610c33a5 100644 --- a/bundles/com.espressif.idf.launch.serial.core/src/com/espressif/idf/launch/serial/util/ESPFlashUtil.java +++ b/bundles/com.espressif.idf.launch.serial.core/src/com/espressif/idf/launch/serial/util/ESPFlashUtil.java @@ -12,8 +12,6 @@ import java.util.regex.Pattern; import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.Path; -import org.eclipse.core.runtime.QualifiedName; import org.eclipse.core.variables.IStringVariableManager; import org.eclipse.core.variables.VariablesPlugin; import org.eclipse.debug.core.DebugPlugin; @@ -26,6 +24,7 @@ import com.espressif.idf.core.IDFCorePlugin; import com.espressif.idf.core.IDFDynamicVariables; import com.espressif.idf.core.IDFEnvironmentVariables; +import com.espressif.idf.core.build.BuildDirectoryResolver; import com.espressif.idf.core.build.IDFLaunchConstants; import com.espressif.idf.core.configparser.EspConfigParser; import com.espressif.idf.core.logging.Logger; @@ -108,16 +107,10 @@ public static String getEspJtagFlashCommand(ILaunchConfiguration configuration) try { - String buildPath = configuration.getMappedResources()[0].getProject() - .getPersistentProperty(new QualifiedName(IDFCorePlugin.PLUGIN_ID, IDFConstants.BUILD_DIR_PROPERTY)); - // converting to UNIX path so openocd could read it - buildPath = new Path(buildPath).toString(); + String buildPath = BuildDirectoryResolver + .resolve(configuration.getMappedResources()[0].getProject(), configuration).toString(); - buildPath = buildPath.isBlank() ? configuration.getMappedResources()[0].getProject() - .getFolder(IDFConstants.BUILD_FOLDER).getLocationURI().getPath() : buildPath; - - char a = buildPath.charAt(2); - if (a == ':') + if (buildPath.length() > 2 && buildPath.charAt(0) == '/' && buildPath.charAt(2) == ':') { buildPath = buildPath.substring(1); } diff --git a/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/KConfigMenuProcessor.java b/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/KConfigMenuProcessor.java index 6a1789f6e..3405a44be 100644 --- a/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/KConfigMenuProcessor.java +++ b/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/KConfigMenuProcessor.java @@ -17,6 +17,7 @@ import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; +import com.espressif.idf.core.util.IDFUtil; import com.espressif.idf.core.util.SDKConfigUtil; /** @@ -26,8 +27,19 @@ public class KConfigMenuProcessor { + private String buildDirectory; private IProject project; + public KConfigMenuProcessor(String buildDirectory) + { + this.buildDirectory = buildDirectory; + } + + /** + * @param project project whose active build directory should be used + * @deprecated Pass an explicit build directory to keep multi-config operations scoped. + */ + @Deprecated(forRemoval = true) public KConfigMenuProcessor(IProject project) { this.project = project; @@ -41,9 +53,12 @@ public KConfigMenuProcessor(IProject project) */ public KConfigMenuItem reader() throws Exception { - + if (buildDirectory == null) + { + buildDirectory = IDFUtil.getBuildDir(project); + } SDKConfigUtil sdkConfigUtil = new SDKConfigUtil(); - String menuConfigPath = sdkConfigUtil.getConfigMenuFilePath(project); + String menuConfigPath = sdkConfigUtil.getConfigMenuFilePath(buildDirectory); if (!new File(menuConfigPath).exists()) { throw new Exception(MessageFormat.format(Messages.KconfMenuJsonNotFound, menuConfigPath)); diff --git a/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/server/ConfigServerManager.java b/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/server/ConfigServerManager.java index 3fb22b96b..103da9983 100644 --- a/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/server/ConfigServerManager.java +++ b/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/server/ConfigServerManager.java @@ -5,12 +5,16 @@ package com.espressif.idf.sdk.config.core.server; import java.io.IOException; +import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; import java.util.Objects; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; + +import com.espressif.idf.core.util.IDFUtil; /** * @author Kondal Kolipaka @@ -26,25 +30,38 @@ public void clearAll() jsonServermap.clear(); } - public void deleteServer(IProject project, IFile file) + public void deleteServer(IProject project, IFile file, String buildDirectory) { - ProjectFileMapKey projectFileMapKey = new ProjectFileMapKey(project, file); + ProjectFileMapKey projectFileMapKey = new ProjectFileMapKey(project, file, + normalizeBuildDirectory(buildDirectory)); jsonServermap.remove(projectFileMapKey); } + /** + * @deprecated Supply the build directory used to start the server. + */ + @Deprecated(forRemoval = true) + public void deleteServer(IProject project, IFile file) + { + jsonServermap.keySet().removeIf(key -> key.project.getName().equals(project.getName()) + && key.file.getLocation().equals(file.getLocation())); + } + /** * @param project * @return * @throws IOException */ - public synchronized JsonConfigServer getServer(final IProject project, final IFile file) throws IOException - { - ProjectFileMapKey projectFileMapKey = new ProjectFileMapKey(project, file); - + public synchronized JsonConfigServer getServer(final IProject project, final IFile file, + final String buildDirectory) throws IOException + { + String normalizedBuildDirectory = normalizeBuildDirectory(buildDirectory); + ProjectFileMapKey projectFileMapKey = new ProjectFileMapKey(project, file, normalizedBuildDirectory); + JsonConfigServer jsonConfigServer = jsonServermap.get(projectFileMapKey); if (jsonConfigServer == null) { - jsonConfigServer = new JsonConfigServer(project, file); + jsonConfigServer = new JsonConfigServer(project, file, normalizedBuildDirectory); jsonServermap.put(projectFileMapKey, jsonConfigServer); jsonConfigServer.start(); return jsonConfigServer; @@ -52,34 +69,63 @@ public synchronized JsonConfigServer getServer(final IProject project, final IFi return jsonConfigServer; } - - + + /** + * @deprecated Supply an explicit build directory to keep multi-config servers isolated. + */ + @Deprecated(forRemoval = true) + public synchronized JsonConfigServer getServer(final IProject project, final IFile file) throws IOException + { + try + { + return getServer(project, file, IDFUtil.getBuildDir(project)); + } + catch (CoreException e) + { + throw new IOException(e); + } + } + + private static String normalizeBuildDirectory(String buildDirectory) + { + return Paths.get(buildDirectory).toAbsolutePath().normalize().toString(); + } + private class ProjectFileMapKey { private IProject project; private IFile file; - - private ProjectFileMapKey(IProject project, IFile file) + private String buildDirectory; + + private ProjectFileMapKey(IProject project, IFile file, String buildDirectory) { this.file = file; this.project = project; + this.buildDirectory = buildDirectory; } - + @Override - public boolean equals(Object o) + public boolean equals(Object object) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - ProjectFileMapKey that = (ProjectFileMapKey) o; - + if (this == object) + { + return true; + } + if (object == null || getClass() != object.getClass()) + { + return false; + } + ProjectFileMapKey that = (ProjectFileMapKey) object; + return project.getName().equals(that.project.getName()) - && file.getLocation().equals(that.file.getLocation()); - } - + && file.getLocation().equals(that.file.getLocation()) + && buildDirectory.equals(that.buildDirectory); + } + @Override public int hashCode() { - return Objects.hash(project.getName(), file.getLocation()); + return Objects.hash(project.getName(), file.getLocation(), buildDirectory); } } } diff --git a/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/server/JsonConfigServer.java b/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/server/JsonConfigServer.java index fa5d050ff..c16934677 100644 --- a/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/server/JsonConfigServer.java +++ b/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/server/JsonConfigServer.java @@ -44,13 +44,37 @@ public class JsonConfigServer implements IMessagesHandlerNotifier private JsonConfigOutput configOutput; private Process process; private IFile file; + private String buildDirectory; + /** + * @deprecated Supply an explicit build directory to keep multi-config servers isolated. + */ + @Deprecated(forRemoval = true) public JsonConfigServer(IProject project, IFile file) + { + this(project, file, resolveBuildDirectory(project)); + } + + public JsonConfigServer(IProject project, IFile file, String buildDirectory) { this.project = project; listeners = new ArrayList(); configOutput = new JsonConfigOutput(); this.file = file; + this.buildDirectory = buildDirectory; + } + + private static String resolveBuildDirectory(IProject project) + { + try + { + return IDFUtil.getBuildDir(project); + } + catch (CoreException e) + { + Logger.log(e); + return project.getFolder(IDFConstants.BUILD_FOLDER).getLocation().toOSString(); + } } public void resetElementById(String id) @@ -127,7 +151,7 @@ public void start() throws IOException arguments.add(pythonPath); arguments.add(idfPythonScriptFile.getAbsolutePath()); arguments.add("-B"); //$NON-NLS-1$ - arguments.add(IDFUtil.getBuildDir(project)); + arguments.add(buildDirectory); arguments.add("-DSDKCONFIG=".concat(file.getName())); //$NON-NLS-1$ arguments.add(IDFConstants.CONF_SERVER_CMD); Logger.log(arguments.toString()); @@ -139,7 +163,7 @@ public void start() throws IOException process = processRunner.run(arguments, workingDir, env); - runnable = new JsonConfigServerRunnable(process, this, project, oldSdkconfigValue); + runnable = new JsonConfigServerRunnable(process, this, buildDirectory, oldSdkconfigValue); Thread t = new Thread(runnable); t.start(); } @@ -188,9 +212,9 @@ private void loadIdfPathWithSystemPath(Map systemEnv) } } - private String getCmakeCacheSdkconfigValue() throws CoreException + private String getCmakeCacheSdkconfigValue() { - File cmakeCacheFile = new File(IDFUtil.getBuildDir(project).concat("/CMakeCache.txt")); + File cmakeCacheFile = new File(buildDirectory, "CMakeCache.txt"); //$NON-NLS-1$ if (cmakeCacheFile.exists()) { try (BufferedReader reader = new BufferedReader(new FileReader(cmakeCacheFile))) diff --git a/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/server/JsonConfigServerRunnable.java b/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/server/JsonConfigServerRunnable.java index 057e85609..07f9b0090 100644 --- a/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/server/JsonConfigServerRunnable.java +++ b/bundles/com.espressif.idf.sdk.config.core/src/com/espressif/idf/sdk/config/core/server/JsonConfigServerRunnable.java @@ -17,14 +17,11 @@ import java.util.concurrent.TimeUnit; import java.util.function.Consumer; -import org.eclipse.core.resources.IProject; -import org.eclipse.core.runtime.CoreException; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; import com.espressif.idf.core.logging.Logger; -import com.espressif.idf.core.util.IDFUtil; import com.espressif.idf.core.util.StringUtil; import com.espressif.idf.sdk.config.core.IJsonServerConfig; import com.espressif.idf.sdk.config.core.SDKConfigCorePlugin; @@ -41,14 +38,15 @@ public class JsonConfigServerRunnable implements Runnable private InputStream out; private CommandType type; private Process process; - private IProject project; + private String buildDirectory; private String oldSdkconfigValue; - public JsonConfigServerRunnable(Process process, JsonConfigServer configServer, IProject project, String oldSdkconfigValue) + public JsonConfigServerRunnable(Process process, JsonConfigServer configServer, String buildDirectory, + String oldSdkconfigValue) { this.process = process; this.configServer = configServer; - this.project = project; + this.buildDirectory = buildDirectory; this.oldSdkconfigValue = oldSdkconfigValue; } @@ -120,14 +118,7 @@ else if (no > 0) builder.append(string); if (string.contains("Server running")) //$NON-NLS-1$ { - try - { - replaceOldCmakeCache(); - } - catch (CoreException e) - { - Logger.log(e); - } + replaceOldCmakeCache(); } } @@ -150,11 +141,11 @@ else if (no > 0) } - private void replaceOldCmakeCache() throws CoreException + private void replaceOldCmakeCache() { // SDKCONFIG:UNINITIALIZED= - File cmakeCacheFile = new File(IDFUtil.getBuildDir(project).concat("/CMakeCache.txt")); //$NON-NLS-1$ + File cmakeCacheFile = new File(buildDirectory, "CMakeCache.txt"); //$NON-NLS-1$ if (cmakeCacheFile.exists() && !StringUtil.isEmpty(oldSdkconfigValue)) { StringBuilder contentBuilder = new StringBuilder(); diff --git a/bundles/com.espressif.idf.sdk.config.ui/src/com/espressif/idf/sdk/config/ui/ConfigContentProvider.java b/bundles/com.espressif.idf.sdk.config.ui/src/com/espressif/idf/sdk/config/ui/ConfigContentProvider.java index 474dca0af..105f6273a 100644 --- a/bundles/com.espressif.idf.sdk.config.ui/src/com/espressif/idf/sdk/config/ui/ConfigContentProvider.java +++ b/bundles/com.espressif.idf.sdk.config.ui/src/com/espressif/idf/sdk/config/ui/ConfigContentProvider.java @@ -29,13 +29,15 @@ public class ConfigContentProvider extends TreeNodeContentProvider { private static Object[] EMPTY_ARRAY = new Object[0]; protected TreeViewer viewer; - private IProject project; - private IFile file; - - public ConfigContentProvider(IProject project, IFile file) + private final IProject project; + private final IFile file; + private final String buildDirectory; + + public ConfigContentProvider(IProject project, IFile file, String buildDirectory) { this.project = project; this.file = file; + this.buildDirectory = buildDirectory; } /* @@ -92,22 +94,23 @@ public Object[] getChildren(Object parentElement) private List getMenuItems(List children) throws IOException { - - JsonConfigServer configServer = ConfigServerManager.INSTANCE.getServer(project, file); + JsonConfigServer configServer = ConfigServerManager.INSTANCE.getServer(project, file, buildDirectory); List menuList = new ArrayList(); for (KConfigMenuItem kConfigMenuItem : children) { if (kConfigMenuItem.getType() != null && kConfigMenuItem.getType().equals(IJsonServerConfig.MENU_TYPE)) { JSONObject visibleJsonMap = configServer.getOutput().getVisibleJsonMap(); - Logger.logTrace(SDKConfigUIPlugin.getDefault(), "item >" + kConfigMenuItem.getTitle() + " type >"+ kConfigMenuItem.getType()); //$NON-NLS-1$ //$NON-NLS-2$ - + Logger.logTrace(SDKConfigUIPlugin.getDefault(), + "item >" + kConfigMenuItem.getTitle() + " type >" + kConfigMenuItem.getType()); //$NON-NLS-1$ //$NON-NLS-2$ + boolean visible = kConfigMenuItem.isVisible(visibleJsonMap); if (!kConfigMenuItem.isMenuConfig()) { visible = true; } - Logger.logTrace(SDKConfigUIPlugin.getDefault(), "visibility >" + kConfigMenuItem.isVisible(visibleJsonMap)); //$NON-NLS-1$ + Logger.logTrace(SDKConfigUIPlugin.getDefault(), + "visibility >" + kConfigMenuItem.isVisible(visibleJsonMap)); //$NON-NLS-1$ if (visible) { menuList.add(kConfigMenuItem); diff --git a/bundles/com.espressif.idf.sdk.config.ui/src/com/espressif/idf/sdk/config/ui/LoadSdkConfigHandler.java b/bundles/com.espressif.idf.sdk.config.ui/src/com/espressif/idf/sdk/config/ui/LoadSdkConfigHandler.java index 77ea24815..9a0b3370b 100644 --- a/bundles/com.espressif.idf.sdk.config.ui/src/com/espressif/idf/sdk/config/ui/LoadSdkConfigHandler.java +++ b/bundles/com.espressif.idf.sdk.config.ui/src/com/espressif/idf/sdk/config/ui/LoadSdkConfigHandler.java @@ -55,7 +55,8 @@ public Object execute(ExecutionEvent event) throws ExecutionException try { - JsonConfigServer server = ConfigServerManager.INSTANCE.getServer(project, file); + String buildDirectory = SDKConfigurationEditor.resolveBuildDirectory(project, file); + JsonConfigServer server = ConfigServerManager.INSTANCE.getServer(project, file, buildDirectory); // load changes JSONObject jsonObject = new JSONObject(); jsonObject.put(IJsonServerConfig.VERSION, 2); diff --git a/bundles/com.espressif.idf.sdk.config.ui/src/com/espressif/idf/sdk/config/ui/SDKConfigurationEditor.java b/bundles/com.espressif.idf.sdk.config.ui/src/com/espressif/idf/sdk/config/ui/SDKConfigurationEditor.java index c093b3241..524aa47d2 100644 --- a/bundles/com.espressif.idf.sdk.config.ui/src/com/espressif/idf/sdk/config/ui/SDKConfigurationEditor.java +++ b/bundles/com.espressif.idf.sdk.config.ui/src/com/espressif/idf/sdk/config/ui/SDKConfigurationEditor.java @@ -10,11 +10,9 @@ import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; -import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; @@ -22,7 +20,6 @@ import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; -import org.eclipse.core.runtime.Path; import org.eclipse.jface.dialogs.ErrorDialog; import org.eclipse.jface.dialogs.IPageChangedListener; import org.eclipse.jface.dialogs.MessageDialog; @@ -117,6 +114,8 @@ public class SDKConfigurationEditor extends MultiPageEditorPart private ScrolledComposite sc; + private String buildDirectory; + private static final int MIN_VERSION_FOR_RESET = 3; public SDKConfigurationEditor() @@ -134,22 +133,14 @@ protected void createPages() IWorkbench workbench = PlatformUI.getWorkbench(); IProgressService progressService = workbench.getProgressService(); - // remember current build folder for the project IEP-1250 - final String buildFolder = getCurrentBuildFolder(); final IRunnableWithProgress runnable = monitor -> { monitor.beginTask(Messages.SDKConfigurationEditor_LaunchSDKConfigEditor, 3); try { - // if sdkconfig is located in the build folder then temporary setting this folder as build folder on the - // project level IEP-1250 - if (isSdkConfigLocatedInBuildFolder()) - { - IDFUtil.setBuildDir(project, getSdkConfigParentFolderOpt().get().getLocation().toOSString()); - } // 1. Getting kconfig_menus.json - final String configMenuJsonPath = new SDKConfigUtil().getConfigMenuFilePath(project); + final String configMenuJsonPath = new SDKConfigUtil().getConfigMenuFilePath(buildDirectory); if (configMenuJsonPath == null || !new File(configMenuJsonPath).exists()) { Display.getDefault().asyncExec(() -> { @@ -170,8 +161,6 @@ protected void createPages() } catch (Exception x) { - // rollback build folder if something went wrong - rollbackBuildFolder(buildFolder); throw new InvocationTargetException(x, x.getMessage()); } }; @@ -196,9 +185,6 @@ protected void createPages() // 3. Build the UI createDesignPage(); createSourcePage(); - - // rollback build folder after UI is built - rollbackBuildFolder(buildFolder); } /** @@ -252,7 +238,7 @@ private void createDesignPage() treeViewer = transfersTree.getViewer(); // Create the tree viewer as a child of the composite parent - treeViewer.setContentProvider(new ConfigContentProvider(project, getFile())); + treeViewer.setContentProvider(new ConfigContentProvider(project, getFile(), buildDirectory)); treeViewer.setLabelProvider(new ConfigLabelProvider()); treeViewer.setUseHashlookup(true); @@ -362,7 +348,7 @@ protected void initConfigServer(IProject project) throws IOException MessageConsoleStream console = new IDFConsole().getConsoleStream("JSON Configuration Server Console", null, //$NON-NLS-1$ false); - configServer = ConfigServerManager.INSTANCE.getServer(project, getFile()); + configServer = ConfigServerManager.INSTANCE.getServer(project, getFile(), buildDirectory); // register the editor with the server to notify about the events configServer.addListener(this); @@ -444,7 +430,7 @@ public void dispose() { configServer.destroy(); } - ConfigServerManager.INSTANCE.deleteServer(project, getFile()); + ConfigServerManager.INSTANCE.deleteServer(project, getFile(), buildDirectory); super.dispose(); } @@ -507,6 +493,14 @@ public void init(IEditorSite site, IEditorInput editorInput) throws PartInitExce super.init(site, editorInput); this.project = getProject(); + try + { + this.buildDirectory = resolveBuildDirectory(project, ((IFileEditorInput) editorInput).getFile()); + } + catch (CoreException e) + { + throw new PartInitException(e.getMessage(), e); + } } /* @@ -523,7 +517,7 @@ public boolean isSaveAsAllowed() */ public KConfigMenuItem getInitalInput() { - KConfigMenuProcessor jsonReader = new KConfigMenuProcessor(project); + KConfigMenuProcessor jsonReader = new KConfigMenuProcessor(buildDirectory); try { return jsonReader.reader(); @@ -828,46 +822,22 @@ public String getSystemProperty(String option) return System.getProperty(option); } - private String getCurrentBuildFolder() + static String resolveBuildDirectory(IProject project, IFile sdkConfigFile) throws CoreException { - String buildFolder = StringUtil.EMPTY; - try - { - IDFUtil.getBuildDir(project); - } - catch (CoreException e) + IPath sdkConfigLocation = sdkConfigFile.getLocation(); + if (sdkConfigLocation != null) { - Logger.log(e); - } - return buildFolder; - } - - private void rollbackBuildFolder(String buildFolder) - { - try - { - IDFUtil.setBuildDir(project, buildFolder); - } - catch (CoreException e) - { - Logger.log(e); - } - } - - private boolean isSdkConfigLocatedInBuildFolder() - { - Optional sdkConfigParentOpt = getSdkConfigParentFolderOpt(); - return sdkConfigParentOpt.isPresent() && sdkConfigParentOpt.get().exists(Path - .fromPortableString(IDFConstants.CONFIG_FOLDER + IPath.SEPARATOR + IDFConstants.KCONFIG_MENUS_JSON)); - } - - private Optional getSdkConfigParentFolderOpt() - { - if (getEditorInput() instanceof IFileEditorInput editorInput) - { - IFile sdkConfigFile = editorInput.getFile(); - return Optional.ofNullable(sdkConfigFile.getParent()); + File sdkConfigParent = sdkConfigLocation.toFile().getParentFile(); + if (sdkConfigParent != null) + { + File configMenuFile = new File(new File(sdkConfigParent, IDFConstants.CONFIG_FOLDER), + IDFConstants.KCONFIG_MENUS_JSON); + if (configMenuFile.isFile()) + { + return sdkConfigParent.toPath().toAbsolutePath().normalize().toString(); + } + } } - return Optional.empty(); + return new File(IDFUtil.getBuildDir(project)).toPath().toAbsolutePath().normalize().toString(); } } diff --git a/bundles/com.espressif.idf.terminal.connector.serial/src/com/espressif/idf/terminal/connector/serial/launcher/CoreDumpPostmortemDebuggerLauncher.java b/bundles/com.espressif.idf.terminal.connector.serial/src/com/espressif/idf/terminal/connector/serial/launcher/CoreDumpPostmortemDebuggerLauncher.java index d4e8eb327..306648dca 100644 --- a/bundles/com.espressif.idf.terminal.connector.serial/src/com/espressif/idf/terminal/connector/serial/launcher/CoreDumpPostmortemDebuggerLauncher.java +++ b/bundles/com.espressif.idf.terminal.connector.serial/src/com/espressif/idf/terminal/connector/serial/launcher/CoreDumpPostmortemDebuggerLauncher.java @@ -83,7 +83,7 @@ public IFile launchDebugSession() throws Exception private void parseExtractedFileFromPythonScript() throws Exception { Logger.log("Converting coredump"); //$NON-NLS-1$ - String coreDumpDestination = getCoreDumpFileFromBuildDir(GENERATED_CORE_ELF_NAME); + String coreDumpDestination = getCoreDumpStagingFilePath(GENERATED_CORE_ELF_NAME); // espcoredump.py @@ -222,7 +222,7 @@ private void createXMLConfig() throws Exception String.valueOf(100)); createElement(dom, root, stringAttribute, "org.eclipse.cdt.launch.COREFILE_PATH", //$NON-NLS-1$ - getCoreDumpFileFromBuildDir(GENERATED_CORE_ELF_NAME)); + getCoreDumpStagingFilePath(GENERATED_CORE_ELF_NAME)); createElement(dom, root, stringAttribute, "org.eclipse.cdt.launch.DEBUGGER_ID", "gdb"); //$NON-NLS-1$ //$NON-NLS-2$ createElement(dom, root, stringAttribute, "org.eclipse.cdt.launch.DEBUGGER_START_MODE", "core"); //$NON-NLS-1$ //$NON-NLS-2$ @@ -250,14 +250,18 @@ private void createXMLConfig() throws Exception Transformer tr = TransformerFactory.newInstance().newTransformer(); tr.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$ - String launchFile = getCoreDumpFileFromBuildDir( + String launchFile = getCoreDumpStagingFilePath( String.format(CORE_DUMP_POSTMORTEM_LAUNCH_CONFIG, project.getName())); tr.transform(new DOMSource(dom), new StreamResult(new File(launchFile))); project.refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor()); } - private String getCoreDumpFileFromBuildDir(String fileName) + /** + * Keeps generated postmortem files in the project-local build folder. These are debugger staging files, not + * ESP-IDF build outputs, so they must not be redirected to or create an external configured build directory. + */ + private String getCoreDumpStagingFilePath(String fileName) { IFolder buildRootFolder = project.getFolder(IDFConstants.BUILD_FOLDER); StringBuilder coreDumpDestination = new StringBuilder(); diff --git a/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/LaunchBarListener.java b/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/LaunchBarListener.java index be7f03ca1..4290fdd10 100644 --- a/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/LaunchBarListener.java +++ b/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/LaunchBarListener.java @@ -5,6 +5,8 @@ package com.espressif.idf.ui; import java.io.File; +import java.io.IOException; +import java.nio.file.Path; import java.text.MessageFormat; import java.util.Optional; import java.util.stream.Stream; @@ -18,7 +20,6 @@ import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; -import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.debug.core.ILaunchMode; @@ -35,7 +36,6 @@ import com.espressif.idf.core.build.IDFLaunchConstants; import com.espressif.idf.core.logging.Logger; import com.espressif.idf.core.util.IDFUtil; -import com.espressif.idf.core.util.LaunchUtil; import com.espressif.idf.core.util.SDKConfigJsonReader; import com.espressif.idf.core.util.StringUtil; @@ -72,7 +72,6 @@ else if (IDFLaunchConstants.DEBUG_LAUNCH_CONFIG_TYPE.equals(configTypeIdentifier setMode(launchBarManager, ILaunchManager.RUN_MODE); setMode(launchBarManager, ILaunchManager.DEBUG_MODE); } - updateProjectBuildFolderBasedOnActiveConfig(activeLaunchConfiguration); } } catch (CoreException e) @@ -166,6 +165,24 @@ private void update(String newTarget) private void deleteBuildFolder(IResource project, File buildLocation) { + Path projectPath; + Path buildPath; + try + { + projectPath = project.getLocation().toFile().toPath().toRealPath(); + buildPath = buildLocation.toPath().toRealPath(); + } + catch (IOException e) + { + Logger.log(e); + return; + } + if (buildPath.equals(projectPath) || !buildPath.startsWith(projectPath)) + { + Logger.log("Skipping automatic deletion of unsafe build directory " + buildPath); //$NON-NLS-1$ + return; + } + IWorkspaceRunnable runnable = new IWorkspaceRunnable() { @@ -246,26 +263,4 @@ private void setMode(ILaunchBarManager launchBarManager, String mode) } } - private void updateProjectBuildFolderBasedOnActiveConfig(ILaunchConfiguration configuration) - { - if (configuration == null) - { - return; - } - try - { - if (configuration.getType().getIdentifier().equals(IDFLaunchConstants.DEBUG_LAUNCH_CONFIG_TYPE)) - { - configuration = new LaunchUtil(DebugPlugin.getDefault().getLaunchManager()) - .getBoundConfiguration(configuration); - } - IDFUtil.updateProjectBuildFolder(configuration.getWorkingCopy()); - - } - catch (CoreException e) - { - Logger.log(e); - } - } - -} \ No newline at end of file +} diff --git a/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/dialogs/CMakeBuildTab2.java b/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/dialogs/CMakeBuildTab2.java index 7487a0d11..1dafc4e1a 100644 --- a/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/dialogs/CMakeBuildTab2.java +++ b/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/dialogs/CMakeBuildTab2.java @@ -123,6 +123,7 @@ public void widgetSelected(SelectionEvent e) buildFolderText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); buildFolderText.setMessage(Messages.CMakeBuildTab2_BuildFolderTextMsg); buildFolderText.setToolTipText(Messages.CMakeBuildTab2_BuildFolderTextToolTip); + buildFolderText.addModifyListener(e -> updateLaunchConfigurationDialog()); // Browse button to select a folder Button browseButton = createPushButton(buildFolderComp, LaunchMessages.Launch_common_Browse_1, null); // $NON-NLS-1$ diff --git a/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/dialogs/SbomCommandDialog.java b/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/dialogs/SbomCommandDialog.java index 0a27a578f..f9dad1712 100644 --- a/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/dialogs/SbomCommandDialog.java +++ b/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/dialogs/SbomCommandDialog.java @@ -51,6 +51,7 @@ import org.eclipse.ui.console.TextConsole; import org.eclipse.ui.ide.IDE; +import com.espressif.idf.core.IDFConstants; import com.espressif.idf.core.IDFCorePlugin; import com.espressif.idf.core.ProcessBuilderFactory; import com.espressif.idf.core.logging.Logger; @@ -276,9 +277,15 @@ private void setDefaults() private String buildProjectDescriptionPath() { - return String.join(FileSystems.getDefault().getSeparator(), - Paths.get(selectedProject.getLocationURI()).toString(), "build", //$NON-NLS-1$ - "project_description.json"); //$NON-NLS-1$ + try + { + return Paths.get(IDFUtil.getBuildDir(selectedProject), IDFConstants.PROECT_DESCRIPTION_JSON).toString(); + } + catch (CoreException e) + { + Logger.log(e); + return StringUtil.EMPTY; + } } private void runEspIdfSbomCommand() diff --git a/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/handlers/HeapDumpAnalysisHandler.java b/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/handlers/HeapDumpAnalysisHandler.java index b9f7ce31d..ad04386da 100644 --- a/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/handlers/HeapDumpAnalysisHandler.java +++ b/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/handlers/HeapDumpAnalysisHandler.java @@ -33,6 +33,7 @@ import com.espressif.idf.core.logging.Logger; import com.espressif.idf.core.util.FileUtil; import com.espressif.idf.core.util.IDFUtil; +import com.espressif.idf.core.util.ProjectDescriptionReader; import com.espressif.idf.ui.IDFConsole; import com.espressif.idf.ui.tracing.heaptracing.HeapTracingAnalysisEditor; @@ -60,14 +61,19 @@ public Object execute(ExecutionEvent event) throws ExecutionException IResource dumpFile = EclipseHandler.getSelectedResource((IEvaluationContext) event.getApplicationContext()); IProject selectedProject = dumpFile.getProject(); - IFile elfSymbolsFile = selectedProject.getFolder("build").getFile(selectedProject.getName().concat(".elf")); //$NON-NLS-1$ //$NON-NLS-2$ + File elfSymbolsFile = new ProjectDescriptionReader(selectedProject).getAppElfFileLocation(); + if (elfSymbolsFile == null || !elfSymbolsFile.isFile()) + { + messageConsoleStream.println("Could not find the application ELF file"); //$NON-NLS-1$ + return null; + } List commands = new ArrayList<>(); commands.add(IDFUtil.getIDFPythonEnvPath()); commands.add(IDFUtil.getIDFSysviewTraceScriptFile().getAbsolutePath()); commands.add("-j"); //$NON-NLS-1$ commands.add("-b"); //$NON-NLS-1$ - commands.add(elfSymbolsFile.getRawLocation().toOSString()); + commands.add(elfSymbolsFile.getAbsolutePath()); commands.addAll(resolveTraceSources(dumpFile)); messageConsoleStream.println("Commands Prepared"); //$NON-NLS-1$ diff --git a/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/handlers/Messages.java b/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/handlers/Messages.java index c7f758c06..801ce4f92 100644 --- a/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/handlers/Messages.java +++ b/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/handlers/Messages.java @@ -28,7 +28,6 @@ public class Messages extends NLS public static String MissingDebugConfigurationTitle; public static String DebugConfigurationNotFoundMsg; - public static String RenameIdfProjectParticipant_RenameBuildFolderPathChangeName; public static String RunActionHandler_NoProjectQuestionText; public static String RunActionHandler_NoProjectQuestionTitle; diff --git a/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/handlers/RenameIdfProjectParticipant.java b/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/handlers/RenameIdfProjectParticipant.java index f409acd73..0198a56fd 100644 --- a/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/handlers/RenameIdfProjectParticipant.java +++ b/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/handlers/RenameIdfProjectParticipant.java @@ -1,12 +1,8 @@ package com.espressif.idf.ui.handlers; -import org.eclipse.core.resources.IFolder; -import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; -import org.eclipse.core.runtime.Status; import org.eclipse.launchbar.core.ILaunchBarManager; import org.eclipse.launchbar.core.target.ILaunchTarget; import org.eclipse.ltk.core.refactoring.Change; @@ -15,75 +11,15 @@ import org.eclipse.ltk.core.refactoring.participants.RenameParticipant; import org.eclipse.swt.widgets.Display; -import com.espressif.idf.core.IDFConstants; import com.espressif.idf.core.IDFCorePlugin; import com.espressif.idf.core.logging.Logger; -import com.espressif.idf.core.util.IDFUtil; import com.espressif.idf.ui.LaunchBarListener; public class RenameIdfProjectParticipant extends RenameParticipant { - private IProject project; - - class UpdateBuildFolderChange extends Change - { - - private String oldBuildFolderPath; - private String newBuildPath; - private IProject newProject; - private IFolder newBuildFolder; - - @Override - public String getName() - { - return String.format(Messages.RenameIdfProjectParticipant_RenameBuildFolderPathChangeName, - oldBuildFolderPath, newBuildPath); - } - - @Override - public RefactoringStatus isValid(IProgressMonitor pm) - { - return RefactoringStatus.create(Status.OK_STATUS); - } - - @Override - public void initializeValidationData(IProgressMonitor pm) - { - try - { - oldBuildFolderPath = IDFUtil.getBuildDir(project); - String newProjectName = getArguments().getNewName(); - newProject = ResourcesPlugin.getWorkspace().getRoot().getProject(newProjectName); - newBuildFolder = newProject.getFolder(IDFConstants.BUILD_FOLDER); - newBuildPath = newBuildFolder.getFullPath().toOSString(); - } - catch (CoreException e) - { - Logger.log(e); - } - } - - @Override - public Change perform(IProgressMonitor pm) throws CoreException - { - IDFUtil.setBuildDir(newProject, newBuildFolder.getLocation().toOSString()); - return null; - } - - @Override - public Object getModifiedElement() - { - return project; - } - } - protected boolean initialize(Object element) { - if (element instanceof IProject projectElement) - { - this.project = projectElement; - } return true; } @@ -117,7 +53,7 @@ public Change createChange(IProgressMonitor pm) throws CoreException, OperationC })); - return new UpdateBuildFolderChange(); + return null; } } diff --git a/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/handlers/messages.properties b/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/handlers/messages.properties index f40491324..c232bce66 100644 --- a/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/handlers/messages.properties +++ b/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/handlers/messages.properties @@ -20,6 +20,5 @@ UpdateEspIdfCommand_InstallToolsJobMsg=Installing tools... UpdateEspIdfCommand_SuggestToOpenInstallToolsWizard = A new set of tools might be required to install. Do you want to open the Install Tools dialog? MissingDebugConfigurationTitle=Missing debug configuration DebugConfigurationNotFoundMsg=No matching debug configuration was found for the selected project. Do you want to create it? -RenameIdfProjectParticipant_RenameBuildFolderPathChangeName=Update build folder path from "%s" to "%s" RunActionHandler_NoProjectQuestionText=The selected configuration does not include a project. Would you like to edit the active launch configuration and specify the project? RunActionHandler_NoProjectQuestionTitle=Edit Active Configuration? diff --git a/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/size/IDFSizeMemoryHandler.java b/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/size/IDFSizeMemoryHandler.java index a5d5f96b8..c46a26dab 100644 --- a/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/size/IDFSizeMemoryHandler.java +++ b/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/size/IDFSizeMemoryHandler.java @@ -81,15 +81,15 @@ private void launchEditor(IPath mapFilePath, IProject project) throws CoreExcept IFile iFile = workspace.getRoot().getFileForLocation(mapFilePath); if (mapFilePath.toFile().exists() && iFile == null) // file is located outside of the workspace { - // create a link in the project/build/ folder to open file in eclipse editor - IFolder buildRootFolder = project.getFolder(IDFConstants.BUILD_FOLDER); - iFile = buildRootFolder.getFile(mapFilePath.lastSegment()); + // Keep the editor bridge project-local; it is only a workspace link, not the configured build directory. + IFolder editorLinkFolder = project.getFolder(IDFConstants.BUILD_FOLDER); + iFile = editorLinkFolder.getFile(mapFilePath.lastSegment()); if (!iFile.exists()) { IProgressMonitor monitor = new NullProgressMonitor(); - if (!buildRootFolder.exists()) + if (!editorLinkFolder.exists()) { - buildRootFolder.create(IResource.FORCE | IResource.DERIVED, true, monitor); + editorLinkFolder.create(IResource.FORCE | IResource.DERIVED, true, monitor); } iFile.createLink(mapFilePath, IResource.NONE, null); } diff --git a/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/tracing/AppLvlTracingDialog.java b/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/tracing/AppLvlTracingDialog.java index 1cd6fc8ad..23c0a97b2 100644 --- a/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/tracing/AppLvlTracingDialog.java +++ b/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/tracing/AppLvlTracingDialog.java @@ -363,8 +363,8 @@ private String wrapOutputFilePath(String baseFilePath) public void setProjectPath(IResource project) { pathToProject = project.getLocation().toString(); - IFile elfFile = new ProjectDescriptionReader(project.getProject()).getAppElfFile(); - elfFilePath = elfFile == null ? null : elfFile.getLocation().toString(); + File elfFile = new ProjectDescriptionReader(project.getProject()).getAppElfFileLocation(); + elfFilePath = elfFile == null ? null : elfFile.getAbsolutePath(); pathToProject = wrapOutputFilePath(pathToProject); } diff --git a/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/tracing/TracingJsonParser.java b/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/tracing/TracingJsonParser.java index 623f4264f..de6a7a0e5 100644 --- a/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/tracing/TracingJsonParser.java +++ b/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/tracing/TracingJsonParser.java @@ -4,6 +4,7 @@ *******************************************************************************/ package com.espressif.idf.ui.tracing; +import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.util.ArrayList; @@ -15,6 +16,9 @@ import java.util.stream.Collectors; import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.Path; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -31,7 +35,8 @@ public class TracingJsonParser { private String jsonFilePath; - private IFile elfFilePath; + private File elfFile; + private IFile workspaceElfFile; private Gson gson; private int allocEventId; private int freeEventId; @@ -40,16 +45,25 @@ public class TracingJsonParser private Map callersAddressMap; private TracingCallerAddressDecoder tracingCallerAddressDecoder; - public TracingJsonParser(String jsonFilePath, IFile elfFilePath) throws FileNotFoundException + public TracingJsonParser(String jsonFilePath, File elfFile, IProject project) throws FileNotFoundException { this.jsonFilePath = jsonFilePath; - this.setElfFilePath(elfFilePath); + this.elfFile = elfFile; gson = new GsonBuilder().registerTypeAdapter(ArrayList.class, new StreamEventsDeserializer()).create(); - tracingCallerAddressDecoder = new TracingCallerAddressDecoder(elfFilePath.getRawLocation().toOSString(), - elfFilePath.getProject()); + tracingCallerAddressDecoder = new TracingCallerAddressDecoder(elfFile.getAbsolutePath(), project); loadJson(); } + /** + * @deprecated Use {@link #TracingJsonParser(String, File, IProject)} for external build directories. + */ + @Deprecated(forRemoval = true) + public TracingJsonParser(String jsonFilePath, IFile elfFile) throws FileNotFoundException + { + this(jsonFilePath, elfFile.getRawLocation().toFile(), elfFile.getProject()); + this.workspaceElfFile = elfFile; + } + private void loadJson() throws FileNotFoundException { JsonReader jsonReader = new JsonReader(new FileReader(jsonFilePath)); @@ -170,14 +184,39 @@ public List getDetailsVOs(List eventIds) return detailsVOs; } + public File getElfFile() + { + return elfFile; + } + + public void setElfFile(File elfFile) + { + this.elfFile = elfFile; + this.workspaceElfFile = null; + } + + /** + * @deprecated Use {@link #getElfFile()} for external build directories. + */ + @Deprecated(forRemoval = true) public IFile getElfFilePath() { - return elfFilePath; + if (workspaceElfFile == null && elfFile != null) + { + workspaceElfFile = ResourcesPlugin.getWorkspace().getRoot() + .getFileForLocation(Path.fromOSString(elfFile.getAbsolutePath())); + } + return workspaceElfFile; } - public void setElfFilePath(IFile elfFilePath) + /** + * @deprecated Use {@link #setElfFile(File)} for external build directories. + */ + @Deprecated(forRemoval = true) + public void setElfFilePath(IFile elfFile) { - this.elfFilePath = elfFilePath; + this.workspaceElfFile = elfFile; + this.elfFile = elfFile == null ? null : elfFile.getRawLocation().toFile(); } public Map getCallersAddressMap() diff --git a/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/tracing/heaptracing/HeapTracingAnalysisEditor.java b/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/tracing/heaptracing/HeapTracingAnalysisEditor.java index de07d385e..f41e6c6bb 100644 --- a/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/tracing/heaptracing/HeapTracingAnalysisEditor.java +++ b/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/tracing/heaptracing/HeapTracingAnalysisEditor.java @@ -5,6 +5,8 @@ package com.espressif.idf.ui.tracing.heaptracing; +import java.io.File; + import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.IProgressMonitor; @@ -36,7 +38,7 @@ public class HeapTracingAnalysisEditor extends MultiPageEditorPart public static final String EDITOR_ID = "com.espressif.idf.ui.editor.heapTraceAnalysis"; //$NON-NLS-1$ private IProject project; private IFile memoryDumpFile; - private IFile elfSymbolsFile; + private File elfSymbolsFile; private TracingJsonParser tracingJsonParser; @Override @@ -47,11 +49,11 @@ public void init(IEditorSite site, IEditorInput input) throws PartInitException memoryDumpFile = editorInput.getFile(); project = memoryDumpFile.getProject(); setPartName(project.getName()); - elfSymbolsFile = new ProjectDescriptionReader(project).getAppElfFile(); + elfSymbolsFile = new ProjectDescriptionReader(project).getAppElfFileLocation(); try { tracingJsonParser = new TracingJsonParser(memoryDumpFile.getRawLocation().toOSString(), - this.elfSymbolsFile); + this.elfSymbolsFile, project); } catch (Exception execption) { diff --git a/docs/en/buildproject.rst b/docs/en/buildproject.rst index 2661417b8..fbdcf62d3 100644 --- a/docs/en/buildproject.rst +++ b/docs/en/buildproject.rst @@ -22,11 +22,18 @@ However, the following steps will guide you through the process of building the Custom Build Directory ---------------------- -The IDE allows configuring a custom build directory for the project: +The IDE stores ``Build folder location`` on each launch configuration. Select that configuration in the **Launch Bar** so that build, ``sdkconfig``, flash, and debug all use the same folder. + +To configure a custom build directory: 1. Select a project and click on the ``Edit`` button for the launch configuration in the top toolbar to open the ``Edit Configuration`` window. 2. Navigate to the ``Build Settings`` tab. -3. In the ``Build folder location`` section, provide a custom build directory. The customized build directory path can be within the project or in any other location in the file system. +3. In the ``Build folder location`` field, enter a path: + + - Leave the field blank to use the default folder ``project/build``. + - A relative path is resolved under the project directory. + - An absolute path can point to any location in the file system. + 4. Click on ``Ok`` and build the project. .. note:: diff --git a/docs/en/faqs.rst b/docs/en/faqs.rst index 8f27adf7f..cfc9696be 100644 --- a/docs/en/faqs.rst +++ b/docs/en/faqs.rst @@ -174,15 +174,19 @@ IDF Eclipse plugin uses CMake commands to build the project, so it's possible to How do I build multiple configurations in Espressif-IDE? -------------------------------------------------------- +Duplicate the launch configuration and set a different ``Build folder location`` for each one. Select the configuration in the **Launch Bar** so that build, ``sdkconfig``, flash, and debug use that folder. + 1. Create a new project. 2. Open the ``Launch Configuration`` dialog. -3. Navigate to the ``Build Settings`` tab and enter ``-B build_release`` in the ``Additional CMake Arguments`` section. Here, ``build_release`` is the name of the build folder. +3. Navigate to the ``Build Settings`` tab and enter ``build_release`` in the ``Build folder location`` field. Here, ``build_release`` is a relative path under the project. 4. Click the ``OK`` button to save the configuration. 5. Reopen the ``Launch Configuration`` dialog. 6. Click the ``Duplicate`` button at the bottom left corner. -7. Navigate to the ``Build Settings`` tab and update the ``Additional CMake Arguments`` section to ``-B build_dev``. Here, ``build_dev`` is the name of the build folder. +7. Navigate to the ``Build Settings`` tab and set ``Build folder location`` to ``build_dev``. Here, ``build_dev`` is a relative path under the project. 8. Click the ``OK`` button to save the configuration. -9. Click the ``Build`` icon from the toolbar (the leftmost icon) for the selected configuration. This will build the project and create a build folder for that configuration. Repeat the same process for the other configuration by selecting it from the dropdown. +9. Select a configuration from the Launch Bar dropdown, then click the ``Build`` icon from the toolbar (the leftmost icon). This builds into that configuration's folder. Repeat for the other configuration. + +Leave ``Build folder location`` blank to use ``project/build``. Absolute paths are also supported. Can I use my old C/C++ editor formatter file (.xml) as a ``.clang-format`` file? -------------------------------------------------------------------------------- diff --git a/docs/zh_CN/buildproject.rst b/docs/zh_CN/buildproject.rst index 2b6c6fc2c..ee89d4e32 100644 --- a/docs/zh_CN/buildproject.rst +++ b/docs/zh_CN/buildproject.rst @@ -22,11 +22,18 @@ 自定义构建目录 -------------- -IDE 允许为项目配置自定义构建目录: +IDE 将 ``Build folder location`` 保存在每个启动配置中。在 **Launch Bar** 中选择该配置后,构建、``sdkconfig``、烧录和调试都会使用同一文件夹。 + +配置自定义构建目录的步骤如下: 1. 选择一个项目,在顶部工具栏中点击启动配置的 ``Edit`` 按钮,打开 ``Edit Configuration`` 窗口。 2. 前往 ``Build Settings`` 选项卡。 -3. 在 ``Build folder location`` 部分填写自定义的构建目录。该自定义目录路径可以位于项目内,也可以位于文件系统中的任意位置。 +3. 在 ``Build folder location`` 字段中填写路径: + + - 留空则使用默认文件夹 ``project/build``。 + - 相对路径会解析到项目目录下。 + - 绝对路径可以指向文件系统中的任意位置。 + 4. 点击 ``Ok`` 并构建项目。 .. note:: diff --git a/docs/zh_CN/faqs.rst b/docs/zh_CN/faqs.rst index fd9819956..0e9e0f9c8 100644 --- a/docs/zh_CN/faqs.rst +++ b/docs/zh_CN/faqs.rst @@ -174,15 +174,19 @@ IDF Eclipse 插件使用 CMake 命令来构建项目,因此可以通过构建 如何在 Espressif-IDE 中构建多个配置? ---------------------------------------- +复制启动配置,并为每个配置设置不同的 ``Build folder location``。在 **Launch Bar** 中选择该配置后,构建、``sdkconfig``、烧录和调试都会使用对应的文件夹。 + 1. 创建一个新项目。 2. 打开 ``Launch Configuration`` 对话框。 -3. 进入 ``Build Settings`` 选项卡,在 ``Additional CMake Arguments`` 中输入 ``-B build_release``。其中,``build_release`` 是构建文件夹的名称。 +3. 进入 ``Build Settings`` 选项卡,在 ``Build folder location`` 中输入 ``build_release``。其中,``build_release`` 是相对于项目目录的路径。 4. 点击 ``OK`` 按钮保存该配置。 5. 重新打开 ``Launch Configuration`` 对话框。 6. 点击左下角的 ``Duplicate`` 按钮。 -7. 进入 ``Build Settings`` 选项卡,将 ``Additional CMake Arguments`` 更新为 ``-B build_dev``。其中,``build_dev`` 是构建文件夹的名称。 +7. 进入 ``Build Settings`` 选项卡,将 ``Build folder location`` 设置为 ``build_dev``。其中,``build_dev`` 是相对于项目目录的路径。 8. 点击 ``OK`` 按钮保存该配置。 -9. 在工具栏中点击所选配置的 ``Build`` 图标(最左侧图标),这将为该配置构建项目并创建一个构建文件夹。然后在下拉菜单中选择另一配置,重复相同的步骤。 +9. 在 Launch Bar 下拉菜单中选择一个配置,然后点击工具栏中的 ``Build`` 图标(最左侧图标)。这将把项目构建到该配置对应的文件夹中。然后选择另一配置,重复相同的步骤。 + +将 ``Build folder location`` 留空则使用 ``project/build``。也支持绝对路径。 可以将我之前的 C/C++ 编辑器格式化文件 (.xml) 用作 ``.clang-format`` 文件吗? ---------------------------------------------------------------------------- diff --git a/tests/com.espressif.idf.core.test/src/com/espressif/idf/core/build/test/BuildDirectoryResolverTest.java b/tests/com.espressif.idf.core.test/src/com/espressif/idf/core/build/test/BuildDirectoryResolverTest.java new file mode 100644 index 000000000..d5f6d04be --- /dev/null +++ b/tests/com.espressif.idf.core.test/src/com/espressif/idf/core/build/test/BuildDirectoryResolverTest.java @@ -0,0 +1,150 @@ +package com.espressif.idf.core.build.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; + +import java.io.File; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchConfigurationType; +import org.eclipse.launchbar.core.ILaunchBarManager; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.mockito.MockedStatic; + +import com.espressif.idf.core.IDFCorePlugin; +import com.espressif.idf.core.build.BuildDirectoryResolver; +import com.espressif.idf.core.build.IDFLaunchConstants; + +class BuildDirectoryResolverTest +{ + @TempDir + File tempDirectory; + + @Test + void resolveActiveConfigurationRelativePathOverridesLegacyProjectProperty() throws Exception + { + IProject project = mockProject(); + ILaunchConfiguration configuration = mockRunConfiguration("build-release", project); //$NON-NLS-1$ + ILaunchBarManager launchBarManager = mock(ILaunchBarManager.class); + when(launchBarManager.getActiveLaunchConfiguration()).thenReturn(configuration); + + try (MockedStatic plugin = mockStatic(IDFCorePlugin.class)) + { + plugin.when(() -> IDFCorePlugin.getService(ILaunchBarManager.class)).thenReturn(launchBarManager); + + IPath result = BuildDirectoryResolver.resolve(project); + + assertEquals(project.getLocation().append("build-release"), result); //$NON-NLS-1$ + } + } + + @Test + void resolveAbsolutePathPreservesExternalLocation() throws Exception + { + IProject project = mockProject(); + String externalBuildDirectory = new File(tempDirectory, "external-build").getAbsolutePath(); //$NON-NLS-1$ + ILaunchConfiguration configuration = mockRunConfiguration(externalBuildDirectory, project); + + assertEquals(Path.fromOSString(externalBuildDirectory), BuildDirectoryResolver.resolve(project, configuration)); + } + + @Test + void resolveBlankPathUsesProjectBuildDirectory() throws Exception + { + IProject project = mockProject(); + ILaunchConfiguration configuration = mockRunConfiguration(" ", project); //$NON-NLS-1$ + + assertEquals(project.getLocation().append("build"), BuildDirectoryResolver.resolve(project, configuration)); //$NON-NLS-1$ + } + + @Test + void resolveConfigurationForAnotherProjectUsesLegacyDirectory() throws Exception + { + IProject project = mockProject(); + ILaunchConfiguration configuration = mockRunConfiguration("other-build", mock(IProject.class)); //$NON-NLS-1$ + + assertEquals(project.getLocation().append("legacy-build"), //$NON-NLS-1$ + BuildDirectoryResolver.resolve(project, configuration)); + } + + @Test + void resolveConfigurationWithoutMappedResourcesUsesLegacyDirectory() throws Exception + { + IProject project = mockProject(); + ILaunchConfiguration configuration = mockRunConfiguration("unmapped-build", null); //$NON-NLS-1$ + + assertEquals(project.getLocation().append("legacy-build"), //$NON-NLS-1$ + BuildDirectoryResolver.resolve(project, configuration)); + } + + @Test + void resolveIgnoresLegacyDirectoryLeftInsideRenamedProject() throws Exception + { + IProject project = mockProject(); + String renamedAwayDirectory = new File(tempDirectory.getParentFile(), "OldProjectName/build") //$NON-NLS-1$ + .getAbsolutePath(); + when(project.getPersistentProperty(any())).thenReturn(renamedAwayDirectory); + ILaunchConfiguration configuration = mockRunConfiguration("build-release", mock(IProject.class)); //$NON-NLS-1$ + + assertEquals(project.getLocation().append("build"), //$NON-NLS-1$ + BuildDirectoryResolver.resolve(project, configuration)); + } + + @Test + void resolveKeepsLegacyDirectoryThatStillExistsOutsideProject() throws Exception + { + IProject project = mockProject(); + File externalDirectory = new File(tempDirectory.getParentFile(), "external-legacy-build"); //$NON-NLS-1$ + externalDirectory.mkdirs(); + when(project.getPersistentProperty(any())).thenReturn(externalDirectory.getAbsolutePath()); + ILaunchConfiguration configuration = mockRunConfiguration("build-release", mock(IProject.class)); //$NON-NLS-1$ + + try + { + assertEquals(Path.fromOSString(externalDirectory.getAbsolutePath()), + BuildDirectoryResolver.resolve(project, configuration)); + } + finally + { + externalDirectory.delete(); + } + } + + private IProject mockProject() throws Exception + { + IProject project = mock(IProject.class); + when(project.getLocation()).thenReturn(Path.fromOSString(tempDirectory.getAbsolutePath())); + when(project.getPersistentProperty(any())).thenReturn("legacy-build"); //$NON-NLS-1$ + return project; + } + + /** + * @param mappedProject project the configuration is mapped to, or null to simulate a configuration + * carrying no mapped resource + */ + private ILaunchConfiguration mockRunConfiguration(String buildDirectory, IProject mappedProject) throws Exception + { + ILaunchConfiguration configuration = mock(ILaunchConfiguration.class); + ILaunchConfigurationType type = mock(ILaunchConfigurationType.class); + when(configuration.getType()).thenReturn(type); + when(type.getIdentifier()).thenReturn(IDFLaunchConstants.RUN_LAUNCH_CONFIG_TYPE); + when(configuration.getAttribute(IDFLaunchConstants.BUILD_FOLDER_PATH, "")).thenReturn(buildDirectory); //$NON-NLS-1$ + + if (mappedProject != null) + { + IResource mappedResource = mock(IResource.class); + when(mappedResource.getProject()).thenReturn(mappedProject); + when(configuration.getMappedResources()).thenReturn(new IResource[] { mappedResource }); + } + + return configuration; + } +} diff --git a/tests/com.espressif.idf.core.test/src/com/espressif/idf/core/util/test/LaunchUtilBoundConfigurationTest.java b/tests/com.espressif.idf.core.test/src/com/espressif/idf/core/util/test/LaunchUtilBoundConfigurationTest.java new file mode 100644 index 000000000..fde53dc5d --- /dev/null +++ b/tests/com.espressif.idf.core.test/src/com/espressif/idf/core/util/test/LaunchUtilBoundConfigurationTest.java @@ -0,0 +1,105 @@ +package com.espressif.idf.core.util.test; + +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchConfigurationType; +import org.eclipse.debug.core.ILaunchManager; +import org.junit.jupiter.api.Test; + +import com.espressif.idf.core.build.IDFLaunchConstants; +import com.espressif.idf.core.util.LaunchUtil; + +class LaunchUtilBoundConfigurationTest +{ + @Test + void getBoundConfigurationReturnsNamedRunConfiguration() throws Exception + { + ILaunchManager launchManager = mock(ILaunchManager.class); + ILaunchConfigurationType runType = mock(ILaunchConfigurationType.class); + ILaunchConfiguration debugConfiguration = mock(ILaunchConfiguration.class); + ILaunchConfiguration expected = mock(ILaunchConfiguration.class); + ILaunchConfiguration other = mock(ILaunchConfiguration.class); + when(debugConfiguration.getAttribute(IDFLaunchConstants.ATTR_LAUNCH_CONFIGURATION_NAME, "")) //$NON-NLS-1$ + .thenReturn("release"); //$NON-NLS-1$ + when(expected.getName()).thenReturn("release"); //$NON-NLS-1$ + when(other.getName()).thenReturn("debug"); //$NON-NLS-1$ + when(launchManager.getLaunchConfigurationType(IDFLaunchConstants.RUN_LAUNCH_CONFIG_TYPE)).thenReturn(runType); + when(launchManager.getLaunchConfigurations(runType)) + .thenReturn(new ILaunchConfiguration[] { other, expected }); + + assertSame(expected, new LaunchUtil(launchManager).getBoundConfiguration(debugConfiguration)); + } + + @Test + void getBoundConfigurationFallsBackOnlyToConfigurationFromSameProject() throws Exception + { + ILaunchManager launchManager = mock(ILaunchManager.class); + ILaunchConfigurationType runType = mock(ILaunchConfigurationType.class); + ILaunchConfiguration debugConfiguration = mock(ILaunchConfiguration.class); + ILaunchConfiguration expected = mock(ILaunchConfiguration.class); + ILaunchConfiguration other = mock(ILaunchConfiguration.class); + IProject project = mock(IProject.class); + IProject otherProject = mock(IProject.class); + when(debugConfiguration.getAttribute(IDFLaunchConstants.ATTR_LAUNCH_CONFIGURATION_NAME, "")) //$NON-NLS-1$ + .thenReturn("missing"); //$NON-NLS-1$ + when(expected.getName()).thenReturn("release"); //$NON-NLS-1$ + when(other.getName()).thenReturn("debug"); //$NON-NLS-1$ + when(launchManager.getLaunchConfigurationType(IDFLaunchConstants.RUN_LAUNCH_CONFIG_TYPE)).thenReturn(runType); + when(launchManager.getLaunchConfigurations(runType)) + .thenReturn(new ILaunchConfiguration[] { other, expected }); + + mapToProject(debugConfiguration, project); + mapToProject(other, otherProject); + mapToProject(expected, project); + + assertSame(expected, new LaunchUtil(launchManager).getBoundConfiguration(debugConfiguration)); + } + + @Test + void getBoundConfigurationReturnsDebugConfigurationWhenNoProjectRunConfigurationExists() throws Exception + { + ILaunchManager launchManager = mock(ILaunchManager.class); + ILaunchConfigurationType runType = mock(ILaunchConfigurationType.class); + ILaunchConfiguration debugConfiguration = mock(ILaunchConfiguration.class); + when(debugConfiguration.getAttribute(IDFLaunchConstants.ATTR_LAUNCH_CONFIGURATION_NAME, "")) //$NON-NLS-1$ + .thenReturn("missing"); //$NON-NLS-1$ + when(launchManager.getLaunchConfigurationType(IDFLaunchConstants.RUN_LAUNCH_CONFIG_TYPE)).thenReturn(runType); + when(launchManager.getLaunchConfigurations(runType)).thenReturn(new ILaunchConfiguration[0]); + + assertSame(debugConfiguration, new LaunchUtil(launchManager).getBoundConfiguration(debugConfiguration)); + } + + @Test + void getBoundConfigurationIgnoresRunConfigurationWithoutMappedResources() throws Exception + { + ILaunchManager launchManager = mock(ILaunchManager.class); + ILaunchConfigurationType runType = mock(ILaunchConfigurationType.class); + ILaunchConfiguration debugConfiguration = mock(ILaunchConfiguration.class); + ILaunchConfiguration unmapped = mock(ILaunchConfiguration.class); + ILaunchConfiguration expected = mock(ILaunchConfiguration.class); + IProject project = mock(IProject.class); + when(debugConfiguration.getAttribute(IDFLaunchConstants.ATTR_LAUNCH_CONFIGURATION_NAME, "")) //$NON-NLS-1$ + .thenReturn("missing"); //$NON-NLS-1$ + when(unmapped.getName()).thenReturn("unmapped"); //$NON-NLS-1$ + when(expected.getName()).thenReturn("release"); //$NON-NLS-1$ + when(launchManager.getLaunchConfigurationType(IDFLaunchConstants.RUN_LAUNCH_CONFIG_TYPE)).thenReturn(runType); + when(launchManager.getLaunchConfigurations(runType)) + .thenReturn(new ILaunchConfiguration[] { unmapped, expected }); + mapToProject(debugConfiguration, project); + mapToProject(expected, project); + + assertSame(expected, new LaunchUtil(launchManager).getBoundConfiguration(debugConfiguration)); + } + + private static void mapToProject(ILaunchConfiguration configuration, IProject project) throws Exception + { + IResource resource = mock(IResource.class); + when(resource.getProject()).thenReturn(project); + when(configuration.getMappedResources()).thenReturn(new IResource[] { resource }); + } +} diff --git a/tests/com.espressif.idf.core.test/src/com/espressif/idf/core/util/test/SDKConfigUtilTest.java b/tests/com.espressif.idf.core.test/src/com/espressif/idf/core/util/test/SDKConfigUtilTest.java new file mode 100644 index 000000000..f9050bcc4 --- /dev/null +++ b/tests/com.espressif.idf.core.test/src/com/espressif/idf/core/util/test/SDKConfigUtilTest.java @@ -0,0 +1,40 @@ +package com.espressif.idf.core.util.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.io.File; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import com.espressif.idf.core.IDFConstants; +import com.espressif.idf.core.util.SDKConfigUtil; + +class SDKConfigUtilTest +{ + @TempDir + File tempDirectory; + + @Test + void getConfigMenuFilePathUsesExplicitBuildDirectory() throws Exception + { + String expected = new File(new File(tempDirectory, IDFConstants.CONFIG_FOLDER), + IDFConstants.KCONFIG_MENUS_JSON).getAbsolutePath(); + + String actual = new SDKConfigUtil().getConfigMenuFilePath(tempDirectory.getAbsolutePath()); + + assertEquals(expected, actual); + } + + @Test + void getConfigMenuFilePathRejectsMissingBuildDirectory() + { + File missingDirectory = new File(tempDirectory, "missing"); //$NON-NLS-1$ + + Exception exception = assertThrows(Exception.class, + () -> new SDKConfigUtil().getConfigMenuFilePath(missingDirectory.getAbsolutePath())); + + assertEquals("Build directory is not found: " + missingDirectory.getAbsolutePath(), exception.getMessage()); //$NON-NLS-1$ + } +} diff --git a/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/executable/cases/launchconfiguration/LaunchBarCDTConfigurationsTest.java b/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/executable/cases/launchconfiguration/LaunchBarCDTConfigurationsTest.java index 0ee44fbc3..be2a917a5 100644 --- a/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/executable/cases/launchconfiguration/LaunchBarCDTConfigurationsTest.java +++ b/tests/com.espressif.idf.ui.test/src/com/espressif/idf/ui/test/executable/cases/launchconfiguration/LaunchBarCDTConfigurationsTest.java @@ -11,6 +11,10 @@ import java.io.IOException; import java.util.Arrays; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.launchbar.core.ILaunchBarManager; import org.eclipse.launchbar.core.ILaunchDescriptor; import org.eclipse.launchbar.core.internal.Activator; @@ -27,6 +31,8 @@ import org.junit.runner.RunWith; import org.junit.runners.MethodSorters; +import com.espressif.idf.core.build.IDFLaunchConstants; +import com.espressif.idf.core.util.IDFUtil; import com.espressif.idf.ui.test.common.WorkBenchSWTBot; import com.espressif.idf.ui.test.common.utility.TestWidgetWaitUtility; import com.espressif.idf.ui.test.operations.EnvSetupOperations; @@ -80,6 +86,25 @@ public void creatingNewEspLaunchTarget() Fixture.thenLaunchTargetIsSelectedFromLaunchTargets("TestESP"); } + @Test + public void verifyCustomBuildFolderFollowsSelectedLaunchConfiguration() throws Exception + { + Fixture.whenActiveLaunchConfigurationUsesBuildFolder("build-release"); //$NON-NLS-1$ + Fixture.thenResolvedBuildFolderIs("build-release"); //$NON-NLS-1$ + Fixture.whenLaunchConfigurationIsDuplicatedWithBuildFolder("TestProject-dev", "build-dev"); //$NON-NLS-1$ //$NON-NLS-2$ + try + { + Fixture.whenLaunchConfigurationIsSelected("TestProject-dev"); //$NON-NLS-1$ + Fixture.thenResolvedBuildFolderIs("build-dev"); //$NON-NLS-1$ + Fixture.whenLaunchConfigurationIsSelected("TestProject"); //$NON-NLS-1$ + Fixture.thenResolvedBuildFolderIs("build-release"); //$NON-NLS-1$ + } + finally + { + Fixture.deleteDuplicatedLaunchConfiguration(); + } + } + private static class Fixture { private static SWTWorkbenchBot bot; @@ -89,6 +114,7 @@ private static class Fixture private static String projectName; private static LaunchBarConfigSelector launchBarConfigSelector; private static LaunchBarTargetSelector launchBarTargetSelector; + private static ILaunchConfiguration duplicateConfiguration; private static final ILaunchTargetManager targetManager = Activator.getService(ILaunchTargetManager.class); private static final ILaunchBarManager manager = Activator.getService(ILaunchBarManager.class); @@ -181,6 +207,45 @@ private static void whenProjectIsBuiltUsingToolbarButton() throws IOException // TestWidgetWaitUtility.waitForOperationsInProgressToFinish(bot); } + private static void whenActiveLaunchConfigurationUsesBuildFolder(String buildFolder) throws Exception + { + ILaunchConfiguration configuration = manager.getActiveLaunchConfiguration(); + ILaunchConfigurationWorkingCopy workingCopy = configuration.getWorkingCopy(); + workingCopy.setAttribute(IDFLaunchConstants.BUILD_FOLDER_PATH, buildFolder); + workingCopy.doSave(); + } + + private static void whenLaunchConfigurationIsDuplicatedWithBuildFolder(String name, String buildFolder) + throws Exception + { + ILaunchConfiguration configuration = manager.getActiveLaunchConfiguration(); + ILaunchConfigurationWorkingCopy workingCopy = configuration.copy(name); + workingCopy.setAttribute(IDFLaunchConstants.BUILD_FOLDER_PATH, buildFolder); + duplicateConfiguration = workingCopy.doSave(); + TestWidgetWaitUtility.waitForOperationsInProgressToFinishSync(bot); + } + + private static void whenLaunchConfigurationIsSelected(String name) + { + launchBarConfigSelector.select(name); + bot.sleep(1000); + } + + private static void deleteDuplicatedLaunchConfiguration() throws Exception + { + if (duplicateConfiguration != null && duplicateConfiguration.exists()) + { + duplicateConfiguration.delete(); + duplicateConfiguration = null; + } + } + + private static void thenResolvedBuildFolderIs(String buildFolder) throws Exception + { + IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName); + assertEquals(project.getLocation().append(buildFolder).toOSString(), IDFUtil.getBuildDir(project)); + } + public static void givenProjectNameIs(String projectName) { Fixture.projectName = projectName; From a4a9a222acc94cb81208a51d536983afb7a4eb52 Mon Sep 17 00:00:00 2001 From: Denys Almazov Date: Mon, 14 Sep 2026 16:05:54 +0300 Subject: [PATCH 2/4] fix: adding the extension point that handles rename --- bundles/com.espressif.idf.ui/plugin.xml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/bundles/com.espressif.idf.ui/plugin.xml b/bundles/com.espressif.idf.ui/plugin.xml index 693973a41..acc0fe87a 100644 --- a/bundles/com.espressif.idf.ui/plugin.xml +++ b/bundles/com.espressif.idf.ui/plugin.xml @@ -881,6 +881,17 @@ triggerCharacters=" "> + + + + + + + Date: Tue, 15 Sep 2026 18:27:00 +0300 Subject: [PATCH 3/4] fix: fix change target issue --- .../idf/core/build/BuildDirectoryResolver.java | 17 ++++++++++++++++- .../idf/core/util/SDKConfigJsonReader.java | 15 ++++++++++++++- .../espressif/idf/core/util/SDKConfigUtil.java | 17 +++++++++++++---- .../com/espressif/idf/ui/LaunchBarListener.java | 9 ++++++--- 4 files changed, 49 insertions(+), 9 deletions(-) diff --git a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/build/BuildDirectoryResolver.java b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/build/BuildDirectoryResolver.java index dbf959df9..4e7e79f73 100644 --- a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/build/BuildDirectoryResolver.java +++ b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/build/BuildDirectoryResolver.java @@ -4,6 +4,7 @@ *******************************************************************************/ package com.espressif.idf.core.build; +import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; @@ -85,7 +86,21 @@ private static ILaunchConfiguration resolveBuildConfiguration(ILaunchConfigurati private static boolean belongsToProject(ILaunchConfiguration configuration, IProject project) throws CoreException { - return project != null && project.equals(LaunchUtil.getMappedProject(configuration)); + if (project == null || configuration == null) + { + return false; + } + + IProject mappedProject = LaunchUtil.getMappedProject(configuration); + if (mappedProject != null) + { + return project.equals(mappedProject); + } + + // A configuration may carry only the project name, for instance while the Launch Bar is switching targets. + // Without this the resolver would silently fall back to the default build folder. + return project.getName().equals( + configuration.getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROJECT_NAME, StringUtil.EMPTY)); } private static IPath resolvePath(IProject project, String buildFolder) diff --git a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/SDKConfigJsonReader.java b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/SDKConfigJsonReader.java index f7e6357eb..772869608 100644 --- a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/SDKConfigJsonReader.java +++ b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/SDKConfigJsonReader.java @@ -23,10 +23,21 @@ public class SDKConfigJsonReader { private IProject project; + private String buildDirectory; public SDKConfigJsonReader(IProject project) + { + this(project, null); + } + + /** + * @param project project owning the configuration + * @param buildDirectory build directory to read from, or null to resolve the project's active one + */ + public SDKConfigJsonReader(IProject project, String buildDirectory) { this.project = project; + this.buildDirectory = buildDirectory; } /** @@ -54,7 +65,9 @@ public String getValue(String key) protected JSONObject read() throws Exception { - String sdkconfigJsonPath = new SDKConfigUtil().getSDKConfigJsonFilePath(project); + String sdkconfigJsonPath = StringUtil.isEmpty(buildDirectory) + ? new SDKConfigUtil().getSDKConfigJsonFilePath(project) + : new SDKConfigUtil().getSDKConfigJsonFilePath(buildDirectory); if (!new File(sdkconfigJsonPath).exists()) { Logger.log(MessageFormat.format("sdkconfig.json file could not find {0}", sdkconfigJsonPath)); //$NON-NLS-1$ diff --git a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/SDKConfigUtil.java b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/SDKConfigUtil.java index b9c2ff911..c689a48d5 100644 --- a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/SDKConfigUtil.java +++ b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/SDKConfigUtil.java @@ -51,12 +51,21 @@ public String getConfigMenuFilePath(String buildDirectory) throws Exception */ public String getSDKConfigJsonFilePath(IProject project) throws Exception { - String buildDir = IDFUtil.getBuildDir(project); - if (!new File(buildDir).exists()) + return getSDKConfigJsonFilePath(IDFUtil.getBuildDir(project)); + } + + /** + * @param buildDirectory + * @return + * @throws Exception + */ + public String getSDKConfigJsonFilePath(String buildDirectory) throws Exception + { + if (!new File(buildDirectory).exists()) { - throw new Exception("Build directory is not found: "+ buildDir); //$NON-NLS-1$ + throw new Exception("Build directory is not found: " + buildDirectory); //$NON-NLS-1$ } - return new File(new File(buildDir, IDFConstants.CONFIG_FOLDER), IDFConstants.SDKCONFIG_JSON_FILE_NAME) + return new File(new File(buildDirectory, IDFConstants.CONFIG_FOLDER), IDFConstants.SDKCONFIG_JSON_FILE_NAME) .getAbsolutePath(); } } diff --git a/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/LaunchBarListener.java b/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/LaunchBarListener.java index 4290fdd10..3cca9d706 100644 --- a/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/LaunchBarListener.java +++ b/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/LaunchBarListener.java @@ -33,9 +33,9 @@ import com.espressif.idf.core.IDFCorePlugin; import com.espressif.idf.core.LaunchBarTargetConstants; +import com.espressif.idf.core.build.BuildDirectoryResolver; import com.espressif.idf.core.build.IDFLaunchConstants; import com.espressif.idf.core.logging.Logger; -import com.espressif.idf.core.util.IDFUtil; import com.espressif.idf.core.util.SDKConfigJsonReader; import com.espressif.idf.core.util.StringUtil; @@ -130,11 +130,14 @@ private void update(String newTarget) // build folder exist? if (project != null) { - File buildLocation = new File(IDFUtil.getBuildDir((IProject) project)); + // Resolve from the configuration already in hand: asking the Launch Bar again while it is + // switching targets can fall back to the default folder and hide the prompt (IEP-1521). + File buildLocation = BuildDirectoryResolver.resolve((IProject) project, activeConfig).toFile(); if (buildLocation.exists()) { // get current target - String currentTarget = new SDKConfigJsonReader((IProject) project).getValue("IDF_TARGET"); //$NON-NLS-1$ + String currentTarget = new SDKConfigJsonReader((IProject) project, + buildLocation.getAbsolutePath()).getValue("IDF_TARGET"); //$NON-NLS-1$ // If both are not same if (currentTarget != null && !newTarget.equals(currentTarget)) From a6c63871344b3cf5a2d21f67c5fc39d1f6b294b9 Mon Sep 17 00:00:00 2001 From: Denys Almazov Date: Wed, 16 Sep 2026 10:27:01 +0300 Subject: [PATCH 4/4] revert: revert the change target issue fix This reverts commit 5f60bce58f0f7bd0f29d81e27e0e5eaf7751058f. --- .../idf/core/build/BuildDirectoryResolver.java | 17 +---------------- .../idf/core/util/SDKConfigJsonReader.java | 15 +-------------- .../espressif/idf/core/util/SDKConfigUtil.java | 17 ++++------------- .../com/espressif/idf/ui/LaunchBarListener.java | 9 +++------ 4 files changed, 9 insertions(+), 49 deletions(-) diff --git a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/build/BuildDirectoryResolver.java b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/build/BuildDirectoryResolver.java index 4e7e79f73..dbf959df9 100644 --- a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/build/BuildDirectoryResolver.java +++ b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/build/BuildDirectoryResolver.java @@ -4,7 +4,6 @@ *******************************************************************************/ package com.espressif.idf.core.build; -import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; @@ -86,21 +85,7 @@ private static ILaunchConfiguration resolveBuildConfiguration(ILaunchConfigurati private static boolean belongsToProject(ILaunchConfiguration configuration, IProject project) throws CoreException { - if (project == null || configuration == null) - { - return false; - } - - IProject mappedProject = LaunchUtil.getMappedProject(configuration); - if (mappedProject != null) - { - return project.equals(mappedProject); - } - - // A configuration may carry only the project name, for instance while the Launch Bar is switching targets. - // Without this the resolver would silently fall back to the default build folder. - return project.getName().equals( - configuration.getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROJECT_NAME, StringUtil.EMPTY)); + return project != null && project.equals(LaunchUtil.getMappedProject(configuration)); } private static IPath resolvePath(IProject project, String buildFolder) diff --git a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/SDKConfigJsonReader.java b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/SDKConfigJsonReader.java index 772869608..f7e6357eb 100644 --- a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/SDKConfigJsonReader.java +++ b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/SDKConfigJsonReader.java @@ -23,21 +23,10 @@ public class SDKConfigJsonReader { private IProject project; - private String buildDirectory; public SDKConfigJsonReader(IProject project) - { - this(project, null); - } - - /** - * @param project project owning the configuration - * @param buildDirectory build directory to read from, or null to resolve the project's active one - */ - public SDKConfigJsonReader(IProject project, String buildDirectory) { this.project = project; - this.buildDirectory = buildDirectory; } /** @@ -65,9 +54,7 @@ public String getValue(String key) protected JSONObject read() throws Exception { - String sdkconfigJsonPath = StringUtil.isEmpty(buildDirectory) - ? new SDKConfigUtil().getSDKConfigJsonFilePath(project) - : new SDKConfigUtil().getSDKConfigJsonFilePath(buildDirectory); + String sdkconfigJsonPath = new SDKConfigUtil().getSDKConfigJsonFilePath(project); if (!new File(sdkconfigJsonPath).exists()) { Logger.log(MessageFormat.format("sdkconfig.json file could not find {0}", sdkconfigJsonPath)); //$NON-NLS-1$ diff --git a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/SDKConfigUtil.java b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/SDKConfigUtil.java index c689a48d5..b9c2ff911 100644 --- a/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/SDKConfigUtil.java +++ b/bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/SDKConfigUtil.java @@ -51,21 +51,12 @@ public String getConfigMenuFilePath(String buildDirectory) throws Exception */ public String getSDKConfigJsonFilePath(IProject project) throws Exception { - return getSDKConfigJsonFilePath(IDFUtil.getBuildDir(project)); - } - - /** - * @param buildDirectory - * @return - * @throws Exception - */ - public String getSDKConfigJsonFilePath(String buildDirectory) throws Exception - { - if (!new File(buildDirectory).exists()) + String buildDir = IDFUtil.getBuildDir(project); + if (!new File(buildDir).exists()) { - throw new Exception("Build directory is not found: " + buildDirectory); //$NON-NLS-1$ + throw new Exception("Build directory is not found: "+ buildDir); //$NON-NLS-1$ } - return new File(new File(buildDirectory, IDFConstants.CONFIG_FOLDER), IDFConstants.SDKCONFIG_JSON_FILE_NAME) + return new File(new File(buildDir, IDFConstants.CONFIG_FOLDER), IDFConstants.SDKCONFIG_JSON_FILE_NAME) .getAbsolutePath(); } } diff --git a/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/LaunchBarListener.java b/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/LaunchBarListener.java index 3cca9d706..4290fdd10 100644 --- a/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/LaunchBarListener.java +++ b/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/LaunchBarListener.java @@ -33,9 +33,9 @@ import com.espressif.idf.core.IDFCorePlugin; import com.espressif.idf.core.LaunchBarTargetConstants; -import com.espressif.idf.core.build.BuildDirectoryResolver; import com.espressif.idf.core.build.IDFLaunchConstants; import com.espressif.idf.core.logging.Logger; +import com.espressif.idf.core.util.IDFUtil; import com.espressif.idf.core.util.SDKConfigJsonReader; import com.espressif.idf.core.util.StringUtil; @@ -130,14 +130,11 @@ private void update(String newTarget) // build folder exist? if (project != null) { - // Resolve from the configuration already in hand: asking the Launch Bar again while it is - // switching targets can fall back to the default folder and hide the prompt (IEP-1521). - File buildLocation = BuildDirectoryResolver.resolve((IProject) project, activeConfig).toFile(); + File buildLocation = new File(IDFUtil.getBuildDir((IProject) project)); if (buildLocation.exists()) { // get current target - String currentTarget = new SDKConfigJsonReader((IProject) project, - buildLocation.getAbsolutePath()).getValue("IDF_TARGET"); //$NON-NLS-1$ + String currentTarget = new SDKConfigJsonReader((IProject) project).getValue("IDF_TARGET"); //$NON-NLS-1$ // If both are not same if (currentTarget != null && !newTarget.equals(currentTarget))