Beispiel #1
0
 static void FillFlatFileCollectionRecursively(ImportedExternalProject.ConstructedVirtualDirectory dir, List <FlatFileReference> fileList)
 {
     foreach (var file in dir.Files ?? new List <ImportedExternalProject.ImportedFile>())
     {
         fileList.Add(new FlatFileReference {
             Directory = dir, File = file
         });
     }
     foreach (var subdir in (dir.Subdirectories ?? new List <ImportedExternalProject.VirtualDirectory>()).OfType <ImportedExternalProject.ConstructedVirtualDirectory>())
     {
         FillFlatFileCollectionRecursively(subdir, fileList);
     }
 }
Beispiel #2
0
        private void ImportProjectFolderRecursively(XmlElement projectOrFolder,
                                                    ImportedExternalProject.ConstructedVirtualDirectory constructedDir,
                                                    ProjectImportParameters parameters,
                                                    IVariableExpander expander,
                                                    IProjectImportService service)
        {
            foreach (var el in projectOrFolder.ChildNodes.OfType <XmlElement>())
            {
                if (el.Name == "file")
                {
                    string relPath = ExpandVariables(el.GetAttribute("file_name"), expander, service);
                    if (!string.IsNullOrEmpty(relPath) && !relPath.EndsWith(".vec"))
                    {
                        string fullPath = Path.Combine(Path.GetDirectoryName(parameters.ProjectFile), relPath);
                        constructedDir.AddFile(fullPath, relPath.EndsWith(".h", StringComparison.InvariantCultureIgnoreCase));
                    }
                }
                else if (el.Name == "folder")
                {
                    string name = el.GetAttribute("Name");
                    if (string.IsNullOrEmpty(name))
                    {
                        name = "Subfolder";
                    }

                    if (name == "Source Files" || name == "Header Files")
                    {
                        //Visual Studio already provides filters for source/header files, so we don't need to specify them explicitly
                        ImportProjectFolderRecursively(el, constructedDir, parameters, expander, service);
                    }
                    else
                    {
                        ImportProjectFolderRecursively(el, constructedDir.ProvideSudirectory(name), parameters, expander, service);
                    }
                }
            }
        }
Beispiel #3
0
        //STM32CubeMX v5.3.0 does not reference some of the FreeRTOS-specific files and references an incorrect system file.
        //The method below detects and fixes this condition.
        static void ApplyFreeRTOSFixes(ImportedExternalProject.ConstructedVirtualDirectory dir, ref string[] includeDirs, ref PropertyDictionary2 mcuConfiguration)
        {
            List <FlatFileReference> allFiles = new List <FlatFileReference>();

            FillFlatFileCollectionRecursively(dir, allFiles);

            var fileListsByName = allFiles.GroupBy(f => Path.GetFileName(f.File.FullPath), StringComparer.InvariantCultureIgnoreCase).ToDictionary(g => g.Key, StringComparer.InvariantCultureIgnoreCase);

            if (!fileListsByName.TryGetValue("queue.c", out var queueFiles) || queueFiles.Count() == 0)
            {
                return; //Could not find the FreeRTOS base directory
            }
            var    queueCFile = queueFiles.First();
            string baseDir    = Path.GetFullPath(Path.GetDirectoryName(queueCFile.File.FullPath));

            string portFile = FindAndAddFileIfMissing(fileListsByName, "port.c", Path.Combine(baseDir, "portable"), queueCFile.Directory, ref includeDirs);

            FindAndAddFileIfMissing(fileListsByName, "cmsis_os.c", baseDir, queueCFile.Directory, ref includeDirs);

            foreach (var file in fileListsByName.SelectMany(g => g.Value))
            {
                if (Path.GetFileName(file.File.FullPath).StartsWith("system_stm32", StringComparison.InvariantCultureIgnoreCase) && !File.Exists(file.File.FullPath))
                {
                    //Found an incorrectly referenced system file (typically Source\Templates\system_stm32f7xx.c). Replace it with the real path.

                    string foundReplacement = null;

                    foreach (var f2 in fileListsByName.SelectMany(g => g.Value))
                    {
                        string candidatePath = Path.Combine(Path.GetDirectoryName(f2.File.FullPath), Path.GetFileName(file.File.FullPath));
                        if (File.Exists(candidatePath))
                        {
                            foundReplacement = candidatePath;
                            break;
                        }
                    }

                    if (foundReplacement != null)
                    {
                        file.File.FullPath = foundReplacement;
                    }
                }
            }

            if (portFile != null)
            {
                string relPath = portFile.Substring(baseDir.Length);
                if (relPath.Contains("ARM_CM7") || relPath.Contains("ARM_CM4F"))
                {
                    if (mcuConfiguration == null)
                    {
                        mcuConfiguration = new PropertyDictionary2();
                    }
                    if (mcuConfiguration.Entries == null)
                    {
                        mcuConfiguration.Entries = new PropertyDictionary2.KeyValue[0];
                    }

                    mcuConfiguration.Entries = mcuConfiguration.Entries.Concat(new[] { new PropertyDictionary2.KeyValue {
                                                                                           Key = "com.sysprogs.bspoptions.arm.floatmode", Value = "-mfloat-abi=hard"
                                                                                       } }).ToArray();
                }
            }
        }
