1 /*
2  * Copyright (C) 2024 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.platform.test.ravenwood.ravenizer
17 
18 import com.android.hoststubgen.ArgIterator
19 import com.android.hoststubgen.ArgumentsException
20 import com.android.hoststubgen.SetOnce
21 import com.android.hoststubgen.ensureFileExists
22 import com.android.hoststubgen.log
23 import java.nio.file.Paths
24 import kotlin.io.path.exists
25 
26 /**
27  * If this file exits, we also read options from it. This is "unsafe" because it could break
28  * incremental builds, if it sets any flag that affects the output file.
29  * (however, for now, there's no such options.)
30  *
31  * For example, to enable verbose logging, do `echo '-v' > ~/.raveniezr-unsafe`
32  *
33  * (but even the content of this file changes, soong won't rerun the command, so you need to
34  * remove the output first and then do a build again.)
35  */
36 private val RAVENIZER_DOTFILE = System.getenv("HOME") + "/.raveniezr-unsafe"
37 
38 class RavenizerOptions(
39     /** Input jar file*/
40     var inJar: SetOnce<String> = SetOnce(""),
41 
42     /** Output jar file */
43     var outJar: SetOnce<String> = SetOnce(""),
44 
45     /** Whether to enable test validation. */
46     var enableValidation: SetOnce<Boolean> = SetOnce(true),
47 
48     /** Whether the validation failure is fatal or not. */
49     var fatalValidation: SetOnce<Boolean> = SetOnce(true),
50 
51     /** Whether to remove mockito and dexmaker classes. */
52     var stripMockito: SetOnce<Boolean> = SetOnce(false),
53 ) {
54     companion object {
55 
parseArgsnull56         fun parseArgs(origArgs: Array<String>): RavenizerOptions {
57             val args = origArgs.toMutableList()
58             if (Paths.get(RAVENIZER_DOTFILE).exists()) {
59                 log.i("Reading options from $RAVENIZER_DOTFILE")
60                 args.add(0, "@$RAVENIZER_DOTFILE")
61             }
62 
63             val ret = RavenizerOptions()
64             val ai = ArgIterator.withAtFiles(args.toTypedArray())
65 
66             while (true) {
67                 val arg = ai.nextArgOptional()
68                 if (arg == null) {
69                     break
70                 }
71 
72                 fun nextArg(): String = ai.nextArgRequired(arg)
73 
74                 if (log.maybeHandleCommandLineArg(arg) { nextArg() }) {
75                     continue
76                 }
77                 try {
78                     when (arg) {
79                         // TODO: Write help
80                         "-h", "--help" -> TODO("Help is not implemented yet")
81 
82                         "--in-jar" -> ret.inJar.set(nextArg()).ensureFileExists()
83                         "--out-jar" -> ret.outJar.set(nextArg())
84 
85                         "--enable-validation" -> ret.enableValidation.set(true)
86                         "--disable-validation" -> ret.enableValidation.set(false)
87 
88                         "--fatal-validation" -> ret.fatalValidation.set(true)
89                         "--no-fatal-validation" -> ret.fatalValidation.set(false)
90 
91                         "--strip-mockito" -> ret.stripMockito.set(true)
92                         "--no-strip-mockito" -> ret.stripMockito.set(false)
93 
94                         else -> throw ArgumentsException("Unknown option: $arg")
95                     }
96                 } catch (e: SetOnce.SetMoreThanOnceException) {
97                     throw ArgumentsException("Duplicate or conflicting argument found: $arg")
98                 }
99             }
100 
101             if (!ret.inJar.isSet) {
102                 throw ArgumentsException("Required option missing: --in-jar")
103             }
104             if (!ret.outJar.isSet) {
105                 throw ArgumentsException("Required option missing: --out-jar")
106             }
107            return ret
108         }
109     }
110 
toStringnull111     override fun toString(): String {
112         return """
113             RavenizerOptions{
114               inJar=$inJar,
115               outJar=$outJar,
116               enableValidation=$enableValidation,
117               fatalValidation=$fatalValidation,
118             }
119             """.trimIndent()
120     }
121 }
122