Skip to content

Commit f5fcb9f

Browse files
[Xamarin.Android.Tools.AndroidSdk] Add support for cmdline-tools (#83)
Context: https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1109288 Context: https://dl.google.com/android/repository/repository2-1.xml Google has deprecated the `tools` Android SDK package, replacing it with the `cmdline-tools` package, which introduces a version directory component. Old and busted: $(AndroidSdkDirectory)/tools/bin/sdkmanager New hotness: $(AndroidSdkDirectory)/cmdline-tools/latest/bin/sdkmanager Of particular interest is that `latest` is a *literal value*. There is also a `cmdline-tools;1.0` package which creates a `cmdline-tools/1.0` directory. Add a new `AndroidSdkInfo.GetCommandLineToolsPaths()` method which returns the "command-line tools paths", ordered by version and (non-) obsolescence. For example, given the directory structure: * `$(AndroidSdkDirectory)/tools/bin/sdkmanager` * `$(AndroidSdkDirectory)/cmdline-tools/1.0/bin/sdkmanager` * `$(AndroidSdkDirectory)/cmdline-tools/latest/bin/sdkmanager` Then `AndroidSdkInfo.GetCommandLineToolsPaths()` will return, in this order: * `$(AndroidSdkDirectory)/cmdline-tools/latest` * `$(AndroidSdkDirectory)/cmdline-tools/1.0` * `$(AndroidSdkDirectory)/tools` The `latest` version is always preferred, if present, followed by any actually versioned cmdline-tools directories, followed by the `tools` directory, if it exists. Note that "prefixes" are returned. All utilities are within a nested `bin` directory, so if you want e.g. the latest `sdkmanager` util, you would want to do: var info = new AndroidSdkInfo (path); var latestSdkManager = Path.Combine ( info.GetCommandLineToolsPaths ().First (), "bin", "sdkmanager"); Finally, remove some unnecessary members from `AndroidSdkBase` which were never used -- and thus are "noise" -- and don't make sense in the new `cmdline-tools` world, as the cmdline-tools package doesn't contain them…
1 parent f473ff9 commit f5fcb9f

File tree

3 files changed

+141
-25
lines changed

3 files changed

+141
-25
lines changed

src/Xamarin.Android.Tools.AndroidSdk/AndroidSdkInfo.cs

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,7 @@ public IEnumerable<string> GetBuildToolsPaths ()
5252
foreach (var d in preview)
5353
yield return d;
5454

55-
var sorted = from p in Directory.EnumerateDirectories (buildTools)
56-
let version = TryParseVersion (Path.GetFileName (p))
57-
where version != null
58-
orderby version descending
59-
select p;
55+
var sorted = SortedSubdirectoriesByVersion (buildTools);
6056

6157
foreach (var d in sorted)
6258
yield return d;
@@ -66,6 +62,15 @@ orderby version descending
6662
yield return ptPath;
6763
}
6864

65+
static IEnumerable<string> SortedSubdirectoriesByVersion (string dir)
66+
{
67+
return from p in Directory.EnumerateDirectories (dir)
68+
let version = TryParseVersion (Path.GetFileName (p))
69+
where version != null
70+
orderby version descending
71+
select p;
72+
}
73+
6974
static Version TryParseVersion (string v)
7075
{
7176
Version version;
@@ -185,5 +190,40 @@ public static void DetectAndSetPreferredJavaSdkPathToLatest (Action<TraceLevel,
185190
var sdk = CreateSdk (logger);
186191
sdk.SetPreferredJavaSdkPath (latestJdk.HomePath);
187192
}
193+
194+
public string TryGetCommandLineToolsPath ()
195+
{
196+
return GetCommandLineToolsPaths ("latest").FirstOrDefault ();
197+
}
198+
199+
public IEnumerable<string> GetCommandLineToolsPaths (string preferredCommandLineToolsVersion)
200+
{
201+
if (!string.IsNullOrEmpty (preferredCommandLineToolsVersion)) {
202+
var preferredDir = Path.Combine (AndroidSdkPath, "cmdline-tools", preferredCommandLineToolsVersion);
203+
if (Directory.Exists (preferredDir))
204+
return new[] { preferredDir }.Concat (GetCommandLineToolsPaths ().Where (p => p != preferredDir));
205+
}
206+
return GetCommandLineToolsPaths ();
207+
}
208+
209+
public IEnumerable<string> GetCommandLineToolsPaths ()
210+
{
211+
var cmdlineToolsDir = Path.Combine (AndroidSdkPath, "cmdline-tools");
212+
if (Directory.Exists (cmdlineToolsDir)) {
213+
var latestDir = Path.Combine (cmdlineToolsDir, "latest");
214+
if (Directory.Exists (latestDir))
215+
yield return latestDir;
216+
foreach (var d in SortedSubdirectoriesByVersion (cmdlineToolsDir)) {
217+
var version = Path.GetFileName (d);
218+
if (version == "latest")
219+
continue;
220+
yield return d;
221+
}
222+
}
223+
var toolsDir = Path.Combine (AndroidSdkPath, "tools");
224+
if (Directory.Exists (toolsDir)) {
225+
yield return toolsDir;
226+
}
227+
}
188228
}
189229
}