Beispiel #4
0
        public ImportedExternalProject ImportProject(ProjectImportParameters parameters, IProjectImportService service)
        {
            XmlDocument xml = new XmlDocument();

            xml.Load(parameters.ProjectFile);

            string deviceName = (xml.SelectSingleNode("package/generators/generator/select/@Dname") as XmlAttribute)?.Value;

            if (deviceName == null)
            {
                throw new Exception("Failed to extract the device name from " + deviceName);
            }

            HashSet <string> allHeaderDirs = new HashSet <string>(StringComparer.InvariantCultureIgnoreCase);
            string           baseDir       = Path.GetDirectoryName(parameters.ProjectFile);

            ImportedExternalProject.ConstructedVirtualDirectory rootDir = new ImportedExternalProject.ConstructedVirtualDirectory();

            foreach (var file in xml.SelectNodes("package/generators/generator/project_files/file").OfType <XmlElement>())
            {
                string category = file.GetAttribute("category");
                string name     = file.GetAttribute("name");

                if (category == "header")
                {
                    allHeaderDirs.Add(Path.GetDirectoryName(name));
                }

                rootDir.AddFile(Path.Combine(baseDir, name), category == "header");
            }

            bool hasFreeRTOS = false;

            foreach (var component in xml.SelectNodes("package/components/component").OfType <XmlElement>())
            {
                string group    = component.GetAttribute("Cgroup");
                string subGroup = component.GetAttribute("Csub");
                if (subGroup == "FREERTOS")
                {
                    hasFreeRTOS = true;
                }
                foreach (var file in component.SelectNodes("files/file").OfType <XmlElement>())
                {
                    string category     = file.GetAttribute("category");
                    string relativePath = file.GetAttribute("name");

                    string condition = file.GetAttribute("condition");
                    if (!string.IsNullOrEmpty(condition))
                    {
                        if (condition == "FreeRTOS")
                        {
                            if (!hasFreeRTOS)
                            {
                                continue;
                            }
                        }
                        else if (condition != "GCC Toolchain")
                        {
                            continue;   //This is a IAR-only or Keil-only file
                        }
                    }

                    int    idx = relativePath.LastIndexOfAny(new[] { '\\', '/' });
                    string name, dir;
                    if (idx == -1)
                    {
                        name = relativePath;
                        dir  = "";
                    }
                    else
                    {
                        name = relativePath.Substring(idx + 1);
                        dir  = relativePath.Substring(0, idx);
                    }

                    if (category == "sourceAsm" && name.StartsWith("startup_", StringComparison.InvariantCultureIgnoreCase))
                    {
                        continue;   //VisualGDB provides its own startup files for STM32 devices that are compatible with STM32CubeMX-generated files
                    }
                    if (category == "header" && dir != "")
                    {
                        allHeaderDirs.Add(dir);
                    }

                    string path = group;
                    if (!string.IsNullOrEmpty(subGroup))
                    {
                        path += "/" + subGroup;
                    }

                    if (relativePath.Contains("*"))
                    {
                        string physicalDir = Path.Combine(baseDir, dir);
                        if (Directory.Exists(physicalDir))
                        {
                            foreach (var fn in Directory.GetFiles(physicalDir, name))
                            {
                                rootDir.ProvideSudirectory(path).AddFile(fn, category == "header");
                            }
                        }
                    }
                    else
                    {
                        rootDir.ProvideSudirectory(path).AddFile(Path.Combine(baseDir, relativePath), category == "header");
                    }
                }
            }

            List <string> macros = new List <string> {
                "$$com.sysprogs.bspoptions.primary_memory$$_layout", "$$com.sysprogs.stm32.hal_device_family$$"
            };

            string[] includeDirs = allHeaderDirs.Select(d => Path.Combine(baseDir, d)).ToArray();

            PropertyDictionary2 mcuConfiguration = null;

            if (hasFreeRTOS)
            {
                macros.Add("USE_FREERTOS");
                ApplyFreeRTOSFixes(rootDir, ref includeDirs, ref mcuConfiguration);
            }

            Dictionary <string, string> temporaryExistingFileCollection = null;

            FixInvalidPathsRecursively(rootDir, baseDir, ref temporaryExistingFileCollection);

            deviceName = deviceName.TrimEnd('x');
            deviceName = deviceName.Substring(0, deviceName.Length - 1);

            return(new ImportedExternalProject
            {
                DeviceNameMask = new Regex(deviceName.Replace("x", ".*") + ".*"),
                OriginalProjectFile = parameters.ProjectFile,
                RootDirectory = rootDir,
                GNUTargetID = "arm-eabi",
                ReferencedFrameworks = new string[0],   //Unless this is explicitly specified, VisualGDB will try to reference the default frameworks (STM32 HAL) that will conflict with the STM32CubeMX-generated files.

                MCUConfiguration = mcuConfiguration,

                Configurations = new[]
                {
                    new ImportedExternalProject.ImportedConfiguration
                    {
                        Settings = new ImportedExternalProject.InvariantProjectBuildSettings
                        {
                            IncludeDirectories = includeDirs,
                            PreprocessorMacros = macros.ToArray()
                        }
                    }
                }
            });
        }
