Beispiel #1
0
 public void Add(string sdkRootPath, SdkSchema sdkSchema)
 {
     SdkProperties.SDK = SdkProperties.SDK != null
                             ? SdkProperties.SDK.Concat(new[] { sdkSchema }).ToArray()
                             : new[] { sdkSchema };
     SaveProperties();
 }
        private async Task AddNewSdk(string sdkPath, bool forced)
        {
            SdkSchema sdkSchema = await sdkExplorer.ExploreSdk(sdkPath, forced).ConfigureAwait(false);

            if (sdkSchema != null)
            {
                sdkContainer.Add(sdkPath, sdkSchema);
            }
        }
Beispiel #3
0
        private async Task <bool> AddNewSdk(string sdkPath, bool forced, bool checkForDuplicate = true)
        {
            if (exploringSdk == sdkPath)
            {
                return(false);
            }

            try
            {
                exploringSdk = sdkPath;
                SdkSchema sdkSchema = await sdkExplorer.ExploreSdk(sdkPath, forced).ConfigureAwait(false);

                if (sdkSchema != null)
                {
                    if (checkForDuplicate)
                    {
                        IEnumerable <Target> targets = GetAllTargets();
                        foreach (TargetSchema targetSchema in sdkSchema.Target)
                        {
                            Target target = new Target(targetSchema.name, targetSchema.version);
                            if (targets.Contains(target))
                            {
                                throw new TargetAlreadyInstalledException(target.GetLongFullName());
                            }
                        }
                    }
                    sdkContainer.Add(sdkPath, sdkSchema);
                }
            }
            finally
            {
                exploringSdk = string.Empty;
            }

            return(true);
        }
