Home | History | Annotate | Download | only in build
      1 /*
      2  * Copyright (C) 2010 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.build.BuildInfoKey.BuildInfoFileKey;
     19 import com.android.tradefed.device.ITestDevice;
     20 import com.android.tradefed.log.LogUtil.CLog;
     21 import com.android.tradefed.util.FileUtil;
     22 import com.android.tradefed.util.MultiMap;
     23 import com.android.tradefed.util.UniqueMultiMap;
     24 
     25 import com.google.common.base.MoreObjects;
     26 import com.google.common.base.Objects;
     27 
     28 import java.io.File;
     29 import java.io.IOException;
     30 import java.lang.reflect.InvocationTargetException;
     31 import java.util.Arrays;
     32 import java.util.Collection;
     33 import java.util.HashSet;
     34 import java.util.Hashtable;
     35 import java.util.List;
     36 import java.util.Map;
     37 import java.util.Set;
     38 
     39 /**
     40  * Generic implementation of a {@link IBuildInfo} that should be associated
     41  * with a {@link ITestDevice}.
     42  */
     43 public class BuildInfo implements IBuildInfo {
     44     private static final long serialVersionUID = BuildSerializedVersion.VERSION;
     45     private static final String BUILD_ALIAS_KEY = "build_alias";
     46 
     47     private String mBuildId = UNKNOWN_BUILD_ID;
     48     private String mTestTag = "stub";
     49     private String mBuildTargetName = "stub";
     50     private final UniqueMultiMap<String, String> mBuildAttributes =
     51             new UniqueMultiMap<String, String>();
     52     private Map<String, VersionedFile> mVersionedFileMap;
     53     private String mBuildFlavor = null;
     54     private String mBuildBranch = null;
     55     private String mDeviceSerial = null;
     56 
     57     /** File handling properties: Some files of the BuildInfo might requires special handling */
     58     private final Set<BuildInfoProperties> mProperties = new HashSet<>();
     59 
     60     /**
     61      * Creates a {@link BuildInfo} using default attribute values.
     62      */
     63     public BuildInfo() {
     64         mVersionedFileMap = new Hashtable<String, VersionedFile>();
     65     }
     66 
     67     /**
     68      * Creates a {@link BuildInfo}
     69      *
     70      * @param buildId the build id
     71      * @param buildTargetName the build target name
     72      */
     73     public BuildInfo(String buildId, String buildTargetName) {
     74         mBuildId = buildId;
     75         mBuildTargetName = buildTargetName;
     76         mVersionedFileMap = new Hashtable<String, VersionedFile>();
     77     }
     78 
     79     /**
     80      * Creates a {@link BuildInfo}
     81      *
     82      * @param buildId the build id
     83      * @param testTag the test tag name
     84      * @param buildTargetName the build target name
     85      * @deprecated use {@link #BuildInfo(String, String)} instead. test-tag should not be mandatory
     86      * when instantiating the build info.
     87      */
     88     @Deprecated
     89     public BuildInfo(String buildId, String testTag, String buildTargetName) {
     90         mBuildId = buildId;
     91         mTestTag = testTag;
     92         mBuildTargetName = buildTargetName;
     93         mVersionedFileMap = new Hashtable<String, VersionedFile>();
     94     }
     95 
     96     /**
     97      * Creates a {@link BuildInfo}, populated with attributes given in another build.
     98      *
     99      * @param buildToCopy
    100      */
    101     BuildInfo(BuildInfo buildToCopy) {
    102         this(buildToCopy.getBuildId(), buildToCopy.getBuildTargetName());
    103         addAllBuildAttributes(buildToCopy);
    104         try {
    105             addAllFiles(buildToCopy);
    106         } catch (IOException e) {
    107             throw new RuntimeException(e);
    108         }
    109     }
    110 
    111     /**
    112      * {@inheritDoc}
    113      */
    114     @Override
    115     public String getBuildId() {
    116         return mBuildId;
    117     }
    118 
    119     /**
    120      * {@inheritDoc}
    121      */
    122     @Override
    123     public void setBuildId(String buildId) {
    124         mBuildId = buildId;
    125     }
    126 
    127     /**
    128      * {@inheritDoc}
    129      */
    130     @Override
    131     public void setTestTag(String testTag) {
    132         mTestTag = testTag;
    133     }
    134 
    135     /**
    136      * {@inheritDoc}
    137      */
    138     @Override
    139     public String getTestTag() {
    140         return mTestTag;
    141     }
    142 
    143     /**
    144      * {@inheritDoc}
    145      */
    146     @Override
    147     public String getDeviceSerial() {
    148         return mDeviceSerial;
    149     }
    150 
    151     /**
    152      * {@inheritDoc}
    153      */
    154     @Override
    155     public Map<String, String> getBuildAttributes() {
    156         return mBuildAttributes.getUniqueMap();
    157     }
    158 
    159     /** {@inheritDoc} */
    160     @Override
    161     public void setProperties(BuildInfoProperties... properties) {
    162         mProperties.clear();
    163         mProperties.addAll(Arrays.asList(properties));
    164     }
    165 
    166     /** {@inheritDoc} */
    167     @Override
    168     public Set<BuildInfoProperties> getProperties() {
    169         return new HashSet<>(mProperties);
    170     }
    171 
    172     /**
    173      * {@inheritDoc}
    174      */
    175     @Override
    176     public String getBuildTargetName() {
    177         return mBuildTargetName;
    178     }
    179 
    180     /**
    181      * {@inheritDoc}
    182      */
    183     @Override
    184     public void addBuildAttribute(String attributeName, String attributeValue) {
    185         mBuildAttributes.put(attributeName, attributeValue);
    186     }
    187 
    188     /**
    189      * Helper method to copy build attributes, branch, and flavor from other build.
    190      */
    191     protected void addAllBuildAttributes(BuildInfo build) {
    192         mBuildAttributes.putAll(build.getAttributesMultiMap());
    193         setBuildFlavor(build.getBuildFlavor());
    194         setBuildBranch(build.getBuildBranch());
    195         setTestTag(build.getTestTag());
    196     }
    197 
    198     protected MultiMap<String, String> getAttributesMultiMap() {
    199         return mBuildAttributes;
    200     }
    201 
    202     /**
    203      * Helper method to copy all files from the other build.
    204      *
    205      * <p>Creates new hardlinks to the files so that each build will have a unique file path to the
    206      * file.
    207      *
    208      * @throws IOException if an exception is thrown when creating the hardlink.
    209      */
    210     protected void addAllFiles(BuildInfo build) throws IOException {
    211         for (Map.Entry<String, VersionedFile> fileEntry : build.getVersionedFileMap().entrySet()) {
    212             File origFile = fileEntry.getValue().getFile();
    213             if (applyBuildProperties(fileEntry.getValue(), build, this)) {
    214                 continue;
    215             }
    216             File copyFile;
    217             if (origFile.isDirectory()) {
    218                 copyFile = FileUtil.createTempDir(fileEntry.getKey());
    219                 FileUtil.recursiveHardlink(origFile, copyFile);
    220             } else {
    221                 // Only using createTempFile to create a unique dest filename
    222                 copyFile = FileUtil.createTempFile(fileEntry.getKey(),
    223                         FileUtil.getExtension(origFile.getName()));
    224                 copyFile.delete();
    225                 FileUtil.hardlinkFile(origFile, copyFile);
    226             }
    227             setFile(fileEntry.getKey(), copyFile, fileEntry.getValue().getVersion());
    228         }
    229     }
    230 
    231     /**
    232      * Allow to apply some of the {@link com.android.tradefed.build.IBuildInfo.BuildInfoProperties}
    233      * and possibly do a different handling.
    234      *
    235      * @param origFileConsidered The currently looked at {@link VersionedFile}.
    236      * @param build the original build being cloned
    237      * @param receiver the build receiving the information.
    238      * @return True if we applied the properties and further handling should be skipped. False
    239      *     otherwise.
    240      */
    241     protected boolean applyBuildProperties(
    242             VersionedFile origFileConsidered, IBuildInfo build, IBuildInfo receiver) {
    243         return false;
    244     }
    245 
    246     protected Map<String, VersionedFile> getVersionedFileMap() {
    247         return mVersionedFileMap;
    248     }
    249 
    250     /**
    251      * {@inheritDoc}
    252      */
    253     @Override
    254     public File getFile(String name) {
    255         VersionedFile fileRecord = mVersionedFileMap.get(name);
    256         if (fileRecord != null) {
    257             return fileRecord.getFile();
    258         }
    259         return null;
    260     }
    261 
    262     /** {@inheritDoc} */
    263     @Override
    264     public File getFile(BuildInfoFileKey key) {
    265         return getFile(key.getFileKey());
    266     }
    267 
    268     /** {@inheritDoc} */
    269     @Override
    270     public VersionedFile getVersionedFile(String name) {
    271         return mVersionedFileMap.get(name);
    272     }
    273 
    274     /** {@inheritDoc} */
    275     @Override
    276     public VersionedFile getVersionedFile(BuildInfoFileKey key) {
    277         return getVersionedFile(key.getFileKey());
    278     }
    279 
    280     /**
    281      * {@inheritDoc}
    282      */
    283     @Override
    284     public Collection<VersionedFile> getFiles() {
    285         return mVersionedFileMap.values();
    286     }
    287 
    288     /**
    289      * {@inheritDoc}
    290      */
    291     @Override
    292     public String getVersion(String name) {
    293         VersionedFile fileRecord = mVersionedFileMap.get(name);
    294         if (fileRecord != null) {
    295             return fileRecord.getVersion();
    296         }
    297         return null;
    298     }
    299 
    300     /** {@inheritDoc} */
    301     @Override
    302     public String getVersion(BuildInfoFileKey key) {
    303         return getVersion(key.getFileKey());
    304     }
    305 
    306     /**
    307      * {@inheritDoc}
    308      */
    309     @Override
    310     public void setFile(String name, File file, String version) {
    311         if (mVersionedFileMap.containsKey(name)) {
    312             CLog.e("Device build already contains a file for %s in thread %s", name,
    313                     Thread.currentThread().getName());
    314             return;
    315         }
    316         mVersionedFileMap.put(name, new VersionedFile(file, version));
    317     }
    318 
    319     /** {@inheritDoc} */
    320     @Override
    321     public void setFile(BuildInfoFileKey key, File file, String version) {
    322         setFile(key.getFileKey(), file, version);
    323     }
    324 
    325     /**
    326      * {@inheritDoc}
    327      */
    328     @Override
    329     public void cleanUp() {
    330         for (VersionedFile fileRecord : mVersionedFileMap.values()) {
    331             FileUtil.recursiveDelete(fileRecord.getFile());
    332         }
    333         mVersionedFileMap.clear();
    334     }
    335 
    336     /** {@inheritDoc} */
    337     @Override
    338     public void cleanUp(List<File> doNotClean) {
    339         if (doNotClean == null) {
    340             cleanUp();
    341         }
    342         for (VersionedFile fileRecord : mVersionedFileMap.values()) {
    343             if (!doNotClean.contains(fileRecord.getFile())) {
    344                 FileUtil.recursiveDelete(fileRecord.getFile());
    345             }
    346         }
    347         refreshVersionedFiles();
    348     }
    349 
    350     /**
    351      * Run through all the {@link VersionedFile} and remove from the map the one that do not exists.
    352      */
    353     private void refreshVersionedFiles() {
    354         Set<String> keys = new HashSet<>(mVersionedFileMap.keySet());
    355         for (String key : keys) {
    356             if (!mVersionedFileMap.get(key).getFile().exists()) {
    357                 mVersionedFileMap.remove(key);
    358             }
    359         }
    360     }
    361 
    362     /**
    363      * {@inheritDoc}
    364      */
    365     @Override
    366     public IBuildInfo clone() {
    367         BuildInfo copy = null;
    368         try {
    369             copy =
    370                     this.getClass()
    371                             .getDeclaredConstructor(String.class, String.class)
    372                             .newInstance(getBuildId(), getBuildTargetName());
    373         } catch (InstantiationException
    374                 | IllegalAccessException
    375                 | IllegalArgumentException
    376                 | InvocationTargetException
    377                 | NoSuchMethodException
    378                 | SecurityException e) {
    379             CLog.e("Failed to clone the build info.");
    380             throw new RuntimeException(e);
    381         }
    382         copy.addAllBuildAttributes(this);
    383         copy.setProperties(this.getProperties().toArray(new BuildInfoProperties[0]));
    384         try {
    385             copy.addAllFiles(this);
    386         } catch (IOException e) {
    387             throw new RuntimeException(e);
    388         }
    389         copy.setBuildBranch(mBuildBranch);
    390         copy.setBuildFlavor(mBuildFlavor);
    391 
    392         return copy;
    393     }
    394 
    395     /**
    396      * {@inheritDoc}
    397      */
    398     @Override
    399     public String getBuildFlavor() {
    400         return mBuildFlavor;
    401     }
    402 
    403     /**
    404      * {@inheritDoc}
    405      */
    406     @Override
    407     public void setBuildFlavor(String buildFlavor) {
    408         mBuildFlavor = buildFlavor;
    409     }
    410 
    411     /**
    412      * {@inheritDoc}
    413      */
    414     @Override
    415     public String getBuildBranch() {
    416         return mBuildBranch;
    417     }
    418 
    419     /**
    420      * {@inheritDoc}
    421      */
    422     @Override
    423     public void setBuildBranch(String branch) {
    424         mBuildBranch = branch;
    425     }
    426 
    427     /**
    428      * {@inheritDoc}
    429      */
    430     @Override
    431     public void setDeviceSerial(String serial) {
    432         mDeviceSerial = serial;
    433     }
    434 
    435     /**
    436      * {@inheritDoc}
    437      */
    438     @Override
    439     public int hashCode() {
    440         return Objects.hashCode(mBuildAttributes, mBuildBranch, mBuildFlavor, mBuildId,
    441                 mBuildTargetName, mTestTag, mDeviceSerial);
    442     }
    443 
    444     /**
    445      * {@inheritDoc}
    446      */
    447     @Override
    448     public boolean equals(Object obj) {
    449         if (this == obj) {
    450             return true;
    451         }
    452         if (obj == null) {
    453             return false;
    454         }
    455         if (getClass() != obj.getClass()) {
    456             return false;
    457         }
    458         BuildInfo other = (BuildInfo) obj;
    459         return Objects.equal(mBuildAttributes, other.mBuildAttributes) &&
    460                 Objects.equal(mBuildBranch, other.mBuildBranch) &&
    461                 Objects.equal(mBuildFlavor, other.mBuildFlavor) &&
    462                 Objects.equal(mBuildId, other.mBuildId) &&
    463                 Objects.equal(mBuildTargetName, other.mBuildTargetName) &&
    464                 Objects.equal(mTestTag, other.mTestTag) &&
    465                 Objects.equal(mDeviceSerial, other.mDeviceSerial);
    466     }
    467 
    468     /**
    469      * {@inheritDoc}
    470      */
    471     @Override
    472     public String toString() {
    473         return MoreObjects.toStringHelper(this.getClass())
    474                 .omitNullValues()
    475                 .add("build_alias", getBuildAttributes().get(BUILD_ALIAS_KEY))
    476                 .add("bid", mBuildId)
    477                 .add("target", mBuildTargetName)
    478                 .add("build_flavor", mBuildFlavor)
    479                 .add("branch", mBuildBranch)
    480                 .add("serial", mDeviceSerial)
    481                 .toString();
    482     }
    483 }
    484