Created
April 20, 2021 00:25
-
-
Save jbevain/875ddc52b331b6ef2d883c6af1606a0a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.IO; | |
using System.Linq; | |
using System.Xml.Linq; | |
namespace UnityProjectExtractor | |
{ | |
internal readonly struct PathMap | |
{ | |
private readonly string _projectDirectory; | |
private readonly string _targetDirectory; | |
public PathMap(string projectDirectory, string targetDirectory) | |
{ | |
_projectDirectory = projectDirectory; | |
_targetDirectory = targetDirectory; | |
} | |
public string ProjectPath(string file) | |
{ | |
if (Path.IsPathRooted(file)) | |
return file; | |
return Path.Combine(_projectDirectory, file); | |
} | |
public string TargetPath(string file) | |
{ | |
return Path.Combine(_targetDirectory, file); | |
} | |
public string ProjectToTarget(string file) | |
{ | |
if (!Path.IsPathRooted(file)) | |
file = Path.Combine(_projectDirectory, file); | |
if (!file.StartsWith(_projectDirectory)) | |
{ | |
throw new ArgumentException("File is not in the project"); | |
} | |
return file.Replace(_projectDirectory, _targetDirectory); | |
} | |
} | |
internal class Program | |
{ | |
private static int Main(string[] args) | |
{ | |
if (args.Length != 2) | |
{ | |
Usage(); | |
return 1; | |
} | |
var projectDirectory = Path.GetFullPath(args[0]); | |
var targetDirectory = Path.GetFullPath(args[1]); | |
if (!Directory.Exists(projectDirectory)) | |
{ | |
Usage(); | |
return 1; | |
} | |
if (Directory.Exists(targetDirectory)) | |
{ | |
if (Directory.GetFileSystemEntries(targetDirectory).Length != 0) | |
{ | |
Usage(); | |
return 1; | |
} | |
} | |
else | |
{ | |
Directory.CreateDirectory(targetDirectory); | |
} | |
Console.WriteLine($"Extracting {projectDirectory} into {targetDirectory}"); | |
var map = new PathMap(projectDirectory, targetDirectory); | |
var solutions = Directory.GetFiles(projectDirectory, "*.sln", SearchOption.TopDirectoryOnly); | |
if (solutions.Length != 1) | |
{ | |
Error("multiple solutions found"); | |
} | |
foreach (var solution in solutions) | |
{ | |
CopyFile(solution, map.ProjectToTarget(solution)); | |
} | |
var solutionText = File.ReadAllText(solutions[0]); | |
var projects = Directory | |
.GetFiles(projectDirectory, "*.csproj", SearchOption.TopDirectoryOnly) | |
.Where(p => solutionText.Contains(Path.GetFileName(p))) | |
.ToArray(); | |
foreach (var project in projects) | |
{ | |
CopyFile(project, map.ProjectToTarget(project)); | |
} | |
foreach (var project in projects) | |
{ | |
CopyProject(map.ProjectToTarget(project), map); | |
} | |
return 0; | |
} | |
private static void CopyProject(string projectFile, in PathMap map) | |
{ | |
XNamespace ns = "http://schemas.microsoft.com/developer/msbuild/2003"; | |
var doc = XDocument.Load(projectFile); | |
var projectTypeGuids = doc.Descendants(ns + "ProjectTypeGuids").ToArray(); | |
if (projectTypeGuids.Length == 1) | |
{ | |
projectTypeGuids[0].Parent.Remove(); | |
} | |
var analyzers = doc.Descendants(ns + "Analyzer"); | |
foreach (var analyzer in analyzers.ToArray()) | |
analyzer.Remove(); | |
var nones = doc.Descendants(ns + "None"); | |
foreach (var none in nones.ToArray()) | |
none.Remove(); | |
var compiles = doc.Descendants(ns + "Compile"); | |
foreach (var compile in compiles) | |
{ | |
var includeAttr = compile.Attribute("Include"); | |
var includePath = ProjectFilePath(includeAttr.Value, map); | |
string targetPath; | |
var index = includePath.IndexOf("BuiltInPackages"); | |
if (index > 0) | |
{ | |
var localPath = Path.Combine("Library", includePath.Substring(index)); | |
includeAttr.Value = localPath.Replace(Path.DirectorySeparatorChar, '\\'); | |
targetPath = map.TargetPath(localPath); | |
} | |
else | |
{ | |
targetPath = map.ProjectToTarget(includePath); | |
} | |
CopyFile(includePath, targetPath, overwrite: true); | |
} | |
var hintPaths = doc.Descendants(ns + "HintPath"); | |
foreach (var hintPath in hintPaths) | |
{ | |
var referencePath = hintPath.Value; | |
if (!Path.IsPathRooted(referencePath)) | |
referencePath = map.ProjectPath(referencePath); | |
var targetPath = ReferencePath(referencePath, map); | |
hintPath.Value = targetPath.Replace(Path.DirectorySeparatorChar, '\\'); | |
CopyFile(referencePath, map.TargetPath(targetPath), overwrite: true); | |
} | |
doc.Save(projectFile); | |
} | |
private static string ReferencePath(string hintPath, in PathMap map) | |
{ | |
var directory = new DirectoryInfo(Path.GetDirectoryName(hintPath)); | |
return Path.Combine("Library", "References", directory.Name, Path.GetFileName(hintPath)); | |
} | |
private static string ProjectFilePath(string path, in PathMap map) | |
{ | |
return map.ProjectPath(path.Replace('\\', Path.DirectorySeparatorChar)); | |
} | |
private static void CopyFile(string origin, string target, bool overwrite = false) | |
{ | |
var targetPath = Path.GetDirectoryName(target); | |
if (!Directory.Exists(targetPath)) | |
Directory.CreateDirectory(targetPath); | |
File.Copy(origin, target, overwrite); | |
} | |
private static void Error(string message) | |
{ | |
Console.WriteLine(message); | |
Environment.Exit(1); | |
} | |
private static void Usage() | |
{ | |
Console.WriteLine("UnityProjectExtractor projectDirectory targetDirectory"); | |
Console.WriteLine(" requires targetDirectory to be empty"); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment