luban/src/Luban.Job.Cfg/Source/JobController.cs

281 lines
11 KiB
C#

using CommandLine;
using Luban.Common.Protos;
using Luban.Common.Utils;
using Luban.Job.Cfg.DataCreators;
using Luban.Job.Cfg.Defs;
using Luban.Job.Cfg.Generate;
using Luban.Job.Cfg.Utils;
using Luban.Job.Common.Defs;
using Luban.Job.Common.Utils;
using Luban.Server.Common;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using FileInfo = Luban.Common.Protos.FileInfo;
namespace Luban.Job.Cfg
{
[Controller("cfg")]
public class JobController : IJobController
{
private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();
private static bool TryParseArg(List<string> args, out GenArgs options, out string errMsg)
{
var helpWriter = new StringWriter();
var parser = new Parser(ps =>
{
ps.HelpWriter = helpWriter;
}); ;
var parseResult = parser.ParseArguments<GenArgs>(args);
if (parseResult.Tag == ParserResultType.NotParsed)
{
errMsg = helpWriter.ToString();
options = null;
return false;
}
else
{
options = (parseResult as Parsed<GenArgs>).Value;
errMsg = null;
string inputDataDir = options.InputDataDir;
string outputCodeDir = options.OutputCodeDir;
string outputDataDir = options.OutputDataDir;
var genTypes = options.GenType.Split(',').Select(s => s.Trim()).ToList();
if (genTypes.Any(t => t.StartsWith("code_", StringComparison.Ordinal)) && string.IsNullOrWhiteSpace(outputCodeDir))
{
errMsg = "--outputcodedir missing";
return false;
}
if (genTypes.Any(t => t.StartsWith("data_", StringComparison.Ordinal)))
{
if (string.IsNullOrWhiteSpace(inputDataDir))
{
errMsg = "--inputdatadir missing";
return false;
}
if (string.IsNullOrWhiteSpace(outputDataDir))
{
errMsg = "--outputdatadir missing";
return false;
}
if (genTypes.Contains("data_resources") && string.IsNullOrWhiteSpace(options.OutputDataResourceListFile))
{
errMsg = "--output_data_resource_list_file missing";
return false;
}
if (genTypes.Contains("output_data_json_monolithic_file") && string.IsNullOrWhiteSpace(options.OutputDataJsonMonolithicFile))
{
errMsg = "--output_data_json_monolithic_file missing";
return false;
}
if (string.IsNullOrWhiteSpace(options.L10nInputTextTableFiles) ^ string.IsNullOrWhiteSpace(options.L10nOutputNotTranslatedTextFile))
{
errMsg = "--input_l10n_text_files must be provided with --output_l10n_not_translated_text_file";
return false;
}
if (genTypes.Contains("data_template") ^ !string.IsNullOrWhiteSpace(options.TemplateDataFile))
{
errMsg = "gen_types data_template should use with --template:data:file";
return false;
}
if (genTypes.Contains("code_template") ^ !string.IsNullOrWhiteSpace(options.TemplateCodeDir))
{
errMsg = "gen_types code_template should use with --template:code:dir";
return false;
}
}
if (string.IsNullOrWhiteSpace(options.L10nPatchName) ^ string.IsNullOrWhiteSpace(options.L10nPatchInputDataDir))
{
errMsg = "--patch must be provided with --patch_input_data_dir";
return false;
}
if (options.GenType.Contains("typescript_bin") && !options.ValidateTypescriptRequire(options.GenType, ref errMsg))
{
return false;
}
if (!options.ValidateConvention(ref errMsg))
{
return false;
}
return true;
}
}
public async Task GenAsync(RemoteAgent agent, GenJob rpc)
{
var res = new GenJobRes()
{
ErrCode = Luban.Common.EErrorCode.OK,
ErrMsg = "succ",
FileGroups = new List<FileGroup>(),
};
if (!TryParseArg(rpc.Arg.JobArguments, out GenArgs args, out string errMsg))
{
res.ErrCode = Luban.Common.EErrorCode.JOB_ARGUMENT_ERROR;
res.ErrMsg = errMsg;
agent.Session.ReplyRpc<GenJob, GenJobArg, GenJobRes>(rpc, res);
return;
}
var timer = new ProfileTimer();
timer.StartPhase("= gen_all =");
try
{
string inputDataDir = args.InputDataDir;
string outputCodeDir = args.OutputCodeDir;
string outputDataDir = args.OutputDataDir;
var genTypes = args.GenType.Split(',').Select(s => s.Trim()).ToList();
timer.StartPhase("build defines");
var loader = new CfgDefLoader(agent);
await loader.LoadAsync(args.DefineFile);
await loader.LoadDefinesFromFileAsync(inputDataDir);
timer.EndPhaseAndLog();
var rawDefines = loader.BuildDefines();
TimeZoneInfo timeZoneInfo = string.IsNullOrEmpty(args.L10nTimeZone) ? null : TimeZoneInfo.FindSystemTimeZoneById(args.L10nTimeZone);
var excludeTags = args.OutputExcludeTags.Split(',').Select(t => t.Trim().ToLowerInvariant()).Where(t => !string.IsNullOrEmpty(t)).ToList();
var ass = new DefAssembly(args.L10nPatchName, timeZoneInfo, excludeTags, agent);
ass.Load(rawDefines, agent, args);
List<DefTable> exportTables = ass.GetExportTables();
List<DefTypeBase> exportTypes = ass.GetExportTypes();
bool hasLoadCfgData = false;
bool needL10NTextConvert = !string.IsNullOrWhiteSpace(args.L10nInputTextTableFiles);
async Task CheckLoadCfgDataAsync()
{
if (!hasLoadCfgData)
{
hasLoadCfgData = true;
var timer = new ProfileTimer();
timer.StartPhase("load config data");
await DataLoaderUtil.LoadCfgDataAsync(agent, ass, args.InputDataDir, args.L10nPatchName, args.L10nPatchInputDataDir, args.InputConvertDataDir);
timer.EndPhaseAndLog();
if (needL10NTextConvert)
{
ass.InitL10n(args.L10nTextValueFieldName);
await DataLoaderUtil.LoadTextTablesAsync(agent, ass, ".", args.L10nInputTextTableFiles);
}
timer.StartPhase("validate");
var validateCtx = new ValidatorContext(ass, args.ValidateRootDir);
await validateCtx.ValidateTables(exportTables);
timer.EndPhaseAndLog();
}
}
var tasks = new List<Task>();
var genCodeFilesInOutputCodeDir = new ConcurrentBag<FileInfo>();
var genDataFilesInOutputDataDir = new ConcurrentBag<FileInfo>();
var genScatteredFiles = new ConcurrentBag<FileInfo>();
foreach (var genType in genTypes)
{
var ctx = new GenContext()
{
GenType = genType,
Assembly = ass,
GenArgs = args,
ExportTypes = exportTypes,
ExportTables = exportTables,
GenCodeFilesInOutputCodeDir = genCodeFilesInOutputCodeDir,
GenDataFilesInOutputDataDir = genDataFilesInOutputDataDir,
GenScatteredFiles = genScatteredFiles,
Tasks = tasks,
DataLoader = CheckLoadCfgDataAsync,
};
GenContext.Ctx = ctx;
var render = RenderFactory.CreateRender(genType);
if (render == null)
{
throw new Exception($"unknown gentype:{genType}");
}
if (render is DataRenderBase)
{
await CheckLoadCfgDataAsync();
}
render.Render(ctx);
GenContext.Ctx = null;
}
await Task.WhenAll(tasks.ToArray());
if (needL10NTextConvert)
{
var notConvertTextList = DataExporterUtil.GenNotConvertTextList(ass.NotConvertTextSet);
var md5 = FileUtil.CalcMD5(notConvertTextList);
string outputNotConvertTextFile = args.L10nOutputNotTranslatedTextFile;
CacheManager.Ins.AddCache(outputNotConvertTextFile, md5, notConvertTextList);
genScatteredFiles.Add(new FileInfo() { FilePath = outputNotConvertTextFile, MD5 = md5 });
}
if (!genCodeFilesInOutputCodeDir.IsEmpty)
{
res.FileGroups.Add(new FileGroup() { Dir = outputCodeDir, Files = genCodeFilesInOutputCodeDir.ToList() });
}
if (!genDataFilesInOutputDataDir.IsEmpty)
{
res.FileGroups.Add(new FileGroup() { Dir = outputDataDir, Files = genDataFilesInOutputDataDir.ToList() });
}
if (!genScatteredFiles.IsEmpty)
{
res.ScatteredFiles.AddRange(genScatteredFiles);
}
}
catch (DataCreateException e)
{
res.ErrCode = Luban.Common.EErrorCode.DATA_PARSE_ERROR;
res.ErrMsg = $@"
=======================================================================
解析失败!
文件: {e.OriginDataLocation}
错误位置: {e.DataLocationInFile}
Err: {e.OriginErrorMsg}
字段: {e.VariableFullPathStr}
=======================================================================
";
res.StackTrace = e.OriginStackTrace;
}
catch (Exception e)
{
res.ErrCode = Luban.Common.EErrorCode.JOB_EXCEPTION;
res.ErrMsg = $@"
=======================================================================
{ExceptionUtil.ExtractMessage(e)}
=======================================================================
";
res.StackTrace = e.StackTrace;
}
DefAssemblyBase.LocalAssebmly = null;
timer.EndPhaseAndLog();
agent.Session.ReplyRpc<GenJob, GenJobArg, GenJobRes>(rpc, res);
}
}
}