src/Xamarin.Android.Tools.AndroidSdk/Sdks/AndroidSdkBase.cs

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,10 @@ public AndroidSdkBase (Action<TraceLevel, string> logger)
3737
public string AndroidNdkPath { get; private set; }
3838
public string JavaSdkPath { get; private set; }
3939
public string JavaBinPath { get; private set; }
40-
public string AndroidToolsPath { get; private set; }
4140
public string AndroidPlatformToolsPath { get; private set; }
42-
public string AndroidToolsPathShort { get; private set; }
4341
public string AndroidPlatformToolsPathShort { get; private set; }
4442

4543
public virtual string Adb { get; protected set; } = "adb";
46-
public virtual string Android { get; protected set; } = "android";
47-
public virtual string Emulator { get; protected set; } = "emulator";
48-
public virtual string Monitor { get; protected set; } = "monitor";
4944
public virtual string ZipAlign { get; protected set; } = "zipalign";
5045
public virtual string JarSigner { get; protected set; } = "jarsigner";
5146
public virtual string KeyTool { get; protected set; } = "keytool";
@@ -76,13 +71,9 @@ public virtual void Initialize (string androidSdkPath = null, string androidNdkP
7671
}
7772

7873
if (!string.IsNullOrEmpty (AndroidSdkPath)) {
79-
AndroidToolsPath = Path.Combine (AndroidSdkPath, "tools");
80-
AndroidToolsPathShort = GetShortFormPath (AndroidToolsPath);
8174
AndroidPlatformToolsPath = Path.Combine (AndroidSdkPath, "platform-tools");
8275
AndroidPlatformToolsPathShort = GetShortFormPath (AndroidPlatformToolsPath);
8376
} else {
84-
AndroidToolsPath = null;
85-
AndroidToolsPathShort = null;
8677
AndroidPlatformToolsPath = null;
8778
AndroidPlatformToolsPathShort = null;
8879
}
@@ -98,9 +89,6 @@ public virtual void Initialize (string androidSdkPath = null, string androidNdkP
9889
// we need to look for extensions other than the default .exe|.bat
9990
// google have a habbit of changing them.
10091
Adb = GetExecutablePath (AndroidPlatformToolsPath, Adb);
101-
Android = GetExecutablePath (AndroidToolsPath, Android);
102-
Emulator = GetExecutablePath (AndroidToolsPath, Emulator);
103-
Monitor = GetExecutablePath (AndroidToolsPath, Monitor);
10492
NdkStack = GetExecutablePath (AndroidNdkPath, NdkStack);
10593
}
10694

tests/Xamarin.Android.Tools.AndroidSdk-Tests/AndroidSdkInfoTests.cs

Lines changed: 96 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -164,13 +164,74 @@ public void JdkDirectory_JavaHome ()
164164
}
165165
}
166166

