am e2962681: Merge "Overhaul of intellij-gen script."

* commit 'e2962681b6f73dba3ff2ac7e63a6f70901fc1751':
  Overhaul of intellij-gen script.
This commit is contained in:
Chiao Cheng
2014-04-30 18:09:33 +00:00
committed by Android Git Automerger
11 changed files with 667 additions and 781 deletions

View File

@@ -38,8 +38,8 @@ if [ ! -e $root_dir/.repo ]; then
exit 1
fi
fi
tmp_file=tmp.txt
dest_file=module-index.txt
tmp_file=${root_dir}/tmp.txt
dest_file=${root_dir}/module-index.txt
echo "Generating index file $dest_file..."
start=$(($(date +%s%N) / 1000000))

View File

@@ -32,13 +32,15 @@
set -e
progname=`basename $0`
if [ $# -ne 1 ]
if [ $# -lt 2 ]
then
echo "Usage: $progname <module_name>"
echo "Usage: $progname project_dir module_dir <module_dir>..."
exit 1
fi
module_name=$1
project_dir=${PWD}/$1
shift
module_dirs=$@
echo $module_dirs
script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
root_dir=$PWD
if [ ! -e $root_dir/.repo ]; then
@@ -63,8 +65,8 @@ fi
echo "Checking for $idegenjar"
if [ -e "$idegenjar" ]; then
echo "Generating project files for $module_name"
cmd="java -cp $idegenjar com.android.idegen.IntellijProject $index_file $module_name"
echo "Generating project files for $module_dirs"
cmd="java -cp $idegenjar com.android.idegen.IntellijProject $index_file $project_dir $module_dirs"
echo $cmd
$cmd
else

View File

@@ -1,132 +0,0 @@
/*
* Copyright (C) 2012 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.idegen;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;
/**
* Module while is a composition of many other modules.
* <p>
* This is needed since intellij does not allow two modules to share the same content root.
*/
public class AggregatedModule extends Module {
private static final Logger logger = Logger.getLogger(AggregatedModule.class.getName());
private String aggregatedModuleName;
private Set<Module> modules;
private HashSet<String> directDependencies = Sets.newHashSet();
public AggregatedModule(String aggregatedName, Set<Module> modules) {
this.aggregatedModuleName = Preconditions.checkNotNull(aggregatedName);
this.modules = Preconditions.checkNotNull(modules);
}
public void build() throws IOException {
// Create an iml file that contains all the srcs of modules.
buildDependentModules();
buildDirectDependencies();
//buildImlFile();
}
@Override
protected File getDir() {
// All modules should be in the same directory so just pull the first.
return modules.iterator().next().getDir();
}
@Override
protected boolean isAndroidModule() {
for (Module module : modules) {
if (module.isAndroidModule()) {
return true;
}
}
return false;
}
@Override
protected List<File> getIntermediatesDirs() {
List<File> result = Lists.newArrayList();
for (Module module : modules) {
Iterables.addAll(result, module.getIntermediatesDirs());
}
return result;
}
public void buildDirectDependencies() {
for (Module module : modules) {
Set<String> deps = module.getDirectDependencies();
directDependencies.addAll(deps);
}
}
@Override
public Set<String> getDirectDependencies() {
return directDependencies;
}
@Override
protected ImmutableList<File> getSourceDirs() {
ImmutableList.Builder<File> builder = ImmutableList.builder();
for (Module module : modules) {
builder.addAll(module.getSourceDirs());
}
return builder.build();
}
@Override
protected ImmutableList<File> getExcludeDirs() {
ImmutableList.Builder<File> builder = ImmutableList.builder();
for (Module module : modules) {
builder.addAll(module.getExcludeDirs());
}
return builder.build();
}
@Override
public Set<File> getAllDependentImlFiles() {
Set<File> result = Sets.newHashSet();
for (Module module : modules) {
result.addAll(module.getAllDependentImlFiles());
}
return result;
}
@Override
public File getRepoRoot() {
return modules.iterator().next().getRepoRoot();
}
@Override
public String getName() {
return aggregatedModuleName;
}
}

View File

@@ -23,6 +23,7 @@ import com.google.common.collect.Sets;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.HashSet;
import java.util.logging.Level;
@@ -37,7 +38,8 @@ public class DirectorySearch {
private static final Logger logger = Logger.getLogger(DirectorySearch.class.getName());
private static final HashSet<String> SOURCE_DIRS = Sets.newHashSet();
public static final HashSet<String> SOURCE_DIRS = Sets.newHashSet();
static {
SOURCE_DIRS.add("src");
SOURCE_DIRS.add("java");
@@ -50,38 +52,72 @@ public class DirectorySearch {
public static final String REL_TEMPLATE_PATH_FROM_ROOT = "development/tools/idegen/"
+ REL_TEMPLATE_DIR;
/**
* Returns the previously initialized repo root.
*/
public static File getRepoRoot() {
Preconditions.checkNotNull(repoRoot, "repoRoot has not been initialized yet. Call "
+ "findAndInitRepoRoot() first.");
return repoRoot;
}
/**
* Find the repo root. This is the root branch directory of a full repo checkout.
*
* @param file any file inside the root.
* @return the root directory.
*/
public static File findRepoRoot(File file) {
public static void findAndInitRepoRoot(File file) {
Preconditions.checkNotNull(file);
if (repoRoot != null) {
return repoRoot;
return;
}
if (file.isDirectory()) {
File[] files = file.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
if (".repo".equals(name)) {
return true;
}
return false;
return ".repo".equals(name);
}
});
if (files.length > 0) {
repoRoot = file;
return file;
}
}
File parent = file.getParentFile();
if (parent == null) {
return null;
throw new IllegalStateException("Repo root not found from starting point " +
file.getPath());
}
return findRepoRoot(parent);
findAndInitRepoRoot(parent);
}
/**
* Searches up the parent chain to find the closes module root directory. A module root is one
* with an Android.mk file in it. <p> For example, the module root for directory
* <code>package/apps/Contacts/src</code> is <code>packages/apps/Contacts</code>
*
* @return the module root.
* @throws IOException when module root is not found.
*/
public static File findModuleRoot(File path) throws IOException {
Preconditions.checkNotNull(path);
File dir;
if (path.isFile()) {
dir = path.getParentFile();
} else {
dir = path;
}
while (dir != null) {
File makeFile = new File(dir, "Android.mk");
if (makeFile.exists()) {
return dir;
} else {
dir = dir.getParentFile();
}
}
// At this point, there are no parents and we have not found a module. Error.
throw new IOException("Module root not found for path " + path.getCanonicalPath());
}
/**
@@ -105,10 +141,19 @@ public class DirectorySearch {
File[] children = file.listFiles();
for (File child : children) {
if (child.isDirectory()) {
if (SOURCE_DIRS.contains(child.getName())) {
builder.add(child);
// Recurse further down the tree first to cover case of:
//
// src/java
// or
// java/src
//
// In either of these cases, we don't want the parent.
ImmutableList<File> dirs = findSourceDirs(child);
if (dirs.isEmpty()) {
if (SOURCE_DIRS.contains(child.getName())) {
builder.add(child);
}
} else {
ImmutableList<File> dirs = findSourceDirs(child);
builder.addAll(dirs);
}
}
@@ -151,20 +196,28 @@ public class DirectorySearch {
private static File templateDirCurrent = null;
private static File templateDirRoot = null;
public static File findTemplateDir() throws FileNotFoundException {
public static File findTemplateDir() throws IOException {
// Cache optimization.
if (templateDirCurrent != null && templateDirCurrent.exists()) return templateDirCurrent;
if (templateDirRoot != null && templateDirRoot.exists()) return templateDirRoot;
if (templateDirCurrent != null && templateDirCurrent.exists()) {
return templateDirCurrent;
}
if (templateDirRoot != null && templateDirRoot.exists()) {
return templateDirRoot;
}
File currentDir = null;
try {
currentDir = new File(IntellijProject.class.getProtectionDomain().getCodeSource()
.getLocation().toURI().getPath()).getParentFile();
currentDir = new File(
IntellijProject.class.getProtectionDomain().getCodeSource().getLocation()
.toURI().getPath()).getParentFile();
} catch (URISyntaxException e) {
logger.log(Level.SEVERE, "Could not get jar location.", e);
return null;
}
// Support for program execution in intellij.
if (currentDir.getPath().endsWith("out/production")) {
return new File(currentDir.getParentFile().getParentFile(), REL_TEMPLATE_DIR);
}
// First check relative to current run directory.
templateDirCurrent = new File(currentDir, REL_TEMPLATE_DIR);
if (templateDirCurrent.exists()) {
@@ -178,7 +231,7 @@ public class DirectorySearch {
}
throw new FileNotFoundException(
"Unable to find template dir. Tried the following locations:\n" +
templateDirCurrent.getAbsolutePath() + "\n" +
templateDirRoot.getAbsolutePath());
templateDirCurrent.getCanonicalPath() + "\n" +
templateDirRoot.getCanonicalPath());
}
}

View File

@@ -16,33 +16,35 @@
package com.android.idegen;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import java.io.File;
import java.io.IOException;
/**
* Special module used for framework to build one off resource directory.
*/
public class FrameworkModule extends StandardModule {
public class FrameworkModule extends Module {
// Framework needs a special constant for it's intermediates because it does not follow
// normal conventions.
private static final String FRAMEWORK_INTERMEDIATES = "framework-res_intermediates";
public FrameworkModule(String moduleName, String makeFile) {
super(IntellijProject.FRAMEWORK_MODULE, makeFile, true);
public FrameworkModule(File moduleDir) throws IOException {
super(Preconditions.checkNotNull(moduleDir), false);
}
@Override
protected String buildIntermediates() {
protected String buildIntermediates() throws IOException {
StringBuilder sb = new StringBuilder();
File intermediates = new File(repoRoot,
File intermediates = new File(DirectorySearch.getRepoRoot(),
REL_OUT_APP_DIR + File.separator + FRAMEWORK_INTERMEDIATES);
ImmutableList<File> intermediateSrcDirs = DirectorySearch.findSourceDirs(intermediates);
sb.append(" <content url=\"file://").append(intermediates).append("\">\n");
for (File src : intermediateSrcDirs) {
sb.append(" <sourceFolder url=\"file://")
.append(src.getAbsolutePath()).append("\" isTestSource=\"false\" />\n");
.append(src.getCanonicalPath()).append("\" isTestSource=\"false\" />\n");
}
sb.append(" </content>\n");
return sb.toString();

View File

@@ -17,14 +17,14 @@
package com.android.idegen;
import com.google.common.base.Preconditions;
import com.google.common.collect.Sets;
import com.google.common.collect.Lists;
import com.google.common.io.Files;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Set;
import java.util.logging.Logger;
/**
@@ -32,7 +32,7 @@ import java.util.logging.Logger;
*/
public class IntellijProject {
public static final String FRAMEWORK_MODULE = "framework";
public static final String FRAMEWORK_MODULE_DIR = "frameworks/base";
public static final Charset CHARSET = Charset.forName("UTF-8");
private static final Logger logger = Logger.getLogger(IntellijProject.class.getName());
@@ -42,147 +42,116 @@ public class IntellijProject {
ModuleCache cache = ModuleCache.getInstance();
boolean buildFramework;
File indexFile;
File repoRoot;
File projectIdeaDir;
String moduleName;
File projectPath;
ArrayList<String> moduleDirs;
public IntellijProject(String indexFile, String moduleName) {
public IntellijProject(String indexFile, String projectPath, ArrayList<String> moduleDirs,
boolean buildFramework) {
this.indexFile = new File(Preconditions.checkNotNull(indexFile));
this.moduleName = Preconditions.checkNotNull(moduleName);
}
private void init() throws IOException {
repoRoot = DirectorySearch.findRepoRoot(indexFile);
cache.init(indexFile);
this.projectPath = new File(Preconditions.checkNotNull(projectPath));
this.moduleDirs = Preconditions.checkNotNull(moduleDirs);
this.buildFramework = buildFramework;
DirectorySearch.findAndInitRepoRoot(this.indexFile);
}
public void build() throws IOException {
init();
buildFrameWorkModule();
// First pass, find all dependencies and cache them.
Module module = cache.getAndCache(moduleName);
if (module == null) {
logger.info("Module '" + moduleName + "' not found." +
" Module names are case senstive.");
return;
}
projectIdeaDir = new File(module.getDir(), ".idea");
projectIdeaDir.mkdir();
copyTemplates();
// Second phase, build aggregate modules.
Set<String> deps = module.getAllDependencies();
for (String dep : deps) {
cache.buildAndCacheAggregatedModule(dep);
cache.init(indexFile);
File repoRoot = DirectorySearch.getRepoRoot();
if (buildFramework) {
File frameworkDir = new File(repoRoot, FRAMEWORK_MODULE_DIR);
// Some unbundled apps/branches do not include the framework.
if (frameworkDir.exists()) {
buildFrameWorkModule(new File(repoRoot, FRAMEWORK_MODULE_DIR));
}
}
// Third phase, replace individual modules with aggregated modules
Iterable<Module> modules = cache.getModules();
for (Module mod : modules) {
replaceWithAggregate(mod);
for (String moduleDir : moduleDirs) {
// First pass, find all dependencies and cache them.
File dir = new File(repoRoot, moduleDir);
if (!dir.exists()) {
logger.info("Directory " + moduleDir + " does not exist in " + repoRoot +
". Are you sure the directory is correct?");
return;
}
Module module = cache.getAndCacheByDir(dir);
if (module == null) {
logger.info("Module '" + dir.getPath() + "' not found." +
" Module names are case senstive.");
return;
}
}
// Finally create iml files for dependencies
Iterable<Module> modules = cache.getModules();
for (Module mod : modules) {
mod.buildImlFile();
}
createModulesFile(module);
createVcsFile(module);
createNameFile(moduleName);
createProjectFiles();
}
private void replaceWithAggregate(Module module) {
replaceWithAggregate(module.getDirectDependencies(), module.getName());
replaceWithAggregate(module.getAllDependencies(), module.getName());
}
private void replaceWithAggregate(Set<String> deps, String moduleName) {
for (String dep : Sets.newHashSet(deps)) {
String replacement = cache.getAggregateReplacementName(dep);
if (replacement != null) {
deps.remove(dep);
// There could be dependencies on self due to aggregation.
// Only add if the replacement is not self.
if (!replacement.equals(moduleName)) {
deps.add(replacement);
}
}
}
private void createProjectFiles() throws IOException {
File ideaDir = new File(projectPath, ".idea");
ideaDir.mkdirs();
copyTemplates(ideaDir);
createModulesFile(ideaDir, cache.getModules());
createVcsFile(ideaDir, cache.getModules());
createNameFile(ideaDir, projectPath.getName());
}
/**
* Framework module needs special handling due to one off resource path:
* frameworks/base/Android.mk
*/
private void buildFrameWorkModule() throws IOException {
String makeFile = cache.getMakeFile(FRAMEWORK_MODULE);
if (makeFile == null) {
logger.warning("Unable to find framework module: " + FRAMEWORK_MODULE +
". Skipping.");
} else {
logger.info("makefile: " + makeFile);
StandardModule frameworkModule = new FrameworkModule(FRAMEWORK_MODULE,
makeFile);
frameworkModule.build();
cache.put(frameworkModule);
}
private void buildFrameWorkModule(File frameworkModuleDir) throws IOException {
FrameworkModule frameworkModule = new FrameworkModule(frameworkModuleDir);
frameworkModule.build();
cache.put(frameworkModule);
}
private void createModulesFile(Module module) throws IOException {
String modulesContent = Files.toString(
new File(DirectorySearch.findTemplateDir(),
"idea" + File.separator + MODULES_TEMPLATE_FILE_NAME),
CHARSET);
private void createModulesFile(File ideaDir, Iterable<Module> modules) throws IOException {
String modulesContent = Files.toString(new File(DirectorySearch.findTemplateDir(),
"idea" + File.separator + MODULES_TEMPLATE_FILE_NAME), CHARSET);
StringBuilder sb = new StringBuilder();
File moduleIml = module.getImlFile();
sb.append(" <module fileurl=\"file://").append(moduleIml.getAbsolutePath())
.append("\" filepath=\"").append(moduleIml.getAbsolutePath()).append("\" />\n");
for (String name : module.getAllDependencies()) {
Module mod = cache.getAndCache(name);
for (Module mod : modules) {
File iml = mod.getImlFile();
sb.append(" <module fileurl=\"file://").append(iml.getAbsolutePath())
.append("\" filepath=\"").append(iml.getAbsolutePath()).append("\" />\n");
sb.append(" <module fileurl=\"file://").append(iml.getCanonicalPath()).append(
"\" filepath=\"").append(iml.getCanonicalPath()).append("\" />\n");
}
modulesContent = modulesContent.replace("@MODULES@", sb.toString());
File out = new File(projectIdeaDir, "modules.xml");
logger.info("Creating " + out.getAbsolutePath());
File out = new File(ideaDir, "modules.xml");
logger.info("Creating " + out.getCanonicalPath());
Files.write(modulesContent, out, CHARSET);
}
private void createVcsFile(Module module) throws IOException {
String vcsTemplate = Files.toString(
new File(DirectorySearch.findTemplateDir(),
"idea" + File.separator + VCS_TEMPLATE_FILE_NAME),
CHARSET);
private void createVcsFile(File ideaDir, Iterable<Module> modules) throws IOException {
String vcsTemplate = Files.toString(new File(DirectorySearch.findTemplateDir(),
"idea" + File.separator + VCS_TEMPLATE_FILE_NAME), CHARSET);
StringBuilder sb = new StringBuilder();
for (String name : module.getAllDependencies()) {
Module mod = cache.getAndCache(name);
for (Module mod : modules) {
File dir = mod.getDir();
File gitRoot = new File(dir, ".git");
if (gitRoot.exists()) {
sb.append(" <mapping directory=\"").append(dir.getAbsolutePath())
.append("\" vcs=\"Git\" />\n");
sb.append(" <mapping directory=\"").append(dir.getCanonicalPath()).append(
"\" vcs=\"Git\" />\n");
}
}
vcsTemplate = vcsTemplate.replace("@VCS@", sb.toString());
Files.write(vcsTemplate, new File(projectIdeaDir, "vcs.xml"), CHARSET);
Files.write(vcsTemplate, new File(ideaDir, "vcs.xml"), CHARSET);
}
private void createNameFile(String name) throws IOException {
File out = new File(projectIdeaDir, ".name");
private void createNameFile(File ideaDir, String name) throws IOException {
File out = new File(ideaDir, ".name");
Files.write(name, out, CHARSET);
}
private void copyTemplates() throws IOException {
private void copyTemplates(File ideaDir) throws IOException {
File templateDir = DirectorySearch.findTemplateDir();
copyTemplates(new File(templateDir, "idea"), projectIdeaDir);
copyTemplates(new File(templateDir, "idea"), ideaDir);
}
private void copyTemplates(File fromDir, File toDir) throws IOException {
@@ -191,11 +160,14 @@ public class IntellijProject {
for (File file : files) {
if (file.isDirectory()) {
File destDir = new File(toDir, file.getName());
if (!destDir.exists()) {
destDir.mkdirs();
}
copyTemplates(file, destDir);
} else {
File toFile = new File(toDir, file.getName());
logger.info("copying " + file.getAbsolutePath() + " to " +
toFile.getAbsolutePath());
logger.info("copying " + file.getCanonicalPath() + " to " +
toFile.getCanonicalPath());
Files.copy(file, toFile);
}
}
@@ -204,10 +176,32 @@ public class IntellijProject {
public static void main(String[] args) {
logger.info("Args: " + Arrays.toString(args));
String indexFile = args[0];
String module = args[1];
if (args.length < 3) {
logger.severe("Not enough input arguments. Aborting");
return;
}
IntellijProject intellij = new IntellijProject(indexFile, module);
boolean buildFramework = true;
int argIndex = 0;
String arg = args[argIndex];
while (arg.startsWith("--")) {
if (arg.equals("--no-framework")) {
buildFramework = false;
}
argIndex++;
arg = args[argIndex];
}
String indexFile = args[argIndex++];
String projectPath = args[argIndex++];
// Remaining args are module directories
ArrayList<String> moduleDirs = Lists.newArrayList();
for (int i = argIndex; i < args.length; i++) {
moduleDirs.add(args[i]);
}
IntellijProject intellij = new IntellijProject(indexFile, projectPath, moduleDirs,
buildFramework);
try {
intellij.build();
} catch (IOException e) {

View File

@@ -22,6 +22,7 @@ import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.io.Files;
import com.google.common.io.LineProcessor;
@@ -29,7 +30,9 @@ import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.logging.Logger;
@@ -41,16 +44,7 @@ public class MakeFileParser {
private static final Logger logger = Logger.getLogger(MakeFileParser.class.getName());
public static final String VALUE_DELIMITER = "|";
private enum State {
NEW, CONTINUE
}
private enum ModuleNameKey {
LOCAL_PACKAGE_NAME,
LOCAL_MODULE
};
private File makeFile;
private String moduleName;
private HashMap<String, String> values;
/**
@@ -59,11 +53,9 @@ public class MakeFileParser {
* A make file may contain multiple modules.
*
* @param makeFile The make file to parse.
* @param moduleName The module to extract.
*/
public MakeFileParser(File makeFile, String moduleName) {
public MakeFileParser(File makeFile) {
this.makeFile = Preconditions.checkNotNull(makeFile);
this.moduleName = Preconditions.checkNotNull(moduleName);
}
public Iterable<String> getValues(String key) {
@@ -71,163 +63,214 @@ public class MakeFileParser {
if (str == null) {
return null;
}
return Splitter.on(VALUE_DELIMITER)
.trimResults()
.omitEmptyStrings()
.split(str);
return Splitter.on(VALUE_DELIMITER).trimResults().omitEmptyStrings().split(str);
}
/**
* Extracts the relevant portion of the make file and converts into key value pairs.
* <p>
* Since each make file may contain multiple build targets (modules), this method will determine
* which part is the correct portion for the given module name.
*
* @throws IOException
* Extracts the relevant portion of the make file and converts into key value pairs. <p> Since
* each make file may contain multiple build targets (modules), this method will determine which
* part is the correct portion for the given module name.
*/
public void parse() throws IOException {
values = Maps.newHashMap();
logger.info("Parsing " + makeFile.getAbsolutePath() + " for module " + moduleName);
logger.info("Parsing " + makeFile.getCanonicalPath());
Files.readLines(makeFile, Charset.forName("UTF-8"), new LineProcessor<Object>() {
private String key;
private State state = State.NEW;
@Override
public boolean processLine(String line) throws IOException {
String trimmed = line.trim();
if (Strings.isNullOrEmpty(trimmed)) {
state = State.NEW;
return true;
}
if (trimmed.equals("include $(CLEAR_VARS)")) {
// See if we are in the right module.
if (moduleName.equals(getModuleName())) {
return false;
} else {
values.clear();
}
} else {
switch (state) {
case NEW:
trimmed = checkContinue(trimmed);
if (trimmed.contains("=")) {
String[] arr;
if (trimmed.contains(":")) {
arr = trimmed.split(":=");
} else {
arr = trimmed.split("\\+=");
}
if (arr.length > 2) {
logger.info("Malformed line " + line);
} else {
// Store the key in case the line continues
this.key = arr[0].trim();
if (arr.length == 2) {
// There may be multiple values on one line.
List<String> valuesArr = tokenizeValue(arr[1].trim());
for (String value : valuesArr) {
appendValue(this.key, value);
}
}
}
} else {
//logger.info("Skipping line " + line);
}
break;
case CONTINUE:
// append
trimmed = checkContinue(trimmed);
appendValue(key, trimmed);
break;
default:
}
}
return true;
}
private List<String> tokenizeValue(String value) {
// Value may contain function calls such as "$(call all-java-files-under)".
// Tokens are separated by spaces unless it's between parens.
StringBuilder token = new StringBuilder();
ArrayList<String> tokens = Lists.newArrayList();
int parenCount = 0;
for (int i = 0; i < value.length(); i++) {
char ch = value.charAt(i);
if (parenCount == 0 && ch == ' ') {
// Not in a paren and delimiter encountered.
// end token
if (token.length() > 0) {
tokens.add(token.toString());
token = new StringBuilder();
}
} else {
token.append(ch);
}
if (ch == '(') {
parenCount++;
} else if (ch == ')') {
parenCount--;
}
}
// end of line check
if (token.length() > 0) {
tokens.add(token.toString());
}
return tokens;
}
private String getModuleName() {
for (ModuleNameKey key : ModuleNameKey.values()) {
String name = values.get(key.name());
if (name != null) {
return name;
}
}
return null;
}
@Override
public Object getResult() {
return null;
}
private String checkContinue(String value) {
// Check for continuation character
if (value.charAt(value.length() - 1) == '\\') {
state = State.CONTINUE;
return value.substring(0, value.length() - 1);
}
state = State.NEW;
return value;
}
/**
* Add a value to the hash map. If the key already exists, will append instead of
* over-writing the existing value.
*
* @param key The hashmap key
* @param newValue The value to append.
*/
private void appendValue(String key, String newValue) {
String value = values.get(key);
if (value == null) {
values.put(key, newValue);
} else {
values.put(key, value + VALUE_DELIMITER + newValue);
}
}
});
Files.readLines(makeFile, Charset.forName("UTF-8"), new MakeFileLineProcessor());
}
@Override
public String toString() {
return Objects.toStringHelper(this)
.add("values", values)
.toString();
return Objects.toStringHelper(this).add("values", values).toString();
}
private class MakeFileLineProcessor implements LineProcessor<Object> {
private StringBuilder lineBuffer;
// Keep a list of LOCAL_ variables to clear when CLEAR_VARS is encountered.
private HashSet<String> localVars = Sets.newHashSet();
@Override
public boolean processLine(String line) throws IOException {
String trimmed = line.trim();
// Skip comments.
if (!trimmed.isEmpty() && trimmed.charAt(0) == '#') {
return true;
}
appendPartialLine(trimmed);
if (!trimmed.isEmpty() && trimmed.charAt(trimmed.length() - 1) == '\\') {
// This is a partial line. Do not process yet.
return true;
}
String completeLine = lineBuffer.toString().trim();
// Reset the line buffer.
lineBuffer = null;
if (Strings.isNullOrEmpty(completeLine)) {
return true;
}
processKeyValuePairs(completeLine);
return true;
}
private void processKeyValuePairs(String line) {
if (line.contains("=")) {
String[] arr;
if (line.contains(":")) {
arr = line.split(":=");
} else {
arr = line.split("\\+=");
}
if (arr.length > 2) {
logger.info("Malformed line " + line);
} else {
// Store the key in case the line continues
String key = arr[0].trim();
if (arr.length == 2) {
// There may be multiple values on one line.
List<String> valuesArr = tokenizeValue(arr[1]);
for (String value : valuesArr) {
appendValue(key, value);
}
}
}
} else {
//logger.info("Skipping line " + line);
}
}
private void appendPartialLine(String line) {
if (lineBuffer == null) {
lineBuffer = new StringBuilder();
} else {
lineBuffer.append(" ");
}
if (line.endsWith("\\")) {
lineBuffer.append(line.substring(0, line.length() - 2).trim());
} else {
lineBuffer.append(line);
}
}
private List<String> tokenizeValue(String rawValue) {
String value = rawValue.trim();
ArrayList<String> result = Lists.newArrayList();
if (value.isEmpty()) {
return result;
}
// Value may contain function calls such as "$(call all-java-files-under)" or refer
// to variables such as "$(my_var)"
value = findVariables(value);
String[] tokens = value.split(" ");
Collections.addAll(result, tokens);
return result;
}
private String findVariables(String value) {
int variableStart = value.indexOf('$');
// Keep going until we substituted all variables.
while (variableStart > -1) {
StringBuilder sb = new StringBuilder();
sb.append(value.substring(0, variableStart));
// variable found
int variableEnd = findClosingParen(value, variableStart);
if (variableEnd > variableStart) {
String result = substituteVariables(value.substring(variableStart + 2, variableEnd));
sb.append(result);
} else {
throw new IllegalArgumentException(
"Malformed variable reference in make file: " + value);
}
if (variableEnd + 1 < value.length()) {
sb.append(value.substring(variableEnd + 1));
}
value = sb.toString();
variableStart = value.indexOf('$');
}
return value;
}
private int findClosingParen(String value, int startIndex) {
int openParenCount = 0;
for (int i = startIndex; i < value.length(); i++) {
char ch = value.charAt(i);
if (ch == ')') {
openParenCount--;
if (openParenCount == 0) {
return i;
}
} else if (ch == '(') {
openParenCount++;
}
}
return -1;
}
/**
* Look for and handle $(...) variables.
*/
private String substituteVariables(String rawValue) {
if (rawValue.isEmpty()) {
return rawValue;
}
String value = rawValue;
if (value.startsWith("call all-java-files-under")) {
// Ignore the call and function, keep the args.
value = value.substring(25).trim();
} else if (value.startsWith("call")) {
value = value.substring(4).trim();
}
// Check for single variable
if (value.indexOf(' ') == -1) {
// Substitute.
value = values.get(value);
if (value == null) {
value = "";
}
return value;
} else {
return findVariables(value);
}
}
@Override
public Object getResult() {
return null;
}
/**
* Add a value to the hash map. If the key already exists, will append instead of
* over-writing the existing value.
*
* @param key The hashmap key
* @param newValue The value to append.
*/
private void appendValue(String key, String newValue) {
String value = values.get(key);
if (value == null) {
values.put(key, newValue);
} else {
values.put(key, value + VALUE_DELIMITER + newValue);
}
}
}
public static void main(String[] args) {
MakeFileParser parser = new MakeFileParser(new File(args[0]));
try {
parser.parse();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(parser.toString());
}
}

View File

@@ -17,24 +17,44 @@
package com.android.idegen;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.io.Files;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;
/**
* Super class for all modules.
* Module constructed from a make file.
*
* TODO: read the make file and understand included source dirs in addition to searching
* sub-directories. Make files can include sources that are not sub-directories. For example, the
* framework module includes sources from:
*
* external/libphonenumber/java/src
*
* to provide:
*
* com.android.i18n.phonenumbers.PhoneNumberUtil;
*/
public abstract class Module {
public class Module {
private static final Logger logger = Logger.getLogger(Module.class.getName());
public static final String REL_OUT_APP_DIR = "out/target/common/obj/APPS";
private static final String IML_TEMPLATE_FILE_NAME = "module-template.iml";
private static final String[] AUTO_DEPENDENCIES = new String[]{"framework", "libcore"};
private static final String[] DIRS_WITH_AUTO_DEPENDENCIES = new String[]{"packages", "vendor",
"frameworks/ex", "frameworks/opt", "frameworks/support"};
/**
* All possible attributes for the make file.
@@ -45,31 +65,155 @@ public abstract class Module {
LOCAL_SRC_FILES
}
ModuleCache moduleCache = ModuleCache.getInstance();
private ModuleCache moduleCache = ModuleCache.getInstance();
private File imlFile;
private Set<String> allDependencies = Sets.newHashSet(); // direct + indirect
private Set<File> allDependentImlFiles = Sets.newHashSet();
protected abstract void build() throws IOException;
private File makeFile;
private File moduleRoot;
private HashSet<File> sourceFiles = Sets.newHashSet();
protected abstract String getName();
// Module dependencies come from LOCAL_STATIC_JAVA_LIBRARIES or LOCAL_JAVA_LIBRARIES
Set<String> explicitModuleNameDependencies = Sets.newHashSet();
// Implicit module dependencies come from src files that fall outside the module root directory.
// For example, if packages/apps/Contacts includes src files from packages/apps/ContactsCommon,
// that is an implicit module dependency. It's not a module dependency from the build
// perspective but it needs to be a separate module in intellij so that the src files can be
// shared by multiple intellij modules.
Set<File> implicitModulePathDependencies = Sets.newHashSet();
protected abstract File getDir();
String relativeIntermediatesDir;
MakeFileParser makeFileParser;
boolean parseMakeFileForSource;
protected abstract boolean isAndroidModule();
public Module(File moduleDir) throws IOException {
this(moduleDir, true);
}
protected abstract List<File> getIntermediatesDirs();
public Module(File moduleDir, boolean parseMakeFileForSource) throws IOException {
this.moduleRoot = Preconditions.checkNotNull(moduleDir);
this.makeFile = new File(moduleDir, "Android.mk");
this.relativeIntermediatesDir = calculateRelativePartToRepoRoot() + REL_OUT_APP_DIR +
File.separatorChar + getName() + "_intermediates" + File.separator + "src";
this.parseMakeFileForSource = parseMakeFileForSource;
public abstract Set<String> getDirectDependencies();
// TODO: auto-detect when framework dependency is needed instead of using coded list.
for (String dir : DIRS_WITH_AUTO_DEPENDENCIES) {
// length + 2 to account for slash
boolean isDir = makeFile.getCanonicalPath().startsWith(
DirectorySearch.getRepoRoot() + "/" + dir);
if (isDir) {
Collections.addAll(this.explicitModuleNameDependencies, AUTO_DEPENDENCIES);
}
}
protected abstract ImmutableList<File> getSourceDirs();
makeFileParser = new MakeFileParser(makeFile);
}
protected abstract ImmutableList<File> getExcludeDirs();
private String calculateRelativePartToRepoRoot() throws IOException {
String rel = moduleRoot.getCanonicalPath().substring(
DirectorySearch.getRepoRoot().getCanonicalPath().length());
int count = 0;
// Count the number of slashes to determine how far back to go.
for (int i = 0; i < rel.length(); i++) {
if (rel.charAt(i) == '/') {
count++;
}
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < count; i++) {
sb.append("../");
}
return sb.toString();
}
public abstract File getRepoRoot();
public void build() throws IOException {
makeFileParser.parse();
buildDependencyList();
buildDependentModules();
logger.info("Done building module " + moduleRoot);
logger.info(toString());
}
public File getDir() {
return moduleRoot;
}
public String getName() {
return moduleRoot.getName();
}
private List<String> getRelativeIntermediatesDirs() throws IOException {
return Lists.newArrayList(relativeIntermediatesDir);
}
private ImmutableList<File> getSourceDirs() {
return ImmutableList.copyOf(sourceFiles);
}
private ImmutableList<File> getExcludeDirs() {
return DirectorySearch.findExcludeDirs(makeFile);
}
private boolean isAndroidModule() {
File manifest = new File(moduleRoot, "AndroidManifest.xml");
return manifest.exists();
}
private void findSourceFilesAndImplicitDependencies() throws IOException {
Iterable<String> values = makeFileParser.getValues(Key.LOCAL_SRC_FILES.name());
if (values != null) {
for (String value : values) {
File src = new File(moduleRoot, value);
// value may contain garbage at this point due to relaxed make file parsing.
// filter by existing file.
if (src.exists()) {
// Look for directories outside the current module directory.
if (value.contains("..")) {
// Find the closest Android make file.
File moduleRoot = DirectorySearch.findModuleRoot(src);
implicitModulePathDependencies.add(moduleRoot);
} else {
if (parseMakeFileForSource) {
// Check if source files are subdirectories of generic parent src
// directories. If so, no need to add since they are already included.
boolean alreadyIncluded = false;
for (String parentDir : DirectorySearch.SOURCE_DIRS) {
if (value.startsWith(parentDir)) {
alreadyIncluded = true;
break;
}
}
if (!alreadyIncluded) {
sourceFiles.add(src);
}
}
}
}
}
}
sourceFiles.addAll(DirectorySearch.findSourceDirs(moduleRoot));
}
private void buildDependencyList() throws IOException {
parseDirectDependencies(Key.LOCAL_STATIC_JAVA_LIBRARIES);
parseDirectDependencies(Key.LOCAL_JAVA_LIBRARIES);
findSourceFilesAndImplicitDependencies();
}
private void parseDirectDependencies(Key key) {
Iterable<String> names = makeFileParser.getValues(key.name());
if (names != null) {
for (String dependency : names) {
explicitModuleNameDependencies.add(dependency);
}
}
}
public void buildImlFile() throws IOException {
String imlTemplate = Files.toString(
@@ -82,19 +226,24 @@ public abstract class Module {
}
imlTemplate = imlTemplate.replace("@FACETS@", facetXml);
String moduleDir = getDir().getAbsolutePath();
String moduleDir = getDir().getCanonicalPath();
StringBuilder sourceDirectories = new StringBuilder();
sourceDirectories.append(" <content url=\"file://$MODULE_DIR$\">\n");
ImmutableList<File> srcDirs = getSourceDirs();
for (File src : srcDirs) {
String relative = src.getAbsolutePath().substring(moduleDir.length());
String relative = src.getCanonicalPath().substring(moduleDir.length());
boolean isTestSource = false;
if (relative.startsWith("/test")) {
isTestSource = true;
}
sourceDirectories.append(" <sourceFolder url=\"file://$MODULE_DIR$")
.append(relative).append("\" isTestSource=\"false\" />\n");
.append(relative).append("\" isTestSource=\"").append(isTestSource)
.append("\" />\n");
}
ImmutableList<File> excludeDirs = getExcludeDirs();
for (File src : excludeDirs) {
String relative = src.getAbsolutePath().substring(moduleDir.length());
String relative = src.getCanonicalPath().substring(moduleDir.length());
sourceDirectories.append(" <excludeFolder url=\"file://$MODULE_DIR$")
.append(relative).append("\"/>\n");
}
@@ -106,43 +255,62 @@ public abstract class Module {
imlTemplate = imlTemplate.replace("@SOURCES@", sourceDirectories.toString());
StringBuilder moduleDependencies = new StringBuilder();
for (String dependency : getDirectDependencies()) {
for (String dependency : getAllDependencies()) {
Module module = moduleCache.getAndCacheByDir(new File(dependency));
moduleDependencies.append(" <orderEntry type=\"module\" module-name=\"")
.append(dependency).append("\" />\n");
.append(module.getName()).append("\" />\n");
}
imlTemplate = imlTemplate.replace("@MODULE_DEPENDENCIES@", moduleDependencies.toString());
imlFile = new File(moduleDir, getName() + ".iml");
logger.info("Creating " + imlFile.getAbsolutePath());
logger.info("Creating " + imlFile.getCanonicalPath());
Files.write(imlTemplate, imlFile, IntellijProject.CHARSET);
}
protected String buildIntermediates() {
protected String buildIntermediates() throws IOException {
StringBuilder sb = new StringBuilder();
for (File intermediatesDir : getIntermediatesDirs()) {
sb.append(" <content url=\"file://").append(intermediatesDir).append("\">\n");
sb.append(" <sourceFolder url=\"file://")
.append(intermediatesDir.getAbsolutePath())
for (String intermediatesDir : getRelativeIntermediatesDirs()) {
sb.append(" <content url=\"file://$MODULE_DIR$/").append(intermediatesDir)
.append("\">\n");
sb.append(" <sourceFolder url=\"file://$MODULE_DIR$/")
.append(intermediatesDir)
.append("\" isTestSource=\"false\" />\n");
sb.append(" </content>\n");
}
return sb.toString();
}
protected void buildDependentModules() throws IOException {
Set<String> directDependencies = getDirectDependencies();
String[] copy = directDependencies.toArray(new String[directDependencies.size()]);
for (String dependency : copy) {
private void buildDependentModules() throws IOException {
Set<String> moduleNameDependencies = explicitModuleNameDependencies;
Module child = moduleCache.getAndCache(dependency);
String[] copy = moduleNameDependencies.toArray(new String[moduleNameDependencies.size()]);
for (String dependency : copy) {
logger.info("Building dependency " + dependency);
Module child = moduleCache.getAndCacheByName(dependency);
if (child == null) {
directDependencies.remove(dependency);
moduleNameDependencies.remove(dependency);
} else {
addAllDependencies(dependency);
addAllDependencies(child.getAllDependencies());
allDependencies.add(child.getDir().getCanonicalPath());
//allDependencies.addAll(child.getAllDependencies());
//logger.info("Adding iml " + child.getName() + " " + child.getImlFile());
allDependentImlFiles.add(child.getImlFile());
allDependentImlFiles.addAll(child.getAllDependentImlFiles());
//allDependentImlFiles.addAll(child.getAllDependentImlFiles());
}
}
// Don't include self. The current module may have been brought in by framework
// dependencies which will create a circular reference.
allDependencies.remove(this.getDir().getCanonicalPath());
allDependentImlFiles.remove(this.getImlFile());
// TODO: add implicit dependencies. Convert all modules to be based on directory.
for (File dependency : implicitModulePathDependencies) {
Module child = moduleCache.getAndCacheByDir(dependency);
if (child != null) {
allDependencies.add(child.getDir().getCanonicalPath());
//allDependencies.addAll(child.getAllDependencies());
//logger.info("Adding iml " + child.getName() + " " + child.getImlFile());
allDependentImlFiles.add(child.getImlFile());
//allDependentImlFiles.addAll(child.getAllDependentImlFiles());
}
}
}
@@ -155,29 +323,22 @@ public abstract class Module {
return allDependencies;
}
public void addAllDependencies(String dependency) {
this.allDependencies.add(dependency);
}
public void addAllDependencies(Set<String> dependencies) {
this.allDependencies.addAll(dependencies);
}
public Set<File> getAllDependentImlFiles() {
return allDependentImlFiles;
}
private String buildAndroidFacet() {
private String buildAndroidFacet() throws IOException {
// Not sure how to handle android facet for multi-module since there could be more than
// one intermediates directory.
String dir = getIntermediatesDirs().get(0).getAbsolutePath();
String dir = getRelativeIntermediatesDirs().get(0);
String xml = ""
+ " <component name=\"FacetManager\">\n"
+ " <facet type=\"android\" name=\"Android\">\n"
+ " <configuration>\n"
+ " <option name=\"GEN_FOLDER_RELATIVE_PATH_APT\" value=\"" + dir + "\" />\n"
+ " <option name=\"GEN_FOLDER_RELATIVE_PATH_AIDL\" value=\"" + dir
+ "\" />\n"
+ " <option name=\"GEN_FOLDER_RELATIVE_PATH_APT\" value=\"" +
dir + "\" />\n"
+ " <option name=\"GEN_FOLDER_RELATIVE_PATH_AIDL\" value=\"" +
dir + "\" />\n"
+ " <option name=\"MANIFEST_FILE_RELATIVE_PATH\" value=\""
+ "/AndroidManifest.xml\" />\n"
+ " <option name=\"RES_FOLDER_RELATIVE_PATH\" value=\"/res\" />\n"
@@ -199,13 +360,34 @@ public abstract class Module {
return xml;
}
@Override
public int hashCode() {
return Objects.hashCode(getName());
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
Module other = (Module) obj;
return Objects.equal(getName(), other.getName());
}
@Override
public String toString() {
return Objects.toStringHelper(Module.class)
return Objects.toStringHelper(this)
.add("name", getName())
.add("allDependencies", allDependencies)
.add("iml files", allDependentImlFiles)
.add("imlFile", imlFile)
.add("iml files", allDependentImlFiles).add("imlFile", imlFile)
.add("makeFileParser", makeFileParser)
.add("explicitModuleNameDependencies", Iterables.toString(
explicitModuleNameDependencies))
.add("implicitModulePathDependencies", Iterables.toString(
implicitModulePathDependencies))
.toString();
}
}

View File

@@ -18,12 +18,10 @@ package com.android.idegen;
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Set;
import java.util.logging.Logger;
/**
@@ -37,7 +35,9 @@ public class ModuleCache {
ModuleIndexes indexes;
HashMap<String, Module> modulesByName = Maps.newHashMap();
// Mapping of canonical module directory to module. Use string instead of File since File
// does not provide equality based on canonical path.
HashMap<String, Module> modulesByPath = Maps.newHashMap();
private ModuleCache() {
}
@@ -51,57 +51,48 @@ public class ModuleCache {
indexes.build();
}
public Module getAndCache(String moduleName) throws IOException {
Preconditions.checkState(indexes != null, "You must call init() first.");
public Module getAndCacheByDir(File moduleDir) throws IOException {
Preconditions.checkNotNull(moduleDir);
Module module = modulesByName.get(moduleName);
if (module == null) {
String makeFile = indexes.getMakeFile(moduleName);
if (makeFile == null) {
logger.warning("Unable to find make file for module: " + moduleName);
} else {
module = new StandardModule(moduleName, makeFile);
if (moduleDir.exists()) {
Module module = getModule(moduleDir);
if (module == null) {
module = new Module(moduleDir);
// Must put module before building it. Otherwise infinite loop.
putModule(moduleDir, module);
module.build();
modulesByName.put(moduleName, module);
}
}
return module;
}
public void buildAndCacheAggregatedModule(String moduleName) throws IOException {
if (indexes.isPartOfAggregatedModule(moduleName)) {
Set<String> moduleNames = indexes.getAggregatedModules(moduleName);
Set<Module> modules = Sets.newHashSet();
for (String name : moduleNames) {
Module m = modulesByName.get(name);
if (m != null) {
modules.add(m);
}
}
String aggregatedName = indexes.getAggregateName(moduleName);
AggregatedModule module = new AggregatedModule(aggregatedName, modules);
module.build();
modulesByName.put(aggregatedName, module);
}
}
public Iterable<Module> getModules() {
return modulesByName.values();
}
public String getMakeFile(String moduleName) {
return indexes.getMakeFile(moduleName);
}
public void put(StandardModule module) {
Preconditions.checkNotNull(module);
modulesByName.put(module.getName(), module);
}
public String getAggregateReplacementName(String moduleName) {
if (indexes.isPartOfAggregatedModule(moduleName)) {
return indexes.getAggregateName(moduleName);
return module;
}
return null;
}
public Module getAndCacheByName(String moduleName) throws IOException {
Preconditions.checkState(indexes != null, "You must call init() first.");
Preconditions.checkNotNull(moduleName);
String makeFile = indexes.getMakeFile(moduleName);
if (makeFile == null) {
logger.warning("Unable to find make file for module: " + moduleName);
return null;
}
return getAndCacheByDir(new File(makeFile).getParentFile());
}
private void putModule(File moduleDir, Module module) throws IOException {
modulesByPath.put(moduleDir.getCanonicalPath(), module);
}
private Module getModule(File moduleDir) throws IOException {
return modulesByPath.get(moduleDir.getCanonicalPath());
}
public Iterable<Module> getModules() {
return modulesByPath.values();
}
public void put(Module module) throws IOException {
Preconditions.checkNotNull(module);
putModule(module.getDir(), module);
}
}

View File

@@ -49,7 +49,7 @@ public class ModuleIndexes {
moduleNameToMakeFileMap = Maps.newHashMap();
makeFileToModuleNamesMap = Maps.newHashMap();
logger.info("Building index from " + indexFile.getAbsolutePath());
logger.info("Building index from " + indexFile.getCanonicalPath());
Files.readLines(indexFile, Charset.forName("UTF-8"),
new LineProcessor<Object>() {
int count = 0;
@@ -84,7 +84,7 @@ public class ModuleIndexes {
makeFileToModuleNamesMap.put(makeFile, moduleNames);
} else {
// Create a aggregate module place holder.
//moduleNameToMakeFileMap.put(getAggregateName(moduleName), makeFile);
//moduleNameToMakeFileMap.put(getAggregateName(moduleDir), makeFile);
}
moduleNames.add(moduleName);
}
@@ -92,31 +92,7 @@ public class ModuleIndexes {
public String getMakeFile(String moduleName) {
Preconditions.checkState(moduleNameToMakeFileMap != null,
"Index not built. Call build() first.");
return moduleNameToMakeFileMap.get(moduleName);
}
public Set<String> getAggregatedModules(String moduleName) {
Preconditions.checkState(makeFileToModuleNamesMap != null,
"Index not built. Call build() first.");
String makeFile = getMakeFile(moduleName);
return makeFileToModuleNamesMap.get(makeFile);
}
public boolean isPartOfAggregatedModule(String moduleName) {
String makeFile = getMakeFile(moduleName);
if (makeFile == null) {
return false;
}
Set<String> moduleNames = makeFileToModuleNamesMap.get(makeFile);
if (moduleNames == null) {
return false;
}
return moduleNames.size() > 1;
}
public String getAggregateName(String moduleName) {
String fileName = getMakeFile(moduleName);
File file = new File(fileName);
return file.getParentFile().getName() + "-aggregate";
}
}

View File

@@ -1,225 +0,0 @@
/*
* Copyright (C) 2012 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.idegen;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Module constructed from a make file.
*
* TODO: read the make file and understand included source dirs in addition to searching
* sub-directories. Make files can include sources that are not sub-directories. For example,
* the framework module includes sources from:
*
* external/libphonenumber/java/src
*
* to provide:
*
* com.android.i18n.phonenumbers.PhoneNumberUtil;
*/
public class StandardModule extends Module {
static final String REL_OUT_APP_DIR = "out/target/common/obj/APPS";
private static final Logger logger = Logger.getLogger(StandardModule.class.getName());
private static final Pattern SRC_PATTERN = Pattern.compile(
".*\\(call all-java-files-under, (.*)\\)");
private static final String[] AUTO_DEPENDENCIES = new String[]{
IntellijProject.FRAMEWORK_MODULE, "libcore"
};
private static final String[] DIRS_WITH_AUTO_DEPENDENCIES = new String[]{
"packages", "vendor", "frameworks/ex", "frameworks/opt", "frameworks/support"
};
String moduleName;
File makeFile;
File moduleRoot;
File repoRoot;
Set<String> directDependencies = Sets.newHashSet();
File intermediatesDir;
MakeFileParser makeFileParser;
boolean searchForSrc;
public StandardModule(String moduleName, String makeFile) {
this(moduleName, new File(makeFile), false);
}
public StandardModule(String moduleName, String makeFile, boolean searchForSrc) {
this(Preconditions.checkNotNull(moduleName), new File(Preconditions.checkNotNull(makeFile)),
searchForSrc);
}
public StandardModule(String moduleName, File makeFile, boolean searchForSrc) {
this.moduleName = moduleName;
this.makeFile = makeFile;
this.moduleRoot = makeFile.getParentFile();
this.repoRoot = DirectorySearch.findRepoRoot(makeFile);
this.intermediatesDir = new File(repoRoot.getAbsolutePath() + File.separator +
REL_OUT_APP_DIR + File.separator + getName() + "_intermediates" +
File.separator + "src");
this.searchForSrc = searchForSrc;
// TODO: auto-detect when framework dependency is needed instead of using coded list.
for (String dir : DIRS_WITH_AUTO_DEPENDENCIES) {
// length + 2 to account for slash
boolean isDir = makeFile.getAbsolutePath().startsWith(repoRoot + "/" + dir);
if (isDir) {
for (String dependency : AUTO_DEPENDENCIES) {
this.directDependencies.add(dependency);
}
}
}
makeFileParser = new MakeFileParser(makeFile, moduleName);
}
protected void build() throws IOException {
makeFileParser.parse();
buildDependencyList();
buildDependentModules();
//buildImlFile();
logger.info("Done building module " + moduleName);
logger.info(toString());
}
@Override
protected File getDir() {
return moduleRoot;
}
@Override
protected String getName() {
return moduleName;
}
@Override
protected List<File> getIntermediatesDirs() {
return Lists.newArrayList(intermediatesDir);
}
@Override
public File getRepoRoot() {
return this.repoRoot;
}
public Set<String> getDirectDependencies() {
return this.directDependencies;
}
@Override
protected ImmutableList<File> getSourceDirs() {
ImmutableList<File> srcDirs;
if (searchForSrc) {
srcDirs = DirectorySearch.findSourceDirs(makeFile);
} else {
srcDirs = parseSourceFiles(makeFile);
}
return srcDirs;
}
@Override
protected ImmutableList<File> getExcludeDirs() {
return DirectorySearch.findExcludeDirs(makeFile);
}
@Override
protected boolean isAndroidModule() {
File manifest = new File(moduleRoot, "AndroidManifest.xml");
return manifest.exists();
}
private ImmutableList<File> parseSourceFiles(File root) {
ImmutableList.Builder<File> builder = ImmutableList.builder();
File rootDir;
if (root.isFile()) {
rootDir = root.getParentFile();
} else {
rootDir = root;
}
Iterable<String> values = makeFileParser.getValues(Key.LOCAL_SRC_FILES.name());
if (values != null) {
for (String value : values) {
Matcher matcher = SRC_PATTERN.matcher(value);
if (matcher.matches()) {
String dir = matcher.group(1);
builder.add(new File(rootDir, dir));
} else if (value.contains("/")) {
// Treat as individual file.
builder.add(new File(rootDir, value));
}
}
}
return builder.build();
}
private void buildDependencyList() {
parseDirectDependencies(Key.LOCAL_STATIC_JAVA_LIBRARIES);
parseDirectDependencies(Key.LOCAL_JAVA_LIBRARIES);
}
private void parseDirectDependencies(Key key) {
Iterable<String> names = makeFileParser.getValues(key.name());
if (names != null) {
for (String dependency : names) {
directDependencies.add(dependency);
}
}
}
@Override
public int hashCode() {
return Objects.hashCode(getName());
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
StandardModule other = (StandardModule) obj;
return Objects.equal(getName(), other.getName());
}
@Override
public String toString() {
return Objects.toStringHelper(this)
.add("super", super.toString())
.add("makeFileParser", makeFileParser)
.add("directDependencies", Iterables.toString(directDependencies))
.toString();
}
}