Skip to content

Commit

Permalink
Add API to set CMake generator default (eg Ninja)
Browse files Browse the repository at this point in the history
ISV can set their desired CMake generator default (eg Ninja).

Addresses Issue: CDT CMake Improvements eclipse-cdt#1000, IDE-82683-REQ-012 CMake
generator default
  • Loading branch information
betamaxbandit committed Jan 22, 2025
1 parent 1abb990 commit ac04d3b
Show file tree
Hide file tree
Showing 17 changed files with 371 additions and 67 deletions.
3 changes: 2 additions & 1 deletion cmake/org.eclipse.cdt.cmake.core.tests/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ Import-Package: org.assertj.core.api;version="[3.24.2,4.0.0)",
Automatic-Module-Name: org.eclipse.cdt.cmake.core.tests
Bundle-Vendor: %Bundle-Vendor
Bundle-Copyright: %Bundle-Copyright
Require-Bundle: org.junit
Require-Bundle: org.junit,
org.eclipse.cdt.core.tests;bundle-version="[5.4.0,6.0.0)"

Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
/*******************************************************************************
* 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.cmake.core;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.is;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.cdt.cmake.core.properties.CMakeGenerator;
import org.eclipse.cdt.cmake.core.properties.ICMakeProperties;
import org.eclipse.cdt.cmake.core.properties.IOsOverrides;
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.core.resources.IBuildConfiguration;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.runtime.CoreException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

/**
* Tests a new API added to the CMake Build Configuration which allows default CMake properties to be set.
* See the new interface {@link ICMakeBuildConfiguration}.
*/
public class CMakeBuildConfigurationTests extends BaseTestCase5 {
private IBuildConfiguration buildConfig;

@BeforeEach
public void setup() throws Exception {
// Create a CMake project
IProject project = createCMakeProject();
// Get the default build config from the project (it always has one)
buildConfig = project.getBuildConfig(IBuildConfiguration.DEFAULT_CONFIG_NAME);
}

/**
* Test for {@link IOsOverrides#setGenerator()}.
*/
@Test
public void getCMakePropertiesTestSetGenerator() throws Exception {
CMakeBuildConfigurationExtended cmBuildConfig = new CMakeBuildConfigurationExtended(buildConfig,
"cmBuildConfigName") {

@Override
public ICMakeProperties getCMakeProperties() {
ICMakeProperties properties = super.getCMakeProperties();

IOsOverrides windowsOverrides = properties.getWindowsOverrides();
windowsOverrides.setGenerator(CMakeGenerator.NMakeMakefiles);
IOsOverrides linuxOverrides = properties.getLinuxOverrides();
linuxOverrides.setGenerator(CMakeGenerator.UnixMakefiles);
return properties;
}
};

// Call the new method on ICMakeBuildConfiguration to get the default CMake properties.
ICMakeProperties cMakeProperties = cmBuildConfig.getCMakeProperties();

// Get overrides for Windows host and check the default value for getGenerator.
IOsOverrides windowsOverrides = cMakeProperties.getWindowsOverrides();
CMakeGenerator winGenerator = windowsOverrides.getGenerator();
assertThat(winGenerator, is(CMakeGenerator.NMakeMakefiles));

// Get overrides for Linux host and check the default value for getGenerator.
IOsOverrides linuxOverrides = cMakeProperties.getLinuxOverrides();
CMakeGenerator linuxGenerator = linuxOverrides.getGenerator();
assertThat(linuxGenerator, is(CMakeGenerator.UnixMakefiles));
}

/**
* Test for {@link IOsOverrides#setDefaultGenerator()}. Also tests that {@link ICMakeProperties#reset(boolean)} works as expected.
*/
@Test
public void getCMakePropertiesTestSetDefaultGenerator() throws Exception {
CMakeBuildConfigurationExtended cmBuildConfig = new CMakeBuildConfigurationExtended(buildConfig,
"cmBuildConfigName") {

@Override
public ICMakeProperties getCMakeProperties() {
ICMakeProperties properties = super.getCMakeProperties();

IOsOverrides windowsOverrides = properties.getWindowsOverrides();
windowsOverrides.setDefaultGenerator(CMakeGenerator.NMakeMakefiles);
IOsOverrides linuxOverrides = properties.getLinuxOverrides();
linuxOverrides.setDefaultGenerator(CMakeGenerator.UnixMakefiles);
// reset(true) causes the CMake generator to be reset to it's default value.
properties.reset(true);
return properties;
}
};

// Call the new method on ICMakeBuildConfiguration to get the default CMake properties.
ICMakeProperties cMakeProperties = cmBuildConfig.getCMakeProperties();

// Get overrides for Windows host and check the default value for getGenerator.
IOsOverrides windowsOverrides = cMakeProperties.getWindowsOverrides();
CMakeGenerator winGenerator = windowsOverrides.getGenerator();
assertThat(winGenerator, is(CMakeGenerator.NMakeMakefiles));

// Get overrides for Linux host and check the default value for getGenerator.
IOsOverrides linuxOverrides = cMakeProperties.getLinuxOverrides();
CMakeGenerator linuxGenerator = linuxOverrides.getGenerator();
assertThat(linuxGenerator, is(CMakeGenerator.UnixMakefiles));
}

/**
* Test for {@link ICMakeProperties#setExtraArguments()}
* This is a different extraArguments to IOsOverrides#setExtraArguments().
* Presumably ICMakeProperties#setExtraArguments() are platform agnostic extra arguments, where as
* IOsOverrides#setExtraArguments() can be set different for Linux and Windows.
*/
@Test
public void getCMakePropertiesTestSetExtraArguments() throws Exception {
// Create a C Build Configuration using the default build config and an arbitrary name
CMakeBuildConfigurationExtended cmBuildConfig = new CMakeBuildConfigurationExtended(buildConfig,
"cmBuildConfigName") {

@Override
public ICMakeProperties getCMakeProperties() {
ICMakeProperties properties = super.getCMakeProperties();
properties.setExtraArguments(
new ArrayList<>((List.of("-DplatformAgnosticArgsTest0=0", "-DplatformAgnosticArgsTest1=1"))));
return properties;
}
};
// Call the new method on ICMakeBuildConfiguration to get the default CMake properties.
ICMakeProperties cMakeProperties = cmBuildConfig.getCMakeProperties();
List<String> extraArguments = cMakeProperties.getExtraArguments();
assertThat(extraArguments, contains("-DplatformAgnosticArgsTest0=0", "-DplatformAgnosticArgsTest1=1"));
}

/**
* Test for {@link IOsOverrides#setExtraArguments()}
*/
@Test
public void getCMakePropertiesTestIOsOverridesSetExtraArguments() throws Exception {
// Create a C Build Configuration using the default build config and an arbitrary name
CMakeBuildConfigurationExtended cmBuildConfig = new CMakeBuildConfigurationExtended(buildConfig,
"cmBuildConfigName");
// Call the new method on ICMakeBuildConfiguration to get the default CMake properties.
ICMakeProperties cMakeProperties = cmBuildConfig.getCMakeProperties();

// Get overrides for Windows host and check the default value for getExtraArguments.
IOsOverrides windowsOverrides = cMakeProperties.getWindowsOverrides();
List<String> winExtraArguments = windowsOverrides.getExtraArguments();
assertThat(winExtraArguments, contains("-Dtest0=0", "-Dtest1=1"));

// Get overrides for Linux host and check the default value for getExtraArguments.
IOsOverrides linuxOverrides = cMakeProperties.getLinuxOverrides();
List<String> linuxExtraArguments = linuxOverrides.getExtraArguments();
assertThat(linuxExtraArguments, contains("-DLinuxtest0=0", "-DLinuxtest1=1"));
}

private class CMakeBuildConfigurationExtended extends CMakeBuildConfiguration {

public CMakeBuildConfigurationExtended(IBuildConfiguration config, String name) throws CoreException {
super(config, name);
}

@Override
public ICMakeProperties getCMakeProperties() {
// get the built-in CDT defaults
ICMakeProperties properties = super.getCMakeProperties();

// provide my customizations (I don't know what Renesas/extenders may actually want to do here)
properties.setWarnUnitialized(true);

IOsOverrides windowsOverrides = properties.getWindowsOverrides();
windowsOverrides.setExtraArguments(new ArrayList<>((List.of("-Dtest0=0", "-Dtest1=1"))));

IOsOverrides linuxOverrides = properties.getLinuxOverrides();
linuxOverrides.setExtraArguments(new ArrayList<>((List.of("-DLinuxtest0=0", "-DLinuxtest1=1"))));

return properties;
}
}

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;
}
}
2 changes: 1 addition & 1 deletion cmake/org.eclipse.cdt.cmake.core/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %pluginName
Bundle-SymbolicName: org.eclipse.cdt.cmake.core;singleton:=true
Bundle-Version: 1.6.0.qualifier
Bundle-Version: 2.0.0.qualifier
Bundle-Activator: org.eclipse.cdt.cmake.core.internal.Activator
Bundle-Vendor: %providerName
Require-Bundle: org.eclipse.core.runtime,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.eclipse.cdt.cmake.core.internal.CommandDescriptorBuilder.CommandDescriptor;
import org.eclipse.cdt.cmake.core.internal.IOsOverridesSelector;
import org.eclipse.cdt.cmake.core.properties.CMakeGenerator;
import org.eclipse.cdt.cmake.core.properties.CMakePropertiesFactory;
import org.eclipse.cdt.cmake.core.properties.ICMakeProperties;
import org.eclipse.cdt.cmake.core.properties.ICMakePropertiesController;
import org.eclipse.cdt.cmake.core.properties.IOsOverrides;
Expand Down Expand Up @@ -72,13 +73,14 @@
import org.osgi.service.prefs.Preferences;

