Gave CreateTemplate the ability to generate a new Solution. Updated documentation. Minor fixes.

master
Andrew Russell 2018-02-20 15:48:59 +10:00
parent 40588a0974
commit 7bf338e925
2 changed files with 269 additions and 36 deletions

View File

@ -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] <ProjectName> <SourceDirectory> <DestinationDirectory>");
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 <DestinationDirectory>" + Path.DirectorySeparatorChar + "<ProjectName>");
Console.WriteLine("Usage:");
Console.WriteLine(" CreateTemplate [--source <SourceDir>] [--solution] [--template]");
Console.WriteLine(" <ProjectName> <DestinationDir>");
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 <DestinationDir>" + Path.DirectorySeparatorChar + "<ProjectName>");
Console.WriteLine();
return 1;
}
bool createTemplate = false;
bool createSolution = false;
string sourceDirectory = null;
string projectName = null;
string destinationDirectory = null;
int parameterNumber = 0;
Queue<string> arguments = new Queue<string>(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 = "<ProjectGuid>";
int guidStart = fnaProjectText.IndexOf(projectGuidString);
int guidEnd = fnaProjectText.IndexOf("</ProjectGuid>");
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("<VSTemplate Version=\"3.0.0\" xmlns=\"http://schemas.microsoft.com/developer/vstemplate/2005\" Type=\"Project\">");
@ -111,9 +309,9 @@ namespace CreateTemplate
for(int i = 0; i < lines.Length; i++)
{
if(lines[i].Contains("<ProjectGuid>"))
lines[i] = " <ProjectGuid>" + (vsTemplate ? "$guid1$" : Guid.NewGuid().ToString()) + "</ProjectGuid>";
lines[i] = " <ProjectGuid>" + (createTemplate ? "$guid1$" : projectGuid) + "</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(" <ProjectItem TargetFileName=\"");
templateFile.Write(templateTargetFileName);
@ -152,8 +350,7 @@ namespace CreateTemplate
}
}
if(vsTemplate)
if(createTemplate)
{
templateFile.WriteLine(" </Project>");
templateFile.WriteLine(" </TemplateContent>");
@ -161,6 +358,44 @@ namespace CreateTemplate
templateFile.Close();
}
return 0;
}
private static void CopyDirectoryRecursive(string sourceDirName, string destDirName, IEnumerable<string> 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);
}
}
}
}

View File

@ -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] <ProjectName> <SourceDirectory> <DestinationDirectory>`
`CreateTemplate [--source <SourceDir>] [--solution] [--template] <ProjectName> <DestinationDir>`
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
============================