2222import static com .android .tools .build .bundletool .model .utils .BundleParser .getModulesZip ;
2323import static com .android .tools .build .bundletool .model .utils .files .FilePreconditions .checkFileDoesNotExist ;
2424import static com .google .common .base .Preconditions .checkArgument ;
25+ import static com .google .common .base .Preconditions .checkState ;
2526
2627import com .android .tools .build .bundletool .androidtools .Aapt2Command ;
2728import com .android .tools .build .bundletool .commands .CommandHelp .CommandDescription ;
3233import com .android .tools .build .bundletool .model .ApkListener ;
3334import com .android .tools .build .bundletool .model .ApkModifier ;
3435import com .android .tools .build .bundletool .model .Password ;
36+ import com .android .tools .build .bundletool .model .SdkAsar ;
3537import com .android .tools .build .bundletool .model .SdkBundle ;
3638import com .android .tools .build .bundletool .model .SignerConfig ;
3739import com .android .tools .build .bundletool .model .SigningConfiguration ;
4042import com .android .tools .build .bundletool .model .utils .DefaultSystemEnvironmentProvider ;
4143import com .android .tools .build .bundletool .model .utils .SystemEnvironmentProvider ;
4244import com .android .tools .build .bundletool .model .utils .files .FilePreconditions ;
45+ import com .android .tools .build .bundletool .validation .SdkAsarValidator ;
4346import com .android .tools .build .bundletool .validation .SdkBundleValidator ;
4447import com .google .auto .value .AutoValue ;
4548import com .google .common .util .concurrent .ListeningExecutorService ;
@@ -72,6 +75,7 @@ public enum OutputFormat {
7275 }
7376
7477 private static final Flag <Path > SDK_BUNDLE_LOCATION_FLAG = Flag .path ("sdk-bundle" );
78+ private static final Flag <Path > SDK_ARCHIVE_LOCATION_FLAG = Flag .path ("sdk-archive" );
7579 private static final Flag <Integer > VERSION_CODE_FLAG = Flag .positiveInteger ("version-code" );
7680 private static final Flag <Path > OUTPUT_FILE_FLAG = Flag .path ("output" );
7781 private static final Flag <OutputFormat > OUTPUT_FORMAT_FLAG =
@@ -90,7 +94,9 @@ public enum OutputFormat {
9094 private static final SystemEnvironmentProvider DEFAULT_PROVIDER =
9195 new DefaultSystemEnvironmentProvider ();
9296
93- abstract Path getSdkBundlePath ();
97+ abstract Optional <Path > getSdkBundlePath ();
98+
99+ abstract Optional <Path > getSdkArchivePath ();
94100
95101 abstract Integer getVersionCode ();
96102
@@ -120,6 +126,7 @@ ListeningExecutorService getExecutorService() {
120126
121127 public abstract Optional <Integer > getFirstVariantNumber ();
122128
129+
123130 /** Creates a builder for the {@link BuildSdkApksCommand} with some default settings. */
124131 public static BuildSdkApksCommand .Builder builder () {
125132 return new AutoValue_BuildSdkApksCommand .Builder ()
@@ -135,6 +142,9 @@ public abstract static class Builder {
135142 /** Sets the path to the input SDK bundle. Must have the extension ".asb". */
136143 public abstract Builder setSdkBundlePath (Path sdkBundlePath );
137144
145+ /** Sets the path to the input SDK archive. Must have the extension ".asar". */
146+ public abstract Builder setSdkArchivePath (Path sdkArchivePath );
147+
138148 /** Sets the SDK version code */
139149 public abstract Builder setVersionCode (Integer versionCode );
140150
@@ -220,6 +230,7 @@ public Builder setExecutorService(ListeningExecutorService executorService) {
220230 */
221231 public abstract Builder setFirstVariantNumber (int firstVariantNumber );
222232
233+
223234 abstract BuildSdkApksCommand autoBuild ();
224235
225236 /**
@@ -232,7 +243,11 @@ public BuildSdkApksCommand build() {
232243 setExecutorServiceInternal (createInternalExecutorService (DEFAULT_THREAD_POOL_SIZE ));
233244 setExecutorServiceCreatedByBundleTool (true );
234245 }
235- return autoBuild ();
246+ BuildSdkApksCommand command = autoBuild ();
247+ checkState (
248+ command .getSdkBundlePath ().isPresent () ^ command .getSdkArchivePath ().isPresent (),
249+ "One and only one of SdkBundlePath and SdkArchivePath should be set." );
250+ return command ;
236251 }
237252 }
238253
@@ -244,10 +259,11 @@ static BuildSdkApksCommand fromFlags(
244259 ParsedFlags flags , PrintStream out , SystemEnvironmentProvider provider ) {
245260 Builder sdkApksCommandBuilder =
246261 BuildSdkApksCommand .builder ()
247- .setSdkBundlePath (SDK_BUNDLE_LOCATION_FLAG .getRequiredValue (flags ))
248262 .setOutputFile (OUTPUT_FILE_FLAG .getRequiredValue (flags ));
249263
250264 // Optional arguments.
265+ SDK_BUNDLE_LOCATION_FLAG .getValue (flags ).ifPresent (sdkApksCommandBuilder ::setSdkBundlePath );
266+ SDK_ARCHIVE_LOCATION_FLAG .getValue (flags ).ifPresent (sdkApksCommandBuilder ::setSdkArchivePath );
251267 OUTPUT_FORMAT_FLAG .getValue (flags ).ifPresent (sdkApksCommandBuilder ::setOutputFormat );
252268 OVERWRITE_OUTPUT_FLAG .getValue (flags ).ifPresent (sdkApksCommandBuilder ::setOverwriteOutput );
253269 VERSION_CODE_FLAG .getValue (flags ).ifPresent (sdkApksCommandBuilder ::setVersionCode );
@@ -275,8 +291,19 @@ static BuildSdkApksCommand fromFlags(
275291
276292 public Path execute () {
277293 validateInput ();
294+ if (getSdkBundlePath ().isPresent ()) {
295+ executeForSdkBundle ();
296+ } else if (getSdkArchivePath ().isPresent ()) {
297+ executeForSdkArchive ();
298+ } else {
299+ throw new IllegalStateException (
300+ "One and only one of SdkBundlePath and SdkArchivePath should be set." );
301+ }
302+ return getOutputFile ();
303+ }
278304
279- try (ZipFile bundleZip = new ZipFile (getSdkBundlePath ().toFile ());
305+ private void executeForSdkBundle () {
306+ try (ZipFile bundleZip = new ZipFile (getSdkBundlePath ().get ().toFile ());
280307 TempDirectory tempDir = new TempDirectory (getClass ().getSimpleName ())) {
281308
282309 SdkBundleValidator bundleValidator = SdkBundleValidator .create ();
@@ -287,34 +314,64 @@ public Path execute() {
287314 bundleValidator .validateModulesFile (modulesZip );
288315 SdkBundle sdkBundle = SdkBundle .buildFromZip (bundleZip , modulesZip , getVersionCode ());
289316 bundleValidator .validate (sdkBundle );
290-
291- DaggerBuildSdkApksManagerComponent .builder ()
292- .setBuildSdkApksCommand (this )
293- .setTempDirectory (tempDir )
294- .setSdkBundle (sdkBundle )
295- .build ()
296- .create ()
297- .execute ();
317+ executeBuildSdkApksManager (sdkBundle , tempDir );
298318 }
299319 } catch (ZipException e ) {
300320 throw InvalidBundleException .builder ()
301321 .withCause (e )
302322 .withUserMessage ("The SDK Bundle is not a valid zip file." )
303323 .build ();
304324 } catch (IOException e ) {
305- throw new UncheckedIOException ("An error occurred when validating the Sdk Bundle." , e );
325+ throw new UncheckedIOException ("An error occurred when processing the Sdk Bundle." , e );
306326 } finally {
307327 if (isExecutorServiceCreatedByBundleTool ()) {
308328 getExecutorService ().shutdown ();
309329 }
310330 }
331+ }
311332
312- return getOutputFile ();
333+ private void executeForSdkArchive () {
334+ try (ZipFile sdkAsarZip = new ZipFile (getSdkArchivePath ().get ().toFile ());
335+ TempDirectory tempDir = new TempDirectory (getClass ().getSimpleName ())) {
336+
337+ SdkAsarValidator .validateFile (sdkAsarZip );
338+
339+ Path modulesPath = tempDir .getPath ().resolve (EXTRACTED_SDK_MODULES_FILE_NAME );
340+ try (ZipFile modulesZip = getModulesZip (sdkAsarZip , modulesPath )) {
341+ SdkAsarValidator .validateModulesFile (modulesZip );
342+ SdkAsar sdkAsar = SdkAsar .buildFromZip (sdkAsarZip , modulesZip , modulesPath );
343+ SdkBundle sdkBundle = SdkBundle .buildFromAsar (sdkAsar , getVersionCode ());
344+ SdkBundleValidator .create ().validate (sdkBundle );
345+ executeBuildSdkApksManager (sdkBundle , tempDir );
346+ }
347+ } catch (ZipException e ) {
348+ throw InvalidBundleException .builder ()
349+ .withCause (e )
350+ .withUserMessage ("The SDK archive is not a valid zip file." )
351+ .build ();
352+ } catch (IOException e ) {
353+ throw new UncheckedIOException ("An error occurred when processing the Sdk archive." , e );
354+ } finally {
355+ if (isExecutorServiceCreatedByBundleTool ()) {
356+ getExecutorService ().shutdown ();
357+ }
358+ }
359+ }
360+
361+ private void executeBuildSdkApksManager (SdkBundle sdkBundle , TempDirectory tempDir )
362+ throws IOException {
363+ DaggerBuildSdkApksManagerComponent .builder ()
364+ .setBuildSdkApksCommand (this )
365+ .setTempDirectory (tempDir )
366+ .setSdkBundle (sdkBundle )
367+ .build ()
368+ .create ()
369+ .execute ();
313370 }
314371
315372 private void validateInput () {
316- FilePreconditions . checkFileExistsAndReadable ( getSdkBundlePath ());
317- FilePreconditions . checkFileHasExtension ( "ASB file" , getSdkBundlePath (), ".asb" );
373+ getSdkBundlePath (). ifPresent ( BuildSdkApksCommand :: validateSdkBundlePath );
374+ getSdkArchivePath (). ifPresent ( BuildSdkApksCommand :: validateSdkArchivePath );
318375
319376 switch (getOutputFormat ()) {
320377 case APK_SET :
@@ -344,6 +401,16 @@ private static ListeningExecutorService createInternalExecutorService(int maxThr
344401 return MoreExecutors .listeningDecorator (Executors .newFixedThreadPool (maxThreads ));
345402 }
346403
404+ private static void validateSdkBundlePath (Path sdkBundlePath ) {
405+ FilePreconditions .checkFileExistsAndReadable (sdkBundlePath );
406+ FilePreconditions .checkFileHasExtension ("ASB file" , sdkBundlePath , ".asb" );
407+ }
408+
409+ private static void validateSdkArchivePath (Path sdkArchivePath ) {
410+ FilePreconditions .checkFileExistsAndReadable (sdkArchivePath );
411+ FilePreconditions .checkFileHasExtension ("ASAR file" , sdkArchivePath , ".asar" );
412+ }
413+
347414 public static CommandHelp help () {
348415 return CommandHelp .builder ()
349416 .setCommandName (COMMAND_NAME )
@@ -355,7 +422,17 @@ public static CommandHelp help() {
355422 FlagDescription .builder ()
356423 .setFlagName (SDK_BUNDLE_LOCATION_FLAG .getName ())
357424 .setExampleValue ("path/to/SDKbundle.asb" )
358- .setDescription ("Path to SDK bundle. Must have the extension '.asb'." )
425+ .setDescription (
426+ "Path to SDK bundle. Must have the extension '.asb'. Cannot be used together"
427+ + " with the `sdk-archive` flag." )
428+ .build ())
429+ .addFlag (
430+ FlagDescription .builder ()
431+ .setFlagName (SDK_ARCHIVE_LOCATION_FLAG .getName ())
432+ .setExampleValue ("path/to/sdk.asar" )
433+ .setDescription (
434+ "Path to SDK archive. Must have the extension '.asar'. Cannot be used together"
435+ + " with the `sdk-bundle` flag." )
359436 .build ())
360437 .addFlag (
361438 FlagDescription .builder ()
0 commit comments