| /* |
| * 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.build.gradle; |
| |
| import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES; |
| import static com.google.common.base.Preconditions.checkState; |
| import static java.io.File.separator; |
| |
| import com.android.annotations.Nullable; |
| import com.android.annotations.VisibleForTesting; |
| import com.android.build.gradle.internal.ApiObjectFactory; |
| import com.android.build.gradle.internal.BadPluginException; |
| import com.android.build.gradle.internal.DependencyManager; |
| import com.android.build.gradle.internal.ExecutionConfigurationUtil; |
| import com.android.build.gradle.internal.ExtraModelInfo; |
| import com.android.build.gradle.internal.LibraryCache; |
| import com.android.build.gradle.internal.LoggerWrapper; |
| import com.android.build.gradle.internal.NativeLibraryFactoryImpl; |
| import com.android.build.gradle.internal.NdkHandler; |
| import com.android.build.gradle.internal.SdkHandler; |
| import com.android.build.gradle.internal.TaskContainerAdaptor; |
| import com.android.build.gradle.internal.TaskManager; |
| import com.android.build.gradle.internal.VariantManager; |
| import com.android.build.gradle.internal.coverage.JacocoPlugin; |
| import com.android.build.gradle.internal.dsl.BuildType; |
| import com.android.build.gradle.internal.dsl.BuildTypeFactory; |
| import com.android.build.gradle.internal.dsl.ProductFlavor; |
| import com.android.build.gradle.internal.dsl.ProductFlavorFactory; |
| import com.android.build.gradle.internal.dsl.SigningConfig; |
| import com.android.build.gradle.internal.dsl.SigningConfigFactory; |
| import com.android.build.gradle.internal.model.ModelBuilder; |
| import com.android.build.gradle.internal.process.GradleJavaProcessExecutor; |
| import com.android.build.gradle.internal.process.GradleProcessExecutor; |
| import com.android.build.gradle.internal.profile.RecordingBuildListener; |
| import com.android.build.gradle.internal.variant.BaseVariantData; |
| import com.android.build.gradle.internal.variant.VariantFactory; |
| import com.android.build.gradle.tasks.JillTask; |
| import com.android.build.gradle.tasks.PreDex; |
| import com.android.builder.Version; |
| import com.android.builder.core.AndroidBuilder; |
| import com.android.builder.core.BuilderConstants; |
| import com.android.builder.internal.compiler.JackConversionCache; |
| import com.android.builder.internal.compiler.PreDexCache; |
| import com.android.builder.profile.ExecutionType; |
| import com.android.builder.profile.ProcessRecorderFactory; |
| import com.android.builder.profile.Recorder; |
| import com.android.builder.profile.ThreadRecorder; |
| import com.android.builder.sdk.TargetInfo; |
| import com.android.ide.common.internal.ExecutorSingleton; |
| import com.android.utils.ILogger; |
| import com.google.common.base.CharMatcher; |
| import com.google.common.base.Splitter; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.Lists; |
| |
| import org.gradle.BuildListener; |
| import org.gradle.BuildResult; |
| import org.gradle.api.Action; |
| import org.gradle.api.GradleException; |
| import org.gradle.api.NamedDomainObjectContainer; |
| import org.gradle.api.Project; |
| import org.gradle.api.Task; |
| import org.gradle.api.artifacts.repositories.MavenArtifactRepository; |
| import org.gradle.api.execution.TaskExecutionGraph; |
| import org.gradle.api.execution.TaskExecutionGraphListener; |
| import org.gradle.api.initialization.Settings; |
| import org.gradle.api.invocation.Gradle; |
| import org.gradle.api.logging.LogLevel; |
| import org.gradle.api.plugins.JavaBasePlugin; |
| import org.gradle.api.plugins.JavaPlugin; |
| import org.gradle.api.tasks.StopExecutionException; |
| import org.gradle.internal.reflect.Instantiator; |
| import org.gradle.tooling.BuildException; |
| import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.UnsupportedEncodingException; |
| import java.math.BigInteger; |
| import java.net.URL; |
| import java.net.URLClassLoader; |
| import java.security.MessageDigest; |
| import java.security.NoSuchAlgorithmException; |
| import java.util.Calendar; |
| import java.util.GregorianCalendar; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.concurrent.TimeUnit; |
| import java.util.jar.Manifest; |
| import java.util.regex.Pattern; |
| |
| /** |
| * Base class for all Android plugins |
| */ |
| public abstract class BasePlugin { |
| |
| private static final String GRADLE_MIN_VERSION = "2.2"; |
| public static final Pattern GRADLE_ACCEPTABLE_VERSIONS = Pattern.compile("2\\.[2-9].*"); |
| private static final String GRADLE_VERSION_CHECK_OVERRIDE_PROPERTY = |
| "com.android.build.gradle.overrideVersionCheck"; |
| private static final String SKIP_PATH_CHECK_PROPERTY = |
| "com.android.build.gradle.overridePathCheck"; |
| /** default retirement age in days since its inception date for RC or beta versions. */ |
| private static final int DEFAULT_RETIREMENT_AGE_FOR_NON_RELEASE_IN_DAYS = 40; |
| |
| |
| protected BaseExtension extension; |
| |
| protected VariantManager variantManager; |
| |
| protected TaskManager taskManager; |
| |
| protected Project project; |
| |
| protected SdkHandler sdkHandler; |
| |
| private NdkHandler ndkHandler; |
| |
| protected AndroidBuilder androidBuilder; |
| |
| protected Instantiator instantiator; |
| |
| protected VariantFactory variantFactory; |
| |
| private ToolingModelBuilderRegistry registry; |
| |
| private JacocoPlugin jacocoPlugin; |
| |
| private LoggerWrapper loggerWrapper; |
| |
| private ExtraModelInfo extraModelInfo; |
| |
| private String creator; |
| |
| private boolean hasCreatedTasks = false; |
| |
| protected BasePlugin(Instantiator instantiator, ToolingModelBuilderRegistry registry) { |
| this.instantiator = instantiator; |
| this.registry = registry; |
| creator = "Android Gradle " + Version.ANDROID_GRADLE_PLUGIN_VERSION; |
| verifyRetirementAge(); |
| |
| ModelBuilder.clearCaches(); |
| } |
| |
| /** |
| * Verify that this plugin execution is within its public time range. |
| */ |
| private void verifyRetirementAge() { |
| |
| Manifest manifest; |
| URLClassLoader cl = (URLClassLoader) getClass().getClassLoader(); |
| try { |
| URL url = cl.findResource("META-INF/MANIFEST.MF"); |
| manifest = new Manifest(url.openStream()); |
| } catch (IOException ignore) { |
| return; |
| } |
| |
| String inceptionDateAttr = manifest.getMainAttributes().getValue("Inception-Date"); |
| // when running in unit tests, etc... the manifest entries are absent. |
| if (inceptionDateAttr == null) { |
| return; |
| } |
| List<String> items = ImmutableList.copyOf(Splitter.on(':').split(inceptionDateAttr)); |
| GregorianCalendar inceptionDate = new GregorianCalendar(Integer.parseInt(items.get(0)), |
| Integer.parseInt(items.get(1)), Integer.parseInt(items.get(2))); |
| |
| int retirementAgeInDays = |
| getRetirementAgeInDays(manifest.getMainAttributes().getValue("Plugin-Version")); |
| |
| if (retirementAgeInDays == -1) { |
| return; |
| } |
| Calendar now = GregorianCalendar.getInstance(); |
| long nowTimestamp = now.getTimeInMillis(); |
| long inceptionTimestamp = inceptionDate.getTimeInMillis(); |
| long days = TimeUnit.DAYS.convert(nowTimestamp - inceptionTimestamp, TimeUnit.MILLISECONDS); |
| if (days > retirementAgeInDays) { |
| // this plugin is too old. |
| String dailyOverride = System.getenv("ANDROID_DAILY_OVERRIDE"); |
| final MessageDigest crypt; |
| try { |
| crypt = MessageDigest.getInstance("SHA-1"); |
| } catch (NoSuchAlgorithmException e) { |
| return; |
| } |
| crypt.reset(); |
| // encode the day, not the current time. |
| try { |
| crypt.update(String.format("%1$s:%2$s:%3$s", |
| now.get(Calendar.YEAR), |
| now.get(Calendar.MONTH), |
| now.get(Calendar.DATE)).getBytes("utf8")); |
| } catch (UnsupportedEncodingException e) { |
| return; |
| } |
| String overrideValue = new BigInteger(1, crypt.digest()).toString(16); |
| if (dailyOverride == null) { |
| String message = "Plugin is too old, please update to a more recent version, or " + |
| "set ANDROID_DAILY_OVERRIDE environment variable to \"" + overrideValue + '"'; |
| System.err.println(message); |
| throw new RuntimeException(message); |
| } else { |
| if (!dailyOverride.equals(overrideValue)) { |
| String message = "Plugin is too old and ANDROID_DAILY_OVERRIDE value is " + |
| "also outdated, please use new value :\"" + overrideValue + '"'; |
| System.err.println(message); |
| throw new RuntimeException(message); |
| } |
| } |
| } |
| } |
| |
| private static int getRetirementAgeInDays(@Nullable String version) { |
| if (version == null || version.contains("rc") || version.contains("beta") |
| || version.contains("alpha")) { |
| return DEFAULT_RETIREMENT_AGE_FOR_NON_RELEASE_IN_DAYS; |
| } |
| return -1; |
| } |
| |
| protected abstract Class<? extends BaseExtension> getExtensionClass(); |
| protected abstract VariantFactory createVariantFactory(); |
| protected abstract TaskManager createTaskManager( |
| Project project, |
| AndroidBuilder androidBuilder, |
| AndroidConfig extension, |
| SdkHandler sdkHandler, |
| DependencyManager dependencyManager, |
| ToolingModelBuilderRegistry toolingRegistry); |
| |
| /** |
| * Return whether this plugin creates Android library. Should be overridden if true. |
| */ |
| protected boolean isLibrary() { |
| return false; |
| } |
| |
| @VisibleForTesting |
| VariantManager getVariantManager() { |
| return variantManager; |
| } |
| |
| protected ILogger getLogger() { |
| if (loggerWrapper == null) { |
| loggerWrapper = new LoggerWrapper(project.getLogger()); |
| } |
| |
| return loggerWrapper; |
| } |
| |
| |
| protected void apply(Project project) throws IOException { |
| this.project = project; |
| |
| ExecutionConfigurationUtil.setThreadPoolSize(project); |
| checkPathForErrors(); |
| checkModulesForErrors(); |
| |
| List<Recorder.Property> propertyList = Lists.newArrayList( |
| new Recorder.Property("plugin_version", Version.ANDROID_GRADLE_PLUGIN_VERSION), |
| new Recorder.Property("next_gen_plugin", "false"), |
| new Recorder.Property("gradle_version", project.getGradle().getGradleVersion()) |
| ); |
| String benchmarkName = AndroidGradleOptions.getBenchmarkName(project); |
| if (benchmarkName != null) { |
| propertyList.add(new Recorder.Property("benchmark_name", benchmarkName)); |
| } |
| String benchmarkMode = AndroidGradleOptions.getBenchmarkMode(project); |
| if (benchmarkMode != null) { |
| propertyList.add(new Recorder.Property("benchmark_mode", benchmarkMode)); |
| } |
| |
| ProcessRecorderFactory.initialize( |
| getLogger(), |
| project.getRootProject().file("profiler" + System.currentTimeMillis() + ".json"), |
| propertyList); |
| project.getGradle().addListener(new RecordingBuildListener(ThreadRecorder.get())); |
| |
| |
| ThreadRecorder.get().record(ExecutionType.BASE_PLUGIN_PROJECT_CONFIGURE, |
| new Recorder.Block<Void>() { |
| @Override |
| public Void call() throws Exception { |
| configureProject(); |
| return null; |
| } |
| }, new Recorder.Property("project", project.getName())); |
| |
| ThreadRecorder.get().record(ExecutionType.BASE_PLUGIN_PROJECT_BASE_EXTENSTION_CREATION, |
| new Recorder.Block<Void>() { |
| @Override |
| public Void call() throws Exception { |
| createExtension(); |
| return null; |
| } |
| }, new Recorder.Property("project", project.getName())); |
| |
| ThreadRecorder.get().record(ExecutionType.BASE_PLUGIN_PROJECT_TASKS_CREATION, |
| new Recorder.Block<Void>() { |
| @Override |
| public Void call() throws Exception { |
| createTasks(); |
| return null; |
| } |
| }, new Recorder.Property("project", project.getName())); |
| } |
| |
| protected void configureProject() { |
| checkGradleVersion(); |
| extraModelInfo = new ExtraModelInfo(project, isLibrary()); |
| sdkHandler = new SdkHandler(project, getLogger()); |
| androidBuilder = new AndroidBuilder( |
| project == project.getRootProject() ? project.getName() : project.getPath(), |
| creator, |
| new GradleProcessExecutor(project), |
| new GradleJavaProcessExecutor(project), |
| extraModelInfo, |
| getLogger(), |
| isVerbose()); |
| |
| project.getPlugins().apply(JavaBasePlugin.class); |
| |
| jacocoPlugin = project.getPlugins().apply(JacocoPlugin.class); |
| |
| project.getTasks().getByName("assemble").setDescription( |
| "Assembles all variants of all applications and secondary packages."); |
| |
| // call back on execution. This is called after the whole build is done (not |
| // after the current project is done). |
| // This is will be called for each (android) projects though, so this should support |
| // being called 2+ times. |
| project.getGradle().addBuildListener(new BuildListener() { |
| @Override |
| public void buildStarted(Gradle gradle) { } |
| |
| @Override |
| public void settingsEvaluated(Settings settings) { } |
| |
| @Override |
| public void projectsLoaded(Gradle gradle) { } |
| |
| @Override |
| public void projectsEvaluated(Gradle gradle) { } |
| |
| @Override |
| public void buildFinished(BuildResult buildResult) { |
| ExecutorSingleton.shutdown(); |
| sdkHandler.unload(); |
| ThreadRecorder.get().record(ExecutionType.BASE_PLUGIN_BUILD_FINISHED, |
| new Recorder.Block() { |
| @Override |
| public Void call() throws Exception { |
| PreDexCache.getCache().clear( |
| new File(project.getRootProject().getBuildDir(), |
| FD_INTERMEDIATES + "/dex-cache/cache.xml"), |
| getLogger()); |
| JackConversionCache.getCache().clear( |
| new File(project.getRootProject().getBuildDir(), |
| FD_INTERMEDIATES + "/jack-cache/cache.xml"), |
| getLogger()); |
| LibraryCache.getCache().unload(); |
| return null; |
| } |
| }, new Recorder.Property("project", project.getName())); |
| |
| try { |
| ProcessRecorderFactory.shutdown(); |
| } catch (InterruptedException e) { |
| throw new RuntimeException(e); |
| } |
| } |
| }); |
| project.getGradle().getTaskGraph().addTaskExecutionGraphListener( |
| new TaskExecutionGraphListener() { |
| @Override |
| public void graphPopulated(TaskExecutionGraph taskGraph) { |
| for (Task task : taskGraph.getAllTasks()) { |
| if (task instanceof PreDex) { |
| PreDexCache.getCache().load( |
| new File(project.getRootProject().getBuildDir(), |
| FD_INTERMEDIATES + "/dex-cache/cache.xml")); |
| break; |
| } else if (task instanceof JillTask) { |
| JackConversionCache.getCache().load( |
| new File(project.getRootProject().getBuildDir(), |
| FD_INTERMEDIATES + "/jack-cache/cache.xml")); |
| break; |
| } |
| } |
| } |
| }); |
| } |
| |
| |
| private void createExtension() { |
| final NamedDomainObjectContainer<BuildType> buildTypeContainer = project.container( |
| BuildType.class, |
| new BuildTypeFactory(instantiator, project, project.getLogger())); |
| final NamedDomainObjectContainer<ProductFlavor> productFlavorContainer = project.container( |
| ProductFlavor.class, |
| new ProductFlavorFactory(instantiator, project, project.getLogger())); |
| final NamedDomainObjectContainer<SigningConfig> signingConfigContainer = project.container( |
| SigningConfig.class, |
| new SigningConfigFactory(instantiator)); |
| |
| extension = project.getExtensions().create("android", getExtensionClass(), |
| project, instantiator, androidBuilder, sdkHandler, |
| buildTypeContainer, productFlavorContainer, signingConfigContainer, |
| extraModelInfo, isLibrary()); |
| |
| // create the default mapping configuration. |
| project.getConfigurations().create("default-mapping") |
| .setDescription("Configuration for default mapping artifacts."); |
| project.getConfigurations().create("default-metadata") |
| .setDescription("Metadata for the produced APKs."); |
| |
| DependencyManager dependencyManager = new DependencyManager(project, extraModelInfo); |
| taskManager = createTaskManager( |
| project, |
| androidBuilder, |
| extension, |
| sdkHandler, |
| dependencyManager, |
| registry); |
| |
| variantFactory = createVariantFactory(); |
| variantManager = new VariantManager( |
| project, |
| androidBuilder, |
| extension, |
| variantFactory, |
| taskManager, |
| instantiator); |
| |
| ndkHandler = new NdkHandler( |
| project.getRootDir(), |
| null, /* compileSkdVersion, this will be set in afterEvaluate */ |
| "gcc", |
| "" /*toolchainVersion*/); |
| |
| // Register a builder for the custom tooling model |
| ModelBuilder modelBuilder = new ModelBuilder( |
| androidBuilder, |
| variantManager, |
| taskManager, |
| extension, |
| extraModelInfo, |
| ndkHandler, |
| new NativeLibraryFactoryImpl(ndkHandler), |
| isLibrary()); |
| registry.register(modelBuilder); |
| |
| // map the whenObjectAdded callbacks on the containers. |
| signingConfigContainer.whenObjectAdded(new Action<SigningConfig>() { |
| @Override |
| public void execute(SigningConfig signingConfig) { |
| variantManager.addSigningConfig(signingConfig); |
| } |
| }); |
| |
| |
| buildTypeContainer.whenObjectAdded(new Action<BuildType>() { |
| @Override |
| public void execute(BuildType buildType) { |
| SigningConfig signingConfig = signingConfigContainer.findByName(BuilderConstants.DEBUG); |
| buildType.init(signingConfig); |
| variantManager.addBuildType(buildType); |
| } |
| }); |
| |
| productFlavorContainer.whenObjectAdded(new Action<ProductFlavor>() { |
| @Override |
| public void execute(ProductFlavor productFlavor) { |
| variantManager.addProductFlavor(productFlavor); |
| } |
| }); |
| |
| // map whenObjectRemoved on the containers to throw an exception. |
| signingConfigContainer.whenObjectRemoved( |
| new UnsupportedAction("Removing signingConfigs is not supported.")); |
| buildTypeContainer.whenObjectRemoved( |
| new UnsupportedAction("Removing build types is not supported.")); |
| productFlavorContainer.whenObjectRemoved( |
| new UnsupportedAction("Removing product flavors is not supported.")); |
| |
| // create default Objects, signingConfig first as its used by the BuildTypes. |
| variantFactory.createDefaultComponents( |
| buildTypeContainer, productFlavorContainer, signingConfigContainer); |
| } |
| |
| private static class UnsupportedAction implements Action<Object> { |
| |
| private final String message; |
| |
| UnsupportedAction(String message) { |
| this.message = message; |
| } |
| |
| @Override |
| public void execute(Object o) { |
| throw new UnsupportedOperationException(message); |
| } |
| } |
| |
| private void createTasks() { |
| ThreadRecorder.get().record(ExecutionType.TASK_MANAGER_CREATE_TASKS, |
| new Recorder.Block<Void>() { |
| @Override |
| public Void call() throws Exception { |
| taskManager.createTasksBeforeEvaluate( |
| new TaskContainerAdaptor(project.getTasks())); |
| return null; |
| } |
| }, |
| new Recorder.Property("project", project.getName())); |
| |
| project.afterEvaluate(new Action<Project>() { |
| @Override |
| public void execute(Project project) { |
| ThreadRecorder.get().record(ExecutionType.BASE_PLUGIN_CREATE_ANDROID_TASKS, |
| new Recorder.Block<Void>() { |
| @Override |
| public Void call() throws Exception { |
| createAndroidTasks(false); |
| return null; |
| } |
| }, |
| new Recorder.Property("project", project.getName())); |
| } |
| }); |
| } |
| |
| private void checkGradleVersion() { |
| if (!GRADLE_ACCEPTABLE_VERSIONS.matcher(project.getGradle().getGradleVersion()).matches()) { |
| boolean allowNonMatching = Boolean.getBoolean(GRADLE_VERSION_CHECK_OVERRIDE_PROPERTY); |
| File file = new File("gradle" + separator + "wrapper" + separator + |
| "gradle-wrapper.properties"); |
| String errorMessage = String.format( |
| "Gradle version %s is required. Current version is %s. " + |
| "If using the gradle wrapper, try editing the distributionUrl in %s " + |
| "to gradle-%s-all.zip", |
| GRADLE_MIN_VERSION, project.getGradle().getGradleVersion(), file.getAbsolutePath(), |
| GRADLE_MIN_VERSION); |
| if (allowNonMatching) { |
| getLogger().warning(errorMessage); |
| getLogger().warning("As %s is set, continuing anyways.", |
| GRADLE_VERSION_CHECK_OVERRIDE_PROPERTY); |
| } else { |
| throw new BuildException(errorMessage, null); |
| } |
| } |
| } |
| |
| @VisibleForTesting |
| final void createAndroidTasks(boolean force) { |
| // Make sure unit tests set the required fields. |
| checkState(extension.getBuildToolsRevision() != null, "buildToolsVersion is not specified."); |
| checkState(extension.getCompileSdkVersion() != null, "compileSdkVersion is not specified."); |
| |
| ndkHandler.setCompileSdkVersion(extension.getCompileSdkVersion()); |
| |
| // get current plugins and look for the default Java plugin. |
| if (project.getPlugins().hasPlugin(JavaPlugin.class)) { |
| throw new BadPluginException( |
| "The 'java' plugin has been applied, but it is not compatible with the Android plugins."); |
| } |
| |
| ensureTargetSetup(); |
| |
| // don't do anything if the project was not initialized. |
| // Unless TEST_SDK_DIR is set in which case this is unit tests and we don't return. |
| // This is because project don't get evaluated in the unit test setup. |
| // See AppPluginDslTest |
| if (!force |
| && (!project.getState().getExecuted() || project.getState().getFailure()!= null) |
| && SdkHandler.sTestSdkFolder == null) { |
| return; |
| } |
| |
| if (hasCreatedTasks) { |
| return; |
| } |
| hasCreatedTasks = true; |
| |
| extension.disableWrite(); |
| |
| ThreadRecorder.get().record( |
| ExecutionType.GENERAL_CONFIG, |
| Recorder.EmptyBlock, |
| new Recorder.Property("build_tools_version", |
| extension.getBuildToolsRevision().toString())); |
| |
| // setup SDK repositories. |
| for (final File file : sdkHandler.getSdkLoader().getRepositories()) { |
| project.getRepositories().maven(new Action<MavenArtifactRepository>() { |
| @Override |
| public void execute(MavenArtifactRepository mavenArtifactRepository) { |
| mavenArtifactRepository.setUrl(file.toURI()); |
| } |
| }); |
| } |
| |
| taskManager.createMockableJarTask(); |
| ThreadRecorder.get().record(ExecutionType.VARIANT_MANAGER_CREATE_ANDROID_TASKS, |
| new Recorder.Block<Void>() { |
| @Override |
| public Void call() throws Exception { |
| variantManager.createAndroidTasks(); |
| ApiObjectFactory apiObjectFactory = new ApiObjectFactory( |
| androidBuilder, extension, variantFactory, instantiator); |
| for (BaseVariantData variantData : variantManager.getVariantDataList()) { |
| apiObjectFactory.create(variantData); |
| } |
| return null; |
| } |
| }, new Recorder.Property("project", project.getName())); |
| } |
| |
| private boolean isVerbose() { |
| return project.getLogger().isEnabled(LogLevel.INFO); |
| } |
| |
| private void ensureTargetSetup() { |
| // check if the target has been set. |
| TargetInfo targetInfo = androidBuilder.getTargetInfo(); |
| if (targetInfo == null) { |
| if (extension.getCompileOptions() == null) { |
| throw new GradleException("Calling getBootClasspath before compileSdkVersion"); |
| } |
| |
| sdkHandler.initTarget( |
| extension.getCompileSdkVersion(), |
| extension.getBuildToolsRevision(), |
| extension.getLibraryRequests(), |
| androidBuilder); |
| } |
| } |
| |
| /** |
| * Check the sub-projects structure : |
| * So far, checks that 2 modules do not have the same identification (group+name). |
| */ |
| private void checkModulesForErrors() { |
| Project rootProject = project.getRootProject(); |
| Map<String, Project> subProjectsById = new HashMap<String, Project>(); |
| for (Project subProject : rootProject.getAllprojects()) { |
| String id = subProject.getGroup().toString() + ":" + subProject.getName(); |
| if (subProjectsById.containsKey(id)) { |
| String message = String.format( |
| "Your project contains 2 or more modules with the same " + |
| "identification %1$s\n" + |
| "at \"%2$s\" and \"%3$s\".\n" + |
| "You must use different identification (either name or group) for " + |
| "each modules.", |
| id, |
| subProjectsById.get(id).getPath(), |
| subProject.getPath() ); |
| throw new StopExecutionException(message); |
| } else { |
| subProjectsById.put(id, subProject); |
| } |
| } |
| } |
| |
| private void checkPathForErrors() { |
| // See if the user disabled the check: |
| if (Boolean.getBoolean(SKIP_PATH_CHECK_PROPERTY)) { |
| return; |
| } |
| |
| if (project.hasProperty(SKIP_PATH_CHECK_PROPERTY) |
| && project.property(SKIP_PATH_CHECK_PROPERTY) instanceof String |
| && Boolean.valueOf((String) project.property(SKIP_PATH_CHECK_PROPERTY))) { |
| return; |
| } |
| |
| // See if we're on Windows: |
| if (!System.getProperty("os.name").toLowerCase().contains("windows")) { |
| return; |
| } |
| |
| // See if the path contains non-ASCII characters. |
| if (CharMatcher.ASCII.matchesAllOf(project.getRootDir().getAbsolutePath())) { |
| return; |
| } |
| |
| String message = "Your project path contains non-ASCII characters. This will most likely " + |
| "cause the build to fail on Windows. Please move your project to a different " + |
| "directory. See http://b.android.com/95744 for details. " + |
| "This warning can be disabled by using the command line flag -D" + |
| SKIP_PATH_CHECK_PROPERTY + "=true, or adding the line " + |
| SKIP_PATH_CHECK_PROPERTY + "=true' to gradle.properties file " + |
| "in the project directory."; |
| |
| throw new StopExecutionException(message); |
| } |
| } |