在工作中,发现有时会因为一些原因using了一些不适用于打包apk的命名空间导致打包失败,为项目的开发造成了不便,因此为项目做了一个小功能:在每次编译完成后自动进行命名空间的检查,根据设置的筛选条件检查是否存在不合理的命名空间引用
PS:最后会附上完整代码
首先是Env数据类:其中存放了几种过滤信息如下
接下来是Util工具类,具体的功能实现都在这里面:
private static bool CheckHasFilter(List filterData,string fileName)
{
//......
}
private static bool CheckHasFilter(List filterData,string fileName,out List hasFilterName)
{
//......
}
///
/// 对传入的文件信息FileInfo类型 进行内容检查
///
///
private static List CheckFileInfo(FileInfo fileInfo)
{
//1.文件名过滤
//2.获取内容
//3.宏定义过滤
//4.内容检查并存放数据
//5.返回
//......
}
///
/// 主方法
///
/// 检查的根目录
///
public static Dictionary> CheckHasErrorNamespace(string checkFolderPath)
{
//1.得到目录下的C#文件,进行命名空间检查
//2.得到目录下的子目录,递归调用该方法获得子目录的检查信息
//3.返回得到的检查信息
//......
}
///
/// 执行并检查传入方法损耗的时间
///
/// 功能主体方法
/// 自定义的功能名字
private static void CheckUseTime(Action testFunc,string modelName)
{
Debug.Log($"开始:---{modelName}---");
Stopwatch s = new Stopwatch();
s.Reset();
s.Start();
testFunc?.Invoke();
s.Stop();
Debug.Log($"结束:本次---{modelName}---的运行时间 = {s.ElapsedMilliseconds} ms");
}
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using UnityEditor;
using UnityEngine;
using Debug = UnityEngine.Debug;
public static class CheckNamespaceUtil
{
[MenuItem("Test/命名空间检查")]
[InitializeOnLoadMethod]
public static void CheckNamespace()
{
var checkNameList = CheckNamespaceEnv.checkName;
string msg = "命名空间检查内容:\n";
for (int i = 0; i < checkNameList.Count; i++)
{
msg += checkNameList[i] + ",";
}
Debug.Log(msg);
CheckUseTime(delegate
{
var checkInfos = CheckHasErrorNamespace(Application.dataPath);
foreach (var v in checkInfos)
{
for (int i = 0; i < v.Value.Count; i++)
{
Debug.LogError($"名字:{v.Key} 含有命名空间字段:{v.Value[i]} ------请及时清理");
}
}
}, "命名空间检查");
}
///
/// 主方法
///
/// 检查的根目录
///
public static Dictionary> CheckHasErrorNamespace(string checkFolderPath)
{
//1.得到目录下的C#文件,进行命名空间检查
//2.得到目录下的子目录,递归调用该方法获得子目录的检查信息
//3.返回得到的检查信息
Dictionary> checkInfo = new Dictionary>();;
//1.
DirectoryInfo floder = new DirectoryInfo(checkFolderPath);
FileInfo[] fileInfos = floder.GetFiles("*.cs");
for (int i = 0; i < fileInfos.Length; i++)
{
if (checkInfo.ContainsKey(fileInfos[i].Name))
{
checkInfo[fileInfos[i].Name].AddRange(CheckFileInfo(fileInfos[i]));
}
else
{
checkInfo.Add(fileInfos[i].Name,CheckFileInfo(fileInfos[i]));
}
}
//2.
string[] dir = Directory.GetDirectories(checkFolderPath);
for (int i = 0; i < dir.Length; i++)
{
var data = CheckHasErrorNamespace(dir[i].Replace("\\", "/"));
foreach (var v in data)
{
if (checkInfo.ContainsKey(v.Key))
{
checkInfo[v.Key].AddRange(v.Value);
}
else
{
checkInfo.Add(v.Key,v.Value);
}
}
}
//PS:上面这样处理是为了 若有不同命名空间下的同名文件时,让他们合并
return checkInfo;
}
///
/// 对传入的文件信息FileInfo类型 进行内容检查
///
///
private static List CheckFileInfo(FileInfo fileInfo)
{
//1.文件名过滤
//2.获取内容
//3.宏定义过滤
//4.内容检查并存放数据
//5.返回
List checkInfo;
//1.
if (CheckHasFilter(CheckNamespaceEnv.filterFile, fileInfo.Name,out checkInfo))
{
return checkInfo;
}
//2.
StreamReader sr = fileInfo.OpenText();
string lineContent = "";
while ((lineContent = sr.ReadLine()) != null)
{
//3.
bool isMacroFilterOpen = false;//标志是否因为宏定义打开的跳过开关
//先判断是否处于宏定义内容中
bool isMacroDefineHead = CheckHasFilter(new List {"#if"}, lineContent);
if (isMacroDefineHead)
{
//过滤符合条件的宏定义
if (CheckHasFilter(CheckNamespaceEnv.filterMacroDefine, lineContent))
{
isMacroFilterOpen = true;
continue;
}
}
bool isMacroDefineFoot = CheckHasFilter(new List {"#endif"}, lineContent);
if (isMacroDefineFoot)
{
isMacroFilterOpen = false;
continue;
}
//根据开关跳过该行
if (isMacroFilterOpen)
{
continue;
}
//4.
List lineCheckInfo;
if (!CheckHasFilter(new List{"using"}, lineContent))
{
continue;
}
if (CheckHasFilter(CheckNamespaceEnv.checkName, lineContent, out lineCheckInfo))
{
checkInfo.AddRange(lineCheckInfo);
}
}
return checkInfo;
}
#region 过滤方法
private static bool CheckHasFilter(List filterData,string fileName)
{
for (int i = 0; i < filterData.Count; i++)
{
int index = fileName.IndexOf(filterData[i]);
//返回结果等于-1表示匹配失败,匹配成功IndexOf会返回匹配的开始下标
if (index != -1)
{
return true;
}
}
return false;
}
private static bool CheckHasFilter(List filterData,string fileName,out List hasFilterName)
{
hasFilterName = new List();
for (int i = 0; i < filterData.Count; i++)
{
int index = fileName.IndexOf(filterData[i]);
//返回结果等于-1表示匹配失败,匹配成功IndexOf会返回匹配的开始下标
if (index != -1)
{
hasFilterName.Add(filterData[i]);
}
}
if (hasFilterName.Count > 0)
{
return true;
}
return false;
}
#endregion
///
/// 执行并检查传入方法损耗的时间
///
/// 功能主体方法
/// 自定义的功能名字
private static void CheckUseTime(Action testFunc,string modelName)
{
Debug.Log($"开始:---{modelName}---");
Stopwatch s = new Stopwatch();
s.Reset();
s.Start();
testFunc?.Invoke();
s.Stop();
Debug.Log($"结束:本次---{modelName}---的运行时间 = {s.ElapsedMilliseconds} ms");
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public static class CheckNamespaceEnv
{
public static int checkLineNum = 40;//检查前20行
///
/// 检查的最上级文件夹列表
///
public static List checkFolder = new List
{
};
///
/// 名字中含有这些词的文件过滤掉
///
public static List filterFile = new List
{
"TMP","Editor"
};
///
/// 名字中含有这些词的子文件夹过滤掉
///
public static List filterFolder = new List
{
"Editor","3rd","Extension","NavMeshComponents","Plugins","Generated"
};
///
/// 跳过这些宏定义
///
public static List filterMacroDefine = new List
{
"UNITY_EDITOR"
};
///
/// 检查包含这些关键字的命名空间引用
///
public static List checkName = new List
{
"NPOI","UnityEditor"
};
}
其实很多地方都可以自己按照需求另外定制的,然后个人建议自己在内部匹配命名空间时加一个行数限制或者其他限制,避免每次都要检查整个文件,严重损耗性能。我这里呢在Env类中设置了行数这个参数,但写到这里才记起来忘记加了,但在我的这个例子中是无关紧要的。
有什么意见建议欢迎提出啦!-------来自一个刚刚开始工作的小萌新