언리얼 플러그인 언어(Unreal Plugin Language,UPL) 는 XML 조작 및 문자열 반환에 사용하는 간단한 XML 기반 언어입니다. 다른 모든 섹션보다 먼저 아키텍처당 한 번 평가되는 섹션이 포함되어 있습니다. 상태는 유지되고 다음 섹션으로 전달되어 평가되므로 섹션이 실행되는 순서가 중요합니다. UPL은 XML 수정 및 쿼리에 일반적으로 사용하는 시스템이며, 주로 플러그인이 속한 패키지의 글로벌 환경설정에 플러그인이 영향을 미칠 수 있도록 하는 데 사용됩니다. 예를 들어 UPL을 사용하면 플러그인에서 Android APK AndroidManfiest.xml 파일 또는 IOS IPA plist 파일을 수정할 수 있습니다. UBT는 또한 Android의 일부 .java 파일처럼 패키지에 대해 공통되어야 하는 파일에 포함할 문자열에 플러그인의 UPL xml 파일을 쿼리합니다.
트레이싱 활성화
인스트럭션이 플러그인 컨텍스트에서 실행되는지 확인해야 한다면, 다음을 추가하여 트레이싱을 활성화합니다.
<trace enable="true"/>
이 인스트럭션 이후 컨텍스트에서 실행되는 모든 노드는
<dumpvars/>
변수 타입
Bool, Int, String 변수 타입이 지원됩니다. 어떤 어트리뷰트든 변수를 참조할 수 있으며, 이 구문을 통해 평가 전에 같은 문자열로 바뀝니다.
$B(name) = boolean 변수 'name'의 값
$I(name) = integer 변수 'name'의 값
$S(name) = string 변수 'name'의 값
$E(name) = element 변수 'name'의 값
다음 변수는 자동으로 초기화됩니다.
$S(Output) = 섹션 평가를 위해 반환된 출력(Input으로 초기화됨)
$S(Architecture) = 타깃 아키텍처(armeabi-armv7a, arm64-v8a, x86, x86_64)
$S(PluginDir) = XML 파일이 로드되었던 디렉터리
$S(EngineDir) = 엔진 디렉터리
$S(BuildDir) = 프로젝트의 플랫폼에 해당하는 (Intermediate 폴더 내) 빌드 디렉터리
$S(Configuration) = 환경설정 유형(Debug, DebugGame, Development, Test, Shipping)
$B(Distribution) = 배포 빌드인 경우 true
위의 변수를 제외하면 전부 플러그인의 컨텍스트에 있어 네임스페이스 충돌을 방지합니다. 위에서 Output을 제외한 변수를 새 값으로 설정하려 하면 현재 컨텍스트에만 영향을 미칩니다.
변수 조작
다음 노드는 변수의 조작을 가능하게 합니다.
<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%"/>
연산자 적용의 결과로 boolean 변수가 설정될 수도 있습니다.
<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=""/>
Integer 변수에서는 이러한 산술 연산을 사용할 수 있습니다.
<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=""/>
다음을 통해 string 길이를 얻을 수 있습니다.
<setIntLength result="" source=""/>
검색 string의 인덱스는 다음을 사용하여 소스에서 찾을 수 있습니다.
<setIntFindString result="" source="" find=""/>
<setBoolStartsWith result="" source="" find=""/>
<setBoolEndsWith result="" source="" find=""/>
<setBoolContains result="" source="" find=""/>
메시지 작성
이 노드를 사용하면 로그에 메시지가 작성됩니다.
<log text=""/>
조건식 실행
조건식 실행은 다음 양식을 사용합니다.
<if condition="">
<true>
<-- Executes if boolean variable if the condition is true -->
</true>
<false>
<-- executes if boolean variable in condition is false -->
</false>
</if>
<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>
<-- do something for Intel if not a distribution build -->
</true>
</if>
"isIntel"은 다음과 같이 수행될 수 있습니다.
<setStringSubstring result="subarch" source="$S(Architecture)" start="0" length="3"/>
<setBoolEquals result="isIntel" arg1="$S(subarch)" arg2="x86"/>
두 개의 shortcut 노드를 조건식 실행에 사용할 수 있습니다. 다음 코드는
<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>
바디 외부의 <break/>
는 <return></return>
과 동일한 역할을 합니다.
1~5를 로그에 쓰고 3을 건너뛰는 루프의 예시입니다. continue 이전에 while 조건의 업데이트가 이루어져야 합니다. 그렇지 않다면 루프가 종료되지 않습니다.
<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>
result 변수 이름을 생성할 때 변수 대체를 사용할 수 있습니다. 이를 통해 루프에서 배열을 생성할 수 있게 됩니다.
<setString result="array_$I(index)" value="element $I(index) in array"/>
다음을 사용하여 얻을 수 있습니다. value는 변수 이름으로 취급됩니다.
<setStringFrom result="out" value="array_$I(index)"/>
result 변수 이름을 생성할 때 변수 대체를 사용할 수 있습니다. 이를 통해 루프에서 배열을 생성할 수 있게 됩니다.
<setString result="array_$I(index)" value="element $I(index) in array"/>
다음을 사용하여 얻을 수 있습니다. value는 변수 이름으로 취급됩니다.
<setStringFrom result="out" value="array_$I(index)"/>
boolean과 integer 타입의 경우
텍스트 삽입
섹션에 텍스트를 삽입하는 노드는 다음과 같습니다.
<insert> body </insert>
<insertNewline/>
<insertValue value=""/>
<loadLibrary name="" failmsg=""/>
첫 번째는 텍스트 또는 노드를 반환된 섹션 문자열에 삽입합니다. 다음 경우에는 반드시 이스케이프 문자를 사용해야 합니다.
< = <
> = >
& = &
<insertValue value=""> /pre>
- 삽입 이전 값의 변수를 평가합니다. 값에 따옴표(")가 포함된 경우 따옴표를 사용하여 이스케이프해야 합니다.
<loadLibrary name="" failmsg="">
- 케이스를 로드하는 데 실패했을 때 선택적으로 로깅된 메시지를 포함한 system.LoadLibrary try/catch 블록을 삽입하는 shortcut입니다.
검색 및 대체
Output에서 다음을 사용하여 검색 및 대체를 수행할 수 있습니다.
<replace find="" with=""/>
$S(Output)를 직접 조작할 수도 있지만 위가 훨씬 효율적입니다.
<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=""> body </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="$"로 참조됩니다. $E(varname) 사용이 XML과 동일한 문자열로 확장되므로 Element 변수는 $varname으로 참조됩니다. 기본적으로 addElement, addElements, removeElement 는 일치하는 모든 태그에 적용됩니다. 일치하는 첫 번째 태그에만 적용되도록 once="true" 어트리뷰트가 추가될 수 있습니다.
<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에서 파일을 제거하는 것으로만 제한됩니다. 다음은 assets 디렉터리에서 Oculus Signature 파일(osig)을 제거하는 예시입니다.
<deleteFiles filespec="assets/oculussig_*"/>
패키징 또는 배포
다음 섹션은 패키징 또는 배포 스테이지 도중 평가됩니다.
모든 플랫폼
<-- init section is always evaluated once per architecture -->
<init> </init>
Android 섹션
<-- optional updates applied to AndroidManifest.xml -->
<androidManifestUpdates> </androidManifestUpdates>
<-- optional additions to proguard -->
<proguardAdditions> </proguardAdditions>
<-- optional AAR imports additions -->
<AARImports> </AARImports>
<-- optional base build.gradle additions -->
<baseBuildGradleAdditions> </baseBuildGradleAdditions>
<-- optional base build.gradle buildscript additions -->
<buildscriptGradleAdditions> </buildscriptGradleAdditions>
<-- optional app build.gradle additions -->
<buildGradleAdditions> </buildGradleAdditions>
<-- optional additions to generated build.xml before ${sdk.dir}/tools/ant/build.xml import -->
<buildXmlPropertyAdditions> </buildXmlPropertyAdditions>
<-- optional files or directories to copy or delete from Intermediate/Android/APK before ndk-build -->
<prebuildCopies> </prebuildCopies>
<-- optional files or directories to copy or delete from Intermediate/Android/APK after ndk-build -->
<resourceCopies> </resourceCopies>
<-- optional files or directories to copy or delete from Intermediate/Android/APK before Gradle -->
<gradleCopies> </gradleCopies>
<-- optional properties to add to gradle.properties -->
<gradleProperties> </gradleProperties>
<-- optional parameters to add to Gradle commandline (prefix with a space or will run into previous parameter(s)) -->
<gradleParameters> </gradleParameters>
<-- optional minimum SDK API level required -->
<minimumSDKAPI> </minimumSDKAPI>
<-- optional additions to the GameActivity imports in GameActivity.java -->
<gameActivityImportAdditions> </gameActivityImportAdditions>
<-- optional additions to the GameActivity after imports in GameActivity.java -->
<gameActivityPostImportAdditions> </gameActivityPostImportAdditions>
<-- optional additions to the GameActivity class implements in GameActivity.java (end each line with a comma) -->
<gameActivityImplementsAdditions> </gameActivityImplementsAdditions>
<-- optional additions to the GameActivity class body in GameActivity.java -->
<gameActivityClassAdditions> </gameActivityOnClassAdditions>
<-- optional additions to GameActivity onCreate metadata reading in GameActivity.java -->
<gameActivityReadMetadata> </gameActivityReadMetadata>
<-- optional additions to GameActivity onCreate in GameActivity.java -->
<gameActivityOnCreateAdditions> </gameActivityOnCreateAdditions>
<-- optional additions to GameActivity onDestroy in GameActivity.java -->
<gameActivityOnDestroyAdditions> </gameActivityOnDestroyAdditions>
<-- optional additions to GameActivity onStart in GameActivity.java -->
<gameActivityOnStartAdditions> </gameActivityOnStartAdditions>
<-- optional additions to GameActivity onStop in GameActivity.java -->
<gameActivityOnStopAdditions> </gameActivityOnStopAdditions>
<-- optional additions to GameActivity onPause in GameActivity.java -->
<gameActivityOnPauseAdditions> </gameActivityOnPauseAdditions>
<-- optional additions to GameActivity onResume in GameActivity.java -->
<gameActivityOnResumeAdditions> </gameActivityOnResumeAdditions>
<-- optional additions to GameActivity onNewIntent in GameActivity.java -->
<gameActivityOnNewIntentAdditions> </gameActivityOnNewIntentAdditions>
<-- optional additions to GameActivity onActivityResult in GameActivity.java -->
<gameActivityOnActivityResultAdditions> </gameActivityOnActivityResultAdditions>
<-- optional libraries to load in GameActivity.java before 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=""/>