blob: 6a6f0b73808a78a523b188810a92efe9aefa6e97 [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* 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.android.manifmerger;
import com.android.annotations.VisibleForTesting;
import com.android.utils.ILogger;
import com.android.utils.StdLogger;
import com.google.common.base.Charsets;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.google.common.io.Files;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Locale;
import java.util.StringTokenizer;
/**
* Command line interface to the {@link ManifestMerger2}
*/
public class Merger {
public static void main(String[] args) {
try {
System.exit(new Merger().process(args));
} catch (FileNotFoundException e) {
System.exit(1);
}
System.exit(0);
}
public int process(String[] args) throws FileNotFoundException {
Iterator<String> arguments = Arrays.asList(args).iterator();
// first pass to get all mandatory parameters.
String mainManifest = null;
StdLogger.Level logLevel = StdLogger.Level.INFO;
ILogger logger = new StdLogger(logLevel);
while (arguments.hasNext()) {
String selector = arguments.next();
if (!selector.startsWith("--")) {
logger.error(null /* throwable */,
"Invalid parameter " + selector + ", expected a command switch");
return 1;
}
if ("--usage".equals(selector)) {
usage();
return 0;
}
if (!arguments.hasNext()) {
logger.error(null /* throwable */,
"Command switch " + selector + " has no value associated");
return 1;
}
String value = arguments.next();
if ("--main".equals(selector)) {
mainManifest = value;
}
if ("--log".equals(selector)) {
logLevel = StdLogger.Level.valueOf(value);
}
}
if (mainManifest == null) {
System.err.println("--main command switch not provided.");
return 1;
}
// recreate the logger with the provided log level for the rest of the processing.
logger = createLogger(logLevel);
File mainManifestFile = checkPath(mainManifest);
ManifestMerger2.Invoker invoker = createInvoker(
mainManifestFile, logger);
// second pass, get optional parameters and store them in the invoker.
arguments = Arrays.asList(args).iterator();
File outFile = null;
// first pass to get all mandatory parameters.
while (arguments.hasNext()) {
String selector = arguments.next();
String value = arguments.next();
if (Strings.isNullOrEmpty(value)) {
logger.error(null /* throwable */,
"Empty value for switch " + selector);
return 1;
}
if ("--libs".equals(selector)) {
StringTokenizer stringTokenizer = new StringTokenizer(value, File.pathSeparator);
while (stringTokenizer.hasMoreElements()) {
File library = checkPath(stringTokenizer.nextToken());
invoker.addLibraryManifest(library);
}
}
if ("--overlays".equals(selector)) {
StringTokenizer stringTokenizer = new StringTokenizer(value, File.pathSeparator);
while (stringTokenizer.hasMoreElements()) {
File library = checkPath(stringTokenizer.nextToken());
invoker.addFlavorAndBuildTypeManifest(library);
}
}
if ("--property".equals(selector)) {
if (!value.contains("=")) {
logger.error(null /* throwable */,
"Invalid property setting, should be NAME=VALUE format");
return 1;
}
try {
ManifestMerger2.SystemProperty systemProperty = ManifestMerger2.SystemProperty
.valueOf(value.substring(0, value.indexOf('='))
.toUpperCase(Locale.ENGLISH));
invoker.setOverride(systemProperty, value.substring(value.indexOf('=') + 1));
} catch (IllegalArgumentException e) {
logger.error(e, "Invalid property name "+ value.substring(0, value.indexOf('='))
+ ", allowed properties are : " + Joiner
.on(',').join(ManifestMerger2.SystemProperty.values()));
return 1;
}
}
if ("--placeholder".equals(selector)) {
if (!value.contains("=")) {
logger.error(null /* throwable */,
"Invalid placeholder setting, should be NAME=VALUE format");
return 1;
}
invoker.setPlaceHolderValue(value.substring(0, value.indexOf('=')),
value.substring(value.indexOf('=') + 1));
}
if ("--out".equals(selector)) {
outFile = new File(value);
}
}
try {
MergingReport merge = invoker.merge();
if (merge.getResult().isSuccess()) {
XmlDocument xmlDocument = merge.getMergedDocument().get();
if (outFile != null) {
try {
Files.write(xmlDocument.prettyPrint(), outFile, Charsets.UTF_8);
} catch (IOException e) {
throw new RuntimeException(e);
}
} else {
System.out.println(xmlDocument.prettyPrint());
}
} else {
for (MergingReport.Record record : merge.getLoggingRecords()) {
System.err.println(record);
}
}
} catch (ManifestMerger2.MergeFailureException e) {
logger.error(e, "Exception while merging manifests");
return 1;
}
return 0;
}
protected ManifestMerger2.Invoker createInvoker(File mainManifestFile,
ILogger logger) {
return ManifestMerger2.newMerger(mainManifestFile, logger, ManifestMerger2.MergeType.APPLICATION);
}
public static void usage() {
System.out.println("Android Manifest Merger Tool Version 2\n");
System.out.println("Usage:");
System.out.println("Merger --main mainAndroidManifest.xml");
System.out.println("\t--log [VERBOSE, INFO, WARNING, ERROR]");
System.out.println("\t--libs [path separated list of lib's manifests]");
System.out.println("\t--overlays [path separated list of overlay's manifests]");
System.out.println("\t--property ["
+ Joiner.on(" | ").join(ManifestMerger2.SystemProperty.values())
+ "=value]");
System.out.println("\t--placeholder [name=value]");
System.out.println("\t--out [path of the output file]");
}
@VisibleForTesting
protected File checkPath(String path) throws FileNotFoundException {
File file = new File(path);
if (!file.exists()) {
System.err.println(path + " does not exist");
throw new FileNotFoundException(path);
}
return file;
}
@VisibleForTesting
protected ILogger createLogger(StdLogger.Level level) {
return new StdLogger(level);
}
}