chr
2026-04-05 fe750b791d5b517cc4e9bc8e99a9a75139a0cfba
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//            Copyright Keysight Technologies 2012-2019
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, you can obtain one at http://mozilla.org/MPL/2.0/.
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Diagnostics;
using Tap.Shared;
 
namespace OpenTap
{
    class AssemblyFinder
    {
        static TraceSource log = Log.CreateSource("AssemblyFinder");
        public void Invalidate()
        {
            lastSearch = DateTime.MinValue;
        }
 
        public bool IncludeDependencies = false;
        public bool Quiet = false;
 
        public AssemblyFinder()
        {
            matching = new Memorizer<string, string[]>(x => allFiles.Where(y => Path.GetFileNameWithoutExtension(y) == x).ToArray());
        }
 
        public IEnumerable<string> DirectoriesToSearch = new List<string>();
 
        DateTime lastSearch = DateTime.MinValue;
        string[] allFiles = null;
        string[] allSearchFiles = null;
        Memorizer<string, string[]> matching;
        object syncLock = new object();
        public string[] FindAssemblies(string fileName)
        {
            SyncFiles();
            return matching.Invoke(fileName);
        }
 
        static bool StrEq(string a, string b) => string.Equals(a, b, StringComparison.OrdinalIgnoreCase);
 
        public string[] AllAssemblies()
        {
            SyncFiles();
            return allSearchFiles;
        }
 
        private struct SearchDir
        {
            public DirectoryInfo Info;
            public bool IgnorePlugins;
 
            public SearchDir(string dir, bool excludeFromSearch)
            {
                this.Info = new DirectoryInfo(dir);
                IgnorePlugins = excludeFromSearch;
            }
            public SearchDir(DirectoryInfo dir, bool excludeFromSearch)
            {
                this.Info = dir;
                IgnorePlugins = excludeFromSearch;
            }
        }
 
        /// <summary>
        /// Updates the dll file cache
        /// </summary>
        private void SyncFiles()
        {
            lock (syncLock)
            {
                if ((DateTime.Now - lastSearch) < TimeSpan.FromSeconds(8))
                    return;
 
                var sw = Stopwatch.StartNew();
                var files = new HashSet<string>(new PathUtils.PathComparer());
                var searchFiles = new HashSet<string>(new PathUtils.PathComparer());
                foreach (var search_dir in DirectoriesToSearch.ToHashSet(new PathUtils.PathComparer()))
                {
                    var dirToSearch = new Queue<SearchDir>();
                    dirToSearch.Enqueue(new SearchDir(search_dir, false));
                    while (dirToSearch.Any())
                    {
                        var dir = dirToSearch.Dequeue();
                        try
                        {
                            // This can happen if the package containing this directory was uninstalled.
                            if (!Directory.Exists(dir.Info.FullName)) 
                            {
                                log.Debug($"Skipping directory {dir.Info.FullName}; directory was removed.");
                                continue;
                            }
 
                            FileInfo[] filesInDir = dir.Info.GetFiles();
                            if (filesInDir.Any(x => StrEq(x.Name, ".OpenTapIgnore")))  // .OpenTapIgnore means we should ignore this folder and sub folders w.r.t. both Assembly resolution and Plugin searching
                                continue;
 
                            bool ignorePlugins = dir.IgnorePlugins;
 
                            foreach (var subDir in dir.Info.EnumerateDirectories())
                            {
                                if (StrEq(subDir.Name, "obj"))
                                    continue; // skip obj subfolder                        
                                var ignorePluginsInSubDir = dir.IgnorePlugins || StrEq(subDir.Name, "Dependencies");
                                if (IncludeDependencies)
                                    ignorePluginsInSubDir = false;
                                dirToSearch.Enqueue(new SearchDir(subDir, ignorePluginsInSubDir));
                            }
 
                            foreach (var file in filesInDir)
                            {
                                var ext = file.Extension;
                                if (false == (StrEq(ext, ".exe") || StrEq(ext, ".dll")))
                                    continue;
                                if (file.Name.Contains(".vshost."))
                                    continue;
 
                                files.Add(file.FullName);
                                if (!ignorePlugins)
                                    searchFiles.Add(file.FullName);
                            }
                        }
                        catch (Exception e)
                        {
                            if (!Quiet)
                            {
                                log.Error("Unable to enumerate directory '{0}': '{1}'", search_dir ?? "(null)", e.Message);
                                log.Debug(e);
                            }
                        }
                    }
                }
 
                allFiles = files.ToArray();
                allSearchFiles = searchFiles.ToArray();
                matching.InvalidateAll();
                lastSearch = DateTime.Now;
                if (!Quiet)
                    log.Debug(sw, "Found {0}/{1} assembly files.", searchFiles.Count, files.Count);
            }
        }
    }
}