1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-06-07 17:56:01 +02:00

Bug 567488: Use snakeyaml to persist command-line options to pass to cmake

Change-Id: Ia6b60865f663aecae74d6d571bc9d213bf7cd36b
Signed-off-by: Martin Weber <fifteenknots505@gmail.com>
This commit is contained in:
Martin Weber 2020-11-03 21:58:54 +01:00
parent ebf2d24c95
commit de80240232
12 changed files with 274 additions and 24 deletions

View file

@ -8,5 +8,6 @@ Automatic-Module-Name: org.eclipse.cdt.cmake.core.tests
Bundle-Vendor: %Bundle-Vendor
Bundle-Copyright: %Bundle-Copyright
Require-Bundle: org.junit,
org.junit.jupiter.api
org.junit.jupiter.api,
org.assertj;bundle-version="3.14.0"

View file

@ -0,0 +1,103 @@
/*******************************************************************************
* Copyright (c) 2020 Martin Weber.
*
* 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.internal;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertNotNull;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
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.junit.Test;
/**
* @author Martin Weber
*/
public class CMakePropertiesControllerTest {
/**
* Test method for {@link org.eclipse.cdt.cmake.core.internal.CMakePropertiesController#load()}.
* @throws IOException
*/
@Test
public void testLoad() throws IOException {
CMakePropertiesController testee;
// test with non-existing file
Path file = Path.of(new File("does-not-exist" + UUID.randomUUID().toString()).toURI());
testee = new CMakePropertiesController(file, () -> {
});
assertNotNull(testee.load());
// test with empty file
File f = File.createTempFile("CMakePropertiesControllerTest", null);
f.deleteOnExit();
file = Path.of(f.toURI());
testee = new CMakePropertiesController(file, () -> {
});
assertNotNull(testee.load());
}
/**
* Test method for {@link org.eclipse.cdt.cmake.core.internal.CMakePropertiesController#save(org.eclipse.cdt.cmake.core.properties.ICMakeProperties)}.
* @throws IOException
*/
@Test
public void testSaveLoad() throws IOException {
Path file = Path.of(File.createTempFile("CMakePropertiesControllerTest", null).toURI());
CMakePropertiesController testee = new CMakePropertiesController(file, () -> {
});
ICMakeProperties props = testee.load();
assertNotNull(props);
props.setCacheFile("cacheFile");
props.setClearCache(true);
props.setDebugOutput(true);
props.setDebugTryCompile(true);
props.setTrace(true);
props.setWarnNoDev(true);
props.setWarnUnitialized(true);
props.setWarnUnused(true);
{
IOsOverrides overrides = props.getLinuxOverrides();
overrides.setGenerator(CMakeGenerator.Ninja);
List<String> extraArgs = new ArrayList<>();
extraArgs.add("arg1l=1");
extraArgs.add("arg2l=2");
overrides.setExtraArguments(extraArgs);
}
{
IOsOverrides overrides = props.getWindowsOverrides();
overrides.setGenerator(CMakeGenerator.BorlandMakefiles);
List<String> extraArgs = new ArrayList<>();
extraArgs.add("arg1w=1");
extraArgs.add("arg2w=2");
overrides.setExtraArguments(extraArgs);
}
List<String> extraArgs = new ArrayList<>();
extraArgs.add("arg1");
extraArgs.add("arg2");
props.setExtraArguments(extraArgs);
testee.save(props);
ICMakeProperties in = testee.load();
assertThat(in).usingRecursiveComparison().isEqualTo(props);
}
}

View file

