2020-10-09 09:32:10 +03:00
|
|
|
using System;
|
2016-04-21 16:40:25 +03:00
|
|
|
using System.IO;
|
|
|
|
using System.Runtime.InteropServices;
|
2021-05-21 23:29:08 +03:00
|
|
|
using System.Text;
|
2016-04-21 16:40:25 +03:00
|
|
|
|
2022-09-28 17:25:35 +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);
|
|
|
|
|
2016-10-10 11:24:41 +03:00
|
|
|
public static string ResolveSymbolicLinks (string path)
|
2016-04-21 16:40:25 +03:00
|
|
|
{
|
2016-10-10 11:24:41 +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;
|
2016-10-10 11:24:41 +03:00
|
|
|
var buffer = IntPtr.Zero;
|
2016-04-21 16:40:25 +03:00
|
|
|
|
|
|
|
try {
|
|
|
|
buffer = Marshal.AllocHGlobal (PATHMAX);
|
|
|
|
var result = realpath (path, buffer);
|
2017-03-12 21:51:28 +03:00
|
|
|
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)
|
|
|
|
{
|
2017-03-12 21:51:28 +03:00
|
|
|
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) {
|
2022-09-28 17:25:35 +03:00
|
|
|
if (ToOrdinalIgnoreCase (absolute [index]) != ToOrdinalIgnoreCase (baseDirectory [index]))
|
2016-04-21 16:40:25 +03:00
|
|
|
break;
|
|
|
|
|
2022-09-28 17:25:35 +03:00
|
|
|
if (IsSeparator (absolute [index])) {
|
2016-04-21 16:40:25 +03:00
|
|
|
baseDirectoryStartIndex = index;
|
|
|
|
absoluteStartIndex = index + 1;
|
|
|
|
separators++;
|
|
|
|
}
|
|
|
|
|
|
|
|
index++;
|
|
|
|
|
|
|
|
if (index >= baseDirectory.Length) {
|
2022-09-28 17:25:35 +03:00
|
|
|
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 ".";
|
|
|
|
|
2022-09-28 17:25:35 +03:00
|
|
|
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) {
|
2022-09-28 17:25:35 +03:00
|
|
|
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++) {
|
2022-09-28 17:25:35 +03:00
|
|
|
result [index++] = '.';
|
|
|
|
result [index++] = '.';
|
|
|
|
result [index++] = Path.DirectorySeparatorChar;
|
2016-04-21 16:40:25 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
while (absoluteStartIndex < absolute.Length)
|
2022-09-28 17:25:35 +03:00
|
|
|
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));
|
|
|
|
}
|
2021-05-21 23:29:08 +03:00
|
|
|
|
|
|
|
[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 ()));
|
|
|
|
}
|
2021-05-21 23:29:08 +03:00
|
|
|
|
|
|
|
[DllImport ("/usr/lib/libSystem.dylib", SetLastError = true)]
|
2022-09-28 17:25:35 +03:00
|
|
|
static extern int readlink (string path, [Out] byte [] buffer, IntPtr len);
|
2021-05-21 23:29:08 +03:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2021-05-21 23:29:08 +03:00
|
|
|
[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;
|
|
|
|
}
|
|
|
|
|
2021-08-16 17:21:21 +03:00
|
|
|
[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);
|
|
|
|
}
|
|
|
|
}
|
2021-05-21 23:29:08 +03:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2021-10-01 09:40:01 +03:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2022-03-10 05:53:14 +03:00
|
|
|
|
|
|
|
// 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
|
|
|
}
|
|
|
|
}
|