(ded4a3e0a) v0.9.0.7

This commit is contained in:
Joonas Rikkonen
2019-06-25 16:00:44 +03:00
parent e5ae622c77
commit 4a51db77b5
1777 changed files with 421528 additions and 917 deletions
@@ -0,0 +1,504 @@
// MonoGame - Copyright (C) The MonoGame Team
// This file is subject to the terms and conditions defined in
// file 'LICENSE.txt', which is part of this source code package.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Xna.Framework.Content.Pipeline;
using Microsoft.Xna.Framework.Graphics;
using MonoGame.Framework.Content.Pipeline.Builder;
namespace MGCB
{
class BuildContent
{
[CommandLineParameter(
Name = "launchdebugger",
Flag = "d",
Description = "Wait for debugger to attach before building content.")]
public bool LaunchDebugger = false;
[CommandLineParameter(
Name = "quiet",
Flag = "q",
Description = "Only output content build errors.")]
public bool Quiet = false;
[CommandLineParameter(
Name = "@",
Flag = "@",
ValueName = "responseFile",
Description = "Read a text response file with additional command line options and switches.")]
// This property only exists for documentation.
// The actual handling of '/@' is done in the preprocess step.
public List<string> ResponseFiles
{
get { throw new InvalidOperationException(); }
set { throw new InvalidOperationException(); }
}
[CommandLineParameter(
Name = "workingDir",
Flag = "w",
ValueName = "directoryPath",
Description = "The working directory where all source content is located.")]
public void SetWorkingDir(string path)
{
Directory.SetCurrentDirectory(path);
}
[CommandLineParameter(
Name = "outputDir",
Flag = "o",
ValueName = "path",
Description = "The directory where all content is written.")]
public string OutputDir = string.Empty;
[CommandLineParameter(
Name = "intermediateDir",
Flag = "n",
ValueName = "path",
Description = "The directory where all intermediate files are written.")]
public string IntermediateDir = string.Empty;
[CommandLineParameter(
Name = "rebuild",
Flag = "r",
Description = "Forces a full rebuild of all content.")]
public bool Rebuild = false;
[CommandLineParameter(
Name = "clean",
Flag = "c",
Description = "Delete all previously built content and intermediate files.")]
public bool Clean = false;
[CommandLineParameter(
Name = "incremental",
Flag = "I",
Description = "Skip cleaning files not included in the current build.")]
public bool Incremental = false;
[CommandLineParameter(
Name = "reference",
Flag = "f",
ValueName = "assembly",
Description = "Adds an assembly reference for resolving content importers, processors, and writers.")]
public readonly List<string> References = new List<string>();
[CommandLineParameter(
Name = "platform",
Flag = "t",
ValueName = "targetPlatform",
Description = "Set the target platform for this build. Defaults to Windows desktop DirectX.")]
public TargetPlatform Platform = TargetPlatform.Windows;
[CommandLineParameter(
Name = "profile",
Flag = "g",
ValueName = "graphicsProfile",
Description = "Set the target graphics profile for this build. Defaults to HiDef.")]
public GraphicsProfile Profile = GraphicsProfile.HiDef;
[CommandLineParameter(
Name = "config",
ValueName = "string",
Description = "The optional build config string from the build system.")]
public string Config = string.Empty;
[CommandLineParameter(
Name = "importer",
Flag = "i",
ValueName = "className",
Description = "Defines the class name of the content importer for reading source content.")]
public string Importer = null;
[CommandLineParameter(
Name = "processor",
Flag = "p",
ValueName = "className",
Description = "Defines the class name of the content processor for processing imported content.")]
public void SetProcessor(string processor)
{
_processor = processor;
// If you are changing the processor then reset all
// the processor parameters.
_processorParams.Clear();
}
private string _processor = null;
private readonly OpaqueDataDictionary _processorParams = new OpaqueDataDictionary();
[CommandLineParameter(
Name = "processorParam",
Flag = "P",
ValueName = "name=value",
Description = "Defines a parameter name and value to set on a content processor.")]
public void AddProcessorParam(string nameAndValue)
{
var keyAndValue = nameAndValue.Split('=', ':');
if (keyAndValue.Length != 2)
{
// Do we error out or something?
return;
}
_processorParams.Remove(keyAndValue[0]);
_processorParams.Add(keyAndValue[0], keyAndValue[1]);
}
[CommandLineParameter(
Name = "build",
Flag = "b",
ValueName = "sourceFile",
Description = "Build the content source file using the previously set switches and options. Optional destination path may be specified with \"sourceFile;destFile\" if you wish to change the output filepath.")]
public void OnBuild(string sourceFile)
{
string link = null;
if (sourceFile.Contains(";"))
{
var split = sourceFile.Split(';');
sourceFile = split[0];
if(split.Length > 0)
link = split[1];
}
// Make sure the source file is absolute.
if (!Path.IsPathRooted(sourceFile))
sourceFile = Path.Combine(Directory.GetCurrentDirectory(), sourceFile);
// link should remain relative, absolute path will get set later when the build occurs
sourceFile = PathHelper.Normalize(sourceFile);
// Remove duplicates... keep this new one.
var previous = _content.FindIndex(e => string.Equals(e.SourceFile, sourceFile, StringComparison.InvariantCultureIgnoreCase));
if (previous != -1)
_content.RemoveAt(previous);
// Create the item for processing later.
var item = new ContentItem
{
SourceFile = sourceFile,
OutputFile = link,
Importer = Importer,
Processor = _processor,
ProcessorParams = new OpaqueDataDictionary()
};
_content.Add(item);
// Copy the current processor parameters blind as we
// will validate and remove invalid parameters during
// the build process later.
foreach (var pair in _processorParams)
item.ProcessorParams.Add(pair.Key, pair.Value);
}
[CommandLineParameter(
Name = "copy",
ValueName = "sourceFile",
Description = "Copy the content source file verbatim to the output directory.")]
public void OnCopy(string sourceFile)
{
string link = null;
if (sourceFile.Contains(";"))
{
var split = sourceFile.Split(';');
sourceFile = split[0];
if (split.Length > 0)
link = split[1];
}
if (!Path.IsPathRooted(sourceFile))
sourceFile = Path.Combine(Directory.GetCurrentDirectory(), sourceFile);
sourceFile = PathHelper.Normalize(sourceFile);
// Remove duplicates... keep this new one.
var previous = _copyItems.FindIndex(e => string.Equals(e.SourceFile, sourceFile, StringComparison.InvariantCultureIgnoreCase));
if (previous != -1)
_copyItems.RemoveAt(previous);
_copyItems.Add(new CopyItem { SourceFile = sourceFile, Link = link });
}
[CommandLineParameter(
Name = "compress",
Description = "Compress the XNB files for smaller file sizes.")]
public bool CompressContent = false;
public class ContentItem
{
public string SourceFile;
// This refers to the "Link" which can override the default output location
public string OutputFile;
public string Importer;
public string Processor;
public OpaqueDataDictionary ProcessorParams;
}
public class CopyItem
{
public string SourceFile;
public string Link;
}
private readonly List<ContentItem> _content = new List<ContentItem>();
private readonly List<CopyItem> _copyItems = new List<CopyItem>();
private PipelineManager _manager;
public bool HasWork
{
get { return _content.Count > 0 || _copyItems.Count > 0 || Clean; }
}
string ReplaceSymbols(string parameter)
{
if (string.IsNullOrWhiteSpace(parameter))
return parameter;
return parameter
.Replace("$(Platform)", Platform.ToString())
.Replace("$(Configuration)", Config)
.Replace("$(Config)", Config)
.Replace("$(Profile)", this.Profile.ToString());
}
public void Build(out int successCount, out int errorCount)
{
var projectDirectory = PathHelper.Normalize(Directory.GetCurrentDirectory());
var outputPath = ReplaceSymbols (OutputDir);
if (!Path.IsPathRooted(outputPath))
outputPath = PathHelper.Normalize(Path.GetFullPath(Path.Combine(projectDirectory, outputPath)));
var intermediatePath = ReplaceSymbols (IntermediateDir);
if (!Path.IsPathRooted(intermediatePath))
intermediatePath = PathHelper.Normalize(Path.GetFullPath(Path.Combine(projectDirectory, intermediatePath)));
_manager = new PipelineManager(projectDirectory, outputPath, intermediatePath);
_manager.Logger = new ConsoleLogger();
_manager.CompressContent = CompressContent;
// If the intent is to debug build, break at the original location
// of any exception, eg, within the actual importer/processor.
if (LaunchDebugger)
_manager.RethrowExceptions = false;
// Feed all the assembly references to the pipeline manager
// so it can resolve importers, processors, writers, and types.
foreach (var r in References)
{
var assembly = r;
if (!Path.IsPathRooted(assembly))
assembly = Path.GetFullPath(Path.Combine(projectDirectory, assembly));
_manager.AddAssembly(assembly);
}
// Load the previously serialized list of built content.
var contentFile = Path.Combine(intermediatePath, PipelineBuildEvent.Extension);
var previousContent = SourceFileCollection.Read(contentFile);
// If the target changed in any way then we need to force
// a full rebuild even under incremental builds.
var targetChanged = previousContent.Config != Config ||
previousContent.Platform != Platform ||
previousContent.Profile != Profile;
// First clean previously built content.
for(int i = 0; i < previousContent.SourceFiles.Count; i++)
{
var sourceFile = previousContent.SourceFiles[i];
// This may be an old file (prior to MG 3.7) which doesn't have destination files:
string destFile = null;
if(i < previousContent.DestFiles.Count)
{
destFile = previousContent.DestFiles[i];
}
var inContent = _content.Any(e => string.Equals(e.SourceFile, sourceFile, StringComparison.InvariantCultureIgnoreCase));
var cleanOldContent = !inContent && !Incremental;
var cleanRebuiltContent = inContent && (Rebuild || Clean);
if (cleanRebuiltContent || cleanOldContent || targetChanged)
_manager.CleanContent(sourceFile, destFile);
}
// TODO: Should we be cleaning copy items? I think maybe we should.
var newContent = new SourceFileCollection
{
Profile = _manager.Profile = Profile,
Platform = _manager.Platform = Platform,
Config = _manager.Config = Config
};
errorCount = 0;
successCount = 0;
// Before building the content, register all files to be built. (Necessary to
// correctly resolve external references.)
foreach (var c in _content)
{
try
{
_manager.RegisterContent(c.SourceFile, c.OutputFile, c.Importer, c.Processor, c.ProcessorParams);
}
catch
{
// Ignore exception. Exception will be handled below.
}
}
foreach (var c in _content)
{
try
{
_manager.BuildContent(c.SourceFile,
c.OutputFile,
c.Importer,
c.Processor,
c.ProcessorParams);
newContent.SourceFiles.Add(c.SourceFile);
newContent.DestFiles.Add(c.OutputFile);
++successCount;
}
catch (InvalidContentException ex)
{
var message = string.Empty;
if (ex.ContentIdentity != null && !string.IsNullOrEmpty(ex.ContentIdentity.SourceFilename))
{
message = ex.ContentIdentity.SourceFilename;
if (!string.IsNullOrEmpty(ex.ContentIdentity.FragmentIdentifier))
message += "(" + ex.ContentIdentity.FragmentIdentifier + ")";
message += ": ";
}
message += ex.Message;
Console.WriteLine(message);
++errorCount;
}
catch (PipelineException ex)
{
Console.Error.WriteLine("{0}: error: {1}", c.SourceFile, ex.Message);
if (ex.InnerException != null)
Console.Error.WriteLine(ex.InnerException.ToString());
++errorCount;
}
catch (Exception ex)
{
Console.Error.WriteLine("{0}: error: {1}", c.SourceFile, ex.Message);
if (ex.InnerException != null)
Console.Error.WriteLine(ex.InnerException.ToString());
++errorCount;
}
}
// If this is an incremental build we merge the list
// of previous content with the new list.
if (Incremental && !targetChanged)
{
newContent.Merge(previousContent);
_manager.ContentStats.MergePreviousStats();
}
// Delete the old file and write the new content
// list if we have any to serialize.
FileHelper.DeleteIfExists(contentFile);
if (newContent.SourceFiles.Count > 0)
newContent.Write(contentFile);
// Process copy items (files that bypass the content pipeline)
foreach (var c in _copyItems)
{
try
{
// Figure out an asset name relative to the project directory,
// retaining the file extension.
// Note that replacing a sub-path like this requires consistent
// directory separator characters.
var relativeName = c.Link;
if (string.IsNullOrWhiteSpace(relativeName))
relativeName = c.SourceFile.Replace(projectDirectory, string.Empty)
.TrimStart(Path.DirectorySeparatorChar)
.TrimStart(Path.AltDirectorySeparatorChar);
var dest = Path.Combine(outputPath, relativeName);
// Only copy if the source file is newer than the destination.
// We may want to provide an option for overriding this, but for
// nearly all cases this is the desired behavior.
if (File.Exists(dest) && !Rebuild)
{
var srcTime = File.GetLastWriteTimeUtc(c.SourceFile);
var dstTime = File.GetLastWriteTimeUtc(dest);
if (srcTime <= dstTime)
{
if (string.IsNullOrEmpty(c.Link))
Console.WriteLine("Skipping {0}", c.SourceFile);
else
Console.WriteLine("Skipping {0} => {1}", c.SourceFile, c.Link);
// Copy the stats from the previous stats collection.
_manager.ContentStats.CopyPreviousStats(c.SourceFile);
continue;
}
}
var startTime = DateTime.UtcNow;
// Create the destination directory if it doesn't already exist.
var destPath = Path.GetDirectoryName(dest);
if (!Directory.Exists(destPath))
Directory.CreateDirectory(destPath);
File.Copy(c.SourceFile, dest, true);
// Destination file should not be read-only even if original was.
var fileAttr = File.GetAttributes(dest);
fileAttr = fileAttr & (~FileAttributes.ReadOnly);
File.SetAttributes(dest, fileAttr);
var buildTime = DateTime.UtcNow - startTime;
if (string.IsNullOrEmpty(c.Link))
Console.WriteLine("{0}", c.SourceFile);
else
Console.WriteLine("{0} => {1}", c.SourceFile, c.Link);
// Record content stats on the copy.
_manager.ContentStats.RecordStats(c.SourceFile, dest, "CopyItem", typeof(File), (float)buildTime.TotalSeconds);
++successCount;
}
catch (Exception ex)
{
Console.Error.WriteLine("{0}: error: {1}", c, ex.Message);
if (ex.InnerException != null)
Console.Error.WriteLine(ex.InnerException.ToString());
++errorCount;
}
}
// Dump the content build stats.
_manager.ContentStats.Write(intermediatePath);
}
[CommandLineParameter(
Name = "help",
Flag = "h",
Description = "Displays this help.")]
public void Help()
{
MGBuildParser.Instance.ShowError(null);
}
}
}
@@ -0,0 +1,680 @@
// MonoGame - Copyright (C) The MonoGame Team
// This file is subject to the terms and conditions defined in
// file 'LICENSE.txt', which is part of this source code package.
using System;
using System.IO;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.ComponentModel;
namespace MGCB
{
/// <summary>
/// Adapted from this generic command line argument parser:
/// http://blogs.msdn.com/b/shawnhar/archive/2012/04/20/a-reusable-reflection-based-command-line-parser.aspx
/// </summary>
public class MGBuildParser
{
public static MGBuildParser Instance;
#region Supporting Types
public class PreprocessorProperty
{
public string Name;
public string CurrentValue;
public PreprocessorProperty()
{
Name = string.Empty;
CurrentValue = string.Empty;
}
}
public class PreprocessorPropertyCollection
{
private readonly List<PreprocessorProperty> _properties;
public PreprocessorPropertyCollection()
{
_properties = new List<PreprocessorProperty>();
}
public string this[string name]
{
get
{
foreach (var i in _properties)
{
if (i.Name.Equals(name))
return i.CurrentValue;
}
return null;
}
set
{
foreach (var i in _properties)
{
if (i.Name.Equals(name))
{
i.CurrentValue = value;
return;
}
}
var prop = new PreprocessorProperty()
{
Name = name,
CurrentValue = value,
};
_properties.Add(prop);
}
}
}
#endregion
private readonly object _optionsObject;
private readonly Queue<MemberInfo> _requiredOptions;
private readonly Dictionary<string, MemberInfo> _optionalOptions;
private readonly Dictionary<string, string> _flags;
private readonly List<string> _requiredUsageHelp;
public readonly PreprocessorPropertyCollection _properties;
public delegate void ErrorCallback(string msg, object[] args);
public event ErrorCallback OnError;
public MGBuildParser(object optionsObject)
{
Instance = this;
_optionsObject = optionsObject;
_requiredOptions = new Queue<MemberInfo>();
_optionalOptions = new Dictionary<string, MemberInfo>();
_requiredUsageHelp = new List<string>();
_properties = new PreprocessorPropertyCollection();
// Reflect to find what commandline options are available...
// Fields
foreach (var field in optionsObject.GetType().GetFields())
{
var param = GetAttribute<CommandLineParameterAttribute>(field);
if (param == null)
continue;
CheckReservedPrefixes(param.Name);
if (param.Required)
{
// Record a required option.
_requiredOptions.Enqueue(field);
_requiredUsageHelp.Add(string.Format("<{0}>", param.Name));
}
else
{
// Record an optional option.
_optionalOptions.Add(param.Name.ToLowerInvariant(), field);
}
}
// Properties
foreach (var property in optionsObject.GetType().GetProperties())
{
var param = GetAttribute<CommandLineParameterAttribute>(property);
if (param == null)
continue;
CheckReservedPrefixes(param.Name);
if (param.Required)
{
// Record a required option.
_requiredOptions.Enqueue(property);
_requiredUsageHelp.Add(string.Format("<{0}>", param.Name));
}
else
{
// Record an optional option.
_optionalOptions.Add(param.Name.ToLowerInvariant(), property);
}
}
// Methods
foreach (var method in optionsObject.GetType().GetMethods())
{
var param = GetAttribute<CommandLineParameterAttribute>(method);
if (param == null)
continue;
CheckReservedPrefixes(param.Name);
// Only accept methods that take less than 1 parameter.
if (method.GetParameters().Length > 1)
throw new NotSupportedException("Methods must have one or zero parameters.");
if (param.Required)
{
// Record a required option.
_requiredOptions.Enqueue(method);
_requiredUsageHelp.Add(string.Format("<{0}>", param.Name));
}
else
{
// Record an optional option.
_optionalOptions.Add(param.Name.ToLowerInvariant(), method);
}
}
_flags = new Dictionary<string, string>();
foreach(var pair in _optionalOptions)
{
var fi = GetAttribute<CommandLineParameterAttribute>(pair.Value);
if(!string.IsNullOrEmpty(fi.Flag))
_flags.Add(fi.Flag, fi.Name);
}
}
public bool Parse(IEnumerable<string> args)
{
args = Preprocess(args);
var showUsage = true;
var success = true;
foreach (var arg in args)
{
showUsage = false;
if (!ParseFlags(arg))
{
success = false;
break;
}
}
var missingRequiredOption = _requiredOptions.FirstOrDefault(field => !IsList(field) || GetList(field).Count == 0);
if (missingRequiredOption != null)
{
ShowError("Missing argument '{0}'", GetAttribute<CommandLineParameterAttribute>(missingRequiredOption).Name);
return false;
}
if (showUsage)
ShowError(null);
return success;
}
private IEnumerable<string> Preprocess(IEnumerable<string> args)
{
var output = new List<string>();
var lines = new List<string>(args);
var ifstack = new Stack<Tuple<string, string>>();
var fileStack = new Stack<string>();
while (lines.Count > 0)
{
var arg = lines[0];
lines.RemoveAt(0);
if (arg.StartsWith("# Begin:"))
{
var file = arg.Substring(8);
fileStack.Push(file);
continue;
}
if (arg.StartsWith("# End:"))
{
fileStack.Pop();
continue;
}
if (arg.StartsWith("$endif"))
{
ifstack.Pop();
continue;
}
if (ifstack.Count > 0)
{
var skip = false;
foreach (var i in ifstack)
{
var val = _properties[i.Item1];
if (!(i.Item2).Equals(val))
{
skip = true;
break;
}
}
if (skip)
continue;
}
if (arg.StartsWith("$set"))
{
var words = arg.Substring(5).Split('=');
var name = words[0];
var value = words[1];
_properties[name] = value;
continue;
}
if (arg.StartsWith("$if"))
{
if (fileStack.Count == 0)
throw new Exception("$if is invalid outside of a response file.");
var words = arg.Substring(4).Split('=');
var name = words[0];
var value = words[1];
var condition = new Tuple<string, string>(name, value);
ifstack.Push(condition);
continue;
}
if (arg.StartsWith("/define:") || arg.StartsWith("--define:"))
{
var words = arg.Substring(8).Split('=');
var name = words[0];
var value = words[1];
_properties[name] = value;
continue;
}
if (arg.StartsWith("/@") || arg.StartsWith("--@") || arg.StartsWith("-@")
|| (arg.EndsWith(".mgcb") && File.Exists(arg)))
{
var file = arg;
if (!File.Exists(arg))
file = arg.Substring(arg.StartsWith("--@") ? 4 : 3);
var commands = File.ReadAllLines(file);
var offset = 0;
lines.Insert(0, string.Format("# Begin:{0} ", file));
offset++;
for (var j = 0; j < commands.Length; j++)
{
var line = commands[j];
if (string.IsNullOrEmpty(line))
continue;
if (line.StartsWith("#"))
continue;
lines.Insert(offset, line);
offset++;
}
lines.Insert(offset, string.Format("# End:{0}", file));
continue;
}
output.Add(arg);
}
return output.ToArray();
}
private bool ParseFlags(string arg)
{
// Filename detected, redo with a build command
if (File.Exists(arg))
return ParseFlags("/build=" + arg);
// Only one flag
if (arg.Length >= 3 &&
(arg[0] == '-' || arg[0] == '/') &&
(arg[2] == '=' || arg[2] == ':'))
{
string name;
if (!_flags.TryGetValue(arg[1].ToString(), out name))
{
ShowError("Unknown option '{0}'", arg[1].ToString());
return false;
}
ParseArgument("/" + name + arg.Substring(2));
return true;
}
// Multiple flags
if (arg.Length >= 2 &&
((arg[0] == '-' && arg[1] != '-') || arg[0] == '/') &&
!arg.Contains(":") && !arg.Contains("=") &&
!_optionalOptions.ContainsKey(arg.Substring(1)))
{
for (int i = 1; i < arg.Length; i++)
{
string name;
if (!_flags.TryGetValue(arg[i].ToString(), out name))
{
ShowError("Unknown option '{0}'", arg[i].ToString());
break;
}
ParseArgument("/" + name);
}
return true;
}
// Not a flag, parse argument
return ParseArgument(arg);
}
private bool ParseArgument(string arg)
{
if (arg.StartsWith("/") || arg.StartsWith("--"))
{
// After the first escaped argument we can no
// longer read non-escaped arguments.
if (_requiredOptions.Count > 0)
return false;
// Parse an optional argument.
char[] separators = { ':', '=' };
var split = arg.Substring(arg.StartsWith("/") ? 1 : 2).Split(separators, 2, StringSplitOptions.None);
var name = split[0];
var value = (split.Length > 1) ? split[1] : "true";
MemberInfo member;
if (!_optionalOptions.TryGetValue(name.ToLowerInvariant(), out member))
{
ShowError("Unknown option '{0}'", name);
return false;
}
return SetOption(member, value);
}
if (_requiredOptions.Count > 0)
{
// Parse the next non escaped argument.
var field = _requiredOptions.Peek();
if (!IsList(field))
_requiredOptions.Dequeue();
return SetOption(field, arg);
}
ShowError("Too many arguments");
return false;
}
bool SetOption(MemberInfo member, string value)
{
try
{
if (IsList(member))
{
// Append this value to a list of options.
GetList(member).Add(ChangeType(value, ListElementType(member)));
}
else
{
// Set the value of a single option.
if (member is MethodInfo)
{
var method = member as MethodInfo;
var parameters = method.GetParameters();
if (parameters.Length == 0)
method.Invoke(_optionsObject, null);
else
method.Invoke(_optionsObject, new[] { ChangeType(value, parameters[0].ParameterType) });
}
else if (member is FieldInfo)
{
var field = member as FieldInfo;
field.SetValue(_optionsObject, ChangeType(value, field.FieldType));
}
else
{
var property = member as PropertyInfo;
property.SetValue(_optionsObject, ChangeType(value, property.PropertyType), null);
}
}
return true;
}
catch
{
ShowError("Invalid value '{0}' for option '{1}'", value, GetAttribute<CommandLineParameterAttribute>(member).Name);
return false;
}
}
static readonly string[] ReservedPrefixes = new[]
{
"$",
"/",
"#",
"--",
"-"
};
static void CheckReservedPrefixes(string str)
{
foreach (var i in ReservedPrefixes)
{
if (str.StartsWith(i))
throw new Exception(string.Format("'{0}' is a reserved prefix and cannot be used at the start of an argument name.", i));
}
}
static object ChangeType(string value, Type type)
{
var converter = TypeDescriptor.GetConverter(type);
return converter.ConvertFromInvariantString(value);
}
static bool IsList(MemberInfo member)
{
if (member is MethodInfo)
return false;
if (member is FieldInfo)
return typeof(IList).IsAssignableFrom((member as FieldInfo).FieldType);
return typeof(IList).IsAssignableFrom((member as PropertyInfo).PropertyType);
}
IList GetList(MemberInfo member)
{
if (member is PropertyInfo)
return (IList)(member as PropertyInfo).GetValue(_optionsObject, null);
if (member is FieldInfo)
return (IList)(member as FieldInfo).GetValue(_optionsObject);
throw new Exception();
}
static Type ListElementType(MemberInfo member)
{
if (member is FieldInfo)
{
var field = member as FieldInfo;
var interfaces = from i in field.FieldType.GetInterfaces()
where i.IsGenericType && i.GetGenericTypeDefinition() == typeof (IEnumerable<>)
select i;
return interfaces.First().GetGenericArguments()[0];
}
if (member is PropertyInfo)
{
var property = member as PropertyInfo;
var interfaces = from i in property.PropertyType.GetInterfaces()
where i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>)
select i;
return interfaces.First().GetGenericArguments()[0];
}
throw new ArgumentException("Only FieldInfo and PropertyInfo are valid arguments.", "member");
}
public string Title { get; set; }
bool IsWindows()
{
switch (Environment.OSVersion.Platform)
{
case PlatformID.Win32NT:
case PlatformID.Win32S:
case PlatformID.Win32Windows:
case PlatformID.WinCE:
return true;
}
return false;
}
public void ShowError(string message, params object[] args)
{
if (!string.IsNullOrEmpty(message) && OnError != null)
{
OnError(message, args);
return;
}
var name = Path.GetFileNameWithoutExtension(Process.GetCurrentProcess().ProcessName);
if (!string.IsNullOrEmpty(Title))
{
Console.Error.WriteLine(Title);
Console.Error.WriteLine();
}
if (!string.IsNullOrEmpty(message))
{
Console.Error.WriteLine(message, args);
Console.Error.WriteLine();
}
var defaultParamPrefix = IsWindows() ? "/" : "--";
Console.Error.WriteLine("Usage: {0} {1}{2}",
name,
_requiredUsageHelp.Count > 0 ? string.Join(" ", _requiredUsageHelp) + " " : string.Empty,
_optionalOptions.Count > 0 ? "<Options>" : string.Empty);
if (_optionalOptions.Count > 0)
{
Console.Error.WriteLine();
Console.Error.WriteLine("Options:");
var data = _optionalOptions.Values.ToList();
data.Sort((x, y) => {
var px = GetAttribute<CommandLineParameterAttribute>(x);
var py = GetAttribute<CommandLineParameterAttribute>(y);
return px.Name.CompareTo(py.Name);
});
foreach(var d in data)
{
var attr = GetAttribute<CommandLineParameterAttribute>(d);
var field = d as FieldInfo;
var prop = d as PropertyInfo;
var method = d as MethodInfo;
var hasValue = false;
if (field != null && field.FieldType != typeof (bool))
hasValue = true;
if (prop != null && prop.PropertyType != typeof (bool))
hasValue = true;
if (method != null && method.GetParameters().Length != 0)
hasValue = true;
var s = " ";
s += (!string.IsNullOrEmpty(attr.Flag)) ? (IsWindows() ? "/" : "-") + attr.Flag + "," : " ";
s += " " + defaultParamPrefix + attr.Name;
if (hasValue)
{
if (IsWindows())
s += ":<" + attr.ValueName + ">";
else
s += "=" + attr.ValueName.Replace("=", ":").ToUpper();
}
s = s.PadRight(35, ' ');
// Wrap text description
var bw = Math.Max(60, Console.BufferWidth);
var desc = attr.Description.Split(' ');
foreach(var dw in desc)
{
if (s.Length + dw.Length >= bw)
{
Console.WriteLine(s);
s = string.Empty.PadRight(37, ' ');
}
s += " " + dw;
}
Console.WriteLine(s);
}
}
}
static T GetAttribute<T>(ICustomAttributeProvider provider) where T : Attribute
{
return provider.GetCustomAttributes(typeof(T), false).OfType<T>().FirstOrDefault();
}
}
// Used on an optionsObject field or method to rename the corresponding commandline option.
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Property)]
public sealed class CommandLineParameterAttribute : Attribute
{
public CommandLineParameterAttribute()
{
ValueName = "value";
}
public string Name { get; set; }
public string Flag { get; set; }
public bool Required { get; set; }
public string ValueName { get; set; }
public string Description { get; set; }
}
}
@@ -0,0 +1,42 @@
// MonoGame - Copyright (C) The MonoGame Team
// This file is subject to the terms and conditions defined in
// file 'LICENSE.txt', which is part of this source code package.
using System;
using Microsoft.Xna.Framework.Content.Pipeline;
namespace MonoGame.Framework.Content.Pipeline.Builder
{
public class ConsoleLogger : ContentBuildLogger
{
public override void LogMessage(string message, params object[] messageArgs)
{
Console.WriteLine(IndentString + message, messageArgs);
}
public override void LogImportantMessage(string message, params object[] messageArgs)
{
// TODO: How do i make it high importance?
Console.WriteLine(IndentString + message, messageArgs);
}
public override void LogWarning(string helpLink, ContentIdentity contentIdentity, string message, params object[] messageArgs)
{
var warning = string.Empty;
if (contentIdentity != null && !string.IsNullOrEmpty(contentIdentity.SourceFilename))
{
warning = contentIdentity.SourceFilename;
if (!string.IsNullOrEmpty(contentIdentity.FragmentIdentifier))
warning += "(" + contentIdentity.FragmentIdentifier + ")";
warning += ": ";
}
if (messageArgs != null && messageArgs.Length != 0)
warning += string.Format(message, messageArgs);
else if (!string.IsNullOrEmpty(message))
warning += message;
Console.WriteLine(warning);
}
}
}
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleIdentifier</key>
<string>com.monogame.mgcb</string>
</dict>
</plist>
@@ -0,0 +1,103 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>10.0.0</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{4243474D-4C2E-6E69-7578-4D4743422E4C}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>MGCB</RootNamespace>
<AssemblyName>MGCB</AssemblyName>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<NoWarn>1591,0436</NoWarn>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkProfile />
<LangVersion>Default</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<Optimize>false</Optimize>
<DebugType>full</DebugType>
<EnableUnmanagedDebugging>true</EnableUnmanagedDebugging>
<OutputPath>bin\Linux\AnyCPU\Debug</OutputPath>
<IntermediateOutputPath>obj\Linux\AnyCPU\Debug</IntermediateOutputPath>
<DocumentationFile>bin\Linux\AnyCPU\Debug\MGCB.xml</DocumentationFile>
<DefineConstants>DEBUG;TRACE;LINUX</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<Optimize>true</Optimize>
<DebugType>none</DebugType>
<OutputPath>bin\Linux\AnyCPU\Release</OutputPath>
<IntermediateOutputPath>obj\Linux\AnyCPU\Release</IntermediateOutputPath>
<DocumentationFile>bin\Linux\AnyCPU\Release\MGCB.xml</DocumentationFile>
<DefineConstants>TRACE;LINUX</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Xml.Linq" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="CommandLineParser.cs" />
<Compile Include="BuildContent.cs" />
<Compile Include="ConsoleLogger.cs" />
<Compile Include="SourceFileCollection.cs" />
</ItemGroup>
<ItemGroup />
<ItemGroup>
<None Include="app.config" />
</ItemGroup>
<ItemGroup />
<ItemGroup />
<ItemGroup />
<ItemGroup />
<ItemGroup />
<ItemGroup />
<ItemGroup />
<ItemGroup />
<ItemGroup />
<ItemGroup />
<ItemGroup />
<ItemGroup />
<ItemGroup />
<ItemGroup />
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<_PostBuildHookTimestamp>@(IntermediateAssembly-&gt;'%(FullPath).timestamp')</_PostBuildHookTimestamp>
<_PostBuildHookHostPlatform>$(Platform)</_PostBuildHookHostPlatform>
</PropertyGroup>
<Target Name="PostBuildHooks" Inputs="@(IntermediateAssembly);@(ReferencePath)" Outputs="@(IntermediateAssembly);$(_PostBuildHookTimestamp)" AfterTargets="CoreCompile" BeforeTargets="AfterCompile">
<Touch Files="$(_PostBuildHookTimestamp)" AlwaysCreate="True" />
</Target>
<ItemGroup>
<ProjectReference Include="..\..\MonoGame.Framework\MonoGame.Framework.Linux.csproj">
<Project>{35253CE1-C864-4CD3-8249-4D1319748E8F}</Project>
<Name>MonoGame.Framework.Linux</Name>
</ProjectReference>
<ProjectReference Include="..\..\MonoGame.Framework.Content.Pipeline\MonoGame.Framework.Content.Pipeline.Linux.csproj">
<Project>{57696462-CE5E-4BC5-80AB-1B959E2420EA}</Project>
<Name>MonoGame.Framework.Content.Pipeline.Linux</Name>
</ProjectReference>
</ItemGroup>
<ProjectExtensions>
<MonoDevelop>
<Properties><Policies>
<TextStylePolicy inheritsSet="VisualStudio" inheritsScope="text/plain" scope="text/x-csharp" />
<CSharpFormattingPolicy IndentSwitchSection="True" NewLinesForBracesInProperties="True" NewLinesForBracesInAccessors="True" NewLinesForBracesInAnonymousMethods="True" NewLinesForBracesInControlBlocks="True" NewLinesForBracesInAnonymousTypes="True" NewLinesForBracesInObjectCollectionArrayInitializers="True" NewLinesForBracesInLambdaExpressionBody="True" NewLineForElse="True" NewLineForCatch="True" NewLineForFinally="True" NewLineForMembersInObjectInit="True" NewLineForMembersInAnonymousTypes="True" NewLineForClausesInQuery="True" SpacingAfterMethodDeclarationName="False" SpaceAfterMethodCallName="False" SpaceBeforeOpenSquareBracket="False" inheritsSet="Mono" inheritsScope="text/x-csharp" scope="text/x-csharp" />
</Policies>
</Properties>
</MonoDevelop>
</ProjectExtensions>
</Project>
@@ -0,0 +1,103 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>10.0.0</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{4243474D-572E-6E69-646F-77734D474342}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>MGCB</RootNamespace>
<AssemblyName>MGCB</AssemblyName>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<NoWarn>1591,0436</NoWarn>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkProfile />
<LangVersion>Default</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<Optimize>false</Optimize>
<DebugType>full</DebugType>
<EnableUnmanagedDebugging>true</EnableUnmanagedDebugging>
<OutputPath>bin\Windows\AnyCPU\Debug</OutputPath>
<IntermediateOutputPath>obj\Windows\AnyCPU\Debug</IntermediateOutputPath>
<DocumentationFile>bin\Windows\AnyCPU\Debug\MGCB.xml</DocumentationFile>
<DefineConstants>DEBUG;TRACE;WINDOWS</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<Optimize>true</Optimize>
<DebugType>none</DebugType>
<OutputPath>bin\Windows\AnyCPU\Release</OutputPath>
<IntermediateOutputPath>obj\Windows\AnyCPU\Release</IntermediateOutputPath>
<DocumentationFile>bin\Windows\AnyCPU\Release\MGCB.xml</DocumentationFile>
<DefineConstants>TRACE;WINDOWS</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Xml.Linq" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="CommandLineParser.cs" />
<Compile Include="BuildContent.cs" />
<Compile Include="ConsoleLogger.cs" />
<Compile Include="SourceFileCollection.cs" />
</ItemGroup>
<ItemGroup />
<ItemGroup>
<None Include="app.config" />
</ItemGroup>
<ItemGroup />
<ItemGroup />
<ItemGroup />
<ItemGroup />
<ItemGroup />
<ItemGroup />
<ItemGroup />
<ItemGroup />
<ItemGroup />
<ItemGroup />
<ItemGroup />
<ItemGroup />
<ItemGroup />
<ItemGroup />
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<_PostBuildHookTimestamp>@(IntermediateAssembly-&gt;'%(FullPath).timestamp')</_PostBuildHookTimestamp>
<_PostBuildHookHostPlatform>$(Platform)</_PostBuildHookHostPlatform>
</PropertyGroup>
<Target Name="PostBuildHooks" Inputs="@(IntermediateAssembly);@(ReferencePath)" Outputs="@(IntermediateAssembly);$(_PostBuildHookTimestamp)" AfterTargets="CoreCompile" BeforeTargets="AfterCompile">
<Touch Files="$(_PostBuildHookTimestamp)" AlwaysCreate="True" />
</Target>
<ItemGroup>
<ProjectReference Include="..\..\MonoGame.Framework\MonoGame.Framework.Windows.csproj">
<Project>{7DE47032-A904-4C29-BD22-2D235E8D91BA}</Project>
<Name>MonoGame.Framework.Windows</Name>
</ProjectReference>
<ProjectReference Include="..\..\MonoGame.Framework.Content.Pipeline\MonoGame.Framework.Content.Pipeline.Windows.csproj">
<Project>{B950DE10-AC5D-4BD9-B817-51247C4A732D}</Project>
<Name>MonoGame.Framework.Content.Pipeline.Windows</Name>
</ProjectReference>
</ItemGroup>
<ProjectExtensions>
<MonoDevelop>
<Properties><Policies>
<TextStylePolicy inheritsSet="VisualStudio" inheritsScope="text/plain" scope="text/x-csharp" />
<CSharpFormattingPolicy IndentSwitchSection="True" NewLinesForBracesInProperties="True" NewLinesForBracesInAccessors="True" NewLinesForBracesInAnonymousMethods="True" NewLinesForBracesInControlBlocks="True" NewLinesForBracesInAnonymousTypes="True" NewLinesForBracesInObjectCollectionArrayInitializers="True" NewLinesForBracesInLambdaExpressionBody="True" NewLineForElse="True" NewLineForCatch="True" NewLineForFinally="True" NewLineForMembersInObjectInit="True" NewLineForMembersInAnonymousTypes="True" NewLineForClausesInQuery="True" SpacingAfterMethodDeclarationName="False" SpaceAfterMethodCallName="False" SpaceBeforeOpenSquareBracket="False" inheritsSet="Mono" inheritsScope="text/x-csharp" scope="text/x-csharp" />
</Policies>
</Properties>
</MonoDevelop>
</ProjectExtensions>
</Project>
@@ -0,0 +1,73 @@
// MonoGame - Copyright (C) The MonoGame Team
// This file is subject to the terms and conditions defined in
// file 'LICENSE.txt', which is part of this source code package.
using System;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
namespace MGCB
{
class Program
{
static int Main(string[] args)
{
// We force all stderr to redirect to stdout
// to avoid any out of order console output.
Console.SetError(Console.Out);
if (!Environment.Is64BitProcess && Environment.OSVersion.Platform != PlatformID.Unix)
{
Console.Error.WriteLine("The MonoGame content tools only work on a 64bit OS.");
return -1;
}
var content = new BuildContent();
// Parse the command line.
var parser = new MGBuildParser(content)
{
Title = "MonoGame Content Builder\n" +
"Builds optimized game content for MonoGame projects."
};
if (!parser.Parse(args))
return -1;
// Launch debugger if requested.
if (content.LaunchDebugger)
{
try {
System.Diagnostics.Debugger.Launch();
} catch (NotImplementedException) {
// not implemented under Mono
}
}
if (content.HasWork)
{
// Print a startup message.
var buildStarted = DateTime.Now;
if (!content.Quiet)
Console.WriteLine("Build started {0}\n", buildStarted);
// Let the content build.
int successCount, errorCount;
content.Build(out successCount, out errorCount);
// Print the finishing info.
if (!content.Quiet)
{
Console.WriteLine("\nBuild {0} succeeded, {1} failed.\n", successCount, errorCount);
Console.WriteLine("Time elapsed {0:hh\\:mm\\:ss\\.ff}.", DateTime.Now - buildStarted);
}
// Return the error count.
return errorCount;
}
return 0;
}
}
}
@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("MGCB")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("MGCB")]
[assembly: AssemblyCopyright("Copyright © 2016")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("b64484a5-f0ca-47cb-8149-59272a68966d")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
@@ -0,0 +1,74 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using Microsoft.Xna.Framework.Content.Pipeline;
using Microsoft.Xna.Framework.Graphics;
namespace MGCB
{
[XmlRoot(ElementName = "SourceFileCollection")]
public sealed class SourceFileCollection
{
public GraphicsProfile Profile { get; set; }
public TargetPlatform Platform { get; set; }
public string Config { get; set; }
[XmlArrayItem("File")]
public List<string> SourceFiles { get; set; }
[XmlArrayItem("File")]
public List<string> DestFiles { get; set; }
public SourceFileCollection()
{
SourceFiles = new List<string>();
DestFiles = new List<string>();
Config = string.Empty;
}
static public SourceFileCollection Read(string filePath)
{
var deserializer = new XmlSerializer(typeof(SourceFileCollection));
try
{
using (var textReader = new StreamReader(filePath))
return (SourceFileCollection)deserializer.Deserialize(textReader);
}
catch (Exception)
{
}
return new SourceFileCollection();
}
public void Write(string filePath)
{
var serializer = new XmlSerializer(typeof(SourceFileCollection));
using (var textWriter = new StreamWriter(filePath, false, new UTF8Encoding(false)))
serializer.Serialize(textWriter, this);
}
public void Merge(SourceFileCollection other)
{
foreach (var sourceFile in other.SourceFiles)
{
var inContent = SourceFiles.Any(e => string.Equals(e, sourceFile, StringComparison.InvariantCultureIgnoreCase));
if (!inContent)
SourceFiles.Add(sourceFile);
}
foreach (var destFile in other.DestFiles)
{
var inContent = DestFiles.Any(e => string.Equals(e, destFile, StringComparison.InvariantCultureIgnoreCase));
if (!inContent)
DestFiles.Add(destFile);
}
}
}
}
@@ -0,0 +1,3 @@
<?xml version="1.0"?>
<configuration>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/></startup></configuration>