1 /*
2  * Copyright (C) 2020 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.config.proxy;
17 
18 import com.android.tradefed.command.CommandOptions;
19 import com.android.tradefed.config.ConfigurationException;
20 import com.android.tradefed.config.Option;
21 import com.android.tradefed.util.FileUtil;
22 import com.android.tradefed.util.UniqueMultiMap;
23 
24 import com.google.common.base.Joiner;
25 
26 import java.io.File;
27 import java.io.IOException;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.List;
31 import java.util.Set;
32 
33 /** Objects that helps delegating the invocation to another Tradefed binary. */
34 public class TradefedDelegator {
35 
36     /** The object reference in the configuration. */
37     public static final String DELEGATE_OBJECT = "DELEGATE";
38 
39     private static final String DELETEGATED_OPTION_NAME = "delegated-tf";
40 
41     @Option(
42             name = DELETEGATED_OPTION_NAME,
43             description =
44                     "Points to the root dir of another Tradefed binary that will be used to drive"
45                             + " the invocation")
46     private File mDelegatedTfRootDir;
47 
48     @Option(
49             name = CommandOptions.INVOCATION_DATA,
50             description = "Mirror of CommandOptions#INVOCATION_DATA")
51     private UniqueMultiMap<String, String> mInvocationData = new UniqueMultiMap<>();
52 
53     // Not used directly but needed for proper parsing.
54     @SuppressWarnings("unused")
55     @Option(
56             name = "cts-params",
57             description =
58                     "This option is special and can mess up the best effort parser, define "
59                             + "it so it knows how to parse it.")
60     private List<String> mCtsParams = new ArrayList<>();
61 
62     private String[] mCommandLine = null;
63 
64     /** Whether or not trigger the delegation logic. */
shouldUseDelegation()65     public boolean shouldUseDelegation() {
66         return mDelegatedTfRootDir != null;
67     }
68 
69     /** Returns the directory of a Tradefed binary. */
getTfRootDir()70     public File getTfRootDir() {
71         return mDelegatedTfRootDir;
72     }
73 
74     /** Creates the classpath out of the jars in the directory. */
createClasspath()75     public String createClasspath() throws IOException {
76         Set<File> jars = FileUtil.findFilesObject(mDelegatedTfRootDir, ".*\\.jar");
77         return Joiner.on(":").join(jars);
78     }
79 
setCommandLine(String[] command)80     public void setCommandLine(String[] command) {
81         mCommandLine = command;
82     }
83 
getCommandLine()84     public String[] getCommandLine() {
85         return mCommandLine;
86     }
87 
88     /**
89      * Returns whether or not this is the staging environment. We do not want to delegate in staging
90      * by default, only if the "staging_delegated" is set.
91      */
isStaging()92     public boolean isStaging() {
93         return mInvocationData.containsKey("staging")
94                 && !mInvocationData.containsKey("staging_delegated");
95     }
96 
97     /**
98      * Remove from the original command line the delegate options so the underlying config does not
99      * delegate again.
100      */
clearCommandline(String[] originalCommand)101     public static String[] clearCommandline(String[] originalCommand)
102             throws ConfigurationException {
103         String[] commandLine = clearCommandlineFromOneArg(originalCommand, DELETEGATED_OPTION_NAME);
104         return commandLine;
105     }
106 
107     /** Remove a given option from the command line. */
clearCommandlineFromOneArg(String[] originalCommand, String optionName)108     private static String[] clearCommandlineFromOneArg(String[] originalCommand, String optionName)
109             throws ConfigurationException {
110         List<String> argsList = new ArrayList<>(Arrays.asList(originalCommand));
111         try {
112             while (argsList.contains("--" + optionName)) {
113                 int index = argsList.indexOf("--" + optionName);
114                 if (index != -1) {
115                     argsList.remove(index + 1);
116                     argsList.remove(index);
117                 }
118             }
119         } catch (RuntimeException e) {
120             throw new ConfigurationException(e.getMessage(), e);
121         }
122         return argsList.toArray(new String[0]);
123     }
124 }
125