@@ -9,20 +9,20 @@ import {
99 ReadFileContents ,
1010 ResolveGlobToPath
1111} from './utilities' ;
12+ import { satisfies } from 'semver' ;
1213
1314const logger = Logger . instance ;
1415
1516/**
1617 * Checks if the required Android SDK is installed for the given Unity Editor and Project.
17- * @param editorPath The path to the Unity Editor executable .
18+ * @param editor The UnityEditor instance .
1819 * @param projectPath The path to the Unity project.
1920 * @returns A promise that resolves when the check is complete.
2021 */
21- export async function CheckAndroidSdkInstalled ( editorPath : string , projectPath : string ) : Promise < void > {
22- logger . ci ( `Checking Android SDK installation for:\n > Editor: ${ editorPath } \n > Project: ${ projectPath } ` ) ;
22+ export async function CheckAndroidSdkInstalled ( editor : UnityEditor , projectPath : string ) : Promise < void > {
23+ logger . ci ( `Checking Android SDK installation for:\n > Editor: ${ editor . editorRootPath } \n > Project: ${ projectPath } ` ) ;
2324 let sdkPath = undefined ;
2425 await createRepositoryCfg ( ) ;
25- const rootEditorPath = UnityEditor . GetEditorRootPath ( editorPath ) ;
2626 const projectSettingsPath = path . join ( projectPath , 'ProjectSettings/ProjectSettings.asset' ) ;
2727 const projectSettingsContent = await ReadFileContents ( projectSettingsPath ) ;
2828 const matchResult = projectSettingsContent . match ( / (?< = A n d r o i d T a r g e t S d k V e r s i o n : ) \d + / ) ;
@@ -31,23 +31,23 @@ export async function CheckAndroidSdkInstalled(editorPath: string, projectPath:
3131
3232 if ( androidTargetSdk === undefined || androidTargetSdk === 0 ) { return ; }
3333
34- sdkPath = await getAndroidSdkPath ( rootEditorPath , androidTargetSdk ) ;
34+ sdkPath = await getAndroidSdkPath ( editor , androidTargetSdk ) ;
3535
3636 if ( sdkPath ) {
3737 logger . ci ( `Target Android SDK android-${ androidTargetSdk } Installed in:\n > "${ sdkPath } "` ) ;
3838 return ;
3939 }
4040
4141 logger . info ( `Installing Android Target SDK:\n > android-${ androidTargetSdk } ` ) ;
42- const sdkManagerPath = await getSdkManager ( rootEditorPath ) ;
43- const javaSdk = await getJDKPath ( rootEditorPath ) ;
42+ const sdkManagerPath = await getSdkManager ( editor ) ;
43+ const javaSdk = await getJDKPath ( editor ) ;
4444 await execSdkManager ( sdkManagerPath , javaSdk , [ '--licenses' ] ) ;
4545 await execSdkManager ( sdkManagerPath , javaSdk , [ '--update' ] ) ;
4646 await execSdkManager ( sdkManagerPath , javaSdk , [ 'platform-tools' , `platforms;android-${ androidTargetSdk } ` ] ) ;
47- sdkPath = await getAndroidSdkPath ( rootEditorPath , androidTargetSdk ) ;
47+ sdkPath = await getAndroidSdkPath ( editor , androidTargetSdk ) ;
4848
4949 if ( ! sdkPath ) {
50- throw new Error ( `Failed to install android-${ androidTargetSdk } in ${ rootEditorPath } ` ) ;
50+ throw new Error ( `Failed to install android-${ androidTargetSdk } in ${ editor . editorRootPath } ` ) ;
5151 }
5252
5353 logger . ci ( `Target Android SDK Installed in:\n > "${ sdkPath } "` ) ;
@@ -61,31 +61,79 @@ async function createRepositoryCfg(): Promise<void> {
6161 await fileHandle . close ( ) ;
6262}
6363
64- async function getJDKPath ( rootEditorPath : string ) : Promise < string > {
65- const jdkPath = await ResolveGlobToPath ( [ rootEditorPath , '**' , 'AndroidPlayer' , 'OpenJDK' ] ) ;
64+ async function getJDKPath ( editor : UnityEditor ) : Promise < string > {
65+ let jdkPath : string | undefined = undefined ;
6666
67- if ( ! jdkPath ) {
68- throw new Error ( `Failed to resolve OpenJDK in ${ rootEditorPath } ` ) ;
67+ if ( editor . version . isGreaterThanOrEqualTo ( '2019.0.0' ) ) {
68+ logger . debug ( 'Using JDK bundled with Unity 2019+' ) ;
69+ jdkPath = await ResolveGlobToPath ( [ editor . editorRootPath , '**' , 'AndroidPlayer' , 'OpenJDK/' ] ) ;
70+
71+ if ( ! jdkPath ) {
72+ throw new Error ( `Failed to resolve OpenJDK in ${ editor . editorRootPath } ` ) ;
73+ }
74+ } else {
75+ logger . debug ( 'Using system JDK for Unity versions prior to 2019' ) ;
76+ jdkPath = process . env . JAVA_HOME || process . env . JDK_HOME ;
77+
78+ if ( ! jdkPath ) {
79+ throw new Error ( 'JDK installation not found: No system JAVA_HOME or JDK_HOME defined' ) ;
80+ }
6981 }
7082
7183 await fs . promises . access ( jdkPath , fs . constants . R_OK ) ;
7284 logger . ci ( `jdkPath:\n > "${ jdkPath } "` ) ;
7385 return jdkPath ;
7486}
7587
76- async function getSdkManager ( rootEditorPath : string ) : Promise < string > {
88+ async function getSdkManager ( editor : UnityEditor ) : Promise < string > {
7789 let globPath : string [ ] = [ ] ;
78- switch ( process . platform ) {
79- case 'darwin' :
80- case 'linux' :
81- globPath = [ rootEditorPath , '**' , 'AndroidPlayer' , '**' , 'sdkmanager' ] ;
82- break ;
83- case 'win32' :
84- globPath = [ rootEditorPath , '**' , 'AndroidPlayer' , '**' , 'sdkmanager.bat' ] ;
85- break ;
86- default :
87- throw new Error ( `Unsupported platform: ${ process . platform } ` ) ;
90+ if ( editor . version . range ( '>=2019.0.0 <2021.0.0' ) ) {
91+ logger . debug ( 'Using sdkmanager bundled with Unity 2019 and 2020' ) ;
92+ switch ( process . platform ) {
93+ case 'darwin' :
94+ case 'linux' :
95+ globPath = [ editor . editorRootPath , '**' , 'AndroidPlayer' , '**' , 'sdkmanager' ] ;
96+ break ;
97+ case 'win32' :
98+ globPath = [ editor . editorRootPath , '**' , 'AndroidPlayer' , '**' , 'sdkmanager.bat' ] ;
99+ break ;
100+ default :
101+ throw new Error ( `Unsupported platform: ${ process . platform } ` ) ;
102+ }
103+ } else if ( editor . version . range ( '>=2021.0.0' ) ) {
104+ logger . debug ( 'Using cmdline-tools sdkmanager bundled with Unity 2021+' ) ;
105+ switch ( process . platform ) {
106+ case 'darwin' :
107+ case 'linux' :
108+ globPath = [ editor . editorRootPath , '**' , 'AndroidPlayer' , '**' , 'cmdline-tools' , '**' , 'sdkmanager' ] ;
109+ break ;
110+ case 'win32' :
111+ globPath = [ editor . editorRootPath , '**' , 'AndroidPlayer' , '**' , 'cmdline-tools' , '**' , 'sdkmanager.bat' ] ;
112+ break ;
113+ default :
114+ throw new Error ( `Unsupported platform: ${ process . platform } ` ) ;
115+ }
116+ } else {
117+ logger . debug ( 'Using system sdkmanager' ) ;
118+ const systemSdkPath = process . env . ANDROID_SDK_ROOT || process . env . ANDROID_HOME ;
119+
120+ if ( ! systemSdkPath ) {
121+ throw new Error ( 'Android installation not found: No system ANDROID_SDK_ROOT or ANDROID_HOME defined' ) ;
122+ }
123+
124+ switch ( process . platform ) {
125+ case 'darwin' :
126+ case 'linux' :
127+ globPath = [ systemSdkPath , 'cmdline-tools' , 'latest' , 'bin' , 'sdkmanager' ] ;
128+ break ;
129+ case 'win32' :
130+ globPath = [ systemSdkPath , 'cmdline-tools' , 'latest' , 'bin' , 'sdkmanager.bat' ] ;
131+ break ;
132+ default :
133+ throw new Error ( `Unsupported platform: ${ process . platform } ` ) ;
134+ }
88135 }
136+
89137 const sdkmanagerPath = await ResolveGlobToPath ( globPath ) ;
90138
91139 if ( ! sdkmanagerPath ) {
@@ -97,19 +145,36 @@ async function getSdkManager(rootEditorPath: string): Promise<string> {
97145 return sdkmanagerPath ;
98146}
99147
100- async function getAndroidSdkPath ( rootEditorPath : string , androidTargetSdk : number ) : Promise < string | undefined > {
101- logger . ci ( `Attempting to locate Android SDK Path...\n > editorPath : ${ rootEditorPath } \n > androidTargetSdk: ${ androidTargetSdk } ` ) ;
148+ async function getAndroidSdkPath ( editor : UnityEditor , androidTargetSdk : number ) : Promise < string | undefined > {
149+ logger . ci ( `Attempting to locate Android SDK Path...\n > editorRootPath : ${ editor . editorRootPath } \n > androidTargetSdk: ${ androidTargetSdk } ` ) ;
102150 let sdkPath : string ;
103151
104- try {
105- sdkPath = await ResolveGlobToPath ( [ rootEditorPath , '**' , 'PlaybackEngines' , 'AndroidPlayer' , 'SDK' , 'platforms' , `android-${ androidTargetSdk } /` ] ) ;
106- await fs . promises . access ( sdkPath , fs . constants . R_OK ) ;
107- } catch ( error ) {
108- logger . debug ( `android-${ androidTargetSdk } not installed` ) ;
109- return undefined ;
152+ // if 2019+ test editor path, else use system android installation
153+ if ( editor . version . isGreaterThanOrEqualTo ( '2019.0.0' ) ) {
154+ logger . debug ( 'Using Android SDK bundled with Unity 2019+' ) ;
155+ try {
156+ sdkPath = await ResolveGlobToPath ( [ editor . editorRootPath , '**' , 'PlaybackEngines' , 'AndroidPlayer' , 'SDK' , 'platforms' , `android-${ androidTargetSdk } /` ] ) ;
157+ } catch ( error ) {
158+ logger . debug ( `android-${ androidTargetSdk } not installed` ) ;
159+ return undefined ;
160+ }
161+ } else { // fall back to system android installation
162+ logger . debug ( 'Using system Android SDK for Unity versions prior to 2019' ) ;
163+ try {
164+ const systemSdkPath = process . env . ANDROID_SDK_ROOT || process . env . ANDROID_HOME ;
165+
166+ if ( ! systemSdkPath ) {
167+ logger . debug ( 'Android installation not found: No system ANDROID_SDK_ROOT or ANDROID_HOME defined' ) ;
168+ return undefined ;
169+ }
170+
171+ sdkPath = await ResolveGlobToPath ( [ systemSdkPath , 'platforms' , `android-${ androidTargetSdk } /` ] ) ;
172+ } catch ( error ) {
173+ logger . debug ( `android-${ androidTargetSdk } not installed` ) ;
174+ return undefined ;
175+ }
110176 }
111177
112- logger . ci ( `Android sdkPath:\n > "${ sdkPath } "` ) ;
113178 return sdkPath ;
114179}
115180
@@ -123,25 +188,27 @@ async function execSdkManager(sdkManagerPath: string, javaPath: string, args: st
123188 fs . accessSync ( sdkManagerPath , fs . constants . R_OK | fs . constants . X_OK ) ;
124189 }
125190
191+ if ( process . platform === 'win32' && ! await isProcessElevated ( ) ) {
192+ throw new Error ( 'Android SDK installation requires elevated (administrator) privileges. Please rerun as Administrator.' ) ;
193+ }
194+
126195 try {
127- exitCode = await new Promise < number > ( ( resolve , reject ) => {
196+ exitCode = await new Promise < number > ( async ( resolve , reject ) => {
197+ let cmdEnv = { ...process . env } ;
198+ cmdEnv . JAVA_HOME = javaPath ;
199+ cmdEnv . JDK_HOME = javaPath ;
200+ cmdEnv . SKIP_JDK_VERSION_CHECK = 'true' ;
128201 let cmd = sdkManagerPath ;
129202 let cmdArgs = args ;
130203
131204 if ( process . platform === 'win32' ) {
132- if ( ! isProcessElevated ( ) ) {
133- throw new Error ( 'Android SDK installation requires elevated (administrator) privileges. Please rerun as Administrator.' ) ;
134- }
135-
136205 cmd = 'cmd.exe' ;
137206 cmdArgs = [ '/c' , sdkManagerPath , ...args ] ;
138207 }
139208
140209 const child = spawn ( cmd , cmdArgs , {
141210 stdio : [ 'pipe' , 'pipe' , 'pipe' ] ,
142- env : {
143- JAVA_HOME : process . platform === 'win32' ? `"${ javaPath } "` : javaPath
144- }
211+ env : cmdEnv
145212 } ) ;
146213 const sigintHandler = ( ) => child . kill ( 'SIGINT' ) ;
147214 const sigtermHandler = ( ) => child . kill ( 'SIGTERM' ) ;
0 commit comments