• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.tools.metalava.apilevels;
18 
19 import com.android.tools.metalava.model.Codebase;
20 import org.jetbrains.annotations.NotNull;
21 import org.jetbrains.annotations.Nullable;
22 
23 import java.io.File;
24 import java.io.IOException;
25 import java.io.PrintStream;
26 import java.util.ArrayList;
27 import java.util.List;
28 
29 /**
30  * Main class for command line command to convert the existing API XML/TXT files into diff-based
31  * simple text files.
32  */
33 public class ApiGenerator {
main(String[] args)34     public static void main(String[] args) {
35         boolean error = false;
36         int minApi = 1;
37         int currentApi = -1;
38         String currentCodename = null;
39         File currentJar = null;
40         List<String> patterns = new ArrayList<>();
41         String outPath = null;
42 
43         for (int i = 0; i < args.length && !error; i++) {
44             String arg = args[i];
45 
46             if (arg.equals("--pattern")) {
47                 i++;
48                 if (i < args.length) {
49                     patterns.add(args[i]);
50                 } else {
51                     System.err.println("Missing argument after " + arg);
52                     error = true;
53                 }
54             } else if (arg.equals("--current-version")) {
55                 i++;
56                 if (i < args.length) {
57                     currentApi = Integer.parseInt(args[i]);
58                     if (currentApi <= 22) {
59                         System.err.println("Suspicious currentApi=" + currentApi + ", expected at least 23");
60                         error = true;
61                     }
62                 } else {
63                     System.err.println("Missing number >= 1 after " + arg);
64                     error = true;
65                 }
66             } else if (arg.equals("--current-codename")) {
67                 i++;
68                 if (i < args.length) {
69                     currentCodename = args[i];
70                 } else {
71                     System.err.println("Missing codename after " + arg);
72                     error = true;
73                 }
74             } else if (arg.equals("--current-jar")) {
75                 i++;
76                 if (i < args.length) {
77                     if (currentJar != null) {
78                         System.err.println("--current-jar should only be specified once");
79                         error = true;
80                     }
81                     String path = args[i];
82                     currentJar = new File(path);
83                 } else {
84                     System.err.println("Missing argument after " + arg);
85                     error = true;
86                 }
87             } else if (arg.equals("--min-api")) {
88                 i++;
89                 if (i < args.length) {
90                     minApi = Integer.parseInt(args[i]);
91                 } else {
92                     System.err.println("Missing number >= 1 after " + arg);
93                     error = true;
94                 }
95             } else if (arg.length() >= 2 && arg.startsWith("--")) {
96                 System.err.println("Unknown argument: " + arg);
97                 error = true;
98             } else if (outPath == null) {
99                 outPath = arg;
100             } else if (new File(arg).isDirectory()) {
101                 String pattern = arg;
102                 if (!pattern.endsWith(File.separator)) {
103                     pattern += File.separator;
104                 }
105                 pattern += "platforms" + File.separator + "android-%" + File.separator + "android.jar";
106                 patterns.add(pattern);
107             } else {
108                 System.err.println("Unknown argument: " + arg);
109                 error = true;
110             }
111         }
112 
113         if (!error && outPath == null) {
114             System.err.println("Missing out file path");
115             error = true;
116         }
117 
118         if (!error && patterns.isEmpty()) {
119             System.err.println("Missing SdkFolder or --pattern.");
120             error = true;
121         }
122 
123         if (currentJar != null && currentApi == -1 || currentJar == null && currentApi != -1) {
124             System.err.println("You must specify both --current-jar and --current-version (or neither one)");
125             error = true;
126         }
127 
128         // The SDK version number
129         if (currentCodename != null && !"REL".equals(currentCodename)) {
130             currentApi++;
131         }
132 
133         if (error) {
134             printUsage();
135             System.exit(1);
136         }
137 
138         try {
139             if (!generate(minApi, currentApi, currentJar, patterns, outPath, null)) {
140                 System.exit(1);
141             }
142         } catch (IOException e) {
143             e.printStackTrace();
144             System.exit(-1);
145         }
146     }
147 
generate(int minApi, int currentApi, @NotNull File currentJar, @NotNull List<String> patterns, @NotNull String outPath, @Nullable Codebase codebase)148     private static boolean generate(int minApi,
149                                     int currentApi,
150                                     @NotNull File currentJar,
151                                     @NotNull List<String> patterns,
152                                     @NotNull String outPath,
153                                     @Nullable Codebase codebase) throws IOException {
154         AndroidJarReader reader = new AndroidJarReader(patterns, minApi, currentJar, currentApi, codebase);
155         Api api = reader.getApi();
156         return createApiFile(new File(outPath), api);
157     }
158 
generate(@otNull File[] apiLevels, @NotNull File outputFile, @Nullable Codebase codebase)159     public static boolean generate(@NotNull File[] apiLevels,
160                                    @NotNull File outputFile,
161                                    @Nullable Codebase codebase) throws IOException {
162         AndroidJarReader reader = new AndroidJarReader(apiLevels, codebase);
163         Api api = reader.getApi();
164         return createApiFile(outputFile, api);
165     }
166 
printUsage()167     private static void printUsage() {
168         System.err.println("\nGenerates a single API file from the content of an SDK.");
169         System.err.println("Usage:");
170         System.err.println("\tApiCheck [--min-api=1] OutFile [SdkFolder | --pattern sdk/%/public/android.jar]+");
171         System.err.println("Options:");
172         System.err.println("--min-api <int> : The first API level to consider (>=1).");
173         System.err.println("--pattern <pattern>: Path pattern to find per-API android.jar files, where\n" +
174             "            '%' is replaced by the API level.");
175         System.err.println("--current-jar <path>: Path pattern to find the current android.jar");
176         System.err.println("--current-version <int>: The API level for the current API");
177         System.err.println("--current-codename <name>: REL, if a release, or codename for previews");
178         System.err.println("SdkFolder: if given, this adds the pattern\n" +
179             "           '$SdkFolder/platforms/android-%/android.jar'");
180         System.err.println("If multiple --pattern are specified, they are tried in the order given.\n");
181     }
182 
183     /**
184      * Creates the simplified diff-based API level.
185      *
186      * @param outFile the output file
187      * @param api     the api to write
188      */
createApiFile(File outFile, Api api)189     private static boolean createApiFile(File outFile, Api api) {
190         File parentFile = outFile.getParentFile();
191         if (!parentFile.exists()) {
192             boolean ok = parentFile.mkdirs();
193             if (!ok) {
194                 System.err.println("Could not create directory " + parentFile);
195                 return false;
196             }
197         }
198         try (PrintStream stream = new PrintStream(outFile, "UTF-8")) {
199             stream.println("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
200             api.print(stream);
201         } catch (Exception e) {
202             e.printStackTrace();
203             return false;
204         }
205 
206         return true;
207     }
208 }
209