Bug 567488: Use command-line options to pass to cmake ICMakeProperties object

Change-Id: I4d6383ce240e6f2b8d7079f281ef7c2e56ea93a6
Signed-off-by: Martin Weber <fifteenknots505@gmail.com>
diff --git a/cmake/org.eclipse.cdt.cmake.core/META-INF/MANIFEST.MF b/cmake/org.eclipse.cdt.cmake.core/META-INF/MANIFEST.MF
index c1092ed..7a0592d 100644
--- a/cmake/org.eclipse.cdt.cmake.core/META-INF/MANIFEST.MF
+++ b/cmake/org.eclipse.cdt.cmake.core/META-INF/MANIFEST.MF
@@ -20,3 +20,4 @@
  org.eclipse.cdt.cmake.core.properties
 Automatic-Module-Name: org.eclipse.cdt.cmake.core
 Bundle-Localization: plugin
+Import-Package: org.eclipse.core.variables
diff --git a/cmake/org.eclipse.cdt.cmake.core/src/org/eclipse/cdt/cmake/core/internal/CMakeBuildConfiguration.java b/cmake/org.eclipse.cdt.cmake.core/src/org/eclipse/cdt/cmake/core/internal/CMakeBuildConfiguration.java
index 1970463..4046625 100644
--- a/cmake/org.eclipse.cdt.cmake.core/src/org/eclipse/cdt/cmake/core/internal/CMakeBuildConfiguration.java
+++ b/cmake/org.eclipse.cdt.cmake.core/src/org/eclipse/cdt/cmake/core/internal/CMakeBuildConfiguration.java
@@ -18,7 +18,6 @@
 import java.nio.file.SimpleFileVisitor;
 import java.nio.file.attribute.BasicFileAttributes;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -27,6 +26,10 @@
 
 import org.eclipse.cdt.cmake.core.ICMakeToolChainFile;
 import org.eclipse.cdt.cmake.core.ICMakeToolChainManager;
+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.eclipse.cdt.cmake.core.properties.IOsOverrides;
 import org.eclipse.cdt.cmake.is.core.CompileCommandsJsonParser;
 import org.eclipse.cdt.cmake.is.core.IIndexerInfoConsumer;
 import org.eclipse.cdt.cmake.is.core.ParseRequest;
@@ -56,6 +59,8 @@
 import org.eclipse.core.runtime.NullProgressMonitor;
 import org.eclipse.core.runtime.Platform;
 import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.core.variables.IStringVariableManager;