167+
[Test]
168+
public void Sdk_GetCommandLineToolsPaths ()
169+
{
170+
CreateSdks(out string root, out string jdk, out string ndk, out string sdk);
171+
172+
var cmdlineTools = Path.Combine (sdk, "cmdline-tools");
173+
var latestToolsVersion = "latest";
174+
var toolsVersion = "2.1";
175+
var higherToolsVersion = "11.2";
176+
177+
void recreateCmdlineToolsDirectory () {
178+
Directory.Delete (cmdlineTools, recursive: true);
179+
Directory.CreateDirectory (cmdlineTools);
180+
}
181+
182+
try {
183+
var info = new AndroidSdkInfo (androidSdkPath: sdk);
184+
185+
// Test cmdline-tools path
186+
recreateCmdlineToolsDirectory();
187+
CreateFauxAndroidSdkToolsDirectory (sdk, createToolsDir: true, toolsVersion: toolsVersion, createOldToolsDir: false);
188+
var toolsPaths = info.GetCommandLineToolsPaths ();
189+
190+
Assert.AreEqual (toolsPaths.Count (), 1, "Incorrect number of elements");
191+
Assert.AreEqual (toolsPaths.First (), Path.Combine (sdk, "cmdline-tools", toolsVersion), "Incorrect command line tools path");
192+
193+
// Test that cmdline-tools is preferred over tools
194+
recreateCmdlineToolsDirectory();
195+
CreateFauxAndroidSdkToolsDirectory (sdk, createToolsDir: true, toolsVersion: latestToolsVersion, createOldToolsDir: true);
196+
toolsPaths = info.GetCommandLineToolsPaths ();
197+
198+
Assert.AreEqual (toolsPaths.Count (), 2, "Incorrect number of elements");
199+
Assert.AreEqual (toolsPaths.First (), Path.Combine (sdk, "cmdline-tools", latestToolsVersion), "Incorrect command line tools path");
200+
Assert.AreEqual (toolsPaths.Last (), Path.Combine (sdk, "tools"), "Incorrect tools path");
201+
202+
// Test sorting
203+
recreateCmdlineToolsDirectory ();
204+
CreateFauxAndroidSdkToolsDirectory (sdk, createToolsDir: true, toolsVersion: latestToolsVersion, createOldToolsDir: false);
205+
CreateFauxAndroidSdkToolsDirectory (sdk, createToolsDir: true, toolsVersion: toolsVersion, createOldToolsDir: false);
206+
CreateFauxAndroidSdkToolsDirectory (sdk, createToolsDir: true, toolsVersion: higherToolsVersion, createOldToolsDir: true);
207+
toolsPaths = info.GetCommandLineToolsPaths ();
208+
209+
var toolsPathsList = toolsPaths.ToList ();
210+
Assert.AreEqual (toolsPaths.Count (), 4, "Incorrect number of elements");
211+
bool isOrderCorrect = toolsPathsList [0].Equals (Path.Combine (sdk, "cmdline-tools", latestToolsVersion), StringComparison.Ordinal)
212+
&& toolsPathsList [1].Equals (Path.Combine (sdk, "cmdline-tools", higherToolsVersion), StringComparison.Ordinal)
213+
&& toolsPathsList [2].Equals (Path.Combine (sdk, "cmdline-tools", toolsVersion), StringComparison.Ordinal)
214+
&& toolsPathsList [3].Equals (Path.Combine (sdk, "tools"), StringComparison.Ordinal);
215+
216+
Assert.IsTrue (isOrderCorrect, "Tools order is not descending");
217+
} finally {
218+
Directory.Delete (root, recursive: true);
219+
}
220+
}
221+
167222
static bool IsWindows => OS.IsWindows;
168223