@ -0,0 +1,86 @@
/*******************************************************************************
* Copyright (c) 2020 Martin Weber.
*
* 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.internal;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.cdt.cmake.core.internal.properties.CMakePropertiesBean;
import org.eclipse.cdt.cmake.core.properties.CMakeGenerator;
import org.junit.Test;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.CustomClassLoaderConstructor;
/**
* @author Martin Weber
*/
public class CMakePropertiesEvolutionTest {
private static final String VALUE_OF_EVOLVED_PROPERTY = "value of evolvedProperty";
/** Tests whether properties persisted by a previous version of our bundle can be loaded by
* a newer version of our bundle.
*/
@Test
public void testSaveLoadEvolution_1() throws IOException {
CMakePropertiesBean propsAtDefault = new CMakePropertiesBean();
propsAtDefault.reset(true);
CMakePropertiesBean props = new CMakePropertiesBean();
props.setCacheFile("cacheFile");
props.setClearCache(true);
props.setDebugOutput(true);
props.setDebugTryCompile(true);
props.setTrace(true);
props.setWarnNoDev(true);
props.setWarnUnitialized(true);
props.setWarnUnused(true);
props.getLinuxOverrides().setGenerator(CMakeGenerator.Ninja);
List<String> extraArgs = new ArrayList<>();
extraArgs.add("arg1");
extraArgs.add("arg2");
props.setExtraArguments(extraArgs);
Yaml yaml = new Yaml(new CustomClassLoaderConstructor(this.getClass().getClassLoader()));
String output = yaml.dump(props);
// try to load as evolved properties..
CMakePropertiesBean_1 in = yaml.loadAs(output, CMakePropertiesBean_1.class);
assertNotNull(in);
assertEquals(CMakePropertiesEvolutionTest.VALUE_OF_EVOLVED_PROPERTY, in.getEvolvedProperty());
assertThat(props).usingRecursiveComparison().isEqualTo(in);
}
private static class CMakePropertiesBean_1 extends CMakePropertiesBean {
private String evolvedProperty;
@Override
public void reset(boolean resetOsOverrides) {
super.reset(resetOsOverrides);
evolvedProperty = CMakePropertiesEvolutionTest.VALUE_OF_EVOLVED_PROPERTY;
}
public String getEvolvedProperty() {
return evolvedProperty;
}
public void setEvolvedProperty(String evolvedProperty) {
this.evolvedProperty = evolvedProperty;
}
}
}

View file

@ -12,7 +12,8 @@ Require-Bundle: org.eclipse.core.runtime,
org.eclipse.cdt.core;bundle-version="5.12.0",
org.eclipse.tools.templates.freemarker;bundle-version="1.0.0";visibility:=reexport,
com.google.gson,
org.eclipse.cdt.cmake.is.core
org.eclipse.cdt.cmake.is.core,
org.yaml.snakeyaml;bundle-version="1.14.0"
Bundle-RequiredExecutionEnvironment: JavaSE-11
Bundle-ActivationPolicy: lazy
Export-Package: org.eclipse.cdt.cmake.core,

View file

