mirror of
https://github.com/revanced/smali.git
synced 2025-05-28 11:50:12 +02:00
Implement a new command line interface for smali
This commit is contained in:
parent
5a5eafb818
commit
735746595f
@ -53,21 +53,21 @@ public class DumpCommand implements Command {
|
||||
|
||||
@Parameter(names = {"-h", "-?", "--help"}, help = true,
|
||||
description = "Show usage information for this command.")
|
||||
public boolean help;
|
||||
private boolean help;
|
||||
|
||||
@Parameter(names = {"-a", "--api"},
|
||||
description = "The numeric api level of the file being disassembled.")
|
||||
public int apiLevel = 15;
|
||||
private int apiLevel = 15;
|
||||
|
||||
@Parameter(names = "--experimental",
|
||||
description = "Enable experimental opcodes to be disassembled, even if they aren't necessarily " +
|
||||
"supported in the Android runtime yet.")
|
||||
public boolean experimentalOpcodes = false;
|
||||
private boolean experimentalOpcodes = false;
|
||||
|
||||
@Parameter(description = "<file> - A dex/apk/oat/odex file. For apk or oat files that contain multiple dex " +
|
||||
"files, you can specify which dex file to disassemble by appending the name of the dex file with a " +
|
||||
"colon. E.g. \"something.apk:classes2.dex\"")
|
||||
public String input;
|
||||
private String input;
|
||||
|
||||
public DumpCommand(@Nonnull JCommander jc) {
|
||||
this.jc = jc;
|
||||
|
@ -49,6 +49,7 @@ dependencies {
|
||||
compile project(':util')
|
||||
compile depends.findbugs
|
||||
compile depends.guava
|
||||
compile depends.commons_cli
|
||||
|
||||
testCompile depends.junit
|
||||
|
||||
|
@ -80,8 +80,8 @@ dependencies {
|
||||
compile project(':util')
|
||||
compile project(':dexlib2')
|
||||
compile depends.antlr_runtime
|
||||
compile depends.jcommander
|
||||
compile depends.stringtemplate
|
||||
compile depends.commons_cli
|
||||
|
||||
testCompile depends.junit
|
||||
|
||||
@ -100,7 +100,7 @@ task fatJar(type: Jar, dependsOn: jar) {
|
||||
classifier = 'fat'
|
||||
|
||||
manifest {
|
||||
attributes('Main-Class': 'org.jf.smali.main')
|
||||
attributes('Main-Class': 'org.jf.smali.Main')
|
||||
}
|
||||
|
||||
doLast {
|
||||
|
111
smali/src/main/java/org/jf/smali/AssembleCommand.java
Normal file
111
smali/src/main/java/org/jf/smali/AssembleCommand.java
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2016, Google Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.jf.smali;
|
||||
|
||||
import com.beust.jcommander.JCommander;
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.beust.jcommander.validators.PositiveInteger;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
@Parameters(commandDescription = "Assembles smali files into a dex file.")
|
||||
public class AssembleCommand implements Command {
|
||||
|
||||
@Nonnull private final JCommander jc;
|
||||
|
||||
@Parameter(names = {"-h", "-?", "--help"}, help = true,
|
||||
description = "Show usage information for this command.")
|
||||
private boolean help;
|
||||
|
||||
@Parameter(names = {"-j", "--jobs"},
|
||||
description = "The number of threads to use. Defaults to the number of cores available.",
|
||||
validateWith = PositiveInteger.class)
|
||||
private int jobs = Runtime.getRuntime().availableProcessors();
|
||||
|
||||
@Parameter(names = {"-a", "--api"},
|
||||
description = "The numeric api level to use while assembling.")
|
||||
private int apiLevel = 15;
|
||||
|
||||
@Parameter(names = {"-o", "--output"},
|
||||
description = "The location of the dex file to write.")
|
||||
private String output = "out.dex";
|
||||
|
||||
@Parameter(names = "--experimental",
|
||||
description = "Enable experimental opcodes to be assembled, even if they aren't necessarily " +
|
||||
"supported in the Android runtime yet.")
|
||||
private boolean experimentalOpcodes = false;
|
||||
|
||||
@Parameter(names = "--verbose",
|
||||
description = "Generate verbose error messages.")
|
||||
private boolean verbose = false;
|
||||
|
||||
@Parameter(names = "--allow-odex-opcodes",
|
||||
description = "Allows the odex opcodes that dalvik doesn't reject to be assembled.")
|
||||
private boolean allowOdexOpcodes;
|
||||
|
||||
@Parameter(description = "[<file>|<dir>]+ - Assembles the given files. If a directory is specified, it will be " +
|
||||
"recursively searched for any file with a .smali prefix")
|
||||
private List<String> input;
|
||||
|
||||
public AssembleCommand(@Nonnull JCommander jc) {
|
||||
this.jc = jc;
|
||||
}
|
||||
|
||||
@Override public void run() {
|
||||
if (help || input == null || input.isEmpty()) {
|
||||
jc.usage(jc.getParsedCommand());
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Smali.assemble(getOptions(), input);
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
protected SmaliOptions getOptions() {
|
||||
SmaliOptions options = new SmaliOptions();
|
||||
|
||||
options.jobs = jobs;
|
||||
options.apiLevel = apiLevel;
|
||||
options.outputDexFile = output;
|
||||
options.experimentalOpcodes = experimentalOpcodes;
|
||||
options.allowOdexOpcodes = allowOdexOpcodes;
|
||||
options.verboseErrors = verbose;
|
||||
|
||||
return options;
|
||||
}
|
||||
}
|
36
smali/src/main/java/org/jf/smali/Command.java
Normal file
36
smali/src/main/java/org/jf/smali/Command.java
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2016, Google Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Google Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.jf.smali;
|
||||
|
||||
public interface Command {
|
||||
void run();
|
||||
}
|
68
smali/src/main/java/org/jf/smali/HelpCommand.java
Normal file
68
smali/src/main/java/org/jf/smali/HelpCommand.java
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2016, Google Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.jf.smali;
|
||||
|
||||
import com.beust.jcommander.JCommander;
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.List;
|
||||
|
||||
@Parameters(commandDescription = "Shows usage information")
|
||||
public class HelpCommand implements Command {
|
||||
@Nonnull private final JCommander jc;
|
||||
|
||||
public HelpCommand(@Nonnull JCommander jc) {
|
||||
this.jc = jc;
|
||||
}
|
||||
|
||||
@Parameter(description = "If specified, only show the usage information for the given commands")
|
||||
private List<String> commands;
|
||||
|
||||
public void run() {
|
||||
if (commands == null || commands.isEmpty()) {
|
||||
jc.usage();
|
||||
} else {
|
||||
for (String cmd : commands) {
|
||||
jc.usage(cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Parameters(hidden = true)
|
||||
public static class HlepCommand extends HelpCommand {
|
||||
public HlepCommand(@Nonnull JCommander jc) {
|
||||
super(jc);
|
||||
}
|
||||
}
|
||||
}
|
99
smali/src/main/java/org/jf/smali/Main.java
Normal file
99
smali/src/main/java/org/jf/smali/Main.java
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright 2016, Google Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.jf.smali;
|
||||
|
||||
import com.beust.jcommander.JCommander;
|
||||
import com.beust.jcommander.Parameter;
|
||||
import org.jf.smali.HelpCommand.HlepCommand;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Properties;
|
||||
|
||||
public class Main {
|
||||
public static final String VERSION = loadVersion();
|
||||
|
||||
@Parameter(names = {"-h", "-?", "--help"}, help = true,
|
||||
description = "Show usage information")
|
||||
private boolean help;
|
||||
|
||||
@Parameter(names = {"-v", "--version"}, help = true,
|
||||
description = "Print the version of baksmali and then exit")
|
||||
public boolean version;
|
||||
|
||||
public static void main(String[] args) {
|
||||
Main main = new Main();
|
||||
|
||||
JCommander jc = new JCommander(main);
|
||||
|
||||
jc.addCommand("assemble", new AssembleCommand(jc), "a", "as");
|
||||
jc.addCommand("help", new HelpCommand(jc), "h");
|
||||
jc.addCommand("hlep", new HlepCommand(jc));
|
||||
|
||||
jc.parse(args);
|
||||
|
||||
if (jc.getParsedCommand() == null || main.help) {
|
||||
jc.usage();
|
||||
return;
|
||||
}
|
||||
|
||||
if (main.version) {
|
||||
version();
|
||||
return;
|
||||
}
|
||||
|
||||
Command command = (Command)jc.getCommands().get(jc.getParsedCommand()).getObjects().get(0);
|
||||
command.run();
|
||||
}
|
||||
|
||||
protected static void version() {
|
||||
System.out.println("smali " + VERSION + " (http://smali.googlecode.com)");
|
||||
System.out.println("Copyright (C) 2010 Ben Gruver (JesusFreke@JesusFreke.com)");
|
||||
System.out.println("BSD license (http://www.opensource.org/licenses/bsd-license.php)");
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
private static String loadVersion() {
|
||||
InputStream propertiesStream = Main.class.getClassLoader().getResourceAsStream("smali.properties");
|
||||
String version = "[unknown version]";
|
||||
if (propertiesStream != null) {
|
||||
Properties properties = new Properties();
|
||||
try {
|
||||
properties.load(propertiesStream);
|
||||
version = properties.getProperty("application.version");
|
||||
} catch (IOException ex) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
return version;
|
||||
}
|
||||
}
|
206
smali/src/main/java/org/jf/smali/Smali.java
Normal file
206
smali/src/main/java/org/jf/smali/Smali.java
Normal file
@ -0,0 +1,206 @@
|
||||
/*
|
||||
* Copyright 2016, Google Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.jf.smali;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import org.antlr.runtime.CommonTokenStream;
|
||||
import org.antlr.runtime.Token;
|
||||
import org.antlr.runtime.TokenSource;
|
||||
import org.antlr.runtime.tree.CommonTree;
|
||||
import org.antlr.runtime.tree.CommonTreeNodeStream;
|
||||
import org.jf.dexlib2.Opcodes;
|
||||
import org.jf.dexlib2.writer.builder.DexBuilder;
|
||||
import org.jf.dexlib2.writer.io.FileDataStore;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class Smali {
|
||||
|
||||
/**
|
||||
* Assemble the specified files, using the given options
|
||||
*
|
||||
* @param options a SmaliOptions object with the options to run smali with
|
||||
* @param input The files/directories to process
|
||||
* @return true if assembly completed with no errors, or false if errors were encountered
|
||||
*/
|
||||
public static boolean assemble(final SmaliOptions options, String... input) throws IOException {
|
||||
return assemble(options, Arrays.asList(input));
|
||||
}
|
||||
|
||||
/**
|
||||
* Assemble the specified files, using the given options
|
||||
*
|
||||
* @param options a SmaliOptions object with the options to run smali with
|
||||
* @param input The files/directories to process
|
||||
* @return true if assembly completed with no errors, or false if errors were encountered
|
||||
*/
|
||||
public static boolean assemble(final SmaliOptions options, List<String> input) throws IOException {
|
||||
LinkedHashSet<File> filesToProcessSet = new LinkedHashSet<File>();
|
||||
|
||||
for (String fileToProcess: input) {
|
||||
File argFile = new File(fileToProcess);
|
||||
|
||||
if (!argFile.exists()) {
|
||||
throw new IllegalArgumentException("Cannot find file or directory \"" + fileToProcess + "\"");
|
||||
}
|
||||
|
||||
if (argFile.isDirectory()) {
|
||||
getSmaliFilesInDir(argFile, filesToProcessSet);
|
||||
} else if (argFile.isFile()) {
|
||||
filesToProcessSet.add(argFile);
|
||||
}
|
||||
}
|
||||
|
||||
boolean errors = false;
|
||||
|
||||
final DexBuilder dexBuilder = DexBuilder.makeDexBuilder(
|
||||
Opcodes.forApi(options.apiLevel, options.experimentalOpcodes));
|
||||
|
||||
ExecutorService executor = Executors.newFixedThreadPool(options.jobs);
|
||||
List<Future<Boolean>> tasks = Lists.newArrayList();
|
||||
|
||||
for (final File file: filesToProcessSet) {
|
||||
tasks.add(executor.submit(new Callable<Boolean>() {
|
||||
@Override public Boolean call() throws Exception {
|
||||
return assembleSmaliFile(file, dexBuilder, options);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
for (Future<Boolean> task: tasks) {
|
||||
while(true) {
|
||||
try {
|
||||
try {
|
||||
if (!task.get()) {
|
||||
errors = true;
|
||||
}
|
||||
} catch (ExecutionException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
} catch (InterruptedException ex) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
executor.shutdown();
|
||||
|
||||
if (errors) {
|
||||
return false;
|
||||
}
|
||||
|
||||
dexBuilder.writeTo(new FileDataStore(new File(options.outputDexFile)));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void getSmaliFilesInDir(@Nonnull File dir, @Nonnull Set<File> smaliFiles) {
|
||||
File[] files = dir.listFiles();
|
||||
if (files != null) {
|
||||
for(File file: files) {
|
||||
if (file.isDirectory()) {
|
||||
getSmaliFilesInDir(file, smaliFiles);
|
||||
} else if (file.getName().endsWith(".smali")) {
|
||||
smaliFiles.add(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean assembleSmaliFile(File smaliFile, DexBuilder dexBuilder, SmaliOptions options)
|
||||
throws Exception {
|
||||
CommonTokenStream tokens;
|
||||
|
||||
LexerErrorInterface lexer;
|
||||
|
||||
FileInputStream fis = new FileInputStream(smaliFile);
|
||||
InputStreamReader reader = new InputStreamReader(fis, "UTF-8");
|
||||
|
||||
lexer = new smaliFlexLexer(reader);
|
||||
((smaliFlexLexer)lexer).setSourceFile(smaliFile);
|
||||
tokens = new CommonTokenStream((TokenSource)lexer);
|
||||
|
||||
if (options.printTokens) {
|
||||
tokens.getTokens();
|
||||
|
||||
for (int i=0; i<tokens.size(); i++) {
|
||||
Token token = tokens.get(i);
|
||||
if (token.getChannel() == smaliParser.HIDDEN) {
|
||||
continue;
|
||||
}
|
||||
|
||||
System.out.println(smaliParser.tokenNames[token.getType()] + ": " + token.getText());
|
||||
}
|
||||
|
||||
System.out.flush();
|
||||
}
|
||||
|
||||
smaliParser parser = new smaliParser(tokens);
|
||||
parser.setVerboseErrors(options.verboseErrors);
|
||||
parser.setAllowOdex(options.allowOdexOpcodes);
|
||||
parser.setApiLevel(options.apiLevel, options.experimentalOpcodes);
|
||||
|
||||
smaliParser.smali_file_return result = parser.smali_file();
|
||||
|
||||
if (parser.getNumberOfSyntaxErrors() > 0 || lexer.getNumberOfSyntaxErrors() > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CommonTree t = result.getTree();
|
||||
|
||||
CommonTreeNodeStream treeStream = new CommonTreeNodeStream(t);
|
||||
treeStream.setTokenStream(tokens);
|
||||
|
||||
if (options.printTokens) {
|
||||
System.out.println(t.toStringTree());
|
||||
}
|
||||
|
||||
smaliTreeWalker dexGen = new smaliTreeWalker(treeStream);
|
||||
dexGen.setApiLevel(options.apiLevel, options.experimentalOpcodes);
|
||||
|
||||
dexGen.setVerboseErrors(options.verboseErrors);
|
||||
dexGen.setDexBuilder(dexBuilder);
|
||||
dexGen.smali_file();
|
||||
|
||||
return dexGen.getNumberOfSyntaxErrors() == 0;
|
||||
}
|
||||
}
|
@ -36,17 +36,8 @@ public class SmaliOptions {
|
||||
public String outputDexFile = "out.dex";
|
||||
|
||||
public int jobs = Runtime.getRuntime().availableProcessors();
|
||||
public boolean allowOdex = false;
|
||||
public boolean allowOdexOpcodes = false;
|
||||
public boolean verboseErrors = false;
|
||||
public boolean printTokens = false;
|
||||
public boolean experimental = false;
|
||||
|
||||
public boolean listMethods = false;
|
||||
public String methodListFilename = null;
|
||||
|
||||
public boolean listFields = false;
|
||||
public String fieldListFilename = null;
|
||||
|
||||
public boolean listTypes = false;
|
||||
public String typeListFilename = null;
|
||||
public boolean experimentalOpcodes = false;
|
||||
}
|
||||
|
@ -1,491 +0,0 @@
|
||||
/*
|
||||
* [The "BSD licence"]
|
||||
* Copyright (c) 2010 Ben Gruver (JesusFreke)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.jf.smali;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Ordering;
|
||||
import org.antlr.runtime.CommonTokenStream;
|
||||
import org.antlr.runtime.Token;
|
||||
import org.antlr.runtime.TokenSource;
|
||||
import org.antlr.runtime.tree.CommonTree;
|
||||
import org.antlr.runtime.tree.CommonTreeNodeStream;
|
||||
import org.apache.commons.cli.*;
|
||||
import org.jf.dexlib2.Opcodes;
|
||||
import org.jf.dexlib2.writer.builder.DexBuilder;
|
||||
import org.jf.dexlib2.writer.io.FileDataStore;
|
||||
import org.jf.util.ConsoleUtil;
|
||||
import org.jf.util.SmaliHelpFormatter;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
/**
|
||||
* Main class for smali. It recognizes enough options to be able to dispatch
|
||||
* to the right "actual" main.
|
||||
*/
|
||||
public class main {
|
||||
|
||||
public static final String VERSION;
|
||||
|
||||
private final static Options basicOptions;
|
||||
private final static Options debugOptions;
|
||||
private final static Options options;
|
||||
|
||||
static {
|
||||
basicOptions = new Options();
|
||||
debugOptions = new Options();
|
||||
options = new Options();
|
||||
buildOptions();
|
||||
|
||||
InputStream templateStream = main.class.getClassLoader().getResourceAsStream("smali.properties");
|
||||
if (templateStream != null) {
|
||||
Properties properties = new Properties();
|
||||
String version = "(unknown)";
|
||||
try {
|
||||
properties.load(templateStream);
|
||||
version = properties.getProperty("application.version");
|
||||
} catch (IOException ex) {
|
||||
// just eat it
|
||||
}
|
||||
VERSION = version;
|
||||
} else {
|
||||
VERSION = "[unknown version]";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This class is uninstantiable.
|
||||
*/
|
||||
private main() {
|
||||
}
|
||||
|
||||
/**
|
||||
* A more programmatic-friendly entry point for smali
|
||||
*
|
||||
* @param options a SmaliOptions object with the options to run smali with
|
||||
* @param input The files/directories to process
|
||||
* @return true if assembly completed with no errors, or false if errors were encountered
|
||||
*/
|
||||
public static boolean run(final SmaliOptions options, String... input) throws IOException {
|
||||
LinkedHashSet<File> filesToProcessSet = new LinkedHashSet<File>();
|
||||
|
||||
for (String fileToProcess: input) {
|
||||
File argFile = new File(fileToProcess);
|
||||
|
||||
if (!argFile.exists()) {
|
||||
throw new IllegalArgumentException("Cannot find file or directory \"" + fileToProcess + "\"");
|
||||
}
|
||||
|
||||
if (argFile.isDirectory()) {
|
||||
getSmaliFilesInDir(argFile, filesToProcessSet);
|
||||
} else if (argFile.isFile()) {
|
||||
filesToProcessSet.add(argFile);
|
||||
}
|
||||
}
|
||||
|
||||
boolean errors = false;
|
||||
|
||||
final DexBuilder dexBuilder = DexBuilder.makeDexBuilder(
|
||||
Opcodes.forApi(options.apiLevel, options.experimental));
|
||||
|
||||
ExecutorService executor = Executors.newFixedThreadPool(options.jobs);
|
||||
List<Future<Boolean>> tasks = Lists.newArrayList();
|
||||
|
||||
for (final File file: filesToProcessSet) {
|
||||
tasks.add(executor.submit(new Callable<Boolean>() {
|
||||
@Override public Boolean call() throws Exception {
|
||||
return assembleSmaliFile(file, dexBuilder, options);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
for (Future<Boolean> task: tasks) {
|
||||
while(true) {
|
||||
try {
|
||||
try {
|
||||
if (!task.get()) {
|
||||
errors = true;
|
||||
}
|
||||
} catch (ExecutionException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
} catch (InterruptedException ex) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
executor.shutdown();
|
||||
|
||||
if (errors) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (options.listMethods) {
|
||||
if (Strings.isNullOrEmpty(options.methodListFilename)) {
|
||||
options.methodListFilename = options.outputDexFile + ".methods";
|
||||
}
|
||||
writeReferences(dexBuilder.getMethodReferences(), options.methodListFilename);
|
||||
}
|
||||
|
||||
if (options.listFields) {
|
||||
if (Strings.isNullOrEmpty(options.fieldListFilename)) {
|
||||
options.fieldListFilename = options.outputDexFile + ".fields";
|
||||
}
|
||||
writeReferences(dexBuilder.getFieldReferences(), options.fieldListFilename);
|
||||
}
|
||||
|
||||
if (options.listTypes) {
|
||||
if (Strings.isNullOrEmpty(options.typeListFilename)) {
|
||||
options.typeListFilename = options.outputDexFile + ".types";
|
||||
}
|
||||
writeReferences(dexBuilder.getTypeReferences(), options.typeListFilename);
|
||||
}
|
||||
|
||||
dexBuilder.writeTo(new FileDataStore(new File(options.outputDexFile)));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run!
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
Locale locale = new Locale("en", "US");
|
||||
Locale.setDefault(locale);
|
||||
|
||||
CommandLineParser parser = new PosixParser();
|
||||
CommandLine commandLine;
|
||||
|
||||
try {
|
||||
commandLine = parser.parse(options, args);
|
||||
} catch (ParseException ex) {
|
||||
usage();
|
||||
return;
|
||||
}
|
||||
|
||||
SmaliOptions smaliOptions = new SmaliOptions();
|
||||
|
||||
String[] remainingArgs = commandLine.getArgs();
|
||||
|
||||
Option[] options = commandLine.getOptions();
|
||||
|
||||
for (int i=0; i<options.length; i++) {
|
||||
Option option = options[i];
|
||||
String opt = option.getOpt();
|
||||
|
||||
switch (opt.charAt(0)) {
|
||||
case 'v':
|
||||
version();
|
||||
return;
|
||||
case '?':
|
||||
while (++i < options.length) {
|
||||
if (options[i].getOpt().charAt(0) == '?') {
|
||||
usage(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
usage(false);
|
||||
return;
|
||||
case 'o':
|
||||
smaliOptions.outputDexFile = commandLine.getOptionValue("o");
|
||||
break;
|
||||
case 'x':
|
||||
smaliOptions.allowOdex = true;
|
||||
break;
|
||||
case 'X':
|
||||
smaliOptions.experimental = true;
|
||||
break;
|
||||
case 'a':
|
||||
smaliOptions.apiLevel = Integer.parseInt(commandLine.getOptionValue("a"));
|
||||
break;
|
||||
case 'j':
|
||||
smaliOptions.jobs = Integer.parseInt(commandLine.getOptionValue("j"));
|
||||
break;
|
||||
case 'm':
|
||||
smaliOptions.listMethods = true;
|
||||
smaliOptions.methodListFilename = commandLine.getOptionValue("m");
|
||||
break;
|
||||
case 'f':
|
||||
smaliOptions.listFields = true;
|
||||
smaliOptions.fieldListFilename = commandLine.getOptionValue("f");
|
||||
break;
|
||||
case 't':
|
||||
smaliOptions.listTypes = true;
|
||||
smaliOptions.typeListFilename = commandLine.getOptionValue("t");
|
||||
break;
|
||||
case 'V':
|
||||
smaliOptions.verboseErrors = true;
|
||||
break;
|
||||
case 'T':
|
||||
smaliOptions.printTokens = true;
|
||||
break;
|
||||
default:
|
||||
assert false;
|
||||
}
|
||||
}
|
||||
|
||||
if (remainingArgs.length == 0) {
|
||||
usage();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!run(smaliOptions, remainingArgs)) {
|
||||
System.exit(1);
|
||||
}
|
||||
} catch (RuntimeException ex) {
|
||||
System.err.println("\nUNEXPECTED TOP-LEVEL EXCEPTION:");
|
||||
ex.printStackTrace();
|
||||
System.exit(2);
|
||||
} catch (Throwable ex) {
|
||||
System.err.println("\nUNEXPECTED TOP-LEVEL ERROR:");
|
||||
ex.printStackTrace();
|
||||
System.exit(3);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeReferences(List<String> references, String filename) {
|
||||
PrintWriter writer = null;
|
||||
try {
|
||||
writer = new PrintWriter(new BufferedWriter(new FileWriter(filename)));
|
||||
|
||||
for (String reference: Ordering.natural().sortedCopy(references)) {
|
||||
writer.println(reference);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
} finally {
|
||||
if (writer != null) {
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void getSmaliFilesInDir(@Nonnull File dir, @Nonnull Set<File> smaliFiles) {
|
||||
File[] files = dir.listFiles();
|
||||
if (files != null) {
|
||||
for(File file: files) {
|
||||
if (file.isDirectory()) {
|
||||
getSmaliFilesInDir(file, smaliFiles);
|
||||
} else if (file.getName().endsWith(".smali")) {
|
||||
smaliFiles.add(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean assembleSmaliFile(File smaliFile, DexBuilder dexBuilder, SmaliOptions options)
|
||||
throws Exception {
|
||||
CommonTokenStream tokens;
|
||||
|
||||
LexerErrorInterface lexer;
|
||||
|
||||
FileInputStream fis = new FileInputStream(smaliFile);
|
||||
InputStreamReader reader = new InputStreamReader(fis, "UTF-8");
|
||||
|
||||
lexer = new smaliFlexLexer(reader);
|
||||
((smaliFlexLexer)lexer).setSourceFile(smaliFile);
|
||||
tokens = new CommonTokenStream((TokenSource)lexer);
|
||||
|
||||
if (options.printTokens) {
|
||||
tokens.getTokens();
|
||||
|
||||
for (int i=0; i<tokens.size(); i++) {
|
||||
Token token = tokens.get(i);
|
||||
if (token.getChannel() == smaliParser.HIDDEN) {
|
||||
continue;
|
||||
}
|
||||
|
||||
System.out.println(smaliParser.tokenNames[token.getType()] + ": " + token.getText());
|
||||
}
|
||||
|
||||
System.out.flush();
|
||||
}
|
||||
|
||||
smaliParser parser = new smaliParser(tokens);
|
||||
parser.setVerboseErrors(options.verboseErrors);
|
||||
parser.setAllowOdex(options.allowOdex);
|
||||
parser.setApiLevel(options.apiLevel, options.experimental);
|
||||
|
||||
smaliParser.smali_file_return result = parser.smali_file();
|
||||
|
||||
if (parser.getNumberOfSyntaxErrors() > 0 || lexer.getNumberOfSyntaxErrors() > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CommonTree t = result.getTree();
|
||||
|
||||
CommonTreeNodeStream treeStream = new CommonTreeNodeStream(t);
|
||||
treeStream.setTokenStream(tokens);
|
||||
|
||||
if (options.printTokens) {
|
||||
System.out.println(t.toStringTree());
|
||||
}
|
||||
|
||||
smaliTreeWalker dexGen = new smaliTreeWalker(treeStream);
|
||||
dexGen.setApiLevel(options.apiLevel, options.experimental);
|
||||
|
||||
dexGen.setVerboseErrors(options.verboseErrors);
|
||||
dexGen.setDexBuilder(dexBuilder);
|
||||
dexGen.smali_file();
|
||||
|
||||
return dexGen.getNumberOfSyntaxErrors() == 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prints the usage message.
|
||||
*/
|
||||
private static void usage(boolean printDebugOptions) {
|
||||
SmaliHelpFormatter formatter = new SmaliHelpFormatter();
|
||||
|
||||
int consoleWidth = ConsoleUtil.getConsoleWidth();
|
||||
if (consoleWidth <= 0) {
|
||||
consoleWidth = 80;
|
||||
}
|
||||
|
||||
formatter.setWidth(consoleWidth);
|
||||
|
||||
formatter.printHelp("java -jar smali.jar [options] [--] [<smali-file>|folder]*",
|
||||
"assembles a set of smali files into a dex file", basicOptions, printDebugOptions?debugOptions:null);
|
||||
}
|
||||
|
||||
private static void usage() {
|
||||
usage(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the version message.
|
||||
*/
|
||||
private static void version() {
|
||||
System.out.println("smali " + VERSION + " (http://smali.googlecode.com)");
|
||||
System.out.println("Copyright (C) 2010 Ben Gruver (JesusFreke@JesusFreke.com)");
|
||||
System.out.println("BSD license (http://www.opensource.org/licenses/bsd-license.php)");
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
@SuppressWarnings("AccessStaticViaInstance")
|
||||
private static void buildOptions() {
|
||||
Option versionOption = OptionBuilder.withLongOpt("version")
|
||||
.withDescription("prints the version then exits")
|
||||
.create("v");
|
||||
|
||||
Option helpOption = OptionBuilder.withLongOpt("help")
|
||||
.withDescription("prints the help message then exits. Specify twice for debug options")
|
||||
.create("?");
|
||||
|
||||
Option outputOption = OptionBuilder.withLongOpt("output")
|
||||
.withDescription("the name of the dex file that will be written. The default is out.dex")
|
||||
.hasArg()
|
||||
.withArgName("FILE")
|
||||
.create("o");
|
||||
|
||||
Option allowOdexOption = OptionBuilder.withLongOpt("allow-odex-instructions")
|
||||
.withDescription("allow odex instructions to be compiled into the dex file. Only a few" +
|
||||
" instructions are supported - the ones that can exist in a dead code path and not" +
|
||||
" cause dalvik to reject the class")
|
||||
.create("x");
|
||||
|
||||
Option apiLevelOption = OptionBuilder.withLongOpt("api-level")
|
||||
.withDescription("The numeric api-level of the file to generate, e.g. 14 for ICS. If not " +
|
||||
"specified, it defaults to 15 (ICS).")
|
||||
.hasArg()
|
||||
.withArgName("API_LEVEL")
|
||||
.create("a");
|
||||
|
||||
Option listMethodsOption = OptionBuilder.withLongOpt("list-methods")
|
||||
.withDescription("Lists all the method references to FILE" +
|
||||
" (<output_dex_filename>.methods by default)")
|
||||
.hasOptionalArg()
|
||||
.withArgName("FILE")
|
||||
.create("m");
|
||||
|
||||
Option listFieldsOption = OptionBuilder.withLongOpt("list-fields")
|
||||
.withDescription("Lists all the field references to FILE" +
|
||||
" (<output_dex_filename>.fields by default)")
|
||||
.hasOptionalArg()
|
||||
.withArgName("FILE")
|
||||
.create("f");
|
||||
|
||||
Option listClassesOption = OptionBuilder.withLongOpt("list-types")
|
||||
.withDescription("Lists all the type references to FILE" +
|
||||
" (<output_dex_filename>.types by default)")
|
||||
.hasOptionalArg()
|
||||
.withArgName("FILE")
|
||||
.create("t");
|
||||
|
||||
Option experimentalOption = OptionBuilder.withLongOpt("experimental")
|
||||
.withDescription("enable experimental opcodes to be assembled, even if they " +
|
||||
" aren't necessarily supported by the Android runtime yet")
|
||||
.create("X");
|
||||
|
||||
Option jobsOption = OptionBuilder.withLongOpt("jobs")
|
||||
.withDescription("The number of threads to use. Defaults to the number of cores available, up to a " +
|
||||
"maximum of 6")
|
||||
.hasArg()
|
||||
.withArgName("NUM_THREADS")
|
||||
.create("j");
|
||||
|
||||
Option verboseErrorsOption = OptionBuilder.withLongOpt("verbose-errors")
|
||||
.withDescription("Generate verbose error messages")
|
||||
.create("V");
|
||||
|
||||
Option printTokensOption = OptionBuilder.withLongOpt("print-tokens")
|
||||
.withDescription("Print the name and text of each token")
|
||||
.create("T");
|
||||
|
||||
basicOptions.addOption(versionOption);
|
||||
basicOptions.addOption(helpOption);
|
||||
basicOptions.addOption(outputOption);
|
||||
basicOptions.addOption(allowOdexOption);
|
||||
basicOptions.addOption(apiLevelOption);
|
||||
basicOptions.addOption(experimentalOption);
|
||||
basicOptions.addOption(jobsOption);
|
||||
basicOptions.addOption(listMethodsOption);
|
||||
basicOptions.addOption(listFieldsOption);
|
||||
basicOptions.addOption(listClassesOption);
|
||||
|
||||
debugOptions.addOption(verboseErrorsOption);
|
||||
debugOptions.addOption(printTokensOption);
|
||||
|
||||
for (Object option: basicOptions.getOptions()) {
|
||||
options.addOption((Option)option);
|
||||
}
|
||||
|
||||
for (Object option: debugOptions.getOptions()) {
|
||||
options.addOption((Option)option);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user