169-
static void CreateSdks (out string root, out string jdk, out string ndk, out string sdk)
224+
static string CreateRoot ()
170225
{
171-
root = Path.GetTempFileName ();
226+
var root = Path.GetTempFileName ();
172227
File.Delete (root);
173228
Directory.CreateDirectory (root);
229+
return root;
230+
}
231+
232+
static void CreateSdks (out string root, out string jdk, out string ndk, out string sdk)
233+
{
234+
root = CreateRoot ();
174235

175236
ndk = Path.Combine (root, "ndk");
176237
sdk = Path.Combine (root, "sdk");
@@ -185,25 +246,52 @@ static void CreateSdks (out string root, out string jdk, out string ndk, out str
185246
CreateFauxJavaSdkDirectory (jdk, "1.8.0", out var _, out var _);
186247
}
187248

188-
static void CreateFauxAndroidSdkDirectory (string androidSdkDirectory, string buildToolsVersion, ApiInfo [] apiLevels = null)
249+
static void CreateFauxAndroidSdkToolsDirectory (string androidSdkDirectory, bool createToolsDir, string toolsVersion, bool createOldToolsDir)
250+
{
251+
if (createToolsDir) {
252+
string androidSdkToolsPath = Path.Combine (androidSdkDirectory, "cmdline-tools", toolsVersion ?? "1.0");
253+
string androidSdkToolsBinPath = Path.Combine (androidSdkToolsPath, "bin");
254+
255+
Directory.CreateDirectory (androidSdkToolsPath);
256+
Directory.CreateDirectory (androidSdkToolsBinPath);
257+
258+
File.WriteAllText (Path.Combine (androidSdkToolsBinPath, IsWindows ? "lint.bat" : "lint"), "");
259+
}
260+
261+
if (createOldToolsDir) {
262+
string androidSdkToolsPath = Path.Combine (androidSdkDirectory, "tools");
263+
string androidSdkToolsBinPath = Path.Combine (androidSdkToolsPath, "bin");
264+
265+
Directory.CreateDirectory (androidSdkToolsPath);
266+
Directory.CreateDirectory (androidSdkToolsBinPath);
267+
268+
File.WriteAllText (Path.Combine (androidSdkToolsBinPath, IsWindows ? "lint.bat" : "lint"), "");
269+
}
270+
271+
}
272+
273+
static void CreateFauxAndroidSdkDirectory (
274+
string androidSdkDirectory,
275+
string buildToolsVersion,
276+
bool createToolsDir = true,
277+
string toolsVersion = null,
278+
bool createOldToolsDir = false,
279+
ApiInfo[] apiLevels = null)
189280
{
190-
var androidSdkToolsPath = Path.Combine (androidSdkDirectory, "tools");
191-
var androidSdkBinPath = Path.Combine (androidSdkToolsPath, "bin");
281+
CreateFauxAndroidSdkToolsDirectory (androidSdkDirectory, createToolsDir, toolsVersion, createOldToolsDir);
282+
192283
var androidSdkPlatformToolsPath = Path.Combine (androidSdkDirectory, "platform-tools");
193284
var androidSdkPlatformsPath = Path.Combine (androidSdkDirectory, "platforms");
194285
var androidSdkBuildToolsPath = Path.Combine (androidSdkDirectory, "build-tools", buildToolsVersion);
195286

196287
Directory.CreateDirectory (androidSdkDirectory);
197-
Directory.CreateDirectory (androidSdkToolsPath);
198-
Directory.CreateDirectory (androidSdkBinPath);
199288
Directory.CreateDirectory (androidSdkPlatformToolsPath);
200289
Directory.CreateDirectory (androidSdkPlatformsPath);
201290
Directory.CreateDirectory (androidSdkBuildToolsPath);
202291

203292
File.WriteAllText (Path.Combine (androidSdkPlatformToolsPath, IsWindows ? "adb.exe" : "adb"), "");
204293
File.WriteAllText (Path.Combine (androidSdkBuildToolsPath, IsWindows ? "zipalign.exe" : "zipalign"), "");
205294
File.WriteAllText (Path.Combine (androidSdkBuildToolsPath, IsWindows ? "aapt.exe" : "aapt"), "");
206-
File.WriteAllText (Path.Combine (androidSdkToolsPath, IsWindows ? "lint.bat" : "lint"), "");
207295

208296
List<ApiInfo> defaults = new List<ApiInfo> ();
209297
for (int i = 10; i < 26; i++) {

0 commit comments

Comments
 (0)