1 /* 2 * Copyright (C) 2011 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 package com.android.tradefed.build; 17 18 import com.android.tradefed.config.Option; 19 import com.android.tradefed.config.OptionClass; 20 import com.android.tradefed.log.LogUtil.CLog; 21 import com.android.tradefed.targetprep.FlashingResourcesParser; 22 import com.android.tradefed.targetprep.IFlashingResourcesParser; 23 import com.android.tradefed.targetprep.TargetSetupError; 24 import com.android.tradefed.util.SystemUtil; 25 import com.android.tradefed.util.ZipUtil; 26 27 import com.google.common.annotations.VisibleForTesting; 28 import com.google.common.io.PatternFilenameFilter; 29 30 import java.io.File; 31 import java.io.IOException; 32 import java.util.ArrayList; 33 import java.util.Arrays; 34 import java.util.List; 35 36 /** 37 * A {@link IBuildProvider} that constructs a {@link IDeviceBuildInfo} based on a provided 38 * filesystem directory path. 39 * <p/> 40 * Specific device build files are recognized based on a configurable set of patterns. 41 */ 42 @OptionClass(alias = "local-device-build") 43 public class LocalDeviceBuildProvider extends StubBuildProvider { 44 45 private static final String BUILD_INFO_FILE = "android-info.txt"; 46 private static final String BUILD_DIR_OPTION_NAME = "build-dir"; 47 48 private String mBootloaderVersion = null; 49 private String mRadioVersion = null; 50 51 @Option(name = BUILD_DIR_OPTION_NAME, description = "the directory containing device files.") 52 private File mBuildDir = SystemUtil.getProductOutputDir(); 53 54 @Option(name = "device-img-pattern", description = 55 "the regex use to find device system image zip file within --build-dir.") 56 private String mImgPattern = ".*-img-.*\\.zip"; 57 58 @Option(name = "test-dir", description = "the directory containing test artifacts.") 59 private File mTestDir = null; 60 61 @Option(name = "test-dir-pattern", description = 62 "the regex use to find optional test artifact directory within --build-dir. " + 63 "Will be ignored if a test-dir is set.") 64 private String mTestDirPattern = ".*-tests-.*"; 65 66 @Option(name = "bootloader-pattern", description = 67 "the regex use to find device bootloader image file within --build-dir.") 68 private String mBootloaderPattern = "bootloader.*\\.img"; 69 70 @Option(name = "radio-pattern", description = 71 "the regex use to find device radio image file within --build-dir.") 72 private String mRadioPattern = "radio.*\\.img"; 73 74 /** 75 * {@inheritDoc} 76 */ 77 @Override 78 public IBuildInfo getBuild() throws BuildRetrievalError { 79 if (mBuildDir == null) { 80 throw new BuildRetrievalError( 81 "Product output directory is not specified. If running " 82 + "from a Android source tree, make sure `lunch` has been run; " 83 + "if outside, provide a valid path via --" 84 + BUILD_DIR_OPTION_NAME); 85 } 86 if (!mBuildDir.exists()) { 87 throw new BuildRetrievalError(String.format("Directory '%s' does not exist. " + 88 "Please provide a valid path via --%s", mBuildDir.getAbsolutePath(), 89 BUILD_DIR_OPTION_NAME)); 90 } 91 if (!mBuildDir.isDirectory()) { 92 throw new BuildRetrievalError(String.format("Path '%s' is not a directory. " + 93 "Please provide a valid path via --%s", mBuildDir.getAbsolutePath(), 94 BUILD_DIR_OPTION_NAME)); 95 } 96 CLog.d("Using device build files from %s", mBuildDir.getAbsolutePath()); 97 98 BuildInfo stubBuild = (BuildInfo)super.getBuild(); 99 DeviceBuildInfo buildInfo = new DeviceBuildInfo(stubBuild.getBuildId(), 100 stubBuild.getBuildTargetName()); 101 buildInfo.addAllBuildAttributes(stubBuild); 102 103 setDeviceImageFile(buildInfo); 104 parseBootloaderAndRadioVersions(buildInfo); 105 setTestsDir(buildInfo); 106 setBootloaderImage(buildInfo); 107 setRadioImage(buildInfo); 108 109 return buildInfo; 110 } 111 112 /** 113 * Parse bootloader and radio versions from the android build info file. 114 * 115 * @param buildInfo a {@link DeviceBuildInfo} 116 * @throws BuildRetrievalError 117 */ 118 void parseBootloaderAndRadioVersions(DeviceBuildInfo buildInfo) throws BuildRetrievalError { 119 try { 120 IFlashingResourcesParser flashingResourcesParser; 121 flashingResourcesParser = new FlashingResourcesParser( 122 buildInfo.getDeviceImageFile()); 123 mBootloaderVersion = flashingResourcesParser.getRequiredBootloaderVersion(); 124 mRadioVersion = flashingResourcesParser.getRequiredBasebandVersion(); 125 } catch (TargetSetupError e) { 126 throw new BuildRetrievalError("Unable parse bootloader and radio versions", e); 127 } 128 } 129 130 /** 131 * Find and and set matching device image file to the build. 132 * 133 * @param buildInfo a {@link DeviceBuildInfo} to set the device image file 134 * @throws BuildRetrievalError 135 */ 136 @VisibleForTesting 137 void setDeviceImageFile(DeviceBuildInfo buildInfo) throws BuildRetrievalError { 138 File deviceImgFile = findFileInDir(mImgPattern); 139 if (deviceImgFile == null) { 140 CLog.i("Unable to find build image zip on %s", mBuildDir.getAbsolutePath()); 141 deviceImgFile = createBuildImageZip(); 142 if (deviceImgFile == null) { 143 throw new BuildRetrievalError(String.format( 144 "Could not find device image file matching matching '%s' in '%s'.", 145 mImgPattern, mBuildDir.getAbsolutePath())); 146 } 147 } 148 CLog.i("Set build image zip to %s", deviceImgFile.getAbsolutePath()); 149 buildInfo.setDeviceImageFile(deviceImgFile, buildInfo.getBuildId()); 150 } 151 152 /** 153 * Creates a build image zip file from the given build-dir 154 * 155 * @return the {@link File} referencing the zip output. 156 * @throws BuildRetrievalError 157 */ 158 @VisibleForTesting 159 File createBuildImageZip() throws BuildRetrievalError { 160 File zipFile = null; 161 File[] imageFiles = mBuildDir.listFiles(new PatternFilenameFilter(".*\\.img")); 162 File buildInfo = findFileInDir(BUILD_INFO_FILE); 163 List<File> buildFiles = new ArrayList<>(Arrays.asList(imageFiles)); 164 buildFiles.add(buildInfo); 165 try { 166 zipFile = ZipUtil.createZip(buildFiles); 167 } catch (IOException e) { 168 throw new BuildRetrievalError("Unable to create build image zip file", e); 169 } 170 CLog.i("Created build image zip on: %s", zipFile.getAbsolutePath()); 171 return zipFile; 172 } 173 174 void setRadioImage(DeviceBuildInfo buildInfo) throws BuildRetrievalError { 175 File radioImgFile = findFileInDir(mRadioPattern); 176 if (radioImgFile != null) { 177 buildInfo.setBasebandImage(radioImgFile, mRadioVersion); 178 } 179 } 180 181 void setBootloaderImage(DeviceBuildInfo buildInfo) throws BuildRetrievalError { 182 File bootloaderImgFile = findFileInDir(mBootloaderPattern); 183 if (bootloaderImgFile != null) { 184 buildInfo.setBootloaderImageFile(bootloaderImgFile, mBootloaderVersion); 185 } 186 } 187 188 /** 189 * Find and set a test directory to the build. 190 * 191 * @param buildInfo a {@link DeviceBuildInfo} to set the test directory 192 * @throws BuildRetrievalError 193 */ 194 @VisibleForTesting 195 void setTestsDir(DeviceBuildInfo buildInfo) throws BuildRetrievalError { 196 File testsDir = null; 197 // If test-dir is specified, use it 198 if (mTestDir != null) { 199 CLog.i("Looking for tests on %s", mTestDir.getAbsolutePath()); 200 testsDir = mTestDir; 201 } else { 202 CLog.i("Looking for tests on %s matching %s", mBuildDir.getAbsolutePath(), 203 mTestDirPattern); 204 testsDir = findFileInDir(mTestDirPattern); 205 } 206 if (testsDir != null) { 207 buildInfo.setTestsDir(testsDir, buildInfo.getBuildId()); 208 CLog.d("Using test files from %s", testsDir.getAbsolutePath()); 209 } 210 } 211 212 /** 213 * Find a matching file in the build directory. 214 * 215 * @param regex Regular expression to match a file 216 * @return A matching {@link File} or null if none is found 217 * @throws BuildRetrievalError 218 */ 219 @VisibleForTesting 220 File findFileInDir(String regex) throws BuildRetrievalError { 221 return findFileInDir(regex, mBuildDir); 222 } 223 224 /** 225 * Find a matching file in a given directory. 226 * 227 * @param regex Regular expression to match a file 228 * @param dir a {@link File} referencing the directory to search 229 * @return A matching {@link File} or null if none is found 230 * @throws BuildRetrievalError 231 */ 232 @VisibleForTesting 233 File findFileInDir(String regex, File dir) throws BuildRetrievalError { 234 File[] files = dir.listFiles(new PatternFilenameFilter(regex)); 235 if (files.length == 0) { 236 return null; 237 } else if (files.length > 1) { 238 throw new BuildRetrievalError(String.format( 239 "Found more than one file matching '%s' in '%s'.", regex, 240 mBuildDir.getAbsolutePath())); 241 } 242 return files[0]; 243 } 244 245 /** 246 * {@inheritDoc} 247 */ 248 @Override 249 public void buildNotTested(IBuildInfo info) { 250 // ignore 251 } 252 253 /** 254 * {@inheritDoc} 255 */ 256 @Override 257 public void cleanUp(IBuildInfo info) { 258 // ignore 259 } 260 261 String getBootloaderVersion() { 262 return mBootloaderVersion; 263 } 264 265 void setBootloaderVersion(String bootloaderVersion) { 266 mBootloaderVersion = bootloaderVersion; 267 } 268 269 String getRadioVersion() { 270 return mRadioVersion; 271 } 272 273 void setRadioVersion(String radioVersion) { 274 mRadioVersion = radioVersion; 275 } 276 277 File getBuildDir() { 278 return mBuildDir; 279 } 280 281 void setBuildDir(File buildDir) { 282 this.mBuildDir = buildDir; 283 } 284 285 File getTestDir() { 286 return mTestDir; 287 } 288 289 void setTestDir(File testDir) { 290 this.mTestDir = testDir; 291 } 292 293 String getTestDirPattern() { 294 return mTestDirPattern; 295 } 296 297 void setTestDirPattern(String testDirPattern) { 298 this.mTestDirPattern = testDirPattern; 299 } 300 } 301