am b25ebf3f: am 9bb23fd7: Use new PowerManager API.
* commit 'b25ebf3fc78c98b508f1b51278d017c070998ea8':
Use new PowerManager API.
diff --git a/cmds/monkey/src/com/android/commands/monkey/MonkeySourceScript.java b/cmds/monkey/src/com/android/commands/monkey/MonkeySourceScript.java
index b2b9b4f..59aeb55 100644
--- a/cmds/monkey/src/com/android/commands/monkey/MonkeySourceScript.java
+++ b/cmds/monkey/src/com/android/commands/monkey/MonkeySourceScript.java
@@ -71,12 +71,18 @@
private long mLastRecordedEventTime = -1;
+ // process scripts in line-by-line mode (true) or batch processing mode (false)
+ private boolean mReadScriptLineByLine = false;
+
private static final boolean THIS_DEBUG = false;
// a parameter that compensates the difference of real elapsed time and
// time in theory
private static final long SLEEP_COMPENSATE_DIFF = 16;
+ // if this header is present, scripts are read and processed in line-by-line mode
+ private static final String HEADER_LINE_BY_LINE = "linebyline";
+
// maximum number of events that we read at one time
private static final int MAX_ONE_TIME_READS = 100;
@@ -147,6 +153,15 @@
BufferedReader mBufferedReader;
+ // X and Y coordincates of last touch event. Array Index is the pointerId
+ private float mLastX[] = new float[2];
+
+ private float mLastY[] = new float[2];
+
+ private long mScriptStartTime = -1;
+
+ private long mMonkeyStartTime = -1;
+
/**
* Creates a MonkeySourceScript instance.
*
@@ -207,6 +222,8 @@
System.err.println(e);
return false;
}
+ } else if (line.indexOf(HEADER_LINE_BY_LINE) >= 0) {
+ mReadScriptLineByLine = true;
} else if (line.indexOf(STARTING_DATA_LINE) >= 0) {
return true;
}
@@ -234,6 +251,21 @@
return MAX_ONE_TIME_READS;
}
+ /**
+ * Reads one line and processes it.
+ *
+ * @return the number of lines read
+ * @throws IOException If there was an error reading the file.
+ */
+ private int readOneLine() throws IOException {
+ String line = mBufferedReader.readLine();
+ if (line == null) {
+ return 0;
+ }
+ line.trim();
+ processLine(line);
+ return 1;
+ }
@@ -306,6 +338,84 @@
return;
}
+ // Handle trackball or multi-touch pointer events. pointer ID is the 13th parameter
+ if ((s.indexOf(EVENT_KEYWORD_POINTER) >= 0 || s.indexOf(EVENT_KEYWORD_TRACKBALL) >= 0)
+ && args.length == 13) {
+ try {
+ long downTime = Long.parseLong(args[0]);
+ long eventTime = Long.parseLong(args[1]);
+ int action = Integer.parseInt(args[2]);
+ float x = Float.parseFloat(args[3]);
+ float y = Float.parseFloat(args[4]);
+ float pressure = Float.parseFloat(args[5]);
+ float size = Float.parseFloat(args[6]);
+ int metaState = Integer.parseInt(args[7]);
+ float xPrecision = Float.parseFloat(args[8]);
+ float yPrecision = Float.parseFloat(args[9]);
+ int device = Integer.parseInt(args[10]);
+ int edgeFlags = Integer.parseInt(args[11]);
+ int pointerId = Integer.parseInt(args[12]);
+
+ MonkeyMotionEvent e;
+ if (s.indexOf("Pointer") > 0) {
+ if (action == MotionEvent.ACTION_POINTER_DOWN) {
+ e = new MonkeyTouchEvent(MotionEvent.ACTION_POINTER_DOWN
+ | (pointerId << MotionEvent.ACTION_POINTER_INDEX_SHIFT))
+ .setIntermediateNote(true);
+ } else {
+ e = new MonkeyTouchEvent(action);
+ }
+ if (mScriptStartTime < 0) {
+ mMonkeyStartTime = SystemClock.uptimeMillis();
+ mScriptStartTime = eventTime;
+ }
+ } else {
+ e = new MonkeyTrackballEvent(action);
+ }
+
+ if (pointerId == 1) {
+ e.setDownTime(downTime)
+ .setEventTime(eventTime)
+ .setMetaState(metaState)
+ .setPrecision(xPrecision, yPrecision)
+ .setDeviceId(device)
+ .setEdgeFlags(edgeFlags)
+ .addPointer(0, mLastX[0], mLastY[0], pressure, size)
+ .addPointer(1, x, y, pressure, size);
+ mLastX[1] = x;
+ mLastY[1] = y;
+ } else if (pointerId == 0) {
+ e.setDownTime(downTime)
+ .setEventTime(eventTime)
+ .setMetaState(metaState)
+ .setPrecision(xPrecision, yPrecision)
+ .setDeviceId(device)
+ .setEdgeFlags(edgeFlags)
+ .addPointer(0, x, y, pressure, size);
+ if(action == MotionEvent.ACTION_POINTER_UP) {
+ e.addPointer(1, mLastX[1], mLastY[1]);
+ }
+ mLastX[0] = x;
+ mLastY[0] = y;
+ }
+
+ // Dynamically adjust waiting time to ensure that simulated evnets follow
+ // the time tap specified in the script
+ if (mReadScriptLineByLine) {
+ long curUpTime = SystemClock.uptimeMillis();
+ long realElapsedTime = curUpTime - mMonkeyStartTime;
+ long scriptElapsedTime = eventTime - mScriptStartTime;
+ if (realElapsedTime < scriptElapsedTime) {
+ long waitDuration = scriptElapsedTime - realElapsedTime;
+ mQ.addLast(new MonkeyWaitEvent(waitDuration));
+ }
+ }
+ mQ.addLast(e);
+ } catch (NumberFormatException e) {
+ }
+ return;
+ }
+
// Handle screen rotation events
if ((s.indexOf(EVENT_KEYWORD_ROTATION) >= 0) && args.length == 2) {
try {
@@ -719,7 +829,11 @@
readHeader();
}
- linesRead = readLines();
+ if (mReadScriptLineByLine) {
+ linesRead = readOneLine();
+ } else {
+ linesRead = readLines();
+ }
if (linesRead == 0) {
closeFile();
diff --git a/testrunner/test_defs.xml b/testrunner/test_defs.xml
index 35aea0f..f3928f1 100644
--- a/testrunner/test_defs.xml
+++ b/testrunner/test_defs.xml
@@ -514,12 +514,6 @@
build_path="external/skia/tests"
description="Skia tests." />
-<!-- Android STL tests -->
-<test-native name="astl"
- build_path="external/astl/tests"
- description="Android STL."
- extra_build_args="ASTL_TESTS=1" />
-
<!-- Google Test -->
<test-native name="gtest"
build_path="external/gtest"
diff --git a/tools/idegen/Android.mk b/tools/idegen/Android.mk
index e76e5b4..d424ab6 100644
--- a/tools/idegen/Android.mk
+++ b/tools/idegen/Android.mk
@@ -2,6 +2,9 @@
include $(CLEAR_VARS)
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ guavalib \
+
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_MODULE:= idegen
diff --git a/tools/idegen/index-gen.sh b/tools/idegen/index-gen.sh
new file mode 100755
index 0000000..eadaa98
--- /dev/null
+++ b/tools/idegen/index-gen.sh
@@ -0,0 +1,50 @@
+#!/bin/bash
+#
+# 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.
+#
+# Generates a module index file by searching through android source tree for make files. The
+# intellij-gen.sh script automatically calls this script the first time or if you delete the
+# generated indexed file. The only time you need to run this manually is if modules are added or
+# deleted.
+#
+# To use:
+# index-gen.sh
+#
+# Only tested on linux. Should work for macs but have not tried.
+#
+set -e
+
+script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+root_dir=`readlink -f -n $script_dir/../../..`
+tmp_file=tmp.txt
+dest_file=module-index.txt
+
+echo "Generating index file $dest_file..."
+start=$(($(date +%s%N) / 1000000))
+find $root_dir -name '*.mk' \( ! -path "$root_dir/build*" -prune \) \
+ \( -exec grep -H '^LOCAL_PACKAGE_NAME ' {} \; \
+ -false -o -exec grep -H '^LOCAL_MODULE ' {} \; \) \
+ > $tmp_file
+sed -e 's/LOCAL_PACKAGE_NAME *:= *//g' -e 's/LOCAL_MODULE *:= *//g' -e 's/\^M*$//g' < $tmp_file > $dest_file
+
+mv $dest_file $tmp_file
+# Exclude specific directories from index here.
+# TODO: make excludes more generic and configurable
+grep -v "^$root_dir/vendor/google" $tmp_file > $dest_file
+
+rm $tmp_file
+end=$(($(date +%s%N) / 1000000))
+elapse=$(($end - $start))
+echo "Took ${elapse}ms"
diff --git a/tools/idegen/intellij-gen.sh b/tools/idegen/intellij-gen.sh
new file mode 100755
index 0000000..7f00eba
--- /dev/null
+++ b/tools/idegen/intellij-gen.sh
@@ -0,0 +1,60 @@
+#!/bin/bash
+#
+# 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.
+#
+# To use:
+# intellij-gen.sh <module name>
+#
+# where module name is the LOCAL_PACKAGE_NAME in Android.mk for the project.
+#
+# For example, to generate a project for Contacts, use:
+# intellij-gen.sh Contacts
+#
+# The project directory (.idea) will be put in the root directory of the module. Sharable iml
+# files will be put into each respective module directory.
+#
+# Only tested on linux. Should work for macs but have not tried.
+#
+set -e
+
+script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+root_dir=`readlink -f -n $script_dir/../../..`
+index_file=$script_dir/module-index.txt
+idegenjar=$root_dir/out/host/common/obj/JAVA_LIBRARIES/idegen_intermediates/javalib.jar
+
+progname=`basename $0`
+if [ $# -ne 1 ]
+then
+ echo "Usage: $progname <module_name>"
+ exit 1
+fi
+
+module_name=$1
+
+if [ ! -e "$index_file" ]; then
+ echo "Module index file missing; generating this is only done the first time."
+ echo "If any dependencies change, you should generate a new index file by running index-gen.sh."
+ $script_dir/index-gen.sh
+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 $cmd
+ $cmd
+else
+ echo "Couldn't find idegen.jar. Please run make first."
+fi
diff --git a/tools/idegen/src/com/android/idegen/AggregatedModule.java b/tools/idegen/src/com/android/idegen/AggregatedModule.java
new file mode 100644
index 0000000..1497da7
--- /dev/null
+++ b/tools/idegen/src/com/android/idegen/AggregatedModule.java
@@ -0,0 +1,132 @@
+/*
+ * 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/Constants.java b/tools/idegen/src/com/android/idegen/Constants.java
new file mode 100644
index 0000000..f541dd4
--- /dev/null
+++ b/tools/idegen/src/com/android/idegen/Constants.java
@@ -0,0 +1,46 @@
+/*
+ * 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 java.nio.charset.Charset;
+
+/**
+ * Constants
+ */
+public class Constants {
+
+ public static final Charset CHARSET = Charset.forName("UTF-8");
+
+ public static final String REL_TEMPLATE_DIR = "development/tools/idegen/templates";
+ public static final String REL_MODULES_TEMPLATE = REL_TEMPLATE_DIR + "/idea/modules.xml";
+ public static final String REL_VCS_TEMPLATE = REL_TEMPLATE_DIR + "/idea/vcs.xml";
+ public static final String REL_IML_TEMPLATE = REL_TEMPLATE_DIR + "/module-template.iml";
+
+ public static final String REL_OUT_APP_DIR = "out/target/common/obj/APPS";
+
+ public static final String FRAMEWORK_MODULE = "framework";
+ public static final String[] AUTO_DEPENDENCIES = new String[]{
+ FRAMEWORK_MODULE, "libcore"
+ };
+ public static final String[] DIRS_WITH_AUTO_DEPENDENCIES = new String[] {
+ "packages", "vendor", "frameworks/ex", "frameworks/opt", "frameworks/support"
+ };
+
+ // Framework needs a special constant for it's intermediates because it does not follow
+ // normal conventions.
+ public static final String FRAMEWORK_INTERMEDIATES = "framework-res_intermediates";
+}
diff --git a/tools/idegen/src/com/android/idegen/DirectorySearch.java b/tools/idegen/src/com/android/idegen/DirectorySearch.java
new file mode 100644
index 0000000..1bbf99f
--- /dev/null
+++ b/tools/idegen/src/com/android/idegen/DirectorySearch.java
@@ -0,0 +1,141 @@
+/*
+ * 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.Sets;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.util.HashSet;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Find directories utility.
+ */
+public class DirectorySearch {
+
+ private static final HashSet<String> SOURCE_DIRS = Sets.newHashSet();
+ static {
+ SOURCE_DIRS.add("src");
+ SOURCE_DIRS.add("java");
+ }
+
+ private static final Pattern EXCLUDE_PATTERN = Pattern.compile("values-..(-.*)*");
+
+ private static File repoRoot = null;
+
+ /**
+ * 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) {
+ Preconditions.checkNotNull(file);
+ if (repoRoot != null) {
+ return repoRoot;
+ }
+
+ 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;
+ }
+ });
+ if (files.length > 0) {
+ repoRoot = file;
+ return file;
+ }
+ }
+ File parent = file.getParentFile();
+ if (parent == null) {
+ return null;
+ }
+ return findRepoRoot(parent);
+ }
+
+ /**
+ * Find all source directories from a given root file.
+ *
+ * If the root file is a file, the directory of that file will be used as the starting
+ * location.
+ *
+ * @param file The starting location. Can be a file or directory.
+ * @return List of
+ */
+ public static ImmutableList<File> findSourceDirs(File file) {
+ Preconditions.checkNotNull(file);
+ if (!file.exists()) {
+ return ImmutableList.of();
+ }
+ if (!file.isDirectory()) {
+ file = file.getParentFile();
+ }
+ ImmutableList.Builder<File> builder = ImmutableList.builder();
+ File[] children = file.listFiles();
+ for (File child : children) {
+ if (child.isDirectory()) {
+ if (SOURCE_DIRS.contains(child.getName())) {
+ builder.add(child);
+ } else {
+ ImmutableList<File> dirs = findSourceDirs(child);
+ builder.addAll(dirs);
+ }
+ }
+ }
+
+ return builder.build();
+ }
+
+ public static ImmutableList<File> findExcludeDirs(File file) {
+ Preconditions.checkNotNull(file);
+ if (!file.exists()) {
+ return ImmutableList.of();
+ }
+ if (!file.isDirectory()) {
+ file = file.getParentFile();
+ }
+ ImmutableList.Builder<File> builder = ImmutableList.builder();
+ // Go into the res folder
+ File resFile = new File(file, "res");
+ if (resFile.exists()) {
+
+ File[] children = resFile.listFiles();
+ for (File child : children) {
+ if (child.isDirectory()) {
+ Matcher matcher = EXCLUDE_PATTERN.matcher(child.getName());
+ if (matcher.matches()) {
+ // Exclude internationalization language folders.
+ // ex: values-zh
+ // But don't exclude values-land. Assume all language folders are two
+ // letters.
+ builder.add(child);
+ }
+ }
+ }
+ }
+
+ return builder.build();
+ }
+}
diff --git a/tools/idegen/src/com/android/idegen/FrameworkModule.java b/tools/idegen/src/com/android/idegen/FrameworkModule.java
new file mode 100644
index 0000000..bfcd1fa
--- /dev/null
+++ b/tools/idegen/src/com/android/idegen/FrameworkModule.java
@@ -0,0 +1,46 @@
+/*
+ * 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.collect.ImmutableList;
+
+import java.io.File;
+
+/**
+ * Special module used for framework to build one off resource directory.
+ */
+public class FrameworkModule extends StandardModule {
+
+ public FrameworkModule(String moduleName, String makeFile) {
+ super(Constants.FRAMEWORK_MODULE, makeFile, true);
+ }
+
+ @Override
+ protected String buildIntermediates() {
+ StringBuilder sb = new StringBuilder();
+ File intermediates = new File(repoRoot,
+ Constants.REL_OUT_APP_DIR + File.separator + Constants.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");
+ }
+ 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
new file mode 100644
index 0000000..d5564d5
--- /dev/null
+++ b/tools/idegen/src/com/android/idegen/IntellijProject.java
@@ -0,0 +1,195 @@
+/*
+ * 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.Sets;
+import com.google.common.io.Files;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Set;
+import java.util.logging.Logger;
+
+/**
+ * Build Intellij projects.
+ */
+public class IntellijProject {
+
+ private static final Logger logger = Logger.getLogger(IntellijProject.class.getName());
+
+ ModuleCache cache = ModuleCache.getInstance();
+ File indexFile;
+ File repoRoot;
+ File projectIdeaDir;
+ String moduleName;
+
+ public IntellijProject(String indexFile, String moduleName) {
+ this.indexFile = new File(Preconditions.checkNotNull(indexFile));
+ this.moduleName = Preconditions.checkNotNull(moduleName);
+ }
+
+ private void init() throws IOException {
+ repoRoot = DirectorySearch.findRepoRoot(indexFile);
+ cache.init(indexFile);
+ }
+
+ public void build() throws IOException {
+ init();
+ buildFrameWorkModule();
+
+ // First pass, find all dependencies and cache them.
+ Module module = cache.getAndCache(moduleName);
+ 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);
+ }
+
+ // Third phase, replace individual modules with aggregated modules
+ Iterable<Module> modules = cache.getModules();
+ for (Module mod : modules) {
+ replaceWithAggregate(mod);
+ }
+
+ // Finally create iml files for dependencies
+ 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());
+
+ }
+
+ 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);
+ }
+ }
+ }
+ }
+
+ /**
+ * Framework module needs special handling due to one off resource path:
+ * frameworks/base/Android.mk
+ */
+ private void buildFrameWorkModule() throws IOException {
+ String makeFile = cache.getMakeFile(Constants.FRAMEWORK_MODULE);
+ logger.info("makefile: " + makeFile);
+ StandardModule frameworkModule = new FrameworkModule(Constants.FRAMEWORK_MODULE, makeFile);
+ frameworkModule.build();
+ cache.put(frameworkModule);
+ }
+
+ private void createModulesFile(Module module) throws IOException {
+ String modulesContent = Files.toString(new File(repoRoot, Constants.REL_MODULES_TEMPLATE),
+ Constants.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);
+ File iml = mod.getImlFile();
+ sb.append(" <module fileurl=\"file://").append(iml.getAbsolutePath())
+ .append("\" filepath=\"").append(iml.getAbsolutePath()).append("\" />\n");
+ }
+ modulesContent = modulesContent.replace("@MODULES@", sb.toString());
+
+ File out = new File(projectIdeaDir, "modules.xml");
+ logger.info("Creating " + out.getAbsolutePath());
+ Files.write(modulesContent, out, Constants.CHARSET);
+ }
+
+ private void createVcsFile(Module module) throws IOException {
+ String vcsTemplate = Files.toString(new File(module.getRepoRoot(),
+ Constants.REL_VCS_TEMPLATE), Constants.CHARSET);
+
+ StringBuilder sb = new StringBuilder();
+ for (String name : module.getAllDependencies()) {
+ Module mod = cache.getAndCache(name);
+ File dir = mod.getDir();
+ File gitRoot = new File(dir, ".git");
+ if (gitRoot.exists()) {
+ sb.append(" <mapping directory=\"").append(dir.getAbsolutePath())
+ .append("\" vcs=\"Git\" />\n");
+ }
+ }
+ vcsTemplate = vcsTemplate.replace("@VCS@", sb.toString());
+ Files.write(vcsTemplate, new File(projectIdeaDir, "vcs.xml"), Constants.CHARSET);
+ }
+
+ private void createNameFile(String name) throws IOException {
+ File out = new File(projectIdeaDir, ".name");
+ Files.write(name, out, Constants.CHARSET);
+ }
+
+ private void copyTemplates() throws IOException {
+ File templateDir = new File(repoRoot,
+ Constants.REL_TEMPLATE_DIR + File.separatorChar + "idea");
+ copyTemplates(templateDir, projectIdeaDir);
+ }
+
+ private void copyTemplates(File fromDir, File toDir) throws IOException {
+ toDir.mkdir();
+ File[] files = fromDir.listFiles();
+ for (File file : files) {
+ if (file.isDirectory()) {
+ File destDir = new File(toDir, file.getName());
+ copyTemplates(file, destDir);
+ } else {
+ File toFile = new File(toDir, file.getName());
+ logger.info("copying " + file.getAbsolutePath() + " to " +
+ toFile.getAbsolutePath());
+ Files.copy(file, toFile);
+ }
+ }
+ }
+
+ public static void main(String[] args) {
+ logger.info("Args: " + Arrays.toString(args));
+
+ String indexFile = args[0];
+ String module = args[1];
+
+ IntellijProject intellij = new IntellijProject(indexFile, module);
+ try {
+ intellij.build();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/tools/idegen/src/com/android/idegen/MakeFileParser.java b/tools/idegen/src/com/android/idegen/MakeFileParser.java
new file mode 100644
index 0000000..3594d50
--- /dev/null
+++ b/tools/idegen/src/com/android/idegen/MakeFileParser.java
@@ -0,0 +1,233 @@
+/*
+ * 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.base.Splitter;
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.io.Files;
+import com.google.common.io.LineProcessor;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.logging.Logger;
+
+/**
+ * Parses the make files and finds the appropriate section a given module.
+ */
+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;
+
+ /**
+ * Create a parser for a given make file and module name.
+ * <p>
+ * 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) {
+ this.makeFile = Preconditions.checkNotNull(makeFile);
+ this.moduleName = Preconditions.checkNotNull(moduleName);
+ }
+
+ public Iterable<String> getValues(String key) {
+ String str = values.get(key);
+ if (str == null) {
+ return null;
+ }
+ 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
+ */
+ public void parse() throws IOException {
+ values = Maps.newHashMap();
+ logger.info("Parsing " + makeFile.getAbsolutePath() + " for module " + moduleName);
+
+ 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);
+ }
+ }
+ });
+ }
+
+ @Override
+ public String toString() {
+ return Objects.toStringHelper(this)
+ .add("values", values)
+ .toString();
+ }
+}
diff --git a/tools/idegen/src/com/android/idegen/Module.java b/tools/idegen/src/com/android/idegen/Module.java
new file mode 100644
index 0000000..7ba42a3
--- /dev/null
+++ b/tools/idegen/src/com/android/idegen/Module.java
@@ -0,0 +1,198 @@
+/*
+ * 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.collect.ImmutableList;
+import com.google.common.collect.Sets;
+import com.google.common.io.Files;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Set;
+import java.util.logging.Logger;
+
+/**
+ * Super class for all modules.
+ */
+public abstract class Module {
+
+ private static final Logger logger = Logger.getLogger(Module.class.getName());
+
+ /**
+ * All possible attributes for the make file.
+ */
+ protected enum Key {
+ LOCAL_STATIC_JAVA_LIBRARIES,
+ LOCAL_JAVA_LIBRARIES,
+ LOCAL_SRC_FILES
+ }
+
+ 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;
+ protected abstract String getName();
+ protected abstract File getDir();
+ protected abstract boolean isAndroidModule();
+ protected abstract List<File> getIntermediatesDirs();
+ public abstract Set<String> getDirectDependencies();
+ protected abstract ImmutableList<File> getSourceDirs();
+ protected abstract ImmutableList<File> getExcludeDirs();
+ public abstract File getRepoRoot();
+
+ public void buildImlFile() throws IOException {
+ String imlTemplate = Files.toString(new File(getRepoRoot(), Constants.REL_IML_TEMPLATE),
+ Constants.CHARSET);
+
+ String facetXml = "";
+ if (isAndroidModule()) {
+ facetXml = buildAndroidFacet();
+ }
+ imlTemplate = imlTemplate.replace("@FACETS@", facetXml);
+
+ String moduleDir = getDir().getAbsolutePath();
+
+ 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());
+ sourceDirectories.append(" <sourceFolder url=\"file://$MODULE_DIR$")
+ .append(relative).append("\" isTestSource=\"false\" />\n");
+ }
+ ImmutableList<File> excludeDirs = getExcludeDirs();
+ for (File src : excludeDirs) {
+ String relative = src.getAbsolutePath().substring(moduleDir.length());
+ sourceDirectories.append(" <excludeFolder url=\"file://$MODULE_DIR$")
+ .append(relative).append("\"/>\n");
+ }
+ sourceDirectories.append(" </content>\n");
+
+ // Intermediates.
+ sourceDirectories.append(buildIntermediates());
+
+ imlTemplate = imlTemplate.replace("@SOURCES@", sourceDirectories.toString());
+
+ StringBuilder moduleDependencies = new StringBuilder();
+ for (String dependency : getDirectDependencies()) {
+ moduleDependencies.append(" <orderEntry type=\"module\" module-name=\"")
+ .append(dependency).append("\" />\n");
+ }
+ imlTemplate = imlTemplate.replace("@MODULE_DEPENDENCIES@", moduleDependencies.toString());
+
+ imlFile = new File(moduleDir, getName() + ".iml");
+ logger.info("Creating " + imlFile.getAbsolutePath());
+ Files.write(imlTemplate, imlFile, Constants.CHARSET);
+ }
+
+ protected String buildIntermediates() {
+ 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())
+ .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) {
+
+ Module child = moduleCache.getAndCache(dependency);
+ if (child == null) {
+ directDependencies.remove(dependency);
+ } else {
+ addAllDependencies(dependency);
+ addAllDependencies(child.getAllDependencies());
+ //logger.info("Adding iml " + child.getName() + " " + child.getImlFile());
+ allDependentImlFiles.add(child.getImlFile());
+ allDependentImlFiles.addAll(child.getAllDependentImlFiles());
+ }
+ }
+ }
+
+ public File getImlFile() {
+ return imlFile;
+ }
+
+ public Set<String> getAllDependencies() {
+ 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() {
+ // 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 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=\"MANIFEST_FILE_RELATIVE_PATH\" value=\""
+ + "/AndroidManifest.xml\" />\n"
+ + " <option name=\"RES_FOLDER_RELATIVE_PATH\" value=\"/res\" />\n"
+ + " <option name=\"ASSETS_FOLDER_RELATIVE_PATH\" value=\"/assets\" />\n"
+ + " <option name=\"LIBS_FOLDER_RELATIVE_PATH\" value=\"/libs\" />\n"
+ + " <option name=\"REGENERATE_R_JAVA\" value=\"true\" />\n"
+ + " <option name=\"REGENERATE_JAVA_BY_AIDL\" value=\"true\" />\n"
+ + " <option name=\"USE_CUSTOM_APK_RESOURCE_FOLDER\" value=\"false\" />\n"
+ + " <option name=\"CUSTOM_APK_RESOURCE_FOLDER\" value=\"\" />\n"
+ + " <option name=\"USE_CUSTOM_COMPILER_MANIFEST\" value=\"false\" />\n"
+ + " <option name=\"CUSTOM_COMPILER_MANIFEST\" value=\"\" />\n"
+ + " <option name=\"APK_PATH\" value=\"\" />\n"
+ + " <option name=\"LIBRARY_PROJECT\" value=\"false\" />\n"
+ + " <option name=\"RUN_PROCESS_RESOURCES_MAVEN_TASK\" value=\"true\" />\n"
+ + " <option name=\"GENERATE_UNSIGNED_APK\" value=\"false\" />\n"
+ + " </configuration>\n"
+ + " </facet>\n"
+ + " </component>\n";
+ return xml;
+ }
+
+ @Override
+ public String toString() {
+ return Objects.toStringHelper(Module.class)
+ .add("name", getName())
+ .add("allDependencies", allDependencies)
+ .add("iml files", allDependentImlFiles)
+ .add("imlFile", imlFile)
+ .toString();
+ }
+}
+
diff --git a/tools/idegen/src/com/android/idegen/ModuleCache.java b/tools/idegen/src/com/android/idegen/ModuleCache.java
new file mode 100644
index 0000000..093242a
--- /dev/null
+++ b/tools/idegen/src/com/android/idegen/ModuleCache.java
@@ -0,0 +1,107 @@
+/*
+ * 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.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;
+
+/**
+ * Cache to hold built modules.
+ */
+public class ModuleCache {
+
+ private static final Logger logger = Logger.getLogger(ModuleCache.class.getName());
+
+ private static ModuleCache cache = new ModuleCache();
+
+ ModuleIndexes indexes;
+
+ HashMap<String, Module> modulesByName = Maps.newHashMap();
+
+ private ModuleCache() {
+ }
+
+ public static ModuleCache getInstance() {
+ return cache;
+ }
+
+ public void init(File indexFile) throws IOException {
+ indexes = new ModuleIndexes(indexFile);
+ indexes.build();
+ }
+
+ public Module getAndCache(String moduleName) throws IOException {
+ Preconditions.checkState(indexes != null, "You must call init() first.");
+
+ 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);
+ 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 null;
+ }
+}
diff --git a/tools/idegen/src/com/android/idegen/ModuleIndexes.java b/tools/idegen/src/com/android/idegen/ModuleIndexes.java
new file mode 100644
index 0000000..890389f
--- /dev/null
+++ b/tools/idegen/src/com/android/idegen/ModuleIndexes.java
@@ -0,0 +1,122 @@
+/*
+ * 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.Maps;
+import com.google.common.collect.Sets;
+import com.google.common.io.Files;
+import com.google.common.io.LineProcessor;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Mapping of module names to make files.
+ */
+public class ModuleIndexes {
+
+ private static final Logger logger = Logger.getLogger(ModuleIndexes.class.getName());
+
+ private File indexFile;
+ private HashMap<String, String> moduleNameToMakeFileMap;
+ private HashMap<String, Set<String>> makeFileToModuleNamesMap;
+
+ public ModuleIndexes(File indexFile) {
+ this.indexFile = indexFile;
+ }
+
+ public void build() throws IOException {
+
+ moduleNameToMakeFileMap = Maps.newHashMap();
+ makeFileToModuleNamesMap = Maps.newHashMap();
+ logger.info("Building index from " + indexFile.getAbsolutePath());
+ Files.readLines(indexFile, Charset.forName("UTF-8"),
+ new LineProcessor<Object>() {
+ int count = 0;
+
+ @Override
+ public boolean processLine(String line) throws IOException {
+ count++;
+ String[] arr = line.split(":");
+ if (arr.length < 2) {
+ logger.log(Level.WARNING,
+ "Ignoring index line " + count + ". Bad format: " + line);
+ } else {
+ String makeFile = arr[0];
+ String moduleName = arr[1];
+ moduleNameToMakeFileMap.put(moduleName, makeFile);
+ append(makeFile, moduleName);
+ }
+ return true;
+ }
+
+ @Override
+ public Object getResult() {
+ return null;
+ }
+ });
+ }
+
+ private void append(String makeFile, String moduleName) {
+ Set<String> moduleNames = makeFileToModuleNamesMap.get(makeFile);
+ if (moduleNames == null) {
+ moduleNames = Sets.newHashSet();
+ makeFileToModuleNamesMap.put(makeFile, moduleNames);
+ } else {
+ // Create a aggregate module place holder.
+ //moduleNameToMakeFileMap.put(getAggregateName(moduleName), makeFile);
+ }
+ moduleNames.add(moduleName);
+ }
+
+ 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";
+ }
+}
diff --git a/tools/idegen/src/com/android/idegen/StandardModule.java b/tools/idegen/src/com/android/idegen/StandardModule.java
new file mode 100644
index 0000000..dffb95e
--- /dev/null
+++ b/tools/idegen/src/com/android/idegen/StandardModule.java
@@ -0,0 +1,215 @@
+/*
+ * 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.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 {
+
+ private static final Logger logger = Logger.getLogger(StandardModule.class.getName());
+
+ private static final Pattern SRC_PATTERN = Pattern.compile(
+ ".*\\(call all-java-files-under, (.*)\\)");
+
+ 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(moduleName, new File(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 +
+ Constants.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 : Constants.DIRS_WITH_AUTO_DEPENDENCIES) {
+ // length + 2 to account for slash
+ boolean isDir = makeFile.getAbsolutePath().startsWith(repoRoot + "/" + dir);
+ if (isDir) {
+ for (String dependency : Constants.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();
+ }
+}
diff --git a/tools/idegen/templates/idea/compiler.xml b/tools/idegen/templates/idea/compiler.xml
new file mode 100644
index 0000000..9d29283
--- /dev/null
+++ b/tools/idegen/templates/idea/compiler.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="CompilerConfiguration">
+ <option name="DEFAULT_COMPILER" value="Javac" />
+ <resourceExtensions />
+ <wildcardResourcePatterns>
+ <entry name="?*.properties" />
+ <entry name="?*.xml" />
+ <entry name="?*.gif" />
+ <entry name="?*.png" />
+ <entry name="?*.jpeg" />
+ <entry name="?*.jpg" />
+ <entry name="?*.html" />
+ <entry name="?*.dtd" />
+ <entry name="?*.tld" />
+ <entry name="?*.ftl" />
+ </wildcardResourcePatterns>
+ <annotationProcessing enabled="false" useClasspath="true" />
+ </component>
+ <component name="JavacSettings">
+ <option name="MAXIMUM_HEAP_SIZE" value="1024" />
+ </component>
+</project>
diff --git a/tools/idegen/templates/idea/copyright/Apache_2.xml b/tools/idegen/templates/idea/copyright/Apache_2.xml
new file mode 100644
index 0000000..fd0c79b
--- /dev/null
+++ b/tools/idegen/templates/idea/copyright/Apache_2.xml
@@ -0,0 +1,9 @@
+<component name="CopyrightManager">
+ <copyright>
+ <option name="notice" value="Copyright (C) &#36;today.year 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" />
+ <option name="keyword" value="Copyright" />
+ <option name="allowReplaceKeyword" value="" />
+ <option name="myName" value="Apache 2" />
+ <option name="myLocal" value="true" />
+ </copyright>
+</component>
\ No newline at end of file
diff --git a/tools/idegen/templates/idea/copyright/profiles_settings.xml b/tools/idegen/templates/idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..7a6be8d
--- /dev/null
+++ b/tools/idegen/templates/idea/copyright/profiles_settings.xml
@@ -0,0 +1,7 @@
+<component name="CopyrightManager">
+ <settings default="">
+ <module2copyright>
+ <element module="Project Files" copyright="Apache 2" />
+ </module2copyright>
+ </settings>
+</component>
\ No newline at end of file
diff --git a/tools/idegen/templates/idea/misc.xml b/tools/idegen/templates/idea/misc.xml
new file mode 100644
index 0000000..d3edec0
--- /dev/null
+++ b/tools/idegen/templates/idea/misc.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="ConfigCheckProjectState">
+ <option name="disabledCheckers">
+ <list>
+ <option value="com.google.devtools.intellig.configcheck.JavacHeapChecker" />
+ <option value="com.google.devtools.intellig.configcheck.VcsMappingsChecker" />
+ </list>
+ </option>
+ </component>
+ <component name="ContinuousBuildConfigurationComponent">
+ <builds>
+ <build intervalToCheckBuild="1" buildKey="" buildLabel="" enabled="false" tapBuild="false" />
+ </builds>
+ </component>
+ <component name="DependencyValidationManager">
+ <option name="SKIP_IMPORT_STATEMENTS" value="false" />
+ </component>
+ <component name="EntryPointsManager">
+ <entry_points version="2.0" />
+ </component>
+ <component name="JavadocGenerationManager">
+ <option name="OUTPUT_DIRECTORY" />
+ <option name="OPTION_SCOPE" value="protected" />
+ <option name="OPTION_HIERARCHY" value="true" />
+ <option name="OPTION_NAVIGATOR" value="true" />
+ <option name="OPTION_INDEX" value="true" />
+ <option name="OPTION_SEPARATE_INDEX" value="true" />
+ <option name="OPTION_DOCUMENT_TAG_USE" value="false" />
+ <option name="OPTION_DOCUMENT_TAG_AUTHOR" value="false" />
+ <option name="OPTION_DOCUMENT_TAG_VERSION" value="false" />
+ <option name="OPTION_DOCUMENT_TAG_DEPRECATED" value="true" />
+ <option name="OPTION_DEPRECATED_LIST" value="true" />
+ <option name="OTHER_OPTIONS" value="" />
+ <option name="HEAP_SIZE" />
+ <option name="LOCALE" />
+ <option name="OPEN_IN_BROWSER" value="true" />
+ </component>
+ <component name="Mach LOCAL_PREFIX stripper" stripping="true" />
+ <component name="ProjectResources">
+ <default-html-doctype>http://www.w3.org/1999/xhtml</default-html-doctype>
+ </component>
+ <component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" assert-keyword="true" jdk-15="true" project-jdk-name="1.6" project-jdk-type="JavaSDK" />
+ <component name="WebServicesPlugin" addRequiredLibraries="true" />
+</project>
+
diff --git a/tools/idegen/templates/idea/modules.xml b/tools/idegen/templates/idea/modules.xml
new file mode 100644
index 0000000..588cc09
--- /dev/null
+++ b/tools/idegen/templates/idea/modules.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="ProjectModuleManager">
+ <modules>
+@MODULES@
+ </modules>
+ </component>
+</project>
+
diff --git a/tools/idegen/templates/idea/vcs.xml b/tools/idegen/templates/idea/vcs.xml
new file mode 100644
index 0000000..176af35
--- /dev/null
+++ b/tools/idegen/templates/idea/vcs.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="VcsDirectoryMappings">
+ <mapping directory="" vcs="Git" />
+@VCS@
+ </component>
+</project>
diff --git a/tools/idegen/templates/module-template.iml b/tools/idegen/templates/module-template.iml
new file mode 100644
index 0000000..02301f7
--- /dev/null
+++ b/tools/idegen/templates/module-template.iml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+@FACETS@
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
+ <exclude-output />
+@SOURCES@
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+@MODULE_DEPENDENCIES@
+ </component>
+</module>
+