Beispiel #5
0
        private static string FindAndAddFileIfMissing(Dictionary <string, IGrouping <string, FlatFileReference> > fileListsByName, string fileName, string baseDir, ImportedExternalProject.ConstructedVirtualDirectory directoryToAddFile, ref string[] includeDirs)
        {
            if (fileListsByName.TryGetValue(fileName, out var group))
            {
                return(group.FirstOrDefault().File?.FullPath);
            }

            if (!Directory.Exists(baseDir))
            {
                return(null);
            }

            string foundFile = Directory.GetFiles(baseDir, fileName, SearchOption.AllDirectories).FirstOrDefault();

            if (foundFile != null)
            {
                directoryToAddFile.AddFile(foundFile, false);
                includeDirs = includeDirs.Concat(new[] { Path.GetDirectoryName(foundFile) }).ToArray();
            }

            return(foundFile);
        }
Beispiel #6
0
        public ImportedExternalProject ImportProject(ProjectImportParameters parameters, IProjectImportService service)
        {
            Regex  rgLine      = new Regex("(PROJECT|OBJECTS|INCLUDE_PATHS|LIBRARY_PATHS|LIBRARIES|LINKER_SCRIPT|C_FLAGS|CXX_FLAGS|ASM_FLAGS|LD_FLAGS|LD_SYS_LIBS|CC)[ \t]*(:=|\\+=|=|\\?=|=)[ \t]*(.*)$");
            string projectName = null;
            string baseDir     = Path.GetDirectoryName(parameters.ProjectFile);

            ConstructedVirtualDirectory     rootDir  = new ConstructedVirtualDirectory();
            ConstructedProjectBuildSettings settings = new ConstructedProjectBuildSettings();


            foreach (var line in File.ReadAllLines(parameters.ProjectFile))
            {
                var m = rgLine.Match(line);
                if (!m.Success)
                {
                    continue;
                }

                string value = m.Groups[3].Value.Trim();
                if (value == "")
                {
                    continue;
                }


                switch (m.Groups[1].Value)
                {
                case "PROJECT":
                    projectName = value;
                    break;

                case "OBJECTS":
                    string relPath = value;
                    if (relPath.StartsWith("./"))
                    {
                        relPath = relPath.Substring(2);
                    }
                    string fullPath = Path.GetFullPath(Path.Combine(baseDir, relPath));
                    foreach (var extension in new[] { ".c", ".cpp", ".s" })
                    {
                        string source = Path.ChangeExtension(fullPath, extension);
                        if (File.Exists(source))
                        {
                            ImportedExternalProject.ConstructedVirtualDirectory subdir = rootDir;
                            if (relPath.Contains('/'))
                            {
                                subdir = rootDir.ProvideSudirectory(Path.GetDirectoryName(relPath));
                            }
                            subdir.AddFile(source, false).ObjectFilePath = relPath;
                        }
                    }
                    break;

                case "INCLUDE_PATHS":
                    ProcessPath(service, settings.IncludeDirectories, "-I../", "include path", value, baseDir);
                    break;

                case "LIBRARY_PATHS":
                    ProcessPath(service, settings.LibraryDirectories, "-L../", "library path", value, baseDir);
                    break;

                case "LIBRARIES":
                    settings.ExtraLDFLAGS.Add(value);
                    break;

                case "LINKER_SCRIPT":
                    if (!value.StartsWith("../"))
                    {
                        service.Logger.LogLine("Warning: unsupported linker script: " + value);
                    }
                    else
                    {
                        settings.LinkerScript = Path.GetFullPath(Path.Combine(baseDir, value.Substring(3)));
                    }
                    break;

                case "C_FLAGS":
                    if (value.StartsWith("-std="))
                    {
                        settings.CStandard = value.Substring(5);
                    }
                    break;

                case "CXX_FLAGS":
                    if (value.StartsWith("-std="))
                    {
                        settings.CXXStandard = value.Substring(5);
                    }
                    else if (value == "-fno-rtti" || value == "-fno-exceptions")
                    {
                        continue;
                    }
                    else if (value.StartsWith("-D"))
                    {
                        settings.PreprocessorMacros.Add(value.Substring(2));
                    }
                    else
                    {
                        settings.ExtraCFLAGS.Add(value);
                    }
                    break;

                case "LD_FLAGS":
                    settings.ExtraLDFLAGS.Add(value);
                    break;

                case "LD_SYS_LIBS":
                    foreach (var lib in value.Split(' '))
                    {
                        if (lib.StartsWith("-l"))
                        {
                            settings.LibraryNames.Add(lib.Substring(2));
                        }
                    }
                    break;

                case "CC":
                    int skip = 0;
                    foreach (var flag in value.Split(' ').Select(f => f.Trim('\'')).Skip(1))
                    {
                        if (skip > 0)
                        {
                            skip--;
                            continue;
                        }
                        if (flag.StartsWith("-std") || flag.StartsWith("-g") || flag.StartsWith("-O"))
                        {
                            continue;
                        }

                        switch (flag)
                        {
                        case "-c":
                        case "-ffunction-sections":
                        case "-fdata-sections":
                            continue;
                        }

                        settings.ExtraCFLAGS.Add(flag);
                    }
                    break;
                }
            }

            return(new ImportedExternalProject
            {
                OriginalProjectFile = parameters.ProjectFile,
                ProjectName = projectName,
                GNUTargetID = "arm-eabi",
                Configurations = new []
                {
                    new ImportedConfiguration
                    {
                        Settings = settings.ToProjectBuildSettings()
                    }
                },
                RootDirectory = rootDir,
                ReferencedFrameworks = new string[0]
            });
        }
        ParsedCubeProject ParseProjectFile(string projectFile, bool importingReconfigurableProject)
        {
            XmlDocument xml = new XmlDocument();

            xml.Load(projectFile);

            ParsedCubeProject result = new ParsedCubeProject
            {
                RootDirectory = new ImportedExternalProject.ConstructedVirtualDirectory()
            };

            result.DeviceName = result.InternalDeviceName = (xml.SelectSingleNode("package/generators/generator/select/@Dname") as XmlAttribute)?.Value;
            if (result.DeviceName == null)
            {
                throw new Exception("Failed to extract the device name from " + projectFile);
            }

            result.DeviceName = result.DeviceName.TrimEnd('x');
            result.DeviceName = result.DeviceName.Substring(0, result.DeviceName.Length - 1);

            HashSet <string> allHeaderDirs = new HashSet <string>(StringComparer.InvariantCultureIgnoreCase);
            string           baseDir       = Path.GetDirectoryName(projectFile);

            ImportedExternalProject.ConstructedVirtualDirectory rootDir = new ImportedExternalProject.ConstructedVirtualDirectory();

            foreach (var file in xml.SelectNodes("package/generators/generator/project_files/file").OfType <XmlElement>())
            {
                string category = file.GetAttribute("category");
                string name     = file.GetAttribute("name");

                if (category == "header")
                {
                    allHeaderDirs.Add(Path.GetDirectoryName(name));
                }

                result.RootDirectory.AddFile(Path.Combine(baseDir, name), category == "header");
            }

            bool hasFreeRTOS = false;

            foreach (var component in xml.SelectNodes("package/components/component").OfType <XmlElement>())
            {
                string group    = component.GetAttribute("Cgroup");
                string subGroup = component.GetAttribute("Csub");
                if (subGroup == "FREERTOS")
                {
                    hasFreeRTOS = true;
                }
                foreach (var file in component.SelectNodes("files/file").OfType <XmlElement>())
                {
                    string category     = file.GetAttribute("category");
                    string relativePath = file.GetAttribute("name");

                    string condition = file.GetAttribute("condition");
                    if (!string.IsNullOrEmpty(condition))
                    {
                        if (condition == "FreeRTOS")
                        {
                            if (!hasFreeRTOS)
                            {
                                continue;
                            }
                        }
                        else if (condition != "GCC Toolchain")
                        {
                            continue;   //This is a IAR-only or Keil-only file
                        }
                    }

                    int    idx = relativePath.LastIndexOfAny(new[] { '\\', '/' });
                    string name, dir;
                    if (idx == -1)
                    {
                        name = relativePath;
                        dir  = "";
                    }
                    else
                    {
                        name = relativePath.Substring(idx + 1);
                        dir  = relativePath.Substring(0, idx);
                    }

                    if (category == "sourceAsm" && name.StartsWith("startup_", StringComparison.InvariantCultureIgnoreCase) && !importingReconfigurableProject)
                    {
                        continue;   //VisualGDB provides its own startup files for STM32 devices that are compatible with STM32CubeMX-generated files
                    }
                    if (category == "header" && dir != "")
                    {
                        allHeaderDirs.Add(dir);
                    }

                    string path = group;
                    if (!string.IsNullOrEmpty(subGroup))
                    {
                        path += "/" + subGroup;
                    }

                    if (relativePath.Contains("*"))
                    {
                        string physicalDir = Path.Combine(baseDir, dir);
                        if (Directory.Exists(physicalDir))
                        {
                            foreach (var fn in Directory.GetFiles(physicalDir, name))
                            {
                                result.RootDirectory.ProvideSudirectory(path).AddFile(fn, category == "header");
                            }
                        }
                    }
                    else
                    {
                        result.RootDirectory.ProvideSudirectory(path).AddFile(Path.Combine(baseDir, relativePath), category == "header");
                    }
                }
            }

            if (importingReconfigurableProject)
            {
                result.PreprocessorMacros = new HashSet <string>();
            }
            else
            {
                result.PreprocessorMacros = new HashSet <string> {
                    "$$com.sysprogs.bspoptions.primary_memory$$_layout", "$$com.sysprogs.stm32.hal_device_family$$"
                }
            };

            result.IncludeDirectories = new HashSet <string>();

            foreach (var dir in allHeaderDirs)
            {
                result.IncludeDirectories.Add(Path.GetFullPath(Path.Combine(baseDir, dir)));
            }

            if (hasFreeRTOS)
            {
                result.PreprocessorMacros.Add("USE_FREERTOS");
                ApplyFreeRTOSFixes(result.RootDirectory, result.IncludeDirectories, ref result.MCUConfiguration);
            }

            Dictionary <string, string> temporaryExistingFileCollection = null;

            FixInvalidPathsRecursively(result.RootDirectory, baseDir, ref temporaryExistingFileCollection);

            result.AllFiles = new HashSet <string>(StringComparer.InvariantCultureIgnoreCase);
            foreach (var f in result.RootDirectory.AllFilesRecursively)
            {
                result.AllFiles.Add(Path.GetFullPath(f.FullPath));
            }

            if (importingReconfigurableProject)
            {
                string makefile = Path.Combine(Path.GetDirectoryName(projectFile), "Makefile");
                if (File.Exists(makefile))
                {
                    AdjustImportedProjectFromMakefile(ref result, makefile);
                }
            }

            return(result);
        }

        bool ExpandValues(ref string[] inputs, Dictionary <string, string[]> dict)
        {
            List <string> result   = new List <string>();
            int           expanded = 0;

            foreach (var t in inputs)
            {
                if (t.StartsWith("$(") && t.EndsWith(")"))
                {
                    expanded++;
                    if (dict.TryGetValue(t.Substring(2, t.Length - 3), out var foundValues))
                    {
                        result.AddRange(foundValues);
                    }
                }
                else
                {
                    result.Add(t);
                }
            }
            inputs = result.ToArray();
            return(expanded > 0);
        }

        void AdjustImportedProjectFromMakefile(ref ParsedCubeProject parsedProject, string makefile)
        {
            var baseDir = Path.GetDirectoryName(makefile);

            Dictionary <string, string[]> listsByKey = ExtractListsFromSTM32CubeMXMakefile(makefile);

            if (listsByKey.TryGetValue("C_DEFS", out var values))
            {
                foreach (var v in values)
                {
                    if (v.StartsWith("-D"))
                    {
                        parsedProject.PreprocessorMacros.Add(v.Substring(2));
                    }
                }
            }

            if (listsByKey.TryGetValue("C_INCLUDES", out values))
            {
                foreach (var v in values)
                {
                    if (v.StartsWith("-I"))
                    {
                        parsedProject.IncludeDirectories.Add(Path.GetFullPath(Path.Combine(baseDir, v.Substring(2))));
                    }
                }
            }

            if (listsByKey.TryGetValue("LDSCRIPT", out values) && values.Length == 1)
            {
                parsedProject.LinkerScript = Path.GetFullPath(Path.Combine(baseDir, values[0]));
            }

            if (listsByKey.TryGetValue("MCU", out values))
            {
                for (int i = 0; i < 10; i++)
                {
                    if (!ExpandValues(ref values, listsByKey))
                    {
                        break;
                    }
                }

                parsedProject.CFLAGS = string.Join(" ", values);
            }

            if (listsByKey.TryGetValue("C_SOURCES", out values))
            {
                //GPDSC files generated by STM32CubeMX are often inaccurate and buggy, so we take the data from the Makefile instead
                parsedProject.AllFiles.RemoveWhere(f => f.EndsWith(".c", StringComparison.InvariantCultureIgnoreCase));

                foreach (var src in values)
                {
                    parsedProject.AllFiles.Add(Path.GetFullPath(Path.Combine(baseDir, src)));
                }
            }

            if (listsByKey.TryGetValue("ASM_SOURCES", out values))
            {
                parsedProject.AllFiles.RemoveWhere(f => f.EndsWith(".s", StringComparison.InvariantCultureIgnoreCase));

                foreach (var src in values)
                {
                    parsedProject.AllFiles.Add(Path.GetFullPath(Path.Combine(baseDir, src)));
                }
            }
        }
