Skip to content

Instantly share code, notes, and snippets.

@jbevain
Created April 20, 2021 00:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jbevain/875ddc52b331b6ef2d883c6af1606a0a to your computer and use it in GitHub Desktop.
Save jbevain/875ddc52b331b6ef2d883c6af1606a0a to your computer and use it in GitHub Desktop.
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