Skip to content
← Back

src/driver/sdk_reference_path_resolver.ghul

1
namespace Driver is
2
// Locates the .NET reference-assembly directory to compile against when the
3
// compiler is invoked with source files but neither `-a` nor
4
// `--sdk-ref-path`. The install root comes from DOTNET_ROOT, or is derived
5
// from the runtime directory the compiler is itself running on; under it,
6
// the highest Microsoft.NETCore.App.Ref pack matching the target
7
// framework's version supplies `ref/<tfm>/`. Every step answers null rather
8
// than throwing, so a layout this doesn't recognise surfaces as the
9
// caller's "no reference assemblies supplied" error.
10
class SDK_REFERENCE_PATH_RESOLVER is
11
init() is si
12
13
resolve(target_framework: string) -> string? is
14
let dotnet_root = discover_dotnet_root()
15
16
if !dotnet_root? then
17
return null
18
fi
19
20
return resolve_under(dotnet_root, target_framework)
21
si
22
23
resolve_under(dotnet_root: string, target_framework: string) -> string? is
24
let version = framework_version(target_framework)
25
26
if !version? then
27
return null
28
fi
29
30
let packs_root = IO.Path.join([dotnet_root, "packs", "Microsoft.NETCore.App.Ref"])
31
32
if !IO.Directory.exists(packs_root) then
33
return null
34
fi
35
36
let pack = latest_ref_pack(packs_root, version)
37
38
if !pack? then
39
return null
40
fi
41
42
let reference_path = IO.Path.join([pack, "ref", target_framework])
43
44
if !IO.Directory.exists(reference_path) then
45
return null
46
fi
47
48
return reference_path
49
si
50
51
// "net10.0" -> "10.0", matching ref-pack directory names like "10.0.10".
52
// A moniker that isn't `net` followed by a version has no pack of this
53
// shape to find, so it answers null rather than globbing on a prefix
54
// that would match nothing (or, for a short value, indexing off the end).
55
framework_version(target_framework: string) -> string? is
56
if target_framework.length <= 3 \/ !target_framework.starts_with("net") then
57
return null
58
fi
59
60
let first = target_framework[3]
61
62
if first < '0' \/ first > '9' then
63
return null
64
fi
65
66
return target_framework.substring(3, target_framework.length - 3)
67
si
68
69
// The runtime directory is `shared/Microsoft.NETCore.App/<version>/`
70
// under the install root, so the root is three levels up. Trailing
71
// separators have to go first, or get_directory_name consumes one of
72
// them instead of a path segment and the walk stops a level short.
73
install_root_from_runtime_directory(runtime_directory: string) -> string? is
74
let trimmed = strip_trailing_separators(runtime_directory)
75
76
if trimmed.length == 0 then
77
return null
78
fi
79
80
let current mut = trimmed
81
82
for i in 0..3 do
83
let parent = IO.Path.get_directory_name(current)
84
85
if !parent? then
86
return null
87
fi
88
89
if parent.length == 0 then
90
return null
91
fi
92
93
current = parent
94
od
95
96
return current
97
si
98
99
strip_trailing_separators(path: string) -> string is
100
let result mut = path
101
102
while result.length > 0 /\ is_separator(result[result.length - 1]) do
103
result = result.substring(0, result.length - 1)
104
od
105
106
return result
107
si
108
109
// Both of the running platform's separators: on Windows that covers the
110
// native `\` as well as `/`, and on Unix `\` is a legal filename
111
// character and deliberately isn't stripped.
112
is_separator(c: char) -> bool =>
113
c == IO.Path.directory_separator_char \/ c == IO.Path.alt_directory_separator_char
114
115
latest_ref_pack(packs_root: string, framework_version: string) -> string? is
116
let best: string? mut = null
117
let best_version: System.Version? mut = null
118
119
for directory in IO.Directory.get_directories(packs_root, "{framework_version}.*") do
120
let version_text = IO.Path.get_file_name(directory) ?? ""
121
122
try
123
let version = System.Version.parse(version_text)
124
125
if !best_version? \/ version.compare_to(best_version) > 0 then
126
best_version = version
127
best = directory
128
fi
129
catch ex: System.Exception
130
// not a version directory; skip
131
yrt
132
od
133
134
return best
135
si
136
137
discover_dotnet_root() -> string? is
138
let env_root = System.Environment.get_environment_variable("DOTNET_ROOT")
139
140
if env_root? /\ env_root.length > 0 /\ IO.Directory.exists(env_root) then
141
return env_root
142
fi
143
144
let derived =
145
install_root_from_runtime_directory(
146
System.Runtime.InteropServices.RuntimeEnvironment.get_runtime_directory())
147
148
if !derived? \/ !IO.Directory.exists(derived) then
149
return null
150
fi
151
152
return derived
153
si
154
si
155
si