Beispiel #8
0
        public ImportedExternalProject ImportProject(ProjectImportParameters parameters, IProjectImportService service)
        {
            Dictionary <string, string> systemDirectories = new Dictionary <string, string>();

            var key = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Rowley Associates Limited\CrossWorks for ARM\Installer");

            service.Logger.LogLine("Detecting CrossWorks directory...");
            string crossWorksDir = null;

            if (key != null)
            {
                foreach (var kn in key.GetSubKeyNames().OrderByDescending(k => k))
                {
                    service.Logger.LogLine($"Checking {kn}...");

                    using (var subkey = key.OpenSubKey(kn + @"\OrganizationDefaults"))
                    {
                        var destDir = subkey?.GetValue("DestDir") as string;
                        if (destDir != null && Directory.Exists(destDir))
                        {
                            service.Logger.LogLine($"Found {destDir}");
                            systemDirectories["StudioDir"] = crossWorksDir = destDir;
                            break;
                        }
                    }
                }
            }

            service.Logger.LogLine("Detecting CrossWorks AppData directory...");

            var appDataDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Rowley Associates Limited\CrossWorks for ARM");

            foreach (var subdir in Directory.GetDirectories(appDataDir).OrderByDescending(d => d))
            {
                string packagesDir = Path.Combine(subdir, "packages");
                service.Logger.LogLine($"Trying {subdir}...");
                if (Directory.Exists(packagesDir))
                {
                    service.Logger.LogLine($"Found {packagesDir}");
                    systemDirectories["TargetsDir"]  = packagesDir + @"\targets";
                    systemDirectories["PackagesDir"] = packagesDir;
                    break;
                }
            }

            systemDirectories["ProjectDir"] = Path.GetDirectoryName(parameters.ProjectFile);
            systemDirectories["LibExt"]     = "";

            XmlDocument xml = new XmlDocument();

            xml.Load(parameters.ProjectFile);

            var project      = xml.DocumentElement.SelectSingleNode("project") as XmlElement ?? throw new Exception("Could not find the project element");
            var commonConfig = project.SelectSingleNode("configuration[@Name='Common']") as XmlElement ?? throw new Exception("Could not find the common configuration element");

            string deviceName = commonConfig.GetAttribute("Target");

            if (string.IsNullOrEmpty(deviceName))
            {
                throw new Exception("Target name is unspecified");
            }

            string[] internalMacros = commonConfig.GetAttribute("macros")?.Split(';');
            string   vectorFile     = null;

            foreach (var im in internalMacros ?? new string[0])
            {
                int idx = im.IndexOf('=');
                if (idx != -1)
                {
                    string mk = im.Substring(0, idx).Trim();
                    string mv = im.Substring(idx + 1).Trim();
                    systemDirectories[mk] = mv;
                    if (mk == "DeviceVectorsFile")
                    {
                        vectorFile = mv;
                    }
                }
            }

            string[] macros      = commonConfig.GetAttribute("c_preprocessor_definitions")?.Split(';');
            string[] includeDirs = (commonConfig.GetAttribute("c_system_include_directories") + ";" + commonConfig.GetAttribute("c_user_include_directories"))?.Split(';');
            if (crossWorksDir != null)
            {
                includeDirs = includeDirs.Concat(new[] { crossWorksDir + "/include" }).ToArray();
            }

            string[] additionalLinkerInputs = commonConfig.GetAttribute("linker_additional_files")?.Split(';');

            macros = macros.Concat(new[] { "__CROSSWORKS_ARM", "STARTUP_FROM_RESET" }).ToArray();
            if (vectorFile != null)
            {
                macros = macros.Concat(new[] { $"__VECTORS=\"{vectorFile}\"" }).ToArray();
            }

            string frequency = commonConfig.GetAttribute("oscillator_frequency");

            if (frequency?.EndsWith("MHz") == true && int.TryParse(frequency.Substring(0, frequency.Length - 3), out int frequencyInMhz))
            {
                macros = macros.Concat(new[] { "OSCILLATOR_CLOCK_FREQUENCY=" + (frequencyInMhz * 1000000) }).ToArray();
            }

            ImportedExternalProject.ConstructedVirtualDirectory rootDir = new ImportedExternalProject.ConstructedVirtualDirectory();

            var expander = service.CreateVariableExpander(systemDirectories, VariableExpansionSyntax.Makefile);

            SettingsFromBuiltProject extraSettings = RetrieveSettingsFromBuiltProject(parameters, service);

            if (extraSettings.AdditionalInputs != null)
            {
                foreach (var input in extraSettings.AdditionalInputs)
                {
                    rootDir.AddFile(input, false);
                }
            }

            foreach (var lib in additionalLinkerInputs)
            {
                var mappedPath = ExpandVariables(lib, expander, service);
                if (!string.IsNullOrEmpty(mappedPath))
                {
                    rootDir.AddFile(mappedPath, false);
                }
            }

            ImportProjectFolderRecursively(project, rootDir, parameters, expander, service);

            return(new ImportedExternalProject
            {
                DeviceNameMask = new Regex(deviceName.Replace("x", ".*") + ".*"),
                OriginalProjectFile = parameters.ProjectFile,
                RootDirectory = rootDir,
                GNUTargetID = "arm-eabi",
                ReferencedFrameworks = new string[0],   //Unless this is explicitly specified, VisualGDB will try to reference the default frameworks (STM32 HAL) that will conflict with the STM32CubeMX-generated files.
                MCUConfiguration = new PropertyDictionary2 {
                    Entries = new[] { new PropertyDictionary2.KeyValue {
                                          Key = "com.sysprogs.mcuoptions.ignore_startup_file", Value = "1"
                                      } }
                },

                Configurations = new[]
                {
                    new ImportedExternalProject.ImportedConfiguration
                    {
                        Settings = new ImportedExternalProject.InvariantProjectBuildSettings
                        {
                            IncludeDirectories = includeDirs?.Select(d => ExpandVariables(d, expander, service))?.Where(d => !string.IsNullOrEmpty(d))?.ToArray(),
                            PreprocessorMacros = macros,
                            ExtraLDFLAGS = new[] { "-nostdlib -Wl,-u_vectors -Wl,-ereset_handler " },
                            LinkerScript = extraSettings.LinkerScript,
                        },
                    }
                }
            });
        }
        public ImportedExternalProject ImportProject(ProjectImportParameters parameters, IProjectImportService service)
        {
            var parser = new ParserImpl();
            List <VendorSample> result = new List <VendorSample>();

            parser.ParseSingleProject(null, parameters.ProjectFile, null, null, null, SW4STM32ProjectParserBase.ProjectSubtype.Auto, result);
            if (result.Count == 0)
            {
                throw new Exception("Failed to parse the project file");
            }

            ImportedExternalProject.ConstructedVirtualDirectory rootDir = new ImportedExternalProject.ConstructedVirtualDirectory();

            Dictionary <string, string> physicalDirToVirtualPaths = new Dictionary <string, string>(StringComparer.InvariantCultureIgnoreCase);

            var sample = result[0];

            if (parser.OptionDictionary.TryGetValue(sample, out var opts) && opts.SourceFiles != null)
            {
                foreach (var sf in opts.SourceFiles)
                {
                    string virtualDir = Path.GetDirectoryName(sf.VirtualPath);
                    physicalDirToVirtualPaths[Path.GetDirectoryName(sf.FullPath)] = virtualDir;
                    rootDir.ProvideSudirectory(virtualDir).AddFile(sf.FullPath, false);
                }
            }
            else
            {
                foreach (var src in sample.SourceFiles ?? new string[0])
                {
                    rootDir.AddFile(src, false);
                }
            }

            foreach (var src in sample.HeaderFiles ?? new string[0])
            {
                if (physicalDirToVirtualPaths.TryGetValue(Path.GetDirectoryName(src), out string virtualDir))
                {
                    rootDir.ProvideSudirectory(virtualDir).AddFile(src, true);
                }
                else if (physicalDirToVirtualPaths.TryGetValue(Path.GetDirectoryName(src).Replace(@"\Inc", @"\Src"), out virtualDir))
                {
                    rootDir.ProvideSudirectory(virtualDir).AddFile(src, true);
                }
                else
                {
                    rootDir.AddFile(src, true);
                }
            }

            return(new ImportedExternalProject
            {
                DeviceNameMask = new Regex(sample.DeviceID),
                OriginalProjectFile = parameters.ProjectFile,
                RootDirectory = rootDir,
                GNUTargetID = "arm-eabi",
                ReferencedFrameworks = new string[0],

                MCUConfiguration = sample.Configuration.MCUConfiguration,

                Configurations = new[]
                {
                    new ImportedExternalProject.ImportedConfiguration
                    {
                        Settings = new ImportedExternalProject.InvariantProjectBuildSettings
                        {
                            IncludeDirectories = sample.IncludeDirectories,
                            PreprocessorMacros = sample.PreprocessorMacros,
                            LinkerScript = sample.LinkerScript,
                        }
                    }
                }
            });
        }
