private VendorSample ParseSingleConfiguration(XmlDocument cproject,
                                                      XmlDocument project,
                                                      XmlElement cconfiguration,
                                                      string optionalProjectRootForLocatingHeaders,
                                                      string cprojectFileDir,
                                                      string boardName,
                                                      MultiConfigurationContext multiConfigurationContext,
                                                      ProjectSubtype subtype)
        {
            VendorSample result = new VendorSample
            {
                UserFriendlyName = (project.SelectSingleNode("projectDescription/name") as XmlElement)?.InnerText ?? throw new Exception("Failed to determine sample name"),
                                         NoImplicitCopy = true,
            };

            if (optionalProjectRootForLocatingHeaders == null)
            {
                optionalProjectRootForLocatingHeaders = cprojectFileDir;
            }

            CommonConfigurationOptions opts;

            if (subtype == ProjectSubtype.Auto)
            {
                var toolchainConfigNode = cconfiguration.SelectSingleNode(ToolchainConfigKey) as XmlNode ?? throw new Exception("Failed to locate the configuration node");
                if (toolchainConfigNode.SelectSingleNode("tool[starts-with(@id, 'fr.ac6.managedbuild.tool.gnu.cross.c.compiler')]") != null)
                {
                    subtype = ProjectSubtype.SW4STM32;
                }
                else if (toolchainConfigNode.SelectSingleNode("tool[@superClass = 'com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler']") != null)
                {
                    subtype = ProjectSubtype.STM32CubeIDE;
                }
                else
                {
                    throw new Exception("Failed to detect the project type");
                }
            }

            if (subtype == ProjectSubtype.SW4STM32)
            {
                opts = ExtractSW4STM32Options(cproject, project, cconfiguration, cprojectFileDir);
            }
            else
            {
                opts = ExtractSTM32CubeIDEOptions(cproject, project, cconfiguration, cprojectFileDir);
            }

            var mcu = opts.MCU;

            if (mcu.EndsWith("x"))
            {
                if (mcu.StartsWith("STM32MP1"))
                {
                    mcu = mcu.Substring(0, mcu.Length - 3) + "_M4";
                }
                else
                {
                    mcu = mcu.Remove(mcu.Length - 2, 2);
                }
            }
            else if (mcu.EndsWith("xP"))
            {
                mcu = mcu.Remove(mcu.Length - 3, 3);
            }

            AdjustMCUName(ref mcu);

            if (multiConfigurationContext is MultiConfigurationContext.MultiCore mc)
            {
                mcu += mc.DeviceSuffix;
                result.InternalUniqueID += mc.DeviceSuffix;
                result.UserFriendlyName += mc.UserFriendlyNameSuffix;
            }

            ValidateFinalMCUName(mcu);

            result.DeviceID           = mcu;
            result.SourceFiles        = opts.SourceFiles.Select(f => f.FullPath).Concat(opts.Libraries).Distinct().ToArray();
            result.IncludeDirectories = opts.IncludeDirectories;
            result.PreprocessorMacros = opts.PreprocessorMacros;
            result.BoardName          = boardName ?? opts.BoardName;
            result.LinkerScript       = opts.LinkerScript;
            OnVendorSampleParsed(result, opts);

            List <PropertyDictionary2.KeyValue> mcuConfig = new List <PropertyDictionary2.KeyValue>();

            if (opts.LDFLAGS?.Contains("rdimon.specs") == true)
            {
                mcuConfig.Add(new PropertyDictionary2.KeyValue {
                    Key = "com.sysprogs.toolchainoptions.arm.libctype", Value = "--specs=rdimon.specs"
                });
            }

            try
            {
                if (result.SourceFiles.Select(f => Path.GetFileName(f)).FirstOrDefault(f => f.StartsWith("startup_", StringComparison.InvariantCultureIgnoreCase) && f.EndsWith(".s", StringComparison.InvariantCultureIgnoreCase)) != null)
                {
                    mcuConfig.Add(new PropertyDictionary2.KeyValue {
                        Key = "com.sysprogs.mcuoptions.ignore_startup_file", Value = "1"
                    });
                }
            }
            catch { }

            if (mcuConfig.Count > 0)
            {
                result.Configuration.MCUConfiguration = new PropertyDictionary2
                {
                    Entries = mcuConfig.ToArray()
                };
            }

            result.Path = Path.GetDirectoryName(optionalProjectRootForLocatingHeaders);

            HashSet <string> possibleIncludeDirs = new HashSet <string>();

            foreach (var src in result.SourceFiles)
            {
                int idx = src.IndexOf(@"\Src\", StringComparison.InvariantCultureIgnoreCase);
                if (idx == -1)
                {
                    continue;
                }
                string possibleInc = src.Substring(0, idx) + @"\Inc";
                possibleIncludeDirs.Add(possibleInc);
            }

            string possibleRoot = optionalProjectRootForLocatingHeaders;

            for (; ;)
            {
                string possibleIncDir = Path.Combine(possibleRoot, "Inc");
                if (Directory.Exists(possibleIncDir))
                {
                    possibleIncludeDirs.Add(possibleIncDir);
                    break;
                }

                var baseDir = Path.GetDirectoryName(possibleRoot);
                if (string.IsNullOrEmpty(baseDir) || baseDir == possibleRoot)
                {
                    break;
                }

                possibleRoot = baseDir;
            }

            List <string> headers = new List <string>();

            foreach (var possibleIncDir in possibleIncludeDirs)
            {
                if (Directory.Exists(possibleIncDir))
                {
                    headers.AddRange(Directory.GetFiles(possibleIncDir, "*.h", SearchOption.AllDirectories));
                }
            }

            result.HeaderFiles = headers.ToArray();
            return(result);
        }
        ConfigurationWithContext[] DetectConfigurationContexts(XmlDocument cproject, string projectFile)
        {
            var cconfigurationNodes = cproject.SelectNodes("cproject/storageModule[@moduleId='org.eclipse.cdt.core.settings']/cconfiguration").OfType <XmlElement>().ToArray();

            if (cconfigurationNodes.Length == 0)
            {
                throw new Exception("No 'cconfiguration' nodes found");
            }

            if (cconfigurationNodes.Length > 1)
            {
                var nonReleaseNodes = cconfigurationNodes.Where(n => !n.GetAttribute("id").Contains(".release.")).ToArray();
                if (nonReleaseNodes.Length > 0)
                {
                    cconfigurationNodes = nonReleaseNodes;
                }
            }

            List <ConfigurationWithContext> result = new List <ConfigurationWithContext>();

            foreach (var cconfiguration in cconfigurationNodes)
            {
                MultiConfigurationContext mctx = null;
                if (cconfigurationNodes.Length > 1)
                {
                    if (cconfigurationNodes.Length != 2)
                    {
                        throw new Exception("Unexpected configuration count for " + projectFile);
                    }

                    string artifactName = cconfiguration.SelectSingleNode("storageModule[@moduleId='cdtBuildSystem']/configuration/@artifactName")?.Value;
                    if (artifactName.EndsWith("_CM4"))
                    {
                        mctx = new MultiConfigurationContext.MultiCore {
                            DeviceSuffix = "_M4", UserFriendlyNameSuffix = " (Cortex-M4 Core)"
                        }
                    }
                    ;
                    else if (artifactName.EndsWith("_CM7"))
                    {
                        mctx = new MultiConfigurationContext.MultiCore {
                            DeviceSuffix = "", UserFriendlyNameSuffix = " (Cortex-M7 Core)"
                        }
                    }
                    ;
                    else
                    {
                        throw new Exception("Don't know how to interpret the difference between multiple configurations for a project. Please review it manually.");
                    }
                }

                result.Add(new ConfigurationWithContext {
                    CConfiguration = cconfiguration, Context = mctx
                });
            }

            if (result.Select(c => c.Context?.IDSuffix ?? "").Distinct().Count() != result.Count)
            {
                OnMultipleConfigurationsFound(projectFile);
                result = result.Take(1).ToList();
            }

            return(result.ToArray());
        }