forked from MonoGame/MonoGame
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathContentBuilderParams.cs
More file actions
320 lines (274 loc) · 14 KB
/
ContentBuilderParams.cs
File metadata and controls
320 lines (274 loc) · 14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
// MonoGame - Copyright (C) MonoGame Foundation, Inc
// This file is subject to the terms and conditions defined in
// file 'LICENSE.txt', which is part of this source code package.
using System.CommandLine;
using System.CommandLine.Binding;
using System.CommandLine.Builder;
using System.CommandLine.Parsing;
using System.Reflection;
using Microsoft.Xna.Framework.Content.Pipeline;
using Microsoft.Xna.Framework.Graphics;
using MonoGame.Framework.Content.Pipeline.Builder.Server;
namespace MonoGame.Framework.Content.Pipeline.Builder;
/// <summary>
/// A list of arguments used by <see cref="ContentBuilder"/>.
/// <para>Use <see cref="ContentBuilderParams.Parse"/> to acquire the arguments from the passed cli args.</para>
/// </summary>
public class ContentBuilderParams
{
class RootOptions : BinderBase<ContentBuilderParams>
{
private readonly Func<BindingContext, ContentBuilderParams> _contentBuilderArgsFunc;
public RootOptions(RootCommand rootCommand)
{
var defaultValues = new ContentBuilderParams();
var workingDirectoryOption = new Option<string>(
name: "--workingDir",
description: "The working directory of the content builder.",
getDefaultValue: () => defaultValues.WorkingDirectory);
rootCommand.AddGlobalOption(workingDirectoryOption);
var srcDirectoryOptions = new Option<string>(
name: "--src",
description: "The source asset directory.",
getDefaultValue: () => defaultValues.SourceDirectory);
srcDirectoryOptions.AddAlias("-s");
rootCommand.AddGlobalOption(srcDirectoryOptions);
var outputDirectoryOption = new Option<string>(
name: "--output",
description: "The output content directory.",
getDefaultValue: () => defaultValues.OutputDirectory);
outputDirectoryOption.AddAlias("-o");
rootCommand.AddGlobalOption(outputDirectoryOption);
var intermediateDirectoryOption = new Option<string>(
name: "--intermediate",
description: "The intermediate content directory.",
getDefaultValue: () => defaultValues.IntermediateDirectory);
intermediateDirectoryOption.AddAlias("-i");
rootCommand.AddGlobalOption(intermediateDirectoryOption);
var platformOption = new Option<TargetPlatform>(
name: "--platform",
description: "The content target platform.",
getDefaultValue: () => defaultValues.Platform);
platformOption.AddAlias("-p");
rootCommand.AddGlobalOption(platformOption);
var graphicsProfileOption = new Option<GraphicsProfile>(
name: "--graphics-profile",
description: "The content graphics profile.",
getDefaultValue: () => defaultValues.GraphicsProfile);
graphicsProfileOption.AddAlias("-g");
rootCommand.AddGlobalOption(graphicsProfileOption);
var compressContentOption = new Option<bool>(
name: "--compress",
description: "Compress the build content files.",
getDefaultValue: () => defaultValues.CompressContent);
rootCommand.AddGlobalOption(compressContentOption);
var logLevelOption = new Option<LogLevel>(
name: "--loglevel",
description: "The log level of messages that get outputed to the console.",
getDefaultValue: () => defaultValues.LogLevel);
logLevelOption.AddAlias("-l");
rootCommand.AddGlobalOption(logLevelOption);
_contentBuilderArgsFunc = (bindingContext) =>
{
var workingDir = bindingContext.ParseResult.GetValueForOption(workingDirectoryOption) ?? defaultValues.WorkingDirectory;
return new ContentBuilderParams
{
WorkingDirectory = workingDir,
SourceDirectory = MakeRelative(workingDir, bindingContext.ParseResult.GetValueForOption(srcDirectoryOptions) ?? defaultValues.SourceDirectory),
OutputDirectory = MakeRelative(workingDir, bindingContext.ParseResult.GetValueForOption(outputDirectoryOption) ?? defaultValues.OutputDirectory),
IntermediateDirectory = MakeRelative(workingDir, bindingContext.ParseResult.GetValueForOption(intermediateDirectoryOption) ?? defaultValues.IntermediateDirectory),
Platform = bindingContext.ParseResult.GetValueForOption(platformOption),
GraphicsProfile = bindingContext.ParseResult.GetValueForOption(graphicsProfileOption),
CompressContent = bindingContext.ParseResult.GetValueForOption(compressContentOption),
LogLevel = bindingContext.ParseResult.GetValueForOption(logLevelOption)
};
};
}
protected override ContentBuilderParams GetBoundValue(BindingContext bindingContext) => _contentBuilderArgsFunc(bindingContext);
}
class ServerOptions : BinderBase<List<ContentServer>>
{
private readonly Func<BindingContext, List<ContentServer>> _contentBuilderArgsFunc;
public ServerOptions(Command rootCommand)
{
var contentServers = new List<ContentServer>();
var options = new List<(Type, PropertyInfo, Option)>();
foreach (var serverType in ContentBuilderHelper.GetServerTypes())
{
var contentServer = (ContentServer)Activator.CreateInstance(serverType)!;
foreach (var (attribute, propertyInfo) in ContentBuilderHelper.GetServerProperties(serverType))
{
var optionType = typeof(Option<>).MakeGenericType(propertyInfo.PropertyType);
var option = (Option)Activator.CreateInstance(optionType, new object[] {
"--" + attribute.Name,
attribute.Description
})!;
option.SetDefaultValueFactory(() => propertyInfo.GetValue(contentServer));
rootCommand.AddGlobalOption(option);
options.Add((serverType, propertyInfo, option));
}
contentServers.Add(contentServer);
}
_contentBuilderArgsFunc = (bindingContext) =>
{
foreach (var (type, propInfo, option) in options)
{
var value = bindingContext.ParseResult.GetValueForOption(option);
if (value != null)
{
var server = contentServers.Find(s => s.GetType() == type);
propInfo.SetValue(server, value);
}
}
return contentServers;
};
}
protected override List<ContentServer> GetBoundValue(BindingContext bindingContext) => _contentBuilderArgsFunc(bindingContext);
}
/// <summary>
/// Set the mode in which the content builder is run in. See <see cref="ContentBuilderMode"/> for available modes.
/// </summary>
/// <value><see cref="ContentBuilderMode.None"/> by default.</value>
public ContentBuilderMode Mode { get; set; } = ContentBuilderMode.None;
/// <summary>
/// Gets or sets the working directory of the <see cref="ContentBuilder"/>.
/// </summary>
/// <value><see cref="Directory.GetCurrentDirectory"/> by default.</value>
public string WorkingDirectory { get; set; } = Directory.GetCurrentDirectory();
/// <summary>
/// Gets or sets the location of the content relative to the <see cref="WorkingDirectory"/>.
/// </summary>
/// <value><c>Content</c> by default.</value>
public string SourceDirectory { get; set; } = "Content";
/// <summary>
/// Gets the rooted location of <see cref="SourceDirectory"/>.
/// </summary>
public string RootedSourceDirectory => MakeRooted(SourceDirectory);
/// <summary>
/// Gets or sets the location for the content output relative to the <see cref="WorkingDirectory"/>.
/// </summary>
/// <value><c>bin/Content</c> by default.</value>
public string OutputDirectory { get; set; } = "bin/Content";
/// <summary>
/// Gets the rooted location of <see cref="OutputDirectory"/>.
/// </summary>
public string RootedOutputDirectory => MakeRooted(OutputDirectory);
/// <summary>
/// Gets or sets the location for the intermediate files for content build relative to the <see cref="WorkingDirectory"/>.
/// </summary>
/// <value><c>obj/Content</c> by default.</value>
public string IntermediateDirectory { get; set; } = "obj/Content";
/// <summary>
/// Gets the rooted location of <see cref="IntermediateDirectory"/>.
/// </summary>
public string RootedIntermediateDirectory => MakeRooted(IntermediateDirectory);
/// <summary>
/// Gets or sets the desired platform for <see cref="ContentBuilder"/> to build the content for.
/// </summary>
/// <value><see cref="TargetPlatform.DesktopGL"/> by default.</value>
public TargetPlatform Platform { get; set; } = TargetPlatform.DesktopGL;
/// <summary>
/// Gets or sets the desired graphics profile for <see cref="ContentBuilder"/> to build the content for.
/// </summary>
/// <value><see cref="GraphicsProfile.HiDef"/> by default.</value>
public GraphicsProfile GraphicsProfile { get; set; } = GraphicsProfile.HiDef;
/// <summary>
/// Gets or sets if <see cref="ContentBuilder"/> should compress each built content file.
/// </summary>
/// <value><c>false</c> by default.</value>
public bool CompressContent { get; set; } = false;
/// <summary>
/// Gets or sets the logging level of information that <see cref="ContentBuilder"/> will display to console.
/// </summary>
/// <value><see cref="LogLevel.Info"/> by default.</value>
public LogLevel LogLevel { get; set; } = LogLevel.Info;
/// <summary>
/// Should the <see cref="ContentBuilder"/> rebuild all the assets and ignore the content cache.
/// </summary>
/// <value><c>false</c> by default.</value>
public bool Rebuild { get; set; } = false;
/// <summary>
/// Should the <see cref="ContentBuilder"/> skip cleaning up old content cache data after the build is finished in <see cref="ContentBuilderMode.Builder"/> mode.
/// </summary>
/// <value><c>false</c> by default.</value>
public bool SkipClean { get; set; } = false;
/// <summary>
/// A list of servers to start up when the <see cref="Mode"/> is set to <see cref="ContentBuilderMode.Server"/>.
/// </summary>
/// <value>A collection of <see cref="ContentServer"/> classes found by scaning all referenced assemblies.</value>
public List<ContentServer> Servers { get; set; } = []; // TODO: Fix command line display
/// <summary>
/// Parses out the main entry point args into a <see cref="ContentBuilderParams"/> to be used by <see cref="ContentBuilder"/>.
/// </summary>
/// <param name="args">Arguments passed to the main entry point of the app.</param>
/// <returns>
/// <see cref="ContentBuilderParams"/> containing the parsed arguments, or an empty <see cref="ContentBuilderParams"/> if no arguments were passed.
/// </returns>
public static ContentBuilderParams Parse(params string[] args)
{
var ret = new ContentBuilderParams();
var defaultValues = new ContentBuilderParams();
var rootCommand = new RootCommand("Content builder and conntent server for MonoGame.");
var rootOptions = new RootOptions(rootCommand);
var buildCommand = new Command("build", "Build all the content.");
var rebuildOption = new Option<bool>(
name: "--rebuild",
description: "Should the builder rebuild all the assets and ignore the content cache.",
getDefaultValue: () => defaultValues.Rebuild);
buildCommand.AddOption(rebuildOption);
var skipCleanOption = new Option<bool>(
name: "--skip-clean",
description: "Should the builder skip cleaning up old content cache data after the build is finished.",
getDefaultValue: () => defaultValues.SkipClean);
buildCommand.AddOption(skipCleanOption);
buildCommand.SetHandler(
(contentBuilder, rebuildOption, skipCleanOption) =>
{
ret = contentBuilder;
ret.Mode = ContentBuilderMode.Builder;
ret.Rebuild = rebuildOption;
ret.SkipClean = skipCleanOption;
},
rootOptions,
rebuildOption,
skipCleanOption);
rootCommand.AddCommand(buildCommand);
var serverCommand = new Command("server", "Start a content server.");
var sererOptions = new ServerOptions(serverCommand);
serverCommand.SetHandler(
(contentBuilder, sererOptions) =>
{
ret = contentBuilder;
ret.Mode = ContentBuilderMode.Server;
ret.Servers = sererOptions;
},
rootOptions,
sererOptions);
rootCommand.AddCommand(serverCommand);
bool helpShown = false;
var parser = new CommandLineBuilder(rootCommand)
.UseDefaults()
.UseHelp(ctx => helpShown = true)
.Build();
parser.Invoke(args);
if (helpShown)
{
ret.Mode = ContentBuilderMode.None;
}
return ret;
}
private string MakeRooted(string path)
{
if (!Path.IsPathRooted(path))
path = Path.Combine(WorkingDirectory, path);
return Path.GetFullPath(path);
}
private static string MakeRelative(string workingDir, string path)
{
if (!Path.IsPathRooted(path))
return FileHelper.NormalizeDirectorySeparators(path);
// Note this may still return an absolute path in the case
// that these directories are on different drives.
return Path.GetRelativePath(workingDir, path);
}
}