2121import static com .android .tools .build .bundletool .model .utils .files .FilePreconditions .checkFileExistsAndReadable ;
2222import static com .android .tools .build .bundletool .model .utils .files .FilePreconditions .checkFileHasExtension ;
2323import static com .google .common .base .Preconditions .checkArgument ;
24+ import static com .google .common .base .Preconditions .checkState ;
2425
2526import com .android .bundle .RuntimeEnabledSdkConfigProto .SdkSplitPropertiesInheritedFromApp ;
2627import com .android .tools .build .bundletool .androidtools .Aapt2Command ;
3233import com .android .tools .build .bundletool .model .BundleModule ;
3334import com .android .tools .build .bundletool .model .Password ;
3435import com .android .tools .build .bundletool .model .SdkAsar ;
36+ import com .android .tools .build .bundletool .model .SdkBundle ;
3537import com .android .tools .build .bundletool .model .SignerConfig ;
3638import com .android .tools .build .bundletool .model .SigningConfiguration ;
3739import com .android .tools .build .bundletool .model .exceptions .CommandExecutionException ;
4143import com .android .tools .build .bundletool .model .utils .files .BufferedIo ;
4244import com .android .tools .build .bundletool .sdkmodule .SdkModuleToAppBundleModuleConverter ;
4345import com .android .tools .build .bundletool .validation .SdkAsarValidator ;
46+ import com .android .tools .build .bundletool .validation .SdkBundleValidator ;
4447import com .google .auto .value .AutoValue ;
4548import com .google .common .util .concurrent .ListeningExecutorService ;
4649import com .google .common .util .concurrent .MoreExecutors ;
@@ -64,7 +67,10 @@ public abstract class BuildSdkApksForAppCommand {
6467
6568 public static final String COMMAND_NAME = "build-sdk-apks-for-app" ;
6669
70+ private static final Flag <Path > SDK_BUNDLE_LOCATION_FLAG = Flag .path ("sdk-bundle" );
71+
6772 private static final Flag <Path > SDK_ARCHIVE_LOCATION_FLAG = Flag .path ("sdk-archive" );
73+
6874 private static final Flag <Path > INHERITED_APP_PROPERTIES_LOCATION_FLAG =
6975 Flag .path ("app-properties" );
7076
@@ -81,7 +87,9 @@ public abstract class BuildSdkApksForAppCommand {
8187 private static final SystemEnvironmentProvider DEFAULT_PROVIDER =
8288 new DefaultSystemEnvironmentProvider ();
8389
84- public abstract Path getSdkArchivePath ();
90+ public abstract Optional <Path > getSdkBundlePath ();
91+
92+ public abstract Optional <Path > getSdkArchivePath ();
8593
8694 public abstract SdkSplitPropertiesInheritedFromApp getInheritedAppProperties ();
8795
@@ -107,6 +115,9 @@ public static BuildSdkApksForAppCommand.Builder builder() {
107115 @ AutoValue .Builder
108116 public abstract static class Builder {
109117
118+ /** Sets the path to the SDK bundle file. Must have the extension ".asb". */
119+ public abstract Builder setSdkBundlePath (Path sdkBundlePath );
120+
110121 /** Sets the path to the SDK archive file. Must have the extension ".asar". */
111122 public abstract Builder setSdkArchivePath (Path sdkArchivePath );
112123
@@ -160,19 +171,33 @@ public BuildSdkApksForAppCommand build() {
160171 setExecutorServiceInternal (createInternalExecutorService (DEFAULT_THREAD_POOL_SIZE ));
161172 setExecutorServiceCreatedByBundleTool (true );
162173 }
163- return autoBuild ();
174+ BuildSdkApksForAppCommand command = autoBuild ();
175+ checkState (
176+ command .getSdkBundlePath ().isPresent () ^ command .getSdkArchivePath ().isPresent (),
177+ "One and only one of SdkBundlePath and SdkArchivePath should be set." );
178+ return command ;
164179 }
165180 }
166181
167182 public static CommandHelp help () {
168183 return CommandHelp .builder ()
169184 .setCommandName (COMMAND_NAME )
170185 .setCommandDescription (CommandDescription .builder ().setShortDescription ("" ).build ())
186+ .addFlag (
187+ FlagDescription .builder ()
188+ .setFlagName (SDK_BUNDLE_LOCATION_FLAG .getName ())
189+ .setExampleValue ("sdk.asb" )
190+ .setDescription (
191+ "Path to SDK bundle to generate app-specific split APKs from. Can"
192+ + " not be used together with the `sdk-archive` flag." )
193+ .build ())
171194 .addFlag (
172195 FlagDescription .builder ()
173196 .setFlagName (SDK_ARCHIVE_LOCATION_FLAG .getName ())
174197 .setExampleValue ("sdk.asar" )
175- .setDescription ("Path to SDK archive to generate app-specific split APKs from." )
198+ .setDescription (
199+ "Path to SDK archive to generate app-specific split APKs from. Can"
200+ + " not be used together with the `sdk-bundle` flag." )
176201 .build ())
177202 .addFlag (
178203 FlagDescription .builder ()
@@ -250,11 +275,11 @@ static BuildSdkApksForAppCommand fromFlags(
250275 ParsedFlags flags , PrintStream out , SystemEnvironmentProvider systemEnvironmentProvider ) {
251276 BuildSdkApksForAppCommand .Builder command =
252277 BuildSdkApksForAppCommand .builder ()
253- .setSdkArchivePath (SDK_ARCHIVE_LOCATION_FLAG .getRequiredValue (flags ))
254278 .setInheritedAppProperties (
255279 INHERITED_APP_PROPERTIES_LOCATION_FLAG .getRequiredValue (flags ))
256280 .setOutputFile (OUTPUT_FILE_FLAG .getRequiredValue (flags ));
257-
281+ SDK_BUNDLE_LOCATION_FLAG .getValue (flags ).ifPresent (command ::setSdkBundlePath );
282+ SDK_ARCHIVE_LOCATION_FLAG .getValue (flags ).ifPresent (command ::setSdkArchivePath );
258283 AAPT2_PATH_FLAG
259284 .getValue (flags )
260285 .ifPresent (
@@ -267,14 +292,58 @@ static BuildSdkApksForAppCommand fromFlags(
267292
268293 public void execute () {
269294 validateInput ();
295+ if (getSdkBundlePath ().isPresent ()) {
296+ executeForSdkBundle ();
297+ } else if (getSdkArchivePath ().isPresent ()) {
298+ executeForSdkArchive ();
299+ } else {
300+ throw new IllegalStateException ("whaaat" );
301+ }
302+ }
303+
304+ private void validateInput () {
305+ if (getSdkBundlePath ().isPresent ()) {
306+ checkFileExistsAndReadable (getSdkBundlePath ().get ());
307+ checkFileHasExtension ("ASB file" , getSdkBundlePath ().get (), ".asb" );
308+ }
309+ if (getSdkArchivePath ().isPresent ()) {
310+ checkFileExistsAndReadable (getSdkArchivePath ().get ());
311+ checkFileHasExtension ("ASAR file" , getSdkArchivePath ().get (), ".asar" );
312+ }
313+ }
314+
315+ private void executeForSdkBundle () {
316+ try (TempDirectory tempDir = new TempDirectory (getClass ().getSimpleName ());
317+ ZipFile sdkBundleZip = new ZipFile (getSdkBundlePath ().get ().toFile ())) {
318+ Path modulesPath = tempDir .getPath ().resolve (EXTRACTED_SDK_MODULES_FILE_NAME );
319+ try (ZipFile modulesZip = getModulesZip (sdkBundleZip , modulesPath )) {
320+ SdkBundleValidator sdkBundleValidator = SdkBundleValidator .create ();
321+ sdkBundleValidator .validateModulesFile (modulesZip );
322+ // SdkBundle#getVersionCode is not used in `build-apks`. It does not matter what
323+ // value we set here, so we are just setting 0.
324+ SdkBundle sdkBundle =
325+ SdkBundle .buildFromZip (sdkBundleZip , modulesZip , /* versionCode= */ 0 );
326+ sdkBundleValidator .validate (sdkBundle );
327+ generateAppApks (sdkBundle .getModule (), tempDir );
328+ }
329+ } catch (ZipException e ) {
330+ throw CommandExecutionException .builder ()
331+ .withInternalMessage ("ASB is not a valid zip file." )
332+ .withCause (e )
333+ .build ();
334+ } catch (IOException e ) {
335+ throw new UncheckedIOException ("An error occurred when processing the SDK bundle." , e );
336+ }
337+ }
270338
339+ private void executeForSdkArchive () {
271340 try (TempDirectory tempDir = new TempDirectory (getClass ().getSimpleName ());
272- ZipFile asarZip = new ZipFile (getSdkArchivePath ().toFile ())) {
341+ ZipFile asarZip = new ZipFile (getSdkArchivePath ().get (). toFile ())) {
273342 Path modulesPath = tempDir .getPath ().resolve (EXTRACTED_SDK_MODULES_FILE_NAME );
274343 try (ZipFile modulesZip = getModulesZip (asarZip , modulesPath )) {
275344 SdkAsarValidator .validateModulesFile (modulesZip );
276345 SdkAsar sdkAsar = SdkAsar .buildFromZip (asarZip , modulesZip , modulesPath );
277- generateAppApks (sdkAsar , tempDir );
346+ generateAppApks (sdkAsar . getModule () , tempDir );
278347 }
279348 } catch (ZipException e ) {
280349 throw CommandExecutionException .builder ()
@@ -286,15 +355,9 @@ public void execute() {
286355 }
287356 }
288357
289- private void validateInput () {
290- checkFileExistsAndReadable (getSdkArchivePath ());
291- checkFileHasExtension ("ASAR file" , getSdkArchivePath (), ".asar" );
292- }
293-
294- private void generateAppApks (SdkAsar sdkAsar , TempDirectory tempDirectory ) {
358+ private void generateAppApks (BundleModule sdkModule , TempDirectory tempDirectory ) {
295359 BundleModule convertedAppModule =
296- new SdkModuleToAppBundleModuleConverter (sdkAsar .getModule (), getInheritedAppProperties ())
297- .convert ();
360+ new SdkModuleToAppBundleModuleConverter (sdkModule , getInheritedAppProperties ()).convert ();
298361 DaggerBuildSdkApksForAppManagerComponent .builder ()
299362 .setBuildSdkApksForAppCommand (this )
300363 .setModule (convertedAppModule )
0 commit comments