| /* |
| * 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); |
| } |
| |
| } |