Beispiel #10
0
        public ImportedExternalProject ImportProject(ProjectImportParameters parameters, IProjectImportService service)
        {
            XmlDocument xml = new XmlDocument();

            xml.Load(parameters.ProjectFile);

            var target = xml.SelectSingleNode("Project/Targets/Target") as XmlElement;

            if (target == null)
            {
                throw new Exception("Failed to locate the target node in " + parameters.ProjectFile);
            }

            string deviceName = (target.SelectSingleNode("TargetOption/TargetCommonOption/Device") as XmlElement)?.InnerText;

            if (deviceName == null)
            {
                throw new Exception("Failed to extract the device name from " + parameters.ProjectFile);
            }

            if (deviceName.EndsWith("x"))
            {
                deviceName = deviceName.TrimEnd('x');
                deviceName = deviceName.Substring(0, deviceName.Length - 1);
            }

            string baseDir = Path.GetDirectoryName(parameters.ProjectFile);

            ImportedExternalProject.ConstructedVirtualDirectory rootDir = new ImportedExternalProject.ConstructedVirtualDirectory();

            foreach (var group in target.SelectNodes("Groups/Group").OfType <XmlElement>())
            {
                string virtualPath = group.SelectSingleNode("GroupName")?.InnerText;
                if (string.IsNullOrEmpty(virtualPath))
                {
                    continue;
                }

                var subdir = rootDir.ProvideSudirectory(virtualPath);
                foreach (var file in group.SelectNodes("Files/File").OfType <XmlElement>())
                {
                    string path = file.SelectSingleNode("FilePath")?.InnerText;
                    string type = file.SelectSingleNode("FileType")?.InnerText;
                    if (type == "2")
                    {
                        //This is an assembly file. Keil uses a different assembly syntax than GCC, so we cannot include this file into the project.
                        //The end user will need to include a GCC-specific replacement manually (unless this is the startup file, in which case VisualGDB
                        //automatically includes a GCC-compatible replacement).
                        continue;
                    }
                    if (string.IsNullOrEmpty(path))
                    {
                        continue;
                    }

                    var adjustedPath = TryAdjustPath(baseDir, path, service);
                    subdir.AddFile(adjustedPath, type == "5");
                }
            }

            List <string> macros = new List <string> {
                "$$com.sysprogs.bspoptions.primary_memory$$_layout"
            };
            List <string> includeDirs = new List <string>();

            var optionsNode = target.SelectSingleNode("TargetOption/TargetArmAds/Cads/VariousControls");

            if (optionsNode != null)
            {
                macros.AddRange((optionsNode.SelectSingleNode("Define")?.InnerText ?? "").Split(',').Select(m => m.Trim()));
                includeDirs.AddRange((optionsNode.SelectSingleNode("IncludePath")?.InnerText ?? "")
                                     .Split(';')
                                     .Select(p => TryAdjustPath(baseDir, p.Trim(), service))
                                     .Where(p => p != null));
            }

            return(new ImportedExternalProject
            {
                DeviceNameMask = new Regex(deviceName.Replace("x", ".*") + ".*"),
                OriginalProjectFile = parameters.ProjectFile,
                RootDirectory = rootDir,
                GNUTargetID = "arm-eabi",
                ReferencedFrameworks = new string[0],   //Unless this is explicitly specified, VisualGDB will try to reference the default frameworks (STM32 HAL) that will conflict with the STM32CubeMX-generated files.

                Configurations = new[]
                {
                    new ImportedExternalProject.ImportedConfiguration
                    {
                        Settings = new ImportedExternalProject.InvariantProjectBuildSettings
                        {
                            IncludeDirectories = includeDirs.ToArray(),
                            PreprocessorMacros = macros.ToArray()
                        }
                    }
                }
            });
        }