xamarin-macios/tools/common/PathUtils.cs

253 строки
6.8 KiB
C#
Исходник Обычный вид История

using System;
2016-04-21 16:40:25 +03:00
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
2016-04-21 16:40:25 +03:00
namespace Xamarin.Utils {
public static class PathUtils {
2016-04-21 16:40:25 +03:00
static bool IsSeparator (char c)
{
return c == Path.DirectorySeparatorChar || c == Path.AltDirectorySeparatorChar || c == Path.VolumeSeparatorChar;
}
static char ToOrdinalIgnoreCase (char c)
{
return (((uint) c - 'a') <= ((uint) 'z' - 'a')) ? (char) (c - 0x20) : c;
}
[msbuild] Rework code signing. The main theme here is that code signing will be done in the outermost executable project, not in any app extension projects or watch projects, nor during the RID-specific build of a .NET universal app. This makes codesigning easier to reason about and other affected logic (such as strip/dsymutil) easier to handle, in particular for .NET universal apps. Another benefit is that the differences between the iOS and macOS code bases have been eliminated. The first step is to collect all the information we need from the targets files. Every app bundle (be it app extension, watch app or main app) will add its own output app bundle (.app/.appex) to the _CodesignBundle item group. Then every app bundle will load this informarion from referenced app bundles, and finally store this information on disk (in the 'codesign-bundle.items' file). This means that in the end the main app bundle will have a list of all contained app bundles in the app (recursively), in the _CodesignBundle item group. Separately we keep a list of other items that need signing, in the _CodesignItems item group, and we do the same store/load logic for every contained/contained app bundle (in the 'codesign.items' file, so a the end the main app bundle will have a list of all the _CodesignItems for all contained app bundles (recursively). The previous steps occur in the _CollectCodesigningData and _StoreCodesigningData targets. The next step is to use the new ComputeCodesignItems task to compute everything we need to know for code signing. This task takes over the responsibility for listing all the *.dylib and *.metallib files, and the *.framework directories in the app bundles, that need signing (which was previously done in the targets file). This logic is significantly easier to write, debug and test in C# than MSBuild. In addition the ComputeCodesignItems also figures out a stamp file path we use to determine if something needs (re-)signing. Previously .framework directories did not have a stamp location, so they'd always end up resigned in a rebuild, while now we'll automatically skip signing *.framework directories unless something changed in them. I've also tried to comment everything thorougly, for the next poor soul having to deal with any bugs, as well has adding a comprehensive test for the new task. Behavioral differences: * We were always signing *.dylib files for macOS. We're now doing the same thing for all platforms. * We're now always signing *.framework directories for all platforms (like we do for *.dylib files), since frameworks are pretty much like dylibs anyways.
2022-02-11 15:55:22 +03:00
public static string EnsureTrailingSlash (this string path)
{
if (path is null)
return null;
if (path.Length == 0)
return Path.DirectorySeparatorChar.ToString ();
if (path [path.Length - 1] != Path.DirectorySeparatorChar)
path += Path.DirectorySeparatorChar;
return path;
}
2016-04-21 16:40:25 +03:00
[DllImport ("/usr/lib/libc.dylib")]
static extern IntPtr realpath (string path, IntPtr buffer);
public static string ResolveSymbolicLinks (string path)
2016-04-21 16:40:25 +03:00
{
if (string.IsNullOrEmpty (path))
return path;
2016-04-21 16:40:25 +03:00
if (Path.DirectorySeparatorChar == '\\')
return Path.GetFullPath (path);
const int PATHMAX = 4096 + 1;
var buffer = IntPtr.Zero;
2016-04-21 16:40:25 +03:00
try {
buffer = Marshal.AllocHGlobal (PATHMAX);
var result = realpath (path, buffer);
return result == IntPtr.Zero ? path : Marshal.PtrToStringAuto (buffer);
2016-04-21 16:40:25 +03:00
} finally {
if (buffer != IntPtr.Zero)
Marshal.FreeHGlobal (buffer);
}
}
public static string AbsoluteToRelative (string baseDirectory, string absolute)
{
if (string.IsNullOrEmpty (baseDirectory))
2016-04-21 16:40:25 +03:00
return absolute;
// canonicalize the paths
baseDirectory = Path.GetFullPath (baseDirectory).TrimEnd (Path.DirectorySeparatorChar);
absolute = Path.GetFullPath (absolute);
int baseDirectoryStartIndex = baseDirectory.Length;
int absoluteStartIndex = absolute.Length;
int separators = 0;
int index = 0;
while (index < absolute.Length) {
if (ToOrdinalIgnoreCase (absolute [index]) != ToOrdinalIgnoreCase (baseDirectory [index]))
2016-04-21 16:40:25 +03:00
break;
if (IsSeparator (absolute [index])) {
2016-04-21 16:40:25 +03:00
baseDirectoryStartIndex = index;
absoluteStartIndex = index + 1;
separators++;
}
index++;
if (index >= baseDirectory.Length) {
if (index >= absolute.Length || IsSeparator (absolute [index])) {
2016-04-21 16:40:25 +03:00
baseDirectoryStartIndex = index;
absoluteStartIndex = index + 1;
separators++;
}
break;
}
}
if (separators == 0)
return absolute;
if (absoluteStartIndex >= absolute.Length)
return ".";
if (index >= absolute.Length && IsSeparator (baseDirectory [index])) {
2016-04-21 16:40:25 +03:00
absoluteStartIndex = index + 1;
baseDirectoryStartIndex = index;
}
int parentDirCount = 0;
while (baseDirectoryStartIndex < baseDirectory.Length) {
if (IsSeparator (baseDirectory [baseDirectoryStartIndex]))
2016-04-21 16:40:25 +03:00
parentDirCount++;
baseDirectoryStartIndex++;
}
var size = (parentDirCount * 3) + (absolute.Length - absoluteStartIndex);
var result = new char [size];
index = 0;
for (int i = 0; i < parentDirCount; i++) {
result [index++] = '.';
result [index++] = '.';
result [index++] = Path.DirectorySeparatorChar;
2016-04-21 16:40:25 +03:00
}
while (absoluteStartIndex < absolute.Length)
result [index++] = absolute [absoluteStartIndex++];
2016-04-21 16:40:25 +03:00
return new string (result);
}
public static string RelativeToAbsolute (string baseDirectory, string relative)
{
return Path.GetFullPath (Path.Combine (baseDirectory, relative));
}
[DllImport ("/usr/lib/libSystem.dylib", SetLastError = true)]
static extern int symlink (string path1, string path2);
public static bool Symlink (string target, string symlink)
{
return PathUtils.symlink (target, symlink) == 0;
}
public static void CreateSymlink (string symlink, string target)
{
FileDelete (symlink); // Delete any existing symlinks.
var rv = PathUtils.symlink (target, symlink);
if (rv != 0)
throw new Exception (string.Format ("Could not create the symlink '{0}': {1}", symlink, Marshal.GetLastWin32Error ()));
}
[DllImport ("/usr/lib/libSystem.dylib", SetLastError = true)]
static extern int readlink (string path, [Out] byte [] buffer, IntPtr len);
public static string GetSymlinkTarget (string path)
{
byte [] buffer = null;
int rv;
do {
buffer = new byte [(buffer?.Length ?? 0) + 1024];
rv = readlink (path, buffer, (IntPtr) (buffer.Length - 1));
} while (rv == buffer.Length - 1);
if (rv == -1)
throw new Exception (string.Format ("Could not readlink '{0}': {1}", path, Marshal.GetLastWin32Error ()));
return Encoding.UTF8.GetString (buffer, 0, rv);
}
[DllImport ("/usr/lib/libSystem.dylib")]
static extern int unlink (string pathname);
// File.Delete can't always delete symlinks (in particular if the symlink points to a file that doesn't exist).
public static void FileDelete (string file)
{
unlink (file);
// ignore any errors.
}
struct timespec {
public IntPtr tv_sec;
public IntPtr tv_nsec;
}
struct stat { /* when _DARWIN_FEATURE_64_BIT_INODE is defined */
public uint st_dev;
public ushort st_mode;
public ushort st_nlink;
public ulong st_ino;
public uint st_uid;
public uint st_gid;
public uint st_rdev;
public timespec st_atimespec;
public timespec st_mtimespec;
public timespec st_ctimespec;
public timespec st_birthtimespec;
public ulong st_size;
public ulong st_blocks;
public uint st_blksize;
public uint st_flags;
public uint st_gen;
public uint st_lspare;
public ulong st_qspare_1;
public ulong st_qspare_2;
}
[DllImport ("/usr/lib/libc.dylib", EntryPoint = "lstat$INODE64", SetLastError = true)]
static extern int lstat_x64 (string file_name, out stat buf);
[DllImport ("/usr/lib/libc.dylib", EntryPoint = "lstat", SetLastError = true)]
static extern int lstat_arm64 (string file_name, out stat buf);
static int lstat (string path, out stat buf)
{
if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64) {
return lstat_arm64 (path, out buf);
} else {
return lstat_x64 (path, out buf);
}
}
public static bool IsSymlink (string file)
{
stat buf;
var rv = lstat (file, out buf);
if (rv != 0)
throw new Exception (string.Format ("Could not lstat '{0}': {1}", file, Marshal.GetLastWin32Error ()));
const int S_IFLNK = 40960;
return (buf.st_mode & S_IFLNK) == S_IFLNK;
}
public static bool IsSymlinkOrContainsSymlinks (string directoryOrFile)
{
if (IsSymlink (directoryOrFile))
return true;
if (!Directory.Exists (directoryOrFile))
return false;
foreach (var entry in Directory.EnumerateFileSystemEntries (directoryOrFile)) {
if (IsSymlinkOrContainsSymlinks (entry))
return true;
}
return false;
}
// Replace any windows-style slashes with mac-style slashes.
public static string ConvertToMacPath (string path)
{
if (string.IsNullOrEmpty (path))
return path;
return path.Replace ('\\', '/');
}
2016-04-21 16:40:25 +03:00
}
}