From 7bf338e925a7f22a79453d29fc4e90b31b323759 Mon Sep 17 00:00:00 2001 From: Andrew Russell Date: Tue, 20 Feb 2018 15:48:59 +1000 Subject: [PATCH] Gave CreateTemplate the ability to generate a new Solution. Updated documentation. Minor fixes. --- CreateTemplate/Program.cs | 285 ++++++++++++++++++++++++++++++++++++++++++---- README.md | 20 ++-- 2 files changed, 269 insertions(+), 36 deletions(-) diff --git a/CreateTemplate/Program.cs b/CreateTemplate/Program.cs index d75645b..df65f18 100644 --- a/CreateTemplate/Program.cs +++ b/CreateTemplate/Program.cs @@ -8,55 +8,253 @@ namespace CreateTemplate { class Program { - static void Main(string[] args) + static int Main(string[] args) { - // Validate command line: - if(args.Length == 0 || args.Length != (args[0] == "--template" ? 4 : 3)) + if(args.Length == 0) { - Console.WriteLine("Usage:"); - Console.WriteLine(" CreateTemplate [--template] "); + Console.WriteLine("Creates a copy of FNA Template suitable for starting new FNA projects."); Console.WriteLine(); - Console.WriteLine("The output will be placed in the folder at " + Path.DirectorySeparatorChar + ""); + Console.WriteLine("Usage:"); + Console.WriteLine(" CreateTemplate [--source ] [--solution] [--template]"); + Console.WriteLine(" "); Console.WriteLine(); Console.WriteLine("Options:"); - Console.WriteLine(" --template: Generate a Visual Studio compatible project template."); + Console.WriteLine(" --source (-s) : Sets the source directory containing FNATemplate.csproj."); + Console.WriteLine(" If not specified, it is searched for up the directory"); + Console.WriteLine(" hierarchy with the name FNATemplate."); + Console.WriteLine(" --solution : Generates a full solution in the output directory."); + Console.WriteLine(" --template (-t) : Generate a Visual Studio compatible project template."); Console.WriteLine(); - return; + Console.WriteLine("The output project is placed at " + Path.DirectorySeparatorChar + ""); + Console.WriteLine(); + return 1; + } + + bool createTemplate = false; + bool createSolution = false; + string sourceDirectory = null; + string projectName = null; + string destinationDirectory = null; + int parameterNumber = 0; + + Queue arguments = new Queue(args); + while(arguments.Count > 0) + { + string a = arguments.Dequeue(); + switch(a) + { + case "--source": + if(arguments.Count > 0) + sourceDirectory = Path.GetFullPath(arguments.Dequeue()); + else + { + Console.WriteLine("ERROR: Must specify a directory for --source."); + return 1; + } + break; + + case "--solution": + createSolution = true; + break; + + case "--template": + case "-t": + createTemplate = true; + break; + + default: + switch(parameterNumber) + { + case 0: + projectName = a; + break; + case 1: + destinationDirectory = Path.GetFullPath(a); + break; + default: + Console.WriteLine("ERROR: Too many parameters."); + return 1; + } + parameterNumber++; + break; + } + } + + if(parameterNumber < 2) + { + Console.WriteLine("ERROR: Not enough parameters."); + return 1; + } + + if(createSolution && createTemplate) + { + Console.WriteLine("WARNING: --template and --solution options together are kind of silly."); } - bool vsTemplate = args[0] == "--template"; - string projectName = args[vsTemplate ? 1 : 0]; - string sourceDirectory = Path.GetFullPath(args[vsTemplate ? 2 : 1]); - string destinationDirectory = Path.GetFullPath(args[vsTemplate ? 3 : 2]); + // Search for the source directory if it is not specified + if(sourceDirectory == null) + { + DirectoryInfo di = new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory); + while(true) + { + string possibleSourceDir = Path.Combine(di.FullName, "FNATemplate"); + if(Directory.Exists(possibleSourceDir) && File.Exists(Path.Combine(possibleSourceDir, "FNATemplate.csproj"))) + { + sourceDirectory = possibleSourceDir; + Console.WriteLine("Found source directory at:"); + Console.WriteLine(sourceDirectory); + break; + } + else + { + if(di.Parent != null) + di = di.Parent; + else + { + Console.WriteLine("ERROR: Could not find SourceDir. Specify with --source or ensure CreateTemplate is in the correct location."); + return 1; + } + } + } + } if(!Directory.Exists(sourceDirectory)) { Console.WriteLine("ERROR: Source directory not found!"); - return; + return 1; } if(!File.Exists(Path.Combine(sourceDirectory, "FNATemplate.csproj"))) { Console.WriteLine("ERROR: Source directory is missing \"FNATemplate.csproj\""); - return; + return 1; } if(destinationDirectory.StartsWith(sourceDirectory)) // <- Probably not the most robust way to do this... { - Console.WriteLine("ERROR: Destination directory is contained with the source directory!"); - return; + Console.WriteLine("ERROR: Destination directory is contained within the source directory!"); + return 1; + } + + + // If we're creating a solution, we need these paths: + string fnaProjectDirectory = null; + string fnaProjectFile = null; + string fnalibsDirectory = null; + string buildDirectory = null; + if(createSolution) + { + DirectoryInfo di = new DirectoryInfo(sourceDirectory).Parent; + + fnaProjectDirectory = Path.Combine(di.FullName, "FNA"); + fnaProjectFile = Path.Combine(fnaProjectDirectory, "FNA.csproj"); + fnalibsDirectory = Path.Combine(di.FullName, "fnalibs"); + buildDirectory = Path.Combine(di.FullName, "build"); + + if(!Directory.Exists(fnaProjectDirectory)) + { + Console.WriteLine("ERROR: Could not find directory \"" + fnaProjectDirectory + "\"."); + return 1; + } + + if(!File.Exists(fnaProjectFile)) + { + Console.WriteLine("ERROR: Could not find \"" + fnaProjectFile + "\"."); + return 1; + } + + if(!Directory.Exists(fnalibsDirectory)) + { + Console.WriteLine("ERROR: Could not find directory \"" + fnalibsDirectory + "\"."); + return 1; + } + + if(!Directory.Exists(buildDirectory)) + { + Console.WriteLine("ERROR: Could not find directory \"" + buildDirectory + "\"."); + return 1; + } } + // // And off we go... + // string projectDirectory = Path.Combine(destinationDirectory, projectName); Directory.CreateDirectory(projectDirectory); - StreamWriter templateFile = null; - if(vsTemplate) + StreamWriter templateFile = null; + StreamWriter solutionFile = null; + + string fnaProjectGuid = null; + string projectGuid = Guid.NewGuid().ToString("B").ToUpperInvariant(); + + if(createSolution) + { + string fnaProjectText = File.ReadAllText(fnaProjectFile); + string projectGuidString = ""; + int guidStart = fnaProjectText.IndexOf(projectGuidString); + int guidEnd = fnaProjectText.IndexOf(""); + if(guidStart >= 0 && guidEnd >= guidStart + projectGuidString.Length) + { + fnaProjectGuid = fnaProjectText.Substring(guidStart + projectGuidString.Length, guidEnd - (guidStart + projectGuidString.Length)); + } + else + { + Console.WriteLine("ERROR: Could not find ProjectGuid in FNA.csproj."); + return 1; + } + + solutionFile = new StreamWriter(Path.Combine(destinationDirectory, projectName + ".sln"), false, + Encoding.UTF8); // <- NOTE: Encoding required so we get a BOM so the VS version selector can deal with us + solutionFile.WriteLine(""); + solutionFile.WriteLine("Microsoft Visual Studio Solution File, Format Version 11.00"); + solutionFile.WriteLine("# Visual Studio 2010"); + solutionFile.WriteLine("Project(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"" + projectName + "\", \"" + projectName + "\\" + projectName + ".csproj\", \"" + projectGuid + "\""); + solutionFile.WriteLine("EndProject"); + solutionFile.WriteLine("Project(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"FNA\", \"FNA\\FNA.csproj\", \"" + fnaProjectGuid + "\""); + solutionFile.WriteLine("EndProject"); + solutionFile.WriteLine("Global"); + solutionFile.WriteLine("\tGlobalSection(SolutionConfigurationPlatforms) = preSolution"); + solutionFile.WriteLine("\t\tDebug|Any CPU = Debug|Any CPU"); + solutionFile.WriteLine("\t\tDebug|x86 = Debug|x86"); + solutionFile.WriteLine("\t\tRelease|Any CPU = Release|Any CPU"); + solutionFile.WriteLine("\t\tRelease|x86 = Release|x86"); + solutionFile.WriteLine("\tEndGlobalSection"); + solutionFile.WriteLine("\tGlobalSection(ProjectConfigurationPlatforms) = postSolution"); + solutionFile.WriteLine("\t\t" + projectGuid + ".Debug|Any CPU.ActiveCfg = Debug|Any CPU"); + solutionFile.WriteLine("\t\t" + projectGuid + ".Debug|Any CPU.Build.0 = Debug|Any CPU"); + solutionFile.WriteLine("\t\t" + projectGuid + ".Debug|x86.ActiveCfg = Debug|x86"); + solutionFile.WriteLine("\t\t" + projectGuid + ".Debug|x86.Build.0 = Debug|x86"); + solutionFile.WriteLine("\t\t" + projectGuid + ".Release|Any CPU.ActiveCfg = Release|Any CPU"); + solutionFile.WriteLine("\t\t" + projectGuid + ".Release|Any CPU.Build.0 = Release|Any CPU"); + solutionFile.WriteLine("\t\t" + projectGuid + ".Release|x86.ActiveCfg = Release|x86"); + solutionFile.WriteLine("\t\t" + projectGuid + ".Release|x86.Build.0 = Release|x86"); + solutionFile.WriteLine("\t\t" + fnaProjectGuid + ".Debug|Any CPU.ActiveCfg = Debug|Any CPU"); + solutionFile.WriteLine("\t\t" + fnaProjectGuid + ".Debug|Any CPU.Build.0 = Debug|Any CPU"); + solutionFile.WriteLine("\t\t" + fnaProjectGuid + ".Debug|x86.ActiveCfg = Debug|Any CPU"); + solutionFile.WriteLine("\t\t" + fnaProjectGuid + ".Debug|x86.Build.0 = Debug|Any CPU"); + solutionFile.WriteLine("\t\t" + fnaProjectGuid + ".Release|Any CPU.ActiveCfg = Release|Any CPU"); + solutionFile.WriteLine("\t\t" + fnaProjectGuid + ".Release|Any CPU.Build.0 = Release|Any CPU"); + solutionFile.WriteLine("\t\t" + fnaProjectGuid + ".Release|x86.ActiveCfg = Release|Any CPU"); + solutionFile.WriteLine("\t\t" + fnaProjectGuid + ".Release|x86.Build.0 = Release|Any CPU"); + solutionFile.WriteLine("\tEndGlobalSection"); + solutionFile.WriteLine("\tGlobalSection(SolutionProperties) = preSolution"); + solutionFile.WriteLine("\t\tHideSolutionNode = FALSE"); + solutionFile.WriteLine("\tEndGlobalSection"); + solutionFile.WriteLine("EndGlobal"); + solutionFile.Close(); + + string[] ignore = { "bin", "obj" }; + CopyDirectoryRecursive(fnaProjectDirectory, Path.Combine(destinationDirectory, "FNA"), ignore); + CopyDirectoryRecursive(fnalibsDirectory, Path.Combine(destinationDirectory, "fnalibs"), ignore); + CopyDirectoryRecursive(buildDirectory, Path.Combine(destinationDirectory, "build"), ignore); + } + + if(createTemplate) { templateFile = new StreamWriter(Path.Combine(projectDirectory, projectName + ".vstemplate")); templateFile.WriteLine(""); @@ -111,9 +309,9 @@ namespace CreateTemplate for(int i = 0; i < lines.Length; i++) { if(lines[i].Contains("")) - lines[i] = " " + (vsTemplate ? "$guid1$" : Guid.NewGuid().ToString()) + ""; + lines[i] = " " + (createTemplate ? "$guid1$" : projectGuid) + ""; // <- TODO: Handle more than one project? else if(lines[i].Contains("FNATemplate")) - lines[i] = lines[i].Replace("FNATemplate", vsTemplate ? "$safeprojectname$" : projectName); + lines[i] = lines[i].Replace("FNATemplate", createTemplate ? "$safeprojectname$" : projectName); } File.WriteAllLines(outputPath, lines); @@ -125,9 +323,9 @@ namespace CreateTemplate for(int i = 0; i < lines.Length; i++) { if(lines[i].Contains("[assembly: Guid(")) - lines[i] = "[assembly: Guid(\"" + (vsTemplate ? "$guid2$" : Guid.NewGuid().ToString()) + "\")]"; + lines[i] = "[assembly: Guid(\"" + (createTemplate ? "$guid2$" : Guid.NewGuid().ToString()) + "\")]"; else if(lines[i].Contains("FNATemplate")) - lines[i] = lines[i].Replace("FNATemplate", vsTemplate ? "$safeprojectname$" : projectName); + lines[i] = lines[i].Replace("FNATemplate", createTemplate ? "$safeprojectname$" : projectName); } File.WriteAllLines(outputPath, lines); @@ -139,7 +337,7 @@ namespace CreateTemplate replaceParameters = false; } - if(vsTemplate) + if(createTemplate) { templateFile.Write(" "); templateFile.WriteLine(" "); @@ -161,6 +358,44 @@ namespace CreateTemplate templateFile.Close(); } + return 0; + } + + + + private static void CopyDirectoryRecursive(string sourceDirName, string destDirName, IEnumerable ignore = null) + { + DirectoryInfo dir = new DirectoryInfo(sourceDirName); + + if(!dir.Exists) + { + throw new DirectoryNotFoundException("Source directory does not exist or could not be found: " + sourceDirName); + } + + DirectoryInfo[] dirs = dir.GetDirectories(); + if(!Directory.Exists(destDirName)) + { + Directory.CreateDirectory(destDirName); + } + + FileInfo[] files = dir.GetFiles(); + foreach(FileInfo file in files) + { + if(ignore != null && ignore.Contains(file.Name)) + continue; + + string temppath = Path.Combine(destDirName, file.Name); + file.CopyTo(temppath, true); + } + + foreach(DirectoryInfo subdir in dirs) + { + if(ignore != null && ignore.Contains(subdir.Name)) + continue; + + string temppath = Path.Combine(destDirName, subdir.Name); + CopyDirectoryRecursive(subdir.FullName, temppath, ignore); + } } } } diff --git a/README.md b/README.md index fd365cb..1e71866 100644 --- a/README.md +++ b/README.md @@ -46,9 +46,9 @@ Once Wine and winetricks are installed: - Setup Wine with `winecfg` - Install the DirectX SDK with `winetricks dxsdk_jun2010` -NOTE: At time of writing there is a bug in winetricks that will cause the DirectX SDK to not install correctly. See https://github.com/Winetricks/winetricks/issues/841. You can either fix that bug (`which winetricks`, then find and replace the broken hash value), or use the alternative method: +NOTE: Ensure you have the latest version of winetricks, or you may hit this bug: https://github.com/Winetricks/winetricks/issues/841. -**Alternative:** Instead of installing the DirectX SDK, you can place a copy of `fxc.exe` from the DirectX SDK in the `build/tools` directory. Then use `winetricks d3dcompiler_43` to install the required DLL from the DirectX redistributable (this is a smaller download than the SDK). See `BuildShaders.targets` for details. The same fallback also works on Windows. +**Alternative method:** Instead of installing the DirectX SDK, you can place a copy of `fxc.exe` from the DirectX SDK in the `build/tools` directory. Then use `winetricks d3dcompiler_43` to install the required DLL from the DirectX redistributable (this is a smaller download than the SDK). See `BuildShaders.targets` for details. The same fallback also works on Windows. (Linux/macOS) Setting the library path for debugging ---------------------------------------------------- @@ -80,15 +80,15 @@ Use the CreateTemplate tool to create new versions of the FNATemplate project (N The command line is: -`CreateTemplate [--template] ` +`CreateTemplate [--source ] [--solution] [--template] ` -You can use it to directly create new projects: +You can use it to directly create new projects (or an entire solution with `--solution`): -`CreateTemplate NewProject "X:\pathToThisSolution\FNATemplate" "X:\yourSolution"` +`CreateTemplate NewProject "X:\yourSolution"` Or you can create a Visual Studio template file, which itself can be used to create projects. -`CreateTemplate --template FNATemplate "X:\pathToThisSolution\FNATemplate" .` +`CreateTemplate --template FNATemplate "X:\outputPath"` To install the generated template in Visual Studio: @@ -99,17 +99,15 @@ To install the generated template in Visual Studio: (NOTE: Ensure that the .vstemplate file is at the root of the resulting .zip file. Otherwise the template will not work.) -Creating your own solution --------------------------- +Adding a FNA Template project to an existing solution +----------------------------------------------------- -To use FNA Template to create projects in a fresh solution, you will need to: +To use an FNA Template-drived project in an existing solution you will need to: - Copy the "build" directory into the same directory as your new solution - Add the "FNA" and "fnalibs" directories as specified above - Add the FNA project to your solution (Right click solution -> Add Existing Project) -From this point, you can use the CreateTemplate tool (or a Visual Studio template created by the same) to add new FNA game projects to your solution. - Building and loading shaders ============================