/**
* @since 1.6
* @since 2.0
*/
public class CMakeBuildConfiguration extends CBuildConfiguration {
public class CMakeBuildConfiguration extends CBuildConfiguration implements ICMakeBuildConfiguration {

public static final String CMAKE_USE_UI_OVERRIDES = "cmake.use.ui.overrides"; //$NON-NLS-1$
public static final boolean CMAKE_USE_UI_OVERRIDES_DEFAULT = false;
public static final String CMAKE_GENERATOR = "cmake.generator"; //$NON-NLS-1$
public static final String CMAKE_GENERATOR_DEFAULT = "Ninja"; //$NON-NLS-1$
public static final String CMAKE_ARGUMENTS = "cmake.arguments"; //$NON-NLS-1$
public static final String CMAKE_ENV = "cmake.environment"; //$NON-NLS-1$
public static final String BUILD_COMMAND = "cmake.command.build"; //$NON-NLS-1$
Expand Down Expand Up @@ -124,6 +126,45 @@ public CMakeBuildConfiguration(IBuildConfiguration config, String name, IToolCha
this.toolChainFile = toolChainFile;
}

@Override
public ICMakeProperties getCMakeProperties() {
// Create CMake default properties
ICMakeProperties properties = CMakePropertiesFactory.createProperties();
// Set defaults for both platforms
IOsOverrides windowsOverrides = properties.getWindowsOverrides();
windowsOverrides.setDefaultGenerator(CMakeGenerator.Ninja);
IOsOverrides linuxOverrides = properties.getLinuxOverrides();
linuxOverrides.setDefaultGenerator(CMakeGenerator.Ninja);
// reset(true) causes the CMake generator to be reset to it's default value.
properties.reset(true);

/*
* Set values for this OS platform for CMake defaults and if UI overrides is enable for UI property values
*/
IOsOverrides osOverrides = properties.getPlatformOsOverrides(properties);
// CMAKE_GENERATOR
String cmakeGeneratorCmakeDefault = osOverrides.getGenerator().getCMakeName();
// CMAKE_ARGUMENTS
String extraArgsCmakeDefault = osOverrides.getExtraArguments().stream().map(String::trim)
.collect(Collectors.joining(" ")); //$NON-NLS-1$

String useUiOverrides = getProperty(CMAKE_USE_UI_OVERRIDES);
if (Boolean.parseBoolean(useUiOverrides)) {
// UI overrides are enabled so UI overridden values take precedence if not null otherwise use CMake default
// CMAKE_GENERATOR
String cmakeGeneratorUiValue = getProperty(CMakeBuildConfiguration.CMAKE_GENERATOR);
String cmakeGenerator = cmakeGeneratorUiValue == null ? cmakeGeneratorCmakeDefault : cmakeGeneratorUiValue;
osOverrides.setGenerator(CMakeGenerator.getGenerator(cmakeGenerator));
// CMAKE_ARGUMENTS
String extraArgsUiValue = getProperty(CMAKE_ARGUMENTS);
String extraArgs = extraArgsUiValue == null ? extraArgsCmakeDefault : extraArgsUiValue;
List<String> extraArgsList = Arrays.stream(extraArgs.split("\\s+")).map(entry -> entry.trim()) //$NON-NLS-1$
.collect(Collectors.toList());
osOverrides.setExtraArguments(extraArgsList);
}
return properties;
}

/**
* Gets the tool-chain description file to pass to the cmake command-line.
*
Expand Down Expand Up @@ -162,7 +203,8 @@ public IProject[] build(int kind, Map<String, String> args, IConsole console, IP
runCMake = true;
}

ICMakeProperties cmakeProperties = getPropertiesController().load();
ICMakeProperties cmakeProperties = getCMakeProperties();

runCMake |= !Files.exists(buildDir.resolve("CMakeCache.txt")); //$NON-NLS-1$

// Causes CMAKE_BUILD_TYPE to be set according to the launch mode
Expand Down Expand Up @@ -303,7 +345,7 @@ public void clean(IConsole console, IProgressMonitor monitor) throws CoreExcepti

project.deleteMarkers(ICModelMarker.C_MODEL_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE);

ICMakeProperties cmakeProperties = getPropertiesController().load();
ICMakeProperties cmakeProperties = getCMakeProperties();
CommandDescriptorBuilder cmdBuilder = new CommandDescriptorBuilder(cmakeProperties,
new SimpleOsOverridesSelector());
CommandDescriptor command = cmdBuilder.makeCMakeBuildCommandline(getCleanCommand());
Expand Down Expand Up @@ -405,7 +447,7 @@ private ICMakePropertiesController getPropertiesController() {
@SuppressWarnings("unchecked")
public <T> T getAdapter(Class<T> adapter) {
if (ICMakePropertiesController.class.equals(adapter)) {
return (T) pc;
return (T) getPropertiesController();
}
return super.getAdapter(adapter);
}
Expand Down Expand Up @@ -582,50 +624,33 @@ public void shutdown() {
}
} // CMakeIndexerInfoConsumer

/**
* Supports OS overrides and also User Interface (UI) overrides, provided in the CMakeBuildTab. The UI
* overrides are stored in the intermediary CBuildConfiguration Preferences (CBuildConfiguration.getSettings())
* and used only when CMAKE_USE_UI_OVERRIDES is true.
*/
private class SimpleOsOverridesSelector implements IOsOverridesSelector {

@Override
public IOsOverrides getOsOverrides(ICMakeProperties cmakeProperties) {
IOsOverrides overrides;
// get overrides. Simplistic approach ATM, probably a strategy might fit better.
// see comment in CMakeIndexerInfoConsumer#getFileForCMakePath()
final String os = Platform.getOS();
if (Platform.OS_WIN32.equals(os)) {
overrides = cmakeProperties.getWindowsOverrides();
} else {
// fall back to linux, if OS is unknown
overrides = cmakeProperties.getLinuxOverrides();
}
return getUiOrOsOverrides(overrides);
}

private IOsOverrides getUiOrOsOverrides(IOsOverrides osOverrides) {
IOsOverrides retVal = Objects.requireNonNull(osOverrides, "osOverrides must not be null"); //$NON-NLS-1$
Preferences settings = getSettings();
boolean useUiOverrides = settings.getBoolean(CMAKE_USE_UI_OVERRIDES, CMAKE_USE_UI_OVERRIDES_DEFAULT);
if (useUiOverrides) {
// Set UI override for generator
String gen = settings.get(CMAKE_GENERATOR, ""); //$NON-NLS-1$
retVal.setGenerator(CMakeGenerator.getGenerator(gen));
// Set UI override for Extra Arguments
String extraArgsStr = settings.get(CMAKE_ARGUMENTS, ""); //$NON-NLS-1$
// Convert String of args, separated by 1 or more spaces, into list of Strings
List<String> extraArgs = Arrays.stream(extraArgsStr.split("\\s+")).map(entry -> entry.trim()) //$NON-NLS-1$
.collect(Collectors.toList());
retVal.setExtraArguments(extraArgs);
// Set UI override for cmake build command, unless it's the default
String buildCommand = settings.get(BUILD_COMMAND, BUILD_COMMAND_DEFAULT);
if (!buildCommand.isBlank() && !BUILD_COMMAND_DEFAULT.equals(buildCommand)) {
retVal.setUseDefaultCommand(false);
retVal.setCommand(buildCommand);
}
}
return retVal;
return cmakeProperties.getPlatformOsOverrides(cmakeProperties);
}
} // SimpleOsOverridesSelector

@Override
public Map<String, String> getDefaultProperties() {
return Map.of(//
CMAKE_GENERATOR, CMAKE_GENERATOR_DEFAULT, //
CMAKE_USE_UI_OVERRIDES, Boolean.toString(CMAKE_USE_UI_OVERRIDES_DEFAULT), //
CMAKE_ARGUMENTS, "" // //$NON-NLS-1$
);
}

@Override
public Map<String, String> getProperties() {
var map = new HashMap<String, String>();
map.putAll(getDefaultProperties());
map.putAll(super.getProperties());
return map;
}

@Override
public String getProperty(String name) {
return getSettings().get(name, getDefaultProperties().get(name));
}
}
Loading

0 comments on commit ac04d3b

Please sign in to comment.