1 /*
<lambda>null2  * Copyright (C) 2023 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.tools.metalava.cli.signature
18 
19 import com.android.tools.metalava.cli.common.existingFile
20 import com.android.tools.metalava.cli.common.map
21 import com.android.tools.metalava.model.text.FileFormat
22 import com.github.ajalt.clikt.parameters.groups.OptionGroup
23 import com.github.ajalt.clikt.parameters.options.convert
24 import com.github.ajalt.clikt.parameters.options.default
25 import com.github.ajalt.clikt.parameters.options.option
26 
27 const val ARG_FORMAT = "--format"
28 const val ARG_USE_SAME_FORMAT_AS = "--use-same-format-as"
29 
30 /** The name of the group, can be used in help text to refer to the options in this group. */
31 const val SIGNATURE_FORMAT_OUTPUT_GROUP = "Signature Format Output"
32 
33 private val versionToFileFormat =
34     mapOf(
35         "v2" to FileFormat.V2,
36         "v3" to FileFormat.V3,
37         "v4" to FileFormat.V4,
38         "latest" to FileFormat.LATEST,
39         "recommended" to FileFormat.V2,
40     )
41 
42 class SignatureFormatOptions(
43     /** If true then the `migrating` property is allowed, otherwise it is not allowed at all. */
44     private val migratingAllowed: Boolean = false,
45 ) :
46     OptionGroup(
47         name = SIGNATURE_FORMAT_OUTPUT_GROUP,
48         help =
49             """
50                 Options controlling the format of the generated signature files.
51 
52                 See `metalava help signature-file-formats` for more information.
53             """
54                 .trimIndent()
55     ) {
56 
57     private val formatDefaults by
58         option(
59                 "--format-defaults",
60                 metavar = "<defaults>",
61                 help =
62                     """
63                         Specifies defaults for format properties.
64 
65                         A comma separated list of `<property>=<value>` assignments where
66                         `<property>` is one of the following:
67                         '${FileFormat.defaultableProperties().joinToString(separator = "', '")}'.
68 
69                         See `metalava help signature-file-formats` for more information on the
70                         properties.
71                     """
72                         .trimIndent(),
73             )
74             .convert { defaults -> FileFormat.parseDefaults(defaults) }
75 
76     /** The output format version being used */
77     private val formatSpecifier by
78         option(
79                 ARG_FORMAT,
80                 metavar = "[v2|v3|v4|latest|recommended|<specifier>]",
81                 help =
82                     """
83                         Specifies the output signature file format.
84 
85                         The preferred way of specifying the format is to use one of the following
86                         values (in no particular order):
87 
88                         latest - The latest in the supported versions. Only use this if you want to
89                         have the very latest and are prepared to update signature files on a
90                         continuous basis.
91 
92                         recommended (default) - The recommended version to use. This is currently
93                         set to `v2` and will only change very infrequently so can be considered
94                         stable.
95 
96                         <specifier> - which has the following syntax:
97                         ```
98                         <version>[:<property>=<value>[,<property>=<value>]*]
99                         ```
100 
101                         Where:
102 
103 
104                         The following values are still supported but should be considered
105                         deprecated.
106 
107                         v2 - The main version used in Android.
108 
109                         v3 - Adds support for using kotlin style syntax to embed nullability
110                         information instead of using explicit and verbose @NonNull and @Nullable
111                         annotations. This can be used for Java files and Kotlin files alike.
112 
113                         v4 - Adds support for using concise default values in parameters. Instead
114                         of specifying the actual default values it just uses the `default` keyword.
115                     """
116                         .trimIndent(),
117             )
118             .convert { specifier ->
119                 versionToFileFormat[specifier]
120                     ?: FileFormat.parseSpecifier(
121                         specifier = specifier,
122                         migratingAllowed = migratingAllowed,
123                         extraVersions = versionToFileFormat.keys,
124                     )
125             }
126             .default(FileFormat.V2, defaultForHelp = "recommended")
127 
128     private val useSameFormatAs by
129         option(
130                 ARG_USE_SAME_FORMAT_AS,
131                 help =
132                     """
133                         Specifies that the output format should be the same as the format used in
134                         the specified file. It is an error if the file does not exist. If the file
135                         is empty then this will behave as if it was not specified. If the file is
136                         not a valid signature file then it will fail. Otherwise, the format read
137                         from the file will be used.
138 
139                         If this is specified (and the file is not empty) then this will be used in
140                         preference to most of the other options in this group. Those options will be
141                         validated but otherwise ignored.
142 
143                         The intention is that the other options will be used to specify the default
144                         for new empty API files (e.g. created using `touch`) while this option is
145                         used to specify the format for generating updates to the existing non-empty
146                         files.
147                     """
148                         .trimIndent(),
149             )
150             .existingFile()
151             .map { file ->
152                 file?.reader(Charsets.UTF_8)?.use { FileFormat.parseHeader(file.toPath(), it) }
153             }
154 
155     /**
156      * The [FileFormat] produced by merging all the format related options into one cohesive set of
157      * format related properties. It combines the defaults
158      */
159     val fileFormat: FileFormat by
160         lazy(LazyThreadSafetyMode.NONE) {
161             // Check the useSameFormatAs first and if it is not specified (or is an empty file) then
162             // fall back to the other options.
163             val format = useSameFormatAs ?: formatSpecifier
164 
165             // Apply any additional overrides.
166             formatDefaults?.let { format.copy(formatDefaults = it) } ?: format
167         }
168 }
169