/// <summary> /// Start a search task that finds plugins to the platform. /// This call is not blocking, some other calls to PluginManager will automatically /// wait for this task to finish (or even start it if it hasn't been already). These calls /// include <see cref="GetAllPlugins"/>, <see cref="GetPlugins{BaseType}"/>, /// <see cref="GetPlugins(Type)"/>, <see cref="LocateType(string)"/> and <see cref="LocateTypeData(string)"/> /// </summary> public static Task SearchAsync() { searchTask.Reset(); searcher = null; ChangeID++; TapThread.Start(Search); return(Task.Run(() => GetSearcher())); }
/// <summary> /// Gets all plugins. I.e. all types that descend from <see cref="ITapPlugin"/>. /// Abstract types and interfaces are not included. /// This does not require/cause the assembly containing the type to be loaded. /// This will search for plugins if not done already (i.e. call and wait for <see cref="PluginManager.SearchAsync()"/>) /// Only C#/.NET types are returned. To also get dynamic types (from custom <see cref="ITypeDataSearcher"/>s) use <see cref="TypeData.GetDerivedTypes(ITypeData)"/> instead. /// </summary> public static ReadOnlyCollection <TypeData> GetAllPlugins() { PluginSearcher searcher = GetSearcher(); return(searcher.PluginTypes .Where(st => !st.TypeAttributes.HasFlag(TypeAttributes.Interface) && !st.TypeAttributes.HasFlag(TypeAttributes.Abstract)) .ToList().AsReadOnly()); }
public void CreatePackageDepVersions() { var tmp = Path.GetTempFileName(); var tmp2 = Path.GetTempFileName(); Directory.Move("Packages", "Packages2"); try { { PackageDef def = new PackageDef(); def.Name = "test"; def.InfoLink = "a"; def.Date = DateTime.Today; def.AddFile(tmp); var bs = PluginManager.GetSearcher().Assemblies.First(asm => asm.Name == "OpenTap.Plugins.BasicSteps"); def.Files[0].DependentAssemblies.Add(bs); var searcher = new PluginSearcher(); searcher.Search(Directory.GetCurrentDirectory()); List <AssemblyData> assemblies = searcher.Assemblies.ToList(); def.findDependencies(new List <string>(), assemblies); Assert.AreEqual(0, def.Dependencies.Count); Assert.AreNotEqual(0, def.Files.Count); } { PackageDef def = new PackageDef(); def.AddFile(tmp); var bs = PluginManager.GetSearcher().Assemblies.First(asm => asm.Name == "OpenTap.Plugins.BasicSteps"); def.Files[0].DependentAssemblies.Add(bs); var searcher = new PluginSearcher(); searcher.Search(Directory.GetCurrentDirectory()); List <AssemblyData> assemblies = searcher.Assemblies.ToList(); def.findDependencies(new List <string> { "OpenTap" }, assemblies); Assert.AreEqual(0, def.Dependencies.Count); Assert.AreNotEqual(1, def.Files.Count); } } finally { if (Directory.Exists("Packages")) { Directory.Delete("Packages", true); } Directory.Move("Packages2", "Packages"); } }
public void SameAssemblyTwice() { PluginSearcher searcher = new PluginSearcher(); try { Directory.CreateDirectory("SameAssemblyTwiceTestDir"); File.Copy("OpenTap.UnitTests.dll", "SameAssemblyTwiceTestDir/OpenTap.UnitTests.dll", true); searcher.Search(new string[] { "OpenTap.UnitTests.dll", "OpenTap.dll", "SameAssemblyTwiceTestDir/OpenTap.UnitTests.dll" }); Assert.AreEqual(2, searcher.Assemblies.Count()); } finally { Directory.Delete("SameAssemblyTwiceTestDir", true); } }
/// <summary> /// Searches the files in fileNames for dlls implementing <see cref="ITapPlugin"/> /// and puts the implementation in the appropriate list. /// </summary> /// <param name="_fileNames">List of files to search.</param> static PluginSearcher SearchAndAddToStore(IEnumerable <string> _fileNames) { var fileNames = _fileNames.ToList(); Stopwatch timer = Stopwatch.StartNew(); PluginSearcher searcher = new PluginSearcher(); try { var w2 = Stopwatch.StartNew(); IEnumerable <TypeData> foundPluginTypes = searcher.Search(fileNames); IEnumerable <AssemblyData> foundAssemblies = foundPluginTypes.Select(p => p.Assembly).Distinct(); log.Debug(w2, "Found {0} plugin assemblies containing {1} plugin types.", foundAssemblies.Count(), foundPluginTypes.Count()); foreach (AssemblyData asm in foundAssemblies) { assemblyResolver.AddAssembly(asm.Name, asm.Location); if (asm.Location.Contains(AppDomain.CurrentDomain.BaseDirectory)) { log.Debug("Found version {0,-16} of {1}", asm.SemanticVersion?.ToString() ?? asm.Version?.ToString(), Path.GetFileName(asm.Location)); } else { // log full path of assembly if it was loaded with --search from another directory. log.Debug("Found version {0,-16} of {1} from {2}", asm.SemanticVersion?.ToString() ?? asm.Version?.ToString(), Path.GetFileName(asm.Location), asm.Location); } } } catch (Exception ex) { log.Error("Plugin search failed for: " + String.Join(", ", fileNames)); log.Debug(ex); } log.Debug(timer, "Searched {0} Assemblies.", fileNames.Count); if (GetPlugins(searcher, typeof(IInstrument).FullName).Count == 0) { log.Warning("No instruments found."); } if (GetPlugins(searcher, typeof(ITestStep).FullName).Count == 0) { log.Warning("No TestSteps found."); } return(searcher); }
public static SemanticVersion GetVersion(string path) { var searcher = new PluginSearcher(); searcher.Search(new[] { path }); var asm = searcher.Assemblies.First(); using (FileStream file = new FileStream(asm.Location, FileMode.Open, FileAccess.Read)) using (PEReader header = new PEReader(file, PEStreamOptions.LeaveOpen)) { var CurrentReader = header.GetMetadataReader(); foreach (CustomAttributeHandle attrHandle in CurrentReader.GetAssemblyDefinition().GetCustomAttributes()) { CustomAttribute attr = CurrentReader.GetCustomAttribute(attrHandle); if (attr.Constructor.Kind == HandleKind.MemberReference) { var ctor = CurrentReader.GetMemberReference((MemberReferenceHandle)attr.Constructor); string attributeFullName = ""; if (ctor.Parent.Kind == HandleKind.TypeDefinition) { var def = CurrentReader.GetTypeDefinition((TypeDefinitionHandle)ctor.Parent); attributeFullName = string.Format("{0}.{1}", CurrentReader.GetString(def.Namespace), CurrentReader.GetString(def.Name)); } if (ctor.Parent.Kind == HandleKind.TypeReference) { var r = CurrentReader.GetTypeReference((TypeReferenceHandle)ctor.Parent); attributeFullName = string.Format("{0}.{1}", CurrentReader.GetString(r.Namespace), CurrentReader.GetString(r.Name)); } if (attributeFullName == typeof(System.Reflection.AssemblyInformationalVersionAttribute).FullName) { var valueString = attr.DecodeValue(new CustomAttributeTypeProvider()); if (SemanticVersion.TryParse(valueString.FixedArguments[0].Value.ToString(), out SemanticVersion infoVer)) { return(infoVer); } } } } } return(null); }
public void PluginSearcherBasics() { PluginSearcher searcher = new PluginSearcher(); searcher.Search(new string[] { "OpenTap.UnitTests.dll", "OpenTap.dll" }); CollectionAssert.AllItemsAreInstancesOfType(searcher.PluginTypes.ToList(), typeof(TypeData)); CollectionAssert.AllItemsAreNotNull(searcher.PluginTypes.ToList()); CollectionAssert.AllItemsAreUnique(searcher.PluginTypes.ToList()); var instrType = searcher.PluginTypes.FirstOrDefault(st => st.Name == "OpenTap.IInstrument"); Assert.IsNotNull(instrType); // Test of SearchType.PluginTypes (should be the type itself, as this is IInstrument is a plugin type - it directly implements ITapPlugin) Assert.AreEqual(instrType, instrType.PluginTypes.First()); // Test of SearchType.Display Assert.IsNotNull(instrType.Display); Assert.AreEqual("Instrument", instrType.Display.Name); // Test of SearchType.Assembly Assert.AreEqual("OpenTap", instrType.Assembly.Name); var instrImplType = searcher.PluginTypes.FirstOrDefault(st => st.Name == "OpenTap.Engine.UnitTests.InstrumentTest"); Assert.IsNotNull(instrImplType); // Test of SearchType.PluginTypes Assert.AreEqual(1, instrImplType.PluginTypes.Count()); Assert.AreEqual(instrType, instrImplType.PluginTypes.First()); // Test of SearchType.DerivedTypes CollectionAssert.Contains(instrType.DerivedTypes.ToArray(), instrImplType); // Test of SearchAssembly.References CollectionAssert.Contains(instrImplType.Assembly.References.ToList(), instrType.Assembly); // Test of SearchAssembly.Load() Assert.AreEqual(Assembly.GetExecutingAssembly(), instrImplType.Assembly.Load()); // Test of SearchType.Load() Assert.AreEqual(typeof(OpenTap.Engine.UnitTests.InstrumentTest), instrImplType.Load()); // Test of nested class var stepType = searcher.PluginTypes.FirstOrDefault(st => st.Name == "OpenTap.Engine.UnitTests.TestPlanTestFixture1+TestPlanTestStep"); Assert.IsNotNull(stepType); CollectionAssert.IsSubsetOf(searcher.PluginTypes.ToList(), searcher.AllTypes.Values); }
static ReadOnlyCollection <Type> getPlugins(Type pluginBaseType) { if (pluginBaseType == null) { throw new ArgumentNullException("pluginBaseType"); } PluginSearcher searcher = GetSearcher(); // Wait for the search to complete (and get the result) var unloadedPlugins = PluginManager.GetPlugins(searcher, pluginBaseType.FullName); if (unloadedPlugins.Count == 0) { return(emptyTypes); } int notLoadedTypesCnt = unloadedPlugins.Count(pl => pl.Status == LoadStatus.NotLoaded); if (notLoadedTypesCnt > 0) { var notLoadedAssembliesCnt = unloadedPlugins.Select(x => x.Assembly).Distinct().Where(asm => asm.Status == LoadStatus.NotLoaded).ToArray(); if (notLoadedAssembliesCnt.Length > 0) { notLoadedAssembliesCnt.AsParallel().ForAll(asm => asm.Load()); } } IEnumerable <TypeData> plugins = unloadedPlugins; if (notLoadedTypesCnt > 8) { // only find types in parallel if there are sufficiently many. plugins = plugins .AsParallel() // This is around 50% faster when many plugins are loaded in parallel. .AsOrdered(); // ensure the order is the same as before. } return(plugins .Select(td => td.Load()) .Where(x => x != null) .ToList() .AsReadOnly()); }
private static Type locateType(string typeName) { PluginSearcher searcher = GetSearcher(); if (string.IsNullOrWhiteSpace(typeName)) { return(null); } if ((typeName.Length > 2) && (typeName[typeName.Length - 2] == '[') && (typeName[typeName.Length - 1] == ']')) { var elemType = LocateType(typeName.Substring(0, typeName.Length - 2)); if (elemType != null) { return(elemType.MakeArrayType()); } else { return(null); } } else if (searcher.AllTypes.ContainsKey(typeName)) { TypeData t = searcher.AllTypes[typeName]; var loaded = t.Load(); if (loaded != null) { return(loaded); } } if (typeName.Contains(",")) { var x = locateType(typeName.Split(',')[0]); if (x != null) { return(x); } } return(Type.GetType(typeName)); }
///<summary>Searches for plugins.</summary> public static void Search() { searchTask.Reset(); searcher = null; assemblyResolver.Invalidate(DirectoriesToSearch); ChangeID++; try { IEnumerable <string> fileNames = assemblyResolver.GetAssembliesToSearch(); searcher = SearchAndAddToStore(fileNames); } catch (Exception e) { log.Error("Caught exception while searching for plugins: '{0}'", e.Message); log.Debug(e); searcher = null; } finally { searchTask.Set(); } }
/// <summary> /// Load from an XML package definition file. /// This file is not expected to have info about the plugins in it, so this method will enumerate the plugins inside each dll by loading them. /// </summary> /// <param name="xmlFilePath">The Package Definition xml file. Usually named package.xml</param> /// <param name="projectDir">Directory used byt GitVersionCalculator to expand any $(GitVersion) macros in the XML file.</param> /// <returns></returns> public static PackageDef FromInputXml(string xmlFilePath, string projectDir) { PackageDef.ValidateXml(xmlFilePath); var pkgDef = PackageDef.FromXml(xmlFilePath); if (pkgDef.Files.Any(f => f.HasCustomData <UseVersionData>() && f.HasCustomData <SetAssemblyInfoData>())) { throw new InvalidDataException("A file cannot specify <SetAssemblyInfo/> and <UseVersion/> at the same time."); } pkgDef.Files = expandGlobEntries(pkgDef.Files); var excludeAdd = pkgDef.Files.Where(file => file.IgnoredDependencies != null).SelectMany(file => file.IgnoredDependencies).Distinct().ToList(); List <Exception> exceptions = new List <Exception>(); foreach (PackageFile item in pkgDef.Files) { string fullPath = Path.GetFullPath(item.FileName); if (!File.Exists(fullPath)) { string fileName = Path.GetFileName(item.FileName); if (File.Exists(fileName) && item.SourcePath == null) { // this is to support building everything to the root folder. This way the developer does not have to specify SourcePath. log.Warning("Specified file '{0}' was not found, using file '{1}' as source instead. Consider setting SourcePath to remove this warning.", item.FileName, fileName); item.SourcePath = fileName; } else { exceptions.Add(new FileNotFoundException("Missing file for package.", fullPath)); } } } if (exceptions.Count > 0) { throw new AggregateException("Missing files", exceptions); } pkgDef.Date = DateTime.UtcNow; // Copy to output directory first foreach (var file in pkgDef.Files) { if (file.RelativeDestinationPath != file.FileName) { try { var destPath = Path.GetFullPath(file.RelativeDestinationPath); if (!File.Exists(destPath)) { Directory.CreateDirectory(Path.GetDirectoryName(destPath)); ProgramHelper.FileCopy(file.FileName, destPath); } } catch { // Catching here. The files might be used by themselves } } } var searcher = new PluginSearcher(PluginSearcher.Options.IncludeSameAssemblies); searcher.Search(Directory.GetCurrentDirectory()); List <AssemblyData> assemblies = searcher.Assemblies.ToList(); // Enumerate plugins if this has not already been done. if (!pkgDef.Files.SelectMany(pfd => pfd.Plugins).Any()) { EnumeratePlugins(pkgDef, assemblies); } log.Info("Updating package version."); pkgDef.updateVersion(projectDir); log.Info("Package version is {0}", pkgDef.Version); pkgDef.findDependencies(excludeAdd, assemblies); return(pkgDef); }
/// <summary> /// Gets the AssemblyData for the OpenTap.dll assembly. /// This will search for plugins if not done already (i.e. call and wait for <see cref="PluginManager.SearchAsync()"/>) /// </summary> public static AssemblyData GetOpenTapAssembly() { PluginSearcher searcher = GetSearcher(); return(searcher.PluginMarkerType.Assembly); }
public void CreatePackageDepReuse() { if (Directory.Exists("Packages2")) { Directory.Delete("Packages2", true); } Directory.Move("Packages", "Packages2"); File.Copy("Packages2/DependencyTest.dll", "DependencyTest.dll", true); try { Directory.CreateDirectory("Packages"); { PackageDef def = new PackageDef() { Name = "pkg1", Version = SemanticVersion.Parse("1.2") }; def.AddFile("OpenTap.dll"); Directory.CreateDirectory("Packages/pkg1"); using (var f = File.OpenWrite("Packages/pkg1/package.xml")) def.SaveTo(f); } { PackageDef def = new PackageDef() { Name = "gui", Version = SemanticVersion.Parse("1.2") }; def.AddFile("Keysight.OpenTap.Wpf.dll"); Directory.CreateDirectory("Packages/gui"); using (var f = File.OpenWrite("Packages/gui/package.xml")) def.SaveTo(f); } { PackageDef def = new PackageDef() { Name = "rv", Version = SemanticVersion.Parse("1.2") }; def.AddFile("Keysight.OpenTap.Wpf.dll"); Directory.CreateDirectory("Packages/rv"); using (var f = File.OpenWrite("Packages/rv/package.xml")) def.SaveTo(f); } { PackageDef def = new PackageDef(); def.Name = "test"; def.InfoLink = "a"; def.Date = DateTime.Today; def.Dependencies.Add(new PackageDependency("rv", VersionSpecifier.Parse("1.2"))); def.AddFile("DependencyTest.dll"); def.Files[0].DependentAssemblies.AddRange(PluginManager.GetSearcher().Assemblies.First(f => f.Name == "DependencyTest").References); var searcher = new PluginSearcher(); searcher.Search(Directory.GetCurrentDirectory()); List <AssemblyData> assemblies = searcher.Assemblies.ToList(); def.findDependencies(new List <string>(), assemblies); //Assert.AreEqual(3, def.Dependencies.Count); Assert.IsTrue(def.Dependencies.Any(d => d.Name == "rv")); Assert.IsTrue(def.Dependencies.Any(d => d.Name == "pkg1")); } { PackageDef def = new PackageDef(); def.Name = "test"; def.InfoLink = "a"; def.Date = DateTime.Today; def.Dependencies.Add(new PackageDependency("gui", VersionSpecifier.Parse("1.2"))); def.AddFile("DependencyTest.dll"); def.Files[0].DependentAssemblies.AddRange(PluginManager.GetSearcher().Assemblies.First(f => f.Name == "DependencyTest").References); var searcher = new PluginSearcher(); searcher.Search(Directory.GetCurrentDirectory()); List <AssemblyData> assemblies = searcher.Assemblies.ToList(); def.findDependencies(new List <string>(), assemblies); //Assert.AreEqual(2, def.Dependencies.Count); Assert.IsTrue(def.Dependencies.Any(d => d.Name == "gui")); Assert.IsTrue(def.Dependencies.Any(d => d.Name == "pkg1")); } } finally { if (Directory.Exists("Packages")) { Directory.Delete("Packages", true); } Directory.Move("Packages2", "Packages"); } }