1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.idegen;
18 
19 import com.google.common.base.Preconditions;
20 import com.google.common.collect.Lists;
21 import com.google.common.io.Files;
22 
23 import java.io.File;
24 import java.io.IOException;
25 import java.nio.charset.Charset;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.HashSet;
29 import java.util.Set;
30 import java.util.logging.Logger;
31 
32 /**
33  * Build Intellij projects.
34  */
35 public class IntellijProject {
36 
37     public static final String FRAMEWORK_MODULE_DIR = "frameworks/base";
38     public static final Charset CHARSET = Charset.forName("UTF-8");
39 
40     private static final Logger logger = Logger.getLogger(IntellijProject.class.getName());
41 
42     private static final String MODULES_TEMPLATE_FILE_NAME = "modules.xml";
43     private static final String VCS_TEMPLATE_FILE_NAME = "vcs.xml";
44 
45     ModuleCache cache = ModuleCache.getInstance();
46 
47     boolean buildFramework;
48     File indexFile;
49     File projectPath;
50     ArrayList<String> moduleDirs;
51 
IntellijProject(String indexFile, String projectPath, ArrayList<String> moduleDirs, boolean buildFramework)52     public IntellijProject(String indexFile, String projectPath, ArrayList<String> moduleDirs,
53             boolean buildFramework) {
54         this.indexFile = new File(Preconditions.checkNotNull(indexFile));
55         this.projectPath = new File(Preconditions.checkNotNull(projectPath));
56         this.moduleDirs = Preconditions.checkNotNull(moduleDirs);
57         this.buildFramework = buildFramework;
58         DirectorySearch.findAndInitRepoRoot(this.indexFile);
59     }
60 
build()61     public void build() throws IOException {
62         cache.init(indexFile);
63         File repoRoot = DirectorySearch.getRepoRoot();
64         if (buildFramework) {
65             File frameworkDir = new File(repoRoot, FRAMEWORK_MODULE_DIR);
66             // Some unbundled apps/branches do not include the framework.
67             if (frameworkDir.exists()) {
68                 buildFrameWorkModule(new File(repoRoot, FRAMEWORK_MODULE_DIR));
69             }
70         }
71 
72         for (String moduleDir : moduleDirs) {
73             // First pass, find all dependencies and cache them.
74             File dir = new File(repoRoot, moduleDir);
75             if (!dir.exists()) {
76                 logger.info("Directory " + moduleDir + " does not exist in " + repoRoot +
77                         ". Are you sure the directory is correct?");
78                 return;
79             }
80             Module module = cache.getAndCacheByDir(dir);
81             if (module == null) {
82                 logger.info("Module '" + dir.getPath() + "' not found." +
83                         " Module names are case senstive.");
84                 return;
85             }
86         }
87 
88         // Finally create iml files for dependencies
89         Iterable<Module> modules = cache.getModules();
90         for (Module mod : modules) {
91             mod.buildImlFile();
92         }
93 
94         createProjectFiles();
95     }
96 
createProjectFiles()97     private void createProjectFiles() throws IOException {
98         File ideaDir = new File(projectPath, ".idea");
99         ideaDir.mkdirs();
100         copyTemplates(ideaDir);
101         createModulesFile(ideaDir, cache.getModules());
102         createVcsFile(ideaDir, cache.getModules());
103         createNameFile(ideaDir, projectPath.getName());
104     }
105 
106     /**
107      * Framework module needs special handling due to one off resource path:
108      * frameworks/base/Android.mk
109      */
buildFrameWorkModule(File frameworkModuleDir)110     private void buildFrameWorkModule(File frameworkModuleDir) throws IOException {
111         FrameworkModule frameworkModule = new FrameworkModule(frameworkModuleDir);
112         frameworkModule.build();
113         cache.put(frameworkModule);
114     }
115 
createModulesFile(File ideaDir, Iterable<Module> modules)116     private void createModulesFile(File ideaDir, Iterable<Module> modules) throws IOException {
117         String modulesContent = Files.toString(new File(DirectorySearch.findTemplateDir(),
118                 "idea" + File.separator + MODULES_TEMPLATE_FILE_NAME), CHARSET);
119         StringBuilder sb = new StringBuilder();
120         for (Module mod : modules) {
121             File iml = mod.getImlFile();
122             sb.append("      <module fileurl=\"file://").append(iml.getCanonicalPath()).append(
123                     "\" filepath=\"").append(iml.getCanonicalPath()).append("\" />\n");
124         }
125         modulesContent = modulesContent.replace("@MODULES@", sb.toString());
126 
127         File out = new File(ideaDir, "modules.xml");
128         logger.info("Creating " + out.getCanonicalPath());
129         Files.write(modulesContent, out, CHARSET);
130     }
131 
createVcsFile(File ideaDir, Iterable<Module> modules)132     private void createVcsFile(File ideaDir, Iterable<Module> modules) throws IOException {
133         String vcsTemplate = Files.toString(new File(DirectorySearch.findTemplateDir(),
134                 "idea" + File.separator + VCS_TEMPLATE_FILE_NAME), CHARSET);
135 
136         Set<String> gitRoots = new HashSet<>();
137         for (Module mod : modules) {
138             File dir = mod.getDir();
139             // Look for git root in the module directory and its parents.
140             while (dir != null) {
141                 File gitRoot = new File(dir, ".git");
142                 if (gitRoot.exists()) {
143                     gitRoots.add(dir.getCanonicalPath());
144                     break;
145                 } else {
146                     dir = dir.getParentFile();
147                 }
148             }
149         }
150         StringBuilder sb = new StringBuilder();
151         for (String root : gitRoots) {
152             sb.append("    <mapping directory=\"").append(root).append("\" vcs=\"Git\" />\n");
153         }
154 
155         vcsTemplate = vcsTemplate.replace("@VCS@", sb.toString());
156         Files.write(vcsTemplate, new File(ideaDir, "vcs.xml"), CHARSET);
157     }
158 
createNameFile(File ideaDir, String name)159     private void createNameFile(File ideaDir, String name) throws IOException {
160         File out = new File(ideaDir, ".name");
161         Files.write(name, out, CHARSET);
162     }
163 
copyTemplates(File ideaDir)164     private void copyTemplates(File ideaDir) throws IOException {
165         File templateDir = DirectorySearch.findTemplateDir();
166         copyTemplates(new File(templateDir, "idea"), ideaDir);
167     }
168 
copyTemplates(File fromDir, File toDir)169     private void copyTemplates(File fromDir, File toDir) throws IOException {
170         toDir.mkdir();
171         File[] files = fromDir.listFiles();
172         for (File file : files) {
173             if (file.isDirectory()) {
174                 File destDir = new File(toDir, file.getName());
175                 if (!destDir.exists()) {
176                     destDir.mkdirs();
177                 }
178                 copyTemplates(file, destDir);
179             } else {
180                 File toFile = new File(toDir, file.getName());
181                 logger.info("copying " + file.getCanonicalPath() + " to " +
182                         toFile.getCanonicalPath());
183                 Files.copy(file, toFile);
184             }
185         }
186     }
187 
main(String[] args)188     public static void main(String[] args) {
189         logger.info("Args: " + Arrays.toString(args));
190 
191         if (args.length < 3) {
192             logger.severe("Not enough input arguments. Aborting");
193             return;
194         }
195 
196         boolean buildFramework = true;
197         int argIndex = 0;
198         String arg = args[argIndex];
199         while (arg.startsWith("--")) {
200             if  (arg.equals("--no-framework")) {
201                 buildFramework = false;
202             }
203             argIndex++;
204             arg = args[argIndex];
205         }
206 
207         String indexFile = args[argIndex++];
208         String projectPath = args[argIndex++];
209         // Remaining args are module directories
210         ArrayList<String> moduleDirs = Lists.newArrayList();
211         for (int i = argIndex; i < args.length; i++) {
212             moduleDirs.add(args[i]);
213         }
214 
215         IntellijProject intellij = new IntellijProject(indexFile, projectPath, moduleDirs,
216                 buildFramework);
217         try {
218             intellij.build();
219         } catch (IOException e) {
220             e.printStackTrace();
221         }
222     }
223 }
224