1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-03-28 14:56:28 +01:00

Stops CMake build output folders being named "default" (#1084)

The default IBuildConfiguration is no longer used by projects that use
ICBuildConfigurationProvider.

For CMake, Makefile and other Core Build projects the build output
folder is sometimes named "default" rather than the pattern
toolName.launchMode.toolchain OS.toolchain Arch.launchTarget Id (eg:
cmake.debug.win32.x86_64.Local). PR #1076 exposes new API
(ICBuildConfigurationProvider.getCBuildConfigName) to encourage this
naming pattern.

The "sometimes" is variable and often happens when a project is first
created when the active launch target is Local and the launch mode is
"run", but not always. This gives a random, inconsistent impression to
CDT.

The Platform project always contains a IBuildConfiguration with the name
IBuildConfiguration.DEFAULT_CONFIG_NAME. It seems the original Core
Build system design went to some length to fit in with this and always
make use of this IBuildConfiguration when pairing it with a new
ICBuildConfiguration.

With this PR, this no longer happens, allowing CDT code to be simplified
and the build folder naming made consistent, always adhering to
ICBuildConfigurationProvider.getCBuildConfigName.

Addresses Issue: CDT CMake Improvements #1000, IDE-82683-REQ-024 Default
CMake build folder
This commit is contained in:
betamax 2025-02-20 01:55:50 +00:00 committed by GitHub
parent e819f8a35c
commit cf359d59ad
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 634 additions and 164 deletions

View file

@ -31,6 +31,12 @@ Added method allowing extenders to customize the Core Build output directory nam
#### org.eclipse.cdt.core.build.CBuildConfiguration
Removed field:
* org.eclipse.cdt.core.build.ICBuildConfiguration.DEFAULT_NAME
This constant was removed because the name "default" is no longer used to name Core Build configurations. Naming now follows the pattern in getCBuildConfigName, described above.
Removed method:
* org.eclipse.cdt.core.build.CBuildConfiguration.setLaunchMode(String)

View file

@ -14,30 +14,21 @@
package org.eclipse.cdt.core.autotools.core;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.cdt.core.autotools.core.internal.Activator;
import org.eclipse.cdt.core.build.CBuildConfigUtils;
import org.eclipse.cdt.core.build.ICBuildConfiguration;
import org.eclipse.cdt.core.build.ICBuildConfigurationManager;
import org.eclipse.cdt.core.build.ICBuildConfigurationProvider;
import org.eclipse.cdt.core.build.IToolChain;
import org.eclipse.cdt.core.build.IToolChainManager;
import org.eclipse.core.resources.IBuildConfiguration;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.launchbar.core.target.ILaunchTarget;
import org.eclipse.launchbar.core.target.ILaunchTargetManager;
public class AutotoolsBuildConfigurationProvider implements ICBuildConfigurationProvider {
public static final String ID = Activator.PLUGIN_ID + ".provider"; //$NON-NLS-1$
private final ILaunchTargetManager launchTargetManager = Activator.getService(ILaunchTargetManager.class);
@Override
public String getId() {
return ID;
@ -46,31 +37,10 @@ public class AutotoolsBuildConfigurationProvider implements ICBuildConfiguration
@Override
public ICBuildConfiguration getCBuildConfiguration(IBuildConfiguration config, String name) throws CoreException {
if (config.getName().equals(IBuildConfiguration.DEFAULT_CONFIG_NAME)) {
IToolChain toolChain = null;
// try the toolchain for the local target
Map<String, String> properties = new HashMap<>();
properties.put(IToolChain.ATTR_OS, Platform.getOS());
properties.put(IToolChain.ATTR_ARCH, Platform.getOSArch());
IToolChainManager toolChainManager = Activator.getService(IToolChainManager.class);
for (IToolChain tc : toolChainManager.getToolChainsMatching(properties)) {
toolChain = tc;
break;
}
// local didn't work, try and find one that does
if (toolChain == null) {
for (IToolChain tc : toolChainManager.getToolChainsMatching(new HashMap<>())) {
toolChain = tc;
break;
}
}
if (toolChain != null) {
return new AutotoolsBuildConfiguration(config, name, toolChain, "run", //$NON-NLS-1$
launchTargetManager.getLocalLaunchTarget());
}
// No valid combinations
/*
* IBuildConfiguration configs with name IBuildConfiguration.DEFAULT_CONFIG_NAME
* are not supported to avoid build output directory being named "default".
*/
return null;
}
AutotoolsBuildConfiguration autotoolsConfig = new AutotoolsBuildConfiguration(config, name);

View file

@ -10,31 +10,23 @@
*******************************************************************************/
package org.eclipse.cdt.make.core;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.cdt.core.build.CBuildConfigUtils;
import org.eclipse.cdt.core.build.ICBuildConfiguration;
import org.eclipse.cdt.core.build.ICBuildConfigurationManager;
import org.eclipse.cdt.core.build.ICBuildConfigurationProvider;
import org.eclipse.cdt.core.build.IToolChain;
import org.eclipse.cdt.core.build.IToolChainManager;
import org.eclipse.cdt.core.build.StandardBuildConfiguration;
import org.eclipse.core.resources.IBuildConfiguration;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.launchbar.core.target.ILaunchTarget;
import org.eclipse.launchbar.core.target.ILaunchTargetManager;
/**
* @since 7.4
*/
public class MakefileBuildConfigurationProvider implements ICBuildConfigurationProvider {
public static final String ID = "org.eclipse.cdt.make.core.provider"; //$NON-NLS-1$
private final ILaunchTargetManager launchTargetManager = MakeCorePlugin.getService(ILaunchTargetManager.class);
@Override
public String getId() {
@ -44,33 +36,11 @@ public class MakefileBuildConfigurationProvider implements ICBuildConfigurationP
@Override
public ICBuildConfiguration getCBuildConfiguration(IBuildConfiguration config, String name) throws CoreException {
if (config.getName().equals(IBuildConfiguration.DEFAULT_CONFIG_NAME)) {
IToolChain toolChain = null;
// try the toolchain for the local target
Map<String, String> properties = new HashMap<>();
properties.put(IToolChain.ATTR_OS, Platform.getOS());
properties.put(IToolChain.ATTR_ARCH, Platform.getOSArch());
IToolChainManager toolChainManager = MakeCorePlugin.getService(IToolChainManager.class);
for (IToolChain tc : toolChainManager.getToolChainsMatching(properties)) {
toolChain = tc;
break;
}
// local didn't work, try and find one that does
if (toolChain == null) {
for (IToolChain tc : toolChainManager.getToolChainsMatching(new HashMap<>())) {
toolChain = tc;
break;
}
}
if (toolChain != null) {
return new StandardBuildConfiguration(config, name, toolChain, "run", //$NON-NLS-1$
launchTargetManager.getLocalLaunchTarget());
} else {
// No valid combinations
return null;
}
/*
* IBuildConfiguration configs with name IBuildConfiguration.DEFAULT_CONFIG_NAME
* are not supported to avoid build output directory being named "default".
*/
return null;
}
return new StandardBuildConfiguration(config, name);
}

View file

@ -22,7 +22,6 @@ import org.eclipse.cdt.core.build.ICBuildConfiguration;
import org.eclipse.cdt.core.build.ICBuildConfigurationManager;
import org.eclipse.cdt.core.build.ICBuildConfigurationProvider;
import org.eclipse.cdt.core.build.IToolChain;
import org.eclipse.cdt.core.build.IToolChainManager;
import org.eclipse.cdt.meson.core.Activator;
import org.eclipse.cdt.meson.core.IMesonToolChainFile;
import org.eclipse.cdt.meson.core.IMesonToolChainManager;
@ -30,15 +29,12 @@ import org.eclipse.core.resources.IBuildConfiguration;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.launchbar.core.target.ILaunchTarget;
import org.eclipse.launchbar.core.target.ILaunchTargetManager;
public class MesonBuildConfigurationProvider implements ICBuildConfigurationProvider {
public static final String ID = "org.eclipse.cdt.meson.core.provider"; //$NON-NLS-1$
private final static ILaunchTargetManager launchTargetManager = Activator.getService(ILaunchTargetManager.class);
private IMesonToolChainManager manager = Activator.getService(IMesonToolChainManager.class);
@Override
public String getId() {
@ -49,31 +45,10 @@ public class MesonBuildConfigurationProvider implements ICBuildConfigurationProv
public synchronized ICBuildConfiguration getCBuildConfiguration(IBuildConfiguration config, String name)
throws CoreException {
if (config.getName().equals(IBuildConfiguration.DEFAULT_CONFIG_NAME)) {
IToolChain toolChain = null;
// try the toolchain for the local target
Map<String, String> properties = new HashMap<>();
properties.put(IToolChain.ATTR_OS, Platform.getOS());
properties.put(IToolChain.ATTR_ARCH, Platform.getOSArch());
IToolChainManager toolChainManager = Activator.getService(IToolChainManager.class);
for (IToolChain tc : toolChainManager.getToolChainsMatching(properties)) {
toolChain = tc;
break;
}
// local didn't work, try and find one that does
if (toolChain == null) {
for (IToolChain tc : toolChainManager.getToolChainsMatching(new HashMap<>())) {
toolChain = tc;
break;
}
}
if (toolChain != null) {
return new MesonBuildConfiguration(config, name, toolChain, "run", //$NON-NLS-1$
launchTargetManager.getLocalLaunchTarget());
}
// No valid combinations
/*
* IBuildConfiguration configs with name IBuildConfiguration.DEFAULT_CONFIG_NAME
* are not supported to avoid build output directory being named "default".
*/
return null;
}
MesonBuildConfiguration mesonConfig = new MesonBuildConfiguration(config, name);
@ -85,6 +60,7 @@ public class MesonBuildConfigurationProvider implements ICBuildConfigurationProv
}
if (tcFile != null && !toolChain.equals(tcFile.getToolChain())) {
// toolchain changed
ILaunchTargetManager launchTargetManager = Activator.getService(ILaunchTargetManager.class);
return new MesonBuildConfiguration(config, name, tcFile.getToolChain(), tcFile, mesonConfig.getLaunchMode(),
launchTargetManager.getLocalLaunchTarget());
}
@ -95,6 +71,7 @@ public class MesonBuildConfigurationProvider implements ICBuildConfigurationProv
public ICBuildConfiguration createCBuildConfiguration(IProject project, IToolChain toolChain, String launchMode,
ILaunchTarget launchTarget, IProgressMonitor monitor) throws CoreException {
// get matching toolchain file if any
IMesonToolChainManager manager = Activator.getService(IMesonToolChainManager.class);
IMesonToolChainFile file = manager.getToolChainFileFor(toolChain);
if (file == null) {
Map<String, String> properties = new HashMap<>();

View file

@ -21,12 +21,10 @@ import org.eclipse.cdt.core.build.ICBuildConfiguration;
import org.eclipse.cdt.core.build.ICBuildConfigurationManager;
import org.eclipse.cdt.core.build.ICBuildConfigurationProvider;
import org.eclipse.cdt.core.build.IToolChain;
import org.eclipse.cdt.core.build.IToolChainManager;
import org.eclipse.core.resources.IBuildConfiguration;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.launchbar.core.target.ILaunchTarget;
import org.eclipse.launchbar.core.target.ILaunchTargetManager;
@ -76,35 +74,12 @@ public class CMakeBuildConfigurationProvider implements ICBuildConfigurationProv
@Override
public synchronized ICBuildConfiguration getCBuildConfiguration(IBuildConfiguration config, String name)
throws CoreException {
ILaunchTargetManager launchTargetManager = Activator.getService(ILaunchTargetManager.class);
if (config.getName().equals(IBuildConfiguration.DEFAULT_CONFIG_NAME)) {
IToolChain toolChain = null;
// try the toolchain for the local target
Map<String, String> properties = new HashMap<>();
properties.put(IToolChain.ATTR_OS, Platform.getOS());
properties.put(IToolChain.ATTR_ARCH, Platform.getOSArch());
IToolChainManager toolChainManager = Activator.getService(IToolChainManager.class);
for (IToolChain tc : toolChainManager.getToolChainsMatching(properties)) {
toolChain = tc;
break;
}
// local didn't work, try and find one that does
if (toolChain == null) {
for (IToolChain tc : toolChainManager.getToolChainsMatching(new HashMap<>())) {
toolChain = tc;
break;
}
}
if (toolChain != null) {
return createCMakeBuildConfiguration(config, name, toolChain, null, "run", //$NON-NLS-1$
launchTargetManager.getLocalLaunchTarget());
} else {
// No valid combinations
return null;
}
/*
* IBuildConfiguration configs with name IBuildConfiguration.DEFAULT_CONFIG_NAME
* are not supported to avoid build output directory being named "default".
*/
return null;
}
CMakeBuildConfiguration cmakeConfig = createCMakeBuildConfiguration(config, name);
ICMakeToolChainFile tcFile = cmakeConfig.getToolChainFile();
@ -115,6 +90,7 @@ public class CMakeBuildConfigurationProvider implements ICBuildConfigurationProv
}
if (tcFile != null && !toolChain.equals(tcFile.getToolChain())) {
// toolchain changed
ILaunchTargetManager launchTargetManager = Activator.getService(ILaunchTargetManager.class);
return createCMakeBuildConfiguration(config, name, tcFile.getToolChain(), tcFile,
cmakeConfig.getLaunchMode(), launchTargetManager.getLocalLaunchTarget());
} else {

View file

@ -47,7 +47,11 @@ Require-Bundle: org.eclipse.core.resources,
org.eclipse.cdt.debug.core,
org.eclipse.cdt.cmake.core,
org.eclipse.debug.core,
org.eclipse.launchbar.core;bundle-version="[3.0.0,4.0.0)"
org.eclipse.launchbar.core;bundle-version="[3.0.0,4.0.0)",
org.eclipse.cdt.make.core,
org.eclipse.cdt.meson.core,
org.eclipse.cdt.core.autotools.core,
org.mockito.mockito-core
Bundle-ActivationPolicy: lazy
Bundle-Vendor: %providerName
Bundle-RequiredExecutionEnvironment: JavaSE-17

View file

@ -0,0 +1,375 @@
/*******************************************************************************
* Copyright (c) 2025 Renesas Electronics Europe.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.cdt.core.build;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
import static org.mockito.ArgumentMatchers.anyMap;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.Collection;
import org.eclipse.cdt.cmake.core.CMakeBuildConfigurationProvider;
import org.eclipse.cdt.cmake.core.CMakeNature;
import org.eclipse.cdt.core.CCProjectNature;
import org.eclipse.cdt.core.CProjectNature;
import org.eclipse.cdt.core.autotools.core.AutotoolsBuildConfigurationProvider;
import org.eclipse.cdt.core.testplugin.ResourceHelper;
import org.eclipse.cdt.core.testplugin.util.BaseTestCase5;
import org.eclipse.cdt.debug.core.CDebugCorePlugin;
import org.eclipse.cdt.debug.core.launch.CoreBuildLaunchBarTracker;
import org.eclipse.cdt.internal.core.build.CBuildConfigurationManager;
import org.eclipse.cdt.internal.meson.core.MesonBuildConfigurationProvider;
import org.eclipse.cdt.make.core.MakefileBuildConfigurationProvider;
import org.eclipse.core.resources.IBuildConfiguration;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.launchbar.core.ILaunchBarManager;
import org.eclipse.launchbar.core.internal.LaunchBarManager;
import org.eclipse.launchbar.core.target.ILaunchTargetManager;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
/**
* Test for IDE-82683-REQ-024 #1084 part of #1000 in CDT 12.0.0. Test Part 1.
*
* Tests that a ICBuildConfiguration is never created with the default name.
*
* @see {@link DefaultBuildConfigurationNameTestsNoToolchains}
*
*/
public class DefaultBuildConfigurationNameTests extends BaseTestCase5 {
private static final String TC_BUILD_CONFIG_NAME_FRAGMENT = "MockToolchainBuildConfigName";
private IProject project;
private IToolChain mockToolchain;
private ICBuildConfigurationManager configManager = CDebugCorePlugin.getService(ICBuildConfigurationManager.class);
private ILaunchTargetManager launchTargetManager = CDebugCorePlugin.getService(ILaunchTargetManager.class);
private IToolChainManager toolchainManager = CDebugCorePlugin.getService(IToolChainManager.class);
private ILaunchBarManager launchBarManager = CDebugCorePlugin.getService(ILaunchBarManager.class);
@BeforeEach
public void setup() throws Exception {
/*
* Remove any discovered toolchains that happen to be installed on the host. This is because
* some test hosts may have a gcc installed and others not. So if we remove that gcc and do not
* rely on it and instead setup our mocked toolchain instead, the test conditions are easier to control.
*/
removeDiscoveredToolchains();
CBuildConfigurationManager cbm = (CBuildConfigurationManager) configManager;
cbm.reset();
/*
* Copied from LaunchBarManagerTest.startupTest()
* Make sure the manager starts up and defaults everything to null
*/
LaunchBarManager manager = new LaunchBarManager(false);
manager.init();
assertThat(manager.getActiveLaunchDescriptor(), is(nullValue()));
assertThat(manager.getActiveLaunchMode(), is(nullValue()));
assertThat(manager.getActiveLaunchTarget(), is(nullValue()));
/*
* Key to repeatedly running tests with the same initial conditions is to reset the
* CoreBuildLaunchBarTracker, so the launchbar controls are as they would be in a new
* workspace.
*/
CDebugCorePlugin.getDefault().resetCoreBuildLaunchBarTracker();
// Add a mocked toolchain, which acts like a gcc toolchain and has Platform related OS ans ARCH.
addMockToolchain();
// Create a CMake project
project = createCMakeProject();
waitForLaunchBarTracker();
}
/**
* This test describes the behaviour BEFORE IDE-82683-REQ-024 was fixed, hence why it is disabled.
*
* Project creation lifecycle:
*
* Project created:
* The project's active IBuildConfiguration defaults to DEFAULT_CONFIG_NAME.
* CModelManager.create(IFile, ICProject) where file==.project
* CModelManager.getBinaryParser(IProject) calls
* config.getAdapter(ICBuildConfiguration.class);
* CBuildConfigAdapterFactory.getAdapter(Object, Class<T>)
* CBuildConfigurationManager.getBuildConfiguration(IBuildConfiguration) with IBuildConfiguration==DEFAULT_CONFIG_NAME
*
* When IToolChainManager contains a valid toolchain:
* When CBuildConfigurationManager.getBuildConfiguration(IBuildConfiguration buildConfig) is called,
* the IBuildConfiguration.DEFAULT_CONFIG_NAME buildConfig is used to get the project's ICBuildConfigurationProvider.
*
* The provider.getCBuildConfiguration(buildConfig, configName) with configName="default" is called which creates
* a new ICBuildConfiguration. This is how the "first" config is often named "default".
*
* This test is disabled and retained for historical and documentation purposes to show the previous behaviour prior to
* fixing IDE-82683-REQ-024.
*/
@Test
@Disabled("This test is permanently disabled and retained for historical and documentation purposes to show"
+ " the previous behaviour prior to fixing IDE-82683-REQ-024.")
public void getBuildConfigurationDefaultName() throws Exception {
IBuildConfiguration buildConfig = project.getActiveBuildConfig();
assertThat(buildConfig.getName(), is(IBuildConfiguration.DEFAULT_CONFIG_NAME));
// This calls CBuildConfigurationManager.getBuildConfiguration(IBuildConfiguration)
ICBuildConfiguration iCBuildConfig = buildConfig.getAdapter(ICBuildConfiguration.class);
// Expected: the ICBuildConfiguration is associated with the default IBuildConfiguration
assertThat(iCBuildConfig.getBuildConfiguration(), is(buildConfig));
// Expected: the CBuildConfiguration has name "default".
CBuildConfiguration cBuildConfig = (CBuildConfiguration) iCBuildConfig;
assertThat(cBuildConfig.getName(), is("default")); // ICBuildConfiguration.DEFAULT_NAME
}
/**
* This test describes the behaviour AFTER IDE-82683-REQ-024 was fixed.
*
* Project creation and CoreBuildLaunchBarTracker lifecycle:
*
* When the project is being created and the .project file is created, the getBinaryParser(), using
* the projects IBuildConfiguration which defaults to DEFAULT_CONFIG_NAME, makes the getAdapter call:
*
* ICBuildConfiguration cconfig = config.getAdapter(ICBuildConfiguration.class);
*
* This eventually calls into:
*
* CBuildConfigurationManager.getBuildConfiguration(IBuildConfiguration)
*
* The first time into getBuildConfiguration, with the default IBuildConfiguration, execution falls through without
* creating an ICBuildConfiguration. When this happens, the default IBuildConfiguration is "quarantined" into the
* noConfigs "bin" so the default IBuildConfiguration is not used on subsequent calls.
*
* Callstack:
* CModelManager.create(IFile, ICProject) line: 364
* CModelManager.createBinaryFile(IFile) line: 679
* CModelManager.getBinaryParser(IProject) line: 619
* BuildConfiguration.getAdapter(Class<T>) line: 109
* ...
* CBuildConfigurationManager.getBuildConfiguration(IBuildConfiguration) line: 249
*
* Project creation continues in IProject.setDescription(...), eventually calling
* ILaunchBarManager.launchObjectChanged(Object) when the launchbar controls are updated
* with the new project contents; active launch descriptor, launch target and mode.This
* triggers CoreBuildLaunchBarTracker.setActiveBuildConfig(...) to fire.
*
* CoreBuildLaunchBarTracker is pivotal in creating new ICBuildConfiguration configs, according
* to the active launchbar controls.
*
* The CoreBuildLaunchBarTracker runs as a workspace Job and gets the project's IBuildConfiguration configs.
* At this point there is only the default IBuildConfiguration.
*
* The CoreBuildLaunchBarTracker calls into the CBuildConfigurationManager.getBuildConfiguration(IBuildConfiguration)
* to get the ICBuildConfiguration for this IBuildConfiguration. Because it's already been quarantined, it returns
* null.
*
* So the CoreBuildLaunchBarTracker requests the CBuildConfigurationManager to create a new ICBuildConfiguration
* by calling:
* CBuildConfigurationManager.getBuildConfiguration(IProject, IToolChain, String, ILaunchTarget, IProgressMonitor)
* This calls the project's ICBuildConfigurationProvider to create a new IBuildConfiguration/ICBuildConfiguration
* combination using:
*
* ICBuildConfigurationProvider.createCBuildConfiguration(IProject, IToolChain, String, ILaunchTarget, IProgressMonitor)
*
* The CoreBuildLaunchBarTracker finally sets the new ICBuildConfiguration as the active configuration.
*
* Callstack:
* Project.setDescription(IProjectDescription, IProgressMonitor) line: 1378
* ...
* LaunchBarManager.launchObjectChanged(Object) line: 398
* ...
* CoreBuildLaunchBarTracker.setActiveBuildConfig(ILaunchMode, ILaunchDescriptor, ILaunchTarget) line: 99
*/
@Test
public void getBuildConfigurationOneToolchainsActiveBuildConfig() throws Exception {
// Expected: the CoreBuildLaunchBarTracker has run and already set the correct active build config
IBuildConfiguration activeBuildConfig = project.getActiveBuildConfig();
assertThat(activeBuildConfig.getName(), is(not(IBuildConfiguration.DEFAULT_CONFIG_NAME)));
// Expected: cBuildConfigName=cmake.run.MockToolchainBuildConfigName.Local
final String expectedCBuildConfigName = "cmake." + ILaunchManager.RUN_MODE + //
"." + TC_BUILD_CONFIG_NAME_FRAGMENT + //
"." + ILaunchTargetManager.localLaunchTargetId;
// Expected: buildConfig name=org.eclipse.cdt.cmake.core.provider/cmake.run.MockToolchainBuildConfigName.Local
final String expectedBuildConfigName = CMakeBuildConfigurationProvider.ID + //
"/" + expectedCBuildConfigName;
assertThat(activeBuildConfig.getName(), is(expectedBuildConfigName));
// This calls CBuildConfigurationManager.getBuildConfiguration(IBuildConfiguration)
ICBuildConfiguration iCBuildConfig = activeBuildConfig.getAdapter(ICBuildConfiguration.class);
// Expected: the ICBuildConfiguration is associated with the active IBuildConfiguration
assertThat(iCBuildConfig.getBuildConfiguration(), is(activeBuildConfig));
CBuildConfiguration cBuildConfig = (CBuildConfiguration) iCBuildConfig;
assertThat(cBuildConfig.getName(), is(expectedCBuildConfigName));
}
/**
* Tests that ICBuildConfiguration configuration is never created with the default name when using
* CBuildConfigAdapterFactory.getAdapter(Object, Class<T>)
*
* Expect: returned ICBuildConfiguration is null.
*/
@Test
public void getBuildConfigurationOneToolchainsNonDefaultBuildConfig() throws Exception {
// Get the default build config from the project (it always has one)
IBuildConfiguration buildConfig = project.getBuildConfig(IBuildConfiguration.DEFAULT_CONFIG_NAME);
// This calls CBuildConfigurationManager.getBuildConfiguration(IBuildConfiguration)
ICBuildConfiguration cBuildConfig = buildConfig.getAdapter(ICBuildConfiguration.class);
assertThat(cBuildConfig, is(nullValue()));
}
/**
* Tests that ICBuildConfiguration configuration is never created with the default name when using
* ICBuildConfigurationManager.getBuildConfiguration(IBuildConfiguration)
*
* Expect: returned ICBuildConfiguration is null.
**/
@Test
public void getBuildConfiguration() throws Exception {
// Get the default build config from the project (it always has one)
IBuildConfiguration buildConfig = project.getBuildConfig(IBuildConfiguration.DEFAULT_CONFIG_NAME);
ICBuildConfiguration cBuildConfig = configManager.getBuildConfiguration(buildConfig);
assertThat(cBuildConfig, is(nullValue()));
}
/**
* Tests that CMake build configurations are never created with the default name.
*
* <p>Test summary:
* Get the project's default IBuildConfiguration.
* Use this and the name "default" as params and call the provider directly,
* ICBuildConfigurationProvider.getCBuildConfiguration(IBuildConfiguration, String).
* Expect: returned ICBuildConfiguration is null.
*/
@Test
public void cMakeBuildConfigurationProviderGetCBuildConfiguration() throws Exception {
ICBuildConfigurationProvider provider = new CMakeBuildConfigurationProvider();
// Get the default build config from the project (it always has one)
IBuildConfiguration buildConfig = project.getBuildConfig(IBuildConfiguration.DEFAULT_CONFIG_NAME);
ICBuildConfiguration cBuildConfig = provider.getCBuildConfiguration(buildConfig, "default");
assertThat(cBuildConfig, is(nullValue()));
}
/**
* Tests that Makefile build configurations are never created with the default name.
*
* @see "Test summary:" {@link #cMakeBuildConfigurationProviderGetCBuildConfiguration()}
*/
@Test
public void makefileBuildConfigurationProviderGetCBuildConfiguration() throws Exception {
ICBuildConfigurationProvider provider = new MakefileBuildConfigurationProvider();
// Get the default build config from the project (it always has one)
IBuildConfiguration buildConfig = project.getBuildConfig(IBuildConfiguration.DEFAULT_CONFIG_NAME);
ICBuildConfiguration cBuildConfig = provider.getCBuildConfiguration(buildConfig, "default");
assertThat(cBuildConfig, is(nullValue()));
}
/**
* Tests that Meson build configurations are never created with the default name.
*
* @see "Test summary:" {@link #cMakeBuildConfigurationProviderGetCBuildConfiguration()}
*/
@Test
public void mesonBuildConfigurationProviderGetCBuildConfiguration() throws Exception {
ICBuildConfigurationProvider provider = new MesonBuildConfigurationProvider();
// Get the default build config from the project (it always has one)
IBuildConfiguration buildConfig = project.getBuildConfig(IBuildConfiguration.DEFAULT_CONFIG_NAME);
ICBuildConfiguration cBuildConfig = provider.getCBuildConfiguration(buildConfig, "default");
assertThat(cBuildConfig, is(nullValue()));
}
/**
* Tests that Autotools build configurations are never created with the default name.
*
* @see "Test summary:" {@link #cMakeBuildConfigurationProviderGetCBuildConfiguration()}
*/
@Test
public void autotoolsBuildConfigurationProviderGetCBuildConfiguration() throws Exception {
ICBuildConfigurationProvider provider = new AutotoolsBuildConfigurationProvider();
// Get the default build config from the project (it always has one)
IBuildConfiguration buildConfig = project.getBuildConfig(IBuildConfiguration.DEFAULT_CONFIG_NAME);
ICBuildConfiguration cBuildConfig = provider.getCBuildConfiguration(buildConfig, "default");
assertThat(cBuildConfig, is(nullValue()));
}
// Test infrastructure...
private void waitForLaunchBarTracker() throws OperationCanceledException, InterruptedException {
Job.getJobManager().join(CoreBuildLaunchBarTracker.JOB_FAMILY_CORE_BUILD_LAUNCH_BAR_TRACKER, null);
}
private IProject createCMakeProject() throws Exception {
// Create a plain Eclipse project
IProject project = ResourceHelper.createProject(this.getName());
// Add C/C++ and CMake natures to make it a CMake project
IProjectDescription description = project.getDescription();
description.setNatureIds(
new String[] { CProjectNature.C_NATURE_ID, CCProjectNature.CC_NATURE_ID, CMakeNature.ID });
project.setDescription(description, null);
return project;
}
/**
* Add a mocked toolchain, which acts like a gcc toolchain and has Platform related OS ans ARCH.
*/
private void addMockToolchain() throws CoreException {
// Setup a toolchain ready to use for creating the ICBuildConfiguration
mockToolchain = mock(IToolChain.class);
when(mockToolchain.getProperty(anyString())).thenAnswer(new Answer<>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
String key = invocation.getArgument(0);
return switch (key) {
case IToolChain.ATTR_OS -> Platform.getOS();
case IToolChain.ATTR_ARCH -> Platform.getOSArch();
case "name" -> null; // name=Local
default -> null;
};
}
});
when(mockToolchain.getTypeId()).thenReturn("tc_typeId");
when(mockToolchain.getId()).thenReturn("tcId");
when(mockToolchain.matches(anyMap())).thenCallRealMethod();
when(mockToolchain.getBuildConfigNameFragment()).thenReturn(TC_BUILD_CONFIG_NAME_FRAGMENT);
when(mockToolchain.getName()).thenReturn("MockToolchainName");
// Add our mocked toolchain
toolchainManager.addToolChain(mockToolchain);
// Expected: there is a single toolchain now
assertThat(toolchainManager.getAllToolChains().size(), is(1));
}
private void removeDiscoveredToolchains() throws CoreException {
Collection<IToolChain> allToolChains = toolchainManager.getAllToolChains();
for (IToolChain toolchain : new ArrayList<>(allToolChains)) {
// System.out.println(String.format("Removing toolchain '%s' for test '%s'", toolchain.getName(), getName()));
toolchainManager.removeToolChain(toolchain);
}
// Expected: there are no toolchains
assertThat(toolchainManager.getAllToolChains().size(), is(0));
}
}

View file

@ -0,0 +1,178 @@
/*******************************************************************************
* Copyright (c) 2025 Renesas Electronics Europe.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.cdt.core.build;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
import java.util.ArrayList;
import java.util.Collection;
import org.eclipse.cdt.cmake.core.CMakeNature;
import org.eclipse.cdt.core.CCProjectNature;
import org.eclipse.cdt.core.CProjectNature;
import org.eclipse.cdt.core.testplugin.ResourceHelper;
import org.eclipse.cdt.core.testplugin.util.BaseTestCase5;
import org.eclipse.cdt.debug.core.CDebugCorePlugin;
import org.eclipse.cdt.debug.core.launch.CoreBuildLaunchBarTracker;
import org.eclipse.cdt.internal.core.build.CBuildConfigurationManager;
import org.eclipse.core.resources.IBuildConfiguration;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.launchbar.core.ILaunchBarManager;
import org.eclipse.launchbar.core.internal.LaunchBarManager;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/**
* Test for IDE-82683-REQ-024 #1084 part of #1000 in CDT 12.0.0. Test Part 2.
*
* Tests that a ICBuildConfiguration is never created with the default name when
* no toolchains are available.
*
* @see {@link DefaultBuildConfigurationNameTests}
*
*/
public class DefaultBuildConfigurationNameTestsNoToolchains extends BaseTestCase5 {
private IProject project;
private ICBuildConfigurationManager configManager = CDebugCorePlugin.getService(ICBuildConfigurationManager.class);
private IToolChainManager toolchainManager = CDebugCorePlugin.getService(IToolChainManager.class);
private ILaunchBarManager launchBarManager = CDebugCorePlugin.getService(ILaunchBarManager.class);
@BeforeEach
public void setup() throws Exception {
/*
* Remove any discovered toolchains that happen to be installed on the host. This is because
* some test hosts may have a gcc installed and others not. So if we remove that gcc and do not
* rely on it and instead setup our mocked toolchain instead, the test conditions are easier to control.
*/
removeDiscoveredToolchains();
CBuildConfigurationManager cbm = (CBuildConfigurationManager) configManager;
cbm.reset();
/*
* Copied from LaunchBarManagerTest.startupTest()
* Make sure the manager starts up and defaults everything to null
*/
LaunchBarManager manager = new LaunchBarManager(false);
manager.init();
assertThat(manager.getActiveLaunchDescriptor(), is(nullValue()));
assertThat(manager.getActiveLaunchMode(), is(nullValue()));
assertThat(manager.getActiveLaunchTarget(), is(nullValue()));
/*
* Key to repeatedly running tests with the same initial conditions is to reset the
* CoreBuildLaunchBarTracker, so the launchbar controls are as they would be in a new
* workspace.
*/
CDebugCorePlugin.getDefault().resetCoreBuildLaunchBarTracker();
}
/**
* Test that when calling {@link ICBuildConfigurationManager#getBuildConfiguration(IBuildConfiguration)}
* when there are no toolchains available, the active IBuildConfiguration becomes set to "buildError/!".
*
* Test to confirm assumptions about the project's IBuildConfiguration lifecycle when no toolchains are
* installed and a project is created.
*
* Behaviour before IDE-82683-REQ-024 fixed AND after fix.
* When IToolChainManager contains no valid toolchains:
*
* After project creation, the project's active IBuildConfiguration name defaults to
* IBuildConfiguration.DEFAULT_CONFIG_NAME.
*
* After project creation, the first interaction with the Core Build system is through the call:
* config.getAdapter(ICBuildConfiguration.class), where config is a IBuildConfiguration.
* This happens when the .project file is added in CModelManager.getBinaryParser(IProject).
* getAdapter calls CBuildConfigurationManager.getBuildConfiguration(IBuildConfiguration) with
* IBuildConfiguration==DEFAULT_CONFIG_NAME.
*
* When CBuildConfigurationManager.getBuildConfiguration(IBuildConfiguration buildConfig) is called,
* the provider.getCBuildConfiguration(buildConfig, configName) returns null because it has no toolchain.
* The CBuildConfigurationManager then quarantines this default buildConfig in the noConfigs bin and returns null.
*
* Later, when CoreBuildLaunchBarTracker triggers, it detects there are no toolchains available and so creates
* a new IBuildConfiguration with name "buildError/!" and then a special error ICBuildConfiguration, ErrorBuildConfiguration,
* with the message "No Toolchain found for Target Local".
* This new IBuildConfiguration/ICBuildConfiguration combo is then added as the active config using
* ProjectDescription.setActiveBuildConfig(String).
*
* Then project.getActiveBuildConfig() returns the buildConfig with name "buildError/!".
*
* And buildConfig.getAdapter(ICBuildConfiguration.class) returns the active buildConfig's ICBuildConfiguration
* which is instanceof ErrorBuildConfiguration.
*/
@Test
public void getBuildConfigurationNoToolchainsErrorBuildConfig() throws Exception {
// Create a CMake project, without any toolchains installed.
project = createCMakeProject();
waitForLaunchBarTracker();
IBuildConfiguration buildConfig = project.getActiveBuildConfig();
assertThat(buildConfig.getName(), is(not(IBuildConfiguration.DEFAULT_CONFIG_NAME)));
assertThat(buildConfig.getName(), is("buildError/!"));
System.out.println(
"getBuildConfigurationNoToolchainsErrorBuildConfig::buildConfig.getName()=" + buildConfig.getName());
// This calls CBuildConfigurationManager.getBuildConfiguration(IBuildConfiguration)
ICBuildConfiguration iCBuildConfig = buildConfig.getAdapter(ICBuildConfiguration.class);
System.out.println();
// expected: config should be an instanceof ErrorBuildConfiguration, but no way to get the name to check.
assertThat(iCBuildConfig, is(instanceOf(ErrorBuildConfiguration.class)));
// expected: the buildConfig should be the buildError one
assertThat(iCBuildConfig.getBuildConfiguration().getName(), is("buildError/!"));
}
/**
* Performs the same test as {@link #getBuildConfigurationNoToolchainsErrorBuildConfig()}.
* The test exists so we can prove multiple test runs function correctly and that
* initial conditions are correctly controlled and reset.
*/
@Test
public void getBuildConfigurationNoToolchainsErrorBuildConfig2() throws Exception {
getBuildConfigurationNoToolchainsErrorBuildConfig();
}
// Test infrastructure...
private void waitForLaunchBarTracker() throws OperationCanceledException, InterruptedException {
Job.getJobManager().join(CoreBuildLaunchBarTracker.JOB_FAMILY_CORE_BUILD_LAUNCH_BAR_TRACKER, null);
}
private IProject createCMakeProject() throws Exception {
// Create a plain Eclipse project
IProject project = ResourceHelper.createProject(this.getName());
// Add C/C++ and CMake natures to make it a CMake project
IProjectDescription description = project.getDescription();
description.setNatureIds(
new String[] { CProjectNature.C_NATURE_ID, CCProjectNature.CC_NATURE_ID, CMakeNature.ID });
project.setDescription(description, null);
return project;
}
private void removeDiscoveredToolchains() throws CoreException {
Collection<IToolChain> allToolChains = toolchainManager.getAllToolChains();
for (IToolChain toolchain : new ArrayList<>(allToolChains)) {
// System.out.println(String.format("Removing toolchain '%s' for test '%s'", toolchain.getName(), getName()));
toolchainManager.removeToolChain(toolchain);
}
// Expected: there are no toolchains
assertThat(toolchainManager.getAllToolChains().size(), is(0));
}
}

View file

@ -66,6 +66,7 @@ Export-Package: org.eclipse.cdt.core,
org.eclipse.cdt.testsrunner.boost,
org.eclipse.cdt.testsrunner.qttest",
org.eclipse.cdt.internal.core.browser;x-friends:="org.eclipse.cdt.ui",
org.eclipse.cdt.internal.core.build;x-friends:="org.eclipse.cdt.core.tests",
org.eclipse.cdt.internal.core.cdtvariables;x-internal:=true,
org.eclipse.cdt.internal.core.dom;x-internal:=true,
org.eclipse.cdt.internal.core.dom.ast.tag;x-internal:=true,

View file

@ -35,12 +35,6 @@ import org.eclipse.launchbar.core.target.ILaunchTarget;
*/
public interface ICBuildConfiguration extends IAdaptable, IScannerInfoProvider {
/**
* CDT doesn't like that the Platform default config name is an empty string.
* It needs a real name for the name of the build directory, for example.
*/
public static final String DEFAULT_NAME = "default"; //$NON-NLS-1$
/**
* @since 6.4
*/

View file

@ -37,9 +37,11 @@ public interface ICBuildConfigurationProvider {
/**
* Returns the Core Build configuration that owns this Platform Build configuration.
*
* @param buildConfig Platform Build Configuration. Must not be null.
* @param buildConfig Platform Build Configuration. Must not be null. Configs with the name
* {@link IBuildConfiguration#DEFAULT_CONFIG_NAME} are ignored.
* @param cBuildConfigName Name to give the ICBuildConfiguration. Must not be null.
* @return a Core Build configuration.
* @return a Core Build configuration or null if buildConfig has name
* {@link IBuildConfiguration#DEFAULT_CONFIG_NAME}.
* @throws CoreException if this method fails. Reasons include:
* <ul>
* <li> Toolchain is missing,</li>

View file

@ -96,6 +96,15 @@ public class CBuildConfigurationManager
private Map<IBuildConfiguration, ICBuildConfiguration> configs = new HashMap<>();
private Set<IBuildConfiguration> noConfigs = new HashSet<>();
/**
* Resets configs. Used for testing only.
* @noreference This method is not intended to be referenced by clients.
*/
public void reset() {
configs = new HashMap<>();
noConfigs = new HashSet<>();
}
public CBuildConfigurationManager() {
ResourcesPlugin.getWorkspace().addResourceChangeListener(this);
}
@ -199,18 +208,13 @@ public class CBuildConfigurationManager
IBuildConfiguration buildConfig = iterator.next();
String configName = null;
ICBuildConfigurationProvider provider = null;
if (IBuildConfiguration.DEFAULT_CONFIG_NAME.equals(buildConfig.getName())) {
configName = ICBuildConfiguration.DEFAULT_NAME;
provider = getProvider(buildConfig.getProject());
} else {
String[] segments = buildConfig.getName().split("/"); //$NON-NLS-1$
if (segments.length == 2) {
String providerId = segments[0];
configName = segments[1];
Provider delegate = getProviderDelegate(providerId);
if (delegate != null && delegate.supports(buildConfig.getProject())) {
provider = delegate.getProvider();
}
String[] segments = buildConfig.getName().split("/"); //$NON-NLS-1$
if (segments.length == 2) {
String providerId = segments[0];
configName = segments[1];
Provider delegate = getProviderDelegate(providerId);
if (delegate != null && delegate.supports(buildConfig.getProject())) {
provider = delegate.getProvider();
}
}
@ -248,18 +252,13 @@ public class CBuildConfigurationManager
if (config == null) {
String configName = null;
ICBuildConfigurationProvider provider = null;
if (IBuildConfiguration.DEFAULT_CONFIG_NAME.equals(buildConfig.getName())) {
configName = ICBuildConfiguration.DEFAULT_NAME;
provider = getProvider(buildConfig.getProject());
} else {
String[] segments = buildConfig.getName().split("/"); //$NON-NLS-1$
if (segments.length == 2) {
String providerId = segments[0];
configName = segments[1];
Provider delegate = getProviderDelegate(providerId);
if (delegate != null && delegate.supports(buildConfig.getProject())) {
provider = delegate.getProvider();
}
String[] segments = buildConfig.getName().split("/"); //$NON-NLS-1$
if (segments.length == 2) {
String providerId = segments[0];
configName = segments[1];
Provider delegate = getProviderDelegate(providerId);
if (delegate != null && delegate.supports(buildConfig.getProject())) {
provider = delegate.getProvider();
}
}

View file

@ -117,6 +117,14 @@ public class CDebugCorePlugin extends Plugin {
return plugin;
}
/**
* Resets CoreBuildLaunchBarTracker. Used for testing only.
* @noreference This method is not intended to be referenced by clients.
*/
public void resetCoreBuildLaunchBarTracker() {
coreBuildLaunchBarTracker.reset();
}
/**
* Returns the workspace instance.
*

View file

@ -76,6 +76,16 @@ public class CoreBuildLaunchBarTracker implements ILaunchBarListener, ILaunchTar
targetManager.addListener(this);
}
/**
* Resets CoreBuildLaunchBarTracker. Used for testing only.
* @noreference This method is not intended to be referenced by clients.
*/
public void reset() {
lastMode = null;
lastDescriptor = null;
lastTarget = null;
}
/**
* @since 8.4
*/

View file

@ -89,7 +89,7 @@ public class LaunchBarManager implements ILaunchBarManager, ILaunchTargetListene
}
// called from unit tests to ensure everything is inited
LaunchBarManager(boolean doInit) {
public LaunchBarManager(boolean doInit) {
launchTargetManager = getLaunchTargetManager();
launchTargetManager.addListener(this);
if (doInit) {
@ -122,7 +122,7 @@ public class LaunchBarManager implements ILaunchBarManager, ILaunchTargetListene
}
// When testing, call this after setting up the mocks.
void init() throws CoreException {
public void init() throws CoreException {
try {
// Fetch the desc order before the init messes it up
IEclipsePreferences store = getPreferenceStore();
@ -219,7 +219,7 @@ public class LaunchBarManager implements ILaunchBarManager, ILaunchTargetListene
descriptorTypes.put(typeInfo.getId(), typeInfo);
if (configProviders.get(typeInfo.getId()) == null) {
// Make sure we initialize the list
configProviders.put(typeInfo.getId(), new ArrayList<LaunchConfigProviderInfo>());
configProviders.put(typeInfo.getId(), new ArrayList<>());
}
} else if (elementName.equals("configProvider")) { //$NON-NLS-1$
LaunchConfigProviderInfo info = new LaunchConfigProviderInfo(element);