+import org.eclipse.core.variables.VariablesPlugin;
 
 public class CMakeBuildConfiguration extends CBuildConfiguration {
 
@@ -66,6 +71,11 @@
 	public static final String CLEAN_COMMAND = "cmake.command.clean"; //$NON-NLS-1$
 
 	private ICMakeToolChainFile toolChainFile;
+
+	private final CMakePropertiesController pc = new CMakePropertiesController(() -> {
+		deleteCMakeCache = true;
+	});
+
 	private Map<IResource, IScannerInfo> infoPerResource;
 	/** whether one of the CMakeLists.txt files in the project has been
 	 * modified and saved by the user since the last build.<br>
@@ -76,6 +86,9 @@
 	 * To work around that, we run cmake in advance with its dedicated working error parser.
 	 */
 	private boolean cmakeListsModified;
+	/** whether we have to delete file CMakeCache.txt to avoid complaints by cmake
+	 */
+	private boolean deleteCMakeCache;
 
 	public CMakeBuildConfiguration(IBuildConfiguration config, String name) throws CoreException {
 		super(config, name);
@@ -94,10 +107,16 @@
 		this.toolChainFile = toolChainFile;
 	}
 
+	/** Gets the tool-chain description file to pass to the cmake command-line.
+	 *
+	 * @return the tool-chain file or <code>null</code> if cmake should take the native (i.e. the tools first found on
+	 * the executable search path aka $path)
+	 */
 	public ICMakeToolChainFile getToolChainFile() {
 		return toolChainFile;
 	}
 
+	@SuppressWarnings("unused") // kept for reference of the property names
 	private boolean isLocal() throws CoreException {
 		IToolChain toolchain = getToolChain();
 		return (Platform.getOS().equals(toolchain.getProperty(IToolChain.ATTR_OS))
@@ -110,81 +129,36 @@
 			throws CoreException {
 		IProject project = getProject();
 
-		try {
-			String generator = getProperty(CMAKE_GENERATOR);
-			if (generator == null) {
-				generator = "Ninja"; //$NON-NLS-1$
-			}
+		project.deleteMarkers(ICModelMarker.C_MODEL_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE);
+		infoPerResource = new HashMap<>();
 
-			project.deleteMarkers(ICModelMarker.C_MODEL_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE);
-			infoPerResource = new HashMap<>();
+		try {
 
 			ConsoleOutputStream infoStream = console.getInfoStream();
 
 			Path buildDir = getBuildDirectory();
 
-			infoStream.write(String.format(Messages.CMakeBuildConfiguration_BuildingIn, buildDir.toString()));
-
-			// Make sure we have a toolchain file if cross
-			if (toolChainFile == null && !isLocal()) {
-				ICMakeToolChainManager manager = Activator.getService(ICMakeToolChainManager.class);
-				toolChainFile = manager.getToolChainFileFor(getToolChain());
-
-				if (toolChainFile == null) {
-					// error
-					console.getErrorStream().write(Messages.CMakeBuildConfiguration_NoToolchainFile);
-					return null;
-				}
-			}
-
 			boolean runCMake = cmakeListsModified;
-			if (!runCMake) {
-				switch (generator) {
-				case "Ninja": //$NON-NLS-1$
-					runCMake = !Files.exists(buildDir.resolve("build.ninja")); //$NON-NLS-1$
-					break;
-				case "Unix Makefiles": //$NON-NLS-1$
-					runCMake = !Files.exists(buildDir.resolve("Makefile")); //$NON-NLS-1$
-					break;
-				default:
-					runCMake = !Files.exists(buildDir.resolve("CMakeFiles")); //$NON-NLS-1$
-				}
+			if (deleteCMakeCache) {
+				Files.deleteIfExists(buildDir.resolve("CMakeCache.txt")); //$NON-NLS-1$
+				deleteCMakeCache = false;
+				runCMake = true;
 			}
 
+			ICMakeProperties cmakeProperties = pc.load();
+			runCMake |= !Files.exists(buildDir.resolve("CMakeCache.txt")); //$NON-NLS-1$
+
+			IOsOverrides overrides = extractCMakeOsOverrides(cmakeProperties);
+			CMakeGenerator generator = overrides.getGenerator();
+			if (!runCMake) {
+				runCMake |= !Files.exists(buildDir.resolve(generator.getMakefileName()));
+			}
 			if (runCMake) {
 				CMakeBuildConfiguration.deleteCMakeErrorMarkers(project);
 
 				infoStream.write(String.format(Messages.CMakeBuildConfiguration_Configuring, buildDir));
-				// clean output to make sure there is no content
-				// incompatible with current settings (cmake config would fail)
-				cleanBuildDirectory(buildDir);
-
-				List<String> command = new ArrayList<>();
-
-				command.add("cmake"); //$NON-NLS-1$
-				command.add("-G"); //$NON-NLS-1$
-				command.add(generator);
-
-				if (toolChainFile != null) {
-					command.add("-DCMAKE_TOOLCHAIN_FILE=" + toolChainFile.getPath().toString()); //$NON-NLS-1$
-				}
-
-				switch (getLaunchMode()) {
-				// TODO what to do with other modes
-				case "debug": //$NON-NLS-1$
-					command.add("-DCMAKE_BUILD_TYPE=Debug"); //$NON-NLS-1$
-					break;
-				case "run": //$NON-NLS-1$
-					command.add("-DCMAKE_BUILD_TYPE=Release"); //$NON-NLS-1$
-					break;
-				}
-				command.add("-DCMAKE_EXPORT_COMPILE_COMMANDS=ON"); //$NON-NLS-1$
-
-				String userArgs = getProperty(CMAKE_ARGUMENTS);
-				if (userArgs != null) {
-					command.addAll(Arrays.asList(userArgs.trim().split("\\s+"))); //$NON-NLS-1$
-				}
-
+				List<String> command = makeCMakeCommandline(cmakeProperties, overrides);
+				// tell cmake where its script is located..
 				IContainer srcFolder = project;
 				command.add(new File(srcFolder.getLocationURI()).getAbsolutePath());
 
@@ -197,6 +171,9 @@
 					ParsingConsoleOutputStream errStream = new ParsingConsoleOutputStream(console.getErrorStream(),
 							errorParser);
 					IConsole errConsole = new CMakeConsoleWrapper(console, errStream);
+					// TODO startBuildProcess() calls java.lang.ProcessBuilder.
+					// Use org.eclipse.cdt.core.ICommandLauncher
+					// in order to run builds in a container.
 					Process p = startBuildProcess(command, new IEnvironmentVariable[0], workingDir, errConsole,
 							monitor);
 					if (p == null) {
@@ -209,11 +186,16 @@
 				cmakeListsModified = false;
 			}
 
+			// parse compile_commands.json file
+			processCompileCommandsFile(console, monitor);
+
+			infoStream.write(String.format(Messages.CMakeBuildConfiguration_BuildingIn, buildDir.toString()));
+			// run the build tool...
 			try (ErrorParserManager epm = new ErrorParserManager(project, getBuildDirectoryURI(), this,
 					getToolChain().getErrorParserIds())) {
 				epm.setOutputStream(console.getOutputStream());
 
-				List<String> command = new ArrayList<>();
+				List<String> command = makeCMakeBuildCommandline(cmakeProperties, overrides, "all"); //$NON-NLS-1$
 
 				String envStr = getProperty(CMAKE_ENV);
 				List<IEnvironmentVariable> envVars = new ArrayList<>();
@@ -229,23 +211,12 @@
 					}
 				}
 
-				String buildCommand = getProperty(BUILD_COMMAND);
-				if (buildCommand == null) {
-					command.add("cmake"); //$NON-NLS-1$
-					command.add("--build"); //$NON-NLS-1$
-					command.add("."); //$NON-NLS-1$
-					if ("Ninja".equals(generator)) { //$NON-NLS-1$
-						command.add("--"); //$NON-NLS-1$
-						command.add("-v"); //$NON-NLS-1$
-					}
-				} else {
-					command.addAll(Arrays.asList(buildCommand.split(" "))); //$NON-NLS-1$
-				}
-
 				infoStream.write(String.join(" ", command) + '\n'); //$NON-NLS-1$
 
 				org.eclipse.core.runtime.Path workingDir = new org.eclipse.core.runtime.Path(
 						getBuildDirectory().toString());
+				// TODO startBuildProcess() calls java.lang.ProcessBuilder. Use org.eclipse.cdt.core.ICommandLauncher
+				// in order to run builds in a container.
 				Process p = startBuildProcess(command, envVars.toArray(new IEnvironmentVariable[0]), workingDir,
 						console, monitor);
 				if (p == null) {
@@ -257,11 +228,6 @@
 
 				project.refreshLocal(IResource.DEPTH_INFINITE, monitor);
 
-				// parse compile_commands.json file
-				// built-ins detection output goes to the build console, if the user requested
-				// output
-				processCompileCommandsFile(console, monitor);
-
 				infoStream.write(String.format(Messages.CMakeBuildConfiguration_BuildingComplete, epm.getErrorCount(),
 						epm.getWarningCount(), buildDir.toString()));
 			}
@@ -273,14 +239,34 @@
 		}
 	}
 
+	/**
+	 * @param cmakeProperties
+	 * @return
+	 */
+	private IOsOverrides extractCMakeOsOverrides(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 overrides;
+	}
+
 	@Override
 	public void clean(IConsole console, IProgressMonitor monitor) throws CoreException {
 		IProject project = getProject();
 		try {
-			String generator = getProperty(CMAKE_GENERATOR);
 
 			project.deleteMarkers(ICModelMarker.C_MODEL_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE);
 
+			ICMakeProperties cmakeProperties = pc.load();
+			IOsOverrides overrides = extractCMakeOsOverrides(cmakeProperties);
+			List<String> command = makeCMakeBuildCommandline(cmakeProperties, overrides, "clean"); //$NON-NLS-1$
 			ConsoleOutputStream outStream = console.getOutputStream();
 
 			Path buildDir = getBuildDirectory();
@@ -290,27 +276,13 @@
 				return;
 			}
 
-			List<String> command = new ArrayList<>();
-			String cleanCommand = getProperty(CLEAN_COMMAND);
-			if (cleanCommand == null) {
-				if (generator == null || generator.equals("Ninja")) { //$NON-NLS-1$
-					command.add("ninja"); //$NON-NLS-1$
-					command.add("clean"); //$NON-NLS-1$
-				} else {
-					command.add("make"); //$NON-NLS-1$
-					command.add("clean"); //$NON-NLS-1$
-				}
-			} else {
-				command.addAll(Arrays.asList(cleanCommand.split(" "))); //$NON-NLS-1$
-			}
-
-			IEnvironmentVariable[] env = new IEnvironmentVariable[0];
-
 			outStream.write(String.join(" ", command) + '\n'); //$NON-NLS-1$
 
 			org.eclipse.core.runtime.Path workingDir = new org.eclipse.core.runtime.Path(
 					getBuildDirectory().toString());
-			Process p = startBuildProcess(command, env, workingDir, console, monitor);
+			// TODO startBuildProcess() calls java.lang.ProcessBuilder. Use org.eclipse.cdt.core.ICommandLauncher
+			// in order to run builds in a container.
+			Process p = startBuildProcess(command, new IEnvironmentVariable[0], workingDir, console, monitor);
 			if (p == null) {
 				console.getErrorStream().write(String.format(Messages.CMakeBuildConfiguration_Failure, "")); //$NON-NLS-1$
 				return;
@@ -328,6 +300,130 @@
 	}
 
 	/**
+	 * Build the command-line for cmake. The first argument will be the
+	 * cmake-command.
+	 *
+	 * @throws CoreException
+	 */
+	private List<String> makeCMakeCommandline(ICMakeProperties cmakeProps, IOsOverrides osOverrideProps)
+			throws CoreException {
+		List<String> args = new ArrayList<>();
+
+		// default for all OSes (maybe replaced later)
+		args.add("cmake"); //$NON-NLS-1$
+		/* add general settings */
+		if (cmakeProps.isWarnNoDev())
+			args.add("-Wno-dev"); //$NON-NLS-1$
+		if (cmakeProps.isDebugTryCompile())
+			args.add("--debug-trycompile"); //$NON-NLS-1$
+		if (cmakeProps.isDebugOutput())
+			args.add("--debug-output"); //$NON-NLS-1$
+		if (cmakeProps.isTrace())
+			args.add("--trace"); //$NON-NLS-1$
+		if (cmakeProps.isWarnUnitialized())
+			args.add("--warn-unitialized"); //$NON-NLS-1$
+		if (cmakeProps.isWarnUnused())
+			args.add("--warn-unused"); //$NON-NLS-1$
+		{
+			String file = cmakeProps.getCacheFile();
+			if (!(file == null || file.isBlank())) {
+				args.add("-C"); //$NON-NLS-1$
+				args.add(file);
+			}
+		}
+		if (toolChainFile != null) {
+			args.add("-DCMAKE_TOOLCHAIN_FILE=" + toolChainFile.getPath().toString()); //$NON-NLS-1$
+		}
+
+		appendCMakeArguments(args, cmakeProps.getExtraArguments());
+
+		/* add settings for the operating system we are running under */
+		appendCMakeOsOverrideArgs(args, osOverrideProps);
+
+		/* add our requirements */
+		{
+			// set argument for build type..
+			String bt = cmakeProps.getBuildType();
+			if (!(bt == null || bt.isBlank())) {
+				args.add("-DCMAKE_BUILD_TYPE=" + bt); //$NON-NLS-1$
+			}
+			// tell cmake to write compile commands to a JSON file
+			args.add("-DCMAKE_EXPORT_COMPILE_COMMANDS=ON"); //$NON-NLS-1$
+		}
+		return args;
+	}
+
+	/**
+	 * Build the command-line for cmake to build the project. The first argument will be the
+	 * cmake-command.
+	 *
+	 * @throws CoreException
+	 */
+	private List<String> makeCMakeBuildCommandline(ICMakeProperties cmakeProps, IOsOverrides osOverrides,
+			String buildscriptTarget) throws CoreException {
+		List<String> args = new ArrayList<>();
+
+		if (osOverrides.getUseDefaultCommand()) {
+			args.add("cmake"); //$NON-NLS-1$
+		} else {
+			IStringVariableManager varManager = VariablesPlugin.getDefault().getStringVariableManager();
+			String cmd = varManager.performStringSubstitution(osOverrides.getCommand());
+			args.add(cmd);
+		}
+		args.add("--build"); //$NON-NLS-1$
+		args.add("."); //$NON-NLS-1$
+		args.add("--target"); //$NON-NLS-1$
+		args.add(buildscriptTarget);
+		// TODO parallel build: use CMAKE_BUILD_PARALLEL_LEVEL envvar (since cmake 3.12)
+		// TODO verbose build: use VERBOSE envvar (since cmake 3.14)
+		// TODO stop on first error: query CMakeGenerator object for argument
+		return args;
+	}
+
+	/**
+	 * Appends the additional arguments to pass on the cmake command-line. Performs variable substitutions.
+	 *
+	 * @param argList
+	 *        the list to append cmake-arguments to
+	 * @param moreArgs
+	 *        the arguments to substitute and append
+	 * @throws CoreException
+	 *         if unable to resolve the value of one or more variables
+	 */
+	private void appendCMakeArguments(List<String> argList, final List<String> moreArgs) throws CoreException {
+		IStringVariableManager mgr = VariablesPlugin.getDefault().getStringVariableManager();
+		for (String arg : moreArgs) {
+			String expanded = mgr.performStringSubstitution(arg);
+			argList.add(expanded);
+		}
+	}
+
+	/**
+	 * Appends arguments specific to the given OS preferences for build-script generation.
+	 * The first argument in the list will be replaced by the cmake command from the specified preferences,
+	 * if given.
+	 *
+	 * @param args
+	 *        the list to append cmake-arguments to.
+	 * @param prefs
+	 *        the generic OS specific cmake build properties to convert and append.
+	 * @throws CoreException
+	 *         if unable to resolve the value of one or more variables
+	 */
+	private void appendCMakeOsOverrideArgs(List<String> args, final IOsOverrides prefs) throws CoreException {
+		// replace cmake command, if given
+		if (!prefs.getUseDefaultCommand()) {
+			IStringVariableManager varManager = VariablesPlugin.getDefault().getStringVariableManager();
+			String cmd = varManager.performStringSubstitution(prefs.getCommand());
+			args.set(0, cmd);
+		}
+		args.add("-G"); //$NON-NLS-1$
+		final CMakeGenerator generator = prefs.getGenerator();
+		args.add(generator.getCMakeName());
+		appendCMakeArguments(args, prefs.getExtraArguments());
+	}
+
+	/**
 	 * @param console the console to print the compiler output during built-ins
 	 *                detection to or <code>null</code> if no separate console is to
 	 *                be allocated. Ignored if workspace preferences indicate that
@@ -365,12 +461,17 @@
 		}
 	}
 
-	private void cleanBuildDirectory(Path buildDir) throws IOException {
-		if (!Files.exists(buildDir))
-			return;
-		if (Files.isDirectory(buildDir))
-			cleanDirectory(buildDir);
-		// TODO: not a directory should we do something?
+	// interface IAdaptable
+	@Override
+	@SuppressWarnings("unchecked")
+	public <T> T getAdapter(Class<T> adapter) {
+		T adapter0 = super.getAdapter(adapter);
+		if (adapter0 == null) {
+			if (ICMakePropertiesController.class.equals(adapter)) {
+				adapter0 = (T) pc;
+			}
+		}
+		return adapter0;
 	}
 
 	/**
@@ -426,7 +527,8 @@
 						IResource resource = resourceDelta.getResource();
 						if (resource.getType() == IResource.FILE) {
 							String name = resource.getName();
-							if (name.equals("CMakeLists.txt") || name.endsWith(".cmake")) { //$NON-NLS-1$ //$NON-NLS-2$
+							if (!resource.isDerived(IResource.CHECK_ANCESTORS)
+									&& (name.equals("CMakeLists.txt") || name.endsWith(".cmake"))) { //$NON-NLS-1$ //$NON-NLS-2$
 								cmakeListsModified = true;
 								return false; // stop processing
 							}
diff --git a/cmake/org.eclipse.cdt.cmake.core/src/org/eclipse/cdt/cmake/core/internal/CMakePropertiesController.java b/cmake/org.eclipse.cdt.cmake.core/src/org/eclipse/cdt/cmake/core/internal/CMakePropertiesController.java
new file mode 100644
index 0000000..ed234f0
--- /dev/null
+++ b/cmake/org.eclipse.cdt.cmake.core/src/org/eclipse/cdt/cmake/core/internal/CMakePropertiesController.java
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * 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 java.util.List;
+import java.util.Objects;
+import java.util.function.BinaryOperator;
+import java.util.function.Predicate;
+
+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;
+
+/**
+ * A {@code ICMakePropertiesController} that monitors modifications to the project properties that force
+ * us to delete file CMakeCache.txt to avoid complaints by cmake.
+ * @author Martin Weber
+ */
+class CMakePropertiesController implements ICMakePropertiesController {
+
+	private final Runnable cmakeCacheDirtyMarker;
+
+	private String cacheFile;
+	private List<String> extraArguments;
+	private CMakeGenerator generatorLinux;
+	private List<String> extraArgumentsLinux;
+	private CMakeGenerator generatorWindows;
+	private List<String> extraArgumentsWindows;
+	private String buildType;
+
+	/** Creates a new CMakePropertiesController object.
+	 *
+	 * @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) {
+		this.cmakeCacheDirtyMarker = Objects.requireNonNull(cmakeCacheDirtyMarker);
+	}
+
+	@Override
+	public ICMakeProperties load() {
+		// TODO implement load()
+		CMakePropertiesBean props = new CMakePropertiesBean();
+
+		setupModifyDetection(props);
+		return props;
+	}
+
+	@Override
+	public void save(ICMakeProperties properties) {
+		// 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())) {
+			cmakeCacheDirtyMarker.run(); // must remove cmake cachefile
+		}
+		// TODO implement save()
+		setupModifyDetection(properties);
+	}
+
+	/** Sets up detection of modifications that force us to delete file CMakeCache.txt to avoid complaints by cmake
+	 */
+	private void setupModifyDetection(ICMakeProperties properties) {
+		buildType = properties.getBuildType();
+		cacheFile = properties.getCacheFile();
+		extraArguments = properties.getExtraArguments();
+		generatorLinux = properties.getLinuxOverrides().getGenerator();
+		extraArgumentsLinux = properties.getLinuxOverrides().getExtraArguments();
+		generatorWindows = properties.getWindowsOverrides().getGenerator();
+		extraArgumentsWindows = properties.getWindowsOverrides().getExtraArguments();
+	}
+
+	private boolean extraArgumentsChange(List<String> args1, List<String> args2) {
+		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);
+		if (!Objects.equals(a1, a2)) {
+			return true;
+		}
+		return false;
+	}
+}
\ No newline at end of file
diff --git a/cmake/org.eclipse.cdt.cmake.core/src/org/eclipse/cdt/cmake/core/internal/Messages.java b/cmake/org.eclipse.cdt.cmake.core/src/org/eclipse/cdt/cmake/core/internal/Messages.java
index d11714f..5cee4eb 100644
--- a/cmake/org.eclipse.cdt.cmake.core/src/org/eclipse/cdt/cmake/core/internal/Messages.java
+++ b/cmake/org.eclipse.cdt.cmake.core/src/org/eclipse/cdt/cmake/core/internal/Messages.java
@@ -21,7 +21,6 @@
 	public static String CMakeBuildConfiguration_Cleaning;
 	public static String CMakeBuildConfiguration_Configuring;
 	public static String CMakeBuildConfiguration_NotFound;
-	public static String CMakeBuildConfiguration_NoToolchainFile;
 	public static String CMakeBuildConfiguration_ProcCompCmds;
 	public static String CMakeBuildConfiguration_ProcCompJson;
 	public static String CMakeBuildConfiguration_Failure;
diff --git a/cmake/org.eclipse.cdt.cmake.core/src/org/eclipse/cdt/cmake/core/internal/messages.properties b/cmake/org.eclipse.cdt.cmake.core/src/org/eclipse/cdt/cmake/core/internal/messages.properties
index 5bd6b24..fef203a 100644
--- a/cmake/org.eclipse.cdt.cmake.core/src/org/eclipse/cdt/cmake/core/internal/messages.properties
+++ b/cmake/org.eclipse.cdt.cmake.core/src/org/eclipse/cdt/cmake/core/internal/messages.properties
@@ -15,7 +15,6 @@
 CMakeBuildConfiguration_Configuring=Configuring in: %s\n
 CMakeBuildConfiguration_Cleaning=Cleaning %s
 CMakeBuildConfiguration_NotFound=CMakeFiles not found. Assuming clean.
-CMakeBuildConfiguration_NoToolchainFile=No CMake toolchain file found for this target.
 CMakeBuildConfiguration_ProcCompCmds=Processing compile commands %s
 CMakeBuildConfiguration_ProcCompJson=Processing compile_commands.json
 CMakeBuildConfiguration_Failure=Failure running cmake: %s\n