Add skeleton for product-config tool.
There's also a bunch of boilerplate error handling and option parsing stuff that I wish someone would put into a library that's available to the android tree. Test: atest product-config-test Change-Id: Ieebcc7bd47a8644d1374fb02c146e9038859f4a2
This commit is contained in:
23
tools/product_config/Android.bp
Normal file
23
tools/product_config/Android.bp
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
java_defaults {
|
||||||
|
name: "product-config-defaults",
|
||||||
|
srcs: ["src/**/*.java"],
|
||||||
|
}
|
||||||
|
|
||||||
|
java_binary_host {
|
||||||
|
name: "product-config",
|
||||||
|
defaults: ["product-config-defaults"],
|
||||||
|
manifest: "MANIFEST.MF"
|
||||||
|
}
|
||||||
|
|
||||||
|
java_test_host {
|
||||||
|
name: "product-config-test",
|
||||||
|
defaults: ["product-config-defaults"],
|
||||||
|
srcs: [
|
||||||
|
"test/**/*.java",
|
||||||
|
],
|
||||||
|
static_libs: [
|
||||||
|
"junit"
|
||||||
|
],
|
||||||
|
test_suites: ["general-tests"]
|
||||||
|
}
|
||||||
|
|
2
tools/product_config/MANIFEST.MF
Normal file
2
tools/product_config/MANIFEST.MF
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
Manifest-Version: 1.0
|
||||||
|
Main-Class: com.android.build.config.Main
|
7
tools/product_config/TEST_MAPPING
Normal file
7
tools/product_config/TEST_MAPPING
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"presubmit": [
|
||||||
|
{
|
||||||
|
"name": "product_config_test"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@@ -0,0 +1,263 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.build.config;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for reporting errors.
|
||||||
|
*/
|
||||||
|
public class ErrorReporter {
|
||||||
|
/**
|
||||||
|
* List of Entries that have occurred.
|
||||||
|
*/
|
||||||
|
// Also used as the lock for this object.
|
||||||
|
private final ArrayList<Entry> mEntries = new ArrayList();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The categories that are for this Errors object.
|
||||||
|
*/
|
||||||
|
private Map<Integer, Category> mCategories;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether there has been a warning or an error yet.
|
||||||
|
*/
|
||||||
|
private boolean mHadWarningOrError;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether there has been an error yet.
|
||||||
|
*/
|
||||||
|
private boolean mHadError;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether errors are errors, warnings or hidden.
|
||||||
|
*/
|
||||||
|
public static enum Level {
|
||||||
|
HIDDEN("hidden"),
|
||||||
|
WARNING("warning"),
|
||||||
|
ERROR("error");
|
||||||
|
|
||||||
|
private final String mLabel;
|
||||||
|
|
||||||
|
Level(String label) {
|
||||||
|
mLabel = label;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getLabel() {
|
||||||
|
return mLabel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The available error codes.
|
||||||
|
*/
|
||||||
|
public class Category {
|
||||||
|
private final int mCode;
|
||||||
|
private boolean mIsLevelSettable;
|
||||||
|
private Level mLevel;
|
||||||
|
private String mHelp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a Category object.
|
||||||
|
*/
|
||||||
|
public Category(int code, boolean isLevelSettable, Level level, String help) {
|
||||||
|
if (!isLevelSettable && level != Level.ERROR) {
|
||||||
|
throw new RuntimeException("Don't have WARNING or HIDDEN without isLevelSettable");
|
||||||
|
}
|
||||||
|
mCode = code;
|
||||||
|
mIsLevelSettable = isLevelSettable;
|
||||||
|
mLevel = level;
|
||||||
|
mHelp = help;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the numeric code for the Category, which can be used to set the level.
|
||||||
|
*/
|
||||||
|
public int getCode() {
|
||||||
|
return mCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get whether the level of this Category can be changed.
|
||||||
|
*/
|
||||||
|
public boolean isLevelSettable() {
|
||||||
|
return mIsLevelSettable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the level of this category.
|
||||||
|
*/
|
||||||
|
public void setLevel(Level level) {
|
||||||
|
if (!mIsLevelSettable) {
|
||||||
|
throw new RuntimeException("Can't set level for error " + mCode);
|
||||||
|
}
|
||||||
|
mLevel = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the level, including any overrides.
|
||||||
|
*/
|
||||||
|
public Level getLevel() {
|
||||||
|
return mLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the category's help text.
|
||||||
|
*/
|
||||||
|
public String getHelp() {
|
||||||
|
return mHelp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An instance of an error happening.
|
||||||
|
*/
|
||||||
|
public class Entry {
|
||||||
|
private final Category mCategory;
|
||||||
|
private final Position mPosition;
|
||||||
|
private final String mMessage;
|
||||||
|
|
||||||
|
Entry(Category category, Position position, String message) {
|
||||||
|
mCategory = category;
|
||||||
|
mPosition = position;
|
||||||
|
mMessage = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Category getCategory() {
|
||||||
|
return mCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Position getPosition() {
|
||||||
|
return mPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return mMessage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initLocked() {
|
||||||
|
if (mCategories == null) {
|
||||||
|
HashMap<Integer, Category> categories = new HashMap();
|
||||||
|
for (Field field: getClass().getFields()) {
|
||||||
|
if (Category.class.isAssignableFrom(field.getType())) {
|
||||||
|
Category category = null;
|
||||||
|
try {
|
||||||
|
category = (Category)field.get(this);
|
||||||
|
} catch (IllegalAccessException ex) {
|
||||||
|
// Wrap and rethrow, this is always on this class, so it's
|
||||||
|
// our programming error if this happens.
|
||||||
|
throw new RuntimeException("Categories on Errors should be public.", ex);
|
||||||
|
}
|
||||||
|
Category prev = categories.put(category.getCode(), category);
|
||||||
|
if (prev != null) {
|
||||||
|
throw new RuntimeException("Duplicate categories with code "
|
||||||
|
+ category.getCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mCategories = Collections.unmodifiableMap(categories);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a map of the category codes to the categories.
|
||||||
|
*/
|
||||||
|
public Map<Integer, Category> getCategories() {
|
||||||
|
synchronized (mEntries) {
|
||||||
|
initLocked();
|
||||||
|
return mCategories;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an error with no source position.
|
||||||
|
*/
|
||||||
|
public void add(Category category, String message) {
|
||||||
|
add(category, new Position(), message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an error.
|
||||||
|
*/
|
||||||
|
public void add(Category category, Position pos, String message) {
|
||||||
|
synchronized (mEntries) {
|
||||||
|
initLocked();
|
||||||
|
if (mCategories.get(category.getCode()) != category) {
|
||||||
|
throw new RuntimeException("Errors.Category used from the wrong Errors object.");
|
||||||
|
}
|
||||||
|
mEntries.add(new Entry(category, pos, message));
|
||||||
|
final Level level = category.getLevel();
|
||||||
|
if (level == Level.WARNING || level == Level.ERROR) {
|
||||||
|
mHadWarningOrError = true;
|
||||||
|
}
|
||||||
|
if (level == Level.ERROR) {
|
||||||
|
mHadError = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether there has been a warning or an error yet.
|
||||||
|
*/
|
||||||
|
public boolean hadWarningOrError() {
|
||||||
|
synchronized (mEntries) {
|
||||||
|
return mHadWarningOrError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether there has been an error yet.
|
||||||
|
*/
|
||||||
|
public boolean hadError() {
|
||||||
|
synchronized (mEntries) {
|
||||||
|
return mHadError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of all entries that were added.
|
||||||
|
*/
|
||||||
|
public List<Entry> getEntries() {
|
||||||
|
synchronized (mEntries) {
|
||||||
|
return new ArrayList<Entry>(mEntries);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints the errors.
|
||||||
|
*/
|
||||||
|
public void printErrors(PrintStream out) {
|
||||||
|
synchronized (mEntries) {
|
||||||
|
for (Entry entry: mEntries) {
|
||||||
|
final Category category = entry.getCategory();
|
||||||
|
final Level level = category.getLevel();
|
||||||
|
if (level == Level.HIDDEN) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
out.println(entry.getPosition() + "[" + level.getLabel() + " "
|
||||||
|
+ category.getCode() + "] " + entry.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.build.config;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error constants and error reporting.
|
||||||
|
* <p>
|
||||||
|
* <b>Naming Convention:</b>
|
||||||
|
* <ul>
|
||||||
|
* <li>ERROR_ for Categories with isLevelSettable false and Level.ERROR
|
||||||
|
* <li>WARNING_ for Categories with isLevelSettable false and default WARNING or HIDDEN
|
||||||
|
* <li>Don't have isLevelSettable true and not ERROR. (The constructor asserts this).
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
public class Errors extends ErrorReporter {
|
||||||
|
|
||||||
|
public final Category ERROR_COMMAND_LINE = new Category(1, false, Level.ERROR,
|
||||||
|
"Error on the command line.");
|
||||||
|
|
||||||
|
public final Category WARNING_UNKNOWN_COMMAND_LINE_ERROR = new Category(2, true, Level.HIDDEN,
|
||||||
|
"Passing unknown errors on the command line. Hidden by default for\n"
|
||||||
|
+ "forward compatibility.");
|
||||||
|
}
|
64
tools/product_config/src/com/android/build/config/Main.java
Normal file
64
tools/product_config/src/com/android/build/config/Main.java
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.build.config;
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
private final Errors mErrors;
|
||||||
|
private final Options mOptions;
|
||||||
|
|
||||||
|
public Main(Errors errors, Options options) {
|
||||||
|
mErrors = errors;
|
||||||
|
mOptions = options;
|
||||||
|
}
|
||||||
|
|
||||||
|
void run() {
|
||||||
|
System.out.println("Hello World");
|
||||||
|
|
||||||
|
// TODO: Check the build environment to make sure we're running in a real
|
||||||
|
// build environment, e.g. actually inside a source tree, with TARGET_PRODUCT
|
||||||
|
// and TARGET_BUILD_VARIANT defined, etc.
|
||||||
|
|
||||||
|
// TODO: Run kati and extract the variables and convert all that into starlark files.
|
||||||
|
|
||||||
|
// TODO: Run starlark with all the generated ones and the hand written ones.
|
||||||
|
|
||||||
|
// TODO: Get the variables that were defined in starlark and use that to write
|
||||||
|
// out the make, soong and bazel input files.
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Errors errors = new Errors();
|
||||||
|
|
||||||
|
Options options = Options.parse(errors, args);
|
||||||
|
if (errors.hadError()) {
|
||||||
|
Options.printHelp(System.err);
|
||||||
|
System.err.println();
|
||||||
|
errors.printErrors(System.err);
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (options.getAction()) {
|
||||||
|
case DEFAULT:
|
||||||
|
(new Main(errors, options)).run();
|
||||||
|
errors.printErrors(System.err);
|
||||||
|
return;
|
||||||
|
case HELP:
|
||||||
|
Options.printHelp(System.out);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
158
tools/product_config/src/com/android/build/config/Options.java
Normal file
158
tools/product_config/src/com/android/build/config/Options.java
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.build.config;
|
||||||
|
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
public class Options {
|
||||||
|
public enum Action {
|
||||||
|
DEFAULT,
|
||||||
|
HELP
|
||||||
|
}
|
||||||
|
|
||||||
|
private Action mAction = Action.DEFAULT;
|
||||||
|
|
||||||
|
public Action getAction() {
|
||||||
|
return mAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void printHelp(PrintStream out) {
|
||||||
|
out.println("usage: product_config");
|
||||||
|
out.println();
|
||||||
|
out.println("OPTIONS");
|
||||||
|
out.println(" --hide ERROR_ID Suppress this error.");
|
||||||
|
out.println(" --error ERROR_ID Make this ERROR_ID a fatal error.");
|
||||||
|
out.println(" --help -h This message.");
|
||||||
|
out.println(" --warning ERROR_ID Make this ERROR_ID a warning.");
|
||||||
|
out.println();
|
||||||
|
out.println("ERRORS");
|
||||||
|
out.println(" The following are the errors that can be controlled on the");
|
||||||
|
out.println(" commandline with the --hide --warning --error flags.");
|
||||||
|
|
||||||
|
TreeMap<Integer,Errors.Category> sorted = new TreeMap((new Errors()).getCategories());
|
||||||
|
|
||||||
|
for (final Errors.Category category: sorted.values()) {
|
||||||
|
if (category.isLevelSettable()) {
|
||||||
|
out.println(String.format(" %-3d %s", category.getCode(),
|
||||||
|
category.getHelp().replace("\n", "\n ")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class Parser {
|
||||||
|
private class ParseException extends Exception {
|
||||||
|
public ParseException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Errors mErrors;
|
||||||
|
private String[] mArgs;
|
||||||
|
private Options mResult = new Options();
|
||||||
|
private int mIndex;
|
||||||
|
|
||||||
|
public Parser(Errors errors, String[] args) {
|
||||||
|
mErrors = errors;
|
||||||
|
mArgs = args;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Options parse() {
|
||||||
|
try {
|
||||||
|
while (mIndex < mArgs.length) {
|
||||||
|
final String arg = mArgs[mIndex];
|
||||||
|
|
||||||
|
if ("--hide".equals(arg)) {
|
||||||
|
handleErrorCode(arg, Errors.Level.HIDDEN);
|
||||||
|
} else if ("--error".equals(arg)) {
|
||||||
|
handleErrorCode(arg, Errors.Level.ERROR);
|
||||||
|
} else if ("--help".equals(arg) || "-h".equals(arg)) {
|
||||||
|
// Help overrides all other commands if there isn't an error, but
|
||||||
|
// we will stop here.
|
||||||
|
if (!mErrors.hadError()) {
|
||||||
|
mResult.mAction = Action.HELP;
|
||||||
|
}
|
||||||
|
return mResult;
|
||||||
|
} else if ("--warning".equals(arg)) {
|
||||||
|
handleErrorCode(arg, Errors.Level.WARNING);
|
||||||
|
} else {
|
||||||
|
throw new ParseException("Unknown command line argument: " + arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
mIndex++;
|
||||||
|
}
|
||||||
|
} catch (ParseException ex) {
|
||||||
|
mErrors.add(mErrors.ERROR_COMMAND_LINE, ex.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return mResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addWarning(Errors.Category category, String message) {
|
||||||
|
mErrors.add(category, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getNextNonFlagArg() {
|
||||||
|
if (mIndex == mArgs.length - 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (mArgs[mIndex + 1].startsWith("-")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
mIndex++;
|
||||||
|
return mArgs[mIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
private int requireNextNumberArg(String arg) throws ParseException {
|
||||||
|
final String val = getNextNonFlagArg();
|
||||||
|
if (val == null) {
|
||||||
|
throw new ParseException(arg + " requires a numeric argument.");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return Integer.parseInt(val);
|
||||||
|
} catch (NumberFormatException ex) {
|
||||||
|
throw new ParseException(arg + " requires a numeric argument. found: " + val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleErrorCode(String arg, Errors.Level level) throws ParseException {
|
||||||
|
final int code = requireNextNumberArg(arg);
|
||||||
|
final Errors.Category category = mErrors.getCategories().get(code);
|
||||||
|
if (category == null) {
|
||||||
|
mErrors.add(mErrors.WARNING_UNKNOWN_COMMAND_LINE_ERROR,
|
||||||
|
"Unknown error code: " + code);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!category.isLevelSettable()) {
|
||||||
|
mErrors.add(mErrors.ERROR_COMMAND_LINE, "Can't set level for error " + code);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
category.setLevel(level);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the arguments and return an options object.
|
||||||
|
* <p>
|
||||||
|
* Updates errors with the hidden / warning / error levels.
|
||||||
|
* <p>
|
||||||
|
* Adds errors encountered to Errors object.
|
||||||
|
*/
|
||||||
|
public static Options parse(Errors errors, String[] args) {
|
||||||
|
return (new Parser(errors, args)).parse();
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.build.config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Position in a source file.
|
||||||
|
*/
|
||||||
|
public class Position implements Comparable<Position> {
|
||||||
|
/**
|
||||||
|
* Sentinel line number for when there is no known line number.
|
||||||
|
*/
|
||||||
|
public static final int NO_LINE = -1;
|
||||||
|
|
||||||
|
private final String mFile;
|
||||||
|
private final int mLine;
|
||||||
|
|
||||||
|
public Position() {
|
||||||
|
mFile = null;
|
||||||
|
mLine = NO_LINE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Position(String file) {
|
||||||
|
mFile = file;
|
||||||
|
mLine = NO_LINE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Position(String file, int line) {
|
||||||
|
if (line < NO_LINE) {
|
||||||
|
throw new IllegalArgumentException("Negative line number. file=" + file
|
||||||
|
+ " line=" + line);
|
||||||
|
}
|
||||||
|
mFile = file;
|
||||||
|
mLine = line;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int compareTo(Position that) {
|
||||||
|
int result = mFile.compareTo(that.mFile);
|
||||||
|
if (result != 0) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return mLine - that.mLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFile() {
|
||||||
|
return mFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLine() {
|
||||||
|
return mLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (mFile == null && mLine == NO_LINE) {
|
||||||
|
return "";
|
||||||
|
} else if (mFile == null && mLine != NO_LINE) {
|
||||||
|
return "<unknown>:" + mLine + ": ";
|
||||||
|
} else if (mFile != null && mLine == NO_LINE) {
|
||||||
|
return mFile + ": ";
|
||||||
|
} else { // if (mFile != null && mLine != NO_LINE)
|
||||||
|
return mFile + ':' + mLine + ": ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,121 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.build.config;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ErrorReporterTest {
|
||||||
|
/**
|
||||||
|
* Test that errors can be recorded and retrieved.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testAdding() {
|
||||||
|
TestErrors errors = new TestErrors();
|
||||||
|
|
||||||
|
errors.add(errors.ERROR, new Position("a", 12), "Errrororrrr");
|
||||||
|
|
||||||
|
Assert.assertTrue(errors.hadWarningOrError());
|
||||||
|
Assert.assertTrue(errors.hadError());
|
||||||
|
|
||||||
|
List<TestErrors.Entry> entries = errors.getEntries();
|
||||||
|
Assert.assertEquals(1, entries.size());
|
||||||
|
|
||||||
|
TestErrors.Entry entry = entries.get(0);
|
||||||
|
Assert.assertEquals(errors.ERROR, entry.getCategory());
|
||||||
|
Assert.assertEquals("a", entry.getPosition().getFile());
|
||||||
|
Assert.assertEquals(12, entry.getPosition().getLine());
|
||||||
|
Assert.assertEquals("Errrororrrr", entry.getMessage());
|
||||||
|
|
||||||
|
Assert.assertNotEquals("", errors.getErrorMessages());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that not adding an error doesn't record errors.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testNoError() {
|
||||||
|
TestErrors errors = new TestErrors();
|
||||||
|
|
||||||
|
Assert.assertFalse(errors.hadWarningOrError());
|
||||||
|
Assert.assertFalse(errors.hadError());
|
||||||
|
Assert.assertEquals("", errors.getErrorMessages());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that not adding a warning doesn't record errors.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testWarning() {
|
||||||
|
TestErrors errors = new TestErrors();
|
||||||
|
|
||||||
|
errors.add(errors.WARNING, "Waaaaarninggggg");
|
||||||
|
|
||||||
|
Assert.assertTrue(errors.hadWarningOrError());
|
||||||
|
Assert.assertFalse(errors.hadError());
|
||||||
|
Assert.assertNotEquals("", errors.getErrorMessages());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that hidden warnings don't report.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testHidden() {
|
||||||
|
TestErrors errors = new TestErrors();
|
||||||
|
|
||||||
|
errors.add(errors.HIDDEN, "Hidddeennn");
|
||||||
|
|
||||||
|
Assert.assertFalse(errors.hadWarningOrError());
|
||||||
|
Assert.assertFalse(errors.hadError());
|
||||||
|
Assert.assertEquals("", errors.getErrorMessages());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test changing an error level.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testSetLevel() {
|
||||||
|
TestErrors errors = new TestErrors();
|
||||||
|
Assert.assertEquals(TestErrors.Level.ERROR, errors.ERROR.getLevel());
|
||||||
|
|
||||||
|
errors.ERROR.setLevel(TestErrors.Level.WARNING);
|
||||||
|
|
||||||
|
Assert.assertEquals(TestErrors.Level.WARNING, errors.ERROR.getLevel());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that changing a fixed error fails.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testSetLevelFails() {
|
||||||
|
TestErrors errors = new TestErrors();
|
||||||
|
Assert.assertEquals(TestErrors.Level.ERROR, errors.ERROR_FIXED.getLevel());
|
||||||
|
|
||||||
|
boolean exceptionThrown = false;
|
||||||
|
try {
|
||||||
|
errors.ERROR_FIXED.setLevel(TestErrors.Level.WARNING);
|
||||||
|
} catch (RuntimeException ex) {
|
||||||
|
exceptionThrown = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.assertTrue(exceptionThrown);
|
||||||
|
Assert.assertEquals(TestErrors.Level.ERROR, errors.ERROR_FIXED.getLevel());
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,114 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.build.config;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class OptionsTest {
|
||||||
|
@Test
|
||||||
|
public void testErrorMissingLast() {
|
||||||
|
final Errors errors = new Errors();
|
||||||
|
|
||||||
|
final Options options = Options.parse(errors, new String[] {
|
||||||
|
"--error"
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.assertNotEquals("", TestErrors.getErrorMessages(errors));
|
||||||
|
Assert.assertEquals(Options.Action.DEFAULT, options.getAction());
|
||||||
|
TestErrors.assertHasEntry(errors.ERROR_COMMAND_LINE, errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testErrorMissingNotLast() {
|
||||||
|
final Errors errors = new Errors();
|
||||||
|
|
||||||
|
final Options options = Options.parse(errors, new String[] {
|
||||||
|
"--error", "--warning", "2"
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.assertNotEquals("", TestErrors.getErrorMessages(errors));
|
||||||
|
Assert.assertEquals(Options.Action.DEFAULT, options.getAction());
|
||||||
|
TestErrors.assertHasEntry(errors.ERROR_COMMAND_LINE, errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testErrorNotNumeric() {
|
||||||
|
final Errors errors = new Errors();
|
||||||
|
|
||||||
|
final Options options = Options.parse(errors, new String[] {
|
||||||
|
"--error", "notgood"
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.assertNotEquals("", TestErrors.getErrorMessages(errors));
|
||||||
|
Assert.assertEquals(Options.Action.DEFAULT, options.getAction());
|
||||||
|
TestErrors.assertHasEntry(errors.ERROR_COMMAND_LINE, errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testErrorInvalidError() {
|
||||||
|
final Errors errors = new Errors();
|
||||||
|
|
||||||
|
final Options options = Options.parse(errors, new String[] {
|
||||||
|
"--error", "50000"
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.assertEquals("", TestErrors.getErrorMessages(errors));
|
||||||
|
Assert.assertEquals(Options.Action.DEFAULT, options.getAction());
|
||||||
|
TestErrors.assertHasEntry(errors.WARNING_UNKNOWN_COMMAND_LINE_ERROR, errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testErrorOne() {
|
||||||
|
final Errors errors = new Errors();
|
||||||
|
|
||||||
|
final Options options = Options.parse(errors, new String[] {
|
||||||
|
"--error", "2"
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.assertEquals("", TestErrors.getErrorMessages(errors));
|
||||||
|
Assert.assertEquals(Options.Action.DEFAULT, options.getAction());
|
||||||
|
Assert.assertFalse(errors.hadWarningOrError());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWarningOne() {
|
||||||
|
final Errors errors = new Errors();
|
||||||
|
|
||||||
|
final Options options = Options.parse(errors, new String[] {
|
||||||
|
"--warning", "2"
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.assertEquals("", TestErrors.getErrorMessages(errors));
|
||||||
|
Assert.assertEquals(Options.Action.DEFAULT, options.getAction());
|
||||||
|
Assert.assertFalse(errors.hadWarningOrError());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHideOne() {
|
||||||
|
final Errors errors = new Errors();
|
||||||
|
|
||||||
|
final Options options = Options.parse(errors, new String[] {
|
||||||
|
"--hide", "2"
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.assertEquals("", TestErrors.getErrorMessages(errors));
|
||||||
|
Assert.assertEquals(Options.Action.DEFAULT, options.getAction());
|
||||||
|
Assert.assertFalse(errors.hadWarningOrError());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.build.config;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Errors for testing.
|
||||||
|
*/
|
||||||
|
public class TestErrors extends ErrorReporter {
|
||||||
|
|
||||||
|
public static final int ERROR_CODE = 1;
|
||||||
|
|
||||||
|
public final Category ERROR = new Category(ERROR_CODE, true, Level.ERROR,
|
||||||
|
"An error.");
|
||||||
|
|
||||||
|
public static final int WARNING_CODE = 2;
|
||||||
|
|
||||||
|
public final Category WARNING = new Category(WARNING_CODE, true, Level.WARNING,
|
||||||
|
"A warning.");
|
||||||
|
|
||||||
|
public static final int HIDDEN_CODE = 3;
|
||||||
|
|
||||||
|
public final Category HIDDEN = new Category(HIDDEN_CODE, true, Level.HIDDEN,
|
||||||
|
"A hidden warning.");
|
||||||
|
|
||||||
|
public static final int ERROR_FIXED_CODE = 4;
|
||||||
|
|
||||||
|
public final Category ERROR_FIXED = new Category(ERROR_FIXED_CODE, false, Level.ERROR,
|
||||||
|
"An error that can't have its level changed.");
|
||||||
|
|
||||||
|
public void assertHasEntry(Errors.Category category) {
|
||||||
|
assertHasEntry(category, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getErrorMessages() {
|
||||||
|
return getErrorMessages(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void assertHasEntry(Errors.Category category, ErrorReporter errors) {
|
||||||
|
StringBuilder found = new StringBuilder();
|
||||||
|
for (Errors.Entry entry: errors.getEntries()) {
|
||||||
|
if (entry.getCategory() == category) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
found.append(' ');
|
||||||
|
found.append(entry.getCategory().getCode());
|
||||||
|
}
|
||||||
|
throw new AssertionError("No error category " + category.getCode() + " found."
|
||||||
|
+ " Found category codes were:" + found);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getErrorMessages(ErrorReporter errors) {
|
||||||
|
final ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||||
|
try {
|
||||||
|
errors.printErrors(new PrintStream(stream, true, StandardCharsets.UTF_8.name()));
|
||||||
|
} catch (UnsupportedEncodingException ex) {
|
||||||
|
// utf-8 is always supported
|
||||||
|
}
|
||||||
|
return new String(stream.toByteArray(), StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Reference in New Issue
Block a user