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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down Expand Up @@ -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))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -548,14 +549,7 @@ private static String runCommand(List<String> arguments, Map<String, String> 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();
}

/**
Expand All @@ -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),
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -41,18 +38,52 @@ public ILaunchConfiguration findAppropriateLaunchConfig(ILaunchDescriptor descri
return null;
}

/**
* Returns the project a launch configuration is mapped to, or <code>null</code> 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
*/
public ILaunchConfiguration getBoundConfiguration(ILaunchConfiguration configuration) throws CoreException
{
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;

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)
{
Expand All @@ -54,7 +67,7 @@ private String getAppElfFileName()

return appElfFileName;
}

public String getIdfPath()
{
String idfPath = StringUtil.EMPTY;
Expand Down
Loading
Loading