虚幻插件语言 (UPL)是一种基于XML的简单语言,用于操作XML和返回字符串。它包含一个分段,在计算任何其他分段之前,每个架构计算一次这个分段。状态被维护并推进到下个分段进行评估,因此分段的执行顺序很重要。虽然UPL是一个用于修改和查询XML的通用系统,但它专门用于允许插件影响其所属包的全局配置。例如,它允许插件修改Android APK AndroidManfiest.xml文件或IOS IPA plist文件。UBT还将查询插件的UPL xml文件,查找文件中要包含的字符串(对于包来说必须是常见的字符串),例如Android上的一些.java文件。
启用追踪
如果需要查看在插件情境中执行的指令,请添加以下命令以启用追踪:
<trace enable="true"/>
在此指令之后,在情境中实际执行的所有节点都将写入日志,直到执行 <trace enable="false"></trace>
。您还可以使用此命令获得情境中所有变量的转储:
<dumpvars/>
变量类型
支持Bool、Int和String变量类型。任何属性都可以引用一个变量,在使用该语法求值之前,将用等效字符串替换该变量:
$B(name) = 布尔变量"名称"值
$I(name) = 整数变量"名称"值
$S(name) = 字符串变量"名称"值
$E(name) = 元素变量"名称"值
以下变量将自动初始化:
$S(Output) = 返回用于计算分段的输出(初始化为输入)
$S(Architecture) = 目标架构(armeabi-armv7a、arm64-v8a、x86、x86_64)
$S(PluginDir) = 加载XML文件的目录
$S(EngineDir) = 引擎目录
$S(BuildDir) = 项目平台相应的构建目录(在中间文件夹中)
$S(Configuration) = 配置类型(Debug、DebugGame、Development、Test、Shipping)
$B(Distribution) = 如果是发布构建,则为true
除上述变量外,所有变量都在插件的情境中,以防止命名空间冲突;尝试将一个新值设置为上述任何一个值(输出除外)只会影响当前情境。
变量操作
以下节点允许变量操作:
<setBool result="" value=""/>
<setInt result="" value=""/>
<setString result="" value=""/>
<setElement result="" value=""/>
<setElement result="" value="" text=""/>
<setElement result="" xml=""/>
包含值的<setElement>用于创建一个空的XML元素,并将标记设置为值。
包含值和文本的<setElement>用于创建一个XML元素,并将标记设置为未解析文本的值。
包含xml的<setElement>将解析提供的XML。记住对任何特殊字符进行换码!
也可以从ini文件中的属性设置变量:
<setBoolFromProperty result="" ini="" section="" property="" default=""/>
<setBoolFromPropertyContains result="" ini="" section="" property="" default="" contains=""/>
<setIntFromProperty result="" ini="" section="" property="" default=""/>
<setStringFromProperty result="" ini="" section="" property="" default=""/>
还可以使用
<setStringFromEnvVar result="" value=""/>
您还可以检查是否定义了特定的环境变量(同样,用"%"字符包装):
<setBoolEnvVarDefined result="" value=""/>
使用环境变量节点的一般示例:
<setBoolEnvVarDefined result="bHasNDK" value="%NDKROOT%"/>
<setStringFromEnvVar result="NDKPath" value="%NDKROOT%"/>
布尔变量也可以设置为应用运算符的结果:
<setBoolNot result="" source=""/>
<setBoolAnd result="" arg1="" arg2=""/>
<setBoolOr result="" arg1="" arg2=""/>
<setBoolIsEqual result="" arg1="" arg2=""/>
<setBoolIsLess result="" arg1="" arg2=""/>
<setBoolIsLessEqual result="" arg1="" arg2=""/>
<setBoolIsGreater result="" arg1="" arg2=""/>
<setBoolIsGreaterEqual result="" arg1="" arg2=""/>
<setBoolFromPropertyContains result="" ini="" section="" property="" contains=""/>
整数变量可以使用这些算术运算:
<setIntAdd result="" arg1="" arg2=""/>
<setIntSubtract result="" arg1="" arg2=""/>
<setIntMultiply result="" arg1="" arg2=""/>
<setIntDivide result="" arg1="" arg2=""/>
字符串是用下面的代码操作的:
<setStringAdd result="" arg1="" arg2=""/>
<setStringSubstring result="" source="" start="" length=""/>
<setStringReplace result="" source="" find="" with=""/>
字符串长度可以用以下代码检索:
<setIntLength result="" source=""/>
搜索字符串的索引可以通过以下命令在源代码中找到:
<setIntFindString result="" source="" find=""/>
还可以使用以下快捷键字符串比较,而不是使用
<setBoolStartsWith result="" source="" find=""/>
<setBoolEndsWith result="" source="" find=""/>
<setBoolContains result="" source="" find=""/>
写入消息
使用此节点将消息写入日志:
<log text=""/>
条件执行
条件执行使用以下形式:
<if condition="">
<true>
<\-- 如果条件为true,且是布尔变量,则执行 -->
</true>
<false>
<\-- 如果条件为false,且是布尔变量,则执行 -->
</false>
</if>
<true>
和 <false>
块是可选的。条件必须在布尔变量中。布尔运算符节点可以组合起来,为更复杂的条件创建最终状态:
<setBoolNot result="notDistribution" source="$B(Distribution)/>
<setBoolIsEqual result="isX86" arg1="$S(Architecture)" arg2="x86"/>
<setBoolIsEqual result="isX86_64" arg2="$S(Architecture)" arg2="x86_64/">
<setBoolOr result="isIntel" arg1="$B(isX86)" arg2="$B(isX86_64)"/>
<setBoolAnd result="intelAndNotDistribution" arg1="$B(isIntel)" arg2="$B(notDistribution)"/>
<if condition="intelAndNotDistribution">
<true>
<\-- 如果不是发布构建,为Intel执行一些操作 -->
</true>
</if>
"isIntel"也可以这么做:
<setStringSubstring result="subarch" source="$S(Architecture)" start="0" length="3"/>
<setBoolEquals result="isIntel" arg1="$S(subarch)" arg2="x86"/>
有两个快捷方式节点可用于条件执行:
以下代码:
<isArch arch="armeabi-armv7">
<\-- do stuff -->
</isArch>
与以下代码等效:
<setBoolEquals result="temp" arg1="$S(Architecture)" arg2="armeabi-armv7">
<if condition="temp">
<true>
<\-- do stuff -->
</true>
</if>
以下代码:
<isDistribution> blah
<\-- do stuff -->
</isDistribution>
与以下代码等效:
<if condition="Distribution">
<\-- do stuff -->
</if>
可以使用以下代码停止执行:
<return/>
循环
可以使用这些节点创建循环:
<while condition="">
<\-- do stuff -->
</while>
<break/>
<continue/>
<while>
主体将执行,直到条件为false或命中一个 <break/>
。如果条件仍然为true或退出,<continue></continue>
将重新启动循环的执行。
<while>
主体外的 <break/>
的作用将与 <return></return>
相同。
下面是一个示例循环,它将1到5写入日志,其中跳过3。注意,while条件的更新应该在continue之前完成,否则它可能不会退出。
<setInt result="index" value="0"/>
<setBool result="loopRun" value="true"/>
<while condition="loopRun">
<setIntAdd result="index" arg1="$I(index)" arg2="1"/>
<setBoolIsLess result="loopRun" arg1="$I(index)" arg2="5"/>
<setBoolIsEqual result="indexIs3" arg1="$I(index)" arg2="3"/>
<if condition="indexIs3">
<true>
<continue/>
</true>
</if>
<log text="$I(index)"/>
</while>
也可以使用变量替换来生成结果变量名。这使得在循环中创建数组成为可能:
<setString result="array_$I(index)" value="element $I(index) in array"/>
可以使用以下方法检索(值作为变量名处理):
<setStringFrom result="out" value="array_$I(index)"/>
也可以使用变量替换来生成结果变量名。这使得在循环中创建数组成为可能:
<setString result="array_$I(index)" value="element $I(index) in array"/>
可以使用以下方法检索(值作为变量名处理):
<setStringFrom result="out" value="array_$I(index)"/>
对于布尔和整数类型,您可以使用 <setBoolFrom>
和 <setIntFrom>
。
插入文本
用于将文本插入分段的节点如下所示:
<insert> 主体 </insert>
<insertNewline/>
<insertValue value=""/>
<loadLibrary name="" failmsg=""/>
第一个将在返回的分段字符串中插入文本或节点。请注意,您必须对以下符号使用换码的字符:
< = <
> = >
& = &
<insertValue value=""> /pre>
- 在插入之前计算变量的值。如果值包含双引号("),则必须用引号对其进行换码。<loadLibrary name="" failmsg="">
- 是插入system.LoadLibrary尝试/捕捉块的快捷方式,带有可选的有关加载失败案例的日志消息。
搜索和替换
您可通过以下方式在输出中进行搜索和替换:
<replace find="" with=""/>
您还可以直接操作实际的$S(输出),上面的方法更有效:
<setStringAdd result="Input" arg1="$S(Output)" arg2="sample\n"/>
<setStringReplace result="Input" source="$S(Output)" find=".LAUNCH" with=".INFO"/>
XML操作
XML操作使用以下节点:
<addElement tag="" name=""/>
<addElements tag=""> 主体 </addElements>
<removeElement tag=""/>
<setStringFromTag result="" tag=""/>
<setStringFromAttribute result="" tag="" name=""/>
<setStringFromTagText result="" tag=""/>
<addAttribute tag="" name="" value=""/>
<removeAttribute tag="" name=""/>
<loopElements tag=""> instructions </loopElements>
当前元素通过tag="$"引用。元素变量通过$varname引用,因为使用$E(varname)将被扩展为与XML等效的字符串。addElement、addElements 和 removeElement 默认应用于所有匹配的标记。可添加到的可选once="true"属性仅应用于第一个匹配标记。
<uses-permission>
、<uses-feature>
和 <uses-library>
使用以下方式更新:
<addPermission android:name="" ../>
<addFeature android:name="" ../>
<addLibrary android:name="" ../>
上述命令中的任何属性都复制到添加到清单的元素中,以便您可以执行以下操作,例如:
<addFeature android:name="android.hardware.usb.host" android:required="true"/>
复制文件
最后,这些节点允许复制用于暂存jar和so文件的文件:
<copyFile src="" dst="" force=""/>
<copyDir src="" dst="" force=""/>
如果force为false,则只有在长度或时间戳不匹配时才替换文件。此值默认为true。
应该使用以下目录作为src和dst路径的基础:
$S(PluginDir) = 加载XML文件的目录
$S(EngineDir) = 引擎目录
$S(BuildDir) = 项目平台相应的构建目录
虽然可以在APK目录之外写入,但不推荐这样做。
移除文件
如果必须移除文件(例如发布构建中的纯开发文件),则可以使用该节点:
<deleteFiles filespec=""/>
它仅限于从BuildDir中移除文件。下面是从资源目录中移除Oculus签名文件(osig)的示例用法:
<deleteFiles filespec="assets/oculussig_*"/>
打包或部署
在打包或部署阶段评估以下部分:
对于所有平台
<\-- 每个架构都要对init部分进行一次评估 -->
<init> </init>
Android特定部分
<\-- 应用于AndroidManifest.xml的可选更新 -->
<androidManifestUpdates> </androidManifestUpdates>
<\-- proguard的可选添加内容 -->
<proguardAdditions> </proguardAdditions>
<\-- 可选的AAR导入添加内容 -->
<AARImports> </AARImports>
<\-- 可选的基础build.gradle添加内容 -->
<baseBuildGradleAdditions> </baseBuildGradleAdditions>
<\-- 可选的基础build.gradle构建脚本添加内容 -->
<buildscriptGradleAdditions> </buildscriptGradleAdditions>
<\-- 可选的应用程序build.gradle添加内容 -->
<buildGradleAdditions> </buildGradleAdditions>
<\-- 在${sdk.dir}/tools/ant/build.xml导入之前,对生成的build.xml的可选添加内容 -->
<buildXmlPropertyAdditions> </buildXmlPropertyAdditions>
<\-- 在ndk构建之前可以选择从Intermediate/Android/APK复制或删除的文件或目录 -->
<prebuildCopies> </prebuildCopies>
<\-- 在ndk构建之后可以选择从Intermediate/Android/APK复制或删除的文件或目录 -->
<resourceCopies> </resourceCopies>
<\-- 在Gradle之前可以选择从Intermediate/Android/APK复制或删除的文件或目录 -->
<gradleCopies> </gradleCopies>
<\-- 可以选择添加到gradle.properties的属性 -->
<gradleProperties> </gradleProperties>
<\-- 可以选择添加到Gradle命令行的参数(带有空格的前缀或将碰到前面的参数) -->
<gradleParameters> </gradleParameters>
<\-- 可选的必要最小SDK API级别 -->
<minimumSDKAPI> </minimumSDKAPI>
<\-- GameActivity.java中的GameActivity导入的可选添加内容 -->
<gameActivityImportAdditions> </gameActivityImportAdditions>
<\-- GameActivity.java中的导入后可以选择向GameActivity添加的内容 -->
<gameActivityPostImportAdditions> </gameActivityPostImportAdditions>
<\-- GameActivity.java中的GameActivity类实现的可选添加内容(每行以逗号结尾) -->
<gameActivityImplementsAdditions> </gameActivityImplementsAdditions>
<\-- GameActivity.java中GameActivity类主体的可选添加内容 -->
<gameActivityClassAdditions> </gameActivityOnClassAdditions>
<\-- GameActivity.java中GameActivity onCreate元数据读取的可选添加内容 -->
<gameActivityReadMetadata> </gameActivityReadMetadata>
<\-- GameActivity.java中GameActivity onCreate的可选添加内容 -->
<gameActivityOnCreateAdditions> </gameActivityOnCreateAdditions>
<\-- GameActivity.java中GameActivity onDestroy的可选添加内容 -->
<gameActivityOnDestroyAdditions> </gameActivityOnDestroyAdditions>
<\-- GameActivity.java中GameActivity onStart的可选添加内容 -->
<gameActivityOnStartAdditions> </gameActivityOnStartAdditions>
<\-- GameActivity.java中GameActivity onStop的可选添加内容 -->
<gameActivityOnStopAdditions> </gameActivityOnStopAdditions>
<\-- GameActivity.java中GameActivity onPause的可选添加内容 -->
<gameActivityOnPauseAdditions> </gameActivityOnPauseAdditions>
<\-- GameActivity.java中GameActivity onResume的可选添加内容 -->
<gameActivityOnResumeAdditions> </gameActivityOnResumeAdditions>
<\-- GameActivity.java中GameActivity onNewIntent的可选添加内容 -->
<gameActivityOnNewIntentAdditions> </gameActivityOnNewIntentAdditions>
<\-- GameActivity.java中GameActivity onActivityResult的可选添加内容 -->
<gameActivityOnActivityResultAdditions> </gameActivityOnActivityResultAdditions>
<\-- GameActivity.java中加载的可选库(在libUE4.so之前) -->
<soLoadLibrary> </soLoadLibrary>
支持的节点
以下是支持节点的完整列表:
<isArch arch="">
<isDistribution>
<if> => <true> / <false>
<while condition="">
<return/>
<break/>
<continue/>
<log text=""/>
<insert> </insert>
<insertValue value=""/>
<replace find="" with""/>
<copyFile src="" dst=""/>
<copyDir src="" dst=""/>
<loadLibrary name="" failmsg=""/>
<setBool result="" value=""/>
<setBoolEnvVarDefined result="" value=""/>
<setBoolFrom result="" value=""/>
<setBoolFromProperty result="" ini="" section="" property="" default=""/>
<setBoolFromPropertyContains result="" ini="" section="" property="" contains=""/>
<setBoolNot result="" source=""/>
<setBoolAnd result="" arg1="" arg2=""/>
<setBoolOr result="" arg1="" arg2=""/>
<setBoolIsEqual result="" arg1="" arg2=""/>
<setBoolIsLess result="" arg1="" arg2=""/>
<setBoolIsLessEqual result="" arg1="" arg2=""/>
<setBoolIsGreater result="" arg1="" arg2=""/>
<setBoolIsGreaterEqual result="" arg1="" arg2=""/>
<setInt result="" value=""/>
<setIntFrom result="" value=""/>
<setIntFromProperty result="" ini="" section="" property="" default=""/>
<setIntAdd result="" arg1="" arg2=""/>
<setIntSubtract result="" arg1="" arg2=""/>
<setIntMultiply result="" arg1="" arg2=""/>
<setIntDivide result="" arg1="" arg2=""/>
<setIntLength result="" source=""/>
<setIntFindString result="" source="" find=""/>
<setString result="" value=""/>
<setStringFrom result="" value=""/>
<setStringFromEnvVar result="" value=""/>
<setStringFromProperty result="" ini="" section="" property="" default=""/>
<setStringAdd result="" arg1="" arg2=""/>
<setStringSubstring result="" source="" index="" length=""/>
<setStringReplace result="" source="" find="" with=""/>