Beispiel #4
0
        public SdkInformation Get(string sdkRootPath)
        {
            sdkRootPath = sdkRootPath.CleanPath();

            return(ParseSdk());

            SdkInformation ParseSdk()
            {
                SdkSchema definition = SdkProperties.SDK?.FirstOrDefault(s => s.path == sdkRootPath);

                if (definition == null)
                {
                    throw new InvalidOperationException($"This should not happen. Expected properties for SDK {sdkRootPath}, but found none.");
                }

                IEnumerable <Target> targets = definition.Target?.Select(t => new Target(t.name, t.version))
                                               ?? Enumerable.Empty <Target>();

                if (!fileSystem.DirectoryExists(definition.path))
                {
                    throw new SdkPathNotExistingException(definition.path);
                }
                return(new SdkInformation(targets, definition.IncludePath ?? Enumerable.Empty <string>(),
                                          fileSystem.GetDirectory(definition.path),
                                          string.IsNullOrEmpty(definition.makepath)
                                   ? null
                                   : fileSystem.GetFile(definition.makepath),
                                          new CompilerInformation(definition.CompilerSpec.compilerPath,
                                                                  definition.CompilerSpec.compilerSysroot,
                                                                  definition.CompilerSpec.compilerFlags,
                                                                  definition.CompilerSpec.IncludePath ?? Enumerable.Empty <string>(),
                                                                  definition.CompilerSpec.CompilerMacro?
                                                                  .Select(m => new CompilerMakro(m.name, m.value))
                                                                  ?? Enumerable.Empty <CompilerMakro>())));
            }
        }
        public async Task <SdkSchema> ExploreSdk(string sdkRootPath, bool forceExploration = false)
        {
            sdkRootPath = sdkRootPath.CleanPath();

            if (AlreadyExplored())
            {
                return(null);
            }

            if (!fileSystem.DirectoryExists(sdkRootPath))
            {
                throw new SdkPathNotExistingException(sdkRootPath);
            }
            VirtualDirectory rootDirectory = fileSystem.GetDirectory(sdkRootPath);
            SdkSchema        sdkSchema     = await Explore().ConfigureAwait(false);

            executionContext.WriteVerbose($"Finished exploration with the following content:{Environment.NewLine}" +
                                          $"{JsonConvert.SerializeObject(sdkSchema, Formatting.Indented, new JsonSerializerSettings {ReferenceLoopHandling = ReferenceLoopHandling.Ignore})}");
            exploredPaths.Add(sdkRootPath);

            return(sdkSchema);

            async Task <SdkSchema> Explore()
            {
                try
                {
                    executionContext.WriteInformation($"Exploring the SDK {sdkRootPath}.");
                    VirtualDirectory temporaryDirectory = fileSystem.GetTemporaryDirectory();
                    CreateSampleProject(temporaryDirectory);
                    string makeExecutable = FindMakeExecutable();
                    ConfigureSampleProject(temporaryDirectory, makeExecutable);
                    return(await ExploreSampleProject(temporaryDirectory, makeExecutable).ConfigureAwait(false));
                }
                catch (Exception e)
                {
                    throw new CMakeExplorationException(rootDirectory.FullName, e);
                }

                async Task <SdkSchema> ExploreSampleProject(VirtualDirectory temporaryDirectory, string makeExecutable)
                {
                    VirtualDirectory sourceDirectory = temporaryDirectory;
                    VirtualDirectory binaryDirectory = temporaryDirectory.Directory("cache");

                    using (CMakeConversation conversation = await CMakeConversation.Start(processManager,
                                                                                          binariesLocator,
                                                                                          formatterPool,
                                                                                          temporaryDirectory,
                                                                                          environmentService.Platform == OSPlatform.Windows,
                                                                                          executionContext,
                                                                                          sourceDirectory,
                                                                                          binaryDirectory)
                                                            .ConfigureAwait(false))
                    {
                        JArray cache = await conversation.GetCache().ConfigureAwait(false);

                        JArray codeModel = await conversation.GetCodeModel().ConfigureAwait(false);

                        return(ExploreCMakeOutput(cache, codeModel));
                    }

                    SdkSchema ExploreCMakeOutput(JArray cache, JArray codeModel)
                    {
                        string[] supportedDevices = GetCacheEntry("ARP_SUPPORTED_DEVICES")["value"].Value <string>()
                                                    .Split(new [] { ';' }, StringSplitOptions.RemoveEmptyEntries);
                        string[] supportedVersions = GetCacheEntry("ARP_SUPPORTED_VERSIONS")["value"].Value <string>()
                                                     .Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
                        IEnumerable <string>  includePaths  = GetIncludePathsFromCodeModel(out string sysroot);
                        CompilerSpecification specification = GetCompilerSpecs(sysroot);

                        return(new SdkSchema
                        {
                            IncludePath = includePaths.ToArray(),
                            CompilerSpec = specification,
                            makepath = makeExecutable,
                            path = rootDirectory.FullName,
                            Target = (from device in supportedDevices
                                      from version in supportedVersions
                                      select new TargetSchema
                            {
                                name = device,
                                version = version
                            }).ToArray()
                        });

                        CompilerSpecification GetCompilerSpecs(string cmakeSysroot)
                        {
                            string compiler = GetCacheEntry("CMAKE_CXX_COMPILER")["value"].Value <string>();
                            string flags    = GetCacheEntry("CMAKE_CXX_FLAGS")["value"].Value <string>();
                            string command  = $"--sysroot=\"{cmakeSysroot}\" {flags} -E -P -v -dD a.cxx";
                            StringBuilderUserInterface stringBuilder = new StringBuilderUserInterface(executionContext, true, true, true, true);

                            string setup = GetSetupFile(binaryDirectory);

                            using (IProcess process = processManager.StartProcessWithSetup(compiler.Replace('/', Path.DirectorySeparatorChar),
                                                                                           command, stringBuilder, setup,
                                                                                           temporaryDirectory.FullName))
                            {
                                process.WaitForExit();
                                if (process.ExitCode != 0)
                                {
                                    throw new FormattableException($"Error while extracting the compiler include paths:{Environment.NewLine}" +
                                                                   $"{stringBuilder.Error}");
                                }
                            }

                            string[] lines = ($"{stringBuilder.Error}{Environment.NewLine}" +
                                              $"{stringBuilder.Information}").Split(new[] { '\n', '\r' },
                                                                                    StringSplitOptions.RemoveEmptyEntries);
                            IEnumerable <string> paths = lines.SkipWhile(l => !l.Contains("#include <...> search starts here:"))
                                                         .Skip(1)
                                                         .TakeWhile(l => !l.Contains("End of search list."));

                            return(new CompilerSpecification
                            {
                                IncludePath = GetIncludePaths().ToArray(),
                                CompilerMacro = GetMakros().ToArray(),
                                compilerPath = compiler.Replace('/', Path.DirectorySeparatorChar),
                                compilerFlags = flags,
                                compilerSysroot = cmakeSysroot
                            });

                            IEnumerable <MakroDefinition> GetMakros()
                            {
                                return(lines.Select(l => MakroDefinition.Match(l))
                                       .Where(l => l.Success)
                                       .Select(m => m.Groups["value"].Success
                                                             ? new MakroDefinition
                                {
                                    name = m.Groups["name"].Value,
                                    value = m.Groups["value"].Value
                                }
                                                             : new MakroDefinition {
                                    name = m.Groups["name"].Value
                                }));
                            }

                            IEnumerable <string> GetIncludePaths()
                            {
                                return(paths.Where(p => fileSystem.DirectoryExists(p))
                                       .Select(p => fileSystem.GetDirectory(p).FullName));
                            }
                        }

                        IEnumerable <string> GetIncludePathsFromCodeModel(out string cmakeSysroot)
                        {
                            JObject project = codeModel.OfType <JObject>()
                                              .Where(o => o.ContainsKey("projects"))
                                              .SelectMany(o => o["projects"])
                                              .OfType <JObject>()
                                              .FirstOrDefault(o => o.ContainsKey("name") &&
                                                              o["name"].Value <string>() == "MyProject");

                            if (project == null)
                            {
                                throw new FormattableException($"The code model does not contain any project with the name 'MyProject'. " +
                                                               $"The code model contains the following data:{Environment.NewLine}" +
                                                               $"{codeModel.ToString(Formatting.Indented)}");
                            }

                            JObject target = project.ContainsKey("targets")
                                                 ? project["targets"].OfType <JObject>()
                                             .Where(o => o.ContainsKey("name"))
                                             .FirstOrDefault(o => o["name"].Value <string>() == "MyProject")
                                                 : null;

                            if (target == null)
                            {
                                throw new FormattableException($"The project 'MyProject' does not contain any target with the name 'MyProject'. " +
                                                               $"The project contains the following data:{Environment.NewLine}" +
                                                               $"{project.ToString(Formatting.Indented)}");
                            }

                            cmakeSysroot = target["sysroot"].Value <string>();

                            return(target.ContainsKey("fileGroups")
                                       ? target["fileGroups"].OfType <JObject>()
                                   .Where(o => o.ContainsKey("includePath"))
                                   .SelectMany(o => o["includePath"])
                                   .OfType <JObject>()
                                   .Where(o => o.ContainsKey("path"))
                                   .Select(o => o["path"].Value <string>())
                                   .Where(p => fileSystem.DirectoryExists(p))
                                   .Select(p => fileSystem.GetDirectory(p).FullName)
                                       : Enumerable.Empty <string>());
                        }

                        JObject GetCacheEntry(string key)
                        {
                            JObject entry = cache.OfType <JObject>().FirstOrDefault(t => t.ContainsKey("key") &&
                                                                                    t["key"].Value <string>() == key);

                            if (entry == null)
                            {
                                throw new FormattableException($"The cache entry {key} was expected, but not found. The cache contains the following data:{Environment.NewLine}" +
                                                               $"{cache.ToString(Formatting.Indented)}");
                            }

                            return(entry);
                        }
                    }
                }

                void ConfigureSampleProject(VirtualDirectory temporaryDirectory, string makeExecutable)
                {
                    VirtualDirectory cache      = temporaryDirectory.Directory("cache");
                    string           makeOption = string.IsNullOrEmpty(makeExecutable)
                                            ? string.Empty
                                            : $"-DCMAKE_MAKE_PROGRAM=\"{makeExecutable.Replace('\\', '/')}\" ";
                    string command =
                        $"-DCMAKE_TOOLCHAIN_FILE=\"{rootDirectory.FullName.Replace('\\', '/')}/toolchain.cmake\" " +
                        $"-G \"Unix Makefiles\" " +
                        $"{makeOption}" +
                        $"-DARP_TOOLCHAIN_ROOT=\"{rootDirectory.FullName.Replace('\\', '/')}\" " +
                        $"-DARP_CHECK_DEVICE=OFF " +
                        $"-DARP_CHECK_DEVICE_VERSION=OFF " +
                        $"..";

                    string setup = GetSetupFile(cache);

                    using (IProcess process = processManager.StartProcessWithSetup(binariesLocator.GetExecutableCommand("cmake"),
                                                                                   command, executionContext, setup,
                                                                                   cache.FullName.Replace("\\", "/"),
                                                                                   showOutput: false, showError: false))
                    {
                        process.WaitForExit();
                    }
                }

                void CreateSampleProject(VirtualDirectory temporaryDirectory)
                {
                    string               resourceBaseString = "PlcNext.Common.Tools.SDK.SampleProject.";
                    Assembly             assembly           = Assembly.GetAssembly(typeof(CMakeSdkExplorer));
                    IEnumerable <string> resources          = assembly.GetManifestResourceNames()
                                                              .Where(n => n.StartsWith(resourceBaseString, StringComparison.OrdinalIgnoreCase));

                    foreach (string resource in resources)
                    {
                        string fileName = resource.Substring(resourceBaseString.Length);
                        using (Stream fileStream = temporaryDirectory.File(fileName).OpenWrite())
                            using (Stream resourceStream = assembly.GetManifestResourceStream(resource))
                            {
                                resourceStream?.CopyTo(fileStream);
                            }
                    }
                }

                string FindMakeExecutable()
                {
                    if (environmentService.Platform != OSPlatform.Windows)
                    {
                        return(string.Empty);
                    }

                    string hintPath = Path.Combine(sdkRootPath, @"sysroots\x86_64-pokysdk-mingw32\usr\bin\make.exe");

                    if (fileSystem.FileExists(hintPath))
                    {
                        return(hintPath);
                    }

                    string makePath = rootDirectory.Files("make.exe", true).FirstOrDefault()?.FullName;

                    if (string.IsNullOrEmpty(makePath))
                    {
                        throw new FormattableException($"Could not find the 'make.exe' in the SDK '{sdkRootPath}'. " +
                                                       "This SDK cannot be used, please remove the SDK from the settings.");
                    }

                    return(makePath);
                }
            }

            bool AlreadyExplored()
            {
                return(!forceExploration && exploredPaths.Contains(sdkRootPath));
            }
        }