blob: 6f797eb187a9f9932417c4d8816d024602586255 [file] [log] [blame]
/*
* Copyright 2017 The Bazel Authors. All rights reserved.
*
* 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.google.idea.blaze.android.npw.project;
import com.android.SdkConstants;
import com.android.builder.model.SourceProvider;
import com.android.tools.idea.npw.project.AndroidProjectPaths;
import com.android.tools.idea.npw.project.AndroidSourceSet;
import com.google.common.collect.Iterables;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.JavaDirectoryService;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiManager;
import com.intellij.psi.PsiPackage;
import java.io.File;
import java.util.Collections;
import java.util.List;
import javax.annotation.Nullable;
import org.jetbrains.android.facet.AndroidFacet;
/**
* Project paths for a Blaze Android project.
*
* <p>We mostly just take whatever directory the user specified and put the new component there.
* Unlike Gradle, Blaze has no strict requirements regarding the structure of an Android project,
* but there are some common conventions:
*
* <pre>
* google3/
* |-java/com/google/foo/bar/... (module root)
* | |-BUILD
* | |-AndroidManifest.xml (manifest directory)
* | |-Baz.java (source directory of com.google.foo.bar.Baz)
* | |-Baz.aidl (aidl directory, option 1)
* | |-aidl/
* | | `-com/google/foo/bar/Baz.aidl (aidl directory, option 2)
* | `-res/... (res directory, one of the few things required by the build system)
* `-javatest/com/google/foo/bar/...
* |-BUILD
* `-BazTest.java (test directory of com.google.foo.bar.BazTest)
* </pre>
*
* However, this is also possible (package name unrelated to directory structure):
*
* <pre>
* google3/experimental/users/foo/my/own/project/
* |-Baz.java (com.google.foo.bar.Baz)
* `-BazTest.java (com.google.foo.bar.BazTest)
* </pre>
*
* So is this (versioned paths that aren't reflected by the package name):
*
* <pre>
* google3/third_party/com/google/foo/bar/
* |-v1/Baz.java (com.google.foo.bar.Baz)
* `-v2/Baz.java (com.google.foo.bar.Baz)
* </pre>
*/
public class BlazeAndroidProjectPaths implements AndroidProjectPaths {
@Nullable private File moduleRoot;
@Nullable private File srcDirectory;
@Nullable private File resDirectory;
@Nullable
@Override
public File getModuleRoot() {
return moduleRoot;
}
@Nullable
@Override
public File getSrcDirectory(@Nullable String packageName) {
return srcDirectory;
}
@Nullable
@Override
public File getTestDirectory(@Nullable String packageName) {
return srcDirectory;
}
@Nullable
@Override
public File getResDirectory() {
return resDirectory;
}
@Nullable
@Override
public File getAidlDirectory(@Nullable String packageName) {
return srcDirectory;
}
@Nullable
@Override
public File getManifestDirectory() {
return srcDirectory;
}
/**
* The new component wizard uses {@link AndroidSourceSet#getName()} for the default package name
* of the new component. If we can figure it out from the target directory here, then we can pass
* it to the new component wizard.
*/
private static String getPackageName(Project project, VirtualFile targetDirectory) {
PsiDirectory psiDirectory = PsiManager.getInstance(project).findDirectory(targetDirectory);
if (psiDirectory == null) {
return null;
}
PsiPackage psiPackage = JavaDirectoryService.getInstance().getPackage(psiDirectory);
if (psiPackage == null) {
return null;
}
return psiPackage.getQualifiedName();
}
public static List<AndroidSourceSet> getSourceSets(
AndroidFacet androidFacet, @Nullable VirtualFile targetDirectory) {
Module module = androidFacet.getModule();
BlazeAndroidProjectPaths paths = new BlazeAndroidProjectPaths();
VirtualFile[] roots = ModuleRootManager.getInstance(module).getContentRoots();
if (roots.length > 0) {
paths.moduleRoot = VfsUtilCore.virtualToIoFile(roots[0]);
}
// We have a res dir if this happens to be a resource module.
SourceProvider sourceProvider = androidFacet.getMainSourceProvider();
paths.resDirectory = Iterables.getFirst(sourceProvider.getResDirectories(), null);
// If this happens to be a resource package,
// the module name (resource package) would be more descriptive than the facet name (Android).
// Otherwise, .workspace is still better than (Android).
String name = androidFacet.getModule().getName();
if (targetDirectory != null) {
String packageName = getPackageName(module.getProject(), targetDirectory);
if (packageName != null) {
name = packageName;
}
paths.srcDirectory = VfsUtilCore.virtualToIoFile(targetDirectory);
} else {
// People usually put the manifest file with their sources.
paths.srcDirectory = sourceProvider.getManifestFile().getParentFile();
}
if (paths.resDirectory == null) {
paths.resDirectory = new File(paths.srcDirectory, SdkConstants.FD_RES);
}
return Collections.singletonList(new AndroidSourceSet(name, paths));
}
}