blob: aa74a7405a0470a917f9318d2ad801844dc98194 [file] [log] [blame]
// Copyright 2018 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.devtools.build.android;
import com.android.builder.core.VariantType;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.devtools.build.android.Converters.PathConverter;
import com.google.devtools.build.android.Converters.SerializedAndroidDataConverter;
import com.google.devtools.build.android.Converters.SerializedAndroidDataListConverter;
import com.google.devtools.common.options.Option;
import com.google.devtools.common.options.OptionDocumentationCategory;
import com.google.devtools.common.options.OptionEffectTag;
import com.google.devtools.common.options.OptionsBase;
import com.google.devtools.common.options.OptionsParser;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.logging.Logger;
/** An action that merges a library's assets (without using resources or manifests). */
public class AndroidAssetMergingAction extends AbstractBusyBoxAction {
private static final Logger logger = Logger.getLogger(AndroidAssetMergingAction.class.getName());
public static void main(String[] args) throws Exception {
create().invoke(args);
}
@VisibleForTesting
static void testingMain(String... args) throws Exception {
create().invokeWithoutExit(args);
}
private static AndroidAssetMergingAction create() {
return new AndroidAssetMergingAction(OptionsParser.newOptionsParser(Options.class));
}
private AndroidAssetMergingAction(OptionsParser optionsParser) {
super(optionsParser, "Merge assets");
}
/** Flag specifications for this action. */
public static final class Options extends OptionsBase {
@Option(
name = "primaryData",
defaultValue = "null",
converter = SerializedAndroidDataConverter.class,
category = "input",
documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
effectTags = {OptionEffectTag.UNKNOWN},
help =
"The assets of the current target. The expected format is "
+ SerializedAndroidData.EXPECTED_FORMAT
)
public SerializedAndroidData primary;
@Option(
name = "directData",
defaultValue = "",
converter = SerializedAndroidDataListConverter.class,
category = "input",
documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
effectTags = {OptionEffectTag.UNKNOWN},
help =
"Direct asset dependencies. These values will be used if not defined in the "
+ "primary assets. The expected format is "
+ SerializedAndroidData.EXPECTED_FORMAT
+ "[&...]"
)
public List<SerializedAndroidData> directData;
@Option(
name = "data",
defaultValue = "",
converter = SerializedAndroidDataListConverter.class,
category = "input",
documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
effectTags = {OptionEffectTag.UNKNOWN},
help =
"Transitive Data dependencies. These values will be used if not defined in the "
+ "primary assets. The expected format is "
+ SerializedAndroidData.EXPECTED_FORMAT
+ "[&...]"
)
public List<SerializedAndroidData> transitiveData;
@Option(
name = "assetsOutput",
defaultValue = "null",
converter = PathConverter.class,
category = "output",
documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
effectTags = {OptionEffectTag.UNKNOWN},
help = "Path to the write merged asset archive."
)
public Path assetsOutput;
@Option(
name = "throwOnAssetConflict",
defaultValue = "true",
category = "config",
documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
effectTags = {OptionEffectTag.UNKNOWN},
help = "If passed, asset merge conflicts will be treated as errors instead of warnings"
)
public boolean throwOnAssetConflict;
}
@Override
void run(Path tmp, ExecutorServiceCloser executorService) throws Exception {
Options options = getOptions(Options.class);
Path mergedAssets = tmp.resolve("merged_assets");
Path ignored = tmp.resolve("ignored");
Preconditions.checkNotNull(options.primary);
MergedAndroidData mergedData =
AndroidResourceMerger.mergeData(
options.primary,
/* primaryManifest = */ null,
options.directData,
options.transitiveData,
/* resourcesOut = */ ignored,
mergedAssets,
/* cruncher = */ null,
VariantType.LIBRARY,
/* symbolsOut = */ null,
/* rclassWriter = */ null,
options.throwOnAssetConflict,
executorService);
logCompletion("Merging");
Preconditions.checkState(
!Files.exists(ignored),
"The asset merging action should not produce non-asset merge results!");
ResourcesZip.from(ignored, mergedData.getAssetDir())
.writeTo(options.assetsOutput, /* compress= */ true);
logCompletion("Create assets zip");
}
@Override
Logger getLogger() {
return logger;
}
}