@ -72,9 +72,8 @@ public class CMakeBuildConfiguration extends CBuildConfiguration {
private ICMakeToolChainFile toolChainFile;
private final CMakePropertiesController pc = new CMakePropertiesController(() -> {
deleteCMakeCache = true;
});
// lazily instantiated..
private CMakePropertiesController pc;
private Map<IResource, IScannerInfo> infoPerResource;
/** whether one of the CMakeLists.txt files in the project has been
@ -461,6 +460,20 @@ public class CMakeBuildConfiguration extends CBuildConfiguration {
}
}
/** Lazily creates the CMakePropertiesController for the project.
*/
private CMakePropertiesController getPropertiesController() {
if (pc == null) {
Path filePath = Path.of(getProject().getFile(".settings/CDT-cmake.yaml").getLocationURI()); //$NON-NLS-1$
pc = new CMakePropertiesController(filePath, () -> {
deleteCMakeCache = true;
// TODO delete cache file here for the case a user restarts the workbench
// prior to running a new build
});
}
return pc;
}
// interface IAdaptable
@Override
@SuppressWarnings("unchecked")
@ -468,7 +481,7 @@ public class CMakeBuildConfiguration extends CBuildConfiguration {
T adapter0 = super.getAdapter(adapter);
if (adapter0 == null) {
if (ICMakePropertiesController.class.equals(adapter)) {
adapter0 = (T) pc;
adapter0 = (T) getPropertiesController();
}
}
return adapter0;

View file

@ -11,6 +11,13 @@
package org.eclipse.cdt.cmake.core.internal;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Objects;
import java.util.function.BinaryOperator;
@ -20,6 +27,8 @@ import org.eclipse.cdt.cmake.core.internal.properties.CMakePropertiesBean;
import org.eclipse.cdt.cmake.core.properties.CMakeGenerator;
import org.eclipse.cdt.cmake.core.properties.ICMakeProperties;
import org.eclipse.cdt.cmake.core.properties.ICMakePropertiesController;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.CustomClassLoaderConstructor;
/**
* A {@code ICMakePropertiesController} that monitors modifications to the project properties that force
@ -28,6 +37,7 @@ import org.eclipse.cdt.cmake.core.properties.ICMakePropertiesController;
*/
class CMakePropertiesController implements ICMakePropertiesController {
private final Path storageFile;
private final Runnable cmakeCacheDirtyMarker;
private String cacheFile;
@ -40,37 +50,60 @@ class CMakePropertiesController implements ICMakePropertiesController {
/** Creates a new CMakePropertiesController object.
*
* @param storageFile
* the file where to persist the properties. The file may not exist, but its parent directory is supposed to exist.
* @param cmakeCacheDirtyMarker
* the object to notify when modifications to the project properties force
* us to delete file CMakeCache.txt to avoid complaints by cmake
*/
CMakePropertiesController(Runnable cmakeCacheDirtyMarker) {
CMakePropertiesController(Path storageFile, Runnable cmakeCacheDirtyMarker) {
this.storageFile = Objects.requireNonNull(storageFile);
this.cmakeCacheDirtyMarker = Objects.requireNonNull(cmakeCacheDirtyMarker);
}
@Override
public ICMakeProperties load() {
// TODO implement load()
CMakePropertiesBean props = new CMakePropertiesBean();
public ICMakeProperties load() throws IOException {
CMakePropertiesBean props = null;
if (Files.exists(storageFile)) {
try (InputStream is = Files.newInputStream(storageFile)) {
props = new Yaml(new CustomClassLoaderConstructor(this.getClass().getClassLoader())).loadAs(is,
CMakePropertiesBean.class);
// props is null here if if no document was available in the file
}
}
if (props == null) {
// nothing was persisted, return default properties
props = new CMakePropertiesBean();
}
setupModifyDetection(props);
return props;
}
@Override
public void save(ICMakeProperties properties) {
public void save(ICMakeProperties properties) throws IOException {
// detect whether changes force us to delete file CMakeCache.txt to avoid complaints by cmake
if (!Objects.equals(buildType, properties.getBuildType())
|| !Objects.equals(cacheFile, properties.getCacheFile())
|| !Objects.equals(generatorLinux, properties.getLinuxOverrides().getGenerator())
|| !Objects.equals(generatorWindows, properties.getWindowsOverrides().getGenerator())) {
cmakeCacheDirtyMarker.run(); // must remove cmake cachefile
} else if (extraArgumentsChange(extraArguments, properties.getExtraArguments())
|| extraArgumentsChange(extraArgumentsLinux, properties.getLinuxOverrides().getExtraArguments())
|| extraArgumentsChange(extraArgumentsWindows, properties.getWindowsOverrides().getExtraArguments())) {
} else if (hasExtraArgumentChanged(extraArguments, properties.getExtraArguments())
|| hasExtraArgumentChanged(extraArgumentsLinux, properties.getLinuxOverrides().getExtraArguments())
|| hasExtraArgumentChanged(extraArgumentsWindows,
properties.getWindowsOverrides().getExtraArguments())) {
cmakeCacheDirtyMarker.run(); // must remove cmake cachefile
}
// TODO implement save()
if (!Files.exists(storageFile)) {
try {
Files.createFile(storageFile);
} catch (FileAlreadyExistsException ignore) {
}
}
try (Writer wr = new OutputStreamWriter(Files.newOutputStream(storageFile))) {
new Yaml().dump(properties, wr);
}
setupModifyDetection(properties);
}
@ -86,13 +119,13 @@ class CMakePropertiesController implements ICMakePropertiesController {
extraArgumentsWindows = properties.getWindowsOverrides().getExtraArguments();
}
private boolean extraArgumentsChange(List<String> args1, List<String> args2) {
private boolean hasExtraArgumentChanged(List<String> expected, List<String> actual) {
String wanted = "CMAKE_TOOLCHAIN_FILE"; //$NON-NLS-1$
// extract the last arguments that contain String wanted..
Predicate<? super String> predContains = a -> a.contains(wanted);
BinaryOperator<String> keepLast = (first, second) -> second;
String a1 = args1.stream().filter(predContains).reduce(keepLast).orElse(null);
String a2 = args2.stream().filter(predContains).reduce(keepLast).orElse(null);
String a1 = expected.stream().filter(predContains).reduce(keepLast).orElse(null);
String a2 = actual.stream().filter(predContains).reduce(keepLast).orElse(null);
if (!Objects.equals(a1, a2)) {
return true;
}

View file

@ -13,7 +13,6 @@ package org.eclipse.cdt.cmake.core.internal.properties;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import org.eclipse.cdt.cmake.core.properties.ICMakeProperties;
@ -144,7 +143,7 @@ public class CMakePropertiesBean implements ICMakeProperties {
@Override
public void setCacheFile(String cacheFile) {
this.cacheFile = Objects.requireNonNull(cacheFile);
this.cacheFile = cacheFile;
}
@Override

View file

@ -17,6 +17,9 @@ import java.util.List;
* Holds project Properties for cmake.
*
* @author Martin Weber
*
* @noimplement This interface is not intended to be implemented by clients.
* @noextend This interface is not intended to be extended by clients.
*/
public interface ICMakeProperties {

View file

@ -11,18 +11,25 @@
package org.eclipse.cdt.cmake.core.properties;
import java.io.IOException;
/**
* Responsible for loading and persting @code ICMakeProperties} objects.
* Responsible for loading and persisting {@code ICMakeProperties} objects.
*
* @author Martin Weber
*/
public interface ICMakePropertiesController {
/** Creates a new {@code ICMakeProperties} object, initialized from the persistence store.
* If the persistence store does not exist, an object initialized to the default values is returned.
*
* @throws IOException if the persistence store exists but could not be read
*/
ICMakeProperties load();
ICMakeProperties load() throws IOException;
/** Saves the specified {@code ICMakeProperties} object to the persistence store.
*
* @throws IOException if the persistence store could not be written to
*/
void save(ICMakeProperties properties);
void save(ICMakeProperties properties) throws IOException;
}

View file

@ -16,6 +16,8 @@ package org.eclipse.cdt.cmake.core.properties;
*
* @author Martin Weber
*
* @noimplement This interface is not intended to be implemented by clients.
* @noextend This interface is not intended to be extended by clients.
*/
public interface IGeneralProperties {

View file

@ -208,6 +208,7 @@
<bundle id="javax.xml.stream" version="0.0.0"/>
<bundle id="com.google.gson" version="0.0.0"/>
<bundle id="org.freemarker" version="0.0.0"/>
<bundle id="org.yaml.snakeyaml" version="0.0.0"/>
<bundle id="org.eclipse.tools.templates.core" version="0.0.0"/>
<bundle id="org.eclipse.tools.templates.core.source" version="0.0.0"/>
<bundle id="org.eclipse.tools.templates.freemarker" version="0.0.0"/>

View file

@ -72,6 +72,7 @@
<unit id="org.junit.jupiter.api" version="0.0.0"/>
<unit id="org.mockito" version="0.0.0"/>
<unit id="org.slf4j.impl.log4j12" version="0.0.0"/>
<unit id="org.yaml.snakeyaml" version="0.0.0"/>
<repository location="https://download.eclipse.org/tools/orbit/downloads/2020-12/"/>
</location>
<location includeAllPlatforms="false" includeConfigurePhase="false" includeMode="planner" includeSource="true" type="InstallableUnit">
@ -88,4 +89,4 @@
-ea</vmArgs>
<programArgs>-consolelog</programArgs>
</launcherArgs>
</target>
</target>