Merge "Overhaul of intellij-gen script."
This commit is contained in:
@@ -38,8 +38,8 @@ if [ ! -e $root_dir/.repo ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
tmp_file=tmp.txt
|
tmp_file=${root_dir}/tmp.txt
|
||||||
dest_file=module-index.txt
|
dest_file=${root_dir}/module-index.txt
|
||||||
|
|
||||||
echo "Generating index file $dest_file..."
|
echo "Generating index file $dest_file..."
|
||||||
start=$(($(date +%s%N) / 1000000))
|
start=$(($(date +%s%N) / 1000000))
|
||||||
|
|||||||
@@ -32,13 +32,15 @@
|
|||||||
set -e
|
set -e
|
||||||
|
|
||||||
progname=`basename $0`
|
progname=`basename $0`
|
||||||
if [ $# -ne 1 ]
|
if [ $# -lt 2 ]
|
||||||
then
|
then
|
||||||
echo "Usage: $progname <module_name>"
|
echo "Usage: $progname project_dir module_dir <module_dir>..."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
module_name=$1
|
project_dir=${PWD}/$1
|
||||||
|
shift
|
||||||
|
module_dirs=$@
|
||||||
|
echo $module_dirs
|
||||||
script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||||
root_dir=$PWD
|
root_dir=$PWD
|
||||||
if [ ! -e $root_dir/.repo ]; then
|
if [ ! -e $root_dir/.repo ]; then
|
||||||
@@ -63,8 +65,8 @@ fi
|
|||||||
|
|
||||||
echo "Checking for $idegenjar"
|
echo "Checking for $idegenjar"
|
||||||
if [ -e "$idegenjar" ]; then
|
if [ -e "$idegenjar" ]; then
|
||||||
echo "Generating project files for $module_name"
|
echo "Generating project files for $module_dirs"
|
||||||
cmd="java -cp $idegenjar com.android.idegen.IntellijProject $index_file $module_name"
|
cmd="java -cp $idegenjar com.android.idegen.IntellijProject $index_file $project_dir $module_dirs"
|
||||||
echo $cmd
|
echo $cmd
|
||||||
$cmd
|
$cmd
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -23,6 +23,7 @@ import com.google.common.collect.Sets;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.FilenameFilter;
|
import java.io.FilenameFilter;
|
||||||
|
import java.io.IOException;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.logging.Level;
|
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 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 {
|
static {
|
||||||
SOURCE_DIRS.add("src");
|
SOURCE_DIRS.add("src");
|
||||||
SOURCE_DIRS.add("java");
|
SOURCE_DIRS.add("java");
|
||||||
@@ -50,38 +52,72 @@ public class DirectorySearch {
|
|||||||
public static final String REL_TEMPLATE_PATH_FROM_ROOT = "development/tools/idegen/"
|
public static final String REL_TEMPLATE_PATH_FROM_ROOT = "development/tools/idegen/"
|
||||||
+ REL_TEMPLATE_DIR;
|
+ 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.
|
* Find the repo root. This is the root branch directory of a full repo checkout.
|
||||||
*
|
*
|
||||||
* @param file any file inside the root.
|
* @param file any file inside the root.
|
||||||
* @return the root directory.
|
* @return the root directory.
|
||||||
*/
|
*/
|
||||||
public static File findRepoRoot(File file) {
|
public static void findAndInitRepoRoot(File file) {
|
||||||
Preconditions.checkNotNull(file);
|
Preconditions.checkNotNull(file);
|
||||||
if (repoRoot != null) {
|
if (repoRoot != null) {
|
||||||
return repoRoot;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file.isDirectory()) {
|
if (file.isDirectory()) {
|
||||||
File[] files = file.listFiles(new FilenameFilter() {
|
File[] files = file.listFiles(new FilenameFilter() {
|
||||||
@Override
|
@Override
|
||||||
public boolean accept(File dir, String name) {
|
public boolean accept(File dir, String name) {
|
||||||
if (".repo".equals(name)) {
|
return ".repo".equals(name);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (files.length > 0) {
|
if (files.length > 0) {
|
||||||
repoRoot = file;
|
repoRoot = file;
|
||||||
return file;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
File parent = file.getParentFile();
|
File parent = file.getParentFile();
|
||||||
if (parent == null) {
|
if (parent == null) {
|
||||||
return null;
|
throw new IllegalStateException("Repo root not found from starting point " +
|
||||||
|
file.getPath());
|
||||||
}
|
}
|
||||||
return findRepoRoot(parent);
|
findAndInitRepoRoot(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Searches up the parent chain to find the closes module root directory. A module root is one
|
||||||
|
* with an Android.mk file in it. <p> For example, the module root for directory
|
||||||
|
* <code>package/apps/Contacts/src</code> is <code>packages/apps/Contacts</code>
|
||||||
|
*
|
||||||
|
* @return the module root.
|
||||||
|
* @throws IOException when module root is not found.
|
||||||
|
*/
|
||||||
|
public static File findModuleRoot(File path) throws IOException {
|
||||||
|
Preconditions.checkNotNull(path);
|
||||||
|
File dir;
|
||||||
|
if (path.isFile()) {
|
||||||
|
dir = path.getParentFile();
|
||||||
|
} else {
|
||||||
|
dir = path;
|
||||||
|
}
|
||||||
|
while (dir != null) {
|
||||||
|
File makeFile = new File(dir, "Android.mk");
|
||||||
|
if (makeFile.exists()) {
|
||||||
|
return dir;
|
||||||
|
} else {
|
||||||
|
dir = dir.getParentFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// At this point, there are no parents and we have not found a module. Error.
|
||||||
|
throw new IOException("Module root not found for path " + path.getCanonicalPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -105,10 +141,19 @@ public class DirectorySearch {
|
|||||||
File[] children = file.listFiles();
|
File[] children = file.listFiles();
|
||||||
for (File child : children) {
|
for (File child : children) {
|
||||||
if (child.isDirectory()) {
|
if (child.isDirectory()) {
|
||||||
|
// 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())) {
|
if (SOURCE_DIRS.contains(child.getName())) {
|
||||||
builder.add(child);
|
builder.add(child);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ImmutableList<File> dirs = findSourceDirs(child);
|
|
||||||
builder.addAll(dirs);
|
builder.addAll(dirs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -151,20 +196,28 @@ public class DirectorySearch {
|
|||||||
private static File templateDirCurrent = null;
|
private static File templateDirCurrent = null;
|
||||||
private static File templateDirRoot = null;
|
private static File templateDirRoot = null;
|
||||||
|
|
||||||
public static File findTemplateDir() throws FileNotFoundException {
|
public static File findTemplateDir() throws IOException {
|
||||||
// Cache optimization.
|
// Cache optimization.
|
||||||
if (templateDirCurrent != null && templateDirCurrent.exists()) return templateDirCurrent;
|
if (templateDirCurrent != null && templateDirCurrent.exists()) {
|
||||||
if (templateDirRoot != null && templateDirRoot.exists()) return templateDirRoot;
|
return templateDirCurrent;
|
||||||
|
}
|
||||||
|
if (templateDirRoot != null && templateDirRoot.exists()) {
|
||||||
|
return templateDirRoot;
|
||||||
|
}
|
||||||
|
|
||||||
File currentDir = null;
|
File currentDir = null;
|
||||||
try {
|
try {
|
||||||
currentDir = new File(IntellijProject.class.getProtectionDomain().getCodeSource()
|
currentDir = new File(
|
||||||
.getLocation().toURI().getPath()).getParentFile();
|
IntellijProject.class.getProtectionDomain().getCodeSource().getLocation()
|
||||||
|
.toURI().getPath()).getParentFile();
|
||||||
} catch (URISyntaxException e) {
|
} catch (URISyntaxException e) {
|
||||||
logger.log(Level.SEVERE, "Could not get jar location.", e);
|
logger.log(Level.SEVERE, "Could not get jar location.", e);
|
||||||
return null;
|
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.
|
// First check relative to current run directory.
|
||||||
templateDirCurrent = new File(currentDir, REL_TEMPLATE_DIR);
|
templateDirCurrent = new File(currentDir, REL_TEMPLATE_DIR);
|
||||||
if (templateDirCurrent.exists()) {
|
if (templateDirCurrent.exists()) {
|
||||||
@@ -178,7 +231,7 @@ public class DirectorySearch {
|
|||||||
}
|
}
|
||||||
throw new FileNotFoundException(
|
throw new FileNotFoundException(
|
||||||
"Unable to find template dir. Tried the following locations:\n" +
|
"Unable to find template dir. Tried the following locations:\n" +
|
||||||
templateDirCurrent.getAbsolutePath() + "\n" +
|
templateDirCurrent.getCanonicalPath() + "\n" +
|
||||||
templateDirRoot.getAbsolutePath());
|
templateDirRoot.getCanonicalPath());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,33 +16,35 @@
|
|||||||
|
|
||||||
package com.android.idegen;
|
package com.android.idegen;
|
||||||
|
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Special module used for framework to build one off resource directory.
|
* 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
|
// Framework needs a special constant for it's intermediates because it does not follow
|
||||||
// normal conventions.
|
// normal conventions.
|
||||||
private static final String FRAMEWORK_INTERMEDIATES = "framework-res_intermediates";
|
private static final String FRAMEWORK_INTERMEDIATES = "framework-res_intermediates";
|
||||||
|
|
||||||
public FrameworkModule(String moduleName, String makeFile) {
|
public FrameworkModule(File moduleDir) throws IOException {
|
||||||
super(IntellijProject.FRAMEWORK_MODULE, makeFile, true);
|
super(Preconditions.checkNotNull(moduleDir), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String buildIntermediates() {
|
protected String buildIntermediates() throws IOException {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
File intermediates = new File(repoRoot,
|
File intermediates = new File(DirectorySearch.getRepoRoot(),
|
||||||
REL_OUT_APP_DIR + File.separator + FRAMEWORK_INTERMEDIATES);
|
REL_OUT_APP_DIR + File.separator + FRAMEWORK_INTERMEDIATES);
|
||||||
ImmutableList<File> intermediateSrcDirs = DirectorySearch.findSourceDirs(intermediates);
|
ImmutableList<File> intermediateSrcDirs = DirectorySearch.findSourceDirs(intermediates);
|
||||||
sb.append(" <content url=\"file://").append(intermediates).append("\">\n");
|
sb.append(" <content url=\"file://").append(intermediates).append("\">\n");
|
||||||
for (File src : intermediateSrcDirs) {
|
for (File src : intermediateSrcDirs) {
|
||||||
sb.append(" <sourceFolder url=\"file://")
|
sb.append(" <sourceFolder url=\"file://")
|
||||||
.append(src.getAbsolutePath()).append("\" isTestSource=\"false\" />\n");
|
.append(src.getCanonicalPath()).append("\" isTestSource=\"false\" />\n");
|
||||||
}
|
}
|
||||||
sb.append(" </content>\n");
|
sb.append(" </content>\n");
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
|
|||||||
@@ -17,14 +17,14 @@
|
|||||||
package com.android.idegen;
|
package com.android.idegen;
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
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 com.google.common.io.Files;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -32,7 +32,7 @@ import java.util.logging.Logger;
|
|||||||
*/
|
*/
|
||||||
public class IntellijProject {
|
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");
|
public static final Charset CHARSET = Charset.forName("UTF-8");
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(IntellijProject.class.getName());
|
private static final Logger logger = Logger.getLogger(IntellijProject.class.getName());
|
||||||
@@ -42,147 +42,116 @@ public class IntellijProject {
|
|||||||
|
|
||||||
ModuleCache cache = ModuleCache.getInstance();
|
ModuleCache cache = ModuleCache.getInstance();
|
||||||
|
|
||||||
|
boolean buildFramework;
|
||||||
File indexFile;
|
File indexFile;
|
||||||
File repoRoot;
|
File projectPath;
|
||||||
File projectIdeaDir;
|
ArrayList<String> moduleDirs;
|
||||||
String moduleName;
|
|
||||||
|
|
||||||
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.indexFile = new File(Preconditions.checkNotNull(indexFile));
|
||||||
this.moduleName = Preconditions.checkNotNull(moduleName);
|
this.projectPath = new File(Preconditions.checkNotNull(projectPath));
|
||||||
}
|
this.moduleDirs = Preconditions.checkNotNull(moduleDirs);
|
||||||
|
this.buildFramework = buildFramework;
|
||||||
private void init() throws IOException {
|
DirectorySearch.findAndInitRepoRoot(this.indexFile);
|
||||||
repoRoot = DirectorySearch.findRepoRoot(indexFile);
|
|
||||||
cache.init(indexFile);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void build() throws IOException {
|
public void build() throws IOException {
|
||||||
init();
|
cache.init(indexFile);
|
||||||
buildFrameWorkModule();
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String moduleDir : moduleDirs) {
|
||||||
// First pass, find all dependencies and cache them.
|
// First pass, find all dependencies and cache them.
|
||||||
Module module = cache.getAndCache(moduleName);
|
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) {
|
if (module == null) {
|
||||||
logger.info("Module '" + moduleName + "' not found." +
|
logger.info("Module '" + dir.getPath() + "' not found." +
|
||||||
" Module names are case senstive.");
|
" Module names are case senstive.");
|
||||||
return;
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
// Finally create iml files for dependencies
|
||||||
|
Iterable<Module> modules = cache.getModules();
|
||||||
for (Module mod : modules) {
|
for (Module mod : modules) {
|
||||||
mod.buildImlFile();
|
mod.buildImlFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
createModulesFile(module);
|
createProjectFiles();
|
||||||
createVcsFile(module);
|
|
||||||
createNameFile(moduleName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void replaceWithAggregate(Module module) {
|
private void createProjectFiles() throws IOException {
|
||||||
replaceWithAggregate(module.getDirectDependencies(), module.getName());
|
File ideaDir = new File(projectPath, ".idea");
|
||||||
replaceWithAggregate(module.getAllDependencies(), module.getName());
|
ideaDir.mkdirs();
|
||||||
|
copyTemplates(ideaDir);
|
||||||
}
|
createModulesFile(ideaDir, cache.getModules());
|
||||||
|
createVcsFile(ideaDir, cache.getModules());
|
||||||
private void replaceWithAggregate(Set<String> deps, String moduleName) {
|
createNameFile(ideaDir, projectPath.getName());
|
||||||
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:
|
* Framework module needs special handling due to one off resource path:
|
||||||
* frameworks/base/Android.mk
|
* frameworks/base/Android.mk
|
||||||
*/
|
*/
|
||||||
private void buildFrameWorkModule() throws IOException {
|
private void buildFrameWorkModule(File frameworkModuleDir) throws IOException {
|
||||||
String makeFile = cache.getMakeFile(FRAMEWORK_MODULE);
|
FrameworkModule frameworkModule = new FrameworkModule(frameworkModuleDir);
|
||||||
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();
|
frameworkModule.build();
|
||||||
cache.put(frameworkModule);
|
cache.put(frameworkModule);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void createModulesFile(Module module) throws IOException {
|
private void createModulesFile(File ideaDir, Iterable<Module> modules) throws IOException {
|
||||||
String modulesContent = Files.toString(
|
String modulesContent = Files.toString(new File(DirectorySearch.findTemplateDir(),
|
||||||
new File(DirectorySearch.findTemplateDir(),
|
"idea" + File.separator + MODULES_TEMPLATE_FILE_NAME), CHARSET);
|
||||||
"idea" + File.separator + MODULES_TEMPLATE_FILE_NAME),
|
|
||||||
CHARSET);
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
File moduleIml = module.getImlFile();
|
for (Module mod : modules) {
|
||||||
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();
|
File iml = mod.getImlFile();
|
||||||
sb.append(" <module fileurl=\"file://").append(iml.getAbsolutePath())
|
sb.append(" <module fileurl=\"file://").append(iml.getCanonicalPath()).append(
|
||||||
.append("\" filepath=\"").append(iml.getAbsolutePath()).append("\" />\n");
|
"\" filepath=\"").append(iml.getCanonicalPath()).append("\" />\n");
|
||||||
}
|
}
|
||||||
modulesContent = modulesContent.replace("@MODULES@", sb.toString());
|
modulesContent = modulesContent.replace("@MODULES@", sb.toString());
|
||||||
|
|
||||||
File out = new File(projectIdeaDir, "modules.xml");
|
File out = new File(ideaDir, "modules.xml");
|
||||||
logger.info("Creating " + out.getAbsolutePath());
|
logger.info("Creating " + out.getCanonicalPath());
|
||||||
Files.write(modulesContent, out, CHARSET);
|
Files.write(modulesContent, out, CHARSET);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createVcsFile(Module module) throws IOException {
|
private void createVcsFile(File ideaDir, Iterable<Module> modules) throws IOException {
|
||||||
String vcsTemplate = Files.toString(
|
String vcsTemplate = Files.toString(new File(DirectorySearch.findTemplateDir(),
|
||||||
new File(DirectorySearch.findTemplateDir(),
|
"idea" + File.separator + VCS_TEMPLATE_FILE_NAME), CHARSET);
|
||||||
"idea" + File.separator + VCS_TEMPLATE_FILE_NAME),
|
|
||||||
CHARSET);
|
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
for (String name : module.getAllDependencies()) {
|
for (Module mod : modules) {
|
||||||
Module mod = cache.getAndCache(name);
|
|
||||||
File dir = mod.getDir();
|
File dir = mod.getDir();
|
||||||
File gitRoot = new File(dir, ".git");
|
File gitRoot = new File(dir, ".git");
|
||||||
if (gitRoot.exists()) {
|
if (gitRoot.exists()) {
|
||||||
sb.append(" <mapping directory=\"").append(dir.getAbsolutePath())
|
sb.append(" <mapping directory=\"").append(dir.getCanonicalPath()).append(
|
||||||
.append("\" vcs=\"Git\" />\n");
|
"\" vcs=\"Git\" />\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vcsTemplate = vcsTemplate.replace("@VCS@", sb.toString());
|
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 {
|
private void createNameFile(File ideaDir, String name) throws IOException {
|
||||||
File out = new File(projectIdeaDir, ".name");
|
File out = new File(ideaDir, ".name");
|
||||||
Files.write(name, out, CHARSET);
|
Files.write(name, out, CHARSET);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void copyTemplates() throws IOException {
|
private void copyTemplates(File ideaDir) throws IOException {
|
||||||
File templateDir = DirectorySearch.findTemplateDir();
|
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 {
|
private void copyTemplates(File fromDir, File toDir) throws IOException {
|
||||||
@@ -191,11 +160,14 @@ public class IntellijProject {
|
|||||||
for (File file : files) {
|
for (File file : files) {
|
||||||
if (file.isDirectory()) {
|
if (file.isDirectory()) {
|
||||||
File destDir = new File(toDir, file.getName());
|
File destDir = new File(toDir, file.getName());
|
||||||
|
if (!destDir.exists()) {
|
||||||
|
destDir.mkdirs();
|
||||||
|
}
|
||||||
copyTemplates(file, destDir);
|
copyTemplates(file, destDir);
|
||||||
} else {
|
} else {
|
||||||
File toFile = new File(toDir, file.getName());
|
File toFile = new File(toDir, file.getName());
|
||||||
logger.info("copying " + file.getAbsolutePath() + " to " +
|
logger.info("copying " + file.getCanonicalPath() + " to " +
|
||||||
toFile.getAbsolutePath());
|
toFile.getCanonicalPath());
|
||||||
Files.copy(file, toFile);
|
Files.copy(file, toFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -204,10 +176,32 @@ public class IntellijProject {
|
|||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
logger.info("Args: " + Arrays.toString(args));
|
logger.info("Args: " + Arrays.toString(args));
|
||||||
|
|
||||||
String indexFile = args[0];
|
if (args.length < 3) {
|
||||||
String module = args[1];
|
logger.severe("Not enough input arguments. Aborting");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
IntellijProject intellij = new IntellijProject(indexFile, module);
|
boolean buildFramework = true;
|
||||||
|
int argIndex = 0;
|
||||||
|
String arg = args[argIndex];
|
||||||
|
while (arg.startsWith("--")) {
|
||||||
|
if (arg.equals("--no-framework")) {
|
||||||
|
buildFramework = false;
|
||||||
|
}
|
||||||
|
argIndex++;
|
||||||
|
arg = args[argIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
String indexFile = args[argIndex++];
|
||||||
|
String projectPath = args[argIndex++];
|
||||||
|
// Remaining args are module directories
|
||||||
|
ArrayList<String> moduleDirs = Lists.newArrayList();
|
||||||
|
for (int i = argIndex; i < args.length; i++) {
|
||||||
|
moduleDirs.add(args[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
IntellijProject intellij = new IntellijProject(indexFile, projectPath, moduleDirs,
|
||||||
|
buildFramework);
|
||||||
try {
|
try {
|
||||||
intellij.build();
|
intellij.build();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import com.google.common.base.Splitter;
|
|||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
import com.google.common.io.Files;
|
import com.google.common.io.Files;
|
||||||
import com.google.common.io.LineProcessor;
|
import com.google.common.io.LineProcessor;
|
||||||
|
|
||||||
@@ -29,7 +30,9 @@ import java.io.File;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
@@ -41,16 +44,7 @@ public class MakeFileParser {
|
|||||||
private static final Logger logger = Logger.getLogger(MakeFileParser.class.getName());
|
private static final Logger logger = Logger.getLogger(MakeFileParser.class.getName());
|
||||||
public static final String VALUE_DELIMITER = "|";
|
public static final String VALUE_DELIMITER = "|";
|
||||||
|
|
||||||
private enum State {
|
|
||||||
NEW, CONTINUE
|
|
||||||
}
|
|
||||||
private enum ModuleNameKey {
|
|
||||||
LOCAL_PACKAGE_NAME,
|
|
||||||
LOCAL_MODULE
|
|
||||||
};
|
|
||||||
|
|
||||||
private File makeFile;
|
private File makeFile;
|
||||||
private String moduleName;
|
|
||||||
private HashMap<String, String> values;
|
private HashMap<String, String> values;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -59,11 +53,9 @@ public class MakeFileParser {
|
|||||||
* A make file may contain multiple modules.
|
* A make file may contain multiple modules.
|
||||||
*
|
*
|
||||||
* @param makeFile The make file to parse.
|
* @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.makeFile = Preconditions.checkNotNull(makeFile);
|
||||||
this.moduleName = Preconditions.checkNotNull(moduleName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Iterable<String> getValues(String key) {
|
public Iterable<String> getValues(String key) {
|
||||||
@@ -71,65 +63,77 @@ public class MakeFileParser {
|
|||||||
if (str == null) {
|
if (str == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return Splitter.on(VALUE_DELIMITER)
|
return Splitter.on(VALUE_DELIMITER).trimResults().omitEmptyStrings().split(str);
|
||||||
.trimResults()
|
|
||||||
.omitEmptyStrings()
|
|
||||||
.split(str);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts the relevant portion of the make file and converts into key value pairs.
|
* Extracts the relevant portion of the make file and converts into key value pairs. <p> Since
|
||||||
* <p>
|
* each make file may contain multiple build targets (modules), this method will determine which
|
||||||
* Since each make file may contain multiple build targets (modules), this method will determine
|
* part is the correct portion for the given module name.
|
||||||
* which part is the correct portion for the given module name.
|
|
||||||
*
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
*/
|
||||||
public void parse() throws IOException {
|
public void parse() throws IOException {
|
||||||
values = Maps.newHashMap();
|
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> {
|
||||||
|
|
||||||
|
private StringBuilder lineBuffer;
|
||||||
|
|
||||||
|
// Keep a list of LOCAL_ variables to clear when CLEAR_VARS is encountered.
|
||||||
|
private HashSet<String> localVars = Sets.newHashSet();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean processLine(String line) throws IOException {
|
public boolean processLine(String line) throws IOException {
|
||||||
String trimmed = line.trim();
|
String trimmed = line.trim();
|
||||||
if (Strings.isNullOrEmpty(trimmed)) {
|
// Skip comments.
|
||||||
state = State.NEW;
|
if (!trimmed.isEmpty() && trimmed.charAt(0) == '#') {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (trimmed.equals("include $(CLEAR_VARS)")) {
|
appendPartialLine(trimmed);
|
||||||
// See if we are in the right module.
|
|
||||||
if (moduleName.equals(getModuleName())) {
|
if (!trimmed.isEmpty() && trimmed.charAt(trimmed.length() - 1) == '\\') {
|
||||||
return false;
|
// This is a partial line. Do not process yet.
|
||||||
} else {
|
return true;
|
||||||
values.clear();
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
switch (state) {
|
String completeLine = lineBuffer.toString().trim();
|
||||||
case NEW:
|
// Reset the line buffer.
|
||||||
trimmed = checkContinue(trimmed);
|
lineBuffer = null;
|
||||||
if (trimmed.contains("=")) {
|
|
||||||
|
if (Strings.isNullOrEmpty(completeLine)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
processKeyValuePairs(completeLine);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processKeyValuePairs(String line) {
|
||||||
|
if (line.contains("=")) {
|
||||||
String[] arr;
|
String[] arr;
|
||||||
if (trimmed.contains(":")) {
|
if (line.contains(":")) {
|
||||||
arr = trimmed.split(":=");
|
arr = line.split(":=");
|
||||||
} else {
|
} else {
|
||||||
arr = trimmed.split("\\+=");
|
arr = line.split("\\+=");
|
||||||
}
|
}
|
||||||
if (arr.length > 2) {
|
if (arr.length > 2) {
|
||||||
logger.info("Malformed line " + line);
|
logger.info("Malformed line " + line);
|
||||||
} else {
|
} else {
|
||||||
// Store the key in case the line continues
|
// Store the key in case the line continues
|
||||||
this.key = arr[0].trim();
|
String key = arr[0].trim();
|
||||||
if (arr.length == 2) {
|
if (arr.length == 2) {
|
||||||
// There may be multiple values on one line.
|
// There may be multiple values on one line.
|
||||||
List<String> valuesArr = tokenizeValue(arr[1].trim());
|
List<String> valuesArr = tokenizeValue(arr[1]);
|
||||||
for (String value : valuesArr) {
|
for (String value : valuesArr) {
|
||||||
appendValue(this.key, value);
|
appendValue(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -137,58 +141,105 @@ public class MakeFileParser {
|
|||||||
} else {
|
} else {
|
||||||
//logger.info("Skipping line " + line);
|
//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) {
|
private void appendPartialLine(String line) {
|
||||||
// Value may contain function calls such as "$(call all-java-files-under)".
|
if (lineBuffer == null) {
|
||||||
// Tokens are separated by spaces unless it's between parens.
|
lineBuffer = new StringBuilder();
|
||||||
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 {
|
} else {
|
||||||
token.append(ch);
|
lineBuffer.append(" ");
|
||||||
}
|
}
|
||||||
if (ch == '(') {
|
if (line.endsWith("\\")) {
|
||||||
parenCount++;
|
lineBuffer.append(line.substring(0, line.length() - 2).trim());
|
||||||
} else if (ch == ')') {
|
} else {
|
||||||
parenCount--;
|
lineBuffer.append(line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// end of line check
|
|
||||||
if (token.length() > 0) {
|
|
||||||
tokens.add(token.toString());
|
|
||||||
}
|
|
||||||
return tokens;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getModuleName() {
|
private List<String> tokenizeValue(String rawValue) {
|
||||||
for (ModuleNameKey key : ModuleNameKey.values()) {
|
String value = rawValue.trim();
|
||||||
String name = values.get(key.name());
|
ArrayList<String> result = Lists.newArrayList();
|
||||||
if (name != null) {
|
if (value.isEmpty()) {
|
||||||
return name;
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value may contain function calls such as "$(call all-java-files-under)" or refer
|
||||||
|
// to variables such as "$(my_var)"
|
||||||
|
value = findVariables(value);
|
||||||
|
|
||||||
|
String[] tokens = value.split(" ");
|
||||||
|
Collections.addAll(result, tokens);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String findVariables(String value) {
|
||||||
|
|
||||||
|
int variableStart = value.indexOf('$');
|
||||||
|
// Keep going until we substituted all variables.
|
||||||
|
while (variableStart > -1) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(value.substring(0, variableStart));
|
||||||
|
|
||||||
|
// variable found
|
||||||
|
int variableEnd = findClosingParen(value, variableStart);
|
||||||
|
if (variableEnd > variableStart) {
|
||||||
|
String result = substituteVariables(value.substring(variableStart + 2, variableEnd));
|
||||||
|
sb.append(result);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Malformed variable reference in make file: " + value);
|
||||||
|
}
|
||||||
|
if (variableEnd + 1 < value.length()) {
|
||||||
|
sb.append(value.substring(variableEnd + 1));
|
||||||
|
}
|
||||||
|
value = sb.toString();
|
||||||
|
variableStart = value.indexOf('$');
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int findClosingParen(String value, int startIndex) {
|
||||||
|
int openParenCount = 0;
|
||||||
|
for (int i = startIndex; i < value.length(); i++) {
|
||||||
|
char ch = value.charAt(i);
|
||||||
|
if (ch == ')') {
|
||||||
|
openParenCount--;
|
||||||
|
if (openParenCount == 0) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
} else if (ch == '(') {
|
||||||
|
openParenCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Look for and handle $(...) variables.
|
||||||
|
*/
|
||||||
|
private String substituteVariables(String rawValue) {
|
||||||
|
if (rawValue.isEmpty()) {
|
||||||
|
return rawValue;
|
||||||
|
}
|
||||||
|
String value = rawValue;
|
||||||
|
if (value.startsWith("call all-java-files-under")) {
|
||||||
|
// Ignore the call and function, keep the args.
|
||||||
|
value = value.substring(25).trim();
|
||||||
|
} else if (value.startsWith("call")) {
|
||||||
|
value = value.substring(4).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for single variable
|
||||||
|
if (value.indexOf(' ') == -1) {
|
||||||
|
// Substitute.
|
||||||
|
value = values.get(value);
|
||||||
|
if (value == null) {
|
||||||
|
value = "";
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
} else {
|
||||||
|
return findVariables(value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -196,16 +247,6 @@ public class MakeFileParser {
|
|||||||
return null;
|
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
|
* Add a value to the hash map. If the key already exists, will append instead of
|
||||||
* over-writing the existing value.
|
* over-writing the existing value.
|
||||||
@@ -221,13 +262,15 @@ public class MakeFileParser {
|
|||||||
values.put(key, value + VALUE_DELIMITER + newValue);
|
values.put(key, value + VALUE_DELIMITER + newValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public static void main(String[] args) {
|
||||||
public String toString() {
|
MakeFileParser parser = new MakeFileParser(new File(args[0]));
|
||||||
return Objects.toStringHelper(this)
|
try {
|
||||||
.add("values", values)
|
parser.parse();
|
||||||
.toString();
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
System.out.println(parser.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,24 +17,44 @@
|
|||||||
package com.android.idegen;
|
package com.android.idegen;
|
||||||
|
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.collect.ImmutableList;
|
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.collect.Sets;
|
||||||
import com.google.common.io.Files;
|
import com.google.common.io.Files;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.logging.Logger;
|
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());
|
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 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.
|
* All possible attributes for the make file.
|
||||||
@@ -45,31 +65,155 @@ public abstract class Module {
|
|||||||
LOCAL_SRC_FILES
|
LOCAL_SRC_FILES
|
||||||
}
|
}
|
||||||
|
|
||||||
ModuleCache moduleCache = ModuleCache.getInstance();
|
private ModuleCache moduleCache = ModuleCache.getInstance();
|
||||||
|
|
||||||
private File imlFile;
|
private File imlFile;
|
||||||
|
|
||||||
private Set<String> allDependencies = Sets.newHashSet(); // direct + indirect
|
private Set<String> allDependencies = Sets.newHashSet(); // direct + indirect
|
||||||
|
|
||||||
private Set<File> allDependentImlFiles = Sets.newHashSet();
|
private Set<File> allDependentImlFiles = Sets.newHashSet();
|
||||||
|
|
||||||
protected abstract void build() throws IOException;
|
private File makeFile;
|
||||||
|
private File moduleRoot;
|
||||||
|
private HashSet<File> sourceFiles = Sets.newHashSet();
|
||||||
|
|
||||||
protected abstract String getName();
|
// Module dependencies come from LOCAL_STATIC_JAVA_LIBRARIES or LOCAL_JAVA_LIBRARIES
|
||||||
|
Set<String> explicitModuleNameDependencies = Sets.newHashSet();
|
||||||
|
// Implicit module dependencies come from src files that fall outside the module root directory.
|
||||||
|
// For example, if packages/apps/Contacts includes src files from packages/apps/ContactsCommon,
|
||||||
|
// that is an implicit module dependency. It's not a module dependency from the build
|
||||||
|
// perspective but it needs to be a separate module in intellij so that the src files can be
|
||||||
|
// shared by multiple intellij modules.
|
||||||
|
Set<File> implicitModulePathDependencies = Sets.newHashSet();
|
||||||
|
|
||||||
protected abstract File getDir();
|
String relativeIntermediatesDir;
|
||||||
|
MakeFileParser makeFileParser;
|
||||||
|
boolean parseMakeFileForSource;
|
||||||
|
|
||||||
protected abstract boolean isAndroidModule();
|
public Module(File moduleDir) throws IOException {
|
||||||
|
this(moduleDir, true);
|
||||||
|
}
|
||||||
|
|
||||||
protected abstract List<File> getIntermediatesDirs();
|
public Module(File moduleDir, boolean parseMakeFileForSource) throws IOException {
|
||||||
|
this.moduleRoot = Preconditions.checkNotNull(moduleDir);
|
||||||
|
this.makeFile = new File(moduleDir, "Android.mk");
|
||||||
|
this.relativeIntermediatesDir = calculateRelativePartToRepoRoot() + REL_OUT_APP_DIR +
|
||||||
|
File.separatorChar + getName() + "_intermediates" + File.separator + "src";
|
||||||
|
this.parseMakeFileForSource = parseMakeFileForSource;
|
||||||
|
|
||||||
public abstract Set<String> getDirectDependencies();
|
// TODO: auto-detect when framework dependency is needed instead of using coded list.
|
||||||
|
for (String dir : DIRS_WITH_AUTO_DEPENDENCIES) {
|
||||||
|
// length + 2 to account for slash
|
||||||
|
boolean isDir = makeFile.getCanonicalPath().startsWith(
|
||||||
|
DirectorySearch.getRepoRoot() + "/" + dir);
|
||||||
|
if (isDir) {
|
||||||
|
Collections.addAll(this.explicitModuleNameDependencies, AUTO_DEPENDENCIES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected abstract ImmutableList<File> getSourceDirs();
|
makeFileParser = new MakeFileParser(makeFile);
|
||||||
|
}
|
||||||
|
|
||||||
protected abstract ImmutableList<File> getExcludeDirs();
|
private String calculateRelativePartToRepoRoot() throws IOException {
|
||||||
|
String rel = moduleRoot.getCanonicalPath().substring(
|
||||||
|
DirectorySearch.getRepoRoot().getCanonicalPath().length());
|
||||||
|
int count = 0;
|
||||||
|
// Count the number of slashes to determine how far back to go.
|
||||||
|
for (int i = 0; i < rel.length(); i++) {
|
||||||
|
if (rel.charAt(i) == '/') {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
sb.append("../");
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
public abstract File getRepoRoot();
|
public void build() throws IOException {
|
||||||
|
makeFileParser.parse();
|
||||||
|
buildDependencyList();
|
||||||
|
buildDependentModules();
|
||||||
|
logger.info("Done building module " + moduleRoot);
|
||||||
|
logger.info(toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getDir() {
|
||||||
|
return moduleRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return moduleRoot.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getRelativeIntermediatesDirs() throws IOException {
|
||||||
|
return Lists.newArrayList(relativeIntermediatesDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ImmutableList<File> getSourceDirs() {
|
||||||
|
return ImmutableList.copyOf(sourceFiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ImmutableList<File> getExcludeDirs() {
|
||||||
|
return DirectorySearch.findExcludeDirs(makeFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isAndroidModule() {
|
||||||
|
File manifest = new File(moduleRoot, "AndroidManifest.xml");
|
||||||
|
return manifest.exists();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void findSourceFilesAndImplicitDependencies() throws IOException {
|
||||||
|
Iterable<String> values = makeFileParser.getValues(Key.LOCAL_SRC_FILES.name());
|
||||||
|
if (values != null) {
|
||||||
|
for (String value : values) {
|
||||||
|
File src = new File(moduleRoot, value);
|
||||||
|
|
||||||
|
// value may contain garbage at this point due to relaxed make file parsing.
|
||||||
|
// filter by existing file.
|
||||||
|
if (src.exists()) {
|
||||||
|
// Look for directories outside the current module directory.
|
||||||
|
if (value.contains("..")) {
|
||||||
|
// Find the closest Android make file.
|
||||||
|
File moduleRoot = DirectorySearch.findModuleRoot(src);
|
||||||
|
implicitModulePathDependencies.add(moduleRoot);
|
||||||
|
} else {
|
||||||
|
if (parseMakeFileForSource) {
|
||||||
|
// Check if source files are subdirectories of generic parent src
|
||||||
|
// directories. If so, no need to add since they are already included.
|
||||||
|
boolean alreadyIncluded = false;
|
||||||
|
for (String parentDir : DirectorySearch.SOURCE_DIRS) {
|
||||||
|
if (value.startsWith(parentDir)) {
|
||||||
|
alreadyIncluded = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!alreadyIncluded) {
|
||||||
|
sourceFiles.add(src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceFiles.addAll(DirectorySearch.findSourceDirs(moduleRoot));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void buildDependencyList() throws IOException {
|
||||||
|
parseDirectDependencies(Key.LOCAL_STATIC_JAVA_LIBRARIES);
|
||||||
|
parseDirectDependencies(Key.LOCAL_JAVA_LIBRARIES);
|
||||||
|
findSourceFilesAndImplicitDependencies();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseDirectDependencies(Key key) {
|
||||||
|
Iterable<String> names = makeFileParser.getValues(key.name());
|
||||||
|
if (names != null) {
|
||||||
|
for (String dependency : names) {
|
||||||
|
explicitModuleNameDependencies.add(dependency);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void buildImlFile() throws IOException {
|
public void buildImlFile() throws IOException {
|
||||||
String imlTemplate = Files.toString(
|
String imlTemplate = Files.toString(
|
||||||
@@ -82,19 +226,24 @@ public abstract class Module {
|
|||||||
}
|
}
|
||||||
imlTemplate = imlTemplate.replace("@FACETS@", facetXml);
|
imlTemplate = imlTemplate.replace("@FACETS@", facetXml);
|
||||||
|
|
||||||
String moduleDir = getDir().getAbsolutePath();
|
String moduleDir = getDir().getCanonicalPath();
|
||||||
|
|
||||||
StringBuilder sourceDirectories = new StringBuilder();
|
StringBuilder sourceDirectories = new StringBuilder();
|
||||||
sourceDirectories.append(" <content url=\"file://$MODULE_DIR$\">\n");
|
sourceDirectories.append(" <content url=\"file://$MODULE_DIR$\">\n");
|
||||||
ImmutableList<File> srcDirs = getSourceDirs();
|
ImmutableList<File> srcDirs = getSourceDirs();
|
||||||
for (File src : srcDirs) {
|
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$")
|
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();
|
ImmutableList<File> excludeDirs = getExcludeDirs();
|
||||||
for (File src : excludeDirs) {
|
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$")
|
sourceDirectories.append(" <excludeFolder url=\"file://$MODULE_DIR$")
|
||||||
.append(relative).append("\"/>\n");
|
.append(relative).append("\"/>\n");
|
||||||
}
|
}
|
||||||
@@ -106,43 +255,62 @@ public abstract class Module {
|
|||||||
imlTemplate = imlTemplate.replace("@SOURCES@", sourceDirectories.toString());
|
imlTemplate = imlTemplate.replace("@SOURCES@", sourceDirectories.toString());
|
||||||
|
|
||||||
StringBuilder moduleDependencies = new StringBuilder();
|
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=\"")
|
moduleDependencies.append(" <orderEntry type=\"module\" module-name=\"")
|
||||||
.append(dependency).append("\" />\n");
|
.append(module.getName()).append("\" />\n");
|
||||||
}
|
}
|
||||||
imlTemplate = imlTemplate.replace("@MODULE_DEPENDENCIES@", moduleDependencies.toString());
|
imlTemplate = imlTemplate.replace("@MODULE_DEPENDENCIES@", moduleDependencies.toString());
|
||||||
|
|
||||||
imlFile = new File(moduleDir, getName() + ".iml");
|
imlFile = new File(moduleDir, getName() + ".iml");
|
||||||
logger.info("Creating " + imlFile.getAbsolutePath());
|
logger.info("Creating " + imlFile.getCanonicalPath());
|
||||||
Files.write(imlTemplate, imlFile, IntellijProject.CHARSET);
|
Files.write(imlTemplate, imlFile, IntellijProject.CHARSET);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String buildIntermediates() {
|
protected String buildIntermediates() throws IOException {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
for (File intermediatesDir : getIntermediatesDirs()) {
|
for (String intermediatesDir : getRelativeIntermediatesDirs()) {
|
||||||
sb.append(" <content url=\"file://").append(intermediatesDir).append("\">\n");
|
sb.append(" <content url=\"file://$MODULE_DIR$/").append(intermediatesDir)
|
||||||
sb.append(" <sourceFolder url=\"file://")
|
.append("\">\n");
|
||||||
.append(intermediatesDir.getAbsolutePath())
|
sb.append(" <sourceFolder url=\"file://$MODULE_DIR$/")
|
||||||
|
.append(intermediatesDir)
|
||||||
.append("\" isTestSource=\"false\" />\n");
|
.append("\" isTestSource=\"false\" />\n");
|
||||||
sb.append(" </content>\n");
|
sb.append(" </content>\n");
|
||||||
}
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void buildDependentModules() throws IOException {
|
private void buildDependentModules() throws IOException {
|
||||||
Set<String> directDependencies = getDirectDependencies();
|
Set<String> moduleNameDependencies = explicitModuleNameDependencies;
|
||||||
String[] copy = directDependencies.toArray(new String[directDependencies.size()]);
|
|
||||||
for (String dependency : copy) {
|
|
||||||
|
|
||||||
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) {
|
if (child == null) {
|
||||||
directDependencies.remove(dependency);
|
moduleNameDependencies.remove(dependency);
|
||||||
} else {
|
} else {
|
||||||
addAllDependencies(dependency);
|
allDependencies.add(child.getDir().getCanonicalPath());
|
||||||
addAllDependencies(child.getAllDependencies());
|
//allDependencies.addAll(child.getAllDependencies());
|
||||||
//logger.info("Adding iml " + child.getName() + " " + child.getImlFile());
|
//logger.info("Adding iml " + child.getName() + " " + child.getImlFile());
|
||||||
allDependentImlFiles.add(child.getImlFile());
|
allDependentImlFiles.add(child.getImlFile());
|
||||||
allDependentImlFiles.addAll(child.getAllDependentImlFiles());
|
//allDependentImlFiles.addAll(child.getAllDependentImlFiles());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Don't include self. The current module may have been brought in by framework
|
||||||
|
// dependencies which will create a circular reference.
|
||||||
|
allDependencies.remove(this.getDir().getCanonicalPath());
|
||||||
|
allDependentImlFiles.remove(this.getImlFile());
|
||||||
|
|
||||||
|
// TODO: add implicit dependencies. Convert all modules to be based on directory.
|
||||||
|
for (File dependency : implicitModulePathDependencies) {
|
||||||
|
Module child = moduleCache.getAndCacheByDir(dependency);
|
||||||
|
if (child != null) {
|
||||||
|
allDependencies.add(child.getDir().getCanonicalPath());
|
||||||
|
//allDependencies.addAll(child.getAllDependencies());
|
||||||
|
//logger.info("Adding iml " + child.getName() + " " + child.getImlFile());
|
||||||
|
allDependentImlFiles.add(child.getImlFile());
|
||||||
|
//allDependentImlFiles.addAll(child.getAllDependentImlFiles());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -155,29 +323,22 @@ public abstract class Module {
|
|||||||
return allDependencies;
|
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() {
|
public Set<File> getAllDependentImlFiles() {
|
||||||
return allDependentImlFiles;
|
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
|
// Not sure how to handle android facet for multi-module since there could be more than
|
||||||
// one intermediates directory.
|
// one intermediates directory.
|
||||||
String dir = getIntermediatesDirs().get(0).getAbsolutePath();
|
String dir = getRelativeIntermediatesDirs().get(0);
|
||||||
String xml = ""
|
String xml = ""
|
||||||
+ " <component name=\"FacetManager\">\n"
|
+ " <component name=\"FacetManager\">\n"
|
||||||
+ " <facet type=\"android\" name=\"Android\">\n"
|
+ " <facet type=\"android\" name=\"Android\">\n"
|
||||||
+ " <configuration>\n"
|
+ " <configuration>\n"
|
||||||
+ " <option name=\"GEN_FOLDER_RELATIVE_PATH_APT\" value=\"" + dir + "\" />\n"
|
+ " <option name=\"GEN_FOLDER_RELATIVE_PATH_APT\" value=\"" +
|
||||||
+ " <option name=\"GEN_FOLDER_RELATIVE_PATH_AIDL\" value=\"" + dir
|
dir + "\" />\n"
|
||||||
+ "\" />\n"
|
+ " <option name=\"GEN_FOLDER_RELATIVE_PATH_AIDL\" value=\"" +
|
||||||
|
dir + "\" />\n"
|
||||||
+ " <option name=\"MANIFEST_FILE_RELATIVE_PATH\" value=\""
|
+ " <option name=\"MANIFEST_FILE_RELATIVE_PATH\" value=\""
|
||||||
+ "/AndroidManifest.xml\" />\n"
|
+ "/AndroidManifest.xml\" />\n"
|
||||||
+ " <option name=\"RES_FOLDER_RELATIVE_PATH\" value=\"/res\" />\n"
|
+ " <option name=\"RES_FOLDER_RELATIVE_PATH\" value=\"/res\" />\n"
|
||||||
@@ -199,13 +360,34 @@ public abstract class Module {
|
|||||||
return xml;
|
return xml;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hashCode(getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null || getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Module other = (Module) obj;
|
||||||
|
return Objects.equal(getName(), other.getName());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return Objects.toStringHelper(Module.class)
|
return Objects.toStringHelper(this)
|
||||||
.add("name", getName())
|
.add("name", getName())
|
||||||
.add("allDependencies", allDependencies)
|
.add("allDependencies", allDependencies)
|
||||||
.add("iml files", allDependentImlFiles)
|
.add("iml files", allDependentImlFiles).add("imlFile", imlFile)
|
||||||
.add("imlFile", imlFile)
|
.add("makeFileParser", makeFileParser)
|
||||||
|
.add("explicitModuleNameDependencies", Iterables.toString(
|
||||||
|
explicitModuleNameDependencies))
|
||||||
|
.add("implicitModulePathDependencies", Iterables.toString(
|
||||||
|
implicitModulePathDependencies))
|
||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,12 +18,10 @@ package com.android.idegen;
|
|||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import com.google.common.collect.Sets;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -37,7 +35,9 @@ public class ModuleCache {
|
|||||||
|
|
||||||
ModuleIndexes indexes;
|
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() {
|
private ModuleCache() {
|
||||||
}
|
}
|
||||||
@@ -51,57 +51,48 @@ public class ModuleCache {
|
|||||||
indexes.build();
|
indexes.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Module getAndCache(String moduleName) throws IOException {
|
public Module getAndCacheByDir(File moduleDir) throws IOException {
|
||||||
Preconditions.checkState(indexes != null, "You must call init() first.");
|
Preconditions.checkNotNull(moduleDir);
|
||||||
|
|
||||||
Module module = modulesByName.get(moduleName);
|
if (moduleDir.exists()) {
|
||||||
|
Module module = getModule(moduleDir);
|
||||||
if (module == null) {
|
if (module == null) {
|
||||||
String makeFile = indexes.getMakeFile(moduleName);
|
module = new Module(moduleDir);
|
||||||
if (makeFile == null) {
|
// Must put module before building it. Otherwise infinite loop.
|
||||||
logger.warning("Unable to find make file for module: " + moduleName);
|
putModule(moduleDir, module);
|
||||||
} else {
|
|
||||||
module = new StandardModule(moduleName, makeFile);
|
|
||||||
module.build();
|
module.build();
|
||||||
modulesByName.put(moduleName, module);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return module;
|
return module;
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public void buildAndCacheAggregatedModule(String moduleName) throws IOException {
|
public Module getAndCacheByName(String moduleName) throws IOException {
|
||||||
if (indexes.isPartOfAggregatedModule(moduleName)) {
|
Preconditions.checkState(indexes != null, "You must call init() first.");
|
||||||
Set<String> moduleNames = indexes.getAggregatedModules(moduleName);
|
Preconditions.checkNotNull(moduleName);
|
||||||
Set<Module> modules = Sets.newHashSet();
|
|
||||||
for (String name : moduleNames) {
|
String makeFile = indexes.getMakeFile(moduleName);
|
||||||
Module m = modulesByName.get(name);
|
if (makeFile == null) {
|
||||||
if (m != null) {
|
logger.warning("Unable to find make file for module: " + moduleName);
|
||||||
modules.add(m);
|
return null;
|
||||||
}
|
}
|
||||||
|
return getAndCacheByDir(new File(makeFile).getParentFile());
|
||||||
}
|
}
|
||||||
String aggregatedName = indexes.getAggregateName(moduleName);
|
|
||||||
AggregatedModule module = new AggregatedModule(aggregatedName, modules);
|
private void putModule(File moduleDir, Module module) throws IOException {
|
||||||
module.build();
|
modulesByPath.put(moduleDir.getCanonicalPath(), module);
|
||||||
modulesByName.put(aggregatedName, module);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Module getModule(File moduleDir) throws IOException {
|
||||||
|
return modulesByPath.get(moduleDir.getCanonicalPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Iterable<Module> getModules() {
|
public Iterable<Module> getModules() {
|
||||||
return modulesByName.values();
|
return modulesByPath.values();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getMakeFile(String moduleName) {
|
public void put(Module module) throws IOException {
|
||||||
return indexes.getMakeFile(moduleName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void put(StandardModule module) {
|
|
||||||
Preconditions.checkNotNull(module);
|
Preconditions.checkNotNull(module);
|
||||||
modulesByName.put(module.getName(), module);
|
putModule(module.getDir(), module);
|
||||||
}
|
|
||||||
|
|
||||||
public String getAggregateReplacementName(String moduleName) {
|
|
||||||
if (indexes.isPartOfAggregatedModule(moduleName)) {
|
|
||||||
return indexes.getAggregateName(moduleName);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ public class ModuleIndexes {
|
|||||||
|
|
||||||
moduleNameToMakeFileMap = Maps.newHashMap();
|
moduleNameToMakeFileMap = Maps.newHashMap();
|
||||||
makeFileToModuleNamesMap = 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"),
|
Files.readLines(indexFile, Charset.forName("UTF-8"),
|
||||||
new LineProcessor<Object>() {
|
new LineProcessor<Object>() {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
@@ -84,7 +84,7 @@ public class ModuleIndexes {
|
|||||||
makeFileToModuleNamesMap.put(makeFile, moduleNames);
|
makeFileToModuleNamesMap.put(makeFile, moduleNames);
|
||||||
} else {
|
} else {
|
||||||
// Create a aggregate module place holder.
|
// Create a aggregate module place holder.
|
||||||
//moduleNameToMakeFileMap.put(getAggregateName(moduleName), makeFile);
|
//moduleNameToMakeFileMap.put(getAggregateName(moduleDir), makeFile);
|
||||||
}
|
}
|
||||||
moduleNames.add(moduleName);
|
moduleNames.add(moduleName);
|
||||||
}
|
}
|
||||||
@@ -92,31 +92,7 @@ public class ModuleIndexes {
|
|||||||
public String getMakeFile(String moduleName) {
|
public String getMakeFile(String moduleName) {
|
||||||
Preconditions.checkState(moduleNameToMakeFileMap != null,
|
Preconditions.checkState(moduleNameToMakeFileMap != null,
|
||||||
"Index not built. Call build() first.");
|
"Index not built. Call build() first.");
|
||||||
|
|
||||||
return moduleNameToMakeFileMap.get(moduleName);
|
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";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user