summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorChiao Cheng <chiaocheng@google.com>2013-06-13 13:41:24 -0700
committerChiao Cheng <chiaocheng@google.com>2014-03-17 12:52:43 -0700
commitc8201d5e84a72b0c4ef259c4ad0c0874513bb3fd (patch)
treef2f0824fe4a09641deadc64799e06cb1142188bf /tools
parent753584340a78f8fc97235fcecf0531b53c4ddcfe (diff)
downloadandroid_development-c8201d5e84a72b0c4ef259c4ad0c0874513bb3fd.tar.gz
android_development-c8201d5e84a72b0c4ef259c4ad0c0874513bb3fd.tar.bz2
android_development-c8201d5e84a72b0c4ef259c4ad0c0874513bb3fd.zip
Overhaul of intellij-gen script.
Overall much better at find proper dependencies. * No more need for aggregate modules. * Source file searching. * Make file token substitution. * Inclusion src directories. * Multi project/package support. * Made framework module optional as some unbundled branches do not include it. Change-Id: Ie5ee66c488fc9141043137f716a3891f952bfe7f
Diffstat (limited to 'tools')
-rwxr-xr-xtools/idegen/index-gen.sh4
-rwxr-xr-xtools/idegen/intellij-gen.sh14
-rw-r--r--tools/idegen/src/com/android/idegen/AggregatedModule.java132
-rw-r--r--tools/idegen/src/com/android/idegen/DirectorySearch.java95
-rw-r--r--tools/idegen/src/com/android/idegen/FrameworkModule.java14
-rw-r--r--tools/idegen/src/com/android/idegen/IntellijProject.java198
-rw-r--r--tools/idegen/src/com/android/idegen/MakeFileParser.java315
-rw-r--r--tools/idegen/src/com/android/idegen/Module.java284
-rw-r--r--tools/idegen/src/com/android/idegen/ModuleCache.java71
-rw-r--r--tools/idegen/src/com/android/idegen/ModuleIndexes.java30
-rw-r--r--tools/idegen/src/com/android/idegen/StandardModule.java225
11 files changed, 634 insertions, 748 deletions
diff --git a/tools/idegen/index-gen.sh b/tools/idegen/index-gen.sh
index 5b9e24bb1..33633081b 100755
--- a/tools/idegen/index-gen.sh
+++ b/tools/idegen/index-gen.sh
@@ -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))
diff --git a/tools/idegen/intellij-gen.sh b/tools/idegen/intellij-gen.sh
index d67c1f867..860bd3c67 100755
--- a/tools/idegen/intellij-gen.sh
+++ b/tools/idegen/intellij-gen.sh
@@ -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
diff --git a/tools/idegen/src/com/android/idegen/AggregatedModule.java b/tools/idegen/src/com/android/idegen/AggregatedModule.java
deleted file mode 100644
index 1497da711..000000000
--- a/tools/idegen/src/com/android/idegen/AggregatedModule.java
+++ /dev/null
@@ -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;
- }
-
-}
diff --git a/tools/idegen/src/com/android/idegen/DirectorySearch.java b/tools/idegen/src/com/android/idegen/DirectorySearch.java
index 2ff23e3de..c289ac251 100644
--- a/tools/idegen/src/com/android/idegen/DirectorySearch.java
+++ b/tools/idegen/src/com/android/idegen/DirectorySearch.java
@@ -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");
@@ -51,37 +53,71 @@ public class DirectorySearch {
+ 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());
+ }
+ 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();
+ }
}
- return findRepoRoot(parent);
+ // 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());
}
}
diff --git a/tools/idegen/src/com/android/idegen/FrameworkModule.java b/tools/idegen/src/com/android/idegen/FrameworkModule.java
index 8743925e4..47ea35a72 100644
--- a/tools/idegen/src/com/android/idegen/FrameworkModule.java
+++ b/tools/idegen/src/com/android/idegen/FrameworkModule.java
@@ -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();
diff --git a/tools/idegen/src/com/android/idegen/IntellijProject.java b/tools/idegen/src/com/android/idegen/IntellijProject.java
index e49a12b72..e3b8d9456 100644
--- a/tools/idegen/src/com/android/idegen/IntellijProject.java
+++ b/tools/idegen/src/com/android/idegen/IntellijProject.java
@@ -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);
- }
-
- private void replaceWithAggregate(Module module) {
- replaceWithAggregate(module.getDirectDependencies(), module.getName());
- replaceWithAggregate(module.getAllDependencies(), module.getName());
-
+ createProjectFiles();
}
- 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;
+ }
+
+ 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, module);
+ IntellijProject intellij = new IntellijProject(indexFile, projectPath, moduleDirs,
+ buildFramework);
try {
intellij.build();
} catch (IOException e) {
diff --git a/tools/idegen/src/com/android/idegen/MakeFileParser.java b/tools/idegen/src/com/android/idegen/MakeFileParser.java
index 3594d5020..9a41b09b3 100644
--- a/tools/idegen/src/com/android/idegen/MakeFileParser.java
+++ b/tools/idegen/src/com/android/idegen/MakeFileParser.java
@@ -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>() {
+ Files.readLines(makeFile, Charset.forName("UTF-8"), new MakeFileLineProcessor());
+ }
- private String key;
+ @Override
+ public String toString() {
+ return Objects.toStringHelper(this).add("values", values).toString();
+ }
- private State state = State.NEW;
+ private class MakeFileLineProcessor implements LineProcessor<Object> {
- @Override
- public boolean processLine(String line) throws IOException {
- String trimmed = line.trim();
- if (Strings.isNullOrEmpty(trimmed)) {
- state = State.NEW;
- return true;
+ 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 (trimmed.equals("include $(CLEAR_VARS)")) {
- // See if we are in the right module.
- if (moduleName.equals(getModuleName())) {
- return false;
- } else {
- values.clear();
- }
+ if (arr.length > 2) {
+ logger.info("Malformed line " + line);
} 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:
+ // 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);
+ }
}
}
- return true;
+ } else {
+ //logger.info("Skipping line " + line);
}
+ }
- 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--;
- }
+ 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);
}
- // end of line check
- if (token.length() > 0) {
- tokens.add(token.toString());
+ if (variableEnd + 1 < value.length()) {
+ sb.append(value.substring(variableEnd + 1));
}
- return tokens;
+ value = sb.toString();
+ variableStart = value.indexOf('$');
}
+ return value;
+ }
- private String getModuleName() {
- for (ModuleNameKey key : ModuleNameKey.values()) {
- String name = values.get(key.name());
- if (name != null) {
- return name;
+ 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 null;
}
+ return -1;
+ }
- @Override
- public Object getResult() {
- return null;
+ /**
+ * 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();
}
- 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);
+ // Check for single variable
+ if (value.indexOf(' ') == -1) {
+ // Substitute.
+ value = values.get(value);
+ if (value == null) {
+ value = "";
}
- state = State.NEW;
return value;
+ } else {
+ return findVariables(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);
- }
+ @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);
}
- });
+ }
}
- @Override
- public String toString() {
- return Objects.toStringHelper(this)
- .add("values", values)
- .toString();
+ 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());
}
}
diff --git a/tools/idegen/src/com/android/idegen/Module.java b/tools/idegen/src/com/android/idegen/Module.java
index deb22811f..4695ca3f6 100644
--- a/tools/idegen/src/com/android/idegen/Module.java
+++ b/tools/idegen/src/com/android/idegen/Module.java
@@ -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();
+
+ // 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();
+
+ String relativeIntermediatesDir;
+ MakeFileParser makeFileParser;
+ boolean parseMakeFileForSource;
+
+ public Module(File moduleDir) throws IOException {
+ this(moduleDir, true);
+ }
+
+ 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;
+
+ // 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);
+ }
+ }
+
+ makeFileParser = new MakeFileParser(makeFile);
+ }
+
+ 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 void build() throws IOException {
+ makeFileParser.parse();
+ buildDependencyList();
+ buildDependentModules();
+ logger.info("Done building module " + moduleRoot);
+ logger.info(toString());
+ }
+
+ public File getDir() {
+ return moduleRoot;
+ }
- protected abstract String getName();
+ public String getName() {
+ return moduleRoot.getName();
+ }
- protected abstract File getDir();
+ private List<String> getRelativeIntermediatesDirs() throws IOException {
+ return Lists.newArrayList(relativeIntermediatesDir);
+ }
+
+ private ImmutableList<File> getSourceDirs() {
+ return ImmutableList.copyOf(sourceFiles);
+ }
- protected abstract boolean isAndroidModule();
+ private ImmutableList<File> getExcludeDirs() {
+ return DirectorySearch.findExcludeDirs(makeFile);
+ }
- protected abstract List<File> getIntermediatesDirs();
+ private boolean isAndroidModule() {
+ File manifest = new File(moduleRoot, "AndroidManifest.xml");
+ return manifest.exists();
+ }
- public abstract Set<String> getDirectDependencies();
+ 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);
+ }
+ }
+ }
+ }
+ }
+ }
- protected abstract ImmutableList<File> getSourceDirs();
+ sourceFiles.addAll(DirectorySearch.findSourceDirs(moduleRoot));
+ }
- protected abstract ImmutableList<File> getExcludeDirs();
+ private void buildDependencyList() throws IOException {
+ parseDirectDependencies(Key.LOCAL_STATIC_JAVA_LIBRARIES);
+ parseDirectDependencies(Key.LOCAL_JAVA_LIBRARIES);
+ findSourceFilesAndImplicitDependencies();
+ }
- public abstract File getRepoRoot();
+ 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());
+ }
+ }
+ // 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());
+ //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"
@@ -200,12 +361,33 @@ public abstract class Module {
}
@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();
}
}
diff --git a/tools/idegen/src/com/android/idegen/ModuleCache.java b/tools/idegen/src/com/android/idegen/ModuleCache.java
index 093242a02..eb1228332 100644
--- a/tools/idegen/src/com/android/idegen/ModuleCache.java
+++ b/tools/idegen/src/com/android/idegen/ModuleCache.java
@@ -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;
}
- return module;
+ return null;
}
- 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 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());
}
- public Iterable<Module> getModules() {
- return modulesByName.values();
+ private void putModule(File moduleDir, Module module) throws IOException {
+ modulesByPath.put(moduleDir.getCanonicalPath(), module);
}
- public String getMakeFile(String moduleName) {
- return indexes.getMakeFile(moduleName);
+ private Module getModule(File moduleDir) throws IOException {
+ return modulesByPath.get(moduleDir.getCanonicalPath());
}
- public void put(StandardModule module) {
- Preconditions.checkNotNull(module);
- modulesByName.put(module.getName(), module);
+ public Iterable<Module> getModules() {
+ return modulesByPath.values();
}
- public String getAggregateReplacementName(String moduleName) {
- if (indexes.isPartOfAggregatedModule(moduleName)) {
- return indexes.getAggregateName(moduleName);
- }
- return null;
+ public void put(Module module) throws IOException {
+ Preconditions.checkNotNull(module);
+ putModule(module.getDir(), module);
}
}
diff --git a/tools/idegen/src/com/android/idegen/ModuleIndexes.java b/tools/idegen/src/com/android/idegen/ModuleIndexes.java
index 890389f26..371c2ba75 100644
--- a/tools/idegen/src/com/android/idegen/ModuleIndexes.java
+++ b/tools/idegen/src/com/android/idegen/ModuleIndexes.java
@@ -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";
+ return moduleNameToMakeFileMap.get(moduleName);
}
}
diff --git a/tools/idegen/src/com/android/idegen/StandardModule.java b/tools/idegen/src/com/android/idegen/StandardModule.java
deleted file mode 100644
index f7b24b085..000000000
--- a/tools/idegen/src/com/android/idegen/StandardModule.java
+++ /dev/null
@@ -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();
- }
-}