Home | History | Annotate | Download | only in repository
      1 /*
      2  * Copyright (C) 2009 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 
     17 package com.android.sdklib.internal.repository;
     18 
     19 import com.android.sdklib.SdkConstants;
     20 import com.android.sdklib.SdkManager;
     21 import com.android.sdklib.internal.repository.Archive.Arch;
     22 import com.android.sdklib.internal.repository.Archive.Os;
     23 
     24 import org.w3c.dom.Node;
     25 
     26 import java.io.BufferedReader;
     27 import java.io.File;
     28 import java.io.IOException;
     29 import java.io.InputStreamReader;
     30 import java.util.Map;
     31 import java.util.Properties;
     32 
     33 /**
     34  * Represents a tool XML node in an SDK repository.
     35  */
     36 public class ToolPackage extends Package {
     37 
     38     /**
     39      * Creates a new tool package from the attributes and elements of the given XML node.
     40      * <p/>
     41      * This constructor should throw an exception if the package cannot be created.
     42      */
     43     ToolPackage(RepoSource source, Node packageNode, Map<String,String> licenses) {
     44         super(source, packageNode, licenses);
     45     }
     46 
     47     /**
     48      * Manually create a new package with one archive and the given attributes or properties.
     49      * This is used to create packages from local directories in which case there must be
     50      * one archive which URL is the actual target location.
     51      * <p/>
     52      * By design, this creates a package with one and only one archive.
     53      */
     54     ToolPackage(
     55             RepoSource source,
     56             Properties props,
     57             int revision,
     58             String license,
     59             String description,
     60             String descUrl,
     61             Os archiveOs,
     62             Arch archiveArch,
     63             String archiveOsPath) {
     64         super(source,
     65                 props,
     66                 revision,
     67                 license,
     68                 description,
     69                 descUrl,
     70                 archiveOs,
     71                 archiveArch,
     72                 archiveOsPath);
     73     }
     74 
     75     /** Returns a short description for an {@link IDescription}. */
     76     @Override
     77     public String getShortDescription() {
     78         return String.format("Android SDK Tools, revision %1$d%2$s",
     79                 getRevision(),
     80                 isObsolete() ? " (Obsolete)" : "");
     81     }
     82 
     83     /** Returns a long description for an {@link IDescription}. */
     84     @Override
     85     public String getLongDescription() {
     86         String s = getDescription();
     87         if (s == null || s.length() == 0) {
     88             s = getShortDescription();
     89         }
     90 
     91         if (s.indexOf("revision") == -1) {
     92             s += String.format("\nRevision %1$d%2$s",
     93                     getRevision(),
     94                     isObsolete() ? " (Obsolete)" : "");
     95         }
     96 
     97         return s;
     98     }
     99 
    100     /**
    101      * Computes a potential installation folder if an archive of this package were
    102      * to be installed right away in the given SDK root.
    103      * <p/>
    104      * A "tool" package should always be located in SDK/tools.
    105      *
    106      * @param osSdkRoot The OS path of the SDK root folder.
    107      * @param suggestedDir A suggestion for the installation folder name, based on the root
    108      *                     folder used in the zip archive.
    109      * @param sdkManager An existing SDK manager to list current platforms and addons.
    110      * @return A new {@link File} corresponding to the directory to use to install this package.
    111      */
    112     @Override
    113     public File getInstallFolder(String osSdkRoot, String suggestedDir, SdkManager sdkManager) {
    114         return new File(osSdkRoot, SdkConstants.FD_TOOLS);
    115     }
    116 
    117     @Override
    118     public boolean sameItemAs(Package pkg) {
    119         // only one tool package so any tool package is the same item.
    120         return pkg instanceof ToolPackage;
    121     }
    122 
    123     /**
    124      * The tool package executes tools/lib/post_tools_install[.bat|.sh]
    125      * {@inheritDoc}
    126      */
    127     @Override
    128     public void postInstallHook(Archive archive, ITaskMonitor monitor, File installFolder) {
    129         super.postInstallHook(archive, monitor, installFolder);
    130 
    131         if (installFolder == null) {
    132             return;
    133         }
    134 
    135         File libDir = new File(installFolder, SdkConstants.FD_LIB);
    136         if (!libDir.isDirectory()) {
    137             return;
    138         }
    139 
    140         String scriptName = "post_tools_install";   //$NON-NLS-1$
    141         String shell = "";
    142         if (SdkConstants.currentPlatform() == SdkConstants.PLATFORM_WINDOWS) {
    143             shell = "cmd.exe /c ";
    144             scriptName += ".bat";                   //$NON-NLS-1$
    145         } else {
    146             scriptName += ".sh";                    //$NON-NLS-1$
    147         }
    148 
    149         File scriptFile = new File(libDir, scriptName);
    150         if (!scriptFile.isFile()) {
    151             return;
    152         }
    153 
    154         Process proc;
    155         int status = -1;
    156 
    157         try {
    158             proc = Runtime.getRuntime().exec(
    159                     shell + scriptName, // command
    160                     null,       // environment
    161                     libDir);    // working dir
    162 
    163             status = grabProcessOutput(proc, monitor, scriptName);
    164 
    165         } catch (Exception e) {
    166             monitor.setResult("Exception: %s", e.toString());
    167         }
    168 
    169         if (status != 0) {
    170             monitor.setResult("Failed to execute %s", scriptName);
    171             return;
    172         }
    173     }
    174 
    175     /**
    176      * Get the stderr/stdout outputs of a process and return when the process is done.
    177      * Both <b>must</b> be read or the process will block on windows.
    178      * @param process The process to get the ouput from.
    179      * @param monitor The monitor where to output errors.
    180      * @param scriptName The name of script being executed.
    181      * @return the process return code.
    182      * @throws InterruptedException
    183      */
    184     private int grabProcessOutput(final Process process,
    185             final ITaskMonitor monitor,
    186             final String scriptName)
    187                 throws InterruptedException {
    188         // read the lines as they come. if null is returned, it's
    189         // because the process finished
    190         Thread t1 = new Thread("") { //$NON-NLS-1$
    191             @Override
    192             public void run() {
    193                 // create a buffer to read the stderr output
    194                 InputStreamReader is = new InputStreamReader(process.getErrorStream());
    195                 BufferedReader errReader = new BufferedReader(is);
    196 
    197                 try {
    198                     while (true) {
    199                         String line = errReader.readLine();
    200                         if (line != null) {
    201                             monitor.setResult("[%1$s] Error: %2$s", scriptName, line);
    202                         } else {
    203                             break;
    204                         }
    205                     }
    206                 } catch (IOException e) {
    207                     // do nothing.
    208                 }
    209             }
    210         };
    211 
    212         Thread t2 = new Thread("") { //$NON-NLS-1$
    213             @Override
    214             public void run() {
    215                 InputStreamReader is = new InputStreamReader(process.getInputStream());
    216                 BufferedReader outReader = new BufferedReader(is);
    217 
    218                 try {
    219                     while (true) {
    220                         String line = outReader.readLine();
    221                         if (line != null) {
    222                             monitor.setResult("[%1$s] %2$s", scriptName, line);
    223                         } else {
    224                             break;
    225                         }
    226                     }
    227                 } catch (IOException e) {
    228                     // do nothing.
    229                 }
    230             }
    231         };
    232 
    233         t1.start();
    234         t2.start();
    235 
    236         // it looks like on windows process#waitFor() can return
    237         // before the thread have filled the arrays, so we wait for both threads and the
    238         // process itself.
    239         /* Disabled since not used. Do we really need this?
    240         if (waitforReaders) {
    241             try {
    242                 t1.join();
    243             } catch (InterruptedException e) {
    244             }
    245             try {
    246                 t2.join();
    247             } catch (InterruptedException e) {
    248             }
    249         }
    250         */
    251 
    252         // get the return code from the process
    253         return process.waitFor();
    254     }
    255 }
    256