Yigit Boyar | 3986e04 | 2016-02-08 18:31:38 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2016 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 | import android.support.build.ApiModule |
| 17 | import java.nio.file.Path; |
| 18 | import java.nio.file.Paths; |
| 19 | |
| 20 | /** |
| 21 | * To add platform specific code to a library: |
| 22 | * 1) add the related source set into the studioCompat map (defined below) |
| 23 | * 2) In your build gradle file, call: |
| 24 | * * createApiSourceSets(project, gradle.ext.studioCompat.modules.<PROJECT>.apiTargets) |
| 25 | * * setApiModuleDependencies(project, dependencies, gradle.ext.studioCompat.modules.<PROJECT>.dependencies) |
| 26 | */ |
| 27 | |
| 28 | def studioCompat = [ |
Yigit Boyar | 6fe8ece | 2016-05-26 17:01:47 +0000 | [diff] [blame] | 29 | enableApiModules : hasProperty('android.injected.invoked.from.ide'), |
Yigit Boyar | 3986e04 | 2016-02-08 18:31:38 -0800 | [diff] [blame] | 30 | modules : [ |
Chris Banes | 2c63c7b | 2016-05-04 13:54:06 +0100 | [diff] [blame] | 31 | compat : [ |
Yigit Boyar | 3986e04 | 2016-02-08 18:31:38 -0800 | [diff] [blame] | 32 | apiTargets : [ |
Kirill Grouchnikov | 81fc7d7 | 2016-05-11 10:01:23 -0700 | [diff] [blame] | 33 | new ApiModule("gingerbread",9), |
Yigit Boyar | 3986e04 | 2016-02-08 18:31:38 -0800 | [diff] [blame] | 34 | new ApiModule("honeycomb",11), |
| 35 | new ApiModule("honeycomb_mr1",12), |
| 36 | new ApiModule("honeycomb_mr2",13), |
| 37 | new ApiModule("ics",14), |
| 38 | new ApiModule("ics-mr1",15), |
| 39 | new ApiModule("jellybean", 16), |
| 40 | new ApiModule("jellybean-mr1",17), |
| 41 | new ApiModule("jellybean-mr2",18), |
| 42 | new ApiModule("kitkat",19), |
Kirill Grouchnikov | 32d1917 | 2016-05-10 16:08:11 -0700 | [diff] [blame] | 43 | new ApiModule("api20",20), |
Yigit Boyar | 3986e04 | 2016-02-08 18:31:38 -0800 | [diff] [blame] | 44 | new ApiModule("api21",21), |
| 45 | new ApiModule("api22",22), |
| 46 | new ApiModule("api23",23), |
| 47 | new ApiModule("api24", ApiModule.CURRENT) |
| 48 | ], |
| 49 | dependencies : [":support-annotations"], |
Chris Banes | 2c63c7b | 2016-05-04 13:54:06 +0100 | [diff] [blame] | 50 | folder : "compat", |
| 51 | moduleName : "support-compat" |
| 52 | ], |
Kirill Grouchnikov | d3c5347 | 2016-05-10 08:02:18 -0700 | [diff] [blame] | 53 | mediacompat : [ |
| 54 | apiTargets : [ |
Kirill Grouchnikov | d3c5347 | 2016-05-10 08:02:18 -0700 | [diff] [blame] | 55 | new ApiModule("ics",14), |
| 56 | new ApiModule("jellybean-mr2",18), |
| 57 | new ApiModule("kitkat",19), |
| 58 | new ApiModule("api21",21), |
| 59 | new ApiModule("api22",22), |
| 60 | new ApiModule("api23",23), |
| 61 | new ApiModule("api24", ApiModule.CURRENT) |
| 62 | ], |
Kirill Grouchnikov | ff22d81 | 2016-05-11 15:24:25 -0700 | [diff] [blame] | 63 | dependencies : [":support-compat"], |
Kirill Grouchnikov | d3c5347 | 2016-05-10 08:02:18 -0700 | [diff] [blame] | 64 | folder : "media-compat", |
| 65 | moduleName : "support-media-compat" |
| 66 | ], |
Kirill Grouchnikov | ff22d81 | 2016-05-11 15:24:25 -0700 | [diff] [blame] | 67 | coreutils : [ |
| 68 | apiTargets : [ |
| 69 | new ApiModule("gingerbread",9), |
| 70 | new ApiModule("honeycomb",11), |
| 71 | new ApiModule("jellybean", 16), |
| 72 | new ApiModule("kitkat",19), |
| 73 | new ApiModule("api20",20), |
| 74 | new ApiModule("api21",21), |
| 75 | new ApiModule("api23",23), |
| 76 | new ApiModule("api24", ApiModule.CURRENT) |
| 77 | ], |
| 78 | dependencies : [":support-compat"], |
| 79 | folder : "v4", |
| 80 | moduleName : "support-core-utils" |
| 81 | ], |
| 82 | coreui : [ |
| 83 | apiTargets : [ |
| 84 | new ApiModule("honeycomb",11), |
| 85 | new ApiModule("ics",14), |
| 86 | new ApiModule("jellybean-mr2",18), |
| 87 | new ApiModule("api21",21) |
| 88 | ], |
| 89 | dependencies : [":support-compat"], |
| 90 | folder : "core-ui", |
| 91 | moduleName : "support-core-ui" |
| 92 | ], |
| 93 | fragment : [ |
| 94 | apiTargets : [ |
| 95 | new ApiModule("gingerbread",9), |
| 96 | new ApiModule("honeycomb",11), |
| 97 | new ApiModule("jellybean", 16), |
| 98 | new ApiModule("api21",21) |
| 99 | ], |
Kirill Grouchnikov | c016050 | 2016-05-16 18:27:36 -0700 | [diff] [blame] | 100 | dependencies : [":support-media-compat", ":support-core-ui", ":support-core-utils"], |
Kirill Grouchnikov | ff22d81 | 2016-05-11 15:24:25 -0700 | [diff] [blame] | 101 | folder : "fragment", |
| 102 | moduleName : "support-fragment" |
Yigit Boyar | 3986e04 | 2016-02-08 18:31:38 -0800 | [diff] [blame] | 103 | ], |
| 104 | v13 : [ |
| 105 | apiTargets : [ |
| 106 | new ApiModule("ics", 14), |
| 107 | new ApiModule("ics-mr1", 15), |
| 108 | new ApiModule("api23", 23), |
| 109 | new ApiModule("api24", ApiModule.CURRENT) |
| 110 | ], |
| 111 | dependencies : [":support-v4"], |
| 112 | folder : "v13", |
| 113 | moduleName : "support-v13" |
Yigit Boyar | 01b9504 | 2016-04-28 14:34:48 -0700 | [diff] [blame] | 114 | ], |
| 115 | mediaRouter : [ |
| 116 | apiTargets : [ |
Yigit Boyar | 6fe8ece | 2016-05-26 17:01:47 +0000 | [diff] [blame] | 117 | new ApiModule("jellybean", 16), |
| 118 | new ApiModule("jellybean-mr1", 17), |
| 119 | new ApiModule("jellybean-mr2", 18), |
Yigit Boyar | 01b9504 | 2016-04-28 14:34:48 -0700 | [diff] [blame] | 120 | new ApiModule("api24", ApiModule.CURRENT) |
| 121 | ], |
Yigit Boyar | 01b9504 | 2016-04-28 14:34:48 -0700 | [diff] [blame] | 122 | folder : "v7/mediarouter", |
| 123 | moduleName : "support-mediarouter-v7" |
Yigit Boyar | 3986e04 | 2016-02-08 18:31:38 -0800 | [diff] [blame] | 124 | ] |
| 125 | ] |
| 126 | ] |
| 127 | |
| 128 | /** |
| 129 | * Adds a link to the previous ApiModule for each module. This information is later used when |
| 130 | * setting dependencies. |
| 131 | */ |
| 132 | def setupDependencies(projectConfig) { |
| 133 | projectConfig.apiTargets.eachWithIndex { entry, index -> |
| 134 | entry.parentModuleName = projectConfig.moduleName |
| 135 | entry.prev = index == 0 ? null : projectConfig.apiTargets[index - 1] |
| 136 | } |
| 137 | } |
Yigit Boyar | 6fe8ece | 2016-05-26 17:01:47 +0000 | [diff] [blame] | 138 | gradle.ext.currentSdk = studioCompat.enableApiModules ? ApiModule.CURRENT : 'current' |
Yigit Boyar | 3986e04 | 2016-02-08 18:31:38 -0800 | [diff] [blame] | 139 | |
| 140 | // the hashmap from apiModuleProjectFolder -> ApiModule |
| 141 | gradle.ext.folderToApiModuleMapping = new HashMap() |
| 142 | |
| 143 | /** |
| 144 | * For each APIModule in the given projectConfig, creates a gradle module. These modules use the |
| 145 | * common `apiModule.gradle` build file. |
| 146 | */ |
| 147 | def createModules(projectConfig) { |
| 148 | Path buildFile = Paths.get(file("apiModule.gradle").toURI()) |
| 149 | projectConfig.apiTargets.each { ApiModule module -> |
| 150 | logger.info "creating ${module.moduleName}" |
| 151 | module.setParentModuleDependencies(projectConfig.dependencies) |
| 152 | include "${module.moduleName}" |
| 153 | def folder = new File(rootDir, "${projectConfig.folder}/${module.folderName}") |
| 154 | project("${module.moduleName}").projectDir = folder |
| 155 | project("${module.moduleName}").buildFileName = Paths.get(folder.toURI()).relativize(buildFile) |
| 156 | gradle.ext.folderToApiModuleMapping[folder.canonicalPath] = module |
| 157 | } |
| 158 | } |
| 159 | |
| 160 | /** |
| 161 | * returns the APIModule for the given project. |
| 162 | */ |
| 163 | ApiModule getApiModule(Project project) { |
| 164 | return gradle.ext.folderToApiModuleMapping[project.projectDir.canonicalPath] |
| 165 | } |
| 166 | |
| 167 | studioCompat.modules.each { k, v -> |
| 168 | setupDependencies(v) |
| 169 | } |
| 170 | // create these fake modules only if Studio opens the project. |
Yigit Boyar | 6fe8ece | 2016-05-26 17:01:47 +0000 | [diff] [blame] | 171 | if (studioCompat.enableApiModules) { |
Yigit Boyar | 3986e04 | 2016-02-08 18:31:38 -0800 | [diff] [blame] | 172 | // validate we have the 99 folder, otherwise it wont work |
| 173 | def currentSdkPrebuilt = file("${rootProject.projectDir}/../../prebuilts/sdk/" + |
| 174 | "${ApiModule.CURRENT}/") |
| 175 | if (!currentSdkPrebuilt.exists()) { |
| 176 | throw new GradleScriptException( |
| 177 | "You need a symlink in prebuilts/sdk/${ApiModule.CURRENT} that points to" |
| 178 | + " prebuilts/sdk/current." |
| 179 | + "Without it, studio cannot understand current SDK.\n" |
| 180 | + "> ln -s ../../prebuilts/sdk/current " |
| 181 | + "../../prebuilts/sdk/${ApiModule.CURRENT}\n" |
| 182 | , new Exception()) |
| 183 | } |
| 184 | Properties localProps = new Properties() |
| 185 | def localPropsStream = new FileInputStream(file("${rootProject.projectDir}/local.properties")) |
| 186 | try { |
| 187 | localProps.load(localPropsStream) |
| 188 | def sdkDir = localProps.get("sdk.dir") |
| 189 | if (sdkDir != null && sdkDir != "") { |
| 190 | throw new GradleScriptException("You should not have sdk.dir in local.properties because" |
| 191 | + " it overrides android.dir and prevents studio from seeing current sdk. " |
| 192 | + " Studio may add it by mistake, just remove it and it will work fine.", |
| 193 | new Exception()) |
| 194 | } |
| 195 | } finally { |
| 196 | localPropsStream.close() |
| 197 | } |
| 198 | studioCompat.modules.each { k, v -> |
| 199 | createModules(v) |
| 200 | } |
| 201 | } |
| 202 | gradle.ext.studioCompat = studioCompat |
Chris Banes | 2c63c7b | 2016-05-04 13:54:06 +0100 | [diff] [blame] | 203 | gradle.ext.getApiModule = this.&getApiModule |