Skip to content

Commit 5eb62c2

Browse files
committed
Locate dotnet tool ipy launcher
1 parent 153895e commit 5eb62c2

File tree

1 file changed

+58
-5
lines changed

1 file changed

+58
-5
lines changed

src/core/IronPython/Hosting/PythonCommandLine.cs

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
using System.Collections.Generic;
77
using System.Diagnostics;
88
using System.IO;
9+
using System.IO.MemoryMappedFiles;
910
using System.Reflection;
1011
using System.Runtime.InteropServices;
12+
using System.Runtime.Versioning;
1113
using System.Text;
1214
using System.Threading;
1315

@@ -261,17 +263,18 @@ private void InitializeModules() {
261263

262264
var name = Path.GetFileNameWithoutExtension(executable);
263265
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
264-
var runner = Path.Combine(prefix, name + ".exe");
265-
if (File.Exists(runner)) {
266+
var exename = name + ".exe";
267+
var runner = Path.Combine(prefix, exename);
268+
if (File.Exists(runner) || FindRunner(prefix, exename, executable, out runner)) {
266269
executable = runner;
267270
} else {
268-
// TODO: was for .NET Core 2.1, can we drop this?
271+
// ipy.bat is created Install-IronPython.ps1, which installs from a zip file
269272
runner = Path.Combine(prefix, name + ".bat");
270273
if (File.Exists(runner)) executable = runner;
271274
}
272275
} else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
273276
var runner = Path.Combine(prefix, name);
274-
if (File.Exists(runner)) {
277+
if (File.Exists(runner) || FindRunner(prefix, name, executable, out runner)) {
275278
executable = runner;
276279
} else {
277280
runner = Path.Combine(prefix, name + ".sh");
@@ -289,7 +292,7 @@ private void InitializeModules() {
289292
if (File.Exists(path)) {
290293
foreach (var line in File.ReadAllLines(path, Encoding.UTF8)) { // TODO: this actually needs to be decoded with surrogateescape
291294
if (line.StartsWith('#')) continue;
292-
var split = line.Split(new[] { '=' }, 2);
295+
var split = line.Split(['='], 2);
293296
if (split.Length != 2) continue;
294297
if (split[0].Trim() == "home") {
295298
pyvenv_prefix = split[1].Trim();
@@ -309,6 +312,56 @@ private void InitializeModules() {
309312
}
310313

311314
PythonContext.SetHostVariables(prefix ?? "", executable, null);
315+
316+
// --------
317+
static bool FindRunner(string prefix, string name, string assembly, out string runner) {
318+
runner = null;
319+
while (prefix != null) {
320+
runner = Path.Combine(prefix, name);
321+
if (File.Exists(runner)) {
322+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || IsExecutable(runner)) {
323+
break;
324+
}
325+
}
326+
prefix = Path.GetDirectoryName(prefix);
327+
}
328+
if (runner != null && Path.GetExtension(assembly).Equals(".dll", StringComparison.OrdinalIgnoreCase)) {
329+
// make sure that the runner refers to this DLL
330+
var relativeAssemblyPath = assembly.Substring(prefix.Length + 1); // skip over the path separator
331+
byte[] fsAssemblyPath = Encoding.UTF8.GetBytes(relativeAssemblyPath);
332+
byte fsap0 = fsAssemblyPath[0];
333+
334+
try {
335+
using var mmf = MemoryMappedFile.CreateFromFile(runner, FileMode.Open, null, 0, MemoryMappedFileAccess.Read);
336+
using var accessor = mmf.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read);
337+
338+
for (long i = accessor.Capacity - fsAssemblyPath.Length; i >= 0; i--) { // the path should be close to the end of the file
339+
if (accessor.ReadByte(i) != fsap0) {
340+
continue;
341+
}
342+
bool found = true;
343+
for (int j = 1; j < fsAssemblyPath.Length; j++) {
344+
if (accessor.ReadByte(i + j) != fsAssemblyPath[j]) {
345+
found = false;
346+
break;
347+
}
348+
}
349+
if (found) {
350+
return true;
351+
}
352+
}
353+
} catch { }
354+
}
355+
return false;
356+
}
357+
358+
[UnsupportedOSPlatform("windows")]
359+
static bool IsExecutable(string filePath) {
360+
var fileInfo = new Mono.Unix.UnixFileInfo(filePath);
361+
var fileMode = fileInfo.FileAccessPermissions;
362+
363+
return (fileMode & (Mono.Unix.FileAccessPermissions.UserExecute | Mono.Unix.FileAccessPermissions.GroupExecute | Mono.Unix.FileAccessPermissions.OtherExecute)) != 0;
364+
}
312365
}
313366

314367
/// <summary>

0 commit comments

Comments
 (0)