Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 108 additions & 0 deletions tools/xabuild/SymbolicLink.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ static class SymbolicLink
public static bool Create (string source, string target)
{
if (!Directory.Exists (source)) {
Directory.CreateDirectory (Path.GetDirectoryName (source));
if (Environment.OSVersion.Platform == PlatformID.Win32NT) {
return CreateWindowsSymLink (source, target);
} else {
Expand Down Expand Up @@ -50,6 +51,74 @@ enum SymbolLinkFlag {
AllowUnprivilegedCreate = 2,
}

public static string GetRealPath (string path)
{
if (string.IsNullOrEmpty (path))
return null;

if (Environment.OSVersion.Platform == PlatformID.Win32NT) {
return GetWindowsRealPath (path);
} else {
return GetUnixRealPath (path);
}
}

static string GetWindowsRealPath (string path)
{
const FileAttributes FILE_FLAG_BACKUP_SEMANTICS = (FileAttributes) 0x02000000;
const FileAccess GENERIC_READ = unchecked((FileAccess) 0x80000000);
IntPtr handle = CreateFileW (lpFileName: path,
dwDesiredAccess: GENERIC_READ,
dwShareMode: FileShare.Read,
lpSecurityAttributes: IntPtr.Zero,
dwCreationDisposition: FileMode.Open,
dwFlagsAndAttributes: FILE_FLAG_BACKUP_SEMANTICS,
hTemplateFile: IntPtr.Zero);
if (handle == INVALID_FILE_HANDLE)
return null;
IntPtr finalPathBuf = IntPtr.Zero;
try {
const FinalPathFlags flags = FinalPathFlags.FILE_NAME_OPENED;
uint len = GetFinalPathNameByHandleW (handle, IntPtr.Zero, 0, flags);
if (len == 0)
return null;
len = checked(len + 1);
finalPathBuf = Marshal.AllocHGlobal (checked ((int) (sizeof (char)*(len))));
uint checkLen = GetFinalPathNameByHandleW (handle, finalPathBuf, len, flags);
if (checkLen == 0 || checkLen > len) {
Console.Error.WriteLine ($"GetFinalPathNameByHandleW: expected {len}, got {checkLen}. Last Error: {Marshal.GetLastWin32Error()}");
return null;
}
const string LocalUncPathPrefix = @"\\?\";
string finalPath = Marshal.PtrToStringUni (finalPathBuf);
if (finalPath?.StartsWith (LocalUncPathPrefix, StringComparison.Ordinal) ?? false)
finalPath = finalPath.Substring (LocalUncPathPrefix.Length);
return finalPath;
}
finally {
Marshal.FreeHGlobal (finalPathBuf);
CloseHandle (handle);
}
}

static string GetUnixRealPath (string path)
{
IntPtr buf = realpath (path, IntPtr.Zero);
try {
if (buf == IntPtr.Zero)
return null;
return Marshal.PtrToStringAnsi (buf);
}
finally {
free (buf);
}
}

public static bool IsPathSymlink (string path)
{
return Path.GetFullPath (path) != GetRealPath (path);
}

[DllImport ("kernel32.dll")]
[return: MarshalAs (UnmanagedType.I1)]
static extern bool CreateSymbolicLink (string lpSymlinkFileName, string lpTargetFileName, SymbolLinkFlag dwFlags);
Expand All @@ -59,5 +128,44 @@ enum SymbolLinkFlag {

[DllImport ("libc")]
static extern void perror (string s);

[DllImport ("libc")]
static extern IntPtr realpath (string file_name, IntPtr resolved_name);

[DllImport ("libc")]
static extern void free (IntPtr p);

static readonly IntPtr INVALID_FILE_HANDLE = new IntPtr (-1);

[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr CreateFileW(
[MarshalAs(UnmanagedType.LPWStr)] string lpFileName,
[MarshalAs(UnmanagedType.U4)] FileAccess dwDesiredAccess,
[MarshalAs(UnmanagedType.U4)] FileShare dwShareMode,
IntPtr lpSecurityAttributes,
[MarshalAs(UnmanagedType.U4)] FileMode dwCreationDisposition,
[MarshalAs(UnmanagedType.U4)] FileAttributes dwFlagsAndAttributes,
IntPtr hTemplateFile);

[DllImport("kernel32.dll", SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CloseHandle(IntPtr hObject);

[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern uint GetFinalPathNameByHandleW(
IntPtr hFile,
IntPtr lpszFilePath,
uint cchFilePath,
FinalPathFlags dwFlags);
}

[Flags]
enum FinalPathFlags : uint {
VOLUME_NAME_DOS = 0x0,
FILE_NAME_NORMALIZED = 0x0,
VOLUME_NAME_GUID = 0x1,
VOLUME_NAME_NT = 0x2,
VOLUME_NAME_NONE = 0x4,
FILE_NAME_OPENED = 0x8,
}
}
29 changes: 23 additions & 6 deletions tools/xabuild/XABuild.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,22 +38,39 @@ static int Main ()
// Create a Microsoft.Build.NuGetSdkResolver.xml
CreateSdkResolverConfig (paths);

//Symbolic links to be created: key=system, value=in-tree
//Symbolic links to be created: key=in-tree-dir, value=system-dir
var symbolicLinks = new Dictionary<string, string> ();
foreach (var dir in Directory.EnumerateDirectories (paths.SystemFrameworks)) {
if (Path.GetFileName (dir) != "MonoAndroid") {
symbolicLinks [dir] = Path.Combine (paths.FrameworksDirectory, Path.GetFileName (dir));
var inTreeFramework = Path.Combine (paths.FrameworksDirectory, Path.GetFileName (dir));
symbolicLinks [inTreeFramework] = dir;
}
}
foreach (var dir in paths.SystemTargetsDirectories) {
symbolicLinks [dir] = Path.Combine (paths.MSBuildExtensionsPath, Path.GetFileName (dir));
var inTreeTargetsDir = Path.Combine (paths.MSBuildExtensionsPath, Path.GetFileName (dir));
if (!symbolicLinks.ContainsKey (inTreeTargetsDir)) {
symbolicLinks [inTreeTargetsDir] = dir;
continue;
}
var prevTargetDir = symbolicLinks [inTreeTargetsDir];
symbolicLinks.Remove (inTreeTargetsDir);
if (Directory.Exists (inTreeTargetsDir) && SymbolicLink.IsPathSymlink (inTreeTargetsDir)) {
Console.WriteLine ($"Removing old symlink: {inTreeTargetsDir}");
Directory.Delete (inTreeTargetsDir);
}
var subTargetDirs = Directory.EnumerateDirectories (prevTargetDir)
.Concat (Directory.EnumerateDirectories (dir));
foreach (var subDir in subTargetDirs) {
var inTreeTargetSubdir = Path.Combine (inTreeTargetsDir, Path.GetFileName (subDir));
symbolicLinks [inTreeTargetSubdir] = subDir;
}
}
if (symbolicLinks.Values.Any (d => !Directory.Exists (d))) {
if (symbolicLinks.Keys.Any (d => !Directory.Exists (d))) {
//Hold open the file while creating the symbolic links
using (var writer = OpenSysLinksFile (paths)) {
foreach (var pair in symbolicLinks) {
var systemDirectory = pair.Key;
var symbolicLink = pair.Value;
var systemDirectory = pair.Value;
var symbolicLink = pair.Key;
Console.WriteLine ($"[xabuild] creating symbolic link '{symbolicLink}' -> '{systemDirectory}'");
if (!SymbolicLink.Create (symbolicLink, systemDirectory)) {
return 1;
Expand Down
6 changes: 5 additions & 1 deletion tools/xabuild/XABuildPaths.cs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,11 @@ public XABuildPaths ()
MSBuildSdksPath = DotNetSdkPath ?? Path.Combine (MSBuildPath, "Sdks");
SystemFrameworks = Path.Combine (programFiles, "Reference Assemblies", "Microsoft", "Framework");
string msbuildDir = Path.GetDirectoryName (MSBuildBin);
SystemTargetsDirectories = new [] { msbuildDir, Path.Combine (MSBuildPath, "Microsoft") };
SystemTargetsDirectories = new [] {
msbuildDir,
Path.Combine (MSBuildPath, "Microsoft"),
Path.Combine (programFiles, "MSBuild", "Microsoft"),
};
SearchPathsOS = "windows";
string nuget = Path.Combine (MSBuildPath, "Microsoft", "NuGet", "16.0");
if (!Directory.Exists (nuget)) {
Expand Down